mirror of
https://github.com/esphome/esphome.git
synced 2025-11-03 00:21:56 +00:00
Compare commits
1172 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f27757039 | ||
|
|
532907219b | ||
|
|
eeaba74553 | ||
|
|
dd637582a4 | ||
|
|
b0d12aeea1 | ||
|
|
bdbd813455 | ||
|
|
a6fac2b175 | ||
|
|
5ce923ea90 | ||
|
|
29f0508dc2 | ||
|
|
3ffa59f0cd | ||
|
|
790d6ef94c | ||
|
|
7828f48b9a | ||
|
|
9fbb3659a6 | ||
|
|
fee446c28a | ||
|
|
1d56f0b035 | ||
|
|
341fddb9aa | ||
|
|
456824669f | ||
|
|
62f3039d82 | ||
|
|
be4c718859 | ||
|
|
c2f9ed7c59 | ||
|
|
bfac6607d1 | ||
|
|
e43dcded62 | ||
|
|
887081fd71 | ||
|
|
71ded24fce | ||
|
|
1e2a9e8348 | ||
|
|
64a3aa7092 | ||
|
|
fda8dd4ce3 | ||
|
|
1efabd27d8 | ||
|
|
caa651e55b | ||
|
|
10a6e9b4ee | ||
|
|
4b8ec44262 | ||
|
|
bd74ed4bc0 | ||
|
|
d01f296420 | ||
|
|
27112e2ace | ||
|
|
837930234f | ||
|
|
e19aa3bbe0 | ||
|
|
35b5c1ed56 | ||
|
|
c9d93ff685 | ||
|
|
fa72990a63 | ||
|
|
e5afb1c4ea | ||
|
|
73ead5f328 | ||
|
|
5c57b51378 | ||
|
|
e25935ef21 | ||
|
|
c7a52c3894 | ||
|
|
53a4689ed1 | ||
|
|
0a82e6e792 | ||
|
|
98855e4123 | ||
|
|
6a09d7c49b | ||
|
|
46e50ba53f | ||
|
|
f1e3ff2ed2 | ||
|
|
7787fa8f29 | ||
|
|
70902029f8 | ||
|
|
4f9a56c884 | ||
|
|
3715ba030b | ||
|
|
0c93be97a9 | ||
|
|
54eb6070fb | ||
|
|
4dbf1c521e | ||
|
|
f30b8f6b3c | ||
|
|
18c08f24ad | ||
|
|
a7f53aea0e | ||
|
|
c399905675 | ||
|
|
5cb0c11feb | ||
|
|
08b67e7aea | ||
|
|
07ae8ec553 | ||
|
|
04c3a43c17 | ||
|
|
b632344596 | ||
|
|
fb8ec79a52 | ||
|
|
7dd16df846 | ||
|
|
551e9c6111 | ||
|
|
cc9f0b3f47 | ||
|
|
d77c3abdc0 | ||
|
|
dd37a4e04c | ||
|
|
1f5c79bd17 | ||
|
|
623570a117 | ||
|
|
cdbc146e5d | ||
|
|
7ae611256a | ||
|
|
b62c47fede | ||
|
|
99f497c3b8 | ||
|
|
4f88c2489b | ||
|
|
294ba1fca7 | ||
|
|
be61b38a2c | ||
|
|
f9797825ad | ||
|
|
fd4b7d4588 | ||
|
|
062cedc200 | ||
|
|
79b9d0579d | ||
|
|
ab31117bf3 | ||
|
|
d31040f5d8 | ||
|
|
52d19fa43d | ||
|
|
8ca34f7098 | ||
|
|
4c4099966a | ||
|
|
86ac7f3a59 | ||
|
|
9e400a7857 | ||
|
|
d5278351da | ||
|
|
36861595f1 | ||
|
|
d604321f37 | ||
|
|
964ab65497 | ||
|
|
3b940b1c04 | ||
|
|
a91e6a6bdf | ||
|
|
8600620305 | ||
|
|
96721f305f | ||
|
|
2bf70d7d00 | ||
|
|
1d8c170f48 | ||
|
|
6009c7edb4 | ||
|
|
e3f36c033e | ||
|
|
d4eb0f1655 | ||
|
|
5fca480921 | ||
|
|
7051f897bc | ||
|
|
2cb3015a28 | ||
|
|
d0859a7d33 | ||
|
|
e20ec00071 | ||
|
|
150114d774 | ||
|
|
89dfa5ea82 | ||
|
|
61ebc629f6 | ||
|
|
32f2da77f8 | ||
|
|
bfca3f242a | ||
|
|
3dfff2930a | ||
|
|
027e0de48e | ||
|
|
c811141a4f | ||
|
|
871c0ee2a5 | ||
|
|
b8a7741c61 | ||
|
|
97aa930ad2 | ||
|
|
2a5def10e7 | ||
|
|
969834e037 | ||
|
|
d73a44c504 | ||
|
|
8aec092ab6 | ||
|
|
4fa959ba45 | ||
|
|
b6011b9353 | ||
|
|
40a5005d94 | ||
|
|
c5eba21ff6 | ||
|
|
4891cfef56 | ||
|
|
4395664547 | ||
|
|
04d926af39 | ||
|
|
f9a31c1abb | ||
|
|
b43712d78d | ||
|
|
01904a0f10 | ||
|
|
dd875e7529 | ||
|
|
f1dcf0f0b8 | ||
|
|
a045d001bf | ||
|
|
066c1022d0 | ||
|
|
dca1c0f160 | ||
|
|
2419bc3678 | ||
|
|
c19b3ecd43 | ||
|
|
ef1e91d838 | ||
|
|
e5929225eb | ||
|
|
59c192becc | ||
|
|
a800816750 | ||
|
|
99d9ab4e40 | ||
|
|
f310ca1b74 | ||
|
|
f763daa577 | ||
|
|
607c3ae651 | ||
|
|
970563e07b | ||
|
|
e006045f59 | ||
|
|
fff3645901 | ||
|
|
a5383fd208 | ||
|
|
5591832b50 | ||
|
|
9ce3a2059f | ||
|
|
69e6cf2c0c | ||
|
|
28635124f9 | ||
|
|
25b116048c | ||
|
|
035be87a83 | ||
|
|
e8bdbc45a9 | ||
|
|
429caccefa | ||
|
|
744ca1af7c | ||
|
|
106f0d611f | ||
|
|
d826416684 | ||
|
|
24ba9eba46 | ||
|
|
424c34225f | ||
|
|
d781f3a11b | ||
|
|
a80f9ed336 | ||
|
|
92bbedfa5a | ||
|
|
86710ed483 | ||
|
|
da7eb9ac90 | ||
|
|
c411043681 | ||
|
|
93f8ee7e60 | ||
|
|
0efc1f06f2 | ||
|
|
81959804df | ||
|
|
75b524ddc4 | ||
|
|
f599c36272 | ||
|
|
9bb64315f3 | ||
|
|
575badc690 | ||
|
|
4b91cfb7f9 | ||
|
|
9ad9d64ac7 | ||
|
|
5a2cfa2798 | ||
|
|
eb24da7c82 | ||
|
|
f93e261d75 | ||
|
|
501f88ca86 | ||
|
|
360effcb72 | ||
|
|
eb9bd69405 | ||
|
|
11b8210e36 | ||
|
|
a57a842f7b | ||
|
|
a8c253a2a5 | ||
|
|
8b737aabd9 | ||
|
|
0db4815f3d | ||
|
|
139db58a66 | ||
|
|
d23376b81e | ||
|
|
99d90845b5 | ||
|
|
ea0127e42b | ||
|
|
c32fec7432 | ||
|
|
8bd23dd457 | ||
|
|
97a12c0169 | ||
|
|
635916737b | ||
|
|
65c50e4f01 | ||
|
|
5cf18235e3 | ||
|
|
b80f3fdec9 | ||
|
|
0426be9280 | ||
|
|
7c678659d4 | ||
|
|
13fe9e83fa | ||
|
|
4711f36a1f | ||
|
|
01e2a51132 | ||
|
|
a70a205ace | ||
|
|
33625e2dd3 | ||
|
|
0277218319 | ||
|
|
18a8c727fa | ||
|
|
80ad784a4e | ||
|
|
ebadaa9660 | ||
|
|
7bc51582f0 | ||
|
|
11fb54c74e | ||
|
|
913ac8b7e8 | ||
|
|
2b9350ce76 | ||
|
|
3b18f1b87f | ||
|
|
c5c24c1989 | ||
|
|
c3938d04f3 | ||
|
|
f968713be8 | ||
|
|
7b11284008 | ||
|
|
f39c0d52ee | ||
|
|
a3756a9600 | ||
|
|
afa436fe8f | ||
|
|
48b5ea9e59 | ||
|
|
56974153f1 | ||
|
|
9a2cd71571 | ||
|
|
d1c6368283 | ||
|
|
5c3268b8d4 | ||
|
|
25af5ab7c6 | ||
|
|
4d586b1446 | ||
|
|
bb759d52c8 | ||
|
|
9a2cf05c5f | ||
|
|
c79d700d03 | ||
|
|
482a3aebc9 | ||
|
|
387f249363 | ||
|
|
3d917d0b7e | ||
|
|
824f3187ac | ||
|
|
e3c27a483c | ||
|
|
a33bb32874 | ||
|
|
93d9d4b50a | ||
|
|
2376a2c941 | ||
|
|
b92702a312 | ||
|
|
b11d5f6799 | ||
|
|
072dce340e | ||
|
|
cccb1a2c9e | ||
|
|
063d9c47a4 | ||
|
|
8d8d421286 | ||
|
|
0ce57e5a39 | ||
|
|
aebad04c0b | ||
|
|
514d11d46f | ||
|
|
96e46db272 | ||
|
|
76f78877f6 | ||
|
|
4ffa68b773 | ||
|
|
8eaffee160 | ||
|
|
557a622f71 | ||
|
|
e9c6556296 | ||
|
|
dce9d59dfe | ||
|
|
d3e291b442 | ||
|
|
d4686c0fb1 | ||
|
|
ae9b247f47 | ||
|
|
a59761d292 | ||
|
|
030c87d142 | ||
|
|
95ed3e9d46 | ||
|
|
d0eaebe19f | ||
|
|
9ecead2645 | ||
|
|
98166dfa66 | ||
|
|
5645be4e0f | ||
|
|
9a7a205510 | ||
|
|
7e3b8fd346 | ||
|
|
3d6dcc9eee | ||
|
|
4f6982fbc5 | ||
|
|
00c144daeb | ||
|
|
fddb05c845 | ||
|
|
3b13e62d3f | ||
|
|
9e7acd6f75 | ||
|
|
7d9c043f1d | ||
|
|
bafbeefb37 | ||
|
|
54660300e9 | ||
|
|
5dc40049be | ||
|
|
1e46b4073f | ||
|
|
29fc4af0fc | ||
|
|
4030a2e253 | ||
|
|
ea80cb751b | ||
|
|
6aa61dbce7 | ||
|
|
cdc9c99d40 | ||
|
|
07f2931841 | ||
|
|
616ad04131 | ||
|
|
b966e58f9e | ||
|
|
a546677b08 | ||
|
|
5c3af1d3f6 | ||
|
|
66b0b6feeb | ||
|
|
7665a220a0 | ||
|
|
4250af4dd9 | ||
|
|
73252ccd25 | ||
|
|
33bf78c369 | ||
|
|
f33c2a48eb | ||
|
|
96ded4e402 | ||
|
|
44d5be6e7b | ||
|
|
076124eb71 | ||
|
|
44562dbef1 | ||
|
|
cafdcaec29 | ||
|
|
b660e5a7fa | ||
|
|
3b4ea0ed0a | ||
|
|
403b6e32e3 | ||
|
|
229bf719a2 | ||
|
|
2225594ee8 | ||
|
|
b91a1aa027 | ||
|
|
13dbdd9b16 | ||
|
|
37bc0b3b5a | ||
|
|
be70a96651 | ||
|
|
d83d214497 | ||
|
|
6ec0f80b76 | ||
|
|
06f566346d | ||
|
|
b680649113 | ||
|
|
5ab2ef4079 | ||
|
|
392ed64375 | ||
|
|
566c129435 | ||
|
|
c903eb2d01 | ||
|
|
69c78651d5 | ||
|
|
f7232b199a | ||
|
|
ffc6fe9ca0 | ||
|
|
06b8e4df27 | ||
|
|
b103be99e8 | ||
|
|
28ed42d879 | ||
|
|
98f0d75180 | ||
|
|
34487c9de4 | ||
|
|
822377be8b | ||
|
|
dd4fb85170 | ||
|
|
07b3327102 | ||
|
|
02aa75f68c | ||
|
|
07db9319ad | ||
|
|
4ae4a4ee88 | ||
|
|
a7c648b60b | ||
|
|
4d7c1ae143 | ||
|
|
cc6d1e85cc | ||
|
|
7fb116d87d | ||
|
|
cc43e01e34 | ||
|
|
6d3ccf4df5 | ||
|
|
bb3d0706d3 | ||
|
|
820dedbcd2 | ||
|
|
aed6f2b1ea | ||
|
|
bf1885af3f | ||
|
|
d8e4f5d56b | ||
|
|
af616473aa | ||
|
|
2033ac34e5 | ||
|
|
5e239d3d88 | ||
|
|
30893afd67 | ||
|
|
a39bb7b92f | ||
|
|
b53f9f2a81 | ||
|
|
586e36906d | ||
|
|
e6f8e73705 | ||
|
|
eaf9735eda | ||
|
|
2e50e1f506 | ||
|
|
76a6c39f25 | ||
|
|
bf01c22e1f | ||
|
|
99f14e03d4 | ||
|
|
d92c8ccadf | ||
|
|
7964b724ed | ||
|
|
e0c5b45694 | ||
|
|
26407e001b | ||
|
|
3ecae3f16f | ||
|
|
5c359856ff | ||
|
|
2d618768d5 | ||
|
|
808e3be324 | ||
|
|
9e23987db8 | ||
|
|
ad76312f66 | ||
|
|
2028362fd5 | ||
|
|
13e0d6b9a1 | ||
|
|
a6255c31fe | ||
|
|
46356cbc4a | ||
|
|
0fe61d9ec7 | ||
|
|
6114d331c9 | ||
|
|
e2e074a3fd | ||
|
|
4d340dc029 | ||
|
|
fb6c5ebe9a | ||
|
|
af3273d930 | ||
|
|
8f1eb77e05 | ||
|
|
89d0d41c5a | ||
|
|
452ca8e4c6 | ||
|
|
e51b0ca15e | ||
|
|
5eeb110d74 | ||
|
|
60b2d57dc3 | ||
|
|
91898cb814 | ||
|
|
818a7f1c78 | ||
|
|
dedf343bd5 | ||
|
|
251240cc90 | ||
|
|
e5b45b6b4b | ||
|
|
a77784a6da | ||
|
|
f63f9168ff | ||
|
|
b5b2036971 | ||
|
|
a96b6e7002 | ||
|
|
f34c9b33fc | ||
|
|
faf577a9dd | ||
|
|
7708b81ef5 | ||
|
|
08998caabc | ||
|
|
848a5f1680 | ||
|
|
2e7c1d7345 | ||
|
|
28a72fa56b | ||
|
|
cd1353ae54 | ||
|
|
c9ee513fa8 | ||
|
|
2a12caa955 | ||
|
|
5e5f8d5f9b | ||
|
|
2c0fe49b86 | ||
|
|
1e227e8051 | ||
|
|
d5cf4b7eac | ||
|
|
570ec36fe3 | ||
|
|
69879920eb | ||
|
|
2b60b0f1fa | ||
|
|
c17624adab | ||
|
|
0f151a8f6b | ||
|
|
e62bf333a2 | ||
|
|
88b46b7487 | ||
|
|
fa290fbce8 | ||
|
|
1883ce1876 | ||
|
|
f3fe2bde38 | ||
|
|
811c13d7d1 | ||
|
|
521dfe08f2 | ||
|
|
2c77d121da | ||
|
|
c5dc736c53 | ||
|
|
8e93735861 | ||
|
|
ac25b138f5 | ||
|
|
b17e0c298e | ||
|
|
422f0ad7a9 | ||
|
|
34d37961c3 | ||
|
|
bdf004867d | ||
|
|
08ecca86bc | ||
|
|
520c4331e3 | ||
|
|
342d5166a0 | ||
|
|
69d39ef0cd | ||
|
|
6588e9380e | ||
|
|
4d6277330b | ||
|
|
87154e9b6f | ||
|
|
b91723344e | ||
|
|
92b36720b6 | ||
|
|
3d0310d0e0 | ||
|
|
f81cddf22e | ||
|
|
808ce6eecb | ||
|
|
665d0fd759 | ||
|
|
c519c02de8 | ||
|
|
eacac78099 | ||
|
|
a925036ff8 | ||
|
|
1468293f3e | ||
|
|
25924ca4e8 | ||
|
|
6c8ace0ce8 | ||
|
|
acc1af0f51 | ||
|
|
c92c439d17 | ||
|
|
410fad3b41 | ||
|
|
dce20680d7 | ||
|
|
f95be6a0df | ||
|
|
a342302114 | ||
|
|
925a992d1b | ||
|
|
61ecbe4273 | ||
|
|
65c7e27a43 | ||
|
|
57b56010da | ||
|
|
ef89249019 | ||
|
|
bc64cf3e47 | ||
|
|
def70dde72 | ||
|
|
b52f7cfe86 | ||
|
|
81b512a7b3 | ||
|
|
57d6185374 | ||
|
|
e288aa07fb | ||
|
|
50006e4c42 | ||
|
|
23cf120977 | ||
|
|
04d8593f38 | ||
|
|
28e39f7f76 | ||
|
|
de3377132d | ||
|
|
b351cd94d7 | ||
|
|
d238e06f86 | ||
|
|
2fc59ecc30 | ||
|
|
0047d24698 | ||
|
|
89a89e1785 | ||
|
|
1952d275f7 | ||
|
|
043095b605 | ||
|
|
bccaa78a90 | ||
|
|
f402c89539 | ||
|
|
431d3578a5 | ||
|
|
5f02d86841 | ||
|
|
a7ec57d4be | ||
|
|
4eeb111fa3 | ||
|
|
1ea5cc497f | ||
|
|
b601cf6bc6 | ||
|
|
5057caa7fc | ||
|
|
95bef53d37 | ||
|
|
ea019a057b | ||
|
|
1d378e416d | ||
|
|
d3b758d10a | ||
|
|
7b9c2d2978 | ||
|
|
9d38543cb0 | ||
|
|
b860a317b9 | ||
|
|
9591c903f7 | ||
|
|
65fbb8bc60 | ||
|
|
97428f2ba2 | ||
|
|
9ab6a7b7ff | ||
|
|
e2ad6fe3d8 | ||
|
|
6781d08c9b | ||
|
|
36a2ce2c23 | ||
|
|
c7c71089ce | ||
|
|
52c67d715b | ||
|
|
f084ab339b | ||
|
|
8352f52fef | ||
|
|
b28735d63b | ||
|
|
4c105398f7 | ||
|
|
828f7946ea | ||
|
|
23c663d5d4 | ||
|
|
6a99789c92 | ||
|
|
52e13164b4 | ||
|
|
28f2582256 | ||
|
|
652f6058d1 | ||
|
|
717aab7c8b | ||
|
|
5e799b5284 | ||
|
|
9f36b25d4e | ||
|
|
d9a2651a5a | ||
|
|
5fcd1e391d | ||
|
|
36089a1400 | ||
|
|
e7b1d2efaa | ||
|
|
bf453ad8cd | ||
|
|
fbc1b3e316 | ||
|
|
400819175d | ||
|
|
3c34b539b0 | ||
|
|
c8058e9636 | ||
|
|
f2474c5c45 | ||
|
|
7acc36d39d | ||
|
|
b01db991a5 | ||
|
|
86385a1c19 | ||
|
|
96ab6b51b8 | ||
|
|
8c849b9002 | ||
|
|
6a0d4cb5a9 | ||
|
|
72002ce70e | ||
|
|
c67539cf5b | ||
|
|
dcd3d2084d | ||
|
|
585bb6dac8 | ||
|
|
02dc49c272 | ||
|
|
5df398ec31 | ||
|
|
699696e8d1 | ||
|
|
93e35a53ed | ||
|
|
a269098a0e | ||
|
|
cac3055261 | ||
|
|
6f7e6cc944 | ||
|
|
0a841fcc50 | ||
|
|
3c64c9b0e9 | ||
|
|
34df25da39 | ||
|
|
9586fb95d1 | ||
|
|
3ee6348e41 | ||
|
|
543f2c8152 | ||
|
|
16d11be213 | ||
|
|
e49b568fd4 | ||
|
|
22ab830ff3 | ||
|
|
095d3181cd | ||
|
|
9aa14a2e83 | ||
|
|
ac15ce576b | ||
|
|
498b59e998 | ||
|
|
63c420254a | ||
|
|
765e641d08 | ||
|
|
be16d10b7d | ||
|
|
4b808611e9 | ||
|
|
7afe202e20 | ||
|
|
039810eef3 | ||
|
|
ff43b45113 | ||
|
|
7cd4c3bdd3 | ||
|
|
c12c9e97c2 | ||
|
|
b3169deda7 | ||
|
|
0ea41e2f71 | ||
|
|
3afb564a48 | ||
|
|
7ff3f752e2 | ||
|
|
d821ead92a | ||
|
|
e42ce64127 | ||
|
|
9d2b0b4e03 | ||
|
|
b5e6ae0d69 | ||
|
|
08f1eac8b2 | ||
|
|
6ed3da33a2 | ||
|
|
a9a00f139b | ||
|
|
63d8071dbd | ||
|
|
d20caa9d60 | ||
|
|
7e40d4246c | ||
|
|
5a2b14cfa4 | ||
|
|
f2d218e5ad | ||
|
|
b493d5bba5 | ||
|
|
c9055f2aef | ||
|
|
9fed7cab5f | ||
|
|
eb5c4b7c4f | ||
|
|
2ab3534a4b | ||
|
|
9816e677a6 | ||
|
|
fc01a70b65 | ||
|
|
7221337442 | ||
|
|
051a1e4772 | ||
|
|
274741a9d5 | ||
|
|
25c01adf51 | ||
|
|
bf2d54c3ef | ||
|
|
49cb8fd9d3 | ||
|
|
e536316e3d | ||
|
|
a6c46eb8e5 | ||
|
|
1a270374e0 | ||
|
|
e73eafbd88 | ||
|
|
9fc3e05b76 | ||
|
|
31c604331c | ||
|
|
3fcdaaefe0 | ||
|
|
20dd744680 | ||
|
|
e4636b99f7 | ||
|
|
10e7abb579 | ||
|
|
d3f03b7acb | ||
|
|
7e53fc9d6a | ||
|
|
0059a6de46 | ||
|
|
22e1758d5b | ||
|
|
59cdc32970 | ||
|
|
adb51cf733 | ||
|
|
d97a9bf8e8 | ||
|
|
f034472e2a | ||
|
|
ed328d2df8 | ||
|
|
1520dc8755 | ||
|
|
bf601c3126 | ||
|
|
ab48e4a466 | ||
|
|
8ef0f5b047 | ||
|
|
2c14d134be | ||
|
|
9cd21bb5a0 | ||
|
|
bd061ac2ee | ||
|
|
dd3e821857 | ||
|
|
b38b7019ea | ||
|
|
2c71ee7853 | ||
|
|
540c62061d | ||
|
|
221ef07c8b | ||
|
|
29fc7ea154 | ||
|
|
7b157aeff1 | ||
|
|
e50644edee | ||
|
|
5f619e6f01 | ||
|
|
b266fb37a3 | ||
|
|
c9caf44c2e | ||
|
|
0c87a9ad2c | ||
|
|
c680b437f5 | ||
|
|
4988349677 | ||
|
|
e35d56defe | ||
|
|
5c86f332b2 | ||
|
|
002861f13b | ||
|
|
dbec3d7c50 | ||
|
|
89cde158d6 | ||
|
|
d7b76aadb2 | ||
|
|
e09fefd389 | ||
|
|
febc485da6 | ||
|
|
2ae709c2ba | ||
|
|
0ccfdd4711 | ||
|
|
0e59243b83 | ||
|
|
01bbd04a5a | ||
|
|
a3b2d384f5 | ||
|
|
50238f8d72 | ||
|
|
704470d606 | ||
|
|
f7e6195466 | ||
|
|
a0bb7c3ed0 | ||
|
|
e3a6c9a6cf | ||
|
|
99598d87a9 | ||
|
|
b5df50893b | ||
|
|
f46b3d15cd | ||
|
|
041b4ec66e | ||
|
|
703e9673c2 | ||
|
|
e7bd93b4b0 | ||
|
|
a401c71d3e | ||
|
|
5bae233334 | ||
|
|
c4edd3047f | ||
|
|
c50da1593a | ||
|
|
1d06426281 | ||
|
|
9c5b693dd5 | ||
|
|
5fecc70db1 | ||
|
|
ff24023b39 | ||
|
|
1a04e2d1b8 | ||
|
|
52c4dd0e35 | ||
|
|
ff90f6a440 | ||
|
|
e24d5c172f | ||
|
|
0918f452a0 | ||
|
|
839fe49e61 | ||
|
|
ff050d634a | ||
|
|
228670df78 | ||
|
|
cfe4638665 | ||
|
|
b7352b1345 | ||
|
|
32ae8fc2d0 | ||
|
|
86df4c1d8d | ||
|
|
dc4a88029c | ||
|
|
5da9b2ede7 | ||
|
|
29cfcfaf0f | ||
|
|
61bfd347ea | ||
|
|
b7436c0b22 | ||
|
|
8a45dfac5c | ||
|
|
69f5d8cd0f | ||
|
|
9a57e8fcb0 | ||
|
|
a9d75ca4f4 | ||
|
|
ccb6fc3010 | ||
|
|
4e9a05fe11 | ||
|
|
8a294e4134 | ||
|
|
aad9a539c1 | ||
|
|
fd6ac529fb | ||
|
|
009cea1abf | ||
|
|
4c3c14ec32 | ||
|
|
636c9db1e3 | ||
|
|
71f625bbd3 | ||
|
|
aea2e9a6bb | ||
|
|
3f6f3c14c4 | ||
|
|
b1d77b7c03 | ||
|
|
49233e4734 | ||
|
|
e2b5ecb78b | ||
|
|
351ce67eae | ||
|
|
44af5e439c | ||
|
|
dbc0d500d8 | ||
|
|
86736aa480 | ||
|
|
57eb05c0e3 | ||
|
|
f6fe6e6bff | ||
|
|
18560f9430 | ||
|
|
cb0ba647ed | ||
|
|
f9fceb7ffc | ||
|
|
2697c9465b | ||
|
|
8d204655be | ||
|
|
8414a22356 | ||
|
|
36e4a8b444 | ||
|
|
949c71dc97 | ||
|
|
a6f6b8da7f | ||
|
|
a9f123e864 | ||
|
|
a64a505817 | ||
|
|
352004221e | ||
|
|
fc6a3e31c2 | ||
|
|
a32b58fdf1 | ||
|
|
2d50ecbecf | ||
|
|
87f1ffec05 | ||
|
|
f0dfde9fa1 | ||
|
|
e36dc2d05e | ||
|
|
08c8fa2c90 | ||
|
|
fe6621357e | ||
|
|
4a0067a2c5 | ||
|
|
b270ff335d | ||
|
|
7d2fcf59fd | ||
|
|
d26c43103d | ||
|
|
389889ad70 | ||
|
|
0af73c7903 | ||
|
|
8aa73bba10 | ||
|
|
5396b05237 | ||
|
|
e898345ff1 | ||
|
|
f39bf9c1e5 | ||
|
|
9483a9c8c4 | ||
|
|
52639a0a7c | ||
|
|
a1e10f384e | ||
|
|
27d4b3b8ad | ||
|
|
6438bd84fb | ||
|
|
4b8e910e3f | ||
|
|
9c0e463698 | ||
|
|
2092939353 | ||
|
|
b9d55fd1ed | ||
|
|
c1748d586e | ||
|
|
4c55b9c58c | ||
|
|
312958c573 | ||
|
|
65a489ebdd | ||
|
|
bd392e2efc | ||
|
|
25ad33a377 | ||
|
|
abc83f6cb0 | ||
|
|
f61a82a568 | ||
|
|
1c3ed71d36 | ||
|
|
8215a018e9 | ||
|
|
bf3b678727 | ||
|
|
f6e3070dd8 | ||
|
|
4996967c79 | ||
|
|
5887fe8302 | ||
|
|
0fc8e8d483 | ||
|
|
55388724af | ||
|
|
32efa5d83e | ||
|
|
275c12150e | ||
|
|
62468198d6 | ||
|
|
43d5e7a8cc | ||
|
|
7524493c3c | ||
|
|
5759de079b | ||
|
|
f3d5d27c8f | ||
|
|
c030be4d3f | ||
|
|
50cf57affb | ||
|
|
25c62319a3 | ||
|
|
27abb1280a | ||
|
|
c73f0eee4c | ||
|
|
9208227d92 | ||
|
|
725e8c69f5 | ||
|
|
facd4f63ec | ||
|
|
2e1d14b8b1 | ||
|
|
e8272759be | ||
|
|
ebbfab608c | ||
|
|
a5e1f8fe19 | ||
|
|
1b2de953d0 | ||
|
|
df5d03b0bc | ||
|
|
11c1fe8827 | ||
|
|
f29622abe1 | ||
|
|
10e411f8c1 | ||
|
|
6405799cc2 | ||
|
|
0afa41d08a | ||
|
|
48f3dfe455 | ||
|
|
6f0bfb286a | ||
|
|
2e54d3f98d | ||
|
|
67b4dcf8ae | ||
|
|
ad91362571 | ||
|
|
76a3e75bc7 | ||
|
|
e88418a01b | ||
|
|
27e08d37ea | ||
|
|
e069687477 | ||
|
|
ef0e611e52 | ||
|
|
d58d0e89c7 | ||
|
|
dfbf225403 | ||
|
|
e962762046 | ||
|
|
8166d0de79 | ||
|
|
d9c33f19e2 | ||
|
|
0cc3902ffc | ||
|
|
4752096520 | ||
|
|
dcadcdf056 | ||
|
|
b6394b7c6d | ||
|
|
3ec9bcaed6 | ||
|
|
d5c59292c8 | ||
|
|
a20e71b32a | ||
|
|
1254ec2849 | ||
|
|
ca144bae90 | ||
|
|
850368b529 | ||
|
|
7bbdfc5553 | ||
|
|
ba73cb1b75 | ||
|
|
eca36cb868 | ||
|
|
2a14473e8c | ||
|
|
9880a425f4 | ||
|
|
13696b1ec4 | ||
|
|
764eb960c6 | ||
|
|
17b55fc23d | ||
|
|
e20c994b82 | ||
|
|
465cd3d1f9 | ||
|
|
412351fd1e | ||
|
|
5776e70d7c | ||
|
|
1ccc6e342c | ||
|
|
a53481e2da | ||
|
|
01b1b688b1 | ||
|
|
3d78248aaf | ||
|
|
cf703f6ac4 | ||
|
|
2012c769f6 | ||
|
|
c8a4eb426c | ||
|
|
351ecf9bd4 | ||
|
|
e6f42fa6f0 | ||
|
|
582ac4ac81 | ||
|
|
6e30bacae3 | ||
|
|
5d136a18af | ||
|
|
1a47e4b524 | ||
|
|
c296b4c348 | ||
|
|
c52cb7bbad | ||
|
|
1923e0312b | ||
|
|
c8998941a5 | ||
|
|
e41ea42074 | ||
|
|
ed5f207eba | ||
|
|
a76b8e745b | ||
|
|
68d29c5af5 | ||
|
|
c3acf08c02 | ||
|
|
ef9e6e4d6e | ||
|
|
f3158c8b24 | ||
|
|
ac4a179703 | ||
|
|
dd4ea51d1f | ||
|
|
45f6926a5a | ||
|
|
5ca4bac10a | ||
|
|
ec026ab3a8 | ||
|
|
0e5e559283 | ||
|
|
417a3cdf51 | ||
|
|
7fa98e288f | ||
|
|
c693c219f4 | ||
|
|
e5d4e12457 | ||
|
|
33212d1abf | ||
|
|
7a16f846eb | ||
|
|
6873f09f57 | ||
|
|
382793de9a | ||
|
|
d4e76185bd | ||
|
|
8566dd9100 | ||
|
|
f479eae714 | ||
|
|
31067530a5 | ||
|
|
6505c30c2b | ||
|
|
7487ffcbcb | ||
|
|
f79299c240 | ||
|
|
2f07225984 | ||
|
|
c99b2b59c2 | ||
|
|
78633c5768 | ||
|
|
c041cc483c | ||
|
|
83a12d980e | ||
|
|
491f7e96f0 | ||
|
|
bfb9cb6732 | ||
|
|
3c9712d683 | ||
|
|
ca4107d450 | ||
|
|
148f5d9418 | ||
|
|
51ab0f0b78 | ||
|
|
bf0cce4ad8 | ||
|
|
82d5d50d61 | ||
|
|
ecb1c77f8b | ||
|
|
f9a8629157 | ||
|
|
e2b655a6cc | ||
|
|
04e6f475b4 | ||
|
|
0082c5b459 | ||
|
|
3fd130869e | ||
|
|
d000440927 | ||
|
|
4fb750de43 | ||
|
|
9632ac0e02 | ||
|
|
35078fd52f | ||
|
|
0bb81e5b2d | ||
|
|
35a2258f12 | ||
|
|
b650704877 | ||
|
|
0c0dec2534 | ||
|
|
d1b051a6bd | ||
|
|
27204aa53c | ||
|
|
8aedac81a5 | ||
|
|
42007d03d4 | ||
|
|
821c1a8bbd | ||
|
|
bab562dc3a | ||
|
|
f63fd9696f | ||
|
|
cd7af19e7c | ||
|
|
64bd33a94e | ||
|
|
60e6366521 | ||
|
|
072b2c445c | ||
|
|
219fe41831 | ||
|
|
dcc8bb83af | ||
|
|
a8e3521f3c | ||
|
|
706dc6d116 | ||
|
|
84accb6df6 | ||
|
|
8421570b18 | ||
|
|
d355543ac9 | ||
|
|
3a597c5aa6 | ||
|
|
c7dddaded4 | ||
|
|
aae4b2ea5d | ||
|
|
310e2a0b20 | ||
|
|
0b04d143ac | ||
|
|
d1f4c2ab57 | ||
|
|
7e4d12f880 | ||
|
|
ecd65003d4 | ||
|
|
fed37b48a1 | ||
|
|
3fba3a5e2e | ||
|
|
9e7e8ab116 | ||
|
|
1bec1faf6d | ||
|
|
e64246f642 | ||
|
|
fb2b7ade41 | ||
|
|
4de44a99eb | ||
|
|
39fbf9c56f | ||
|
|
021055f0b8 | ||
|
|
c2e0ea97d8 | ||
|
|
153aadadae | ||
|
|
1319ff1129 | ||
|
|
7df72ddb96 | ||
|
|
1d9ce2afc5 | ||
|
|
53986279d7 | ||
|
|
26d6738abb | ||
|
|
7fa5cab8e3 | ||
|
|
4f2e2fa297 | ||
|
|
0473c4c79f | ||
|
|
a62b6548d2 | ||
|
|
da390d32f8 | ||
|
|
8b92456ded | ||
|
|
14e9375262 | ||
|
|
d49ee47018 | ||
|
|
af66753c1b | ||
|
|
39b35b79ba | ||
|
|
a2a83c5004 | ||
|
|
ba1222eae4 | ||
|
|
31ae337931 | ||
|
|
bab0ba9c0f | ||
|
|
c9e224e999 | ||
|
|
6123cb7c69 | ||
|
|
8040e3cf95 | ||
|
|
d447548893 | ||
|
|
269812e781 | ||
|
|
65f4d30fd0 | ||
|
|
c1dfed5c08 | ||
|
|
835079ad43 | ||
|
|
17fd9d5107 | ||
|
|
8613c02d5c | ||
|
|
dea6675c21 | ||
|
|
43cf3063e0 | ||
|
|
3b7a47fb90 | ||
|
|
79248e8b74 | ||
|
|
4620ad6124 | ||
|
|
25cdbacecc | ||
|
|
4ec636c08f | ||
|
|
4cb30a22ac | ||
|
|
c632b0e1d4 | ||
|
|
714d28a61a | ||
|
|
c60989a7be | ||
|
|
11b727fdf7 | ||
|
|
a1dfd355f7 | ||
|
|
fcb2cc2471 | ||
|
|
177617e6e3 | ||
|
|
e0b4226930 | ||
|
|
426e6a1b46 | ||
|
|
66083c5e97 | ||
|
|
aff4f1e9e2 | ||
|
|
3c68348868 | ||
|
|
7f2a6e7403 | ||
|
|
11069085e3 | ||
|
|
854d735ab3 | ||
|
|
a4ab52918b | ||
|
|
eb895d2095 | ||
|
|
67cbaabd99 | ||
|
|
4402a6eb4c | ||
|
|
6ae1efcf9f | ||
|
|
1d136ab0df | ||
|
|
7721049ed7 | ||
|
|
e6f21873c3 | ||
|
|
499903bd3d | ||
|
|
2d0d794a9d | ||
|
|
a55787f40c | ||
|
|
990a4d4774 | ||
|
|
6a60f01753 | ||
|
|
30ecb58e06 | ||
|
|
3b689ef39c | ||
|
|
170d52e0db | ||
|
|
92d93d658c | ||
|
|
d7a2816c58 | ||
|
|
a30d2f291c | ||
|
|
d33a158585 | ||
|
|
e21dbc4b60 | ||
|
|
8a754421fe | ||
|
|
a73fd55fc2 | ||
|
|
45630d74f3 | ||
|
|
a6d31f05ee | ||
|
|
05f9dede70 | ||
|
|
7c870556c6 | ||
|
|
420c860424 | ||
|
|
828e291538 | ||
|
|
eea78531a1 | ||
|
|
c8ccb06f11 | ||
|
|
f5b7cc81d8 | ||
|
|
ae784dc74c | ||
|
|
056c72d50d | ||
|
|
b5714cd70f | ||
|
|
74aca2137b | ||
|
|
d09dff3ae3 | ||
|
|
d280380c8d | ||
|
|
8a08d1fb5d | ||
|
|
ea652e3587 | ||
|
|
7a6df38515 | ||
|
|
bba6d6897d | ||
|
|
33c08812cc | ||
|
|
f68a3a9334 | ||
|
|
0698be3995 | ||
|
|
064589934c | ||
|
|
e9e92afc9e | ||
|
|
e86f2e993f | ||
|
|
d26cd85306 | ||
|
|
73f80a8ea1 | ||
|
|
b7dff4bbab | ||
|
|
31d964c16a | ||
|
|
7f895abc24 | ||
|
|
0f406c38eb | ||
|
|
6433da13a3 | ||
|
|
fa1adfd934 | ||
|
|
36ffef083b | ||
|
|
fe89dcdc08 | ||
|
|
be36eef939 | ||
|
|
6a0268b852 | ||
|
|
b7b23ffdb2 | ||
|
|
ad76709d00 | ||
|
|
53c231a7eb | ||
|
|
d44ce82aa1 | ||
|
|
a055de48e4 | ||
|
|
37b8d665fe | ||
|
|
dd7c8dabb1 | ||
|
|
e41a9875e3 | ||
|
|
c5c42c4338 | ||
|
|
531428b8b0 | ||
|
|
ea8068e001 | ||
|
|
7842a55c81 | ||
|
|
51d39862b1 | ||
|
|
bfea6ca79b | ||
|
|
6297395018 | ||
|
|
a5b49dbfa6 | ||
|
|
7c0d777173 | ||
|
|
74878276fc | ||
|
|
226e3b1dad | ||
|
|
7752794fc5 | ||
|
|
b3094d6a53 | ||
|
|
e3640e710f | ||
|
|
2ef64b55c5 | ||
|
|
7f6672bb37 | ||
|
|
68a3b31628 | ||
|
|
1b35855e68 | ||
|
|
1e1837000d | ||
|
|
e2d5257632 | ||
|
|
387c75793b | ||
|
|
4f3a74d08a | ||
|
|
072cd5b83e | ||
|
|
cfd42ea162 | ||
|
|
b55544b860 | ||
|
|
5becaebdda | ||
|
|
1814e4a46b | ||
|
|
4f8f59f705 | ||
|
|
aca306d120 | ||
|
|
694395ac91 | ||
|
|
092bca0d63 | ||
|
|
a386bb476f | ||
|
|
39a520f552 | ||
|
|
663f84f8b4 | ||
|
|
8677d47777 | ||
|
|
4f1a28d460 | ||
|
|
7b142525b4 | ||
|
|
7d4f279206 | ||
|
|
51233e1931 | ||
|
|
907c14aa98 | ||
|
|
fb055750df | ||
|
|
fad05d5a2e | ||
|
|
9580b13b9f | ||
|
|
367ae906c3 | ||
|
|
f8d98ac494 | ||
|
|
3e8fd48dc0 | ||
|
|
003326f2eb | ||
|
|
b5af3aa048 | ||
|
|
a919b015b4 | ||
|
|
f94e9b6b1e | ||
|
|
1ed8e63d59 | ||
|
|
d97bc95798 | ||
|
|
5c56f15c67 | ||
|
|
3fdb68cba8 | ||
|
|
85c46becdf | ||
|
|
0cbd373817 | ||
|
|
fdbc59a159 | ||
|
|
0db37bb55c | ||
|
|
8027facb39 | ||
|
|
16c2dc2aaf | ||
|
|
dc2279b74f | ||
|
|
75275c4e93 | ||
|
|
65d3dc9cb8 | ||
|
|
66aa02fc34 | ||
|
|
be6b4ee47f | ||
|
|
90f909d2ea | ||
|
|
09fd505f08 | ||
|
|
042ccde441 | ||
|
|
442030b6ca | ||
|
|
1df9ae53f8 | ||
|
|
adf2a463fd | ||
|
|
80aaf66963 | ||
|
|
560251ab2a | ||
|
|
864c5d8908 | ||
|
|
742c21506c | ||
|
|
a6faccb4d9 | ||
|
|
69fd3e8937 | ||
|
|
41233d7f25 | ||
|
|
07286d1d76 | ||
|
|
08148c5830 | ||
|
|
969bdb06ce | ||
|
|
b0bb692af4 | ||
|
|
7bf6fd316f | ||
|
|
c1f5e04d6c | ||
|
|
5a67e72389 | ||
|
|
91c9b11647 | ||
|
|
bb2582717f | ||
|
|
d62ef35860 | ||
|
|
59c5956f93 | ||
|
|
e4f055597c | ||
|
|
4c49beb3c7 | ||
|
|
8ff742d9ab | ||
|
|
d63cd8b4cd | ||
|
|
3f0503c296 | ||
|
|
c18050bda0 | ||
|
|
6542be5588 | ||
|
|
9fb60b8015 | ||
|
|
1177b856a0 | ||
|
|
c0adaa8de8 | ||
|
|
ae8700447e | ||
|
|
d64a4ef2b4 | ||
|
|
1e22b1e959 | ||
|
|
e077ad56bd | ||
|
|
f1e00f8c8e | ||
|
|
9a152e588e | ||
|
|
16f42a3d03 | ||
|
|
6c8d0f1852 | ||
|
|
b59cf6572b | ||
|
|
4fa11dfa68 | ||
|
|
352bdd9fb5 | ||
|
|
96ff9a162c | ||
|
|
af15a4e710 | ||
|
|
18426b71e4 | ||
|
|
7063aa6009 | ||
|
|
286ca07cc8 | ||
|
|
58b6311821 | ||
|
|
e553c0768e | ||
|
|
62d4b29662 | ||
|
|
16bc60644d |
@@ -49,7 +49,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
|||||||
ConstructorInitializerIndentWidth: 4
|
ConstructorInitializerIndentWidth: 4
|
||||||
ContinuationIndentWidth: 4
|
ContinuationIndentWidth: 4
|
||||||
Cpp11BracedListStyle: true
|
Cpp11BracedListStyle: true
|
||||||
DerivePointerAlignment: true
|
DerivePointerAlignment: false
|
||||||
DisableFormat: false
|
DisableFormat: false
|
||||||
ExperimentalAutoDetectBinPacking: false
|
ExperimentalAutoDetectBinPacking: false
|
||||||
FixNamespaceComments: true
|
FixNamespaceComments: true
|
||||||
|
|||||||
47
.clang-tidy
47
.clang-tidy
@@ -4,14 +4,24 @@ Checks: >-
|
|||||||
-abseil-*,
|
-abseil-*,
|
||||||
-android-*,
|
-android-*,
|
||||||
-boost-*,
|
-boost-*,
|
||||||
-bugprone-macro-parentheses,
|
-bugprone-branch-clone,
|
||||||
|
-bugprone-narrowing-conversions,
|
||||||
|
-bugprone-signed-char-misuse,
|
||||||
|
-bugprone-too-small-loop-variable,
|
||||||
-cert-dcl50-cpp,
|
-cert-dcl50-cpp,
|
||||||
-cert-err58-cpp,
|
-cert-err58-cpp,
|
||||||
-clang-analyzer-core.CallAndMessage,
|
-cert-oop57-cpp,
|
||||||
|
-cert-str34-c,
|
||||||
|
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
||||||
-clang-analyzer-osx.*,
|
-clang-analyzer-osx.*,
|
||||||
-clang-analyzer-security.*,
|
-clang-diagnostic-shadow-field,
|
||||||
|
-cppcoreguidelines-avoid-c-arrays,
|
||||||
-cppcoreguidelines-avoid-goto,
|
-cppcoreguidelines-avoid-goto,
|
||||||
-cppcoreguidelines-c-copy-assignment-signature,
|
-cppcoreguidelines-avoid-magic-numbers,
|
||||||
|
-cppcoreguidelines-init-variables,
|
||||||
|
-cppcoreguidelines-macro-usage,
|
||||||
|
-cppcoreguidelines-narrowing-conversions,
|
||||||
|
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||||
-cppcoreguidelines-owning-memory,
|
-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,
|
||||||
@@ -24,40 +34,51 @@ 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-*,
|
|
||||||
-fuchsia-default-arguments,
|
-fuchsia-default-arguments,
|
||||||
-fuchsia-multiple-inheritance,
|
-fuchsia-multiple-inheritance,
|
||||||
-fuchsia-overloaded-operator,
|
-fuchsia-overloaded-operator,
|
||||||
-fuchsia-statically-constructed-objects,
|
-fuchsia-statically-constructed-objects,
|
||||||
|
-fuchsia-default-arguments-declarations,
|
||||||
|
-fuchsia-default-arguments-calls,
|
||||||
-google-build-using-namespace,
|
-google-build-using-namespace,
|
||||||
-google-explicit-constructor,
|
-google-explicit-constructor,
|
||||||
-google-readability-braces-around-statements,
|
-google-readability-braces-around-statements,
|
||||||
-google-readability-casting,
|
-google-readability-casting,
|
||||||
-google-readability-todo,
|
-google-readability-todo,
|
||||||
-google-runtime-int,
|
|
||||||
-google-runtime-references,
|
-google-runtime-references,
|
||||||
-hicpp-*,
|
-hicpp-*,
|
||||||
|
-llvm-else-after-return,
|
||||||
-llvm-header-guard,
|
-llvm-header-guard,
|
||||||
-llvm-include-order,
|
-llvm-include-order,
|
||||||
-misc-unconventional-assign-operator,
|
-llvm-qualified-auto,
|
||||||
|
-llvmlibc-*,
|
||||||
|
-misc-non-private-member-variables-in-classes,
|
||||||
|
-misc-no-recursion,
|
||||||
-misc-unused-parameters,
|
-misc-unused-parameters,
|
||||||
-modernize-deprecated-headers,
|
-modernize-avoid-c-arrays,
|
||||||
-modernize-pass-by-value,
|
|
||||||
-modernize-pass-by-value,
|
|
||||||
-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,
|
||||||
-mpi-*,
|
-mpi-*,
|
||||||
-objc-*,
|
-objc-*,
|
||||||
-performance-unnecessary-value-param,
|
|
||||||
-readability-braces-around-statements,
|
-readability-braces-around-statements,
|
||||||
|
-readability-const-return-type,
|
||||||
|
-readability-convert-member-functions-to-static,
|
||||||
-readability-else-after-return,
|
-readability-else-after-return,
|
||||||
-readability-implicit-bool-conversion,
|
-readability-implicit-bool-conversion,
|
||||||
|
-readability-isolate-declaration,
|
||||||
|
-readability-magic-numbers,
|
||||||
|
-readability-make-member-function-const,
|
||||||
-readability-named-parameter,
|
-readability-named-parameter,
|
||||||
|
-readability-qualified-auto,
|
||||||
|
-readability-redundant-access-specifiers,
|
||||||
-readability-redundant-member-init,
|
-readability-redundant-member-init,
|
||||||
-warnings-as-errors,
|
-readability-redundant-string-init,
|
||||||
-zircon-*
|
-readability-uppercase-literal-suffix,
|
||||||
|
-readability-use-anyofallof,
|
||||||
|
-warnings-as-errors
|
||||||
WarningsAsErrors: '*'
|
WarningsAsErrors: '*'
|
||||||
HeaderFilterRegex: '^.*/src/esphome/.*'
|
HeaderFilterRegex: '^.*/src/esphome/.*'
|
||||||
AnalyzeTemporaryDtors: false
|
AnalyzeTemporaryDtors: false
|
||||||
|
|||||||
2
.coveragerc
Normal file
2
.coveragerc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[run]
|
||||||
|
omit = esphome/components/*
|
||||||
57
.devcontainer/devcontainer.json
Normal file
57
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"name": "ESPHome Dev",
|
||||||
|
"context": "..",
|
||||||
|
"dockerFile": "../docker/Dockerfile.dev",
|
||||||
|
"postCreateCommand": [
|
||||||
|
"script/devcontainer-post-create"
|
||||||
|
],
|
||||||
|
"runArgs": [
|
||||||
|
"--privileged",
|
||||||
|
"-e",
|
||||||
|
"ESPHOME_DASHBOARD_USE_PING=1"
|
||||||
|
],
|
||||||
|
"appPort": 6052,
|
||||||
|
"extensions": [
|
||||||
|
// python
|
||||||
|
"ms-python.python",
|
||||||
|
"visualstudioexptteam.vscodeintellicode",
|
||||||
|
// yaml
|
||||||
|
"redhat.vscode-yaml",
|
||||||
|
// cpp
|
||||||
|
"ms-vscode.cpptools",
|
||||||
|
// editorconfig
|
||||||
|
"editorconfig.editorconfig",
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"python.languageServer": "Pylance",
|
||||||
|
"python.pythonPath": "/usr/bin/python3",
|
||||||
|
"python.linting.pylintEnabled": true,
|
||||||
|
"python.linting.enabled": true,
|
||||||
|
"python.formatting.provider": "black",
|
||||||
|
"editor.formatOnPaste": false,
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.formatOnType": true,
|
||||||
|
"files.trimTrailingWhitespace": true,
|
||||||
|
"terminal.integrated.defaultProfile.linux": "bash",
|
||||||
|
"yaml.customTags": [
|
||||||
|
"!secret scalar",
|
||||||
|
"!lambda scalar",
|
||||||
|
"!include_dir_named scalar",
|
||||||
|
"!include_dir_list scalar",
|
||||||
|
"!include_dir_merge_list scalar",
|
||||||
|
"!include_dir_merge_named scalar"
|
||||||
|
],
|
||||||
|
"files.exclude": {
|
||||||
|
"**/.git": true,
|
||||||
|
"**/.DS_Store": true,
|
||||||
|
"**/*.pyc": {
|
||||||
|
"when": "$(basename).py"
|
||||||
|
},
|
||||||
|
"**/__pycache__": true
|
||||||
|
},
|
||||||
|
"files.associations": {
|
||||||
|
"**/.vscode/*.json": "jsonc"
|
||||||
|
},
|
||||||
|
"C_Cpp.clang_format_path": "/usr/bin/clang-format-11",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -103,6 +103,10 @@ venv.bak/
|
|||||||
# mypy
|
# mypy
|
||||||
.mypy_cache/
|
.mypy_cache/
|
||||||
|
|
||||||
|
# PlatformIO
|
||||||
|
.pio/
|
||||||
|
|
||||||
|
# ESPHome
|
||||||
config/
|
config/
|
||||||
examples/
|
examples/
|
||||||
Dockerfile
|
Dockerfile
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ insert_final_newline = true
|
|||||||
charset = utf-8
|
charset = utf-8
|
||||||
|
|
||||||
# python
|
# python
|
||||||
[*.{py}]
|
[*.py]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
@@ -25,3 +25,10 @@ indent_size = 2
|
|||||||
[*.{yaml,yml}]
|
[*.{yaml,yml}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
quote_type = single
|
||||||
|
|
||||||
|
# JSON
|
||||||
|
[*.json]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
|||||||
7
.github/FUNDING.yml
vendored
7
.github/FUNDING.yml
vendored
@@ -1,8 +1,3 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
github:
|
custom: https://www.nabucasa.com
|
||||||
patreon: ottowinter
|
|
||||||
open_collective:
|
|
||||||
ko_fi:
|
|
||||||
tidelift:
|
|
||||||
custom: https://esphome.io/guides/supporters.html
|
|
||||||
|
|||||||
12
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
12
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: Issue Tracker
|
||||||
|
url: https://github.com/esphome/issues
|
||||||
|
about: Please create bug reports in the dedicated issue tracker.
|
||||||
|
- name: Feature Request Tracker
|
||||||
|
url: https://github.com/esphome/feature-requests
|
||||||
|
about: Please create feature requests in the dedicated feature request tracker.
|
||||||
|
- name: Frequently Asked Question
|
||||||
|
url: https://esphome.io/guides/faq.html
|
||||||
|
about: Please view the FAQ for common questions and what to include in a bug report.
|
||||||
|
|
||||||
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
32
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,13 +1,39 @@
|
|||||||
## Description:
|
# What does this implement/fix?
|
||||||
|
|
||||||
|
Quick description and explanation of changes
|
||||||
|
|
||||||
**Related issue (if applicable):** fixes <link to issue>
|
## Types of changes
|
||||||
|
|
||||||
|
- [ ] Bugfix (non-breaking change which fixes an issue)
|
||||||
|
- [ ] New feature (non-breaking change which adds functionality)
|
||||||
|
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||||
|
- [ ] Other
|
||||||
|
|
||||||
|
**Related issue or feature (if applicable):** fixes <link to issue>
|
||||||
|
|
||||||
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>
|
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>
|
||||||
|
|
||||||
|
## Test Environment
|
||||||
|
|
||||||
|
- [ ] ESP32
|
||||||
|
- [ ] ESP8266
|
||||||
|
|
||||||
|
## Example entry for `config.yaml`:
|
||||||
|
<!--
|
||||||
|
Supplying a configuration snippet, makes it easier for a maintainer to test
|
||||||
|
your PR. Furthermore, for new integrations, it gives an impression of how
|
||||||
|
the configuration would look like.
|
||||||
|
Note: Remove this section if this PR does not have an example entry.
|
||||||
|
-->
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# Example config.yaml
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
## Checklist:
|
## Checklist:
|
||||||
- [ ] The code change is tested and works locally.
|
- [ ] The code change is tested and works locally.
|
||||||
- [ ] Tests have been added to verify that the new code works (under `tests/` folder).
|
- [ ] Tests have been added to verify that the new code works (under `tests/` folder).
|
||||||
|
|
||||||
If user exposed functionality or configuration variables are added/changed:
|
If user exposed functionality or configuration variables are added/changed:
|
||||||
- [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).
|
- [ ] Documentation added/updated in [esphome-docs](https://github.com/esphome/esphome-docs).
|
||||||
|
|||||||
9
.github/dependabot.yml
vendored
Normal file
9
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "pip"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
ignore:
|
||||||
|
# Hypotehsis is only used for testing and is updated quite often
|
||||||
|
- dependency-name: hypothesis
|
||||||
36
.github/lock.yml
vendored
Normal file
36
.github/lock.yml
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# 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
Normal file
59
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# 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
|
||||||
40
.github/workflows/ci-docker.yml
vendored
Normal file
40
.github/workflows/ci-docker.yml
vendored
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
name: CI for docker images
|
||||||
|
|
||||||
|
# Only run when docker paths change
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [dev, beta, release]
|
||||||
|
paths:
|
||||||
|
- 'docker/**'
|
||||||
|
- '.github/workflows/**'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'docker/**'
|
||||||
|
- '.github/workflows/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-docker:
|
||||||
|
name: Build docker containers
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: [amd64, armv7, aarch64]
|
||||||
|
build_type: ["ha-addon", "docker", "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=check" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Run build
|
||||||
|
run: |
|
||||||
|
docker/build.py \
|
||||||
|
--tag "${TAG}" \
|
||||||
|
--arch "${{ matrix.arch }}" \
|
||||||
|
--build-type "${{ matrix.build_type }}" \
|
||||||
|
build
|
||||||
139
.github/workflows/ci.yml
vendored
Normal file
139
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
# THESE JOBS ARE COPIED IN release.yml and release-dev.yml
|
||||||
|
# PLEASE ALSO UPDATE THOSE FILES WHEN CHANGING LINES HERE
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [dev, beta, release]
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
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
|
||||||
|
# Set up the pio project so that the cpp checks know how files are compiled
|
||||||
|
# (build flags, libraries etc)
|
||||||
|
- name: Set up platformio environment
|
||||||
|
run: pio init --ide atom
|
||||||
|
|
||||||
|
- name: Register problem matchers
|
||||||
|
run: |
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||||
|
|
||||||
|
- name: Run clang-format
|
||||||
|
run: script/clang-format -i
|
||||||
|
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: Suggest changes
|
||||||
|
run: script/ci-suggest-changes
|
||||||
|
|
||||||
|
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 }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- id: ci-custom
|
||||||
|
name: Run script/ci-custom
|
||||||
|
- id: lint-python
|
||||||
|
name: Run script/lint-python
|
||||||
|
- id: test
|
||||||
|
file: tests/test1.yaml
|
||||||
|
name: Test tests/test1.yaml
|
||||||
|
- id: test
|
||||||
|
file: tests/test2.yaml
|
||||||
|
name: Test tests/test2.yaml
|
||||||
|
- id: test
|
||||||
|
file: tests/test3.yaml
|
||||||
|
name: Test tests/test3.yaml
|
||||||
|
- id: test
|
||||||
|
file: tests/test4.yaml
|
||||||
|
name: Test tests/test4.yaml
|
||||||
|
- id: pytest
|
||||||
|
name: Run pytest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: '3.7'
|
||||||
|
|
||||||
|
- name: Cache pip modules
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.cache/pip
|
||||||
|
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||||
|
restore-keys: |
|
||||||
|
esphome-pip-3.7-
|
||||||
|
|
||||||
|
# Use per test platformio cache because tests have different platform versions
|
||||||
|
- name: Cache ~/.platformio
|
||||||
|
uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.platformio
|
||||||
|
key: test-home-platformio-${{ matrix.file }}-${{ hashFiles('esphome/core/config.py') }}
|
||||||
|
restore-keys: |
|
||||||
|
test-home-platformio-${{ matrix.file }}-
|
||||||
|
if: ${{ matrix.id == 'test' }}
|
||||||
|
|
||||||
|
- name: Set up python environment
|
||||||
|
run: script/setup
|
||||||
|
|
||||||
|
- name: Register problem matchers
|
||||||
|
run: |
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||||
|
|
||||||
|
- name: Lint Custom
|
||||||
|
run: |
|
||||||
|
script/ci-custom.py
|
||||||
|
script/build_codeowners.py --check
|
||||||
|
if: ${{ matrix.id == 'ci-custom' }}
|
||||||
|
- name: Lint Python
|
||||||
|
run: script/lint-python
|
||||||
|
if: ${{ matrix.id == 'lint-python' }}
|
||||||
|
|
||||||
|
- run: esphome compile ${{ matrix.file }}
|
||||||
|
if: ${{ matrix.id == 'test' }}
|
||||||
|
|
||||||
|
- name: Run pytest
|
||||||
|
run: |
|
||||||
|
pytest -vv --tb=native tests
|
||||||
|
if: ${{ matrix.id == 'pytest' }}
|
||||||
100
.github/workflows/docker-lint-build.yml
vendored
Normal file
100
.github/workflows/docker-lint-build.yml
vendored
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
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
|
||||||
16
.github/workflows/matchers/ci-custom.json
vendored
Normal file
16
.github/workflows/matchers/ci-custom.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "ci-custom",
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"regexp": "^ERROR (.*):(\\d+):(\\d+) - (.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"message": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
17
.github/workflows/matchers/clang-tidy.json
vendored
Normal file
17
.github/workflows/matchers/clang-tidy.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "clang-tidy",
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"regexp": "^(.*):(\\d+):(\\d+):\\s+(error):\\s+(.*) \\[([a-z0-9,\\-]+)\\]\\s*$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
18
.github/workflows/matchers/gcc.json
vendored
Normal file
18
.github/workflows/matchers/gcc.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "gcc",
|
||||||
|
"severity": "error",
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"regexp": "^(.*):(\\d+):(\\d+):\\s+(?:fatal\\s+)?(warning|error):\\s+(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
28
.github/workflows/matchers/lint-python.json
vendored
Normal file
28
.github/workflows/matchers/lint-python.json
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "flake8",
|
||||||
|
"severity": "error",
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"regexp": "^(.*):(\\d+) - ([EFCDNW]\\d{3}.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"message": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"owner": "pylint",
|
||||||
|
"severity": "error",
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"regexp": "^(.*):(\\d+) - (\\[[EFCRW]\\d{4}\\(.*\\),.*\\].*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"message": 3
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
19
.github/workflows/matchers/pytest.json
vendored
Normal file
19
.github/workflows/matchers/pytest.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "pytest",
|
||||||
|
"fileLocation": "absolute",
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"regexp": "^\\s+File \"(.*)\", line (\\d+), in (.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regexp": "^\\s+(.*)$",
|
||||||
|
"message": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
18
.github/workflows/matchers/python.json
vendored
Normal file
18
.github/workflows/matchers/python.json
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "python",
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"regexp": "^\\s*File\\s\\\"(.*)\\\",\\sline\\s(\\d+),\\sin\\s(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regexp": "^\\s*raise\\s(.*)\\(\\'(.*)\\'\\)$",
|
||||||
|
"message": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
147
.github/workflows/release.yml
vendored
Normal file
147
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
name: Publish Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
schedule:
|
||||||
|
- cron: "0 2 * * *"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
init:
|
||||||
|
name: Initialize build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
tag: ${{ steps.tag.outputs.tag }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Get tag
|
||||||
|
id: tag
|
||||||
|
run: |
|
||||||
|
if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then
|
||||||
|
TAG="${GITHUB_REF#refs/tags/v}"
|
||||||
|
else
|
||||||
|
TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p")
|
||||||
|
today="$(date --utc '+%Y%m%d')"
|
||||||
|
TAG="${TAG}${today}"
|
||||||
|
fi
|
||||||
|
echo "::set-output name=tag::${TAG}"
|
||||||
|
|
||||||
|
deploy-pypi:
|
||||||
|
name: Build and publish to PyPi
|
||||||
|
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v1
|
||||||
|
with:
|
||||||
|
python-version: '3.x'
|
||||||
|
- name: Set up python environment
|
||||||
|
run: |
|
||||||
|
script/setup
|
||||||
|
pip install setuptools wheel twine
|
||||||
|
- name: Build
|
||||||
|
run: python setup.py sdist bdist_wheel
|
||||||
|
- name: Upload
|
||||||
|
env:
|
||||||
|
TWINE_USERNAME: __token__
|
||||||
|
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
||||||
|
run: twine upload dist/*
|
||||||
|
|
||||||
|
deploy-docker:
|
||||||
|
name: Build and publish docker containers
|
||||||
|
if: github.repository == 'esphome/esphome'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [init]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: [amd64, armv7, aarch64]
|
||||||
|
build_type: ["ha-addon", "docker"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: '3.9'
|
||||||
|
|
||||||
|
- name: Run build
|
||||||
|
run: |
|
||||||
|
docker/build.py \
|
||||||
|
--tag "${{ needs.init.outputs.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 "${{ needs.init.outputs.tag }}" \
|
||||||
|
--arch "${{ matrix.arch }}" \
|
||||||
|
--build-type "${{ matrix.build_type }}" \
|
||||||
|
push
|
||||||
|
|
||||||
|
deploy-docker-manifest:
|
||||||
|
if: github.repository == 'esphome/esphome'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [init, deploy-docker]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
build_type: ["ha-addon", "docker"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: '3.9'
|
||||||
|
- 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 "${{ needs.init.outputs.tag }}" \
|
||||||
|
--build-type "${{ matrix.build_type }}" \
|
||||||
|
manifest
|
||||||
|
|
||||||
|
deploy-hassio-repo:
|
||||||
|
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [deploy-docker]
|
||||||
|
steps:
|
||||||
|
- env:
|
||||||
|
TOKEN: ${{ secrets.DEPLOY_HASSIO_TOKEN }}
|
||||||
|
run: |
|
||||||
|
TAG="${GITHUB_REF#refs/tags/v}"
|
||||||
|
curl \
|
||||||
|
-u ":$TOKEN" \
|
||||||
|
-X POST \
|
||||||
|
-H "Accept: application/vnd.github.v3+json" \
|
||||||
|
https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \
|
||||||
|
-d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"
|
||||||
14
.gitignore
vendored
14
.gitignore
vendored
@@ -10,6 +10,12 @@ __pycache__/
|
|||||||
*.sublime-project
|
*.sublime-project
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# Intellij Idea
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Vim
|
||||||
|
*.swp
|
||||||
|
|
||||||
# Hide some OS X stuff
|
# Hide some OS X stuff
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.AppleDouble
|
.AppleDouble
|
||||||
@@ -48,8 +54,10 @@ htmlcov/
|
|||||||
.coverage
|
.coverage
|
||||||
.coverage.*
|
.coverage.*
|
||||||
.cache
|
.cache
|
||||||
|
.esphome
|
||||||
nosetests.xml
|
nosetests.xml
|
||||||
coverage.xml
|
coverage.xml
|
||||||
|
cov.xml
|
||||||
*.cover
|
*.cover
|
||||||
.hypothesis/
|
.hypothesis/
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
@@ -76,7 +84,8 @@ venv.bak/
|
|||||||
.pioenvs
|
.pioenvs
|
||||||
.piolibdeps
|
.piolibdeps
|
||||||
.pio
|
.pio
|
||||||
.vscode
|
.vscode/
|
||||||
|
!.vscode/tasks.json
|
||||||
CMakeListsPrivate.txt
|
CMakeListsPrivate.txt
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
|
|
||||||
@@ -94,6 +103,8 @@ CMakeLists.txt
|
|||||||
|
|
||||||
# CMake
|
# CMake
|
||||||
cmake-build-debug/
|
cmake-build-debug/
|
||||||
|
cmake-build-livingroom8266/
|
||||||
|
cmake-build-livingroom32/
|
||||||
cmake-build-release/
|
cmake-build-release/
|
||||||
|
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
@@ -114,3 +125,4 @@ config/
|
|||||||
tests/build/
|
tests/build/
|
||||||
tests/.esphome/
|
tests/.esphome/
|
||||||
/.temp-clang-tidy.cpp
|
/.temp-clang-tidy.cpp
|
||||||
|
.pio/
|
||||||
|
|||||||
342
.gitlab-ci.yml
342
.gitlab-ci.yml
@@ -1,342 +0,0 @@
|
|||||||
---
|
|
||||||
# Based on https://gitlab.com/hassio-addons/addon-node-red/blob/master/.gitlab-ci.yml
|
|
||||||
variables:
|
|
||||||
DOCKER_DRIVER: overlay2
|
|
||||||
DOCKER_HOST: tcp://docker:2375/
|
|
||||||
BASE_VERSION: '2.0.1'
|
|
||||||
TZ: UTC
|
|
||||||
|
|
||||||
stages:
|
|
||||||
- lint
|
|
||||||
- test
|
|
||||||
- deploy
|
|
||||||
|
|
||||||
.lint: &lint
|
|
||||||
image: esphome/esphome-lint:latest
|
|
||||||
stage: lint
|
|
||||||
before_script:
|
|
||||||
- script/setup
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
.test: &test
|
|
||||||
image: esphome/esphome-lint:latest
|
|
||||||
stage: test
|
|
||||||
before_script:
|
|
||||||
- script/setup
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
|
|
||||||
.docker-base: &docker-base
|
|
||||||
image: esphome/esphome-base-builder
|
|
||||||
before_script:
|
|
||||||
- docker info
|
|
||||||
- docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD"
|
|
||||||
script:
|
|
||||||
- docker run --rm --privileged multiarch/qemu-user-static:4.1.0-1 --reset -p yes
|
|
||||||
- TAG="${CI_COMMIT_TAG#v}"
|
|
||||||
- TAG="${TAG:-${CI_COMMIT_SHA:0:7}}"
|
|
||||||
- echo "Tag ${TAG}"
|
|
||||||
|
|
||||||
- |
|
|
||||||
if [[ "${IS_HASSIO}" == "YES" ]]; then
|
|
||||||
BUILD_FROM=esphome/esphome-hassio-base-${BUILD_ARCH}:${BASE_VERSION}
|
|
||||||
BUILD_TO=esphome/esphome-hassio-${BUILD_ARCH}
|
|
||||||
DOCKERFILE=docker/Dockerfile.hassio
|
|
||||||
else
|
|
||||||
BUILD_FROM=esphome/esphome-base-${BUILD_ARCH}:${BASE_VERSION}
|
|
||||||
if [[ "${BUILD_ARCH}" == "amd64" ]]; then
|
|
||||||
BUILD_TO=esphome/esphome
|
|
||||||
else
|
|
||||||
BUILD_TO=esphome/esphome-${BUILD_ARCH}
|
|
||||||
fi
|
|
||||||
DOCKERFILE=docker/Dockerfile
|
|
||||||
fi
|
|
||||||
|
|
||||||
- |
|
|
||||||
docker build \
|
|
||||||
--build-arg "BUILD_FROM=${BUILD_FROM}" \
|
|
||||||
--build-arg "BUILD_VERSION=${TAG}" \
|
|
||||||
--tag "${BUILD_TO}:${TAG}" \
|
|
||||||
--file "${DOCKERFILE}" \
|
|
||||||
.
|
|
||||||
- |
|
|
||||||
if [[ "${RELEASE}" = "YES" ]]; then
|
|
||||||
echo "Pushing to ${BUILD_TO}:${TAG}"
|
|
||||||
docker push "${BUILD_TO}:${TAG}"
|
|
||||||
fi
|
|
||||||
- |
|
|
||||||
if [[ "${LATEST}" = "YES" ]]; then
|
|
||||||
echo "Pushing to :latest"
|
|
||||||
docker tag ${BUILD_TO}:${TAG} ${BUILD_TO}:latest
|
|
||||||
docker push ${BUILD_TO}:latest
|
|
||||||
fi
|
|
||||||
- |
|
|
||||||
if [[ "${BETA}" = "YES" ]]; then
|
|
||||||
echo "Pushing to :beta"
|
|
||||||
docker tag \
|
|
||||||
${BUILD_TO}:${TAG} \
|
|
||||||
${BUILD_TO}:beta
|
|
||||||
docker push ${BUILD_TO}:beta
|
|
||||||
fi
|
|
||||||
- |
|
|
||||||
if [[ "${DEV}" = "YES" ]]; then
|
|
||||||
echo "Pushing to :dev"
|
|
||||||
docker tag \
|
|
||||||
${BUILD_TO}:${TAG} \
|
|
||||||
${BUILD_TO}:dev
|
|
||||||
docker push ${BUILD_TO}:dev
|
|
||||||
fi
|
|
||||||
services:
|
|
||||||
- docker:dind
|
|
||||||
tags:
|
|
||||||
- docker
|
|
||||||
stage: deploy
|
|
||||||
|
|
||||||
lint-custom:
|
|
||||||
<<: *lint
|
|
||||||
script:
|
|
||||||
- script/ci-custom.py
|
|
||||||
|
|
||||||
lint-python:
|
|
||||||
<<: *lint
|
|
||||||
script:
|
|
||||||
- script/lint-python
|
|
||||||
|
|
||||||
lint-tidy:
|
|
||||||
<<: *lint
|
|
||||||
script:
|
|
||||||
- pio init --ide atom
|
|
||||||
- script/clang-tidy --all-headers --fix
|
|
||||||
- script/ci-suggest-changes
|
|
||||||
|
|
||||||
lint-format:
|
|
||||||
<<: *lint
|
|
||||||
script:
|
|
||||||
- script/clang-format -i
|
|
||||||
- script/ci-suggest-changes
|
|
||||||
|
|
||||||
test1:
|
|
||||||
<<: *test
|
|
||||||
script:
|
|
||||||
- esphome tests/test1.yaml compile
|
|
||||||
|
|
||||||
test2:
|
|
||||||
<<: *test
|
|
||||||
script:
|
|
||||||
- esphome tests/test2.yaml compile
|
|
||||||
|
|
||||||
test3:
|
|
||||||
<<: *test
|
|
||||||
script:
|
|
||||||
- esphome tests/test3.yaml compile
|
|
||||||
|
|
||||||
.deploy-pypi: &deploy-pypi
|
|
||||||
<<: *lint
|
|
||||||
stage: deploy
|
|
||||||
script:
|
|
||||||
- pip install twine wheel
|
|
||||||
- python setup.py sdist bdist_wheel
|
|
||||||
- twine upload dist/*
|
|
||||||
|
|
||||||
deploy-release:pypi:
|
|
||||||
<<: *deploy-pypi
|
|
||||||
only:
|
|
||||||
- /^v\d+\.\d+\.\d+$/
|
|
||||||
except:
|
|
||||||
- /^(?!master).+@/
|
|
||||||
|
|
||||||
deploy-beta:pypi:
|
|
||||||
<<: *deploy-pypi
|
|
||||||
only:
|
|
||||||
- /^v\d+\.\d+\.\d+b\d+$/
|
|
||||||
except:
|
|
||||||
- /^(?!rc).+@/
|
|
||||||
|
|
||||||
.latest: &latest
|
|
||||||
<<: *docker-base
|
|
||||||
only:
|
|
||||||
- /^v([0-9\.]+)$/
|
|
||||||
except:
|
|
||||||
- branches
|
|
||||||
|
|
||||||
.beta: &beta
|
|
||||||
<<: *docker-base
|
|
||||||
only:
|
|
||||||
- /^v([0-9\.]+b\d+)$/
|
|
||||||
except:
|
|
||||||
- branches
|
|
||||||
|
|
||||||
.dev: &dev
|
|
||||||
<<: *docker-base
|
|
||||||
only:
|
|
||||||
- dev
|
|
||||||
|
|
||||||
aarch64-beta-docker:
|
|
||||||
<<: *beta
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: aarch64
|
|
||||||
IS_HASSIO: "NO"
|
|
||||||
RELEASE: "YES"
|
|
||||||
aarch64-beta-hassio:
|
|
||||||
<<: *beta
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: aarch64
|
|
||||||
IS_HASSIO: "YES"
|
|
||||||
RELEASE: "YES"
|
|
||||||
aarch64-dev-docker:
|
|
||||||
<<: *dev
|
|
||||||
variables:
|
|
||||||
BUILD_ARCH: aarch64
|
|
||||||
DEV: "YES"
|
|
||||||
IS_HASSIO: "NO"
|
|
||||||
aarch64-dev-hassio:
|
|
||||||
<<: *dev
|
|
||||||
variables:
|
|
||||||
BUILD_ARCH: aarch64
|
|
||||||
DEV: "YES"
|
|
||||||
IS_HASSIO: "YES"
|
|
||||||
aarch64-latest-docker:
|
|
||||||
<<: *latest
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: aarch64
|
|
||||||
IS_HASSIO: "NO"
|
|
||||||
LATEST: "YES"
|
|
||||||
RELEASE: "YES"
|
|
||||||
aarch64-latest-hassio:
|
|
||||||
<<: *latest
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: aarch64
|
|
||||||
IS_HASSIO: "YES"
|
|
||||||
LATEST: "YES"
|
|
||||||
RELEASE: "YES"
|
|
||||||
amd64-beta-docker:
|
|
||||||
<<: *beta
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: amd64
|
|
||||||
IS_HASSIO: "NO"
|
|
||||||
RELEASE: "YES"
|
|
||||||
amd64-beta-hassio:
|
|
||||||
<<: *beta
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: amd64
|
|
||||||
IS_HASSIO: "YES"
|
|
||||||
RELEASE: "YES"
|
|
||||||
amd64-dev-docker:
|
|
||||||
<<: *dev
|
|
||||||
variables:
|
|
||||||
BUILD_ARCH: amd64
|
|
||||||
DEV: "YES"
|
|
||||||
IS_HASSIO: "NO"
|
|
||||||
amd64-dev-hassio:
|
|
||||||
<<: *dev
|
|
||||||
variables:
|
|
||||||
BUILD_ARCH: amd64
|
|
||||||
DEV: "YES"
|
|
||||||
IS_HASSIO: "YES"
|
|
||||||
amd64-latest-docker:
|
|
||||||
<<: *latest
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: amd64
|
|
||||||
IS_HASSIO: "NO"
|
|
||||||
LATEST: "YES"
|
|
||||||
RELEASE: "YES"
|
|
||||||
amd64-latest-hassio:
|
|
||||||
<<: *latest
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: amd64
|
|
||||||
IS_HASSIO: "YES"
|
|
||||||
LATEST: "YES"
|
|
||||||
RELEASE: "YES"
|
|
||||||
armv7-beta-docker:
|
|
||||||
<<: *beta
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: armv7
|
|
||||||
IS_HASSIO: "NO"
|
|
||||||
RELEASE: "YES"
|
|
||||||
armv7-beta-hassio:
|
|
||||||
<<: *beta
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: armv7
|
|
||||||
IS_HASSIO: "YES"
|
|
||||||
RELEASE: "YES"
|
|
||||||
armv7-dev-docker:
|
|
||||||
<<: *dev
|
|
||||||
variables:
|
|
||||||
BUILD_ARCH: armv7
|
|
||||||
DEV: "YES"
|
|
||||||
IS_HASSIO: "NO"
|
|
||||||
armv7-dev-hassio:
|
|
||||||
<<: *dev
|
|
||||||
variables:
|
|
||||||
BUILD_ARCH: armv7
|
|
||||||
DEV: "YES"
|
|
||||||
IS_HASSIO: "YES"
|
|
||||||
armv7-latest-docker:
|
|
||||||
<<: *latest
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: armv7
|
|
||||||
IS_HASSIO: "NO"
|
|
||||||
LATEST: "YES"
|
|
||||||
RELEASE: "YES"
|
|
||||||
armv7-latest-hassio:
|
|
||||||
<<: *latest
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: armv7
|
|
||||||
IS_HASSIO: "YES"
|
|
||||||
LATEST: "YES"
|
|
||||||
RELEASE: "YES"
|
|
||||||
i386-beta-docker:
|
|
||||||
<<: *beta
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: i386
|
|
||||||
IS_HASSIO: "NO"
|
|
||||||
RELEASE: "YES"
|
|
||||||
i386-beta-hassio:
|
|
||||||
<<: *beta
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: i386
|
|
||||||
IS_HASSIO: "YES"
|
|
||||||
RELEASE: "YES"
|
|
||||||
i386-dev-docker:
|
|
||||||
<<: *dev
|
|
||||||
variables:
|
|
||||||
BUILD_ARCH: i386
|
|
||||||
DEV: "YES"
|
|
||||||
IS_HASSIO: "NO"
|
|
||||||
i386-dev-hassio:
|
|
||||||
<<: *dev
|
|
||||||
variables:
|
|
||||||
BUILD_ARCH: i386
|
|
||||||
DEV: "YES"
|
|
||||||
IS_HASSIO: "YES"
|
|
||||||
i386-latest-docker:
|
|
||||||
<<: *latest
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: i386
|
|
||||||
IS_HASSIO: "NO"
|
|
||||||
LATEST: "YES"
|
|
||||||
RELEASE: "YES"
|
|
||||||
i386-latest-hassio:
|
|
||||||
<<: *latest
|
|
||||||
variables:
|
|
||||||
BETA: "YES"
|
|
||||||
BUILD_ARCH: i386
|
|
||||||
IS_HASSIO: "YES"
|
|
||||||
LATEST: "YES"
|
|
||||||
RELEASE: "YES"
|
|
||||||
@@ -2,5 +2,5 @@ ports:
|
|||||||
- port: 6052
|
- port: 6052
|
||||||
onOpen: open-preview
|
onOpen: open-preview
|
||||||
tasks:
|
tasks:
|
||||||
- before: 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 config dashboard
|
||||||
|
|||||||
27
.pre-commit-config.yaml
Normal file
27
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# See https://pre-commit.com for more information
|
||||||
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/ambv/black
|
||||||
|
rev: 20.8b1
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
args:
|
||||||
|
- --safe
|
||||||
|
- --quiet
|
||||||
|
files: ^((esphome|script|tests)/.+)?[^/]+\.py$
|
||||||
|
- repo: https://gitlab.com/pycqa/flake8
|
||||||
|
rev: 3.8.4
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
additional_dependencies:
|
||||||
|
- flake8-docstrings==1.5.0
|
||||||
|
- pydocstyle==5.1.1
|
||||||
|
files: ^(esphome|tests)/.+\.py$
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v3.4.0
|
||||||
|
hooks:
|
||||||
|
- id: no-commit-to-branch
|
||||||
|
args:
|
||||||
|
- --branch=dev
|
||||||
|
- --branch=release
|
||||||
|
- --branch=beta
|
||||||
49
.travis.yml
49
.travis.yml
@@ -1,49 +0,0 @@
|
|||||||
sudo: false
|
|
||||||
language: python
|
|
||||||
python: '3.5'
|
|
||||||
install: script/setup
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- "~/.platformio"
|
|
||||||
|
|
||||||
matrix:
|
|
||||||
fast_finish: true
|
|
||||||
include:
|
|
||||||
- python: "3.7"
|
|
||||||
env: TARGET=Lint3.7
|
|
||||||
script:
|
|
||||||
- script/ci-custom.py
|
|
||||||
- flake8 esphome
|
|
||||||
- pylint esphome
|
|
||||||
- python: "3.5"
|
|
||||||
env: TARGET=Test3.5
|
|
||||||
script:
|
|
||||||
- esphome tests/test1.yaml compile
|
|
||||||
- esphome tests/test2.yaml compile
|
|
||||||
- esphome tests/test3.yaml compile
|
|
||||||
- python: "2.7"
|
|
||||||
env: TARGET=Test2.7
|
|
||||||
script:
|
|
||||||
- esphome tests/test1.yaml compile
|
|
||||||
- esphome tests/test2.yaml compile
|
|
||||||
- esphome tests/test3.yaml compile
|
|
||||||
- env: TARGET=Cpp-Lint
|
|
||||||
dist: trusty
|
|
||||||
sudo: required
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
- llvm-toolchain-trusty-7
|
|
||||||
packages:
|
|
||||||
- clang-tidy-7
|
|
||||||
- clang-format-7
|
|
||||||
before_script:
|
|
||||||
- pio init --ide atom
|
|
||||||
- clang-tidy-7 -version
|
|
||||||
- clang-format-7 -version
|
|
||||||
- clang-apply-replacements-7 -version
|
|
||||||
script:
|
|
||||||
- script/clang-tidy --all-headers -j 2 --fix
|
|
||||||
- script/clang-format -i -j 2
|
|
||||||
- script/ci-suggest-changes
|
|
||||||
32
.vscode/tasks.json
vendored
Normal file
32
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "run",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "python3 -m esphome dashboard config/",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "clang-tidy",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "test -f .gcc-flags.json || pio init --silent --ide atom; ./script/clang-tidy",
|
||||||
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "clang-tidy",
|
||||||
|
"fileLocation": "absolute",
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"regexp": "^(.*):(\\d+):(\\d+):\\s+(error):\\s+(.*) \\[([a-z0-9,\\-]+)\\]\\s*$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2,
|
||||||
|
"column": 3,
|
||||||
|
"severity": 4,
|
||||||
|
"message": 5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
141
CODEOWNERS
Normal file
141
CODEOWNERS
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
# This file is generated by script/build_codeowners.py
|
||||||
|
# People marked here will be automatically requested for a review
|
||||||
|
# when the code that they own is touched.
|
||||||
|
#
|
||||||
|
# Every time an issue is created with a label corresponding to an integration,
|
||||||
|
# the integration's code owner is automatically notified.
|
||||||
|
|
||||||
|
# Core Code
|
||||||
|
setup.py @esphome/core
|
||||||
|
esphome/*.py @esphome/core
|
||||||
|
esphome/core/* @esphome/core
|
||||||
|
|
||||||
|
# Integrations
|
||||||
|
esphome/components/ac_dimmer/* @glmnet
|
||||||
|
esphome/components/adc/* @esphome/core
|
||||||
|
esphome/components/addressable_light/* @justfalter
|
||||||
|
esphome/components/animation/* @syndlex
|
||||||
|
esphome/components/anova/* @buxtronix
|
||||||
|
esphome/components/api/* @OttoWinter
|
||||||
|
esphome/components/async_tcp/* @OttoWinter
|
||||||
|
esphome/components/atc_mithermometer/* @ahpohl
|
||||||
|
esphome/components/b_parasite/* @rbaron
|
||||||
|
esphome/components/ballu/* @bazuchan
|
||||||
|
esphome/components/bang_bang/* @OttoWinter
|
||||||
|
esphome/components/binary_sensor/* @esphome/core
|
||||||
|
esphome/components/ble_client/* @buxtronix
|
||||||
|
esphome/components/bme680_bsec/* @trvrnrth
|
||||||
|
esphome/components/canbus/* @danielschramm @mvturnho
|
||||||
|
esphome/components/captive_portal/* @OttoWinter
|
||||||
|
esphome/components/climate/* @esphome/core
|
||||||
|
esphome/components/climate_ir/* @glmnet
|
||||||
|
esphome/components/coolix/* @glmnet
|
||||||
|
esphome/components/cover/* @esphome/core
|
||||||
|
esphome/components/cs5460a/* @balrog-kun
|
||||||
|
esphome/components/ct_clamp/* @jesserockz
|
||||||
|
esphome/components/debug/* @OttoWinter
|
||||||
|
esphome/components/dfplayer/* @glmnet
|
||||||
|
esphome/components/dht/* @OttoWinter
|
||||||
|
esphome/components/ds1307/* @badbadc0ffee
|
||||||
|
esphome/components/esp32_ble/* @jesserockz
|
||||||
|
esphome/components/esp32_ble_server/* @jesserockz
|
||||||
|
esphome/components/esp32_improv/* @jesserockz
|
||||||
|
esphome/components/exposure_notifications/* @OttoWinter
|
||||||
|
esphome/components/ezo/* @ssieb
|
||||||
|
esphome/components/fastled_base/* @OttoWinter
|
||||||
|
esphome/components/fingerprint_grow/* @OnFreund @loongyh
|
||||||
|
esphome/components/globals/* @esphome/core
|
||||||
|
esphome/components/gpio/* @esphome/core
|
||||||
|
esphome/components/gps/* @coogle
|
||||||
|
esphome/components/havells_solar/* @sourabhjaiswal
|
||||||
|
esphome/components/homeassistant/* @OttoWinter
|
||||||
|
esphome/components/i2c/* @esphome/core
|
||||||
|
esphome/components/improv/* @jesserockz
|
||||||
|
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
||||||
|
esphome/components/inkplate6/* @jesserockz
|
||||||
|
esphome/components/integration/* @OttoWinter
|
||||||
|
esphome/components/interval/* @esphome/core
|
||||||
|
esphome/components/json/* @OttoWinter
|
||||||
|
esphome/components/ledc/* @OttoWinter
|
||||||
|
esphome/components/light/* @esphome/core
|
||||||
|
esphome/components/logger/* @esphome/core
|
||||||
|
esphome/components/max7219digit/* @rspaargaren
|
||||||
|
esphome/components/mcp23008/* @jesserockz
|
||||||
|
esphome/components/mcp23017/* @jesserockz
|
||||||
|
esphome/components/mcp23s08/* @SenexCrenshaw @jesserockz
|
||||||
|
esphome/components/mcp23s17/* @SenexCrenshaw @jesserockz
|
||||||
|
esphome/components/mcp23x08_base/* @jesserockz
|
||||||
|
esphome/components/mcp23x17_base/* @jesserockz
|
||||||
|
esphome/components/mcp23xxx_base/* @jesserockz
|
||||||
|
esphome/components/mcp2515/* @danielschramm @mvturnho
|
||||||
|
esphome/components/mcp9808/* @k7hpn
|
||||||
|
esphome/components/midea_ac/* @dudanov
|
||||||
|
esphome/components/midea_dongle/* @dudanov
|
||||||
|
esphome/components/mitsubishi/* @RubyBailey
|
||||||
|
esphome/components/network/* @esphome/core
|
||||||
|
esphome/components/nextion/* @senexcrenshaw
|
||||||
|
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
||||||
|
esphome/components/nextion/sensor/* @senexcrenshaw
|
||||||
|
esphome/components/nextion/switch/* @senexcrenshaw
|
||||||
|
esphome/components/nextion/text_sensor/* @senexcrenshaw
|
||||||
|
esphome/components/nfc/* @jesserockz
|
||||||
|
esphome/components/number/* @esphome/core
|
||||||
|
esphome/components/ota/* @esphome/core
|
||||||
|
esphome/components/output/* @esphome/core
|
||||||
|
esphome/components/pid/* @OttoWinter
|
||||||
|
esphome/components/pn532/* @OttoWinter @jesserockz
|
||||||
|
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
||||||
|
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
||||||
|
esphome/components/power_supply/* @esphome/core
|
||||||
|
esphome/components/pulse_meter/* @stevebaxter
|
||||||
|
esphome/components/rc522/* @glmnet
|
||||||
|
esphome/components/rc522_i2c/* @glmnet
|
||||||
|
esphome/components/rc522_spi/* @glmnet
|
||||||
|
esphome/components/restart/* @esphome/core
|
||||||
|
esphome/components/rf_bridge/* @jesserockz
|
||||||
|
esphome/components/rtttl/* @glmnet
|
||||||
|
esphome/components/script/* @esphome/core
|
||||||
|
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
||||||
|
esphome/components/sensor/* @esphome/core
|
||||||
|
esphome/components/sgp40/* @SenexCrenshaw
|
||||||
|
esphome/components/sht4x/* @sjtrny
|
||||||
|
esphome/components/shutdown/* @esphome/core
|
||||||
|
esphome/components/sim800l/* @glmnet
|
||||||
|
esphome/components/sm2135/* @BoukeHaarsma23
|
||||||
|
esphome/components/spi/* @esphome/core
|
||||||
|
esphome/components/ssd1322_base/* @kbx81
|
||||||
|
esphome/components/ssd1322_spi/* @kbx81
|
||||||
|
esphome/components/ssd1325_base/* @kbx81
|
||||||
|
esphome/components/ssd1325_spi/* @kbx81
|
||||||
|
esphome/components/ssd1327_base/* @kbx81
|
||||||
|
esphome/components/ssd1327_i2c/* @kbx81
|
||||||
|
esphome/components/ssd1327_spi/* @kbx81
|
||||||
|
esphome/components/ssd1331_base/* @kbx81
|
||||||
|
esphome/components/ssd1331_spi/* @kbx81
|
||||||
|
esphome/components/ssd1351_base/* @kbx81
|
||||||
|
esphome/components/ssd1351_spi/* @kbx81
|
||||||
|
esphome/components/st7735/* @SenexCrenshaw
|
||||||
|
esphome/components/st7789v/* @kbx81
|
||||||
|
esphome/components/substitutions/* @esphome/core
|
||||||
|
esphome/components/sun/* @OttoWinter
|
||||||
|
esphome/components/switch/* @esphome/core
|
||||||
|
esphome/components/tca9548a/* @andreashergert1984
|
||||||
|
esphome/components/tcl112/* @glmnet
|
||||||
|
esphome/components/teleinfo/* @0hax
|
||||||
|
esphome/components/thermostat/* @kbx81
|
||||||
|
esphome/components/time/* @OttoWinter
|
||||||
|
esphome/components/tm1637/* @glmnet
|
||||||
|
esphome/components/tmp102/* @timsavage
|
||||||
|
esphome/components/tof10120/* @wstrzalka
|
||||||
|
esphome/components/tuya/binary_sensor/* @jesserockz
|
||||||
|
esphome/components/tuya/climate/* @jesserockz
|
||||||
|
esphome/components/tuya/sensor/* @jesserockz
|
||||||
|
esphome/components/tuya/switch/* @jesserockz
|
||||||
|
esphome/components/uart/* @esphome/core
|
||||||
|
esphome/components/ultrasonic/* @OttoWinter
|
||||||
|
esphome/components/version/* @esphome/core
|
||||||
|
esphome/components/web_server_base/* @OttoWinter
|
||||||
|
esphome/components/whirlpool/* @glmnet
|
||||||
|
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
||||||
|
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
||||||
|
esphome/components/xpt2046/* @numo68
|
||||||
@@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
|
|||||||
|
|
||||||
Examples of behavior that contributes to creating a positive environment include:
|
Examples of behavior that contributes to creating a positive environment include:
|
||||||
|
|
||||||
* Using welcoming and inclusive language
|
- Using welcoming and inclusive language
|
||||||
* Being respectful of differing viewpoints and experiences
|
- Being respectful of differing viewpoints and experiences
|
||||||
* Gracefully accepting constructive criticism
|
- Gracefully accepting constructive criticism
|
||||||
* Focusing on what is best for the community
|
- Focusing on what is best for the community
|
||||||
* Showing empathy towards other community members
|
- Showing empathy towards other community members
|
||||||
|
|
||||||
Examples of unacceptable behavior by participants include:
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
* Public or private harassment
|
- Public or private harassment
|
||||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||||
|
|
||||||
## Our Responsibilities
|
## Our Responsibilities
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
|
|||||||
|
|
||||||
## Enforcement
|
## Enforcement
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@otto-winter.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at esphome@nabucasa.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
include LICENSE
|
include LICENSE
|
||||||
include README.md
|
include README.md
|
||||||
|
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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# ESPHome [](https://travis-ci.org/esphome/esphome) [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
|
# ESPHome [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
|
||||||
|
|
||||||
[](https://esphome.io/)
|
[](https://esphome.io/)
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,30 @@
|
|||||||
ARG BUILD_FROM=esphome/esphome-base-amd64:2.0.1
|
ARG BUILD_FROM=esphome/esphome-base:latest
|
||||||
FROM ${BUILD_FROM}
|
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
|
||||||
|
|
||||||
|
# Then copy esphome and install
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN pip3 install --no-cache-dir -e .
|
RUN pip3 install --no-cache-dir -e .
|
||||||
|
|
||||||
ENV USERNAME=""
|
# Settings for dashboard
|
||||||
ENV PASSWORD=""
|
ENV USERNAME="" PASSWORD=""
|
||||||
|
|
||||||
|
# Expose the dashboard to Docker
|
||||||
|
EXPOSE 6052
|
||||||
|
|
||||||
|
# Run healthcheck (heartbeat)
|
||||||
|
HEALTHCHECK --interval=30s --timeout=30s \
|
||||||
|
CMD curl --fail http://localhost:6052 || exit 1
|
||||||
|
|
||||||
|
# The directory the user should mount their configuration files to
|
||||||
WORKDIR /config
|
WORKDIR /config
|
||||||
|
# Set entrypoint to esphome so that the user doesn't have to type 'esphome'
|
||||||
|
# in every docker command twice
|
||||||
ENTRYPOINT ["esphome"]
|
ENTRYPOINT ["esphome"]
|
||||||
CMD ["/config", "dashboard"]
|
# When no arguments given, start the dashboard in the workdir
|
||||||
|
CMD ["dashboard", "/config"]
|
||||||
|
|||||||
1
docker/Dockerfile.dev
Normal file
1
docker/Dockerfile.dev
Normal file
@@ -0,0 +1 @@
|
|||||||
|
FROM esphome/esphome-lint:1.1
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
ARG BUILD_FROM
|
ARG BUILD_FROM=esphome/esphome-hassio-base:latest
|
||||||
FROM ${BUILD_FROM}
|
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 root filesystem
|
||||||
COPY docker/rootfs/ /
|
COPY docker/rootfs/ /
|
||||||
COPY setup.py setup.cfg MANIFEST.in /opt/esphome/
|
|
||||||
COPY esphome /opt/esphome/esphome
|
|
||||||
|
|
||||||
|
# Then copy esphome and install
|
||||||
|
COPY . /opt/esphome/
|
||||||
RUN pip3 install --no-cache-dir -e /opt/esphome
|
RUN pip3 install --no-cache-dir -e /opt/esphome
|
||||||
|
|
||||||
# Build arguments
|
# Build arguments
|
||||||
|
|||||||
@@ -1,20 +1,10 @@
|
|||||||
FROM esphome/esphome-base-amd64:2.0.1
|
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 \
|
RUN \
|
||||||
apt-get update \
|
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
|
||||||
&& apt-get install -y --no-install-recommends \
|
&& /platformio_install_deps.py /platformio.ini
|
||||||
clang-format-7 \
|
|
||||||
clang-tidy-7 \
|
|
||||||
patch \
|
|
||||||
&& rm -rf \
|
|
||||||
/tmp/* \
|
|
||||||
/var/{cache,log}/* \
|
|
||||||
/var/lib/apt/lists/*
|
|
||||||
|
|
||||||
COPY requirements_test.txt /requirements_test.txt
|
|
||||||
RUN pip3 install --no-cache-dir wheel && pip3 install --no-cache-dir -r /requirements_test.txt
|
|
||||||
|
|
||||||
RUN ln -s /usr/bin/pip3 /usr/bin/pip && ln -f -s /usr/bin/python3 /usr/bin/python
|
|
||||||
|
|
||||||
VOLUME ["/esphome"]
|
VOLUME ["/esphome"]
|
||||||
WORKDIR /esphome
|
WORKDIR /esphome
|
||||||
|
|||||||
177
docker/build.py
Executable file
177
docker/build.py
Executable file
@@ -0,0 +1,177 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from dataclasses import dataclass
|
||||||
|
import subprocess
|
||||||
|
import argparse
|
||||||
|
import platform
|
||||||
|
import shlex
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
CHANNEL_DEV = 'dev'
|
||||||
|
CHANNEL_BETA = 'beta'
|
||||||
|
CHANNEL_RELEASE = 'release'
|
||||||
|
CHANNELS = [CHANNEL_DEV, CHANNEL_BETA, CHANNEL_RELEASE]
|
||||||
|
|
||||||
|
ARCH_AMD64 = 'amd64'
|
||||||
|
ARCH_ARMV7 = 'armv7'
|
||||||
|
ARCH_AARCH64 = 'aarch64'
|
||||||
|
ARCHS = [ARCH_AMD64, ARCH_ARMV7, ARCH_AARCH64]
|
||||||
|
|
||||||
|
TYPE_DOCKER = 'docker'
|
||||||
|
TYPE_HA_ADDON = 'ha-addon'
|
||||||
|
TYPE_LINT = 'lint'
|
||||||
|
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
|
||||||
|
|
||||||
|
|
||||||
|
BASE_VERSION = "3.6.0"
|
||||||
|
|
||||||
|
|
||||||
|
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("--arch", choices=ARCHS, required=False, help="The architecture to build for")
|
||||||
|
parser.add_argument("--build-type", choices=TYPES, required=True, help="The type of build to run")
|
||||||
|
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)
|
||||||
|
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")
|
||||||
|
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)
|
||||||
|
class DockerParams:
|
||||||
|
build_from: str
|
||||||
|
build_to: str
|
||||||
|
manifest_to: str
|
||||||
|
dockerfile: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def for_type_arch(cls, build_type, arch):
|
||||||
|
prefix = {
|
||||||
|
TYPE_DOCKER: "esphome/esphome",
|
||||||
|
TYPE_HA_ADDON: "esphome/esphome-hassio",
|
||||||
|
TYPE_LINT: "esphome/esphome-lint"
|
||||||
|
}[build_type]
|
||||||
|
build_from = f"ghcr.io/{prefix}-base-{arch}:{BASE_VERSION}"
|
||||||
|
build_to = f"{prefix}-{arch}"
|
||||||
|
dockerfile = {
|
||||||
|
TYPE_DOCKER: "docker/Dockerfile",
|
||||||
|
TYPE_HA_ADDON: "docker/Dockerfile.hassio",
|
||||||
|
TYPE_LINT: "docker/Dockerfile.lint",
|
||||||
|
}[build_type]
|
||||||
|
return cls(
|
||||||
|
build_from=build_from,
|
||||||
|
build_to=build_to,
|
||||||
|
manifest_to=prefix,
|
||||||
|
dockerfile=dockerfile
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
def run_command(*cmd, ignore_error: bool = False):
|
||||||
|
print(f"$ {shlex.join(list(cmd))}")
|
||||||
|
if not args.dry_run:
|
||||||
|
rc = subprocess.call(list(cmd))
|
||||||
|
if rc != 0 and not ignore_error:
|
||||||
|
print("Command failed")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# detect channel from tag
|
||||||
|
match = re.match(r'^\d+\.\d+(?:\.\d+)?(b\d+)?$', args.tag)
|
||||||
|
if match is None:
|
||||||
|
channel = CHANNEL_DEV
|
||||||
|
elif match.group(1) is None:
|
||||||
|
channel = CHANNEL_RELEASE
|
||||||
|
else:
|
||||||
|
channel = CHANNEL_BETA
|
||||||
|
|
||||||
|
tags_to_push = [args.tag]
|
||||||
|
if channel == CHANNEL_DEV:
|
||||||
|
tags_to_push.append("dev")
|
||||||
|
elif channel == CHANNEL_BETA:
|
||||||
|
tags_to_push.append("beta")
|
||||||
|
elif channel == CHANNEL_RELEASE:
|
||||||
|
# Additionally push to beta
|
||||||
|
tags_to_push.append("beta")
|
||||||
|
tags_to_push.append("latest")
|
||||||
|
|
||||||
|
if args.command == "build":
|
||||||
|
# 1. pull cache image
|
||||||
|
params = DockerParams.for_type_arch(args.build_type, args.arch)
|
||||||
|
cache_tag = {
|
||||||
|
CHANNEL_DEV: "dev",
|
||||||
|
CHANNEL_BETA: "beta",
|
||||||
|
CHANNEL_RELEASE: "latest",
|
||||||
|
}[channel]
|
||||||
|
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"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push]
|
||||||
|
src = imgs[0]
|
||||||
|
# 1. tag images
|
||||||
|
for img in imgs[1:]:
|
||||||
|
run_command(
|
||||||
|
"docker", "tag", src, img
|
||||||
|
)
|
||||||
|
# 2. push images
|
||||||
|
for img in imgs:
|
||||||
|
run_command(
|
||||||
|
"docker", "push", img
|
||||||
|
)
|
||||||
|
elif args.command == "manifest":
|
||||||
|
manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to
|
||||||
|
|
||||||
|
targets = [f"{manifest}:{tag}" for tag in tags_to_push]
|
||||||
|
targets += [f"ghcr.io/{manifest}:{tag}" for tag in tags_to_push]
|
||||||
|
# 1. Create manifests
|
||||||
|
for target in targets:
|
||||||
|
cmd = ["docker", "manifest", "create", target]
|
||||||
|
for arch in ARCHS:
|
||||||
|
src = f"{DockerParams.for_type_arch(args.build_type, arch).build_to}:{args.tag}"
|
||||||
|
if target.startswith("ghcr.io"):
|
||||||
|
src = f"ghcr.io/{src}"
|
||||||
|
cmd.append(src)
|
||||||
|
run_command(*cmd)
|
||||||
|
# 2. Push manifests
|
||||||
|
for target in targets:
|
||||||
|
run_command(
|
||||||
|
"docker", "manifest", "push", target
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
20
docker/platformio_install_deps.py
Executable file
20
docker/platformio_install_deps.py
Executable file
@@ -0,0 +1,20 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# This script is used in the docker containers to preinstall
|
||||||
|
# all platformio libraries in the global storage
|
||||||
|
|
||||||
|
import configparser
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(sys.argv[1])
|
||||||
|
libs = []
|
||||||
|
for line in config['common']['lib_deps'].splitlines():
|
||||||
|
# Format: '1655@1.0.2 ; TinyGPSPlus (has name conflict)' (includes comment)
|
||||||
|
m = re.search(r'([a-zA-Z0-9-_/]+@[0-9\.]+)', line)
|
||||||
|
if m is None:
|
||||||
|
continue
|
||||||
|
libs.append(m.group(1))
|
||||||
|
|
||||||
|
subprocess.check_call(['platformio', 'lib', '-g', 'install', *libs])
|
||||||
10
docker/rootfs/etc/cont-init.d/30-esphome.sh
Normal file → Executable file
10
docker/rootfs/etc/cont-init.d/30-esphome.sh
Normal file → Executable file
@@ -8,7 +8,15 @@ declare esphome_version
|
|||||||
|
|
||||||
if bashio::config.has_value 'esphome_version'; then
|
if bashio::config.has_value 'esphome_version'; then
|
||||||
esphome_version=$(bashio::config 'esphome_version')
|
esphome_version=$(bashio::config 'esphome_version')
|
||||||
full_url="https://github.com/esphome/esphome/archive/${esphome_version}.zip"
|
if [[ $esphome_version == *":"* ]]; then
|
||||||
|
IFS=':' read -r -a array <<< "$esphome_version"
|
||||||
|
username=${array[0]}
|
||||||
|
ref=${array[1]}
|
||||||
|
else
|
||||||
|
username="esphome"
|
||||||
|
ref=$esphome_version
|
||||||
|
fi
|
||||||
|
full_url="https://github.com/${username}/esphome/archive/${ref}.zip"
|
||||||
bashio::log.info "Installing esphome version '${esphome_version}' (${full_url})..."
|
bashio::log.info "Installing esphome version '${esphome_version}' (${full_url})..."
|
||||||
pip3 install -U --no-cache-dir "${full_url}" \
|
pip3 install -U --no-cache-dir "${full_url}" \
|
||||||
|| bashio::exit.nok "Failed installing esphome pinned version."
|
|| bashio::exit.nok "Failed installing esphome pinned version."
|
||||||
|
|||||||
0
docker/rootfs/etc/cont-init.d/40-migrate.sh
Normal file → Executable file
0
docker/rootfs/etc/cont-init.d/40-migrate.sh
Normal file → Executable file
0
docker/rootfs/etc/nginx/nginx.conf
Executable file → Normal file
0
docker/rootfs/etc/nginx/nginx.conf
Executable file → Normal file
@@ -23,4 +23,4 @@ if bashio::config.has_value 'relative_url'; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
bashio::log.info "Starting ESPHome dashboard..."
|
bashio::log.info "Starting ESPHome dashboard..."
|
||||||
exec esphome /config/esphome dashboard --socket /var/run/esphome.sock --hassio
|
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
@@ -10,44 +8,47 @@ from datetime import datetime
|
|||||||
from esphome import const, writer, yaml_util
|
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 CONF_BAUD_RATE, CONF_BROKER, CONF_LOGGER, CONF_OTA, \
|
from esphome.const import (
|
||||||
CONF_PASSWORD, CONF_PORT, CONF_ESPHOME, CONF_PLATFORMIO_OPTIONS
|
CONF_BAUD_RATE,
|
||||||
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
|
CONF_BROKER,
|
||||||
from esphome.helpers import color, indent
|
CONF_LOGGER,
|
||||||
from esphome.py_compat import IS_PY2, safe_input
|
CONF_OTA,
|
||||||
from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files
|
CONF_PASSWORD,
|
||||||
|
CONF_PORT,
|
||||||
|
CONF_ESPHOME,
|
||||||
|
CONF_PLATFORMIO_OPTIONS,
|
||||||
|
)
|
||||||
|
from esphome.core import CORE, EsphomeError, coroutine
|
||||||
|
from esphome.helpers import indent
|
||||||
|
from esphome.util import (
|
||||||
|
run_external_command,
|
||||||
|
run_external_process,
|
||||||
|
safe_print,
|
||||||
|
list_yaml_files,
|
||||||
|
get_serial_ports,
|
||||||
|
)
|
||||||
|
from esphome.log import color, setup_log, Fore
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_serial_ports():
|
|
||||||
# from https://github.com/pyserial/pyserial/blob/master/serial/tools/list_ports.py
|
|
||||||
from serial.tools.list_ports import comports
|
|
||||||
result = []
|
|
||||||
for port, desc, info in comports(include_links=True):
|
|
||||||
if not port:
|
|
||||||
continue
|
|
||||||
if "VID:PID" in info:
|
|
||||||
result.append((port, desc))
|
|
||||||
result.sort(key=lambda x: x[0])
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
||||||
def choose_prompt(options):
|
def choose_prompt(options):
|
||||||
if not options:
|
if not options:
|
||||||
raise EsphomeError("Found no valid options for upload/logging, please make sure relevant "
|
raise EsphomeError(
|
||||||
"sections (ota, mqtt, ...) are in your configuration and/or the device "
|
"Found no valid options for upload/logging, please make sure relevant "
|
||||||
"is plugged in.")
|
"sections (ota, api, mqtt, ...) are in your configuration and/or the "
|
||||||
|
"device is plugged in."
|
||||||
|
)
|
||||||
|
|
||||||
if len(options) == 1:
|
if len(options) == 1:
|
||||||
return options[0][1]
|
return options[0][1]
|
||||||
|
|
||||||
safe_print(u"Found multiple options, please choose one:")
|
safe_print("Found multiple options, please choose one:")
|
||||||
for i, (desc, _) in enumerate(options):
|
for i, (desc, _) in enumerate(options):
|
||||||
safe_print(u" [{}] {}".format(i + 1, desc))
|
safe_print(f" [{i+1}] {desc}")
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
opt = safe_input('(number): ')
|
opt = input("(number): ")
|
||||||
if opt in options:
|
if opt in options:
|
||||||
opt = options.index(opt)
|
opt = options.index(opt)
|
||||||
break
|
break
|
||||||
@@ -57,22 +58,22 @@ def choose_prompt(options):
|
|||||||
raise ValueError
|
raise ValueError
|
||||||
break
|
break
|
||||||
except ValueError:
|
except ValueError:
|
||||||
safe_print(color('red', u"Invalid option: '{}'".format(opt)))
|
safe_print(color(Fore.RED, f"Invalid option: '{opt}'"))
|
||||||
return options[opt - 1][1]
|
return options[opt - 1][1]
|
||||||
|
|
||||||
|
|
||||||
def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api):
|
def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api):
|
||||||
options = []
|
options = []
|
||||||
for res, desc in get_serial_ports():
|
for port in get_serial_ports():
|
||||||
options.append((u"{} ({})".format(res, desc), res))
|
options.append((f"{port.path} ({port.description})", port.path))
|
||||||
if (show_ota and 'ota' in CORE.config) or (show_api and 'api' in CORE.config):
|
if (show_ota and "ota" in CORE.config) or (show_api and "api" in CORE.config):
|
||||||
options.append((u"Over The Air ({})".format(CORE.address), CORE.address))
|
options.append((f"Over The Air ({CORE.address})", CORE.address))
|
||||||
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((u"MQTT ({})".format(CORE.config['mqtt'][CONF_BROKER]), 'MQTT'))
|
options.append(("MQTT ({})".format(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:
|
||||||
return default
|
return default
|
||||||
if check_default is not None and check_default in [opt[1] for opt in options]:
|
if check_default is not None and check_default in [opt[1] for opt in options]:
|
||||||
@@ -81,11 +82,11 @@ def choose_upload_log_host(default, check_default, show_ota, show_mqtt, show_api
|
|||||||
|
|
||||||
|
|
||||||
def get_port_type(port):
|
def get_port_type(port):
|
||||||
if port.startswith('/') or port.startswith('COM'):
|
if port.startswith("/") or port.startswith("COM"):
|
||||||
return 'SERIAL'
|
return "SERIAL"
|
||||||
if port == 'MQTT':
|
if port == "MQTT":
|
||||||
return 'MQTT'
|
return "MQTT"
|
||||||
return 'NETWORK'
|
return "NETWORK"
|
||||||
|
|
||||||
|
|
||||||
def run_miniterm(config, port):
|
def run_miniterm(config, port):
|
||||||
@@ -95,7 +96,7 @@ def run_miniterm(config, port):
|
|||||||
if CONF_LOGGER not in config:
|
if CONF_LOGGER not in config:
|
||||||
_LOGGER.info("Logger is not enabled. Not starting UART logs.")
|
_LOGGER.info("Logger is not enabled. Not starting UART logs.")
|
||||||
return
|
return
|
||||||
baud_rate = config['logger'][CONF_BAUD_RATE]
|
baud_rate = config["logger"][CONF_BAUD_RATE]
|
||||||
if baud_rate == 0:
|
if baud_rate == 0:
|
||||||
_LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
|
_LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
|
||||||
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
|
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
|
||||||
@@ -108,38 +109,43 @@ def run_miniterm(config, port):
|
|||||||
except serial.SerialException:
|
except serial.SerialException:
|
||||||
_LOGGER.error("Serial port closed!")
|
_LOGGER.error("Serial port closed!")
|
||||||
return
|
return
|
||||||
if IS_PY2:
|
line = (
|
||||||
line = raw.replace('\r', '').replace('\n', '')
|
raw.replace(b"\r", b"")
|
||||||
else:
|
.replace(b"\n", b"")
|
||||||
line = raw.replace(b'\r', b'').replace(b'\n', b'').decode('utf8',
|
.decode("utf8", "backslashreplace")
|
||||||
'backslashreplace')
|
)
|
||||||
time = datetime.now().time().strftime('[%H:%M:%S]')
|
time = datetime.now().time().strftime("[%H:%M:%S]")
|
||||||
message = time + line
|
message = time + line
|
||||||
safe_print(message)
|
safe_print(message)
|
||||||
|
|
||||||
backtrace_state = platformio_api.process_stacktrace(
|
backtrace_state = platformio_api.process_stacktrace(
|
||||||
config, line, backtrace_state=backtrace_state)
|
config, line, backtrace_state=backtrace_state
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def wrap_to_code(name, comp):
|
def wrap_to_code(name, comp):
|
||||||
coro = coroutine(comp.to_code)
|
coro = coroutine(comp.to_code)
|
||||||
|
|
||||||
@functools.wraps(comp.to_code)
|
@functools.wraps(comp.to_code)
|
||||||
@coroutine_with_priority(coro.priority)
|
async def wrapped(conf):
|
||||||
def wrapped(conf):
|
cg.add(cg.LineComment(f"{name}:"))
|
||||||
cg.add(cg.LineComment(u"{}:".format(name)))
|
|
||||||
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)
|
||||||
if IS_PY2:
|
conf_str = conf_str.replace("//", "")
|
||||||
conf_str = conf_str.decode('utf-8')
|
|
||||||
conf_str = conf_str.replace('//', '')
|
|
||||||
cg.add(cg.LineComment(indent(conf_str)))
|
cg.add(cg.LineComment(indent(conf_str)))
|
||||||
yield coro(conf)
|
await coro(conf)
|
||||||
|
|
||||||
|
if hasattr(coro, "priority"):
|
||||||
|
wrapped.priority = coro.priority
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
def write_cpp(config):
|
def write_cpp(config):
|
||||||
|
generate_cpp_contents(config)
|
||||||
|
return write_cpp_file()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_cpp_contents(config):
|
||||||
_LOGGER.info("Generating C++ source...")
|
_LOGGER.info("Generating C++ source...")
|
||||||
|
|
||||||
for name, component, conf in iter_components(CORE.config):
|
for name, component, conf in iter_components(CORE.config):
|
||||||
@@ -149,6 +155,8 @@ def write_cpp(config):
|
|||||||
|
|
||||||
CORE.flush_tasks()
|
CORE.flush_tasks()
|
||||||
|
|
||||||
|
|
||||||
|
def write_cpp_file():
|
||||||
writer.write_platformio_project()
|
writer.write_platformio_project()
|
||||||
|
|
||||||
code_s = indent(CORE.cpp_main_section)
|
code_s = indent(CORE.cpp_main_section)
|
||||||
@@ -165,21 +173,50 @@ def compile_program(args, config):
|
|||||||
|
|
||||||
def upload_using_esptool(config, port):
|
def upload_using_esptool(config, port):
|
||||||
path = CORE.firmware_bin
|
path = CORE.firmware_bin
|
||||||
cmd = ['esptool.py', '--before', 'default_reset', '--after', 'hard_reset',
|
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
|
||||||
'--baud', str(config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get('upload_speed', 460800)),
|
"upload_speed", 460800
|
||||||
'--chip', 'esp8266', '--port', port, 'write_flash', '0x0', path]
|
)
|
||||||
|
|
||||||
if os.environ.get('ESPHOME_USE_SUBPROCESS') is None:
|
def run_esptool(baud_rate):
|
||||||
import esptool
|
cmd = [
|
||||||
# pylint: disable=protected-access
|
"esptool.py",
|
||||||
return run_external_command(esptool._main, *cmd)
|
"--before",
|
||||||
|
"default_reset",
|
||||||
|
"--after",
|
||||||
|
"hard_reset",
|
||||||
|
"--baud",
|
||||||
|
str(baud_rate),
|
||||||
|
"--chip",
|
||||||
|
"esp8266",
|
||||||
|
"--port",
|
||||||
|
port,
|
||||||
|
"write_flash",
|
||||||
|
"0x0",
|
||||||
|
path,
|
||||||
|
]
|
||||||
|
|
||||||
return run_external_process(*cmd)
|
if os.environ.get("ESPHOME_USE_SUBPROCESS") is None:
|
||||||
|
import esptool
|
||||||
|
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
return run_external_command(esptool._main, *cmd)
|
||||||
|
|
||||||
|
return run_external_process(*cmd)
|
||||||
|
|
||||||
|
rc = run_esptool(first_baudrate)
|
||||||
|
if rc == 0 or first_baudrate == 115200:
|
||||||
|
return rc
|
||||||
|
# Try with 115200 baud rate, with some serial chips the faster baud rates do not work well
|
||||||
|
_LOGGER.info(
|
||||||
|
"Upload with baud rate %s failed. Trying again with baud rate 115200.",
|
||||||
|
first_baudrate,
|
||||||
|
)
|
||||||
|
return run_esptool(115200)
|
||||||
|
|
||||||
|
|
||||||
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
|
from esphome import platformio_api
|
||||||
|
|
||||||
if CORE.is_esp8266:
|
if CORE.is_esp8266:
|
||||||
@@ -188,6 +225,12 @@ def upload_program(config, args, host):
|
|||||||
|
|
||||||
from esphome import espota2
|
from esphome import espota2
|
||||||
|
|
||||||
|
if CONF_OTA not in config:
|
||||||
|
raise EsphomeError(
|
||||||
|
"Cannot upload Over the Air as the config does not include the ota: "
|
||||||
|
"component"
|
||||||
|
)
|
||||||
|
|
||||||
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[CONF_PASSWORD]
|
||||||
@@ -195,19 +238,21 @@ def upload_program(config, args, host):
|
|||||||
|
|
||||||
|
|
||||||
def show_logs(config, args, port):
|
def show_logs(config, args, port):
|
||||||
if 'logger' not in config:
|
if "logger" not in config:
|
||||||
raise EsphomeError("Logger is not configured!")
|
raise EsphomeError("Logger is not configured!")
|
||||||
if get_port_type(port) == 'SERIAL':
|
if get_port_type(port) == "SERIAL":
|
||||||
run_miniterm(config, port)
|
run_miniterm(config, port)
|
||||||
return 0
|
return 0
|
||||||
if get_port_type(port) == 'NETWORK' and 'api' in config:
|
if get_port_type(port) == "NETWORK" and "api" in config:
|
||||||
from esphome.api.client import run_logs
|
from esphome.api.client import run_logs
|
||||||
|
|
||||||
return run_logs(config, port)
|
return run_logs(config, port)
|
||||||
if get_port_type(port) == 'MQTT' and 'mqtt' in config:
|
if get_port_type(port) == "MQTT" and "mqtt" in config:
|
||||||
from esphome import mqtt
|
from esphome import mqtt
|
||||||
|
|
||||||
return mqtt.show_logs(config, args.topic, args.username, args.password, args.client_id)
|
return mqtt.show_logs(
|
||||||
|
config, args.topic, args.username, args.password, args.client_id
|
||||||
|
)
|
||||||
|
|
||||||
raise EsphomeError("No remote or local logging method configured (api/mqtt/logger)")
|
raise EsphomeError("No remote or local logging method configured (api/mqtt/logger)")
|
||||||
|
|
||||||
@@ -215,46 +260,15 @@ def show_logs(config, args, port):
|
|||||||
def clean_mqtt(config, args):
|
def clean_mqtt(config, args):
|
||||||
from esphome import mqtt
|
from esphome import mqtt
|
||||||
|
|
||||||
return mqtt.clear_topic(config, args.topic, args.username, args.password, args.client_id)
|
return mqtt.clear_topic(
|
||||||
|
config, args.topic, args.username, args.password, args.client_id
|
||||||
|
)
|
||||||
def setup_log(debug=False, quiet=False):
|
|
||||||
if debug:
|
|
||||||
log_level = logging.DEBUG
|
|
||||||
CORE.verbose = True
|
|
||||||
elif quiet:
|
|
||||||
log_level = logging.CRITICAL
|
|
||||||
else:
|
|
||||||
log_level = logging.INFO
|
|
||||||
logging.basicConfig(level=log_level)
|
|
||||||
fmt = "%(levelname)s %(message)s"
|
|
||||||
colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
|
|
||||||
datefmt = '%H:%M:%S'
|
|
||||||
|
|
||||||
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
|
||||||
|
|
||||||
try:
|
|
||||||
from colorlog import ColoredFormatter
|
|
||||||
logging.getLogger().handlers[0].setFormatter(ColoredFormatter(
|
|
||||||
colorfmt,
|
|
||||||
datefmt=datefmt,
|
|
||||||
reset=True,
|
|
||||||
log_colors={
|
|
||||||
'DEBUG': 'cyan',
|
|
||||||
'INFO': 'green',
|
|
||||||
'WARNING': 'yellow',
|
|
||||||
'ERROR': 'red',
|
|
||||||
'CRITICAL': 'red',
|
|
||||||
}
|
|
||||||
))
|
|
||||||
except ImportError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def command_wizard(args):
|
def command_wizard(args):
|
||||||
from esphome import wizard
|
from esphome import wizard
|
||||||
|
|
||||||
return wizard.wizard(args.configuration[0])
|
return wizard.wizard(args.configuration)
|
||||||
|
|
||||||
|
|
||||||
def command_config(args, config):
|
def command_config(args, config):
|
||||||
@@ -268,7 +282,9 @@ def command_config(args, config):
|
|||||||
def command_vscode(args):
|
def command_vscode(args):
|
||||||
from esphome import vscode
|
from esphome import vscode
|
||||||
|
|
||||||
CORE.config_path = args.configuration[0]
|
logging.disable(logging.INFO)
|
||||||
|
logging.disable(logging.WARNING)
|
||||||
|
CORE.config_path = args.configuration
|
||||||
vscode.read_config(args)
|
vscode.read_config(args)
|
||||||
|
|
||||||
|
|
||||||
@@ -277,28 +293,38 @@ def command_compile(args, config):
|
|||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return exit_code
|
return exit_code
|
||||||
if args.only_generate:
|
if args.only_generate:
|
||||||
_LOGGER.info(u"Successfully generated source code.")
|
_LOGGER.info("Successfully generated source code.")
|
||||||
return 0
|
return 0
|
||||||
exit_code = compile_program(args, config)
|
exit_code = compile_program(args, config)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return exit_code
|
return exit_code
|
||||||
_LOGGER.info(u"Successfully compiled program.")
|
_LOGGER.info("Successfully compiled program.")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def command_upload(args, config):
|
def command_upload(args, config):
|
||||||
port = choose_upload_log_host(default=args.upload_port, check_default=None,
|
port = choose_upload_log_host(
|
||||||
show_ota=True, show_mqtt=False, show_api=False)
|
default=args.device,
|
||||||
|
check_default=None,
|
||||||
|
show_ota=True,
|
||||||
|
show_mqtt=False,
|
||||||
|
show_api=False,
|
||||||
|
)
|
||||||
exit_code = upload_program(config, args, port)
|
exit_code = upload_program(config, args, port)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return exit_code
|
return exit_code
|
||||||
_LOGGER.info(u"Successfully uploaded program.")
|
_LOGGER.info("Successfully uploaded program.")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def command_logs(args, config):
|
def command_logs(args, config):
|
||||||
port = choose_upload_log_host(default=args.serial_port, check_default=None,
|
port = choose_upload_log_host(
|
||||||
show_ota=False, show_mqtt=True, show_api=True)
|
default=args.device,
|
||||||
|
check_default=None,
|
||||||
|
show_ota=False,
|
||||||
|
show_mqtt=True,
|
||||||
|
show_api=True,
|
||||||
|
)
|
||||||
return show_logs(config, args, port)
|
return show_logs(config, args, port)
|
||||||
|
|
||||||
|
|
||||||
@@ -309,17 +335,27 @@ def command_run(args, config):
|
|||||||
exit_code = compile_program(args, config)
|
exit_code = compile_program(args, config)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return exit_code
|
return exit_code
|
||||||
_LOGGER.info(u"Successfully compiled program.")
|
_LOGGER.info("Successfully compiled program.")
|
||||||
port = choose_upload_log_host(default=args.upload_port, check_default=None,
|
port = choose_upload_log_host(
|
||||||
show_ota=True, show_mqtt=False, show_api=True)
|
default=args.device,
|
||||||
|
check_default=None,
|
||||||
|
show_ota=True,
|
||||||
|
show_mqtt=False,
|
||||||
|
show_api=True,
|
||||||
|
)
|
||||||
exit_code = upload_program(config, args, port)
|
exit_code = upload_program(config, args, port)
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
return exit_code
|
return exit_code
|
||||||
_LOGGER.info(u"Successfully uploaded program.")
|
_LOGGER.info("Successfully uploaded program.")
|
||||||
if args.no_logs:
|
if args.no_logs:
|
||||||
return 0
|
return 0
|
||||||
port = choose_upload_log_host(default=args.upload_port, check_default=port,
|
port = choose_upload_log_host(
|
||||||
show_ota=False, show_mqtt=True, show_api=True)
|
default=args.device,
|
||||||
|
check_default=port,
|
||||||
|
show_ota=False,
|
||||||
|
show_mqtt=True,
|
||||||
|
show_api=True,
|
||||||
|
)
|
||||||
return show_logs(config, args, port)
|
return show_logs(config, args, port)
|
||||||
|
|
||||||
|
|
||||||
@@ -334,7 +370,7 @@ def command_mqtt_fingerprint(args, config):
|
|||||||
|
|
||||||
|
|
||||||
def command_version(args):
|
def command_version(args):
|
||||||
safe_print(u"Version: {}".format(const.__version__))
|
safe_print(f"Version: {const.__version__}")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
@@ -362,141 +398,298 @@ def command_update_all(args):
|
|||||||
twidth = 60
|
twidth = 60
|
||||||
|
|
||||||
def print_bar(middle_text):
|
def print_bar(middle_text):
|
||||||
middle_text = " {} ".format(middle_text)
|
middle_text = f" {middle_text} "
|
||||||
width = len(click.unstyle(middle_text))
|
width = len(click.unstyle(middle_text))
|
||||||
half_line = "=" * ((twidth - width) // 2)
|
half_line = "=" * ((twidth - width) // 2)
|
||||||
click.echo("%s%s%s" % (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('cyan', f)))
|
print("Updating {}".format(color(Fore.CYAN, f)))
|
||||||
print('-' * twidth)
|
print("-" * twidth)
|
||||||
print()
|
print()
|
||||||
rc = run_external_process('esphome', '--dashboard', f, 'run', '--no-logs', '--upload-port',
|
rc = run_external_process(
|
||||||
'OTA')
|
"esphome", "--dashboard", "run", "--no-logs", "--device", "OTA", f
|
||||||
|
)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
print_bar("[{}] {}".format(color('bold_green', 'SUCCESS'), f))
|
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
|
||||||
success[f] = True
|
success[f] = True
|
||||||
else:
|
else:
|
||||||
print_bar("[{}] {}".format(color('bold_red', 'ERROR'), f))
|
print_bar("[{}] {}".format(color(Fore.BOLD_RED, "ERROR"), f))
|
||||||
success[f] = False
|
success[f] = False
|
||||||
|
|
||||||
print()
|
print()
|
||||||
print()
|
print()
|
||||||
print()
|
print()
|
||||||
|
|
||||||
print_bar('[{}]'.format(color('bold_white', 'SUMMARY')))
|
print_bar("[{}]".format(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('green', 'SUCCESS')))
|
print(" - {}: {}".format(f, color(Fore.GREEN, "SUCCESS")))
|
||||||
else:
|
else:
|
||||||
print(" - {}: {}".format(f, color('bold_red', 'FAILED')))
|
print(" - {}: {}".format(f, color(Fore.BOLD_RED, "FAILED")))
|
||||||
failed += 1
|
failed += 1
|
||||||
return failed
|
return failed
|
||||||
|
|
||||||
|
|
||||||
PRE_CONFIG_ACTIONS = {
|
PRE_CONFIG_ACTIONS = {
|
||||||
'wizard': command_wizard,
|
"wizard": command_wizard,
|
||||||
'version': command_version,
|
"version": command_version,
|
||||||
'dashboard': command_dashboard,
|
"dashboard": command_dashboard,
|
||||||
'vscode': command_vscode,
|
"vscode": command_vscode,
|
||||||
'update-all': command_update_all,
|
"update-all": command_update_all,
|
||||||
}
|
}
|
||||||
|
|
||||||
POST_CONFIG_ACTIONS = {
|
POST_CONFIG_ACTIONS = {
|
||||||
'config': command_config,
|
"config": command_config,
|
||||||
'compile': command_compile,
|
"compile": command_compile,
|
||||||
'upload': command_upload,
|
"upload": command_upload,
|
||||||
'logs': command_logs,
|
"logs": command_logs,
|
||||||
'run': command_run,
|
"run": command_run,
|
||||||
'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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def parse_args(argv):
|
def parse_args(argv):
|
||||||
parser = argparse.ArgumentParser(description='ESPHome v{}'.format(const.__version__))
|
options_parser = argparse.ArgumentParser(add_help=False)
|
||||||
parser.add_argument('-v', '--verbose', help="Enable verbose esphome logs.",
|
options_parser.add_argument(
|
||||||
action='store_true')
|
"-v", "--verbose", help="Enable verbose ESPHome logs.", action="store_true"
|
||||||
parser.add_argument('-q', '--quiet', help="Disable all esphome logs.",
|
)
|
||||||
action='store_true')
|
options_parser.add_argument(
|
||||||
parser.add_argument('--dashboard', help=argparse.SUPPRESS, action='store_true')
|
"-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
|
||||||
parser.add_argument('configuration', help='Your YAML configuration file.', nargs='*')
|
)
|
||||||
|
options_parser.add_argument(
|
||||||
|
"--dashboard", help=argparse.SUPPRESS, action="store_true"
|
||||||
|
)
|
||||||
|
options_parser.add_argument(
|
||||||
|
"-s",
|
||||||
|
"--substitution",
|
||||||
|
nargs=2,
|
||||||
|
action="append",
|
||||||
|
help="Add a substitution",
|
||||||
|
metavar=("key", "value"),
|
||||||
|
)
|
||||||
|
|
||||||
subparsers = parser.add_subparsers(help='Commands', dest='command')
|
# Keep backward compatibility with the old command line format of
|
||||||
|
# esphome <config> <command>.
|
||||||
|
#
|
||||||
|
# Unfortunately this can't be done by adding another configuration argument to the
|
||||||
|
# main config parser, as argparse is greedy when parsing arguments, so in regular
|
||||||
|
# usage it'll eat the command as the configuration argument and error out out
|
||||||
|
# because it can't parse the configuration as a command.
|
||||||
|
#
|
||||||
|
# Instead, construct an ad-hoc parser for the old format that doesn't actually
|
||||||
|
# process the arguments, but parses them enough to let us figure out if the old
|
||||||
|
# format is used. In that case, swap the command and configuration in the arguments
|
||||||
|
# and continue on with the normal parser (after raising a deprecation warning).
|
||||||
|
#
|
||||||
|
# Disable argparse's built-in help option and add it manually to prevent this
|
||||||
|
# parser from printing the help messagefor the old format when invoked with -h.
|
||||||
|
compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False)
|
||||||
|
compat_parser.add_argument("-h", "--help")
|
||||||
|
compat_parser.add_argument("configuration", nargs="*")
|
||||||
|
compat_parser.add_argument(
|
||||||
|
"command",
|
||||||
|
choices=[
|
||||||
|
"config",
|
||||||
|
"compile",
|
||||||
|
"upload",
|
||||||
|
"logs",
|
||||||
|
"run",
|
||||||
|
"clean-mqtt",
|
||||||
|
"wizard",
|
||||||
|
"mqtt-fingerprint",
|
||||||
|
"version",
|
||||||
|
"clean",
|
||||||
|
"dashboard",
|
||||||
|
"vscode",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
# on Python 3.9+ we can simply set exit_on_error=False in the constructor
|
||||||
|
def _raise(x):
|
||||||
|
raise argparse.ArgumentError(None, x)
|
||||||
|
|
||||||
|
compat_parser.error = _raise
|
||||||
|
|
||||||
|
deprecated_argv_suggestion = None
|
||||||
|
|
||||||
|
if ["dashboard", "config"] == argv[1:3] or ["version"] == argv[1:2]:
|
||||||
|
# this is most likely meant in new-style arg format. do not try compat parsing
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
result, unparsed = compat_parser.parse_known_args(argv[1:])
|
||||||
|
last_option = len(argv) - len(unparsed) - 1 - len(result.configuration)
|
||||||
|
unparsed = [
|
||||||
|
"--device" if arg in ("--upload-port", "--serial-port") else arg
|
||||||
|
for arg in unparsed
|
||||||
|
]
|
||||||
|
argv = (
|
||||||
|
argv[0:last_option] + [result.command] + result.configuration + unparsed
|
||||||
|
)
|
||||||
|
deprecated_argv_suggestion = argv
|
||||||
|
except argparse.ArgumentError:
|
||||||
|
# This is not an old-style command line, so we don't have to do anything.
|
||||||
|
pass
|
||||||
|
|
||||||
|
# And continue on with regular parsing
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description=f"ESPHome v{const.__version__}", parents=[options_parser]
|
||||||
|
)
|
||||||
|
parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
|
||||||
|
|
||||||
|
mqtt_options = argparse.ArgumentParser(add_help=False)
|
||||||
|
mqtt_options.add_argument("--topic", help="Manually set the MQTT topic.")
|
||||||
|
mqtt_options.add_argument("--username", help="Manually set the MQTT username.")
|
||||||
|
mqtt_options.add_argument("--password", help="Manually set the MQTT password.")
|
||||||
|
mqtt_options.add_argument("--client-id", help="Manually set the MQTT client id.")
|
||||||
|
|
||||||
|
subparsers = parser.add_subparsers(
|
||||||
|
help="Command to run:", dest="command", metavar="command"
|
||||||
|
)
|
||||||
subparsers.required = True
|
subparsers.required = True
|
||||||
subparsers.add_parser('config', help='Validate the configuration and spit it out.')
|
|
||||||
|
|
||||||
parser_compile = subparsers.add_parser('compile',
|
parser_config = subparsers.add_parser(
|
||||||
help='Read the configuration and compile a program.')
|
"config", help="Validate the configuration and spit it out."
|
||||||
parser_compile.add_argument('--only-generate',
|
)
|
||||||
help="Only generate source code, do not compile.",
|
parser_config.add_argument(
|
||||||
action='store_true')
|
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||||
|
)
|
||||||
|
|
||||||
parser_upload = subparsers.add_parser('upload', help='Validate the configuration '
|
parser_compile = subparsers.add_parser(
|
||||||
'and upload the latest binary.')
|
"compile", help="Read the configuration and compile a program."
|
||||||
parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. "
|
)
|
||||||
"For example /dev/cu.SLAB_USBtoUART.")
|
parser_compile.add_argument(
|
||||||
|
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||||
|
)
|
||||||
|
parser_compile.add_argument(
|
||||||
|
"--only-generate",
|
||||||
|
help="Only generate source code, do not compile.",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
|
||||||
parser_logs = subparsers.add_parser('logs', help='Validate the configuration '
|
parser_upload = subparsers.add_parser(
|
||||||
'and show all MQTT logs.')
|
"upload", help="Validate the configuration and upload the latest binary."
|
||||||
parser_logs.add_argument('--topic', help='Manually set the topic to subscribe to.')
|
)
|
||||||
parser_logs.add_argument('--username', help='Manually set the username.')
|
parser_upload.add_argument(
|
||||||
parser_logs.add_argument('--password', help='Manually set the password.')
|
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||||
parser_logs.add_argument('--client-id', help='Manually set the client id.')
|
)
|
||||||
parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use"
|
parser_upload.add_argument(
|
||||||
"For example /dev/cu.SLAB_USBtoUART.")
|
"--device",
|
||||||
|
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||||
|
)
|
||||||
|
|
||||||
parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, '
|
parser_logs = subparsers.add_parser(
|
||||||
'upload it, and start MQTT logs.')
|
"logs",
|
||||||
parser_run.add_argument('--upload-port', help="Manually specify the upload port/ip to use. "
|
help="Validate the configuration and show all logs.",
|
||||||
"For example /dev/cu.SLAB_USBtoUART.")
|
parents=[mqtt_options],
|
||||||
parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.',
|
)
|
||||||
action='store_true')
|
parser_logs.add_argument(
|
||||||
parser_run.add_argument('--topic', help='Manually set the topic to subscribe to for logs.')
|
"configuration", help="Your YAML configuration file.", nargs=1
|
||||||
parser_run.add_argument('--username', help='Manually set the MQTT username for logs.')
|
)
|
||||||
parser_run.add_argument('--password', help='Manually set the MQTT password for logs.')
|
parser_logs.add_argument(
|
||||||
parser_run.add_argument('--client-id', help='Manually set the client id for logs.')
|
"--device",
|
||||||
|
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||||
|
)
|
||||||
|
|
||||||
parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from "
|
parser_run = subparsers.add_parser(
|
||||||
"retain messages.")
|
"run",
|
||||||
parser_clean.add_argument('--topic', help='Manually set the topic to subscribe to.')
|
help="Validate the configuration, create a binary, upload it, and start logs.",
|
||||||
parser_clean.add_argument('--username', help='Manually set the username.')
|
parents=[mqtt_options],
|
||||||
parser_clean.add_argument('--password', help='Manually set the password.')
|
)
|
||||||
parser_clean.add_argument('--client-id', help='Manually set the client id.')
|
parser_run.add_argument(
|
||||||
|
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||||
|
)
|
||||||
|
parser_run.add_argument(
|
||||||
|
"--device",
|
||||||
|
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||||
|
)
|
||||||
|
parser_run.add_argument(
|
||||||
|
"--no-logs", help="Disable starting logs.", action="store_true"
|
||||||
|
)
|
||||||
|
|
||||||
subparsers.add_parser('wizard', help="A helpful setup wizard that will guide "
|
parser_clean = subparsers.add_parser(
|
||||||
"you through setting up esphome.")
|
"clean-mqtt",
|
||||||
|
help="Helper to clear retained messages from an MQTT topic.",
|
||||||
|
parents=[mqtt_options],
|
||||||
|
)
|
||||||
|
parser_clean.add_argument(
|
||||||
|
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||||
|
)
|
||||||
|
|
||||||
subparsers.add_parser('mqtt-fingerprint', help="Get the SSL fingerprint from a MQTT broker.")
|
parser_wizard = subparsers.add_parser(
|
||||||
|
"wizard",
|
||||||
|
help="A helpful setup wizard that will guide you through setting up ESPHome.",
|
||||||
|
)
|
||||||
|
parser_wizard.add_argument(
|
||||||
|
"configuration",
|
||||||
|
help="Your YAML configuration file.",
|
||||||
|
)
|
||||||
|
|
||||||
subparsers.add_parser('version', help="Print the esphome version and exit.")
|
parser_fingerprint = subparsers.add_parser(
|
||||||
|
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
|
||||||
|
)
|
||||||
|
parser_fingerprint.add_argument(
|
||||||
|
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||||
|
)
|
||||||
|
|
||||||
subparsers.add_parser('clean', help="Delete all temporary build files.")
|
subparsers.add_parser("version", help="Print the ESPHome version and exit.")
|
||||||
|
|
||||||
dashboard = subparsers.add_parser('dashboard',
|
parser_clean = subparsers.add_parser(
|
||||||
help="Create a simple web server for a dashboard.")
|
"clean", help="Delete all temporary build files."
|
||||||
dashboard.add_argument("--port", help="The HTTP port to open connections on. Defaults to 6052.",
|
)
|
||||||
type=int, default=6052)
|
parser_clean.add_argument(
|
||||||
dashboard.add_argument("--username", help="The optional username to require "
|
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||||
"for authentication.",
|
)
|
||||||
type=str, default='')
|
|
||||||
dashboard.add_argument("--password", help="The optional password to require "
|
|
||||||
"for authentication.",
|
|
||||||
type=str, default='')
|
|
||||||
dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.",
|
|
||||||
action='store_true')
|
|
||||||
dashboard.add_argument("--hassio",
|
|
||||||
help=argparse.SUPPRESS,
|
|
||||||
action="store_true")
|
|
||||||
dashboard.add_argument("--socket",
|
|
||||||
help="Make the dashboard serve under a unix socket", type=str)
|
|
||||||
|
|
||||||
vscode = subparsers.add_parser('vscode', help=argparse.SUPPRESS)
|
parser_dashboard = subparsers.add_parser(
|
||||||
vscode.add_argument('--ace', action='store_true')
|
"dashboard", help="Create a simple web server for a dashboard."
|
||||||
|
)
|
||||||
|
parser_dashboard.add_argument(
|
||||||
|
"configuration",
|
||||||
|
help="Your YAML configuration file directory.",
|
||||||
|
)
|
||||||
|
parser_dashboard.add_argument(
|
||||||
|
"--port",
|
||||||
|
help="The HTTP port to open connections on. Defaults to 6052.",
|
||||||
|
type=int,
|
||||||
|
default=6052,
|
||||||
|
)
|
||||||
|
parser_dashboard.add_argument(
|
||||||
|
"--username",
|
||||||
|
help="The optional username to require for authentication.",
|
||||||
|
type=str,
|
||||||
|
default="",
|
||||||
|
)
|
||||||
|
parser_dashboard.add_argument(
|
||||||
|
"--password",
|
||||||
|
help="The optional password to require for authentication.",
|
||||||
|
type=str,
|
||||||
|
default="",
|
||||||
|
)
|
||||||
|
parser_dashboard.add_argument(
|
||||||
|
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
|
||||||
|
)
|
||||||
|
parser_dashboard.add_argument(
|
||||||
|
"--hassio", help=argparse.SUPPRESS, action="store_true"
|
||||||
|
)
|
||||||
|
parser_dashboard.add_argument(
|
||||||
|
"--socket", help="Make the dashboard serve under a unix socket", type=str
|
||||||
|
)
|
||||||
|
|
||||||
subparsers.add_parser('update-all', help=argparse.SUPPRESS)
|
parser_vscode = subparsers.add_parser("vscode")
|
||||||
|
parser_vscode.add_argument(
|
||||||
|
"configuration", help="Your YAML configuration file.", nargs=1
|
||||||
|
)
|
||||||
|
parser_vscode.add_argument("--ace", action="store_true")
|
||||||
|
|
||||||
|
parser_update = subparsers.add_parser("update-all")
|
||||||
|
parser_update.add_argument(
|
||||||
|
"configuration", help="Your YAML configuration file directory.", nargs=1
|
||||||
|
)
|
||||||
|
|
||||||
return parser.parse_args(argv[1:])
|
return parser.parse_args(argv[1:])
|
||||||
|
|
||||||
@@ -506,38 +699,44 @@ def run_esphome(argv):
|
|||||||
CORE.dashboard = args.dashboard
|
CORE.dashboard = args.dashboard
|
||||||
|
|
||||||
setup_log(args.verbose, args.quiet)
|
setup_log(args.verbose, args.quiet)
|
||||||
if args.command != 'version' and not args.configuration:
|
if args.deprecated_argv_suggestion is not None and args.command != "vscode":
|
||||||
_LOGGER.error("Missing configuration parameter, see esphome --help.")
|
_LOGGER.warning(
|
||||||
return 1
|
"Calling ESPHome with the configuration before the command is deprecated "
|
||||||
|
"and will be removed in the future. "
|
||||||
|
)
|
||||||
|
_LOGGER.warning("Please instead use:")
|
||||||
|
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion[1:]))
|
||||||
|
|
||||||
if IS_PY2:
|
if sys.version_info < (3, 7, 0):
|
||||||
_LOGGER.warning("You're using ESPHome with python 2. Support for python 2 is deprecated "
|
_LOGGER.error(
|
||||||
"and will be removed in 1.15.0. Please reinstall ESPHome with python 3.6 "
|
"You're running ESPHome with Python <3.7. ESPHome is no longer compatible "
|
||||||
"or higher.")
|
"with this Python version. Please reinstall ESPHome with Python 3.7+"
|
||||||
|
)
|
||||||
|
return 1
|
||||||
|
|
||||||
if args.command in PRE_CONFIG_ACTIONS:
|
if args.command in PRE_CONFIG_ACTIONS:
|
||||||
try:
|
try:
|
||||||
return PRE_CONFIG_ACTIONS[args.command](args)
|
return PRE_CONFIG_ACTIONS[args.command](args)
|
||||||
except EsphomeError as e:
|
except EsphomeError as e:
|
||||||
_LOGGER.error(e)
|
_LOGGER.error(e, exc_info=args.verbose)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
for conf_path in args.configuration:
|
for conf_path in args.configuration:
|
||||||
CORE.config_path = conf_path
|
CORE.config_path = conf_path
|
||||||
CORE.dashboard = args.dashboard
|
CORE.dashboard = args.dashboard
|
||||||
|
|
||||||
config = read_config()
|
config = read_config(dict(args.substitution) if args.substitution else {})
|
||||||
if config is None:
|
if config is None:
|
||||||
return 1
|
return 1
|
||||||
CORE.config = config
|
CORE.config = config
|
||||||
|
|
||||||
if args.command not in POST_CONFIG_ACTIONS:
|
if args.command not in POST_CONFIG_ACTIONS:
|
||||||
safe_print(u"Unknown command {}".format(args.command))
|
safe_print(f"Unknown command {args.command}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
rc = POST_CONFIG_ACTIONS[args.command](args, config)
|
rc = POST_CONFIG_ACTIONS[args.command](args, config)
|
||||||
except EsphomeError as e:
|
except EsphomeError as e:
|
||||||
_LOGGER.error(e)
|
_LOGGER.error(e, exc_info=args.verbose)
|
||||||
return 1
|
return 1
|
||||||
if rc != 0:
|
if rc != 0:
|
||||||
return rc
|
return rc
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -13,8 +13,8 @@ from esphome import const
|
|||||||
import esphome.api.api_pb2 as pb
|
import esphome.api.api_pb2 as pb
|
||||||
from esphome.const import CONF_PASSWORD, CONF_PORT
|
from esphome.const import CONF_PASSWORD, CONF_PORT
|
||||||
from esphome.core import EsphomeError
|
from esphome.core import EsphomeError
|
||||||
from esphome.helpers import resolve_ip_address, indent, color
|
from esphome.helpers import resolve_ip_address, indent
|
||||||
from esphome.py_compat import text_type, IS_PY2, byte_to_bytes, char_to_byte
|
from esphome.log import color, Fore
|
||||||
from esphome.util import safe_print
|
from esphome.util import safe_print
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -67,16 +67,16 @@ MESSAGE_TYPE_TO_PROTO = {
|
|||||||
|
|
||||||
def _varuint_to_bytes(value):
|
def _varuint_to_bytes(value):
|
||||||
if value <= 0x7F:
|
if value <= 0x7F:
|
||||||
return byte_to_bytes(value)
|
return bytes([value])
|
||||||
|
|
||||||
ret = bytes()
|
ret = bytes()
|
||||||
while value:
|
while value:
|
||||||
temp = value & 0x7F
|
temp = value & 0x7F
|
||||||
value >>= 7
|
value >>= 7
|
||||||
if value:
|
if value:
|
||||||
ret += byte_to_bytes(temp | 0x80)
|
ret += bytes([temp | 0x80])
|
||||||
else:
|
else:
|
||||||
ret += byte_to_bytes(temp)
|
ret += bytes([temp])
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@@ -84,8 +84,7 @@ def _varuint_to_bytes(value):
|
|||||||
def _bytes_to_varuint(value):
|
def _bytes_to_varuint(value):
|
||||||
result = 0
|
result = 0
|
||||||
bitpos = 0
|
bitpos = 0
|
||||||
for c in value:
|
for val in value:
|
||||||
val = char_to_byte(c)
|
|
||||||
result |= (val & 0x7F) << bitpos
|
result |= (val & 0x7F) << bitpos
|
||||||
bitpos += 7
|
bitpos += 7
|
||||||
if (val & 0x80) == 0:
|
if (val & 0x80) == 0:
|
||||||
@@ -179,11 +178,15 @@ class APIClient(threading.Thread):
|
|||||||
try:
|
try:
|
||||||
ip = resolve_ip_address(self._address)
|
ip = resolve_ip_address(self._address)
|
||||||
except EsphomeError as err:
|
except EsphomeError as err:
|
||||||
_LOGGER.warning("Error resolving IP address of %s. Is it connected to WiFi?",
|
_LOGGER.warning(
|
||||||
self._address)
|
"Error resolving IP address of %s. Is it connected to WiFi?",
|
||||||
_LOGGER.warning("(If this error persists, please set a static IP address: "
|
self._address,
|
||||||
"https://esphome.io/components/wifi.html#manual-ips)")
|
)
|
||||||
raise APIConnectionError(err)
|
_LOGGER.warning(
|
||||||
|
"(If this error persists, please set a static IP address: "
|
||||||
|
"https://esphome.io/components/wifi.html#manual-ips)"
|
||||||
|
)
|
||||||
|
raise APIConnectionError(err) from err
|
||||||
|
|
||||||
_LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip)
|
_LOGGER.info("Connecting to %s:%s (%s)", self._address, self._port, ip)
|
||||||
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
@@ -191,8 +194,8 @@ class APIClient(threading.Thread):
|
|||||||
self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
try:
|
try:
|
||||||
self._socket.connect((ip, self._port))
|
self._socket.connect((ip, self._port))
|
||||||
except socket.error as err:
|
except OSError as err:
|
||||||
err = APIConnectionError("Error connecting to {}: {}".format(ip, err))
|
err = APIConnectionError(f"Error connecting to {ip}: {err}")
|
||||||
self._fatal_error(err)
|
self._fatal_error(err)
|
||||||
raise err
|
raise err
|
||||||
self._socket.settimeout(0.1)
|
self._socket.settimeout(0.1)
|
||||||
@@ -200,14 +203,19 @@ class APIClient(threading.Thread):
|
|||||||
self._socket_open_event.set()
|
self._socket_open_event.set()
|
||||||
|
|
||||||
hello = pb.HelloRequest()
|
hello = pb.HelloRequest()
|
||||||
hello.client_info = 'ESPHome v{}'.format(const.__version__)
|
hello.client_info = f"ESPHome v{const.__version__}"
|
||||||
try:
|
try:
|
||||||
resp = self._send_message_await_response(hello, pb.HelloResponse)
|
resp = self._send_message_await_response(hello, pb.HelloResponse)
|
||||||
except APIConnectionError as err:
|
except APIConnectionError as err:
|
||||||
self._fatal_error(err)
|
self._fatal_error(err)
|
||||||
raise err
|
raise err
|
||||||
_LOGGER.debug("Successfully connected to %s ('%s' API=%s.%s)", self._address,
|
_LOGGER.debug(
|
||||||
resp.server_info, resp.api_version_major, resp.api_version_minor)
|
"Successfully connected to %s ('%s' API=%s.%s)",
|
||||||
|
self._address,
|
||||||
|
resp.server_info,
|
||||||
|
resp.api_version_major,
|
||||||
|
resp.api_version_minor,
|
||||||
|
)
|
||||||
self._connected = True
|
self._connected = True
|
||||||
self._refresh_ping()
|
self._refresh_ping()
|
||||||
if self.on_connect is not None:
|
if self.on_connect is not None:
|
||||||
@@ -251,8 +259,8 @@ class APIClient(threading.Thread):
|
|||||||
with self._socket_write_lock:
|
with self._socket_write_lock:
|
||||||
try:
|
try:
|
||||||
self._socket.sendall(data)
|
self._socket.sendall(data)
|
||||||
except socket.error as err:
|
except OSError as err:
|
||||||
err = APIConnectionError("Error while writing data: {}".format(err))
|
err = APIConnectionError(f"Error while writing data: {err}")
|
||||||
self._fatal_error(err)
|
self._fatal_error(err)
|
||||||
raise err
|
raise err
|
||||||
|
|
||||||
@@ -265,17 +273,16 @@ class APIClient(threading.Thread):
|
|||||||
raise ValueError
|
raise ValueError
|
||||||
|
|
||||||
encoded = msg.SerializeToString()
|
encoded = msg.SerializeToString()
|
||||||
_LOGGER.debug("Sending %s:\n%s", type(msg), indent(text_type(msg)))
|
_LOGGER.debug("Sending %s:\n%s", type(msg), indent(str(msg)))
|
||||||
if IS_PY2:
|
req = bytes([0])
|
||||||
req = chr(0x00)
|
|
||||||
else:
|
|
||||||
req = bytes([0])
|
|
||||||
req += _varuint_to_bytes(len(encoded))
|
req += _varuint_to_bytes(len(encoded))
|
||||||
req += _varuint_to_bytes(message_type)
|
req += _varuint_to_bytes(message_type)
|
||||||
req += encoded
|
req += encoded
|
||||||
self._write(req)
|
self._write(req)
|
||||||
|
|
||||||
def _send_message_await_response_complex(self, send_msg, do_append, do_stop, timeout=5):
|
def _send_message_await_response_complex(
|
||||||
|
self, send_msg, do_append, do_stop, timeout=5
|
||||||
|
):
|
||||||
event = threading.Event()
|
event = threading.Event()
|
||||||
responses = []
|
responses = []
|
||||||
|
|
||||||
@@ -300,12 +307,15 @@ class APIClient(threading.Thread):
|
|||||||
def is_response(msg):
|
def is_response(msg):
|
||||||
return isinstance(msg, response_type)
|
return isinstance(msg, response_type)
|
||||||
|
|
||||||
return self._send_message_await_response_complex(send_msg, is_response, is_response,
|
return self._send_message_await_response_complex(
|
||||||
timeout)[0]
|
send_msg, is_response, is_response, timeout
|
||||||
|
)[0]
|
||||||
|
|
||||||
def device_info(self):
|
def device_info(self):
|
||||||
self._check_connected()
|
self._check_connected()
|
||||||
return self._send_message_await_response(pb.DeviceInfoRequest(), pb.DeviceInfoResponse)
|
return self._send_message_await_response(
|
||||||
|
pb.DeviceInfoRequest(), pb.DeviceInfoResponse
|
||||||
|
)
|
||||||
|
|
||||||
def ping(self):
|
def ping(self):
|
||||||
self._check_connected()
|
self._check_connected()
|
||||||
@@ -315,7 +325,9 @@ class APIClient(threading.Thread):
|
|||||||
self._check_connected()
|
self._check_connected()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._send_message_await_response(pb.DisconnectRequest(), pb.DisconnectResponse)
|
self._send_message_await_response(
|
||||||
|
pb.DisconnectRequest(), pb.DisconnectResponse
|
||||||
|
)
|
||||||
except APIConnectionError:
|
except APIConnectionError:
|
||||||
pass
|
pass
|
||||||
self._close_socket()
|
self._close_socket()
|
||||||
@@ -351,18 +363,18 @@ class APIClient(threading.Thread):
|
|||||||
raise APIConnectionError("No socket!")
|
raise APIConnectionError("No socket!")
|
||||||
try:
|
try:
|
||||||
val = self._socket.recv(amount - len(ret))
|
val = self._socket.recv(amount - len(ret))
|
||||||
except AttributeError:
|
except AttributeError as err:
|
||||||
raise APIConnectionError("Socket was closed")
|
raise APIConnectionError("Socket was closed") from err
|
||||||
except socket.timeout:
|
except socket.timeout:
|
||||||
continue
|
continue
|
||||||
except socket.error as err:
|
except OSError as err:
|
||||||
raise APIConnectionError("Error while receiving data: {}".format(err))
|
raise APIConnectionError(f"Error while receiving data: {err}") from err
|
||||||
ret += val
|
ret += val
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _recv_varint(self):
|
def _recv_varint(self):
|
||||||
raw = bytes()
|
raw = bytes()
|
||||||
while not raw or char_to_byte(raw[-1]) & 0x80:
|
while not raw or raw[-1] & 0x80:
|
||||||
raw += self._recv(1)
|
raw += self._recv(1)
|
||||||
return _bytes_to_varuint(raw)
|
return _bytes_to_varuint(raw)
|
||||||
|
|
||||||
@@ -371,7 +383,7 @@ class APIClient(threading.Thread):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# Preamble
|
# Preamble
|
||||||
if char_to_byte(self._recv(1)[0]) != 0x00:
|
if self._recv(1)[0] != 0x00:
|
||||||
raise APIConnectionError("Invalid preamble")
|
raise APIConnectionError("Invalid preamble")
|
||||||
|
|
||||||
length = self._recv_varint()
|
length = self._recv_varint()
|
||||||
@@ -420,7 +432,7 @@ class APIClient(threading.Thread):
|
|||||||
|
|
||||||
|
|
||||||
def run_logs(config, address):
|
def run_logs(config, address):
|
||||||
conf = config['api']
|
conf = config["api"]
|
||||||
port = conf[CONF_PORT]
|
port = conf[CONF_PORT]
|
||||||
password = conf[CONF_PASSWORD]
|
password = conf[CONF_PASSWORD]
|
||||||
_LOGGER.info("Starting log output from %s using esphome API", address)
|
_LOGGER.info("Starting log output from %s using esphome API", address)
|
||||||
@@ -436,7 +448,7 @@ def run_logs(config, address):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if err:
|
if err:
|
||||||
_LOGGER.warning(u"Disconnected from API: %s", err)
|
_LOGGER.warning("Disconnected from API: %s", err)
|
||||||
|
|
||||||
while retry_timer:
|
while retry_timer:
|
||||||
retry_timer.pop(0).cancel()
|
retry_timer.pop(0).cancel()
|
||||||
@@ -452,24 +464,35 @@ def run_logs(config, address):
|
|||||||
_LOGGER.info("Successfully connected to %s", address)
|
_LOGGER.info("Successfully connected to %s", address)
|
||||||
return
|
return
|
||||||
|
|
||||||
wait_time = int(min(1.5**min(tries, 100), 30))
|
wait_time = int(min(1.5 ** min(tries, 100), 30))
|
||||||
if not has_connects:
|
if not has_connects:
|
||||||
_LOGGER.warning(u"Initial connection failed. The ESP might not be connected "
|
_LOGGER.warning(
|
||||||
u"to WiFi yet (%s). Re-Trying in %s seconds",
|
"Initial connection failed. The ESP might not be connected "
|
||||||
error, wait_time)
|
"to WiFi yet (%s). Re-Trying in %s seconds",
|
||||||
|
error,
|
||||||
|
wait_time,
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
_LOGGER.warning(u"Couldn't connect to API (%s). Trying to reconnect in %s seconds",
|
_LOGGER.warning(
|
||||||
error, wait_time)
|
"Couldn't connect to API (%s). Trying to reconnect in %s seconds",
|
||||||
timer = threading.Timer(wait_time, functools.partial(try_connect, None, tries + 1))
|
error,
|
||||||
|
wait_time,
|
||||||
|
)
|
||||||
|
timer = threading.Timer(
|
||||||
|
wait_time, functools.partial(try_connect, None, tries + 1)
|
||||||
|
)
|
||||||
timer.start()
|
timer.start()
|
||||||
retry_timer.append(timer)
|
retry_timer.append(timer)
|
||||||
|
|
||||||
def on_log(msg):
|
def on_log(msg):
|
||||||
time_ = datetime.now().time().strftime(u'[%H:%M:%S]')
|
time_ = datetime.now().time().strftime("[%H:%M:%S]")
|
||||||
text = msg.message
|
text = msg.message
|
||||||
if msg.send_failed:
|
if msg.send_failed:
|
||||||
text = color('white', '(Message skipped because it was too big to fit in '
|
text = color(
|
||||||
'TCP buffer - This is only cosmetic)')
|
Fore.WHITE,
|
||||||
|
"(Message skipped because it was too big to fit in "
|
||||||
|
"TCP buffer - This is only cosmetic)",
|
||||||
|
)
|
||||||
safe_print(time_ + text)
|
safe_print(time_ + text)
|
||||||
|
|
||||||
def on_login():
|
def on_login():
|
||||||
|
|||||||
@@ -1,19 +1,36 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_AUTOMATION_ID, CONF_CONDITION, CONF_ELSE, CONF_ID, CONF_THEN, \
|
from esphome.const import (
|
||||||
CONF_TRIGGER_ID, CONF_TYPE_ID, CONF_TIME
|
CONF_AUTOMATION_ID,
|
||||||
from esphome.core import coroutine
|
CONF_CONDITION,
|
||||||
|
CONF_ELSE,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_THEN,
|
||||||
|
CONF_TRIGGER_ID,
|
||||||
|
CONF_TYPE_ID,
|
||||||
|
CONF_TIME,
|
||||||
|
)
|
||||||
|
from esphome.jsonschema import jschema_extractor
|
||||||
from esphome.util import Registry
|
from esphome.util import Registry
|
||||||
|
|
||||||
|
|
||||||
def maybe_simple_id(*validators):
|
def maybe_simple_id(*validators):
|
||||||
|
return maybe_conf(CONF_ID, *validators)
|
||||||
|
|
||||||
|
|
||||||
|
def maybe_conf(conf, *validators):
|
||||||
validator = cv.All(*validators)
|
validator = cv.All(*validators)
|
||||||
|
|
||||||
|
@jschema_extractor("maybe")
|
||||||
def validate(value):
|
def validate(value):
|
||||||
|
# pylint: disable=comparison-with-callable
|
||||||
|
if value == jschema_extractor:
|
||||||
|
return validator
|
||||||
|
|
||||||
if isinstance(value, dict):
|
if isinstance(value, dict):
|
||||||
return validator(value)
|
return validator(value)
|
||||||
with cv.remove_prepend_path([CONF_ID]):
|
with cv.remove_prepend_path([conf]):
|
||||||
return validator({CONF_ID: value})
|
return validator({conf: value})
|
||||||
|
|
||||||
return validate
|
return validate
|
||||||
|
|
||||||
@@ -26,36 +43,34 @@ def register_condition(name, condition_type, schema):
|
|||||||
return CONDITION_REGISTRY.register(name, condition_type, schema)
|
return CONDITION_REGISTRY.register(name, condition_type, schema)
|
||||||
|
|
||||||
|
|
||||||
Action = cg.esphome_ns.class_('Action')
|
Action = cg.esphome_ns.class_("Action")
|
||||||
Trigger = cg.esphome_ns.class_('Trigger')
|
Trigger = cg.esphome_ns.class_("Trigger")
|
||||||
ACTION_REGISTRY = Registry()
|
ACTION_REGISTRY = Registry()
|
||||||
Condition = cg.esphome_ns.class_('Condition')
|
Condition = cg.esphome_ns.class_("Condition")
|
||||||
CONDITION_REGISTRY = Registry()
|
CONDITION_REGISTRY = Registry()
|
||||||
validate_action = cv.validate_registry_entry('action', ACTION_REGISTRY)
|
validate_action = cv.validate_registry_entry("action", ACTION_REGISTRY)
|
||||||
validate_action_list = cv.validate_registry('action', ACTION_REGISTRY)
|
validate_action_list = cv.validate_registry("action", ACTION_REGISTRY)
|
||||||
validate_condition = cv.validate_registry_entry('condition', CONDITION_REGISTRY)
|
validate_condition = cv.validate_registry_entry("condition", CONDITION_REGISTRY)
|
||||||
validate_condition_list = cv.validate_registry('condition', CONDITION_REGISTRY)
|
validate_condition_list = cv.validate_registry("condition", CONDITION_REGISTRY)
|
||||||
|
|
||||||
|
|
||||||
def validate_potentially_and_condition(value):
|
def validate_potentially_and_condition(value):
|
||||||
if isinstance(value, list):
|
if isinstance(value, list):
|
||||||
with cv.remove_prepend_path(['and']):
|
with cv.remove_prepend_path(["and"]):
|
||||||
return validate_condition({
|
return validate_condition({"and": value})
|
||||||
'and': value
|
|
||||||
})
|
|
||||||
return validate_condition(value)
|
return validate_condition(value)
|
||||||
|
|
||||||
|
|
||||||
DelayAction = cg.esphome_ns.class_('DelayAction', Action, cg.Component)
|
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)
|
||||||
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")
|
||||||
|
|
||||||
LambdaCondition = cg.esphome_ns.class_('LambdaCondition', Condition)
|
LambdaCondition = cg.esphome_ns.class_("LambdaCondition", Condition)
|
||||||
ForCondition = cg.esphome_ns.class_('ForCondition', Condition, cg.Component)
|
ForCondition = cg.esphome_ns.class_("ForCondition", Condition, cg.Component)
|
||||||
|
|
||||||
|
|
||||||
def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
||||||
@@ -79,9 +94,10 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
|||||||
try:
|
try:
|
||||||
return cv.Schema([schema])(value)
|
return cv.Schema([schema])(value)
|
||||||
except cv.Invalid as err2:
|
except cv.Invalid as err2:
|
||||||
if u'extra keys not allowed' in str(err2) and len(err2.path) == 2:
|
if "extra keys not allowed" in str(err2) and len(err2.path) == 2:
|
||||||
|
# pylint: disable=raise-missing-from
|
||||||
raise err
|
raise err
|
||||||
if u'Unable to find action' in str(err):
|
if "Unable to find action" in str(err):
|
||||||
raise err2
|
raise err2
|
||||||
raise cv.MultipleInvalid([err, err2])
|
raise cv.MultipleInvalid([err, err2])
|
||||||
elif isinstance(value, dict):
|
elif isinstance(value, dict):
|
||||||
@@ -92,7 +108,13 @@ 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")
|
||||||
def validator(value):
|
def validator(value):
|
||||||
|
# hack to get the schema
|
||||||
|
# pylint: disable=comparison-with-callable
|
||||||
|
if value == jschema_extractor:
|
||||||
|
return schema
|
||||||
|
|
||||||
value = validator_(value)
|
value = validator_(value)
|
||||||
if extra_validators is not None:
|
if extra_validators is not None:
|
||||||
value = cv.Schema([extra_validators])(value)
|
value = cv.Schema([extra_validators])(value)
|
||||||
@@ -105,162 +127,198 @@ def validate_automation(extra_schema=None, extra_validators=None, single=False):
|
|||||||
return validator
|
return validator
|
||||||
|
|
||||||
|
|
||||||
AUTOMATION_SCHEMA = cv.Schema({
|
AUTOMATION_SCHEMA = cv.Schema(
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger),
|
{
|
||||||
cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(Trigger),
|
||||||
cv.Required(CONF_THEN): validate_action_list,
|
cv.GenerateID(CONF_AUTOMATION_ID): cv.declare_id(Automation),
|
||||||
})
|
cv.Required(CONF_THEN): validate_action_list,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
AndCondition = cg.esphome_ns.class_('AndCondition', Condition)
|
AndCondition = cg.esphome_ns.class_("AndCondition", Condition)
|
||||||
OrCondition = cg.esphome_ns.class_('OrCondition', Condition)
|
OrCondition = cg.esphome_ns.class_("OrCondition", Condition)
|
||||||
NotCondition = cg.esphome_ns.class_('NotCondition', Condition)
|
NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
|
||||||
|
|
||||||
|
|
||||||
@register_condition('and', AndCondition, validate_condition_list)
|
@register_condition("and", AndCondition, validate_condition_list)
|
||||||
def and_condition_to_code(config, condition_id, template_arg, args):
|
async def and_condition_to_code(config, condition_id, template_arg, args):
|
||||||
conditions = yield build_condition_list(config, template_arg, args)
|
conditions = await build_condition_list(config, template_arg, args)
|
||||||
yield cg.new_Pvariable(condition_id, template_arg, conditions)
|
return cg.new_Pvariable(condition_id, template_arg, conditions)
|
||||||
|
|
||||||
|
|
||||||
@register_condition('or', OrCondition, validate_condition_list)
|
@register_condition("or", OrCondition, validate_condition_list)
|
||||||
def or_condition_to_code(config, condition_id, template_arg, args):
|
async def or_condition_to_code(config, condition_id, template_arg, args):
|
||||||
conditions = yield build_condition_list(config, template_arg, args)
|
conditions = await build_condition_list(config, template_arg, args)
|
||||||
yield cg.new_Pvariable(condition_id, template_arg, conditions)
|
return cg.new_Pvariable(condition_id, template_arg, conditions)
|
||||||
|
|
||||||
|
|
||||||
@register_condition('not', NotCondition, validate_potentially_and_condition)
|
@register_condition("not", NotCondition, validate_potentially_and_condition)
|
||||||
def not_condition_to_code(config, condition_id, template_arg, args):
|
async def not_condition_to_code(config, condition_id, template_arg, args):
|
||||||
condition = yield build_condition(config, template_arg, args)
|
condition = await build_condition(config, template_arg, args)
|
||||||
yield cg.new_Pvariable(condition_id, template_arg, condition)
|
return cg.new_Pvariable(condition_id, template_arg, condition)
|
||||||
|
|
||||||
|
|
||||||
@register_condition('lambda', LambdaCondition, cv.lambda_)
|
@register_condition("lambda", LambdaCondition, cv.returning_lambda)
|
||||||
def lambda_condition_to_code(config, condition_id, template_arg, args):
|
async def lambda_condition_to_code(config, condition_id, template_arg, args):
|
||||||
lambda_ = yield cg.process_lambda(config, args, return_type=bool)
|
lambda_ = await cg.process_lambda(config, args, return_type=bool)
|
||||||
yield cg.new_Pvariable(condition_id, template_arg, lambda_)
|
return cg.new_Pvariable(condition_id, template_arg, lambda_)
|
||||||
|
|
||||||
|
|
||||||
@register_condition('for', ForCondition, cv.Schema({
|
@register_condition(
|
||||||
cv.Required(CONF_TIME): cv.templatable(cv.positive_time_period_milliseconds),
|
"for",
|
||||||
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
ForCondition,
|
||||||
}).extend(cv.COMPONENT_SCHEMA))
|
cv.Schema(
|
||||||
def for_condition_to_code(config, condition_id, template_arg, args):
|
{
|
||||||
condition = yield build_condition(config[CONF_CONDITION], cg.TemplateArguments(), [])
|
cv.Required(CONF_TIME): cv.templatable(
|
||||||
|
cv.positive_time_period_milliseconds
|
||||||
|
),
|
||||||
|
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
|
)
|
||||||
|
async def for_condition_to_code(config, condition_id, template_arg, args):
|
||||||
|
condition = await build_condition(
|
||||||
|
config[CONF_CONDITION], cg.TemplateArguments(), []
|
||||||
|
)
|
||||||
var = cg.new_Pvariable(condition_id, template_arg, condition)
|
var = cg.new_Pvariable(condition_id, template_arg, condition)
|
||||||
yield cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32)
|
templ = await cg.templatable(config[CONF_TIME], args, cg.uint32)
|
||||||
cg.add(var.set_time(templ))
|
cg.add(var.set_time(templ))
|
||||||
yield var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@register_action('delay', DelayAction, cv.templatable(cv.positive_time_period_milliseconds))
|
@register_action(
|
||||||
def delay_action_to_code(config, action_id, template_arg, args):
|
"delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds)
|
||||||
|
)
|
||||||
|
async def delay_action_to_code(config, action_id, template_arg, args):
|
||||||
var = cg.new_Pvariable(action_id, template_arg)
|
var = cg.new_Pvariable(action_id, template_arg)
|
||||||
yield cg.register_component(var, {})
|
await cg.register_component(var, {})
|
||||||
template_ = yield cg.templatable(config, args, cg.uint32)
|
template_ = await cg.templatable(config, args, cg.uint32)
|
||||||
cg.add(var.set_delay(template_))
|
cg.add(var.set_delay(template_))
|
||||||
yield var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@register_action('if', IfAction, cv.All({
|
@register_action(
|
||||||
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
"if",
|
||||||
cv.Optional(CONF_THEN): validate_action_list,
|
IfAction,
|
||||||
cv.Optional(CONF_ELSE): validate_action_list,
|
cv.All(
|
||||||
}, cv.has_at_least_one_key(CONF_THEN, CONF_ELSE)))
|
{
|
||||||
def if_action_to_code(config, action_id, template_arg, args):
|
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||||
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
|
cv.Optional(CONF_THEN): validate_action_list,
|
||||||
|
cv.Optional(CONF_ELSE): validate_action_list,
|
||||||
|
},
|
||||||
|
cv.has_at_least_one_key(CONF_THEN, CONF_ELSE),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def if_action_to_code(config, action_id, 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_THEN in config:
|
if CONF_THEN in config:
|
||||||
actions = yield build_action_list(config[CONF_THEN], template_arg, args)
|
actions = await build_action_list(config[CONF_THEN], template_arg, args)
|
||||||
cg.add(var.add_then(actions))
|
cg.add(var.add_then(actions))
|
||||||
if CONF_ELSE in config:
|
if CONF_ELSE in config:
|
||||||
actions = yield build_action_list(config[CONF_ELSE], template_arg, args)
|
actions = await build_action_list(config[CONF_ELSE], template_arg, args)
|
||||||
cg.add(var.add_else(actions))
|
cg.add(var.add_else(actions))
|
||||||
yield var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@register_action('while', WhileAction, cv.Schema({
|
@register_action(
|
||||||
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
"while",
|
||||||
cv.Required(CONF_THEN): validate_action_list,
|
WhileAction,
|
||||||
}))
|
cv.Schema(
|
||||||
def while_action_to_code(config, action_id, template_arg, args):
|
{
|
||||||
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
|
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||||
|
cv.Required(CONF_THEN): validate_action_list,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def while_action_to_code(config, action_id, 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)
|
||||||
actions = yield build_action_list(config[CONF_THEN], template_arg, args)
|
actions = await build_action_list(config[CONF_THEN], template_arg, args)
|
||||||
cg.add(var.add_then(actions))
|
cg.add(var.add_then(actions))
|
||||||
yield var
|
return var
|
||||||
|
|
||||||
|
|
||||||
def validate_wait_until(value):
|
def validate_wait_until(value):
|
||||||
schema = cv.Schema({
|
schema = cv.Schema(
|
||||||
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
{
|
||||||
})
|
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||||
|
}
|
||||||
|
)
|
||||||
if isinstance(value, dict) and CONF_CONDITION in value:
|
if isinstance(value, dict) and CONF_CONDITION in value:
|
||||||
return schema(value)
|
return schema(value)
|
||||||
return validate_wait_until({CONF_CONDITION: value})
|
return validate_wait_until({CONF_CONDITION: value})
|
||||||
|
|
||||||
|
|
||||||
@register_action('wait_until', WaitUntilAction, validate_wait_until)
|
@register_action("wait_until", WaitUntilAction, validate_wait_until)
|
||||||
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 = yield 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)
|
||||||
yield cg.register_component(var, {})
|
await cg.register_component(var, {})
|
||||||
yield var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@register_action('lambda', LambdaAction, cv.lambda_)
|
@register_action("lambda", LambdaAction, cv.lambda_)
|
||||||
def lambda_action_to_code(config, action_id, template_arg, args):
|
async def lambda_action_to_code(config, action_id, template_arg, args):
|
||||||
lambda_ = yield cg.process_lambda(config, args, return_type=cg.void)
|
lambda_ = await cg.process_lambda(config, args, return_type=cg.void)
|
||||||
yield cg.new_Pvariable(action_id, template_arg, lambda_)
|
return cg.new_Pvariable(action_id, template_arg, lambda_)
|
||||||
|
|
||||||
|
|
||||||
@register_action('component.update', UpdateComponentAction, maybe_simple_id({
|
@register_action(
|
||||||
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
|
"component.update",
|
||||||
}))
|
UpdateComponentAction,
|
||||||
def component_update_action_to_code(config, action_id, template_arg, args):
|
maybe_simple_id(
|
||||||
comp = yield cg.get_variable(config[CONF_ID])
|
{
|
||||||
yield cg.new_Pvariable(action_id, template_arg, comp)
|
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
async def component_update_action_to_code(config, action_id, template_arg, args):
|
||||||
|
comp = await cg.get_variable(config[CONF_ID])
|
||||||
|
return cg.new_Pvariable(action_id, template_arg, comp)
|
||||||
|
|
||||||
|
|
||||||
@coroutine
|
async def build_action(full_config, template_arg, args):
|
||||||
def build_action(full_config, template_arg, args):
|
registry_entry, config = cg.extract_registry_entry_config(
|
||||||
registry_entry, config = cg.extract_registry_entry_config(ACTION_REGISTRY, full_config)
|
ACTION_REGISTRY, full_config
|
||||||
|
)
|
||||||
action_id = full_config[CONF_TYPE_ID]
|
action_id = full_config[CONF_TYPE_ID]
|
||||||
builder = registry_entry.coroutine_fun
|
builder = registry_entry.coroutine_fun
|
||||||
yield builder(config, action_id, template_arg, args)
|
ret = await builder(config, action_id, template_arg, args)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@coroutine
|
async def build_action_list(config, templ, arg_type):
|
||||||
def build_action_list(config, templ, arg_type):
|
|
||||||
actions = []
|
actions = []
|
||||||
for conf in config:
|
for conf in config:
|
||||||
action = yield build_action(conf, templ, arg_type)
|
action = await build_action(conf, templ, arg_type)
|
||||||
actions.append(action)
|
actions.append(action)
|
||||||
yield actions
|
return actions
|
||||||
|
|
||||||
|
|
||||||
@coroutine
|
async def build_condition(full_config, template_arg, args):
|
||||||
def build_condition(full_config, template_arg, args):
|
registry_entry, config = cg.extract_registry_entry_config(
|
||||||
registry_entry, config = cg.extract_registry_entry_config(CONDITION_REGISTRY, full_config)
|
CONDITION_REGISTRY, full_config
|
||||||
|
)
|
||||||
action_id = full_config[CONF_TYPE_ID]
|
action_id = full_config[CONF_TYPE_ID]
|
||||||
builder = registry_entry.coroutine_fun
|
builder = registry_entry.coroutine_fun
|
||||||
yield builder(config, action_id, template_arg, args)
|
ret = await builder(config, action_id, template_arg, args)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
@coroutine
|
async def build_condition_list(config, templ, args):
|
||||||
def build_condition_list(config, templ, args):
|
|
||||||
conditions = []
|
conditions = []
|
||||||
for conf in config:
|
for conf in config:
|
||||||
condition = yield build_condition(conf, templ, args)
|
condition = await build_condition(conf, templ, args)
|
||||||
conditions.append(condition)
|
conditions.append(condition)
|
||||||
yield conditions
|
return conditions
|
||||||
|
|
||||||
|
|
||||||
@coroutine
|
async def build_automation(trigger, args, config):
|
||||||
def build_automation(trigger, args, config):
|
|
||||||
arg_types = [arg[0] for arg in args]
|
arg_types = [arg[0] for arg in args]
|
||||||
templ = cg.TemplateArguments(*arg_types)
|
templ = cg.TemplateArguments(*arg_types)
|
||||||
obj = cg.new_Pvariable(config[CONF_AUTOMATION_ID], templ, trigger)
|
obj = cg.new_Pvariable(config[CONF_AUTOMATION_ID], templ, trigger)
|
||||||
actions = yield build_action_list(config[CONF_THEN], templ, args)
|
actions = await build_action_list(config[CONF_THEN], templ, args)
|
||||||
cg.add(obj.add_actions(actions))
|
cg.add(obj.add_actions(actions))
|
||||||
yield obj
|
return obj
|
||||||
|
|||||||
@@ -9,18 +9,73 @@
|
|||||||
|
|
||||||
# pylint: disable=unused-import
|
# pylint: disable=unused-import
|
||||||
from esphome.cpp_generator import ( # noqa
|
from esphome.cpp_generator import ( # noqa
|
||||||
Expression, RawExpression, RawStatement, TemplateArguments,
|
Expression,
|
||||||
StructInitializer, ArrayInitializer, safe_exp, Statement, LineComment,
|
RawExpression,
|
||||||
progmem_array, statement, variable, Pvariable, new_Pvariable,
|
RawStatement,
|
||||||
add, add_global, add_library, add_build_flag, add_define,
|
TemplateArguments,
|
||||||
get_variable, get_variable_with_full_id, process_lambda, is_template, templatable, MockObj,
|
StructInitializer,
|
||||||
MockObjClass)
|
ArrayInitializer,
|
||||||
|
safe_exp,
|
||||||
|
Statement,
|
||||||
|
LineComment,
|
||||||
|
progmem_array,
|
||||||
|
static_const_array,
|
||||||
|
statement,
|
||||||
|
variable,
|
||||||
|
new_variable,
|
||||||
|
Pvariable,
|
||||||
|
new_Pvariable,
|
||||||
|
add,
|
||||||
|
add_global,
|
||||||
|
add_library,
|
||||||
|
add_build_flag,
|
||||||
|
add_define,
|
||||||
|
get_variable,
|
||||||
|
get_variable_with_full_id,
|
||||||
|
process_lambda,
|
||||||
|
is_template,
|
||||||
|
templatable,
|
||||||
|
MockObj,
|
||||||
|
MockObjClass,
|
||||||
|
)
|
||||||
from esphome.cpp_helpers import ( # noqa
|
from esphome.cpp_helpers import ( # noqa
|
||||||
gpio_pin_expression, register_component, build_registry_entry,
|
gpio_pin_expression,
|
||||||
build_registry_list, extract_registry_entry_config, register_parented)
|
register_component,
|
||||||
|
build_registry_entry,
|
||||||
|
build_registry_list,
|
||||||
|
extract_registry_entry_config,
|
||||||
|
register_parented,
|
||||||
|
)
|
||||||
from esphome.cpp_types import ( # noqa
|
from esphome.cpp_types import ( # noqa
|
||||||
global_ns, void, nullptr, float_, double, bool_, std_ns, std_string,
|
global_ns,
|
||||||
std_vector, uint8, uint16, uint32, int32, const_char_ptr, NAN,
|
void,
|
||||||
esphome_ns, App, Nameable, Component, ComponentPtr,
|
nullptr,
|
||||||
PollingComponent, Application, optional, arduino_json_ns, JsonObject,
|
float_,
|
||||||
JsonObjectRef, JsonObjectConstRef, Controller, GPIOPin)
|
double,
|
||||||
|
bool_,
|
||||||
|
int_,
|
||||||
|
std_ns,
|
||||||
|
std_string,
|
||||||
|
std_vector,
|
||||||
|
uint8,
|
||||||
|
uint16,
|
||||||
|
uint32,
|
||||||
|
uint64,
|
||||||
|
int32,
|
||||||
|
const_char_ptr,
|
||||||
|
NAN,
|
||||||
|
esphome_ns,
|
||||||
|
App,
|
||||||
|
Nameable,
|
||||||
|
Component,
|
||||||
|
ComponentPtr,
|
||||||
|
PollingComponent,
|
||||||
|
Application,
|
||||||
|
optional,
|
||||||
|
arduino_json_ns,
|
||||||
|
JsonObject,
|
||||||
|
JsonObjectRef,
|
||||||
|
JsonObjectConstRef,
|
||||||
|
Controller,
|
||||||
|
GPIOPin,
|
||||||
|
)
|
||||||
|
|||||||
@@ -4,13 +4,14 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace a4988 {
|
namespace a4988 {
|
||||||
|
|
||||||
static const char *TAG = "a4988.stepper";
|
static const char *const TAG = "a4988.stepper";
|
||||||
|
|
||||||
void A4988::setup() {
|
void A4988::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up A4988...");
|
ESP_LOGCONFIG(TAG, "Setting up A4988...");
|
||||||
if (this->sleep_pin_ != nullptr) {
|
if (this->sleep_pin_ != nullptr) {
|
||||||
this->sleep_pin_->setup();
|
this->sleep_pin_->setup();
|
||||||
this->sleep_pin_->digital_write(false);
|
this->sleep_pin_->digital_write(false);
|
||||||
|
this->sleep_pin_state_ = false;
|
||||||
}
|
}
|
||||||
this->step_pin_->setup();
|
this->step_pin_->setup();
|
||||||
this->step_pin_->digital_write(false);
|
this->step_pin_->digital_write(false);
|
||||||
@@ -27,7 +28,12 @@ void A4988::dump_config() {
|
|||||||
void A4988::loop() {
|
void A4988::loop() {
|
||||||
bool at_target = this->has_reached_target();
|
bool at_target = this->has_reached_target();
|
||||||
if (this->sleep_pin_ != nullptr) {
|
if (this->sleep_pin_ != nullptr) {
|
||||||
|
bool sleep_rising_edge = !sleep_pin_state_ & !at_target;
|
||||||
this->sleep_pin_->digital_write(!at_target);
|
this->sleep_pin_->digital_write(!at_target);
|
||||||
|
this->sleep_pin_state_ = !at_target;
|
||||||
|
if (sleep_rising_edge) {
|
||||||
|
delayMicroseconds(1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (at_target) {
|
if (at_target) {
|
||||||
this->high_freq_.stop();
|
this->high_freq_.stop();
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class A4988 : public stepper::Stepper, public Component {
|
|||||||
GPIOPin *step_pin_;
|
GPIOPin *step_pin_;
|
||||||
GPIOPin *dir_pin_;
|
GPIOPin *dir_pin_;
|
||||||
GPIOPin *sleep_pin_{nullptr};
|
GPIOPin *sleep_pin_{nullptr};
|
||||||
|
bool sleep_pin_state_;
|
||||||
HighFrequencyLoopRequester high_freq_;
|
HighFrequencyLoopRequester high_freq_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,27 +5,29 @@ import esphome.codegen as cg
|
|||||||
from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN
|
from esphome.const import CONF_DIR_PIN, CONF_ID, CONF_SLEEP_PIN, CONF_STEP_PIN
|
||||||
|
|
||||||
|
|
||||||
a4988_ns = cg.esphome_ns.namespace('a4988')
|
a4988_ns = cg.esphome_ns.namespace("a4988")
|
||||||
A4988 = a4988_ns.class_('A4988', stepper.Stepper, cg.Component)
|
A4988 = a4988_ns.class_("A4988", stepper.Stepper, cg.Component)
|
||||||
|
|
||||||
CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend({
|
CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend(
|
||||||
cv.Required(CONF_ID): cv.declare_id(A4988),
|
{
|
||||||
cv.Required(CONF_STEP_PIN): pins.gpio_output_pin_schema,
|
cv.Required(CONF_ID): cv.declare_id(A4988),
|
||||||
cv.Required(CONF_DIR_PIN): pins.gpio_output_pin_schema,
|
cv.Required(CONF_STEP_PIN): pins.gpio_output_pin_schema,
|
||||||
cv.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema,
|
cv.Required(CONF_DIR_PIN): pins.gpio_output_pin_schema,
|
||||||
}).extend(cv.COMPONENT_SCHEMA)
|
cv.Optional(CONF_SLEEP_PIN): pins.gpio_output_pin_schema,
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
yield cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
yield stepper.register_stepper(var, config)
|
await stepper.register_stepper(var, config)
|
||||||
|
|
||||||
step_pin = yield cg.gpio_pin_expression(config[CONF_STEP_PIN])
|
step_pin = await cg.gpio_pin_expression(config[CONF_STEP_PIN])
|
||||||
cg.add(var.set_step_pin(step_pin))
|
cg.add(var.set_step_pin(step_pin))
|
||||||
dir_pin = yield cg.gpio_pin_expression(config[CONF_DIR_PIN])
|
dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
|
||||||
cg.add(var.set_dir_pin(dir_pin))
|
cg.add(var.set_dir_pin(dir_pin))
|
||||||
|
|
||||||
if CONF_SLEEP_PIN in config:
|
if CONF_SLEEP_PIN in config:
|
||||||
sleep_pin = yield cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
|
sleep_pin = await cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
|
||||||
cg.add(var.set_sleep_pin(sleep_pin))
|
cg.add(var.set_sleep_pin(sleep_pin))
|
||||||
|
|||||||
217
esphome/components/ac_dimmer/ac_dimmer.cpp
Normal file
217
esphome/components/ac_dimmer/ac_dimmer.cpp
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
#include "ac_dimmer.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
#include <core_esp8266_waveform.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ac_dimmer {
|
||||||
|
|
||||||
|
static const char *const TAG = "ac_dimmer";
|
||||||
|
|
||||||
|
// Global array to store dimmer objects
|
||||||
|
static AcDimmerDataStore *all_dimmers[32]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
|
/// Time in microseconds the gate should be held high
|
||||||
|
/// 10µs should be long enough for most triacs
|
||||||
|
/// For reference: BT136 datasheet says 2µs nominal (page 7)
|
||||||
|
static const uint32_t GATE_ENABLE_TIME = 10;
|
||||||
|
|
||||||
|
/// Function called from timer interrupt
|
||||||
|
/// Input is current time in microseconds (micros())
|
||||||
|
/// 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) {
|
||||||
|
// If no ZC signal received yet.
|
||||||
|
if (this->crossed_zero_at == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
uint32_t time_since_zc = now - this->crossed_zero_at;
|
||||||
|
if (this->value == 65535 || this->value == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->enable_time_us != 0 && time_since_zc >= this->enable_time_us) {
|
||||||
|
this->enable_time_us = 0;
|
||||||
|
this->gate_pin->digital_write(true);
|
||||||
|
// Prevent too short pulses
|
||||||
|
this->disable_time_us = max(this->disable_time_us, time_since_zc + GATE_ENABLE_TIME);
|
||||||
|
}
|
||||||
|
if (this->disable_time_us != 0 && time_since_zc >= this->disable_time_us) {
|
||||||
|
this->disable_time_us = 0;
|
||||||
|
this->gate_pin->digital_write(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time_since_zc < this->enable_time_us)
|
||||||
|
// Next event is enable, return time until that event
|
||||||
|
return this->enable_time_us - time_since_zc;
|
||||||
|
else if (time_since_zc < disable_time_us) {
|
||||||
|
// Next event is disable, return time until that event
|
||||||
|
return this->disable_time_us - time_since_zc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time_since_zc >= this->cycle_time_us) {
|
||||||
|
// Already past last cycle time, schedule next call shortly
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->cycle_time_us - time_since_zc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run timer interrupt code and return in how many µs the next event is expected
|
||||||
|
uint32_t ICACHE_RAM_ATTR HOT timer_interrupt() {
|
||||||
|
// run at least with 1kHz
|
||||||
|
uint32_t min_dt_us = 1000;
|
||||||
|
uint32_t now = micros();
|
||||||
|
for (auto *dimmer : all_dimmers) {
|
||||||
|
if (dimmer == nullptr)
|
||||||
|
// no more dimmers
|
||||||
|
break;
|
||||||
|
uint32_t res = dimmer->timer_intr(now);
|
||||||
|
if (res != 0 && res < min_dt_us)
|
||||||
|
min_dt_us = res;
|
||||||
|
}
|
||||||
|
// return time until next timer1 interrupt in µs
|
||||||
|
return min_dt_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GPIO interrupt routine, called when ZC pin triggers
|
||||||
|
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
||||||
|
uint32_t prev_crossed = this->crossed_zero_at;
|
||||||
|
|
||||||
|
// 50Hz mains frequency should give a half cycle of 10ms a 60Hz will give 8.33ms
|
||||||
|
// in any case the cycle last at least 5ms
|
||||||
|
this->crossed_zero_at = micros();
|
||||||
|
uint32_t cycle_time = this->crossed_zero_at - prev_crossed;
|
||||||
|
if (cycle_time > 5000) {
|
||||||
|
this->cycle_time_us = cycle_time;
|
||||||
|
} else {
|
||||||
|
// Otherwise this is noise and this is 2nd (or 3rd...) fall in the same pulse
|
||||||
|
// Consider this is the right fall edge and accumulate the cycle time instead
|
||||||
|
this->cycle_time_us += cycle_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->value == 65535) {
|
||||||
|
// fully on, enable output immediately
|
||||||
|
this->gate_pin->digital_write(true);
|
||||||
|
} else if (this->init_cycle) {
|
||||||
|
// send a full cycle
|
||||||
|
this->init_cycle = false;
|
||||||
|
this->enable_time_us = 0;
|
||||||
|
this->disable_time_us = cycle_time_us;
|
||||||
|
} else if (this->value == 0) {
|
||||||
|
// fully off, disable output immediately
|
||||||
|
this->gate_pin->digital_write(false);
|
||||||
|
} else {
|
||||||
|
if (this->method == DIM_METHOD_TRAILING) {
|
||||||
|
this->enable_time_us = 1; // cannot be 0
|
||||||
|
this->disable_time_us = max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
|
||||||
|
} else {
|
||||||
|
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
|
||||||
|
// also take into account min_power
|
||||||
|
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);
|
||||||
|
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
|
||||||
|
// this is for brightness near 99%
|
||||||
|
this->disable_time_us = max(this->enable_time_us + GATE_ENABLE_TIME, (uint32_t) cycle_time_us / 10);
|
||||||
|
} else {
|
||||||
|
this->gate_pin->digital_write(false);
|
||||||
|
this->disable_time_us = this->cycle_time_us;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
|
||||||
|
// Attaching pin interrupts on the same pin will override the previous interupt
|
||||||
|
// 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
|
||||||
|
// if any of them are using the same ZC pin, and also trigger the interrupt for *them*.
|
||||||
|
for (auto *dimmer : all_dimmers) {
|
||||||
|
if (dimmer == nullptr)
|
||||||
|
break;
|
||||||
|
if (dimmer->zero_cross_pin_number == store->zero_cross_pin_number) {
|
||||||
|
dimmer->gpio_intr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
// ESP32 implementation, uses basically the same code but needs to wrap
|
||||||
|
// timer_interrupt() function to auto-reschedule
|
||||||
|
static hw_timer_t *dimmer_timer = nullptr;
|
||||||
|
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_timer_intr() { timer_interrupt(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void AcDimmer::setup() {
|
||||||
|
// extend all_dimmers array with our dimmer
|
||||||
|
|
||||||
|
// Need to be sure the zero cross pin is setup only once, ESP8266 fails and ESP32 seems to fail silently
|
||||||
|
auto setup_zero_cross_pin = true;
|
||||||
|
|
||||||
|
for (auto &all_dimmer : all_dimmers) {
|
||||||
|
if (all_dimmer == nullptr) {
|
||||||
|
all_dimmer = &this->store_;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (all_dimmer->zero_cross_pin_number == this->zero_cross_pin_->get_pin()) {
|
||||||
|
setup_zero_cross_pin = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->gate_pin_->setup();
|
||||||
|
this->store_.gate_pin = this->gate_pin_->to_isr();
|
||||||
|
this->store_.zero_cross_pin_number = this->zero_cross_pin_->get_pin();
|
||||||
|
this->store_.min_power = static_cast<uint16_t>(this->min_power_ * 1000);
|
||||||
|
this->min_power_ = 0;
|
||||||
|
this->store_.method = this->method_;
|
||||||
|
|
||||||
|
if (setup_zero_cross_pin) {
|
||||||
|
this->zero_cross_pin_->setup();
|
||||||
|
this->store_.zero_cross_pin = this->zero_cross_pin_->to_isr();
|
||||||
|
this->zero_cross_pin_->attach_interrupt(&AcDimmerDataStore::s_gpio_intr, &this->store_, FALLING);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP8266
|
||||||
|
// Uses ESP8266 waveform (soft PWM) class
|
||||||
|
// PWM and AcDimmer can even run at the same time this way
|
||||||
|
setTimer1Callback(&timer_interrupt);
|
||||||
|
#endif
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
// 80 Divider -> 1 count=1µs
|
||||||
|
dimmer_timer = timerBegin(0, 80, true);
|
||||||
|
timerAttachInterrupt(dimmer_timer, &AcDimmerDataStore::s_timer_intr, true);
|
||||||
|
// For ESP32, we can't use dynamic interval calculation because the timerX functions
|
||||||
|
// are not callable from ISR (placed in flash storage).
|
||||||
|
// Here we just use an interrupt firing every 50 µs.
|
||||||
|
timerAlarmWrite(dimmer_timer, 50, true);
|
||||||
|
timerAlarmEnable(dimmer_timer);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
void AcDimmer::write_state(float state) {
|
||||||
|
auto new_value = static_cast<uint16_t>(roundf(state * 65535));
|
||||||
|
if (new_value != 0 && this->store_.value == 0)
|
||||||
|
this->store_.init_cycle = this->init_with_half_cycle_;
|
||||||
|
this->store_.value = new_value;
|
||||||
|
}
|
||||||
|
void AcDimmer::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "AcDimmer:");
|
||||||
|
LOG_PIN(" Output Pin: ", this->gate_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, " Init with half cycle: %s", YESNO(this->init_with_half_cycle_));
|
||||||
|
if (method_ == DIM_METHOD_LEADING_PULSE)
|
||||||
|
ESP_LOGCONFIG(TAG, " Method: leading pulse");
|
||||||
|
else if (method_ == DIM_METHOD_LEADING)
|
||||||
|
ESP_LOGCONFIG(TAG, " Method: leading");
|
||||||
|
else
|
||||||
|
ESP_LOGCONFIG(TAG, " Method: trailing");
|
||||||
|
|
||||||
|
LOG_FLOAT_OUTPUT(this);
|
||||||
|
ESP_LOGV(TAG, " Estimated Frequency: %.3fHz", 1e6f / this->store_.cycle_time_us / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ac_dimmer
|
||||||
|
} // namespace esphome
|
||||||
66
esphome/components/ac_dimmer/ac_dimmer.h
Normal file
66
esphome/components/ac_dimmer/ac_dimmer.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/esphal.h"
|
||||||
|
#include "esphome/components/output/float_output.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace ac_dimmer {
|
||||||
|
|
||||||
|
enum DimMethod { DIM_METHOD_LEADING_PULSE = 0, DIM_METHOD_LEADING, DIM_METHOD_TRAILING };
|
||||||
|
|
||||||
|
struct AcDimmerDataStore {
|
||||||
|
/// Zero-cross pin
|
||||||
|
ISRInternalGPIOPin *zero_cross_pin;
|
||||||
|
/// Zero-cross pin number - used to share ZC pin across multiple dimmers
|
||||||
|
uint8_t zero_cross_pin_number;
|
||||||
|
/// Output pin to write to
|
||||||
|
ISRInternalGPIOPin *gate_pin;
|
||||||
|
/// Value of the dimmer - 0 to 65535.
|
||||||
|
uint16_t value;
|
||||||
|
/// Minimum power for activation
|
||||||
|
uint16_t min_power;
|
||||||
|
/// Time between the last two ZC pulses
|
||||||
|
uint32_t cycle_time_us;
|
||||||
|
/// Time (in micros()) of last ZC signal
|
||||||
|
uint32_t crossed_zero_at;
|
||||||
|
/// Time since last ZC pulse to enable gate pin. 0 means not set.
|
||||||
|
uint32_t enable_time_us;
|
||||||
|
/// Time since last ZC pulse to disable gate pin. 0 means no disable.
|
||||||
|
uint32_t disable_time_us;
|
||||||
|
/// Set to send the first half ac cycle complete
|
||||||
|
bool init_cycle;
|
||||||
|
/// Dimmer method
|
||||||
|
DimMethod method;
|
||||||
|
|
||||||
|
uint32_t timer_intr(uint32_t now);
|
||||||
|
|
||||||
|
void gpio_intr();
|
||||||
|
static void s_gpio_intr(AcDimmerDataStore *store);
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
static void s_timer_intr();
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
class AcDimmer : public output::FloatOutput, public Component {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
|
||||||
|
void dump_config() override;
|
||||||
|
void set_gate_pin(GPIOPin *gate_pin) { gate_pin_ = gate_pin; }
|
||||||
|
void set_zero_cross_pin(GPIOPin *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_method(DimMethod method) { method_ = method; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(float state) override;
|
||||||
|
|
||||||
|
GPIOPin *gate_pin_;
|
||||||
|
GPIOPin *zero_cross_pin_;
|
||||||
|
AcDimmerDataStore store_;
|
||||||
|
bool init_with_half_cycle_;
|
||||||
|
DimMethod method_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ac_dimmer
|
||||||
|
} // namespace esphome
|
||||||
49
esphome/components/ac_dimmer/output.py
Normal file
49
esphome/components/ac_dimmer/output.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome import pins
|
||||||
|
from esphome.components import output
|
||||||
|
from esphome.const import CONF_ID, CONF_MIN_POWER, CONF_METHOD
|
||||||
|
|
||||||
|
CODEOWNERS = ["@glmnet"]
|
||||||
|
|
||||||
|
ac_dimmer_ns = cg.esphome_ns.namespace("ac_dimmer")
|
||||||
|
AcDimmer = ac_dimmer_ns.class_("AcDimmer", output.FloatOutput, cg.Component)
|
||||||
|
|
||||||
|
DimMethod = ac_dimmer_ns.enum("DimMethod")
|
||||||
|
DIM_METHODS = {
|
||||||
|
"LEADING_PULSE": DimMethod.DIM_METHOD_LEADING_PULSE,
|
||||||
|
"LEADING": DimMethod.DIM_METHOD_LEADING,
|
||||||
|
"TRAILING": DimMethod.DIM_METHOD_TRAILING,
|
||||||
|
}
|
||||||
|
|
||||||
|
CONF_GATE_PIN = "gate_pin"
|
||||||
|
CONF_ZERO_CROSS_PIN = "zero_cross_pin"
|
||||||
|
CONF_INIT_WITH_HALF_CYCLE = "init_with_half_cycle"
|
||||||
|
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.declare_id(AcDimmer),
|
||||||
|
cv.Required(CONF_GATE_PIN): pins.internal_gpio_output_pin_schema,
|
||||||
|
cv.Required(CONF_ZERO_CROSS_PIN): pins.internal_gpio_input_pin_schema,
|
||||||
|
cv.Optional(CONF_INIT_WITH_HALF_CYCLE, default=True): cv.boolean,
|
||||||
|
cv.Optional(CONF_METHOD, default="leading pulse"): cv.enum(
|
||||||
|
DIM_METHODS, upper=True, space="_"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
|
# override default min power to 10%
|
||||||
|
if CONF_MIN_POWER not in config:
|
||||||
|
config[CONF_MIN_POWER] = 0.1
|
||||||
|
await output.register_output(var, config)
|
||||||
|
|
||||||
|
pin = await cg.gpio_pin_expression(config[CONF_GATE_PIN])
|
||||||
|
cg.add(var.set_gate_pin(pin))
|
||||||
|
pin = await cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN])
|
||||||
|
cg.add(var.set_zero_cross_pin(pin))
|
||||||
|
cg.add(var.set_init_with_half_cycle(config[CONF_INIT_WITH_HALF_CYCLE]))
|
||||||
|
cg.add(var.set_method(config[CONF_METHOD]))
|
||||||
27
esphome/components/adalight/__init__.py
Normal file
27
esphome/components/adalight/__init__.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import uart
|
||||||
|
from esphome.components.light.types import AddressableLightEffect
|
||||||
|
from esphome.components.light.effects import register_addressable_effect
|
||||||
|
from esphome.const import CONF_NAME, CONF_UART_ID
|
||||||
|
|
||||||
|
DEPENDENCIES = ["uart"]
|
||||||
|
|
||||||
|
adalight_ns = cg.esphome_ns.namespace("adalight")
|
||||||
|
AdalightLightEffect = adalight_ns.class_(
|
||||||
|
"AdalightLightEffect", uart.UARTDevice, AddressableLightEffect
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.Schema({})
|
||||||
|
|
||||||
|
|
||||||
|
@register_addressable_effect(
|
||||||
|
"adalight",
|
||||||
|
AdalightLightEffect,
|
||||||
|
"Adalight",
|
||||||
|
{cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)},
|
||||||
|
)
|
||||||
|
async def adalight_light_effect_to_code(config, effect_id):
|
||||||
|
effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
|
||||||
|
await uart.register_uart_device(effect, config)
|
||||||
|
return effect
|
||||||
140
esphome/components/adalight/adalight_light_effect.cpp
Normal file
140
esphome/components/adalight/adalight_light_effect.cpp
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
#include "adalight_light_effect.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace adalight {
|
||||||
|
|
||||||
|
static const char *const TAG = "adalight_light_effect";
|
||||||
|
|
||||||
|
static const uint32_t ADALIGHT_ACK_INTERVAL = 1000;
|
||||||
|
static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000;
|
||||||
|
|
||||||
|
AdalightLightEffect::AdalightLightEffect(const std::string &name) : AddressableLightEffect(name) {}
|
||||||
|
|
||||||
|
void AdalightLightEffect::start() {
|
||||||
|
AddressableLightEffect::start();
|
||||||
|
|
||||||
|
last_ack_ = 0;
|
||||||
|
last_byte_ = 0;
|
||||||
|
last_reset_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdalightLightEffect::stop() {
|
||||||
|
frame_.resize(0);
|
||||||
|
|
||||||
|
AddressableLightEffect::stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
int AdalightLightEffect::get_frame_size_(int led_count) const {
|
||||||
|
// 3 bytes: Ada
|
||||||
|
// 2 bytes: LED count
|
||||||
|
// 1 byte: checksum
|
||||||
|
// 3 bytes per LED
|
||||||
|
return 3 + 2 + 1 + led_count * 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdalightLightEffect::reset_frame_(light::AddressableLight &it) {
|
||||||
|
int buffer_capacity = get_frame_size_(it.size());
|
||||||
|
|
||||||
|
frame_.clear();
|
||||||
|
frame_.reserve(buffer_capacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) {
|
||||||
|
for (int led = it.size(); led-- > 0;) {
|
||||||
|
it[led].set(COLOR_BLACK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdalightLightEffect::apply(light::AddressableLight &it, const Color ¤t_color) {
|
||||||
|
const uint32_t now = millis();
|
||||||
|
|
||||||
|
if (now - this->last_ack_ >= ADALIGHT_ACK_INTERVAL) {
|
||||||
|
ESP_LOGV(TAG, "Sending ACK");
|
||||||
|
this->write_str("Ada\n");
|
||||||
|
this->last_ack_ = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->last_reset_) {
|
||||||
|
ESP_LOGW(TAG, "Frame: Reset.");
|
||||||
|
reset_frame_(it);
|
||||||
|
blank_all_leds_(it);
|
||||||
|
this->last_reset_ = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this->frame_.empty() && now - this->last_byte_ >= ADALIGHT_RECEIVE_TIMEOUT) {
|
||||||
|
ESP_LOGW(TAG, "Frame: Receive timeout (size=%zu).", this->frame_.size());
|
||||||
|
reset_frame_(it);
|
||||||
|
blank_all_leds_(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->available() > 0) {
|
||||||
|
ESP_LOGV(TAG, "Frame: Available (size=%d).", this->available());
|
||||||
|
}
|
||||||
|
|
||||||
|
while (this->available() != 0) {
|
||||||
|
uint8_t data;
|
||||||
|
if (!this->read_byte(&data))
|
||||||
|
break;
|
||||||
|
this->frame_.push_back(data);
|
||||||
|
this->last_byte_ = now;
|
||||||
|
|
||||||
|
switch (this->parse_frame_(it)) {
|
||||||
|
case INVALID:
|
||||||
|
ESP_LOGD(TAG, "Frame: Invalid (size=%zu, first=%d).", this->frame_.size(), this->frame_[0]);
|
||||||
|
reset_frame_(it);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PARTIAL:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case CONSUMED:
|
||||||
|
ESP_LOGV(TAG, "Frame: Consumed (size=%zu).", this->frame_.size());
|
||||||
|
reset_frame_(it);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableLight &it) {
|
||||||
|
if (frame_.empty())
|
||||||
|
return INVALID;
|
||||||
|
|
||||||
|
// Check header: `Ada`
|
||||||
|
if (frame_[0] != 'A')
|
||||||
|
return INVALID;
|
||||||
|
if (frame_.size() > 1 && frame_[1] != 'd')
|
||||||
|
return INVALID;
|
||||||
|
if (frame_.size() > 2 && frame_[2] != 'a')
|
||||||
|
return INVALID;
|
||||||
|
|
||||||
|
// 3 bytes: Count Hi, Count Lo, Checksum
|
||||||
|
if (frame_.size() < 6)
|
||||||
|
return PARTIAL;
|
||||||
|
|
||||||
|
// Check checksum
|
||||||
|
uint16_t checksum = frame_[3] ^ frame_[4] ^ 0x55;
|
||||||
|
if (checksum != frame_[5])
|
||||||
|
return INVALID;
|
||||||
|
|
||||||
|
// Check if we received the full frame
|
||||||
|
uint16_t led_count = (frame_[3] << 8) + frame_[4] + 1;
|
||||||
|
auto buffer_size = get_frame_size_(led_count);
|
||||||
|
if (frame_.size() < buffer_size)
|
||||||
|
return PARTIAL;
|
||||||
|
|
||||||
|
// Apply lights
|
||||||
|
auto accepted_led_count = std::min<int>(led_count, it.size());
|
||||||
|
uint8_t *led_data = &frame_[6];
|
||||||
|
|
||||||
|
for (int led = 0; led < accepted_led_count; led++, led_data += 3) {
|
||||||
|
auto white = std::min(std::min(led_data[0], led_data[1]), led_data[2]);
|
||||||
|
|
||||||
|
it[led].set(Color(led_data[0], led_data[1], led_data[2], white));
|
||||||
|
}
|
||||||
|
|
||||||
|
return CONSUMED;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace adalight
|
||||||
|
} // namespace esphome
|
||||||
41
esphome/components/adalight/adalight_light_effect.h
Normal file
41
esphome/components/adalight/adalight_light_effect.h
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/light/addressable_light_effect.h"
|
||||||
|
#include "esphome/components/uart/uart.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace adalight {
|
||||||
|
|
||||||
|
class AdalightLightEffect : public light::AddressableLightEffect, public uart::UARTDevice {
|
||||||
|
public:
|
||||||
|
AdalightLightEffect(const std::string &name);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void start() override;
|
||||||
|
void stop() override;
|
||||||
|
void apply(light::AddressableLight &it, const Color ¤t_color) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum Frame {
|
||||||
|
INVALID,
|
||||||
|
PARTIAL,
|
||||||
|
CONSUMED,
|
||||||
|
};
|
||||||
|
|
||||||
|
int get_frame_size_(int led_count) const;
|
||||||
|
void reset_frame_(light::AddressableLight &it);
|
||||||
|
void blank_all_leds_(light::AddressableLight &it);
|
||||||
|
Frame parse_frame_(light::AddressableLight &it);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint32_t last_ack_{0};
|
||||||
|
uint32_t last_byte_{0};
|
||||||
|
uint32_t last_reset_{0};
|
||||||
|
std::vector<uint8_t> frame_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace adalight
|
||||||
|
} // namespace esphome
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
CODEOWNERS = ["@esphome/core"]
|
||||||
|
|||||||
@@ -8,18 +8,45 @@ ADC_MODE(ADC_VCC)
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace adc {
|
namespace adc {
|
||||||
|
|
||||||
static const char *TAG = "adc";
|
static const char *const TAG = "adc";
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuation_ = attenuation; }
|
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
|
||||||
|
|
||||||
|
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
|
||||||
|
switch (pin) {
|
||||||
|
case 36:
|
||||||
|
return ADC1_CHANNEL_0;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
#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
|
||||||
GPIOPin(this->pin_, INPUT).setup();
|
GPIOPin(this->pin_, INPUT).setup();
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
analogSetPinAttenuation(this->pin_, this->attenuation_);
|
adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_);
|
||||||
|
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||||
|
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
void ADCSensor::dump_config() {
|
void ADCSensor::dump_config() {
|
||||||
@@ -34,18 +61,20 @@ void ADCSensor::dump_config() {
|
|||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
|
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
|
||||||
switch (this->attenuation_) {
|
switch (this->attenuation_) {
|
||||||
case ADC_0db:
|
case ADC_ATTEN_DB_0:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
|
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
|
||||||
break;
|
break;
|
||||||
case ADC_2_5db:
|
case ADC_ATTEN_DB_2_5:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
|
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
|
||||||
break;
|
break;
|
||||||
case ADC_6db:
|
case ADC_ATTEN_DB_6:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
|
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
|
||||||
break;
|
break;
|
||||||
case ADC_11db:
|
case ADC_ATTEN_DB_11:
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
|
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
|
||||||
break;
|
break;
|
||||||
|
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
LOG_UPDATE_INTERVAL(this);
|
LOG_UPDATE_INTERVAL(this);
|
||||||
@@ -58,20 +87,23 @@ void ADCSensor::update() {
|
|||||||
}
|
}
|
||||||
float ADCSensor::sample() {
|
float ADCSensor::sample() {
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
float value_v = analogRead(this->pin_) / 4095.0f;
|
int raw = adc1_get_raw(gpio_to_adc1(pin_));
|
||||||
|
float value_v = raw / 4095.0f;
|
||||||
switch (this->attenuation_) {
|
switch (this->attenuation_) {
|
||||||
case ADC_0db:
|
case ADC_ATTEN_DB_0:
|
||||||
value_v *= 1.1;
|
value_v *= 1.1;
|
||||||
break;
|
break;
|
||||||
case ADC_2_5db:
|
case ADC_ATTEN_DB_2_5:
|
||||||
value_v *= 1.5;
|
value_v *= 1.5;
|
||||||
break;
|
break;
|
||||||
case ADC_6db:
|
case ADC_ATTEN_DB_6:
|
||||||
value_v *= 2.2;
|
value_v *= 2.2;
|
||||||
break;
|
break;
|
||||||
case ADC_11db:
|
case ADC_ATTEN_DB_11:
|
||||||
value_v *= 3.9;
|
value_v *= 3.9;
|
||||||
break;
|
break;
|
||||||
|
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return value_v;
|
return value_v;
|
||||||
#endif
|
#endif
|
||||||
@@ -80,7 +112,7 @@ float ADCSensor::sample() {
|
|||||||
#ifdef USE_ADC_SENSOR_VCC
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
return ESP.getVcc() / 1024.0f;
|
return ESP.getVcc() / 1024.0f;
|
||||||
#else
|
#else
|
||||||
return analogRead(this->pin_) / 1024.0f;
|
return analogRead(this->pin_) / 1024.0f; // NOLINT
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
#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
|
||||||
|
#include "driver/adc.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace adc {
|
namespace adc {
|
||||||
|
|
||||||
@@ -13,7 +17,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||||||
public:
|
public:
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_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_attenuation_t attenuation);
|
void set_attenuation(adc_atten_t attenuation);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// Update adc values.
|
/// Update adc values.
|
||||||
@@ -34,7 +38,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||||||
uint8_t pin_;
|
uint8_t pin_;
|
||||||
|
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
adc_attenuation_t attenuation_{ADC_0db};
|
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,45 +2,63 @@ import esphome.codegen as cg
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.components import sensor, voltage_sampler
|
from esphome.components import sensor, voltage_sampler
|
||||||
from esphome.const import CONF_ATTENUATION, CONF_ID, CONF_PIN, ICON_FLASH, UNIT_VOLT
|
from esphome.const import (
|
||||||
|
CONF_ATTENUATION,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_PIN,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
|
ICON_EMPTY,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_VOLT,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
AUTO_LOAD = ['voltage_sampler']
|
AUTO_LOAD = ["voltage_sampler"]
|
||||||
|
|
||||||
ATTENUATION_MODES = {
|
ATTENUATION_MODES = {
|
||||||
'0db': cg.global_ns.ADC_0db,
|
"0db": cg.global_ns.ADC_ATTEN_DB_0,
|
||||||
'2.5db': cg.global_ns.ADC_2_5db,
|
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
|
||||||
'6db': cg.global_ns.ADC_6db,
|
"6db": cg.global_ns.ADC_ATTEN_DB_6,
|
||||||
'11db': cg.global_ns.ADC_11db,
|
"11db": cg.global_ns.ADC_ATTEN_DB_11,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def validate_adc_pin(value):
|
def validate_adc_pin(value):
|
||||||
vcc = str(value).upper()
|
vcc = str(value).upper()
|
||||||
if vcc == 'VCC':
|
if vcc == "VCC":
|
||||||
return cv.only_on_esp8266(vcc)
|
return cv.only_on_esp8266(vcc)
|
||||||
return pins.analog_pin(value)
|
return pins.analog_pin(value)
|
||||||
|
|
||||||
|
|
||||||
adc_ns = cg.esphome_ns.namespace('adc')
|
adc_ns = cg.esphome_ns.namespace("adc")
|
||||||
ADCSensor = adc_ns.class_('ADCSensor', sensor.Sensor, cg.PollingComponent,
|
ADCSensor = adc_ns.class_(
|
||||||
voltage_sampler.VoltageSampler)
|
"ADCSensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
|
||||||
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2).extend({
|
CONFIG_SCHEMA = (
|
||||||
cv.GenerateID(): cv.declare_id(ADCSensor),
|
sensor.sensor_schema(
|
||||||
cv.Required(CONF_PIN): validate_adc_pin,
|
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
|
||||||
cv.SplitDefault(CONF_ATTENUATION, esp32='0db'):
|
)
|
||||||
cv.All(cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)),
|
.extend(
|
||||||
}).extend(cv.polling_component_schema('60s'))
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(ADCSensor),
|
||||||
|
cv.Required(CONF_PIN): validate_adc_pin,
|
||||||
|
cv.SplitDefault(CONF_ATTENUATION, esp32="0db"): cv.All(
|
||||||
|
cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
yield cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
yield sensor.register_sensor(var, config)
|
await sensor.register_sensor(var, 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]))
|
cg.add(var.set_pin(config[CONF_PIN]))
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#include "addressable_light_display.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace addressable_light {
|
||||||
|
|
||||||
|
static const char *const TAG = "addressable_light.display";
|
||||||
|
|
||||||
|
int AddressableLightDisplay::get_width_internal() { return this->width_; }
|
||||||
|
int AddressableLightDisplay::get_height_internal() { return this->height_; }
|
||||||
|
|
||||||
|
void AddressableLightDisplay::setup() {
|
||||||
|
this->addressable_light_buffer_.resize(this->width_ * this->height_, {0, 0, 0, 0});
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddressableLightDisplay::update() {
|
||||||
|
if (!this->enabled_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this->do_update_();
|
||||||
|
this->display();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddressableLightDisplay::display() {
|
||||||
|
bool dirty = false;
|
||||||
|
uint8_t old_r, old_g, old_b, old_w;
|
||||||
|
Color *c;
|
||||||
|
|
||||||
|
for (uint32_t offset = 0; offset < this->addressable_light_buffer_.size(); offset++) {
|
||||||
|
c = &(this->addressable_light_buffer_[offset]);
|
||||||
|
|
||||||
|
light::ESPColorView pixel = (*this->light_)[offset];
|
||||||
|
|
||||||
|
// Track the original values for the pixel view. If it has changed updating, then
|
||||||
|
// we trigger a redraw. Avoiding redraws == avoiding flicker!
|
||||||
|
old_r = pixel.get_red();
|
||||||
|
old_g = pixel.get_green();
|
||||||
|
old_b = pixel.get_blue();
|
||||||
|
old_w = pixel.get_white();
|
||||||
|
|
||||||
|
pixel.set_rgbw(c->r, c->g, c->b, c->w);
|
||||||
|
|
||||||
|
// If the actual value of the pixel changed, then schedule a redraw.
|
||||||
|
if (pixel.get_red() != old_r || pixel.get_green() != old_g || pixel.get_blue() != old_b ||
|
||||||
|
pixel.get_white() != old_w) {
|
||||||
|
dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dirty) {
|
||||||
|
this->light_->schedule_show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HOT AddressableLightDisplay::draw_absolute_pixel_internal(int x, int y, Color color) {
|
||||||
|
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this->pixel_mapper_f_.has_value()) {
|
||||||
|
// Params are passed by reference, so they may be modified in call.
|
||||||
|
this->addressable_light_buffer_[(*this->pixel_mapper_f_)(x, y)] = color;
|
||||||
|
} else {
|
||||||
|
this->addressable_light_buffer_[y * this->get_width_internal() + x] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace addressable_light
|
||||||
|
} // namespace esphome
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/core/color.h"
|
||||||
|
#include "esphome/components/display/display_buffer.h"
|
||||||
|
#include "esphome/components/light/addressable_light.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace addressable_light {
|
||||||
|
|
||||||
|
class AddressableLightDisplay : public display::DisplayBuffer, public PollingComponent {
|
||||||
|
public:
|
||||||
|
light::AddressableLight *get_light() const { return this->light_; }
|
||||||
|
|
||||||
|
void set_width(int32_t width) { width_ = width; }
|
||||||
|
void set_height(int32_t height) { height_ = height; }
|
||||||
|
void set_light(light::LightState *state) {
|
||||||
|
light_state_ = state;
|
||||||
|
light_ = static_cast<light::AddressableLight *>(state->get_output());
|
||||||
|
}
|
||||||
|
void set_enabled(bool enabled) {
|
||||||
|
if (light_state_) {
|
||||||
|
if (enabled_ && !enabled) { // enabled -> disabled
|
||||||
|
// - Tell the parent light to refresh, effectively wiping the display. Also
|
||||||
|
// restores the previous effect (if any).
|
||||||
|
light_state_->make_call().set_effect(this->last_effect_).perform();
|
||||||
|
|
||||||
|
} else if (!enabled_ && enabled) { // disabled -> enabled
|
||||||
|
// - Save the current effect.
|
||||||
|
this->last_effect_ = light_state_->get_effect_name();
|
||||||
|
// - Disable any current effect.
|
||||||
|
light_state_->make_call().set_effect(0).perform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enabled_ = enabled;
|
||||||
|
}
|
||||||
|
bool get_enabled() { return enabled_; }
|
||||||
|
|
||||||
|
void set_pixel_mapper(std::function<int(int, int)> &&pixel_mapper_f) { this->pixel_mapper_f_ = pixel_mapper_f; }
|
||||||
|
void setup() override;
|
||||||
|
void display();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int get_width_internal() override;
|
||||||
|
int get_height_internal() override;
|
||||||
|
void draw_absolute_pixel_internal(int x, int y, Color color) override;
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
light::LightState *light_state_;
|
||||||
|
light::AddressableLight *light_;
|
||||||
|
bool enabled_{true};
|
||||||
|
int32_t width_;
|
||||||
|
int32_t height_;
|
||||||
|
std::vector<Color> addressable_light_buffer_;
|
||||||
|
optional<std::string> last_effect_;
|
||||||
|
optional<std::function<int(int, int)>> pixel_mapper_f_;
|
||||||
|
};
|
||||||
|
} // namespace addressable_light
|
||||||
|
} // namespace esphome
|
||||||
63
esphome/components/addressable_light/display.py
Normal file
63
esphome/components/addressable_light/display.py
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import display, light
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_LAMBDA,
|
||||||
|
CONF_PAGES,
|
||||||
|
CONF_ADDRESSABLE_LIGHT_ID,
|
||||||
|
CONF_HEIGHT,
|
||||||
|
CONF_WIDTH,
|
||||||
|
CONF_UPDATE_INTERVAL,
|
||||||
|
CONF_PIXEL_MAPPER,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@justfalter"]
|
||||||
|
|
||||||
|
addressable_light_ns = cg.esphome_ns.namespace("addressable_light")
|
||||||
|
AddressableLightDisplay = addressable_light_ns.class_(
|
||||||
|
"AddressableLightDisplay", display.DisplayBuffer, cg.PollingComponent
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(
|
||||||
|
display.FULL_DISPLAY_SCHEMA.extend(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(AddressableLightDisplay),
|
||||||
|
cv.Required(CONF_ADDRESSABLE_LIGHT_ID): cv.use_id(
|
||||||
|
light.AddressableLightState
|
||||||
|
),
|
||||||
|
cv.Required(CONF_WIDTH): cv.positive_int,
|
||||||
|
cv.Required(CONF_HEIGHT): cv.positive_int,
|
||||||
|
cv.Optional(
|
||||||
|
CONF_UPDATE_INTERVAL, default="16ms"
|
||||||
|
): cv.positive_time_period_milliseconds,
|
||||||
|
cv.Optional(CONF_PIXEL_MAPPER): cv.returning_lambda,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
cv.has_at_most_one_key(CONF_PAGES, CONF_LAMBDA),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
wrapped_light = await cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID])
|
||||||
|
cg.add(var.set_width(config[CONF_WIDTH]))
|
||||||
|
cg.add(var.set_height(config[CONF_HEIGHT]))
|
||||||
|
cg.add(var.set_light(wrapped_light))
|
||||||
|
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await display.register_display(var, config)
|
||||||
|
|
||||||
|
if CONF_PIXEL_MAPPER in config:
|
||||||
|
pixel_mapper_template_ = await cg.process_lambda(
|
||||||
|
config[CONF_PIXEL_MAPPER],
|
||||||
|
[(int, "x"), (int, "y")],
|
||||||
|
return_type=cg.int_,
|
||||||
|
)
|
||||||
|
cg.add(var.set_pixel_mapper(pixel_mapper_template_))
|
||||||
|
|
||||||
|
if CONF_LAMBDA in config:
|
||||||
|
lambda_ = await cg.process_lambda(
|
||||||
|
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
|
||||||
|
)
|
||||||
|
cg.add(var.set_writer(lambda_))
|
||||||
@@ -4,10 +4,13 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ade7953 {
|
namespace ade7953 {
|
||||||
|
|
||||||
static const char *TAG = "ade7953";
|
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_) {
|
||||||
|
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_);
|
||||||
@@ -18,8 +21,8 @@ void ADE7953::dump_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define ADE_PUBLISH_(name, factor) \
|
#define ADE_PUBLISH_(name, factor) \
|
||||||
if (name) { \
|
if ((name) && this->name##_sensor_) { \
|
||||||
float value = *name / factor; \
|
float value = *(name) / (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, factor) ADE_PUBLISH_(name, factor)
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ 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) {
|
||||||
|
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; }
|
||||||
@@ -20,6 +24,11 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void setup() override {
|
void setup() override {
|
||||||
|
if (this->has_irq_) {
|
||||||
|
auto pin = GPIOPin(this->irq_pin_number_, INPUT);
|
||||||
|
this->irq_pin_ = &pin;
|
||||||
|
this->irq_pin_->setup();
|
||||||
|
}
|
||||||
this->set_timeout(100, [this]() {
|
this->set_timeout(100, [this]() {
|
||||||
this->ade_write_<uint8_t>(0x0010, 0x04);
|
this->ade_write_<uint8_t>(0x0010, 0x04);
|
||||||
this->ade_write_<uint8_t>(0x00FE, 0xAD);
|
this->ade_write_<uint8_t>(0x00FE, 0xAD);
|
||||||
@@ -55,6 +64,9 @@ class ADE7953 : public i2c::I2CDevice, public PollingComponent {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool has_irq_ = false;
|
||||||
|
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};
|
||||||
|
|||||||
@@ -1,39 +1,83 @@
|
|||||||
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 sensor, i2c
|
from esphome.components import sensor, i2c
|
||||||
from esphome.const import CONF_ID, CONF_VOLTAGE, \
|
from esphome import pins
|
||||||
UNIT_VOLT, ICON_FLASH, UNIT_AMPERE, UNIT_WATT
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_VOLTAGE,
|
||||||
|
DEVICE_CLASS_CURRENT,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
|
ICON_EMPTY,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_VOLT,
|
||||||
|
UNIT_AMPERE,
|
||||||
|
UNIT_WATT,
|
||||||
|
)
|
||||||
|
|
||||||
DEPENDENCIES = ['i2c']
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
ace7953_ns = cg.esphome_ns.namespace('ade7953')
|
ade7953_ns = cg.esphome_ns.namespace("ade7953")
|
||||||
ADE7953 = ace7953_ns.class_('ADE7953', cg.PollingComponent, i2c.I2CDevice)
|
ADE7953 = ade7953_ns.class_("ADE7953", cg.PollingComponent, i2c.I2CDevice)
|
||||||
|
|
||||||
CONF_CURRENT_A = 'current_a'
|
CONF_IRQ_PIN = "irq_pin"
|
||||||
CONF_CURRENT_B = 'current_b'
|
CONF_CURRENT_A = "current_a"
|
||||||
CONF_ACTIVE_POWER_A = 'active_power_a'
|
CONF_CURRENT_B = "current_b"
|
||||||
CONF_ACTIVE_POWER_B = 'active_power_b'
|
CONF_ACTIVE_POWER_A = "active_power_a"
|
||||||
|
CONF_ACTIVE_POWER_B = "active_power_b"
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = (
|
||||||
cv.GenerateID(): cv.declare_id(ADE7953),
|
cv.Schema(
|
||||||
|
{
|
||||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 1),
|
cv.GenerateID(): cv.declare_id(ADE7953),
|
||||||
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
|
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
|
||||||
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2),
|
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||||
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1),
|
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
|
||||||
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 1),
|
),
|
||||||
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x38))
|
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(
|
||||||
|
UNIT_AMPERE,
|
||||||
|
ICON_EMPTY,
|
||||||
|
2,
|
||||||
|
DEVICE_CLASS_CURRENT,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
|
||||||
|
UNIT_AMPERE,
|
||||||
|
ICON_EMPTY,
|
||||||
|
2,
|
||||||
|
DEVICE_CLASS_CURRENT,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
|
||||||
|
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
|
||||||
|
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x38))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
yield cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
yield i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
for key in [CONF_VOLTAGE, CONF_CURRENT_A, CONF_CURRENT_B, CONF_ACTIVE_POWER_A,
|
if CONF_IRQ_PIN in config:
|
||||||
CONF_ACTIVE_POWER_B]:
|
cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
|
||||||
|
|
||||||
|
for key in [
|
||||||
|
CONF_VOLTAGE,
|
||||||
|
CONF_CURRENT_A,
|
||||||
|
CONF_CURRENT_B,
|
||||||
|
CONF_ACTIVE_POWER_A,
|
||||||
|
CONF_ACTIVE_POWER_B,
|
||||||
|
]:
|
||||||
if key not in config:
|
if key not in config:
|
||||||
continue
|
continue
|
||||||
conf = config[key]
|
conf = config[key]
|
||||||
sens = yield sensor.new_sensor(conf)
|
sens = await sensor.new_sensor(conf)
|
||||||
cg.add(getattr(var, 'set_{}_sensor'.format(key))(sens))
|
cg.add(getattr(var, f"set_{key}_sensor")(sens))
|
||||||
|
|||||||
@@ -3,23 +3,29 @@ import esphome.config_validation as cv
|
|||||||
from esphome.components import i2c
|
from esphome.components import i2c
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
DEPENDENCIES = ['i2c']
|
DEPENDENCIES = ["i2c"]
|
||||||
AUTO_LOAD = ['sensor', 'voltage_sampler']
|
AUTO_LOAD = ["sensor", "voltage_sampler"]
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
|
||||||
ads1115_ns = cg.esphome_ns.namespace('ads1115')
|
ads1115_ns = cg.esphome_ns.namespace("ads1115")
|
||||||
ADS1115Component = ads1115_ns.class_('ADS1115Component', cg.Component, i2c.I2CDevice)
|
ADS1115Component = ads1115_ns.class_("ADS1115Component", cg.Component, i2c.I2CDevice)
|
||||||
|
|
||||||
CONF_CONTINUOUS_MODE = 'continuous_mode'
|
CONF_CONTINUOUS_MODE = "continuous_mode"
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = (
|
||||||
cv.GenerateID(): cv.declare_id(ADS1115Component),
|
cv.Schema(
|
||||||
cv.Optional(CONF_CONTINUOUS_MODE, default=False): cv.boolean,
|
{
|
||||||
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(None))
|
cv.GenerateID(): cv.declare_id(ADS1115Component),
|
||||||
|
cv.Optional(CONF_CONTINUOUS_MODE, default=False): cv.boolean,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.extend(i2c.i2c_device_schema(None))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
yield cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
yield i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
|
cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace ads1115 {
|
namespace ads1115 {
|
||||||
|
|
||||||
static const char *TAG = "ads1115";
|
static const char *const TAG = "ads1115";
|
||||||
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
|
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
|
||||||
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
|
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
|
||||||
|
|
||||||
@@ -64,11 +64,6 @@ void ADS1115Component::setup() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->prev_config_ = config;
|
this->prev_config_ = config;
|
||||||
|
|
||||||
for (auto *sensor : this->sensors_) {
|
|
||||||
this->set_interval(sensor->get_name(), sensor->update_interval(),
|
|
||||||
[this, sensor] { this->request_measurement(sensor); });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
void ADS1115Component::dump_config() {
|
void ADS1115Component::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
|
ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
|
||||||
@@ -107,17 +102,22 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
|
|||||||
}
|
}
|
||||||
this->prev_config_ = config;
|
this->prev_config_ = config;
|
||||||
|
|
||||||
// about 1.6 ms with 860 samples per second
|
// about 1.2 ms with 860 samples per second
|
||||||
delay(2);
|
delay(2);
|
||||||
|
|
||||||
uint32_t start = millis();
|
// in continuous mode, conversion will always be running, rely on the delay
|
||||||
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
|
// to ensure conversion is taking place with the correct settings
|
||||||
if (millis() - start > 100) {
|
// can we use the rdy pin to trigger when a conversion is done?
|
||||||
ESP_LOGW(TAG, "Reading ADS1115 timed out");
|
if (!this->continuous_mode_) {
|
||||||
this->status_set_warning();
|
uint32_t start = millis();
|
||||||
return NAN;
|
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
|
||||||
|
if (millis() - start > 100) {
|
||||||
|
ESP_LOGW(TAG, "Reading ADS1115 timed out");
|
||||||
|
this->status_set_warning();
|
||||||
|
return NAN;
|
||||||
|
}
|
||||||
|
yield();
|
||||||
}
|
}
|
||||||
yield();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,61 +1,77 @@
|
|||||||
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 sensor, voltage_sampler
|
from esphome.components import sensor, voltage_sampler
|
||||||
from esphome.const import CONF_GAIN, CONF_MULTIPLEXER, ICON_FLASH, UNIT_VOLT, CONF_ID
|
from esphome.const import (
|
||||||
from esphome.py_compat import string_types
|
CONF_GAIN,
|
||||||
|
CONF_MULTIPLEXER,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
|
ICON_EMPTY,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_VOLT,
|
||||||
|
CONF_ID,
|
||||||
|
)
|
||||||
from . import ads1115_ns, ADS1115Component
|
from . import ads1115_ns, ADS1115Component
|
||||||
|
|
||||||
DEPENDENCIES = ['ads1115']
|
DEPENDENCIES = ["ads1115"]
|
||||||
|
|
||||||
ADS1115Multiplexer = ads1115_ns.enum('ADS1115Multiplexer')
|
ADS1115Multiplexer = ads1115_ns.enum("ADS1115Multiplexer")
|
||||||
MUX = {
|
MUX = {
|
||||||
'A0_A1': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1,
|
"A0_A1": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N1,
|
||||||
'A0_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3,
|
"A0_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_N3,
|
||||||
'A1_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3,
|
"A1_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_N3,
|
||||||
'A2_A3': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3,
|
"A2_A3": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_N3,
|
||||||
'A0_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG,
|
"A0_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P0_NG,
|
||||||
'A1_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG,
|
"A1_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P1_NG,
|
||||||
'A2_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG,
|
"A2_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P2_NG,
|
||||||
'A3_GND': ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG,
|
"A3_GND": ADS1115Multiplexer.ADS1115_MULTIPLEXER_P3_NG,
|
||||||
}
|
}
|
||||||
|
|
||||||
ADS1115Gain = ads1115_ns.enum('ADS1115Gain')
|
ADS1115Gain = ads1115_ns.enum("ADS1115Gain")
|
||||||
GAIN = {
|
GAIN = {
|
||||||
'6.144': ADS1115Gain.ADS1115_GAIN_6P144,
|
"6.144": ADS1115Gain.ADS1115_GAIN_6P144,
|
||||||
'4.096': ADS1115Gain.ADS1115_GAIN_4P096,
|
"4.096": ADS1115Gain.ADS1115_GAIN_4P096,
|
||||||
'2.048': ADS1115Gain.ADS1115_GAIN_2P048,
|
"2.048": ADS1115Gain.ADS1115_GAIN_2P048,
|
||||||
'1.024': ADS1115Gain.ADS1115_GAIN_1P024,
|
"1.024": ADS1115Gain.ADS1115_GAIN_1P024,
|
||||||
'0.512': ADS1115Gain.ADS1115_GAIN_0P512,
|
"0.512": ADS1115Gain.ADS1115_GAIN_0P512,
|
||||||
'0.256': ADS1115Gain.ADS1115_GAIN_0P256,
|
"0.256": ADS1115Gain.ADS1115_GAIN_0P256,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def validate_gain(value):
|
def validate_gain(value):
|
||||||
if isinstance(value, float):
|
if isinstance(value, float):
|
||||||
value = u'{:0.03f}'.format(value)
|
value = f"{value:0.03f}"
|
||||||
elif not isinstance(value, string_types):
|
elif not isinstance(value, str):
|
||||||
raise cv.Invalid('invalid gain "{}"'.format(value))
|
raise cv.Invalid(f'invalid gain "{value}"')
|
||||||
|
|
||||||
return cv.enum(GAIN)(value)
|
return cv.enum(GAIN)(value)
|
||||||
|
|
||||||
|
|
||||||
ADS1115Sensor = ads1115_ns.class_('ADS1115Sensor', sensor.Sensor, cg.PollingComponent,
|
ADS1115Sensor = ads1115_ns.class_(
|
||||||
voltage_sampler.VoltageSampler)
|
"ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
|
||||||
|
)
|
||||||
|
|
||||||
CONF_ADS1115_ID = 'ads1115_id'
|
CONF_ADS1115_ID = "ads1115_id"
|
||||||
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 3).extend({
|
CONFIG_SCHEMA = (
|
||||||
cv.GenerateID(): cv.declare_id(ADS1115Sensor),
|
sensor.sensor_schema(
|
||||||
cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
|
UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE, STATE_CLASS_MEASUREMENT
|
||||||
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space='_'),
|
)
|
||||||
cv.Required(CONF_GAIN): validate_gain,
|
.extend(
|
||||||
}).extend(cv.polling_component_schema('60s'))
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(ADS1115Sensor),
|
||||||
|
cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
|
||||||
|
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space="_"),
|
||||||
|
cv.Required(CONF_GAIN): validate_gain,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
paren = yield cg.get_variable(config[CONF_ADS1115_ID])
|
paren = await cg.get_variable(config[CONF_ADS1115_ID])
|
||||||
var = cg.new_Pvariable(config[CONF_ID], paren)
|
var = cg.new_Pvariable(config[CONF_ID], paren)
|
||||||
yield sensor.register_sensor(var, config)
|
await sensor.register_sensor(var, config)
|
||||||
yield cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
|
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
|
||||||
cg.add(var.set_gain(config[CONF_GAIN]))
|
cg.add(var.set_gain(config[CONF_GAIN]))
|
||||||
|
|||||||
0
esphome/components/aht10/__init__.py
Normal file
0
esphome/components/aht10/__init__.py
Normal file
128
esphome/components/aht10/aht10.cpp
Normal file
128
esphome/components/aht10/aht10.cpp
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
// Implementation based on:
|
||||||
|
// - AHT10: https://github.com/Thinary/AHT10
|
||||||
|
// - Official Datasheet (cn):
|
||||||
|
// http://www.aosong.com/userfiles/files/media/aht10%E8%A7%84%E6%A0%BC%E4%B9%A6v1_1%EF%BC%8820191015%EF%BC%89.pdf
|
||||||
|
// - Unofficial Translated Datasheet (en):
|
||||||
|
// https://wiki.liutyi.info/download/attachments/30507639/Aosong_AHT10_en_draft_0c.pdf
|
||||||
|
//
|
||||||
|
// When configured for humidity, the log 'Components should block for at most 20-30ms in loop().' will be generated in
|
||||||
|
// verbose mode. This is due to technical specs of the sensor and can not be avoided.
|
||||||
|
//
|
||||||
|
// According to the datasheet, the component is supposed to respond in more than 75ms. In fact, it can answer almost
|
||||||
|
// immediately for temperature. But for humidity, it takes >90ms to get a valid data. From experience, we have best
|
||||||
|
// results making successive requests; the current implementation make 3 attemps with a delay of 30ms each time.
|
||||||
|
|
||||||
|
#include "aht10.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace aht10 {
|
||||||
|
|
||||||
|
static const char *const TAG = "aht10";
|
||||||
|
static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1};
|
||||||
|
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
|
||||||
|
static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement
|
||||||
|
static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms
|
||||||
|
static const uint8_t AHT10_ATTEMPS = 3; // safety margin, normally 3 attemps are enough: 3*30=90ms
|
||||||
|
|
||||||
|
void AHT10Component::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up AHT10...");
|
||||||
|
|
||||||
|
if (!this->write_bytes(0, AHT10_CALIBRATE_CMD, sizeof(AHT10_CALIBRATE_CMD))) {
|
||||||
|
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t data;
|
||||||
|
if (!this->read_byte(0, &data, AHT10_DEFAULT_DELAY)) {
|
||||||
|
ESP_LOGD(TAG, "Communication with AHT10 failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((data & 0x68) != 0x08) { // Bit[6:5] = 0b00, NORMAL mode and Bit[3] = 0b1, CALIBRATED
|
||||||
|
ESP_LOGE(TAG, "AHT10 calibration failed!");
|
||||||
|
this->mark_failed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGV(TAG, "AHT10 calibrated");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AHT10Component::update() {
|
||||||
|
if (!this->write_bytes(0, AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD))) {
|
||||||
|
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint8_t data[6];
|
||||||
|
uint8_t delay = AHT10_DEFAULT_DELAY;
|
||||||
|
if (this->humidity_sensor_ != nullptr)
|
||||||
|
delay = AHT10_HUMIDITY_DELAY;
|
||||||
|
for (int i = 0; i < AHT10_ATTEMPS; ++i) {
|
||||||
|
ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis());
|
||||||
|
delay_microseconds_accurate(4);
|
||||||
|
if (!this->read_bytes(0, data, 6, delay)) {
|
||||||
|
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
||||||
|
} else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
|
||||||
|
ESP_LOGD(TAG, "AHT10 is busy, waiting...");
|
||||||
|
} else if (data[1] == 0x0 && data[2] == 0x0 && (data[3] >> 4) == 0x0) {
|
||||||
|
// Unrealistic humidity (0x0)
|
||||||
|
if (this->humidity_sensor_ == nullptr) {
|
||||||
|
ESP_LOGVV(TAG, "ATH10 Unrealistic humidity (0x0), but humidity is not required");
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "ATH10 Unrealistic humidity (0x0), retrying...");
|
||||||
|
if (!this->write_bytes(0, AHT10_MEASURE_CMD, sizeof(AHT10_MEASURE_CMD))) {
|
||||||
|
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// data is valid, we can break the loop
|
||||||
|
ESP_LOGVV(TAG, "Answer at %6ld", millis());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((data[0] & 0x80) == 0x80) {
|
||||||
|
ESP_LOGE(TAG, "Measurements reading timed-out!");
|
||||||
|
this->status_set_warning();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
float temperature = ((200.0 * (float) raw_temperature) / 1048576.0) - 50.0;
|
||||||
|
float humidity;
|
||||||
|
if (raw_humidity == 0) { // unrealistic value
|
||||||
|
humidity = NAN;
|
||||||
|
} else {
|
||||||
|
humidity = (float) raw_humidity * 100.0 / 1048576.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->temperature_sensor_ != nullptr) {
|
||||||
|
this->temperature_sensor_->publish_state(temperature);
|
||||||
|
}
|
||||||
|
if (this->humidity_sensor_ != nullptr) {
|
||||||
|
if (isnan(humidity))
|
||||||
|
ESP_LOGW(TAG, "Invalid humidity! Sensor reported 0%% Hum");
|
||||||
|
this->humidity_sensor_->publish_state(humidity);
|
||||||
|
}
|
||||||
|
this->status_clear_warning();
|
||||||
|
}
|
||||||
|
|
||||||
|
float AHT10Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
void AHT10Component::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "AHT10:");
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
if (this->is_failed()) {
|
||||||
|
ESP_LOGE(TAG, "Communication with AHT10 failed!");
|
||||||
|
}
|
||||||
|
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace aht10
|
||||||
|
} // namespace esphome
|
||||||
26
esphome/components/aht10/aht10.h
Normal file
26
esphome/components/aht10/aht10.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/i2c/i2c.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace aht10 {
|
||||||
|
|
||||||
|
class AHT10Component : public PollingComponent, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void update() override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override;
|
||||||
|
|
||||||
|
void set_temperature_sensor(sensor::Sensor *temperature_sensor) { temperature_sensor_ = temperature_sensor; }
|
||||||
|
void set_humidity_sensor(sensor::Sensor *humidity_sensor) { humidity_sensor_ = humidity_sensor; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
sensor::Sensor *temperature_sensor_;
|
||||||
|
sensor::Sensor *humidity_sensor_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace aht10
|
||||||
|
} // namespace esphome
|
||||||
57
esphome/components/aht10/sensor.py
Normal file
57
esphome/components/aht10/sensor.py
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import i2c, sensor
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_HUMIDITY,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_HUMIDITY,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
ICON_EMPTY,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
)
|
||||||
|
|
||||||
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
|
aht10_ns = cg.esphome_ns.namespace("aht10")
|
||||||
|
AHT10Component = aht10_ns.class_("AHT10Component", cg.PollingComponent, i2c.I2CDevice)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(AHT10Component),
|
||||||
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
ICON_EMPTY,
|
||||||
|
2,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
|
UNIT_PERCENT,
|
||||||
|
ICON_EMPTY,
|
||||||
|
2,
|
||||||
|
DEVICE_CLASS_HUMIDITY,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x38))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
if CONF_TEMPERATURE in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||||
|
cg.add(var.set_temperature_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_HUMIDITY in config:
|
||||||
|
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
||||||
|
cg.add(var.set_humidity_sensor(sens))
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace am2320 {
|
namespace am2320 {
|
||||||
|
|
||||||
static const char *TAG = "am2320";
|
static const char *const TAG = "am2320";
|
||||||
|
|
||||||
// ---=== Calc CRC16 ===---
|
// ---=== Calc CRC16 ===---
|
||||||
uint16_t crc_16(uint8_t *ptr, uint8_t length) {
|
uint16_t crc_16(uint8_t *ptr, uint8_t length) {
|
||||||
|
|||||||
@@ -1,30 +1,59 @@
|
|||||||
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 i2c, sensor
|
from esphome.components import i2c, sensor
|
||||||
from esphome.const import CONF_HUMIDITY, CONF_ID, CONF_TEMPERATURE, \
|
from esphome.const import (
|
||||||
UNIT_CELSIUS, ICON_THERMOMETER, ICON_WATER_PERCENT, UNIT_PERCENT
|
CONF_HUMIDITY,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_HUMIDITY,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
ICON_EMPTY,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
)
|
||||||
|
|
||||||
DEPENDENCIES = ['i2c']
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
am2320_ns = cg.esphome_ns.namespace('am2320')
|
am2320_ns = cg.esphome_ns.namespace("am2320")
|
||||||
AM2320Component = am2320_ns.class_('AM2320Component', cg.PollingComponent, i2c.I2CDevice)
|
AM2320Component = am2320_ns.class_(
|
||||||
|
"AM2320Component", cg.PollingComponent, i2c.I2CDevice
|
||||||
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = (
|
||||||
cv.GenerateID(): cv.declare_id(AM2320Component),
|
cv.Schema(
|
||||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(UNIT_CELSIUS, ICON_THERMOMETER, 1),
|
{
|
||||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(UNIT_PERCENT, ICON_WATER_PERCENT, 1),
|
cv.GenerateID(): cv.declare_id(AM2320Component),
|
||||||
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x5C))
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
ICON_EMPTY,
|
||||||
|
1,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
|
UNIT_PERCENT,
|
||||||
|
ICON_EMPTY,
|
||||||
|
1,
|
||||||
|
DEVICE_CLASS_HUMIDITY,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x5C))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
yield cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
yield i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
if CONF_TEMPERATURE in config:
|
if CONF_TEMPERATURE in config:
|
||||||
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
|
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||||
cg.add(var.set_temperature_sensor(sens))
|
cg.add(var.set_temperature_sensor(sens))
|
||||||
|
|
||||||
if CONF_HUMIDITY in config:
|
if CONF_HUMIDITY in config:
|
||||||
sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
|
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
||||||
cg.add(var.set_humidity_sensor(sens))
|
cg.add(var.set_humidity_sensor(sens))
|
||||||
|
|||||||
106
esphome/components/animation/__init__.py
Normal file
106
esphome/components/animation/__init__.py
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import logging
|
||||||
|
|
||||||
|
from esphome import core
|
||||||
|
from esphome.components import display, font
|
||||||
|
import esphome.components.image as espImage
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.const import CONF_FILE, CONF_ID, CONF_TYPE, CONF_RESIZE
|
||||||
|
from esphome.core import CORE, HexInt
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DEPENDENCIES = ["display"]
|
||||||
|
MULTI_CONF = True
|
||||||
|
|
||||||
|
Animation_ = display.display_ns.class_("Animation")
|
||||||
|
|
||||||
|
CONF_RAW_DATA_ID = "raw_data_id"
|
||||||
|
|
||||||
|
ANIMATION_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.declare_id(Animation_),
|
||||||
|
cv.Required(CONF_FILE): cv.file_,
|
||||||
|
cv.Optional(CONF_RESIZE): cv.dimensions,
|
||||||
|
cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(
|
||||||
|
espImage.IMAGE_TYPE, upper=True
|
||||||
|
),
|
||||||
|
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@syndlex"]
|
||||||
|
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
path = CORE.relative_config_path(config[CONF_FILE])
|
||||||
|
try:
|
||||||
|
image = Image.open(path)
|
||||||
|
except Exception as e:
|
||||||
|
raise core.EsphomeError(f"Could not load image file {path}: {e}")
|
||||||
|
|
||||||
|
width, height = image.size
|
||||||
|
frames = image.n_frames
|
||||||
|
if CONF_RESIZE in config:
|
||||||
|
image.thumbnail(config[CONF_RESIZE])
|
||||||
|
width, height = image.size
|
||||||
|
else:
|
||||||
|
if width > 500 or height > 500:
|
||||||
|
_LOGGER.warning(
|
||||||
|
"The image you requested is very big. Please consider using"
|
||||||
|
" the resize parameter."
|
||||||
|
)
|
||||||
|
|
||||||
|
if config[CONF_TYPE] == "GRAYSCALE":
|
||||||
|
data = [0 for _ in range(height * width * frames)]
|
||||||
|
pos = 0
|
||||||
|
for frameIndex in range(frames):
|
||||||
|
image.seek(frameIndex)
|
||||||
|
frame = image.convert("L", dither=Image.NONE)
|
||||||
|
pixels = list(frame.getdata())
|
||||||
|
for pix in pixels:
|
||||||
|
data[pos] = pix
|
||||||
|
pos += 1
|
||||||
|
|
||||||
|
elif config[CONF_TYPE] == "RGB24":
|
||||||
|
data = [0 for _ in range(height * width * 3 * frames)]
|
||||||
|
pos = 0
|
||||||
|
for frameIndex in range(frames):
|
||||||
|
image.seek(frameIndex)
|
||||||
|
frame = image.convert("RGB")
|
||||||
|
pixels = list(frame.getdata())
|
||||||
|
for pix in pixels:
|
||||||
|
data[pos] = pix[0]
|
||||||
|
pos += 1
|
||||||
|
data[pos] = pix[1]
|
||||||
|
pos += 1
|
||||||
|
data[pos] = pix[2]
|
||||||
|
pos += 1
|
||||||
|
|
||||||
|
elif config[CONF_TYPE] == "BINARY":
|
||||||
|
width8 = ((width + 7) // 8) * 8
|
||||||
|
data = [0 for _ in range((height * width8 // 8) * frames)]
|
||||||
|
for frameIndex in range(frames):
|
||||||
|
image.seek(frameIndex)
|
||||||
|
frame = image.convert("1", dither=Image.NONE)
|
||||||
|
for y in range(height):
|
||||||
|
for x in range(width):
|
||||||
|
if frame.getpixel((x, y)):
|
||||||
|
continue
|
||||||
|
pos = x + y * width8 + (height * width8 * frameIndex)
|
||||||
|
data[pos // 8] |= 0x80 >> (pos % 8)
|
||||||
|
|
||||||
|
rhs = [HexInt(x) for x in data]
|
||||||
|
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
||||||
|
cg.new_Pvariable(
|
||||||
|
config[CONF_ID],
|
||||||
|
prog_arr,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
frames,
|
||||||
|
espImage.IMAGE_TYPE[config[CONF_TYPE]],
|
||||||
|
)
|
||||||
0
esphome/components/anova/__init__.py
Normal file
0
esphome/components/anova/__init__.py
Normal file
141
esphome/components/anova/anova.cpp
Normal file
141
esphome/components/anova/anova.cpp
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
#include "anova.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace anova {
|
||||||
|
|
||||||
|
static const char *TAG = "anova";
|
||||||
|
|
||||||
|
using namespace esphome::climate;
|
||||||
|
|
||||||
|
void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
|
||||||
|
|
||||||
|
void Anova::setup() {
|
||||||
|
this->codec_ = new AnovaCodec();
|
||||||
|
this->current_request_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Anova::loop() {}
|
||||||
|
|
||||||
|
void Anova::control(const ClimateCall &call) {
|
||||||
|
if (call.get_mode().has_value()) {
|
||||||
|
ClimateMode mode = *call.get_mode();
|
||||||
|
AnovaPacket *pkt;
|
||||||
|
switch (mode) {
|
||||||
|
case climate::CLIMATE_MODE_OFF:
|
||||||
|
pkt = this->codec_->get_stop_request();
|
||||||
|
break;
|
||||||
|
case climate::CLIMATE_MODE_HEAT:
|
||||||
|
pkt = this->codec_->get_start_request();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGW(TAG, "Unsupported mode: %d", mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
if (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()) {
|
||||||
|
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_,
|
||||||
|
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||||
|
if (status)
|
||||||
|
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Anova::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_DISCONNECT_EVT: {
|
||||||
|
this->current_temperature = NAN;
|
||||||
|
this->target_temperature = NAN;
|
||||||
|
this->publish_state();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||||
|
auto chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
|
||||||
|
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] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this->char_handle_ = chr->handle;
|
||||||
|
|
||||||
|
auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle);
|
||||||
|
if (status) {
|
||||||
|
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||||
|
this->node_state = espbt::ClientState::Established;
|
||||||
|
this->current_request_ = 0;
|
||||||
|
this->update();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ESP_GATTC_NOTIFY_EVT: {
|
||||||
|
if (param->notify.handle != this->char_handle_)
|
||||||
|
break;
|
||||||
|
this->codec_->decode(param->notify.value, param->notify.value_len);
|
||||||
|
if (this->codec_->has_target_temp()) {
|
||||||
|
this->target_temperature = this->codec_->target_temp_;
|
||||||
|
}
|
||||||
|
if (this->codec_->has_current_temp()) {
|
||||||
|
this->current_temperature = this->codec_->current_temp_;
|
||||||
|
}
|
||||||
|
if (this->codec_->has_running()) {
|
||||||
|
this->mode = this->codec_->running_ ? climate::CLIMATE_MODE_HEAT : climate::CLIMATE_MODE_OFF;
|
||||||
|
}
|
||||||
|
this->publish_state();
|
||||||
|
|
||||||
|
if (this->current_request_ > 0) {
|
||||||
|
AnovaPacket *pkt = nullptr;
|
||||||
|
switch (this->current_request_++) {
|
||||||
|
case 1:
|
||||||
|
pkt = this->codec_->get_read_target_temp_request();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
pkt = this->codec_->get_read_current_temp_request();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this->current_request_ = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (pkt != nullptr) {
|
||||||
|
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);
|
||||||
|
if (status)
|
||||||
|
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
|
||||||
|
status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Anova::update() {
|
||||||
|
if (this->node_state != espbt::ClientState::Established)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (this->current_request_ == 0) {
|
||||||
|
auto pkt = this->codec_->get_read_device_status_request();
|
||||||
|
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);
|
||||||
|
if (status)
|
||||||
|
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
||||||
|
this->current_request_++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace anova
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
||||||
50
esphome/components/anova/anova.h
Normal file
50
esphome/components/anova/anova.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/ble_client/ble_client.h"
|
||||||
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
|
#include "esphome/components/climate/climate.h"
|
||||||
|
#include "anova_base.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
|
#include <esp_gattc_api.h>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace anova {
|
||||||
|
|
||||||
|
namespace espbt = esphome::esp32_ble_tracker;
|
||||||
|
|
||||||
|
static const uint16_t ANOVA_SERVICE_UUID = 0xFFE0;
|
||||||
|
static const uint16_t ANOVA_CHARACTERISTIC_UUID = 0xFFE1;
|
||||||
|
|
||||||
|
class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode, public PollingComponent {
|
||||||
|
public:
|
||||||
|
void setup() override;
|
||||||
|
void loop() 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 dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
climate::ClimateTraits traits() {
|
||||||
|
auto traits = climate::ClimateTraits();
|
||||||
|
traits.set_supports_current_temperature(true);
|
||||||
|
traits.set_supports_heat_mode(true);
|
||||||
|
traits.set_visual_min_temperature(25.0);
|
||||||
|
traits.set_visual_max_temperature(100.0);
|
||||||
|
traits.set_visual_temperature_step(0.1);
|
||||||
|
return traits;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AnovaCodec *codec_;
|
||||||
|
void control(const climate::ClimateCall &call) override;
|
||||||
|
uint16_t char_handle_;
|
||||||
|
uint8_t current_request_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace anova
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
||||||
119
esphome/components/anova/anova_base.cpp
Normal file
119
esphome/components/anova/anova_base.cpp
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#include "anova_base.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace anova {
|
||||||
|
|
||||||
|
AnovaPacket *AnovaCodec::clean_packet_() {
|
||||||
|
this->packet_.length = strlen((char *) this->packet_.data);
|
||||||
|
this->packet_.data[this->packet_.length] = '\0';
|
||||||
|
ESP_LOGV("anova", "SendPkt: %s\n", this->packet_.data);
|
||||||
|
return &this->packet_;
|
||||||
|
}
|
||||||
|
|
||||||
|
AnovaPacket *AnovaCodec::get_read_device_status_request() {
|
||||||
|
this->current_query_ = READ_DEVICE_STATUS;
|
||||||
|
sprintf((char *) this->packet_.data, "%s", CMD_READ_DEVICE_STATUS);
|
||||||
|
return this->clean_packet_();
|
||||||
|
}
|
||||||
|
|
||||||
|
AnovaPacket *AnovaCodec::get_read_target_temp_request() {
|
||||||
|
this->current_query_ = READ_TARGET_TEMPERATURE;
|
||||||
|
sprintf((char *) this->packet_.data, "%s", CMD_READ_TARGET_TEMP);
|
||||||
|
return this->clean_packet_();
|
||||||
|
}
|
||||||
|
|
||||||
|
AnovaPacket *AnovaCodec::get_read_current_temp_request() {
|
||||||
|
this->current_query_ = READ_CURRENT_TEMPERATURE;
|
||||||
|
sprintf((char *) this->packet_.data, "%s", CMD_READ_CURRENT_TEMP);
|
||||||
|
return this->clean_packet_();
|
||||||
|
}
|
||||||
|
|
||||||
|
AnovaPacket *AnovaCodec::get_read_unit_request() {
|
||||||
|
this->current_query_ = READ_UNIT;
|
||||||
|
sprintf((char *) this->packet_.data, "%s", CMD_READ_UNIT);
|
||||||
|
return this->clean_packet_();
|
||||||
|
}
|
||||||
|
|
||||||
|
AnovaPacket *AnovaCodec::get_read_data_request() {
|
||||||
|
this->current_query_ = READ_DATA;
|
||||||
|
sprintf((char *) this->packet_.data, "%s", CMD_READ_DATA);
|
||||||
|
return this->clean_packet_();
|
||||||
|
}
|
||||||
|
|
||||||
|
AnovaPacket *AnovaCodec::get_set_target_temp_request(float temperature) {
|
||||||
|
this->current_query_ = SET_TARGET_TEMPERATURE;
|
||||||
|
sprintf((char *) this->packet_.data, CMD_SET_TARGET_TEMP, temperature);
|
||||||
|
return this->clean_packet_();
|
||||||
|
}
|
||||||
|
|
||||||
|
AnovaPacket *AnovaCodec::get_set_unit_request(char unit) {
|
||||||
|
this->current_query_ = SET_UNIT;
|
||||||
|
sprintf((char *) this->packet_.data, CMD_SET_TEMP_UNIT, unit);
|
||||||
|
return this->clean_packet_();
|
||||||
|
}
|
||||||
|
|
||||||
|
AnovaPacket *AnovaCodec::get_start_request() {
|
||||||
|
this->current_query_ = START;
|
||||||
|
sprintf((char *) this->packet_.data, CMD_START);
|
||||||
|
return this->clean_packet_();
|
||||||
|
}
|
||||||
|
|
||||||
|
AnovaPacket *AnovaCodec::get_stop_request() {
|
||||||
|
this->current_query_ = STOP;
|
||||||
|
sprintf((char *) this->packet_.data, CMD_STOP);
|
||||||
|
return this->clean_packet_();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
||||||
|
memset(this->buf_, 0, 32);
|
||||||
|
strncpy(this->buf_, (char *) data, length);
|
||||||
|
ESP_LOGV("anova", "Received: %s\n", this->buf_);
|
||||||
|
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
|
||||||
|
switch (this->current_query_) {
|
||||||
|
case READ_DEVICE_STATUS: {
|
||||||
|
if (!strncmp(this->buf_, "stopped", 7)) {
|
||||||
|
this->has_running_ = true;
|
||||||
|
this->running_ = false;
|
||||||
|
}
|
||||||
|
if (!strncmp(this->buf_, "running", 7)) {
|
||||||
|
this->has_running_ = true;
|
||||||
|
this->running_ = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case START: {
|
||||||
|
if (!strncmp(this->buf_, "start", 5)) {
|
||||||
|
this->has_running_ = true;
|
||||||
|
this->running_ = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case STOP: {
|
||||||
|
if (!strncmp(this->buf_, "stop", 4)) {
|
||||||
|
this->has_running_ = true;
|
||||||
|
this->running_ = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case READ_TARGET_TEMPERATURE: {
|
||||||
|
this->target_temp_ = strtof(this->buf_, nullptr);
|
||||||
|
this->has_target_temp_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SET_TARGET_TEMPERATURE: {
|
||||||
|
this->target_temp_ = strtof(this->buf_, nullptr);
|
||||||
|
this->has_target_temp_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case READ_CURRENT_TEMPERATURE: {
|
||||||
|
this->current_temp_ = strtof(this->buf_, nullptr);
|
||||||
|
this->has_current_temp_ = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace anova
|
||||||
|
} // namespace esphome
|
||||||
79
esphome/components/anova/anova_base.h
Normal file
79
esphome/components/anova/anova_base.h
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace anova {
|
||||||
|
|
||||||
|
enum CurrentQuery {
|
||||||
|
NONE,
|
||||||
|
READ_DEVICE_STATUS,
|
||||||
|
READ_TARGET_TEMPERATURE,
|
||||||
|
READ_CURRENT_TEMPERATURE,
|
||||||
|
READ_DATA,
|
||||||
|
READ_UNIT,
|
||||||
|
SET_TARGET_TEMPERATURE,
|
||||||
|
SET_UNIT,
|
||||||
|
START,
|
||||||
|
STOP,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AnovaPacket {
|
||||||
|
uint16_t length;
|
||||||
|
uint8_t data[24];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CMD_READ_DEVICE_STATUS "status\r"
|
||||||
|
#define CMD_READ_TARGET_TEMP "read set temp\r"
|
||||||
|
#define CMD_READ_CURRENT_TEMP "read temp\r"
|
||||||
|
#define CMD_READ_UNIT "read unit\r"
|
||||||
|
#define CMD_READ_DATA "read data\r"
|
||||||
|
#define CMD_SET_TARGET_TEMP "set temp %.1f\r"
|
||||||
|
#define CMD_SET_TEMP_UNIT "set unit %c\r"
|
||||||
|
|
||||||
|
#define CMD_START "start\r"
|
||||||
|
#define CMD_STOP "stop\r"
|
||||||
|
|
||||||
|
class AnovaCodec {
|
||||||
|
public:
|
||||||
|
AnovaPacket *get_read_device_status_request();
|
||||||
|
AnovaPacket *get_read_target_temp_request();
|
||||||
|
AnovaPacket *get_read_current_temp_request();
|
||||||
|
AnovaPacket *get_read_data_request();
|
||||||
|
AnovaPacket *get_read_unit_request();
|
||||||
|
|
||||||
|
AnovaPacket *get_set_target_temp_request(float temperature);
|
||||||
|
AnovaPacket *get_set_unit_request(char unit);
|
||||||
|
|
||||||
|
AnovaPacket *get_start_request();
|
||||||
|
AnovaPacket *get_stop_request();
|
||||||
|
|
||||||
|
void decode(const uint8_t *data, uint16_t length);
|
||||||
|
bool has_target_temp() { return this->has_target_temp_; }
|
||||||
|
bool has_current_temp() { return this->has_current_temp_; }
|
||||||
|
bool has_unit() { return this->has_unit_; }
|
||||||
|
bool has_running() { return this->has_running_; }
|
||||||
|
|
||||||
|
union {
|
||||||
|
float target_temp_;
|
||||||
|
float current_temp_;
|
||||||
|
char unit_;
|
||||||
|
bool running_;
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AnovaPacket *clean_packet_();
|
||||||
|
AnovaPacket packet_;
|
||||||
|
|
||||||
|
bool has_target_temp_;
|
||||||
|
bool has_current_temp_;
|
||||||
|
bool has_unit_;
|
||||||
|
bool has_running_;
|
||||||
|
char buf_[32];
|
||||||
|
|
||||||
|
CurrentQuery current_query_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace anova
|
||||||
|
} // namespace esphome
|
||||||
25
esphome/components/anova/climate.py
Normal file
25
esphome/components/anova/climate.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import climate, ble_client
|
||||||
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
CODEOWNERS = ["@buxtronix"]
|
||||||
|
DEPENDENCIES = ["ble_client"]
|
||||||
|
|
||||||
|
anova_ns = cg.esphome_ns.namespace("anova")
|
||||||
|
Anova = anova_ns.class_(
|
||||||
|
"Anova", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
climate.CLIMATE_SCHEMA.extend({cv.GenerateID(): cv.declare_id(Anova)})
|
||||||
|
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield climate.register_climate(var, config)
|
||||||
|
yield ble_client.register_ble_node(var, config)
|
||||||
@@ -3,21 +3,27 @@ import esphome.config_validation as cv
|
|||||||
from esphome.components import i2c
|
from esphome.components import i2c
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
DEPENDENCIES = ['i2c']
|
DEPENDENCIES = ["i2c"]
|
||||||
AUTO_LOAD = ['sensor', 'binary_sensor']
|
AUTO_LOAD = ["sensor", "binary_sensor"]
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
|
||||||
CONF_APDS9960_ID = 'apds9960_id'
|
CONF_APDS9960_ID = "apds9960_id"
|
||||||
|
|
||||||
apds9960_nds = cg.esphome_ns.namespace('apds9960')
|
apds9960_nds = cg.esphome_ns.namespace("apds9960")
|
||||||
APDS9960 = apds9960_nds.class_('APDS9960', cg.PollingComponent, i2c.I2CDevice)
|
APDS9960 = apds9960_nds.class_("APDS9960", cg.PollingComponent, i2c.I2CDevice)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = (
|
||||||
cv.GenerateID(): cv.declare_id(APDS9960),
|
cv.Schema(
|
||||||
}).extend(cv.polling_component_schema('60s')).extend(i2c.i2c_device_schema(0x39))
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(APDS9960),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x39))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
yield cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
yield i2c.register_i2c_device(var, config)
|
await i2c.register_i2c_device(var, config)
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace apds9960 {
|
namespace apds9960 {
|
||||||
|
|
||||||
static const char *TAG = "apds9960";
|
static const char *const TAG = "apds9960";
|
||||||
|
|
||||||
#define APDS9960_ERROR_CHECK(func) \
|
#define APDS9960_ERROR_CHECK(func) \
|
||||||
if (!func) { \
|
if (!(func)) { \
|
||||||
this->mark_failed(); \
|
this->mark_failed(); \
|
||||||
return; \
|
return; \
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,24 +4,28 @@ from esphome.components import binary_sensor
|
|||||||
from esphome.const import CONF_DIRECTION, CONF_DEVICE_CLASS, DEVICE_CLASS_MOVING
|
from esphome.const import CONF_DIRECTION, CONF_DEVICE_CLASS, DEVICE_CLASS_MOVING
|
||||||
from . import APDS9960, CONF_APDS9960_ID
|
from . import APDS9960, CONF_APDS9960_ID
|
||||||
|
|
||||||
DEPENDENCIES = ['apds9960']
|
DEPENDENCIES = ["apds9960"]
|
||||||
|
|
||||||
DIRECTIONS = {
|
DIRECTIONS = {
|
||||||
'UP': 'set_up_direction',
|
"UP": "set_up_direction",
|
||||||
'DOWN': 'set_down_direction',
|
"DOWN": "set_down_direction",
|
||||||
'LEFT': 'set_left_direction',
|
"LEFT": "set_left_direction",
|
||||||
'RIGHT': 'set_right_direction',
|
"RIGHT": "set_right_direction",
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
|
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||||
cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
|
{
|
||||||
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
|
cv.Required(CONF_DIRECTION): cv.one_of(*DIRECTIONS, upper=True),
|
||||||
cv.Optional(CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING): binary_sensor.device_class,
|
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
|
||||||
})
|
cv.Optional(
|
||||||
|
CONF_DEVICE_CLASS, default=DEVICE_CLASS_MOVING
|
||||||
|
): binary_sensor.device_class,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
hub = yield cg.get_variable(config[CONF_APDS9960_ID])
|
hub = await cg.get_variable(config[CONF_APDS9960_ID])
|
||||||
var = yield binary_sensor.new_binary_sensor(config)
|
var = await binary_sensor.new_binary_sensor(config)
|
||||||
func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]])
|
func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]])
|
||||||
cg.add(func(var))
|
cg.add(func(var))
|
||||||
|
|||||||
@@ -1,27 +1,37 @@
|
|||||||
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 sensor
|
from esphome.components import sensor
|
||||||
from esphome.const import CONF_TYPE, UNIT_PERCENT, ICON_LIGHTBULB
|
from esphome.const import (
|
||||||
|
CONF_TYPE,
|
||||||
|
DEVICE_CLASS_EMPTY,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
ICON_LIGHTBULB,
|
||||||
|
)
|
||||||
from . import APDS9960, CONF_APDS9960_ID
|
from . import APDS9960, CONF_APDS9960_ID
|
||||||
|
|
||||||
DEPENDENCIES = ['apds9960']
|
DEPENDENCIES = ["apds9960"]
|
||||||
|
|
||||||
TYPES = {
|
TYPES = {
|
||||||
'CLEAR': 'set_clear_channel',
|
"CLEAR": "set_clear_channel",
|
||||||
'RED': 'set_red_channel',
|
"RED": "set_red_channel",
|
||||||
'GREEN': 'set_green_channel',
|
"GREEN": "set_green_channel",
|
||||||
'BLUE': 'set_blue_channel',
|
"BLUE": "set_blue_channel",
|
||||||
'PROXIMITY': 'set_proximity',
|
"PROXIMITY": "set_proximity",
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_SCHEMA = sensor.sensor_schema(UNIT_PERCENT, ICON_LIGHTBULB, 1).extend({
|
CONFIG_SCHEMA = sensor.sensor_schema(
|
||||||
cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
|
UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY, STATE_CLASS_MEASUREMENT
|
||||||
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
|
).extend(
|
||||||
})
|
{
|
||||||
|
cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
|
||||||
|
cv.GenerateID(CONF_APDS9960_ID): cv.use_id(APDS9960),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
hub = yield cg.get_variable(config[CONF_APDS9960_ID])
|
hub = await cg.get_variable(config[CONF_APDS9960_ID])
|
||||||
var = yield sensor.new_sensor(config)
|
var = await sensor.new_sensor(config)
|
||||||
func = getattr(hub, TYPES[config[CONF_TYPE]])
|
func = getattr(hub, TYPES[config[CONF_TYPE]])
|
||||||
cg.add(func(var))
|
cg.add(func(var))
|
||||||
|
|||||||
@@ -2,50 +2,75 @@ import esphome.codegen as cg
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
from esphome.automation import Condition
|
from esphome.automation import Condition
|
||||||
from esphome.const import CONF_DATA, CONF_DATA_TEMPLATE, CONF_ID, CONF_PASSWORD, CONF_PORT, \
|
from esphome.const import (
|
||||||
CONF_REBOOT_TIMEOUT, CONF_SERVICE, CONF_VARIABLES, CONF_SERVICES, CONF_TRIGGER_ID, CONF_EVENT
|
CONF_DATA,
|
||||||
|
CONF_DATA_TEMPLATE,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_PASSWORD,
|
||||||
|
CONF_PORT,
|
||||||
|
CONF_REBOOT_TIMEOUT,
|
||||||
|
CONF_SERVICE,
|
||||||
|
CONF_VARIABLES,
|
||||||
|
CONF_SERVICES,
|
||||||
|
CONF_TRIGGER_ID,
|
||||||
|
CONF_EVENT,
|
||||||
|
CONF_TAG,
|
||||||
|
)
|
||||||
from esphome.core import coroutine_with_priority
|
from esphome.core import coroutine_with_priority
|
||||||
|
|
||||||
DEPENDENCIES = ['network']
|
DEPENDENCIES = ["network"]
|
||||||
AUTO_LOAD = ['async_tcp']
|
AUTO_LOAD = ["async_tcp"]
|
||||||
|
CODEOWNERS = ["@OttoWinter"]
|
||||||
|
|
||||||
api_ns = cg.esphome_ns.namespace('api')
|
api_ns = cg.esphome_ns.namespace("api")
|
||||||
APIServer = api_ns.class_('APIServer', cg.Component, cg.Controller)
|
APIServer = api_ns.class_("APIServer", cg.Component, cg.Controller)
|
||||||
HomeAssistantServiceCallAction = api_ns.class_('HomeAssistantServiceCallAction', automation.Action)
|
HomeAssistantServiceCallAction = api_ns.class_(
|
||||||
APIConnectedCondition = api_ns.class_('APIConnectedCondition', Condition)
|
"HomeAssistantServiceCallAction", automation.Action
|
||||||
|
)
|
||||||
|
APIConnectedCondition = api_ns.class_("APIConnectedCondition", Condition)
|
||||||
|
|
||||||
UserServiceTrigger = api_ns.class_('UserServiceTrigger', automation.Trigger)
|
UserServiceTrigger = api_ns.class_("UserServiceTrigger", automation.Trigger)
|
||||||
ListEntitiesServicesArgument = api_ns.class_('ListEntitiesServicesArgument')
|
ListEntitiesServicesArgument = api_ns.class_("ListEntitiesServicesArgument")
|
||||||
SERVICE_ARG_NATIVE_TYPES = {
|
SERVICE_ARG_NATIVE_TYPES = {
|
||||||
'bool': bool,
|
"bool": bool,
|
||||||
'int': cg.int32,
|
"int": cg.int32,
|
||||||
'float': float,
|
"float": float,
|
||||||
'string': cg.std_string,
|
"string": cg.std_string,
|
||||||
'bool[]': cg.std_vector.template(bool),
|
"bool[]": cg.std_vector.template(bool),
|
||||||
'int[]': cg.std_vector.template(cg.int32),
|
"int[]": cg.std_vector.template(cg.int32),
|
||||||
'float[]': cg.std_vector.template(float),
|
"float[]": cg.std_vector.template(float),
|
||||||
'string[]': cg.std_vector.template(cg.std_string),
|
"string[]": cg.std_vector.template(cg.std_string),
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
cv.GenerateID(): cv.declare_id(APIServer),
|
{
|
||||||
cv.Optional(CONF_PORT, default=6053): cv.port,
|
cv.GenerateID(): cv.declare_id(APIServer),
|
||||||
cv.Optional(CONF_PASSWORD, default=''): cv.string_strict,
|
cv.Optional(CONF_PORT, default=6053): cv.port,
|
||||||
cv.Optional(CONF_REBOOT_TIMEOUT, default='15min'): cv.positive_time_period_milliseconds,
|
cv.Optional(CONF_PASSWORD, default=""): cv.string_strict,
|
||||||
cv.Optional(CONF_SERVICES): automation.validate_automation({
|
cv.Optional(
|
||||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger),
|
CONF_REBOOT_TIMEOUT, default="15min"
|
||||||
cv.Required(CONF_SERVICE): cv.valid_name,
|
): cv.positive_time_period_milliseconds,
|
||||||
cv.Optional(CONF_VARIABLES, default={}): cv.Schema({
|
cv.Optional(CONF_SERVICES): automation.validate_automation(
|
||||||
cv.validate_id_name: cv.one_of(*SERVICE_ARG_NATIVE_TYPES, lower=True),
|
{
|
||||||
}),
|
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(UserServiceTrigger),
|
||||||
}),
|
cv.Required(CONF_SERVICE): cv.valid_name,
|
||||||
}).extend(cv.COMPONENT_SCHEMA)
|
cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
|
||||||
|
{
|
||||||
|
cv.validate_id_name: cv.one_of(
|
||||||
|
*SERVICE_ARG_NATIVE_TYPES, lower=True
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(40.0)
|
@coroutine_with_priority(40.0)
|
||||||
def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
yield cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
|
|
||||||
cg.add(var.set_port(config[CONF_PORT]))
|
cg.add(var.set_port(config[CONF_PORT]))
|
||||||
cg.add(var.set_password(config[CONF_PASSWORD]))
|
cg.add(var.set_password(config[CONF_PASSWORD]))
|
||||||
@@ -61,81 +86,119 @@ def to_code(config):
|
|||||||
func_args.append((native, name))
|
func_args.append((native, name))
|
||||||
service_arg_names.append(name)
|
service_arg_names.append(name)
|
||||||
templ = cg.TemplateArguments(*template_args)
|
templ = cg.TemplateArguments(*template_args)
|
||||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], templ,
|
trigger = cg.new_Pvariable(
|
||||||
conf[CONF_SERVICE], service_arg_names)
|
conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names
|
||||||
|
)
|
||||||
cg.add(var.register_user_service(trigger))
|
cg.add(var.register_user_service(trigger))
|
||||||
yield automation.build_automation(trigger, func_args, conf)
|
await automation.build_automation(trigger, func_args, conf)
|
||||||
|
|
||||||
cg.add_define('USE_API')
|
cg.add_define("USE_API")
|
||||||
cg.add_global(api_ns.using)
|
cg.add_global(api_ns.using)
|
||||||
|
|
||||||
|
|
||||||
KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string)})
|
KEY_VALUE_SCHEMA = cv.Schema({cv.string: cv.templatable(cv.string_strict)})
|
||||||
|
|
||||||
HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema({
|
HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema(
|
||||||
cv.GenerateID(): cv.use_id(APIServer),
|
{
|
||||||
cv.Required(CONF_SERVICE): cv.templatable(cv.string),
|
cv.GenerateID(): cv.use_id(APIServer),
|
||||||
cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
|
cv.Required(CONF_SERVICE): cv.templatable(cv.string),
|
||||||
cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
|
cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
|
||||||
cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA,
|
cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
|
||||||
})
|
cv.Optional(CONF_VARIABLES, default={}): cv.Schema(
|
||||||
|
{cv.string: cv.returning_lambda}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action('homeassistant.service', HomeAssistantServiceCallAction,
|
@automation.register_action(
|
||||||
HOMEASSISTANT_SERVICE_ACTION_SCHEMA)
|
"homeassistant.service",
|
||||||
def homeassistant_service_to_code(config, action_id, template_arg, args):
|
HomeAssistantServiceCallAction,
|
||||||
serv = yield cg.get_variable(config[CONF_ID])
|
HOMEASSISTANT_SERVICE_ACTION_SCHEMA,
|
||||||
|
)
|
||||||
|
async def homeassistant_service_to_code(config, action_id, template_arg, args):
|
||||||
|
serv = await cg.get_variable(config[CONF_ID])
|
||||||
var = cg.new_Pvariable(action_id, template_arg, serv, False)
|
var = cg.new_Pvariable(action_id, template_arg, serv, False)
|
||||||
templ = yield cg.templatable(config[CONF_SERVICE], args, None)
|
templ = await cg.templatable(config[CONF_SERVICE], args, None)
|
||||||
cg.add(var.set_service(templ))
|
cg.add(var.set_service(templ))
|
||||||
for key, value in config[CONF_DATA].items():
|
for key, value in config[CONF_DATA].items():
|
||||||
templ = yield cg.templatable(value, args, None)
|
templ = await cg.templatable(value, args, None)
|
||||||
cg.add(var.add_data(key, templ))
|
cg.add(var.add_data(key, templ))
|
||||||
for key, value in config[CONF_DATA_TEMPLATE].items():
|
for key, value in config[CONF_DATA_TEMPLATE].items():
|
||||||
templ = yield cg.templatable(value, args, None)
|
templ = await cg.templatable(value, args, None)
|
||||||
cg.add(var.add_data_template(key, templ))
|
cg.add(var.add_data_template(key, templ))
|
||||||
for key, value in config[CONF_VARIABLES].items():
|
for key, value in config[CONF_VARIABLES].items():
|
||||||
templ = yield cg.templatable(value, args, None)
|
templ = await cg.templatable(value, args, None)
|
||||||
cg.add(var.add_variable(key, templ))
|
cg.add(var.add_variable(key, templ))
|
||||||
yield var
|
return var
|
||||||
|
|
||||||
|
|
||||||
def validate_homeassistant_event(value):
|
def validate_homeassistant_event(value):
|
||||||
value = cv.string(value)
|
value = cv.string(value)
|
||||||
if not value.startswith(u'esphome.'):
|
if not value.startswith("esphome."):
|
||||||
raise cv.Invalid("ESPHome can only generate Home Assistant events that begin with "
|
raise cv.Invalid(
|
||||||
"esphome. For example 'esphome.xyz'")
|
"ESPHome can only generate Home Assistant events that begin with "
|
||||||
|
"esphome. For example 'esphome.xyz'"
|
||||||
|
)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema({
|
HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema(
|
||||||
cv.GenerateID(): cv.use_id(APIServer),
|
{
|
||||||
cv.Required(CONF_EVENT): validate_homeassistant_event,
|
cv.GenerateID(): cv.use_id(APIServer),
|
||||||
cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
|
cv.Required(CONF_EVENT): validate_homeassistant_event,
|
||||||
cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
|
cv.Optional(CONF_DATA, default={}): KEY_VALUE_SCHEMA,
|
||||||
cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA,
|
cv.Optional(CONF_DATA_TEMPLATE, default={}): KEY_VALUE_SCHEMA,
|
||||||
})
|
cv.Optional(CONF_VARIABLES, default={}): KEY_VALUE_SCHEMA,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action('homeassistant.event', HomeAssistantServiceCallAction,
|
@automation.register_action(
|
||||||
HOMEASSISTANT_EVENT_ACTION_SCHEMA)
|
"homeassistant.event",
|
||||||
def homeassistant_event_to_code(config, action_id, template_arg, args):
|
HomeAssistantServiceCallAction,
|
||||||
serv = yield cg.get_variable(config[CONF_ID])
|
HOMEASSISTANT_EVENT_ACTION_SCHEMA,
|
||||||
|
)
|
||||||
|
async def homeassistant_event_to_code(config, action_id, template_arg, args):
|
||||||
|
serv = await cg.get_variable(config[CONF_ID])
|
||||||
var = cg.new_Pvariable(action_id, template_arg, serv, True)
|
var = cg.new_Pvariable(action_id, template_arg, serv, True)
|
||||||
templ = yield cg.templatable(config[CONF_EVENT], args, None)
|
templ = await cg.templatable(config[CONF_EVENT], args, None)
|
||||||
cg.add(var.set_service(templ))
|
cg.add(var.set_service(templ))
|
||||||
for key, value in config[CONF_DATA].items():
|
for key, value in config[CONF_DATA].items():
|
||||||
templ = yield cg.templatable(value, args, None)
|
templ = await cg.templatable(value, args, None)
|
||||||
cg.add(var.add_data(key, templ))
|
cg.add(var.add_data(key, templ))
|
||||||
for key, value in config[CONF_DATA_TEMPLATE].items():
|
for key, value in config[CONF_DATA_TEMPLATE].items():
|
||||||
templ = yield cg.templatable(value, args, None)
|
templ = await cg.templatable(value, args, None)
|
||||||
cg.add(var.add_data_template(key, templ))
|
cg.add(var.add_data_template(key, templ))
|
||||||
for key, value in config[CONF_VARIABLES].items():
|
for key, value in config[CONF_VARIABLES].items():
|
||||||
templ = yield cg.templatable(value, args, None)
|
templ = await cg.templatable(value, args, None)
|
||||||
cg.add(var.add_variable(key, templ))
|
cg.add(var.add_variable(key, templ))
|
||||||
yield var
|
return var
|
||||||
|
|
||||||
|
|
||||||
@automation.register_condition('api.connected', APIConnectedCondition, {})
|
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||||
def api_connected_to_code(config, condition_id, template_arg, args):
|
{
|
||||||
yield cg.new_Pvariable(condition_id, template_arg)
|
cv.GenerateID(): cv.use_id(APIServer),
|
||||||
|
cv.Required(CONF_TAG): cv.templatable(cv.string_strict),
|
||||||
|
},
|
||||||
|
key=CONF_TAG,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_action(
|
||||||
|
"homeassistant.tag_scanned",
|
||||||
|
HomeAssistantServiceCallAction,
|
||||||
|
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA,
|
||||||
|
)
|
||||||
|
async def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
|
||||||
|
serv = await cg.get_variable(config[CONF_ID])
|
||||||
|
var = cg.new_Pvariable(action_id, template_arg, serv, True)
|
||||||
|
cg.add(var.set_service("esphome.tag_scanned"))
|
||||||
|
templ = await cg.templatable(config[CONF_TAG], args, cg.std_string)
|
||||||
|
cg.add(var.add_data("tag_id", templ))
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_condition("api.connected", APIConnectedCondition, {})
|
||||||
|
async def api_connected_to_code(config, condition_id, template_arg, args):
|
||||||
|
return cg.new_Pvariable(condition_id, template_arg)
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ service APIConnection {
|
|||||||
rpc switch_command (SwitchCommandRequest) returns (void) {}
|
rpc switch_command (SwitchCommandRequest) returns (void) {}
|
||||||
rpc camera_image (CameraImageRequest) returns (void) {}
|
rpc camera_image (CameraImageRequest) returns (void) {}
|
||||||
rpc climate_command (ClimateCommandRequest) returns (void) {}
|
rpc climate_command (ClimateCommandRequest) returns (void) {}
|
||||||
|
rpc number_command (NumberCommandRequest) returns (void) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ service APIConnection {
|
|||||||
// The Home Assistant protocol is structured as a simple
|
// The Home Assistant protocol is structured as a simple
|
||||||
// TCP socket with short binary messages encoded in the protocol buffers format
|
// TCP socket with short binary messages encoded in the protocol buffers format
|
||||||
// First, a message in this protocol has a specific format:
|
// First, a message in this protocol has a specific format:
|
||||||
|
// * A zero byte.
|
||||||
// * VarInt denoting the size of the message object. (type is not part of this)
|
// * VarInt denoting the size of the message object. (type is not part of this)
|
||||||
// * VarInt denoting the type of message.
|
// * VarInt denoting the type of message.
|
||||||
// * The message object encoded as a ProtoBuf message
|
// * The message object encoded as a ProtoBuf message
|
||||||
@@ -175,6 +177,10 @@ message DeviceInfoResponse {
|
|||||||
string model = 6;
|
string model = 6;
|
||||||
|
|
||||||
bool has_deep_sleep = 7;
|
bool has_deep_sleep = 7;
|
||||||
|
|
||||||
|
// The esphome project details if set
|
||||||
|
string project_name = 8;
|
||||||
|
string project_version = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListEntitiesRequest {
|
message ListEntitiesRequest {
|
||||||
@@ -216,6 +222,9 @@ message BinarySensorStateResponse {
|
|||||||
|
|
||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
bool state = 2;
|
bool state = 2;
|
||||||
|
// If the binary sensor does not have a valid state yet.
|
||||||
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
|
bool missing_state = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== COVER ====================
|
// ==================== COVER ====================
|
||||||
@@ -298,12 +307,18 @@ message ListEntitiesFanResponse {
|
|||||||
|
|
||||||
bool supports_oscillation = 5;
|
bool supports_oscillation = 5;
|
||||||
bool supports_speed = 6;
|
bool supports_speed = 6;
|
||||||
|
bool supports_direction = 7;
|
||||||
|
int32 supported_speed_count = 8;
|
||||||
}
|
}
|
||||||
enum FanSpeed {
|
enum FanSpeed {
|
||||||
FAN_SPEED_LOW = 0;
|
FAN_SPEED_LOW = 0;
|
||||||
FAN_SPEED_MEDIUM = 1;
|
FAN_SPEED_MEDIUM = 1;
|
||||||
FAN_SPEED_HIGH = 2;
|
FAN_SPEED_HIGH = 2;
|
||||||
}
|
}
|
||||||
|
enum FanDirection {
|
||||||
|
FAN_DIRECTION_FORWARD = 0;
|
||||||
|
FAN_DIRECTION_REVERSE = 1;
|
||||||
|
}
|
||||||
message FanStateResponse {
|
message FanStateResponse {
|
||||||
option (id) = 23;
|
option (id) = 23;
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
@@ -313,7 +328,9 @@ message FanStateResponse {
|
|||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
bool state = 2;
|
bool state = 2;
|
||||||
bool oscillating = 3;
|
bool oscillating = 3;
|
||||||
FanSpeed speed = 4;
|
FanSpeed speed = 4 [deprecated = true];
|
||||||
|
FanDirection direction = 5;
|
||||||
|
int32 speed_level = 6;
|
||||||
}
|
}
|
||||||
message FanCommandRequest {
|
message FanCommandRequest {
|
||||||
option (id) = 31;
|
option (id) = 31;
|
||||||
@@ -324,10 +341,14 @@ message FanCommandRequest {
|
|||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
bool has_state = 2;
|
bool has_state = 2;
|
||||||
bool state = 3;
|
bool state = 3;
|
||||||
bool has_speed = 4;
|
bool has_speed = 4 [deprecated = true];
|
||||||
FanSpeed speed = 5;
|
FanSpeed speed = 5 [deprecated = true];
|
||||||
bool has_oscillating = 6;
|
bool has_oscillating = 6;
|
||||||
bool oscillating = 7;
|
bool oscillating = 7;
|
||||||
|
bool has_direction = 8;
|
||||||
|
FanDirection direction = 9;
|
||||||
|
bool has_speed_level = 10;
|
||||||
|
int32 speed_level = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== LIGHT ====================
|
// ==================== LIGHT ====================
|
||||||
@@ -358,6 +379,7 @@ message LightStateResponse {
|
|||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
bool state = 2;
|
bool state = 2;
|
||||||
float brightness = 3;
|
float brightness = 3;
|
||||||
|
float color_brightness = 10;
|
||||||
float red = 4;
|
float red = 4;
|
||||||
float green = 5;
|
float green = 5;
|
||||||
float blue = 6;
|
float blue = 6;
|
||||||
@@ -376,6 +398,8 @@ message LightCommandRequest {
|
|||||||
bool state = 3;
|
bool state = 3;
|
||||||
bool has_brightness = 4;
|
bool has_brightness = 4;
|
||||||
float brightness = 5;
|
float brightness = 5;
|
||||||
|
bool has_color_brightness = 20;
|
||||||
|
float color_brightness = 21;
|
||||||
bool has_rgb = 6;
|
bool has_rgb = 6;
|
||||||
float red = 7;
|
float red = 7;
|
||||||
float green = 8;
|
float green = 8;
|
||||||
@@ -393,6 +417,17 @@ message LightCommandRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ==================== SENSOR ====================
|
// ==================== SENSOR ====================
|
||||||
|
enum SensorStateClass {
|
||||||
|
STATE_CLASS_NONE = 0;
|
||||||
|
STATE_CLASS_MEASUREMENT = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SensorLastResetType {
|
||||||
|
LAST_RESET_NONE = 0;
|
||||||
|
LAST_RESET_NEVER = 1;
|
||||||
|
LAST_RESET_AUTO = 2;
|
||||||
|
}
|
||||||
|
|
||||||
message ListEntitiesSensorResponse {
|
message ListEntitiesSensorResponse {
|
||||||
option (id) = 16;
|
option (id) = 16;
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
@@ -407,6 +442,9 @@ message ListEntitiesSensorResponse {
|
|||||||
string unit_of_measurement = 6;
|
string unit_of_measurement = 6;
|
||||||
int32 accuracy_decimals = 7;
|
int32 accuracy_decimals = 7;
|
||||||
bool force_update = 8;
|
bool force_update = 8;
|
||||||
|
string device_class = 9;
|
||||||
|
SensorStateClass state_class = 10;
|
||||||
|
SensorLastResetType last_reset_type = 11;
|
||||||
}
|
}
|
||||||
message SensorStateResponse {
|
message SensorStateResponse {
|
||||||
option (id) = 25;
|
option (id) = 25;
|
||||||
@@ -416,6 +454,9 @@ message SensorStateResponse {
|
|||||||
|
|
||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
float state = 2;
|
float state = 2;
|
||||||
|
// If the sensor does not have a valid state yet.
|
||||||
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
|
bool missing_state = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== SWITCH ====================
|
// ==================== SWITCH ====================
|
||||||
@@ -472,6 +513,9 @@ message TextSensorStateResponse {
|
|||||||
|
|
||||||
fixed32 key = 1;
|
fixed32 key = 1;
|
||||||
string state = 2;
|
string state = 2;
|
||||||
|
// If the text sensor does not have a valid state yet.
|
||||||
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
|
bool missing_state = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== SUBSCRIBE LOGS ====================
|
// ==================== SUBSCRIBE LOGS ====================
|
||||||
@@ -538,6 +582,7 @@ message SubscribeHomeAssistantStateResponse {
|
|||||||
option (id) = 39;
|
option (id) = 39;
|
||||||
option (source) = SOURCE_SERVER;
|
option (source) = SOURCE_SERVER;
|
||||||
string entity_id = 1;
|
string entity_id = 1;
|
||||||
|
string attribute = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message HomeAssistantStateResponse {
|
message HomeAssistantStateResponse {
|
||||||
@@ -547,6 +592,7 @@ message HomeAssistantStateResponse {
|
|||||||
|
|
||||||
string entity_id = 1;
|
string entity_id = 1;
|
||||||
string state = 2;
|
string state = 2;
|
||||||
|
string attribute = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== IMPORT TIME ====================
|
// ==================== IMPORT TIME ====================
|
||||||
@@ -641,15 +687,48 @@ message CameraImageRequest {
|
|||||||
// ==================== CLIMATE ====================
|
// ==================== CLIMATE ====================
|
||||||
enum ClimateMode {
|
enum ClimateMode {
|
||||||
CLIMATE_MODE_OFF = 0;
|
CLIMATE_MODE_OFF = 0;
|
||||||
CLIMATE_MODE_AUTO = 1;
|
CLIMATE_MODE_HEAT_COOL = 1;
|
||||||
CLIMATE_MODE_COOL = 2;
|
CLIMATE_MODE_COOL = 2;
|
||||||
CLIMATE_MODE_HEAT = 3;
|
CLIMATE_MODE_HEAT = 3;
|
||||||
|
CLIMATE_MODE_FAN_ONLY = 4;
|
||||||
|
CLIMATE_MODE_DRY = 5;
|
||||||
|
CLIMATE_MODE_AUTO = 6;
|
||||||
|
}
|
||||||
|
enum ClimateFanMode {
|
||||||
|
CLIMATE_FAN_ON = 0;
|
||||||
|
CLIMATE_FAN_OFF = 1;
|
||||||
|
CLIMATE_FAN_AUTO = 2;
|
||||||
|
CLIMATE_FAN_LOW = 3;
|
||||||
|
CLIMATE_FAN_MEDIUM = 4;
|
||||||
|
CLIMATE_FAN_HIGH = 5;
|
||||||
|
CLIMATE_FAN_MIDDLE = 6;
|
||||||
|
CLIMATE_FAN_FOCUS = 7;
|
||||||
|
CLIMATE_FAN_DIFFUSE = 8;
|
||||||
|
}
|
||||||
|
enum ClimateSwingMode {
|
||||||
|
CLIMATE_SWING_OFF = 0;
|
||||||
|
CLIMATE_SWING_BOTH = 1;
|
||||||
|
CLIMATE_SWING_VERTICAL = 2;
|
||||||
|
CLIMATE_SWING_HORIZONTAL = 3;
|
||||||
}
|
}
|
||||||
enum ClimateAction {
|
enum ClimateAction {
|
||||||
CLIMATE_ACTION_OFF = 0;
|
CLIMATE_ACTION_OFF = 0;
|
||||||
// values same as mode for readability
|
// values same as mode for readability
|
||||||
CLIMATE_ACTION_COOLING = 2;
|
CLIMATE_ACTION_COOLING = 2;
|
||||||
CLIMATE_ACTION_HEATING = 3;
|
CLIMATE_ACTION_HEATING = 3;
|
||||||
|
CLIMATE_ACTION_IDLE = 4;
|
||||||
|
CLIMATE_ACTION_DRYING = 5;
|
||||||
|
CLIMATE_ACTION_FAN = 6;
|
||||||
|
}
|
||||||
|
enum ClimatePreset {
|
||||||
|
CLIMATE_PRESET_NONE = 0;
|
||||||
|
CLIMATE_PRESET_HOME = 1;
|
||||||
|
CLIMATE_PRESET_AWAY = 2;
|
||||||
|
CLIMATE_PRESET_BOOST = 3;
|
||||||
|
CLIMATE_PRESET_COMFORT = 4;
|
||||||
|
CLIMATE_PRESET_ECO = 5;
|
||||||
|
CLIMATE_PRESET_SLEEP = 6;
|
||||||
|
CLIMATE_PRESET_ACTIVITY = 7;
|
||||||
}
|
}
|
||||||
message ListEntitiesClimateResponse {
|
message ListEntitiesClimateResponse {
|
||||||
option (id) = 46;
|
option (id) = 46;
|
||||||
@@ -667,8 +746,15 @@ message ListEntitiesClimateResponse {
|
|||||||
float visual_min_temperature = 8;
|
float visual_min_temperature = 8;
|
||||||
float visual_max_temperature = 9;
|
float visual_max_temperature = 9;
|
||||||
float visual_temperature_step = 10;
|
float visual_temperature_step = 10;
|
||||||
bool supports_away = 11;
|
// for older peer versions - in new system this
|
||||||
|
// is if CLIMATE_PRESET_AWAY exists is supported_presets
|
||||||
|
bool legacy_supports_away = 11;
|
||||||
bool supports_action = 12;
|
bool supports_action = 12;
|
||||||
|
repeated ClimateFanMode supported_fan_modes = 13;
|
||||||
|
repeated ClimateSwingMode supported_swing_modes = 14;
|
||||||
|
repeated string supported_custom_fan_modes = 15;
|
||||||
|
repeated ClimatePreset supported_presets = 16;
|
||||||
|
repeated string supported_custom_presets = 17;
|
||||||
}
|
}
|
||||||
message ClimateStateResponse {
|
message ClimateStateResponse {
|
||||||
option (id) = 47;
|
option (id) = 47;
|
||||||
@@ -682,8 +768,14 @@ message ClimateStateResponse {
|
|||||||
float target_temperature = 4;
|
float target_temperature = 4;
|
||||||
float target_temperature_low = 5;
|
float target_temperature_low = 5;
|
||||||
float target_temperature_high = 6;
|
float target_temperature_high = 6;
|
||||||
bool away = 7;
|
// For older peers, equal to preset == CLIMATE_PRESET_AWAY
|
||||||
|
bool legacy_away = 7;
|
||||||
ClimateAction action = 8;
|
ClimateAction action = 8;
|
||||||
|
ClimateFanMode fan_mode = 9;
|
||||||
|
ClimateSwingMode swing_mode = 10;
|
||||||
|
string custom_fan_mode = 11;
|
||||||
|
ClimatePreset preset = 12;
|
||||||
|
string custom_preset = 13;
|
||||||
}
|
}
|
||||||
message ClimateCommandRequest {
|
message ClimateCommandRequest {
|
||||||
option (id) = 48;
|
option (id) = 48;
|
||||||
@@ -700,6 +792,55 @@ message ClimateCommandRequest {
|
|||||||
float target_temperature_low = 7;
|
float target_temperature_low = 7;
|
||||||
bool has_target_temperature_high = 8;
|
bool has_target_temperature_high = 8;
|
||||||
float target_temperature_high = 9;
|
float target_temperature_high = 9;
|
||||||
bool has_away = 10;
|
// legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
|
||||||
bool away = 11;
|
bool has_legacy_away = 10;
|
||||||
|
bool legacy_away = 11;
|
||||||
|
bool has_fan_mode = 12;
|
||||||
|
ClimateFanMode fan_mode = 13;
|
||||||
|
bool has_swing_mode = 14;
|
||||||
|
ClimateSwingMode swing_mode = 15;
|
||||||
|
bool has_custom_fan_mode = 16;
|
||||||
|
string custom_fan_mode = 17;
|
||||||
|
bool has_preset = 18;
|
||||||
|
ClimatePreset preset = 19;
|
||||||
|
bool has_custom_preset = 20;
|
||||||
|
string custom_preset = 21;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== NUMBER ====================
|
||||||
|
message ListEntitiesNumberResponse {
|
||||||
|
option (id) = 49;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_NUMBER";
|
||||||
|
|
||||||
|
string object_id = 1;
|
||||||
|
fixed32 key = 2;
|
||||||
|
string name = 3;
|
||||||
|
string unique_id = 4;
|
||||||
|
|
||||||
|
string icon = 5;
|
||||||
|
float min_value = 6;
|
||||||
|
float max_value = 7;
|
||||||
|
float step = 8;
|
||||||
|
}
|
||||||
|
message NumberStateResponse {
|
||||||
|
option (id) = 50;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_NUMBER";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
float state = 2;
|
||||||
|
// If the number does not have a valid state yet.
|
||||||
|
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||||
|
bool missing_state = 3;
|
||||||
|
}
|
||||||
|
message NumberCommandRequest {
|
||||||
|
option (id) = 51;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_NUMBER";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
float state = 2;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,14 @@
|
|||||||
#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 *TAG = "api.connection";
|
static const char *const TAG = "api.connection";
|
||||||
|
|
||||||
APIConnection::APIConnection(AsyncClient *client, APIServer *parent)
|
APIConnection::APIConnection(AsyncClient *client, APIServer *parent)
|
||||||
: client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
|
: client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
|
||||||
@@ -137,7 +140,6 @@ void APIConnection::loop() {
|
|||||||
// bool done = 3;
|
// bool done = 3;
|
||||||
bool done = this->image_reader_.available() == to_send;
|
bool done = this->image_reader_.available() == to_send;
|
||||||
buffer.encode_bool(3, done);
|
buffer.encode_bool(3, done);
|
||||||
this->set_nodelay(false);
|
|
||||||
bool success = this->send_buffer(buffer, 44);
|
bool success = this->send_buffer(buffer, 44);
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
@@ -163,6 +165,7 @@ bool APIConnection::send_binary_sensor_state(binary_sensor::BinarySensor *binary
|
|||||||
BinarySensorStateResponse resp;
|
BinarySensorStateResponse resp;
|
||||||
resp.key = binary_sensor->get_object_id_hash();
|
resp.key = binary_sensor->get_object_id_hash();
|
||||||
resp.state = state;
|
resp.state = state;
|
||||||
|
resp.missing_state = !binary_sensor->has_state();
|
||||||
return this->send_binary_sensor_state_response(resp);
|
return this->send_binary_sensor_state_response(resp);
|
||||||
}
|
}
|
||||||
bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) {
|
bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor) {
|
||||||
@@ -246,8 +249,12 @@ bool APIConnection::send_fan_state(fan::FanState *fan) {
|
|||||||
resp.state = fan->state;
|
resp.state = fan->state;
|
||||||
if (traits.supports_oscillation())
|
if (traits.supports_oscillation())
|
||||||
resp.oscillating = fan->oscillating;
|
resp.oscillating = fan->oscillating;
|
||||||
if (traits.supports_speed())
|
if (traits.supports_speed()) {
|
||||||
resp.speed = static_cast<enums::FanSpeed>(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())
|
||||||
|
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::FanState *fan) {
|
||||||
@@ -259,6 +266,8 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
|
|||||||
msg.unique_id = get_default_unique_id("fan", fan);
|
msg.unique_id = get_default_unique_id("fan", fan);
|
||||||
msg.supports_oscillation = traits.supports_oscillation();
|
msg.supports_oscillation = traits.supports_oscillation();
|
||||||
msg.supports_speed = traits.supports_speed();
|
msg.supports_speed = traits.supports_speed();
|
||||||
|
msg.supports_direction = traits.supports_direction();
|
||||||
|
msg.supported_speed_count = traits.supported_speed_count();
|
||||||
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) {
|
||||||
@@ -266,13 +275,21 @@ void APIConnection::fan_command(const FanCommandRequest &msg) {
|
|||||||
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);
|
||||||
if (msg.has_oscillating)
|
if (msg.has_oscillating)
|
||||||
call.set_oscillating(msg.oscillating);
|
call.set_oscillating(msg.oscillating);
|
||||||
if (msg.has_speed)
|
if (msg.has_speed_level) {
|
||||||
call.set_speed(static_cast<fan::FanSpeed>(msg.speed));
|
// Prefer 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)
|
||||||
|
call.set_direction(static_cast<fan::FanDirection>(msg.direction));
|
||||||
call.perform();
|
call.perform();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -291,6 +308,7 @@ bool APIConnection::send_light_state(light::LightState *light) {
|
|||||||
if (traits.get_supports_brightness())
|
if (traits.get_supports_brightness())
|
||||||
resp.brightness = values.get_brightness();
|
resp.brightness = values.get_brightness();
|
||||||
if (traits.get_supports_rgb()) {
|
if (traits.get_supports_rgb()) {
|
||||||
|
resp.color_brightness = values.get_color_brightness();
|
||||||
resp.red = values.get_red();
|
resp.red = values.get_red();
|
||||||
resp.green = values.get_green();
|
resp.green = values.get_green();
|
||||||
resp.blue = values.get_blue();
|
resp.blue = values.get_blue();
|
||||||
@@ -335,6 +353,8 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
|
|||||||
call.set_state(msg.state);
|
call.set_state(msg.state);
|
||||||
if (msg.has_brightness)
|
if (msg.has_brightness)
|
||||||
call.set_brightness(msg.brightness);
|
call.set_brightness(msg.brightness);
|
||||||
|
if (msg.has_color_brightness)
|
||||||
|
call.set_color_brightness(msg.color_brightness);
|
||||||
if (msg.has_rgb) {
|
if (msg.has_rgb) {
|
||||||
call.set_red(msg.red);
|
call.set_red(msg.red);
|
||||||
call.set_green(msg.green);
|
call.set_green(msg.green);
|
||||||
@@ -362,6 +382,7 @@ bool APIConnection::send_sensor_state(sensor::Sensor *sensor, float state) {
|
|||||||
SensorStateResponse resp{};
|
SensorStateResponse resp{};
|
||||||
resp.key = sensor->get_object_id_hash();
|
resp.key = sensor->get_object_id_hash();
|
||||||
resp.state = state;
|
resp.state = state;
|
||||||
|
resp.missing_state = !sensor->has_state();
|
||||||
return this->send_sensor_state_response(resp);
|
return this->send_sensor_state_response(resp);
|
||||||
}
|
}
|
||||||
bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
||||||
@@ -375,6 +396,11 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
|||||||
msg.icon = sensor->get_icon();
|
msg.icon = sensor->get_icon();
|
||||||
msg.unit_of_measurement = sensor->get_unit_of_measurement();
|
msg.unit_of_measurement = sensor->get_unit_of_measurement();
|
||||||
msg.accuracy_decimals = sensor->get_accuracy_decimals();
|
msg.accuracy_decimals = sensor->get_accuracy_decimals();
|
||||||
|
msg.force_update = sensor->get_force_update();
|
||||||
|
msg.device_class = sensor->get_device_class();
|
||||||
|
msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
|
||||||
|
msg.last_reset_type = static_cast<enums::SensorLastResetType>(sensor->last_reset_type);
|
||||||
|
|
||||||
return this->send_list_entities_sensor_response(msg);
|
return this->send_list_entities_sensor_response(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -419,6 +445,7 @@ bool APIConnection::send_text_sensor_state(text_sensor::TextSensor *text_sensor,
|
|||||||
TextSensorStateResponse resp{};
|
TextSensorStateResponse resp{};
|
||||||
resp.key = text_sensor->get_object_id_hash();
|
resp.key = text_sensor->get_object_id_hash();
|
||||||
resp.state = std::move(state);
|
resp.state = std::move(state);
|
||||||
|
resp.missing_state = !text_sensor->has_state();
|
||||||
return this->send_text_sensor_state_response(resp);
|
return this->send_text_sensor_state_response(resp);
|
||||||
}
|
}
|
||||||
bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) {
|
bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor) {
|
||||||
@@ -452,8 +479,18 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
|
|||||||
} else {
|
} else {
|
||||||
resp.target_temperature = climate->target_temperature;
|
resp.target_temperature = climate->target_temperature;
|
||||||
}
|
}
|
||||||
if (traits.get_supports_away())
|
if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
|
||||||
resp.away = climate->away;
|
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
|
||||||
|
if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
|
||||||
|
resp.custom_fan_mode = climate->custom_fan_mode.value();
|
||||||
|
if (traits.get_supports_presets() && climate->preset.has_value()) {
|
||||||
|
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
|
||||||
|
resp.legacy_away = resp.preset == enums::CLIMATE_PRESET_AWAY;
|
||||||
|
}
|
||||||
|
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
|
||||||
|
resp.custom_preset = climate->custom_preset.value();
|
||||||
|
if (traits.get_supports_swing_modes())
|
||||||
|
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
|
||||||
return this->send_climate_state_response(resp);
|
return this->send_climate_state_response(resp);
|
||||||
}
|
}
|
||||||
bool APIConnection::send_climate_info(climate::Climate *climate) {
|
bool APIConnection::send_climate_info(climate::Climate *climate) {
|
||||||
@@ -465,16 +502,26 @@ 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.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();
|
||||||
for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL,
|
|
||||||
climate::CLIMATE_MODE_HEAT}) {
|
for (auto mode : traits.get_supported_modes())
|
||||||
if (traits.supports_mode(mode))
|
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
|
||||||
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
|
|
||||||
}
|
|
||||||
msg.visual_min_temperature = traits.get_visual_min_temperature();
|
msg.visual_min_temperature = traits.get_visual_min_temperature();
|
||||||
msg.visual_max_temperature = traits.get_visual_max_temperature();
|
msg.visual_max_temperature = traits.get_visual_max_temperature();
|
||||||
msg.visual_temperature_step = traits.get_visual_temperature_step();
|
msg.visual_temperature_step = traits.get_visual_temperature_step();
|
||||||
msg.supports_away = traits.get_supports_away();
|
msg.legacy_supports_away = traits.supports_preset(climate::CLIMATE_PRESET_AWAY);
|
||||||
msg.supports_action = traits.get_supports_action();
|
msg.supports_action = traits.get_supports_action();
|
||||||
|
|
||||||
|
for (auto fan_mode : traits.get_supported_fan_modes())
|
||||||
|
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
|
||||||
|
for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
|
||||||
|
msg.supported_custom_fan_modes.push_back(custom_fan_mode);
|
||||||
|
for (auto preset : traits.get_supported_presets())
|
||||||
|
msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
|
||||||
|
for (auto const &custom_preset : traits.get_supported_custom_presets())
|
||||||
|
msg.supported_custom_presets.push_back(custom_preset);
|
||||||
|
for (auto swing_mode : traits.get_supported_swing_modes())
|
||||||
|
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
|
||||||
return this->send_list_entities_climate_response(msg);
|
return this->send_list_entities_climate_response(msg);
|
||||||
}
|
}
|
||||||
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
||||||
@@ -491,8 +538,54 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
|||||||
call.set_target_temperature_low(msg.target_temperature_low);
|
call.set_target_temperature_low(msg.target_temperature_low);
|
||||||
if (msg.has_target_temperature_high)
|
if (msg.has_target_temperature_high)
|
||||||
call.set_target_temperature_high(msg.target_temperature_high);
|
call.set_target_temperature_high(msg.target_temperature_high);
|
||||||
if (msg.has_away)
|
if (msg.has_legacy_away)
|
||||||
call.set_away(msg.away);
|
call.set_preset(msg.legacy_away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME);
|
||||||
|
if (msg.has_fan_mode)
|
||||||
|
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
|
||||||
|
if (msg.has_custom_fan_mode)
|
||||||
|
call.set_fan_mode(msg.custom_fan_mode);
|
||||||
|
if (msg.has_preset)
|
||||||
|
call.set_preset(static_cast<climate::ClimatePreset>(msg.preset));
|
||||||
|
if (msg.has_custom_preset)
|
||||||
|
call.set_preset(msg.custom_preset);
|
||||||
|
if (msg.has_swing_mode)
|
||||||
|
call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode));
|
||||||
|
call.perform();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
bool APIConnection::send_number_state(number::Number *number, float state) {
|
||||||
|
if (!this->state_subscription_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
NumberStateResponse resp{};
|
||||||
|
resp.key = number->get_object_id_hash();
|
||||||
|
resp.state = state;
|
||||||
|
resp.missing_state = !number->has_state();
|
||||||
|
return this->send_number_state_response(resp);
|
||||||
|
}
|
||||||
|
bool APIConnection::send_number_info(number::Number *number) {
|
||||||
|
ListEntitiesNumberResponse msg;
|
||||||
|
msg.key = number->get_object_id_hash();
|
||||||
|
msg.object_id = number->get_object_id();
|
||||||
|
msg.name = number->get_name();
|
||||||
|
msg.unique_id = get_default_unique_id("number", number);
|
||||||
|
msg.icon = number->traits.get_icon();
|
||||||
|
|
||||||
|
msg.min_value = number->traits.get_min_value();
|
||||||
|
msg.max_value = number->traits.get_max_value();
|
||||||
|
msg.step = number->traits.get_step();
|
||||||
|
|
||||||
|
return this->send_list_entities_number_response(msg);
|
||||||
|
}
|
||||||
|
void APIConnection::number_command(const NumberCommandRequest &msg) {
|
||||||
|
number::Number *number = App.get_number_by_key(msg.key);
|
||||||
|
if (number == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto call = number->make_call();
|
||||||
|
call.set_value(msg.state);
|
||||||
call.perform();
|
call.perform();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -535,8 +628,6 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin
|
|||||||
if (this->log_subscription_ < level)
|
if (this->log_subscription_ < level)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
this->set_nodelay(false);
|
|
||||||
|
|
||||||
// Send raw so that we don't copy too much
|
// Send raw so that we don't copy too much
|
||||||
auto buffer = this->create_buffer();
|
auto buffer = this->create_buffer();
|
||||||
// LogLevel level = 1;
|
// LogLevel level = 1;
|
||||||
@@ -564,7 +655,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
|||||||
|
|
||||||
HelloResponse resp;
|
HelloResponse resp;
|
||||||
resp.api_version_major = 1;
|
resp.api_version_major = 1;
|
||||||
resp.api_version_minor = 3;
|
resp.api_version_minor = 5;
|
||||||
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
||||||
this->connection_state_ = ConnectionState::CONNECTED;
|
this->connection_state_ = ConnectionState::CONNECTED;
|
||||||
return resp;
|
return resp;
|
||||||
@@ -599,13 +690,18 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_DEEP_SLEEP
|
#ifdef USE_DEEP_SLEEP
|
||||||
resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
|
resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
|
||||||
|
#endif
|
||||||
|
#ifdef ESPHOME_PROJECT_NAME
|
||||||
|
resp.project_name = ESPHOME_PROJECT_NAME;
|
||||||
|
resp.project_version = ESPHOME_PROJECT_VERSION;
|
||||||
#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)
|
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;
|
||||||
@@ -622,6 +718,7 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
|
|||||||
for (auto &it : this->parent_->get_state_subs()) {
|
for (auto &it : this->parent_->get_state_subs()) {
|
||||||
SubscribeHomeAssistantStateResponse resp;
|
SubscribeHomeAssistantStateResponse resp;
|
||||||
resp.entity_id = it.entity_id;
|
resp.entity_id = it.entity_id;
|
||||||
|
resp.attribute = it.attribute.value();
|
||||||
if (!this->send_subscribe_home_assistant_state_response(resp)) {
|
if (!this->send_subscribe_home_assistant_state_response(resp)) {
|
||||||
this->on_fatal_error();
|
this->on_fatal_error();
|
||||||
return;
|
return;
|
||||||
@@ -651,8 +748,10 @@ bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this->client_->add(reinterpret_cast<char *>(header.data()), header.size());
|
this->client_->add(reinterpret_cast<char *>(header.data()), header.size(),
|
||||||
this->client_->add(reinterpret_cast<char *>(buffer.get_buffer()->data()), buffer.get_buffer()->size());
|
ASYNC_WRITE_FLAG_COPY | ASYNC_WRITE_FLAG_MORE);
|
||||||
|
this->client_->add(reinterpret_cast<char *>(buffer.get_buffer()->data()), buffer.get_buffer()->size(),
|
||||||
|
ASYNC_WRITE_FLAG_COPY);
|
||||||
bool ret = this->client_->send();
|
bool ret = this->client_->send();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,11 @@ class APIConnection : public APIServerConnection {
|
|||||||
bool send_climate_state(climate::Climate *climate);
|
bool send_climate_state(climate::Climate *climate);
|
||||||
bool send_climate_info(climate::Climate *climate);
|
bool send_climate_info(climate::Climate *climate);
|
||||||
void climate_command(const ClimateCommandRequest &msg) override;
|
void climate_command(const ClimateCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
bool send_number_state(number::Number *number, float state);
|
||||||
|
bool send_number_info(number::Number *number);
|
||||||
|
void number_command(const NumberCommandRequest &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) {
|
||||||
@@ -138,12 +143,6 @@ class APIConnection : public APIServerConnection {
|
|||||||
void on_timeout_(uint32_t time);
|
void on_timeout_(uint32_t time);
|
||||||
void on_data_(uint8_t *buf, size_t len);
|
void on_data_(uint8_t *buf, size_t len);
|
||||||
void parse_recv_buffer_();
|
void parse_recv_buffer_();
|
||||||
void set_nodelay(bool nodelay) override {
|
|
||||||
if (nodelay == this->current_nodelay_)
|
|
||||||
return;
|
|
||||||
this->client_->setNoDelay(nodelay);
|
|
||||||
this->current_nodelay_ = nodelay;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class ConnectionState {
|
enum class ConnectionState {
|
||||||
WAITING_FOR_HELLO,
|
WAITING_FOR_HELLO,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,5 @@
|
|||||||
|
// This file was automatically generated with a tool.
|
||||||
|
// See scripts/api_protobuf/api_protobuf.py
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
@@ -26,6 +28,19 @@ enum FanSpeed : uint32_t {
|
|||||||
FAN_SPEED_MEDIUM = 1,
|
FAN_SPEED_MEDIUM = 1,
|
||||||
FAN_SPEED_HIGH = 2,
|
FAN_SPEED_HIGH = 2,
|
||||||
};
|
};
|
||||||
|
enum FanDirection : uint32_t {
|
||||||
|
FAN_DIRECTION_FORWARD = 0,
|
||||||
|
FAN_DIRECTION_REVERSE = 1,
|
||||||
|
};
|
||||||
|
enum SensorStateClass : uint32_t {
|
||||||
|
STATE_CLASS_NONE = 0,
|
||||||
|
STATE_CLASS_MEASUREMENT = 1,
|
||||||
|
};
|
||||||
|
enum SensorLastResetType : uint32_t {
|
||||||
|
LAST_RESET_NONE = 0,
|
||||||
|
LAST_RESET_NEVER = 1,
|
||||||
|
LAST_RESET_AUTO = 2,
|
||||||
|
};
|
||||||
enum LogLevel : uint32_t {
|
enum LogLevel : uint32_t {
|
||||||
LOG_LEVEL_NONE = 0,
|
LOG_LEVEL_NONE = 0,
|
||||||
LOG_LEVEL_ERROR = 1,
|
LOG_LEVEL_ERROR = 1,
|
||||||
@@ -47,21 +62,54 @@ enum ServiceArgType : uint32_t {
|
|||||||
};
|
};
|
||||||
enum ClimateMode : uint32_t {
|
enum ClimateMode : uint32_t {
|
||||||
CLIMATE_MODE_OFF = 0,
|
CLIMATE_MODE_OFF = 0,
|
||||||
CLIMATE_MODE_AUTO = 1,
|
CLIMATE_MODE_HEAT_COOL = 1,
|
||||||
CLIMATE_MODE_COOL = 2,
|
CLIMATE_MODE_COOL = 2,
|
||||||
CLIMATE_MODE_HEAT = 3,
|
CLIMATE_MODE_HEAT = 3,
|
||||||
|
CLIMATE_MODE_FAN_ONLY = 4,
|
||||||
|
CLIMATE_MODE_DRY = 5,
|
||||||
|
CLIMATE_MODE_AUTO = 6,
|
||||||
|
};
|
||||||
|
enum ClimateFanMode : uint32_t {
|
||||||
|
CLIMATE_FAN_ON = 0,
|
||||||
|
CLIMATE_FAN_OFF = 1,
|
||||||
|
CLIMATE_FAN_AUTO = 2,
|
||||||
|
CLIMATE_FAN_LOW = 3,
|
||||||
|
CLIMATE_FAN_MEDIUM = 4,
|
||||||
|
CLIMATE_FAN_HIGH = 5,
|
||||||
|
CLIMATE_FAN_MIDDLE = 6,
|
||||||
|
CLIMATE_FAN_FOCUS = 7,
|
||||||
|
CLIMATE_FAN_DIFFUSE = 8,
|
||||||
|
};
|
||||||
|
enum ClimateSwingMode : uint32_t {
|
||||||
|
CLIMATE_SWING_OFF = 0,
|
||||||
|
CLIMATE_SWING_BOTH = 1,
|
||||||
|
CLIMATE_SWING_VERTICAL = 2,
|
||||||
|
CLIMATE_SWING_HORIZONTAL = 3,
|
||||||
};
|
};
|
||||||
enum ClimateAction : uint32_t {
|
enum ClimateAction : uint32_t {
|
||||||
CLIMATE_ACTION_OFF = 0,
|
CLIMATE_ACTION_OFF = 0,
|
||||||
CLIMATE_ACTION_COOLING = 2,
|
CLIMATE_ACTION_COOLING = 2,
|
||||||
CLIMATE_ACTION_HEATING = 3,
|
CLIMATE_ACTION_HEATING = 3,
|
||||||
|
CLIMATE_ACTION_IDLE = 4,
|
||||||
|
CLIMATE_ACTION_DRYING = 5,
|
||||||
|
CLIMATE_ACTION_FAN = 6,
|
||||||
|
};
|
||||||
|
enum ClimatePreset : uint32_t {
|
||||||
|
CLIMATE_PRESET_NONE = 0,
|
||||||
|
CLIMATE_PRESET_HOME = 1,
|
||||||
|
CLIMATE_PRESET_AWAY = 2,
|
||||||
|
CLIMATE_PRESET_BOOST = 3,
|
||||||
|
CLIMATE_PRESET_COMFORT = 4,
|
||||||
|
CLIMATE_PRESET_ECO = 5,
|
||||||
|
CLIMATE_PRESET_SLEEP = 6,
|
||||||
|
CLIMATE_PRESET_ACTIVITY = 7,
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace enums
|
} // namespace enums
|
||||||
|
|
||||||
class HelloRequest : public ProtoMessage {
|
class HelloRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string client_info{}; // NOLINT
|
std::string client_info{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -70,9 +118,9 @@ class HelloRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class HelloResponse : public ProtoMessage {
|
class HelloResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t api_version_major{0}; // NOLINT
|
uint32_t api_version_major{0};
|
||||||
uint32_t api_version_minor{0}; // NOLINT
|
uint32_t api_version_minor{0};
|
||||||
std::string server_info{}; // NOLINT
|
std::string server_info{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -82,7 +130,7 @@ class HelloResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ConnectRequest : public ProtoMessage {
|
class ConnectRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string password{}; // NOLINT
|
std::string password{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -91,7 +139,7 @@ class ConnectRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ConnectResponse : public ProtoMessage {
|
class ConnectResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
bool invalid_password{false}; // NOLINT
|
bool invalid_password{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -135,13 +183,15 @@ class DeviceInfoRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class DeviceInfoResponse : public ProtoMessage {
|
class DeviceInfoResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
bool uses_password{false}; // NOLINT
|
bool uses_password{false};
|
||||||
std::string name{}; // NOLINT
|
std::string name{};
|
||||||
std::string mac_address{}; // NOLINT
|
std::string mac_address{};
|
||||||
std::string esphome_version{}; // NOLINT
|
std::string esphome_version{};
|
||||||
std::string compilation_time{}; // NOLINT
|
std::string compilation_time{};
|
||||||
std::string model{}; // NOLINT
|
std::string model{};
|
||||||
bool has_deep_sleep{false}; // NOLINT
|
bool has_deep_sleep{false};
|
||||||
|
std::string project_name{};
|
||||||
|
std::string project_version{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -172,12 +222,12 @@ class SubscribeStatesRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ListEntitiesBinarySensorResponse : public ProtoMessage {
|
class ListEntitiesBinarySensorResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{}; // NOLINT
|
std::string object_id{};
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::string name{}; // NOLINT
|
std::string name{};
|
||||||
std::string unique_id{}; // NOLINT
|
std::string unique_id{};
|
||||||
std::string device_class{}; // NOLINT
|
std::string device_class{};
|
||||||
bool is_status_binary_sensor{false}; // NOLINT
|
bool is_status_binary_sensor{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -188,8 +238,9 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class BinarySensorStateResponse : public ProtoMessage {
|
class BinarySensorStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
bool state{false}; // NOLINT
|
bool state{false};
|
||||||
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -199,14 +250,14 @@ class BinarySensorStateResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ListEntitiesCoverResponse : public ProtoMessage {
|
class ListEntitiesCoverResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{}; // NOLINT
|
std::string object_id{};
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::string name{}; // NOLINT
|
std::string name{};
|
||||||
std::string unique_id{}; // NOLINT
|
std::string unique_id{};
|
||||||
bool assumed_state{false}; // NOLINT
|
bool assumed_state{false};
|
||||||
bool supports_position{false}; // NOLINT
|
bool supports_position{false};
|
||||||
bool supports_tilt{false}; // NOLINT
|
bool supports_tilt{false};
|
||||||
std::string device_class{}; // NOLINT
|
std::string device_class{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -217,11 +268,11 @@ class ListEntitiesCoverResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class CoverStateResponse : public ProtoMessage {
|
class CoverStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
enums::LegacyCoverState legacy_state{}; // NOLINT
|
enums::LegacyCoverState legacy_state{};
|
||||||
float position{0.0f}; // NOLINT
|
float position{0.0f};
|
||||||
float tilt{0.0f}; // NOLINT
|
float tilt{0.0f};
|
||||||
enums::CoverOperation current_operation{}; // NOLINT
|
enums::CoverOperation current_operation{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -231,14 +282,14 @@ class CoverStateResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class CoverCommandRequest : public ProtoMessage {
|
class CoverCommandRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
bool has_legacy_command{false}; // NOLINT
|
bool has_legacy_command{false};
|
||||||
enums::LegacyCoverCommand legacy_command{}; // NOLINT
|
enums::LegacyCoverCommand legacy_command{};
|
||||||
bool has_position{false}; // NOLINT
|
bool has_position{false};
|
||||||
float position{0.0f}; // NOLINT
|
float position{0.0f};
|
||||||
bool has_tilt{false}; // NOLINT
|
bool has_tilt{false};
|
||||||
float tilt{0.0f}; // NOLINT
|
float tilt{0.0f};
|
||||||
bool stop{false}; // NOLINT
|
bool stop{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -248,12 +299,14 @@ class CoverCommandRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ListEntitiesFanResponse : public ProtoMessage {
|
class ListEntitiesFanResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{}; // NOLINT
|
std::string object_id{};
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::string name{}; // NOLINT
|
std::string name{};
|
||||||
std::string unique_id{}; // NOLINT
|
std::string unique_id{};
|
||||||
bool supports_oscillation{false}; // NOLINT
|
bool supports_oscillation{false};
|
||||||
bool supports_speed{false}; // NOLINT
|
bool supports_speed{false};
|
||||||
|
bool supports_direction{false};
|
||||||
|
int32_t supported_speed_count{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -264,10 +317,12 @@ class ListEntitiesFanResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class FanStateResponse : public ProtoMessage {
|
class FanStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
bool state{false}; // NOLINT
|
bool state{false};
|
||||||
bool oscillating{false}; // NOLINT
|
bool oscillating{false};
|
||||||
enums::FanSpeed speed{}; // NOLINT
|
enums::FanSpeed speed{};
|
||||||
|
enums::FanDirection direction{};
|
||||||
|
int32_t speed_level{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -277,13 +332,17 @@ class FanStateResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class FanCommandRequest : public ProtoMessage {
|
class FanCommandRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
bool has_state{false}; // NOLINT
|
bool has_state{false};
|
||||||
bool state{false}; // NOLINT
|
bool state{false};
|
||||||
bool has_speed{false}; // NOLINT
|
bool has_speed{false};
|
||||||
enums::FanSpeed speed{}; // NOLINT
|
enums::FanSpeed speed{};
|
||||||
bool has_oscillating{false}; // NOLINT
|
bool has_oscillating{false};
|
||||||
bool oscillating{false}; // NOLINT
|
bool oscillating{false};
|
||||||
|
bool has_direction{false};
|
||||||
|
enums::FanDirection direction{};
|
||||||
|
bool has_speed_level{false};
|
||||||
|
int32_t speed_level{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -293,17 +352,17 @@ class FanCommandRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ListEntitiesLightResponse : public ProtoMessage {
|
class ListEntitiesLightResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{}; // NOLINT
|
std::string object_id{};
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::string name{}; // NOLINT
|
std::string name{};
|
||||||
std::string unique_id{}; // NOLINT
|
std::string unique_id{};
|
||||||
bool supports_brightness{false}; // NOLINT
|
bool supports_brightness{false};
|
||||||
bool supports_rgb{false}; // NOLINT
|
bool supports_rgb{false};
|
||||||
bool supports_white_value{false}; // NOLINT
|
bool supports_white_value{false};
|
||||||
bool supports_color_temperature{false}; // NOLINT
|
bool supports_color_temperature{false};
|
||||||
float min_mireds{0.0f}; // NOLINT
|
float min_mireds{0.0f};
|
||||||
float max_mireds{0.0f}; // NOLINT
|
float max_mireds{0.0f};
|
||||||
std::vector<std::string> effects{}; // NOLINT
|
std::vector<std::string> effects{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -314,15 +373,16 @@ class ListEntitiesLightResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class LightStateResponse : public ProtoMessage {
|
class LightStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
bool state{false}; // NOLINT
|
bool state{false};
|
||||||
float brightness{0.0f}; // NOLINT
|
float brightness{0.0f};
|
||||||
float red{0.0f}; // NOLINT
|
float color_brightness{0.0f};
|
||||||
float green{0.0f}; // NOLINT
|
float red{0.0f};
|
||||||
float blue{0.0f}; // NOLINT
|
float green{0.0f};
|
||||||
float white{0.0f}; // NOLINT
|
float blue{0.0f};
|
||||||
float color_temperature{0.0f}; // NOLINT
|
float white{0.0f};
|
||||||
std::string effect{}; // NOLINT
|
float color_temperature{0.0f};
|
||||||
|
std::string effect{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -333,25 +393,27 @@ class LightStateResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class LightCommandRequest : public ProtoMessage {
|
class LightCommandRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
bool has_state{false}; // NOLINT
|
bool has_state{false};
|
||||||
bool state{false}; // NOLINT
|
bool state{false};
|
||||||
bool has_brightness{false}; // NOLINT
|
bool has_brightness{false};
|
||||||
float brightness{0.0f}; // NOLINT
|
float brightness{0.0f};
|
||||||
bool has_rgb{false}; // NOLINT
|
bool has_color_brightness{false};
|
||||||
float red{0.0f}; // NOLINT
|
float color_brightness{0.0f};
|
||||||
float green{0.0f}; // NOLINT
|
bool has_rgb{false};
|
||||||
float blue{0.0f}; // NOLINT
|
float red{0.0f};
|
||||||
bool has_white{false}; // NOLINT
|
float green{0.0f};
|
||||||
float white{0.0f}; // NOLINT
|
float blue{0.0f};
|
||||||
bool has_color_temperature{false}; // NOLINT
|
bool has_white{false};
|
||||||
float color_temperature{0.0f}; // NOLINT
|
float white{0.0f};
|
||||||
bool has_transition_length{false}; // NOLINT
|
bool has_color_temperature{false};
|
||||||
uint32_t transition_length{0}; // NOLINT
|
float color_temperature{0.0f};
|
||||||
bool has_flash_length{false}; // NOLINT
|
bool has_transition_length{false};
|
||||||
uint32_t flash_length{0}; // NOLINT
|
uint32_t transition_length{0};
|
||||||
bool has_effect{false}; // NOLINT
|
bool has_flash_length{false};
|
||||||
std::string effect{}; // NOLINT
|
uint32_t flash_length{0};
|
||||||
|
bool has_effect{false};
|
||||||
|
std::string effect{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -362,14 +424,17 @@ class LightCommandRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ListEntitiesSensorResponse : public ProtoMessage {
|
class ListEntitiesSensorResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{}; // NOLINT
|
std::string object_id{};
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::string name{}; // NOLINT
|
std::string name{};
|
||||||
std::string unique_id{}; // NOLINT
|
std::string unique_id{};
|
||||||
std::string icon{}; // NOLINT
|
std::string icon{};
|
||||||
std::string unit_of_measurement{}; // NOLINT
|
std::string unit_of_measurement{};
|
||||||
int32_t accuracy_decimals{0}; // NOLINT
|
int32_t accuracy_decimals{0};
|
||||||
bool force_update{false}; // NOLINT
|
bool force_update{false};
|
||||||
|
std::string device_class{};
|
||||||
|
enums::SensorStateClass state_class{};
|
||||||
|
enums::SensorLastResetType last_reset_type{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -380,22 +445,24 @@ class ListEntitiesSensorResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class SensorStateResponse : public ProtoMessage {
|
class SensorStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
float state{0.0f}; // NOLINT
|
float state{0.0f};
|
||||||
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ListEntitiesSwitchResponse : public ProtoMessage {
|
class ListEntitiesSwitchResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{}; // NOLINT
|
std::string object_id{};
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::string name{}; // NOLINT
|
std::string name{};
|
||||||
std::string unique_id{}; // NOLINT
|
std::string unique_id{};
|
||||||
std::string icon{}; // NOLINT
|
std::string icon{};
|
||||||
bool assumed_state{false}; // NOLINT
|
bool assumed_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -406,8 +473,8 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class SwitchStateResponse : public ProtoMessage {
|
class SwitchStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
bool state{false}; // NOLINT
|
bool state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -417,8 +484,8 @@ class SwitchStateResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class SwitchCommandRequest : public ProtoMessage {
|
class SwitchCommandRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
bool state{false}; // NOLINT
|
bool state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -428,11 +495,11 @@ class SwitchCommandRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ListEntitiesTextSensorResponse : public ProtoMessage {
|
class ListEntitiesTextSensorResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{}; // NOLINT
|
std::string object_id{};
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::string name{}; // NOLINT
|
std::string name{};
|
||||||
std::string unique_id{}; // NOLINT
|
std::string unique_id{};
|
||||||
std::string icon{}; // NOLINT
|
std::string icon{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -442,19 +509,21 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class TextSensorStateResponse : public ProtoMessage {
|
class TextSensorStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::string state{}; // NOLINT
|
std::string state{};
|
||||||
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class SubscribeLogsRequest : public ProtoMessage {
|
class SubscribeLogsRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
enums::LogLevel level{}; // NOLINT
|
enums::LogLevel level{};
|
||||||
bool dump_config{false}; // NOLINT
|
bool dump_config{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -463,10 +532,10 @@ class SubscribeLogsRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class SubscribeLogsResponse : public ProtoMessage {
|
class SubscribeLogsResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
enums::LogLevel level{}; // NOLINT
|
enums::LogLevel level{};
|
||||||
std::string tag{}; // NOLINT
|
std::string tag{};
|
||||||
std::string message{}; // NOLINT
|
std::string message{};
|
||||||
bool send_failed{false}; // NOLINT
|
bool send_failed{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -483,8 +552,8 @@ class SubscribeHomeassistantServicesRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class HomeassistantServiceMap : public ProtoMessage {
|
class HomeassistantServiceMap : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string key{}; // NOLINT
|
std::string key{};
|
||||||
std::string value{}; // NOLINT
|
std::string value{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -493,11 +562,11 @@ class HomeassistantServiceMap : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class HomeassistantServiceResponse : public ProtoMessage {
|
class HomeassistantServiceResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string service{}; // NOLINT
|
std::string service{};
|
||||||
std::vector<HomeassistantServiceMap> data{}; // NOLINT
|
std::vector<HomeassistantServiceMap> data{};
|
||||||
std::vector<HomeassistantServiceMap> data_template{}; // NOLINT
|
std::vector<HomeassistantServiceMap> data_template{};
|
||||||
std::vector<HomeassistantServiceMap> variables{}; // NOLINT
|
std::vector<HomeassistantServiceMap> variables{};
|
||||||
bool is_event{false}; // NOLINT
|
bool is_event{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -514,7 +583,8 @@ class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class SubscribeHomeAssistantStateResponse : public ProtoMessage {
|
class SubscribeHomeAssistantStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string entity_id{}; // NOLINT
|
std::string entity_id{};
|
||||||
|
std::string attribute{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -523,8 +593,9 @@ class SubscribeHomeAssistantStateResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class HomeAssistantStateResponse : public ProtoMessage {
|
class HomeAssistantStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string entity_id{}; // NOLINT
|
std::string entity_id{};
|
||||||
std::string state{}; // NOLINT
|
std::string state{};
|
||||||
|
std::string attribute{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -540,7 +611,7 @@ class GetTimeRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class GetTimeResponse : public ProtoMessage {
|
class GetTimeResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t epoch_seconds{0}; // NOLINT
|
uint32_t epoch_seconds{0};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -549,8 +620,8 @@ class GetTimeResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ListEntitiesServicesArgument : public ProtoMessage {
|
class ListEntitiesServicesArgument : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string name{}; // NOLINT
|
std::string name{};
|
||||||
enums::ServiceArgType type{}; // NOLINT
|
enums::ServiceArgType type{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -560,9 +631,9 @@ class ListEntitiesServicesArgument : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ListEntitiesServicesResponse : public ProtoMessage {
|
class ListEntitiesServicesResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string name{}; // NOLINT
|
std::string name{};
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::vector<ListEntitiesServicesArgument> args{}; // NOLINT
|
std::vector<ListEntitiesServicesArgument> args{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -572,15 +643,15 @@ class ListEntitiesServicesResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ExecuteServiceArgument : public ProtoMessage {
|
class ExecuteServiceArgument : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
bool bool_{false}; // NOLINT
|
bool bool_{false};
|
||||||
int32_t legacy_int{0}; // NOLINT
|
int32_t legacy_int{0};
|
||||||
float float_{0.0f}; // NOLINT
|
float float_{0.0f};
|
||||||
std::string string_{}; // NOLINT
|
std::string string_{};
|
||||||
int32_t int_{0}; // NOLINT
|
int32_t int_{0};
|
||||||
std::vector<bool> bool_array{}; // NOLINT
|
std::vector<bool> bool_array{};
|
||||||
std::vector<int32_t> int_array{}; // NOLINT
|
std::vector<int32_t> int_array{};
|
||||||
std::vector<float> float_array{}; // NOLINT
|
std::vector<float> float_array{};
|
||||||
std::vector<std::string> string_array{}; // NOLINT
|
std::vector<std::string> string_array{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -591,8 +662,8 @@ class ExecuteServiceArgument : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ExecuteServiceRequest : public ProtoMessage {
|
class ExecuteServiceRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::vector<ExecuteServiceArgument> args{}; // NOLINT
|
std::vector<ExecuteServiceArgument> args{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -602,10 +673,10 @@ class ExecuteServiceRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ListEntitiesCameraResponse : public ProtoMessage {
|
class ListEntitiesCameraResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{}; // NOLINT
|
std::string object_id{};
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::string name{}; // NOLINT
|
std::string name{};
|
||||||
std::string unique_id{}; // NOLINT
|
std::string unique_id{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -615,9 +686,9 @@ class ListEntitiesCameraResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class CameraImageResponse : public ProtoMessage {
|
class CameraImageResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::string data{}; // NOLINT
|
std::string data{};
|
||||||
bool done{false}; // NOLINT
|
bool done{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -628,8 +699,8 @@ class CameraImageResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class CameraImageRequest : public ProtoMessage {
|
class CameraImageRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
bool single{false}; // NOLINT
|
bool single{false};
|
||||||
bool stream{false}; // NOLINT
|
bool stream{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -638,18 +709,23 @@ class CameraImageRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ListEntitiesClimateResponse : public ProtoMessage {
|
class ListEntitiesClimateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{}; // NOLINT
|
std::string object_id{};
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
std::string name{}; // NOLINT
|
std::string name{};
|
||||||
std::string unique_id{}; // NOLINT
|
std::string unique_id{};
|
||||||
bool supports_current_temperature{false}; // NOLINT
|
bool supports_current_temperature{false};
|
||||||
bool supports_two_point_target_temperature{false}; // NOLINT
|
bool supports_two_point_target_temperature{false};
|
||||||
std::vector<enums::ClimateMode> supported_modes{}; // NOLINT
|
std::vector<enums::ClimateMode> supported_modes{};
|
||||||
float visual_min_temperature{0.0f}; // NOLINT
|
float visual_min_temperature{0.0f};
|
||||||
float visual_max_temperature{0.0f}; // NOLINT
|
float visual_max_temperature{0.0f};
|
||||||
float visual_temperature_step{0.0f}; // NOLINT
|
float visual_temperature_step{0.0f};
|
||||||
bool supports_away{false}; // NOLINT
|
bool legacy_supports_away{false};
|
||||||
bool supports_action{false}; // NOLINT
|
bool supports_action{false};
|
||||||
|
std::vector<enums::ClimateFanMode> supported_fan_modes{};
|
||||||
|
std::vector<enums::ClimateSwingMode> supported_swing_modes{};
|
||||||
|
std::vector<std::string> supported_custom_fan_modes{};
|
||||||
|
std::vector<enums::ClimatePreset> supported_presets{};
|
||||||
|
std::vector<std::string> supported_custom_presets{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -660,34 +736,80 @@ class ListEntitiesClimateResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ClimateStateResponse : public ProtoMessage {
|
class ClimateStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
enums::ClimateMode mode{}; // NOLINT
|
enums::ClimateMode mode{};
|
||||||
float current_temperature{0.0f}; // NOLINT
|
float current_temperature{0.0f};
|
||||||
float target_temperature{0.0f}; // NOLINT
|
float target_temperature{0.0f};
|
||||||
float target_temperature_low{0.0f}; // NOLINT
|
float target_temperature_low{0.0f};
|
||||||
float target_temperature_high{0.0f}; // NOLINT
|
float target_temperature_high{0.0f};
|
||||||
bool away{false}; // NOLINT
|
bool legacy_away{false};
|
||||||
enums::ClimateAction action{}; // NOLINT
|
enums::ClimateAction action{};
|
||||||
|
enums::ClimateFanMode fan_mode{};
|
||||||
|
enums::ClimateSwingMode swing_mode{};
|
||||||
|
std::string custom_fan_mode{};
|
||||||
|
enums::ClimatePreset preset{};
|
||||||
|
std::string custom_preset{};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
class ClimateCommandRequest : public ProtoMessage {
|
class ClimateCommandRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0};
|
||||||
bool has_mode{false}; // NOLINT
|
bool has_mode{false};
|
||||||
enums::ClimateMode mode{}; // NOLINT
|
enums::ClimateMode mode{};
|
||||||
bool has_target_temperature{false}; // NOLINT
|
bool has_target_temperature{false};
|
||||||
float target_temperature{0.0f}; // NOLINT
|
float target_temperature{0.0f};
|
||||||
bool has_target_temperature_low{false}; // NOLINT
|
bool has_target_temperature_low{false};
|
||||||
float target_temperature_low{0.0f}; // NOLINT
|
float target_temperature_low{0.0f};
|
||||||
bool has_target_temperature_high{false}; // NOLINT
|
bool has_target_temperature_high{false};
|
||||||
float target_temperature_high{0.0f}; // NOLINT
|
float target_temperature_high{0.0f};
|
||||||
bool has_away{false}; // NOLINT
|
bool has_legacy_away{false};
|
||||||
bool away{false}; // NOLINT
|
bool legacy_away{false};
|
||||||
|
bool has_fan_mode{false};
|
||||||
|
enums::ClimateFanMode fan_mode{};
|
||||||
|
bool has_swing_mode{false};
|
||||||
|
enums::ClimateSwingMode swing_mode{};
|
||||||
|
bool has_custom_fan_mode{false};
|
||||||
|
std::string custom_fan_mode{};
|
||||||
|
bool has_preset{false};
|
||||||
|
enums::ClimatePreset preset{};
|
||||||
|
bool has_custom_preset{false};
|
||||||
|
std::string custom_preset{};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
|
};
|
||||||
|
class ListEntitiesNumberResponse : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
std::string object_id{};
|
||||||
|
uint32_t key{0};
|
||||||
|
std::string name{};
|
||||||
|
std::string unique_id{};
|
||||||
|
std::string icon{};
|
||||||
|
float min_value{0.0f};
|
||||||
|
float max_value{0.0f};
|
||||||
|
float step{0.0f};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||||
|
};
|
||||||
|
class NumberStateResponse : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
uint32_t key{0};
|
||||||
|
float state{0.0f};
|
||||||
|
bool missing_state{false};
|
||||||
void encode(ProtoWriteBuffer buffer) const override;
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
void dump_to(std::string &out) const override;
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
@@ -695,6 +817,16 @@ class ClimateCommandRequest : public ProtoMessage {
|
|||||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||||
};
|
};
|
||||||
|
class NumberCommandRequest : public ProtoMessage {
|
||||||
|
public:
|
||||||
|
uint32_t key{0};
|
||||||
|
float state{0.0f};
|
||||||
|
void encode(ProtoWriteBuffer buffer) const override;
|
||||||
|
void dump_to(std::string &out) const override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -1,76 +1,66 @@
|
|||||||
|
// This file was automatically generated with a tool.
|
||||||
|
// See scripts/api_protobuf/api_protobuf.py
|
||||||
#include "api_pb2_service.h"
|
#include "api_pb2_service.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
static const char *TAG = "api.service";
|
static const char *const TAG = "api.service";
|
||||||
|
|
||||||
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
|
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<HelloResponse>(msg, 2);
|
return this->send_message_<HelloResponse>(msg, 2);
|
||||||
}
|
}
|
||||||
bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
|
bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<ConnectResponse>(msg, 4);
|
return this->send_message_<ConnectResponse>(msg, 4);
|
||||||
}
|
}
|
||||||
bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
|
bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
|
||||||
ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<DisconnectRequest>(msg, 5);
|
return this->send_message_<DisconnectRequest>(msg, 5);
|
||||||
}
|
}
|
||||||
bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
|
bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<DisconnectResponse>(msg, 6);
|
return this->send_message_<DisconnectResponse>(msg, 6);
|
||||||
}
|
}
|
||||||
bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
|
bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
|
||||||
ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<PingRequest>(msg, 7);
|
return this->send_message_<PingRequest>(msg, 7);
|
||||||
}
|
}
|
||||||
bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
|
bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<PingResponse>(msg, 8);
|
return this->send_message_<PingResponse>(msg, 8);
|
||||||
}
|
}
|
||||||
bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
|
bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<DeviceInfoResponse>(msg, 10);
|
return this->send_message_<DeviceInfoResponse>(msg, 10);
|
||||||
}
|
}
|
||||||
bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
|
bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
|
return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
|
||||||
}
|
}
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) {
|
bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
|
return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
|
bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<BinarySensorStateResponse>(msg, 21);
|
return this->send_message_<BinarySensorStateResponse>(msg, 21);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
|
bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
|
return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
|
bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<CoverStateResponse>(msg, 22);
|
return this->send_message_<CoverStateResponse>(msg, 22);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -79,14 +69,12 @@ bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse
|
|||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
|
bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<ListEntitiesFanResponse>(msg, 14);
|
return this->send_message_<ListEntitiesFanResponse>(msg, 14);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
|
bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<FanStateResponse>(msg, 23);
|
return this->send_message_<FanStateResponse>(msg, 23);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -95,14 +83,12 @@ bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &ms
|
|||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
|
bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<ListEntitiesLightResponse>(msg, 15);
|
return this->send_message_<ListEntitiesLightResponse>(msg, 15);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
|
bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<LightStateResponse>(msg, 24);
|
return this->send_message_<LightStateResponse>(msg, 24);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -111,28 +97,24 @@ bool APIServerConnectionBase::send_light_state_response(const LightStateResponse
|
|||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
|
bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
|
return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
|
bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<SensorStateResponse>(msg, 25);
|
return this->send_message_<SensorStateResponse>(msg, 25);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
|
bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
|
return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
|
bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<SwitchStateResponse>(msg, 26);
|
return this->send_message_<SwitchStateResponse>(msg, 26);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -141,58 +123,48 @@ bool APIServerConnectionBase::send_switch_state_response(const SwitchStateRespon
|
|||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) {
|
bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
|
return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
|
bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<TextSensorStateResponse>(msg, 27);
|
return this->send_message_<TextSensorStateResponse>(msg, 27);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
|
bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<SubscribeLogsResponse>(msg, 29);
|
return this->send_message_<SubscribeLogsResponse>(msg, 29);
|
||||||
}
|
}
|
||||||
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
|
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<HomeassistantServiceResponse>(msg, 35);
|
return this->send_message_<HomeassistantServiceResponse>(msg, 35);
|
||||||
}
|
}
|
||||||
bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
|
bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
|
||||||
const SubscribeHomeAssistantStateResponse &msg) {
|
const SubscribeHomeAssistantStateResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
|
return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
|
||||||
}
|
}
|
||||||
bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
|
bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
|
||||||
ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<GetTimeRequest>(msg, 36);
|
return this->send_message_<GetTimeRequest>(msg, 36);
|
||||||
}
|
}
|
||||||
bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
|
bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<GetTimeResponse>(msg, 37);
|
return this->send_message_<GetTimeResponse>(msg, 37);
|
||||||
}
|
}
|
||||||
bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
|
bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
|
return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
|
||||||
}
|
}
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_ESP32_CAMERA
|
||||||
bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
|
bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
|
return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_ESP32_CAMERA
|
||||||
bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
|
bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<CameraImageResponse>(msg, 44);
|
return this->send_message_<CameraImageResponse>(msg, 44);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -201,19 +173,31 @@ bool APIServerConnectionBase::send_camera_image_response(const CameraImageRespon
|
|||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
|
bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(false);
|
|
||||||
return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
|
return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
|
bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
|
||||||
ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
|
||||||
this->set_nodelay(true);
|
|
||||||
return this->send_message_<ClimateStateResponse>(msg, 47);
|
return this->send_message_<ClimateStateResponse>(msg, 47);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) {
|
||||||
|
ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str());
|
||||||
|
return this->send_message_<ListEntitiesNumberResponse>(msg, 49);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) {
|
||||||
|
ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str());
|
||||||
|
return this->send_message_<NumberStateResponse>(msg, 50);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
#endif
|
||||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
||||||
switch (msg_type) {
|
switch (msg_type) {
|
||||||
case 1: {
|
case 1: {
|
||||||
@@ -379,6 +363,15 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
msg.decode(msg_data, msg_size);
|
msg.decode(msg_data, msg_size);
|
||||||
ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str());
|
||||||
this->on_climate_command_request(msg);
|
this->on_climate_command_request(msg);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 51: {
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
NumberCommandRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
ESP_LOGVV(TAG, "on_number_command_request: %s", msg.dump().c_str());
|
||||||
|
this->on_number_command_request(msg);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -577,6 +570,19 @@ void APIServerConnection::on_climate_command_request(const ClimateCommandRequest
|
|||||||
this->climate_command(msg);
|
this->climate_command(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->number_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// This file was automatically generated with a tool.
|
||||||
|
// See scripts/api_protobuf/api_protobuf.py
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "api_pb2.h"
|
#include "api_pb2.h"
|
||||||
@@ -109,6 +111,15 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
virtual void on_climate_command_request(const ClimateCommandRequest &value){};
|
virtual void on_climate_command_request(const ClimateCommandRequest &value){};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
bool send_list_entities_number_response(const ListEntitiesNumberResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
bool send_number_state_response(const NumberStateResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
virtual void on_number_command_request(const NumberCommandRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||||
@@ -145,6 +156,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
virtual void climate_command(const ClimateCommandRequest &msg) = 0;
|
virtual void climate_command(const ClimateCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
virtual void number_command(const NumberCommandRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
protected:
|
protected:
|
||||||
void on_hello_request(const HelloRequest &msg) override;
|
void on_hello_request(const HelloRequest &msg) override;
|
||||||
@@ -177,6 +191,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
void on_climate_command_request(const ClimateCommandRequest &msg) override;
|
void on_climate_command_request(const ClimateCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
void on_number_command_request(const NumberCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
static const char *TAG = "api";
|
static const char *const TAG = "api";
|
||||||
|
|
||||||
// APIServer
|
// APIServer
|
||||||
void APIServer::setup() {
|
void APIServer::setup() {
|
||||||
@@ -180,7 +180,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) {
|
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
return;
|
return;
|
||||||
for (auto *c : this->clients_)
|
for (auto *c : this->clients_)
|
||||||
@@ -197,9 +197,18 @@ void APIServer::on_climate_update(climate::Climate *obj) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
void APIServer::on_number_update(number::Number *obj, float state) {
|
||||||
|
if (obj->is_internal())
|
||||||
|
return;
|
||||||
|
for (auto *c : this->clients_)
|
||||||
|
c->send_number_state(obj, state);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
|
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
|
||||||
void APIServer::set_port(uint16_t port) { this->port_ = port; }
|
void APIServer::set_port(uint16_t port) { this->port_ = port; }
|
||||||
APIServer *global_api_server = nullptr;
|
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
void APIServer::set_password(const std::string &password) { this->password_ = password; }
|
void APIServer::set_password(const std::string &password) { this->password_ = password; }
|
||||||
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||||
@@ -208,9 +217,11 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
APIServer::APIServer() { global_api_server = this; }
|
APIServer::APIServer() { global_api_server = this; }
|
||||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f) {
|
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||||
|
std::function<void(std::string)> f) {
|
||||||
this->state_subs_.push_back(HomeAssistantStateSubscription{
|
this->state_subs_.push_back(HomeAssistantStateSubscription{
|
||||||
.entity_id = std::move(entity_id),
|
.entity_id = std::move(entity_id),
|
||||||
|
.attribute = std::move(attribute),
|
||||||
.callback = std::move(f),
|
.callback = std::move(f),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,10 +56,13 @@ class APIServer : public Component, public Controller {
|
|||||||
void on_switch_update(switch_::Switch *obj, bool state) override;
|
void on_switch_update(switch_::Switch *obj, bool state) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
void on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) override;
|
void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
void on_climate_update(climate::Climate *obj) override;
|
void on_climate_update(climate::Climate *obj) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
void on_number_update(number::Number *obj, float state) override;
|
||||||
#endif
|
#endif
|
||||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
|
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
|
||||||
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
|
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
|
||||||
@@ -71,10 +74,12 @@ class APIServer : public Component, public Controller {
|
|||||||
|
|
||||||
struct HomeAssistantStateSubscription {
|
struct HomeAssistantStateSubscription {
|
||||||
std::string entity_id;
|
std::string entity_id;
|
||||||
|
optional<std::string> attribute;
|
||||||
std::function<void(std::string)> callback;
|
std::function<void(std::string)> callback;
|
||||||
};
|
};
|
||||||
|
|
||||||
void subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f);
|
void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||||
|
std::function<void(std::string)> f);
|
||||||
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
|
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
|
||||||
const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
|
const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
|
||||||
|
|
||||||
@@ -89,7 +94,7 @@ class APIServer : public Component, public Controller {
|
|||||||
std::vector<UserServiceDescriptor *> user_services_;
|
std::vector<UserServiceDescriptor *> user_services_;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern APIServer *global_api_server;
|
extern APIServer *global_api_server; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
|
template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -76,13 +76,13 @@ class CustomAPIDevice {
|
|||||||
global_api_server->register_user_service(service);
|
global_api_server->register_user_service(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Subscribe to the state of an entity from Home Assistant.
|
/** Subscribe to the state (or attribute state) of an entity from Home Assistant.
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
*
|
*
|
||||||
* ```cpp
|
* ```cpp
|
||||||
* void setup() override {
|
* void setup() override {
|
||||||
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
|
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "climate.kitchen", "current_temperature");
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* void on_state_changed(std::string state) {
|
* void on_state_changed(std::string state) {
|
||||||
@@ -93,17 +93,19 @@ class CustomAPIDevice {
|
|||||||
* @tparam T The class type creating the service, automatically deduced from the function pointer.
|
* @tparam T The class type creating the service, automatically deduced from the function pointer.
|
||||||
* @param callback The member function to call when the entity state changes.
|
* @param callback The member function to call when the entity state changes.
|
||||||
* @param entity_id The entity_id to track.
|
* @param entity_id The entity_id to track.
|
||||||
|
* @param attribute The entity state attribute to track.
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id) {
|
void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id,
|
||||||
|
const std::string &attribute = "") {
|
||||||
auto f = std::bind(callback, (T *) this, std::placeholders::_1);
|
auto f = std::bind(callback, (T *) this, std::placeholders::_1);
|
||||||
global_api_server->subscribe_home_assistant_state(entity_id, f);
|
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Subscribe to the state of an entity from Home Assistant.
|
/** Subscribe to the state (or attribute state) of an entity from Home Assistant.
|
||||||
*
|
*
|
||||||
* Usage:
|
* Usage:
|
||||||
*
|
*å
|
||||||
* ```cpp
|
* ```cpp
|
||||||
* void setup() override {
|
* void setup() override {
|
||||||
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
|
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
|
||||||
@@ -117,11 +119,13 @@ class CustomAPIDevice {
|
|||||||
* @tparam T The class type creating the service, automatically deduced from the function pointer.
|
* @tparam T The class type creating the service, automatically deduced from the function pointer.
|
||||||
* @param callback The member function to call when the entity state changes.
|
* @param callback The member function to call when the entity state changes.
|
||||||
* @param entity_id The entity_id to track.
|
* @param entity_id The entity_id to track.
|
||||||
|
* @param attribute The entity state attribute to track.
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id) {
|
void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id,
|
||||||
|
const std::string &attribute = "") {
|
||||||
auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
|
auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
|
||||||
global_api_server->subscribe_home_assistant_state(entity_id, f);
|
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Call a Home Assistant service from ESPHome.
|
/** Call a Home Assistant service from ESPHome.
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
|
|||||||
template<typename T> void add_variable(std::string key, T value) {
|
template<typename T> void add_variable(std::string key, T value) {
|
||||||
this->variables_.push_back(TemplatableKeyValuePair<Ts...>(key, value));
|
this->variables_.push_back(TemplatableKeyValuePair<Ts...>(key, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
void play(Ts... x) override {
|
void play(Ts... x) override {
|
||||||
HomeassistantServiceResponse resp;
|
HomeassistantServiceResponse resp;
|
||||||
resp.service = this->service_.value(x...);
|
resp.service = this->service_.value(x...);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user