This commit is contained in:
Paulus Schoutsen 2022-09-07 12:49:59 -04:00 committed by GitHub
commit 910f27f3a2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3932 changed files with 92145 additions and 24644 deletions

View File

@ -56,7 +56,9 @@ omit =
homeassistant/components/ambient_station/sensor.py homeassistant/components/ambient_station/sensor.py
homeassistant/components/amcrest/* homeassistant/components/amcrest/*
homeassistant/components/ampio/* homeassistant/components/ampio/*
homeassistant/components/android_ip_webcam/* homeassistant/components/android_ip_webcam/binary_sensor.py
homeassistant/components/android_ip_webcam/sensor.py
homeassistant/components/android_ip_webcam/switch.py
homeassistant/components/androidtv/diagnostics.py homeassistant/components/androidtv/diagnostics.py
homeassistant/components/anel_pwrctrl/switch.py homeassistant/components/anel_pwrctrl/switch.py
homeassistant/components/anthemav/media_player.py homeassistant/components/anthemav/media_player.py
@ -137,6 +139,7 @@ omit =
homeassistant/components/bosch_shc/switch.py homeassistant/components/bosch_shc/switch.py
homeassistant/components/braviatv/__init__.py homeassistant/components/braviatv/__init__.py
homeassistant/components/braviatv/const.py homeassistant/components/braviatv/const.py
homeassistant/components/braviatv/coordinator.py
homeassistant/components/braviatv/entity.py homeassistant/components/braviatv/entity.py
homeassistant/components/braviatv/media_player.py homeassistant/components/braviatv/media_player.py
homeassistant/components/braviatv/remote.py homeassistant/components/braviatv/remote.py
@ -257,6 +260,11 @@ omit =
homeassistant/components/econet/const.py homeassistant/components/econet/const.py
homeassistant/components/econet/sensor.py homeassistant/components/econet/sensor.py
homeassistant/components/econet/water_heater.py homeassistant/components/econet/water_heater.py
homeassistant/components/ecowitt/__init__.py
homeassistant/components/ecowitt/binary_sensor.py
homeassistant/components/ecowitt/diagnostics.py
homeassistant/components/ecowitt/entity.py
homeassistant/components/ecowitt/sensor.py
homeassistant/components/ecovacs/* homeassistant/components/ecovacs/*
homeassistant/components/edl21/* homeassistant/components/edl21/*
homeassistant/components/eddystone_temperature/sensor.py homeassistant/components/eddystone_temperature/sensor.py
@ -309,8 +317,12 @@ omit =
homeassistant/components/epson/media_player.py homeassistant/components/epson/media_player.py
homeassistant/components/epsonworkforce/sensor.py homeassistant/components/epsonworkforce/sensor.py
homeassistant/components/eq3btsmart/climate.py homeassistant/components/eq3btsmart/climate.py
homeassistant/components/escea/climate.py
homeassistant/components/escea/discovery.py
homeassistant/components/escea/__init__.py
homeassistant/components/esphome/__init__.py homeassistant/components/esphome/__init__.py
homeassistant/components/esphome/binary_sensor.py homeassistant/components/esphome/binary_sensor.py
homeassistant/components/esphome/bluetooth.py
homeassistant/components/esphome/button.py homeassistant/components/esphome/button.py
homeassistant/components/esphome/camera.py homeassistant/components/esphome/camera.py
homeassistant/components/esphome/climate.py homeassistant/components/esphome/climate.py
@ -386,6 +398,8 @@ omit =
homeassistant/components/flick_electric/sensor.py homeassistant/components/flick_electric/sensor.py
homeassistant/components/flock/notify.py homeassistant/components/flock/notify.py
homeassistant/components/flume/__init__.py homeassistant/components/flume/__init__.py
homeassistant/components/flume/coordinator.py
homeassistant/components/flume/entity.py
homeassistant/components/flume/sensor.py homeassistant/components/flume/sensor.py
homeassistant/components/flunearyou/__init__.py homeassistant/components/flunearyou/__init__.py
homeassistant/components/flunearyou/repairs.py homeassistant/components/flunearyou/repairs.py
@ -432,10 +446,10 @@ omit =
homeassistant/components/gitlab_ci/sensor.py homeassistant/components/gitlab_ci/sensor.py
homeassistant/components/gitter/sensor.py homeassistant/components/gitter/sensor.py
homeassistant/components/glances/__init__.py homeassistant/components/glances/__init__.py
homeassistant/components/glances/const.py
homeassistant/components/glances/sensor.py homeassistant/components/glances/sensor.py
homeassistant/components/goalfeed/* homeassistant/components/goalfeed/*
homeassistant/components/goodwe/__init__.py homeassistant/components/goodwe/__init__.py
homeassistant/components/goodwe/button.py
homeassistant/components/goodwe/const.py homeassistant/components/goodwe/const.py
homeassistant/components/goodwe/number.py homeassistant/components/goodwe/number.py
homeassistant/components/goodwe/select.py homeassistant/components/goodwe/select.py
@ -470,7 +484,6 @@ omit =
homeassistant/components/harmony/remote.py homeassistant/components/harmony/remote.py
homeassistant/components/harmony/util.py homeassistant/components/harmony/util.py
homeassistant/components/haveibeenpwned/sensor.py homeassistant/components/haveibeenpwned/sensor.py
homeassistant/components/hdmi_cec/*
homeassistant/components/heatmiser/climate.py homeassistant/components/heatmiser/climate.py
homeassistant/components/hikvision/binary_sensor.py homeassistant/components/hikvision/binary_sensor.py
homeassistant/components/hikvisioncam/switch.py homeassistant/components/hikvisioncam/switch.py
@ -574,7 +587,7 @@ omit =
homeassistant/components/iqvia/sensor.py homeassistant/components/iqvia/sensor.py
homeassistant/components/irish_rail_transport/sensor.py homeassistant/components/irish_rail_transport/sensor.py
homeassistant/components/iss/__init__.py homeassistant/components/iss/__init__.py
homeassistant/components/iss/binary_sensor.py homeassistant/components/iss/sensor.py
homeassistant/components/isy994/__init__.py homeassistant/components/isy994/__init__.py
homeassistant/components/isy994/binary_sensor.py homeassistant/components/isy994/binary_sensor.py
homeassistant/components/isy994/climate.py homeassistant/components/isy994/climate.py
@ -600,6 +613,10 @@ omit =
homeassistant/components/juicenet/number.py homeassistant/components/juicenet/number.py
homeassistant/components/juicenet/sensor.py homeassistant/components/juicenet/sensor.py
homeassistant/components/juicenet/switch.py homeassistant/components/juicenet/switch.py
homeassistant/components/justnimbus/const.py
homeassistant/components/justnimbus/coordinator.py
homeassistant/components/justnimbus/entity.py
homeassistant/components/justnimbus/sensor.py
homeassistant/components/kaiterra/* homeassistant/components/kaiterra/*
homeassistant/components/kankun/switch.py homeassistant/components/kankun/switch.py
homeassistant/components/keba/* homeassistant/components/keba/*
@ -630,7 +647,12 @@ omit =
homeassistant/components/kostal_plenticore/switch.py homeassistant/components/kostal_plenticore/switch.py
homeassistant/components/kwb/sensor.py homeassistant/components/kwb/sensor.py
homeassistant/components/lacrosse/sensor.py homeassistant/components/lacrosse/sensor.py
homeassistant/components/lametric/* homeassistant/components/lametric/__init__.py
homeassistant/components/lametric/button.py
homeassistant/components/lametric/coordinator.py
homeassistant/components/lametric/entity.py
homeassistant/components/lametric/notify.py
homeassistant/components/lametric/number.py
homeassistant/components/lannouncer/notify.py homeassistant/components/lannouncer/notify.py
homeassistant/components/lastfm/sensor.py homeassistant/components/lastfm/sensor.py
homeassistant/components/launch_library/__init__.py homeassistant/components/launch_library/__init__.py
@ -641,6 +663,9 @@ omit =
homeassistant/components/lcn/helpers.py homeassistant/components/lcn/helpers.py
homeassistant/components/lcn/scene.py homeassistant/components/lcn/scene.py
homeassistant/components/lcn/services.py homeassistant/components/lcn/services.py
homeassistant/components/led_ble/__init__.py
homeassistant/components/led_ble/light.py
homeassistant/components/led_ble/util.py
homeassistant/components/lg_netcast/media_player.py homeassistant/components/lg_netcast/media_player.py
homeassistant/components/lg_soundbar/media_player.py homeassistant/components/lg_soundbar/media_player.py
homeassistant/components/life360/__init__.py homeassistant/components/life360/__init__.py
@ -700,6 +725,10 @@ omit =
homeassistant/components/melcloud/const.py homeassistant/components/melcloud/const.py
homeassistant/components/melcloud/sensor.py homeassistant/components/melcloud/sensor.py
homeassistant/components/melcloud/water_heater.py homeassistant/components/melcloud/water_heater.py
homeassistant/components/melnor/__init__.py
homeassistant/components/melnor/const.py
homeassistant/components/melnor/models.py
homeassistant/components/melnor/switch.py
homeassistant/components/message_bird/notify.py homeassistant/components/message_bird/notify.py
homeassistant/components/met/weather.py homeassistant/components/met/weather.py
homeassistant/components/met_eireann/__init__.py homeassistant/components/met_eireann/__init__.py
@ -851,6 +880,8 @@ omit =
homeassistant/components/open_meteo/weather.py homeassistant/components/open_meteo/weather.py
homeassistant/components/opencv/* homeassistant/components/opencv/*
homeassistant/components/openevse/sensor.py homeassistant/components/openevse/sensor.py
homeassistant/components/openexchangerates/__init__.py
homeassistant/components/openexchangerates/coordinator.py
homeassistant/components/openexchangerates/sensor.py homeassistant/components/openexchangerates/sensor.py
homeassistant/components/opengarage/__init__.py homeassistant/components/opengarage/__init__.py
homeassistant/components/opengarage/binary_sensor.py homeassistant/components/opengarage/binary_sensor.py
@ -976,9 +1007,11 @@ omit =
homeassistant/components/raincloud/* homeassistant/components/raincloud/*
homeassistant/components/rainmachine/__init__.py homeassistant/components/rainmachine/__init__.py
homeassistant/components/rainmachine/binary_sensor.py homeassistant/components/rainmachine/binary_sensor.py
homeassistant/components/rainmachine/button.py
homeassistant/components/rainmachine/model.py homeassistant/components/rainmachine/model.py
homeassistant/components/rainmachine/sensor.py homeassistant/components/rainmachine/sensor.py
homeassistant/components/rainmachine/switch.py homeassistant/components/rainmachine/switch.py
homeassistant/components/rainmachine/update.py
homeassistant/components/rainmachine/util.py homeassistant/components/rainmachine/util.py
homeassistant/components/raspyrfm/* homeassistant/components/raspyrfm/*
homeassistant/components/recollect_waste/__init__.py homeassistant/components/recollect_waste/__init__.py
@ -1183,6 +1216,7 @@ omit =
homeassistant/components/switchbot/const.py homeassistant/components/switchbot/const.py
homeassistant/components/switchbot/entity.py homeassistant/components/switchbot/entity.py
homeassistant/components/switchbot/cover.py homeassistant/components/switchbot/cover.py
homeassistant/components/switchbot/light.py
homeassistant/components/switchbot/sensor.py homeassistant/components/switchbot/sensor.py
homeassistant/components/switchbot/coordinator.py homeassistant/components/switchbot/coordinator.py
homeassistant/components/switchmate/switch.py homeassistant/components/switchmate/switch.py
@ -1210,6 +1244,7 @@ omit =
homeassistant/components/system_bridge/binary_sensor.py homeassistant/components/system_bridge/binary_sensor.py
homeassistant/components/system_bridge/const.py homeassistant/components/system_bridge/const.py
homeassistant/components/system_bridge/coordinator.py homeassistant/components/system_bridge/coordinator.py
homeassistant/components/system_bridge/media_source.py
homeassistant/components/system_bridge/sensor.py homeassistant/components/system_bridge/sensor.py
homeassistant/components/systemmonitor/sensor.py homeassistant/components/systemmonitor/sensor.py
homeassistant/components/tado/__init__.py homeassistant/components/tado/__init__.py
@ -1408,7 +1443,14 @@ omit =
homeassistant/components/volumio/__init__.py homeassistant/components/volumio/__init__.py
homeassistant/components/volumio/browse_media.py homeassistant/components/volumio/browse_media.py
homeassistant/components/volumio/media_player.py homeassistant/components/volumio/media_player.py
homeassistant/components/volvooncall/* homeassistant/components/volvooncall/__init__.py
homeassistant/components/volvooncall/binary_sensor.py
homeassistant/components/volvooncall/const.py
homeassistant/components/volvooncall/device_tracker.py
homeassistant/components/volvooncall/errors.py
homeassistant/components/volvooncall/lock.py
homeassistant/components/volvooncall/sensor.py
homeassistant/components/volvooncall/switch.py
homeassistant/components/vulcan/__init__.py homeassistant/components/vulcan/__init__.py
homeassistant/components/vulcan/calendar.py homeassistant/components/vulcan/calendar.py
homeassistant/components/vulcan/fetch_data.py homeassistant/components/vulcan/fetch_data.py
@ -1464,12 +1506,17 @@ omit =
homeassistant/components/xiaomi_miio/light.py homeassistant/components/xiaomi_miio/light.py
homeassistant/components/xiaomi_miio/number.py homeassistant/components/xiaomi_miio/number.py
homeassistant/components/xiaomi_miio/remote.py homeassistant/components/xiaomi_miio/remote.py
homeassistant/components/xiaomi_miio/select.py
homeassistant/components/xiaomi_miio/sensor.py homeassistant/components/xiaomi_miio/sensor.py
homeassistant/components/xiaomi_miio/switch.py homeassistant/components/xiaomi_miio/switch.py
homeassistant/components/xiaomi_tv/media_player.py homeassistant/components/xiaomi_tv/media_player.py
homeassistant/components/xmpp/notify.py homeassistant/components/xmpp/notify.py
homeassistant/components/xs1/* homeassistant/components/xs1/*
homeassistant/components/yalexs_ble/__init__.py
homeassistant/components/yalexs_ble/binary_sensor.py
homeassistant/components/yalexs_ble/entity.py
homeassistant/components/yalexs_ble/lock.py
homeassistant/components/yalexs_ble/sensor.py
homeassistant/components/yalexs_ble/util.py
homeassistant/components/yale_smart_alarm/__init__.py homeassistant/components/yale_smart_alarm/__init__.py
homeassistant/components/yale_smart_alarm/alarm_control_panel.py homeassistant/components/yale_smart_alarm/alarm_control_panel.py
homeassistant/components/yale_smart_alarm/binary_sensor.py homeassistant/components/yale_smart_alarm/binary_sensor.py
@ -1519,7 +1566,6 @@ omit =
homeassistant/components/zha/core/registries.py homeassistant/components/zha/core/registries.py
homeassistant/components/zha/entity.py homeassistant/components/zha/entity.py
homeassistant/components/zha/light.py homeassistant/components/zha/light.py
homeassistant/components/zha/sensor.py
homeassistant/components/zhong_hong/climate.py homeassistant/components/zhong_hong/climate.py
homeassistant/components/ziggo_mediabox_xl/media_player.py homeassistant/components/ziggo_mediabox_xl/media_player.py
homeassistant/components/zoneminder/* homeassistant/components/zoneminder/*

View File

@ -22,7 +22,7 @@ on:
env: env:
CACHE_VERSION: 1 CACHE_VERSION: 1
PIP_CACHE_VERSION: 1 PIP_CACHE_VERSION: 1
HA_SHORT_VERSION: 2022.8 HA_SHORT_VERSION: 2022.9
DEFAULT_PYTHON: 3.9 DEFAULT_PYTHON: 3.9
PRE_COMMIT_CACHE: ~/.cache/pre-commit PRE_COMMIT_CACHE: ~/.cache/pre-commit
PIP_CACHE: /tmp/pip-cache PIP_CACHE: /tmp/pip-cache
@ -172,7 +172,7 @@ jobs:
cache: "pip" cache: "pip"
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: venv path: venv
key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }} key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }}
@ -185,7 +185,7 @@ jobs:
pip install "$(cat requirements_test.txt | grep pre-commit)" pip install "$(cat requirements_test.txt | grep pre-commit)"
- name: Restore pre-commit environment from cache - name: Restore pre-commit environment from cache
id: cache-precommit id: cache-precommit
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }} key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }}
@ -211,7 +211,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: venv path: venv
key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }} key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }}
@ -222,7 +222,7 @@ jobs:
exit 1 exit 1
- name: Restore pre-commit environment from cache - name: Restore pre-commit environment from cache
id: cache-precommit id: cache-precommit
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }} key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }}
@ -260,7 +260,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: venv path: venv
key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }} key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }}
@ -271,7 +271,7 @@ jobs:
exit 1 exit 1
- name: Restore pre-commit environment from cache - name: Restore pre-commit environment from cache
id: cache-precommit id: cache-precommit
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }} key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }}
@ -312,7 +312,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: venv path: venv
key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }} key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }}
@ -323,7 +323,7 @@ jobs:
exit 1 exit 1
- name: Restore pre-commit environment from cache - name: Restore pre-commit environment from cache
id: cache-precommit id: cache-precommit
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }} key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }}
@ -353,7 +353,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: venv path: venv
key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }} key: ${{ runner.os }}-venv-${{ needs.info.outputs.pre-commit_cache_key }}
@ -364,7 +364,7 @@ jobs:
exit 1 exit 1
- name: Restore pre-commit environment from cache - name: Restore pre-commit environment from cache
id: cache-precommit id: cache-precommit
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: ${{ env.PRE_COMMIT_CACHE }} path: ${{ env.PRE_COMMIT_CACHE }}
key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }} key: ${{ runner.os }}-pre-commit-${{ needs.info.outputs.pre-commit_cache_key }}
@ -480,7 +480,7 @@ jobs:
env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')" env.HA_SHORT_VERSION }}-$(date -u '+%Y-%m-%dT%H:%M:%s')"
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: venv path: venv
key: >- key: >-
@ -488,7 +488,7 @@ jobs:
needs.info.outputs.python_cache_key }} needs.info.outputs.python_cache_key }}
- name: Restore pip wheel cache - name: Restore pip wheel cache
if: steps.cache-venv.outputs.cache-hit != 'true' if: steps.cache-venv.outputs.cache-hit != 'true'
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: ${{ env.PIP_CACHE }} path: ${{ env.PIP_CACHE }}
key: >- key: >-
@ -538,7 +538,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment - name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: venv path: venv
key: >- key: >-
@ -570,7 +570,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore base Python virtual environment - name: Restore base Python virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: venv path: venv
key: >- key: >-
@ -603,7 +603,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment - name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: venv path: venv
key: >- key: >-
@ -647,7 +647,7 @@ jobs:
python-version: ${{ env.DEFAULT_PYTHON }} python-version: ${{ env.DEFAULT_PYTHON }}
- name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment - name: Restore full Python ${{ env.DEFAULT_PYTHON }} virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: venv path: venv
key: >- key: >-
@ -695,7 +695,7 @@ jobs:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Restore full Python ${{ matrix.python-version }} virtual environment - name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: venv path: venv
key: >- key: >-
@ -749,7 +749,7 @@ jobs:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Restore full Python ${{ matrix.python-version }} virtual environment - name: Restore full Python ${{ matrix.python-version }} virtual environment
id: cache-venv id: cache-venv
uses: actions/cache@v3.0.5 uses: actions/cache@v3.0.8
with: with:
path: venv path: venv
key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{

View File

@ -1,6 +1,6 @@
repos: repos:
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.37.2 rev: v2.37.3
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py39-plus] args: [--py39-plus]
@ -32,7 +32,7 @@ repos:
- flake8-docstrings==1.6.0 - flake8-docstrings==1.6.0
- pydocstyle==6.1.1 - pydocstyle==6.1.1
- flake8-comprehensions==3.10.0 - flake8-comprehensions==3.10.0
- flake8-noqa==1.2.5 - flake8-noqa==1.2.8
- mccabe==0.6.1 - mccabe==0.6.1
files: ^(homeassistant|script|tests)/.+\.py$ files: ^(homeassistant|script|tests)/.+\.py$
- repo: https://github.com/PyCQA/bandit - repo: https://github.com/PyCQA/bandit

View File

@ -1,4 +1,5 @@
*.md *.md
.strict-typing
azure-*.yml azure-*.yml
docs/source/_templates/* docs/source/_templates/*
homeassistant/components/*/translations/*.json homeassistant/components/*/translations/*.json

View File

@ -56,9 +56,12 @@ homeassistant.components.ambee.*
homeassistant.components.ambient_station.* homeassistant.components.ambient_station.*
homeassistant.components.amcrest.* homeassistant.components.amcrest.*
homeassistant.components.ampio.* homeassistant.components.ampio.*
homeassistant.components.anthemav.*
homeassistant.components.aseko_pool_live.* homeassistant.components.aseko_pool_live.*
homeassistant.components.asuswrt.* homeassistant.components.asuswrt.*
homeassistant.components.auth.*
homeassistant.components.automation.* homeassistant.components.automation.*
homeassistant.components.awair.*
homeassistant.components.backup.* homeassistant.components.backup.*
homeassistant.components.baf.* homeassistant.components.baf.*
homeassistant.components.binary_sensor.* homeassistant.components.binary_sensor.*
@ -95,6 +98,8 @@ homeassistant.components.energy.*
homeassistant.components.evil_genius_labs.* homeassistant.components.evil_genius_labs.*
homeassistant.components.fan.* homeassistant.components.fan.*
homeassistant.components.fastdotcom.* homeassistant.components.fastdotcom.*
homeassistant.components.feedreader.*
homeassistant.components.file_upload.*
homeassistant.components.filesize.* homeassistant.components.filesize.*
homeassistant.components.fitbit.* homeassistant.components.fitbit.*
homeassistant.components.flunearyou.* homeassistant.components.flunearyou.*
@ -105,6 +110,7 @@ homeassistant.components.fritzbox_callmonitor.*
homeassistant.components.fronius.* homeassistant.components.fronius.*
homeassistant.components.frontend.* homeassistant.components.frontend.*
homeassistant.components.fritz.* homeassistant.components.fritz.*
homeassistant.components.fully_kiosk.*
homeassistant.components.geo_location.* homeassistant.components.geo_location.*
homeassistant.components.geocaching.* homeassistant.components.geocaching.*
homeassistant.components.gios.* homeassistant.components.gios.*
@ -149,6 +155,7 @@ homeassistant.components.jewish_calendar.*
homeassistant.components.kaleidescape.* homeassistant.components.kaleidescape.*
homeassistant.components.knx.* homeassistant.components.knx.*
homeassistant.components.kraken.* homeassistant.components.kraken.*
homeassistant.components.lacrosse_view.*
homeassistant.components.lametric.* homeassistant.components.lametric.*
homeassistant.components.laundrify.* homeassistant.components.laundrify.*
homeassistant.components.lcn.* homeassistant.components.lcn.*
@ -164,6 +171,7 @@ homeassistant.components.mailbox.*
homeassistant.components.media_player.* homeassistant.components.media_player.*
homeassistant.components.media_source.* homeassistant.components.media_source.*
homeassistant.components.metoffice.* homeassistant.components.metoffice.*
homeassistant.components.mikrotik.*
homeassistant.components.mjpeg.* homeassistant.components.mjpeg.*
homeassistant.components.modbus.* homeassistant.components.modbus.*
homeassistant.components.modem_callerid.* homeassistant.components.modem_callerid.*
@ -185,6 +193,7 @@ homeassistant.components.nut.*
homeassistant.components.oncue.* homeassistant.components.oncue.*
homeassistant.components.onewire.* homeassistant.components.onewire.*
homeassistant.components.open_meteo.* homeassistant.components.open_meteo.*
homeassistant.components.openexchangerates.*
homeassistant.components.openuv.* homeassistant.components.openuv.*
homeassistant.components.peco.* homeassistant.components.peco.*
homeassistant.components.overkiz.* homeassistant.components.overkiz.*
@ -192,6 +201,7 @@ homeassistant.components.persistent_notification.*
homeassistant.components.pi_hole.* homeassistant.components.pi_hole.*
homeassistant.components.powerwall.* homeassistant.components.powerwall.*
homeassistant.components.proximity.* homeassistant.components.proximity.*
homeassistant.components.prusalink.*
homeassistant.components.pvoutput.* homeassistant.components.pvoutput.*
homeassistant.components.pure_energie.* homeassistant.components.pure_energie.*
homeassistant.components.qnap_qsw.* homeassistant.components.qnap_qsw.*
@ -210,6 +220,7 @@ homeassistant.components.rpi_power.*
homeassistant.components.rtsp_to_webrtc.* homeassistant.components.rtsp_to_webrtc.*
homeassistant.components.samsungtv.* homeassistant.components.samsungtv.*
homeassistant.components.scene.* homeassistant.components.scene.*
homeassistant.components.schedule.*
homeassistant.components.select.* homeassistant.components.select.*
homeassistant.components.sensibo.* homeassistant.components.sensibo.*
homeassistant.components.sensor.* homeassistant.components.sensor.*

View File

@ -72,6 +72,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/amcrest/ @flacjacket /homeassistant/components/amcrest/ @flacjacket
/homeassistant/components/analytics/ @home-assistant/core @ludeeus /homeassistant/components/analytics/ @home-assistant/core @ludeeus
/tests/components/analytics/ @home-assistant/core @ludeeus /tests/components/analytics/ @home-assistant/core @ludeeus
/homeassistant/components/android_ip_webcam/ @engrbm87
/tests/components/android_ip_webcam/ @engrbm87
/homeassistant/components/androidtv/ @JeffLIrion @ollo69 /homeassistant/components/androidtv/ @JeffLIrion @ollo69
/tests/components/androidtv/ @JeffLIrion @ollo69 /tests/components/androidtv/ @JeffLIrion @ollo69
/homeassistant/components/anthemav/ @hyralex /homeassistant/components/anthemav/ @hyralex
@ -135,6 +137,8 @@ build.json @home-assistant/supervisor
/tests/components/blebox/ @bbx-a @riokuu /tests/components/blebox/ @bbx-a @riokuu
/homeassistant/components/blink/ @fronzbot /homeassistant/components/blink/ @fronzbot
/tests/components/blink/ @fronzbot /tests/components/blink/ @fronzbot
/homeassistant/components/bluemaestro/ @bdraco
/tests/components/bluemaestro/ @bdraco
/homeassistant/components/blueprint/ @home-assistant/core /homeassistant/components/blueprint/ @home-assistant/core
/tests/components/blueprint/ @home-assistant/core /tests/components/blueprint/ @home-assistant/core
/homeassistant/components/bluesound/ @thrawnarn /homeassistant/components/bluesound/ @thrawnarn
@ -157,6 +161,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/bsblan/ @liudger /homeassistant/components/bsblan/ @liudger
/tests/components/bsblan/ @liudger /tests/components/bsblan/ @liudger
/homeassistant/components/bt_smarthub/ @jxwolstenholme /homeassistant/components/bt_smarthub/ @jxwolstenholme
/homeassistant/components/bthome/ @Ernst79
/tests/components/bthome/ @Ernst79
/homeassistant/components/buienradar/ @mjj4791 @ties @Robbie1221 /homeassistant/components/buienradar/ @mjj4791 @ties @Robbie1221
/tests/components/buienradar/ @mjj4791 @ties @Robbie1221 /tests/components/buienradar/ @mjj4791 @ties @Robbie1221
/homeassistant/components/button/ @home-assistant/core /homeassistant/components/button/ @home-assistant/core
@ -271,7 +277,9 @@ build.json @home-assistant/supervisor
/tests/components/ecobee/ @marthoc /tests/components/ecobee/ @marthoc
/homeassistant/components/econet/ @vangorra @w1ll1am23 /homeassistant/components/econet/ @vangorra @w1ll1am23
/tests/components/econet/ @vangorra @w1ll1am23 /tests/components/econet/ @vangorra @w1ll1am23
/homeassistant/components/ecovacs/ @OverloadUT /homeassistant/components/ecovacs/ @OverloadUT @mib1185
/homeassistant/components/ecowitt/ @pvizeli
/tests/components/ecowitt/ @pvizeli
/homeassistant/components/edl21/ @mtdcr /homeassistant/components/edl21/ @mtdcr
/homeassistant/components/efergy/ @tkdrob /homeassistant/components/efergy/ @tkdrob
/tests/components/efergy/ @tkdrob /tests/components/efergy/ @tkdrob
@ -309,6 +317,8 @@ build.json @home-assistant/supervisor
/tests/components/epson/ @pszafer /tests/components/epson/ @pszafer
/homeassistant/components/epsonworkforce/ @ThaStealth /homeassistant/components/epsonworkforce/ @ThaStealth
/homeassistant/components/eq3btsmart/ @rytilahti /homeassistant/components/eq3btsmart/ @rytilahti
/homeassistant/components/escea/ @lazdavila
/tests/components/escea/ @lazdavila
/homeassistant/components/esphome/ @OttoWinter @jesserockz /homeassistant/components/esphome/ @OttoWinter @jesserockz
/tests/components/esphome/ @OttoWinter @jesserockz /tests/components/esphome/ @OttoWinter @jesserockz
/homeassistant/components/evil_genius_labs/ @balloob /homeassistant/components/evil_genius_labs/ @balloob
@ -325,6 +335,8 @@ build.json @home-assistant/supervisor
/tests/components/fibaro/ @rappenze /tests/components/fibaro/ @rappenze
/homeassistant/components/file/ @fabaff /homeassistant/components/file/ @fabaff
/tests/components/file/ @fabaff /tests/components/file/ @fabaff
/homeassistant/components/file_upload/ @home-assistant/core
/tests/components/file_upload/ @home-assistant/core
/homeassistant/components/filesize/ @gjohansson-ST /homeassistant/components/filesize/ @gjohansson-ST
/tests/components/filesize/ @gjohansson-ST /tests/components/filesize/ @gjohansson-ST
/homeassistant/components/filter/ @dgomes /homeassistant/components/filter/ @dgomes
@ -343,8 +355,8 @@ build.json @home-assistant/supervisor
/tests/components/flipr/ @cnico /tests/components/flipr/ @cnico
/homeassistant/components/flo/ @dmulcahey /homeassistant/components/flo/ @dmulcahey
/tests/components/flo/ @dmulcahey /tests/components/flo/ @dmulcahey
/homeassistant/components/flume/ @ChrisMandich @bdraco /homeassistant/components/flume/ @ChrisMandich @bdraco @jeeftor
/tests/components/flume/ @ChrisMandich @bdraco /tests/components/flume/ @ChrisMandich @bdraco @jeeftor
/homeassistant/components/flunearyou/ @bachya /homeassistant/components/flunearyou/ @bachya
/tests/components/flunearyou/ @bachya /tests/components/flunearyou/ @bachya
/homeassistant/components/flux_led/ @icemanch @bdraco /homeassistant/components/flux_led/ @icemanch @bdraco
@ -371,6 +383,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/frontend/ @home-assistant/frontend /homeassistant/components/frontend/ @home-assistant/frontend
/tests/components/frontend/ @home-assistant/frontend /tests/components/frontend/ @home-assistant/frontend
/homeassistant/components/frontier_silicon/ @wlcrs /homeassistant/components/frontier_silicon/ @wlcrs
/homeassistant/components/fully_kiosk/ @cgarwood
/tests/components/fully_kiosk/ @cgarwood
/homeassistant/components/garages_amsterdam/ @klaasnicolaas /homeassistant/components/garages_amsterdam/ @klaasnicolaas
/tests/components/garages_amsterdam/ @klaasnicolaas /tests/components/garages_amsterdam/ @klaasnicolaas
/homeassistant/components/gdacs/ @exxamalte /homeassistant/components/gdacs/ @exxamalte
@ -434,6 +448,8 @@ build.json @home-assistant/supervisor
/tests/components/harmony/ @ehendrix23 @bramkragten @bdraco @mkeesey @Aohzan /tests/components/harmony/ @ehendrix23 @bramkragten @bdraco @mkeesey @Aohzan
/homeassistant/components/hassio/ @home-assistant/supervisor /homeassistant/components/hassio/ @home-assistant/supervisor
/tests/components/hassio/ @home-assistant/supervisor /tests/components/hassio/ @home-assistant/supervisor
/homeassistant/components/hdmi_cec/ @inytar
/tests/components/hdmi_cec/ @inytar
/homeassistant/components/heatmiser/ @andylockran /homeassistant/components/heatmiser/ @andylockran
/homeassistant/components/heos/ @andrewsayre /homeassistant/components/heos/ @andrewsayre
/tests/components/heos/ @andrewsayre /tests/components/heos/ @andrewsayre
@ -457,6 +473,8 @@ build.json @home-assistant/supervisor
/tests/components/homeassistant/ @home-assistant/core /tests/components/homeassistant/ @home-assistant/core
/homeassistant/components/homeassistant_alerts/ @home-assistant/core /homeassistant/components/homeassistant_alerts/ @home-assistant/core
/tests/components/homeassistant_alerts/ @home-assistant/core /tests/components/homeassistant_alerts/ @home-assistant/core
/homeassistant/components/homeassistant_sky_connect/ @home-assistant/core
/tests/components/homeassistant_sky_connect/ @home-assistant/core
/homeassistant/components/homeassistant_yellow/ @home-assistant/core /homeassistant/components/homeassistant_yellow/ @home-assistant/core
/tests/components/homeassistant_yellow/ @home-assistant/core /tests/components/homeassistant_yellow/ @home-assistant/core
/homeassistant/components/homekit/ @bdraco /homeassistant/components/homekit/ @bdraco
@ -551,6 +569,8 @@ build.json @home-assistant/supervisor
/tests/components/jewish_calendar/ @tsvi /tests/components/jewish_calendar/ @tsvi
/homeassistant/components/juicenet/ @jesserockz /homeassistant/components/juicenet/ @jesserockz
/tests/components/juicenet/ @jesserockz /tests/components/juicenet/ @jesserockz
/homeassistant/components/justnimbus/ @kvanzuijlen
/tests/components/justnimbus/ @kvanzuijlen
/homeassistant/components/kaiterra/ @Michsior14 /homeassistant/components/kaiterra/ @Michsior14
/homeassistant/components/kaleidescape/ @SteveEasley /homeassistant/components/kaleidescape/ @SteveEasley
/tests/components/kaleidescape/ @SteveEasley /tests/components/kaleidescape/ @SteveEasley
@ -573,13 +593,20 @@ build.json @home-assistant/supervisor
/tests/components/kraken/ @eifinger /tests/components/kraken/ @eifinger
/homeassistant/components/kulersky/ @emlove /homeassistant/components/kulersky/ @emlove
/tests/components/kulersky/ @emlove /tests/components/kulersky/ @emlove
/homeassistant/components/lacrosse_view/ @IceBotYT
/tests/components/lacrosse_view/ @IceBotYT
/homeassistant/components/lametric/ @robbiet480 @frenck /homeassistant/components/lametric/ @robbiet480 @frenck
/tests/components/lametric/ @robbiet480 @frenck
/homeassistant/components/landisgyr_heat_meter/ @vpathuis
/tests/components/landisgyr_heat_meter/ @vpathuis
/homeassistant/components/launch_library/ @ludeeus @DurgNomis-drol /homeassistant/components/launch_library/ @ludeeus @DurgNomis-drol
/tests/components/launch_library/ @ludeeus @DurgNomis-drol /tests/components/launch_library/ @ludeeus @DurgNomis-drol
/homeassistant/components/laundrify/ @xLarry /homeassistant/components/laundrify/ @xLarry
/tests/components/laundrify/ @xLarry /tests/components/laundrify/ @xLarry
/homeassistant/components/lcn/ @alengwenus /homeassistant/components/lcn/ @alengwenus
/tests/components/lcn/ @alengwenus /tests/components/lcn/ @alengwenus
/homeassistant/components/led_ble/ @bdraco
/tests/components/led_ble/ @bdraco
/homeassistant/components/lg_netcast/ @Drafteed /homeassistant/components/lg_netcast/ @Drafteed
/homeassistant/components/life360/ @pnbruckner /homeassistant/components/life360/ @pnbruckner
/tests/components/life360/ @pnbruckner /tests/components/life360/ @pnbruckner
@ -590,8 +617,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/linux_battery/ @fabaff /homeassistant/components/linux_battery/ @fabaff
/homeassistant/components/litejet/ @joncar /homeassistant/components/litejet/ @joncar
/tests/components/litejet/ @joncar /tests/components/litejet/ @joncar
/homeassistant/components/litterrobot/ @natekspencer /homeassistant/components/litterrobot/ @natekspencer @tkdrob
/tests/components/litterrobot/ @natekspencer /tests/components/litterrobot/ @natekspencer @tkdrob
/homeassistant/components/local_ip/ @issacg /homeassistant/components/local_ip/ @issacg
/tests/components/local_ip/ @issacg /tests/components/local_ip/ @issacg
/homeassistant/components/lock/ @home-assistant/core /homeassistant/components/lock/ @home-assistant/core
@ -611,8 +638,8 @@ build.json @home-assistant/supervisor
/tests/components/luftdaten/ @fabaff @frenck /tests/components/luftdaten/ @fabaff @frenck
/homeassistant/components/lupusec/ @majuss /homeassistant/components/lupusec/ @majuss
/homeassistant/components/lutron/ @JonGilmore /homeassistant/components/lutron/ @JonGilmore
/homeassistant/components/lutron_caseta/ @swails @bdraco /homeassistant/components/lutron_caseta/ @swails @bdraco @danaues
/tests/components/lutron_caseta/ @swails @bdraco /tests/components/lutron_caseta/ @swails @bdraco @danaues
/homeassistant/components/lyric/ @timmo001 /homeassistant/components/lyric/ @timmo001
/tests/components/lyric/ @timmo001 /tests/components/lyric/ @timmo001
/homeassistant/components/mastodon/ @fabaff /homeassistant/components/mastodon/ @fabaff
@ -630,6 +657,8 @@ build.json @home-assistant/supervisor
/tests/components/melcloud/ @vilppuvuorinen /tests/components/melcloud/ @vilppuvuorinen
/homeassistant/components/melissa/ @kennedyshead /homeassistant/components/melissa/ @kennedyshead
/tests/components/melissa/ @kennedyshead /tests/components/melissa/ @kennedyshead
/homeassistant/components/melnor/ @vanstinator
/tests/components/melnor/ @vanstinator
/homeassistant/components/met/ @danielhiversen @thimic /homeassistant/components/met/ @danielhiversen @thimic
/tests/components/met/ @danielhiversen @thimic /tests/components/met/ @danielhiversen @thimic
/homeassistant/components/met_eireann/ @DylanGore /homeassistant/components/met_eireann/ @DylanGore
@ -764,6 +793,8 @@ build.json @home-assistant/supervisor
/tests/components/open_meteo/ @frenck /tests/components/open_meteo/ @frenck
/homeassistant/components/openerz/ @misialq /homeassistant/components/openerz/ @misialq
/tests/components/openerz/ @misialq /tests/components/openerz/ @misialq
/homeassistant/components/openexchangerates/ @MartinHjelmare
/tests/components/openexchangerates/ @MartinHjelmare
/homeassistant/components/opengarage/ @danielhiversen /homeassistant/components/opengarage/ @danielhiversen
/tests/components/opengarage/ @danielhiversen /tests/components/opengarage/ @danielhiversen
/homeassistant/components/openhome/ @bazwilliams /homeassistant/components/openhome/ @bazwilliams
@ -821,17 +852,23 @@ build.json @home-assistant/supervisor
/homeassistant/components/prosegur/ @dgomes /homeassistant/components/prosegur/ @dgomes
/tests/components/prosegur/ @dgomes /tests/components/prosegur/ @dgomes
/homeassistant/components/proxmoxve/ @jhollowe @Corbeno /homeassistant/components/proxmoxve/ @jhollowe @Corbeno
/homeassistant/components/prusalink/ @balloob
/tests/components/prusalink/ @balloob
/homeassistant/components/ps4/ @ktnrg45 /homeassistant/components/ps4/ @ktnrg45
/tests/components/ps4/ @ktnrg45 /tests/components/ps4/ @ktnrg45
/homeassistant/components/pure_energie/ @klaasnicolaas /homeassistant/components/pure_energie/ @klaasnicolaas
/tests/components/pure_energie/ @klaasnicolaas /tests/components/pure_energie/ @klaasnicolaas
/homeassistant/components/push/ @dgomes /homeassistant/components/push/ @dgomes
/tests/components/push/ @dgomes /tests/components/push/ @dgomes
/homeassistant/components/pushover/ @engrbm87
/tests/components/pushover/ @engrbm87
/homeassistant/components/pvoutput/ @frenck /homeassistant/components/pvoutput/ @frenck
/tests/components/pvoutput/ @frenck /tests/components/pvoutput/ @frenck
/homeassistant/components/pvpc_hourly_pricing/ @azogue /homeassistant/components/pvpc_hourly_pricing/ @azogue
/tests/components/pvpc_hourly_pricing/ @azogue /tests/components/pvpc_hourly_pricing/ @azogue
/homeassistant/components/qbittorrent/ @geoffreylagaisse /homeassistant/components/qbittorrent/ @geoffreylagaisse
/homeassistant/components/qingping/ @bdraco
/tests/components/qingping/ @bdraco
/homeassistant/components/qld_bushfire/ @exxamalte /homeassistant/components/qld_bushfire/ @exxamalte
/tests/components/qld_bushfire/ @exxamalte /tests/components/qld_bushfire/ @exxamalte
/homeassistant/components/qnap_qsw/ @Noltari /homeassistant/components/qnap_qsw/ @Noltari
@ -909,6 +946,8 @@ build.json @home-assistant/supervisor
/tests/components/samsungtv/ @chemelli74 @epenet /tests/components/samsungtv/ @chemelli74 @epenet
/homeassistant/components/scene/ @home-assistant/core /homeassistant/components/scene/ @home-assistant/core
/tests/components/scene/ @home-assistant/core /tests/components/scene/ @home-assistant/core
/homeassistant/components/schedule/ @home-assistant/core
/tests/components/schedule/ @home-assistant/core
/homeassistant/components/schluter/ @prairieapps /homeassistant/components/schluter/ @prairieapps
/homeassistant/components/scrape/ @fabaff /homeassistant/components/scrape/ @fabaff
/tests/components/scrape/ @fabaff /tests/components/scrape/ @fabaff
@ -930,6 +969,8 @@ build.json @home-assistant/supervisor
/tests/components/sensibo/ @andrey-git @gjohansson-ST /tests/components/sensibo/ @andrey-git @gjohansson-ST
/homeassistant/components/sensor/ @home-assistant/core /homeassistant/components/sensor/ @home-assistant/core
/tests/components/sensor/ @home-assistant/core /tests/components/sensor/ @home-assistant/core
/homeassistant/components/sensorpro/ @bdraco
/tests/components/sensorpro/ @bdraco
/homeassistant/components/sensorpush/ @bdraco /homeassistant/components/sensorpush/ @bdraco
/tests/components/sensorpush/ @bdraco /tests/components/sensorpush/ @bdraco
/homeassistant/components/sentry/ @dcramer @frenck /homeassistant/components/sentry/ @dcramer @frenck
@ -1078,6 +1119,10 @@ build.json @home-assistant/supervisor
/homeassistant/components/tesla_wall_connector/ @einarhauks /homeassistant/components/tesla_wall_connector/ @einarhauks
/tests/components/tesla_wall_connector/ @einarhauks /tests/components/tesla_wall_connector/ @einarhauks
/homeassistant/components/tfiac/ @fredrike @mellado /homeassistant/components/tfiac/ @fredrike @mellado
/homeassistant/components/thermobeacon/ @bdraco
/tests/components/thermobeacon/ @bdraco
/homeassistant/components/thermopro/ @bdraco
/tests/components/thermopro/ @bdraco
/homeassistant/components/thethingsnetwork/ @fabaff /homeassistant/components/thethingsnetwork/ @fabaff
/homeassistant/components/threshold/ @fabaff /homeassistant/components/threshold/ @fabaff
/tests/components/threshold/ @fabaff /tests/components/threshold/ @fabaff
@ -1173,7 +1218,8 @@ build.json @home-assistant/supervisor
/tests/components/vlc_telnet/ @rodripf @MartinHjelmare /tests/components/vlc_telnet/ @rodripf @MartinHjelmare
/homeassistant/components/volumio/ @OnFreund /homeassistant/components/volumio/ @OnFreund
/tests/components/volumio/ @OnFreund /tests/components/volumio/ @OnFreund
/homeassistant/components/volvooncall/ @molobrakos @decompil3d /homeassistant/components/volvooncall/ @molobrakos
/tests/components/volvooncall/ @molobrakos
/homeassistant/components/vulcan/ @Antoni-Czaplicki /homeassistant/components/vulcan/ @Antoni-Czaplicki
/tests/components/vulcan/ @Antoni-Czaplicki /tests/components/vulcan/ @Antoni-Czaplicki
/homeassistant/components/wake_on_lan/ @ntilley905 /homeassistant/components/wake_on_lan/ @ntilley905
@ -1234,6 +1280,8 @@ build.json @home-assistant/supervisor
/homeassistant/components/xmpp/ @fabaff @flowolf /homeassistant/components/xmpp/ @fabaff @flowolf
/homeassistant/components/yale_smart_alarm/ @gjohansson-ST /homeassistant/components/yale_smart_alarm/ @gjohansson-ST
/tests/components/yale_smart_alarm/ @gjohansson-ST /tests/components/yale_smart_alarm/ @gjohansson-ST
/homeassistant/components/yalexs_ble/ @bdraco
/tests/components/yalexs_ble/ @bdraco
/homeassistant/components/yamaha_musiccast/ @vigonotion @micha91 /homeassistant/components/yamaha_musiccast/ @vigonotion @micha91
/tests/components/yamaha_musiccast/ @vigonotion @micha91 /tests/components/yamaha_musiccast/ @vigonotion @micha91
/homeassistant/components/yandex_transport/ @rishatik92 @devbis /homeassistant/components/yandex_transport/ @rishatik92 @devbis

View File

@ -12,7 +12,7 @@ demo <https://home-assistant.io/demo/>`__, `installation instructions <https://h
Featured integrations Featured integrations
--------------------- ---------------------
|screenshot-components| |screenshot-integrations|
The system is built using a modular approach so support for other devices or actions can be implemented easily. See also the `section on architecture <https://developers.home-assistant.io/docs/architecture_index/>`__ and the `section on creating your own The system is built using a modular approach so support for other devices or actions can be implemented easily. See also the `section on architecture <https://developers.home-assistant.io/docs/architecture_index/>`__ and the `section on creating your own
components <https://developers.home-assistant.io/docs/creating_component_index/>`__. components <https://developers.home-assistant.io/docs/creating_component_index/>`__.
@ -24,5 +24,5 @@ of a component, check the `Home Assistant help section <https://home-assistant.i
:target: https://discord.gg/c5DvZ4e :target: https://discord.gg/c5DvZ4e
.. |screenshot-states| image:: https://raw.githubusercontent.com/home-assistant/core/master/docs/screenshots.png .. |screenshot-states| image:: https://raw.githubusercontent.com/home-assistant/core/master/docs/screenshots.png
:target: https://home-assistant.io/demo/ :target: https://home-assistant.io/demo/
.. |screenshot-components| image:: https://raw.githubusercontent.com/home-assistant/core/dev/docs/screenshot-components.png .. |screenshot-integrations| image:: https://raw.githubusercontent.com/home-assistant/core/dev/docs/screenshot-integrations.png
:target: https://home-assistant.io/integrations/ :target: https://home-assistant.io/integrations/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

@ -24,7 +24,13 @@ from .const import (
SIGNAL_BOOTSTRAP_INTEGRATONS, SIGNAL_BOOTSTRAP_INTEGRATONS,
) )
from .exceptions import HomeAssistantError from .exceptions import HomeAssistantError
from .helpers import area_registry, device_registry, entity_registry, recorder from .helpers import (
area_registry,
device_registry,
entity_registry,
issue_registry,
recorder,
)
from .helpers.dispatcher import async_dispatcher_send from .helpers.dispatcher import async_dispatcher_send
from .helpers.typing import ConfigType from .helpers.typing import ConfigType
from .setup import ( from .setup import (
@ -521,9 +527,10 @@ async def _async_set_up_integrations(
# Load the registries and cache the result of platform.uname().processor # Load the registries and cache the result of platform.uname().processor
await asyncio.gather( await asyncio.gather(
area_registry.async_load(hass),
device_registry.async_load(hass), device_registry.async_load(hass),
entity_registry.async_load(hass), entity_registry.async_load(hass),
area_registry.async_load(hass), issue_registry.async_load(hass),
hass.async_add_executor_job(_cache_uname_processor), hass.async_add_executor_job(_cache_uname_processor),
) )

View File

@ -248,6 +248,7 @@ class AbodeEntity(entity.Entity):
"""Representation of an Abode entity.""" """Representation of an Abode entity."""
_attr_attribution = ATTRIBUTION _attr_attribution = ATTRIBUTION
_attr_has_entity_name = True
def __init__(self, data: AbodeSystem) -> None: def __init__(self, data: AbodeSystem) -> None:
"""Initialize Abode entity.""" """Initialize Abode entity."""
@ -283,7 +284,6 @@ class AbodeDevice(AbodeEntity):
"""Initialize Abode device.""" """Initialize Abode device."""
super().__init__(data) super().__init__(data)
self._device = device self._device = device
self._attr_name = device.name
self._attr_unique_id = device.device_uuid self._attr_unique_id = device.device_uuid
async def async_added_to_hass(self) -> None: async def async_added_to_hass(self) -> None:

View File

@ -30,12 +30,10 @@ async def async_setup_entry(
CONST.TYPE_OPENING, CONST.TYPE_OPENING,
] ]
entities = [] async_add_entities(
AbodeBinarySensor(data, device)
for device in data.abode.get_devices(generic_type=device_types): for device in data.abode.get_devices(generic_type=device_types)
entities.append(AbodeBinarySensor(data, device)) )
async_add_entities(entities)
class AbodeBinarySensor(AbodeDevice, BinarySensorEntity): class AbodeBinarySensor(AbodeDevice, BinarySensorEntity):

View File

@ -28,12 +28,11 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up Abode camera devices.""" """Set up Abode camera devices."""
data: AbodeSystem = hass.data[DOMAIN] data: AbodeSystem = hass.data[DOMAIN]
entities = []
for device in data.abode.get_devices(generic_type=CONST.TYPE_CAMERA): async_add_entities(
entities.append(AbodeCamera(data, device, TIMELINE.CAPTURE_IMAGE)) AbodeCamera(data, device, TIMELINE.CAPTURE_IMAGE)
for device in data.abode.get_devices(generic_type=CONST.TYPE_CAMERA)
async_add_entities(entities) )
class AbodeCamera(AbodeDevice, Camera): class AbodeCamera(AbodeDevice, Camera):
@ -75,7 +74,9 @@ class AbodeCamera(AbodeDevice, Camera):
"""Attempt to download the most recent capture.""" """Attempt to download the most recent capture."""
if self._device.image_url: if self._device.image_url:
try: try:
self._response = requests.get(self._device.image_url, stream=True) self._response = requests.get(
self._device.image_url, stream=True, timeout=10
)
self._response.raise_for_status() self._response.raise_for_status()
except requests.HTTPError as err: except requests.HTTPError as err:

View File

@ -19,12 +19,10 @@ async def async_setup_entry(
"""Set up Abode cover devices.""" """Set up Abode cover devices."""
data: AbodeSystem = hass.data[DOMAIN] data: AbodeSystem = hass.data[DOMAIN]
entities = [] async_add_entities(
AbodeCover(data, device)
for device in data.abode.get_devices(generic_type=CONST.TYPE_COVER): for device in data.abode.get_devices(generic_type=CONST.TYPE_COVER)
entities.append(AbodeCover(data, device)) )
async_add_entities(entities)
class AbodeCover(AbodeDevice, CoverEntity): class AbodeCover(AbodeDevice, CoverEntity):

View File

@ -32,12 +32,10 @@ async def async_setup_entry(
"""Set up Abode light devices.""" """Set up Abode light devices."""
data: AbodeSystem = hass.data[DOMAIN] data: AbodeSystem = hass.data[DOMAIN]
entities = [] async_add_entities(
AbodeLight(data, device)
for device in data.abode.get_devices(generic_type=CONST.TYPE_LIGHT): for device in data.abode.get_devices(generic_type=CONST.TYPE_LIGHT)
entities.append(AbodeLight(data, device)) )
async_add_entities(entities)
class AbodeLight(AbodeDevice, LightEntity): class AbodeLight(AbodeDevice, LightEntity):

View File

@ -19,12 +19,10 @@ async def async_setup_entry(
"""Set up Abode lock devices.""" """Set up Abode lock devices."""
data: AbodeSystem = hass.data[DOMAIN] data: AbodeSystem = hass.data[DOMAIN]
entities = [] async_add_entities(
AbodeLock(data, device)
for device in data.abode.get_devices(generic_type=CONST.TYPE_LOCK): for device in data.abode.get_devices(generic_type=CONST.TYPE_LOCK)
entities.append(AbodeLock(data, device)) )
async_add_entities(entities)
class AbodeLock(AbodeDevice, LockEntity): class AbodeLock(AbodeDevice, LockEntity):

View File

@ -42,20 +42,13 @@ async def async_setup_entry(
"""Set up Abode sensor devices.""" """Set up Abode sensor devices."""
data: AbodeSystem = hass.data[DOMAIN] data: AbodeSystem = hass.data[DOMAIN]
entities = [] async_add_entities(
for device in data.abode.get_devices(generic_type=CONST.TYPE_SENSOR):
conditions = device.get_value(CONST.STATUSES_KEY)
entities.extend(
[
AbodeSensor(data, device, description) AbodeSensor(data, device, description)
for description in SENSOR_TYPES for description in SENSOR_TYPES
if description.key in conditions for device in data.abode.get_devices(generic_type=CONST.TYPE_SENSOR)
] if description.key in device.get_value(CONST.STATUSES_KEY)
) )
async_add_entities(entities)
class AbodeSensor(AbodeDevice, SensorEntity): class AbodeSensor(AbodeDevice, SensorEntity):
"""A sensor implementation for Abode devices.""" """A sensor implementation for Abode devices."""
@ -71,7 +64,6 @@ class AbodeSensor(AbodeDevice, SensorEntity):
"""Initialize a sensor for an Abode device.""" """Initialize a sensor for an Abode device."""
super().__init__(data, device) super().__init__(data, device)
self.entity_description = description self.entity_description = description
self._attr_name = f"{device.name} {description.name}"
self._attr_unique_id = f"{device.device_uuid}-{description.key}" self._attr_unique_id = f"{device.device_uuid}-{description.key}"
if description.key == CONST.TEMP_STATUS_KEY: if description.key == CONST.TEMP_STATUS_KEY:
self._attr_native_unit_of_measurement = device.temp_unit self._attr_native_unit_of_measurement = device.temp_unit

View File

@ -25,14 +25,16 @@ async def async_setup_entry(
"""Set up Abode switch devices.""" """Set up Abode switch devices."""
data: AbodeSystem = hass.data[DOMAIN] data: AbodeSystem = hass.data[DOMAIN]
entities: list[SwitchEntity] = [] entities: list[SwitchEntity] = [
AbodeSwitch(data, device)
for device_type in DEVICE_TYPES
for device in data.abode.get_devices(generic_type=device_type)
]
for device_type in DEVICE_TYPES: entities.extend(
for device in data.abode.get_devices(generic_type=device_type): AbodeAutomationSwitch(data, automation)
entities.append(AbodeSwitch(data, device)) for automation in data.abode.get_automations()
)
for automation in data.abode.get_automations():
entities.append(AbodeAutomationSwitch(data, automation))
async_add_entities(entities) async_add_entities(entities)

View File

@ -7,7 +7,7 @@
"error": { "error": {
"cannot_connect": "No se pudo conectar", "cannot_connect": "No se pudo conectar",
"invalid_auth": "Autenticaci\u00f3n no v\u00e1lida", "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida",
"invalid_mfa_code": "C\u00f3digo MFA inv\u00e1lido" "invalid_mfa_code": "C\u00f3digo MFA no v\u00e1lido"
}, },
"step": { "step": {
"mfa": { "mfa": {
@ -21,14 +21,14 @@
"password": "Contrase\u00f1a", "password": "Contrase\u00f1a",
"username": "Correo electr\u00f3nico" "username": "Correo electr\u00f3nico"
}, },
"title": "Rellene su informaci\u00f3n de inicio de sesi\u00f3n de Abode" "title": "Completa tu informaci\u00f3n de inicio de sesi\u00f3n de Abode"
}, },
"user": { "user": {
"data": { "data": {
"password": "Contrase\u00f1a", "password": "Contrase\u00f1a",
"username": "Correo electr\u00f3nico" "username": "Correo electr\u00f3nico"
}, },
"title": "Rellene la informaci\u00f3n de acceso Abode" "title": "Completa tu informaci\u00f3n de inicio de sesi\u00f3n de Abode"
} }
} }
} }

View File

@ -1,13 +1,27 @@
{ {
"config": { "config": {
"abort": { "abort": {
"reauth_successful": "\u00c5terautentisering lyckades",
"single_instance_allowed": "Endast en enda konfiguration av Abode \u00e4r till\u00e5ten." "single_instance_allowed": "Endast en enda konfiguration av Abode \u00e4r till\u00e5ten."
}, },
"error": {
"cannot_connect": "Det gick inte att ansluta.",
"invalid_auth": "Ogiltig autentisering",
"invalid_mfa_code": "Ogiltig MFA-kod"
},
"step": { "step": {
"mfa": {
"data": {
"mfa_code": "MFA-kod (6 siffror)"
},
"title": "Ange din MFA-kod f\u00f6r Abode"
},
"reauth_confirm": { "reauth_confirm": {
"data": { "data": {
"password": "L\u00f6senord",
"username": "E-postadress" "username": "E-postadress"
} },
"title": "Fyll i din Abode-inloggningsinformation"
}, },
"user": { "user": {
"data": { "data": {

View File

@ -81,7 +81,6 @@ class AccuWeatherDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
"""Initialize.""" """Initialize."""
self.location_key = location_key self.location_key = location_key
self.forecast = forecast self.forecast = forecast
self.is_metric = hass.config.units.is_metric
self.accuweather = AccuWeather(api_key, session, location_key=location_key) self.accuweather = AccuWeather(api_key, session, location_key=location_key)
self.device_info = DeviceInfo( self.device_info = DeviceInfo(
entry_type=DeviceEntryType.SERVICE, entry_type=DeviceEntryType.SERVICE,
@ -116,7 +115,9 @@ class AccuWeatherDataUpdateCoordinator(DataUpdateCoordinator[dict[str, Any]]):
async with timeout(10): async with timeout(10):
current = await self.accuweather.async_get_current_conditions() current = await self.accuweather.async_get_current_conditions()
forecast = ( forecast = (
await self.accuweather.async_get_forecast(metric=self.is_metric) await self.accuweather.async_get_forecast(
metric=self.hass.config.units.is_metric
)
if self.forecast if self.forecast
else {} else {}
) )

View File

@ -23,7 +23,13 @@ from homeassistant.components.weather import (
API_IMPERIAL: Final = "Imperial" API_IMPERIAL: Final = "Imperial"
API_METRIC: Final = "Metric" API_METRIC: Final = "Metric"
ATTRIBUTION: Final = "Data provided by AccuWeather" ATTRIBUTION: Final = "Data provided by AccuWeather"
ATTR_CATEGORY: Final = "Category"
ATTR_DIRECTION: Final = "Direction"
ATTR_ENGLISH: Final = "English"
ATTR_LEVEL: Final = "level"
ATTR_FORECAST: Final = "forecast" ATTR_FORECAST: Final = "forecast"
ATTR_SPEED: Final = "Speed"
ATTR_VALUE: Final = "Value"
CONF_FORECAST: Final = "forecast" CONF_FORECAST: Final = "forecast"
DOMAIN: Final = "accuweather" DOMAIN: Final = "accuweather"
MANUFACTURER: Final = "AccuWeather, Inc." MANUFACTURER: Final = "AccuWeather, Inc."

View File

@ -2,7 +2,7 @@
"domain": "accuweather", "domain": "accuweather",
"name": "AccuWeather", "name": "AccuWeather",
"documentation": "https://www.home-assistant.io/integrations/accuweather/", "documentation": "https://www.home-assistant.io/integrations/accuweather/",
"requirements": ["accuweather==0.3.0"], "requirements": ["accuweather==0.4.0"],
"codeowners": ["@bieniu"], "codeowners": ["@bieniu"],
"config_flow": true, "config_flow": true,
"quality_scale": "platinum", "quality_scale": "platinum",

View File

@ -1,6 +1,7 @@
"""Support for the AccuWeather service.""" """Support for the AccuWeather service."""
from __future__ import annotations from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, cast from typing import Any, cast
@ -34,7 +35,13 @@ from . import AccuWeatherDataUpdateCoordinator
from .const import ( from .const import (
API_IMPERIAL, API_IMPERIAL,
API_METRIC, API_METRIC,
ATTR_CATEGORY,
ATTR_DIRECTION,
ATTR_ENGLISH,
ATTR_FORECAST, ATTR_FORECAST,
ATTR_LEVEL,
ATTR_SPEED,
ATTR_VALUE,
ATTRIBUTION, ATTRIBUTION,
DOMAIN, DOMAIN,
MAX_FORECAST_DAYS, MAX_FORECAST_DAYS,
@ -44,11 +51,20 @@ PARALLEL_UPDATES = 1
@dataclass @dataclass
class AccuWeatherSensorDescription(SensorEntityDescription): class AccuWeatherSensorDescriptionMixin:
"""Mixin for AccuWeather sensor."""
value_fn: Callable[[dict[str, Any], str], StateType]
@dataclass
class AccuWeatherSensorDescription(
SensorEntityDescription, AccuWeatherSensorDescriptionMixin
):
"""Class describing AccuWeather sensor entities.""" """Class describing AccuWeather sensor entities."""
unit_metric: str | None = None attr_fn: Callable[[dict[str, Any]], dict[str, StateType]] = lambda _: {}
unit_imperial: str | None = None unit_fn: Callable[[bool], str | None] = lambda _: None
FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = ( FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
@ -56,145 +72,162 @@ FORECAST_SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
key="CloudCoverDay", key="CloudCoverDay",
icon="mdi:weather-cloudy", icon="mdi:weather-cloudy",
name="Cloud cover day", name="Cloud cover day",
unit_metric=PERCENTAGE,
unit_imperial=PERCENTAGE,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
unit_fn=lambda _: PERCENTAGE,
value_fn=lambda data, _: cast(int, data),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="CloudCoverNight", key="CloudCoverNight",
icon="mdi:weather-cloudy", icon="mdi:weather-cloudy",
name="Cloud cover night", name="Cloud cover night",
unit_metric=PERCENTAGE,
unit_imperial=PERCENTAGE,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
unit_fn=lambda _: PERCENTAGE,
value_fn=lambda data, _: cast(int, data),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="Grass", key="Grass",
icon="mdi:grass", icon="mdi:grass",
name="Grass pollen", name="Grass pollen",
unit_metric=CONCENTRATION_PARTS_PER_CUBIC_METER,
unit_imperial=CONCENTRATION_PARTS_PER_CUBIC_METER,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
unit_fn=lambda _: CONCENTRATION_PARTS_PER_CUBIC_METER,
value_fn=lambda data, _: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="HoursOfSun", key="HoursOfSun",
icon="mdi:weather-partly-cloudy", icon="mdi:weather-partly-cloudy",
name="Hours of sun", name="Hours of sun",
unit_metric=TIME_HOURS, unit_fn=lambda _: TIME_HOURS,
unit_imperial=TIME_HOURS, value_fn=lambda data, _: cast(float, data),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="Mold", key="Mold",
icon="mdi:blur", icon="mdi:blur",
name="Mold pollen", name="Mold pollen",
unit_metric=CONCENTRATION_PARTS_PER_CUBIC_METER,
unit_imperial=CONCENTRATION_PARTS_PER_CUBIC_METER,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
unit_fn=lambda _: CONCENTRATION_PARTS_PER_CUBIC_METER,
value_fn=lambda data, _: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="Ozone", key="Ozone",
icon="mdi:vector-triangle", icon="mdi:vector-triangle",
name="Ozone", name="Ozone",
unit_metric=None,
unit_imperial=None,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
value_fn=lambda data, _: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="Ragweed", key="Ragweed",
icon="mdi:sprout", icon="mdi:sprout",
name="Ragweed pollen", name="Ragweed pollen",
unit_metric=CONCENTRATION_PARTS_PER_CUBIC_METER, unit_fn=lambda _: CONCENTRATION_PARTS_PER_CUBIC_METER,
unit_imperial=CONCENTRATION_PARTS_PER_CUBIC_METER,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
value_fn=lambda data, _: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="RealFeelTemperatureMax", key="RealFeelTemperatureMax",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
name="RealFeel temperature max", name="RealFeel temperature max",
unit_metric=TEMP_CELSIUS, unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT,
unit_imperial=TEMP_FAHRENHEIT, value_fn=lambda data, _: cast(float, data[ATTR_VALUE]),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="RealFeelTemperatureMin", key="RealFeelTemperatureMin",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
name="RealFeel temperature min", name="RealFeel temperature min",
unit_metric=TEMP_CELSIUS, unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT,
unit_imperial=TEMP_FAHRENHEIT, value_fn=lambda data, _: cast(float, data[ATTR_VALUE]),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="RealFeelTemperatureShadeMax", key="RealFeelTemperatureShadeMax",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
name="RealFeel temperature shade max", name="RealFeel temperature shade max",
unit_metric=TEMP_CELSIUS,
unit_imperial=TEMP_FAHRENHEIT,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT,
value_fn=lambda data, _: cast(float, data[ATTR_VALUE]),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="RealFeelTemperatureShadeMin", key="RealFeelTemperatureShadeMin",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
name="RealFeel temperature shade min", name="RealFeel temperature shade min",
unit_metric=TEMP_CELSIUS,
unit_imperial=TEMP_FAHRENHEIT,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT,
value_fn=lambda data, _: cast(float, data[ATTR_VALUE]),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="ThunderstormProbabilityDay", key="ThunderstormProbabilityDay",
icon="mdi:weather-lightning", icon="mdi:weather-lightning",
name="Thunderstorm probability day", name="Thunderstorm probability day",
unit_metric=PERCENTAGE, unit_fn=lambda _: PERCENTAGE,
unit_imperial=PERCENTAGE, value_fn=lambda data, _: cast(int, data),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="ThunderstormProbabilityNight", key="ThunderstormProbabilityNight",
icon="mdi:weather-lightning", icon="mdi:weather-lightning",
name="Thunderstorm probability night", name="Thunderstorm probability night",
unit_metric=PERCENTAGE, unit_fn=lambda _: PERCENTAGE,
unit_imperial=PERCENTAGE, value_fn=lambda data, _: cast(int, data),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="Tree", key="Tree",
icon="mdi:tree-outline", icon="mdi:tree-outline",
name="Tree pollen", name="Tree pollen",
unit_metric=CONCENTRATION_PARTS_PER_CUBIC_METER, unit_fn=lambda _: CONCENTRATION_PARTS_PER_CUBIC_METER,
unit_imperial=CONCENTRATION_PARTS_PER_CUBIC_METER,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
value_fn=lambda data, _: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="UVIndex", key="UVIndex",
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
name="UV index", name="UV index",
unit_metric=UV_INDEX, unit_fn=lambda _: UV_INDEX,
unit_imperial=UV_INDEX, value_fn=lambda data, _: cast(int, data[ATTR_VALUE]),
attr_fn=lambda data: {ATTR_LEVEL: data[ATTR_CATEGORY]},
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="WindGustDay", key="WindGustDay",
icon="mdi:weather-windy", icon="mdi:weather-windy",
name="Wind gust day", name="Wind gust day",
unit_metric=SPEED_KILOMETERS_PER_HOUR,
unit_imperial=SPEED_MILES_PER_HOUR,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR
if metric
else SPEED_MILES_PER_HOUR,
value_fn=lambda data, _: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="WindGustNight", key="WindGustNight",
icon="mdi:weather-windy", icon="mdi:weather-windy",
name="Wind gust night", name="Wind gust night",
unit_metric=SPEED_KILOMETERS_PER_HOUR,
unit_imperial=SPEED_MILES_PER_HOUR,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR
if metric
else SPEED_MILES_PER_HOUR,
value_fn=lambda data, _: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="WindDay", key="WindDay",
icon="mdi:weather-windy", icon="mdi:weather-windy",
name="Wind day", name="Wind day",
unit_metric=SPEED_KILOMETERS_PER_HOUR, unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR
unit_imperial=SPEED_MILES_PER_HOUR, if metric
else SPEED_MILES_PER_HOUR,
value_fn=lambda data, _: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="WindNight", key="WindNight",
icon="mdi:weather-windy", icon="mdi:weather-windy",
name="Wind night", name="Wind night",
unit_metric=SPEED_KILOMETERS_PER_HOUR, unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR
unit_imperial=SPEED_MILES_PER_HOUR, if metric
else SPEED_MILES_PER_HOUR,
value_fn=lambda data, _: cast(float, data[ATTR_SPEED][ATTR_VALUE]),
attr_fn=lambda data: {"direction": data[ATTR_DIRECTION][ATTR_ENGLISH]},
), ),
) )
@ -203,112 +236,117 @@ SENSOR_TYPES: tuple[AccuWeatherSensorDescription, ...] = (
key="ApparentTemperature", key="ApparentTemperature",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
name="Apparent temperature", name="Apparent temperature",
unit_metric=TEMP_CELSIUS,
unit_imperial=TEMP_FAHRENHEIT,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT,
value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="Ceiling", key="Ceiling",
icon="mdi:weather-fog", icon="mdi:weather-fog",
name="Cloud ceiling", name="Cloud ceiling",
unit_metric=LENGTH_METERS,
unit_imperial=LENGTH_FEET,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
unit_fn=lambda metric: LENGTH_METERS if metric else LENGTH_FEET,
value_fn=lambda data, unit: round(data[unit][ATTR_VALUE]),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="CloudCover", key="CloudCover",
icon="mdi:weather-cloudy", icon="mdi:weather-cloudy",
name="Cloud cover", name="Cloud cover",
unit_metric=PERCENTAGE,
unit_imperial=PERCENTAGE,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
unit_fn=lambda _: PERCENTAGE,
value_fn=lambda data, _: cast(int, data),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="DewPoint", key="DewPoint",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
name="Dew point", name="Dew point",
unit_metric=TEMP_CELSIUS,
unit_imperial=TEMP_FAHRENHEIT,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT,
value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="RealFeelTemperature", key="RealFeelTemperature",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
name="RealFeel temperature", name="RealFeel temperature",
unit_metric=TEMP_CELSIUS,
unit_imperial=TEMP_FAHRENHEIT,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT,
value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="RealFeelTemperatureShade", key="RealFeelTemperatureShade",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
name="RealFeel temperature shade", name="RealFeel temperature shade",
unit_metric=TEMP_CELSIUS,
unit_imperial=TEMP_FAHRENHEIT,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT,
value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="Precipitation", key="Precipitation",
icon="mdi:weather-rainy", icon="mdi:weather-rainy",
name="Precipitation", name="Precipitation",
unit_metric=LENGTH_MILLIMETERS,
unit_imperial=LENGTH_INCHES,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
unit_fn=lambda metric: LENGTH_MILLIMETERS if metric else LENGTH_INCHES,
value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]),
attr_fn=lambda data: {"type": data["PrecipitationType"]},
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="PressureTendency", key="PressureTendency",
device_class="accuweather__pressure_tendency", device_class="accuweather__pressure_tendency",
icon="mdi:gauge", icon="mdi:gauge",
name="Pressure tendency", name="Pressure tendency",
unit_metric=None, value_fn=lambda data, _: cast(str, data["LocalizedText"]).lower(),
unit_imperial=None,
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="UVIndex", key="UVIndex",
icon="mdi:weather-sunny", icon="mdi:weather-sunny",
name="UV index", name="UV index",
unit_metric=UV_INDEX,
unit_imperial=UV_INDEX,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
unit_fn=lambda _: UV_INDEX,
value_fn=lambda data, _: cast(int, data),
attr_fn=lambda data: {ATTR_LEVEL: data["UVIndexText"]},
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="WetBulbTemperature", key="WetBulbTemperature",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
name="Wet bulb temperature", name="Wet bulb temperature",
unit_metric=TEMP_CELSIUS,
unit_imperial=TEMP_FAHRENHEIT,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT,
value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="WindChillTemperature", key="WindChillTemperature",
device_class=SensorDeviceClass.TEMPERATURE, device_class=SensorDeviceClass.TEMPERATURE,
name="Wind chill temperature", name="Wind chill temperature",
unit_metric=TEMP_CELSIUS,
unit_imperial=TEMP_FAHRENHEIT,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
unit_fn=lambda metric: TEMP_CELSIUS if metric else TEMP_FAHRENHEIT,
value_fn=lambda data, unit: cast(float, data[unit][ATTR_VALUE]),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="Wind", key="Wind",
icon="mdi:weather-windy", icon="mdi:weather-windy",
name="Wind", name="Wind",
unit_metric=SPEED_KILOMETERS_PER_HOUR,
unit_imperial=SPEED_MILES_PER_HOUR,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR
if metric
else SPEED_MILES_PER_HOUR,
value_fn=lambda data, unit: cast(float, data[ATTR_SPEED][unit][ATTR_VALUE]),
), ),
AccuWeatherSensorDescription( AccuWeatherSensorDescription(
key="WindGust", key="WindGust",
icon="mdi:weather-windy", icon="mdi:weather-windy",
name="Wind gust", name="Wind gust",
unit_metric=SPEED_KILOMETERS_PER_HOUR,
unit_imperial=SPEED_MILES_PER_HOUR,
entity_registry_enabled_default=False, entity_registry_enabled_default=False,
state_class=SensorStateClass.MEASUREMENT, state_class=SensorStateClass.MEASUREMENT,
unit_fn=lambda metric: SPEED_KILOMETERS_PER_HOUR
if metric
else SPEED_MILES_PER_HOUR,
value_fn=lambda data, unit: cast(float, data[ATTR_SPEED][unit][ATTR_VALUE]),
), ),
) )
@ -320,18 +358,18 @@ async def async_setup_entry(
coordinator: AccuWeatherDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id] coordinator: AccuWeatherDataUpdateCoordinator = hass.data[DOMAIN][entry.entry_id]
sensors: list[AccuWeatherSensor] = [] sensors = [
for description in SENSOR_TYPES: AccuWeatherSensor(coordinator, description) for description in SENSOR_TYPES
sensors.append(AccuWeatherSensor(coordinator, description)) ]
if coordinator.forecast: if coordinator.forecast:
for description in FORECAST_SENSOR_TYPES:
for day in range(MAX_FORECAST_DAYS + 1):
# Some air quality/allergy sensors are only available for certain # Some air quality/allergy sensors are only available for certain
# locations. # locations.
if description.key in coordinator.data[ATTR_FORECAST][0]: sensors.extend(
sensors.append( AccuWeatherForecastSensor(coordinator, description, forecast_day=day)
AccuWeatherSensor(coordinator, description, forecast_day=day) for day in range(MAX_FORECAST_DAYS + 1)
for description in FORECAST_SENSOR_TYPES
if description.key in coordinator.data[ATTR_FORECAST][0]
) )
async_add_entities(sensors) async_add_entities(sensors)
@ -356,9 +394,8 @@ class AccuWeatherSensor(
super().__init__(coordinator) super().__init__(coordinator)
self.entity_description = description self.entity_description = description
self._sensor_data = _get_sensor_data( self._sensor_data = _get_sensor_data(
coordinator.data, forecast_day, description.key coordinator.data, description.key, forecast_day
) )
self._attrs: dict[str, Any] = {}
if forecast_day is not None: if forecast_day is not None:
self._attr_name = f"{description.name} {forecast_day}d" self._attr_name = f"{description.name} {forecast_day}d"
self._attr_unique_id = ( self._attr_unique_id = (
@ -368,82 +405,40 @@ class AccuWeatherSensor(
self._attr_unique_id = ( self._attr_unique_id = (
f"{coordinator.location_key}-{description.key}".lower() f"{coordinator.location_key}-{description.key}".lower()
) )
if coordinator.is_metric: if self.coordinator.hass.config.units.is_metric:
self._unit_system = API_METRIC self._unit_system = API_METRIC
self._attr_native_unit_of_measurement = description.unit_metric
else: else:
self._unit_system = API_IMPERIAL self._unit_system = API_IMPERIAL
self._attr_native_unit_of_measurement = description.unit_imperial self._attr_native_unit_of_measurement = self.entity_description.unit_fn(
self.coordinator.hass.config.units.is_metric
)
self._attr_device_info = coordinator.device_info self._attr_device_info = coordinator.device_info
if forecast_day is not None:
self.forecast_day = forecast_day self.forecast_day = forecast_day
@property @property
def native_value(self) -> StateType: def native_value(self) -> StateType:
"""Return the state.""" """Return the state."""
if self.forecast_day is not None: return self.entity_description.value_fn(self._sensor_data, self._unit_system)
if self.entity_description.device_class == SensorDeviceClass.TEMPERATURE:
return cast(float, self._sensor_data["Value"])
if self.entity_description.key == "UVIndex":
return cast(int, self._sensor_data["Value"])
if self.entity_description.key in ("Grass", "Mold", "Ragweed", "Tree", "Ozone"):
return cast(int, self._sensor_data["Value"])
if self.entity_description.key == "Ceiling":
return round(self._sensor_data[self._unit_system]["Value"])
if self.entity_description.key == "PressureTendency":
return cast(str, self._sensor_data["LocalizedText"].lower())
if self.entity_description.device_class == SensorDeviceClass.TEMPERATURE:
return cast(float, self._sensor_data[self._unit_system]["Value"])
if self.entity_description.key == "Precipitation":
return cast(float, self._sensor_data[self._unit_system]["Value"])
if self.entity_description.key in ("Wind", "WindGust"):
return cast(float, self._sensor_data["Speed"][self._unit_system]["Value"])
if self.entity_description.key in (
"WindDay",
"WindNight",
"WindGustDay",
"WindGustNight",
):
return cast(StateType, self._sensor_data["Speed"]["Value"])
return cast(StateType, self._sensor_data)
@property @property
def extra_state_attributes(self) -> dict[str, Any]: def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes.""" """Return the state attributes."""
if self.forecast_day is not None: return self.entity_description.attr_fn(self.coordinator.data)
if self.entity_description.key in (
"WindDay",
"WindNight",
"WindGustDay",
"WindGustNight",
):
self._attrs["direction"] = self._sensor_data["Direction"]["English"]
elif self.entity_description.key in (
"Grass",
"Mold",
"Ozone",
"Ragweed",
"Tree",
"UVIndex",
):
self._attrs["level"] = self._sensor_data["Category"]
return self._attrs
if self.entity_description.key == "UVIndex":
self._attrs["level"] = self.coordinator.data["UVIndexText"]
elif self.entity_description.key == "Precipitation":
self._attrs["type"] = self.coordinator.data["PrecipitationType"]
return self._attrs
@callback @callback
def _handle_coordinator_update(self) -> None: def _handle_coordinator_update(self) -> None:
"""Handle data update.""" """Handle data update."""
self._sensor_data = _get_sensor_data( self._sensor_data = _get_sensor_data(
self.coordinator.data, self.forecast_day, self.entity_description.key self.coordinator.data, self.entity_description.key
) )
self.async_write_ha_state() self.async_write_ha_state()
def _get_sensor_data( def _get_sensor_data(
sensors: dict[str, Any], forecast_day: int | None, kind: str sensors: dict[str, Any],
kind: str,
forecast_day: int | None = None,
) -> Any: ) -> Any:
"""Get sensor data.""" """Get sensor data."""
if forecast_day is not None: if forecast_day is not None:
@ -453,3 +448,20 @@ def _get_sensor_data(
return sensors["PrecipitationSummary"][kind] return sensors["PrecipitationSummary"][kind]
return sensors[kind] return sensors[kind]
class AccuWeatherForecastSensor(AccuWeatherSensor):
"""Define an AccuWeather forecast entity."""
@property
def extra_state_attributes(self) -> dict[str, Any]:
"""Return the state attributes."""
return self.entity_description.attr_fn(self._sensor_data)
@callback
def _handle_coordinator_update(self) -> None:
"""Handle data update."""
self._sensor_data = _get_sensor_data(
self.coordinator.data, self.entity_description.key, self.forecast_day
)
self.async_write_ha_state()

View File

@ -3,6 +3,9 @@
"abort": { "abort": {
"single_instance_allowed": "Ya configurado. Solo es posible una \u00fanica configuraci\u00f3n." "single_instance_allowed": "Ya configurado. Solo es posible una \u00fanica configuraci\u00f3n."
}, },
"create_entry": {
"default": "Algunos sensores no est\u00e1n habilitados de forma predeterminada. Puede habilitarlos en el registro de la entidad despu\u00e9s de la configuraci\u00f3n de la integraci\u00f3n. \nEl pron\u00f3stico del tiempo no est\u00e1 habilitado de forma predeterminada. Puedes habilitarlo en las opciones de integraci\u00f3n."
},
"error": { "error": {
"cannot_connect": "No se pudo conectar", "cannot_connect": "No se pudo conectar",
"invalid_api_key": "Clave de API no v\u00e1lida", "invalid_api_key": "Clave de API no v\u00e1lida",
@ -21,6 +24,7 @@
}, },
"system_health": { "system_health": {
"info": { "info": {
"can_reach_server": "Llegar al servidor de AccuWeather",
"remaining_requests": "Solicitudes permitidas restantes" "remaining_requests": "Solicitudes permitidas restantes"
} }
} }

View File

@ -4,7 +4,7 @@
"single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n."
}, },
"create_entry": { "create_entry": {
"default": "Algunos sensores no est\u00e1n habilitados de forma predeterminada. Puede habilitarlos en el registro de la entidad despu\u00e9s de la configuraci\u00f3n de la integraci\u00f3n.\n El pron\u00f3stico del tiempo no est\u00e1 habilitado de forma predeterminada. Puedes habilitarlo en las opciones de integraci\u00f3n." "default": "Algunos sensores no est\u00e1n habilitados de forma predeterminada. Puedes habilitarlos en el registro de la entidad despu\u00e9s de la configuraci\u00f3n de la integraci\u00f3n.\nEl pron\u00f3stico del tiempo no est\u00e1 habilitado de forma predeterminada. Puedes habilitarlo en las opciones de integraci\u00f3n."
}, },
"error": { "error": {
"cannot_connect": "No se pudo conectar", "cannot_connect": "No se pudo conectar",
@ -28,13 +28,13 @@
"data": { "data": {
"forecast": "Pron\u00f3stico del tiempo" "forecast": "Pron\u00f3stico del tiempo"
}, },
"description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilitas el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 64 minutos en lugar de cada 32 minutos." "description": "Debido a las limitaciones de la versi\u00f3n gratuita de la clave API de AccuWeather, cuando habilitas el pron\u00f3stico del tiempo, las actualizaciones de datos se realizar\u00e1n cada 80 minutos en lugar de cada 40 minutos."
} }
} }
}, },
"system_health": { "system_health": {
"info": { "info": {
"can_reach_server": "Alcanzar el servidor AccuWeather", "can_reach_server": "Se puede llegar al servidor AccuWeather",
"remaining_requests": "Solicitudes permitidas restantes" "remaining_requests": "Solicitudes permitidas restantes"
} }
} }

View File

@ -2,7 +2,8 @@
"state": { "state": {
"accuweather__pressure_tendency": { "accuweather__pressure_tendency": {
"falling": "Fallande", "falling": "Fallande",
"rising": "Stigande" "rising": "Stigande",
"steady": "Stadig"
} }
} }
} }

View File

@ -1,11 +1,41 @@
{ {
"config": { "config": {
"abort": {
"single_instance_allowed": "Redan konfigurerad. Endast en konfiguration m\u00f6jlig."
},
"create_entry": {
"default": "Vissa sensorer \u00e4r inte aktiverade som standard. Du kan aktivera dem i entitetsregistret efter integrationskonfigurationen.\n V\u00e4derprognos \u00e4r inte aktiverat som standard. Du kan aktivera det i integrationsalternativen."
},
"error": {
"cannot_connect": "Det gick inte att ansluta.",
"invalid_api_key": "Ogiltig API-nyckel",
"requests_exceeded": "Det till\u00e5tna antalet f\u00f6rfr\u00e5gningar till Accuweather API har \u00f6verskridits. Du m\u00e5ste v\u00e4nta eller \u00e4ndra API-nyckel."
},
"step": { "step": {
"user": { "user": {
"data": { "data": {
"api_key": "API-nyckel" "api_key": "API-nyckel",
"latitude": "Latitud",
"longitude": "Longitud",
"name": "Namn"
} }
} }
} }
},
"options": {
"step": {
"user": {
"data": {
"forecast": "V\u00e4derprognos"
},
"description": "P\u00e5 grund av begr\u00e4nsningarna f\u00f6r den kostnadsfria versionen av AccuWeather API-nyckeln, n\u00e4r du aktiverar v\u00e4derprognos, kommer datauppdateringar att utf\u00f6ras var 80:e minut ist\u00e4llet f\u00f6r var 40:e minut."
}
}
},
"system_health": {
"info": {
"can_reach_server": "N\u00e5 AccuWeather-servern",
"remaining_requests": "\u00c5terst\u00e5ende till\u00e5tna f\u00f6rfr\u00e5gningar"
}
} }
} }

View File

@ -70,7 +70,7 @@ class AccuWeatherEntity(
# Coordinator data is used also for sensors which don't have units automatically # Coordinator data is used also for sensors which don't have units automatically
# converted, hence the weather entity's native units follow the configured unit # converted, hence the weather entity's native units follow the configured unit
# system # system
if coordinator.is_metric: if coordinator.hass.config.units.is_metric:
self._attr_native_precipitation_unit = LENGTH_MILLIMETERS self._attr_native_precipitation_unit = LENGTH_MILLIMETERS
self._attr_native_pressure_unit = PRESSURE_HPA self._attr_native_pressure_unit = PRESSURE_HPA
self._attr_native_temperature_unit = TEMP_CELSIUS self._attr_native_temperature_unit = TEMP_CELSIUS

View File

@ -1,4 +1,6 @@
"""Base class for Acmeda Roller Blinds.""" """Base class for Acmeda Roller Blinds."""
from __future__ import annotations
import aiopulse import aiopulse
from homeassistant.core import callback from homeassistant.core import callback
@ -11,11 +13,13 @@ from .const import ACMEDA_ENTITY_REMOVE, DOMAIN, LOGGER
class AcmedaBase(entity.Entity): class AcmedaBase(entity.Entity):
"""Base representation of an Acmeda roller.""" """Base representation of an Acmeda roller."""
_attr_should_poll = False
def __init__(self, roller: aiopulse.Roller) -> None: def __init__(self, roller: aiopulse.Roller) -> None:
"""Initialize the roller.""" """Initialize the roller."""
self.roller = roller self.roller = roller
async def async_remove_and_unregister(self): async def async_remove_and_unregister(self) -> None:
"""Unregister from entity and device registry and call entity remove function.""" """Unregister from entity and device registry and call entity remove function."""
LOGGER.error("Removing %s %s", self.__class__.__name__, self.unique_id) LOGGER.error("Removing %s %s", self.__class__.__name__, self.unique_id)
@ -25,14 +29,18 @@ class AcmedaBase(entity.Entity):
dev_registry = dr.async_get(self.hass) dev_registry = dr.async_get(self.hass)
device = dev_registry.async_get_device(identifiers={(DOMAIN, self.unique_id)}) device = dev_registry.async_get_device(identifiers={(DOMAIN, self.unique_id)})
if device is not None: if (
device is not None
and self.registry_entry is not None
and self.registry_entry.config_entry_id is not None
):
dev_registry.async_update_device( dev_registry.async_update_device(
device.id, remove_config_entry_id=self.registry_entry.config_entry_id device.id, remove_config_entry_id=self.registry_entry.config_entry_id
) )
await self.async_remove(force_remove=True) await self.async_remove(force_remove=True)
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Entity has been added to hass.""" """Entity has been added to hass."""
self.roller.callback_subscribe(self.notify_update) self.roller.callback_subscribe(self.notify_update)
@ -44,33 +52,28 @@ class AcmedaBase(entity.Entity):
) )
) )
async def async_will_remove_from_hass(self): async def async_will_remove_from_hass(self) -> None:
"""Entity being removed from hass.""" """Entity being removed from hass."""
self.roller.callback_unsubscribe(self.notify_update) self.roller.callback_unsubscribe(self.notify_update)
@callback @callback
def notify_update(self): def notify_update(self) -> None:
"""Write updated device state information.""" """Write updated device state information."""
LOGGER.debug("Device update notification received: %s", self.name) LOGGER.debug("Device update notification received: %s", self.name)
self.async_write_ha_state() self.async_write_ha_state()
@property @property
def should_poll(self): def unique_id(self) -> str:
"""Report that Acmeda entities do not need polling."""
return False
@property
def unique_id(self):
"""Return the unique ID of this roller.""" """Return the unique ID of this roller."""
return self.roller.id return self.roller.id
@property @property
def device_id(self): def device_id(self) -> str:
"""Return the ID of this roller.""" """Return the ID of this roller."""
return self.roller.id return self.roller.id
@property @property
def name(self): def name(self) -> str | None:
"""Return the name of roller.""" """Return the name of roller."""
return self.roller.name return self.roller.name

View File

@ -11,6 +11,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .base import AcmedaBase from .base import AcmedaBase
from .const import ACMEDA_HUB_UPDATE, DOMAIN from .const import ACMEDA_HUB_UPDATE, DOMAIN
from .helpers import async_add_acmeda_entities from .helpers import async_add_acmeda_entities
from .hub import PulseHub
async def async_setup_entry( async def async_setup_entry(
@ -19,7 +20,7 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Set up the Acmeda Rollers from a config entry.""" """Set up the Acmeda Rollers from a config entry."""
hub = hass.data[DOMAIN][config_entry.entry_id] hub: PulseHub = hass.data[DOMAIN][config_entry.entry_id]
current: set[int] = set() current: set[int] = set()
@ -41,15 +42,15 @@ async def async_setup_entry(
class AcmedaBattery(AcmedaBase, SensorEntity): class AcmedaBattery(AcmedaBase, SensorEntity):
"""Representation of a Acmeda cover device.""" """Representation of a Acmeda cover device."""
device_class = SensorDeviceClass.BATTERY _attr_device_class = SensorDeviceClass.BATTERY
_attr_native_unit_of_measurement = PERCENTAGE _attr_native_unit_of_measurement = PERCENTAGE
@property @property
def name(self): def name(self) -> str:
"""Return the name of roller.""" """Return the name of roller."""
return f"{super().name} Battery" return f"{super().name} Battery"
@property @property
def native_value(self): def native_value(self) -> float | int | None:
"""Return the state of the device.""" """Return the state of the device."""
return self.roller.battery return self.roller.battery

View File

@ -8,7 +8,7 @@
"data": { "data": {
"id": "ID de host" "id": "ID de host"
}, },
"title": "Elige un hub para a\u00f1adir" "title": "Elige un concentrador para a\u00f1adir"
} }
} }
} }

View File

@ -0,0 +1,15 @@
{
"config": {
"abort": {
"no_devices_found": "Inga enheter hittades i n\u00e4tverket"
},
"step": {
"user": {
"data": {
"id": "V\u00e4rd-ID"
},
"title": "V\u00e4lj en hubb att l\u00e4gga till"
}
}
}
}

View File

@ -80,7 +80,7 @@ class AdaxDevice(ClimateEntity):
manufacturer="Adax", manufacturer="Adax",
) )
async def async_set_hvac_mode(self, hvac_mode: str) -> None: async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set hvac mode.""" """Set hvac mode."""
if hvac_mode == HVACMode.HEAT: if hvac_mode == HVACMode.HEAT:
temperature = max(self.min_temp, self.target_temperature or self.min_temp) temperature = max(self.min_temp, self.target_temperature or self.min_temp)
@ -140,7 +140,7 @@ class LocalAdaxDevice(ClimateEntity):
manufacturer="Adax", manufacturer="Adax",
) )
async def async_set_temperature(self, **kwargs): async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature.""" """Set new target temperature."""
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None: if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
return return

View File

@ -2,8 +2,8 @@
"config": { "config": {
"abort": { "abort": {
"already_configured": "El dispositivo ya est\u00e1 configurado", "already_configured": "El dispositivo ya est\u00e1 configurado",
"heater_not_available": "Calentador no disponible. Intente restablecer el calentador pulsando + y OK durante algunos segundos.", "heater_not_available": "Calefactor no disponible. Intenta reiniciar el calentador presionando + y OK durante unos segundos.",
"heater_not_found": "No se encuentra el calefactor. Intente acercar el calefactor al ordenador del Asistente de Hogar.", "heater_not_found": "No se encontr\u00f3 el calentador. Intenta acercar el calentador al ordenador con Home Assistant.",
"invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida"
}, },
"error": { "error": {
@ -21,13 +21,13 @@
"wifi_pswd": "Contrase\u00f1a Wi-Fi", "wifi_pswd": "Contrase\u00f1a Wi-Fi",
"wifi_ssid": "SSID Wi-Fi" "wifi_ssid": "SSID Wi-Fi"
}, },
"description": "Reinicie el calentador presionando + y OK hasta que la pantalla muestre 'Reiniciar'. Luego presione y mantenga presionado el bot\u00f3n OK en el calentador hasta que el LED azul comience a parpadear antes de presionar Enviar. La configuraci\u00f3n del calentador puede llevar algunos minutos." "description": "Reinicia el calentador pulsando + y OK hasta que la pantalla muestre 'Restablecer'. Luego mant\u00e9n pulsado el bot\u00f3n OK en el calentador hasta que el led azul comience a parpadear antes de pulsar Enviar. La configuraci\u00f3n del calentador puede tardar algunos minutos."
}, },
"user": { "user": {
"data": { "data": {
"connection_type": "Seleccione el tipo de conexi\u00f3n" "connection_type": "Selecciona el tipo de conexi\u00f3n"
}, },
"description": "Seleccione el tipo de conexi\u00f3n. Local requiere calentadores con bluetooth" "description": "Selecciona el tipo de conexi\u00f3n. Local requiere calefactores con bluetooth"
} }
} }
} }

View File

@ -0,0 +1,34 @@
{
"config": {
"abort": {
"already_configured": "Enheten \u00e4r redan konfigurerad",
"heater_not_available": "V\u00e4rmare inte tillg\u00e4nglig. F\u00f6rs\u00f6k att \u00e5terst\u00e4lla v\u00e4rmaren genom att trycka p\u00e5 + och OK i n\u00e5gra sekunder.",
"heater_not_found": "V\u00e4rmare hittades inte. F\u00f6rs\u00f6k att flytta v\u00e4rmaren n\u00e4rmare Home Assistant-datorn.",
"invalid_auth": "Ogiltig autentisering"
},
"error": {
"cannot_connect": "Det gick inte att ansluta."
},
"step": {
"cloud": {
"data": {
"account_id": "Konto-ID",
"password": "L\u00f6senord"
}
},
"local": {
"data": {
"wifi_pswd": "Wi-Fi l\u00f6senord",
"wifi_ssid": "Wi-Fi SSID"
},
"description": "\u00c5terst\u00e4ll v\u00e4rmaren genom att trycka p\u00e5 + och OK tills displayen visar 'Reset'. Tryck sedan och h\u00e5ll ner OK-knappen p\u00e5 v\u00e4rmaren tills den bl\u00e5 lysdioden b\u00f6rjar blinka innan du trycker p\u00e5 Skicka. Det kan ta n\u00e5gra minuter att konfigurera v\u00e4rmaren."
},
"user": {
"data": {
"connection_type": "V\u00e4lj anslutningstyp"
},
"description": "V\u00e4lj anslutningstyp. Lokalt kr\u00e4ver v\u00e4rmare med bluetooth"
}
}
}
}

View File

@ -27,7 +27,7 @@
"data": { "data": {
"connection_type": "\u9078\u64c7\u9023\u7dda\u985e\u5225" "connection_type": "\u9078\u64c7\u9023\u7dda\u985e\u5225"
}, },
"description": "\u9078\u64c7\u9023\u7dda\u985e\u5225\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\u7259\u52a0\u71b1\u5668"
} }
} }
} }

View File

@ -9,8 +9,8 @@
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "\u00bfDesea configurar Home Assistant para conectarse al AdGuard Home proporcionado por el complemento Supervisor: {addon} ?", "description": "\u00bfQuieres configurar Home Assistant para conectarse al AdGuard Home proporcionado por el complemento: {addon}?",
"title": "AdGuard Home v\u00eda complemento de Home Assistant" "title": "AdGuard Home a trav\u00e9s del complemento Home Assistant"
}, },
"user": { "user": {
"data": { "data": {
@ -18,10 +18,10 @@
"password": "Contrase\u00f1a", "password": "Contrase\u00f1a",
"port": "Puerto", "port": "Puerto",
"ssl": "Utiliza un certificado SSL", "ssl": "Utiliza un certificado SSL",
"username": "Usuario", "username": "Nombre de usuario",
"verify_ssl": "Verificar certificado SSL" "verify_ssl": "Verificar el certificado SSL"
}, },
"description": "Configure su instancia de AdGuard Home para permitir la supervisi\u00f3n y el control." "description": "Configura tu instancia AdGuard Home para permitir la supervisi\u00f3n y el control."
} }
} }
} }

View File

@ -1,8 +1,12 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Tj\u00e4nsten \u00e4r redan konfigurerad",
"existing_instance_updated": "Uppdaterade existerande konfiguration." "existing_instance_updated": "Uppdaterade existerande konfiguration."
}, },
"error": {
"cannot_connect": "Det gick inte att ansluta."
},
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "Vill du konfigurera Home Assistant f\u00f6r att ansluta till AdGuard Home som tillhandah\u00e5lls av Supervisor Add-on: {addon}?", "description": "Vill du konfigurera Home Assistant f\u00f6r att ansluta till AdGuard Home som tillhandah\u00e5lls av Supervisor Add-on: {addon}?",
@ -10,7 +14,9 @@
}, },
"user": { "user": {
"data": { "data": {
"host": "V\u00e4rd",
"password": "L\u00f6senord", "password": "L\u00f6senord",
"port": "Port",
"ssl": "AdGuard Home anv\u00e4nder ett SSL-certifikat", "ssl": "AdGuard Home anv\u00e4nder ett SSL-certifikat",
"username": "Anv\u00e4ndarnamn", "username": "Anv\u00e4ndarnamn",
"verify_ssl": "AdGuard Home anv\u00e4nder ett korrekt certifikat" "verify_ssl": "AdGuard Home anv\u00e4nder ett korrekt certifikat"

View File

@ -53,7 +53,7 @@ class AdsBinarySensor(AdsEntity, BinarySensorEntity):
super().__init__(ads_hub, name, ads_var) super().__init__(ads_hub, name, ads_var)
self._attr_device_class = device_class or BinarySensorDeviceClass.MOVING self._attr_device_class = device_class or BinarySensorDeviceClass.MOVING
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Register device notification.""" """Register device notification."""
await self.async_initialize_device(self._ads_var, pyads.PLCTYPE_BOOL) await self.async_initialize_device(self._ads_var, pyads.PLCTYPE_BOOL)

View File

@ -1,6 +1,8 @@
"""Support for ADS light sources.""" """Support for ADS light sources."""
from __future__ import annotations from __future__ import annotations
from typing import Any
import pyads import pyads
import voluptuous as vol import voluptuous as vol
@ -66,7 +68,7 @@ class AdsLight(AdsEntity, LightEntity):
self._attr_color_mode = ColorMode.ONOFF self._attr_color_mode = ColorMode.ONOFF
self._attr_supported_color_modes = {ColorMode.ONOFF} self._attr_supported_color_modes = {ColorMode.ONOFF}
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Register device notification.""" """Register device notification."""
await self.async_initialize_device(self._ads_var, pyads.PLCTYPE_BOOL) await self.async_initialize_device(self._ads_var, pyads.PLCTYPE_BOOL)
@ -87,7 +89,7 @@ class AdsLight(AdsEntity, LightEntity):
"""Return True if the entity is on.""" """Return True if the entity is on."""
return self._state_dict[STATE_KEY_STATE] return self._state_dict[STATE_KEY_STATE]
def turn_on(self, **kwargs): def turn_on(self, **kwargs: Any) -> None:
"""Turn the light on or set a specific dimmer value.""" """Turn the light on or set a specific dimmer value."""
brightness = kwargs.get(ATTR_BRIGHTNESS) brightness = kwargs.get(ATTR_BRIGHTNESS)
self._ads_hub.write_by_name(self._ads_var, True, pyads.PLCTYPE_BOOL) self._ads_hub.write_by_name(self._ads_var, True, pyads.PLCTYPE_BOOL)
@ -97,6 +99,6 @@ class AdsLight(AdsEntity, LightEntity):
self._ads_var_brightness, brightness, pyads.PLCTYPE_UINT self._ads_var_brightness, brightness, pyads.PLCTYPE_UINT
) )
def turn_off(self, **kwargs): def turn_off(self, **kwargs: Any) -> None:
"""Turn the light off.""" """Turn the light off."""
self._ads_hub.write_by_name(self._ads_var, False, pyads.PLCTYPE_BOOL) self._ads_hub.write_by_name(self._ads_var, False, pyads.PLCTYPE_BOOL)

View File

@ -3,7 +3,6 @@ from __future__ import annotations
import voluptuous as vol import voluptuous as vol
from homeassistant.components import ads
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.const import CONF_NAME, CONF_UNIT_OF_MEASUREMENT from homeassistant.const import CONF_NAME, CONF_UNIT_OF_MEASUREMENT
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -19,6 +18,7 @@ from . import (
STATE_KEY_STATE, STATE_KEY_STATE,
AdsEntity, AdsEntity,
) )
from .. import ads
DEFAULT_NAME = "ADS sensor" DEFAULT_NAME = "ADS sensor"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
@ -70,7 +70,7 @@ class AdsSensor(AdsEntity, SensorEntity):
self._ads_type = ads_type self._ads_type = ads_type
self._factor = factor self._factor = factor
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Register device notification.""" """Register device notification."""
await self.async_initialize_device( await self.async_initialize_device(
self._ads_var, self._ads_var,

View File

@ -1,6 +1,8 @@
"""Support for ADS switch platform.""" """Support for ADS switch platform."""
from __future__ import annotations from __future__ import annotations
from typing import Any
import pyads import pyads
import voluptuous as vol import voluptuous as vol
@ -41,7 +43,7 @@ def setup_platform(
class AdsSwitch(AdsEntity, SwitchEntity): class AdsSwitch(AdsEntity, SwitchEntity):
"""Representation of an ADS switch device.""" """Representation of an ADS switch device."""
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Register device notification.""" """Register device notification."""
await self.async_initialize_device(self._ads_var, pyads.PLCTYPE_BOOL) await self.async_initialize_device(self._ads_var, pyads.PLCTYPE_BOOL)
@ -50,10 +52,10 @@ class AdsSwitch(AdsEntity, SwitchEntity):
"""Return True if the entity is on.""" """Return True if the entity is on."""
return self._state_dict[STATE_KEY_STATE] return self._state_dict[STATE_KEY_STATE]
def turn_on(self, **kwargs): def turn_on(self, **kwargs: Any) -> None:
"""Turn the switch on.""" """Turn the switch on."""
self._ads_hub.write_by_name(self._ads_var, True, pyads.PLCTYPE_BOOL) self._ads_hub.write_by_name(self._ads_var, True, pyads.PLCTYPE_BOOL)
def turn_off(self, **kwargs): def turn_off(self, **kwargs: Any) -> None:
"""Turn the switch off.""" """Turn the switch off."""
self._ads_hub.write_by_name(self._ads_var, False, pyads.PLCTYPE_BOOL) self._ads_hub.write_by_name(self._ads_var, False, pyads.PLCTYPE_BOOL)

View File

@ -20,6 +20,8 @@ PLATFORMS = [
Platform.SELECT, Platform.SELECT,
Platform.SENSOR, Platform.SENSOR,
Platform.SWITCH, Platform.SWITCH,
Platform.UPDATE,
Platform.LIGHT,
] ]
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -50,19 +52,25 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
update_interval=timedelta(seconds=ADVANTAGE_AIR_SYNC_INTERVAL), update_interval=timedelta(seconds=ADVANTAGE_AIR_SYNC_INTERVAL),
) )
async def async_change(change): def error_handle_factory(func):
"""Return the provided API function wrapped in an error handler and coordinator refresh."""
async def error_handle(param):
try: try:
if await api.async_change(change): if await func(param):
await coordinator.async_refresh() await coordinator.async_refresh()
except ApiError as err: except ApiError as err:
_LOGGER.warning(err) _LOGGER.warning(err)
return error_handle
await coordinator.async_config_entry_first_refresh() await coordinator.async_config_entry_first_refresh()
hass.data.setdefault(DOMAIN, {}) hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = { hass.data[DOMAIN][entry.entry_id] = {
"coordinator": coordinator, "coordinator": coordinator,
"async_change": async_change, "async_change": error_handle_factory(api.aircon.async_set),
"async_set_light": error_handle_factory(api.lights.async_set),
} }
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import Any
from homeassistant.components.climate import ClimateEntity from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
@ -44,7 +45,7 @@ AC_HVAC_MODES = [
] ]
ADVANTAGE_AIR_FAN_MODES = { ADVANTAGE_AIR_FAN_MODES = {
"auto": FAN_AUTO, "autoAA": FAN_AUTO,
"low": FAN_LOW, "low": FAN_LOW,
"medium": FAN_MEDIUM, "medium": FAN_MEDIUM,
"high": FAN_HIGH, "high": FAN_HIGH,
@ -114,7 +115,7 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
"""Return the current fan modes.""" """Return the current fan modes."""
return ADVANTAGE_AIR_FAN_MODES.get(self._ac["fan"]) return ADVANTAGE_AIR_FAN_MODES.get(self._ac["fan"])
async def async_set_hvac_mode(self, hvac_mode): async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC Mode and State.""" """Set the HVAC Mode and State."""
if hvac_mode == HVACMode.OFF: if hvac_mode == HVACMode.OFF:
await self.async_change( await self.async_change(
@ -132,13 +133,13 @@ class AdvantageAirAC(AdvantageAirAcEntity, ClimateEntity):
} }
) )
async def async_set_fan_mode(self, fan_mode): async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set the Fan Mode.""" """Set the Fan Mode."""
await self.async_change( await self.async_change(
{self.ac_key: {"info": {"fan": HASS_FAN_MODES.get(fan_mode)}}} {self.ac_key: {"info": {"fan": HASS_FAN_MODES.get(fan_mode)}}}
) )
async def async_set_temperature(self, **kwargs): async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the Temperature.""" """Set the Temperature."""
temp = kwargs.get(ATTR_TEMPERATURE) temp = kwargs.get(ATTR_TEMPERATURE)
await self.async_change({self.ac_key: {"info": {"setTemp": temp}}}) await self.async_change({self.ac_key: {"info": {"setTemp": temp}}})
@ -179,7 +180,7 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
"""Return the target temperature.""" """Return the target temperature."""
return self._zone["setTemp"] return self._zone["setTemp"]
async def async_set_hvac_mode(self, hvac_mode): async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set the HVAC Mode and State.""" """Set the HVAC Mode and State."""
if hvac_mode == HVACMode.OFF: if hvac_mode == HVACMode.OFF:
await self.async_change( await self.async_change(
@ -198,7 +199,7 @@ class AdvantageAirZone(AdvantageAirZoneEntity, ClimateEntity):
} }
) )
async def async_set_temperature(self, **kwargs): async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set the Temperature.""" """Set the Temperature."""
temp = kwargs.get(ATTR_TEMPERATURE) temp = kwargs.get(ATTR_TEMPERATURE)
await self.async_change( await self.async_change(

View File

@ -0,0 +1,90 @@
"""Light platform for Advantage Air integration."""
from typing import Any
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import (
ADVANTAGE_AIR_STATE_OFF,
ADVANTAGE_AIR_STATE_ON,
DOMAIN as ADVANTAGE_AIR_DOMAIN,
)
from .entity import AdvantageAirEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up AdvantageAir light platform."""
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
entities = []
if "myLights" in instance["coordinator"].data:
for light in instance["coordinator"].data["myLights"]["lights"].values():
if light.get("relay"):
entities.append(AdvantageAirLight(instance, light))
else:
entities.append(AdvantageAirLightDimmable(instance, light))
async_add_entities(entities)
class AdvantageAirLight(AdvantageAirEntity, LightEntity):
"""Representation of Advantage Air Light."""
_attr_supported_color_modes = {ColorMode.ONOFF}
def __init__(self, instance, light):
"""Initialize an Advantage Air Light."""
super().__init__(instance)
self.async_set_light = instance["async_set_light"]
self._id = light["id"]
self._attr_unique_id += f"-{self._id}"
self._attr_device_info = DeviceInfo(
identifiers={(ADVANTAGE_AIR_DOMAIN, self._attr_unique_id)},
via_device=(ADVANTAGE_AIR_DOMAIN, self.coordinator.data["system"]["rid"]),
manufacturer="Advantage Air",
model=light.get("moduleType"),
name=light["name"],
)
@property
def _light(self):
"""Return the light object."""
return self.coordinator.data["myLights"]["lights"][self._id]
@property
def is_on(self) -> bool:
"""Return if the light is on."""
return self._light["state"] == ADVANTAGE_AIR_STATE_ON
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on."""
await self.async_set_light({"id": self._id, "state": ADVANTAGE_AIR_STATE_ON})
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn the light off."""
await self.async_set_light({"id": self._id, "state": ADVANTAGE_AIR_STATE_OFF})
class AdvantageAirLightDimmable(AdvantageAirLight):
"""Representation of Advantage Air Dimmable Light."""
_attr_supported_color_modes = {ColorMode.ONOFF, ColorMode.BRIGHTNESS}
@property
def brightness(self) -> int:
"""Return the brightness of this light between 0..255."""
return round(self._light["value"] * 255 / 100)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the light on and optionally set the brightness."""
data = {"id": self._id, "state": ADVANTAGE_AIR_STATE_ON}
if ATTR_BRIGHTNESS in kwargs:
data["value"] = round(kwargs[ATTR_BRIGHTNESS] * 100 / 255)
await self.async_set_light(data)

View File

@ -4,7 +4,7 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/advantage_air", "documentation": "https://www.home-assistant.io/integrations/advantage_air",
"codeowners": ["@Bre77"], "codeowners": ["@Bre77"],
"requirements": ["advantage_air==0.3.1"], "requirements": ["advantage_air==0.4.1"],
"quality_scale": "platinum", "quality_scale": "platinum",
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["advantage_air"] "loggers": ["advantage_air"]

View File

@ -29,15 +29,15 @@ class AdvantageAirMyZone(AdvantageAirAcEntity, SelectEntity):
"""Representation of Advantage Air MyZone control.""" """Representation of Advantage Air MyZone control."""
_attr_icon = "mdi:home-thermometer" _attr_icon = "mdi:home-thermometer"
_attr_options = [ADVANTAGE_AIR_INACTIVE]
_number_to_name = {0: ADVANTAGE_AIR_INACTIVE}
_name_to_number = {ADVANTAGE_AIR_INACTIVE: 0}
_attr_name = "MyZone" _attr_name = "MyZone"
def __init__(self, instance, ac_key): def __init__(self, instance, ac_key):
"""Initialize an Advantage Air MyZone control.""" """Initialize an Advantage Air MyZone control."""
super().__init__(instance, ac_key) super().__init__(instance, ac_key)
self._attr_unique_id += "-myzone" self._attr_unique_id += "-myzone"
self._attr_options = [ADVANTAGE_AIR_INACTIVE]
self._number_to_name = {0: ADVANTAGE_AIR_INACTIVE}
self._name_to_number = {ADVANTAGE_AIR_INACTIVE: 0}
for zone in instance["coordinator"].data["aircons"][ac_key]["zones"].values(): for zone in instance["coordinator"].data["aircons"][ac_key]["zones"].values():
if zone["type"] > 0: if zone["type"] > 0:
@ -46,11 +46,11 @@ class AdvantageAirMyZone(AdvantageAirAcEntity, SelectEntity):
self._attr_options.append(zone["name"]) self._attr_options.append(zone["name"])
@property @property
def current_option(self): def current_option(self) -> str:
"""Return the current MyZone.""" """Return the current MyZone."""
return self._number_to_name[self._ac["myZone"]] return self._number_to_name[self._ac["myZone"]]
async def async_select_option(self, option): async def async_select_option(self, option: str) -> None:
"""Set the MyZone.""" """Set the MyZone."""
await self.async_change( await self.async_change(
{self.ac_key: {"info": {"myZone": self._name_to_number[option]}}} {self.ac_key: {"info": {"myZone": self._name_to_number[option]}}}

View File

@ -1,4 +1,6 @@
"""Switch platform for Advantage Air integration.""" """Switch platform for Advantage Air integration."""
from typing import Any
from homeassistant.components.switch import SwitchEntity from homeassistant.components.switch import SwitchEntity
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
@ -44,13 +46,13 @@ class AdvantageAirFreshAir(AdvantageAirAcEntity, SwitchEntity):
"""Return the fresh air status.""" """Return the fresh air status."""
return self._ac["freshAirStatus"] == ADVANTAGE_AIR_STATE_ON return self._ac["freshAirStatus"] == ADVANTAGE_AIR_STATE_ON
async def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn fresh air on.""" """Turn fresh air on."""
await self.async_change( await self.async_change(
{self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_ON}}} {self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_ON}}}
) )
async def async_turn_off(self, **kwargs): async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn fresh air off.""" """Turn fresh air off."""
await self.async_change( await self.async_change(
{self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_OFF}}} {self.ac_key: {"info": {"freshAirStatus": ADVANTAGE_AIR_STATE_OFF}}}

View File

@ -0,0 +1,20 @@
{
"config": {
"abort": {
"already_configured": "Enheten \u00e4r redan konfigurerad"
},
"error": {
"cannot_connect": "Det gick inte att ansluta."
},
"step": {
"user": {
"data": {
"ip_address": "IP-adress",
"port": "Port"
},
"description": "Anslut till API:et f\u00f6r din Advantage Air v\u00e4ggmonterade surfplatta.",
"title": "Anslut"
}
}
}
}

View File

@ -0,0 +1,52 @@
"""Advantage Air Update platform."""
from homeassistant.components.update import UpdateEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import DOMAIN as ADVANTAGE_AIR_DOMAIN
from .entity import AdvantageAirEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up AdvantageAir update platform."""
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
async_add_entities([AdvantageAirApp(instance)])
class AdvantageAirApp(AdvantageAirEntity, UpdateEntity):
"""Representation of Advantage Air App."""
_attr_name = "App"
def __init__(self, instance):
"""Initialize the Advantage Air App."""
super().__init__(instance)
self._attr_device_info = DeviceInfo(
identifiers={
(ADVANTAGE_AIR_DOMAIN, self.coordinator.data["system"]["rid"])
},
manufacturer="Advantage Air",
model=self.coordinator.data["system"]["sysType"],
name=self.coordinator.data["system"]["name"],
sw_version=self.coordinator.data["system"]["myAppRev"],
)
@property
def installed_version(self):
"""Return the current app version."""
return self.coordinator.data["system"]["myAppRev"]
@property
def latest_version(self):
"""Return if there is an update."""
if self.coordinator.data["system"]["needsUpdate"]:
return "Needs Update"
return self.installed_version

View File

@ -3,7 +3,6 @@ from __future__ import annotations
from homeassistant.components.sensor import SensorEntity, SensorEntityDescription from homeassistant.components.sensor import SensorEntity, SensorEntityDescription
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_ATTRIBUTION
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity from homeassistant.helpers.update_coordinator import CoordinatorEntity
@ -63,7 +62,7 @@ async def async_setup_entry(
class AbstractAemetSensor(CoordinatorEntity[WeatherUpdateCoordinator], SensorEntity): class AbstractAemetSensor(CoordinatorEntity[WeatherUpdateCoordinator], SensorEntity):
"""Abstract class for an AEMET OpenData sensor.""" """Abstract class for an AEMET OpenData sensor."""
_attr_extra_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION} _attr_attribution = ATTRIBUTION
def __init__( def __init__(
self, self,

View File

@ -14,7 +14,7 @@
"longitude": "Longitud", "longitude": "Longitud",
"name": "Nombre de la integraci\u00f3n" "name": "Nombre de la integraci\u00f3n"
}, },
"description": "Configurar la integraci\u00f3n de AEMET OpenData. Para generar la clave API, ve a https://opendata.aemet.es/centrodedescargas/altaUsuario" "description": "Para generar la clave API, ve a https://opendata.aemet.es/centrodedescargas/altaUsuario"
} }
} }
}, },

View File

@ -1,9 +1,28 @@
{ {
"config": { "config": {
"abort": {
"already_configured": "Platsen \u00e4r redan konfigurerad"
},
"error": {
"invalid_api_key": "Ogiltig API-nyckel"
},
"step": { "step": {
"user": { "user": {
"data": { "data": {
"api_key": "API-nyckel" "api_key": "API-nyckel",
"latitude": "Latitud",
"longitude": "Longitud",
"name": "Integrationens namn"
},
"description": "F\u00f6r att generera API-nyckel g\u00e5 till https://opendata.aemet.es/centrodedescargas/altaUsuario"
}
}
},
"options": {
"step": {
"init": {
"data": {
"station_updates": "Samla data fr\u00e5n AEMET v\u00e4derstationer"
} }
} }
} }

View File

@ -7,7 +7,6 @@ from agent import AgentError
from homeassistant.components.camera import CameraEntityFeature from homeassistant.components.camera import CameraEntityFeature
from homeassistant.components.mjpeg import MjpegCamera, filter_urllib3_logging from homeassistant.components.mjpeg import MjpegCamera, filter_urllib3_logging
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_ATTRIBUTION
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import DeviceInfo from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import ( from homeassistant.helpers.entity_platform import (
@ -70,6 +69,8 @@ async def async_setup_entry(
class AgentCamera(MjpegCamera): class AgentCamera(MjpegCamera):
"""Representation of an Agent Device Stream.""" """Representation of an Agent Device Stream."""
_attr_attribution = ATTRIBUTION
_attr_should_poll = True # Cameras default to False
_attr_supported_features = CameraEntityFeature.ON_OFF _attr_supported_features = CameraEntityFeature.ON_OFF
def __init__(self, device): def __init__(self, device):
@ -91,7 +92,7 @@ class AgentCamera(MjpegCamera):
sw_version=device.client.version, sw_version=device.client.version,
) )
async def async_update(self): async def async_update(self) -> None:
"""Update our state from the Agent API.""" """Update our state from the Agent API."""
try: try:
await self.device.update() await self.device.update()
@ -108,7 +109,6 @@ class AgentCamera(MjpegCamera):
self._attr_icon = "mdi:camcorder" self._attr_icon = "mdi:camcorder"
self._attr_available = self.device.client.is_available self._attr_available = self.device.client.is_available
self._attr_extra_state_attributes = { self._attr_extra_state_attributes = {
ATTR_ATTRIBUTION: ATTRIBUTION,
"editable": False, "editable": False,
"enabled": self.is_on, "enabled": self.is_on,
"connected": self.connected, "connected": self.connected,
@ -118,11 +118,6 @@ class AgentCamera(MjpegCamera):
"alerts_enabled": self.device.alerts_active, "alerts_enabled": self.device.alerts_active,
} }
@property
def should_poll(self) -> bool:
"""Update the state periodically."""
return True
@property @property
def is_recording(self) -> bool: def is_recording(self) -> bool:
"""Return whether the monitor is recording.""" """Return whether the monitor is recording."""
@ -149,7 +144,7 @@ class AgentCamera(MjpegCamera):
return self.device.online return self.device.online
@property @property
def motion_detection_enabled(self): def motion_detection_enabled(self) -> bool:
"""Return the camera motion detection status.""" """Return the camera motion detection status."""
return self.device.detector_active return self.device.detector_active
@ -161,11 +156,11 @@ class AgentCamera(MjpegCamera):
"""Disable alerts.""" """Disable alerts."""
await self.device.alerts_off() await self.device.alerts_off()
async def async_enable_motion_detection(self): async def async_enable_motion_detection(self) -> None:
"""Enable motion detection.""" """Enable motion detection."""
await self.device.detector_on() await self.device.detector_on()
async def async_disable_motion_detection(self): async def async_disable_motion_detection(self) -> None:
"""Disable motion detection.""" """Disable motion detection."""
await self.device.detector_off() await self.device.detector_off()
@ -177,7 +172,7 @@ class AgentCamera(MjpegCamera):
"""Stop recording.""" """Stop recording."""
await self.device.record_stop() await self.device.record_stop()
async def async_turn_on(self): async def async_turn_on(self) -> None:
"""Enable the camera.""" """Enable the camera."""
await self.device.enable() await self.device.enable()
@ -185,6 +180,6 @@ class AgentCamera(MjpegCamera):
"""Take a snapshot.""" """Take a snapshot."""
await self.device.snapshot() await self.device.snapshot()
async def async_turn_off(self): async def async_turn_off(self) -> None:
"""Disable the camera.""" """Disable the camera."""
await self.device.disable() await self.device.disable()

View File

@ -4,7 +4,7 @@
"already_configured": "El dispositivo ya est\u00e1 configurado" "already_configured": "El dispositivo ya est\u00e1 configurado"
}, },
"error": { "error": {
"already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en proceso", "already_in_progress": "El flujo de configuraci\u00f3n ya est\u00e1 en curso",
"cannot_connect": "No se pudo conectar" "cannot_connect": "No se pudo conectar"
}, },
"step": { "step": {
@ -13,7 +13,7 @@
"host": "Host", "host": "Host",
"port": "Puerto" "port": "Puerto"
}, },
"title": "Configurar el Agente de DVR" "title": "Configurar Agent DVR"
} }
} }
} }

View File

@ -11,7 +11,7 @@
"user": { "user": {
"data": { "data": {
"host": "\u05de\u05d0\u05e8\u05d7", "host": "\u05de\u05d0\u05e8\u05d7",
"port": "\u05e4\u05d5\u05e8\u05d8" "port": "\u05e4\u05ea\u05d7\u05d4"
} }
} }
} }

View File

@ -4,7 +4,8 @@
"already_configured": "Enheten \u00e4r redan konfigurerad" "already_configured": "Enheten \u00e4r redan konfigurerad"
}, },
"error": { "error": {
"already_in_progress": "Konfigurationsfl\u00f6de f\u00f6r enhet p\u00e5g\u00e5r redan." "already_in_progress": "Konfigurationsfl\u00f6de f\u00f6r enhet p\u00e5g\u00e5r redan.",
"cannot_connect": "Det gick inte att ansluta."
}, },
"step": { "step": {
"user": { "user": {

View File

@ -13,7 +13,6 @@ from homeassistant.components.sensor import (
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
ATTR_ATTRIBUTION,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONF_NAME, CONF_NAME,
PERCENTAGE, PERCENTAGE,
@ -136,6 +135,7 @@ async def async_setup_entry(
class AirlySensor(CoordinatorEntity[AirlyDataUpdateCoordinator], SensorEntity): class AirlySensor(CoordinatorEntity[AirlyDataUpdateCoordinator], SensorEntity):
"""Define an Airly sensor.""" """Define an Airly sensor."""
_attr_attribution = ATTRIBUTION
_attr_has_entity_name = True _attr_has_entity_name = True
entity_description: AirlySensorEntityDescription entity_description: AirlySensorEntityDescription
@ -159,7 +159,7 @@ class AirlySensor(CoordinatorEntity[AirlyDataUpdateCoordinator], SensorEntity):
self._attr_unique_id = ( self._attr_unique_id = (
f"{coordinator.latitude}-{coordinator.longitude}-{description.key}".lower() f"{coordinator.latitude}-{coordinator.longitude}-{description.key}".lower()
) )
self._attrs: dict[str, Any] = {ATTR_ATTRIBUTION: ATTRIBUTION} self._attrs: dict[str, Any] = {}
self.entity_description = description self.entity_description = description
@property @property

View File

@ -20,6 +20,7 @@
}, },
"system_health": { "system_health": {
"info": { "info": {
"can_reach_server": "Llegar al servidor de Airly",
"requests_per_day": "Solicitudes permitidas por d\u00eda", "requests_per_day": "Solicitudes permitidas por d\u00eda",
"requests_remaining": "Solicitudes permitidas restantes" "requests_remaining": "Solicitudes permitidas restantes"
} }

View File

@ -5,7 +5,7 @@
}, },
"error": { "error": {
"invalid_api_key": "Clave API no v\u00e1lida", "invalid_api_key": "Clave API no v\u00e1lida",
"wrong_location": "No hay estaciones de medici\u00f3n Airly en esta zona." "wrong_location": "No hay estaciones de medici\u00f3n Airly en esta \u00e1rea."
}, },
"step": { "step": {
"user": { "user": {
@ -15,13 +15,13 @@
"longitude": "Longitud", "longitude": "Longitud",
"name": "Nombre" "name": "Nombre"
}, },
"description": "Establecer la integraci\u00f3n de la calidad del aire de Airly. Para generar la clave de la API vaya a https://developer.airly.eu/register" "description": "Para generar la clave API, ve a https://developer.airly.eu/register"
} }
} }
}, },
"system_health": { "system_health": {
"info": { "info": {
"can_reach_server": "Alcanzar el servidor Airly", "can_reach_server": "Se puede llegar al servidor Airly",
"requests_per_day": "Solicitudes permitidas por d\u00eda", "requests_per_day": "Solicitudes permitidas por d\u00eda",
"requests_remaining": "Solicitudes permitidas restantes" "requests_remaining": "Solicitudes permitidas restantes"
} }

View File

@ -18,5 +18,12 @@
"description": "Konfigurera integration av luftkvalitet. F\u00f6r att skapa API-nyckel, g\u00e5 till https://developer.airly.eu/register" "description": "Konfigurera integration av luftkvalitet. F\u00f6r att skapa API-nyckel, g\u00e5 till https://developer.airly.eu/register"
} }
} }
},
"system_health": {
"info": {
"can_reach_server": "N\u00e5 Airly-servern",
"requests_per_day": "Till\u00e5tna f\u00f6rfr\u00e5gningar per dag",
"requests_remaining": "\u00c5terst\u00e5ende till\u00e5tna f\u00f6rfr\u00e5gningar"
}
} }
} }

View File

@ -8,7 +8,6 @@ from homeassistant.components.sensor import (
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
ATTR_ATTRIBUTION,
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER, CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
CONCENTRATION_PARTS_PER_MILLION, CONCENTRATION_PARTS_PER_MILLION,
) )
@ -73,6 +72,8 @@ async def async_setup_entry(
class AirNowSensor(CoordinatorEntity[AirNowDataUpdateCoordinator], SensorEntity): class AirNowSensor(CoordinatorEntity[AirNowDataUpdateCoordinator], SensorEntity):
"""Define an AirNow sensor.""" """Define an AirNow sensor."""
_attr_attribution = ATTRIBUTION
def __init__( def __init__(
self, self,
coordinator: AirNowDataUpdateCoordinator, coordinator: AirNowDataUpdateCoordinator,
@ -82,7 +83,7 @@ class AirNowSensor(CoordinatorEntity[AirNowDataUpdateCoordinator], SensorEntity)
super().__init__(coordinator) super().__init__(coordinator)
self.entity_description = description self.entity_description = description
self._state = None self._state = None
self._attrs = {ATTR_ATTRIBUTION: ATTRIBUTION} self._attrs: dict[str, str] = {}
self._attr_name = f"AirNow {description.name}" self._attr_name = f"AirNow {description.name}"
self._attr_unique_id = ( self._attr_unique_id = (
f"{coordinator.latitude}-{coordinator.longitude}-{description.key.lower()}" f"{coordinator.latitude}-{coordinator.longitude}-{description.key.lower()}"

View File

@ -17,7 +17,7 @@
"longitude": "Longitud", "longitude": "Longitud",
"radius": "Radio de la estaci\u00f3n (millas; opcional)" "radius": "Radio de la estaci\u00f3n (millas; opcional)"
}, },
"description": "Configurar la integraci\u00f3n de calidad del aire de AirNow. Para generar una clave API, ve a https://docs.airnowapi.org/account/request/" "description": "Para generar la clave API, ve a https://docs.airnowapi.org/account/request/"
} }
} }
} }

View File

@ -1,10 +1,23 @@
{ {
"config": { "config": {
"abort": {
"already_configured": "Enheten \u00e4r redan konfigurerad"
},
"error": {
"cannot_connect": "Det gick inte att ansluta.",
"invalid_auth": "Ogiltig autentisering",
"invalid_location": "Inga resultat hittades f\u00f6r den platsen",
"unknown": "Ov\u00e4ntat fel"
},
"step": { "step": {
"user": { "user": {
"data": { "data": {
"api_key": "API-nyckel" "api_key": "API-nyckel",
} "latitude": "Latitud",
"longitude": "Longitud",
"radius": "Stationsradie (miles; valfritt)"
},
"description": "F\u00f6r att generera API-nyckel g\u00e5 till https://docs.airnowapi.org/account/request/"
} }
} }
} }

View File

@ -11,7 +11,7 @@
"step": { "step": {
"user": { "user": {
"data": { "data": {
"description": "Inicie sesi\u00f3n en {url} para encontrar sus credenciales", "description": "Inicia sesi\u00f3n en {url} para encontrar tus credenciales",
"id": "ID", "id": "ID",
"secret": "Secreto" "secret": "Secreto"
} }

View File

@ -0,0 +1,21 @@
{
"config": {
"abort": {
"already_configured": "Konto har redan konfigurerats"
},
"error": {
"cannot_connect": "Det gick inte att ansluta.",
"invalid_auth": "Ogiltig autentisering",
"unknown": "Ov\u00e4ntat fel"
},
"step": {
"user": {
"data": {
"description": "Logga in p\u00e5 {url} f\u00f6r att hitta dina autentiseringsuppgifter",
"id": "ID",
"secret": "Hemlighet"
}
}
}
}
}

View File

@ -2,6 +2,7 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from typing import Any
from homeassistant.components.climate import ClimateEntity from homeassistant.components.climate import ClimateEntity
from homeassistant.components.climate.const import ( from homeassistant.components.climate.const import (
@ -154,7 +155,7 @@ class AirtouchAC(CoordinatorEntity, ClimateEntity):
modes.append(HVACMode.OFF) modes.append(HVACMode.OFF)
return modes return modes
async def async_set_hvac_mode(self, hvac_mode): async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new operation mode.""" """Set new operation mode."""
if hvac_mode not in HA_STATE_TO_AT: if hvac_mode not in HA_STATE_TO_AT:
raise ValueError(f"Unsupported HVAC mode: {hvac_mode}") raise ValueError(f"Unsupported HVAC mode: {hvac_mode}")
@ -170,7 +171,7 @@ class AirtouchAC(CoordinatorEntity, ClimateEntity):
_LOGGER.debug("Setting operation mode of %s to %s", self._ac_number, hvac_mode) _LOGGER.debug("Setting operation mode of %s to %s", self._ac_number, hvac_mode)
self.async_write_ha_state() self.async_write_ha_state()
async def async_set_fan_mode(self, fan_mode): async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set new fan mode.""" """Set new fan mode."""
if fan_mode not in self.fan_modes: if fan_mode not in self.fan_modes:
raise ValueError(f"Unsupported fan mode: {fan_mode}") raise ValueError(f"Unsupported fan mode: {fan_mode}")
@ -182,14 +183,14 @@ class AirtouchAC(CoordinatorEntity, ClimateEntity):
self._unit = self._airtouch.GetAcs()[self._ac_number] self._unit = self._airtouch.GetAcs()[self._ac_number]
self.async_write_ha_state() self.async_write_ha_state()
async def async_turn_on(self): async def async_turn_on(self) -> None:
"""Turn on.""" """Turn on."""
_LOGGER.debug("Turning %s on", self.unique_id) _LOGGER.debug("Turning %s on", self.unique_id)
# in case ac is not on. Airtouch turns itself off if no groups are turned on # in case ac is not on. Airtouch turns itself off if no groups are turned on
# (even if groups turned back on) # (even if groups turned back on)
await self._airtouch.TurnAcOn(self._ac_number) await self._airtouch.TurnAcOn(self._ac_number)
async def async_turn_off(self): async def async_turn_off(self) -> None:
"""Turn off.""" """Turn off."""
_LOGGER.debug("Turning %s off", self.unique_id) _LOGGER.debug("Turning %s off", self.unique_id)
await self._airtouch.TurnAcOff(self._ac_number) await self._airtouch.TurnAcOff(self._ac_number)
@ -266,7 +267,7 @@ class AirtouchGroup(CoordinatorEntity, ClimateEntity):
return HVACMode.FAN_ONLY return HVACMode.FAN_ONLY
async def async_set_hvac_mode(self, hvac_mode): async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set new operation mode.""" """Set new operation mode."""
if hvac_mode not in HA_STATE_TO_AT: if hvac_mode not in HA_STATE_TO_AT:
raise ValueError(f"Unsupported HVAC mode: {hvac_mode}") raise ValueError(f"Unsupported HVAC mode: {hvac_mode}")
@ -294,9 +295,11 @@ class AirtouchGroup(CoordinatorEntity, ClimateEntity):
) )
return [AT_TO_HA_FAN_SPEED[speed] for speed in airtouch_fan_speeds] return [AT_TO_HA_FAN_SPEED[speed] for speed in airtouch_fan_speeds]
async def async_set_temperature(self, **kwargs): async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperatures.""" """Set new target temperatures."""
temp = kwargs.get(ATTR_TEMPERATURE) if (temp := kwargs.get(ATTR_TEMPERATURE)) is None:
_LOGGER.debug("Argument `temperature` is missing in set_temperature")
return
_LOGGER.debug("Setting temp of %s to %s", self._group_number, str(temp)) _LOGGER.debug("Setting temp of %s to %s", self._group_number, str(temp))
self._unit = await self._airtouch.SetGroupToTemperature( self._unit = await self._airtouch.SetGroupToTemperature(
@ -304,7 +307,7 @@ class AirtouchGroup(CoordinatorEntity, ClimateEntity):
) )
self.async_write_ha_state() self.async_write_ha_state()
async def async_set_fan_mode(self, fan_mode): async def async_set_fan_mode(self, fan_mode: str) -> None:
"""Set new fan mode.""" """Set new fan mode."""
if fan_mode not in self.fan_modes: if fan_mode not in self.fan_modes:
raise ValueError(f"Unsupported fan mode: {fan_mode}") raise ValueError(f"Unsupported fan mode: {fan_mode}")
@ -315,7 +318,7 @@ class AirtouchGroup(CoordinatorEntity, ClimateEntity):
) )
self.async_write_ha_state() self.async_write_ha_state()
async def async_turn_on(self): async def async_turn_on(self) -> None:
"""Turn on.""" """Turn on."""
_LOGGER.debug("Turning %s on", self.unique_id) _LOGGER.debug("Turning %s on", self.unique_id)
await self._airtouch.TurnGroupOn(self._group_number) await self._airtouch.TurnGroupOn(self._group_number)
@ -330,7 +333,7 @@ class AirtouchGroup(CoordinatorEntity, ClimateEntity):
await self.coordinator.async_request_refresh() await self.coordinator.async_request_refresh()
self.async_write_ha_state() self.async_write_ha_state()
async def async_turn_off(self): async def async_turn_off(self) -> None:
"""Turn off.""" """Turn off."""
_LOGGER.debug("Turning %s off", self.unique_id) _LOGGER.debug("Turning %s off", self.unique_id)
await self._airtouch.TurnGroupOff(self._group_number) await self._airtouch.TurnGroupOff(self._group_number)

View File

@ -12,7 +12,7 @@
"data": { "data": {
"host": "Host" "host": "Host"
}, },
"title": "Configura los detalles de conexi\u00f3n de tu AirTouch 4." "title": "Configurar los detalles de conexi\u00f3n de tu AirTouch 4."
} }
} }
} }

View File

@ -0,0 +1,19 @@
{
"config": {
"abort": {
"already_configured": "Enheten \u00e4r redan konfigurerad"
},
"error": {
"cannot_connect": "Det gick inte att ansluta.",
"no_units": "Det gick inte att hitta n\u00e5gra AirTouch 4-grupper."
},
"step": {
"user": {
"data": {
"host": "V\u00e4rd"
},
"title": "St\u00e4ll in dina AirTouch 4-anslutningsdetaljer."
}
}
}
}

View File

@ -6,7 +6,7 @@
}, },
"error": { "error": {
"cannot_connect": "No se pudo conectar", "cannot_connect": "No se pudo conectar",
"general_error": "Se ha producido un error desconocido.", "general_error": "Error inesperado",
"invalid_api_key": "Clave API no v\u00e1lida", "invalid_api_key": "Clave API no v\u00e1lida",
"location_not_found": "Ubicaci\u00f3n no encontrada" "location_not_found": "Ubicaci\u00f3n no encontrada"
}, },
@ -17,7 +17,7 @@
"latitude": "Latitud", "latitude": "Latitud",
"longitude": "Longitud" "longitude": "Longitud"
}, },
"description": "Utilice la API de la nube de AirVisual para supervisar una latitud/longitud.", "description": "Usar la API de la nube de AirVisual para supervisar una latitud/longitud.",
"title": "Configurar una geograf\u00eda" "title": "Configurar una geograf\u00eda"
}, },
"geography_by_name": { "geography_by_name": {
@ -27,7 +27,7 @@
"country": "Pa\u00eds", "country": "Pa\u00eds",
"state": "estado" "state": "estado"
}, },
"description": "Utilice la API de la nube de AirVisual para supervisar una ciudad/estado/pa\u00eds.", "description": "Usar la API de la nube de AirVisual para supervisar una ciudad/estado/pa\u00eds.",
"title": "Configurar una geograf\u00eda" "title": "Configurar una geograf\u00eda"
}, },
"node_pro": { "node_pro": {
@ -35,7 +35,7 @@
"ip_address": "Host", "ip_address": "Host",
"password": "Contrase\u00f1a" "password": "Contrase\u00f1a"
}, },
"description": "Monitorizar una unidad personal AirVisual. La contrase\u00f1a puede ser recuperada desde la interfaz de la unidad.", "description": "Supervisar una unidad AirVisual personal. La contrase\u00f1a se puede recuperar desde la IU de la unidad.",
"title": "Configurar un AirVisual Node/Pro" "title": "Configurar un AirVisual Node/Pro"
}, },
"reauth_confirm": { "reauth_confirm": {
@ -45,7 +45,7 @@
"title": "Volver a autenticar AirVisual" "title": "Volver a autenticar AirVisual"
}, },
"user": { "user": {
"description": "Elige qu\u00e9 tipo de datos de AirVisual quieres monitorizar.", "description": "Elige qu\u00e9 tipo de datos de AirVisual quieres supervisar.",
"title": "Configurar AirVisual" "title": "Configurar AirVisual"
} }
} }
@ -54,7 +54,7 @@
"step": { "step": {
"init": { "init": {
"data": { "data": {
"show_on_map": "Mostrar geograf\u00eda monitorizada en el mapa" "show_on_map": "Mostrar geograf\u00eda supervisada en el mapa"
}, },
"title": "Configurar AirVisual" "title": "Configurar AirVisual"
} }

View File

@ -12,8 +12,8 @@
"good": "Bueno", "good": "Bueno",
"hazardous": "Da\u00f1ino", "hazardous": "Da\u00f1ino",
"moderate": "Moderado", "moderate": "Moderado",
"unhealthy": "Insalubre", "unhealthy": "Poco saludable",
"unhealthy_sensitive": "Insalubre para grupos sensibles", "unhealthy_sensitive": "Poco saludable para grupos sensibles",
"very_unhealthy": "Muy poco saludable" "very_unhealthy": "Muy poco saludable"
} }
} }

View File

@ -1,7 +1,20 @@
{ {
"state": { "state": {
"airvisual__pollutant_label": { "airvisual__pollutant_label": {
"co": "Kolmonoxid" "co": "Kolmonoxid",
"n2": "Kv\u00e4vedioxid",
"o3": "Ozon",
"p1": "PM10",
"p2": "PM2,5",
"s2": "Svaveldioxid"
},
"airvisual__pollutant_level": {
"good": "Bra",
"hazardous": "Farlig",
"moderate": "M\u00e5ttlig",
"unhealthy": "Oh\u00e4lsosam",
"unhealthy_sensitive": "Oh\u00e4lsosamt f\u00f6r k\u00e4nsliga grupper",
"very_unhealthy": "Mycket oh\u00e4lsosamt"
} }
} }
} }

View File

@ -1,22 +1,61 @@
{ {
"config": { "config": {
"abort": {
"already_configured": "Platsen \u00e4r redan konfigurerad eller Node/Pro ID \u00e4r redan regristrerat.",
"reauth_successful": "\u00c5terautentisering lyckades"
},
"error": { "error": {
"cannot_connect": "Det gick inte att ansluta.",
"general_error": "Ett ok\u00e4nt fel intr\u00e4ffade.", "general_error": "Ett ok\u00e4nt fel intr\u00e4ffade.",
"invalid_api_key": "Ogiltig API-nyckel" "invalid_api_key": "Ogiltig API-nyckel",
"location_not_found": "Platsen hittades inte"
}, },
"step": { "step": {
"geography_by_coords": {
"data": {
"api_key": "API-nyckel",
"latitude": "Latitud",
"longitude": "Longitud"
},
"description": "Anv\u00e4nd AirVisuals moln-API f\u00f6r att \u00f6vervaka en latitud/longitud.",
"title": "Konfigurera en geografi"
},
"geography_by_name": { "geography_by_name": {
"data": { "data": {
"api_key": "API-nyckel" "api_key": "API-nyckel",
} "city": "Stad",
"country": "Land",
"state": "stat"
},
"description": "Anv\u00e4nd AirVisuals moln-API f\u00f6r att \u00f6vervaka en stad/stat/land.",
"title": "Konfigurera en geografi"
}, },
"node_pro": { "node_pro": {
"data": { "data": {
"ip_address": "Enhets IP-adress / v\u00e4rdnamn", "ip_address": "Enhets IP-adress / v\u00e4rdnamn",
"password": "Enhetsl\u00f6senord" "password": "Enhetsl\u00f6senord"
} },
"description": "\u00d6vervaka en personlig AirVisual-enhet. L\u00f6senordet kan h\u00e4mtas fr\u00e5n enhetens anv\u00e4ndargr\u00e4nssnitt.",
"title": "Konfigurera en AirVisual Node/Pro"
},
"reauth_confirm": {
"data": {
"api_key": "API-nyckel"
},
"title": "Autentisera AirVisual igen"
}, },
"user": { "user": {
"description": "V\u00e4lj typ av AirVisual data att \u00f6vervaka.",
"title": "Konfigurera AirVisual"
}
}
},
"options": {
"step": {
"init": {
"data": {
"show_on_map": "Visa \u00f6vervakad geografi p\u00e5 kartan"
},
"title": "Konfigurera AirVisual" "title": "Konfigurera AirVisual"
} }
} }

View File

@ -3,7 +3,7 @@
"name": "Airzone", "name": "Airzone",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/airzone", "documentation": "https://www.home-assistant.io/integrations/airzone",
"requirements": ["aioairzone==0.4.6"], "requirements": ["aioairzone==0.4.8"],
"codeowners": ["@Noltari"], "codeowners": ["@Noltari"],
"iot_class": "local_polling", "iot_class": "local_polling",
"loggers": ["aioairzone"] "loggers": ["aioairzone"]

View File

@ -0,0 +1,12 @@
{
"config": {
"error": {
"invalid_system_id": "ID del sistema Airzone no v\u00e1lido"
},
"step": {
"user": {
"description": "Configurar la integraci\u00f3n de Airzone."
}
}
}
}

View File

@ -4,8 +4,8 @@
"already_configured": "El dispositivo ya est\u00e1 configurado" "already_configured": "El dispositivo ya est\u00e1 configurado"
}, },
"error": { "error": {
"cannot_connect": "Fall\u00f3 la conexi\u00f3n", "cannot_connect": "No se pudo conectar",
"invalid_system_id": "ID de sistema Airzone inv\u00e1lido" "invalid_system_id": "ID del sistema Airzone no v\u00e1lido"
}, },
"step": { "step": {
"user": { "user": {

View File

@ -0,0 +1,20 @@
{
"config": {
"abort": {
"already_configured": "Enheten \u00e4r redan konfigurerad"
},
"error": {
"cannot_connect": "Det gick inte att ansluta.",
"invalid_system_id": "Ogiltigt Airzone System ID"
},
"step": {
"user": {
"data": {
"host": "V\u00e4rd",
"port": "Port"
},
"description": "St\u00e4ll in Airzone-integration."
}
}
}
}

View File

@ -44,7 +44,6 @@ async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> None:
CLIENT_ID, CLIENT_ID,
) )
login = await acc.login() login = await acc.login()
await acc.close()
if not login: if not login:
raise InvalidAuth raise InvalidAuth

View File

@ -111,7 +111,7 @@ class AladdinDevice(CoverEntity):
"""Schedule a state update.""" """Schedule a state update."""
self.async_write_ha_state() self.async_write_ha_state()
self._acc.register_callback(update_callback, self._serial) self._acc.register_callback(update_callback, self._serial, self._number)
await self._acc.get_doors(self._serial) await self._acc.get_doors(self._serial)
async def async_will_remove_from_hass(self) -> None: async def async_will_remove_from_hass(self) -> None:

View File

@ -2,7 +2,7 @@
"domain": "aladdin_connect", "domain": "aladdin_connect",
"name": "Aladdin Connect", "name": "Aladdin Connect",
"documentation": "https://www.home-assistant.io/integrations/aladdin_connect", "documentation": "https://www.home-assistant.io/integrations/aladdin_connect",
"requirements": ["AIOAladdinConnect==0.1.41"], "requirements": ["AIOAladdinConnect==0.1.44"],
"codeowners": ["@mkmer"], "codeowners": ["@mkmer"],
"iot_class": "cloud_polling", "iot_class": "cloud_polling",
"loggers": ["aladdin_connect"], "loggers": ["aladdin_connect"],

View File

@ -2,10 +2,10 @@
"config": { "config": {
"abort": { "abort": {
"already_configured": "El dispositivo ya est\u00e1 configurado", "already_configured": "El dispositivo ya est\u00e1 configurado",
"reauth_successful": "La reautenticaci\u00f3n fue exitosa" "reauth_successful": "La reautenticaci\u00f3n se realiz\u00f3 correctamente"
}, },
"error": { "error": {
"cannot_connect": "Error al conectar", "cannot_connect": "No se pudo conectar",
"invalid_auth": "Autenticaci\u00f3n no v\u00e1lida" "invalid_auth": "Autenticaci\u00f3n no v\u00e1lida"
}, },
"step": { "step": {
@ -13,8 +13,8 @@
"data": { "data": {
"password": "Contrase\u00f1a" "password": "Contrase\u00f1a"
}, },
"description": "La integraci\u00f3n de Aladdin Connect necesita volver a autenticar su cuenta", "description": "La integraci\u00f3n Aladdin Connect necesita volver a autenticar tu cuenta",
"title": "Reautenticaci\u00f3n de la integraci\u00f3n" "title": "Volver a autenticar la integraci\u00f3n"
}, },
"user": { "user": {
"data": { "data": {

View File

@ -13,6 +13,7 @@
"data": { "data": {
"password": "L\u00f6senord" "password": "L\u00f6senord"
}, },
"description": "Aladdin Connect-integrationen m\u00e5ste autentisera ditt konto igen",
"title": "\u00c5terautenticera integration" "title": "\u00c5terautenticera integration"
}, },
"user": { "user": {

View File

@ -5,10 +5,6 @@ from typing import Final
import voluptuous as vol import voluptuous as vol
from homeassistant.components.automation import (
AutomationActionType,
AutomationTriggerInfo,
)
from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA from homeassistant.components.device_automation import DEVICE_TRIGGER_BASE_SCHEMA
from homeassistant.components.homeassistant.triggers import state as state_trigger from homeassistant.components.homeassistant.triggers import state as state_trigger
from homeassistant.const import ( from homeassistant.const import (
@ -29,6 +25,7 @@ from homeassistant.const import (
from homeassistant.core import CALLBACK_TYPE, HomeAssistant from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.helpers import config_validation as cv, entity_registry from homeassistant.helpers import config_validation as cv, entity_registry
from homeassistant.helpers.entity import get_supported_features from homeassistant.helpers.entity import get_supported_features
from homeassistant.helpers.trigger import TriggerActionType, TriggerInfo
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from . import DOMAIN from . import DOMAIN
@ -131,8 +128,8 @@ async def async_get_trigger_capabilities(
async def async_attach_trigger( async def async_attach_trigger(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
action: AutomationActionType, action: TriggerActionType,
automation_info: AutomationTriggerInfo, trigger_info: TriggerInfo,
) -> CALLBACK_TYPE: ) -> CALLBACK_TYPE:
"""Attach a trigger.""" """Attach a trigger."""
if config[CONF_TYPE] == "triggered": if config[CONF_TYPE] == "triggered":
@ -159,5 +156,5 @@ async def async_attach_trigger(
state_config[CONF_FOR] = config[CONF_FOR] state_config[CONF_FOR] = config[CONF_FOR]
state_config = await state_trigger.async_validate_trigger_config(hass, state_config) state_config = await state_trigger.async_validate_trigger_config(hass, state_config)
return await state_trigger.async_attach_trigger( return await state_trigger.async_attach_trigger(
hass, state_config, action, automation_info, platform_type="device" hass, state_config, action, trigger_info, platform_type="device"
) )

View File

@ -1,18 +1,18 @@
{ {
"device_automation": { "device_automation": {
"action_type": { "action_type": {
"arm_away": "Armar {entity_name} exterior", "arm_away": "Armar ausente en {entity_name}",
"arm_home": "Armar {entity_name} modo casa", "arm_home": "Armar en casa en {entity_name}",
"arm_night": "Armar {entity_name} por la noche", "arm_night": "Armar noche en {entity_name}",
"arm_vacation": "Armar las vacaciones de {entity_name}", "arm_vacation": "Armar de vacaciones {entity_name}",
"disarm": "Desarmar {entity_name}", "disarm": "Desarmar {entity_name}",
"trigger": "Lanzar {entity_name}" "trigger": "Disparar {entity_name}"
}, },
"condition_type": { "condition_type": {
"is_armed_away": "{entity_name} est\u00e1 armada ausente", "is_armed_away": "{entity_name} est\u00e1 armada ausente",
"is_armed_home": "{entity_name} est\u00e1 armada en casa", "is_armed_home": "{entity_name} est\u00e1 armada en casa",
"is_armed_night": "{entity_name} est\u00e1 armada noche", "is_armed_night": "{entity_name} est\u00e1 armada noche",
"is_armed_vacation": "{entity_name} est\u00e1 armado de vacaciones", "is_armed_vacation": "{entity_name} est\u00e1 armada de vacaciones",
"is_disarmed": "{entity_name} est\u00e1 desarmada", "is_disarmed": "{entity_name} est\u00e1 desarmada",
"is_triggered": "{entity_name} est\u00e1 disparada" "is_triggered": "{entity_name} est\u00e1 disparada"
}, },
@ -20,9 +20,9 @@
"armed_away": "{entity_name} armada ausente", "armed_away": "{entity_name} armada ausente",
"armed_home": "{entity_name} armada en casa", "armed_home": "{entity_name} armada en casa",
"armed_night": "{entity_name} armada noche", "armed_night": "{entity_name} armada noche",
"armed_vacation": "Vacaciones armadas de {entity_name}", "armed_vacation": "{entity_name} en armada de vacaciones",
"disarmed": "{entity_name} desarmada", "disarmed": "{entity_name} desarmada",
"triggered": "{entity_name} activado" "triggered": "{entity_name} disparada"
} }
}, },
"state": { "state": {
@ -32,7 +32,7 @@
"armed_custom_bypass": "Armada personalizada", "armed_custom_bypass": "Armada personalizada",
"armed_home": "Armada en casa", "armed_home": "Armada en casa",
"armed_night": "Armada noche", "armed_night": "Armada noche",
"armed_vacation": "Vacaciones armadas", "armed_vacation": "Armada de vacaciones",
"arming": "Armando", "arming": "Armando",
"disarmed": "Desarmada", "disarmed": "Desarmada",
"disarming": "Desarmando", "disarming": "Desarmando",

View File

@ -4,16 +4,23 @@
"arm_away": "Larma {entity_name} borta", "arm_away": "Larma {entity_name} borta",
"arm_home": "Larma {entity_name} hemma", "arm_home": "Larma {entity_name} hemma",
"arm_night": "Larma {entity_name} natt", "arm_night": "Larma {entity_name} natt",
"arm_vacation": "Larma semesterl\u00e4ge {entity_name}",
"disarm": "Avlarma {entity_name}", "disarm": "Avlarma {entity_name}",
"trigger": "Utl\u00f6sare {entity_name}" "trigger": "Utl\u00f6sare {entity_name}"
}, },
"condition_type": { "condition_type": {
"is_armed_away": "{entity_name} \u00e4r bortalarmat",
"is_armed_home": "{entity_name} \u00e4r hemmalarmat",
"is_armed_night": "{entity_name} \u00e4r nattlarmat",
"is_armed_vacation": "{entity_name} \u00e4r larmad i semesterl\u00e4ge",
"is_disarmed": "{entity_name} \u00e4r bortkopplad",
"is_triggered": "har utl\u00f6sts" "is_triggered": "har utl\u00f6sts"
}, },
"trigger_type": { "trigger_type": {
"armed_away": "{entity_name} larmad borta", "armed_away": "{entity_name} larmad borta",
"armed_home": "{entity_name} larmad hemma", "armed_home": "{entity_name} larmad hemma",
"armed_night": "{entity_name} larmad natt", "armed_night": "{entity_name} larmad natt",
"armed_vacation": "{entity_name} larmad i semesterl\u00e4ge",
"disarmed": "{entity_name} bortkopplad", "disarmed": "{entity_name} bortkopplad",
"triggered": "{entity_name} utl\u00f6st" "triggered": "{entity_name} utl\u00f6st"
} }
@ -25,6 +32,7 @@
"armed_custom_bypass": "Larm f\u00f6rbikopplat", "armed_custom_bypass": "Larm f\u00f6rbikopplat",
"armed_home": "Hemmalarmat", "armed_home": "Hemmalarmat",
"armed_night": "Nattlarmat", "armed_night": "Nattlarmat",
"armed_vacation": "Larmad semesterl\u00e4ge",
"arming": "Tillkopplar", "arming": "Tillkopplar",
"disarmed": "Avlarmat", "disarmed": "Avlarmat",
"disarming": "Fr\u00e5nkopplar", "disarming": "Fr\u00e5nkopplar",

View File

@ -29,6 +29,7 @@
"armed_custom_bypass": "\u041e\u0445\u043e\u0440\u043e\u043d\u0430 \u0437 \u0432\u0438\u043d\u044f\u0442\u043a\u0430\u043c\u0438", "armed_custom_bypass": "\u041e\u0445\u043e\u0440\u043e\u043d\u0430 \u0437 \u0432\u0438\u043d\u044f\u0442\u043a\u0430\u043c\u0438",
"armed_home": "\u041e\u0445\u043e\u0440\u043e\u043d\u0430 (\u0412\u0434\u043e\u043c\u0430)", "armed_home": "\u041e\u0445\u043e\u0440\u043e\u043d\u0430 (\u0412\u0434\u043e\u043c\u0430)",
"armed_night": "\u041d\u0456\u0447\u043d\u0430 \u043e\u0445\u043e\u0440\u043e\u043d\u0430", "armed_night": "\u041d\u0456\u0447\u043d\u0430 \u043e\u0445\u043e\u0440\u043e\u043d\u0430",
"armed_vacation": "\u041e\u0445\u043e\u0440\u043e\u043d\u0430 (\u0432\u0456\u0434\u043f\u0443\u0441\u0442\u043a\u0430)",
"arming": "\u0421\u0442\u0430\u0432\u043b\u044e \u043d\u0430 \u043e\u0445\u043e\u0440\u043e\u043d\u0443", "arming": "\u0421\u0442\u0430\u0432\u043b\u044e \u043d\u0430 \u043e\u0445\u043e\u0440\u043e\u043d\u0443",
"disarmed": "\u0417\u043d\u044f\u0442\u043e \u0437 \u043e\u0445\u043e\u0440\u043e\u043d\u0438", "disarmed": "\u0417\u043d\u044f\u0442\u043e \u0437 \u043e\u0445\u043e\u0440\u043e\u043d\u0438",
"disarming": "\u0417\u043d\u044f\u0442\u0442\u044f", "disarming": "\u0417\u043d\u044f\u0442\u0442\u044f",

View File

@ -88,7 +88,7 @@ class AlarmDecoderBinarySensor(BinarySensorEntity):
CONF_ZONE_NUMBER: self._zone_number, CONF_ZONE_NUMBER: self._zone_number,
} }
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Register callbacks.""" """Register callbacks."""
self.async_on_remove( self.async_on_remove(
async_dispatcher_connect(self.hass, SIGNAL_ZONE_FAULT, self._fault_callback) async_dispatcher_connect(self.hass, SIGNAL_ZONE_FAULT, self._fault_callback)

View File

@ -24,7 +24,7 @@ class AlarmDecoderSensor(SensorEntity):
_attr_name = "Alarm Panel Display" _attr_name = "Alarm Panel Display"
_attr_should_poll = False _attr_should_poll = False
async def async_added_to_hass(self): async def async_added_to_hass(self) -> None:
"""Register callbacks.""" """Register callbacks."""
self.async_on_remove( self.async_on_remove(
async_dispatcher_connect( async_dispatcher_connect(

View File

@ -23,23 +23,23 @@
"data": { "data": {
"protocol": "Protocolo" "protocol": "Protocolo"
}, },
"title": "Elige el protocolo del AlarmDecoder" "title": "Elige el protocolo AlarmDecoder"
} }
} }
}, },
"options": { "options": {
"error": { "error": {
"int": "El campo siguiente debe ser un n\u00famero entero.", "int": "El siguiente campo debe ser un n\u00famero entero.",
"loop_range": "El bucle RF debe ser un n\u00famero entero entre 1 y 4.", "loop_range": "RF Loop debe ser un n\u00famero entero entre 1 y 4.",
"loop_rfid": "El bucle de RF no puede utilizarse sin el serie RF.", "loop_rfid": "RF Loop no se puede utilizar sin RF Serial.",
"relay_inclusive": "La direcci\u00f3n de retransmisi\u00f3n y el canal de retransmisi\u00f3n son codependientes y deben incluirse a la vez." "relay_inclusive": "La direcci\u00f3n de retransmisi\u00f3n y el canal de retransmisi\u00f3n son c\u00f3digopendientes y deben incluirse a la vez."
}, },
"step": { "step": {
"arm_settings": { "arm_settings": {
"data": { "data": {
"alt_night_mode": "Modo noche alternativo", "alt_night_mode": "Modo nocturno alternativo",
"auto_bypass": "Desv\u00edo autom\u00e1tico al armar", "auto_bypass": "Anulaci\u00f3n autom\u00e1tica en armado",
"code_arm_required": "C\u00f3digo requerido para el armado" "code_arm_required": "C\u00f3digo Requerido para Armar"
}, },
"title": "Configurar AlarmDecoder" "title": "Configurar AlarmDecoder"
}, },
@ -52,14 +52,14 @@
}, },
"zone_details": { "zone_details": {
"data": { "data": {
"zone_loop": "Bucle RF", "zone_loop": "RF Loop",
"zone_name": "Nombre de zona", "zone_name": "Nombre de zona",
"zone_relayaddr": "Direcci\u00f3n de retransmisi\u00f3n", "zone_relayaddr": "Direcci\u00f3n de retransmisi\u00f3n",
"zone_relaychan": "Canal de retransmisi\u00f3n", "zone_relaychan": "Canal de retransmisi\u00f3n",
"zone_rfid": "Serie RF", "zone_rfid": "RF Serial",
"zone_type": "Tipo de zona" "zone_type": "Tipo de zona"
}, },
"description": "Introduce los detalles para la zona {zona_number}. Para borrar la zona {zone_number}, deja el nombre de la zona en blanco.", "description": "Introduce los detalles para la zona {zone_number}. Para eliminar la zona {zone_number}, deja el nombre de la zona en blanco.",
"title": "Configurar AlarmDecoder" "title": "Configurar AlarmDecoder"
}, },
"zone_select": { "zone_select": {

View File

@ -1,26 +1,73 @@
{ {
"config": { "config": {
"abort": {
"already_configured": "Enheten \u00e4r redan konfigurerad"
},
"create_entry": {
"default": "Ansluten till AlarmDecoder."
},
"error": {
"cannot_connect": "Det gick inte att ansluta."
},
"step": { "step": {
"protocol": { "protocol": {
"data": { "data": {
"device_path": "Enhetsv\u00e4g" "device_baudrate": "Enhetens Baud Rate",
"device_path": "Enhetsv\u00e4g",
"host": "V\u00e4rd",
"port": "Port"
}, },
"title": "Konfigurera anslutningsinst\u00e4llningar" "title": "Konfigurera anslutningsinst\u00e4llningar"
}, },
"user": { "user": {
"data": { "data": {
"protocol": "Protokoll" "protocol": "Protokoll"
} },
"title": "V\u00e4lj AlarmDecoder Protocol"
} }
} }
}, },
"options": { "options": {
"error": {
"int": "F\u00e4ltet nedan m\u00e5ste vara ett heltal.",
"loop_range": "RF Loop m\u00e5ste vara ett heltal mellan 1 och 4.",
"loop_rfid": "RF Loop kan inte anv\u00e4ndas utan RF Serial.",
"relay_inclusive": "Rel\u00e4adress och rel\u00e4kanal \u00e4r beroende av varandra och m\u00e5ste inkluderas tillsammans."
},
"step": { "step": {
"arm_settings": {
"data": {
"alt_night_mode": "Alternativt nattl\u00e4ge",
"auto_bypass": "Automatisk f\u00f6rbikoppling p\u00e5 arm",
"code_arm_required": "Kod kr\u00e4vs f\u00f6r tillkoppling"
},
"title": "Konfigurera AlarmDecoder"
},
"init": { "init": {
"data": { "data": {
"edit_select": "Redigera" "edit_select": "Redigera"
}, },
"description": "Vad vill du redigera?" "description": "Vad vill du redigera?",
"title": "Konfigurera AlarmDecoder"
},
"zone_details": {
"data": {
"zone_loop": "RF loop",
"zone_name": "Zonnamn",
"zone_relayaddr": "Rel\u00e4adress",
"zone_relaychan": "Rel\u00e4kanal",
"zone_rfid": "RF seriell",
"zone_type": "Zontyp"
},
"description": "Ange detaljer f\u00f6r zon {zone_number} . F\u00f6r att ta bort zon {zone_number} l\u00e4mnar du Zonnamn tomt.",
"title": "Konfigurera AlarmDecoder"
},
"zone_select": {
"data": {
"zone_number": "Zonnummer"
},
"description": "Ange zonnumret du vill l\u00e4gga till, redigera eller ta bort.",
"title": "Konfigurera AlarmDecoder"
} }
} }
} }

View File

@ -269,6 +269,7 @@ class Alexa(AlexaCapability):
""" """
supported_locales = { supported_locales = {
"ar-SA",
"de-DE", "de-DE",
"en-AU", "en-AU",
"en-CA", "en-CA",
@ -277,10 +278,13 @@ class Alexa(AlexaCapability):
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX", "es-MX",
"es-US",
"fr-CA", "fr-CA",
"fr-FR", "fr-FR",
"hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR",
} }
def name(self): def name(self):
@ -295,6 +299,7 @@ class AlexaEndpointHealth(AlexaCapability):
""" """
supported_locales = { supported_locales = {
"ar-SA",
"de-DE", "de-DE",
"en-AU", "en-AU",
"en-CA", "en-CA",
@ -302,9 +307,14 @@ class AlexaEndpointHealth(AlexaCapability):
"en-IN", "en-IN",
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR", "fr-FR",
"hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR",
} }
def __init__(self, hass, entity): def __init__(self, hass, entity):
@ -345,6 +355,7 @@ class AlexaPowerController(AlexaCapability):
""" """
supported_locales = { supported_locales = {
"ar-SA",
"de-DE", "de-DE",
"en-AU", "en-AU",
"en-CA", "en-CA",
@ -352,9 +363,14 @@ class AlexaPowerController(AlexaCapability):
"en-IN", "en-IN",
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR", "fr-FR",
"hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR",
} }
def name(self): def name(self):
@ -379,7 +395,7 @@ class AlexaPowerController(AlexaCapability):
raise UnsupportedProperty(name) raise UnsupportedProperty(name)
if self.entity.domain == climate.DOMAIN: if self.entity.domain == climate.DOMAIN:
is_on = self.entity.state != climate.HVAC_MODE_OFF is_on = self.entity.state != climate.HVACMode.OFF
elif self.entity.domain == fan.DOMAIN: elif self.entity.domain == fan.DOMAIN:
is_on = self.entity.state == fan.STATE_ON is_on = self.entity.state == fan.STATE_ON
elif self.entity.domain == vacuum.DOMAIN: elif self.entity.domain == vacuum.DOMAIN:
@ -400,6 +416,7 @@ class AlexaLockController(AlexaCapability):
""" """
supported_locales = { supported_locales = {
"ar-SA",
"de-DE", "de-DE",
"en-AU", "en-AU",
"en-CA", "en-CA",
@ -461,9 +478,14 @@ class AlexaSceneController(AlexaCapability):
"en-IN", "en-IN",
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR", "fr-FR",
"hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR",
} }
def __init__(self, entity, supports_deactivation): def __init__(self, entity, supports_deactivation):
@ -483,6 +505,7 @@ class AlexaBrightnessController(AlexaCapability):
""" """
supported_locales = { supported_locales = {
"ar-SA",
"de-DE", "de-DE",
"en-AU", "en-AU",
"en-CA", "en-CA",
@ -490,6 +513,9 @@ class AlexaBrightnessController(AlexaCapability):
"en-IN", "en-IN",
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR", "fr-FR",
"hi-IN", "hi-IN",
"it-IT", "it-IT",
@ -536,6 +562,9 @@ class AlexaColorController(AlexaCapability):
"en-IN", "en-IN",
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR", "fr-FR",
"hi-IN", "hi-IN",
"it-IT", "it-IT",
@ -587,6 +616,9 @@ class AlexaColorTemperatureController(AlexaCapability):
"en-IN", "en-IN",
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR", "fr-FR",
"hi-IN", "hi-IN",
"it-IT", "it-IT",
@ -635,9 +667,13 @@ class AlexaPercentageController(AlexaCapability):
"en-IN", "en-IN",
"en-US", "en-US",
"es-ES", "es-ES",
"es-US",
"fr-CA",
"fr-FR", "fr-FR",
"hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR",
} }
def name(self): def name(self):
@ -758,7 +794,24 @@ class AlexaPlaybackController(AlexaCapability):
https://developer.amazon.com/docs/device-apis/alexa-playbackcontroller.html https://developer.amazon.com/docs/device-apis/alexa-playbackcontroller.html
""" """
supported_locales = {"de-DE", "en-AU", "en-CA", "en-GB", "en-IN", "en-US", "fr-FR"} supported_locales = {
"ar-SA",
"de-DE",
"en-AU",
"en-CA",
"en-GB",
"en-IN",
"en-US",
"es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
"pt-BR",
}
def name(self): def name(self):
"""Return the Alexa API name of this interface.""" """Return the Alexa API name of this interface."""
@ -792,7 +845,24 @@ class AlexaInputController(AlexaCapability):
https://developer.amazon.com/docs/device-apis/alexa-inputcontroller.html https://developer.amazon.com/docs/device-apis/alexa-inputcontroller.html
""" """
supported_locales = {"de-DE", "en-AU", "en-CA", "en-GB", "en-IN", "en-US"} supported_locales = {
"ar-SA",
"de-DE",
"en-AU",
"en-CA",
"en-GB",
"en-IN",
"en-US",
"es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
"pt-BR",
}
def name(self): def name(self):
"""Return the Alexa API name of this interface.""" """Return the Alexa API name of this interface."""
@ -830,6 +900,7 @@ class AlexaTemperatureSensor(AlexaCapability):
""" """
supported_locales = { supported_locales = {
"ar-SA",
"de-DE", "de-DE",
"en-AU", "en-AU",
"en-CA", "en-CA",
@ -837,9 +908,14 @@ class AlexaTemperatureSensor(AlexaCapability):
"en-IN", "en-IN",
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR", "fr-FR",
"hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR",
} }
def __init__(self, hass, entity): def __init__(self, hass, entity):
@ -901,12 +977,18 @@ class AlexaContactSensor(AlexaCapability):
"de-DE", "de-DE",
"en-AU", "en-AU",
"en-CA", "en-CA",
"en-GB",
"en-IN", "en-IN",
"en-US", "en-US",
"en-GB",
"es-ES", "es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR",
"hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR",
} }
def __init__(self, hass, entity): def __init__(self, hass, entity):
@ -950,10 +1032,15 @@ class AlexaMotionSensor(AlexaCapability):
"de-DE", "de-DE",
"en-AU", "en-AU",
"en-CA", "en-CA",
"en-GB",
"en-IN", "en-IN",
"en-US", "en-US",
"en-GB",
"es-ES", "es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR",
"hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR", "pt-BR",
@ -997,6 +1084,7 @@ class AlexaThermostatController(AlexaCapability):
""" """
supported_locales = { supported_locales = {
"ar-SA",
"de-DE", "de-DE",
"en-AU", "en-AU",
"en-CA", "en-CA",
@ -1004,7 +1092,11 @@ class AlexaThermostatController(AlexaCapability):
"en-IN", "en-IN",
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR", "fr-FR",
"hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR", "pt-BR",
@ -1127,6 +1219,8 @@ class AlexaPowerLevelController(AlexaCapability):
"en-IN", "en-IN",
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX",
"fr-CA",
"fr-FR", "fr-FR",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
@ -1260,10 +1354,13 @@ class AlexaModeController(AlexaCapability):
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX", "es-MX",
"es-US",
"fr-CA", "fr-CA",
"fr-FR", "fr-FR",
"hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR",
} }
def __init__(self, entity, instance, non_controllable=False): def __init__(self, entity, instance, non_controllable=False):
@ -1446,10 +1543,13 @@ class AlexaRangeController(AlexaCapability):
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX", "es-MX",
"es-US",
"fr-CA", "fr-CA",
"fr-FR", "fr-FR",
"hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR",
} }
def __init__(self, entity, instance, non_controllable=False): def __init__(self, entity, instance, non_controllable=False):
@ -1691,8 +1791,10 @@ class AlexaToggleController(AlexaCapability):
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX", "es-MX",
"es-US",
"fr-CA", "fr-CA",
"fr-FR", "fr-FR",
"hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR", "pt-BR",
@ -1753,6 +1855,7 @@ class AlexaChannelController(AlexaCapability):
""" """
supported_locales = { supported_locales = {
"ar-SA",
"de-DE", "de-DE",
"en-AU", "en-AU",
"en-CA", "en-CA",
@ -1761,6 +1864,8 @@ class AlexaChannelController(AlexaCapability):
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX", "es-MX",
"es-US",
"fr-CA",
"fr-FR", "fr-FR",
"hi-IN", "hi-IN",
"it-IT", "it-IT",
@ -1780,6 +1885,7 @@ class AlexaDoorbellEventSource(AlexaCapability):
""" """
supported_locales = { supported_locales = {
"ar-SA",
"de-DE", "de-DE",
"en-AU", "en-AU",
"en-CA", "en-CA",
@ -1794,6 +1900,7 @@ class AlexaDoorbellEventSource(AlexaCapability):
"hi-IN", "hi-IN",
"it-IT", "it-IT",
"ja-JP", "ja-JP",
"pt-BR",
} }
def name(self): def name(self):
@ -1811,7 +1918,24 @@ class AlexaPlaybackStateReporter(AlexaCapability):
https://developer.amazon.com/docs/device-apis/alexa-playbackstatereporter.html https://developer.amazon.com/docs/device-apis/alexa-playbackstatereporter.html
""" """
supported_locales = {"de-DE", "en-GB", "en-US", "es-MX", "fr-FR"} supported_locales = {
"ar-SA",
"de-DE",
"en-AU",
"en-CA",
"en-GB",
"en-IN",
"en-US",
"es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
"pt-BR",
}
def name(self): def name(self):
"""Return the Alexa API name of this interface.""" """Return the Alexa API name of this interface."""
@ -1849,7 +1973,24 @@ class AlexaSeekController(AlexaCapability):
https://developer.amazon.com/docs/device-apis/alexa-seekcontroller.html https://developer.amazon.com/docs/device-apis/alexa-seekcontroller.html
""" """
supported_locales = {"de-DE", "en-GB", "en-US", "es-MX"} supported_locales = {
"ar-SA",
"de-DE",
"en-AU",
"en-CA",
"en-GB",
"en-IN",
"en-US",
"es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
"pt-BR",
}
def name(self): def name(self):
"""Return the Alexa API name of this interface.""" """Return the Alexa API name of this interface."""
@ -1925,7 +2066,24 @@ class AlexaEqualizerController(AlexaCapability):
https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-equalizercontroller.html https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-equalizercontroller.html
""" """
supported_locales = {"de-DE", "en-IN", "en-US", "es-ES", "it-IT", "ja-JP", "pt-BR"} supported_locales = {
"de-DE",
"en-AU",
"en-CA",
"en-GB",
"en-IN",
"en-US",
"es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR",
"hi-IN",
"it-IT",
"ja-JP",
"pt-BR",
}
VALID_SOUND_MODES = { VALID_SOUND_MODES = {
"MOVIE", "MOVIE",
"MUSIC", "MUSIC",
@ -2017,6 +2175,7 @@ class AlexaCameraStreamController(AlexaCapability):
""" """
supported_locales = { supported_locales = {
"ar-SA",
"de-DE", "de-DE",
"en-AU", "en-AU",
"en-CA", "en-CA",
@ -2024,6 +2183,9 @@ class AlexaCameraStreamController(AlexaCapability):
"en-IN", "en-IN",
"en-US", "en-US",
"es-ES", "es-ES",
"es-MX",
"es-US",
"fr-CA",
"fr-FR", "fr-FR",
"hi-IN", "hi-IN",
"it-IT", "it-IT",

View File

@ -460,7 +460,7 @@ class ClimateCapabilities(AlexaEntity):
def interfaces(self): def interfaces(self):
"""Yield the supported interfaces.""" """Yield the supported interfaces."""
# If we support two modes, one being off, we allow turning on too. # If we support two modes, one being off, we allow turning on too.
if climate.HVAC_MODE_OFF in self.entity.attributes.get( if climate.HVACMode.OFF in self.entity.attributes.get(
climate.ATTR_HVAC_MODES, [] climate.ATTR_HVAC_MODES, []
): ):
yield AlexaPowerController(self.entity) yield AlexaPowerController(self.entity)

View File

@ -1,6 +1,10 @@
"""Alexa message handlers.""" """Alexa message handlers."""
from __future__ import annotations
from collections.abc import Callable, Coroutine
import logging import logging
import math import math
from typing import Any
from homeassistant import core as ha from homeassistant import core as ha
from homeassistant.components import ( from homeassistant.components import (
@ -51,6 +55,7 @@ from homeassistant.util.decorator import Registry
import homeassistant.util.dt as dt_util import homeassistant.util.dt as dt_util
from homeassistant.util.temperature import convert as convert_temperature from homeassistant.util.temperature import convert as convert_temperature
from .config import AbstractConfig
from .const import ( from .const import (
API_TEMP_UNITS, API_TEMP_UNITS,
API_THERMOSTAT_MODES, API_THERMOSTAT_MODES,
@ -70,14 +75,27 @@ from .errors import (
AlexaUnsupportedThermostatModeError, AlexaUnsupportedThermostatModeError,
AlexaVideoActionNotPermittedForContentError, AlexaVideoActionNotPermittedForContentError,
) )
from .messages import AlexaDirective, AlexaResponse
from .state_report import async_enable_proactive_mode from .state_report import async_enable_proactive_mode
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
HANDLERS = Registry() # type: ignore[var-annotated] DIRECTIVE_NOT_SUPPORTED = "Entity does not support directive"
HANDLERS: Registry[
tuple[str, str],
Callable[
[ha.HomeAssistant, AbstractConfig, AlexaDirective, ha.Context],
Coroutine[Any, Any, AlexaResponse],
],
] = Registry()
@HANDLERS.register(("Alexa.Discovery", "Discover")) @HANDLERS.register(("Alexa.Discovery", "Discover"))
async def async_api_discovery(hass, config, directive, context): async def async_api_discovery(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Create a API formatted discovery response. """Create a API formatted discovery response.
Async friendly. Async friendly.
@ -96,7 +114,12 @@ async def async_api_discovery(hass, config, directive, context):
@HANDLERS.register(("Alexa.Authorization", "AcceptGrant")) @HANDLERS.register(("Alexa.Authorization", "AcceptGrant"))
async def async_api_accept_grant(hass, config, directive, context): async def async_api_accept_grant(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Create a API formatted AcceptGrant response. """Create a API formatted AcceptGrant response.
Async friendly. Async friendly.
@ -116,7 +139,12 @@ async def async_api_accept_grant(hass, config, directive, context):
@HANDLERS.register(("Alexa.PowerController", "TurnOn")) @HANDLERS.register(("Alexa.PowerController", "TurnOn"))
async def async_api_turn_on(hass, config, directive, context): async def async_api_turn_on(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a turn on request.""" """Process a turn on request."""
entity = directive.entity entity = directive.entity
if (domain := entity.domain) == group.DOMAIN: if (domain := entity.domain) == group.DOMAIN:
@ -157,7 +185,12 @@ async def async_api_turn_on(hass, config, directive, context):
@HANDLERS.register(("Alexa.PowerController", "TurnOff")) @HANDLERS.register(("Alexa.PowerController", "TurnOff"))
async def async_api_turn_off(hass, config, directive, context): async def async_api_turn_off(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a turn off request.""" """Process a turn off request."""
entity = directive.entity entity = directive.entity
domain = entity.domain domain = entity.domain
@ -199,7 +232,12 @@ async def async_api_turn_off(hass, config, directive, context):
@HANDLERS.register(("Alexa.BrightnessController", "SetBrightness")) @HANDLERS.register(("Alexa.BrightnessController", "SetBrightness"))
async def async_api_set_brightness(hass, config, directive, context): async def async_api_set_brightness(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a set brightness request.""" """Process a set brightness request."""
entity = directive.entity entity = directive.entity
brightness = int(directive.payload["brightness"]) brightness = int(directive.payload["brightness"])
@ -216,7 +254,12 @@ async def async_api_set_brightness(hass, config, directive, context):
@HANDLERS.register(("Alexa.BrightnessController", "AdjustBrightness")) @HANDLERS.register(("Alexa.BrightnessController", "AdjustBrightness"))
async def async_api_adjust_brightness(hass, config, directive, context): async def async_api_adjust_brightness(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process an adjust brightness request.""" """Process an adjust brightness request."""
entity = directive.entity entity = directive.entity
brightness_delta = int(directive.payload["brightnessDelta"]) brightness_delta = int(directive.payload["brightnessDelta"])
@ -237,7 +280,12 @@ async def async_api_adjust_brightness(hass, config, directive, context):
@HANDLERS.register(("Alexa.ColorController", "SetColor")) @HANDLERS.register(("Alexa.ColorController", "SetColor"))
async def async_api_set_color(hass, config, directive, context): async def async_api_set_color(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a set color request.""" """Process a set color request."""
entity = directive.entity entity = directive.entity
rgb = color_util.color_hsb_to_RGB( rgb = color_util.color_hsb_to_RGB(
@ -258,7 +306,12 @@ async def async_api_set_color(hass, config, directive, context):
@HANDLERS.register(("Alexa.ColorTemperatureController", "SetColorTemperature")) @HANDLERS.register(("Alexa.ColorTemperatureController", "SetColorTemperature"))
async def async_api_set_color_temperature(hass, config, directive, context): async def async_api_set_color_temperature(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a set color temperature request.""" """Process a set color temperature request."""
entity = directive.entity entity = directive.entity
kelvin = int(directive.payload["colorTemperatureInKelvin"]) kelvin = int(directive.payload["colorTemperatureInKelvin"])
@ -275,7 +328,12 @@ async def async_api_set_color_temperature(hass, config, directive, context):
@HANDLERS.register(("Alexa.ColorTemperatureController", "DecreaseColorTemperature")) @HANDLERS.register(("Alexa.ColorTemperatureController", "DecreaseColorTemperature"))
async def async_api_decrease_color_temp(hass, config, directive, context): async def async_api_decrease_color_temp(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a decrease color temperature request.""" """Process a decrease color temperature request."""
entity = directive.entity entity = directive.entity
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP)) current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
@ -294,7 +352,12 @@ async def async_api_decrease_color_temp(hass, config, directive, context):
@HANDLERS.register(("Alexa.ColorTemperatureController", "IncreaseColorTemperature")) @HANDLERS.register(("Alexa.ColorTemperatureController", "IncreaseColorTemperature"))
async def async_api_increase_color_temp(hass, config, directive, context): async def async_api_increase_color_temp(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process an increase color temperature request.""" """Process an increase color temperature request."""
entity = directive.entity entity = directive.entity
current = int(entity.attributes.get(light.ATTR_COLOR_TEMP)) current = int(entity.attributes.get(light.ATTR_COLOR_TEMP))
@ -313,7 +376,12 @@ async def async_api_increase_color_temp(hass, config, directive, context):
@HANDLERS.register(("Alexa.SceneController", "Activate")) @HANDLERS.register(("Alexa.SceneController", "Activate"))
async def async_api_activate(hass, config, directive, context): async def async_api_activate(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process an activate request.""" """Process an activate request."""
entity = directive.entity entity = directive.entity
domain = entity.domain domain = entity.domain
@ -343,7 +411,12 @@ async def async_api_activate(hass, config, directive, context):
@HANDLERS.register(("Alexa.SceneController", "Deactivate")) @HANDLERS.register(("Alexa.SceneController", "Deactivate"))
async def async_api_deactivate(hass, config, directive, context): async def async_api_deactivate(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a deactivate request.""" """Process a deactivate request."""
entity = directive.entity entity = directive.entity
domain = entity.domain domain = entity.domain
@ -367,16 +440,24 @@ async def async_api_deactivate(hass, config, directive, context):
@HANDLERS.register(("Alexa.PercentageController", "SetPercentage")) @HANDLERS.register(("Alexa.PercentageController", "SetPercentage"))
async def async_api_set_percentage(hass, config, directive, context): async def async_api_set_percentage(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a set percentage request.""" """Process a set percentage request."""
entity = directive.entity entity = directive.entity
service = None
data = {ATTR_ENTITY_ID: entity.entity_id}
if entity.domain == fan.DOMAIN: if entity.domain != fan.DOMAIN:
service = fan.SERVICE_SET_PERCENTAGE raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
percentage = int(directive.payload["percentage"]) percentage = int(directive.payload["percentage"])
data[fan.ATTR_PERCENTAGE] = percentage service = fan.SERVICE_SET_PERCENTAGE
data = {
ATTR_ENTITY_ID: entity.entity_id,
fan.ATTR_PERCENTAGE: percentage,
}
await hass.services.async_call( await hass.services.async_call(
entity.domain, service, data, blocking=False, context=context entity.domain, service, data, blocking=False, context=context
@ -386,20 +467,27 @@ async def async_api_set_percentage(hass, config, directive, context):
@HANDLERS.register(("Alexa.PercentageController", "AdjustPercentage")) @HANDLERS.register(("Alexa.PercentageController", "AdjustPercentage"))
async def async_api_adjust_percentage(hass, config, directive, context): async def async_api_adjust_percentage(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process an adjust percentage request.""" """Process an adjust percentage request."""
entity = directive.entity entity = directive.entity
if entity.domain != fan.DOMAIN:
raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
percentage_delta = int(directive.payload["percentageDelta"]) percentage_delta = int(directive.payload["percentageDelta"])
service = None
data = {ATTR_ENTITY_ID: entity.entity_id}
if entity.domain == fan.DOMAIN:
service = fan.SERVICE_SET_PERCENTAGE
current = entity.attributes.get(fan.ATTR_PERCENTAGE) or 0 current = entity.attributes.get(fan.ATTR_PERCENTAGE) or 0
# set percentage # set percentage
percentage = min(100, max(0, percentage_delta + current)) percentage = min(100, max(0, percentage_delta + current))
data[fan.ATTR_PERCENTAGE] = percentage service = fan.SERVICE_SET_PERCENTAGE
data = {
ATTR_ENTITY_ID: entity.entity_id,
fan.ATTR_PERCENTAGE: percentage,
}
await hass.services.async_call( await hass.services.async_call(
entity.domain, service, data, blocking=False, context=context entity.domain, service, data, blocking=False, context=context
@ -409,7 +497,12 @@ async def async_api_adjust_percentage(hass, config, directive, context):
@HANDLERS.register(("Alexa.LockController", "Lock")) @HANDLERS.register(("Alexa.LockController", "Lock"))
async def async_api_lock(hass, config, directive, context): async def async_api_lock(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a lock request.""" """Process a lock request."""
entity = directive.entity entity = directive.entity
await hass.services.async_call( await hass.services.async_call(
@ -428,7 +521,12 @@ async def async_api_lock(hass, config, directive, context):
@HANDLERS.register(("Alexa.LockController", "Unlock")) @HANDLERS.register(("Alexa.LockController", "Unlock"))
async def async_api_unlock(hass, config, directive, context): async def async_api_unlock(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process an unlock request.""" """Process an unlock request."""
if config.locale not in {"de-DE", "en-US", "ja-JP"}: if config.locale not in {"de-DE", "en-US", "ja-JP"}:
msg = f"The unlock directive is not supported for the following locales: {config.locale}" msg = f"The unlock directive is not supported for the following locales: {config.locale}"
@ -452,7 +550,12 @@ async def async_api_unlock(hass, config, directive, context):
@HANDLERS.register(("Alexa.Speaker", "SetVolume")) @HANDLERS.register(("Alexa.Speaker", "SetVolume"))
async def async_api_set_volume(hass, config, directive, context): async def async_api_set_volume(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a set volume request.""" """Process a set volume request."""
volume = round(float(directive.payload["volume"] / 100), 2) volume = round(float(directive.payload["volume"] / 100), 2)
entity = directive.entity entity = directive.entity
@ -470,7 +573,12 @@ async def async_api_set_volume(hass, config, directive, context):
@HANDLERS.register(("Alexa.InputController", "SelectInput")) @HANDLERS.register(("Alexa.InputController", "SelectInput"))
async def async_api_select_input(hass, config, directive, context): async def async_api_select_input(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a set input request.""" """Process a set input request."""
media_input = directive.payload["input"] media_input = directive.payload["input"]
entity = directive.entity entity = directive.entity
@ -514,7 +622,12 @@ async def async_api_select_input(hass, config, directive, context):
@HANDLERS.register(("Alexa.Speaker", "AdjustVolume")) @HANDLERS.register(("Alexa.Speaker", "AdjustVolume"))
async def async_api_adjust_volume(hass, config, directive, context): async def async_api_adjust_volume(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process an adjust volume request.""" """Process an adjust volume request."""
volume_delta = int(directive.payload["volume"]) volume_delta = int(directive.payload["volume"])
@ -542,7 +655,12 @@ async def async_api_adjust_volume(hass, config, directive, context):
@HANDLERS.register(("Alexa.StepSpeaker", "AdjustVolume")) @HANDLERS.register(("Alexa.StepSpeaker", "AdjustVolume"))
async def async_api_adjust_volume_step(hass, config, directive, context): async def async_api_adjust_volume_step(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process an adjust volume step request.""" """Process an adjust volume step request."""
# media_player volume up/down service does not support specifying steps # media_player volume up/down service does not support specifying steps
# each component handles it differently e.g. via config. # each component handles it differently e.g. via config.
@ -575,7 +693,12 @@ async def async_api_adjust_volume_step(hass, config, directive, context):
@HANDLERS.register(("Alexa.StepSpeaker", "SetMute")) @HANDLERS.register(("Alexa.StepSpeaker", "SetMute"))
@HANDLERS.register(("Alexa.Speaker", "SetMute")) @HANDLERS.register(("Alexa.Speaker", "SetMute"))
async def async_api_set_mute(hass, config, directive, context): async def async_api_set_mute(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a set mute request.""" """Process a set mute request."""
mute = bool(directive.payload["mute"]) mute = bool(directive.payload["mute"])
entity = directive.entity entity = directive.entity
@ -592,7 +715,12 @@ async def async_api_set_mute(hass, config, directive, context):
@HANDLERS.register(("Alexa.PlaybackController", "Play")) @HANDLERS.register(("Alexa.PlaybackController", "Play"))
async def async_api_play(hass, config, directive, context): async def async_api_play(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a play request.""" """Process a play request."""
entity = directive.entity entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id} data = {ATTR_ENTITY_ID: entity.entity_id}
@ -605,7 +733,12 @@ async def async_api_play(hass, config, directive, context):
@HANDLERS.register(("Alexa.PlaybackController", "Pause")) @HANDLERS.register(("Alexa.PlaybackController", "Pause"))
async def async_api_pause(hass, config, directive, context): async def async_api_pause(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a pause request.""" """Process a pause request."""
entity = directive.entity entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id} data = {ATTR_ENTITY_ID: entity.entity_id}
@ -618,7 +751,12 @@ async def async_api_pause(hass, config, directive, context):
@HANDLERS.register(("Alexa.PlaybackController", "Stop")) @HANDLERS.register(("Alexa.PlaybackController", "Stop"))
async def async_api_stop(hass, config, directive, context): async def async_api_stop(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a stop request.""" """Process a stop request."""
entity = directive.entity entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id} data = {ATTR_ENTITY_ID: entity.entity_id}
@ -631,7 +769,12 @@ async def async_api_stop(hass, config, directive, context):
@HANDLERS.register(("Alexa.PlaybackController", "Next")) @HANDLERS.register(("Alexa.PlaybackController", "Next"))
async def async_api_next(hass, config, directive, context): async def async_api_next(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a next request.""" """Process a next request."""
entity = directive.entity entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id} data = {ATTR_ENTITY_ID: entity.entity_id}
@ -644,7 +787,12 @@ async def async_api_next(hass, config, directive, context):
@HANDLERS.register(("Alexa.PlaybackController", "Previous")) @HANDLERS.register(("Alexa.PlaybackController", "Previous"))
async def async_api_previous(hass, config, directive, context): async def async_api_previous(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a previous request.""" """Process a previous request."""
entity = directive.entity entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id} data = {ATTR_ENTITY_ID: entity.entity_id}
@ -676,7 +824,12 @@ def temperature_from_object(hass, temp_obj, interval=False):
@HANDLERS.register(("Alexa.ThermostatController", "SetTargetTemperature")) @HANDLERS.register(("Alexa.ThermostatController", "SetTargetTemperature"))
async def async_api_set_target_temp(hass, config, directive, context): async def async_api_set_target_temp(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a set target temperature request.""" """Process a set target temperature request."""
entity = directive.entity entity = directive.entity
min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP) min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP)
@ -736,7 +889,12 @@ async def async_api_set_target_temp(hass, config, directive, context):
@HANDLERS.register(("Alexa.ThermostatController", "AdjustTargetTemperature")) @HANDLERS.register(("Alexa.ThermostatController", "AdjustTargetTemperature"))
async def async_api_adjust_target_temp(hass, config, directive, context): async def async_api_adjust_target_temp(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process an adjust target temperature request.""" """Process an adjust target temperature request."""
entity = directive.entity entity = directive.entity
min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP) min_temp = entity.attributes.get(climate.ATTR_MIN_TEMP)
@ -773,7 +931,12 @@ async def async_api_adjust_target_temp(hass, config, directive, context):
@HANDLERS.register(("Alexa.ThermostatController", "SetThermostatMode")) @HANDLERS.register(("Alexa.ThermostatController", "SetThermostatMode"))
async def async_api_set_thermostat_mode(hass, config, directive, context): async def async_api_set_thermostat_mode(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a set thermostat mode request.""" """Process a set thermostat mode request."""
entity = directive.entity entity = directive.entity
mode = directive.payload["thermostatMode"] mode = directive.payload["thermostatMode"]
@ -836,13 +999,23 @@ async def async_api_set_thermostat_mode(hass, config, directive, context):
@HANDLERS.register(("Alexa", "ReportState")) @HANDLERS.register(("Alexa", "ReportState"))
async def async_api_reportstate(hass, config, directive, context): async def async_api_reportstate(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a ReportState request.""" """Process a ReportState request."""
return directive.response(name="StateReport") return directive.response(name="StateReport")
@HANDLERS.register(("Alexa.SecurityPanelController", "Arm")) @HANDLERS.register(("Alexa.SecurityPanelController", "Arm"))
async def async_api_arm(hass, config, directive, context): async def async_api_arm(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a Security Panel Arm request.""" """Process a Security Panel Arm request."""
entity = directive.entity entity = directive.entity
service = None service = None
@ -859,6 +1032,8 @@ async def async_api_arm(hass, config, directive, context):
service = SERVICE_ALARM_ARM_NIGHT service = SERVICE_ALARM_ARM_NIGHT
elif arm_state == "ARMED_STAY": elif arm_state == "ARMED_STAY":
service = SERVICE_ALARM_ARM_HOME service = SERVICE_ALARM_ARM_HOME
else:
raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
await hass.services.async_call( await hass.services.async_call(
entity.domain, service, data, blocking=False, context=context entity.domain, service, data, blocking=False, context=context
@ -883,7 +1058,12 @@ async def async_api_arm(hass, config, directive, context):
@HANDLERS.register(("Alexa.SecurityPanelController", "Disarm")) @HANDLERS.register(("Alexa.SecurityPanelController", "Disarm"))
async def async_api_disarm(hass, config, directive, context): async def async_api_disarm(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a Security Panel Disarm request.""" """Process a Security Panel Disarm request."""
entity = directive.entity entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id} data = {ATTR_ENTITY_ID: entity.entity_id}
@ -916,7 +1096,12 @@ async def async_api_disarm(hass, config, directive, context):
@HANDLERS.register(("Alexa.ModeController", "SetMode")) @HANDLERS.register(("Alexa.ModeController", "SetMode"))
async def async_api_set_mode(hass, config, directive, context): async def async_api_set_mode(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a SetMode directive.""" """Process a SetMode directive."""
entity = directive.entity entity = directive.entity
instance = directive.instance instance = directive.instance
@ -955,9 +1140,8 @@ async def async_api_set_mode(hass, config, directive, context):
elif position == "custom": elif position == "custom":
service = cover.SERVICE_STOP_COVER service = cover.SERVICE_STOP_COVER
else: if not service:
msg = "Entity does not support directive" raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
raise AlexaInvalidDirectiveError(msg)
await hass.services.async_call( await hass.services.async_call(
domain, service, data, blocking=False, context=context domain, service, data, blocking=False, context=context
@ -977,7 +1161,12 @@ async def async_api_set_mode(hass, config, directive, context):
@HANDLERS.register(("Alexa.ModeController", "AdjustMode")) @HANDLERS.register(("Alexa.ModeController", "AdjustMode"))
async def async_api_adjust_mode(hass, config, directive, context): async def async_api_adjust_mode(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a AdjustMode request. """Process a AdjustMode request.
Requires capabilityResources supportedModes to be ordered. Requires capabilityResources supportedModes to be ordered.
@ -985,26 +1174,30 @@ async def async_api_adjust_mode(hass, config, directive, context):
""" """
# Currently no supportedModes are configured with ordered=True to support this request. # Currently no supportedModes are configured with ordered=True to support this request.
msg = "Entity does not support directive" raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
raise AlexaInvalidDirectiveError(msg)
@HANDLERS.register(("Alexa.ToggleController", "TurnOn")) @HANDLERS.register(("Alexa.ToggleController", "TurnOn"))
async def async_api_toggle_on(hass, config, directive, context): async def async_api_toggle_on(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a toggle on request.""" """Process a toggle on request."""
entity = directive.entity entity = directive.entity
instance = directive.instance instance = directive.instance
domain = entity.domain domain = entity.domain
service = None
data = {ATTR_ENTITY_ID: entity.entity_id}
# Fan Oscillating # Fan Oscillating
if instance == f"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}": if instance != f"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}":
raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
service = fan.SERVICE_OSCILLATE service = fan.SERVICE_OSCILLATE
data[fan.ATTR_OSCILLATING] = True data = {
else: ATTR_ENTITY_ID: entity.entity_id,
msg = "Entity does not support directive" fan.ATTR_OSCILLATING: True,
raise AlexaInvalidDirectiveError(msg) }
await hass.services.async_call( await hass.services.async_call(
domain, service, data, blocking=False, context=context domain, service, data, blocking=False, context=context
@ -1024,21 +1217,26 @@ async def async_api_toggle_on(hass, config, directive, context):
@HANDLERS.register(("Alexa.ToggleController", "TurnOff")) @HANDLERS.register(("Alexa.ToggleController", "TurnOff"))
async def async_api_toggle_off(hass, config, directive, context): async def async_api_toggle_off(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a toggle off request.""" """Process a toggle off request."""
entity = directive.entity entity = directive.entity
instance = directive.instance instance = directive.instance
domain = entity.domain domain = entity.domain
service = None
data = {ATTR_ENTITY_ID: entity.entity_id}
# Fan Oscillating # Fan Oscillating
if instance == f"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}": if instance != f"{fan.DOMAIN}.{fan.ATTR_OSCILLATING}":
raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
service = fan.SERVICE_OSCILLATE service = fan.SERVICE_OSCILLATE
data[fan.ATTR_OSCILLATING] = False data = {
else: ATTR_ENTITY_ID: entity.entity_id,
msg = "Entity does not support directive" fan.ATTR_OSCILLATING: False,
raise AlexaInvalidDirectiveError(msg) }
await hass.services.async_call( await hass.services.async_call(
domain, service, data, blocking=False, context=context domain, service, data, blocking=False, context=context
@ -1058,7 +1256,12 @@ async def async_api_toggle_off(hass, config, directive, context):
@HANDLERS.register(("Alexa.RangeController", "SetRangeValue")) @HANDLERS.register(("Alexa.RangeController", "SetRangeValue"))
async def async_api_set_range(hass, config, directive, context): async def async_api_set_range(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a next request.""" """Process a next request."""
entity = directive.entity entity = directive.entity
instance = directive.instance instance = directive.instance
@ -1125,8 +1328,7 @@ async def async_api_set_range(hass, config, directive, context):
data[vacuum.ATTR_FAN_SPEED] = speed data[vacuum.ATTR_FAN_SPEED] = speed
else: else:
msg = "Entity does not support directive" raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
raise AlexaInvalidDirectiveError(msg)
await hass.services.async_call( await hass.services.async_call(
domain, service, data, blocking=False, context=context domain, service, data, blocking=False, context=context
@ -1146,16 +1348,21 @@ async def async_api_set_range(hass, config, directive, context):
@HANDLERS.register(("Alexa.RangeController", "AdjustRangeValue")) @HANDLERS.register(("Alexa.RangeController", "AdjustRangeValue"))
async def async_api_adjust_range(hass, config, directive, context): async def async_api_adjust_range(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a next request.""" """Process a next request."""
entity = directive.entity entity = directive.entity
instance = directive.instance instance = directive.instance
domain = entity.domain domain = entity.domain
service = None service = None
data = {ATTR_ENTITY_ID: entity.entity_id} data: dict[str, Any] = {ATTR_ENTITY_ID: entity.entity_id}
range_delta = directive.payload["rangeValueDelta"] range_delta = directive.payload["rangeValueDelta"]
range_delta_default = bool(directive.payload["rangeValueDeltaDefault"]) range_delta_default = bool(directive.payload["rangeValueDeltaDefault"])
response_value = 0 response_value: int | None = 0
# Cover Position # Cover Position
if instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}": if instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
@ -1232,12 +1439,10 @@ async def async_api_adjust_range(hass, config, directive, context):
speed = next( speed = next(
(v for i, v in enumerate(speed_list) if i == new_speed_index), None (v for i, v in enumerate(speed_list) if i == new_speed_index), None
) )
data[vacuum.ATTR_FAN_SPEED] = response_value = speed data[vacuum.ATTR_FAN_SPEED] = response_value = speed
else: else:
msg = "Entity does not support directive" raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
raise AlexaInvalidDirectiveError(msg)
await hass.services.async_call( await hass.services.async_call(
domain, service, data, blocking=False, context=context domain, service, data, blocking=False, context=context
@ -1257,7 +1462,12 @@ async def async_api_adjust_range(hass, config, directive, context):
@HANDLERS.register(("Alexa.ChannelController", "ChangeChannel")) @HANDLERS.register(("Alexa.ChannelController", "ChangeChannel"))
async def async_api_changechannel(hass, config, directive, context): async def async_api_changechannel(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a change channel request.""" """Process a change channel request."""
channel = "0" channel = "0"
entity = directive.entity entity = directive.entity
@ -1309,7 +1519,12 @@ async def async_api_changechannel(hass, config, directive, context):
@HANDLERS.register(("Alexa.ChannelController", "SkipChannels")) @HANDLERS.register(("Alexa.ChannelController", "SkipChannels"))
async def async_api_skipchannel(hass, config, directive, context): async def async_api_skipchannel(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a skipchannel request.""" """Process a skipchannel request."""
channel = int(directive.payload["channelCount"]) channel = int(directive.payload["channelCount"])
entity = directive.entity entity = directive.entity
@ -1340,7 +1555,12 @@ async def async_api_skipchannel(hass, config, directive, context):
@HANDLERS.register(("Alexa.SeekController", "AdjustSeekPosition")) @HANDLERS.register(("Alexa.SeekController", "AdjustSeekPosition"))
async def async_api_seek(hass, config, directive, context): async def async_api_seek(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a seek request.""" """Process a seek request."""
entity = directive.entity entity = directive.entity
position_delta = int(directive.payload["deltaPositionMilliseconds"]) position_delta = int(directive.payload["deltaPositionMilliseconds"])
@ -1379,7 +1599,12 @@ async def async_api_seek(hass, config, directive, context):
@HANDLERS.register(("Alexa.EqualizerController", "SetMode")) @HANDLERS.register(("Alexa.EqualizerController", "SetMode"))
async def async_api_set_eq_mode(hass, config, directive, context): async def async_api_set_eq_mode(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a SetMode request for EqualizerController.""" """Process a SetMode request for EqualizerController."""
mode = directive.payload["mode"] mode = directive.payload["mode"]
entity = directive.entity entity = directive.entity
@ -1406,18 +1631,27 @@ async def async_api_set_eq_mode(hass, config, directive, context):
@HANDLERS.register(("Alexa.EqualizerController", "AdjustBands")) @HANDLERS.register(("Alexa.EqualizerController", "AdjustBands"))
@HANDLERS.register(("Alexa.EqualizerController", "ResetBands")) @HANDLERS.register(("Alexa.EqualizerController", "ResetBands"))
@HANDLERS.register(("Alexa.EqualizerController", "SetBands")) @HANDLERS.register(("Alexa.EqualizerController", "SetBands"))
async def async_api_bands_directive(hass, config, directive, context): async def async_api_bands_directive(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Handle an AdjustBands, ResetBands, SetBands request. """Handle an AdjustBands, ResetBands, SetBands request.
Only mode directives are currently supported for the EqualizerController. Only mode directives are currently supported for the EqualizerController.
""" """
# Currently bands directives are not supported. # Currently bands directives are not supported.
msg = "Entity does not support directive" raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
raise AlexaInvalidDirectiveError(msg)
@HANDLERS.register(("Alexa.TimeHoldController", "Hold")) @HANDLERS.register(("Alexa.TimeHoldController", "Hold"))
async def async_api_hold(hass, config, directive, context): async def async_api_hold(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a TimeHoldController Hold request.""" """Process a TimeHoldController Hold request."""
entity = directive.entity entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id} data = {ATTR_ENTITY_ID: entity.entity_id}
@ -1429,8 +1663,7 @@ async def async_api_hold(hass, config, directive, context):
service = vacuum.SERVICE_START_PAUSE service = vacuum.SERVICE_START_PAUSE
else: else:
msg = "Entity does not support directive" raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
raise AlexaInvalidDirectiveError(msg)
await hass.services.async_call( await hass.services.async_call(
entity.domain, service, data, blocking=False, context=context entity.domain, service, data, blocking=False, context=context
@ -1440,7 +1673,12 @@ async def async_api_hold(hass, config, directive, context):
@HANDLERS.register(("Alexa.TimeHoldController", "Resume")) @HANDLERS.register(("Alexa.TimeHoldController", "Resume"))
async def async_api_resume(hass, config, directive, context): async def async_api_resume(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a TimeHoldController Resume request.""" """Process a TimeHoldController Resume request."""
entity = directive.entity entity = directive.entity
data = {ATTR_ENTITY_ID: entity.entity_id} data = {ATTR_ENTITY_ID: entity.entity_id}
@ -1452,8 +1690,7 @@ async def async_api_resume(hass, config, directive, context):
service = vacuum.SERVICE_START_PAUSE service = vacuum.SERVICE_START_PAUSE
else: else:
msg = "Entity does not support directive" raise AlexaInvalidDirectiveError(DIRECTIVE_NOT_SUPPORTED)
raise AlexaInvalidDirectiveError(msg)
await hass.services.async_call( await hass.services.async_call(
entity.domain, service, data, blocking=False, context=context entity.domain, service, data, blocking=False, context=context
@ -1463,11 +1700,18 @@ async def async_api_resume(hass, config, directive, context):
@HANDLERS.register(("Alexa.CameraStreamController", "InitializeCameraStreams")) @HANDLERS.register(("Alexa.CameraStreamController", "InitializeCameraStreams"))
async def async_api_initialize_camera_stream(hass, config, directive, context): async def async_api_initialize_camera_stream(
hass: ha.HomeAssistant,
config: AbstractConfig,
directive: AlexaDirective,
context: ha.Context,
) -> AlexaResponse:
"""Process a InitializeCameraStreams request.""" """Process a InitializeCameraStreams request."""
entity = directive.entity entity = directive.entity
stream_source = await camera.async_request_stream(hass, entity.entity_id, fmt="hls") stream_source = await camera.async_request_stream(hass, entity.entity_id, fmt="hls")
camera_image = hass.states.get(entity.entity_id).attributes[ATTR_ENTITY_PICTURE] state = hass.states.get(entity.entity_id)
assert state
camera_image = state.attributes[ATTR_ENTITY_PICTURE]
try: try:
external_url = network.get_url( external_url = network.get_url(

View File

@ -2,17 +2,17 @@
"config": { "config": {
"abort": { "abort": {
"cannot_connect": "No se pudo conectar", "cannot_connect": "No se pudo conectar",
"missing_configuration": "El componente no est\u00e1 configurado. Mira su documentaci\u00f3n.", "missing_configuration": "El componente no est\u00e1 configurado. Por favor, sigue la documentaci\u00f3n.",
"no_url_available": "No hay URL disponible. Para obtener informaci\u00f3n sobre este error, [consulta la secci\u00f3n de ayuda]({docs_url})", "no_url_available": "No hay URL disponible. Para obtener informaci\u00f3n sobre este error, [revisa la secci\u00f3n de ayuda]({docs_url})",
"single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n." "single_instance_allowed": "Ya est\u00e1 configurado. Solo es posible una \u00fanica configuraci\u00f3n."
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {
"description": "\u00bfDesea configurar Home Assistant para conectarse a Almond proporcionado por el complemento Supervisor: {addon} ?", "description": "\u00bfQuieres configurar Home Assistant para conectarse a Almond proporcionado por el complemento: {addon}?",
"title": "Almond a trav\u00e9s del complemento Supervisor" "title": "Almond a trav\u00e9s del complemento Home Assistant"
}, },
"pick_implementation": { "pick_implementation": {
"title": "Seleccione el m\u00e9todo de autenticaci\u00f3n" "title": "Selecciona el m\u00e9todo de autenticaci\u00f3n"
} }
} }
} }

View File

@ -2,7 +2,9 @@
"config": { "config": {
"abort": { "abort": {
"cannot_connect": "Det g\u00e5r inte att ansluta till Almond-servern.", "cannot_connect": "Det g\u00e5r inte att ansluta till Almond-servern.",
"missing_configuration": "Kontrollera dokumentationen f\u00f6r hur du st\u00e4ller in Almond." "missing_configuration": "Kontrollera dokumentationen f\u00f6r hur du st\u00e4ller in Almond.",
"no_url_available": "Ingen webbadress tillg\u00e4nglig. F\u00f6r information om detta fel, [kolla hj\u00e4lpavsnittet]({docs_url})",
"single_instance_allowed": "Redan konfigurerad. Endast en konfiguration m\u00f6jlig."
}, },
"step": { "step": {
"hassio_confirm": { "hassio_confirm": {

View File

@ -10,7 +10,7 @@ import voluptuous as vol
from homeassistant.components import persistent_notification from homeassistant.components import persistent_notification
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.const import ATTR_ATTRIBUTION, CONF_API_KEY, CONF_CURRENCY, CONF_NAME from homeassistant.const import CONF_API_KEY, CONF_CURRENCY, CONF_NAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -118,6 +118,8 @@ def setup_platform(
class AlphaVantageSensor(SensorEntity): class AlphaVantageSensor(SensorEntity):
"""Representation of a Alpha Vantage sensor.""" """Representation of a Alpha Vantage sensor."""
_attr_attribution = ATTRIBUTION
def __init__(self, timeseries, symbol): def __init__(self, timeseries, symbol):
"""Initialize the sensor.""" """Initialize the sensor."""
self._symbol = symbol[CONF_SYMBOL] self._symbol = symbol[CONF_SYMBOL]
@ -126,7 +128,7 @@ class AlphaVantageSensor(SensorEntity):
self._attr_native_unit_of_measurement = symbol.get(CONF_CURRENCY, self._symbol) self._attr_native_unit_of_measurement = symbol.get(CONF_CURRENCY, self._symbol)
self._attr_icon = ICONS.get(symbol.get(CONF_CURRENCY, "USD")) self._attr_icon = ICONS.get(symbol.get(CONF_CURRENCY, "USD"))
def update(self): def update(self) -> None:
"""Get the latest data and updates the states.""" """Get the latest data and updates the states."""
_LOGGER.debug("Requesting new data for symbol %s", self._symbol) _LOGGER.debug("Requesting new data for symbol %s", self._symbol)
all_values, _ = self._timeseries.get_intraday(self._symbol) all_values, _ = self._timeseries.get_intraday(self._symbol)
@ -137,13 +139,12 @@ class AlphaVantageSensor(SensorEntity):
self._attr_native_value = None self._attr_native_value = None
self._attr_extra_state_attributes = ( self._attr_extra_state_attributes = (
{ {
ATTR_ATTRIBUTION: ATTRIBUTION,
ATTR_CLOSE: values["4. close"], ATTR_CLOSE: values["4. close"],
ATTR_HIGH: values["2. high"], ATTR_HIGH: values["2. high"],
ATTR_LOW: values["3. low"], ATTR_LOW: values["3. low"],
} }
if isinstance(values, dict) if isinstance(values, dict)
else None else {}
) )
_LOGGER.debug("Received new values for symbol %s", self._symbol) _LOGGER.debug("Received new values for symbol %s", self._symbol)
@ -151,6 +152,8 @@ class AlphaVantageSensor(SensorEntity):
class AlphaVantageForeignExchange(SensorEntity): class AlphaVantageForeignExchange(SensorEntity):
"""Sensor for foreign exchange rates.""" """Sensor for foreign exchange rates."""
_attr_attribution = ATTRIBUTION
def __init__(self, foreign_exchange, config): def __init__(self, foreign_exchange, config):
"""Initialize the sensor.""" """Initialize the sensor."""
self._foreign_exchange = foreign_exchange self._foreign_exchange = foreign_exchange
@ -164,7 +167,7 @@ class AlphaVantageForeignExchange(SensorEntity):
self._attr_icon = ICONS.get(self._from_currency, "USD") self._attr_icon = ICONS.get(self._from_currency, "USD")
self._attr_native_unit_of_measurement = self._to_currency self._attr_native_unit_of_measurement = self._to_currency
def update(self): def update(self) -> None:
"""Get the latest data and updates the states.""" """Get the latest data and updates the states."""
_LOGGER.debug( _LOGGER.debug(
"Requesting new data for forex %s - %s", "Requesting new data for forex %s - %s",
@ -180,12 +183,11 @@ class AlphaVantageForeignExchange(SensorEntity):
self._attr_native_value = None self._attr_native_value = None
self._attr_extra_state_attributes = ( self._attr_extra_state_attributes = (
{ {
ATTR_ATTRIBUTION: ATTRIBUTION,
CONF_FROM: self._from_currency, CONF_FROM: self._from_currency,
CONF_TO: self._to_currency, CONF_TO: self._to_currency,
} }
if values is not None if values is not None
else None else {}
) )
_LOGGER.debug( _LOGGER.debug(

View File

@ -36,6 +36,7 @@ SUPPORTED_VOICES: Final[list[str]] = [
"Aditi", # Hindi "Aditi", # Hindi
"Amy", "Amy",
"Aria", "Aria",
"Arthur", # English, Neural
"Astrid", # Swedish "Astrid", # Swedish
"Ayanda", "Ayanda",
"Bianca", # Italian "Bianca", # Italian
@ -47,6 +48,7 @@ SUPPORTED_VOICES: Final[list[str]] = [
"Chantal", # French Canadian "Chantal", # French Canadian
"Conchita", "Conchita",
"Cristiano", "Cristiano",
"Daniel", # German, Neural
"Dora", # Icelandic "Dora", # Icelandic
"Emma", # English "Emma", # English
"Enrique", "Enrique",
@ -69,6 +71,7 @@ SUPPORTED_VOICES: Final[list[str]] = [
"Kevin", "Kevin",
"Kimberly", "Kimberly",
"Lea", # French "Lea", # French
"Liam", # Canadian French, Neural
"Liv", # Norwegian "Liv", # Norwegian
"Lotte", # Dutch "Lotte", # Dutch
"Lucia", # Spanish European "Lucia", # Spanish European
@ -86,6 +89,7 @@ SUPPORTED_VOICES: Final[list[str]] = [
"Nicole", # English Australian "Nicole", # English Australian
"Olivia", # Female, Australian, Neural "Olivia", # Female, Australian, Neural
"Penelope", # Spanish US "Penelope", # Spanish US
"Pedro", # Spanish US, Neural
"Raveena", # English, Indian "Raveena", # English, Indian
"Ricardo", "Ricardo",
"Ruben", "Ruben",

View File

@ -3,15 +3,15 @@ from __future__ import annotations
from ambee import AirQuality, Ambee, AmbeeAuthenticationError, Pollen from ambee import AirQuality, Ambee, AmbeeAuthenticationError, Pollen
from homeassistant.components.repairs import (
IssueSeverity,
async_create_issue,
async_delete_issue,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, Platform from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.issue_registry import (
IssueSeverity,
async_create_issue,
async_delete_issue,
)
from homeassistant.helpers.typing import ConfigType from homeassistant.helpers.typing import ConfigType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.update_coordinator import DataUpdateCoordinator

View File

@ -4,7 +4,6 @@
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/ambee", "documentation": "https://www.home-assistant.io/integrations/ambee",
"requirements": ["ambee==0.4.0"], "requirements": ["ambee==0.4.0"],
"dependencies": ["repairs"],
"codeowners": ["@frenck"], "codeowners": ["@frenck"],
"quality_scale": "platinum", "quality_scale": "platinum",
"iot_class": "cloud_polling" "iot_class": "cloud_polling"

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