mirror of
https://github.com/Koenkk/zigbee2mqtt.git
synced 2026-08-29 23:28:34 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ffc2ff1d0e | ||
|
|
3aadab0925 | ||
|
|
85c00b8b43 | ||
|
|
e51792c65a | ||
|
|
1e74b2f53f | ||
|
|
886b5345d7 | ||
|
|
7d8b5374ad | ||
|
|
0c7730f4c5 | ||
|
|
a7aab714ba | ||
|
|
032b9a096e | ||
|
|
1f3d61dc7f | ||
|
|
32b173c653 | ||
|
|
8c4cd8c9aa | ||
|
|
73423ebd60 | ||
|
|
334e260b85 | ||
|
|
731957dab9 | ||
|
|
9c505fd75f | ||
|
|
25cec20f0d | ||
|
|
f05d501380 | ||
|
|
b02c34c6e5 | ||
|
|
801134b630 | ||
|
|
73d43007d4 | ||
|
|
1a9e05e6ab | ||
|
|
1006725b09 | ||
|
|
a4f9c49ab5 | ||
|
|
658622880f | ||
|
|
720fc491ce | ||
|
|
56feb77f2d | ||
|
|
233d263e13 | ||
|
|
dbd616fbe5 | ||
|
|
86c2e90e06 | ||
|
|
43610bbff4 | ||
|
|
29543bad4b | ||
|
|
2a047a469c | ||
|
|
83502f2d30 | ||
|
|
b60f679814 | ||
|
|
14e044093c | ||
|
|
3188038a7b | ||
|
|
ebf996acd8 | ||
|
|
0e0eccbd13 | ||
|
|
e834e93deb | ||
|
|
d37c164993 | ||
|
|
6daa7aa61d | ||
|
|
1110d3f8ef | ||
|
|
1d73d9d476 | ||
|
|
2da80954d6 | ||
|
|
e2fa66a38c | ||
|
|
94382ee418 | ||
|
|
94c92ef021 | ||
|
|
8c86849bb9 | ||
|
|
30909d57fb | ||
|
|
aaed8ab95e | ||
|
|
f24b20b133 | ||
|
|
2a53b8e59b | ||
|
|
ccaa1281a3 | ||
|
|
01843fe6c4 | ||
|
|
66fb672492 | ||
|
|
38ed953599 | ||
|
|
86ed71cb1f | ||
|
|
ccc31670ec | ||
|
|
0d8f5780dd | ||
|
|
a8a262640f | ||
|
|
72333f0888 | ||
|
|
a84083be40 | ||
|
|
9b8583e778 | ||
|
|
aeaa040920 | ||
|
|
222b67b1b0 | ||
|
|
1ee1710a96 | ||
|
|
a1e2ee7837 | ||
|
|
f04ade6d76 | ||
|
|
fee0811893 | ||
|
|
956003aa0a | ||
|
|
8fb28f9e2f | ||
|
|
a06a0ea059 | ||
|
|
4f71c1e8bf | ||
|
|
c56470c83f | ||
|
|
cc27032752 | ||
|
|
7c97902d4a | ||
|
|
23959c5b0e | ||
|
|
e726d5907e | ||
|
|
1337f54e4a | ||
|
|
471902e531 | ||
|
|
22d33360f6 | ||
|
|
db1e3b73d1 | ||
|
|
fe73db4ff6 | ||
|
|
9dd6eeee7b | ||
|
|
3c609658fd | ||
|
|
8b694432aa | ||
|
|
b32bd309ca | ||
|
|
78b5e6fd4b | ||
|
|
c5c0a8b193 | ||
|
|
b51eef2319 | ||
|
|
ebed9d31d7 | ||
|
|
16b251d3c9 | ||
|
|
5766bf479b | ||
|
|
8a9477f2bb | ||
|
|
2aaf878889 | ||
|
|
dfa499659e | ||
|
|
24b5f9b43a | ||
|
|
0ab7390a64 | ||
|
|
c7b820c376 | ||
|
|
ce41f2f8ba | ||
|
|
8bc3c46c34 | ||
|
|
b4473d6e24 | ||
|
|
910f3a6860 | ||
|
|
81335e5b4b | ||
|
|
55653c974e | ||
|
|
a5a87a79a8 | ||
|
|
a479bf9039 | ||
|
|
b7e19cc86a | ||
|
|
fe0742a628 | ||
|
|
29bae3a734 | ||
|
|
6aa6f04059 | ||
|
|
fc6d74e2b7 | ||
|
|
92e0d7895b | ||
|
|
250d5bfc24 | ||
|
|
dfda63138f | ||
|
|
8ab6c16f6e | ||
|
|
a6d51a60a8 | ||
|
|
3d37892296 | ||
|
|
1df54f071f | ||
|
|
b3253bacc4 | ||
|
|
4b1721721b | ||
|
|
2db07236db | ||
|
|
7295955397 | ||
|
|
f83709d96c | ||
|
|
a678a1c652 | ||
|
|
3d1336dc6c | ||
|
|
da1d0d81c2 | ||
|
|
335e18fe98 | ||
|
|
6e2539af09 | ||
|
|
fdc9f7d4d4 | ||
|
|
08c03e48cc | ||
|
|
6621f6fd91 | ||
|
|
06b3395cff | ||
|
|
0b59a51c3c | ||
|
|
1cee011ca0 | ||
|
|
630b4ec560 | ||
|
|
4b6f50daf3 | ||
|
|
0361b13b66 | ||
|
|
49dea233b6 | ||
|
|
c16abdd832 | ||
|
|
c37f3e834e | ||
|
|
f3e79df4b4 | ||
|
|
03ba745e46 | ||
|
|
7e7d3c6328 | ||
|
|
59001babca | ||
|
|
0d716288bf | ||
|
|
a879382cbd | ||
|
|
a858798054 | ||
|
|
416e561b32 | ||
|
|
a240487bb4 | ||
|
|
518972882c | ||
|
|
ed639ec9d7 | ||
|
|
6372d84259 | ||
|
|
45579dcf10 | ||
|
|
97eac16aaf | ||
|
|
032557b1db | ||
|
|
de7bc7cf60 | ||
|
|
92588fefe3 | ||
|
|
8de20e2cf1 | ||
|
|
a1360d8e90 | ||
|
|
9f3b10235c | ||
|
|
1d6d386a47 | ||
|
|
76ea57b732 | ||
|
|
e76f568f0f | ||
|
|
080f3b6b50 | ||
|
|
a741b2f598 | ||
|
|
ac74fac1db | ||
|
|
ab9124a517 | ||
|
|
4e997c7b83 | ||
|
|
0007db42b0 | ||
|
|
6ccbb11fc2 | ||
|
|
4fe4e2ec2f | ||
|
|
2bd7df84eb | ||
|
|
ec3e37350d | ||
|
|
769bc14163 | ||
|
|
a38b88455f | ||
|
|
9a9bcd503e | ||
|
|
f58ee7ad58 | ||
|
|
3c9620428e | ||
|
|
2bf244c2de | ||
|
|
5c8f95ae01 | ||
|
|
363792aee8 | ||
|
|
ce7d022fda | ||
|
|
52f25eb101 | ||
|
|
ef84ff86fe | ||
|
|
dfbe93ef9d | ||
|
|
6bbe8d1844 | ||
|
|
9eaaa0f9d1 | ||
|
|
0807049b6c | ||
|
|
759c235923 | ||
|
|
14ac9a8f68 | ||
|
|
d942db9a92 | ||
|
|
212c70913c | ||
|
|
b17c22bb50 | ||
|
|
c8c5ea2de3 | ||
|
|
00230baedf | ||
|
|
540969be21 | ||
|
|
804182e30c | ||
|
|
99a57bfd08 | ||
|
|
b8edf273c6 | ||
|
|
e9aee4c6dc | ||
|
|
e6bcf91e17 | ||
|
|
7e37365a5f | ||
|
|
32b1d5b073 | ||
|
|
cb7b773e0d | ||
|
|
84387b27a4 | ||
|
|
3d8f0fc3a8 | ||
|
|
02a587f745 | ||
|
|
b5a90e921b | ||
|
|
32b0ded17c | ||
|
|
dee8781e51 | ||
|
|
64fd21c9c2 | ||
|
|
f9e0c2058f | ||
|
|
16730b14a1 | ||
|
|
05c65d31ed | ||
|
|
a8c791b030 | ||
|
|
a3a87f4580 | ||
|
|
ffa43915f0 | ||
|
|
7bca19dae7 | ||
|
|
6b6cc8aa34 | ||
|
|
03a1ce3895 | ||
|
|
82fea1fe41 | ||
|
|
084b048ff3 | ||
|
|
085b46c995 | ||
|
|
eaaf1b0b4f | ||
|
|
265d280efc | ||
|
|
e6e3d92971 | ||
|
|
bf744f48dc | ||
|
|
ec06efe86a | ||
|
|
56f57625b0 | ||
|
|
2110954bb8 | ||
|
|
d616e57daf | ||
|
|
d702919735 | ||
|
|
fe7e82f7d3 | ||
|
|
03857916c4 | ||
|
|
1a7265017a | ||
|
|
89f8e43ad1 | ||
|
|
6875146211 | ||
|
|
27ccd1a570 | ||
|
|
a5b959a6d5 | ||
|
|
b67888f938 | ||
|
|
af8c2d86fe | ||
|
|
29635a9b6a | ||
|
|
65d7f5f3e2 | ||
|
|
c26a6a28e4 | ||
|
|
19754c69e5 | ||
|
|
1e4ac34c44 | ||
|
|
89e6d307f2 | ||
|
|
e7e9616f7e | ||
|
|
6e52b2a7af | ||
|
|
2e704f3ff7 | ||
|
|
8f62a0a618 | ||
|
|
e0f50218d7 | ||
|
|
2c5d3e14f7 | ||
|
|
851e3f559a | ||
|
|
cefd6e5685 | ||
|
|
25de4fded9 | ||
|
|
ca6b24304c | ||
|
|
b415443126 | ||
|
|
bcf50bd0c5 | ||
|
|
7c81eda46a | ||
|
|
abde909b71 | ||
|
|
48347a51dc | ||
|
|
f64c00c8d3 | ||
|
|
87f6b9e81a | ||
|
|
0a2d9ba952 | ||
|
|
5aeec130b1 | ||
|
|
a26008f74c | ||
|
|
c7e294482b | ||
|
|
5ff10c3e5c | ||
|
|
be17544598 | ||
|
|
8929ab3b97 | ||
|
|
d1501052ad | ||
|
|
4cc88f1e16 | ||
|
|
4e6bc33a22 | ||
|
|
81033700e2 | ||
|
|
a17f650ccb | ||
|
|
59efc15f3a | ||
|
|
cb509426ec | ||
|
|
aa8144b739 | ||
|
|
f11eb2459b | ||
|
|
cb74db47f7 | ||
|
|
299b37a721 | ||
|
|
ffcb8202f0 | ||
|
|
e60b94b553 | ||
|
|
fbabe9674c | ||
|
|
8ab96e124f | ||
|
|
483acc3ca1 | ||
|
|
e0deef8889 | ||
|
|
bbb0ea329c | ||
|
|
bf69db3e82 | ||
|
|
adc08125fe | ||
|
|
aa9a916d61 | ||
|
|
5e3bd75f09 | ||
|
|
04d8522b3b | ||
|
|
dd1e0db407 | ||
|
|
2fa8afe37f | ||
|
|
5afd16ac65 | ||
|
|
9a2425a202 | ||
|
|
af02370777 | ||
|
|
9c8aee17dc | ||
|
|
a822873748 | ||
|
|
6e843d0f1c | ||
|
|
346cb90e9e | ||
|
|
6619146028 | ||
|
|
56589dccf1 | ||
|
|
982af4b724 | ||
|
|
7a516bba16 | ||
|
|
2080e36f19 | ||
|
|
a71f43d75b | ||
|
|
c20293078a | ||
|
|
37636b243f | ||
|
|
aae7312d8a | ||
|
|
3bbf89cba0 | ||
|
|
f976d4efb4 | ||
|
|
a904e7a396 | ||
|
|
12521add39 | ||
|
|
2dabc1e61b | ||
|
|
e5dc271a78 | ||
|
|
4e128e26fe | ||
|
|
b4c0a59501 | ||
|
|
9032bb3d13 | ||
|
|
8a6d90d70e | ||
|
|
b17487e6bb | ||
|
|
838629ca42 | ||
|
|
51a93418fa | ||
|
|
ef00a64bf8 | ||
|
|
ef4d9e1e55 | ||
|
|
2d08baf620 | ||
|
|
8cf41a137a | ||
|
|
61b08c94bf | ||
|
|
c87ac9cc2b | ||
|
|
ed20e2a1bc | ||
|
|
d4e3aa9d97 | ||
|
|
91aed7e98e | ||
|
|
fd19c86cb1 | ||
|
|
ad4bed8921 | ||
|
|
01d32cb230 | ||
|
|
746066c920 | ||
|
|
4815ac7110 | ||
|
|
035e2a3023 | ||
|
|
311ea070b1 | ||
|
|
736caac46d | ||
|
|
157dff14d3 | ||
|
|
a152bded93 | ||
|
|
6587812b72 | ||
|
|
c8848be981 | ||
|
|
baae082258 | ||
|
|
c8dcd716ab | ||
|
|
45ac44dd06 | ||
|
|
da04d223b4 | ||
|
|
d26ea1a411 | ||
|
|
9d5d61754d | ||
|
|
2bb77a31cf | ||
|
|
86ae0ed90f | ||
|
|
2540fbd771 | ||
|
|
354ada5c4c | ||
|
|
0fec5958fa | ||
|
|
ccc08a3fc7 | ||
|
|
5889cdd9ab | ||
|
|
93af087264 | ||
|
|
2d5bb2462e | ||
|
|
b71aa0a3fd | ||
|
|
1fd1686ee6 | ||
|
|
fc073e8743 | ||
|
|
be48d545cc | ||
|
|
13443a42ce | ||
|
|
071842ef49 | ||
|
|
366e29d4d9 | ||
|
|
02a5a1b14f | ||
|
|
64b26e47da | ||
|
|
1f2bd841c4 | ||
|
|
1e905598f2 | ||
|
|
e76b0ff282 | ||
|
|
9996c931e2 | ||
|
|
35386903f2 | ||
|
|
d79235e27b | ||
|
|
69f6891db5 | ||
|
|
fe25924780 | ||
|
|
0b3776916b | ||
|
|
98c7a2ab56 | ||
|
|
e2fccc219b | ||
|
|
6c7be58a3f | ||
|
|
2541feac25 | ||
|
|
7e82a491f6 | ||
|
|
e7749e25e9 | ||
|
|
d0a4f5f628 | ||
|
|
1fa10f6f28 | ||
|
|
f73cca0e39 | ||
|
|
0e55b382ba | ||
|
|
4d8c6b2856 | ||
|
|
52a545d86e | ||
|
|
08a5f32ea9 | ||
|
|
71dd0ab772 | ||
|
|
6d387b31c1 | ||
|
|
17ad362fa7 | ||
|
|
0cd99487cc | ||
|
|
e36983b4b5 | ||
|
|
62f5dc875a | ||
|
|
51f15bd306 | ||
|
|
8a28154661 | ||
|
|
ff85c63f29 | ||
|
|
a86f3d8765 | ||
|
|
5d1bd256f9 | ||
|
|
3461f11869 | ||
|
|
43a5c7b2f0 | ||
|
|
81a8df9855 | ||
|
|
31541bd6ba | ||
|
|
70b0fed2de | ||
|
|
cc86e76a7a | ||
|
|
29247e19ac | ||
|
|
afb83fcbd0 | ||
|
|
a0c13c1c1e | ||
|
|
d027b85200 | ||
|
|
bd57f45929 | ||
|
|
9cff19b322 | ||
|
|
a9e320e029 | ||
|
|
95d22c0822 | ||
|
|
2aae1bd54a | ||
|
|
9615faac2f | ||
|
|
cdce6b11d2 | ||
|
|
de7736a370 | ||
|
|
fcb0af00d3 | ||
|
|
daaade45d9 | ||
|
|
b085fac50f | ||
|
|
0b60cd1100 | ||
|
|
32c1bdfccb | ||
|
|
b261f2c6c1 | ||
|
|
8cda915637 | ||
|
|
de0f060378 | ||
|
|
0edc0cda94 | ||
|
|
7e63039144 | ||
|
|
b990ed9a60 | ||
|
|
ccc0f7ce53 | ||
|
|
83e331323c | ||
|
|
45ce494951 | ||
|
|
1c7bb8fc24 | ||
|
|
90763fd83f | ||
|
|
7ccb4bcafe | ||
|
|
a7bb3fc9e8 | ||
|
|
1190a342e9 | ||
|
|
31e4e8c552 | ||
|
|
4d53ac4c84 | ||
|
|
d99da63b89 | ||
|
|
9a7aeaf62b | ||
|
|
9b66ab1367 | ||
|
|
e3d1c63463 | ||
|
|
af209b6f53 | ||
|
|
7b558fef5f | ||
|
|
eac9240e06 | ||
|
|
84f9950556 | ||
|
|
5477163727 | ||
|
|
0a69bb54c8 | ||
|
|
4040840d8e | ||
|
|
cc95c85969 | ||
|
|
3b64ea9d92 | ||
|
|
08a1468dda | ||
|
|
6de7662ca8 | ||
|
|
4daddfc5f7 | ||
|
|
d6ed0f45da | ||
|
|
ea8de2ba1e | ||
|
|
f509410189 | ||
|
|
91a9e32a83 | ||
|
|
914b4c6757 | ||
|
|
03f381fdc2 | ||
|
|
3285850efa | ||
|
|
6654c7f1a3 | ||
|
|
d280e5ef2b | ||
|
|
f224ef7b7c | ||
|
|
0543ad4b19 | ||
|
|
9165bbb7b4 | ||
|
|
f4ef421d27 | ||
|
|
7ee207fc0e | ||
|
|
e78aa3589b | ||
|
|
68169f0e50 | ||
|
|
97f6a840e4 | ||
|
|
9a6445a8a0 | ||
|
|
0ec68a4b71 | ||
|
|
5084b1293e | ||
|
|
4949a54d66 | ||
|
|
b193d89042 | ||
|
|
bc17e6406b | ||
|
|
d1e50ce534 | ||
|
|
677db105e2 | ||
|
|
514d9f38b2 | ||
|
|
704ecf366e | ||
|
|
1439f4c6ac | ||
|
|
6a1b6a1403 | ||
|
|
239c8fc70c | ||
|
|
e33e335c0c | ||
|
|
71c4211d13 | ||
|
|
4b3a2c9f5a | ||
|
|
7185d9e501 | ||
|
|
dcbf4e5b0e | ||
|
|
db812cd729 | ||
|
|
c214f83298 | ||
|
|
3736b5901b | ||
|
|
3af130a2c0 | ||
|
|
145bc05dc3 | ||
|
|
cc0536c5ab | ||
|
|
0d00a144f5 | ||
|
|
aa021d988d | ||
|
|
ea147900ab | ||
|
|
b1796e3e1a | ||
|
|
b55cc0a037 | ||
|
|
8675b1c9ed | ||
|
|
b09b8ec3d6 | ||
|
|
7f05f31c72 | ||
|
|
53cb42103c | ||
|
|
a892a1b898 | ||
|
|
1f52faee39 | ||
|
|
e0038380c7 | ||
|
|
6050428566 | ||
|
|
b5e9ed436c | ||
|
|
cf313c503b | ||
|
|
abba8b3ba8 | ||
|
|
f08cd81ac0 | ||
|
|
68ec507e30 | ||
|
|
0718ca162d | ||
|
|
2d917a7d0d | ||
|
|
ed22ecedbf | ||
|
|
f6196107a7 | ||
|
|
49920ce093 | ||
|
|
33543c8588 | ||
|
|
c1413afa04 | ||
|
|
df93e4c8f9 | ||
|
|
7709af8a5e | ||
|
|
1ec1e57244 | ||
|
|
758b8144a1 | ||
|
|
590564c158 | ||
|
|
ea412cfe82 | ||
|
|
fa02bb5b74 | ||
|
|
683d29f9f7 | ||
|
|
3478e40017 | ||
|
|
ba0cc61965 | ||
|
|
8139d7f1f4 | ||
|
|
d487f37a7a | ||
|
|
087b85cb9f | ||
|
|
4bd281ce6c | ||
|
|
ced3d43dad | ||
|
|
91f22f1230 | ||
|
|
57d031d54a | ||
|
|
8e946623a9 | ||
|
|
e1d52ae43a | ||
|
|
f64cd7e8f3 | ||
|
|
df8e1687b2 | ||
|
|
3408e3c05a | ||
|
|
7a22cee639 | ||
|
|
dec91919bc | ||
|
|
313865b0a3 | ||
|
|
ee1a5f0400 | ||
|
|
7f2803b020 | ||
|
|
83205e43da | ||
|
|
45e127e22f | ||
|
|
d22d45a697 | ||
|
|
330a913e76 | ||
|
|
af0521b54c | ||
|
|
8cf8cc0231 | ||
|
|
f5f5b037b8 | ||
|
|
1252f10d39 | ||
|
|
a3c8e1c3c9 | ||
|
|
073989c7a4 | ||
|
|
05eff957eb | ||
|
|
3c855d2042 | ||
|
|
0bdf663ca1 | ||
|
|
813dd49a41 | ||
|
|
dab7e6a1ae | ||
|
|
b73c3e3dfb | ||
|
|
4ccfdfc6f4 | ||
|
|
15601d6339 | ||
|
|
1046f0e74a | ||
|
|
032e5238b8 | ||
|
|
5b3fdf7b9f | ||
|
|
0489e75e1e | ||
|
|
eb655f17db | ||
|
|
d8c7c00ca2 | ||
|
|
9aa7e1ec86 | ||
|
|
caa652b0ad | ||
|
|
08e82e4980 | ||
|
|
5067721fdf | ||
|
|
b014857a13 | ||
|
|
eb7945248b | ||
|
|
69638d7956 | ||
|
|
953f3a232b | ||
|
|
d1824f169b | ||
|
|
81973c3a2a | ||
|
|
213e2b50d6 | ||
|
|
0a63ae5376 | ||
|
|
61dd1dd96d | ||
|
|
ed85367e53 | ||
|
|
38c2ea5bf7 | ||
|
|
2d62027e24 | ||
|
|
a56719d19c | ||
|
|
94744525ba | ||
|
|
ea193f1938 | ||
|
|
88339473ce | ||
|
|
57566c76ed | ||
|
|
a09342836b | ||
|
|
b3fbf78bec | ||
|
|
a744838e9c | ||
|
|
712c7bdbaf | ||
|
|
6d3389b409 | ||
|
|
03005b38c8 | ||
|
|
17cff2a26f | ||
|
|
5777d76e55 | ||
|
|
91d67a0ef5 | ||
|
|
e083a6bff7 | ||
|
|
57aaa5a85a | ||
|
|
34bad92ec6 | ||
|
|
b6a4acda9b | ||
|
|
1dc0b24e9a | ||
|
|
38de19bb2c | ||
|
|
c47f5df021 | ||
|
|
4ad838c114 | ||
|
|
b0b3aee648 | ||
|
|
446dbbf1fc | ||
|
|
11f0be55d8 | ||
|
|
e10dd893ab | ||
|
|
0dd899679d | ||
|
|
05cab61e1f | ||
|
|
0c7e7e5089 | ||
|
|
5f71776434 | ||
|
|
a7e02a7be8 | ||
|
|
7df6b2bab5 | ||
|
|
21f51258a9 | ||
|
|
c6904686c3 | ||
|
|
a1bea8c5ab | ||
|
|
c23d244405 | ||
|
|
86898e5ceb | ||
|
|
3572c60770 | ||
|
|
ffc7ab83a2 | ||
|
|
c12bedb3e5 | ||
|
|
fd1622ba80 | ||
|
|
7564019ecd | ||
|
|
6d381da075 | ||
|
|
b10aa83fc8 | ||
|
|
a7997a3cd4 | ||
|
|
73c19bff71 | ||
|
|
80aa21123c | ||
|
|
afd1760b6f | ||
|
|
ddcab827ad | ||
|
|
d0a41548fd | ||
|
|
ff8e6b9c66 | ||
|
|
25f30a94f5 | ||
|
|
efe451df79 | ||
|
|
b97a09c75b | ||
|
|
f3b8c689a9 | ||
|
|
4d1a8ee873 | ||
|
|
f440c86019 | ||
|
|
4b20dfafab | ||
|
|
b228d9daf6 | ||
|
|
be82b737be | ||
|
|
d119f609de | ||
|
|
a71857c723 | ||
|
|
a404f9ff92 | ||
|
|
0cbbf40040 | ||
|
|
277ac10e44 | ||
|
|
2e8dfbb96e | ||
|
|
366642cf71 | ||
|
|
b7b3b51cfe | ||
|
|
e6f9aeda06 | ||
|
|
bad365b9a7 | ||
|
|
1f8a68d4c9 | ||
|
|
443d5ea43c | ||
|
|
af0cea0fb9 | ||
|
|
3701cce15e | ||
|
|
5b807c36ec | ||
|
|
7cea62995c | ||
|
|
2a7d49b709 | ||
|
|
1b7138e0c8 | ||
|
|
0476b3362e | ||
|
|
3ed7ac90c9 | ||
|
|
8b579b6533 | ||
|
|
2d731ba1b6 | ||
|
|
6a80d187fd | ||
|
|
1e8c3d05e5 | ||
|
|
ce7f09afc6 | ||
|
|
c0e8efcde5 | ||
|
|
1344ae0616 | ||
|
|
24baed3053 | ||
|
|
8e95061fe5 | ||
|
|
626b2c47c1 | ||
|
|
95dff212e7 | ||
|
|
c6416c9c61 | ||
|
|
1898140f64 | ||
|
|
8457c3735b | ||
|
|
47c08b6cbd | ||
|
|
b3de973af6 | ||
|
|
0d17d26133 | ||
|
|
c802e776d6 | ||
|
|
aa0f78269f | ||
|
|
97b14cc2d4 | ||
|
|
d952ee60a9 | ||
|
|
3d3bff88d7 | ||
|
|
c1cb4697f7 | ||
|
|
bfc51e0dc5 | ||
|
|
d339460269 | ||
|
|
fb60deeebc | ||
|
|
1b65ff5a65 | ||
|
|
e3ebb1c5bc | ||
|
|
099a8d35b0 | ||
|
|
3085608a35 | ||
|
|
307e0dcadd | ||
|
|
aeb737012c | ||
|
|
dddd2bca88 | ||
|
|
daedc54dd6 | ||
|
|
df80a11663 | ||
|
|
a6bebf1e7a | ||
|
|
52fdbbe057 | ||
|
|
b2422589dc | ||
|
|
d602426951 | ||
|
|
64009d0dbb | ||
|
|
b8e46075df | ||
|
|
589d656594 | ||
|
|
0872c670ad | ||
|
|
9b68684428 | ||
|
|
626b8d2b08 | ||
|
|
10d111e36a | ||
|
|
eb93c8aed7 | ||
|
|
fe005e478a | ||
|
|
8f88c4d26c | ||
|
|
946887b841 | ||
|
|
b2dd21e164 | ||
|
|
01f4f1f940 | ||
|
|
563eb3a890 | ||
|
|
ab4101dae8 | ||
|
|
9ab37735fe | ||
|
|
8f275f414b | ||
|
|
55b17c95a4 | ||
|
|
13c7348a97 | ||
|
|
62453a5d2a | ||
|
|
9840433fc0 | ||
|
|
8fe36d74ec | ||
|
|
8e85a1e081 | ||
|
|
ce9a8ed86f | ||
|
|
49a1f56641 | ||
|
|
76cf6fa444 | ||
|
|
e1ecbd97d0 | ||
|
|
c389ab28cd | ||
|
|
4c0c69b15f | ||
|
|
704bbb54d3 | ||
|
|
17947aa809 | ||
|
|
33811fec9f | ||
|
|
76cd07a701 | ||
|
|
e2b18d8f02 | ||
|
|
b3c886638a | ||
|
|
f9121b2f0f | ||
|
|
fb827f9dc0 | ||
|
|
e5be5b0219 | ||
|
|
c360f5a7b1 | ||
|
|
15f741b446 | ||
|
|
3b648c9e63 | ||
|
|
951360beba | ||
|
|
71df01d613 | ||
|
|
a8f4daff30 | ||
|
|
d7dc083351 | ||
|
|
45d55c9c71 | ||
|
|
085d5c9f8b | ||
|
|
09a5650345 | ||
|
|
24050a3a10 | ||
|
|
c6a2400f56 | ||
|
|
9da7f53e78 | ||
|
|
f5a2c802ca | ||
|
|
372d991db9 | ||
|
|
bab0a3e902 | ||
|
|
b85aaf8e54 | ||
|
|
64d744ea88 | ||
|
|
7748dd3321 | ||
|
|
24c6b2e2c9 | ||
|
|
9589d12353 | ||
|
|
dcc13b7b53 | ||
|
|
2d1733d0e7 | ||
|
|
1368a78767 | ||
|
|
f29073e413 | ||
|
|
7005497cba | ||
|
|
298c595deb | ||
|
|
bdbe2b4a70 | ||
|
|
653334b208 | ||
|
|
24c6c0bf8e | ||
|
|
3e252e4307 | ||
|
|
ae2de04251 | ||
|
|
07d238d5b9 | ||
|
|
407f0df6ad | ||
|
|
2e593e37a6 | ||
|
|
9f7fd479c4 | ||
|
|
0407a3b67c | ||
|
|
2c57bf81b0 | ||
|
|
960456401e | ||
|
|
d4774526c3 | ||
|
|
2a365d39ac | ||
|
|
347c023968 | ||
|
|
4272d9a13e | ||
|
|
54900c3680 | ||
|
|
461519cf39 | ||
|
|
8edb176dc0 | ||
|
|
61cdda96ff | ||
|
|
43394b3376 | ||
|
|
ca7f9ec8ff | ||
|
|
0857d087bd | ||
|
|
818a9e3946 | ||
|
|
51460aacc8 | ||
|
|
354beb486d | ||
|
|
8261d4efc6 | ||
|
|
bbb19371f3 | ||
|
|
04ae93f00c | ||
|
|
999c01086e | ||
|
|
0e67f8fa0a | ||
|
|
f352d18854 | ||
|
|
f865c976e3 | ||
|
|
aa90765db8 | ||
|
|
017fc03157 | ||
|
|
cdf62ea560 | ||
|
|
5dcd3f67ed | ||
|
|
b013a7d8ef | ||
|
|
8e6e38b968 | ||
|
|
8f20640008 | ||
|
|
57707cd9b9 | ||
|
|
911683ad13 | ||
|
|
99204682b8 | ||
|
|
fec7fca89e | ||
|
|
6bea88eeb6 | ||
|
|
6afcb97c0d | ||
|
|
73d60396bc | ||
|
|
942faf412f | ||
|
|
b62f65f045 | ||
|
|
f5cceb1719 | ||
|
|
0b6040dd25 | ||
|
|
45fda8aefd | ||
|
|
85f32c039f | ||
|
|
7fa2b5fcc3 | ||
|
|
08a0ace11a | ||
|
|
7605e1ec06 | ||
|
|
e934b0d94e | ||
|
|
4aa8e17214 | ||
|
|
a9f5f7c620 | ||
|
|
6e193ca442 | ||
|
|
2eadf30199 | ||
|
|
600e743941 | ||
|
|
932541649e | ||
|
|
0217593c77 | ||
|
|
10e65b5f26 | ||
|
|
e9a38353fa | ||
|
|
68caf0260d | ||
|
|
7d887adb7f | ||
|
|
ec57d899e5 | ||
|
|
61b1732ecb | ||
|
|
a35705cd38 | ||
|
|
eb878d3d8e | ||
|
|
3420cf508b | ||
|
|
d5a3bf626f | ||
|
|
0e1220ff24 | ||
|
|
1496b95067 | ||
|
|
bd0694e164 | ||
|
|
d580741dfe | ||
|
|
a24586bec3 | ||
|
|
1e7a76139d | ||
|
|
ea0a9990b9 | ||
|
|
97af5a64e7 | ||
|
|
8f781db93e | ||
|
|
1a6ba18ec2 | ||
|
|
d91e363d76 | ||
|
|
f7e45b8284 | ||
|
|
bbbaaf4d89 | ||
|
|
c9076f8ce6 | ||
|
|
82fd062668 | ||
|
|
769a1afd96 | ||
|
|
f943619387 | ||
|
|
43e204b7d1 | ||
|
|
a3f1399489 | ||
|
|
4179d879d2 | ||
|
|
6ce2d2e73f | ||
|
|
981542827c | ||
|
|
2f0a2cee6e | ||
|
|
0bc685a8e3 | ||
|
|
02a99703a8 | ||
|
|
7639ce44a7 | ||
|
|
b89a99fe0c | ||
|
|
06820d2220 | ||
|
|
140213950d | ||
|
|
f4f2c498b7 | ||
|
|
31807c6c6b | ||
|
|
8ccad8ee53 | ||
|
|
a930539dc3 | ||
|
|
b33db9397d | ||
|
|
de4b7fb022 | ||
|
|
9e85419f9e | ||
|
|
80c464c951 | ||
|
|
e56dff4972 | ||
|
|
d8501f04eb | ||
|
|
c927b4b935 | ||
|
|
fb9872a4ff | ||
|
|
39b0964739 | ||
|
|
2c509c0368 | ||
|
|
49e1c68cac | ||
|
|
cddbb9a5e5 | ||
|
|
65366478e3 | ||
|
|
1d8b19dce9 | ||
|
|
f600066134 | ||
|
|
75533c20b8 | ||
|
|
0605a5e0e3 | ||
|
|
cf9640bd59 | ||
|
|
328b9fbd60 | ||
|
|
bc6d1070c7 | ||
|
|
bf339636b5 | ||
|
|
d90dae5d9d | ||
|
|
b34b0e866f | ||
|
|
2288c0f1c2 | ||
|
|
9c4abb64c3 | ||
|
|
559f235e88 | ||
|
|
30df0e25f9 | ||
|
|
e7735112e8 | ||
|
|
b943feef29 | ||
|
|
e92d3f4ede | ||
|
|
044cd0b3a6 | ||
|
|
3351bc2f42 | ||
|
|
6a8d236b4d | ||
|
|
cf801344f8 | ||
|
|
50bca161ce | ||
|
|
15ccf945c3 | ||
|
|
d49f06f34d | ||
|
|
826f03220d | ||
|
|
cdb984f830 | ||
|
|
4e40c8223f | ||
|
|
02d86ab233 | ||
|
|
a49079f560 |
@@ -1,5 +1,8 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: "IMPORTANT: Check development branch changelog first!!"
|
||||
url: https://gist.github.com/Koenkk/bfd4c3d1725a2cccacc11d6ba51008ba
|
||||
about: "Before submitting an issue, check that the issue hasn't already been solved in the development branch. Click 'Open' to see the release notes of the development branch. In case it is, you can read here how to switch to the development branch: https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html"
|
||||
- name: Questions/discussion
|
||||
url: https://github.com/Koenkk/zigbee2mqtt/discussions/new
|
||||
about: Ask questions, discuss about devices or show things you made
|
||||
|
||||
@@ -7,12 +7,12 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
**IMPORTANT:** Before submitting:
|
||||
- Is your feature request related to the fronted? Then click [here](https://github.com/nurikk/zigbee2mqtt-frontend/issues/new?assignees=&labels=&template=feature_request.md&title=)
|
||||
- Is your feature request related to the frontend? Then click [here](https://github.com/nurikk/zigbee2mqtt-frontend/issues/new?assignees=&labels=&template=feature_request.md&title=)
|
||||
- type: textarea
|
||||
id: textarea1
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe
|
||||
placeholder: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
placeholder: A clear and concise description of what the problem is. Eg. I'm always frustrated when [...]
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
@@ -7,7 +7,7 @@ body:
|
||||
attributes:
|
||||
value: |
|
||||
**IMPORTANT:** Before submitting:
|
||||
- Make sure this device is not already supported in the [dev branch](https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html)
|
||||
- Make sure this device is not already supported in the dev branch by checking the [dev branch changelog](https://gist.github.com/Koenkk/bfd4c3d1725a2cccacc11d6ba51008ba#new-supported-devices)
|
||||
- Make sure there is no existing issue or PR for this device already, search for your device here: https://github.com/Koenkk/zigbee2mqtt/issues
|
||||
- Follow this [guide](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html)
|
||||
- If you are using the Home Assistant addon and are still on 1.18.1, check the first point of [this](https://github.com/Koenkk/zigbee2mqtt/releases/tag/1.19.0)
|
||||
@@ -36,20 +36,8 @@ body:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: External converter
|
||||
description: In case you tried to create an external converter, put it below
|
||||
label: External definition
|
||||
description: See [Creating the external definition](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html#_2-creating-the-external-definition)
|
||||
render: shell
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
# Lights
|
||||
In case this device is a light, fill in the fields below:
|
||||
- type: input
|
||||
attributes:
|
||||
label: Supported color modes
|
||||
placeholder: color temperature, color (rgb)
|
||||
- type: input
|
||||
attributes:
|
||||
label: Color temperature range
|
||||
description: In case this light supports color temperature, provide the color temperature range. [How to do this](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html#_3-1-retrieving-color-temperature-range-only-required-for-lights-which-support-color-temperature).
|
||||
placeholder: '{"colorTempPhysicalMin":153,"colorTempPhysicalMax":454}'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
@@ -7,11 +7,12 @@ body:
|
||||
value: |
|
||||
**IMPORTANT:** Before submitting:
|
||||
- You read the [FAQ](https://www.zigbee2mqtt.io/guide/faq/)
|
||||
- Are you using an EZSP adapter (e.g. Dongle-E/SkyConnect)? Try the [new driver](https://github.com/Koenkk/zigbee2mqtt/discussions/21462)
|
||||
- Zigbee2MQTT fails to start? Read [this](https://www.zigbee2mqtt.io/guide/installation/20_zigbee2mqtt-fails-to-start.html)
|
||||
- Make sure the bug also occurs in the [dev branch](https://www.zigbee2mqtt.io/advanced/more/switch-to-dev-branch.html)
|
||||
- Make sure you are using the [latest firmware](https://www.zigbee2mqtt.io/guide/adapters/#recommended) on your adapter
|
||||
- The issue has not been [reported already](https://github.com/Koenkk/zigbee2mqtt/issues)
|
||||
- Is your issue related to the fronted? Then click [here](https://github.com/nurikk/zigbee2mqtt-frontend/issues/new?assignees=&labels=bug%2Ctriage&template=bug_report.yaml&title=%5BBug%5D%3A+)
|
||||
- Is your issue related to the frontend? Then click [here](https://github.com/nurikk/zigbee2mqtt-frontend/issues/new?assignees=&labels=bug%2Ctriage&template=bug_report.yaml&title=%5BBug%5D%3A+)
|
||||
- If you are using the Home Assistant addon and are still on 1.18.1, check the first point of [this](https://github.com/Koenkk/zigbee2mqtt/releases/tag/1.19.0)
|
||||
- type: textarea
|
||||
id: what_happend
|
||||
@@ -30,7 +31,7 @@ body:
|
||||
id: reproduce
|
||||
attributes:
|
||||
label: How to reproduce it (minimal and precise)
|
||||
placeholder: First do this, than this..
|
||||
placeholder: First do this, then this..
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
@@ -57,6 +58,14 @@ body:
|
||||
placeholder: Electrolama zig-a-zig-ah! (zzh!), Slaesh's CC2652RB stick, ...
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: setup
|
||||
attributes:
|
||||
label: Setup
|
||||
description: How do you run Z2M (plain, add-on...) and on what machine (Pi, x86-64, containerized...)?
|
||||
placeholder: Add-on on Home Assistant OS on Intel NUC, Plain on Docker container, ...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: log
|
||||
attributes:
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
name: Wrong device picture/vendor/model/description
|
||||
description: Use if device is detected as supported and is fully functional but has a wrong picture, vendor, model or description
|
||||
title: "[Wrong device]: "
|
||||
labels: [wrong device]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Only use this if the device is detected as **supported** and is **fully functional** but has a wrong picture, vendor, model or description.
|
||||
- type: input
|
||||
id: link
|
||||
attributes:
|
||||
label: Link
|
||||
description: Link of this device (product page)
|
||||
placeholder: https://www.linktomydevice.org
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: model
|
||||
attributes:
|
||||
label: Model
|
||||
description: Expected model, model that is printed on the device, for TuYa device this is NOT something like TS0601 or _TZE200_cf1sl3tj
|
||||
placeholder: RTCGQ01LM
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Expected description
|
||||
placeholder: Motion sensor
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: vendor
|
||||
attributes:
|
||||
label: Vendor
|
||||
description: Expected vendor
|
||||
placeholder: Xiaomi
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: picture
|
||||
attributes:
|
||||
label: Picture (link)
|
||||
description: Expected picture
|
||||
placeholder: https://www.linktomydevice.org/RTCGQ01LM.jpg
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: database
|
||||
attributes:
|
||||
label: Database entry
|
||||
description: Entry of this device in `data/database.db` after pairing it
|
||||
placeholder: '{"id":53,"type":"Router","ieeeAddr":"0x10458d00024284f69","nwkAddr":10148,"manufId":4151,"manufName":"LUMI","powerSource":"DC Source","modelId":"lumi.relay.c2acn01","epList":[1,2],"endpoints":{"1":{"profId":260,"epId":1,"devId":257,"inClusterList":[0,3,4,5,1,2,10,6,16,2820,12],"outClusterList":[25,10],"clusters":{"genBasic":{"attributes":{"modelId":"lumi.relay.c2acn01","appVersion":1,"manufacturerName":"LUMI","powerSource":4,"zclVersion":0,"stackVersion":2,"hwVersion":18,"dateCode":"8-6-2020"}},"genAnalogInput":{"attributes":{"presentValue":129.04425048828125}},"genOnOff":{"attributes":{"61440":117440715,"onOff":1}}},"binds":[],"configuredReportings":[],"meta":{}},"2":{"profId":260,"epId":2,"devId":257,"inClusterList":[6,16,4,5],"outClusterList":[],"clusters":{"genOnOff":{"attributes":{"61440":237478966,"onOff":0}}},"binds":[],"configuredReportings":[],"meta":{}}},"appVersion":1,"stackVersion":2,"hwVersion":18,"dateCode":"8-6-2020","zclVersion":0,"interviewCompleted":true,"meta":{},"lastSeen":1640285631405}'
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: notes
|
||||
attributes:
|
||||
label: Notes
|
||||
placeholder: Some additional notes...
|
||||
validations:
|
||||
required: false
|
||||
+58
-20
@@ -1,20 +1,27 @@
|
||||
name: CI
|
||||
name: ci
|
||||
|
||||
on: [push, pull_request]
|
||||
on: [pull_request, push]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
|
||||
with:
|
||||
node-version: 18
|
||||
# Required for `release: merge dev -> master and promote dev`
|
||||
token: ${{secrets.GH_TOKEN}}
|
||||
- uses: actions/checkout@v4
|
||||
if: ((github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push') == false
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
registry-url: https://registry.npmjs.org/
|
||||
cache: 'npm'
|
||||
cache: npm
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Build
|
||||
@@ -28,29 +35,30 @@ jobs:
|
||||
run: echo ${{ secrets.DOCKER_KEY }} | docker login -u koenkk --password-stdin
|
||||
- name: Docker login ghcr.io
|
||||
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
|
||||
run: echo ${{ secrets.CR_PAT }} | docker login ghcr.io -u koenkk --password-stdin
|
||||
run: echo ${{ secrets.GH_TOKEN }} | docker login ghcr.io -u koenkk --password-stdin
|
||||
- name: Docker setup - QEMU
|
||||
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
|
||||
uses: docker/setup-qemu-action@v2
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
platforms: all
|
||||
- name: Docker setup - Buildx
|
||||
if: (github.ref == 'refs/heads/dev' || startsWith(github.ref, 'refs/tags/')) && github.event_name == 'push'
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
version: latest
|
||||
- name: Docker build dev
|
||||
- name: "dev: Docker build"
|
||||
if: github.ref == 'refs/heads/dev' && github.event_name == 'push'
|
||||
run: |
|
||||
docker buildx build \
|
||||
--build-arg COMMIT=$(git rev-parse --short HEAD) \
|
||||
--platform linux/arm64/v8,linux/386,linux/amd64,linux/arm/v6,linux/arm/v7 \
|
||||
-f docker/Dockerfile \
|
||||
--provenance=false \
|
||||
--push \
|
||||
-t koenkk/zigbee2mqtt:latest-dev -t ghcr.io/koenkk/zigbee2mqtt:latest-dev \
|
||||
.
|
||||
- name: Docker build release
|
||||
- name: "release: Docker build"
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
|
||||
run: |
|
||||
TAG="$(git describe --tags)"
|
||||
@@ -58,34 +66,66 @@ jobs:
|
||||
--build-arg COMMIT=$(git rev-parse --short HEAD) \
|
||||
--platform linux/arm64/v8,linux/386,linux/amd64,linux/arm/v6,linux/arm/v7 \
|
||||
-f docker/Dockerfile \
|
||||
--provenance=false \
|
||||
--push \
|
||||
-t koenkk/zigbee2mqtt:latest -t "koenkk/zigbee2mqtt:$TAG" -t ghcr.io/koenkk/zigbee2mqtt:latest -t "ghcr.io/koenkk/zigbee2mqtt:$TAG" \
|
||||
.
|
||||
- name: Publish to npm
|
||||
- name: "release: Publish to npm"
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
|
||||
run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN }}
|
||||
- name: Trigger zigbee2mqtt/hassio-zigbee2mqtt build
|
||||
- name: "dev: Trigger zigbee2mqtt/hassio-zigbee2mqtt build"
|
||||
if: github.ref == 'refs/heads/dev' && github.event_name == 'push'
|
||||
run: |
|
||||
curl \
|
||||
-X POST \
|
||||
-H "Authorization: token ${{ secrets.CR_PAT }}" \
|
||||
-H "Authorization: token ${{ secrets.GH_TOKEN }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/zigbee2mqtt/hassio-zigbee2mqtt/actions/workflows/ci.yml/dispatches \
|
||||
-d '{"ref":"master","inputs":{}}'
|
||||
- name: "release: Trigger zigbee2mqtt/hassio-zigbee2mqtt build"
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
|
||||
run: |
|
||||
TAG=${GITHUB_REF#refs/*/}
|
||||
echo "Triggering with tag '$TAG'"
|
||||
curl \
|
||||
-X POST \
|
||||
-H "Authorization: token ${{ secrets.GH_TOKEN }}" \
|
||||
-H "Accept: application/vnd.github.everest-preview+json" \
|
||||
-H "Content-Type: application/json" \
|
||||
https://api.github.com/repos/zigbee2mqtt/hassio-zigbee2mqtt/dispatches \
|
||||
--data "{\"event_type\": \"release\", \"client_payload\": { \"version\": \"$TAG-1\"}}"
|
||||
- name: "release: merge dev -> master and promote dev"
|
||||
if: startsWith(github.ref, 'refs/tags/') && github.event_name == 'push'
|
||||
run: |
|
||||
TAG=${GITHUB_REF#refs/*/}
|
||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git fetch --unshallow
|
||||
git fetch origin
|
||||
git checkout master
|
||||
git merge --ff-only origin/dev
|
||||
git push origin master
|
||||
git checkout dev
|
||||
jq ".version = \"$TAG-dev\"" package.json > package.json.tmp
|
||||
jq ".version = \"$TAG-dev\"" package-lock.json > package-lock.json.tmp
|
||||
mv package.json.tmp package.json
|
||||
mv package-lock.json.tmp package-lock.json
|
||||
git add -A
|
||||
git commit -m "chore: promote to dev"
|
||||
git push origin dev
|
||||
|
||||
tests:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
node: [14, 16, 18, 19]
|
||||
node: [18, 20]
|
||||
runs-on: ${{ matrix.os }}
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
registry-url: https://registry.npmjs.org/
|
||||
@@ -93,8 +133,6 @@ jobs:
|
||||
- name: Install dependencies
|
||||
# --ignore-scripts prevents the serialport build which often fails on Windows
|
||||
run: npm ci --ignore-scripts
|
||||
- name: Lint
|
||||
run: npm run eslint
|
||||
- name: Build
|
||||
run: npm run build
|
||||
- name: Test
|
||||
|
||||
@@ -1,34 +1,23 @@
|
||||
name: "Code Scanning - Action"
|
||||
name: codeql
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
# CodeQL runs on ubuntu-latest, windows-latest, and macos-latest
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
uses: actions/checkout@v4
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below).
|
||||
uses: github/codeql-action/init@v3
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
name: 'Dependency review'
|
||||
name: deps-review
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
@@ -9,6 +9,6 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout repository'
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
- name: 'Dependency review'
|
||||
uses: actions/dependency-review-action@v3
|
||||
uses: actions/dependency-review-action@v4
|
||||
@@ -1,7 +1,7 @@
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
name: GHCR cleanup
|
||||
name: ghcr-cleanup
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
@@ -10,9 +10,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Delete untagged images
|
||||
uses: actions/github-script@v6
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.CR_PAT }}
|
||||
github-token: ${{ secrets.GH_TOKEN }}
|
||||
script: |
|
||||
const response = await github.request("GET /${{ env.OWNER }}/packages/container/${{ env.PACKAGE_NAME }}/versions",
|
||||
{ per_page: ${{ env.PER_PAGE }}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
name: merge-master-to-dev
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
merge_master_to_dev:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: devmasx/merge-branch@master
|
||||
with:
|
||||
type: now
|
||||
head_to_merge: master
|
||||
target_branch: dev
|
||||
message: "chore: merge master to dev"
|
||||
github_token: ${{ secrets.GH_TOKEN }}
|
||||
@@ -0,0 +1,69 @@
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
name: release-please
|
||||
|
||||
jobs:
|
||||
release-please:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
release_created: ${{ steps.release.outputs.release_created }}
|
||||
version: "${{steps.release.outputs.major}}.${{steps.release.outputs.minor}}.${{steps.release.outputs.patch}}"
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
|
||||
- uses: google-github-actions/release-please-action@v4
|
||||
id: release
|
||||
with:
|
||||
target-branch: dev
|
||||
token: ${{secrets.GH_TOKEN}}
|
||||
|
||||
# Checkout repos
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: koenkk/zigbee2mqtt
|
||||
path: ./z2m
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: koenkk/zigbee2mqtt
|
||||
path: ./z2m-master
|
||||
ref: master
|
||||
|
||||
- name: Restore cache commit-user-lookup.json
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: z2m/scripts/commit-user-lookup.json
|
||||
key: commit-user-lookup-dummy
|
||||
restore-keys: |
|
||||
commit-user-lookup-
|
||||
- name: Generate changelog
|
||||
run: |
|
||||
MASTER_Z2M_VERSION=$(cat z2m-master/package.json | jq -r '.version')
|
||||
MASTER_ZHC_VERSION=$(cat z2m-master/package.json | jq -r '.dependencies."zigbee-herdsman-converters"')
|
||||
MASTER_ZH_VERSION=$(cat z2m-master/package.json | jq -r '.dependencies."zigbee-herdsman"')
|
||||
MASTER_FRONTEND_VERSION=$(cat z2m-master/package.json | jq -r '.dependencies."zigbee2mqtt-frontend"')
|
||||
wget -q -O - https://raw.githubusercontent.com/Koenkk/zigbee2mqtt/release-please--branches--dev--components--zigbee2mqtt/CHANGELOG.md > z2m/CHANGELOG.md
|
||||
cd z2m
|
||||
npm ci
|
||||
node scripts/generateChangelog.js $MASTER_Z2M_VERSION $MASTER_ZHC_VERSION $MASTER_ZH_VERSION $MASTER_FRONTEND_VERSION >> ../changelog.md
|
||||
env:
|
||||
GH_TOKEN: ${{secrets.GH_TOKEN}}
|
||||
- name: Update changelog gist
|
||||
run: |
|
||||
gh gist edit bfd4c3d1725a2cccacc11d6ba51008ba -a changelog.md
|
||||
env:
|
||||
GH_TOKEN: ${{secrets.GH_TOKEN}}
|
||||
- name: Save cache commit-user-lookup.json
|
||||
uses: actions/cache/save@v4
|
||||
if: always()
|
||||
with:
|
||||
path: z2m/scripts/commit-user-lookup.json
|
||||
key: commit-user-lookup-${{ hashFiles('z2m/scripts/commit-user-lookup.json') }}
|
||||
@@ -1,25 +1,20 @@
|
||||
name: "Close stale issues/pull requests"
|
||||
name: stale
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
permissions:
|
||||
issues: write # for actions/stale to close stale issues
|
||||
pull-requests: write # for actions/stale to close stale PRs
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v7
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days'
|
||||
stale-pr-message: 'This pull request is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 7 days'
|
||||
days-before-stale: 30
|
||||
days-before-close: 7
|
||||
operations-per-run: 500
|
||||
days-before-stale: 180
|
||||
days-before-close: 30
|
||||
exempt-issue-labels: dont-stale
|
||||
operations-per-run: 500
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: update_dep
|
||||
|
||||
name: update-dep
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
update_dep:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: dev
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
- run: npm install ${{ github.event.client_payload.package }}@${{ github.event.client_payload.version }} --save-exact
|
||||
- uses: peter-evans/create-pull-request@v6
|
||||
id: cpr
|
||||
with:
|
||||
commit-message: "fix(ignore): update ${{ github.event.client_payload.package }} to ${{ github.event.client_payload.version }}"
|
||||
branch: "deps/${{ github.event.client_payload.package }}"
|
||||
title: Update ${{ github.event.client_payload.package }} to ${{ github.event.client_payload.version }}
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
- run: sleep 5 # Otherwise pull request may not exist yet causing automerge to fail
|
||||
- run: gh pr merge --squash --auto "${{ steps.cpr.outputs.pull-request-number }}"
|
||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
@@ -14,17 +14,21 @@ jobs:
|
||||
pull-requests: write # for peter-evans/create-pull-request to create a PR
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: dev
|
||||
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
- uses: actions/setup-node@v3
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npx npm-check-updates -u -x connect-gzip-static # connect-gzip-static only supports node 16
|
||||
node-version: 20
|
||||
cache: npm
|
||||
# connect-gzip-static@4.0.0 requires Node 20 >=
|
||||
- run: npx npm-check-updates -u -x connect-gzip-static
|
||||
- run: rm -f package-lock.json
|
||||
- run: npm install
|
||||
- uses: peter-evans/create-pull-request@v4
|
||||
- uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
commit-message: Update dependencies
|
||||
branch: "update_deps"
|
||||
commit-message: "fix(ignore): update dependencies"
|
||||
branch: "deps/all"
|
||||
title: Update dependencies
|
||||
token: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
name: Update zigbee-frontend
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: update_frontend
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
update_frontend:
|
||||
permissions:
|
||||
contents: write # to create branch (peter-evans/create-pull-request)
|
||||
pull-requests: write # to create a PR (peter-evans/create-pull-request)
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: dev
|
||||
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm install zigbee2mqtt-frontend@${{ github.event.client_payload.version }} --save-exact
|
||||
- uses: peter-evans/create-pull-request@v4
|
||||
id: cpr
|
||||
with:
|
||||
commit-message: Update zigbee2mqtt-frontend to ${{ github.event.client_payload.version }}
|
||||
branch: "update_frontend"
|
||||
title: Update zigbee-frontend to ${{ github.event.client_payload.version }}
|
||||
- run: sleep 5 # Otherwise pull request may not exist yet causing automerge to fail
|
||||
- uses: peter-evans/enable-pull-request-automerge@v2
|
||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||
with:
|
||||
token: ${{ secrets.CR_PAT }}
|
||||
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
merge-method: squash
|
||||
@@ -1,35 +0,0 @@
|
||||
name: Update zigbee-herdsman
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: update_zh
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
update_zh:
|
||||
permissions:
|
||||
contents: write # to create branch (peter-evans/create-pull-request)
|
||||
pull-requests: write # to create a PR (peter-evans/create-pull-request)
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: dev
|
||||
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm install zigbee-herdsman@${{ github.event.client_payload.version }} --save-exact
|
||||
- uses: peter-evans/create-pull-request@v4
|
||||
id: cpr
|
||||
with:
|
||||
commit-message: Update zigbee-herdsman to ${{ github.event.client_payload.version }}
|
||||
branch: "update_zh"
|
||||
title: Update zigbee-herdsman to ${{ github.event.client_payload.version }}
|
||||
- run: sleep 5 # Otherwise pull request may not exist yet causing automerge to fail
|
||||
- uses: peter-evans/enable-pull-request-automerge@v2
|
||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||
with:
|
||||
token: ${{ secrets.CR_PAT }}
|
||||
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
merge-method: squash
|
||||
@@ -1,35 +0,0 @@
|
||||
name: Update zigbee-herdsman-converters
|
||||
on:
|
||||
repository_dispatch:
|
||||
types: update_zhc
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
update_zhc:
|
||||
permissions:
|
||||
contents: write # to create branch (peter-evans/create-pull-request)
|
||||
pull-requests: write # to create a PR (peter-evans/create-pull-request)
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: dev
|
||||
ssh-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm install zigbee-herdsman-converters@${{ github.event.client_payload.version }} --save-exact
|
||||
- uses: peter-evans/create-pull-request@v4
|
||||
id: cpr
|
||||
with:
|
||||
commit-message: Update zigbee-herdsman-converters to ${{ github.event.client_payload.version }}
|
||||
branch: "update_zhc"
|
||||
title: Update zigbee-herdsman-converters to ${{ github.event.client_payload.version }}
|
||||
- run: sleep 5 # Otherwise pull request may not exist yet causing automerge to fail
|
||||
- uses: peter-evans/enable-pull-request-automerge@v2
|
||||
if: steps.cpr.outputs.pull-request-operation == 'created'
|
||||
with:
|
||||
token: ${{ secrets.CR_PAT }}
|
||||
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||
merge-method: squash
|
||||
@@ -48,6 +48,9 @@ typings/
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Build cache
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
@@ -80,3 +83,7 @@ data/extension
|
||||
|
||||
# Ignore config
|
||||
data/*.yaml
|
||||
!data/configuration.example.yaml
|
||||
|
||||
# commit-user-lookup.json
|
||||
scripts/commit-user-lookup.json
|
||||
|
||||
@@ -8,6 +8,7 @@ coverage
|
||||
.codeclimate.yml
|
||||
.github
|
||||
babel.config.js
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
#linters
|
||||
.jscsrc
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
".": "1.36.1"
|
||||
}
|
||||
+584
@@ -0,0 +1,584 @@
|
||||
# Changelog
|
||||
|
||||
## [1.36.1](https://github.com/Koenkk/zigbee2mqtt/compare/1.36.0...1.36.1) (2024-04-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add more Home Assistant entity icons ([#21652](https://github.com/Koenkk/zigbee2mqtt/issues/21652)) ([01843fe](https://github.com/Koenkk/zigbee2mqtt/commit/01843fe6c43210ade1d1dfcd134fdd979d47f194))
|
||||
* Fix `set` endpoint name conversion ([#21653](https://github.com/Koenkk/zigbee2mqtt/issues/21653)) ([66fb672](https://github.com/Koenkk/zigbee2mqtt/commit/66fb672492c8f049368d5dbda3925500941cd534))
|
||||
* Fix crash due to race condition during interview ([#21680](https://github.com/Koenkk/zigbee2mqtt/issues/21680)) ([2da8095](https://github.com/Koenkk/zigbee2mqtt/commit/2da80954d69c753bcfbef30c2579584d7a0f8779))
|
||||
* Fix incorrect update state when Z2M is restarted during OTA update https://github.com/Koenkk/zigbee2mqtt/issues/12794 ([1110d3f](https://github.com/Koenkk/zigbee2mqtt/commit/1110d3f8efff948811a980968f8dc84465500d4f))
|
||||
* Home Assistant: remove button name if `device_class` is present ([#21726](https://github.com/Koenkk/zigbee2mqtt/issues/21726)) ([14e0440](https://github.com/Koenkk/zigbee2mqtt/commit/14e044093c4f481fc7c4f58c35c68ebddddf621c))
|
||||
* **ignore:** fix tests ([29543ba](https://github.com/Koenkk/zigbee2mqtt/commit/29543bad4bb8768bb66c15f4f084b0d9ccc6c1bc))
|
||||
* **ignore:** update dependencies ([#21657](https://github.com/Koenkk/zigbee2mqtt/issues/21657)) ([8c86849](https://github.com/Koenkk/zigbee2mqtt/commit/8c86849bb992e1125e9fed301eef8655aa95466c))
|
||||
* **ignore:** Update dependencies ([#21740](https://github.com/Koenkk/zigbee2mqtt/issues/21740)) ([43610bb](https://github.com/Koenkk/zigbee2mqtt/commit/43610bbff44572937650ee03ccd239d084d9959f))
|
||||
* **ignore:** update dependencies ([#21832](https://github.com/Koenkk/zigbee2mqtt/issues/21832)) ([1006725](https://github.com/Koenkk/zigbee2mqtt/commit/1006725b09a6e07e60586409798de3cbc7f3c1f1))
|
||||
* **ignore:** update dependencies ([#21923](https://github.com/Koenkk/zigbee2mqtt/issues/21923)) ([8c4cd8c](https://github.com/Koenkk/zigbee2mqtt/commit/8c4cd8c9aa8777027d538a114fc9310b1f2fe7a2))
|
||||
* **ignore:** Update manufacturer codes ([#21729](https://github.com/Koenkk/zigbee2mqtt/issues/21729)) ([83502f2](https://github.com/Koenkk/zigbee2mqtt/commit/83502f2d30cf2d79f3ea8576ab065590da29abd8))
|
||||
* **ignore:** update zigbee-herdsman to 0.35.2 ([#21654](https://github.com/Koenkk/zigbee2mqtt/issues/21654)) ([ccaa128](https://github.com/Koenkk/zigbee2mqtt/commit/ccaa1281a35249874a065fd2d4975aeab93f05f4))
|
||||
* **ignore:** update zigbee-herdsman to 0.35.3 ([#21677](https://github.com/Koenkk/zigbee2mqtt/issues/21677)) ([30909d5](https://github.com/Koenkk/zigbee2mqtt/commit/30909d57fba7f7a45caeb14afcbf5f8ac724c876))
|
||||
* **ignore:** update zigbee-herdsman to 0.36.0 ([#21689](https://github.com/Koenkk/zigbee2mqtt/issues/21689)) ([1d73d9d](https://github.com/Koenkk/zigbee2mqtt/commit/1d73d9d4768d8511ca24066fe199d4096b0301ac))
|
||||
* **ignore:** update zigbee-herdsman to 0.36.1 ([#21713](https://github.com/Koenkk/zigbee2mqtt/issues/21713)) ([e834e93](https://github.com/Koenkk/zigbee2mqtt/commit/e834e93debbd343236e71e275bbd3726a3b4ae3c))
|
||||
* **ignore:** update zigbee-herdsman to 0.36.2 ([#21727](https://github.com/Koenkk/zigbee2mqtt/issues/21727)) ([ebf996a](https://github.com/Koenkk/zigbee2mqtt/commit/ebf996acd83f32668be7dfd3994c2976db52bc58))
|
||||
* **ignore:** update zigbee-herdsman to 0.37.0 ([#21743](https://github.com/Koenkk/zigbee2mqtt/issues/21743)) ([b60f679](https://github.com/Koenkk/zigbee2mqtt/commit/b60f679814294ce454e4e7c08fc3dd11b9a224ff))
|
||||
* **ignore:** update zigbee-herdsman to 0.38.0 ([#21777](https://github.com/Koenkk/zigbee2mqtt/issues/21777)) ([233d263](https://github.com/Koenkk/zigbee2mqtt/commit/233d263e132e7b046194dedd620ae687a815f1ec))
|
||||
* **ignore:** update zigbee-herdsman to 0.38.1 ([#21802](https://github.com/Koenkk/zigbee2mqtt/issues/21802)) ([6586228](https://github.com/Koenkk/zigbee2mqtt/commit/658622880ffc7eee1c27ba499ce75da03b25c216))
|
||||
* **ignore:** update zigbee-herdsman to 0.39.0 ([#21871](https://github.com/Koenkk/zigbee2mqtt/issues/21871)) ([801134b](https://github.com/Koenkk/zigbee2mqtt/commit/801134b6303ecea0d41254ac84a6f0f96393dc3f))
|
||||
* **ignore:** update zigbee-herdsman to 0.39.1 ([#21891](https://github.com/Koenkk/zigbee2mqtt/issues/21891)) ([f05d501](https://github.com/Koenkk/zigbee2mqtt/commit/f05d501380a5416fb610f6eeeea7c9b7a04394cb))
|
||||
* **ignore:** update zigbee-herdsman to 0.40.0 ([#21901](https://github.com/Koenkk/zigbee2mqtt/issues/21901)) ([334e260](https://github.com/Koenkk/zigbee2mqtt/commit/334e260b851535379548e52a61cbef5956411f46))
|
||||
* **ignore:** update zigbee-herdsman to 0.40.1 ([#21987](https://github.com/Koenkk/zigbee2mqtt/issues/21987)) ([0c7730f](https://github.com/Koenkk/zigbee2mqtt/commit/0c7730f4c50fb97313de35533140a8c99f6ad374))
|
||||
* **ignore:** update zigbee-herdsman to 0.40.2 ([#22000](https://github.com/Koenkk/zigbee2mqtt/issues/22000)) ([886b534](https://github.com/Koenkk/zigbee2mqtt/commit/886b5345d7c134f27aa14e259ce1cfd8423b8636))
|
||||
* **ignore:** update zigbee-herdsman to 0.40.3 ([#22010](https://github.com/Koenkk/zigbee2mqtt/issues/22010)) ([1e74b2f](https://github.com/Koenkk/zigbee2mqtt/commit/1e74b2f53ff7af8d1067265c966f75aef8a794f1))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.44.0 ([#21690](https://github.com/Koenkk/zigbee2mqtt/issues/21690)) ([6daa7aa](https://github.com/Koenkk/zigbee2mqtt/commit/6daa7aa61db7c6ceef707ab27afca14e31a2e79f))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.45.0 ([#21699](https://github.com/Koenkk/zigbee2mqtt/issues/21699)) ([d37c164](https://github.com/Koenkk/zigbee2mqtt/commit/d37c164993cd4b3bc2f027be3a3a2b9a5b30d200))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.46.0 ([#21714](https://github.com/Koenkk/zigbee2mqtt/issues/21714)) ([0e0eccb](https://github.com/Koenkk/zigbee2mqtt/commit/0e0eccbd139e38df31b0860af26c50032f629fae))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.47.0 ([#21728](https://github.com/Koenkk/zigbee2mqtt/issues/21728)) ([3188038](https://github.com/Koenkk/zigbee2mqtt/commit/3188038a7bf673b2053783910229e1e60b6f5921))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.48.0 ([#21753](https://github.com/Koenkk/zigbee2mqtt/issues/21753)) ([2a047a4](https://github.com/Koenkk/zigbee2mqtt/commit/2a047a469c70f17a3c12d0a6f6fc2de9744835e7))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.0.0 ([#21768](https://github.com/Koenkk/zigbee2mqtt/issues/21768)) ([dbd616f](https://github.com/Koenkk/zigbee2mqtt/commit/dbd616fbe5a62986d2c28bbcc9cea93d293da397))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.1.0 ([#21778](https://github.com/Koenkk/zigbee2mqtt/issues/21778)) ([56feb77](https://github.com/Koenkk/zigbee2mqtt/commit/56feb77f2d1c357d59245db3b7cc37520554ed41))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.1.1 ([#21789](https://github.com/Koenkk/zigbee2mqtt/issues/21789)) ([720fc49](https://github.com/Koenkk/zigbee2mqtt/commit/720fc491ce70f197778b5abe750ac2d779a9ad4c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.10.0 ([#21996](https://github.com/Koenkk/zigbee2mqtt/issues/21996)) ([7d8b537](https://github.com/Koenkk/zigbee2mqtt/commit/7d8b5374adca3c97976e557f1d44e1b85728d575))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.11.0 ([#22013](https://github.com/Koenkk/zigbee2mqtt/issues/22013)) ([e51792c](https://github.com/Koenkk/zigbee2mqtt/commit/e51792c65afa5a2bc863bb619c0057fc7242b191))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.11.1 ([#22014](https://github.com/Koenkk/zigbee2mqtt/issues/22014)) ([85c00b8](https://github.com/Koenkk/zigbee2mqtt/commit/85c00b8b43e2716231203599fac8bf01662535d5))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.11.2 ([#22021](https://github.com/Koenkk/zigbee2mqtt/issues/22021)) ([3aadab0](https://github.com/Koenkk/zigbee2mqtt/commit/3aadab0925d52839baf6d884ea59d25ff083a015))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.2.1 ([#21801](https://github.com/Koenkk/zigbee2mqtt/issues/21801)) ([a4f9c49](https://github.com/Koenkk/zigbee2mqtt/commit/a4f9c49ab5ef647829b8f505df7f91df8fca5036))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.3.0 ([#21851](https://github.com/Koenkk/zigbee2mqtt/issues/21851)) ([1a9e05e](https://github.com/Koenkk/zigbee2mqtt/commit/1a9e05e6ab93a1e93024600a58c5b8e62544daf4))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.4.0 ([#21870](https://github.com/Koenkk/zigbee2mqtt/issues/21870)) ([73d4300](https://github.com/Koenkk/zigbee2mqtt/commit/73d43007d45ee3cfc2d49ff2f4475fef63560421))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.4.1 ([#21872](https://github.com/Koenkk/zigbee2mqtt/issues/21872)) ([b02c34c](https://github.com/Koenkk/zigbee2mqtt/commit/b02c34c6e52cf97364f6affce82b281cd40c1780))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.4.2 ([#21892](https://github.com/Koenkk/zigbee2mqtt/issues/21892)) ([25cec20](https://github.com/Koenkk/zigbee2mqtt/commit/25cec20f0d0b6201679b719bf8dec9e7ebb26b62))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.5.0 ([#21902](https://github.com/Koenkk/zigbee2mqtt/issues/21902)) ([73423eb](https://github.com/Koenkk/zigbee2mqtt/commit/73423ebd60a04f0caf7ac40743422cea3ef052e9))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.6.0 ([#21927](https://github.com/Koenkk/zigbee2mqtt/issues/21927)) ([32b173c](https://github.com/Koenkk/zigbee2mqtt/commit/32b173c6537a4cdc3c303e521c5fa159cfcfb58d))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.8.0 ([#21957](https://github.com/Koenkk/zigbee2mqtt/issues/21957)) ([032b9a0](https://github.com/Koenkk/zigbee2mqtt/commit/032b9a096eac7ba01a8eee188c042287db55bfed))
|
||||
* **ignore:** update zigbee-herdsman-converters to 19.9.0 ([#21970](https://github.com/Koenkk/zigbee2mqtt/issues/21970)) ([a7aab71](https://github.com/Koenkk/zigbee2mqtt/commit/a7aab714ba1861eab15d0df3228e222feb8fa996))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.161 ([#21660](https://github.com/Koenkk/zigbee2mqtt/issues/21660)) ([aaed8ab](https://github.com/Koenkk/zigbee2mqtt/commit/aaed8ab95ec031ad5ea1f00933cc5f79b58fce29))
|
||||
|
||||
## [1.36.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.35.3...1.36.0) (2024-03-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add experimental Ember adapter @Nerivec https://github.com/Koenkk/zigbee-herdsman/pull/918 ([#21459](https://github.com/Koenkk/zigbee2mqtt/issues/21459)) ([c5c0a8b](https://github.com/Koenkk/zigbee2mqtt/commit/c5c0a8b1938ed51887b21ded06d584f200ba32d1))
|
||||
* Add support for button entity in Home Assistant ([#21372](https://github.com/Koenkk/zigbee2mqtt/issues/21372)) ([24b5f9b](https://github.com/Koenkk/zigbee2mqtt/commit/24b5f9b43af8303f87fd5f29e57cca4a850d7b1d))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* `trigger_count` & `power_outage_count` adjustments in HA ([#21492](https://github.com/Koenkk/zigbee2mqtt/issues/21492)) ([fe73db4](https://github.com/Koenkk/zigbee2mqtt/commit/fe73db4ff686915841fc045750c7c7c98457422d))
|
||||
* Add noise and noise_detected to HA lookup ([#21610](https://github.com/Koenkk/zigbee2mqtt/issues/21610)) ([9b8583e](https://github.com/Koenkk/zigbee2mqtt/commit/9b8583e778ffbd92ba82315d5a53ed68f51e4fc3))
|
||||
* Check if Zigbee2MQTT is running in `update.sh` ([#21494](https://github.com/Koenkk/zigbee2mqtt/issues/21494)) ([8b69443](https://github.com/Koenkk/zigbee2mqtt/commit/8b694432aaa229823674eb4933eded3f2ab1517a))
|
||||
* Fix endpoint matching for TuYa dummy endpoints ([#21576](https://github.com/Koenkk/zigbee2mqtt/issues/21576)) ([956003a](https://github.com/Koenkk/zigbee2mqtt/commit/956003aa0ab104f611cf3d0c61bec1c476ac17d0))
|
||||
* Fix parsing entity name that contains slash in the name and endpoint specification ([#21493](https://github.com/Koenkk/zigbee2mqtt/issues/21493)) ([3c60965](https://github.com/Koenkk/zigbee2mqtt/commit/3c609658fd788410270af3343f01cc47b6ef0ab1))
|
||||
* **ignore:** update dependencies ([#21347](https://github.com/Koenkk/zigbee2mqtt/issues/21347)) ([ce41f2f](https://github.com/Koenkk/zigbee2mqtt/commit/ce41f2f8ba89d31926cdce7d2d93ec19ffccf7fc))
|
||||
* **ignore:** update dependencies ([#21444](https://github.com/Koenkk/zigbee2mqtt/issues/21444)) ([16b251d](https://github.com/Koenkk/zigbee2mqtt/commit/16b251d3c9c0651436899de6b63033d98e7b1353))
|
||||
* **ignore:** update dependencies ([#21561](https://github.com/Koenkk/zigbee2mqtt/issues/21561)) ([c56470c](https://github.com/Koenkk/zigbee2mqtt/commit/c56470c83fd3b9ea366f67814e09146bcac6d188))
|
||||
* **ignore:** update zigbee-herdsman to 0.33.9 ([#21386](https://github.com/Koenkk/zigbee2mqtt/issues/21386)) ([dfa4996](https://github.com/Koenkk/zigbee2mqtt/commit/dfa499659ec69db40ccc3d5c1dff874f446587d0))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.1 ([#21461](https://github.com/Koenkk/zigbee2mqtt/issues/21461)) ([b51eef2](https://github.com/Koenkk/zigbee2mqtt/commit/b51eef2319adf2b26b7b9a4a279adf2b671dd380))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.2 ([#21478](https://github.com/Koenkk/zigbee2mqtt/issues/21478)) ([78b5e6f](https://github.com/Koenkk/zigbee2mqtt/commit/78b5e6fd4b11ba138f83e848c60ac017fe5b10f2))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.3 ([#21530](https://github.com/Koenkk/zigbee2mqtt/issues/21530)) ([e726d59](https://github.com/Koenkk/zigbee2mqtt/commit/e726d5907e86ca3fabef4491579994d9e89f0fe0))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.4 ([#21545](https://github.com/Koenkk/zigbee2mqtt/issues/21545)) ([23959c5](https://github.com/Koenkk/zigbee2mqtt/commit/23959c5b0ec7e36183400920617e3052cd9bf17a))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.5 ([#21563](https://github.com/Koenkk/zigbee2mqtt/issues/21563)) ([cc27032](https://github.com/Koenkk/zigbee2mqtt/commit/cc2703275272c268b1f04ce75488027d6c70b870))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.6 ([#21567](https://github.com/Koenkk/zigbee2mqtt/issues/21567)) ([4f71c1e](https://github.com/Koenkk/zigbee2mqtt/commit/4f71c1e8bf3fe430cc03d3054a6e2a75ba6ec7de))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.7 ([#21575](https://github.com/Koenkk/zigbee2mqtt/issues/21575)) ([a06a0ea](https://github.com/Koenkk/zigbee2mqtt/commit/a06a0ea059354ac43de47c9d4e6d573bd6186288))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.8 ([#21588](https://github.com/Koenkk/zigbee2mqtt/issues/21588)) ([fee0811](https://github.com/Koenkk/zigbee2mqtt/commit/fee0811893a6bc90b86978e5c80cdb4e1a23ef70))
|
||||
* **ignore:** update zigbee-herdsman to 0.34.9 ([#21602](https://github.com/Koenkk/zigbee2mqtt/issues/21602)) ([222b67b](https://github.com/Koenkk/zigbee2mqtt/commit/222b67b1b0c9151222c4d9cc2e531bf74a080ba6))
|
||||
* **ignore:** update zigbee-herdsman to 0.35.0 ([#21621](https://github.com/Koenkk/zigbee2mqtt/issues/21621)) ([a84083b](https://github.com/Koenkk/zigbee2mqtt/commit/a84083be404a54303eb9337cc86c3938a1d5d41e))
|
||||
* **ignore:** update zigbee-herdsman to 0.35.1 ([#21636](https://github.com/Koenkk/zigbee2mqtt/issues/21636)) ([0d8f578](https://github.com/Koenkk/zigbee2mqtt/commit/0d8f5780ddf05d176b199a152b3a1a212bca6810))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.29.1 ([#21290](https://github.com/Koenkk/zigbee2mqtt/issues/21290)) ([55653c9](https://github.com/Koenkk/zigbee2mqtt/commit/55653c974ef72e8b5d8107d1b2e1b67efb70e4e1))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.30.0 ([#21315](https://github.com/Koenkk/zigbee2mqtt/issues/21315)) ([910f3a6](https://github.com/Koenkk/zigbee2mqtt/commit/910f3a68603bf4b7f31d7a80370816d02f7126b8))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.31.0 ([#21359](https://github.com/Koenkk/zigbee2mqtt/issues/21359)) ([c7b820c](https://github.com/Koenkk/zigbee2mqtt/commit/c7b820c3763e6052cc6755e0e12926c316de7f42))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.32.0 ([#21373](https://github.com/Koenkk/zigbee2mqtt/issues/21373)) ([0ab7390](https://github.com/Koenkk/zigbee2mqtt/commit/0ab7390a642a723fc4e1847bbd95ea12487388ab))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.33.0 ([#21387](https://github.com/Koenkk/zigbee2mqtt/issues/21387)) ([2aaf878](https://github.com/Koenkk/zigbee2mqtt/commit/2aaf878889b4211b8a470c847721c75b5a91e39e))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.33.1 ([#21400](https://github.com/Koenkk/zigbee2mqtt/issues/21400)) ([8a9477f](https://github.com/Koenkk/zigbee2mqtt/commit/8a9477f2bb6afbfeacf64547c8bfe4ae4f18ca7f))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.33.2 ([#21416](https://github.com/Koenkk/zigbee2mqtt/issues/21416)) ([5766bf4](https://github.com/Koenkk/zigbee2mqtt/commit/5766bf479ba548d819f87de2046f554b6de15945))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.34.0 ([#21456](https://github.com/Koenkk/zigbee2mqtt/issues/21456)) ([ebed9d3](https://github.com/Koenkk/zigbee2mqtt/commit/ebed9d31d710ac6853e3eee6b677cfe6e84fd556))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.35.0 ([#21487](https://github.com/Koenkk/zigbee2mqtt/issues/21487)) ([b32bd30](https://github.com/Koenkk/zigbee2mqtt/commit/b32bd309ca7748d4dcb0b3ca5b3428578a86487c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.36.0 ([#21504](https://github.com/Koenkk/zigbee2mqtt/issues/21504)) ([22d3336](https://github.com/Koenkk/zigbee2mqtt/commit/22d33360f6966625afb9f456d5ffe96c42baaa3f))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.37.0 ([#21533](https://github.com/Koenkk/zigbee2mqtt/issues/21533)) ([1337f54](https://github.com/Koenkk/zigbee2mqtt/commit/1337f54e4a49ac9e28b9c3eb780829915b59cb57))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.37.1 ([#21549](https://github.com/Koenkk/zigbee2mqtt/issues/21549)) ([7c97902](https://github.com/Koenkk/zigbee2mqtt/commit/7c97902d4a088d680010511148b67ac19d05b91a))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.40.0 ([#21603](https://github.com/Koenkk/zigbee2mqtt/issues/21603)) ([aeaa040](https://github.com/Koenkk/zigbee2mqtt/commit/aeaa0409203e19d54511601cf7e0124db6a09778))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.41.0 ([#21622](https://github.com/Koenkk/zigbee2mqtt/issues/21622)) ([72333f0](https://github.com/Koenkk/zigbee2mqtt/commit/72333f0888b5be3abacb286ed0a227438d593563))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.42.0 ([#21637](https://github.com/Koenkk/zigbee2mqtt/issues/21637)) ([ccc3167](https://github.com/Koenkk/zigbee2mqtt/commit/ccc31670ec5b93bddaa8cef3f965fd8cc77e51a4))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.157 ([#21269](https://github.com/Koenkk/zigbee2mqtt/issues/21269)) ([a479bf9](https://github.com/Koenkk/zigbee2mqtt/commit/a479bf9039248d0b0ebc7e0f65dfc8ff342090d4))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.158 ([#21356](https://github.com/Koenkk/zigbee2mqtt/issues/21356)) ([8bc3c46](https://github.com/Koenkk/zigbee2mqtt/commit/8bc3c46c34e1c13c893b5d52143f6e357ff58546))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.159 ([#21623](https://github.com/Koenkk/zigbee2mqtt/issues/21623)) ([a8a2626](https://github.com/Koenkk/zigbee2mqtt/commit/a8a262640ff075e92bfb8d0491f4f9efe8f33ce2))
|
||||
* Improve nested group and device name match ([#21521](https://github.com/Koenkk/zigbee2mqtt/issues/21521)) ([471902e](https://github.com/Koenkk/zigbee2mqtt/commit/471902e53134dbf2baddb9c37e2c20105b8f0043))
|
||||
* Match endpoint name in the MQTT topic based on endpoints listed in the definition ([#21214](https://github.com/Koenkk/zigbee2mqtt/issues/21214)) ([a5a87a7](https://github.com/Koenkk/zigbee2mqtt/commit/a5a87a79a80aaa261a34a8fb541f47347c4ca729))
|
||||
* Only configure routers on startup https://github.com/Koenkk/zigbee2mqtt/issues/21446 ([db1e3b7](https://github.com/Koenkk/zigbee2mqtt/commit/db1e3b73d1d4a2a98879ab10e5b18f84b30bd3bc))
|
||||
* Remove dependency on predefined list of endpoints (`parseEntityID()` function) ([#21264](https://github.com/Koenkk/zigbee2mqtt/issues/21264)) ([81335e5](https://github.com/Koenkk/zigbee2mqtt/commit/81335e5b4b0ed0e1dacdd764de1711f5a1481f7f))
|
||||
* Remove predefined endpoint list, remove limits on device friendly name ([#21325](https://github.com/Koenkk/zigbee2mqtt/issues/21325)) ([b4473d6](https://github.com/Koenkk/zigbee2mqtt/commit/b4473d6e24de8dc2db0ae7cb7bbb014788854ae4))
|
||||
|
||||
## [1.35.3](https://github.com/Koenkk/zigbee2mqtt/compare/1.35.2...1.35.3) (2024-02-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ignore:** set zhc logger ([f83709d](https://github.com/Koenkk/zigbee2mqtt/commit/f83709d96c4a98b2c5f34a2e06fe2ea26f9cb9d5))
|
||||
* **ignore:** update dependencies ([#21204](https://github.com/Koenkk/zigbee2mqtt/issues/21204)) ([1df54f0](https://github.com/Koenkk/zigbee2mqtt/commit/1df54f071f301240f4a7efeb8db87a9a66babf44))
|
||||
* **ignore:** update zigbee-herdsman to 0.33.7 ([#21178](https://github.com/Koenkk/zigbee2mqtt/issues/21178)) ([3d1336d](https://github.com/Koenkk/zigbee2mqtt/commit/3d1336dc6cb3f73810f4ed467df443e46cca23e0))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.26.0 ([#21145](https://github.com/Koenkk/zigbee2mqtt/issues/21145)) ([08c03e4](https://github.com/Koenkk/zigbee2mqtt/commit/08c03e48cc47b344c3f5664147afd3c4fe8c1802))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.27.0 ([#21184](https://github.com/Koenkk/zigbee2mqtt/issues/21184)) ([a678a1c](https://github.com/Koenkk/zigbee2mqtt/commit/a678a1c652a1a77fe80245d5944945ab7c75352b))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.27.1 ([#21189](https://github.com/Koenkk/zigbee2mqtt/issues/21189)) ([4b17217](https://github.com/Koenkk/zigbee2mqtt/commit/4b1721721b1580115ff952a2f49cd0c4e0dabafb))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.28.0 ([#21207](https://github.com/Koenkk/zigbee2mqtt/issues/21207)) ([3d37892](https://github.com/Koenkk/zigbee2mqtt/commit/3d3789229692eb522ef3f4b5d920f90336cf2be9))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.28.1 ([#21222](https://github.com/Koenkk/zigbee2mqtt/issues/21222)) ([a6d51a6](https://github.com/Koenkk/zigbee2mqtt/commit/a6d51a60a8b338f1245a17d228212364a6bf235c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.28.2 ([#21245](https://github.com/Koenkk/zigbee2mqtt/issues/21245)) ([250d5bf](https://github.com/Koenkk/zigbee2mqtt/commit/250d5bfc24c146e6055db7f069770af02fccca7c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.28.3 ([#21265](https://github.com/Koenkk/zigbee2mqtt/issues/21265)) ([29bae3a](https://github.com/Koenkk/zigbee2mqtt/commit/29bae3a73421c18c8437d21d727d70f10e08a1e9))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.153 ([#21187](https://github.com/Koenkk/zigbee2mqtt/issues/21187)) ([7295955](https://github.com/Koenkk/zigbee2mqtt/commit/7295955397dd829a15de08081c08ed236dee2c21))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.154 ([#21188](https://github.com/Koenkk/zigbee2mqtt/issues/21188)) ([2db0723](https://github.com/Koenkk/zigbee2mqtt/commit/2db07236dbc3b8ed2817209c72fd7103efb5fba7))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.155 ([#21228](https://github.com/Koenkk/zigbee2mqtt/issues/21228)) ([8ab6c16](https://github.com/Koenkk/zigbee2mqtt/commit/8ab6c16f6ecbf8dde9a208ea51f48603f5e21b68))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.156 ([#21229](https://github.com/Koenkk/zigbee2mqtt/issues/21229)) ([dfda631](https://github.com/Koenkk/zigbee2mqtt/commit/dfda63138ff4e0c6a0b28503d3e9c9482991b8b8))
|
||||
|
||||
## [1.35.2](https://github.com/Koenkk/zigbee2mqtt/compare/1.35.1...1.35.2) (2024-01-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Added icon and device_class for some Aqara device entities ([#20746](https://github.com/Koenkk/zigbee2mqtt/issues/20746)) ([ac74fac](https://github.com/Koenkk/zigbee2mqtt/commit/ac74fac1db9b1ee6d473c5045989003548e451f6))
|
||||
* Home Assistant: additional icons for Aqara TRV entities and various others ([#20903](https://github.com/Koenkk/zigbee2mqtt/issues/20903)) ([92588fe](https://github.com/Koenkk/zigbee2mqtt/commit/92588fefe3f10783c93c145b9b937c39bd6a5773))
|
||||
* **ignore:** Ability to trigger events from converters on incoming Zigbee messages ([#20776](https://github.com/Koenkk/zigbee2mqtt/issues/20776)) ([0007db4](https://github.com/Koenkk/zigbee2mqtt/commit/0007db42b0365c0b14510f2b03f60277a2a22303))
|
||||
* **ignore:** update zigbee-herdsman to 0.31.0 ([#20639](https://github.com/Koenkk/zigbee2mqtt/issues/20639)) ([ef84ff8](https://github.com/Koenkk/zigbee2mqtt/commit/ef84ff86fed89d0957961580f92541b20546b951))
|
||||
* **ignore:** update zigbee-herdsman to 0.32.0 ([#20681](https://github.com/Koenkk/zigbee2mqtt/issues/20681)) ([52f25eb](https://github.com/Koenkk/zigbee2mqtt/commit/52f25eb101ba6102a2c9fcb20d0bb9c56d1bd627))
|
||||
* **ignore:** update zigbee-herdsman to 0.32.1 ([#20706](https://github.com/Koenkk/zigbee2mqtt/issues/20706)) ([2bf244c](https://github.com/Koenkk/zigbee2mqtt/commit/2bf244c2dea1b6c9db6f8273dcb03e4a40fbe18a))
|
||||
* **ignore:** update zigbee-herdsman to 0.32.2 ([#20726](https://github.com/Koenkk/zigbee2mqtt/issues/20726)) ([9a9bcd5](https://github.com/Koenkk/zigbee2mqtt/commit/9a9bcd503eb736d3227dcf4fd3853739cfee293a))
|
||||
* **ignore:** update zigbee-herdsman to 0.32.3 ([#20744](https://github.com/Koenkk/zigbee2mqtt/issues/20744)) ([769bc14](https://github.com/Koenkk/zigbee2mqtt/commit/769bc14163666df402d52abba4c14a7eef99667a))
|
||||
* **ignore:** update zigbee-herdsman to 0.32.4 ([#20772](https://github.com/Koenkk/zigbee2mqtt/issues/20772)) ([ec3e373](https://github.com/Koenkk/zigbee2mqtt/commit/ec3e37350dde666512d7a966dd28db54d3da7e7c))
|
||||
* **ignore:** update zigbee-herdsman to 0.32.5 ([#20826](https://github.com/Koenkk/zigbee2mqtt/issues/20826)) ([4fe4e2e](https://github.com/Koenkk/zigbee2mqtt/commit/4fe4e2ec2f9d0d1a6173c7cca2641204c702e933))
|
||||
* **ignore:** update zigbee-herdsman to 0.32.6 ([#20846](https://github.com/Koenkk/zigbee2mqtt/issues/20846)) ([4e997c7](https://github.com/Koenkk/zigbee2mqtt/commit/4e997c7b83772dfbd73272f7ebd3acbda1a1c55e))
|
||||
* **ignore:** update zigbee-herdsman to 0.32.7 ([#20900](https://github.com/Koenkk/zigbee2mqtt/issues/20900)) ([a1360d8](https://github.com/Koenkk/zigbee2mqtt/commit/a1360d8e901ae0c5b3e3987215794e48f56ec65d))
|
||||
* **ignore:** update zigbee-herdsman to 0.33.0 ([#20932](https://github.com/Koenkk/zigbee2mqtt/issues/20932)) ([032557b](https://github.com/Koenkk/zigbee2mqtt/commit/032557b1dbf810872cef61c3a65e2f6df93441e7))
|
||||
* **ignore:** update zigbee-herdsman to 0.33.1 ([#20965](https://github.com/Koenkk/zigbee2mqtt/issues/20965)) ([ed639ec](https://github.com/Koenkk/zigbee2mqtt/commit/ed639ec9d7867785aeb776c63c927929b365993d))
|
||||
* **ignore:** update zigbee-herdsman to 0.33.2 ([#20994](https://github.com/Koenkk/zigbee2mqtt/issues/20994)) ([a240487](https://github.com/Koenkk/zigbee2mqtt/commit/a240487bb4711a1403fd1f069a8f32dc454e5b55))
|
||||
* **ignore:** update zigbee-herdsman to 0.33.3 ([#21094](https://github.com/Koenkk/zigbee2mqtt/issues/21094)) ([59001ba](https://github.com/Koenkk/zigbee2mqtt/commit/59001babca79f04938127e1592f553106b2b0b9f))
|
||||
* **ignore:** update zigbee-herdsman to 0.33.4 ([#21104](https://github.com/Koenkk/zigbee2mqtt/issues/21104)) ([03ba745](https://github.com/Koenkk/zigbee2mqtt/commit/03ba745e46b849cf7a62bdf598b8bf1d128281d2))
|
||||
* **ignore:** update zigbee-herdsman to 0.33.5 ([#21113](https://github.com/Koenkk/zigbee2mqtt/issues/21113)) ([49dea23](https://github.com/Koenkk/zigbee2mqtt/commit/49dea233b6248d699a2d0f641ce0a84c9b91e92c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.10.1 ([#20705](https://github.com/Koenkk/zigbee2mqtt/issues/20705)) ([5c8f95a](https://github.com/Koenkk/zigbee2mqtt/commit/5c8f95ae0146aff91c305643c9fa41c8dc690fa7))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.11.0 ([#20742](https://github.com/Koenkk/zigbee2mqtt/issues/20742)) ([a38b884](https://github.com/Koenkk/zigbee2mqtt/commit/a38b88455f8a77fe5e9997f05374d3454b100b41))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.13.0 ([#20828](https://github.com/Koenkk/zigbee2mqtt/issues/20828)) ([6ccbb11](https://github.com/Koenkk/zigbee2mqtt/commit/6ccbb11fc20d63f86eea03ffc26c9bd790b2ee5e))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.14.0 ([#20847](https://github.com/Koenkk/zigbee2mqtt/issues/20847)) ([ab9124a](https://github.com/Koenkk/zigbee2mqtt/commit/ab9124a5179bd87d496944d7bab5794eab6505e3))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.15.0 ([#20860](https://github.com/Koenkk/zigbee2mqtt/issues/20860)) ([080f3b6](https://github.com/Koenkk/zigbee2mqtt/commit/080f3b6b50c58636aa217d143a0f3d18fbb55c7f))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.15.1 ([#20876](https://github.com/Koenkk/zigbee2mqtt/issues/20876)) ([9f3b102](https://github.com/Koenkk/zigbee2mqtt/commit/9f3b10235cf0f5db38098c4a73bd64d4d53ba6c2))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.16.0 ([#20901](https://github.com/Koenkk/zigbee2mqtt/issues/20901)) ([8de20e2](https://github.com/Koenkk/zigbee2mqtt/commit/8de20e2cf1107366dd958c7e781a2011f7dd8f2d))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.17.0 ([#20907](https://github.com/Koenkk/zigbee2mqtt/issues/20907)) ([de7bc7c](https://github.com/Koenkk/zigbee2mqtt/commit/de7bc7cf60752874262f684ac7f3923057aab3c2))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.19.0 ([#20966](https://github.com/Koenkk/zigbee2mqtt/issues/20966)) ([5189728](https://github.com/Koenkk/zigbee2mqtt/commit/518972882c7a02bfc076aa5093853f23947d769d))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.20.0 ([#20996](https://github.com/Koenkk/zigbee2mqtt/issues/20996)) ([416e561](https://github.com/Koenkk/zigbee2mqtt/commit/416e561b329b65fa50af23e08c3d82cbc52387da))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.21.0 ([#21012](https://github.com/Koenkk/zigbee2mqtt/issues/21012)) ([a879382](https://github.com/Koenkk/zigbee2mqtt/commit/a879382cbd32bd0935d91b62e6cbbf2a0c9987a4))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.23.1 ([#21093](https://github.com/Koenkk/zigbee2mqtt/issues/21093)) ([7e7d3c6](https://github.com/Koenkk/zigbee2mqtt/commit/7e7d3c6328c0cbd89264d6313367dbd1295b0b42))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.24.0 ([#21106](https://github.com/Koenkk/zigbee2mqtt/issues/21106)) ([f3e79df](https://github.com/Koenkk/zigbee2mqtt/commit/f3e79df4b4c7a5c68a70375e25507e58c6378b1a))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.25.1 ([#21129](https://github.com/Koenkk/zigbee2mqtt/issues/21129)) ([0b59a51](https://github.com/Koenkk/zigbee2mqtt/commit/0b59a51c3c740da5c5d7b86514651c21a5c7387b))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.152 ([#21003](https://github.com/Koenkk/zigbee2mqtt/issues/21003)) ([a858798](https://github.com/Koenkk/zigbee2mqtt/commit/a85879805489761d429d1dec4d98c1ae2ebd9400))
|
||||
* Notify systemd for start, stop, watchdog ([#20482](https://github.com/Koenkk/zigbee2mqtt/issues/20482)) ([97eac16](https://github.com/Koenkk/zigbee2mqtt/commit/97eac16aaf10d9bec4a5353b07ae3ebef92868e5))
|
||||
* Remove deprecated sendWhen ([#20861](https://github.com/Koenkk/zigbee2mqtt/issues/20861)) ([e76f568](https://github.com/Koenkk/zigbee2mqtt/commit/e76f568f0fe0a36e5324b20b8ca3173f618cd8e6))
|
||||
* Republish correctly on scene changes to Home Assistant ([#20952](https://github.com/Koenkk/zigbee2mqtt/issues/20952)) ([6372d84](https://github.com/Koenkk/zigbee2mqtt/commit/6372d842596240685ead4a5a10f9a739ec208504))
|
||||
* Revert "Enable incremental TSC builds ([#19293](https://github.com/Koenkk/zigbee2mqtt/issues/19293))" https://github.com/Koenkk/zigbee2mqtt/discussions/20805 ([a741b2f](https://github.com/Koenkk/zigbee2mqtt/commit/a741b2f598159e1fbd6799619d0e575ad11c0256))
|
||||
* Set Home Assistant entity categories `config` and `diagnostic` according to expose categories ([#20663](https://github.com/Koenkk/zigbee2mqtt/issues/20663)) ([363792a](https://github.com/Koenkk/zigbee2mqtt/commit/363792aee8f9535163e7c0aa3e5b9961165fc088))
|
||||
* Use Alpine 3.18.4 as base https://github.com/Koenkk/zigbee2mqtt/issues/20868 ([c16abdd](https://github.com/Koenkk/zigbee2mqtt/commit/c16abdd83233c51fa5f7d6b56db6b19bea9b81f6))
|
||||
|
||||
## [1.35.1](https://github.com/Koenkk/zigbee2mqtt/compare/1.35.0...1.35.1) (2024-01-07)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix configure on startup crashing coordinator https://github.com/Koenkk/zigbee2mqtt/issues/20526 ([e9aee4c](https://github.com/Koenkk/zigbee2mqtt/commit/e9aee4c6dc1b7dcfedfa821723b029e1a0423d15))
|
||||
* Fix crash due to unhandled Promise exception https://github.com/Koenkk/zigbee2mqtt/issues/20473 ([e6bcf91](https://github.com/Koenkk/zigbee2mqtt/commit/e6bcf91e17747eff38ce7774107d697aeff4be15))
|
||||
* Fix Home Assistant `program` not truncated when too large https://github.com/Koenkk/zigbee2mqtt/issues/19788 ([32b1d5b](https://github.com/Koenkk/zigbee2mqtt/commit/32b1d5b0732544a2026f8582f8bb12a3e5691b2b))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.4.0 ([#20523](https://github.com/Koenkk/zigbee2mqtt/issues/20523)) ([7e37365](https://github.com/Koenkk/zigbee2mqtt/commit/7e37365a5faec6d8d33db406ed18d5492fa2fc19))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.6.0 ([#20585](https://github.com/Koenkk/zigbee2mqtt/issues/20585)) ([b17c22b](https://github.com/Koenkk/zigbee2mqtt/commit/b17c22bb501599fcc68685ff0fc65642d1dea9ca))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.8.0 ([#20613](https://github.com/Koenkk/zigbee2mqtt/issues/20613)) ([14ac9a8](https://github.com/Koenkk/zigbee2mqtt/commit/14ac9a8f68480c433450cb1f33f8feda00359268))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.9.0 ([#20635](https://github.com/Koenkk/zigbee2mqtt/issues/20635)) ([759c235](https://github.com/Koenkk/zigbee2mqtt/commit/759c2359234a5f83b9a7f2a5b97e75aef9d0d567))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.151 ([#20509](https://github.com/Koenkk/zigbee2mqtt/issues/20509)) ([cb7b773](https://github.com/Koenkk/zigbee2mqtt/commit/cb7b773e0defb1e28dc17af42e3807e7628338df))
|
||||
* Prevent extension errors from crashing Zigbee2MQTT https://github.com/Koenkk/zigbee2mqtt/issues/20477 ([212c709](https://github.com/Koenkk/zigbee2mqtt/commit/212c70913c06a97dea8dac7d532a7d601c69a3d1))
|
||||
* Prevent warnings for bridge entities with legacy_entity_attributes enabled ([#20548](https://github.com/Koenkk/zigbee2mqtt/issues/20548)) ([99a57bf](https://github.com/Koenkk/zigbee2mqtt/commit/99a57bfd080fcf311f7cc45e7d0d04cdc0a6138a))
|
||||
* Support legacy_availability_payload for Home Assistant connection_state ([#20547](https://github.com/Koenkk/zigbee2mqtt/issues/20547)) ([b8edf27](https://github.com/Koenkk/zigbee2mqtt/commit/b8edf273c6de799e30c95c607f315f02b0dcacdd))
|
||||
|
||||
## [1.35.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.34.0...v1.35.0) (2024-01-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Support automatically generating device definitions ([#20358](https://github.com/Koenkk/zigbee2mqtt/issues/20358)) ([56f5762](https://github.com/Koenkk/zigbee2mqtt/commit/56f57625b0d5ce5b3bf6fa6a1a048f6ae8902311))
|
||||
* Support bridge discovery for Home Assistant ([#19994](https://github.com/Koenkk/zigbee2mqtt/issues/19994)) ([f11eb24](https://github.com/Koenkk/zigbee2mqtt/commit/f11eb2459bd1ad89a1588b8d2d023d949faf3313))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add Home Assistant `entity_category` for various Ubisys D1 entities ([#20393](https://github.com/Koenkk/zigbee2mqtt/issues/20393)) ([085b46c](https://github.com/Koenkk/zigbee2mqtt/commit/085b46c995c888751ca5a1ff73391b8f56591826))
|
||||
* Allow `null` for `zigbee_ota_override_index_location` option ([#20151](https://github.com/Koenkk/zigbee2mqtt/issues/20151)) ([48347a5](https://github.com/Koenkk/zigbee2mqtt/commit/48347a51dcff3ce8ed533c71cc27651bc884228b))
|
||||
* Clear old Home Assistant discovery topics when exposes are changed ([#20313](https://github.com/Koenkk/zigbee2mqtt/issues/20313)) ([a5b959a](https://github.com/Koenkk/zigbee2mqtt/commit/a5b959a6d520679bce1586c50f01a57f13d6af01))
|
||||
* Don't crash on startup when external converters fails to load https://github.com/Koenkk/zigbee2mqtt/issues/20010 ([8ab96e1](https://github.com/Koenkk/zigbee2mqtt/commit/8ab96e124fa5e24e504b2a11ae6cf9836d93f5c8))
|
||||
* Fix availability checks not stopped on extension stop ([#20093](https://github.com/Koenkk/zigbee2mqtt/issues/20093)) ([5aeec13](https://github.com/Koenkk/zigbee2mqtt/commit/5aeec130b19e112ad52cbc506a810dcdfc2e2605))
|
||||
* Fix build on Node 20+ ([#19983](https://github.com/Koenkk/zigbee2mqtt/issues/19983)) ([9c8aee1](https://github.com/Koenkk/zigbee2mqtt/commit/9c8aee17dc9ee0cea311d99c282f1558d2fb841b))
|
||||
* Fix Home Assistant `Entity category `config` is invalid for sensors` warning https://github.com/Koenkk/zigbee2mqtt/issues/20252 ([8f62a0a](https://github.com/Koenkk/zigbee2mqtt/commit/8f62a0a61899837c63d7053efd68b7a315d623ac))
|
||||
* Fix load of empty configuration `.yaml` file failing ([#20289](https://github.com/Koenkk/zigbee2mqtt/issues/20289)) ([b67888f](https://github.com/Koenkk/zigbee2mqtt/commit/b67888f938e9e04b8547d7569b78058ba3fb017b))
|
||||
* **ignore:** Ability to use a specified endpoint in the Tz.Converter ([#20356](https://github.com/Koenkk/zigbee2mqtt/issues/20356)) ([d702919](https://github.com/Koenkk/zigbee2mqtt/commit/d702919735c2f399d73c3fb902721e4b5367b320))
|
||||
* **ignore:** Changes for zhc 18.0.0 ([#20390](https://github.com/Koenkk/zigbee2mqtt/issues/20390)) ([e6e3d92](https://github.com/Koenkk/zigbee2mqtt/commit/e6e3d92971162494ff999f74d13b00429b164c51))
|
||||
* **ignore:** Downgrade connect-gzip-static to 3.0.1 https://github.com/Koenkk/zigbee2mqtt/issues/20149 ([f64c00c](https://github.com/Koenkk/zigbee2mqtt/commit/f64c00c8d34dae64279a607368c82c06dc9dc53a))
|
||||
* **ignore:** Fix "Permit join timeout" template ([#20096](https://github.com/Koenkk/zigbee2mqtt/issues/20096)) ([59efc15](https://github.com/Koenkk/zigbee2mqtt/commit/59efc15f3adb58954f3a9eb524d728679fa28e77))
|
||||
* **ignore:** fix bind test ([d150105](https://github.com/Koenkk/zigbee2mqtt/commit/d1501052ad1dc7d1cdf9e0f925c3e9694c748226))
|
||||
* **ignore:** fix dd1e0db407f80add7355879823cf519ae869a7f6 ([04d8522](https://github.com/Koenkk/zigbee2mqtt/commit/04d8522b3ba95f83e0b569373726e4da7a1ece13))
|
||||
* **ignore:** fix dee8781e51f2aac466e273fb81cbdd31da56ac18 ([32b0ded](https://github.com/Koenkk/zigbee2mqtt/commit/32b0ded17c781fea0474c9928d795a3eb3c81acf))
|
||||
* **ignore:** fix homeassistant.test.js ([dee8781](https://github.com/Koenkk/zigbee2mqtt/commit/dee8781e51f2aac466e273fb81cbdd31da56ac18))
|
||||
* **ignore:** Fix OTA sensor discoverd while device doesn't support it https://github.com/Koenkk/zigbee2mqtt/issues/20131 ([abde909](https://github.com/Koenkk/zigbee2mqtt/commit/abde909b7106ffdc0d841c44b9791aaafd874cc2))
|
||||
* **ignore:** improve 8f62a0a ([2e704f3](https://github.com/Koenkk/zigbee2mqtt/commit/2e704f3ff7ac1dbe1b6f12c7cdace29850a288cf))
|
||||
* **ignore:** Revert module to commonjs ([6b6cc8a](https://github.com/Koenkk/zigbee2mqtt/commit/6b6cc8aa34ec8b63db332cd8ad33d3592e6c515b))
|
||||
* **ignore:** Update alpine to 3.19.0 ([#20392](https://github.com/Koenkk/zigbee2mqtt/issues/20392)) ([eaaf1b0](https://github.com/Koenkk/zigbee2mqtt/commit/eaaf1b0b4f6ebd631117ae13666e5e0ebcb89b23))
|
||||
* **ignore:** update dependencies ([#19896](https://github.com/Koenkk/zigbee2mqtt/issues/19896)) ([6e843d0](https://github.com/Koenkk/zigbee2mqtt/commit/6e843d0f1cdf0e76a2a07cf3d33f3287f0a5e70c))
|
||||
* **ignore:** update dependencies ([#20023](https://github.com/Koenkk/zigbee2mqtt/issues/20023)) ([e0deef8](https://github.com/Koenkk/zigbee2mqtt/commit/e0deef8889835949af99e4dfd4c47a182d329514))
|
||||
* **ignore:** update dependencies ([#20133](https://github.com/Koenkk/zigbee2mqtt/issues/20133)) ([b415443](https://github.com/Koenkk/zigbee2mqtt/commit/b415443126e3ee0bda25a9fee8e37615c2290b2e))
|
||||
* **ignore:** update dependencies ([#20249](https://github.com/Koenkk/zigbee2mqtt/issues/20249)) ([6e52b2a](https://github.com/Koenkk/zigbee2mqtt/commit/6e52b2a7af0ec9bd636a9be12ff7aa16e2c37f04))
|
||||
* **ignore:** update dependencies ([#20256](https://github.com/Koenkk/zigbee2mqtt/issues/20256)) ([1e4ac34](https://github.com/Koenkk/zigbee2mqtt/commit/1e4ac34c443e29df9631b97ed83c407c2c174af1))
|
||||
* **ignore:** update dependencies ([#20257](https://github.com/Koenkk/zigbee2mqtt/issues/20257)) ([19754c6](https://github.com/Koenkk/zigbee2mqtt/commit/19754c69e51d642dd9360aa27c3d0a121f8d5d61))
|
||||
* **ignore:** update dependencies ([#20368](https://github.com/Koenkk/zigbee2mqtt/issues/20368)) ([0385791](https://github.com/Koenkk/zigbee2mqtt/commit/03857916c455a08c42bc241bc643af7a91061b5d))
|
||||
* **ignore:** update dependencies ([#20452](https://github.com/Koenkk/zigbee2mqtt/issues/20452)) ([16730b1](https://github.com/Koenkk/zigbee2mqtt/commit/16730b14a1d1de396fd8f6260567a1da4afae623))
|
||||
* **ignore:** Update tsconfig for Node 18 ([084b048](https://github.com/Koenkk/zigbee2mqtt/commit/084b048ff389261edbd72f05e0c461e725574b6e))
|
||||
* **ignore:** update zigbee-herdsman to 0.25.1 ([#19985](https://github.com/Koenkk/zigbee2mqtt/issues/19985)) ([a822873](https://github.com/Koenkk/zigbee2mqtt/commit/a8228737480dac0d61768131c7b4850a614db4fc))
|
||||
* **ignore:** update zigbee-herdsman to 0.25.2 ([#19997](https://github.com/Koenkk/zigbee2mqtt/issues/19997)) ([9a2425a](https://github.com/Koenkk/zigbee2mqtt/commit/9a2425a202bc70a6d7f4ba534e145bcb3736f041))
|
||||
* **ignore:** update zigbee-herdsman to 0.25.3 ([#20025](https://github.com/Koenkk/zigbee2mqtt/issues/20025)) ([483acc3](https://github.com/Koenkk/zigbee2mqtt/commit/483acc3ca156e441bb82d9e8040ccf9dc7a5e1e5))
|
||||
* **ignore:** update zigbee-herdsman to 0.26.0 ([#20092](https://github.com/Koenkk/zigbee2mqtt/issues/20092)) ([8929ab3](https://github.com/Koenkk/zigbee2mqtt/commit/8929ab3b9704e924fc2f0f5e84e6d15e5c14a8b5))
|
||||
* **ignore:** update zigbee-herdsman to 0.26.1 ([#20143](https://github.com/Koenkk/zigbee2mqtt/issues/20143)) ([a26008f](https://github.com/Koenkk/zigbee2mqtt/commit/a26008f74c7a8ef138e51debc79965fde53ca61b))
|
||||
* **ignore:** update zigbee-herdsman to 0.27.1 ([#20262](https://github.com/Koenkk/zigbee2mqtt/issues/20262)) ([65d7f5f](https://github.com/Koenkk/zigbee2mqtt/commit/65d7f5f3e27ad4a8dbe2abeca1f5ffa349c9631b))
|
||||
* **ignore:** update zigbee-herdsman to 0.28.0 ([#20357](https://github.com/Koenkk/zigbee2mqtt/issues/20357)) ([89f8e43](https://github.com/Koenkk/zigbee2mqtt/commit/89f8e43ad11ffd84cf6a3c9d3bd57d5577063e15))
|
||||
* **ignore:** update zigbee-herdsman to 0.29.0 ([#20403](https://github.com/Koenkk/zigbee2mqtt/issues/20403)) ([03a1ce3](https://github.com/Koenkk/zigbee2mqtt/commit/03a1ce3895cbc9dafb1c83a7013e1f96409db05a))
|
||||
* **ignore:** update zigbee-herdsman to 0.29.1 ([#20406](https://github.com/Koenkk/zigbee2mqtt/issues/20406)) ([7bca19d](https://github.com/Koenkk/zigbee2mqtt/commit/7bca19dae777eb919a0017b4c52304ec83972e73))
|
||||
* **ignore:** update zigbee-herdsman to 0.30.0 ([#20464](https://github.com/Koenkk/zigbee2mqtt/issues/20464)) ([05c65d3](https://github.com/Koenkk/zigbee2mqtt/commit/05c65d31ed76f0f5f080518a4196ca7999a41adc))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.131.0 ([#19987](https://github.com/Koenkk/zigbee2mqtt/issues/19987)) ([af02370](https://github.com/Koenkk/zigbee2mqtt/commit/af02370777fc5e81363810c759359e5ba17e3bf7))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.132.0 ([#20005](https://github.com/Koenkk/zigbee2mqtt/issues/20005)) ([5afd16a](https://github.com/Koenkk/zigbee2mqtt/commit/5afd16ac652560250ec9a21f4d05fba7e25af853))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.0.0 ([#20008](https://github.com/Koenkk/zigbee2mqtt/issues/20008)) ([2fa8afe](https://github.com/Koenkk/zigbee2mqtt/commit/2fa8afe37f99425234b2841a3ffd8c954a7b78c6))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.0.1 ([#20012](https://github.com/Koenkk/zigbee2mqtt/issues/20012)) ([5e3bd75](https://github.com/Koenkk/zigbee2mqtt/commit/5e3bd75f098b28a5df544ef5b06eab9f35020419))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.0.2 ([#20027](https://github.com/Koenkk/zigbee2mqtt/issues/20027)) ([e60b94b](https://github.com/Koenkk/zigbee2mqtt/commit/e60b94b55329e30f00c1dec5094f139233d08521))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.1.0 ([#20059](https://github.com/Koenkk/zigbee2mqtt/issues/20059)) ([cb74db4](https://github.com/Koenkk/zigbee2mqtt/commit/cb74db47f7140357796618350c1ae73b7b1d4d1a))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.11.0 ([#20228](https://github.com/Koenkk/zigbee2mqtt/issues/20228)) ([2c5d3e1](https://github.com/Koenkk/zigbee2mqtt/commit/2c5d3e14f779ff138949f11f6a443fd8821e0120))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.12.0 ([#20258](https://github.com/Koenkk/zigbee2mqtt/issues/20258)) ([c26a6a2](https://github.com/Koenkk/zigbee2mqtt/commit/c26a6a28e4562d7cc97763f6c02782525575f84f))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.13.0 ([#20280](https://github.com/Koenkk/zigbee2mqtt/issues/20280)) ([29635a9](https://github.com/Koenkk/zigbee2mqtt/commit/29635a9b6aedd8f140c126a36813bb922f6a450e))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.14.1 ([#20301](https://github.com/Koenkk/zigbee2mqtt/issues/20301)) ([af8c2d8](https://github.com/Koenkk/zigbee2mqtt/commit/af8c2d86fe1b50934ec8ccd675b298dc2f86a29a))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.15.0 ([#20318](https://github.com/Koenkk/zigbee2mqtt/issues/20318)) ([27ccd1a](https://github.com/Koenkk/zigbee2mqtt/commit/27ccd1a5704b366f86b4c708865831abe40cd2ad))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.16.0 ([#20326](https://github.com/Koenkk/zigbee2mqtt/issues/20326)) ([6875146](https://github.com/Koenkk/zigbee2mqtt/commit/687514621152c48809d922af9e712a45c762173d))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.17.0 ([#20359](https://github.com/Koenkk/zigbee2mqtt/issues/20359)) ([1a72650](https://github.com/Koenkk/zigbee2mqtt/commit/1a7265017a0b7facbe88058daa1ef47fcc79747e))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.18.0 ([#20374](https://github.com/Koenkk/zigbee2mqtt/issues/20374)) ([fe7e82f](https://github.com/Koenkk/zigbee2mqtt/commit/fe7e82f7d34db4d79448f198f51902592633fbc1))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.2.0 ([#20094](https://github.com/Koenkk/zigbee2mqtt/issues/20094)) ([cb50942](https://github.com/Koenkk/zigbee2mqtt/commit/cb509426eca455d693723b5631313163b931dbc9))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.3.1 ([#20109](https://github.com/Koenkk/zigbee2mqtt/issues/20109)) ([4e6bc33](https://github.com/Koenkk/zigbee2mqtt/commit/4e6bc33a2203591a9bcee84eb90cefdb9c883c69))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.4.0 ([#20120](https://github.com/Koenkk/zigbee2mqtt/issues/20120)) ([4cc88f1](https://github.com/Koenkk/zigbee2mqtt/commit/4cc88f1e16e5bd8e901c555c6950f14d46013787))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.5.0 ([#20125](https://github.com/Koenkk/zigbee2mqtt/issues/20125)) ([5ff10c3](https://github.com/Koenkk/zigbee2mqtt/commit/5ff10c3e5ced22a6070360f76ea9ffd7e9805194))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.6.0 ([#20145](https://github.com/Koenkk/zigbee2mqtt/issues/20145)) ([87f6b9e](https://github.com/Koenkk/zigbee2mqtt/commit/87f6b9e81a4efaa857c06ba179665535e1beb742))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.7.0 ([#20167](https://github.com/Koenkk/zigbee2mqtt/issues/20167)) ([7c81eda](https://github.com/Koenkk/zigbee2mqtt/commit/7c81eda46a1b7d3ce190a11f89618e60afaba7b5))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.8.0 ([#20185](https://github.com/Koenkk/zigbee2mqtt/issues/20185)) ([bcf50bd](https://github.com/Koenkk/zigbee2mqtt/commit/bcf50bd0c532d822768d380cb99f793e00db07f0))
|
||||
* **ignore:** update zigbee-herdsman-converters to 16.9.0 ([#20200](https://github.com/Koenkk/zigbee2mqtt/issues/20200)) ([25de4fd](https://github.com/Koenkk/zigbee2mqtt/commit/25de4fded9bd67bafb2e8a596490988c624dce88))
|
||||
* **ignore:** update zigbee-herdsman-converters to 17.0.0 ([#20378](https://github.com/Koenkk/zigbee2mqtt/issues/20378)) ([2110954](https://github.com/Koenkk/zigbee2mqtt/commit/2110954bb8c47b4fb9b3b536e857183cb4fc83ac))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.0.0 ([#20391](https://github.com/Koenkk/zigbee2mqtt/issues/20391)) ([bf744f4](https://github.com/Koenkk/zigbee2mqtt/commit/bf744f48dc820f46f9944cf52513437908586d79))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.1.0 ([#20426](https://github.com/Koenkk/zigbee2mqtt/issues/20426)) ([a3a87f4](https://github.com/Koenkk/zigbee2mqtt/commit/a3a87f4580ad4b2a77d1c039232035f7cf5797ac))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.2.0 ([#20451](https://github.com/Koenkk/zigbee2mqtt/issues/20451)) ([a8c791b](https://github.com/Koenkk/zigbee2mqtt/commit/a8c791b0302cb885671bdfd0dd785a83185dffe0))
|
||||
* **ignore:** update zigbee-herdsman-converters to 18.3.0 ([#20465](https://github.com/Koenkk/zigbee2mqtt/issues/20465)) ([f9e0c20](https://github.com/Koenkk/zigbee2mqtt/commit/f9e0c2058fe15517d6b17445eb48ad6c9777c3db))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.148 ([#20034](https://github.com/Koenkk/zigbee2mqtt/issues/20034)) ([ffcb820](https://github.com/Koenkk/zigbee2mqtt/commit/ffcb8202f01d9ba627a362abdbc350793e269647))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.149 ([#20141](https://github.com/Koenkk/zigbee2mqtt/issues/20141)) ([c7e2944](https://github.com/Koenkk/zigbee2mqtt/commit/c7e294482bfd7dacb5fc70858041141070a6971d))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.150 ([#20377](https://github.com/Koenkk/zigbee2mqtt/issues/20377)) ([d616e57](https://github.com/Koenkk/zigbee2mqtt/commit/d616e57daf89f36b145a46bec01b80deaaaae5a7))
|
||||
* **ignore:** Use zhc types ([#20006](https://github.com/Koenkk/zigbee2mqtt/issues/20006)) ([dd1e0db](https://github.com/Koenkk/zigbee2mqtt/commit/dd1e0db407f80add7355879823cf519ae869a7f6))
|
||||
* Make update script work from anywhere ([#19965](https://github.com/Koenkk/zigbee2mqtt/issues/19965)) ([346cb90](https://github.com/Koenkk/zigbee2mqtt/commit/346cb90e9e78fd08ffb49cb954bcbee2f3bc9191))
|
||||
* Only republish changed scenes to Home Assistant when changed ([#20248](https://github.com/Koenkk/zigbee2mqtt/issues/20248)) ([e0f5021](https://github.com/Koenkk/zigbee2mqtt/commit/e0f50218d70c1932fbc32f28f60737a4bff5f048))
|
||||
* Prevent duplicate Home Assistant scene entities ([#20097](https://github.com/Koenkk/zigbee2mqtt/issues/20097)) ([a17f650](https://github.com/Koenkk/zigbee2mqtt/commit/a17f650ccbb8113874dc5dd5f5ad3e3419dc6c15))
|
||||
* Support Node 21 and drop 16 ([adc0812](https://github.com/Koenkk/zigbee2mqtt/commit/adc08125fe892dfe456edf3a4d85da23b6f5798d))
|
||||
|
||||
## [1.34.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.33.2...1.34.0) (2023-12-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add Home Assistant scene discovery ([#19838](https://github.com/Koenkk/zigbee2mqtt/issues/19838)) ([838629c](https://github.com/Koenkk/zigbee2mqtt/commit/838629ca42162d3d910b06bbc100b6270f75de33))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix `in_progress` attribute for Home Assistant update sensor ([#19542](https://github.com/Koenkk/zigbee2mqtt/issues/19542)) ([1f2bd84](https://github.com/Koenkk/zigbee2mqtt/commit/1f2bd841c4548777fc0c0f6ad6c54dfe913b3fd0))
|
||||
* Fix entity category for Home Assistant `binary_sensor` https://github.com/Koenkk/zigbee2mqtt/pull/19474 ([071842e](https://github.com/Koenkk/zigbee2mqtt/commit/071842ef49eccc002efefbad3abf2780e6fdd0a2))
|
||||
* Fix Home Assistant `in_progress` Erroneous JSON error https://github.com/Koenkk/zigbee2mqtt/issues/19597 ([2540fbd](https://github.com/Koenkk/zigbee2mqtt/commit/2540fbd771d01ee18cbb72c67cb0a24b44131912))
|
||||
* Fix invalid JSON error in Home Assistant update entity ([#19659](https://github.com/Koenkk/zigbee2mqtt/issues/19659)) ([da04d22](https://github.com/Koenkk/zigbee2mqtt/commit/da04d223b446000df5cc59ccb38115eb3e5eac96))
|
||||
* Fix unable to configure again after configure fails ([#19908](https://github.com/Koenkk/zigbee2mqtt/issues/19908)) ([a904e7a](https://github.com/Koenkk/zigbee2mqtt/commit/a904e7a396e34bbeef2e624702d55e9d0522b242))
|
||||
* **ignore:** Fix color_temp_percent test ([01d32cb](https://github.com/Koenkk/zigbee2mqtt/commit/01d32cb2301f6db11f61bca113a2db8d86c7acc1))
|
||||
* **ignore:** Fixes quote type for update JSON attribute discovery ([#19668](https://github.com/Koenkk/zigbee2mqtt/issues/19668)) ([baae082](https://github.com/Koenkk/zigbee2mqtt/commit/baae0822583fb29e079c57746b047cb602f017a7))
|
||||
* **ignore:** remove console.log in test ([8cf41a1](https://github.com/Koenkk/zigbee2mqtt/commit/8cf41a137aa8275e61a244b0096796466b10648d))
|
||||
* **ignore:** remove unused variable ([#19584](https://github.com/Koenkk/zigbee2mqtt/issues/19584)) ([be48d54](https://github.com/Koenkk/zigbee2mqtt/commit/be48d545cc03c95b0a6302b67cc04b4e7d187065))
|
||||
* **ignore:** update dependencies ([#19570](https://github.com/Koenkk/zigbee2mqtt/issues/19570)) ([fc073e8](https://github.com/Koenkk/zigbee2mqtt/commit/fc073e8743c0ba9c05d0a952f6bf3ae6fa804f68))
|
||||
* **ignore:** update dependencies ([#19679](https://github.com/Koenkk/zigbee2mqtt/issues/19679)) ([c8848be](https://github.com/Koenkk/zigbee2mqtt/commit/c8848be981c709ebb52bfb9c7347ab3a88cce82e))
|
||||
* **ignore:** update zigbee-herdsman to 0.22.0 ([#19547](https://github.com/Koenkk/zigbee2mqtt/issues/19547)) ([02a5a1b](https://github.com/Koenkk/zigbee2mqtt/commit/02a5a1b14f4907e7d170ef3e3e4f93b176c4bbc5))
|
||||
* **ignore:** update zigbee-herdsman to 0.22.1 ([#19602](https://github.com/Koenkk/zigbee2mqtt/issues/19602)) ([2d5bb24](https://github.com/Koenkk/zigbee2mqtt/commit/2d5bb2462eb547bc75df1bace2f01b6b15e6d171))
|
||||
* **ignore:** update zigbee-herdsman to 0.23.0 ([#19700](https://github.com/Koenkk/zigbee2mqtt/issues/19700)) ([a152bde](https://github.com/Koenkk/zigbee2mqtt/commit/a152bded93877cfab3839b9b22e7d22ce43fb263))
|
||||
* **ignore:** update zigbee-herdsman to 0.24.0 ([#19926](https://github.com/Koenkk/zigbee2mqtt/issues/19926)) ([e5dc271](https://github.com/Koenkk/zigbee2mqtt/commit/e5dc271a783aeec0c28e106f0f259a083d737d7c))
|
||||
* **ignore:** update zigbee-herdsman to 0.25.0 ([#19933](https://github.com/Koenkk/zigbee2mqtt/issues/19933)) ([2dabc1e](https://github.com/Koenkk/zigbee2mqtt/commit/2dabc1e61b897fdb041a42bda7adc6acac6bb2b3))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.107.0 ([#19548](https://github.com/Koenkk/zigbee2mqtt/issues/19548)) ([366e29d](https://github.com/Koenkk/zigbee2mqtt/commit/366e29d4d9126eaeca2476cc0fa56cf7faf70764))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.108.0 ([#19589](https://github.com/Koenkk/zigbee2mqtt/issues/19589)) ([1fd1686](https://github.com/Koenkk/zigbee2mqtt/commit/1fd1686ee63cbd2b41d9d0a13669f9c665eb36da))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.109.0 ([#19601](https://github.com/Koenkk/zigbee2mqtt/issues/19601)) ([b71aa0a](https://github.com/Koenkk/zigbee2mqtt/commit/b71aa0a3fd9a8e6a7fcc5e7bdb4a2f2b37cd690a))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.110.0 ([#19619](https://github.com/Koenkk/zigbee2mqtt/issues/19619)) ([5889cdd](https://github.com/Koenkk/zigbee2mqtt/commit/5889cdd9abe6b41899a4a7422b696bde8bbb0390))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.111.0 ([#19630](https://github.com/Koenkk/zigbee2mqtt/issues/19630)) ([354ada5](https://github.com/Koenkk/zigbee2mqtt/commit/354ada5c4c33d88677ce7f8b072b682dde6f19aa))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.112.0 ([#19643](https://github.com/Koenkk/zigbee2mqtt/issues/19643)) ([d26ea1a](https://github.com/Koenkk/zigbee2mqtt/commit/d26ea1a4112f9e142ac72e2add82784012c28c51))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.113.0 ([#19663](https://github.com/Koenkk/zigbee2mqtt/issues/19663)) ([c8dcd71](https://github.com/Koenkk/zigbee2mqtt/commit/c8dcd716ab271bdbe9d259943fb77d6d6accbc06))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.114.0 ([#19692](https://github.com/Koenkk/zigbee2mqtt/issues/19692)) ([6587812](https://github.com/Koenkk/zigbee2mqtt/commit/6587812b725d28dc200eb42f6b0714ed1ed76e9f))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.115.0 ([#19701](https://github.com/Koenkk/zigbee2mqtt/issues/19701)) ([157dff1](https://github.com/Koenkk/zigbee2mqtt/commit/157dff14d3ac32c70f13f5f7be0a85e2763bf2a6))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.116.0 ([#19714](https://github.com/Koenkk/zigbee2mqtt/issues/19714)) ([4815ac7](https://github.com/Koenkk/zigbee2mqtt/commit/4815ac711003632ac21977b120dea7444ebeda3e))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.117.0 ([#19727](https://github.com/Koenkk/zigbee2mqtt/issues/19727)) ([746066c](https://github.com/Koenkk/zigbee2mqtt/commit/746066c9205a7ab77393d1934aacb7f17209a1e0))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.119.0 ([#19740](https://github.com/Koenkk/zigbee2mqtt/issues/19740)) ([ad4bed8](https://github.com/Koenkk/zigbee2mqtt/commit/ad4bed89215e1727f0966708f510b8c933ac1a32))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.120.0 ([#19761](https://github.com/Koenkk/zigbee2mqtt/issues/19761)) ([fd19c86](https://github.com/Koenkk/zigbee2mqtt/commit/fd19c86cb19513dadae9a44ee8b50d287cc1860e))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.121.0 ([#19777](https://github.com/Koenkk/zigbee2mqtt/issues/19777)) ([d4e3aa9](https://github.com/Koenkk/zigbee2mqtt/commit/d4e3aa9d976b67cb1519ca78c9b8de5e70247ad9))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.123.1 ([#19809](https://github.com/Koenkk/zigbee2mqtt/issues/19809)) ([2d08baf](https://github.com/Koenkk/zigbee2mqtt/commit/2d08baf6204bde6f2ec90dba4a6a77963b14310c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.124.0 ([#19836](https://github.com/Koenkk/zigbee2mqtt/issues/19836)) ([ef4d9e1](https://github.com/Koenkk/zigbee2mqtt/commit/ef4d9e1e558561c1625d1a0d41ddc718d40b5c61))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.125.0 ([#19854](https://github.com/Koenkk/zigbee2mqtt/issues/19854)) ([ef00a64](https://github.com/Koenkk/zigbee2mqtt/commit/ef00a64bf87adf38346461f5fa15757052400b2a))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.125.1 ([#19867](https://github.com/Koenkk/zigbee2mqtt/issues/19867)) ([51a9341](https://github.com/Koenkk/zigbee2mqtt/commit/51a93418fa2de62e6691ef23a65725d28d9c8b42))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.126.0 ([#19878](https://github.com/Koenkk/zigbee2mqtt/issues/19878)) ([b17487e](https://github.com/Koenkk/zigbee2mqtt/commit/b17487e6bb8d04bca0aaa5b0ad2338dc9eef46aa))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.127.0 ([#19894](https://github.com/Koenkk/zigbee2mqtt/issues/19894)) ([8a6d90d](https://github.com/Koenkk/zigbee2mqtt/commit/8a6d90d70e9ebcb2eeeacf385f4c3d47adeebc88))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.128.0 ([#19906](https://github.com/Koenkk/zigbee2mqtt/issues/19906)) ([4e128e2](https://github.com/Koenkk/zigbee2mqtt/commit/4e128e26fee314f2bdcce014132f85d5167ce0bf))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.129.0 ([#19934](https://github.com/Koenkk/zigbee2mqtt/issues/19934)) ([12521ad](https://github.com/Koenkk/zigbee2mqtt/commit/12521add3968cc9ed5394cf128bf2917f75597a5))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.130.0 ([#19942](https://github.com/Koenkk/zigbee2mqtt/issues/19942)) ([f976d4e](https://github.com/Koenkk/zigbee2mqtt/commit/f976d4efb48440ee7899490d31f14d820d71087d))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.130.1 ([#19945](https://github.com/Koenkk/zigbee2mqtt/issues/19945)) ([3bbf89c](https://github.com/Koenkk/zigbee2mqtt/commit/3bbf89cba038c6bdfab08c3e2860e561b2845a43))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.143 ([#19642](https://github.com/Koenkk/zigbee2mqtt/issues/19642)) ([9d5d617](https://github.com/Koenkk/zigbee2mqtt/commit/9d5d61754d2808c83eee48dfc6a0e0c4c740002a))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.144 ([#19776](https://github.com/Koenkk/zigbee2mqtt/issues/19776)) ([91aed7e](https://github.com/Koenkk/zigbee2mqtt/commit/91aed7e98e65d1c1c1469e3bfaab74233f0002bf))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.145 ([#19778](https://github.com/Koenkk/zigbee2mqtt/issues/19778)) ([ed20e2a](https://github.com/Koenkk/zigbee2mqtt/commit/ed20e2a1bc0a474157880c2c54f7c2d656658901))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.146 ([#19784](https://github.com/Koenkk/zigbee2mqtt/issues/19784)) ([c87ac9c](https://github.com/Koenkk/zigbee2mqtt/commit/c87ac9cc2b1d360ff0962deed973545a9846fcd6))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.147 ([#19801](https://github.com/Koenkk/zigbee2mqtt/issues/19801)) ([61b08c9](https://github.com/Koenkk/zigbee2mqtt/commit/61b08c94bfd3dd58efdb22632476f410fac56d13))
|
||||
* Make frontend listen on both IPv4 and IPv6 by default ([#19660](https://github.com/Koenkk/zigbee2mqtt/issues/19660)) ([45ac44d](https://github.com/Koenkk/zigbee2mqtt/commit/45ac44dd064a3f8aca87a25e7a0b2daafe53c5d3))
|
||||
* Map climate `idle` to `idle` instead of `off` in Home Assistant ([#19545](https://github.com/Koenkk/zigbee2mqtt/issues/19545)) ([64b26e4](https://github.com/Koenkk/zigbee2mqtt/commit/64b26e47da3d765bfde8ccac8f9a1e304b28c697))
|
||||
* Set entity category `config` for Home Assistant `temperature_display_mode` sensor ([#19631](https://github.com/Koenkk/zigbee2mqtt/issues/19631)) ([86ae0ed](https://github.com/Koenkk/zigbee2mqtt/commit/86ae0ed90fb1d94f5cb09545355968affa5654ca))
|
||||
* **typo:** correct misspell ([#19581](https://github.com/Koenkk/zigbee2mqtt/issues/19581)) ([13443a4](https://github.com/Koenkk/zigbee2mqtt/commit/13443a42ce7239a7308efb829c8ff007e59ca45d))
|
||||
|
||||
## [1.33.2](https://github.com/Koenkk/zigbee2mqtt/compare/1.33.1...1.33.2) (2023-11-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add device class for soil_moisture sensor in HA ([#19486](https://github.com/Koenkk/zigbee2mqtt/issues/19486)) ([98c7a2a](https://github.com/Koenkk/zigbee2mqtt/commit/98c7a2ab56ccf6db227a8ff9f8dd333e7f61eaf9))
|
||||
* Add in_progress attribute for Home Assistant update sensor ([#19207](https://github.com/Koenkk/zigbee2mqtt/issues/19207)) ([cdce6b1](https://github.com/Koenkk/zigbee2mqtt/commit/cdce6b11d268ab3de9a7aa3a16020268efdfe7cf))
|
||||
* Adjust default entity category of updates in Home Assistant ([#19435](https://github.com/Koenkk/zigbee2mqtt/issues/19435)) ([d0a4f5f](https://github.com/Koenkk/zigbee2mqtt/commit/d0a4f5f628de7c1ba8f004ee3fd8587b6391fd5c))
|
||||
* Bump semver version ([#19179](https://github.com/Koenkk/zigbee2mqtt/issues/19179)) ([32c1bdf](https://github.com/Koenkk/zigbee2mqtt/commit/32c1bdfccbcef21e0405dd0529dfc101efca9811))
|
||||
* Enable Home Assistant force entity by default ([#19212](https://github.com/Koenkk/zigbee2mqtt/issues/19212)) ([95d22c0](https://github.com/Koenkk/zigbee2mqtt/commit/95d22c0822a3b3d4b17c10aa424e26e488f70556))
|
||||
* Enable incremental TSC builds ([#19293](https://github.com/Koenkk/zigbee2mqtt/issues/19293)) ([cc86e76](https://github.com/Koenkk/zigbee2mqtt/commit/cc86e76a7ad21fc13a9df4b3c859c512edc5d762))
|
||||
* Fix device keeps requesting OTA due to incorrect transaction sequence number. https://github.com/Koenkk/zigbee2mqtt/issues/19129 ([daaade4](https://github.com/Koenkk/zigbee2mqtt/commit/daaade45d919d982461e49cfa4cefd195e70cd7d))
|
||||
* Fix entity category of Home Assistant exposed enum sensors ([#19474](https://github.com/Koenkk/zigbee2mqtt/issues/19474)) ([69f6891](https://github.com/Koenkk/zigbee2mqtt/commit/69f6891db58e432c7b397d8f98f401aeccd5b2f7))
|
||||
* Fix entity category of Home Assistant exposed sensor entities ([#19434](https://github.com/Koenkk/zigbee2mqtt/issues/19434)) ([e7749e2](https://github.com/Koenkk/zigbee2mqtt/commit/e7749e25e9056c8c4fd6144addba3567ee4b9fb9))
|
||||
* **ignore:** update dependencies ([#19208](https://github.com/Koenkk/zigbee2mqtt/issues/19208)) ([9615faa](https://github.com/Koenkk/zigbee2mqtt/commit/9615faac2f197007562a9a55767644ff61a5a055))
|
||||
* **ignore:** update dependencies ([#19301](https://github.com/Koenkk/zigbee2mqtt/issues/19301)) ([31541bd](https://github.com/Koenkk/zigbee2mqtt/commit/31541bd6bae09537acb87dfa3f223743d2effce8))
|
||||
* **ignore:** update dependencies ([#19375](https://github.com/Koenkk/zigbee2mqtt/issues/19375)) ([6d387b3](https://github.com/Koenkk/zigbee2mqtt/commit/6d387b31c1abd0360b947695a3f3adff71c24b7f))
|
||||
* **ignore:** update dependencies ([#19466](https://github.com/Koenkk/zigbee2mqtt/issues/19466)) ([0b37769](https://github.com/Koenkk/zigbee2mqtt/commit/0b3776916b1ff9241602dbae8072abfcbd66d18b))
|
||||
* **ignore:** update zigbee-herdsman to 0.19.1 ([#19194](https://github.com/Koenkk/zigbee2mqtt/issues/19194)) ([fcb0af0](https://github.com/Koenkk/zigbee2mqtt/commit/fcb0af00d3044d51901051e5bc29376b400b63eb))
|
||||
* **ignore:** update zigbee-herdsman to 0.19.2 ([#19277](https://github.com/Koenkk/zigbee2mqtt/issues/19277)) ([a0c13c1](https://github.com/Koenkk/zigbee2mqtt/commit/a0c13c1c1ea2c9dc04e797a3a7031a753b6e8c21))
|
||||
* **ignore:** update zigbee-herdsman to 0.20.0 ([#19313](https://github.com/Koenkk/zigbee2mqtt/issues/19313)) ([43a5c7b](https://github.com/Koenkk/zigbee2mqtt/commit/43a5c7b2f0940bd95980ea5cfeb39f3165537c5d))
|
||||
* **ignore:** update zigbee-herdsman to 0.21.0 ([#19421](https://github.com/Koenkk/zigbee2mqtt/issues/19421)) ([f73cca0](https://github.com/Koenkk/zigbee2mqtt/commit/f73cca0e396399640e39bbd8d77ebae5b0a12105))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.100.0 ([#19376](https://github.com/Koenkk/zigbee2mqtt/issues/19376)) ([71dd0ab](https://github.com/Koenkk/zigbee2mqtt/commit/71dd0ab772185427eed687845c8ecfacb4e66072))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.101.0 ([#19397](https://github.com/Koenkk/zigbee2mqtt/issues/19397)) ([52a545d](https://github.com/Koenkk/zigbee2mqtt/commit/52a545d86e441619fadbb942bab40c9ae9135fe0))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.102.0 ([#19422](https://github.com/Koenkk/zigbee2mqtt/issues/19422)) ([1fa10f6](https://github.com/Koenkk/zigbee2mqtt/commit/1fa10f6f289234d6b2e90d35b01425075958ea9e))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.103.0 ([#19443](https://github.com/Koenkk/zigbee2mqtt/issues/19443)) ([7e82a49](https://github.com/Koenkk/zigbee2mqtt/commit/7e82a491f6d7294340cd01baaa74f66acd1e88e4))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.104.0 ([#19479](https://github.com/Koenkk/zigbee2mqtt/issues/19479)) ([e2fccc2](https://github.com/Koenkk/zigbee2mqtt/commit/e2fccc219b63943b7cd5939e53733c63f6ba181c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.105.0 ([#19492](https://github.com/Koenkk/zigbee2mqtt/issues/19492)) ([fe25924](https://github.com/Koenkk/zigbee2mqtt/commit/fe25924780c454751bada81a783151e5ee5746a8))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.105.1 ([#19503](https://github.com/Koenkk/zigbee2mqtt/issues/19503)) ([d79235e](https://github.com/Koenkk/zigbee2mqtt/commit/d79235e27bf85046d269246678a0859985efd9f8))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.106.0 ([#19511](https://github.com/Koenkk/zigbee2mqtt/issues/19511)) ([3538690](https://github.com/Koenkk/zigbee2mqtt/commit/35386903f2a429c241fa996c29bbbd571c7b046d))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.87.0 ([#19156](https://github.com/Koenkk/zigbee2mqtt/issues/19156)) ([de0f060](https://github.com/Koenkk/zigbee2mqtt/commit/de0f060378738a6a32bd9a7b9780fc3ede56e331))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.88.0 ([#19185](https://github.com/Koenkk/zigbee2mqtt/issues/19185)) ([0b60cd1](https://github.com/Koenkk/zigbee2mqtt/commit/0b60cd1100c264fb58cb54fe769aeed2b630f72c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.89.0 ([#19190](https://github.com/Koenkk/zigbee2mqtt/issues/19190)) ([b085fac](https://github.com/Koenkk/zigbee2mqtt/commit/b085fac50f9d3969192cce9c93cc7330359aca70))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.89.1 ([#19195](https://github.com/Koenkk/zigbee2mqtt/issues/19195)) ([de7736a](https://github.com/Koenkk/zigbee2mqtt/commit/de7736a370cf6c3d62b0d51dea5ea63824edbe8d))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.90.0 ([#19209](https://github.com/Koenkk/zigbee2mqtt/issues/19209)) ([2aae1bd](https://github.com/Koenkk/zigbee2mqtt/commit/2aae1bd54a94e64a52a9f373c3cecc7667198d00))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.92.1 ([#19234](https://github.com/Koenkk/zigbee2mqtt/issues/19234)) ([d027b85](https://github.com/Koenkk/zigbee2mqtt/commit/d027b8520068af471e8bf5cc29ef5a2a5eb6552c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.93.0 ([#19278](https://github.com/Koenkk/zigbee2mqtt/issues/19278)) ([afb83fc](https://github.com/Koenkk/zigbee2mqtt/commit/afb83fcbd0ec60ce02d2ce1e677243258bbf2b4d))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.94.0 ([#19290](https://github.com/Koenkk/zigbee2mqtt/issues/19290)) ([29247e1](https://github.com/Koenkk/zigbee2mqtt/commit/29247e19ac8a0e67d193b12fde71743c02afc86c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.94.1 ([#19295](https://github.com/Koenkk/zigbee2mqtt/issues/19295)) ([70b0fed](https://github.com/Koenkk/zigbee2mqtt/commit/70b0fed2dedeb76609e9afc0ef0ac2b34a3032b5))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.94.2 ([#19304](https://github.com/Koenkk/zigbee2mqtt/issues/19304)) ([81a8df9](https://github.com/Koenkk/zigbee2mqtt/commit/81a8df9855e4069d57ab6ba990c0ef8ac79e66d6))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.95.0 ([#19314](https://github.com/Koenkk/zigbee2mqtt/issues/19314)) ([3461f11](https://github.com/Koenkk/zigbee2mqtt/commit/3461f118690646f643cab53f02d50a1392a9ead3))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.96.0 ([#19328](https://github.com/Koenkk/zigbee2mqtt/issues/19328)) ([5d1bd25](https://github.com/Koenkk/zigbee2mqtt/commit/5d1bd256f9c58a47d7b8120e3d625b770f5516ef))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.97.0 ([#19337](https://github.com/Koenkk/zigbee2mqtt/issues/19337)) ([a86f3d8](https://github.com/Koenkk/zigbee2mqtt/commit/a86f3d8765f31df18451bcd1c34ad031d476dae4))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.98.0 ([#19358](https://github.com/Koenkk/zigbee2mqtt/issues/19358)) ([8a28154](https://github.com/Koenkk/zigbee2mqtt/commit/8a2815466193f94bc9d462afe6f9386a29f21200))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.99.0 ([#19366](https://github.com/Koenkk/zigbee2mqtt/issues/19366)) ([0cd9948](https://github.com/Koenkk/zigbee2mqtt/commit/0cd99487ccb3f401b02fa580b24a9209021e0216))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.137 ([#19165](https://github.com/Koenkk/zigbee2mqtt/issues/19165)) ([b261f2c](https://github.com/Koenkk/zigbee2mqtt/commit/b261f2c6c1f2ec0ea0e0f1b443d30acb62a740d3))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.138 ([#19365](https://github.com/Koenkk/zigbee2mqtt/issues/19365)) ([17ad362](https://github.com/Koenkk/zigbee2mqtt/commit/17ad362fa7006574270649faf50c88e949253830))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.139 ([#19378](https://github.com/Koenkk/zigbee2mqtt/issues/19378)) ([08a5f32](https://github.com/Koenkk/zigbee2mqtt/commit/08a5f32ea9951fccf3b5a6ee3c56372a200906b2))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.140 ([#19413](https://github.com/Koenkk/zigbee2mqtt/issues/19413)) ([0e55b38](https://github.com/Koenkk/zigbee2mqtt/commit/0e55b382ba32f1157375d35bdc2200061122cdb2))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.141 ([#19446](https://github.com/Koenkk/zigbee2mqtt/issues/19446)) ([2541fea](https://github.com/Koenkk/zigbee2mqtt/commit/2541feac25f8cde9a6417935b31848ac1b04d78f))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.142 ([#19447](https://github.com/Koenkk/zigbee2mqtt/issues/19447)) ([6c7be58](https://github.com/Koenkk/zigbee2mqtt/commit/6c7be58a3f77efb5341309e801a9a2279a7a4e35))
|
||||
* Publish zigbee-herdsman/zigbee-herdsman-converters version to `bridge/info` ([#19364](https://github.com/Koenkk/zigbee2mqtt/issues/19364)) ([e36983b](https://github.com/Koenkk/zigbee2mqtt/commit/e36983b4b5d50af8586f8f0bb226d1be59b34c9b))
|
||||
|
||||
## [1.33.1](https://github.com/Koenkk/zigbee2mqtt/compare/1.33.0...1.33.1) (2023-10-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Correct typos ([#19143](https://github.com/Koenkk/zigbee2mqtt/issues/19143)) ([83e3313](https://github.com/Koenkk/zigbee2mqtt/commit/83e331323c19c70f25617fa6baf75e933cd9baf7))
|
||||
* Fix `configuration.example.yaml` missing from container ([#18836](https://github.com/Koenkk/zigbee2mqtt/issues/18836)) ([0543ad4](https://github.com/Koenkk/zigbee2mqtt/commit/0543ad4b19f2a8e79a12f2b84b94d005acb60396))
|
||||
* **ignore:** update dependencies ([#18995](https://github.com/Koenkk/zigbee2mqtt/issues/18995)) ([5477163](https://github.com/Koenkk/zigbee2mqtt/commit/5477163727d0ed3399614fbb5c12201b2c098127))
|
||||
* **ignore:** update dependencies ([#19061](https://github.com/Koenkk/zigbee2mqtt/issues/19061)) ([d99da63](https://github.com/Koenkk/zigbee2mqtt/commit/d99da63b8968c1cb8ae9788317e493f4343dd1a1))
|
||||
* **ignore:** update dependencies ([#19127](https://github.com/Koenkk/zigbee2mqtt/issues/19127)) ([1c7bb8f](https://github.com/Koenkk/zigbee2mqtt/commit/1c7bb8fc241d7c3232d2088d6a603318d9cf6c01))
|
||||
* **ignore:** update zigbee-herdsman to 0.18.6 ([#18838](https://github.com/Koenkk/zigbee2mqtt/issues/18838)) ([6654c7f](https://github.com/Koenkk/zigbee2mqtt/commit/6654c7f1a34694590fc575270f80faca32472cca))
|
||||
* **ignore:** update zigbee-herdsman to 0.18.7 ([#18865](https://github.com/Koenkk/zigbee2mqtt/issues/18865)) ([914b4c6](https://github.com/Koenkk/zigbee2mqtt/commit/914b4c67575b77534af98e9ae76749e331e81ffa))
|
||||
* **ignore:** update zigbee-herdsman to 0.19.0 ([#18998](https://github.com/Koenkk/zigbee2mqtt/issues/18998)) ([84f9950](https://github.com/Koenkk/zigbee2mqtt/commit/84f99505561501690f8cf15cd9c98bda580d13df))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.69.0 ([#18835](https://github.com/Koenkk/zigbee2mqtt/issues/18835)) ([3285850](https://github.com/Koenkk/zigbee2mqtt/commit/3285850efa2080c9bfc71373387d4cf5296bbc5b))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.70.0 ([#18866](https://github.com/Koenkk/zigbee2mqtt/issues/18866)) ([91a9e32](https://github.com/Koenkk/zigbee2mqtt/commit/91a9e32a8369fc5c9b919670e4d7564ace3b9371))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.71.0 ([#18887](https://github.com/Koenkk/zigbee2mqtt/issues/18887)) ([f509410](https://github.com/Koenkk/zigbee2mqtt/commit/f5094101898d33fc691311a45ec32f6ac85c18cb))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.72.0 ([#18901](https://github.com/Koenkk/zigbee2mqtt/issues/18901)) ([ea8de2b](https://github.com/Koenkk/zigbee2mqtt/commit/ea8de2ba1e60abeda455092ee79219ea2d11adcc))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.73.0 ([#18917](https://github.com/Koenkk/zigbee2mqtt/issues/18917)) ([d6ed0f4](https://github.com/Koenkk/zigbee2mqtt/commit/d6ed0f45da8a4e2aa0629b89f1a3361f495245e7))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.74.0 ([#18949](https://github.com/Koenkk/zigbee2mqtt/issues/18949)) ([6de7662](https://github.com/Koenkk/zigbee2mqtt/commit/6de7662ca80667cbba669ea3a6ccc7bd3e83362b))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.75.0 ([#18972](https://github.com/Koenkk/zigbee2mqtt/issues/18972)) ([cc95c85](https://github.com/Koenkk/zigbee2mqtt/commit/cc95c859692b4316f3313ad49f99ad7f8e2b29d3))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.76.0 ([#18978](https://github.com/Koenkk/zigbee2mqtt/issues/18978)) ([4040840](https://github.com/Koenkk/zigbee2mqtt/commit/4040840d8e9dc222324be917a281cf29c3bf0da6))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.77.0 ([#18984](https://github.com/Koenkk/zigbee2mqtt/issues/18984)) ([0a69bb5](https://github.com/Koenkk/zigbee2mqtt/commit/0a69bb54c8eb7b4bc459a72d15b408e0c6f3d3de))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.78.0 ([#18999](https://github.com/Koenkk/zigbee2mqtt/issues/18999)) ([eac9240](https://github.com/Koenkk/zigbee2mqtt/commit/eac9240e0600e91e9fcd71f45e861cb4d90acdef))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.80.0 ([#19023](https://github.com/Koenkk/zigbee2mqtt/issues/19023)) ([af209b6](https://github.com/Koenkk/zigbee2mqtt/commit/af209b6f53ebca7032169c689617da169c7b56d4))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.81.1 ([#19041](https://github.com/Koenkk/zigbee2mqtt/issues/19041)) ([9a7aeaf](https://github.com/Koenkk/zigbee2mqtt/commit/9a7aeaf62b1d17fe3935ac731029e93ccd7b32d5))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.82.0 ([#19067](https://github.com/Koenkk/zigbee2mqtt/issues/19067)) ([1190a34](https://github.com/Koenkk/zigbee2mqtt/commit/1190a342e90194184e76de80fd4264f87a65de94))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.83.0 ([#19088](https://github.com/Koenkk/zigbee2mqtt/issues/19088)) ([a7bb3fc](https://github.com/Koenkk/zigbee2mqtt/commit/a7bb3fc9e882d0ffabaea61357db41818bdbbaa3))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.84.0 ([#19096](https://github.com/Koenkk/zigbee2mqtt/issues/19096)) ([7ccb4bc](https://github.com/Koenkk/zigbee2mqtt/commit/7ccb4bcafe9837a59285e8cebac4dfda1095ca62))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.85.0 ([#19102](https://github.com/Koenkk/zigbee2mqtt/issues/19102)) ([90763fd](https://github.com/Koenkk/zigbee2mqtt/commit/90763fd83f2904be174ab86f74623de1029babca))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.86.0 ([#19149](https://github.com/Koenkk/zigbee2mqtt/issues/19149)) ([45ce494](https://github.com/Koenkk/zigbee2mqtt/commit/45ce4949518c7755fba2ac0ded36c54c254c66e1))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.86.1 ([#19150](https://github.com/Koenkk/zigbee2mqtt/issues/19150)) ([b990ed9](https://github.com/Koenkk/zigbee2mqtt/commit/b990ed9a60351c5d2782c34b38234cdcddff0f3a))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.136 ([#19040](https://github.com/Koenkk/zigbee2mqtt/issues/19040)) ([9b66ab1](https://github.com/Koenkk/zigbee2mqtt/commit/9b66ab1367638c7291c8b4a997c215354915a642))
|
||||
|
||||
## [1.33.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.32.2...1.33.0) (2023-09-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add Home Assistant origin to discovery ([#18741](https://github.com/Koenkk/zigbee2mqtt/issues/18741)) ([704ecf3](https://github.com/Koenkk/zigbee2mqtt/commit/704ecf366e51ff777e96379acbb89d9c3c416d6d))
|
||||
* Drop Node.js 14 support ([#18555](https://github.com/Koenkk/zigbee2mqtt/issues/18555)) ([1f52fae](https://github.com/Koenkk/zigbee2mqtt/commit/1f52faee398e720ad64e61b559e04be81b58b979))
|
||||
* Let Home Assistant generate entity name when device_class is present ([#18208](https://github.com/Koenkk/zigbee2mqtt/issues/18208)) ([df93e4c](https://github.com/Koenkk/zigbee2mqtt/commit/df93e4c8f9490e503e5896e082fc2578f98c2e0e))
|
||||
* Support coordinator check ([#18599](https://github.com/Koenkk/zigbee2mqtt/issues/18599)) ([3af130a](https://github.com/Koenkk/zigbee2mqtt/commit/3af130a2c056aab74d793a963aceead348f56058))
|
||||
* Support generating extended_pan_id ([#18596](https://github.com/Koenkk/zigbee2mqtt/issues/18596)) ([aa021d9](https://github.com/Koenkk/zigbee2mqtt/commit/aa021d988db31ef206a9bfa6b5312d631bb3de79))
|
||||
* Support scene renaming ([#18667](https://github.com/Koenkk/zigbee2mqtt/issues/18667)) ([4b3a2c9](https://github.com/Koenkk/zigbee2mqtt/commit/4b3a2c9f5a74ec25fd82d219a7fc31cddb7c7def))
|
||||
* Use labels in Home Assistant entity naming ([#18678](https://github.com/Koenkk/zigbee2mqtt/issues/18678)) ([e33e335](https://github.com/Koenkk/zigbee2mqtt/commit/e33e335c0ca92670eee4be672ec53282cc2b79d3))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add secure default config ([#18357](https://github.com/Koenkk/zigbee2mqtt/issues/18357)) ([68ec507](https://github.com/Koenkk/zigbee2mqtt/commit/68ec507e30bead5e9263c0fdc4c47e18c257d82f))
|
||||
* Fix Home Assistant `MQTT device name is equal to entity name in your config ([#18491](https://github.com/Koenkk/zigbee2mqtt/issues/18491)) ([f619610](https://github.com/Koenkk/zigbee2mqtt/commit/f6196107a75f9257366505a3ad5ef7b29ff5bc1c))
|
||||
* Fix Home Assistant `TypeError: object of type 'NoneType' has no len()` error. https://github.com/Koenkk/zigbee2mqtt/issues/17861 ([ed22ece](https://github.com/Koenkk/zigbee2mqtt/commit/ed22ecedbfd51fc2f5572930aacd37e5b48e04cf))
|
||||
* Generate pan_id for new installations ([#18530](https://github.com/Koenkk/zigbee2mqtt/issues/18530)) ([cf313c5](https://github.com/Koenkk/zigbee2mqtt/commit/cf313c503b7b070e24739a1f5139e5833405eebc))
|
||||
* **ignore:** chore: Update dependencies ([#18459](https://github.com/Koenkk/zigbee2mqtt/issues/18459)) ([b55cc0a](https://github.com/Koenkk/zigbee2mqtt/commit/b55cc0a037ccb2c16ac2147ae7e1fb2e4d596806))
|
||||
* **ignore:** Revert "chore: Update dependencies ([#18459](https://github.com/Koenkk/zigbee2mqtt/issues/18459))" ([b09b8ec](https://github.com/Koenkk/zigbee2mqtt/commit/b09b8ec3d6b30b0bc34c36819e39298ec5a94c63))
|
||||
* **ignore:** update dependencies ([#18598](https://github.com/Koenkk/zigbee2mqtt/issues/18598)) ([0d00a14](https://github.com/Koenkk/zigbee2mqtt/commit/0d00a144f5c40bf1fce30f9ee65335a6d0076972))
|
||||
* **ignore:** update dependencies ([#18755](https://github.com/Koenkk/zigbee2mqtt/issues/18755)) ([bc17e64](https://github.com/Koenkk/zigbee2mqtt/commit/bc17e6406bdd4104ba7f1b4c7663a18f82fee35b))
|
||||
* **ignore:** update zigbee-herdsman to 0.17.3 ([#18556](https://github.com/Koenkk/zigbee2mqtt/issues/18556)) ([a892a1b](https://github.com/Koenkk/zigbee2mqtt/commit/a892a1b898f8cf4fc02c1ec7d9159d426c5ea547))
|
||||
* **ignore:** update zigbee-herdsman to 0.18.0 ([#18600](https://github.com/Koenkk/zigbee2mqtt/issues/18600)) ([cc0536c](https://github.com/Koenkk/zigbee2mqtt/commit/cc0536c5ab1d5f04b75bf9cd21c4f9579c60dc31))
|
||||
* **ignore:** update zigbee-herdsman to 0.18.1 ([#18627](https://github.com/Koenkk/zigbee2mqtt/issues/18627)) ([3736b59](https://github.com/Koenkk/zigbee2mqtt/commit/3736b5901b9f497b74a2efa57adb105802ddc153))
|
||||
* **ignore:** update zigbee-herdsman to 0.18.2 ([#18648](https://github.com/Koenkk/zigbee2mqtt/issues/18648)) ([db812cd](https://github.com/Koenkk/zigbee2mqtt/commit/db812cd72918eb0438129639c9d7a8af86f8fc37))
|
||||
* **ignore:** update zigbee-herdsman to 0.18.3 ([#18714](https://github.com/Koenkk/zigbee2mqtt/issues/18714)) ([6a1b6a1](https://github.com/Koenkk/zigbee2mqtt/commit/6a1b6a14038290fc9d13008b09da002fd55f540f))
|
||||
* **ignore:** update zigbee-herdsman to 0.18.4 ([#18727](https://github.com/Koenkk/zigbee2mqtt/issues/18727)) ([1439f4c](https://github.com/Koenkk/zigbee2mqtt/commit/1439f4c6ace962f8b14ec43a8b418ca12a4db27f))
|
||||
* **ignore:** update zigbee-herdsman to 0.18.5 ([#18816](https://github.com/Koenkk/zigbee2mqtt/issues/18816)) ([68169f0](https://github.com/Koenkk/zigbee2mqtt/commit/68169f0e50290c8c2fa5fba052b4446c9a46d2f1))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.51.0 ([#18501](https://github.com/Koenkk/zigbee2mqtt/issues/18501)) ([2d917a7](https://github.com/Koenkk/zigbee2mqtt/commit/2d917a7d0db1fc4d89ba163d63e2d2932d0655fe))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.52.0 ([#18514](https://github.com/Koenkk/zigbee2mqtt/issues/18514)) ([0718ca1](https://github.com/Koenkk/zigbee2mqtt/commit/0718ca162dcf6ab41f192e4dd8ecc83b38e25410))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.53.0 ([#18524](https://github.com/Koenkk/zigbee2mqtt/issues/18524)) ([f08cd81](https://github.com/Koenkk/zigbee2mqtt/commit/f08cd81ac0ad4436883acf39947e7254002ef9fe))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.54.0 ([#18527](https://github.com/Koenkk/zigbee2mqtt/issues/18527)) ([abba8b3](https://github.com/Koenkk/zigbee2mqtt/commit/abba8b3ba8c31c2b9057f74e455824045082a4ec))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.54.1 ([#18537](https://github.com/Koenkk/zigbee2mqtt/issues/18537)) ([b5e9ed4](https://github.com/Koenkk/zigbee2mqtt/commit/b5e9ed436cd3a8e5b8183fe44685d2f41422b2fd))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.55.0 ([#18547](https://github.com/Koenkk/zigbee2mqtt/issues/18547)) ([6050428](https://github.com/Koenkk/zigbee2mqtt/commit/60504285663e346c2078819c6445905fb2aaff1c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.55.1 ([#18557](https://github.com/Koenkk/zigbee2mqtt/issues/18557)) ([7f05f31](https://github.com/Koenkk/zigbee2mqtt/commit/7f05f31c72cfd394cbe0d8e20de5c9f5d245db85))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.55.2 ([#18566](https://github.com/Koenkk/zigbee2mqtt/issues/18566)) ([8675b1c](https://github.com/Koenkk/zigbee2mqtt/commit/8675b1c9ed405d7abe42f6725c0627968c533f4b))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.55.3 ([#18578](https://github.com/Koenkk/zigbee2mqtt/issues/18578)) ([b1796e3](https://github.com/Koenkk/zigbee2mqtt/commit/b1796e3e1a1d58c79dda39a8041ce67ceea133a4))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.57.0 ([#18616](https://github.com/Koenkk/zigbee2mqtt/issues/18616)) ([145bc05](https://github.com/Koenkk/zigbee2mqtt/commit/145bc05dc30d952e919035a7b741f91ee98a8997))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.58.1 ([#18658](https://github.com/Koenkk/zigbee2mqtt/issues/18658)) ([dcbf4e5](https://github.com/Koenkk/zigbee2mqtt/commit/dcbf4e5b0e75d0e50688434079588fe179406520))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.59.0 ([#18661](https://github.com/Koenkk/zigbee2mqtt/issues/18661)) ([7185d9e](https://github.com/Koenkk/zigbee2mqtt/commit/7185d9e5013888f8276e74849c7463560c46f369))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.60.0 ([#18672](https://github.com/Koenkk/zigbee2mqtt/issues/18672)) ([71c4211](https://github.com/Koenkk/zigbee2mqtt/commit/71c4211d13df29aefb217a030c5e114c7a666112))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.62.0 ([#18742](https://github.com/Koenkk/zigbee2mqtt/issues/18742)) ([514d9f3](https://github.com/Koenkk/zigbee2mqtt/commit/514d9f38b2a7a46fe766dd422f540350bbbe0f5e))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.63.0 ([#18758](https://github.com/Koenkk/zigbee2mqtt/issues/18758)) ([b193d89](https://github.com/Koenkk/zigbee2mqtt/commit/b193d8904299a0c5673da3b9185200ef6a3ed368))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.64.0 ([#18784](https://github.com/Koenkk/zigbee2mqtt/issues/18784)) ([5084b12](https://github.com/Koenkk/zigbee2mqtt/commit/5084b1293e2ea857b363837eabf7ca0087f2607c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.66.1 ([#18790](https://github.com/Koenkk/zigbee2mqtt/issues/18790)) ([0ec68a4](https://github.com/Koenkk/zigbee2mqtt/commit/0ec68a4b71208fe42ea011e0084c8af0b187a261))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.67.0 ([#18815](https://github.com/Koenkk/zigbee2mqtt/issues/18815)) ([97f6a84](https://github.com/Koenkk/zigbee2mqtt/commit/97f6a840e47612e98f20ba355dae7fb3dfd0868c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.67.1 ([#18817](https://github.com/Koenkk/zigbee2mqtt/issues/18817)) ([e78aa35](https://github.com/Koenkk/zigbee2mqtt/commit/e78aa3589b4c468716917f9f734f03801f86c3cd))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.134 ([#18761](https://github.com/Koenkk/zigbee2mqtt/issues/18761)) ([4949a54](https://github.com/Koenkk/zigbee2mqtt/commit/4949a54d664083f6152bafa78c5468f9a2c50f2b))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.135 ([#18814](https://github.com/Koenkk/zigbee2mqtt/issues/18814)) ([9a6445a](https://github.com/Koenkk/zigbee2mqtt/commit/9a6445a8a024c8b415caa9cd08e48df12a46e83b))
|
||||
* Use QOS1 for rarely sent discovery and availability messages ([#18756](https://github.com/Koenkk/zigbee2mqtt/issues/18756)) ([d1e50ce](https://github.com/Koenkk/zigbee2mqtt/commit/d1e50ce534b906f426b28f2aaf8b0a51a97de264))
|
||||
|
||||
## [1.32.2](https://github.com/Koenkk/zigbee2mqtt/compare/1.32.1...1.32.2) (2023-08-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix `'dict object' has no attribute 'ir_code_to_send'` error. https://github.com/Koenkk/zigbee2mqtt/issues/18180 ([dec9191](https://github.com/Koenkk/zigbee2mqtt/commit/dec91919bc50aed1f1d3fd9525abd2db49242e7f))
|
||||
* Fix insecure Zigbee network encryption key generation ([#18372](https://github.com/Koenkk/zigbee2mqtt/issues/18372)) ([df8e168](https://github.com/Koenkk/zigbee2mqtt/commit/df8e1687b2db3f49ba8c3d97a4c92b7892a54f1e))
|
||||
* Fix not all cluster unbound/bound when specified. https://github.com/Koenkk/zigbee2mqtt/issues/10740 ([0bdf663](https://github.com/Koenkk/zigbee2mqtt/commit/0bdf663ca126d0569b62871e1b787992472faea4))
|
||||
* Fix socket error crashing Zigbee2MQTT ([#18388](https://github.com/Koenkk/zigbee2mqtt/issues/18388)) ([f64cd7e](https://github.com/Koenkk/zigbee2mqtt/commit/f64cd7e8f38f0c1fc1bbd85b5b5caf560565c024))
|
||||
* **ignore:** Add ep `l` till 24 ([#18422](https://github.com/Koenkk/zigbee2mqtt/issues/18422)) ([4bd281c](https://github.com/Koenkk/zigbee2mqtt/commit/4bd281ce6cbd43f38b2a3131cd38b25885a22b3a))
|
||||
* **ignore:** Add th1-th10 endpoints for Ubisys H10 ([#18405](https://github.com/Koenkk/zigbee2mqtt/issues/18405)) ([91f22f1](https://github.com/Koenkk/zigbee2mqtt/commit/91f22f1230241f5fb3b27b2ee84d944a1c3c5107))
|
||||
* **ignore:** Attempt 2 to fix cache saving ([ea412cf](https://github.com/Koenkk/zigbee2mqtt/commit/ea412cfe82a0462930ec8dddf00b9a363c484b73))
|
||||
* **ignore:** Attempt 3 to fix commit-user-lookup ([590564c](https://github.com/Koenkk/zigbee2mqtt/commit/590564c15888f73826f20eca91e2c253c7d000e7))
|
||||
* **ignore:** Attempt to fix cache not saving ([fa02bb5](https://github.com/Koenkk/zigbee2mqtt/commit/fa02bb5b74216db78b23db144c915d00c7dc48c1))
|
||||
* **ignore:** fix commit-user-lookup cache ([758b814](https://github.com/Koenkk/zigbee2mqtt/commit/758b8144a19ca04eac5faae5b7e16c090e9fda77))
|
||||
* **ignore:** Fix failing test ([ba0cc61](https://github.com/Koenkk/zigbee2mqtt/commit/ba0cc6196550b3b6873f3733690daef9002c5863))
|
||||
* **ignore:** update dependencies ([#18260](https://github.com/Koenkk/zigbee2mqtt/issues/18260)) ([ee1a5f0](https://github.com/Koenkk/zigbee2mqtt/commit/ee1a5f04008beaac54fb68e20c31c1664563a4f2))
|
||||
* **ignore:** update dependencies ([#18403](https://github.com/Koenkk/zigbee2mqtt/issues/18403)) ([8e94662](https://github.com/Koenkk/zigbee2mqtt/commit/8e946623a9db6282a53b55d5d5f3e4b000d080ee))
|
||||
* **ignore:** update zigbee-herdsman to 0.17.2 ([#18291](https://github.com/Koenkk/zigbee2mqtt/issues/18291)) ([073989c](https://github.com/Koenkk/zigbee2mqtt/commit/073989c7a434c3fd73abaee95fa4ada9394d695d))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.38.0 ([#18262](https://github.com/Koenkk/zigbee2mqtt/issues/18262)) ([813dd49](https://github.com/Koenkk/zigbee2mqtt/commit/813dd49a41e7d3f41b6c79db708bb08d29cd7f0c))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.39.0 ([#18282](https://github.com/Koenkk/zigbee2mqtt/issues/18282)) ([3c855d2](https://github.com/Koenkk/zigbee2mqtt/commit/3c855d20424761d43ace9b5ed55460eedce092f0))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.39.1 ([#18292](https://github.com/Koenkk/zigbee2mqtt/issues/18292)) ([a3c8e1c](https://github.com/Koenkk/zigbee2mqtt/commit/a3c8e1c3c98bd97ef52c29929dc9bb5f9598d0fd))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.40.0 ([#18314](https://github.com/Koenkk/zigbee2mqtt/issues/18314)) ([af0521b](https://github.com/Koenkk/zigbee2mqtt/commit/af0521b54ca9195e62d68ccd9fcb49d0d0c226e4))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.41.0 ([#18328](https://github.com/Koenkk/zigbee2mqtt/issues/18328)) ([330a913](https://github.com/Koenkk/zigbee2mqtt/commit/330a913e76b556295f57290743670a40e48139ca))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.42.0 ([#18343](https://github.com/Koenkk/zigbee2mqtt/issues/18343)) ([313865b](https://github.com/Koenkk/zigbee2mqtt/commit/313865b0a38754df52ef2f28132053a47379cc43))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.43.0 ([#18356](https://github.com/Koenkk/zigbee2mqtt/issues/18356)) ([7a22cee](https://github.com/Koenkk/zigbee2mqtt/commit/7a22cee639bf9115c6782302900b89f4978468d9))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.44.0 ([#18365](https://github.com/Koenkk/zigbee2mqtt/issues/18365)) ([3408e3c](https://github.com/Koenkk/zigbee2mqtt/commit/3408e3c05a102d32818260c0368a47c9726aa40d))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.45.0 ([#18404](https://github.com/Koenkk/zigbee2mqtt/issues/18404)) ([57d031d](https://github.com/Koenkk/zigbee2mqtt/commit/57d031d54aadaf87513d2c7d32f3302db501be67))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.46.0 ([#18421](https://github.com/Koenkk/zigbee2mqtt/issues/18421)) ([ced3d43](https://github.com/Koenkk/zigbee2mqtt/commit/ced3d43dad5f8a578f1875ffaf68ffb1d177c584))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.47.0 ([#18430](https://github.com/Koenkk/zigbee2mqtt/issues/18430)) ([087b85c](https://github.com/Koenkk/zigbee2mqtt/commit/087b85cb9f3bc7a317dc22483461de32a9c1a0d8))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.48.0 ([#18438](https://github.com/Koenkk/zigbee2mqtt/issues/18438)) ([d487f37](https://github.com/Koenkk/zigbee2mqtt/commit/d487f37a7a755e37caf3a8da4f8190981fdb5625))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.49.0 ([#18443](https://github.com/Koenkk/zigbee2mqtt/issues/18443)) ([8139d7f](https://github.com/Koenkk/zigbee2mqtt/commit/8139d7f1f494b4051c1bba7ea453e76e4be77fad))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.50.0 ([#18475](https://github.com/Koenkk/zigbee2mqtt/issues/18475)) ([3478e40](https://github.com/Koenkk/zigbee2mqtt/commit/3478e4001760541b65d9bfdbdcd1d082a3da8023))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.50.1 ([#18487](https://github.com/Koenkk/zigbee2mqtt/issues/18487)) ([683d29f](https://github.com/Koenkk/zigbee2mqtt/commit/683d29f9f70f3149cbbff6b543d53af0f931e706))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.132 ([#18289](https://github.com/Koenkk/zigbee2mqtt/issues/18289)) ([05eff95](https://github.com/Koenkk/zigbee2mqtt/commit/05eff957eb3c5414df7bf2909b5b6d9ff6fdb258))
|
||||
* **ignore:** update zigbee2mqtt-frontend to 0.6.133 ([#18390](https://github.com/Koenkk/zigbee2mqtt/issues/18390)) ([e1d52ae](https://github.com/Koenkk/zigbee2mqtt/commit/e1d52ae43aa132f296d151eced7d208cfb1b3a62))
|
||||
* Use HA sensor device class `atmospheric_pressure` instead of `pressure` ([#18306](https://github.com/Koenkk/zigbee2mqtt/issues/18306)) ([1252f10](https://github.com/Koenkk/zigbee2mqtt/commit/1252f10d39beed640370410a45d5c2fdc504b440))
|
||||
|
||||
## [1.32.1](https://github.com/Koenkk/zigbee2mqtt/compare/1.32.0...1.32.1) (2023-07-08)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.33.1-hotfix.0 ([15601d6](https://github.com/Koenkk/zigbee2mqtt/commit/15601d6339cfc7408872a71071a98abadd9b33ff))
|
||||
|
||||
## [1.32.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.31.2...1.32.0) (2023-07-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Support Home Assistant water device_class ([#18066](https://github.com/Koenkk/zigbee2mqtt/issues/18066)) ([e083a6b](https://github.com/Koenkk/zigbee2mqtt/commit/e083a6bff7fafd17d5ff6ad4b87916b83727d8be))
|
||||
* Support settable Home Assistant text ([#18114](https://github.com/Koenkk/zigbee2mqtt/issues/18114)) ([9474452](https://github.com/Koenkk/zigbee2mqtt/commit/94744525ba466c6512ab6a6181b3073970a02023))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix Home Assistant truncate error when `program` is `null`. https://github.com/Koenkk/zigbee2mqtt/issues/16460 ([11f0be5](https://github.com/Koenkk/zigbee2mqtt/commit/11f0be55d871363cb4c07adbbeac57fb4ddf479e))
|
||||
* **ignore:** Better fix for [#17891](https://github.com/Koenkk/zigbee2mqtt/issues/17891) ([#17951](https://github.com/Koenkk/zigbee2mqtt/issues/17951)) ([a7e02a7](https://github.com/Koenkk/zigbee2mqtt/commit/a7e02a7be847418df463095b68951c1a1459ca83))
|
||||
* **ignore:** Fix DeviceMessage.meta type ([8833947](https://github.com/Koenkk/zigbee2mqtt/commit/88339473ce7eb09add8f1836ab4c33b4a405d7e7))
|
||||
* **ignore:** Fix e10dd893ab2875e5420952b4452bf75aeec93922 ([446dbbf](https://github.com/Koenkk/zigbee2mqtt/commit/446dbbf1fc0e07c61cd233c8b4ea509ce6423c86))
|
||||
* **ignore:** update dependencies ([#17991](https://github.com/Koenkk/zigbee2mqtt/issues/17991)) ([e10dd89](https://github.com/Koenkk/zigbee2mqtt/commit/e10dd893ab2875e5420952b4452bf75aeec93922))
|
||||
* **ignore:** update dependencies ([#18052](https://github.com/Koenkk/zigbee2mqtt/issues/18052)) ([1dc0b24](https://github.com/Koenkk/zigbee2mqtt/commit/1dc0b24e9a8ae9a03809aff5d3ff9758b63d9f22))
|
||||
* **ignore:** update dependencies ([#18112](https://github.com/Koenkk/zigbee2mqtt/issues/18112)) ([57566c7](https://github.com/Koenkk/zigbee2mqtt/commit/57566c76ed8ccaabcdc32f84a7fddcf5da3b02b3))
|
||||
* **ignore:** update zigbee-herdsman to 0.15.0 ([#17966](https://github.com/Koenkk/zigbee2mqtt/issues/17966)) ([05cab61](https://github.com/Koenkk/zigbee2mqtt/commit/05cab61e1faa15a9a72d87f8e9ec26e3d1eb4046))
|
||||
* **ignore:** update zigbee-herdsman to 0.15.1 ([#18080](https://github.com/Koenkk/zigbee2mqtt/issues/18080)) ([17cff2a](https://github.com/Koenkk/zigbee2mqtt/commit/17cff2a26f16207bd30bb2659e11ce792d02dd75))
|
||||
* **ignore:** update zigbee-herdsman to 0.15.2 ([#18091](https://github.com/Koenkk/zigbee2mqtt/issues/18091)) ([03005b3](https://github.com/Koenkk/zigbee2mqtt/commit/03005b38c86f79384e20bb240a8947ce969411c6))
|
||||
* **ignore:** update zigbee-herdsman to 0.15.3 ([#18116](https://github.com/Koenkk/zigbee2mqtt/issues/18116)) ([2d62027](https://github.com/Koenkk/zigbee2mqtt/commit/2d62027e24ea2de401155e0bc7fabaee85fa7bab))
|
||||
* **ignore:** update zigbee-herdsman to 0.16.0 ([#18124](https://github.com/Koenkk/zigbee2mqtt/issues/18124)) ([38c2ea5](https://github.com/Koenkk/zigbee2mqtt/commit/38c2ea5bf72f67f29b89dc84524e91923989fd27))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.20.0 ([#17954](https://github.com/Koenkk/zigbee2mqtt/issues/17954)) ([0c7e7e5](https://github.com/Koenkk/zigbee2mqtt/commit/0c7e7e5089d1023150f23123df7eab90ea65fe59))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.22.0 ([#18004](https://github.com/Koenkk/zigbee2mqtt/issues/18004)) ([b0b3aee](https://github.com/Koenkk/zigbee2mqtt/commit/b0b3aee6483efe9fbb2f434c44ed76d6b3a4e54a))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.23.0 ([#18015](https://github.com/Koenkk/zigbee2mqtt/issues/18015)) ([4ad838c](https://github.com/Koenkk/zigbee2mqtt/commit/4ad838c114edc7dd5d9d104df9b2ce49933a8c20))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.24.0 ([#18025](https://github.com/Koenkk/zigbee2mqtt/issues/18025)) ([c47f5df](https://github.com/Koenkk/zigbee2mqtt/commit/c47f5df021478e8997f13f06ea17591b745615e3))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.25.0 ([#18056](https://github.com/Koenkk/zigbee2mqtt/issues/18056)) ([57aaa5a](https://github.com/Koenkk/zigbee2mqtt/commit/57aaa5a85aff308d4bd66353fd42969eb38a60d4))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.26.0 ([#18067](https://github.com/Koenkk/zigbee2mqtt/issues/18067)) ([91d67a0](https://github.com/Koenkk/zigbee2mqtt/commit/91d67a0ef5135e08ac81171b78931b7903efcc5a))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.27.0 ([#18075](https://github.com/Koenkk/zigbee2mqtt/issues/18075)) ([5777d76](https://github.com/Koenkk/zigbee2mqtt/commit/5777d76e5508d7dba4750aa5efc34977ba01de1a))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.28.0 ([#18092](https://github.com/Koenkk/zigbee2mqtt/issues/18092)) ([6d3389b](https://github.com/Koenkk/zigbee2mqtt/commit/6d3389b409b003740ac4b2b90aa39a8ccbec26e3))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.29.0 ([#18099](https://github.com/Koenkk/zigbee2mqtt/issues/18099)) ([a744838](https://github.com/Koenkk/zigbee2mqtt/commit/a744838e9c982461479f0b5273e25abd32c4074d))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.30.0 ([#18115](https://github.com/Koenkk/zigbee2mqtt/issues/18115)) ([a56719d](https://github.com/Koenkk/zigbee2mqtt/commit/a56719d19ca32fabf375e293cdef0172826c8672))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.31.0 ([#18125](https://github.com/Koenkk/zigbee2mqtt/issues/18125)) ([ed85367](https://github.com/Koenkk/zigbee2mqtt/commit/ed85367e5383b9a59a69fe2836b02763f6744849))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.32.0 ([#18132](https://github.com/Koenkk/zigbee2mqtt/issues/18132)) ([61dd1dd](https://github.com/Koenkk/zigbee2mqtt/commit/61dd1dd96dc9154f2b46a635bf2ccaeac2d69ba5))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.33.0 ([#18156](https://github.com/Koenkk/zigbee2mqtt/issues/18156)) ([81973c3](https://github.com/Koenkk/zigbee2mqtt/commit/81973c3a2a356c52859d167d3eedaf78f747b8e5))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.33.1 ([#18161](https://github.com/Koenkk/zigbee2mqtt/issues/18161)) ([d1824f1](https://github.com/Koenkk/zigbee2mqtt/commit/d1824f169b71e0590dce14d7f451dbbb67bdf5d9))
|
||||
|
||||
## [1.31.0](https://github.com/Koenkk/zigbee2mqtt/compare/1.30.4...v1.31.0) (2023-06-01)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **ignore:** update CI/CD ([b3de973](https://github.com/Koenkk/zigbee2mqtt/commit/b3de973af66e7c308987adeaab4d89759d4bfef0))
|
||||
* **ignore:** update CI/CD ([0d17d26](https://github.com/Koenkk/zigbee2mqtt/commit/0d17d2613357db225f224bdbf191fbd9ad17b4a5))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.6.0 ([#17774](https://github.com/Koenkk/zigbee2mqtt/issues/17774)) ([24baed3](https://github.com/Koenkk/zigbee2mqtt/commit/24baed3053d91276369d0cad02e96afee97329f8))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.7.0 ([#17777](https://github.com/Koenkk/zigbee2mqtt/issues/17777)) ([1344ae0](https://github.com/Koenkk/zigbee2mqtt/commit/1344ae06163f5b6a3c68e532f39b379158040c6f))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix Home Assistant `Payload is not supported (e.g. open, closed, opening, closing, stopped): STOP` error. https://github.com/Koenkk/zigbee2mqtt/issues/17552 ([e6f9aed](https://github.com/Koenkk/zigbee2mqtt/commit/e6f9aeda06ec96c76c3d455bb016a10073d0bf54))
|
||||
* **ignore:** Fix build ([#17838](https://github.com/Koenkk/zigbee2mqtt/issues/17838)) ([d119f60](https://github.com/Koenkk/zigbee2mqtt/commit/d119f609de683313335fbb37e2ce358a16fdc597))
|
||||
* **ignore:** update dependencies ([#17811](https://github.com/Koenkk/zigbee2mqtt/issues/17811)) ([a71857c](https://github.com/Koenkk/zigbee2mqtt/commit/a71857c7236c66da577352160de96eacc683c7c3))
|
||||
* **ignore:** update zigbee-herdsman to 0.14.117 ([#17770](https://github.com/Koenkk/zigbee2mqtt/issues/17770)) ([8457c37](https://github.com/Koenkk/zigbee2mqtt/commit/8457c3735b2f2c9c5106ce76fc7d2988f9eb1ea1))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.10.0 ([#17797](https://github.com/Koenkk/zigbee2mqtt/issues/17797)) ([b7b3b51](https://github.com/Koenkk/zigbee2mqtt/commit/b7b3b51cfeb9b672f2cca128733b273f50966e19))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.11.0 ([#17808](https://github.com/Koenkk/zigbee2mqtt/issues/17808)) ([2e8dfbb](https://github.com/Koenkk/zigbee2mqtt/commit/2e8dfbb96ecbebece84cc1aab12899162e27f82e))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.12.0 ([#17837](https://github.com/Koenkk/zigbee2mqtt/issues/17837)) ([be82b73](https://github.com/Koenkk/zigbee2mqtt/commit/be82b737be5f7a7e58d653628ce66e0813fad111))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.13.0 ([#17842](https://github.com/Koenkk/zigbee2mqtt/issues/17842)) ([b228d9d](https://github.com/Koenkk/zigbee2mqtt/commit/b228d9daf64c8d315803120814dc31272ee1c459))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.13.1 ([#17851](https://github.com/Koenkk/zigbee2mqtt/issues/17851)) ([4b20dfa](https://github.com/Koenkk/zigbee2mqtt/commit/4b20dfafab6193f328aa9133d2b063284abf6cbf))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.8.2 ([#17789](https://github.com/Koenkk/zigbee2mqtt/issues/17789)) ([8b579b6](https://github.com/Koenkk/zigbee2mqtt/commit/8b579b6533876f3b7440dc74b2b709cbfc7ccaf3))
|
||||
* **ignore:** update zigbee-herdsman-converters to 15.9.0 ([#17791](https://github.com/Koenkk/zigbee2mqtt/issues/17791)) ([5b807c3](https://github.com/Koenkk/zigbee2mqtt/commit/5b807c36eced55e790a8393667e07680736f37be))
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
# Contributing to Zigbee2MQTT
|
||||
|
||||
Everybody is invited and welcome to contribute to Zigbee2MQTT. Zigbee2MQTT is writen in JavaScript and is based upon [zigbee-herdsman](https://github.com/koenkk/zigbee-herdsman) and [zigbee-herdsman-converters](https://github.com/koenkk/zigbee-herdsman-converters). Zigbee-herdsman-converters contains all device definition, zigbee-herdsman is responsible for handling all communication with the adapter.
|
||||
Everybody is invited and welcome to contribute to Zigbee2MQTT. Zigbee2MQTT is written in JavaScript and is based upon [zigbee-herdsman](https://github.com/koenkk/zigbee-herdsman) and [zigbee-herdsman-converters](https://github.com/koenkk/zigbee-herdsman-converters). Zigbee-herdsman-converters contains all device definitions, zigbee-herdsman is responsible for handling all communication with the adapter.
|
||||
|
||||
- Pull requests are always created against the [**dev**](https://github.com/Koenkk/zigbee2mqtt/tree/dev) branch.
|
||||
- Easiest way to start developing Zigbee2MQTT is by setting up a development environment (aka bare-metal installation). You can follow this [guide](https://www.zigbee2mqtt.io/guide/installation/01_linux.html) to do this.
|
||||
- You can run the tests locally by executing `npm test`. Zigbee2MQTT enforces 100% code coverage, in case you add new code check if your code is covered by running `npm run test-with-coverage`. The coverage report can be found under `coverage/lcov-report/index.html`. Linting is also enforced and can be run with `npm run eslint`.
|
||||
- When you want to add support for a new devices no changes to Zigbee2MQTT have to be made, only to zigbee-herdsman-converters. You can find a guide for it [here](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html).
|
||||
- When you want to add support for a new device no changes to Zigbee2MQTT have to be made, only to zigbee-herdsman-converters. You can find a guide for it [here](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html).
|
||||
|
||||
@@ -53,16 +53,40 @@ Zigbee2MQTT integrates well with (almost) every home automation solution because
|
||||
<img align="left" height="100px" width="100px" src="https://user-images.githubusercontent.com/7738048/40914297-49e6e560-6800-11e8-8904-36cce896e5a8.png">
|
||||
|
||||
### [Home Assistant](https://www.home-assistant.io/)
|
||||
- [Home Assistant](https://www.home-assistant.io/hassio/): Using [the official addon](https://github.com/zigbee2mqtt/hassio-zigbee2mqtt)
|
||||
- Generic install: using instructions [here](https://www.zigbee2mqtt.io/guide/usage/integrations/home_assistant.html)
|
||||
- [Home Assistant OS](https://www.home-assistant.io/installation/): Using [the official addon](https://github.com/zigbee2mqtt/hassio-zigbee2mqtt)
|
||||
- Other installation: using instructions [here](https://www.zigbee2mqtt.io/guide/usage/integrations/home_assistant.html)
|
||||
|
||||
<br>
|
||||
|
||||
<img align="left" height="100px" width="100px" src="https://etc.athom.com/logo/white/256.png">
|
||||
|
||||
### [Homey](https://homey.app/)
|
||||
- Integration implemented in the [Homey App](https://homey.app/nl-nl/app/com.gruijter.zigbee2mqtt/)
|
||||
- Documentation and support in the [Homey Forum](https://community.homey.app/t/83214)
|
||||
|
||||
<br>
|
||||
|
||||
<img align="left" height="100px" width="100px" src="https://user-images.githubusercontent.com/2734836/47615848-b8dd8700-dabd-11e8-9d77-175002dd8987.png">
|
||||
|
||||
### [Domoticz](https://www.domoticz.com/)
|
||||
- Integration implemented in [domoticz-zigbee2mqtt-plugin](https://github.com/stas-demydiuk/domoticz-zigbee2mqtt-plugin)
|
||||
- Integration implemented in Domoticz ([documentation](https://www.domoticz.com/wiki/Zigbee2MQTT)).
|
||||
|
||||
<br>
|
||||
|
||||
<img align="left" height="100px" width="100px" src="./images/gladys-assistant-logo.jpg">
|
||||
|
||||
### [Gladys Assistant](https://gladysassistant.com/)
|
||||
- Integration implemented natively in Gladys Assistant ([documentation](https://gladysassistant.com/docs/integrations/zigbee2mqtt/)).
|
||||
|
||||
<br>
|
||||
|
||||
<img align="left" height="100px" width="100px" src="https://forum.iobroker.net/assets/uploads/system/site-logo.png">
|
||||
|
||||
### [IoBroker](https://www.iobroker.net/)
|
||||
- Integration implemented in IoBroker ([documentation](https://github.com/o0shojo0o/ioBroker.zigbee2mqtt)).
|
||||
|
||||
<br>
|
||||
|
||||
## Architecture
|
||||

|
||||
|
||||
@@ -73,7 +97,7 @@ Zigbee2MQTT is made up of three modules, each developed in its own Github projec
|
||||
Zigbee2MQTT uses TypeScript (partially for now). Therefore after making changes to files in the `lib/` directory you need to recompile Zigbee2MQTT. This can be done by executing `npm run build`. For faster development instead of running `npm run build` you can run `npm run build-watch` in another terminal session, this will recompile as you change files.
|
||||
|
||||
## Supported devices
|
||||
See [Supported devices](https://www.zigbee2mqtt.io/supported-devices) to check whether your device is supported. There is quite an extensive list, including devices from vendors like Xiaomi, Ikea, Philips, OSRAM and more.
|
||||
See [Supported devices](https://www.zigbee2mqtt.io/supported-devices) to check whether your device is supported. There is quite an extensive list, including devices from vendors like [Xiaomi](https://www.zigbee2mqtt.io/supported-devices/#v=Xiaomi), [Ikea](https://www.zigbee2mqtt.io/supported-devices/#v=IKEA), [Philips](https://www.zigbee2mqtt.io/supported-devices/#v=Philips), [OSRAM](https://www.zigbee2mqtt.io/supported-devices/#v=OSRAM) and more.
|
||||
|
||||
If it's not listed in [Supported devices](https://www.zigbee2mqtt.io/supported-devices), support can be added (fairly) easily, see [How to support new devices](https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html).
|
||||
|
||||
|
||||
@@ -6,6 +6,5 @@ module.exports = {
|
||||
assumptions: {setPublicClassFields: true},
|
||||
plugins: [
|
||||
['@babel/plugin-proposal-decorators', {'legacy': true}],
|
||||
['@babel/plugin-proposal-class-properties'],
|
||||
],
|
||||
};
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Home Assistant integration (MQTT discovery)
|
||||
homeassistant: false
|
||||
|
||||
# Enable the frontend, runs on port 8080 by default
|
||||
frontend: true
|
||||
|
||||
# MQTT settings
|
||||
mqtt:
|
||||
# MQTT base topic for zigbee2mqtt MQTT messages
|
||||
base_topic: zigbee2mqtt
|
||||
# MQTT server URL
|
||||
server: 'mqtt://localhost'
|
||||
# MQTT server authentication, uncomment if required:
|
||||
# user: my_user
|
||||
# password: my_password
|
||||
|
||||
# Serial settings
|
||||
serial:
|
||||
# Location of CC2531 USB sniffer
|
||||
port: /dev/ttyACM0
|
||||
|
||||
# Advanced settings
|
||||
advanced:
|
||||
# Let Zigbee2MQTT generate a network key on first start
|
||||
network_key: GENERATE
|
||||
# Let Zigbee2MQTT generate a pan_id on first start
|
||||
pan_id: GENERATE
|
||||
# Let Zigbee2MQTT generate a ext_pan_id on first start
|
||||
ext_pan_id: GENERATE
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.17.0 as base
|
||||
FROM alpine:3.18.4 as base
|
||||
|
||||
WORKDIR /app
|
||||
RUN apk add --no-cache tzdata eudev tini nodejs
|
||||
@@ -20,7 +20,7 @@ FROM base as release
|
||||
|
||||
COPY --from=dependencies_and_build /app/node_modules ./node_modules
|
||||
COPY dist ./dist
|
||||
COPY package.json LICENSE index.js data/configuration.yaml ./
|
||||
COPY package.json LICENSE index.js data/configuration.yaml data/configuration.example.yaml ./
|
||||
|
||||
COPY docker/docker-entrypoint.sh /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
||||
|
||||
@@ -11,7 +11,7 @@ echo "Using '$DATA' as data directory"
|
||||
|
||||
if [ ! -f "$DATA/configuration.yaml" ]; then
|
||||
echo "Creating configuration file..."
|
||||
cp /app/configuration.yaml "$DATA/configuration.yaml"
|
||||
cp /app/configuration.example.yaml "$DATA/configuration.yaml"
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 69 KiB |
Executable → Regular
+120
-1
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 25 KiB |
@@ -4,7 +4,7 @@ const fs = require('fs');
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const {exec} = require('child_process');
|
||||
const rimraf = require('rimraf');
|
||||
const {rimrafSync} = require('rimraf');
|
||||
require('source-map-support').install();
|
||||
|
||||
let controller;
|
||||
@@ -41,7 +41,7 @@ async function writeHash() {
|
||||
async function build(reason) {
|
||||
return new Promise((resolve, reject) => {
|
||||
process.stdout.write(`Building Zigbee2MQTT... (${reason})`);
|
||||
rimraf.sync('dist');
|
||||
rimrafSync('dist');
|
||||
const env = {...process.env};
|
||||
const _600mb = 629145600;
|
||||
if (_600mb > os.totalmem() && !env.NODE_OPTIONS) {
|
||||
|
||||
+28
-7
@@ -8,6 +8,7 @@ import utils from './util/utils';
|
||||
import stringify from 'json-stable-stringify-without-jsonify';
|
||||
import assert from 'assert';
|
||||
import bind from 'bind-decorator';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
|
||||
// Extensions
|
||||
import ExtensionFrontend from './extension/frontend';
|
||||
@@ -39,6 +40,14 @@ const AllExtensions = [
|
||||
type ExtensionArgs = [Zigbee, MQTT, State, PublishEntityState, EventBus,
|
||||
(enable: boolean, name: string) => Promise<void>, () => void, (extension: Extension) => Promise<void>];
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let sdNotify: any = null;
|
||||
try {
|
||||
sdNotify = process.env.NOTIFY_SOCKET ? require('sd-notify') : null;
|
||||
} catch {
|
||||
// sd-notify is optional
|
||||
}
|
||||
|
||||
export class Controller {
|
||||
private eventBus: EventBus;
|
||||
private zigbee: Zigbee;
|
||||
@@ -51,10 +60,7 @@ export class Controller {
|
||||
|
||||
constructor(restartCallback: () => void, exitCallback: (code: number, restart: boolean) => void) {
|
||||
logger.init();
|
||||
this.eventBus = new EventBus( /* istanbul ignore next */ (error) => {
|
||||
logger.error(`Error: ${error.message}`);
|
||||
logger.debug(error.stack);
|
||||
});
|
||||
this.eventBus = new EventBus();
|
||||
this.zigbee = new Zigbee(this.eventBus);
|
||||
this.mqtt = new MQTT(this.eventBus);
|
||||
this.state = new State(this.eventBus, this.zigbee);
|
||||
@@ -86,6 +92,8 @@ export class Controller {
|
||||
/* istanbul ignore next */
|
||||
settings.get().advanced.soft_reset_timeout !== 0 && new ExtensionSoftReset(...this.extensionArgs),
|
||||
].filter((n) => n);
|
||||
|
||||
zhc.setLogger(logger);
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
@@ -121,7 +129,7 @@ export class Controller {
|
||||
const devices = this.zigbee.devices(false);
|
||||
logger.info(`Currently ${devices.length} devices are joined:`);
|
||||
for (const device of devices) {
|
||||
const model = device.definition ?
|
||||
const model = device.isSupported ?
|
||||
`${device.definition.model} - ${device.definition.vendor} ${device.definition.description}` :
|
||||
'Not supported';
|
||||
logger.info(`${device.name} (${device.ieeeAddr}): ${model} (${device.zh.type})`);
|
||||
@@ -165,6 +173,12 @@ export class Controller {
|
||||
(data) => utils.publishLastSeen(data, settings.get(), false, this.publishEntityState));
|
||||
|
||||
logger.info(`Zigbee2MQTT started!`);
|
||||
|
||||
const watchdogInterval = sdNotify?.watchdogInterval() || 0;
|
||||
if (watchdogInterval > 0) {
|
||||
sdNotify.startWatchdogMode(Math.floor(watchdogInterval / 2));
|
||||
}
|
||||
sdNotify?.ready();
|
||||
}
|
||||
|
||||
@bind async enableDisableExtension(enable: boolean, name: string): Promise<void> {
|
||||
@@ -189,6 +203,8 @@ export class Controller {
|
||||
}
|
||||
|
||||
async stop(restart = false): Promise<void> {
|
||||
sdNotify?.stopping();
|
||||
|
||||
// Call extensions
|
||||
await this.callExtensions('stop', this.extensions);
|
||||
this.eventBus.removeListeners(this);
|
||||
@@ -205,6 +221,8 @@ export class Controller {
|
||||
logger.error('Failed to stop Zigbee2MQTT');
|
||||
await this.exit(1, restart);
|
||||
}
|
||||
|
||||
sdNotify?.stopWatchdogMode();
|
||||
}
|
||||
|
||||
async exit(code: number, restart = false): Promise<void> {
|
||||
@@ -241,13 +259,16 @@ export class Controller {
|
||||
|
||||
if (entity.isDevice() && settings.get().mqtt.include_device_information) {
|
||||
message.device = {
|
||||
friendlyName: entity.name, model: entity.definition ? entity.definition.model : 'unknown',
|
||||
friendlyName: entity.name, model: entity.definition?.model,
|
||||
ieeeAddr: entity.ieeeAddr, networkAddress: entity.zh.networkAddress, type: entity.zh.type,
|
||||
manufacturerID: entity.zh.manufacturerID, manufacturerName: entity.zh.manufacturerName,
|
||||
manufacturerID: entity.zh.manufacturerID,
|
||||
powerSource: entity.zh.powerSource, applicationVersion: entity.zh.applicationVersion,
|
||||
stackVersion: entity.zh.stackVersion, zclVersion: entity.zh.zclVersion,
|
||||
hardwareVersion: entity.zh.hardwareVersion, dateCode: entity.zh.dateCode,
|
||||
softwareBuildID: entity.zh.softwareBuildID,
|
||||
// Manufacturer name can contain \u0000, remove this.
|
||||
// https://github.com/home-assistant/core/issues/85691
|
||||
manufacturerName: entity.zh.manufacturerName?.split('\u0000')[0],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+23
-9
@@ -1,5 +1,5 @@
|
||||
import events from 'events';
|
||||
events.captureRejections = true;
|
||||
import logger from './util/logger';
|
||||
|
||||
// eslint-disable-next-line
|
||||
type ListenerKey = object;
|
||||
@@ -8,9 +8,8 @@ export default class EventBus {
|
||||
private callbacksByExtension: { [s: string]: { event: string, callback: (...args: unknown[]) => void }[] } = {};
|
||||
private emitter = new events.EventEmitter();
|
||||
|
||||
constructor(onError: (error: Error) => void) {
|
||||
constructor() {
|
||||
this.emitter.setMaxListeners(100);
|
||||
this.emitter.on('error', onError);
|
||||
}
|
||||
|
||||
public emitAdapterDisconnected(): void {
|
||||
@@ -91,6 +90,13 @@ export default class EventBus {
|
||||
this.on('entityOptionsChanged', callback, key);
|
||||
}
|
||||
|
||||
public emitExposesChanged(data: eventdata.ExposesChanged): void {
|
||||
this.emitter.emit('exposesChanged', data);
|
||||
}
|
||||
public onExposesChanged(key: ListenerKey, callback: (data: eventdata.ExposesChanged) => void): void {
|
||||
this.on('exposesChanged', callback, key);
|
||||
}
|
||||
|
||||
public emitDeviceLeave(data: eventdata.DeviceLeave): void {
|
||||
this.emitter.emit('deviceLeave', data);
|
||||
}
|
||||
@@ -140,10 +146,10 @@ export default class EventBus {
|
||||
this.on('devicesChanged', callback, key);
|
||||
}
|
||||
|
||||
public emitScenesChanged(): void {
|
||||
this.emitter.emit('scenesChanged');
|
||||
public emitScenesChanged(data: eventdata.ScenesChanged): void {
|
||||
this.emitter.emit('scenesChanged', data);
|
||||
}
|
||||
public onScenesChanged(key: ListenerKey, callback: () => void): void {
|
||||
public onScenesChanged(key: ListenerKey, callback: (data: eventdata.ScenesChanged) => void): void {
|
||||
this.on('scenesChanged', callback, key);
|
||||
}
|
||||
|
||||
@@ -161,10 +167,18 @@ export default class EventBus {
|
||||
this.on('stateChange', callback, key);
|
||||
}
|
||||
|
||||
private on(event: string, callback: (...args: unknown[]) => void, key: ListenerKey): void {
|
||||
private on(event: string, callback: (...args: unknown[]) => (Promise<void> | void), key: ListenerKey): void {
|
||||
if (!this.callbacksByExtension[key.constructor.name]) this.callbacksByExtension[key.constructor.name] = [];
|
||||
this.callbacksByExtension[key.constructor.name].push({event, callback});
|
||||
this.emitter.on(event, callback);
|
||||
const wrappedCallback = async (...args: unknown[]): Promise<void> => {
|
||||
try {
|
||||
await callback(...args);
|
||||
} catch (error) {
|
||||
logger.error(`EventBus error '${key.constructor.name}/${event}': ${error.message}`);
|
||||
logger.debug(error.stack);
|
||||
}
|
||||
};
|
||||
this.callbacksByExtension[key.constructor.name].push({event, callback: wrappedCallback});
|
||||
this.emitter.on(event, wrappedCallback);
|
||||
}
|
||||
|
||||
public removeListeners(key: ListenerKey): void {
|
||||
|
||||
@@ -4,6 +4,7 @@ import utils from '../util/utils';
|
||||
import * as settings from '../util/settings';
|
||||
import debounce from 'debounce';
|
||||
import bind from 'bind-decorator';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
|
||||
const retrieveOnReconnect = [
|
||||
{keys: ['state']},
|
||||
@@ -17,6 +18,7 @@ export default class Availability extends Extension {
|
||||
private retrieveStateDebouncers: {[s: string]: () => void} = {};
|
||||
private pingQueue: Device[] = [];
|
||||
private pingQueueExecuting = false;
|
||||
private stopped = false;
|
||||
|
||||
private getTimeout(device: Device): number {
|
||||
if (typeof device.options.availability === 'object' && device.options.availability?.timeout != null) {
|
||||
@@ -47,7 +49,7 @@ export default class Availability extends Extension {
|
||||
private resetTimer(device: Device): void {
|
||||
clearTimeout(this.timers[device.ieeeAddr]);
|
||||
|
||||
// If the timer triggers, the device is not avaiable anymore otherwise resetTimer already have been called
|
||||
// If the timer triggers, the device is not available anymore otherwise resetTimer already have been called
|
||||
if (this.isActiveDevice(device)) {
|
||||
// If device did not check in, ping it, if that fails it will be marked as offline
|
||||
this.timers[device.ieeeAddr] = setTimeout(
|
||||
@@ -82,7 +84,7 @@ export default class Availability extends Extension {
|
||||
const disableRecovery = !(i == 1 && available);
|
||||
await device.zh.ping(disableRecovery);
|
||||
pingedSuccessfully = true;
|
||||
logger.debug(`Succesfully pinged '${device.name}' (attempt ${i + 1}/${attempts})`);
|
||||
logger.debug(`Successfully pinged '${device.name}' (attempt ${i + 1}/${attempts})`);
|
||||
break;
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to ping '${device.name}' (attempt ${i + 1}/${attempts}, ${error.message})`);
|
||||
@@ -92,6 +94,11 @@ export default class Availability extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.stopped) {
|
||||
// Exit here to avoid triggering any follow-up activity (e.g., re-queuing another ping attempt).
|
||||
return;
|
||||
}
|
||||
|
||||
this.publishAvailability(device, !pingedSuccessfully);
|
||||
this.resetTimer(device);
|
||||
this.removeFromPingQueue(device);
|
||||
@@ -103,9 +110,13 @@ export default class Availability extends Extension {
|
||||
}
|
||||
|
||||
override async start(): Promise<void> {
|
||||
if (this.stopped) {
|
||||
throw new Error('This extension cannot be restarted.');
|
||||
}
|
||||
|
||||
this.eventBus.onEntityRenamed(this, (data) => {
|
||||
if (utils.isAvailabilityEnabledForEntity(data.entity, settings.get())) {
|
||||
this.mqtt.publish(`${data.from}/availability`, null, {retain: true, qos: 0});
|
||||
this.mqtt.publish(`${data.from}/availability`, null, {retain: true, qos: 1});
|
||||
this.publishAvailability(data.entity, false, true);
|
||||
}
|
||||
});
|
||||
@@ -122,7 +133,7 @@ export default class Availability extends Extension {
|
||||
@bind private publishAvailabilityForAllEntities(): void {
|
||||
for (const entity of [...this.zigbee.devices(false), ...this.zigbee.groups()]) {
|
||||
if (utils.isAvailabilityEnabledForEntity(entity, settings.get())) {
|
||||
// Publish initial availablility
|
||||
// Publish initial availability
|
||||
this.publishAvailability(entity, true, false, true);
|
||||
|
||||
if (entity.isDevice()) {
|
||||
@@ -164,7 +175,7 @@ export default class Availability extends Extension {
|
||||
const topic = `${entity.name}/availability`;
|
||||
const payload = utils.availabilityPayload(available ? 'online' : 'offline', settings.get());
|
||||
this.availabilityCache[entity.ID] = available;
|
||||
this.mqtt.publish(topic, payload, {retain: true, qos: 0});
|
||||
this.mqtt.publish(topic, payload, {retain: true, qos: 1});
|
||||
|
||||
if (!skipGroups && entity.isDevice()) {
|
||||
this.zigbee.groups().filter((g) => g.hasMember(entity))
|
||||
@@ -183,8 +194,10 @@ export default class Availability extends Extension {
|
||||
}
|
||||
|
||||
override async stop(): Promise<void> {
|
||||
this.stopped = true;
|
||||
this.pingQueue = [];
|
||||
Object.values(this.timers).forEach((t) => clearTimeout(t));
|
||||
super.stop();
|
||||
await super.stop();
|
||||
}
|
||||
|
||||
private retrieveState(device: Device): void {
|
||||
@@ -199,8 +212,13 @@ export default class Availability extends Extension {
|
||||
for (const item of retrieveOnReconnect) {
|
||||
if (item.condition && this.state.get(device) && !item.condition(this.state.get(device))) continue;
|
||||
const converter = device.definition.toZigbee.find((c) => c.key.find((k) => item.keys.includes(k)));
|
||||
await converter?.convertGet?.(device.endpoint(), item.keys[0],
|
||||
{message: this.state.get(device), mapped: device.definition})
|
||||
const options: KeyValue = device.options;
|
||||
const state = this.state.get(device);
|
||||
const meta: zhc.Tz.Meta = {
|
||||
message: this.state.get(device), mapped: device.definition, logger, endpoint_name: null,
|
||||
options, state, device: device.zh,
|
||||
};
|
||||
await converter?.convertGet?.(device.endpoint(), item.keys[0], meta)
|
||||
.catch((e) => {
|
||||
logger.error(`Failed to read state of '${device.name}' after reconnect (${e.message})`);
|
||||
});
|
||||
|
||||
+33
-27
@@ -4,7 +4,7 @@ import utils from '../util/utils';
|
||||
import Extension from './extension';
|
||||
import stringify from 'json-stable-stringify-without-jsonify';
|
||||
import debounce from 'debounce';
|
||||
import * as zigbeeHersdman from 'zigbee-herdsman/dist';
|
||||
import * as zigbeeHerdsman from 'zigbee-herdsman/dist';
|
||||
import bind from 'bind-decorator';
|
||||
import Device from '../model/device';
|
||||
import Group from '../model/group';
|
||||
@@ -12,7 +12,7 @@ import Group from '../model/group';
|
||||
const legacyApi = settings.get().advanced.legacy_api;
|
||||
const legacyTopicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/(bind|unbind)/.+$`);
|
||||
const topicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/request/device/(bind|unbind)`);
|
||||
const clusterCandidates = ['genScenes', 'genOnOff', 'genLevelCtrl', 'lightingColorCtrl', 'closuresWindowCovering',
|
||||
const allClusterCandidates = ['genScenes', 'genOnOff', 'genLevelCtrl', 'lightingColorCtrl', 'closuresWindowCovering',
|
||||
'hvacThermostat', 'msIlluminanceMeasurement', 'msTemperatureMeasurement', 'msRelativeHumidity',
|
||||
'msSoilMoisture', 'msCO2'];
|
||||
|
||||
@@ -97,11 +97,12 @@ const pollOnMessage: PollOnMessage = [
|
||||
read: {cluster: 'genLevelCtrl', attributes: ['currentLevel']},
|
||||
// When the bound devices/members of group have the following manufacturerIDs
|
||||
manufacturerIDs: [
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.Philips,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.ATMEL,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.TELINK,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK_MICRO,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.BUSCH_JAEGER_ELEKTRO,
|
||||
],
|
||||
manufacturerNames: [
|
||||
'GLEDOPTO',
|
||||
@@ -132,11 +133,12 @@ const pollOnMessage: PollOnMessage = [
|
||||
},
|
||||
read: {cluster: 'genOnOff', attributes: ['onOff']},
|
||||
manufacturerIDs: [
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.Philips,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.ATMEL,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.TELINK,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK_MICRO,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.BUSCH_JAEGER_ELEKTRO,
|
||||
],
|
||||
manufacturerNames: [
|
||||
'GLEDOPTO',
|
||||
@@ -163,11 +165,12 @@ const pollOnMessage: PollOnMessage = [
|
||||
},
|
||||
},
|
||||
manufacturerIDs: [
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.Philips,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.ATMEL,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.MUELLER_LICHT_INT,
|
||||
zigbeeHersdman.Zcl.ManufacturerCode.TELINK,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.SIGNIFY_NETHERLANDS_B_V,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.ATMEL,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.GLEDOPTO_CO_LTD,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.MUELLER_LICHT_INTERNATIONAL_INC,
|
||||
zigbeeHerdsman.Zcl.ManufacturerCode.TELINK_MICRO,
|
||||
// Note: ManufacturerCode.BUSCH_JAEGER is left out intentionally here as their devices don't support colors
|
||||
],
|
||||
manufacturerNames: [
|
||||
'GLEDOPTO',
|
||||
@@ -219,32 +222,35 @@ export default class Bind extends Extension {
|
||||
const message = utils.parseJSON(data.message, data.message);
|
||||
|
||||
let error = null;
|
||||
const parsedSource = utils.parseEntityID(sourceKey);
|
||||
const parsedTarget = utils.parseEntityID(targetKey);
|
||||
const source = this.zigbee.resolveEntity(parsedSource.ID);
|
||||
const target = targetKey === 'default_bind_group' ?
|
||||
defaultBindGroup : this.zigbee.resolveEntity(parsedTarget.ID);
|
||||
const parsedSource = this.zigbee.resolveEntityAndEndpoint(sourceKey);
|
||||
const parsedTarget = this.zigbee.resolveEntityAndEndpoint(targetKey);
|
||||
const source = parsedSource.entity;
|
||||
const target = targetKey === 'default_bind_group' ? defaultBindGroup : parsedTarget.entity;
|
||||
const responseData: KeyValue = {from: sourceKey, to: targetKey};
|
||||
|
||||
if (!source || !(source instanceof Device)) {
|
||||
error = `Source device '${sourceKey}' does not exist`;
|
||||
} else if (parsedSource.endpointID && !parsedSource.endpoint) {
|
||||
error = `Source device '${parsedSource.ID}' does not have endpoint '${parsedSource.endpointID}'`;
|
||||
} else if (!target) {
|
||||
error = `Target device or group '${targetKey}' does not exist`;
|
||||
} else if (target instanceof Device && parsedTarget.endpointID && !parsedTarget.endpoint) {
|
||||
error = `Target device '${parsedTarget.ID}' does not have endpoint '${parsedTarget.endpointID}'`;
|
||||
} else {
|
||||
const successfulClusters: string[] = [];
|
||||
const failedClusters = [];
|
||||
const attemptedClusters = [];
|
||||
|
||||
const bindSource: zh.Endpoint = source.endpoint(parsedSource.endpoint);
|
||||
const bindSource: zh.Endpoint = parsedSource.endpoint;
|
||||
let bindTarget: number | zh.Group | zh.Endpoint = null;
|
||||
if (target instanceof Device) bindTarget = target.endpoint(parsedTarget.endpoint);
|
||||
if (target instanceof Device) bindTarget = parsedTarget.endpoint;
|
||||
else if (target instanceof Group) bindTarget = target.zh;
|
||||
else bindTarget = Number(target.ID);
|
||||
|
||||
// Find which clusters are supported by both the source and target.
|
||||
// Groups are assumed to support all clusters.
|
||||
const clusterCandidates = clusters ?? allClusterCandidates;
|
||||
for (const cluster of clusterCandidates) {
|
||||
if (clusters && !clusters.includes(cluster)) continue;
|
||||
let matchingClusters = false;
|
||||
|
||||
const anyClusterValid = utils.isZHGroup(bindTarget) || typeof bindTarget === 'number' ||
|
||||
@@ -386,7 +392,7 @@ export default class Bind extends Extension {
|
||||
}
|
||||
|
||||
await endpoint.configureReporting(bind.cluster.name, items);
|
||||
logger.info(`Succesfully setup reporting for '${entity}' cluster '${bind.cluster.name}'`);
|
||||
logger.info(`Successfully setup reporting for '${entity}' cluster '${bind.cluster.name}'`);
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to setup reporting for '${entity}' cluster '${bind.cluster.name}'`);
|
||||
}
|
||||
@@ -428,7 +434,7 @@ export default class Bind extends Extension {
|
||||
}
|
||||
|
||||
await endpoint.configureReporting(cluster, items);
|
||||
logger.info(`Succesfully disabled reporting for '${entity}' cluster '${cluster}'`);
|
||||
logger.info(`Successfully disabled reporting for '${entity}' cluster '${cluster}'`);
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to disable reporting for '${entity}' cluster '${cluster}'`);
|
||||
}
|
||||
|
||||
+49
-32
@@ -12,17 +12,19 @@ import Group from '../model/group';
|
||||
import data from '../util/data';
|
||||
import JSZip from 'jszip';
|
||||
import fs from 'fs';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
|
||||
const requestRegex = new RegExp(`${settings.get().mqtt.base_topic}/bridge/request/(.*)`);
|
||||
|
||||
type Scene = {id: number, name: string};
|
||||
type DefinitionPayload = {
|
||||
model: string, vendor: string, description: string, exposes: zhc.DefinitionExpose[], supports_ota:
|
||||
boolean, icon: string, options: zhc.DefinitionExpose[],
|
||||
model: string, vendor: string, description: string, exposes: zhc.Expose[], supports_ota:
|
||||
boolean, icon: string, options: zhc.Expose[],
|
||||
};
|
||||
|
||||
export default class Bridge extends Extension {
|
||||
private zigbee2mqttVersion: {commitHash: string, version: string};
|
||||
private zigbeeHerdsmanVersion: {version: string};
|
||||
private zigbeeHerdsmanConvertersVersion: {version: string};
|
||||
private coordinatorVersion: zh.CoordinatorVersion;
|
||||
private restartRequired = false;
|
||||
private lastJoinedDeviceIeeeAddr: string;
|
||||
@@ -33,6 +35,7 @@ export default class Bridge extends Extension {
|
||||
'device/options': this.deviceOptions,
|
||||
'device/configure_reporting': this.deviceConfigureReporting,
|
||||
'device/remove': this.deviceRemove,
|
||||
'device/generate_external_definition': this.deviceGenerateExternalDefinition,
|
||||
'device/rename': this.deviceRename,
|
||||
'group/add': this.groupAdd,
|
||||
'group/options': this.groupOptions,
|
||||
@@ -46,6 +49,7 @@ export default class Bridge extends Extension {
|
||||
'install_code/add': this.installCodeAdd,
|
||||
'touchlink/scan': this.touchlinkScan,
|
||||
'health_check': this.healthCheck,
|
||||
'coordinator_check': this.coordinatorCheck,
|
||||
'options': this.bridgeOptions,
|
||||
// Below are deprecated
|
||||
'config/last_seen': this.configLastSeen,
|
||||
@@ -66,6 +70,8 @@ export default class Bridge extends Extension {
|
||||
logger.addTransport(new EventTransport());
|
||||
|
||||
this.zigbee2mqttVersion = await utils.getZigbee2MQTTVersion();
|
||||
this.zigbeeHerdsmanVersion = await utils.getDependencyVersion('zigbee-herdsman');
|
||||
this.zigbeeHerdsmanConvertersVersion = await utils.getDependencyVersion('zigbee-herdsman-converters');
|
||||
this.coordinatorVersion = await this.zigbee.getCoordinatorVersion();
|
||||
|
||||
this.eventBus.onEntityRenamed(this, () => this.publishInfo());
|
||||
@@ -95,7 +101,7 @@ export default class Bridge extends Extension {
|
||||
const payload: KeyValue =
|
||||
{friendly_name: data.device.name, status: data.status, ieee_address: data.device.ieeeAddr};
|
||||
if (data.status === 'successful') {
|
||||
payload.supported = !!data.device.definition;
|
||||
payload.supported = data.device.isSupported;
|
||||
payload.definition = this.getDefinitionPayload(data.device);
|
||||
}
|
||||
publishEvent('device_interview', payload);
|
||||
@@ -164,7 +170,7 @@ export default class Bridge extends Extension {
|
||||
logger.setLevel(newSettings.advanced.log_level);
|
||||
}
|
||||
|
||||
logger.info('Succesfully changed options');
|
||||
logger.info('Successfully changed options');
|
||||
this.publishInfo();
|
||||
return utils.getResponse(message, {restart_required: this.restartRequired}, null);
|
||||
}
|
||||
@@ -181,6 +187,14 @@ export default class Bridge extends Extension {
|
||||
return utils.getResponse(message, {healthy: true}, null);
|
||||
}
|
||||
|
||||
@bind async coordinatorCheck(message: string | KeyValue): Promise<MQTTResponse> {
|
||||
const result = await this.zigbee.coordinatorCheck();
|
||||
const missingRouters = result.missingRouters.map((d) => {
|
||||
return {ieee_address: d.ieeeAddr, friendly_name: d.name};
|
||||
});
|
||||
return utils.getResponse(message, {missing_routers: missingRouters}, null);
|
||||
}
|
||||
|
||||
@bind async groupAdd(message: string | KeyValue): Promise<MQTTResponse> {
|
||||
if (typeof message === 'object' && !message.hasOwnProperty('friendly_name')) {
|
||||
throw new Error(`Invalid payload`);
|
||||
@@ -416,8 +430,13 @@ export default class Bridge extends Extension {
|
||||
throw new Error(`Invalid payload`);
|
||||
}
|
||||
|
||||
const parsedID = utils.parseEntityID(message.id);
|
||||
const endpoint = (this.getEntity('device', parsedID.ID) as Device).endpoint(parsedID.endpoint);
|
||||
const device = this.zigbee.resolveEntityAndEndpoint(message.id);
|
||||
if (!device.entity) throw new Error(`Device '${message.id}' does not exist`);
|
||||
|
||||
const endpoint = device.endpoint;
|
||||
if (device.endpointID && !endpoint) {
|
||||
throw new Error(`Device '${device.ID}' does not have endpoint '${device.endpointID}'`);
|
||||
}
|
||||
|
||||
const coordinatorEndpoint = this.zigbee.firstCoordinatorEndpoint();
|
||||
await endpoint.bind(message.cluster, coordinatorEndpoint);
|
||||
@@ -438,6 +457,19 @@ export default class Bridge extends Extension {
|
||||
}, null);
|
||||
}
|
||||
|
||||
@bind async deviceGenerateExternalDefinition(message: string | KeyValue): Promise<MQTTResponse> {
|
||||
if (typeof message !== 'object' || !message.hasOwnProperty('id')) {
|
||||
throw new Error(`Invalid payload`);
|
||||
}
|
||||
|
||||
const device = this.zigbee.resolveEntityAndEndpoint(message.id).entity as Device;
|
||||
if (!device) throw new Error(`Device '${message.id}' does not exist`);
|
||||
|
||||
const source = await zhc.generateExternalDefinitionSource(device.zh);
|
||||
|
||||
return utils.getResponse(message, {id: message.id, source}, null);
|
||||
}
|
||||
|
||||
async renameEntity(entityType: 'group' | 'device', message: string | KeyValue): Promise<MQTTResponse> {
|
||||
const deviceAndHasLast = entityType === 'device' && typeof message === 'object' && message.last === true;
|
||||
if (typeof message !== 'object' || (!message.hasOwnProperty('from') && !deviceAndHasLast) ||
|
||||
@@ -470,7 +502,7 @@ export default class Bridge extends Extension {
|
||||
this.publishInfo();
|
||||
}
|
||||
|
||||
// Repulish entity state
|
||||
// Republish entity state
|
||||
this.publishEntityState(entity, {});
|
||||
|
||||
return utils.getResponse(
|
||||
@@ -510,7 +542,7 @@ export default class Bridge extends Extension {
|
||||
}
|
||||
|
||||
if (force) {
|
||||
await entity.zh.removeFromDatabase();
|
||||
entity.zh.removeFromDatabase();
|
||||
} else {
|
||||
await entity.zh.removeFromNetwork();
|
||||
}
|
||||
@@ -573,6 +605,8 @@ export default class Bridge extends Extension {
|
||||
const payload = {
|
||||
version: this.zigbee2mqttVersion.version,
|
||||
commit: this.zigbee2mqttVersion.commitHash,
|
||||
zigbee_herdsman_converters: this.zigbeeHerdsmanConvertersVersion,
|
||||
zigbee_herdsman: this.zigbeeHerdsmanVersion,
|
||||
coordinator: {
|
||||
ieee_address: this.zigbee.firstCoordinatorEndpoint().getDevice().ieeeAddr,
|
||||
...this.coordinatorVersion,
|
||||
@@ -590,25 +624,6 @@ export default class Bridge extends Extension {
|
||||
'bridge/info', stringify(payload), {retain: true, qos: 0}, settings.get().mqtt.base_topic, true);
|
||||
}
|
||||
|
||||
private getScenes(entity: zh.Endpoint | zh.Group): Scene[] {
|
||||
const scenes: {[id: number]: Scene} = {};
|
||||
const endpoints = utils.isEndpoint(entity) ? [entity] : entity.members;
|
||||
const groupID = utils.isEndpoint(entity) ? 0 : entity.groupID;
|
||||
|
||||
for (const endpoint of endpoints) {
|
||||
for (const [key, data] of Object.entries(endpoint.meta?.scenes || {})) {
|
||||
const split = key.split('_');
|
||||
const sceneID = parseInt(split[0], 10);
|
||||
const sceneGroupID = parseInt(split[1], 10);
|
||||
if (sceneGroupID === groupID) {
|
||||
scenes[sceneID] = {id: sceneID, name: (data as KeyValue).name || `Scene ${sceneID}`};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Object.values(scenes);
|
||||
}
|
||||
|
||||
async publishDevices(): Promise<void> {
|
||||
interface Data {
|
||||
bindings: {cluster: string, target: {type: string, endpoint?: number, ieee_address?: string, id?: number}}[]
|
||||
@@ -621,7 +636,7 @@ export default class Bridge extends Extension {
|
||||
const endpoints: {[s: number]: Data} = {};
|
||||
for (const endpoint of device.zh.endpoints) {
|
||||
const data: Data = {
|
||||
scenes: this.getScenes(endpoint),
|
||||
scenes: utils.getScenes(endpoint),
|
||||
bindings: [],
|
||||
configured_reportings: [],
|
||||
clusters: {
|
||||
@@ -654,7 +669,7 @@ export default class Bridge extends Extension {
|
||||
ieee_address: device.ieeeAddr,
|
||||
type: device.zh.type,
|
||||
network_address: device.zh.networkAddress,
|
||||
supported: !!device.definition,
|
||||
supported: device.isSupported,
|
||||
friendly_name: device.name,
|
||||
disabled: !!device.options.disabled,
|
||||
description: device.options.description,
|
||||
@@ -680,7 +695,7 @@ export default class Bridge extends Extension {
|
||||
id: g.ID,
|
||||
friendly_name: g.ID === 901 ? 'default_bind_group' : g.name,
|
||||
description: g.options.description,
|
||||
scenes: this.getScenes(g.zh),
|
||||
scenes: utils.getScenes(g.zh),
|
||||
members: g.zh.members.map((e) => {
|
||||
return {ieee_address: e.getDevice().ieeeAddr, endpoint: e.ID};
|
||||
}),
|
||||
@@ -692,7 +707,9 @@ export default class Bridge extends Extension {
|
||||
|
||||
getDefinitionPayload(device: Device): DefinitionPayload {
|
||||
if (!device.definition) return null;
|
||||
let icon = device.options.icon ? device.options.icon : device.definition.icon;
|
||||
// @ts-expect-error icon is valid for external definitions
|
||||
const definitionIcon = device.definition.icon;
|
||||
let icon = device.options.icon ?? definitionIcon;
|
||||
if (icon) {
|
||||
icon = icon.replace('${zigbeeModel}', utils.sanitizeImageParameter(device.zh.modelID));
|
||||
icon = icon.replace('${model}', utils.sanitizeImageParameter(device.definition.model));
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as settings from '../util/settings';
|
||||
import utils from '../util/utils';
|
||||
import logger from '../util/logger';
|
||||
import stringify from 'json-stable-stringify-without-jsonify';
|
||||
import zhc from 'zigbee-herdsman-converters';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
import Extension from './extension';
|
||||
import bind from 'bind-decorator';
|
||||
import Device from '../model/device';
|
||||
@@ -65,7 +65,12 @@ export default class Configure extends Extension {
|
||||
|
||||
override async start(): Promise<void> {
|
||||
setImmediate(async () => {
|
||||
for (const device of this.zigbee.devices(false)) {
|
||||
// Only configure routers on startup, end devices are likely sleeping and
|
||||
// will reconfigure once they send a message
|
||||
for (const device of this.zigbee.devices(false).filter((d) => d.zh.type === 'Router')) {
|
||||
// Sleep 10 seconds between configuring on startup to not DDoS the coordinator
|
||||
// when many devices have to be configured.
|
||||
await utils.sleep(10);
|
||||
await this.configure(device, 'started');
|
||||
}
|
||||
});
|
||||
@@ -85,7 +90,7 @@ export default class Configure extends Extension {
|
||||
}
|
||||
|
||||
private async configure(device: Device, event: 'started' | 'zigbee_event' | 'reporting_disabled' | 'mqtt_message',
|
||||
force=false, thowError=false): Promise<void> {
|
||||
force=false, throwError=false): Promise<void> {
|
||||
if (!force) {
|
||||
if (device.options.disabled || !device.definition?.configure || !device.zh.interviewCompleted) {
|
||||
return;
|
||||
@@ -114,8 +119,7 @@ export default class Configure extends Extension {
|
||||
|
||||
logger.info(`Configuring '${device.name}'`);
|
||||
try {
|
||||
await device.definition.configure(device.zh, this.zigbee.firstCoordinatorEndpoint(), logger,
|
||||
device.options);
|
||||
await device.definition.configure(device.zh, this.zigbee.firstCoordinatorEndpoint(), logger);
|
||||
logger.info(`Successfully configured '${device.name}'`);
|
||||
device.zh.meta.configured = zhc.getConfigureKey(device.definition);
|
||||
device.zh.save();
|
||||
@@ -126,11 +130,11 @@ export default class Configure extends Extension {
|
||||
const msg = `Failed to configure '${device.name}', attempt ${attempt} (${error.stack})`;
|
||||
logger.error(msg);
|
||||
|
||||
if (thowError) {
|
||||
if (throwError) {
|
||||
throw error;
|
||||
}
|
||||
} finally {
|
||||
this.configuring.delete(device.ieeeAddr);
|
||||
}
|
||||
|
||||
this.configuring.delete(device.ieeeAddr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ abstract class Extension {
|
||||
protected addExtension: (extension: Extension) => Promise<void>;
|
||||
|
||||
/**
|
||||
* Besides intializing variables, the constructor should do nothing!
|
||||
* Besides initializing variables, the constructor should do nothing!
|
||||
*
|
||||
* @param {Zigbee} zigbee Zigbee controller
|
||||
* @param {MQTT} mqtt MQTT controller
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import zhc from 'zigbee-herdsman-converters';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
import * as settings from '../util/settings';
|
||||
import utils from '../util/utils';
|
||||
import {loadExternalConverter} from '../util/utils';
|
||||
import Extension from './extension';
|
||||
import logger from '../util/logger';
|
||||
|
||||
export default class ExternalConverters extends Extension {
|
||||
constructor(zigbee: Zigbee, mqtt: MQTT, state: State, publishEntityState: PublishEntityState,
|
||||
@@ -9,10 +10,23 @@ export default class ExternalConverters extends Extension {
|
||||
restartCallback: () => void, addExtension: (extension: Extension) => Promise<void>) {
|
||||
super(zigbee, mqtt, state, publishEntityState, eventBus, enableDisableExtension, restartCallback, addExtension);
|
||||
|
||||
for (const definition of utils.getExternalConvertersDefinitions(settings.get())) {
|
||||
const toAdd = {...definition};
|
||||
delete toAdd['homeassistant'];
|
||||
zhc.addDeviceDefinition(toAdd);
|
||||
for (const file of settings.get().external_converters) {
|
||||
try {
|
||||
for (const definition of loadExternalConverter(file)) {
|
||||
const toAdd = {...definition};
|
||||
delete toAdd['homeassistant'];
|
||||
zhc.addDefinition(toAdd);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Failed to load external converter file '${file}' (${error.message})`);
|
||||
logger.error(
|
||||
`Probably there is a syntax error in the file or the external converter is not ` +
|
||||
`compatible with the current Zigbee2MQTT version`);
|
||||
logger.error(
|
||||
`Note that external converters are not meant for long term usage, it's meant for local ` +
|
||||
`testing after which a pull request should be created to add out-of-the-box support for the device`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,8 @@ export default class ExternalExtension extends Extension {
|
||||
const basePath = this.getExtensionsBasePath();
|
||||
if (fs.existsSync(basePath)) {
|
||||
return fs.readdirSync(basePath).filter((f) => f.endsWith('.js')).map((fileName) => {
|
||||
const extensonFilePath = path.join(basePath, fileName);
|
||||
return {'name': fileName, 'code': fs.readFileSync(extensonFilePath, 'utf-8')};
|
||||
const extensionFilePath = path.join(basePath, fileName);
|
||||
return {'name': fileName, 'code': fs.readFileSync(extensionFilePath, 'utf-8')};
|
||||
});
|
||||
} else {
|
||||
return [];
|
||||
@@ -63,8 +63,8 @@ export default class ExternalExtension extends Extension {
|
||||
if (!fs.existsSync(basePath)) {
|
||||
fs.mkdirSync(basePath);
|
||||
}
|
||||
const extensonFilePath = path.join(basePath, path.basename(name));
|
||||
fs.writeFileSync(extensonFilePath, code);
|
||||
const extensionFilePath = path.join(basePath, path.basename(name));
|
||||
fs.writeFileSync(extensionFilePath, code);
|
||||
this.publishExtensions();
|
||||
logger.info(`Extension ${name} loaded`);
|
||||
return utils.getResponse(message, {}, null);
|
||||
|
||||
@@ -74,18 +74,29 @@ export default class Frontend extends Extension {
|
||||
this.wss = new WebSocket.Server({noServer: true});
|
||||
this.wss.on('connection', this.onWebSocketConnection);
|
||||
|
||||
this.server.listen(this.port, this.host);
|
||||
logger.info(`Started frontend on port ${this.host}:${this.port}`);
|
||||
if (!this.host) {
|
||||
this.server.listen(this.port);
|
||||
logger.info(`Started frontend on port ${this.port}`);
|
||||
} else if (this.host.startsWith('/')) {
|
||||
this.server.listen(this.host);
|
||||
logger.info(`Started frontend on socket ${this.host}`);
|
||||
} else {
|
||||
this.server.listen(this.port, this.host);
|
||||
logger.info(`Started frontend on port ${this.host}:${this.port}`);
|
||||
}
|
||||
}
|
||||
|
||||
override async stop(): Promise<void> {
|
||||
super.stop();
|
||||
for (const client of this.wss.clients) {
|
||||
this.wss?.clients.forEach((client) => {
|
||||
client.send(stringify({topic: 'bridge/state', payload: 'offline'}));
|
||||
client.terminate();
|
||||
});
|
||||
this.wss?.close();
|
||||
/* istanbul ignore else */
|
||||
if (this.server) {
|
||||
return new Promise((cb: () => void) => this.server.close(cb));
|
||||
}
|
||||
this.wss.close();
|
||||
return new Promise((cb: () => void) => this.server.close(cb));
|
||||
}
|
||||
|
||||
@bind private onRequest(request: http.IncomingMessage, response: http.ServerResponse): void {
|
||||
@@ -100,8 +111,8 @@ export default class Frontend extends Extension {
|
||||
|
||||
@bind private onUpgrade(request: http.IncomingMessage, socket: net.Socket, head: Buffer): void {
|
||||
this.wss.handleUpgrade(request, socket, head, (ws) => {
|
||||
this.authenticate(request, (isAuthentificated) => {
|
||||
if (isAuthentificated) {
|
||||
this.authenticate(request, (isAuthenticated) => {
|
||||
if (isAuthenticated) {
|
||||
this.wss.emit('connection', ws, request);
|
||||
} else {
|
||||
ws.close(4401, 'Unauthorized');
|
||||
@@ -111,11 +122,12 @@ export default class Frontend extends Extension {
|
||||
}
|
||||
|
||||
@bind private onWebSocketConnection(ws: WebSocket): void {
|
||||
ws.on('error', (msg) => logger.error(`WebSocket error: ${msg.message}`));
|
||||
ws.on('message', (data: Buffer, isBinary: boolean) => {
|
||||
if (!isBinary && data) {
|
||||
const message = data.toString();
|
||||
const {topic, payload} = JSON.parse(message);
|
||||
this.mqtt.onMessage(`${this.mqttBaseTopic}/${topic}`, stringify(payload));
|
||||
this.mqtt.onMessage(`${this.mqttBaseTopic}/${topic}`, Buffer.from(stringify(payload)));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+24
-11
@@ -7,13 +7,14 @@ import bind from 'bind-decorator';
|
||||
import Extension from './extension';
|
||||
import Device from '../model/device';
|
||||
import Group from '../model/group';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
|
||||
const topicRegex =
|
||||
new RegExp(`^${settings.get().mqtt.base_topic}/bridge/request/group/members/(remove|add|remove_all)$`);
|
||||
const legacyTopicRegex = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/(.+)/(remove|add|remove_all)$`);
|
||||
const legacyTopicRegexRemoveAll = new RegExp(`^${settings.get().mqtt.base_topic}/bridge/group/remove_all$`);
|
||||
|
||||
const stateProperties: {[s: string]: (value: string, exposes: zhc.DefinitionExpose[]) => boolean} = {
|
||||
const stateProperties: {[s: string]: (value: string, exposes: zhc.Expose[]) => boolean} = {
|
||||
'state': () => true,
|
||||
'brightness': (value, exposes) =>
|
||||
!!exposes.find((e) => e.type === 'light' && e.features.find((f) => f.name === 'brightness')),
|
||||
@@ -67,10 +68,13 @@ export default class Groups extends Extension {
|
||||
const groupID = settingGroup.ID;
|
||||
const zigbeeGroup = zigbeeGroups.find((g) => g.ID === groupID) || this.zigbee.createGroup(groupID);
|
||||
const settingsEndpoint = settingGroup.devices.map((d) => {
|
||||
const parsed = utils.parseEntityID(d);
|
||||
const entity = this.zigbee.resolveEntity(parsed.ID) as Device;
|
||||
const parsed = this.zigbee.resolveEntityAndEndpoint(d);
|
||||
const entity = parsed.entity as Device;
|
||||
if (!entity) logger.error(`Cannot find '${d}' of group '${settingGroup.friendly_name}'`);
|
||||
return {'endpoint': entity?.endpoint(parsed.endpoint), 'name': entity?.name};
|
||||
if (parsed.endpointID && !parsed.endpoint) {
|
||||
logger.error(`Cannot find endpoint '${parsed.endpointID}' of device '${parsed.ID}'`);
|
||||
}
|
||||
return {'endpoint': parsed.endpoint, 'name': entity?.name};
|
||||
}).filter((e) => e.endpoint != null);
|
||||
|
||||
// In settings but not in zigbee
|
||||
@@ -108,8 +112,9 @@ export default class Groups extends Extension {
|
||||
const payload: KeyValue = {};
|
||||
|
||||
let endpointName: string = null;
|
||||
const endpointNames: string[] = data.entity instanceof Device ? data.entity.getEndpointNames() : [];
|
||||
for (let [prop, value] of Object.entries(data.update)) {
|
||||
const endpointNameMatch = utils.endpointNames.find((n) => prop.endsWith(`_${n}`));
|
||||
const endpointNameMatch = endpointNames.find((n) => prop.endsWith(`_${n}`));
|
||||
if (endpointNameMatch) {
|
||||
prop = prop.substring(0, prop.length - endpointNameMatch.length - 1);
|
||||
endpointName = endpointNameMatch;
|
||||
@@ -236,8 +241,8 @@ export default class Groups extends Extension {
|
||||
type = 'remove_all';
|
||||
}
|
||||
|
||||
const parsedEntity = utils.parseEntityID(data.message);
|
||||
resolvedEntityDevice = this.zigbee.resolveEntity(parsedEntity.ID) as Device;
|
||||
const parsedEntity = this.zigbee.resolveEntityAndEndpoint(data.message);
|
||||
resolvedEntityDevice = parsedEntity.entity as Device;
|
||||
if (!resolvedEntityDevice || !(resolvedEntityDevice instanceof Device)) {
|
||||
logger.error(`Device '${data.message}' does not exist`);
|
||||
|
||||
@@ -254,7 +259,12 @@ export default class Groups extends Extension {
|
||||
|
||||
return null;
|
||||
}
|
||||
resolvedEntityEndpoint = resolvedEntityDevice.endpoint(parsedEntity.endpoint);
|
||||
|
||||
resolvedEntityEndpoint = parsedEntity.endpoint;
|
||||
if (parsedEntity.endpointID && !resolvedEntityEndpoint) {
|
||||
logger.error(`Device '${parsedEntity.ID}' does not have endpoint '${parsedEntity.endpointID}'`);
|
||||
return null;
|
||||
}
|
||||
} else if (topicRegexMatch) {
|
||||
type = topicRegexMatch[1] as 'remove' | 'add' | 'remove_all';
|
||||
const message = JSON.parse(data.message);
|
||||
@@ -269,13 +279,16 @@ export default class Groups extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
const parsed = utils.parseEntityID(message.device);
|
||||
resolvedEntityDevice = this.zigbee.resolveEntity(parsed.ID) as Device;
|
||||
const parsed = this.zigbee.resolveEntityAndEndpoint(message.device);
|
||||
resolvedEntityDevice = parsed?.entity as Device;
|
||||
if (!error && (!resolvedEntityDevice || !(resolvedEntityDevice instanceof Device))) {
|
||||
error = `Device '${message.device}' does not exist`;
|
||||
}
|
||||
if (!error) {
|
||||
resolvedEntityEndpoint = resolvedEntityDevice.endpoint(parsed.endpoint);
|
||||
resolvedEntityEndpoint = parsed.endpoint;
|
||||
if (parsed.endpointID && !resolvedEntityEndpoint) {
|
||||
error = `Device '${parsed.ID}' does not have endpoint '${parsed.endpointID}'`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+589
-113
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
import * as settings from '../../util/settings';
|
||||
import logger from '../../util/logger';
|
||||
import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
|
||||
import utils from '../../util/utils';
|
||||
import assert from 'assert';
|
||||
import Extension from '../extension';
|
||||
@@ -90,7 +89,7 @@ export default class BridgeLegacy extends Extension {
|
||||
@bind async reset(): Promise<void> {
|
||||
try {
|
||||
await this.zigbee.reset('soft');
|
||||
logger.info('Soft resetted ZNP');
|
||||
logger.info('Soft reset ZNP');
|
||||
} catch (error) {
|
||||
logger.error('Soft reset failed');
|
||||
}
|
||||
@@ -140,7 +139,7 @@ export default class BridgeLegacy extends Extension {
|
||||
};
|
||||
|
||||
if (device.zh.type !== 'Coordinator') {
|
||||
const definition = zigbeeHerdsmanConverters.findByDevice(device.zh);
|
||||
const definition = device.definition;
|
||||
payload.model = definition ? definition.model : device.zh.modelID;
|
||||
payload.vendor = definition ? definition.vendor : '-';
|
||||
payload.description = definition ? definition.description : '-';
|
||||
@@ -321,7 +320,7 @@ export default class BridgeLegacy extends Extension {
|
||||
try {
|
||||
logger.info(`${lookup[action][1]} '${entity.name}'`);
|
||||
if (action === 'force_remove') {
|
||||
await entity.zh.removeFromDatabase();
|
||||
entity.zh.removeFromDatabase();
|
||||
} else {
|
||||
await entity.zh.removeFromNetwork();
|
||||
}
|
||||
@@ -385,7 +384,7 @@ export default class BridgeLegacy extends Extension {
|
||||
);
|
||||
} else if (type === 'deviceInterview') {
|
||||
if (data.status === 'successful') {
|
||||
if (resolvedEntity.definition) {
|
||||
if (resolvedEntity.isSupported) {
|
||||
const {vendor, description, model} = resolvedEntity.definition;
|
||||
const log = {friendly_name: resolvedEntity.name, model, vendor, description, supported: true};
|
||||
this.mqtt.publish(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* istanbul ignore file */
|
||||
import * as settings from '../../util/settings';
|
||||
import logger from '../../util/logger';
|
||||
import utils from '../../util/utils';
|
||||
import Extension from '../extension';
|
||||
import bind from 'bind-decorator';
|
||||
import Device from '../../model/device';
|
||||
@@ -19,13 +18,19 @@ export default class DeviceGroupMembership extends Extension {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parsed = utils.parseEntityID(match[1]);
|
||||
const device = this.zigbee.resolveEntity(parsed.ID) as Device;
|
||||
const parsed = this.zigbee.resolveEntityAndEndpoint(match[1]);
|
||||
const device = parsed?.entity as Device;
|
||||
if (!device || !(device instanceof Device)) {
|
||||
logger.error(`Device '${match[1]}' does not exist`);
|
||||
return;
|
||||
}
|
||||
const endpoint = device.endpoint(parsed.endpoint);
|
||||
|
||||
const endpoint = parsed.endpoint;
|
||||
if (parsed.endpointID && !endpoint) {
|
||||
logger.error(`Device '${parsed.ID}' does not have endpoint '${parsed.endpointID}'`);
|
||||
return;
|
||||
}
|
||||
|
||||
const response = await endpoint.command(
|
||||
`genGroups`, 'getMembership', {groupcount: 0, grouplist: []}, {},
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
import logger from '../../util/logger';
|
||||
import * as settings from '../../util/settings';
|
||||
import Extension from '../extension';
|
||||
@@ -7,14 +7,14 @@ const defaultConfiguration = {
|
||||
minimumReportInterval: 3, maximumReportInterval: 300, reportableChange: 1,
|
||||
};
|
||||
|
||||
const ZNLDP12LM = zigbeeHerdsmanConverters.definitions.find((d) => d.model === 'ZNLDP12LM');
|
||||
const ZNLDP12LM = zhc.definitions.find((d) => d.model === 'ZNLDP12LM');
|
||||
|
||||
const devicesNotSupportingReporting = [
|
||||
zigbeeHerdsmanConverters.definitions.find((d) => d.model === 'CC2530.ROUTER'),
|
||||
zigbeeHerdsmanConverters.definitions.find((d) => d.model === 'BASICZBR3'),
|
||||
zigbeeHerdsmanConverters.definitions.find((d) => d.model === 'ZM-CSW032-D'),
|
||||
zigbeeHerdsmanConverters.definitions.find((d) => d.model === 'TS0001'),
|
||||
zigbeeHerdsmanConverters.definitions.find((d) => d.model === 'TS0115'),
|
||||
zhc.definitions.find((d) => d.model === 'CC2530.ROUTER'),
|
||||
zhc.definitions.find((d) => d.model === 'BASICZBR3'),
|
||||
zhc.definitions.find((d) => d.model === 'ZM-CSW032-D'),
|
||||
zhc.definitions.find((d) => d.model === 'TS0001'),
|
||||
zhc.definitions.find((d) => d.model === 'TS0115'),
|
||||
];
|
||||
|
||||
const reportKey = 1;
|
||||
|
||||
@@ -9,7 +9,7 @@ import Extension from '../extension';
|
||||
* This extensions soft resets the ZNP after a certain timeout.
|
||||
*/
|
||||
export default class SoftReset extends Extension {
|
||||
private timer: NodeJS.Timer = null;
|
||||
private timer: NodeJS.Timeout = null;
|
||||
private timeout = utils.seconds(settings.get().advanced.soft_reset_timeout);
|
||||
|
||||
override async start(): Promise<void> {
|
||||
@@ -43,7 +43,7 @@ export default class SoftReset extends Extension {
|
||||
|
||||
try {
|
||||
await this.zigbee.reset('soft');
|
||||
logger.warn('Soft resetted ZNP due to timeout');
|
||||
logger.warn('Soft reset ZNP due to timeout');
|
||||
} catch (error) {
|
||||
logger.warn('Soft reset failed, trying stop/start');
|
||||
|
||||
|
||||
@@ -99,12 +99,7 @@ export default class NetworkMap extends Extension {
|
||||
|
||||
// Add the device model
|
||||
if (node.type !== 'Coordinator') {
|
||||
if (node.definition) {
|
||||
labels.push(`${node.definition.vendor} ${node.definition.description} (${node.definition.model})`);
|
||||
} else {
|
||||
// This model is not supported by zigbee-herdsman-converters, add zigbee model information
|
||||
labels.push(`${node.manufacturerName} ${node.modelID}`);
|
||||
}
|
||||
labels.push(`${node.definition?.vendor} ${node.definition?.description} (${node.definition?.model})`);
|
||||
}
|
||||
|
||||
// Add the device last_seen timestamp
|
||||
@@ -177,12 +172,7 @@ export default class NetworkMap extends Extension {
|
||||
if (node.type !== 'Coordinator') {
|
||||
text.push(`---`);
|
||||
const definition = (this.zigbee.resolveEntity(node.ieeeAddr) as Device).definition;
|
||||
if (definition) {
|
||||
text.push(`${definition.vendor} ${definition.description} (${definition.model})`);
|
||||
} else {
|
||||
// This model is not supported by zigbee-herdsman-converters, add zigbee model information
|
||||
text.push(`${node.manufacturerName} ${node.modelID}`);
|
||||
}
|
||||
text.push(`${definition?.vendor} ${definition?.description} (${definition?.model})`);
|
||||
}
|
||||
|
||||
// Add the device last_seen timestamp
|
||||
@@ -269,8 +259,7 @@ export default class NetworkMap extends Extension {
|
||||
vendor: device.definition.vendor,
|
||||
description: device.definition.description,
|
||||
supports: Array.from(new Set((device.exposes()).map((e) => {
|
||||
return e.hasOwnProperty('name') ? e.name :
|
||||
`${e.type} (${e.features.map((f) => f.name).join(', ')})`;
|
||||
return e.name ?? `${e.type} (${e.features.map((f) => f.name).join(', ')})`;
|
||||
}))).join(', '),
|
||||
} : null;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import zhc from 'zigbee-herdsman-converters';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
import Extension from './extension';
|
||||
|
||||
/**
|
||||
@@ -39,12 +39,13 @@ export default class OnEvent extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
private async callOnEvent(device: Device, type: string, data: KeyValue): Promise<void> {
|
||||
private async callOnEvent(device: Device, type: zhc.OnEventType, data: KeyValue): Promise<void> {
|
||||
const state = this.state.get(device);
|
||||
zhc.onEvent(type, data, device.zh, device.options, state);
|
||||
zhc.onEvent(type, data, device.zh);
|
||||
|
||||
if (device.definition?.onEvent) {
|
||||
await device.definition.onEvent(type, data, device.zh, device.options, state);
|
||||
const options: KeyValue = device.options;
|
||||
await device.definition.onEvent(type, data, device.zh, options, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-16
@@ -2,14 +2,13 @@ import * as settings from '../util/settings';
|
||||
import logger from '../util/logger';
|
||||
import stringify from 'json-stable-stringify-without-jsonify';
|
||||
import utils from '../util/utils';
|
||||
import tradfriOTA from 'zigbee-herdsman-converters/lib/ota/tradfri';
|
||||
import zigbeeOTA from 'zigbee-herdsman-converters/lib/ota/zigbeeOTA';
|
||||
import Extension from './extension';
|
||||
import bind from 'bind-decorator';
|
||||
import Device from '../model/device';
|
||||
import dataDir from '../util/data';
|
||||
import * as URI from 'uri-js';
|
||||
import path from 'path';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
|
||||
function isValidUrl(url: string): boolean {
|
||||
let parsed;
|
||||
@@ -45,7 +44,7 @@ export default class OTAUpdate extends Extension {
|
||||
this.eventBus.onMQTTMessage(this, this.onMQTTMessage);
|
||||
this.eventBus.onDeviceMessage(this, this.onZigbeeEvent);
|
||||
if (settings.get().ota.ikea_ota_use_test_url) {
|
||||
tradfriOTA.useTestURL();
|
||||
zhc.ota.tradfri.useTestURL();
|
||||
}
|
||||
|
||||
// Let zigbeeOTA module know if the override index file is provided
|
||||
@@ -56,16 +55,20 @@ export default class OTAUpdate extends Extension {
|
||||
overrideOTAIndex = dataDir.joinPath(overrideOTAIndex);
|
||||
}
|
||||
|
||||
zigbeeOTA.useIndexOverride(overrideOTAIndex);
|
||||
zhc.ota.zigbeeOTA.useIndexOverride(overrideOTAIndex);
|
||||
}
|
||||
|
||||
// In order to support local firmware files we need to let zigbeeOTA know where the data directory is
|
||||
zigbeeOTA.setDataDir(dataDir.getPath());
|
||||
zhc.ota.setDataDir(dataDir.getPath());
|
||||
|
||||
// In case Zigbee2MQTT is restared during an update, progress and remaining values are still in state.
|
||||
// remove them.
|
||||
for (const device of this.zigbee.devices(false)) {
|
||||
this.removeProgressAndRemainingFromState(device);
|
||||
// Reset update state, e.g. when Z2M restarted during update.
|
||||
if (this.state.get(device).update?.state === 'updating') {
|
||||
this.state.get(device).update.state = 'available';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +83,7 @@ export default class OTAUpdate extends Extension {
|
||||
logger.debug(`Device '${data.device.name}' requested OTA`);
|
||||
|
||||
const automaticOTACheckDisabled = settings.get().ota.disable_automatic_update_check;
|
||||
let supportsOTA = data.device.definition.hasOwnProperty('ota');
|
||||
let supportsOTA = !!data.device.definition.ota;
|
||||
if (supportsOTA && !automaticOTACheckDisabled) {
|
||||
// When a device does a next image request, it will usually do it a few times after each other
|
||||
// with only 10 - 60 seconds inbetween. It doesn't make sense to check for a new update
|
||||
@@ -93,7 +96,9 @@ export default class OTAUpdate extends Extension {
|
||||
this.lastChecked[data.device.ieeeAddr] = Date.now();
|
||||
let availableResult: zhc.OtaUpdateAvailableResult = null;
|
||||
try {
|
||||
availableResult = await data.device.definition.ota.isUpdateAvailable(data.device.zh, logger, data.data);
|
||||
// @ts-expect-error typing guaranteed by data.type
|
||||
const dataData: zhc.ota.ImageInfo = data.data;
|
||||
availableResult = await data.device.definition.ota.isUpdateAvailable(data.device.zh, logger, dataData);
|
||||
} catch (e) {
|
||||
supportsOTA = false;
|
||||
logger.debug(`Failed to check if update available for '${data.device.name}' (${e.message})`);
|
||||
@@ -118,18 +123,17 @@ export default class OTAUpdate extends Extension {
|
||||
}
|
||||
|
||||
// Respond to the OTA request: respond with NO_IMAGE_AVAILABLE (0x98) (so the client stops requesting OTAs)
|
||||
const endpoint = data.device.zh.endpoints.find((e) => e.supportsOutputCluster('genOta'));
|
||||
if (endpoint) {
|
||||
// Some devices send OTA requests without defining OTA cluster as input cluster.
|
||||
await endpoint.commandResponse('genOta', 'queryNextImageResponse', {status: 0x98});
|
||||
}
|
||||
const endpoint = data.device.zh.endpoints.find((e) => e.supportsOutputCluster('genOta')) || data.endpoint;
|
||||
await endpoint.commandResponse('genOta', 'queryNextImageResponse',
|
||||
{status: 0x98}, undefined, data.meta.zclTransactionSequenceNumber);
|
||||
logger.debug(`Responded to OTA request of '${data.device.name}' with 'NO_IMAGE_AVAILABLE'`);
|
||||
}
|
||||
|
||||
private async readSoftwareBuildIDAndDateCode(device: Device, sendWhen: 'active' | 'immediate'):
|
||||
private async readSoftwareBuildIDAndDateCode(device: Device, sendPolicy?: 'immediate'):
|
||||
Promise<{softwareBuildID: string, dateCode: string}> {
|
||||
try {
|
||||
const endpoint = device.zh.endpoints.find((e) => e.supportsInputCluster('genBasic'));
|
||||
const result = await endpoint.read('genBasic', ['dateCode', 'swBuildId'], {sendWhen});
|
||||
const result = await endpoint.read('genBasic', ['dateCode', 'swBuildId'], {sendPolicy});
|
||||
return {softwareBuildID: result.swBuildId, dateCode: result.dateCode};
|
||||
} catch (e) {
|
||||
return null;
|
||||
@@ -202,7 +206,7 @@ export default class OTAUpdate extends Extension {
|
||||
}
|
||||
|
||||
try {
|
||||
const availableResult = await device.definition.ota.isUpdateAvailable(device.zh, logger);
|
||||
const availableResult = await device.definition.ota.isUpdateAvailable(device.zh, logger, null);
|
||||
const msg = `${availableResult.available ? 'Update' : 'No update'} available for '${device.name}'`;
|
||||
logger.info(msg);
|
||||
|
||||
@@ -273,7 +277,7 @@ export default class OTAUpdate extends Extension {
|
||||
const payload = this.getEntityPublishPayload(device,
|
||||
{available: false, currentFileVersion: fileVersion, otaFileVersion: fileVersion});
|
||||
this.publishEntityState(device, payload);
|
||||
const to = await this.readSoftwareBuildIDAndDateCode(device, 'active');
|
||||
const to = await this.readSoftwareBuildIDAndDateCode(device);
|
||||
const [fromS, toS] = [stringify(from_), stringify(to)];
|
||||
logger.info(`Device '${device.name}' was updated from '${fromS}' to '${toS}'`);
|
||||
responseData.from = from_ ? utils.toSnakeCase(from_) : null;
|
||||
|
||||
+60
-47
@@ -1,6 +1,7 @@
|
||||
|
||||
import * as settings from '../util/settings';
|
||||
import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
import * as philips from 'zigbee-herdsman-converters/lib/philips';
|
||||
import logger from '../util/logger';
|
||||
import utils from '../util/utils';
|
||||
import Extension from './extension';
|
||||
@@ -9,26 +10,25 @@ import Group from '../model/group';
|
||||
import Device from '../model/device';
|
||||
import bind from 'bind-decorator';
|
||||
|
||||
const topicRegex = new RegExp(`^(.+?)(?:/(${utils.endpointNames.join('|')}|\\d+))?/(get|set)(?:/(.+))?`);
|
||||
const propertyEndpointRegex = new RegExp(`^(.*?)_(${utils.endpointNames.join('|')})$`);
|
||||
const topicGetSetRegex = new RegExp(`^(.+?)/(get|set)(?:/(.+))?`);
|
||||
const stateValues = ['on', 'off', 'toggle', 'open', 'close', 'stop', 'lock', 'unlock'];
|
||||
const sceneConverterKeys = ['scene_store', 'scene_add', 'scene_remove', 'scene_remove_all'];
|
||||
const sceneConverterKeys = ['scene_store', 'scene_add', 'scene_remove', 'scene_remove_all', 'scene_rename'];
|
||||
|
||||
// Legacy: don't provide default converters anymore, this is required by older z2m installs not saving group members
|
||||
const defaultGroupConverters = [
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.light_onoff_brightness,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.light_color_colortemp,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.effect,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.ignore_transition,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.cover_position_tilt,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.thermostat_occupied_heating_setpoint,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.tint_scene,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.light_brightness_move,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.light_brightness_step,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.light_colortemp_step,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.light_colortemp_move,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.light_hue_saturation_move,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.light_hue_saturation_step,
|
||||
zhc.toZigbee.light_onoff_brightness,
|
||||
zhc.toZigbee.light_color_colortemp,
|
||||
philips.tz.effect, // Support Hue effects for groups
|
||||
zhc.toZigbee.ignore_transition,
|
||||
zhc.toZigbee.cover_position_tilt,
|
||||
zhc.toZigbee.thermostat_occupied_heating_setpoint,
|
||||
zhc.toZigbee.tint_scene,
|
||||
zhc.toZigbee.light_brightness_move,
|
||||
zhc.toZigbee.light_brightness_step,
|
||||
zhc.toZigbee.light_colortemp_step,
|
||||
zhc.toZigbee.light_colortemp_move,
|
||||
zhc.toZigbee.light_hue_saturation_move,
|
||||
zhc.toZigbee.light_hue_saturation_step,
|
||||
];
|
||||
|
||||
interface ParsedTopic {ID: string, endpoint: string, attribute: string, type: 'get' | 'set'}
|
||||
@@ -39,18 +39,30 @@ export default class Publish extends Extension {
|
||||
}
|
||||
|
||||
parseTopic(topic: string): ParsedTopic | null {
|
||||
const match = topic.match(topicRegex);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
// The function supports the following topic formats (below are for 'set'. 'get' will look the same):
|
||||
// - <base_topic>/device_name/set (endpoint and attribute is defined in the payload)
|
||||
// - <base_topic>/device_name/set/attribute (default endpoint used)
|
||||
// - <base_topic>/device_name/endpoint/set (attribute is defined in the payload)
|
||||
// - <base_topic>/device_name/endpoint/set/attribute (payload is the value)
|
||||
|
||||
const ID = match[1].replace(`${settings.get().mqtt.base_topic}/`, '');
|
||||
// If we didn't replace base_topic we received something we don't care about
|
||||
if (ID === match[1] || ID.match(/bridge/)) {
|
||||
return null;
|
||||
}
|
||||
// The first step is to get rid of base topic part
|
||||
topic = topic.replace(`${settings.get().mqtt.base_topic}/`, '');
|
||||
|
||||
return {ID: ID, endpoint: match[2], type: match[3] as 'get' | 'set', attribute: match[4]};
|
||||
// Also bridge requests are something we don't care about
|
||||
if (topic.match(/bridge/)) return null;
|
||||
|
||||
// Make the rough split on get/set keyword.
|
||||
// Before the get/set is the device name and optional endpoint name.
|
||||
// After it there will be an optional attribute name.
|
||||
const match = topic.match(topicGetSetRegex);
|
||||
if (!match) return null;
|
||||
|
||||
const deviceNameAndEndpoint = match[1];
|
||||
const attribute = match[3];
|
||||
|
||||
// Now parse the device/group name, and endpoint name
|
||||
const entity = this.zigbee.resolveEntityAndEndpoint(deviceNameAndEndpoint);
|
||||
return {ID: entity.ID, endpoint: entity.endpointID, type: match[2] as 'get' | 'set', attribute: attribute};
|
||||
}
|
||||
|
||||
parseMessage(parsedTopic: ParsedTopic, data: eventdata.MQTTMessage): KeyValue | null {
|
||||
@@ -80,8 +92,8 @@ export default class Publish extends Extension {
|
||||
}
|
||||
}
|
||||
|
||||
legacyRetrieveState(re: Device | Group, converter: zhc.ToZigbeeConverter, result: zhc.ToZigbeeConverterResult,
|
||||
target: zh.Endpoint | zh.Group, key: string, meta: zhc.ToZigbeeConverterGetMeta): void {
|
||||
legacyRetrieveState(re: Device | Group, converter: zhc.Tz.Converter, result: zhc.Tz.ConvertSetResult,
|
||||
target: zh.Endpoint | zh.Group, key: string, meta: zhc.Tz.Meta): void {
|
||||
// It's possible for devices to get out of sync when writing an attribute that's not reportable.
|
||||
// So here we re-read the value after a specified timeout, this timeout could for example be the
|
||||
// transition time of a color change or for forcing a state read for devices that don't
|
||||
@@ -89,7 +101,7 @@ export default class Publish extends Extension {
|
||||
// When reporting is requested for a device (report: true in device-specific settings) we won't
|
||||
// ever issue a read here, as we assume the device will properly report changes.
|
||||
// Only do this when the retrieve_state option is enabled for this device.
|
||||
// retrieve_state == decprecated
|
||||
// retrieve_state == deprecated
|
||||
if (re instanceof Device && result && result.hasOwnProperty('readAfterWriteTime') &&
|
||||
re.options.retrieve_state
|
||||
) {
|
||||
@@ -101,7 +113,7 @@ export default class Publish extends Extension {
|
||||
/**
|
||||
* Home Assistant always publishes 'state', even when e.g. only setting
|
||||
* the color temperature. This would lead to 2 zigbee publishes, where the first one
|
||||
* (state) is probably unecessary.
|
||||
* (state) is probably unnecessary.
|
||||
*/
|
||||
if (settings.get().homeassistant) {
|
||||
const hasColorTemp = message.hasOwnProperty('color_temp');
|
||||
@@ -139,17 +151,14 @@ export default class Publish extends Extension {
|
||||
const membersState = re instanceof Group ?
|
||||
Object.fromEntries(re.zh.members.map((e) => [e.getDevice().ieeeAddr,
|
||||
this.state.get(this.zigbee.resolveEntity(e.getDevice().ieeeAddr))])) : null;
|
||||
let converters: zhc.ToZigbeeConverter[];
|
||||
let converters: zhc.Tz.Converter[];
|
||||
{
|
||||
if (Array.isArray(definition)) {
|
||||
const c = new Set(definition.map((d) => d.toZigbee).flat());
|
||||
if (c.size == 0) converters = defaultGroupConverters;
|
||||
else converters = Array.from(c);
|
||||
} else if (definition) {
|
||||
converters = definition.toZigbee;
|
||||
} else {
|
||||
converters = [zigbeeHerdsmanConverters.toZigbeeConverters.read,
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.write];
|
||||
converters = definition.toZigbee;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +174,7 @@ export default class Publish extends Extension {
|
||||
* Order state & brightness based on current bulb state
|
||||
*
|
||||
* Not all bulbs support setting the color/color_temp while it is off
|
||||
* this results in inconsistant behavior between different vendors.
|
||||
* this results in inconsistent behavior between different vendors.
|
||||
*
|
||||
* bulb on => move state & brightness to the back
|
||||
* bulb off => move state & brightness to the front
|
||||
@@ -175,7 +184,7 @@ export default class Publish extends Extension {
|
||||
entries.sort((a) => (['state', 'brightness', 'brightness_percent'].includes(a[0]) ? sorter : sorter * -1));
|
||||
|
||||
// For each attribute call the corresponding converter
|
||||
const usedConverters: {[s: number]: zhc.ToZigbeeConverter[]} = {};
|
||||
const usedConverters: {[s: number]: zhc.Tz.Converter[]} = {};
|
||||
const toPublish: {[s: number | string]: KeyValue} = {};
|
||||
const toPublishEntity: {[s: number | string]: Device | Group} = {};
|
||||
const addToToPublish = (entity: Device | Group, payload: KeyValue): void => {
|
||||
@@ -187,6 +196,9 @@ export default class Publish extends Extension {
|
||||
toPublish[ID] = {...toPublish[ID], ...payload};
|
||||
};
|
||||
|
||||
const endpointNames = re instanceof Device ? re.getEndpointNames() : [];
|
||||
const propertyEndpointRegex = new RegExp(`^(.*?)_(${endpointNames.join('|')})$`);
|
||||
|
||||
for (let [key, value] of entries) {
|
||||
let endpointName = parsedTopic.endpoint;
|
||||
let localTarget = target;
|
||||
@@ -198,15 +210,13 @@ export default class Publish extends Extension {
|
||||
endpointName = propertyEndpointMatch[2];
|
||||
key = propertyEndpointMatch[1];
|
||||
localTarget = re.endpoint(endpointName);
|
||||
if (localTarget == null) {
|
||||
logger.error(`Device '${re.name}' has no endpoint '${endpointName}'`);
|
||||
continue;
|
||||
}
|
||||
endpointOrGroupID = localTarget.ID;
|
||||
}
|
||||
|
||||
if (!usedConverters.hasOwnProperty(endpointOrGroupID)) usedConverters[endpointOrGroupID] = [];
|
||||
const converter = converters.find((c) => c.key.includes(key));
|
||||
/* istanbul ignore next */
|
||||
const converter = converters.find((c) =>
|
||||
c.key.includes(key) && (!c.endpoint || c.endpoint == endpointName));
|
||||
|
||||
if (parsedTopic.type === 'set' && usedConverters[endpointOrGroupID].includes(converter)) {
|
||||
// Use a converter for set only once
|
||||
@@ -219,15 +229,18 @@ export default class Publish extends Extension {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the endpoint_name name is a nubmer, try to map it to a friendlyName
|
||||
// If the endpoint_name name is a number, try to map it to a friendlyName
|
||||
if (!isNaN(Number(endpointName)) && re.isDevice() && utils.isEndpoint(localTarget) &&
|
||||
re.endpointName(localTarget)) {
|
||||
endpointName = re.endpointName(localTarget);
|
||||
}
|
||||
|
||||
// Converter didn't return a result, skip
|
||||
const meta = {endpoint_name: endpointName, options: entitySettings, message: {...message}, logger, device,
|
||||
state: entityState, membersState, mapped: definition};
|
||||
const entitySettingsKeyValue: KeyValue = entitySettings;
|
||||
const meta = {
|
||||
endpoint_name: endpointName, options: entitySettingsKeyValue,
|
||||
message: {...message}, logger, device, state: entityState, membersState, mapped: definition,
|
||||
};
|
||||
|
||||
// Strip endpoint name from meta.message properties.
|
||||
if (endpointName) {
|
||||
@@ -295,7 +308,7 @@ export default class Publish extends Extension {
|
||||
const scenesChanged = Object.values(usedConverters)
|
||||
.some((cl) => cl.some((c) => c.key.some((k) => sceneConverterKeys.includes(k))));
|
||||
if (scenesChanged) {
|
||||
this.eventBus.emitScenesChanged();
|
||||
this.eventBus.emitScenesChanged({entity: re});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-13
@@ -5,6 +5,7 @@ import Extension from './extension';
|
||||
import stringify from 'json-stable-stringify-without-jsonify';
|
||||
import bind from 'bind-decorator';
|
||||
import utils from '../util/utils';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
|
||||
type DebounceFunction = (() => void) & { clear(): void; } & { flush(): void; };
|
||||
|
||||
@@ -49,6 +50,13 @@ export default class Receive extends Extension {
|
||||
|
||||
// extend debounced payload with current
|
||||
this.debouncers[device.ieeeAddr].payload = {...this.debouncers[device.ieeeAddr].payload, ...payload};
|
||||
|
||||
// Update state cache right away. This makes sure that during debouncing cached state is always up to date.
|
||||
// ( Update right away as "lastSeenChanged" event might occur while debouncer is still active.
|
||||
// And if that happens it would cause old message to be published from cache.
|
||||
// By updating cache we make sure that state cache is always up-to-date.
|
||||
this.state.set(device, this.debouncers[device.ieeeAddr].payload);
|
||||
|
||||
this.debouncers[device.ieeeAddr].publish();
|
||||
}
|
||||
|
||||
@@ -70,17 +78,8 @@ export default class Receive extends Extension {
|
||||
}
|
||||
|
||||
shouldProcess(data: eventdata.DeviceMessage): boolean {
|
||||
if (!data.device.definition) {
|
||||
if (data.device.zh.interviewing) {
|
||||
logger.debug(`Skipping message, definition is undefined and still interviewing`);
|
||||
} else {
|
||||
logger.warn(
|
||||
`Received message from unsupported device with Zigbee model '${data.device.zh.modelID}' ` +
|
||||
`and manufacturer name '${data.device.zh.manufacturerName}'`);
|
||||
// eslint-disable-next-line max-len
|
||||
logger.warn(`Please see: https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html`);
|
||||
}
|
||||
|
||||
if (!data.device.definition || data.device.zh.interviewing) {
|
||||
logger.debug(`Skipping message, still interviewing`);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -118,6 +117,9 @@ export default class Receive extends Extension {
|
||||
// - If NO payload is returned do nothing. This is for non-standard behaviour
|
||||
// for e.g. click switches where we need to count number of clicks and detect long presses.
|
||||
const publish = (payload: KeyValue): void => {
|
||||
const options: KeyValue = data.device.options;
|
||||
zhc.postProcessConvertedFromZigbeeMessage(data.device.definition, payload, options, logger);
|
||||
|
||||
if (settings.get().advanced.elapsed) {
|
||||
const now = Date.now();
|
||||
if (this.elapsed[data.device.ieeeAddr]) {
|
||||
@@ -136,12 +138,20 @@ export default class Receive extends Extension {
|
||||
}
|
||||
};
|
||||
|
||||
const meta = {device: data.device.zh, logger, state: this.state.get(data.device)};
|
||||
const deviceExposesChanged = (): void => {
|
||||
this.eventBus.emitDevicesChanged();
|
||||
this.eventBus.emitExposesChanged({device: data.device});
|
||||
};
|
||||
|
||||
const meta = {device: data.device.zh, logger, state: this.state.get(data.device),
|
||||
deviceExposesChanged: deviceExposesChanged};
|
||||
let payload: KeyValue = {};
|
||||
for (const converter of converters) {
|
||||
try {
|
||||
const convertData = {...data, device: data.device.zh};
|
||||
const options: KeyValue = data.device.options;
|
||||
const converted = await converter.convert(
|
||||
data.device.definition, data, publish, data.device.options, meta);
|
||||
data.device.definition, convertData, publish, options, meta);
|
||||
if (converted) {
|
||||
payload = {...payload, ...converted};
|
||||
}
|
||||
|
||||
+26
-15
@@ -1,10 +1,10 @@
|
||||
/* eslint-disable brace-style */
|
||||
import * as settings from '../util/settings';
|
||||
import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
|
||||
export default class Device {
|
||||
public zh: zh.Device;
|
||||
private _definition: zhc.Definition;
|
||||
public definition: zhc.Definition;
|
||||
private _definitionModelID: string;
|
||||
|
||||
get ieeeAddr(): string {return this.zh.ieeeAddr;}
|
||||
@@ -13,29 +13,31 @@ export default class Device {
|
||||
get name(): string {
|
||||
return this.zh.type === 'Coordinator' ? 'Coordinator' : this.options?.friendly_name || this.ieeeAddr;
|
||||
}
|
||||
get definition(): zhc.Definition {
|
||||
// Some devices can change modelID, reconsider the definition in that case.
|
||||
// https://github.com/Koenkk/zigbee-herdsman-converters/issues/3016
|
||||
if (!this.zh.interviewing && (!this._definition || this._definitionModelID !== this.zh.modelID)) {
|
||||
this._definition = zigbeeHerdsmanConverters.findByDevice(this.zh);
|
||||
this._definitionModelID = this.zh.modelID;
|
||||
}
|
||||
return this._definition;
|
||||
get isSupported(): boolean {
|
||||
return this.zh.type === 'Coordinator' || (this.definition && !this.definition.generated);
|
||||
}
|
||||
|
||||
constructor(device: zh.Device) {
|
||||
this.zh = device;
|
||||
}
|
||||
|
||||
exposes(): zhc.DefinitionExpose[] {
|
||||
exposes(): zhc.Expose[] {
|
||||
/* istanbul ignore if */
|
||||
if (typeof this.definition.exposes == 'function') {
|
||||
return this.definition.exposes(this.zh, this.options);
|
||||
const options: KeyValue = this.options;
|
||||
return this.definition.exposes(this.zh, options);
|
||||
} else {
|
||||
return this.definition.exposes;
|
||||
}
|
||||
}
|
||||
|
||||
async resolveDefinition(): Promise<void> {
|
||||
if (!this.zh.interviewing && (!this.definition || this._definitionModelID !== this.zh.modelID)) {
|
||||
this.definition = await zhc.findByDevice(this.zh, true);
|
||||
this._definitionModelID = this.zh.modelID;
|
||||
}
|
||||
}
|
||||
|
||||
ensureInSettings(): void {
|
||||
if (this.zh.type !== 'Coordinator' && !settings.getDevice(this.zh.ieeeAddr)) {
|
||||
settings.addDevice(this.zh.ieeeAddr);
|
||||
@@ -63,12 +65,21 @@ export default class Device {
|
||||
}
|
||||
|
||||
endpointName(endpoint: zh.Endpoint): string {
|
||||
let name = null;
|
||||
let epName = null;
|
||||
if (this.definition?.endpoint) {
|
||||
name = Object.entries(this.definition?.endpoint(this.zh)).find((e) => e[1] == endpoint.ID)[0];
|
||||
const mapping = this.definition?.endpoint(this.zh);
|
||||
for (const [name, id] of Object.entries(mapping)) {
|
||||
if (id == endpoint.ID) {
|
||||
epName = name;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* istanbul ignore next */
|
||||
return name === 'default' ? null : name;
|
||||
return epName === 'default' ? null : epName;
|
||||
}
|
||||
|
||||
getEndpointNames(): string[] {
|
||||
return Object.keys(this.definition?.endpoint?.(this.zh) ?? {}).filter((name) => name !== 'default');
|
||||
}
|
||||
|
||||
isIkeaTradfri(): boolean {return this.zh.manufacturerID === 4476;}
|
||||
|
||||
+2
-3
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable brace-style */
|
||||
import * as settings from '../util/settings';
|
||||
import zigbeeHerdsmanConverters from 'zigbee-herdsman-converters';
|
||||
import * as zhc from 'zigbee-herdsman-converters';
|
||||
|
||||
export default class Group {
|
||||
public zh: zh.Group;
|
||||
@@ -24,8 +24,7 @@ export default class Group {
|
||||
}
|
||||
|
||||
membersDefinitions(): zhc.Definition[] {
|
||||
return this.zh.members.map((m) =>
|
||||
zigbeeHerdsmanConverters.findByDevice(m.getDevice())).filter((d) => d) as zhc.Definition[];
|
||||
return this.membersDevices().map((d) => d.definition).filter((d) => d);
|
||||
}
|
||||
|
||||
isDevice(): this is Device {return false;}
|
||||
|
||||
+9
-8
@@ -1,9 +1,10 @@
|
||||
import mqtt from 'mqtt';
|
||||
import * as mqtt from 'mqtt';
|
||||
import logger from './util/logger';
|
||||
import * as settings from './util/settings';
|
||||
import utils from './util/utils';
|
||||
import fs from 'fs';
|
||||
import bind from 'bind-decorator';
|
||||
import type {QoS} from 'mqtt-packet';
|
||||
|
||||
export default class MQTT {
|
||||
private publishedTopics: Set<string> = new Set();
|
||||
@@ -11,7 +12,7 @@ export default class MQTT {
|
||||
private client: mqtt.MqttClient;
|
||||
private eventBus: EventBus;
|
||||
private initialConnect = true;
|
||||
private republishRetainedTimer: NodeJS.Timer;
|
||||
private republishRetainedTimer: NodeJS.Timeout;
|
||||
private retainedMessages: {[s: string]: {payload: string, options: MQTTOptions,
|
||||
skipLog: boolean, skipReceive: boolean, topic: string, base: string}} = {};
|
||||
|
||||
@@ -26,7 +27,7 @@ export default class MQTT {
|
||||
const options: mqtt.IClientOptions = {
|
||||
will: {
|
||||
topic: `${settings.get().mqtt.base_topic}/bridge/state`,
|
||||
payload: utils.availabilityPayload('offline', settings.get()),
|
||||
payload: Buffer.from(utils.availabilityPayload('offline', settings.get())),
|
||||
retain: settings.get().mqtt.force_disable_retain ? false : true,
|
||||
qos: 1,
|
||||
},
|
||||
@@ -133,14 +134,14 @@ export default class MQTT {
|
||||
this.client.subscribe(topic);
|
||||
}
|
||||
|
||||
@bind public onMessage(topic: string, message: string): void {
|
||||
@bind public onMessage(topic: string, message: Buffer): void {
|
||||
// Since we subscribe to zigbee2mqtt/# we also receive the message we send ourselves, skip these.
|
||||
if (!this.publishedTopics.has(topic)) {
|
||||
logger.debug(`Received MQTT message on '${topic}' with data '${message}'`);
|
||||
this.eventBus.emitMQTTMessage({topic, message: message + ''});
|
||||
logger.debug(`Received MQTT message on '${topic}' with data '${message.toString()}'`);
|
||||
this.eventBus.emitMQTTMessage({topic, message: message.toString()});
|
||||
}
|
||||
|
||||
if (this.republishRetainedTimer && topic == `${settings.get().mqtt.base_topic}/bridge/state`) {
|
||||
if (this.republishRetainedTimer && topic === `${settings.get().mqtt.base_topic}/bridge/info`) {
|
||||
clearTimeout(this.republishRetainedTimer);
|
||||
this.republishRetainedTimer = null;
|
||||
}
|
||||
@@ -153,7 +154,7 @@ export default class MQTT {
|
||||
async publish(topic: string, payload: string, options: MQTTOptions={},
|
||||
base=settings.get().mqtt.base_topic, skipLog=false, skipReceive=true,
|
||||
): Promise<void> {
|
||||
const defaultOptions: {qos: mqtt.QoS, retain: boolean} = {qos: 0, retain: false};
|
||||
const defaultOptions: {qos: QoS, retain: boolean} = {qos: 0, retain: false};
|
||||
topic = `${base}/${topic}`;
|
||||
|
||||
if (skipReceive) {
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ const dontCacheProperties = [
|
||||
class State {
|
||||
private state: {[s: string | number]: KeyValue} = {};
|
||||
private file = data.joinPath('state.json');
|
||||
private timer: NodeJS.Timer = null;
|
||||
private timer: NodeJS.Timeout = null;
|
||||
|
||||
constructor(private readonly eventBus: EventBus, private readonly zigbee: Zigbee) {
|
||||
this.eventBus = eventBus;
|
||||
|
||||
Vendored
+17
-74
@@ -17,6 +17,12 @@ import type {
|
||||
Cluster as ZHCluster,
|
||||
} from 'zigbee-herdsman/dist/zcl/tstype';
|
||||
|
||||
import type {
|
||||
FrameControl as ZHFrameControl,
|
||||
} from 'zigbee-herdsman/dist/zcl';
|
||||
|
||||
import type * as zhc from 'zigbee-herdsman-converters';
|
||||
|
||||
import type * as ZHEvents from 'zigbee-herdsman/dist/controller/events';
|
||||
|
||||
import type TypeEventBus from 'lib/eventBus';
|
||||
@@ -27,7 +33,7 @@ import type TypeDevice from 'lib/model/device';
|
||||
import type TypeGroup from 'lib/model/group';
|
||||
import type TypeExtension from 'lib/extension/extension';
|
||||
|
||||
import type mqtt from 'mqtt';
|
||||
import type {QoS} from 'mqtt-packet';
|
||||
|
||||
declare global {
|
||||
// Define some class types as global
|
||||
@@ -40,8 +46,11 @@ declare global {
|
||||
type Extension = TypeExtension;
|
||||
|
||||
// Types
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type ExternalDefinition = zhc.Definition & {homeassistant: any};
|
||||
interface MQTTResponse {data: KeyValue, status: 'error' | 'ok', error?: string, transaction?: string}
|
||||
interface MQTTOptions {qos?: mqtt.QoS, retain?: boolean, properties?: {messageExpiryInterval: number}}
|
||||
interface MQTTOptions {qos?: QoS, retain?: boolean, properties?: {messageExpiryInterval: number}}
|
||||
type Scene = {id: number, name: string};
|
||||
type StateChangeReason = 'publishDebounce' | 'groupOptimistic' | 'lastSeenChanged' | 'publishCached';
|
||||
type PublishEntityState = (entity: Device | Group, payload: KeyValue,
|
||||
stateChangeReason?: StateChangeReason) => Promise<void>;
|
||||
@@ -66,73 +75,6 @@ declare global {
|
||||
}
|
||||
}
|
||||
|
||||
// zigbee-herdsman-converters
|
||||
namespace zhc {
|
||||
interface Logger {
|
||||
info: (message: string) => void;
|
||||
warn: (message: string) => void;
|
||||
error: (message: string) => void;
|
||||
debug: (message: string) => void;
|
||||
}
|
||||
|
||||
interface ToZigbeeConverterGetMeta {message?: KeyValue, mapped?: Definition | Definition[]}
|
||||
|
||||
interface ToZigbeeConverterResult {state: KeyValue,
|
||||
membersState: {[s: string]: KeyValue}, readAfterWriteTime?: number}
|
||||
|
||||
interface ToZigbeeConverter {
|
||||
key: string[],
|
||||
convertGet?: (entity: zh.Endpoint | zh.Group, key: string, meta: ToZigbeeConverterGetMeta) => Promise<void>
|
||||
convertSet?: (entity: zh.Endpoint | zh.Group, key: string, value: KeyValue | string | number,
|
||||
meta: {state: KeyValue}) => Promise<ToZigbeeConverterResult>
|
||||
}
|
||||
|
||||
interface FromZigbeeConverter {
|
||||
cluster: string,
|
||||
type: string[] | string,
|
||||
convert: (model: Definition, msg: KeyValue, publish: (payload: KeyValue) => void, options: KeyValue,
|
||||
meta: {state: KeyValue, logger: Logger, device: zh.Device}) => Promise<KeyValue>,
|
||||
}
|
||||
|
||||
interface DefinitionExposeFeature {name: string, endpoint?: string,
|
||||
property: string, value_max?: number, value_min?: number, unit?: string,
|
||||
value_off?: string, value_on?: string, value_step?: number, values: string[], access: number}
|
||||
|
||||
interface DefinitionExpose {
|
||||
type: string, name?: string, features?: DefinitionExposeFeature[],
|
||||
endpoint?: string, values?: string[], value_off?: string, value_on?: string, value_step?: number,
|
||||
access: number, property: string, unit?: string,
|
||||
value_min?: number, value_max?: number}
|
||||
|
||||
interface OtaUpdateAvailableResult {available: boolean, currentFileVersion: number, otaFileVersion: number}
|
||||
|
||||
interface Definition {
|
||||
model: string,
|
||||
zigbeeModel: string[],
|
||||
endpoint?: (device: zh.Device) => {[s: string]: number}
|
||||
toZigbee: ToZigbeeConverter[]
|
||||
fromZigbee: FromZigbeeConverter[]
|
||||
icon?: string
|
||||
description: string
|
||||
options: zhc.DefinitionExpose[],
|
||||
vendor: string
|
||||
exposes: DefinitionExpose[] | ((device: zh.Device, options: KeyValue) => DefinitionExpose[])
|
||||
configure?: (device: zh.Device, coordinatorEndpoint: zh.Endpoint, logger: Logger,
|
||||
options?: DeviceOptions) => Promise<void>;
|
||||
onEvent?: (type: string, data: KeyValue, device: zh.Device,
|
||||
settings: KeyValue, state: KeyValue) => Promise<void>;
|
||||
ota?: {
|
||||
isUpdateAvailable: (device: zh.Device, logger: Logger, data?: KeyValue)
|
||||
=> Promise<OtaUpdateAvailableResult>;
|
||||
updateToLatest: (device: zh.Device, logger: Logger,
|
||||
onProgress: (progress: number, remaining: number) => void) => Promise<number>;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type ExternalDefinition = Definition & {homeassistant: any};
|
||||
}
|
||||
|
||||
namespace eventdata {
|
||||
type EntityRenamed = { entity: Device | Group, homeAssisantRename: boolean, from: string, to: string };
|
||||
type DeviceRemoved = { ieeeAddr: string, name: string };
|
||||
@@ -148,6 +90,7 @@ declare global {
|
||||
type DeviceInterview = { device: Device, status: 'started' | 'successful' | 'failed' };
|
||||
type DeviceJoined = { device: Device };
|
||||
type EntityOptionsChanged = { entity: Device | Group, from: KeyValue, to: KeyValue };
|
||||
type ExposesChanged = { device: Device };
|
||||
type Reconfigure = { device: Device };
|
||||
type DeviceLeave = { ieeeAddr: string, name: string };
|
||||
type GroupMembersChanged = {group: Group, action: 'remove' | 'add' | 'remove_all',
|
||||
@@ -162,8 +105,9 @@ declare global {
|
||||
groupID: number;
|
||||
cluster: string | number;
|
||||
data: KeyValue | Array<string | number>;
|
||||
meta: {zclTransactionSequenceNumber?: number;};
|
||||
meta: {zclTransactionSequenceNumber?: number; manufacturerCode?: number; frameControl?: ZHFrameControl;};
|
||||
};
|
||||
type ScenesChanged = { entity: Device | Group };
|
||||
}
|
||||
|
||||
// Settings
|
||||
@@ -185,7 +129,7 @@ declare global {
|
||||
base_topic: string,
|
||||
include_device_information: boolean,
|
||||
force_disable_retain: boolean
|
||||
version?: number,
|
||||
version?: 3 | 4 | 5,
|
||||
user?: string,
|
||||
password?: string,
|
||||
server: string,
|
||||
@@ -199,7 +143,7 @@ declare global {
|
||||
serial: {
|
||||
disable_led: boolean,
|
||||
port?: string,
|
||||
adapter?: 'deconz' | 'zstack' | 'ezsp' | 'zigate',
|
||||
adapter?: 'deconz' | 'zstack' | 'ezsp' | 'zigate' | 'ember',
|
||||
baudrate?: number,
|
||||
rtscts?: boolean,
|
||||
},
|
||||
@@ -253,7 +197,7 @@ declare global {
|
||||
log_level: 'debug' | 'info' | 'error' | 'warn',
|
||||
log_syslog: KeyValue,
|
||||
pan_id: number | 'GENERATE',
|
||||
ext_pan_id: number[],
|
||||
ext_pan_id: number[] | 'GENERATE',
|
||||
channel: number,
|
||||
adapter_concurrent: number | null,
|
||||
adapter_delay: number | null,
|
||||
@@ -312,4 +256,3 @@ declare global {
|
||||
qos?: 0 | 1 | 2,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
declare module 'zigbee-herdsman-converters' {
|
||||
export function findByDevice(device: zh.Device): zhc.Definition;
|
||||
export function getConfigureKey(definition: zhc.Definition): string | number;
|
||||
export const toZigbeeConverters: {[s: string]: zhc.ToZigbeeConverter};
|
||||
export const definitions: zhc.Definition[];
|
||||
export function addDeviceDefinition(definition: zhc.Definition): Promise<void>;
|
||||
export function onEvent(type: string, data: KeyValue, device: zh.Device, settings: KeyValue,
|
||||
state: KeyValue): Promise<void>;
|
||||
}
|
||||
|
||||
declare module 'zigbee-herdsman-converters/lib/ota/tradfri' {
|
||||
export function useTestURL(): void;
|
||||
}
|
||||
|
||||
declare module 'zigbee-herdsman-converters/lib/ota/zigbeeOTA' {
|
||||
export function useIndexOverride(indexFileName: string): void;
|
||||
export function setDataDir(dataDir: string): void;
|
||||
}
|
||||
+5
-4
@@ -4,7 +4,7 @@ import * as settings from './settings';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import fx from 'mkdir-recursive';
|
||||
import rimraf from 'rimraf';
|
||||
import {rimrafSync} from 'rimraf';
|
||||
import assert from 'assert';
|
||||
|
||||
const colorizer = winston.format.colorize();
|
||||
@@ -38,7 +38,7 @@ function init(): void {
|
||||
directory = settings.get().advanced.log_directory.replace('%TIMESTAMP%', timestamp);
|
||||
logFilename = settings.get().advanced.log_file.replace('%TIMESTAMP%', timestamp);
|
||||
|
||||
// Make sure that log directoy exsists when not logging to stdout only
|
||||
// Make sure that log directory exists when not logging to stdout only
|
||||
if (output.includes('file')) {
|
||||
fx.mkdirSync(directory);
|
||||
|
||||
@@ -144,7 +144,7 @@ function cleanup(): void {
|
||||
directories = directories.slice(10, directories.length);
|
||||
directories.forEach((dir) => {
|
||||
logger.debug(`Removing old log directory '${dir.path}'`);
|
||||
rimraf.sync(dir.path);
|
||||
rimrafSync(dir.path);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -177,11 +177,12 @@ function setLevel(level: Z2MLogLevel): void {
|
||||
}
|
||||
|
||||
function warn(message: string): void {
|
||||
// winston.config.syslog.levels doesnt have warn, but is required for syslog.
|
||||
// @ts-expect-error
|
||||
logger.warning(message);
|
||||
}
|
||||
|
||||
function warning(message: string): void {
|
||||
// @ts-expect-error
|
||||
logger.warning(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@
|
||||
},
|
||||
"adapter": {
|
||||
"type": ["string"],
|
||||
"enum": ["deconz", "zstack", "zigate", "ezsp", "auto"],
|
||||
"enum": ["deconz", "zstack", "zigate", "ezsp", "auto", "ember"],
|
||||
"title": "Adapter",
|
||||
"default": "auto",
|
||||
"requiresRestart": true,
|
||||
@@ -345,7 +345,7 @@
|
||||
"default": false
|
||||
},
|
||||
"zigbee_ota_override_index_location": {
|
||||
"type": "string",
|
||||
"type": ["string", "null"],
|
||||
"title": "OTA index override file name",
|
||||
"requiresRestart": true,
|
||||
"description": "Location of override OTA index file",
|
||||
@@ -366,15 +366,15 @@
|
||||
"port": {
|
||||
"type": "number",
|
||||
"title": "Port",
|
||||
"description": "Frontend binding port",
|
||||
"description": "Frontend binding port. Ignored when using a unix domain socket",
|
||||
"default": 8080,
|
||||
"requiresRestart": true
|
||||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"type": ["string", "null"],
|
||||
"title": "Bind host",
|
||||
"description": "Frontend binding host",
|
||||
"default": "0.0.0.0",
|
||||
"description": "Frontend binding host. Binds to a unix socket when an absolute path is given instead.",
|
||||
"examples": ["127.0.0.1", "::1", "/run/zigbee2mqtt/zigbee2mqtt.sock"],
|
||||
"requiresRestart": true
|
||||
},
|
||||
"auth_token": {
|
||||
@@ -448,7 +448,7 @@
|
||||
"type": "boolean",
|
||||
"title": "Legacy availability payload",
|
||||
"requiresRestart": true,
|
||||
"description": "Payload to be used for device availabilty and bridge/state topics. true = text, false = JSON",
|
||||
"description": "Payload to be used for device availability and bridge/state topics. true = text, false = JSON",
|
||||
"default": true
|
||||
},
|
||||
"log_rotation": {
|
||||
@@ -511,13 +511,13 @@
|
||||
"type": "number",
|
||||
"title": "Port",
|
||||
"description": "The port on the host that syslog is running on, defaults to syslogd's default port.",
|
||||
"default": 123
|
||||
"default": 514
|
||||
},
|
||||
"protocol": {
|
||||
"type": "string",
|
||||
"title": "Protocol",
|
||||
"description": "The network protocol to log over (e.g. tcp4, udp4, tls4, unix, unix-connect, etc).",
|
||||
"default": "tcp4",
|
||||
"default": "udp4",
|
||||
"examples": [
|
||||
"udp4",
|
||||
"tls4",
|
||||
@@ -581,10 +581,18 @@
|
||||
"description": "ZigBee pan ID, changing requires repairing all devices!"
|
||||
},
|
||||
"ext_pan_id": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number"
|
||||
},
|
||||
"oneOf": [{
|
||||
"type": "string",
|
||||
"title": "Extended pan ID (string)"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number"
|
||||
},
|
||||
"title": "Extended pan ID (array)"
|
||||
}
|
||||
],
|
||||
"title": "Ext Pan ID",
|
||||
"requiresRestart": true,
|
||||
"description": "Zigbee extended pan ID, changing requires repairing all devices!"
|
||||
@@ -903,7 +911,7 @@
|
||||
"title": "Group off state",
|
||||
"default": "auto",
|
||||
"requiresRestart": true,
|
||||
"description": "Control when to publish state OFF for a group. 'all_members_off': only publish state OFF when all group memebers are in state OFF, 'last_member_state': publish state OFF whenever one of its members changes to OFF"
|
||||
"description": "Control when to publish state OFF for a group. 'all_members_off': only publish state OFF when all group members are in state OFF, 'last_member_state': publish state OFF whenever one of its members changes to OFF"
|
||||
},
|
||||
"filtered_attributes": {
|
||||
"type": "array",
|
||||
|
||||
+17
-12
@@ -150,7 +150,7 @@ function loadSettingsWithDefaults(): void {
|
||||
}
|
||||
|
||||
if (_settingsWithDefaults.frontend) {
|
||||
const defaults = {port: 8080, auth_token: false, host: '0.0.0.0'};
|
||||
const defaults = {port: 8080, auth_token: false};
|
||||
const s = typeof _settingsWithDefaults.frontend === 'object' ? _settingsWithDefaults.frontend : {};
|
||||
// @ts-ignore
|
||||
_settingsWithDefaults.frontend = {};
|
||||
@@ -211,7 +211,7 @@ function write(): void {
|
||||
// Read settings to check if we have to split devices/groups into separate file.
|
||||
const actual = yaml.read(file);
|
||||
|
||||
// In case the setting is defined in a separte file (e.g. !secret network_key) update it there.
|
||||
// In case the setting is defined in a separate file (e.g. !secret network_key) update it there.
|
||||
for (const path of [
|
||||
['mqtt', 'server'],
|
||||
['mqtt', 'user'],
|
||||
@@ -230,7 +230,7 @@ function write(): void {
|
||||
|
||||
// Write devices/groups to separate file if required.
|
||||
const writeDevicesOrGroups = (type: 'devices' | 'groups'): void => {
|
||||
if (typeof actual[type] === 'string' || Array.isArray(actual[type])) {
|
||||
if (typeof actual[type] === 'string' || (Array.isArray(actual[type]) && actual[type].length > 0)) {
|
||||
const fileToWrite = Array.isArray(actual[type]) ? actual[type][0] : actual[type];
|
||||
const content = objectAssignDeep({}, settings[type]);
|
||||
|
||||
@@ -285,6 +285,11 @@ export function validate(): string[] {
|
||||
errors.push(`advanced.pan_id: should be number or 'GENERATE' (is '${_settings.advanced.pan_id}')`);
|
||||
}
|
||||
|
||||
if (_settings.advanced && _settings.advanced.ext_pan_id && typeof _settings.advanced.ext_pan_id === 'string' &&
|
||||
_settings.advanced.ext_pan_id !== 'GENERATE') {
|
||||
errors.push(`advanced.ext_pan_id: should be array or 'GENERATE' (is '${_settings.advanced.ext_pan_id}')`);
|
||||
}
|
||||
|
||||
// Verify that all friendly names are unique
|
||||
const names: string[] = [];
|
||||
const check = (e: DeviceOptions | GroupOptions): void => {
|
||||
@@ -330,7 +335,7 @@ function read(): Settings {
|
||||
|
||||
// Read !secret MQTT username and password if set
|
||||
// eslint-disable-next-line
|
||||
const interpetValue = (value: any): any => {
|
||||
const interpretValue = (value: any): any => {
|
||||
const ref = parseValueRef(value);
|
||||
if (ref) {
|
||||
return yaml.read(data.joinPath(ref.filename))[ref.key];
|
||||
@@ -340,28 +345,28 @@ function read(): Settings {
|
||||
};
|
||||
|
||||
if (s.mqtt?.user) {
|
||||
s.mqtt.user = interpetValue(s.mqtt.user);
|
||||
s.mqtt.user = interpretValue(s.mqtt.user);
|
||||
}
|
||||
|
||||
if (s.mqtt?.password) {
|
||||
s.mqtt.password = interpetValue(s.mqtt.password);
|
||||
s.mqtt.password = interpretValue(s.mqtt.password);
|
||||
}
|
||||
|
||||
if (s.mqtt?.server) {
|
||||
s.mqtt.server = interpetValue(s.mqtt.server);
|
||||
s.mqtt.server = interpretValue(s.mqtt.server);
|
||||
}
|
||||
|
||||
if (s.advanced?.network_key) {
|
||||
s.advanced.network_key = interpetValue(s.advanced.network_key);
|
||||
s.advanced.network_key = interpretValue(s.advanced.network_key);
|
||||
}
|
||||
|
||||
if (s.frontend?.auth_token) {
|
||||
s.frontend.auth_token = interpetValue(s.frontend.auth_token);
|
||||
s.frontend.auth_token = interpretValue(s.frontend.auth_token);
|
||||
}
|
||||
|
||||
// Read devices/groups configuration from separate file if specified.
|
||||
const readDevicesOrGroups = (type: 'devices' | 'groups'): void => {
|
||||
if (typeof s[type] === 'string' || Array.isArray(s[type])) {
|
||||
if (typeof s[type] === 'string' || (Array.isArray(s[type]) && Array(s[type]).length > 0)) {
|
||||
/* eslint-disable-line */ // @ts-ignore
|
||||
const files: string[] = Array.isArray(s[type]) ? s[type] : [s[type]];
|
||||
s[type] = {};
|
||||
@@ -472,7 +477,7 @@ export function apply(newSettings: Record<string, unknown>): boolean {
|
||||
throw new Error(`${error.instancePath.substring(1)} ${error.message}`);
|
||||
}
|
||||
|
||||
getInternalSettings(); // Ensure _settings is intialized.
|
||||
getInternalSettings(); // Ensure _settings is initialized.
|
||||
/* eslint-disable-line */ // @ts-ignore
|
||||
_settings = objectAssignDeep.noMutate(_settings, newSettings);
|
||||
write();
|
||||
@@ -588,7 +593,7 @@ export function removeDevice(IDorName: string): void {
|
||||
// Remove device from groups
|
||||
if (settings.groups) {
|
||||
const regex =
|
||||
new RegExp(`^(${device.friendly_name}|${device.ID})(/(\\d|${utils.endpointNames.join('|')}))?$`);
|
||||
new RegExp(`^(${device.friendly_name}|${device.ID})(/[^/]+)?$`);
|
||||
for (const group of Object.values(settings.groups).filter((g) => g.devices)) {
|
||||
group.devices = group.devices.filter((device) => !device.match(regex));
|
||||
}
|
||||
|
||||
+51
-47
@@ -6,6 +6,7 @@ import fs from 'fs';
|
||||
import path from 'path';
|
||||
import {detailedDiff} from 'deep-object-diff';
|
||||
import objectAssignDeep from 'object-assign-deep';
|
||||
import type * as zhc from 'zigbee-herdsman-converters';
|
||||
|
||||
// construct a local ISO8601 string (instead of UTC-based)
|
||||
// Example:
|
||||
@@ -29,20 +30,6 @@ function toLocalISOString(date: Date): string {
|
||||
':' + pad(tzOffset % 60);
|
||||
}
|
||||
|
||||
const endpointNames = [
|
||||
'left', 'right', 'center', 'bottom_left', 'bottom_right', 'default',
|
||||
'top_left', 'top_right', 'white', 'rgb', 'cct', 'system', 'top', 'bottom', 'center_left', 'center_right',
|
||||
'ep1', 'ep2', 'row_1', 'row_2', 'row_3', 'row_4', 'relay', 'usb',
|
||||
'l1', 'l2', 'l3', 'l4', 'l5', 'l6', 'l7', 'l8',
|
||||
'l9', 'l10', 'l11', 'l12', 'l13', 'l14', 'l15', 'l16',
|
||||
'button_1', 'button_2', 'button_3', 'button_4', 'button_5',
|
||||
'button_6', 'button_7', 'button_8', 'button_9', 'button_10',
|
||||
'button_11', 'button_12', 'button_13', 'button_14', 'button_15',
|
||||
'button_16', 'button_17', 'button_18', 'button_19', 'button_20',
|
||||
'button_light', 'button_fan_high', 'button_fan_med', 'button_fan_low',
|
||||
'heat', 'cool', 'water', 'meter', 'wifi', 'no_occupancy_since',
|
||||
];
|
||||
|
||||
function capitalize(s: string): string {
|
||||
return s[0].toUpperCase() + s.slice(1);
|
||||
}
|
||||
@@ -79,7 +66,9 @@ async function getZigbee2MQTTVersion(includeCommitHash=true): Promise<{commitHas
|
||||
}
|
||||
|
||||
async function getDependencyVersion(depend: string): Promise<{version: string}> {
|
||||
const packageJSON = await import(path.join(require.resolve(depend), '..', '..', 'package.json'));
|
||||
const modulePath = path.dirname(require.resolve(depend));
|
||||
const packageJSONPath = path.join(modulePath.slice(0, modulePath.indexOf(depend) + depend.length), 'package.json');
|
||||
const packageJSON = await import(packageJSONPath);
|
||||
const version = packageJSON.version;
|
||||
return {version};
|
||||
}
|
||||
@@ -157,25 +146,21 @@ function loadModuleFromFile(modulePath: string): unknown {
|
||||
return loadModuleFromText(moduleCode);
|
||||
}
|
||||
|
||||
function* getExternalConvertersDefinitions(settings: Settings): Generator<zhc.ExternalDefinition> {
|
||||
const externalConverters = settings.external_converters;
|
||||
export function* loadExternalConverter(moduleName: string): Generator<ExternalDefinition> {
|
||||
let converter;
|
||||
|
||||
for (const moduleName of externalConverters) {
|
||||
let converter;
|
||||
if (moduleName.endsWith('.js')) {
|
||||
converter = loadModuleFromFile(data.joinPath(moduleName));
|
||||
} else {
|
||||
converter = require(moduleName);
|
||||
}
|
||||
|
||||
if (moduleName.endsWith('.js')) {
|
||||
converter = loadModuleFromFile(data.joinPath(moduleName));
|
||||
} else {
|
||||
converter = require(moduleName);
|
||||
}
|
||||
|
||||
if (Array.isArray(converter)) {
|
||||
for (const item of converter) {
|
||||
yield item;
|
||||
}
|
||||
} else {
|
||||
yield converter;
|
||||
if (Array.isArray(converter)) {
|
||||
for (const item of converter) {
|
||||
yield item;
|
||||
}
|
||||
} else {
|
||||
yield converter;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,16 +236,10 @@ function getAllFiles(path_: string): string[] {
|
||||
|
||||
function validateFriendlyName(name: string, throwFirstError=false): string[] {
|
||||
const errors = [];
|
||||
for (const endpointName of endpointNames) {
|
||||
if (name.toLowerCase().endsWith('/' + endpointName)) {
|
||||
errors.push(`friendly_name is not allowed to end with: '/${endpointName}'`);
|
||||
}
|
||||
}
|
||||
|
||||
if (name.length === 0) errors.push(`friendly_name must be at least 1 char long`);
|
||||
if (name.endsWith('/') || name.startsWith('/')) errors.push(`friendly_name is not allowed to end or start with /`);
|
||||
if (containsControlCharacter(name)) errors.push(`friendly_name is not allowed to contain control char`);
|
||||
if (endpointNames.includes(name)) errors.push(`Following friendly_name are not allowed: '${endpointNames}'`);
|
||||
if (name.match(/.*\/\d*$/)) errors.push(`Friendly name cannot end with a "/DIGIT" ('${name}')`);
|
||||
if (name.includes('#') || name.includes('+')) {
|
||||
errors.push(`MQTT wildcard (+ and #) not allowed in friendly_name ('${name}')`);
|
||||
@@ -308,12 +287,6 @@ function isAvailabilityEnabledForEntity(entity: Device | Group, settings: Settin
|
||||
return !blocklist.includes(entity.name) && !blocklist.includes(entity.ieeeAddr);
|
||||
}
|
||||
|
||||
const entityIDRegex = new RegExp(`^(.+?)(?:/(${endpointNames.join('|')}|\\d+))?$`);
|
||||
function parseEntityID(ID: string): {ID: string, endpoint: string} {
|
||||
const match = ID.match(entityIDRegex);
|
||||
return match && {ID: match[1], endpoint: match[2]};
|
||||
}
|
||||
|
||||
function isEndpoint(obj: unknown): obj is zh.Endpoint {
|
||||
return obj.constructor.name.toLowerCase() === 'endpoint';
|
||||
}
|
||||
@@ -367,6 +340,18 @@ function clone(obj: KeyValue): KeyValue {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
export function isNumericExposeFeature(feature: zhc.Feature): feature is zhc.Numeric {
|
||||
return feature?.type === 'numeric';
|
||||
}
|
||||
|
||||
export function isEnumExposeFeature(feature: zhc.Feature): feature is zhc.Enum {
|
||||
return feature?.type === 'enum';
|
||||
}
|
||||
|
||||
export function isBinaryExposeFeature(feature: zhc.Feature): feature is zhc.Binary {
|
||||
return feature?.type === 'binary';
|
||||
}
|
||||
|
||||
function computeSettingsToChange(current: KeyValue, new_: KeyValue): KeyValue {
|
||||
const diff: KeyValue = detailedDiff(current, new_);
|
||||
|
||||
@@ -400,11 +385,30 @@ function computeSettingsToChange(current: KeyValue, new_: KeyValue): KeyValue {
|
||||
return newSettings;
|
||||
}
|
||||
|
||||
function getScenes(entity: zh.Endpoint | zh.Group): Scene[] {
|
||||
const scenes: {[id: number]: Scene} = {};
|
||||
const endpoints = isEndpoint(entity) ? [entity] : entity.members;
|
||||
const groupID = isEndpoint(entity) ? 0 : entity.groupID;
|
||||
|
||||
for (const endpoint of endpoints) {
|
||||
for (const [key, data] of Object.entries(endpoint.meta?.scenes || {})) {
|
||||
const split = key.split('_');
|
||||
const sceneID = parseInt(split[0], 10);
|
||||
const sceneGroupID = parseInt(split[1], 10);
|
||||
if (sceneGroupID === groupID) {
|
||||
scenes[sceneID] = {id: sceneID, name: (data as KeyValue).name || `Scene ${sceneID}`};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Object.values(scenes);
|
||||
}
|
||||
|
||||
export default {
|
||||
endpointNames, capitalize, getZigbee2MQTTVersion, getDependencyVersion, formatDate, objectHasProperties,
|
||||
capitalize, getZigbee2MQTTVersion, getDependencyVersion, formatDate, objectHasProperties,
|
||||
equalsPartial, getObjectProperty, getResponse, parseJSON, loadModuleFromText, loadModuleFromFile,
|
||||
getExternalConvertersDefinitions, removeNullPropertiesFromObject, toNetworkAddressHex, toSnakeCase,
|
||||
parseEntityID, isEndpoint, isZHGroup, hours, minutes, seconds, validateFriendlyName, sleep,
|
||||
removeNullPropertiesFromObject, toNetworkAddressHex, toSnakeCase,
|
||||
isEndpoint, isZHGroup, hours, minutes, seconds, validateFriendlyName, sleep,
|
||||
sanitizeImageParameter, isAvailabilityEnabledForEntity, publishLastSeen, availabilityPayload,
|
||||
getAllFiles, filterProperties, flatten, arrayUnique, clone, computeSettingsToChange,
|
||||
getAllFiles, filterProperties, flatten, arrayUnique, clone, computeSettingsToChange, getScenes,
|
||||
};
|
||||
|
||||
+2
-1
@@ -4,7 +4,8 @@ import equals from 'fast-deep-equal/es6';
|
||||
|
||||
function read(file: string): KeyValue {
|
||||
try {
|
||||
return yaml.load(fs.readFileSync(file, 'utf8'));
|
||||
const result = yaml.load(fs.readFileSync(file, 'utf8'));
|
||||
return result as KeyValue ?? {};
|
||||
} catch (error) {
|
||||
if (error.name === 'YAMLException') {
|
||||
error.file = file;
|
||||
|
||||
+61
-7
@@ -9,6 +9,9 @@ import Device from './model/device';
|
||||
import Group from './model/group';
|
||||
import * as ZHEvents from 'zigbee-herdsman/dist/controller/events';
|
||||
import bind from 'bind-decorator';
|
||||
import {randomInt} from 'crypto';
|
||||
|
||||
const entityIDRegex = new RegExp(`^(.+?)(?:/([^/]+))?$`);
|
||||
|
||||
export default class Zigbee {
|
||||
private herdsman: Controller;
|
||||
@@ -27,7 +30,8 @@ export default class Zigbee {
|
||||
network: {
|
||||
panID: settings.get().advanced.pan_id === 'GENERATE' ?
|
||||
this.generatePanID() : settings.get().advanced.pan_id as number,
|
||||
extendedPanID: settings.get().advanced.ext_pan_id,
|
||||
extendedPanID: settings.get().advanced.ext_pan_id === 'GENERATE' ?
|
||||
this.generateExtPanID() : settings.get().advanced.ext_pan_id as number[],
|
||||
channelList: [settings.get().advanced.channel],
|
||||
networkKey: settings.get().advanced.network_key === 'GENERATE' ?
|
||||
this.generateNetworkKey() : settings.get().advanced.network_key as number[],
|
||||
@@ -61,6 +65,10 @@ export default class Zigbee {
|
||||
throw error;
|
||||
}
|
||||
|
||||
for (const device of this.devices(false)) {
|
||||
await device.resolveDefinition();
|
||||
}
|
||||
|
||||
this.herdsman.on('adapterDisconnected', () => this.eventBus.emitAdapterDisconnected());
|
||||
this.herdsman.on('lastSeenChanged', (data: ZHEvents.LastSeenChangedPayload) => {
|
||||
this.eventBus.emitLastSeenChanged({device: this.resolveDevice(data.device.ieeeAddr), reason: data.reason});
|
||||
@@ -78,16 +86,18 @@ export default class Zigbee {
|
||||
logger.debug(`Device '${device.name}' announced itself`);
|
||||
this.eventBus.emitDeviceAnnounce({device});
|
||||
});
|
||||
this.herdsman.on('deviceInterview', (data: ZHEvents.DeviceInterviewPayload) => {
|
||||
this.herdsman.on('deviceInterview', async (data: ZHEvents.DeviceInterviewPayload) => {
|
||||
const device = this.resolveDevice(data.device.ieeeAddr);
|
||||
/* istanbul ignore if */ if (!device) return; // Prevent potential race
|
||||
await device.resolveDefinition();
|
||||
const d = {device, status: data.status};
|
||||
this.logDeviceInterview(d);
|
||||
this.eventBus.emitDeviceInterview(d);
|
||||
});
|
||||
this.herdsman.on('deviceJoined', (data: ZHEvents.DeviceJoinedPayload) => {
|
||||
this.herdsman.on('deviceJoined', async (data: ZHEvents.DeviceJoinedPayload) => {
|
||||
const device = this.resolveDevice(data.device.ieeeAddr);
|
||||
/* istanbul ignore if */ if (!device) return; // Prevent potential race
|
||||
await device.resolveDefinition();
|
||||
logger.info(`Device '${device.name}' joined`);
|
||||
this.eventBus.emitDeviceJoined({device});
|
||||
});
|
||||
@@ -96,8 +106,9 @@ export default class Zigbee {
|
||||
logger.warn(`Device '${name}' left the network`);
|
||||
this.eventBus.emitDeviceLeave({ieeeAddr: data.ieeeAddr, name});
|
||||
});
|
||||
this.herdsman.on('message', (data: ZHEvents.MessagePayload) => {
|
||||
this.herdsman.on('message', async (data: ZHEvents.MessagePayload) => {
|
||||
const device = this.resolveDevice(data.device.ieeeAddr);
|
||||
await device.resolveDefinition();
|
||||
logger.debug(`Received Zigbee message from '${device.name}', type '${data.type}', ` +
|
||||
`cluster '${data.cluster}', data '${stringify(data.data)}' from endpoint ${data.endpoint.ID}` +
|
||||
(data.hasOwnProperty('groupID') ? ` with groupID ${data.groupID}` : ``) +
|
||||
@@ -147,7 +158,7 @@ export default class Zigbee {
|
||||
if (data.status === 'successful') {
|
||||
logger.info(`Successfully interviewed '${name}', device has successfully been paired`);
|
||||
|
||||
if (data.device.definition) {
|
||||
if (data.device.isSupported) {
|
||||
const {vendor, description, model} = data.device.definition;
|
||||
logger.info(`Device '${name}' is supported, identified as: ${vendor} ${description} (${model})`);
|
||||
} else {
|
||||
@@ -164,13 +175,19 @@ export default class Zigbee {
|
||||
}
|
||||
|
||||
private generateNetworkKey(): number[] {
|
||||
const key = Array.from({length: 16}, () => Math.floor(Math.random() * 255));
|
||||
const key = Array.from({length: 16}, () => randomInt(256));
|
||||
settings.set(['advanced', 'network_key'], key);
|
||||
return key;
|
||||
}
|
||||
|
||||
private generateExtPanID(): number[] {
|
||||
const key = Array.from({length: 8}, () => randomInt(256));
|
||||
settings.set(['advanced', 'ext_pan_id'], key);
|
||||
return key;
|
||||
}
|
||||
|
||||
private generatePanID(): number {
|
||||
const panID = Math.floor(Math.random() * (0xFFFF - 2)) + 1;
|
||||
const panID = randomInt(1, 0xFFFF - 1);
|
||||
settings.set(['advanced', 'pan_id'], panID);
|
||||
return panID;
|
||||
}
|
||||
@@ -187,6 +204,11 @@ export default class Zigbee {
|
||||
return this.herdsman.backup();
|
||||
}
|
||||
|
||||
async coordinatorCheck(): Promise<{missingRouters: Device[]}> {
|
||||
const check = await this.herdsman.coordinatorCheck();
|
||||
return {missingRouters: check.missingRouters.map((d) => this.resolveDevice(d.ieeeAddr))};
|
||||
}
|
||||
|
||||
async getNetworkParameters(): Promise<zh.NetworkParameters> {
|
||||
return this.herdsman.getNetworkParameters();
|
||||
}
|
||||
@@ -263,6 +285,38 @@ export default class Zigbee {
|
||||
}
|
||||
}
|
||||
|
||||
resolveEntityAndEndpoint(ID: string)
|
||||
: {ID: string, entity: Device | Group, endpointID: string, endpoint: zh.Endpoint} {
|
||||
// This function matches the following entity formats:
|
||||
// device_name (just device name)
|
||||
// device_name/ep_name (device name and endpoint numeric ID or name)
|
||||
// device/name (device name with slashes)
|
||||
// device/name/ep_name (device name with slashes, and endpoint numeric ID or name)
|
||||
|
||||
// The function tries to find an exact match first
|
||||
let entityName = ID;
|
||||
let deviceOrGroup = this.resolveEntity(ID);
|
||||
let endpointNameOrID = undefined;
|
||||
|
||||
// If exact match did not happenc, try matching a device_name/endpoint pattern
|
||||
if (!deviceOrGroup) {
|
||||
// First split the input token by the latest slash
|
||||
const match = ID.match(entityIDRegex);
|
||||
|
||||
// Get the resulting IDs from the match
|
||||
entityName = match[1];
|
||||
deviceOrGroup = this.resolveEntity(match[1]);
|
||||
endpointNameOrID = match[2];
|
||||
}
|
||||
|
||||
// If the function returns non-null endpoint name, but the endpoint field is null, then
|
||||
// it means that endpoint was not matched because there is no such endpoint on the device
|
||||
// (or the entity is a group)
|
||||
const endpoint = deviceOrGroup?.isDevice() ? deviceOrGroup.endpoint(endpointNameOrID) : null;
|
||||
|
||||
return {ID: entityName, entity: deviceOrGroup, endpointID: endpointNameOrID, endpoint: endpoint};
|
||||
}
|
||||
|
||||
firstCoordinatorEndpoint(): zh.Endpoint {
|
||||
return this.herdsman.getDevicesByType('Coordinator')[0].endpoints[0];
|
||||
}
|
||||
|
||||
Generated
+2964
-8838
File diff suppressed because it is too large
Load Diff
+45
-36
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "zigbee2mqtt",
|
||||
"version": "1.29.2",
|
||||
"version": "1.36.1",
|
||||
"description": "Zigbee to MQTT bridge using Zigbee-herdsman",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -8,7 +8,7 @@
|
||||
"url": "git+https://github.com/Koenkk/zigbee2mqtt.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14 || ^16 || ^18 || ^19"
|
||||
"node": "^18 || ^20 || ^21"
|
||||
},
|
||||
"keywords": [
|
||||
"xiaomi",
|
||||
@@ -35,56 +35,62 @@
|
||||
},
|
||||
"homepage": "https://koenkk.github.io/zigbee2mqtt",
|
||||
"dependencies": {
|
||||
"ajv": "^8.11.2",
|
||||
"ajv": "^8.12.0",
|
||||
"bind-decorator": "^1.0.11",
|
||||
"connect-gzip-static": "2.1.1",
|
||||
"debounce": "^1.2.1",
|
||||
"connect-gzip-static": "3.0.1",
|
||||
"debounce": "^2.0.0",
|
||||
"deep-object-diff": "^1.1.9",
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"finalhandler": "^1.2.0",
|
||||
"git-last-commit": "^1.0.1",
|
||||
"humanize-duration": "^3.27.3",
|
||||
"humanize-duration": "^3.31.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"json-stable-stringify-without-jsonify": "^1.0.1",
|
||||
"jszip": "^3.10.1",
|
||||
"mkdir-recursive": "^0.4.0",
|
||||
"moment": "^2.29.4",
|
||||
"mqtt": "4.3.7",
|
||||
"moment": "^2.30.1",
|
||||
"mqtt": "^5.5.0",
|
||||
"object-assign-deep": "^0.4.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.8",
|
||||
"rimraf": "^5.0.5",
|
||||
"semver": "^7.6.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
"uri-js": "^4.4.1",
|
||||
"winston": "^3.8.2",
|
||||
"winston": "^3.12.0",
|
||||
"winston-syslog": "^2.7.0",
|
||||
"winston-transport": "^4.5.0",
|
||||
"ws": "^8.11.0",
|
||||
"zigbee-herdsman": "0.14.83-hotfix.0",
|
||||
"zigbee-herdsman-converters": "15.0.12-hotfix.1",
|
||||
"zigbee2mqtt-frontend": "0.6.121"
|
||||
"winston-transport": "^4.7.0",
|
||||
"ws": "^8.16.0",
|
||||
"zigbee-herdsman": "0.40.3",
|
||||
"zigbee-herdsman-converters": "19.11.2",
|
||||
"zigbee2mqtt-frontend": "0.6.161"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.7",
|
||||
"@babel/plugin-proposal-decorators": "^7.20.7",
|
||||
"@babel/preset-env": "^7.20.2",
|
||||
"@babel/preset-typescript": "^7.18.6",
|
||||
"@types/debounce": "^1.2.1",
|
||||
"@types/finalhandler": "^1.2.0",
|
||||
"@types/humanize-duration": "^3.27.1",
|
||||
"@types/jest": "^29.2.4",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/object-assign-deep": "^0.4.0",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@types/ws": "^8.5.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.47.0",
|
||||
"@typescript-eslint/parser": "^5.47.0",
|
||||
"babel-jest": "^29.3.1",
|
||||
"eslint": "^8.30.0",
|
||||
"@babel/core": "^7.24.3",
|
||||
"@babel/plugin-proposal-decorators": "^7.24.1",
|
||||
"@babel/preset-env": "^7.24.3",
|
||||
"@babel/preset-typescript": "^7.24.1",
|
||||
"@types/debounce": "^1.2.4",
|
||||
"@types/finalhandler": "^1.2.3",
|
||||
"@types/humanize-duration": "^3.27.4",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/node": "^20.11.30",
|
||||
"@types/object-assign-deep": "^0.4.3",
|
||||
"@types/readable-stream": "4.0.11",
|
||||
"@types/ws": "8.5.10",
|
||||
"@typescript-eslint/eslint-plugin": "^7.3.1",
|
||||
"@typescript-eslint/parser": "^7.3.1",
|
||||
"babel-jest": "^29.7.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"eslint-plugin-jest": "^27.1.7",
|
||||
"jest": "^29.3.1",
|
||||
"tmp": "^0.2.1",
|
||||
"typescript": "^4.9.4"
|
||||
"eslint-plugin-jest": "^27.9.0",
|
||||
"jest": "^29.7.0",
|
||||
"tmp": "^0.2.3",
|
||||
"typescript": "^5.4.3"
|
||||
},
|
||||
"overrides": {
|
||||
"zigbee-herdsman-converters": {
|
||||
"zigbee-herdsman": "$zigbee-herdsman"
|
||||
}
|
||||
},
|
||||
"jest": {
|
||||
"coverageThreshold": {
|
||||
@@ -102,5 +108,8 @@
|
||||
},
|
||||
"bin": {
|
||||
"zigbee2mqtt": "cli.js"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"sd-notify": "^2.8.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"packages": {
|
||||
".": {
|
||||
"release-type": "node",
|
||||
"include-component-in-tag": false,
|
||||
"include-v-in-tag": false,
|
||||
"draft": true
|
||||
}
|
||||
},
|
||||
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/* eslint max-len: 0 */
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const process = require('process');
|
||||
const {execSync} = require('child_process');
|
||||
const zhc = require('zigbee-herdsman-converters');
|
||||
|
||||
const z2mTillVersion = process.argv[2];
|
||||
const zhcTillVersion = process.argv[3];
|
||||
const zhTillVersion = process.argv[4];
|
||||
const frontendTillVersion = process.argv[5];
|
||||
|
||||
const changelogs = [
|
||||
{tillVersion: z2mTillVersion, project: 'koenkk/zigbee2mqtt',
|
||||
contents: fs.readFileSync(path.join(__dirname, '..', 'CHANGELOG.md'), 'utf-8').split('\n')},
|
||||
{tillVersion: zhcTillVersion, project: 'koenkk/zigbee-herdsman-converters',
|
||||
contents: fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'zigbee-herdsman-converters', 'CHANGELOG.md'), 'utf-8').split('\n')},
|
||||
{tillVersion: zhTillVersion, project: 'koenkk/zigbee-herdsman',
|
||||
contents: fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'zigbee-herdsman', 'CHANGELOG.md'), 'utf-8').split('\n')},
|
||||
{tillVersion: frontendTillVersion, project: 'nurikk/zigbee2mqtt-frontend', isFrontend: true,
|
||||
contents: fs.readFileSync(path.join(__dirname, '..', 'node_modules', 'zigbee2mqtt-frontend', 'CHANGELOG.md'), 'utf-8').split('\n')},
|
||||
];
|
||||
|
||||
const releaseRe = /## \[(.+)\]/;
|
||||
const changes = {features: [], fixes: [], detect: [], add: [], error: [], frontend: []};
|
||||
let context = null;
|
||||
const changeRe = [
|
||||
/^\* (\*\*(.+):\*\*)?(.+)\((\[#\d+\]\(.+\))\) \(\[.+\]\(https:.+\/(.+)\)\)$/,
|
||||
/^\* (\*\*(.+):\*\*)?(.+)(https:\/\/github\.com.+) \(\[.+\]\(https:.+\/(.+)\)\)$/,
|
||||
/^\* (\*\*(.+):\*\*)?(.+)() \(\[.+\]\(https:.+\/(.+)\)\)$/,
|
||||
];
|
||||
const frontendChangeRe = /^\* (\*\*.+:\*\* )()?(.+) \(\[.+\]\(https:.+\/()(.+)\)\)(.+)?$/;
|
||||
|
||||
let commitUserLookup = {};
|
||||
const commitUserFile = path.join(__dirname, 'commit-user-lookup.json');
|
||||
if (fs.existsSync(commitUserFile)) {
|
||||
commitUserLookup = JSON.parse(fs.readFileSync(commitUserFile, 'utf8'));
|
||||
}
|
||||
|
||||
for (const changelog of changelogs) {
|
||||
for (const line of changelog.contents) {
|
||||
const releaseMatch = line.match(releaseRe);
|
||||
const changeMatch = changelog.isFrontend ? line.match(frontendChangeRe) : changeRe.map((re) => line.match(re)).find((e) => e);
|
||||
if (releaseMatch) {
|
||||
if (releaseMatch[1] === changelog.tillVersion) {
|
||||
break;
|
||||
}
|
||||
} else if (line === '### Features') {
|
||||
context = 'features';
|
||||
} else if (line === '### Bug Fixes') {
|
||||
context = 'fixes';
|
||||
} else if (line.startsWith('* **ignore:**')) {
|
||||
continue;
|
||||
} else if (changeMatch) {
|
||||
let localContext = changelog.isFrontend ? 'frontend' : (changeMatch[2] ? changeMatch[2] : context);
|
||||
if (!changes[localContext]) localContext = 'error';
|
||||
|
||||
const commit = changeMatch[5];
|
||||
const commitUserKey = `${changelog.project}-${commit} `;
|
||||
let user = commitUserKey in commitUserLookup ? commitUserLookup[commitUserKey] :
|
||||
execSync(`curl -s https://api.github.com/repos/${changelog.project}/commits/${commit} | jq -r '.author.login'`).toString().trim();
|
||||
if (user !== 'null') commitUserLookup[commitUserKey] = user;
|
||||
const messages = [];
|
||||
let message = changeMatch[3].trim();
|
||||
if (message.endsWith('.')) message = message.substring(0, message.length - 1);
|
||||
message = message.charAt(0).toUpperCase() + message.slice(1);
|
||||
|
||||
if (changelog.isFrontend) {
|
||||
changes[localContext].push(`- [${commit.slice(0, 7)}](https://github.com/${changelog.project}/commit/${commit}) ${message} (@${user})`);
|
||||
messages.push(message);
|
||||
} else {
|
||||
const otherUser = message.match(/\[@(.+)\]\(https:\/\/github.com\/.+\)/) || message.match(/@(.+)/);
|
||||
if (otherUser) {
|
||||
user = otherUser[1];
|
||||
message = message.replace(otherUser[0], '');
|
||||
}
|
||||
|
||||
if (localContext === 'add') {
|
||||
for (const model of message.split(',')) {
|
||||
const definition = zhc.definitions.find((d) => d.model === model.trim());
|
||||
if (definition) {
|
||||
messages.push(`\`${definition.model}\` ${definition.vendor} ${definition.description}`);
|
||||
} else {
|
||||
changes['error'].push(`${line} (model '${model}' does not exist)`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
messages.push(message);
|
||||
}
|
||||
|
||||
let issue = changeMatch[4].trim();
|
||||
if (issue && !issue.startsWith('[#')) issue = `[#${issue.split('/').pop()}](${issue})`;
|
||||
if (!issue) {
|
||||
issue = '_NO_ISSUE_';
|
||||
localContext = 'error';
|
||||
}
|
||||
|
||||
messages.forEach((m) => changes[localContext].push(`- ${issue} ${m} (@${user})`));
|
||||
}
|
||||
} else if (line === '# Changelog' || line === '### ⚠ BREAKING CHANGES' || !line) {
|
||||
continue;
|
||||
} else {
|
||||
changes['error'].push(`- Unmatched line: ${line}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = '';
|
||||
const names = [
|
||||
['features', 'Improvements'],
|
||||
['fixes', 'Fixes'],
|
||||
['frontend', 'Frontend'],
|
||||
['add', 'New supported devices'],
|
||||
['detect', 'Fixed device detections'],
|
||||
['error', 'Changelog generator error'],
|
||||
];
|
||||
for (const name of names) {
|
||||
result += `# ${name[1]}\n`;
|
||||
if (name[0] === 'add') {
|
||||
result += `This release adds support for ${changes['add'].length} devices: \n`;
|
||||
}
|
||||
changes[name[0]].forEach((e) => result += `${e}\n`);
|
||||
result += '\n';
|
||||
}
|
||||
|
||||
fs.writeFileSync(commitUserFile, JSON.stringify(commitUserLookup), 'utf-8');
|
||||
|
||||
console.log(result.trim());
|
||||
@@ -1,12 +0,0 @@
|
||||
const fs = require('fs');
|
||||
const filename = process.argv[2];
|
||||
const text = fs.readFileSync(filename, 'utf8');
|
||||
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
|
||||
|
||||
|
||||
for (const line of text.split('\n')) {
|
||||
const [modelNumber, user] = line.split(',').map((t) => t.trim());
|
||||
const model = zigbeeHerdsmanConverters.devices.find((d) => d.model === modelNumber);
|
||||
if (!model) throw new Error(`${line} does not exist`);
|
||||
console.log(`- \`${modelNumber}\` ${model.vendor} ${model.description} (${user})`);
|
||||
}
|
||||
+4
-2
@@ -24,7 +24,7 @@ echo "Cloning Zigbee2MQTT git repository"
|
||||
git clone https://github.com/Koenkk/zigbee2mqtt.git /opt/zigbee2mqtt
|
||||
chown -R pi:pi /opt/zigbee2mqtt
|
||||
|
||||
echo "Running install. This might take a while and can produce som expected errors"
|
||||
echo "Running install. This might take a while and can produce some expected errors"
|
||||
cd /opt/zigbee2mqtt || exit
|
||||
su pi -c "npm ci"
|
||||
|
||||
@@ -36,10 +36,12 @@ Description=zigbee2mqtt
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/usr/bin/npm start
|
||||
Type=notify
|
||||
ExecStart=/usr/bin/node index.js
|
||||
WorkingDirectory=/opt/zigbee2mqtt
|
||||
StandardOutput=inherit
|
||||
StandardError=inherit
|
||||
WatchdogSec=10s
|
||||
Restart=always
|
||||
User=pi
|
||||
|
||||
|
||||
+46
-21
@@ -6,6 +6,7 @@ import zigbeeHerdsman from './stub/zigbeeHerdsman';
|
||||
import utils from '../lib/util/utils';
|
||||
import * as settings from '../lib/util/settings';
|
||||
import Controller from '../lib/controller';
|
||||
import Availability from '../lib/extension/availability';
|
||||
import flushPromises from './lib/flushPromises';
|
||||
import stringify from 'json-stable-stringify-without-jsonify';
|
||||
|
||||
@@ -58,13 +59,13 @@ describe('Availability', () => {
|
||||
jest.useRealTimers();
|
||||
})
|
||||
|
||||
it('Should publish availabilty on startup for device where it is enabled for', async () => {
|
||||
it('Should publish availability on startup for device where it is enabled for', async () => {
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
|
||||
'online', {retain: true, qos: 0}, expect.any(Function));
|
||||
'online', {retain: true, qos: 1}, expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
|
||||
'online', {retain: true, qos: 0}, expect.any(Function));
|
||||
'online', {retain: true, qos: 1}, expect.any(Function));
|
||||
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/bulb_color_2/availability',
|
||||
'online', {retain: true, qos: 0}, expect.any(Function));
|
||||
'online', {retain: true, qos: 1}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Should publish offline for active device when not seen for 10 minutes', async () => {
|
||||
@@ -77,7 +78,7 @@ describe('Availability', () => {
|
||||
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
|
||||
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
|
||||
'offline', {retain: true, qos: 0}, expect.any(Function));
|
||||
'offline', {retain: true, qos: 1}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Shouldnt do anything for a device when availability: false is set for device', async () => {
|
||||
@@ -93,7 +94,7 @@ describe('Availability', () => {
|
||||
await advancedTime(utils.hours(26));
|
||||
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
|
||||
'offline', {retain: true, qos: 0}, expect.any(Function));
|
||||
'offline', {retain: true, qos: 1}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Should reset ping timer when device last seen changes for active device', async () => {
|
||||
@@ -111,7 +112,7 @@ describe('Availability', () => {
|
||||
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
|
||||
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
|
||||
'offline', {retain: true, qos: 0}, expect.any(Function));
|
||||
'offline', {retain: true, qos: 1}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Should ping again when first ping fails', async () => {
|
||||
@@ -132,7 +133,7 @@ describe('Availability', () => {
|
||||
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(1, true);
|
||||
expect(devices.bulb_color.ping).toHaveBeenNthCalledWith(2, false);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
|
||||
'offline', {retain: true, qos: 0}, expect.any(Function));
|
||||
'offline', {retain: true, qos: 1}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Should reset ping timer when device last seen changes for passive device', async () => {
|
||||
@@ -150,7 +151,7 @@ describe('Availability', () => {
|
||||
await advancedTime(utils.hours(3));
|
||||
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
|
||||
'offline', {retain: true, qos: 0}, expect.any(Function));
|
||||
'offline', {retain: true, qos: 1}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Should immediately mark device as online when it lastSeen changes', async () => {
|
||||
@@ -158,13 +159,13 @@ describe('Availability', () => {
|
||||
|
||||
await advancedTime(utils.minutes(15));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
|
||||
'offline', {retain: true, qos: 0}, expect.any(Function));
|
||||
'offline', {retain: true, qos: 1}, expect.any(Function));
|
||||
|
||||
devices.bulb_color.lastSeen = Date.now();
|
||||
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color});
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
|
||||
'online', {retain: true, qos: 0}, expect.any(Function));
|
||||
'online', {retain: true, qos: 1}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Should allow to change availability timeout via device options', async () => {
|
||||
@@ -236,7 +237,7 @@ describe('Availability', () => {
|
||||
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('Should to enable availabilty for just one device', async () => {
|
||||
it('Should to enable availability for just one device', async () => {
|
||||
settings.set(['availability'], false);
|
||||
settings.set(['devices', devices.bulb_color.ieeeAddr, 'availability'], true);
|
||||
|
||||
@@ -283,22 +284,22 @@ describe('Availability', () => {
|
||||
await flushPromises();
|
||||
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_color/availability',
|
||||
null, {retain: true, qos: 0}, expect.any(Function));
|
||||
null, {retain: true, qos: 1}, expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability',
|
||||
'online', {retain: true, qos: 0}, expect.any(Function));
|
||||
'online', {retain: true, qos: 1}, expect.any(Function));
|
||||
await advancedTime(utils.hours(12));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb_new_name/availability',
|
||||
'offline', {retain: true, qos: 0}, expect.any(Function));
|
||||
'offline', {retain: true, qos: 1}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Should publish availabiltiy payload in JSON format', async () => {
|
||||
it('Should publish availability payload in JSON format', async () => {
|
||||
settings.set(['advanced', 'legacy_availability_payload'], false);
|
||||
await resetExtension();
|
||||
MQTT.publish.mockClear();
|
||||
await advancedTime(utils.hours(26));
|
||||
expect(devices.remote.ping).toHaveBeenCalledTimes(0);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote/availability',
|
||||
stringify({state: 'offline'}), {retain: true, qos: 0}, expect.any(Function));
|
||||
stringify({state: 'offline'}), {retain: true, qos: 1}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Deprecated - should allow to block via advanced.availability_blocklist', async () => {
|
||||
@@ -328,20 +329,44 @@ describe('Availability', () => {
|
||||
expect(devices.bulb_color.ping).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('Should publish availabilty for groups', async () => {
|
||||
it('Should publish availability for groups', async () => {
|
||||
settings.set(['devices', devices.bulb_color_2.ieeeAddr, 'availability'], true);
|
||||
await resetExtension();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability',
|
||||
'online', {retain: true, qos: 0}, expect.any(Function));
|
||||
'online', {retain: true, qos: 1}, expect.any(Function));
|
||||
MQTT.publish.mockClear();
|
||||
await advancedTime(utils.minutes(12));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability',
|
||||
'offline', {retain: true, qos: 0}, expect.any(Function));
|
||||
'offline', {retain: true, qos: 1}, expect.any(Function));
|
||||
MQTT.publish.mockClear();
|
||||
devices.bulb_color_2.lastSeen = Date.now();
|
||||
await zigbeeHerdsman.events.lastSeenChanged({device: devices.bulb_color_2});
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/group_tradfri_remote/availability',
|
||||
'online', {retain: true, qos: 0}, expect.any(Function));
|
||||
'online', {retain: true, qos: 1}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Should clear the ping queue on stop', async () => {
|
||||
const availability = controller.extensions.find((extension) => extension instanceof Availability);
|
||||
const publishAvailabilitySpy = jest.spyOn(availability, 'publishAvailability');
|
||||
|
||||
devices.bulb_color.zh = { ping: jest.fn().mockImplementation(() => new Promise((resolve) => setTimeout(resolve, 1000)))};
|
||||
availability.addToPingQueue(devices.bulb_color);
|
||||
availability.addToPingQueue(devices.bulb_color_2);
|
||||
|
||||
await availability.stop();
|
||||
await advancedTime(utils.minutes(1));
|
||||
|
||||
expect(availability.pingQueue).toEqual([]);
|
||||
// Validate the stop-interrupt implicitly by checking that it prevents further function invocations
|
||||
expect(publishAvailabilitySpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should prevent instance restart', async () => {
|
||||
const availability = controller.extensions.find((extension) => extension instanceof Availability);
|
||||
|
||||
await availability.stop();
|
||||
|
||||
await expect(() => availability.start()).rejects.toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
+31
-3
@@ -157,7 +157,7 @@ describe('Bind', () => {
|
||||
expect(target.configureReporting).toHaveBeenCalledWith("genOnOff",[{"attribute": "onOff", "maximumReportInterval": 0xFFFF, "minimumReportInterval": 0, "reportableChange": 0}]);
|
||||
expect(target.configureReporting).toHaveBeenCalledWith("genLevelCtrl",[{"attribute": "currentLevel", "maximumReportInterval": 0xFFFF, "minimumReportInterval": 5, "reportableChange": 1}]);
|
||||
expect(target.configureReporting).toHaveBeenCalledWith("lightingColorCtrl",[{"attribute":"colorTemperature","minimumReportInterval":5,"maximumReportInterval":0xFFFF,"reportableChange":1},{"attribute":"currentX","minimumReportInterval":5,"maximumReportInterval":0xFFFF,"reportableChange":1},{"attribute":"currentY","minimumReportInterval":5,"maximumReportInterval":0xFFFF,"reportableChange":1}]);
|
||||
expect(zigbeeHerdsman.devices.bulb_color.meta.configured).toBe(2);
|
||||
expect(zigbeeHerdsman.devices.bulb_color.meta.configured).toBe(1324213189);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/unbind',
|
||||
stringify({"data":{"from":"remote","to":"bulb_color","clusters":["genScenes","genOnOff","genLevelCtrl"],"failed":[]},"status":"ok"}),
|
||||
@@ -387,7 +387,7 @@ describe('Bind', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Error bind fails when source not existing', async () => {
|
||||
it('Error bind fails when source device does not exist', async () => {
|
||||
const device = zigbeeHerdsman.devices.remote;
|
||||
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
|
||||
const endpoint = device.getEndpoint(1);
|
||||
@@ -401,7 +401,21 @@ describe('Bind', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Error bind fails when target not existing', async () => {
|
||||
it("Error bind fails when source device's endpoint does not exist", async () => {
|
||||
const device = zigbeeHerdsman.devices.remote;
|
||||
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
|
||||
const endpoint = device.getEndpoint(1);
|
||||
mockClear(device);
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote/not_existing_endpoint', to: 'bulb_color'}));
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({"data":{"from":"remote/not_existing_endpoint","to":"bulb_color"},"status":"error","error":"Source device 'remote' does not have endpoint 'not_existing_endpoint'"}),
|
||||
{retain: false, qos: 0}, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Error bind fails when target device or group does not exist', async () => {
|
||||
const device = zigbeeHerdsman.devices.remote;
|
||||
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
|
||||
const endpoint = device.getEndpoint(1);
|
||||
@@ -415,6 +429,20 @@ describe('Bind', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("Error bind fails when target device's endpoint does not exist", async () => {
|
||||
const device = zigbeeHerdsman.devices.remote;
|
||||
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
|
||||
const endpoint = device.getEndpoint(1);
|
||||
mockClear(device);
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/device/bind', stringify({from: 'remote', to: 'bulb_color/not_existing_endpoint'}));
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/bind',
|
||||
stringify({"data":{"from":"remote","to":"bulb_color/not_existing_endpoint"},"status":"error","error":"Target device 'bulb_color' does not have endpoint 'not_existing_endpoint'"}),
|
||||
{retain: false, qos: 0}, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Legacy api: Should bind', async () => {
|
||||
const device = zigbeeHerdsman.devices.remote;
|
||||
const target = zigbeeHerdsman.devices.bulb_color.getEndpoint(1);
|
||||
|
||||
+110
-12
File diff suppressed because one or more lines are too long
@@ -24,7 +24,7 @@ describe('Configure', () => {
|
||||
const endpoint2 = device.getEndpoint(2);
|
||||
expect(endpoint2.write).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint2.write).toHaveBeenCalledWith("genBasic", {"49": {"type": 25, "value": 11}}, {"disableDefaultResponse": true, "manufacturerCode": 4107});
|
||||
expect(device.meta.configured).toBe(1);
|
||||
expect(device.meta.configured).toBe(1324213189);
|
||||
}
|
||||
|
||||
const expectBulbConfigured = () => {
|
||||
@@ -176,11 +176,11 @@ describe('Configure', () => {
|
||||
});
|
||||
|
||||
it('Fail to configure via MQTT when device has no configure', async () => {
|
||||
await MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: "0x90fd9ffffe4b64ax", transaction: 20}));
|
||||
await MQTT.events.message('zigbee2mqtt/bridge/request/device/configure', stringify({id: "0x0017882104a44559", transaction: 20}));
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/configure',
|
||||
stringify({"data":{"id": "0x90fd9ffffe4b64ax"},"status":"error","error": "Device 'ZNLDP12LM' cannot be configured","transaction":20}),
|
||||
stringify({"data":{"id": "0x0017882104a44559"},"status":"error","error": "Device 'TS0601_thermostat' cannot be configured","transaction":20}),
|
||||
{retain: false, qos: 0}, expect.any(Function)
|
||||
);
|
||||
});
|
||||
@@ -200,9 +200,9 @@ describe('Configure', () => {
|
||||
});
|
||||
|
||||
it('Legacy api: Should skip reconfigure when device does not require this', async () => {
|
||||
await MQTT.events.message('zigbee2mqtt/bridge/configure', '0x90fd9ffffe4b64ax');
|
||||
await MQTT.events.message('zigbee2mqtt/bridge/configure', '0x0017882104a44559');
|
||||
await flushPromises();
|
||||
expect(logger.warn).toHaveBeenCalledWith(`Skipping configure of 'ZNLDP12LM', device does not require this.`)
|
||||
expect(logger.warn).toHaveBeenCalledWith(`Skipping configure of 'TS0601_thermostat', device does not require this.`)
|
||||
});
|
||||
|
||||
it('Should not configure when interview not completed', async () => {
|
||||
|
||||
+30
-7
@@ -1,3 +1,4 @@
|
||||
process.env.NOTIFY_SOCKET = "mocked";
|
||||
const data = require('./stub/data');
|
||||
const logger = require('./stub/logger');
|
||||
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
|
||||
@@ -16,6 +17,16 @@ const mocksClear = [
|
||||
|
||||
const fs = require('fs');
|
||||
|
||||
jest.mock('sd-notify', () => {
|
||||
return {
|
||||
watchdogInterval: () => {return 3000;},
|
||||
startWatchdogMode: (interval) => {},
|
||||
stopWatchdogMode: () => {},
|
||||
ready: () => {},
|
||||
stopping: () => {},
|
||||
};
|
||||
}, {virtual: true});
|
||||
|
||||
describe('Controller', () => {
|
||||
let controller;
|
||||
let mockExit;
|
||||
@@ -52,7 +63,7 @@ describe('Controller', () => {
|
||||
expect(logger.info).toHaveBeenCalledWith('remote (0x0017880104e45517): 324131092621 - Philips Hue dimmer switch (EndDevice)');
|
||||
expect(logger.info).toHaveBeenCalledWith('0x0017880104e45518 (0x0017880104e45518): Not supported (EndDevice)');
|
||||
expect(MQTT.connect).toHaveBeenCalledTimes(1);
|
||||
expect(MQTT.connect).toHaveBeenCalledWith("mqtt://localhost", {"will": {"payload": "offline", "retain": true, "topic": "zigbee2mqtt/bridge/state", "qos": 1}});
|
||||
expect(MQTT.connect).toHaveBeenCalledWith("mqtt://localhost", {"will": {"payload": Buffer.from("offline"), "retain": true, "topic": "zigbee2mqtt/bridge/state", "qos": 1}});
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bulb', stringify({"state":"ON","brightness":50,"color_temp":370,"linkquality":99}),{ retain: true, qos: 0 }, expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/remote', stringify({"brightness":255}), { retain: true, qos: 0 }, expect.any(Function));
|
||||
});
|
||||
@@ -88,7 +99,7 @@ describe('Controller', () => {
|
||||
await flushPromises();
|
||||
expect(MQTT.connect).toHaveBeenCalledTimes(1);
|
||||
const expected = {
|
||||
"will": {"payload": "offline", "retain": true, "topic": "zigbee2mqtt/bridge/state", "qos": 1},
|
||||
"will": {"payload": Buffer.from("offline"), "retain": true, "topic": "zigbee2mqtt/bridge/state", "qos": 1},
|
||||
keepalive: 30,
|
||||
ca: Buffer.from([99, 97]),
|
||||
key: Buffer.from([107, 101, 121]),
|
||||
@@ -103,14 +114,17 @@ describe('Controller', () => {
|
||||
expect(MQTT.connect).toHaveBeenCalledWith("mqtt://localhost", expected);
|
||||
});
|
||||
|
||||
it('Should generate network_key and pan_id when set to GENERATE', async () => {
|
||||
it('Should generate network_key, pan_id and ext_pan_id when set to GENERATE', async () => {
|
||||
settings.set(['advanced', 'network_key'], 'GENERATE');
|
||||
settings.set(['advanced', 'pan_id'], 'GENERATE');
|
||||
settings.set(['advanced', 'ext_pan_id'], 'GENERATE');
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
expect(zigbeeHerdsman.constructor.mock.calls[0][0].network.networkKey.length).toStrictEqual(16);
|
||||
expect(zigbeeHerdsman.constructor.mock.calls[0][0].network.extendedPanID.length).toStrictEqual(8);
|
||||
expect(zigbeeHerdsman.constructor.mock.calls[0][0].network.panID).toStrictEqual(expect.any(Number));
|
||||
expect(data.read().advanced.network_key.length).toStrictEqual(16);
|
||||
expect(data.read().advanced.ext_pan_id.length).toStrictEqual(8);
|
||||
expect(data.read().advanced.pan_id).toStrictEqual(expect.any(Number));
|
||||
});
|
||||
|
||||
@@ -147,7 +161,6 @@ describe('Controller', () => {
|
||||
logger.error.mockClear();
|
||||
controller.mqtt.client.reconnecting = true;
|
||||
jest.advanceTimersByTime(11 * 1000);
|
||||
await flushPromises();
|
||||
expect(logger.error).toHaveBeenCalledWith("Not connected to MQTT server!");
|
||||
controller.mqtt.client.reconnecting = false;
|
||||
});
|
||||
@@ -544,7 +557,7 @@ describe('Controller', () => {
|
||||
device = controller.zigbee.resolveEntity('unsupported2');
|
||||
await controller.publishEntityState(device, {state: 'ON'});
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/unsupported2', stringify({"state":"ON","device":{"friendlyName":"unsupported2","model":"unknown","ieeeAddr":"0x0017880104e45529","networkAddress":6536,"type":"EndDevice","manufacturerID":0,"powerSource":"Battery","dateCode":null, "softwareBuildID": null}}), {"qos": 0, "retain": false}, expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/unsupported2', stringify({"state":"ON","device":{"friendlyName":"unsupported2","model":"notSupportedModelID","ieeeAddr":"0x0017880104e45529","networkAddress":6536,"type":"EndDevice","manufacturerID":0,"powerSource":"Battery","dateCode":null, "softwareBuildID": null}}), {"qos": 0, "retain": false}, expect.any(Function));
|
||||
});
|
||||
|
||||
it('Should publish entity state without retain', async () => {
|
||||
@@ -641,7 +654,7 @@ describe('Controller', () => {
|
||||
await flushPromises();
|
||||
expect(MQTT.connect).toHaveBeenCalledTimes(1);
|
||||
const expected = {
|
||||
"will": { "payload": "offline", "retain": false, "topic": "zigbee2mqtt/bridge/state", "qos": 1 },
|
||||
"will": { "payload": Buffer.from("offline"), "retain": false, "topic": "zigbee2mqtt/bridge/state", "qos": 1 },
|
||||
}
|
||||
expect(MQTT.connect).toHaveBeenCalledWith("mqtt://localhost", expected);
|
||||
});
|
||||
@@ -661,7 +674,7 @@ describe('Controller', () => {
|
||||
MQTT.publish.mockClear();
|
||||
MQTT.events['connect']();
|
||||
await flushPromises();
|
||||
await MQTT.events.message('zigbee2mqtt/bridge/state', 'online');
|
||||
await MQTT.events.message('zigbee2mqtt/bridge/info', 'dummy');
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith('zigbee2mqtt/bridge/state', expect.any(String), { retain: true, qos: 0 }, expect.any(Function));
|
||||
@@ -728,4 +741,14 @@ describe('Controller', () => {
|
||||
await controller.stop();
|
||||
expect(controller.state.state[device.ieeeAddr]).toStrictEqual(undefined);
|
||||
});
|
||||
|
||||
it('EventBus should handle errors', async () => {
|
||||
const eventbus = controller.eventBus;
|
||||
const callback = jest.fn().mockImplementation(async () => {throw new Error('Whoops!')});
|
||||
eventbus.onStateChange('test', callback);
|
||||
eventbus.emitStateChange({});
|
||||
await flushPromises();
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
expect(logger.error).toHaveBeenCalledWith(`EventBus error 'String/stateChange': Whoops!`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,9 +10,9 @@ const flushPromises = require('./lib/flushPromises');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
zigbeeHerdsmanConverters.addDeviceDefinition = jest.fn();
|
||||
zigbeeHerdsmanConverters.addDefinition = jest.fn();
|
||||
|
||||
const mocksClear = [zigbeeHerdsmanConverters.addDeviceDefinition, zigbeeHerdsman.permitJoin,
|
||||
const mocksClear = [zigbeeHerdsmanConverters.addDefinition, zigbeeHerdsman.permitJoin,
|
||||
mockExit, MQTT.end, zigbeeHerdsman.stop, logger.debug,
|
||||
MQTT.publish, MQTT.connect, zigbeeHerdsman.devices.bulb_color.removeFromNetwork,
|
||||
zigbeeHerdsman.devices.bulb.removeFromNetwork, logger.error,
|
||||
@@ -67,16 +67,15 @@ describe('Loads external converters', () => {
|
||||
it('Does not load external converters', async () => {
|
||||
settings.set(['external_converters'], []);
|
||||
await resetExtension();
|
||||
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(0);
|
||||
expect(zigbeeHerdsmanConverters.addDefinition).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('Loads external converters', async () => {
|
||||
fs.copyFileSync(path.join(__dirname, 'assets', 'mock-external-converter.js'), path.join(data.mockDir, 'mock-external-converter.js'));
|
||||
const devicesCount = zigbeeHerdsman.devices.lenght;
|
||||
settings.set(['external_converters'], ['mock-external-converter.js']);
|
||||
await resetExtension();
|
||||
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(1);
|
||||
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledWith({
|
||||
expect(zigbeeHerdsmanConverters.addDefinition).toHaveBeenCalledTimes(1);
|
||||
expect(zigbeeHerdsmanConverters.addDefinition).toHaveBeenCalledWith({
|
||||
mock: true,
|
||||
zigbeeModel: ['external_converter_device'],
|
||||
vendor: 'external',
|
||||
@@ -90,11 +89,10 @@ describe('Loads external converters', () => {
|
||||
|
||||
it('Loads multiple external converters', async () => {
|
||||
fs.copyFileSync(path.join(__dirname, 'assets', 'mock-external-converter-multiple.js'), path.join(data.mockDir, 'mock-external-converter-multiple.js'));
|
||||
const devicesCount = zigbeeHerdsman.devices.lenght;
|
||||
settings.set(['external_converters'], ['mock-external-converter-multiple.js']);
|
||||
await resetExtension();
|
||||
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(2);
|
||||
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenNthCalledWith(1, {
|
||||
expect(zigbeeHerdsmanConverters.addDefinition).toHaveBeenCalledTimes(2);
|
||||
expect(zigbeeHerdsmanConverters.addDefinition).toHaveBeenNthCalledWith(1, {
|
||||
mock: 1,
|
||||
model: 'external_converters_device_1',
|
||||
zigbeeModel: ['external_converter_device_1'],
|
||||
@@ -104,7 +102,7 @@ describe('Loads external converters', () => {
|
||||
toZigbee: [],
|
||||
exposes: []
|
||||
});
|
||||
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenNthCalledWith(2, {
|
||||
expect(zigbeeHerdsmanConverters.addDefinition).toHaveBeenNthCalledWith(2, {
|
||||
mock: 2,
|
||||
model: 'external_converters_device_2',
|
||||
zigbeeModel: ['external_converter_device_2'],
|
||||
@@ -119,8 +117,8 @@ describe('Loads external converters', () => {
|
||||
it('Loads external converters from package', async () => {
|
||||
settings.set(['external_converters'], ['mock-external-converter-module']);
|
||||
await resetExtension();
|
||||
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(1);
|
||||
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledWith({
|
||||
expect(zigbeeHerdsmanConverters.addDefinition).toHaveBeenCalledTimes(1);
|
||||
expect(zigbeeHerdsmanConverters.addDefinition).toHaveBeenCalledWith({
|
||||
mock: true
|
||||
});
|
||||
});
|
||||
@@ -128,12 +126,20 @@ describe('Loads external converters', () => {
|
||||
it('Loads multiple external converters from package', async () => {
|
||||
settings.set(['external_converters'], ['mock-multiple-external-converter-module']);
|
||||
await resetExtension();
|
||||
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenCalledTimes(2);
|
||||
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenNthCalledWith(1, {
|
||||
expect(zigbeeHerdsmanConverters.addDefinition).toHaveBeenCalledTimes(2);
|
||||
expect(zigbeeHerdsmanConverters.addDefinition).toHaveBeenNthCalledWith(1, {
|
||||
mock: 1
|
||||
});
|
||||
expect(zigbeeHerdsmanConverters.addDeviceDefinition).toHaveBeenNthCalledWith(2, {
|
||||
expect(zigbeeHerdsmanConverters.addDefinition).toHaveBeenNthCalledWith(2, {
|
||||
mock: 2
|
||||
});
|
||||
});
|
||||
|
||||
it('Loads external converters with error', async () => {
|
||||
fs.copyFileSync(path.join(__dirname, 'assets', 'mock-external-converter.js'), path.join(data.mockDir, 'mock-external-converter.js'));
|
||||
settings.set(['external_converters'], ['mock-external-converter.js']);
|
||||
zigbeeHerdsmanConverters.addDefinition.mockImplementationOnce(() => {throw new Error('Invalid definition!')});
|
||||
await resetExtension();
|
||||
expect(logger.error).toHaveBeenCalledWith(`Failed to load external converter file 'mock-external-converter.js' (Invalid definition!)`);
|
||||
});
|
||||
});
|
||||
@@ -3,7 +3,7 @@ const logger = require('./stub/logger');
|
||||
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
|
||||
const MQTT = require('./stub/mqtt');
|
||||
const path = require('path');
|
||||
const rimraf = require('rimraf');
|
||||
const {rimrafSync} = require('rimraf');
|
||||
const settings = require('../lib/util/settings');
|
||||
const Controller = require('../lib/controller');
|
||||
const stringify = require('json-stable-stringify-without-jsonify');
|
||||
@@ -45,7 +45,7 @@ describe('User extensions', () => {
|
||||
});
|
||||
afterEach(() => {
|
||||
const extensionPath = path.join(data.mockDir, 'extension');
|
||||
rimraf.sync(extensionPath);
|
||||
rimrafSync(extensionPath);
|
||||
})
|
||||
|
||||
it('Load user extension', async () => {
|
||||
|
||||
+57
-1
@@ -1,5 +1,5 @@
|
||||
const data = require('./stub/data');
|
||||
require('./stub/logger');
|
||||
const logger = require('./stub/logger');
|
||||
require('./stub/zigbeeHerdsman');
|
||||
const MQTT = require('./stub/mqtt');
|
||||
const settings = require('../lib/util/settings');
|
||||
@@ -59,6 +59,7 @@ jest.mock('http', () => ({
|
||||
mockHTTP.variables.onRequest = onRequest;
|
||||
return mockHTTP.implementation;
|
||||
}),
|
||||
Agent: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('https', () => ({
|
||||
@@ -66,6 +67,7 @@ jest.mock('https', () => ({
|
||||
mockHTTPS.variables.onRequest = onRequest;
|
||||
return mockHTTPS.implementation;
|
||||
}),
|
||||
Agent: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock("connect-gzip-static", () =>
|
||||
@@ -128,6 +130,56 @@ describe('Frontend', () => {
|
||||
expect(mockWSClient.implementation.terminate).toHaveBeenCalledTimes(1);
|
||||
expect(mockHTTP.implementation.close).toHaveBeenCalledTimes(1);
|
||||
expect(mockWS.implementation.close).toHaveBeenCalledTimes(1);
|
||||
mockWS.implementation.close.mockClear();
|
||||
mockHTTP.implementation.close.mockClear();
|
||||
mockHTTP.implementation.listen.mockClear();
|
||||
mockHTTPS.implementation.listen.mockClear();
|
||||
});
|
||||
|
||||
it('Start/stop without host', async () => {
|
||||
settings.set(['frontend'], {port: 8081});
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
await controller.start();
|
||||
expect(mockNodeStatic.variables.path).toBe("my/dummy/path");
|
||||
expect(mockHTTP.implementation.listen).toHaveBeenCalledWith(8081);
|
||||
const mockWSClient = {
|
||||
implementation: {
|
||||
terminate: jest.fn(),
|
||||
send: jest.fn(),
|
||||
},
|
||||
events: {},
|
||||
};
|
||||
mockWS.implementation.clients.push(mockWSClient.implementation);
|
||||
await controller.stop();
|
||||
expect(mockWSClient.implementation.terminate).toHaveBeenCalledTimes(1);
|
||||
expect(mockHTTP.implementation.close).toHaveBeenCalledTimes(1);
|
||||
expect(mockWS.implementation.close).toHaveBeenCalledTimes(1);
|
||||
mockWS.implementation.close.mockClear();
|
||||
mockHTTP.implementation.close.mockClear();
|
||||
mockHTTP.implementation.listen.mockClear();
|
||||
mockHTTPS.implementation.listen.mockClear();
|
||||
});
|
||||
|
||||
it('Start/stop unix socket', async () => {
|
||||
settings.set(['frontend'], {host: "/tmp/zigbee2mqtt.sock"});
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
await controller.start();
|
||||
expect(mockNodeStatic.variables.path).toBe("my/dummy/path");
|
||||
expect(mockHTTP.implementation.listen).toHaveBeenCalledWith("/tmp/zigbee2mqtt.sock");
|
||||
const mockWSClient = {
|
||||
implementation: {
|
||||
terminate: jest.fn(),
|
||||
send: jest.fn(),
|
||||
},
|
||||
events: {},
|
||||
};
|
||||
mockWS.implementation.clients.push(mockWSClient.implementation);
|
||||
await controller.stop();
|
||||
expect(mockWSClient.implementation.terminate).toHaveBeenCalledTimes(1);
|
||||
expect(mockHTTP.implementation.close).toHaveBeenCalledTimes(1);
|
||||
expect(mockWS.implementation.close).toHaveBeenCalledTimes(1);
|
||||
mockWS.implementation.close.mockClear();
|
||||
mockHTTP.implementation.close.mockClear();
|
||||
mockHTTP.implementation.listen.mockClear();
|
||||
mockHTTPS.implementation.listen.mockClear();
|
||||
});
|
||||
@@ -206,6 +258,10 @@ describe('Frontend', () => {
|
||||
mockWSClient.events.message(null, false);
|
||||
await flushPromises();
|
||||
|
||||
// Error
|
||||
mockWSClient.events.error(new Error('This is an error'));
|
||||
expect(logger.error).toHaveBeenCalledWith('WebSocket error: This is an error');
|
||||
|
||||
// Received message on socket
|
||||
expect(mockWSClient.implementation.send).toHaveBeenCalledTimes(1);
|
||||
expect(mockWSClient.implementation.send).toHaveBeenCalledWith(stringify({topic: 'bulb_color', payload: {state: 'ON', power_on_behavior:null, linkquality: null, update_available: null, update: {state: null, installed_version: -1, latest_version: -1}}}));
|
||||
|
||||
+25
-3
@@ -41,7 +41,7 @@ describe('Groups', () => {
|
||||
settings.reRead();
|
||||
MQTT.publish.mockClear();
|
||||
zigbeeHerdsman.groups.gledopto_group.command.mockClear();
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.__clearStore__();
|
||||
zigbeeHerdsmanConverters.toZigbee.__clearStore__();
|
||||
controller.state.state = {};
|
||||
})
|
||||
|
||||
@@ -273,7 +273,7 @@ describe('Groups', () => {
|
||||
expect(logger.error).toHaveBeenCalledWith("Group 'group_1_not_existing' does not exist");
|
||||
});
|
||||
|
||||
it('Legacy api: Log when adding to non-existing device', async () => {
|
||||
it('Legacy api: Log when adding a non-existing device', async () => {
|
||||
await resetExtension();
|
||||
logger.error.mockClear();
|
||||
MQTT.events.message('zigbee2mqtt/bridge/group/group_1/add', 'bulb_color_not_existing');
|
||||
@@ -281,6 +281,14 @@ describe('Groups', () => {
|
||||
expect(logger.error).toHaveBeenCalledWith("Device 'bulb_color_not_existing' does not exist");
|
||||
});
|
||||
|
||||
it('Legacy api: Log when adding a non-existing endpoint', async () => {
|
||||
await resetExtension();
|
||||
logger.error.mockClear();
|
||||
MQTT.events.message('zigbee2mqtt/bridge/group/group_1/add', 'bulb_color/not_existing_endpoint');
|
||||
await flushPromises();
|
||||
expect(logger.error).toHaveBeenCalledWith("Device 'bulb_color' does not have endpoint 'not_existing_endpoint'");
|
||||
});
|
||||
|
||||
it('Should publish group state change when a device in it changes state', async () => {
|
||||
const device = zigbeeHerdsman.devices.bulb_color;
|
||||
const endpoint = device.getEndpoint(1);
|
||||
@@ -869,7 +877,7 @@ describe('Groups', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Error when adding to non-existing device', async () => {
|
||||
it('Error when adding a non-existing device', async () => {
|
||||
await resetExtension();
|
||||
logger.error.mockClear();
|
||||
MQTT.publish.mockClear();
|
||||
@@ -883,6 +891,20 @@ describe('Groups', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Error when adding a non-existing endpoint', async () => {
|
||||
await resetExtension();
|
||||
logger.error.mockClear();
|
||||
MQTT.publish.mockClear();
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/group/members/add', stringify({group: 'group_1', device: 'bulb_color/not_existing_endpoint'}));
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).not.toHaveBeenCalledWith('zigbee2mqtt/bridge/groups', expect.any(String), expect.any(Object), expect.any(Function));
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/group/members/add',
|
||||
stringify({"data":{"device":"bulb_color/not_existing_endpoint","group":"group_1"},"status":"error","error":"Device 'bulb_color' does not have endpoint 'not_existing_endpoint'"}),
|
||||
{retain: false, qos: 0}, expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Should only include relevant properties when publishing member states', async () => {
|
||||
const bulbColor = zigbeeHerdsman.devices.bulb_color;
|
||||
const bulbColorTemp = zigbeeHerdsman.devices.bulb;
|
||||
|
||||
+696
-190
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -5,7 +5,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const data = require('./stub/data');
|
||||
let stdOutWriteOriginal;
|
||||
const rimraf = require('rimraf');
|
||||
const {rimrafSync} = require('rimraf');
|
||||
const Transport = require('winston-transport');
|
||||
|
||||
describe('Logger', () => {
|
||||
@@ -37,7 +37,7 @@ describe('Logger', () => {
|
||||
logger.logOutput();
|
||||
|
||||
for (const d of fs.readdirSync(dir.name)) {
|
||||
rimraf.sync(path.join(dir.name, d));
|
||||
rimrafSync(path.join(dir.name, d));
|
||||
}
|
||||
|
||||
for (let i = 0; i < 21; i++) {
|
||||
|
||||
+10
-10
@@ -102,7 +102,7 @@ describe('Networkmap', () => {
|
||||
let call = MQTT.publish.mock.calls[0];
|
||||
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/networkmap/raw');
|
||||
|
||||
const expected = {"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, power_on_behavior, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description": "external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]};
|
||||
const expected = {"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, color_options, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, voltage, power_outage_count, action, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"action, linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"CC2530 router","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]};
|
||||
expect(JSON.parse(call[1])).toStrictEqual(expected);
|
||||
|
||||
/**
|
||||
@@ -148,10 +148,10 @@ describe('Networkmap', () => {
|
||||
"0x000b57fffec6a5b3" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{bulb_color|0x000b57fffec6a5b3 (0x9dcf)|Philips Hue Go (7146060PH)|unknown}"];
|
||||
"0x000b57fffec6a5b3" -> "0x00124b00120144ae" [penwidth=0.5, weight=0, color="#994444", label="120"]
|
||||
"0x000b57fffec6a5b3" -> "0x000b57fffec6a5b2" [penwidth=0.5, weight=0, color="#994444", label="110"]
|
||||
"0x0017880104e45521" [style="rounded, dashed, filled", fillcolor="#fff8ce", fontcolor="#000000", label="{button_double_key|0x0017880104e45521 (0x198a)|Xiaomi Aqara double key wireless wall switch (2016 model) (WXKG02LM_rev1)|9 seconds ago}"];
|
||||
"0x0017880104e45521" [style="rounded, dashed, filled", fillcolor="#fff8ce", fontcolor="#000000", label="{button_double_key|0x0017880104e45521 (0x198a)|Aqara Wireless remote switch (double rocker), 2016 model (WXKG02LM_rev1)|9 seconds ago}"];
|
||||
"0x0017880104e45521" -> "0x0017880104e45559" [penwidth=1, weight=0, color="#994444", label="130"]
|
||||
"0x0017880104e45525" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{0x0017880104e45525|0x0017880104e45525 (0x1988)failed: lqi,routingTable|Boef notSupportedModelID|9 seconds ago}"];
|
||||
"0x0017880104e45559" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{cc2530_router|0x0017880104e45559 (0x198c)|Custom devices (DiY) [CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/) (CC2530.ROUTER)|9 seconds ago}"];
|
||||
"0x0017880104e45525" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{0x0017880104e45525|0x0017880104e45525 (0x1988)failed: lqi,routingTable|Boef Automatically generated definition (notSupportedModelID)|9 seconds ago}"];
|
||||
"0x0017880104e45559" [style="rounded, filled", fillcolor="#4ea3e0", fontcolor="#ffffff", label="{cc2530_router|0x0017880104e45559 (0x198c)|Custom devices (DiY) CC2530 router (CC2530.ROUTER)|9 seconds ago}"];
|
||||
"0x0017880104e45559" -> "0x000b57fffec6a5b2" [penwidth=0.5, weight=0, color="#994444", label="100"]
|
||||
"0x0017880104e45511" [style="rounded, dashed, filled", fillcolor="#fff8ce", fontcolor="#000000", label="{0x0017880104e45511|0x0017880104e45511 (0x045a)|external external (external_converter_device)|9 seconds ago}"];
|
||||
"0x0017880104e45511" -> "0x00124b00120144ae" [penwidth=1, weight=0, color="#994444", label="92"]
|
||||
@@ -197,7 +197,7 @@ describe('Networkmap', () => {
|
||||
---
|
||||
0x0017880104e45525 (0x1988) failed: lqi,routingTable
|
||||
---
|
||||
Boef notSupportedModelID
|
||||
Boef Automatically generated definition (notSupportedModelID)
|
||||
---
|
||||
9 seconds ago
|
||||
]
|
||||
@@ -227,7 +227,7 @@ describe('Networkmap', () => {
|
||||
---
|
||||
0x0017880104e45521 (0x198a)
|
||||
---
|
||||
Xiaomi Aqara double key wireless wall switch (2016 model) (WXKG02LM_rev1)
|
||||
Aqara Wireless remote switch (double rocker), 2016 model (WXKG02LM_rev1)
|
||||
---
|
||||
9 seconds ago
|
||||
]
|
||||
@@ -237,7 +237,7 @@ describe('Networkmap', () => {
|
||||
---
|
||||
0x0017880104e45559 (0x198c)
|
||||
---
|
||||
Custom devices (DiY) [CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/) (CC2530.ROUTER)
|
||||
Custom devices (DiY) CC2530 router (CC2530.ROUTER)
|
||||
---
|
||||
9 seconds ago
|
||||
]
|
||||
@@ -276,12 +276,12 @@ describe('Networkmap', () => {
|
||||
let call = MQTT.publish.mock.calls[0];
|
||||
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
|
||||
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, power_on_behavior, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
|
||||
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":110,"lqi":110,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":100,"lqi":100,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"sourceIeeeAddr":"0x0017880104e45559","sourceNwkAddr":6540,"target":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"targetIeeeAddr":"0x000b57fffec6a5b2"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"TRADFRI LED bulb E26/E27 980 lumen, dimmable, white spectrum, opal white","model":"LED1545G12","supports":"light (state, brightness, color_temp, color_temp_startup), effect, power_on_behavior, color_options, linkquality","vendor":"IKEA"},"failed":[],"friendlyName":"bulb","ieeeAddr":"0x000b57fffec6a5b2","lastSeen":1000,"modelID":"TRADFRI bulb E27 WS opal 980lm","networkAddress":40369,"type":"Router"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, voltage, power_outage_count, action, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"action, linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"CC2530 router","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
|
||||
const actual = JSON.parse(call[1]);
|
||||
expect(actual).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it('Should throw error when rquesting invalid type', async () => {
|
||||
it('Should throw error when requesting invalid type', async () => {
|
||||
mock();
|
||||
MQTT.publish.mockClear();
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/networkmap', 'not_existing');
|
||||
@@ -303,7 +303,7 @@ describe('Networkmap', () => {
|
||||
let call = MQTT.publish.mock.calls[0];
|
||||
expect(call[0]).toStrictEqual('zigbee2mqtt/bridge/response/networkmap');
|
||||
|
||||
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), effect, power_on_behavior, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Aqara double key wireless wall switch (2016 model)","model":"WXKG02LM_rev1","supports":"battery, action, voltage, power_outage_count, linkquality","vendor":"Xiaomi"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":null,"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"[CC2530 router](http://ptvo.info/cc2530-based-zigbee-coordinator-and-router-112/)","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"};
|
||||
const expected = {"data":{"routes":true,"type":"raw","value":{"links":[{"depth":1,"linkquality":120,"lqi":120,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x000b57fffec6a5b3","networkAddress":40399},"sourceIeeeAddr":"0x000b57fffec6a5b3","sourceNwkAddr":40399,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[{"destinationAddress":6540,"nextHop":40369,"status":"ACTIVE"}],"source":{"ieeeAddr":"0x000b57fffec6a5b2","networkAddress":40369},"sourceIeeeAddr":"0x000b57fffec6a5b2","sourceNwkAddr":40369,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":1,"linkquality":92,"lqi":92,"relationship":2,"routes":[],"source":{"ieeeAddr":"0x0017880104e45511","networkAddress":1114},"sourceIeeeAddr":"0x0017880104e45511","sourceNwkAddr":1114,"target":{"ieeeAddr":"0x00124b00120144ae","networkAddress":0},"targetIeeeAddr":"0x00124b00120144ae"},{"depth":2,"linkquality":130,"lqi":130,"relationship":1,"routes":[],"source":{"ieeeAddr":"0x0017880104e45521","networkAddress":6538},"sourceIeeeAddr":"0x0017880104e45521","sourceNwkAddr":6538,"target":{"ieeeAddr":"0x0017880104e45559","networkAddress":6540},"targetIeeeAddr":"0x0017880104e45559"}],"nodes":[{"definition":null,"failed":[],"friendlyName":"Coordinator","ieeeAddr":"0x00124b00120144ae","lastSeen":1000,"modelID":null,"networkAddress":0,"type":"Coordinator"},{"definition":{"description":"Hue Go","model":"7146060PH","supports":"light (state, brightness, color_temp, color_temp_startup, color_xy, color_hs), power_on_behavior, effect, linkquality","vendor":"Philips"},"failed":[],"friendlyName":"bulb_color","ieeeAddr":"0x000b57fffec6a5b3","lastSeen":1000,"modelID":"LLC020","networkAddress":40399,"type":"Router"},{"definition":{"description":"Wireless remote switch (double rocker), 2016 model","model":"WXKG02LM_rev1","supports":"battery, voltage, power_outage_count, action, linkquality","vendor":"Aqara"},"friendlyName":"button_double_key","ieeeAddr":"0x0017880104e45521","lastSeen":1000,"modelID":"lumi.sensor_86sw2.es1","networkAddress":6538,"type":"EndDevice"},{"definition":{"description":"Automatically generated definition","model":"notSupportedModelID","supports":"action, linkquality","vendor":"Boef"},"failed":["lqi","routingTable"],"friendlyName":"0x0017880104e45525","ieeeAddr":"0x0017880104e45525","lastSeen":1000,"manufacturerName":"Boef","modelID":"notSupportedModelID","networkAddress":6536,"type":"Router"},{"definition":{"description":"CC2530 router","model":"CC2530.ROUTER","supports":"led, linkquality","vendor":"Custom devices (DiY)"},"failed":[],"friendlyName":"cc2530_router","ieeeAddr":"0x0017880104e45559","lastSeen":1000,"modelID":"lumi.router","networkAddress":6540,"type":"Router"},{"definition":{"description":"external","model":"external_converter_device","supports":"linkquality","vendor":"external"},"friendlyName":"0x0017880104e45511","ieeeAddr":"0x0017880104e45511","lastSeen":1000,"modelID":"external_converter_device","networkAddress":1114,"type":"EndDevice"}]}},"status":"ok"}
|
||||
const actual = JSON.parse(call[1]);
|
||||
expect(actual).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
@@ -7,13 +7,12 @@ const MQTT = require('./stub/mqtt');
|
||||
const settings = require('../lib/util/settings');
|
||||
const Controller = require('../lib/controller');
|
||||
const flushPromises = require('./lib/flushPromises');
|
||||
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});
|
||||
|
||||
const mocksClear = [MQTT.publish, logger.warn, logger.debug];
|
||||
|
||||
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
|
||||
const mockOnEvent = jest.fn();
|
||||
const mappedLivolo = zigbeeHerdsmanConverters.findByDevice(zigbeeHerdsman.devices.LIVOLO);
|
||||
const mappedLivolo = zigbeeHerdsmanConverters.findByModel('TI0001');
|
||||
mappedLivolo.onEvent = mockOnEvent;
|
||||
zigbeeHerdsmanConverters.onEvent = jest.fn();
|
||||
|
||||
@@ -76,6 +75,6 @@ describe('On event', () => {
|
||||
await zigbeeHerdsman.events.deviceAnnounce({device});
|
||||
await flushPromises();
|
||||
expect(zigbeeHerdsmanConverters.onEvent).toHaveBeenCalledTimes(1);
|
||||
expect(zigbeeHerdsmanConverters.onEvent).toHaveBeenCalledWith('deviceAnnounce', {device}, device, settings.getDevice(device.ieeeAddr), {});
|
||||
expect(zigbeeHerdsmanConverters.onEvent).toHaveBeenCalledWith('deviceAnnounce', {device}, device);
|
||||
});
|
||||
});
|
||||
|
||||
+46
-36
@@ -2,6 +2,7 @@ const path = require('path');
|
||||
|
||||
const data = require('./stub/data');
|
||||
const logger = require('./stub/logger');
|
||||
const sleep = require('./stub/sleep');
|
||||
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
|
||||
const MQTT = require('./stub/mqtt');
|
||||
const settings = require('../lib/util/settings');
|
||||
@@ -35,6 +36,7 @@ describe('OTA update', () => {
|
||||
settings.reRead();
|
||||
jest.useFakeTimers();
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
sleep.mock();
|
||||
await controller.start();
|
||||
await jest.runOnlyPendingTimers();
|
||||
await flushPromises();
|
||||
@@ -47,6 +49,7 @@ describe('OTA update', () => {
|
||||
|
||||
afterAll(async () => {
|
||||
jest.useRealTimers();
|
||||
sleep.restore();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -65,7 +68,7 @@ describe('OTA update', () => {
|
||||
count++;
|
||||
return {swBuildId: count, dateCode: '2019010' + count}
|
||||
});
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
logger.info.mockClear();
|
||||
device.save.mockClear();
|
||||
@@ -86,8 +89,8 @@ describe('OTA update', () => {
|
||||
expect(logger.info).toHaveBeenCalledWith(`Finished update of 'bulb'`);
|
||||
expect(logger.info).toHaveBeenCalledWith(`Device 'bulb' was updated from '{"dateCode":"20190101","softwareBuildID":1}' to '{"dateCode":"20190103","softwareBuildID":3}'`);
|
||||
expect(device.save).toHaveBeenCalledTimes(2);
|
||||
expect(endpoint.read).toHaveBeenCalledWith('genBasic', ['dateCode', 'swBuildId'], {'sendWhen': 'immediate'});
|
||||
expect(endpoint.read).toHaveBeenCalledWith('genBasic', ['dateCode', 'swBuildId'], {'sendWhen': 'active'});
|
||||
expect(endpoint.read).toHaveBeenCalledWith('genBasic', ['dateCode', 'swBuildId'], {'sendPolicy': 'immediate'});
|
||||
expect(endpoint.read).toHaveBeenCalledWith('genBasic', ['dateCode', 'swBuildId'], {});
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({"update_available":false,"update":{"state":"updating","progress":0}}),
|
||||
@@ -120,7 +123,7 @@ describe('OTA update', () => {
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
const endpoint = device.endpoints[0];
|
||||
endpoint.read.mockImplementation(() => {return {swBuildId: 1, dateCode: '2019010'}});
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
device.save.mockClear();
|
||||
mapped.ota.updateToLatest.mockImplementationOnce((a, b, onUpdate) => {
|
||||
@@ -143,7 +146,7 @@ describe('OTA update', () => {
|
||||
|
||||
it('Should be able to check if OTA update is available', async () => {
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
|
||||
mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: false, currentFileVersion: 10, otaFileVersion: 10});
|
||||
@@ -172,9 +175,9 @@ describe('OTA update', () => {
|
||||
|
||||
it('Should handle if OTA update check fails', async () => {
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
mapped.ota.isUpdateAvailable.mockImplementationOnce(() => {throw new Error('RF singals disturbed because of dogs barking')});
|
||||
mapped.ota.isUpdateAvailable.mockImplementationOnce(() => {throw new Error('RF signals disturbed because of dogs barking')});
|
||||
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/device/ota_update/check', "bulb");
|
||||
await flushPromises();
|
||||
@@ -182,7 +185,7 @@ describe('OTA update', () => {
|
||||
expect(mapped.ota.updateToLatest).toHaveBeenCalledTimes(0);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bridge/response/device/ota_update/check',
|
||||
stringify({"data":{"id": "bulb"},"status":"error","error": `Failed to check if update available for 'bulb' (RF singals disturbed because of dogs barking)`}),
|
||||
stringify({"data":{"id": "bulb"},"status":"error","error": `Failed to check if update available for 'bulb' (RF signals disturbed because of dogs barking)`}),
|
||||
{retain: false, qos: 0}, expect.any(Function)
|
||||
);
|
||||
});
|
||||
@@ -209,7 +212,7 @@ describe('OTA update', () => {
|
||||
|
||||
it('Should refuse to check/update when already in progress', async () => {
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
|
||||
mapped.ota.isUpdateAvailable.mockImplementationOnce(() => {
|
||||
@@ -234,7 +237,7 @@ describe('OTA update', () => {
|
||||
const endpoint = device.endpoints[0];
|
||||
endpoint.read.mockImplementation(() => {throw new Error('Failed!')});
|
||||
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
MQTT.events.message('zigbee2mqtt/bridge/request/device/ota_update/update', "bulb");
|
||||
await flushPromises();
|
||||
@@ -249,10 +252,10 @@ describe('OTA update', () => {
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
device.endpoints[0].commandResponse.mockClear();
|
||||
const data = {imageType: 12382};
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: true, currentFileVersion: 10, otaFileVersion: 12});
|
||||
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10};
|
||||
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10, meta: {zclTransactionSequenceNumber: 10}};
|
||||
logger.info.mockClear();
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
await flushPromises();
|
||||
@@ -260,7 +263,7 @@ describe('OTA update', () => {
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382});
|
||||
expect(logger.info).toHaveBeenCalledWith(`Update available for 'bulb'`);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98});
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98}, undefined, 10);
|
||||
|
||||
// Should not request again when device asks again after a short time
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
@@ -283,17 +286,17 @@ describe('OTA update', () => {
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
device.endpoints[0].commandResponse.mockClear();
|
||||
const data = {imageType: 12382};
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
mapped.ota.isUpdateAvailable.mockImplementationOnce(() => {throw new Error('Nothing to find here')})
|
||||
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10};
|
||||
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10, meta: {zclTransactionSequenceNumber: 10}};
|
||||
logger.info.mockClear();
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
await flushPromises();
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382});
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98});
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98}, undefined, 10);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({"update_available":false,"update":{"state":"idle"}}),
|
||||
@@ -305,17 +308,17 @@ describe('OTA update', () => {
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
device.endpoints[0].commandResponse.mockClear();
|
||||
const data = {imageType: 12382};
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: false, currentFileVersion: 13, otaFileVersion: 13});
|
||||
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10};
|
||||
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10, meta: {zclTransactionSequenceNumber: 10}};
|
||||
logger.info.mockClear();
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
await flushPromises();
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledWith(device, logger, {"imageType": 12382});
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98});
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 0x98}, undefined, 10);
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/bulb',
|
||||
stringify({"update_available":false,"update":{"state":"idle","installed_version": 13, "latest_version": 13}}),
|
||||
@@ -327,10 +330,10 @@ describe('OTA update', () => {
|
||||
settings.set(['ota', 'disable_automatic_update_check'], true);
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
const data = {imageType: 12382};
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
mapped.ota.isUpdateAvailable.mockReturnValueOnce({available: true, currentFileVersion: 10, otaFileVersion: 13});
|
||||
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10};
|
||||
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10, meta: {zclTransactionSequenceNumber: 10}};
|
||||
logger.info.mockClear();
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
await flushPromises();
|
||||
@@ -340,22 +343,22 @@ describe('OTA update', () => {
|
||||
it('Should respond with NO_IMAGE_AVAILABLE when not supporting OTA', async () => {
|
||||
const device = zigbeeHerdsman.devices.HGZB04D;
|
||||
const data = {imageType: 12382};
|
||||
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10};
|
||||
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10, meta: {zclTransactionSequenceNumber: 10}};
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
await flushPromises();
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 152});
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 152}, undefined, 10);
|
||||
});
|
||||
|
||||
it('Shouldnt respond with NO_IMAGE_AVAILABLE when not supporting OTA and device has no OTA endpoint', async () => {
|
||||
it('Should respond with NO_IMAGE_AVAILABLE when not supporting OTA and device has no OTA endpoint to standard endpoint', async () => {
|
||||
const device = zigbeeHerdsman.devices.SV01;
|
||||
const data = {imageType: 12382};
|
||||
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10};
|
||||
const payload = {data, cluster: 'genOta', device, endpoint: device.getEndpoint(1), type: 'commandQueryNextImageRequest', linkquality: 10, meta: {zclTransactionSequenceNumber: 10}};
|
||||
logger.error.mockClear();
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
await flushPromises();
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(0);
|
||||
expect(logger.error).toHaveBeenCalledTimes(0);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledTimes(1);
|
||||
expect(device.endpoints[0].commandResponse).toHaveBeenCalledWith("genOta", "queryNextImageResponse", {"status": 152}, undefined, 10);
|
||||
});
|
||||
|
||||
it('Legacy api: Should OTA update a device', async () => {
|
||||
@@ -366,7 +369,7 @@ describe('OTA update', () => {
|
||||
count++;
|
||||
return {swBuildId: count, dateCode: '2019010' + count}
|
||||
});
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
logger.info.mockClear();
|
||||
logger.error.mockClear();
|
||||
@@ -386,17 +389,17 @@ describe('OTA update', () => {
|
||||
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 0.00%`);
|
||||
expect(logger.info).toHaveBeenCalledWith(`Update of 'bulb' at 10.00%, ≈ 60 minutes remaining`);
|
||||
expect(logger.info).toHaveBeenCalledWith(`Finished update of 'bulb'`);
|
||||
expect(logger.info).toHaveBeenCalledWith(`Device 'bulb' was updated from '{"dateCode":"20190101","softwareBuildID":1}' to '{"dateCode":"20190103","softwareBuildID":3}'`);
|
||||
expect(logger.info).toHaveBeenCalledWith(`Device 'bulb' was updated from '{"dateCode":"20190101","softwareBuildID":1}' to '{"dateCode":"20190103","softwareBuildID":3}'`);
|
||||
expect(logger.error).toHaveBeenCalledTimes(0);
|
||||
expect(device.save).toHaveBeenCalledTimes(2);
|
||||
expect(endpoint.read).toHaveBeenCalledWith('genBasic', ['dateCode', 'swBuildId'], {'sendWhen': 'immediate'});
|
||||
expect(endpoint.read).toHaveBeenCalledWith('genBasic', ['dateCode', 'swBuildId'], {'sendPolicy': 'immediate'});
|
||||
});
|
||||
|
||||
it('Legacy api: Should handle when OTA update fails', async () => {
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
const endpoint = device.endpoints[0];
|
||||
endpoint.read.mockImplementation(() => {return {swBuildId: 1, dateCode: '2019010'}});
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
logger.info.mockClear();
|
||||
logger.error.mockClear();
|
||||
@@ -413,7 +416,7 @@ describe('OTA update', () => {
|
||||
|
||||
it('Legacy api: Should be able to check if OTA update is available', async () => {
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
|
||||
logger.info.mockClear();
|
||||
@@ -435,16 +438,16 @@ describe('OTA update', () => {
|
||||
|
||||
it('Legacy api: Should handle if OTA update check fails', async () => {
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
logger.error.mockClear();
|
||||
mapped.ota.isUpdateAvailable.mockImplementationOnce(() => {throw new Error('RF singals disturbed because of dogs barking')});
|
||||
mapped.ota.isUpdateAvailable.mockImplementationOnce(() => {throw new Error('RF signals disturbed because of dogs barking')});
|
||||
|
||||
MQTT.events.message('zigbee2mqtt/bridge/ota_update/check', 'bulb');
|
||||
await flushPromises();
|
||||
expect(mapped.ota.isUpdateAvailable).toHaveBeenCalledTimes(1);
|
||||
expect(mapped.ota.updateToLatest).toHaveBeenCalledTimes(0);
|
||||
expect(logger.error).toHaveBeenCalledWith(`Failed to check if update available for 'bulb' (RF singals disturbed because of dogs barking)`);
|
||||
expect(logger.error).toHaveBeenCalledWith(`Failed to check if update available for 'bulb' (RF signals disturbed because of dogs barking)`);
|
||||
});
|
||||
|
||||
it('Legacy api: Should not check for OTA when device does not support it', async () => {
|
||||
@@ -463,7 +466,7 @@ describe('OTA update', () => {
|
||||
return {swBuildId: 1, dateCode: '2019010'}
|
||||
});
|
||||
|
||||
const mapped = zigbeeHerdsmanConverters.findByDevice(device)
|
||||
const mapped = await zigbeeHerdsmanConverters.findByDevice(device)
|
||||
mockClear(mapped);
|
||||
logger.info.mockClear();
|
||||
MQTT.events.message('zigbee2mqtt/bridge/ota_update/update', 'bulb');
|
||||
@@ -482,4 +485,11 @@ describe('OTA update', () => {
|
||||
expect(spyUseIndexOverride).toHaveBeenCalledWith('http://my.site/index.json');
|
||||
spyUseIndexOverride.mockClear();
|
||||
});
|
||||
|
||||
it('Clear update state on startup', async () => {
|
||||
const device = controller.zigbee.resolveEntity(zigbeeHerdsman.devices.bulb_color.ieeeAddr);
|
||||
controller.state.set(device, {update: {progress: 100, remaining: 10, state: 'updating'}})
|
||||
await resetExtension();
|
||||
expect(controller.state.get(device)).toStrictEqual({update: {state: 'available'}});
|
||||
});
|
||||
});
|
||||
|
||||
+46
-14
@@ -1,4 +1,5 @@
|
||||
const data = require('./stub/data');
|
||||
const sleep = require('./stub/sleep');
|
||||
const logger = require('./stub/logger');
|
||||
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
|
||||
const zigbeeHerdsmanConverters = require('zigbee-herdsman-converters');
|
||||
@@ -30,6 +31,7 @@ describe('Publish', () => {
|
||||
jest.useFakeTimers();
|
||||
data.writeEmptyState();
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
sleep.mock();
|
||||
await controller.start();
|
||||
await flushPromises();
|
||||
});
|
||||
@@ -50,12 +52,13 @@ describe('Publish', () => {
|
||||
g.command.mockClear();
|
||||
});
|
||||
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.__clearStore__();
|
||||
zigbeeHerdsmanConverters.toZigbee.__clearStore__();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
jest.runOnlyPendingTimers();
|
||||
jest.useRealTimers();
|
||||
sleep.restore();
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices', async () => {
|
||||
@@ -71,12 +74,7 @@ describe('Publish', () => {
|
||||
});
|
||||
|
||||
it('Should corretly handle mallformed messages', async () => {
|
||||
await MQTT.events.message('zigbee2mqtt/foo', undefined);
|
||||
await MQTT.events.message('zigbee2mqtt/foo', null);
|
||||
await MQTT.events.message('zigbee2mqtt/foo', "");
|
||||
|
||||
await MQTT.events.message('zigbee2mqtt/bulb_color/set', undefined);
|
||||
await MQTT.events.message('zigbee2mqtt/bulb_color/set', null);
|
||||
await MQTT.events.message('zigbee2mqtt/bulb_color/set', "");
|
||||
await flushPromises();
|
||||
expectNothingPublished();
|
||||
@@ -150,7 +148,7 @@ describe('Publish', () => {
|
||||
it('Should publish messages to zigbee devices with color_temp in %', async () => {
|
||||
const device = zigbeeHerdsman.devices.bulb_color;
|
||||
const endpoint = device.getEndpoint(1);
|
||||
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({color_temp_percent: '100'}));
|
||||
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({color_temp_percent: 100}));
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith("lightingColorCtrl", "moveToColorTemp", {colortemp: 500, transtime: 0}, {});
|
||||
@@ -225,6 +223,40 @@ describe('Publish', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('Should publish messages to TuYa switch with dummy endpoints', async () => {
|
||||
const device = zigbeeHerdsman.devices.TS0601_switch;
|
||||
const endpoint = device.getEndpoint(1);
|
||||
await MQTT.events.message('zigbee2mqtt/TS0601_switch/set', stringify({state_l2: 'ON'}));
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuya", "dataRequest", {dpValues: [{data: [1], datatype: 1, dp: 2}], seq: expect.any(Number)}, {disableDefaultResponse: true});
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/TS0601_switch',
|
||||
stringify({state_l2: 'ON'}),
|
||||
{ retain: false, qos: 0 },
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Should publish messages to TuYa cover switch with dummy endpoints', async () => {
|
||||
const device = zigbeeHerdsman.devices.TS0601_cover_switch;
|
||||
const endpoint = device.getEndpoint(1);
|
||||
await MQTT.events.message('zigbee2mqtt/TS0601_cover_switch/set', stringify({state: 'OPEN'}));
|
||||
await MQTT.events.message('zigbee2mqtt/TS0601_cover_switch/set', stringify({state_l1: 'ON'}));
|
||||
await MQTT.events.message('zigbee2mqtt/TS0601_cover_switch/l2/set', stringify({state: 'OFF'}));
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(3);
|
||||
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuya", "dataRequest", {dpValues: [{data: [0], datatype: 4, dp: 1}], seq: expect.any(Number)}, {disableDefaultResponse: true});
|
||||
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuya", "dataRequest", {dpValues: [{data: [1], datatype: 1, dp: 102}], seq: expect.any(Number)}, {disableDefaultResponse: true});
|
||||
expect(endpoint.command).toHaveBeenCalledWith("manuSpecificTuya", "dataRequest", {dpValues: [{data: [0], datatype: 1, dp: 101}], seq: expect.any(Number)}, {disableDefaultResponse: true});
|
||||
expect(MQTT.publish).toHaveBeenCalledWith(
|
||||
'zigbee2mqtt/TS0601_cover_switch',
|
||||
stringify({state_l2: 'OFF', state_l1: 'ON', state: 'OPEN'}),
|
||||
{ retain: false, qos: 0 },
|
||||
expect.any(Function)
|
||||
);
|
||||
});
|
||||
|
||||
it('Should publish messages to zigbee devices with color xy', async () => {
|
||||
const device = zigbeeHerdsman.devices.bulb_color;
|
||||
const endpoint = device.getEndpoint(1);
|
||||
@@ -536,7 +568,7 @@ describe('Publish', () => {
|
||||
logger.error.mockClear();
|
||||
await MQTT.events.message('zigbee2mqtt/0x0017880104e45542/get', stringify({state_center: '', state_right: ''}));
|
||||
await flushPromises();
|
||||
expect(logger.error).toHaveBeenCalledWith(`Device 'wall_switch_double' has no endpoint 'center'`);
|
||||
expect(logger.error).toHaveBeenCalledWith(`No converter available for 'state_center' ("")`);
|
||||
expect(endpoint2.read).toHaveBeenCalledTimes(0);
|
||||
expect(endpoint3.read).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint3.read).toHaveBeenCalledWith('genOnOff', ['onOff']);
|
||||
@@ -964,7 +996,7 @@ describe('Publish', () => {
|
||||
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify(payload));
|
||||
await flushPromises();
|
||||
expect(endpoint.command).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.command.mock.calls[0]).toEqual(["lightingColorCtrl", "enhancedMoveToHueAndSaturation", {"direction": 0, "enhancehue": 44877, "saturation": 200, "transtime": 0,}, {}]);
|
||||
expect(endpoint.command.mock.calls[0]).toEqual(["lightingColorCtrl", "enhancedMoveToHueAndSaturation", {"direction": 0, "enhancehue": 45510, "saturation": 127, "transtime": 0,}, {}]);
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
||||
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/bulb_color');
|
||||
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({"color":{"hue":250,"saturation":50}, "color_mode": "hs"});
|
||||
@@ -977,7 +1009,7 @@ describe('Publish', () => {
|
||||
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
|
||||
await flushPromises();
|
||||
expect(endpoint.write).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {"85": {"type": 57, "value": 100}});
|
||||
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {presentValue: 100});
|
||||
});
|
||||
|
||||
it('ZNCLDJ11LM position', async () => {
|
||||
@@ -987,7 +1019,7 @@ describe('Publish', () => {
|
||||
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
|
||||
await flushPromises();
|
||||
expect(endpoint.write).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {"85": {"type": 57, "value": 10}});
|
||||
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {presentValue: 10});
|
||||
});
|
||||
|
||||
it('ZNCLDJ11LM position', async () => {
|
||||
@@ -997,7 +1029,7 @@ describe('Publish', () => {
|
||||
await MQTT.events.message('zigbee2mqtt/curtain/set', stringify(payload));
|
||||
await flushPromises();
|
||||
expect(endpoint.write).toHaveBeenCalledTimes(1);
|
||||
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {"85": {"type": 57, "value": 0}});
|
||||
expect(endpoint.write).toHaveBeenCalledWith("genAnalogOutput", {presentValue: 0});
|
||||
});
|
||||
|
||||
it('ZNCLDJ11LM position', async () => {
|
||||
@@ -1281,7 +1313,7 @@ describe('Publish', () => {
|
||||
expect(endpoint.command).toHaveBeenCalledWith("ssIasWd", "startWarning", {"startwarninginfo": 53, "warningduration": 10, "strobedutycycle": 0, "strobelevel": 1}, {disableDefaultResponse: true});
|
||||
});
|
||||
|
||||
it('Shouldnt do anythign when device is not supported', async () => {
|
||||
it('Shouldnt do anything when device is not supported', async () => {
|
||||
const payload = {state: 'ON'};
|
||||
await MQTT.events.message('zigbee2mqtt/unsupported2/set', stringify(payload));
|
||||
await flushPromises();
|
||||
@@ -1348,7 +1380,7 @@ describe('Publish', () => {
|
||||
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({state: 'ON', brightness: 20, transition: 0.0}));
|
||||
await flushPromises();
|
||||
|
||||
zigbeeHerdsmanConverters.toZigbeeConverters.__clearStore__();
|
||||
zigbeeHerdsmanConverters.toZigbee.__clearStore__();
|
||||
|
||||
await MQTT.events.message('zigbee2mqtt/bulb_color/set', stringify({"state": "ON", "transition": 1.0}));
|
||||
await flushPromises();
|
||||
|
||||
+54
-15
@@ -1,4 +1,5 @@
|
||||
const data = require('./stub/data');
|
||||
const sleep = require('./stub/sleep');
|
||||
const logger = require('./stub/logger');
|
||||
const stringify = require('json-stable-stringify-without-jsonify');
|
||||
const zigbeeHerdsman = require('./stub/zigbeeHerdsman');
|
||||
@@ -15,6 +16,7 @@ describe('Receive', () => {
|
||||
beforeAll(async () => {
|
||||
jest.useFakeTimers();
|
||||
controller = new Controller(jest.fn(), jest.fn());
|
||||
sleep.mock();
|
||||
await controller.start();
|
||||
await jest.runOnlyPendingTimers();
|
||||
await flushPromises();
|
||||
@@ -30,6 +32,7 @@ describe('Receive', () => {
|
||||
|
||||
afterAll(async () => {
|
||||
jest.useRealTimers();
|
||||
sleep.restore();
|
||||
});
|
||||
|
||||
it('Should handle a zigbee message', async () => {
|
||||
@@ -178,6 +181,47 @@ describe('Receive', () => {
|
||||
expect(JSON.parse(MQTT.publish.mock.calls[1][1])).toStrictEqual({temperature: 0.07, pressure: 2, humidity: 0.03});
|
||||
});
|
||||
|
||||
it('Should NOT publish old messages from State cache during debouncing', async () => {
|
||||
|
||||
// Summary:
|
||||
// First send multiple measurements to device that is debouncing. Make sure only one message is sent out to MQTT. This also ensures first message is cached to "State".
|
||||
// Then send another measurement to that same device and trigger asynchronous event to push data from Cache. Newest value should be sent out.
|
||||
|
||||
const device = zigbeeHerdsman.devices.WSDCGQ11LM;
|
||||
settings.set(['devices', device.ieeeAddr, 'debounce'], 0.1);
|
||||
await zigbeeHerdsman.events.message({data: {measuredValue: 8}, cluster: 'msTemperatureMeasurement', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10});
|
||||
await zigbeeHerdsman.events.message( {data: {measuredValue: 1}, cluster: 'msRelativeHumidity', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10} );
|
||||
await zigbeeHerdsman.events.message( {data: {measuredValue: 2}, cluster: 'msPressureMeasurement', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10} );
|
||||
await flushPromises();
|
||||
jest.advanceTimersByTime(50);
|
||||
// Test that measurements are combined(=debounced)
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(0);
|
||||
jest.runOnlyPendingTimers();
|
||||
await flushPromises();
|
||||
|
||||
// Test that only one MQTT is sent out and test its values.
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
||||
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/weather_sensor');
|
||||
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({temperature: 0.08, humidity: 0.01, pressure: 2});
|
||||
|
||||
// Send another Zigbee message...
|
||||
await zigbeeHerdsman.events.message({data: {measuredValue: 9}, cluster: 'msTemperatureMeasurement', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10});
|
||||
const realDevice = controller.zigbee.resolveEntity(device);
|
||||
|
||||
// Trigger asynchronous event while device is "debouncing" to trigger Message to be sent out from State cache.
|
||||
await controller.publishEntityState( realDevice, {} );
|
||||
jest.runOnlyPendingTimers();
|
||||
await flushPromises();
|
||||
|
||||
// Total of 3 messages should have triggered.
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(3);
|
||||
|
||||
// Test that message pushed by asynchronous message contains NEW measurement and not old.
|
||||
expect(JSON.parse(MQTT.publish.mock.calls[1][1])).toStrictEqual({temperature: 0.09, humidity: 0.01, pressure: 2});
|
||||
// Test that messages after debouncing contains NEW measurement and not old.
|
||||
expect(JSON.parse(MQTT.publish.mock.calls[2][1])).toStrictEqual({temperature: 0.09, humidity: 0.01, pressure: 2});
|
||||
});
|
||||
|
||||
it('Shouldnt republish old state', async () => {
|
||||
// https://github.com/Koenkk/zigbee2mqtt/issues/3572
|
||||
const device = zigbeeHerdsman.devices.bulb;
|
||||
@@ -258,7 +302,7 @@ describe('Receive', () => {
|
||||
|
||||
it('Should publish 1 message when converted twice', async () => {
|
||||
const device = zigbeeHerdsman.devices.RTCGQ11LM;
|
||||
const data = {'65281': {'1': 3045, '3': 19, '4': 17320, '5': 35, '6': [0, 3], '10': 51107, '11': 381, '100': 0}}
|
||||
const data = {'65281': {'1': 3045, '3': 19, '5': 35, '6': [0, 3], '11': 381, '100': 0}}
|
||||
const payload = {data, cluster: 'genBasic', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
await flushPromises();
|
||||
@@ -347,18 +391,6 @@ describe('Receive', () => {
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('Should not handle messages from unsupported devices and link to docs', async () => {
|
||||
const device = zigbeeHerdsman.devices.unsupported;
|
||||
const data = {onOff: 1};
|
||||
logger.warn.mockClear();
|
||||
const payload = {data, cluster: 'genOnOff', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
await flushPromises();
|
||||
expect(logger.warn).toHaveBeenCalledWith(`Received message from unsupported device with Zigbee model 'notSupportedModelID' and manufacturer name 'notSupportedMfg'`);
|
||||
expect(logger.warn).toHaveBeenCalledWith(`Please see: https://www.zigbee2mqtt.io/advanced/support-new-devices/01_support_new_devices.html`);
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('Should not handle messages from still interviewing devices with unknown definition', async () => {
|
||||
const device = zigbeeHerdsman.devices.interviewing;
|
||||
const data = {onOff: 1};
|
||||
@@ -367,7 +399,7 @@ describe('Receive', () => {
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(0);
|
||||
expect(logger.debug).toHaveBeenCalledWith(`Skipping message, definition is undefined and still interviewing`);
|
||||
expect(logger.debug).toHaveBeenCalledWith(`Skipping message, still interviewing`);
|
||||
});
|
||||
|
||||
it('Should handle a command', async () => {
|
||||
@@ -433,6 +465,13 @@ describe('Receive', () => {
|
||||
await flushPromises();
|
||||
expect(MQTT.publish).toHaveBeenCalledTimes(1);
|
||||
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual('zigbee2mqtt/SP600_OLD');
|
||||
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({energy: 6.648, power: 496});
|
||||
expect(JSON.parse(MQTT.publish.mock.calls[0][1])).toStrictEqual({energy: 6.65, power: 496});
|
||||
});
|
||||
it('Should emit DevicesChanged event when a converter announces changed exposes', async () => {
|
||||
const device = zigbeeHerdsman.devices['BMCT-SLZ'];
|
||||
const data = {deviceMode: 0}
|
||||
const payload = {data, cluster: 'manuSpecificBosch10', device, endpoint: device.getEndpoint(1), type: 'attributeReport', linkquality: 10};
|
||||
await zigbeeHerdsman.events.message(payload);
|
||||
expect(MQTT.publish.mock.calls[0][0]).toStrictEqual("zigbee2mqtt/bridge/devices");
|
||||
});
|
||||
});
|
||||
|
||||
+55
-34
@@ -123,6 +123,22 @@ describe('Settings', () => {
|
||||
expect(actual).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it('Should add devices even when devices exist empty', () => {
|
||||
write(configurationFile, {devices: []});
|
||||
settings.addDevice('0x12345678');
|
||||
|
||||
const actual = read(configurationFile);
|
||||
const expected = {
|
||||
devices: {
|
||||
'0x12345678': {
|
||||
friendly_name: '0x12345678',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(actual).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it('Should read devices', () => {
|
||||
const content = {
|
||||
devices: {
|
||||
@@ -319,22 +335,15 @@ describe('Settings', () => {
|
||||
expect(read(devicesFile)).toStrictEqual(expected);
|
||||
});
|
||||
|
||||
it('Should add devices for first file when using 2 separates file', () => {
|
||||
function extractFromMultipleDeviceConfigs(contentDevices2) {
|
||||
const contentConfiguration = {
|
||||
devices: ['devices.yaml', 'devices2.yaml']
|
||||
devices: ['devices.yaml', 'devices2.yaml'],
|
||||
};
|
||||
|
||||
const contentDevices = {
|
||||
'0x12345678': {
|
||||
friendly_name: '0x12345678',
|
||||
retain: false,
|
||||
},
|
||||
};
|
||||
|
||||
const contentDevices2 = {
|
||||
'0x87654321': {
|
||||
friendly_name: '0x87654321',
|
||||
retain: false,
|
||||
retain: false,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -349,15 +358,28 @@ describe('Settings', () => {
|
||||
const expected = {
|
||||
'0x12345678': {
|
||||
friendly_name: '0x12345678',
|
||||
retain: false,
|
||||
retain: false,
|
||||
},
|
||||
'0x1234': {
|
||||
'0x1234': {
|
||||
friendly_name: '0x1234',
|
||||
},
|
||||
};
|
||||
|
||||
expect(read(devicesFile)).toStrictEqual(expected);
|
||||
expect(read(devicesFile2)).toStrictEqual(contentDevices2);
|
||||
}
|
||||
|
||||
it('Should add devices for first file when using 2 separates file', () => {
|
||||
extractFromMultipleDeviceConfigs({
|
||||
'0x87654321': {
|
||||
friendly_name: '0x87654321',
|
||||
retain: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('Should add devices for first file when using 2 separates file and the second file is empty', () => {
|
||||
extractFromMultipleDeviceConfigs(null)
|
||||
});
|
||||
|
||||
it('Should add devices to a separate file if devices.yaml doesnt exist', () => {
|
||||
@@ -666,6 +688,18 @@ describe('Settings', () => {
|
||||
}).toThrow(new Error("Device '0x123' already exists"));
|
||||
});
|
||||
|
||||
it('Should not allow any string values for ext_pan_id', () => {
|
||||
write(configurationFile, {
|
||||
...minimalConfig,
|
||||
advanced: {ext_pan_id: 'NOT_GENERATE'},
|
||||
});
|
||||
|
||||
settings.reRead();
|
||||
|
||||
const error = `advanced.ext_pan_id: should be array or 'GENERATE' (is 'NOT_GENERATE')`;
|
||||
expect(settings.validate()).toEqual(expect.arrayContaining([error]));
|
||||
});
|
||||
|
||||
it('Should not allow any string values for network_key', () => {
|
||||
write(configurationFile, {
|
||||
...minimalConfig,
|
||||
@@ -760,8 +794,7 @@ describe('Settings', () => {
|
||||
|
||||
it('Should throw error when yaml file does not exist', () => {
|
||||
settings.testing.clear();
|
||||
const error = `ENOENT: no such file or directory, open '${configurationFile}'`;
|
||||
expect(settings.validate()).toEqual(expect.arrayContaining([error]));
|
||||
expect(settings.validate()[0].startsWith(`ENOENT: no such file or directory, open `)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('Configuration shouldnt be valid when invalid QOS value is used', async () => {
|
||||
@@ -849,18 +882,6 @@ describe('Settings', () => {
|
||||
expect(settings.validate()).toEqual(expect.arrayContaining([error]));
|
||||
});
|
||||
|
||||
it('Configuration shouldnt be valid when friendly_name is a postfix', async () => {
|
||||
write(configurationFile, {
|
||||
...minimalConfig,
|
||||
devices: {'0x0017880104e45519': {friendly_name: 'left', retain: false}},
|
||||
});
|
||||
|
||||
settings.reRead();
|
||||
|
||||
const error = `Following friendly_name are not allowed: '${utils.endpointNames}'`;
|
||||
expect(settings.validate()).toEqual(expect.arrayContaining([error]));
|
||||
});
|
||||
|
||||
it('Configuration shouldnt be valid when duplicate friendly_name are used', async () => {
|
||||
write(configurationFile, {
|
||||
devices: {
|
||||
@@ -904,16 +925,16 @@ describe('Settings', () => {
|
||||
|
||||
it('Frontend config', () => {
|
||||
write(configurationFile, {...minimalConfig,
|
||||
frontend: true,
|
||||
frontend: true,
|
||||
});
|
||||
|
||||
settings.reRead();
|
||||
expect(settings.get().frontend).toStrictEqual({port: 8080, auth_token: false, host: '0.0.0.0'})
|
||||
expect(settings.get().frontend).toStrictEqual({port: 8080, auth_token: false})
|
||||
});
|
||||
|
||||
it('Baudrate config', () => {
|
||||
write(configurationFile, {...minimalConfig,
|
||||
advanced: {baudrate: 20},
|
||||
advanced: {baudrate: 20},
|
||||
});
|
||||
|
||||
settings.reRead();
|
||||
@@ -922,7 +943,7 @@ describe('Settings', () => {
|
||||
|
||||
it('ikea_ota_use_test_url config', () => {
|
||||
write(configurationFile, {...minimalConfig,
|
||||
advanced: {ikea_ota_use_test_url: true},
|
||||
advanced: {ikea_ota_use_test_url: true},
|
||||
});
|
||||
|
||||
settings.reRead();
|
||||
@@ -931,7 +952,7 @@ describe('Settings', () => {
|
||||
|
||||
it('transmit_power config', () => {
|
||||
write(configurationFile, {...minimalConfig,
|
||||
experimental: {transmit_power: 1337},
|
||||
experimental: {transmit_power: 1337},
|
||||
});
|
||||
|
||||
settings.reRead();
|
||||
@@ -940,7 +961,7 @@ describe('Settings', () => {
|
||||
|
||||
it('output config', () => {
|
||||
write(configurationFile, {...minimalConfig,
|
||||
experimental: {output: 'json'},
|
||||
experimental: {output: 'json'},
|
||||
});
|
||||
|
||||
settings.reRead();
|
||||
@@ -949,7 +970,7 @@ describe('Settings', () => {
|
||||
|
||||
it('Baudrartsctste config', () => {
|
||||
write(configurationFile, {...minimalConfig,
|
||||
advanced: {rtscts: true},
|
||||
advanced: {rtscts: true},
|
||||
});
|
||||
|
||||
settings.reRead();
|
||||
@@ -958,7 +979,7 @@ describe('Settings', () => {
|
||||
|
||||
it('Deprecated: Home Assistant config', () => {
|
||||
write(configurationFile, {...minimalConfig,
|
||||
homeassistant: {discovery_topic: 'new'},
|
||||
homeassistant: {discovery_topic: 'new'},
|
||||
advanced: {homeassistant_discovery_topic: 'old', homeassistant_status_topic: 'olds'},
|
||||
});
|
||||
|
||||
|
||||
@@ -179,6 +179,12 @@ function writeDefaultConfiguration() {
|
||||
'0x0017882104a44559': {
|
||||
friendly_name: 'TS0601_thermostat',
|
||||
},
|
||||
'0x0017882104a44560': {
|
||||
friendly_name: 'TS0601_switch',
|
||||
},
|
||||
'0x0017882104a44562': {
|
||||
friendly_name: 'TS0601_cover_switch',
|
||||
},
|
||||
'0x0017882194e45543': {
|
||||
friendly_name: 'QS-Zigbee-D02-TRIAC-2C-LN',
|
||||
},
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
const utils = require('../../lib/util/utils');
|
||||
const spy = jest.spyOn(utils.default, 'sleep');
|
||||
|
||||
export function mock() {
|
||||
spy.mockImplementation(() => {});
|
||||
}
|
||||
|
||||
export function restore() {
|
||||
spy.mockRestore();
|
||||
}
|
||||
@@ -134,7 +134,9 @@ const bulb_color = new Device('Router', '0x000b57fffec6a5b3', 40399, 4107, [new
|
||||
const bulb_color_2 = new Device('Router', '0x000b57fffec6a5b4', 401292, 4107, [new Endpoint(1, [0,3,4,5,6,8,768,2821,4096], [5,25,32,4096], '0x000b57fffec6a5b4', [], {lightingColorCtrl: {colorCapabilities: 254}}, [], null, null, {'scenes': {'1_0': {name: 'Chill scene', state: {state: 'ON'}}, '4_9': {state: {state: 'OFF'}}}})], true, "Mains (single phase)", "LLC020", false, 'Philips', '2019.09', '5.127.1.26581');
|
||||
const bulb_2 = new Device('Router', '0x000b57fffec6a5b7', 40369, 4476, [new Endpoint(1, [0,3,4,5,6,8,768,2821,4096], [5,25,32,4096], '0x000b57fffec6a5b7', [], {lightingColorCtrl: {colorCapabilities: 17}})], true, "Mains (single phase)", "TRADFRI bulb E27 WS opal 980lm");
|
||||
const TS0601_thermostat = new Device('EndDevice', '0x0017882104a44559', 6544,4151, [new Endpoint(1, [], [], '0x0017882104a44559')], true, "Mains (single phase)", 'kud7u2l');
|
||||
const ZNCZ02LM = new Device('Router', '0x0017880104e45524', 6540,4151, [new Endpoint(1, [0], [], '0x0017880104e45524')], true, "Mains (single phase)", "lumi.plug");
|
||||
const TS0601_switch = new Device('EndDevice', '0x0017882104a44560', 6544,4151, [new Endpoint(1, [], [], '0x0017882104a44560')], true, "Mains (single phase)", 'kjintbl');
|
||||
const TS0601_cover_switch = new Device('EndDevice', '0x0017882104a44562', 6544,4151, [new Endpoint(1, [], [], '0x0017882104a44562')], true, "Mains (single phase)", 'TS0601', false, '_TZE200_5nldle7w');
|
||||
const ZNCZ02LM = new Device('Router', '0x0017880104e45524', 6540,4151, [new Endpoint(1, [0, 6], [], '0x0017880104e45524')], true, "Mains (single phase)", "lumi.plug");
|
||||
const GLEDOPTO_2ID = new Device('Router', '0x0017880104e45724', 6540,4151, [new Endpoint(11, [0,3,4,5,6,8,768], [], '0x0017880104e45724', [], {}, [], 49246, 528), new Endpoint(12, [0, 3, 4, 5, 6, 8, 768], [], '0x0017880104e45724', [], {}, [], 260, 258), new Endpoint(13, [4096], [4096], '0x0017880104e45724', [], {}, [], 49246, 57694), new Endpoint(15, [0, 3, 4, 5, 6, 8, 768], [], '0x0017880104e45724', [], {}, [], 49246, 256)], true, "Mains (single phase)", 'GL-C-007', false, 'GLEDOPTO');
|
||||
const QBKG03LM = new Device('Router', '0x0017880104e45542', 6540,4151, [new Endpoint(1, [0], [], '0x0017880104e45542'), new Endpoint(2, [0, 6], [], '0x0017880104e45542'), new Endpoint(3, [0, 6], [], '0x0017880104e45542')], true, "Mains (single phase)", 'lumi.ctrl_neutral2');
|
||||
const zigfred_plus = new Device('Router', '0xf4ce368a38be56a1', 6589, 0x129C, [new Endpoint(5, [0, 3, 4, 5, 6, 8, 0x0300, 0xFC42], [0xFC42], '0xf4ce368a38be56a1'), new Endpoint(7, [0, 3, 4, 5, 6, 8], [], '0xf4ce368a38be56a1'), new Endpoint(8, [0, 3, 4, 5, 6, 8], [], '0xf4ce368a38be56a1'), new Endpoint(9, [0, 3, 4, 5, 6, 8], [], '0xf4ce368a38be56a1'), new Endpoint(10, [0, 3, 4, 5, 6, 8], [], '0xf4ce368a38be56a1'), new Endpoint(11, [0, 3, 4, 5, 0x0102], [], '0xf4ce368a38be56a1'), new Endpoint(12, [0, 3, 4, 5, 0x0102], [], '0xf4ce368a38be56a1')], true, "Mains (single phase)", 'zigfred plus', false, 'Siglis');
|
||||
@@ -197,6 +199,8 @@ const devices = {
|
||||
'3157100': new Device('Router', '0x0017880104e44559', 6542,4151, [new Endpoint(1, [], [], '0x0017880104e44559')], true, "Mains (single phase)", '3157100', false, 'Centralite'),
|
||||
'J1': new Device('Router', '0x0017880104a44559', 6543,4151, [new Endpoint(1, [], [], '0x0017880104a44559')], true, "Mains (single phase)", 'J1 (5502)'),
|
||||
'TS0601_thermostat': TS0601_thermostat,
|
||||
'TS0601_switch': TS0601_switch,
|
||||
'TS0601_cover_switch': TS0601_cover_switch,
|
||||
'external_converter_device': new Device('EndDevice', '0x0017880104e45511', 1114, 'external', [new Endpoint(1, [], [], '0x0017880104e45511')], false, null, 'external_converter_device' ),
|
||||
'QS_Zigbee_D02_TRIAC_2C_LN':new Device('Router', '0x0017882194e45543', 6549,4151, [new Endpoint(1, [0], [], '0x0017882194e45543'), new Endpoint(2, [0, 6], [], '0x0017882194e45543')], true, "Mains (single phase)", 'TS110F', false, '_TYZB01_v8gtiaed'),
|
||||
'unknown': new Device('Router', '0x0017980134e45545', 6540,4151, [], true, "Mains (single phase)"),
|
||||
@@ -204,6 +208,8 @@ const devices = {
|
||||
'heating_actuator': new Device('Router', '0x0017880104e45562', 6545,4151, [new Endpoint(1, [0,3,4,513], [1026])], true, "Mains (single phase)", "heating.actuator"),
|
||||
'bj_scene_switch': new Device('EndDevice', '0xd85def11a1002caa', 50117, 4398, [new Endpoint(10, [0,4096], [3,4,5,6,8,25,768,4096], '0xd85def11a1002caa', [{target: bulb_color_2.endpoints[0], cluster: {ID: 8, name: 'genLevelCtrl'}}, {target: bulb_color_2.endpoints[0], cluster: {ID: 6, name: 'genOnOff'}}, {target: bulb_color_2.endpoints[0], cluster: {ID: 768, name: 'lightingColorCtrl'}},]), new Endpoint(11, [0,4096], [3,4,5,6,8,25,768,4096], '0xd85def11a1002caa')], true, 'Battery', 'RB01', false, 'Busch-Jaeger', '20161222', '1.2.0'),
|
||||
'GW003-AS-IN-TE-FC': new Device('Router', '0x0017548104a44669', 6545,4699, [new Endpoint(1, [3], [0,3,513,514], '0x0017548104a44669')], true, "Mains (single phase)", 'Adapter Zigbee FUJITSU'),
|
||||
'BMCT-SLZ': new Device('Router', '0x18fc26000000cafe', 6546,4617, [new Endpoint(1, [0,3,4,5,258,1794,2820,2821,64672], [10,25], '0x18fc26000000cafe')], true, "Mains (single phase)", 'RBSH-MMS-ZB-EU'),
|
||||
|
||||
}
|
||||
|
||||
const mock = {
|
||||
@@ -214,6 +220,7 @@ const mock = {
|
||||
touchlinkIdentify: jest.fn(),
|
||||
start: jest.fn(),
|
||||
backup: jest.fn(),
|
||||
coordinatorCheck: jest.fn(),
|
||||
isStopping: jest.fn(),
|
||||
permitJoin: jest.fn(),
|
||||
addInstallCode: jest.fn(),
|
||||
@@ -255,7 +262,7 @@ const mockConstructor = jest.fn().mockImplementation(() => mock);
|
||||
|
||||
jest.mock('zigbee-herdsman', () => ({
|
||||
Controller: mockConstructor,
|
||||
Zcl: {ManufacturerCode: {Philips: 4107}},
|
||||
Zcl: {ManufacturerCode: {SIGNIFY_NETHERLANDS_B_V: 4107}},
|
||||
}));
|
||||
|
||||
module.exports = {
|
||||
|
||||
+3
-2
@@ -1,6 +1,7 @@
|
||||
const utils = require('../lib/util/utils').default;
|
||||
const version = require('../package.json').version;
|
||||
const versionHerdsman = require('../node_modules/zigbee-herdsman/package.json').version;
|
||||
const versionHerdsmanConverters = require('../node_modules/zigbee-herdsman-converters/package.json').version;
|
||||
|
||||
describe('Utils', () => {
|
||||
it('Object has properties', () => {
|
||||
@@ -22,8 +23,8 @@ describe('Utils', () => {
|
||||
})
|
||||
|
||||
it('Check dependency version', async () => {
|
||||
var dependency = 'zigbee-herdsman';
|
||||
expect(await utils.getDependencyVersion(dependency)).toStrictEqual({"version": versionHerdsman});
|
||||
expect(await utils.getDependencyVersion('zigbee-herdsman')).toStrictEqual({"version": versionHerdsman});
|
||||
expect(await utils.getDependencyVersion('zigbee-herdsman-converters')).toStrictEqual({"version": versionHerdsmanConverters});
|
||||
})
|
||||
|
||||
it('To local iso string', async () => {
|
||||
|
||||
+6
-2
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"target": "es2018",
|
||||
"lib": ["es2018"],
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022"],
|
||||
"noImplicitAny": true,
|
||||
"noImplicitThis": true,
|
||||
"moduleResolution": "node",
|
||||
@@ -21,4 +22,7 @@
|
||||
"lib/**/*",
|
||||
"lib/util/settings.schema.json"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
],
|
||||
}
|
||||
@@ -1,37 +1,48 @@
|
||||
#!/bin/bash -e
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
NEED_RESTART=0
|
||||
|
||||
if [ -d data-backup ]; then
|
||||
echo "ERROR: Backup directory exists. May be previous restoring was failed?"
|
||||
echo "1. Save 'data-backup' and 'data' dirs to safe location to make possibility to restore config later."
|
||||
echo "2. Manually delete 'data-backup' dir and try again."
|
||||
exit 1
|
||||
echo "ERROR: Backup directory exists. May be previous restoring was failed?"
|
||||
echo "1. Save 'data-backup' and 'data' dirs to safe location to make possibility to restore config later."
|
||||
echo "2. Manually delete 'data-backup' dir and try again."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if which systemctl 2> /dev/null > /dev/null; then
|
||||
echo "Stopping Zigbee2MQTT..."
|
||||
sudo systemctl stop zigbee2mqtt
|
||||
echo "Checking Zigbee2MQTT status..."
|
||||
if systemctl is-active --quiet zigbee2mqtt; then
|
||||
echo "Stopping Zigbee2MQTT..."
|
||||
sudo systemctl stop zigbee2mqtt
|
||||
NEED_RESTART=1
|
||||
fi
|
||||
else
|
||||
echo "Skipped stopping Zigbee2MQTT, no systemctl found"
|
||||
echo "Skipped stopping Zigbee2MQTT, no systemctl found"
|
||||
fi
|
||||
|
||||
echo "Creating backup of configuration..."
|
||||
cp -R data data-backup
|
||||
|
||||
echo "Checking out changes to package-lock.json..."
|
||||
git checkout package-lock.json
|
||||
|
||||
echo "Updating..."
|
||||
git pull
|
||||
|
||||
echo "Installing dependencies..."
|
||||
npm ci
|
||||
|
||||
echo "Building..."
|
||||
npm run build
|
||||
|
||||
echo "Restore configuration..."
|
||||
cp -R data-backup/* data
|
||||
rm -rf data-backup
|
||||
|
||||
if which systemctl 2> /dev/null > /dev/null; then
|
||||
echo "Starting Zigbee2MQTT..."
|
||||
sudo systemctl start zigbee2mqtt
|
||||
else
|
||||
echo "Skipped starting Zigbee2MQTT, no systemctl found"
|
||||
if [ $NEED_RESTART -eq 1 ]; then
|
||||
echo "Starting Zigbee2MQTT..."
|
||||
sudo systemctl start zigbee2mqtt
|
||||
fi
|
||||
|
||||
echo "Done!"
|
||||
|
||||
Reference in New Issue
Block a user