mirror of
https://github.com/home-assistant/core.git
synced 2025-07-28 23:57:06 +00:00
Merge pull request #69413 from home-assistant/rc
This commit is contained in:
commit
7dd19066e8
189
.core_files.yaml
189
.core_files.yaml
@ -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
|
||||
|
||||
|
78
.coveragerc
78
.coveragerc
@ -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]
|
||||
|
24
.github/workflows/builder.yml
vendored
24
.github/workflows/builder.yml
vendored
@ -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'
|
||||
@ -339,7 +339,7 @@ jobs:
|
||||
|
||||
# Create general tags
|
||||
if [[ "${{ needs.init.outputs.version }}" =~ d ]]; then
|
||||
create_manifest"dev" "${{ needs.init.outputs.version }}"
|
||||
create_manifest "dev" "${{ needs.init.outputs.version }}"
|
||||
elif [[ "${{ needs.init.outputs.version }}" =~ b ]]; then
|
||||
create_manifest "beta" "${{ needs.init.outputs.version }}"
|
||||
create_manifest "rc" "${{ needs.init.outputs.version }}"
|
||||
|
114
.github/workflows/ci.yaml
vendored
114
.github/workflows/ci.yaml
vendored
@ -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)
|
||||
|
8
.github/workflows/translations.yaml
vendored
8
.github/workflows/translations.yaml
vendored
@ -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 }}
|
||||
|
||||
|
21
.github/workflows/wheels.yml
vendored
21
.github/workflows/wheels.yml
vendored
@ -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"
|
||||
|
@ -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:
|
||||
|
@ -2,4 +2,3 @@
|
||||
azure-*.yml
|
||||
docs/source/_templates/*
|
||||
homeassistant/components/*/translations/*.json
|
||||
tests/fixtures/*
|
||||
|
@ -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
2313
CODEOWNERS
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
@ -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:
|
||||
"""
|
||||
|
@ -26,11 +26,10 @@
|
||||
"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%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
|
28
homeassistant/components/accuweather/diagnostics.py
Normal file
28
homeassistant/components/accuweather/diagnostics.py
Normal 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
|
@ -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__(
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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:
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -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."
|
||||
},
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {},
|
||||
)
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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.
|
||||
|
@ -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"]
|
||||
}
|
||||
|
@ -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}
|
||||
|
@ -1,31 +1,30 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]"
|
||||
},
|
||||
"error": {
|
||||
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"latitude": "[%key:common::config_flow::data::latitude%]",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]"
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"station_updates": "Gather data from AEMET weather stations"
|
||||
}
|
||||
}
|
||||
}
|
||||
"error": {
|
||||
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"latitude": "[%key:common::config_flow::data::latitude%]",
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]",
|
||||
"name": "Name of the integration"
|
||||
},
|
||||
"description": "To generate API key go to https://opendata.aemet.es/centrodedescargas/altaUsuario"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"station_updates": "Gather data from AEMET weather stations"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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__(
|
||||
|
@ -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%]",
|
||||
|
@ -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": {
|
||||
|
@ -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,
|
||||
|
@ -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%]",
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
@ -18,4 +18,4 @@
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"invalid_auth": "Authentification invalide",
|
||||
"invalid_auth": "Authentification non valide",
|
||||
"unknown": "Erreur inattendue"
|
||||
},
|
||||
"step": {
|
||||
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
89
homeassistant/components/airzone/__init__.py
Normal file
89
homeassistant/components/airzone/__init__.py
Normal 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
|
114
homeassistant/components/airzone/binary_sensor.py
Normal file
114
homeassistant/components/airzone/binary_sensor.py
Normal 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)
|
191
homeassistant/components/airzone/climate.py
Normal file
191
homeassistant/components/airzone/climate.py
Normal 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)
|
62
homeassistant/components/airzone/config_flow.py
Normal file
62
homeassistant/components/airzone/config_flow.py
Normal 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,
|
||||
)
|
19
homeassistant/components/airzone/const.py
Normal file
19
homeassistant/components/airzone/const.py
Normal 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,
|
||||
}
|
42
homeassistant/components/airzone/coordinator.py
Normal file
42
homeassistant/components/airzone/coordinator.py
Normal 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()
|
10
homeassistant/components/airzone/manifest.json
Normal file
10
homeassistant/components/airzone/manifest.json
Normal 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"]
|
||||
}
|
93
homeassistant/components/airzone/sensor.py
Normal file
93
homeassistant/components/airzone/sensor.py
Normal 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)
|
19
homeassistant/components/airzone/strings.json
Normal file
19
homeassistant/components/airzone/strings.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
homeassistant/components/airzone/translations/bg.json
Normal file
18
homeassistant/components/airzone/translations/bg.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/ca.json
Normal file
19
homeassistant/components/airzone/translations/ca.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/de.json
Normal file
19
homeassistant/components/airzone/translations/de.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/el.json
Normal file
19
homeassistant/components/airzone/translations/el.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/en.json
Normal file
19
homeassistant/components/airzone/translations/en.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/et.json
Normal file
19
homeassistant/components/airzone/translations/et.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/fr.json
Normal file
19
homeassistant/components/airzone/translations/fr.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
homeassistant/components/airzone/translations/he.json
Normal file
18
homeassistant/components/airzone/translations/he.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/hu.json
Normal file
19
homeassistant/components/airzone/translations/hu.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/id.json
Normal file
19
homeassistant/components/airzone/translations/id.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/it.json
Normal file
19
homeassistant/components/airzone/translations/it.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/ja.json
Normal file
19
homeassistant/components/airzone/translations/ja.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/nl.json
Normal file
19
homeassistant/components/airzone/translations/nl.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/no.json
Normal file
19
homeassistant/components/airzone/translations/no.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/pl.json
Normal file
19
homeassistant/components/airzone/translations/pl.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/pt-BR.json
Normal file
19
homeassistant/components/airzone/translations/pt-BR.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/ru.json
Normal file
19
homeassistant/components/airzone/translations/ru.json
Normal 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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airzone/translations/zh-Hant.json
Normal file
19
homeassistant/components/airzone/translations/zh-Hant.json
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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"
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
@ -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
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -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
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
self._cancel()
|
||||
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()
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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({}),
|
||||
)
|
||||
|
@ -3,7 +3,7 @@
|
||||
"abort": {
|
||||
"cannot_connect": "\u00c9chec de connexion",
|
||||
"missing_configuration": "Le composant n'est pas configur\u00e9. Veuillez suivre la documentation.",
|
||||
"no_url_available": "Aucune URL disponible. Pour plus d'informations sur cette erreur, [consultez la section d'aide] ( {docs_url} )",
|
||||
"no_url_available": "Aucune URL disponible. Pour plus d'informations sur cette erreur, [consultez la section d'aide]({docs_url})",
|
||||
"single_instance_allowed": "D\u00e9j\u00e0 configur\u00e9. Une seule configuration possible."
|
||||
},
|
||||
"step": {
|
||||
|
@ -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": {
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
]
|
||||
|
@ -1,14 +1,10 @@
|
||||
{
|
||||
"domain": "amberelectric",
|
||||
"name": "Amber Electric",
|
||||
"documentation": "https://www.home-assistant.io/integrations/amberelectric",
|
||||
"config_flow": true,
|
||||
"codeowners": [
|
||||
"@madpilot"
|
||||
],
|
||||
"requirements": [
|
||||
"amberelectric==1.0.3"
|
||||
],
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["amberelectric"]
|
||||
}
|
||||
"domain": "amberelectric",
|
||||
"name": "Amber Electric",
|
||||
"documentation": "https://www.home-assistant.io/integrations/amberelectric",
|
||||
"config_flow": true,
|
||||
"codeowners": ["@madpilot"],
|
||||
"requirements": ["amberelectric==1.0.4"],
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["amberelectric"]
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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,9 +13,8 @@
|
||||
"site_nmi": "Site NMI",
|
||||
"site_name": "Site Name"
|
||||
},
|
||||
"title": "Amber Electric",
|
||||
"description": "Select the NMI of the site you would like to add"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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"
|
||||
|
@ -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",
|
||||
|
@ -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": {
|
||||
|
@ -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."
|
||||
|
@ -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:
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user