Merge pull request #33814 from home-assistant/rc

0.108.0
This commit is contained in:
Franck Nijhof 2020-04-08 16:04:35 +02:00 committed by GitHub
commit 38158376b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1576 changed files with 55461 additions and 15382 deletions

View File

@ -8,15 +8,6 @@ omit =
homeassistant/scripts/*.py homeassistant/scripts/*.py
# omit pieces of code that rely on external devices being present # omit pieces of code that rely on external devices being present
homeassistant/components/abode/__init__.py
homeassistant/components/abode/alarm_control_panel.py
homeassistant/components/abode/binary_sensor.py
homeassistant/components/abode/camera.py
homeassistant/components/abode/cover.py
homeassistant/components/abode/light.py
homeassistant/components/abode/lock.py
homeassistant/components/abode/sensor.py
homeassistant/components/abode/switch.py
homeassistant/components/acer_projector/switch.py homeassistant/components/acer_projector/switch.py
homeassistant/components/actiontec/device_tracker.py homeassistant/components/actiontec/device_tracker.py
homeassistant/components/adguard/__init__.py homeassistant/components/adguard/__init__.py
@ -33,7 +24,6 @@ omit =
homeassistant/components/airvisual/sensor.py homeassistant/components/airvisual/sensor.py
homeassistant/components/aladdin_connect/cover.py homeassistant/components/aladdin_connect/cover.py
homeassistant/components/alarmdecoder/* homeassistant/components/alarmdecoder/*
homeassistant/components/alarmdotcom/alarm_control_panel.py
homeassistant/components/alpha_vantage/sensor.py homeassistant/components/alpha_vantage/sensor.py
homeassistant/components/amazon_polly/tts.py homeassistant/components/amazon_polly/tts.py
homeassistant/components/ambiclimate/climate.py homeassistant/components/ambiclimate/climate.py
@ -85,6 +75,7 @@ omit =
homeassistant/components/bluetooth_tracker/* homeassistant/components/bluetooth_tracker/*
homeassistant/components/bme280/sensor.py homeassistant/components/bme280/sensor.py
homeassistant/components/bme680/sensor.py homeassistant/components/bme680/sensor.py
homeassistant/components/bmp280/sensor.py
homeassistant/components/bmw_connected_drive/* homeassistant/components/bmw_connected_drive/*
homeassistant/components/bom/camera.py homeassistant/components/bom/camera.py
homeassistant/components/bom/sensor.py homeassistant/components/bom/sensor.py
@ -93,9 +84,6 @@ omit =
homeassistant/components/broadlink/remote.py homeassistant/components/broadlink/remote.py
homeassistant/components/broadlink/sensor.py homeassistant/components/broadlink/sensor.py
homeassistant/components/broadlink/switch.py homeassistant/components/broadlink/switch.py
homeassistant/components/brother/__init__.py
homeassistant/components/brother/sensor.py
homeassistant/components/brother/const.py
homeassistant/components/brottsplatskartan/sensor.py homeassistant/components/brottsplatskartan/sensor.py
homeassistant/components/browser/* homeassistant/components/browser/*
homeassistant/components/brunt/cover.py homeassistant/components/brunt/cover.py
@ -242,7 +230,11 @@ omit =
homeassistant/components/foscam/const.py homeassistant/components/foscam/const.py
homeassistant/components/foursquare/* homeassistant/components/foursquare/*
homeassistant/components/free_mobile/notify.py homeassistant/components/free_mobile/notify.py
homeassistant/components/freebox/* homeassistant/components/freebox/__init__.py
homeassistant/components/freebox/device_tracker.py
homeassistant/components/freebox/router.py
homeassistant/components/freebox/sensor.py
homeassistant/components/freebox/switch.py
homeassistant/components/fritz/device_tracker.py homeassistant/components/fritz/device_tracker.py
homeassistant/components/fritzbox/* homeassistant/components/fritzbox/*
homeassistant/components/fritzbox_callmonitor/sensor.py homeassistant/components/fritzbox_callmonitor/sensor.py
@ -433,6 +425,7 @@ omit =
homeassistant/components/minecraft_server/__init__.py homeassistant/components/minecraft_server/__init__.py
homeassistant/components/minecraft_server/binary_sensor.py homeassistant/components/minecraft_server/binary_sensor.py
homeassistant/components/minecraft_server/const.py homeassistant/components/minecraft_server/const.py
homeassistant/components/minecraft_server/helpers.py
homeassistant/components/minecraft_server/sensor.py homeassistant/components/minecraft_server/sensor.py
homeassistant/components/minio/* homeassistant/components/minio/*
homeassistant/components/mitemp_bt/sensor.py homeassistant/components/mitemp_bt/sensor.py
@ -441,7 +434,6 @@ omit =
homeassistant/components/mochad/* homeassistant/components/mochad/*
homeassistant/components/modbus/* homeassistant/components/modbus/*
homeassistant/components/modem_callerid/sensor.py homeassistant/components/modem_callerid/sensor.py
homeassistant/components/mopar/*
homeassistant/components/mpchc/media_player.py homeassistant/components/mpchc/media_player.py
homeassistant/components/mpd/media_player.py homeassistant/components/mpd/media_player.py
homeassistant/components/mqtt_room/sensor.py homeassistant/components/mqtt_room/sensor.py
@ -450,7 +442,6 @@ omit =
homeassistant/components/mychevy/* homeassistant/components/mychevy/*
homeassistant/components/mycroft/* homeassistant/components/mycroft/*
homeassistant/components/mycroft/notify.py homeassistant/components/mycroft/notify.py
homeassistant/components/myq/cover.py
homeassistant/components/mysensors/* homeassistant/components/mysensors/*
homeassistant/components/mystrom/binary_sensor.py homeassistant/components/mystrom/binary_sensor.py
homeassistant/components/mystrom/light.py homeassistant/components/mystrom/light.py
@ -476,6 +467,7 @@ omit =
homeassistant/components/netgear_lte/* homeassistant/components/netgear_lte/*
homeassistant/components/netio/switch.py homeassistant/components/netio/switch.py
homeassistant/components/neurio_energy/sensor.py homeassistant/components/neurio_energy/sensor.py
homeassistant/components/nextcloud/*
homeassistant/components/nfandroidtv/notify.py homeassistant/components/nfandroidtv/notify.py
homeassistant/components/niko_home_control/light.py homeassistant/components/niko_home_control/light.py
homeassistant/components/nilu/air_quality.py homeassistant/components/nilu/air_quality.py
@ -597,7 +589,9 @@ omit =
homeassistant/components/ring/camera.py homeassistant/components/ring/camera.py
homeassistant/components/ripple/sensor.py homeassistant/components/ripple/sensor.py
homeassistant/components/rocketchat/notify.py homeassistant/components/rocketchat/notify.py
homeassistant/components/roku/* homeassistant/components/roku/__init__.py
homeassistant/components/roku/media_player.py
homeassistant/components/roku/remote.py
homeassistant/components/roomba/vacuum.py homeassistant/components/roomba/vacuum.py
homeassistant/components/route53/* homeassistant/components/route53/*
homeassistant/components/rova/sensor.py homeassistant/components/rova/sensor.py
@ -614,6 +608,7 @@ omit =
homeassistant/components/saj/sensor.py homeassistant/components/saj/sensor.py
homeassistant/components/salt/device_tracker.py homeassistant/components/salt/device_tracker.py
homeassistant/components/satel_integra/* homeassistant/components/satel_integra/*
homeassistant/components/schluter/*
homeassistant/components/scrape/sensor.py homeassistant/components/scrape/sensor.py
homeassistant/components/scsgate/* homeassistant/components/scsgate/*
homeassistant/components/scsgate/cover.py homeassistant/components/scsgate/cover.py

49
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,49 @@
<!-- READ THIS FIRST:
- If you need additional help with this template, please refer to https://www.home-assistant.io/help/reporting_issues/
- Make sure you are running the latest version of Home Assistant before reporting an issue: https://github.com/home-assistant/core/releases
- Do not report issues for integrations if you are using custom components or integrations.
- Provide as many details as possible. Paste logs, configuration samples and code into the backticks.
DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed without comment.
-->
## The problem
<!--
Describe the issue you are experiencing here to communicate to the
maintainers. Tell us what you were trying to do and what happened.
-->
## Environment
<!--
Provide details about the versions you are using, which helps us to reproduce
and find the issue quicker. Version information is found in the
Home Assistant frontend: Developer tools -> Info.
-->
- Home Assistant Core release with the issue:
- Last working Home Assistant Core release (if known):
- Operating environment (Home Assistant/Supervised/Docker/venv):
- Integration causing this issue:
- Link to integration documentation on our website:
## Problem-relevant `configuration.yaml`
<!--
An example configuration that caused the problem for you. Fill this out even
if it seems unimportant to you. Please be sure to remove personal information
like passwords, private URLs and other credentials.
-->
```yaml
```
## Traceback/Error logs
<!--
If you come across any trace or error logs, please provide them.
-->
```txt
```
## Additional information

View File

@ -1,10 +1,10 @@
--- ---
name: Report a bug with Home Assistant name: Report a bug with Home Assistant Core
about: Report an issue with Home Assistant about: Report an issue with Home Assistant Core
--- ---
<!-- READ THIS FIRST: <!-- READ THIS FIRST:
- If you need additional help with this template, please refer to https://www.home-assistant.io/help/reporting_issues/ - If you need additional help with this template, please refer to https://www.home-assistant.io/help/reporting_issues/
- Make sure you are running the latest version of Home Assistant before reporting an issue: https://github.com/home-assistant/home-assistant/releases - Make sure you are running the latest version of Home Assistant before reporting an issue: https://github.com/home-assistant/core/releases
- Do not report issues for integrations if you are using custom components or integrations. - Do not report issues for integrations if you are using custom components or integrations.
- Provide as many details as possible. Paste logs, configuration samples and code into the backticks. - Provide as many details as possible. Paste logs, configuration samples and code into the backticks.
DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed without comment. DO NOT DELETE ANY TEXT from this template! Otherwise, your issue may be closed without comment.
@ -12,7 +12,7 @@ about: Report an issue with Home Assistant
## The problem ## The problem
<!-- <!--
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 instead. maintainers. Tell us what you were trying to do and what happened.
--> -->
@ -23,9 +23,9 @@ about: Report an issue with Home Assistant
Home Assistant frontend: Developer tools -> Info. Home Assistant frontend: Developer tools -> Info.
--> -->
- Home Assistant release with the issue: - Home Assistant Core release with the issue:
- Last working Home Assistant release (if known): - Last working Home Assistant Core release (if known):
- Operating environment (Hass.io/Docker/Windows/etc.): - Operating environment (Home Assistant/Supervised/Docker/venv):
- Integration causing this issue: - Integration causing this issue:
- Link to integration documentation on our website: - Link to integration documentation on our website:

1
.github/stale.yml vendored
View File

@ -57,6 +57,7 @@ limitPerRun: 30
# Handle pull requests a little bit faster and with an adjusted comment. # Handle pull requests a little bit faster and with an adjusted comment.
pulls: pulls:
daysUntilStale: 30 daysUntilStale: 30
exemptProjects: false
markComment: > markComment: >
There hasn't been any activity on this pull request recently. This pull There hasn't been any activity on this pull request recently. This pull
request has been automatically marked as stale because of that and will request has been automatically marked as stale because of that and will

View File

@ -59,3 +59,17 @@ repos:
types: [python] types: [python]
require_serial: true require_serial: true
files: ^homeassistant/.+\.py$ files: ^homeassistant/.+\.py$
- id: gen_requirements_all
name: gen_requirements_all
entry: script/run-in-env.sh python3 -m script.gen_requirements_all
pass_filenames: false
language: script
types: [json]
files: ^homeassistant/.+/manifest\.json$
- id: hassfest
name: hassfest
entry: script/run-in-env.sh python3 -m script.hassfest
pass_filenames: false
language: script
types: [json]
files: ^homeassistant/.+/manifest\.json$

View File

@ -17,6 +17,7 @@ homeassistant/components/abode/* @shred86
homeassistant/components/adguard/* @frenck homeassistant/components/adguard/* @frenck
homeassistant/components/airly/* @bieniu homeassistant/components/airly/* @bieniu
homeassistant/components/airvisual/* @bachya homeassistant/components/airvisual/* @bachya
homeassistant/components/alarmdecoder/* @ajschmidt8
homeassistant/components/alexa/* @home-assistant/cloud @ochlocracy homeassistant/components/alexa/* @home-assistant/cloud @ochlocracy
homeassistant/components/almond/* @gcampax @balloob homeassistant/components/almond/* @gcampax @balloob
homeassistant/components/alpha_vantage/* @fabaff homeassistant/components/alpha_vantage/* @fabaff
@ -51,6 +52,7 @@ homeassistant/components/beewi_smartclim/* @alemuro
homeassistant/components/bitcoin/* @fabaff homeassistant/components/bitcoin/* @fabaff
homeassistant/components/bizkaibus/* @UgaitzEtxebarria homeassistant/components/bizkaibus/* @UgaitzEtxebarria
homeassistant/components/blink/* @fronzbot homeassistant/components/blink/* @fronzbot
homeassistant/components/bmp280/* @belidzs
homeassistant/components/bmw_connected_drive/* @gerard33 homeassistant/components/bmw_connected_drive/* @gerard33
homeassistant/components/bom/* @maddenp homeassistant/components/bom/* @maddenp
homeassistant/components/braviatv/* @robbiet480 homeassistant/components/braviatv/* @robbiet480
@ -80,12 +82,13 @@ homeassistant/components/darksky/* @fabaff
homeassistant/components/deconz/* @kane610 homeassistant/components/deconz/* @kane610
homeassistant/components/delijn/* @bollewolle homeassistant/components/delijn/* @bollewolle
homeassistant/components/demo/* @home-assistant/core homeassistant/components/demo/* @home-assistant/core
homeassistant/components/denonavr/* @scarface-4711 @starkillerOG
homeassistant/components/derivative/* @afaucogney homeassistant/components/derivative/* @afaucogney
homeassistant/components/device_automation/* @home-assistant/core homeassistant/components/device_automation/* @home-assistant/core
homeassistant/components/digital_ocean/* @fabaff homeassistant/components/digital_ocean/* @fabaff
homeassistant/components/directv/* @ctalkington homeassistant/components/directv/* @ctalkington
homeassistant/components/discogs/* @thibmaek homeassistant/components/discogs/* @thibmaek
homeassistant/components/doorbird/* @oblogic7 homeassistant/components/doorbird/* @oblogic7 @bdraco
homeassistant/components/dsmr_reader/* @depl0y homeassistant/components/dsmr_reader/* @depl0y
homeassistant/components/dweet/* @fabaff homeassistant/components/dweet/* @fabaff
homeassistant/components/dynalite/* @ziv1234 homeassistant/components/dynalite/* @ziv1234
@ -96,6 +99,7 @@ homeassistant/components/edl21/* @mtdcr
homeassistant/components/egardia/* @jeroenterheerdt homeassistant/components/egardia/* @jeroenterheerdt
homeassistant/components/eight_sleep/* @mezz64 homeassistant/components/eight_sleep/* @mezz64
homeassistant/components/elgato/* @frenck homeassistant/components/elgato/* @frenck
homeassistant/components/elkm1/* @bdraco
homeassistant/components/elv/* @majuss homeassistant/components/elv/* @majuss
homeassistant/components/emby/* @mezz64 homeassistant/components/emby/* @mezz64
homeassistant/components/emoncms/* @borpin homeassistant/components/emoncms/* @borpin
@ -122,7 +126,7 @@ homeassistant/components/fortigate/* @kifeo
homeassistant/components/fortios/* @kimfrellsen homeassistant/components/fortios/* @kimfrellsen
homeassistant/components/foscam/* @skgsergio homeassistant/components/foscam/* @skgsergio
homeassistant/components/foursquare/* @robbiet480 homeassistant/components/foursquare/* @robbiet480
homeassistant/components/freebox/* @snoof85 homeassistant/components/freebox/* @snoof85 @Quentame
homeassistant/components/fronius/* @nielstron homeassistant/components/fronius/* @nielstron
homeassistant/components/frontend/* @home-assistant/frontend homeassistant/components/frontend/* @home-assistant/frontend
homeassistant/components/garmin_connect/* @cyberjunky homeassistant/components/garmin_connect/* @cyberjunky
@ -146,7 +150,7 @@ homeassistant/components/griddy/* @bdraco
homeassistant/components/group/* @home-assistant/core homeassistant/components/group/* @home-assistant/core
homeassistant/components/growatt_server/* @indykoning homeassistant/components/growatt_server/* @indykoning
homeassistant/components/gtfs/* @robbiet480 homeassistant/components/gtfs/* @robbiet480
homeassistant/components/harmony/* @ehendrix23 homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco
homeassistant/components/hassio/* @home-assistant/hass-io homeassistant/components/hassio/* @home-assistant/hass-io
homeassistant/components/heatmiser/* @andylockran homeassistant/components/heatmiser/* @andylockran
homeassistant/components/heos/* @andrewsayre homeassistant/components/heos/* @andrewsayre
@ -183,6 +187,7 @@ homeassistant/components/intesishome/* @jnimmo
homeassistant/components/ios/* @robbiet480 homeassistant/components/ios/* @robbiet480
homeassistant/components/iperf3/* @rohankapoorcom homeassistant/components/iperf3/* @rohankapoorcom
homeassistant/components/ipma/* @dgomes @abmantis homeassistant/components/ipma/* @dgomes @abmantis
homeassistant/components/ipp/* @ctalkington
homeassistant/components/iqvia/* @bachya homeassistant/components/iqvia/* @bachya
homeassistant/components/irish_rail_transport/* @ttroy50 homeassistant/components/irish_rail_transport/* @ttroy50
homeassistant/components/izone/* @Swamp-Ig homeassistant/components/izone/* @Swamp-Ig
@ -210,6 +215,7 @@ homeassistant/components/luci/* @fbradyirl @mzdrale
homeassistant/components/luftdaten/* @fabaff homeassistant/components/luftdaten/* @fabaff
homeassistant/components/lupusec/* @majuss homeassistant/components/lupusec/* @majuss
homeassistant/components/lutron/* @JonGilmore homeassistant/components/lutron/* @JonGilmore
homeassistant/components/lutron_caseta/* @swails
homeassistant/components/mastodon/* @fabaff homeassistant/components/mastodon/* @fabaff
homeassistant/components/matrix/* @tinloaf homeassistant/components/matrix/* @tinloaf
homeassistant/components/mcp23017/* @jardiamj homeassistant/components/mcp23017/* @jardiamj
@ -226,12 +232,13 @@ homeassistant/components/min_max/* @fabaff
homeassistant/components/minecraft_server/* @elmurato homeassistant/components/minecraft_server/* @elmurato
homeassistant/components/minio/* @tkislan homeassistant/components/minio/* @tkislan
homeassistant/components/mobile_app/* @robbiet480 homeassistant/components/mobile_app/* @robbiet480
homeassistant/components/modbus/* @adamchengtkc homeassistant/components/modbus/* @adamchengtkc @janiversen
homeassistant/components/monoprice/* @etsinko homeassistant/components/monoprice/* @etsinko
homeassistant/components/moon/* @fabaff homeassistant/components/moon/* @fabaff
homeassistant/components/mpd/* @fabaff homeassistant/components/mpd/* @fabaff
homeassistant/components/mqtt/* @home-assistant/core homeassistant/components/mqtt/* @home-assistant/core
homeassistant/components/msteams/* @peroyvind homeassistant/components/msteams/* @peroyvind
homeassistant/components/myq/* @bdraco
homeassistant/components/mysensors/* @MartinHjelmare homeassistant/components/mysensors/* @MartinHjelmare
homeassistant/components/mystrom/* @fabaff homeassistant/components/mystrom/* @fabaff
homeassistant/components/neato/* @dshokouhi @Santobert homeassistant/components/neato/* @dshokouhi @Santobert
@ -241,7 +248,9 @@ homeassistant/components/ness_alarm/* @nickw444
homeassistant/components/nest/* @awarecan homeassistant/components/nest/* @awarecan
homeassistant/components/netatmo/* @cgtobi homeassistant/components/netatmo/* @cgtobi
homeassistant/components/netdata/* @fabaff homeassistant/components/netdata/* @fabaff
homeassistant/components/nexia/* @ryannazaretian @bdraco
homeassistant/components/nextbus/* @vividboarder homeassistant/components/nextbus/* @vividboarder
homeassistant/components/nextcloud/* @meichthys
homeassistant/components/nilu/* @hfurubotten homeassistant/components/nilu/* @hfurubotten
homeassistant/components/nissan_leaf/* @filcole homeassistant/components/nissan_leaf/* @filcole
homeassistant/components/nmbs/* @thibmaek homeassistant/components/nmbs/* @thibmaek
@ -250,7 +259,9 @@ homeassistant/components/notify/* @home-assistant/core
homeassistant/components/notion/* @bachya homeassistant/components/notion/* @bachya
homeassistant/components/nsw_fuel_station/* @nickw444 homeassistant/components/nsw_fuel_station/* @nickw444
homeassistant/components/nsw_rural_fire_service_feed/* @exxamalte homeassistant/components/nsw_rural_fire_service_feed/* @exxamalte
homeassistant/components/nuheat/* @bdraco
homeassistant/components/nuki/* @pvizeli homeassistant/components/nuki/* @pvizeli
homeassistant/components/nut/* @bdraco
homeassistant/components/nws/* @MatthewFlamm homeassistant/components/nws/* @MatthewFlamm
homeassistant/components/nzbget/* @chriscla homeassistant/components/nzbget/* @chriscla
homeassistant/components/obihai/* @dshokouhi homeassistant/components/obihai/* @dshokouhi
@ -275,17 +286,21 @@ homeassistant/components/plaato/* @JohNan
homeassistant/components/plant/* @ChristianKuehnel homeassistant/components/plant/* @ChristianKuehnel
homeassistant/components/plex/* @jjlawren homeassistant/components/plex/* @jjlawren
homeassistant/components/plugwise/* @laetificat @CoMPaTech @bouwew homeassistant/components/plugwise/* @laetificat @CoMPaTech @bouwew
homeassistant/components/plum_lightpad/* @ColinHarrington
homeassistant/components/point/* @fredrike homeassistant/components/point/* @fredrike
homeassistant/components/powerwall/* @bdraco
homeassistant/components/proxmoxve/* @k4ds3 homeassistant/components/proxmoxve/* @k4ds3
homeassistant/components/ps4/* @ktnrg45 homeassistant/components/ps4/* @ktnrg45
homeassistant/components/ptvsd/* @swamp-ig homeassistant/components/ptvsd/* @swamp-ig
homeassistant/components/push/* @dgomes homeassistant/components/push/* @dgomes
homeassistant/components/pvoutput/* @fabaff homeassistant/components/pvoutput/* @fabaff
homeassistant/components/pvpc_hourly_pricing/* @azogue
homeassistant/components/qld_bushfire/* @exxamalte homeassistant/components/qld_bushfire/* @exxamalte
homeassistant/components/qnap/* @colinodell homeassistant/components/qnap/* @colinodell
homeassistant/components/quantum_gateway/* @cisasteelersfan homeassistant/components/quantum_gateway/* @cisasteelersfan
homeassistant/components/qvr_pro/* @oblogic7 homeassistant/components/qvr_pro/* @oblogic7
homeassistant/components/qwikswitch/* @kellerza homeassistant/components/qwikswitch/* @kellerza
homeassistant/components/rachio/* @bdraco
homeassistant/components/rainbird/* @konikvranik homeassistant/components/rainbird/* @konikvranik
homeassistant/components/raincloud/* @vanstinator homeassistant/components/raincloud/* @vanstinator
homeassistant/components/rainforest_eagle/* @gtdiehl @jcalbert homeassistant/components/rainforest_eagle/* @gtdiehl @jcalbert
@ -302,6 +317,7 @@ homeassistant/components/saj/* @fredericvl
homeassistant/components/salt/* @bjornorri homeassistant/components/salt/* @bjornorri
homeassistant/components/samsungtv/* @escoand homeassistant/components/samsungtv/* @escoand
homeassistant/components/scene/* @home-assistant/core homeassistant/components/scene/* @home-assistant/core
homeassistant/components/schluter/* @prairieapps
homeassistant/components/scrape/* @fabaff homeassistant/components/scrape/* @fabaff
homeassistant/components/script/* @home-assistant/core homeassistant/components/script/* @home-assistant/core
homeassistant/components/search/* @home-assistant/core homeassistant/components/search/* @home-assistant/core
@ -331,6 +347,7 @@ homeassistant/components/solax/* @squishykid
homeassistant/components/soma/* @ratsept homeassistant/components/soma/* @ratsept
homeassistant/components/somfy/* @tetienne homeassistant/components/somfy/* @tetienne
homeassistant/components/songpal/* @rytilahti homeassistant/components/songpal/* @rytilahti
homeassistant/components/sonos/* @amelchio
homeassistant/components/spaceapi/* @fabaff homeassistant/components/spaceapi/* @fabaff
homeassistant/components/speedtestdotnet/* @rohankapoorcom homeassistant/components/speedtestdotnet/* @rohankapoorcom
homeassistant/components/spider/* @peternijssen homeassistant/components/spider/* @peternijssen
@ -354,7 +371,7 @@ homeassistant/components/switchmate/* @danielhiversen
homeassistant/components/syncthru/* @nielstron homeassistant/components/syncthru/* @nielstron
homeassistant/components/synology_srm/* @aerialls homeassistant/components/synology_srm/* @aerialls
homeassistant/components/syslog/* @fabaff homeassistant/components/syslog/* @fabaff
homeassistant/components/tado/* @michaelarnauts homeassistant/components/tado/* @michaelarnauts @bdraco
homeassistant/components/tahoma/* @philklei homeassistant/components/tahoma/* @philklei
homeassistant/components/tankerkoenig/* @guillempages homeassistant/components/tankerkoenig/* @guillempages
homeassistant/components/tautulli/* @ludeeus homeassistant/components/tautulli/* @ludeeus

View File

@ -7,7 +7,7 @@ trigger:
- dev - dev
pr: none pr: none
schedules: schedules:
- cron: "30 0 * * *" - cron: "0 0 * * *"
displayName: "translation update" displayName: "translation update"
branches: branches:
include: include:

View File

@ -1,11 +1,11 @@
{ {
"image": "homeassistant/{arch}-homeassistant", "image": "homeassistant/{arch}-homeassistant",
"build_from": { "build_from": {
"aarch64": "homeassistant/aarch64-homeassistant-base:7.0.1", "aarch64": "homeassistant/aarch64-homeassistant-base:7.1.0",
"armhf": "homeassistant/armhf-homeassistant-base:7.0.1", "armhf": "homeassistant/armhf-homeassistant-base:7.1.0",
"armv7": "homeassistant/armv7-homeassistant-base:7.0.1", "armv7": "homeassistant/armv7-homeassistant-base:7.1.0",
"amd64": "homeassistant/amd64-homeassistant-base:7.0.1", "amd64": "homeassistant/amd64-homeassistant-base:7.1.0",
"i386": "homeassistant/i386-homeassistant-base:7.0.1" "i386": "homeassistant/i386-homeassistant-base:7.1.0"
}, },
"labels": { "labels": {
"io.hass.type": "core" "io.hass.type": "core"

29
docs/source/api/auth.rst Normal file
View File

@ -0,0 +1,29 @@
:mod:`homeassistant.auth`
=========================
.. automodule:: homeassistant.auth
:members:
homeassistant.auth.auth\_store
------------------------------
.. automodule:: homeassistant.auth.auth_store
:members:
:undoc-members:
:show-inheritance:
homeassistant.auth.const
------------------------
.. automodule:: homeassistant.auth.const
:members:
:undoc-members:
:show-inheritance:
homeassistant.auth.models
-------------------------
.. automodule:: homeassistant.auth.models
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,7 +1,7 @@
.. _bootstrap_module: .. _bootstrap_module:
:mod:`homeassistant.bootstrap` :mod:`homeassistant.bootstrap`
------------------------- ------------------------------
.. automodule:: homeassistant.bootstrap .. automodule:: homeassistant.bootstrap
:members: :members:

View File

@ -0,0 +1,170 @@
:mod:`homeassistant.components`
===============================
air\_quality
--------------------------------------------
.. automodule:: homeassistant.components.air_quality
:members:
:undoc-members:
:show-inheritance:
alarm\_control\_panel
--------------------------------------------
.. automodule:: homeassistant.components.alarm_control_panel
:members:
:undoc-members:
:show-inheritance:
binary\_sensor
--------------------------------------------
.. automodule:: homeassistant.components.binary_sensor
:members:
:undoc-members:
:show-inheritance:
camera
---------------------------
.. automodule:: homeassistant.components.camera
:members:
:undoc-members:
:show-inheritance:
calendar
---------------------------
.. automodule:: homeassistant.components.calendar
:members:
:undoc-members:
:show-inheritance:
climate
---------------------------
.. automodule:: homeassistant.components.climate
:members:
:undoc-members:
:show-inheritance:
conversation
---------------------------
.. automodule:: homeassistant.components.conversation
:members:
:undoc-members:
:show-inheritance:
cover
---------------------------
.. automodule:: homeassistant.components.cover
:members:
:undoc-members:
:show-inheritance:
device\_tracker
---------------------------
.. automodule:: homeassistant.components.device_tracker
:members:
:undoc-members:
:show-inheritance:
fan
---------------------------
.. automodule:: homeassistant.components.fan
:members:
:undoc-members:
:show-inheritance:
light
---------------------------
.. automodule:: homeassistant.components.light
:members:
:undoc-members:
:show-inheritance:
lock
---------------------------
.. automodule:: homeassistant.components.lock
:members:
:undoc-members:
:show-inheritance:
media\_player
---------------------------
.. automodule:: homeassistant.components.media_player
:members:
:undoc-members:
:show-inheritance:
notify
---------------------------
.. automodule:: homeassistant.components.notify
:members:
:undoc-members:
:show-inheritance:
remote
---------------------------
.. automodule:: homeassistant.components.remote
:members:
:undoc-members:
:show-inheritance:
switch
---------------------------
.. automodule:: homeassistant.components.switch
:members:
:undoc-members:
:show-inheritance:
sensor
-------------------------------------
.. automodule:: homeassistant.components.sensor
:members:
:undoc-members:
:show-inheritance:
vacuum
-------------------------------------
.. automodule:: homeassistant.components.vacuum
:members:
:undoc-members:
:show-inheritance:
water\_heater
-------------------------------------
.. automodule:: homeassistant.components.water_heater
:members:
:undoc-members:
:show-inheritance:
weather
---------------------------
.. automodule:: homeassistant.components.weather
:members:
:undoc-members:
:show-inheritance:
webhook
---------------------------
.. automodule:: homeassistant.components.webhook
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
.. _config_entries_module:
:mod:`homeassistant.config_entries`
-----------------------------------
.. automodule:: homeassistant.config_entries
:members:

View File

@ -4,35 +4,4 @@
------------------------- -------------------------
.. automodule:: homeassistant.core .. automodule:: homeassistant.core
.. autoclass:: Config
:members: :members:
.. autoclass:: Event
:members:
.. autoclass:: EventBus
:members:
.. autoclass:: HomeAssistant
:members:
.. autoclass:: State
:members:
.. autoclass:: StateMachine
:members:
.. autoclass:: ServiceCall
:members:
.. autoclass:: ServiceRegistry
:members:
Module contents
---------------
.. automodule:: homeassistant.core
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
.. _data_entry_flow_module:
:mod:`homeassistant.data_entry_flow`
-----------------------------
.. automodule:: homeassistant.data_entry_flow
:members:

View File

@ -1,10 +0,0 @@
.. _components_device_tracker_module:
:mod:`homeassistant.components.device_tracker`
----------------------------------------------
.. automodule:: homeassistant.components.device_tracker
:members:
.. autoclass:: Device
:members:

View File

@ -1,12 +0,0 @@
.. _helpers_entity_module:
:mod:`homeassistant.helpers.entity`
-----------------------------------
.. automodule:: homeassistant.helpers.entity
.. autoclass:: Entity
:members:
.. autoclass:: ToggleEntity
:members:

View File

@ -1,20 +0,0 @@
.. _helpers_event_module:
:mod:`homeassistant.helpers.event`
----------------------------------
.. automodule:: homeassistant.helpers.event
.. autofunction:: track_state_change
.. autofunction:: track_point_in_time
.. autofunction:: track_point_in_utc_time
.. autofunction:: track_sunrise
.. autofunction:: track_sunset
.. autofunction:: track_utc_time_change
.. autofunction:: track_time_change

View File

@ -0,0 +1,7 @@
.. _exceptions_module:
:mod:`homeassistant.exceptions`
-------------------------------
.. automodule:: homeassistant.exceptions
:members:

View File

@ -1,287 +1,335 @@
homeassistant.helpers package :mod:`homeassistant.helpers`
============================= ============================
Submodules
----------
homeassistant.helpers.aiohttp_client module
-------------------------------------------
.. automodule:: homeassistant.helpers.aiohttp_client
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.area_registry module
------------------------------------------
.. automodule:: homeassistant.helpers.area_registry
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.condition module
--------------------------------------
.. automodule:: homeassistant.helpers.condition
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.config_entry_flow module
----------------------------------------------
.. automodule:: homeassistant.helpers.config_entry_flow
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.config_validation module
----------------------------------------------
.. automodule:: homeassistant.helpers.config_validation
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.data_entry_flow module
--------------------------------------------
.. automodule:: homeassistant.helpers.data_entry_flow
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.deprecation module
----------------------------------------
.. automodule:: homeassistant.helpers.deprecation
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.device_registry module
--------------------------------------------
.. automodule:: homeassistant.helpers.device_registry
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.discovery module
--------------------------------------
.. automodule:: homeassistant.helpers.discovery
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.dispatcher module
---------------------------------------
.. automodule:: homeassistant.helpers.dispatcher
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.entity module
-----------------------------------
.. automodule:: homeassistant.helpers.entity
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.entity_component module
---------------------------------------------
.. automodule:: homeassistant.helpers.entity_component
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.entity_platform module
--------------------------------------------
.. automodule:: homeassistant.helpers.entity_platform
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.entity_registry module
--------------------------------------------
.. automodule:: homeassistant.helpers.entity_registry
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.entity_values module
------------------------------------------
.. automodule:: homeassistant.helpers.entity_values
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.entityfilter module
-----------------------------------------
.. automodule:: homeassistant.helpers.entityfilter
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.event module
----------------------------------
.. automodule:: homeassistant.helpers.event
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.icon module
---------------------------------
.. automodule:: homeassistant.helpers.icon
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.intent module
-----------------------------------
.. automodule:: homeassistant.helpers.intent
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.json module
---------------------------------
.. automodule:: homeassistant.helpers.json
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.location module
-------------------------------------
.. automodule:: homeassistant.helpers.location
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.logging module
------------------------------------
.. automodule:: homeassistant.helpers.logging
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.restore_state module
------------------------------------------
.. automodule:: homeassistant.helpers.restore_state
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.script module
-----------------------------------
.. automodule:: homeassistant.helpers.script
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.service module
------------------------------------
.. automodule:: homeassistant.helpers.service
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.signal module
-----------------------------------
.. automodule:: homeassistant.helpers.signal
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.state module
----------------------------------
.. automodule:: homeassistant.helpers.state
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.storage module
------------------------------------
.. automodule:: homeassistant.helpers.storage
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.sun module
--------------------------------
.. automodule:: homeassistant.helpers.sun
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.system_info module
----------------------------------------
.. automodule:: homeassistant.helpers.system_info
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.temperature module
----------------------------------------
.. automodule:: homeassistant.helpers.temperature
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.template module
-------------------------------------
.. automodule:: homeassistant.helpers.template
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.translation module
-----------------------------------------
.. automodule:: homeassistant.helpers.translation
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.typing module
-----------------------------------
.. automodule:: homeassistant.helpers.typing
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: homeassistant.helpers .. automodule:: homeassistant.helpers
:members: :members:
:undoc-members: :undoc-members:
:show-inheritance: :show-inheritance:
homeassistant.helpers.aiohttp\_client
-------------------------------------
.. automodule:: homeassistant.helpers.aiohttp_client
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.area\_registry
------------------------------------
.. automodule:: homeassistant.helpers.area_registry
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.check\_config
-----------------------------------
.. automodule:: homeassistant.helpers.check_config
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.collection
--------------------------------
.. automodule:: homeassistant.helpers.collection
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.condition
-------------------------------
.. automodule:: homeassistant.helpers.condition
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.config\_entry\_flow
-----------------------------------------
.. automodule:: homeassistant.helpers.config_entry_flow
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.config\_entry\_oauth2\_flow
-------------------------------------------------
.. automodule:: homeassistant.helpers.config_entry_oauth2_flow
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.config\_validation
----------------------------------------
.. automodule:: homeassistant.helpers.config_validation
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.data\_entry\_flow
---------------------------------------
.. automodule:: homeassistant.helpers.data_entry_flow
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.debounce
------------------------------
.. automodule:: homeassistant.helpers.debounce
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.deprecation
---------------------------------
.. automodule:: homeassistant.helpers.deprecation
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.device\_registry
--------------------------------------
.. automodule:: homeassistant.helpers.device_registry
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.discovery
-------------------------------
.. automodule:: homeassistant.helpers.discovery
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.dispatcher
--------------------------------
.. automodule:: homeassistant.helpers.dispatcher
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.entity
----------------------------
.. automodule:: homeassistant.helpers.entity
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.entity\_component
---------------------------------------
.. automodule:: homeassistant.helpers.entity_component
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.entity\_platform
--------------------------------------
.. automodule:: homeassistant.helpers.entity_platform
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.entity\_registry
--------------------------------------
.. automodule:: homeassistant.helpers.entity_registry
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.entity\_values
------------------------------------
.. automodule:: homeassistant.helpers.entity_values
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.entityfilter
----------------------------------
.. automodule:: homeassistant.helpers.entityfilter
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.event
---------------------------
.. automodule:: homeassistant.helpers.event
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.icon
--------------------------
.. automodule:: homeassistant.helpers.icon
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.integration\_platform
-------------------------------------------
.. automodule:: homeassistant.helpers.integration_platform
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.intent
----------------------------
.. automodule:: homeassistant.helpers.intent
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.json
--------------------------
.. automodule:: homeassistant.helpers.json
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.location
------------------------------
.. automodule:: homeassistant.helpers.location
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.logging
-----------------------------
.. automodule:: homeassistant.helpers.logging
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.network
-----------------------------
.. automodule:: homeassistant.helpers.network
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.restore\_state
------------------------------------
.. automodule:: homeassistant.helpers.restore_state
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.script
----------------------------
.. automodule:: homeassistant.helpers.script
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.service
-----------------------------
.. automodule:: homeassistant.helpers.service
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.signal
-----------------------------
.. automodule:: homeassistant.helpers.signal
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.state
---------------------------
.. automodule:: homeassistant.helpers.state
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.storage
-----------------------------
.. automodule:: homeassistant.helpers.storage
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.sun
-------------------------
.. automodule:: homeassistant.helpers.sun
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.system\_info
----------------------------------
.. automodule:: homeassistant.helpers.system_info
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.temperature
---------------------------------
.. automodule:: homeassistant.helpers.temperature
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.template
------------------------------
.. automodule:: homeassistant.helpers.template
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.translation
---------------------------------
.. automodule:: homeassistant.helpers.translation
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.typing
----------------------------
.. automodule:: homeassistant.helpers.typing
:members:
:undoc-members:
:show-inheritance:
homeassistant.helpers.update\_coordinator
-----------------------------------------
.. automodule:: homeassistant.helpers.update_coordinator
:members:
:undoc-members:
:show-inheritance:

View File

@ -1,70 +0,0 @@
homeassistant package
=====================
Subpackages
-----------
.. toctree::
helpers
util
Submodules
----------
bootstrap module
------------------------------
.. automodule:: homeassistant.bootstrap
:members:
:undoc-members:
:show-inheritance:
config module
---------------------------
.. automodule:: homeassistant.config
:members:
:undoc-members:
:show-inheritance:
const module
--------------------------
.. automodule:: homeassistant.const
:members:
:undoc-members:
:show-inheritance:
core module
-------------------------
.. automodule:: homeassistant.core
:members:
:undoc-members:
:show-inheritance:
exceptions module
-------------------------------
.. automodule:: homeassistant.exceptions
:members:
:undoc-members:
:show-inheritance:
loader module
---------------------------
.. automodule:: homeassistant.loader
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: homeassistant
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
.. _loader_module:
:mod:`homeassistant.loader`
---------------------------
.. automodule:: homeassistant.loader
:members:

View File

@ -1,86 +1,159 @@
homeassistant.util package :mod:`homeassistant.util`
========================== =========================
Submodules
----------
homeassistant.util.async_ module
-------------------------------
.. automodule:: homeassistant.util.async_
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.color module
-------------------------------
.. automodule:: homeassistant.util.color
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.distance module
----------------------------------
.. automodule:: homeassistant.util.distance
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.dt module
----------------------------
.. automodule:: homeassistant.util.dt
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.location module
----------------------------------
.. automodule:: homeassistant.util.location
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.package module
---------------------------------
.. automodule:: homeassistant.util.package
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.temperature module
-------------------------------------
.. automodule:: homeassistant.util.temperature
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.unit_system module
-------------------------------------
.. automodule:: homeassistant.util.unit_system
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.yaml module
------------------------------
.. automodule:: homeassistant.util.yaml
:members:
:undoc-members:
:show-inheritance:
Module contents
---------------
.. automodule:: homeassistant.util .. automodule:: homeassistant.util
:members: :members:
:undoc-members: :undoc-members:
:show-inheritance: :show-inheritance:
homeassistant.util.yaml
-----------------------
.. automodule:: homeassistant.util.yaml
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.aiohttp
--------------------------
.. automodule:: homeassistant.util.aiohttp
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.async\_
--------------------------
.. automodule:: homeassistant.util.async_
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.color
------------------------
.. automodule:: homeassistant.util.color
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.decorator
----------------------------
.. automodule:: homeassistant.util.decorator
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.distance
---------------------------
.. automodule:: homeassistant.util.distance
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.dt
---------------------
.. automodule:: homeassistant.util.dt
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.json
-----------------------
.. automodule:: homeassistant.util.json
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.location
---------------------------
.. automodule:: homeassistant.util.location
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.logging
--------------------------
.. automodule:: homeassistant.util.logging
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.network
--------------------------
.. automodule:: homeassistant.util.network
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.package
--------------------------
.. automodule:: homeassistant.util.package
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.pil
----------------------
.. automodule:: homeassistant.util.pil
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.pressure
---------------------------
.. automodule:: homeassistant.util.pressure
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.ruamel\_yaml
-------------------------------
.. automodule:: homeassistant.util.ruamel_yaml
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.ssl
----------------------
.. automodule:: homeassistant.util.ssl
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.temperature
------------------------------
.. automodule:: homeassistant.util.temperature
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.unit\_system
-------------------------------
.. automodule:: homeassistant.util.unit_system
:members:
:undoc-members:
:show-inheritance:
homeassistant.util.volume
-------------------------
.. automodule:: homeassistant.util.volume
:members:
:undoc-members:
:show-inheritance:

View File

@ -26,7 +26,7 @@ from homeassistant.const import __short_version__, __version__
PROJECT_NAME = 'Home Assistant' PROJECT_NAME = 'Home Assistant'
PROJECT_PACKAGE_NAME = 'homeassistant' PROJECT_PACKAGE_NAME = 'homeassistant'
PROJECT_AUTHOR = 'The Home Assistant Authors' PROJECT_AUTHOR = 'The Home Assistant Authors'
PROJECT_COPYRIGHT = ' 2013-2018, {}'.format(PROJECT_AUTHOR) PROJECT_COPYRIGHT = ' 2013-2020, {}'.format(PROJECT_AUTHOR)
PROJECT_LONG_DESCRIPTION = ('Home Assistant is an open-source ' PROJECT_LONG_DESCRIPTION = ('Home Assistant is an open-source '
'home automation platform running on Python 3. ' 'home automation platform running on Python 3. '
'Track and control all devices at home and ' 'Track and control all devices at home and '

View File

@ -339,7 +339,7 @@ def main() -> int:
if args.pid_file: if args.pid_file:
write_pid(args.pid_file) write_pid(args.pid_file)
exit_code = asyncio.run(setup_and_run_hass(config_dir, args)) exit_code = asyncio.run(setup_and_run_hass(config_dir, args), debug=args.debug)
if exit_code == RESTART_EXIT_CODE and not args.runner: if exit_code == RESTART_EXIT_CODE and not args.runner:
try_to_restart() try_to_restart()

View File

@ -215,12 +215,14 @@ class AuthManager:
return user return user
async def async_create_user(self, name: str) -> models.User: async def async_create_user(
self, name: str, group_ids: Optional[List[str]] = None
) -> models.User:
"""Create a user.""" """Create a user."""
kwargs: Dict[str, Any] = { kwargs: Dict[str, Any] = {
"name": name, "name": name,
"is_active": True, "is_active": True,
"group_ids": [GROUP_ID_ADMIN], "group_ids": group_ids or [],
} }
if await self._user_should_be_owner(): if await self._user_should_be_owner():

View File

@ -123,4 +123,8 @@ class Credentials:
is_new = attr.ib(type=bool, default=True) is_new = attr.ib(type=bool, default=True)
UserMeta = NamedTuple("UserMeta", [("name", Optional[str]), ("is_active", bool)]) class UserMeta(NamedTuple):
"""User metadata."""
name: Optional[str]
is_active: bool

View File

@ -20,6 +20,7 @@ from homeassistant.const import (
REQUIRED_NEXT_PYTHON_VER, REQUIRED_NEXT_PYTHON_VER,
) )
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.typing import ConfigType
from homeassistant.setup import DATA_SETUP, async_setup_component from homeassistant.setup import DATA_SETUP, async_setup_component
from homeassistant.util.logging import AsyncHandler from homeassistant.util.logging import AsyncHandler
from homeassistant.util.package import async_get_user_site, is_virtual_env from homeassistant.util.package import async_get_user_site, is_virtual_env
@ -133,7 +134,7 @@ async def async_setup_hass(
async def async_from_config_dict( async def async_from_config_dict(
config: Dict[str, Any], hass: core.HomeAssistant config: ConfigType, hass: core.HomeAssistant
) -> Optional[core.HomeAssistant]: ) -> Optional[core.HomeAssistant]:
"""Try to configure Home Assistant from a configuration dictionary. """Try to configure Home Assistant from a configuration dictionary.
@ -324,15 +325,30 @@ async def _async_set_up_integrations(
hass: core.HomeAssistant, config: Dict[str, Any] hass: core.HomeAssistant, config: Dict[str, Any]
) -> None: ) -> None:
"""Set up all the integrations.""" """Set up all the integrations."""
async def async_setup_multi_components(domains: Set[str]) -> None:
"""Set up multiple domains. Log on failure."""
futures = {
domain: hass.async_create_task(async_setup_component(hass, domain, config))
for domain in domains
}
await asyncio.wait(futures.values())
errors = [domain for domain in domains if futures[domain].exception()]
for domain in errors:
exception = futures[domain].exception()
_LOGGER.error(
"Error setting up integration %s - received exception",
domain,
exc_info=(type(exception), exception, exception.__traceback__),
)
domains = _get_domains(hass, config) domains = _get_domains(hass, config)
# Start up debuggers. Start these first in case they want to wait. # Start up debuggers. Start these first in case they want to wait.
debuggers = domains & DEBUGGER_INTEGRATIONS debuggers = domains & DEBUGGER_INTEGRATIONS
if debuggers: if debuggers:
_LOGGER.debug("Starting up debuggers %s", debuggers) _LOGGER.debug("Starting up debuggers %s", debuggers)
await asyncio.gather( await async_setup_multi_components(debuggers)
*(async_setup_component(hass, domain, config) for domain in debuggers)
)
domains -= DEBUGGER_INTEGRATIONS domains -= DEBUGGER_INTEGRATIONS
# Resolve all dependencies of all components so we can find the logging # Resolve all dependencies of all components so we can find the logging
@ -357,9 +373,7 @@ async def _async_set_up_integrations(
if logging_domains: if logging_domains:
_LOGGER.info("Setting up %s", logging_domains) _LOGGER.info("Setting up %s", logging_domains)
await asyncio.gather( await async_setup_multi_components(logging_domains)
*(async_setup_component(hass, domain, config) for domain in logging_domains)
)
# Kick off loading the registries. They don't need to be awaited. # Kick off loading the registries. They don't need to be awaited.
asyncio.gather( asyncio.gather(
@ -369,9 +383,7 @@ async def _async_set_up_integrations(
) )
if stage_1_domains: if stage_1_domains:
await asyncio.gather( await async_setup_multi_components(stage_1_domains)
*(async_setup_component(hass, domain, config) for domain in stage_1_domains)
)
# Load all integrations # Load all integrations
after_dependencies: Dict[str, Set[str]] = {} after_dependencies: Dict[str, Set[str]] = {}
@ -400,9 +412,7 @@ async def _async_set_up_integrations(
_LOGGER.debug("Setting up %s", domains_to_load) _LOGGER.debug("Setting up %s", domains_to_load)
await asyncio.gather( await async_setup_multi_components(domains_to_load)
*(async_setup_component(hass, domain, config) for domain in domains_to_load)
)
last_load = domains_to_load last_load = domains_to_load
stage_2_domains -= domains_to_load stage_2_domains -= domains_to_load
@ -412,9 +422,7 @@ async def _async_set_up_integrations(
if stage_2_domains: if stage_2_domains:
_LOGGER.debug("Final set up: %s", stage_2_domains) _LOGGER.debug("Final set up: %s", stage_2_domains)
await asyncio.gather( await async_setup_multi_components(stage_2_domains)
*(async_setup_component(hass, domain, config) for domain in stage_2_domains)
)
# Wrap up startup # Wrap up startup
await hass.async_block_till_done() await hass.async_block_till_done()

View File

@ -0,0 +1,26 @@
{
"config": {
"abort": {
"already_configured": "L'amfitri\u00f3 ja est\u00e0 configurat"
},
"error": {
"login": "Error d\u2019inici de sessi\u00f3: comprova el nom d'usuari i la contrasenya",
"unknown": "Error desconegut: torna-ho a provar m\u00e9s tard o revisa la configuraci\u00f3"
},
"step": {
"user": {
"data": {
"api_version": "Versi\u00f3 DSM",
"host": "Amfitri\u00f3",
"name": "Nom",
"password": "Contrasenya",
"port": "Port",
"ssl": "Utilitza SSL/TLS per connectar-te al servidor NAS",
"username": "Nom d'usuari"
},
"title": "Synology DSM"
}
},
"title": "Synology DSM"
}
}

View File

@ -0,0 +1,18 @@
{
"config": {
"step": {
"link": {
"data": {
"password": "Adgangskode",
"username": "Brugernavn"
}
},
"user": {
"data": {
"password": "Adgangskode",
"username": "Brugernavn"
}
}
}
}
}

View File

@ -0,0 +1,38 @@
{
"config": {
"abort": {
"already_configured": "Host already configured"
},
"error": {
"login": "Login error: please check your username & password",
"unknown": "Unknown error: please retry later or an other configuration"
},
"flow_title": "Synology DSM {name} ({host})",
"step": {
"link": {
"data": {
"api_version": "DSM version",
"password": "Password",
"port": "Port (Optional)",
"ssl": "Use SSL/TLS to connect to your NAS",
"username": "Username"
},
"description": "Do you want to setup {name} ({host})?",
"title": "Synology DSM"
},
"user": {
"data": {
"api_version": "DSM version",
"host": "Host",
"name": "Name",
"password": "Password",
"port": "Port (Optional)",
"ssl": "Use SSL/TLS to connect to your NAS",
"username": "Username"
},
"title": "Synology DSM"
}
},
"title": "Synology DSM"
}
}

View File

@ -0,0 +1,26 @@
{
"config": {
"abort": {
"already_configured": "El host ya est\u00e1 configurado."
},
"error": {
"login": "Error de inicio de sesi\u00f3n: comprueba tu direcci\u00f3n de correo electr\u00f3nico y contrase\u00f1a",
"unknown": "Error desconocido: por favor vuelve a intentarlo m\u00e1s tarde o usa otra configuraci\u00f3n"
},
"step": {
"user": {
"data": {
"api_version": "Versi\u00f3n del DSM",
"host": "Host",
"name": "Nombre",
"password": "Contrase\u00f1a",
"port": "Puerto",
"ssl": "Usar SSL/TLS para conectar con tu NAS",
"username": "Usuario"
},
"title": "Synology DSM"
}
},
"title": "Synology DSM"
}
}

View File

@ -0,0 +1,26 @@
{
"config": {
"abort": {
"already_configured": "\ud638\uc2a4\ud2b8\uac00 \uc774\ubbf8 \uad6c\uc131\ub418\uc5c8\uc2b5\ub2c8\ub2e4."
},
"error": {
"login": "\ub85c\uadf8\uc778 \uc624\ub958: \uc0ac\uc6a9\uc790 \uc774\ub984 \ubc0f \ube44\ubc00\ubc88\ud638\ub97c \ud655\uc778\ud574\uc8fc\uc138\uc694",
"unknown": "\uc54c \uc218 \uc5c6\ub294 \uc624\ub958\uc785\ub2c8\ub2e4. \ub098\uc911\uc5d0 \ub2e4\uc2dc \uc2dc\ub3c4\ud558\uac70\ub098 \ub2e4\ub978 \uad6c\uc131\uc744 \uc2dc\ub3c4\ud574\ubcf4\uc138\uc694"
},
"step": {
"user": {
"data": {
"api_version": "DSM \ubc84\uc804",
"host": "\ud638\uc2a4\ud2b8",
"name": "\uc774\ub984",
"password": "\ube44\ubc00\ubc88\ud638",
"port": "\ud3ec\ud2b8",
"ssl": "SSL/TLS \ub97c \uc0ac\uc6a9\ud558\uc5ec NAS \uc5d0 \uc5f0\uacb0",
"username": "\uc0ac\uc6a9\uc790 \uc774\ub984"
},
"title": "Synology DSM"
}
},
"title": "Synology DSM"
}
}

View File

@ -0,0 +1,26 @@
{
"config": {
"abort": {
"already_configured": "Apparat ass scho konfigur\u00e9iert"
},
"error": {
"login": "Feeler beim Login: iwwerpr\u00e9if de Benotzernumm & Passwuert",
"unknown": "Onbekannte Feeler: prob\u00e9ier sp\u00e9ider nach emol oder mat enger aner Konfiguratioun"
},
"step": {
"user": {
"data": {
"api_version": "DSM Versioun",
"host": "Apparat",
"name": "Numm",
"password": "Passwuert",
"port": "Port",
"ssl": "Benotzt SSL/TLS fir sech mam NAS ze verbannen",
"username": "Benotzernumm"
},
"title": "Synology DSM"
}
},
"title": "Synology DSM"
}
}

View File

@ -0,0 +1,37 @@
{
"config": {
"abort": {
"already_configured": "Host is al geconfigureerd."
},
"error": {
"unknown": "Onbekende fout: probeer het later opnieuw of een andere configuratie"
},
"flow_title": "Synology DSM {name} ({host})",
"step": {
"link": {
"data": {
"api_version": "DSM-versie",
"password": "Wachtwoord",
"port": "Poort (optioneel)",
"ssl": "Gebruik SSL/TLS om verbinding te maken met uw NAS",
"username": "Gebruikersnaam"
},
"description": "Wil je {name} ({host}) instellen?",
"title": "Synology DSM"
},
"user": {
"data": {
"api_version": "DSM-versie",
"host": "Host",
"name": "Naam",
"password": "Wachtwoord",
"port": "Poort (optioneel)",
"ssl": "Gebruik SSL/TLS om verbinding te maken met uw NAS",
"username": "Gebruikersnaam"
},
"title": "Synology DSM"
}
},
"title": "Synology DSM"
}
}

View File

@ -0,0 +1,26 @@
{
"config": {
"abort": {
"already_configured": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u044d\u0442\u043e\u0433\u043e \u0445\u043e\u0441\u0442\u0430 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0430."
},
"error": {
"login": "\u041e\u0448\u0438\u0431\u043a\u0430 \u0432\u0445\u043e\u0434\u0430: \u043f\u0440\u043e\u0432\u0435\u0440\u044c\u0442\u0435 \u043b\u043e\u0433\u0438\u043d \u0438 \u043f\u0430\u0440\u043e\u043b\u044c.",
"unknown": "\u041d\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u0430\u044f \u043e\u0448\u0438\u0431\u043a\u0430: \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0439\u0442\u0435 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u0442\u044c\u0441\u044f \u0441 \u0434\u0440\u0443\u0433\u043e\u0439 \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u0435\u0439 \u0438\u043b\u0438 \u043f\u043e\u0432\u0442\u043e\u0440\u0438\u0442\u0435 \u043f\u043e\u043f\u044b\u0442\u043a\u0443 \u043f\u043e\u0437\u0436\u0435."
},
"step": {
"user": {
"data": {
"api_version": "\u0412\u0435\u0440\u0441\u0438\u044f DSM",
"host": "\u0425\u043e\u0441\u0442",
"name": "\u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435",
"password": "\u041f\u0430\u0440\u043e\u043b\u044c",
"port": "\u041f\u043e\u0440\u0442",
"ssl": "\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c SSL / TLS \u0434\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f",
"username": "\u041b\u043e\u0433\u0438\u043d"
},
"title": "Synology DSM"
}
},
"title": "Synology DSM"
}
}

View File

@ -17,6 +17,6 @@
"title": "Fyll ut innloggingsinformasjonen for Abode" "title": "Fyll ut innloggingsinformasjonen for Abode"
} }
}, },
"title": "Abode" "title": ""
} }
} }

View File

@ -263,7 +263,6 @@ def setup_abode_events(hass):
TIMELINE.TEST_GROUP, TIMELINE.TEST_GROUP,
TIMELINE.CAPTURE_GROUP, TIMELINE.CAPTURE_GROUP,
TIMELINE.DEVICE_GROUP, TIMELINE.DEVICE_GROUP,
TIMELINE.AUTOMATION_EDIT_GROUP,
] ]
for event in events: for event in events:
@ -343,21 +342,14 @@ class AbodeDevice(Entity):
class AbodeAutomation(Entity): class AbodeAutomation(Entity):
"""Representation of an Abode automation.""" """Representation of an Abode automation."""
def __init__(self, data, automation, event=None): def __init__(self, data, automation):
"""Initialize for Abode automation.""" """Initialize for Abode automation."""
self._data = data self._data = data
self._automation = automation self._automation = automation
self._event = event
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Subscribe to a group of Abode timeline events.""" """Set up automation entity."""
if self._event: self.hass.data[DOMAIN].entity_ids.add(self.entity_id)
self.hass.async_add_job(
self._data.abode.events.add_event_callback,
self._event,
self._update_callback,
)
self.hass.data[DOMAIN].entity_ids.add(self.entity_id)
@property @property
def should_poll(self): def should_poll(self):
@ -385,8 +377,3 @@ class AbodeAutomation(Entity):
def unique_id(self): def unique_id(self):
"""Return a unique ID to use for this automation.""" """Return a unique ID to use for this automation."""
return self._automation.automation_id return self._automation.automation_id
def _update_callback(self, device):
"""Update the automation state."""
self._automation.refresh()
self.schedule_update_ha_state()

View File

@ -1,7 +1,10 @@
"""Support for Abode Security System binary sensors.""" """Support for Abode Security System binary sensors."""
import abodepy.helpers.constants as CONST import abodepy.helpers.constants as CONST
from homeassistant.components.binary_sensor import BinarySensorDevice from homeassistant.components.binary_sensor import (
DEVICE_CLASS_WINDOW,
BinarySensorDevice,
)
from . import AbodeDevice from . import AbodeDevice
from .const import DOMAIN from .const import DOMAIN
@ -38,4 +41,6 @@ class AbodeBinarySensor(AbodeDevice, BinarySensorDevice):
@property @property
def device_class(self): def device_class(self):
"""Return the class of the binary sensor.""" """Return the class of the binary sensor."""
if self._device.get_value("is_window") == "1":
return DEVICE_CLASS_WINDOW
return self._device.generic_type return self._device.generic_type

View File

@ -1,6 +1,5 @@
"""Support for Abode Security System switches.""" """Support for Abode Security System switches."""
import abodepy.helpers.constants as CONST import abodepy.helpers.constants as CONST
import abodepy.helpers.timeline as TIMELINE
from homeassistant.components.switch import SwitchDevice from homeassistant.components.switch import SwitchDevice
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
@ -24,9 +23,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
entities.append(AbodeSwitch(data, device)) entities.append(AbodeSwitch(data, device))
for automation in data.abode.get_automations(): for automation in data.abode.get_automations():
entities.append( entities.append(AbodeAutomationSwitch(data, automation))
AbodeAutomationSwitch(data, automation, TIMELINE.AUTOMATION_EDIT_GROUP)
)
async_add_entities(entities) async_add_entities(entities)
@ -52,7 +49,7 @@ class AbodeAutomationSwitch(AbodeAutomation, SwitchDevice):
"""A switch implementation for Abode automations.""" """A switch implementation for Abode automations."""
async def async_added_to_hass(self): async def async_added_to_hass(self):
"""Subscribe Abode events.""" """Set up trigger automation service."""
await super().async_added_to_hass() await super().async_added_to_hass()
signal = f"abode_trigger_automation_{self.entity_id}" signal = f"abode_trigger_automation_{self.entity_id}"

View File

@ -18,7 +18,7 @@
"data": { "data": {
"host": "Vert", "host": "Vert",
"password": "Passord", "password": "Passord",
"port": "Port", "port": "",
"ssl": "AdGuard Hjem bruker et SSL-sertifikat", "ssl": "AdGuard Hjem bruker et SSL-sertifikat",
"username": "Brukernavn", "username": "Brukernavn",
"verify_ssl": "AdGuard Home bruker et riktig sertifikat" "verify_ssl": "AdGuard Home bruker et riktig sertifikat"

View File

@ -3,7 +3,7 @@
"name": "AdGuard Home", "name": "AdGuard Home",
"config_flow": true, "config_flow": true,
"documentation": "https://www.home-assistant.io/integrations/adguard", "documentation": "https://www.home-assistant.io/integrations/adguard",
"requirements": ["adguardhome==0.4.1"], "requirements": ["adguardhome==0.4.2"],
"dependencies": [], "dependencies": [],
"codeowners": ["@frenck"] "codeowners": ["@frenck"]
} }

View File

@ -2,7 +2,6 @@
"config": { "config": {
"error": { "error": {
"auth": "API \u043a\u043b\u044e\u0447\u044a\u0442 \u043d\u0435 \u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0435\u043d.", "auth": "API \u043a\u043b\u044e\u0447\u044a\u0442 \u043d\u0435 \u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u0435\u043d.",
"name_exists": "\u0418\u043c\u0435\u0442\u043e \u0432\u0435\u0447\u0435 \u0441\u044a\u0449\u0435\u0441\u0442\u0432\u0443\u0432\u0430.",
"wrong_location": "\u0412 \u0442\u0430\u0437\u0438 \u043e\u0431\u043b\u0430\u0441\u0442 \u043d\u044f\u043c\u0430 \u0438\u0437\u043c\u0435\u0440\u0432\u0430\u0442\u0435\u043b\u043d\u0438 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u043d\u0430 Airly." "wrong_location": "\u0412 \u0442\u0430\u0437\u0438 \u043e\u0431\u043b\u0430\u0441\u0442 \u043d\u044f\u043c\u0430 \u0438\u0437\u043c\u0435\u0440\u0432\u0430\u0442\u0435\u043b\u043d\u0438 \u0441\u0442\u0430\u043d\u0446\u0438\u0438 \u043d\u0430 Airly."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "La clau API no \u00e9s correcta.", "auth": "La clau API no \u00e9s correcta.",
"name_exists": "El nom ja existeix.",
"wrong_location": "No hi ha estacions de mesura Airly en aquesta zona." "wrong_location": "No hi ha estacions de mesura Airly en aquesta zona."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "API-n\u00f8glen er ikke korrekt.", "auth": "API-n\u00f8glen er ikke korrekt.",
"name_exists": "Navnet findes allerede.",
"wrong_location": "Ingen Airly-m\u00e5lestationer i dette omr\u00e5de." "wrong_location": "Ingen Airly-m\u00e5lestationer i dette omr\u00e5de."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "Der API-Schl\u00fcssel ist nicht korrekt.", "auth": "Der API-Schl\u00fcssel ist nicht korrekt.",
"name_exists": "Name existiert bereits",
"wrong_location": "Keine Airly Luftmessstation an diesem Ort" "wrong_location": "Keine Airly Luftmessstation an diesem Ort"
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "API key is not correct.", "auth": "API key is not correct.",
"name_exists": "Name already exists.",
"wrong_location": "No Airly measuring stations in this area." "wrong_location": "No Airly measuring stations in this area."
}, },
"step": { "step": {

View File

@ -2,7 +2,6 @@
"config": { "config": {
"error": { "error": {
"auth": "La clave API no es correcta.", "auth": "La clave API no es correcta.",
"name_exists": "El nombre ya existe.",
"wrong_location": "No hay estaciones de medici\u00f3n Airly en esta \u00e1rea." "wrong_location": "No hay estaciones de medici\u00f3n Airly en esta \u00e1rea."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "La clave de la API no es correcta.", "auth": "La clave de la API no es correcta.",
"name_exists": "El nombre ya existe.",
"wrong_location": "No hay estaciones de medici\u00f3n Airly en esta zona." "wrong_location": "No hay estaciones de medici\u00f3n Airly en esta zona."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "La cl\u00e9 API n'est pas correcte.", "auth": "La cl\u00e9 API n'est pas correcte.",
"name_exists": "Le nom existe d\u00e9j\u00e0.",
"wrong_location": "Aucune station de mesure Airly dans cette zone." "wrong_location": "Aucune station de mesure Airly dans cette zone."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "Az API kulcs nem megfelel\u0151.", "auth": "Az API kulcs nem megfelel\u0151.",
"name_exists": "A n\u00e9v m\u00e1r l\u00e9tezik",
"wrong_location": "Ezen a ter\u00fcleten nincs Airly m\u00e9r\u0151\u00e1llom\u00e1s." "wrong_location": "Ezen a ter\u00fcleten nincs Airly m\u00e9r\u0151\u00e1llom\u00e1s."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "La chiave API non \u00e8 corretta.", "auth": "La chiave API non \u00e8 corretta.",
"name_exists": "Il nome \u00e8 gi\u00e0 esistente",
"wrong_location": "Nessuna stazione di misurazione Airly in quest'area." "wrong_location": "Nessuna stazione di misurazione Airly in quest'area."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "API \ud0a4\uac00 \uc62c\ubc14\ub974\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.", "auth": "API \ud0a4\uac00 \uc62c\ubc14\ub974\uc9c0 \uc54a\uc2b5\ub2c8\ub2e4.",
"name_exists": "\uc774\ub984\uc774 \uc774\ubbf8 \uc874\uc7ac\ud569\ub2c8\ub2e4.",
"wrong_location": "\uc774 \uc9c0\uc5ed\uc5d0\ub294 Airly \uce21\uc815 \uc2a4\ud14c\uc774\uc158\uc774 \uc5c6\uc2b5\ub2c8\ub2e4." "wrong_location": "\uc774 \uc9c0\uc5ed\uc5d0\ub294 Airly \uce21\uc815 \uc2a4\ud14c\uc774\uc158\uc774 \uc5c6\uc2b5\ub2c8\ub2e4."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "Api Schl\u00ebssel ass net korrekt.", "auth": "Api Schl\u00ebssel ass net korrekt.",
"name_exists": "Numm g\u00ebtt et schonn",
"wrong_location": "Keng Airly Moos Statioun an d\u00ebsem Ber\u00e4ich" "wrong_location": "Keng Airly Moos Statioun an d\u00ebsem Ber\u00e4ich"
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "API-sleutel is niet correct.", "auth": "API-sleutel is niet correct.",
"name_exists": "Naam bestaat al.",
"wrong_location": "Geen Airly meetstations in dit gebied." "wrong_location": "Geen Airly meetstations in dit gebied."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "API-n\u00f8kkelen er ikke korrekt.", "auth": "API-n\u00f8kkelen er ikke korrekt.",
"name_exists": "Navnet finnes allerede.",
"wrong_location": "Ingen Airly m\u00e5lestasjoner i dette omr\u00e5det." "wrong_location": "Ingen Airly m\u00e5lestasjoner i dette omr\u00e5det."
}, },
"step": { "step": {
@ -17,9 +16,9 @@
"name": "Navn p\u00e5 integrasjonen" "name": "Navn p\u00e5 integrasjonen"
}, },
"description": "Sett opp Airly luftkvalitet integrering. For \u00e5 generere API-n\u00f8kkel g\u00e5 til https://developer.airly.eu/register", "description": "Sett opp Airly luftkvalitet integrering. For \u00e5 generere API-n\u00f8kkel g\u00e5 til https://developer.airly.eu/register",
"title": "Airly" "title": ""
} }
}, },
"title": "Airly" "title": ""
} }
} }

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "Klucz API jest nieprawid\u0142owy.", "auth": "Klucz API jest nieprawid\u0142owy.",
"name_exists": "Nazwa ju\u017c istnieje.",
"wrong_location": "Brak stacji pomiarowych Airly w tym rejonie." "wrong_location": "Brak stacji pomiarowych Airly w tym rejonie."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API.", "auth": "\u041d\u0435\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API.",
"name_exists": "\u042d\u0442\u043e \u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435 \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f.",
"wrong_location": "\u0412 \u044d\u0442\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u043d\u0435\u0442 \u0438\u0437\u043c\u0435\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u0442\u0430\u043d\u0446\u0438\u0439 Airly." "wrong_location": "\u0412 \u044d\u0442\u043e\u0439 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u043d\u0435\u0442 \u0438\u0437\u043c\u0435\u0440\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0441\u0442\u0430\u043d\u0446\u0438\u0439 Airly."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "Klju\u010d API ni pravilen.", "auth": "Klju\u010d API ni pravilen.",
"name_exists": "Ime \u017ee obstaja",
"wrong_location": "Na tem obmo\u010dju ni merilnih postaj Airly." "wrong_location": "Na tem obmo\u010dju ni merilnih postaj Airly."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "API-nyckeln \u00e4r inte korrekt.", "auth": "API-nyckeln \u00e4r inte korrekt.",
"name_exists": "Namnet finns redan.",
"wrong_location": "Inga Airly m\u00e4tstationer i detta omr\u00e5de." "wrong_location": "Inga Airly m\u00e4tstationer i detta omr\u00e5de."
}, },
"step": { "step": {

View File

@ -5,7 +5,6 @@
}, },
"error": { "error": {
"auth": "API \u5bc6\u9470\u4e0d\u6b63\u78ba\u3002", "auth": "API \u5bc6\u9470\u4e0d\u6b63\u78ba\u3002",
"name_exists": "\u8a72\u540d\u7a31\u5df2\u5b58\u5728",
"wrong_location": "\u8a72\u5340\u57df\u6c92\u6709 Arily \u76e3\u6e2c\u7ad9\u3002" "wrong_location": "\u8a72\u5340\u57df\u6c92\u6709 Arily \u76e3\u6e2c\u7ad9\u3002"
}, },
"step": { "step": {

View File

@ -2,6 +2,7 @@
import asyncio import asyncio
from datetime import timedelta from datetime import timedelta
import logging import logging
from math import ceil
from aiohttp.client_exceptions import ClientConnectorError from aiohttp.client_exceptions import ClientConnectorError
from airly import Airly from airly import Airly
@ -10,28 +11,40 @@ import async_timeout
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
from homeassistant.core import Config, HomeAssistant from homeassistant.core import Config, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.util import Throttle from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import ( from .const import (
ATTR_API_ADVICE, ATTR_API_ADVICE,
ATTR_API_CAQI, ATTR_API_CAQI,
ATTR_API_CAQI_DESCRIPTION, ATTR_API_CAQI_DESCRIPTION,
ATTR_API_CAQI_LEVEL, ATTR_API_CAQI_LEVEL,
DATA_CLIENT,
DOMAIN, DOMAIN,
MAX_REQUESTS_PER_DAY,
NO_AIRLY_SENSORS, NO_AIRLY_SENSORS,
) )
PLATFORMS = ["air_quality", "sensor"]
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DEFAULT_SCAN_INTERVAL = timedelta(minutes=10)
def set_update_interval(hass, instances):
"""Set update_interval to another configured Airly instances."""
# We check how many Airly configured instances are and calculate interval to not
# exceed allowed numbers of requests.
interval = timedelta(minutes=ceil(24 * 60 / MAX_REQUESTS_PER_DAY) * instances)
if hass.data.get(DOMAIN):
for instance in hass.data[DOMAIN].values():
instance.update_interval = interval
return interval
async def async_setup(hass: HomeAssistant, config: Config) -> bool: async def async_setup(hass: HomeAssistant, config: Config) -> bool:
"""Set up configured Airly.""" """Set up configured Airly."""
hass.data[DOMAIN] = {}
hass.data[DOMAIN][DATA_CLIENT] = {}
return True return True
@ -48,70 +61,85 @@ async def async_setup_entry(hass, config_entry):
) )
websession = async_get_clientsession(hass) websession = async_get_clientsession(hass)
# Change update_interval for other Airly instances
airly = AirlyData(websession, api_key, latitude, longitude) update_interval = set_update_interval(
hass, len(hass.config_entries.async_entries(DOMAIN))
await airly.async_update()
hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] = airly
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, "air_quality")
) )
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, "sensor") coordinator = AirlyDataUpdateCoordinator(
hass, websession, api_key, latitude, longitude, update_interval
) )
await coordinator.async_refresh()
if not coordinator.last_update_success:
raise ConfigEntryNotReady
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][config_entry.entry_id] = coordinator
for component in PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(config_entry, component)
)
return True return True
async def async_unload_entry(hass, config_entry): async def async_unload_entry(hass, config_entry):
"""Unload a config entry.""" """Unload a config entry."""
hass.data[DOMAIN][DATA_CLIENT].pop(config_entry.entry_id) unload_ok = all(
await hass.config_entries.async_forward_entry_unload(config_entry, "air_quality") await asyncio.gather(
await hass.config_entries.async_forward_entry_unload(config_entry, "sensor") *[
return True hass.config_entries.async_forward_entry_unload(config_entry, component)
for component in PLATFORMS
]
)
)
if unload_ok:
hass.data[DOMAIN].pop(config_entry.entry_id)
# Change update_interval for other Airly instances
set_update_interval(hass, len(hass.data[DOMAIN]))
return unload_ok
class AirlyData: class AirlyDataUpdateCoordinator(DataUpdateCoordinator):
"""Define an object to hold Airly data.""" """Define an object to hold Airly data."""
def __init__(self, session, api_key, latitude, longitude): def __init__(self, hass, session, api_key, latitude, longitude, update_interval):
"""Initialize.""" """Initialize."""
self.latitude = latitude self.latitude = latitude
self.longitude = longitude self.longitude = longitude
self.airly = Airly(api_key, session) self.airly = Airly(api_key, session)
self.data = {}
@Throttle(DEFAULT_SCAN_INTERVAL) super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
async def async_update(self):
"""Update Airly data."""
try: async def _async_update_data(self):
with async_timeout.timeout(20): """Update data via library."""
measurements = self.airly.create_measurements_session_point( data = {}
self.latitude, self.longitude with async_timeout.timeout(20):
) measurements = self.airly.create_measurements_session_point(
self.latitude, self.longitude
)
try:
await measurements.update() await measurements.update()
except (AirlyError, ClientConnectorError) as error:
raise UpdateFailed(error)
values = measurements.current["values"] values = measurements.current["values"]
index = measurements.current["indexes"][0] index = measurements.current["indexes"][0]
standards = measurements.current["standards"] standards = measurements.current["standards"]
if index["description"] == NO_AIRLY_SENSORS: if index["description"] == NO_AIRLY_SENSORS:
_LOGGER.error("Can't retrieve data: no Airly sensors in this area") raise UpdateFailed("Can't retrieve data: no Airly sensors in this area")
return for value in values:
for value in values: data[value["name"]] = value["value"]
self.data[value["name"]] = value["value"] for standard in standards:
for standard in standards: data[f"{standard['pollutant']}_LIMIT"] = standard["limit"]
self.data[f"{standard['pollutant']}_LIMIT"] = standard["limit"] data[f"{standard['pollutant']}_PERCENT"] = standard["percent"]
self.data[f"{standard['pollutant']}_PERCENT"] = standard["percent"] data[ATTR_API_CAQI] = index["value"]
self.data[ATTR_API_CAQI] = index["value"] data[ATTR_API_CAQI_LEVEL] = index["level"].lower().replace("_", " ")
self.data[ATTR_API_CAQI_LEVEL] = index["level"].lower().replace("_", " ") data[ATTR_API_CAQI_DESCRIPTION] = index["description"]
self.data[ATTR_API_CAQI_DESCRIPTION] = index["description"] data[ATTR_API_ADVICE] = index["advice"]
self.data[ATTR_API_ADVICE] = index["advice"] return data
_LOGGER.debug("Data retrieved from Airly")
except asyncio.TimeoutError:
_LOGGER.error("Asyncio Timeout Error")
except (ValueError, AirlyError, ClientConnectorError) as error:
_LOGGER.error(error)
self.data = {}

View File

@ -18,13 +18,13 @@ from .const import (
ATTR_API_PM25, ATTR_API_PM25,
ATTR_API_PM25_LIMIT, ATTR_API_PM25_LIMIT,
ATTR_API_PM25_PERCENT, ATTR_API_PM25_PERCENT,
DATA_CLIENT,
DOMAIN, DOMAIN,
) )
ATTRIBUTION = "Data provided by Airly" ATTRIBUTION = "Data provided by Airly"
LABEL_ADVICE = "advice" LABEL_ADVICE = "advice"
LABEL_AQI_DESCRIPTION = f"{ATTR_AQI}_description"
LABEL_AQI_LEVEL = f"{ATTR_AQI}_level" LABEL_AQI_LEVEL = f"{ATTR_AQI}_level"
LABEL_PM_2_5_LIMIT = f"{ATTR_PM_2_5}_limit" LABEL_PM_2_5_LIMIT = f"{ATTR_PM_2_5}_limit"
LABEL_PM_2_5_PERCENT = f"{ATTR_PM_2_5}_percent_of_limit" LABEL_PM_2_5_PERCENT = f"{ATTR_PM_2_5}_percent_of_limit"
@ -36,9 +36,11 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Airly air_quality entity based on a config entry.""" """Set up Airly air_quality entity based on a config entry."""
name = config_entry.data[CONF_NAME] name = config_entry.data[CONF_NAME]
data = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] coordinator = hass.data[DOMAIN][config_entry.entry_id]
async_add_entities([AirlyAirQuality(data, name, config_entry.unique_id)], True) async_add_entities(
[AirlyAirQuality(coordinator, name, config_entry.unique_id)], False
)
def round_state(func): def round_state(func):
@ -56,23 +58,23 @@ def round_state(func):
class AirlyAirQuality(AirQualityEntity): class AirlyAirQuality(AirQualityEntity):
"""Define an Airly air quality.""" """Define an Airly air quality."""
def __init__(self, airly, name, unique_id): def __init__(self, coordinator, name, unique_id):
"""Initialize.""" """Initialize."""
self.airly = airly self.coordinator = coordinator
self.data = airly.data
self._name = name self._name = name
self._unique_id = unique_id self._unique_id = unique_id
self._pm_2_5 = None
self._pm_10 = None
self._aqi = None
self._icon = "mdi:blur" self._icon = "mdi:blur"
self._attrs = {}
@property @property
def name(self): def name(self):
"""Return the name.""" """Return the name."""
return self._name return self._name
@property
def should_poll(self):
"""Return the polling requirement of the entity."""
return False
@property @property
def icon(self): def icon(self):
"""Return the icon.""" """Return the icon."""
@ -82,30 +84,25 @@ class AirlyAirQuality(AirQualityEntity):
@round_state @round_state
def air_quality_index(self): def air_quality_index(self):
"""Return the air quality index.""" """Return the air quality index."""
return self._aqi return self.coordinator.data[ATTR_API_CAQI]
@property @property
@round_state @round_state
def particulate_matter_2_5(self): def particulate_matter_2_5(self):
"""Return the particulate matter 2.5 level.""" """Return the particulate matter 2.5 level."""
return self._pm_2_5 return self.coordinator.data[ATTR_API_PM25]
@property @property
@round_state @round_state
def particulate_matter_10(self): def particulate_matter_10(self):
"""Return the particulate matter 10 level.""" """Return the particulate matter 10 level."""
return self._pm_10 return self.coordinator.data[ATTR_API_PM10]
@property @property
def attribution(self): def attribution(self):
"""Return the attribution.""" """Return the attribution."""
return ATTRIBUTION return ATTRIBUTION
@property
def state(self):
"""Return the CAQI description."""
return self.data[ATTR_API_CAQI_DESCRIPTION]
@property @property
def unique_id(self): def unique_id(self):
"""Return a unique_id for this entity.""" """Return a unique_id for this entity."""
@ -114,25 +111,29 @@ class AirlyAirQuality(AirQualityEntity):
@property @property
def available(self): def available(self):
"""Return True if entity is available.""" """Return True if entity is available."""
return bool(self.data) return self.coordinator.last_update_success
@property @property
def device_state_attributes(self): def device_state_attributes(self):
"""Return the state attributes.""" """Return the state attributes."""
self._attrs[LABEL_ADVICE] = self.data[ATTR_API_ADVICE] return {
self._attrs[LABEL_AQI_LEVEL] = self.data[ATTR_API_CAQI_LEVEL] LABEL_AQI_DESCRIPTION: self.coordinator.data[ATTR_API_CAQI_DESCRIPTION],
self._attrs[LABEL_PM_2_5_LIMIT] = self.data[ATTR_API_PM25_LIMIT] LABEL_ADVICE: self.coordinator.data[ATTR_API_ADVICE],
self._attrs[LABEL_PM_2_5_PERCENT] = round(self.data[ATTR_API_PM25_PERCENT]) LABEL_AQI_LEVEL: self.coordinator.data[ATTR_API_CAQI_LEVEL],
self._attrs[LABEL_PM_10_LIMIT] = self.data[ATTR_API_PM10_LIMIT] LABEL_PM_2_5_LIMIT: self.coordinator.data[ATTR_API_PM25_LIMIT],
self._attrs[LABEL_PM_10_PERCENT] = round(self.data[ATTR_API_PM10_PERCENT]) LABEL_PM_2_5_PERCENT: round(self.coordinator.data[ATTR_API_PM25_PERCENT]),
return self._attrs LABEL_PM_10_LIMIT: self.coordinator.data[ATTR_API_PM10_LIMIT],
LABEL_PM_10_PERCENT: round(self.coordinator.data[ATTR_API_PM10_PERCENT]),
}
async def async_added_to_hass(self):
"""Connect to dispatcher listening for entity data notifications."""
self.coordinator.async_add_listener(self.async_write_ha_state)
async def async_will_remove_from_hass(self):
"""Disconnect from update signal."""
self.coordinator.async_remove_listener(self.async_write_ha_state)
async def async_update(self): async def async_update(self):
"""Update the entity.""" """Update Airly entity."""
await self.airly.async_update() await self.coordinator.async_request_refresh()
if self.airly.data:
self.data = self.airly.data
self._pm_10 = self.data[ATTR_API_PM10]
self._pm_2_5 = self.data[ATTR_API_PM25]
self._aqi = self.data[ATTR_API_CAQI]

View File

@ -13,7 +13,7 @@ ATTR_API_PM25_LIMIT = "PM25_LIMIT"
ATTR_API_PM25_PERCENT = "PM25_PERCENT" ATTR_API_PM25_PERCENT = "PM25_PERCENT"
ATTR_API_PRESSURE = "PRESSURE" ATTR_API_PRESSURE = "PRESSURE"
ATTR_API_TEMPERATURE = "TEMPERATURE" ATTR_API_TEMPERATURE = "TEMPERATURE"
DATA_CLIENT = "client"
DEFAULT_NAME = "Airly" DEFAULT_NAME = "Airly"
DOMAIN = "airly" DOMAIN = "airly"
MAX_REQUESTS_PER_DAY = 100
NO_AIRLY_SENSORS = "There are no Airly sensors in this area yet." NO_AIRLY_SENSORS = "There are no Airly sensors in this area yet."

View File

@ -18,7 +18,6 @@ from .const import (
ATTR_API_PM1, ATTR_API_PM1,
ATTR_API_PRESSURE, ATTR_API_PRESSURE,
ATTR_API_TEMPERATURE, ATTR_API_TEMPERATURE,
DATA_CLIENT,
DOMAIN, DOMAIN,
) )
@ -60,14 +59,14 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
"""Set up Airly sensor entities based on a config entry.""" """Set up Airly sensor entities based on a config entry."""
name = config_entry.data[CONF_NAME] name = config_entry.data[CONF_NAME]
data = hass.data[DOMAIN][DATA_CLIENT][config_entry.entry_id] coordinator = hass.data[DOMAIN][config_entry.entry_id]
sensors = [] sensors = []
for sensor in SENSOR_TYPES: for sensor in SENSOR_TYPES:
unique_id = f"{config_entry.unique_id}-{sensor.lower()}" unique_id = f"{config_entry.unique_id}-{sensor.lower()}"
sensors.append(AirlySensor(data, name, sensor, unique_id)) sensors.append(AirlySensor(coordinator, name, sensor, unique_id))
async_add_entities(sensors, True) async_add_entities(sensors, False)
def round_state(func): def round_state(func):
@ -85,10 +84,9 @@ def round_state(func):
class AirlySensor(Entity): class AirlySensor(Entity):
"""Define an Airly sensor.""" """Define an Airly sensor."""
def __init__(self, airly, name, kind, unique_id): def __init__(self, coordinator, name, kind, unique_id):
"""Initialize.""" """Initialize."""
self.airly = airly self.coordinator = coordinator
self.data = airly.data
self._name = name self._name = name
self._unique_id = unique_id self._unique_id = unique_id
self.kind = kind self.kind = kind
@ -103,10 +101,15 @@ class AirlySensor(Entity):
"""Return the name.""" """Return the name."""
return f"{self._name} {SENSOR_TYPES[self.kind][ATTR_LABEL]}" return f"{self._name} {SENSOR_TYPES[self.kind][ATTR_LABEL]}"
@property
def should_poll(self):
"""Return the polling requirement of the entity."""
return False
@property @property
def state(self): def state(self):
"""Return the state.""" """Return the state."""
self._state = self.data[self.kind] self._state = self.coordinator.data[self.kind]
if self.kind in [ATTR_API_PM1, ATTR_API_PRESSURE]: if self.kind in [ATTR_API_PM1, ATTR_API_PRESSURE]:
self._state = round(self._state) self._state = round(self._state)
if self.kind in [ATTR_API_TEMPERATURE, ATTR_API_HUMIDITY]: if self.kind in [ATTR_API_TEMPERATURE, ATTR_API_HUMIDITY]:
@ -142,11 +145,16 @@ class AirlySensor(Entity):
@property @property
def available(self): def available(self):
"""Return True if entity is available.""" """Return True if entity is available."""
return bool(self.data) return self.coordinator.last_update_success
async def async_added_to_hass(self):
"""Connect to dispatcher listening for entity data notifications."""
self.coordinator.async_add_listener(self.async_write_ha_state)
async def async_will_remove_from_hass(self):
"""Disconnect from update signal."""
self.coordinator.async_remove_listener(self.async_write_ha_state)
async def async_update(self): async def async_update(self):
"""Update the sensor.""" """Update Airly entity."""
await self.airly.async_update() await self.coordinator.async_request_refresh()
if self.airly.data:
self.data = self.airly.data

View File

@ -11,13 +11,23 @@
"data": { "data": {
"api_key": "Clau API", "api_key": "Clau API",
"latitude": "Latitud", "latitude": "Latitud",
"longitude": "Longitud", "longitude": "Longitud"
"show_on_map": "Mostra al mapa l'\u00e0rea geogr\u00e0fica monitoritzada"
}, },
"description": "Monitoritzaci\u00f3 de la qualitat de l'aire per ubicaci\u00f3 geogr\u00e0fica.", "description": "Monitoritzaci\u00f3 de la qualitat de l'aire per ubicaci\u00f3 geogr\u00e0fica.",
"title": "Configura AirVisual" "title": "Configura AirVisual"
} }
}, },
"title": "AirVisual" "title": "AirVisual"
},
"options": {
"step": {
"init": {
"data": {
"show_on_map": "Mostra al mapa l'\u00e0rea geogr\u00e0fica monitoritzada"
},
"description": "Estableix les diferents opcions de la integraci\u00f3 AirVisual.",
"title": "Configuraci\u00f3 d'AirVisual"
}
}
} }
} }

View File

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Dieser API-Schl\u00fcssel wird bereits verwendet." "already_configured": "Diese Koordinaten wurden bereits registriert."
}, },
"error": { "error": {
"invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel" "invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel"
@ -13,9 +13,21 @@
"latitude": "Breitengrad", "latitude": "Breitengrad",
"longitude": "L\u00e4ngengrad" "longitude": "L\u00e4ngengrad"
}, },
"description": "\u00dcberwachen Sie die Luftqualit\u00e4t an einem geografischen Ort.",
"title": "Konfigurieren Sie AirVisual" "title": "Konfigurieren Sie AirVisual"
} }
}, },
"title": "AirVisual" "title": "AirVisual"
},
"options": {
"step": {
"init": {
"data": {
"show_on_map": "Zeigen Sie die \u00fcberwachte Geografie auf der Karte an"
},
"description": "Legen Sie verschiedene Optionen f\u00fcr die AirVisual-Integration fest.",
"title": "Konfigurieren Sie AirVisual"
}
}
} }
} }

View File

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "This API key is already in use." "already_configured": "These coordinates have already been registered."
}, },
"error": { "error": {
"invalid_api_key": "Invalid API key" "invalid_api_key": "Invalid API key"

View File

@ -11,13 +11,23 @@
"data": { "data": {
"api_key": "Clave API", "api_key": "Clave API",
"latitude": "Latitud", "latitude": "Latitud",
"longitude": "Longitud", "longitude": "Longitud"
"show_on_map": "Mostrar geograf\u00eda monitorizada en el mapa"
}, },
"description": "Monitorizar la calidad del aire en una ubicaci\u00f3n geogr\u00e1fica.", "description": "Monitorizar la calidad del aire en una ubicaci\u00f3n geogr\u00e1fica.",
"title": "Configurar AirVisual" "title": "Configurar AirVisual"
} }
}, },
"title": "AirVisual" "title": "AirVisual"
},
"options": {
"step": {
"init": {
"data": {
"show_on_map": "Mostrar geograf\u00eda monitorizada en el mapa"
},
"description": "Ajustar varias opciones para la integraci\u00f3n de AirVisual.",
"title": "Configurar AirVisual"
}
}
} }
} }

View File

@ -0,0 +1,30 @@
{
"config": {
"abort": {
"already_configured": "Cette cl\u00e9 API est d\u00e9j\u00e0 utilis\u00e9e."
},
"error": {
"invalid_api_key": "Cl\u00e9 API invalide"
},
"step": {
"user": {
"data": {
"api_key": "Cl\u00e9 API",
"latitude": "Latitude",
"longitude": "Longitude"
},
"description": "Surveiller la qualit\u00e9 de l\u2019air dans un emplacement g\u00e9ographique.",
"title": "Configurer AirVisual"
}
},
"title": "AirVisual"
},
"options": {
"step": {
"init": {
"description": "D\u00e9finissez diverses options pour l'int\u00e9gration d'AirVisual.",
"title": "Configurer AirVisual"
}
}
}
}

View File

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Questa chiave API \u00e8 gi\u00e0 in uso." "already_configured": "Queste coordinate sono gi\u00e0 state registrate."
}, },
"error": { "error": {
"invalid_api_key": "Chiave API non valida" "invalid_api_key": "Chiave API non valida"
@ -11,13 +11,23 @@
"data": { "data": {
"api_key": "Chiave API", "api_key": "Chiave API",
"latitude": "Latitudine", "latitude": "Latitudine",
"longitude": "Logitudine", "longitude": "Logitudine"
"show_on_map": "Mostra l'area geografica monitorata sulla mappa"
}, },
"description": "Monitorare la qualit\u00e0 dell'aria in una posizione geografica.", "description": "Monitorare la qualit\u00e0 dell'aria in una posizione geografica.",
"title": "Configura AirVisual" "title": "Configura AirVisual"
} }
}, },
"title": "AirVisual" "title": "AirVisual"
},
"options": {
"step": {
"init": {
"data": {
"show_on_map": "Mostra l'area geografica monitorata sulla mappa"
},
"description": "Impostare varie opzioni per l'integrazione AirVisual.",
"title": "Configurare AirVisual"
}
}
} }
} }

View File

@ -0,0 +1,33 @@
{
"config": {
"abort": {
"already_configured": "\uc88c\ud45c\uac12\uc774 \uc774\ubbf8 \ub4f1\ub85d\ub418\uc5c8\uc2b5\ub2c8\ub2e4"
},
"error": {
"invalid_api_key": "\uc798\ubabb\ub41c API \ud0a4"
},
"step": {
"user": {
"data": {
"api_key": "API \ud0a4",
"latitude": "\uc704\ub3c4",
"longitude": "\uacbd\ub3c4"
},
"description": "\uc9c0\ub9ac\uc801 \uc704\uce58\uc5d0\uc11c \ub300\uae30\uc9c8\uc744 \ubaa8\ub2c8\ud130\ub9c1\ud569\ub2c8\ub2e4.",
"title": "AirVisual \uad6c\uc131"
}
},
"title": "AirVisual"
},
"options": {
"step": {
"init": {
"data": {
"show_on_map": "\uc9c0\ub3c4\uc5d0 \ubaa8\ub2c8\ud130\ub9c1\ub41c \uc9c0\ub9ac \uc815\ubcf4 \ud45c\uc2dc"
},
"description": "AirVisual \ud1b5\ud569 \uad6c\uc131\uc694\uc18c\uc5d0 \ub300\ud55c \ub2e4\uc591\ud55c \uc635\uc158\uc744 \uc124\uc815\ud574\uc8fc\uc138\uc694.",
"title": "AirVisual \uad6c\uc131"
}
}
}
}

View File

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "D\u00ebsen App Schl\u00ebssel g\u00ebtt scho benotzt" "already_configured": "D\u00ebs Koordinate si schon registr\u00e9iert."
}, },
"error": { "error": {
"invalid_api_key": "Ong\u00ebltegen API Schl\u00ebssel" "invalid_api_key": "Ong\u00ebltegen API Schl\u00ebssel"
@ -13,9 +13,21 @@
"latitude": "Breedegrad", "latitude": "Breedegrad",
"longitude": "L\u00e4ngegrad" "longitude": "L\u00e4ngegrad"
}, },
"description": "Loft Qualit\u00e9it an enger geografescher Lag iwwerwaachen.",
"title": "AirVisual konfigur\u00e9ieren" "title": "AirVisual konfigur\u00e9ieren"
} }
}, },
"title": "AirVisual" "title": "AirVisual"
},
"options": {
"step": {
"init": {
"data": {
"show_on_map": "Iwwerwaachte Geografie op der Kaart uweisen"
},
"description": "Verschidden Optioune fir d'AirVisual Integratioun d\u00e9fin\u00e9ieren.",
"title": "Airvisual ariichten"
}
}
} }
} }

View File

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "Denne API-n\u00f8kkelen er allerede i bruk." "already_configured": "Disse koordinatene er allerede registrert."
}, },
"error": { "error": {
"invalid_api_key": "Ugyldig API-n\u00f8kkel" "invalid_api_key": "Ugyldig API-n\u00f8kkel"
@ -11,13 +11,23 @@
"data": { "data": {
"api_key": "API-n\u00f8kkel", "api_key": "API-n\u00f8kkel",
"latitude": "Breddegrad", "latitude": "Breddegrad",
"longitude": "Lengdegrad", "longitude": "Lengdegrad"
"show_on_map": "Vis overv\u00e5ket geografi p\u00e5 kartet"
}, },
"description": "Overv\u00e5k luftkvaliteten p\u00e5 et geografisk sted.", "description": "Overv\u00e5k luftkvaliteten p\u00e5 et geografisk sted.",
"title": "Konfigurer AirVisual" "title": "Konfigurer AirVisual"
} }
}, },
"title": "AirVisual" "title": ""
},
"options": {
"step": {
"init": {
"data": {
"show_on_map": "Vis overv\u00e5ket geografi p\u00e5 kartet"
},
"description": "Angi forskjellige alternativer for AirVisual-integrasjonen.",
"title": "Konfigurer AirVisual"
}
}
} }
} }

View File

@ -0,0 +1,33 @@
{
"config": {
"abort": {
"already_configured": "Ten klucz API jest ju\u017c w u\u017cyciu."
},
"error": {
"invalid_api_key": "Nieprawid\u0142owy klucz API"
},
"step": {
"user": {
"data": {
"api_key": "Klucz API",
"latitude": "Szeroko\u015b\u0107 geograficzna",
"longitude": "D\u0142ugo\u015b\u0107 geograficzna"
},
"description": "Monitoruj jako\u015b\u0107 powietrza w okre\u015blonej lokalizacji geograficznej.",
"title": "Konfiguracja AirVisual"
}
},
"title": "AirVisual"
},
"options": {
"step": {
"init": {
"data": {
"show_on_map": "Wy\u015bwietlaj encje na mapie"
},
"description": "Konfiguracja opcji integracji AirVisual.",
"title": "Konfiguracja AirVisual"
}
}
}
}

View File

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "\u042d\u0442\u043e\u0442 \u043a\u043b\u044e\u0447 API \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f." "already_configured": "\u041a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0443\u0436\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u043d\u044b."
}, },
"error": { "error": {
"invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API." "invalid_api_key": "\u041d\u0435\u0432\u0435\u0440\u043d\u044b\u0439 \u043a\u043b\u044e\u0447 API."
@ -11,13 +11,23 @@
"data": { "data": {
"api_key": "\u041a\u043b\u044e\u0447 API", "api_key": "\u041a\u043b\u044e\u0447 API",
"latitude": "\u0428\u0438\u0440\u043e\u0442\u0430", "latitude": "\u0428\u0438\u0440\u043e\u0442\u0430",
"longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430", "longitude": "\u0414\u043e\u043b\u0433\u043e\u0442\u0430"
"show_on_map": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u043c\u0443\u044e \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u043d\u0430 \u043a\u0430\u0440\u0442\u0435"
}, },
"description": "\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u0443\u0439\u0442\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u043e\u0437\u0434\u0443\u0445\u0430 \u0432 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0438.", "description": "\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u0443\u0439\u0442\u0435 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u043e\u0437\u0434\u0443\u0445\u0430 \u0432 \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c \u043c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0438.",
"title": "AirVisual" "title": "AirVisual"
} }
}, },
"title": "AirVisual" "title": "AirVisual"
},
"options": {
"step": {
"init": {
"data": {
"show_on_map": "\u041f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0442\u044c \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0435\u043c\u0443\u044e \u043e\u0431\u043b\u0430\u0441\u0442\u044c \u043d\u0430 \u043a\u0430\u0440\u0442\u0435"
},
"description": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 AirVisual.",
"title": "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 AirVisual"
}
}
} }
} }

View File

@ -0,0 +1,12 @@
{
"config": {
"step": {
"user": {
"data": {
"latitude": "Zemepisn\u00e1 \u0161\u00edrka",
"longitude": "Zemepisn\u00e1 d\u013a\u017eka"
}
}
}
}
}

View File

@ -0,0 +1,33 @@
{
"config": {
"abort": {
"already_configured": "Ta klju\u010d API je \u017ee v uporabi."
},
"error": {
"invalid_api_key": "Neveljaven API klju\u010d"
},
"step": {
"user": {
"data": {
"api_key": "API Klju\u010d",
"latitude": "Zemljepisna \u0161irina",
"longitude": "Zemljepisna dol\u017eina"
},
"description": "Spremljajte kakovost zraka na zemljepisni lokaciji.",
"title": "Nastavite AirVisual"
}
},
"title": "AirVisual"
},
"options": {
"step": {
"init": {
"data": {
"show_on_map": "Prika\u017ei nadzorovano obmo\u010dje na zemljevidu"
},
"description": "Nastavite razli\u010dne mo\u017enosti za integracijo AirVisual.",
"title": "Nastavite AirVisual"
}
}
}
}

View File

@ -1,7 +1,7 @@
{ {
"config": { "config": {
"abort": { "abort": {
"already_configured": "\u6b64 API \u5bc6\u9470\u5df2\u88ab\u4f7f\u7528\u3002" "already_configured": "\u6b64\u4e9b\u5ea7\u6a19\u5df2\u8a3b\u518a\u3002"
}, },
"error": { "error": {
"invalid_api_key": "API \u5bc6\u78bc\u7121\u6548" "invalid_api_key": "API \u5bc6\u78bc\u7121\u6548"
@ -11,13 +11,23 @@
"data": { "data": {
"api_key": "API \u5bc6\u9470", "api_key": "API \u5bc6\u9470",
"latitude": "\u7def\u5ea6", "latitude": "\u7def\u5ea6",
"longitude": "\u7d93\u5ea6", "longitude": "\u7d93\u5ea6"
"show_on_map": "\u65bc\u5730\u5716\u4e0a\u986f\u793a\u76e3\u63a7\u4f4d\u7f6e\u3002"
}, },
"description": "\u4f9d\u5730\u7406\u4f4d\u7f6e\u76e3\u63a7\u7a7a\u6c23\u54c1\u8cea\u3002", "description": "\u4f9d\u5730\u7406\u4f4d\u7f6e\u76e3\u63a7\u7a7a\u6c23\u54c1\u8cea\u3002",
"title": "\u8a2d\u5b9a AirVisual" "title": "\u8a2d\u5b9a AirVisual"
} }
}, },
"title": "AirVisual" "title": "AirVisual"
},
"options": {
"step": {
"init": {
"data": {
"show_on_map": "\u65bc\u5730\u5716\u4e0a\u986f\u793a\u76e3\u63a7\u4f4d\u7f6e\u3002"
},
"description": "\u8a2d\u5b9a AirVisual \u6574\u5408\u9078\u9805\u3002",
"title": "\u8a2d\u5b9a AirVisual"
}
}
} }
} }

View File

@ -1,5 +1,4 @@
"""The airvisual component.""" """The airvisual component."""
import asyncio
import logging import logging
from pyairvisual import Client from pyairvisual import Client
@ -23,7 +22,6 @@ from homeassistant.helpers.event import async_track_time_interval
from .const import ( from .const import (
CONF_CITY, CONF_CITY,
CONF_COUNTRY, CONF_COUNTRY,
CONF_GEOGRAPHIES,
DATA_CLIENT, DATA_CLIENT,
DEFAULT_SCAN_INTERVAL, DEFAULT_SCAN_INTERVAL,
DOMAIN, DOMAIN,
@ -36,7 +34,7 @@ DATA_LISTENER = "listener"
DEFAULT_OPTIONS = {CONF_SHOW_ON_MAP: True} DEFAULT_OPTIONS = {CONF_SHOW_ON_MAP: True}
CONF_NODE_ID = "node_id" CONF_GEOGRAPHIES = "geographies"
GEOGRAPHY_COORDINATES_SCHEMA = vol.Schema( GEOGRAPHY_COORDINATES_SCHEMA = vol.Schema(
{ {
@ -70,34 +68,38 @@ CONFIG_SCHEMA = vol.Schema({DOMAIN: CLOUD_API_SCHEMA}, extra=vol.ALLOW_EXTRA)
def async_get_geography_id(geography_dict): def async_get_geography_id(geography_dict):
"""Generate a unique ID from a geography dict.""" """Generate a unique ID from a geography dict."""
if CONF_CITY in geography_dict: if CONF_CITY in geography_dict:
return ",".join( return ", ".join(
( (
geography_dict[CONF_CITY], geography_dict[CONF_CITY],
geography_dict[CONF_STATE], geography_dict[CONF_STATE],
geography_dict[CONF_COUNTRY], geography_dict[CONF_COUNTRY],
) )
) )
return ",".join( return ", ".join(
(str(geography_dict[CONF_LATITUDE]), str(geography_dict[CONF_LONGITUDE])) (str(geography_dict[CONF_LATITUDE]), str(geography_dict[CONF_LONGITUDE]))
) )
async def async_setup(hass, config): async def async_setup(hass, config):
"""Set up the AirVisual component.""" """Set up the AirVisual component."""
hass.data[DOMAIN] = {} hass.data[DOMAIN] = {DATA_CLIENT: {}, DATA_LISTENER: {}}
hass.data[DOMAIN][DATA_CLIENT] = {}
hass.data[DOMAIN][DATA_LISTENER] = {}
if DOMAIN not in config: if DOMAIN not in config:
return True return True
conf = config[DOMAIN] conf = config[DOMAIN]
hass.async_create_task( for geography in conf.get(
hass.config_entries.flow.async_init( CONF_GEOGRAPHIES,
DOMAIN, context={"source": SOURCE_IMPORT}, data=conf [{CONF_LATITUDE: hass.config.latitude, CONF_LONGITUDE: hass.config.longitude}],
):
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data={CONF_API_KEY: conf[CONF_API_KEY], **geography},
)
) )
)
return True return True
@ -144,6 +146,45 @@ async def async_setup_entry(hass, config_entry):
return True return True
async def async_migrate_entry(hass, config_entry):
"""Migrate an old config entry."""
version = config_entry.version
_LOGGER.debug("Migrating from version %s", version)
# 1 -> 2: One geography per config entry
if version == 1:
version = config_entry.version = 2
# Update the config entry to only include the first geography (there is always
# guaranteed to be at least one):
data = {**config_entry.data}
geographies = data.pop(CONF_GEOGRAPHIES)
first_geography = geographies.pop(0)
first_id = async_get_geography_id(first_geography)
hass.config_entries.async_update_entry(
config_entry,
unique_id=first_id,
title=f"Cloud API ({first_id})",
data={CONF_API_KEY: config_entry.data[CONF_API_KEY], **first_geography},
)
# For any geographies that remain, create a new config entry for each one:
for geography in geographies:
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data={CONF_API_KEY: config_entry.data[CONF_API_KEY], **geography},
)
)
_LOGGER.info("Migration to version %s successful", version)
return True
async def async_unload_entry(hass, config_entry): async def async_unload_entry(hass, config_entry):
"""Unload an AirVisual config entry.""" """Unload an AirVisual config entry."""
hass.data[DOMAIN][DATA_CLIENT].pop(config_entry.entry_id) hass.data[DOMAIN][DATA_CLIENT].pop(config_entry.entry_id)
@ -170,40 +211,28 @@ class AirVisualData:
self._client = client self._client = client
self._hass = hass self._hass = hass
self.data = {} self.data = {}
self.geography_data = config_entry.data
self.geography_id = config_entry.unique_id
self.options = config_entry.options self.options = config_entry.options
self.geographies = {
async_get_geography_id(geography): geography
for geography in config_entry.data[CONF_GEOGRAPHIES]
}
async def async_update(self): async def async_update(self):
"""Get new data for all locations from the AirVisual cloud API.""" """Get new data for all locations from the AirVisual cloud API."""
tasks = [] if CONF_CITY in self.geography_data:
api_coro = self._client.api.city(
self.geography_data[CONF_CITY],
self.geography_data[CONF_STATE],
self.geography_data[CONF_COUNTRY],
)
else:
api_coro = self._client.api.nearest_city(
self.geography_data[CONF_LATITUDE], self.geography_data[CONF_LONGITUDE],
)
for geography in self.geographies.values(): try:
if CONF_CITY in geography: self.data[self.geography_id] = await api_coro
tasks.append( except AirVisualError as err:
self._client.api.city( _LOGGER.error("Error while retrieving data: %s", err)
geography[CONF_CITY], self.data[self.geography_id] = {}
geography[CONF_STATE],
geography[CONF_COUNTRY],
)
)
else:
tasks.append(
self._client.api.nearest_city(
geography[CONF_LATITUDE], geography[CONF_LONGITUDE],
)
)
results = await asyncio.gather(*tasks, return_exceptions=True)
for geography_id, result in zip(self.geographies, results):
if isinstance(result, AirVisualError):
_LOGGER.error("Error while retrieving data: %s", result)
self.data[geography_id] = {}
continue
self.data[geography_id] = result
_LOGGER.debug("Received new data") _LOGGER.debug("Received new data")
async_dispatcher_send(self._hass, TOPIC_UPDATE) async_dispatcher_send(self._hass, TOPIC_UPDATE)

View File

@ -1,5 +1,5 @@
"""Define a config flow manager for AirVisual.""" """Define a config flow manager for AirVisual."""
import logging import asyncio
from pyairvisual import Client from pyairvisual import Client
from pyairvisual.errors import InvalidKeyError from pyairvisual.errors import InvalidKeyError
@ -15,15 +15,14 @@ from homeassistant.const import (
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client, config_validation as cv from homeassistant.helpers import aiohttp_client, config_validation as cv
from .const import CONF_GEOGRAPHIES, DOMAIN # pylint: disable=unused-import from . import async_get_geography_id
from .const import DOMAIN # pylint: disable=unused-import
_LOGGER = logging.getLogger("homeassistant.components.airvisual")
class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle an AirVisual config flow.""" """Handle an AirVisual config flow."""
VERSION = 1 VERSION = 2
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
@property @property
@ -68,35 +67,33 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
if not user_input: if not user_input:
return await self._show_form() return await self._show_form()
await self._async_set_unique_id(user_input[CONF_API_KEY]) geo_id = async_get_geography_id(user_input)
await self._async_set_unique_id(geo_id)
websession = aiohttp_client.async_get_clientsession(self.hass) websession = aiohttp_client.async_get_clientsession(self.hass)
client = Client(websession, api_key=user_input[CONF_API_KEY]) client = Client(websession, api_key=user_input[CONF_API_KEY])
try: # If this is the first (and only the first) time we've seen this API key, check
await client.api.nearest_city() # that it's valid:
except InvalidKeyError: checked_keys = self.hass.data.setdefault("airvisual_checked_api_keys", set())
return await self._show_form(errors={CONF_API_KEY: "invalid_api_key"}) check_keys_lock = self.hass.data.setdefault(
"airvisual_checked_api_keys_lock", asyncio.Lock()
data = {CONF_API_KEY: user_input[CONF_API_KEY]}
if user_input.get(CONF_GEOGRAPHIES):
data[CONF_GEOGRAPHIES] = user_input[CONF_GEOGRAPHIES]
else:
data[CONF_GEOGRAPHIES] = [
{
CONF_LATITUDE: user_input.get(
CONF_LATITUDE, self.hass.config.latitude
),
CONF_LONGITUDE: user_input.get(
CONF_LONGITUDE, self.hass.config.longitude
),
}
]
return self.async_create_entry(
title=f"Cloud API (API key: {user_input[CONF_API_KEY][:4]}...)", data=data
) )
async with check_keys_lock:
if user_input[CONF_API_KEY] not in checked_keys:
try:
await client.api.nearest_city()
except InvalidKeyError:
return await self._show_form(
errors={CONF_API_KEY: "invalid_api_key"}
)
checked_keys.add(user_input[CONF_API_KEY])
return self.async_create_entry(
title=f"Cloud API ({geo_id})", data=user_input
)
class AirVisualOptionsFlowHandler(config_entries.OptionsFlow): class AirVisualOptionsFlowHandler(config_entries.OptionsFlow):
"""Handle an AirVisual options flow.""" """Handle an AirVisual options flow."""

View File

@ -5,7 +5,6 @@ DOMAIN = "airvisual"
CONF_CITY = "city" CONF_CITY = "city"
CONF_COUNTRY = "country" CONF_COUNTRY = "country"
CONF_GEOGRAPHIES = "geographies"
DATA_CLIENT = "client" DATA_CLIENT = "client"

View File

@ -191,16 +191,19 @@ class AirVisualSensor(Entity):
} }
) )
geography = self._airvisual.geographies[self._geography_id] if CONF_LATITUDE in self._airvisual.geography_data:
if CONF_LATITUDE in geography:
if self._airvisual.options[CONF_SHOW_ON_MAP]: if self._airvisual.options[CONF_SHOW_ON_MAP]:
self._attrs[ATTR_LATITUDE] = geography[CONF_LATITUDE] self._attrs[ATTR_LATITUDE] = self._airvisual.geography_data[
self._attrs[ATTR_LONGITUDE] = geography[CONF_LONGITUDE] CONF_LATITUDE
]
self._attrs[ATTR_LONGITUDE] = self._airvisual.geography_data[
CONF_LONGITUDE
]
self._attrs.pop("lati", None) self._attrs.pop("lati", None)
self._attrs.pop("long", None) self._attrs.pop("long", None)
else: else:
self._attrs["lati"] = geography[CONF_LATITUDE] self._attrs["lati"] = self._airvisual.geography_data[CONF_LATITUDE]
self._attrs["long"] = geography[CONF_LONGITUDE] self._attrs["long"] = self._airvisual.geography_data[CONF_LONGITUDE]
self._attrs.pop(ATTR_LATITUDE, None) self._attrs.pop(ATTR_LATITUDE, None)
self._attrs.pop(ATTR_LONGITUDE, None) self._attrs.pop(ATTR_LONGITUDE, None)

View File

@ -16,7 +16,7 @@
"invalid_api_key": "Invalid API key" "invalid_api_key": "Invalid API key"
}, },
"abort": { "abort": {
"already_configured": "This API key is already in use." "already_configured": "These coordinates have already been registered."
} }
}, },
"options": { "options": {

View File

@ -7,10 +7,17 @@
"disarm": "Desactiva {entity_name}", "disarm": "Desactiva {entity_name}",
"trigger": "Dispara {entity_name}" "trigger": "Dispara {entity_name}"
}, },
"condition_type": {
"is_armed_away": "{entity_name} est\u00e0 activada en mode 'a fora'",
"is_armed_home": "{entity_name} est\u00e0 activada en mode 'a casa'",
"is_armed_night": "{entity_name} est\u00e0 activada en mode 'nocturn'",
"is_disarmed": "{entity_name} est\u00e0 desactivada",
"is_triggered": "{entity_name} est\u00e0 disparada"
},
"trigger_type": { "trigger_type": {
"armed_away": "{entity_name} activada en mode a fora", "armed_away": "{entity_name} activada en mode 'a fora'",
"armed_home": "{entity_name} activada en mode a casa", "armed_home": "{entity_name} activada en mode 'a casa'",
"armed_night": "{entity_name} activada en mode nocturn", "armed_night": "{entity_name} activada en mode 'nocturn'",
"disarmed": "{entity_name} desactivada", "disarmed": "{entity_name} desactivada",
"triggered": "{entity_name} disparat/ada" "triggered": "{entity_name} disparat/ada"
} }

View File

@ -7,6 +7,13 @@
"disarm": "Deaktivere {entity_name}", "disarm": "Deaktivere {entity_name}",
"trigger": "Ausl\u00f6ser {entity_name}" "trigger": "Ausl\u00f6ser {entity_name}"
}, },
"condition_type": {
"is_armed_away": "{entity_name} ist aktiviert - Unterwegs",
"is_armed_home": "{entity_name} ist aktiviert - Zuhause",
"is_armed_night": "{entity_name} ist aktiviert - Nacht",
"is_disarmed": "{entity_name} ist deaktiviert",
"is_triggered": "{entity_name} wurde ausgel\u00f6st"
},
"trigger_type": { "trigger_type": {
"armed_away": "{entity_name} Unterwegs", "armed_away": "{entity_name} Unterwegs",
"armed_home": "{entity_name} Zuhause", "armed_home": "{entity_name} Zuhause",

View File

@ -7,6 +7,13 @@
"disarm": "Disarm {entity_name}", "disarm": "Disarm {entity_name}",
"trigger": "Trigger {entity_name}" "trigger": "Trigger {entity_name}"
}, },
"condition_type": {
"is_armed_away": "{entity_name} is armed away",
"is_armed_home": "{entity_name} is armed home",
"is_armed_night": "{entity_name} is armed night",
"is_disarmed": "{entity_name} is disarmed",
"is_triggered": "{entity_name} is triggered"
},
"trigger_type": { "trigger_type": {
"armed_away": "{entity_name} armed away", "armed_away": "{entity_name} armed away",
"armed_home": "{entity_name} armed home", "armed_home": "{entity_name} armed home",

View File

@ -7,6 +7,13 @@
"disarm": "Desarmar {entity_name}", "disarm": "Desarmar {entity_name}",
"trigger": "Lanzar {entity_name}" "trigger": "Lanzar {entity_name}"
}, },
"condition_type": {
"is_armed_away": "{entity_name} est\u00e1 armada fuera",
"is_armed_home": "{entity_name} est\u00e1 armada en casa",
"is_armed_night": "{entity_name} est\u00e1 armada noche",
"is_disarmed": "{entity_name} est\u00e1 desarmada",
"is_triggered": "{entity_name} est\u00e1 disparada"
},
"trigger_type": { "trigger_type": {
"armed_away": "{entity_name} armado fuera", "armed_away": "{entity_name} armado fuera",
"armed_home": "{entity_name} armado en casa", "armed_home": "{entity_name} armado en casa",

View File

@ -7,6 +7,13 @@
"disarm": "D\u00e9sarmer {entity_name}", "disarm": "D\u00e9sarmer {entity_name}",
"trigger": "D\u00e9clencheur {entity_name}" "trigger": "D\u00e9clencheur {entity_name}"
}, },
"condition_type": {
"is_armed_away": "{entity_name} est arm\u00e9",
"is_armed_home": "{entity_name} est arm\u00e9 \u00e0 la maison",
"is_armed_night": "{entity_name} est arm\u00e9 la nuit",
"is_disarmed": "{entity_name} est d\u00e9sarm\u00e9",
"is_triggered": "{entity_name} est d\u00e9clench\u00e9"
},
"trigger_type": { "trigger_type": {
"armed_away": "Armer {entity_name} en mode \"sortie\"", "armed_away": "Armer {entity_name} en mode \"sortie\"",
"armed_home": "Armer {entity_name} en mode \"maison\"", "armed_home": "Armer {entity_name} en mode \"maison\"",

View File

@ -7,11 +7,18 @@
"disarm": "Disarmare {entity_name}", "disarm": "Disarmare {entity_name}",
"trigger": "Attivazione {entity_name}" "trigger": "Attivazione {entity_name}"
}, },
"condition_type": {
"is_armed_away": "{entity_name} \u00e8 attivo in modalit\u00e0 fuori casa",
"is_armed_home": "{entity_name} \u00e8 attivo in modalit\u00e0 a casa",
"is_armed_night": "{entity_name} \u00e8 attivo in modalit\u00e0 notte",
"is_disarmed": "{entity_name} \u00e8 disattivo",
"is_triggered": "{entity_name} \u00e8 attivato"
},
"trigger_type": { "trigger_type": {
"armed_away": "{entity_name} armata modalit\u00e0 fuori casa", "armed_away": "{entity_name} attivato in modalit\u00e0 fuori casa",
"armed_home": "{entity_name} armata modalit\u00e0 a casa", "armed_home": "{entity_name} attivato in modalit\u00e0 a casa",
"armed_night": "{entity_name} armata modalit\u00e0 notte", "armed_night": "{entity_name} attivato in modalit\u00e0 notte",
"disarmed": "{entity_name} disarmato", "disarmed": "{entity_name} disattivato",
"triggered": "{entity_name} attivato" "triggered": "{entity_name} attivato"
} }
} }

View File

@ -7,6 +7,13 @@
"disarm": "{entity_name} \uacbd\ube44\ud574\uc81c", "disarm": "{entity_name} \uacbd\ube44\ud574\uc81c",
"trigger": "{entity_name} \ud2b8\ub9ac\uac70" "trigger": "{entity_name} \ud2b8\ub9ac\uac70"
}, },
"condition_type": {
"is_armed_away": "{entity_name} \uc774(\uac00) \uc678\ucd9c \uacbd\ube44\ubaa8\ub4dc \uc0c1\ud0dc\uc774\uba74",
"is_armed_home": "{entity_name} \uc774(\uac00) \uc7ac\uc2e4 \uacbd\ube44\ubaa8\ub4dc \uc0c1\ud0dc\uc774\uba74",
"is_armed_night": "{entity_name} \uc774(\uac00) \uc57c\uac04 \uacbd\ube44\ubaa8\ub4dc \uc0c1\ud0dc\uc774\uba74",
"is_disarmed": "{entity_name} \uc774(\uac00) \ud574\uc81c \uc0c1\ud0dc\uc774\uba74",
"is_triggered": "{entity_name} \uc774(\uac00) \ud2b8\ub9ac\uac70\ub418\uc5c8\uc73c\uba74"
},
"trigger_type": { "trigger_type": {
"armed_away": "{entity_name} \uc774(\uac00) \uc678\ucd9c \uacbd\ube44\ubaa8\ub4dc\ub85c \uc124\uc815\ub420 \ub54c", "armed_away": "{entity_name} \uc774(\uac00) \uc678\ucd9c \uacbd\ube44\ubaa8\ub4dc\ub85c \uc124\uc815\ub420 \ub54c",
"armed_home": "{entity_name} \uc774(\uac00) \uc7ac\uc2e4 \uacbd\ube44\ubaa8\ub4dc\ub85c \uc124\uc815\ub420 \ub54c", "armed_home": "{entity_name} \uc774(\uac00) \uc7ac\uc2e4 \uacbd\ube44\ubaa8\ub4dc\ub85c \uc124\uc815\ub420 \ub54c",

View File

@ -7,6 +7,13 @@
"disarm": "{entity_name} entsch\u00e4rfen", "disarm": "{entity_name} entsch\u00e4rfen",
"trigger": "{entity_name} ausl\u00e9isen" "trigger": "{entity_name} ausl\u00e9isen"
}, },
"condition_type": {
"is_armed_away": "{entity_name} ass ugeschalt fir Ennerwee",
"is_armed_home": "{entity_name} ass ugeschalt fir Doheem",
"is_armed_night": "{entity_name} ass ugeschalt fir Nuecht",
"is_disarmed": "{entity_name} ass entsch\u00e4rft",
"is_triggered": "{entity_name} ass ausgel\u00e9ist"
},
"trigger_type": { "trigger_type": {
"armed_away": "{entity_name} ugeschalt fir Ennerwee", "armed_away": "{entity_name} ugeschalt fir Ennerwee",
"armed_home": "{entity_name} ugeschalt fir Doheem", "armed_home": "{entity_name} ugeschalt fir Doheem",

View File

@ -7,6 +7,13 @@
"disarm": "Deaktiver {entity_name}", "disarm": "Deaktiver {entity_name}",
"trigger": "Utl\u00f8ser {entity_name}" "trigger": "Utl\u00f8ser {entity_name}"
}, },
"condition_type": {
"is_armed_away": "{entity_name} aktivert borte",
"is_armed_home": "{entity_name} aktivert hjemme",
"is_armed_night": "{entity_name} aktivert natt",
"is_disarmed": "{entity_name} er deaktivert",
"is_triggered": "{entity_name} er utl\u00f8st"
},
"trigger_type": { "trigger_type": {
"armed_away": "{entity_name} aktivert borte", "armed_away": "{entity_name} aktivert borte",
"armed_home": "{entity_name} aktivert hjemme", "armed_home": "{entity_name} aktivert hjemme",

View File

@ -7,6 +7,13 @@
"disarm": "rozbr\u00f3j {entity_name}", "disarm": "rozbr\u00f3j {entity_name}",
"trigger": "wyzw\u00f3l {entity_name}" "trigger": "wyzw\u00f3l {entity_name}"
}, },
"condition_type": {
"is_armed_away": "{entity_name} jest uzbrojony (poza domem)",
"is_armed_home": "{entity_name} jest uzbrojony (w domu)",
"is_armed_night": "{entity_name} jest uzbrojony (noc)",
"is_disarmed": "{entity_name} jest rozbrojony",
"is_triggered": "{entity_name} jest wyzwolony"
},
"trigger_type": { "trigger_type": {
"armed_away": "{entity_name} zostanie uzbrojony (poza domem)", "armed_away": "{entity_name} zostanie uzbrojony (poza domem)",
"armed_home": "{entity_name} zostanie uzbrojony (w domu)", "armed_home": "{entity_name} zostanie uzbrojony (w domu)",

View File

@ -7,6 +7,13 @@
"disarm": "\u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043e\u0445\u0440\u0430\u043d\u0443 \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 {entity_name}", "disarm": "\u041e\u0442\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u043e\u0445\u0440\u0430\u043d\u0443 \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 {entity_name}",
"trigger": "{entity_name} \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442" "trigger": "{entity_name} \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442"
}, },
"condition_type": {
"is_armed_away": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d \u0440\u0435\u0436\u0438\u043c \u043e\u0445\u0440\u0430\u043d\u044b \"\u041d\u0435 \u0434\u043e\u043c\u0430\" \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 {entity_name}",
"is_armed_home": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d \u0440\u0435\u0436\u0438\u043c \u043e\u0445\u0440\u0430\u043d\u044b \"\u0414\u043e\u043c\u0430\" \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 {entity_name}",
"is_armed_night": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d \u0440\u0435\u0436\u0438\u043c \u043e\u0445\u0440\u0430\u043d\u044b \"\u041d\u043e\u0447\u044c\" \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 {entity_name}",
"is_disarmed": "\u041e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0430 \u043e\u0445\u0440\u0430\u043d\u0430 \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 {entity_name}",
"is_triggered": "{entity_name} \u0441\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u0442"
},
"trigger_type": { "trigger_type": {
"armed_away": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d \u0440\u0435\u0436\u0438\u043c \u043e\u0445\u0440\u0430\u043d\u044b \"\u041d\u0435 \u0434\u043e\u043c\u0430\" \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 {entity_name}", "armed_away": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d \u0440\u0435\u0436\u0438\u043c \u043e\u0445\u0440\u0430\u043d\u044b \"\u041d\u0435 \u0434\u043e\u043c\u0430\" \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 {entity_name}",
"armed_home": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d \u0440\u0435\u0436\u0438\u043c \u043e\u0445\u0440\u0430\u043d\u044b \"\u0414\u043e\u043c\u0430\" \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 {entity_name}", "armed_home": "\u0412\u043a\u043b\u044e\u0447\u0435\u043d \u0440\u0435\u0436\u0438\u043c \u043e\u0445\u0440\u0430\u043d\u044b \"\u0414\u043e\u043c\u0430\" \u043d\u0430 \u043f\u0430\u043d\u0435\u043b\u0438 {entity_name}",

View File

@ -7,6 +7,13 @@
"disarm": "Razoro\u017ei {entity_name}", "disarm": "Razoro\u017ei {entity_name}",
"trigger": "Spro\u017ei {entity_name}" "trigger": "Spro\u017ei {entity_name}"
}, },
"condition_type": {
"is_armed_away": "{entity_name} je oboro\u017een na \"zdoma\"",
"is_armed_home": "{entity_name} je oboro\u017een na \"dom\"",
"is_armed_night": "{entity_name} je oboro\u017een na \"no\u010d\"",
"is_disarmed": "{entity_name} razoro\u017een",
"is_triggered": "{entity_name} spro\u017een"
},
"trigger_type": { "trigger_type": {
"armed_away": "{entity_name} oboro\u017een - zdoma", "armed_away": "{entity_name} oboro\u017een - zdoma",
"armed_home": "{entity_name} oboro\u017een - dom", "armed_home": "{entity_name} oboro\u017een - dom",

View File

@ -7,6 +7,13 @@
"disarm": "\u89e3\u9664{entity_name}", "disarm": "\u89e3\u9664{entity_name}",
"trigger": "\u89f8\u767c{entity_name}" "trigger": "\u89f8\u767c{entity_name}"
}, },
"condition_type": {
"is_armed_away": "{entity_name}\u8a2d\u5b9a\u5916\u51fa",
"is_armed_home": "{entity_name}\u8a2d\u5b9a\u5728\u5bb6",
"is_armed_night": "{entity_name}\u8a2d\u5b9a\u591c\u9593",
"is_disarmed": "{entity_name}\u5df2\u89e3\u9664",
"is_triggered": "{entity_name}\u5df2\u89f8\u767c"
},
"trigger_type": { "trigger_type": {
"armed_away": "{entity_name}\u8a2d\u5b9a\u5916\u51fa", "armed_away": "{entity_name}\u8a2d\u5b9a\u5916\u51fa",
"armed_home": "{entity_name}\u8a2d\u5b9a\u5728\u5bb6", "armed_home": "{entity_name}\u8a2d\u5b9a\u5728\u5bb6",

View File

@ -5,3 +5,10 @@ SUPPORT_ALARM_ARM_AWAY = 2
SUPPORT_ALARM_ARM_NIGHT = 4 SUPPORT_ALARM_ARM_NIGHT = 4
SUPPORT_ALARM_TRIGGER = 8 SUPPORT_ALARM_TRIGGER = 8
SUPPORT_ALARM_ARM_CUSTOM_BYPASS = 16 SUPPORT_ALARM_ARM_CUSTOM_BYPASS = 16
CONDITION_TRIGGERED = "is_triggered"
CONDITION_DISARMED = "is_disarmed"
CONDITION_ARMED_HOME = "is_armed_home"
CONDITION_ARMED_AWAY = "is_armed_away"
CONDITION_ARMED_NIGHT = "is_armed_night"
CONDITION_ARMED_CUSTOM_BYPASS = "is_armed_custom_bypass"

View File

@ -0,0 +1,162 @@
"""Provide the device automations for Alarm control panel."""
from typing import Dict, List
import voluptuous as vol
from homeassistant.components.alarm_control_panel.const import (
SUPPORT_ALARM_ARM_AWAY,
SUPPORT_ALARM_ARM_CUSTOM_BYPASS,
SUPPORT_ALARM_ARM_HOME,
SUPPORT_ALARM_ARM_NIGHT,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
CONF_CONDITION,
CONF_DEVICE_ID,
CONF_DOMAIN,
CONF_ENTITY_ID,
CONF_TYPE,
STATE_ALARM_ARMED_AWAY,
STATE_ALARM_ARMED_CUSTOM_BYPASS,
STATE_ALARM_ARMED_HOME,
STATE_ALARM_ARMED_NIGHT,
STATE_ALARM_DISARMED,
STATE_ALARM_TRIGGERED,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import condition, config_validation as cv, entity_registry
from homeassistant.helpers.config_validation import DEVICE_CONDITION_BASE_SCHEMA
from homeassistant.helpers.typing import ConfigType, TemplateVarsType
from . import DOMAIN
from .const import (
CONDITION_ARMED_AWAY,
CONDITION_ARMED_CUSTOM_BYPASS,
CONDITION_ARMED_HOME,
CONDITION_ARMED_NIGHT,
CONDITION_DISARMED,
CONDITION_TRIGGERED,
)
CONDITION_TYPES = {
CONDITION_TRIGGERED,
CONDITION_DISARMED,
CONDITION_ARMED_HOME,
CONDITION_ARMED_AWAY,
CONDITION_ARMED_NIGHT,
CONDITION_ARMED_CUSTOM_BYPASS,
}
CONDITION_SCHEMA = DEVICE_CONDITION_BASE_SCHEMA.extend(
{
vol.Required(CONF_ENTITY_ID): cv.entity_id,
vol.Required(CONF_TYPE): vol.In(CONDITION_TYPES),
}
)
async def async_get_conditions(
hass: HomeAssistant, device_id: str
) -> List[Dict[str, str]]:
"""List device conditions for Alarm control panel devices."""
registry = await entity_registry.async_get_registry(hass)
conditions = []
# Get all the integrations entities for this device
for entry in entity_registry.async_entries_for_device(registry, device_id):
if entry.domain != DOMAIN:
continue
state = hass.states.get(entry.entity_id)
# We need a state or else we can't populate the different armed conditions
if state is None:
continue
supported_features = state.attributes["supported_features"]
# Add conditions for each entity that belongs to this integration
conditions += [
{
CONF_CONDITION: "device",
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_ENTITY_ID: entry.entity_id,
CONF_TYPE: CONDITION_DISARMED,
},
{
CONF_CONDITION: "device",
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_ENTITY_ID: entry.entity_id,
CONF_TYPE: CONDITION_TRIGGERED,
},
]
if supported_features & SUPPORT_ALARM_ARM_HOME:
conditions.append(
{
CONF_CONDITION: "device",
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_ENTITY_ID: entry.entity_id,
CONF_TYPE: CONDITION_ARMED_HOME,
}
)
if supported_features & SUPPORT_ALARM_ARM_AWAY:
conditions.append(
{
CONF_CONDITION: "device",
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_ENTITY_ID: entry.entity_id,
CONF_TYPE: CONDITION_ARMED_AWAY,
}
)
if supported_features & SUPPORT_ALARM_ARM_NIGHT:
conditions.append(
{
CONF_CONDITION: "device",
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_ENTITY_ID: entry.entity_id,
CONF_TYPE: CONDITION_ARMED_NIGHT,
}
)
if supported_features & SUPPORT_ALARM_ARM_CUSTOM_BYPASS:
conditions.append(
{
CONF_CONDITION: "device",
CONF_DEVICE_ID: device_id,
CONF_DOMAIN: DOMAIN,
CONF_ENTITY_ID: entry.entity_id,
CONF_TYPE: CONDITION_ARMED_CUSTOM_BYPASS,
}
)
return conditions
def async_condition_from_config(
config: ConfigType, config_validation: bool
) -> condition.ConditionCheckerType:
"""Create a function to test a device condition."""
if config_validation:
config = CONDITION_SCHEMA(config)
if config[CONF_TYPE] == CONDITION_TRIGGERED:
state = STATE_ALARM_TRIGGERED
elif config[CONF_TYPE] == CONDITION_DISARMED:
state = STATE_ALARM_DISARMED
elif config[CONF_TYPE] == CONDITION_ARMED_HOME:
state = STATE_ALARM_ARMED_HOME
elif config[CONF_TYPE] == CONDITION_ARMED_AWAY:
state = STATE_ALARM_ARMED_AWAY
elif config[CONF_TYPE] == CONDITION_ARMED_NIGHT:
state = STATE_ALARM_ARMED_NIGHT
elif config[CONF_TYPE] == CONDITION_ARMED_CUSTOM_BYPASS:
state = STATE_ALARM_ARMED_CUSTOM_BYPASS
def test_is_state(hass: HomeAssistant, variables: TemplateVarsType) -> bool:
"""Test if an entity is a certain state."""
return condition.state(hass, config[ATTR_ENTITY_ID], state)
return test_is_state

View File

@ -1,18 +1,25 @@
{ {
"device_automation": { "device_automation": {
"action_type": { "action_type": {
"arm_away": "Arm {entity_name} away", "arm_away": "Arm {entity_name} away",
"arm_home": "Arm {entity_name} home", "arm_home": "Arm {entity_name} home",
"arm_night": "Arm {entity_name} night", "arm_night": "Arm {entity_name} night",
"disarm": "Disarm {entity_name}", "disarm": "Disarm {entity_name}",
"trigger": "Trigger {entity_name}" "trigger": "Trigger {entity_name}"
}, },
"trigger_type": { "condition_type": {
"triggered": "{entity_name} triggered", "is_triggered": "{entity_name} is triggered",
"disarmed": "{entity_name} disarmed", "is_disarmed": "{entity_name} is disarmed",
"armed_home": "{entity_name} armed home", "is_armed_home": "{entity_name} is armed home",
"armed_away": "{entity_name} armed away", "is_armed_away": "{entity_name} is armed away",
"armed_night": "{entity_name} armed night" "is_armed_night": "{entity_name} is armed night"
},
"trigger_type": {
"triggered": "{entity_name} triggered",
"disarmed": "{entity_name} disarmed",
"armed_home": "{entity_name} armed home",
"armed_away": "{entity_name} armed away",
"armed_night": "{entity_name} armed night"
}
} }
}
} }

View File

@ -33,6 +33,7 @@ CONF_ZONE_RFID = "rfid"
CONF_ZONES = "zones" CONF_ZONES = "zones"
CONF_RELAY_ADDR = "relayaddr" CONF_RELAY_ADDR = "relayaddr"
CONF_RELAY_CHAN = "relaychan" CONF_RELAY_CHAN = "relaychan"
CONF_CODE_ARM_REQUIRED = "code_arm_required"
DEFAULT_DEVICE_TYPE = "socket" DEFAULT_DEVICE_TYPE = "socket"
DEFAULT_DEVICE_HOST = "localhost" DEFAULT_DEVICE_HOST = "localhost"
@ -42,6 +43,7 @@ DEFAULT_DEVICE_BAUD = 115200
DEFAULT_AUTO_BYPASS = False DEFAULT_AUTO_BYPASS = False
DEFAULT_PANEL_DISPLAY = False DEFAULT_PANEL_DISPLAY = False
DEFAULT_CODE_ARM_REQUIRED = True
DEFAULT_ZONE_TYPE = "opening" DEFAULT_ZONE_TYPE = "opening"
@ -105,6 +107,9 @@ CONFIG_SCHEMA = vol.Schema(
CONF_PANEL_DISPLAY, default=DEFAULT_PANEL_DISPLAY CONF_PANEL_DISPLAY, default=DEFAULT_PANEL_DISPLAY
): cv.boolean, ): cv.boolean,
vol.Optional(CONF_AUTO_BYPASS, default=DEFAULT_AUTO_BYPASS): cv.boolean, vol.Optional(CONF_AUTO_BYPASS, default=DEFAULT_AUTO_BYPASS): cv.boolean,
vol.Optional(
CONF_CODE_ARM_REQUIRED, default=DEFAULT_CODE_ARM_REQUIRED
): cv.boolean,
vol.Optional(CONF_ZONES): {vol.Coerce(int): ZONE_SCHEMA}, vol.Optional(CONF_ZONES): {vol.Coerce(int): ZONE_SCHEMA},
} }
) )
@ -121,6 +126,7 @@ def setup(hass, config):
device = conf[CONF_DEVICE] device = conf[CONF_DEVICE]
display = conf[CONF_PANEL_DISPLAY] display = conf[CONF_PANEL_DISPLAY]
auto_bypass = conf[CONF_AUTO_BYPASS] auto_bypass = conf[CONF_AUTO_BYPASS]
code_arm_required = conf[CONF_CODE_ARM_REQUIRED]
zones = conf.get(CONF_ZONES) zones = conf.get(CONF_ZONES)
device_type = device[CONF_DEVICE_TYPE] device_type = device[CONF_DEVICE_TYPE]
@ -206,7 +212,11 @@ def setup(hass, config):
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_alarmdecoder) hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_alarmdecoder)
load_platform( load_platform(
hass, "alarm_control_panel", DOMAIN, {CONF_AUTO_BYPASS: auto_bypass}, config hass,
"alarm_control_panel",
DOMAIN,
{CONF_AUTO_BYPASS: auto_bypass, CONF_CODE_ARM_REQUIRED: code_arm_required},
config,
) )
if zones: if zones:

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