mirror of
https://github.com/home-assistant/core.git
synced 2026-01-05 23:35:24 +01:00
Compare commits
386 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e27d5cd9fb | ||
|
|
6f449cd383 | ||
|
|
d47cef4ba2 | ||
|
|
a8da03912e | ||
|
|
8f233b822f | ||
|
|
060c6c89e3 | ||
|
|
96dc0319d8 | ||
|
|
08b0c1178b | ||
|
|
252c724602 | ||
|
|
c529bcca9b | ||
|
|
9b94d128ad | ||
|
|
9c224e0515 | ||
|
|
899e7bfb5a | ||
|
|
ef5f4b2aca | ||
|
|
e86fb3fc5c | ||
|
|
c63ec698a1 | ||
|
|
71aaf2d809 | ||
|
|
400602a8b3 | ||
|
|
4a32a0f1da | ||
|
|
0cf9268ca8 | ||
|
|
fb93b79b12 | ||
|
|
eff9b2a1a0 | ||
|
|
4dbbf93af9 | ||
|
|
3d73f166be | ||
|
|
0e6aacb440 | ||
|
|
cc443ff37a | ||
|
|
2cfa0af532 | ||
|
|
83fb5e5071 | ||
|
|
955c94e313 | ||
|
|
b892dbc6ea | ||
|
|
3566803d2e | ||
|
|
774b1d1663 | ||
|
|
90dd796644 | ||
|
|
a473ae6711 | ||
|
|
be99f3bf32 | ||
|
|
5047635224 | ||
|
|
e6ed2f0377 | ||
|
|
b88f56cbfb | ||
|
|
06216a8a45 | ||
|
|
f085a0c54a | ||
|
|
b783aab41b | ||
|
|
12b408219e | ||
|
|
6cafc9aaef | ||
|
|
f5cbc9d208 | ||
|
|
dd1608db0d | ||
|
|
f2f03cf552 | ||
|
|
977f1a6916 | ||
|
|
ffefdcfe22 | ||
|
|
f4eb1f0652 | ||
|
|
23668f3c5e | ||
|
|
6208d8c911 | ||
|
|
d0dad4bfd6 | ||
|
|
7330e30fd3 | ||
|
|
a9cbd355ca | ||
|
|
3e0eaa3c87 | ||
|
|
98f68f4798 | ||
|
|
0e3c1dc031 | ||
|
|
9508c51403 | ||
|
|
531207e005 | ||
|
|
952aa02e37 | ||
|
|
3e0ccd2e86 | ||
|
|
bcd1eb952c | ||
|
|
01bf4daf37 | ||
|
|
0186ce7896 | ||
|
|
f42804805c | ||
|
|
eee0a6e9f4 | ||
|
|
0e6b905cdf | ||
|
|
d59209ff47 | ||
|
|
ad8cf2d0d0 | ||
|
|
fe0db80fb8 | ||
|
|
ef61118d49 | ||
|
|
de54659097 | ||
|
|
ad3c5240c2 | ||
|
|
0f9790f5f1 | ||
|
|
68e86c5e3a | ||
|
|
1cd0e764b6 | ||
|
|
d45c386149 | ||
|
|
dd3cd95954 | ||
|
|
188ca630de | ||
|
|
ffafcf27a8 | ||
|
|
21098bc3e5 | ||
|
|
1f9f5bfaa8 | ||
|
|
312af53935 | ||
|
|
3c2df7f8f2 | ||
|
|
42cb5a5239 | ||
|
|
d832ce0b26 | ||
|
|
f32ae95ef4 | ||
|
|
de2f506585 | ||
|
|
f7ae78f78e | ||
|
|
5bedc4ede2 | ||
|
|
03a090e384 | ||
|
|
dd232a3507 | ||
|
|
03a9e3284c | ||
|
|
c08ca8a439 | ||
|
|
bf16b50679 | ||
|
|
5a1b0edd96 | ||
|
|
1477087c71 | ||
|
|
4a2236fe85 | ||
|
|
95cefd1acc | ||
|
|
5ce31cb383 | ||
|
|
ff391e538a | ||
|
|
28a2c9c653 | ||
|
|
369ffe2288 | ||
|
|
5f72ad8da6 | ||
|
|
00c38c5f70 | ||
|
|
18a4829314 | ||
|
|
c629e7dc0e | ||
|
|
73c52e668e | ||
|
|
6cafc45d2b | ||
|
|
4f767dd3ef | ||
|
|
c89975adf6 | ||
|
|
867630a4a7 | ||
|
|
a6d5ed0160 | ||
|
|
f93e4e3de7 | ||
|
|
f5a6c3484d | ||
|
|
b8afb9277a | ||
|
|
262ed9ed2a | ||
|
|
f1e58d0784 | ||
|
|
6c6318d18f | ||
|
|
3dc6612cd9 | ||
|
|
a38db1f677 | ||
|
|
558cccc68c | ||
|
|
b598ff94d1 | ||
|
|
c267946284 | ||
|
|
6c4b4ad1e0 | ||
|
|
815d153e55 | ||
|
|
5650b390b9 | ||
|
|
8f4d3146c1 | ||
|
|
2647296475 | ||
|
|
6d311a31dd | ||
|
|
4bbc0a03ca | ||
|
|
16670a38a4 | ||
|
|
eb8e8d00a6 | ||
|
|
d5f4dfdd6b | ||
|
|
4a0a56ebdc | ||
|
|
6990c70123 | ||
|
|
bb68e7a532 | ||
|
|
bb3592baa0 | ||
|
|
1fa996ed68 | ||
|
|
2a3c94bad0 | ||
|
|
3ee05ad4bb | ||
|
|
44425a184e | ||
|
|
28c2f9caa9 | ||
|
|
2a36adae46 | ||
|
|
46985bba0d | ||
|
|
3f4a7ec396 | ||
|
|
ad9f4db983 | ||
|
|
0e2fa7700d | ||
|
|
109f083c5d | ||
|
|
fb22f6c301 | ||
|
|
aec2fe86e4 | ||
|
|
763ed0dc7b | ||
|
|
f150c9c65c | ||
|
|
d979648c01 | ||
|
|
9226589bcd | ||
|
|
e616ab5fb2 | ||
|
|
3c59791b2e | ||
|
|
b50281a917 | ||
|
|
2d002f3ef6 | ||
|
|
6180f7bd64 | ||
|
|
45241e57ca | ||
|
|
cd57b764ce | ||
|
|
c2a9aba467 | ||
|
|
5c2bd8b743 | ||
|
|
513abcb7e5 | ||
|
|
1ff245d9c2 | ||
|
|
2360fd4141 | ||
|
|
ea23ffedfe | ||
|
|
bf4b099f11 | ||
|
|
b8fdebd05c | ||
|
|
0e3dc7976c | ||
|
|
c8d4cf08d9 | ||
|
|
df67ab995f | ||
|
|
c21a2eab22 | ||
|
|
df2351b920 | ||
|
|
4332cbe112 | ||
|
|
f9a7c64106 | ||
|
|
49ebea2be3 | ||
|
|
b09a9fc81a | ||
|
|
fa60e9b03b | ||
|
|
0b2a8bf79a | ||
|
|
087b672449 | ||
|
|
d3f9408650 | ||
|
|
6e6ad94df6 | ||
|
|
e344c2ea64 | ||
|
|
ab8c50895e | ||
|
|
f0472f2dc2 | ||
|
|
0249daef2e | ||
|
|
8d2e72cdf6 | ||
|
|
52ac7285a7 | ||
|
|
2a3d688923 | ||
|
|
ca3a22b5a3 | ||
|
|
912cda4e6f | ||
|
|
c79b3df73f | ||
|
|
8423d18d8d | ||
|
|
9d87c1ab1a | ||
|
|
f95c3e265d | ||
|
|
66402b9b38 | ||
|
|
99877c32b1 | ||
|
|
4510e83150 | ||
|
|
252d934caa | ||
|
|
e8bd1b9216 | ||
|
|
99d732b974 | ||
|
|
05e7238c45 | ||
|
|
991afccbd4 | ||
|
|
d5e606640c | ||
|
|
6d4fa76107 | ||
|
|
ba2558790d | ||
|
|
79f6d55fe8 | ||
|
|
403b4a2e0b | ||
|
|
8272c71811 | ||
|
|
699ca44260 | ||
|
|
5893f6b14b | ||
|
|
a7b08c48f3 | ||
|
|
0148708613 | ||
|
|
81cef9a281 | ||
|
|
2bb29485be | ||
|
|
57820be92a | ||
|
|
e18bea0215 | ||
|
|
49c2a4a4e3 | ||
|
|
ebc4804e04 | ||
|
|
ffc9bcb4d7 | ||
|
|
e87fab6b5f | ||
|
|
b6d9454b54 | ||
|
|
85328399e0 | ||
|
|
836413a4a8 | ||
|
|
d16d44d3e7 | ||
|
|
c2ac8e813a | ||
|
|
37687561c0 | ||
|
|
acf41d03db | ||
|
|
92d373055f | ||
|
|
c00f04221f | ||
|
|
414559f018 | ||
|
|
661101df08 | ||
|
|
ecbcdee934 | ||
|
|
eb77b94315 | ||
|
|
6ab14a3729 | ||
|
|
f81464161a | ||
|
|
3461f3a1ed | ||
|
|
0ed7bc3b8e | ||
|
|
d8e3e9abaa | ||
|
|
c3c5cc9ae7 | ||
|
|
5db1a67c20 | ||
|
|
1a4199c485 | ||
|
|
8cb1d630c8 | ||
|
|
f275b7e5ed | ||
|
|
c9592c1447 | ||
|
|
ff2367fffb | ||
|
|
4c9303bbd5 | ||
|
|
84712d2f40 | ||
|
|
5a2aabea9c | ||
|
|
23045af4a7 | ||
|
|
9451920ab5 | ||
|
|
62f2ee5f60 | ||
|
|
c8c81d493d | ||
|
|
d33a3ca90f | ||
|
|
5b4d2aed64 | ||
|
|
242aff9269 | ||
|
|
445ef861c0 | ||
|
|
03c906a2f1 | ||
|
|
e0d2e5dcb0 | ||
|
|
5c1dc60505 | ||
|
|
f6ce5f2d05 | ||
|
|
08aa4b098c | ||
|
|
5123baba3f | ||
|
|
ede0cfaeb8 | ||
|
|
34e44e7f3a | ||
|
|
0cb27ff236 | ||
|
|
cb450dcebd | ||
|
|
661570dfad | ||
|
|
05abf37046 | ||
|
|
7c79adad8f | ||
|
|
e0769f9ad4 | ||
|
|
7d23a734fc | ||
|
|
4e4fd90455 | ||
|
|
4517f0d59a | ||
|
|
c1ceab09e5 | ||
|
|
609263e1bb | ||
|
|
f02c5f66d6 | ||
|
|
82c8f18bc7 | ||
|
|
a9d16d4276 | ||
|
|
505de3dce3 | ||
|
|
52a4c16980 | ||
|
|
a2ac335222 | ||
|
|
cd79720a14 | ||
|
|
576970d1ad | ||
|
|
9146f76b01 | ||
|
|
e97d21aec0 | ||
|
|
097b056324 | ||
|
|
aece76f6cd | ||
|
|
7bbffa6e6d | ||
|
|
ff582721dd | ||
|
|
3910ab6cab | ||
|
|
5c83367bb0 | ||
|
|
433b89de50 | ||
|
|
4c32fd12fc | ||
|
|
a3e2504470 | ||
|
|
d98432c328 | ||
|
|
1da35e2939 | ||
|
|
0c49c8578b | ||
|
|
abd1909e2b | ||
|
|
86d48c608e | ||
|
|
a278cf3db2 | ||
|
|
2cda7bf1e7 | ||
|
|
7ac014744c | ||
|
|
51b9afe3c1 | ||
|
|
397238372e | ||
|
|
682fcec99e | ||
|
|
41cd3ba532 | ||
|
|
426f546c2f | ||
|
|
2f1824774f | ||
|
|
999c5443c1 | ||
|
|
7ec7306ea8 | ||
|
|
8d68f34650 | ||
|
|
c1908d16b5 | ||
|
|
40356b4fc5 | ||
|
|
f4b3760a1a | ||
|
|
fa63dc1e25 | ||
|
|
b9ad40ed38 | ||
|
|
77b3f31e9b | ||
|
|
451c6c25cd | ||
|
|
af021b1c81 | ||
|
|
4e3b079a29 | ||
|
|
9a099bdf0a | ||
|
|
cf8dfdae47 | ||
|
|
6e95b90f42 | ||
|
|
d36259f067 | ||
|
|
d62bb9ed47 | ||
|
|
ef54f33af7 | ||
|
|
1391f90a30 | ||
|
|
2889067ece | ||
|
|
f4bf66aecd | ||
|
|
2f80489428 | ||
|
|
b6e69cd370 | ||
|
|
f9634f0232 | ||
|
|
6affb27711 | ||
|
|
5ec76af875 | ||
|
|
5b41680506 | ||
|
|
4f81109304 | ||
|
|
04763c5bfb | ||
|
|
e5e38edcb2 | ||
|
|
e86919a997 | ||
|
|
d04479044c | ||
|
|
7737387efe | ||
|
|
743166d284 | ||
|
|
5dd031af17 | ||
|
|
750ed2facd | ||
|
|
aa972b0005 | ||
|
|
628f77f8f2 | ||
|
|
9db3900cff | ||
|
|
fd5895118e | ||
|
|
607cdfdd32 | ||
|
|
460857a765 | ||
|
|
6bd55011a8 | ||
|
|
e2a113a2de | ||
|
|
86f61b8e55 | ||
|
|
f3fed5647e | ||
|
|
992daa4a44 | ||
|
|
26d7b2164e | ||
|
|
31d150794d | ||
|
|
7e6e36db15 | ||
|
|
4f0997f6e9 | ||
|
|
94b6ab2862 | ||
|
|
11a25157c1 | ||
|
|
ff92a8b260 | ||
|
|
1fe26c77e0 | ||
|
|
00d5e5cfb2 | ||
|
|
c968e455a9 | ||
|
|
a3dd9979d2 | ||
|
|
6e97975ff8 | ||
|
|
af7c01f957 | ||
|
|
29533d8d4d | ||
|
|
374a8157e7 | ||
|
|
40fc72aac2 | ||
|
|
b8fab33e69 | ||
|
|
221d5205e4 | ||
|
|
77ebda0c20 | ||
|
|
ac30e5799c | ||
|
|
f9a0b4b3cf | ||
|
|
233568ac29 | ||
|
|
76b0302c7f | ||
|
|
8bc542776b | ||
|
|
5f5cb8bea8 | ||
|
|
da761fdd39 | ||
|
|
22415ce49a | ||
|
|
19be4a5d6d |
29
.coveragerc
29
.coveragerc
@@ -8,15 +8,6 @@ omit =
|
||||
homeassistant/scripts/*.py
|
||||
|
||||
# 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/actiontec/device_tracker.py
|
||||
homeassistant/components/adguard/__init__.py
|
||||
@@ -33,7 +24,6 @@ omit =
|
||||
homeassistant/components/airvisual/sensor.py
|
||||
homeassistant/components/aladdin_connect/cover.py
|
||||
homeassistant/components/alarmdecoder/*
|
||||
homeassistant/components/alarmdotcom/alarm_control_panel.py
|
||||
homeassistant/components/alpha_vantage/sensor.py
|
||||
homeassistant/components/amazon_polly/tts.py
|
||||
homeassistant/components/ambiclimate/climate.py
|
||||
@@ -85,6 +75,7 @@ omit =
|
||||
homeassistant/components/bluetooth_tracker/*
|
||||
homeassistant/components/bme280/sensor.py
|
||||
homeassistant/components/bme680/sensor.py
|
||||
homeassistant/components/bmp280/sensor.py
|
||||
homeassistant/components/bmw_connected_drive/*
|
||||
homeassistant/components/bom/camera.py
|
||||
homeassistant/components/bom/sensor.py
|
||||
@@ -93,9 +84,6 @@ omit =
|
||||
homeassistant/components/broadlink/remote.py
|
||||
homeassistant/components/broadlink/sensor.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/browser/*
|
||||
homeassistant/components/brunt/cover.py
|
||||
@@ -242,7 +230,11 @@ omit =
|
||||
homeassistant/components/foscam/const.py
|
||||
homeassistant/components/foursquare/*
|
||||
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/fritzbox/*
|
||||
homeassistant/components/fritzbox_callmonitor/sensor.py
|
||||
@@ -433,6 +425,7 @@ omit =
|
||||
homeassistant/components/minecraft_server/__init__.py
|
||||
homeassistant/components/minecraft_server/binary_sensor.py
|
||||
homeassistant/components/minecraft_server/const.py
|
||||
homeassistant/components/minecraft_server/helpers.py
|
||||
homeassistant/components/minecraft_server/sensor.py
|
||||
homeassistant/components/minio/*
|
||||
homeassistant/components/mitemp_bt/sensor.py
|
||||
@@ -441,7 +434,6 @@ omit =
|
||||
homeassistant/components/mochad/*
|
||||
homeassistant/components/modbus/*
|
||||
homeassistant/components/modem_callerid/sensor.py
|
||||
homeassistant/components/mopar/*
|
||||
homeassistant/components/mpchc/media_player.py
|
||||
homeassistant/components/mpd/media_player.py
|
||||
homeassistant/components/mqtt_room/sensor.py
|
||||
@@ -450,7 +442,6 @@ omit =
|
||||
homeassistant/components/mychevy/*
|
||||
homeassistant/components/mycroft/*
|
||||
homeassistant/components/mycroft/notify.py
|
||||
homeassistant/components/myq/cover.py
|
||||
homeassistant/components/mysensors/*
|
||||
homeassistant/components/mystrom/binary_sensor.py
|
||||
homeassistant/components/mystrom/light.py
|
||||
@@ -476,6 +467,7 @@ omit =
|
||||
homeassistant/components/netgear_lte/*
|
||||
homeassistant/components/netio/switch.py
|
||||
homeassistant/components/neurio_energy/sensor.py
|
||||
homeassistant/components/nextcloud/*
|
||||
homeassistant/components/nfandroidtv/notify.py
|
||||
homeassistant/components/niko_home_control/light.py
|
||||
homeassistant/components/nilu/air_quality.py
|
||||
@@ -597,7 +589,9 @@ omit =
|
||||
homeassistant/components/ring/camera.py
|
||||
homeassistant/components/ripple/sensor.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/route53/*
|
||||
homeassistant/components/rova/sensor.py
|
||||
@@ -614,6 +608,7 @@ omit =
|
||||
homeassistant/components/saj/sensor.py
|
||||
homeassistant/components/salt/device_tracker.py
|
||||
homeassistant/components/satel_integra/*
|
||||
homeassistant/components/schluter/*
|
||||
homeassistant/components/scrape/sensor.py
|
||||
homeassistant/components/scsgate/*
|
||||
homeassistant/components/scsgate/cover.py
|
||||
|
||||
49
.github/ISSUE_TEMPLATE.md
vendored
Normal file
49
.github/ISSUE_TEMPLATE.md
vendored
Normal 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
|
||||
|
||||
14
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
14
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
@@ -1,10 +1,10 @@
|
||||
---
|
||||
name: Report a bug with Home Assistant
|
||||
about: Report an issue with Home Assistant
|
||||
name: Report a bug with Home Assistant Core
|
||||
about: Report an issue with Home Assistant Core
|
||||
---
|
||||
<!-- 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/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.
|
||||
- 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.
|
||||
@@ -12,7 +12,7 @@ about: Report an issue with Home Assistant
|
||||
## 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 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 release with the issue:
|
||||
- Last working Home Assistant release (if known):
|
||||
- Operating environment (Hass.io/Docker/Windows/etc.):
|
||||
- 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:
|
||||
|
||||
|
||||
1
.github/stale.yml
vendored
1
.github/stale.yml
vendored
@@ -57,6 +57,7 @@ limitPerRun: 30
|
||||
# Handle pull requests a little bit faster and with an adjusted comment.
|
||||
pulls:
|
||||
daysUntilStale: 30
|
||||
exemptProjects: false
|
||||
markComment: >
|
||||
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
|
||||
|
||||
@@ -59,3 +59,17 @@ repos:
|
||||
types: [python]
|
||||
require_serial: true
|
||||
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$
|
||||
|
||||
27
CODEOWNERS
27
CODEOWNERS
@@ -17,6 +17,7 @@ homeassistant/components/abode/* @shred86
|
||||
homeassistant/components/adguard/* @frenck
|
||||
homeassistant/components/airly/* @bieniu
|
||||
homeassistant/components/airvisual/* @bachya
|
||||
homeassistant/components/alarmdecoder/* @ajschmidt8
|
||||
homeassistant/components/alexa/* @home-assistant/cloud @ochlocracy
|
||||
homeassistant/components/almond/* @gcampax @balloob
|
||||
homeassistant/components/alpha_vantage/* @fabaff
|
||||
@@ -51,6 +52,7 @@ homeassistant/components/beewi_smartclim/* @alemuro
|
||||
homeassistant/components/bitcoin/* @fabaff
|
||||
homeassistant/components/bizkaibus/* @UgaitzEtxebarria
|
||||
homeassistant/components/blink/* @fronzbot
|
||||
homeassistant/components/bmp280/* @belidzs
|
||||
homeassistant/components/bmw_connected_drive/* @gerard33
|
||||
homeassistant/components/bom/* @maddenp
|
||||
homeassistant/components/braviatv/* @robbiet480
|
||||
@@ -80,12 +82,13 @@ homeassistant/components/darksky/* @fabaff
|
||||
homeassistant/components/deconz/* @kane610
|
||||
homeassistant/components/delijn/* @bollewolle
|
||||
homeassistant/components/demo/* @home-assistant/core
|
||||
homeassistant/components/denonavr/* @scarface-4711 @starkillerOG
|
||||
homeassistant/components/derivative/* @afaucogney
|
||||
homeassistant/components/device_automation/* @home-assistant/core
|
||||
homeassistant/components/digital_ocean/* @fabaff
|
||||
homeassistant/components/directv/* @ctalkington
|
||||
homeassistant/components/discogs/* @thibmaek
|
||||
homeassistant/components/doorbird/* @oblogic7
|
||||
homeassistant/components/doorbird/* @oblogic7 @bdraco
|
||||
homeassistant/components/dsmr_reader/* @depl0y
|
||||
homeassistant/components/dweet/* @fabaff
|
||||
homeassistant/components/dynalite/* @ziv1234
|
||||
@@ -96,6 +99,7 @@ homeassistant/components/edl21/* @mtdcr
|
||||
homeassistant/components/egardia/* @jeroenterheerdt
|
||||
homeassistant/components/eight_sleep/* @mezz64
|
||||
homeassistant/components/elgato/* @frenck
|
||||
homeassistant/components/elkm1/* @bdraco
|
||||
homeassistant/components/elv/* @majuss
|
||||
homeassistant/components/emby/* @mezz64
|
||||
homeassistant/components/emoncms/* @borpin
|
||||
@@ -122,7 +126,7 @@ homeassistant/components/fortigate/* @kifeo
|
||||
homeassistant/components/fortios/* @kimfrellsen
|
||||
homeassistant/components/foscam/* @skgsergio
|
||||
homeassistant/components/foursquare/* @robbiet480
|
||||
homeassistant/components/freebox/* @snoof85
|
||||
homeassistant/components/freebox/* @snoof85 @Quentame
|
||||
homeassistant/components/fronius/* @nielstron
|
||||
homeassistant/components/frontend/* @home-assistant/frontend
|
||||
homeassistant/components/garmin_connect/* @cyberjunky
|
||||
@@ -146,7 +150,7 @@ homeassistant/components/griddy/* @bdraco
|
||||
homeassistant/components/group/* @home-assistant/core
|
||||
homeassistant/components/growatt_server/* @indykoning
|
||||
homeassistant/components/gtfs/* @robbiet480
|
||||
homeassistant/components/harmony/* @ehendrix23
|
||||
homeassistant/components/harmony/* @ehendrix23 @bramkragten @bdraco
|
||||
homeassistant/components/hassio/* @home-assistant/hass-io
|
||||
homeassistant/components/heatmiser/* @andylockran
|
||||
homeassistant/components/heos/* @andrewsayre
|
||||
@@ -183,6 +187,7 @@ homeassistant/components/intesishome/* @jnimmo
|
||||
homeassistant/components/ios/* @robbiet480
|
||||
homeassistant/components/iperf3/* @rohankapoorcom
|
||||
homeassistant/components/ipma/* @dgomes @abmantis
|
||||
homeassistant/components/ipp/* @ctalkington
|
||||
homeassistant/components/iqvia/* @bachya
|
||||
homeassistant/components/irish_rail_transport/* @ttroy50
|
||||
homeassistant/components/izone/* @Swamp-Ig
|
||||
@@ -210,6 +215,7 @@ homeassistant/components/luci/* @fbradyirl @mzdrale
|
||||
homeassistant/components/luftdaten/* @fabaff
|
||||
homeassistant/components/lupusec/* @majuss
|
||||
homeassistant/components/lutron/* @JonGilmore
|
||||
homeassistant/components/lutron_caseta/* @swails
|
||||
homeassistant/components/mastodon/* @fabaff
|
||||
homeassistant/components/matrix/* @tinloaf
|
||||
homeassistant/components/mcp23017/* @jardiamj
|
||||
@@ -226,12 +232,13 @@ homeassistant/components/min_max/* @fabaff
|
||||
homeassistant/components/minecraft_server/* @elmurato
|
||||
homeassistant/components/minio/* @tkislan
|
||||
homeassistant/components/mobile_app/* @robbiet480
|
||||
homeassistant/components/modbus/* @adamchengtkc
|
||||
homeassistant/components/modbus/* @adamchengtkc @janiversen
|
||||
homeassistant/components/monoprice/* @etsinko
|
||||
homeassistant/components/moon/* @fabaff
|
||||
homeassistant/components/mpd/* @fabaff
|
||||
homeassistant/components/mqtt/* @home-assistant/core
|
||||
homeassistant/components/msteams/* @peroyvind
|
||||
homeassistant/components/myq/* @bdraco
|
||||
homeassistant/components/mysensors/* @MartinHjelmare
|
||||
homeassistant/components/mystrom/* @fabaff
|
||||
homeassistant/components/neato/* @dshokouhi @Santobert
|
||||
@@ -241,7 +248,9 @@ homeassistant/components/ness_alarm/* @nickw444
|
||||
homeassistant/components/nest/* @awarecan
|
||||
homeassistant/components/netatmo/* @cgtobi
|
||||
homeassistant/components/netdata/* @fabaff
|
||||
homeassistant/components/nexia/* @ryannazaretian @bdraco
|
||||
homeassistant/components/nextbus/* @vividboarder
|
||||
homeassistant/components/nextcloud/* @meichthys
|
||||
homeassistant/components/nilu/* @hfurubotten
|
||||
homeassistant/components/nissan_leaf/* @filcole
|
||||
homeassistant/components/nmbs/* @thibmaek
|
||||
@@ -250,7 +259,9 @@ homeassistant/components/notify/* @home-assistant/core
|
||||
homeassistant/components/notion/* @bachya
|
||||
homeassistant/components/nsw_fuel_station/* @nickw444
|
||||
homeassistant/components/nsw_rural_fire_service_feed/* @exxamalte
|
||||
homeassistant/components/nuheat/* @bdraco
|
||||
homeassistant/components/nuki/* @pvizeli
|
||||
homeassistant/components/nut/* @bdraco
|
||||
homeassistant/components/nws/* @MatthewFlamm
|
||||
homeassistant/components/nzbget/* @chriscla
|
||||
homeassistant/components/obihai/* @dshokouhi
|
||||
@@ -275,17 +286,21 @@ homeassistant/components/plaato/* @JohNan
|
||||
homeassistant/components/plant/* @ChristianKuehnel
|
||||
homeassistant/components/plex/* @jjlawren
|
||||
homeassistant/components/plugwise/* @laetificat @CoMPaTech @bouwew
|
||||
homeassistant/components/plum_lightpad/* @ColinHarrington
|
||||
homeassistant/components/point/* @fredrike
|
||||
homeassistant/components/powerwall/* @bdraco
|
||||
homeassistant/components/proxmoxve/* @k4ds3
|
||||
homeassistant/components/ps4/* @ktnrg45
|
||||
homeassistant/components/ptvsd/* @swamp-ig
|
||||
homeassistant/components/push/* @dgomes
|
||||
homeassistant/components/pvoutput/* @fabaff
|
||||
homeassistant/components/pvpc_hourly_pricing/* @azogue
|
||||
homeassistant/components/qld_bushfire/* @exxamalte
|
||||
homeassistant/components/qnap/* @colinodell
|
||||
homeassistant/components/quantum_gateway/* @cisasteelersfan
|
||||
homeassistant/components/qvr_pro/* @oblogic7
|
||||
homeassistant/components/qwikswitch/* @kellerza
|
||||
homeassistant/components/rachio/* @bdraco
|
||||
homeassistant/components/rainbird/* @konikvranik
|
||||
homeassistant/components/raincloud/* @vanstinator
|
||||
homeassistant/components/rainforest_eagle/* @gtdiehl @jcalbert
|
||||
@@ -302,6 +317,7 @@ homeassistant/components/saj/* @fredericvl
|
||||
homeassistant/components/salt/* @bjornorri
|
||||
homeassistant/components/samsungtv/* @escoand
|
||||
homeassistant/components/scene/* @home-assistant/core
|
||||
homeassistant/components/schluter/* @prairieapps
|
||||
homeassistant/components/scrape/* @fabaff
|
||||
homeassistant/components/script/* @home-assistant/core
|
||||
homeassistant/components/search/* @home-assistant/core
|
||||
@@ -331,6 +347,7 @@ homeassistant/components/solax/* @squishykid
|
||||
homeassistant/components/soma/* @ratsept
|
||||
homeassistant/components/somfy/* @tetienne
|
||||
homeassistant/components/songpal/* @rytilahti
|
||||
homeassistant/components/sonos/* @amelchio
|
||||
homeassistant/components/spaceapi/* @fabaff
|
||||
homeassistant/components/speedtestdotnet/* @rohankapoorcom
|
||||
homeassistant/components/spider/* @peternijssen
|
||||
@@ -354,7 +371,7 @@ homeassistant/components/switchmate/* @danielhiversen
|
||||
homeassistant/components/syncthru/* @nielstron
|
||||
homeassistant/components/synology_srm/* @aerialls
|
||||
homeassistant/components/syslog/* @fabaff
|
||||
homeassistant/components/tado/* @michaelarnauts
|
||||
homeassistant/components/tado/* @michaelarnauts @bdraco
|
||||
homeassistant/components/tahoma/* @philklei
|
||||
homeassistant/components/tankerkoenig/* @guillempages
|
||||
homeassistant/components/tautulli/* @ludeeus
|
||||
|
||||
@@ -7,7 +7,7 @@ trigger:
|
||||
- dev
|
||||
pr: none
|
||||
schedules:
|
||||
- cron: "30 0 * * *"
|
||||
- cron: "0 0 * * *"
|
||||
displayName: "translation update"
|
||||
branches:
|
||||
include:
|
||||
|
||||
10
build.json
10
build.json
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"image": "homeassistant/{arch}-homeassistant",
|
||||
"build_from": {
|
||||
"aarch64": "homeassistant/aarch64-homeassistant-base:7.0.1",
|
||||
"armhf": "homeassistant/armhf-homeassistant-base:7.0.1",
|
||||
"armv7": "homeassistant/armv7-homeassistant-base:7.0.1",
|
||||
"amd64": "homeassistant/amd64-homeassistant-base:7.0.1",
|
||||
"i386": "homeassistant/i386-homeassistant-base:7.0.1"
|
||||
"aarch64": "homeassistant/aarch64-homeassistant-base:7.1.0",
|
||||
"armhf": "homeassistant/armhf-homeassistant-base:7.1.0",
|
||||
"armv7": "homeassistant/armv7-homeassistant-base:7.1.0",
|
||||
"amd64": "homeassistant/amd64-homeassistant-base:7.1.0",
|
||||
"i386": "homeassistant/i386-homeassistant-base:7.1.0"
|
||||
},
|
||||
"labels": {
|
||||
"io.hass.type": "core"
|
||||
|
||||
29
docs/source/api/auth.rst
Normal file
29
docs/source/api/auth.rst
Normal 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:
|
||||
@@ -1,7 +1,7 @@
|
||||
.. _bootstrap_module:
|
||||
|
||||
:mod:`homeassistant.bootstrap`
|
||||
-------------------------
|
||||
------------------------------
|
||||
|
||||
.. automodule:: homeassistant.bootstrap
|
||||
:members:
|
||||
|
||||
170
docs/source/api/components.rst
Normal file
170
docs/source/api/components.rst
Normal 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:
|
||||
7
docs/source/api/config_entries.rst
Normal file
7
docs/source/api/config_entries.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
.. _config_entries_module:
|
||||
|
||||
:mod:`homeassistant.config_entries`
|
||||
-----------------------------------
|
||||
|
||||
.. automodule:: homeassistant.config_entries
|
||||
:members:
|
||||
@@ -4,35 +4,4 @@
|
||||
-------------------------
|
||||
|
||||
.. automodule:: homeassistant.core
|
||||
|
||||
.. autoclass:: Config
|
||||
: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:
|
||||
:members:
|
||||
7
docs/source/api/data_entry_flow.rst
Normal file
7
docs/source/api/data_entry_flow.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
.. _data_entry_flow_module:
|
||||
|
||||
:mod:`homeassistant.data_entry_flow`
|
||||
-----------------------------
|
||||
|
||||
.. automodule:: homeassistant.data_entry_flow
|
||||
:members:
|
||||
@@ -1,10 +0,0 @@
|
||||
.. _components_device_tracker_module:
|
||||
|
||||
:mod:`homeassistant.components.device_tracker`
|
||||
----------------------------------------------
|
||||
|
||||
.. automodule:: homeassistant.components.device_tracker
|
||||
:members:
|
||||
|
||||
.. autoclass:: Device
|
||||
:members:
|
||||
@@ -1,12 +0,0 @@
|
||||
.. _helpers_entity_module:
|
||||
|
||||
:mod:`homeassistant.helpers.entity`
|
||||
-----------------------------------
|
||||
|
||||
.. automodule:: homeassistant.helpers.entity
|
||||
|
||||
.. autoclass:: Entity
|
||||
:members:
|
||||
|
||||
.. autoclass:: ToggleEntity
|
||||
:members:
|
||||
@@ -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
|
||||
7
docs/source/api/exceptions.rst
Normal file
7
docs/source/api/exceptions.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
.. _exceptions_module:
|
||||
|
||||
:mod:`homeassistant.exceptions`
|
||||
-------------------------------
|
||||
|
||||
.. automodule:: homeassistant.exceptions
|
||||
:members:
|
||||
@@ -1,287 +1,335 @@
|
||||
homeassistant.helpers package
|
||||
=============================
|
||||
|
||||
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
|
||||
---------------
|
||||
:mod:`homeassistant.helpers`
|
||||
============================
|
||||
|
||||
.. automodule:: homeassistant.helpers
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
: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:
|
||||
|
||||
@@ -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:
|
||||
7
docs/source/api/loader.rst
Normal file
7
docs/source/api/loader.rst
Normal file
@@ -0,0 +1,7 @@
|
||||
.. _loader_module:
|
||||
|
||||
:mod:`homeassistant.loader`
|
||||
---------------------------
|
||||
|
||||
.. automodule:: homeassistant.loader
|
||||
:members:
|
||||
@@ -1,86 +1,159 @@
|
||||
homeassistant.util package
|
||||
==========================
|
||||
|
||||
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
|
||||
---------------
|
||||
:mod:`homeassistant.util`
|
||||
=========================
|
||||
|
||||
.. automodule:: homeassistant.util
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
: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:
|
||||
|
||||
@@ -26,7 +26,7 @@ from homeassistant.const import __short_version__, __version__
|
||||
PROJECT_NAME = 'Home Assistant'
|
||||
PROJECT_PACKAGE_NAME = 'homeassistant'
|
||||
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 '
|
||||
'home automation platform running on Python 3. '
|
||||
'Track and control all devices at home and '
|
||||
|
||||
@@ -339,7 +339,7 @@ def main() -> int:
|
||||
if 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:
|
||||
try_to_restart()
|
||||
|
||||
|
||||
@@ -215,12 +215,14 @@ class AuthManager:
|
||||
|
||||
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."""
|
||||
kwargs: Dict[str, Any] = {
|
||||
"name": name,
|
||||
"is_active": True,
|
||||
"group_ids": [GROUP_ID_ADMIN],
|
||||
"group_ids": group_ids or [],
|
||||
}
|
||||
|
||||
if await self._user_should_be_owner():
|
||||
|
||||
@@ -123,4 +123,8 @@ class Credentials:
|
||||
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
|
||||
|
||||
@@ -20,6 +20,7 @@ from homeassistant.const import (
|
||||
REQUIRED_NEXT_PYTHON_VER,
|
||||
)
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.setup import DATA_SETUP, async_setup_component
|
||||
from homeassistant.util.logging import AsyncHandler
|
||||
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(
|
||||
config: Dict[str, Any], hass: core.HomeAssistant
|
||||
config: ConfigType, hass: core.HomeAssistant
|
||||
) -> Optional[core.HomeAssistant]:
|
||||
"""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]
|
||||
) -> None:
|
||||
"""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)
|
||||
|
||||
# Start up debuggers. Start these first in case they want to wait.
|
||||
debuggers = domains & DEBUGGER_INTEGRATIONS
|
||||
if debuggers:
|
||||
_LOGGER.debug("Starting up debuggers %s", debuggers)
|
||||
await asyncio.gather(
|
||||
*(async_setup_component(hass, domain, config) for domain in debuggers)
|
||||
)
|
||||
await async_setup_multi_components(debuggers)
|
||||
domains -= DEBUGGER_INTEGRATIONS
|
||||
|
||||
# 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:
|
||||
_LOGGER.info("Setting up %s", logging_domains)
|
||||
|
||||
await asyncio.gather(
|
||||
*(async_setup_component(hass, domain, config) for domain in logging_domains)
|
||||
)
|
||||
await async_setup_multi_components(logging_domains)
|
||||
|
||||
# Kick off loading the registries. They don't need to be awaited.
|
||||
asyncio.gather(
|
||||
@@ -369,9 +383,7 @@ async def _async_set_up_integrations(
|
||||
)
|
||||
|
||||
if stage_1_domains:
|
||||
await asyncio.gather(
|
||||
*(async_setup_component(hass, domain, config) for domain in stage_1_domains)
|
||||
)
|
||||
await async_setup_multi_components(stage_1_domains)
|
||||
|
||||
# Load all integrations
|
||||
after_dependencies: Dict[str, Set[str]] = {}
|
||||
@@ -400,9 +412,7 @@ async def _async_set_up_integrations(
|
||||
|
||||
_LOGGER.debug("Setting up %s", domains_to_load)
|
||||
|
||||
await asyncio.gather(
|
||||
*(async_setup_component(hass, domain, config) for domain in domains_to_load)
|
||||
)
|
||||
await async_setup_multi_components(domains_to_load)
|
||||
|
||||
last_load = domains_to_load
|
||||
stage_2_domains -= domains_to_load
|
||||
@@ -412,9 +422,7 @@ async def _async_set_up_integrations(
|
||||
if stage_2_domains:
|
||||
_LOGGER.debug("Final set up: %s", stage_2_domains)
|
||||
|
||||
await asyncio.gather(
|
||||
*(async_setup_component(hass, domain, config) for domain in stage_2_domains)
|
||||
)
|
||||
await async_setup_multi_components(stage_2_domains)
|
||||
|
||||
# Wrap up startup
|
||||
await hass.async_block_till_done()
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
"title": "Fyll ut innloggingsinformasjonen for Abode"
|
||||
}
|
||||
},
|
||||
"title": "Abode"
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
@@ -263,7 +263,6 @@ def setup_abode_events(hass):
|
||||
TIMELINE.TEST_GROUP,
|
||||
TIMELINE.CAPTURE_GROUP,
|
||||
TIMELINE.DEVICE_GROUP,
|
||||
TIMELINE.AUTOMATION_EDIT_GROUP,
|
||||
]
|
||||
|
||||
for event in events:
|
||||
@@ -343,21 +342,14 @@ class AbodeDevice(Entity):
|
||||
class AbodeAutomation(Entity):
|
||||
"""Representation of an Abode automation."""
|
||||
|
||||
def __init__(self, data, automation, event=None):
|
||||
def __init__(self, data, automation):
|
||||
"""Initialize for Abode automation."""
|
||||
self._data = data
|
||||
self._automation = automation
|
||||
self._event = event
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Subscribe to a group of Abode timeline events."""
|
||||
if self._event:
|
||||
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)
|
||||
"""Set up automation entity."""
|
||||
self.hass.data[DOMAIN].entity_ids.add(self.entity_id)
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
@@ -385,8 +377,3 @@ class AbodeAutomation(Entity):
|
||||
def unique_id(self):
|
||||
"""Return a unique ID to use for this automation."""
|
||||
return self._automation.automation_id
|
||||
|
||||
def _update_callback(self, device):
|
||||
"""Update the automation state."""
|
||||
self._automation.refresh()
|
||||
self.schedule_update_ha_state()
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
"""Support for Abode Security System binary sensors."""
|
||||
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 .const import DOMAIN
|
||||
@@ -38,4 +41,6 @@ class AbodeBinarySensor(AbodeDevice, BinarySensorDevice):
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return the class of the binary sensor."""
|
||||
if self._device.get_value("is_window") == "1":
|
||||
return DEVICE_CLASS_WINDOW
|
||||
return self._device.generic_type
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Support for Abode Security System switches."""
|
||||
import abodepy.helpers.constants as CONST
|
||||
import abodepy.helpers.timeline as TIMELINE
|
||||
|
||||
from homeassistant.components.switch import SwitchDevice
|
||||
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))
|
||||
|
||||
for automation in data.abode.get_automations():
|
||||
entities.append(
|
||||
AbodeAutomationSwitch(data, automation, TIMELINE.AUTOMATION_EDIT_GROUP)
|
||||
)
|
||||
entities.append(AbodeAutomationSwitch(data, automation))
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
@@ -52,7 +49,7 @@ class AbodeAutomationSwitch(AbodeAutomation, SwitchDevice):
|
||||
"""A switch implementation for Abode automations."""
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Subscribe Abode events."""
|
||||
"""Set up trigger automation service."""
|
||||
await super().async_added_to_hass()
|
||||
|
||||
signal = f"abode_trigger_automation_{self.entity_id}"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"data": {
|
||||
"host": "Vert",
|
||||
"password": "Passord",
|
||||
"port": "Port",
|
||||
"port": "",
|
||||
"ssl": "AdGuard Hjem bruker et SSL-sertifikat",
|
||||
"username": "Brukernavn",
|
||||
"verify_ssl": "AdGuard Home bruker et riktig sertifikat"
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
"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",
|
||||
"title": "Airly"
|
||||
"title": ""
|
||||
}
|
||||
},
|
||||
"title": "Airly"
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from math import ceil
|
||||
|
||||
from aiohttp.client_exceptions import ClientConnectorError
|
||||
from airly import Airly
|
||||
@@ -10,28 +11,40 @@ import async_timeout
|
||||
|
||||
from homeassistant.const import CONF_API_KEY, CONF_LATITUDE, CONF_LONGITUDE
|
||||
from homeassistant.core import Config, HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
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 (
|
||||
ATTR_API_ADVICE,
|
||||
ATTR_API_CAQI,
|
||||
ATTR_API_CAQI_DESCRIPTION,
|
||||
ATTR_API_CAQI_LEVEL,
|
||||
DATA_CLIENT,
|
||||
DOMAIN,
|
||||
MAX_REQUESTS_PER_DAY,
|
||||
NO_AIRLY_SENSORS,
|
||||
)
|
||||
|
||||
PLATFORMS = ["air_quality", "sensor"]
|
||||
|
||||
_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:
|
||||
"""Set up configured Airly."""
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN][DATA_CLIENT] = {}
|
||||
return True
|
||||
|
||||
|
||||
@@ -48,70 +61,85 @@ async def async_setup_entry(hass, config_entry):
|
||||
)
|
||||
|
||||
websession = async_get_clientsession(hass)
|
||||
|
||||
airly = AirlyData(websession, api_key, latitude, longitude)
|
||||
|
||||
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")
|
||||
# Change update_interval for other Airly instances
|
||||
update_interval = set_update_interval(
|
||||
hass, len(hass.config_entries.async_entries(DOMAIN))
|
||||
)
|
||||
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
|
||||
|
||||
|
||||
async def async_unload_entry(hass, config_entry):
|
||||
"""Unload a config entry."""
|
||||
hass.data[DOMAIN][DATA_CLIENT].pop(config_entry.entry_id)
|
||||
await hass.config_entries.async_forward_entry_unload(config_entry, "air_quality")
|
||||
await hass.config_entries.async_forward_entry_unload(config_entry, "sensor")
|
||||
return True
|
||||
unload_ok = all(
|
||||
await asyncio.gather(
|
||||
*[
|
||||
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."""
|
||||
|
||||
def __init__(self, session, api_key, latitude, longitude):
|
||||
def __init__(self, hass, session, api_key, latitude, longitude, update_interval):
|
||||
"""Initialize."""
|
||||
self.latitude = latitude
|
||||
self.longitude = longitude
|
||||
self.airly = Airly(api_key, session)
|
||||
self.data = {}
|
||||
|
||||
@Throttle(DEFAULT_SCAN_INTERVAL)
|
||||
async def async_update(self):
|
||||
"""Update Airly data."""
|
||||
super().__init__(hass, _LOGGER, name=DOMAIN, update_interval=update_interval)
|
||||
|
||||
try:
|
||||
with async_timeout.timeout(20):
|
||||
measurements = self.airly.create_measurements_session_point(
|
||||
self.latitude, self.longitude
|
||||
)
|
||||
async def _async_update_data(self):
|
||||
"""Update data via library."""
|
||||
data = {}
|
||||
with async_timeout.timeout(20):
|
||||
measurements = self.airly.create_measurements_session_point(
|
||||
self.latitude, self.longitude
|
||||
)
|
||||
try:
|
||||
await measurements.update()
|
||||
except (AirlyError, ClientConnectorError) as error:
|
||||
raise UpdateFailed(error)
|
||||
|
||||
values = measurements.current["values"]
|
||||
index = measurements.current["indexes"][0]
|
||||
standards = measurements.current["standards"]
|
||||
values = measurements.current["values"]
|
||||
index = measurements.current["indexes"][0]
|
||||
standards = measurements.current["standards"]
|
||||
|
||||
if index["description"] == NO_AIRLY_SENSORS:
|
||||
_LOGGER.error("Can't retrieve data: no Airly sensors in this area")
|
||||
return
|
||||
for value in values:
|
||||
self.data[value["name"]] = value["value"]
|
||||
for standard in standards:
|
||||
self.data[f"{standard['pollutant']}_LIMIT"] = standard["limit"]
|
||||
self.data[f"{standard['pollutant']}_PERCENT"] = standard["percent"]
|
||||
self.data[ATTR_API_CAQI] = index["value"]
|
||||
self.data[ATTR_API_CAQI_LEVEL] = index["level"].lower().replace("_", " ")
|
||||
self.data[ATTR_API_CAQI_DESCRIPTION] = index["description"]
|
||||
self.data[ATTR_API_ADVICE] = index["advice"]
|
||||
_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 = {}
|
||||
if index["description"] == NO_AIRLY_SENSORS:
|
||||
raise UpdateFailed("Can't retrieve data: no Airly sensors in this area")
|
||||
for value in values:
|
||||
data[value["name"]] = value["value"]
|
||||
for standard in standards:
|
||||
data[f"{standard['pollutant']}_LIMIT"] = standard["limit"]
|
||||
data[f"{standard['pollutant']}_PERCENT"] = standard["percent"]
|
||||
data[ATTR_API_CAQI] = index["value"]
|
||||
data[ATTR_API_CAQI_LEVEL] = index["level"].lower().replace("_", " ")
|
||||
data[ATTR_API_CAQI_DESCRIPTION] = index["description"]
|
||||
data[ATTR_API_ADVICE] = index["advice"]
|
||||
return data
|
||||
|
||||
@@ -18,13 +18,13 @@ from .const import (
|
||||
ATTR_API_PM25,
|
||||
ATTR_API_PM25_LIMIT,
|
||||
ATTR_API_PM25_PERCENT,
|
||||
DATA_CLIENT,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
ATTRIBUTION = "Data provided by Airly"
|
||||
|
||||
LABEL_ADVICE = "advice"
|
||||
LABEL_AQI_DESCRIPTION = f"{ATTR_AQI}_description"
|
||||
LABEL_AQI_LEVEL = f"{ATTR_AQI}_level"
|
||||
LABEL_PM_2_5_LIMIT = f"{ATTR_PM_2_5}_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."""
|
||||
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):
|
||||
@@ -56,23 +58,23 @@ def round_state(func):
|
||||
class AirlyAirQuality(AirQualityEntity):
|
||||
"""Define an Airly air quality."""
|
||||
|
||||
def __init__(self, airly, name, unique_id):
|
||||
def __init__(self, coordinator, name, unique_id):
|
||||
"""Initialize."""
|
||||
self.airly = airly
|
||||
self.data = airly.data
|
||||
self.coordinator = coordinator
|
||||
self._name = name
|
||||
self._unique_id = unique_id
|
||||
self._pm_2_5 = None
|
||||
self._pm_10 = None
|
||||
self._aqi = None
|
||||
self._icon = "mdi:blur"
|
||||
self._attrs = {}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return the polling requirement of the entity."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def icon(self):
|
||||
"""Return the icon."""
|
||||
@@ -82,30 +84,25 @@ class AirlyAirQuality(AirQualityEntity):
|
||||
@round_state
|
||||
def air_quality_index(self):
|
||||
"""Return the air quality index."""
|
||||
return self._aqi
|
||||
return self.coordinator.data[ATTR_API_CAQI]
|
||||
|
||||
@property
|
||||
@round_state
|
||||
def particulate_matter_2_5(self):
|
||||
"""Return the particulate matter 2.5 level."""
|
||||
return self._pm_2_5
|
||||
return self.coordinator.data[ATTR_API_PM25]
|
||||
|
||||
@property
|
||||
@round_state
|
||||
def particulate_matter_10(self):
|
||||
"""Return the particulate matter 10 level."""
|
||||
return self._pm_10
|
||||
return self.coordinator.data[ATTR_API_PM10]
|
||||
|
||||
@property
|
||||
def attribution(self):
|
||||
"""Return the attribution."""
|
||||
return ATTRIBUTION
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the CAQI description."""
|
||||
return self.data[ATTR_API_CAQI_DESCRIPTION]
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return a unique_id for this entity."""
|
||||
@@ -114,25 +111,29 @@ class AirlyAirQuality(AirQualityEntity):
|
||||
@property
|
||||
def available(self):
|
||||
"""Return True if entity is available."""
|
||||
return bool(self.data)
|
||||
return self.coordinator.last_update_success
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
self._attrs[LABEL_ADVICE] = self.data[ATTR_API_ADVICE]
|
||||
self._attrs[LABEL_AQI_LEVEL] = self.data[ATTR_API_CAQI_LEVEL]
|
||||
self._attrs[LABEL_PM_2_5_LIMIT] = self.data[ATTR_API_PM25_LIMIT]
|
||||
self._attrs[LABEL_PM_2_5_PERCENT] = round(self.data[ATTR_API_PM25_PERCENT])
|
||||
self._attrs[LABEL_PM_10_LIMIT] = self.data[ATTR_API_PM10_LIMIT]
|
||||
self._attrs[LABEL_PM_10_PERCENT] = round(self.data[ATTR_API_PM10_PERCENT])
|
||||
return self._attrs
|
||||
return {
|
||||
LABEL_AQI_DESCRIPTION: self.coordinator.data[ATTR_API_CAQI_DESCRIPTION],
|
||||
LABEL_ADVICE: self.coordinator.data[ATTR_API_ADVICE],
|
||||
LABEL_AQI_LEVEL: self.coordinator.data[ATTR_API_CAQI_LEVEL],
|
||||
LABEL_PM_2_5_LIMIT: self.coordinator.data[ATTR_API_PM25_LIMIT],
|
||||
LABEL_PM_2_5_PERCENT: round(self.coordinator.data[ATTR_API_PM25_PERCENT]),
|
||||
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):
|
||||
"""Update the entity."""
|
||||
await self.airly.async_update()
|
||||
|
||||
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]
|
||||
"""Update Airly entity."""
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
@@ -13,7 +13,7 @@ ATTR_API_PM25_LIMIT = "PM25_LIMIT"
|
||||
ATTR_API_PM25_PERCENT = "PM25_PERCENT"
|
||||
ATTR_API_PRESSURE = "PRESSURE"
|
||||
ATTR_API_TEMPERATURE = "TEMPERATURE"
|
||||
DATA_CLIENT = "client"
|
||||
DEFAULT_NAME = "Airly"
|
||||
DOMAIN = "airly"
|
||||
MAX_REQUESTS_PER_DAY = 100
|
||||
NO_AIRLY_SENSORS = "There are no Airly sensors in this area yet."
|
||||
|
||||
@@ -18,7 +18,6 @@ from .const import (
|
||||
ATTR_API_PM1,
|
||||
ATTR_API_PRESSURE,
|
||||
ATTR_API_TEMPERATURE,
|
||||
DATA_CLIENT,
|
||||
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."""
|
||||
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 = []
|
||||
for sensor in SENSOR_TYPES:
|
||||
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):
|
||||
@@ -85,10 +84,9 @@ def round_state(func):
|
||||
class AirlySensor(Entity):
|
||||
"""Define an Airly sensor."""
|
||||
|
||||
def __init__(self, airly, name, kind, unique_id):
|
||||
def __init__(self, coordinator, name, kind, unique_id):
|
||||
"""Initialize."""
|
||||
self.airly = airly
|
||||
self.data = airly.data
|
||||
self.coordinator = coordinator
|
||||
self._name = name
|
||||
self._unique_id = unique_id
|
||||
self.kind = kind
|
||||
@@ -103,10 +101,15 @@ class AirlySensor(Entity):
|
||||
"""Return the name."""
|
||||
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
|
||||
def state(self):
|
||||
"""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]:
|
||||
self._state = round(self._state)
|
||||
if self.kind in [ATTR_API_TEMPERATURE, ATTR_API_HUMIDITY]:
|
||||
@@ -142,11 +145,16 @@ class AirlySensor(Entity):
|
||||
@property
|
||||
def available(self):
|
||||
"""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):
|
||||
"""Update the sensor."""
|
||||
await self.airly.async_update()
|
||||
|
||||
if self.airly.data:
|
||||
self.data = self.airly.data
|
||||
"""Update Airly entity."""
|
||||
await self.coordinator.async_request_refresh()
|
||||
|
||||
@@ -19,5 +19,16 @@
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Dieser API-Schl\u00fcssel wird bereits verwendet."
|
||||
"already_configured": "Diese Koordinaten wurden bereits registriert."
|
||||
},
|
||||
"error": {
|
||||
"invalid_api_key": "Ung\u00fcltiger API-Schl\u00fcssel"
|
||||
@@ -11,11 +11,24 @@
|
||||
"data": {
|
||||
"api_key": "API-Schl\u00fcssel",
|
||||
"latitude": "Breitengrad",
|
||||
"longitude": "L\u00e4ngengrad"
|
||||
"longitude": "L\u00e4ngengrad",
|
||||
"show_on_map": "Zeigen Sie die \u00fcberwachte Geografie auf der Karte an"
|
||||
},
|
||||
"description": "\u00dcberwachen Sie die Luftqualit\u00e4t an einem geografischen Ort.",
|
||||
"title": "Konfigurieren Sie 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "This API key is already in use."
|
||||
"already_configured": "These coordinates have already been registered."
|
||||
},
|
||||
"error": {
|
||||
"invalid_api_key": "Invalid API key"
|
||||
@@ -11,7 +11,8 @@
|
||||
"data": {
|
||||
"api_key": "API Key",
|
||||
"latitude": "Latitude",
|
||||
"longitude": "Longitude"
|
||||
"longitude": "Longitude",
|
||||
"show_on_map": "Show monitored geography on the map"
|
||||
},
|
||||
"description": "Monitor air quality in a geographical location.",
|
||||
"title": "Configure AirVisual"
|
||||
|
||||
@@ -19,5 +19,16 @@
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
homeassistant/components/airvisual/.translations/fr.json
Normal file
31
homeassistant/components/airvisual/.translations/fr.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"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",
|
||||
"show_on_map": "Afficher la g\u00e9ographie surveill\u00e9e sur la carte"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Questa chiave API \u00e8 gi\u00e0 in uso."
|
||||
"already_configured": "Queste coordinate sono gi\u00e0 state registrate."
|
||||
},
|
||||
"error": {
|
||||
"invalid_api_key": "Chiave API non valida"
|
||||
@@ -19,5 +19,16 @@
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
homeassistant/components/airvisual/.translations/ko.json
Normal file
34
homeassistant/components/airvisual/.translations/ko.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\uc774 API \ud0a4\ub294 \uc774\ubbf8 \uc0ac\uc6a9 \uc911\uc785\ub2c8\ub2e4."
|
||||
},
|
||||
"error": {
|
||||
"invalid_api_key": "\uc798\ubabb\ub41c API \ud0a4"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "API \ud0a4",
|
||||
"latitude": "\uc704\ub3c4",
|
||||
"longitude": "\uacbd\ub3c4",
|
||||
"show_on_map": "\uc9c0\ub3c4\uc5d0 \ubaa8\ub2c8\ud130\ub9c1\ub41c \uc9c0\ub9ac \uc815\ubcf4 \ud45c\uc2dc"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,5 +17,13 @@
|
||||
}
|
||||
},
|
||||
"title": "AirVisual"
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"description": "Verschidden Optioune fir d'AirVisual Integratioun d\u00e9fin\u00e9ieren.",
|
||||
"title": "Airvisual ariichten"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Denne API-n\u00f8kkelen er allerede i bruk."
|
||||
"already_configured": "Disse koordinatene er allerede registrert."
|
||||
},
|
||||
"error": {
|
||||
"invalid_api_key": "Ugyldig API-n\u00f8kkel"
|
||||
@@ -18,6 +18,17 @@
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
homeassistant/components/airvisual/.translations/pl.json
Normal file
34
homeassistant/components/airvisual/.translations/pl.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"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",
|
||||
"show_on_map": "Wy\u015bwietlaj encje na mapie"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,5 +19,16 @@
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
homeassistant/components/airvisual/.translations/sk.json
Normal file
12
homeassistant/components/airvisual/.translations/sk.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"latitude": "Zemepisn\u00e1 \u0161\u00edrka",
|
||||
"longitude": "Zemepisn\u00e1 d\u013a\u017eka"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
homeassistant/components/airvisual/.translations/sl.json
Normal file
34
homeassistant/components/airvisual/.translations/sl.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"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",
|
||||
"show_on_map": "Prika\u017ei nadzorovano obmo\u010dje na zemljevidu"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "\u6b64 API \u5bc6\u9470\u5df2\u88ab\u4f7f\u7528\u3002"
|
||||
"already_configured": "\u6b64\u4e9b\u5ea7\u6a19\u5df2\u8a3b\u518a\u3002"
|
||||
},
|
||||
"error": {
|
||||
"invalid_api_key": "API \u5bc6\u78bc\u7121\u6548"
|
||||
@@ -19,5 +19,16 @@
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
"""The airvisual component."""
|
||||
import asyncio
|
||||
import logging
|
||||
|
||||
from pyairvisual import Client
|
||||
@@ -23,7 +22,6 @@ from homeassistant.helpers.event import async_track_time_interval
|
||||
from .const import (
|
||||
CONF_CITY,
|
||||
CONF_COUNTRY,
|
||||
CONF_GEOGRAPHIES,
|
||||
DATA_CLIENT,
|
||||
DEFAULT_SCAN_INTERVAL,
|
||||
DOMAIN,
|
||||
@@ -36,7 +34,7 @@ DATA_LISTENER = "listener"
|
||||
|
||||
DEFAULT_OPTIONS = {CONF_SHOW_ON_MAP: True}
|
||||
|
||||
CONF_NODE_ID = "node_id"
|
||||
CONF_GEOGRAPHIES = "geographies"
|
||||
|
||||
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):
|
||||
"""Generate a unique ID from a geography dict."""
|
||||
if CONF_CITY in geography_dict:
|
||||
return ",".join(
|
||||
return ", ".join(
|
||||
(
|
||||
geography_dict[CONF_CITY],
|
||||
geography_dict[CONF_STATE],
|
||||
geography_dict[CONF_COUNTRY],
|
||||
)
|
||||
)
|
||||
return ",".join(
|
||||
return ", ".join(
|
||||
(str(geography_dict[CONF_LATITUDE]), str(geography_dict[CONF_LONGITUDE]))
|
||||
)
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Set up the AirVisual component."""
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN][DATA_CLIENT] = {}
|
||||
hass.data[DOMAIN][DATA_LISTENER] = {}
|
||||
hass.data[DOMAIN] = {DATA_CLIENT: {}, DATA_LISTENER: {}}
|
||||
|
||||
if DOMAIN not in config:
|
||||
return True
|
||||
|
||||
conf = config[DOMAIN]
|
||||
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": SOURCE_IMPORT}, data=conf
|
||||
for geography in conf.get(
|
||||
CONF_GEOGRAPHIES,
|
||||
[{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
|
||||
|
||||
@@ -144,6 +146,45 @@ async def async_setup_entry(hass, config_entry):
|
||||
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):
|
||||
"""Unload an AirVisual config entry."""
|
||||
hass.data[DOMAIN][DATA_CLIENT].pop(config_entry.entry_id)
|
||||
@@ -170,40 +211,28 @@ class AirVisualData:
|
||||
self._client = client
|
||||
self._hass = hass
|
||||
self.data = {}
|
||||
self.geography_data = config_entry.data
|
||||
self.geography_id = config_entry.unique_id
|
||||
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):
|
||||
"""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():
|
||||
if CONF_CITY in geography:
|
||||
tasks.append(
|
||||
self._client.api.city(
|
||||
geography[CONF_CITY],
|
||||
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
|
||||
try:
|
||||
self.data[self.geography_id] = await api_coro
|
||||
except AirVisualError as err:
|
||||
_LOGGER.error("Error while retrieving data: %s", err)
|
||||
self.data[self.geography_id] = {}
|
||||
|
||||
_LOGGER.debug("Received new data")
|
||||
async_dispatcher_send(self._hass, TOPIC_UPDATE)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""Define a config flow manager for AirVisual."""
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
from pyairvisual import Client
|
||||
from pyairvisual.errors import InvalidKeyError
|
||||
@@ -15,15 +15,14 @@ from homeassistant.const import (
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import aiohttp_client, config_validation as cv
|
||||
|
||||
from .const import CONF_GEOGRAPHIES, DOMAIN # pylint: disable=unused-import
|
||||
|
||||
_LOGGER = logging.getLogger("homeassistant.components.airvisual")
|
||||
from . import async_get_geography_id
|
||||
from .const import DOMAIN # pylint: disable=unused-import
|
||||
|
||||
|
||||
class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
"""Handle an AirVisual config flow."""
|
||||
|
||||
VERSION = 1
|
||||
VERSION = 2
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
|
||||
|
||||
@property
|
||||
@@ -68,35 +67,33 @@ class AirVisualFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||
if not user_input:
|
||||
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)
|
||||
client = Client(websession, api_key=user_input[CONF_API_KEY])
|
||||
|
||||
try:
|
||||
await client.api.nearest_city()
|
||||
except InvalidKeyError:
|
||||
return await self._show_form(errors={CONF_API_KEY: "invalid_api_key"})
|
||||
|
||||
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
|
||||
# If this is the first (and only the first) time we've seen this API key, check
|
||||
# that it's valid:
|
||||
checked_keys = self.hass.data.setdefault("airvisual_checked_api_keys", set())
|
||||
check_keys_lock = self.hass.data.setdefault(
|
||||
"airvisual_checked_api_keys_lock", asyncio.Lock()
|
||||
)
|
||||
|
||||
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):
|
||||
"""Handle an AirVisual options flow."""
|
||||
|
||||
@@ -5,7 +5,6 @@ DOMAIN = "airvisual"
|
||||
|
||||
CONF_CITY = "city"
|
||||
CONF_COUNTRY = "country"
|
||||
CONF_GEOGRAPHIES = "geographies"
|
||||
|
||||
DATA_CLIENT = "client"
|
||||
|
||||
|
||||
@@ -191,16 +191,19 @@ class AirVisualSensor(Entity):
|
||||
}
|
||||
)
|
||||
|
||||
geography = self._airvisual.geographies[self._geography_id]
|
||||
if CONF_LATITUDE in geography:
|
||||
if CONF_LATITUDE in self._airvisual.geography_data:
|
||||
if self._airvisual.options[CONF_SHOW_ON_MAP]:
|
||||
self._attrs[ATTR_LATITUDE] = geography[CONF_LATITUDE]
|
||||
self._attrs[ATTR_LONGITUDE] = geography[CONF_LONGITUDE]
|
||||
self._attrs[ATTR_LATITUDE] = self._airvisual.geography_data[
|
||||
CONF_LATITUDE
|
||||
]
|
||||
self._attrs[ATTR_LONGITUDE] = self._airvisual.geography_data[
|
||||
CONF_LONGITUDE
|
||||
]
|
||||
self._attrs.pop("lati", None)
|
||||
self._attrs.pop("long", None)
|
||||
else:
|
||||
self._attrs["lati"] = geography[CONF_LATITUDE]
|
||||
self._attrs["long"] = geography[CONF_LONGITUDE]
|
||||
self._attrs["lati"] = self._airvisual.geography_data[CONF_LATITUDE]
|
||||
self._attrs["long"] = self._airvisual.geography_data[CONF_LONGITUDE]
|
||||
self._attrs.pop(ATTR_LATITUDE, None)
|
||||
self._attrs.pop(ATTR_LONGITUDE, None)
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"invalid_api_key": "Invalid API key"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "This API key is already in use."
|
||||
"already_configured": "These coordinates have already been registered."
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
|
||||
@@ -7,10 +7,17 @@
|
||||
"disarm": "Desactiva {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": {
|
||||
"armed_away": "{entity_name} activada en mode a fora",
|
||||
"armed_home": "{entity_name} activada en mode a casa",
|
||||
"armed_night": "{entity_name} activada en mode nocturn",
|
||||
"armed_away": "{entity_name} activada en mode 'a fora'",
|
||||
"armed_home": "{entity_name} activada en mode 'a casa'",
|
||||
"armed_night": "{entity_name} activada en mode 'nocturn'",
|
||||
"disarmed": "{entity_name} desactivada",
|
||||
"triggered": "{entity_name} disparat/ada"
|
||||
}
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"disarm": "Deaktivere {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": {
|
||||
"armed_away": "{entity_name} Unterwegs",
|
||||
"armed_home": "{entity_name} Zuhause",
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"disarm": "Disarm {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": {
|
||||
"armed_away": "{entity_name} armed away",
|
||||
"armed_home": "{entity_name} armed home",
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"disarm": "Desarmar {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": {
|
||||
"armed_away": "{entity_name} armado fuera",
|
||||
"armed_home": "{entity_name} armado en casa",
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"disarm": "D\u00e9sarmer {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": {
|
||||
"armed_away": "Armer {entity_name} en mode \"sortie\"",
|
||||
"armed_home": "Armer {entity_name} en mode \"maison\"",
|
||||
|
||||
@@ -7,11 +7,18 @@
|
||||
"disarm": "Disarmare {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": {
|
||||
"armed_away": "{entity_name} armata modalit\u00e0 fuori casa",
|
||||
"armed_home": "{entity_name} armata modalit\u00e0 a casa",
|
||||
"armed_night": "{entity_name} armata modalit\u00e0 notte",
|
||||
"disarmed": "{entity_name} disarmato",
|
||||
"armed_away": "{entity_name} attivato in modalit\u00e0 fuori casa",
|
||||
"armed_home": "{entity_name} attivato in modalit\u00e0 a casa",
|
||||
"armed_night": "{entity_name} attivato in modalit\u00e0 notte",
|
||||
"disarmed": "{entity_name} disattivato",
|
||||
"triggered": "{entity_name} attivato"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"disarm": "{entity_name} \uacbd\ube44\ud574\uc81c",
|
||||
"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": {
|
||||
"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",
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"disarm": "{entity_name} entsch\u00e4rfen",
|
||||
"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": {
|
||||
"armed_away": "{entity_name} ugeschalt fir Ennerwee",
|
||||
"armed_home": "{entity_name} ugeschalt fir Doheem",
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"disarm": "Deaktiver {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": {
|
||||
"armed_away": "{entity_name} aktivert borte",
|
||||
"armed_home": "{entity_name} aktivert hjemme",
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"disarm": "rozbr\u00f3j {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": {
|
||||
"armed_away": "{entity_name} zostanie uzbrojony (poza domem)",
|
||||
"armed_home": "{entity_name} zostanie uzbrojony (w domu)",
|
||||
|
||||
@@ -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}",
|
||||
"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": {
|
||||
"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}",
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"disarm": "Razoro\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": {
|
||||
"armed_away": "{entity_name} oboro\u017een - zdoma",
|
||||
"armed_home": "{entity_name} oboro\u017een - dom",
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
"disarm": "\u89e3\u9664{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": {
|
||||
"armed_away": "{entity_name}\u8a2d\u5b9a\u5916\u51fa",
|
||||
"armed_home": "{entity_name}\u8a2d\u5b9a\u5728\u5bb6",
|
||||
|
||||
@@ -5,3 +5,10 @@ SUPPORT_ALARM_ARM_AWAY = 2
|
||||
SUPPORT_ALARM_ARM_NIGHT = 4
|
||||
SUPPORT_ALARM_TRIGGER = 8
|
||||
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"
|
||||
|
||||
162
homeassistant/components/alarm_control_panel/device_condition.py
Normal file
162
homeassistant/components/alarm_control_panel/device_condition.py
Normal 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
|
||||
@@ -1,18 +1,25 @@
|
||||
{
|
||||
"device_automation": {
|
||||
"action_type": {
|
||||
"arm_away": "Arm {entity_name} away",
|
||||
"arm_home": "Arm {entity_name} home",
|
||||
"arm_night": "Arm {entity_name} night",
|
||||
"disarm": "Disarm {entity_name}",
|
||||
"trigger": "Trigger {entity_name}"
|
||||
},
|
||||
"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"
|
||||
"device_automation": {
|
||||
"action_type": {
|
||||
"arm_away": "Arm {entity_name} away",
|
||||
"arm_home": "Arm {entity_name} home",
|
||||
"arm_night": "Arm {entity_name} night",
|
||||
"disarm": "Disarm {entity_name}",
|
||||
"trigger": "Trigger {entity_name}"
|
||||
},
|
||||
"condition_type": {
|
||||
"is_triggered": "{entity_name} is triggered",
|
||||
"is_disarmed": "{entity_name} is disarmed",
|
||||
"is_armed_home": "{entity_name} is armed home",
|
||||
"is_armed_away": "{entity_name} is armed away",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ CONF_ZONE_RFID = "rfid"
|
||||
CONF_ZONES = "zones"
|
||||
CONF_RELAY_ADDR = "relayaddr"
|
||||
CONF_RELAY_CHAN = "relaychan"
|
||||
CONF_CODE_ARM_REQUIRED = "code_arm_required"
|
||||
|
||||
DEFAULT_DEVICE_TYPE = "socket"
|
||||
DEFAULT_DEVICE_HOST = "localhost"
|
||||
@@ -42,6 +43,7 @@ DEFAULT_DEVICE_BAUD = 115200
|
||||
|
||||
DEFAULT_AUTO_BYPASS = False
|
||||
DEFAULT_PANEL_DISPLAY = False
|
||||
DEFAULT_CODE_ARM_REQUIRED = True
|
||||
|
||||
DEFAULT_ZONE_TYPE = "opening"
|
||||
|
||||
@@ -105,6 +107,9 @@ CONFIG_SCHEMA = vol.Schema(
|
||||
CONF_PANEL_DISPLAY, default=DEFAULT_PANEL_DISPLAY
|
||||
): 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},
|
||||
}
|
||||
)
|
||||
@@ -121,6 +126,7 @@ def setup(hass, config):
|
||||
device = conf[CONF_DEVICE]
|
||||
display = conf[CONF_PANEL_DISPLAY]
|
||||
auto_bypass = conf[CONF_AUTO_BYPASS]
|
||||
code_arm_required = conf[CONF_CODE_ARM_REQUIRED]
|
||||
zones = conf.get(CONF_ZONES)
|
||||
|
||||
device_type = device[CONF_DEVICE_TYPE]
|
||||
@@ -206,7 +212,11 @@ def setup(hass, config):
|
||||
hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, stop_alarmdecoder)
|
||||
|
||||
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:
|
||||
|
||||
@@ -21,7 +21,13 @@ from homeassistant.const import (
|
||||
)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
from . import CONF_AUTO_BYPASS, DATA_AD, DOMAIN, SIGNAL_PANEL_MESSAGE
|
||||
from . import (
|
||||
CONF_AUTO_BYPASS,
|
||||
CONF_CODE_ARM_REQUIRED,
|
||||
DATA_AD,
|
||||
DOMAIN,
|
||||
SIGNAL_PANEL_MESSAGE,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -39,7 +45,8 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
return
|
||||
|
||||
auto_bypass = discovery_info[CONF_AUTO_BYPASS]
|
||||
entity = AlarmDecoderAlarmPanel(auto_bypass)
|
||||
code_arm_required = discovery_info[CONF_CODE_ARM_REQUIRED]
|
||||
entity = AlarmDecoderAlarmPanel(auto_bypass, code_arm_required)
|
||||
add_entities([entity])
|
||||
|
||||
def alarm_toggle_chime_handler(service):
|
||||
@@ -70,7 +77,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
|
||||
class AlarmDecoderAlarmPanel(AlarmControlPanel):
|
||||
"""Representation of an AlarmDecoder-based alarm panel."""
|
||||
|
||||
def __init__(self, auto_bypass):
|
||||
def __init__(self, auto_bypass, code_arm_required):
|
||||
"""Initialize the alarm panel."""
|
||||
self._display = ""
|
||||
self._name = "Alarm Panel"
|
||||
@@ -85,6 +92,7 @@ class AlarmDecoderAlarmPanel(AlarmControlPanel):
|
||||
self._ready = None
|
||||
self._zone_bypassed = None
|
||||
self._auto_bypass = auto_bypass
|
||||
self._code_arm_required = code_arm_required
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Register callbacks."""
|
||||
@@ -140,6 +148,11 @@ class AlarmDecoderAlarmPanel(AlarmControlPanel):
|
||||
"""Return the list of supported features."""
|
||||
return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY | SUPPORT_ALARM_ARM_NIGHT
|
||||
|
||||
@property
|
||||
def code_arm_required(self):
|
||||
"""Whether the code is required for arm actions."""
|
||||
return self._code_arm_required
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
@@ -153,6 +166,7 @@ class AlarmDecoderAlarmPanel(AlarmControlPanel):
|
||||
"programming_mode": self._programming_mode,
|
||||
"ready": self._ready,
|
||||
"zone_bypassed": self._zone_bypassed,
|
||||
"code_arm_required": self._code_arm_required,
|
||||
}
|
||||
|
||||
def alarm_disarm(self, code=None):
|
||||
@@ -166,6 +180,8 @@ class AlarmDecoderAlarmPanel(AlarmControlPanel):
|
||||
if self._auto_bypass:
|
||||
self.hass.data[DATA_AD].send(f"{code!s}6#")
|
||||
self.hass.data[DATA_AD].send(f"{code!s}2")
|
||||
elif not self._code_arm_required:
|
||||
self.hass.data[DATA_AD].send("#2")
|
||||
|
||||
def alarm_arm_home(self, code=None):
|
||||
"""Send arm home command."""
|
||||
@@ -173,11 +189,15 @@ class AlarmDecoderAlarmPanel(AlarmControlPanel):
|
||||
if self._auto_bypass:
|
||||
self.hass.data[DATA_AD].send(f"{code!s}6#")
|
||||
self.hass.data[DATA_AD].send(f"{code!s}3")
|
||||
elif not self._code_arm_required:
|
||||
self.hass.data[DATA_AD].send("#3")
|
||||
|
||||
def alarm_arm_night(self, code=None):
|
||||
"""Send arm night command."""
|
||||
if code:
|
||||
self.hass.data[DATA_AD].send(f"{code!s}7")
|
||||
elif not self._code_arm_required:
|
||||
self.hass.data[DATA_AD].send("#7")
|
||||
|
||||
def alarm_toggle_chime(self, code=None):
|
||||
"""Send toggle chime command."""
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
{
|
||||
"domain": "alarmdecoder",
|
||||
"name": "AlarmDecoder",
|
||||
"documentation": "https://www.home-assistant.io/integrations/alarmdecoder",
|
||||
"requirements": [
|
||||
"alarmdecoder==1.13.2"
|
||||
],
|
||||
"dependencies": [],
|
||||
"codeowners": []
|
||||
"domain": "alarmdecoder",
|
||||
"name": "AlarmDecoder",
|
||||
"documentation": "https://www.home-assistant.io/integrations/alarmdecoder",
|
||||
"requirements": ["alarmdecoder==1.13.2"],
|
||||
"dependencies": [],
|
||||
"codeowners": ["@ajschmidt8"]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
alarm_keypress:
|
||||
description: Send custom keypresses to the alarm.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name of the alarm control panel to trigger.
|
||||
example: 'alarm_control_panel.downstairs'
|
||||
keypress:
|
||||
description: 'String to send to the alarm panel.'
|
||||
example: '*71'
|
||||
@@ -11,9 +8,6 @@ alarm_keypress:
|
||||
alarm_toggle_chime:
|
||||
description: Send the alarm the toggle chime command.
|
||||
fields:
|
||||
entity_id:
|
||||
description: Name of the alarm control panel to trigger.
|
||||
example: 'alarm_control_panel.downstairs'
|
||||
code:
|
||||
description: A required code to toggle the alarm control panel chime with.
|
||||
example: 1234
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
"""The alarmdotcom component."""
|
||||
@@ -1,132 +0,0 @@
|
||||
"""Interfaces with Alarm.com alarm control panels."""
|
||||
import logging
|
||||
import re
|
||||
|
||||
from pyalarmdotcom import Alarmdotcom
|
||||
import voluptuous as vol
|
||||
|
||||
import homeassistant.components.alarm_control_panel as alarm
|
||||
from homeassistant.components.alarm_control_panel import PLATFORM_SCHEMA
|
||||
from homeassistant.components.alarm_control_panel.const import (
|
||||
SUPPORT_ALARM_ARM_AWAY,
|
||||
SUPPORT_ALARM_ARM_HOME,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_CODE,
|
||||
CONF_NAME,
|
||||
CONF_PASSWORD,
|
||||
CONF_USERNAME,
|
||||
STATE_ALARM_ARMED_AWAY,
|
||||
STATE_ALARM_ARMED_HOME,
|
||||
STATE_ALARM_DISARMED,
|
||||
)
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_NAME = "Alarm.com"
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(CONF_PASSWORD): cv.string,
|
||||
vol.Required(CONF_USERNAME): cv.string,
|
||||
vol.Optional(CONF_CODE): cv.positive_int,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up a Alarm.com control panel."""
|
||||
name = config.get(CONF_NAME)
|
||||
code = config.get(CONF_CODE)
|
||||
username = config.get(CONF_USERNAME)
|
||||
password = config.get(CONF_PASSWORD)
|
||||
|
||||
alarmdotcom = AlarmDotCom(hass, name, code, username, password)
|
||||
await alarmdotcom.async_login()
|
||||
async_add_entities([alarmdotcom])
|
||||
|
||||
|
||||
class AlarmDotCom(alarm.AlarmControlPanel):
|
||||
"""Representation of an Alarm.com status."""
|
||||
|
||||
def __init__(self, hass, name, code, username, password):
|
||||
"""Initialize the Alarm.com status."""
|
||||
|
||||
_LOGGER.debug("Setting up Alarm.com...")
|
||||
self._hass = hass
|
||||
self._name = name
|
||||
self._code = str(code) if code else None
|
||||
self._username = username
|
||||
self._password = password
|
||||
self._websession = async_get_clientsession(self._hass)
|
||||
self._state = None
|
||||
self._alarm = Alarmdotcom(username, password, self._websession, hass.loop)
|
||||
|
||||
async def async_login(self):
|
||||
"""Login to Alarm.com."""
|
||||
await self._alarm.async_login()
|
||||
|
||||
async def async_update(self):
|
||||
"""Fetch the latest state."""
|
||||
await self._alarm.async_update()
|
||||
return self._alarm.state
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the alarm."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def code_format(self):
|
||||
"""Return one or more digits/characters."""
|
||||
if self._code is None:
|
||||
return None
|
||||
if isinstance(self._code, str) and re.search("^\\d+$", self._code):
|
||||
return alarm.FORMAT_NUMBER
|
||||
return alarm.FORMAT_TEXT
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the device."""
|
||||
if self._alarm.state.lower() == "disarmed":
|
||||
return STATE_ALARM_DISARMED
|
||||
if self._alarm.state.lower() == "armed stay":
|
||||
return STATE_ALARM_ARMED_HOME
|
||||
if self._alarm.state.lower() == "armed away":
|
||||
return STATE_ALARM_ARMED_AWAY
|
||||
return None
|
||||
|
||||
@property
|
||||
def supported_features(self) -> int:
|
||||
"""Return the list of supported features."""
|
||||
return SUPPORT_ALARM_ARM_HOME | SUPPORT_ALARM_ARM_AWAY
|
||||
|
||||
@property
|
||||
def device_state_attributes(self):
|
||||
"""Return the state attributes."""
|
||||
return {"sensor_status": self._alarm.sensor_status}
|
||||
|
||||
async def async_alarm_disarm(self, code=None):
|
||||
"""Send disarm command."""
|
||||
if self._validate_code(code):
|
||||
await self._alarm.async_alarm_disarm()
|
||||
|
||||
async def async_alarm_arm_home(self, code=None):
|
||||
"""Send arm home command."""
|
||||
if self._validate_code(code):
|
||||
await self._alarm.async_alarm_arm_home()
|
||||
|
||||
async def async_alarm_arm_away(self, code=None):
|
||||
"""Send arm away command."""
|
||||
if self._validate_code(code):
|
||||
await self._alarm.async_alarm_arm_away()
|
||||
|
||||
def _validate_code(self, code):
|
||||
"""Validate given code."""
|
||||
check = self._code is None or code == self._code
|
||||
if not check:
|
||||
_LOGGER.warning("Wrong code entered")
|
||||
return check
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"domain": "alarmdotcom",
|
||||
"name": "Alarm.com",
|
||||
"documentation": "https://www.home-assistant.io/integrations/alarmdotcom",
|
||||
"requirements": ["pyalarmdotcom==0.3.2"],
|
||||
"dependencies": [],
|
||||
"codeowners": []
|
||||
}
|
||||
@@ -98,11 +98,7 @@ async def async_setup(hass, config):
|
||||
f"send command {data['request']['namespace']}/{data['request']['name']}"
|
||||
)
|
||||
|
||||
return {
|
||||
"name": "Amazon Alexa",
|
||||
"message": message,
|
||||
"entity_id": entity_id,
|
||||
}
|
||||
return {"name": "Amazon Alexa", "message": message, "entity_id": entity_id}
|
||||
|
||||
hass.components.logbook.async_describe_event(
|
||||
DOMAIN, EVENT_ALEXA_SMART_HOME, async_describe_logbook_event
|
||||
|
||||
@@ -169,6 +169,11 @@ class AlexaCapability:
|
||||
"""Return the supportedOperations object."""
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def camera_stream_configurations():
|
||||
"""Applicable only to CameraStreamController."""
|
||||
return None
|
||||
|
||||
def serialize_discovery(self):
|
||||
"""Serialize according to the Discovery API."""
|
||||
result = {"type": "AlexaInterface", "interface": self.name(), "version": "3"}
|
||||
@@ -222,6 +227,10 @@ class AlexaCapability:
|
||||
if inputs:
|
||||
result["inputs"] = inputs
|
||||
|
||||
camera_stream_configurations = self.camera_stream_configurations()
|
||||
if camera_stream_configurations:
|
||||
result["cameraStreamConfigurations"] = camera_stream_configurations
|
||||
|
||||
return result
|
||||
|
||||
def serialize_properties(self):
|
||||
@@ -1854,3 +1863,40 @@ class AlexaTimeHoldController(AlexaCapability):
|
||||
When false, Alexa does not send the Resume directive.
|
||||
"""
|
||||
return {"allowRemoteResume": self._allow_remote_resume}
|
||||
|
||||
|
||||
class AlexaCameraStreamController(AlexaCapability):
|
||||
"""Implements Alexa.CameraStreamController.
|
||||
|
||||
https://developer.amazon.com/docs/device-apis/alexa-camerastreamcontroller.html
|
||||
"""
|
||||
|
||||
supported_locales = {
|
||||
"de-DE",
|
||||
"en-AU",
|
||||
"en-CA",
|
||||
"en-GB",
|
||||
"en-IN",
|
||||
"en-US",
|
||||
"es-ES",
|
||||
"fr-FR",
|
||||
"it-IT",
|
||||
"ja-JP",
|
||||
}
|
||||
|
||||
def name(self):
|
||||
"""Return the Alexa API name of this interface."""
|
||||
return "Alexa.CameraStreamController"
|
||||
|
||||
def camera_stream_configurations(self):
|
||||
"""Return cameraStreamConfigurations object."""
|
||||
camera_stream_configurations = [
|
||||
{
|
||||
"protocols": ["HLS"],
|
||||
"resolutions": [{"width": 1280, "height": 720}],
|
||||
"authorizationTypes": ["NONE"],
|
||||
"videoCodecs": ["H264"],
|
||||
"audioCodecs": ["AAC"],
|
||||
}
|
||||
]
|
||||
return camera_stream_configurations
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
"""Alexa entity adapters."""
|
||||
import logging
|
||||
from typing import List
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from homeassistant.components import (
|
||||
alarm_control_panel,
|
||||
alert,
|
||||
automation,
|
||||
binary_sensor,
|
||||
camera,
|
||||
cover,
|
||||
fan,
|
||||
group,
|
||||
@@ -33,11 +36,13 @@ from homeassistant.const import (
|
||||
TEMP_FAHRENHEIT,
|
||||
)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers import network
|
||||
from homeassistant.util.decorator import Registry
|
||||
|
||||
from .capabilities import (
|
||||
Alexa,
|
||||
AlexaBrightnessController,
|
||||
AlexaCameraStreamController,
|
||||
AlexaChannelController,
|
||||
AlexaColorController,
|
||||
AlexaColorTemperatureController,
|
||||
@@ -68,6 +73,8 @@ from .capabilities import (
|
||||
)
|
||||
from .const import CONF_DESCRIPTION, CONF_DISPLAY_CATEGORIES
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
ENTITY_ADAPTERS = Registry()
|
||||
|
||||
TRANSLATION_TABLE = dict.fromkeys(map(ord, r"}{\/|\"()[]+~!><*%"), None)
|
||||
@@ -763,3 +770,41 @@ class VacuumCapabilities(AlexaEntity):
|
||||
|
||||
yield AlexaEndpointHealth(self.hass, self.entity)
|
||||
yield Alexa(self.hass)
|
||||
|
||||
|
||||
@ENTITY_ADAPTERS.register(camera.DOMAIN)
|
||||
class CameraCapabilities(AlexaEntity):
|
||||
"""Class to represent Camera capabilities."""
|
||||
|
||||
def default_display_categories(self):
|
||||
"""Return the display categories for this entity."""
|
||||
return [DisplayCategory.CAMERA]
|
||||
|
||||
def interfaces(self):
|
||||
"""Yield the supported interfaces."""
|
||||
if self._check_requirements():
|
||||
supported = self.entity.attributes.get(ATTR_SUPPORTED_FEATURES, 0)
|
||||
if supported & camera.SUPPORT_STREAM:
|
||||
yield AlexaCameraStreamController(self.entity)
|
||||
|
||||
yield AlexaEndpointHealth(self.hass, self.entity)
|
||||
yield Alexa(self.hass)
|
||||
|
||||
def _check_requirements(self):
|
||||
"""Check the hass URL for HTTPS scheme and port 443."""
|
||||
if "stream" not in self.hass.config.components:
|
||||
_LOGGER.error(
|
||||
"%s requires stream component for AlexaCameraStreamController",
|
||||
self.entity_id,
|
||||
)
|
||||
return False
|
||||
|
||||
url = urlparse(network.async_get_external_url(self.hass))
|
||||
if url.scheme != "https" or (url.port is not None and url.port != 443):
|
||||
_LOGGER.error(
|
||||
"%s requires HTTPS support on port 443 for AlexaCameraStreamController",
|
||||
self.entity_id,
|
||||
)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -4,6 +4,7 @@ import math
|
||||
|
||||
from homeassistant import core as ha
|
||||
from homeassistant.components import (
|
||||
camera,
|
||||
cover,
|
||||
fan,
|
||||
group,
|
||||
@@ -41,6 +42,7 @@ from homeassistant.const import (
|
||||
TEMP_CELSIUS,
|
||||
TEMP_FAHRENHEIT,
|
||||
)
|
||||
from homeassistant.helpers import network
|
||||
import homeassistant.util.color as color_util
|
||||
from homeassistant.util.decorator import Registry
|
||||
import homeassistant.util.dt as dt_util
|
||||
@@ -1523,3 +1525,28 @@ async def async_api_resume(hass, config, directive, context):
|
||||
)
|
||||
|
||||
return directive.response()
|
||||
|
||||
|
||||
@HANDLERS.register(("Alexa.CameraStreamController", "InitializeCameraStreams"))
|
||||
async def async_api_initialize_camera_stream(hass, config, directive, context):
|
||||
"""Process a InitializeCameraStreams request."""
|
||||
entity = directive.entity
|
||||
stream_source = await camera.async_request_stream(hass, entity.entity_id, fmt="hls")
|
||||
camera_image = hass.states.get(entity.entity_id).attributes["entity_picture"]
|
||||
external_url = network.async_get_external_url(hass)
|
||||
payload = {
|
||||
"cameraStreams": [
|
||||
{
|
||||
"uri": f"{external_url}{stream_source}",
|
||||
"protocol": "HLS",
|
||||
"resolution": {"width": 1280, "height": 720},
|
||||
"authorizationType": "NONE",
|
||||
"videoCodec": "H264",
|
||||
"audioCodec": "AAC",
|
||||
}
|
||||
],
|
||||
"imageUri": f"{external_url}{camera_image}",
|
||||
}
|
||||
return directive.response(
|
||||
name="Response", namespace="Alexa.CameraStreamController", payload=payload
|
||||
)
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/alexa",
|
||||
"requirements": [],
|
||||
"dependencies": ["http"],
|
||||
"after_dependencies": ["logbook"],
|
||||
"after_dependencies": ["logbook", "camera"],
|
||||
"codeowners": ["@home-assistant/cloud", "@ochlocracy"]
|
||||
}
|
||||
|
||||
@@ -14,6 +14,6 @@
|
||||
"title": "Velg autentiseringsmetode"
|
||||
}
|
||||
},
|
||||
"title": "Almond"
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,6 @@
|
||||
"title": "Autensiere Ambiclimate"
|
||||
}
|
||||
},
|
||||
"title": "Ambiclimate"
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Cette cl\u00e9 d'application est d\u00e9j\u00e0 utilis\u00e9e."
|
||||
},
|
||||
"error": {
|
||||
"identifier_exists": "Cl\u00e9 d'application et / ou cl\u00e9 API d\u00e9j\u00e0 enregistr\u00e9e",
|
||||
"invalid_key": "Cl\u00e9 d'API et / ou cl\u00e9 d'application non valide",
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
"title": "Fyll ut informasjonen din"
|
||||
}
|
||||
},
|
||||
"title": "Ambient PWS"
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "Ta klju\u010d za aplikacijo je \u017ee v uporabi."
|
||||
},
|
||||
"error": {
|
||||
"identifier_exists": "Aplikacijski klju\u010d in / ali klju\u010d API je \u017ee registriran",
|
||||
"invalid_key": "Neveljaven klju\u010d API in / ali klju\u010d aplikacije",
|
||||
|
||||
@@ -41,7 +41,6 @@ _LOGGER = logging.getLogger(__name__)
|
||||
DATA_CONFIG = "config"
|
||||
|
||||
DEFAULT_SOCKET_MIN_RETRY = 15
|
||||
DEFAULT_WATCHDOG_SECONDS = 5 * 60
|
||||
|
||||
TYPE_24HOURRAININ = "24hourrainin"
|
||||
TYPE_BAROMABSIN = "baromabsin"
|
||||
@@ -342,7 +341,6 @@ class AmbientStation:
|
||||
self._config_entry = config_entry
|
||||
self._entry_setup_complete = False
|
||||
self._hass = hass
|
||||
self._watchdog_listener = None
|
||||
self._ws_reconnect_delay = DEFAULT_SOCKET_MIN_RETRY
|
||||
self.client = client
|
||||
self.stations = {}
|
||||
@@ -359,21 +357,9 @@ class AmbientStation:
|
||||
async def ws_connect(self):
|
||||
"""Register handlers and connect to the websocket."""
|
||||
|
||||
async def _ws_reconnect(event_time):
|
||||
"""Forcibly disconnect from and reconnect to the websocket."""
|
||||
_LOGGER.debug("Watchdog expired; forcing socket reconnection")
|
||||
await self.client.websocket.disconnect()
|
||||
await self._attempt_connect()
|
||||
|
||||
def on_connect():
|
||||
"""Define a handler to fire when the websocket is connected."""
|
||||
_LOGGER.info("Connected to websocket")
|
||||
_LOGGER.debug("Watchdog starting")
|
||||
if self._watchdog_listener is not None:
|
||||
self._watchdog_listener()
|
||||
self._watchdog_listener = async_call_later(
|
||||
self._hass, DEFAULT_WATCHDOG_SECONDS, _ws_reconnect
|
||||
)
|
||||
|
||||
def on_data(data):
|
||||
"""Define a handler to fire when the data is received."""
|
||||
@@ -385,12 +371,6 @@ class AmbientStation:
|
||||
self._hass, f"ambient_station_data_update_{mac_address}"
|
||||
)
|
||||
|
||||
_LOGGER.debug("Resetting watchdog")
|
||||
self._watchdog_listener()
|
||||
self._watchdog_listener = async_call_later(
|
||||
self._hass, DEFAULT_WATCHDOG_SECONDS, _ws_reconnect
|
||||
)
|
||||
|
||||
def on_disconnect():
|
||||
"""Define a handler to fire when the websocket is disconnected."""
|
||||
_LOGGER.info("Disconnected from websocket")
|
||||
@@ -520,13 +500,22 @@ class AmbientWeatherEntity(Entity):
|
||||
@callback
|
||||
def update():
|
||||
"""Update the state."""
|
||||
self.async_schedule_update_ha_state(True)
|
||||
self.update_from_latest_data()
|
||||
self.async_write_ha_state()
|
||||
|
||||
self._async_unsub_dispatcher_connect = async_dispatcher_connect(
|
||||
self.hass, f"ambient_station_data_update_{self._mac_address}", update
|
||||
)
|
||||
|
||||
self.update_from_latest_data()
|
||||
|
||||
async def async_will_remove_from_hass(self):
|
||||
"""Disconnect dispatcher listener when removed."""
|
||||
if self._async_unsub_dispatcher_connect:
|
||||
self._async_unsub_dispatcher_connect()
|
||||
self._async_unsub_dispatcher_connect = None
|
||||
|
||||
@callback
|
||||
def update_from_latest_data(self):
|
||||
"""Update the entity from the latest data."""
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -3,6 +3,7 @@ import logging
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorDevice
|
||||
from homeassistant.const import ATTR_NAME
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import (
|
||||
SENSOR_TYPES,
|
||||
@@ -76,7 +77,8 @@ class AmbientWeatherBinarySensor(AmbientWeatherEntity, BinarySensorDevice):
|
||||
|
||||
return self._state == 1
|
||||
|
||||
async def async_update(self):
|
||||
@callback
|
||||
def update_from_latest_data(self):
|
||||
"""Fetch new state data for the entity."""
|
||||
self._state = self._ambient.stations[self._mac_address][ATTR_LAST_DATA].get(
|
||||
self._sensor_type
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Ambient Weather Station",
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/ambient_station",
|
||||
"requirements": ["aioambient==1.0.4"],
|
||||
"requirements": ["aioambient==1.1.0"],
|
||||
"dependencies": [],
|
||||
"codeowners": ["@bachya"]
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import logging
|
||||
|
||||
from homeassistant.const import ATTR_NAME
|
||||
from homeassistant.core import callback
|
||||
|
||||
from . import (
|
||||
SENSOR_TYPES,
|
||||
@@ -74,7 +75,8 @@ class AmbientWeatherSensor(AmbientWeatherEntity):
|
||||
"""Return the unit of measurement."""
|
||||
return self._unit
|
||||
|
||||
async def async_update(self):
|
||||
@callback
|
||||
def update_from_latest_data(self):
|
||||
"""Fetch new state data for the sensor."""
|
||||
if self._sensor_type == TYPE_SOLARRADIATION_LX:
|
||||
# If the user requests the solarradiation_lx sensor, use the
|
||||
|
||||
@@ -42,6 +42,8 @@ from .const import (
|
||||
DATA_AMCREST,
|
||||
DEVICES,
|
||||
DOMAIN,
|
||||
SENSOR_EVENT_CODE,
|
||||
SERVICE_EVENT,
|
||||
SERVICE_UPDATE,
|
||||
)
|
||||
from .helpers import service_signal
|
||||
@@ -96,9 +98,11 @@ AMCREST_SCHEMA = vol.Schema(
|
||||
vol.Optional(CONF_FFMPEG_ARGUMENTS, default=DEFAULT_ARGUMENTS): cv.string,
|
||||
vol.Optional(CONF_SCAN_INTERVAL, default=SCAN_INTERVAL): cv.time_period,
|
||||
vol.Optional(CONF_BINARY_SENSORS): vol.All(
|
||||
cv.ensure_list, [vol.In(BINARY_SENSORS)]
|
||||
cv.ensure_list, [vol.In(BINARY_SENSORS)], vol.Unique()
|
||||
),
|
||||
vol.Optional(CONF_SENSORS): vol.All(
|
||||
cv.ensure_list, [vol.In(SENSORS)], vol.Unique()
|
||||
),
|
||||
vol.Optional(CONF_SENSORS): vol.All(cv.ensure_list, [vol.In(SENSORS)]),
|
||||
vol.Optional(CONF_CONTROL_LIGHT, default=True): cv.boolean,
|
||||
}
|
||||
)
|
||||
@@ -119,6 +123,8 @@ class AmcrestChecker(Http):
|
||||
self._wrap_errors = 0
|
||||
self._wrap_lock = threading.Lock()
|
||||
self._wrap_login_err = False
|
||||
self._wrap_event_flag = threading.Event()
|
||||
self._wrap_event_flag.set()
|
||||
self._unsub_recheck = None
|
||||
super().__init__(
|
||||
host,
|
||||
@@ -134,16 +140,22 @@ class AmcrestChecker(Http):
|
||||
"""Return if camera's API is responding."""
|
||||
return self._wrap_errors <= MAX_ERRORS and not self._wrap_login_err
|
||||
|
||||
@property
|
||||
def available_flag(self):
|
||||
"""Return threading event flag that indicates if camera's API is responding."""
|
||||
return self._wrap_event_flag
|
||||
|
||||
def _start_recovery(self):
|
||||
self._wrap_event_flag.clear()
|
||||
dispatcher_send(self._hass, service_signal(SERVICE_UPDATE, self._wrap_name))
|
||||
self._unsub_recheck = track_time_interval(
|
||||
self._hass, self._wrap_test_online, RECHECK_INTERVAL
|
||||
)
|
||||
|
||||
def command(self, cmd, retries=None, timeout_cmd=None, stream=False):
|
||||
def command(self, *args, **kwargs):
|
||||
"""amcrest.Http.command wrapper to catch errors."""
|
||||
try:
|
||||
ret = super().command(cmd, retries, timeout_cmd, stream)
|
||||
ret = super().command(*args, **kwargs)
|
||||
except LoginError as ex:
|
||||
with self._wrap_lock:
|
||||
was_online = self.available
|
||||
@@ -172,6 +184,7 @@ class AmcrestChecker(Http):
|
||||
self._unsub_recheck()
|
||||
self._unsub_recheck = None
|
||||
_LOGGER.error("%s camera back online", self._wrap_name)
|
||||
self._wrap_event_flag.set()
|
||||
dispatcher_send(self._hass, service_signal(SERVICE_UPDATE, self._wrap_name))
|
||||
return ret
|
||||
|
||||
@@ -184,6 +197,31 @@ class AmcrestChecker(Http):
|
||||
pass
|
||||
|
||||
|
||||
def _monitor_events(hass, name, api, event_codes):
|
||||
event_codes = ",".join(event_codes)
|
||||
while True:
|
||||
api.available_flag.wait()
|
||||
try:
|
||||
for code, start in api.event_actions(event_codes, retries=5):
|
||||
signal = service_signal(SERVICE_EVENT, name, code)
|
||||
_LOGGER.debug("Sending signal: '%s': %s", signal, start)
|
||||
dispatcher_send(hass, signal, start)
|
||||
except AmcrestError as error:
|
||||
_LOGGER.warning(
|
||||
"Error while processing events from %s camera: %r", name, error
|
||||
)
|
||||
|
||||
|
||||
def _start_event_monitor(hass, name, api, event_codes):
|
||||
thread = threading.Thread(
|
||||
target=_monitor_events,
|
||||
name=f"Amcrest {name}",
|
||||
args=(hass, name, api, event_codes),
|
||||
daemon=True,
|
||||
)
|
||||
thread.start()
|
||||
|
||||
|
||||
def setup(hass, config):
|
||||
"""Set up the Amcrest IP Camera component."""
|
||||
hass.data.setdefault(DATA_AMCREST, {DEVICES: {}, CAMERAS: []})
|
||||
@@ -230,6 +268,13 @@ def setup(hass, config):
|
||||
{CONF_NAME: name, CONF_BINARY_SENSORS: binary_sensors},
|
||||
config,
|
||||
)
|
||||
event_codes = [
|
||||
BINARY_SENSORS[sensor_type][SENSOR_EVENT_CODE]
|
||||
for sensor_type in binary_sensors
|
||||
if BINARY_SENSORS[sensor_type][SENSOR_EVENT_CODE] is not None
|
||||
]
|
||||
if event_codes:
|
||||
_start_event_monitor(hass, name, api, event_codes)
|
||||
|
||||
if sensors:
|
||||
discovery.load_platform(
|
||||
|
||||
@@ -10,12 +10,17 @@ from homeassistant.components.binary_sensor import (
|
||||
BinarySensorDevice,
|
||||
)
|
||||
from homeassistant.const import CONF_BINARY_SENSORS, CONF_NAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from .const import (
|
||||
BINARY_SENSOR_SCAN_INTERVAL_SECS,
|
||||
DATA_AMCREST,
|
||||
DEVICES,
|
||||
SENSOR_DEVICE_CLASS,
|
||||
SENSOR_EVENT_CODE,
|
||||
SENSOR_NAME,
|
||||
SERVICE_EVENT,
|
||||
SERVICE_UPDATE,
|
||||
)
|
||||
from .helpers import log_update_error, service_signal
|
||||
@@ -26,11 +31,20 @@ SCAN_INTERVAL = timedelta(seconds=BINARY_SENSOR_SCAN_INTERVAL_SECS)
|
||||
|
||||
BINARY_SENSOR_MOTION_DETECTED = "motion_detected"
|
||||
BINARY_SENSOR_ONLINE = "online"
|
||||
# Binary sensor types are defined like: Name, device class
|
||||
BINARY_SENSORS = {
|
||||
BINARY_SENSOR_MOTION_DETECTED: ("Motion Detected", DEVICE_CLASS_MOTION),
|
||||
BINARY_SENSOR_ONLINE: ("Online", DEVICE_CLASS_CONNECTIVITY),
|
||||
BINARY_SENSOR_MOTION_DETECTED: (
|
||||
"Motion Detected",
|
||||
DEVICE_CLASS_MOTION,
|
||||
"VideoMotion",
|
||||
),
|
||||
BINARY_SENSOR_ONLINE: ("Online", DEVICE_CLASS_CONNECTIVITY, None),
|
||||
}
|
||||
BINARY_SENSORS = {
|
||||
k: dict(zip((SENSOR_NAME, SENSOR_DEVICE_CLASS, SENSOR_EVENT_CODE), v))
|
||||
for k, v in BINARY_SENSORS.items()
|
||||
}
|
||||
|
||||
_UPDATE_MSG = "Updating %s binary sensor"
|
||||
|
||||
|
||||
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
|
||||
@@ -54,18 +68,19 @@ class AmcrestBinarySensor(BinarySensorDevice):
|
||||
|
||||
def __init__(self, name, device, sensor_type):
|
||||
"""Initialize entity."""
|
||||
self._name = f"{name} {BINARY_SENSORS[sensor_type][0]}"
|
||||
self._name = f"{name} {BINARY_SENSORS[sensor_type][SENSOR_NAME]}"
|
||||
self._signal_name = name
|
||||
self._api = device.api
|
||||
self._sensor_type = sensor_type
|
||||
self._state = None
|
||||
self._device_class = BINARY_SENSORS[sensor_type][1]
|
||||
self._unsub_dispatcher = None
|
||||
self._device_class = BINARY_SENSORS[sensor_type][SENSOR_DEVICE_CLASS]
|
||||
self._event_code = BINARY_SENSORS[sensor_type][SENSOR_EVENT_CODE]
|
||||
self._unsub_dispatcher = []
|
||||
|
||||
@property
|
||||
def should_poll(self):
|
||||
"""Return True if entity has to be polled for state."""
|
||||
return self._sensor_type != BINARY_SENSOR_ONLINE
|
||||
return self._sensor_type == BINARY_SENSOR_ONLINE
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
@@ -89,16 +104,34 @@ class AmcrestBinarySensor(BinarySensorDevice):
|
||||
|
||||
def update(self):
|
||||
"""Update entity."""
|
||||
if self._sensor_type == BINARY_SENSOR_ONLINE:
|
||||
self._update_online()
|
||||
else:
|
||||
self._update_others()
|
||||
|
||||
def _update_online(self):
|
||||
if not (self._api.available or self.is_on):
|
||||
return
|
||||
_LOGGER.debug(_UPDATE_MSG, self._name)
|
||||
if self._api.available:
|
||||
# Send a command to the camera to test if we can still communicate with it.
|
||||
# Override of Http.command() in __init__.py will set self._api.available
|
||||
# accordingly.
|
||||
try:
|
||||
self._api.current_time
|
||||
except AmcrestError:
|
||||
pass
|
||||
self._state = self._api.available
|
||||
|
||||
def _update_others(self):
|
||||
if not self.available:
|
||||
return
|
||||
_LOGGER.debug("Updating %s binary sensor", self._name)
|
||||
_LOGGER.debug(_UPDATE_MSG, self._name)
|
||||
|
||||
try:
|
||||
if self._sensor_type == BINARY_SENSOR_MOTION_DETECTED:
|
||||
self._state = self._api.is_motion_detected
|
||||
|
||||
elif self._sensor_type == BINARY_SENSOR_ONLINE:
|
||||
self._state = self._api.available
|
||||
self._state = "channels" in self._api.event_channels_happened(
|
||||
self._event_code
|
||||
)
|
||||
except AmcrestError as error:
|
||||
log_update_error(_LOGGER, "update", self.name, "binary sensor", error)
|
||||
|
||||
@@ -106,14 +139,32 @@ class AmcrestBinarySensor(BinarySensorDevice):
|
||||
"""Update state."""
|
||||
self.async_schedule_update_ha_state(True)
|
||||
|
||||
@callback
|
||||
def async_event_received(self, start):
|
||||
"""Update state from received event."""
|
||||
_LOGGER.debug(_UPDATE_MSG, self._name)
|
||||
self._state = start
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_added_to_hass(self):
|
||||
"""Subscribe to update signal."""
|
||||
self._unsub_dispatcher = async_dispatcher_connect(
|
||||
self.hass,
|
||||
service_signal(SERVICE_UPDATE, self._signal_name),
|
||||
self.async_on_demand_update,
|
||||
"""Subscribe to signals."""
|
||||
self._unsub_dispatcher.append(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
service_signal(SERVICE_UPDATE, self._signal_name),
|
||||
self.async_on_demand_update,
|
||||
)
|
||||
)
|
||||
if self._event_code:
|
||||
self._unsub_dispatcher.append(
|
||||
async_dispatcher_connect(
|
||||
self.hass,
|
||||
service_signal(SERVICE_EVENT, self._signal_name, self._event_code),
|
||||
self.async_event_received,
|
||||
)
|
||||
)
|
||||
|
||||
async def async_will_remove_from_hass(self):
|
||||
"""Disconnect from update signal."""
|
||||
self._unsub_dispatcher()
|
||||
for unsub_dispatcher in self._unsub_dispatcher:
|
||||
unsub_dispatcher()
|
||||
|
||||
@@ -21,6 +21,7 @@ from homeassistant.helpers.aiohttp_client import (
|
||||
async_aiohttp_proxy_web,
|
||||
async_get_clientsession,
|
||||
)
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
|
||||
from .const import (
|
||||
@@ -51,6 +52,28 @@ _SRV_CBW = "set_color_bw"
|
||||
_SRV_TOUR_ON = "start_tour"
|
||||
_SRV_TOUR_OFF = "stop_tour"
|
||||
|
||||
_SRV_PTZ_CTRL = "ptz_control"
|
||||
_ATTR_PTZ_TT = "travel_time"
|
||||
_ATTR_PTZ_MOV = "movement"
|
||||
_MOV = [
|
||||
"zoom_out",
|
||||
"zoom_in",
|
||||
"right",
|
||||
"left",
|
||||
"up",
|
||||
"down",
|
||||
"right_down",
|
||||
"right_up",
|
||||
"left_down",
|
||||
"left_up",
|
||||
]
|
||||
_ZOOM_ACTIONS = ["ZoomWide", "ZoomTele"]
|
||||
_MOVE_1_ACTIONS = ["Right", "Left", "Up", "Down"]
|
||||
_MOVE_2_ACTIONS = ["RightDown", "RightUp", "LeftDown", "LeftUp"]
|
||||
_ACTION = _ZOOM_ACTIONS + _MOVE_1_ACTIONS + _MOVE_2_ACTIONS
|
||||
|
||||
_DEFAULT_TT = 0.2
|
||||
|
||||
_ATTR_PRESET = "preset"
|
||||
_ATTR_COLOR_BW = "color_bw"
|
||||
|
||||
@@ -65,6 +88,12 @@ _SRV_GOTO_SCHEMA = CAMERA_SERVICE_SCHEMA.extend(
|
||||
_SRV_CBW_SCHEMA = CAMERA_SERVICE_SCHEMA.extend(
|
||||
{vol.Required(_ATTR_COLOR_BW): vol.In(_CBW)}
|
||||
)
|
||||
_SRV_PTZ_SCHEMA = CAMERA_SERVICE_SCHEMA.extend(
|
||||
{
|
||||
vol.Required(_ATTR_PTZ_MOV): vol.In(_MOV),
|
||||
vol.Optional(_ATTR_PTZ_TT, default=_DEFAULT_TT): cv.small_float,
|
||||
}
|
||||
)
|
||||
|
||||
CAMERA_SERVICES = {
|
||||
_SRV_EN_REC: (CAMERA_SERVICE_SCHEMA, "async_enable_recording", ()),
|
||||
@@ -77,6 +106,11 @@ CAMERA_SERVICES = {
|
||||
_SRV_CBW: (_SRV_CBW_SCHEMA, "async_set_color_bw", (_ATTR_COLOR_BW,)),
|
||||
_SRV_TOUR_ON: (CAMERA_SERVICE_SCHEMA, "async_start_tour", ()),
|
||||
_SRV_TOUR_OFF: (CAMERA_SERVICE_SCHEMA, "async_stop_tour", ()),
|
||||
_SRV_PTZ_CTRL: (
|
||||
_SRV_PTZ_SCHEMA,
|
||||
"async_ptz_control",
|
||||
(_ATTR_PTZ_MOV, _ATTR_PTZ_TT),
|
||||
),
|
||||
}
|
||||
|
||||
_BOOL_TO_STATE = {True: STATE_ON, False: STATE_OFF}
|
||||
@@ -406,6 +440,29 @@ class AmcrestCam(Camera):
|
||||
"""Call the job and stop camera tour."""
|
||||
await self.hass.async_add_executor_job(self._start_tour, False)
|
||||
|
||||
async def async_ptz_control(self, movement, travel_time):
|
||||
"""Move or zoom camera in specified direction."""
|
||||
code = _ACTION[_MOV.index(movement)]
|
||||
|
||||
kwargs = {"code": code, "arg1": 0, "arg2": 0, "arg3": 0}
|
||||
if code in _MOVE_1_ACTIONS:
|
||||
kwargs["arg2"] = 1
|
||||
elif code in _MOVE_2_ACTIONS:
|
||||
kwargs["arg1"] = kwargs["arg2"] = 1
|
||||
|
||||
try:
|
||||
await self.hass.async_add_executor_job(
|
||||
partial(self._api.ptz_control_command, action="start", **kwargs)
|
||||
)
|
||||
await asyncio.sleep(travel_time)
|
||||
await self.hass.async_add_executor_job(
|
||||
partial(self._api.ptz_control_command, action="stop", **kwargs)
|
||||
)
|
||||
except AmcrestError as error:
|
||||
log_update_error(
|
||||
_LOGGER, "move", self.name, f"camera PTZ {movement}", error
|
||||
)
|
||||
|
||||
# Methods to send commands to Amcrest camera and handle errors
|
||||
|
||||
def _enable_video_stream(self, enable):
|
||||
|
||||
@@ -4,11 +4,16 @@ DATA_AMCREST = DOMAIN
|
||||
CAMERAS = "cameras"
|
||||
DEVICES = "devices"
|
||||
|
||||
BINARY_SENSOR_SCAN_INTERVAL_SECS = 5
|
||||
BINARY_SENSOR_SCAN_INTERVAL_SECS = 60
|
||||
CAMERA_WEB_SESSION_TIMEOUT = 10
|
||||
COMM_RETRIES = 1
|
||||
COMM_TIMEOUT = 6.05
|
||||
SENSOR_SCAN_INTERVAL_SECS = 10
|
||||
SNAPSHOT_TIMEOUT = 20
|
||||
|
||||
SERVICE_EVENT = "event"
|
||||
SERVICE_UPDATE = "update"
|
||||
|
||||
SENSOR_DEVICE_CLASS = "class"
|
||||
SENSOR_EVENT_CODE = "code"
|
||||
SENSOR_NAME = "name"
|
||||
|
||||
@@ -2,12 +2,9 @@
|
||||
from .const import DOMAIN
|
||||
|
||||
|
||||
def service_signal(service, ident=None):
|
||||
"""Encode service and identifier into signal."""
|
||||
signal = f"{DOMAIN}_{service}"
|
||||
if ident:
|
||||
signal += f"_{ident.replace('.', '_')}"
|
||||
return signal
|
||||
def service_signal(service, *args):
|
||||
"""Encode signal."""
|
||||
return "_".join([DOMAIN, service, *args])
|
||||
|
||||
|
||||
def log_update_error(logger, action, name, entity_type, error):
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"domain": "amcrest",
|
||||
"name": "Amcrest",
|
||||
"documentation": "https://www.home-assistant.io/integrations/amcrest",
|
||||
"requirements": ["amcrest==1.5.6"],
|
||||
"requirements": ["amcrest==1.7.0"],
|
||||
"dependencies": ["ffmpeg"],
|
||||
"codeowners": ["@pnbruckner"]
|
||||
}
|
||||
|
||||
@@ -73,3 +73,16 @@ stop_tour:
|
||||
entity_id:
|
||||
description: "Name(s) of the cameras, or 'all' for all cameras."
|
||||
example: 'camera.house_front'
|
||||
|
||||
ptz_control:
|
||||
description: Move (Pan/Tilt) and/or Zoom a PTZ camera
|
||||
fields:
|
||||
entity_id:
|
||||
description: "Name of the camera, or 'all' for all cameras."
|
||||
example: 'camera.house_front'
|
||||
movement:
|
||||
description: "up, down, right, left, right_up, right_down, left_up, left_down, zoom_in, zoom_out"
|
||||
example: 'right'
|
||||
travel_time:
|
||||
description: "(optional) Travel time in fractional seconds: from 0 to 1. Default: .2"
|
||||
example: '.5'
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user