mirror of
https://github.com/home-assistant/core.git
synced 2025-07-29 16:17:20 +00:00
Merge pull request #58994 from home-assistant/rc
This commit is contained in:
commit
85a4ee68e3
61
.coveragerc
61
.coveragerc
@ -29,10 +29,8 @@ omit =
|
||||
homeassistant/components/ads/*
|
||||
homeassistant/components/aemet/weather_update_coordinator.py
|
||||
homeassistant/components/aftership/*
|
||||
homeassistant/components/agent_dvr/__init__.py
|
||||
homeassistant/components/agent_dvr/alarm_control_panel.py
|
||||
homeassistant/components/agent_dvr/camera.py
|
||||
homeassistant/components/agent_dvr/const.py
|
||||
homeassistant/components/agent_dvr/helpers.py
|
||||
homeassistant/components/airnow/__init__.py
|
||||
homeassistant/components/airnow/sensor.py
|
||||
@ -85,7 +83,6 @@ omit =
|
||||
homeassistant/components/aurora/binary_sensor.py
|
||||
homeassistant/components/aurora/const.py
|
||||
homeassistant/components/aurora/sensor.py
|
||||
homeassistant/components/aurora_abb_powerone/sensor.py
|
||||
homeassistant/components/avea/light.py
|
||||
homeassistant/components/avion/light.py
|
||||
homeassistant/components/azure_devops/__init__.py
|
||||
@ -134,6 +131,7 @@ omit =
|
||||
homeassistant/components/braviatv/remote.py
|
||||
homeassistant/components/broadlink/__init__.py
|
||||
homeassistant/components/broadlink/const.py
|
||||
homeassistant/components/broadlink/light.py
|
||||
homeassistant/components/broadlink/remote.py
|
||||
homeassistant/components/broadlink/switch.py
|
||||
homeassistant/components/broadlink/updater.py
|
||||
@ -269,7 +267,10 @@ omit =
|
||||
homeassistant/components/enphase_envoy/__init__.py
|
||||
homeassistant/components/enphase_envoy/sensor.py
|
||||
homeassistant/components/entur_public_transport/*
|
||||
homeassistant/components/environment_canada/*
|
||||
homeassistant/components/environment_canada/__init__.py
|
||||
homeassistant/components/environment_canada/camera.py
|
||||
homeassistant/components/environment_canada/sensor.py
|
||||
homeassistant/components/environment_canada/weather.py
|
||||
homeassistant/components/envirophat/sensor.py
|
||||
homeassistant/components/envisalink/*
|
||||
homeassistant/components/ephember/climate.py
|
||||
@ -290,7 +291,6 @@ omit =
|
||||
homeassistant/components/esphome/select.py
|
||||
homeassistant/components/esphome/sensor.py
|
||||
homeassistant/components/esphome/switch.py
|
||||
homeassistant/components/essent/sensor.py
|
||||
homeassistant/components/etherscan/sensor.py
|
||||
homeassistant/components/eufy/*
|
||||
homeassistant/components/everlights/light.py
|
||||
@ -299,6 +299,7 @@ omit =
|
||||
homeassistant/components/ezviz/camera.py
|
||||
homeassistant/components/ezviz/coordinator.py
|
||||
homeassistant/components/ezviz/const.py
|
||||
homeassistant/components/ezviz/entity.py
|
||||
homeassistant/components/ezviz/binary_sensor.py
|
||||
homeassistant/components/ezviz/sensor.py
|
||||
homeassistant/components/ezviz/switch.py
|
||||
@ -331,6 +332,7 @@ omit =
|
||||
homeassistant/components/fjaraskupan/const.py
|
||||
homeassistant/components/fjaraskupan/fan.py
|
||||
homeassistant/components/fjaraskupan/light.py
|
||||
homeassistant/components/fjaraskupan/number.py
|
||||
homeassistant/components/fjaraskupan/sensor.py
|
||||
homeassistant/components/fleetgo/device_tracker.py
|
||||
homeassistant/components/flexit/climate.py
|
||||
@ -343,7 +345,6 @@ omit =
|
||||
homeassistant/components/flume/sensor.py
|
||||
homeassistant/components/flunearyou/__init__.py
|
||||
homeassistant/components/flunearyou/sensor.py
|
||||
homeassistant/components/flux_led/light.py
|
||||
homeassistant/components/folder/sensor.py
|
||||
homeassistant/components/folder_watcher/*
|
||||
homeassistant/components/foobot/sensor.py
|
||||
@ -457,7 +458,6 @@ omit =
|
||||
homeassistant/components/hp_ilo/sensor.py
|
||||
homeassistant/components/htu21d/sensor.py
|
||||
homeassistant/components/huawei_lte/*
|
||||
homeassistant/components/huawei_router/device_tracker.py
|
||||
homeassistant/components/hue/light.py
|
||||
homeassistant/components/hunterdouglas_powerview/__init__.py
|
||||
homeassistant/components/hunterdouglas_powerview/scene.py
|
||||
@ -585,6 +585,11 @@ omit =
|
||||
homeassistant/components/logi_circle/const.py
|
||||
homeassistant/components/logi_circle/sensor.py
|
||||
homeassistant/components/london_underground/sensor.py
|
||||
homeassistant/components/lookin/__init__.py
|
||||
homeassistant/components/lookin/entity.py
|
||||
homeassistant/components/lookin/models.py
|
||||
homeassistant/components/lookin/sensor.py
|
||||
homeassistant/components/lookin/climate.py
|
||||
homeassistant/components/loopenergy/sensor.py
|
||||
homeassistant/components/luci/device_tracker.py
|
||||
homeassistant/components/luftdaten/__init__.py
|
||||
@ -599,7 +604,6 @@ omit =
|
||||
homeassistant/components/lutron_caseta/scene.py
|
||||
homeassistant/components/lutron_caseta/switch.py
|
||||
homeassistant/components/lw12wifi/light.py
|
||||
homeassistant/components/lyft/sensor.py
|
||||
homeassistant/components/lyric/__init__.py
|
||||
homeassistant/components/lyric/api.py
|
||||
homeassistant/components/lyric/climate.py
|
||||
@ -649,13 +653,7 @@ omit =
|
||||
homeassistant/components/mitemp_bt/sensor.py
|
||||
homeassistant/components/mjpeg/camera.py
|
||||
homeassistant/components/mochad/*
|
||||
homeassistant/components/modbus/base_platform.py
|
||||
homeassistant/components/modbus/binary_sensor.py
|
||||
homeassistant/components/modbus/cover.py
|
||||
homeassistant/components/modbus/climate.py
|
||||
homeassistant/components/modbus/modbus.py
|
||||
homeassistant/components/modbus/sensor.py
|
||||
homeassistant/components/modbus/validators.py
|
||||
homeassistant/components/modem_callerid/sensor.py
|
||||
homeassistant/components/motion_blinds/__init__.py
|
||||
homeassistant/components/motion_blinds/const.py
|
||||
@ -703,7 +701,6 @@ omit =
|
||||
homeassistant/components/neato/switch.py
|
||||
homeassistant/components/neato/vacuum.py
|
||||
homeassistant/components/nederlandse_spoorwegen/sensor.py
|
||||
homeassistant/components/nello/lock.py
|
||||
homeassistant/components/nest/legacy/*
|
||||
homeassistant/components/netdata/sensor.py
|
||||
homeassistant/components/netgear/__init__.py
|
||||
@ -734,11 +731,10 @@ omit =
|
||||
homeassistant/components/nuki/const.py
|
||||
homeassistant/components/nuki/binary_sensor.py
|
||||
homeassistant/components/nuki/lock.py
|
||||
homeassistant/components/nut/sensor.py
|
||||
homeassistant/components/nx584/alarm_control_panel.py
|
||||
homeassistant/components/nzbget/coordinator.py
|
||||
homeassistant/components/obihai/*
|
||||
homeassistant/components/octoprint/*
|
||||
homeassistant/components/octoprint/__init__.py
|
||||
homeassistant/components/oem/climate.py
|
||||
homeassistant/components/oasa_telematics/sensor.py
|
||||
homeassistant/components/ohmconnect/sensor.py
|
||||
@ -765,7 +761,10 @@ omit =
|
||||
homeassistant/components/openevse/sensor.py
|
||||
homeassistant/components/openexchangerates/sensor.py
|
||||
homeassistant/components/opengarage/__init__.py
|
||||
homeassistant/components/opengarage/binary_sensor.py
|
||||
homeassistant/components/opengarage/cover.py
|
||||
homeassistant/components/opengarage/entity.py
|
||||
homeassistant/components/opengarage/sensor.py
|
||||
homeassistant/components/openhome/__init__.py
|
||||
homeassistant/components/openhome/media_player.py
|
||||
homeassistant/components/openhome/const.py
|
||||
@ -781,7 +780,6 @@ omit =
|
||||
homeassistant/components/openweathermap/sensor.py
|
||||
homeassistant/components/openweathermap/weather.py
|
||||
homeassistant/components/openweathermap/weather_update_coordinator.py
|
||||
homeassistant/components/openweathermap/abstract_owm_sensor.py
|
||||
homeassistant/components/opnsense/*
|
||||
homeassistant/components/opple/light.py
|
||||
homeassistant/components/orangepi_gpio/*
|
||||
@ -907,6 +905,7 @@ omit =
|
||||
homeassistant/components/screenlogic/__init__.py
|
||||
homeassistant/components/screenlogic/binary_sensor.py
|
||||
homeassistant/components/screenlogic/climate.py
|
||||
homeassistant/components/screenlogic/light.py
|
||||
homeassistant/components/screenlogic/sensor.py
|
||||
homeassistant/components/screenlogic/services.py
|
||||
homeassistant/components/screenlogic/switch.py
|
||||
@ -1002,7 +1001,8 @@ omit =
|
||||
homeassistant/components/starlingbank/sensor.py
|
||||
homeassistant/components/steam_online/sensor.py
|
||||
homeassistant/components/stiebel_eltron/*
|
||||
homeassistant/components/stookalert/*
|
||||
homeassistant/components/stookalert/__init__.py
|
||||
homeassistant/components/stookalert/binary_sensor.py
|
||||
homeassistant/components/stream/*
|
||||
homeassistant/components/streamlabswater/*
|
||||
homeassistant/components/suez_water/*
|
||||
@ -1106,7 +1106,14 @@ omit =
|
||||
homeassistant/components/tractive/entity.py
|
||||
homeassistant/components/tractive/sensor.py
|
||||
homeassistant/components/tractive/switch.py
|
||||
homeassistant/components/tradfri/*
|
||||
homeassistant/components/tradfri/__init__.py
|
||||
homeassistant/components/tradfri/base_class.py
|
||||
homeassistant/components/tradfri/config_flow.py
|
||||
homeassistant/components/tradfri/cover.py
|
||||
homeassistant/components/tradfri/fan.py
|
||||
homeassistant/components/tradfri/light.py
|
||||
homeassistant/components/tradfri/sensor.py
|
||||
homeassistant/components/tradfri/switch.py
|
||||
homeassistant/components/trafikverket_train/sensor.py
|
||||
homeassistant/components/trafikverket_weatherstation/sensor.py
|
||||
homeassistant/components/transmission/sensor.py
|
||||
@ -1116,12 +1123,22 @@ omit =
|
||||
homeassistant/components/travisci/sensor.py
|
||||
homeassistant/components/tuya/__init__.py
|
||||
homeassistant/components/tuya/base.py
|
||||
homeassistant/components/tuya/binary_sensor.py
|
||||
homeassistant/components/tuya/camera.py
|
||||
homeassistant/components/tuya/climate.py
|
||||
homeassistant/components/tuya/const.py
|
||||
homeassistant/components/tuya/cover.py
|
||||
homeassistant/components/tuya/fan.py
|
||||
homeassistant/components/tuya/humidifier.py
|
||||
homeassistant/components/tuya/light.py
|
||||
homeassistant/components/tuya/number.py
|
||||
homeassistant/components/tuya/scene.py
|
||||
homeassistant/components/tuya/select.py
|
||||
homeassistant/components/tuya/sensor.py
|
||||
homeassistant/components/tuya/siren.py
|
||||
homeassistant/components/tuya/switch.py
|
||||
homeassistant/components/tuya/util.py
|
||||
homeassistant/components/tuya/vacuum.py
|
||||
homeassistant/components/twentemilieu/const.py
|
||||
homeassistant/components/twentemilieu/sensor.py
|
||||
homeassistant/components/twilio_call/notify.py
|
||||
@ -1151,6 +1168,7 @@ omit =
|
||||
homeassistant/components/velbus/sensor.py
|
||||
homeassistant/components/velbus/switch.py
|
||||
homeassistant/components/velux/*
|
||||
homeassistant/components/venstar/__init__.py
|
||||
homeassistant/components/venstar/climate.py
|
||||
homeassistant/components/verisure/__init__.py
|
||||
homeassistant/components/verisure/alarm_control_panel.py
|
||||
@ -1174,6 +1192,7 @@ omit =
|
||||
homeassistant/components/vilfo/const.py
|
||||
homeassistant/components/vivotek/camera.py
|
||||
homeassistant/components/vlc/media_player.py
|
||||
homeassistant/components/vlc_telnet/__init__.py
|
||||
homeassistant/components/vlc_telnet/media_player.py
|
||||
homeassistant/components/volkszaehler/sensor.py
|
||||
homeassistant/components/volumio/__init__.py
|
||||
@ -1193,7 +1212,6 @@ omit =
|
||||
homeassistant/components/webostv/*
|
||||
homeassistant/components/whois/sensor.py
|
||||
homeassistant/components/wiffi/*
|
||||
homeassistant/components/wink/*
|
||||
homeassistant/components/wirelesstag/*
|
||||
homeassistant/components/wolflink/__init__.py
|
||||
homeassistant/components/wolflink/sensor.py
|
||||
@ -1236,7 +1254,6 @@ omit =
|
||||
homeassistant/components/xiaomi_miio/select.py
|
||||
homeassistant/components/xiaomi_miio/sensor.py
|
||||
homeassistant/components/xiaomi_miio/switch.py
|
||||
homeassistant/components/xiaomi_miio/vacuum.py
|
||||
homeassistant/components/xiaomi_tv/media_player.py
|
||||
homeassistant/components/xmpp/notify.py
|
||||
homeassistant/components/xs1/*
|
||||
|
24
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
24
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@ -15,7 +15,7 @@ body:
|
||||
attributes:
|
||||
label: The problem
|
||||
description: >-
|
||||
Describe the issue you are experiencing here to communicate to the
|
||||
Describe the issue you are experiencing here, to communicate to the
|
||||
maintainers. Tell us what you were trying to do and what happened.
|
||||
|
||||
Provide a clear and concise description of what the problem is.
|
||||
@ -28,10 +28,12 @@ body:
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: What is version of Home Assistant Core has the issue?
|
||||
label: What version of Home Assistant Core has the issue?
|
||||
placeholder: core-
|
||||
description: >
|
||||
Can be found in the Configuration panel -> Info.
|
||||
Can be found in: [Configuration panel -> Info](https://my.home-assistant.io/redirect/info/).
|
||||
|
||||
[](https://my.home-assistant.io/redirect/info/)
|
||||
- type: input
|
||||
attributes:
|
||||
label: What was the last working version of Home Assistant Core?
|
||||
@ -44,7 +46,9 @@ body:
|
||||
attributes:
|
||||
label: What type of installation are you running?
|
||||
description: >
|
||||
If you don't know, you can find it in: Configuration panel -> Info.
|
||||
Can be found in: [Configuration panel -> Info](https://my.home-assistant.io/redirect/info/).
|
||||
|
||||
[](https://my.home-assistant.io/redirect/info/)
|
||||
options:
|
||||
- Home Assistant OS
|
||||
- Home Assistant Container
|
||||
@ -55,15 +59,15 @@ body:
|
||||
attributes:
|
||||
label: Integration causing the issue
|
||||
description: >
|
||||
The name of the integration, for example, Automation or Philips Hue.
|
||||
The name of the integration. For example: Automation, Philips Hue
|
||||
- type: input
|
||||
id: integration_link
|
||||
attributes:
|
||||
label: Link to integration documentation on our website
|
||||
placeholder: "https://www.home-assistant.io/integrations/..."
|
||||
description: |
|
||||
Providing a link [to the documentation][docs] help us categorizing the
|
||||
issue, while providing a useful reference at the same time.
|
||||
Providing a link [to the documentation][docs] helps us categorize the
|
||||
issue, while also providing a useful reference for others.
|
||||
|
||||
[docs]: https://www.home-assistant.io/integrations
|
||||
|
||||
@ -75,8 +79,8 @@ body:
|
||||
attributes:
|
||||
label: Example YAML snippet
|
||||
description: |
|
||||
If this issue has an example piece of YAML that can help reproducing this problem, please provide.
|
||||
This can be an piece of YAML from, e.g., an automation, script, scene or configuration.
|
||||
If applicable, please provide an example piece of YAML that can help reproduce this problem.
|
||||
This can be from an automation, script, scene or configuration.
|
||||
render: yaml
|
||||
- type: textarea
|
||||
attributes:
|
||||
@ -88,5 +92,3 @@ body:
|
||||
label: Additional information
|
||||
description: >
|
||||
If you have any additional information for us, use the field below.
|
||||
Please note, you can attach screenshots or screen recordings here, by
|
||||
dragging and dropping files in the field below.
|
||||
|
12
.github/workflows/builder.yml
vendored
12
.github/workflows/builder.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
publish: ${{ steps.version.outputs.publish }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@ -67,7 +67,7 @@ jobs:
|
||||
if: needs.init.outputs.publish == 'true'
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
@ -97,7 +97,7 @@ jobs:
|
||||
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
if: needs.init.outputs.channel == 'dev'
|
||||
@ -170,7 +170,7 @@ jobs:
|
||||
- tinker
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1.10.0
|
||||
@ -201,7 +201,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Initialize git
|
||||
uses: home-assistant/actions/helpers/git-init@master
|
||||
@ -233,7 +233,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1.10.0
|
||||
|
42
.github/workflows/ci.yaml
vendored
42
.github/workflows/ci.yaml
vendored
@ -26,7 +26,7 @@ jobs:
|
||||
pre-commit-key: ${{ steps.generate-pre-commit-key.outputs.key }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
id: python
|
||||
uses: actions/setup-python@v2.2.2
|
||||
@ -84,7 +84,7 @@ jobs:
|
||||
needs: prepare-base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
@ -124,7 +124,7 @@ jobs:
|
||||
needs: prepare-base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
@ -164,7 +164,7 @@ jobs:
|
||||
needs: prepare-base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
@ -207,7 +207,7 @@ jobs:
|
||||
needs: prepare-base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Register hadolint problem matcher
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/hadolint.json"
|
||||
@ -226,7 +226,7 @@ jobs:
|
||||
needs: prepare-base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
@ -269,7 +269,7 @@ jobs:
|
||||
needs: prepare-base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
@ -312,7 +312,7 @@ jobs:
|
||||
needs: prepare-base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
@ -352,7 +352,7 @@ jobs:
|
||||
needs: prepare-base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
@ -395,7 +395,7 @@ jobs:
|
||||
needs: prepare-base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
@ -436,7 +436,7 @@ jobs:
|
||||
# needs: prepare-base
|
||||
# steps:
|
||||
# - name: Check out code from GitHub
|
||||
# uses: actions/checkout@v2.3.4
|
||||
# uses: actions/checkout@v2.3.5
|
||||
# - name: Run ShellCheck
|
||||
# uses: ludeeus/action-shellcheck@0.3.0
|
||||
|
||||
@ -446,7 +446,7 @@ jobs:
|
||||
needs: prepare-base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
@ -493,7 +493,7 @@ jobs:
|
||||
container: homeassistant/ci-azure:${{ matrix.python-version }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Restore full Python ${{ matrix.python-version }} virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.6
|
||||
@ -517,7 +517,7 @@ jobs:
|
||||
needs: prepare-base
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
id: python
|
||||
@ -551,7 +551,7 @@ jobs:
|
||||
container: homeassistant/ci-azure:${{ matrix.python-version }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Generate partial Python venv restore key
|
||||
id: generate-python-key
|
||||
run: >-
|
||||
@ -595,7 +595,7 @@ jobs:
|
||||
container: homeassistant/ci-azure:${{ matrix.python-version }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Restore full Python ${{ matrix.python-version }} virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.6
|
||||
@ -626,7 +626,7 @@ jobs:
|
||||
container: homeassistant/ci-azure:${{ matrix.python-version }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Restore full Python ${{ matrix.python-version }} virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.6
|
||||
@ -660,7 +660,7 @@ jobs:
|
||||
container: homeassistant/ci-azure:${{ matrix.python-version }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Restore full Python ${{ matrix.python-version }} virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.6
|
||||
@ -682,11 +682,11 @@ jobs:
|
||||
# Ideally this should be part of our dependencies
|
||||
# However this plugin is fairly new and doesn't run correctly
|
||||
# on a non-GitHub environment.
|
||||
pip install pytest-github-actions-annotate-failures
|
||||
pip install pytest-github-actions-annotate-failures==0.1.3
|
||||
- name: Run pytest
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
pytest \
|
||||
python3 -X dev -bb -m pytest \
|
||||
-qq \
|
||||
--timeout=9 \
|
||||
--durations=10 \
|
||||
@ -718,7 +718,7 @@ jobs:
|
||||
container: homeassistant/ci-azure:${{ matrix.python-version }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
- name: Restore full Python ${{ matrix.python-version }} virtual environment
|
||||
id: cache-venv
|
||||
uses: actions/cache@v2.1.6
|
||||
|
4
.github/workflows/translations.yaml
vendored
4
.github/workflows/translations.yaml
vendored
@ -20,7 +20,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
@ -39,7 +39,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
uses: actions/setup-python@v2.2.2
|
||||
|
6
.github/workflows/wheels.yml
vendored
6
.github/workflows/wheels.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
architectures: ${{ steps.info.outputs.architectures }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Get information
|
||||
id: info
|
||||
@ -68,7 +68,7 @@ jobs:
|
||||
- "3.9-alpine3.14"
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Download env_file
|
||||
uses: actions/download-artifact@v2
|
||||
@ -108,7 +108,7 @@ jobs:
|
||||
- "3.9-alpine3.14"
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2.3.4
|
||||
uses: actions/checkout@v2.3.5
|
||||
|
||||
- name: Download env_file
|
||||
uses: actions/download-artifact@v2
|
||||
|
@ -22,17 +22,17 @@ repos:
|
||||
- --quiet-level=2
|
||||
exclude_types: [csv, json]
|
||||
exclude: ^tests/fixtures/
|
||||
- repo: https://gitlab.com/pycqa/flake8
|
||||
rev: 3.9.2
|
||||
- repo: https://github.com/PyCQA/flake8
|
||||
rev: 4.0.1
|
||||
hooks:
|
||||
- id: flake8
|
||||
additional_dependencies:
|
||||
- pycodestyle==2.7.0
|
||||
- pyflakes==2.3.1
|
||||
- pycodestyle==2.8.0
|
||||
- pyflakes==2.4.0
|
||||
- flake8-docstrings==1.6.0
|
||||
- pydocstyle==6.0.0
|
||||
- flake8-comprehensions==3.5.0
|
||||
- flake8-noqa==1.1.0
|
||||
- pydocstyle==6.1.1
|
||||
- flake8-comprehensions==3.7.0
|
||||
- flake8-noqa==1.2.0
|
||||
- mccabe==0.6.1
|
||||
files: ^(homeassistant|script|tests)/.+\.py$
|
||||
- repo: https://github.com/PyCQA/bandit
|
||||
|
@ -20,6 +20,7 @@ homeassistant.components.ampio.*
|
||||
homeassistant.components.automation.*
|
||||
homeassistant.components.binary_sensor.*
|
||||
homeassistant.components.bluetooth_tracker.*
|
||||
homeassistant.components.bmw_connected_drive.*
|
||||
homeassistant.components.bond.*
|
||||
homeassistant.components.braviatv.*
|
||||
homeassistant.components.brother.*
|
||||
@ -35,18 +36,21 @@ homeassistant.components.dlna_dmr.*
|
||||
homeassistant.components.dnsip.*
|
||||
homeassistant.components.dsmr.*
|
||||
homeassistant.components.dunehd.*
|
||||
homeassistant.components.efergy.*
|
||||
homeassistant.components.elgato.*
|
||||
homeassistant.components.esphome.*
|
||||
homeassistant.components.energy.*
|
||||
homeassistant.components.fastdotcom.*
|
||||
homeassistant.components.fitbit.*
|
||||
homeassistant.components.flunearyou.*
|
||||
homeassistant.components.flux_led.*
|
||||
homeassistant.components.forecast_solar.*
|
||||
homeassistant.components.fritzbox.*
|
||||
homeassistant.components.frontend.*
|
||||
homeassistant.components.fritz.*
|
||||
homeassistant.components.geo_location.*
|
||||
homeassistant.components.gios.*
|
||||
homeassistant.components.goalzero.*
|
||||
homeassistant.components.group.*
|
||||
homeassistant.components.guardian.*
|
||||
homeassistant.components.history.*
|
||||
@ -55,23 +59,30 @@ homeassistant.components.http.*
|
||||
homeassistant.components.huawei_lte.*
|
||||
homeassistant.components.hyperion.*
|
||||
homeassistant.components.image_processing.*
|
||||
homeassistant.components.input_select.*
|
||||
homeassistant.components.integration.*
|
||||
homeassistant.components.iqvia.*
|
||||
homeassistant.components.jewish_calendar.*
|
||||
homeassistant.components.knx.*
|
||||
homeassistant.components.kraken.*
|
||||
homeassistant.components.lcn.*
|
||||
homeassistant.components.light.*
|
||||
homeassistant.components.local_ip.*
|
||||
homeassistant.components.lock.*
|
||||
homeassistant.components.lookin.*
|
||||
homeassistant.components.mailbox.*
|
||||
homeassistant.components.media_player.*
|
||||
homeassistant.components.modbus.*
|
||||
homeassistant.components.modem_callerid.*
|
||||
homeassistant.components.media_source.*
|
||||
homeassistant.components.mysensors.*
|
||||
homeassistant.components.nam.*
|
||||
homeassistant.components.nanoleaf.*
|
||||
homeassistant.components.neato.*
|
||||
homeassistant.components.nest.*
|
||||
homeassistant.components.netatmo.*
|
||||
homeassistant.components.network.*
|
||||
homeassistant.components.nfandroidtv.*
|
||||
homeassistant.components.no_ip.*
|
||||
homeassistant.components.notify.*
|
||||
homeassistant.components.notion.*
|
||||
@ -89,6 +100,7 @@ homeassistant.components.recorder.statistics
|
||||
homeassistant.components.remote.*
|
||||
homeassistant.components.renault.*
|
||||
homeassistant.components.rituals_perfume_genie.*
|
||||
homeassistant.components.rpi_power.*
|
||||
homeassistant.components.samsungtv.*
|
||||
homeassistant.components.scene.*
|
||||
homeassistant.components.select.*
|
||||
@ -98,6 +110,7 @@ homeassistant.components.simplisafe.*
|
||||
homeassistant.components.slack.*
|
||||
homeassistant.components.sonos.media_player
|
||||
homeassistant.components.ssdp.*
|
||||
homeassistant.components.stookalert.*
|
||||
homeassistant.components.stream.*
|
||||
homeassistant.components.sun.*
|
||||
homeassistant.components.surepetcare.*
|
||||
@ -110,6 +123,7 @@ homeassistant.components.tautulli.*
|
||||
homeassistant.components.tcp.*
|
||||
homeassistant.components.tile.*
|
||||
homeassistant.components.tplink.*
|
||||
homeassistant.components.tractive.*
|
||||
homeassistant.components.tradfri.*
|
||||
homeassistant.components.tts.*
|
||||
homeassistant.components.upcloud.*
|
||||
@ -118,6 +132,7 @@ homeassistant.components.uptimerobot.*
|
||||
homeassistant.components.vacuum.*
|
||||
homeassistant.components.vallox.*
|
||||
homeassistant.components.water_heater.*
|
||||
homeassistant.components.watttime.*
|
||||
homeassistant.components.weather.*
|
||||
homeassistant.components.websocket_api.*
|
||||
homeassistant.components.zodiac.*
|
||||
|
31
CODEOWNERS
31
CODEOWNERS
@ -75,10 +75,10 @@ homeassistant/components/blink/* @fronzbot
|
||||
homeassistant/components/blueprint/* @home-assistant/core
|
||||
homeassistant/components/bmp280/* @belidzs
|
||||
homeassistant/components/bmw_connected_drive/* @gerard33 @rikroe
|
||||
homeassistant/components/bond/* @prystupa @joshs85
|
||||
homeassistant/components/bond/* @bdraco @prystupa @joshs85
|
||||
homeassistant/components/bosch_shc/* @tschamm
|
||||
homeassistant/components/braviatv/* @bieniu @Drafteed
|
||||
homeassistant/components/broadlink/* @danielhiversen @felipediel
|
||||
homeassistant/components/broadlink/* @danielhiversen @felipediel @L-I-Am
|
||||
homeassistant/components/brother/* @bieniu
|
||||
homeassistant/components/brunt/* @eavanvalkenburg
|
||||
homeassistant/components/bsblan/* @liudger
|
||||
@ -114,7 +114,7 @@ homeassistant/components/debugpy/* @frenck
|
||||
homeassistant/components/deconz/* @Kane610
|
||||
homeassistant/components/delijn/* @bollewolle @Emilv2
|
||||
homeassistant/components/demo/* @home-assistant/core
|
||||
homeassistant/components/denonavr/* @scarface-4711 @starkillerOG
|
||||
homeassistant/components/denonavr/* @ol-iver @starkillerOG
|
||||
homeassistant/components/derivative/* @afaucogney
|
||||
homeassistant/components/device_automation/* @home-assistant/core
|
||||
homeassistant/components/devolo_home_control/* @2Fake @Shutgun
|
||||
@ -138,7 +138,7 @@ homeassistant/components/ecovacs/* @OverloadUT
|
||||
homeassistant/components/edl21/* @mtdcr
|
||||
homeassistant/components/efergy/* @tkdrob
|
||||
homeassistant/components/egardia/* @jeroenterheerdt
|
||||
homeassistant/components/eight_sleep/* @mezz64
|
||||
homeassistant/components/eight_sleep/* @mezz64 @raman325
|
||||
homeassistant/components/elgato/* @frenck
|
||||
homeassistant/components/elkm1/* @gwww @bdraco
|
||||
homeassistant/components/elv/* @majuss
|
||||
@ -151,13 +151,12 @@ homeassistant/components/enigma2/* @fbradyirl
|
||||
homeassistant/components/enocean/* @bdurrer
|
||||
homeassistant/components/enphase_envoy/* @gtdiehl
|
||||
homeassistant/components/entur_public_transport/* @hfurubotten
|
||||
homeassistant/components/environment_canada/* @michaeldavie
|
||||
homeassistant/components/environment_canada/* @gwww @michaeldavie
|
||||
homeassistant/components/ephember/* @ttroy50
|
||||
homeassistant/components/epson/* @pszafer
|
||||
homeassistant/components/epsonworkforce/* @ThaStealth
|
||||
homeassistant/components/eq3btsmart/* @rytilahti
|
||||
homeassistant/components/esphome/* @OttoWinter @jesserockz
|
||||
homeassistant/components/essent/* @TheLastProject
|
||||
homeassistant/components/evohome/* @zxdavb
|
||||
homeassistant/components/ezviz/* @RenierM26 @baqs
|
||||
homeassistant/components/faa_delays/* @ntilley905
|
||||
@ -174,6 +173,7 @@ homeassistant/components/flo/* @dmulcahey
|
||||
homeassistant/components/flock/* @fabaff
|
||||
homeassistant/components/flume/* @ChrisMandich @bdraco
|
||||
homeassistant/components/flunearyou/* @bachya
|
||||
homeassistant/components/flux_led/* @icemanch
|
||||
homeassistant/components/forecast_solar/* @klaasnicolaas @frenck
|
||||
homeassistant/components/forked_daapd/* @uvjustin
|
||||
homeassistant/components/fortios/* @kimfrellsen
|
||||
@ -181,7 +181,7 @@ homeassistant/components/foscam/* @skgsergio
|
||||
homeassistant/components/freebox/* @hacf-fr @Quentame
|
||||
homeassistant/components/freedompro/* @stefano055415
|
||||
homeassistant/components/fritz/* @mammuth @AaronDavidSchneider @chemelli74
|
||||
homeassistant/components/fritzbox/* @mib1185
|
||||
homeassistant/components/fritzbox/* @mib1185 @flabbamann
|
||||
homeassistant/components/fronius/* @nielstron
|
||||
homeassistant/components/frontend/* @home-assistant/frontend
|
||||
homeassistant/components/garages_amsterdam/* @klaasnicolaas
|
||||
@ -227,9 +227,8 @@ homeassistant/components/homematic/* @pvizeli @danielperna84
|
||||
homeassistant/components/honeywell/* @rdfurman
|
||||
homeassistant/components/http/* @home-assistant/core
|
||||
homeassistant/components/huawei_lte/* @scop @fphammerle
|
||||
homeassistant/components/huawei_router/* @abmantis
|
||||
homeassistant/components/hue/* @balloob @frenck
|
||||
homeassistant/components/huisbaasje/* @denniss17
|
||||
homeassistant/components/huisbaasje/* @dennisschroer
|
||||
homeassistant/components/humidifier/* @home-assistant/core @Shulyaka
|
||||
homeassistant/components/hunterdouglas_powerview/* @bdraco
|
||||
homeassistant/components/hvv_departures/* @vigonotion
|
||||
@ -287,6 +286,7 @@ homeassistant/components/litterrobot/* @natekspencer
|
||||
homeassistant/components/local_ip/* @issacg
|
||||
homeassistant/components/logger/* @home-assistant/core
|
||||
homeassistant/components/logi_circle/* @evanjd
|
||||
homeassistant/components/lookin/* @ANMalko
|
||||
homeassistant/components/loopenergy/* @pavoni
|
||||
homeassistant/components/lovelace/* @home-assistant/frontend
|
||||
homeassistant/components/luci/* @mzdrale
|
||||
@ -336,7 +336,6 @@ homeassistant/components/nam/* @bieniu
|
||||
homeassistant/components/nanoleaf/* @milanmeu
|
||||
homeassistant/components/neato/* @dshokouhi @Santobert
|
||||
homeassistant/components/nederlandse_spoorwegen/* @YarmoM
|
||||
homeassistant/components/nello/* @pschmitt
|
||||
homeassistant/components/ness_alarm/* @nickw444
|
||||
homeassistant/components/nest/* @allenporter
|
||||
homeassistant/components/netatmo/* @cgtobi
|
||||
@ -361,10 +360,11 @@ homeassistant/components/nsw_rural_fire_service_feed/* @exxamalte
|
||||
homeassistant/components/nuki/* @pschmitt @pvizeli @pree
|
||||
homeassistant/components/numato/* @clssn
|
||||
homeassistant/components/number/* @home-assistant/core @Shulyaka
|
||||
homeassistant/components/nut/* @bdraco
|
||||
homeassistant/components/nut/* @bdraco @ollo69
|
||||
homeassistant/components/nws/* @MatthewFlamm
|
||||
homeassistant/components/nzbget/* @chriscla
|
||||
homeassistant/components/obihai/* @dshokouhi
|
||||
homeassistant/components/octoprint/* @rfleming71
|
||||
homeassistant/components/ohmconnect/* @robbiet480
|
||||
homeassistant/components/ombi/* @larssont
|
||||
homeassistant/components/omnilogic/* @oliver84 @djtimca @gentoosu
|
||||
@ -423,6 +423,7 @@ homeassistant/components/rainforest_eagle/* @gtdiehl @jcalbert
|
||||
homeassistant/components/rainmachine/* @bachya
|
||||
homeassistant/components/random/* @fabaff
|
||||
homeassistant/components/recollect_waste/* @bachya
|
||||
homeassistant/components/recorder/* @home-assistant/core
|
||||
homeassistant/components/rejseplanen/* @DarkFox
|
||||
homeassistant/components/renault/* @epenet
|
||||
homeassistant/components/repetier/* @MTrab
|
||||
@ -497,7 +498,7 @@ homeassistant/components/srp_energy/* @briglx
|
||||
homeassistant/components/starline/* @anonym-tsk
|
||||
homeassistant/components/statistics/* @fabaff
|
||||
homeassistant/components/stiebel_eltron/* @fucm
|
||||
homeassistant/components/stookalert/* @fwestenberg
|
||||
homeassistant/components/stookalert/* @fwestenberg @frenck
|
||||
homeassistant/components/stream/* @hunterjm @uvjustin @allenporter
|
||||
homeassistant/components/stt/* @pvizeli
|
||||
homeassistant/components/subaru/* @G-Two
|
||||
@ -533,7 +534,6 @@ homeassistant/components/tile/* @bachya
|
||||
homeassistant/components/time_date/* @fabaff
|
||||
homeassistant/components/tmb/* @alemuro
|
||||
homeassistant/components/todoist/* @boralyl
|
||||
homeassistant/components/toon/* @frenck
|
||||
homeassistant/components/totalconnect/* @austinmroczek
|
||||
homeassistant/components/tplink/* @rytilahti @thegardenmonkey
|
||||
homeassistant/components/traccar/* @ludeeus
|
||||
@ -544,7 +544,7 @@ homeassistant/components/trafikverket_train/* @endor-force
|
||||
homeassistant/components/trafikverket_weatherstation/* @endor-force
|
||||
homeassistant/components/transmission/* @engrbm87 @JPHutchins
|
||||
homeassistant/components/tts/* @pvizeli
|
||||
homeassistant/components/tuya/* @Tuya @zlinoliver @METISU
|
||||
homeassistant/components/tuya/* @Tuya @zlinoliver @METISU @frenck
|
||||
homeassistant/components/twentemilieu/* @frenck
|
||||
homeassistant/components/twinkly/* @dr1rrb
|
||||
homeassistant/components/ubus/* @noltari
|
||||
@ -562,6 +562,7 @@ homeassistant/components/utility_meter/* @dgomes
|
||||
homeassistant/components/vallox/* @andre-richter
|
||||
homeassistant/components/velbus/* @Cereal2nd @brefra
|
||||
homeassistant/components/velux/* @Julius2342
|
||||
homeassistant/components/venstar/* @garbled1
|
||||
homeassistant/components/vera/* @pavoni
|
||||
homeassistant/components/verisure/* @frenck
|
||||
homeassistant/components/versasense/* @flamm3blemuff1n
|
||||
@ -571,7 +572,7 @@ homeassistant/components/vicare/* @oischinger
|
||||
homeassistant/components/vilfo/* @ManneW
|
||||
homeassistant/components/vivotek/* @HarlemSquirrel
|
||||
homeassistant/components/vizio/* @raman325
|
||||
homeassistant/components/vlc_telnet/* @rodripf @dmcc
|
||||
homeassistant/components/vlc_telnet/* @rodripf @dmcc @MartinHjelmare
|
||||
homeassistant/components/volkszaehler/* @fabaff
|
||||
homeassistant/components/volumio/* @OnFreund
|
||||
homeassistant/components/wake_on_lan/* @ntilley905
|
||||
|
@ -9,7 +9,7 @@ from typing import Any, Dict, Mapping, Optional, Tuple, cast
|
||||
import jwt
|
||||
|
||||
from homeassistant import data_entry_flow
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
@ -155,6 +155,7 @@ class AuthManager:
|
||||
self._providers = providers
|
||||
self._mfa_modules = mfa_modules
|
||||
self.login_flow = AuthManagerFlowManager(hass, self)
|
||||
self._revoke_callbacks: dict[str, list[CALLBACK_TYPE]] = {}
|
||||
|
||||
@property
|
||||
def auth_providers(self) -> list[AuthProvider]:
|
||||
@ -275,6 +276,12 @@ class AuthManager:
|
||||
self, user: models.User, credentials: models.Credentials
|
||||
) -> None:
|
||||
"""Link credentials to an existing user."""
|
||||
linked_user = await self.async_get_user_by_credentials(credentials)
|
||||
if linked_user == user:
|
||||
return
|
||||
if linked_user is not None:
|
||||
raise ValueError("Credential is already linked to a user")
|
||||
|
||||
await self._store.async_link_user(user, credentials)
|
||||
|
||||
async def async_remove_user(self, user: models.User) -> None:
|
||||
@ -285,7 +292,7 @@ class AuthManager:
|
||||
]
|
||||
|
||||
if tasks:
|
||||
await asyncio.wait(tasks)
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
await self._store.async_remove_user(user)
|
||||
|
||||
@ -446,6 +453,28 @@ class AuthManager:
|
||||
"""Delete a refresh token."""
|
||||
await self._store.async_remove_refresh_token(refresh_token)
|
||||
|
||||
callbacks = self._revoke_callbacks.pop(refresh_token.id, [])
|
||||
for revoke_callback in callbacks:
|
||||
revoke_callback()
|
||||
|
||||
@callback
|
||||
def async_register_revoke_token_callback(
|
||||
self, refresh_token_id: str, revoke_callback: CALLBACK_TYPE
|
||||
) -> CALLBACK_TYPE:
|
||||
"""Register a callback to be called when the refresh token id is revoked."""
|
||||
if refresh_token_id not in self._revoke_callbacks:
|
||||
self._revoke_callbacks[refresh_token_id] = []
|
||||
|
||||
callbacks = self._revoke_callbacks[refresh_token_id]
|
||||
callbacks.append(revoke_callback)
|
||||
|
||||
@callback
|
||||
def unregister() -> None:
|
||||
if revoke_callback in callbacks:
|
||||
callbacks.remove(revoke_callback)
|
||||
|
||||
return unregister
|
||||
|
||||
@callback
|
||||
def async_create_access_token(
|
||||
self, refresh_token: models.RefreshToken, remote_ip: str | None = None
|
||||
|
@ -25,7 +25,7 @@ from . import (
|
||||
SetupFlow,
|
||||
)
|
||||
|
||||
REQUIREMENTS = ["pyotp==2.3.0"]
|
||||
REQUIREMENTS = ["pyotp==2.6.0"]
|
||||
|
||||
CONF_MESSAGE = "message"
|
||||
|
||||
@ -56,10 +56,10 @@ def _generate_secret() -> str:
|
||||
|
||||
|
||||
def _generate_random() -> int:
|
||||
"""Generate a 8 digit number."""
|
||||
"""Generate a 32 digit number."""
|
||||
import pyotp # pylint: disable=import-outside-toplevel
|
||||
|
||||
return int(pyotp.random_base32(length=8, chars=list("1234567890")))
|
||||
return int(pyotp.random_base32(length=32, chars=list("1234567890")))
|
||||
|
||||
|
||||
def _generate_otp(secret: str, count: int) -> str:
|
||||
@ -245,8 +245,7 @@ class NotifyAuthModule(MultiFactorAuthModule):
|
||||
await self._async_load()
|
||||
assert self._user_settings is not None
|
||||
|
||||
notify_setting = self._user_settings.get(user_id)
|
||||
if notify_setting is None:
|
||||
if (notify_setting := self._user_settings.get(user_id)) is None:
|
||||
_LOGGER.error("Cannot find user %s", user_id)
|
||||
return
|
||||
|
||||
|
@ -18,7 +18,7 @@ from . import (
|
||||
SetupFlow,
|
||||
)
|
||||
|
||||
REQUIREMENTS = ["pyotp==2.3.0", "PyQRCode==1.2.1"]
|
||||
REQUIREMENTS = ["pyotp==2.6.0", "PyQRCode==1.2.1"]
|
||||
|
||||
CONFIG_SCHEMA = MULTI_FACTOR_AUTH_MODULE_SCHEMA.extend({}, extra=vol.PREVENT_EXTRA)
|
||||
|
||||
@ -181,7 +181,7 @@ class TotpSetupFlow(SetupFlow):
|
||||
# to fix typing complaint
|
||||
self._auth_module: TotpAuthModule = auth_module
|
||||
self._user = user
|
||||
self._ota_secret: str | None = None
|
||||
self._ota_secret: str = ""
|
||||
self._url = None # type Optional[str]
|
||||
self._image = None # type Optional[str]
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
import voluptuous as vol
|
||||
@ -16,8 +15,6 @@ from .util import test_all
|
||||
|
||||
POLICY_SCHEMA = vol.Schema({vol.Optional(CAT_ENTITIES): ENTITY_POLICY_SCHEMA})
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AbstractPermissions:
|
||||
"""Default permissions class."""
|
||||
|
@ -17,7 +17,10 @@ import yarl
|
||||
|
||||
from homeassistant import config as conf_util, config_entries, core, loader
|
||||
from homeassistant.components import http
|
||||
from homeassistant.const import REQUIRED_NEXT_PYTHON_DATE, REQUIRED_NEXT_PYTHON_VER
|
||||
from homeassistant.const import (
|
||||
REQUIRED_NEXT_PYTHON_HA_RELEASE,
|
||||
REQUIRED_NEXT_PYTHON_VER,
|
||||
)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import area_registry, device_registry, entity_registry
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
@ -240,11 +243,14 @@ async def async_from_config_dict(
|
||||
stop = monotonic()
|
||||
_LOGGER.info("Home Assistant initialized in %.2fs", stop - start)
|
||||
|
||||
if REQUIRED_NEXT_PYTHON_DATE and sys.version_info[:3] < REQUIRED_NEXT_PYTHON_VER:
|
||||
if (
|
||||
REQUIRED_NEXT_PYTHON_HA_RELEASE
|
||||
and sys.version_info[:3] < REQUIRED_NEXT_PYTHON_VER
|
||||
):
|
||||
msg = (
|
||||
"Support for the running Python version "
|
||||
f"{'.'.join(str(x) for x in sys.version_info[:3])} is deprecated and will "
|
||||
f"be removed in the first release after {REQUIRED_NEXT_PYTHON_DATE}. "
|
||||
f"be removed in Home Assistant {REQUIRED_NEXT_PYTHON_HA_RELEASE}. "
|
||||
"Please upgrade Python to "
|
||||
f"{'.'.join(str(x) for x in REQUIRED_NEXT_PYTHON_VER)} or "
|
||||
"higher."
|
||||
|
@ -20,7 +20,7 @@ from homeassistant.const import (
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import dispatcher_send
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity
|
||||
|
||||
from .const import ATTRIBUTION, DEFAULT_CACHEDB, DOMAIN, LOGGER
|
||||
|
||||
@ -322,14 +322,14 @@ class AbodeDevice(AbodeEntity):
|
||||
}
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return device registry information for this entity."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self._device.device_id)},
|
||||
"manufacturer": "Abode",
|
||||
"name": self._device.name,
|
||||
"device_type": self._device.type,
|
||||
}
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self._device.device_id)},
|
||||
manufacturer="Abode",
|
||||
model=self._device.type,
|
||||
name=self._device.name,
|
||||
)
|
||||
|
||||
def _update_callback(self, device):
|
||||
"""Update the device state."""
|
||||
|
@ -1,4 +1,6 @@
|
||||
"""Config flow for the Abode Security System component."""
|
||||
from http import HTTPStatus
|
||||
|
||||
from abodepy import Abode
|
||||
from abodepy.exceptions import AbodeAuthenticationException, AbodeException
|
||||
from abodepy.helpers.errors import MFA_CODE_REQUIRED
|
||||
@ -6,7 +8,7 @@ from requests.exceptions import ConnectTimeout, HTTPError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME, HTTP_BAD_REQUEST
|
||||
from homeassistant.const import CONF_PASSWORD, CONF_USERNAME
|
||||
|
||||
from .const import DEFAULT_CACHEDB, DOMAIN, LOGGER
|
||||
|
||||
@ -51,7 +53,7 @@ class AbodeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
LOGGER.error("Unable to connect to Abode: %s", ex)
|
||||
|
||||
if ex.errcode == HTTP_BAD_REQUEST:
|
||||
if ex.errcode == HTTPStatus.BAD_REQUEST:
|
||||
errors = {"base": "invalid_auth"}
|
||||
|
||||
else:
|
||||
|
@ -1,9 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e",
|
||||
"single_instance_allowed": "\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f \u043d\u0430 Abode."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435"
|
||||
},
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"password": "\u041f\u0430\u0440\u043e\u043b\u0430",
|
||||
"username": "Email"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "\u041f\u0430\u0440\u043e\u043b\u0430",
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt",
|
||||
"reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt.",
|
||||
"single_instance_allowed": "M\u00e1r konfigur\u00e1lva van. Csak egy konfigur\u00e1ci\u00f3 lehets\u00e9ges."
|
||||
},
|
||||
"error": {
|
||||
|
@ -2,7 +2,7 @@
|
||||
"domain": "accuweather",
|
||||
"name": "AccuWeather",
|
||||
"documentation": "https://www.home-assistant.io/integrations/accuweather/",
|
||||
"requirements": ["accuweather==0.2.0"],
|
||||
"requirements": ["accuweather==0.3.0"],
|
||||
"codeowners": ["@bieniu"],
|
||||
"config_flow": true,
|
||||
"quality_scale": "platinum",
|
||||
|
@ -5,8 +5,9 @@ from typing import Any, cast
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, CONF_NAME, DEVICE_CLASS_TEMPERATURE
|
||||
from homeassistant.const import CONF_NAME, DEVICE_CLASS_TEMPERATURE
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
@ -59,6 +60,7 @@ async def async_setup_entry(
|
||||
class AccuWeatherSensor(CoordinatorEntity, SensorEntity):
|
||||
"""Define an AccuWeather entity."""
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
coordinator: AccuWeatherDataUpdateCoordinator
|
||||
entity_description: AccuWeatherSensorDescription
|
||||
|
||||
@ -75,7 +77,7 @@ class AccuWeatherSensor(CoordinatorEntity, SensorEntity):
|
||||
self._sensor_data = _get_sensor_data(
|
||||
coordinator.data, forecast_day, description.key
|
||||
)
|
||||
self._attrs = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
self._attrs: dict[str, Any] = {}
|
||||
if forecast_day is not None:
|
||||
self._attr_name = f"{name} {description.name} {forecast_day}d"
|
||||
self._attr_unique_id = (
|
||||
@ -92,12 +94,12 @@ class AccuWeatherSensor(CoordinatorEntity, SensorEntity):
|
||||
else:
|
||||
self._unit_system = API_IMPERIAL
|
||||
self._attr_native_unit_of_measurement = description.unit_imperial
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(DOMAIN, coordinator.location_key)},
|
||||
"name": NAME,
|
||||
"manufacturer": MANUFACTURER,
|
||||
"entry_type": "service",
|
||||
}
|
||||
self._attr_device_info = DeviceInfo(
|
||||
entry_type="service",
|
||||
identifiers={(DOMAIN, coordinator.location_key)},
|
||||
manufacturer=MANUFACTURER,
|
||||
name=NAME,
|
||||
)
|
||||
self.forecast_day = forecast_day
|
||||
|
||||
@property
|
||||
|
@ -0,0 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435"
|
||||
}
|
||||
}
|
||||
}
|
@ -16,7 +16,7 @@
|
||||
"longitude": "\u7d93\u5ea6",
|
||||
"name": "\u540d\u7a31"
|
||||
},
|
||||
"description": "\u5047\u5982\u4f60\u9700\u8981\u5354\u52a9\u9032\u884c\u8a2d\u5b9a\uff0c\u8acb\u53c3\u95b1\uff1ahttps://www.home-assistant.io/integrations/accuweather/\n\n\u67d0\u4e9b\u50b3\u611f\u5668\u9810\u8a2d\u70ba\u672a\u555f\u7528\uff0c\u53ef\u4ee5\u65bc\u6574\u5408\u8a2d\u5b9a\u4e2d\u555f\u7528\u9019\u4e9b\u5be6\u9ad4\u3002\u5929\u6c23\u9810\u5831\u9810\u8a2d\u672a\u958b\u555f\u3002\u53ef\u4ee5\u65bc\u6574\u5408\u9078\u9805\u4e2d\u958b\u555f\u3002",
|
||||
"description": "\u5047\u5982\u4f60\u9700\u8981\u5354\u52a9\u9032\u884c\u8a2d\u5b9a\uff0c\u8acb\u53c3\u95b1\uff1ahttps://www.home-assistant.io/integrations/accuweather/\n\n\u67d0\u4e9b\u611f\u6e2c\u5668\u9810\u8a2d\u70ba\u672a\u555f\u7528\uff0c\u53ef\u4ee5\u65bc\u6574\u5408\u8a2d\u5b9a\u4e2d\u555f\u7528\u9019\u4e9b\u5be6\u9ad4\u3002\u5929\u6c23\u9810\u5831\u9810\u8a2d\u672a\u958b\u555f\u3002\u53ef\u4ee5\u65bc\u6574\u5408\u9078\u9805\u4e2d\u958b\u555f\u3002",
|
||||
"title": "AccuWeather"
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ from homeassistant.components.weather import (
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_NAME, TEMP_CELSIUS, TEMP_FAHRENHEIT
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
from homeassistant.util.dt import utc_from_timestamp
|
||||
@ -66,12 +67,12 @@ class AccuWeatherEntity(CoordinatorEntity, WeatherEntity):
|
||||
TEMP_CELSIUS if coordinator.is_metric else TEMP_FAHRENHEIT
|
||||
)
|
||||
self._attr_attribution = ATTRIBUTION
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(DOMAIN, coordinator.location_key)},
|
||||
"name": NAME,
|
||||
"manufacturer": MANUFACTURER,
|
||||
"entry_type": "service",
|
||||
}
|
||||
self._attr_device_info = DeviceInfo(
|
||||
entry_type="service",
|
||||
identifiers={(DOMAIN, coordinator.location_key)},
|
||||
manufacturer=MANUFACTURER,
|
||||
name=NAME,
|
||||
)
|
||||
|
||||
@property
|
||||
def condition(self) -> str | None:
|
||||
|
@ -129,8 +129,7 @@ class AcerSwitch(SwitchEntity):
|
||||
self._attr_available = False
|
||||
|
||||
for key in self._attributes:
|
||||
msg = CMD_DICT.get(key)
|
||||
if msg:
|
||||
if msg := CMD_DICT.get(key):
|
||||
awns = self._write_read_format(msg)
|
||||
self._attributes[key] = awns
|
||||
self._attr_extra_state_attributes = self._attributes
|
||||
|
@ -77,11 +77,11 @@ class AcmedaBase(entity.Entity):
|
||||
return self.roller.name
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
def device_info(self) -> entity.DeviceInfo:
|
||||
"""Return the device info."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.unique_id)},
|
||||
"name": self.roller.name,
|
||||
"manufacturer": "Rollease Acmeda",
|
||||
"via_device": (DOMAIN, self.roller.hub.id),
|
||||
}
|
||||
return entity.DeviceInfo(
|
||||
identifiers={(DOMAIN, self.unique_id)},
|
||||
manufacturer="Rollease Acmeda",
|
||||
name=self.roller.name,
|
||||
via_device=(DOMAIN, self.roller.hub.id),
|
||||
)
|
||||
|
@ -1,7 +1,6 @@
|
||||
"""Support for Adax wifi-enabled home heaters."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from adax import Adax
|
||||
@ -21,11 +20,10 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import ACCOUNT_ID
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
from .const import ACCOUNT_ID, DOMAIN
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@ -41,8 +39,11 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
async_add_entities(
|
||||
AdaxDevice(room, adax_data_handler)
|
||||
for room in await adax_data_handler.get_rooms()
|
||||
(
|
||||
AdaxDevice(room, adax_data_handler)
|
||||
for room in await adax_data_handler.get_rooms()
|
||||
),
|
||||
True,
|
||||
)
|
||||
|
||||
|
||||
@ -58,69 +59,51 @@ class AdaxDevice(ClimateEntity):
|
||||
|
||||
def __init__(self, heater_data: dict[str, Any], adax_data_handler: Adax) -> None:
|
||||
"""Initialize the heater."""
|
||||
self._heater_data = heater_data
|
||||
self._device_id = heater_data["id"]
|
||||
self._adax_data_handler = adax_data_handler
|
||||
|
||||
self._attr_unique_id = f"{heater_data['homeId']}_{heater_data['id']}"
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""Return the name of the device, if any."""
|
||||
return self._heater_data["name"]
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> str:
|
||||
"""Return hvac operation ie. heat, cool mode."""
|
||||
if self._heater_data["heatingEnabled"]:
|
||||
return HVAC_MODE_HEAT
|
||||
return HVAC_MODE_OFF
|
||||
|
||||
@property
|
||||
def icon(self) -> str:
|
||||
"""Return nice icon for heater."""
|
||||
if self.hvac_mode == HVAC_MODE_HEAT:
|
||||
return "mdi:radiator"
|
||||
return "mdi:radiator-off"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, heater_data["id"])},
|
||||
name=self.name,
|
||||
manufacturer="Adax",
|
||||
)
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: str) -> None:
|
||||
"""Set hvac mode."""
|
||||
if hvac_mode == HVAC_MODE_HEAT:
|
||||
temperature = max(
|
||||
self.min_temp, self._heater_data.get("targetTemperature", self.min_temp)
|
||||
)
|
||||
temperature = max(self.min_temp, self.target_temperature or self.min_temp)
|
||||
await self._adax_data_handler.set_room_target_temperature(
|
||||
self._heater_data["id"], temperature, True
|
||||
self._device_id, temperature, True
|
||||
)
|
||||
elif hvac_mode == HVAC_MODE_OFF:
|
||||
await self._adax_data_handler.set_room_target_temperature(
|
||||
self._heater_data["id"], self.min_temp, False
|
||||
self._device_id, self.min_temp, False
|
||||
)
|
||||
else:
|
||||
return
|
||||
await self._adax_data_handler.update()
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> float | None:
|
||||
"""Return the current temperature."""
|
||||
return self._heater_data.get("temperature")
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> int | None:
|
||||
"""Return the temperature we try to reach."""
|
||||
return self._heater_data.get("targetTemperature")
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
temperature = kwargs.get(ATTR_TEMPERATURE)
|
||||
if temperature is None:
|
||||
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
|
||||
return
|
||||
await self._adax_data_handler.set_room_target_temperature(
|
||||
self._heater_data["id"], temperature, True
|
||||
self._device_id, temperature, True
|
||||
)
|
||||
|
||||
async def async_update(self) -> None:
|
||||
"""Get the latest data."""
|
||||
for room in await self._adax_data_handler.get_rooms():
|
||||
if room["id"] == self._heater_data["id"]:
|
||||
self._heater_data = room
|
||||
return
|
||||
if room["id"] != self._device_id:
|
||||
continue
|
||||
self._attr_name = room["name"]
|
||||
self._attr_current_temperature = room.get("temperature")
|
||||
self._attr_target_temperature = room.get("targetTemperature")
|
||||
if room["heatingEnabled"]:
|
||||
self._attr_hvac_mode = HVAC_MODE_HEAT
|
||||
self._attr_icon = "mdi:radiator"
|
||||
else:
|
||||
self._attr_hvac_mode = HVAC_MODE_OFF
|
||||
self._attr_icon = "mdi:radiator-off"
|
||||
return
|
||||
|
19
homeassistant/components/adax/translations/bg.json
Normal file
19
homeassistant/components/adax/translations/bg.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435",
|
||||
"invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "\u0425\u043e\u0441\u0442",
|
||||
"password": "\u041f\u0430\u0440\u043e\u043b\u0430"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -196,14 +196,14 @@ class AdGuardHomeDeviceEntity(AdGuardHomeEntity):
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return device information about this AdGuard Home instance."""
|
||||
return {
|
||||
"identifiers": {
|
||||
return DeviceInfo(
|
||||
entry_type="service",
|
||||
identifiers={
|
||||
(DOMAIN, self.adguard.host, self.adguard.port, self.adguard.base_path) # type: ignore
|
||||
},
|
||||
"name": "AdGuard Home",
|
||||
"manufacturer": "AdGuard Team",
|
||||
"sw_version": self.hass.data[DOMAIN][self._entry.entry_id].get(
|
||||
manufacturer="AdGuard Team",
|
||||
name="AdGuard Home",
|
||||
sw_version=self.hass.data[DOMAIN][self._entry.entry_id].get(
|
||||
DATA_ADGUARD_VERSION
|
||||
),
|
||||
"entry_type": "service",
|
||||
}
|
||||
)
|
||||
|
@ -3,6 +3,9 @@
|
||||
"abort": {
|
||||
"existing_instance_updated": "\u0410\u043a\u0442\u0443\u0430\u043b\u0438\u0437\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430\u0449\u0430\u0442\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435"
|
||||
},
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
"description": "\u0418\u0441\u043a\u0430\u0442\u0435 \u043b\u0438 \u0434\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u0442\u0435 Home Assistant \u0434\u0430 \u0441\u0435 \u0441\u0432\u044a\u0440\u0437\u0432\u0430 \u0441 AdGuard Home, \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0435\u043d \u043e\u0442 Supervisor \u0434\u043e\u0431\u0430\u0432\u043a\u0430\u0442\u0430: {addon} ?",
|
||||
@ -10,7 +13,9 @@
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "\u0425\u043e\u0441\u0442",
|
||||
"password": "\u041f\u0430\u0440\u043e\u043b\u0430",
|
||||
"port": "\u041f\u043e\u0440\u0442",
|
||||
"ssl": "AdGuard Home \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 SSL \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442",
|
||||
"username": "\u041f\u043e\u0442\u0440\u0435\u0431\u0438\u0442\u0435\u043b\u0441\u043a\u043e \u0438\u043c\u0435",
|
||||
"verify_ssl": "AdGuard Home \u0438\u0437\u043f\u043e\u043b\u0437\u0432\u0430 \u043d\u0430\u0434\u0435\u0436\u0434\u0435\u043d \u0441\u0435\u0440\u0442\u0438\u0444\u0438\u043a\u0430\u0442"
|
||||
|
@ -12,7 +12,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, Upda
|
||||
from .const import ADVANTAGE_AIR_RETRY, DOMAIN
|
||||
|
||||
ADVANTAGE_AIR_SYNC_INTERVAL = 15
|
||||
PLATFORMS = ["climate", "cover", "binary_sensor", "sensor", "switch"]
|
||||
PLATFORMS = ["binary_sensor", "climate", "cover", "select", "sensor", "switch"]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -5,6 +5,7 @@ from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASS_PROBLEM,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.const import ENTITY_CATEGORY_DIAGNOSTIC
|
||||
|
||||
from .const import DOMAIN as ADVANTAGE_AIR_DOMAIN
|
||||
from .entity import AdvantageAirEntity
|
||||
@ -34,6 +35,7 @@ class AdvantageAirZoneFilter(AdvantageAirEntity, BinarySensorEntity):
|
||||
"""Advantage Air Filter."""
|
||||
|
||||
_attr_device_class = DEVICE_CLASS_PROBLEM
|
||||
_attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC
|
||||
|
||||
def __init__(self, instance, ac_key):
|
||||
"""Initialize an Advantage Air Filter."""
|
||||
@ -65,13 +67,14 @@ class AdvantageAirZoneMotion(AdvantageAirEntity, BinarySensorEntity):
|
||||
@property
|
||||
def is_on(self):
|
||||
"""Return if motion is detect."""
|
||||
return self._zone["motion"]
|
||||
return self._zone["motion"] == 20
|
||||
|
||||
|
||||
class AdvantageAirZoneMyZone(AdvantageAirEntity, BinarySensorEntity):
|
||||
"""Advantage Air Zone MyZone."""
|
||||
|
||||
_attr_entity_registry_enabled_default = False
|
||||
_attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC
|
||||
|
||||
def __init__(self, instance, ac_key, zone_key):
|
||||
"""Initialize an Advantage Air Zone MyZone."""
|
||||
|
@ -1,5 +1,6 @@
|
||||
"""Advantage Air parent entity class."""
|
||||
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
@ -14,13 +15,13 @@ class AdvantageAirEntity(CoordinatorEntity):
|
||||
self.async_change = instance["async_change"]
|
||||
self.ac_key = ac_key
|
||||
self.zone_key = zone_key
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(DOMAIN, self.coordinator.data["system"]["rid"])},
|
||||
"name": self.coordinator.data["system"]["name"],
|
||||
"manufacturer": "Advantage Air",
|
||||
"model": self.coordinator.data["system"]["sysType"],
|
||||
"sw_version": self.coordinator.data["system"]["myAppRev"],
|
||||
}
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(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 _ac(self):
|
||||
|
53
homeassistant/components/advantage_air/select.py
Normal file
53
homeassistant/components/advantage_air/select.py
Normal file
@ -0,0 +1,53 @@
|
||||
"""Select platform for Advantage Air integration."""
|
||||
|
||||
from homeassistant.components.select import SelectEntity
|
||||
|
||||
from .const import DOMAIN as ADVANTAGE_AIR_DOMAIN
|
||||
from .entity import AdvantageAirEntity
|
||||
|
||||
ADVANTAGE_AIR_INACTIVE = "Inactive"
|
||||
|
||||
|
||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
||||
"""Set up AdvantageAir toggle platform."""
|
||||
|
||||
instance = hass.data[ADVANTAGE_AIR_DOMAIN][config_entry.entry_id]
|
||||
|
||||
entities = []
|
||||
for ac_key in instance["coordinator"].data["aircons"]:
|
||||
entities.append(AdvantageAirMyZone(instance, ac_key))
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class AdvantageAirMyZone(AdvantageAirEntity, SelectEntity):
|
||||
"""Representation of Advantage Air MyZone control."""
|
||||
|
||||
_attr_icon = "mdi:home-thermometer"
|
||||
_attr_options = [ADVANTAGE_AIR_INACTIVE]
|
||||
_number_to_name = {0: ADVANTAGE_AIR_INACTIVE}
|
||||
_name_to_number = {ADVANTAGE_AIR_INACTIVE: 0}
|
||||
|
||||
def __init__(self, instance, ac_key):
|
||||
"""Initialize an Advantage Air MyZone control."""
|
||||
super().__init__(instance, ac_key)
|
||||
self._attr_name = f'{self._ac["name"]} MyZone'
|
||||
self._attr_unique_id = (
|
||||
f'{self.coordinator.data["system"]["rid"]}-{ac_key}-myzone'
|
||||
)
|
||||
|
||||
for zone in instance["coordinator"].data["aircons"][ac_key]["zones"].values():
|
||||
if zone["type"] > 0:
|
||||
self._name_to_number[zone["name"]] = zone["number"]
|
||||
self._number_to_name[zone["number"]] = zone["name"]
|
||||
self._attr_options.append(zone["name"])
|
||||
|
||||
@property
|
||||
def current_option(self):
|
||||
"""Return the fresh air status."""
|
||||
return self._number_to_name[self._ac["myZone"]]
|
||||
|
||||
async def async_select_option(self, option):
|
||||
"""Set the MyZone."""
|
||||
await self.async_change(
|
||||
{self.ac_key: {"info": {"myZone": self._name_to_number[option]}}}
|
||||
)
|
@ -6,7 +6,7 @@ from homeassistant.components.sensor import (
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import PERCENTAGE, TEMP_CELSIUS
|
||||
from homeassistant.const import ENTITY_CATEGORY_DIAGNOSTIC, PERCENTAGE, TEMP_CELSIUS
|
||||
from homeassistant.helpers import config_validation as cv, entity_platform
|
||||
|
||||
from .const import ADVANTAGE_AIR_STATE_OPEN, DOMAIN as ADVANTAGE_AIR_DOMAIN
|
||||
@ -50,6 +50,7 @@ class AdvantageAirTimeTo(AdvantageAirEntity, SensorEntity):
|
||||
"""Representation of Advantage Air timer control."""
|
||||
|
||||
_attr_native_unit_of_measurement = ADVANTAGE_AIR_SET_COUNTDOWN_UNIT
|
||||
_attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC
|
||||
|
||||
def __init__(self, instance, ac_key, action):
|
||||
"""Initialize the Advantage Air timer control."""
|
||||
@ -84,6 +85,7 @@ class AdvantageAirZoneVent(AdvantageAirEntity, SensorEntity):
|
||||
|
||||
_attr_native_unit_of_measurement = PERCENTAGE
|
||||
_attr_state_class = STATE_CLASS_MEASUREMENT
|
||||
_attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC
|
||||
|
||||
def __init__(self, instance, ac_key, zone_key):
|
||||
"""Initialize an Advantage Air Zone Vent Sensor."""
|
||||
@ -113,6 +115,7 @@ class AdvantageAirZoneSignal(AdvantageAirEntity, SensorEntity):
|
||||
|
||||
_attr_native_unit_of_measurement = PERCENTAGE
|
||||
_attr_state_class = STATE_CLASS_MEASUREMENT
|
||||
_attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC
|
||||
|
||||
def __init__(self, instance, ac_key, zone_key):
|
||||
"""Initialize an Advantage Air Zone wireless signal sensor."""
|
||||
@ -148,6 +151,7 @@ class AdvantageAirZoneTemp(AdvantageAirEntity, SensorEntity):
|
||||
_attr_device_class = DEVICE_CLASS_TEMPERATURE
|
||||
_attr_state_class = STATE_CLASS_MEASUREMENT
|
||||
_attr_entity_registry_enabled_default = False
|
||||
_attr_entity_category = ENTITY_CATEGORY_DIAGNOSTIC
|
||||
|
||||
def __init__(self, instance, ac_key, zone_key):
|
||||
"""Initialize an Advantage Air Zone Temp Sensor."""
|
||||
|
18
homeassistant/components/advantage_air/translations/bg.json
Normal file
18
homeassistant/components/advantage_air/translations/bg.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"ip_address": "IP \u0430\u0434\u0440\u0435\u0441",
|
||||
"port": "\u041f\u043e\u0440\u0442"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -52,7 +52,7 @@ async def async_update_options(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
"""Constant values for the AEMET OpenData component."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.components.sensor import SensorEntityDescription
|
||||
from homeassistant.components.sensor import (
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.components.weather import (
|
||||
ATTR_CONDITION_CLEAR_NIGHT,
|
||||
ATTR_CONDITION_CLOUDY,
|
||||
@ -252,12 +255,14 @@ WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
name="Humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_PRESSURE,
|
||||
name="Pressure",
|
||||
native_unit_of_measurement=PRESSURE_HPA,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_RAIN,
|
||||
@ -268,6 +273,7 @@ WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
key=ATTR_API_RAIN_PROB,
|
||||
name="Rain probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_SNOW,
|
||||
@ -278,6 +284,7 @@ WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
key=ATTR_API_SNOW_PROB,
|
||||
name="Snow probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_STATION_ID,
|
||||
@ -296,18 +303,21 @@ WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
key=ATTR_API_STORM_PROB,
|
||||
name="Storm probability",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_TEMPERATURE,
|
||||
name="Temperature",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_TEMPERATURE_FEELING,
|
||||
name="Temperature feeling",
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_TOWN_ID,
|
||||
@ -326,6 +336,7 @@ WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
key=ATTR_API_WIND_BEARING,
|
||||
name="Wind bearing",
|
||||
native_unit_of_measurement=DEGREE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
key=ATTR_API_WIND_MAX_SPEED,
|
||||
@ -336,6 +347,7 @@ WEATHER_SENSOR_TYPES: tuple[SensorEntityDescription, ...] = (
|
||||
key=ATTR_API_WIND_SPEED,
|
||||
name="Wind speed",
|
||||
native_unit_of_measurement=SPEED_KILOMETERS_PER_HOUR,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Support for non-delivered packages recorded in AfterShip."""
|
||||
from __future__ import annotations
|
||||
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
from typing import Any, Final
|
||||
|
||||
@ -11,7 +12,7 @@ from homeassistant.components.sensor import (
|
||||
PLATFORM_SCHEMA as BASE_PLATFORM_SCHEMA,
|
||||
SensorEntity,
|
||||
)
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, CONF_API_KEY, CONF_NAME, HTTP_OK
|
||||
from homeassistant.const import CONF_API_KEY, CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@ -64,7 +65,7 @@ async def async_setup_platform(
|
||||
|
||||
await aftership.get_trackings()
|
||||
|
||||
if not aftership.meta or aftership.meta["code"] != HTTP_OK:
|
||||
if not aftership.meta or aftership.meta["code"] != HTTPStatus.OK:
|
||||
_LOGGER.error(
|
||||
"No tracking data found. Check API key is correct: %s", aftership.meta
|
||||
)
|
||||
@ -109,6 +110,7 @@ async def async_setup_platform(
|
||||
class AfterShipSensor(SensorEntity):
|
||||
"""Representation of a AfterShip sensor."""
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
_attr_native_unit_of_measurement: str = "packages"
|
||||
_attr_icon: str = ICON
|
||||
|
||||
@ -150,7 +152,7 @@ class AfterShipSensor(SensorEntity):
|
||||
if not self.aftership.meta:
|
||||
_LOGGER.error("Unknown errors when querying")
|
||||
return
|
||||
if self.aftership.meta["code"] != HTTP_OK:
|
||||
if self.aftership.meta["code"] != HTTPStatus.OK:
|
||||
_LOGGER.error(
|
||||
"Errors when querying AfterShip. %s", str(self.aftership.meta)
|
||||
)
|
||||
@ -191,7 +193,6 @@ class AfterShipSensor(SensorEntity):
|
||||
_LOGGER.debug("Ignoring %s as it has status: %s", name, status)
|
||||
|
||||
self._attributes = {
|
||||
ATTR_ATTRIBUTION: ATTRIBUTION,
|
||||
**status_counts,
|
||||
ATTR_TRACKINGS: trackings,
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ from homeassistant.const import (
|
||||
STATE_ALARM_ARMED_NIGHT,
|
||||
STATE_ALARM_DISARMED,
|
||||
)
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
|
||||
from .const import CONNECTION, DOMAIN as AGENT_DOMAIN
|
||||
|
||||
@ -45,12 +46,12 @@ class AgentBaseStation(AlarmControlPanelEntity):
|
||||
self._client = client
|
||||
self._attr_name = f"{client.name} {CONST_ALARM_CONTROL_PANEL_NAME}"
|
||||
self._attr_unique_id = f"{client.unique}_CP"
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(AGENT_DOMAIN, client.unique)},
|
||||
"manufacturer": "Agent",
|
||||
"model": CONST_ALARM_CONTROL_PANEL_NAME,
|
||||
"sw_version": client.version,
|
||||
}
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(AGENT_DOMAIN, client.unique)},
|
||||
manufacturer="Agent",
|
||||
model=CONST_ALARM_CONTROL_PANEL_NAME,
|
||||
sw_version=client.version,
|
||||
)
|
||||
|
||||
async def async_update(self):
|
||||
"""Update the state of the device."""
|
||||
|
@ -13,6 +13,7 @@ from homeassistant.components.mjpeg.camera import (
|
||||
)
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, CONF_NAME
|
||||
from homeassistant.helpers import entity_platform
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
|
||||
from .const import (
|
||||
ATTRIBUTION,
|
||||
@ -79,13 +80,13 @@ class AgentCamera(MjpegCamera):
|
||||
self._attr_name = f"{device.client.name} {device.name}"
|
||||
self._attr_unique_id = f"{device._client.unique}_{device.typeID}_{device.id}"
|
||||
super().__init__(device_info)
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(AGENT_DOMAIN, self.unique_id)},
|
||||
"name": self.name,
|
||||
"manufacturer": "Agent",
|
||||
"model": "Camera",
|
||||
"sw_version": device.client.version,
|
||||
}
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(AGENT_DOMAIN, self.unique_id)},
|
||||
manufacturer="Agent",
|
||||
model="Camera",
|
||||
name=self.name,
|
||||
sw_version=device.client.version,
|
||||
)
|
||||
|
||||
async def async_update(self):
|
||||
"""Update our state from the Agent API."""
|
||||
|
@ -33,9 +33,7 @@ class AgentFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
|
||||
try:
|
||||
await agent_client.update()
|
||||
except AgentConnectionError:
|
||||
pass
|
||||
except AgentError:
|
||||
except (AgentConnectionError, AgentError):
|
||||
pass
|
||||
|
||||
await agent_client.close()
|
||||
|
15
homeassistant/components/agent_dvr/translations/bg.json
Normal file
15
homeassistant/components/agent_dvr/translations/bg.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "\u0425\u043e\u0441\u0442",
|
||||
"port": "\u041f\u043e\u0440\u0442"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@
|
||||
"already_configured": "Az eszk\u00f6z m\u00e1r konfigur\u00e1lva van"
|
||||
},
|
||||
"error": {
|
||||
"already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van.",
|
||||
"already_in_progress": "A konfigur\u00e1l\u00e1s m\u00e1r folyamatban van",
|
||||
"cannot_connect": "Sikertelen csatlakoz\u00e1s"
|
||||
},
|
||||
"step": {
|
||||
|
@ -6,10 +6,7 @@ import logging
|
||||
from typing import Final, final
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_ATTRIBUTION,
|
||||
CONCENTRATION_MICROGRAMS_PER_CUBIC_METER,
|
||||
)
|
||||
from homeassistant.const import CONCENTRATION_MICROGRAMS_PER_CUBIC_METER
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.config_validation import ( # noqa: F401
|
||||
PLATFORM_SCHEMA,
|
||||
@ -41,7 +38,6 @@ SCAN_INTERVAL: Final = timedelta(seconds=30)
|
||||
|
||||
PROP_TO_ATTR: Final[dict[str, str]] = {
|
||||
"air_quality_index": ATTR_AQI,
|
||||
"attribution": ATTR_ATTRIBUTION,
|
||||
"carbon_dioxide": ATTR_CO2,
|
||||
"carbon_monoxide": ATTR_CO,
|
||||
"nitrogen_oxide": ATTR_N2O,
|
||||
@ -114,11 +110,6 @@ class AirQualityEntity(Entity):
|
||||
"""Return the CO2 (carbon dioxide) level."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def attribution(self) -> StateType:
|
||||
"""Return the attribution."""
|
||||
return None
|
||||
|
||||
@property
|
||||
def sulphur_dioxide(self) -> StateType:
|
||||
"""Return the SO2 (sulphur dioxide) level."""
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Adds config flow for Airly."""
|
||||
from __future__ import annotations
|
||||
|
||||
from http import HTTPStatus
|
||||
from typing import Any
|
||||
|
||||
from aiohttp import ClientSession
|
||||
@ -10,14 +11,7 @@ import async_timeout
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.const import (
|
||||
CONF_API_KEY,
|
||||
CONF_LATITUDE,
|
||||
CONF_LONGITUDE,
|
||||
CONF_NAME,
|
||||
HTTP_NOT_FOUND,
|
||||
HTTP_UNAUTHORIZED,
|
||||
)
|
||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE, CONF_NAME
|
||||
from homeassistant.data_entry_flow import FlowResult
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
@ -60,9 +54,9 @@ class AirlyFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
use_nearest=True,
|
||||
)
|
||||
except AirlyError as err:
|
||||
if err.status_code == HTTP_UNAUTHORIZED:
|
||||
if err.status_code == HTTPStatus.UNAUTHORIZED:
|
||||
errors["base"] = "invalid_api_key"
|
||||
if err.status_code == HTTP_NOT_FOUND:
|
||||
if err.status_code == HTTPStatus.NOT_FOUND:
|
||||
errors["base"] = "wrong_location"
|
||||
else:
|
||||
if not location_point_valid:
|
||||
|
@ -32,3 +32,4 @@ MANUFACTURER: Final = "Airly sp. z o.o."
|
||||
MAX_UPDATE_INTERVAL: Final = 90
|
||||
MIN_UPDATE_INTERVAL: Final = 5
|
||||
NO_AIRLY_SENSORS: Final = "There are no Airly sensors in this area yet."
|
||||
URL = "https://airly.org/map/#{latitude},{longitude}"
|
||||
|
@ -27,6 +27,7 @@ from homeassistant.const import (
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
@ -54,6 +55,7 @@ from .const import (
|
||||
MANUFACTURER,
|
||||
SUFFIX_LIMIT,
|
||||
SUFFIX_PERCENT,
|
||||
URL,
|
||||
)
|
||||
|
||||
PARALLEL_UPDATES = 1
|
||||
@ -151,14 +153,15 @@ class AirlySensor(CoordinatorEntity, SensorEntity):
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
self._attr_device_info = {
|
||||
"identifiers": {
|
||||
(DOMAIN, f"{coordinator.latitude}-{coordinator.longitude}")
|
||||
},
|
||||
"name": DEFAULT_NAME,
|
||||
"manufacturer": MANUFACTURER,
|
||||
"entry_type": "service",
|
||||
}
|
||||
self._attr_device_info = DeviceInfo(
|
||||
entry_type="service",
|
||||
identifiers={(DOMAIN, f"{coordinator.latitude}-{coordinator.longitude}")},
|
||||
manufacturer=MANUFACTURER,
|
||||
name=DEFAULT_NAME,
|
||||
configuration_url=URL.format(
|
||||
latitude=coordinator.latitude, longitude=coordinator.longitude
|
||||
),
|
||||
)
|
||||
self._attr_name = f"{name} {description.name}"
|
||||
self._attr_unique_id = (
|
||||
f"{coordinator.latitude}-{coordinator.longitude}-{description.key}".lower()
|
||||
|
@ -64,7 +64,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
|
@ -1,7 +1,22 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435",
|
||||
"invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435",
|
||||
"invalid_location": "\u041d\u044f\u043c\u0430 \u043d\u0430\u043c\u0435\u0440\u0435\u043d\u0438 \u0440\u0435\u0437\u0443\u043b\u0442\u0430\u0442\u0438 \u0437\u0430 \u0442\u043e\u0432\u0430 \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435",
|
||||
"unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API \u043a\u043b\u044e\u0447",
|
||||
"latitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0448\u0438\u0440\u0438\u043d\u0430",
|
||||
"longitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0434\u044a\u043b\u0436\u0438\u043d\u0430"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,12 +22,14 @@ from homeassistant.const import (
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
PERCENTAGE,
|
||||
PRESSURE_MBAR,
|
||||
SIGNAL_STRENGTH_DECIBELS,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
CoordinatorEntity,
|
||||
@ -64,6 +66,7 @@ SENSORS: dict[str, SensorEntityDescription] = {
|
||||
key="battery",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
name="Battery",
|
||||
),
|
||||
"co2": SensorEntityDescription(
|
||||
@ -96,6 +99,7 @@ SENSORS: dict[str, SensorEntityDescription] = {
|
||||
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
name="RSSI",
|
||||
entity_registry_enabled_default=False,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
"pm1": SensorEntityDescription(
|
||||
key="pm1",
|
||||
@ -152,11 +156,12 @@ class AirthingsHeaterEnergySensor(CoordinatorEntity, SensorEntity):
|
||||
self._attr_name = f"{airthings_device.name} {entity_description.name}"
|
||||
self._attr_unique_id = f"{airthings_device.device_id}_{entity_description.key}"
|
||||
self._id = airthings_device.device_id
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(DOMAIN, airthings_device.device_id)},
|
||||
"name": airthings_device.name,
|
||||
"manufacturer": "Airthings",
|
||||
}
|
||||
self._attr_device_info = DeviceInfo(
|
||||
configuration_url="https://dashboard.airthings.com/",
|
||||
identifiers={(DOMAIN, airthings_device.device_id)},
|
||||
name=airthings_device.name,
|
||||
manufacturer="Airthings",
|
||||
)
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
|
19
homeassistant/components/airthings/translations/bg.json
Normal file
19
homeassistant/components/airthings/translations/bg.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435",
|
||||
"invalid_auth": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u043d\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435",
|
||||
"unknown": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
19
homeassistant/components/airthings/translations/cs.json
Normal file
19
homeassistant/components/airthings/translations/cs.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u00da\u010det je ji\u017e nastaven"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Nepoda\u0159ilo se p\u0159ipojit",
|
||||
"invalid_auth": "Neplatn\u00e9 ov\u011b\u0159en\u00ed",
|
||||
"unknown": "Neo\u010dek\u00e1van\u00e1 chyba"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
20
homeassistant/components/airthings/translations/pl.json
Normal file
20
homeassistant/components/airthings/translations/pl.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Konto jest ju\u017c skonfigurowane"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Nie mo\u017cna nawi\u0105za\u0107 po\u0142\u0105czenia",
|
||||
"invalid_auth": "Niepoprawne uwierzytelnienie",
|
||||
"unknown": "Nieoczekiwany b\u0142\u0105d"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "ID",
|
||||
"secret": "Sekret"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ from homeassistant.components.climate.const import (
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, TEMP_CELSIUS
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
@ -96,14 +97,14 @@ class AirtouchAC(CoordinatorEntity, ClimateEntity):
|
||||
return super()._handle_coordinator_update()
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return device info for this device."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.unique_id)},
|
||||
"name": self.name,
|
||||
"manufacturer": "Airtouch",
|
||||
"model": "Airtouch 4",
|
||||
}
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self.unique_id)},
|
||||
name=self.name,
|
||||
manufacturer="Airtouch",
|
||||
model="Airtouch 4",
|
||||
)
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
@ -211,14 +212,14 @@ class AirtouchGroup(CoordinatorEntity, ClimateEntity):
|
||||
return super()._handle_coordinator_update()
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return device info for this device."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.unique_id)},
|
||||
"name": self.name,
|
||||
"manufacturer": "Airtouch",
|
||||
"model": "Airtouch 4",
|
||||
}
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self.unique_id)},
|
||||
manufacturer="Airtouch",
|
||||
model="Airtouch 4",
|
||||
name=self.name,
|
||||
)
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
|
17
homeassistant/components/airtouch4/translations/bg.json
Normal file
17
homeassistant/components/airtouch4/translations/bg.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "\u0425\u043e\u0441\u0442"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
from collections.abc import Mapping
|
||||
from datetime import timedelta
|
||||
from math import ceil
|
||||
from typing import Any
|
||||
from typing import Any, Dict, cast
|
||||
|
||||
from pyairvisual import CloudAPI, NodeSamba
|
||||
from pyairvisual.errors import (
|
||||
@ -54,8 +54,6 @@ from .const import (
|
||||
|
||||
PLATFORMS = ["sensor"]
|
||||
|
||||
DATA_LISTENER = "listener"
|
||||
|
||||
DEFAULT_ATTRIBUTION = "Data provided by AirVisual"
|
||||
DEFAULT_NODE_PRO_UPDATE_INTERVAL = timedelta(minutes=1)
|
||||
|
||||
@ -106,12 +104,12 @@ def async_get_cloud_coordinators_by_api_key(
|
||||
hass: HomeAssistant, api_key: str
|
||||
) -> list[DataUpdateCoordinator]:
|
||||
"""Get all DataUpdateCoordinator objects related to a particular API key."""
|
||||
coordinators = []
|
||||
for entry_id, coordinator in hass.data[DOMAIN][DATA_COORDINATOR].items():
|
||||
config_entry = hass.config_entries.async_get_entry(entry_id)
|
||||
if config_entry and config_entry.data.get(CONF_API_KEY) == api_key:
|
||||
coordinators.append(coordinator)
|
||||
return coordinators
|
||||
return [
|
||||
attrs[DATA_COORDINATOR]
|
||||
for entry_id, attrs in hass.data[DOMAIN].items()
|
||||
if (entry := hass.config_entries.async_get_entry(entry_id))
|
||||
and entry.data.get(CONF_API_KEY) == api_key
|
||||
]
|
||||
|
||||
|
||||
@callback
|
||||
@ -139,25 +137,25 @@ def async_sync_geo_coordinator_update_intervals(
|
||||
|
||||
@callback
|
||||
def _standardize_geography_config_entry(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
hass: HomeAssistant, entry: ConfigEntry
|
||||
) -> None:
|
||||
"""Ensure that geography config entries have appropriate properties."""
|
||||
entry_updates = {}
|
||||
|
||||
if not config_entry.unique_id:
|
||||
if not entry.unique_id:
|
||||
# If the config entry doesn't already have a unique ID, set one:
|
||||
entry_updates["unique_id"] = config_entry.data[CONF_API_KEY]
|
||||
if not config_entry.options:
|
||||
entry_updates["unique_id"] = entry.data[CONF_API_KEY]
|
||||
if not entry.options:
|
||||
# If the config entry doesn't already have any options set, set defaults:
|
||||
entry_updates["options"] = {CONF_SHOW_ON_MAP: True}
|
||||
if config_entry.data.get(CONF_INTEGRATION_TYPE) not in [
|
||||
if entry.data.get(CONF_INTEGRATION_TYPE) not in [
|
||||
INTEGRATION_TYPE_GEOGRAPHY_COORDS,
|
||||
INTEGRATION_TYPE_GEOGRAPHY_NAME,
|
||||
]:
|
||||
# If the config entry data doesn't contain an integration type that we know
|
||||
# about, infer it from the data we have:
|
||||
entry_updates["data"] = {**config_entry.data}
|
||||
if CONF_CITY in config_entry.data:
|
||||
entry_updates["data"] = {**entry.data}
|
||||
if CONF_CITY in entry.data:
|
||||
entry_updates["data"][
|
||||
CONF_INTEGRATION_TYPE
|
||||
] = INTEGRATION_TYPE_GEOGRAPHY_NAME
|
||||
@ -169,55 +167,55 @@ def _standardize_geography_config_entry(
|
||||
if not entry_updates:
|
||||
return
|
||||
|
||||
hass.config_entries.async_update_entry(config_entry, **entry_updates)
|
||||
hass.config_entries.async_update_entry(entry, **entry_updates)
|
||||
|
||||
|
||||
@callback
|
||||
def _standardize_node_pro_config_entry(
|
||||
hass: HomeAssistant, config_entry: ConfigEntry
|
||||
) -> None:
|
||||
def _standardize_node_pro_config_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Ensure that Node/Pro config entries have appropriate properties."""
|
||||
entry_updates: dict[str, Any] = {}
|
||||
|
||||
if CONF_INTEGRATION_TYPE not in config_entry.data:
|
||||
if CONF_INTEGRATION_TYPE not in entry.data:
|
||||
# If the config entry data doesn't contain the integration type, add it:
|
||||
entry_updates["data"] = {
|
||||
**config_entry.data,
|
||||
**entry.data,
|
||||
CONF_INTEGRATION_TYPE: INTEGRATION_TYPE_NODE_PRO,
|
||||
}
|
||||
|
||||
if not entry_updates:
|
||||
return
|
||||
|
||||
hass.config_entries.async_update_entry(config_entry, **entry_updates)
|
||||
hass.config_entries.async_update_entry(entry, **entry_updates)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up AirVisual as config entry."""
|
||||
hass.data.setdefault(DOMAIN, {DATA_COORDINATOR: {}, DATA_LISTENER: {}})
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = {}
|
||||
|
||||
if CONF_API_KEY in config_entry.data:
|
||||
_standardize_geography_config_entry(hass, config_entry)
|
||||
if CONF_API_KEY in entry.data:
|
||||
_standardize_geography_config_entry(hass, entry)
|
||||
|
||||
websession = aiohttp_client.async_get_clientsession(hass)
|
||||
cloud_api = CloudAPI(config_entry.data[CONF_API_KEY], session=websession)
|
||||
cloud_api = CloudAPI(entry.data[CONF_API_KEY], session=websession)
|
||||
|
||||
async def async_update_data() -> dict[str, Any]:
|
||||
"""Get new data from the API."""
|
||||
if CONF_CITY in config_entry.data:
|
||||
if CONF_CITY in entry.data:
|
||||
api_coro = cloud_api.air_quality.city(
|
||||
config_entry.data[CONF_CITY],
|
||||
config_entry.data[CONF_STATE],
|
||||
config_entry.data[CONF_COUNTRY],
|
||||
entry.data[CONF_CITY],
|
||||
entry.data[CONF_STATE],
|
||||
entry.data[CONF_COUNTRY],
|
||||
)
|
||||
else:
|
||||
api_coro = cloud_api.air_quality.nearest_city(
|
||||
config_entry.data[CONF_LATITUDE],
|
||||
config_entry.data[CONF_LONGITUDE],
|
||||
entry.data[CONF_LATITUDE],
|
||||
entry.data[CONF_LONGITUDE],
|
||||
)
|
||||
|
||||
try:
|
||||
return await api_coro
|
||||
data = await api_coro
|
||||
return cast(Dict[str, Any], data)
|
||||
except (InvalidKeyError, KeyExpiredError) as ex:
|
||||
raise ConfigEntryAuthFailed from ex
|
||||
except AirVisualError as err:
|
||||
@ -226,7 +224,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
coordinator = DataUpdateCoordinator(
|
||||
hass,
|
||||
LOGGER,
|
||||
name=async_get_geography_id(config_entry.data),
|
||||
name=async_get_geography_id(entry.data),
|
||||
# We give a placeholder update interval in order to create the coordinator;
|
||||
# then, below, we use the coordinator's presence (along with any other
|
||||
# coordinators using the same API key) to calculate an actual, leveled
|
||||
@ -236,16 +234,14 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
)
|
||||
|
||||
# Only geography-based entries have options:
|
||||
hass.data[DOMAIN][DATA_LISTENER][
|
||||
config_entry.entry_id
|
||||
] = config_entry.add_update_listener(async_reload_entry)
|
||||
entry.async_on_unload(entry.add_update_listener(async_reload_entry))
|
||||
else:
|
||||
# Remove outdated air_quality entities from the entity registry if they exist:
|
||||
ent_reg = entity_registry.async_get(hass)
|
||||
for entity_entry in [
|
||||
e
|
||||
for e in ent_reg.entities.values()
|
||||
if e.config_entry_id == config_entry.entry_id
|
||||
if e.config_entry_id == entry.entry_id
|
||||
and e.entity_id.startswith("air_quality")
|
||||
]:
|
||||
LOGGER.debug(
|
||||
@ -253,15 +249,16 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
)
|
||||
ent_reg.async_remove(entity_entry.entity_id)
|
||||
|
||||
_standardize_node_pro_config_entry(hass, config_entry)
|
||||
_standardize_node_pro_config_entry(hass, entry)
|
||||
|
||||
async def async_update_data() -> dict[str, Any]:
|
||||
"""Get new data from the API."""
|
||||
try:
|
||||
async with NodeSamba(
|
||||
config_entry.data[CONF_IP_ADDRESS], config_entry.data[CONF_PASSWORD]
|
||||
entry.data[CONF_IP_ADDRESS], entry.data[CONF_PASSWORD]
|
||||
) as node:
|
||||
return await node.async_get_latest_measurements()
|
||||
data = await node.async_get_latest_measurements()
|
||||
return cast(Dict[str, Any], data)
|
||||
except NodeProError as err:
|
||||
raise UpdateFailed(f"Error while retrieving data: {err}") from err
|
||||
|
||||
@ -274,41 +271,38 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
)
|
||||
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
|
||||
hass.data[DOMAIN][DATA_COORDINATOR][config_entry.entry_id] = coordinator
|
||||
hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR] = coordinator
|
||||
|
||||
# Reassess the interval between 2 server requests
|
||||
if CONF_API_KEY in config_entry.data:
|
||||
async_sync_geo_coordinator_update_intervals(
|
||||
hass, config_entry.data[CONF_API_KEY]
|
||||
)
|
||||
if CONF_API_KEY in entry.data:
|
||||
async_sync_geo_coordinator_update_intervals(hass, entry.data[CONF_API_KEY])
|
||||
|
||||
hass.config_entries.async_setup_platforms(config_entry, PLATFORMS)
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Migrate an old config entry."""
|
||||
version = config_entry.version
|
||||
version = entry.version
|
||||
|
||||
LOGGER.debug("Migrating from version %s", version)
|
||||
|
||||
# 1 -> 2: One geography per config entry
|
||||
if version == 1:
|
||||
version = config_entry.version = 2
|
||||
version = entry.version = 2
|
||||
|
||||
# Update the config entry to only include the first geography (there is always
|
||||
# guaranteed to be at least one):
|
||||
geographies = list(config_entry.data[CONF_GEOGRAPHIES])
|
||||
geographies = list(entry.data[CONF_GEOGRAPHIES])
|
||||
first_geography = geographies.pop(0)
|
||||
first_id = async_get_geography_id(first_geography)
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry,
|
||||
entry,
|
||||
unique_id=first_id,
|
||||
title=f"Cloud API ({first_id})",
|
||||
data={CONF_API_KEY: config_entry.data[CONF_API_KEY], **first_geography},
|
||||
data={CONF_API_KEY: entry.data[CONF_API_KEY], **first_geography},
|
||||
)
|
||||
|
||||
# For any geographies that remain, create a new config entry for each one:
|
||||
@ -321,7 +315,7 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": source},
|
||||
data={CONF_API_KEY: config_entry.data[CONF_API_KEY], **geography},
|
||||
data={CONF_API_KEY: entry.data[CONF_API_KEY], **geography},
|
||||
)
|
||||
)
|
||||
|
||||
@ -330,42 +324,39 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) ->
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload an AirVisual config entry."""
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(
|
||||
config_entry, PLATFORMS
|
||||
)
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
||||
if unload_ok:
|
||||
hass.data[DOMAIN][DATA_COORDINATOR].pop(config_entry.entry_id)
|
||||
remove_listener = hass.data[DOMAIN][DATA_LISTENER].pop(config_entry.entry_id)
|
||||
remove_listener()
|
||||
|
||||
if CONF_API_KEY in config_entry.data:
|
||||
hass.data[DOMAIN].pop(entry.entry_id)
|
||||
if CONF_API_KEY in entry.data:
|
||||
# Re-calculate the update interval period for any remaining consumers of
|
||||
# this API key:
|
||||
async_sync_geo_coordinator_update_intervals(
|
||||
hass, config_entry.data[CONF_API_KEY]
|
||||
)
|
||||
async_sync_geo_coordinator_update_intervals(hass, entry.data[CONF_API_KEY])
|
||||
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def async_reload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Handle an options update."""
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
class AirVisualEntity(CoordinatorEntity):
|
||||
"""Define a generic AirVisual entity."""
|
||||
|
||||
def __init__(
|
||||
self, coordinator: DataUpdateCoordinator, description: EntityDescription
|
||||
self,
|
||||
coordinator: DataUpdateCoordinator,
|
||||
entry: ConfigEntry,
|
||||
description: EntityDescription,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator)
|
||||
|
||||
self._attr_extra_state_attributes = {ATTR_ATTRIBUTION: DEFAULT_ATTRIBUTION}
|
||||
self._entry = entry
|
||||
self.entity_description = description
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
|
@ -262,9 +262,9 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
class AirVisualOptionsFlowHandler(config_entries.OptionsFlow):
|
||||
"""Handle an AirVisual options flow."""
|
||||
|
||||
def __init__(self, config_entry: ConfigEntry) -> None:
|
||||
def __init__(self, entry: ConfigEntry) -> None:
|
||||
"""Initialize."""
|
||||
self.config_entry = config_entry
|
||||
self.entry = entry
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, str] | None = None
|
||||
@ -279,7 +279,7 @@ class AirVisualOptionsFlowHandler(config_entries.OptionsFlow):
|
||||
{
|
||||
vol.Required(
|
||||
CONF_SHOW_ON_MAP,
|
||||
default=self.config_entry.options.get(CONF_SHOW_ON_MAP),
|
||||
default=self.entry.options.get(CONF_SHOW_ON_MAP),
|
||||
): bool
|
||||
}
|
||||
),
|
||||
|
@ -27,6 +27,7 @@ from homeassistant.const import (
|
||||
DEVICE_CLASS_PM25,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
PERCENTAGE,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
@ -103,6 +104,7 @@ NODE_PRO_SENSOR_DESCRIPTIONS = (
|
||||
key=SENSOR_KIND_BATTERY_LEVEL,
|
||||
name="Battery",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
),
|
||||
SensorEntityDescription(
|
||||
@ -189,26 +191,24 @@ POLLUTANT_UNITS = {
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up AirVisual sensors based on a config entry."""
|
||||
coordinator = hass.data[DOMAIN][DATA_COORDINATOR][config_entry.entry_id]
|
||||
coordinator = hass.data[DOMAIN][entry.entry_id][DATA_COORDINATOR]
|
||||
|
||||
sensors: list[AirVisualGeographySensor | AirVisualNodeProSensor]
|
||||
if config_entry.data[CONF_INTEGRATION_TYPE] in (
|
||||
if entry.data[CONF_INTEGRATION_TYPE] in (
|
||||
INTEGRATION_TYPE_GEOGRAPHY_COORDS,
|
||||
INTEGRATION_TYPE_GEOGRAPHY_NAME,
|
||||
):
|
||||
sensors = [
|
||||
AirVisualGeographySensor(coordinator, config_entry, description, locale)
|
||||
AirVisualGeographySensor(coordinator, entry, description, locale)
|
||||
for locale in GEOGRAPHY_SENSOR_LOCALES
|
||||
for description in GEOGRAPHY_SENSOR_DESCRIPTIONS
|
||||
]
|
||||
else:
|
||||
sensors = [
|
||||
AirVisualNodeProSensor(coordinator, description)
|
||||
AirVisualNodeProSensor(coordinator, entry, description)
|
||||
for description in NODE_PRO_SENSOR_DESCRIPTIONS
|
||||
]
|
||||
|
||||
@ -221,23 +221,22 @@ class AirVisualGeographySensor(AirVisualEntity, SensorEntity):
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: DataUpdateCoordinator,
|
||||
config_entry: ConfigEntry,
|
||||
entry: ConfigEntry,
|
||||
description: SensorEntityDescription,
|
||||
locale: str,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator, description)
|
||||
super().__init__(coordinator, entry, description)
|
||||
|
||||
self._attr_extra_state_attributes.update(
|
||||
{
|
||||
ATTR_CITY: config_entry.data.get(CONF_CITY),
|
||||
ATTR_STATE: config_entry.data.get(CONF_STATE),
|
||||
ATTR_COUNTRY: config_entry.data.get(CONF_COUNTRY),
|
||||
ATTR_CITY: entry.data.get(CONF_CITY),
|
||||
ATTR_STATE: entry.data.get(CONF_STATE),
|
||||
ATTR_COUNTRY: entry.data.get(CONF_COUNTRY),
|
||||
}
|
||||
)
|
||||
self._attr_name = f"{GEOGRAPHY_SENSOR_LOCALES[locale]} {description.name}"
|
||||
self._attr_unique_id = f"{config_entry.unique_id}_{locale}_{description.key}"
|
||||
self._config_entry = config_entry
|
||||
self._attr_unique_id = f"{entry.unique_id}_{locale}_{description.key}"
|
||||
self._locale = locale
|
||||
|
||||
@property
|
||||
@ -279,16 +278,16 @@ class AirVisualGeographySensor(AirVisualEntity, SensorEntity):
|
||||
#
|
||||
# We use any coordinates in the config entry and, in the case of a geography by
|
||||
# name, we fall back to the latitude longitude provided in the coordinator data:
|
||||
latitude = self._config_entry.data.get(
|
||||
latitude = self._entry.data.get(
|
||||
CONF_LATITUDE,
|
||||
self.coordinator.data["location"]["coordinates"][1],
|
||||
)
|
||||
longitude = self._config_entry.data.get(
|
||||
longitude = self._entry.data.get(
|
||||
CONF_LONGITUDE,
|
||||
self.coordinator.data["location"]["coordinates"][0],
|
||||
)
|
||||
|
||||
if self._config_entry.options[CONF_SHOW_ON_MAP]:
|
||||
if self._entry.options[CONF_SHOW_ON_MAP]:
|
||||
self._attr_extra_state_attributes[ATTR_LATITUDE] = latitude
|
||||
self._attr_extra_state_attributes[ATTR_LONGITUDE] = longitude
|
||||
self._attr_extra_state_attributes.pop("lati", None)
|
||||
@ -304,10 +303,13 @@ class AirVisualNodeProSensor(AirVisualEntity, SensorEntity):
|
||||
"""Define an AirVisual sensor related to a Node/Pro unit."""
|
||||
|
||||
def __init__(
|
||||
self, coordinator: DataUpdateCoordinator, description: SensorEntityDescription
|
||||
self,
|
||||
coordinator: DataUpdateCoordinator,
|
||||
entry: ConfigEntry,
|
||||
description: SensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
super().__init__(coordinator, description)
|
||||
super().__init__(coordinator, entry, description)
|
||||
|
||||
self._attr_name = (
|
||||
f"{coordinator.data['settings']['node_name']} Node/Pro: {description.name}"
|
||||
@ -317,16 +319,16 @@ class AirVisualNodeProSensor(AirVisualEntity, SensorEntity):
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
"""Return device registry information for this entity."""
|
||||
return {
|
||||
"identifiers": {(DOMAIN, self.coordinator.data["serial_number"])},
|
||||
"name": self.coordinator.data["settings"]["node_name"],
|
||||
"manufacturer": "AirVisual",
|
||||
"model": f'{self.coordinator.data["status"]["model"]}',
|
||||
"sw_version": (
|
||||
return DeviceInfo(
|
||||
identifiers={(DOMAIN, self.coordinator.data["serial_number"])},
|
||||
manufacturer="AirVisual",
|
||||
model=f'{self.coordinator.data["status"]["model"]}',
|
||||
name=self.coordinator.data["settings"]["node_name"],
|
||||
sw_version=(
|
||||
f'Version {self.coordinator.data["status"]["system_version"]}'
|
||||
f'{self.coordinator.data["status"]["app_version"]}'
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@callback
|
||||
def update_from_latest_data(self) -> None:
|
||||
|
@ -1,11 +1,30 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435",
|
||||
"general_error": "\u041d\u0435\u043e\u0447\u0430\u043a\u0432\u0430\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430",
|
||||
"invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447"
|
||||
},
|
||||
"step": {
|
||||
"geography_by_name": {
|
||||
"data": {
|
||||
"city": "\u0413\u0440\u0430\u0434",
|
||||
"country": "\u0421\u0442\u0440\u0430\u043d\u0430"
|
||||
}
|
||||
},
|
||||
"node_pro": {
|
||||
"data": {
|
||||
"ip_address": "\u0425\u043e\u0441\u0442",
|
||||
"password": "\u041f\u0430\u0440\u043e\u043b\u0430"
|
||||
}
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "API \u043a\u043b\u044e\u0447"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "A hely m\u00e1r konfigur\u00e1lva van vagy a Node/Pro azonos\u00edt\u00f3 m\u00e1r regisztr\u00e1lva van.",
|
||||
"reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt"
|
||||
"reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Sikertelen csatlakoz\u00e1s",
|
||||
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"state": {
|
||||
"airvisual__pollutant_label": {
|
||||
"o3": "\u041e\u0437\u043e\u043d",
|
||||
"p1": "PM10",
|
||||
"p2": "PM2.5"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"state": {
|
||||
"airvisual__pollutant_label": {
|
||||
"co": "\u4e00\u6c27\u5316\u78b3",
|
||||
"n2": "\u4e8c\u6c27\u5316\u6c2e",
|
||||
"o3": "\u81ed\u6c27",
|
||||
"p1": "PM10",
|
||||
"p2": "PM2.5",
|
||||
"s2": "\u4e8c\u6c27\u5316\u786b"
|
||||
},
|
||||
"airvisual__pollutant_level": {
|
||||
"good": "\u826f\u597d",
|
||||
"hazardous": "\u5371\u5bb3\u5065\u5eb7",
|
||||
"moderate": "\u4e2d\u7b49",
|
||||
"unhealthy": "\u4e0d\u5229\u4e8e\u5065\u5eb7",
|
||||
"unhealthy_sensitive": "\u4e0d\u5229\u4e8e\u654f\u611f\u4eba\u7fa4",
|
||||
"very_unhealthy": "\u975e\u5e38\u4e0d\u5229\u4e8e\u5065\u5eb7"
|
||||
}
|
||||
}
|
||||
}
|
@ -48,9 +48,7 @@ async def _async_reproduce_state(
|
||||
reproduce_options: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
"""Reproduce a single state."""
|
||||
cur_state = hass.states.get(state.entity_id)
|
||||
|
||||
if cur_state is None:
|
||||
if (cur_state := hass.states.get(state.entity_id)) is None:
|
||||
_LOGGER.warning("Unable to find entity %s", state.entity_id)
|
||||
return
|
||||
|
||||
|
@ -1,4 +1,30 @@
|
||||
{
|
||||
"device_automation": {
|
||||
"action_type": {
|
||||
"arm_away": "\u05d3\u05e8\u05d9\u05db\u05ea {entity_name} \u05dc\u05d0 \u05d1\u05d1\u05d9\u05ea",
|
||||
"arm_home": "\u05d3\u05e8\u05d9\u05db\u05ea {entity_name} \u05d4\u05d1\u05d9\u05ea\u05d4",
|
||||
"arm_night": "\u05d3\u05e8\u05d9\u05db\u05ea {entity_name} \u05dc\u05d9\u05dc\u05d4",
|
||||
"arm_vacation": "\u05d3\u05e8\u05d9\u05db\u05ea {entity_name} \u05d7\u05d5\u05e4\u05e9\u05d4",
|
||||
"disarm": "\u05e0\u05d9\u05d8\u05e8\u05d5\u05dc {entity_name}",
|
||||
"trigger": "\u05d4\u05e4\u05e2\u05dc\u05ea {entity_name}"
|
||||
},
|
||||
"condition_type": {
|
||||
"is_armed_away": "{entity_name} \u05d3\u05e8\u05d5\u05da \u05dc\u05d0 \u05d1\u05d1\u05d9\u05ea",
|
||||
"is_armed_home": "{entity_name} \u05d3\u05e8\u05d5\u05da \u05d1\u05d9\u05ea",
|
||||
"is_armed_night": "{entity_name} \u05d3\u05e8\u05d5\u05da \u05dc\u05d9\u05dc\u05d4",
|
||||
"is_armed_vacation": "{entity_name} \u05d1\u05d7\u05d5\u05e4\u05e9\u05d4 \u05d3\u05e8\u05d5\u05db\u05d4",
|
||||
"is_disarmed": "{entity_name} \u05de\u05e0\u05d5\u05d8\u05e8\u05dc",
|
||||
"is_triggered": "{entity_name} \u05de\u05d5\u05e4\u05e2\u05dc"
|
||||
},
|
||||
"trigger_type": {
|
||||
"armed_away": "{entity_name} \u05d3\u05e8\u05d5\u05da \u05dc\u05d0 \u05d1\u05d1\u05d9\u05ea",
|
||||
"armed_home": "{entity_name} \u05d3\u05e8\u05d5\u05da \u05d1\u05d1\u05d9\u05ea",
|
||||
"armed_night": "{entity_name} \u05d3\u05e8\u05d5\u05da \u05dc\u05d9\u05dc\u05d4",
|
||||
"armed_vacation": "{entity_name} \u05d7\u05d5\u05e4\u05e9\u05d4 \u05d3\u05e8\u05d5\u05db\u05d4",
|
||||
"disarmed": "{entity_name} \u05de\u05e0\u05d5\u05d8\u05e8\u05dc",
|
||||
"triggered": "{entity_name} \u05de\u05d5\u05e4\u05e2\u05dc"
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"_": {
|
||||
"armed": "\u05d3\u05e8\u05d5\u05da",
|
||||
|
@ -1,23 +1,40 @@
|
||||
{
|
||||
"device_automation": {
|
||||
"action_type": {
|
||||
"arm_away": "D\u0131\u015farda",
|
||||
"arm_home": "Evde",
|
||||
"arm_night": "Gece",
|
||||
"disarm": "Devre d\u0131\u015f\u0131 {entity_name}",
|
||||
"trigger": "Tetikle {entity_name}"
|
||||
},
|
||||
"condition_type": {
|
||||
"is_armed_away": "{entity_name} D\u0131\u015farda Modu Aktif",
|
||||
"is_armed_home": "{entity_name} Evde Modu Aktif",
|
||||
"is_armed_night": "{entity_name} Gece Modu Aktif",
|
||||
"is_disarmed": "{entity_name} Devre D\u0131\u015f\u0131",
|
||||
"is_triggered": "{entity_name} tetiklendi"
|
||||
},
|
||||
"trigger_type": {
|
||||
"disarmed": "{entity_name} b\u0131rak\u0131ld\u0131",
|
||||
"armed_away": "{entity_name} D\u0131\u015farda Modu Aktif",
|
||||
"armed_home": "{entity_name} Evde Modu Aktif",
|
||||
"armed_night": "{entity_name} Gece Modu Aktif",
|
||||
"disarmed": "{entity_name} Devre D\u0131\u015f\u0131",
|
||||
"triggered": "{entity_name} tetiklendi"
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"_": {
|
||||
"armed": "Etkin",
|
||||
"armed_away": "Etkin d\u0131\u015far\u0131da",
|
||||
"armed_custom_bypass": "Alarm etkin \u00f6zel baypas",
|
||||
"armed_home": "Etkin evde",
|
||||
"armed_night": "Etkin gece",
|
||||
"armed": "Aktif",
|
||||
"armed_away": "D\u0131\u015farda Aktif",
|
||||
"armed_custom_bypass": "\u00d6zel Mod Aktif",
|
||||
"armed_home": "Evde Aktif",
|
||||
"armed_night": "Gece Aktif",
|
||||
"arming": "Alarm etkinle\u015fiyor",
|
||||
"disarmed": "Etkisiz",
|
||||
"disarmed": "Devre D\u0131\u015f\u0131",
|
||||
"disarming": "Alarm devre d\u0131\u015f\u0131",
|
||||
"pending": "Beklemede",
|
||||
"triggered": "Tetiklendi"
|
||||
}
|
||||
},
|
||||
"title": "Alarm kontrol paneli"
|
||||
"title": "Alarm Kontrol Paneli"
|
||||
}
|
@ -29,6 +29,7 @@
|
||||
"armed_custom_bypass": "\u81ea\u5b9a\u4e49\u533a\u57df\u8b66\u6212",
|
||||
"armed_home": "\u5728\u5bb6\u8b66\u6212",
|
||||
"armed_night": "\u591c\u95f4\u8b66\u6212",
|
||||
"armed_vacation": "\u5ea6\u5047\u8b66\u6212",
|
||||
"arming": "\u8b66\u6212\u4e2d",
|
||||
"disarmed": "\u8b66\u6212\u89e3\u9664",
|
||||
"disarming": "\u8b66\u6212\u89e3\u9664",
|
||||
|
20
homeassistant/components/alarmdecoder/translations/bg.json
Normal file
20
homeassistant/components/alarmdecoder/translations/bg.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u0423\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u043e\u0442\u043e \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e"
|
||||
},
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"host": "\u0425\u043e\u0441\u0442",
|
||||
"port": "\u041f\u043e\u0440\u0442"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "\u041f\u0440\u043e\u0442\u043e\u043a\u043e\u043b"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -34,7 +34,9 @@
|
||||
"data": {
|
||||
"zone_name": "B\u00f6lge Ad\u0131",
|
||||
"zone_relayaddr": "R\u00f6le Adresi",
|
||||
"zone_relaychan": "R\u00f6le Kanal\u0131"
|
||||
"zone_relaychan": "R\u00f6le Kanal\u0131",
|
||||
"zone_rfid": "RF Id",
|
||||
"zone_type": "B\u00f6lge Tipi"
|
||||
}
|
||||
},
|
||||
"zone_select": {
|
||||
|
@ -221,8 +221,7 @@ class Alert(ToggleEntity):
|
||||
|
||||
async def watched_entity_change(self, ev):
|
||||
"""Determine if the alert should start or stop."""
|
||||
to_state = ev.data.get("new_state")
|
||||
if to_state is None:
|
||||
if (to_state := ev.data.get("new_state")) is None:
|
||||
return
|
||||
_LOGGER.debug("Watched entity (%s) has changed", ev.data.get("entity_id"))
|
||||
if to_state.state == self._alert_state and not self._firing:
|
||||
|
@ -30,9 +30,7 @@ async def _async_reproduce_state(
|
||||
reproduce_options: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
"""Reproduce a single state."""
|
||||
cur_state = hass.states.get(state.entity_id)
|
||||
|
||||
if cur_state is None:
|
||||
if (cur_state := hass.states.get(state.entity_id)) is None:
|
||||
_LOGGER.warning("Unable to find entity %s", state.entity_id)
|
||||
return
|
||||
|
||||
|
@ -1,13 +1,14 @@
|
||||
"""Support for Alexa skill auth."""
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
from http import HTTPStatus
|
||||
import json
|
||||
import logging
|
||||
|
||||
import aiohttp
|
||||
import async_timeout
|
||||
|
||||
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET, HTTP_OK
|
||||
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import aiohttp_client
|
||||
from homeassistant.util import dt
|
||||
@ -119,7 +120,7 @@ class Auth:
|
||||
_LOGGER.debug("LWA response header: %s", response.headers)
|
||||
_LOGGER.debug("LWA response status: %s", response.status)
|
||||
|
||||
if response.status != HTTP_OK:
|
||||
if response.status != HTTPStatus.OK:
|
||||
_LOGGER.error("Error calling LWA to get auth token")
|
||||
return None
|
||||
|
||||
|
@ -182,8 +182,7 @@ class AlexaCapability:
|
||||
"""Serialize according to the Discovery API."""
|
||||
result = {"type": "AlexaInterface", "interface": self.name(), "version": "3"}
|
||||
|
||||
instance = self.instance
|
||||
if instance is not None:
|
||||
if (instance := self.instance) is not None:
|
||||
result["instance"] = instance
|
||||
|
||||
properties_supported = self.properties_supported()
|
||||
@ -264,8 +263,7 @@ class AlexaCapability:
|
||||
"timeOfSample": dt_util.utcnow().strftime(DATE_FORMAT),
|
||||
"uncertaintyInMilliseconds": 0,
|
||||
}
|
||||
instance = self.instance
|
||||
if instance is not None:
|
||||
if (instance := self.instance) is not None:
|
||||
result["instance"] = instance
|
||||
|
||||
yield result
|
||||
@ -1098,8 +1096,7 @@ class AlexaThermostatController(AlexaCapability):
|
||||
supported_modes = []
|
||||
hvac_modes = self.entity.attributes.get(climate.ATTR_HVAC_MODES)
|
||||
for mode in hvac_modes:
|
||||
thermostat_mode = API_THERMOSTAT_MODES.get(mode)
|
||||
if thermostat_mode:
|
||||
if thermostat_mode := API_THERMOSTAT_MODES.get(mode):
|
||||
supported_modes.append(thermostat_mode)
|
||||
|
||||
preset_modes = self.entity.attributes.get(climate.ATTR_PRESET_MODES)
|
||||
@ -1538,7 +1535,9 @@ class AlexaRangeController(AlexaCapability):
|
||||
labels=["Percentage", AlexaGlobalCatalog.SETTING_FAN_SPEED],
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
precision=percentage_step if percentage_step else 100,
|
||||
# precision must be a divider of 100 and must be an integer; set step
|
||||
# size to 1 for a consistent behavior except for on/off fans
|
||||
precision=1 if percentage_step else 100,
|
||||
unit=AlexaGlobalCatalog.UNIT_PERCENT,
|
||||
)
|
||||
return self._resource.serialize_capability_resources()
|
||||
|
@ -1,11 +1,12 @@
|
||||
"""Support for Alexa skill service end point."""
|
||||
import copy
|
||||
import hmac
|
||||
from http import HTTPStatus
|
||||
import logging
|
||||
import uuid
|
||||
|
||||
from homeassistant.components import http
|
||||
from homeassistant.const import CONF_PASSWORD, HTTP_NOT_FOUND, HTTP_UNAUTHORIZED
|
||||
from homeassistant.const import CONF_PASSWORD
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import template
|
||||
import homeassistant.util.dt as dt_util
|
||||
@ -58,7 +59,7 @@ class AlexaFlashBriefingView(http.HomeAssistantView):
|
||||
if request.query.get(API_PASSWORD) is None:
|
||||
err = "No password provided for Alexa flash briefing: %s"
|
||||
_LOGGER.error(err, briefing_id)
|
||||
return b"", HTTP_UNAUTHORIZED
|
||||
return b"", HTTPStatus.UNAUTHORIZED
|
||||
|
||||
if not hmac.compare_digest(
|
||||
request.query[API_PASSWORD].encode("utf-8"),
|
||||
@ -66,12 +67,12 @@ class AlexaFlashBriefingView(http.HomeAssistantView):
|
||||
):
|
||||
err = "Wrong password for Alexa flash briefing: %s"
|
||||
_LOGGER.error(err, briefing_id)
|
||||
return b"", HTTP_UNAUTHORIZED
|
||||
return b"", HTTPStatus.UNAUTHORIZED
|
||||
|
||||
if not isinstance(self.flash_briefings.get(briefing_id), list):
|
||||
err = "No configured Alexa flash briefing was found for: %s"
|
||||
_LOGGER.error(err, briefing_id)
|
||||
return b"", HTTP_NOT_FOUND
|
||||
return b"", HTTPStatus.NOT_FOUND
|
||||
|
||||
briefing = []
|
||||
|
||||
@ -93,8 +94,7 @@ class AlexaFlashBriefingView(http.HomeAssistantView):
|
||||
else:
|
||||
output[ATTR_MAIN_TEXT] = item.get(CONF_TEXT)
|
||||
|
||||
uid = item.get(CONF_UID)
|
||||
if uid is None:
|
||||
if (uid := item.get(CONF_UID)) is None:
|
||||
uid = str(uuid.uuid4())
|
||||
output[ATTR_UID] = uid
|
||||
|
||||
|
@ -117,8 +117,7 @@ async def async_api_accept_grant(hass, config, directive, context):
|
||||
async def async_api_turn_on(hass, config, directive, context):
|
||||
"""Process a turn on request."""
|
||||
entity = directive.entity
|
||||
domain = entity.domain
|
||||
if domain == group.DOMAIN:
|
||||
if (domain := entity.domain) == group.DOMAIN:
|
||||
domain = ha.DOMAIN
|
||||
|
||||
service = SERVICE_TURN_ON
|
||||
@ -1151,8 +1150,7 @@ async def async_api_adjust_range(hass, config, directive, context):
|
||||
if instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
range_delta = int(range_delta * 20) if range_delta_default else int(range_delta)
|
||||
service = SERVICE_SET_COVER_POSITION
|
||||
current = entity.attributes.get(cover.ATTR_POSITION)
|
||||
if not current:
|
||||
if not (current := entity.attributes.get(cover.ATTR_POSITION)):
|
||||
msg = f"Unable to determine {entity.entity_id} current position"
|
||||
raise AlexaInvalidValueError(msg)
|
||||
position = response_value = min(100, max(0, range_delta + current))
|
||||
@ -1188,8 +1186,7 @@ async def async_api_adjust_range(hass, config, directive, context):
|
||||
else int(range_delta)
|
||||
)
|
||||
service = fan.SERVICE_SET_PERCENTAGE
|
||||
current = entity.attributes.get(fan.ATTR_PERCENTAGE)
|
||||
if not current:
|
||||
if not (current := entity.attributes.get(fan.ATTR_PERCENTAGE)):
|
||||
msg = f"Unable to determine {entity.entity_id} current fan speed"
|
||||
raise AlexaInvalidValueError(msg)
|
||||
percentage = response_value = min(100, max(0, range_delta + current))
|
||||
|
@ -120,9 +120,7 @@ async def async_handle_message(hass, message):
|
||||
req = message.get("request")
|
||||
req_type = req["type"]
|
||||
|
||||
handler = HANDLERS.get(req_type)
|
||||
|
||||
if not handler:
|
||||
if not (handler := HANDLERS.get(req_type)):
|
||||
raise UnknownRequest(f"Received unknown request {req_type}")
|
||||
|
||||
return await handler(hass, message)
|
||||
|
@ -12,9 +12,8 @@ def async_describe_events(hass, async_describe_event):
|
||||
def async_describe_logbook_event(event):
|
||||
"""Describe a logbook event."""
|
||||
data = event.data
|
||||
entity_id = data["request"].get("entity_id")
|
||||
|
||||
if entity_id:
|
||||
if entity_id := data["request"].get("entity_id"):
|
||||
state = hass.states.get(entity_id)
|
||||
name = state.name if state else entity_id
|
||||
message = f"sent command {data['request']['namespace']}/{data['request']['name']} for {name}"
|
||||
|
@ -3,7 +3,13 @@ import logging
|
||||
|
||||
from homeassistant import core
|
||||
from homeassistant.components.http.view import HomeAssistantView
|
||||
from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
|
||||
from homeassistant.const import (
|
||||
CONF_CLIENT_ID,
|
||||
CONF_CLIENT_SECRET,
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
)
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .auth import Auth
|
||||
from .config import AbstractConfig
|
||||
@ -60,7 +66,18 @@ class AlexaConfig(AbstractConfig):
|
||||
|
||||
def should_expose(self, entity_id):
|
||||
"""If an entity should be exposed."""
|
||||
return self._config[CONF_FILTER](entity_id)
|
||||
if not self._config[CONF_FILTER].empty_filter:
|
||||
return self._config[CONF_FILTER](entity_id)
|
||||
|
||||
entity_registry = er.async_get(self.hass)
|
||||
if registry_entry := entity_registry.async_get(entity_id):
|
||||
auxiliary_entity = registry_entry.entity_category in (
|
||||
ENTITY_CATEGORY_CONFIG,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
)
|
||||
else:
|
||||
auxiliary_entity = False
|
||||
return not auxiliary_entity
|
||||
|
||||
@core.callback
|
||||
def async_invalidate_access_token(self):
|
||||
|
@ -2,13 +2,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from http import HTTPStatus
|
||||
import json
|
||||
import logging
|
||||
|
||||
import aiohttp
|
||||
import async_timeout
|
||||
|
||||
from homeassistant.const import HTTP_ACCEPTED, MATCH_ALL, STATE_ON
|
||||
from homeassistant.const import MATCH_ALL, STATE_ON
|
||||
from homeassistant.core import HomeAssistant, State, callback
|
||||
from homeassistant.helpers.significant_change import create_checker
|
||||
import homeassistant.util.dt as dt_util
|
||||
@ -148,7 +149,7 @@ async def async_send_changereport_message(
|
||||
_LOGGER.debug("Sent: %s", json.dumps(message_serialized))
|
||||
_LOGGER.debug("Received (%s): %s", response.status, response_text)
|
||||
|
||||
if response.status == HTTP_ACCEPTED:
|
||||
if response.status == HTTPStatus.ACCEPTED:
|
||||
return
|
||||
|
||||
response_json = json.loads(response_text)
|
||||
@ -279,7 +280,7 @@ async def async_send_doorbell_event_message(hass, config, alexa_entity):
|
||||
_LOGGER.debug("Sent: %s", json.dumps(message_serialized))
|
||||
_LOGGER.debug("Received (%s): %s", response.status, response_text)
|
||||
|
||||
if response.status == HTTP_ACCEPTED:
|
||||
if response.status == HTTPStatus.ACCEPTED:
|
||||
return
|
||||
|
||||
response_json = json.loads(response_text)
|
||||
|
@ -2,7 +2,8 @@
|
||||
"config": {
|
||||
"abort": {
|
||||
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435 \u0441 Almond \u0441\u044a\u0440\u0432\u044a\u0440\u0430.",
|
||||
"missing_configuration": "\u041c\u043e\u043b\u044f, \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430 \u043a\u0430\u043a \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 Almond."
|
||||
"missing_configuration": "\u041c\u043e\u043b\u044f, \u043f\u0440\u043e\u0432\u0435\u0440\u0435\u0442\u0435 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f\u0442\u0430 \u043a\u0430\u043a \u0434\u0430 \u043d\u0430\u0441\u0442\u0440\u043e\u0438\u0442\u0435 Almond.",
|
||||
"single_instance_allowed": "\u0412\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d\u043e. \u0412\u044a\u0437\u043c\u043e\u0436\u043d\u0430 \u0435 \u0441\u0430\u043c\u043e \u0435\u0434\u043d\u0430 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044f."
|
||||
},
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
|
@ -21,7 +21,6 @@ DOMAIN: Final = "ambee"
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
SCAN_INTERVAL = timedelta(hours=1)
|
||||
|
||||
ATTR_ENTRY_TYPE: Final = "entry_type"
|
||||
ENTRY_TYPE_SERVICE: Final = "service"
|
||||
|
||||
DEVICE_CLASS_AMBEE_RISK: Final = "ambee__risk"
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "Ambee",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/ambee",
|
||||
"requirements": ["ambee==0.3.0"],
|
||||
"requirements": ["ambee==0.4.0"],
|
||||
"codeowners": ["@frenck"],
|
||||
"quality_scale": "platinum",
|
||||
"iot_class": "cloud_polling"
|
||||
|
@ -7,8 +7,8 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_IDENTIFIERS, ATTR_MANUFACTURER, ATTR_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.typing import StateType
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
@ -16,7 +16,7 @@ from homeassistant.helpers.update_coordinator import (
|
||||
DataUpdateCoordinator,
|
||||
)
|
||||
|
||||
from .const import ATTR_ENTRY_TYPE, DOMAIN, ENTRY_TYPE_SERVICE, SENSORS, SERVICES
|
||||
from .const import DOMAIN, ENTRY_TYPE_SERVICE, SENSORS, SERVICES
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
@ -58,12 +58,12 @@ class AmbeeSensorEntity(CoordinatorEntity, SensorEntity):
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{entry_id}_{service_key}_{description.key}"
|
||||
|
||||
self._attr_device_info = {
|
||||
ATTR_IDENTIFIERS: {(DOMAIN, f"{entry_id}_{service_key}")},
|
||||
ATTR_NAME: service,
|
||||
ATTR_MANUFACTURER: "Ambee",
|
||||
ATTR_ENTRY_TYPE: ENTRY_TYPE_SERVICE,
|
||||
}
|
||||
self._attr_device_info = DeviceInfo(
|
||||
entry_type=ENTRY_TYPE_SERVICE,
|
||||
identifiers={(DOMAIN, f"{entry_id}_{service_key}")},
|
||||
manufacturer="Ambee",
|
||||
name=service,
|
||||
)
|
||||
|
||||
@property
|
||||
def native_value(self) -> StateType:
|
||||
|
26
homeassistant/components/ambee/translations/bg.json
Normal file
26
homeassistant/components/ambee/translations/bg.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "\u041f\u043e\u0432\u0442\u043e\u0440\u043d\u043e\u0442\u043e \u0443\u0434\u043e\u0441\u0442\u043e\u0432\u0435\u0440\u044f\u0432\u0430\u043d\u0435 \u0431\u0435\u0448\u0435 \u0443\u0441\u043f\u0435\u0448\u043d\u043e"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "\u041d\u0435\u0443\u0441\u043f\u0435\u0445 \u043f\u0440\u0438 \u0441\u0432\u044a\u0440\u0437\u0432\u0430\u043d\u0435",
|
||||
"invalid_api_key": "\u041d\u0435\u0432\u0430\u043b\u0438\u0434\u0435\u043d API \u043a\u043b\u044e\u0447"
|
||||
},
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "API \u043a\u043b\u044e\u0447"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API \u043a\u043b\u044e\u0447",
|
||||
"latitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0448\u0438\u0440\u0438\u043d\u0430",
|
||||
"longitude": "\u0413\u0435\u043e\u0433\u0440\u0430\u0444\u0441\u043a\u0430 \u0434\u044a\u043b\u0436\u0438\u043d\u0430",
|
||||
"name": "\u0418\u043c\u0435"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt"
|
||||
"reauth_successful": "Az \u00fajrahiteles\u00edt\u00e9s sikeres volt."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "Sikertelen csatlakoz\u00e1s",
|
||||
|
@ -11,7 +11,7 @@
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "\u041a\u043b\u044e\u0447 API",
|
||||
"description": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Ambee."
|
||||
"description": "\u0422\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0432\u0442\u043e\u0440\u043d\u0430\u044f \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u044f \u0443\u0447\u0435\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Ambee"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
|
@ -10,7 +10,6 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_ATTRIBUTION
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
@ -28,6 +27,8 @@ PRICE_SPIKE_ICONS = {
|
||||
class AmberPriceGridSensor(CoordinatorEntity, BinarySensorEntity):
|
||||
"""Sensor to show single grid binary values."""
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AmberUpdateCoordinator,
|
||||
@ -38,7 +39,6 @@ class AmberPriceGridSensor(CoordinatorEntity, BinarySensorEntity):
|
||||
self.site_id = coordinator.site_id
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{coordinator.site_id}-{description.key}"
|
||||
self._attr_device_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool | None:
|
||||
@ -67,7 +67,6 @@ class AmberPriceSpikeBinarySensor(AmberPriceGridSensor):
|
||||
spike_status = self.coordinator.data["grid"]["price_spike"]
|
||||
return {
|
||||
"spike_status": spike_status,
|
||||
ATTR_ATTRIBUTION: ATTRIBUTION,
|
||||
}
|
||||
|
||||
|
||||
|
@ -20,7 +20,7 @@ from homeassistant.components.sensor import (
|
||||
SensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_ATTRIBUTION, CURRENCY_DOLLAR, ENERGY_KILO_WATT_HOUR
|
||||
from homeassistant.const import CURRENCY_DOLLAR, ENERGY_KILO_WATT_HOUR
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
@ -54,6 +54,8 @@ def friendly_channel_type(channel_type: str) -> str:
|
||||
class AmberSensor(CoordinatorEntity, SensorEntity):
|
||||
"""Amber Base Sensor."""
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AmberUpdateCoordinator,
|
||||
@ -88,7 +90,7 @@ class AmberPriceSensor(AmberSensor):
|
||||
"""Return additional pieces of information about the price."""
|
||||
interval = self.coordinator.data[self.entity_description.key][self.channel_type]
|
||||
|
||||
data: dict[str, Any] = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
data: dict[str, Any] = {}
|
||||
if interval is None:
|
||||
return data
|
||||
|
||||
@ -143,7 +145,6 @@ class AmberForecastSensor(AmberSensor):
|
||||
data = {
|
||||
"forecasts": [],
|
||||
"channel_type": intervals[0].channel_type.value,
|
||||
ATTR_ATTRIBUTION: ATTRIBUTION,
|
||||
}
|
||||
|
||||
for interval in intervals:
|
||||
@ -172,6 +173,8 @@ class AmberForecastSensor(AmberSensor):
|
||||
class AmberGridSensor(CoordinatorEntity, SensorEntity):
|
||||
"""Sensor to show single grid specific values."""
|
||||
|
||||
_attr_attribution = ATTRIBUTION
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: AmberUpdateCoordinator,
|
||||
@ -181,7 +184,6 @@ class AmberGridSensor(CoordinatorEntity, SensorEntity):
|
||||
super().__init__(coordinator)
|
||||
self.site_id = coordinator.site_id
|
||||
self.entity_description = description
|
||||
self._attr_device_state_attributes = {ATTR_ATTRIBUTION: ATTRIBUTION}
|
||||
self._attr_unique_id = f"{coordinator.site_id}-{description.key}"
|
||||
|
||||
@property
|
||||
|
13
homeassistant/components/amberelectric/translations/bg.json
Normal file
13
homeassistant/components/amberelectric/translations/bg.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"site": {
|
||||
"title": "Amber Electric"
|
||||
},
|
||||
"user": {
|
||||
"description": "\u041e\u0442\u0438\u0434\u0435\u0442\u0435 \u043d\u0430 {api_url}, \u0437\u0430 \u0434\u0430 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u0442\u0435 API \u043a\u043b\u044e\u0447",
|
||||
"title": "Amber Electric"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
homeassistant/components/amberelectric/translations/ja.json
Normal file
11
homeassistant/components/amberelectric/translations/ja.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_token": "API\u30c8\u30fc\u30af\u30f3"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Amber Electric"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
|
||||
from .const import (
|
||||
ATTR_VALUE,
|
||||
@ -149,16 +150,15 @@ class AmbiclimateEntity(ClimateEntity):
|
||||
self._store = store
|
||||
self._attr_unique_id = heater.device_id
|
||||
self._attr_name = heater.name
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(DOMAIN, self.unique_id)},
|
||||
"name": self.name,
|
||||
"manufacturer": "Ambiclimate",
|
||||
}
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, self.unique_id)},
|
||||
manufacturer="Ambiclimate",
|
||||
name=self.name,
|
||||
)
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set new target temperature."""
|
||||
temperature = kwargs.get(ATTR_TEMPERATURE)
|
||||
if temperature is None:
|
||||
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
|
||||
return
|
||||
await self._heater.set_target_temperature(temperature)
|
||||
|
||||
|
@ -143,8 +143,7 @@ class AmbiclimateAuthCallbackView(HomeAssistantView):
|
||||
async def get(self, request: web.Request) -> str:
|
||||
"""Receive authorization token."""
|
||||
# pylint: disable=no-self-use
|
||||
code = request.query.get("code")
|
||||
if code is None:
|
||||
if (code := request.query.get("code")) is None:
|
||||
return "No code"
|
||||
hass = request.app["hass"]
|
||||
hass.async_create_task(
|
||||
|
@ -1,7 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"access_token": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u0434 \u0437\u0430 \u0434\u043e\u0441\u0442\u044a\u043f."
|
||||
"access_token": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430 \u0433\u0440\u0435\u0448\u043a\u0430 \u043f\u0440\u0438 \u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0430\u043d\u0435 \u043d\u0430 \u043a\u043e\u0434 \u0437\u0430 \u0434\u043e\u0441\u0442\u044a\u043f.",
|
||||
"already_configured": "\u0410\u043a\u0430\u0443\u043d\u0442\u0430 \u0432\u0435\u0447\u0435 \u0435 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0438\u0440\u0430\u043d"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "\u0423\u0441\u043f\u0435\u0448\u043d\u043e \u0430\u0443\u0442\u0435\u043d\u0442\u0438\u043a\u0438\u0440\u0430\u043d\u0435 \u0441 Ambiclimate."
|
||||
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from aioambient import Client
|
||||
from aioambient import Websocket
|
||||
from aioambient.errors import WebsocketError
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
@ -15,18 +15,17 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import Event, HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import aiohttp_client, config_validation as cv
|
||||
from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import (
|
||||
async_dispatcher_connect,
|
||||
async_dispatcher_send,
|
||||
)
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription
|
||||
from homeassistant.helpers.event import async_call_later
|
||||
|
||||
from .const import (
|
||||
ATTR_LAST_DATA,
|
||||
CONF_APP_KEY,
|
||||
DATA_CLIENT,
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
TYPE_SOLARRADIATION,
|
||||
@ -57,37 +56,32 @@ def async_hydrate_station_data(data: dict[str, Any]) -> dict[str, Any]:
|
||||
return data
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up the Ambient PWS as config entry."""
|
||||
hass.data.setdefault(DOMAIN, {DATA_CLIENT: {}})
|
||||
hass.data.setdefault(DOMAIN, {})
|
||||
hass.data[DOMAIN][entry.entry_id] = {}
|
||||
|
||||
if not config_entry.unique_id:
|
||||
if not entry.unique_id:
|
||||
hass.config_entries.async_update_entry(
|
||||
config_entry, unique_id=config_entry.data[CONF_APP_KEY]
|
||||
entry, unique_id=entry.data[CONF_APP_KEY]
|
||||
)
|
||||
session = aiohttp_client.async_get_clientsession(hass)
|
||||
|
||||
try:
|
||||
ambient = AmbientStation(
|
||||
hass,
|
||||
config_entry,
|
||||
Client(
|
||||
config_entry.data[CONF_API_KEY],
|
||||
config_entry.data[CONF_APP_KEY],
|
||||
session=session,
|
||||
logger=LOGGER,
|
||||
),
|
||||
entry,
|
||||
Websocket(entry.data[CONF_APP_KEY], entry.data[CONF_API_KEY]),
|
||||
)
|
||||
hass.loop.create_task(ambient.ws_connect())
|
||||
hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = ambient
|
||||
hass.data[DOMAIN][entry.entry_id] = ambient
|
||||
except WebsocketError as err:
|
||||
LOGGER.error("Config entry failed: %s", err)
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
async def _async_disconnect_websocket(_: Event) -> None:
|
||||
await ambient.client.websocket.disconnect()
|
||||
await ambient.websocket.disconnect()
|
||||
|
||||
config_entry.async_on_unload(
|
||||
entry.async_on_unload(
|
||||
hass.bus.async_listen_once(
|
||||
EVENT_HOMEASSISTANT_STOP, _async_disconnect_websocket
|
||||
)
|
||||
@ -96,30 +90,32 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload an Ambient PWS config entry."""
|
||||
ambient = hass.data[DOMAIN][DATA_CLIENT].pop(config_entry.entry_id)
|
||||
hass.async_create_task(ambient.ws_disconnect())
|
||||
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
if unload_ok:
|
||||
ambient = hass.data[DOMAIN].pop(entry.entry_id)
|
||||
hass.async_create_task(ambient.ws_disconnect())
|
||||
|
||||
return await hass.config_entries.async_unload_platforms(config_entry, PLATFORMS)
|
||||
return unload_ok
|
||||
|
||||
|
||||
async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Migrate old entry."""
|
||||
version = config_entry.version
|
||||
version = entry.version
|
||||
|
||||
LOGGER.debug("Migrating from version %s", version)
|
||||
|
||||
# 1 -> 2: Unique ID format changed, so delete and re-import:
|
||||
if version == 1:
|
||||
dev_reg = await hass.helpers.device_registry.async_get_registry()
|
||||
dev_reg.async_clear_config_entry(config_entry)
|
||||
dev_reg.async_clear_config_entry(entry)
|
||||
|
||||
en_reg = await hass.helpers.entity_registry.async_get_registry()
|
||||
en_reg.async_clear_config_entry(config_entry)
|
||||
en_reg.async_clear_config_entry(entry)
|
||||
|
||||
version = config_entry.version = 2
|
||||
hass.config_entries.async_update_entry(config_entry)
|
||||
version = entry.version = 2
|
||||
hass.config_entries.async_update_entry(entry)
|
||||
LOGGER.info("Migration to version %s successful", version)
|
||||
|
||||
return True
|
||||
@ -129,22 +125,22 @@ class AmbientStation:
|
||||
"""Define a class to handle the Ambient websocket."""
|
||||
|
||||
def __init__(
|
||||
self, hass: HomeAssistant, config_entry: ConfigEntry, client: Client
|
||||
self, hass: HomeAssistant, entry: ConfigEntry, websocket: Websocket
|
||||
) -> None:
|
||||
"""Initialize."""
|
||||
self._config_entry = config_entry
|
||||
self._entry = entry
|
||||
self._entry_setup_complete = False
|
||||
self._hass = hass
|
||||
self._ws_reconnect_delay = DEFAULT_SOCKET_MIN_RETRY
|
||||
self.client = client
|
||||
self.stations: dict[str, dict] = {}
|
||||
self.websocket = websocket
|
||||
|
||||
async def _attempt_connect(self) -> None:
|
||||
"""Attempt to connect to the socket (retrying later on fail)."""
|
||||
|
||||
async def connect(timestamp: int | None = None) -> None:
|
||||
"""Connect."""
|
||||
await self.client.websocket.connect()
|
||||
await self.websocket.connect()
|
||||
|
||||
try:
|
||||
await connect()
|
||||
@ -194,22 +190,20 @@ class AmbientStation:
|
||||
# attempt forward setup of the config entry (because it will have
|
||||
# already been done):
|
||||
if not self._entry_setup_complete:
|
||||
self._hass.config_entries.async_setup_platforms(
|
||||
self._config_entry, PLATFORMS
|
||||
)
|
||||
self._hass.config_entries.async_setup_platforms(self._entry, PLATFORMS)
|
||||
self._entry_setup_complete = True
|
||||
self._ws_reconnect_delay = DEFAULT_SOCKET_MIN_RETRY
|
||||
|
||||
self.client.websocket.on_connect(on_connect)
|
||||
self.client.websocket.on_data(on_data)
|
||||
self.client.websocket.on_disconnect(on_disconnect)
|
||||
self.client.websocket.on_subscribed(on_subscribed)
|
||||
self.websocket.on_connect(on_connect)
|
||||
self.websocket.on_data(on_data)
|
||||
self.websocket.on_disconnect(on_disconnect)
|
||||
self.websocket.on_subscribed(on_subscribed)
|
||||
|
||||
await self._attempt_connect()
|
||||
|
||||
async def ws_disconnect(self) -> None:
|
||||
"""Disconnect from the websocket."""
|
||||
await self.client.websocket.disconnect()
|
||||
await self.websocket.disconnect()
|
||||
|
||||
|
||||
class AmbientWeatherEntity(Entity):
|
||||
@ -226,11 +220,11 @@ class AmbientWeatherEntity(Entity):
|
||||
) -> None:
|
||||
"""Initialize the entity."""
|
||||
self._ambient = ambient
|
||||
self._attr_device_info = {
|
||||
"identifiers": {(DOMAIN, mac_address)},
|
||||
"name": station_name,
|
||||
"manufacturer": "Ambient Weather",
|
||||
}
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, mac_address)},
|
||||
manufacturer="Ambient Weather",
|
||||
name=station_name,
|
||||
)
|
||||
self._attr_name = f"{station_name}_{description.name}"
|
||||
self._attr_unique_id = f"{mac_address}_{description.key}"
|
||||
self._mac_address = mac_address
|
||||
|
@ -11,12 +11,12 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_NAME
|
||||
from homeassistant.const import ATTR_NAME, ENTITY_CATEGORY_DIAGNOSTIC
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import AmbientWeatherEntity
|
||||
from .const import ATTR_LAST_DATA, DATA_CLIENT, DOMAIN
|
||||
from .const import ATTR_LAST_DATA, DOMAIN
|
||||
|
||||
TYPE_BATT1 = "batt1"
|
||||
TYPE_BATT10 = "batt10"
|
||||
@ -63,144 +63,168 @@ BINARY_SENSOR_DESCRIPTIONS = (
|
||||
key=TYPE_BATTOUT,
|
||||
name="Battery",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT1,
|
||||
name="Battery 1",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT2,
|
||||
name="Battery 2",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT3,
|
||||
name="Battery 3",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT4,
|
||||
name="Battery 4",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT5,
|
||||
name="Battery 5",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT6,
|
||||
name="Battery 6",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT7,
|
||||
name="Battery 7",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT8,
|
||||
name="Battery 8",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT9,
|
||||
name="Battery 9",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT10,
|
||||
name="Battery 10",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_BATT_CO2,
|
||||
name="CO2 Battery",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_PM25IN_BATT,
|
||||
name="PM25 Indoor Battery",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_PM25_BATT,
|
||||
name="PM25 Battery",
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=0,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY1,
|
||||
name="Relay 1",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY2,
|
||||
name="Relay 2",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY3,
|
||||
name="Relay 3",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY4,
|
||||
name="Relay 4",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY5,
|
||||
name="Relay 5",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY6,
|
||||
name="Relay 6",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY7,
|
||||
name="Relay 7",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY8,
|
||||
name="Relay 8",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY9,
|
||||
name="Relay 9",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=1,
|
||||
),
|
||||
AmbientBinarySensorDescription(
|
||||
key=TYPE_RELAY10,
|
||||
name="Relay 10",
|
||||
device_class=DEVICE_CLASS_CONNECTIVITY,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
on_state=1,
|
||||
),
|
||||
)
|
||||
@ -210,7 +234,7 @@ async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Ambient PWS binary sensors based on a config entry."""
|
||||
ambient = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id]
|
||||
ambient = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
|
@ -1,7 +1,7 @@
|
||||
"""Config flow to configure the Ambient PWS component."""
|
||||
from __future__ import annotations
|
||||
|
||||
from aioambient import Client
|
||||
from aioambient import API
|
||||
from aioambient.errors import AmbientError
|
||||
import voluptuous as vol
|
||||
|
||||
@ -41,12 +41,10 @@ class AmbientStationFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
session = aiohttp_client.async_get_clientsession(self.hass)
|
||||
client = Client(
|
||||
user_input[CONF_API_KEY], user_input[CONF_APP_KEY], session=session
|
||||
)
|
||||
api = API(user_input[CONF_APP_KEY], user_input[CONF_API_KEY], session=session)
|
||||
|
||||
try:
|
||||
devices = await client.api.get_devices()
|
||||
devices = await api.get_devices()
|
||||
except AmbientError:
|
||||
return await self._show_form({"base": "invalid_key"})
|
||||
|
||||
|
@ -8,7 +8,5 @@ ATTR_LAST_DATA = "last_data"
|
||||
|
||||
CONF_APP_KEY = "app_key"
|
||||
|
||||
DATA_CLIENT = "data_client"
|
||||
|
||||
TYPE_SOLARRADIATION = "solarradiation"
|
||||
TYPE_SOLARRADIATION_LX = "solarradiation_lx"
|
||||
|
@ -3,7 +3,7 @@
|
||||
"name": "Ambient Weather Station",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/ambient_station",
|
||||
"requirements": ["aioambient==1.3.0"],
|
||||
"requirements": ["aioambient==2021.10.1"],
|
||||
"codeowners": ["@bachya"],
|
||||
"iot_class": "cloud_push"
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ from . import (
|
||||
AmbientStation,
|
||||
AmbientWeatherEntity,
|
||||
)
|
||||
from .const import ATTR_LAST_DATA, DATA_CLIENT, DOMAIN
|
||||
from .const import ATTR_LAST_DATA, DOMAIN
|
||||
|
||||
TYPE_24HOURRAININ = "24hourrainin"
|
||||
TYPE_BAROMABSIN = "baromabsin"
|
||||
@ -609,7 +609,7 @@ async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up Ambient PWS sensors based on a config entry."""
|
||||
ambient = hass.data[DOMAIN][DATA_CLIENT][entry.entry_id]
|
||||
ambient = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
async_add_entities(
|
||||
[
|
||||
|
@ -13,7 +13,7 @@
|
||||
"api_key": "API kulcs",
|
||||
"app_key": "Alkalmaz\u00e1skulcs"
|
||||
},
|
||||
"title": "T\u00f6ltsd ki az adataid"
|
||||
"title": "T\u00f6ltse ki az adatait"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user