Merge pull request #69413 from home-assistant/rc

This commit is contained in:
Franck Nijhof 2022-04-06 15:14:55 +02:00 committed by GitHub
commit 7dd19066e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3607 changed files with 209794 additions and 163612 deletions

View File

@ -3,102 +3,104 @@
core: &core
- homeassistant/*.py
- homeassistant/auth/**
- homeassistant/helpers/*
- homeassistant/helpers/**
- homeassistant/package_constraints.txt
- homeassistant/util/*
- homeassistant/util/**
- pyproject.toml
- requirements.txt
- setup.cfg
# Our base platforms, that are used by other integrations
base_platforms: &base_platforms
- homeassistant/components/air_quality/*
- homeassistant/components/alarm_control_panel/*
- homeassistant/components/binary_sensor/*
- homeassistant/components/button/*
- homeassistant/components/calendar/*
- homeassistant/components/camera/*
- homeassistant/components/climate/*
- homeassistant/components/cover/*
- homeassistant/components/device_tracker/*
- homeassistant/components/diagnostics/*
- homeassistant/components/fan/*
- homeassistant/components/geo_location/*
- homeassistant/components/humidifier/*
- homeassistant/components/image_processing/*
- homeassistant/components/light/*
- homeassistant/components/lock/*
- homeassistant/components/media_player/*
- homeassistant/components/notify/*
- homeassistant/components/number/*
- homeassistant/components/remote/*
- homeassistant/components/scene/*
- homeassistant/components/select/*
- homeassistant/components/sensor/*
- homeassistant/components/siren/*
- homeassistant/components/stt/*
- homeassistant/components/switch/*
- homeassistant/components/tts/*
- homeassistant/components/vacuum/*
- homeassistant/components/water_heater/*
- homeassistant/components/weather/*
- homeassistant/components/air_quality/**
- homeassistant/components/alarm_control_panel/**
- homeassistant/components/binary_sensor/**
- homeassistant/components/button/**
- homeassistant/components/calendar/**
- homeassistant/components/camera/**
- homeassistant/components/climate/**
- homeassistant/components/cover/**
- homeassistant/components/device_tracker/**
- homeassistant/components/diagnostics/**
- homeassistant/components/fan/**
- homeassistant/components/geo_location/**
- homeassistant/components/humidifier/**
- homeassistant/components/image_processing/**
- homeassistant/components/light/**
- homeassistant/components/lock/**
- homeassistant/components/media_player/**
- homeassistant/components/notify/**
- homeassistant/components/number/**
- homeassistant/components/remote/**
- homeassistant/components/scene/**
- homeassistant/components/select/**
- homeassistant/components/sensor/**
- homeassistant/components/siren/**
- homeassistant/components/stt/**
- homeassistant/components/switch/**
- homeassistant/components/tts/**
- homeassistant/components/update/**
- homeassistant/components/vacuum/**
- homeassistant/components/water_heater/**
- homeassistant/components/weather/**
# Extra components that trigger the full suite
components: &components
- homeassistant/components/alert/*
- homeassistant/components/alexa/*
- homeassistant/components/auth/*
- homeassistant/components/automation/*
- homeassistant/components/cloud/*
- homeassistant/components/config/*
- homeassistant/components/configurator/*
- homeassistant/components/conversation/*
- homeassistant/components/demo/*
- homeassistant/components/device_automation/*
- homeassistant/components/dhcp/*
- homeassistant/components/discovery/*
- homeassistant/components/energy/*
- homeassistant/components/ffmpeg/*
- homeassistant/components/frontend/*
- homeassistant/components/google_assistant/*
- homeassistant/components/group/*
- homeassistant/components/hassio/*
- homeassistant/components/alert/**
- homeassistant/components/alexa/**
- homeassistant/components/auth/**
- homeassistant/components/automation/**
- homeassistant/components/backup/**
- homeassistant/components/cloud/**
- homeassistant/components/config/**
- homeassistant/components/configurator/**
- homeassistant/components/conversation/**
- homeassistant/components/demo/**
- homeassistant/components/device_automation/**
- homeassistant/components/dhcp/**
- homeassistant/components/discovery/**
- homeassistant/components/energy/**
- homeassistant/components/ffmpeg/**
- homeassistant/components/frontend/**
- homeassistant/components/google_assistant/**
- homeassistant/components/group/**
- homeassistant/components/hassio/**
- homeassistant/components/homeassistant/**
- homeassistant/components/http/**
- homeassistant/components/image/*
- homeassistant/components/input_boolean/*
- homeassistant/components/input_button/*
- homeassistant/components/input_datetime/*
- homeassistant/components/input_number/*
- homeassistant/components/input_select/*
- homeassistant/components/input_text/*
- homeassistant/components/logbook/*
- homeassistant/components/logger/*
- homeassistant/components/lovelace/*
- homeassistant/components/media_source/*
- homeassistant/components/mjpeg/*
- homeassistant/components/mqtt/*
- homeassistant/components/network/*
- homeassistant/components/onboarding/*
- homeassistant/components/otp/*
- homeassistant/components/persistent_notification/*
- homeassistant/components/person/*
- homeassistant/components/recorder/*
- homeassistant/components/safe_mode/*
- homeassistant/components/script/*
- homeassistant/components/shopping_list/*
- homeassistant/components/ssdp/*
- homeassistant/components/stream/*
- homeassistant/components/sun/*
- homeassistant/components/system_health/*
- homeassistant/components/tag/*
- homeassistant/components/template/*
- homeassistant/components/timer/*
- homeassistant/components/usb/*
- homeassistant/components/webhook/*
- homeassistant/components/websocket_api/*
- homeassistant/components/zeroconf/*
- homeassistant/components/zone/*
- homeassistant/components/image/**
- homeassistant/components/input_boolean/**
- homeassistant/components/input_button/**
- homeassistant/components/input_datetime/**
- homeassistant/components/input_number/**
- homeassistant/components/input_select/**
- homeassistant/components/input_text/**
- homeassistant/components/logbook/**
- homeassistant/components/logger/**
- homeassistant/components/lovelace/**
- homeassistant/components/media_source/**
- homeassistant/components/mjpeg/**
- homeassistant/components/mqtt/**
- homeassistant/components/network/**
- homeassistant/components/onboarding/**
- homeassistant/components/otp/**
- homeassistant/components/persistent_notification/**
- homeassistant/components/person/**
- homeassistant/components/recorder/**
- homeassistant/components/safe_mode/**
- homeassistant/components/script/**
- homeassistant/components/shopping_list/**
- homeassistant/components/ssdp/**
- homeassistant/components/stream/**
- homeassistant/components/sun/**
- homeassistant/components/system_health/**
- homeassistant/components/tag/**
- homeassistant/components/template/**
- homeassistant/components/timer/**
- homeassistant/components/usb/**
- homeassistant/components/webhook/**
- homeassistant/components/websocket_api/**
- homeassistant/components/zeroconf/**
- homeassistant/components/zone/**
# Testing related files that affect the whole test/linting suite
tests: &tests
@ -107,26 +109,27 @@ tests: &tests
- requirements_test_pre_commit.txt
- requirements_test.txt
- tests/auth/**
- tests/backports/*
- tests/backports/**
- tests/common.py
- tests/conftest.py
- tests/hassfest/*
- tests/helpers/*
- tests/hassfest/**
- tests/helpers/**
- tests/ignore_uncaught_exceptions.py
- tests/mock/*
- tests/pylint/*
- tests/scripts/*
- tests/test_util/*
- tests/mock/**
- tests/pylint/**
- tests/scripts/**
- tests/test_util/**
- tests/testing_config/**
- tests/util/**
other: &other
- .github/workflows/*
- .github/workflows/**
- homeassistant/scripts/**
requirements: &requirements
- .github/workflows/*
- .github/workflows/**
- homeassistant/package_constraints.txt
- script/pip_check
- requirements*.txt
- setup.cfg

View File

@ -59,6 +59,7 @@ omit =
homeassistant/components/ampio/*
homeassistant/components/android_ip_webcam/*
homeassistant/components/androidtv/__init__.py
homeassistant/components/androidtv/diagnostics.py
homeassistant/components/anel_pwrctrl/switch.py
homeassistant/components/anthemav/media_player.py
homeassistant/components/apcupsd/*
@ -101,10 +102,8 @@ omit =
homeassistant/components/baidu/tts.py
homeassistant/components/balboa/__init__.py
homeassistant/components/beewi_smartclim/sensor.py
homeassistant/components/bbb_gpio/*
homeassistant/components/bbox/device_tracker.py
homeassistant/components/bbox/sensor.py
homeassistant/components/bh1750/sensor.py
homeassistant/components/bitcoin/sensor.py
homeassistant/components/bizkaibus/sensor.py
homeassistant/components/blink/__init__.py
@ -114,16 +113,10 @@ omit =
homeassistant/components/blink/const.py
homeassistant/components/blink/sensor.py
homeassistant/components/blinksticklight/light.py
homeassistant/components/blinkt/light.py
homeassistant/components/blockchain/sensor.py
homeassistant/components/bloomsky/*
homeassistant/components/bluesound/*
homeassistant/components/bluetooth_tracker/*
homeassistant/components/bme280/__init__.py
homeassistant/components/bme280/const.py
homeassistant/components/bme280/sensor.py
homeassistant/components/bme680/sensor.py
homeassistant/components/bmp280/sensor.py
homeassistant/components/bmw_connected_drive/__init__.py
homeassistant/components/bmw_connected_drive/binary_sensor.py
homeassistant/components/bmw_connected_drive/button.py
@ -137,6 +130,7 @@ omit =
homeassistant/components/bosch_shc/cover.py
homeassistant/components/bosch_shc/entity.py
homeassistant/components/bosch_shc/sensor.py
homeassistant/components/bosch_shc/switch.py
homeassistant/components/braviatv/__init__.py
homeassistant/components/braviatv/const.py
homeassistant/components/braviatv/media_player.py
@ -204,6 +198,8 @@ omit =
homeassistant/components/decora/light.py
homeassistant/components/decora_wifi/light.py
homeassistant/components/delijn/*
homeassistant/components/deluge/__init__.py
homeassistant/components/deluge/coordinator.py
homeassistant/components/deluge/sensor.py
homeassistant/components/deluge/switch.py
homeassistant/components/denon/media_player.py
@ -218,10 +214,10 @@ omit =
homeassistant/components/devolo_home_control/sensor.py
homeassistant/components/devolo_home_control/subscriber.py
homeassistant/components/devolo_home_control/switch.py
homeassistant/components/dht/sensor.py
homeassistant/components/digital_ocean/*
homeassistant/components/digitalloggers/switch.py
homeassistant/components/discogs/sensor.py
homeassistant/components/discord/__init__.py
homeassistant/components/discord/notify.py
homeassistant/components/dlib_face_detect/image_processing.py
homeassistant/components/dlib_face_identify/image_processing.py
@ -303,7 +299,6 @@ omit =
homeassistant/components/environment_canada/camera.py
homeassistant/components/environment_canada/sensor.py
homeassistant/components/environment_canada/weather.py
homeassistant/components/envirophat/sensor.py
homeassistant/components/envisalink/*
homeassistant/components/ephember/climate.py
homeassistant/components/epson/__init__.py
@ -342,7 +337,15 @@ omit =
homeassistant/components/faa_delays/binary_sensor.py
homeassistant/components/fastdotcom/*
homeassistant/components/ffmpeg/camera.py
homeassistant/components/fibaro/*
homeassistant/components/fibaro/__init__.py
homeassistant/components/fibaro/binary_sensor.py
homeassistant/components/fibaro/climate.py
homeassistant/components/fibaro/cover.py
homeassistant/components/fibaro/light.py
homeassistant/components/fibaro/lock.py
homeassistant/components/fibaro/scene.py
homeassistant/components/fibaro/sensor.py
homeassistant/components/fibaro/switch.py
homeassistant/components/filesize/sensor.py
homeassistant/components/fints/sensor.py
homeassistant/components/fireservicerota/__init__.py
@ -398,7 +401,6 @@ omit =
homeassistant/components/fritz/common.py
homeassistant/components/fritz/const.py
homeassistant/components/fritz/device_tracker.py
homeassistant/components/fritz/sensor.py
homeassistant/components/fritz/services.py
homeassistant/components/fritz/switch.py
homeassistant/components/fritzbox_callmonitor/__init__.py
@ -490,7 +492,6 @@ omit =
homeassistant/components/honeywell/climate.py
homeassistant/components/horizon/media_player.py
homeassistant/components/hp_ilo/sensor.py
homeassistant/components/htu21d/sensor.py
homeassistant/components/huawei_lte/__init__.py
homeassistant/components/huawei_lte/binary_sensor.py
homeassistant/components/huawei_lte/device_tracker.py
@ -622,7 +623,6 @@ omit =
homeassistant/components/launch_library/sensor.py
homeassistant/components/lcn/binary_sensor.py
homeassistant/components/lcn/climate.py
homeassistant/components/lcn/cover.py
homeassistant/components/lcn/helpers.py
homeassistant/components/lcn/scene.py
homeassistant/components/lcn/sensor.py
@ -678,7 +678,6 @@ omit =
homeassistant/components/map/*
homeassistant/components/mastodon/notify.py
homeassistant/components/matrix/*
homeassistant/components/mcp23017/*
homeassistant/components/media_extractor/*
homeassistant/components/mediaroom/media_player.py
homeassistant/components/melcloud/__init__.py
@ -718,8 +717,6 @@ omit =
homeassistant/components/mjpeg/camera.py
homeassistant/components/mjpeg/util.py
homeassistant/components/mochad/*
homeassistant/components/modbus/climate.py
homeassistant/components/modbus/binary_sensor.py
homeassistant/components/modem_callerid/button.py
homeassistant/components/modem_callerid/sensor.py
homeassistant/components/moehlenhoff_alpha2/__init__.py
@ -729,7 +726,6 @@ omit =
homeassistant/components/motion_blinds/const.py
homeassistant/components/motion_blinds/cover.py
homeassistant/components/motion_blinds/sensor.py
homeassistant/components/mpchc/media_player.py
homeassistant/components/mpd/media_player.py
homeassistant/components/mqtt_room/sensor.py
homeassistant/components/msteams/notify.py
@ -751,7 +747,6 @@ omit =
homeassistant/components/mysensors/handler.py
homeassistant/components/mysensors/helpers.py
homeassistant/components/mysensors/light.py
homeassistant/components/mysensors/notify.py
homeassistant/components/mysensors/switch.py
homeassistant/components/mystrom/binary_sensor.py
homeassistant/components/mystrom/light.py
@ -862,12 +857,12 @@ omit =
homeassistant/components/openweathermap/weather_update_coordinator.py
homeassistant/components/opnsense/*
homeassistant/components/opple/light.py
homeassistant/components/orangepi_gpio/*
homeassistant/components/oru/*
homeassistant/components/orvibo/switch.py
homeassistant/components/osramlightify/light.py
homeassistant/components/otp/sensor.py
homeassistant/components/overkiz/__init__.py
homeassistant/components/overkiz/alarm_control_panel.py
homeassistant/components/overkiz/binary_sensor.py
homeassistant/components/overkiz/button.py
homeassistant/components/overkiz/climate.py
@ -889,13 +884,9 @@ omit =
homeassistant/components/ovo_energy/__init__.py
homeassistant/components/ovo_energy/const.py
homeassistant/components/ovo_energy/sensor.py
homeassistant/components/ozw/__init__.py
homeassistant/components/ozw/entity.py
homeassistant/components/ozw/services.py
homeassistant/components/panasonic_bluray/media_player.py
homeassistant/components/panasonic_viera/media_player.py
homeassistant/components/pandora/media_player.py
homeassistant/components/pcal9535a/*
homeassistant/components/pencom/switch.py
homeassistant/components/philips_js/__init__.py
homeassistant/components/philips_js/diagnostics.py
@ -904,10 +895,7 @@ omit =
homeassistant/components/philips_js/remote.py
homeassistant/components/philips_js/switch.py
homeassistant/components/pi_hole/sensor.py
homeassistant/components/pi4ioe5v9xxxx/binary_sensor.py
homeassistant/components/pi4ioe5v9xxxx/switch.py
homeassistant/components/picotts/tts.py
homeassistant/components/piglow/light.py
homeassistant/components/pilight/*
homeassistant/components/ping/__init__.py
homeassistant/components/ping/const.py
@ -920,8 +908,10 @@ omit =
homeassistant/components/plaato/const.py
homeassistant/components/plaato/entity.py
homeassistant/components/plaato/sensor.py
homeassistant/components/plex/cast.py
homeassistant/components/plex/media_player.py
homeassistant/components/plex/view.py
homeassistant/components/plugwise/select.py
homeassistant/components/plum_lightpad/light.py
homeassistant/components/pocketcasts/sensor.py
homeassistant/components/point/__init__.py
@ -967,7 +957,6 @@ omit =
homeassistant/components/rainmachine/model.py
homeassistant/components/rainmachine/sensor.py
homeassistant/components/rainmachine/switch.py
homeassistant/components/raspihats/*
homeassistant/components/raspyrfm/*
homeassistant/components/recollect_waste/__init__.py
homeassistant/components/recollect_waste/sensor.py
@ -981,6 +970,7 @@ omit =
homeassistant/components/remote_rpi_gpio/*
homeassistant/components/rest/notify.py
homeassistant/components/rest/switch.py
homeassistant/components/rfxtrx/diagnostics.py
homeassistant/components/ridwell/__init__.py
homeassistant/components/ridwell/sensor.py
homeassistant/components/ridwell/switch.py
@ -1003,9 +993,6 @@ omit =
homeassistant/components/rova/sensor.py
homeassistant/components/rpi_camera/*
homeassistant/components/rpi_gpio/*
homeassistant/components/rpi_gpio_pwm/light.py
homeassistant/components/rpi_pfio/*
homeassistant/components/rpi_rf/switch.py
homeassistant/components/rtorrent/sensor.py
homeassistant/components/russound_rio/media_player.py
homeassistant/components/russound_rnet/media_player.py
@ -1016,6 +1003,7 @@ omit =
homeassistant/components/screenlogic/__init__.py
homeassistant/components/screenlogic/binary_sensor.py
homeassistant/components/screenlogic/climate.py
homeassistant/components/screenlogic/diagnostics.py
homeassistant/components/screenlogic/light.py
homeassistant/components/screenlogic/number.py
homeassistant/components/screenlogic/sensor.py
@ -1026,8 +1014,6 @@ omit =
homeassistant/components/sense/__init__.py
homeassistant/components/sense/binary_sensor.py
homeassistant/components/sense/sensor.py
homeassistant/components/sensehat/light.py
homeassistant/components/sensehat/sensor.py
homeassistant/components/senseme/__init__.py
homeassistant/components/senseme/binary_sensor.py
homeassistant/components/senseme/discovery.py
@ -1036,10 +1022,14 @@ omit =
homeassistant/components/senseme/light.py
homeassistant/components/senseme/switch.py
homeassistant/components/sensibo/__init__.py
homeassistant/components/sensibo/binary_sensor.py
homeassistant/components/sensibo/climate.py
homeassistant/components/sensibo/coordinator.py
homeassistant/components/sensibo/diagnostics.py
homeassistant/components/sensibo/entity.py
homeassistant/components/sensibo/number.py
homeassistant/components/sensibo/select.py
homeassistant/components/sensibo/sensor.py
homeassistant/components/serial/sensor.py
homeassistant/components/serial_pm/sensor.py
homeassistant/components/sesame/lock.py
@ -1055,7 +1045,6 @@ omit =
homeassistant/components/shelly/number.py
homeassistant/components/shelly/sensor.py
homeassistant/components/shelly/utils.py
homeassistant/components/sht31/sensor.py
homeassistant/components/sigfox/sensor.py
homeassistant/components/simplepush/notify.py
homeassistant/components/simplisafe/__init__.py
@ -1086,9 +1075,6 @@ omit =
homeassistant/components/smappee/sensor.py
homeassistant/components/smappee/switch.py
homeassistant/components/smarty/*
homeassistant/components/smarthab/__init__.py
homeassistant/components/smarthab/cover.py
homeassistant/components/smarthab/light.py
homeassistant/components/sms/__init__.py
homeassistant/components/sms/const.py
homeassistant/components/sms/gateway.py
@ -1194,6 +1180,7 @@ omit =
homeassistant/components/synology_dsm/sensor.py
homeassistant/components/synology_dsm/service.py
homeassistant/components/synology_dsm/switch.py
homeassistant/components/synology_dsm/update.py
homeassistant/components/synology_srm/device_tracker.py
homeassistant/components/syslog/notify.py
homeassistant/components/system_bridge/__init__.py
@ -1209,7 +1196,10 @@ omit =
homeassistant/components/tado/sensor.py
homeassistant/components/tado/water_heater.py
homeassistant/components/tank_utility/sensor.py
homeassistant/components/tankerkoenig/*
homeassistant/components/tankerkoenig/__init__.py
homeassistant/components/tankerkoenig/binary_sensor.py
homeassistant/components/tankerkoenig/const.py
homeassistant/components/tankerkoenig/sensor.py
homeassistant/components/tapsaff/binary_sensor.py
homeassistant/components/tautulli/const.py
homeassistant/components/tautulli/coordinator.py
@ -1244,7 +1234,6 @@ omit =
homeassistant/components/tmb/sensor.py
homeassistant/components/todoist/calendar.py
homeassistant/components/todoist/const.py
homeassistant/components/tof/sensor.py
homeassistant/components/tolo/__init__.py
homeassistant/components/tolo/binary_sensor.py
homeassistant/components/tolo/button.py
@ -1287,8 +1276,10 @@ omit =
homeassistant/components/tradfri/light.py
homeassistant/components/tradfri/sensor.py
homeassistant/components/tradfri/switch.py
homeassistant/components/trafikverket_train/__init__.py
homeassistant/components/trafikverket_train/sensor.py
homeassistant/components/trafikverket_weatherstation/__init__.py
homeassistant/components/trafikverket_weatherstation/coordinator.py
homeassistant/components/trafikverket_weatherstation/sensor.py
homeassistant/components/transmission/sensor.py
homeassistant/components/transmission/switch.py
@ -1335,10 +1326,8 @@ omit =
homeassistant/components/upc_connect/*
homeassistant/components/uscis/sensor.py
homeassistant/components/vallox/__init__.py
homeassistant/components/vallox/const.py
homeassistant/components/vallox/fan.py
homeassistant/components/vallox/sensor.py
homeassistant/components/vallox/binary_sensor.py
homeassistant/components/vasttrafik/sensor.py
homeassistant/components/velbus/__init__.py
homeassistant/components/velbus/binary_sensor.py
@ -1391,6 +1380,9 @@ omit =
homeassistant/components/volumio/browse_media.py
homeassistant/components/volumio/media_player.py
homeassistant/components/volvooncall/*
homeassistant/components/vulcan/__init__.py
homeassistant/components/vulcan/calendar.py
homeassistant/components/vulcan/fetch_data.py
homeassistant/components/w800rf32/*
homeassistant/components/waqi/sensor.py
homeassistant/components/waterfurnace/*
@ -1433,6 +1425,7 @@ omit =
homeassistant/components/xiaomi_miio/air_quality.py
homeassistant/components/xiaomi_miio/alarm_control_panel.py
homeassistant/components/xiaomi_miio/binary_sensor.py
homeassistant/components/xiaomi_miio/button.py
homeassistant/components/xiaomi_miio/device.py
homeassistant/components/xiaomi_miio/device_tracker.py
homeassistant/components/xiaomi_miio/fan.py
@ -1489,18 +1482,19 @@ omit =
homeassistant/components/ziggo_mediabox_xl/media_player.py
homeassistant/components/zoneminder/*
homeassistant/components/supla/*
homeassistant/components/zwave/util.py
homeassistant/components/zwave_js/discovery.py
homeassistant/components/zwave_js/sensor.py
homeassistant/components/zwave_me/__init__.py
homeassistant/components/zwave_me/binary_sensor.py
homeassistant/components/zwave_me/button.py
homeassistant/components/zwave_me/cover.py
homeassistant/components/zwave_me/climate.py
homeassistant/components/zwave_me/helpers.py
homeassistant/components/zwave_me/light.py
homeassistant/components/zwave_me/lock.py
homeassistant/components/zwave_me/number.py
homeassistant/components/zwave_me/sensor.py
homeassistant/components/zwave_me/siren.py
homeassistant/components/zwave_me/switch.py
[report]

View File

@ -24,12 +24,12 @@ jobs:
publish: ${{ steps.version.outputs.publish }}
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
with:
fetch-depth: 0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.0.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@ -67,10 +67,10 @@ jobs:
if: github.repository_owner == 'home-assistant' && needs.init.outputs.publish == 'true'
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.0.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@ -100,11 +100,11 @@ jobs:
arch: ${{ fromJson(needs.init.outputs.architectures) }}
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
if: needs.init.outputs.channel == 'dev'
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.0.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@ -135,7 +135,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build base image
uses: home-assistant/builder@2022.01.0
uses: home-assistant/builder@2022.03.1
with:
args: |
$BUILD_ARGS \
@ -173,7 +173,7 @@ jobs:
- tinker
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set build additional args
run: |
@ -200,7 +200,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build base image
uses: home-assistant/builder@2022.01.0
uses: home-assistant/builder@2022.03.1
with:
args: |
$BUILD_ARGS \
@ -216,7 +216,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Initialize git
uses: home-assistant/actions/helpers/git-init@master
@ -255,7 +255,7 @@ jobs:
- "homeassistant"
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Login to DockerHub
if: matrix.registry == 'homeassistant'

View File

@ -11,18 +11,18 @@ on:
workflow_dispatch:
inputs:
full:
description: 'Full run (regardless of changes)'
description: "Full run (regardless of changes)"
default: false
type: boolean
lint-only:
description: 'Skip pytest'
description: "Skip pytest"
default: false
type: boolean
env:
CACHE_VERSION: 9
PIP_CACHE_VERSION: 3
HA_SHORT_VERSION: 2022.3
HA_SHORT_VERSION: 2022.4
DEFAULT_PYTHON: 3.9
PRE_COMMIT_CACHE: ~/.cache/pre-commit
PIP_CACHE: /tmp/pip-cache
@ -51,7 +51,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Filter for core changes
uses: dorny/paths-filter@v2.10.2
id: core
@ -62,7 +62,7 @@ jobs:
integrations=$(ls -Ad ./homeassistant/components/[!_]* | xargs -n 1 basename)
touch .integration_paths.yaml
for integration in $integrations; do
echo "${integration}: [homeassistant/components/${integration}/*, tests/components/${integration}/*]" \
echo "${integration}: [homeassistant/components/${integration}/**, tests/components/${integration}/**]" \
>> .integration_paths.yaml;
done
echo "Result:"
@ -119,7 +119,8 @@ jobs:
|| [[ "${{ github.ref }}" == "refs/heads/master" ]] \
|| [[ "${{ github.ref }}" == "refs/heads/rc" ]] \
|| [[ "${{ steps.core.outputs.any }}" == "true" ]] \
|| [[ "${{ github.event.inputs.full }}" == "true" ]];
|| [[ "${{ github.event.inputs.full }}" == "true" ]] \
|| [[ "${{ contains(github.event.pull_request.labels.*.name, 'ci-full-run') }}" == "true" ]];
then
test_groups="[1, 2, 3, 4, 5, 6]"
test_group_count=6
@ -151,10 +152,10 @@ jobs:
pre-commit-key: ${{ steps.generate-pre-commit-key.outputs.key }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
id: python
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.0.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Generate partial Python venv restore key
@ -171,7 +172,7 @@ jobs:
env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')"
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: venv
key: >-
@ -188,7 +189,7 @@ jobs:
# ${{ runner.os }}-${{ steps.python.outputs.python-version }}-base-venv-${{ env.CACHE_VERSION }}-
- name: Restore pip wheel cache
if: steps.cache-venv.outputs.cache-hit != 'true'
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: ${{ env.PIP_CACHE }}
key: >-
@ -211,7 +212,7 @@ jobs:
hashFiles('.pre-commit-config.yaml') }}"
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: ${{ env.PRE_COMMIT_CACHE }}
key: >-
@ -232,15 +233,15 @@ jobs:
- prepare-base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.0.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -252,7 +253,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -282,15 +283,15 @@ jobs:
- prepare-base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.0.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -302,7 +303,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -333,15 +334,15 @@ jobs:
needs: prepare-base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.0.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -353,7 +354,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -375,15 +376,15 @@ jobs:
- prepare-base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.0.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -395,7 +396,7 @@ jobs:
exit 1
- name: Restore pre-commit environment from cache
id: cache-precommit
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }}
@ -434,6 +435,11 @@ jobs:
. venv/bin/activate
pre-commit run --hook-stage manual check-json --all-files
- name: Run prettier
run: |
. venv/bin/activate
pre-commit run --hook-stage manual prettier --all-files
- name: Register check executables problem matcher
run: |
echo "::add-matcher::.github/workflows/matchers/check-executables-have-shebangs.json"
@ -485,10 +491,10 @@ jobs:
container: homeassistant/ci-azure:${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: venv
key: ${{ runner.os }}-${{ matrix.python-version }}-${{
@ -509,15 +515,15 @@ jobs:
needs: prepare-base
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.0.0
id: python
with:
python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{
@ -544,7 +550,7 @@ jobs:
container: homeassistant/ci-azure:${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Generate partial Python venv restore key
id: generate-python-key
run: >-
@ -559,7 +565,7 @@ jobs:
env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')"
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: venv
key: >-
@ -576,7 +582,7 @@ jobs:
# ${{ runner.os }}-${{ matrix.python-version }}-venv-${{ env.CACHE_VERSION }}-
- name: Restore pip wheel cache
if: steps.cache-venv.outputs.cache-hit != 'true'
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: ${{ env.PIP_CACHE }}
key: >-
@ -612,10 +618,10 @@ jobs:
container: homeassistant/ci-azure:${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: venv
key: ${{ runner.os }}-${{ matrix.python-version }}-${{
@ -654,10 +660,10 @@ jobs:
container: homeassistant/ci-azure:${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: venv
key: ${{ runner.os }}-${{ matrix.python-version }}-${{
@ -686,7 +692,7 @@ jobs:
pip-check:
runs-on: ubuntu-latest
if: needs.changes.outputs.requirements == 'true'
if: needs.changes.outputs.requirements == 'true' || github.event.inputs.full == 'true'
needs:
- changes
- prepare-tests
@ -698,10 +704,10 @@ jobs:
container: homeassistant/ci-azure:${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: venv
key: ${{ runner.os }}-${{ matrix.python-version }}-${{
@ -741,10 +747,10 @@ jobs:
container: homeassistant/ci-azure:${{ matrix.python-version }}
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv
uses: actions/cache@v2.1.7
uses: actions/cache@v3.0.0
with:
path: venv
key: ${{ runner.os }}-${{ matrix.python-version }}-${{
@ -812,24 +818,8 @@ jobs:
--durations-min=1 \
-p no:sugar \
tests/components/${{ matrix.group }}
- name: Run pytest (partially); no coverage
if: needs.changes.outputs.test_full_suite == 'false'
timeout-minutes: 10
run: |
. venv/bin/activate
python --version
python3 -X dev -m pytest \
-qq \
--timeout=9 \
--durations=10 \
-n auto \
-o console_output_style=count \
--durations=0 \
--durations-min=1 \
-p no:sugar \
tests/components/${{ matrix.group }}
- name: Upload coverage artifact
uses: actions/upload-artifact@v2.3.1
uses: actions/upload-artifact@v3.0.0
with:
name: coverage-${{ matrix.python-version }}-${{ matrix.group }}
path: coverage.xml
@ -845,7 +835,7 @@ jobs:
- pytest
steps:
- name: Check out code from GitHub
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Download all coverage artifacts
uses: actions/download-artifact@v2
- name: Upload coverage to Codecov (full coverage)

View File

@ -21,10 +21,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.0.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}
@ -40,10 +40,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
uses: actions/setup-python@v2.3.2
uses: actions/setup-python@v3.0.0
with:
python-version: ${{ env.DEFAULT_PYTHON }}

View File

@ -22,7 +22,7 @@ jobs:
architectures: ${{ steps.info.outputs.architectures }}
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Get information
id: info
@ -50,13 +50,13 @@ jobs:
) > .env_file
- name: Upload env_file
uses: actions/upload-artifact@v2.3.1
uses: actions/upload-artifact@v3.0.0
with:
name: env_file
path: ./.env_file
- name: Upload requirements_diff
uses: actions/upload-artifact@v2.3.1
uses: actions/upload-artifact@v3.0.0
with:
name: requirements_diff
path: ./requirements_diff.txt
@ -74,7 +74,7 @@ jobs:
- "3.9-alpine3.14"
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Download env_file
uses: actions/download-artifact@v2
@ -99,7 +99,7 @@ jobs:
pip: "Cython;numpy"
skip-binary: aiohttp
constraints: "homeassistant/package_constraints.txt"
requirements-diff: 'requirements_diff.txt'
requirements-diff: "requirements_diff.txt"
requirements: "requirements.txt"
integrations:
@ -115,7 +115,7 @@ jobs:
- "3.9-alpine3.14"
steps:
- name: Checkout the repository
uses: actions/checkout@v2.4.0
uses: actions/checkout@v3.0.0
- name: Download env_file
uses: actions/download-artifact@v2
@ -135,14 +135,9 @@ jobs:
sed -i "s|# bluepy|bluepy|g" ${requirement_file}
sed -i "s|# beacontools|beacontools|g" ${requirement_file}
sed -i "s|# RPi.GPIO|RPi.GPIO|g" ${requirement_file}
sed -i "s|# raspihats|raspihats|g" ${requirement_file}
sed -i "s|# rpi-rf|rpi-rf|g" ${requirement_file}
sed -i "s|# blinkt|blinkt|g" ${requirement_file}
sed -i "s|# fritzconnection|fritzconnection|g" ${requirement_file}
sed -i "s|# pyuserinput|pyuserinput|g" ${requirement_file}
sed -i "s|# evdev|evdev|g" ${requirement_file}
sed -i "s|# smbus-cffi|smbus-cffi|g" ${requirement_file}
sed -i "s|# i2csense|i2csense|g" ${requirement_file}
sed -i "s|# python-eq3bt|python-eq3bt|g" ${requirement_file}
sed -i "s|# pycups|pycups|g" ${requirement_file}
sed -i "s|# homekit|homekit|g" ${requirement_file}
@ -152,9 +147,7 @@ jobs:
sed -i "s|# PySwitchbot|PySwitchbot|g" ${requirement_file}
sed -i "s|# pySwitchmate|pySwitchmate|g" ${requirement_file}
sed -i "s|# face_recognition|face_recognition|g" ${requirement_file}
sed -i "s|# bme680|bme680|g" ${requirement_file}
sed -i "s|# python-gammu|python-gammu|g" ${requirement_file}
sed -i "s|# homeassistant-pyozw|homeassistant-pyozw|g" ${requirement_file}
done
- name: Build wheels
@ -170,5 +163,5 @@ jobs:
pip: "Cython;numpy;scikit-build"
skip-binary: aiohttp,grpcio
constraints: "homeassistant/package_constraints.txt"
requirements-diff: 'requirements_diff.txt'
requirements-diff: "requirements_diff.txt"
requirements: "requirements_all.txt"

View File

@ -1,11 +1,11 @@
repos:
- repo: https://github.com/asottile/pyupgrade
rev: v2.31.0
rev: v2.31.1
hooks:
- id: pyupgrade
args: [--py39-plus]
- repo: https://github.com/psf/black
rev: 22.1.0
rev: 22.3.0
hooks:
- id: black
args:
@ -31,12 +31,12 @@ repos:
- pyflakes==2.4.0
- flake8-docstrings==1.6.0
- pydocstyle==6.1.1
- flake8-comprehensions==3.7.0
- flake8-comprehensions==3.8.0
- flake8-noqa==1.2.1
- mccabe==0.6.1
files: ^(homeassistant|script|tests)/.+\.py$
- repo: https://github.com/PyCQA/bandit
rev: 1.7.0
rev: 1.7.4
hooks:
- id: bandit
args:
@ -45,7 +45,7 @@ repos:
- --configfile=tests/bandit.yaml
files: ^(homeassistant|script|tests)/.+\.py$
- repo: https://github.com/PyCQA/isort
rev: 5.10.0
rev: 5.10.1
hooks:
- id: isort
- repo: https://github.com/pre-commit/pre-commit-hooks
@ -65,10 +65,9 @@ repos:
hooks:
- id: yamllint
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v2.2.1
rev: v2.6.1
hooks:
- id: prettier
stages: [manual]
- repo: https://github.com/cdce8p/python-typing-update
rev: v0.3.5
hooks:

View File

@ -2,4 +2,3 @@
azure-*.yml
docs/source/_templates/*
homeassistant/components/*/translations/*.json
tests/fixtures/*

View File

@ -28,10 +28,12 @@ homeassistant.util.unit_system
# --- Add components below this line ---
homeassistant.components
homeassistant.components.alert.*
homeassistant.components.abode.*
homeassistant.components.acer_projector.*
homeassistant.components.accuweather.*
homeassistant.components.actiontec.*
homeassistant.components.adguard.*
homeassistant.components.aftership.*
homeassistant.components.air_quality.*
homeassistant.components.airly.*
@ -45,6 +47,7 @@ homeassistant.components.amcrest.*
homeassistant.components.ampio.*
homeassistant.components.aseko_pool_live.*
homeassistant.components.automation.*
homeassistant.components.backup.*
homeassistant.components.binary_sensor.*
homeassistant.components.bluetooth_tracker.*
homeassistant.components.bmw_connected_drive.*
@ -68,6 +71,7 @@ homeassistant.components.device_automation.*
homeassistant.components.device_tracker.*
homeassistant.components.devolo_home_control.*
homeassistant.components.devolo_home_network.*
homeassistant.components.dhcp.*
homeassistant.components.dlna_dmr.*
homeassistant.components.dnsip.*
homeassistant.components.dsmr.*
@ -78,6 +82,7 @@ homeassistant.components.esphome.*
homeassistant.components.energy.*
homeassistant.components.evil_genius_labs.*
homeassistant.components.fastdotcom.*
homeassistant.components.filesize.*
homeassistant.components.fitbit.*
homeassistant.components.flunearyou.*
homeassistant.components.flux_led.*
@ -94,6 +99,14 @@ homeassistant.components.group.*
homeassistant.components.guardian.*
homeassistant.components.history.*
homeassistant.components.homeassistant.triggers.event
homeassistant.components.homekit
homeassistant.components.homekit.accessories
homeassistant.components.homekit.aidmanager
homeassistant.components.homekit.config_flow
homeassistant.components.homekit.diagnostics
homeassistant.components.homekit.logbook
homeassistant.components.homekit.type_triggers
homeassistant.components.homekit.util
homeassistant.components.homekit_controller
homeassistant.components.homekit_controller.alarm_control_panel
homeassistant.components.homekit_controller.button
@ -114,6 +127,7 @@ homeassistant.components.isy994.*
homeassistant.components.iqvia.*
homeassistant.components.jellyfin.*
homeassistant.components.jewish_calendar.*
homeassistant.components.kaleidescape.*
homeassistant.components.knx.*
homeassistant.components.kraken.*
homeassistant.components.lametric.*
@ -125,10 +139,11 @@ homeassistant.components.lookin.*
homeassistant.components.luftdaten.*
homeassistant.components.mailbox.*
homeassistant.components.media_player.*
homeassistant.components.media_source.*
homeassistant.components.mjpeg.*
homeassistant.components.modbus.*
homeassistant.components.modem_callerid.*
homeassistant.components.media_source.*
homeassistant.components.moon.*
homeassistant.components.mysensors.*
homeassistant.components.nam.*
homeassistant.components.nanoleaf.*
@ -146,6 +161,7 @@ homeassistant.components.oncue.*
homeassistant.components.onewire.*
homeassistant.components.open_meteo.*
homeassistant.components.openuv.*
homeassistant.components.peco.*
homeassistant.components.overkiz.*
homeassistant.components.persistent_notification.*
homeassistant.components.pi_hole.*
@ -156,9 +172,18 @@ homeassistant.components.pure_energie.*
homeassistant.components.rainmachine.*
homeassistant.components.rdw.*
homeassistant.components.recollect_waste.*
homeassistant.components.recorder
homeassistant.components.recorder.const
homeassistant.components.recorder.backup
homeassistant.components.recorder.executor
homeassistant.components.recorder.history
homeassistant.components.recorder.models
homeassistant.components.recorder.pool
homeassistant.components.recorder.purge
homeassistant.components.recorder.repack
homeassistant.components.recorder.statistics
homeassistant.components.recorder.util
homeassistant.components.recorder.websocket_api
homeassistant.components.remote.*
homeassistant.components.renault.*
homeassistant.components.ridwell.*
@ -202,8 +227,10 @@ homeassistant.components.tts.*
homeassistant.components.twentemilieu.*
homeassistant.components.unifiprotect.*
homeassistant.components.upcloud.*
homeassistant.components.update.*
homeassistant.components.uptime.*
homeassistant.components.uptimerobot.*
homeassistant.components.usb.*
homeassistant.components.vacuum.*
homeassistant.components.vallox.*
homeassistant.components.velbus.*
@ -217,6 +244,8 @@ homeassistant.components.websocket_api.*
homeassistant.components.wemo.*
homeassistant.components.whois.*
homeassistant.components.wiz.*
homeassistant.components.worldclock.*
homeassistant.components.yale_smart_alarm.*
homeassistant.components.zodiac.*
homeassistant.components.zeroconf.*
homeassistant.components.zone.*

2313
CODEOWNERS

File diff suppressed because it is too large Load Diff

View File

@ -15,8 +15,7 @@ RUN \
-r homeassistant/requirements.txt --use-deprecated=legacy-resolver
COPY requirements_all.txt homeassistant/
RUN \
sed -i "s|# homeassistant-pyozw|homeassistant-pyozw|g" homeassistant/requirements_all.txt \
&& pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \
pip3 install --no-cache-dir --no-index --only-binary=:all: --find-links "${WHEELS_LINKS}" \
-r homeassistant/requirements_all.txt --use-deprecated=legacy-resolver
## Setup Home Assistant Core

View File

@ -4,13 +4,15 @@ from __future__ import annotations
from enum import Enum
from typing import Any, TypeVar
T = TypeVar("T", bound="StrEnum")
_StrEnumT = TypeVar("_StrEnumT", bound="StrEnum")
class StrEnum(str, Enum):
"""Partial backport of Python 3.11's StrEnum for our basic use cases."""
def __new__(cls: type[T], value: str, *args: Any, **kwargs: Any) -> T:
def __new__(
cls: type[_StrEnumT], value: str, *args: Any, **kwargs: Any
) -> _StrEnumT:
"""Create a new StrEnum instance."""
if not isinstance(value, str):
raise TypeError(f"{value!r} is not a string")
@ -21,7 +23,7 @@ class StrEnum(str, Enum):
return str(self.value)
@staticmethod
def _generate_next_value_( # pylint: disable=arguments-differ # https://github.com/PyCQA/pylint/issues/5371
def _generate_next_value_(
name: str, start: int, count: int, last_values: list[Any]
) -> Any:
"""

View File

@ -26,7 +26,6 @@
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_mfa_code": "Invalid MFA code"
},
"abort": {
"single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]",

View File

@ -6,7 +6,7 @@
},
"error": {
"cannot_connect": "\u00c9chec de connexion",
"invalid_auth": "Authentification invalide",
"invalid_auth": "Authentification non valide",
"invalid_mfa_code": "Code MFA non valide"
},
"step": {
@ -19,14 +19,14 @@
"reauth_confirm": {
"data": {
"password": "Mot de passe",
"username": "Email"
"username": "Courriel"
},
"title": "Remplissez vos informations de connexion Abode"
},
"user": {
"data": {
"password": "Mot de passe",
"username": "Email"
"username": "Courriel"
},
"title": "Remplissez vos informations de connexion Abode"
}

View File

@ -0,0 +1,28 @@
"""Diagnostics support for AccuWeather."""
from __future__ import annotations
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import HomeAssistant
from . import AccuWeatherDataUpdateCoordinator
from .const import DOMAIN
TO_REDACT = {CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE}
async def async_get_config_entry_diagnostics(
hass: HomeAssistant, config_entry: ConfigEntry
) -> dict:
"""Return diagnostics for a config entry."""
coordinator: AccuWeatherDataUpdateCoordinator = hass.data[DOMAIN][
config_entry.entry_id
]
diagnostics_data = {
"config_entry_data": async_redact_data(dict(config_entry.data), TO_REDACT),
"coordinator_data": coordinator.data,
}
return diagnostics_data

View File

@ -58,11 +58,12 @@ async def async_setup_entry(
async_add_entities(sensors)
class AccuWeatherSensor(CoordinatorEntity, SensorEntity):
class AccuWeatherSensor(
CoordinatorEntity[AccuWeatherDataUpdateCoordinator], SensorEntity
):
"""Define an AccuWeather entity."""
_attr_attribution = ATTRIBUTION
coordinator: AccuWeatherDataUpdateCoordinator
entity_description: AccuWeatherSensorDescription
def __init__(

View File

@ -2,8 +2,6 @@
"config": {
"step": {
"user": {
"title": "AccuWeather",
"description": "If you need help with the configuration have a look here: https://www.home-assistant.io/integrations/accuweather/\n\nSome sensors are not enabled by default. You can enable them in the entity registry after the integration configuration.\nWeather forecast is not enabled by default. You can enable it in the integration options.",
"data": {
"name": "[%key:common::config_flow::data::name%]",
"api_key": "[%key:common::config_flow::data::api_key%]",
@ -12,6 +10,9 @@
}
}
},
"create_entry": {
"default": "Some sensors are not enabled by default. You can enable them in the entity registry after the integration configuration.\nWeather forecast is not enabled by default. You can enable it in the integration options."
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
@ -24,7 +25,6 @@
"options": {
"step": {
"user": {
"title": "AccuWeather Options",
"description": "Due to the limitations of the free version of the AccuWeather API key, when you enable weather forecast, data updates will be performed every 80 minutes instead of every 40 minutes.",
"data": {
"forecast": "Weather forecast"

View File

@ -16,7 +16,7 @@
"longitude": "Longitud",
"name": "Nombre"
},
"description": "Si necesitas ayuda con la configuraci\u00f3n, echa un vistazo aqu\u00ed: https://www.home-assistant.io/integrations/accuweather/ \n\nEl pron\u00f3stico del tiempo no est\u00e1 habilitado por defecto. Puedes habilitarlo en las opciones de la integraci\u00f3n.",
"description": "Si necesitas ayuda con la configuraci\u00f3n, echa un vistazo aqu\u00ed: https://www.home-assistant.io/integrations/accuweather/ \n\nAlgunos sensores no est\u00e1n habilitados por defecto. Los puedes habilitar en el registro de entidades despu\u00e9s de configurar la integraci\u00f3n.\nEl pron\u00f3stico del tiempo no est\u00e1 habilitado por defecto. Puedes habilitarlo en las opciones de la integraci\u00f3n.",
"title": "AccuWeather"
}
}

View File

@ -5,7 +5,7 @@
},
"error": {
"cannot_connect": "\u00c9chec de connexion",
"invalid_api_key": "Cl\u00e9 API invalide",
"invalid_api_key": "Cl\u00e9 d'API non valide",
"requests_exceeded": "Le nombre autoris\u00e9 de requ\u00eates adress\u00e9es \u00e0 l'API AccuWeather a \u00e9t\u00e9 d\u00e9pass\u00e9. Vous devez attendre ou modifier la cl\u00e9 API."
},
"step": {

View File

@ -56,11 +56,11 @@ async def async_setup_entry(
async_add_entities([AccuWeatherEntity(name, coordinator)])
class AccuWeatherEntity(CoordinatorEntity, WeatherEntity):
class AccuWeatherEntity(
CoordinatorEntity[AccuWeatherDataUpdateCoordinator], WeatherEntity
):
"""Define an AccuWeather entity."""
coordinator: AccuWeatherDataUpdateCoordinator
def __init__(
self, name: str, coordinator: AccuWeatherDataUpdateCoordinator
) -> None:

View File

@ -3,12 +3,8 @@
"name": "Adax",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/adax",
"requirements": [
"adax==0.2.0", "Adax-local==0.1.3"
],
"codeowners": [
"@danielhiversen"
],
"requirements": ["adax==0.2.0", "Adax-local==0.1.3"],
"codeowners": ["@danielhiversen"],
"iot_class": "local_polling",
"loggers": ["adax", "adax_local"]
}

View File

@ -4,23 +4,23 @@
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9",
"heater_not_available": "Chauffage non disponible. Essayez de r\u00e9initialiser le chauffage en appuyant sur + et OK pendant quelques secondes.",
"heater_not_found": "Chauffage introuvable. Essayez de rapprocher le radiateur de l'ordinateur Home Assistant.",
"invalid_auth": "Authentification invalide"
"invalid_auth": "Authentification non valide"
},
"error": {
"cannot_connect": "\u00c9chec de connexion",
"invalid_auth": "Authentification invalide"
"invalid_auth": "Authentification non valide"
},
"step": {
"cloud": {
"data": {
"account_id": "Identifiant de compte",
"password": "Mot der passe"
"password": "Mot de passe"
}
},
"local": {
"data": {
"wifi_pswd": "Mot de passe WiFi",
"wifi_ssid": "identifiant Wifi"
"wifi_pswd": "Mot de passe Wi-Fi",
"wifi_ssid": "R\u00e9seau Wi-Fi"
},
"description": "R\u00e9initialisez le radiateur en appuyant sur + et OK jusqu'\u00e0 ce que l'\u00e9cran affiche \u00ab\u00a0Reset\u00a0\u00bb. Appuyez ensuite sur le bouton OK du radiateur et maintenez-le enfonc\u00e9 jusqu'\u00e0 ce que le voyant bleu commence \u00e0 clignoter avant d'appuyer sur Soumettre. La configuration du chauffage peut prendre quelques minutes."
},

View File

@ -27,11 +27,11 @@
"user": {
"data": {
"account_id": "\u5e33\u865f ID",
"connection_type": "\u9078\u64c7\u9023\u7dda\u985e\u578b",
"connection_type": "\u9078\u64c7\u9023\u7dda\u985e\u5225",
"host": "\u4e3b\u6a5f\u7aef",
"password": "\u5bc6\u78bc"
},
"description": "\u9078\u64c7\u9023\u7dda\u985e\u578b\u3002\u672c\u5730\u7aef\u5c07\u9700\u8981\u5177\u5099\u85cd\u82bd\u52a0\u71b1\u5668"
"description": "\u9078\u64c7\u9023\u7dda\u985e\u5225\u3002\u672c\u5730\u7aef\u5c07\u9700\u8981\u5177\u5099\u85cd\u82bd\u52a0\u71b1\u5668"
}
}
}

View File

@ -27,7 +27,7 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
VERSION = 1
_hassio_discovery = None
_hassio_discovery: dict[str, Any] | None = None
async def _show_setup_form(
self, errors: dict[str, str] | None = None
@ -56,7 +56,6 @@ class AdGuardHomeFlowHandler(ConfigFlow, domain=DOMAIN):
return self.async_show_form(
step_id="hassio_confirm",
description_placeholders={"addon": self._hassio_discovery["addon"]},
data_schema=vol.Schema({}),
errors=errors or {},
)

View File

@ -3,6 +3,7 @@ from __future__ import annotations
from datetime import timedelta
import logging
from typing import Any
from adguardhome import AdGuardHome, AdGuardHomeConnectionError, AdGuardHomeError
@ -76,7 +77,7 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity):
"""Return the state of the switch."""
return self._state
async def async_turn_off(self, **kwargs) -> None:
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn off the switch."""
try:
await self._adguard_turn_off()
@ -88,7 +89,7 @@ class AdGuardHomeSwitch(AdGuardHomeDeviceEntity, SwitchEntity):
"""Turn off the switch."""
raise NotImplementedError()
async def async_turn_on(self, **kwargs) -> None:
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn on the switch."""
try:
await self._adguard_turn_on()

View File

@ -18,12 +18,12 @@ write_data_by_name:
selector:
select:
options:
- 'bool'
- 'byte'
- 'dint'
- 'int'
- 'udint'
- 'uint'
- "bool"
- "byte"
- "dint"
- "int"
- "udint"
- "uint"
value:
name: Value
description: The value to write to the variable.

View File

@ -3,15 +3,9 @@
"name": "Advantage Air",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/advantage_air",
"codeowners": [
"@Bre77"
],
"requirements": [
"advantage_air==0.3.1"
],
"codeowners": ["@Bre77"],
"requirements": ["advantage_air==0.3.1"],
"quality_scale": "platinum",
"iot_class": "local_polling",
"loggers": [
"advantage_air"
]
"loggers": ["advantage_air"]
}

View File

@ -64,7 +64,7 @@ async def async_setup_entry(
async_add_entities(entities)
class AbstractAemetSensor(CoordinatorEntity, SensorEntity):
class AbstractAemetSensor(CoordinatorEntity[WeatherUpdateCoordinator], SensorEntity):
"""Abstract class for an AEMET OpenData sensor."""
_attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}

View File

@ -14,8 +14,7 @@
"longitude": "[%key:common::config_flow::data::longitude%]",
"name": "Name of the integration"
},
"description": "Set up AEMET OpenData integration. To generate API key go to https://opendata.aemet.es/centrodedescargas/altaUsuario",
"title": "AEMET OpenData"
"description": "To generate API key go to https://opendata.aemet.es/centrodedescargas/altaUsuario"
}
}
},

View File

@ -4,7 +4,7 @@
"already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9"
},
"error": {
"invalid_api_key": "Cl\u00e9 API invalide"
"invalid_api_key": "Cl\u00e9 d'API non valide"
},
"step": {
"user": {

View File

@ -43,7 +43,7 @@ async def async_setup_entry(
async_add_entities(entities, False)
class AemetWeather(CoordinatorEntity, WeatherEntity):
class AemetWeather(CoordinatorEntity[WeatherUpdateCoordinator], WeatherEntity):
"""Implementation of an AEMET OpenData sensor."""
_attr_attribution = ATTRIBUTION

View File

@ -2,9 +2,7 @@
"domain": "aftership",
"name": "AfterShip",
"documentation": "https://www.home-assistant.io/integrations/aftership",
"requirements": [
"pyaftership==21.11.0"
],
"requirements": ["pyaftership==21.11.0"],
"codeowners": [],
"iot_class": "cloud_polling"
}

View File

@ -15,7 +15,10 @@ from homeassistant.const import CONF_API_KEY, CONF_NAME
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers.aiohttp_client import async_get_clientsession
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_send
from homeassistant.helpers.dispatcher import (
async_dispatcher_connect,
async_dispatcher_send,
)
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.util import Throttle
@ -128,9 +131,7 @@ class AfterShipSensor(SensorEntity):
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
self.async_on_remove(
self.hass.helpers.dispatcher.async_dispatcher_connect(
UPDATE_TOPIC, self._force_update
)
async_dispatcher_connect(self.hass, UPDATE_TOPIC, self._force_update)
)
async def _force_update(self) -> None:

View File

@ -2,6 +2,6 @@
"domain": "air_quality",
"name": "Air Quality",
"documentation": "https://www.home-assistant.io/integrations/air_quality",
"codeowners": [],
"codeowners": ["@home-assistant/core"],
"quality_scale": "internal"
}

View File

@ -134,10 +134,9 @@ async def async_setup_entry(
async_add_entities(sensors, False)
class AirlySensor(CoordinatorEntity, SensorEntity):
class AirlySensor(CoordinatorEntity[AirlyDataUpdateCoordinator], SensorEntity):
"""Define an Airly sensor."""
coordinator: AirlyDataUpdateCoordinator
entity_description: AirlySensorEntityDescription
def __init__(

View File

@ -2,8 +2,7 @@
"config": {
"step": {
"user": {
"title": "Airly",
"description": "Set up Airly air quality integration. To generate API key go to https://developer.airly.eu/register",
"description": "To generate API key go to https://developer.airly.eu/register",
"data": {
"name": "[%key:common::config_flow::data::name%]",
"api_key": "[%key:common::config_flow::data::api_key%]",

View File

@ -4,7 +4,7 @@
"already_configured": "L'emplacement est d\u00e9j\u00e0 configur\u00e9"
},
"error": {
"invalid_api_key": "Cl\u00e9 API invalide",
"invalid_api_key": "Cl\u00e9 d'API non valide",
"wrong_location": "Aucune station de mesure Airly dans cette zone."
},
"step": {

View File

@ -70,11 +70,9 @@ async def async_setup_entry(
async_add_entities(entities, False)
class AirNowSensor(CoordinatorEntity, SensorEntity):
class AirNowSensor(CoordinatorEntity[AirNowDataUpdateCoordinator], SensorEntity):
"""Define an AirNow sensor."""
coordinator: AirNowDataUpdateCoordinator
def __init__(
self,
coordinator: AirNowDataUpdateCoordinator,

View File

@ -2,8 +2,7 @@
"config": {
"step": {
"user": {
"title": "AirNow",
"description": "Set up AirNow air quality integration. To generate API key go to https://docs.airnowapi.org/account/request/",
"description": "To generate API key go to https://docs.airnowapi.org/account/request/",
"data": {
"api_key": "[%key:common::config_flow::data::api_key%]",
"latitude": "[%key:common::config_flow::data::latitude%]",

View File

@ -5,7 +5,7 @@
},
"error": {
"cannot_connect": "\u00c9chec de connexion",
"invalid_auth": "Authentification invalide",
"invalid_auth": "Authentification non valide",
"invalid_location": "Aucun r\u00e9sultat trouv\u00e9 pour cet emplacement",
"unknown": "Erreur inattendue"
},

View File

@ -4,9 +4,7 @@
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airthings",
"requirements": ["airthings_cloud==0.1.0"],
"codeowners": [
"@danielhiversen"
],
"codeowners": ["@danielhiversen"],
"iot_class": "cloud_polling",
"loggers": ["airthings"]
}

View File

@ -5,7 +5,7 @@
},
"error": {
"cannot_connect": "\u00c9chec de connexion",
"invalid_auth": "Authentification invalide",
"invalid_auth": "Authentification non valide",
"unknown": "Erreur inattendue"
},
"step": {

View File

@ -3,12 +3,8 @@
"name": "AirTouch 4",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airtouch4",
"requirements": [
"airtouch4pyapi==1.0.5"
],
"codeowners": [
"@LonePurpleWolf"
],
"requirements": ["airtouch4pyapi==1.0.5"],
"codeowners": ["@LonePurpleWolf"],
"iot_class": "local_polling",
"loggers": ["airtouch4pyapi"]
}

View File

@ -7,7 +7,7 @@
"error": {
"cannot_connect": "\u00c9chec de connexion",
"general_error": "Erreur inattendue",
"invalid_api_key": "Cl\u00e9 API invalide",
"invalid_api_key": "Cl\u00e9 d'API non valide",
"location_not_found": "Emplacement introuvable"
},
"step": {
@ -25,7 +25,7 @@
"api_key": "Cl\u00e9 d'API",
"city": "Ville",
"country": "Pays",
"state": "Etat"
"state": "\u00c9tat"
},
"description": "Utilisez l'API cloud AirVisual pour surveiller une ville / un \u00e9tat / un pays.",
"title": "Configurer un lieu g\u00e9ographique"

View File

@ -11,7 +11,7 @@
"airvisual__pollutant_level": {
"good": "Bon",
"hazardous": "Hasardeux",
"moderate": "Mod\u00e9rer",
"moderate": "Mod\u00e9r\u00e9",
"unhealthy": "Malsain",
"unhealthy_sensitive": "Malsain pour les groupes sensibles",
"very_unhealthy": "Tr\u00e8s malsain"

View File

@ -2,7 +2,8 @@
"state": {
"airvisual__pollutant_level": {
"good": "\u05d8\u05d5\u05d1",
"unhealthy": "\u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0"
"unhealthy": "\u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0",
"unhealthy_sensitive": "\u05dc\u05d0 \u05d1\u05e8\u05d9\u05d0 \u05dc\u05e7\u05d1\u05d5\u05e6\u05d5\u05ea \u05e8\u05d2\u05d9\u05e9\u05d5\u05ea"
}
}
}

View File

@ -45,7 +45,7 @@
"title": "\u91cd\u65b0\u8a8d\u8b49 AirVisual"
},
"user": {
"description": "\u9078\u64c7\u6240\u8981\u76e3\u63a7\u7684 AirVisual \u8cc7\u6599\u985e\u578b\u3002",
"description": "\u9078\u64c7\u6240\u8981\u76e3\u63a7\u7684 AirVisual \u8cc7\u6599\u985e\u5225\u3002",
"title": "\u8a2d\u5b9a AirVisual"
}
}

View File

@ -0,0 +1,89 @@
"""The Airzone integration."""
from __future__ import annotations
from typing import Any
from aioairzone.common import ConnectionOptions
from aioairzone.const import (
AZD_ID,
AZD_NAME,
AZD_SYSTEM,
AZD_THERMOSTAT_FW,
AZD_THERMOSTAT_MODEL,
AZD_ZONES,
)
from aioairzone.localapi import AirzoneLocalApi
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PORT, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import aiohttp_client
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN, MANUFACTURER
from .coordinator import AirzoneUpdateCoordinator
PLATFORMS: list[Platform] = [Platform.BINARY_SENSOR, Platform.CLIMATE, Platform.SENSOR]
class AirzoneEntity(CoordinatorEntity[AirzoneUpdateCoordinator]):
"""Define an Airzone entity."""
def __init__(
self,
coordinator: AirzoneUpdateCoordinator,
entry: ConfigEntry,
system_zone_id: str,
zone_data: dict[str, Any],
) -> None:
"""Initialize."""
super().__init__(coordinator)
self.system_id = zone_data[AZD_SYSTEM]
self.system_zone_id = system_zone_id
self.zone_id = zone_data[AZD_ID]
self._attr_device_info: DeviceInfo = {
"identifiers": {(DOMAIN, f"{entry.entry_id}_{system_zone_id}")},
"manufacturer": MANUFACTURER,
"model": self.get_zone_value(AZD_THERMOSTAT_MODEL),
"name": f"Airzone [{system_zone_id}] {zone_data[AZD_NAME]}",
"sw_version": self.get_zone_value(AZD_THERMOSTAT_FW),
}
def get_zone_value(self, key):
"""Return zone value by key."""
value = None
if self.system_zone_id in self.coordinator.data[AZD_ZONES]:
zone = self.coordinator.data[AZD_ZONES][self.system_zone_id]
if key in zone:
value = zone[key]
return value
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up Airzone from a config entry."""
options = ConnectionOptions(
entry.data[CONF_HOST],
entry.data[CONF_PORT],
)
airzone = AirzoneLocalApi(aiohttp_client.async_get_clientsession(hass), options)
coordinator = AirzoneUpdateCoordinator(hass, airzone)
await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = coordinator
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
if unload_ok := await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View File

@ -0,0 +1,114 @@
"""Support for the Airzone sensors."""
from __future__ import annotations
from collections.abc import Mapping
from dataclasses import dataclass
from typing import Any, Final
from aioairzone.const import (
AZD_AIR_DEMAND,
AZD_ERRORS,
AZD_FLOOR_DEMAND,
AZD_NAME,
AZD_PROBLEMS,
AZD_ZONES,
)
from homeassistant.components.binary_sensor import (
DEVICE_CLASS_PROBLEM,
DEVICE_CLASS_RUNNING,
BinarySensorEntity,
BinarySensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirzoneEntity
from .const import DOMAIN
from .coordinator import AirzoneUpdateCoordinator
@dataclass
class AirzoneBinarySensorEntityDescription(BinarySensorEntityDescription):
"""A class that describes airzone binary sensor entities."""
attributes: dict[str, str] | None = None
BINARY_SENSOR_TYPES: Final[tuple[AirzoneBinarySensorEntityDescription, ...]] = (
AirzoneBinarySensorEntityDescription(
device_class=DEVICE_CLASS_RUNNING,
key=AZD_AIR_DEMAND,
name="Air Demand",
),
AirzoneBinarySensorEntityDescription(
device_class=DEVICE_CLASS_RUNNING,
key=AZD_FLOOR_DEMAND,
name="Floor Demand",
),
AirzoneBinarySensorEntityDescription(
attributes={
"errors": AZD_ERRORS,
},
device_class=DEVICE_CLASS_PROBLEM,
entity_category=EntityCategory.DIAGNOSTIC,
key=AZD_PROBLEMS,
name="Problem",
),
)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Add Airzone binary sensors from a config_entry."""
coordinator = hass.data[DOMAIN][entry.entry_id]
binary_sensors = []
for system_zone_id, zone_data in coordinator.data[AZD_ZONES].items():
for description in BINARY_SENSOR_TYPES:
if description.key in zone_data:
binary_sensors.append(
AirzoneBinarySensor(
coordinator,
description,
entry,
system_zone_id,
zone_data,
)
)
async_add_entities(binary_sensors)
class AirzoneBinarySensor(AirzoneEntity, BinarySensorEntity):
"""Define an Airzone sensor."""
def __init__(
self,
coordinator: AirzoneUpdateCoordinator,
description: AirzoneBinarySensorEntityDescription,
entry: ConfigEntry,
system_zone_id: str,
zone_data: dict[str, Any],
) -> None:
"""Initialize."""
super().__init__(coordinator, entry, system_zone_id, zone_data)
self._attr_name = f"{zone_data[AZD_NAME]} {description.name}"
self._attr_unique_id = f"{entry.entry_id}_{system_zone_id}_{description.key}"
self.attributes = description.attributes
self.entity_description = description
@property
def extra_state_attributes(self) -> Mapping[str, Any] | None:
"""Return state attributes."""
if not self.attributes:
return None
return {key: self.get_zone_value(val) for key, val in self.attributes.items()}
@property
def is_on(self) -> bool | None:
"""Return true if the binary sensor is on."""
return self.get_zone_value(self.entity_description.key)

View File

@ -0,0 +1,191 @@
"""Support for the Airzone climate."""
from __future__ import annotations
import logging
from typing import Final
from aioairzone.common import OperationMode
from aioairzone.const import (
API_MODE,
API_ON,
API_SET_POINT,
API_SYSTEM_ID,
API_ZONE_ID,
AZD_DEMAND,
AZD_HUMIDITY,
AZD_MASTER,
AZD_MODE,
AZD_MODES,
AZD_NAME,
AZD_ON,
AZD_TEMP,
AZD_TEMP_MAX,
AZD_TEMP_MIN,
AZD_TEMP_SET,
AZD_TEMP_UNIT,
AZD_ZONES,
)
from aioairzone.exceptions import AirzoneError
from aiohttp.client_exceptions import ClientConnectorError
from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import (
CURRENT_HVAC_COOL,
CURRENT_HVAC_DRY,
CURRENT_HVAC_FAN,
CURRENT_HVAC_HEAT,
CURRENT_HVAC_IDLE,
CURRENT_HVAC_OFF,
HVAC_MODE_COOL,
HVAC_MODE_DRY,
HVAC_MODE_FAN_ONLY,
HVAC_MODE_HEAT,
HVAC_MODE_HEAT_COOL,
HVAC_MODE_OFF,
SUPPORT_TARGET_TEMPERATURE,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE
from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirzoneEntity
from .const import API_TEMPERATURE_STEP, DOMAIN, TEMP_UNIT_LIB_TO_HASS
from .coordinator import AirzoneUpdateCoordinator
_LOGGER = logging.getLogger(__name__)
HVAC_ACTION_LIB_TO_HASS: Final[dict[OperationMode, str]] = {
OperationMode.STOP: CURRENT_HVAC_OFF,
OperationMode.COOLING: CURRENT_HVAC_COOL,
OperationMode.HEATING: CURRENT_HVAC_HEAT,
OperationMode.FAN: CURRENT_HVAC_FAN,
OperationMode.DRY: CURRENT_HVAC_DRY,
}
HVAC_MODE_LIB_TO_HASS: Final[dict[OperationMode, str]] = {
OperationMode.STOP: HVAC_MODE_OFF,
OperationMode.COOLING: HVAC_MODE_COOL,
OperationMode.HEATING: HVAC_MODE_HEAT,
OperationMode.FAN: HVAC_MODE_FAN_ONLY,
OperationMode.DRY: HVAC_MODE_DRY,
OperationMode.AUTO: HVAC_MODE_HEAT_COOL,
}
HVAC_MODE_HASS_TO_LIB: Final[dict[str, OperationMode]] = {
HVAC_MODE_OFF: OperationMode.STOP,
HVAC_MODE_COOL: OperationMode.COOLING,
HVAC_MODE_HEAT: OperationMode.HEATING,
HVAC_MODE_FAN_ONLY: OperationMode.FAN,
HVAC_MODE_DRY: OperationMode.DRY,
HVAC_MODE_HEAT_COOL: OperationMode.AUTO,
}
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Add Airzone sensors from a config_entry."""
coordinator = hass.data[DOMAIN][entry.entry_id]
async_add_entities(
AirzoneClimate(
coordinator,
entry,
system_zone_id,
zone_data,
)
for system_zone_id, zone_data in coordinator.data[AZD_ZONES].items()
)
class AirzoneClimate(AirzoneEntity, ClimateEntity):
"""Define an Airzone sensor."""
def __init__(
self,
coordinator: AirzoneUpdateCoordinator,
entry: ConfigEntry,
system_zone_id: str,
zone_data: dict,
) -> None:
"""Initialize Airzone climate entity."""
super().__init__(coordinator, entry, system_zone_id, zone_data)
self._attr_name = f"{zone_data[AZD_NAME]}"
self._attr_unique_id = f"{entry.entry_id}_{system_zone_id}"
self._attr_supported_features = SUPPORT_TARGET_TEMPERATURE
self._attr_target_temperature_step = API_TEMPERATURE_STEP
self._attr_max_temp = self.get_zone_value(AZD_TEMP_MAX)
self._attr_min_temp = self.get_zone_value(AZD_TEMP_MIN)
self._attr_temperature_unit = TEMP_UNIT_LIB_TO_HASS[
self.get_zone_value(AZD_TEMP_UNIT)
]
self._attr_hvac_modes = [
HVAC_MODE_LIB_TO_HASS[mode] for mode in self.get_zone_value(AZD_MODES)
]
self._async_update_attrs()
async def _async_update_hvac_params(self, params) -> None:
"""Send HVAC parameters to API."""
try:
await self.coordinator.airzone.put_hvac(params)
except (AirzoneError, ClientConnectorError) as error:
raise HomeAssistantError(
f"Failed to set zone {self.name}: {error}"
) from error
else:
self.coordinator.async_set_updated_data(self.coordinator.airzone.data())
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
"""Set hvac mode."""
params = {
API_SYSTEM_ID: self.system_id,
API_ZONE_ID: self.zone_id,
}
if hvac_mode == HVAC_MODE_OFF:
params[API_ON] = 0
else:
mode = HVAC_MODE_HASS_TO_LIB[hvac_mode]
if mode != self.get_zone_value(AZD_MODE):
if self.get_zone_value(AZD_MASTER):
params[API_MODE] = mode
else:
raise HomeAssistantError(
f"Mode can't be changed on slave zone {self.name}"
)
params[API_ON] = 1
_LOGGER.debug("Set hvac_mode=%s params=%s", hvac_mode, params)
await self._async_update_hvac_params(params)
async def async_set_temperature(self, **kwargs) -> None:
"""Set new target temperature."""
temp = kwargs.get(ATTR_TEMPERATURE)
params = {
API_SYSTEM_ID: self.system_id,
API_ZONE_ID: self.zone_id,
API_SET_POINT: temp,
}
_LOGGER.debug("Set temp=%s params=%s", temp, params)
await self._async_update_hvac_params(params)
@callback
def _handle_coordinator_update(self) -> None:
"""Update attributes when the coordinator updates."""
self._async_update_attrs()
super()._handle_coordinator_update()
@callback
def _async_update_attrs(self) -> None:
"""Update climate attributes."""
self._attr_current_temperature = self.get_zone_value(AZD_TEMP)
self._attr_current_humidity = self.get_zone_value(AZD_HUMIDITY)
if self.get_zone_value(AZD_ON):
mode = self.get_zone_value(AZD_MODE)
self._attr_hvac_mode = HVAC_MODE_LIB_TO_HASS[mode]
if self.get_zone_value(AZD_DEMAND):
self._attr_hvac_action = HVAC_ACTION_LIB_TO_HASS[mode]
else:
self._attr_hvac_action = CURRENT_HVAC_IDLE
else:
self._attr_hvac_action = CURRENT_HVAC_OFF
self._attr_hvac_mode = HVAC_MODE_OFF
self._attr_target_temperature = self.get_zone_value(AZD_TEMP_SET)

View File

@ -0,0 +1,62 @@
"""Config flow for Airzone."""
from __future__ import annotations
from typing import Any
from aioairzone.common import ConnectionOptions
from aioairzone.exceptions import InvalidHost
from aioairzone.localapi import AirzoneLocalApi
from aiohttp.client_exceptions import ClientConnectorError
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.data_entry_flow import FlowResult
from homeassistant.helpers import aiohttp_client
from .const import DEFAULT_LOCAL_API_PORT, DOMAIN
class ConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle config flow for an Airzone device."""
async def async_step_user(
self, user_input: dict[str, Any] | None = None
) -> FlowResult:
"""Handle the initial step."""
errors = {}
if user_input is not None:
self._async_abort_entries_match(
{
CONF_HOST: user_input[CONF_HOST],
CONF_PORT: user_input[CONF_PORT],
}
)
airzone = AirzoneLocalApi(
aiohttp_client.async_get_clientsession(self.hass),
ConnectionOptions(
user_input[CONF_HOST],
user_input[CONF_PORT],
),
)
try:
await airzone.validate_airzone()
except (ClientConnectorError, InvalidHost):
errors["base"] = "cannot_connect"
else:
title = f"Airzone {user_input[CONF_HOST]}:{user_input[CONF_PORT]}"
return self.async_create_entry(title=title, data=user_input)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{
vol.Required(CONF_HOST): str,
vol.Required(CONF_PORT, default=DEFAULT_LOCAL_API_PORT): int,
}
),
errors=errors,
)

View File

@ -0,0 +1,19 @@
"""Constants for the Airzone integration."""
from typing import Final
from aioairzone.common import TemperatureUnit
from homeassistant.const import TEMP_CELSIUS, TEMP_FAHRENHEIT
DOMAIN: Final = "airzone"
MANUFACTURER: Final = "Airzone"
AIOAIRZONE_DEVICE_TIMEOUT_SEC: Final = 10
API_TEMPERATURE_STEP: Final = 0.5
DEFAULT_LOCAL_API_PORT: Final = 3000
TEMP_UNIT_LIB_TO_HASS: Final[dict[TemperatureUnit, str]] = {
TemperatureUnit.CELSIUS: TEMP_CELSIUS,
TemperatureUnit.FAHRENHEIT: TEMP_FAHRENHEIT,
}

View File

@ -0,0 +1,42 @@
"""The Airzone integration."""
from __future__ import annotations
from datetime import timedelta
import logging
from aioairzone.localapi import AirzoneLocalApi
from aiohttp.client_exceptions import ClientConnectorError
import async_timeout
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import AIOAIRZONE_DEVICE_TIMEOUT_SEC, DOMAIN
SCAN_INTERVAL = timedelta(seconds=60)
_LOGGER = logging.getLogger(__name__)
class AirzoneUpdateCoordinator(DataUpdateCoordinator):
"""Class to manage fetching data from the Airzone device."""
def __init__(self, hass: HomeAssistant, airzone: AirzoneLocalApi) -> None:
"""Initialize."""
self.airzone = airzone
super().__init__(
hass,
_LOGGER,
name=DOMAIN,
update_interval=SCAN_INTERVAL,
)
async def _async_update_data(self):
"""Update data via library."""
async with async_timeout.timeout(AIOAIRZONE_DEVICE_TIMEOUT_SEC):
try:
await self.airzone.update_airzone()
except ClientConnectorError as error:
raise UpdateFailed(error) from error
return self.airzone.data()

View File

@ -0,0 +1,10 @@
{
"domain": "airzone",
"name": "Airzone",
"config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airzone",
"requirements": ["aioairzone==0.2.3"],
"codeowners": ["@Noltari"],
"iot_class": "local_polling",
"loggers": ["aioairzone"]
}

View File

@ -0,0 +1,93 @@
"""Support for the Airzone sensors."""
from __future__ import annotations
from typing import Any, Final
from aioairzone.const import AZD_HUMIDITY, AZD_NAME, AZD_TEMP, AZD_TEMP_UNIT, AZD_ZONES
from homeassistant.components.sensor import (
STATE_CLASS_MEASUREMENT,
SensorEntity,
SensorEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_TEMPERATURE,
PERCENTAGE,
TEMP_CELSIUS,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import AirzoneEntity
from .const import DOMAIN, TEMP_UNIT_LIB_TO_HASS
from .coordinator import AirzoneUpdateCoordinator
SENSOR_TYPES: Final[tuple[SensorEntityDescription, ...]] = (
SensorEntityDescription(
device_class=DEVICE_CLASS_TEMPERATURE,
key=AZD_TEMP,
name="Temperature",
native_unit_of_measurement=TEMP_CELSIUS,
state_class=STATE_CLASS_MEASUREMENT,
),
SensorEntityDescription(
device_class=DEVICE_CLASS_HUMIDITY,
key=AZD_HUMIDITY,
name="Humidity",
native_unit_of_measurement=PERCENTAGE,
state_class=STATE_CLASS_MEASUREMENT,
),
)
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
"""Add Airzone sensors from a config_entry."""
coordinator = hass.data[DOMAIN][entry.entry_id]
sensors = []
for system_zone_id, zone_data in coordinator.data[AZD_ZONES].items():
for description in SENSOR_TYPES:
if description.key in zone_data:
sensors.append(
AirzoneSensor(
coordinator,
description,
entry,
system_zone_id,
zone_data,
)
)
async_add_entities(sensors)
class AirzoneSensor(AirzoneEntity, SensorEntity):
"""Define an Airzone sensor."""
def __init__(
self,
coordinator: AirzoneUpdateCoordinator,
description: SensorEntityDescription,
entry: ConfigEntry,
system_zone_id: str,
zone_data: dict[str, Any],
) -> None:
"""Initialize."""
super().__init__(coordinator, entry, system_zone_id, zone_data)
self._attr_name = f"{zone_data[AZD_NAME]} {description.name}"
self._attr_unique_id = f"{entry.entry_id}_{system_zone_id}_{description.key}"
self.entity_description = description
if description.key == AZD_TEMP:
self._attr_native_unit_of_measurement = TEMP_UNIT_LIB_TO_HASS.get(
self.get_zone_value(AZD_TEMP_UNIT)
)
@property
def native_value(self):
"""Return the state."""
return self.get_zone_value(self.entity_description.key)

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
},
"error": {
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
},
"step": {
"user": {
"data": {
"host": "[%key:common::config_flow::data::host%]",
"port": "[%key:common::config_flow::data::port%]"
},
"description": "Set up Airzone integration."
}
}
}
}

View File

@ -0,0 +1,18 @@
{
"config": {
"abort": {
"already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e"
},
"error": {
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435"
},
"step": {
"user": {
"data": {
"host": "\u0425\u043e\u0441\u0442",
"port": "\u041f\u043e\u0440\u0442"
}
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "El dispositiu ja est\u00e0 configurat"
},
"error": {
"cannot_connect": "Ha fallat la connexi\u00f3"
},
"step": {
"user": {
"data": {
"host": "Amfitri\u00f3",
"port": "Port"
},
"description": "Configura la integraci\u00f3 Airzone."
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Ger\u00e4t ist bereits konfiguriert"
},
"error": {
"cannot_connect": "Verbindung fehlgeschlagen"
},
"step": {
"user": {
"data": {
"host": "Host",
"port": "Port"
},
"description": "Richte die Airzone-Integration ein."
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "\u0397 \u03c3\u03c5\u03c3\u03ba\u03b5\u03c5\u03ae \u03ad\u03c7\u03b5\u03b9 \u03ae\u03b4\u03b7 \u03b4\u03b9\u03b1\u03bc\u03bf\u03c1\u03c6\u03c9\u03b8\u03b5\u03af"
},
"error": {
"cannot_connect": "\u0391\u03c0\u03bf\u03c4\u03c5\u03c7\u03af\u03b1 \u03c3\u03cd\u03bd\u03b4\u03b5\u03c3\u03b7\u03c2"
},
"step": {
"user": {
"data": {
"host": "\u039a\u03b5\u03bd\u03c4\u03c1\u03b9\u03ba\u03cc\u03c2 \u03c5\u03c0\u03bf\u03bb\u03bf\u03b3\u03b9\u03c3\u03c4\u03ae\u03c2",
"port": "\u0398\u03cd\u03c1\u03b1"
},
"description": "\u03a1\u03c5\u03b8\u03bc\u03af\u03c3\u03c4\u03b5 \u03c4\u03b7\u03bd \u03b5\u03bd\u03c3\u03c9\u03bc\u03ac\u03c4\u03c9\u03c3\u03b7 \u03c4\u03bf\u03c5 Airzone."
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Device is already configured"
},
"error": {
"cannot_connect": "Failed to connect"
},
"step": {
"user": {
"data": {
"host": "Host",
"port": "Port"
},
"description": "Set up Airzone integration."
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Seade on juba h\u00e4\u00e4lestatud"
},
"error": {
"cannot_connect": "\u00dchendamine nurjus"
},
"step": {
"user": {
"data": {
"host": "Host",
"port": "Port"
},
"description": "Seadista Airzone'i sidumine"
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "L'appareil est d\u00e9j\u00e0 configur\u00e9"
},
"error": {
"cannot_connect": "\u00c9chec de connexion"
},
"step": {
"user": {
"data": {
"host": "H\u00f4te",
"port": "Port"
},
"description": "Configurer l'int\u00e9gration Airzone."
}
}
}
}

View File

@ -0,0 +1,18 @@
{
"config": {
"abort": {
"already_configured": "\u05ea\u05e6\u05d5\u05e8\u05ea \u05d4\u05d4\u05ea\u05e7\u05df \u05db\u05d1\u05e8 \u05e0\u05e7\u05d1\u05e2\u05d4"
},
"error": {
"cannot_connect": "\u05d4\u05d4\u05ea\u05d7\u05d1\u05e8\u05d5\u05ea \u05e0\u05db\u05e9\u05dc\u05d4"
},
"step": {
"user": {
"data": {
"host": "\u05de\u05d0\u05e8\u05d7",
"port": "\u05e4\u05ea\u05d7\u05d4"
}
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van"
},
"error": {
"cannot_connect": "Sikertelen csatlakoz\u00e1s"
},
"step": {
"user": {
"data": {
"host": "C\u00edm",
"port": "Port"
},
"description": "Airzone integr\u00e1ci\u00f3 be\u00e1ll\u00edt\u00e1sa."
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Perangkat sudah dikonfigurasi"
},
"error": {
"cannot_connect": "Gagal terhubung"
},
"step": {
"user": {
"data": {
"host": "Host",
"port": "Port"
},
"description": "Siapkan integrasi Airzone"
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Il dispositivo \u00e8 gi\u00e0 configurato"
},
"error": {
"cannot_connect": "Impossibile connettersi"
},
"step": {
"user": {
"data": {
"host": "Host",
"port": "Porta"
},
"description": "Imposta l'integrazione Airzone."
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "\u30c7\u30d0\u30a4\u30b9\u306f\u3059\u3067\u306b\u8a2d\u5b9a\u3055\u308c\u3066\u3044\u307e\u3059"
},
"error": {
"cannot_connect": "\u63a5\u7d9a\u306b\u5931\u6557\u3057\u307e\u3057\u305f"
},
"step": {
"user": {
"data": {
"host": "\u30db\u30b9\u30c8",
"port": "\u30dd\u30fc\u30c8"
},
"description": "Airzone\u30a4\u30f3\u30c6\u30b0\u30ec\u30fc\u30b7\u30e7\u30f3\u3092\u30bb\u30c3\u30c8\u30a2\u30c3\u30d7"
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Apparaat is al geconfigureerd"
},
"error": {
"cannot_connect": "Kon niet verbinden"
},
"step": {
"user": {
"data": {
"host": "Host",
"port": "Poort"
},
"description": "Airzone integratie instellen."
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Enheten er allerede konfigurert"
},
"error": {
"cannot_connect": "Tilkobling mislyktes"
},
"step": {
"user": {
"data": {
"host": "Vert",
"port": "Port"
},
"description": "Sett opp Airzone-integrasjon."
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Urz\u0105dzenie jest ju\u017c skonfigurowane"
},
"error": {
"cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia"
},
"step": {
"user": {
"data": {
"host": "Nazwa hosta lub adres IP",
"port": "Port"
},
"description": "Skonfiguruj integracj\u0119 Airzone."
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Dispositivo j\u00e1 est\u00e1 configurado"
},
"error": {
"cannot_connect": "Falha ao conectar"
},
"step": {
"user": {
"data": {
"host": "Nome do host",
"port": "Porta"
},
"description": "Configure a integra\u00e7\u00e3o Airzone."
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "\u042d\u0442\u043e \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e \u0443\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0432 Home Assistant."
},
"error": {
"cannot_connect": "\u041d\u0435 \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f."
},
"step": {
"user": {
"data": {
"host": "\u0425\u043e\u0441\u0442",
"port": "\u041f\u043e\u0440\u0442"
},
"description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 Home Assistant \u0434\u043b\u044f \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 Airzone."
}
}
}
}

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "\u88dd\u7f6e\u5df2\u7d93\u8a2d\u5b9a\u5b8c\u6210"
},
"error": {
"cannot_connect": "\u9023\u7dda\u5931\u6557"
},
"step": {
"user": {
"data": {
"host": "\u4e3b\u6a5f\u7aef",
"port": "\u901a\u8a0a\u57e0"
},
"description": "\u8a2d\u5b9a Airzone \u6574\u5408\u3002"
}
}
}
}

View File

@ -2,6 +2,6 @@
"domain": "alarm_control_panel",
"name": "Alarm Control Panel",
"documentation": "https://www.home-assistant.io/integrations/alarm_control_panel",
"codeowners": [],
"codeowners": ["@home-assistant/core"],
"quality_scale": "internal"
}

View File

@ -30,15 +30,15 @@
"armed": "\u039f\u03c0\u03bb\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2",
"armed_away": "\u039f\u03c0\u03bb\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03bc\u03b1\u03ba\u03c1\u03b9\u03ac",
"armed_custom_bypass": "\u03a0\u03c1\u03bf\u03c3\u03b1\u03c1\u03bc\u03bf\u03c3\u03bc\u03ad\u03bd\u03b7 \u03c0\u03b1\u03c1\u03ac\u03ba\u03b1\u03bc\u03c8\u03b7 \u03b5\u03bd\u03b5\u03c1\u03b3\u03ae",
"armed_home": "\u03a3\u03c0\u03af\u03c4\u03b9 \u039f\u03c0\u03bb\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf",
"armed_night": "\u039f\u03c0\u03bb\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf \u03b2\u03c1\u03ac\u03b4\u03c5",
"armed_home": "\u039f\u03c0\u03bb\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03c3\u03c0\u03af\u03c4\u03b9",
"armed_night": "\u039f\u03c0\u03bb\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2 \u03b2\u03c1\u03ac\u03b4\u03c5",
"armed_vacation": "\u039f\u03c0\u03bb\u03b9\u03c3\u03bc\u03cc\u03c2 \u03b4\u03b9\u03b1\u03ba\u03bf\u03c0\u03ce\u03bd",
"arming": "\u038c\u03c0\u03bb\u03b9\u03c3\u03b7",
"disarmed": "\u0391\u03c6\u03bf\u03c0\u03bb\u03b9\u03c3\u03bc\u03ad\u03bd\u03bf\u03c2",
"disarming": "\u0391\u03c6\u03cc\u03c0\u03bb\u03b9\u03c3\u03b7",
"pending": "\u0395\u03ba\u03ba\u03c1\u03b5\u03bc\u03ae\u03c2",
"triggered": "\u03a0\u03b1\u03c1\u03b1\u03b2\u03af\u03b1\u03c3\u03b7"
"triggered": "\u03a0\u03c5\u03c1\u03bf\u03b4\u03bf\u03c4\u03ae\u03b8\u03b7\u03ba\u03b5"
}
},
"title": "\u03a0\u03af\u03bd\u03b1\u03ba\u03b1\u03c2 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03b5\u03b9\u03b4\u03bf\u03c0\u03bf\u03b9\u03ae\u03c3\u03b5\u03c9\u03bd"
"title": "\u03a0\u03af\u03bd\u03b1\u03ba\u03b1\u03c2 \u03b5\u03bb\u03ad\u03b3\u03c7\u03bf\u03c5 \u03c3\u03c5\u03bd\u03b1\u03b3\u03b5\u03c1\u03bc\u03bf\u03cd"
}

View File

@ -22,6 +22,7 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_platform
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import (
@ -95,8 +96,8 @@ class AlarmDecoderAlarmPanel(AlarmControlPanelEntity):
async def async_added_to_hass(self):
"""Register callbacks."""
self.async_on_remove(
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_PANEL_MESSAGE, self._message_callback
async_dispatcher_connect(
self.hass, SIGNAL_PANEL_MESSAGE, self._message_callback
)
)

View File

@ -4,6 +4,7 @@ import logging
from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import (
@ -90,26 +91,24 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
async def async_added_to_hass(self):
"""Register callbacks."""
self.async_on_remove(
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_ZONE_FAULT, self._fault_callback
async_dispatcher_connect(self.hass, SIGNAL_ZONE_FAULT, self._fault_callback)
)
self.async_on_remove(
async_dispatcher_connect(
self.hass, SIGNAL_ZONE_RESTORE, self._restore_callback
)
)
self.async_on_remove(
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_ZONE_RESTORE, self._restore_callback
async_dispatcher_connect(
self.hass, SIGNAL_RFX_MESSAGE, self._rfx_message_callback
)
)
self.async_on_remove(
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_RFX_MESSAGE, self._rfx_message_callback
)
)
self.async_on_remove(
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_REL_MESSAGE, self._rel_message_callback
async_dispatcher_connect(
self.hass, SIGNAL_REL_MESSAGE, self._rel_message_callback
)
)

View File

@ -2,6 +2,7 @@
from homeassistant.components.sensor import SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import SIGNAL_PANEL_MESSAGE
@ -26,8 +27,8 @@ class AlarmDecoderSensor(SensorEntity):
async def async_added_to_hass(self):
"""Register callbacks."""
self.async_on_remove(
self.hass.helpers.dispatcher.async_dispatcher_connect(
SIGNAL_PANEL_MESSAGE, self._message_callback
async_dispatcher_connect(
self.hass, SIGNAL_PANEL_MESSAGE, self._message_callback
)
)

View File

@ -57,7 +57,7 @@
"zone_relayaddr": "\u4e2d\u7e7c\u4f4d\u5740",
"zone_relaychan": "\u4e2d\u7e7c\u983b\u9053",
"zone_rfid": "RF \u5e8f\u5217",
"zone_type": "\u5340\u57df\u985e\u578b"
"zone_type": "\u5340\u57df\u985e\u5225"
},
"description": "\u8f38\u5165\u5340\u57df {zone_number} \u8a73\u7d30\u8cc7\u6599\u3002\u6b32\u522a\u9664\u5340\u57df {zone_number}\uff0c\u4fdd\u6301\u5340\u57df\u540d\u7a31\u7a7a\u767d\u3002",
"title": "\u8a2d\u5b9a AlarmDecoder"

View File

@ -1,6 +1,10 @@
"""Support for repeating alerts when conditions are met."""
from __future__ import annotations
from collections.abc import Callable
from datetime import timedelta
import logging
from typing import Any, final
import voluptuous as vol
@ -23,10 +27,15 @@ from homeassistant.const import (
STATE_OFF,
STATE_ON,
)
from homeassistant.core import HomeAssistant, ServiceCall
from homeassistant.helpers import event, service
from homeassistant.core import Event, HomeAssistant, ServiceCall
from homeassistant.helpers import service
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity import ToggleEntity
from homeassistant.helpers.event import (
async_track_point_in_time,
async_track_state_change_event,
)
from homeassistant.helpers.template import Template
from homeassistant.helpers.typing import ConfigType
from homeassistant.util.dt import now
@ -62,7 +71,7 @@ ALERT_SCHEMA = vol.Schema(
vol.Optional(CONF_DONE_MESSAGE): cv.template,
vol.Optional(CONF_TITLE): cv.template,
vol.Optional(CONF_DATA): dict,
vol.Required(CONF_NOTIFIERS): cv.ensure_list,
vol.Required(CONF_NOTIFIERS): vol.All(cv.ensure_list, [cv.string]),
}
)
@ -73,14 +82,14 @@ CONFIG_SCHEMA = vol.Schema(
ALERT_SERVICE_SCHEMA = vol.Schema({vol.Required(ATTR_ENTITY_ID): cv.entity_ids})
def is_on(hass, entity_id):
def is_on(hass: HomeAssistant, entity_id: str) -> bool:
"""Return if the alert is firing and not acknowledged."""
return hass.states.is_state(entity_id, STATE_ON)
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""Set up the Alert component."""
entities = []
entities: list[Alert] = []
for object_id, cfg in config[DOMAIN].items():
if not cfg:
@ -144,10 +153,16 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
schema=ALERT_SERVICE_SCHEMA,
)
hass.services.async_register(
DOMAIN, SERVICE_TURN_ON, async_handle_alert_service, schema=ALERT_SERVICE_SCHEMA
DOMAIN,
SERVICE_TURN_ON,
async_handle_alert_service,
schema=ALERT_SERVICE_SCHEMA,
)
hass.services.async_register(
DOMAIN, SERVICE_TOGGLE, async_handle_alert_service, schema=ALERT_SERVICE_SCHEMA
DOMAIN,
SERVICE_TOGGLE,
async_handle_alert_service,
schema=ALERT_SERVICE_SCHEMA,
)
for alert in entities:
@ -163,20 +178,20 @@ class Alert(ToggleEntity):
def __init__(
self,
hass,
entity_id,
name,
watched_entity_id,
state,
repeat,
skip_first,
message_template,
done_message_template,
notifiers,
can_ack,
title_template,
data,
):
hass: HomeAssistant,
entity_id: str,
name: str,
watched_entity_id: str,
state: str,
repeat: list[float],
skip_first: bool,
message_template: Template | None,
done_message_template: Template | None,
notifiers: list[str],
can_ack: bool,
title_template: Template | None,
data: dict[Any, Any],
) -> None:
"""Initialize the alert."""
self.hass = hass
self._attr_name = name
@ -204,16 +219,18 @@ class Alert(ToggleEntity):
self._firing = False
self._ack = False
self._cancel = None
self._cancel: Callable[[], None] | None = None
self._send_done_message = False
self.entity_id = f"{DOMAIN}.{entity_id}"
event.async_track_state_change_event(
async_track_state_change_event(
hass, [watched_entity_id], self.watched_entity_change
)
@final # type: ignore[misc]
@property
def state(self): # pylint: disable=overridden-final-method
# pylint: disable=overridden-final-method
def state(self) -> str: # type: ignore[override]
"""Return the alert status."""
if self._firing:
if self._ack:
@ -221,17 +238,17 @@ class Alert(ToggleEntity):
return STATE_ON
return STATE_IDLE
async def watched_entity_change(self, ev):
async def watched_entity_change(self, event: Event) -> None:
"""Determine if the alert should start or stop."""
if (to_state := ev.data.get("new_state")) is None:
if (to_state := event.data.get("new_state")) is None:
return
_LOGGER.debug("Watched entity (%s) has changed", ev.data.get("entity_id"))
_LOGGER.debug("Watched entity (%s) has changed", event.data.get("entity_id"))
if to_state.state == self._alert_state and not self._firing:
await self.begin_alerting()
if to_state.state != self._alert_state and self._firing:
await self.end_alerting()
async def begin_alerting(self):
async def begin_alerting(self) -> None:
"""Begin the alert procedures."""
_LOGGER.debug("Beginning Alert: %s", self._attr_name)
self._ack = False
@ -245,26 +262,27 @@ class Alert(ToggleEntity):
self.async_write_ha_state()
async def end_alerting(self):
async def end_alerting(self) -> None:
"""End the alert procedures."""
_LOGGER.debug("Ending Alert: %s", self._attr_name)
if self._cancel is not None:
self._cancel()
self._cancel = None
self._ack = False
self._firing = False
if self._send_done_message:
await self._notify_done_message()
self.async_write_ha_state()
async def _schedule_notify(self):
async def _schedule_notify(self) -> None:
"""Schedule a notification."""
delay = self._delay[self._next_delay]
next_msg = now() + delay
self._cancel = event.async_track_point_in_time(
self.hass, self._notify, next_msg
)
self._cancel = async_track_point_in_time(self.hass, self._notify, next_msg)
self._next_delay = min(self._next_delay + 1, len(self._delay) - 1)
async def _notify(self, *args):
async def _notify(self, *args: Any) -> None:
"""Send the alert notification."""
if not self._firing:
return
@ -281,7 +299,7 @@ class Alert(ToggleEntity):
await self._send_notification_message(message)
await self._schedule_notify()
async def _notify_done_message(self, *args):
async def _notify_done_message(self) -> None:
"""Send notification of complete alert."""
_LOGGER.info("Alerting: %s", self._done_message_template)
self._send_done_message = False
@ -293,15 +311,15 @@ class Alert(ToggleEntity):
await self._send_notification_message(message)
async def _send_notification_message(self, message):
async def _send_notification_message(self, message: Any) -> None:
msg_payload = {ATTR_MESSAGE: message}
if self._title_template is not None:
title = self._title_template.async_render(parse_result=False)
msg_payload.update({ATTR_TITLE: title})
msg_payload[ATTR_TITLE] = title
if self._data:
msg_payload.update({ATTR_DATA: self._data})
msg_payload[ATTR_DATA] = self._data
_LOGGER.debug(msg_payload)
@ -310,19 +328,19 @@ class Alert(ToggleEntity):
DOMAIN_NOTIFY, target, msg_payload, context=self._context
)
async def async_turn_on(self, **kwargs):
async def async_turn_on(self, **kwargs: Any) -> None:
"""Async Unacknowledge alert."""
_LOGGER.debug("Reset Alert: %s", self._attr_name)
self._ack = False
self.async_write_ha_state()
async def async_turn_off(self, **kwargs):
async def async_turn_off(self, **kwargs: Any) -> None:
"""Async Acknowledge alert."""
_LOGGER.debug("Acknowledged Alert: %s", self._attr_name)
self._ack = True
self.async_write_ha_state()
async def async_toggle(self, **kwargs):
async def async_toggle(self, **kwargs: Any) -> None:
"""Async toggle alert."""
if self._ack:
return await self.async_turn_on()

View File

@ -3,7 +3,7 @@
"name": "Alert",
"documentation": "https://www.home-assistant.io/integrations/alert",
"after_dependencies": ["notify"],
"codeowners": [],
"codeowners": ["@home-assistant/core"],
"quality_scale": "internal",
"iot_class": "local_push"
}

View File

@ -75,6 +75,8 @@ class AlexaCapability:
https://developer.amazon.com/docs/device-apis/message-guide.html
"""
# pylint: disable=no-self-use
supported_locales = {"en-US"}
def __init__(self, entity: State, instance: str | None = None) -> None:
@ -86,28 +88,23 @@ class AlexaCapability:
"""Return the Alexa API name of this interface."""
raise NotImplementedError
@staticmethod
def properties_supported() -> list[dict]:
def properties_supported(self) -> list[dict]:
"""Return what properties this entity supports."""
return []
@staticmethod
def properties_proactively_reported() -> bool:
def properties_proactively_reported(self) -> bool:
"""Return True if properties asynchronously reported."""
return False
@staticmethod
def properties_retrievable() -> bool:
def properties_retrievable(self) -> bool:
"""Return True if properties can be retrieved."""
return False
@staticmethod
def properties_non_controllable() -> bool | None:
def properties_non_controllable(self) -> bool | None:
"""Return True if non controllable."""
return None
@staticmethod
def get_property(name):
def get_property(self, name):
"""Read and return a property.
Return value should be a dict, or raise UnsupportedProperty.
@ -117,13 +114,11 @@ class AlexaCapability:
"""
raise UnsupportedProperty(name)
@staticmethod
def supports_deactivation():
def supports_deactivation(self):
"""Applicable only to scenes."""
return None
@staticmethod
def capability_proactively_reported():
def capability_proactively_reported(self):
"""Return True if the capability is proactively reported.
Set properties_proactively_reported() for proactively reported properties.
@ -131,16 +126,14 @@ class AlexaCapability:
"""
return None
@staticmethod
def capability_resources():
def capability_resources(self):
"""Return the capability object.
Applicable to ToggleController, RangeController, and ModeController interfaces.
"""
return []
@staticmethod
def configuration():
def configuration(self):
"""Return the configuration object.
Applicable to the ThermostatController, SecurityControlPanel, ModeController, RangeController,
@ -148,8 +141,7 @@ class AlexaCapability:
"""
return []
@staticmethod
def configurations():
def configurations(self):
"""Return the configurations object.
The plural configurations object is different that the singular configuration object.
@ -157,31 +149,29 @@ class AlexaCapability:
"""
return []
@staticmethod
def inputs():
def inputs(self):
"""Applicable only to media players."""
return []
@staticmethod
def semantics():
def semantics(self):
"""Return the semantics object.
Applicable to ToggleController, RangeController, and ModeController interfaces.
"""
return []
@staticmethod
def supported_operations():
def supported_operations(self):
"""Return the supportedOperations object."""
return []
@staticmethod
def camera_stream_configurations():
def camera_stream_configurations(self):
"""Applicable only to CameraStreamController."""
return None
def serialize_discovery(self):
"""Serialize according to the Discovery API."""
# pylint: disable=assignment-from-none
# Methods may be overridden and return a value.
result = {"type": "AlexaInterface", "interface": self.name(), "version": "3"}
if (instance := self.instance) is not None:

View File

@ -200,6 +200,8 @@ class AlexaCapabilityResource:
https://developer.amazon.com/docs/device-apis/resources-and-assets.html#capability-resources
"""
# pylint: disable=no-self-use
def __init__(self, labels):
"""Initialize an Alexa resource."""
self._resource_labels = []
@ -210,13 +212,11 @@ class AlexaCapabilityResource:
"""Return capabilityResources object serialized for an API response."""
return self.serialize_labels(self._resource_labels)
@staticmethod
def serialize_configuration():
def serialize_configuration(self):
"""Return ModeResources, PresetResources friendlyNames serialized for an API response."""
return []
@staticmethod
def serialize_labels(resources):
def serialize_labels(self, resources):
"""Return resource label objects for friendlyNames serialized for an API response."""
labels = []
for label in resources:

View File

@ -68,7 +68,10 @@ class AlexaConfig(AbstractConfig):
entity_registry = er.async_get(self.hass)
if registry_entry := entity_registry.async_get(entity_id):
auxiliary_entity = registry_entry.entity_category is not None
auxiliary_entity = (
registry_entry.entity_category is not None
or registry_entry.hidden_by is not None
)
else:
auxiliary_entity = False
return not auxiliary_entity

View File

@ -8,7 +8,6 @@ from typing import Any
from aiohttp import ClientError
import async_timeout
from pyalmond import AlmondLocalAuth, WebAlmondAPI
import voluptuous as vol
from yarl import URL
from homeassistant import core, data_entry_flow
@ -122,5 +121,4 @@ class AlmondFlowHandler(
return self.async_show_form(
step_id="hassio_confirm",
description_placeholders={"addon": data["addon"]},
data_schema=vol.Schema({}),
)

View File

@ -5,7 +5,7 @@
},
"error": {
"cannot_connect": "\u00c9chec de connexion",
"invalid_api_key": "Cl\u00e9 API invalide"
"invalid_api_key": "Cl\u00e9 d'API non valide"
},
"step": {
"reauth_confirm": {

View File

@ -24,7 +24,9 @@ PRICE_SPIKE_ICONS = {
}
class AmberPriceGridSensor(CoordinatorEntity, BinarySensorEntity):
class AmberPriceGridSensor(
CoordinatorEntity[AmberUpdateCoordinator], BinarySensorEntity
):
"""Sensor to show single grid binary values."""
_attr_attribution = ATTRIBUTION

View File

@ -10,6 +10,7 @@ from amberelectric.model.actual_interval import ActualInterval
from amberelectric.model.channel import ChannelType
from amberelectric.model.current_interval import CurrentInterval
from amberelectric.model.forecast_interval import ForecastInterval
from amberelectric.model.interval import Descriptor
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
@ -44,6 +45,27 @@ def is_feed_in(interval: ActualInterval | CurrentInterval | ForecastInterval) ->
return interval.channel_type == ChannelType.FEED_IN
def normalize_descriptor(descriptor: Descriptor) -> str | None:
"""Return the snake case versions of descriptor names. Returns None if the name is not recognized."""
if descriptor is None:
return None
if descriptor.value == "spike":
return "spike"
if descriptor.value == "high":
return "high"
if descriptor.value == "neutral":
return "neutral"
if descriptor.value == "low":
return "low"
if descriptor.value == "veryLow":
return "very_low"
if descriptor.value == "extremelyLow":
return "extremely_low"
if descriptor.value == "negative":
return "negative"
return None
class AmberUpdateCoordinator(DataUpdateCoordinator):
"""AmberUpdateCoordinator - In charge of downloading the data for a site, which all the sensors read."""
@ -65,6 +87,7 @@ class AmberUpdateCoordinator(DataUpdateCoordinator):
result: dict[str, dict[str, Any]] = {
"current": {},
"descriptors": {},
"forecasts": {},
"grid": {},
}
@ -81,6 +104,7 @@ class AmberUpdateCoordinator(DataUpdateCoordinator):
raise UpdateFailed("No general channel configured")
result["current"]["general"] = general[0]
result["descriptors"]["general"] = normalize_descriptor(general[0].descriptor)
result["forecasts"]["general"] = [
interval for interval in forecasts if is_general(interval)
]
@ -92,6 +116,9 @@ class AmberUpdateCoordinator(DataUpdateCoordinator):
]
if controlled_load:
result["current"]["controlled_load"] = controlled_load[0]
result["descriptors"]["controlled_load"] = normalize_descriptor(
controlled_load[0].descriptor
)
result["forecasts"]["controlled_load"] = [
interval for interval in forecasts if is_controlled_load(interval)
]
@ -99,6 +126,9 @@ class AmberUpdateCoordinator(DataUpdateCoordinator):
feed_in = [interval for interval in current if is_feed_in(interval)]
if feed_in:
result["current"]["feed_in"] = feed_in[0]
result["descriptors"]["feed_in"] = normalize_descriptor(
feed_in[0].descriptor
)
result["forecasts"]["feed_in"] = [
interval for interval in forecasts if is_feed_in(interval)
]

View File

@ -3,12 +3,8 @@
"name": "Amber Electric",
"documentation": "https://www.home-assistant.io/integrations/amberelectric",
"config_flow": true,
"codeowners": [
"@madpilot"
],
"requirements": [
"amberelectric==1.0.3"
],
"codeowners": ["@madpilot"],
"requirements": ["amberelectric==1.0.4"],
"iot_class": "cloud_polling",
"loggers": ["amberelectric"]
}

View File

@ -26,7 +26,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import ATTRIBUTION, DOMAIN
from .coordinator import AmberUpdateCoordinator
from .coordinator import AmberUpdateCoordinator, normalize_descriptor
ICONS = {
"general": "mdi:transmission-tower",
@ -51,7 +51,7 @@ def friendly_channel_type(channel_type: str) -> str:
return "General"
class AmberSensor(CoordinatorEntity, SensorEntity):
class AmberSensor(CoordinatorEntity[AmberUpdateCoordinator], SensorEntity):
"""Amber Base Sensor."""
_attr_attribution = ATTRIBUTION
@ -160,6 +160,7 @@ class AmberForecastSensor(AmberSensor):
datum["end_time"] = interval.end_time.isoformat()
datum["renewables"] = round(interval.renewables)
datum["spike_status"] = interval.spike_status.value
datum["descriptor"] = normalize_descriptor(interval.descriptor)
if interval.range is not None:
datum["range_min"] = format_cents_to_dollars(interval.range.min)
@ -170,7 +171,16 @@ class AmberForecastSensor(AmberSensor):
return data
class AmberGridSensor(CoordinatorEntity, SensorEntity):
class AmberPriceDescriptorSensor(AmberSensor):
"""Amber Price Descriptor Sensor."""
@property
def native_value(self) -> str | None:
"""Return the current price descriptor."""
return self.coordinator.data[self.entity_description.key][self.channel_type]
class AmberGridSensor(CoordinatorEntity[AmberUpdateCoordinator], SensorEntity):
"""Sensor to show single grid specific values."""
_attr_attribution = ATTRIBUTION
@ -214,6 +224,16 @@ async def async_setup_entry(
)
entities.append(AmberPriceSensor(coordinator, description, channel_type))
for channel_type in current:
description = SensorEntityDescription(
key="descriptors",
name=f"{entry.title} - {friendly_channel_type(channel_type)} Price Descriptor",
icon=ICONS[channel_type],
)
entities.append(
AmberPriceDescriptorSensor(coordinator, description, channel_type)
)
for channel_type in forecasts:
description = SensorEntityDescription(
key="forecasts",

View File

@ -6,7 +6,6 @@
"api_token": "API Token",
"site_id": "Site ID"
},
"title": "Amber Electric",
"description": "Go to {api_url} to generate an API key"
},
"site": {
@ -14,7 +13,6 @@
"site_nmi": "Site NMI",
"site_name": "Site Name"
},
"title": "Amber Electric",
"description": "Select the NMI of the site you would like to add"
}
}

View File

@ -28,10 +28,21 @@ TYPE_BATT6 = "batt6"
TYPE_BATT7 = "batt7"
TYPE_BATT8 = "batt8"
TYPE_BATT9 = "batt9"
TYPE_BATT_CO2 = "batt_co2"
TYPE_BATTOUT = "battout"
TYPE_PM25_BATT = "batt_25"
TYPE_BATT_CO2 = "batt_co2"
TYPE_BATT_LIGHTNING = "batt_lightning"
TYPE_BATT_SM1 = "battsm1"
TYPE_BATT_SM10 = "battsm10"
TYPE_BATT_SM2 = "battsm2"
TYPE_BATT_SM3 = "battsm3"
TYPE_BATT_SM4 = "battsm4"
TYPE_BATT_SM5 = "battsm5"
TYPE_BATT_SM6 = "battsm6"
TYPE_BATT_SM7 = "battsm7"
TYPE_BATT_SM8 = "battsm8"
TYPE_BATT_SM9 = "battsm9"
TYPE_PM25IN_BATT = "batt_25in"
TYPE_PM25_BATT = "batt_25"
TYPE_RELAY1 = "relay1"
TYPE_RELAY10 = "relay10"
TYPE_RELAY2 = "relay2"
@ -131,7 +142,77 @@ BINARY_SENSOR_DESCRIPTIONS = (
),
AmbientBinarySensorDescription(
key=TYPE_BATT10,
name="Battery 10",
name="Soil Monitor Battery 10",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM1,
name="Soil Monitor Battery 1",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM2,
name="Soil Monitor Battery 2",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM3,
name="Soil Monitor Battery 3",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM4,
name="Soil Monitor Battery 4",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM5,
name="Soil Monitor Battery 5",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM6,
name="Soil Monitor Battery 6",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM7,
name="Soil Monitor Battery 7",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM8,
name="Soil Monitor Battery 8",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM9,
name="Soil Monitor Battery 9",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_SM10,
name="Soil Monitor Battery 10",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
@ -143,6 +224,13 @@ BINARY_SENSOR_DESCRIPTIONS = (
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_BATT_LIGHTNING,
name="Lightning Detector Battery",
device_class=BinarySensorDeviceClass.BATTERY,
entity_category=EntityCategory.DIAGNOSTIC,
on_state=0,
),
AmbientBinarySensorDescription(
key=TYPE_PM25IN_BATT,
name="PM25 Indoor Battery",

View File

@ -5,7 +5,7 @@ from typing import Any
from homeassistant.components.diagnostics import async_redact_data
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY
from homeassistant.const import CONF_API_KEY, CONF_LOCATION
from homeassistant.core import HomeAssistant
from . import AmbientStation
@ -14,7 +14,6 @@ from .const import CONF_APP_KEY, DOMAIN
CONF_API_KEY_CAMEL = "apiKey"
CONF_APP_KEY_CAMEL = "appKey"
CONF_DEVICE_ID_CAMEL = "deviceId"
CONF_LOCATION = "location"
CONF_MAC_ADDRESS = "mac_address"
CONF_MAC_ADDRESS_CAMEL = "macAddress"
CONF_TZ = "tz"

View File

@ -32,6 +32,10 @@ from . import AmbientStation, AmbientWeatherEntity
from .const import ATTR_LAST_DATA, DOMAIN, TYPE_SOLARRADIATION, TYPE_SOLARRADIATION_LX
TYPE_24HOURRAININ = "24hourrainin"
TYPE_AQI_PM25 = "aqi_pm25"
TYPE_AQI_PM25_24H = "aqi_pm25_24h"
TYPE_AQI_PM25_IN = "aqi_pm25_in"
TYPE_AQI_PM25_IN_24H = "aqi_pm25_in_24h"
TYPE_BAROMABSIN = "baromabsin"
TYPE_BAROMRELIN = "baromrelin"
TYPE_CO2 = "co2"
@ -53,6 +57,8 @@ TYPE_HUMIDITY8 = "humidity8"
TYPE_HUMIDITY9 = "humidity9"
TYPE_HUMIDITYIN = "humidityin"
TYPE_LASTRAIN = "lastRain"
TYPE_LIGHTNING_PER_DAY = "lightning_day"
TYPE_LIGHTNING_PER_HOUR = "lightning_hour"
TYPE_MAXDAILYGUST = "maxdailygust"
TYPE_MONTHLYRAININ = "monthlyrainin"
TYPE_PM25 = "pm25"
@ -112,6 +118,30 @@ SENSOR_DESCRIPTIONS = (
native_unit_of_measurement=PRECIPITATION_INCHES,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key=TYPE_AQI_PM25,
name="AQI PM2.5",
device_class=SensorDeviceClass.AQI,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_AQI_PM25_24H,
name="AQI PM2.5 24h Avg",
device_class=SensorDeviceClass.AQI,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key=TYPE_AQI_PM25_IN,
name="AQI PM2.5 Indoor",
device_class=SensorDeviceClass.AQI,
state_class=SensorStateClass.MEASUREMENT,
),
SensorEntityDescription(
key=TYPE_AQI_PM25_IN_24H,
name="AQI PM2.5 Indoor 24h Avg",
device_class=SensorDeviceClass.AQI,
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key=TYPE_BAROMABSIN,
name="Abs Pressure",
@ -246,6 +276,20 @@ SENSOR_DESCRIPTIONS = (
icon="mdi:water",
device_class=SensorDeviceClass.TIMESTAMP,
),
SensorEntityDescription(
key=TYPE_LIGHTNING_PER_DAY,
name="Lightning Strikes Per Day",
icon="mdi:lightning-bolt",
native_unit_of_measurement="strikes",
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key=TYPE_LIGHTNING_PER_HOUR,
name="Lightning Strikes Per Hour",
icon="mdi:lightning-bolt",
native_unit_of_measurement="strikes",
state_class=SensorStateClass.TOTAL_INCREASING,
),
SensorEntityDescription(
key=TYPE_MAXDAILYGUST,
name="Max Gust",

View File

@ -4,7 +4,7 @@
"already_configured": "Le service est d\u00e9j\u00e0 configur\u00e9"
},
"error": {
"invalid_key": "Cl\u00e9 API invalide",
"invalid_key": "Cl\u00e9 d'API non valide",
"no_devices": "Aucun appareil trouv\u00e9 dans le compte"
},
"step": {

View File

@ -99,9 +99,9 @@ set_color_bw:
selector:
select:
options:
- 'auto'
- 'bw'
- 'color'
- "auto"
- "bw"
- "color"
start_tour:
name: Start tour
@ -142,16 +142,16 @@ ptz_control:
selector:
select:
options:
- 'down'
- 'left'
- 'left_down'
- 'left_up'
- 'right'
- 'right_down'
- 'right_up'
- 'up'
- 'zoom_in'
- 'zoom_out'
- "down"
- "left"
- "left_down"
- "left_up"
- "right"
- "right_down"
- "right_up"
- "up"
- "zoom_in"
- "zoom_out"
travel_time:
name: Travel time
description: "Travel time in fractional seconds: from 0 to 1."

View File

@ -31,6 +31,7 @@ from .const import (
ATTR_AUTOMATION_COUNT,
ATTR_BASE,
ATTR_BOARD,
ATTR_CERTIFICATE,
ATTR_CONFIGURED,
ATTR_CUSTOM_INTEGRATIONS,
ATTR_DIAGNOSTICS,
@ -228,6 +229,7 @@ class Analytics:
)
if self.preferences.get(ATTR_USAGE, False):
payload[ATTR_CERTIFICATE] = self.hass.http.ssl_certificate is not None
payload[ATTR_INTEGRATIONS] = integrations
payload[ATTR_CUSTOM_INTEGRATIONS] = custom_integrations
if supervisor_info is not None:

View File

@ -21,6 +21,7 @@ ATTR_AUTO_UPDATE = "auto_update"
ATTR_AUTOMATION_COUNT = "automation_count"
ATTR_BASE = "base"
ATTR_BOARD = "board"
ATTR_CERTIFICATE = "certificate"
ATTR_CONFIGURED = "configured"
ATTR_CUSTOM_INTEGRATIONS = "custom_integrations"
ATTR_DIAGNOSTICS = "diagnostics"

View File

@ -2,17 +2,9 @@
"domain": "analytics",
"name": "Analytics",
"documentation": "https://www.home-assistant.io/integrations/analytics",
"codeowners": [
"@home-assistant/core",
"@ludeeus"
],
"dependencies": [
"api",
"websocket_api"
],
"after_dependencies": [
"energy"
],
"codeowners": ["@home-assistant/core", "@ludeeus"],
"dependencies": ["api", "websocket_api"],
"after_dependencies": ["energy"],
"quality_scale": "internal",
"iot_class": "cloud_push"
}

Some files were not shown because too many files have changed in this diff Show More