mirror of
https://github.com/home-assistant/core.git
synced 2025-10-29 21:49:33 +00:00
Compare commits
1239 Commits
2025.10.3
...
matter-err
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08eee8d479 | ||
|
|
6bbaae7235 | ||
|
|
86a5dff3f5 | ||
|
|
34e137005d | ||
|
|
67baa2c737 | ||
|
|
f9c504fcde | ||
|
|
4b25d04326 | ||
|
|
4e3eb44e69 | ||
|
|
6c84d25024 | ||
|
|
c4dc4135e1 | ||
|
|
78bbdf108b | ||
|
|
47397fd736 | ||
|
|
2b62d2d636 | ||
|
|
350f99baab | ||
|
|
1245385371 | ||
|
|
c86852eb21 | ||
|
|
ad635d2eff | ||
|
|
cf0e2b85dd | ||
|
|
b9e7f1c628 | ||
|
|
079d65acea | ||
|
|
162737a473 | ||
|
|
d074c5b7c8 | ||
|
|
d6ae0c142e | ||
|
|
58182a344d | ||
|
|
1a1f3d6b4e | ||
|
|
71589d212f | ||
|
|
9364a40fd2 | ||
|
|
7ead8f9154 | ||
|
|
09ac47b35f | ||
|
|
404393d6fe | ||
|
|
de5a26830d | ||
|
|
c0b0ce0c16 | ||
|
|
88e27d9017 | ||
|
|
a37ba6dba4 | ||
|
|
f38c0d510e | ||
|
|
be9fa9a606 | ||
|
|
2bc6e728a3 | ||
|
|
4e4a0d1e28 | ||
|
|
c860aa1531 | ||
|
|
39c73cbbbd | ||
|
|
ca7332f597 | ||
|
|
eafedeb12a | ||
|
|
ce93de7fc6 | ||
|
|
03f339b3a3 | ||
|
|
6cfeeea1ba | ||
|
|
ccbdaabe74 | ||
|
|
dd22c78d11 | ||
|
|
1a732accdd | ||
|
|
59d663ee6e | ||
|
|
2442a96c27 | ||
|
|
f69de99501 | ||
|
|
8ce9238f7a | ||
|
|
2fe697486d | ||
|
|
0bd25dc254 | ||
|
|
ed6af953af | ||
|
|
84f65c3f77 | ||
|
|
77c024fcdd | ||
|
|
27570138e7 | ||
|
|
96f84b2b99 | ||
|
|
28bee6d1aa | ||
|
|
c9d68ddd5c | ||
|
|
ac6dddc895 | ||
|
|
f7e5dc7637 | ||
|
|
1a5f431485 | ||
|
|
faa04755e2 | ||
|
|
76584161a6 | ||
|
|
103b121868 | ||
|
|
76a6b3cea6 | ||
|
|
87e7fe6e37 | ||
|
|
c782489973 | ||
|
|
c9dbc1458c | ||
|
|
d4a33611f0 | ||
|
|
f028b7ab6e | ||
|
|
86a9df761a | ||
|
|
e3359fb62d | ||
|
|
84065767d1 | ||
|
|
e7c60f573d | ||
|
|
a59847309a | ||
|
|
3c8ac20881 | ||
|
|
6394fdbc97 | ||
|
|
a7a673d437 | ||
|
|
196d7bb8cf | ||
|
|
ce5538e615 | ||
|
|
6819b0ce5b | ||
|
|
91eb35d7cb | ||
|
|
256615644c | ||
|
|
aff6cfb032 | ||
|
|
a5d8c383c4 | ||
|
|
81df40e2b0 | ||
|
|
3e6ddda54e | ||
|
|
76c6b92910 | ||
|
|
f7db56a6b3 | ||
|
|
507645a60f | ||
|
|
7c4406265a | ||
|
|
4e6bd48416 | ||
|
|
255b1efe81 | ||
|
|
afa5093d7a | ||
|
|
a43368675e | ||
|
|
39d76a24db | ||
|
|
3e17a97422 | ||
|
|
e6e78f86bd | ||
|
|
a72fe28d7a | ||
|
|
fa7ff1d996 | ||
|
|
2cc910f4b1 | ||
|
|
b686e4d8db | ||
|
|
07493e5b3e | ||
|
|
477073da75 | ||
|
|
b59bc45ef1 | ||
|
|
da055795c7 | ||
|
|
3cd17a2b9f | ||
|
|
d689400b3b | ||
|
|
a1f024eed8 | ||
|
|
07b6358fff | ||
|
|
6fa73f7f6a | ||
|
|
3c46b40cee | ||
|
|
4c9810a10e | ||
|
|
83e9fca6a2 | ||
|
|
fc9313f7ef | ||
|
|
278f32285a | ||
|
|
8c360908ef | ||
|
|
82c5337fcf | ||
|
|
7950f9ab38 | ||
|
|
66eeb41e56 | ||
|
|
1bef707cd1 | ||
|
|
2125a4123d | ||
|
|
27516dee6a | ||
|
|
40c9e5356e | ||
|
|
2521920376 | ||
|
|
16eb8315ee | ||
|
|
2c3d65b461 | ||
|
|
7e938f4f13 | ||
|
|
c5c4cf0284 | ||
|
|
68c38ac047 | ||
|
|
f5a6fa8be1 | ||
|
|
3c751918fd | ||
|
|
a3c8760b3f | ||
|
|
7bceaf74be | ||
|
|
750f06327a | ||
|
|
98bffdb9d3 | ||
|
|
174b0f7c01 | ||
|
|
807edc9f47 | ||
|
|
9b0f67fbde | ||
|
|
27390647ff | ||
|
|
d9f6a6bf99 | ||
|
|
67be4f863f | ||
|
|
c960bd2845 | ||
|
|
447fb68085 | ||
|
|
750a7c9797 | ||
|
|
5679ab0f86 | ||
|
|
2761dcbc48 | ||
|
|
3a00d96571 | ||
|
|
c86ad896b8 | ||
|
|
9c1d8747be | ||
|
|
4a003114bd | ||
|
|
dcc3f14b1f | ||
|
|
7687d5ea48 | ||
|
|
27cc3c838a | ||
|
|
619cb91839 | ||
|
|
5e5e130d4e | ||
|
|
f6ac23cc58 | ||
|
|
244b6437b2 | ||
|
|
5dc271b201 | ||
|
|
531cc3e1ce | ||
|
|
ed7c3cb339 | ||
|
|
3b7e1a2610 | ||
|
|
16e11ed801 | ||
|
|
32a7bf4dbb | ||
|
|
856c99dc22 | ||
|
|
a50b00b3c2 | ||
|
|
1df8b1063b | ||
|
|
32cd4364f6 | ||
|
|
0828a842a5 | ||
|
|
f63a527a01 | ||
|
|
254a9ecc25 | ||
|
|
a518907b09 | ||
|
|
cd85699151 | ||
|
|
f49dfbd459 | ||
|
|
3ed70bb751 | ||
|
|
b4b1065737 | ||
|
|
7267c3c04e | ||
|
|
6ac4d2dd59 | ||
|
|
03abd5d277 | ||
|
|
66bb0db08b | ||
|
|
56ae579e83 | ||
|
|
add1915b8a | ||
|
|
18ef4af8d0 | ||
|
|
3c6788212f | ||
|
|
dbd8b1bc19 | ||
|
|
d135f1c110 | ||
|
|
c11cacbb58 | ||
|
|
d8d6490fb4 | ||
|
|
2341f53ac0 | ||
|
|
94e8ffadd2 | ||
|
|
5186c402e7 | ||
|
|
4403447d53 | ||
|
|
9a8a2bfebc | ||
|
|
a907a34f6e | ||
|
|
03b15f1dba | ||
|
|
a48b915d90 | ||
|
|
e8227baa50 | ||
|
|
4d525dee48 | ||
|
|
c38e02072e | ||
|
|
2d84bd65fe | ||
|
|
1d8eaeb8af | ||
|
|
33ed851477 | ||
|
|
a571271f6a | ||
|
|
43445f5945 | ||
|
|
3c098db35e | ||
|
|
c72072fbfb | ||
|
|
10e2c7ec95 | ||
|
|
64493ca578 | ||
|
|
5f008dcae5 | ||
|
|
80c06c689c | ||
|
|
b19070fa4b | ||
|
|
53984f7abc | ||
|
|
38620a0cda | ||
|
|
b09927bb53 | ||
|
|
11b9f7915b | ||
|
|
3ddb520693 | ||
|
|
ffc54c699d | ||
|
|
91b516d739 | ||
|
|
25a9948020 | ||
|
|
f0f420ebe2 | ||
|
|
312812dd8b | ||
|
|
e0d404456b | ||
|
|
439fc18860 | ||
|
|
774ab06206 | ||
|
|
f484db8f0e | ||
|
|
4af3c4f720 | ||
|
|
a020a32d8a | ||
|
|
1ac2ae3443 | ||
|
|
2fce7db132 | ||
|
|
6e49911e1c | ||
|
|
4215a16285 | ||
|
|
65ff4fe10e | ||
|
|
5b7675e389 | ||
|
|
3019744035 | ||
|
|
21ab630380 | ||
|
|
564ff12db0 | ||
|
|
6c919e698f | ||
|
|
5d644815fa | ||
|
|
8dfa0f2f65 | ||
|
|
f9484acbfa | ||
|
|
d0c0247086 | ||
|
|
b116619af1 | ||
|
|
a3d760156f | ||
|
|
6e194ad6ef | ||
|
|
1e2a21b69f | ||
|
|
e90fe96b4e | ||
|
|
4774ed508a | ||
|
|
8f4a4d4c47 | ||
|
|
a83bbe2332 | ||
|
|
e5b93d3275 | ||
|
|
1c024f58af | ||
|
|
fa86148df0 | ||
|
|
7c6bbb97ea | ||
|
|
a5af501da4 | ||
|
|
f23cfb5594 | ||
|
|
67a12dc007 | ||
|
|
5783b3a576 | ||
|
|
7bc43039bd | ||
|
|
23e2316c36 | ||
|
|
9e9c8f5724 | ||
|
|
11772dbc46 | ||
|
|
c12df5d776 | ||
|
|
b57ca143e6 | ||
|
|
b3e16bd4fa | ||
|
|
18d5035877 | ||
|
|
d6db50fcc7 | ||
|
|
84d9fa3bd7 | ||
|
|
b08eb3a201 | ||
|
|
c74c317922 | ||
|
|
9edc6249ca | ||
|
|
4fbcb79889 | ||
|
|
68fd5bc67e | ||
|
|
882d047bb5 | ||
|
|
5c070c8f03 | ||
|
|
854882d612 | ||
|
|
b078c0ee7e | ||
|
|
080b16a33d | ||
|
|
6a1cf9827c | ||
|
|
23e7b14eae | ||
|
|
2a5cf83f50 | ||
|
|
5dcb68cdf6 | ||
|
|
fedeca107a | ||
|
|
4fef19c7bc | ||
|
|
8c953b0c4e | ||
|
|
949544874f | ||
|
|
237407010a | ||
|
|
64e48816c7 | ||
|
|
6b76b3e729 | ||
|
|
4912280193 | ||
|
|
d4e72ad2cf | ||
|
|
711526fc6c | ||
|
|
4be428fce7 | ||
|
|
ea226806a0 | ||
|
|
bc77daf2ce | ||
|
|
acead56bd5 | ||
|
|
fd08c55b79 | ||
|
|
0c342c4750 | ||
|
|
da6986e58c | ||
|
|
2f5fbc1f0e | ||
|
|
e79c76cd35 | ||
|
|
6edafd8965 | ||
|
|
204ff5d45f | ||
|
|
591eb94515 | ||
|
|
0f3de627c5 | ||
|
|
b2699d8a03 | ||
|
|
769a770cf1 | ||
|
|
2d96e8ac4d | ||
|
|
354cacdcae | ||
|
|
d999dd05d1 | ||
|
|
81572c6a84 | ||
|
|
8165ac196f | ||
|
|
41c95247ec | ||
|
|
2eb3360e8c | ||
|
|
fcd07902b0 | ||
|
|
71f94cad97 | ||
|
|
05277aa708 | ||
|
|
9f74471d22 | ||
|
|
1c8487a7e7 | ||
|
|
3c8612b6fd | ||
|
|
f28892c526 | ||
|
|
24b7cf261c | ||
|
|
ef69e6d54b | ||
|
|
ca31a279fa | ||
|
|
e50c4c4787 | ||
|
|
3ecddda8dd | ||
|
|
af77f835a5 | ||
|
|
6de2016aa3 | ||
|
|
f1e72c1616 | ||
|
|
7af3eb638b | ||
|
|
363e5f088c | ||
|
|
5b1e3ef574 | ||
|
|
d607323731 | ||
|
|
31f595a3f8 | ||
|
|
9a27805349 | ||
|
|
477cdbb711 | ||
|
|
62b39fdd10 | ||
|
|
f806cc8b4b | ||
|
|
b6108001e4 | ||
|
|
56f33a8a5f | ||
|
|
1e91ad6e23 | ||
|
|
9032de4b26 | ||
|
|
553fcb5156 | ||
|
|
378295e1cc | ||
|
|
ff95c6235f | ||
|
|
d398a13899 | ||
|
|
10b300e573 | ||
|
|
e95c0ef3a8 | ||
|
|
3b09adb360 | ||
|
|
d2380608e1 | ||
|
|
37188a0832 | ||
|
|
3134fd75e8 | ||
|
|
861f4a0578 | ||
|
|
a82c512472 | ||
|
|
10392d9719 | ||
|
|
b7acc66153 | ||
|
|
6249cabcba | ||
|
|
84f2fd106d | ||
|
|
45cc68d3e4 | ||
|
|
7fd75c7742 | ||
|
|
9522b11042 | ||
|
|
c874c4ac73 | ||
|
|
907ef8fa15 | ||
|
|
bc93153c40 | ||
|
|
6964829699 | ||
|
|
62e59608b0 | ||
|
|
9507b3f3aa | ||
|
|
1d187abe10 | ||
|
|
0464cb8929 | ||
|
|
f410d94f80 | ||
|
|
dee3c11203 | ||
|
|
06e4b0a798 | ||
|
|
2fd55a49cb | ||
|
|
80d7224dcf | ||
|
|
9d03b1b9b4 | ||
|
|
cecdf553f3 | ||
|
|
54e6fbc042 | ||
|
|
9c098d3471 | ||
|
|
394575e4f7 | ||
|
|
effc33d0d2 | ||
|
|
7af4c337c6 | ||
|
|
4f222d7adf | ||
|
|
00f16812e4 | ||
|
|
0efaf7efe8 | ||
|
|
55643f0632 | ||
|
|
36f4723f6e | ||
|
|
03bc698936 | ||
|
|
0c1dc73422 | ||
|
|
c31537081b | ||
|
|
d13067abb3 | ||
|
|
64da32b5f9 | ||
|
|
3990fc6ab2 | ||
|
|
e4071bd305 | ||
|
|
8dda26c227 | ||
|
|
b182d5ce87 | ||
|
|
175365bdea | ||
|
|
cbe52cbfca | ||
|
|
9251dde2c6 | ||
|
|
24d77cc453 | ||
|
|
a1f98abe49 | ||
|
|
d25dde1d11 | ||
|
|
8ec483b38b | ||
|
|
bf14caca69 | ||
|
|
e5fb6b2fb2 | ||
|
|
7dfeb3a3f6 | ||
|
|
9d3b1562c4 | ||
|
|
e14407f066 | ||
|
|
67872e3746 | ||
|
|
06bd1a2003 | ||
|
|
37ea360304 | ||
|
|
25ce57424c | ||
|
|
3d46ab549d | ||
|
|
567cc9f842 | ||
|
|
b5457a5abd | ||
|
|
e4b5e35d1d | ||
|
|
12023c33b5 | ||
|
|
a28749937c | ||
|
|
3fe37d651f | ||
|
|
cb3424cdf0 | ||
|
|
a799f7ff91 | ||
|
|
34ab725b75 | ||
|
|
2dfc7f02ba | ||
|
|
c8919222bd | ||
|
|
a888264d2f | ||
|
|
ae84c7e15d | ||
|
|
415c8b490b | ||
|
|
6038f15406 | ||
|
|
a8758253c4 | ||
|
|
fa4eb2e820 | ||
|
|
58f35d0614 | ||
|
|
f72a91ca29 | ||
|
|
5d99da6e1f | ||
|
|
64746eb99c | ||
|
|
70fc6df599 | ||
|
|
8dc33ece7b | ||
|
|
3d4d8e7f20 | ||
|
|
c92d319e12 | ||
|
|
1bdba7906a | ||
|
|
aa8198d852 | ||
|
|
b7f30ec17f | ||
|
|
2da1878f60 | ||
|
|
872b33a088 | ||
|
|
e0faa36157 | ||
|
|
14b270a2db | ||
|
|
8402bead4f | ||
|
|
6bf7a4278e | ||
|
|
3de62b2b4c | ||
|
|
0d2558c030 | ||
|
|
9efbcb2f82 | ||
|
|
f210bb35ed | ||
|
|
0581ceb771 | ||
|
|
7ba2e60af3 | ||
|
|
75fa0ffd04 | ||
|
|
01effb7ca6 | ||
|
|
88d383962c | ||
|
|
3c001bd6ed | ||
|
|
ec5c4843d1 | ||
|
|
e2c281549e | ||
|
|
051e472537 | ||
|
|
1e5910215d | ||
|
|
645089edba | ||
|
|
7abe289681 | ||
|
|
7829c2d03e | ||
|
|
148a13361f | ||
|
|
57dccd1474 | ||
|
|
a3b0132299 | ||
|
|
fbd8443745 | ||
|
|
cd7015c6b7 | ||
|
|
1012c7bdf9 | ||
|
|
ca912906f5 | ||
|
|
d0cad43a6c | ||
|
|
751540e606 | ||
|
|
3d2ec712f1 | ||
|
|
e3a6c06997 | ||
|
|
08b94e29e6 | ||
|
|
79323189fb | ||
|
|
7508828518 | ||
|
|
f257e89b2a | ||
|
|
a2e469eb28 | ||
|
|
7c80491325 | ||
|
|
adedf2037a | ||
|
|
188459e3ff | ||
|
|
7324a12ada | ||
|
|
fe07e9c840 | ||
|
|
afeaf2409f | ||
|
|
69f9c0a6cc | ||
|
|
46f52db87c | ||
|
|
d877761dbb | ||
|
|
95da65f552 | ||
|
|
6ec82d0b21 | ||
|
|
f6a16f63a4 | ||
|
|
9ff2dab468 | ||
|
|
9422703288 | ||
|
|
d91eccb209 | ||
|
|
939cbc8644 | ||
|
|
0f1d2a77cb | ||
|
|
385fc5b3d0 | ||
|
|
18c63e3b8f | ||
|
|
cf477186aa | ||
|
|
0eef44be91 | ||
|
|
e7ac56c59f | ||
|
|
3cc4091f31 | ||
|
|
00025c8f42 | ||
|
|
db48f8cb28 | ||
|
|
4fdbe82df2 | ||
|
|
742f1b2157 | ||
|
|
681eb6b594 | ||
|
|
1d6c6628f4 | ||
|
|
b6337c07d6 | ||
|
|
8b6fb05ee4 | ||
|
|
28405e2b04 | ||
|
|
31857a03d6 | ||
|
|
97a0a4ea17 | ||
|
|
b494074ee0 | ||
|
|
6aff1287dd | ||
|
|
655de3dfd2 | ||
|
|
11ee7d63be | ||
|
|
080a7dcfa7 | ||
|
|
3e20c506f4 | ||
|
|
2abc197dcd | ||
|
|
a3dec46d59 | ||
|
|
7a3630e647 | ||
|
|
2812d7c712 | ||
|
|
c0fc7b66f0 | ||
|
|
c6e334ca60 | ||
|
|
416f6b922c | ||
|
|
d2af875d63 | ||
|
|
1237010b4a | ||
|
|
26fec2fdcc | ||
|
|
13e828038d | ||
|
|
b517774be0 | ||
|
|
6e515d4829 | ||
|
|
7f5128eb15 | ||
|
|
7ddfcd350b | ||
|
|
a92e73ff17 | ||
|
|
ae3d32073c | ||
|
|
38d0299951 | ||
|
|
8dba1edbe5 | ||
|
|
f3c4288026 | ||
|
|
8db6505a97 | ||
|
|
61a9094d5f | ||
|
|
d140eb4c76 | ||
|
|
21f24c2f6a | ||
|
|
85b26479de | ||
|
|
bddbf9c73c | ||
|
|
64f48564ff | ||
|
|
06e4922021 | ||
|
|
cdc6c44a49 | ||
|
|
106a74c954 | ||
|
|
8464dad8e0 | ||
|
|
c3e2f0e19b | ||
|
|
fbf875b5af | ||
|
|
fcea5e0da6 | ||
|
|
81fd9e1c5a | ||
|
|
d108d5f106 | ||
|
|
487940872e | ||
|
|
aaf58075c6 | ||
|
|
a23bed6f4d | ||
|
|
02e05643f1 | ||
|
|
5f9b098c19 | ||
|
|
143f7df7fd | ||
|
|
9a28ee5378 | ||
|
|
82f33fbc39 | ||
|
|
6a632a71b6 | ||
|
|
ae8678b2af | ||
|
|
b52ee6915a | ||
|
|
b0e1b00598 | ||
|
|
fd902af23b | ||
|
|
07d6ebef4c | ||
|
|
c9b9f05f4b | ||
|
|
90a0262217 | ||
|
|
324aa09ebe | ||
|
|
663431fc80 | ||
|
|
610183c11b | ||
|
|
b7718f6f0f | ||
|
|
5708f61964 | ||
|
|
4fb3c9fed2 | ||
|
|
1e5f5f4ad3 | ||
|
|
82c536a4e9 | ||
|
|
97afec1912 | ||
|
|
0bfdd70730 | ||
|
|
01dee6507b | ||
|
|
04f83bc067 | ||
|
|
f0756af52d | ||
|
|
dd6bc715d8 | ||
|
|
1452aec47f | ||
|
|
6f8439de5b | ||
|
|
f649717372 | ||
|
|
bf273ef407 | ||
|
|
94d015e00a | ||
|
|
f185ffddf1 | ||
|
|
2d0b4dd7e9 | ||
|
|
eab1205823 | ||
|
|
a991dcbe6a | ||
|
|
6f79a65762 | ||
|
|
ce1fdc6b75 | ||
|
|
d7aa0834c7 | ||
|
|
3151384867 | ||
|
|
8aa5e7de91 | ||
|
|
cca5c807ad | ||
|
|
89433219dd | ||
|
|
694b169c79 | ||
|
|
f1e0954c61 | ||
|
|
3c3b4ef14a | ||
|
|
54ff49115c | ||
|
|
2512dad843 | ||
|
|
a3b67d5f28 | ||
|
|
76a0b2d616 | ||
|
|
1182082c1f | ||
|
|
e0811558cb | ||
|
|
d389405218 | ||
|
|
3a71087c9c | ||
|
|
c7d7cfa7ad | ||
|
|
e4ea79866d | ||
|
|
ddfa6f33d2 | ||
|
|
15e99650aa | ||
|
|
58bacbb84e | ||
|
|
82758f7671 | ||
|
|
7739cdc626 | ||
|
|
4ca1ae61aa | ||
|
|
3d130a9bdf | ||
|
|
2b38f33d50 | ||
|
|
19dedb038e | ||
|
|
59781422f7 | ||
|
|
083277d1ff | ||
|
|
9b9c55b37b | ||
|
|
c9d67d596b | ||
|
|
7948b35265 | ||
|
|
be843970fd | ||
|
|
53b65b2fb4 | ||
|
|
ac7be97245 | ||
|
|
09e539bf0e | ||
|
|
6ef1b3bad3 | ||
|
|
38e46f7a53 | ||
|
|
ef60d16659 | ||
|
|
bf4f8b48a3 | ||
|
|
3c1496d2bb | ||
|
|
d457787639 | ||
|
|
de4bfd6f05 | ||
|
|
34c5748132 | ||
|
|
5bfd9620db | ||
|
|
6f8766e4bd | ||
|
|
d3b519846b | ||
|
|
36d952800b | ||
|
|
b832561e53 | ||
|
|
c59d295bf2 | ||
|
|
6e28e3aed1 | ||
|
|
6d8944d379 | ||
|
|
762fd6d241 | ||
|
|
4c6500e7a4 | ||
|
|
cdc224715f | ||
|
|
648b250fc8 | ||
|
|
ba61562300 | ||
|
|
8d67182e0e | ||
|
|
3ce1ef4c3f | ||
|
|
bde4eb5011 | ||
|
|
a58a7065b6 | ||
|
|
0c9b72bf1d | ||
|
|
541d94d8c6 | ||
|
|
c370c86a4f | ||
|
|
bc6accf4ae | ||
|
|
d40eeee422 | ||
|
|
c9d9730c4a | ||
|
|
d3a8f3191b | ||
|
|
cb3829ddee | ||
|
|
73383e6c26 | ||
|
|
217894ee8b | ||
|
|
c7321a337e | ||
|
|
517124dfbe | ||
|
|
f49299b009 | ||
|
|
1001da08f6 | ||
|
|
0da019404c | ||
|
|
9a4280d0de | ||
|
|
c28e105df5 | ||
|
|
68787248f6 | ||
|
|
36be6b6187 | ||
|
|
42dea92c51 | ||
|
|
4b828d4753 | ||
|
|
8e79c38f34 | ||
|
|
c92107b8d4 | ||
|
|
b25622f40e | ||
|
|
e887d5e6ad | ||
|
|
1f19e40cfe | ||
|
|
3d2d2271d3 | ||
|
|
d1dd5eecd6 | ||
|
|
cdec29ffb7 | ||
|
|
07f3e00f18 | ||
|
|
084d029168 | ||
|
|
17e997ee18 | ||
|
|
16d4c6c95a | ||
|
|
0205a636ef | ||
|
|
4707fd2f94 | ||
|
|
ad3cadab83 | ||
|
|
3fce815415 | ||
|
|
ee67619cb1 | ||
|
|
1a744a2c91 | ||
|
|
951978e483 | ||
|
|
54d30377d3 | ||
|
|
eb04dda197 | ||
|
|
1e192aadfa | ||
|
|
6f680f3d03 | ||
|
|
f0663dc275 | ||
|
|
96bb67bef9 | ||
|
|
929d76e236 | ||
|
|
fe1ff083de | ||
|
|
90c68f8ad0 | ||
|
|
6b79aa7738 | ||
|
|
f6fb4c8d5a | ||
|
|
a6e575ecfa | ||
|
|
85392ae167 | ||
|
|
9d124be491 | ||
|
|
8bca3931ab | ||
|
|
0367a01287 | ||
|
|
86e2c2f361 | ||
|
|
335c8e50a2 | ||
|
|
8152a9e5da | ||
|
|
250e562caf | ||
|
|
a3b641e53d | ||
|
|
135ea4c02e | ||
|
|
bc980c1212 | ||
|
|
59ca88a7e8 | ||
|
|
d45114cd11 | ||
|
|
2eba650064 | ||
|
|
de4adb8855 | ||
|
|
1d86c03b02 | ||
|
|
77fb1036cc | ||
|
|
b15b4e4888 | ||
|
|
dddf6d5f1a | ||
|
|
66fb5f4d95 | ||
|
|
42a9d5d4e3 | ||
|
|
93fa162913 | ||
|
|
c432b1c8da | ||
|
|
00955b8e6a | ||
|
|
045b9d7f01 | ||
|
|
438c4c7871 | ||
|
|
abc360460c | ||
|
|
26437bb253 | ||
|
|
56d953ac1e | ||
|
|
fe4eb8766d | ||
|
|
2d9f14c401 | ||
|
|
7b6ccb07fd | ||
|
|
2ba5728060 | ||
|
|
b5f163cc85 | ||
|
|
65540a3e0b | ||
|
|
cbf1b39edb | ||
|
|
142daf5e49 | ||
|
|
8bd0ff7cca | ||
|
|
ac676e12f6 | ||
|
|
c0ac3292cd | ||
|
|
80fd07c128 | ||
|
|
3701d8859a | ||
|
|
6dd26bae88 | ||
|
|
1a0abe296c | ||
|
|
de6c61a4ab | ||
|
|
33c677596e | ||
|
|
e9b4b8e99b | ||
|
|
0525c04c42 | ||
|
|
d57b502551 | ||
|
|
9fb708baf4 | ||
|
|
abdf24b7a0 | ||
|
|
29bfbd27bb | ||
|
|
224553f8d9 | ||
|
|
7c9f6a061f | ||
|
|
8e115d4685 | ||
|
|
00c189844f | ||
|
|
4587c286bb | ||
|
|
b46097a7fc | ||
|
|
299cb6a2ff | ||
|
|
1b7b91b328 | ||
|
|
01a1480ebd | ||
|
|
26b8abb118 | ||
|
|
53d1bbb530 | ||
|
|
a3ef55274e | ||
|
|
2034915457 | ||
|
|
9e46d7964a | ||
|
|
f9828a227b | ||
|
|
3341fa5f33 | ||
|
|
e38ae47e76 | ||
|
|
934c0e3c4c | ||
|
|
994a6ae7ed | ||
|
|
cdbe93c289 | ||
|
|
56f90e4d96 | ||
|
|
34977abfec | ||
|
|
5622103eb1 | ||
|
|
b9a1ab4a44 | ||
|
|
18997833c4 | ||
|
|
f99b194afc | ||
|
|
566a347da7 | ||
|
|
881306f6a4 | ||
|
|
f63504af01 | ||
|
|
d140b82a70 | ||
|
|
681211b1a5 | ||
|
|
6c8b1f3618 | ||
|
|
d341065c34 | ||
|
|
81b1346080 | ||
|
|
5613be3980 | ||
|
|
fbcf0eb94c | ||
|
|
1c7b9cc354 | ||
|
|
75e900606e | ||
|
|
7c665c53b5 | ||
|
|
f72047eb02 | ||
|
|
ade424c074 | ||
|
|
5ad805de3c | ||
|
|
ece77cf620 | ||
|
|
7eaa559056 | ||
|
|
08a9377373 | ||
|
|
a2837e6aee | ||
|
|
fa03f6194d | ||
|
|
d2851ea1df | ||
|
|
72f8ac7857 | ||
|
|
77a267bc2f | ||
|
|
ad238daadc | ||
|
|
42370ba203 | ||
|
|
d9691c2a3b | ||
|
|
66cca981a9 | ||
|
|
9640ebb593 | ||
|
|
645f32fd65 | ||
|
|
cb6e65f972 | ||
|
|
425bdc0ba6 | ||
|
|
c36341e51f | ||
|
|
553d896899 | ||
|
|
ac79b3072e | ||
|
|
c0aa9bfd4b | ||
|
|
e97100028d | ||
|
|
da89617432 | ||
|
|
e6203dffd3 | ||
|
|
c13cfe9c37 | ||
|
|
2447df9341 | ||
|
|
1c1fbe0ec1 | ||
|
|
4a6d2017fd | ||
|
|
b4997a52df | ||
|
|
464dec1dcb | ||
|
|
85506ac78a | ||
|
|
6d97355b42 | ||
|
|
f9e75c616a | ||
|
|
a821d02dfb | ||
|
|
e05169c7a4 | ||
|
|
1cc3431529 | ||
|
|
4ba765f265 | ||
|
|
50a7af4179 | ||
|
|
e0a2116e88 | ||
|
|
d8e1ed5f4a | ||
|
|
f1b8e8a963 | ||
|
|
9a9fd44c62 | ||
|
|
bc3fe7a18e | ||
|
|
19f3559345 | ||
|
|
fad0e23797 | ||
|
|
7f931e4d70 | ||
|
|
a04835629b | ||
|
|
78cd80746d | ||
|
|
9ac93920d8 | ||
|
|
1818fce1ae | ||
|
|
f524edc4b9 | ||
|
|
19f990ed31 | ||
|
|
5d83c82b81 | ||
|
|
d63d154457 | ||
|
|
933b15ce36 | ||
|
|
6ec7b63ebe | ||
|
|
26bfbc55e9 | ||
|
|
d75ca0f5f3 | ||
|
|
fed8f137e9 | ||
|
|
f44d65e023 | ||
|
|
a270bd76de | ||
|
|
9209e419ec | ||
|
|
98f8f15e90 | ||
|
|
b2a2868afd | ||
|
|
0d4737d360 | ||
|
|
2b370a0eca | ||
|
|
618fe81207 | ||
|
|
c0fe4861f9 | ||
|
|
dfd33fdab1 | ||
|
|
cceee05c15 | ||
|
|
f560d2a05e | ||
|
|
3601cff88e | ||
|
|
ca5c0a759f | ||
|
|
6f9e6909ce | ||
|
|
ccf563437b | ||
|
|
78e97428fd | ||
|
|
8b4c730993 | ||
|
|
0a071a13e2 | ||
|
|
ab80991eac | ||
|
|
ee7262efb4 | ||
|
|
ea5a52cdc8 | ||
|
|
31fe0322ab | ||
|
|
e8e0eabb99 | ||
|
|
1629dad1a8 | ||
|
|
d9baad530a | ||
|
|
4a1d00e59a | ||
|
|
437e4e027c | ||
|
|
3726f7eca9 | ||
|
|
c943cf515c | ||
|
|
3b0c2a7e56 | ||
|
|
6ebaa9cd1d | ||
|
|
f81c32f6ea | ||
|
|
c0cd7a1a62 | ||
|
|
7a61c818c6 | ||
|
|
2800625bcf | ||
|
|
cfec998221 | ||
|
|
7203cffbd7 | ||
|
|
23397ef6a9 | ||
|
|
0e154635ff | ||
|
|
2e6e518722 | ||
|
|
e0cded97c7 | ||
|
|
87a6a029bb | ||
|
|
1cc3c22d3f | ||
|
|
2341d1d965 | ||
|
|
a0bae9485c | ||
|
|
f281b0fc6b | ||
|
|
6f89fe81cc | ||
|
|
34f6ead7a1 | ||
|
|
8985527a87 | ||
|
|
bd87a3aa4d | ||
|
|
768a505904 | ||
|
|
d97c1f0fc3 | ||
|
|
c3fcd34d4c | ||
|
|
44d9eaea95 | ||
|
|
0f34f5139a | ||
|
|
2afb1a673d | ||
|
|
c2f7f29630 | ||
|
|
b01f5dd24b | ||
|
|
0cda0c449f | ||
|
|
40fdf12bc9 | ||
|
|
3939a80302 | ||
|
|
d32a102613 | ||
|
|
20949d39c4 | ||
|
|
310a0c8d13 | ||
|
|
c9e80ac7e9 | ||
|
|
5df4e9e1cf | ||
|
|
4022ee74e8 | ||
|
|
80a4115c44 | ||
|
|
ce548efd80 | ||
|
|
2edf622b41 | ||
|
|
66ac9078aa | ||
|
|
ba75f18f5a | ||
|
|
8ee2ece03e | ||
|
|
7060ab8c44 | ||
|
|
85d8244b8a | ||
|
|
3f9421ab08 | ||
|
|
2f3fbf00b7 | ||
|
|
d595ec8a07 | ||
|
|
4ff5462cc4 | ||
|
|
404f95b442 | ||
|
|
89cf784022 | ||
|
|
02142f352d | ||
|
|
ec3dd7d1e5 | ||
|
|
7355799030 | ||
|
|
982166df3c | ||
|
|
c7d3512ad2 | ||
|
|
ada6f7b3fb | ||
|
|
78e16495bd | ||
|
|
12085e6152 | ||
|
|
6764463689 | ||
|
|
7055276665 | ||
|
|
71b3ebd15a | ||
|
|
b87910e596 | ||
|
|
e19bfd670b | ||
|
|
7b3c96e80b | ||
|
|
01ff3cf9d9 | ||
|
|
d66da0c10d | ||
|
|
3491bb1b40 | ||
|
|
3bf995eb71 | ||
|
|
2169ce1722 | ||
|
|
275e9485e9 | ||
|
|
95198ae540 | ||
|
|
aed2d3899d | ||
|
|
4011d62ac7 | ||
|
|
d2aa0573de | ||
|
|
571b2e3ab6 | ||
|
|
a7f48360b7 | ||
|
|
22f2f8680a | ||
|
|
d92004a9e7 | ||
|
|
64875894d6 | ||
|
|
3f7a288526 | ||
|
|
a2a067a81c | ||
|
|
f9f61b8da7 | ||
|
|
cd69b82fc9 | ||
|
|
d20631598e | ||
|
|
229ebe16f3 | ||
|
|
a172f67d37 | ||
|
|
ee4a1de566 | ||
|
|
7ab99c028c | ||
|
|
0e1d12b1ae | ||
|
|
e090ddd761 | ||
|
|
9721ce6877 | ||
|
|
8dde94f421 | ||
|
|
f5f6b22af1 | ||
|
|
f8a93b6561 | ||
|
|
840a03f048 | ||
|
|
85f3b5ce78 | ||
|
|
f4284fec2f | ||
|
|
3a89b3152f | ||
|
|
a0356328c3 | ||
|
|
4b6f37b1d7 | ||
|
|
716705fb5a | ||
|
|
d246836480 | ||
|
|
6ee2b82d15 | ||
|
|
73ff8d36a5 | ||
|
|
1397def3b8 | ||
|
|
d443529041 | ||
|
|
373bb20f1b | ||
|
|
3b44cce6dc | ||
|
|
46056fe45b | ||
|
|
1816c190b2 | ||
|
|
00abaee6b3 | ||
|
|
3a301f54e0 | ||
|
|
762accbd6d | ||
|
|
e0422d7d34 | ||
|
|
6ba2057a88 | ||
|
|
752969bce5 | ||
|
|
efbdfd2954 | ||
|
|
bb7a177a5d | ||
|
|
9b56ca8cde | ||
|
|
b0a08782e0 | ||
|
|
6c9955f220 | ||
|
|
f56b94c0f9 | ||
|
|
3cf035820b | ||
|
|
99a796d066 | ||
|
|
1cd1b1aba8 | ||
|
|
4131c14629 | ||
|
|
c2acda5796 | ||
|
|
4806e7e9d9 | ||
|
|
76606fd44f | ||
|
|
2983f1a3b6 | ||
|
|
8019779b3a | ||
|
|
62cdcbf422 | ||
|
|
b12a5a36e1 | ||
|
|
e32763e464 | ||
|
|
b85cf3f9d2 | ||
|
|
3777bcc2af | ||
|
|
52cde48ff0 | ||
|
|
bf1da35303 | ||
|
|
c1bf11da34 | ||
|
|
3c20325b37 | ||
|
|
fd8ccb8d8f | ||
|
|
d76e947021 | ||
|
|
c91ed96543 | ||
|
|
b164531ba8 | ||
|
|
7c623a8704 | ||
|
|
7ae3340336 | ||
|
|
653b73c601 | ||
|
|
7c93d91bae | ||
|
|
07da0cfb2b | ||
|
|
b411a11c2c | ||
|
|
0555b84d05 | ||
|
|
790bddef63 | ||
|
|
a3089b8aa7 | ||
|
|
77c8426d63 | ||
|
|
faf226f6c2 | ||
|
|
06d143b81a | ||
|
|
08b6a0a702 | ||
|
|
a20d1e3656 | ||
|
|
36cc3682ca | ||
|
|
1b495ecafa | ||
|
|
7d1a0be07e | ||
|
|
327f65c991 | ||
|
|
4ac89f6849 | ||
|
|
db3b070ed0 | ||
|
|
6d940f476a | ||
|
|
1ca701dda4 | ||
|
|
291c44100c | ||
|
|
c8d676e06b | ||
|
|
4c1ae0eddc | ||
|
|
39eadc814f | ||
|
|
f7ecad61ba | ||
|
|
fa4cb54549 | ||
|
|
2be33c5e0a | ||
|
|
904d7e5d5a | ||
|
|
dbc4a65d48 | ||
|
|
b93f4aabf1 | ||
|
|
9eaa40c7a4 | ||
|
|
b308a882fb | ||
|
|
7f63ba2087 | ||
|
|
d7269cfcc6 | ||
|
|
2850a574f6 | ||
|
|
dcb8d4f702 | ||
|
|
aeadc0c4b0 | ||
|
|
683c6b17be | ||
|
|
69dd5c91b7 | ||
|
|
5cf7dfca8f | ||
|
|
62a49d4244 | ||
|
|
93ee6322f2 | ||
|
|
914990b58a | ||
|
|
f78bb5adb6 | ||
|
|
905f5e7289 | ||
|
|
ec503618c3 | ||
|
|
7a41cbc314 | ||
|
|
c58ba734e7 | ||
|
|
68f63be62f | ||
|
|
2aa4ca1351 | ||
|
|
fbabb27787 | ||
|
|
0960d78eb5 | ||
|
|
474b40511f | ||
|
|
18b80aced3 | ||
|
|
b964d362b7 | ||
|
|
3914e41f3c | ||
|
|
82bdfcb99b | ||
|
|
976cea600f | ||
|
|
8c8713c3f7 | ||
|
|
2359ae6ce7 | ||
|
|
b570fd35c8 | ||
|
|
9d94e6b3b4 | ||
|
|
cfab789823 | ||
|
|
81917425dc | ||
|
|
bfb62709d4 | ||
|
|
ca3f2ee782 | ||
|
|
fc8703a40f | ||
|
|
80517c7ac1 | ||
|
|
2b4b46eaf8 | ||
|
|
40b9dae608 | ||
|
|
5975cd6e09 | ||
|
|
258c9ff52b | ||
|
|
89c5d498a4 | ||
|
|
76cb4d123a | ||
|
|
f0c29c7699 | ||
|
|
aa4151ced7 | ||
|
|
0a6fa978fa | ||
|
|
dc02002b9d | ||
|
|
f071a3f38b | ||
|
|
b935231e47 | ||
|
|
b9f7613567 | ||
|
|
1289a031ab | ||
|
|
289546ef6d | ||
|
|
aacff4db5d | ||
|
|
f833b56122 | ||
|
|
7eb0f2993f | ||
|
|
abb341abfe | ||
|
|
0d90614369 | ||
|
|
ec84bebeea | ||
|
|
9176867d6b | ||
|
|
281a137ff5 | ||
|
|
d6543480ac | ||
|
|
ae6391b866 | ||
|
|
10b56e4258 | ||
|
|
0ff2597957 | ||
|
|
026b28e962 | ||
|
|
9a1e67294a | ||
|
|
cdb448a5cc | ||
|
|
ab80e726e2 | ||
|
|
2d5d0f67b2 | ||
|
|
d4100b6096 | ||
|
|
955e854d77 | ||
|
|
0c37f88c49 | ||
|
|
48167eeb9c | ||
|
|
24177197f7 | ||
|
|
863fc0ba97 | ||
|
|
9f7b229d02 | ||
|
|
ffd909f3d9 | ||
|
|
1ebf096a33 | ||
|
|
96d51965e5 | ||
|
|
04b510b020 | ||
|
|
c9a301d50e | ||
|
|
b304bd1a8b | ||
|
|
b99525b231 | ||
|
|
634db13990 | ||
|
|
ad51a77989 | ||
|
|
3348a39e8a | ||
|
|
81c2e356ec | ||
|
|
de6c3512d2 | ||
|
|
36dc1e938a | ||
|
|
07a78cf6f7 | ||
|
|
eaa673e0c3 | ||
|
|
f2c4ca081f | ||
|
|
e3d707f0b4 | ||
|
|
fb93fed2e5 | ||
|
|
95dfc2f23d | ||
|
|
408df2093a | ||
|
|
f32bf0cc3e | ||
|
|
dbbe3145b6 | ||
|
|
f8bf3ea2ef | ||
|
|
053bd31d43 | ||
|
|
1aefc3f37a | ||
|
|
3de955d9ce | ||
|
|
0ff88fd366 | ||
|
|
eb84020773 | ||
|
|
4bbfea3c7c | ||
|
|
63d4fb7558 | ||
|
|
953895cd81 | ||
|
|
a6c3f4efc0 | ||
|
|
11e880d034 | ||
|
|
e4d6bdb398 | ||
|
|
6ced1783e3 | ||
|
|
8051f78d10 | ||
|
|
b724176b23 | ||
|
|
fdca16ea92 | ||
|
|
f8fd8b432a | ||
|
|
9148ae70ce | ||
|
|
447cb26d28 | ||
|
|
2af36465f6 | ||
|
|
d5f7265424 | ||
|
|
cc16af7f2d | ||
|
|
7a4d75bc44 | ||
|
|
ec0380fd3b | ||
|
|
b17cc71dfb | ||
|
|
89b327ed7b | ||
|
|
9bf361a1b8 | ||
|
|
d11c171c75 | ||
|
|
c523c45d17 | ||
|
|
c1b9c0e1b6 | ||
|
|
487b9ff03e | ||
|
|
ec62b0cdfb | ||
|
|
6d0470064f | ||
|
|
7450b3fd1a | ||
|
|
5b70910d77 | ||
|
|
52de5ff5ff | ||
|
|
c4389a1679 | ||
|
|
35faaa6cae | ||
|
|
3c0b13975a | ||
|
|
bc88696339 | ||
|
|
8f99c3f64a | ||
|
|
88016d96d4 | ||
|
|
47df73b18f | ||
|
|
1c12d2b8cd | ||
|
|
eb38837a8c | ||
|
|
159c7fbfd1 | ||
|
|
7ee31f0884 | ||
|
|
0c5e12571a | ||
|
|
9db973217f | ||
|
|
cf1a745283 | ||
|
|
834e3f1963 | ||
|
|
3f8f7573c9 | ||
|
|
0ae272f1f6 | ||
|
|
8774295e2e | ||
|
|
0c8d2594ef | ||
|
|
205bd2676b | ||
|
|
25849fd9cc | ||
|
|
7d6eac9ff7 | ||
|
|
31017ebc98 | ||
|
|
724a7b0ecc | ||
|
|
91e13d447a | ||
|
|
7c8ad9d535 | ||
|
|
9cd3ab853d | ||
|
|
0b0f8c5829 | ||
|
|
ae7bc7fb1b | ||
|
|
09750872b5 | ||
|
|
076e51017b | ||
|
|
95e7b00996 | ||
|
|
ddecf1ac21 |
@@ -33,7 +33,7 @@
|
||||
"GitHub.vscode-pull-request-github",
|
||||
"GitHub.copilot"
|
||||
],
|
||||
// Please keep this file in sync with settings in home-assistant/.vscode/settings.default.json
|
||||
// Please keep this file in sync with settings in home-assistant/.vscode/settings.default.jsonc
|
||||
"settings": {
|
||||
"python.experiments.optOutFrom": ["pythonTestAdapter"],
|
||||
"python.defaultInterpreterPath": "/home/vscode/.local/ha-venv/bin/python",
|
||||
@@ -41,6 +41,7 @@
|
||||
"python.terminal.activateEnvInCurrentTerminal": true,
|
||||
"python.testing.pytestArgs": ["--no-cov"],
|
||||
"pylint.importStrategy": "fromEnvironment",
|
||||
"python.analysis.typeCheckingMode": "basic",
|
||||
"editor.formatOnPaste": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnType": true,
|
||||
@@ -62,6 +63,9 @@
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "charliermarsh.ruff"
|
||||
},
|
||||
"[json][jsonc][yaml]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": ["homeassistant/components/*/manifest.json"],
|
||||
|
||||
1
.github/copilot-instructions.md
vendored
1
.github/copilot-instructions.md
vendored
@@ -74,6 +74,7 @@ rules:
|
||||
- **Formatting**: Ruff
|
||||
- **Linting**: PyLint and Ruff
|
||||
- **Type Checking**: MyPy
|
||||
- **Lint/Type/Format Fixes**: Always prefer addressing the underlying issue (e.g., import the typed source, update shared stubs, align with Ruff expectations, or correct formatting at the source) before disabling a rule, adding `# type: ignore`, or skipping a formatter. Treat suppressions and `noqa` comments as a last resort once no compliant fix exists
|
||||
- **Testing**: pytest with plain functions and fixtures
|
||||
- **Language**: American English for all code, comments, and documentation (use sentence case, including titles)
|
||||
|
||||
|
||||
18
.github/workflows/builder.yml
vendored
18
.github/workflows/builder.yml
vendored
@@ -69,7 +69,7 @@ jobs:
|
||||
run: find ./homeassistant/components/*/translations -name "*.json" | tar zcvf translations.tar.gz -T -
|
||||
|
||||
- name: Upload translations
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
with:
|
||||
name: translations
|
||||
path: translations.tar.gz
|
||||
@@ -175,7 +175,7 @@ jobs:
|
||||
sed -i "s|pykrakenapi|# pykrakenapi|g" requirements_all.txt
|
||||
|
||||
- name: Download translations
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
with:
|
||||
name: translations
|
||||
|
||||
@@ -190,7 +190,7 @@ jobs:
|
||||
echo "${{ github.sha }};${{ github.ref }};${{ github.event_name }};${{ github.actor }}" > rootfs/OFFICIAL_IMAGE
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -257,7 +257,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -326,20 +326,20 @@ jobs:
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
|
||||
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
|
||||
with:
|
||||
cosign-release: "v2.2.3"
|
||||
|
||||
- name: Login to DockerHub
|
||||
if: matrix.registry == 'docker.io/homeassistant'
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
if: matrix.registry == 'ghcr.io/home-assistant'
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
@@ -464,7 +464,7 @@ jobs:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
|
||||
- name: Download translations
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
with:
|
||||
name: translations
|
||||
|
||||
@@ -504,7 +504,7 @@ jobs:
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
|
||||
uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
|
||||
746
.github/workflows/ci.yaml
vendored
746
.github/workflows/ci.yaml
vendored
File diff suppressed because it is too large
Load Diff
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@@ -24,11 +24,11 @@ jobs:
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
|
||||
uses: github/codeql-action/init@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
|
||||
with:
|
||||
languages: python
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 # v3.30.3
|
||||
uses: github/codeql-action/analyze@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
|
||||
with:
|
||||
category: "/language:python"
|
||||
|
||||
6
.github/workflows/stale.yml
vendored
6
.github/workflows/stale.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
# - No PRs marked as no-stale
|
||||
# - No issues (-1)
|
||||
- name: 60 days stale PRs policy
|
||||
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
|
||||
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 60
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
# - No issues marked as no-stale or help-wanted
|
||||
# - No PRs (-1)
|
||||
- name: 90 days stale issues
|
||||
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
|
||||
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
|
||||
with:
|
||||
repo-token: ${{ steps.token.outputs.token }}
|
||||
days-before-stale: 90
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
# - No Issues marked as no-stale or help-wanted
|
||||
# - No PRs (-1)
|
||||
- name: Needs more information stale issues policy
|
||||
uses: actions/stale@3a9db7e6a41a89f618792c92c0e97cc736e1b13f # v10.0.0
|
||||
uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 # v10.1.0
|
||||
with:
|
||||
repo-token: ${{ steps.token.outputs.token }}
|
||||
only-labels: "needs-more-information"
|
||||
|
||||
78
.github/workflows/wheels.yml
vendored
78
.github/workflows/wheels.yml
vendored
@@ -31,7 +31,8 @@ jobs:
|
||||
outputs:
|
||||
architectures: ${{ steps.info.outputs.architectures }}
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
- &checkout
|
||||
name: Checkout the repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
@@ -91,7 +92,7 @@ jobs:
|
||||
) > build_constraints.txt
|
||||
|
||||
- name: Upload env_file
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: &actions-upload-artifact actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
with:
|
||||
name: env_file
|
||||
path: ./.env_file
|
||||
@@ -99,14 +100,14 @@ jobs:
|
||||
overwrite: true
|
||||
|
||||
- name: Upload build_constraints
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: *actions-upload-artifact
|
||||
with:
|
||||
name: build_constraints
|
||||
path: ./build_constraints.txt
|
||||
overwrite: true
|
||||
|
||||
- name: Upload requirements_diff
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: *actions-upload-artifact
|
||||
with:
|
||||
name: requirements_diff
|
||||
path: ./requirements_diff.txt
|
||||
@@ -118,7 +119,7 @@ jobs:
|
||||
python -m script.gen_requirements_all ci
|
||||
|
||||
- name: Upload requirements_all_wheels
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
||||
uses: *actions-upload-artifact
|
||||
with:
|
||||
name: requirements_all_wheels
|
||||
path: ./requirements_all_wheels_*.txt
|
||||
@@ -127,28 +128,41 @@ jobs:
|
||||
name: Build Core wheels ${{ matrix.abi }} for ${{ matrix.arch }} (musllinux_1_2)
|
||||
if: github.repository_owner == 'home-assistant'
|
||||
needs: init
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
abi: ["cp313"]
|
||||
matrix: &matrix-build
|
||||
abi: ["cp313", "cp314"]
|
||||
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
- arch: aarch64
|
||||
os: ubuntu-24.04-arm
|
||||
exclude:
|
||||
- abi: cp314
|
||||
arch: armv7
|
||||
- abi: cp314
|
||||
arch: armhf
|
||||
- abi: cp314
|
||||
arch: i386
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- *checkout
|
||||
|
||||
- name: Download env_file
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
- &download-env-file
|
||||
name: Download env_file
|
||||
uses: &actions-download-artifact actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0
|
||||
with:
|
||||
name: env_file
|
||||
|
||||
- name: Download build_constraints
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
- &download-build-constraints
|
||||
name: Download build_constraints
|
||||
uses: *actions-download-artifact
|
||||
with:
|
||||
name: build_constraints
|
||||
|
||||
- name: Download requirements_diff
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
- &download-requirements-diff
|
||||
name: Download requirements_diff
|
||||
uses: *actions-download-artifact
|
||||
with:
|
||||
name: requirements_diff
|
||||
|
||||
@@ -160,7 +174,7 @@ jobs:
|
||||
|
||||
# home-assistant/wheels doesn't support sha pinning
|
||||
- name: Build wheels
|
||||
uses: home-assistant/wheels@2025.09.1
|
||||
uses: &home-assistant-wheels home-assistant/wheels@2025.10.0
|
||||
with:
|
||||
abi: ${{ matrix.abi }}
|
||||
tag: musllinux_1_2
|
||||
@@ -177,33 +191,19 @@ jobs:
|
||||
name: Build wheels ${{ matrix.abi }} for ${{ matrix.arch }}
|
||||
if: github.repository_owner == 'home-assistant'
|
||||
needs: init
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
abi: ["cp313"]
|
||||
arch: ${{ fromJson(needs.init.outputs.architectures) }}
|
||||
matrix: *matrix-build
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- *checkout
|
||||
|
||||
- name: Download env_file
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
with:
|
||||
name: env_file
|
||||
|
||||
- name: Download build_constraints
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
with:
|
||||
name: build_constraints
|
||||
|
||||
- name: Download requirements_diff
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
with:
|
||||
name: requirements_diff
|
||||
- *download-env-file
|
||||
- *download-build-constraints
|
||||
- *download-requirements-diff
|
||||
|
||||
- name: Download requirements_all_wheels
|
||||
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.0
|
||||
uses: *actions-download-artifact
|
||||
with:
|
||||
name: requirements_all_wheels
|
||||
|
||||
@@ -221,7 +221,7 @@ jobs:
|
||||
|
||||
# home-assistant/wheels doesn't support sha pinning
|
||||
- name: Build wheels
|
||||
uses: home-assistant/wheels@2025.09.1
|
||||
uses: *home-assistant-wheels
|
||||
with:
|
||||
abi: ${{ matrix.abi }}
|
||||
tag: musllinux_1_2
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -79,7 +79,6 @@ junit.xml
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
.python-version
|
||||
.tool-versions
|
||||
|
||||
# emacs auto backups
|
||||
@@ -112,6 +111,7 @@ virtualization/vagrant/config
|
||||
!.vscode/cSpell.json
|
||||
!.vscode/extensions.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/settings.default.jsonc
|
||||
.env
|
||||
|
||||
# Windows Explorer
|
||||
@@ -141,4 +141,5 @@ pytest_buckets.txt
|
||||
|
||||
# AI tooling
|
||||
.claude/settings.local.json
|
||||
.serena/
|
||||
|
||||
|
||||
@@ -33,10 +33,13 @@ repos:
|
||||
rev: v1.37.1
|
||||
hooks:
|
||||
- id: yamllint
|
||||
- repo: https://github.com/pre-commit/mirrors-prettier
|
||||
rev: v3.0.3
|
||||
- repo: https://github.com/rbubley/mirrors-prettier
|
||||
rev: v3.6.2
|
||||
hooks:
|
||||
- id: prettier
|
||||
additional_dependencies:
|
||||
- prettier@3.6.2
|
||||
- prettier-plugin-sort-json@4.1.1
|
||||
- repo: https://github.com/cdce8p/python-typing-update
|
||||
rev: v0.6.0
|
||||
hooks:
|
||||
|
||||
24
.prettierrc.js
Normal file
24
.prettierrc.js
Normal file
@@ -0,0 +1,24 @@
|
||||
/** @type {import("prettier").Config} */
|
||||
module.exports = {
|
||||
overrides: [
|
||||
{
|
||||
files: "./homeassistant/**/*.json",
|
||||
options: {
|
||||
plugins: [require.resolve("prettier-plugin-sort-json")],
|
||||
jsonRecursiveSort: true,
|
||||
jsonSortOrder: JSON.stringify({ [/.*/]: "numeric" }),
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["manifest.json", "./**/brands/*.json"],
|
||||
options: {
|
||||
// domain and name should stay at the top
|
||||
jsonSortOrder: JSON.stringify({
|
||||
domain: null,
|
||||
name: null,
|
||||
[/.*/]: "numeric",
|
||||
}),
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.13
|
||||
@@ -182,7 +182,6 @@ homeassistant.components.efergy.*
|
||||
homeassistant.components.eheimdigital.*
|
||||
homeassistant.components.electrasmart.*
|
||||
homeassistant.components.electric_kiwi.*
|
||||
homeassistant.components.elevenlabs.*
|
||||
homeassistant.components.elgato.*
|
||||
homeassistant.components.elkm1.*
|
||||
homeassistant.components.emulated_hue.*
|
||||
@@ -203,6 +202,7 @@ homeassistant.components.feedreader.*
|
||||
homeassistant.components.file_upload.*
|
||||
homeassistant.components.filesize.*
|
||||
homeassistant.components.filter.*
|
||||
homeassistant.components.firefly_iii.*
|
||||
homeassistant.components.fitbit.*
|
||||
homeassistant.components.flexit_bacnet.*
|
||||
homeassistant.components.flux_led.*
|
||||
@@ -220,6 +220,7 @@ homeassistant.components.generic_thermostat.*
|
||||
homeassistant.components.geo_location.*
|
||||
homeassistant.components.geocaching.*
|
||||
homeassistant.components.gios.*
|
||||
homeassistant.components.github.*
|
||||
homeassistant.components.glances.*
|
||||
homeassistant.components.go2rtc.*
|
||||
homeassistant.components.goalzero.*
|
||||
@@ -277,6 +278,7 @@ homeassistant.components.imap.*
|
||||
homeassistant.components.imgw_pib.*
|
||||
homeassistant.components.immich.*
|
||||
homeassistant.components.incomfort.*
|
||||
homeassistant.components.inels.*
|
||||
homeassistant.components.input_button.*
|
||||
homeassistant.components.input_select.*
|
||||
homeassistant.components.input_text.*
|
||||
@@ -325,6 +327,7 @@ homeassistant.components.london_underground.*
|
||||
homeassistant.components.lookin.*
|
||||
homeassistant.components.lovelace.*
|
||||
homeassistant.components.luftdaten.*
|
||||
homeassistant.components.lunatone.*
|
||||
homeassistant.components.madvr.*
|
||||
homeassistant.components.manual.*
|
||||
homeassistant.components.mastodon.*
|
||||
@@ -475,6 +478,7 @@ homeassistant.components.skybell.*
|
||||
homeassistant.components.slack.*
|
||||
homeassistant.components.sleep_as_android.*
|
||||
homeassistant.components.sleepiq.*
|
||||
homeassistant.components.sma.*
|
||||
homeassistant.components.smhi.*
|
||||
homeassistant.components.smlight.*
|
||||
homeassistant.components.smtp.*
|
||||
@@ -553,6 +557,7 @@ homeassistant.components.vacuum.*
|
||||
homeassistant.components.vallox.*
|
||||
homeassistant.components.valve.*
|
||||
homeassistant.components.velbus.*
|
||||
homeassistant.components.vivotek.*
|
||||
homeassistant.components.vlc_telnet.*
|
||||
homeassistant.components.vodafone_station.*
|
||||
homeassistant.components.volvo.*
|
||||
|
||||
@@ -7,13 +7,19 @@
|
||||
"python.testing.pytestEnabled": false,
|
||||
// https://code.visualstudio.com/docs/python/linting#_general-settings
|
||||
"pylint.importStrategy": "fromEnvironment",
|
||||
// Pyright is too pedantic for Home Assistant
|
||||
"python.analysis.typeCheckingMode": "basic",
|
||||
"[python]": {
|
||||
"editor.defaultFormatter": "charliermarsh.ruff",
|
||||
},
|
||||
"[json][jsonc][yaml]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
},
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": [
|
||||
"homeassistant/components/*/manifest.json"
|
||||
],
|
||||
// This value differs between working with devcontainer and locally, therefor this value should NOT be in sync!
|
||||
"url": "./script/json_schemas/manifest_schema.json"
|
||||
}
|
||||
]
|
||||
{
|
||||
"fileMatch": ["homeassistant/components/*/manifest.json"],
|
||||
// This value differs between working with devcontainer and locally, therefore this value should NOT be in sync!
|
||||
"url": "./script/json_schemas/manifest_schema.json",
|
||||
},
|
||||
],
|
||||
}
|
||||
30
CODEOWNERS
generated
30
CODEOWNERS
generated
@@ -46,6 +46,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/accuweather/ @bieniu
|
||||
/homeassistant/components/acmeda/ @atmurray
|
||||
/tests/components/acmeda/ @atmurray
|
||||
/homeassistant/components/actron_air/ @kclif9 @JagadishDhanamjayam
|
||||
/tests/components/actron_air/ @kclif9 @JagadishDhanamjayam
|
||||
/homeassistant/components/adax/ @danielhiversen @lazytarget
|
||||
/tests/components/adax/ @danielhiversen @lazytarget
|
||||
/homeassistant/components/adguard/ @frenck
|
||||
@@ -492,6 +494,10 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/filesize/ @gjohansson-ST
|
||||
/homeassistant/components/filter/ @dgomes
|
||||
/tests/components/filter/ @dgomes
|
||||
/homeassistant/components/fing/ @Lorenzo-Gasparini
|
||||
/tests/components/fing/ @Lorenzo-Gasparini
|
||||
/homeassistant/components/firefly_iii/ @erwindouna
|
||||
/tests/components/firefly_iii/ @erwindouna
|
||||
/homeassistant/components/fireservicerota/ @cyberjunky
|
||||
/tests/components/fireservicerota/ @cyberjunky
|
||||
/homeassistant/components/firmata/ @DaAwesomeP
|
||||
@@ -615,6 +621,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/greeneye_monitor/ @jkeljo
|
||||
/homeassistant/components/group/ @home-assistant/core
|
||||
/tests/components/group/ @home-assistant/core
|
||||
/homeassistant/components/growatt_server/ @johanzander
|
||||
/tests/components/growatt_server/ @johanzander
|
||||
/homeassistant/components/guardian/ @bachya
|
||||
/tests/components/guardian/ @bachya
|
||||
/homeassistant/components/habitica/ @tr4nt0r
|
||||
@@ -735,6 +743,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/improv_ble/ @emontnemery
|
||||
/homeassistant/components/incomfort/ @jbouwh
|
||||
/tests/components/incomfort/ @jbouwh
|
||||
/homeassistant/components/inels/ @epdevlab
|
||||
/tests/components/inels/ @epdevlab
|
||||
/homeassistant/components/influxdb/ @mdegat01
|
||||
/tests/components/influxdb/ @mdegat01
|
||||
/homeassistant/components/inkbird/ @bdraco
|
||||
@@ -908,6 +918,8 @@ build.json @home-assistant/supervisor
|
||||
/homeassistant/components/luci/ @mzdrale
|
||||
/homeassistant/components/luftdaten/ @fabaff @frenck
|
||||
/tests/components/luftdaten/ @fabaff @frenck
|
||||
/homeassistant/components/lunatone/ @MoonDevLT
|
||||
/tests/components/lunatone/ @MoonDevLT
|
||||
/homeassistant/components/lupusec/ @majuss @suaveolent
|
||||
/tests/components/lupusec/ @majuss @suaveolent
|
||||
/homeassistant/components/lutron/ @cdheiser @wilburCForce
|
||||
@@ -953,6 +965,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/met_eireann/ @DylanGore
|
||||
/homeassistant/components/meteo_france/ @hacf-fr @oncleben31 @Quentame
|
||||
/tests/components/meteo_france/ @hacf-fr @oncleben31 @Quentame
|
||||
/homeassistant/components/meteo_lt/ @xE1H
|
||||
/tests/components/meteo_lt/ @xE1H
|
||||
/homeassistant/components/meteoalarm/ @rolfberkenbosch
|
||||
/homeassistant/components/meteoclimatic/ @adrianmo
|
||||
/tests/components/meteoclimatic/ @adrianmo
|
||||
@@ -1059,6 +1073,8 @@ build.json @home-assistant/supervisor
|
||||
/homeassistant/components/nilu/ @hfurubotten
|
||||
/homeassistant/components/nina/ @DeerMaximum
|
||||
/tests/components/nina/ @DeerMaximum
|
||||
/homeassistant/components/nintendo_parental_controls/ @pantherale0
|
||||
/tests/components/nintendo_parental_controls/ @pantherale0
|
||||
/homeassistant/components/nissan_leaf/ @filcole
|
||||
/homeassistant/components/noaa_tides/ @jdelaney72
|
||||
/homeassistant/components/nobo_hub/ @echoromeo @oyvindwe
|
||||
@@ -1127,6 +1143,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/opengarage/ @danielhiversen
|
||||
/homeassistant/components/openhome/ @bazwilliams
|
||||
/tests/components/openhome/ @bazwilliams
|
||||
/homeassistant/components/openrgb/ @felipecrs
|
||||
/tests/components/openrgb/ @felipecrs
|
||||
/homeassistant/components/opensky/ @joostlek
|
||||
/tests/components/opensky/ @joostlek
|
||||
/homeassistant/components/opentherm_gw/ @mvn23
|
||||
@@ -1190,8 +1208,6 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/plex/ @jjlawren
|
||||
/homeassistant/components/plugwise/ @CoMPaTech @bouwew
|
||||
/tests/components/plugwise/ @CoMPaTech @bouwew
|
||||
/homeassistant/components/plum_lightpad/ @ColinHarrington @prystupa
|
||||
/tests/components/plum_lightpad/ @ColinHarrington @prystupa
|
||||
/homeassistant/components/point/ @fredrike
|
||||
/tests/components/point/ @fredrike
|
||||
/homeassistant/components/pooldose/ @lmaertin
|
||||
@@ -1407,8 +1423,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/sfr_box/ @epenet
|
||||
/homeassistant/components/sftp_storage/ @maretodoric
|
||||
/tests/components/sftp_storage/ @maretodoric
|
||||
/homeassistant/components/sharkiq/ @JeffResc @funkybunch
|
||||
/tests/components/sharkiq/ @JeffResc @funkybunch
|
||||
/homeassistant/components/sharkiq/ @JeffResc @funkybunch @TheOneOgre
|
||||
/tests/components/sharkiq/ @JeffResc @funkybunch @TheOneOgre
|
||||
/homeassistant/components/shell_command/ @home-assistant/core
|
||||
/tests/components/shell_command/ @home-assistant/core
|
||||
/homeassistant/components/shelly/ @bieniu @thecode @chemelli74 @bdraco
|
||||
@@ -1473,8 +1489,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/snoo/ @Lash-L
|
||||
/homeassistant/components/snooz/ @AustinBrunkhorst
|
||||
/tests/components/snooz/ @AustinBrunkhorst
|
||||
/homeassistant/components/solaredge/ @frenck @bdraco
|
||||
/tests/components/solaredge/ @frenck @bdraco
|
||||
/homeassistant/components/solaredge/ @frenck @bdraco @tronikos
|
||||
/tests/components/solaredge/ @frenck @bdraco @tronikos
|
||||
/homeassistant/components/solaredge_local/ @drobtravels @scheric
|
||||
/homeassistant/components/solarlog/ @Ernst79 @dontinelli
|
||||
/tests/components/solarlog/ @Ernst79 @dontinelli
|
||||
@@ -1527,6 +1543,8 @@ build.json @home-assistant/supervisor
|
||||
/tests/components/suez_water/ @ooii @jb101010-2
|
||||
/homeassistant/components/sun/ @home-assistant/core
|
||||
/tests/components/sun/ @home-assistant/core
|
||||
/homeassistant/components/sunricher_dali_center/ @niracler
|
||||
/tests/components/sunricher_dali_center/ @niracler
|
||||
/homeassistant/components/supla/ @mwegrzynek
|
||||
/homeassistant/components/surepetcare/ @benleb @danielhiversen
|
||||
/tests/components/surepetcare/ @benleb @danielhiversen
|
||||
|
||||
4
Dockerfile
generated
4
Dockerfile
generated
@@ -25,13 +25,13 @@ RUN \
|
||||
"armv7") go2rtc_suffix='arm' ;; \
|
||||
*) go2rtc_suffix=${BUILD_ARCH} ;; \
|
||||
esac \
|
||||
&& curl -L https://github.com/AlexxIT/go2rtc/releases/download/v1.9.9/go2rtc_linux_${go2rtc_suffix} --output /bin/go2rtc \
|
||||
&& curl -L https://github.com/AlexxIT/go2rtc/releases/download/v1.9.11/go2rtc_linux_${go2rtc_suffix} --output /bin/go2rtc \
|
||||
&& chmod +x /bin/go2rtc \
|
||||
# Verify go2rtc can be executed
|
||||
&& go2rtc --version
|
||||
|
||||
# Install uv
|
||||
RUN pip3 install uv==0.8.9
|
||||
RUN pip3 install uv==0.9.5
|
||||
|
||||
WORKDIR /usr/src
|
||||
|
||||
|
||||
@@ -34,9 +34,11 @@ WORKDIR /usr/src
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||
|
||||
RUN uv python install 3.13.2
|
||||
|
||||
USER vscode
|
||||
|
||||
COPY .python-version ./
|
||||
RUN uv python install
|
||||
|
||||
ENV VIRTUAL_ENV="/home/vscode/.local/ha-venv"
|
||||
RUN uv venv $VIRTUAL_ENV
|
||||
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
|
||||
13
build.yaml
13
build.yaml
@@ -1,13 +1,10 @@
|
||||
image: ghcr.io/home-assistant/{arch}-homeassistant
|
||||
build_from:
|
||||
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2025.10.0
|
||||
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2025.10.0
|
||||
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2025.10.0
|
||||
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2025.10.0
|
||||
i386: ghcr.io/home-assistant/i386-homeassistant-base:2025.10.0
|
||||
codenotary:
|
||||
signer: notary@home-assistant.io
|
||||
base_image: notary@home-assistant.io
|
||||
aarch64: ghcr.io/home-assistant/aarch64-homeassistant-base:2025.10.1
|
||||
armhf: ghcr.io/home-assistant/armhf-homeassistant-base:2025.10.1
|
||||
armv7: ghcr.io/home-assistant/armv7-homeassistant-base:2025.10.1
|
||||
amd64: ghcr.io/home-assistant/amd64-homeassistant-base:2025.10.1
|
||||
i386: ghcr.io/home-assistant/i386-homeassistant-base:2025.10.1
|
||||
cosign:
|
||||
base_identity: https://github.com/home-assistant/docker/.*
|
||||
identity: https://github.com/home-assistant/core/.*
|
||||
|
||||
@@ -34,6 +34,9 @@ INPUT_FIELD_CODE = "code"
|
||||
|
||||
DUMMY_SECRET = "FPPTH34D4E3MI2HG"
|
||||
|
||||
GOOGLE_AUTHENTICATOR_URL = "https://support.google.com/accounts/answer/1066447"
|
||||
AUTHY_URL = "https://authy.com/"
|
||||
|
||||
|
||||
def _generate_qr_code(data: str) -> str:
|
||||
"""Generate a base64 PNG string represent QR Code image of data."""
|
||||
@@ -229,6 +232,8 @@ class TotpSetupFlow(SetupFlow[TotpAuthModule]):
|
||||
"code": self._ota_secret,
|
||||
"url": self._url,
|
||||
"qr_code": self._image,
|
||||
"google_authenticator_url": GOOGLE_AUTHENTICATOR_URL,
|
||||
"authy_url": AUTHY_URL,
|
||||
},
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
@@ -616,34 +616,34 @@ async def async_enable_logging(
|
||||
),
|
||||
)
|
||||
|
||||
# Log errors to a file if we have write access to file or config dir
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.INFO if verbose else logging.WARNING)
|
||||
|
||||
if log_file is None:
|
||||
err_log_path = hass.config.path(ERROR_LOG_FILENAME)
|
||||
default_log_path = hass.config.path(ERROR_LOG_FILENAME)
|
||||
if "SUPERVISOR" in os.environ:
|
||||
_LOGGER.info("Running in Supervisor, not logging to file")
|
||||
# Rename the default log file if it exists, since previous versions created
|
||||
# it even on Supervisor
|
||||
if os.path.isfile(default_log_path):
|
||||
with contextlib.suppress(OSError):
|
||||
os.rename(default_log_path, f"{default_log_path}.old")
|
||||
err_log_path = None
|
||||
else:
|
||||
err_log_path = default_log_path
|
||||
else:
|
||||
err_log_path = os.path.abspath(log_file)
|
||||
|
||||
err_path_exists = os.path.isfile(err_log_path)
|
||||
err_dir = os.path.dirname(err_log_path)
|
||||
|
||||
# Check if we can write to the error log if it exists or that
|
||||
# we can create files in the containing directory if not.
|
||||
if (err_path_exists and os.access(err_log_path, os.W_OK)) or (
|
||||
not err_path_exists and os.access(err_dir, os.W_OK)
|
||||
):
|
||||
if err_log_path:
|
||||
err_handler = await hass.async_add_executor_job(
|
||||
_create_log_file, err_log_path, log_rotate_days
|
||||
)
|
||||
|
||||
err_handler.setFormatter(logging.Formatter(fmt, datefmt=FORMAT_DATETIME))
|
||||
|
||||
logger = logging.getLogger()
|
||||
logger.addHandler(err_handler)
|
||||
logger.setLevel(logging.INFO if verbose else logging.WARNING)
|
||||
|
||||
# Save the log file location for access by other components.
|
||||
hass.data[DATA_LOGGING] = err_log_path
|
||||
else:
|
||||
_LOGGER.error("Unable to set up error log %s (access denied)", err_log_path)
|
||||
|
||||
async_activate_log_queue_handler(hass)
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"domain": "ibm",
|
||||
"name": "IBM",
|
||||
"integrations": ["watson_iot", "watson_tts"]
|
||||
}
|
||||
@@ -1,70 +1,70 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Fill in your Abode login information",
|
||||
"data": {
|
||||
"username": "[%key:common::config_flow::data::email%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
},
|
||||
"mfa": {
|
||||
"title": "Enter your MFA code for Abode",
|
||||
"data": {
|
||||
"mfa_code": "MFA code (6-digits)"
|
||||
}
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"title": "[%key:component::abode::config::step::user::title%]",
|
||||
"data": {
|
||||
"username": "[%key:common::config_flow::data::email%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_mfa_code": "Invalid MFA code"
|
||||
},
|
||||
"abort": {
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"invalid_mfa_code": "Invalid MFA code"
|
||||
},
|
||||
"step": {
|
||||
"mfa": {
|
||||
"data": {
|
||||
"mfa_code": "MFA code (6-digits)"
|
||||
},
|
||||
"title": "Enter your MFA code for Abode"
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"username": "[%key:common::config_flow::data::email%]"
|
||||
},
|
||||
"title": "[%key:component::abode::config::step::user::title%]"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"username": "[%key:common::config_flow::data::email%]"
|
||||
},
|
||||
"title": "Fill in your Abode login information"
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"capture_image": {
|
||||
"name": "Capture image",
|
||||
"description": "Requests a new image capture from a camera device.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "Entity",
|
||||
"description": "Entity ID of the camera to request an image from."
|
||||
"description": "Entity ID of the camera to request an image from.",
|
||||
"name": "Entity"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Capture image"
|
||||
},
|
||||
"change_setting": {
|
||||
"name": "Change setting",
|
||||
"description": "Changes an Abode system setting.",
|
||||
"fields": {
|
||||
"setting": {
|
||||
"name": "Setting",
|
||||
"description": "Setting to change."
|
||||
"description": "Setting to change.",
|
||||
"name": "Setting"
|
||||
},
|
||||
"value": {
|
||||
"name": "Value",
|
||||
"description": "Value of the setting."
|
||||
"description": "Value of the setting.",
|
||||
"name": "Value"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Change setting"
|
||||
},
|
||||
"trigger_automation": {
|
||||
"name": "Trigger automation",
|
||||
"description": "Triggers an Abode automation.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "Entity",
|
||||
"description": "Entity ID of the automation to trigger."
|
||||
"description": "Entity ID of the automation to trigger.",
|
||||
"name": "Entity"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Trigger automation"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@ from homeassistant.components.bluetooth import async_get_scanner
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_ADDRESS
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.debounce import Debouncer
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
|
||||
from .const import CONF_IS_NEW_STYLE_SCALE
|
||||
|
||||
SCAN_INTERVAL = timedelta(seconds=15)
|
||||
UPDATE_DEBOUNCE_TIME = 0.2
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -38,11 +40,19 @@ class AcaiaCoordinator(DataUpdateCoordinator[None]):
|
||||
config_entry=entry,
|
||||
)
|
||||
|
||||
debouncer = Debouncer(
|
||||
hass=hass,
|
||||
logger=_LOGGER,
|
||||
cooldown=UPDATE_DEBOUNCE_TIME,
|
||||
immediate=True,
|
||||
function=self.async_update_listeners,
|
||||
)
|
||||
|
||||
self._scale = AcaiaScale(
|
||||
address_or_ble_device=entry.data[CONF_ADDRESS],
|
||||
name=entry.title,
|
||||
is_new_style_scale=entry.data[CONF_IS_NEW_STYLE_SCALE],
|
||||
notify_callback=self.async_update_listeners,
|
||||
notify_callback=debouncer.async_schedule_call,
|
||||
scanner=async_get_scanner(hass),
|
||||
)
|
||||
|
||||
|
||||
@@ -4,20 +4,20 @@
|
||||
"timer_running": {
|
||||
"default": "mdi:timer",
|
||||
"state": {
|
||||
"on": "mdi:timer-play",
|
||||
"off": "mdi:timer-off"
|
||||
"off": "mdi:timer-off",
|
||||
"on": "mdi:timer-play"
|
||||
}
|
||||
}
|
||||
},
|
||||
"button": {
|
||||
"tare": {
|
||||
"default": "mdi:scale-balance"
|
||||
},
|
||||
"reset_timer": {
|
||||
"default": "mdi:timer-refresh"
|
||||
},
|
||||
"start_stop": {
|
||||
"default": "mdi:timer-play"
|
||||
},
|
||||
"tare": {
|
||||
"default": "mdi:scale-balance"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "{name}",
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
|
||||
@@ -10,18 +9,19 @@
|
||||
"device_not_found": "Device could not be found.",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"bluetooth_confirm": {
|
||||
"description": "[%key:component::bluetooth::config::step::bluetooth_confirm::description%]"
|
||||
},
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
"data": {
|
||||
"address": "[%key:common::config_flow::data::device%]"
|
||||
},
|
||||
"data_description": {
|
||||
"address": "Select Acaia scale you want to set up"
|
||||
}
|
||||
},
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -32,14 +32,14 @@
|
||||
}
|
||||
},
|
||||
"button": {
|
||||
"tare": {
|
||||
"name": "Tare"
|
||||
},
|
||||
"reset_timer": {
|
||||
"name": "Reset timer"
|
||||
},
|
||||
"start_stop": {
|
||||
"name": "Start/stop timer"
|
||||
},
|
||||
"tare": {
|
||||
"name": "Tare"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"name": "[%key:common::config_flow::data::name%]",
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"latitude": "[%key:common::config_flow::data::latitude%]",
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]"
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "API key generated in the AccuWeather APIs portal."
|
||||
}
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "[%key:component::accuweather::config::step::user::data_description::api_key%]"
|
||||
}
|
||||
}
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Some sensors are not enabled by default. You can enable them in the entity registry after the integration configuration."
|
||||
@@ -29,9 +12,26 @@
|
||||
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
|
||||
"requests_exceeded": "The allowed number of requests to the AccuWeather API has been exceeded. You have to wait or change the API key."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "[%key:component::accuweather::config::step::user::data_description::api_key%]"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"latitude": "[%key:common::config_flow::data::latitude%]",
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]",
|
||||
"name": "[%key:common::config_flow::data::name%]"
|
||||
},
|
||||
"data_description": {
|
||||
"api_key": "API key generated in the AccuWeather APIs portal."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
@@ -120,9 +120,9 @@
|
||||
"pressure_tendency": {
|
||||
"name": "Pressure tendency",
|
||||
"state": {
|
||||
"steady": "Steady",
|
||||
"falling": "Falling",
|
||||
"rising": "Rising",
|
||||
"falling": "Falling"
|
||||
"steady": "Steady"
|
||||
},
|
||||
"state_attributes": {
|
||||
"options": {
|
||||
@@ -227,9 +227,6 @@
|
||||
"wet_bulb_temperature": {
|
||||
"name": "Wet bulb temperature"
|
||||
},
|
||||
"wind_speed": {
|
||||
"name": "[%key:component::weather::entity_component::_::state_attributes::wind_speed::name%]"
|
||||
},
|
||||
"wind_chill_temperature": {
|
||||
"name": "Wind chill temperature"
|
||||
},
|
||||
@@ -242,6 +239,9 @@
|
||||
"wind_gust_speed_night": {
|
||||
"name": "Wind gust speed night {forecast_day}"
|
||||
},
|
||||
"wind_speed": {
|
||||
"name": "[%key:component::weather::entity_component::_::state_attributes::wind_speed::name%]"
|
||||
},
|
||||
"wind_speed_day": {
|
||||
"name": "Wind speed day {forecast_day}"
|
||||
},
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Pick a hub to add",
|
||||
"data": {
|
||||
"id": "Host ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "Host ID"
|
||||
},
|
||||
"title": "Pick a hub to add"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
57
homeassistant/components/actron_air/__init__.py
Normal file
57
homeassistant/components/actron_air/__init__.py
Normal file
@@ -0,0 +1,57 @@
|
||||
"""The Actron Air integration."""
|
||||
|
||||
from actron_neo_api import (
|
||||
ActronAirNeoACSystem,
|
||||
ActronNeoAPI,
|
||||
ActronNeoAPIError,
|
||||
ActronNeoAuthError,
|
||||
)
|
||||
|
||||
from homeassistant.const import CONF_API_TOKEN, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import _LOGGER
|
||||
from .coordinator import (
|
||||
ActronAirConfigEntry,
|
||||
ActronAirRuntimeData,
|
||||
ActronAirSystemCoordinator,
|
||||
)
|
||||
|
||||
PLATFORM = [Platform.CLIMATE]
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) -> bool:
|
||||
"""Set up Actron Air integration from a config entry."""
|
||||
|
||||
api = ActronNeoAPI(refresh_token=entry.data[CONF_API_TOKEN])
|
||||
systems: list[ActronAirNeoACSystem] = []
|
||||
|
||||
try:
|
||||
systems = await api.get_ac_systems()
|
||||
await api.update_status()
|
||||
except ActronNeoAuthError:
|
||||
_LOGGER.error("Authentication error while setting up Actron Air integration")
|
||||
raise
|
||||
except ActronNeoAPIError as err:
|
||||
_LOGGER.error("API error while setting up Actron Air integration: %s", err)
|
||||
raise
|
||||
|
||||
system_coordinators: dict[str, ActronAirSystemCoordinator] = {}
|
||||
for system in systems:
|
||||
coordinator = ActronAirSystemCoordinator(hass, entry, api, system)
|
||||
_LOGGER.debug("Setting up coordinator for system: %s", system["serial"])
|
||||
await coordinator.async_config_entry_first_refresh()
|
||||
system_coordinators[system["serial"]] = coordinator
|
||||
|
||||
entry.runtime_data = ActronAirRuntimeData(
|
||||
api=api,
|
||||
system_coordinators=system_coordinators,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORM)
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ActronAirConfigEntry) -> bool:
|
||||
"""Unload a config entry."""
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORM)
|
||||
259
homeassistant/components/actron_air/climate.py
Normal file
259
homeassistant/components/actron_air/climate.py
Normal file
@@ -0,0 +1,259 @@
|
||||
"""Climate platform for Actron Air integration."""
|
||||
|
||||
from typing import Any
|
||||
|
||||
from actron_neo_api import ActronAirNeoStatus, ActronAirNeoZone
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
FAN_AUTO,
|
||||
FAN_HIGH,
|
||||
FAN_LOW,
|
||||
FAN_MEDIUM,
|
||||
ClimateEntity,
|
||||
ClimateEntityFeature,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
||||
from homeassistant.helpers.update_coordinator import CoordinatorEntity
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import ActronAirConfigEntry, ActronAirSystemCoordinator
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
FAN_MODE_MAPPING_ACTRONAIR_TO_HA = {
|
||||
"AUTO": FAN_AUTO,
|
||||
"LOW": FAN_LOW,
|
||||
"MED": FAN_MEDIUM,
|
||||
"HIGH": FAN_HIGH,
|
||||
}
|
||||
FAN_MODE_MAPPING_HA_TO_ACTRONAIR = {
|
||||
v: k for k, v in FAN_MODE_MAPPING_ACTRONAIR_TO_HA.items()
|
||||
}
|
||||
HVAC_MODE_MAPPING_ACTRONAIR_TO_HA = {
|
||||
"COOL": HVACMode.COOL,
|
||||
"HEAT": HVACMode.HEAT,
|
||||
"FAN": HVACMode.FAN_ONLY,
|
||||
"AUTO": HVACMode.AUTO,
|
||||
"OFF": HVACMode.OFF,
|
||||
}
|
||||
HVAC_MODE_MAPPING_HA_TO_ACTRONAIR = {
|
||||
v: k for k, v in HVAC_MODE_MAPPING_ACTRONAIR_TO_HA.items()
|
||||
}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ActronAirConfigEntry,
|
||||
async_add_entities: AddConfigEntryEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up Actron Air climate entities."""
|
||||
system_coordinators = entry.runtime_data.system_coordinators
|
||||
entities: list[ClimateEntity] = []
|
||||
|
||||
for coordinator in system_coordinators.values():
|
||||
status = coordinator.data
|
||||
name = status.ac_system.system_name
|
||||
entities.append(ActronSystemClimate(coordinator, name))
|
||||
|
||||
entities.extend(
|
||||
ActronZoneClimate(coordinator, zone)
|
||||
for zone in status.remote_zone_info
|
||||
if zone.exists
|
||||
)
|
||||
|
||||
async_add_entities(entities)
|
||||
|
||||
|
||||
class BaseClimateEntity(CoordinatorEntity[ActronAirSystemCoordinator], ClimateEntity):
|
||||
"""Base class for Actron Air climate entities."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.FAN_MODE
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
)
|
||||
_attr_name = None
|
||||
_attr_fan_modes = list(FAN_MODE_MAPPING_ACTRONAIR_TO_HA.values())
|
||||
_attr_hvac_modes = list(HVAC_MODE_MAPPING_ACTRONAIR_TO_HA.values())
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: ActronAirSystemCoordinator,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Initialize an Actron Air unit."""
|
||||
super().__init__(coordinator)
|
||||
self._serial_number = coordinator.serial_number
|
||||
|
||||
|
||||
class ActronSystemClimate(BaseClimateEntity):
|
||||
"""Representation of the Actron Air system."""
|
||||
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.FAN_MODE
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: ActronAirSystemCoordinator,
|
||||
name: str,
|
||||
) -> None:
|
||||
"""Initialize an Actron Air unit."""
|
||||
super().__init__(coordinator, name)
|
||||
serial_number = coordinator.serial_number
|
||||
self._attr_unique_id = serial_number
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, serial_number)},
|
||||
name=self._status.ac_system.system_name,
|
||||
manufacturer="Actron Air",
|
||||
model_id=self._status.ac_system.master_wc_model,
|
||||
sw_version=self._status.ac_system.master_wc_firmware_version,
|
||||
serial_number=serial_number,
|
||||
)
|
||||
|
||||
@property
|
||||
def min_temp(self) -> float:
|
||||
"""Return the minimum temperature that can be set."""
|
||||
return self._status.min_temp
|
||||
|
||||
@property
|
||||
def max_temp(self) -> float:
|
||||
"""Return the maximum temperature that can be set."""
|
||||
return self._status.max_temp
|
||||
|
||||
@property
|
||||
def _status(self) -> ActronAirNeoStatus:
|
||||
"""Get the current status from the coordinator."""
|
||||
return self.coordinator.data
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode | None:
|
||||
"""Return the current HVAC mode."""
|
||||
if not self._status.user_aircon_settings.is_on:
|
||||
return HVACMode.OFF
|
||||
|
||||
mode = self._status.user_aircon_settings.mode
|
||||
return HVAC_MODE_MAPPING_ACTRONAIR_TO_HA.get(mode)
|
||||
|
||||
@property
|
||||
def fan_mode(self) -> str | None:
|
||||
"""Return the current fan mode."""
|
||||
fan_mode = self._status.user_aircon_settings.fan_mode
|
||||
return FAN_MODE_MAPPING_ACTRONAIR_TO_HA.get(fan_mode)
|
||||
|
||||
@property
|
||||
def current_humidity(self) -> float:
|
||||
"""Return the current humidity."""
|
||||
return self._status.master_info.live_humidity_pc
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> float:
|
||||
"""Return the current temperature."""
|
||||
return self._status.master_info.live_temp_c
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> float:
|
||||
"""Return the target temperature."""
|
||||
return self._status.user_aircon_settings.temperature_setpoint_cool_c
|
||||
|
||||
async def async_set_fan_mode(self, fan_mode: str) -> None:
|
||||
"""Set a new fan mode."""
|
||||
api_fan_mode = FAN_MODE_MAPPING_HA_TO_ACTRONAIR.get(fan_mode.lower())
|
||||
await self._status.user_aircon_settings.set_fan_mode(api_fan_mode)
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set the HVAC mode."""
|
||||
ac_mode = HVAC_MODE_MAPPING_HA_TO_ACTRONAIR.get(hvac_mode)
|
||||
await self._status.ac_system.set_system_mode(ac_mode)
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set the temperature."""
|
||||
temp = kwargs.get(ATTR_TEMPERATURE)
|
||||
await self._status.user_aircon_settings.set_temperature(temperature=temp)
|
||||
|
||||
|
||||
class ActronZoneClimate(BaseClimateEntity):
|
||||
"""Representation of a zone within the Actron Air system."""
|
||||
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
| ClimateEntityFeature.TURN_ON
|
||||
| ClimateEntityFeature.TURN_OFF
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coordinator: ActronAirSystemCoordinator,
|
||||
zone: ActronAirNeoZone,
|
||||
) -> None:
|
||||
"""Initialize an Actron Air unit."""
|
||||
super().__init__(coordinator, zone.title)
|
||||
serial_number = coordinator.serial_number
|
||||
self._zone_id: int = zone.zone_id
|
||||
self._attr_unique_id: str = f"{serial_number}_zone_{zone.zone_id}"
|
||||
self._attr_device_info: DeviceInfo = DeviceInfo(
|
||||
identifiers={(DOMAIN, self._attr_unique_id)},
|
||||
name=zone.title,
|
||||
manufacturer="Actron Air",
|
||||
model="Zone",
|
||||
suggested_area=zone.title,
|
||||
via_device=(DOMAIN, serial_number),
|
||||
)
|
||||
|
||||
@property
|
||||
def min_temp(self) -> float:
|
||||
"""Return the minimum temperature that can be set."""
|
||||
return self._zone.min_temp
|
||||
|
||||
@property
|
||||
def max_temp(self) -> float:
|
||||
"""Return the maximum temperature that can be set."""
|
||||
return self._zone.max_temp
|
||||
|
||||
@property
|
||||
def _zone(self) -> ActronAirNeoZone:
|
||||
"""Get the current zone data from the coordinator."""
|
||||
status = self.coordinator.data
|
||||
return status.zones[self._zone_id]
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode | None:
|
||||
"""Return the current HVAC mode."""
|
||||
if self._zone.is_active:
|
||||
mode = self._zone.hvac_mode
|
||||
return HVAC_MODE_MAPPING_ACTRONAIR_TO_HA.get(mode)
|
||||
return HVACMode.OFF
|
||||
|
||||
@property
|
||||
def current_humidity(self) -> float | None:
|
||||
"""Return the current humidity."""
|
||||
return self._zone.humidity
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> float | None:
|
||||
"""Return the current temperature."""
|
||||
return self._zone.live_temp_c
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> float | None:
|
||||
"""Return the target temperature."""
|
||||
return self._zone.temperature_setpoint_cool_c
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set the HVAC mode."""
|
||||
is_enabled = hvac_mode != HVACMode.OFF
|
||||
await self._zone.enable(is_enabled)
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
"""Set the temperature."""
|
||||
await self._zone.set_temperature(temperature=kwargs["temperature"])
|
||||
132
homeassistant/components/actron_air/config_flow.py
Normal file
132
homeassistant/components/actron_air/config_flow.py
Normal file
@@ -0,0 +1,132 @@
|
||||
"""Setup config flow for Actron Air integration."""
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from actron_neo_api import ActronNeoAPI, ActronNeoAuthError
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.const import CONF_API_TOKEN
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
|
||||
from .const import _LOGGER, DOMAIN
|
||||
|
||||
|
||||
class ActronAirConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Actron Air."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the config flow."""
|
||||
self._api: ActronNeoAPI | None = None
|
||||
self._device_code: str | None = None
|
||||
self._user_code: str = ""
|
||||
self._verification_uri: str = ""
|
||||
self._expires_minutes: str = "30"
|
||||
self.login_task: asyncio.Task | None = None
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle the initial step."""
|
||||
if self._api is None:
|
||||
_LOGGER.debug("Initiating device authorization")
|
||||
self._api = ActronNeoAPI()
|
||||
try:
|
||||
device_code_response = await self._api.request_device_code()
|
||||
except ActronNeoAuthError as err:
|
||||
_LOGGER.error("OAuth2 flow failed: %s", err)
|
||||
return self.async_abort(reason="oauth2_error")
|
||||
|
||||
self._device_code = device_code_response["device_code"]
|
||||
self._user_code = device_code_response["user_code"]
|
||||
self._verification_uri = device_code_response["verification_uri_complete"]
|
||||
self._expires_minutes = str(device_code_response["expires_in"] // 60)
|
||||
|
||||
async def _wait_for_authorization() -> None:
|
||||
"""Wait for the user to authorize the device."""
|
||||
assert self._api is not None
|
||||
assert self._device_code is not None
|
||||
_LOGGER.debug("Waiting for device authorization")
|
||||
try:
|
||||
await self._api.poll_for_token(self._device_code)
|
||||
_LOGGER.debug("Authorization successful")
|
||||
except ActronNeoAuthError as ex:
|
||||
_LOGGER.exception("Error while waiting for device authorization")
|
||||
raise CannotConnect from ex
|
||||
|
||||
_LOGGER.debug("Checking login task")
|
||||
if self.login_task is None:
|
||||
_LOGGER.debug("Creating task for device authorization")
|
||||
self.login_task = self.hass.async_create_task(_wait_for_authorization())
|
||||
|
||||
if self.login_task.done():
|
||||
_LOGGER.debug("Login task is done, checking results")
|
||||
if exception := self.login_task.exception():
|
||||
if isinstance(exception, CannotConnect):
|
||||
return self.async_show_progress_done(
|
||||
next_step_id="connection_error"
|
||||
)
|
||||
return self.async_show_progress_done(next_step_id="timeout")
|
||||
return self.async_show_progress_done(next_step_id="finish_login")
|
||||
|
||||
return self.async_show_progress(
|
||||
step_id="user",
|
||||
progress_action="wait_for_authorization",
|
||||
description_placeholders={
|
||||
"user_code": self._user_code,
|
||||
"verification_uri": self._verification_uri,
|
||||
"expires_minutes": self._expires_minutes,
|
||||
},
|
||||
progress_task=self.login_task,
|
||||
)
|
||||
|
||||
async def async_step_finish_login(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle the finalization of login."""
|
||||
_LOGGER.debug("Finalizing authorization")
|
||||
assert self._api is not None
|
||||
|
||||
try:
|
||||
user_data = await self._api.get_user_info()
|
||||
except ActronNeoAuthError as err:
|
||||
_LOGGER.error("Error getting user info: %s", err)
|
||||
return self.async_abort(reason="oauth2_error")
|
||||
|
||||
unique_id = str(user_data["id"])
|
||||
await self.async_set_unique_id(unique_id)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return self.async_create_entry(
|
||||
title=user_data["email"],
|
||||
data={CONF_API_TOKEN: self._api.refresh_token_value},
|
||||
)
|
||||
|
||||
async def async_step_timeout(
|
||||
self,
|
||||
user_input: dict[str, Any] | None = None,
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle issues that need transition await from progress step."""
|
||||
if user_input is None:
|
||||
return self.async_show_form(
|
||||
step_id="timeout",
|
||||
)
|
||||
del self.login_task
|
||||
return await self.async_step_user()
|
||||
|
||||
async def async_step_connection_error(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle connection error from progress step."""
|
||||
if user_input is None:
|
||||
return self.async_show_form(step_id="connection_error")
|
||||
|
||||
# Reset state and try again
|
||||
self._api = None
|
||||
self._device_code = None
|
||||
self.login_task = None
|
||||
return await self.async_step_user()
|
||||
|
||||
|
||||
class CannotConnect(HomeAssistantError):
|
||||
"""Error to indicate we cannot connect."""
|
||||
6
homeassistant/components/actron_air/const.py
Normal file
6
homeassistant/components/actron_air/const.py
Normal file
@@ -0,0 +1,6 @@
|
||||
"""Constants used by Actron Air integration."""
|
||||
|
||||
import logging
|
||||
|
||||
_LOGGER = logging.getLogger(__package__)
|
||||
DOMAIN = "actron_air"
|
||||
69
homeassistant/components/actron_air/coordinator.py
Normal file
69
homeassistant/components/actron_air/coordinator.py
Normal file
@@ -0,0 +1,69 @@
|
||||
"""Coordinator for Actron Air integration."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
|
||||
from actron_neo_api import ActronAirNeoACSystem, ActronAirNeoStatus, ActronNeoAPI
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .const import _LOGGER
|
||||
|
||||
STALE_DEVICE_TIMEOUT = timedelta(hours=24)
|
||||
ERROR_NO_SYSTEMS_FOUND = "no_systems_found"
|
||||
ERROR_UNKNOWN = "unknown_error"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ActronAirRuntimeData:
|
||||
"""Runtime data for the Actron Air integration."""
|
||||
|
||||
api: ActronNeoAPI
|
||||
system_coordinators: dict[str, ActronAirSystemCoordinator]
|
||||
|
||||
|
||||
type ActronAirConfigEntry = ConfigEntry[ActronAirRuntimeData]
|
||||
|
||||
AUTH_ERROR_THRESHOLD = 3
|
||||
SCAN_INTERVAL = timedelta(seconds=30)
|
||||
|
||||
|
||||
class ActronAirSystemCoordinator(DataUpdateCoordinator[ActronAirNeoACSystem]):
|
||||
"""System coordinator for Actron Air integration."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
entry: ActronAirConfigEntry,
|
||||
api: ActronNeoAPI,
|
||||
system: ActronAirNeoACSystem,
|
||||
) -> None:
|
||||
"""Initialize the coordinator."""
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name="Actron Air Status",
|
||||
update_interval=SCAN_INTERVAL,
|
||||
config_entry=entry,
|
||||
)
|
||||
self.system = system
|
||||
self.serial_number = system["serial"]
|
||||
self.api = api
|
||||
self.status = self.api.state_manager.get_status(self.serial_number)
|
||||
self.last_seen = dt_util.utcnow()
|
||||
|
||||
async def _async_update_data(self) -> ActronAirNeoStatus:
|
||||
"""Fetch updates and merge incremental changes into the full state."""
|
||||
await self.api.update_status()
|
||||
self.status = self.api.state_manager.get_status(self.serial_number)
|
||||
self.last_seen = dt_util.utcnow()
|
||||
return self.status
|
||||
|
||||
def is_device_stale(self) -> bool:
|
||||
"""Check if a device is stale (not seen for a while)."""
|
||||
return (dt_util.utcnow() - self.last_seen) > STALE_DEVICE_TIMEOUT
|
||||
16
homeassistant/components/actron_air/manifest.json
Normal file
16
homeassistant/components/actron_air/manifest.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"domain": "actron_air",
|
||||
"name": "Actron Air",
|
||||
"codeowners": ["@kclif9", "@JagadishDhanamjayam"],
|
||||
"config_flow": true,
|
||||
"dhcp": [
|
||||
{
|
||||
"hostname": "neo-*",
|
||||
"macaddress": "FC0FE7*"
|
||||
}
|
||||
],
|
||||
"documentation": "https://www.home-assistant.io/integrations/actron_air",
|
||||
"iot_class": "cloud_polling",
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["actron-neo-api==0.1.84"]
|
||||
}
|
||||
78
homeassistant/components/actron_air/quality_scale.yaml
Normal file
78
homeassistant/components/actron_air/quality_scale.yaml
Normal file
@@ -0,0 +1,78 @@
|
||||
rules:
|
||||
# Bronze
|
||||
action-setup:
|
||||
status: exempt
|
||||
comment: This integration does not have custom service actions.
|
||||
appropriate-polling: done
|
||||
brands: done
|
||||
common-modules: done
|
||||
config-flow-test-coverage: done
|
||||
config-flow: done
|
||||
dependency-transparency: done
|
||||
docs-actions:
|
||||
status: exempt
|
||||
comment: This integration does not have custom service actions.
|
||||
docs-high-level-description: done
|
||||
docs-installation-instructions: done
|
||||
docs-removal-instructions: done
|
||||
entity-event-setup:
|
||||
status: exempt
|
||||
comment: This integration does not subscribe to external events.
|
||||
entity-unique-id: done
|
||||
has-entity-name: done
|
||||
runtime-data: done
|
||||
test-before-configure: done
|
||||
test-before-setup: done
|
||||
unique-config-entry: done
|
||||
|
||||
# Silver
|
||||
action-exceptions: todo
|
||||
config-entry-unloading: done
|
||||
docs-configuration-parameters:
|
||||
status: exempt
|
||||
comment: No options flow
|
||||
docs-installation-parameters: done
|
||||
entity-unavailable: done
|
||||
integration-owner: done
|
||||
log-when-unavailable: done
|
||||
parallel-updates: done
|
||||
reauthentication-flow: todo
|
||||
test-coverage: todo
|
||||
|
||||
# Gold
|
||||
devices: done
|
||||
diagnostics: todo
|
||||
discovery-update-info:
|
||||
status: exempt
|
||||
comment: This integration uses DHCP discovery, however is cloud polling. Therefore there is no information to update.
|
||||
discovery: done
|
||||
docs-data-update: done
|
||||
docs-examples: done
|
||||
docs-known-limitations: done
|
||||
docs-supported-devices: done
|
||||
docs-supported-functions: done
|
||||
docs-troubleshooting: done
|
||||
docs-use-cases: done
|
||||
dynamic-devices: todo
|
||||
entity-category:
|
||||
status: exempt
|
||||
comment: This integration does not use entity categories.
|
||||
entity-device-class:
|
||||
status: exempt
|
||||
comment: This integration does not use entity device classes.
|
||||
entity-disabled-by-default:
|
||||
status: exempt
|
||||
comment: Not required for this integration at this stage.
|
||||
entity-translations: todo
|
||||
exception-translations: todo
|
||||
icon-translations: todo
|
||||
reconfiguration-flow: todo
|
||||
repair-issues:
|
||||
status: exempt
|
||||
comment: This integration does not have any known issues that require repair.
|
||||
stale-devices: todo
|
||||
|
||||
# Platinum
|
||||
async-dependency: done
|
||||
inject-websession: todo
|
||||
strict-typing: todo
|
||||
29
homeassistant/components/actron_air/strings.json
Normal file
29
homeassistant/components/actron_air/strings.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
|
||||
"oauth2_error": "Failed to start OAuth2 flow"
|
||||
},
|
||||
"error": {
|
||||
"oauth2_error": "Failed to start OAuth2 flow. Please try again later."
|
||||
},
|
||||
"progress": {
|
||||
"wait_for_authorization": "To authenticate, open the following URL and login at Actron Air:\n{verification_uri}\nIf the code is not automatically copied, paste the following code to authorize the integration:\n\n```{user_code}```\n\n\nThe login attempt will time out after {expires_minutes} minutes."
|
||||
},
|
||||
"step": {
|
||||
"connection_error": {
|
||||
"data": {},
|
||||
"description": "Failed to connect to Actron Air. Please check your internet connection and try again.",
|
||||
"title": "Connection error"
|
||||
},
|
||||
"timeout": {
|
||||
"data": {},
|
||||
"description": "The authorization process timed out. Please try again.",
|
||||
"title": "Authorization timeout"
|
||||
},
|
||||
"user": {
|
||||
"title": "Actron Air OAuth2 Authorization"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,5 +6,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/adax",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["adax", "adax_local"],
|
||||
"requirements": ["adax==0.4.0", "Adax-local==0.1.5"]
|
||||
"requirements": ["adax==0.4.0", "Adax-local==0.2.0"]
|
||||
}
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"connection_type": "Select connection type"
|
||||
},
|
||||
"description": "Select connection type. Local requires heaters with Bluetooth"
|
||||
},
|
||||
"local": {
|
||||
"data": {
|
||||
"wifi_ssid": "Wi-Fi SSID",
|
||||
"wifi_pswd": "Wi-Fi password"
|
||||
},
|
||||
"description": "Reset the heater by pressing + and OK until display shows 'Reset'. Then press and hold OK button on the heater until the blue LED starts blinking before pressing Submit. Configuring heater might take some minutes."
|
||||
},
|
||||
"cloud": {
|
||||
"data": {
|
||||
"account_id": "Account ID",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"heater_not_available": "Heater not available. Try to reset the heater by pressing + and OK for some seconds.",
|
||||
"heater_not_found": "Heater not found. Try to move the heater closer to Home Assistant computer.",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"step": {
|
||||
"cloud": {
|
||||
"data": {
|
||||
"account_id": "Account ID",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
},
|
||||
"local": {
|
||||
"data": {
|
||||
"wifi_pswd": "Wi-Fi password",
|
||||
"wifi_ssid": "Wi-Fi SSID"
|
||||
},
|
||||
"description": "Reset the heater by pressing + and OK until display shows 'Reset'. Then press and hold OK button on the heater until the blue LED starts blinking before pressing Submit. Configuring heater might take some minutes."
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"connection_type": "Select connection type"
|
||||
},
|
||||
"description": "Select connection type. Local requires heaters with Bluetooth"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
{
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"average_processing_speed": {
|
||||
"default": "mdi:speedometer"
|
||||
},
|
||||
"dns_queries": {
|
||||
"default": "mdi:magnify"
|
||||
},
|
||||
@@ -13,21 +16,18 @@
|
||||
"parental_control_blocked": {
|
||||
"default": "mdi:human-male-girl"
|
||||
},
|
||||
"rules_count": {
|
||||
"default": "mdi:counter"
|
||||
},
|
||||
"safe_browsing_blocked": {
|
||||
"default": "mdi:shield-half-full"
|
||||
},
|
||||
"safe_searches_enforced": {
|
||||
"default": "mdi:shield-search"
|
||||
},
|
||||
"average_processing_speed": {
|
||||
"default": "mdi:speedometer"
|
||||
},
|
||||
"rules_count": {
|
||||
"default": "mdi:counter"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
"protection": {
|
||||
"filtering": {
|
||||
"default": "mdi:shield-check",
|
||||
"state": {
|
||||
"off": "mdi:shield-off"
|
||||
@@ -39,7 +39,13 @@
|
||||
"off": "mdi:shield-off"
|
||||
}
|
||||
},
|
||||
"safe_search": {
|
||||
"protection": {
|
||||
"default": "mdi:shield-check",
|
||||
"state": {
|
||||
"off": "mdi:shield-off"
|
||||
}
|
||||
},
|
||||
"query_log": {
|
||||
"default": "mdi:shield-check",
|
||||
"state": {
|
||||
"off": "mdi:shield-off"
|
||||
@@ -51,13 +57,7 @@
|
||||
"off": "mdi:shield-off"
|
||||
}
|
||||
},
|
||||
"filtering": {
|
||||
"default": "mdi:shield-check",
|
||||
"state": {
|
||||
"off": "mdi:shield-off"
|
||||
}
|
||||
},
|
||||
"query_log": {
|
||||
"safe_search": {
|
||||
"default": "mdi:shield-check",
|
||||
"state": {
|
||||
"off": "mdi:shield-off"
|
||||
@@ -69,17 +69,17 @@
|
||||
"add_url": {
|
||||
"service": "mdi:link-plus"
|
||||
},
|
||||
"remove_url": {
|
||||
"service": "mdi:link-off"
|
||||
"disable_url": {
|
||||
"service": "mdi:link-variant-off"
|
||||
},
|
||||
"enable_url": {
|
||||
"service": "mdi:link-variant"
|
||||
},
|
||||
"disable_url": {
|
||||
"service": "mdi:link-variant-off"
|
||||
},
|
||||
"refresh": {
|
||||
"service": "mdi:refresh"
|
||||
},
|
||||
"remove_url": {
|
||||
"service": "mdi:link-off"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,38 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "Set up your AdGuard Home instance to allow monitoring and control.",
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"port": "[%key:common::config_flow::data::port%]",
|
||||
"username": "[%key:common::config_flow::data::username%]",
|
||||
"ssl": "[%key:common::config_flow::data::ssl%]",
|
||||
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The hostname or IP address of the device running your AdGuard Home."
|
||||
}
|
||||
},
|
||||
"hassio_confirm": {
|
||||
"title": "AdGuard Home via Home Assistant add-on",
|
||||
"description": "Do you want to configure Home Assistant to connect to the AdGuard Home provided by the add-on: {addon}?"
|
||||
}
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
|
||||
"existing_instance_updated": "Updated existing configuration."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"abort": {
|
||||
"existing_instance_updated": "Updated existing configuration.",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
"step": {
|
||||
"hassio_confirm": {
|
||||
"description": "Do you want to configure Home Assistant to connect to the AdGuard Home provided by the add-on: {addon}?",
|
||||
"title": "AdGuard Home via Home Assistant add-on"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"port": "[%key:common::config_flow::data::port%]",
|
||||
"ssl": "[%key:common::config_flow::data::ssl%]",
|
||||
"username": "[%key:common::config_flow::data::username%]",
|
||||
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The hostname or IP address of the device running your AdGuard Home."
|
||||
},
|
||||
"description": "Set up your AdGuard Home instance to allow monitoring and control."
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"average_processing_speed": {
|
||||
"name": "Average processing speed"
|
||||
},
|
||||
"dns_queries": {
|
||||
"name": "DNS queries"
|
||||
},
|
||||
@@ -42,94 +45,91 @@
|
||||
"parental_control_blocked": {
|
||||
"name": "Parental control blocked"
|
||||
},
|
||||
"rules_count": {
|
||||
"name": "Rules count"
|
||||
},
|
||||
"safe_browsing_blocked": {
|
||||
"name": "Safe browsing blocked"
|
||||
},
|
||||
"safe_searches_enforced": {
|
||||
"name": "Safe searches enforced"
|
||||
},
|
||||
"average_processing_speed": {
|
||||
"name": "Average processing speed"
|
||||
},
|
||||
"rules_count": {
|
||||
"name": "Rules count"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
"protection": {
|
||||
"name": "Protection"
|
||||
"filtering": {
|
||||
"name": "Filtering"
|
||||
},
|
||||
"parental": {
|
||||
"name": "Parental control"
|
||||
},
|
||||
"safe_search": {
|
||||
"name": "Safe search"
|
||||
"protection": {
|
||||
"name": "Protection"
|
||||
},
|
||||
"query_log": {
|
||||
"name": "Query log"
|
||||
},
|
||||
"safe_browsing": {
|
||||
"name": "Safe browsing"
|
||||
},
|
||||
"filtering": {
|
||||
"name": "Filtering"
|
||||
},
|
||||
"query_log": {
|
||||
"name": "Query log"
|
||||
"safe_search": {
|
||||
"name": "Safe search"
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"add_url": {
|
||||
"name": "Add URL",
|
||||
"description": "Adds a new filter subscription to AdGuard Home.",
|
||||
"fields": {
|
||||
"name": {
|
||||
"name": "[%key:common::config_flow::data::name%]",
|
||||
"description": "The name of the filter subscription."
|
||||
"description": "The name of the filter subscription.",
|
||||
"name": "[%key:common::config_flow::data::name%]"
|
||||
},
|
||||
"url": {
|
||||
"name": "[%key:common::config_flow::data::url%]",
|
||||
"description": "The filter URL to subscribe to, containing the filter rules."
|
||||
"description": "The filter URL to subscribe to, containing the filter rules.",
|
||||
"name": "[%key:common::config_flow::data::url%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"remove_url": {
|
||||
"name": "Remove URL",
|
||||
"description": "Removes a filter subscription from AdGuard Home.",
|
||||
"fields": {
|
||||
"url": {
|
||||
"name": "[%key:common::config_flow::data::url%]",
|
||||
"description": "The filter subscription URL to remove."
|
||||
}
|
||||
}
|
||||
},
|
||||
"enable_url": {
|
||||
"name": "Enable URL",
|
||||
"description": "Enables a filter subscription in AdGuard Home.",
|
||||
"fields": {
|
||||
"url": {
|
||||
"name": "[%key:common::config_flow::data::url%]",
|
||||
"description": "The filter subscription URL to enable."
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Add URL"
|
||||
},
|
||||
"disable_url": {
|
||||
"name": "Disable URL",
|
||||
"description": "Disables a filter subscription in AdGuard Home.",
|
||||
"fields": {
|
||||
"url": {
|
||||
"name": "[%key:common::config_flow::data::url%]",
|
||||
"description": "The filter subscription URL to disable."
|
||||
"description": "The filter subscription URL to disable.",
|
||||
"name": "[%key:common::config_flow::data::url%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Disable URL"
|
||||
},
|
||||
"enable_url": {
|
||||
"description": "Enables a filter subscription in AdGuard Home.",
|
||||
"fields": {
|
||||
"url": {
|
||||
"description": "The filter subscription URL to enable.",
|
||||
"name": "[%key:common::config_flow::data::url%]"
|
||||
}
|
||||
},
|
||||
"name": "Enable URL"
|
||||
},
|
||||
"refresh": {
|
||||
"name": "Refresh",
|
||||
"description": "Refreshes all filter subscriptions in AdGuard Home.",
|
||||
"fields": {
|
||||
"force": {
|
||||
"name": "Force",
|
||||
"description": "Force update (bypasses AdGuard Home throttling), omit for a regular refresh."
|
||||
"description": "Force update (bypasses AdGuard Home throttling), omit for a regular refresh.",
|
||||
"name": "Force"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Refresh"
|
||||
},
|
||||
"remove_url": {
|
||||
"description": "Removes a filter subscription from AdGuard Home.",
|
||||
"fields": {
|
||||
"url": {
|
||||
"description": "The filter subscription URL to remove.",
|
||||
"name": "[%key:common::config_flow::data::url%]"
|
||||
}
|
||||
},
|
||||
"name": "Remove URL"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
{
|
||||
"services": {
|
||||
"write_data_by_name": {
|
||||
"name": "Write data by name",
|
||||
"description": "Write a value to the connected ADS device.",
|
||||
"fields": {
|
||||
"adsvar": {
|
||||
"name": "ADS variable",
|
||||
"description": "The name of the variable to write to."
|
||||
},
|
||||
"adstype": {
|
||||
"name": "ADS type",
|
||||
"description": "The data type of the variable to write to."
|
||||
"description": "The data type of the variable to write to.",
|
||||
"name": "ADS type"
|
||||
},
|
||||
"adsvar": {
|
||||
"description": "The name of the variable to write to.",
|
||||
"name": "ADS variable"
|
||||
},
|
||||
"value": {
|
||||
"name": "Value",
|
||||
"description": "The value to write to the variable."
|
||||
"description": "The value to write to the variable.",
|
||||
"name": "Value"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Write data by name"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"config": {
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
@@ -19,14 +19,14 @@
|
||||
},
|
||||
"services": {
|
||||
"set_time_to": {
|
||||
"name": "Set time to",
|
||||
"description": "Controls timers to turn the system on or off after a set number of minutes.",
|
||||
"fields": {
|
||||
"minutes": {
|
||||
"name": "Minutes",
|
||||
"description": "Minutes until action."
|
||||
"description": "Minutes until action.",
|
||||
"name": "Minutes"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Set time to"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,14 @@ class AemetConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
}
|
||||
)
|
||||
|
||||
return self.async_show_form(step_id="user", data_schema=schema, errors=errors)
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=schema,
|
||||
errors=errors,
|
||||
description_placeholders={
|
||||
"api_key_url": "https://opendata.aemet.es/centrodedescargas/altaUsuario"
|
||||
},
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]",
|
||||
"name": "Name of the integration"
|
||||
},
|
||||
"description": "To generate API key go to https://opendata.aemet.es/centrodedescargas/altaUsuario"
|
||||
"description": "To generate API key go to {api_key_url}"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,57 +1,57 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"add_tracking": {
|
||||
"name": "Add tracking",
|
||||
"description": "Adds a new tracking number to Aftership.",
|
||||
"fields": {
|
||||
"tracking_number": {
|
||||
"name": "Tracking number",
|
||||
"description": "Tracking number for the new tracking."
|
||||
},
|
||||
"title": {
|
||||
"name": "Title",
|
||||
"description": "A custom title for the new tracking."
|
||||
},
|
||||
"slug": {
|
||||
"name": "Slug",
|
||||
"description": "Slug (carrier) of the new tracking."
|
||||
}
|
||||
}
|
||||
},
|
||||
"remove_tracking": {
|
||||
"name": "Remove tracking",
|
||||
"description": "Removes a tracking number from Aftership.",
|
||||
"fields": {
|
||||
"tracking_number": {
|
||||
"name": "[%key:component::aftership::services::add_tracking::fields::tracking_number::name%]",
|
||||
"description": "Tracking number of the tracking to remove."
|
||||
},
|
||||
"slug": {
|
||||
"name": "[%key:component::aftership::services::add_tracking::fields::slug::name%]",
|
||||
"description": "Slug (carrier) of the tracking to remove."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"deprecated_yaml_import_issue_cannot_connect": {
|
||||
"title": "The {integration_title} YAML configuration import failed",
|
||||
"description": "Configuring {integration_title} using YAML is being removed but there was a connection error importing your YAML configuration.\n\nEnsure connection to {integration_title} works and restart Home Assistant to try again or remove the {integration_title} YAML configuration from your configuration.yaml file and continue to [set up the integration]({url}) manually."
|
||||
"description": "Configuring {integration_title} using YAML is being removed but there was a connection error importing your YAML configuration.\n\nEnsure connection to {integration_title} works and restart Home Assistant to try again or remove the {integration_title} YAML configuration from your configuration.yaml file and continue to [set up the integration]({url}) manually.",
|
||||
"title": "The {integration_title} YAML configuration import failed"
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"add_tracking": {
|
||||
"description": "Adds a new tracking number to Aftership.",
|
||||
"fields": {
|
||||
"slug": {
|
||||
"description": "Slug (carrier) of the new tracking.",
|
||||
"name": "Slug"
|
||||
},
|
||||
"title": {
|
||||
"description": "A custom title for the new tracking.",
|
||||
"name": "Title"
|
||||
},
|
||||
"tracking_number": {
|
||||
"description": "Tracking number for the new tracking.",
|
||||
"name": "Tracking number"
|
||||
}
|
||||
},
|
||||
"name": "Add tracking"
|
||||
},
|
||||
"remove_tracking": {
|
||||
"description": "Removes a tracking number from Aftership.",
|
||||
"fields": {
|
||||
"slug": {
|
||||
"description": "Slug (carrier) of the tracking to remove.",
|
||||
"name": "[%key:component::aftership::services::add_tracking::fields::slug::name%]"
|
||||
},
|
||||
"tracking_number": {
|
||||
"description": "Tracking number of the tracking to remove.",
|
||||
"name": "[%key:component::aftership::services::add_tracking::fields::tracking_number::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Remove tracking"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"services": {
|
||||
"disable_alerts": {
|
||||
"service": "mdi:bell-off"
|
||||
},
|
||||
"enable_alerts": {
|
||||
"service": "mdi:bell-alert"
|
||||
},
|
||||
"snapshot": {
|
||||
"service": "mdi:camera"
|
||||
},
|
||||
"start_recording": {
|
||||
"service": "mdi:record-rec"
|
||||
},
|
||||
"stop_recording": {
|
||||
"service": "mdi:stop"
|
||||
},
|
||||
"enable_alerts": {
|
||||
"service": "mdi:bell-alert"
|
||||
},
|
||||
"disable_alerts": {
|
||||
"service": "mdi:bell-off"
|
||||
},
|
||||
"snapshot": {
|
||||
"service": "mdi:camera"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Set up Agent DVR",
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"port": "[%key:common::config_flow::data::port%]"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The IP address of the Agent DVR server."
|
||||
}
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
},
|
||||
"error": {
|
||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"port": "[%key:common::config_flow::data::port%]"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The IP address of the Agent DVR server."
|
||||
},
|
||||
"title": "Set up Agent DVR"
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"start_recording": {
|
||||
"name": "Start recording",
|
||||
"description": "Enables continuous recording."
|
||||
},
|
||||
"stop_recording": {
|
||||
"name": "Stop recording",
|
||||
"description": "Disables continuous recording."
|
||||
"disable_alerts": {
|
||||
"description": "Disables alerts.",
|
||||
"name": "Disable alerts"
|
||||
},
|
||||
"enable_alerts": {
|
||||
"name": "Enable alerts",
|
||||
"description": "Enables alerts."
|
||||
},
|
||||
"disable_alerts": {
|
||||
"name": "Disable alerts",
|
||||
"description": "Disables alerts."
|
||||
"description": "Enables alerts.",
|
||||
"name": "Enable alerts"
|
||||
},
|
||||
"snapshot": {
|
||||
"name": "Snapshot",
|
||||
"description": "Takes a photo."
|
||||
"description": "Takes a photo.",
|
||||
"name": "Snapshot"
|
||||
},
|
||||
"start_recording": {
|
||||
"description": "Enables continuous recording.",
|
||||
"name": "Start recording"
|
||||
},
|
||||
"stop_recording": {
|
||||
"description": "Disables continuous recording.",
|
||||
"name": "Stop recording"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +53,6 @@ __all__ = [
|
||||
"GenImageTaskResult",
|
||||
"async_generate_data",
|
||||
"async_generate_image",
|
||||
"async_setup",
|
||||
"async_setup_entry",
|
||||
"async_unload_entry",
|
||||
]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
{
|
||||
"services": {
|
||||
"generate_data": {
|
||||
"name": "Generate data",
|
||||
"description": "Uses AI to run a task that generates data.",
|
||||
"fields": {
|
||||
"task_name": {
|
||||
"name": "Task name",
|
||||
"description": "Name of the task."
|
||||
},
|
||||
"instructions": {
|
||||
"name": "Instructions",
|
||||
"description": "Instructions on what needs to be done."
|
||||
"attachments": {
|
||||
"description": "List of files to attach for multi-modal AI analysis.",
|
||||
"name": "Attachments"
|
||||
},
|
||||
"entity_id": {
|
||||
"name": "Entity ID",
|
||||
"description": "Entity ID to run the task on. If not provided, the preferred entity will be used."
|
||||
"description": "Entity ID to run the task on. If not provided, the preferred entity will be used.",
|
||||
"name": "Entity ID"
|
||||
},
|
||||
"instructions": {
|
||||
"description": "Instructions on what needs to be done.",
|
||||
"name": "Instructions"
|
||||
},
|
||||
"structure": {
|
||||
"name": "Structured output",
|
||||
"description": "When set, the AI Task will output fields with this in structure. The structure is a dictionary where the keys are the field names and the values contain a 'description', a 'selector', and an optional 'required' field."
|
||||
"description": "When set, the AI Task will output fields with this in structure. The structure is a dictionary where the keys are the field names and the values contain a 'description', a 'selector', and an optional 'required' field.",
|
||||
"name": "Structured output"
|
||||
},
|
||||
"attachments": {
|
||||
"name": "Attachments",
|
||||
"description": "List of files to attach for multi-modal AI analysis."
|
||||
"task_name": {
|
||||
"description": "Name of the task.",
|
||||
"name": "Task name"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Generate data"
|
||||
},
|
||||
"generate_image": {
|
||||
"name": "Generate image",
|
||||
"description": "Uses AI to generate image.",
|
||||
"fields": {
|
||||
"task_name": {
|
||||
"name": "Task name",
|
||||
"description": "Name of the task."
|
||||
},
|
||||
"instructions": {
|
||||
"name": "Instructions",
|
||||
"description": "Instructions that explains the image to be generated."
|
||||
"attachments": {
|
||||
"description": "List of files to attach for using as references.",
|
||||
"name": "Attachments"
|
||||
},
|
||||
"entity_id": {
|
||||
"name": "Entity ID",
|
||||
"description": "Entity ID to run the task on."
|
||||
"description": "Entity ID to run the task on.",
|
||||
"name": "Entity ID"
|
||||
},
|
||||
"attachments": {
|
||||
"name": "Attachments",
|
||||
"description": "List of files to attach for using as references."
|
||||
"instructions": {
|
||||
"description": "Instructions that explains the image to be generated.",
|
||||
"name": "Instructions"
|
||||
},
|
||||
"task_name": {
|
||||
"description": "Name of the task.",
|
||||
"name": "Task name"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Generate image"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,14 +9,17 @@
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"led_bar_brightness": {
|
||||
"display_brightness": {
|
||||
"default": "mdi:brightness-percent"
|
||||
},
|
||||
"display_brightness": {
|
||||
"led_bar_brightness": {
|
||||
"default": "mdi:brightness-percent"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"co2_automatic_baseline_calibration": {
|
||||
"default": "mdi:molecule-co2"
|
||||
},
|
||||
"configuration_control": {
|
||||
"default": "mdi:cloud-cog"
|
||||
},
|
||||
@@ -31,23 +34,11 @@
|
||||
},
|
||||
"voc_index_learning_time_offset": {
|
||||
"default": "mdi:clock-outline"
|
||||
},
|
||||
"co2_automatic_baseline_calibration": {
|
||||
"default": "mdi:molecule-co2"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"total_volatile_organic_component_index": {
|
||||
"default": "mdi:molecule"
|
||||
},
|
||||
"nitrogen_index": {
|
||||
"default": "mdi:molecule"
|
||||
},
|
||||
"pm003_count": {
|
||||
"default": "mdi:blur"
|
||||
},
|
||||
"led_bar_brightness": {
|
||||
"default": "mdi:brightness-percent"
|
||||
"co2_automatic_baseline_calibration": {
|
||||
"default": "mdi:molecule-co2"
|
||||
},
|
||||
"display_brightness": {
|
||||
"default": "mdi:brightness-percent"
|
||||
@@ -55,17 +46,26 @@
|
||||
"display_temperature_unit": {
|
||||
"default": "mdi:thermometer-lines"
|
||||
},
|
||||
"led_bar_brightness": {
|
||||
"default": "mdi:brightness-percent"
|
||||
},
|
||||
"led_bar_mode": {
|
||||
"default": "mdi:led-strip"
|
||||
},
|
||||
"nitrogen_index": {
|
||||
"default": "mdi:molecule"
|
||||
},
|
||||
"nox_index_learning_time_offset": {
|
||||
"default": "mdi:clock-outline"
|
||||
},
|
||||
"pm003_count": {
|
||||
"default": "mdi:blur"
|
||||
},
|
||||
"total_volatile_organic_component_index": {
|
||||
"default": "mdi:molecule"
|
||||
},
|
||||
"voc_index_learning_time_offset": {
|
||||
"default": "mdi:clock-outline"
|
||||
},
|
||||
"co2_automatic_baseline_calibration": {
|
||||
"default": "mdi:molecule-co2"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
{
|
||||
"config": {
|
||||
"flow_title": "{model}",
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The hostname or IP address of the Airgradient device."
|
||||
}
|
||||
},
|
||||
"discovery_confirm": {
|
||||
"description": "Do you want to set up {model}?"
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||
@@ -24,6 +10,20 @@
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"flow_title": "{model}",
|
||||
"step": {
|
||||
"discovery_confirm": {
|
||||
"description": "Do you want to set up {model}?"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The hostname or IP address of the Airgradient device."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
@@ -36,14 +36,25 @@
|
||||
}
|
||||
},
|
||||
"number": {
|
||||
"led_bar_brightness": {
|
||||
"name": "LED bar brightness"
|
||||
},
|
||||
"display_brightness": {
|
||||
"name": "Display brightness"
|
||||
},
|
||||
"led_bar_brightness": {
|
||||
"name": "LED bar brightness"
|
||||
}
|
||||
},
|
||||
"select": {
|
||||
"co2_automatic_baseline_calibration": {
|
||||
"name": "CO2 automatic baseline duration",
|
||||
"state": {
|
||||
"0": "[%key:common::state::off%]",
|
||||
"1": "1 day",
|
||||
"8": "8 days",
|
||||
"30": "30 days",
|
||||
"90": "90 days",
|
||||
"180": "180 days"
|
||||
}
|
||||
},
|
||||
"configuration_control": {
|
||||
"name": "Configuration source",
|
||||
"state": {
|
||||
@@ -51,13 +62,6 @@
|
||||
"local": "Local"
|
||||
}
|
||||
},
|
||||
"display_temperature_unit": {
|
||||
"name": "Display temperature unit",
|
||||
"state": {
|
||||
"c": "Celsius",
|
||||
"f": "Fahrenheit"
|
||||
}
|
||||
},
|
||||
"display_pm_standard": {
|
||||
"name": "Display PM standard",
|
||||
"state": {
|
||||
@@ -65,11 +69,18 @@
|
||||
"us_aqi": "US AQI"
|
||||
}
|
||||
},
|
||||
"display_temperature_unit": {
|
||||
"name": "Display temperature unit",
|
||||
"state": {
|
||||
"c": "Celsius",
|
||||
"f": "Fahrenheit"
|
||||
}
|
||||
},
|
||||
"led_bar_mode": {
|
||||
"name": "LED bar mode",
|
||||
"state": {
|
||||
"off": "[%key:common::state::off%]",
|
||||
"co2": "[%key:component::sensor::entity_component::carbon_dioxide::name%]",
|
||||
"off": "[%key:common::state::off%]",
|
||||
"pm": "Particulate matter"
|
||||
}
|
||||
},
|
||||
@@ -92,37 +103,14 @@
|
||||
"360": "[%key:component::airgradient::entity::select::nox_index_learning_time_offset::state::360%]",
|
||||
"720": "[%key:component::airgradient::entity::select::nox_index_learning_time_offset::state::720%]"
|
||||
}
|
||||
},
|
||||
"co2_automatic_baseline_calibration": {
|
||||
"name": "CO2 automatic baseline duration",
|
||||
"state": {
|
||||
"1": "1 day",
|
||||
"8": "8 days",
|
||||
"30": "30 days",
|
||||
"90": "90 days",
|
||||
"180": "180 days",
|
||||
"0": "[%key:common::state::off%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"total_volatile_organic_component_index": {
|
||||
"name": "VOC index"
|
||||
"co2_automatic_baseline_calibration_days": {
|
||||
"name": "Carbon dioxide automatic baseline calibration"
|
||||
},
|
||||
"nitrogen_index": {
|
||||
"name": "NOx index"
|
||||
},
|
||||
"pm003_count": {
|
||||
"name": "PM0.3"
|
||||
},
|
||||
"raw_total_volatile_organic_component": {
|
||||
"name": "Raw VOC"
|
||||
},
|
||||
"raw_nitrogen": {
|
||||
"name": "Raw NOx"
|
||||
},
|
||||
"raw_pm02": {
|
||||
"name": "Raw PM2.5"
|
||||
"display_brightness": {
|
||||
"name": "[%key:component::airgradient::entity::number::display_brightness::name%]"
|
||||
},
|
||||
"display_pm_standard": {
|
||||
"name": "[%key:component::airgradient::entity::select::display_pm_standard::name%]",
|
||||
@@ -131,26 +119,6 @@
|
||||
"us_aqi": "[%key:component::airgradient::entity::select::display_pm_standard::state::us_aqi%]"
|
||||
}
|
||||
},
|
||||
"co2_automatic_baseline_calibration_days": {
|
||||
"name": "Carbon dioxide automatic baseline calibration"
|
||||
},
|
||||
"nox_learning_offset": {
|
||||
"name": "[%key:component::airgradient::entity::select::nox_index_learning_time_offset::name%]"
|
||||
},
|
||||
"tvoc_learning_offset": {
|
||||
"name": "[%key:component::airgradient::entity::select::voc_index_learning_time_offset::name%]"
|
||||
},
|
||||
"led_bar_mode": {
|
||||
"name": "[%key:component::airgradient::entity::select::led_bar_mode::name%]",
|
||||
"state": {
|
||||
"off": "[%key:common::state::off%]",
|
||||
"co2": "[%key:component::sensor::entity_component::carbon_dioxide::name%]",
|
||||
"pm": "[%key:component::airgradient::entity::select::led_bar_mode::state::pm%]"
|
||||
}
|
||||
},
|
||||
"led_bar_brightness": {
|
||||
"name": "[%key:component::airgradient::entity::number::led_bar_brightness::name%]"
|
||||
},
|
||||
"display_temperature_unit": {
|
||||
"name": "[%key:component::airgradient::entity::select::display_temperature_unit::name%]",
|
||||
"state": {
|
||||
@@ -158,8 +126,40 @@
|
||||
"f": "[%key:component::airgradient::entity::select::display_temperature_unit::state::f%]"
|
||||
}
|
||||
},
|
||||
"display_brightness": {
|
||||
"name": "[%key:component::airgradient::entity::number::display_brightness::name%]"
|
||||
"led_bar_brightness": {
|
||||
"name": "[%key:component::airgradient::entity::number::led_bar_brightness::name%]"
|
||||
},
|
||||
"led_bar_mode": {
|
||||
"name": "[%key:component::airgradient::entity::select::led_bar_mode::name%]",
|
||||
"state": {
|
||||
"co2": "[%key:component::sensor::entity_component::carbon_dioxide::name%]",
|
||||
"off": "[%key:common::state::off%]",
|
||||
"pm": "[%key:component::airgradient::entity::select::led_bar_mode::state::pm%]"
|
||||
}
|
||||
},
|
||||
"nitrogen_index": {
|
||||
"name": "NOx index"
|
||||
},
|
||||
"nox_learning_offset": {
|
||||
"name": "[%key:component::airgradient::entity::select::nox_index_learning_time_offset::name%]"
|
||||
},
|
||||
"pm003_count": {
|
||||
"name": "PM0.3"
|
||||
},
|
||||
"raw_nitrogen": {
|
||||
"name": "Raw NOx"
|
||||
},
|
||||
"raw_pm02": {
|
||||
"name": "Raw PM2.5"
|
||||
},
|
||||
"raw_total_volatile_organic_component": {
|
||||
"name": "Raw VOC"
|
||||
},
|
||||
"total_volatile_organic_component_index": {
|
||||
"name": "VOC index"
|
||||
},
|
||||
"tvoc_learning_offset": {
|
||||
"name": "[%key:component::airgradient::entity::select::voc_index_learning_time_offset::name%]"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
|
||||
@@ -18,6 +18,10 @@ from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import CONF_USE_NEAREST, DOMAIN, NO_AIRLY_SENSORS
|
||||
|
||||
DESCRIPTION_PLACEHOLDERS = {
|
||||
"developer_registration_url": "https://developer.airly.eu/register",
|
||||
}
|
||||
|
||||
|
||||
class AirlyFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
"""Config flow for Airly."""
|
||||
@@ -85,6 +89,7 @@ class AirlyFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
}
|
||||
),
|
||||
errors=errors,
|
||||
description_placeholders=DESCRIPTION_PLACEHOLDERS,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,30 +1,23 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "To generate API key go to https://developer.airly.eu/register",
|
||||
"data": {
|
||||
"name": "[%key:common::config_flow::data::name%]",
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"latitude": "[%key:common::config_flow::data::latitude%]",
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"wrong_location": "No Airly measuring stations in this area.",
|
||||
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]",
|
||||
"wrong_location": "[%key:component::airly::config::error::wrong_location%]"
|
||||
}
|
||||
},
|
||||
"system_health": {
|
||||
"info": {
|
||||
"can_reach_server": "Reach Airly server",
|
||||
"requests_remaining": "Remaining allowed requests",
|
||||
"requests_per_day": "Allowed requests per day"
|
||||
},
|
||||
"error": {
|
||||
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
|
||||
"wrong_location": "No Airly measuring stations in this area."
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"latitude": "[%key:common::config_flow::data::latitude%]",
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]",
|
||||
"name": "[%key:common::config_flow::data::name%]"
|
||||
},
|
||||
"description": "To generate API key go to {developer_registration_url}"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
@@ -38,11 +31,18 @@
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"update_error": {
|
||||
"message": "An error occurred while retrieving data from the Airly API for {entry}: {error}"
|
||||
},
|
||||
"no_station": {
|
||||
"message": "An error occurred while retrieving data from the Airly API for {entry}: no measuring stations in this area"
|
||||
},
|
||||
"update_error": {
|
||||
"message": "An error occurred while retrieving data from the Airly API for {entry}: {error}"
|
||||
}
|
||||
},
|
||||
"system_health": {
|
||||
"info": {
|
||||
"can_reach_server": "Reach Airly server",
|
||||
"requests_per_day": "Allowed requests per day",
|
||||
"requests_remaining": "Remaining allowed requests"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ from .const import DOMAIN
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Documentation URL for API key generation
|
||||
_API_KEY_URL = "https://docs.airnowapi.org/account/request/"
|
||||
|
||||
|
||||
async def validate_input(hass: HomeAssistant, data: dict[str, Any]) -> bool:
|
||||
"""Validate the user input allows us to connect.
|
||||
|
||||
@@ -114,6 +118,7 @@ class AirNowConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
),
|
||||
}
|
||||
),
|
||||
description_placeholders={"api_key_url": _API_KEY_URL},
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
"aqi": {
|
||||
"default": "mdi:blur"
|
||||
},
|
||||
"o3": {
|
||||
"default": "mdi:blur"
|
||||
},
|
||||
"pm10": {
|
||||
"default": "mdi:blur"
|
||||
},
|
||||
"pm25": {
|
||||
"default": "mdi:blur"
|
||||
},
|
||||
"o3": {
|
||||
"default": "mdi:blur"
|
||||
},
|
||||
"station": {
|
||||
"default": "mdi:blur"
|
||||
}
|
||||
|
||||
@@ -1,15 +1,7 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "To generate API key go to https://docs.airnowapi.org/account/request/",
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"latitude": "[%key:common::config_flow::data::latitude%]",
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]",
|
||||
"radius": "Station radius (miles; optional)"
|
||||
}
|
||||
}
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
@@ -17,16 +9,15 @@
|
||||
"invalid_location": "No results found for that location, try changing the location or station radius.",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"user": {
|
||||
"data": {
|
||||
"radius": "Station radius (miles)"
|
||||
}
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"latitude": "[%key:common::config_flow::data::latitude%]",
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]",
|
||||
"radius": "Station radius (miles; optional)"
|
||||
},
|
||||
"description": "To generate API key go to {api_key_url}"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -43,5 +34,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"radius": "Station radius (miles)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from airos.airos8 import AirOS8
|
||||
|
||||
from homeassistant.const import (
|
||||
@@ -12,10 +14,11 @@ from homeassistant.const import (
|
||||
CONF_VERIFY_SSL,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
|
||||
from .const import DEFAULT_SSL, DEFAULT_VERIFY_SSL, SECTION_ADVANCED_SETTINGS
|
||||
from .const import DEFAULT_SSL, DEFAULT_VERIFY_SSL, DOMAIN, SECTION_ADVANCED_SETTINGS
|
||||
from .coordinator import AirOSConfigEntry, AirOSDataUpdateCoordinator
|
||||
|
||||
_PLATFORMS: list[Platform] = [
|
||||
@@ -23,6 +26,8 @@ _PLATFORMS: list[Platform] = [
|
||||
Platform.SENSOR,
|
||||
]
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> bool:
|
||||
"""Set up Ubiquiti airOS from a config entry."""
|
||||
@@ -54,11 +59,13 @@ async def async_setup_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> boo
|
||||
async def async_migrate_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> bool:
|
||||
"""Migrate old config entry."""
|
||||
|
||||
if entry.version > 1:
|
||||
# This means the user has downgraded from a future version
|
||||
# This means the user has downgraded from a future version
|
||||
if entry.version > 2:
|
||||
return False
|
||||
|
||||
# 1.1 Migrate config_entry to add advanced ssl settings
|
||||
if entry.version == 1 and entry.minor_version == 1:
|
||||
new_minor_version = 2
|
||||
new_data = {**entry.data}
|
||||
advanced_data = {
|
||||
CONF_SSL: DEFAULT_SSL,
|
||||
@@ -69,7 +76,52 @@ async def async_migrate_entry(hass: HomeAssistant, entry: AirOSConfigEntry) -> b
|
||||
hass.config_entries.async_update_entry(
|
||||
entry,
|
||||
data=new_data,
|
||||
minor_version=2,
|
||||
minor_version=new_minor_version,
|
||||
)
|
||||
|
||||
# 2.1 Migrate binary_sensor entity unique_id from device_id to mac_address
|
||||
# Step 1 - migrate binary_sensor entity unique_id
|
||||
# Step 2 - migrate device entity identifier
|
||||
if entry.version == 1:
|
||||
new_version = 2
|
||||
new_minor_version = 1
|
||||
|
||||
mac_adress = dr.format_mac(entry.unique_id)
|
||||
|
||||
device_registry = dr.async_get(hass)
|
||||
if device_entry := device_registry.async_get_device(
|
||||
connections={(dr.CONNECTION_NETWORK_MAC, mac_adress)}
|
||||
):
|
||||
old_device_id = next(
|
||||
(
|
||||
device_id
|
||||
for domain, device_id in device_entry.identifiers
|
||||
if domain == DOMAIN
|
||||
),
|
||||
)
|
||||
|
||||
@callback
|
||||
def update_unique_id(
|
||||
entity_entry: er.RegistryEntry,
|
||||
) -> dict[str, str] | None:
|
||||
"""Update unique id from device_id to mac address."""
|
||||
if old_device_id and entity_entry.unique_id.startswith(old_device_id):
|
||||
suffix = entity_entry.unique_id.removeprefix(old_device_id)
|
||||
new_unique_id = f"{mac_adress}{suffix}"
|
||||
return {"new_unique_id": new_unique_id}
|
||||
return None
|
||||
|
||||
await er.async_migrate_entries(hass, entry.entry_id, update_unique_id)
|
||||
|
||||
new_identifiers = device_entry.identifiers.copy()
|
||||
new_identifiers.discard((DOMAIN, old_device_id))
|
||||
new_identifiers.add((DOMAIN, mac_adress))
|
||||
device_registry.async_update_device(
|
||||
device_entry.id, new_identifiers=new_identifiers
|
||||
)
|
||||
|
||||
hass.config_entries.async_update_entry(
|
||||
entry, version=new_version, minor_version=new_minor_version
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@@ -98,7 +98,7 @@ class AirOSBinarySensor(AirOSEntity, BinarySensorEntity):
|
||||
super().__init__(coordinator)
|
||||
|
||||
self.entity_description = description
|
||||
self._attr_unique_id = f"{coordinator.data.host.device_id}_{description.key}"
|
||||
self._attr_unique_id = f"{coordinator.data.derived.mac}_{description.key}"
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
@@ -14,7 +15,12 @@ from airos.exceptions import (
|
||||
)
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
|
||||
from homeassistant.config_entries import (
|
||||
SOURCE_REAUTH,
|
||||
SOURCE_RECONFIGURE,
|
||||
ConfigFlow,
|
||||
ConfigFlowResult,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
CONF_HOST,
|
||||
CONF_PASSWORD,
|
||||
@@ -24,6 +30,11 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.data_entry_flow import section
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.selector import (
|
||||
TextSelector,
|
||||
TextSelectorConfig,
|
||||
TextSelectorType,
|
||||
)
|
||||
|
||||
from .const import DEFAULT_SSL, DEFAULT_VERIFY_SSL, DOMAIN, SECTION_ADVANCED_SETTINGS
|
||||
from .coordinator import AirOS8
|
||||
@@ -51,53 +62,161 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
class AirOSConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Ubiquiti airOS."""
|
||||
|
||||
VERSION = 1
|
||||
MINOR_VERSION = 2
|
||||
VERSION = 2
|
||||
MINOR_VERSION = 1
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the config flow."""
|
||||
super().__init__()
|
||||
self.airos_device: AirOS8
|
||||
self.errors: dict[str, str] = {}
|
||||
|
||||
async def async_step_user(
|
||||
self,
|
||||
user_input: dict[str, Any] | None = None,
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle the initial step."""
|
||||
errors: dict[str, str] = {}
|
||||
"""Handle the manual input of host and credentials."""
|
||||
self.errors = {}
|
||||
if user_input is not None:
|
||||
# By default airOS 8 comes with self-signed SSL certificates,
|
||||
# with no option in the web UI to change or upload a custom certificate.
|
||||
session = async_get_clientsession(
|
||||
self.hass,
|
||||
verify_ssl=user_input[SECTION_ADVANCED_SETTINGS][CONF_VERIFY_SSL],
|
||||
)
|
||||
|
||||
airos_device = AirOS8(
|
||||
host=user_input[CONF_HOST],
|
||||
username=user_input[CONF_USERNAME],
|
||||
password=user_input[CONF_PASSWORD],
|
||||
session=session,
|
||||
use_ssl=user_input[SECTION_ADVANCED_SETTINGS][CONF_SSL],
|
||||
)
|
||||
try:
|
||||
await airos_device.login()
|
||||
airos_data = await airos_device.status()
|
||||
|
||||
except (
|
||||
AirOSConnectionSetupError,
|
||||
AirOSDeviceConnectionError,
|
||||
):
|
||||
errors["base"] = "cannot_connect"
|
||||
except (AirOSConnectionAuthenticationError, AirOSDataMissingError):
|
||||
errors["base"] = "invalid_auth"
|
||||
except AirOSKeyDataMissingError:
|
||||
errors["base"] = "key_data_missing"
|
||||
except Exception:
|
||||
_LOGGER.exception("Unexpected exception")
|
||||
errors["base"] = "unknown"
|
||||
else:
|
||||
await self.async_set_unique_id(airos_data.derived.mac)
|
||||
self._abort_if_unique_id_configured()
|
||||
validated_info = await self._validate_and_get_device_info(user_input)
|
||||
if validated_info:
|
||||
return self.async_create_entry(
|
||||
title=airos_data.host.hostname, data=user_input
|
||||
title=validated_info["title"],
|
||||
data=validated_info["data"],
|
||||
)
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=self.errors
|
||||
)
|
||||
|
||||
async def _validate_and_get_device_info(
|
||||
self, config_data: dict[str, Any]
|
||||
) -> dict[str, Any] | None:
|
||||
"""Validate user input with the device API."""
|
||||
# By default airOS 8 comes with self-signed SSL certificates,
|
||||
# with no option in the web UI to change or upload a custom certificate.
|
||||
session = async_get_clientsession(
|
||||
self.hass,
|
||||
verify_ssl=config_data[SECTION_ADVANCED_SETTINGS][CONF_VERIFY_SSL],
|
||||
)
|
||||
|
||||
airos_device = AirOS8(
|
||||
host=config_data[CONF_HOST],
|
||||
username=config_data[CONF_USERNAME],
|
||||
password=config_data[CONF_PASSWORD],
|
||||
session=session,
|
||||
use_ssl=config_data[SECTION_ADVANCED_SETTINGS][CONF_SSL],
|
||||
)
|
||||
try:
|
||||
await airos_device.login()
|
||||
airos_data = await airos_device.status()
|
||||
|
||||
except (
|
||||
AirOSConnectionSetupError,
|
||||
AirOSDeviceConnectionError,
|
||||
):
|
||||
self.errors["base"] = "cannot_connect"
|
||||
except (AirOSConnectionAuthenticationError, AirOSDataMissingError):
|
||||
self.errors["base"] = "invalid_auth"
|
||||
except AirOSKeyDataMissingError:
|
||||
self.errors["base"] = "key_data_missing"
|
||||
except Exception:
|
||||
_LOGGER.exception("Unexpected exception during credential validation")
|
||||
self.errors["base"] = "unknown"
|
||||
else:
|
||||
await self.async_set_unique_id(airos_data.derived.mac)
|
||||
|
||||
if self.source in [SOURCE_REAUTH, SOURCE_RECONFIGURE]:
|
||||
self._abort_if_unique_id_mismatch()
|
||||
else:
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
return {"title": airos_data.host.hostname, "data": config_data}
|
||||
|
||||
return None
|
||||
|
||||
async def async_step_reauth(
|
||||
self,
|
||||
user_input: Mapping[str, Any],
|
||||
) -> ConfigFlowResult:
|
||||
"""Perform reauthentication upon an API authentication error."""
|
||||
return await self.async_step_reauth_confirm(user_input)
|
||||
|
||||
async def async_step_reauth_confirm(
|
||||
self,
|
||||
user_input: Mapping[str, Any],
|
||||
) -> ConfigFlowResult:
|
||||
"""Perform reauthentication upon an API authentication error."""
|
||||
self.errors = {}
|
||||
|
||||
if user_input:
|
||||
validate_data = {**self._get_reauth_entry().data, **user_input}
|
||||
if await self._validate_and_get_device_info(config_data=validate_data):
|
||||
return self.async_update_reload_and_abort(
|
||||
self._get_reauth_entry(),
|
||||
data_updates=validate_data,
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||
step_id="reauth_confirm",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_PASSWORD): TextSelector(
|
||||
TextSelectorConfig(
|
||||
type=TextSelectorType.PASSWORD,
|
||||
autocomplete="current-password",
|
||||
)
|
||||
),
|
||||
}
|
||||
),
|
||||
errors=self.errors,
|
||||
)
|
||||
|
||||
async def async_step_reconfigure(
|
||||
self,
|
||||
user_input: Mapping[str, Any] | None = None,
|
||||
) -> ConfigFlowResult:
|
||||
"""Handle reconfiguration of airOS."""
|
||||
self.errors = {}
|
||||
entry = self._get_reconfigure_entry()
|
||||
current_data = entry.data
|
||||
|
||||
if user_input is not None:
|
||||
validate_data = {**current_data, **user_input}
|
||||
if await self._validate_and_get_device_info(config_data=validate_data):
|
||||
return self.async_update_reload_and_abort(
|
||||
entry,
|
||||
data_updates=validate_data,
|
||||
)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="reconfigure",
|
||||
data_schema=vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_PASSWORD): TextSelector(
|
||||
TextSelectorConfig(
|
||||
type=TextSelectorType.PASSWORD,
|
||||
autocomplete="current-password",
|
||||
)
|
||||
),
|
||||
vol.Required(SECTION_ADVANCED_SETTINGS): section(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Required(
|
||||
CONF_SSL,
|
||||
default=current_data[SECTION_ADVANCED_SETTINGS][
|
||||
CONF_SSL
|
||||
],
|
||||
): bool,
|
||||
vol.Required(
|
||||
CONF_VERIFY_SSL,
|
||||
default=current_data[SECTION_ADVANCED_SETTINGS][
|
||||
CONF_VERIFY_SSL
|
||||
],
|
||||
): bool,
|
||||
}
|
||||
),
|
||||
{"collapsed": True},
|
||||
),
|
||||
}
|
||||
),
|
||||
errors=self.errors,
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ from airos.exceptions import (
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryError
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
|
||||
from .const import DOMAIN, SCAN_INTERVAL
|
||||
@@ -47,9 +47,9 @@ class AirOSDataUpdateCoordinator(DataUpdateCoordinator[AirOS8Data]):
|
||||
try:
|
||||
await self.airos_device.login()
|
||||
return await self.airos_device.status()
|
||||
except (AirOSConnectionAuthenticationError,) as err:
|
||||
except AirOSConnectionAuthenticationError as err:
|
||||
_LOGGER.exception("Error authenticating with airOS device")
|
||||
raise ConfigEntryError(
|
||||
raise ConfigEntryAuthFailed(
|
||||
translation_domain=DOMAIN, translation_key="invalid_auth"
|
||||
) from err
|
||||
except (
|
||||
|
||||
@@ -33,9 +33,14 @@ class AirOSEntity(CoordinatorEntity[AirOSDataUpdateCoordinator]):
|
||||
self._attr_device_info = DeviceInfo(
|
||||
connections={(CONNECTION_NETWORK_MAC, airos_data.derived.mac)},
|
||||
configuration_url=configuration_url,
|
||||
identifiers={(DOMAIN, str(airos_data.host.device_id))},
|
||||
identifiers={(DOMAIN, airos_data.derived.mac)},
|
||||
manufacturer=MANUFACTURER,
|
||||
model=airos_data.host.devmodel,
|
||||
model_id=(
|
||||
sku
|
||||
if (sku := airos_data.derived.sku) not in ["UNKNOWN", "AMBIGUOUS"]
|
||||
else None
|
||||
),
|
||||
name=airos_data.host.hostname,
|
||||
sw_version=airos_data.host.fwversion,
|
||||
)
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
"codeowners": ["@CoMPaTech"],
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/airos",
|
||||
"integration_type": "device",
|
||||
"iot_class": "local_polling",
|
||||
"quality_scale": "bronze",
|
||||
"requirements": ["airos==0.5.5"]
|
||||
"quality_scale": "silver",
|
||||
"requirements": ["airos==0.6.0"]
|
||||
}
|
||||
|
||||
@@ -32,11 +32,11 @@ rules:
|
||||
config-entry-unloading: done
|
||||
docs-configuration-parameters: done
|
||||
docs-installation-parameters: done
|
||||
entity-unavailable: todo
|
||||
entity-unavailable: done
|
||||
integration-owner: done
|
||||
log-when-unavailable: todo
|
||||
parallel-updates: todo
|
||||
reauthentication-flow: todo
|
||||
log-when-unavailable: done
|
||||
parallel-updates: done
|
||||
reauthentication-flow: done
|
||||
test-coverage: done
|
||||
|
||||
# Gold
|
||||
@@ -48,9 +48,9 @@ rules:
|
||||
docs-examples: todo
|
||||
docs-known-limitations: done
|
||||
docs-supported-devices: done
|
||||
docs-supported-functions: todo
|
||||
docs-supported-functions: done
|
||||
docs-troubleshooting: done
|
||||
docs-use-cases: todo
|
||||
docs-use-cases: done
|
||||
dynamic-devices: todo
|
||||
entity-category: done
|
||||
entity-device-class: done
|
||||
@@ -60,7 +60,7 @@ rules:
|
||||
icon-translations:
|
||||
status: exempt
|
||||
comment: no (custom) icons used or envisioned
|
||||
reconfiguration-flow: todo
|
||||
reconfiguration-flow: done
|
||||
repair-issues: todo
|
||||
stale-devices: todo
|
||||
|
||||
|
||||
@@ -1,17 +1,58 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||
"reconfigure_successful": "[%key:common::config_flow::abort::reconfigure_successful%]",
|
||||
"unique_id_mismatch": "Re-authentication should be used for the same device not a new one"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"key_data_missing": "Expected data not returned from the device, check the documentation for supported devices",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"flow_title": "Ubiquiti airOS device",
|
||||
"step": {
|
||||
"user": {
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"username": "[%key:common::config_flow::data::username%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
},
|
||||
"data_description": {
|
||||
"password": "[%key:component::airos::config::step::user::data_description::password%]"
|
||||
}
|
||||
},
|
||||
"reconfigure": {
|
||||
"data": {
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
},
|
||||
"data_description": {
|
||||
"password": "[%key:component::airos::config::step::user::data_description::password%]"
|
||||
},
|
||||
"sections": {
|
||||
"advanced_settings": {
|
||||
"data": {
|
||||
"ssl": "[%key:component::airos::config::step::user::sections::advanced_settings::data::ssl%]",
|
||||
"verify_ssl": "[%key:common::config_flow::data::verify_ssl%]"
|
||||
},
|
||||
"data_description": {
|
||||
"ssl": "[%key:component::airos::config::step::user::sections::advanced_settings::data_description::ssl%]",
|
||||
"verify_ssl": "[%key:component::airos::config::step::user::sections::advanced_settings::data_description::verify_ssl%]"
|
||||
},
|
||||
"name": "[%key:component::airos::config::step::user::sections::advanced_settings::name%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"username": "[%key:common::config_flow::data::username%]"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "IP address or hostname of the airOS device",
|
||||
"username": "Administrator username for the airOS device, normally 'ubnt'",
|
||||
"password": "Password configured through the UISP app or web interface"
|
||||
"password": "Password configured through the UISP app or web interface",
|
||||
"username": "Administrator username for the airOS device, normally 'ubnt'"
|
||||
},
|
||||
"sections": {
|
||||
"advanced_settings": {
|
||||
@@ -22,25 +63,17 @@
|
||||
"data_description": {
|
||||
"ssl": "Whether the connection should be encrypted (required for most devices)",
|
||||
"verify_ssl": "Whether the certificate should be verified when using HTTPS. This should be off for self-signed certificates"
|
||||
}
|
||||
},
|
||||
"name": "Advanced settings"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"key_data_missing": "Expected data not returned from the device, check the documentation for supported devices",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"port_forwarding": {
|
||||
"name": "Port forwarding"
|
||||
"dhcp6_server": {
|
||||
"name": "DHCPv6 server"
|
||||
},
|
||||
"dhcp_client": {
|
||||
"name": "DHCP client"
|
||||
@@ -48,8 +81,8 @@
|
||||
"dhcp_server": {
|
||||
"name": "DHCP server"
|
||||
},
|
||||
"dhcp6_server": {
|
||||
"name": "DHCPv6 server"
|
||||
"port_forwarding": {
|
||||
"name": "Port forwarding"
|
||||
},
|
||||
"pppoe": {
|
||||
"name": "PPPoE link"
|
||||
@@ -66,20 +99,27 @@
|
||||
"router": "Router"
|
||||
}
|
||||
},
|
||||
"wireless_frequency": {
|
||||
"name": "Wireless frequency"
|
||||
},
|
||||
"wireless_essid": {
|
||||
"name": "Wireless SSID"
|
||||
"host_uptime": {
|
||||
"name": "Uptime"
|
||||
},
|
||||
"wireless_antenna_gain": {
|
||||
"name": "Antenna gain"
|
||||
},
|
||||
"wireless_throughput_tx": {
|
||||
"name": "Throughput transmit (actual)"
|
||||
"wireless_distance": {
|
||||
"name": "Wireless distance"
|
||||
},
|
||||
"wireless_throughput_rx": {
|
||||
"name": "Throughput receive (actual)"
|
||||
"wireless_essid": {
|
||||
"name": "Wireless SSID"
|
||||
},
|
||||
"wireless_frequency": {
|
||||
"name": "Wireless frequency"
|
||||
},
|
||||
"wireless_mode": {
|
||||
"name": "Wireless mode",
|
||||
"state": {
|
||||
"point_to_multipoint": "Point-to-multipoint",
|
||||
"point_to_point": "Point-to-point"
|
||||
}
|
||||
},
|
||||
"wireless_polling_dl_capacity": {
|
||||
"name": "Download capacity"
|
||||
@@ -90,12 +130,6 @@
|
||||
"wireless_remote_hostname": {
|
||||
"name": "Remote hostname"
|
||||
},
|
||||
"host_uptime": {
|
||||
"name": "Uptime"
|
||||
},
|
||||
"wireless_distance": {
|
||||
"name": "Wireless distance"
|
||||
},
|
||||
"wireless_role": {
|
||||
"name": "Wireless role",
|
||||
"state": {
|
||||
@@ -103,27 +137,26 @@
|
||||
"station": "Station"
|
||||
}
|
||||
},
|
||||
"wireless_mode": {
|
||||
"name": "Wireless mode",
|
||||
"state": {
|
||||
"point_to_point": "Point-to-point",
|
||||
"point_to_multipoint": "Point-to-multipoint"
|
||||
}
|
||||
"wireless_throughput_rx": {
|
||||
"name": "Throughput receive (actual)"
|
||||
},
|
||||
"wireless_throughput_tx": {
|
||||
"name": "Throughput transmit (actual)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"invalid_auth": {
|
||||
"message": "[%key:common::config_flow::error::invalid_auth%]"
|
||||
},
|
||||
"cannot_connect": {
|
||||
"message": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"key_data_missing": {
|
||||
"message": "Key data not returned from device"
|
||||
},
|
||||
"error_data_missing": {
|
||||
"message": "Data incomplete or missing"
|
||||
},
|
||||
"invalid_auth": {
|
||||
"message": "[%key:common::config_flow::error::invalid_auth%]"
|
||||
},
|
||||
"key_data_missing": {
|
||||
"message": "Key data not returned from device"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,36 +1,21 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Identify the device",
|
||||
"description": "Provide the IP address or mDNS of the device and its password",
|
||||
"data": {
|
||||
"ip_address": "[%key:common::config_flow::data::ip%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
}
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"invalid_input": "[%key:common::config_flow::error::invalid_host%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "Configure air-Q integration",
|
||||
"user": {
|
||||
"data": {
|
||||
"return_average": "Show values averaged by the device",
|
||||
"clip_negatives": "Clip negative values"
|
||||
"ip_address": "[%key:common::config_flow::data::ip%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
},
|
||||
"data_description": {
|
||||
"return_average": "air-Q allows to poll both the noisy sensor readings as well as the values averaged on the device (default)",
|
||||
"clip_negatives": "For baseline calibration purposes, certain sensor values may briefly become negative. The default behaviour is to clip such values to 0"
|
||||
}
|
||||
"description": "Provide the IP address or mDNS of the device and its password",
|
||||
"title": "Identify the device"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -53,8 +38,11 @@
|
||||
"bromine": {
|
||||
"name": "Bromine"
|
||||
},
|
||||
"methanethiol": {
|
||||
"name": "Methanethiol"
|
||||
"carbon_disulfide": {
|
||||
"name": "Carbon disulfide"
|
||||
},
|
||||
"carbon_monoxide": {
|
||||
"name": "[%key:component::sensor::entity_component::carbon_monoxide::name%]"
|
||||
},
|
||||
"chlorine": {
|
||||
"name": "Chlorine"
|
||||
@@ -62,12 +50,6 @@
|
||||
"chlorine_dioxide": {
|
||||
"name": "Chlorine dioxide"
|
||||
},
|
||||
"carbon_disulfide": {
|
||||
"name": "Carbon disulfide"
|
||||
},
|
||||
"carbon_monoxide": {
|
||||
"name": "[%key:component::sensor::entity_component::carbon_monoxide::name%]"
|
||||
},
|
||||
"dew_point": {
|
||||
"name": "Dew point"
|
||||
},
|
||||
@@ -77,36 +59,51 @@
|
||||
"ethylene": {
|
||||
"name": "Ethylene"
|
||||
},
|
||||
"formaldehyde": {
|
||||
"name": "Formaldehyde"
|
||||
},
|
||||
"fluorine": {
|
||||
"name": "Fluorine"
|
||||
},
|
||||
"hydrogen_sulfide": {
|
||||
"name": "Hydrogen sulfide"
|
||||
"formaldehyde": {
|
||||
"name": "Formaldehyde"
|
||||
},
|
||||
"health_index": {
|
||||
"name": "Health index"
|
||||
},
|
||||
"hydrochloric_acid": {
|
||||
"name": "Hydrochloric acid"
|
||||
},
|
||||
"hydrogen": {
|
||||
"name": "Hydrogen"
|
||||
},
|
||||
"hydrogen_cyanide": {
|
||||
"name": "Hydrogen cyanide"
|
||||
},
|
||||
"hydrogen_fluoride": {
|
||||
"name": "Hydrogen fluoride"
|
||||
},
|
||||
"health_index": {
|
||||
"name": "Health index"
|
||||
},
|
||||
"hydrogen": {
|
||||
"name": "Hydrogen"
|
||||
},
|
||||
"hydrogen_peroxide": {
|
||||
"name": "Hydrogen peroxide"
|
||||
},
|
||||
"hydrogen_phosphide": {
|
||||
"name": "Hydrogen phosphide"
|
||||
},
|
||||
"hydrogen_sulfide": {
|
||||
"name": "Hydrogen sulfide"
|
||||
},
|
||||
"industrial_volatile_organic_compounds": {
|
||||
"name": "VOCs (industrial)"
|
||||
},
|
||||
"maximum_noise": {
|
||||
"name": "Noise (maximum)"
|
||||
},
|
||||
"methane": {
|
||||
"name": "Methane"
|
||||
},
|
||||
"methanethiol": {
|
||||
"name": "Methanethiol"
|
||||
},
|
||||
"noise": {
|
||||
"name": "Noise"
|
||||
},
|
||||
"organic_acid": {
|
||||
"name": "Organic acid"
|
||||
},
|
||||
@@ -116,36 +113,39 @@
|
||||
"performance_index": {
|
||||
"name": "Performance index"
|
||||
},
|
||||
"hydrogen_phosphide": {
|
||||
"name": "Hydrogen phosphide"
|
||||
},
|
||||
"relative_pressure": {
|
||||
"name": "Relative pressure"
|
||||
},
|
||||
"propane": {
|
||||
"name": "Propane"
|
||||
},
|
||||
"refigerant": {
|
||||
"name": "Refrigerant"
|
||||
},
|
||||
"silicon_hydride": {
|
||||
"name": "Silicon hydride"
|
||||
},
|
||||
"noise": {
|
||||
"name": "Noise"
|
||||
},
|
||||
"maximum_noise": {
|
||||
"name": "Noise (maximum)"
|
||||
},
|
||||
"radon": {
|
||||
"name": "Radon"
|
||||
},
|
||||
"industrial_volatile_organic_compounds": {
|
||||
"name": "VOCs (industrial)"
|
||||
"refigerant": {
|
||||
"name": "Refrigerant"
|
||||
},
|
||||
"relative_pressure": {
|
||||
"name": "Relative pressure"
|
||||
},
|
||||
"silicon_hydride": {
|
||||
"name": "Silicon hydride"
|
||||
},
|
||||
"virus_index": {
|
||||
"name": "Virus index"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"clip_negatives": "Clip negative values",
|
||||
"return_average": "Show values averaged by the device"
|
||||
},
|
||||
"data_description": {
|
||||
"clip_negatives": "For baseline calibration purposes, certain sensor values may briefly become negative. The default behavior is to clip such values to 0",
|
||||
"return_average": "air-Q allows to poll both the noisy sensor readings as well as the values averaged on the device (default)"
|
||||
},
|
||||
"title": "Configure air-Q integration"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ STEP_USER_DATA_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
URL_API_INTEGRATION = {
|
||||
"url": "https://dashboard.airthings.com/integrations/api-integration"
|
||||
}
|
||||
|
||||
|
||||
class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Airthings."""
|
||||
@@ -37,11 +41,7 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=STEP_USER_DATA_SCHEMA,
|
||||
description_placeholders={
|
||||
"url": (
|
||||
"https://dashboard.airthings.com/integrations/api-integration"
|
||||
),
|
||||
},
|
||||
description_placeholders=URL_API_INTEGRATION,
|
||||
)
|
||||
|
||||
errors = {}
|
||||
@@ -65,5 +65,8 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
return self.async_create_entry(title="Airthings", data=user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="user", data_schema=STEP_USER_DATA_SCHEMA, errors=errors
|
||||
step_id="user",
|
||||
data_schema=STEP_USER_DATA_SCHEMA,
|
||||
errors=errors,
|
||||
description_placeholders=URL_API_INTEGRATION,
|
||||
)
|
||||
|
||||
@@ -1,36 +1,36 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "ID",
|
||||
"secret": "Secret",
|
||||
"description": "Login at {url} to find your credentials"
|
||||
}
|
||||
}
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]"
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "ID",
|
||||
"secret": "Secret"
|
||||
},
|
||||
"description": "Log in at {url} to find your credentials"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"radon": {
|
||||
"name": "Radon"
|
||||
},
|
||||
"light": {
|
||||
"name": "Light"
|
||||
},
|
||||
"virus_risk": {
|
||||
"name": "Virus Risk"
|
||||
},
|
||||
"mold": {
|
||||
"name": "Mold"
|
||||
},
|
||||
"radon": {
|
||||
"name": "Radon"
|
||||
},
|
||||
"virus_risk": {
|
||||
"name": "Virus Risk"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,13 @@ import dataclasses
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from airthings_ble import AirthingsBluetoothDeviceData, AirthingsDevice
|
||||
from airthings_ble import (
|
||||
AirthingsBluetoothDeviceData,
|
||||
AirthingsDevice,
|
||||
UnsupportedDeviceError,
|
||||
)
|
||||
from bleak import BleakError
|
||||
from habluetooth import BluetoothServiceInfoBleak
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components import bluetooth
|
||||
@@ -27,6 +32,7 @@ SERVICE_UUIDS = [
|
||||
"b42e4a8e-ade7-11e4-89d3-123b93f75cba",
|
||||
"b42e1c08-ade7-11e4-89d3-123b93f75cba",
|
||||
"b42e3882-ade7-11e4-89d3-123b93f75cba",
|
||||
"b42e90a2-ade7-11e4-89d3-123b93f75cba",
|
||||
]
|
||||
|
||||
|
||||
@@ -37,6 +43,7 @@ class Discovery:
|
||||
name: str
|
||||
discovery_info: BluetoothServiceInfo
|
||||
device: AirthingsDevice
|
||||
data: AirthingsBluetoothDeviceData
|
||||
|
||||
|
||||
def get_name(device: AirthingsDevice) -> str:
|
||||
@@ -44,7 +51,7 @@ def get_name(device: AirthingsDevice) -> str:
|
||||
|
||||
name = device.friendly_name()
|
||||
if identifier := device.identifier:
|
||||
name += f" ({identifier})"
|
||||
name += f" ({device.model.value}{identifier})"
|
||||
return name
|
||||
|
||||
|
||||
@@ -62,8 +69,8 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self._discovered_device: Discovery | None = None
|
||||
self._discovered_devices: dict[str, Discovery] = {}
|
||||
|
||||
async def _get_device_data(
|
||||
self, discovery_info: BluetoothServiceInfo
|
||||
async def _get_device(
|
||||
self, data: AirthingsBluetoothDeviceData, discovery_info: BluetoothServiceInfo
|
||||
) -> AirthingsDevice:
|
||||
ble_device = bluetooth.async_ble_device_from_address(
|
||||
self.hass, discovery_info.address
|
||||
@@ -72,10 +79,8 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
_LOGGER.debug("no ble_device in _get_device_data")
|
||||
raise AirthingsDeviceUpdateError("No ble_device")
|
||||
|
||||
airthings = AirthingsBluetoothDeviceData(_LOGGER)
|
||||
|
||||
try:
|
||||
data = await airthings.update_device(ble_device)
|
||||
device = await data.update_device(ble_device)
|
||||
except BleakError as err:
|
||||
_LOGGER.error(
|
||||
"Error connecting to and getting data from %s: %s",
|
||||
@@ -83,12 +88,15 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
err,
|
||||
)
|
||||
raise AirthingsDeviceUpdateError("Failed getting device data") from err
|
||||
except UnsupportedDeviceError:
|
||||
_LOGGER.debug("Skipping unsupported device: %s", discovery_info.name)
|
||||
raise
|
||||
except Exception as err:
|
||||
_LOGGER.error(
|
||||
"Unknown error occurred from %s: %s", discovery_info.address, err
|
||||
)
|
||||
raise
|
||||
return data
|
||||
return device
|
||||
|
||||
async def async_step_bluetooth(
|
||||
self, discovery_info: BluetoothServiceInfo
|
||||
@@ -98,17 +106,21 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
await self.async_set_unique_id(discovery_info.address)
|
||||
self._abort_if_unique_id_configured()
|
||||
|
||||
data = AirthingsBluetoothDeviceData(logger=_LOGGER)
|
||||
|
||||
try:
|
||||
device = await self._get_device_data(discovery_info)
|
||||
device = await self._get_device(data=data, discovery_info=discovery_info)
|
||||
except AirthingsDeviceUpdateError:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
except UnsupportedDeviceError:
|
||||
return self.async_abort(reason="unsupported_device")
|
||||
except Exception:
|
||||
_LOGGER.exception("Unknown error occurred")
|
||||
return self.async_abort(reason="unknown")
|
||||
|
||||
name = get_name(device)
|
||||
self.context["title_placeholders"] = {"name": name}
|
||||
self._discovered_device = Discovery(name, discovery_info, device)
|
||||
self._discovered_device = Discovery(name, discovery_info, device, data=data)
|
||||
|
||||
return await self.async_step_bluetooth_confirm()
|
||||
|
||||
@@ -117,6 +129,12 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
) -> ConfigFlowResult:
|
||||
"""Confirm discovery."""
|
||||
if user_input is not None:
|
||||
if (
|
||||
self._discovered_device is not None
|
||||
and self._discovered_device.device.firmware.need_firmware_upgrade
|
||||
):
|
||||
return self.async_abort(reason="firmware_upgrade_required")
|
||||
|
||||
return self.async_create_entry(
|
||||
title=self.context["title_placeholders"]["name"], data={}
|
||||
)
|
||||
@@ -137,6 +155,9 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
self._abort_if_unique_id_configured()
|
||||
discovery = self._discovered_devices[address]
|
||||
|
||||
if discovery.device.firmware.need_firmware_upgrade:
|
||||
return self.async_abort(reason="firmware_upgrade_required")
|
||||
|
||||
self.context["title_placeholders"] = {
|
||||
"name": discovery.name,
|
||||
}
|
||||
@@ -146,32 +167,53 @@ class AirthingsConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
return self.async_create_entry(title=discovery.name, data={})
|
||||
|
||||
current_addresses = self._async_current_ids(include_ignore=False)
|
||||
devices: list[BluetoothServiceInfoBleak] = []
|
||||
for discovery_info in async_discovered_service_info(self.hass):
|
||||
address = discovery_info.address
|
||||
if address in current_addresses or address in self._discovered_devices:
|
||||
continue
|
||||
|
||||
if MFCT_ID not in discovery_info.manufacturer_data:
|
||||
continue
|
||||
|
||||
if not any(uuid in SERVICE_UUIDS for uuid in discovery_info.service_uuids):
|
||||
_LOGGER.debug(
|
||||
"Skipping unsupported device: %s (%s)", discovery_info.name, address
|
||||
)
|
||||
continue
|
||||
devices.append(discovery_info)
|
||||
|
||||
for discovery_info in devices:
|
||||
address = discovery_info.address
|
||||
data = AirthingsBluetoothDeviceData(logger=_LOGGER)
|
||||
try:
|
||||
device = await self._get_device_data(discovery_info)
|
||||
device = await self._get_device(data, discovery_info)
|
||||
except AirthingsDeviceUpdateError:
|
||||
return self.async_abort(reason="cannot_connect")
|
||||
_LOGGER.error(
|
||||
"Error connecting to and getting data from %s (%s)",
|
||||
discovery_info.name,
|
||||
discovery_info.address,
|
||||
)
|
||||
continue
|
||||
except UnsupportedDeviceError:
|
||||
_LOGGER.debug(
|
||||
"Skipping unsupported device: %s (%s)",
|
||||
discovery_info.name,
|
||||
discovery_info.address,
|
||||
)
|
||||
continue
|
||||
except Exception:
|
||||
_LOGGER.exception("Unknown error occurred")
|
||||
return self.async_abort(reason="unknown")
|
||||
name = get_name(device)
|
||||
self._discovered_devices[address] = Discovery(name, discovery_info, device)
|
||||
_LOGGER.debug("Discovered Airthings device: %s (%s)", name, address)
|
||||
self._discovered_devices[address] = Discovery(
|
||||
name, discovery_info, device, data
|
||||
)
|
||||
|
||||
if not self._discovered_devices:
|
||||
return self.async_abort(reason="no_devices_found")
|
||||
|
||||
titles = {
|
||||
address: discovery.device.name
|
||||
address: get_name(discovery.device)
|
||||
for (address, discovery) in self._discovered_devices.items()
|
||||
}
|
||||
return self.async_show_form(
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
"radon_1day_avg": {
|
||||
"default": "mdi:radioactive"
|
||||
},
|
||||
"radon_longterm_avg": {
|
||||
"radon_1day_level": {
|
||||
"default": "mdi:radioactive"
|
||||
},
|
||||
"radon_1day_level": {
|
||||
"radon_longterm_avg": {
|
||||
"default": "mdi:radioactive"
|
||||
},
|
||||
"radon_longterm_level": {
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
{
|
||||
"manufacturer_id": 820,
|
||||
"service_uuid": "b42e3882-ade7-11e4-89d3-123b93f75cba"
|
||||
},
|
||||
{
|
||||
"manufacturer_id": 820,
|
||||
"service_uuid": "b42e90a2-ade7-11e4-89d3-123b93f75cba"
|
||||
}
|
||||
],
|
||||
"codeowners": ["@vincegio", "@LaStrada"],
|
||||
@@ -24,5 +28,5 @@
|
||||
"dependencies": ["bluetooth_adapters"],
|
||||
"documentation": "https://www.home-assistant.io/integrations/airthings_ble",
|
||||
"iot_class": "local_polling",
|
||||
"requirements": ["airthings-ble==0.9.2"]
|
||||
"requirements": ["airthings-ble==1.1.1"]
|
||||
}
|
||||
|
||||
@@ -16,10 +16,12 @@ from homeassistant.components.sensor import (
|
||||
from homeassistant.const import (
|
||||
CONCENTRATION_PARTS_PER_BILLION,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
LIGHT_LUX,
|
||||
PERCENTAGE,
|
||||
EntityCategory,
|
||||
Platform,
|
||||
UnitOfPressure,
|
||||
UnitOfSoundPressure,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
@@ -112,8 +114,25 @@ SENSORS_MAPPING_TEMPLATE: dict[str, SensorEntityDescription] = {
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
),
|
||||
"lux": SensorEntityDescription(
|
||||
key="lux",
|
||||
device_class=SensorDeviceClass.ILLUMINANCE,
|
||||
native_unit_of_measurement=LIGHT_LUX,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
),
|
||||
"noise": SensorEntityDescription(
|
||||
key="noise",
|
||||
translation_key="ambient_noise",
|
||||
device_class=SensorDeviceClass.SOUND_PRESSURE,
|
||||
native_unit_of_measurement=UnitOfSoundPressure.WEIGHTED_DECIBEL_A,
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
suggested_display_precision=0,
|
||||
),
|
||||
}
|
||||
|
||||
PARALLEL_UPDATES = 0
|
||||
|
||||
|
||||
@callback
|
||||
def async_migrate(hass: HomeAssistant, address: str, sensor_name: str) -> None:
|
||||
|
||||
@@ -1,41 +1,49 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"firmware_upgrade_required": "Your device requires a firmware upgrade. Please use the Airthings app (Android/iOS) to upgrade it.",
|
||||
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]",
|
||||
"unsupported_device": "Unsupported device"
|
||||
},
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"user": {
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]",
|
||||
"data": {
|
||||
"address": "[%key:common::config_flow::data::device%]"
|
||||
}
|
||||
},
|
||||
"bluetooth_confirm": {
|
||||
"description": "[%key:component::bluetooth::config::step::bluetooth_confirm::description%]"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"address": "[%key:common::config_flow::data::device%]"
|
||||
},
|
||||
"data_description": {
|
||||
"address": "The Airthings devices discovered via Bluetooth."
|
||||
},
|
||||
"description": "[%key:component::bluetooth::config::step::user::description%]"
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
|
||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"ambient_noise": {
|
||||
"name": "Ambient noise"
|
||||
},
|
||||
"illuminance": {
|
||||
"name": "[%key:component::sensor::entity_component::illuminance::name%]"
|
||||
},
|
||||
"radon_1day_avg": {
|
||||
"name": "Radon 1-day average"
|
||||
},
|
||||
"radon_longterm_avg": {
|
||||
"name": "Radon longterm average"
|
||||
},
|
||||
"radon_1day_level": {
|
||||
"name": "Radon 1-day level"
|
||||
},
|
||||
"radon_longterm_avg": {
|
||||
"name": "Radon longterm average"
|
||||
},
|
||||
"radon_longterm_level": {
|
||||
"name": "Radon longterm level"
|
||||
},
|
||||
"illuminance": {
|
||||
"name": "[%key:component::sensor::entity_component::illuminance::name%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Set up your AirTouch 4 connection details.",
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The hostname or IP address of your AirTouch controller."
|
||||
}
|
||||
},
|
||||
"title": "Set up your AirTouch 4 connection details."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
@@ -21,8 +21,8 @@
|
||||
"state_attributes": {
|
||||
"fan_mode": {
|
||||
"state": {
|
||||
"turbo": "Turbo",
|
||||
"intelligent_auto": "Intelligent Auto"
|
||||
"intelligent_auto": "Intelligent Auto",
|
||||
"turbo": "Turbo"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"pollutant_level": {
|
||||
"default": "mdi:gauge"
|
||||
},
|
||||
"pollutant_label": {
|
||||
"default": "mdi:chemical-weapon"
|
||||
},
|
||||
"pollutant_level": {
|
||||
"default": "mdi:gauge"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,54 +1,44 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
|
||||
"location_not_found": "Location not found",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"step": {
|
||||
"geography_by_coords": {
|
||||
"title": "Configure a geography",
|
||||
"description": "Use the AirVisual cloud API to monitor a latitude/longitude.",
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"latitude": "[%key:common::config_flow::data::latitude%]",
|
||||
"longitude": "[%key:common::config_flow::data::longitude%]"
|
||||
}
|
||||
},
|
||||
"description": "Use the AirVisual cloud API to monitor a latitude/longitude.",
|
||||
"title": "Configure a geography"
|
||||
},
|
||||
"geography_by_name": {
|
||||
"title": "[%key:component::airvisual::config::step::geography_by_coords::title%]",
|
||||
"description": "Use the AirVisual cloud API to monitor a city/state/country.",
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"city": "City",
|
||||
"state": "State",
|
||||
"country": "[%key:common::config_flow::data::country%]"
|
||||
}
|
||||
"country": "[%key:common::config_flow::data::country%]",
|
||||
"state": "State"
|
||||
},
|
||||
"description": "Use the AirVisual cloud API to monitor a city/state/country.",
|
||||
"title": "[%key:component::airvisual::config::step::geography_by_coords::title%]"
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"title": "Re-authenticate AirVisual",
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]"
|
||||
}
|
||||
},
|
||||
"title": "Re-authenticate AirVisual"
|
||||
},
|
||||
"user": {
|
||||
"title": "Configure AirVisual",
|
||||
"description": "Pick what type of AirVisual data you want to monitor."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]",
|
||||
"invalid_api_key": "[%key:common::config_flow::error::invalid_api_key%]",
|
||||
"location_not_found": "Location not found",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_location%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"title": "[%key:component::airvisual::config::step::user::title%]",
|
||||
"data": {
|
||||
"show_on_map": "Show monitored geography on the map"
|
||||
}
|
||||
"description": "Pick what type of AirVisual data you want to monitor.",
|
||||
"title": "Configure AirVisual"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -67,19 +57,29 @@
|
||||
"pollutant_level": {
|
||||
"state": {
|
||||
"good": "Good",
|
||||
"hazardous": "Hazardous",
|
||||
"moderate": "Moderate",
|
||||
"unhealthy": "Unhealthy",
|
||||
"unhealthy_sensitive": "Unhealthy for sensitive groups",
|
||||
"very_unhealthy": "Very unhealthy",
|
||||
"hazardous": "Hazardous"
|
||||
"very_unhealthy": "Very unhealthy"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"airvisual_pro_migration": {
|
||||
"title": "{ip_address} is now part of the AirVisual Pro integration",
|
||||
"description": "AirVisual Pro units are now their own Home Assistant integration (as opposed to be included with the original AirVisual integration that uses the AirVisual cloud API). The Pro device located at `{ip_address}` has automatically been migrated.\n\nAs part of that migration, the Pro's device ID has changed from `{old_device_id}` to `{new_device_id}`. Please update these automations to use the new device ID: {device_automations_string}."
|
||||
"description": "AirVisual Pro units are now their own Home Assistant integration (as opposed to be included with the original AirVisual integration that uses the AirVisual cloud API). The Pro device located at `{ip_address}` has automatically been migrated.\n\nAs part of that migration, the Pro's device ID has changed from `{old_device_id}` to `{new_device_id}`. Please update these automations to use the new device ID: {device_automations_string}.",
|
||||
"title": "{ip_address} is now part of the AirVisual Pro integration"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"show_on_map": "Show monitored geography on the map"
|
||||
},
|
||||
"title": "[%key:component::airvisual::config::step::user::title%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"description": "[%key:component::airvisual_pro::config::step::user::description%]",
|
||||
"data": {
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
}
|
||||
},
|
||||
"description": "[%key:component::airvisual_pro::config::step::user::description%]"
|
||||
},
|
||||
"user": {
|
||||
"description": "The password can be retrieved from the AirVisual Pro's UI.",
|
||||
"data": {
|
||||
"ip_address": "[%key:common::config_flow::data::host%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
},
|
||||
"data_description": {
|
||||
"ip_address": "The hostname or IP address of your AirVisual Pro device."
|
||||
}
|
||||
},
|
||||
"description": "The password can be retrieved from the AirVisual Pro's UI."
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"pm01": {
|
||||
"name": "PM0.1"
|
||||
},
|
||||
"outdoor_air_quality_index": {
|
||||
"name": "Outdoor air quality index"
|
||||
},
|
||||
"pm01": {
|
||||
"name": "PM0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,5 +11,5 @@
|
||||
"documentation": "https://www.home-assistant.io/integrations/airzone",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": ["aioairzone"],
|
||||
"requirements": ["aioairzone==1.0.1"]
|
||||
"requirements": ["aioairzone==1.0.2"]
|
||||
}
|
||||
|
||||
@@ -10,15 +10,15 @@
|
||||
"step": {
|
||||
"discovered_connection": {
|
||||
"data": {
|
||||
"id": "[%key:component::airzone::config::step::user::data::id%]",
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"id": "[%key:component::airzone::config::step::user::data::id%]",
|
||||
"port": "[%key:common::config_flow::data::port%]"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "System ID",
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"id": "System ID",
|
||||
"port": "[%key:common::config_flow::data::port%]"
|
||||
}
|
||||
}
|
||||
@@ -37,19 +37,19 @@
|
||||
"grille_angles": {
|
||||
"name": "Cold angle",
|
||||
"state": {
|
||||
"90deg": "90°",
|
||||
"50deg": "50°",
|
||||
"40deg": "40°",
|
||||
"45deg": "45°",
|
||||
"40deg": "40°"
|
||||
"50deg": "50°",
|
||||
"90deg": "90°"
|
||||
}
|
||||
},
|
||||
"heat_angles": {
|
||||
"name": "Heat angle",
|
||||
"state": {
|
||||
"90deg": "[%key:component::airzone::entity::select::grille_angles::state::90deg%]",
|
||||
"50deg": "[%key:component::airzone::entity::select::grille_angles::state::50deg%]",
|
||||
"40deg": "[%key:component::airzone::entity::select::grille_angles::state::40deg%]",
|
||||
"45deg": "[%key:component::airzone::entity::select::grille_angles::state::45deg%]",
|
||||
"40deg": "[%key:component::airzone::entity::select::grille_angles::state::40deg%]"
|
||||
"50deg": "[%key:component::airzone::entity::select::grille_angles::state::50deg%]",
|
||||
"90deg": "[%key:component::airzone::entity::select::grille_angles::state::90deg%]"
|
||||
}
|
||||
},
|
||||
"modes": {
|
||||
@@ -66,20 +66,20 @@
|
||||
"q_adapt": {
|
||||
"name": "Q-Adapt",
|
||||
"state": {
|
||||
"standard": "Standard",
|
||||
"maximum": "Maximum",
|
||||
"minimum": "Minimum",
|
||||
"power": "Power",
|
||||
"silence": "Silence",
|
||||
"minimum": "Minimum",
|
||||
"maximum": "Maximum"
|
||||
"standard": "Standard"
|
||||
}
|
||||
},
|
||||
"sleep_times": {
|
||||
"name": "Sleep",
|
||||
"state": {
|
||||
"off": "[%key:common::state::off%]",
|
||||
"30m": "30 minutes",
|
||||
"60m": "60 minutes",
|
||||
"90m": "90 minutes"
|
||||
"90m": "90 minutes",
|
||||
"off": "[%key:common::state::off%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"user": {
|
||||
"data": {
|
||||
"id": "Installation",
|
||||
"username": "[%key:common::config_flow::data::username%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"username": "[%key:common::config_flow::data::username%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,9 +32,9 @@
|
||||
"air_quality": {
|
||||
"name": "Air Quality mode",
|
||||
"state": {
|
||||
"auto": "[%key:common::state::auto%]",
|
||||
"off": "[%key:common::state::off%]",
|
||||
"on": "[%key:common::state::on%]",
|
||||
"auto": "[%key:common::state::auto%]"
|
||||
"on": "[%key:common::state::on%]"
|
||||
}
|
||||
},
|
||||
"modes": {
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"pick_implementation": {
|
||||
"title": "[%key:common::config_flow::title::oauth2_pick_implementation%]"
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"title": "[%key:common::config_flow::title::reauth%]",
|
||||
"description": "Aladdin Connect needs to re-authenticate your account"
|
||||
},
|
||||
"oauth_discovery": {
|
||||
"description": "Home Assistant has found an Aladdin Connect device on your network. Press **Submit** to continue setting up Aladdin Connect."
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_account%]",
|
||||
"already_in_progress": "[%key:common::config_flow::abort::already_in_progress%]",
|
||||
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
|
||||
"cloud_not_enabled": "Please make sure you run Home Assistant with `{default_config}` enabled in your configuration.yaml.",
|
||||
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
|
||||
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
|
||||
"oauth_error": "[%key:common::config_flow::abort::oauth2_error%]",
|
||||
"oauth_failed": "[%key:common::config_flow::abort::oauth2_failed%]",
|
||||
"oauth_timeout": "[%key:common::config_flow::abort::oauth2_timeout%]",
|
||||
"oauth_unauthorized": "[%key:common::config_flow::abort::oauth2_unauthorized%]",
|
||||
"missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
|
||||
"authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
|
||||
"no_url_available": "[%key:common::config_flow::abort::oauth2_no_url_available%]",
|
||||
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
|
||||
"reauth_successful": "[%key:common::config_flow::abort::reauth_successful%]",
|
||||
"wrong_account": "You are authenticated with a different account than the one set up. Please authenticate with the configured account.",
|
||||
"cloud_not_enabled": "Please make sure you run Home Assistant with `{default_config}` enabled in your configuration.yaml."
|
||||
"user_rejected_authorize": "[%key:common::config_flow::abort::oauth2_user_rejected_authorize%]",
|
||||
"wrong_account": "You are authenticated with a different account than the one set up. Please authenticate with the configured account."
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "[%key:common::config_flow::create_entry::authenticated%]"
|
||||
},
|
||||
"step": {
|
||||
"oauth_discovery": {
|
||||
"description": "Home Assistant has found an Aladdin Connect device on your network. Press **Submit** to continue setting up Aladdin Connect."
|
||||
},
|
||||
"pick_implementation": {
|
||||
"title": "[%key:common::config_flow::title::oauth2_pick_implementation%]"
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"description": "Aladdin Connect needs to re-authenticate your account",
|
||||
"title": "[%key:common::config_flow::title::reauth%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import TYPE_CHECKING, Any, Final, final
|
||||
from typing import Any, Final, final
|
||||
|
||||
from propcache.api import cached_property
|
||||
import voluptuous as vol
|
||||
@@ -28,8 +27,6 @@ from homeassistant.helpers import config_validation as cv
|
||||
from homeassistant.helpers.config_validation import make_entity_service_schema
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
from homeassistant.helpers.entity_component import EntityComponent
|
||||
from homeassistant.helpers.entity_platform import EntityPlatform
|
||||
from homeassistant.helpers.frame import ReportBehavior, report_usage
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
from homeassistant.util.hass_dict import HassKey
|
||||
|
||||
@@ -149,68 +146,11 @@ class AlarmControlPanelEntity(Entity, cached_properties=CACHED_PROPERTIES_WITH_A
|
||||
)
|
||||
_alarm_control_panel_option_default_code: str | None = None
|
||||
|
||||
__alarm_legacy_state: bool = False
|
||||
|
||||
def __init_subclass__(cls, **kwargs: Any) -> None:
|
||||
"""Post initialisation processing."""
|
||||
super().__init_subclass__(**kwargs)
|
||||
if any(method in cls.__dict__ for method in ("_attr_state", "state")):
|
||||
# Integrations should use the 'alarm_state' property instead of
|
||||
# setting the state directly.
|
||||
cls.__alarm_legacy_state = True
|
||||
|
||||
def __setattr__(self, name: str, value: Any, /) -> None:
|
||||
"""Set attribute.
|
||||
|
||||
Deprecation warning if setting '_attr_state' directly
|
||||
unless already reported.
|
||||
"""
|
||||
if name == "_attr_state":
|
||||
self._report_deprecated_alarm_state_handling()
|
||||
return super().__setattr__(name, value)
|
||||
|
||||
@callback
|
||||
def add_to_platform_start(
|
||||
self,
|
||||
hass: HomeAssistant,
|
||||
platform: EntityPlatform,
|
||||
parallel_updates: asyncio.Semaphore | None,
|
||||
) -> None:
|
||||
"""Start adding an entity to a platform."""
|
||||
super().add_to_platform_start(hass, platform, parallel_updates)
|
||||
if self.__alarm_legacy_state:
|
||||
self._report_deprecated_alarm_state_handling()
|
||||
|
||||
@callback
|
||||
def _report_deprecated_alarm_state_handling(self) -> None:
|
||||
"""Report on deprecated handling of alarm state.
|
||||
|
||||
Integrations should implement alarm_state instead of using state directly.
|
||||
"""
|
||||
report_usage(
|
||||
"is setting state directly."
|
||||
f" Entity {self.entity_id} ({type(self)}) should implement the 'alarm_state'"
|
||||
" property and return its state using the AlarmControlPanelState enum",
|
||||
core_integration_behavior=ReportBehavior.ERROR,
|
||||
custom_integration_behavior=ReportBehavior.LOG,
|
||||
breaks_in_ha_version="2025.11",
|
||||
integration_domain=self.platform.platform_name if self.platform else None,
|
||||
exclude_integrations={DOMAIN},
|
||||
)
|
||||
|
||||
@final
|
||||
@property
|
||||
def state(self) -> str | None:
|
||||
"""Return the current state."""
|
||||
if (alarm_state := self.alarm_state) is not None:
|
||||
return alarm_state
|
||||
if self._attr_state is not None:
|
||||
# Backwards compatibility for integrations that set state directly
|
||||
# Should be removed in 2025.11
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(self._attr_state, str)
|
||||
return self._attr_state
|
||||
return None
|
||||
return self.alarm_state
|
||||
|
||||
@cached_property
|
||||
def alarm_state(self) -> AlarmControlPanelState | None:
|
||||
|
||||
@@ -18,23 +18,23 @@
|
||||
"alarm_arm_away": {
|
||||
"service": "mdi:shield-lock"
|
||||
},
|
||||
"alarm_arm_custom_bypass": {
|
||||
"service": "mdi:security"
|
||||
},
|
||||
"alarm_arm_home": {
|
||||
"service": "mdi:shield-home"
|
||||
},
|
||||
"alarm_arm_night": {
|
||||
"service": "mdi:shield-moon"
|
||||
},
|
||||
"alarm_arm_custom_bypass": {
|
||||
"service": "mdi:security"
|
||||
"alarm_arm_vacation": {
|
||||
"service": "mdi:shield-airplane"
|
||||
},
|
||||
"alarm_disarm": {
|
||||
"service": "mdi:shield-off"
|
||||
},
|
||||
"alarm_trigger": {
|
||||
"service": "mdi:bell-ring"
|
||||
},
|
||||
"alarm_arm_vacation": {
|
||||
"service": "mdi:shield-airplane"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"title": "Alarm control panel",
|
||||
"device_automation": {
|
||||
"action_type": {
|
||||
"arm_away": "Arm {entity_name} away",
|
||||
@@ -10,24 +9,24 @@
|
||||
"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_home": "{entity_name} is armed home",
|
||||
"is_armed_night": "{entity_name} is armed night",
|
||||
"is_armed_vacation": "{entity_name} is armed vacation"
|
||||
"is_armed_vacation": "{entity_name} is armed vacation",
|
||||
"is_disarmed": "{entity_name} is disarmed",
|
||||
"is_triggered": "{entity_name} is triggered"
|
||||
},
|
||||
"extra_fields": {
|
||||
"code": "Code",
|
||||
"for": "[%key:common::device_automation::extra_fields::for%]"
|
||||
},
|
||||
"trigger_type": {
|
||||
"triggered": "{entity_name} triggered",
|
||||
"disarmed": "{entity_name} disarmed",
|
||||
"armed_home": "{entity_name} armed home",
|
||||
"armed_away": "{entity_name} armed away",
|
||||
"armed_home": "{entity_name} armed home",
|
||||
"armed_night": "{entity_name} armed night",
|
||||
"armed_vacation": "{entity_name} armed vacation"
|
||||
"armed_vacation": "{entity_name} armed vacation",
|
||||
"disarmed": "{entity_name} disarmed",
|
||||
"triggered": "{entity_name} triggered"
|
||||
}
|
||||
},
|
||||
"entity_component": {
|
||||
@@ -35,106 +34,34 @@
|
||||
"name": "[%key:component::alarm_control_panel::title%]",
|
||||
"state": {
|
||||
"armed": "Armed",
|
||||
"disarmed": "Disarmed",
|
||||
"armed_home": "Armed home",
|
||||
"armed_away": "Armed away",
|
||||
"armed_custom_bypass": "Armed custom bypass",
|
||||
"armed_home": "Armed home",
|
||||
"armed_night": "Armed night",
|
||||
"armed_vacation": "Armed vacation",
|
||||
"armed_custom_bypass": "Armed custom bypass",
|
||||
"pending": "Pending",
|
||||
"arming": "Arming",
|
||||
"disarmed": "Disarmed",
|
||||
"disarming": "Disarming",
|
||||
"pending": "Pending",
|
||||
"triggered": "Triggered"
|
||||
},
|
||||
"state_attributes": {
|
||||
"code_format": {
|
||||
"name": "Code format",
|
||||
"state": {
|
||||
"text": "Text",
|
||||
"number": "Number"
|
||||
}
|
||||
},
|
||||
"changed_by": {
|
||||
"name": "Changed by"
|
||||
},
|
||||
"code_arm_required": {
|
||||
"name": "Code for arming",
|
||||
"state": {
|
||||
"true": "Required",
|
||||
"false": "Not required"
|
||||
"false": "Not required",
|
||||
"true": "Required"
|
||||
}
|
||||
},
|
||||
"code_format": {
|
||||
"name": "Code format",
|
||||
"state": {
|
||||
"number": "Number",
|
||||
"text": "Text"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"alarm_disarm": {
|
||||
"name": "Disarm",
|
||||
"description": "Disarms the alarm.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"name": "Code",
|
||||
"description": "Code to disarm the alarm."
|
||||
}
|
||||
}
|
||||
},
|
||||
"alarm_arm_custom_bypass": {
|
||||
"name": "Arm with custom bypass",
|
||||
"description": "Arms the alarm while allowing to bypass a custom area.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"name": "[%key:component::alarm_control_panel::services::alarm_disarm::fields::code::name%]",
|
||||
"description": "Code to arm the alarm."
|
||||
}
|
||||
}
|
||||
},
|
||||
"alarm_arm_home": {
|
||||
"name": "Arm home",
|
||||
"description": "Arms the alarm in the home mode.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"name": "[%key:component::alarm_control_panel::services::alarm_disarm::fields::code::name%]",
|
||||
"description": "[%key:component::alarm_control_panel::services::alarm_arm_custom_bypass::fields::code::description%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"alarm_arm_away": {
|
||||
"name": "Arm away",
|
||||
"description": "Arms the alarm in the away mode.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"name": "[%key:component::alarm_control_panel::services::alarm_disarm::fields::code::name%]",
|
||||
"description": "[%key:component::alarm_control_panel::services::alarm_arm_custom_bypass::fields::code::description%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"alarm_arm_night": {
|
||||
"name": "Arm night",
|
||||
"description": "Arms the alarm in the night mode.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"name": "[%key:component::alarm_control_panel::services::alarm_disarm::fields::code::name%]",
|
||||
"description": "[%key:component::alarm_control_panel::services::alarm_arm_custom_bypass::fields::code::description%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"alarm_arm_vacation": {
|
||||
"name": "Arm vacation",
|
||||
"description": "Arms the alarm in the vacation mode.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"name": "[%key:component::alarm_control_panel::services::alarm_disarm::fields::code::name%]",
|
||||
"description": "[%key:component::alarm_control_panel::services::alarm_arm_custom_bypass::fields::code::description%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"alarm_trigger": {
|
||||
"name": "Trigger",
|
||||
"description": "Triggers the alarm manually.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"name": "[%key:component::alarm_control_panel::services::alarm_disarm::fields::code::name%]",
|
||||
"description": "[%key:component::alarm_control_panel::services::alarm_arm_custom_bypass::fields::code::description%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,5 +70,78 @@
|
||||
"code_arm_required": {
|
||||
"message": "Arming requires a code but none was given for {entity_id}."
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"alarm_arm_away": {
|
||||
"description": "Arms the alarm in the away mode.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"description": "[%key:component::alarm_control_panel::services::alarm_arm_custom_bypass::fields::code::description%]",
|
||||
"name": "[%key:component::alarm_control_panel::services::alarm_disarm::fields::code::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Arm away"
|
||||
},
|
||||
"alarm_arm_custom_bypass": {
|
||||
"description": "Arms the alarm while allowing to bypass a custom area.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"description": "Code to arm the alarm.",
|
||||
"name": "[%key:component::alarm_control_panel::services::alarm_disarm::fields::code::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Arm with custom bypass"
|
||||
},
|
||||
"alarm_arm_home": {
|
||||
"description": "Arms the alarm in the home mode.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"description": "[%key:component::alarm_control_panel::services::alarm_arm_custom_bypass::fields::code::description%]",
|
||||
"name": "[%key:component::alarm_control_panel::services::alarm_disarm::fields::code::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Arm home"
|
||||
},
|
||||
"alarm_arm_night": {
|
||||
"description": "Arms the alarm in the night mode.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"description": "[%key:component::alarm_control_panel::services::alarm_arm_custom_bypass::fields::code::description%]",
|
||||
"name": "[%key:component::alarm_control_panel::services::alarm_disarm::fields::code::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Arm night"
|
||||
},
|
||||
"alarm_arm_vacation": {
|
||||
"description": "Arms the alarm in the vacation mode.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"description": "[%key:component::alarm_control_panel::services::alarm_arm_custom_bypass::fields::code::description%]",
|
||||
"name": "[%key:component::alarm_control_panel::services::alarm_disarm::fields::code::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Arm vacation"
|
||||
},
|
||||
"alarm_disarm": {
|
||||
"description": "Disarms the alarm.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"description": "Code to disarm the alarm.",
|
||||
"name": "Code"
|
||||
}
|
||||
},
|
||||
"name": "Disarm"
|
||||
},
|
||||
"alarm_trigger": {
|
||||
"description": "Triggers the alarm manually.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"description": "[%key:component::alarm_control_panel::services::alarm_arm_custom_bypass::fields::code::description%]",
|
||||
"name": "[%key:component::alarm_control_panel::services::alarm_disarm::fields::code::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Trigger"
|
||||
}
|
||||
},
|
||||
"title": "Alarm control panel"
|
||||
}
|
||||
|
||||
@@ -1,101 +1,101 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Choose AlarmDecoder protocol",
|
||||
"data": {
|
||||
"protocol": "Protocol"
|
||||
}
|
||||
},
|
||||
"protocol": {
|
||||
"title": "Configure connection settings",
|
||||
"data": {
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"port": "[%key:common::config_flow::data::port%]",
|
||||
"device_baudrate": "Device baud rate",
|
||||
"device_path": "Device path"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The hostname or IP address of the AlarmDecoder device that is connected to your alarm panel.",
|
||||
"port": "The port on which AlarmDecoder is accessible (for example, 10000)"
|
||||
}
|
||||
}
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Successfully connected to AlarmDecoder."
|
||||
},
|
||||
"error": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"create_entry": {
|
||||
"default": "Successfully connected to AlarmDecoder."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
"step": {
|
||||
"protocol": {
|
||||
"data": {
|
||||
"device_baudrate": "Device baud rate",
|
||||
"device_path": "Device path",
|
||||
"host": "[%key:common::config_flow::data::host%]",
|
||||
"port": "[%key:common::config_flow::data::port%]"
|
||||
},
|
||||
"data_description": {
|
||||
"host": "The hostname or IP address of the AlarmDecoder device that is connected to your alarm panel.",
|
||||
"port": "The port on which AlarmDecoder is accessible (for example, 10000)"
|
||||
},
|
||||
"title": "Configure connection settings"
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"protocol": "Protocol"
|
||||
},
|
||||
"title": "Choose AlarmDecoder protocol"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"error": {
|
||||
"int": "The field below must be an integer.",
|
||||
"loop_range": "'RF loop' must be an integer between 1 and 4.",
|
||||
"loop_rfid": "'RF loop' cannot be used without 'RF serial'.",
|
||||
"relay_inclusive": "'Relay address' and 'Relay channel' are codependent and must be included together."
|
||||
},
|
||||
"step": {
|
||||
"arm_settings": {
|
||||
"data": {
|
||||
"alt_night_mode": "Alternative night mode",
|
||||
"auto_bypass": "Auto-bypass on arm",
|
||||
"code_arm_required": "Code required for arming"
|
||||
},
|
||||
"title": "[%key:component::alarmdecoder::options::step::init::title%]"
|
||||
},
|
||||
"init": {
|
||||
"title": "Configure AlarmDecoder",
|
||||
"description": "What would you like to edit?",
|
||||
"data": {
|
||||
"edit_selection": "Edit"
|
||||
}
|
||||
},
|
||||
"arm_settings": {
|
||||
"title": "[%key:component::alarmdecoder::options::step::init::title%]",
|
||||
"data": {
|
||||
"auto_bypass": "Auto-bypass on arm",
|
||||
"code_arm_required": "Code required for arming",
|
||||
"alt_night_mode": "Alternative night mode"
|
||||
}
|
||||
},
|
||||
"zone_select": {
|
||||
"title": "[%key:component::alarmdecoder::options::step::init::title%]",
|
||||
"description": "Enter the zone number you'd like to to add, edit, or remove.",
|
||||
"data": {
|
||||
"zone_number": "Zone number"
|
||||
}
|
||||
},
|
||||
"description": "What would you like to edit?",
|
||||
"title": "Configure AlarmDecoder"
|
||||
},
|
||||
"zone_details": {
|
||||
"title": "[%key:component::alarmdecoder::options::step::init::title%]",
|
||||
"description": "Enter details for zone {zone_number}. To delete zone {zone_number}, leave 'Zone name' blank.",
|
||||
"data": {
|
||||
"zone_name": "Zone name",
|
||||
"zone_type": "Zone type",
|
||||
"zone_rfid": "RF serial",
|
||||
"zone_loop": "RF loop",
|
||||
"zone_name": "Zone name",
|
||||
"zone_relayaddr": "Relay address",
|
||||
"zone_relaychan": "Relay channel"
|
||||
}
|
||||
"zone_relaychan": "Relay channel",
|
||||
"zone_rfid": "RF serial",
|
||||
"zone_type": "Zone type"
|
||||
},
|
||||
"description": "Enter details for zone {zone_number}. To delete zone {zone_number}, leave 'Zone name' blank.",
|
||||
"title": "[%key:component::alarmdecoder::options::step::init::title%]"
|
||||
},
|
||||
"zone_select": {
|
||||
"data": {
|
||||
"zone_number": "Zone number"
|
||||
},
|
||||
"description": "Enter the zone number you'd like to to add, edit, or remove.",
|
||||
"title": "[%key:component::alarmdecoder::options::step::init::title%]"
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"relay_inclusive": "'Relay address' and 'Relay channel' are codependent and must be included together.",
|
||||
"int": "The field below must be an integer.",
|
||||
"loop_rfid": "'RF loop' cannot be used without 'RF serial'.",
|
||||
"loop_range": "'RF loop' must be an integer between 1 and 4."
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"alarm_keypress": {
|
||||
"name": "Key press",
|
||||
"description": "Sends custom keypresses to the alarm.",
|
||||
"fields": {
|
||||
"keypress": {
|
||||
"name": "[%key:component::alarmdecoder::services::alarm_keypress::name%]",
|
||||
"description": "String to send to the alarm panel."
|
||||
"description": "String to send to the alarm panel.",
|
||||
"name": "[%key:component::alarmdecoder::services::alarm_keypress::name%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Key press"
|
||||
},
|
||||
"alarm_toggle_chime": {
|
||||
"name": "Toggle chime",
|
||||
"description": "Sends the alarm the toggle chime command.",
|
||||
"fields": {
|
||||
"code": {
|
||||
"name": "Code",
|
||||
"description": "Code to toggle the alarm control panel chime with."
|
||||
"description": "Code to toggle the alarm control panel chime with.",
|
||||
"name": "Code"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Toggle chime"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"title": "Alert",
|
||||
"entity_component": {
|
||||
"_": {
|
||||
"name": "[%key:component::alert::title%]",
|
||||
@@ -12,16 +11,17 @@
|
||||
},
|
||||
"services": {
|
||||
"toggle": {
|
||||
"name": "[%key:common::action::toggle%]",
|
||||
"description": "Toggles alert's notifications."
|
||||
"description": "Toggles alert's notifications.",
|
||||
"name": "[%key:common::action::toggle%]"
|
||||
},
|
||||
"turn_off": {
|
||||
"name": "[%key:common::action::turn_off%]",
|
||||
"description": "Silences alert's notifications."
|
||||
"description": "Silences alert's notifications.",
|
||||
"name": "[%key:common::action::turn_off%]"
|
||||
},
|
||||
"turn_on": {
|
||||
"name": "[%key:common::action::turn_on%]",
|
||||
"description": "Resets alert's notifications."
|
||||
"description": "Resets alert's notifications.",
|
||||
"name": "[%key:common::action::turn_on%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"title": "Alert"
|
||||
}
|
||||
|
||||
@@ -1472,10 +1472,10 @@ class AlexaModeController(AlexaCapability):
|
||||
# Return state instead of position when using ModeController.
|
||||
mode = self.entity.state
|
||||
if mode in (
|
||||
cover.STATE_OPEN,
|
||||
cover.STATE_OPENING,
|
||||
cover.STATE_CLOSED,
|
||||
cover.STATE_CLOSING,
|
||||
cover.CoverState.OPEN,
|
||||
cover.CoverState.OPENING,
|
||||
cover.CoverState.CLOSED,
|
||||
cover.CoverState.CLOSING,
|
||||
STATE_UNKNOWN,
|
||||
):
|
||||
return f"{cover.ATTR_POSITION}.{mode}"
|
||||
@@ -1594,11 +1594,11 @@ class AlexaModeController(AlexaCapability):
|
||||
["Position", AlexaGlobalCatalog.SETTING_OPENING], False
|
||||
)
|
||||
self._resource.add_mode(
|
||||
f"{cover.ATTR_POSITION}.{cover.STATE_OPEN}",
|
||||
f"{cover.ATTR_POSITION}.{cover.CoverState.OPEN}",
|
||||
[AlexaGlobalCatalog.VALUE_OPEN],
|
||||
)
|
||||
self._resource.add_mode(
|
||||
f"{cover.ATTR_POSITION}.{cover.STATE_CLOSED}",
|
||||
f"{cover.ATTR_POSITION}.{cover.CoverState.CLOSED}",
|
||||
[AlexaGlobalCatalog.VALUE_CLOSE],
|
||||
)
|
||||
self._resource.add_mode(
|
||||
@@ -1651,22 +1651,22 @@ class AlexaModeController(AlexaCapability):
|
||||
raise_labels.append(AlexaSemantics.ACTION_OPEN)
|
||||
self._semantics.add_states_to_value(
|
||||
[AlexaSemantics.STATES_CLOSED],
|
||||
f"{cover.ATTR_POSITION}.{cover.STATE_CLOSED}",
|
||||
f"{cover.ATTR_POSITION}.{cover.CoverState.CLOSED}",
|
||||
)
|
||||
self._semantics.add_states_to_value(
|
||||
[AlexaSemantics.STATES_OPEN],
|
||||
f"{cover.ATTR_POSITION}.{cover.STATE_OPEN}",
|
||||
f"{cover.ATTR_POSITION}.{cover.CoverState.OPEN}",
|
||||
)
|
||||
|
||||
self._semantics.add_action_to_directive(
|
||||
lower_labels,
|
||||
"SetMode",
|
||||
{"mode": f"{cover.ATTR_POSITION}.{cover.STATE_CLOSED}"},
|
||||
{"mode": f"{cover.ATTR_POSITION}.{cover.CoverState.CLOSED}"},
|
||||
)
|
||||
self._semantics.add_action_to_directive(
|
||||
raise_labels,
|
||||
"SetMode",
|
||||
{"mode": f"{cover.ATTR_POSITION}.{cover.STATE_OPEN}"},
|
||||
{"mode": f"{cover.ATTR_POSITION}.{cover.CoverState.OPEN}"},
|
||||
)
|
||||
|
||||
return self._semantics.serialize_semantics()
|
||||
|
||||
@@ -1261,9 +1261,9 @@ async def async_api_set_mode(
|
||||
elif instance == f"{cover.DOMAIN}.{cover.ATTR_POSITION}":
|
||||
position = mode.split(".")[1]
|
||||
|
||||
if position == cover.STATE_CLOSED:
|
||||
if position == cover.CoverState.CLOSED:
|
||||
service = cover.SERVICE_CLOSE_COVER
|
||||
elif position == cover.STATE_OPEN:
|
||||
elif position == cover.CoverState.OPEN:
|
||||
service = cover.SERVICE_OPEN_COVER
|
||||
elif position == "custom":
|
||||
service = cover.SERVICE_STOP_COVER
|
||||
|
||||
@@ -8,5 +8,5 @@
|
||||
"iot_class": "cloud_polling",
|
||||
"loggers": ["aioamazondevices"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["aioamazondevices==6.4.4"]
|
||||
"requirements": ["aioamazondevices==6.4.6"]
|
||||
}
|
||||
|
||||
@@ -1,47 +1,12 @@
|
||||
{
|
||||
"common": {
|
||||
"data_code": "One-time password (OTP code)",
|
||||
"data_description_username": "The email address of your Amazon account.",
|
||||
"data_description_password": "The password of your Amazon account.",
|
||||
"data_description_code": "The one-time password to log in to your account. Currently, only tokens from OTP applications are supported.",
|
||||
"data_description_password": "The password of your Amazon account.",
|
||||
"data_description_username": "The email address of your Amazon account.",
|
||||
"device_id_description": "The ID of the device to send the command to."
|
||||
},
|
||||
"config": {
|
||||
"flow_title": "{username}",
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"username": "[%key:common::config_flow::data::username%]",
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"code": "[%key:component::alexa_devices::common::data_code%]"
|
||||
},
|
||||
"data_description": {
|
||||
"username": "[%key:component::alexa_devices::common::data_description_username%]",
|
||||
"password": "[%key:component::alexa_devices::common::data_description_password%]",
|
||||
"code": "[%key:component::alexa_devices::common::data_description_code%]"
|
||||
}
|
||||
},
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"code": "[%key:component::alexa_devices::common::data_code%]"
|
||||
},
|
||||
"data_description": {
|
||||
"password": "[%key:component::alexa_devices::common::data_description_password%]",
|
||||
"code": "[%key:component::alexa_devices::common::data_description_code%]"
|
||||
}
|
||||
},
|
||||
"reconfigure": {
|
||||
"data": {
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"code": "[%key:component::alexa_devices::common::data_code%]"
|
||||
},
|
||||
"data_description": {
|
||||
"password": "[%key:component::alexa_devices::common::data_description_password%]",
|
||||
"code": "[%key:component::alexa_devices::common::data_description_code%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]",
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
@@ -55,15 +20,50 @@
|
||||
"cannot_retrieve_data": "Unable to retrieve data from Amazon. Please try again later.",
|
||||
"invalid_auth": "[%key:common::config_flow::error::invalid_auth%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"flow_title": "{username}",
|
||||
"step": {
|
||||
"reauth_confirm": {
|
||||
"data": {
|
||||
"code": "[%key:component::alexa_devices::common::data_code%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
},
|
||||
"data_description": {
|
||||
"code": "[%key:component::alexa_devices::common::data_description_code%]",
|
||||
"password": "[%key:component::alexa_devices::common::data_description_password%]"
|
||||
}
|
||||
},
|
||||
"reconfigure": {
|
||||
"data": {
|
||||
"code": "[%key:component::alexa_devices::common::data_code%]",
|
||||
"password": "[%key:common::config_flow::data::password%]"
|
||||
},
|
||||
"data_description": {
|
||||
"code": "[%key:component::alexa_devices::common::data_description_code%]",
|
||||
"password": "[%key:component::alexa_devices::common::data_description_password%]"
|
||||
}
|
||||
},
|
||||
"user": {
|
||||
"data": {
|
||||
"code": "[%key:component::alexa_devices::common::data_code%]",
|
||||
"password": "[%key:common::config_flow::data::password%]",
|
||||
"username": "[%key:common::config_flow::data::username%]"
|
||||
},
|
||||
"data_description": {
|
||||
"code": "[%key:component::alexa_devices::common::data_description_code%]",
|
||||
"password": "[%key:component::alexa_devices::common::data_description_password%]",
|
||||
"username": "[%key:component::alexa_devices::common::data_description_username%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"notify": {
|
||||
"speak": {
|
||||
"name": "Speak"
|
||||
},
|
||||
"announce": {
|
||||
"name": "Announce"
|
||||
},
|
||||
"speak": {
|
||||
"name": "Speak"
|
||||
}
|
||||
},
|
||||
"switch": {
|
||||
@@ -72,34 +72,24 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"send_sound": {
|
||||
"name": "Send sound",
|
||||
"description": "Sends a sound to a device",
|
||||
"fields": {
|
||||
"device_id": {
|
||||
"name": "Device",
|
||||
"description": "[%key:component::alexa_devices::common::device_id_description%]"
|
||||
},
|
||||
"sound": {
|
||||
"name": "Alexa Skill sound file",
|
||||
"description": "The sound file to play."
|
||||
}
|
||||
}
|
||||
"exceptions": {
|
||||
"cannot_connect_with_error": {
|
||||
"message": "Error connecting: {error}"
|
||||
},
|
||||
"send_text_command": {
|
||||
"name": "Send text command",
|
||||
"description": "Sends a text command to a device",
|
||||
"fields": {
|
||||
"text_command": {
|
||||
"name": "Alexa text command",
|
||||
"description": "The text command to send."
|
||||
},
|
||||
"device_id": {
|
||||
"name": "Device",
|
||||
"description": "[%key:component::alexa_devices::common::device_id_description%]"
|
||||
}
|
||||
}
|
||||
"cannot_retrieve_data_with_error": {
|
||||
"message": "Error retrieving data: {error}"
|
||||
},
|
||||
"device_serial_number_missing": {
|
||||
"message": "Device serial number missing: {device_id}"
|
||||
},
|
||||
"entry_not_loaded": {
|
||||
"message": "Entry not loaded: {entry}"
|
||||
},
|
||||
"invalid_device_id": {
|
||||
"message": "Invalid device ID specified: {device_id}"
|
||||
},
|
||||
"invalid_sound_value": {
|
||||
"message": "Invalid sound {sound} specified"
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
@@ -149,24 +139,34 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"cannot_connect_with_error": {
|
||||
"message": "Error connecting: {error}"
|
||||
"services": {
|
||||
"send_sound": {
|
||||
"description": "Sends a sound to a device",
|
||||
"fields": {
|
||||
"device_id": {
|
||||
"description": "[%key:component::alexa_devices::common::device_id_description%]",
|
||||
"name": "Device"
|
||||
},
|
||||
"sound": {
|
||||
"description": "The sound file to play.",
|
||||
"name": "Alexa Skill sound file"
|
||||
}
|
||||
},
|
||||
"name": "Send sound"
|
||||
},
|
||||
"cannot_retrieve_data_with_error": {
|
||||
"message": "Error retrieving data: {error}"
|
||||
},
|
||||
"device_serial_number_missing": {
|
||||
"message": "Device serial number missing: {device_id}"
|
||||
},
|
||||
"invalid_device_id": {
|
||||
"message": "Invalid device ID specified: {device_id}"
|
||||
},
|
||||
"invalid_sound_value": {
|
||||
"message": "Invalid sound {sound} specified"
|
||||
},
|
||||
"entry_not_loaded": {
|
||||
"message": "Entry not loaded: {entry}"
|
||||
"send_text_command": {
|
||||
"description": "Sends a text command to a device",
|
||||
"fields": {
|
||||
"device_id": {
|
||||
"description": "[%key:component::alexa_devices::common::device_id_description%]",
|
||||
"name": "Device"
|
||||
},
|
||||
"text_command": {
|
||||
"description": "The text command to send.",
|
||||
"name": "Alexa text command"
|
||||
}
|
||||
},
|
||||
"name": "Send text command"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,6 +65,31 @@ SENSOR_DESCRIPTIONS = [
|
||||
suggested_display_precision=2,
|
||||
translation_placeholders={"sensor_name": "BME280"},
|
||||
),
|
||||
AltruistSensorEntityDescription(
|
||||
device_class=SensorDeviceClass.HUMIDITY,
|
||||
key="BME680_humidity",
|
||||
translation_key="humidity",
|
||||
native_unit_of_measurement=PERCENTAGE,
|
||||
suggested_display_precision=2,
|
||||
translation_placeholders={"sensor_name": "BME680"},
|
||||
),
|
||||
AltruistSensorEntityDescription(
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
key="BME680_pressure",
|
||||
translation_key="pressure",
|
||||
native_unit_of_measurement=UnitOfPressure.PA,
|
||||
suggested_unit_of_measurement=UnitOfPressure.MMHG,
|
||||
suggested_display_precision=0,
|
||||
translation_placeholders={"sensor_name": "BME680"},
|
||||
),
|
||||
AltruistSensorEntityDescription(
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
key="BME680_temperature",
|
||||
translation_key="temperature",
|
||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||
suggested_display_precision=2,
|
||||
translation_placeholders={"sensor_name": "BME680"},
|
||||
),
|
||||
AltruistSensorEntityDescription(
|
||||
device_class=SensorDeviceClass.PRESSURE,
|
||||
key="BMP_pressure",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]",
|
||||
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]"
|
||||
},
|
||||
"error": {
|
||||
"no_device_found": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"flow_title": "{name}",
|
||||
"step": {
|
||||
"discovery_confirm": {
|
||||
@@ -14,37 +21,30 @@
|
||||
},
|
||||
"description": "Fill in Altruist IP address or hostname in your local network"
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"no_devices_found": "[%key:common::config_flow::abort::no_devices_found%]",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
},
|
||||
"error": {
|
||||
"no_device_found": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"co2": {
|
||||
"name": "{sensor_name} CO2"
|
||||
},
|
||||
"humidity": {
|
||||
"name": "{sensor_name} humidity"
|
||||
},
|
||||
"pressure": {
|
||||
"name": "{sensor_name} pressure"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "{sensor_name} temperature"
|
||||
},
|
||||
"noise_max": {
|
||||
"name": "Maximum noise"
|
||||
},
|
||||
"noise_avg": {
|
||||
"name": "Average noise"
|
||||
},
|
||||
"co2": {
|
||||
"name": "{sensor_name} CO2"
|
||||
"noise_max": {
|
||||
"name": "Maximum noise"
|
||||
},
|
||||
"pressure": {
|
||||
"name": "{sensor_name} pressure"
|
||||
},
|
||||
"radiation": {
|
||||
"name": "Radiation level"
|
||||
},
|
||||
"temperature": {
|
||||
"name": "{sensor_name} temperature"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
{
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"general": {
|
||||
"default": "mdi:transmission-tower"
|
||||
},
|
||||
"controlled_load": {
|
||||
"default": "mdi:clock-outline"
|
||||
},
|
||||
"feed_in": {
|
||||
"default": "mdi:solar-power"
|
||||
},
|
||||
"renewables": {
|
||||
"default": "mdi:solar-power"
|
||||
}
|
||||
},
|
||||
"binary_sensor": {
|
||||
"demand_window": {
|
||||
"default": "mdi:meter-electric",
|
||||
@@ -21,6 +7,20 @@
|
||||
"off": "mdi:meter-electric-outline"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"controlled_load": {
|
||||
"default": "mdi:clock-outline"
|
||||
},
|
||||
"feed_in": {
|
||||
"default": "mdi:solar-power"
|
||||
},
|
||||
"general": {
|
||||
"default": "mdi:transmission-tower"
|
||||
},
|
||||
"renewables": {
|
||||
"default": "mdi:solar-power"
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
|
||||
@@ -22,40 +22,40 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"get_forecasts": {
|
||||
"name": "Get price forecasts",
|
||||
"description": "Retrieves price forecasts from Amber Electric for a site.",
|
||||
"fields": {
|
||||
"config_entry_id": {
|
||||
"description": "The config entry of the site to get forecasts for.",
|
||||
"name": "Config entry"
|
||||
},
|
||||
"channel_type": {
|
||||
"name": "Channel type",
|
||||
"description": "The channel to get forecasts for."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"channel_not_found": {
|
||||
"message": "There is no {channel_type} channel at this site."
|
||||
},
|
||||
"integration_not_found": {
|
||||
"message": "Config entry \"{target}\" not found in registry."
|
||||
},
|
||||
"not_loaded": {
|
||||
"message": "{target} is not loaded."
|
||||
},
|
||||
"channel_not_found": {
|
||||
"message": "There is no {channel_type} channel at this site."
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"channel_type": {
|
||||
"options": {
|
||||
"general": "General",
|
||||
"controlled_load": "Controlled load",
|
||||
"feed_in": "Feed-in"
|
||||
"feed_in": "Feed-in",
|
||||
"general": "General"
|
||||
}
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"get_forecasts": {
|
||||
"description": "Retrieves price forecasts from Amber Electric for a site.",
|
||||
"fields": {
|
||||
"channel_type": {
|
||||
"description": "The channel to get forecasts for.",
|
||||
"name": "Channel type"
|
||||
},
|
||||
"config_entry_id": {
|
||||
"description": "The config entry of the site to get forecasts for.",
|
||||
"name": "Config entry"
|
||||
}
|
||||
},
|
||||
"name": "Get price forecasts"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
"last_rain": {
|
||||
"default": "mdi:water"
|
||||
},
|
||||
"lightning_distance": {
|
||||
"default": "mdi:lightning-bolt"
|
||||
},
|
||||
"lightning_strikes_per_day": {
|
||||
"default": "mdi:lightning-bolt"
|
||||
},
|
||||
"lightning_strikes_per_hour": {
|
||||
"default": "mdi:lightning-bolt"
|
||||
},
|
||||
"lightning_distance": {
|
||||
"default": "mdi:lightning-bolt"
|
||||
},
|
||||
"wind_direction": {
|
||||
"default": "mdi:compass-outline"
|
||||
}
|
||||
|
||||
@@ -1,42 +1,30 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Select region",
|
||||
"description": "Choose the region you want to survey in order to locate Ambient personal weather stations."
|
||||
},
|
||||
"station": {
|
||||
"title": "Select station",
|
||||
"description": "Select the weather station you want to add to Home Assistant.",
|
||||
"data": {
|
||||
"station": "Station"
|
||||
}
|
||||
}
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
},
|
||||
"error": {
|
||||
"no_stations_found": "Did not find any stations in the selected region."
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_device%]"
|
||||
"step": {
|
||||
"station": {
|
||||
"data": {
|
||||
"station": "Station"
|
||||
},
|
||||
"description": "Select the weather station you want to add to Home Assistant.",
|
||||
"title": "Select station"
|
||||
},
|
||||
"user": {
|
||||
"description": "Choose the region you want to survey in order to locate Ambient personal weather stations.",
|
||||
"title": "Select region"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"sensor": {
|
||||
"pm25_24h_average": {
|
||||
"name": "PM2.5 (24 hour average)"
|
||||
},
|
||||
"pm25_aqi": {
|
||||
"name": "PM2.5 AQI"
|
||||
},
|
||||
"pm25_aqi_24h_average": {
|
||||
"name": "PM2.5 AQI (24 hour average)"
|
||||
},
|
||||
"absolute_pressure": {
|
||||
"name": "Absolute pressure"
|
||||
},
|
||||
"relative_pressure": {
|
||||
"name": "Relative pressure"
|
||||
},
|
||||
"daily_rain": {
|
||||
"name": "Daily rain"
|
||||
},
|
||||
@@ -52,21 +40,33 @@
|
||||
"last_rain": {
|
||||
"name": "Last rain"
|
||||
},
|
||||
"lightning_distance": {
|
||||
"name": "Lightning distance"
|
||||
},
|
||||
"lightning_strikes_per_day": {
|
||||
"name": "Lightning strikes per day"
|
||||
},
|
||||
"lightning_strikes_per_hour": {
|
||||
"name": "Lightning strikes per hour"
|
||||
},
|
||||
"lightning_distance": {
|
||||
"name": "Lightning distance"
|
||||
},
|
||||
"max_daily_gust": {
|
||||
"name": "Max daily gust"
|
||||
},
|
||||
"monthly_rain": {
|
||||
"name": "Monthly rain"
|
||||
},
|
||||
"pm25_24h_average": {
|
||||
"name": "PM2.5 (24 hour average)"
|
||||
},
|
||||
"pm25_aqi": {
|
||||
"name": "PM2.5 AQI"
|
||||
},
|
||||
"pm25_aqi_24h_average": {
|
||||
"name": "PM2.5 AQI (24 hour average)"
|
||||
},
|
||||
"relative_pressure": {
|
||||
"name": "Relative pressure"
|
||||
},
|
||||
"uv_index": {
|
||||
"name": "UV index"
|
||||
},
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"title": "Fill in your information",
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"app_key": "Application Key"
|
||||
}
|
||||
}
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
},
|
||||
"error": {
|
||||
"invalid_key": "[%key:common::config_flow::error::invalid_api_key%]",
|
||||
"no_devices": "No devices found in account"
|
||||
},
|
||||
"abort": {
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"api_key": "[%key:common::config_flow::data::api_key%]",
|
||||
"app_key": "Application Key"
|
||||
},
|
||||
"title": "Fill in your information"
|
||||
}
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
@@ -22,6 +22,9 @@
|
||||
"battery_1": {
|
||||
"name": "Battery 1"
|
||||
},
|
||||
"battery_10": {
|
||||
"name": "Battery 10"
|
||||
},
|
||||
"battery_2": {
|
||||
"name": "Battery 2"
|
||||
},
|
||||
@@ -46,59 +49,11 @@
|
||||
"battery_9": {
|
||||
"name": "Battery 9"
|
||||
},
|
||||
"battery_10": {
|
||||
"name": "Battery 10"
|
||||
},
|
||||
"interior_battery": {
|
||||
"name": "Interior battery"
|
||||
},
|
||||
"leak_detector_battery_1": {
|
||||
"name": "Leak detector battery 1"
|
||||
},
|
||||
"leak_detector_battery_2": {
|
||||
"name": "Leak detector battery 2"
|
||||
},
|
||||
"leak_detector_battery_3": {
|
||||
"name": "Leak detector battery 3"
|
||||
},
|
||||
"leak_detector_battery_4": {
|
||||
"name": "Leak detector battery 4"
|
||||
},
|
||||
"soil_monitor_battery_1": {
|
||||
"name": "Soil monitor battery 1"
|
||||
},
|
||||
"soil_monitor_battery_2": {
|
||||
"name": "Soil monitor battery 2"
|
||||
},
|
||||
"soil_monitor_battery_3": {
|
||||
"name": "Soil monitor battery 3"
|
||||
},
|
||||
"soil_monitor_battery_4": {
|
||||
"name": "Soil monitor battery 4"
|
||||
},
|
||||
"soil_monitor_battery_5": {
|
||||
"name": "Soil monitor battery 5"
|
||||
},
|
||||
"soil_monitor_battery_6": {
|
||||
"name": "Soil monitor battery 6"
|
||||
},
|
||||
"soil_monitor_battery_7": {
|
||||
"name": "Soil monitor battery 7"
|
||||
},
|
||||
"soil_monitor_battery_8": {
|
||||
"name": "Soil monitor battery 8"
|
||||
},
|
||||
"soil_monitor_battery_9": {
|
||||
"name": "Soil monitor battery 9"
|
||||
},
|
||||
"soil_monitor_battery_10": {
|
||||
"name": "Soil monitor battery 10"
|
||||
},
|
||||
"co2_battery": {
|
||||
"name": "Carbon dioxide battery"
|
||||
},
|
||||
"lightning_detector_battery": {
|
||||
"name": "Lightning detector battery"
|
||||
"interior_battery": {
|
||||
"name": "Interior battery"
|
||||
},
|
||||
"leak_detector_1": {
|
||||
"name": "Leak detector 1"
|
||||
@@ -112,15 +67,33 @@
|
||||
"leak_detector_4": {
|
||||
"name": "Leak detector 4"
|
||||
},
|
||||
"pm25_indoor_battery": {
|
||||
"name": "PM25 indoor battery"
|
||||
"leak_detector_battery_1": {
|
||||
"name": "Leak detector battery 1"
|
||||
},
|
||||
"leak_detector_battery_2": {
|
||||
"name": "Leak detector battery 2"
|
||||
},
|
||||
"leak_detector_battery_3": {
|
||||
"name": "Leak detector battery 3"
|
||||
},
|
||||
"leak_detector_battery_4": {
|
||||
"name": "Leak detector battery 4"
|
||||
},
|
||||
"lightning_detector_battery": {
|
||||
"name": "Lightning detector battery"
|
||||
},
|
||||
"pm25_battery": {
|
||||
"name": "PM25 battery"
|
||||
},
|
||||
"pm25_indoor_battery": {
|
||||
"name": "PM25 indoor battery"
|
||||
},
|
||||
"relay_1": {
|
||||
"name": "Relay 1"
|
||||
},
|
||||
"relay_10": {
|
||||
"name": "Relay 10"
|
||||
},
|
||||
"relay_2": {
|
||||
"name": "Relay 2"
|
||||
},
|
||||
@@ -145,32 +118,44 @@
|
||||
"relay_9": {
|
||||
"name": "Relay 9"
|
||||
},
|
||||
"relay_10": {
|
||||
"name": "Relay 10"
|
||||
"soil_monitor_battery_1": {
|
||||
"name": "Soil monitor battery 1"
|
||||
},
|
||||
"soil_monitor_battery_10": {
|
||||
"name": "Soil monitor battery 10"
|
||||
},
|
||||
"soil_monitor_battery_2": {
|
||||
"name": "Soil monitor battery 2"
|
||||
},
|
||||
"soil_monitor_battery_3": {
|
||||
"name": "Soil monitor battery 3"
|
||||
},
|
||||
"soil_monitor_battery_4": {
|
||||
"name": "Soil monitor battery 4"
|
||||
},
|
||||
"soil_monitor_battery_5": {
|
||||
"name": "Soil monitor battery 5"
|
||||
},
|
||||
"soil_monitor_battery_6": {
|
||||
"name": "Soil monitor battery 6"
|
||||
},
|
||||
"soil_monitor_battery_7": {
|
||||
"name": "Soil monitor battery 7"
|
||||
},
|
||||
"soil_monitor_battery_8": {
|
||||
"name": "Soil monitor battery 8"
|
||||
},
|
||||
"soil_monitor_battery_9": {
|
||||
"name": "Soil monitor battery 9"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"24_hour_rain": {
|
||||
"name": "Rain 24 hours"
|
||||
},
|
||||
"pm25_aqi": {
|
||||
"name": "PM2.5 AQI"
|
||||
},
|
||||
"pm25_aqi_24h_average": {
|
||||
"name": "PM2.5 AQI 24 hour average"
|
||||
},
|
||||
"pm25_indoor_aqi": {
|
||||
"name": "PM2.5 indoor AQI"
|
||||
},
|
||||
"pm25_indoor_aqi_24h_average": {
|
||||
"name": "PM2.5 indoor AQI"
|
||||
},
|
||||
"absolute_pressure": {
|
||||
"name": "Absolute pressure"
|
||||
},
|
||||
"relative_pressure": {
|
||||
"name": "Relative pressure"
|
||||
},
|
||||
"daily_rain": {
|
||||
"name": "Daily rain"
|
||||
},
|
||||
@@ -186,6 +171,9 @@
|
||||
"humidity_1": {
|
||||
"name": "Humidity 1"
|
||||
},
|
||||
"humidity_10": {
|
||||
"name": "Humidity 10"
|
||||
},
|
||||
"humidity_2": {
|
||||
"name": "Humidity 2"
|
||||
},
|
||||
@@ -210,14 +198,11 @@
|
||||
"humidity_9": {
|
||||
"name": "Humidity 9"
|
||||
},
|
||||
"humidity_10": {
|
||||
"name": "Humidity 10"
|
||||
},
|
||||
"humidity_indoor": {
|
||||
"name": "Humidity indoor"
|
||||
},
|
||||
"last_rain": {
|
||||
"name": "Last rain"
|
||||
"inside_temperature": {
|
||||
"name": "Inside temperature"
|
||||
},
|
||||
"last_lightning_strike": {
|
||||
"name": "Last Lightning strike"
|
||||
@@ -225,6 +210,12 @@
|
||||
"last_lightning_strike_distance": {
|
||||
"name": "Last Lightning strike distance"
|
||||
},
|
||||
"last_rain": {
|
||||
"name": "Last rain"
|
||||
},
|
||||
"lifetime_rain": {
|
||||
"name": "Lifetime rain"
|
||||
},
|
||||
"lightning_strikes_per_day": {
|
||||
"name": "Lightning strikes per day"
|
||||
},
|
||||
@@ -240,15 +231,33 @@
|
||||
"pm25_24h_average": {
|
||||
"name": "PM2.5 24 hour average"
|
||||
},
|
||||
"pm25_aqi": {
|
||||
"name": "PM2.5 AQI"
|
||||
},
|
||||
"pm25_aqi_24h_average": {
|
||||
"name": "PM2.5 AQI 24 hour average"
|
||||
},
|
||||
"pm25_indoor": {
|
||||
"name": "PM2.5 indoor"
|
||||
},
|
||||
"pm25_indoor_24h_average": {
|
||||
"name": "PM2.5 indoor 24 hour average"
|
||||
},
|
||||
"pm25_indoor_aqi": {
|
||||
"name": "PM2.5 indoor AQI"
|
||||
},
|
||||
"pm25_indoor_aqi_24h_average": {
|
||||
"name": "PM2.5 indoor AQI"
|
||||
},
|
||||
"relative_pressure": {
|
||||
"name": "Relative pressure"
|
||||
},
|
||||
"soil_humidity_1": {
|
||||
"name": "Soil humidity 1"
|
||||
},
|
||||
"soil_humidity_10": {
|
||||
"name": "Soil humidity 10"
|
||||
},
|
||||
"soil_humidity_2": {
|
||||
"name": "Soil humidity 2"
|
||||
},
|
||||
@@ -273,12 +282,12 @@
|
||||
"soil_humidity_9": {
|
||||
"name": "Soil humidity 9"
|
||||
},
|
||||
"soil_humidity_10": {
|
||||
"name": "Soil humidity 10"
|
||||
},
|
||||
"soil_temperature_1": {
|
||||
"name": "Soil temperature 1"
|
||||
},
|
||||
"soil_temperature_10": {
|
||||
"name": "Soil temperature 10"
|
||||
},
|
||||
"soil_temperature_2": {
|
||||
"name": "Soil temperature 2"
|
||||
},
|
||||
@@ -303,12 +312,12 @@
|
||||
"soil_temperature_9": {
|
||||
"name": "Soil temperature 9"
|
||||
},
|
||||
"soil_temperature_10": {
|
||||
"name": "Soil temperature 10"
|
||||
},
|
||||
"temperature_1": {
|
||||
"name": "Temperature 1"
|
||||
},
|
||||
"temperature_10": {
|
||||
"name": "Temperature 10"
|
||||
},
|
||||
"temperature_2": {
|
||||
"name": "Temperature 2"
|
||||
},
|
||||
@@ -333,21 +342,18 @@
|
||||
"temperature_9": {
|
||||
"name": "Temperature 9"
|
||||
},
|
||||
"temperature_10": {
|
||||
"name": "Temperature 10"
|
||||
},
|
||||
"inside_temperature": {
|
||||
"name": "Inside temperature"
|
||||
},
|
||||
"lifetime_rain": {
|
||||
"name": "Lifetime rain"
|
||||
},
|
||||
"uv_index": {
|
||||
"name": "UV index"
|
||||
},
|
||||
"weekly_rain": {
|
||||
"name": "Weekly rain"
|
||||
},
|
||||
"wind_average_10m": {
|
||||
"name": "Wind average 10 minutes"
|
||||
},
|
||||
"wind_average_2m": {
|
||||
"name": "Wind average 2 minutes"
|
||||
},
|
||||
"wind_direction": {
|
||||
"name": "Wind direction"
|
||||
},
|
||||
@@ -357,17 +363,11 @@
|
||||
"wind_direction_average_2m": {
|
||||
"name": "Wind direction average 2 minutes"
|
||||
},
|
||||
"wind_gust_direction": {
|
||||
"name": "Wind gust direction"
|
||||
},
|
||||
"wind_gust": {
|
||||
"name": "Wind gust"
|
||||
},
|
||||
"wind_average_10m": {
|
||||
"name": "Wind average 10 minutes"
|
||||
},
|
||||
"wind_average_2m": {
|
||||
"name": "Wind average 2 minutes"
|
||||
"wind_gust_direction": {
|
||||
"name": "Wind gust direction"
|
||||
},
|
||||
"yearly_rain": {
|
||||
"name": "Yearly rain"
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
{
|
||||
"services": {
|
||||
"enable_recording": {
|
||||
"service": "mdi:record-rec"
|
||||
"disable_audio": {
|
||||
"service": "mdi:volume-off"
|
||||
},
|
||||
"disable_motion_recording": {
|
||||
"service": "mdi:motion-sensor-off"
|
||||
},
|
||||
"disable_recording": {
|
||||
"service": "mdi:stop"
|
||||
@@ -9,18 +12,18 @@
|
||||
"enable_audio": {
|
||||
"service": "mdi:volume-high"
|
||||
},
|
||||
"disable_audio": {
|
||||
"service": "mdi:volume-off"
|
||||
},
|
||||
"enable_motion_recording": {
|
||||
"service": "mdi:motion-sensor"
|
||||
},
|
||||
"disable_motion_recording": {
|
||||
"service": "mdi:motion-sensor-off"
|
||||
"enable_recording": {
|
||||
"service": "mdi:record-rec"
|
||||
},
|
||||
"goto_preset": {
|
||||
"service": "mdi:pan"
|
||||
},
|
||||
"ptz_control": {
|
||||
"service": "mdi:pan"
|
||||
},
|
||||
"set_color_bw": {
|
||||
"service": "mdi:palette"
|
||||
},
|
||||
@@ -29,9 +32,6 @@
|
||||
},
|
||||
"stop_tour": {
|
||||
"service": "mdi:panorama-outline"
|
||||
},
|
||||
"ptz_control": {
|
||||
"service": "mdi:pan"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,130 +1,130 @@
|
||||
{
|
||||
"services": {
|
||||
"enable_recording": {
|
||||
"name": "Enable recording",
|
||||
"description": "Enables continuous recording to camera storage.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "Entity",
|
||||
"description": "Name(s) of the cameras, or 'all' for all cameras."
|
||||
}
|
||||
}
|
||||
},
|
||||
"disable_recording": {
|
||||
"name": "Disable recording",
|
||||
"description": "Disables continuous recording to camera storage.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]",
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"enable_audio": {
|
||||
"name": "Enable audio",
|
||||
"description": "Enables audio stream.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]",
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"disable_audio": {
|
||||
"name": "Disable audio",
|
||||
"description": "Disables audio stream.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]",
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]"
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]",
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"enable_motion_recording": {
|
||||
"name": "Enable motion recording",
|
||||
"description": "Enables recording a clip to camera storage when motion is detected.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]",
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Disable audio"
|
||||
},
|
||||
"disable_motion_recording": {
|
||||
"name": "Disable motion recording",
|
||||
"description": "Disables recording a clip to camera storage when motion is detected.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]",
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]"
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]",
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Disable motion recording"
|
||||
},
|
||||
"disable_recording": {
|
||||
"description": "Disables continuous recording to camera storage.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]",
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Disable recording"
|
||||
},
|
||||
"enable_audio": {
|
||||
"description": "Enables audio stream.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]",
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Enable audio"
|
||||
},
|
||||
"enable_motion_recording": {
|
||||
"description": "Enables recording a clip to camera storage when motion is detected.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]",
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Enable motion recording"
|
||||
},
|
||||
"enable_recording": {
|
||||
"description": "Enables continuous recording to camera storage.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"description": "Name(s) of the cameras, or 'all' for all cameras.",
|
||||
"name": "Entity"
|
||||
}
|
||||
},
|
||||
"name": "Enable recording"
|
||||
},
|
||||
"goto_preset": {
|
||||
"name": "Go to preset",
|
||||
"description": "Moves camera to PTZ preset.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]",
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]"
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]",
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]"
|
||||
},
|
||||
"preset": {
|
||||
"name": "Preset",
|
||||
"description": "Preset number."
|
||||
"description": "Preset number.",
|
||||
"name": "Preset"
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_color_bw": {
|
||||
"name": "Set color",
|
||||
"description": "Sets camera color mode.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]",
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]"
|
||||
},
|
||||
"color_bw": {
|
||||
"name": "Color",
|
||||
"description": "Color mode."
|
||||
}
|
||||
}
|
||||
},
|
||||
"start_tour": {
|
||||
"name": "Start tour",
|
||||
"description": "Starts camera's PTZ tour function.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]",
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"stop_tour": {
|
||||
"name": "Stop tour",
|
||||
"description": "Stops camera's PTZ tour function.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]",
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "Go to preset"
|
||||
},
|
||||
"ptz_control": {
|
||||
"name": "PTZ control",
|
||||
"description": "Moves (pan/tilt) and/or zoom a PTZ camera.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]",
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]"
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]",
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]"
|
||||
},
|
||||
"movement": {
|
||||
"name": "Movement",
|
||||
"description": "Direction to move the camera."
|
||||
"description": "Direction to move the camera.",
|
||||
"name": "Movement"
|
||||
},
|
||||
"travel_time": {
|
||||
"name": "Travel time",
|
||||
"description": "Travel time in fractional seconds: from 0 to 1."
|
||||
"description": "Travel time in fractional seconds: from 0 to 1.",
|
||||
"name": "Travel time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"name": "PTZ control"
|
||||
},
|
||||
"set_color_bw": {
|
||||
"description": "Sets camera color mode.",
|
||||
"fields": {
|
||||
"color_bw": {
|
||||
"description": "Color mode.",
|
||||
"name": "Color"
|
||||
},
|
||||
"entity_id": {
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]",
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Set color"
|
||||
},
|
||||
"start_tour": {
|
||||
"description": "Starts camera's PTZ tour function.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]",
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Start tour"
|
||||
},
|
||||
"stop_tour": {
|
||||
"description": "Stops camera's PTZ tour function.",
|
||||
"fields": {
|
||||
"entity_id": {
|
||||
"description": "[%key:component::amcrest::services::enable_recording::fields::entity_id::description%]",
|
||||
"name": "[%key:component::amcrest::services::enable_recording::fields::entity_id::name%]"
|
||||
}
|
||||
},
|
||||
"name": "Stop tour"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,7 +629,6 @@ async def async_devices_payload(hass: HomeAssistant) -> dict: # noqa: C901
|
||||
|
||||
devices_info.append(
|
||||
{
|
||||
"entities": [],
|
||||
"entry_type": device_entry.entry_type,
|
||||
"has_configuration_url": device_entry.configuration_url is not None,
|
||||
"hw_version": device_entry.hw_version,
|
||||
@@ -638,6 +637,7 @@ async def async_devices_payload(hass: HomeAssistant) -> dict: # noqa: C901
|
||||
"model_id": device_entry.model_id,
|
||||
"sw_version": device_entry.sw_version,
|
||||
"via_device": device_entry.via_device_id,
|
||||
"entities": [],
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,47 +1,25 @@
|
||||
{
|
||||
"config": {
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"tracked_addons": "Add-ons",
|
||||
"tracked_integrations": "Integrations",
|
||||
"tracked_custom_integrations": "Custom integrations"
|
||||
},
|
||||
"data_description": {
|
||||
"tracked_addons": "Select the add-ons you want to track",
|
||||
"tracked_integrations": "Select the integrations you want to track",
|
||||
"tracked_custom_integrations": "Select the custom integrations you want to track"
|
||||
}
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]",
|
||||
"unknown": "[%key:common::config_flow::error::unknown%]"
|
||||
},
|
||||
"error": {
|
||||
"no_integrations_selected": "You must select at least one integration to track"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
},
|
||||
"step": {
|
||||
"init": {
|
||||
"user": {
|
||||
"data": {
|
||||
"tracked_addons": "[%key:component::analytics_insights::config::step::user::data::tracked_addons%]",
|
||||
"tracked_integrations": "[%key:component::analytics_insights::config::step::user::data::tracked_integrations%]",
|
||||
"tracked_custom_integrations": "[%key:component::analytics_insights::config::step::user::data::tracked_custom_integrations%]"
|
||||
"tracked_addons": "Add-ons",
|
||||
"tracked_custom_integrations": "Custom integrations",
|
||||
"tracked_integrations": "Integrations"
|
||||
},
|
||||
"data_description": {
|
||||
"tracked_addons": "[%key:component::analytics_insights::config::step::user::data_description::tracked_addons%]",
|
||||
"tracked_integrations": "[%key:component::analytics_insights::config::step::user::data_description::tracked_integrations%]",
|
||||
"tracked_custom_integrations": "[%key:component::analytics_insights::config::step::user::data_description::tracked_custom_integrations%]"
|
||||
"tracked_addons": "Select the add-ons you want to track",
|
||||
"tracked_custom_integrations": "Select the custom integrations you want to track",
|
||||
"tracked_integrations": "Select the integrations you want to track"
|
||||
}
|
||||
}
|
||||
},
|
||||
"abort": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"error": {
|
||||
"no_integrations_selected": "[%key:component::analytics_insights::config::error::no_integrations_selected%]"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
@@ -56,5 +34,27 @@
|
||||
"name": "Total reported integrations"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"abort": {
|
||||
"cannot_connect": "[%key:common::config_flow::error::cannot_connect%]"
|
||||
},
|
||||
"error": {
|
||||
"no_integrations_selected": "[%key:component::analytics_insights::config::error::no_integrations_selected%]"
|
||||
},
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"tracked_addons": "[%key:component::analytics_insights::config::step::user::data::tracked_addons%]",
|
||||
"tracked_custom_integrations": "[%key:component::analytics_insights::config::step::user::data::tracked_custom_integrations%]",
|
||||
"tracked_integrations": "[%key:component::analytics_insights::config::step::user::data::tracked_integrations%]"
|
||||
},
|
||||
"data_description": {
|
||||
"tracked_addons": "[%key:component::analytics_insights::config::step::user::data_description::tracked_addons%]",
|
||||
"tracked_custom_integrations": "[%key:component::analytics_insights::config::step::user::data_description::tracked_custom_integrations%]",
|
||||
"tracked_integrations": "[%key:component::analytics_insights::config::step::user::data_description::tracked_integrations%]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user