mirror of
https://github.com/esphome/esphome.git
synced 2025-11-04 17:11:51 +00:00
Compare commits
1410 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adc8c1aa38 | ||
|
|
3a82f500d4 | ||
|
|
7d1d4831a8 | ||
|
|
1a98e882dc | ||
|
|
c943d84036 | ||
|
|
d07a6704d5 | ||
|
|
8cfcd5904c | ||
|
|
fb8846bb45 | ||
|
|
0d0733dd94 | ||
|
|
a67d58948d | ||
|
|
84c051d097 | ||
|
|
b918abfd54 | ||
|
|
7f41b7cd93 | ||
|
|
4759b4fe2e | ||
|
|
e2c8e69d12 | ||
|
|
917e8e155c | ||
|
|
a97e3d827d | ||
|
|
029014d9d6 | ||
|
|
e4c2922536 | ||
|
|
7133ae6aaa | ||
|
|
2f7f0ff3a1 | ||
|
|
df853bf61e | ||
|
|
f0ac753f9b | ||
|
|
4d56a975e6 | ||
|
|
d56c53c848 | ||
|
|
f83b16320d | ||
|
|
ac10e27f08 | ||
|
|
34df7a6072 | ||
|
|
e2cddf1005 | ||
|
|
ced423748e | ||
|
|
77fb02729e | ||
|
|
fef39b9fbe | ||
|
|
02810105fb | ||
|
|
baad92515b | ||
|
|
ab86ddcf02 | ||
|
|
bf8eddb13b | ||
|
|
e5eaf7a3fe | ||
|
|
50f32a3aa5 | ||
|
|
eb878710c1 | ||
|
|
311980e0e4 | ||
|
|
df73170e5a | ||
|
|
a12c6b5f35 | ||
|
|
522646c64d | ||
|
|
4791093e48 | ||
|
|
599a455150 | ||
|
|
2deef16ebe | ||
|
|
989b7be99b | ||
|
|
cd473e1395 | ||
|
|
e246ebfb2e | ||
|
|
8546ae56da | ||
|
|
9e227b0192 | ||
|
|
ba7737e9f8 | ||
|
|
98aa3d51ed | ||
|
|
e7cfb5492e | ||
|
|
9ed136dc3a | ||
|
|
9217216723 | ||
|
|
936c408a58 | ||
|
|
54427eac9a | ||
|
|
e809488cc0 | ||
|
|
ed26c57d99 | ||
|
|
2a49811f6e | ||
|
|
c95acd2568 | ||
|
|
6a4e0cf667 | ||
|
|
04f4dd8a22 | ||
|
|
f33d829ce9 | ||
|
|
578671ea94 | ||
|
|
d10300c330 | ||
|
|
e0555e140f | ||
|
|
093989406f | ||
|
|
cdb16f08f6 | ||
|
|
53139c293b | ||
|
|
6a8bdcc315 | ||
|
|
fe535939a3 | ||
|
|
09e6c11d73 | ||
|
|
8112bdfaa8 | ||
|
|
f7db9aaa9f | ||
|
|
435f972357 | ||
|
|
f82b46c16b | ||
|
|
bca96f91b2 | ||
|
|
6f83a49c63 | ||
|
|
72cce391ab | ||
|
|
ccc13cc9e1 | ||
|
|
020b2c05c8 | ||
|
|
4c37c17df1 | ||
|
|
8f67acadd8 | ||
|
|
ca13c4c1a6 | ||
|
|
d0c646c721 | ||
|
|
8a055675af | ||
|
|
28d2949ebe | ||
|
|
c4a0015997 | ||
|
|
f564be6aea | ||
|
|
988f15e6af | ||
|
|
37b6d442bd | ||
|
|
fb2467f6f0 | ||
|
|
29045b0435 | ||
|
|
311a48c64e | ||
|
|
01b3815f27 | ||
|
|
b0d1c801bd | ||
|
|
5aaac06f5b | ||
|
|
34adbf0588 | ||
|
|
0a4213182e | ||
|
|
b0c0258e70 | ||
|
|
8110e591d0 | ||
|
|
fe05d7aec1 | ||
|
|
57f5884070 | ||
|
|
f329c74a15 | ||
|
|
7c86f3fa9e | ||
|
|
203b8b01bf | ||
|
|
8a1034a92f | ||
|
|
aa0c2dedd9 | ||
|
|
d045908e05 | ||
|
|
f002a23d2d | ||
|
|
29d6d0a906 | ||
|
|
c8b58b5c23 | ||
|
|
01bfafc5f1 | ||
|
|
8c9948bb56 | ||
|
|
2d1abaa68e | ||
|
|
664a3df0b4 | ||
|
|
9ff893881c | ||
|
|
94f6c6861a | ||
|
|
b1d614e6c4 | ||
|
|
7fceb070e5 | ||
|
|
06440d0202 | ||
|
|
0ecf9f4f2f | ||
|
|
5c7c0834c0 | ||
|
|
f3a25de11d | ||
|
|
041bef8bcd | ||
|
|
8998c5f6dd | ||
|
|
6e83790308 | ||
|
|
d2d4eb4eae | ||
|
|
5942a3898c | ||
|
|
93421f0fa7 | ||
|
|
3a9ab50dd2 | ||
|
|
5abd91d6d5 | ||
|
|
c3da42516b | ||
|
|
6cb5cd48c2 | ||
|
|
ec1fae6883 | ||
|
|
746fd1122f | ||
|
|
9663760ec5 | ||
|
|
a3d73d1e23 | ||
|
|
d63e14a4b6 | ||
|
|
03944e6cd8 | ||
|
|
0d1028be2e | ||
|
|
6a85259e4d | ||
|
|
ebca936b7e | ||
|
|
31c4551890 | ||
|
|
dd470d4197 | ||
|
|
612822490b | ||
|
|
f8969605e8 | ||
|
|
dd24ffa24e | ||
|
|
d0dda48932 | ||
|
|
6349b5f654 | ||
|
|
a6ff02a3cf | ||
|
|
4f57bf786b | ||
|
|
6221f6d47d | ||
|
|
a922efeafa | ||
|
|
5aa42e5e66 | ||
|
|
708672ec7e | ||
|
|
d2cefbf224 | ||
|
|
adb7aa6950 | ||
|
|
77f322166e | ||
|
|
f3f6e54818 | ||
|
|
fb0fec1f25 | ||
|
|
b66af9fb4d | ||
|
|
6617d576a7 | ||
|
|
cd35ead890 | ||
|
|
9dc804ee27 | ||
|
|
a8ceeaa7b0 | ||
|
|
7092f7663e | ||
|
|
d9d2edeb08 | ||
|
|
dda1ddcb26 | ||
|
|
f0c890f160 | ||
|
|
4f52d43347 | ||
|
|
0ed7db979b | ||
|
|
9c78049359 | ||
|
|
7882105661 | ||
|
|
c000e1d6dd | ||
|
|
420dacb22d | ||
|
|
ae2f6ad4d1 | ||
|
|
2c28d79bf8 | ||
|
|
c5069edc78 | ||
|
|
282d9e138c | ||
|
|
72fcf2cbe1 | ||
|
|
6f49f5465b | ||
|
|
17b8bd8316 | ||
|
|
9b6b9c1fa2 | ||
|
|
609a2ca592 | ||
|
|
6dabf24bf3 | ||
|
|
7e88938932 | ||
|
|
c707e64685 | ||
|
|
a639690716 | ||
|
|
01222dbab7 | ||
|
|
93e2506279 | ||
|
|
f62d5d3b9d | ||
|
|
0665acd190 | ||
|
|
fea05e9d33 | ||
|
|
7a03c7d56f | ||
|
|
2dc2aec954 | ||
|
|
39c6c2417a | ||
|
|
ff72d6a146 | ||
|
|
603d0d0c7c | ||
|
|
28883f711b | ||
|
|
e914828add | ||
|
|
c1480029fb | ||
|
|
40f622949e | ||
|
|
63096ac2bc | ||
|
|
03d5a0ec1d | ||
|
|
1c873e0034 | ||
|
|
bcb47c306c | ||
|
|
01c4d3c225 | ||
|
|
c2aaae4818 | ||
|
|
3f678e218d | ||
|
|
c2a59cb476 | ||
|
|
f8a1bd4e79 | ||
|
|
d6e039a1d1 | ||
|
|
0f1a7c2b69 | ||
|
|
40ad9f4911 | ||
|
|
4116caff6a | ||
|
|
0b69f72315 | ||
|
|
c569f5ddcf | ||
|
|
62f9e181e0 | ||
|
|
235a97ea10 | ||
|
|
e541ae400c | ||
|
|
4822abde86 | ||
|
|
b7e52812f8 | ||
|
|
69118120d9 | ||
|
|
7cba0c6fb0 | ||
|
|
5fac67ce15 | ||
|
|
98c733108e | ||
|
|
782186e13d | ||
|
|
4e1f6518e8 | ||
|
|
53e0fe8e51 | ||
|
|
0e547390da | ||
|
|
86b52df839 | ||
|
|
d685fdf54a | ||
|
|
d9caab4108 | ||
|
|
44b68f140e | ||
|
|
3a3d97dfa7 | ||
|
|
47898b527c | ||
|
|
a35f36ad39 | ||
|
|
d13a397f8e | ||
|
|
df999723f8 | ||
|
|
8236e840a7 | ||
|
|
e5b3625f73 | ||
|
|
2e4645310b | ||
|
|
50a32b387e | ||
|
|
2059283707 | ||
|
|
8e3af515c9 | ||
|
|
6f88f0ea3f | ||
|
|
d2f37cf3f9 | ||
|
|
7c30d6254e | ||
|
|
64fb39a653 | ||
|
|
91895aa70c | ||
|
|
68dfaf238b | ||
|
|
ebf13a0ba0 | ||
|
|
2bff9937b7 | ||
|
|
256395c28d | ||
|
|
3346bc8bba | ||
|
|
6fe22a7e62 | ||
|
|
757b98748b | ||
|
|
7a778f3f33 | ||
|
|
993044c870 | ||
|
|
a8c1b63edb | ||
|
|
db7d946e1b | ||
|
|
41d9059a2f | ||
|
|
e26e0d7c01 | ||
|
|
ad41c07a1f | ||
|
|
9576d246ee | ||
|
|
988d3ea8ba | ||
|
|
0767b92b62 | ||
|
|
5732f3b044 | ||
|
|
712115b6ce | ||
|
|
9283559c6b | ||
|
|
6b393438e9 | ||
|
|
2064abe16d | ||
|
|
b605982f94 | ||
|
|
343b9ab455 | ||
|
|
dcb226b202 | ||
|
|
2243021b58 | ||
|
|
d5134e88b1 | ||
|
|
c59adf612f | ||
|
|
93b628d9a8 | ||
|
|
6bac551d9f | ||
|
|
70a35656e4 | ||
|
|
047c18eac0 | ||
|
|
b4a86ce6cf | ||
|
|
a82d8ea0c3 | ||
|
|
b778eed419 | ||
|
|
ad57faa9a9 | ||
|
|
a9b5e8d036 | ||
|
|
8be704e591 | ||
|
|
b622a8fa58 | ||
|
|
a519e5c475 | ||
|
|
d620b6dd5e | ||
|
|
99335d986e | ||
|
|
7895cd92cd | ||
|
|
8b2c032da6 | ||
|
|
da336247eb | ||
|
|
dabd27d4be | ||
|
|
fdda47db6e | ||
|
|
efa6fd03e5 | ||
|
|
9e3e34acf5 | ||
|
|
a2d0c1bf18 | ||
|
|
7663716ae8 | ||
|
|
c2cacb3478 | ||
|
|
84666b54b9 | ||
|
|
2b91c23bf3 | ||
|
|
3297267a16 | ||
|
|
a9e653724c | ||
|
|
5e79a1f500 | ||
|
|
d4ff98680a | ||
|
|
ba8d255cb4 | ||
|
|
06f4ad922c | ||
|
|
bff06e448b | ||
|
|
d48ffa2913 | ||
|
|
d97c3a7e01 | ||
|
|
0b1161f7ef | ||
|
|
061e1a471d | ||
|
|
a39d874600 | ||
|
|
de96376565 | ||
|
|
c54c20ab3c | ||
|
|
70fafa473b | ||
|
|
2e436eae6b | ||
|
|
fd7e861ff5 | ||
|
|
792108686c | ||
|
|
fa1b5117fd | ||
|
|
b0bd9e0a34 | ||
|
|
05dc97099a | ||
|
|
9de61fcf58 | ||
|
|
fc7348d46d | ||
|
|
8be2456c7e | ||
|
|
bb5f7249a6 | ||
|
|
7f7175b184 | ||
|
|
cf5c640ae4 | ||
|
|
6b9371d105 | ||
|
|
9a82057303 | ||
|
|
48584e94c4 | ||
|
|
fc94a5d0ee | ||
|
|
d8024a5928 | ||
|
|
2034ab4f6c | ||
|
|
24029cc918 | ||
|
|
9a9d5964ee | ||
|
|
4e4a512107 | ||
|
|
0729ed538e | ||
|
|
24b75b7ed6 | ||
|
|
58b70b42dd | ||
|
|
1496bc1b07 | ||
|
|
bfbf88b2ea | ||
|
|
e621b938e3 | ||
|
|
ec3618ecb8 | ||
|
|
792a24f38d | ||
|
|
652e8a015b | ||
|
|
59e6e798dd | ||
|
|
e5c2dbc7ec | ||
|
|
756f71c382 | ||
|
|
b7535693fa | ||
|
|
06a3505698 | ||
|
|
0372d17a11 | ||
|
|
4525588116 | ||
|
|
68e957c147 | ||
|
|
99f5ed1461 | ||
|
|
59f67796dc | ||
|
|
aafdfa933e | ||
|
|
3208c8ed1e | ||
|
|
6bf733e24e | ||
|
|
65d3e8fbfc | ||
|
|
a29d65d47c | ||
|
|
efa8f0730d | ||
|
|
0af1edefff | ||
|
|
023d26f521 | ||
|
|
5068619f1b | ||
|
|
5b2457af0b | ||
|
|
900b4f1af9 | ||
|
|
4c22a98b0b | ||
|
|
3b8ca80900 | ||
|
|
1ef6fd8fb0 | ||
|
|
942b0de7fd | ||
|
|
859cca49d1 | ||
|
|
dc6eff83ea | ||
|
|
38ff66debd | ||
|
|
1d2e0f74ea | ||
|
|
8f7ff25624 | ||
|
|
97aca8e54c | ||
|
|
95acf19067 | ||
|
|
3d0899aa58 | ||
|
|
bf60e40d0b | ||
|
|
c9094ca537 | ||
|
|
68b3fd6b8f | ||
|
|
9323b3a248 | ||
|
|
b55e9329d9 | ||
|
|
a5b4105971 | ||
|
|
d1feaa935d | ||
|
|
6919930aaa | ||
|
|
69633826bb | ||
|
|
771162bfb1 | ||
|
|
ba785e29e9 | ||
|
|
138d6e505b | ||
|
|
2748e6ba29 | ||
|
|
dbd4e927d8 | ||
|
|
e73d47918f | ||
|
|
b881bc071e | ||
|
|
1d0395d1c7 | ||
|
|
616c787e37 | ||
|
|
0c4de2bc97 | ||
|
|
2c7b104f4a | ||
|
|
78951c197a | ||
|
|
07c1cf7137 | ||
|
|
d26141151a | ||
|
|
f59dbe4a88 | ||
|
|
8dae7f8225 | ||
|
|
5811389891 | ||
|
|
debcaf6fb7 | ||
|
|
b8d10a62c2 | ||
|
|
d2b209234f | ||
|
|
34c9d8be50 | ||
|
|
ae57ad0c81 | ||
|
|
0c1520dd9c | ||
|
|
d594f43ebd | ||
|
|
125c693e3f | ||
|
|
ad2f857e15 | ||
|
|
e445d6aada | ||
|
|
88fbb0ffbb | ||
|
|
231908fe9f | ||
|
|
f137cc10f4 | ||
|
|
c2f5ac9eba | ||
|
|
5764c988af | ||
|
|
ccc2fbfd67 | ||
|
|
1a8f8adc2a | ||
|
|
7a242bb4ed | ||
|
|
10b4adb8e6 | ||
|
|
3b8bb09ae3 | ||
|
|
83b7181bcb | ||
|
|
8886b7e141 | ||
|
|
7dcc4d030b | ||
|
|
b9398897c1 | ||
|
|
140db85d21 | ||
|
|
ccce4b19e8 | ||
|
|
8cb9be7560 | ||
|
|
953f0569fb | ||
|
|
34c229fd33 | ||
|
|
958ad0d750 | ||
|
|
36ddd9dd69 | ||
|
|
38259c96c9 | ||
|
|
c054fb8a2c | ||
|
|
5a0b8328d8 | ||
|
|
ffa19426d7 | ||
|
|
c123804294 | ||
|
|
4e24551b90 | ||
|
|
657b1c60ae | ||
|
|
dc54b17778 | ||
|
|
1fb214165b | ||
|
|
51cb5da7f0 | ||
|
|
81b2fd78f5 | ||
|
|
69002fb1e6 | ||
|
|
75332a752d | ||
|
|
b528f48417 | ||
|
|
ec7a79049a | ||
|
|
6ddad6b299 | ||
|
|
16dc7762f9 | ||
|
|
41f84447cc | ||
|
|
ce073a704b | ||
|
|
113232ebb6 | ||
|
|
dc0ed8857f | ||
|
|
bb6b77bd98 | ||
|
|
dcc80f9032 | ||
|
|
dd554bcdf4 | ||
|
|
f376a39e55 | ||
|
|
8dcc9d6b66 | ||
|
|
a13a1225b7 | ||
|
|
0ec84be5da | ||
|
|
b1cefb7e3e | ||
|
|
cc0c1c08b9 | ||
|
|
3a67884451 | ||
|
|
72e716cdf1 | ||
|
|
40e06c9819 | ||
|
|
ad6c5ff11d | ||
|
|
335512e232 | ||
|
|
2622e59b0b | ||
|
|
35e6a13cd1 | ||
|
|
a576c9f21f | ||
|
|
b48490badc | ||
|
|
71a438e2cb | ||
|
|
272d6f2a8b | ||
|
|
5c22065135 | ||
|
|
e7dd6c52ac | ||
|
|
3bf042dce9 | ||
|
|
09ed1aed93 | ||
|
|
53d3718028 | ||
|
|
2b5dce5232 | ||
|
|
9ad84150aa | ||
|
|
64f798d4b2 | ||
|
|
f43e04e15a | ||
|
|
88d72f8c9a | ||
|
|
9826726a72 | ||
|
|
c66d0550e8 | ||
|
|
4aeacfd16e | ||
|
|
58fa63ad88 | ||
|
|
94f944dc9c | ||
|
|
116ddbdd01 | ||
|
|
1c0697b5d4 | ||
|
|
434ca47ea0 | ||
|
|
397ef72b16 | ||
|
|
7ca9245735 | ||
|
|
69856286e8 | ||
|
|
ad43d6a5bc | ||
|
|
1e5004f495 | ||
|
|
253161d3d0 | ||
|
|
ab47e201c7 | ||
|
|
42984fa72a | ||
|
|
e7864a28a1 | ||
|
|
21803607e7 | ||
|
|
c0523590b4 | ||
|
|
c7f091ab10 | ||
|
|
7479e0aada | ||
|
|
62b366a5ec | ||
|
|
2b39988707 | ||
|
|
f9e7291050 | ||
|
|
4de642ff28 | ||
|
|
0384efcfc2 | ||
|
|
bf91443f38 | ||
|
|
4a5970b4af | ||
|
|
e3fd68c849 | ||
|
|
df0de2fc2d | ||
|
|
0c3568fad5 | ||
|
|
976f5d91ed | ||
|
|
0f3d4d9a47 | ||
|
|
ad1f4429c9 | ||
|
|
7590d5eacb | ||
|
|
c5974b8833 | ||
|
|
511c8de6f3 | ||
|
|
a718ac7ee0 | ||
|
|
ef832becf1 | ||
|
|
3a62455948 | ||
|
|
297824e2d7 | ||
|
|
d92f297bc0 | ||
|
|
7a0827e3d0 | ||
|
|
ef256a64b8 | ||
|
|
1de941e837 | ||
|
|
28b65cb810 | ||
|
|
6ff3942e8b | ||
|
|
ef5d959788 | ||
|
|
5bbee1a1fe | ||
|
|
6a2c58fcc0 | ||
|
|
bdb9546ca3 | ||
|
|
46af4cad6e | ||
|
|
76a238912b | ||
|
|
4e6bdb31ac | ||
|
|
80d03a631e | ||
|
|
6b27f2d2cf | ||
|
|
7cb6729fa7 | ||
|
|
2f46267994 | ||
|
|
cdda648360 | ||
|
|
f2d677d51a | ||
|
|
c2ee0f0864 | ||
|
|
2a84db7f85 | ||
|
|
8187a4bce9 | ||
|
|
97681d142e | ||
|
|
b2430097f2 | ||
|
|
7da12a878f | ||
|
|
a31700e16f | ||
|
|
7854522792 | ||
|
|
a6a9ebfde2 | ||
|
|
c6cbe2748e | ||
|
|
f9a7f00843 | ||
|
|
f0b183a552 | ||
|
|
338ada5c9f | ||
|
|
ef88f9923f | ||
|
|
6f8c7d9ec4 | ||
|
|
ec769ccf72 | ||
|
|
045952939e | ||
|
|
1c51cac5ba | ||
|
|
ea11462e1e | ||
|
|
62f9736b1d | ||
|
|
1f8a1f0046 | ||
|
|
172507acb5 | ||
|
|
434ab65c16 | ||
|
|
909a526967 | ||
|
|
cd6f4fb93f | ||
|
|
c19458696e | ||
|
|
cb5f793ede | ||
|
|
318b930e9f | ||
|
|
9296a078a7 | ||
|
|
5dc776e55f | ||
|
|
72d60f30f7 | ||
|
|
869743a742 | ||
|
|
7b03e07908 | ||
|
|
348f880e15 | ||
|
|
737188ae50 | ||
|
|
db21731b14 | ||
|
|
cdb4fa2487 | ||
|
|
514204f0d4 | ||
|
|
ead597d0fb | ||
|
|
afbf989715 | ||
|
|
01b62a16c3 | ||
|
|
c5eba04517 | ||
|
|
282313ab52 | ||
|
|
d274545e77 | ||
|
|
45ac577c4d | ||
|
|
09402fdb22 | ||
|
|
89e7448007 | ||
|
|
1ea6f957bc | ||
|
|
f44fca0a4b | ||
|
|
52d2f62a57 | ||
|
|
d3fda37615 | ||
|
|
cbe3092404 | ||
|
|
6dfe3039d0 | ||
|
|
d6009453df | ||
|
|
2a8668ea60 | ||
|
|
cc0d433621 | ||
|
|
c81323ef91 | ||
|
|
1fe89fb364 | ||
|
|
961c27f1c2 | ||
|
|
fe4a14e6cc | ||
|
|
ee58ad1ac0 | ||
|
|
c0ff899812 | ||
|
|
d9c938de33 | ||
|
|
56547b3d50 | ||
|
|
5026bc7a78 | ||
|
|
27364ee72c | ||
|
|
ece71a0228 | ||
|
|
073828235f | ||
|
|
41bcc8c0f4 | ||
|
|
a0ea2aae6e | ||
|
|
f34b46a621 | ||
|
|
7217a4f7a4 | ||
|
|
6383eca54a | ||
|
|
e55bd1e559 | ||
|
|
9e8b701dea | ||
|
|
a4431abea8 | ||
|
|
5844c1767b | ||
|
|
9a70bfa471 | ||
|
|
b406c6403c | ||
|
|
499625f266 | ||
|
|
6b773553fc | ||
|
|
15fe049a99 | ||
|
|
e4555f6997 | ||
|
|
470071e0b0 | ||
|
|
ea1be8e7bf | ||
|
|
84a830195f | ||
|
|
e62c3e00c1 | ||
|
|
07e790f900 | ||
|
|
640142fc0c | ||
|
|
5c339d4597 | ||
|
|
a4931f5d78 | ||
|
|
5e1e543b06 | ||
|
|
df929f9445 | ||
|
|
d8e719d1c4 | ||
|
|
3067e482fc | ||
|
|
ed5930e934 | ||
|
|
ffea3597f4 | ||
|
|
193d3e0206 | ||
|
|
c8f4fbb7dd | ||
|
|
c855bc31b4 | ||
|
|
b924b179ab | ||
|
|
3df0fee3de | ||
|
|
b601560e81 | ||
|
|
e5775cf812 | ||
|
|
26dd1f8532 | ||
|
|
5143a5b5c5 | ||
|
|
15ce27992e | ||
|
|
dbc2812022 | ||
|
|
dce3713f12 | ||
|
|
f849d45bb6 | ||
|
|
8ad06fb9ea | ||
|
|
9124d9d6e6 | ||
|
|
45ebe51e4f | ||
|
|
407661d56b | ||
|
|
998d4229af | ||
|
|
a02d2e2e11 | ||
|
|
72fa68849f | ||
|
|
33f17f75a0 | ||
|
|
23edb18d7e | ||
|
|
07ff3a853f | ||
|
|
2cf36bdb46 | ||
|
|
50848c2f4d | ||
|
|
d32633b3c7 | ||
|
|
b37739eec2 | ||
|
|
28f87dc804 | ||
|
|
41879e41e6 | ||
|
|
fc0a6546a2 | ||
|
|
ffd4280d6c | ||
|
|
f859b346a6 | ||
|
|
cb0677cafe | ||
|
|
c6956527d1 | ||
|
|
72c6bfaa50 | ||
|
|
7927b5f624 | ||
|
|
b7aad39daf | ||
|
|
f48de6dd43 | ||
|
|
79d73d8f8b | ||
|
|
cc5947467f | ||
|
|
e152f128c8 | ||
|
|
99bd808ebe | ||
|
|
beb5f3dc9d | ||
|
|
f5c3b3446f | ||
|
|
db3b955b0f | ||
|
|
f431c7402f | ||
|
|
5516f65971 | ||
|
|
9471df0a1b | ||
|
|
6d39f64be7 | ||
|
|
4907e6f6d7 | ||
|
|
1ccee86705 | ||
|
|
542fb2175b | ||
|
|
6ec9cfb044 | ||
|
|
66e0ff8392 | ||
|
|
1fb0a7109d | ||
|
|
b89d0a9a73 | ||
|
|
4bb779d9a5 | ||
|
|
386a5b6362 | ||
|
|
e32a999cd0 | ||
|
|
192eb49589 | ||
|
|
5d70ff702b | ||
|
|
a7b05db2a1 | ||
|
|
45e346cf1b | ||
|
|
80e2bfada3 | ||
|
|
16e7bd0388 | ||
|
|
b3fb35783e | ||
|
|
a79c6aa9e0 | ||
|
|
4bb58b2de9 | ||
|
|
4e10881331 | ||
|
|
cec4a81e14 | ||
|
|
da45923d05 | ||
|
|
31a61b598b | ||
|
|
9c0506592b | ||
|
|
beeb0c7c5a | ||
|
|
b2f05faee0 | ||
|
|
bfbc6a4bad | ||
|
|
8c9e0e552d | ||
|
|
8aaf9fd83f | ||
|
|
08057720b8 | ||
|
|
bfaa648837 | ||
|
|
d504daef91 | ||
|
|
8375e1d64d | ||
|
|
cf5193d3e5 | ||
|
|
b8d3ef2f49 | ||
|
|
3bf6320030 | ||
|
|
708b928c73 | ||
|
|
649366ff44 | ||
|
|
e5c9e87fad | ||
|
|
c490388e80 | ||
|
|
24ec5a6e9d | ||
|
|
f3d9d707b6 | ||
|
|
090e10730c | ||
|
|
fbc84861c7 | ||
|
|
e763469af8 | ||
|
|
6df1d5222d | ||
|
|
58fb7a02f6 | ||
|
|
3d51ac8df0 | ||
|
|
6fe4ff7f85 | ||
|
|
3c0c514e44 | ||
|
|
2253d4bc16 | ||
|
|
e5cc19de43 | ||
|
|
ed5e2dd332 | ||
|
|
09b7c6f550 | ||
|
|
df315a1f51 | ||
|
|
7ee4bb621c | ||
|
|
24874f4c3c | ||
|
|
c128880033 | ||
|
|
a66e94a0b0 | ||
|
|
56870ed4a8 | ||
|
|
3ac720df47 | ||
|
|
1bc757ad06 | ||
|
|
f72abc6f3d | ||
|
|
5ac88de985 | ||
|
|
5404617d43 | ||
|
|
12467a18e6 | ||
|
|
1db7043a4d | ||
|
|
49932747b3 | ||
|
|
55db190875 | ||
|
|
71fe2f7ed3 | ||
|
|
ffc112c9d0 | ||
|
|
d3e48e296f | ||
|
|
14f6ae75ea | ||
|
|
c84efe64d3 | ||
|
|
10e89a7dbb | ||
|
|
ef44acbf10 | ||
|
|
06da540ab0 | ||
|
|
0826b367d6 | ||
|
|
329bf861d6 | ||
|
|
9dcd3d18a0 | ||
|
|
40c017fd54 | ||
|
|
db66cd88b6 | ||
|
|
f0bcf81a98 | ||
|
|
86c205fe43 | ||
|
|
6a0b343289 | ||
|
|
c6414138c7 | ||
|
|
36b355eb82 | ||
|
|
9ca4e8f32a | ||
|
|
1b88b7a166 | ||
|
|
caf352ff06 | ||
|
|
54106179a1 | ||
|
|
607601b3a4 | ||
|
|
f58828cb82 | ||
|
|
11330af05f | ||
|
|
fbe1bca1b9 | ||
|
|
24a5325db3 | ||
|
|
1ec3140759 | ||
|
|
ca8db7696e | ||
|
|
c9190574a9 | ||
|
|
bfeb0b3639 | ||
|
|
cbc1334b8d | ||
|
|
08cbb97ec9 | ||
|
|
5719cc1a24 | ||
|
|
d9513e5ff2 | ||
|
|
b5a0e8b2c0 | ||
|
|
b32b918936 | ||
|
|
0f47ffd908 | ||
|
|
cd018ad3a5 | ||
|
|
24dfecb6f0 | ||
|
|
ab027a6ae2 | ||
|
|
556d071e7f | ||
|
|
939fb313df | ||
|
|
b5639a6472 | ||
|
|
f50e40e0b8 | ||
|
|
6f07421911 | ||
|
|
adf48246a9 | ||
|
|
7be9291b13 | ||
|
|
ea9e75039b | ||
|
|
a5fb036011 | ||
|
|
e55506f9db | ||
|
|
50ec1d0445 | ||
|
|
3d5e1d8d91 | ||
|
|
db2128a344 | ||
|
|
cae283dc86 | ||
|
|
7afcb0fb04 | ||
|
|
10f830c3ef | ||
|
|
7a5c3aa7ed | ||
|
|
2b50406856 | ||
|
|
10a2a7e0fc | ||
|
|
7a564b222d | ||
|
|
21db43db06 | ||
|
|
5009b3029f | ||
|
|
57a029189c | ||
|
|
671d68bc2c | ||
|
|
5946c37925 | ||
|
|
17a37b1de9 | ||
|
|
e7827a6997 | ||
|
|
2347e043a9 | ||
|
|
00965fe19e | ||
|
|
9681dfb458 | ||
|
|
5e631bc6ba | ||
|
|
b5f660398c | ||
|
|
d50bdf619f | ||
|
|
4e448b21ff | ||
|
|
2a78c2970d | ||
|
|
0cb715bb76 | ||
|
|
7d03823afd | ||
|
|
8e1c9f5042 | ||
|
|
980b7cda8f | ||
|
|
3a72dd5cb6 | ||
|
|
3178243811 | ||
|
|
d30e2f2a4f | ||
|
|
3637be251e | ||
|
|
2aea27d272 | ||
|
|
ceb9b1d1ff | ||
|
|
ccfa1e23f0 | ||
|
|
290da8df2d | ||
|
|
4b1d73791d | ||
|
|
7e8012c1a0 | ||
|
|
15cd602e8b | ||
|
|
598f5b241f | ||
|
|
335e69e6cd | ||
|
|
05fe5db030 | ||
|
|
710096b1c6 | ||
|
|
07b882c801 | ||
|
|
3e5331a263 | ||
|
|
897277992b | ||
|
|
1424091ee5 | ||
|
|
61ec16cdfc | ||
|
|
e5cb5756aa | ||
|
|
9e1c3e8f01 | ||
|
|
448e1690aa | ||
|
|
8267f01ccd | ||
|
|
6f9439e1bc | ||
|
|
06994c0dfc | ||
|
|
dee5d639e2 | ||
|
|
df6730be55 | ||
|
|
6226dae05c | ||
|
|
9c6a475a6e | ||
|
|
8294d10d5b | ||
|
|
67558bec47 | ||
|
|
6c1ef398bb | ||
|
|
84873d4074 | ||
|
|
58a0b28a39 | ||
|
|
0469e19f54 | ||
|
|
dbcfa7b599 | ||
|
|
b37d3a66cc | ||
|
|
7e495a5e27 | ||
|
|
c41547fd4a | ||
|
|
0d47d41c85 | ||
|
|
df68403b6d | ||
|
|
57bdc2b885 | ||
|
|
f565ff5def | ||
|
|
8ece639987 | ||
|
|
b35f509784 | ||
|
|
41a3a17456 | ||
|
|
cbbafbcca2 | ||
|
|
c75566b374 | ||
|
|
f1954df573 | ||
|
|
7279f1fcc1 | ||
|
|
d7432f7c10 | ||
|
|
b0a0a153f3 | ||
|
|
9e4fa5dcf1 | ||
|
|
024632dbd0 | ||
|
|
0a545a28b9 | ||
|
|
0f2df59998 | ||
|
|
0809673ba9 | ||
|
|
b386284180 | ||
|
|
515519bc87 | ||
|
|
5404163be0 | ||
|
|
29a7d32f77 | ||
|
|
687a7e9b2f | ||
|
|
09e8782318 | ||
|
|
0b193eee43 | ||
|
|
f2aea02210 | ||
|
|
194f922312 | ||
|
|
fea3c48098 | ||
|
|
c2f57baec2 | ||
|
|
f4a140e126 | ||
|
|
ab506b09fe | ||
|
|
87e1cdeedb | ||
|
|
81a36146ef | ||
|
|
7333123ba4 | ||
|
|
d99c5ed890 | ||
|
|
04740fbcbb | ||
|
|
6a7440f7d3 | ||
|
|
14299bb2cc | ||
|
|
66cebfc992 | ||
|
|
108b8e6705 | ||
|
|
4eaa6afa4d | ||
|
|
f643a46bbf | ||
|
|
aae63a7ff3 | ||
|
|
582567696e | ||
|
|
2e0c89409d | ||
|
|
7fa4a68a27 | ||
|
|
f1c5e2ef81 | ||
|
|
b526155cce | ||
|
|
62c3f301e7 | ||
|
|
38cb988809 | ||
|
|
7bb7456a8b | ||
|
|
0372e12b81 | ||
|
|
a6873c1520 | ||
|
|
f11220da3a | ||
|
|
b976ac54c8 | ||
|
|
78026e766f | ||
|
|
b4cd8d21a5 | ||
|
|
7552893311 | ||
|
|
21c896d8f8 | ||
|
|
4b7fe202ec | ||
|
|
bb9793d5b7 | ||
|
|
e99af991ec | ||
|
|
abf3708cc2 | ||
|
|
4395d6156d | ||
|
|
9f4519210f | ||
|
|
b0506afa5b | ||
|
|
8cbb379898 | ||
|
|
04ba53c870 | ||
|
|
b226215593 | ||
|
|
19970729a9 | ||
|
|
f310cacd41 | ||
|
|
5ff7c8418c | ||
|
|
0bdb48bcac | ||
|
|
99c775d8cb | ||
|
|
4d43396835 | ||
|
|
92321e219a | ||
|
|
c422b2fb0b | ||
|
|
8aa72f4c1e | ||
|
|
d8e33c5a69 | ||
|
|
15f9677d33 | ||
|
|
219b225ac0 | ||
|
|
2ac232e634 | ||
|
|
710866ff4e | ||
|
|
662773b075 | ||
|
|
875b803483 | ||
|
|
6e5cfac927 | ||
|
|
97eaf3d4a1 | ||
|
|
366552a969 | ||
|
|
57b07441a1 | ||
|
|
fb57ab0add | ||
|
|
d6717c0032 | ||
|
|
f72389147d | ||
|
|
a509f6ccd2 | ||
|
|
add484a2ea | ||
|
|
a17a6d5346 | ||
|
|
be9439f10d | ||
|
|
96a50f5c6b | ||
|
|
3c0414c420 | ||
|
|
b450d4c734 | ||
|
|
d536509a63 | ||
|
|
11f1e28139 | ||
|
|
379c3e98f5 | ||
|
|
5ea77894b7 | ||
|
|
d54b4e7c44 | ||
|
|
d8b3af3815 | ||
|
|
2b04152482 | ||
|
|
331a3ac387 | ||
|
|
7eee3cdc7f | ||
|
|
b12c7432e0 | ||
|
|
696643d037 | ||
|
|
7e54f97003 | ||
|
|
77dbf84e55 | ||
|
|
2350c5054c | ||
|
|
73accf747f | ||
|
|
0d3e6b2c4c | ||
|
|
b3d7cc637b | ||
|
|
2147bcbc29 | ||
|
|
980c2d4cae | ||
|
|
d2ebfd2833 | ||
|
|
bd782fc828 | ||
|
|
23560e608c | ||
|
|
f1377b560e | ||
|
|
72108684ea | ||
|
|
c6adaaea97 | ||
|
|
91999a38ca | ||
|
|
b34eed125d | ||
|
|
2abe09529a | ||
|
|
9aaaf4dd4b | ||
|
|
cbfbcf7f1b | ||
|
|
68316cbcf9 | ||
|
|
2f4b9263c3 | ||
|
|
c2623a08e3 | ||
|
|
9f625ee7d1 | ||
|
|
2f85c27a05 | ||
|
|
c612a3bf60 | ||
|
|
a01f5f5cf1 | ||
|
|
87328686a0 | ||
|
|
81c11ba1f7 | ||
|
|
49b17c5a2d | ||
|
|
de06a781ff | ||
|
|
8e77e3c685 | ||
|
|
a687b083ae | ||
|
|
b9e5c7eb35 | ||
|
|
1a6a063e04 | ||
|
|
d85b7a6bd0 | ||
|
|
1c4700f447 | ||
|
|
c7651dc40d | ||
|
|
eda1c471ad | ||
|
|
c7ef18fbc4 | ||
|
|
901ec918b1 | ||
|
|
6bdae55ee1 | ||
|
|
dfb96e4b7f | ||
|
|
ff2c316b18 | ||
|
|
5be52f71f9 | ||
|
|
42873dd37c | ||
|
|
f93e7d4e3a | ||
|
|
bbcd523967 | ||
|
|
68cbe58d00 | ||
|
|
115bca98f1 | ||
|
|
ed0b34b2fe | ||
|
|
ab34401421 | ||
|
|
eed0c18d65 | ||
|
|
83400d0417 | ||
|
|
77a6461c9d | ||
|
|
6db9d1122f | ||
|
|
83bef85415 | ||
|
|
b5b3914bbf | ||
|
|
0d90ef94ae | ||
|
|
c08b21b7cd | ||
|
|
be3cb9ef00 | ||
|
|
f7b3f52731 | ||
|
|
9220d9fc52 | ||
|
|
68c8547067 | ||
|
|
1468acfced | ||
|
|
b141aea4c0 | ||
|
|
a88c022406 | ||
|
|
5389382798 | ||
|
|
4765173778 | ||
|
|
07a9cb910f | ||
|
|
f408f074c4 | ||
|
|
f1f2640d0e | ||
|
|
27d7d7ca69 | ||
|
|
c0fc5b48ae | ||
|
|
8735d3b83e | ||
|
|
ca59dd1302 | ||
|
|
eccdef8211 | ||
|
|
f2ebfe7aef | ||
|
|
cac5b356db | ||
|
|
e5a38ce748 | ||
|
|
7d9d9fcf36 | ||
|
|
156104d5f5 | ||
|
|
f0aba6ceb2 | ||
|
|
c248ba4043 | ||
|
|
ab07ee57c6 | ||
|
|
c615dc573a | ||
|
|
eae3d72a4d | ||
|
|
7b8d826704 | ||
|
|
e7baa42e63 | ||
|
|
2f32833a22 | ||
|
|
f6935a4b4b | ||
|
|
332c9e891b | ||
|
|
1caabb6419 | ||
|
|
f41f7994a3 | ||
|
|
e39f314e7a | ||
|
|
7f34561e53 | ||
|
|
34606b0f1f | ||
|
|
c51b509501 | ||
|
|
b91ee4847f | ||
|
|
625463d871 | ||
|
|
6433a01e07 | ||
|
|
56cc31e8e7 | ||
|
|
3af297aa76 | ||
|
|
996ec59d28 | ||
|
|
95593eeeab | ||
|
|
dad244fb7a | ||
|
|
15b5968418 | ||
|
|
64a45dc6a6 | ||
|
|
7cfede5b83 | ||
|
|
e4d17e0b15 | ||
|
|
e79f7ce290 | ||
|
|
adb5d27d95 | ||
|
|
8456a8cecb | ||
|
|
b9f66373c1 | ||
|
|
9ac365feef | ||
|
|
bcc77c73e1 | ||
|
|
8b11e5aeb1 | ||
|
|
43bbd58a44 | ||
|
|
7feffa64f3 | ||
|
|
ea0977abb4 | ||
|
|
cb48394e8a | ||
|
|
4c83dc7c28 | ||
|
|
e10ab1da78 | ||
|
|
1b0e60374b | ||
|
|
3a760fbb44 | ||
|
|
f5441a87e3 | ||
|
|
e2a812fa4b | ||
|
|
5b5ead872b | ||
|
|
6ef57a2973 | ||
|
|
3e9c7f2e9f | ||
|
|
430598b7a1 | ||
|
|
03cfd78c59 | ||
|
|
91611b09b4 | ||
|
|
ecd115851f | ||
|
|
4a1e50fed1 | ||
|
|
d6d037047b | ||
|
|
b5734c2b20 | ||
|
|
723fb7eaac | ||
|
|
63a9acaa19 | ||
|
|
0524f8c677 | ||
|
|
70b62f272e | ||
|
|
ced11bc707 | ||
|
|
6b9c084162 | ||
|
|
644ce2a26c | ||
|
|
5425e45851 | ||
|
|
f0089b7940 | ||
|
|
4b44280d53 | ||
|
|
f045382d20 | ||
|
|
db3fa1ade7 | ||
|
|
f83950fd75 | ||
|
|
4dd1bf920d | ||
|
|
98755f3621 | ||
|
|
c3a8a044b9 | ||
|
|
15b5ea43a7 | ||
|
|
12fce7a08d | ||
|
|
0991ab3543 | ||
|
|
65d2b37496 | ||
|
|
94d518a418 | ||
|
|
7cca673902 | ||
|
|
384f8d97d8 | ||
|
|
c82d5d63e3 | ||
|
|
653a3d5d11 | ||
|
|
884b7201de | ||
|
|
85d2f24447 | ||
|
|
935992bcb3 | ||
|
|
dc15d1c8ec | ||
|
|
6beb9e568a | ||
|
|
7178f10bda | ||
|
|
ec683fc227 | ||
|
|
d4e65eb82a | ||
|
|
10c6601b0a | ||
|
|
73940bc1bd | ||
|
|
9b7fb829f9 | ||
|
|
c51d8c9021 | ||
|
|
d8a6dfe5ce | ||
|
|
5f7cef0b06 | ||
|
|
48ff2ffc68 | ||
|
|
b3b9ccd314 | ||
|
|
1308236429 | ||
|
|
63d6b610b8 | ||
|
|
8823024509 | ||
|
|
4896f870f0 | ||
|
|
7e482901d9 | ||
|
|
07b309e65d | ||
|
|
6bbb5e9b56 | ||
|
|
867fecd157 | ||
|
|
05388d2dfc | ||
|
|
e06b6d7140 | ||
|
|
859e508392 | ||
|
|
534ce11d54 | ||
|
|
e63c7b483b | ||
|
|
f57980b069 | ||
|
|
7006aa0d2a | ||
|
|
bb86db869a | ||
|
|
4c4dd23e15 | ||
|
|
8051c1ca99 | ||
|
|
fe5a6847b5 | ||
|
|
a779592414 | ||
|
|
112215848d | ||
|
|
3dee057826 | ||
|
|
4406a08fa7 | ||
|
|
c33077bc61 | ||
|
|
34db9d9ef2 | ||
|
|
1184bbc976 | ||
|
|
a3eb2a7ee0 | ||
|
|
d13134135b | ||
|
|
b4f57972fb | ||
|
|
6a5eb43454 | ||
|
|
04ec1c8b56 | ||
|
|
d7ad155885 | ||
|
|
85461a752a | ||
|
|
039fbc677d | ||
|
|
ea56a39e11 | ||
|
|
55e9560e74 | ||
|
|
3cb4b4ca03 | ||
|
|
11d2866755 | ||
|
|
2c517e3e8c | ||
|
|
42739f0b22 | ||
|
|
a1f9b0d7f2 | ||
|
|
c3b8c84131 | ||
|
|
471b82f727 | ||
|
|
92b85f98e8 | ||
|
|
c092d92d45 | ||
|
|
e514a1fcd4 | ||
|
|
a1b28cb36e | ||
|
|
3f2d9abfe6 | ||
|
|
f3ec4b514d | ||
|
|
fc5798fa71 | ||
|
|
95d7ad543f | ||
|
|
d9b2903d78 | ||
|
|
32a664eedc | ||
|
|
e7477890cf | ||
|
|
9bf72ff05f | ||
|
|
5461f87ff0 | ||
|
|
1c58b17235 | ||
|
|
d34a1c3ed6 | ||
|
|
22e3bc7cfe | ||
|
|
955c96731e | ||
|
|
54a173dbf1 | ||
|
|
9ff8240802 | ||
|
|
7bbb5213f3 | ||
|
|
b8b30599ee | ||
|
|
e083d7f4d0 | ||
|
|
a57580b5ab | ||
|
|
e22f1fc044 | ||
|
|
e09ee8f23d | ||
|
|
6ec546a6a4 | ||
|
|
877367677b | ||
|
|
8be4086224 | ||
|
|
871d3b66fb | ||
|
|
87358e8843 | ||
|
|
5c06cd8eb3 | ||
|
|
46b4c970d1 | ||
|
|
49f46a7cdd | ||
|
|
1627dff166 | ||
|
|
cee08debff | ||
|
|
912793eddf | ||
|
|
eaa5200a35 | ||
|
|
a7687c3e17 | ||
|
|
932e0469f7 | ||
|
|
15ab8918af | ||
|
|
d0dfc94a61 | ||
|
|
5a2984d03a | ||
|
|
c89018a431 | ||
|
|
1031ea4313 | ||
|
|
5b0fbbaada | ||
|
|
0e4f1ac40d | ||
|
|
946db3fd50 | ||
|
|
3dfc8d4291 | ||
|
|
4f5e4f3b86 | ||
|
|
505d1d78fb | ||
|
|
7af1c04493 | ||
|
|
855c98d815 | ||
|
|
c26ea7e4e0 | ||
|
|
c39ac9edfe | ||
|
|
af04f565cf | ||
|
|
2b9054d3b2 | ||
|
|
2234f6aacf | ||
|
|
be965a60eb | ||
|
|
5596751c2c | ||
|
|
6417d8132d | ||
|
|
5624fafb3a | ||
|
|
2eb5f89d82 | ||
|
|
e30f17f64f | ||
|
|
1ba560dc9e | ||
|
|
8c86a18dc6 | ||
|
|
b2d516c70a | ||
|
|
45940b0514 | ||
|
|
97e76d64d6 | ||
|
|
756c6721e9 | ||
|
|
4c390d9f9f | ||
|
|
0d0954d74b | ||
|
|
7672ba2c8d | ||
|
|
4d28afc153 | ||
|
|
7246f42a8e | ||
|
|
bdcffc7ba9 | ||
|
|
95a6715b2b | ||
|
|
5342edf04a | ||
|
|
d344b1ca0e | ||
|
|
278863d027 | ||
|
|
8503e08ee6 | ||
|
|
aec02afcdc | ||
|
|
52dd79691b | ||
|
|
aea2491fa4 | ||
|
|
963b28181f | ||
|
|
210a9a4162 | ||
|
|
a27a884191 | ||
|
|
17dcba8f8a | ||
|
|
ea6a7a22ff | ||
|
|
5ddba719c5 | ||
|
|
b398d826c1 | ||
|
|
edb557f79e | ||
|
|
f463cd98f8 | ||
|
|
262d69308d | ||
|
|
0406e27100 | ||
|
|
ed3ad615d8 | ||
|
|
66761ff340 | ||
|
|
8bebf138ee | ||
|
|
fd836e982e | ||
|
|
e32722db70 | ||
|
|
b20760c93c | ||
|
|
654e31124e | ||
|
|
8e36e1b92e | ||
|
|
9fe7b08874 | ||
|
|
f1364d4af4 | ||
|
|
ed593544d8 | ||
|
|
0929a0f8aa | ||
|
|
13b3412b45 | ||
|
|
888e315553 | ||
|
|
11daabc9c2 | ||
|
|
40e0100c1e | ||
|
|
c51352d04d | ||
|
|
c8a8acd46e | ||
|
|
bbac1534a3 | ||
|
|
637b55bfbf | ||
|
|
92a24d52be | ||
|
|
491f8cc611 | ||
|
|
71fc61117b | ||
|
|
24f445dade | ||
|
|
7c884329eb | ||
|
|
bac58bba4d | ||
|
|
250bf3f054 | ||
|
|
e65a7d887f | ||
|
|
ac0d921413 | ||
|
|
1e8e471dec | ||
|
|
2d7f8b3bdf | ||
|
|
7452ef23b1 | ||
|
|
9ebe075f9b | ||
|
|
3052c64dd7 | ||
|
|
5e345783bd | ||
|
|
81685573e1 | ||
|
|
945ed5d3bd | ||
|
|
fff5ba03c2 | ||
|
|
82eca13d7b | ||
|
|
5f21b925da | ||
|
|
272ceadbb0 | ||
|
|
a990898256 | ||
|
|
c60c618204 | ||
|
|
53bd197c44 | ||
|
|
dbb195691b | ||
|
|
50da630811 | ||
|
|
30eca885c9 | ||
|
|
f76685fccf | ||
|
|
68d547595e | ||
|
|
64341d1d18 | ||
|
|
2e49039c01 | ||
|
|
8f3a739da7 | ||
|
|
aed140d802 | ||
|
|
c69b88bb55 | ||
|
|
c6dc8a11e2 | ||
|
|
6366ff6421 | ||
|
|
607ddaa632 | ||
|
|
d281e59f3a | ||
|
|
2db8c42e1d | ||
|
|
0ea77de98c | ||
|
|
19014331d8 | ||
|
|
b276ac0588 | ||
|
|
de33cbd7e7 | ||
|
|
103ba4c696 | ||
|
|
5a90b83f63 | ||
|
|
716039e452 | ||
|
|
4cc2817fcd | ||
|
|
d437cc915c | ||
|
|
dd3f2f6c7e | ||
|
|
5f61897bec | ||
|
|
c5d26a5b4a | ||
|
|
855112dfc3 | ||
|
|
b9767bdcbc | ||
|
|
e6b0a0ca2b | ||
|
|
924df1e7de | ||
|
|
ed7983af41 | ||
|
|
40c474cd83 | ||
|
|
a2d2863c72 | ||
|
|
133a17d6eb | ||
|
|
fe47ddc27a | ||
|
|
aad03f1bf5 | ||
|
|
a4867a00ea | ||
|
|
e0cff214b2 | ||
|
|
c6109024aa | ||
|
|
4e308f551c | ||
|
|
8a2b1d9359 | ||
|
|
63a186bdf9 | ||
|
|
d594a6fcbc | ||
|
|
e18dfdd656 | ||
|
|
3aa107142b | ||
|
|
0cd24c629a | ||
|
|
f31e0532c4 | ||
|
|
f0b6aabc96 | ||
|
|
97a18717e6 | ||
|
|
ede1de9021 | ||
|
|
3d71e2e189 | ||
|
|
affaaf7d2c | ||
|
|
bad161a5c1 |
50
.clang-tidy
50
.clang-tidy
@@ -2,12 +2,11 @@
|
|||||||
Checks: >-
|
Checks: >-
|
||||||
*,
|
*,
|
||||||
-abseil-*,
|
-abseil-*,
|
||||||
|
-altera-*,
|
||||||
-android-*,
|
-android-*,
|
||||||
-boost-*,
|
-boost-*,
|
||||||
-bugprone-branch-clone,
|
|
||||||
-bugprone-narrowing-conversions,
|
-bugprone-narrowing-conversions,
|
||||||
-bugprone-signed-char-misuse,
|
-bugprone-signed-char-misuse,
|
||||||
-bugprone-too-small-loop-variable,
|
|
||||||
-cert-dcl50-cpp,
|
-cert-dcl50-cpp,
|
||||||
-cert-err58-cpp,
|
-cert-err58-cpp,
|
||||||
-cert-oop57-cpp,
|
-cert-oop57-cpp,
|
||||||
@@ -17,17 +16,15 @@ Checks: >-
|
|||||||
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
||||||
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
||||||
-clang-diagnostic-shadow-field,
|
-clang-diagnostic-shadow-field,
|
||||||
-clang-diagnostic-sign-compare,
|
|
||||||
-clang-diagnostic-unused-variable,
|
|
||||||
-clang-diagnostic-unused-const-variable,
|
-clang-diagnostic-unused-const-variable,
|
||||||
|
-clang-diagnostic-unused-parameter,
|
||||||
|
-concurrency-*,
|
||||||
-cppcoreguidelines-avoid-c-arrays,
|
-cppcoreguidelines-avoid-c-arrays,
|
||||||
-cppcoreguidelines-avoid-goto,
|
|
||||||
-cppcoreguidelines-avoid-magic-numbers,
|
-cppcoreguidelines-avoid-magic-numbers,
|
||||||
-cppcoreguidelines-init-variables,
|
-cppcoreguidelines-init-variables,
|
||||||
-cppcoreguidelines-macro-usage,
|
-cppcoreguidelines-macro-usage,
|
||||||
-cppcoreguidelines-narrowing-conversions,
|
-cppcoreguidelines-narrowing-conversions,
|
||||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||||
-cppcoreguidelines-owning-memory,
|
|
||||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||||
@@ -39,7 +36,6 @@ Checks: >-
|
|||||||
-cppcoreguidelines-pro-type-union-access,
|
-cppcoreguidelines-pro-type-union-access,
|
||||||
-cppcoreguidelines-pro-type-vararg,
|
-cppcoreguidelines-pro-type-vararg,
|
||||||
-cppcoreguidelines-special-member-functions,
|
-cppcoreguidelines-special-member-functions,
|
||||||
-fuchsia-default-arguments,
|
|
||||||
-fuchsia-multiple-inheritance,
|
-fuchsia-multiple-inheritance,
|
||||||
-fuchsia-overloaded-operator,
|
-fuchsia-overloaded-operator,
|
||||||
-fuchsia-statically-constructed-objects,
|
-fuchsia-statically-constructed-objects,
|
||||||
@@ -49,6 +45,7 @@ Checks: >-
|
|||||||
-google-explicit-constructor,
|
-google-explicit-constructor,
|
||||||
-google-readability-braces-around-statements,
|
-google-readability-braces-around-statements,
|
||||||
-google-readability-casting,
|
-google-readability-casting,
|
||||||
|
-google-readability-namespace-comments,
|
||||||
-google-readability-todo,
|
-google-readability-todo,
|
||||||
-google-runtime-references,
|
-google-runtime-references,
|
||||||
-hicpp-*,
|
-hicpp-*,
|
||||||
@@ -61,29 +58,26 @@ Checks: >-
|
|||||||
-misc-no-recursion,
|
-misc-no-recursion,
|
||||||
-misc-unused-parameters,
|
-misc-unused-parameters,
|
||||||
-modernize-avoid-c-arrays,
|
-modernize-avoid-c-arrays,
|
||||||
|
-modernize-avoid-bind,
|
||||||
|
-modernize-concat-nested-namespaces,
|
||||||
-modernize-return-braced-init-list,
|
-modernize-return-braced-init-list,
|
||||||
-modernize-use-auto,
|
-modernize-use-auto,
|
||||||
-modernize-use-default-member-init,
|
-modernize-use-default-member-init,
|
||||||
-modernize-use-equals-default,
|
-modernize-use-equals-default,
|
||||||
-modernize-use-trailing-return-type,
|
-modernize-use-trailing-return-type,
|
||||||
|
-modernize-use-nodiscard,
|
||||||
-mpi-*,
|
-mpi-*,
|
||||||
-objc-*,
|
-objc-*,
|
||||||
-readability-braces-around-statements,
|
|
||||||
-readability-const-return-type,
|
|
||||||
-readability-convert-member-functions-to-static,
|
-readability-convert-member-functions-to-static,
|
||||||
-readability-else-after-return,
|
-readability-else-after-return,
|
||||||
|
-readability-function-cognitive-complexity,
|
||||||
-readability-implicit-bool-conversion,
|
-readability-implicit-bool-conversion,
|
||||||
-readability-isolate-declaration,
|
-readability-isolate-declaration,
|
||||||
-readability-magic-numbers,
|
-readability-magic-numbers,
|
||||||
-readability-make-member-function-const,
|
-readability-make-member-function-const,
|
||||||
-readability-named-parameter,
|
|
||||||
-readability-qualified-auto,
|
|
||||||
-readability-redundant-access-specifiers,
|
|
||||||
-readability-redundant-member-init,
|
|
||||||
-readability-redundant-string-init,
|
-readability-redundant-string-init,
|
||||||
-readability-uppercase-literal-suffix,
|
-readability-uppercase-literal-suffix,
|
||||||
-readability-use-anyofallof,
|
-readability-use-anyofallof,
|
||||||
-warnings-as-errors
|
|
||||||
WarningsAsErrors: '*'
|
WarningsAsErrors: '*'
|
||||||
AnalyzeTemporaryDtors: false
|
AnalyzeTemporaryDtors: false
|
||||||
FormatStyle: google
|
FormatStyle: google
|
||||||
@@ -92,9 +86,11 @@ CheckOptions:
|
|||||||
value: '1'
|
value: '1'
|
||||||
- key: google-readability-function-size.StatementThreshold
|
- key: google-readability-function-size.StatementThreshold
|
||||||
value: '800'
|
value: '800'
|
||||||
- key: google-readability-namespace-comments.ShortNamespaceLines
|
- key: google-runtime-int.TypeSuffix
|
||||||
|
value: '_t'
|
||||||
|
- key: llvm-namespace-comment.ShortNamespaceLines
|
||||||
value: '10'
|
value: '10'
|
||||||
- key: google-readability-namespace-comments.SpacesBeforeComments
|
- key: llvm-namespace-comment.SpacesBeforeComments
|
||||||
value: '2'
|
value: '2'
|
||||||
- key: modernize-loop-convert.MaxCopySize
|
- key: modernize-loop-convert.MaxCopySize
|
||||||
value: '16'
|
value: '16'
|
||||||
@@ -108,6 +104,12 @@ CheckOptions:
|
|||||||
value: llvm
|
value: llvm
|
||||||
- key: modernize-use-nullptr.NullMacros
|
- key: modernize-use-nullptr.NullMacros
|
||||||
value: 'NULL'
|
value: 'NULL'
|
||||||
|
- key: modernize-make-unique.MakeSmartPtrFunction
|
||||||
|
value: 'make_unique'
|
||||||
|
- key: modernize-make-unique.MakeSmartPtrFunctionHeader
|
||||||
|
value: 'esphome/core/helpers.h'
|
||||||
|
- key: readability-braces-around-statements.ShortStatementLines
|
||||||
|
value: 2
|
||||||
- key: readability-identifier-naming.LocalVariableCase
|
- key: readability-identifier-naming.LocalVariableCase
|
||||||
value: 'lower_case'
|
value: 'lower_case'
|
||||||
- key: readability-identifier-naming.ClassCase
|
- key: readability-identifier-naming.ClassCase
|
||||||
@@ -121,15 +123,19 @@ CheckOptions:
|
|||||||
- key: readability-identifier-naming.StaticConstantCase
|
- key: readability-identifier-naming.StaticConstantCase
|
||||||
value: 'UPPER_CASE'
|
value: 'UPPER_CASE'
|
||||||
- key: readability-identifier-naming.StaticVariableCase
|
- key: readability-identifier-naming.StaticVariableCase
|
||||||
value: 'UPPER_CASE'
|
value: 'lower_case'
|
||||||
- key: readability-identifier-naming.GlobalConstantCase
|
- key: readability-identifier-naming.GlobalConstantCase
|
||||||
value: 'UPPER_CASE'
|
value: 'UPPER_CASE'
|
||||||
- key: readability-identifier-naming.ParameterCase
|
- key: readability-identifier-naming.ParameterCase
|
||||||
value: 'lower_case'
|
value: 'lower_case'
|
||||||
- key: readability-identifier-naming.PrivateMemberPrefix
|
- key: readability-identifier-naming.PrivateMemberCase
|
||||||
value: 'NO_PRIVATE_MEMBERS_ALWAYS_USE_PROTECTED'
|
value: 'lower_case'
|
||||||
- key: readability-identifier-naming.PrivateMethodPrefix
|
- key: readability-identifier-naming.PrivateMemberSuffix
|
||||||
value: 'NO_PRIVATE_METHODS_ALWAYS_USE_PROTECTED'
|
value: '_'
|
||||||
|
- key: readability-identifier-naming.PrivateMethodCase
|
||||||
|
value: 'lower_case'
|
||||||
|
- key: readability-identifier-naming.PrivateMethodSuffix
|
||||||
|
value: '_'
|
||||||
- key: readability-identifier-naming.ClassMemberCase
|
- key: readability-identifier-naming.ClassMemberCase
|
||||||
value: 'lower_case'
|
value: 'lower_case'
|
||||||
- key: readability-identifier-naming.ClassMemberCase
|
- key: readability-identifier-naming.ClassMemberCase
|
||||||
@@ -150,3 +156,5 @@ CheckOptions:
|
|||||||
value: 'lower_case'
|
value: 'lower_case'
|
||||||
- key: readability-identifier-naming.VirtualMethodSuffix
|
- key: readability-identifier-naming.VirtualMethodSuffix
|
||||||
value: ''
|
value: ''
|
||||||
|
- key: readability-qualified-auto.AddConstToQualified
|
||||||
|
value: 0
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ESPHome Dev",
|
"name": "ESPHome Dev",
|
||||||
"context": "..",
|
"image": "esphome/esphome-lint:dev",
|
||||||
"dockerFile": "../docker/Dockerfile.dev",
|
|
||||||
"postCreateCommand": [
|
"postCreateCommand": [
|
||||||
"script/devcontainer-post-create"
|
"script/devcontainer-post-create"
|
||||||
],
|
],
|
||||||
|
|||||||
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Normalize line endings to LF in the repository
|
||||||
|
* text eol=lf
|
||||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
1
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -9,4 +9,3 @@ contact_links:
|
|||||||
- name: Frequently Asked Question
|
- name: Frequently Asked Question
|
||||||
url: https://esphome.io/guides/faq.html
|
url: https://esphome.io/guides/faq.html
|
||||||
about: Please view the FAQ for common questions and what to include in a bug report.
|
about: Please view the FAQ for common questions and what to include in a bug report.
|
||||||
|
|
||||||
|
|||||||
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -16,6 +16,7 @@ Quick description and explanation of changes
|
|||||||
## Test Environment
|
## Test Environment
|
||||||
|
|
||||||
- [ ] ESP32
|
- [ ] ESP32
|
||||||
|
- [ ] ESP32 IDF
|
||||||
- [ ] ESP8266
|
- [ ] ESP8266
|
||||||
|
|
||||||
## Example entry for `config.yaml`:
|
## Example entry for `config.yaml`:
|
||||||
|
|||||||
5
.github/dependabot.yml
vendored
5
.github/dependabot.yml
vendored
@@ -7,3 +7,8 @@ updates:
|
|||||||
ignore:
|
ignore:
|
||||||
# Hypotehsis is only used for testing and is updated quite often
|
# Hypotehsis is only used for testing and is updated quite often
|
||||||
- dependency-name: hypothesis
|
- dependency-name: hypothesis
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
|||||||
7
.github/issue-close-app.yml
vendored
7
.github/issue-close-app.yml
vendored
@@ -1,7 +0,0 @@
|
|||||||
comment: >-
|
|
||||||
https://github.com/esphome/esphome/issues/430
|
|
||||||
issueConfigs:
|
|
||||||
- content:
|
|
||||||
- "OTHERWISE THE ISSUE WILL BE CLOSED AUTOMATICALLY"
|
|
||||||
|
|
||||||
caseInsensitive: false
|
|
||||||
36
.github/lock.yml
vendored
36
.github/lock.yml
vendored
@@ -1,36 +0,0 @@
|
|||||||
# Configuration for Lock Threads - https://github.com/dessant/lock-threads
|
|
||||||
|
|
||||||
# Number of days of inactivity before a closed issue or pull request is locked
|
|
||||||
daysUntilLock: 7
|
|
||||||
|
|
||||||
# Skip issues and pull requests created before a given timestamp. Timestamp must
|
|
||||||
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
|
|
||||||
skipCreatedBefore: false
|
|
||||||
|
|
||||||
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
|
|
||||||
exemptLabels:
|
|
||||||
- keep-open
|
|
||||||
|
|
||||||
# Label to add before locking, such as `outdated`. Set to `false` to disable
|
|
||||||
lockLabel: false
|
|
||||||
|
|
||||||
# Comment to post before locking. Set to `false` to disable
|
|
||||||
lockComment: false
|
|
||||||
|
|
||||||
# Assign `resolved` as the reason for locking. Set to `false` to disable
|
|
||||||
setLockReason: false
|
|
||||||
|
|
||||||
# Limit to only `issues` or `pulls`
|
|
||||||
# only: issues
|
|
||||||
|
|
||||||
# Optionally, specify configuration settings just for `issues` or `pulls`
|
|
||||||
# issues:
|
|
||||||
# exemptLabels:
|
|
||||||
# - help-wanted
|
|
||||||
# lockLabel: outdated
|
|
||||||
|
|
||||||
# pulls:
|
|
||||||
# daysUntilLock: 30
|
|
||||||
|
|
||||||
# Repository to extend settings from
|
|
||||||
# _extends: repo
|
|
||||||
59
.github/stale.yml
vendored
59
.github/stale.yml
vendored
@@ -1,59 +0,0 @@
|
|||||||
# Configuration for probot-stale - https://github.com/probot/stale
|
|
||||||
|
|
||||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
|
||||||
daysUntilStale: 60
|
|
||||||
|
|
||||||
# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.
|
|
||||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
|
||||||
daysUntilClose: 7
|
|
||||||
|
|
||||||
# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)
|
|
||||||
onlyLabels: []
|
|
||||||
|
|
||||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
|
||||||
exemptLabels:
|
|
||||||
- not-stale
|
|
||||||
|
|
||||||
# Set to true to ignore issues in a project (defaults to false)
|
|
||||||
exemptProjects: false
|
|
||||||
|
|
||||||
# Set to true to ignore issues in a milestone (defaults to false)
|
|
||||||
exemptMilestones: true
|
|
||||||
|
|
||||||
# Set to true to ignore issues with an assignee (defaults to false)
|
|
||||||
exemptAssignees: false
|
|
||||||
|
|
||||||
# Label to use when marking as stale
|
|
||||||
staleLabel: stale
|
|
||||||
|
|
||||||
# Comment to post when marking as stale. Set to `false` to disable
|
|
||||||
markComment: >
|
|
||||||
This issue has been automatically marked as stale because it has not had
|
|
||||||
recent activity. It will be closed if no further activity occurs. Thank you
|
|
||||||
for your contributions.
|
|
||||||
|
|
||||||
# Comment to post when removing the stale label.
|
|
||||||
# unmarkComment: >
|
|
||||||
# Your comment here.
|
|
||||||
|
|
||||||
# Comment to post when closing a stale Issue or Pull Request.
|
|
||||||
# closeComment: >
|
|
||||||
# Your comment here.
|
|
||||||
|
|
||||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
|
||||||
limitPerRun: 10
|
|
||||||
|
|
||||||
# Limit to only `issues` or `pulls`
|
|
||||||
only: pulls
|
|
||||||
|
|
||||||
# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':
|
|
||||||
# pulls:
|
|
||||||
# daysUntilStale: 30
|
|
||||||
# markComment: >
|
|
||||||
# This pull request has been automatically marked as stale because it has not had
|
|
||||||
# recent activity. It will be closed if no further activity occurs. Thank you
|
|
||||||
# for your contributions.
|
|
||||||
|
|
||||||
# issues:
|
|
||||||
# exemptLabels:
|
|
||||||
# - confirmed
|
|
||||||
17
.github/workflows/ci-docker.yml
vendored
17
.github/workflows/ci-docker.yml
vendored
@@ -7,11 +7,19 @@ on:
|
|||||||
paths:
|
paths:
|
||||||
- 'docker/**'
|
- 'docker/**'
|
||||||
- '.github/workflows/**'
|
- '.github/workflows/**'
|
||||||
|
- 'requirements*.txt'
|
||||||
|
- 'platformio.ini'
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'docker/**'
|
- 'docker/**'
|
||||||
- '.github/workflows/**'
|
- '.github/workflows/**'
|
||||||
|
- 'requirements*.txt'
|
||||||
|
- 'platformio.ini'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-docker:
|
check-docker:
|
||||||
@@ -22,11 +30,16 @@ jobs:
|
|||||||
arch: [amd64, armv7, aarch64]
|
arch: [amd64, armv7, aarch64]
|
||||||
build_type: ["ha-addon", "docker", "lint"]
|
build_type: ["ha-addon", "docker", "lint"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v2
|
||||||
|
|
||||||
- name: Set TAG
|
- name: Set TAG
|
||||||
run: |
|
run: |
|
||||||
echo "TAG=check" >> $GITHUB_ENV
|
echo "TAG=check" >> $GITHUB_ENV
|
||||||
|
|||||||
173
.github/workflows/ci.yml
vendored
173
.github/workflows/ci.yml
vendored
@@ -1,5 +1,3 @@
|
|||||||
# THESE JOBS ARE COPIED IN release.yml and release-dev.yml
|
|
||||||
# PLEASE ALSO UPDATE THOSE FILES WHEN CHANGING LINES HERE
|
|
||||||
name: CI
|
name: CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -8,59 +6,15 @@ on:
|
|||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ci-with-container:
|
|
||||||
name: ${{ matrix.name }}
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- id: clang-format
|
|
||||||
name: Run script/clang-format
|
|
||||||
- id: clang-tidy
|
|
||||||
name: Run script/clang-tidy 1/4
|
|
||||||
split: 1
|
|
||||||
- id: clang-tidy
|
|
||||||
name: Run script/clang-tidy 2/4
|
|
||||||
split: 2
|
|
||||||
- id: clang-tidy
|
|
||||||
name: Run script/clang-tidy 3/4
|
|
||||||
split: 3
|
|
||||||
- id: clang-tidy
|
|
||||||
name: Run script/clang-tidy 4/4
|
|
||||||
split: 4
|
|
||||||
|
|
||||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
|
||||||
# doesn't have to be installed
|
|
||||||
container: ghcr.io/esphome/esphome-lint:1.1
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Register problem matchers
|
|
||||||
run: |
|
|
||||||
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
|
||||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
|
||||||
|
|
||||||
# Also run git-diff-index so that the step is marked as failed on formatting errors,
|
|
||||||
# since clang-format doesn't do anything but change files if -i is passed.
|
|
||||||
- name: Run clang-format
|
|
||||||
run: |
|
|
||||||
script/clang-format -i
|
|
||||||
git diff-index --quiet HEAD --
|
|
||||||
if: ${{ matrix.id == 'clang-format' }}
|
|
||||||
|
|
||||||
- name: Run clang-tidy
|
|
||||||
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
|
|
||||||
if: ${{ matrix.id == 'clang-tidy' }}
|
|
||||||
|
|
||||||
- name: Suggested changes
|
|
||||||
run: script/ci-suggest-changes
|
|
||||||
if: always()
|
|
||||||
|
|
||||||
ci:
|
ci:
|
||||||
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
|
||||||
# This way, all dependencies are cached via the cache action.
|
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
@@ -74,48 +28,92 @@ jobs:
|
|||||||
- id: test
|
- id: test
|
||||||
file: tests/test1.yaml
|
file: tests/test1.yaml
|
||||||
name: Test tests/test1.yaml
|
name: Test tests/test1.yaml
|
||||||
|
pio_cache_key: test1
|
||||||
- id: test
|
- id: test
|
||||||
file: tests/test2.yaml
|
file: tests/test2.yaml
|
||||||
name: Test tests/test2.yaml
|
name: Test tests/test2.yaml
|
||||||
|
pio_cache_key: test2
|
||||||
- id: test
|
- id: test
|
||||||
file: tests/test3.yaml
|
file: tests/test3.yaml
|
||||||
name: Test tests/test3.yaml
|
name: Test tests/test3.yaml
|
||||||
|
pio_cache_key: test3
|
||||||
- id: test
|
- id: test
|
||||||
file: tests/test4.yaml
|
file: tests/test4.yaml
|
||||||
name: Test tests/test4.yaml
|
name: Test tests/test4.yaml
|
||||||
|
pio_cache_key: test4
|
||||||
- id: test
|
- id: test
|
||||||
file: tests/test5.yaml
|
file: tests/test5.yaml
|
||||||
name: Test tests/test5.yaml
|
name: Test tests/test5.yaml
|
||||||
|
pio_cache_key: test5
|
||||||
- id: pytest
|
- id: pytest
|
||||||
name: Run pytest
|
name: Run pytest
|
||||||
|
- id: clang-format
|
||||||
|
name: Run script/clang-format
|
||||||
|
- id: clang-tidy
|
||||||
|
name: Run script/clang-tidy for ESP8266
|
||||||
|
options: --environment esp8266-arduino-tidy --grep USE_ESP8266
|
||||||
|
pio_cache_key: tidyesp8266
|
||||||
|
- id: clang-tidy
|
||||||
|
name: Run script/clang-tidy for ESP32 Arduino 1/4
|
||||||
|
options: --environment esp32-arduino-tidy --split-num 4 --split-at 1
|
||||||
|
pio_cache_key: tidyesp32
|
||||||
|
- id: clang-tidy
|
||||||
|
name: Run script/clang-tidy for ESP32 Arduino 2/4
|
||||||
|
options: --environment esp32-arduino-tidy --split-num 4 --split-at 2
|
||||||
|
pio_cache_key: tidyesp32
|
||||||
|
- id: clang-tidy
|
||||||
|
name: Run script/clang-tidy for ESP32 Arduino 3/4
|
||||||
|
options: --environment esp32-arduino-tidy --split-num 4 --split-at 3
|
||||||
|
pio_cache_key: tidyesp32
|
||||||
|
- id: clang-tidy
|
||||||
|
name: Run script/clang-tidy for ESP32 Arduino 4/4
|
||||||
|
options: --environment esp32-arduino-tidy --split-num 4 --split-at 4
|
||||||
|
pio_cache_key: tidyesp32
|
||||||
|
- id: clang-tidy
|
||||||
|
name: Run script/clang-tidy for ESP32 IDF
|
||||||
|
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
|
||||||
|
pio_cache_key: tidyesp32-idf
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v4
|
||||||
|
id: python
|
||||||
with:
|
with:
|
||||||
python-version: '3.7'
|
python-version: '3.8'
|
||||||
|
|
||||||
- name: Cache pip modules
|
- name: Cache virtualenv
|
||||||
uses: actions/cache@v1
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ~/.cache/pip
|
path: .venv
|
||||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
key: venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements*.txt') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
esphome-pip-3.7-
|
venv-${{ steps.python.outputs.python-version }}-
|
||||||
|
|
||||||
# Use per test platformio cache because tests have different platform versions
|
- name: Set up virtualenv
|
||||||
- name: Cache ~/.platformio
|
run: |
|
||||||
uses: actions/cache@v1
|
python -m venv .venv
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install -U pip
|
||||||
|
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
||||||
|
pip install -e .
|
||||||
|
echo "$GITHUB_WORKSPACE/.venv/bin" >> $GITHUB_PATH
|
||||||
|
echo "VIRTUAL_ENV=$GITHUB_WORKSPACE/.venv" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
# Use per check platformio cache because checks use different parts
|
||||||
|
- name: Cache platformio
|
||||||
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: test-home-platformio-${{ matrix.file }}-${{ hashFiles('esphome/core/config.py') }}
|
key: platformio-${{ matrix.pio_cache_key }}-${{ hashFiles('platformio.ini') }}
|
||||||
restore-keys: |
|
if: matrix.id == 'test' || matrix.id == 'clang-tidy'
|
||||||
test-home-platformio-${{ matrix.file }}-
|
|
||||||
if: ${{ matrix.id == 'test' }}
|
|
||||||
|
|
||||||
- name: Set up python environment
|
- name: Install clang tools
|
||||||
run: script/setup
|
run: |
|
||||||
|
sudo apt-get install \
|
||||||
|
clang-format-11 \
|
||||||
|
clang-tidy-11
|
||||||
|
if: matrix.id == 'clang-tidy' || matrix.id == 'clang-format'
|
||||||
|
|
||||||
- name: Register problem matchers
|
- name: Register problem matchers
|
||||||
run: |
|
run: |
|
||||||
@@ -124,20 +122,45 @@ jobs:
|
|||||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||||
echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
||||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
||||||
|
|
||||||
- name: Lint Custom
|
- name: Lint Custom
|
||||||
run: |
|
run: |
|
||||||
script/ci-custom.py
|
script/ci-custom.py
|
||||||
script/build_codeowners.py --check
|
script/build_codeowners.py --check
|
||||||
if: ${{ matrix.id == 'ci-custom' }}
|
if: matrix.id == 'ci-custom'
|
||||||
|
|
||||||
- name: Lint Python
|
- name: Lint Python
|
||||||
run: script/lint-python
|
run: script/lint-python -a
|
||||||
if: ${{ matrix.id == 'lint-python' }}
|
if: matrix.id == 'lint-python'
|
||||||
|
|
||||||
- run: esphome compile ${{ matrix.file }}
|
- run: esphome compile ${{ matrix.file }}
|
||||||
if: ${{ matrix.id == 'test' }}
|
if: matrix.id == 'test'
|
||||||
|
env:
|
||||||
|
# Also cache libdeps, store them in a ~/.platformio subfolder
|
||||||
|
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
|
||||||
|
|
||||||
- name: Run pytest
|
- name: Run pytest
|
||||||
run: |
|
run: |
|
||||||
pytest -vv --tb=native tests
|
pytest -vv --tb=native tests
|
||||||
if: ${{ matrix.id == 'pytest' }}
|
if: matrix.id == 'pytest'
|
||||||
|
|
||||||
|
# Also run git-diff-index so that the step is marked as failed on formatting errors,
|
||||||
|
# since clang-format doesn't do anything but change files if -i is passed.
|
||||||
|
- name: Run clang-format
|
||||||
|
run: |
|
||||||
|
script/clang-format -i
|
||||||
|
git diff-index --quiet HEAD --
|
||||||
|
if: matrix.id == 'clang-format'
|
||||||
|
|
||||||
|
- name: Run clang-tidy
|
||||||
|
run: |
|
||||||
|
script/clang-tidy --all-headers --fix ${{ matrix.options }}
|
||||||
|
if: matrix.id == 'clang-tidy'
|
||||||
|
env:
|
||||||
|
# Also cache libdeps, store them in a ~/.platformio subfolder
|
||||||
|
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
|
||||||
|
|
||||||
|
- name: Suggested changes
|
||||||
|
run: script/ci-suggest-changes
|
||||||
|
if: always() && (matrix.id == 'clang-tidy' || matrix.id == 'clang-format' || matrix.id == 'lint-python')
|
||||||
|
|||||||
100
.github/workflows/docker-lint-build.yml
vendored
100
.github/workflows/docker-lint-build.yml
vendored
@@ -1,100 +0,0 @@
|
|||||||
name: Build and publish lint docker image
|
|
||||||
|
|
||||||
# Only run when docker paths change
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [dev]
|
|
||||||
paths:
|
|
||||||
- 'docker/Dockerfile.lint'
|
|
||||||
- 'requirements.txt'
|
|
||||||
- 'requirements_optional.txt'
|
|
||||||
- 'requirements_test.txt'
|
|
||||||
- 'platformio.ini'
|
|
||||||
- '.github/workflows/docker-lint-build.yml'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy-docker:
|
|
||||||
name: Build and publish docker containers
|
|
||||||
if: github.repository == 'esphome/esphome'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
arch: [amd64, armv7, aarch64]
|
|
||||||
build_type: ["lint"]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: '3.9'
|
|
||||||
- name: Set TAG
|
|
||||||
run: |
|
|
||||||
echo "TAG=1.1" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Run build
|
|
||||||
run: |
|
|
||||||
docker/build.py \
|
|
||||||
--tag "${TAG}" \
|
|
||||||
--arch "${{ matrix.arch }}" \
|
|
||||||
--build-type "${{ matrix.build_type }}" \
|
|
||||||
build
|
|
||||||
|
|
||||||
- name: Log in to docker hub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
- name: Log in to the GitHub container registry
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Run push
|
|
||||||
run: |
|
|
||||||
docker/build.py \
|
|
||||||
--tag "${TAG}" \
|
|
||||||
--arch "${{ matrix.arch }}" \
|
|
||||||
--build-type "${{ matrix.build_type }}" \
|
|
||||||
push
|
|
||||||
|
|
||||||
deploy-docker-manifest:
|
|
||||||
if: github.repository == 'esphome/esphome'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [deploy-docker]
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
build_type: ["lint"]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Set up Python
|
|
||||||
uses: actions/setup-python@v2
|
|
||||||
with:
|
|
||||||
python-version: '3.9'
|
|
||||||
- name: Set TAG
|
|
||||||
run: |
|
|
||||||
echo "TAG=1.1" >> $GITHUB_ENV
|
|
||||||
- name: Enable experimental manifest support
|
|
||||||
run: |
|
|
||||||
mkdir -p ~/.docker
|
|
||||||
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
|
||||||
|
|
||||||
- name: Log in to docker hub
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
|
||||||
- name: Log in to the GitHub container registry
|
|
||||||
uses: docker/login-action@v1
|
|
||||||
with:
|
|
||||||
registry: ghcr.io
|
|
||||||
username: ${{ github.actor }}
|
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Run manifest
|
|
||||||
run: |
|
|
||||||
docker/build.py \
|
|
||||||
--tag "${TAG}" \
|
|
||||||
--build-type "${{ matrix.build_type }}" \
|
|
||||||
manifest
|
|
||||||
27
.github/workflows/lock.yml
vendored
Normal file
27
.github/workflows/lock.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: Lock
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '30 0 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: lock
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lock:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: dessant/lock-threads@v3
|
||||||
|
with:
|
||||||
|
pr-inactive-days: "1"
|
||||||
|
pr-lock-reason: ""
|
||||||
|
exclude-any-pr-labels: keep-open
|
||||||
|
|
||||||
|
issue-inactive-days: "7"
|
||||||
|
issue-lock-reason: ""
|
||||||
|
exclude-any-issue-labels: keep-open
|
||||||
2
.github/workflows/matchers/ci-custom.json
vendored
2
.github/workflows/matchers/ci-custom.json
vendored
@@ -4,7 +4,7 @@
|
|||||||
"owner": "ci-custom",
|
"owner": "ci-custom",
|
||||||
"pattern": [
|
"pattern": [
|
||||||
{
|
{
|
||||||
"regexp": "^ERROR (.*):(\\d+):(\\d+) - (.*)$",
|
"regexp": "^(.*):(\\d+):(\\d+):\\s+lint:\\s+(.*)$",
|
||||||
"file": 1,
|
"file": 1,
|
||||||
"line": 2,
|
"line": 2,
|
||||||
"column": 3,
|
"column": 3,
|
||||||
|
|||||||
2
.github/workflows/matchers/gcc.json
vendored
2
.github/workflows/matchers/gcc.json
vendored
@@ -5,7 +5,7 @@
|
|||||||
"severity": "error",
|
"severity": "error",
|
||||||
"pattern": [
|
"pattern": [
|
||||||
{
|
{
|
||||||
"regexp": "^(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
|
"regexp": "^src/(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
|
||||||
"file": 1,
|
"file": 1,
|
||||||
"line": 2,
|
"line": 2,
|
||||||
"column": 3,
|
"column": 3,
|
||||||
|
|||||||
15
.github/workflows/matchers/lint-python.json
vendored
15
.github/workflows/matchers/lint-python.json
vendored
@@ -1,11 +1,22 @@
|
|||||||
{
|
{
|
||||||
"problemMatcher": [
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "black",
|
||||||
|
"severity": "error",
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"regexp": "^(.*): (Please format this file with the black formatter)",
|
||||||
|
"file": 1,
|
||||||
|
"message": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"owner": "flake8",
|
"owner": "flake8",
|
||||||
"severity": "error",
|
"severity": "error",
|
||||||
"pattern": [
|
"pattern": [
|
||||||
{
|
{
|
||||||
"regexp": "^(.*):(\\d+) - ([EFCDNW]\\d{3}.*)$",
|
"regexp": "^(.*):(\\d+): ([EFCDNW]\\d{3}.*)$",
|
||||||
"file": 1,
|
"file": 1,
|
||||||
"line": 2,
|
"line": 2,
|
||||||
"message": 3
|
"message": 3
|
||||||
@@ -17,7 +28,7 @@
|
|||||||
"severity": "error",
|
"severity": "error",
|
||||||
"pattern": [
|
"pattern": [
|
||||||
{
|
{
|
||||||
"regexp": "^(.*):(\\d+) - (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$",
|
"regexp": "^(.*):(\\d+): (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$",
|
||||||
"file": 1,
|
"file": 1,
|
||||||
"line": 2,
|
"line": 2,
|
||||||
"message": 3
|
"message": 3
|
||||||
|
|||||||
57
.github/workflows/release.yml
vendored
57
.github/workflows/release.yml
vendored
@@ -7,6 +7,9 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: "0 2 * * *"
|
- cron: "0 2 * * *"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
init:
|
init:
|
||||||
name: Initialize build
|
name: Initialize build
|
||||||
@@ -14,7 +17,7 @@ jobs:
|
|||||||
outputs:
|
outputs:
|
||||||
tag: ${{ steps.tag.outputs.tag }}
|
tag: ${{ steps.tag.outputs.tag }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Get tag
|
- name: Get tag
|
||||||
id: tag
|
id: tag
|
||||||
run: |
|
run: |
|
||||||
@@ -32,9 +35,9 @@ jobs:
|
|||||||
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v1
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- name: Set up python environment
|
- name: Set up python environment
|
||||||
@@ -52,58 +55,62 @@ jobs:
|
|||||||
deploy-docker:
|
deploy-docker:
|
||||||
name: Build and publish docker containers
|
name: Build and publish docker containers
|
||||||
if: github.repository == 'esphome/esphome'
|
if: github.repository == 'esphome/esphome'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [init]
|
needs: [init]
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
arch: [amd64, armv7, aarch64]
|
arch: [amd64, armv7, aarch64]
|
||||||
build_type: ["ha-addon", "docker"]
|
build_type: ["ha-addon", "docker", "lint"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
|
|
||||||
- name: Run build
|
- name: Set up Docker Buildx
|
||||||
run: |
|
uses: docker/setup-buildx-action@v2
|
||||||
docker/build.py \
|
- name: Set up QEMU
|
||||||
--tag "${{ needs.init.outputs.tag }}" \
|
uses: docker/setup-qemu-action@v2
|
||||||
--arch "${{ matrix.arch }}" \
|
|
||||||
--build-type "${{ matrix.build_type }}" \
|
|
||||||
build
|
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Run push
|
- name: Build and push
|
||||||
run: |
|
run: |
|
||||||
docker/build.py \
|
docker/build.py \
|
||||||
--tag "${{ needs.init.outputs.tag }}" \
|
--tag "${{ needs.init.outputs.tag }}" \
|
||||||
--arch "${{ matrix.arch }}" \
|
--arch "${{ matrix.arch }}" \
|
||||||
--build-type "${{ matrix.build_type }}" \
|
--build-type "${{ matrix.build_type }}" \
|
||||||
push
|
build \
|
||||||
|
--push
|
||||||
|
|
||||||
deploy-docker-manifest:
|
deploy-docker-manifest:
|
||||||
if: github.repository == 'esphome/esphome'
|
if: github.repository == 'esphome/esphome'
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [init, deploy-docker]
|
needs: [init, deploy-docker]
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
build_type: ["ha-addon", "docker"]
|
build_type: ["ha-addon", "docker", "lint"]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v2
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: '3.9'
|
python-version: '3.9'
|
||||||
- name: Enable experimental manifest support
|
- name: Enable experimental manifest support
|
||||||
@@ -112,12 +119,12 @@ jobs:
|
|||||||
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
uses: docker/login-action@v1
|
uses: docker/login-action@v2
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -130,18 +137,18 @@ jobs:
|
|||||||
--build-type "${{ matrix.build_type }}" \
|
--build-type "${{ matrix.build_type }}" \
|
||||||
manifest
|
manifest
|
||||||
|
|
||||||
deploy-hassio-repo:
|
deploy-ha-addon-repo:
|
||||||
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [deploy-docker]
|
needs: [deploy-docker]
|
||||||
steps:
|
steps:
|
||||||
- env:
|
- env:
|
||||||
TOKEN: ${{ secrets.DEPLOY_HASSIO_TOKEN }}
|
TOKEN: ${{ secrets.DEPLOY_HA_ADDON_REPO_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
TAG="${GITHUB_REF#refs/tags/}"
|
TAG="${GITHUB_REF#refs/tags/}"
|
||||||
curl \
|
curl \
|
||||||
-u ":$TOKEN" \
|
-u ":$TOKEN" \
|
||||||
-X POST \
|
-X POST \
|
||||||
-H "Accept: application/vnd.github.v3+json" \
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \
|
https://api.github.com/repos/esphome/home-assistant-addon/actions/workflows/bump-version.yml/dispatches \
|
||||||
-d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"
|
-d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"
|
||||||
|
|||||||
48
.github/workflows/stale.yml
vendored
Normal file
48
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
name: Stale
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '30 0 * * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: lock
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v5
|
||||||
|
with:
|
||||||
|
days-before-pr-stale: 90
|
||||||
|
days-before-pr-close: 7
|
||||||
|
days-before-issue-stale: -1
|
||||||
|
days-before-issue-close: -1
|
||||||
|
remove-stale-when-updated: true
|
||||||
|
stale-pr-label: "stale"
|
||||||
|
exempt-pr-labels: "no-stale"
|
||||||
|
stale-pr-message: >
|
||||||
|
There hasn't been any activity on this pull request recently. This
|
||||||
|
pull request has been automatically marked as stale because of that
|
||||||
|
and will be closed if no further activity occurs within 7 days.
|
||||||
|
Thank you for your contributions.
|
||||||
|
|
||||||
|
# Use stale to automatically close issues with a reference to the issue tracker
|
||||||
|
close-issues:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v5
|
||||||
|
with:
|
||||||
|
days-before-pr-stale: -1
|
||||||
|
days-before-pr-close: -1
|
||||||
|
days-before-issue-stale: 1
|
||||||
|
days-before-issue-close: 1
|
||||||
|
remove-stale-when-updated: true
|
||||||
|
stale-issue-label: "stale"
|
||||||
|
exempt-issue-labels: "not-stale"
|
||||||
|
stale-issue-message: >
|
||||||
|
https://github.com/esphome/esphome/issues/430
|
||||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -77,6 +77,7 @@ venv/
|
|||||||
ENV/
|
ENV/
|
||||||
env.bak/
|
env.bak/
|
||||||
venv.bak/
|
venv.bak/
|
||||||
|
venv-*/
|
||||||
|
|
||||||
# mypy
|
# mypy
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
@@ -102,10 +103,7 @@ CMakeLists.txt
|
|||||||
.idea/**/dynamic.xml
|
.idea/**/dynamic.xml
|
||||||
|
|
||||||
# CMake
|
# CMake
|
||||||
cmake-build-debug/
|
cmake-build-*/
|
||||||
cmake-build-livingroom8266/
|
|
||||||
cmake-build-livingroom32/
|
|
||||||
cmake-build-release/
|
|
||||||
|
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
CMakeFiles
|
CMakeFiles
|
||||||
@@ -127,3 +125,6 @@ tests/.esphome/
|
|||||||
/.temp-clang-tidy.cpp
|
/.temp-clang-tidy.cpp
|
||||||
/.temp/
|
/.temp/
|
||||||
.pio/
|
.pio/
|
||||||
|
|
||||||
|
sdkconfig.*
|
||||||
|
!sdkconfig.defaults
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ ports:
|
|||||||
onOpen: open-preview
|
onOpen: open-preview
|
||||||
tasks:
|
tasks:
|
||||||
- before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup
|
- before: pyenv local $(pyenv version | grep '^3\.' | cut -d ' ' -f 1) && script/setup
|
||||||
command: python -m esphome config dashboard
|
command: python -m esphome dashboard config
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# See https://pre-commit.com/hooks.html for more hooks
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/ambv/black
|
- repo: https://github.com/ambv/black
|
||||||
rev: 20.8b1
|
rev: 22.6.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: black
|
- id: black
|
||||||
args:
|
args:
|
||||||
@@ -10,7 +10,7 @@ repos:
|
|||||||
- --quiet
|
- --quiet
|
||||||
files: ^((esphome|script|tests)/.+)?[^/]+\.py$
|
files: ^((esphome|script|tests)/.+)?[^/]+\.py$
|
||||||
- repo: https://gitlab.com/pycqa/flake8
|
- repo: https://gitlab.com/pycqa/flake8
|
||||||
rev: 3.8.4
|
rev: 4.0.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
@@ -25,3 +25,8 @@ repos:
|
|||||||
- --branch=dev
|
- --branch=dev
|
||||||
- --branch=release
|
- --branch=release
|
||||||
- --branch=beta
|
- --branch=beta
|
||||||
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
|
rev: v2.37.3
|
||||||
|
hooks:
|
||||||
|
- id: pyupgrade
|
||||||
|
args: [--py38-plus]
|
||||||
|
|||||||
88
CODEOWNERS
88
CODEOWNERS
@@ -15,9 +15,11 @@ esphome/components/ac_dimmer/* @glmnet
|
|||||||
esphome/components/adc/* @esphome/core
|
esphome/components/adc/* @esphome/core
|
||||||
esphome/components/addressable_light/* @justfalter
|
esphome/components/addressable_light/* @justfalter
|
||||||
esphome/components/airthings_ble/* @jeromelaban
|
esphome/components/airthings_ble/* @jeromelaban
|
||||||
|
esphome/components/airthings_wave_mini/* @ncareau
|
||||||
esphome/components/airthings_wave_plus/* @jeromelaban
|
esphome/components/airthings_wave_plus/* @jeromelaban
|
||||||
esphome/components/am43/* @buxtronix
|
esphome/components/am43/* @buxtronix
|
||||||
esphome/components/am43/cover/* @buxtronix
|
esphome/components/am43/cover/* @buxtronix
|
||||||
|
esphome/components/analog_threshold/* @ianchi
|
||||||
esphome/components/animation/* @syndlex
|
esphome/components/animation/* @syndlex
|
||||||
esphome/components/anova/* @buxtronix
|
esphome/components/anova/* @buxtronix
|
||||||
esphome/components/api/* @OttoWinter
|
esphome/components/api/* @OttoWinter
|
||||||
@@ -26,51 +28,87 @@ esphome/components/atc_mithermometer/* @ahpohl
|
|||||||
esphome/components/b_parasite/* @rbaron
|
esphome/components/b_parasite/* @rbaron
|
||||||
esphome/components/ballu/* @bazuchan
|
esphome/components/ballu/* @bazuchan
|
||||||
esphome/components/bang_bang/* @OttoWinter
|
esphome/components/bang_bang/* @OttoWinter
|
||||||
|
esphome/components/bedjet/* @jhansche
|
||||||
|
esphome/components/bh1750/* @OttoWinter
|
||||||
esphome/components/binary_sensor/* @esphome/core
|
esphome/components/binary_sensor/* @esphome/core
|
||||||
|
esphome/components/bl0939/* @ziceva
|
||||||
|
esphome/components/bl0940/* @tobias-
|
||||||
esphome/components/ble_client/* @buxtronix
|
esphome/components/ble_client/* @buxtronix
|
||||||
esphome/components/bme680_bsec/* @trvrnrth
|
esphome/components/bme680_bsec/* @trvrnrth
|
||||||
|
esphome/components/bmp3xx/* @martgras
|
||||||
|
esphome/components/button/* @esphome/core
|
||||||
esphome/components/canbus/* @danielschramm @mvturnho
|
esphome/components/canbus/* @danielschramm @mvturnho
|
||||||
|
esphome/components/cap1188/* @MrEditor97
|
||||||
esphome/components/captive_portal/* @OttoWinter
|
esphome/components/captive_portal/* @OttoWinter
|
||||||
esphome/components/ccs811/* @habbie
|
esphome/components/ccs811/* @habbie
|
||||||
|
esphome/components/cd74hc4067/* @asoehlke
|
||||||
esphome/components/climate/* @esphome/core
|
esphome/components/climate/* @esphome/core
|
||||||
esphome/components/climate_ir/* @glmnet
|
esphome/components/climate_ir/* @glmnet
|
||||||
esphome/components/color_temperature/* @jesserockz
|
esphome/components/color_temperature/* @jesserockz
|
||||||
esphome/components/coolix/* @glmnet
|
esphome/components/coolix/* @glmnet
|
||||||
|
esphome/components/copy/* @OttoWinter
|
||||||
esphome/components/cover/* @esphome/core
|
esphome/components/cover/* @esphome/core
|
||||||
esphome/components/cs5460a/* @balrog-kun
|
esphome/components/cs5460a/* @balrog-kun
|
||||||
|
esphome/components/cse7761/* @berfenger
|
||||||
esphome/components/ct_clamp/* @jesserockz
|
esphome/components/ct_clamp/* @jesserockz
|
||||||
|
esphome/components/current_based/* @djwmarcx
|
||||||
|
esphome/components/dac7678/* @NickB1
|
||||||
|
esphome/components/daly_bms/* @s1lvi0
|
||||||
|
esphome/components/dashboard_import/* @esphome/core
|
||||||
esphome/components/debug/* @OttoWinter
|
esphome/components/debug/* @OttoWinter
|
||||||
|
esphome/components/delonghi/* @grob6000
|
||||||
esphome/components/dfplayer/* @glmnet
|
esphome/components/dfplayer/* @glmnet
|
||||||
esphome/components/dht/* @OttoWinter
|
esphome/components/dht/* @OttoWinter
|
||||||
esphome/components/ds1307/* @badbadc0ffee
|
esphome/components/ds1307/* @badbadc0ffee
|
||||||
esphome/components/dsmr/* @glmnet @zuidwijk
|
esphome/components/dsmr/* @glmnet @zuidwijk
|
||||||
|
esphome/components/ektf2232/* @jesserockz
|
||||||
|
esphome/components/ens210/* @itn3rd77
|
||||||
|
esphome/components/esp32/* @esphome/core
|
||||||
esphome/components/esp32_ble/* @jesserockz
|
esphome/components/esp32_ble/* @jesserockz
|
||||||
esphome/components/esp32_ble_server/* @jesserockz
|
esphome/components/esp32_ble_server/* @jesserockz
|
||||||
|
esphome/components/esp32_camera_web_server/* @ayufan
|
||||||
|
esphome/components/esp32_can/* @Sympatron
|
||||||
esphome/components/esp32_improv/* @jesserockz
|
esphome/components/esp32_improv/* @jesserockz
|
||||||
|
esphome/components/esp8266/* @esphome/core
|
||||||
esphome/components/exposure_notifications/* @OttoWinter
|
esphome/components/exposure_notifications/* @OttoWinter
|
||||||
esphome/components/ezo/* @ssieb
|
esphome/components/ezo/* @ssieb
|
||||||
esphome/components/fastled_base/* @OttoWinter
|
esphome/components/fastled_base/* @OttoWinter
|
||||||
|
esphome/components/feedback/* @ianchi
|
||||||
esphome/components/fingerprint_grow/* @OnFreund @loongyh
|
esphome/components/fingerprint_grow/* @OnFreund @loongyh
|
||||||
esphome/components/globals/* @esphome/core
|
esphome/components/globals/* @esphome/core
|
||||||
esphome/components/gpio/* @esphome/core
|
esphome/components/gpio/* @esphome/core
|
||||||
esphome/components/gps/* @coogle
|
esphome/components/gps/* @coogle
|
||||||
|
esphome/components/graph/* @synco
|
||||||
|
esphome/components/growatt_solar/* @leeuwte
|
||||||
esphome/components/havells_solar/* @sourabhjaiswal
|
esphome/components/havells_solar/* @sourabhjaiswal
|
||||||
esphome/components/hbridge/fan/* @WeekendWarrior
|
esphome/components/hbridge/fan/* @WeekendWarrior
|
||||||
esphome/components/hbridge/light/* @DotNetDann
|
esphome/components/hbridge/light/* @DotNetDann
|
||||||
|
esphome/components/heatpumpir/* @rob-deutsch
|
||||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||||
esphome/components/homeassistant/* @OttoWinter
|
esphome/components/homeassistant/* @OttoWinter
|
||||||
|
esphome/components/honeywellabp/* @RubyBailey
|
||||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
||||||
|
esphome/components/hydreon_rgxx/* @functionpointer
|
||||||
esphome/components/i2c/* @esphome/core
|
esphome/components/i2c/* @esphome/core
|
||||||
esphome/components/improv/* @jesserockz
|
esphome/components/i2s_audio/* @jesserockz
|
||||||
|
esphome/components/improv_serial/* @esphome/core
|
||||||
|
esphome/components/ina260/* @MrEditor97
|
||||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
||||||
esphome/components/inkplate6/* @jesserockz
|
esphome/components/inkplate6/* @jesserockz
|
||||||
esphome/components/integration/* @OttoWinter
|
esphome/components/integration/* @OttoWinter
|
||||||
esphome/components/interval/* @esphome/core
|
esphome/components/interval/* @esphome/core
|
||||||
esphome/components/json/* @OttoWinter
|
esphome/components/json/* @OttoWinter
|
||||||
|
esphome/components/kalman_combinator/* @Cat-Ion
|
||||||
esphome/components/ledc/* @OttoWinter
|
esphome/components/ledc/* @OttoWinter
|
||||||
esphome/components/light/* @esphome/core
|
esphome/components/light/* @esphome/core
|
||||||
|
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
||||||
|
esphome/components/lock/* @esphome/core
|
||||||
esphome/components/logger/* @esphome/core
|
esphome/components/logger/* @esphome/core
|
||||||
|
esphome/components/ltr390/* @sjtrny
|
||||||
|
esphome/components/max31865/* @DAVe3283
|
||||||
|
esphome/components/max44009/* @berfenger
|
||||||
esphome/components/max7219digit/* @rspaargaren
|
esphome/components/max7219digit/* @rspaargaren
|
||||||
|
esphome/components/max9611/* @mckaymatthew
|
||||||
esphome/components/mcp23008/* @jesserockz
|
esphome/components/mcp23008/* @jesserockz
|
||||||
esphome/components/mcp23017/* @jesserockz
|
esphome/components/mcp23017/* @jesserockz
|
||||||
esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz
|
esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz
|
||||||
@@ -79,9 +117,28 @@ esphome/components/mcp23x08_base/* @jesserockz
|
|||||||
esphome/components/mcp23x17_base/* @jesserockz
|
esphome/components/mcp23x17_base/* @jesserockz
|
||||||
esphome/components/mcp23xxx_base/* @jesserockz
|
esphome/components/mcp23xxx_base/* @jesserockz
|
||||||
esphome/components/mcp2515/* @danielschramm @mvturnho
|
esphome/components/mcp2515/* @danielschramm @mvturnho
|
||||||
|
esphome/components/mcp3204/* @rsumner
|
||||||
|
esphome/components/mcp4728/* @berfenger
|
||||||
|
esphome/components/mcp47a1/* @jesserockz
|
||||||
esphome/components/mcp9808/* @k7hpn
|
esphome/components/mcp9808/* @k7hpn
|
||||||
|
esphome/components/md5/* @esphome/core
|
||||||
|
esphome/components/mdns/* @esphome/core
|
||||||
|
esphome/components/media_player/* @jesserockz
|
||||||
esphome/components/midea/* @dudanov
|
esphome/components/midea/* @dudanov
|
||||||
|
esphome/components/midea_ir/* @dudanov
|
||||||
esphome/components/mitsubishi/* @RubyBailey
|
esphome/components/mitsubishi/* @RubyBailey
|
||||||
|
esphome/components/mlx90393/* @functionpointer
|
||||||
|
esphome/components/modbus_controller/* @martgras
|
||||||
|
esphome/components/modbus_controller/binary_sensor/* @martgras
|
||||||
|
esphome/components/modbus_controller/number/* @martgras
|
||||||
|
esphome/components/modbus_controller/output/* @martgras
|
||||||
|
esphome/components/modbus_controller/select/* @martgras @stegm
|
||||||
|
esphome/components/modbus_controller/sensor/* @martgras
|
||||||
|
esphome/components/modbus_controller/switch/* @martgras
|
||||||
|
esphome/components/modbus_controller/text_sensor/* @martgras
|
||||||
|
esphome/components/mopeka_ble/* @spbrogan
|
||||||
|
esphome/components/mopeka_pro_check/* @spbrogan
|
||||||
|
esphome/components/mpu6886/* @fabaff
|
||||||
esphome/components/network/* @esphome/core
|
esphome/components/network/* @esphome/core
|
||||||
esphome/components/nextion/* @senexcrenshaw
|
esphome/components/nextion/* @senexcrenshaw
|
||||||
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
||||||
@@ -100,8 +157,14 @@ esphome/components/pn532/* @OttoWinter @jesserockz
|
|||||||
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
||||||
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
||||||
esphome/components/power_supply/* @esphome/core
|
esphome/components/power_supply/* @esphome/core
|
||||||
esphome/components/pulse_meter/* @stevebaxter
|
esphome/components/preferences/* @esphome/core
|
||||||
|
esphome/components/psram/* @esphome/core
|
||||||
|
esphome/components/pulse_meter/* @cstaahl @stevebaxter
|
||||||
esphome/components/pvvx_mithermometer/* @pasiz
|
esphome/components/pvvx_mithermometer/* @pasiz
|
||||||
|
esphome/components/qmp6988/* @andrewpc
|
||||||
|
esphome/components/qr_code/* @wjtje
|
||||||
|
esphome/components/radon_eye_ble/* @jeffeb3
|
||||||
|
esphome/components/radon_eye_rd200/* @jeffeb3
|
||||||
esphome/components/rc522/* @glmnet
|
esphome/components/rc522/* @glmnet
|
||||||
esphome/components/rc522_i2c/* @glmnet
|
esphome/components/rc522_i2c/* @glmnet
|
||||||
esphome/components/rc522_spi/* @glmnet
|
esphome/components/rc522_spi/* @glmnet
|
||||||
@@ -109,19 +172,30 @@ esphome/components/restart/* @esphome/core
|
|||||||
esphome/components/rf_bridge/* @jesserockz
|
esphome/components/rf_bridge/* @jesserockz
|
||||||
esphome/components/rgbct/* @jesserockz
|
esphome/components/rgbct/* @jesserockz
|
||||||
esphome/components/rtttl/* @glmnet
|
esphome/components/rtttl/* @glmnet
|
||||||
|
esphome/components/safe_mode/* @jsuanet @paulmonigatti
|
||||||
|
esphome/components/scd4x/* @martgras @sjtrny
|
||||||
esphome/components/script/* @esphome/core
|
esphome/components/script/* @esphome/core
|
||||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
||||||
esphome/components/sdp3x/* @Azimath
|
esphome/components/sdp3x/* @Azimath
|
||||||
esphome/components/selec_meter/* @sourabhjaiswal
|
esphome/components/selec_meter/* @sourabhjaiswal
|
||||||
esphome/components/select/* @esphome/core
|
esphome/components/select/* @esphome/core
|
||||||
|
esphome/components/sen5x/* @martgras
|
||||||
|
esphome/components/sensirion_common/* @martgras
|
||||||
esphome/components/sensor/* @esphome/core
|
esphome/components/sensor/* @esphome/core
|
||||||
esphome/components/sgp40/* @SenexCrenshaw
|
esphome/components/sgp40/* @SenexCrenshaw
|
||||||
|
esphome/components/sgp4x/* @SenexCrenshaw @martgras
|
||||||
|
esphome/components/shelly_dimmer/* @edge90 @rnauber
|
||||||
esphome/components/sht4x/* @sjtrny
|
esphome/components/sht4x/* @sjtrny
|
||||||
esphome/components/shutdown/* @esphome/core
|
esphome/components/shutdown/* @esphome/core @jsuanet
|
||||||
esphome/components/sim800l/* @glmnet
|
esphome/components/sim800l/* @glmnet
|
||||||
esphome/components/sm2135/* @BoukeHaarsma23
|
esphome/components/sm2135/* @BoukeHaarsma23
|
||||||
|
esphome/components/sml/* @alengwenus
|
||||||
|
esphome/components/smt100/* @piechade
|
||||||
esphome/components/socket/* @esphome/core
|
esphome/components/socket/* @esphome/core
|
||||||
|
esphome/components/sonoff_d1/* @anatoly-savchenkov
|
||||||
esphome/components/spi/* @esphome/core
|
esphome/components/spi/* @esphome/core
|
||||||
|
esphome/components/sprinkler/* @kbx81
|
||||||
|
esphome/components/sps30/* @martgras
|
||||||
esphome/components/ssd1322_base/* @kbx81
|
esphome/components/ssd1322_base/* @kbx81
|
||||||
esphome/components/ssd1322_spi/* @kbx81
|
esphome/components/ssd1322_spi/* @kbx81
|
||||||
esphome/components/ssd1325_base/* @kbx81
|
esphome/components/ssd1325_base/* @kbx81
|
||||||
@@ -151,16 +225,24 @@ esphome/components/tmp102/* @timsavage
|
|||||||
esphome/components/tmp117/* @Azimath
|
esphome/components/tmp117/* @Azimath
|
||||||
esphome/components/tof10120/* @wstrzalka
|
esphome/components/tof10120/* @wstrzalka
|
||||||
esphome/components/toshiba/* @kbx81
|
esphome/components/toshiba/* @kbx81
|
||||||
|
esphome/components/touchscreen/* @jesserockz
|
||||||
esphome/components/tsl2591/* @wjcarpenter
|
esphome/components/tsl2591/* @wjcarpenter
|
||||||
esphome/components/tuya/binary_sensor/* @jesserockz
|
esphome/components/tuya/binary_sensor/* @jesserockz
|
||||||
esphome/components/tuya/climate/* @jesserockz
|
esphome/components/tuya/climate/* @jesserockz
|
||||||
|
esphome/components/tuya/number/* @frankiboy1
|
||||||
|
esphome/components/tuya/select/* @bearpawmaxim
|
||||||
esphome/components/tuya/sensor/* @jesserockz
|
esphome/components/tuya/sensor/* @jesserockz
|
||||||
esphome/components/tuya/switch/* @jesserockz
|
esphome/components/tuya/switch/* @jesserockz
|
||||||
|
esphome/components/tuya/text_sensor/* @dentra
|
||||||
esphome/components/uart/* @esphome/core
|
esphome/components/uart/* @esphome/core
|
||||||
esphome/components/ultrasonic/* @OttoWinter
|
esphome/components/ultrasonic/* @OttoWinter
|
||||||
esphome/components/version/* @esphome/core
|
esphome/components/version/* @esphome/core
|
||||||
|
esphome/components/wake_on_lan/* @willwill2will54
|
||||||
esphome/components/web_server_base/* @OttoWinter
|
esphome/components/web_server_base/* @OttoWinter
|
||||||
esphome/components/whirlpool/* @glmnet
|
esphome/components/whirlpool/* @glmnet
|
||||||
|
esphome/components/whynter/* @aeonsablaze
|
||||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
||||||
|
esphome/components/xiaomi_mhoc303/* @drug123
|
||||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
||||||
|
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
||||||
esphome/components/xpt2046/* @numo68
|
esphome/components/xpt2046/* @numo68
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
# Contributing to ESPHome
|
# Contributing to ESPHome
|
||||||
|
|
||||||
This python project is responsible for reading in YAML configuration files,
|
For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphome
|
||||||
converting them to C++ code. This code is then converted to a platformio project and compiled
|
|
||||||
with [esphome-core](https://github.com/esphome/esphome-core), the C++ framework behind the project.
|
|
||||||
|
|
||||||
For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphomeyaml
|
|
||||||
|
|
||||||
Things to note when contributing:
|
Things to note when contributing:
|
||||||
|
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ include requirements.txt
|
|||||||
include esphome/dashboard/templates/*.html
|
include esphome/dashboard/templates/*.html
|
||||||
recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
|
recursive-include esphome/dashboard/static *.ico *.js *.css *.woff* LICENSE
|
||||||
recursive-include esphome *.cpp *.h *.tcc
|
recursive-include esphome *.cpp *.h *.tcc
|
||||||
|
recursive-include esphome *.py.script
|
||||||
recursive-include esphome LICENSE.txt
|
recursive-include esphome LICENSE.txt
|
||||||
|
|||||||
@@ -1,5 +1,57 @@
|
|||||||
ARG BUILD_FROM=esphome/esphome-base:latest
|
# Build these with the build.py script
|
||||||
FROM ${BUILD_FROM}
|
# Example:
|
||||||
|
# python3 docker/build.py --tag dev --arch amd64 --build-type docker build
|
||||||
|
|
||||||
|
# One of "docker", "hassio"
|
||||||
|
ARG BASEIMGTYPE=docker
|
||||||
|
|
||||||
|
# https://github.com/hassio-addons/addon-debian-base/releases
|
||||||
|
FROM ghcr.io/hassio-addons/debian-base/amd64:5.3.0 AS base-hassio-amd64
|
||||||
|
FROM ghcr.io/hassio-addons/debian-base/aarch64:5.3.0 AS base-hassio-arm64
|
||||||
|
FROM ghcr.io/hassio-addons/debian-base/armv7:5.3.0 AS base-hassio-armv7
|
||||||
|
# https://hub.docker.com/_/debian?tab=tags&page=1&name=bullseye
|
||||||
|
FROM debian:bullseye-20220328-slim AS base-docker-amd64
|
||||||
|
FROM debian:bullseye-20220328-slim AS base-docker-arm64
|
||||||
|
FROM debian:bullseye-20220328-slim AS base-docker-armv7
|
||||||
|
|
||||||
|
# Use TARGETARCH/TARGETVARIANT defined by docker
|
||||||
|
# https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope
|
||||||
|
FROM base-${BASEIMGTYPE}-${TARGETARCH}${TARGETVARIANT} AS base
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update \
|
||||||
|
# Use pinned versions so that we get updates with build caching
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
python3=3.9.2-3 \
|
||||||
|
python3-pip=20.3.4-4+deb11u1 \
|
||||||
|
python3-setuptools=52.0.0-4 \
|
||||||
|
python3-pil=8.1.2+dfsg-0.3+deb11u1 \
|
||||||
|
python3-cryptography=3.3.2-1 \
|
||||||
|
iputils-ping=3:20210202-1 \
|
||||||
|
git=1:2.30.2-1 \
|
||||||
|
curl=7.74.0-1.3+deb11u1 \
|
||||||
|
openssh-client=1:8.4p1-5 \
|
||||||
|
&& rm -rf \
|
||||||
|
/tmp/* \
|
||||||
|
/var/{cache,log}/* \
|
||||||
|
/var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ENV \
|
||||||
|
# Fix click python3 lang warning https://click.palletsprojects.com/en/7.x/python3/
|
||||||
|
LANG=C.UTF-8 LC_ALL=C.UTF-8 \
|
||||||
|
# Store globally installed pio libs in /piolibs
|
||||||
|
PLATFORMIO_GLOBALLIB_DIR=/piolibs
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
# Ubuntu python3-pip is missing wheel
|
||||||
|
pip3 install --no-cache-dir \
|
||||||
|
wheel==0.37.1 \
|
||||||
|
platformio==6.0.2 \
|
||||||
|
# Change some platformio settings
|
||||||
|
&& platformio settings set enable_telemetry No \
|
||||||
|
&& platformio settings set check_platformio_interval 1000000 \
|
||||||
|
&& mkdir -p /piolibs
|
||||||
|
|
||||||
|
|
||||||
# First install requirements to leverage caching when requirements don't change
|
# First install requirements to leverage caching when requirements don't change
|
||||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
||||||
@@ -7,9 +59,13 @@ RUN \
|
|||||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
||||||
&& /platformio_install_deps.py /platformio.ini
|
&& /platformio_install_deps.py /platformio.ini
|
||||||
|
|
||||||
# Then copy esphome and install
|
|
||||||
COPY . .
|
# ======================= docker-type image =======================
|
||||||
RUN pip3 install --no-cache-dir -e .
|
FROM base AS docker
|
||||||
|
|
||||||
|
# Copy esphome and install
|
||||||
|
COPY . /esphome
|
||||||
|
RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
|
||||||
|
|
||||||
# Settings for dashboard
|
# Settings for dashboard
|
||||||
ENV USERNAME="" PASSWORD=""
|
ENV USERNAME="" PASSWORD=""
|
||||||
@@ -17,14 +73,77 @@ ENV USERNAME="" PASSWORD=""
|
|||||||
# Expose the dashboard to Docker
|
# Expose the dashboard to Docker
|
||||||
EXPOSE 6052
|
EXPOSE 6052
|
||||||
|
|
||||||
# Run healthcheck (heartbeat)
|
COPY docker/docker_entrypoint.sh /entrypoint.sh
|
||||||
HEALTHCHECK --interval=30s --timeout=30s \
|
|
||||||
CMD curl --fail http://localhost:6052 || exit 1
|
|
||||||
|
|
||||||
# The directory the user should mount their configuration files to
|
# The directory the user should mount their configuration files to
|
||||||
|
VOLUME /config
|
||||||
WORKDIR /config
|
WORKDIR /config
|
||||||
# Set entrypoint to esphome so that the user doesn't have to type 'esphome'
|
# Set entrypoint to esphome (via a script) so that the user doesn't have to type 'esphome'
|
||||||
# in every docker command twice
|
# in every docker command twice
|
||||||
ENTRYPOINT ["esphome"]
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
# When no arguments given, start the dashboard in the workdir
|
# When no arguments given, start the dashboard in the workdir
|
||||||
CMD ["dashboard", "/config"]
|
CMD ["dashboard", "/config"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ======================= hassio-type image =======================
|
||||||
|
FROM base AS hassio
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update \
|
||||||
|
# Use pinned versions so that we get updates with build caching
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
nginx-light=1.18.0-6.1+deb11u2 \
|
||||||
|
&& rm -rf \
|
||||||
|
/tmp/* \
|
||||||
|
/var/{cache,log}/* \
|
||||||
|
/var/lib/apt/lists/*
|
||||||
|
|
||||||
|
ARG BUILD_VERSION=dev
|
||||||
|
|
||||||
|
# Copy root filesystem
|
||||||
|
COPY docker/ha-addon-rootfs/ /
|
||||||
|
|
||||||
|
# Copy esphome and install
|
||||||
|
COPY . /esphome
|
||||||
|
RUN pip3 install --no-cache-dir --no-use-pep517 -e /esphome
|
||||||
|
|
||||||
|
# Labels
|
||||||
|
LABEL \
|
||||||
|
io.hass.name="ESPHome" \
|
||||||
|
io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
|
||||||
|
io.hass.type="addon" \
|
||||||
|
io.hass.version="${BUILD_VERSION}"
|
||||||
|
# io.hass.arch is inherited from addon-debian-base
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ======================= lint-type image =======================
|
||||||
|
FROM base AS lint
|
||||||
|
|
||||||
|
ENV \
|
||||||
|
PLATFORMIO_CORE_DIR=/esphome/.temp/platformio
|
||||||
|
|
||||||
|
RUN \
|
||||||
|
apt-get update \
|
||||||
|
# Use pinned versions so that we get updates with build caching
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
clang-format-11=1:11.0.1-2 \
|
||||||
|
clang-tidy-11=1:11.0.1-2 \
|
||||||
|
patch=2.7.6-7 \
|
||||||
|
software-properties-common=0.96.20.2-2.1 \
|
||||||
|
nano=5.4-2+deb11u1 \
|
||||||
|
build-essential=12.9 \
|
||||||
|
python3-dev=3.9.2-3 \
|
||||||
|
&& rm -rf \
|
||||||
|
/tmp/* \
|
||||||
|
/var/{cache,log}/* \
|
||||||
|
/var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY requirements_test.txt /
|
||||||
|
RUN pip3 install --no-cache-dir -r /requirements_test.txt
|
||||||
|
|
||||||
|
VOLUME ["/esphome"]
|
||||||
|
WORKDIR /esphome
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
FROM esphome/esphome-lint:1.1
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
ARG BUILD_FROM=esphome/esphome-hassio-base:latest
|
|
||||||
FROM ${BUILD_FROM}
|
|
||||||
|
|
||||||
# First install requirements to leverage caching when requirements don't change
|
|
||||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
|
||||||
RUN \
|
|
||||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
|
||||||
&& /platformio_install_deps.py /platformio.ini
|
|
||||||
|
|
||||||
# Copy root filesystem
|
|
||||||
COPY docker/rootfs/ /
|
|
||||||
|
|
||||||
# Then copy esphome and install
|
|
||||||
COPY . /opt/esphome/
|
|
||||||
RUN pip3 install --no-cache-dir -e /opt/esphome
|
|
||||||
|
|
||||||
# Build arguments
|
|
||||||
ARG BUILD_VERSION=dev
|
|
||||||
|
|
||||||
# Labels
|
|
||||||
LABEL \
|
|
||||||
io.hass.name="ESPHome" \
|
|
||||||
io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
|
|
||||||
io.hass.type="addon" \
|
|
||||||
io.hass.version=${BUILD_VERSION}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
ARG BUILD_FROM=esphome/esphome-lint-base:latest
|
|
||||||
FROM ${BUILD_FROM}
|
|
||||||
|
|
||||||
COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
|
|
||||||
RUN \
|
|
||||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
|
|
||||||
&& /platformio_install_deps.py /platformio.ini
|
|
||||||
|
|
||||||
VOLUME ["/esphome"]
|
|
||||||
WORKDIR /esphome
|
|
||||||
103
docker/build.py
103
docker/build.py
@@ -2,7 +2,7 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import subprocess
|
import subprocess
|
||||||
import argparse
|
import argparse
|
||||||
import platform
|
from platform import machine
|
||||||
import shlex
|
import shlex
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
@@ -24,9 +24,6 @@ TYPE_LINT = 'lint'
|
|||||||
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
|
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
|
||||||
|
|
||||||
|
|
||||||
BASE_VERSION = "4.2.0"
|
|
||||||
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("--tag", type=str, required=True, help="The main docker tag to push to. If a version number also adds latest and/or beta tag")
|
parser.add_argument("--tag", type=str, required=True, help="The main docker tag to push to. If a version number also adds latest and/or beta tag")
|
||||||
parser.add_argument("--arch", choices=ARCHS, required=False, help="The architecture to build for")
|
parser.add_argument("--arch", choices=ARCHS, required=False, help="The architecture to build for")
|
||||||
@@ -34,27 +31,18 @@ parser.add_argument("--build-type", choices=TYPES, required=True, help="The type
|
|||||||
parser.add_argument("--dry-run", action="store_true", help="Don't run any commands, just print them")
|
parser.add_argument("--dry-run", action="store_true", help="Don't run any commands, just print them")
|
||||||
subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True)
|
subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True)
|
||||||
build_parser = subparsers.add_parser("build", help="Build the image")
|
build_parser = subparsers.add_parser("build", help="Build the image")
|
||||||
push_parser = subparsers.add_parser("push", help="Tag the already built image and push it to docker hub")
|
build_parser.add_argument("--push", help="Also push the images", action="store_true")
|
||||||
|
build_parser.add_argument("--load", help="Load the docker image locally", action="store_true")
|
||||||
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
|
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# only lists some possibilities, doesn't have to be perfect
|
|
||||||
# https://stackoverflow.com/a/45125525
|
|
||||||
UNAME_TO_ARCH = {
|
|
||||||
"x86_64": ARCH_AMD64,
|
|
||||||
"aarch64": ARCH_AARCH64,
|
|
||||||
"aarch64_be": ARCH_AARCH64,
|
|
||||||
"arm": ARCH_ARMV7,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class DockerParams:
|
class DockerParams:
|
||||||
build_from: str
|
|
||||||
build_to: str
|
build_to: str
|
||||||
manifest_to: str
|
manifest_to: str
|
||||||
dockerfile: str
|
baseimgtype: str
|
||||||
|
platform: str
|
||||||
|
target: str
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def for_type_arch(cls, build_type, arch):
|
def for_type_arch(cls, build_type, arch):
|
||||||
@@ -63,18 +51,28 @@ class DockerParams:
|
|||||||
TYPE_HA_ADDON: "esphome/esphome-hassio",
|
TYPE_HA_ADDON: "esphome/esphome-hassio",
|
||||||
TYPE_LINT: "esphome/esphome-lint"
|
TYPE_LINT: "esphome/esphome-lint"
|
||||||
}[build_type]
|
}[build_type]
|
||||||
build_from = f"ghcr.io/{prefix}-base-{arch}:{BASE_VERSION}"
|
|
||||||
build_to = f"{prefix}-{arch}"
|
build_to = f"{prefix}-{arch}"
|
||||||
dockerfile = {
|
baseimgtype = {
|
||||||
TYPE_DOCKER: "docker/Dockerfile",
|
TYPE_DOCKER: "docker",
|
||||||
TYPE_HA_ADDON: "docker/Dockerfile.hassio",
|
TYPE_HA_ADDON: "hassio",
|
||||||
TYPE_LINT: "docker/Dockerfile.lint",
|
TYPE_LINT: "docker",
|
||||||
|
}[build_type]
|
||||||
|
platform = {
|
||||||
|
ARCH_AMD64: "linux/amd64",
|
||||||
|
ARCH_ARMV7: "linux/arm/v7",
|
||||||
|
ARCH_AARCH64: "linux/arm64",
|
||||||
|
}[arch]
|
||||||
|
target = {
|
||||||
|
TYPE_DOCKER: "docker",
|
||||||
|
TYPE_HA_ADDON: "hassio",
|
||||||
|
TYPE_LINT: "lint",
|
||||||
}[build_type]
|
}[build_type]
|
||||||
return cls(
|
return cls(
|
||||||
build_from=build_from,
|
|
||||||
build_to=build_to,
|
build_to=build_to,
|
||||||
manifest_to=prefix,
|
manifest_to=prefix,
|
||||||
dockerfile=dockerfile
|
baseimgtype=baseimgtype,
|
||||||
|
platform=platform,
|
||||||
|
target=target,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -112,46 +110,33 @@ def main():
|
|||||||
# 1. pull cache image
|
# 1. pull cache image
|
||||||
params = DockerParams.for_type_arch(args.build_type, args.arch)
|
params = DockerParams.for_type_arch(args.build_type, args.arch)
|
||||||
cache_tag = {
|
cache_tag = {
|
||||||
CHANNEL_DEV: "dev",
|
CHANNEL_DEV: "cache-dev",
|
||||||
CHANNEL_BETA: "beta",
|
CHANNEL_BETA: "cache-beta",
|
||||||
CHANNEL_RELEASE: "latest",
|
CHANNEL_RELEASE: "cache-latest",
|
||||||
}[channel]
|
}[channel]
|
||||||
cache_img = f"ghcr.io/{params.build_to}:{cache_tag}"
|
cache_img = f"ghcr.io/{params.build_to}:{cache_tag}"
|
||||||
run_command("docker", "pull", cache_img, ignore_error=True)
|
|
||||||
|
|
||||||
# 2. register QEMU binfmt (if not host arch)
|
|
||||||
is_native = UNAME_TO_ARCH.get(platform.machine()) == args.arch
|
|
||||||
if not is_native:
|
|
||||||
run_command(
|
|
||||||
"docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static:5.2.0-2",
|
|
||||||
"--reset", "-p", "yes"
|
|
||||||
)
|
|
||||||
|
|
||||||
# 3. build
|
|
||||||
run_command(
|
|
||||||
"docker", "build",
|
|
||||||
"--build-arg", f"BUILD_FROM={params.build_from}",
|
|
||||||
"--build-arg", f"BUILD_VERSION={args.tag}",
|
|
||||||
"--tag", f"{params.build_to}:{args.tag}",
|
|
||||||
"--cache-from", cache_img,
|
|
||||||
"--file", params.dockerfile,
|
|
||||||
"."
|
|
||||||
)
|
|
||||||
elif args.command == "push":
|
|
||||||
params = DockerParams.for_type_arch(args.build_type, args.arch)
|
|
||||||
imgs = [f"{params.build_to}:{tag}" for tag in tags_to_push]
|
imgs = [f"{params.build_to}:{tag}" for tag in tags_to_push]
|
||||||
imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push]
|
imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push]
|
||||||
src = imgs[0]
|
|
||||||
# 1. tag images
|
# 3. build
|
||||||
for img in imgs[1:]:
|
cmd = [
|
||||||
run_command(
|
"docker", "buildx", "build",
|
||||||
"docker", "tag", src, img
|
"--build-arg", f"BASEIMGTYPE={params.baseimgtype}",
|
||||||
)
|
"--build-arg", f"BUILD_VERSION={args.tag}",
|
||||||
# 2. push images
|
"--cache-from", f"type=registry,ref={cache_img}",
|
||||||
|
"--file", "docker/Dockerfile",
|
||||||
|
"--platform", params.platform,
|
||||||
|
"--target", params.target,
|
||||||
|
]
|
||||||
for img in imgs:
|
for img in imgs:
|
||||||
run_command(
|
cmd += ["--tag", img]
|
||||||
"docker", "push", img
|
if args.push:
|
||||||
)
|
cmd += ["--push", "--cache-to", f"type=registry,ref={cache_img},mode=max"]
|
||||||
|
if args.load:
|
||||||
|
cmd += ["--load"]
|
||||||
|
|
||||||
|
run_command(*cmd, ".")
|
||||||
elif args.command == "manifest":
|
elif args.command == "manifest":
|
||||||
manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to
|
manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to
|
||||||
|
|
||||||
|
|||||||
24
docker/docker_entrypoint.sh
Executable file
24
docker/docker_entrypoint.sh
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# If /cache is mounted, use that as PIO's coredir
|
||||||
|
# otherwise use path in /config (so that PIO packages aren't downloaded on each compile)
|
||||||
|
|
||||||
|
if [[ -d /cache ]]; then
|
||||||
|
pio_cache_base=/cache/platformio
|
||||||
|
else
|
||||||
|
pio_cache_base=/config/.esphome/platformio
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -d "${pio_cache_base}" ]]; then
|
||||||
|
echo "Creating cache directory ${pio_cache_base}"
|
||||||
|
echo "You can change this behavior by mounting a directory to the container's /cache directory."
|
||||||
|
mkdir -p "${pio_cache_base}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# we can't set core_dir, because the settings file is stored in `core_dir/appstate.json`
|
||||||
|
# setting `core_dir` would therefore prevent pio from accessing
|
||||||
|
export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
|
||||||
|
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
|
||||||
|
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
|
||||||
|
|
||||||
|
exec esphome "$@"
|
||||||
@@ -7,12 +7,12 @@
|
|||||||
# Check SSL requirements, if enabled
|
# Check SSL requirements, if enabled
|
||||||
if bashio::config.true 'ssl'; then
|
if bashio::config.true 'ssl'; then
|
||||||
if ! bashio::config.has_value 'certfile'; then
|
if ! bashio::config.has_value 'certfile'; then
|
||||||
bashio::fatal 'SSL is enabled, but no certfile was specified.'
|
bashio::log.fatal 'SSL is enabled, but no certfile was specified.'
|
||||||
bashio::exit.nok
|
bashio::exit.nok
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! bashio::config.has_value 'keyfile'; then
|
if ! bashio::config.has_value 'keyfile'; then
|
||||||
bashio::fatal 'SSL is enabled, but no keyfile was specified'
|
bashio::log.fatal 'SSL is enabled, but no keyfile was specified'
|
||||||
bashio::exit.nok
|
bashio::exit.nok
|
||||||
fi
|
fi
|
||||||
|
|
||||||
9
docker/ha-addon-rootfs/etc/cont-init.d/30-dirs.sh
Normal file
9
docker/ha-addon-rootfs/etc/cont-init.d/30-dirs.sh
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/with-contenv bashio
|
||||||
|
# ==============================================================================
|
||||||
|
# Community Hass.io Add-ons: ESPHome
|
||||||
|
# This files creates all directories used by esphome
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
pio_cache_base=/data/cache/platformio
|
||||||
|
|
||||||
|
mkdir -p "${pio_cache_base}"
|
||||||
@@ -10,7 +10,7 @@ server {
|
|||||||
ssl_certificate_key /ssl/%%keyfile%%;
|
ssl_certificate_key /ssl/%%keyfile%%;
|
||||||
|
|
||||||
# Clear Hass.io Ingress header
|
# Clear Hass.io Ingress header
|
||||||
proxy_set_header X-Hassio-Ingress "";
|
proxy_set_header X-HA-Ingress "";
|
||||||
|
|
||||||
# Redirect http requests to https on the same port.
|
# Redirect http requests to https on the same port.
|
||||||
# https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/
|
# https://rageagainstshell.com/2016/11/redirect-http-to-https-on-the-same-port-in-nginx/
|
||||||
@@ -4,7 +4,7 @@ server {
|
|||||||
include /etc/nginx/includes/server_params.conf;
|
include /etc/nginx/includes/server_params.conf;
|
||||||
include /etc/nginx/includes/proxy_params.conf;
|
include /etc/nginx/includes/proxy_params.conf;
|
||||||
# Clear Hass.io Ingress header
|
# Clear Hass.io Ingress header
|
||||||
proxy_set_header X-Hassio-Ingress "";
|
proxy_set_header X-HA-Ingress "";
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://esphome;
|
proxy_pass http://esphome;
|
||||||
@@ -3,8 +3,8 @@ server {
|
|||||||
|
|
||||||
include /etc/nginx/includes/server_params.conf;
|
include /etc/nginx/includes/server_params.conf;
|
||||||
include /etc/nginx/includes/proxy_params.conf;
|
include /etc/nginx/includes/proxy_params.conf;
|
||||||
# Set Hass.io Ingress header
|
# Set Home Assistant Ingress header
|
||||||
proxy_set_header X-Hassio-Ingress "YES";
|
proxy_set_header X-HA-Ingress "YES";
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
# Only allow from Hass.io supervisor
|
# Only allow from Hass.io supervisor
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
# Runs the ESPHome dashboard
|
# Runs the ESPHome dashboard
|
||||||
# ==============================================================================
|
# ==============================================================================
|
||||||
|
|
||||||
export ESPHOME_IS_HASSIO=true
|
export ESPHOME_IS_HA_ADDON=true
|
||||||
|
|
||||||
if bashio::config.true 'leave_front_door_open'; then
|
if bashio::config.true 'leave_front_door_open'; then
|
||||||
export DISABLE_HA_AUTHENTICATION=true
|
export DISABLE_HA_AUTHENTICATION=true
|
||||||
@@ -22,5 +22,14 @@ if bashio::config.has_value 'relative_url'; then
|
|||||||
export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
|
export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
pio_cache_base=/data/cache/platformio
|
||||||
|
# we can't set core_dir, because the settings file is stored in `core_dir/appstate.json`
|
||||||
|
# setting `core_dir` would therefore prevent pio from accessing
|
||||||
|
export PLATFORMIO_PLATFORMS_DIR="${pio_cache_base}/platforms"
|
||||||
|
export PLATFORMIO_PACKAGES_DIR="${pio_cache_base}/packages"
|
||||||
|
export PLATFORMIO_CACHE_DIR="${pio_cache_base}/cache"
|
||||||
|
|
||||||
|
export PLATFORMIO_GLOBALLIB_DIR=/piolibs
|
||||||
|
|
||||||
bashio::log.info "Starting ESPHome dashboard..."
|
bashio::log.info "Starting ESPHome dashboard..."
|
||||||
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio
|
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon
|
||||||
@@ -3,18 +3,28 @@
|
|||||||
# all platformio libraries in the global storage
|
# all platformio libraries in the global storage
|
||||||
|
|
||||||
import configparser
|
import configparser
|
||||||
import re
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser(inline_comment_prefixes=(';', ))
|
||||||
config.read(sys.argv[1])
|
config.read(sys.argv[1])
|
||||||
|
|
||||||
libs = []
|
libs = []
|
||||||
for line in config['common']['lib_deps'].splitlines():
|
# Extract from every lib_deps key in all sections
|
||||||
# Format: '1655@1.0.2 ; TinyGPSPlus (has name conflict)' (includes comment)
|
for section in config.sections():
|
||||||
m = re.search(r'([a-zA-Z0-9-_/]+@[0-9\.]+)', line)
|
conf = config[section]
|
||||||
if m is None:
|
if "lib_deps" not in conf:
|
||||||
continue
|
continue
|
||||||
libs.append(m.group(1))
|
for lib_dep in conf["lib_deps"].splitlines():
|
||||||
|
if not lib_dep:
|
||||||
|
# Empty line or comment
|
||||||
|
continue
|
||||||
|
if lib_dep.startswith("${"):
|
||||||
|
# Extending from another section
|
||||||
|
continue
|
||||||
|
if "@" not in lib_dep:
|
||||||
|
# No version pinned, this is an internal lib
|
||||||
|
continue
|
||||||
|
libs.append(lib_dep)
|
||||||
|
|
||||||
subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])
|
subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import argparse
|
|||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
@@ -9,15 +10,19 @@ from esphome import const, writer, yaml_util
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.config import iter_components, read_config, strip_default_ids
|
from esphome.config import iter_components, read_config, strip_default_ids
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
ALLOWED_NAME_CHARS,
|
||||||
CONF_BAUD_RATE,
|
CONF_BAUD_RATE,
|
||||||
CONF_BROKER,
|
CONF_BROKER,
|
||||||
CONF_DEASSERT_RTS_DTR,
|
CONF_DEASSERT_RTS_DTR,
|
||||||
CONF_LOGGER,
|
CONF_LOGGER,
|
||||||
|
CONF_NAME,
|
||||||
CONF_OTA,
|
CONF_OTA,
|
||||||
CONF_PASSWORD,
|
CONF_PASSWORD,
|
||||||
CONF_PORT,
|
CONF_PORT,
|
||||||
CONF_ESPHOME,
|
CONF_ESPHOME,
|
||||||
CONF_PLATFORMIO_OPTIONS,
|
CONF_PLATFORMIO_OPTIONS,
|
||||||
|
CONF_SUBSTITUTIONS,
|
||||||
|
SECRETS_FILES,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, EsphomeError, coroutine
|
from esphome.core import CORE, EsphomeError, coroutine
|
||||||
from esphome.helpers import indent
|
from esphome.helpers import indent
|
||||||
@@ -72,7 +77,7 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api
|
|||||||
if default == "OTA":
|
if default == "OTA":
|
||||||
return CORE.address
|
return CORE.address
|
||||||
if show_mqtt and "mqtt" in CORE.config:
|
if show_mqtt and "mqtt" in CORE.config:
|
||||||
options.append(("MQTT ({})".format(CORE.config["mqtt"][CONF_BROKER]), "MQTT"))
|
options.append((f"MQTT ({CORE.config['mqtt'][CONF_BROKER]})", "MQTT"))
|
||||||
if default == "OTA":
|
if default == "OTA":
|
||||||
return "MQTT"
|
return "MQTT"
|
||||||
if default is not None:
|
if default is not None:
|
||||||
@@ -144,6 +149,8 @@ def wrap_to_code(name, comp):
|
|||||||
if comp.config_schema is not None:
|
if comp.config_schema is not None:
|
||||||
conf_str = yaml_util.dump(conf)
|
conf_str = yaml_util.dump(conf)
|
||||||
conf_str = conf_str.replace("//", "")
|
conf_str = conf_str.replace("//", "")
|
||||||
|
# remove tailing \ to avoid multi-line comment warning
|
||||||
|
conf_str = conf_str.replace("\\\n", "\n")
|
||||||
cg.add(cg.LineComment(indent(conf_str)))
|
cg.add(cg.LineComment(indent(conf_str)))
|
||||||
await coro(conf)
|
await coro(conf)
|
||||||
|
|
||||||
@@ -180,16 +187,37 @@ def compile_program(args, config):
|
|||||||
from esphome import platformio_api
|
from esphome import platformio_api
|
||||||
|
|
||||||
_LOGGER.info("Compiling app...")
|
_LOGGER.info("Compiling app...")
|
||||||
return platformio_api.run_compile(config, CORE.verbose)
|
rc = platformio_api.run_compile(config, CORE.verbose)
|
||||||
|
if rc != 0:
|
||||||
|
return rc
|
||||||
|
idedata = platformio_api.get_idedata(config)
|
||||||
|
return 0 if idedata is not None else 1
|
||||||
|
|
||||||
|
|
||||||
def upload_using_esptool(config, port):
|
def upload_using_esptool(config, port):
|
||||||
path = CORE.firmware_bin
|
from esphome import platformio_api
|
||||||
|
|
||||||
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
|
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
|
||||||
"upload_speed", 460800
|
"upload_speed", 460800
|
||||||
)
|
)
|
||||||
|
|
||||||
def run_esptool(baud_rate):
|
def run_esptool(baud_rate):
|
||||||
|
idedata = platformio_api.get_idedata(config)
|
||||||
|
|
||||||
|
firmware_offset = "0x10000" if CORE.is_esp32 else "0x0"
|
||||||
|
flash_images = [
|
||||||
|
platformio_api.FlashImage(
|
||||||
|
path=idedata.firmware_bin_path, offset=firmware_offset
|
||||||
|
),
|
||||||
|
*idedata.extra_flash_images,
|
||||||
|
]
|
||||||
|
|
||||||
|
mcu = "esp8266"
|
||||||
|
if CORE.is_esp32:
|
||||||
|
from esphome.components.esp32 import get_esp32_variant
|
||||||
|
|
||||||
|
mcu = get_esp32_variant().lower()
|
||||||
|
|
||||||
cmd = [
|
cmd = [
|
||||||
"esptool.py",
|
"esptool.py",
|
||||||
"--before",
|
"--before",
|
||||||
@@ -198,14 +226,17 @@ def upload_using_esptool(config, port):
|
|||||||
"hard_reset",
|
"hard_reset",
|
||||||
"--baud",
|
"--baud",
|
||||||
str(baud_rate),
|
str(baud_rate),
|
||||||
"--chip",
|
|
||||||
"esp8266",
|
|
||||||
"--port",
|
"--port",
|
||||||
port,
|
port,
|
||||||
|
"--chip",
|
||||||
|
mcu,
|
||||||
"write_flash",
|
"write_flash",
|
||||||
"0x0",
|
"-z",
|
||||||
path,
|
"--flash_size",
|
||||||
|
"detect",
|
||||||
]
|
]
|
||||||
|
for img in flash_images:
|
||||||
|
cmd += [img.offset, img.path]
|
||||||
|
|
||||||
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
||||||
import esptool
|
import esptool
|
||||||
@@ -229,11 +260,7 @@ def upload_using_esptool(config, port):
|
|||||||
def upload_program(config, args, host):
|
def upload_program(config, args, host):
|
||||||
# if upload is to a serial port use platformio, otherwise assume ota
|
# if upload is to a serial port use platformio, otherwise assume ota
|
||||||
if get_port_type(host) == "SERIAL":
|
if get_port_type(host) == "SERIAL":
|
||||||
from esphome import platformio_api
|
|
||||||
|
|
||||||
if CORE.is_esp8266:
|
|
||||||
return upload_using_esptool(config, host)
|
return upload_using_esptool(config, host)
|
||||||
return platformio_api.run_upload(config, CORE.verbose, host)
|
|
||||||
|
|
||||||
from esphome import espota2
|
from esphome import espota2
|
||||||
|
|
||||||
@@ -245,7 +272,7 @@ def upload_program(config, args, host):
|
|||||||
|
|
||||||
ota_conf = config[CONF_OTA]
|
ota_conf = config[CONF_OTA]
|
||||||
remote_port = ota_conf[CONF_PORT]
|
remote_port = ota_conf[CONF_PORT]
|
||||||
password = ota_conf[CONF_PASSWORD]
|
password = ota_conf.get(CONF_PASSWORD, "")
|
||||||
return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
|
return espota2.run_ota(host, remote_port, password, CORE.firmware_bin)
|
||||||
|
|
||||||
|
|
||||||
@@ -415,34 +442,141 @@ def command_update_all(args):
|
|||||||
click.echo(f"{half_line}{middle_text}{half_line}")
|
click.echo(f"{half_line}{middle_text}{half_line}")
|
||||||
|
|
||||||
for f in files:
|
for f in files:
|
||||||
print("Updating {}".format(color(Fore.CYAN, f)))
|
print(f"Updating {color(Fore.CYAN, f)}")
|
||||||
print("-" * twidth)
|
print("-" * twidth)
|
||||||
print()
|
print()
|
||||||
rc = run_external_process(
|
rc = run_external_process(
|
||||||
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
|
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
|
||||||
)
|
)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
|
print_bar(f"[{color(Fore.BOLD_GREEN, 'SUCCESS')}] {f}")
|
||||||
success[f] = True
|
success[f] = True
|
||||||
else:
|
else:
|
||||||
print_bar("[{}] {}".format(color(Fore.BOLD_RED, "ERROR"), f))
|
print_bar(f"[{color(Fore.BOLD_RED, 'ERROR')}] {f}")
|
||||||
success[f] = False
|
success[f] = False
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print()
|
print()
|
||||||
print()
|
print()
|
||||||
|
|
||||||
print_bar("[{}]".format(color(Fore.BOLD_WHITE, "SUMMARY")))
|
print_bar(f"[{color(Fore.BOLD_WHITE, 'SUMMARY')}]")
|
||||||
failed = 0
|
failed = 0
|
||||||
for f in files:
|
for f in files:
|
||||||
if success[f]:
|
if success[f]:
|
||||||
print(" - {}: {}".format(f, color(Fore.GREEN, "SUCCESS")))
|
print(f" - {f}: {color(Fore.GREEN, 'SUCCESS')}")
|
||||||
else:
|
else:
|
||||||
print(" - {}: {}".format(f, color(Fore.BOLD_RED, "FAILED")))
|
print(f" - {f}: {color(Fore.BOLD_RED, 'FAILED')}")
|
||||||
failed += 1
|
failed += 1
|
||||||
return failed
|
return failed
|
||||||
|
|
||||||
|
|
||||||
|
def command_idedata(args, config):
|
||||||
|
from esphome import platformio_api
|
||||||
|
import json
|
||||||
|
|
||||||
|
logging.disable(logging.INFO)
|
||||||
|
logging.disable(logging.WARNING)
|
||||||
|
|
||||||
|
idedata = platformio_api.get_idedata(config)
|
||||||
|
if idedata is None:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print(json.dumps(idedata.raw, indent=2) + "\n")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def command_rename(args, config):
|
||||||
|
for c in args.name:
|
||||||
|
if c not in ALLOWED_NAME_CHARS:
|
||||||
|
print(
|
||||||
|
color(
|
||||||
|
Fore.BOLD_RED,
|
||||||
|
f"'{c}' is an invalid character for names. Valid characters are: "
|
||||||
|
f"{ALLOWED_NAME_CHARS} (lowercase, no spaces)",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
# Load existing yaml file
|
||||||
|
with open(CORE.config_path, mode="r+", encoding="utf-8") as raw_file:
|
||||||
|
raw_contents = raw_file.read()
|
||||||
|
|
||||||
|
yaml = yaml_util.load_yaml(CORE.config_path)
|
||||||
|
if CONF_ESPHOME not in yaml or CONF_NAME not in yaml[CONF_ESPHOME]:
|
||||||
|
print(
|
||||||
|
color(Fore.BOLD_RED, "Complex YAML files cannot be automatically renamed.")
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
old_name = yaml[CONF_ESPHOME][CONF_NAME]
|
||||||
|
match = re.match(r"^\$\{?([a-zA-Z0-9_]+)\}?$", old_name)
|
||||||
|
if match is None:
|
||||||
|
new_raw = re.sub(
|
||||||
|
rf"name:\s+[\"']?{old_name}[\"']?",
|
||||||
|
f'name: "{args.name}"',
|
||||||
|
raw_contents,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
old_name = yaml[CONF_SUBSTITUTIONS][match.group(1)]
|
||||||
|
if (
|
||||||
|
len(
|
||||||
|
re.findall(
|
||||||
|
rf"^\s+{match.group(1)}:\s+[\"']?{old_name}[\"']?",
|
||||||
|
raw_contents,
|
||||||
|
flags=re.MULTILINE,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
> 1
|
||||||
|
):
|
||||||
|
print(color(Fore.BOLD_RED, "Too many matches in YAML to safely rename"))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
new_raw = re.sub(
|
||||||
|
rf"^(\s+{match.group(1)}):\s+[\"']?{old_name}[\"']?",
|
||||||
|
f'\\1: "{args.name}"',
|
||||||
|
raw_contents,
|
||||||
|
flags=re.MULTILINE,
|
||||||
|
)
|
||||||
|
|
||||||
|
new_path = os.path.join(CORE.config_dir, args.name + ".yaml")
|
||||||
|
print(
|
||||||
|
f"Updating {color(Fore.CYAN, CORE.config_path)} to {color(Fore.CYAN, new_path)}"
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
|
||||||
|
with open(new_path, mode="w", encoding="utf-8") as new_file:
|
||||||
|
new_file.write(new_raw)
|
||||||
|
|
||||||
|
rc = run_external_process("esphome", "config", new_path)
|
||||||
|
if rc != 0:
|
||||||
|
print(color(Fore.BOLD_RED, "Rename failed. Reverting changes."))
|
||||||
|
os.remove(new_path)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
cli_args = [
|
||||||
|
"run",
|
||||||
|
new_path,
|
||||||
|
"--no-logs",
|
||||||
|
"--device",
|
||||||
|
CORE.address,
|
||||||
|
]
|
||||||
|
|
||||||
|
if args.dashboard:
|
||||||
|
cli_args.insert(0, "--dashboard")
|
||||||
|
|
||||||
|
try:
|
||||||
|
rc = run_external_process("esphome", *cli_args)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
rc = 1
|
||||||
|
if rc != 0:
|
||||||
|
os.remove(new_path)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
os.remove(CORE.config_path)
|
||||||
|
|
||||||
|
print(color(Fore.BOLD_GREEN, "SUCCESS"))
|
||||||
|
print()
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
PRE_CONFIG_ACTIONS = {
|
PRE_CONFIG_ACTIONS = {
|
||||||
"wizard": command_wizard,
|
"wizard": command_wizard,
|
||||||
"version": command_version,
|
"version": command_version,
|
||||||
@@ -460,6 +594,8 @@ POST_CONFIG_ACTIONS = {
|
|||||||
"clean-mqtt": command_clean_mqtt,
|
"clean-mqtt": command_clean_mqtt,
|
||||||
"mqtt-fingerprint": command_mqtt_fingerprint,
|
"mqtt-fingerprint": command_mqtt_fingerprint,
|
||||||
"clean": command_clean,
|
"clean": command_clean,
|
||||||
|
"idedata": command_idedata,
|
||||||
|
"rename": command_rename,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -570,10 +706,7 @@ def parse_args(argv):
|
|||||||
"wizard",
|
"wizard",
|
||||||
help="A helpful setup wizard that will guide you through setting up ESPHome.",
|
help="A helpful setup wizard that will guide you through setting up ESPHome.",
|
||||||
)
|
)
|
||||||
parser_wizard.add_argument(
|
parser_wizard.add_argument("configuration", help="Your YAML configuration file.")
|
||||||
"configuration",
|
|
||||||
help="Your YAML configuration file.",
|
|
||||||
)
|
|
||||||
|
|
||||||
parser_fingerprint = subparsers.add_parser(
|
parser_fingerprint = subparsers.add_parser(
|
||||||
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
|
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
|
||||||
@@ -595,8 +728,7 @@ def parse_args(argv):
|
|||||||
"dashboard", help="Create a simple web server for a dashboard."
|
"dashboard", help="Create a simple web server for a dashboard."
|
||||||
)
|
)
|
||||||
parser_dashboard.add_argument(
|
parser_dashboard.add_argument(
|
||||||
"configuration",
|
"configuration", help="Your YAML configuration file directory."
|
||||||
help="Your YAML configuration file directory.",
|
|
||||||
)
|
)
|
||||||
parser_dashboard.add_argument(
|
parser_dashboard.add_argument(
|
||||||
"--port",
|
"--port",
|
||||||
@@ -604,6 +736,12 @@ def parse_args(argv):
|
|||||||
type=int,
|
type=int,
|
||||||
default=6052,
|
default=6052,
|
||||||
)
|
)
|
||||||
|
parser_dashboard.add_argument(
|
||||||
|
"--address",
|
||||||
|
help="The address to bind to.",
|
||||||
|
type=str,
|
||||||
|
default="0.0.0.0",
|
||||||
|
)
|
||||||
parser_dashboard.add_argument(
|
parser_dashboard.add_argument(
|
||||||
"--username",
|
"--username",
|
||||||
help="The optional username to require for authentication.",
|
help="The optional username to require for authentication.",
|
||||||
@@ -620,7 +758,7 @@ def parse_args(argv):
|
|||||||
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
|
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
|
||||||
)
|
)
|
||||||
parser_dashboard.add_argument(
|
parser_dashboard.add_argument(
|
||||||
"--hassio", help=argparse.SUPPRESS, action="store_true"
|
"--ha-addon", help=argparse.SUPPRESS, action="store_true"
|
||||||
)
|
)
|
||||||
parser_dashboard.add_argument(
|
parser_dashboard.add_argument(
|
||||||
"--socket", help="Make the dashboard serve under a unix socket", type=str
|
"--socket", help="Make the dashboard serve under a unix socket", type=str
|
||||||
@@ -635,6 +773,20 @@ def parse_args(argv):
|
|||||||
"configuration", help="Your YAML configuration file directories.", nargs="+"
|
"configuration", help="Your YAML configuration file directories.", nargs="+"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser_idedata = subparsers.add_parser("idedata")
|
||||||
|
parser_idedata.add_argument(
|
||||||
|
"configuration", help="Your YAML configuration file(s).", nargs=1
|
||||||
|
)
|
||||||
|
|
||||||
|
parser_rename = subparsers.add_parser(
|
||||||
|
"rename",
|
||||||
|
help="Rename a device in YAML, compile the binary and upload it.",
|
||||||
|
)
|
||||||
|
parser_rename.add_argument(
|
||||||
|
"configuration", help="Your YAML configuration file.", nargs=1
|
||||||
|
)
|
||||||
|
parser_rename.add_argument("name", help="The new name for the device.", type=str)
|
||||||
|
|
||||||
# Keep backward compatibility with the old command line format of
|
# Keep backward compatibility with the old command line format of
|
||||||
# esphome <config> <command>.
|
# esphome <config> <command>.
|
||||||
#
|
#
|
||||||
@@ -718,7 +870,12 @@ def run_esphome(argv):
|
|||||||
args = parse_args(argv)
|
args = parse_args(argv)
|
||||||
CORE.dashboard = args.dashboard
|
CORE.dashboard = args.dashboard
|
||||||
|
|
||||||
setup_log(args.verbose, args.quiet)
|
setup_log(
|
||||||
|
args.verbose,
|
||||||
|
args.quiet,
|
||||||
|
# Show timestamp for dashboard access logs
|
||||||
|
args.command == "dashboard",
|
||||||
|
)
|
||||||
if args.deprecated_argv_suggestion is not None and args.command != "vscode":
|
if args.deprecated_argv_suggestion is not None and args.command != "vscode":
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
"Calling ESPHome with the configuration before the command is deprecated "
|
"Calling ESPHome with the configuration before the command is deprecated "
|
||||||
@@ -727,10 +884,10 @@ def run_esphome(argv):
|
|||||||
_LOGGER.warning("Please instead use:")
|
_LOGGER.warning("Please instead use:")
|
||||||
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion))
|
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion))
|
||||||
|
|
||||||
if sys.version_info < (3, 7, 0):
|
if sys.version_info < (3, 8, 0):
|
||||||
_LOGGER.error(
|
_LOGGER.error(
|
||||||
"You're running ESPHome with Python <3.7. ESPHome is no longer compatible "
|
"You're running ESPHome with Python <3.8. ESPHome is no longer compatible "
|
||||||
"with this Python version. Please reinstall ESPHome with Python 3.7+"
|
"with this Python version. Please reinstall ESPHome with Python 3.8+"
|
||||||
)
|
)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
@@ -742,12 +899,16 @@ def run_esphome(argv):
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
for conf_path in args.configuration:
|
for conf_path in args.configuration:
|
||||||
|
if any(os.path.basename(conf_path) == x for x in SECRETS_FILES):
|
||||||
|
_LOGGER.warning("Skipping secrets file %s", conf_path)
|
||||||
|
continue
|
||||||
|
|
||||||
CORE.config_path = conf_path
|
CORE.config_path = conf_path
|
||||||
CORE.dashboard = args.dashboard
|
CORE.dashboard = args.dashboard
|
||||||
|
|
||||||
config = read_config(dict(args.substitution) if args.substitution else {})
|
config = read_config(dict(args.substitution) if args.substitution else {})
|
||||||
if config is None:
|
if config is None:
|
||||||
return 1
|
return 2
|
||||||
CORE.config = config
|
CORE.config = config
|
||||||
|
|
||||||
if args.command not in POST_CONFIG_ACTIONS:
|
if args.command not in POST_CONFIG_ACTIONS:
|
||||||
|
|||||||
@@ -3,14 +3,16 @@ import esphome.config_validation as cv
|
|||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_AUTOMATION_ID,
|
CONF_AUTOMATION_ID,
|
||||||
CONF_CONDITION,
|
CONF_CONDITION,
|
||||||
|
CONF_COUNT,
|
||||||
CONF_ELSE,
|
CONF_ELSE,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_THEN,
|
CONF_THEN,
|
||||||
|
CONF_TIMEOUT,
|
||||||
CONF_TRIGGER_ID,
|
CONF_TRIGGER_ID,
|
||||||
CONF_TYPE_ID,
|
CONF_TYPE_ID,
|
||||||
CONF_TIME,
|
CONF_TIME,
|
||||||
)
|
)
|
||||||
from esphome.jsonschema import jschema_extractor
|
from esphome.schema_extractors import SCHEMA_EXTRACT, schema_extractor
|
||||||
from esphome.util import Registry
|
from esphome.util import Registry
|
||||||
|
|
||||||
|
|
||||||
@@ -21,11 +23,10 @@ def maybe_simple_id(*validators):
|
|||||||
def maybe_conf(conf, *validators):
|
def maybe_conf(conf, *validators):
|
||||||
validator = cv.All(*validators)
|
validator = cv.All(*validators)
|
||||||
|
|
||||||
@jschema_extractor("maybe")
|
@schema_extractor("maybe")
|
||||||
def validate(value):
|
def validate(value):
|
||||||
# pylint: disable=comparison-with-callable
|
if value == SCHEMA_EXTRACT:
|
||||||
if value == jschema_extractor:
|
return (validator, conf)
|
||||||
return validator
|
|
||||||
|
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
return validator(value)
|
return validator(value)
|
||||||
@@ -65,6 +66,7 @@ DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
|
|||||||
LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
|
LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
|
||||||
IfAction = cg.esphome_ns.class_("IfAction", Action)
|
IfAction = cg.esphome_ns.class_("IfAction", Action)
|
||||||
WhileAction = cg.esphome_ns.class_("WhileAction", Action)
|
WhileAction = cg.esphome_ns.class_("WhileAction", Action)
|
||||||
|
RepeatAction = cg.esphome_ns.class_("RepeatAction", Action)
|
||||||
WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
|
WaitUntilAction = cg.esphome_ns.class_("WaitUntilAction", Action, cg.Component)
|
||||||
UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
|
UpdateComponentAction = cg.esphome_ns.class_("UpdateComponentAction", Action)
|
||||||
Automation = cg.esphome_ns.class_("Automation")
|
Automation = cg.esphome_ns.class_("Automation")
|
||||||
@@ -108,11 +110,9 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
|||||||
# This should only happen with invalid configs, but let's have a nice error message.
|
# This should only happen with invalid configs, but let's have a nice error message.
|
||||||
return [schema(value)]
|
return [schema(value)]
|
||||||
|
|
||||||
@jschema_extractor("automation")
|
@schema_extractor("automation")
|
||||||
def validator(value):
|
def validator(value):
|
||||||
# hack to get the schema
|
if value == SCHEMA_EXTRACT:
|
||||||
# pylint: disable=comparison-with-callable
|
|
||||||
if value == jschema_extractor:
|
|
||||||
return schema
|
return schema
|
||||||
|
|
||||||
value = validator_(value)
|
value = validator_(value)
|
||||||
@@ -240,21 +240,41 @@ async def while_action_to_code(config, action_id, template_arg, args):
|
|||||||
return var
|
return var
|
||||||
|
|
||||||
|
|
||||||
def validate_wait_until(value):
|
@register_action(
|
||||||
schema = cv.Schema(
|
"repeat",
|
||||||
|
RepeatAction,
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_COUNT): cv.templatable(cv.positive_not_null_int),
|
||||||
|
cv.Required(CONF_THEN): validate_action_list,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def repeat_action_to_code(config, action_id, template_arg, args):
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
|
count_template = await cg.templatable(config[CONF_COUNT], args, cg.uint32)
|
||||||
|
cg.add(var.set_count(count_template))
|
||||||
|
actions = await build_action_list(config[CONF_THEN], template_arg, args)
|
||||||
|
cg.add(var.add_then(actions))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
_validate_wait_until = cv.maybe_simple_value(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||||
}
|
cv.Optional(CONF_TIMEOUT): cv.templatable(cv.positive_time_period_milliseconds),
|
||||||
)
|
},
|
||||||
if isinstance(value, dict) and CONF_CONDITION in value:
|
key=CONF_CONDITION,
|
||||||
return schema(value)
|
)
|
||||||
return validate_wait_until({CONF_CONDITION: value})
|
|
||||||
|
|
||||||
|
|
||||||
@register_action("wait_until", WaitUntilAction, validate_wait_until)
|
@register_action("wait_until", WaitUntilAction, _validate_wait_until)
|
||||||
async def wait_until_action_to_code(config, action_id, template_arg, args):
|
async def wait_until_action_to_code(config, action_id, template_arg, args):
|
||||||
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
||||||
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
||||||
|
if CONF_TIMEOUT in config:
|
||||||
|
template_ = await cg.templatable(config[CONF_TIMEOUT], args, cg.uint32)
|
||||||
|
cg.add(var.set_timeout_value(template_))
|
||||||
await cg.register_component(var, {})
|
await cg.register_component(var, {})
|
||||||
return var
|
return var
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ from esphome.cpp_generator import ( # noqa
|
|||||||
add_library,
|
add_library,
|
||||||
add_build_flag,
|
add_build_flag,
|
||||||
add_define,
|
add_define,
|
||||||
|
add_platformio_option,
|
||||||
get_variable,
|
get_variable,
|
||||||
get_variable_with_full_id,
|
get_variable_with_full_id,
|
||||||
process_lambda,
|
process_lambda,
|
||||||
@@ -62,11 +63,13 @@ from esphome.cpp_types import ( # noqa
|
|||||||
uint32,
|
uint32,
|
||||||
uint64,
|
uint64,
|
||||||
int32,
|
int32,
|
||||||
|
int64,
|
||||||
|
size_t,
|
||||||
const_char_ptr,
|
const_char_ptr,
|
||||||
NAN,
|
NAN,
|
||||||
esphome_ns,
|
esphome_ns,
|
||||||
App,
|
App,
|
||||||
Nameable,
|
EntityBase,
|
||||||
Component,
|
Component,
|
||||||
ComponentPtr,
|
ComponentPtr,
|
||||||
PollingComponent,
|
PollingComponent,
|
||||||
@@ -74,8 +77,11 @@ from esphome.cpp_types import ( # noqa
|
|||||||
optional,
|
optional,
|
||||||
arduino_json_ns,
|
arduino_json_ns,
|
||||||
JsonObject,
|
JsonObject,
|
||||||
JsonObjectRef,
|
JsonObjectConst,
|
||||||
JsonObjectConstRef,
|
|
||||||
Controller,
|
Controller,
|
||||||
GPIOPin,
|
GPIOPin,
|
||||||
|
InternalGPIOPin,
|
||||||
|
gpio_Flags,
|
||||||
|
EntityCategory,
|
||||||
|
Parented,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/esphal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/components/stepper/stepper.h"
|
#include "esphome/components/stepper/stepper.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
|
#ifdef USE_ARDUINO
|
||||||
|
|
||||||
#include "ac_dimmer.h"
|
#include "ac_dimmer.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#include <core_esp8266_waveform.h>
|
#include <core_esp8266_waveform.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_ESP32_FRAMEWORK_ARDUINO
|
||||||
|
#include <esp32-hal-timer.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ac_dimmer {
|
namespace ac_dimmer {
|
||||||
@@ -17,12 +23,15 @@ static AcDimmerDataStore *all_dimmers[32]; // NOLINT(cppcoreguidelines-avoid-no
|
|||||||
/// Time in microseconds the gate should be held high
|
/// Time in microseconds the gate should be held high
|
||||||
/// 10µs should be long enough for most triacs
|
/// 10µs should be long enough for most triacs
|
||||||
/// For reference: BT136 datasheet says 2µs nominal (page 7)
|
/// For reference: BT136 datasheet says 2µs nominal (page 7)
|
||||||
static const uint32_t GATE_ENABLE_TIME = 10;
|
/// However other factors like gate driver propagation time
|
||||||
|
/// are also considered and a really low value is not important
|
||||||
|
/// See also: https://github.com/esphome/issues/issues/1632
|
||||||
|
static const uint32_t GATE_ENABLE_TIME = 50;
|
||||||
|
|
||||||
/// Function called from timer interrupt
|
/// Function called from timer interrupt
|
||||||
/// Input is current time in microseconds (micros())
|
/// Input is current time in microseconds (micros())
|
||||||
/// Returns when next "event" is expected in µs, or 0 if no such event known.
|
/// Returns when next "event" is expected in µs, or 0 if no such event known.
|
||||||
uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
uint32_t IRAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
||||||
// If no ZC signal received yet.
|
// If no ZC signal received yet.
|
||||||
if (this->crossed_zero_at == 0)
|
if (this->crossed_zero_at == 0)
|
||||||
return 0;
|
return 0;
|
||||||
@@ -34,19 +43,19 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
|||||||
|
|
||||||
if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) {
|
if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) {
|
||||||
this->enable_time_us = 0;
|
this->enable_time_us = 0;
|
||||||
this->gate_pin->digital_write(true);
|
this->gate_pin.digital_write(true);
|
||||||
// Prevent too short pulses
|
// Prevent too short pulses
|
||||||
this->disable_time_us = max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
|
this->disable_time_us = std::max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
|
||||||
}
|
}
|
||||||
if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) {
|
if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) {
|
||||||
this->disable_time_us = 0;
|
this->disable_time_us = 0;
|
||||||
this->gate_pin->digital_write(false);
|
this->gate_pin.digital_write(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (time_since_zc < this->enable_time_us)
|
if (time_since_zc < this->enable_time_us) {
|
||||||
// Next event is enable, return time until that event
|
// Next event is enable, return time until that event
|
||||||
return this->enable_time_us - time_since_zc;
|
return this->enable_time_us - time_since_zc;
|
||||||
else if (time_since_zc < disable_time_us) {
|
} else if (time_since_zc < disable_time_us) {
|
||||||
// Next event is disable, return time until that event
|
// Next event is disable, return time until that event
|
||||||
return this->disable_time_us - time_since_zc;
|
return this->disable_time_us - time_since_zc;
|
||||||
}
|
}
|
||||||
@@ -60,14 +69,15 @@ uint32_t ICACHE_RAM_ATTR HOT AcDimmerDataStore::timer_intr(uint32_t now) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Run timer interrupt code and return in how many µs the next event is expected
|
/// Run timer interrupt code and return in how many µs the next event is expected
|
||||||
uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
|
uint32_t IRAM_ATTR HOT timer_interrupt() {
|
||||||
// run at least with 1kHz
|
// run at least with 1kHz
|
||||||
uint32_t min_dt_us = 1000;
|
uint32_t min_dt_us = 1000;
|
||||||
uint32_t now = micros();
|
uint32_t now = micros();
|
||||||
for (auto *dimmer : all_dimmers) {
|
for (auto *dimmer : all_dimmers) {
|
||||||
if (dimmer == nullptr)
|
if (dimmer == nullptr) {
|
||||||
// no more dimmers
|
// no more dimmers
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
uint32_t res = dimmer->timer_intr(now);
|
uint32_t res = dimmer->timer_intr(now);
|
||||||
if (res != 0 && res < min_dt_us)
|
if (res != 0 && res < min_dt_us)
|
||||||
min_dt_us = res;
|
min_dt_us = res;
|
||||||
@@ -77,7 +87,7 @@ uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// GPIO interrupt routine, called when ZC pin triggers
|
/// GPIO interrupt routine, called when ZC pin triggers
|
||||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
||||||
uint32_t prev_crossed = this->crossed_zero_at;
|
uint32_t prev_crossed = this->crossed_zero_at;
|
||||||
|
|
||||||
// 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms
|
// 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms
|
||||||
@@ -94,7 +104,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
|||||||
|
|
||||||
if (this->value == 65535) {
|
if (this->value == 65535) {
|
||||||
// fully on, enable output immediately
|
// fully on, enable output immediately
|
||||||
this->gate_pin->digital_write(true);
|
this->gate_pin.digital_write(true);
|
||||||
} else if (this->init_cycle) {
|
} else if (this->init_cycle) {
|
||||||
// send a full cycle
|
// send a full cycle
|
||||||
this->init_cycle = false;
|
this->init_cycle = false;
|
||||||
@@ -102,29 +112,33 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
|||||||
this->disable_time_us = cycle_time_us;
|
this->disable_time_us = cycle_time_us;
|
||||||
} else if (this->value == 0) {
|
} else if (this->value == 0) {
|
||||||
// fully off, disable output immediately
|
// fully off, disable output immediately
|
||||||
this->gate_pin->digital_write(false);
|
this->gate_pin.digital_write(false);
|
||||||
} else {
|
} else {
|
||||||
if (this->method == DIM_METHOD_TRAILING) {
|
if (this->method == DIM_METHOD_TRAILING) {
|
||||||
this->enable_time_us = 1; // cannot be 0
|
this->enable_time_us = 1; // cannot be 0
|
||||||
this->disable_time_us = max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
|
this->disable_time_us = std::max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
|
||||||
} else {
|
} else {
|
||||||
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
|
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
|
||||||
// also take into account min_power
|
// also take into account min_power
|
||||||
auto min_us = this->cycle_time_us * this->min_power / 1000;
|
auto min_us = this->cycle_time_us * this->min_power / 1000;
|
||||||
this->enable_time_us = max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
|
// calculate required value to provide a true RMS voltage output
|
||||||
|
this->enable_time_us =
|
||||||
|
std::max((uint32_t) 1, (uint32_t)((65535 - (acos(1 - (2 * this->value / 65535.0)) / 3.14159 * 65535)) *
|
||||||
|
(this->cycle_time_us - min_us)) /
|
||||||
|
65535);
|
||||||
if (this->method == DIM_METHOD_LEADING_PULSE) {
|
if (this->method == DIM_METHOD_LEADING_PULSE) {
|
||||||
// Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone
|
// Minimum pulse time should be enough for the triac to trigger when it is close to the ZC zone
|
||||||
// this is for brightness near 99%
|
// this is for brightness near 99%
|
||||||
this->disable_time_us = max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
|
this->disable_time_us = std::max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
|
||||||
} else {
|
} else {
|
||||||
this->gate_pin->digital_write(false);
|
this->gate_pin.digital_write(false);
|
||||||
this->disable_time_us = this->cycle_time_us;
|
this->disable_time_us = this->cycle_time_us;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
|
void IRAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
|
||||||
// Attaching pin interrupts on the same pin will override the previous interrupt
|
// Attaching pin interrupts on the same pin will override the previous interrupt
|
||||||
// However, the user expects that multiple dimmers sharing the same ZC pin will work.
|
// However, the user expects that multiple dimmers sharing the same ZC pin will work.
|
||||||
// We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
|
// We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
|
||||||
@@ -138,11 +152,11 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
// ESP32 implementation, uses basically the same code but needs to wrap
|
// ESP32 implementation, uses basically the same code but needs to wrap
|
||||||
// timer_interrupt() function to auto-reschedule
|
// timer_interrupt() function to auto-reschedule
|
||||||
static hw_timer_t *dimmer_timer = nullptr;
|
static hw_timer_t *dimmer_timer = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
|
void IRAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void AcDimmer::setup() {
|
void AcDimmer::setup() {
|
||||||
@@ -171,15 +185,16 @@ void AcDimmer::setup() {
|
|||||||
if (setup_zero_cross_pin) {
|
if (setup_zero_cross_pin) {
|
||||||
this->zero_cross_pin_->setup();
|
this->zero_cross_pin_->setup();
|
||||||
this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr();
|
this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr();
|
||||||
this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, FALLING);
|
this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_,
|
||||||
|
gpio::INTERRUPT_FALLING_EDGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef USE_ESP8266
|
||||||
// Uses ESP8266 waveform (soft PWM) class
|
// Uses ESP8266 waveform (soft PWM) class
|
||||||
// PWM and AcDimmer can even run at the same time this way
|
// PWM and AcDimmer can even run at the same time this way
|
||||||
setTimer1Callback(&timer_interrupt);
|
setTimer1Callback(&timer_interrupt);
|
||||||
#endif
|
#endif
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
// 80 Divider -> 1 count=1µs
|
// 80 Divider -> 1 count=1µs
|
||||||
dimmer_timer = timerBegin(0, 80, true);
|
dimmer_timer = timerBegin(0, 80, true);
|
||||||
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
|
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
|
||||||
@@ -202,12 +217,13 @@ void AcDimmer::dump_config() {
|
|||||||
LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_);
|
LOG_PIN(" Zero-Cross Pin: ", this->zero_cross_pin_);
|
||||||
ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->store_.min_power / 10.0f);
|
ESP_LOGCONFIG(TAG, " Min Power: %.1f%%", this->store_.min_power / 10.0f);
|
||||||
ESP_LOGCONFIG(TAG, " Init with half cycle: %s", YESNO(this->init_with_half_cycle_));
|
ESP_LOGCONFIG(TAG, " Init with half cycle: %s", YESNO(this->init_with_half_cycle_));
|
||||||
if (method_ == DIM_METHOD_LEADING_PULSE)
|
if (method_ == DIM_METHOD_LEADING_PULSE) {
|
||||||
ESP_LOGCONFIG(TAG, " Method: leading pulse");
|
ESP_LOGCONFIG(TAG, " Method: leading pulse");
|
||||||
else if (method_ == DIM_METHOD_LEADING)
|
} else if (method_ == DIM_METHOD_LEADING) {
|
||||||
ESP_LOGCONFIG(TAG, " Method: leading");
|
ESP_LOGCONFIG(TAG, " Method: leading");
|
||||||
else
|
} else {
|
||||||
ESP_LOGCONFIG(TAG, " Method: trailing");
|
ESP_LOGCONFIG(TAG, " Method: trailing");
|
||||||
|
}
|
||||||
|
|
||||||
LOG_FLOAT_OUTPUT(this);
|
LOG_FLOAT_OUTPUT(this);
|
||||||
ESP_LOGV(TAG, " Estimated Frequency: %.3fHz", 1e6f / this->store_.cycle_time_us / 2);
|
ESP_LOGV(TAG, " Estimated Frequency: %.3fHz", 1e6f / this->store_.cycle_time_us / 2);
|
||||||
@@ -215,3 +231,5 @@ void AcDimmer::dump_config() {
|
|||||||
|
|
||||||
} // namespace ac_dimmer
|
} // namespace ac_dimmer
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ARDUINO
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef USE_ARDUINO
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/esphal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/components/output/float_output.h"
|
#include "esphome/components/output/float_output.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
@@ -11,11 +13,11 @@ enum DimMethod { DIM_METHOD_LEADING_PULSE = 0, DIM_METHOD_LEADING, DIM_METHOD_TR
|
|||||||
|
|
||||||
struct AcDimmerDataStore {
|
struct AcDimmerDataStore {
|
||||||
/// Zero-cross pin
|
/// Zero-cross pin
|
||||||
ISRInternalGPIOPin *zero_cross_pin;
|
ISRInternalGPIOPin zero_cross_pin;
|
||||||
/// Zero-cross pin number - used to share ZC pin across multiple dimmers
|
/// Zero-cross pin number - used to share ZC pin across multiple dimmers
|
||||||
uint8_t zero_cross_pin_number;
|
uint8_t zero_cross_pin_number;
|
||||||
/// Output pin to write to
|
/// Output pin to write to
|
||||||
ISRInternalGPIOPin *gate_pin;
|
ISRInternalGPIOPin gate_pin;
|
||||||
/// Value of the dimmer - 0 to 65535.
|
/// Value of the dimmer - 0 to 65535.
|
||||||
uint16_t value;
|
uint16_t value;
|
||||||
/// Minimum power for activation
|
/// Minimum power for activation
|
||||||
@@ -37,7 +39,7 @@ struct AcDimmerDataStore {
|
|||||||
|
|
||||||
void gpio_intr();
|
void gpio_intr();
|
||||||
static void s_gpio_intr(AcDimmerDataStore *store);
|
static void s_gpio_intr(AcDimmerDataStore *store);
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
static void s_timer_intr();
|
static void s_timer_intr();
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
@@ -47,16 +49,16 @@ class AcDimmer : public output::FloatOutput, public Component {
|
|||||||
void setup() override;
|
void setup() override;
|
||||||
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void set_gate_pin(GPIOPin *gate_pin) { gate_pin_ = gate_pin; }
|
void set_gate_pin(InternalGPIOPin *gate_pin) { gate_pin_ = gate_pin; }
|
||||||
void set_zero_cross_pin(GPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; }
|
void set_zero_cross_pin(InternalGPIOPin *zero_cross_pin) { zero_cross_pin_ = zero_cross_pin; }
|
||||||
void set_init_with_half_cycle(bool init_with_half_cycle) { init_with_half_cycle_ = init_with_half_cycle; }
|
void set_init_with_half_cycle(bool init_with_half_cycle) { init_with_half_cycle_ = init_with_half_cycle; }
|
||||||
void set_method(DimMethod method) { method_ = method; }
|
void set_method(DimMethod method) { method_ = method; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void write_state(float state) override;
|
void write_state(float state) override;
|
||||||
|
|
||||||
GPIOPin *gate_pin_;
|
InternalGPIOPin *gate_pin_;
|
||||||
GPIOPin *zero_cross_pin_;
|
InternalGPIOPin *zero_cross_pin_;
|
||||||
AcDimmerDataStore store_;
|
AcDimmerDataStore store_;
|
||||||
bool init_with_half_cycle_;
|
bool init_with_half_cycle_;
|
||||||
DimMethod method_;
|
DimMethod method_;
|
||||||
@@ -64,3 +66,5 @@ class AcDimmer : public output::FloatOutput, public Component {
|
|||||||
|
|
||||||
} // namespace ac_dimmer
|
} // namespace ac_dimmer
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ARDUINO
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ DIM_METHODS = {
|
|||||||
CONF_GATE_PIN = "gate_pin"
|
CONF_GATE_PIN = "gate_pin"
|
||||||
CONF_ZERO_CROSS_PIN = "zero_cross_pin"
|
CONF_ZERO_CROSS_PIN = "zero_cross_pin"
|
||||||
CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle"
|
CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle"
|
||||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_ID): cv.declare_id(AcDimmer),
|
cv.Required(CONF_ID): cv.declare_id(AcDimmer),
|
||||||
cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema,
|
cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema,
|
||||||
@@ -29,7 +30,9 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
|||||||
DIM_METHODS, upper=True, space="_"
|
DIM_METHODS, upper=True, space="_"
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA)
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
|
cv.only_with_arduino,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ void AdalightLightEffect::stop() {
|
|||||||
AddressableLightEffect::stop();
|
AddressableLightEffect::stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
int AdalightLightEffect::get_frame_size_(int led_count) const {
|
unsigned int AdalightLightEffect::get_frame_size_(int led_count) const {
|
||||||
// 3 bytes: Ada
|
// 3 bytes: Ada
|
||||||
// 2 bytes: LED count
|
// 2 bytes: LED count
|
||||||
// 1 byte: checksum
|
// 1 byte: checksum
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
|
|||||||
public:
|
public:
|
||||||
AdalightLightEffect(const std::string &name);
|
AdalightLightEffect(const std::string &name);
|
||||||
|
|
||||||
public:
|
|
||||||
void start() override;
|
void start() override;
|
||||||
void stop() override;
|
void stop() override;
|
||||||
void apply(light::AddressableLight &it, const Color ¤t_color) override;
|
void apply(light::AddressableLight &it, const Color ¤t_color) override;
|
||||||
@@ -25,12 +24,11 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
|
|||||||
CONSUMED,
|
CONSUMED,
|
||||||
};
|
};
|
||||||
|
|
||||||
int get_frame_size_(int led_count) const;
|
unsigned int get_frame_size_(int led_count) const;
|
||||||
void reset_frame_(light::AddressableLight &it);
|
void reset_frame_(light::AddressableLight &it);
|
||||||
void blank_all_leds_(light::AddressableLight &it);
|
void blank_all_leds_(light::AddressableLight &it);
|
||||||
Frame parse_frame_(light::AddressableLight &it);
|
Frame parse_frame_(light::AddressableLight &it);
|
||||||
|
|
||||||
protected:
|
|
||||||
uint32_t last_ack_{0};
|
uint32_t last_ack_{0};
|
||||||
uint32_t last_byte_{0};
|
uint32_t last_byte_{0};
|
||||||
uint32_t last_reset_{0};
|
uint32_t last_reset_{0};
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
#include "adc_sensor.h"
|
#include "adc_sensor.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
|
||||||
|
#ifdef USE_ESP8266
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
#include <Esp.h>
|
||||||
ADC_MODE(ADC_VCC)
|
ADC_MODE(ADC_VCC)
|
||||||
|
#else
|
||||||
|
#include <Arduino.h>
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
@@ -10,151 +16,166 @@ namespace adc {
|
|||||||
|
|
||||||
static const char *const TAG = "adc";
|
static const char *const TAG = "adc";
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
// 13bit for S2, and 12bit for all other esp32 variants
|
||||||
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
|
#ifdef USE_ESP32
|
||||||
|
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
|
||||||
|
|
||||||
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
|
#ifndef SOC_ADC_RTC_MAX_BITWIDTH
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
#if USE_ESP32_VARIANT_ESP32S2
|
||||||
switch (pin) {
|
static const int SOC_ADC_RTC_MAX_BITWIDTH = 13;
|
||||||
case 36:
|
#else
|
||||||
return ADC1_CHANNEL_0;
|
static const int SOC_ADC_RTC_MAX_BITWIDTH = 12;
|
||||||
case 37:
|
|
||||||
return ADC1_CHANNEL_1;
|
|
||||||
case 38:
|
|
||||||
return ADC1_CHANNEL_2;
|
|
||||||
case 39:
|
|
||||||
return ADC1_CHANNEL_3;
|
|
||||||
case 32:
|
|
||||||
return ADC1_CHANNEL_4;
|
|
||||||
case 33:
|
|
||||||
return ADC1_CHANNEL_5;
|
|
||||||
case 34:
|
|
||||||
return ADC1_CHANNEL_6;
|
|
||||||
case 35:
|
|
||||||
return ADC1_CHANNEL_7;
|
|
||||||
default:
|
|
||||||
return ADC1_CHANNEL_MAX;
|
|
||||||
}
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
switch (pin) {
|
|
||||||
case 0:
|
|
||||||
return ADC1_CHANNEL_0;
|
|
||||||
case 1:
|
|
||||||
return ADC1_CHANNEL_1;
|
|
||||||
case 2:
|
|
||||||
return ADC1_CHANNEL_2;
|
|
||||||
case 3:
|
|
||||||
return ADC1_CHANNEL_3;
|
|
||||||
case 4:
|
|
||||||
return ADC1_CHANNEL_4;
|
|
||||||
default:
|
|
||||||
return ADC1_CHANNEL_MAX;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
#endif
|
||||||
|
|
||||||
|
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; // 4095 (12 bit) or 8191 (13 bit)
|
||||||
|
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; // 2048 (12 bit) or 4096 (13 bit)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void ADCSensor::setup() {
|
void ADCSensor::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
||||||
#ifndef USE_ADC_SENSOR_VCC
|
#ifndef USE_ADC_SENSOR_VCC
|
||||||
GPIOPin(this->pin_, INPUT).setup();
|
pin_->setup();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_);
|
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
|
||||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
if (!autorange_) {
|
||||||
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2
|
adc1_config_channel_atten(channel_, attenuation_);
|
||||||
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_));
|
}
|
||||||
#endif
|
|
||||||
#endif
|
// load characteristics for each attenuation
|
||||||
|
for (int i = 0; i < (int) ADC_ATTEN_MAX; i++) {
|
||||||
|
auto cal_value = esp_adc_cal_characterize(ADC_UNIT_1, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
|
||||||
|
1100, // default vref
|
||||||
|
&cal_characteristics_[i]);
|
||||||
|
switch (cal_value) {
|
||||||
|
case ESP_ADC_CAL_VAL_EFUSE_VREF:
|
||||||
|
ESP_LOGV(TAG, "Using eFuse Vref for calibration");
|
||||||
|
break;
|
||||||
|
case ESP_ADC_CAL_VAL_EFUSE_TP:
|
||||||
|
ESP_LOGV(TAG, "Using two-point eFuse Vref for calibration");
|
||||||
|
break;
|
||||||
|
case ESP_ADC_CAL_VAL_DEFAULT_VREF:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // USE_ESP32
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADCSensor::dump_config() {
|
void ADCSensor::dump_config() {
|
||||||
LOG_SENSOR("", "ADC Sensor", this);
|
LOG_SENSOR("", "ADC Sensor", this);
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef USE_ESP8266
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
||||||
#else
|
#else
|
||||||
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
|
LOG_PIN(" Pin: ", pin_);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif // USE_ESP8266
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
|
||||||
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
|
#ifdef USE_ESP32
|
||||||
|
LOG_PIN(" Pin: ", pin_);
|
||||||
|
if (autorange_) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Attenuation: auto");
|
||||||
|
} else {
|
||||||
switch (this->attenuation_) {
|
switch (this->attenuation_) {
|
||||||
case ADC_ATTEN_DB_0:
|
case ADC_ATTEN_DB_0:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
|
ESP_LOGCONFIG(TAG, " Attenuation: 0db");
|
||||||
break;
|
break;
|
||||||
case ADC_ATTEN_DB_2_5:
|
case ADC_ATTEN_DB_2_5:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
|
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db");
|
||||||
break;
|
break;
|
||||||
case ADC_ATTEN_DB_6:
|
case ADC_ATTEN_DB_6:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
|
ESP_LOGCONFIG(TAG, " Attenuation: 6db");
|
||||||
break;
|
break;
|
||||||
case ADC_ATTEN_DB_11:
|
case ADC_ATTEN_DB_11:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
|
ESP_LOGCONFIG(TAG, " Attenuation: 11db");
|
||||||
break;
|
break;
|
||||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
}
|
||||||
|
#endif // USE_ESP32
|
||||||
LOG_UPDATE_INTERVAL(this);
|
LOG_UPDATE_INTERVAL(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
|
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
void ADCSensor::update() {
|
void ADCSensor::update() {
|
||||||
float value_v = this->sample();
|
float value_v = this->sample();
|
||||||
ESP_LOGD(TAG, "'%s': Got voltage=%.2fV", this->get_name().c_str(), value_v);
|
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
|
||||||
this->publish_state(value_v);
|
this->publish_state(value_v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_ESP8266
|
||||||
float ADCSensor::sample() {
|
float ADCSensor::sample() {
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
int raw = adc1_get_raw(gpio_to_adc1(pin_));
|
int raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
|
||||||
float value_v = raw / 4095.0f;
|
#else
|
||||||
#if CONFIG_IDF_TARGET_ESP32
|
int raw = analogRead(this->pin_->get_pin()); // NOLINT
|
||||||
switch (this->attenuation_) {
|
|
||||||
case ADC_ATTEN_DB_0:
|
|
||||||
value_v *= 1.1;
|
|
||||||
break;
|
|
||||||
case ADC_ATTEN_DB_2_5:
|
|
||||||
value_v *= 1.5;
|
|
||||||
break;
|
|
||||||
case ADC_ATTEN_DB_6:
|
|
||||||
value_v *= 2.2;
|
|
||||||
break;
|
|
||||||
case ADC_ATTEN_DB_11:
|
|
||||||
value_v *= 3.9;
|
|
||||||
break;
|
|
||||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
|
|
||||||
switch (this->attenuation_) {
|
|
||||||
case ADC_ATTEN_DB_0:
|
|
||||||
value_v *= 0.84;
|
|
||||||
break;
|
|
||||||
case ADC_ATTEN_DB_2_5:
|
|
||||||
value_v *= 1.13;
|
|
||||||
break;
|
|
||||||
case ADC_ATTEN_DB_6:
|
|
||||||
value_v *= 1.56;
|
|
||||||
break;
|
|
||||||
case ADC_ATTEN_DB_11:
|
|
||||||
value_v *= 3.0;
|
|
||||||
break;
|
|
||||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
return value_v;
|
if (output_raw_) {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
return raw / 1024.0f;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef USE_ESP32
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
float ADCSensor::sample() {
|
||||||
return ESP.getVcc() / 1024.0f;
|
if (!autorange_) {
|
||||||
#else
|
int raw = adc1_get_raw(channel_);
|
||||||
return analogRead(this->pin_) / 1024.0f; // NOLINT
|
if (raw == -1) {
|
||||||
#endif
|
return NAN;
|
||||||
#endif
|
}
|
||||||
|
if (output_raw_) {
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
uint32_t mv = esp_adc_cal_raw_to_voltage(raw, &cal_characteristics_[(int) attenuation_]);
|
||||||
|
return mv / 1000.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
int raw11, raw6 = ADC_MAX, raw2 = ADC_MAX, raw0 = ADC_MAX;
|
||||||
|
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_11);
|
||||||
|
raw11 = adc1_get_raw(channel_);
|
||||||
|
if (raw11 < ADC_MAX) {
|
||||||
|
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_6);
|
||||||
|
raw6 = adc1_get_raw(channel_);
|
||||||
|
if (raw6 < ADC_MAX) {
|
||||||
|
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_2_5);
|
||||||
|
raw2 = adc1_get_raw(channel_);
|
||||||
|
if (raw2 < ADC_MAX) {
|
||||||
|
adc1_config_channel_atten(channel_, ADC_ATTEN_DB_0);
|
||||||
|
raw0 = adc1_get_raw(channel_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (raw0 == -1 || raw2 == -1 || raw6 == -1 || raw11 == -1) {
|
||||||
|
return NAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t mv11 = esp_adc_cal_raw_to_voltage(raw11, &cal_characteristics_[(int) ADC_ATTEN_DB_11]);
|
||||||
|
uint32_t mv6 = esp_adc_cal_raw_to_voltage(raw6, &cal_characteristics_[(int) ADC_ATTEN_DB_6]);
|
||||||
|
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &cal_characteristics_[(int) ADC_ATTEN_DB_2_5]);
|
||||||
|
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &cal_characteristics_[(int) ADC_ATTEN_DB_0]);
|
||||||
|
|
||||||
|
// Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
|
||||||
|
uint32_t c11 = std::min(raw11, ADC_HALF);
|
||||||
|
uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
|
||||||
|
uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
|
||||||
|
uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
|
||||||
|
// max theoretical csum value is 4096*4 = 16384
|
||||||
|
uint32_t csum = c11 + c6 + c2 + c0;
|
||||||
|
|
||||||
|
// each mv is max 3900; so max value is 3900*4096*4, fits in unsigned32
|
||||||
|
uint32_t mv_scaled = (mv11 * c11) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
|
||||||
|
return mv_scaled / (float) (csum * 1000U);
|
||||||
}
|
}
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#endif // USE_ESP32
|
||||||
|
|
||||||
|
#ifdef USE_ESP8266
|
||||||
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
|
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/esphal.h"
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
#include "driver/adc.h"
|
#include "driver/adc.h"
|
||||||
|
#include <esp_adc_cal.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
@@ -15,9 +16,11 @@ namespace adc {
|
|||||||
|
|
||||||
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
||||||
public:
|
public:
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
/// Set the attenuation for this pin. Only available on the ESP32.
|
/// Set the attenuation for this pin. Only available on the ESP32.
|
||||||
void set_attenuation(adc_atten_t attenuation);
|
void set_attenuation(adc_atten_t attenuation) { attenuation_ = attenuation; }
|
||||||
|
void set_channel(adc1_channel_t channel) { channel_ = channel; }
|
||||||
|
void set_autorange(bool autorange) { autorange_ = autorange; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Update adc values.
|
/// Update adc values.
|
||||||
@@ -27,18 +30,23 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
/// `HARDWARE_LATE` setup priority.
|
/// `HARDWARE_LATE` setup priority.
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
void set_pin(uint8_t pin) { this->pin_ = pin; }
|
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
|
||||||
|
void set_output_raw(bool output_raw) { output_raw_ = output_raw; }
|
||||||
float sample() override;
|
float sample() override;
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP8266
|
#ifdef USE_ESP8266
|
||||||
std::string unique_id() override;
|
std::string unique_id() override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint8_t pin_;
|
InternalGPIOPin *pin_;
|
||||||
|
bool output_raw_{false};
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
||||||
|
adc1_channel_t channel_{};
|
||||||
|
bool autorange_{false};
|
||||||
|
esp_adc_cal_characteristics_t cal_characteristics_[(int) ADC_ATTEN_MAX] = {};
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,24 @@ from esphome import pins
|
|||||||
from esphome.components import sensor, voltage_sampler
|
from esphome.components import sensor, voltage_sampler
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ATTENUATION,
|
CONF_ATTENUATION,
|
||||||
|
CONF_RAW,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_INPUT,
|
||||||
|
CONF_NUMBER,
|
||||||
CONF_PIN,
|
CONF_PIN,
|
||||||
DEVICE_CLASS_VOLTAGE,
|
DEVICE_CLASS_VOLTAGE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
)
|
)
|
||||||
|
from esphome.core import CORE
|
||||||
|
from esphome.components.esp32 import get_esp32_variant
|
||||||
|
from esphome.components.esp32.const import (
|
||||||
|
VARIANT_ESP32,
|
||||||
|
VARIANT_ESP32C3,
|
||||||
|
VARIANT_ESP32H2,
|
||||||
|
VARIANT_ESP32S2,
|
||||||
|
VARIANT_ESP32S3,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
AUTO_LOAD = ["voltage_sampler"]
|
AUTO_LOAD = ["voltage_sampler"]
|
||||||
@@ -19,14 +31,99 @@ ATTENUATION_MODES = {
|
|||||||
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
|
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
|
||||||
"6db": cg.global_ns.ADC_ATTEN_DB_6,
|
"6db": cg.global_ns.ADC_ATTEN_DB_6,
|
||||||
"11db": cg.global_ns.ADC_ATTEN_DB_11,
|
"11db": cg.global_ns.ADC_ATTEN_DB_11,
|
||||||
|
"auto": "auto",
|
||||||
|
}
|
||||||
|
|
||||||
|
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
|
||||||
|
|
||||||
|
# From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
|
||||||
|
# pin to adc1 channel mapping
|
||||||
|
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
|
||||||
|
VARIANT_ESP32: {
|
||||||
|
36: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
37: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
38: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
39: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
32: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
33: adc1_channel_t.ADC1_CHANNEL_5,
|
||||||
|
34: adc1_channel_t.ADC1_CHANNEL_6,
|
||||||
|
35: adc1_channel_t.ADC1_CHANNEL_7,
|
||||||
|
},
|
||||||
|
VARIANT_ESP32S2: {
|
||||||
|
1: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
2: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
3: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
4: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
5: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
6: adc1_channel_t.ADC1_CHANNEL_5,
|
||||||
|
7: adc1_channel_t.ADC1_CHANNEL_6,
|
||||||
|
8: adc1_channel_t.ADC1_CHANNEL_7,
|
||||||
|
9: adc1_channel_t.ADC1_CHANNEL_8,
|
||||||
|
10: adc1_channel_t.ADC1_CHANNEL_9,
|
||||||
|
},
|
||||||
|
VARIANT_ESP32S3: {
|
||||||
|
1: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
2: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
3: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
4: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
5: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
6: adc1_channel_t.ADC1_CHANNEL_5,
|
||||||
|
7: adc1_channel_t.ADC1_CHANNEL_6,
|
||||||
|
8: adc1_channel_t.ADC1_CHANNEL_7,
|
||||||
|
9: adc1_channel_t.ADC1_CHANNEL_8,
|
||||||
|
10: adc1_channel_t.ADC1_CHANNEL_9,
|
||||||
|
},
|
||||||
|
VARIANT_ESP32C3: {
|
||||||
|
0: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
1: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
2: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
3: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
4: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
},
|
||||||
|
VARIANT_ESP32H2: {
|
||||||
|
0: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
1: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
2: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
3: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
4: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def validate_adc_pin(value):
|
def validate_adc_pin(value):
|
||||||
vcc = str(value).upper()
|
if str(value).upper() == "VCC":
|
||||||
if vcc == "VCC":
|
return cv.only_on_esp8266("VCC")
|
||||||
return cv.only_on_esp8266(vcc)
|
|
||||||
return pins.analog_pin(value)
|
if CORE.is_esp32:
|
||||||
|
value = pins.internal_gpio_input_pin_number(value)
|
||||||
|
variant = get_esp32_variant()
|
||||||
|
if variant not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL:
|
||||||
|
raise cv.Invalid(f"This ESP32 variant ({variant}) is not supported")
|
||||||
|
|
||||||
|
if value not in ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant]:
|
||||||
|
raise cv.Invalid(f"{variant} doesn't support ADC on this pin")
|
||||||
|
return pins.internal_gpio_input_pin_schema(value)
|
||||||
|
|
||||||
|
if CORE.is_esp8266:
|
||||||
|
from esphome.components.esp8266.gpio import CONF_ANALOG
|
||||||
|
|
||||||
|
value = pins.internal_gpio_pin_number({CONF_ANALOG: True, CONF_INPUT: True})(
|
||||||
|
value
|
||||||
|
)
|
||||||
|
|
||||||
|
if value != 17: # A0
|
||||||
|
raise cv.Invalid("ESP8266: Only pin A0 (GPIO17) supports ADC.")
|
||||||
|
return pins.gpio_pin_schema(
|
||||||
|
{CONF_ANALOG: True, CONF_INPUT: True}, internal=True
|
||||||
|
)(value)
|
||||||
|
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
def validate_config(config):
|
||||||
|
if config[CONF_RAW] and config.get(CONF_ATTENUATION, None) == "auto":
|
||||||
|
raise cv.Invalid("Automatic attenuation cannot be used when raw output is set.")
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
adc_ns = cg.esphome_ns.namespace("adc")
|
adc_ns = cg.esphome_ns.namespace("adc")
|
||||||
@@ -34,8 +131,9 @@ ADCSensor = adc_ns.class_(
|
|||||||
"ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
|
"ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
|
||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = cv.All(
|
||||||
sensor.sensor_schema(
|
sensor.sensor_schema(
|
||||||
|
ADCSensor,
|
||||||
unit_of_measurement=UNIT_VOLT,
|
unit_of_measurement=UNIT_VOLT,
|
||||||
accuracy_decimals=2,
|
accuracy_decimals=2,
|
||||||
device_class=DEVICE_CLASS_VOLTAGE,
|
device_class=DEVICE_CLASS_VOLTAGE,
|
||||||
@@ -43,14 +141,15 @@ CONFIG_SCHEMA = (
|
|||||||
)
|
)
|
||||||
.extend(
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(ADCSensor),
|
|
||||||
cv.Required(CONF_PIN): validate_adc_pin,
|
cv.Required(CONF_PIN): validate_adc_pin,
|
||||||
|
cv.Optional(CONF_RAW, default=False): cv.boolean,
|
||||||
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
|
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
|
||||||
cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)
|
cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.polling_component_schema("60s"))
|
.extend(cv.polling_component_schema("60s")),
|
||||||
|
validate_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -62,7 +161,20 @@ async def to_code(config):
|
|||||||
if config[CONF_PIN] == "VCC":
|
if config[CONF_PIN] == "VCC":
|
||||||
cg.add_define("USE_ADC_SENSOR_VCC")
|
cg.add_define("USE_ADC_SENSOR_VCC")
|
||||||
else:
|
else:
|
||||||
cg.add(var.set_pin(config[CONF_PIN]))
|
pin = await cg.gpio_pin_expression(config[CONF_PIN])
|
||||||
|
cg.add(var.set_pin(pin))
|
||||||
|
|
||||||
|
if CONF_RAW in config:
|
||||||
|
cg.add(var.set_output_raw(config[CONF_RAW]))
|
||||||
|
|
||||||
if CONF_ATTENUATION in config:
|
if CONF_ATTENUATION in config:
|
||||||
|
if config[CONF_ATTENUATION] == "auto":
|
||||||
|
cg.add(var.set_autorange(cg.global_ns.true))
|
||||||
|
else:
|
||||||
cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
|
cg.add(var.set_attenuation(config[CONF_ATTENUATION]))
|
||||||
|
|
||||||
|
if CORE.is_esp32:
|
||||||
|
variant = get_esp32_variant()
|
||||||
|
pin_num = config[CONF_PIN][CONF_NUMBER]
|
||||||
|
chan = ESP32_VARIANT_ADC1_PIN_TO_CHANNEL[variant][pin_num]
|
||||||
|
cg.add(var.set_channel(chan))
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ class AddressableLightDisplay : public display::DisplayBuffer, public PollingCom
|
|||||||
void setup() override;
|
void setup() override;
|
||||||
void display();
|
void display();
|
||||||
|
|
||||||
|
display::DisplayType get_display_type() override { return display::DisplayType::DISPLAY_TYPE_COLOR; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int get_width_internal() override;
|
int get_width_internal() override;
|
||||||
int get_height_internal() override;
|
int get_height_internal() override;
|
||||||
|
|||||||
@@ -8,9 +8,7 @@ static const char *const TAG = "ade7953";
|
|||||||
|
|
||||||
void ADE7953::dump_config() {
|
void ADE7953::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "ADE7953:");
|
ESP_LOGCONFIG(TAG, "ADE7953:");
|
||||||
if (this->has_irq_) {
|
LOG_PIN(" IRQ Pin: ", irq_pin_);
|
||||||
ESP_LOGCONFIG(TAG, " IRQ Pin: GPIO%u", this->irq_pin_number_);
|
|
||||||
}
|
|
||||||
LOG_I2C_DEVICE(this);
|
LOG_I2C_DEVICE(this);
|
||||||
LOG_UPDATE_INTERVAL(this);
|
LOG_UPDATE_INTERVAL(this);
|
||||||
LOG_SENSOR(" ", "Voltage Sensor", this->voltage_sensor_);
|
LOG_SENSOR(" ", "Voltage Sensor", this->voltage_sensor_);
|
||||||
@@ -20,27 +18,28 @@ void ADE7953::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_);
|
LOG_SENSOR(" ", "Active Power B Sensor", this->active_power_b_sensor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define ADE_PUBLISH_(name, factor) \
|
#define ADE_PUBLISH_(name, val, factor) \
|
||||||
if ((name) && this->name##_sensor_) { \
|
if (err == i2c::ERROR_OK && this->name##_sensor_) { \
|
||||||
float value = *(name) / (factor); \
|
float value = (val) / (factor); \
|
||||||
this->name##_sensor_->publish_state(value); \
|
this->name##_sensor_->publish_state(value); \
|
||||||
}
|
}
|
||||||
#define ADE_PUBLISH(name, factor) ADE_PUBLISH_(name, factor)
|
#define ADE_PUBLISH(name, val, factor) ADE_PUBLISH_(name, val, factor)
|
||||||
|
|
||||||
void ADE7953::update() {
|
void ADE7953::update() {
|
||||||
if (!this->is_setup_)
|
if (!this->is_setup_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto active_power_a = this->ade_read_<int32_t>(0x0312);
|
uint32_t val;
|
||||||
ADE_PUBLISH(active_power_a, 154.0f);
|
i2c::ErrorCode err = ade_read_32_(0x0312, &val);
|
||||||
auto active_power_b = this->ade_read_<int32_t>(0x0313);
|
ADE_PUBLISH(active_power_a, (int32_t) val, 154.0f);
|
||||||
ADE_PUBLISH(active_power_b, 154.0f);
|
err = ade_read_32_(0x0313, &val);
|
||||||
auto current_a = this->ade_read_<uint32_t>(0x031A);
|
ADE_PUBLISH(active_power_b, (int32_t) val, 154.0f);
|
||||||
ADE_PUBLISH(current_a, 100000.0f);
|
err = ade_read_32_(0x031A, &val);
|
||||||
auto current_b = this->ade_read_<uint32_t>(0x031B);
|
ADE_PUBLISH(current_a, (uint32_t) val, 100000.0f);
|
||||||
ADE_PUBLISH(current_b, 100000.0f);
|
err = ade_read_32_(0x031B, &val);
|
||||||
auto voltage = this->ade_read_<uint32_t>(0x031C);
|
ADE_PUBLISH(current_b, (uint32_t) val, 100000.0f);
|
||||||
ADE_PUBLISH(voltage, 26000.0f);
|
err = ade_read_32_(0x031C, &val);
|
||||||
|
ADE_PUBLISH(voltage, (uint32_t) val, 26000.0f);
|
||||||
|
|
||||||
// auto apparent_power_a = this->ade_read_<int32_t>(0x0310);
|
// auto apparent_power_a = this->ade_read_<int32_t>(0x0310);
|
||||||
// auto apparent_power_b = this->ade_read_<int32_t>(0x0311);
|
// auto apparent_power_b = this->ade_read_<int32_t>(0x0311);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/components/i2c/i2c.h"
|
#include "esphome/components/i2c/i2c.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
|
||||||
@@ -9,10 +10,7 @@ namespace ade7953 {
|
|||||||
|
|
||||||
class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
||||||
public:
|
public:
|
||||||
void set_irq_pin(uint8_t irq_pin) {
|
void set_irq_pin(InternalGPIOPin *irq_pin) { irq_pin_ = irq_pin; }
|
||||||
has_irq_ = true;
|
|
||||||
irq_pin_number_ = irq_pin;
|
|
||||||
}
|
|
||||||
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
||||||
void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; }
|
void set_current_a_sensor(sensor::Sensor *current_a_sensor) { current_a_sensor_ = current_a_sensor; }
|
||||||
void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; }
|
void set_current_b_sensor(sensor::Sensor *current_b_sensor) { current_b_sensor_ = current_b_sensor; }
|
||||||
@@ -24,15 +22,13 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setup() override {
|
void setup() override {
|
||||||
if (this->has_irq_) {
|
if (this->irq_pin_ != nullptr) {
|
||||||
auto pin = GPIOPin(this->irq_pin_number_, INPUT);
|
|
||||||
this->irq_pin_ = &pin;
|
|
||||||
this->irq_pin_->setup();
|
this->irq_pin_->setup();
|
||||||
}
|
}
|
||||||
this->set_timeout(100, [this]() {
|
this->set_timeout(100, [this]() {
|
||||||
this->ade_write_<uint8_t>(0x0010, 0x04);
|
this->ade_write_8_(0x0010, 0x04);
|
||||||
this->ade_write_<uint8_t>(0x00FE, 0xAD);
|
this->ade_write_8_(0x00FE, 0xAD);
|
||||||
this->ade_write_<uint16_t>(0x0120, 0x0030);
|
this->ade_write_16_(0x0120, 0x0030);
|
||||||
this->is_setup_ = true;
|
this->is_setup_ = true;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -42,31 +38,51 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
|||||||
void update() override;
|
void update() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
template<typename T> bool ade_write_(uint16_t reg, T value) {
|
i2c::ErrorCode ade_write_8_(uint16_t reg, uint8_t value) {
|
||||||
std::vector<uint8_t> data;
|
std::vector<uint8_t> data;
|
||||||
data.push_back(reg >> 8);
|
data.push_back(reg >> 8);
|
||||||
data.push_back(reg >> 0);
|
data.push_back(reg >> 0);
|
||||||
for (int i = sizeof(T) - 1; i >= 0; i--)
|
data.push_back(value);
|
||||||
data.push_back(value >> (i * 8));
|
return write(data.data(), data.size());
|
||||||
return this->write_bytes_raw(data);
|
|
||||||
}
|
}
|
||||||
template<typename T> optional<T> ade_read_(uint16_t reg) {
|
i2c::ErrorCode ade_write_16_(uint16_t reg, uint16_t value) {
|
||||||
uint8_t hi = reg >> 8;
|
std::vector<uint8_t> data;
|
||||||
uint8_t lo = reg >> 0;
|
data.push_back(reg >> 8);
|
||||||
if (!this->write_bytes_raw({hi, lo}))
|
data.push_back(reg >> 0);
|
||||||
return {};
|
data.push_back(value >> 8);
|
||||||
auto ret = this->read_bytes_raw<sizeof(T)>();
|
data.push_back(value >> 0);
|
||||||
if (!ret.has_value())
|
return write(data.data(), data.size());
|
||||||
return {};
|
}
|
||||||
T result = 0;
|
i2c::ErrorCode ade_write_32_(uint16_t reg, uint32_t value) {
|
||||||
for (int i = 0, j = sizeof(T) - 1; i < sizeof(T); i++, j--)
|
std::vector<uint8_t> data;
|
||||||
result |= T((*ret)[i]) << (j * 8);
|
data.push_back(reg >> 8);
|
||||||
return result;
|
data.push_back(reg >> 0);
|
||||||
|
data.push_back(value >> 24);
|
||||||
|
data.push_back(value >> 16);
|
||||||
|
data.push_back(value >> 8);
|
||||||
|
data.push_back(value >> 0);
|
||||||
|
return write(data.data(), data.size());
|
||||||
|
}
|
||||||
|
i2c::ErrorCode ade_read_32_(uint16_t reg, uint32_t *value) {
|
||||||
|
uint8_t reg_data[2];
|
||||||
|
reg_data[0] = reg >> 8;
|
||||||
|
reg_data[1] = reg >> 0;
|
||||||
|
i2c::ErrorCode err = write(reg_data, 2);
|
||||||
|
if (err != i2c::ERROR_OK)
|
||||||
|
return err;
|
||||||
|
uint8_t recv[4];
|
||||||
|
err = read(recv, 4);
|
||||||
|
if (err != i2c::ERROR_OK)
|
||||||
|
return err;
|
||||||
|
*value = 0;
|
||||||
|
*value |= ((uint32_t) recv[0]) << 24;
|
||||||
|
*value |= ((uint32_t) recv[1]) << 16;
|
||||||
|
*value |= ((uint32_t) recv[2]) << 8;
|
||||||
|
*value |= ((uint32_t) recv[3]);
|
||||||
|
return i2c::ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool has_irq_ = false;
|
InternalGPIOPin *irq_pin_ = nullptr;
|
||||||
uint8_t irq_pin_number_;
|
|
||||||
GPIOPin *irq_pin_{nullptr};
|
|
||||||
bool is_setup_{false};
|
bool is_setup_{false};
|
||||||
sensor::Sensor *voltage_sensor_{nullptr};
|
sensor::Sensor *voltage_sensor_{nullptr};
|
||||||
sensor::Sensor *current_a_sensor_{nullptr};
|
sensor::Sensor *current_a_sensor_{nullptr};
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ CONFIG_SCHEMA = (
|
|||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(ADE7953),
|
cv.GenerateID(): cv.declare_id(ADE7953),
|
||||||
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
|
cv.Optional(CONF_IRQ_PIN): pins.internal_gpio_input_pin_schema,
|
||||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_VOLT,
|
unit_of_measurement=UNIT_VOLT,
|
||||||
accuracy_decimals=1,
|
accuracy_decimals=1,
|
||||||
@@ -73,7 +73,8 @@ async def to_code(config):
|
|||||||
await i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
if CONF_IRQ_PIN in config:
|
if CONF_IRQ_PIN in config:
|
||||||
cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
|
irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
|
||||||
|
cg.add(var.set_irq_pin(irq_pin))
|
||||||
|
|
||||||
for key in [
|
for key in [
|
||||||
CONF_VOLTAGE,
|
CONF_VOLTAGE,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "ads1115.h"
|
#include "ads1115.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ads1115 {
|
namespace ads1115 {
|
||||||
@@ -159,7 +160,7 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
|
|||||||
float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); }
|
float ADS1115Sensor::sample() { return this->parent_->request_measurement(this); }
|
||||||
void ADS1115Sensor::update() {
|
void ADS1115Sensor::update() {
|
||||||
float v = this->parent_->request_measurement(this);
|
float v = this->parent_->request_measurement(this);
|
||||||
if (!isnan(v)) {
|
if (!std::isnan(v)) {
|
||||||
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
|
ESP_LOGD(TAG, "'%s': Got Voltage=%fV", this->get_name().c_str(), v);
|
||||||
this->publish_state(v);
|
this->publish_state(v);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ ADS1115Sensor = ads1115_ns.class_(
|
|||||||
CONF_ADS1115_ID = "ads1115_id"
|
CONF_ADS1115_ID = "ads1115_id"
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
sensor.sensor_schema(
|
sensor.sensor_schema(
|
||||||
|
ADS1115Sensor,
|
||||||
unit_of_measurement=UNIT_VOLT,
|
unit_of_measurement=UNIT_VOLT,
|
||||||
accuracy_decimals=3,
|
accuracy_decimals=3,
|
||||||
device_class=DEVICE_CLASS_VOLTAGE,
|
device_class=DEVICE_CLASS_VOLTAGE,
|
||||||
@@ -59,7 +60,6 @@ CONFIG_SCHEMA = (
|
|||||||
)
|
)
|
||||||
.extend(
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(ADS1115Sensor),
|
|
||||||
cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
|
cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
|
||||||
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
|
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
|
||||||
cv.Required(CONF_GAIN): validate_gain,
|
cv.Required(CONF_GAIN): validate_gain,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "aht10.h"
|
#include "aht10.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace aht10 {
|
namespace aht10 {
|
||||||
@@ -33,8 +34,19 @@ void AHT10Component::setup() {
|
|||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint8_t data;
|
uint8_t data = 0;
|
||||||
if (!this->read_byte(0, &data, AHT10_DEFAULT_DELAY)) {
|
if (this->write(&data, 1) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delay(AHT10_DEFAULT_DELAY);
|
||||||
|
if (this->read(&data, 1) != i2c::ERROR_OK) {
|
||||||
|
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this->read(&data, 1) != i2c::ERROR_OK) {
|
||||||
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
@@ -55,15 +67,19 @@ void AHT10Component::update() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint8_t data[6];
|
uint8_t data[6];
|
||||||
uint8_t delay = AHT10_DEFAULT_DELAY;
|
uint8_t delay_ms = AHT10_DEFAULT_DELAY;
|
||||||
if (this->humidity_sensor_ != nullptr)
|
if (this->humidity_sensor_ != nullptr)
|
||||||
delay = AHT10_HUMIDITY_DELAY;
|
delay_ms = AHT10_HUMIDITY_DELAY;
|
||||||
|
bool success = false;
|
||||||
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
|
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
|
||||||
ESP_LOGVV(TAG, "Attempt %u at %6ld", i, millis());
|
ESP_LOGVV(TAG, "Attempt %d at %6u", i, millis());
|
||||||
delay_microseconds_accurate(4);
|
delay(delay_ms);
|
||||||
if (!this->read_bytes(0, data, 6, delay)) {
|
if (this->read(data, 6) != i2c::ERROR_OK) {
|
||||||
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
||||||
} else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
|
||||||
ESP_LOGD(TAG, "AHT10 is busy, waiting...");
|
ESP_LOGD(TAG, "AHT10 is busy, waiting...");
|
||||||
} else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
|
} else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
|
||||||
// Unrealistic humidity (0x0)
|
// Unrealistic humidity (0x0)
|
||||||
@@ -80,11 +96,12 @@ void AHT10Component::update() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// data is valid, we can break the loop
|
// data is valid, we can break the loop
|
||||||
ESP_LOGVV(TAG, "Answer at %6ld", millis());
|
ESP_LOGVV(TAG, "Answer at %6u", millis());
|
||||||
|
success = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((data[0] & 0x80) == 0x80) {
|
if (!success || (data[0] & 0x80) == 0x80) {
|
||||||
ESP_LOGE(TAG, "Measurements reading timed-out!");
|
ESP_LOGE(TAG, "Measurements reading timed-out!");
|
||||||
this->status_set_warning();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
@@ -93,19 +110,19 @@ void AHT10Component::update() {
|
|||||||
uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
|
uint32_t raw_temperature = ((data[3] & 0x0F) << 16) | (data[4] << 8) | data[5];
|
||||||
uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
|
uint32_t raw_humidity = ((data[1] << 16) | (data[2] << 8) | data[3]) >> 4;
|
||||||
|
|
||||||
float temperature = ((200.0 * (float) raw_temperature) / 1048576.0) - 50.0;
|
float temperature = ((200.0f * (float) raw_temperature) / 1048576.0f) - 50.0f;
|
||||||
float humidity;
|
float humidity;
|
||||||
if (raw_humidity == 0) { // unrealistic value
|
if (raw_humidity == 0) { // unrealistic value
|
||||||
humidity = NAN;
|
humidity = NAN;
|
||||||
} else {
|
} else {
|
||||||
humidity = (float) raw_humidity * 100.0 / 1048576.0;
|
humidity = (float) raw_humidity * 100.0f / 1048576.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->temperature_sensor_ != nullptr) {
|
if (this->temperature_sensor_ != nullptr) {
|
||||||
this->temperature_sensor_->publish_state(temperature);
|
this->temperature_sensor_->publish_state(temperature);
|
||||||
}
|
}
|
||||||
if (this->humidity_sensor_ != nullptr) {
|
if (this->humidity_sensor_ != nullptr) {
|
||||||
if (isnan(humidity))
|
if (std::isnan(humidity))
|
||||||
ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
|
ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
|
||||||
this->humidity_sensor_->publish_state(humidity);
|
this->humidity_sensor_->publish_state(humidity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#include "airthings_listener.h"
|
#include "airthings_listener.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace airthings_ble {
|
namespace airthings_ble {
|
||||||
|
|
||||||
static const char *TAG = "airthings_ble";
|
static const char *const TAG = "airthings_ble";
|
||||||
|
|
||||||
bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
for (auto &it : device.get_manufacturer_datas()) {
|
for (auto &it : device.get_manufacturer_datas()) {
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
#include <BLEDevice.h>
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace airthings_ble {
|
namespace airthings_ble {
|
||||||
|
|||||||
1
esphome/components/airthings_wave_mini/__init__.py
Normal file
1
esphome/components/airthings_wave_mini/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CODEOWNERS = ["@ncareau"]
|
||||||
113
esphome/components/airthings_wave_mini/airthings_wave_mini.cpp
Normal file
113
esphome/components/airthings_wave_mini/airthings_wave_mini.cpp
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#include "airthings_wave_mini.h"
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace airthings_wave_mini {
|
||||||
|
|
||||||
|
static const char *const TAG = "airthings_wave_mini";
|
||||||
|
|
||||||
|
void AirthingsWaveMini::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
|
esp_ble_gattc_cb_param_t *param) {
|
||||||
|
switch (event) {
|
||||||
|
case ESP_GATTC_OPEN_EVT: {
|
||||||
|
if (param->open.status == ESP_GATT_OK) {
|
||||||
|
ESP_LOGI(TAG, "Connected successfully!");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ESP_GATTC_DISCONNECT_EVT: {
|
||||||
|
ESP_LOGW(TAG, "Disconnected!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||||
|
this->handle_ = 0;
|
||||||
|
auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
|
||||||
|
if (chr == nullptr) {
|
||||||
|
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
|
||||||
|
sensors_data_characteristic_uuid_.to_string().c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this->handle_ = chr->handle;
|
||||||
|
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
|
||||||
|
|
||||||
|
request_read_values_();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case ESP_GATTC_READ_CHAR_EVT: {
|
||||||
|
if (param->read.conn_id != this->parent()->conn_id)
|
||||||
|
break;
|
||||||
|
if (param->read.status != ESP_GATT_OK) {
|
||||||
|
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (param->read.handle == this->handle_) {
|
||||||
|
read_sensors_(param->read.value, param->read.value_len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AirthingsWaveMini::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
|
||||||
|
auto *value = (WaveMiniReadings *) raw_value;
|
||||||
|
|
||||||
|
if (sizeof(WaveMiniReadings) <= value_len) {
|
||||||
|
this->humidity_sensor_->publish_state(value->humidity / 100.0f);
|
||||||
|
this->pressure_sensor_->publish_state(value->pressure / 50.0f);
|
||||||
|
this->temperature_sensor_->publish_state(value->temperature / 100.0f - 273.15f);
|
||||||
|
if (is_valid_voc_value_(value->voc)) {
|
||||||
|
this->tvoc_sensor_->publish_state(value->voc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This instance must not stay connected
|
||||||
|
// so other clients can connect to it (e.g. the
|
||||||
|
// mobile app).
|
||||||
|
parent()->set_enabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AirthingsWaveMini::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
|
||||||
|
|
||||||
|
void AirthingsWaveMini::update() {
|
||||||
|
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
|
||||||
|
if (!parent()->enabled) {
|
||||||
|
ESP_LOGW(TAG, "Reconnecting to device");
|
||||||
|
parent()->set_enabled(true);
|
||||||
|
parent()->connect();
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Connection in progress");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AirthingsWaveMini::request_read_values_() {
|
||||||
|
auto status =
|
||||||
|
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
if (status) {
|
||||||
|
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AirthingsWaveMini::dump_config() {
|
||||||
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
||||||
|
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
AirthingsWaveMini::AirthingsWaveMini()
|
||||||
|
: PollingComponent(10000),
|
||||||
|
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
|
||||||
|
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
|
||||||
|
|
||||||
|
} // namespace airthings_wave_mini
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ESP32
|
||||||
65
esphome/components/airthings_wave_mini/airthings_wave_mini.h
Normal file
65
esphome/components/airthings_wave_mini/airthings_wave_mini.h
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include <esp_gattc_api.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include "esphome/components/ble_client/ble_client.h"
|
||||||
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace airthings_wave_mini {
|
||||||
|
|
||||||
|
static const char *const SERVICE_UUID = "b42e3882-ade7-11e4-89d3-123b93f75cba";
|
||||||
|
static const char *const CHARACTERISTIC_UUID = "b42e3b98-ade7-11e4-89d3-123b93f75cba";
|
||||||
|
|
||||||
|
class AirthingsWaveMini : public PollingComponent, public ble_client::BLEClientNode {
|
||||||
|
public:
|
||||||
|
AirthingsWaveMini();
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
|
|
||||||
|
void set_temperature(sensor::Sensor *temperature) { temperature_sensor_ = temperature; }
|
||||||
|
void set_humidity(sensor::Sensor *humidity) { humidity_sensor_ = humidity; }
|
||||||
|
void set_pressure(sensor::Sensor *pressure) { pressure_sensor_ = pressure; }
|
||||||
|
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool is_valid_voc_value_(uint16_t voc);
|
||||||
|
|
||||||
|
void read_sensors_(uint8_t *value, uint16_t value_len);
|
||||||
|
void request_read_values_();
|
||||||
|
|
||||||
|
sensor::Sensor *temperature_sensor_{nullptr};
|
||||||
|
sensor::Sensor *humidity_sensor_{nullptr};
|
||||||
|
sensor::Sensor *pressure_sensor_{nullptr};
|
||||||
|
sensor::Sensor *tvoc_sensor_{nullptr};
|
||||||
|
|
||||||
|
uint16_t handle_;
|
||||||
|
esp32_ble_tracker::ESPBTUUID service_uuid_;
|
||||||
|
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
|
||||||
|
|
||||||
|
struct WaveMiniReadings {
|
||||||
|
uint16_t unused01;
|
||||||
|
uint16_t temperature;
|
||||||
|
uint16_t pressure;
|
||||||
|
uint16_t humidity;
|
||||||
|
uint16_t voc;
|
||||||
|
uint16_t unused02;
|
||||||
|
uint32_t unused03;
|
||||||
|
uint32_t unused04;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace airthings_wave_mini
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ESP32
|
||||||
82
esphome/components/airthings_wave_mini/sensor.py
Normal file
82
esphome/components/airthings_wave_mini/sensor.py
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor, ble_client
|
||||||
|
|
||||||
|
from esphome.const import (
|
||||||
|
DEVICE_CLASS_HUMIDITY,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_PRESSURE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_HECTOPASCAL,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_HUMIDITY,
|
||||||
|
CONF_TVOC,
|
||||||
|
CONF_PRESSURE,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
|
UNIT_PARTS_PER_BILLION,
|
||||||
|
ICON_RADIATOR,
|
||||||
|
)
|
||||||
|
|
||||||
|
DEPENDENCIES = ["ble_client"]
|
||||||
|
|
||||||
|
airthings_wave_mini_ns = cg.esphome_ns.namespace("airthings_wave_mini")
|
||||||
|
AirthingsWaveMini = airthings_wave_mini_ns.class_(
|
||||||
|
"AirthingsWaveMini", cg.PollingComponent, ble_client.BLEClientNode
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(AirthingsWaveMini),
|
||||||
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
device_class=DEVICE_CLASS_HUMIDITY,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_CELSIUS,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_HECTOPASCAL,
|
||||||
|
accuracy_decimals=2,
|
||||||
|
device_class=DEVICE_CLASS_PRESSURE,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_TVOC): sensor.sensor_schema(
|
||||||
|
unit_of_measurement=UNIT_PARTS_PER_BILLION,
|
||||||
|
icon=ICON_RADIATOR,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
state_class=STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("5min"))
|
||||||
|
.extend(ble_client.BLE_CLIENT_SCHEMA),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
|
await ble_client.register_ble_node(var, config)
|
||||||
|
|
||||||
|
if CONF_HUMIDITY in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
||||||
|
cg.add(var.set_humidity(sens))
|
||||||
|
if CONF_TEMPERATURE in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||||
|
cg.add(var.set_temperature(sens))
|
||||||
|
if CONF_PRESSURE in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_PRESSURE])
|
||||||
|
cg.add(var.set_pressure(sens))
|
||||||
|
if CONF_TVOC in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_TVOC])
|
||||||
|
cg.add(var.set_tvoc(sens))
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
#include "airthings_wave_plus.h"
|
#include "airthings_wave_plus.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace airthings_wave_plus {
|
namespace airthings_wave_plus {
|
||||||
|
|
||||||
|
static const char *const TAG = "airthings_wave_plus";
|
||||||
|
|
||||||
void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
esp_ble_gattc_cb_param_t *param) {
|
esp_ble_gattc_cb_param_t *param) {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
@@ -21,15 +23,15 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
|
|||||||
}
|
}
|
||||||
|
|
||||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||||
this->handle = 0;
|
this->handle_ = 0;
|
||||||
auto chr = this->parent()->get_characteristic(service_uuid, sensors_data_characteristic_uuid);
|
auto *chr = this->parent()->get_characteristic(service_uuid_, sensors_data_characteristic_uuid_);
|
||||||
if (chr == nullptr) {
|
if (chr == nullptr) {
|
||||||
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid.to_string().c_str(),
|
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", service_uuid_.to_string().c_str(),
|
||||||
sensors_data_characteristic_uuid.to_string().c_str());
|
sensors_data_characteristic_uuid_.to_string().c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this->handle = chr->handle;
|
this->handle_ = chr->handle;
|
||||||
this->node_state = espbt::ClientState::Established;
|
this->node_state = esp32_ble_tracker::ClientState::ESTABLISHED;
|
||||||
|
|
||||||
request_read_values_();
|
request_read_values_();
|
||||||
break;
|
break;
|
||||||
@@ -42,7 +44,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
|
|||||||
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (param->read.handle == this->handle) {
|
if (param->read.handle == this->handle_) {
|
||||||
read_sensors_(param->read.value, param->read.value_len);
|
read_sensors_(param->read.value, param->read.value_len);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -54,7 +56,7 @@ void AirthingsWavePlus::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
|
void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
|
||||||
auto value = (WavePlusReadings *) raw_value;
|
auto *value = (WavePlusReadings *) raw_value;
|
||||||
|
|
||||||
if (sizeof(WavePlusReadings) <= value_len) {
|
if (sizeof(WavePlusReadings) <= value_len) {
|
||||||
ESP_LOGD(TAG, "version = %d", value->version);
|
ESP_LOGD(TAG, "version = %d", value->version);
|
||||||
@@ -88,16 +90,14 @@ void AirthingsWavePlus::read_sensors_(uint8_t *raw_value, uint16_t value_len) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AirthingsWavePlus::is_valid_radon_value_(short radon) { return 0 <= radon && radon <= 16383; }
|
bool AirthingsWavePlus::is_valid_radon_value_(uint16_t radon) { return 0 <= radon && radon <= 16383; }
|
||||||
|
|
||||||
bool AirthingsWavePlus::is_valid_voc_value_(short voc) { return 0 <= voc && voc <= 16383; }
|
bool AirthingsWavePlus::is_valid_voc_value_(uint16_t voc) { return 0 <= voc && voc <= 16383; }
|
||||||
|
|
||||||
bool AirthingsWavePlus::is_valid_co2_value_(short co2) { return 0 <= co2 && co2 <= 16383; }
|
bool AirthingsWavePlus::is_valid_co2_value_(uint16_t co2) { return 0 <= co2 && co2 <= 16383; }
|
||||||
|
|
||||||
void AirthingsWavePlus::loop() {}
|
|
||||||
|
|
||||||
void AirthingsWavePlus::update() {
|
void AirthingsWavePlus::update() {
|
||||||
if (this->node_state != espbt::ClientState::Established) {
|
if (this->node_state != esp32_ble_tracker::ClientState::ESTABLISHED) {
|
||||||
if (!parent()->enabled) {
|
if (!parent()->enabled) {
|
||||||
ESP_LOGW(TAG, "Reconnecting to device");
|
ESP_LOGW(TAG, "Reconnecting to device");
|
||||||
parent()->set_enabled(true);
|
parent()->set_enabled(true);
|
||||||
@@ -110,7 +110,7 @@ void AirthingsWavePlus::update() {
|
|||||||
|
|
||||||
void AirthingsWavePlus::request_read_values_() {
|
void AirthingsWavePlus::request_read_values_() {
|
||||||
auto status =
|
auto status =
|
||||||
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE);
|
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle_, ESP_GATT_AUTH_REQ_NONE);
|
||||||
if (status) {
|
if (status) {
|
||||||
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
|
ESP_LOGW(TAG, "Error sending read request for sensor, status=%d", status);
|
||||||
}
|
}
|
||||||
@@ -126,17 +126,12 @@ void AirthingsWavePlus::dump_config() {
|
|||||||
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
LOG_SENSOR(" ", "TVOC", this->tvoc_sensor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
AirthingsWavePlus::AirthingsWavePlus() : PollingComponent(10000) {
|
AirthingsWavePlus::AirthingsWavePlus()
|
||||||
auto service_bt = *BLEUUID::fromString(std::string("b42e1c08-ade7-11e4-89d3-123b93f75cba")).getNative();
|
: PollingComponent(10000),
|
||||||
auto characteristic_bt = *BLEUUID::fromString(std::string("b42e2a68-ade7-11e4-89d3-123b93f75cba")).getNative();
|
service_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(SERVICE_UUID)),
|
||||||
|
sensors_data_characteristic_uuid_(esp32_ble_tracker::ESPBTUUID::from_raw(CHARACTERISTIC_UUID)) {}
|
||||||
service_uuid = espbt::ESPBTUUID::from_uuid(service_bt);
|
|
||||||
sensors_data_characteristic_uuid = espbt::ESPBTUUID::from_uuid(characteristic_bt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AirthingsWavePlus::setup() {}
|
|
||||||
|
|
||||||
} // namespace airthings_wave_plus
|
} // namespace airthings_wave_plus
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif // ARDUINO_ARCH_ESP32
|
#endif // USE_ESP32
|
||||||
|
|||||||
@@ -1,32 +1,28 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/component.h"
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
|
#include <esp_gattc_api.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
#include "esphome/components/ble_client/ble_client.h"
|
#include "esphome/components/ble_client/ble_client.h"
|
||||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include <algorithm>
|
|
||||||
#include <iterator>
|
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
|
||||||
#include <esp_gattc_api.h>
|
|
||||||
#include <BLEDevice.h>
|
|
||||||
|
|
||||||
using namespace esphome::ble_client;
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace airthings_wave_plus {
|
namespace airthings_wave_plus {
|
||||||
|
|
||||||
static const char *TAG = "airthings_wave_plus";
|
static const char *const SERVICE_UUID = "b42e1c08-ade7-11e4-89d3-123b93f75cba";
|
||||||
|
static const char *const CHARACTERISTIC_UUID = "b42e2a68-ade7-11e4-89d3-123b93f75cba";
|
||||||
|
|
||||||
class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
class AirthingsWavePlus : public PollingComponent, public ble_client::BLEClientNode {
|
||||||
public:
|
public:
|
||||||
AirthingsWavePlus();
|
AirthingsWavePlus();
|
||||||
|
|
||||||
void setup() override;
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
void update() override;
|
void update() override;
|
||||||
void loop() override;
|
|
||||||
|
|
||||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||||
esp_ble_gattc_cb_param_t *param) override;
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
@@ -40,9 +36,9 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
|||||||
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
|
void set_tvoc(sensor::Sensor *tvoc) { tvoc_sensor_ = tvoc; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool is_valid_radon_value_(short radon);
|
bool is_valid_radon_value_(uint16_t radon);
|
||||||
bool is_valid_voc_value_(short voc);
|
bool is_valid_voc_value_(uint16_t voc);
|
||||||
bool is_valid_co2_value_(short co2);
|
bool is_valid_co2_value_(uint16_t co2);
|
||||||
|
|
||||||
void read_sensors_(uint8_t *value, uint16_t value_len);
|
void read_sensors_(uint8_t *value, uint16_t value_len);
|
||||||
void request_read_values_();
|
void request_read_values_();
|
||||||
@@ -55,9 +51,9 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
|||||||
sensor::Sensor *co2_sensor_{nullptr};
|
sensor::Sensor *co2_sensor_{nullptr};
|
||||||
sensor::Sensor *tvoc_sensor_{nullptr};
|
sensor::Sensor *tvoc_sensor_{nullptr};
|
||||||
|
|
||||||
uint16_t handle;
|
uint16_t handle_;
|
||||||
espbt::ESPBTUUID service_uuid;
|
esp32_ble_tracker::ESPBTUUID service_uuid_;
|
||||||
espbt::ESPBTUUID sensors_data_characteristic_uuid;
|
esp32_ble_tracker::ESPBTUUID sensors_data_characteristic_uuid_;
|
||||||
|
|
||||||
struct WavePlusReadings {
|
struct WavePlusReadings {
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
@@ -76,4 +72,4 @@ class AirthingsWavePlus : public PollingComponent, public BLEClientNode {
|
|||||||
} // namespace airthings_wave_plus
|
} // namespace airthings_wave_plus
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
#endif // ARDUINO_ARCH_ESP32
|
#endif // USE_ESP32
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ AirthingsWavePlus = airthings_wave_plus_ns.class_(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
|
cv.GenerateID(): cv.declare_id(AirthingsWavePlus),
|
||||||
@@ -82,8 +82,8 @@ CONFIG_SCHEMA = (
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.polling_component_schema("5mins"))
|
.extend(cv.polling_component_schema("5min"))
|
||||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
.extend(ble_client.BLE_CLIENT_SCHEMA),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "am2320.h"
|
#include "am2320.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace am2320 {
|
namespace am2320 {
|
||||||
@@ -18,13 +19,15 @@ uint16_t crc_16(uint8_t *ptr, uint8_t length) {
|
|||||||
//------------------------------
|
//------------------------------
|
||||||
while (length--) {
|
while (length--) {
|
||||||
crc ^= *ptr++;
|
crc ^= *ptr++;
|
||||||
for (i = 0; i < 8; i++)
|
for (i = 0; i < 8; i++) {
|
||||||
if ((crc & 0x01) != 0) {
|
if ((crc & 0x01) != 0) {
|
||||||
crc >>= 1;
|
crc >>= 1;
|
||||||
crc ^= 0xA001;
|
crc ^= 0xA001;
|
||||||
} else
|
} else {
|
||||||
crc >>= 1;
|
crc >>= 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,9 +40,9 @@ void AM2320Component::update() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0;
|
float temperature = (((data[4] & 0x7F) << 8) + data[5]) / 10.0f;
|
||||||
temperature = (data[4] & 0x80) ? -temperature : temperature;
|
temperature = (data[4] & 0x80) ? -temperature : temperature;
|
||||||
float humidity = ((data[2] << 8) + data[3]) / 10.0;
|
float humidity = ((data[2] << 8) + data[3]) / 10.0f;
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Got temperature=%.1f°C humidity=%.1f%%", temperature, humidity);
|
ESP_LOGD(TAG, "Got temperature=%.1f°C humidity=%.1f%%", temperature, humidity);
|
||||||
if (this->temperature_sensor_ != nullptr)
|
if (this->temperature_sensor_ != nullptr)
|
||||||
@@ -77,7 +80,7 @@ bool AM2320Component::read_bytes_(uint8_t a_register, uint8_t *data, uint8_t len
|
|||||||
|
|
||||||
if (conversion > 0)
|
if (conversion > 0)
|
||||||
delay(conversion);
|
delay(conversion);
|
||||||
return this->parent_->raw_receive(this->address_, data, len);
|
return this->read(data, len) == i2c::ERROR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AM2320Component::read_data_(uint8_t *data) {
|
bool AM2320Component::read_data_(uint8_t *data) {
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
#include "am43.h"
|
#include "am43.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace am43 {
|
namespace am43 {
|
||||||
|
|
||||||
static const char *TAG = "am43";
|
static const char *const TAG = "am43";
|
||||||
|
|
||||||
void Am43::dump_config() {
|
void Am43::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "AM43");
|
ESP_LOGCONFIG(TAG, "AM43");
|
||||||
@@ -15,8 +16,8 @@ void Am43::dump_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Am43::setup() {
|
void Am43::setup() {
|
||||||
this->encoder_ = new Am43Encoder();
|
this->encoder_ = make_unique<Am43Encoder>();
|
||||||
this->decoder_ = new Am43Decoder();
|
this->decoder_ = make_unique<Am43Decoder>();
|
||||||
this->logged_in_ = false;
|
this->logged_in_ = false;
|
||||||
this->last_battery_update_ = 0;
|
this->last_battery_update_ = 0;
|
||||||
this->current_sensor_ = 0;
|
this->current_sensor_ = 0;
|
||||||
@@ -30,7 +31,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
|
|||||||
}
|
}
|
||||||
case ESP_GATTC_DISCONNECT_EVT: {
|
case ESP_GATTC_DISCONNECT_EVT: {
|
||||||
this->logged_in_ = false;
|
this->logged_in_ = false;
|
||||||
this->node_state = espbt::ClientState::Idle;
|
this->node_state = espbt::ClientState::IDLE;
|
||||||
if (this->battery_ != nullptr)
|
if (this->battery_ != nullptr)
|
||||||
this->battery_->publish_state(NAN);
|
this->battery_->publish_state(NAN);
|
||||||
if (this->illuminance_ != nullptr)
|
if (this->illuminance_ != nullptr)
|
||||||
@@ -38,7 +39,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||||
auto chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
|
auto *chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
|
||||||
if (chr == nullptr) {
|
if (chr == nullptr) {
|
||||||
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
|
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
|
||||||
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.",
|
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.",
|
||||||
@@ -53,7 +54,7 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
this->node_state = espbt::ClientState::Established;
|
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||||
this->update();
|
this->update();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -74,14 +75,15 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
|
|||||||
|
|
||||||
if (this->current_sensor_ > 0) {
|
if (this->current_sensor_ > 0) {
|
||||||
if (this->illuminance_ != nullptr) {
|
if (this->illuminance_ != nullptr) {
|
||||||
auto packet = this->encoder_->get_light_level_request();
|
auto *packet = this->encoder_->get_light_level_request();
|
||||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||||
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
|
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
|
||||||
ESP_GATT_AUTH_REQ_NONE);
|
ESP_GATT_AUTH_REQ_NONE);
|
||||||
if (status)
|
if (status) {
|
||||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
|
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
|
||||||
status);
|
status);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this->current_sensor_ = 0;
|
this->current_sensor_ = 0;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -92,13 +94,13 @@ void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_i
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Am43::update() {
|
void Am43::update() {
|
||||||
if (this->node_state != espbt::ClientState::Established) {
|
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||||
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str());
|
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this->current_sensor_ == 0) {
|
if (this->current_sensor_ == 0) {
|
||||||
if (this->battery_ != nullptr) {
|
if (this->battery_ != nullptr) {
|
||||||
auto packet = this->encoder_->get_battery_level_request();
|
auto *packet = this->encoder_->get_battery_level_request();
|
||||||
auto status =
|
auto status =
|
||||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||||
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/am43/am43_base.h"
|
#include "esphome/components/am43/am43_base.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include <esp_gattc_api.h>
|
#include <esp_gattc_api.h>
|
||||||
|
|
||||||
@@ -28,8 +28,8 @@ class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint16_t char_handle_;
|
uint16_t char_handle_;
|
||||||
Am43Encoder *encoder_;
|
std::unique_ptr<Am43Encoder> encoder_;
|
||||||
Am43Decoder *decoder_;
|
std::unique_ptr<Am43Decoder> decoder_;
|
||||||
bool logged_in_;
|
bool logged_in_;
|
||||||
sensor::Sensor *battery_{nullptr};
|
sensor::Sensor *battery_{nullptr};
|
||||||
sensor::Sensor *illuminance_{nullptr};
|
sensor::Sensor *illuminance_{nullptr};
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#include "am43_base.h"
|
#include "am43_base.h"
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace am43 {
|
namespace am43 {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from esphome.const import CONF_ID, CONF_PIN
|
|||||||
|
|
||||||
CODEOWNERS = ["@buxtronix"]
|
CODEOWNERS = ["@buxtronix"]
|
||||||
DEPENDENCIES = ["ble_client"]
|
DEPENDENCIES = ["ble_client"]
|
||||||
AUTO_LOAD = ["am43"]
|
AUTO_LOAD = ["am43", "sensor"]
|
||||||
|
|
||||||
CONF_INVERT_POSITION = "invert_position"
|
CONF_INVERT_POSITION = "invert_position"
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
#include "am43_cover.h"
|
#include "am43_cover.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace am43 {
|
namespace am43 {
|
||||||
|
|
||||||
static const char *TAG = "am43_cover";
|
static const char *const TAG = "am43_cover";
|
||||||
|
|
||||||
using namespace esphome::cover;
|
using namespace esphome::cover;
|
||||||
|
|
||||||
@@ -18,23 +18,24 @@ void Am43Component::dump_config() {
|
|||||||
|
|
||||||
void Am43Component::setup() {
|
void Am43Component::setup() {
|
||||||
this->position = COVER_OPEN;
|
this->position = COVER_OPEN;
|
||||||
this->encoder_ = new Am43Encoder();
|
this->encoder_ = make_unique<Am43Encoder>();
|
||||||
this->decoder_ = new Am43Decoder();
|
this->decoder_ = make_unique<Am43Decoder>();
|
||||||
this->logged_in_ = false;
|
this->logged_in_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Am43Component::loop() {
|
void Am43Component::loop() {
|
||||||
if (this->node_state == espbt::ClientState::Established && !this->logged_in_) {
|
if (this->node_state == espbt::ClientState::ESTABLISHED && !this->logged_in_) {
|
||||||
auto packet = this->encoder_->get_send_pin_request(this->pin_);
|
auto *packet = this->encoder_->get_send_pin_request(this->pin_);
|
||||||
auto status =
|
auto status =
|
||||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||||
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
ESP_LOGI(TAG, "[%s] Logging into AM43", this->get_name().c_str());
|
ESP_LOGI(TAG, "[%s] Logging into AM43", this->get_name().c_str());
|
||||||
if (status)
|
if (status) {
|
||||||
ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", this->get_name().c_str(), status);
|
ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", this->get_name().c_str(), status);
|
||||||
else
|
} else {
|
||||||
this->logged_in_ = true;
|
this->logged_in_ = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CoverTraits Am43Component::get_traits() {
|
CoverTraits Am43Component::get_traits() {
|
||||||
@@ -46,12 +47,12 @@ CoverTraits Am43Component::get_traits() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Am43Component::control(const CoverCall &call) {
|
void Am43Component::control(const CoverCall &call) {
|
||||||
if (this->node_state != espbt::ClientState::Established) {
|
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||||
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str());
|
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (call.get_stop()) {
|
if (call.get_stop()) {
|
||||||
auto packet = this->encoder_->get_stop_request();
|
auto *packet = this->encoder_->get_stop_request();
|
||||||
auto status =
|
auto status =
|
||||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||||
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
@@ -63,7 +64,7 @@ void Am43Component::control(const CoverCall &call) {
|
|||||||
|
|
||||||
if (this->invert_position_)
|
if (this->invert_position_)
|
||||||
pos = 1 - pos;
|
pos = 1 - pos;
|
||||||
auto packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100));
|
auto *packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100));
|
||||||
auto status =
|
auto status =
|
||||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||||
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
@@ -80,7 +81,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||||
auto chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
|
auto *chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
|
||||||
if (chr == nullptr) {
|
if (chr == nullptr) {
|
||||||
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
|
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
|
||||||
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", this->get_name().c_str());
|
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", this->get_name().c_str());
|
||||||
@@ -98,7 +99,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
this->node_state = espbt::ClientState::Established;
|
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_NOTIFY_EVT: {
|
case ESP_GATTC_NOTIFY_EVT: {
|
||||||
@@ -120,7 +121,7 @@ void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_
|
|||||||
if (this->decoder_->has_pin_response()) {
|
if (this->decoder_->has_pin_response()) {
|
||||||
if (this->decoder_->pin_ok_) {
|
if (this->decoder_->pin_ok_) {
|
||||||
ESP_LOGI(TAG, "[%s] AM43 pin accepted.", this->get_name().c_str());
|
ESP_LOGI(TAG, "[%s] AM43 pin accepted.", this->get_name().c_str());
|
||||||
auto packet = this->encoder_->get_position_request();
|
auto *packet = this->encoder_->get_position_request();
|
||||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||||
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
|
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
|
||||||
ESP_GATT_AUTH_REQ_NONE);
|
ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "esphome/components/cover/cover.h"
|
#include "esphome/components/cover/cover.h"
|
||||||
#include "esphome/components/am43/am43_base.h"
|
#include "esphome/components/am43/am43_base.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include <esp_gattc_api.h>
|
#include <esp_gattc_api.h>
|
||||||
|
|
||||||
@@ -32,8 +32,8 @@ class Am43Component : public cover::Cover, public esphome::ble_client::BLEClient
|
|||||||
uint16_t char_handle_;
|
uint16_t char_handle_;
|
||||||
uint16_t pin_;
|
uint16_t pin_;
|
||||||
bool invert_position_;
|
bool invert_position_;
|
||||||
Am43Encoder *encoder_;
|
std::unique_ptr<Am43Encoder> encoder_;
|
||||||
Am43Decoder *decoder_;
|
std::unique_ptr<Am43Decoder> decoder_;
|
||||||
bool logged_in_;
|
bool logged_in_;
|
||||||
|
|
||||||
float position_;
|
float position_;
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ from esphome.components import sensor, ble_client
|
|||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_BATTERY_LEVEL,
|
CONF_BATTERY_LEVEL,
|
||||||
ICON_BATTERY,
|
DEVICE_CLASS_BATTERY,
|
||||||
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
CONF_ILLUMINANCE,
|
CONF_ILLUMINANCE,
|
||||||
ICON_BRIGHTNESS_5,
|
ICON_BRIGHTNESS_5,
|
||||||
UNIT_PERCENT,
|
UNIT_PERCENT,
|
||||||
@@ -20,10 +21,15 @@ CONFIG_SCHEMA = (
|
|||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(Am43),
|
cv.GenerateID(): cv.declare_id(Am43),
|
||||||
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
|
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
|
||||||
UNIT_PERCENT, ICON_BATTERY, 0
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
device_class=DEVICE_CLASS_BATTERY,
|
||||||
|
accuracy_decimals=0,
|
||||||
|
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
|
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
|
||||||
UNIT_PERCENT, ICON_BRIGHTNESS_5, 0
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
icon=ICON_BRIGHTNESS_5,
|
||||||
|
accuracy_decimals=0,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
1
esphome/components/analog_threshold/__init__.py
Normal file
1
esphome/components/analog_threshold/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CODEOWNERS = ["@ianchi"]
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#include "analog_threshold_binary_sensor.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace analog_threshold {
|
||||||
|
|
||||||
|
static const char *const TAG = "analog_threshold.binary_sensor";
|
||||||
|
|
||||||
|
void AnalogThresholdBinarySensor::setup() {
|
||||||
|
float sensor_value = this->sensor_->get_state();
|
||||||
|
|
||||||
|
// TRUE state is defined to be when sensor is >= threshold
|
||||||
|
// so when undefined sensor value initialize to FALSE
|
||||||
|
if (std::isnan(sensor_value)) {
|
||||||
|
this->publish_initial_state(false);
|
||||||
|
} else {
|
||||||
|
this->publish_initial_state(sensor_value >= (this->lower_threshold_ + this->upper_threshold_) / 2.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) {
|
||||||
|
this->sensor_ = analog_sensor;
|
||||||
|
|
||||||
|
this->sensor_->add_on_state_callback([this](float sensor_value) {
|
||||||
|
// if there is an invalid sensor reading, ignore the change and keep the current state
|
||||||
|
if (!std::isnan(sensor_value)) {
|
||||||
|
this->publish_state(sensor_value >= (this->state ? this->lower_threshold_ : this->upper_threshold_));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnalogThresholdBinarySensor::dump_config() {
|
||||||
|
LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this);
|
||||||
|
LOG_SENSOR(" ", "Sensor", this->sensor_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Upper threshold: %.11f", this->upper_threshold_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Lower threshold: %.11f", this->lower_threshold_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace analog_threshold
|
||||||
|
} // namespace esphome
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace analog_threshold {
|
||||||
|
|
||||||
|
class AnalogThresholdBinarySensor : public Component, public binary_sensor::BinarySensor {
|
||||||
|
public:
|
||||||
|
void dump_config() override;
|
||||||
|
void setup() override;
|
||||||
|
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
void set_sensor(sensor::Sensor *analog_sensor);
|
||||||
|
void set_upper_threshold(float threshold) { this->upper_threshold_ = threshold; }
|
||||||
|
void set_lower_threshold(float threshold) { this->lower_threshold_ = threshold; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
sensor::Sensor *sensor_{nullptr};
|
||||||
|
|
||||||
|
float upper_threshold_;
|
||||||
|
float lower_threshold_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace analog_threshold
|
||||||
|
} // namespace esphome
|
||||||
44
esphome/components/analog_threshold/binary_sensor.py
Normal file
44
esphome/components/analog_threshold/binary_sensor.py
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import binary_sensor, sensor
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_SENSOR_ID,
|
||||||
|
CONF_THRESHOLD,
|
||||||
|
)
|
||||||
|
|
||||||
|
analog_threshold_ns = cg.esphome_ns.namespace("analog_threshold")
|
||||||
|
|
||||||
|
AnalogThresholdBinarySensor = analog_threshold_ns.class_(
|
||||||
|
"AnalogThresholdBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||||
|
)
|
||||||
|
|
||||||
|
CONF_UPPER = "upper"
|
||||||
|
CONF_LOWER = "lower"
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(AnalogThresholdBinarySensor),
|
||||||
|
cv.Required(CONF_SENSOR_ID): cv.use_id(sensor.Sensor),
|
||||||
|
cv.Required(CONF_THRESHOLD): cv.Any(
|
||||||
|
cv.float_,
|
||||||
|
cv.Schema(
|
||||||
|
{cv.Required(CONF_UPPER): cv.float_, cv.Required(CONF_LOWER): cv.float_}
|
||||||
|
),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = await binary_sensor.new_binary_sensor(config)
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
|
sens = await cg.get_variable(config[CONF_SENSOR_ID])
|
||||||
|
cg.add(var.set_sensor(sens))
|
||||||
|
|
||||||
|
if isinstance(config[CONF_THRESHOLD], float):
|
||||||
|
cg.add(var.set_upper_threshold(config[CONF_THRESHOLD]))
|
||||||
|
cg.add(var.set_lower_threshold(config[CONF_THRESHOLD]))
|
||||||
|
else:
|
||||||
|
cg.add(var.set_upper_threshold(config[CONF_THRESHOLD][CONF_UPPER]))
|
||||||
|
cg.add(var.set_lower_threshold(config[CONF_THRESHOLD][CONF_LOWER]))
|
||||||
@@ -5,7 +5,7 @@ from esphome.components import display, font
|
|||||||
import esphome.components.image as espImage
|
import esphome.components.image as espImage
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.const import CONF_FILE, CONF_ID, CONF_TYPE, CONF_RESIZE
|
from esphome.const import CONF_FILE, CONF_ID, CONF_RAW_DATA_ID, CONF_RESIZE, CONF_TYPE
|
||||||
from esphome.core import CORE, HexInt
|
from esphome.core import CORE, HexInt
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -15,8 +15,6 @@ MULTI_CONF = True
|
|||||||
|
|
||||||
Animation_ = display.display_ns.class_("Animation")
|
Animation_ = display.display_ns.class_("Animation")
|
||||||
|
|
||||||
CONF_RAW_DATA_ID = "raw_data_id"
|
|
||||||
|
|
||||||
ANIMATION_SCHEMA = cv.Schema(
|
ANIMATION_SCHEMA = cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_ID): cv.declare_id(Animation_),
|
cv.Required(CONF_ID): cv.declare_id(Animation_),
|
||||||
@@ -46,8 +44,9 @@ async def to_code(config):
|
|||||||
width, height = image.size
|
width, height = image.size
|
||||||
frames = image.n_frames
|
frames = image.n_frames
|
||||||
if CONF_RESIZE in config:
|
if CONF_RESIZE in config:
|
||||||
image.thumbnail(config[CONF_RESIZE])
|
new_width_max, new_height_max = config[CONF_RESIZE]
|
||||||
width, height = image.size
|
ratio = min(new_width_max / width, new_height_max / height)
|
||||||
|
width, height = int(width * ratio), int(height * ratio)
|
||||||
else:
|
else:
|
||||||
if width > 500 or height > 500:
|
if width > 500 or height > 500:
|
||||||
_LOGGER.warning(
|
_LOGGER.warning(
|
||||||
@@ -61,7 +60,13 @@ async def to_code(config):
|
|||||||
for frameIndex in range(frames):
|
for frameIndex in range(frames):
|
||||||
image.seek(frameIndex)
|
image.seek(frameIndex)
|
||||||
frame = image.convert("L", dither=Image.NONE)
|
frame = image.convert("L", dither=Image.NONE)
|
||||||
|
if CONF_RESIZE in config:
|
||||||
|
frame = frame.resize([width, height])
|
||||||
pixels = list(frame.getdata())
|
pixels = list(frame.getdata())
|
||||||
|
if len(pixels) != height * width:
|
||||||
|
raise core.EsphomeError(
|
||||||
|
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
|
||||||
|
)
|
||||||
for pix in pixels:
|
for pix in pixels:
|
||||||
data[pos] = pix
|
data[pos] = pix
|
||||||
pos += 1
|
pos += 1
|
||||||
@@ -71,8 +76,16 @@ async def to_code(config):
|
|||||||
pos = 0
|
pos = 0
|
||||||
for frameIndex in range(frames):
|
for frameIndex in range(frames):
|
||||||
image.seek(frameIndex)
|
image.seek(frameIndex)
|
||||||
|
if CONF_RESIZE in config:
|
||||||
|
image.thumbnail(config[CONF_RESIZE])
|
||||||
frame = image.convert("RGB")
|
frame = image.convert("RGB")
|
||||||
|
if CONF_RESIZE in config:
|
||||||
|
frame = frame.resize([width, height])
|
||||||
pixels = list(frame.getdata())
|
pixels = list(frame.getdata())
|
||||||
|
if len(pixels) != height * width:
|
||||||
|
raise core.EsphomeError(
|
||||||
|
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
|
||||||
|
)
|
||||||
for pix in pixels:
|
for pix in pixels:
|
||||||
data[pos] = pix[0]
|
data[pos] = pix[0]
|
||||||
pos += 1
|
pos += 1
|
||||||
@@ -81,12 +94,37 @@ async def to_code(config):
|
|||||||
data[pos] = pix[2]
|
data[pos] = pix[2]
|
||||||
pos += 1
|
pos += 1
|
||||||
|
|
||||||
|
elif config[CONF_TYPE] == "RGB565":
|
||||||
|
data = [0 for _ in range(height * width * 2 * frames)]
|
||||||
|
pos = 0
|
||||||
|
for frameIndex in range(frames):
|
||||||
|
image.seek(frameIndex)
|
||||||
|
frame = image.convert("RGB")
|
||||||
|
if CONF_RESIZE in config:
|
||||||
|
frame = frame.resize([width, height])
|
||||||
|
pixels = list(frame.getdata())
|
||||||
|
if len(pixels) != height * width:
|
||||||
|
raise core.EsphomeError(
|
||||||
|
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height*width})"
|
||||||
|
)
|
||||||
|
for pix in pixels:
|
||||||
|
R = pix[0] >> 3
|
||||||
|
G = pix[1] >> 2
|
||||||
|
B = pix[2] >> 3
|
||||||
|
rgb = (R << 11) | (G << 5) | B
|
||||||
|
data[pos] = rgb >> 8
|
||||||
|
pos += 1
|
||||||
|
data[pos] = rgb & 255
|
||||||
|
pos += 1
|
||||||
|
|
||||||
elif config[CONF_TYPE] == "BINARY":
|
elif config[CONF_TYPE] == "BINARY":
|
||||||
width8 = ((width + 7) // 8) * 8
|
width8 = ((width + 7) // 8) * 8
|
||||||
data = [0 for _ in range((height * width8 // 8) * frames)]
|
data = [0 for _ in range((height * width8 // 8) * frames)]
|
||||||
for frameIndex in range(frames):
|
for frameIndex in range(frames):
|
||||||
image.seek(frameIndex)
|
image.seek(frameIndex)
|
||||||
frame = image.convert("1", dither=Image.NONE)
|
frame = image.convert("1", dither=Image.NONE)
|
||||||
|
if CONF_RESIZE in config:
|
||||||
|
frame = frame.resize([width, height])
|
||||||
for y in range(height):
|
for y in range(height):
|
||||||
for x in range(width):
|
for x in range(width):
|
||||||
if frame.getpixel((x, y)):
|
if frame.getpixel((x, y)):
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
#include "anova.h"
|
#include "anova.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace anova {
|
namespace anova {
|
||||||
|
|
||||||
static const char *TAG = "anova";
|
static const char *const TAG = "anova";
|
||||||
|
|
||||||
using namespace esphome::climate;
|
using namespace esphome::climate;
|
||||||
|
|
||||||
void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
|
void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
|
||||||
|
|
||||||
void Anova::setup() {
|
void Anova::setup() {
|
||||||
this->codec_ = new AnovaCodec();
|
this->codec_ = make_unique<AnovaCodec>();
|
||||||
this->current_request_ = 0;
|
this->current_request_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ void Anova::control(const ClimateCall &call) {
|
|||||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
||||||
}
|
}
|
||||||
if (call.get_target_temperature().has_value()) {
|
if (call.get_target_temperature().has_value()) {
|
||||||
auto pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature());
|
auto *pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature());
|
||||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||||
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
if (status)
|
if (status)
|
||||||
@@ -57,7 +57,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||||
auto chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
|
auto *chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
|
||||||
if (chr == nullptr) {
|
if (chr == nullptr) {
|
||||||
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
|
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
|
||||||
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
|
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
|
||||||
@@ -72,7 +72,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
this->node_state = espbt::ClientState::Established;
|
this->node_state = espbt::ClientState::ESTABLISHED;
|
||||||
this->current_request_ = 0;
|
this->current_request_ = 0;
|
||||||
this->update();
|
this->update();
|
||||||
break;
|
break;
|
||||||
@@ -92,7 +92,7 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
|
|||||||
}
|
}
|
||||||
if (this->codec_->has_unit()) {
|
if (this->codec_->has_unit()) {
|
||||||
this->fahrenheit_ = (this->codec_->unit_ == 'f');
|
this->fahrenheit_ = (this->codec_->unit_ == 'f');
|
||||||
ESP_LOGD(TAG, "Anova units is %s", this->fahrenheit_ ? "fahrenheit" : "celcius");
|
ESP_LOGD(TAG, "Anova units is %s", this->fahrenheit_ ? "fahrenheit" : "celsius");
|
||||||
this->current_request_++;
|
this->current_request_++;
|
||||||
}
|
}
|
||||||
this->publish_state();
|
this->publish_state();
|
||||||
@@ -114,11 +114,12 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
|
|||||||
auto status =
|
auto status =
|
||||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length,
|
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length,
|
||||||
pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
if (status)
|
if (status) {
|
||||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
|
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
|
||||||
status);
|
status);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -129,13 +130,13 @@ void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_
|
|||||||
void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); }
|
void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); }
|
||||||
|
|
||||||
void Anova::update() {
|
void Anova::update() {
|
||||||
if (this->node_state != espbt::ClientState::Established)
|
if (this->node_state != espbt::ClientState::ESTABLISHED)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (this->current_request_ < 2) {
|
if (this->current_request_ < 2) {
|
||||||
auto pkt = this->codec_->get_read_device_status_request();
|
auto *pkt = this->codec_->get_read_device_status_request();
|
||||||
if (this->current_request_ == 0)
|
if (this->current_request_ == 0)
|
||||||
auto pkt = this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
|
this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
|
||||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||||
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
if (status)
|
if (status)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "esphome/components/climate/climate.h"
|
#include "esphome/components/climate/climate.h"
|
||||||
#include "anova_base.h"
|
#include "anova_base.h"
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include <esp_gattc_api.h>
|
#include <esp_gattc_api.h>
|
||||||
|
|
||||||
@@ -27,19 +27,19 @@ class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode
|
|||||||
esp_ble_gattc_cb_param_t *param) override;
|
esp_ble_gattc_cb_param_t *param) override;
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
climate::ClimateTraits traits() {
|
climate::ClimateTraits traits() override {
|
||||||
auto traits = climate::ClimateTraits();
|
auto traits = climate::ClimateTraits();
|
||||||
traits.set_supports_current_temperature(true);
|
traits.set_supports_current_temperature(true);
|
||||||
traits.set_supports_heat_mode(true);
|
traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::ClimateMode::CLIMATE_MODE_HEAT});
|
||||||
traits.set_visual_min_temperature(25.0);
|
traits.set_visual_min_temperature(25.0);
|
||||||
traits.set_visual_max_temperature(100.0);
|
traits.set_visual_max_temperature(100.0);
|
||||||
traits.set_visual_temperature_step(0.1);
|
traits.set_visual_temperature_step(0.1);
|
||||||
return traits;
|
return traits;
|
||||||
}
|
}
|
||||||
void set_unit_of_measurement(const char *);
|
void set_unit_of_measurement(const char *unit);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
AnovaCodec *codec_;
|
std::unique_ptr<AnovaCodec> codec_;
|
||||||
void control(const climate::ClimateCall &call) override;
|
void control(const climate::ClimateCall &call) override;
|
||||||
uint16_t char_handle_;
|
uint16_t char_handle_;
|
||||||
uint8_t current_request_;
|
uint8_t current_request_;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#include "anova_base.h"
|
#include "anova_base.h"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace anova {
|
namespace anova {
|
||||||
@@ -71,51 +73,46 @@ AnovaPacket *AnovaCodec::get_stop_request() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
||||||
memset(this->buf_, 0, 32);
|
char buf[32];
|
||||||
strncpy(this->buf_, (char *) data, length);
|
memset(buf, 0, sizeof(buf));
|
||||||
|
strncpy(buf, (char *) data, std::min<uint16_t>(length, sizeof(buf) - 1));
|
||||||
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
|
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
|
||||||
switch (this->current_query_) {
|
switch (this->current_query_) {
|
||||||
case READ_DEVICE_STATUS: {
|
case READ_DEVICE_STATUS: {
|
||||||
if (!strncmp(this->buf_, "stopped", 7)) {
|
if (!strncmp(buf, "stopped", 7)) {
|
||||||
this->has_running_ = true;
|
this->has_running_ = true;
|
||||||
this->running_ = false;
|
this->running_ = false;
|
||||||
}
|
}
|
||||||
if (!strncmp(this->buf_, "running", 7)) {
|
if (!strncmp(buf, "running", 7)) {
|
||||||
this->has_running_ = true;
|
this->has_running_ = true;
|
||||||
this->running_ = true;
|
this->running_ = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case START: {
|
case START: {
|
||||||
if (!strncmp(this->buf_, "start", 5)) {
|
if (!strncmp(buf, "start", 5)) {
|
||||||
this->has_running_ = true;
|
this->has_running_ = true;
|
||||||
this->running_ = true;
|
this->running_ = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case STOP: {
|
case STOP: {
|
||||||
if (!strncmp(this->buf_, "stop", 4)) {
|
if (!strncmp(buf, "stop", 4)) {
|
||||||
this->has_running_ = true;
|
this->has_running_ = true;
|
||||||
this->running_ = false;
|
this->running_ = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case READ_TARGET_TEMPERATURE: {
|
case READ_TARGET_TEMPERATURE:
|
||||||
this->target_temp_ = strtof(this->buf_, nullptr);
|
|
||||||
if (this->fahrenheit_)
|
|
||||||
this->target_temp_ = ftoc(this->target_temp_);
|
|
||||||
this->has_target_temp_ = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SET_TARGET_TEMPERATURE: {
|
case SET_TARGET_TEMPERATURE: {
|
||||||
this->target_temp_ = strtof(this->buf_, nullptr);
|
this->target_temp_ = parse_number<float>(str_until(buf, '\r')).value_or(0.0f);
|
||||||
if (this->fahrenheit_)
|
if (this->fahrenheit_)
|
||||||
this->target_temp_ = ftoc(this->target_temp_);
|
this->target_temp_ = ftoc(this->target_temp_);
|
||||||
this->has_target_temp_ = true;
|
this->has_target_temp_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case READ_CURRENT_TEMPERATURE: {
|
case READ_CURRENT_TEMPERATURE: {
|
||||||
this->current_temp_ = strtof(this->buf_, nullptr);
|
this->current_temp_ = parse_number<float>(str_until(buf, '\r')).value_or(0.0f);
|
||||||
if (this->fahrenheit_)
|
if (this->fahrenheit_)
|
||||||
this->current_temp_ = ftoc(this->current_temp_);
|
this->current_temp_ = ftoc(this->current_temp_);
|
||||||
this->has_current_temp_ = true;
|
this->has_current_temp_ = true;
|
||||||
@@ -123,8 +120,8 @@ void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
|||||||
}
|
}
|
||||||
case SET_UNIT:
|
case SET_UNIT:
|
||||||
case READ_UNIT: {
|
case READ_UNIT: {
|
||||||
this->unit_ = this->buf_[0];
|
this->unit_ = buf[0];
|
||||||
this->fahrenheit_ = this->buf_[0] == 'f';
|
this->fahrenheit_ = buf[0] == 'f';
|
||||||
this->has_unit_ = true;
|
this->has_unit_ = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -70,7 +70,6 @@ class AnovaCodec {
|
|||||||
bool has_current_temp_;
|
bool has_current_temp_;
|
||||||
bool has_unit_;
|
bool has_unit_;
|
||||||
bool has_running_;
|
bool has_running_;
|
||||||
char buf_[32];
|
|
||||||
bool fahrenheit_;
|
bool fahrenheit_;
|
||||||
|
|
||||||
CurrentQuery current_query_;
|
CurrentQuery current_query_;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "apds9960.h"
|
#include "apds9960.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace apds9960 {
|
namespace apds9960 {
|
||||||
@@ -224,9 +225,10 @@ void APDS9960::read_gesture_data_() {
|
|||||||
|
|
||||||
uint8_t fifo_level;
|
uint8_t fifo_level;
|
||||||
APDS9960_WARNING_CHECK(this->read_byte(0xAE, &fifo_level), "Reading FIFO level failed.");
|
APDS9960_WARNING_CHECK(this->read_byte(0xAE, &fifo_level), "Reading FIFO level failed.");
|
||||||
if (fifo_level == 0)
|
if (fifo_level == 0) {
|
||||||
// no data to process
|
// no data to process
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
APDS9960_WARNING_CHECK(fifo_level <= 32, "FIFO level has invalid value.")
|
APDS9960_WARNING_CHECK(fifo_level <= 32, "FIFO level has invalid value.")
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import binary_sensor
|
from esphome.components import binary_sensor
|
||||||
from esphome.const import CONF_DIRECTION, CONF_DEVICE_CLASS, DEVICE_CLASS_MOVING
|
from esphome.const import CONF_DIRECTION, DEVICE_CLASS_MOVING
|
||||||
from . import APDS9960, CONF_APDS9960_ID
|
from . import APDS9960, CONF_APDS9960_ID
|
||||||
|
|
||||||
DEPENDENCIES = ["apds9960"]
|
DEPENDENCIES = ["apds9960"]
|
||||||
@@ -13,13 +13,12 @@ DIRECTIONS = {
|
|||||||
"RIGHT": "set_right_direction",
|
"RIGHT": "set_right_direction",
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema(
|
||||||
|
device_class=DEVICE_CLASS_MOVING
|
||||||
|
).extend(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
|
|
||||||
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
|
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
|
||||||
cv.Optional(
|
cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
|
||||||
CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING
|
|
||||||
): binary_sensor.device_class,
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ async def to_code(config):
|
|||||||
decoded = base64.b64decode(conf[CONF_KEY])
|
decoded = base64.b64decode(conf[CONF_KEY])
|
||||||
cg.add(var.set_noise_psk(list(decoded)))
|
cg.add(var.set_noise_psk(list(decoded)))
|
||||||
cg.add_define("USE_API_NOISE")
|
cg.add_define("USE_API_NOISE")
|
||||||
cg.add_library("esphome/noise-c", "0.1.1")
|
cg.add_library("esphome/noise-c", "0.1.4")
|
||||||
else:
|
else:
|
||||||
cg.add_define("USE_API_PLAINTEXT")
|
cg.add_define("USE_API_PLAINTEXT")
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ service APIConnection {
|
|||||||
rpc climate_command (ClimateCommandRequest) returns (void) {}
|
rpc climate_command (ClimateCommandRequest) returns (void) {}
|
||||||
rpc number_command (NumberCommandRequest) returns (void) {}
|
rpc number_command (NumberCommandRequest) returns (void) {}
|
||||||
rpc select_command (SelectCommandRequest) returns (void) {}
|
rpc select_command (SelectCommandRequest) returns (void) {}
|
||||||
|
rpc button_command (ButtonCommandRequest) returns (void) {}
|
||||||
|
rpc lock_command (LockCommandRequest) returns (void) {}
|
||||||
|
rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -94,6 +97,9 @@ message HelloResponse {
|
|||||||
// and only exists for debugging/logging purposes.
|
// and only exists for debugging/logging purposes.
|
||||||
// For example "ESPHome v1.10.0 on ESP8266"
|
// For example "ESPHome v1.10.0 on ESP8266"
|
||||||
string server_info = 3;
|
string server_info = 3;
|
||||||
|
|
||||||
|
// The name of the server (App.get_name())
|
||||||
|
string name = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message sent at the beginning of each connection to authenticate the client
|
// Message sent at the beginning of each connection to authenticate the client
|
||||||
@@ -182,6 +188,8 @@ message DeviceInfoResponse {
|
|||||||
// The esphome project details if set
|
// The esphome project details if set
|
||||||
string project_name = 8;
|
string project_name = 8;
|
||||||
string project_version = 9;
|
string project_version = 9;
|
||||||
|
|
||||||
|
uint32 webserver_port = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListEntitiesRequest {
|
message ListEntitiesRequest {
|
||||||
@@ -201,6 +209,14 @@ message SubscribeStatesRequest {
|
|||||||
// Empty
|
// Empty
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== COMMON =====================
|
||||||
|
|
||||||
|
enum EntityCategory {
|
||||||
|
ENTITY_CATEGORY_NONE = 0;
|
||||||
|
ENTITY_CATEGORY_CONFIG = 1;
|
||||||
|
ENTITY_CATEGORY_DIAGNOSTIC = 2;
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== BINARY SENSOR ====================
|
// ==================== BINARY SENSOR ====================
|
||||||
message ListEntitiesBinarySensorResponse {
|
message ListEntitiesBinarySensorResponse {
|
||||||
option (id) = 12;
|
option (id) = 12;
|
||||||
@@ -215,6 +231,8 @@ message ListEntitiesBinarySensorResponse {
|
|||||||
string device_class = 5;
|
string device_class = 5;
|
||||||
bool is_status_binary_sensor = 6;
|
bool is_status_binary_sensor = 6;
|
||||||
bool disabled_by_default = 7;
|
bool disabled_by_default = 7;
|
||||||
|
string icon = 8;
|
||||||
|
EntityCategory entity_category = 9;
|
||||||
}
|
}
|
||||||
message BinarySensorStateResponse {
|
message BinarySensorStateResponse {
|
||||||
option (id) = 21;
|
option (id) = 21;
|
||||||
@@ -245,6 +263,8 @@ message ListEntitiesCoverResponse {
|
|||||||
bool supports_tilt = 7;
|
bool supports_tilt = 7;
|
||||||
string device_class = 8;
|
string device_class = 8;
|
||||||
bool disabled_by_default = 9;
|
bool disabled_by_default = 9;
|
||||||
|
string icon = 10;
|
||||||
|
EntityCategory entity_category = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum LegacyCoverState {
|
enum LegacyCoverState {
|
||||||
@@ -313,6 +333,8 @@ message ListEntitiesFanResponse {
|
|||||||
bool supports_direction = 7;
|
bool supports_direction = 7;
|
||||||
int32 supported_speed_count = 8;
|
int32 supported_speed_count = 8;
|
||||||
bool disabled_by_default = 9;
|
bool disabled_by_default = 9;
|
||||||
|
string icon = 10;
|
||||||
|
EntityCategory entity_category = 11;
|
||||||
}
|
}
|
||||||
enum FanSpeed {
|
enum FanSpeed {
|
||||||
FAN_SPEED_LOW = 0;
|
FAN_SPEED_LOW = 0;
|
||||||
@@ -388,6 +410,8 @@ message ListEntitiesLightResponse {
|
|||||||
float max_mireds = 10;
|
float max_mireds = 10;
|
||||||
repeated string effects = 11;
|
repeated string effects = 11;
|
||||||
bool disabled_by_default = 13;
|
bool disabled_by_default = 13;
|
||||||
|
string icon = 14;
|
||||||
|
EntityCategory entity_category = 15;
|
||||||
}
|
}
|
||||||
message LightStateResponse {
|
message LightStateResponse {
|
||||||
option (id) = 24;
|
option (id) = 24;
|
||||||
@@ -449,6 +473,7 @@ enum SensorStateClass {
|
|||||||
STATE_CLASS_NONE = 0;
|
STATE_CLASS_NONE = 0;
|
||||||
STATE_CLASS_MEASUREMENT = 1;
|
STATE_CLASS_MEASUREMENT = 1;
|
||||||
STATE_CLASS_TOTAL_INCREASING = 2;
|
STATE_CLASS_TOTAL_INCREASING = 2;
|
||||||
|
STATE_CLASS_TOTAL = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SensorLastResetType {
|
enum SensorLastResetType {
|
||||||
@@ -476,6 +501,7 @@ message ListEntitiesSensorResponse {
|
|||||||
// Last reset type removed in 2021.9.0
|
// Last reset type removed in 2021.9.0
|
||||||
SensorLastResetType legacy_last_reset_type = 11;
|
SensorLastResetType legacy_last_reset_type = 11;
|
||||||
bool disabled_by_default = 12;
|
bool disabled_by_default = 12;
|
||||||
|
EntityCategory entity_category = 13;
|
||||||
}
|
}
|
||||||
message SensorStateResponse {
|
message SensorStateResponse {
|
||||||
option (id) = 25;
|
option (id) = 25;
|
||||||
@@ -504,6 +530,8 @@ message ListEntitiesSwitchResponse {
|
|||||||
string icon = 5;
|
string icon = 5;
|
||||||
bool assumed_state = 6;
|
bool assumed_state = 6;
|
||||||
bool disabled_by_default = 7;
|
bool disabled_by_default = 7;
|
||||||
|
EntityCategory entity_category = 8;
|
||||||
|
string device_class = 9;
|
||||||
}
|
}
|
||||||
message SwitchStateResponse {
|
message SwitchStateResponse {
|
||||||
option (id) = 26;
|
option (id) = 26;
|
||||||
@@ -537,6 +565,7 @@ message ListEntitiesTextSensorResponse {
|
|||||||
|
|
||||||
string icon = 5;
|
string icon = 5;
|
||||||
bool disabled_by_default = 6;
|
bool disabled_by_default = 6;
|
||||||
|
EntityCategory entity_category = 7;
|
||||||
}
|
}
|
||||||
message TextSensorStateResponse {
|
message TextSensorStateResponse {
|
||||||
option (id) = 27;
|
option (id) = 27;
|
||||||
@@ -697,6 +726,8 @@ message ListEntitiesCameraResponse {
|
|||||||
string name = 3;
|
string name = 3;
|
||||||
string unique_id = 4;
|
string unique_id = 4;
|
||||||
bool disabled_by_default = 5;
|
bool disabled_by_default = 5;
|
||||||
|
string icon = 6;
|
||||||
|
EntityCategory entity_category = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CameraImageResponse {
|
message CameraImageResponse {
|
||||||
@@ -790,6 +821,8 @@ message ListEntitiesClimateResponse {
|
|||||||
repeated ClimatePreset supported_presets = 16;
|
repeated ClimatePreset supported_presets = 16;
|
||||||
repeated string supported_custom_presets = 17;
|
repeated string supported_custom_presets = 17;
|
||||||
bool disabled_by_default = 18;
|
bool disabled_by_default = 18;
|
||||||
|
string icon = 19;
|
||||||
|
EntityCategory entity_category = 20;
|
||||||
}
|
}
|
||||||
message ClimateStateResponse {
|
message ClimateStateResponse {
|
||||||
option (id) = 47;
|
option (id) = 47;
|
||||||
@@ -843,6 +876,11 @@ message ClimateCommandRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==================== NUMBER ====================
|
// ==================== NUMBER ====================
|
||||||
|
enum NumberMode {
|
||||||
|
NUMBER_MODE_AUTO = 0;
|
||||||
|
NUMBER_MODE_BOX = 1;
|
||||||
|
NUMBER_MODE_SLIDER = 2;
|
||||||
|
}
|
||||||
message ListEntitiesNumberResponse {
|
message ListEntitiesNumberResponse {
|
||||||
option (id) = 49;
|
option (id) = 49;
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
@@ -858,6 +896,9 @@ message ListEntitiesNumberResponse {
|
|||||||
float max_value = 7;
|
float max_value = 7;
|
||||||
float step = 8;
|
float step = 8;
|
||||||
bool disabled_by_default = 9;
|
bool disabled_by_default = 9;
|
||||||
|
EntityCategory entity_category = 10;
|
||||||
|
string unit_of_measurement = 11;
|
||||||
|
NumberMode mode = 12;
|
||||||
}
|
}
|
||||||
message NumberStateResponse {
|
message NumberStateResponse {
|
||||||
option (id) = 50;
|
option (id) = 50;
|
||||||
@@ -895,6 +936,7 @@ message ListEntitiesSelectResponse {
|
|||||||
string icon = 5;
|
string icon = 5;
|
||||||
repeated string options = 6;
|
repeated string options = 6;
|
||||||
bool disabled_by_default = 7;
|
bool disabled_by_default = 7;
|
||||||
|
EntityCategory entity_category = 8;
|
||||||
}
|
}
|
||||||
message SelectStateResponse {
|
message SelectStateResponse {
|
||||||
option (id) = 53;
|
option (id) = 53;
|
||||||
@@ -917,3 +959,143 @@ message SelectCommandRequest {
|
|||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
string state = 2;
|
string state = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ==================== LOCK ====================
|
||||||
|
enum LockState {
|
||||||
|
LOCK_STATE_NONE = 0;
|
||||||
|
LOCK_STATE_LOCKED = 1;
|
||||||
|
LOCK_STATE_UNLOCKED = 2;
|
||||||
|
LOCK_STATE_JAMMED = 3;
|
||||||
|
LOCK_STATE_LOCKING = 4;
|
||||||
|
LOCK_STATE_UNLOCKING = 5;
|
||||||
|
}
|
||||||
|
enum LockCommand {
|
||||||
|
LOCK_UNLOCK = 0;
|
||||||
|
LOCK_LOCK = 1;
|
||||||
|
LOCK_OPEN = 2;
|
||||||
|
}
|
||||||
|
message ListEntitiesLockResponse {
|
||||||
|
option (id) = 58;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_LOCK";
|
||||||
|
|
||||||
|
string object_id = 1;
|
||||||
|
fixed32 key = 2;
|
||||||
|
string name = 3;
|
||||||
|
string unique_id = 4;
|
||||||
|
|
||||||
|
string icon = 5;
|
||||||
|
bool disabled_by_default = 6;
|
||||||
|
EntityCategory entity_category = 7;
|
||||||
|
bool assumed_state = 8;
|
||||||
|
|
||||||
|
bool supports_open = 9;
|
||||||
|
bool requires_code = 10;
|
||||||
|
|
||||||
|
// Not yet implemented:
|
||||||
|
string code_format = 11;
|
||||||
|
}
|
||||||
|
message LockStateResponse {
|
||||||
|
option (id) = 59;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_LOCK";
|
||||||
|
option (no_delay) = true;
|
||||||
|
fixed32 key = 1;
|
||||||
|
LockState state = 2;
|
||||||
|
}
|
||||||
|
message LockCommandRequest {
|
||||||
|
option (id) = 60;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_LOCK";
|
||||||
|
option (no_delay) = true;
|
||||||
|
fixed32 key = 1;
|
||||||
|
LockCommand command = 2;
|
||||||
|
|
||||||
|
// Not yet implemented:
|
||||||
|
bool has_code = 3;
|
||||||
|
string code = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== BUTTON ====================
|
||||||
|
message ListEntitiesButtonResponse {
|
||||||
|
option (id) = 61;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_BUTTON";
|
||||||
|
|
||||||
|
string object_id = 1;
|
||||||
|
fixed32 key = 2;
|
||||||
|
string name = 3;
|
||||||
|
string unique_id = 4;
|
||||||
|
|
||||||
|
string icon = 5;
|
||||||
|
bool disabled_by_default = 6;
|
||||||
|
EntityCategory entity_category = 7;
|
||||||
|
string device_class = 8;
|
||||||
|
}
|
||||||
|
message ButtonCommandRequest {
|
||||||
|
option (id) = 62;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_BUTTON";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== MEDIA PLAYER ====================
|
||||||
|
enum MediaPlayerState {
|
||||||
|
MEDIA_PLAYER_STATE_NONE = 0;
|
||||||
|
MEDIA_PLAYER_STATE_IDLE = 1;
|
||||||
|
MEDIA_PLAYER_STATE_PLAYING = 2;
|
||||||
|
MEDIA_PLAYER_STATE_PAUSED = 3;
|
||||||
|
}
|
||||||
|
enum MediaPlayerCommand {
|
||||||
|
MEDIA_PLAYER_COMMAND_PLAY = 0;
|
||||||
|
MEDIA_PLAYER_COMMAND_PAUSE = 1;
|
||||||
|
MEDIA_PLAYER_COMMAND_STOP = 2;
|
||||||
|
MEDIA_PLAYER_COMMAND_MUTE = 3;
|
||||||
|
MEDIA_PLAYER_COMMAND_UNMUTE = 4;
|
||||||
|
}
|
||||||
|
message ListEntitiesMediaPlayerResponse {
|
||||||
|
option (id) = 63;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_MEDIA_PLAYER";
|
||||||
|
|
||||||
|
string object_id = 1;
|
||||||
|
fixed32 key = 2;
|
||||||
|
string name = 3;
|
||||||
|
string unique_id = 4;
|
||||||
|
|
||||||
|
string icon = 5;
|
||||||
|
bool disabled_by_default = 6;
|
||||||
|
EntityCategory entity_category = 7;
|
||||||
|
|
||||||
|
bool supports_pause = 8;
|
||||||
|
}
|
||||||
|
message MediaPlayerStateResponse {
|
||||||
|
option (id) = 64;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_MEDIA_PLAYER";
|
||||||
|
option (no_delay) = true;
|
||||||
|
fixed32 key = 1;
|
||||||
|
MediaPlayerState state = 2;
|
||||||
|
float volume = 3;
|
||||||
|
bool muted = 4;
|
||||||
|
}
|
||||||
|
message MediaPlayerCommandRequest {
|
||||||
|
option (id) = 65;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_MEDIA_PLAYER";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
|
||||||
|
bool has_command = 2;
|
||||||
|
MediaPlayerCommand command = 3;
|
||||||
|
|
||||||
|
bool has_volume = 4;
|
||||||
|
float volume = 5;
|
||||||
|
|
||||||
|
bool has_media_url = 6;
|
||||||
|
string media_url = 7;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
#include "api_connection.h"
|
#include "api_connection.h"
|
||||||
|
#include "esphome/core/entity_base.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
#include "esphome/core/util.h"
|
#include "esphome/components/network/util.h"
|
||||||
#include "esphome/core/version.h"
|
#include "esphome/core/version.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
||||||
#ifdef USE_DEEP_SLEEP
|
#ifdef USE_DEEP_SLEEP
|
||||||
@@ -10,17 +12,15 @@
|
|||||||
#ifdef USE_HOMEASSISTANT_TIME
|
#ifdef USE_HOMEASSISTANT_TIME
|
||||||
#include "esphome/components/homeassistant/time/homeassistant_time.h"
|
#include "esphome/components/homeassistant/time/homeassistant_time.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
|
||||||
#include "esphome/components/fan/fan_helpers.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
static const char *const TAG = "api.connection";
|
static const char *const TAG = "api.connection";
|
||||||
|
static const int ESP32_CAMERA_STOP_STREAM = 5000;
|
||||||
|
|
||||||
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
|
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
|
||||||
: parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
|
: parent_(parent), initial_state_iterator_(this), list_entities_iterator_(this) {
|
||||||
this->proto_write_buffer_.reserve(64);
|
this->proto_write_buffer_.reserve(64);
|
||||||
|
|
||||||
#if defined(USE_API_PLAINTEXT)
|
#if defined(USE_API_PLAINTEXT)
|
||||||
@@ -48,7 +48,7 @@ void APIConnection::loop() {
|
|||||||
if (this->remove_)
|
if (this->remove_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!network_is_connected()) {
|
if (!network::is_connected()) {
|
||||||
// when network is disconnected force disconnect immediately
|
// when network is disconnected force disconnect immediately
|
||||||
// don't wait for timeout
|
// don't wait for timeout
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
@@ -76,6 +76,8 @@ void APIConnection::loop() {
|
|||||||
on_fatal_error();
|
on_fatal_error();
|
||||||
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
|
if (err == APIError::SOCKET_READ_FAILED && errno == ECONNRESET) {
|
||||||
ESP_LOGW(TAG, "%s: Connection reset", client_info_.c_str());
|
ESP_LOGW(TAG, "%s: Connection reset", client_info_.c_str());
|
||||||
|
} else if (err == APIError::CONNECTION_CLOSED) {
|
||||||
|
ESP_LOGW(TAG, "%s: Connection closed", client_info_.c_str());
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno);
|
ESP_LOGW(TAG, "%s: Reading failed: %s errno=%d", client_info_.c_str(), api_error_to_str(err), errno);
|
||||||
}
|
}
|
||||||
@@ -100,6 +102,7 @@ void APIConnection::loop() {
|
|||||||
ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str());
|
ESP_LOGW(TAG, "%s didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str());
|
||||||
}
|
}
|
||||||
} else if (now - this->last_traffic_ > keepalive) {
|
} else if (now - this->last_traffic_ > keepalive) {
|
||||||
|
ESP_LOGVV(TAG, "Sending keepalive PING...");
|
||||||
this->sent_ping_ = true;
|
this->sent_ping_ = true;
|
||||||
this->send_ping_request(PingRequest());
|
this->send_ping_request(PingRequest());
|
||||||
}
|
}
|
||||||
@@ -128,7 +131,7 @@ void APIConnection::loop() {
|
|||||||
|
|
||||||
if (state_subs_at_ != -1) {
|
if (state_subs_at_ != -1) {
|
||||||
const auto &subs = this->parent_->get_state_subs();
|
const auto &subs = this->parent_->get_state_subs();
|
||||||
if (state_subs_at_ >= subs.size()) {
|
if (state_subs_at_ >= (int) subs.size()) {
|
||||||
state_subs_at_ = -1;
|
state_subs_at_ = -1;
|
||||||
} else {
|
} else {
|
||||||
auto &it = subs[state_subs_at_];
|
auto &it = subs[state_subs_at_];
|
||||||
@@ -142,8 +145,8 @@ void APIConnection::loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) {
|
std::string get_default_unique_id(const std::string &component_type, EntityBase *entity) {
|
||||||
return App.get_name() + component_type + nameable->get_object_id();
|
return App.get_name() + component_type + entity->get_object_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
|
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
|
||||||
@@ -179,6 +182,8 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
|
|||||||
msg.device_class = binary_sensor->get_device_class();
|
msg.device_class = binary_sensor->get_device_class();
|
||||||
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
||||||
msg.disabled_by_default = binary_sensor->is_disabled_by_default();
|
msg.disabled_by_default = binary_sensor->is_disabled_by_default();
|
||||||
|
msg.icon = binary_sensor->get_icon();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(binary_sensor->get_entity_category());
|
||||||
return this->send_list_entities_binary_sensor_response(msg);
|
return this->send_list_entities_binary_sensor_response(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -211,6 +216,8 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
|
|||||||
msg.supports_tilt = traits.get_supports_tilt();
|
msg.supports_tilt = traits.get_supports_tilt();
|
||||||
msg.device_class = cover->get_device_class();
|
msg.device_class = cover->get_device_class();
|
||||||
msg.disabled_by_default = cover->is_disabled_by_default();
|
msg.disabled_by_default = cover->is_disabled_by_default();
|
||||||
|
msg.icon = cover->get_icon();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(cover->get_entity_category());
|
||||||
return this->send_list_entities_cover_response(msg);
|
return this->send_list_entities_cover_response(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
||||||
@@ -243,10 +250,7 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
// Shut-up about usage of deprecated speed_level_to_enum/speed_enum_to_level functions for a bit.
|
bool APIConnection::send_fan_state(fan::Fan *fan) {
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
||||||
bool APIConnection::send_fan_state(fan::FanState *fan) {
|
|
||||||
if (!this->state_subscription_)
|
if (!this->state_subscription_)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -258,13 +262,12 @@ bool APIConnection::send_fan_state(fan::FanState *fan) {
|
|||||||
resp.oscillating = fan->oscillating;
|
resp.oscillating = fan->oscillating;
|
||||||
if (traits.supports_speed()) {
|
if (traits.supports_speed()) {
|
||||||
resp.speed_level = fan->speed;
|
resp.speed_level = fan->speed;
|
||||||
resp.speed = static_cast<enums::FanSpeed>(fan::speed_level_to_enum(fan->speed, traits.supported_speed_count()));
|
|
||||||
}
|
}
|
||||||
if (traits.supports_direction())
|
if (traits.supports_direction())
|
||||||
resp.direction = static_cast<enums::FanDirection>(fan->direction);
|
resp.direction = static_cast<enums::FanDirection>(fan->direction);
|
||||||
return this->send_fan_state_response(resp);
|
return this->send_fan_state_response(resp);
|
||||||
}
|
}
|
||||||
bool APIConnection::send_fan_info(fan::FanState *fan) {
|
bool APIConnection::send_fan_info(fan::Fan *fan) {
|
||||||
auto traits = fan->get_traits();
|
auto traits = fan->get_traits();
|
||||||
ListEntitiesFanResponse msg;
|
ListEntitiesFanResponse msg;
|
||||||
msg.key = fan->get_object_id_hash();
|
msg.key = fan->get_object_id_hash();
|
||||||
@@ -276,15 +279,15 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
|
|||||||
msg.supports_direction = traits.supports_direction();
|
msg.supports_direction = traits.supports_direction();
|
||||||
msg.supported_speed_count = traits.supported_speed_count();
|
msg.supported_speed_count = traits.supported_speed_count();
|
||||||
msg.disabled_by_default = fan->is_disabled_by_default();
|
msg.disabled_by_default = fan->is_disabled_by_default();
|
||||||
|
msg.icon = fan->get_icon();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(fan->get_entity_category());
|
||||||
return this->send_list_entities_fan_response(msg);
|
return this->send_list_entities_fan_response(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
||||||
fan::FanState *fan = App.get_fan_by_key(msg.key);
|
fan::Fan *fan = App.get_fan_by_key(msg.key);
|
||||||
if (fan == nullptr)
|
if (fan == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto traits = fan->get_traits();
|
|
||||||
|
|
||||||
auto call = fan->make_call();
|
auto call = fan->make_call();
|
||||||
if (msg.has_state)
|
if (msg.has_state)
|
||||||
call.set_state(msg.state);
|
call.set_state(msg.state);
|
||||||
@@ -293,14 +296,11 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
|
|||||||
if (msg.has_speed_level) {
|
if (msg.has_speed_level) {
|
||||||
// Prefer level
|
// Prefer level
|
||||||
call.set_speed(msg.speed_level);
|
call.set_speed(msg.speed_level);
|
||||||
} else if (msg.has_speed) {
|
|
||||||
call.set_speed(fan::speed_enum_to_level(static_cast<fan::FanSpeed>(msg.speed), traits.supported_speed_count()));
|
|
||||||
}
|
}
|
||||||
if (msg.has_direction)
|
if (msg.has_direction)
|
||||||
call.set_direction(static_cast<fan::FanDirection>(msg.direction));
|
call.set_direction(static_cast<fan::FanDirection>(msg.direction));
|
||||||
call.perform();
|
call.perform();
|
||||||
}
|
}
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
@@ -338,6 +338,8 @@ bool APIConnection::send_light_info(light::LightState *light) {
|
|||||||
msg.unique_id = get_default_unique_id("light", light);
|
msg.unique_id = get_default_unique_id("light", light);
|
||||||
|
|
||||||
msg.disabled_by_default = light->is_disabled_by_default();
|
msg.disabled_by_default = light->is_disabled_by_default();
|
||||||
|
msg.icon = light->get_icon();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(light->get_entity_category());
|
||||||
|
|
||||||
for (auto mode : traits.get_supported_color_modes())
|
for (auto mode : traits.get_supported_color_modes())
|
||||||
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
|
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
|
||||||
@@ -424,7 +426,7 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
|||||||
msg.device_class = sensor->get_device_class();
|
msg.device_class = sensor->get_device_class();
|
||||||
msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
|
msg.state_class = static_cast<enums::SensorStateClass>(sensor->get_state_class());
|
||||||
msg.disabled_by_default = sensor->is_disabled_by_default();
|
msg.disabled_by_default = sensor->is_disabled_by_default();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(sensor->get_entity_category());
|
||||||
return this->send_list_entities_sensor_response(msg);
|
return this->send_list_entities_sensor_response(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -448,6 +450,8 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
|
|||||||
msg.icon = a_switch->get_icon();
|
msg.icon = a_switch->get_icon();
|
||||||
msg.assumed_state = a_switch->assumed_state();
|
msg.assumed_state = a_switch->assumed_state();
|
||||||
msg.disabled_by_default = a_switch->is_disabled_by_default();
|
msg.disabled_by_default = a_switch->is_disabled_by_default();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(a_switch->get_entity_category());
|
||||||
|
msg.device_class = a_switch->get_device_class();
|
||||||
return this->send_list_entities_switch_response(msg);
|
return this->send_list_entities_switch_response(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
|
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
|
||||||
@@ -455,10 +459,11 @@ void APIConnection::switch_command(const SwitchCommandRequest &msg) {
|
|||||||
if (a_switch == nullptr)
|
if (a_switch == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (msg.state)
|
if (msg.state) {
|
||||||
a_switch->turn_on();
|
a_switch->turn_on();
|
||||||
else
|
} else {
|
||||||
a_switch->turn_off();
|
a_switch->turn_off();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -483,6 +488,7 @@ bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor)
|
|||||||
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
|
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
|
||||||
msg.icon = text_sensor->get_icon();
|
msg.icon = text_sensor->get_icon();
|
||||||
msg.disabled_by_default = text_sensor->is_disabled_by_default();
|
msg.disabled_by_default = text_sensor->is_disabled_by_default();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(text_sensor->get_entity_category());
|
||||||
return this->send_list_entities_text_sensor_response(msg);
|
return this->send_list_entities_text_sensor_response(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -528,6 +534,8 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
|
|||||||
msg.unique_id = get_default_unique_id("climate", climate);
|
msg.unique_id = get_default_unique_id("climate", climate);
|
||||||
|
|
||||||
msg.disabled_by_default = climate->is_disabled_by_default();
|
msg.disabled_by_default = climate->is_disabled_by_default();
|
||||||
|
msg.icon = climate->get_icon();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(climate->get_entity_category());
|
||||||
|
|
||||||
msg.supports_current_temperature = traits.get_supports_current_temperature();
|
msg.supports_current_temperature = traits.get_supports_current_temperature();
|
||||||
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
|
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
|
||||||
@@ -600,8 +608,11 @@ bool APIConnection::send_number_info(number::Number *number) {
|
|||||||
msg.object_id = number->get_object_id();
|
msg.object_id = number->get_object_id();
|
||||||
msg.name = number->get_name();
|
msg.name = number->get_name();
|
||||||
msg.unique_id = get_default_unique_id("number", number);
|
msg.unique_id = get_default_unique_id("number", number);
|
||||||
msg.icon = number->traits.get_icon();
|
msg.icon = number->get_icon();
|
||||||
msg.disabled_by_default = number->is_disabled_by_default();
|
msg.disabled_by_default = number->is_disabled_by_default();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(number->get_entity_category());
|
||||||
|
msg.unit_of_measurement = number->traits.get_unit_of_measurement();
|
||||||
|
msg.mode = static_cast<enums::NumberMode>(number->traits.get_mode());
|
||||||
|
|
||||||
msg.min_value = number->traits.get_min_value();
|
msg.min_value = number->traits.get_min_value();
|
||||||
msg.max_value = number->traits.get_max_value();
|
msg.max_value = number->traits.get_max_value();
|
||||||
@@ -637,8 +648,9 @@ bool APIConnection::send_select_info(select::Select *select) {
|
|||||||
msg.object_id = select->get_object_id();
|
msg.object_id = select->get_object_id();
|
||||||
msg.name = select->get_name();
|
msg.name = select->get_name();
|
||||||
msg.unique_id = get_default_unique_id("select", select);
|
msg.unique_id = get_default_unique_id("select", select);
|
||||||
msg.icon = select->traits.get_icon();
|
msg.icon = select->get_icon();
|
||||||
msg.disabled_by_default = select->is_disabled_by_default();
|
msg.disabled_by_default = select->is_disabled_by_default();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(select->get_entity_category());
|
||||||
|
|
||||||
for (const auto &option : select->traits.get_options())
|
for (const auto &option : select->traits.get_options())
|
||||||
msg.options.push_back(option);
|
msg.options.push_back(option);
|
||||||
@@ -656,13 +668,126 @@ void APIConnection::select_command(const SelectCommandRequest &msg) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_BUTTON
|
||||||
|
bool APIConnection::send_button_info(button::Button *button) {
|
||||||
|
ListEntitiesButtonResponse msg;
|
||||||
|
msg.key = button->get_object_id_hash();
|
||||||
|
msg.object_id = button->get_object_id();
|
||||||
|
msg.name = button->get_name();
|
||||||
|
msg.unique_id = get_default_unique_id("button", button);
|
||||||
|
msg.icon = button->get_icon();
|
||||||
|
msg.disabled_by_default = button->is_disabled_by_default();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(button->get_entity_category());
|
||||||
|
msg.device_class = button->get_device_class();
|
||||||
|
return this->send_list_entities_button_response(msg);
|
||||||
|
}
|
||||||
|
void APIConnection::button_command(const ButtonCommandRequest &msg) {
|
||||||
|
button::Button *button = App.get_button_by_key(msg.key);
|
||||||
|
if (button == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
button->press();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_LOCK
|
||||||
|
bool APIConnection::send_lock_state(lock::Lock *a_lock, lock::LockState state) {
|
||||||
|
if (!this->state_subscription_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
LockStateResponse resp{};
|
||||||
|
resp.key = a_lock->get_object_id_hash();
|
||||||
|
resp.state = static_cast<enums::LockState>(state);
|
||||||
|
return this->send_lock_state_response(resp);
|
||||||
|
}
|
||||||
|
bool APIConnection::send_lock_info(lock::Lock *a_lock) {
|
||||||
|
ListEntitiesLockResponse msg;
|
||||||
|
msg.key = a_lock->get_object_id_hash();
|
||||||
|
msg.object_id = a_lock->get_object_id();
|
||||||
|
msg.name = a_lock->get_name();
|
||||||
|
msg.unique_id = get_default_unique_id("lock", a_lock);
|
||||||
|
msg.icon = a_lock->get_icon();
|
||||||
|
msg.assumed_state = a_lock->traits.get_assumed_state();
|
||||||
|
msg.disabled_by_default = a_lock->is_disabled_by_default();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(a_lock->get_entity_category());
|
||||||
|
msg.supports_open = a_lock->traits.get_supports_open();
|
||||||
|
msg.requires_code = a_lock->traits.get_requires_code();
|
||||||
|
return this->send_list_entities_lock_response(msg);
|
||||||
|
}
|
||||||
|
void APIConnection::lock_command(const LockCommandRequest &msg) {
|
||||||
|
lock::Lock *a_lock = App.get_lock_by_key(msg.key);
|
||||||
|
if (a_lock == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (msg.command) {
|
||||||
|
case enums::LOCK_UNLOCK:
|
||||||
|
a_lock->unlock();
|
||||||
|
break;
|
||||||
|
case enums::LOCK_LOCK:
|
||||||
|
a_lock->lock();
|
||||||
|
break;
|
||||||
|
case enums::LOCK_OPEN:
|
||||||
|
a_lock->open();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_MEDIA_PLAYER
|
||||||
|
bool APIConnection::send_media_player_state(media_player::MediaPlayer *media_player) {
|
||||||
|
if (!this->state_subscription_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MediaPlayerStateResponse resp{};
|
||||||
|
resp.key = media_player->get_object_id_hash();
|
||||||
|
resp.state = static_cast<enums::MediaPlayerState>(media_player->state);
|
||||||
|
resp.volume = media_player->volume;
|
||||||
|
resp.muted = media_player->is_muted();
|
||||||
|
return this->send_media_player_state_response(resp);
|
||||||
|
}
|
||||||
|
bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_player) {
|
||||||
|
ListEntitiesMediaPlayerResponse msg;
|
||||||
|
msg.key = media_player->get_object_id_hash();
|
||||||
|
msg.object_id = media_player->get_object_id();
|
||||||
|
msg.name = media_player->get_name();
|
||||||
|
msg.unique_id = get_default_unique_id("media_player", media_player);
|
||||||
|
msg.icon = media_player->get_icon();
|
||||||
|
msg.disabled_by_default = media_player->is_disabled_by_default();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(media_player->get_entity_category());
|
||||||
|
|
||||||
|
auto traits = media_player->get_traits();
|
||||||
|
msg.supports_pause = traits.get_supports_pause();
|
||||||
|
|
||||||
|
return this->send_list_entities_media_player_response(msg);
|
||||||
|
}
|
||||||
|
void APIConnection::media_player_command(const MediaPlayerCommandRequest &msg) {
|
||||||
|
media_player::MediaPlayer *media_player = App.get_media_player_by_key(msg.key);
|
||||||
|
if (media_player == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto call = media_player->make_call();
|
||||||
|
if (msg.has_command) {
|
||||||
|
call.set_command(static_cast<media_player::MediaPlayerCommand>(msg.command));
|
||||||
|
}
|
||||||
|
if (msg.has_volume) {
|
||||||
|
call.set_volume(msg.volume);
|
||||||
|
}
|
||||||
|
if (msg.has_media_url) {
|
||||||
|
call.set_media_url(msg.media_url);
|
||||||
|
}
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_ESP32_CAMERA
|
||||||
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
|
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
|
||||||
if (!this->state_subscription_)
|
if (!this->state_subscription_)
|
||||||
return;
|
return;
|
||||||
if (this->image_reader_.available())
|
if (this->image_reader_.available())
|
||||||
return;
|
return;
|
||||||
this->image_reader_.set_image(image);
|
if (image->was_requested_by(esphome::esp32_camera::API_REQUESTER) ||
|
||||||
|
image->was_requested_by(esphome::esp32_camera::IDLE))
|
||||||
|
this->image_reader_.set_image(std::move(image));
|
||||||
}
|
}
|
||||||
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
|
bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
|
||||||
ListEntitiesCameraResponse msg;
|
ListEntitiesCameraResponse msg;
|
||||||
@@ -671,6 +796,8 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
|
|||||||
msg.name = camera->get_name();
|
msg.name = camera->get_name();
|
||||||
msg.unique_id = get_default_unique_id("camera", camera);
|
msg.unique_id = get_default_unique_id("camera", camera);
|
||||||
msg.disabled_by_default = camera->is_disabled_by_default();
|
msg.disabled_by_default = camera->is_disabled_by_default();
|
||||||
|
msg.icon = camera->get_icon();
|
||||||
|
msg.entity_category = static_cast<enums::EntityCategory>(camera->get_entity_category());
|
||||||
return this->send_list_entities_camera_response(msg);
|
return this->send_list_entities_camera_response(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::camera_image(const CameraImageRequest &msg) {
|
void APIConnection::camera_image(const CameraImageRequest &msg) {
|
||||||
@@ -678,9 +805,14 @@ void APIConnection::camera_image(const CameraImageRequest &msg) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (msg.single)
|
if (msg.single)
|
||||||
esp32_camera::global_esp32_camera->request_image();
|
esp32_camera::global_esp32_camera->request_image(esphome::esp32_camera::API_REQUESTER);
|
||||||
if (msg.stream)
|
if (msg.stream) {
|
||||||
esp32_camera::global_esp32_camera->request_stream();
|
esp32_camera::global_esp32_camera->start_stream(esphome::esp32_camera::API_REQUESTER);
|
||||||
|
|
||||||
|
App.scheduler.set_timeout(this->parent_, "api_esp32_camera_stop_stream", ESP32_CAMERA_STOP_STREAM, []() {
|
||||||
|
esp32_camera::global_esp32_camera->stop_stream(esphome::esp32_camera::API_REQUESTER);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -714,6 +846,8 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
|||||||
resp.api_version_major = 1;
|
resp.api_version_major = 1;
|
||||||
resp.api_version_minor = 6;
|
resp.api_version_minor = 6;
|
||||||
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
||||||
|
resp.name = App.get_name();
|
||||||
|
|
||||||
this->connection_state_ = ConnectionState::CONNECTED;
|
this->connection_state_ = ConnectionState::CONNECTED;
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
@@ -749,14 +883,18 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
|||||||
#ifdef ESPHOME_PROJECT_NAME
|
#ifdef ESPHOME_PROJECT_NAME
|
||||||
resp.project_name = ESPHOME_PROJECT_NAME;
|
resp.project_name = ESPHOME_PROJECT_NAME;
|
||||||
resp.project_version = ESPHOME_PROJECT_VERSION;
|
resp.project_version = ESPHOME_PROJECT_VERSION;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_WEBSERVER
|
||||||
|
resp.webserver_port = USE_WEBSERVER_PORT;
|
||||||
#endif
|
#endif
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
|
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
|
||||||
for (auto &it : this->parent_->get_state_subs())
|
for (auto &it : this->parent_->get_state_subs()) {
|
||||||
if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
|
if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
|
||||||
it.callback(msg.state);
|
it.callback(msg.state);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
|
void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
@@ -805,7 +943,7 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type)
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this->last_traffic_ = millis();
|
// Do not set last_traffic_ on send
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
void APIConnection::on_unauthenticated_access() {
|
void APIConnection::on_unauthenticated_access() {
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ class APIConnection : public APIServerConnection {
|
|||||||
void cover_command(const CoverCommandRequest &msg) override;
|
void cover_command(const CoverCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool send_fan_state(fan::FanState *fan);
|
bool send_fan_state(fan::Fan *fan);
|
||||||
bool send_fan_info(fan::FanState *fan);
|
bool send_fan_info(fan::Fan *fan);
|
||||||
void fan_command(const FanCommandRequest &msg) override;
|
void fan_command(const FanCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
@@ -73,6 +73,20 @@ class APIConnection : public APIServerConnection {
|
|||||||
bool send_select_state(select::Select *select, std::string state);
|
bool send_select_state(select::Select *select, std::string state);
|
||||||
bool send_select_info(select::Select *select);
|
bool send_select_info(select::Select *select);
|
||||||
void select_command(const SelectCommandRequest &msg) override;
|
void select_command(const SelectCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BUTTON
|
||||||
|
bool send_button_info(button::Button *button);
|
||||||
|
void button_command(const ButtonCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_LOCK
|
||||||
|
bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
|
||||||
|
bool send_lock_info(lock::Lock *a_lock);
|
||||||
|
void lock_command(const LockCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_MEDIA_PLAYER
|
||||||
|
bool send_media_player_state(media_player::MediaPlayer *media_player);
|
||||||
|
bool send_media_player_info(media_player::MediaPlayer *media_player);
|
||||||
|
void media_player_command(const MediaPlayerCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
bool send_log_message(int level, const char *tag, const char *line);
|
bool send_log_message(int level, const char *tag, const char *line);
|
||||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
#include "api_frame_helper.h"
|
#include "api_frame_helper.h"
|
||||||
|
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/application.h"
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
static const char *const TAG = "api.socket";
|
static const char *const TAG = "api.socket";
|
||||||
|
|
||||||
/// Is the given return value (from read/write syscalls) a wouldblock error?
|
/// Is the given return value (from write syscalls) a wouldblock error?
|
||||||
bool is_would_block(ssize_t ret) {
|
bool is_would_block(ssize_t ret) {
|
||||||
if (ret == -1) {
|
if (ret == -1) {
|
||||||
return errno == EWOULDBLOCK || errno == EAGAIN;
|
return errno == EWOULDBLOCK || errno == EAGAIN;
|
||||||
@@ -63,6 +66,8 @@ const char *api_error_to_str(APIError err) {
|
|||||||
return "HANDSHAKESTATE_SPLIT_FAILED";
|
return "HANDSHAKESTATE_SPLIT_FAILED";
|
||||||
} else if (err == APIError::BAD_HANDSHAKE_ERROR_BYTE) {
|
} else if (err == APIError::BAD_HANDSHAKE_ERROR_BYTE) {
|
||||||
return "BAD_HANDSHAKE_ERROR_BYTE";
|
return "BAD_HANDSHAKE_ERROR_BYTE";
|
||||||
|
} else if (err == APIError::CONNECTION_CLOSED) {
|
||||||
|
return "CONNECTION_CLOSED";
|
||||||
}
|
}
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
@@ -126,6 +131,14 @@ APIError APINoiseFrameHelper::init() {
|
|||||||
return APIError::TCP_NONBLOCKING_FAILED;
|
return APIError::TCP_NONBLOCKING_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int enable = 1;
|
||||||
|
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
||||||
|
if (err != 0) {
|
||||||
|
state_ = State::FAILED;
|
||||||
|
HELPER_LOG("Setting nodelay failed with errno %d", errno);
|
||||||
|
return APIError::TCP_NODELAY_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
// init prologue
|
// init prologue
|
||||||
prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT));
|
prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT));
|
||||||
|
|
||||||
@@ -163,9 +176,6 @@ APIError APINoiseFrameHelper::loop() {
|
|||||||
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
|
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
|
||||||
*/
|
*/
|
||||||
APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
||||||
int err;
|
|
||||||
APIError aerr;
|
|
||||||
|
|
||||||
if (frame == nullptr) {
|
if (frame == nullptr) {
|
||||||
HELPER_LOG("Bad argument for try_read_frame_");
|
HELPER_LOG("Bad argument for try_read_frame_");
|
||||||
return APIError::BAD_ARG;
|
return APIError::BAD_ARG;
|
||||||
@@ -176,15 +186,20 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
// no header information yet
|
// no header information yet
|
||||||
size_t to_read = 3 - rx_header_buf_len_;
|
size_t to_read = 3 - rx_header_buf_len_;
|
||||||
ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
|
ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
|
||||||
if (is_would_block(received)) {
|
if (received == -1) {
|
||||||
|
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||||
return APIError::WOULD_BLOCK;
|
return APIError::WOULD_BLOCK;
|
||||||
} else if (received == -1) {
|
}
|
||||||
state_ = State::FAILED;
|
state_ = State::FAILED;
|
||||||
HELPER_LOG("Socket read failed with errno %d", errno);
|
HELPER_LOG("Socket read failed with errno %d", errno);
|
||||||
return APIError::SOCKET_READ_FAILED;
|
return APIError::SOCKET_READ_FAILED;
|
||||||
|
} else if (received == 0) {
|
||||||
|
state_ = State::FAILED;
|
||||||
|
HELPER_LOG("Connection closed");
|
||||||
|
return APIError::CONNECTION_CLOSED;
|
||||||
}
|
}
|
||||||
rx_header_buf_len_ += received;
|
rx_header_buf_len_ += received;
|
||||||
if (received != to_read) {
|
if ((size_t) received != to_read) {
|
||||||
// not a full read
|
// not a full read
|
||||||
return APIError::WOULD_BLOCK;
|
return APIError::WOULD_BLOCK;
|
||||||
}
|
}
|
||||||
@@ -218,15 +233,20 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
// more data to read
|
// more data to read
|
||||||
size_t to_read = msg_size - rx_buf_len_;
|
size_t to_read = msg_size - rx_buf_len_;
|
||||||
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
||||||
if (is_would_block(received)) {
|
if (received == -1) {
|
||||||
|
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||||
return APIError::WOULD_BLOCK;
|
return APIError::WOULD_BLOCK;
|
||||||
} else if (received == -1) {
|
}
|
||||||
state_ = State::FAILED;
|
state_ = State::FAILED;
|
||||||
HELPER_LOG("Socket read failed with errno %d", errno);
|
HELPER_LOG("Socket read failed with errno %d", errno);
|
||||||
return APIError::SOCKET_READ_FAILED;
|
return APIError::SOCKET_READ_FAILED;
|
||||||
|
} else if (received == 0) {
|
||||||
|
state_ = State::FAILED;
|
||||||
|
HELPER_LOG("Connection closed");
|
||||||
|
return APIError::CONNECTION_CLOSED;
|
||||||
}
|
}
|
||||||
rx_buf_len_ += received;
|
rx_buf_len_ += received;
|
||||||
if (received != to_read) {
|
if ((size_t) received != to_read) {
|
||||||
// not all read
|
// not all read
|
||||||
return APIError::WOULD_BLOCK;
|
return APIError::WOULD_BLOCK;
|
||||||
}
|
}
|
||||||
@@ -234,7 +254,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
|
|
||||||
// uncomment for even more debugging
|
// uncomment for even more debugging
|
||||||
#ifdef HELPER_LOG_PACKETS
|
#ifdef HELPER_LOG_PACKETS
|
||||||
ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str());
|
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
|
||||||
#endif
|
#endif
|
||||||
frame->msg = std::move(rx_buf_);
|
frame->msg = std::move(rx_buf_);
|
||||||
// consume msg
|
// consume msg
|
||||||
@@ -250,7 +270,7 @@ APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
*
|
*
|
||||||
* If the handshake is still active when this method returns and a read/write can't take place at
|
* If the handshake is still active when this method returns and a read/write can't take place at
|
||||||
* the moment, returns WOULD_BLOCK.
|
* the moment, returns WOULD_BLOCK.
|
||||||
* If an error occured, returns that error. Only returns OK if the transport is ready for data
|
* If an error occurred, returns that error. Only returns OK if the transport is ready for data
|
||||||
* traffic.
|
* traffic.
|
||||||
*/
|
*/
|
||||||
APIError APINoiseFrameHelper::state_action_() {
|
APIError APINoiseFrameHelper::state_action_() {
|
||||||
@@ -283,9 +303,16 @@ APIError APINoiseFrameHelper::state_action_() {
|
|||||||
}
|
}
|
||||||
if (state_ == State::SERVER_HELLO) {
|
if (state_ == State::SERVER_HELLO) {
|
||||||
// send server hello
|
// send server hello
|
||||||
uint8_t msg[1];
|
std::vector<uint8_t> msg;
|
||||||
msg[0] = 0x01; // chosen proto
|
// chosen proto
|
||||||
aerr = write_frame_(msg, 1);
|
msg.push_back(0x01);
|
||||||
|
|
||||||
|
// node name, terminated by null byte
|
||||||
|
const std::string &name = App.get_name();
|
||||||
|
const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
|
||||||
|
msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
|
||||||
|
|
||||||
|
aerr = write_frame_(msg.data(), msg.size());
|
||||||
if (aerr != APIError::OK)
|
if (aerr != APIError::OK)
|
||||||
return aerr;
|
return aerr;
|
||||||
|
|
||||||
@@ -523,13 +550,13 @@ APIError APINoiseFrameHelper::try_send_tx_buf_() {
|
|||||||
APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
||||||
if (iovcnt == 0)
|
if (iovcnt == 0)
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
int err;
|
|
||||||
APIError aerr;
|
APIError aerr;
|
||||||
|
|
||||||
size_t total_write_len = 0;
|
size_t total_write_len = 0;
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
#ifdef HELPER_LOG_PACKETS
|
#ifdef HELPER_LOG_PACKETS
|
||||||
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
ESP_LOGVV(TAG, "Sending raw: %s",
|
||||||
|
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
||||||
#endif
|
#endif
|
||||||
total_write_len += iov[i].iov_len;
|
total_write_len += iov[i].iov_len;
|
||||||
}
|
}
|
||||||
@@ -559,11 +586,11 @@ APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
|||||||
}
|
}
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
} else if (sent == -1) {
|
} else if (sent == -1) {
|
||||||
// an error occured
|
// an error occurred
|
||||||
state_ = State::FAILED;
|
state_ = State::FAILED;
|
||||||
HELPER_LOG("Socket write failed with errno %d", errno);
|
HELPER_LOG("Socket write failed with errno %d", errno);
|
||||||
return APIError::SOCKET_WRITE_FAILED;
|
return APIError::SOCKET_WRITE_FAILED;
|
||||||
} else if (sent != total_write_len) {
|
} else if ((size_t) sent != total_write_len) {
|
||||||
// partially sent, add end to tx_buf
|
// partially sent, add end to tx_buf
|
||||||
size_t to_consume = sent;
|
size_t to_consume = sent;
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
@@ -703,7 +730,12 @@ APIError APINoiseFrameHelper::shutdown(int how) {
|
|||||||
}
|
}
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// declare how noise generates random bytes (here with a good HWRNG based on the RF system)
|
// declare how noise generates random bytes (here with a good HWRNG based on the RF system)
|
||||||
void noise_rand_bytes(void *output, size_t len) { esphome::fill_random(reinterpret_cast<uint8_t *>(output), len); }
|
void noise_rand_bytes(void *output, size_t len) {
|
||||||
|
if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
|
||||||
|
ESP_LOGE(TAG, "Failed to acquire random bytes, rebooting!");
|
||||||
|
arch_restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif // USE_API_NOISE
|
#endif // USE_API_NOISE
|
||||||
|
|
||||||
@@ -721,6 +753,13 @@ APIError APIPlaintextFrameHelper::init() {
|
|||||||
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
|
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
|
||||||
return APIError::TCP_NONBLOCKING_FAILED;
|
return APIError::TCP_NONBLOCKING_FAILED;
|
||||||
}
|
}
|
||||||
|
int enable = 1;
|
||||||
|
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
||||||
|
if (err != 0) {
|
||||||
|
state_ = State::FAILED;
|
||||||
|
HELPER_LOG("Setting nodelay failed with errno %d", errno);
|
||||||
|
return APIError::TCP_NODELAY_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
state_ = State::DATA;
|
state_ = State::DATA;
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
@@ -750,9 +789,6 @@ APIError APIPlaintextFrameHelper::loop() {
|
|||||||
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
||||||
*/
|
*/
|
||||||
APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
||||||
int err;
|
|
||||||
APIError aerr;
|
|
||||||
|
|
||||||
if (frame == nullptr) {
|
if (frame == nullptr) {
|
||||||
HELPER_LOG("Bad argument for try_read_frame_");
|
HELPER_LOG("Bad argument for try_read_frame_");
|
||||||
return APIError::BAD_ARG;
|
return APIError::BAD_ARG;
|
||||||
@@ -762,12 +798,17 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
while (!rx_header_parsed_) {
|
while (!rx_header_parsed_) {
|
||||||
uint8_t data;
|
uint8_t data;
|
||||||
ssize_t received = socket_->read(&data, 1);
|
ssize_t received = socket_->read(&data, 1);
|
||||||
if (is_would_block(received)) {
|
if (received == -1) {
|
||||||
|
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||||
return APIError::WOULD_BLOCK;
|
return APIError::WOULD_BLOCK;
|
||||||
} else if (received == -1) {
|
}
|
||||||
state_ = State::FAILED;
|
state_ = State::FAILED;
|
||||||
HELPER_LOG("Socket read failed with errno %d", errno);
|
HELPER_LOG("Socket read failed with errno %d", errno);
|
||||||
return APIError::SOCKET_READ_FAILED;
|
return APIError::SOCKET_READ_FAILED;
|
||||||
|
} else if (received == 0) {
|
||||||
|
state_ = State::FAILED;
|
||||||
|
HELPER_LOG("Connection closed");
|
||||||
|
return APIError::CONNECTION_CLOSED;
|
||||||
}
|
}
|
||||||
rx_header_buf_.push_back(data);
|
rx_header_buf_.push_back(data);
|
||||||
|
|
||||||
@@ -808,15 +849,20 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
// more data to read
|
// more data to read
|
||||||
size_t to_read = rx_header_parsed_len_ - rx_buf_len_;
|
size_t to_read = rx_header_parsed_len_ - rx_buf_len_;
|
||||||
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
||||||
if (is_would_block(received)) {
|
if (received == -1) {
|
||||||
|
if (errno == EWOULDBLOCK || errno == EAGAIN) {
|
||||||
return APIError::WOULD_BLOCK;
|
return APIError::WOULD_BLOCK;
|
||||||
} else if (received == -1) {
|
}
|
||||||
state_ = State::FAILED;
|
state_ = State::FAILED;
|
||||||
HELPER_LOG("Socket read failed with errno %d", errno);
|
HELPER_LOG("Socket read failed with errno %d", errno);
|
||||||
return APIError::SOCKET_READ_FAILED;
|
return APIError::SOCKET_READ_FAILED;
|
||||||
|
} else if (received == 0) {
|
||||||
|
state_ = State::FAILED;
|
||||||
|
HELPER_LOG("Connection closed");
|
||||||
|
return APIError::CONNECTION_CLOSED;
|
||||||
}
|
}
|
||||||
rx_buf_len_ += received;
|
rx_buf_len_ += received;
|
||||||
if (received != to_read) {
|
if ((size_t) received != to_read) {
|
||||||
// not all read
|
// not all read
|
||||||
return APIError::WOULD_BLOCK;
|
return APIError::WOULD_BLOCK;
|
||||||
}
|
}
|
||||||
@@ -824,7 +870,7 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
|
|
||||||
// uncomment for even more debugging
|
// uncomment for even more debugging
|
||||||
#ifdef HELPER_LOG_PACKETS
|
#ifdef HELPER_LOG_PACKETS
|
||||||
ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str());
|
ESP_LOGVV(TAG, "Received frame: %s", format_hex_pretty(rx_buf_).c_str());
|
||||||
#endif
|
#endif
|
||||||
frame->msg = std::move(rx_buf_);
|
frame->msg = std::move(rx_buf_);
|
||||||
// consume msg
|
// consume msg
|
||||||
@@ -836,7 +882,6 @@ APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
||||||
int err;
|
|
||||||
APIError aerr;
|
APIError aerr;
|
||||||
|
|
||||||
if (state_ != State::DATA) {
|
if (state_ != State::DATA) {
|
||||||
@@ -856,9 +901,6 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|||||||
}
|
}
|
||||||
bool APIPlaintextFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
|
bool APIPlaintextFrameHelper::can_write_without_blocking() { return state_ == State::DATA && tx_buf_.empty(); }
|
||||||
APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
|
APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
|
||||||
int err;
|
|
||||||
APIError aerr;
|
|
||||||
|
|
||||||
if (state_ != State::DATA) {
|
if (state_ != State::DATA) {
|
||||||
return APIError::BAD_STATE;
|
return APIError::BAD_STATE;
|
||||||
}
|
}
|
||||||
@@ -902,13 +944,13 @@ APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
|
|||||||
APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
||||||
if (iovcnt == 0)
|
if (iovcnt == 0)
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
int err;
|
|
||||||
APIError aerr;
|
APIError aerr;
|
||||||
|
|
||||||
size_t total_write_len = 0;
|
size_t total_write_len = 0;
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
#ifdef HELPER_LOG_PACKETS
|
#ifdef HELPER_LOG_PACKETS
|
||||||
ESP_LOGVV(TAG, "Sending raw: %s", hexencode(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
ESP_LOGVV(TAG, "Sending raw: %s",
|
||||||
|
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
||||||
#endif
|
#endif
|
||||||
total_write_len += iov[i].iov_len;
|
total_write_len += iov[i].iov_len;
|
||||||
}
|
}
|
||||||
@@ -938,11 +980,11 @@ APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt
|
|||||||
}
|
}
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
} else if (sent == -1) {
|
} else if (sent == -1) {
|
||||||
// an error occured
|
// an error occurred
|
||||||
state_ = State::FAILED;
|
state_ = State::FAILED;
|
||||||
HELPER_LOG("Socket write failed with errno %d", errno);
|
HELPER_LOG("Socket write failed with errno %d", errno);
|
||||||
return APIError::SOCKET_WRITE_FAILED;
|
return APIError::SOCKET_WRITE_FAILED;
|
||||||
} else if (sent != total_write_len) {
|
} else if ((size_t) sent != total_write_len) {
|
||||||
// partially sent, add end to tx_buf
|
// partially sent, add end to tx_buf
|
||||||
size_t to_consume = sent;
|
size_t to_consume = sent;
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ enum class APIError : int {
|
|||||||
HANDSHAKESTATE_SETUP_FAILED = 1019,
|
HANDSHAKESTATE_SETUP_FAILED = 1019,
|
||||||
HANDSHAKESTATE_SPLIT_FAILED = 1020,
|
HANDSHAKESTATE_SPLIT_FAILED = 1020,
|
||||||
BAD_HANDSHAKE_ERROR_BYTE = 1021,
|
BAD_HANDSHAKE_ERROR_BYTE = 1021,
|
||||||
|
CONNECTION_CLOSED = 1022,
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *api_error_to_str(APIError err);
|
const char *api_error_to_str(APIError err);
|
||||||
@@ -75,8 +77,8 @@ class APIFrameHelper {
|
|||||||
class APINoiseFrameHelper : public APIFrameHelper {
|
class APINoiseFrameHelper : public APIFrameHelper {
|
||||||
public:
|
public:
|
||||||
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx)
|
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx)
|
||||||
: socket_(std::move(socket)), ctx_(ctx) {}
|
: socket_(std::move(socket)), ctx_(std::move(std::move(ctx))) {}
|
||||||
~APINoiseFrameHelper();
|
~APINoiseFrameHelper() override;
|
||||||
APIError init() override;
|
APIError init() override;
|
||||||
APIError loop() override;
|
APIError loop() override;
|
||||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||||
@@ -136,7 +138,7 @@ class APINoiseFrameHelper : public APIFrameHelper {
|
|||||||
class APIPlaintextFrameHelper : public APIFrameHelper {
|
class APIPlaintextFrameHelper : public APIFrameHelper {
|
||||||
public:
|
public:
|
||||||
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {}
|
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {}
|
||||||
~APIPlaintextFrameHelper() = default;
|
~APIPlaintextFrameHelper() override = default;
|
||||||
APIError init() override;
|
APIError init() override;
|
||||||
APIError loop() override;
|
APIError loop() override;
|
||||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ using psk_t = std::array<uint8_t, 32>;
|
|||||||
|
|
||||||
class APINoiseContext {
|
class APINoiseContext {
|
||||||
public:
|
public:
|
||||||
void set_psk(psk_t psk) { psk_ = std::move(psk); }
|
void set_psk(psk_t psk) { psk_ = psk; }
|
||||||
const psk_t &get_psk() const { return psk_; }
|
const psk_t &get_psk() const { return psk_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user