mirror of
https://github.com/esphome/esphome.git
synced 2025-11-04 17:11:51 +00:00
Compare commits
920 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
2ff2750628 | ||
|
|
16c2dc2aaf | ||
|
|
eae5c17b87 | ||
|
|
a59cde91ad | ||
|
|
1f243ae37e | ||
|
|
603f82977e | ||
|
|
2d70422a6f | ||
|
|
e2c8b21195 | ||
|
|
7adaeacd0b | ||
|
|
dc2279b74f | ||
|
|
75275c4e93 | ||
|
|
65d3dc9cb8 | ||
|
|
66aa02fc34 | ||
|
|
be6b4ee47f | ||
|
|
90f909d2ea | ||
|
|
3aaa92fdff | ||
|
|
5efd076c08 | ||
|
|
09fd505f08 | ||
|
|
042ccde441 | ||
|
|
442030b6ca | ||
|
|
1df9ae53f8 | ||
|
|
5f535e9756 | ||
|
|
70faeb2fa8 | ||
|
|
469c0db981 | ||
|
|
440e428aa4 | ||
|
|
dde70c95a4 | ||
|
|
09d1846261 | ||
|
|
34d26a517d | ||
|
|
d24b88271c | ||
|
|
f22115792a | ||
|
|
82a30558e1 | ||
|
|
847fe5adca | ||
|
|
775b51c6a1 | ||
|
|
e0ad5a9009 | ||
|
|
1bf01a9081 | ||
|
|
6ae59bb43d | ||
|
|
adf2a463fd | ||
|
|
80aaf66963 | ||
|
|
560251ab2a | ||
|
|
864c5d8908 | ||
|
|
742c21506c | ||
|
|
a6faccb4d9 | ||
|
|
69fd3e8937 | ||
|
|
41233d7f25 | ||
|
|
07286d1d76 | ||
|
|
08148c5830 | ||
|
|
969bdb06ce | ||
|
|
b0bb692af4 | ||
|
|
7bf6fd316f | ||
|
|
c1f5e04d6c | ||
|
|
5a67e72389 | ||
|
|
91c9b11647 | ||
|
|
929600d7f7 | ||
|
|
163d0c55ab | ||
|
|
327ccb241e | ||
|
|
6b3c7b0854 | ||
|
|
681dcb51da | ||
|
|
576d5021fd | ||
|
|
6cd76f00ac | ||
|
|
6f63a62a8d | ||
|
|
8867a0fcfb | ||
|
|
bb2582717f | ||
|
|
d62ef35860 | ||
|
|
59c5956f93 | ||
|
|
e4f055597c | ||
|
|
4c49beb3c7 | ||
|
|
8ff742d9ab | ||
|
|
d63cd8b4cd | ||
|
|
42b4a166ec | ||
|
|
c27fd0f01a | ||
|
|
dcb4a0a81e | ||
|
|
17da9fddc3 | ||
|
|
31aa3c55ca | ||
|
|
eca3685ea0 | ||
|
|
bd216c5c63 | ||
|
|
31ff76427c | ||
|
|
3f0503c296 | ||
|
|
c18050bda0 | ||
|
|
6542be5588 | ||
|
|
9fb60b8015 | ||
|
|
1177b856a0 | ||
|
|
c0adaa8de8 | ||
|
|
ae8700447e | ||
|
|
d64a4ef2b4 | ||
|
|
2229aa6ccc | ||
|
|
872b468415 | ||
|
|
9f022a7433 | ||
|
|
85a958e300 | ||
|
|
0ee56195ae | ||
|
|
48f52db1d9 | ||
|
|
d2c7afeef0 | ||
|
|
644aec791e | ||
|
|
b70a0325c5 | ||
|
|
268387f829 | ||
|
|
b975caef1e | ||
|
|
54fe1c7d55 | ||
|
|
89c1274d56 | ||
|
|
f9ca3f1c27 | ||
|
|
26dbc30279 | ||
|
|
4bee316425 | ||
|
|
1e22b1e959 | ||
|
|
e077ad56bd | ||
|
|
f1e00f8c8e | ||
|
|
9a152e588e | ||
|
|
16f42a3d03 | ||
|
|
6c8d0f1852 | ||
|
|
b59cf6572b | ||
|
|
4fa11dfa68 | ||
|
|
352bdd9fb5 | ||
|
|
96ff9a162c | ||
|
|
af15a4e710 | ||
|
|
18426b71e4 | ||
|
|
7063aa6009 | ||
|
|
286ca07cc8 | ||
|
|
58b6311821 | ||
|
|
e553c0768e | ||
|
|
62d4b29662 | ||
|
|
16bc60644d | ||
|
|
1ca241615d | ||
|
|
b8aa84002a | ||
|
|
10cc0b1d5b | ||
|
|
11d9c203c1 | ||
|
|
c9ab454c3c | ||
|
|
4a55692885 | ||
|
|
88c129e705 | ||
|
|
80b48f01fb | ||
|
|
642bc91a76 | ||
|
|
d69926ee56 | ||
|
|
4758403d44 | ||
|
|
4b0ec5c28a | ||
|
|
4b2a9e5e49 | ||
|
|
1449c51d49 | ||
|
|
a451705e0b | ||
|
|
2e6db39173 | ||
|
|
373f75253c | ||
|
|
724842084e | ||
|
|
8f3635b167 | ||
|
|
11605a36f7 | ||
|
|
533f81d625 | ||
|
|
aacb9e44e8 | ||
|
|
c6e3f1bca6 | ||
|
|
a933d4aeb6 | ||
|
|
caa5b20791 | ||
|
|
e2ad9ed746 | ||
|
|
32c0e7c2ae | ||
|
|
6c564c7b7f | ||
|
|
c81e3a3be4 | ||
|
|
6b1b9ef7ec | ||
|
|
c26a8b8718 | ||
|
|
4a89a475bd | ||
|
|
8cf15c7f5c | ||
|
|
adc76ca1b8 | ||
|
|
8f8892440c | ||
|
|
570843150d | ||
|
|
f3fc9e4142 | ||
|
|
075fcb77a8 | ||
|
|
e5899ff717 | ||
|
|
2fb3970027 | ||
|
|
1a4efa1b8c | ||
|
|
72f656ffef | ||
|
|
b60239d5e5 | ||
|
|
d02e280c3c | ||
|
|
6535b0966e | ||
|
|
82dbacbee5 | ||
|
|
2432901974 | ||
|
|
ebb5d58c14 | ||
|
|
605e365405 | ||
|
|
5ab995d8ca | ||
|
|
4248741b11 | ||
|
|
4b8ecc7634 | ||
|
|
25d04c759c | ||
|
|
b4ec84030e | ||
|
|
29e8761373 | ||
|
|
a04299c59e | ||
|
|
d7bf3c51d9 | ||
|
|
ac0b095941 | ||
|
|
cda9bad233 | ||
|
|
41db8a1264 | ||
|
|
e7e785fd60 | ||
|
|
300d3a1f46 | ||
|
|
356554c08d | ||
|
|
ced28ad006 |
2
.coveragerc
Normal file
2
.coveragerc
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[run]
|
||||||
|
omit = esphome/components/*
|
||||||
32
.devcontainer/devcontainer.json
Normal file
32
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"name": "ESPHome Dev",
|
||||||
|
"context": "..",
|
||||||
|
"dockerFile": "../docker/Dockerfile.dev",
|
||||||
|
"postCreateCommand": "mkdir -p config && pip3 install -e .",
|
||||||
|
"runArgs": ["--privileged", "-e", "ESPHOME_DASHBOARD_USE_PING=1"],
|
||||||
|
"appPort": 6052,
|
||||||
|
"extensions": [
|
||||||
|
"ms-python.python",
|
||||||
|
"visualstudioexptteam.vscodeintellicode",
|
||||||
|
"redhat.vscode-yaml"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"python.pythonPath": "/usr/local/bin/python",
|
||||||
|
"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.shell.linux": "/bin/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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,3 +25,4 @@ indent_size = 2
|
|||||||
[*.{yaml,yml}]
|
[*.{yaml,yml}]
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
quote_type = single
|
||||||
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.
|
||||||
|
|
||||||
40
.github/PULL_REQUEST_TEMPLATE.md
vendored
40
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,13 +1,47 @@
|
|||||||
## Description:
|
# What does this implement/fix?
|
||||||
|
|
||||||
|
Quick description
|
||||||
|
|
||||||
**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)
|
||||||
|
- [ ] Configuration change (this will require users to update their yaml configuration files to keep working)
|
||||||
|
|
||||||
|
**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
|
||||||
|
- [ ] Windows
|
||||||
|
- [ ] Mac OS
|
||||||
|
- [ ] Linux
|
||||||
|
|
||||||
|
## 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
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
# Explain your changes
|
||||||
|
|
||||||
|
Describe your changes here to communicate to the maintainers **why we should accept this pull request**.
|
||||||
|
Very important to fill if no issue linked
|
||||||
|
|
||||||
## 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
|
||||||
55
.github/workflows/ci-docker.yml
vendored
Normal file
55
.github/workflows/ci-docker.yml
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
name: CI for docker images
|
||||||
|
|
||||||
|
# Only run when docker paths change
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [dev, beta, master]
|
||||||
|
paths:
|
||||||
|
- 'docker/**'
|
||||||
|
- '.github/workflows/**'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'docker/**'
|
||||||
|
- '.github/workflows/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-docker:
|
||||||
|
name: Build docker containers
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
arch: [amd64, armv7, aarch64]
|
||||||
|
build_type: ["hassio", "docker"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up env variables
|
||||||
|
run: |
|
||||||
|
base_version="3.0.0"
|
||||||
|
|
||||||
|
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
||||||
|
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
||||||
|
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
|
||||||
|
dockerfile="docker/Dockerfile.hassio"
|
||||||
|
else
|
||||||
|
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
|
||||||
|
build_to="esphome/esphome-${{ matrix.arch }}"
|
||||||
|
dockerfile="docker/Dockerfile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
|
||||||
|
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
|
||||||
|
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
|
||||||
|
- name: Pull for cache
|
||||||
|
run: |
|
||||||
|
docker pull "${BUILD_TO}:dev" || true
|
||||||
|
- name: Register QEMU binfmt
|
||||||
|
run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
|
||||||
|
- run: |
|
||||||
|
docker build \
|
||||||
|
--build-arg "BUILD_FROM=${BUILD_FROM}" \
|
||||||
|
--build-arg "BUILD_VERSION=ci" \
|
||||||
|
--cache-from "${BUILD_TO}:dev" \
|
||||||
|
--file "${DOCKERFILE}" \
|
||||||
|
.
|
||||||
160
.github/workflows/ci.yml
vendored
Normal file
160
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
# THESE JOBS ARE COPIED IN release.yml and release-dev.yml
|
||||||
|
# PLEASE ALSO UPDATE THOSE FILES WHEN CHANGING LINES HERE
|
||||||
|
name: CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
# On dev branch release-dev already performs CI checks
|
||||||
|
# On other branches the `pull_request` trigger will be used
|
||||||
|
branches: [beta, master]
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint-clang-format:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||||
|
# doesn't have to be installed
|
||||||
|
container: esphome/esphome-lint:latest
|
||||||
|
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: Run clang-format
|
||||||
|
run: script/clang-format -i
|
||||||
|
- name: Suggest changes
|
||||||
|
run: script/ci-suggest-changes
|
||||||
|
|
||||||
|
lint-clang-tidy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||||
|
# doesn't have to be installed
|
||||||
|
container: esphome/esphome-lint:latest
|
||||||
|
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
split: [1, 2, 3, 4]
|
||||||
|
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-tidy
|
||||||
|
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
|
||||||
|
- name: Suggest changes
|
||||||
|
run: script/ci-suggest-changes
|
||||||
|
|
||||||
|
lint-python:
|
||||||
|
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
||||||
|
# This way, all dependencies are cached via the cache action.
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
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-
|
||||||
|
- 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"
|
||||||
|
- name: Lint Custom
|
||||||
|
run: script/ci-custom.py
|
||||||
|
- name: Lint Python
|
||||||
|
run: script/lint-python
|
||||||
|
- name: Lint CODEOWNERS
|
||||||
|
run: script/build_codeowners.py --check
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
test:
|
||||||
|
- test1
|
||||||
|
- test2
|
||||||
|
- test3
|
||||||
|
- test4
|
||||||
|
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.test }}-${{ hashFiles('esphome/core_config.py') }}
|
||||||
|
restore-keys: |
|
||||||
|
test-home-platformio-${{ matrix.test }}-
|
||||||
|
- name: Set up environment
|
||||||
|
run: script/setup
|
||||||
|
|
||||||
|
|
||||||
|
- name: Register problem matchers
|
||||||
|
run: |
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||||
|
- run: esphome tests/${{ matrix.test }}.yaml compile
|
||||||
|
|
||||||
|
pytest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
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-
|
||||||
|
- name: Set up environment
|
||||||
|
run: script/setup
|
||||||
|
- name: Install Github Actions annotator
|
||||||
|
run: pip install pytest-github-actions-annotate-failures
|
||||||
|
|
||||||
|
- name: Register problem matchers
|
||||||
|
run: |
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||||
|
- name: Run pytest
|
||||||
|
run: |
|
||||||
|
pytest \
|
||||||
|
-qq \
|
||||||
|
--durations=10 \
|
||||||
|
-o console_output_style=count \
|
||||||
|
tests
|
||||||
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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
246
.github/workflows/release-dev.yml
vendored
Normal file
246
.github/workflows/release-dev.yml
vendored
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
name: Publish dev releases to docker hub
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# THE LINT/TEST JOBS ARE COPIED FROM ci.yaml
|
||||||
|
|
||||||
|
lint-clang-format:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||||
|
# doesn't have to be installed
|
||||||
|
container: esphome/esphome-lint:latest
|
||||||
|
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: Run clang-format
|
||||||
|
run: script/clang-format -i
|
||||||
|
- name: Suggest changes
|
||||||
|
run: script/ci-suggest-changes
|
||||||
|
|
||||||
|
lint-clang-tidy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||||
|
# doesn't have to be installed
|
||||||
|
container: esphome/esphome-lint:latest
|
||||||
|
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
split: [1, 2, 3, 4]
|
||||||
|
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-tidy
|
||||||
|
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
|
||||||
|
- name: Suggest changes
|
||||||
|
run: script/ci-suggest-changes
|
||||||
|
|
||||||
|
lint-python:
|
||||||
|
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
||||||
|
# This way, all dependencies are cached via the cache action.
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
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-
|
||||||
|
- 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"
|
||||||
|
- name: Lint Custom
|
||||||
|
run: script/ci-custom.py
|
||||||
|
- name: Lint Python
|
||||||
|
run: script/lint-python
|
||||||
|
- name: Lint CODEOWNERS
|
||||||
|
run: script/build_codeowners.py --check
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
test:
|
||||||
|
- test1
|
||||||
|
- test2
|
||||||
|
- test3
|
||||||
|
- test4
|
||||||
|
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.test }}-${{ hashFiles('esphome/core_config.py') }}
|
||||||
|
restore-keys: |
|
||||||
|
test-home-platformio-${{ matrix.test }}-
|
||||||
|
- name: Set up environment
|
||||||
|
run: script/setup
|
||||||
|
|
||||||
|
|
||||||
|
- name: Register problem matchers
|
||||||
|
run: |
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||||
|
- run: esphome tests/${{ matrix.test }}.yaml compile
|
||||||
|
|
||||||
|
pytest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
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-
|
||||||
|
- name: Set up environment
|
||||||
|
run: script/setup
|
||||||
|
- name: Install Github Actions annotator
|
||||||
|
run: pip install pytest-github-actions-annotate-failures
|
||||||
|
|
||||||
|
- name: Register problem matchers
|
||||||
|
run: |
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||||
|
- name: Run pytest
|
||||||
|
run: |
|
||||||
|
pytest \
|
||||||
|
-qq \
|
||||||
|
--durations=10 \
|
||||||
|
-o console_output_style=count \
|
||||||
|
tests
|
||||||
|
|
||||||
|
deploy-docker:
|
||||||
|
name: Build and publish docker containers
|
||||||
|
if: github.repository == 'esphome/esphome'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: [amd64, armv7, aarch64]
|
||||||
|
# Hassio dev image doesn't use esphome/esphome-hassio-$arch and uses base directly
|
||||||
|
build_type: ["docker"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set TAG
|
||||||
|
run: |
|
||||||
|
TAG="${GITHUB_SHA:0:7}"
|
||||||
|
echo "TAG=${TAG}" >> $GITHUB_ENV
|
||||||
|
- name: Set up env variables
|
||||||
|
run: |
|
||||||
|
base_version="3.0.0"
|
||||||
|
|
||||||
|
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
||||||
|
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
||||||
|
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
|
||||||
|
dockerfile="docker/Dockerfile.hassio"
|
||||||
|
else
|
||||||
|
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
|
||||||
|
build_to="esphome/esphome-${{ matrix.arch }}"
|
||||||
|
dockerfile="docker/Dockerfile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
|
||||||
|
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
|
||||||
|
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
|
||||||
|
- name: Pull for cache
|
||||||
|
run: |
|
||||||
|
docker pull "${BUILD_TO}:dev" || true
|
||||||
|
- name: Register QEMU binfmt
|
||||||
|
run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
|
||||||
|
- run: |
|
||||||
|
docker build \
|
||||||
|
--build-arg "BUILD_FROM=${BUILD_FROM}" \
|
||||||
|
--build-arg "BUILD_VERSION=${TAG}" \
|
||||||
|
--tag "${BUILD_TO}:${TAG}" \
|
||||||
|
--tag "${BUILD_TO}:dev" \
|
||||||
|
--cache-from "${BUILD_TO}:dev" \
|
||||||
|
--file "${DOCKERFILE}" \
|
||||||
|
.
|
||||||
|
- name: Log in to docker hub
|
||||||
|
env:
|
||||||
|
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||||
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||||
|
- run: |
|
||||||
|
docker push "${BUILD_TO}:${TAG}"
|
||||||
|
docker push "${BUILD_TO}:dev"
|
||||||
|
|
||||||
|
|
||||||
|
deploy-docker-manifest:
|
||||||
|
if: github.repository == 'esphome/esphome'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [deploy-docker]
|
||||||
|
steps:
|
||||||
|
- name: Enable experimental manifest support
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.docker
|
||||||
|
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
||||||
|
- name: Set TAG
|
||||||
|
run: |
|
||||||
|
TAG="${GITHUB_SHA:0:7}"
|
||||||
|
echo "TAG=${TAG}" >> $GITHUB_ENV
|
||||||
|
- name: Log in to docker hub
|
||||||
|
env:
|
||||||
|
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||||
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||||
|
- name: "Create the manifest"
|
||||||
|
run: |
|
||||||
|
docker manifest create esphome/esphome:${TAG} \
|
||||||
|
esphome/esphome-aarch64:${TAG} \
|
||||||
|
esphome/esphome-amd64:${TAG} \
|
||||||
|
esphome/esphome-armv7:${TAG}
|
||||||
|
docker manifest push esphome/esphome:${TAG}
|
||||||
|
|
||||||
|
docker manifest create esphome/esphome:dev \
|
||||||
|
esphome/esphome-aarch64:${TAG} \
|
||||||
|
esphome/esphome-amd64:${TAG} \
|
||||||
|
esphome/esphome-armv7:${TAG}
|
||||||
|
docker manifest push esphome/esphome:dev
|
||||||
309
.github/workflows/release.yml
vendored
Normal file
309
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
name: Publish Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# THE LINT/TEST JOBS ARE COPIED FROM ci.yaml
|
||||||
|
|
||||||
|
lint-clang-format:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||||
|
# doesn't have to be installed
|
||||||
|
container: esphome/esphome-lint:latest
|
||||||
|
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: Run clang-format
|
||||||
|
run: script/clang-format -i
|
||||||
|
- name: Suggest changes
|
||||||
|
run: script/ci-suggest-changes
|
||||||
|
|
||||||
|
lint-clang-tidy:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||||
|
# doesn't have to be installed
|
||||||
|
container: esphome/esphome-lint:latest
|
||||||
|
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
split: [1, 2, 3, 4]
|
||||||
|
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-tidy
|
||||||
|
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
|
||||||
|
- name: Suggest changes
|
||||||
|
run: script/ci-suggest-changes
|
||||||
|
|
||||||
|
lint-python:
|
||||||
|
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
||||||
|
# This way, all dependencies are cached via the cache action.
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
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-
|
||||||
|
- 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"
|
||||||
|
- name: Lint Custom
|
||||||
|
run: script/ci-custom.py
|
||||||
|
- name: Lint Python
|
||||||
|
run: script/lint-python
|
||||||
|
- name: Lint CODEOWNERS
|
||||||
|
run: script/build_codeowners.py --check
|
||||||
|
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
test:
|
||||||
|
- test1
|
||||||
|
- test2
|
||||||
|
- test3
|
||||||
|
- test4
|
||||||
|
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.test }}-${{ hashFiles('esphome/core_config.py') }}
|
||||||
|
restore-keys: |
|
||||||
|
test-home-platformio-${{ matrix.test }}-
|
||||||
|
- name: Set up environment
|
||||||
|
run: script/setup
|
||||||
|
|
||||||
|
- name: Register problem matchers
|
||||||
|
run: |
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||||
|
- run: esphome tests/${{ matrix.test }}.yaml compile
|
||||||
|
|
||||||
|
pytest:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
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-
|
||||||
|
- name: Set up environment
|
||||||
|
run: script/setup
|
||||||
|
- name: Install Github Actions annotator
|
||||||
|
run: pip install pytest-github-actions-annotate-failures
|
||||||
|
|
||||||
|
- name: Register problem matchers
|
||||||
|
run: |
|
||||||
|
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||||
|
- name: Run pytest
|
||||||
|
run: |
|
||||||
|
pytest \
|
||||||
|
-qq \
|
||||||
|
--durations=10 \
|
||||||
|
-o console_output_style=count \
|
||||||
|
tests
|
||||||
|
|
||||||
|
deploy-pypi:
|
||||||
|
name: Build and publish to PyPi
|
||||||
|
if: github.repository == 'esphome/esphome'
|
||||||
|
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
|
||||||
|
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: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch: [amd64, armv7, aarch64]
|
||||||
|
build_type: ["hassio", "docker"]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set TAG
|
||||||
|
run: |
|
||||||
|
TAG="${GITHUB_REF#refs/tags/v}"
|
||||||
|
echo "TAG=${TAG}" >> $GITHUB_ENV
|
||||||
|
- name: Set up env variables
|
||||||
|
run: |
|
||||||
|
base_version="3.0.0"
|
||||||
|
|
||||||
|
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
||||||
|
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
||||||
|
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
|
||||||
|
dockerfile="docker/Dockerfile.hassio"
|
||||||
|
else
|
||||||
|
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
|
||||||
|
build_to="esphome/esphome-${{ matrix.arch }}"
|
||||||
|
dockerfile="docker/Dockerfile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then
|
||||||
|
cache_tag="beta"
|
||||||
|
else
|
||||||
|
cache_tag="latest"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set env variables so these values don't need to be calculated again
|
||||||
|
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
|
||||||
|
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
|
||||||
|
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
|
||||||
|
echo "CACHE_TAG=${cache_tag}" >> $GITHUB_ENV
|
||||||
|
- name: Pull for cache
|
||||||
|
run: |
|
||||||
|
docker pull "${BUILD_TO}:${CACHE_TAG}" || true
|
||||||
|
- name: Register QEMU binfmt
|
||||||
|
run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
|
||||||
|
- run: |
|
||||||
|
docker build \
|
||||||
|
--build-arg "BUILD_FROM=${BUILD_FROM}" \
|
||||||
|
--build-arg "BUILD_VERSION=${TAG}" \
|
||||||
|
--tag "${BUILD_TO}:${TAG}" \
|
||||||
|
--cache-from "${BUILD_TO}:${CACHE_TAG}" \
|
||||||
|
--file "${DOCKERFILE}" \
|
||||||
|
.
|
||||||
|
- name: Log in to docker hub
|
||||||
|
env:
|
||||||
|
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||||
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||||
|
- run: docker push "${BUILD_TO}:${TAG}"
|
||||||
|
|
||||||
|
# Always publish to beta tag (also full releases)
|
||||||
|
- name: Publish docker beta tag
|
||||||
|
run: |
|
||||||
|
docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:beta"
|
||||||
|
docker push "${BUILD_TO}:beta"
|
||||||
|
|
||||||
|
- if: ${{ !github.event.release.prerelease }}
|
||||||
|
name: Publish docker latest tag
|
||||||
|
run: |
|
||||||
|
docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:latest"
|
||||||
|
docker push "${BUILD_TO}:latest"
|
||||||
|
|
||||||
|
deploy-docker-manifest:
|
||||||
|
if: github.repository == 'esphome/esphome'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [deploy-docker]
|
||||||
|
steps:
|
||||||
|
- name: Enable experimental manifest support
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.docker
|
||||||
|
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
||||||
|
- name: Set TAG
|
||||||
|
run: |
|
||||||
|
TAG="${GITHUB_REF#refs/tags/v}"
|
||||||
|
echo "TAG=${TAG}" >> $GITHUB_ENV
|
||||||
|
- name: Log in to docker hub
|
||||||
|
env:
|
||||||
|
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||||
|
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||||
|
- name: "Create the manifest"
|
||||||
|
run: |
|
||||||
|
docker manifest create esphome/esphome:${TAG} \
|
||||||
|
esphome/esphome-aarch64:${TAG} \
|
||||||
|
esphome/esphome-amd64:${TAG} \
|
||||||
|
esphome/esphome-armv7:${TAG}
|
||||||
|
docker manifest push esphome/esphome:${TAG}
|
||||||
|
|
||||||
|
- name: Publish docker beta tag
|
||||||
|
run: |
|
||||||
|
docker manifest create esphome/esphome:beta \
|
||||||
|
esphome/esphome-aarch64:${TAG} \
|
||||||
|
esphome/esphome-amd64:${TAG} \
|
||||||
|
esphome/esphome-armv7:${TAG}
|
||||||
|
docker manifest push esphome/esphome:beta
|
||||||
|
|
||||||
|
- name: Publish docker latest tag
|
||||||
|
if: ${{ !github.event.release.prerelease }}
|
||||||
|
run: |
|
||||||
|
docker manifest create esphome/esphome:latest \
|
||||||
|
esphome/esphome-aarch64:${TAG} \
|
||||||
|
esphome/esphome-amd64:${TAG} \
|
||||||
|
esphome/esphome-armv7:${TAG}
|
||||||
|
docker manifest push esphome/esphome:latest
|
||||||
|
|
||||||
|
deploy-hassio-repo:
|
||||||
|
if: github.repository == 'esphome/esphome'
|
||||||
|
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\":\"master\",\"inputs\":{\"version\":\"$TAG\"}}"
|
||||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -10,6 +10,9 @@ __pycache__/
|
|||||||
*.sublime-project
|
*.sublime-project
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# Intellij Idea
|
||||||
|
.idea
|
||||||
|
|
||||||
# Hide some OS X stuff
|
# Hide some OS X stuff
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.AppleDouble
|
.AppleDouble
|
||||||
@@ -48,8 +51,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 +81,8 @@ venv.bak/
|
|||||||
.pioenvs
|
.pioenvs
|
||||||
.piolibdeps
|
.piolibdeps
|
||||||
.pio
|
.pio
|
||||||
.vscode
|
.vscode/
|
||||||
|
!.vscode/tasks.json
|
||||||
CMakeListsPrivate.txt
|
CMakeListsPrivate.txt
|
||||||
CMakeLists.txt
|
CMakeLists.txt
|
||||||
|
|
||||||
@@ -114,3 +120,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.0'
|
|
||||||
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=master
|
||||||
|
- --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
|
|
||||||
11
.vscode/tasks.json
vendored
Normal file
11
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "run",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "python3 -m esphome config dashboard",
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
114
CODEOWNERS
Normal file
114
CODEOWNERS
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
# 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/api/* @OttoWinter
|
||||||
|
esphome/components/async_tcp/* @OttoWinter
|
||||||
|
esphome/components/atc_mithermometer/* @ahpohl
|
||||||
|
esphome/components/bang_bang/* @OttoWinter
|
||||||
|
esphome/components/binary_sensor/* @esphome/core
|
||||||
|
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/ct_clamp/* @jesserockz
|
||||||
|
esphome/components/debug/* @OttoWinter
|
||||||
|
esphome/components/dfplayer/* @glmnet
|
||||||
|
esphome/components/dht/* @OttoWinter
|
||||||
|
esphome/components/ds1307/* @badbadc0ffee
|
||||||
|
esphome/components/exposure_notifications/* @OttoWinter
|
||||||
|
esphome/components/ezo/* @ssieb
|
||||||
|
esphome/components/fastled_base/* @OttoWinter
|
||||||
|
esphome/components/globals/* @esphome/core
|
||||||
|
esphome/components/gpio/* @esphome/core
|
||||||
|
esphome/components/homeassistant/* @OttoWinter
|
||||||
|
esphome/components/i2c/* @esphome/core
|
||||||
|
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/network/* @esphome/core
|
||||||
|
esphome/components/nfc/* @jesserockz
|
||||||
|
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/sensor/* @esphome/core
|
||||||
|
esphome/components/shutdown/* @esphome/core
|
||||||
|
esphome/components/sim800l/* @glmnet
|
||||||
|
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/tcl112/* @glmnet
|
||||||
|
esphome/components/teleinfo/* @0hax
|
||||||
|
esphome/components/thermostat/* @kbx81
|
||||||
|
esphome/components/time/* @OttoWinter
|
||||||
|
esphome/components/tm1637/* @glmnet
|
||||||
|
esphome/components/tmp102/* @timsavage
|
||||||
|
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
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
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
|
||||||
|
recursive-include esphome LICENSE.txt
|
||||||
|
|||||||
@@ -1,12 +1,30 @@
|
|||||||
ARG BUILD_FROM=esphome/esphome-base-amd64:2.0.0
|
ARG BUILD_FROM=esphome/esphome-base-amd64:3.0.0
|
||||||
FROM ${BUILD_FROM}
|
FROM ${BUILD_FROM}
|
||||||
|
|
||||||
|
# First install requirements to leverage caching when requirements don't change
|
||||||
|
COPY requirements.txt docker/platformio_install_deps.py platformio.ini /
|
||||||
|
RUN \
|
||||||
|
pip3 install --no-cache-dir -r /requirements.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"]
|
||||||
|
# When no arguments given, start the dashboard in the workdir
|
||||||
CMD ["/config", "dashboard"]
|
CMD ["/config", "dashboard"]
|
||||||
|
|||||||
13
docker/Dockerfile.dev
Normal file
13
docker/Dockerfile.dev
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
FROM esphome/esphome-base-amd64:3.0.0
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
python3-wheel \
|
||||||
|
net-tools \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /workspaces
|
||||||
|
ENV SHELL /bin/bash
|
||||||
@@ -1,11 +1,17 @@
|
|||||||
ARG BUILD_FROM
|
ARG BUILD_FROM
|
||||||
FROM ${BUILD_FROM}
|
FROM ${BUILD_FROM}
|
||||||
|
|
||||||
|
# First install requirements to leverage caching when requirements don't change
|
||||||
|
COPY requirements.txt docker/platformio_install_deps.py platformio.ini /
|
||||||
|
RUN \
|
||||||
|
pip3 install --no-cache-dir -r /requirements.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,18 +1,9 @@
|
|||||||
FROM esphome/esphome-base-amd64:2.0.0
|
FROM esphome/esphome-lint-base:3.0.0
|
||||||
|
|
||||||
|
COPY requirements.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_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
|
|
||||||
|
|
||||||
VOLUME ["/esphome"]
|
VOLUME ["/esphome"]
|
||||||
WORKDIR /esphome
|
WORKDIR /esphome
|
||||||
|
|||||||
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
@@ -1,5 +1,3 @@
|
|||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import functools
|
import functools
|
||||||
import logging
|
import logging
|
||||||
@@ -10,44 +8,46 @@ 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,
|
||||||
|
CONF_BROKER,
|
||||||
|
CONF_LOGGER,
|
||||||
|
CONF_OTA,
|
||||||
|
CONF_PASSWORD,
|
||||||
|
CONF_PORT,
|
||||||
|
CONF_ESPHOME,
|
||||||
|
CONF_PLATFORMIO_OPTIONS,
|
||||||
|
)
|
||||||
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
|
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
|
||||||
from esphome.helpers import color, indent
|
from esphome.helpers import color, indent
|
||||||
from esphome.py_compat import IS_PY2, safe_input
|
from esphome.util import (
|
||||||
from esphome.util import run_external_command, run_external_process, safe_print, list_yaml_files
|
run_external_command,
|
||||||
|
run_external_process,
|
||||||
|
safe_print,
|
||||||
|
list_yaml_files,
|
||||||
|
get_serial_ports,
|
||||||
|
)
|
||||||
|
|
||||||
_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 +57,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("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 +81,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 +95,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,17 +108,18 @@ 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):
|
||||||
@@ -127,12 +128,10 @@ def wrap_to_code(name, comp):
|
|||||||
@functools.wraps(comp.to_code)
|
@functools.wraps(comp.to_code)
|
||||||
@coroutine_with_priority(coro.priority)
|
@coroutine_with_priority(coro.priority)
|
||||||
def wrapped(conf):
|
def wrapped(conf):
|
||||||
cg.add(cg.LineComment(u"{}:".format(name)))
|
cg.add(cg.LineComment(f"{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)
|
yield coro(conf)
|
||||||
|
|
||||||
@@ -140,6 +139,11 @@ def wrap_to_code(name, comp):
|
|||||||
|
|
||||||
|
|
||||||
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 +153,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 +171,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 +223,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 +236,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,7 +258,9 @@ 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):
|
def setup_log(debug=False, quiet=False):
|
||||||
@@ -228,25 +273,32 @@ def setup_log(debug=False, quiet=False):
|
|||||||
log_level = logging.INFO
|
log_level = logging.INFO
|
||||||
logging.basicConfig(level=log_level)
|
logging.basicConfig(level=log_level)
|
||||||
fmt = "%(levelname)s %(message)s"
|
fmt = "%(levelname)s %(message)s"
|
||||||
colorfmt = "%(log_color)s{}%(reset)s".format(fmt)
|
colorfmt = f"%(log_color)s{fmt}%(reset)s"
|
||||||
datefmt = '%H:%M:%S'
|
datefmt = "%H:%M:%S"
|
||||||
|
|
||||||
logging.getLogger('urllib3').setLevel(logging.WARNING)
|
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
import colorama
|
||||||
|
|
||||||
|
colorama.init(strip=True)
|
||||||
|
|
||||||
from colorlog import ColoredFormatter
|
from colorlog import ColoredFormatter
|
||||||
logging.getLogger().handlers[0].setFormatter(ColoredFormatter(
|
|
||||||
colorfmt,
|
logging.getLogger().handlers[0].setFormatter(
|
||||||
datefmt=datefmt,
|
ColoredFormatter(
|
||||||
reset=True,
|
colorfmt,
|
||||||
log_colors={
|
datefmt=datefmt,
|
||||||
'DEBUG': 'cyan',
|
reset=True,
|
||||||
'INFO': 'green',
|
log_colors={
|
||||||
'WARNING': 'yellow',
|
"DEBUG": "cyan",
|
||||||
'ERROR': 'red',
|
"INFO": "green",
|
||||||
'CRITICAL': 'red',
|
"WARNING": "yellow",
|
||||||
}
|
"ERROR": "red",
|
||||||
))
|
"CRITICAL": "red",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -268,6 +320,8 @@ def command_config(args, config):
|
|||||||
def command_vscode(args):
|
def command_vscode(args):
|
||||||
from esphome import vscode
|
from esphome import vscode
|
||||||
|
|
||||||
|
logging.disable(logging.INFO)
|
||||||
|
logging.disable(logging.WARNING)
|
||||||
CORE.config_path = args.configuration[0]
|
CORE.config_path = args.configuration[0]
|
||||||
vscode.read_config(args)
|
vscode.read_config(args)
|
||||||
|
|
||||||
@@ -277,28 +331,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.upload_port,
|
||||||
|
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.serial_port,
|
||||||
|
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 +373,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.upload_port,
|
||||||
|
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.upload_port,
|
||||||
|
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 +408,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,140 +436,195 @@ 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("cyan", f)))
|
||||||
print('-' * twidth)
|
print("-" * twidth)
|
||||||
print()
|
print()
|
||||||
rc = run_external_process('esphome', '--dashboard', f, 'run', '--no-logs')
|
rc = run_external_process(
|
||||||
|
"esphome", "--dashboard", f, "run", "--no-logs", "--upload-port", "OTA"
|
||||||
|
)
|
||||||
if rc == 0:
|
if rc == 0:
|
||||||
print_bar("[{}] {}".format(color('bold_green', 'SUCCESS'), f))
|
print_bar("[{}] {}".format(color("bold_green", "SUCCESS"), f))
|
||||||
success[f] = True
|
success[f] = True
|
||||||
else:
|
else:
|
||||||
print_bar("[{}] {}".format(color('bold_red', 'ERROR'), f))
|
print_bar("[{}] {}".format(color("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("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("green", "SUCCESS")))
|
||||||
else:
|
else:
|
||||||
print(" - {}: {}".format(f, color('bold_red', 'FAILED')))
|
print(" - {}: {}".format(f, color("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__))
|
parser = argparse.ArgumentParser(description=f"ESPHome v{const.__version__}")
|
||||||
parser.add_argument('-v', '--verbose', help="Enable verbose esphome logs.",
|
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')
|
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='*')
|
)
|
||||||
|
parser.add_argument("--dashboard", help=argparse.SUPPRESS, action="store_true")
|
||||||
|
parser.add_argument(
|
||||||
|
"-s",
|
||||||
|
"--substitution",
|
||||||
|
nargs=2,
|
||||||
|
action="append",
|
||||||
|
help="Add a substitution",
|
||||||
|
metavar=("key", "value"),
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"configuration", help="Your YAML configuration file.", nargs="*"
|
||||||
|
)
|
||||||
|
|
||||||
subparsers = parser.add_subparsers(help='Commands', dest='command')
|
subparsers = parser.add_subparsers(help="Commands", dest="command")
|
||||||
subparsers.required = True
|
subparsers.required = True
|
||||||
subparsers.add_parser('config', help='Validate the configuration and spit it out.')
|
subparsers.add_parser("config", help="Validate the configuration and spit it out.")
|
||||||
|
|
||||||
parser_compile = subparsers.add_parser('compile',
|
parser_compile = subparsers.add_parser(
|
||||||
help='Read the configuration and compile a program.')
|
"compile", help="Read the configuration and compile a program."
|
||||||
parser_compile.add_argument('--only-generate',
|
)
|
||||||
help="Only generate source code, do not compile.",
|
parser_compile.add_argument(
|
||||||
action='store_true')
|
"--only-generate",
|
||||||
|
help="Only generate source code, do not compile.",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
|
||||||
parser_upload = subparsers.add_parser('upload', help='Validate the configuration '
|
parser_upload = subparsers.add_parser(
|
||||||
'and upload the latest binary.')
|
"upload", help="Validate the configuration " "and upload the latest binary."
|
||||||
parser_upload.add_argument('--upload-port', help="Manually specify the upload port to use. "
|
)
|
||||||
"For example /dev/cu.SLAB_USBtoUART.")
|
parser_upload.add_argument(
|
||||||
|
"--upload-port",
|
||||||
|
help="Manually specify the upload port to use. "
|
||||||
|
"For example /dev/cu.SLAB_USBtoUART.",
|
||||||
|
)
|
||||||
|
|
||||||
parser_logs = subparsers.add_parser('logs', help='Validate the configuration '
|
parser_logs = subparsers.add_parser(
|
||||||
'and show all MQTT logs.')
|
"logs", help="Validate the configuration " "and show all MQTT logs."
|
||||||
parser_logs.add_argument('--topic', help='Manually set the topic to subscribe to.')
|
)
|
||||||
parser_logs.add_argument('--username', help='Manually set the username.')
|
parser_logs.add_argument("--topic", help="Manually set the topic to subscribe to.")
|
||||||
parser_logs.add_argument('--password', help='Manually set the password.')
|
parser_logs.add_argument("--username", help="Manually set the username.")
|
||||||
parser_logs.add_argument('--client-id', help='Manually set the client id.')
|
parser_logs.add_argument("--password", help="Manually set the password.")
|
||||||
parser_logs.add_argument('--serial-port', help="Manually specify a serial port to use"
|
parser_logs.add_argument("--client-id", help="Manually set the client id.")
|
||||||
"For example /dev/cu.SLAB_USBtoUART.")
|
parser_logs.add_argument(
|
||||||
|
"--serial-port",
|
||||||
|
help="Manually specify a serial port to use"
|
||||||
|
"For example /dev/cu.SLAB_USBtoUART.",
|
||||||
|
)
|
||||||
|
|
||||||
parser_run = subparsers.add_parser('run', help='Validate the configuration, create a binary, '
|
parser_run = subparsers.add_parser(
|
||||||
'upload it, and start MQTT logs.')
|
"run",
|
||||||
parser_run.add_argument('--upload-port', help="Manually specify the upload port/ip to use. "
|
help="Validate the configuration, create a binary, "
|
||||||
"For example /dev/cu.SLAB_USBtoUART.")
|
"upload it, and start MQTT logs.",
|
||||||
parser_run.add_argument('--no-logs', help='Disable starting MQTT logs.',
|
)
|
||||||
action='store_true')
|
parser_run.add_argument(
|
||||||
parser_run.add_argument('--topic', help='Manually set the topic to subscribe to for logs.')
|
"--upload-port",
|
||||||
parser_run.add_argument('--username', help='Manually set the MQTT username for logs.')
|
help="Manually specify the upload port/ip to use. "
|
||||||
parser_run.add_argument('--password', help='Manually set the MQTT password for logs.')
|
"For example /dev/cu.SLAB_USBtoUART.",
|
||||||
parser_run.add_argument('--client-id', help='Manually set the client id for logs.')
|
)
|
||||||
|
parser_run.add_argument(
|
||||||
|
"--no-logs", help="Disable starting MQTT logs.", action="store_true"
|
||||||
|
)
|
||||||
|
parser_run.add_argument(
|
||||||
|
"--topic", help="Manually set the topic to subscribe to for logs."
|
||||||
|
)
|
||||||
|
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_run.add_argument("--client-id", help="Manually set the client id for logs.")
|
||||||
|
|
||||||
parser_clean = subparsers.add_parser('clean-mqtt', help="Helper to clear an MQTT topic from "
|
parser_clean = subparsers.add_parser(
|
||||||
"retain messages.")
|
"clean-mqtt", help="Helper to clear an MQTT topic from " "retain messages."
|
||||||
parser_clean.add_argument('--topic', help='Manually set the topic to subscribe to.')
|
)
|
||||||
parser_clean.add_argument('--username', help='Manually set the username.')
|
parser_clean.add_argument("--topic", help="Manually set the topic to subscribe to.")
|
||||||
parser_clean.add_argument('--password', help='Manually set the password.')
|
parser_clean.add_argument("--username", help="Manually set the username.")
|
||||||
parser_clean.add_argument('--client-id', help='Manually set the client id.')
|
parser_clean.add_argument("--password", help="Manually set the password.")
|
||||||
|
parser_clean.add_argument("--client-id", help="Manually set the client id.")
|
||||||
|
|
||||||
subparsers.add_parser('wizard', help="A helpful setup wizard that will guide "
|
subparsers.add_parser(
|
||||||
"you through setting up esphome.")
|
"wizard",
|
||||||
|
help="A helpful setup wizard that will guide "
|
||||||
|
"you through setting up esphome.",
|
||||||
|
)
|
||||||
|
|
||||||
subparsers.add_parser('mqtt-fingerprint', help="Get the SSL fingerprint from a MQTT broker.")
|
subparsers.add_parser(
|
||||||
|
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
|
||||||
|
)
|
||||||
|
|
||||||
subparsers.add_parser('version', help="Print the esphome version and exit.")
|
subparsers.add_parser("version", help="Print the esphome version and exit.")
|
||||||
|
|
||||||
subparsers.add_parser('clean', help="Delete all temporary build files.")
|
subparsers.add_parser("clean", help="Delete all temporary build files.")
|
||||||
|
|
||||||
dashboard = subparsers.add_parser('dashboard',
|
dashboard = subparsers.add_parser(
|
||||||
help="Create a simple web server for a dashboard.")
|
"dashboard", help="Create a simple web server for a dashboard."
|
||||||
dashboard.add_argument("--port", help="The HTTP port to open connections on. Defaults to 6052.",
|
)
|
||||||
type=int, default=6052)
|
dashboard.add_argument(
|
||||||
dashboard.add_argument("--username", help="The optional username to require "
|
"--port",
|
||||||
"for authentication.",
|
help="The HTTP port to open connections on. Defaults to 6052.",
|
||||||
type=str, default='')
|
type=int,
|
||||||
dashboard.add_argument("--password", help="The optional password to require "
|
default=6052,
|
||||||
"for authentication.",
|
)
|
||||||
type=str, default='')
|
dashboard.add_argument(
|
||||||
dashboard.add_argument("--open-ui", help="Open the dashboard UI in a browser.",
|
"--username",
|
||||||
action='store_true')
|
help="The optional username to require " "for authentication.",
|
||||||
dashboard.add_argument("--hassio",
|
type=str,
|
||||||
help=argparse.SUPPRESS,
|
default="",
|
||||||
action="store_true")
|
)
|
||||||
dashboard.add_argument("--socket",
|
dashboard.add_argument(
|
||||||
help="Make the dashboard serve under a unix socket", type=str)
|
"--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)
|
vscode = subparsers.add_parser("vscode", help=argparse.SUPPRESS)
|
||||||
vscode.add_argument('--ace', action='store_true')
|
vscode.add_argument("--ace", action="store_true")
|
||||||
|
|
||||||
subparsers.add_parser('update-all', help=argparse.SUPPRESS)
|
subparsers.add_parser("update-all", help=argparse.SUPPRESS)
|
||||||
|
|
||||||
return parser.parse_args(argv[1:])
|
return parser.parse_args(argv[1:])
|
||||||
|
|
||||||
@@ -505,10 +634,17 @@ 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.command != "version" and not args.configuration:
|
||||||
_LOGGER.error("Missing configuration parameter, see esphome --help.")
|
_LOGGER.error("Missing configuration parameter, see esphome --help.")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
if sys.version_info < (3, 6, 0):
|
||||||
|
_LOGGER.error(
|
||||||
|
"You're running ESPHome with Python <3.6. ESPHome is no longer compatible "
|
||||||
|
"with this Python version. Please reinstall ESPHome with Python 3.6+"
|
||||||
|
)
|
||||||
|
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)
|
||||||
@@ -520,13 +656,13 @@ def run_esphome(argv):
|
|||||||
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)
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -14,7 +14,6 @@ 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, color
|
||||||
from esphome.py_compat import text_type, IS_PY2, byte_to_bytes, char_to_byte
|
|
||||||
from esphome.util import safe_print
|
from esphome.util import safe_print
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -67,16 +66,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 +83,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 +177,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 +193,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 +202,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 +258,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 +272,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 +306,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 +324,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 +362,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 +382,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 +431,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 +447,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 +463,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)')
|
"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,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.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,
|
||||||
|
CONF_CONDITION,
|
||||||
|
CONF_ELSE,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_THEN,
|
||||||
|
CONF_TRIGGER_ID,
|
||||||
|
CONF_TYPE_ID,
|
||||||
|
CONF_TIME,
|
||||||
|
)
|
||||||
from esphome.core import coroutine
|
from esphome.core import coroutine
|
||||||
|
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 +44,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 +95,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 +109,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,47 +128,59 @@ 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):
|
def and_condition_to_code(config, condition_id, template_arg, args):
|
||||||
conditions = yield build_condition_list(config, template_arg, args)
|
conditions = yield build_condition_list(config, template_arg, args)
|
||||||
yield cg.new_Pvariable(condition_id, template_arg, conditions)
|
yield 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):
|
def or_condition_to_code(config, condition_id, template_arg, args):
|
||||||
conditions = yield build_condition_list(config, template_arg, args)
|
conditions = yield build_condition_list(config, template_arg, args)
|
||||||
yield cg.new_Pvariable(condition_id, template_arg, conditions)
|
yield 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):
|
def not_condition_to_code(config, condition_id, template_arg, args):
|
||||||
condition = yield build_condition(config, template_arg, args)
|
condition = yield build_condition(config, template_arg, args)
|
||||||
yield cg.new_Pvariable(condition_id, template_arg, condition)
|
yield cg.new_Pvariable(condition_id, template_arg, condition)
|
||||||
|
|
||||||
|
|
||||||
@register_condition('lambda', LambdaCondition, cv.lambda_)
|
@register_condition("lambda", LambdaCondition, cv.lambda_)
|
||||||
def lambda_condition_to_code(config, condition_id, template_arg, args):
|
def lambda_condition_to_code(config, condition_id, template_arg, args):
|
||||||
lambda_ = yield cg.process_lambda(config, args, return_type=bool)
|
lambda_ = yield cg.process_lambda(config, args, return_type=bool)
|
||||||
yield cg.new_Pvariable(condition_id, template_arg, lambda_)
|
yield 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(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_TIME): cv.templatable(
|
||||||
|
cv.positive_time_period_milliseconds
|
||||||
|
),
|
||||||
|
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
|
)
|
||||||
def for_condition_to_code(config, condition_id, template_arg, args):
|
def for_condition_to_code(config, condition_id, template_arg, args):
|
||||||
condition = yield build_condition(config[CONF_CONDITION], cg.TemplateArguments(), [])
|
condition = yield 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)
|
yield cg.register_component(var, config)
|
||||||
templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32)
|
templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32)
|
||||||
@@ -153,7 +188,9 @@ def for_condition_to_code(config, condition_id, template_arg, args):
|
|||||||
yield var
|
yield var
|
||||||
|
|
||||||
|
|
||||||
@register_action('delay', DelayAction, cv.templatable(cv.positive_time_period_milliseconds))
|
@register_action(
|
||||||
|
"delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds)
|
||||||
|
)
|
||||||
def delay_action_to_code(config, action_id, template_arg, args):
|
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, {})
|
yield cg.register_component(var, {})
|
||||||
@@ -162,11 +199,18 @@ def delay_action_to_code(config, action_id, template_arg, args):
|
|||||||
yield var
|
yield 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)))
|
{
|
||||||
|
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||||
|
cv.Optional(CONF_THEN): validate_action_list,
|
||||||
|
cv.Optional(CONF_ELSE): validate_action_list,
|
||||||
|
},
|
||||||
|
cv.has_at_least_one_key(CONF_THEN, CONF_ELSE),
|
||||||
|
),
|
||||||
|
)
|
||||||
def if_action_to_code(config, action_id, template_arg, args):
|
def if_action_to_code(config, action_id, template_arg, args):
|
||||||
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
|
conditions = yield 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)
|
||||||
@@ -179,10 +223,16 @@ def if_action_to_code(config, action_id, template_arg, args):
|
|||||||
yield var
|
yield 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(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_CONDITION): validate_potentially_and_condition,
|
||||||
|
cv.Required(CONF_THEN): validate_action_list,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
def while_action_to_code(config, action_id, template_arg, args):
|
def while_action_to_code(config, action_id, template_arg, args):
|
||||||
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
|
conditions = yield 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)
|
||||||
@@ -192,15 +242,17 @@ def while_action_to_code(config, action_id, template_arg, args):
|
|||||||
|
|
||||||
|
|
||||||
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):
|
def wait_until_action_to_code(config, action_id, template_arg, args):
|
||||||
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
|
conditions = yield 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)
|
||||||
@@ -208,15 +260,21 @@ def wait_until_action_to_code(config, action_id, template_arg, args):
|
|||||||
yield var
|
yield var
|
||||||
|
|
||||||
|
|
||||||
@register_action('lambda', LambdaAction, cv.lambda_)
|
@register_action("lambda", LambdaAction, cv.lambda_)
|
||||||
def lambda_action_to_code(config, action_id, template_arg, args):
|
def lambda_action_to_code(config, action_id, template_arg, args):
|
||||||
lambda_ = yield cg.process_lambda(config, args, return_type=cg.void)
|
lambda_ = yield cg.process_lambda(config, args, return_type=cg.void)
|
||||||
yield cg.new_Pvariable(action_id, template_arg, lambda_)
|
yield 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,
|
||||||
|
maybe_simple_id(
|
||||||
|
{
|
||||||
|
cv.Required(CONF_ID): cv.use_id(cg.PollingComponent),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
def component_update_action_to_code(config, action_id, template_arg, args):
|
def component_update_action_to_code(config, action_id, template_arg, args):
|
||||||
comp = yield cg.get_variable(config[CONF_ID])
|
comp = yield cg.get_variable(config[CONF_ID])
|
||||||
yield cg.new_Pvariable(action_id, template_arg, comp)
|
yield cg.new_Pvariable(action_id, template_arg, comp)
|
||||||
@@ -224,7 +282,9 @@ def component_update_action_to_code(config, action_id, template_arg, args):
|
|||||||
|
|
||||||
@coroutine
|
@coroutine
|
||||||
def build_action(full_config, template_arg, args):
|
def build_action(full_config, template_arg, args):
|
||||||
registry_entry, config = cg.extract_registry_entry_config(ACTION_REGISTRY, full_config)
|
registry_entry, config = cg.extract_registry_entry_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)
|
yield builder(config, action_id, template_arg, args)
|
||||||
@@ -241,7 +301,9 @@ def build_action_list(config, templ, arg_type):
|
|||||||
|
|
||||||
@coroutine
|
@coroutine
|
||||||
def build_condition(full_config, template_arg, args):
|
def build_condition(full_config, template_arg, args):
|
||||||
registry_entry, config = cg.extract_registry_entry_config(CONDITION_REGISTRY, full_config)
|
registry_entry, config = cg.extract_registry_entry_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)
|
yield builder(config, action_id, template_arg, args)
|
||||||
|
|||||||
@@ -9,18 +9,71 @@
|
|||||||
|
|
||||||
# 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,
|
||||||
|
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,
|
||||||
|
int32,
|
||||||
|
const_char_ptr,
|
||||||
|
NAN,
|
||||||
|
esphome_ns,
|
||||||
|
App,
|
||||||
|
Nameable,
|
||||||
|
Component,
|
||||||
|
ComponentPtr,
|
||||||
|
PollingComponent,
|
||||||
|
Application,
|
||||||
|
optional,
|
||||||
|
arduino_json_ns,
|
||||||
|
JsonObject,
|
||||||
|
JsonObjectRef,
|
||||||
|
JsonObjectConstRef,
|
||||||
|
Controller,
|
||||||
|
GPIOPin,
|
||||||
|
)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ void A4988::setup() {
|
|||||||
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,15 +5,17 @@ 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):
|
def to_code(config):
|
||||||
|
|||||||
0
esphome/components/ac_dimmer/__init__.py
Normal file
0
esphome/components/ac_dimmer/__init__.py
Normal file
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 *TAG = "ac_dimmer";
|
||||||
|
|
||||||
|
// Global array to store dimmer objects
|
||||||
|
static AcDimmerDataStore *all_dimmers[32];
|
||||||
|
|
||||||
|
/// 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 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)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
|
||||||
|
# override default min power to 10%
|
||||||
|
if CONF_MIN_POWER not in config:
|
||||||
|
config[CONF_MIN_POWER] = 0.1
|
||||||
|
yield output.register_output(var, config)
|
||||||
|
|
||||||
|
pin = yield cg.gpio_pin_expression(config[CONF_GATE_PIN])
|
||||||
|
cg.add(var.set_gate_pin(pin))
|
||||||
|
pin = yield 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]))
|
||||||
28
esphome/components/adalight/__init__.py
Normal file
28
esphome/components/adalight/__init__.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
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)},
|
||||||
|
)
|
||||||
|
def adalight_light_effect_to_code(config, effect_id):
|
||||||
|
effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
|
||||||
|
yield uart.register_uart_device(effect, config)
|
||||||
|
|
||||||
|
yield 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 *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"]
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuati
|
|||||||
|
|
||||||
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_);
|
analogSetPinAttenuation(this->pin_, this->attenuation_);
|
||||||
@@ -58,7 +60,7 @@ 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;
|
float value_v = analogRead(this->pin_) / 4095.0f; // NOLINT
|
||||||
switch (this->attenuation_) {
|
switch (this->attenuation_) {
|
||||||
case ADC_0db:
|
case ADC_0db:
|
||||||
value_v *= 1.1;
|
value_v *= 1.1;
|
||||||
@@ -80,7 +82,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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,36 +2,51 @@ 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,
|
||||||
|
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_0db,
|
||||||
'2.5db': cg.global_ns.ADC_2_5db,
|
"2.5db": cg.global_ns.ADC_2_5db,
|
||||||
'6db': cg.global_ns.ADC_6db,
|
"6db": cg.global_ns.ADC_6db,
|
||||||
'11db': cg.global_ns.ADC_11db,
|
"11db": cg.global_ns.ADC_11db,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE)
|
||||||
cv.Required(CONF_PIN): validate_adc_pin,
|
.extend(
|
||||||
cv.SplitDefault(CONF_ATTENUATION, esp32='0db'):
|
{
|
||||||
cv.All(cv.only_on_esp32, cv.enum(ATTENUATION_MODES, lower=True)),
|
cv.GenerateID(): cv.declare_id(ADCSensor),
|
||||||
}).extend(cv.polling_component_schema('60s'))
|
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):
|
def to_code(config):
|
||||||
@@ -39,8 +54,8 @@ def to_code(config):
|
|||||||
yield cg.register_component(var, config)
|
yield cg.register_component(var, config)
|
||||||
yield sensor.register_sensor(var, config)
|
yield 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
esphome/components/addressable_light/__init__.py
Normal file
0
esphome/components/addressable_light/__init__.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include "addressable_light_display.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace addressable_light {
|
||||||
|
|
||||||
|
static const char* 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),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
wrapped_light = yield 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))
|
||||||
|
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield display.register_display(var, config)
|
||||||
|
|
||||||
|
if CONF_PIXEL_MAPPER in config:
|
||||||
|
pixel_mapper_template_ = yield 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_ = yield cg.process_lambda(
|
||||||
|
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
|
||||||
|
)
|
||||||
|
cg.add(var.set_writer(lambda_))
|
||||||
@@ -8,6 +8,9 @@ static const char *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,7 +21,7 @@ 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); \
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,28 +1,55 @@
|
|||||||
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,
|
||||||
|
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
|
||||||
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
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
|
||||||
|
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
|
||||||
|
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
|
||||||
|
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x38))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
@@ -30,10 +57,18 @@ def to_code(config):
|
|||||||
yield cg.register_component(var, config)
|
yield cg.register_component(var, config)
|
||||||
yield i2c.register_i2c_device(var, config)
|
yield 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 = yield sensor.new_sensor(conf)
|
||||||
cg.add(getattr(var, 'set_{}_sensor'.format(key))(sens))
|
cg.add(getattr(var, f"set_{key}_sensor")(sens))
|
||||||
|
|||||||
@@ -3,18 +3,24 @@ 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):
|
def to_code(config):
|
||||||
|
|||||||
@@ -1,54 +1,67 @@
|
|||||||
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,
|
||||||
|
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(UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE)
|
||||||
cv.GenerateID(CONF_ADS1115_ID): cv.use_id(ADS1115Component),
|
.extend(
|
||||||
cv.Required(CONF_MULTIPLEXER): cv.enum(MUX, upper=True, space='_'),
|
{
|
||||||
cv.Required(CONF_GAIN): validate_gain,
|
cv.GenerateID(): cv.declare_id(ADS1115Sensor),
|
||||||
}).extend(cv.polling_component_schema('60s'))
|
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):
|
def to_code(config):
|
||||||
|
|||||||
0
esphome/components/aht10/__init__.py
Normal file
0
esphome/components/aht10/__init__.py
Normal file
127
esphome/components/aht10/aht10.cpp
Normal file
127
esphome/components/aht10/aht10.cpp
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
// 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 *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());
|
||||||
|
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
|
||||||
48
esphome/components/aht10/sensor.py
Normal file
48
esphome/components/aht10/sensor.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
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,
|
||||||
|
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
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
|
UNIT_PERCENT, ICON_EMPTY, 2, DEVICE_CLASS_HUMIDITY
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x38))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield i2c.register_i2c_device(var, config)
|
||||||
|
|
||||||
|
if CONF_TEMPERATURE in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||||
|
cg.add(var.set_temperature_sensor(sens))
|
||||||
|
|
||||||
|
if CONF_HUMIDITY in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
|
||||||
|
cg.add(var.set_humidity_sensor(sens))
|
||||||
@@ -1,19 +1,39 @@
|
|||||||
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,
|
||||||
|
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
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
|
UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.polling_component_schema("60s"))
|
||||||
|
.extend(i2c.i2c_device_schema(0x5C))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
|
|||||||
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"]
|
||||||
|
|
||||||
|
|
||||||
|
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]],
|
||||||
|
)
|
||||||
@@ -3,18 +3,24 @@ 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):
|
def to_code(config):
|
||||||
|
|||||||
@@ -4,20 +4,24 @@ 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):
|
def to_code(config):
|
||||||
|
|||||||
@@ -1,23 +1,27 @@
|
|||||||
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, 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
|
||||||
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):
|
def to_code(config):
|
||||||
|
|||||||
@@ -2,44 +2,69 @@ 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)
|
||||||
@@ -61,28 +86,36 @@ 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)
|
yield 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",
|
||||||
|
HomeAssistantServiceCallAction,
|
||||||
|
HOMEASSISTANT_SERVICE_ACTION_SCHEMA,
|
||||||
|
)
|
||||||
def homeassistant_service_to_code(config, action_id, template_arg, args):
|
def homeassistant_service_to_code(config, action_id, template_arg, args):
|
||||||
serv = yield cg.get_variable(config[CONF_ID])
|
serv = yield 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)
|
||||||
@@ -102,23 +135,30 @@ def homeassistant_service_to_code(config, action_id, template_arg, args):
|
|||||||
|
|
||||||
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",
|
||||||
|
HomeAssistantServiceCallAction,
|
||||||
|
HOMEASSISTANT_EVENT_ACTION_SCHEMA,
|
||||||
|
)
|
||||||
def homeassistant_event_to_code(config, action_id, template_arg, args):
|
def homeassistant_event_to_code(config, action_id, template_arg, args):
|
||||||
serv = yield cg.get_variable(config[CONF_ID])
|
serv = yield 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)
|
||||||
@@ -136,6 +176,29 @@ def homeassistant_event_to_code(config, action_id, template_arg, args):
|
|||||||
yield var
|
yield var
|
||||||
|
|
||||||
|
|
||||||
@automation.register_condition('api.connected', APIConnectedCondition, {})
|
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
|
||||||
|
serv = yield 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 = yield cg.templatable(config[CONF_TAG], args, cg.std_string)
|
||||||
|
cg.add(var.add_data("tag_id", templ))
|
||||||
|
yield var
|
||||||
|
|
||||||
|
|
||||||
|
@automation.register_condition("api.connected", APIConnectedCondition, {})
|
||||||
def api_connected_to_code(config, condition_id, template_arg, args):
|
def api_connected_to_code(config, condition_id, template_arg, args):
|
||||||
yield cg.new_Pvariable(condition_id, template_arg)
|
yield cg.new_Pvariable(condition_id, template_arg)
|
||||||
|
|||||||
@@ -46,6 +46,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
|
||||||
@@ -216,6 +217,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 +302,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 +323,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 +336,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 ====================
|
||||||
@@ -406,6 +422,8 @@ message ListEntitiesSensorResponse {
|
|||||||
string icon = 5;
|
string icon = 5;
|
||||||
string unit_of_measurement = 6;
|
string unit_of_measurement = 6;
|
||||||
int32 accuracy_decimals = 7;
|
int32 accuracy_decimals = 7;
|
||||||
|
bool force_update = 8;
|
||||||
|
string device_class = 9;
|
||||||
}
|
}
|
||||||
message SensorStateResponse {
|
message SensorStateResponse {
|
||||||
option (id) = 25;
|
option (id) = 25;
|
||||||
@@ -415,6 +433,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 ====================
|
||||||
@@ -471,6 +492,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 ====================
|
||||||
@@ -643,12 +667,34 @@ enum ClimateMode {
|
|||||||
CLIMATE_MODE_AUTO = 1;
|
CLIMATE_MODE_AUTO = 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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
message ListEntitiesClimateResponse {
|
message ListEntitiesClimateResponse {
|
||||||
option (id) = 46;
|
option (id) = 46;
|
||||||
@@ -668,6 +714,8 @@ message ListEntitiesClimateResponse {
|
|||||||
float visual_temperature_step = 10;
|
float visual_temperature_step = 10;
|
||||||
bool supports_away = 11;
|
bool supports_away = 11;
|
||||||
bool supports_action = 12;
|
bool supports_action = 12;
|
||||||
|
repeated ClimateFanMode supported_fan_modes = 13;
|
||||||
|
repeated ClimateSwingMode supported_swing_modes = 14;
|
||||||
}
|
}
|
||||||
message ClimateStateResponse {
|
message ClimateStateResponse {
|
||||||
option (id) = 47;
|
option (id) = 47;
|
||||||
@@ -683,6 +731,8 @@ message ClimateStateResponse {
|
|||||||
float target_temperature_high = 6;
|
float target_temperature_high = 6;
|
||||||
bool away = 7;
|
bool away = 7;
|
||||||
ClimateAction action = 8;
|
ClimateAction action = 8;
|
||||||
|
ClimateFanMode fan_mode = 9;
|
||||||
|
ClimateSwingMode swing_mode = 10;
|
||||||
}
|
}
|
||||||
message ClimateCommandRequest {
|
message ClimateCommandRequest {
|
||||||
option (id) = 48;
|
option (id) = 48;
|
||||||
@@ -701,4 +751,8 @@ message ClimateCommandRequest {
|
|||||||
float target_temperature_high = 9;
|
float target_temperature_high = 9;
|
||||||
bool has_away = 10;
|
bool has_away = 10;
|
||||||
bool away = 11;
|
bool away = 11;
|
||||||
|
bool has_fan_mode = 12;
|
||||||
|
ClimateFanMode fan_mode = 13;
|
||||||
|
bool has_swing_mode = 14;
|
||||||
|
ClimateSwingMode swing_mode = 15;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,9 @@
|
|||||||
#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 {
|
||||||
@@ -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) {
|
||||||
@@ -185,11 +188,12 @@ bool APIConnection::send_cover_state(cover::Cover *cover) {
|
|||||||
auto traits = cover->get_traits();
|
auto traits = cover->get_traits();
|
||||||
CoverStateResponse resp{};
|
CoverStateResponse resp{};
|
||||||
resp.key = cover->get_object_id_hash();
|
resp.key = cover->get_object_id_hash();
|
||||||
resp.legacy_state = (cover->position == cover::COVER_OPEN) ? LEGACY_COVER_STATE_OPEN : LEGACY_COVER_STATE_CLOSED;
|
resp.legacy_state =
|
||||||
|
(cover->position == cover::COVER_OPEN) ? enums::LEGACY_COVER_STATE_OPEN : enums::LEGACY_COVER_STATE_CLOSED;
|
||||||
resp.position = cover->position;
|
resp.position = cover->position;
|
||||||
if (traits.get_supports_tilt())
|
if (traits.get_supports_tilt())
|
||||||
resp.tilt = cover->tilt;
|
resp.tilt = cover->tilt;
|
||||||
resp.current_operation = static_cast<EnumCoverOperation>(cover->current_operation);
|
resp.current_operation = static_cast<enums::CoverOperation>(cover->current_operation);
|
||||||
return this->send_cover_state_response(resp);
|
return this->send_cover_state_response(resp);
|
||||||
}
|
}
|
||||||
bool APIConnection::send_cover_info(cover::Cover *cover) {
|
bool APIConnection::send_cover_info(cover::Cover *cover) {
|
||||||
@@ -213,13 +217,13 @@ void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
|||||||
auto call = cover->make_call();
|
auto call = cover->make_call();
|
||||||
if (msg.has_legacy_command) {
|
if (msg.has_legacy_command) {
|
||||||
switch (msg.legacy_command) {
|
switch (msg.legacy_command) {
|
||||||
case LEGACY_COVER_COMMAND_OPEN:
|
case enums::LEGACY_COVER_COMMAND_OPEN:
|
||||||
call.set_command_open();
|
call.set_command_open();
|
||||||
break;
|
break;
|
||||||
case LEGACY_COVER_COMMAND_CLOSE:
|
case enums::LEGACY_COVER_COMMAND_CLOSE:
|
||||||
call.set_command_close();
|
call.set_command_close();
|
||||||
break;
|
break;
|
||||||
case LEGACY_COVER_COMMAND_STOP:
|
case enums::LEGACY_COVER_COMMAND_STOP:
|
||||||
call.set_command_stop();
|
call.set_command_stop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -245,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<EnumFanSpeed>(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) {
|
||||||
@@ -258,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) {
|
||||||
@@ -265,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
|
||||||
@@ -361,6 +379,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) {
|
||||||
@@ -374,6 +393,8 @@ 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();
|
||||||
return this->send_list_entities_sensor_response(msg);
|
return this->send_list_entities_sensor_response(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -418,6 +439,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) {
|
||||||
@@ -441,8 +463,8 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
|
|||||||
auto traits = climate->get_traits();
|
auto traits = climate->get_traits();
|
||||||
ClimateStateResponse resp{};
|
ClimateStateResponse resp{};
|
||||||
resp.key = climate->get_object_id_hash();
|
resp.key = climate->get_object_id_hash();
|
||||||
resp.mode = static_cast<EnumClimateMode>(climate->mode);
|
resp.mode = static_cast<enums::ClimateMode>(climate->mode);
|
||||||
resp.action = static_cast<EnumClimateAction>(climate->action);
|
resp.action = static_cast<enums::ClimateAction>(climate->action);
|
||||||
if (traits.get_supports_current_temperature())
|
if (traits.get_supports_current_temperature())
|
||||||
resp.current_temperature = climate->current_temperature;
|
resp.current_temperature = climate->current_temperature;
|
||||||
if (traits.get_supports_two_point_target_temperature()) {
|
if (traits.get_supports_two_point_target_temperature()) {
|
||||||
@@ -453,6 +475,10 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
|
|||||||
}
|
}
|
||||||
if (traits.get_supports_away())
|
if (traits.get_supports_away())
|
||||||
resp.away = climate->away;
|
resp.away = climate->away;
|
||||||
|
if (traits.get_supports_fan_modes())
|
||||||
|
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode);
|
||||||
|
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,15 +491,26 @@ bool APIConnection::send_climate_info(climate::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,
|
for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL,
|
||||||
climate::CLIMATE_MODE_HEAT}) {
|
climate::CLIMATE_MODE_HEAT, climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY}) {
|
||||||
if (traits.supports_mode(mode))
|
if (traits.supports_mode(mode))
|
||||||
msg.supported_modes.push_back(static_cast<EnumClimateMode>(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.supports_away = traits.get_supports_away();
|
||||||
msg.supports_action = traits.get_supports_action();
|
msg.supports_action = traits.get_supports_action();
|
||||||
|
for (auto fan_mode : {climate::CLIMATE_FAN_ON, climate::CLIMATE_FAN_OFF, climate::CLIMATE_FAN_AUTO,
|
||||||
|
climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH,
|
||||||
|
climate::CLIMATE_FAN_MIDDLE, climate::CLIMATE_FAN_FOCUS, climate::CLIMATE_FAN_DIFFUSE}) {
|
||||||
|
if (traits.supports_fan_mode(fan_mode))
|
||||||
|
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
|
||||||
|
}
|
||||||
|
for (auto swing_mode : {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL,
|
||||||
|
climate::CLIMATE_SWING_HORIZONTAL}) {
|
||||||
|
if (traits.supports_swing_mode(swing_mode))
|
||||||
|
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) {
|
||||||
@@ -492,6 +529,10 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
|||||||
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_away)
|
||||||
call.set_away(msg.away);
|
call.set_away(msg.away);
|
||||||
|
if (msg.has_fan_mode)
|
||||||
|
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
|
||||||
|
if (msg.has_swing_mode)
|
||||||
|
call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode));
|
||||||
call.perform();
|
call.perform();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -534,8 +575,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;
|
||||||
@@ -563,7 +602,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 = 4;
|
||||||
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;
|
||||||
@@ -650,8 +689,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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,12 +138,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,
|
||||||
|
|||||||
@@ -1,119 +1,179 @@
|
|||||||
|
// This file was automatically generated with a tool.
|
||||||
|
// See scripts/api_protobuf/api_protobuf.py
|
||||||
#include "api_pb2.h"
|
#include "api_pb2.h"
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
template<> const char *proto_enum_to_string<EnumLegacyCoverState>(EnumLegacyCoverState value) {
|
template<> const char *proto_enum_to_string<enums::LegacyCoverState>(enums::LegacyCoverState value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case LEGACY_COVER_STATE_OPEN:
|
case enums::LEGACY_COVER_STATE_OPEN:
|
||||||
return "LEGACY_COVER_STATE_OPEN";
|
return "LEGACY_COVER_STATE_OPEN";
|
||||||
case LEGACY_COVER_STATE_CLOSED:
|
case enums::LEGACY_COVER_STATE_CLOSED:
|
||||||
return "LEGACY_COVER_STATE_CLOSED";
|
return "LEGACY_COVER_STATE_CLOSED";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<> const char *proto_enum_to_string<EnumCoverOperation>(EnumCoverOperation value) {
|
template<> const char *proto_enum_to_string<enums::CoverOperation>(enums::CoverOperation value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case COVER_OPERATION_IDLE:
|
case enums::COVER_OPERATION_IDLE:
|
||||||
return "COVER_OPERATION_IDLE";
|
return "COVER_OPERATION_IDLE";
|
||||||
case COVER_OPERATION_IS_OPENING:
|
case enums::COVER_OPERATION_IS_OPENING:
|
||||||
return "COVER_OPERATION_IS_OPENING";
|
return "COVER_OPERATION_IS_OPENING";
|
||||||
case COVER_OPERATION_IS_CLOSING:
|
case enums::COVER_OPERATION_IS_CLOSING:
|
||||||
return "COVER_OPERATION_IS_CLOSING";
|
return "COVER_OPERATION_IS_CLOSING";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<> const char *proto_enum_to_string<EnumLegacyCoverCommand>(EnumLegacyCoverCommand value) {
|
template<> const char *proto_enum_to_string<enums::LegacyCoverCommand>(enums::LegacyCoverCommand value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case LEGACY_COVER_COMMAND_OPEN:
|
case enums::LEGACY_COVER_COMMAND_OPEN:
|
||||||
return "LEGACY_COVER_COMMAND_OPEN";
|
return "LEGACY_COVER_COMMAND_OPEN";
|
||||||
case LEGACY_COVER_COMMAND_CLOSE:
|
case enums::LEGACY_COVER_COMMAND_CLOSE:
|
||||||
return "LEGACY_COVER_COMMAND_CLOSE";
|
return "LEGACY_COVER_COMMAND_CLOSE";
|
||||||
case LEGACY_COVER_COMMAND_STOP:
|
case enums::LEGACY_COVER_COMMAND_STOP:
|
||||||
return "LEGACY_COVER_COMMAND_STOP";
|
return "LEGACY_COVER_COMMAND_STOP";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<> const char *proto_enum_to_string<EnumFanSpeed>(EnumFanSpeed value) {
|
template<> const char *proto_enum_to_string<enums::FanSpeed>(enums::FanSpeed value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case FAN_SPEED_LOW:
|
case enums::FAN_SPEED_LOW:
|
||||||
return "FAN_SPEED_LOW";
|
return "FAN_SPEED_LOW";
|
||||||
case FAN_SPEED_MEDIUM:
|
case enums::FAN_SPEED_MEDIUM:
|
||||||
return "FAN_SPEED_MEDIUM";
|
return "FAN_SPEED_MEDIUM";
|
||||||
case FAN_SPEED_HIGH:
|
case enums::FAN_SPEED_HIGH:
|
||||||
return "FAN_SPEED_HIGH";
|
return "FAN_SPEED_HIGH";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<> const char *proto_enum_to_string<EnumLogLevel>(EnumLogLevel value) {
|
template<> const char *proto_enum_to_string<enums::FanDirection>(enums::FanDirection value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case LOG_LEVEL_NONE:
|
case enums::FAN_DIRECTION_FORWARD:
|
||||||
|
return "FAN_DIRECTION_FORWARD";
|
||||||
|
case enums::FAN_DIRECTION_REVERSE:
|
||||||
|
return "FAN_DIRECTION_REVERSE";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::LOG_LEVEL_NONE:
|
||||||
return "LOG_LEVEL_NONE";
|
return "LOG_LEVEL_NONE";
|
||||||
case LOG_LEVEL_ERROR:
|
case enums::LOG_LEVEL_ERROR:
|
||||||
return "LOG_LEVEL_ERROR";
|
return "LOG_LEVEL_ERROR";
|
||||||
case LOG_LEVEL_WARN:
|
case enums::LOG_LEVEL_WARN:
|
||||||
return "LOG_LEVEL_WARN";
|
return "LOG_LEVEL_WARN";
|
||||||
case LOG_LEVEL_INFO:
|
case enums::LOG_LEVEL_INFO:
|
||||||
return "LOG_LEVEL_INFO";
|
return "LOG_LEVEL_INFO";
|
||||||
case LOG_LEVEL_DEBUG:
|
case enums::LOG_LEVEL_DEBUG:
|
||||||
return "LOG_LEVEL_DEBUG";
|
return "LOG_LEVEL_DEBUG";
|
||||||
case LOG_LEVEL_VERBOSE:
|
case enums::LOG_LEVEL_VERBOSE:
|
||||||
return "LOG_LEVEL_VERBOSE";
|
return "LOG_LEVEL_VERBOSE";
|
||||||
case LOG_LEVEL_VERY_VERBOSE:
|
case enums::LOG_LEVEL_VERY_VERBOSE:
|
||||||
return "LOG_LEVEL_VERY_VERBOSE";
|
return "LOG_LEVEL_VERY_VERBOSE";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<> const char *proto_enum_to_string<EnumServiceArgType>(EnumServiceArgType value) {
|
template<> const char *proto_enum_to_string<enums::ServiceArgType>(enums::ServiceArgType value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case SERVICE_ARG_TYPE_BOOL:
|
case enums::SERVICE_ARG_TYPE_BOOL:
|
||||||
return "SERVICE_ARG_TYPE_BOOL";
|
return "SERVICE_ARG_TYPE_BOOL";
|
||||||
case SERVICE_ARG_TYPE_INT:
|
case enums::SERVICE_ARG_TYPE_INT:
|
||||||
return "SERVICE_ARG_TYPE_INT";
|
return "SERVICE_ARG_TYPE_INT";
|
||||||
case SERVICE_ARG_TYPE_FLOAT:
|
case enums::SERVICE_ARG_TYPE_FLOAT:
|
||||||
return "SERVICE_ARG_TYPE_FLOAT";
|
return "SERVICE_ARG_TYPE_FLOAT";
|
||||||
case SERVICE_ARG_TYPE_STRING:
|
case enums::SERVICE_ARG_TYPE_STRING:
|
||||||
return "SERVICE_ARG_TYPE_STRING";
|
return "SERVICE_ARG_TYPE_STRING";
|
||||||
case SERVICE_ARG_TYPE_BOOL_ARRAY:
|
case enums::SERVICE_ARG_TYPE_BOOL_ARRAY:
|
||||||
return "SERVICE_ARG_TYPE_BOOL_ARRAY";
|
return "SERVICE_ARG_TYPE_BOOL_ARRAY";
|
||||||
case SERVICE_ARG_TYPE_INT_ARRAY:
|
case enums::SERVICE_ARG_TYPE_INT_ARRAY:
|
||||||
return "SERVICE_ARG_TYPE_INT_ARRAY";
|
return "SERVICE_ARG_TYPE_INT_ARRAY";
|
||||||
case SERVICE_ARG_TYPE_FLOAT_ARRAY:
|
case enums::SERVICE_ARG_TYPE_FLOAT_ARRAY:
|
||||||
return "SERVICE_ARG_TYPE_FLOAT_ARRAY";
|
return "SERVICE_ARG_TYPE_FLOAT_ARRAY";
|
||||||
case SERVICE_ARG_TYPE_STRING_ARRAY:
|
case enums::SERVICE_ARG_TYPE_STRING_ARRAY:
|
||||||
return "SERVICE_ARG_TYPE_STRING_ARRAY";
|
return "SERVICE_ARG_TYPE_STRING_ARRAY";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<> const char *proto_enum_to_string<EnumClimateMode>(EnumClimateMode value) {
|
template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMode value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case CLIMATE_MODE_OFF:
|
case enums::CLIMATE_MODE_OFF:
|
||||||
return "CLIMATE_MODE_OFF";
|
return "CLIMATE_MODE_OFF";
|
||||||
case CLIMATE_MODE_AUTO:
|
case enums::CLIMATE_MODE_AUTO:
|
||||||
return "CLIMATE_MODE_AUTO";
|
return "CLIMATE_MODE_AUTO";
|
||||||
case CLIMATE_MODE_COOL:
|
case enums::CLIMATE_MODE_COOL:
|
||||||
return "CLIMATE_MODE_COOL";
|
return "CLIMATE_MODE_COOL";
|
||||||
case CLIMATE_MODE_HEAT:
|
case enums::CLIMATE_MODE_HEAT:
|
||||||
return "CLIMATE_MODE_HEAT";
|
return "CLIMATE_MODE_HEAT";
|
||||||
|
case enums::CLIMATE_MODE_FAN_ONLY:
|
||||||
|
return "CLIMATE_MODE_FAN_ONLY";
|
||||||
|
case enums::CLIMATE_MODE_DRY:
|
||||||
|
return "CLIMATE_MODE_DRY";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template<> const char *proto_enum_to_string<EnumClimateAction>(EnumClimateAction value) {
|
template<> const char *proto_enum_to_string<enums::ClimateFanMode>(enums::ClimateFanMode value) {
|
||||||
switch (value) {
|
switch (value) {
|
||||||
case CLIMATE_ACTION_OFF:
|
case enums::CLIMATE_FAN_ON:
|
||||||
|
return "CLIMATE_FAN_ON";
|
||||||
|
case enums::CLIMATE_FAN_OFF:
|
||||||
|
return "CLIMATE_FAN_OFF";
|
||||||
|
case enums::CLIMATE_FAN_AUTO:
|
||||||
|
return "CLIMATE_FAN_AUTO";
|
||||||
|
case enums::CLIMATE_FAN_LOW:
|
||||||
|
return "CLIMATE_FAN_LOW";
|
||||||
|
case enums::CLIMATE_FAN_MEDIUM:
|
||||||
|
return "CLIMATE_FAN_MEDIUM";
|
||||||
|
case enums::CLIMATE_FAN_HIGH:
|
||||||
|
return "CLIMATE_FAN_HIGH";
|
||||||
|
case enums::CLIMATE_FAN_MIDDLE:
|
||||||
|
return "CLIMATE_FAN_MIDDLE";
|
||||||
|
case enums::CLIMATE_FAN_FOCUS:
|
||||||
|
return "CLIMATE_FAN_FOCUS";
|
||||||
|
case enums::CLIMATE_FAN_DIFFUSE:
|
||||||
|
return "CLIMATE_FAN_DIFFUSE";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<> const char *proto_enum_to_string<enums::ClimateSwingMode>(enums::ClimateSwingMode value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::CLIMATE_SWING_OFF:
|
||||||
|
return "CLIMATE_SWING_OFF";
|
||||||
|
case enums::CLIMATE_SWING_BOTH:
|
||||||
|
return "CLIMATE_SWING_BOTH";
|
||||||
|
case enums::CLIMATE_SWING_VERTICAL:
|
||||||
|
return "CLIMATE_SWING_VERTICAL";
|
||||||
|
case enums::CLIMATE_SWING_HORIZONTAL:
|
||||||
|
return "CLIMATE_SWING_HORIZONTAL";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<> const char *proto_enum_to_string<enums::ClimateAction>(enums::ClimateAction value) {
|
||||||
|
switch (value) {
|
||||||
|
case enums::CLIMATE_ACTION_OFF:
|
||||||
return "CLIMATE_ACTION_OFF";
|
return "CLIMATE_ACTION_OFF";
|
||||||
case CLIMATE_ACTION_COOLING:
|
case enums::CLIMATE_ACTION_COOLING:
|
||||||
return "CLIMATE_ACTION_COOLING";
|
return "CLIMATE_ACTION_COOLING";
|
||||||
case CLIMATE_ACTION_HEATING:
|
case enums::CLIMATE_ACTION_HEATING:
|
||||||
return "CLIMATE_ACTION_HEATING";
|
return "CLIMATE_ACTION_HEATING";
|
||||||
|
case enums::CLIMATE_ACTION_IDLE:
|
||||||
|
return "CLIMATE_ACTION_IDLE";
|
||||||
|
case enums::CLIMATE_ACTION_DRYING:
|
||||||
|
return "CLIMATE_ACTION_DRYING";
|
||||||
|
case enums::CLIMATE_ACTION_FAN:
|
||||||
|
return "CLIMATE_ACTION_FAN";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
@@ -404,6 +464,10 @@ bool BinarySensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt val
|
|||||||
this->state = value.as_bool();
|
this->state = value.as_bool();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 3: {
|
||||||
|
this->missing_state = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -421,6 +485,7 @@ bool BinarySensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value
|
|||||||
void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_fixed32(1, this->key);
|
buffer.encode_fixed32(1, this->key);
|
||||||
buffer.encode_bool(2, this->state);
|
buffer.encode_bool(2, this->state);
|
||||||
|
buffer.encode_bool(3, this->missing_state);
|
||||||
}
|
}
|
||||||
void BinarySensorStateResponse::dump_to(std::string &out) const {
|
void BinarySensorStateResponse::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
@@ -433,6 +498,10 @@ void BinarySensorStateResponse::dump_to(std::string &out) const {
|
|||||||
out.append(" state: ");
|
out.append(" state: ");
|
||||||
out.append(YESNO(this->state));
|
out.append(YESNO(this->state));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" missing_state: ");
|
||||||
|
out.append(YESNO(this->missing_state));
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
@@ -535,11 +604,11 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
|
|||||||
bool CoverStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool CoverStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 2: {
|
case 2: {
|
||||||
this->legacy_state = value.as_enum<EnumLegacyCoverState>();
|
this->legacy_state = value.as_enum<enums::LegacyCoverState>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 5: {
|
case 5: {
|
||||||
this->current_operation = value.as_enum<EnumCoverOperation>();
|
this->current_operation = value.as_enum<enums::CoverOperation>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -566,10 +635,10 @@ bool CoverStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
|||||||
}
|
}
|
||||||
void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
|
void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_fixed32(1, this->key);
|
buffer.encode_fixed32(1, this->key);
|
||||||
buffer.encode_enum<EnumLegacyCoverState>(2, this->legacy_state);
|
buffer.encode_enum<enums::LegacyCoverState>(2, this->legacy_state);
|
||||||
buffer.encode_float(3, this->position);
|
buffer.encode_float(3, this->position);
|
||||||
buffer.encode_float(4, this->tilt);
|
buffer.encode_float(4, this->tilt);
|
||||||
buffer.encode_enum<EnumCoverOperation>(5, this->current_operation);
|
buffer.encode_enum<enums::CoverOperation>(5, this->current_operation);
|
||||||
}
|
}
|
||||||
void CoverStateResponse::dump_to(std::string &out) const {
|
void CoverStateResponse::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
@@ -580,7 +649,7 @@ void CoverStateResponse::dump_to(std::string &out) const {
|
|||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" legacy_state: ");
|
out.append(" legacy_state: ");
|
||||||
out.append(proto_enum_to_string<EnumLegacyCoverState>(this->legacy_state));
|
out.append(proto_enum_to_string<enums::LegacyCoverState>(this->legacy_state));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" position: ");
|
out.append(" position: ");
|
||||||
@@ -594,7 +663,7 @@ void CoverStateResponse::dump_to(std::string &out) const {
|
|||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" current_operation: ");
|
out.append(" current_operation: ");
|
||||||
out.append(proto_enum_to_string<EnumCoverOperation>(this->current_operation));
|
out.append(proto_enum_to_string<enums::CoverOperation>(this->current_operation));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
@@ -605,7 +674,7 @@ bool CoverCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 3: {
|
case 3: {
|
||||||
this->legacy_command = value.as_enum<EnumLegacyCoverCommand>();
|
this->legacy_command = value.as_enum<enums::LegacyCoverCommand>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 4: {
|
case 4: {
|
||||||
@@ -645,7 +714,7 @@ bool CoverCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
|||||||
void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_fixed32(1, this->key);
|
buffer.encode_fixed32(1, this->key);
|
||||||
buffer.encode_bool(2, this->has_legacy_command);
|
buffer.encode_bool(2, this->has_legacy_command);
|
||||||
buffer.encode_enum<EnumLegacyCoverCommand>(3, this->legacy_command);
|
buffer.encode_enum<enums::LegacyCoverCommand>(3, this->legacy_command);
|
||||||
buffer.encode_bool(4, this->has_position);
|
buffer.encode_bool(4, this->has_position);
|
||||||
buffer.encode_float(5, this->position);
|
buffer.encode_float(5, this->position);
|
||||||
buffer.encode_bool(6, this->has_tilt);
|
buffer.encode_bool(6, this->has_tilt);
|
||||||
@@ -665,7 +734,7 @@ void CoverCommandRequest::dump_to(std::string &out) const {
|
|||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" legacy_command: ");
|
out.append(" legacy_command: ");
|
||||||
out.append(proto_enum_to_string<EnumLegacyCoverCommand>(this->legacy_command));
|
out.append(proto_enum_to_string<enums::LegacyCoverCommand>(this->legacy_command));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" has_position: ");
|
out.append(" has_position: ");
|
||||||
@@ -701,6 +770,14 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value
|
|||||||
this->supports_speed = value.as_bool();
|
this->supports_speed = value.as_bool();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 7: {
|
||||||
|
this->supports_direction = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 8: {
|
||||||
|
this->supported_speed_count = value.as_int32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -740,6 +817,8 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
buffer.encode_string(4, this->unique_id);
|
buffer.encode_string(4, this->unique_id);
|
||||||
buffer.encode_bool(5, this->supports_oscillation);
|
buffer.encode_bool(5, this->supports_oscillation);
|
||||||
buffer.encode_bool(6, this->supports_speed);
|
buffer.encode_bool(6, this->supports_speed);
|
||||||
|
buffer.encode_bool(7, this->supports_direction);
|
||||||
|
buffer.encode_int32(8, this->supported_speed_count);
|
||||||
}
|
}
|
||||||
void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
@@ -768,6 +847,15 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
|||||||
out.append(" supports_speed: ");
|
out.append(" supports_speed: ");
|
||||||
out.append(YESNO(this->supports_speed));
|
out.append(YESNO(this->supports_speed));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" supports_direction: ");
|
||||||
|
out.append(YESNO(this->supports_direction));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" supported_speed_count: ");
|
||||||
|
sprintf(buffer, "%d", this->supported_speed_count);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
@@ -781,7 +869,15 @@ bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 4: {
|
case 4: {
|
||||||
this->speed = value.as_enum<EnumFanSpeed>();
|
this->speed = value.as_enum<enums::FanSpeed>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 5: {
|
||||||
|
this->direction = value.as_enum<enums::FanDirection>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 6: {
|
||||||
|
this->speed_level = value.as_int32();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -802,7 +898,9 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
buffer.encode_fixed32(1, this->key);
|
buffer.encode_fixed32(1, this->key);
|
||||||
buffer.encode_bool(2, this->state);
|
buffer.encode_bool(2, this->state);
|
||||||
buffer.encode_bool(3, this->oscillating);
|
buffer.encode_bool(3, this->oscillating);
|
||||||
buffer.encode_enum<EnumFanSpeed>(4, this->speed);
|
buffer.encode_enum<enums::FanSpeed>(4, this->speed);
|
||||||
|
buffer.encode_enum<enums::FanDirection>(5, this->direction);
|
||||||
|
buffer.encode_int32(6, this->speed_level);
|
||||||
}
|
}
|
||||||
void FanStateResponse::dump_to(std::string &out) const {
|
void FanStateResponse::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
@@ -821,7 +919,16 @@ void FanStateResponse::dump_to(std::string &out) const {
|
|||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" speed: ");
|
out.append(" speed: ");
|
||||||
out.append(proto_enum_to_string<EnumFanSpeed>(this->speed));
|
out.append(proto_enum_to_string<enums::FanSpeed>(this->speed));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" direction: ");
|
||||||
|
out.append(proto_enum_to_string<enums::FanDirection>(this->direction));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" speed_level: ");
|
||||||
|
sprintf(buffer, "%d", this->speed_level);
|
||||||
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
@@ -840,7 +947,7 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 5: {
|
case 5: {
|
||||||
this->speed = value.as_enum<EnumFanSpeed>();
|
this->speed = value.as_enum<enums::FanSpeed>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 6: {
|
case 6: {
|
||||||
@@ -851,6 +958,22 @@ bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
|||||||
this->oscillating = value.as_bool();
|
this->oscillating = value.as_bool();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 8: {
|
||||||
|
this->has_direction = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 9: {
|
||||||
|
this->direction = value.as_enum<enums::FanDirection>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 10: {
|
||||||
|
this->has_speed_level = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 11: {
|
||||||
|
this->speed_level = value.as_int32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -870,9 +993,13 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
|||||||
buffer.encode_bool(2, this->has_state);
|
buffer.encode_bool(2, this->has_state);
|
||||||
buffer.encode_bool(3, this->state);
|
buffer.encode_bool(3, this->state);
|
||||||
buffer.encode_bool(4, this->has_speed);
|
buffer.encode_bool(4, this->has_speed);
|
||||||
buffer.encode_enum<EnumFanSpeed>(5, this->speed);
|
buffer.encode_enum<enums::FanSpeed>(5, this->speed);
|
||||||
buffer.encode_bool(6, this->has_oscillating);
|
buffer.encode_bool(6, this->has_oscillating);
|
||||||
buffer.encode_bool(7, this->oscillating);
|
buffer.encode_bool(7, this->oscillating);
|
||||||
|
buffer.encode_bool(8, this->has_direction);
|
||||||
|
buffer.encode_enum<enums::FanDirection>(9, this->direction);
|
||||||
|
buffer.encode_bool(10, this->has_speed_level);
|
||||||
|
buffer.encode_int32(11, this->speed_level);
|
||||||
}
|
}
|
||||||
void FanCommandRequest::dump_to(std::string &out) const {
|
void FanCommandRequest::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
@@ -895,7 +1022,7 @@ void FanCommandRequest::dump_to(std::string &out) const {
|
|||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" speed: ");
|
out.append(" speed: ");
|
||||||
out.append(proto_enum_to_string<EnumFanSpeed>(this->speed));
|
out.append(proto_enum_to_string<enums::FanSpeed>(this->speed));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" has_oscillating: ");
|
out.append(" has_oscillating: ");
|
||||||
@@ -905,6 +1032,23 @@ void FanCommandRequest::dump_to(std::string &out) const {
|
|||||||
out.append(" oscillating: ");
|
out.append(" oscillating: ");
|
||||||
out.append(YESNO(this->oscillating));
|
out.append(YESNO(this->oscillating));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" has_direction: ");
|
||||||
|
out.append(YESNO(this->has_direction));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" direction: ");
|
||||||
|
out.append(proto_enum_to_string<enums::FanDirection>(this->direction));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" has_speed_level: ");
|
||||||
|
out.append(YESNO(this->has_speed_level));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" speed_level: ");
|
||||||
|
sprintf(buffer, "%d", this->speed_level);
|
||||||
|
out.append(buffer);
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
@@ -1359,6 +1503,10 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va
|
|||||||
this->accuracy_decimals = value.as_int32();
|
this->accuracy_decimals = value.as_int32();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 8: {
|
||||||
|
this->force_update = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1385,6 +1533,10 @@ bool ListEntitiesSensorResponse::decode_length(uint32_t field_id, ProtoLengthDel
|
|||||||
this->unit_of_measurement = value.as_string();
|
this->unit_of_measurement = value.as_string();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 9: {
|
||||||
|
this->device_class = value.as_string();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1407,6 +1559,8 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
buffer.encode_string(5, this->icon);
|
buffer.encode_string(5, this->icon);
|
||||||
buffer.encode_string(6, this->unit_of_measurement);
|
buffer.encode_string(6, this->unit_of_measurement);
|
||||||
buffer.encode_int32(7, this->accuracy_decimals);
|
buffer.encode_int32(7, this->accuracy_decimals);
|
||||||
|
buffer.encode_bool(8, this->force_update);
|
||||||
|
buffer.encode_string(9, this->device_class);
|
||||||
}
|
}
|
||||||
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
|
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
@@ -1440,8 +1594,26 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
|
|||||||
sprintf(buffer, "%d", this->accuracy_decimals);
|
sprintf(buffer, "%d", this->accuracy_decimals);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" force_update: ");
|
||||||
|
out.append(YESNO(this->force_update));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" device_class: ");
|
||||||
|
out.append("'").append(this->device_class).append("'");
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
|
bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 3: {
|
||||||
|
this->missing_state = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
bool SensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
bool SensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 1: {
|
case 1: {
|
||||||
@@ -1459,6 +1631,7 @@ bool SensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
|||||||
void SensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
void SensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_fixed32(1, this->key);
|
buffer.encode_fixed32(1, this->key);
|
||||||
buffer.encode_float(2, this->state);
|
buffer.encode_float(2, this->state);
|
||||||
|
buffer.encode_bool(3, this->missing_state);
|
||||||
}
|
}
|
||||||
void SensorStateResponse::dump_to(std::string &out) const {
|
void SensorStateResponse::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
@@ -1472,6 +1645,10 @@ void SensorStateResponse::dump_to(std::string &out) const {
|
|||||||
sprintf(buffer, "%g", this->state);
|
sprintf(buffer, "%g", this->state);
|
||||||
out.append(buffer);
|
out.append(buffer);
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" missing_state: ");
|
||||||
|
out.append(YESNO(this->missing_state));
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
@@ -1691,6 +1868,16 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
|
|||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
|
bool TextSensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
|
switch (field_id) {
|
||||||
|
case 3: {
|
||||||
|
this->missing_state = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
bool TextSensorStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
bool TextSensorStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 2: {
|
case 2: {
|
||||||
@@ -1714,6 +1901,7 @@ bool TextSensorStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value)
|
|||||||
void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_fixed32(1, this->key);
|
buffer.encode_fixed32(1, this->key);
|
||||||
buffer.encode_string(2, this->state);
|
buffer.encode_string(2, this->state);
|
||||||
|
buffer.encode_bool(3, this->missing_state);
|
||||||
}
|
}
|
||||||
void TextSensorStateResponse::dump_to(std::string &out) const {
|
void TextSensorStateResponse::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
@@ -1726,12 +1914,16 @@ void TextSensorStateResponse::dump_to(std::string &out) const {
|
|||||||
out.append(" state: ");
|
out.append(" state: ");
|
||||||
out.append("'").append(this->state).append("'");
|
out.append("'").append(this->state).append("'");
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" missing_state: ");
|
||||||
|
out.append(YESNO(this->missing_state));
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 1: {
|
case 1: {
|
||||||
this->level = value.as_enum<EnumLogLevel>();
|
this->level = value.as_enum<enums::LogLevel>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 2: {
|
case 2: {
|
||||||
@@ -1743,14 +1935,14 @@ bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const {
|
void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_enum<EnumLogLevel>(1, this->level);
|
buffer.encode_enum<enums::LogLevel>(1, this->level);
|
||||||
buffer.encode_bool(2, this->dump_config);
|
buffer.encode_bool(2, this->dump_config);
|
||||||
}
|
}
|
||||||
void SubscribeLogsRequest::dump_to(std::string &out) const {
|
void SubscribeLogsRequest::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
out.append("SubscribeLogsRequest {\n");
|
out.append("SubscribeLogsRequest {\n");
|
||||||
out.append(" level: ");
|
out.append(" level: ");
|
||||||
out.append(proto_enum_to_string<EnumLogLevel>(this->level));
|
out.append(proto_enum_to_string<enums::LogLevel>(this->level));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" dump_config: ");
|
out.append(" dump_config: ");
|
||||||
@@ -1761,7 +1953,7 @@ void SubscribeLogsRequest::dump_to(std::string &out) const {
|
|||||||
bool SubscribeLogsResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool SubscribeLogsResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 1: {
|
case 1: {
|
||||||
this->level = value.as_enum<EnumLogLevel>();
|
this->level = value.as_enum<enums::LogLevel>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 4: {
|
case 4: {
|
||||||
@@ -1787,7 +1979,7 @@ bool SubscribeLogsResponse::decode_length(uint32_t field_id, ProtoLengthDelimite
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const {
|
void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_enum<EnumLogLevel>(1, this->level);
|
buffer.encode_enum<enums::LogLevel>(1, this->level);
|
||||||
buffer.encode_string(2, this->tag);
|
buffer.encode_string(2, this->tag);
|
||||||
buffer.encode_string(3, this->message);
|
buffer.encode_string(3, this->message);
|
||||||
buffer.encode_bool(4, this->send_failed);
|
buffer.encode_bool(4, this->send_failed);
|
||||||
@@ -1796,7 +1988,7 @@ void SubscribeLogsResponse::dump_to(std::string &out) const {
|
|||||||
char buffer[64];
|
char buffer[64];
|
||||||
out.append("SubscribeLogsResponse {\n");
|
out.append("SubscribeLogsResponse {\n");
|
||||||
out.append(" level: ");
|
out.append(" level: ");
|
||||||
out.append(proto_enum_to_string<EnumLogLevel>(this->level));
|
out.append(proto_enum_to_string<enums::LogLevel>(this->level));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" tag: ");
|
out.append(" tag: ");
|
||||||
@@ -2001,7 +2193,7 @@ void GetTimeResponse::dump_to(std::string &out) const {
|
|||||||
bool ListEntitiesServicesArgument::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool ListEntitiesServicesArgument::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 2: {
|
case 2: {
|
||||||
this->type = value.as_enum<EnumServiceArgType>();
|
this->type = value.as_enum<enums::ServiceArgType>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -2020,7 +2212,7 @@ bool ListEntitiesServicesArgument::decode_length(uint32_t field_id, ProtoLengthD
|
|||||||
}
|
}
|
||||||
void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
|
void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_string(1, this->name);
|
buffer.encode_string(1, this->name);
|
||||||
buffer.encode_enum<EnumServiceArgType>(2, this->type);
|
buffer.encode_enum<enums::ServiceArgType>(2, this->type);
|
||||||
}
|
}
|
||||||
void ListEntitiesServicesArgument::dump_to(std::string &out) const {
|
void ListEntitiesServicesArgument::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
@@ -2030,7 +2222,7 @@ void ListEntitiesServicesArgument::dump_to(std::string &out) const {
|
|||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" type: ");
|
out.append(" type: ");
|
||||||
out.append(proto_enum_to_string<EnumServiceArgType>(this->type));
|
out.append(proto_enum_to_string<enums::ServiceArgType>(this->type));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
@@ -2399,7 +2591,7 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 7: {
|
case 7: {
|
||||||
this->supported_modes.push_back(value.as_enum<EnumClimateMode>());
|
this->supported_modes.push_back(value.as_enum<enums::ClimateMode>());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 11: {
|
case 11: {
|
||||||
@@ -2410,6 +2602,14 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
|
|||||||
this->supports_action = value.as_bool();
|
this->supports_action = value.as_bool();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 13: {
|
||||||
|
this->supported_fan_modes.push_back(value.as_enum<enums::ClimateFanMode>());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 14: {
|
||||||
|
this->supported_swing_modes.push_back(value.as_enum<enums::ClimateSwingMode>());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2462,13 +2662,19 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
|
|||||||
buffer.encode_bool(5, this->supports_current_temperature);
|
buffer.encode_bool(5, this->supports_current_temperature);
|
||||||
buffer.encode_bool(6, this->supports_two_point_target_temperature);
|
buffer.encode_bool(6, this->supports_two_point_target_temperature);
|
||||||
for (auto &it : this->supported_modes) {
|
for (auto &it : this->supported_modes) {
|
||||||
buffer.encode_enum<EnumClimateMode>(7, it, true);
|
buffer.encode_enum<enums::ClimateMode>(7, it, true);
|
||||||
}
|
}
|
||||||
buffer.encode_float(8, this->visual_min_temperature);
|
buffer.encode_float(8, this->visual_min_temperature);
|
||||||
buffer.encode_float(9, this->visual_max_temperature);
|
buffer.encode_float(9, this->visual_max_temperature);
|
||||||
buffer.encode_float(10, this->visual_temperature_step);
|
buffer.encode_float(10, this->visual_temperature_step);
|
||||||
buffer.encode_bool(11, this->supports_away);
|
buffer.encode_bool(11, this->supports_away);
|
||||||
buffer.encode_bool(12, this->supports_action);
|
buffer.encode_bool(12, this->supports_action);
|
||||||
|
for (auto &it : this->supported_fan_modes) {
|
||||||
|
buffer.encode_enum<enums::ClimateFanMode>(13, it, true);
|
||||||
|
}
|
||||||
|
for (auto &it : this->supported_swing_modes) {
|
||||||
|
buffer.encode_enum<enums::ClimateSwingMode>(14, it, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
@@ -2500,7 +2706,7 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
|||||||
|
|
||||||
for (const auto &it : this->supported_modes) {
|
for (const auto &it : this->supported_modes) {
|
||||||
out.append(" supported_modes: ");
|
out.append(" supported_modes: ");
|
||||||
out.append(proto_enum_to_string<EnumClimateMode>(it));
|
out.append(proto_enum_to_string<enums::ClimateMode>(it));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2526,12 +2732,24 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
|||||||
out.append(" supports_action: ");
|
out.append(" supports_action: ");
|
||||||
out.append(YESNO(this->supports_action));
|
out.append(YESNO(this->supports_action));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
for (const auto &it : this->supported_fan_modes) {
|
||||||
|
out.append(" supported_fan_modes: ");
|
||||||
|
out.append(proto_enum_to_string<enums::ClimateFanMode>(it));
|
||||||
|
out.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &it : this->supported_swing_modes) {
|
||||||
|
out.append(" supported_swing_modes: ");
|
||||||
|
out.append(proto_enum_to_string<enums::ClimateSwingMode>(it));
|
||||||
|
out.append("\n");
|
||||||
|
}
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||||
switch (field_id) {
|
switch (field_id) {
|
||||||
case 2: {
|
case 2: {
|
||||||
this->mode = value.as_enum<EnumClimateMode>();
|
this->mode = value.as_enum<enums::ClimateMode>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 7: {
|
case 7: {
|
||||||
@@ -2539,7 +2757,15 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 8: {
|
case 8: {
|
||||||
this->action = value.as_enum<EnumClimateAction>();
|
this->action = value.as_enum<enums::ClimateAction>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 9: {
|
||||||
|
this->fan_mode = value.as_enum<enums::ClimateFanMode>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 10: {
|
||||||
|
this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -2574,13 +2800,15 @@ bool ClimateStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
|||||||
}
|
}
|
||||||
void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
|
void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_fixed32(1, this->key);
|
buffer.encode_fixed32(1, this->key);
|
||||||
buffer.encode_enum<EnumClimateMode>(2, this->mode);
|
buffer.encode_enum<enums::ClimateMode>(2, this->mode);
|
||||||
buffer.encode_float(3, this->current_temperature);
|
buffer.encode_float(3, this->current_temperature);
|
||||||
buffer.encode_float(4, this->target_temperature);
|
buffer.encode_float(4, this->target_temperature);
|
||||||
buffer.encode_float(5, this->target_temperature_low);
|
buffer.encode_float(5, this->target_temperature_low);
|
||||||
buffer.encode_float(6, this->target_temperature_high);
|
buffer.encode_float(6, this->target_temperature_high);
|
||||||
buffer.encode_bool(7, this->away);
|
buffer.encode_bool(7, this->away);
|
||||||
buffer.encode_enum<EnumClimateAction>(8, this->action);
|
buffer.encode_enum<enums::ClimateAction>(8, this->action);
|
||||||
|
buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode);
|
||||||
|
buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode);
|
||||||
}
|
}
|
||||||
void ClimateStateResponse::dump_to(std::string &out) const {
|
void ClimateStateResponse::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
@@ -2591,7 +2819,7 @@ void ClimateStateResponse::dump_to(std::string &out) const {
|
|||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" mode: ");
|
out.append(" mode: ");
|
||||||
out.append(proto_enum_to_string<EnumClimateMode>(this->mode));
|
out.append(proto_enum_to_string<enums::ClimateMode>(this->mode));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" current_temperature: ");
|
out.append(" current_temperature: ");
|
||||||
@@ -2619,7 +2847,15 @@ void ClimateStateResponse::dump_to(std::string &out) const {
|
|||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" action: ");
|
out.append(" action: ");
|
||||||
out.append(proto_enum_to_string<EnumClimateAction>(this->action));
|
out.append(proto_enum_to_string<enums::ClimateAction>(this->action));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" fan_mode: ");
|
||||||
|
out.append(proto_enum_to_string<enums::ClimateFanMode>(this->fan_mode));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" swing_mode: ");
|
||||||
|
out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
@@ -2630,7 +2866,7 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 3: {
|
case 3: {
|
||||||
this->mode = value.as_enum<EnumClimateMode>();
|
this->mode = value.as_enum<enums::ClimateMode>();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 4: {
|
case 4: {
|
||||||
@@ -2653,6 +2889,22 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
|
|||||||
this->away = value.as_bool();
|
this->away = value.as_bool();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case 12: {
|
||||||
|
this->has_fan_mode = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 13: {
|
||||||
|
this->fan_mode = value.as_enum<enums::ClimateFanMode>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 14: {
|
||||||
|
this->has_swing_mode = value.as_bool();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 15: {
|
||||||
|
this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -2682,7 +2934,7 @@ bool ClimateCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
|||||||
void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||||
buffer.encode_fixed32(1, this->key);
|
buffer.encode_fixed32(1, this->key);
|
||||||
buffer.encode_bool(2, this->has_mode);
|
buffer.encode_bool(2, this->has_mode);
|
||||||
buffer.encode_enum<EnumClimateMode>(3, this->mode);
|
buffer.encode_enum<enums::ClimateMode>(3, this->mode);
|
||||||
buffer.encode_bool(4, this->has_target_temperature);
|
buffer.encode_bool(4, this->has_target_temperature);
|
||||||
buffer.encode_float(5, this->target_temperature);
|
buffer.encode_float(5, this->target_temperature);
|
||||||
buffer.encode_bool(6, this->has_target_temperature_low);
|
buffer.encode_bool(6, this->has_target_temperature_low);
|
||||||
@@ -2691,6 +2943,10 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
|||||||
buffer.encode_float(9, this->target_temperature_high);
|
buffer.encode_float(9, this->target_temperature_high);
|
||||||
buffer.encode_bool(10, this->has_away);
|
buffer.encode_bool(10, this->has_away);
|
||||||
buffer.encode_bool(11, this->away);
|
buffer.encode_bool(11, this->away);
|
||||||
|
buffer.encode_bool(12, this->has_fan_mode);
|
||||||
|
buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode);
|
||||||
|
buffer.encode_bool(14, this->has_swing_mode);
|
||||||
|
buffer.encode_enum<enums::ClimateSwingMode>(15, this->swing_mode);
|
||||||
}
|
}
|
||||||
void ClimateCommandRequest::dump_to(std::string &out) const {
|
void ClimateCommandRequest::dump_to(std::string &out) const {
|
||||||
char buffer[64];
|
char buffer[64];
|
||||||
@@ -2705,7 +2961,7 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
|
|||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" mode: ");
|
out.append(" mode: ");
|
||||||
out.append(proto_enum_to_string<EnumClimateMode>(this->mode));
|
out.append(proto_enum_to_string<enums::ClimateMode>(this->mode));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
out.append(" has_target_temperature: ");
|
out.append(" has_target_temperature: ");
|
||||||
@@ -2742,6 +2998,22 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
|
|||||||
out.append(" away: ");
|
out.append(" away: ");
|
||||||
out.append(YESNO(this->away));
|
out.append(YESNO(this->away));
|
||||||
out.append("\n");
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" has_fan_mode: ");
|
||||||
|
out.append(YESNO(this->has_fan_mode));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" fan_mode: ");
|
||||||
|
out.append(proto_enum_to_string<enums::ClimateFanMode>(this->fan_mode));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" has_swing_mode: ");
|
||||||
|
out.append(YESNO(this->has_swing_mode));
|
||||||
|
out.append("\n");
|
||||||
|
|
||||||
|
out.append(" swing_mode: ");
|
||||||
|
out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
|
||||||
|
out.append("\n");
|
||||||
out.append("}");
|
out.append("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -5,26 +7,32 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
enum EnumLegacyCoverState : uint32_t {
|
namespace enums {
|
||||||
|
|
||||||
|
enum LegacyCoverState : uint32_t {
|
||||||
LEGACY_COVER_STATE_OPEN = 0,
|
LEGACY_COVER_STATE_OPEN = 0,
|
||||||
LEGACY_COVER_STATE_CLOSED = 1,
|
LEGACY_COVER_STATE_CLOSED = 1,
|
||||||
};
|
};
|
||||||
enum EnumCoverOperation : uint32_t {
|
enum CoverOperation : uint32_t {
|
||||||
COVER_OPERATION_IDLE = 0,
|
COVER_OPERATION_IDLE = 0,
|
||||||
COVER_OPERATION_IS_OPENING = 1,
|
COVER_OPERATION_IS_OPENING = 1,
|
||||||
COVER_OPERATION_IS_CLOSING = 2,
|
COVER_OPERATION_IS_CLOSING = 2,
|
||||||
};
|
};
|
||||||
enum EnumLegacyCoverCommand : uint32_t {
|
enum LegacyCoverCommand : uint32_t {
|
||||||
LEGACY_COVER_COMMAND_OPEN = 0,
|
LEGACY_COVER_COMMAND_OPEN = 0,
|
||||||
LEGACY_COVER_COMMAND_CLOSE = 1,
|
LEGACY_COVER_COMMAND_CLOSE = 1,
|
||||||
LEGACY_COVER_COMMAND_STOP = 2,
|
LEGACY_COVER_COMMAND_STOP = 2,
|
||||||
};
|
};
|
||||||
enum EnumFanSpeed : uint32_t {
|
enum FanSpeed : uint32_t {
|
||||||
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 EnumLogLevel : uint32_t {
|
enum FanDirection : uint32_t {
|
||||||
|
FAN_DIRECTION_FORWARD = 0,
|
||||||
|
FAN_DIRECTION_REVERSE = 1,
|
||||||
|
};
|
||||||
|
enum LogLevel : uint32_t {
|
||||||
LOG_LEVEL_NONE = 0,
|
LOG_LEVEL_NONE = 0,
|
||||||
LOG_LEVEL_ERROR = 1,
|
LOG_LEVEL_ERROR = 1,
|
||||||
LOG_LEVEL_WARN = 2,
|
LOG_LEVEL_WARN = 2,
|
||||||
@@ -33,7 +41,7 @@ enum EnumLogLevel : uint32_t {
|
|||||||
LOG_LEVEL_VERBOSE = 5,
|
LOG_LEVEL_VERBOSE = 5,
|
||||||
LOG_LEVEL_VERY_VERBOSE = 6,
|
LOG_LEVEL_VERY_VERBOSE = 6,
|
||||||
};
|
};
|
||||||
enum EnumServiceArgType : uint32_t {
|
enum ServiceArgType : uint32_t {
|
||||||
SERVICE_ARG_TYPE_BOOL = 0,
|
SERVICE_ARG_TYPE_BOOL = 0,
|
||||||
SERVICE_ARG_TYPE_INT = 1,
|
SERVICE_ARG_TYPE_INT = 1,
|
||||||
SERVICE_ARG_TYPE_FLOAT = 2,
|
SERVICE_ARG_TYPE_FLOAT = 2,
|
||||||
@@ -43,17 +51,42 @@ enum EnumServiceArgType : uint32_t {
|
|||||||
SERVICE_ARG_TYPE_FLOAT_ARRAY = 6,
|
SERVICE_ARG_TYPE_FLOAT_ARRAY = 6,
|
||||||
SERVICE_ARG_TYPE_STRING_ARRAY = 7,
|
SERVICE_ARG_TYPE_STRING_ARRAY = 7,
|
||||||
};
|
};
|
||||||
enum EnumClimateMode : uint32_t {
|
enum ClimateMode : uint32_t {
|
||||||
CLIMATE_MODE_OFF = 0,
|
CLIMATE_MODE_OFF = 0,
|
||||||
CLIMATE_MODE_AUTO = 1,
|
CLIMATE_MODE_AUTO = 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,
|
||||||
};
|
};
|
||||||
enum EnumClimateAction : uint32_t {
|
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 {
|
||||||
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace enums
|
||||||
|
|
||||||
class HelloRequest : public ProtoMessage {
|
class HelloRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string client_info{}; // NOLINT
|
std::string client_info{}; // NOLINT
|
||||||
@@ -183,8 +216,9 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class BinarySensorStateResponse : public ProtoMessage {
|
class BinarySensorStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0}; // NOLINT
|
||||||
bool state{false}; // NOLINT
|
bool state{false}; // NOLINT
|
||||||
|
bool missing_state{false}; // NOLINT
|
||||||
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;
|
||||||
|
|
||||||
@@ -212,11 +246,11 @@ class ListEntitiesCoverResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class CoverStateResponse : public ProtoMessage {
|
class CoverStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0}; // NOLINT
|
||||||
EnumLegacyCoverState legacy_state{}; // NOLINT
|
enums::LegacyCoverState legacy_state{}; // NOLINT
|
||||||
float position{0.0f}; // NOLINT
|
float position{0.0f}; // NOLINT
|
||||||
float tilt{0.0f}; // NOLINT
|
float tilt{0.0f}; // NOLINT
|
||||||
EnumCoverOperation current_operation{}; // NOLINT
|
enums::CoverOperation current_operation{}; // NOLINT
|
||||||
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;
|
||||||
|
|
||||||
@@ -226,14 +260,14 @@ class CoverStateResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class CoverCommandRequest : public ProtoMessage {
|
class CoverCommandRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0}; // NOLINT
|
||||||
bool has_legacy_command{false}; // NOLINT
|
bool has_legacy_command{false}; // NOLINT
|
||||||
EnumLegacyCoverCommand legacy_command{}; // NOLINT
|
enums::LegacyCoverCommand legacy_command{}; // NOLINT
|
||||||
bool has_position{false}; // NOLINT
|
bool has_position{false}; // NOLINT
|
||||||
float position{0.0f}; // NOLINT
|
float position{0.0f}; // NOLINT
|
||||||
bool has_tilt{false}; // NOLINT
|
bool has_tilt{false}; // NOLINT
|
||||||
float tilt{0.0f}; // NOLINT
|
float tilt{0.0f}; // NOLINT
|
||||||
bool stop{false}; // NOLINT
|
bool stop{false}; // NOLINT
|
||||||
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;
|
||||||
|
|
||||||
@@ -249,6 +283,8 @@ class ListEntitiesFanResponse : public ProtoMessage {
|
|||||||
std::string unique_id{}; // NOLINT
|
std::string unique_id{}; // NOLINT
|
||||||
bool supports_oscillation{false}; // NOLINT
|
bool supports_oscillation{false}; // NOLINT
|
||||||
bool supports_speed{false}; // NOLINT
|
bool supports_speed{false}; // NOLINT
|
||||||
|
bool supports_direction{false}; // NOLINT
|
||||||
|
int32_t supported_speed_count{0}; // NOLINT
|
||||||
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;
|
||||||
|
|
||||||
@@ -259,10 +295,12 @@ class ListEntitiesFanResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class FanStateResponse : public ProtoMessage {
|
class FanStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0}; // NOLINT
|
||||||
bool state{false}; // NOLINT
|
bool state{false}; // NOLINT
|
||||||
bool oscillating{false}; // NOLINT
|
bool oscillating{false}; // NOLINT
|
||||||
EnumFanSpeed speed{}; // NOLINT
|
enums::FanSpeed speed{}; // NOLINT
|
||||||
|
enums::FanDirection direction{}; // NOLINT
|
||||||
|
int32_t speed_level{0}; // NOLINT
|
||||||
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;
|
||||||
|
|
||||||
@@ -272,13 +310,17 @@ class FanStateResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class FanCommandRequest : public ProtoMessage {
|
class FanCommandRequest : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0}; // NOLINT
|
||||||
bool has_state{false}; // NOLINT
|
bool has_state{false}; // NOLINT
|
||||||
bool state{false}; // NOLINT
|
bool state{false}; // NOLINT
|
||||||
bool has_speed{false}; // NOLINT
|
bool has_speed{false}; // NOLINT
|
||||||
EnumFanSpeed speed{}; // NOLINT
|
enums::FanSpeed speed{}; // NOLINT
|
||||||
bool has_oscillating{false}; // NOLINT
|
bool has_oscillating{false}; // NOLINT
|
||||||
bool oscillating{false}; // NOLINT
|
bool oscillating{false}; // NOLINT
|
||||||
|
bool has_direction{false}; // NOLINT
|
||||||
|
enums::FanDirection direction{}; // NOLINT
|
||||||
|
bool has_speed_level{false}; // NOLINT
|
||||||
|
int32_t speed_level{0}; // NOLINT
|
||||||
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;
|
||||||
|
|
||||||
@@ -364,6 +406,8 @@ class ListEntitiesSensorResponse : public ProtoMessage {
|
|||||||
std::string icon{}; // NOLINT
|
std::string icon{}; // NOLINT
|
||||||
std::string unit_of_measurement{}; // NOLINT
|
std::string unit_of_measurement{}; // NOLINT
|
||||||
int32_t accuracy_decimals{0}; // NOLINT
|
int32_t accuracy_decimals{0}; // NOLINT
|
||||||
|
bool force_update{false}; // NOLINT
|
||||||
|
std::string device_class{}; // NOLINT
|
||||||
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;
|
||||||
|
|
||||||
@@ -374,13 +418,15 @@ class ListEntitiesSensorResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class SensorStateResponse : public ProtoMessage {
|
class SensorStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0}; // NOLINT
|
||||||
float state{0.0f}; // NOLINT
|
float state{0.0f}; // NOLINT
|
||||||
|
bool missing_state{false}; // NOLINT
|
||||||
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:
|
||||||
@@ -436,18 +482,20 @@ class ListEntitiesTextSensorResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class TextSensorStateResponse : public ProtoMessage {
|
class TextSensorStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0}; // NOLINT
|
||||||
std::string state{}; // NOLINT
|
std::string state{}; // NOLINT
|
||||||
|
bool missing_state{false}; // NOLINT
|
||||||
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:
|
||||||
EnumLogLevel level{}; // NOLINT
|
enums::LogLevel level{}; // NOLINT
|
||||||
bool dump_config{false}; // NOLINT
|
bool dump_config{false}; // NOLINT
|
||||||
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;
|
||||||
@@ -457,7 +505,7 @@ class SubscribeLogsRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class SubscribeLogsResponse : public ProtoMessage {
|
class SubscribeLogsResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
EnumLogLevel level{}; // NOLINT
|
enums::LogLevel level{}; // NOLINT
|
||||||
std::string tag{}; // NOLINT
|
std::string tag{}; // NOLINT
|
||||||
std::string message{}; // NOLINT
|
std::string message{}; // NOLINT
|
||||||
bool send_failed{false}; // NOLINT
|
bool send_failed{false}; // NOLINT
|
||||||
@@ -543,8 +591,8 @@ class GetTimeResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ListEntitiesServicesArgument : public ProtoMessage {
|
class ListEntitiesServicesArgument : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string name{}; // NOLINT
|
std::string name{}; // NOLINT
|
||||||
EnumServiceArgType type{}; // NOLINT
|
enums::ServiceArgType type{}; // NOLINT
|
||||||
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;
|
||||||
|
|
||||||
@@ -632,18 +680,20 @@ class CameraImageRequest : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ListEntitiesClimateResponse : public ProtoMessage {
|
class ListEntitiesClimateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
std::string object_id{}; // NOLINT
|
std::string object_id{}; // NOLINT
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0}; // NOLINT
|
||||||
std::string name{}; // NOLINT
|
std::string name{}; // NOLINT
|
||||||
std::string unique_id{}; // NOLINT
|
std::string unique_id{}; // NOLINT
|
||||||
bool supports_current_temperature{false}; // NOLINT
|
bool supports_current_temperature{false}; // NOLINT
|
||||||
bool supports_two_point_target_temperature{false}; // NOLINT
|
bool supports_two_point_target_temperature{false}; // NOLINT
|
||||||
std::vector<EnumClimateMode> supported_modes{}; // NOLINT
|
std::vector<enums::ClimateMode> supported_modes{}; // NOLINT
|
||||||
float visual_min_temperature{0.0f}; // NOLINT
|
float visual_min_temperature{0.0f}; // NOLINT
|
||||||
float visual_max_temperature{0.0f}; // NOLINT
|
float visual_max_temperature{0.0f}; // NOLINT
|
||||||
float visual_temperature_step{0.0f}; // NOLINT
|
float visual_temperature_step{0.0f}; // NOLINT
|
||||||
bool supports_away{false}; // NOLINT
|
bool supports_away{false}; // NOLINT
|
||||||
bool supports_action{false}; // NOLINT
|
bool supports_action{false}; // NOLINT
|
||||||
|
std::vector<enums::ClimateFanMode> supported_fan_modes{}; // NOLINT
|
||||||
|
std::vector<enums::ClimateSwingMode> supported_swing_modes{}; // NOLINT
|
||||||
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;
|
||||||
|
|
||||||
@@ -654,14 +704,16 @@ class ListEntitiesClimateResponse : public ProtoMessage {
|
|||||||
};
|
};
|
||||||
class ClimateStateResponse : public ProtoMessage {
|
class ClimateStateResponse : public ProtoMessage {
|
||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0}; // NOLINT
|
||||||
EnumClimateMode mode{}; // NOLINT
|
enums::ClimateMode mode{}; // NOLINT
|
||||||
float current_temperature{0.0f}; // NOLINT
|
float current_temperature{0.0f}; // NOLINT
|
||||||
float target_temperature{0.0f}; // NOLINT
|
float target_temperature{0.0f}; // NOLINT
|
||||||
float target_temperature_low{0.0f}; // NOLINT
|
float target_temperature_low{0.0f}; // NOLINT
|
||||||
float target_temperature_high{0.0f}; // NOLINT
|
float target_temperature_high{0.0f}; // NOLINT
|
||||||
bool away{false}; // NOLINT
|
bool away{false}; // NOLINT
|
||||||
EnumClimateAction action{}; // NOLINT
|
enums::ClimateAction action{}; // NOLINT
|
||||||
|
enums::ClimateFanMode fan_mode{}; // NOLINT
|
||||||
|
enums::ClimateSwingMode swing_mode{}; // NOLINT
|
||||||
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;
|
||||||
|
|
||||||
@@ -673,7 +725,7 @@ class ClimateCommandRequest : public ProtoMessage {
|
|||||||
public:
|
public:
|
||||||
uint32_t key{0}; // NOLINT
|
uint32_t key{0}; // NOLINT
|
||||||
bool has_mode{false}; // NOLINT
|
bool has_mode{false}; // NOLINT
|
||||||
EnumClimateMode mode{}; // NOLINT
|
enums::ClimateMode mode{}; // NOLINT
|
||||||
bool has_target_temperature{false}; // NOLINT
|
bool has_target_temperature{false}; // NOLINT
|
||||||
float target_temperature{0.0f}; // NOLINT
|
float target_temperature{0.0f}; // NOLINT
|
||||||
bool has_target_temperature_low{false}; // NOLINT
|
bool has_target_temperature_low{false}; // NOLINT
|
||||||
@@ -682,6 +734,10 @@ class ClimateCommandRequest : public ProtoMessage {
|
|||||||
float target_temperature_high{0.0f}; // NOLINT
|
float target_temperature_high{0.0f}; // NOLINT
|
||||||
bool has_away{false}; // NOLINT
|
bool has_away{false}; // NOLINT
|
||||||
bool away{false}; // NOLINT
|
bool away{false}; // NOLINT
|
||||||
|
bool has_fan_mode{false}; // NOLINT
|
||||||
|
enums::ClimateFanMode fan_mode{}; // NOLINT
|
||||||
|
bool has_swing_mode{false}; // NOLINT
|
||||||
|
enums::ClimateSwingMode swing_mode{}; // NOLINT
|
||||||
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;
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// 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"
|
||||||
|
|
||||||
@@ -8,69 +10,57 @@ static const char *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,14 +173,12 @@ 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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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...);
|
||||||
|
|||||||
@@ -62,8 +62,7 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
|
|||||||
error = true;
|
error = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
uint32_t val = (uint32_t(buffer[i]) << 0) | (uint32_t(buffer[i + 1]) << 8) | (uint32_t(buffer[i + 2]) << 16) |
|
uint32_t val = encode_uint32(buffer[i + 3], buffer[i + 2], buffer[i + 1], buffer[i]);
|
||||||
(uint32_t(buffer[i + 3]) << 24);
|
|
||||||
if (!this->decode_32bit(field_id, Proto32Bit(val))) {
|
if (!this->decode_32bit(field_id, Proto32Bit(val))) {
|
||||||
ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val);
|
ESP_LOGV(TAG, "Cannot decode 32-bit field %u with value %u!", field_id, val);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -266,7 +266,6 @@ class ProtoService {
|
|||||||
virtual ProtoWriteBuffer create_buffer() = 0;
|
virtual ProtoWriteBuffer create_buffer() = 0;
|
||||||
virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0;
|
virtual bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) = 0;
|
||||||
virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
|
virtual bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) = 0;
|
||||||
virtual void set_nodelay(bool nodelay) = 0;
|
|
||||||
|
|
||||||
template<class C> bool send_message_(const C &msg, uint32_t message_type) {
|
template<class C> bool send_message_(const C &msg, uint32_t message_type) {
|
||||||
auto buffer = this->create_buffer();
|
auto buffer = this->create_buffer();
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ namespace api {
|
|||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
|
bool InitialStateIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
|
||||||
if (!binary_sensor->has_state())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state);
|
return this->client_->send_binary_sensor_state(binary_sensor, binary_sensor->state);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -24,9 +21,6 @@ bool InitialStateIterator::on_light(light::LightState *light) { return this->cli
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) {
|
bool InitialStateIterator::on_sensor(sensor::Sensor *sensor) {
|
||||||
if (!sensor->has_state())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return this->client_->send_sensor_state(sensor, sensor->state);
|
return this->client_->send_sensor_state(sensor, sensor->state);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -37,9 +31,6 @@ bool InitialStateIterator::on_switch(switch_::Switch *a_switch) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
|
bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
|
||||||
if (!text_sensor->has_state())
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return this->client_->send_text_sensor_state(text_sensor, text_sensor->state);
|
return this->client_->send_text_sensor_state(text_sensor, text_sensor->state);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -25,14 +25,18 @@ template<> std::vector<std::string> get_execute_arg_value<std::vector<std::strin
|
|||||||
return arg.string_array;
|
return arg.string_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> EnumServiceArgType to_service_arg_type<bool>() { return SERVICE_ARG_TYPE_BOOL; }
|
template<> enums::ServiceArgType to_service_arg_type<bool>() { return enums::SERVICE_ARG_TYPE_BOOL; }
|
||||||
template<> EnumServiceArgType to_service_arg_type<int>() { return SERVICE_ARG_TYPE_INT; }
|
template<> enums::ServiceArgType to_service_arg_type<int>() { return enums::SERVICE_ARG_TYPE_INT; }
|
||||||
template<> EnumServiceArgType to_service_arg_type<float>() { return SERVICE_ARG_TYPE_FLOAT; }
|
template<> enums::ServiceArgType to_service_arg_type<float>() { return enums::SERVICE_ARG_TYPE_FLOAT; }
|
||||||
template<> EnumServiceArgType to_service_arg_type<std::string>() { return SERVICE_ARG_TYPE_STRING; }
|
template<> enums::ServiceArgType to_service_arg_type<std::string>() { return enums::SERVICE_ARG_TYPE_STRING; }
|
||||||
template<> EnumServiceArgType to_service_arg_type<std::vector<bool>>() { return SERVICE_ARG_TYPE_BOOL_ARRAY; }
|
template<> enums::ServiceArgType to_service_arg_type<std::vector<bool>>() { return enums::SERVICE_ARG_TYPE_BOOL_ARRAY; }
|
||||||
template<> EnumServiceArgType to_service_arg_type<std::vector<int>>() { return SERVICE_ARG_TYPE_INT_ARRAY; }
|
template<> enums::ServiceArgType to_service_arg_type<std::vector<int>>() { return enums::SERVICE_ARG_TYPE_INT_ARRAY; }
|
||||||
template<> EnumServiceArgType to_service_arg_type<std::vector<float>>() { return SERVICE_ARG_TYPE_FLOAT_ARRAY; }
|
template<> enums::ServiceArgType to_service_arg_type<std::vector<float>>() {
|
||||||
template<> EnumServiceArgType to_service_arg_type<std::vector<std::string>>() { return SERVICE_ARG_TYPE_STRING_ARRAY; }
|
return enums::SERVICE_ARG_TYPE_FLOAT_ARRAY;
|
||||||
|
}
|
||||||
|
template<> enums::ServiceArgType to_service_arg_type<std::vector<std::string>>() {
|
||||||
|
return enums::SERVICE_ARG_TYPE_STRING_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class UserServiceDescriptor {
|
|||||||
|
|
||||||
template<typename T> T get_execute_arg_value(const ExecuteServiceArgument &arg);
|
template<typename T> T get_execute_arg_value(const ExecuteServiceArgument &arg);
|
||||||
|
|
||||||
template<typename T> EnumServiceArgType to_service_arg_type();
|
template<typename T> enums::ServiceArgType to_service_arg_type();
|
||||||
|
|
||||||
template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
||||||
public:
|
public:
|
||||||
@@ -29,7 +29,7 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
|||||||
ListEntitiesServicesResponse msg;
|
ListEntitiesServicesResponse msg;
|
||||||
msg.name = this->name_;
|
msg.name = this->name_;
|
||||||
msg.key = this->key_;
|
msg.key = this->key_;
|
||||||
std::array<EnumServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...};
|
std::array<enums::ServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...};
|
||||||
for (int i = 0; i < sizeof...(Ts); i++) {
|
for (int i = 0; i < sizeof...(Ts); i++) {
|
||||||
ListEntitiesServicesArgument arg;
|
ListEntitiesServicesArgument arg;
|
||||||
arg.type = arg_types[i];
|
arg.type = arg_types[i];
|
||||||
|
|||||||
@@ -1,42 +1,51 @@
|
|||||||
import esphome.codegen as cg
|
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.const import CONF_PIN, CONF_INDOOR, CONF_WATCHDOG_THRESHOLD, \
|
from esphome.const import (
|
||||||
CONF_NOISE_LEVEL, CONF_SPIKE_REJECTION, CONF_LIGHTNING_THRESHOLD, \
|
CONF_INDOOR,
|
||||||
CONF_MASK_DISTURBER, CONF_DIV_RATIO, CONF_CAPACITANCE
|
CONF_WATCHDOG_THRESHOLD,
|
||||||
|
CONF_NOISE_LEVEL,
|
||||||
|
CONF_SPIKE_REJECTION,
|
||||||
|
CONF_LIGHTNING_THRESHOLD,
|
||||||
|
CONF_MASK_DISTURBER,
|
||||||
|
CONF_DIV_RATIO,
|
||||||
|
CONF_CAPACITANCE,
|
||||||
|
)
|
||||||
from esphome.core import coroutine
|
from esphome.core import coroutine
|
||||||
|
|
||||||
|
AUTO_LOAD = ["sensor", "binary_sensor"]
|
||||||
AUTO_LOAD = ['sensor', 'binary_sensor']
|
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
|
||||||
CONF_AS3935_ID = 'as3935_id'
|
CONF_AS3935_ID = "as3935_id"
|
||||||
|
|
||||||
as3935_ns = cg.esphome_ns.namespace('as3935')
|
as3935_ns = cg.esphome_ns.namespace("as3935")
|
||||||
AS3935 = as3935_ns.class_('AS3935Component', cg.Component)
|
AS3935 = as3935_ns.class_("AS3935Component", cg.Component)
|
||||||
|
|
||||||
AS3935_SCHEMA = cv.Schema({
|
CONF_IRQ_PIN = "irq_pin"
|
||||||
cv.GenerateID(): cv.declare_id(AS3935),
|
AS3935_SCHEMA = cv.Schema(
|
||||||
cv.Required(CONF_PIN): cv.All(pins.internal_gpio_input_pin_schema,
|
{
|
||||||
pins.validate_has_interrupt),
|
cv.GenerateID(): cv.declare_id(AS3935),
|
||||||
|
cv.Required(CONF_IRQ_PIN): pins.gpio_input_pin_schema,
|
||||||
cv.Optional(CONF_INDOOR, default=True): cv.boolean,
|
cv.Optional(CONF_INDOOR, default=True): cv.boolean,
|
||||||
cv.Optional(CONF_NOISE_LEVEL, default=2): cv.int_range(min=1, max=7),
|
cv.Optional(CONF_NOISE_LEVEL, default=2): cv.int_range(min=1, max=7),
|
||||||
cv.Optional(CONF_WATCHDOG_THRESHOLD, default=2): cv.int_range(min=1, max=10),
|
cv.Optional(CONF_WATCHDOG_THRESHOLD, default=2): cv.int_range(min=1, max=10),
|
||||||
cv.Optional(CONF_SPIKE_REJECTION, default=2): cv.int_range(min=1, max=11),
|
cv.Optional(CONF_SPIKE_REJECTION, default=2): cv.int_range(min=1, max=11),
|
||||||
cv.Optional(CONF_LIGHTNING_THRESHOLD, default=1): cv.one_of(1, 5, 9, 16, int=True),
|
cv.Optional(CONF_LIGHTNING_THRESHOLD, default=1): cv.one_of(
|
||||||
cv.Optional(CONF_MASK_DISTURBER, default=False): cv.boolean,
|
1, 5, 9, 16, int=True
|
||||||
cv.Optional(CONF_DIV_RATIO, default=0): cv.one_of(0, 16, 22, 64, 128, int=True),
|
),
|
||||||
cv.Optional(CONF_CAPACITANCE, default=0): cv.int_range(min=0, max=15),
|
cv.Optional(CONF_MASK_DISTURBER, default=False): cv.boolean,
|
||||||
})
|
cv.Optional(CONF_DIV_RATIO, default=0): cv.one_of(0, 16, 32, 64, 128, int=True),
|
||||||
|
cv.Optional(CONF_CAPACITANCE, default=0): cv.int_range(min=0, max=15),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@coroutine
|
@coroutine
|
||||||
def setup_as3935(var, config):
|
def setup_as3935(var, config):
|
||||||
yield cg.register_component(var, config)
|
yield cg.register_component(var, config)
|
||||||
|
|
||||||
pin = yield cg.gpio_pin_expression(config[CONF_PIN])
|
irq_pin = yield cg.gpio_pin_expression(config[CONF_IRQ_PIN])
|
||||||
cg.add(var.set_pin(pin))
|
cg.add(var.set_irq_pin(irq_pin))
|
||||||
cg.add(var.set_indoor(config[CONF_INDOOR]))
|
cg.add(var.set_indoor(config[CONF_INDOOR]))
|
||||||
cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL]))
|
cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL]))
|
||||||
cg.add(var.set_watchdog_threshold(config[CONF_WATCHDOG_THRESHOLD]))
|
cg.add(var.set_watchdog_threshold(config[CONF_WATCHDOG_THRESHOLD]))
|
||||||
|
|||||||
@@ -9,10 +9,8 @@ static const char *TAG = "as3935";
|
|||||||
void AS3935Component::setup() {
|
void AS3935Component::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up AS3935...");
|
ESP_LOGCONFIG(TAG, "Setting up AS3935...");
|
||||||
|
|
||||||
this->pin_->setup();
|
this->irq_pin_->setup();
|
||||||
this->store_.pin = this->pin_->to_isr();
|
LOG_PIN(" IRQ Pin: ", this->irq_pin_);
|
||||||
LOG_PIN(" Interrupt Pin: ", this->pin_);
|
|
||||||
this->pin_->attach_interrupt(AS3935ComponentStore::gpio_intr, &this->store_, RISING);
|
|
||||||
|
|
||||||
// Write properties to sensor
|
// Write properties to sensor
|
||||||
this->write_indoor(this->indoor_);
|
this->write_indoor(this->indoor_);
|
||||||
@@ -27,13 +25,16 @@ void AS3935Component::setup() {
|
|||||||
|
|
||||||
void AS3935Component::dump_config() {
|
void AS3935Component::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "AS3935:");
|
ESP_LOGCONFIG(TAG, "AS3935:");
|
||||||
LOG_PIN(" Interrupt Pin: ", this->pin_);
|
LOG_PIN(" Interrupt Pin: ", this->irq_pin_);
|
||||||
|
LOG_BINARY_SENSOR(" ", "Thunder alert", this->thunder_alert_binary_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Distance", this->distance_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Lightning energy", this->energy_sensor_);
|
||||||
}
|
}
|
||||||
|
|
||||||
float AS3935Component::get_setup_priority() const { return setup_priority::DATA; }
|
float AS3935Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void AS3935Component::loop() {
|
void AS3935Component::loop() {
|
||||||
if (!this->store_.interrupt)
|
if (!this->irq_pin_->digital_read())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint8_t int_value = this->read_interrupt_register_();
|
uint8_t int_value = this->read_interrupt_register_();
|
||||||
@@ -53,7 +54,6 @@ void AS3935Component::loop() {
|
|||||||
this->energy_sensor_->publish_state(energy);
|
this->energy_sensor_->publish_state(energy);
|
||||||
}
|
}
|
||||||
this->thunder_alert_binary_sensor_->publish_state(false);
|
this->thunder_alert_binary_sensor_->publish_state(false);
|
||||||
this->store_.interrupt = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AS3935Component::write_indoor(bool indoor) {
|
void AS3935Component::write_indoor(bool indoor) {
|
||||||
@@ -222,7 +222,5 @@ uint8_t AS3935Component::read_register_(uint8_t reg, uint8_t mask) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ICACHE_RAM_ATTR AS3935ComponentStore::gpio_intr(AS3935ComponentStore *arg) { arg->interrupt = true; }
|
|
||||||
|
|
||||||
} // namespace as3935
|
} // namespace as3935
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -50,14 +50,6 @@ enum AS3935Values {
|
|||||||
NOISE_INT = 0x01
|
NOISE_INT = 0x01
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Store data in a class that doesn't use multiple-inheritance (vtables in flash)
|
|
||||||
struct AS3935ComponentStore {
|
|
||||||
volatile bool interrupt;
|
|
||||||
|
|
||||||
ISRInternalGPIOPin *pin;
|
|
||||||
static void gpio_intr(AS3935ComponentStore *arg);
|
|
||||||
};
|
|
||||||
|
|
||||||
class AS3935Component : public Component {
|
class AS3935Component : public Component {
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
@@ -65,7 +57,7 @@ class AS3935Component : public Component {
|
|||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
void loop() override;
|
void loop() override;
|
||||||
|
|
||||||
void set_pin(GPIOPin *pin) { pin_ = pin; }
|
void set_irq_pin(GPIOPin *irq_pin) { irq_pin_ = irq_pin; }
|
||||||
void set_distance_sensor(sensor::Sensor *distance_sensor) { distance_sensor_ = distance_sensor; }
|
void set_distance_sensor(sensor::Sensor *distance_sensor) { distance_sensor_ = distance_sensor; }
|
||||||
void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
|
void set_energy_sensor(sensor::Sensor *energy_sensor) { energy_sensor_ = energy_sensor; }
|
||||||
void set_thunder_alert_binary_sensor(binary_sensor::BinarySensor *thunder_alert_binary_sensor) {
|
void set_thunder_alert_binary_sensor(binary_sensor::BinarySensor *thunder_alert_binary_sensor) {
|
||||||
@@ -102,8 +94,7 @@ class AS3935Component : public Component {
|
|||||||
sensor::Sensor *distance_sensor_;
|
sensor::Sensor *distance_sensor_;
|
||||||
sensor::Sensor *energy_sensor_;
|
sensor::Sensor *energy_sensor_;
|
||||||
binary_sensor::BinarySensor *thunder_alert_binary_sensor_;
|
binary_sensor::BinarySensor *thunder_alert_binary_sensor_;
|
||||||
GPIOPin *pin_;
|
GPIOPin *irq_pin_;
|
||||||
AS3935ComponentStore store_;
|
|
||||||
|
|
||||||
bool indoor_;
|
bool indoor_;
|
||||||
uint8_t noise_level_;
|
uint8_t noise_level_;
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ import esphome.config_validation as cv
|
|||||||
from esphome.components import binary_sensor
|
from esphome.components import binary_sensor
|
||||||
from . import AS3935, CONF_AS3935_ID
|
from . import AS3935, CONF_AS3935_ID
|
||||||
|
|
||||||
DEPENDENCIES = ['as3935']
|
DEPENDENCIES = ["as3935"]
|
||||||
|
|
||||||
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend({
|
CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||||
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
|
{
|
||||||
})
|
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
|
|||||||
@@ -1,19 +1,30 @@
|
|||||||
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_DISTANCE, CONF_LIGHTNING_ENERGY, \
|
from esphome.const import (
|
||||||
UNIT_KILOMETER, UNIT_EMPTY, ICON_SIGNAL_DISTANCE_VARIANT, ICON_FLASH
|
CONF_DISTANCE,
|
||||||
|
CONF_LIGHTNING_ENERGY,
|
||||||
|
DEVICE_CLASS_EMPTY,
|
||||||
|
UNIT_KILOMETER,
|
||||||
|
UNIT_EMPTY,
|
||||||
|
ICON_SIGNAL_DISTANCE_VARIANT,
|
||||||
|
ICON_FLASH,
|
||||||
|
)
|
||||||
from . import AS3935, CONF_AS3935_ID
|
from . import AS3935, CONF_AS3935_ID
|
||||||
|
|
||||||
DEPENDENCIES = ['as3935']
|
DEPENDENCIES = ["as3935"]
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.Schema({
|
CONFIG_SCHEMA = cv.Schema(
|
||||||
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
|
{
|
||||||
cv.Optional(CONF_DISTANCE):
|
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
|
||||||
sensor.sensor_schema(UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1),
|
cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
|
||||||
cv.Optional(CONF_LIGHTNING_ENERGY):
|
UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1, DEVICE_CLASS_EMPTY
|
||||||
sensor.sensor_schema(UNIT_EMPTY, ICON_FLASH, 1),
|
),
|
||||||
}).extend(cv.COMPONENT_SCHEMA)
|
cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema(
|
||||||
|
UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY
|
||||||
|
),
|
||||||
|
}
|
||||||
|
).extend(cv.COMPONENT_SCHEMA)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
@@ -27,4 +38,4 @@ def to_code(config):
|
|||||||
if CONF_LIGHTNING_ENERGY in config:
|
if CONF_LIGHTNING_ENERGY in config:
|
||||||
conf = config[CONF_LIGHTNING_ENERGY]
|
conf = config[CONF_LIGHTNING_ENERGY]
|
||||||
lightning_energy_sensor = yield sensor.new_sensor(conf)
|
lightning_energy_sensor = yield sensor.new_sensor(conf)
|
||||||
cg.add(hub.set_distance_sensor(lightning_energy_sensor))
|
cg.add(hub.set_energy_sensor(lightning_energy_sensor))
|
||||||
|
|||||||
@@ -3,15 +3,21 @@ import esphome.config_validation as cv
|
|||||||
from esphome.components import as3935, i2c
|
from esphome.components import as3935, i2c
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
AUTO_LOAD = ['as3935']
|
AUTO_LOAD = ["as3935"]
|
||||||
DEPENDENCIES = ['i2c']
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|
||||||
as3935_i2c_ns = cg.esphome_ns.namespace('as3935_i2c')
|
as3935_i2c_ns = cg.esphome_ns.namespace("as3935_i2c")
|
||||||
I2CAS3935 = as3935_i2c_ns.class_('I2CAS3935Component', as3935.AS3935, i2c.I2CDevice)
|
I2CAS3935 = as3935_i2c_ns.class_("I2CAS3935Component", as3935.AS3935, i2c.I2CDevice)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(as3935.AS3935_SCHEMA.extend({
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.GenerateID(): cv.declare_id(I2CAS3935),
|
as3935.AS3935_SCHEMA.extend(
|
||||||
}).extend(cv.COMPONENT_SCHEMA).extend(i2c.i2c_device_schema(0x03)))
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(I2CAS3935),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.extend(i2c.i2c_device_schema(0x03))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ uint8_t I2CAS3935Component::read_register(uint8_t reg) {
|
|||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
void I2CAS3935Component::dump_config() {
|
||||||
|
AS3935Component::dump_config();
|
||||||
|
LOG_I2C_DEVICE(this);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace as3935_i2c
|
} // namespace as3935_i2c
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ namespace esphome {
|
|||||||
namespace as3935_i2c {
|
namespace as3935_i2c {
|
||||||
|
|
||||||
class I2CAS3935Component : public as3935::AS3935Component, public i2c::I2CDevice {
|
class I2CAS3935Component : public as3935::AS3935Component, public i2c::I2CDevice {
|
||||||
|
public:
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position) override;
|
void write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_position) override;
|
||||||
uint8_t read_register(uint8_t reg) override;
|
uint8_t read_register(uint8_t reg) override;
|
||||||
|
|||||||
@@ -3,15 +3,21 @@ import esphome.config_validation as cv
|
|||||||
from esphome.components import as3935, spi
|
from esphome.components import as3935, spi
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
AUTO_LOAD = ['as3935']
|
AUTO_LOAD = ["as3935"]
|
||||||
DEPENDENCIES = ['spi']
|
DEPENDENCIES = ["spi"]
|
||||||
|
|
||||||
as3935_spi_ns = cg.esphome_ns.namespace('as3935_spi')
|
as3935_spi_ns = cg.esphome_ns.namespace("as3935_spi")
|
||||||
SPIAS3935 = as3935_spi_ns.class_('SPIAS3935Component', as3935.AS3935, spi.SPIDevice)
|
SPIAS3935 = as3935_spi_ns.class_("SPIAS3935Component", as3935.AS3935, spi.SPIDevice)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(as3935.AS3935_SCHEMA.extend({
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.GenerateID(): cv.declare_id(SPIAS3935)
|
as3935.AS3935_SCHEMA.extend(
|
||||||
}).extend(cv.COMPONENT_SCHEMA).extend(spi.SPI_DEVICE_SCHEMA))
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(SPIAS3935),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.extend(spi.spi_device_schema(cs_pin_required=True))
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
|
|||||||
@@ -1,23 +1,15 @@
|
|||||||
# Dummy integration to allow relying on AsyncTCP
|
# Dummy integration to allow relying on AsyncTCP
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.const import ARDUINO_VERSION_ESP32_1_0_0, ARDUINO_VERSION_ESP32_1_0_1, \
|
|
||||||
ARDUINO_VERSION_ESP32_1_0_2
|
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
|
|
||||||
|
CODEOWNERS = ["@OttoWinter"]
|
||||||
|
|
||||||
|
|
||||||
@coroutine_with_priority(200.0)
|
@coroutine_with_priority(200.0)
|
||||||
def to_code(config):
|
def to_code(config):
|
||||||
if CORE.is_esp32:
|
if CORE.is_esp32:
|
||||||
# https://github.com/me-no-dev/AsyncTCP/blob/master/library.json
|
# https://github.com/OttoWinter/AsyncTCP/blob/master/library.json
|
||||||
versions_requiring_older_asynctcp = [
|
cg.add_library("AsyncTCP-esphome", "1.1.1")
|
||||||
ARDUINO_VERSION_ESP32_1_0_0,
|
|
||||||
ARDUINO_VERSION_ESP32_1_0_1,
|
|
||||||
ARDUINO_VERSION_ESP32_1_0_2,
|
|
||||||
]
|
|
||||||
if CORE.arduino_version in versions_requiring_older_asynctcp:
|
|
||||||
cg.add_library('AsyncTCP', '1.0.3')
|
|
||||||
else:
|
|
||||||
cg.add_library('AsyncTCP', '1.1.1')
|
|
||||||
elif CORE.is_esp8266:
|
elif CORE.is_esp8266:
|
||||||
# https://github.com/OttoWinter/ESPAsyncTCP
|
# https://github.com/OttoWinter/ESPAsyncTCP
|
||||||
cg.add_library('ESPAsyncTCP-esphome', '1.2.2')
|
cg.add_library("ESPAsyncTCP-esphome", "1.2.3")
|
||||||
|
|||||||
0
esphome/components/atc_mithermometer/__init__.py
Normal file
0
esphome/components/atc_mithermometer/__init__.py
Normal file
137
esphome/components/atc_mithermometer/atc_mithermometer.cpp
Normal file
137
esphome/components/atc_mithermometer/atc_mithermometer.cpp
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
#include "atc_mithermometer.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace atc_mithermometer {
|
||||||
|
|
||||||
|
static const char *TAG = "atc_mithermometer";
|
||||||
|
|
||||||
|
void ATCMiThermometer::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG, "ATC MiThermometer");
|
||||||
|
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||||
|
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
||||||
|
LOG_SENSOR(" ", "Battery Level", this->battery_level_);
|
||||||
|
LOG_SENSOR(" ", "Battery Voltage", this->battery_voltage_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ATCMiThermometer::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||||
|
if (device.address_uint64() != this->address_) {
|
||||||
|
ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str());
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
for (auto &service_data : device.get_service_datas()) {
|
||||||
|
auto res = parse_header(service_data);
|
||||||
|
if (res->is_duplicate) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(parse_message(service_data.data, *res))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(report_results(res, device.address_str()))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (res->temperature.has_value() && this->temperature_ != nullptr)
|
||||||
|
this->temperature_->publish_state(*res->temperature);
|
||||||
|
if (res->humidity.has_value() && this->humidity_ != nullptr)
|
||||||
|
this->humidity_->publish_state(*res->humidity);
|
||||||
|
if (res->battery_level.has_value() && this->battery_level_ != nullptr)
|
||||||
|
this->battery_level_->publish_state(*res->battery_level);
|
||||||
|
if (res->battery_voltage.has_value() && this->battery_voltage_ != nullptr)
|
||||||
|
this->battery_voltage_->publish_state(*res->battery_voltage);
|
||||||
|
success = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional<ParseResult> ATCMiThermometer::parse_header(const esp32_ble_tracker::ServiceData &service_data) {
|
||||||
|
ParseResult result;
|
||||||
|
if (!service_data.uuid.contains(0x1A, 0x18)) {
|
||||||
|
ESP_LOGVV(TAG, "parse_header(): no service data UUID magic bytes.");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto raw = service_data.data;
|
||||||
|
|
||||||
|
static uint8_t last_frame_count = 0;
|
||||||
|
if (last_frame_count == raw[12]) {
|
||||||
|
ESP_LOGVV(TAG, "parse_header(): duplicate data packet received (%d).", static_cast<int>(last_frame_count));
|
||||||
|
result.is_duplicate = true;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
last_frame_count = raw[12];
|
||||||
|
result.is_duplicate = false;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ATCMiThermometer::parse_message(const std::vector<uint8_t> &message, ParseResult &result) {
|
||||||
|
// Byte 0-5 mac in correct order
|
||||||
|
// Byte 6-7 Temperature in uint16
|
||||||
|
// Byte 8 Humidity in percent
|
||||||
|
// Byte 9 Battery in percent
|
||||||
|
// Byte 10-11 Battery in mV uint16_t
|
||||||
|
// Byte 12 frame packet counter
|
||||||
|
|
||||||
|
const uint8_t *data = message.data();
|
||||||
|
const int data_length = 13;
|
||||||
|
|
||||||
|
if (message.size() != data_length) {
|
||||||
|
ESP_LOGVV(TAG, "parse_message(): payload has wrong size (%d)!", message.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// temperature, 2 bytes, 16-bit signed integer (LE), 0.1 °C
|
||||||
|
const int16_t temperature = uint16_t(data[7]) | (uint16_t(data[6]) << 8);
|
||||||
|
result.temperature = temperature / 10.0f;
|
||||||
|
|
||||||
|
// humidity, 1 byte, 8-bit unsigned integer, 1.0 %
|
||||||
|
result.humidity = data[8];
|
||||||
|
|
||||||
|
// battery, 1 byte, 8-bit unsigned integer, 1.0 %
|
||||||
|
result.battery_level = data[9];
|
||||||
|
|
||||||
|
// battery, 2 bytes, 16-bit unsigned integer, 0.001 V
|
||||||
|
const int16_t battery_voltage = uint16_t(data[11]) | (uint16_t(data[10]) << 8);
|
||||||
|
result.battery_voltage = battery_voltage / 1.0e3f;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ATCMiThermometer::report_results(const optional<ParseResult> &result, const std::string &address) {
|
||||||
|
if (!result.has_value()) {
|
||||||
|
ESP_LOGVV(TAG, "report_results(): no results available.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Got ATC MiThermometer (%s):", address.c_str());
|
||||||
|
|
||||||
|
if (result->temperature.has_value()) {
|
||||||
|
ESP_LOGD(TAG, " Temperature: %.1f °C", *result->temperature);
|
||||||
|
}
|
||||||
|
if (result->humidity.has_value()) {
|
||||||
|
ESP_LOGD(TAG, " Humidity: %.0f %%", *result->humidity);
|
||||||
|
}
|
||||||
|
if (result->battery_level.has_value()) {
|
||||||
|
ESP_LOGD(TAG, " Battery Level: %.0f %%", *result->battery_level);
|
||||||
|
}
|
||||||
|
if (result->battery_voltage.has_value()) {
|
||||||
|
ESP_LOGD(TAG, " Battery Voltage: %.3f V", *result->battery_voltage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace atc_mithermometer
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
||||||
48
esphome/components/atc_mithermometer/atc_mithermometer.h
Normal file
48
esphome/components/atc_mithermometer/atc_mithermometer.h
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace atc_mithermometer {
|
||||||
|
|
||||||
|
struct ParseResult {
|
||||||
|
optional<float> temperature;
|
||||||
|
optional<float> humidity;
|
||||||
|
optional<float> battery_level;
|
||||||
|
optional<float> battery_voltage;
|
||||||
|
bool is_duplicate;
|
||||||
|
int raw_offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ATCMiThermometer : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
|
||||||
|
public:
|
||||||
|
void set_address(uint64_t address) { address_ = address; };
|
||||||
|
|
||||||
|
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||||
|
void dump_config() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
|
||||||
|
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
|
||||||
|
void set_battery_level(sensor::Sensor *battery_level) { battery_level_ = battery_level; }
|
||||||
|
void set_battery_voltage(sensor::Sensor *battery_voltage) { battery_voltage_ = battery_voltage; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint64_t address_;
|
||||||
|
sensor::Sensor *temperature_{nullptr};
|
||||||
|
sensor::Sensor *humidity_{nullptr};
|
||||||
|
sensor::Sensor *battery_level_{nullptr};
|
||||||
|
sensor::Sensor *battery_voltage_{nullptr};
|
||||||
|
|
||||||
|
optional<ParseResult> parse_header(const esp32_ble_tracker::ServiceData &service_data);
|
||||||
|
bool parse_message(const std::vector<uint8_t> &message, ParseResult &result);
|
||||||
|
bool report_results(const optional<ParseResult> &result, const std::string &address);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace atc_mithermometer
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif
|
||||||
72
esphome/components/atc_mithermometer/sensor.py
Normal file
72
esphome/components/atc_mithermometer/sensor.py
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.components import sensor, esp32_ble_tracker
|
||||||
|
from esphome.const import (
|
||||||
|
CONF_BATTERY_LEVEL,
|
||||||
|
CONF_BATTERY_VOLTAGE,
|
||||||
|
CONF_MAC_ADDRESS,
|
||||||
|
CONF_HUMIDITY,
|
||||||
|
CONF_TEMPERATURE,
|
||||||
|
CONF_ID,
|
||||||
|
DEVICE_CLASS_BATTERY,
|
||||||
|
DEVICE_CLASS_HUMIDITY,
|
||||||
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
|
ICON_EMPTY,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
UNIT_VOLT,
|
||||||
|
)
|
||||||
|
|
||||||
|
CODEOWNERS = ["@ahpohl"]
|
||||||
|
|
||||||
|
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||||
|
|
||||||
|
atc_mithermometer_ns = cg.esphome_ns.namespace("atc_mithermometer")
|
||||||
|
ATCMiThermometer = atc_mithermometer_ns.class_(
|
||||||
|
"ATCMiThermometer", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
|
||||||
|
)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
cv.Schema(
|
||||||
|
{
|
||||||
|
cv.GenerateID(): cv.declare_id(ATCMiThermometer),
|
||||||
|
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||||
|
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||||
|
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
|
UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
|
||||||
|
UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
|
||||||
|
UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
yield cg.register_component(var, config)
|
||||||
|
yield esp32_ble_tracker.register_ble_device(var, config)
|
||||||
|
|
||||||
|
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||||
|
|
||||||
|
if CONF_TEMPERATURE in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||||
|
cg.add(var.set_temperature(sens))
|
||||||
|
if CONF_HUMIDITY in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
|
||||||
|
cg.add(var.set_humidity(sens))
|
||||||
|
if CONF_BATTERY_LEVEL in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
||||||
|
cg.add(var.set_battery_level(sens))
|
||||||
|
if CONF_BATTERY_VOLTAGE in config:
|
||||||
|
sens = yield sensor.new_sensor(config[CONF_BATTERY_VOLTAGE])
|
||||||
|
cg.add(var.set_battery_voltage(sens))
|
||||||
@@ -40,19 +40,45 @@ void ATM90E32Component::update() {
|
|||||||
if (this->phase_[2].power_sensor_ != nullptr) {
|
if (this->phase_[2].power_sensor_ != nullptr) {
|
||||||
this->phase_[2].power_sensor_->publish_state(this->get_active_power_c_());
|
this->phase_[2].power_sensor_->publish_state(this->get_active_power_c_());
|
||||||
}
|
}
|
||||||
|
if (this->phase_[0].reactive_power_sensor_ != nullptr) {
|
||||||
|
this->phase_[0].reactive_power_sensor_->publish_state(this->get_reactive_power_a_());
|
||||||
|
}
|
||||||
|
if (this->phase_[1].reactive_power_sensor_ != nullptr) {
|
||||||
|
this->phase_[1].reactive_power_sensor_->publish_state(this->get_reactive_power_b_());
|
||||||
|
}
|
||||||
|
if (this->phase_[2].reactive_power_sensor_ != nullptr) {
|
||||||
|
this->phase_[2].reactive_power_sensor_->publish_state(this->get_reactive_power_c_());
|
||||||
|
}
|
||||||
|
if (this->phase_[0].power_factor_sensor_ != nullptr) {
|
||||||
|
this->phase_[0].power_factor_sensor_->publish_state(this->get_power_factor_a_());
|
||||||
|
}
|
||||||
|
if (this->phase_[1].power_factor_sensor_ != nullptr) {
|
||||||
|
this->phase_[1].power_factor_sensor_->publish_state(this->get_power_factor_b_());
|
||||||
|
}
|
||||||
|
if (this->phase_[2].power_factor_sensor_ != nullptr) {
|
||||||
|
this->phase_[2].power_factor_sensor_->publish_state(this->get_power_factor_c_());
|
||||||
|
}
|
||||||
if (this->freq_sensor_ != nullptr) {
|
if (this->freq_sensor_ != nullptr) {
|
||||||
this->freq_sensor_->publish_state(this->get_frequency_());
|
this->freq_sensor_->publish_state(this->get_frequency_());
|
||||||
}
|
}
|
||||||
|
if (this->chip_temperature_sensor_ != nullptr) {
|
||||||
|
this->chip_temperature_sensor_->publish_state(this->get_chip_temperature_());
|
||||||
|
}
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ATM90E32Component::setup() {
|
void ATM90E32Component::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up ATM90E32Component...");
|
ESP_LOGCONFIG(TAG, "Setting up ATM90E32 Component...");
|
||||||
this->spi_setup();
|
this->spi_setup();
|
||||||
|
|
||||||
uint16_t mmode0 = 0x185;
|
uint16_t mmode0 = 0x87; // 3P4W 50Hz
|
||||||
if (line_freq_ == 60) {
|
if (line_freq_ == 60) {
|
||||||
mmode0 |= 1 << 12;
|
mmode0 |= 1 << 12; // sets 12th bit to 1, 60Hz
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current_phases_ == 2) {
|
||||||
|
mmode0 |= 1 << 8; // sets 8th bit to 1, 3P3W
|
||||||
|
mmode0 |= 0 << 1; // sets 1st bit to 0, phase b is not counted into the all-phase sum energy/power (P/Q/S)
|
||||||
}
|
}
|
||||||
|
|
||||||
this->write16_(ATM90E32_REGISTER_SOFTRESET, 0x789A); // Perform soft reset
|
this->write16_(ATM90E32_REGISTER_SOFTRESET, 0x789A); // Perform soft reset
|
||||||
@@ -63,13 +89,15 @@ void ATM90E32Component::setup() {
|
|||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this->write16_(ATM90E32_REGISTER_PLCONSTH, 0x0861); // PL Constant MSB (default) = 140625000
|
||||||
this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0x0A55); // ZX2, ZX1, ZX0 pin config
|
this->write16_(ATM90E32_REGISTER_PLCONSTL, 0xC468); // PL Constant LSB (default)
|
||||||
this->write16_(ATM90E32_REGISTER_MMODE0, mmode0); // Mode Config (frequency set in main program)
|
this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0xD654); // ZX2, ZX1, ZX0 pin config
|
||||||
this->write16_(ATM90E32_REGISTER_MMODE1, pga_gain_); // PGA Gain Configuration for Current Channels
|
this->write16_(ATM90E32_REGISTER_MMODE0, mmode0); // Mode Config (frequency set in main program)
|
||||||
this->write16_(ATM90E32_REGISTER_PSTARTTH, 0x0AFC); // Active Startup Power Threshold = 50%
|
this->write16_(ATM90E32_REGISTER_MMODE1, pga_gain_); // PGA Gain Configuration for Current Channels
|
||||||
this->write16_(ATM90E32_REGISTER_QSTARTTH, 0x0AEC); // Reactive Startup Power Threshold = 50%
|
this->write16_(ATM90E32_REGISTER_PSTARTTH, 0x1D4C); // All Active Startup Power Threshold - 0.02A/0.00032 = 7500
|
||||||
this->write16_(ATM90E32_REGISTER_PPHASETH, 0x00BC); // Active Phase Threshold = 10%
|
this->write16_(ATM90E32_REGISTER_QSTARTTH, 0x1D4C); // All Reactive Startup Power Threshold - 50%
|
||||||
|
this->write16_(ATM90E32_REGISTER_PPHASETH, 0x02EE); // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750
|
||||||
|
this->write16_(ATM90E32_REGISTER_QPHASETH, 0x02EE); // Each phase Reactive Phase Threshold - 10%
|
||||||
this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[0].volt_gain_); // A Voltage rms gain
|
this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[0].volt_gain_); // A Voltage rms gain
|
||||||
this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[0].ct_gain_); // A line current gain
|
this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[0].ct_gain_); // A line current gain
|
||||||
this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[1].volt_gain_); // B Voltage rms gain
|
this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[1].volt_gain_); // B Voltage rms gain
|
||||||
@@ -89,13 +117,20 @@ void ATM90E32Component::dump_config() {
|
|||||||
LOG_SENSOR(" ", "Voltage A", this->phase_[0].voltage_sensor_);
|
LOG_SENSOR(" ", "Voltage A", this->phase_[0].voltage_sensor_);
|
||||||
LOG_SENSOR(" ", "Current A", this->phase_[0].current_sensor_);
|
LOG_SENSOR(" ", "Current A", this->phase_[0].current_sensor_);
|
||||||
LOG_SENSOR(" ", "Power A", this->phase_[0].power_sensor_);
|
LOG_SENSOR(" ", "Power A", this->phase_[0].power_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Reactive Power A", this->phase_[0].reactive_power_sensor_);
|
||||||
|
LOG_SENSOR(" ", "PF A", this->phase_[0].power_factor_sensor_);
|
||||||
LOG_SENSOR(" ", "Voltage B", this->phase_[1].voltage_sensor_);
|
LOG_SENSOR(" ", "Voltage B", this->phase_[1].voltage_sensor_);
|
||||||
LOG_SENSOR(" ", "Current B", this->phase_[1].current_sensor_);
|
LOG_SENSOR(" ", "Current B", this->phase_[1].current_sensor_);
|
||||||
LOG_SENSOR(" ", "Power B", this->phase_[1].power_sensor_);
|
LOG_SENSOR(" ", "Power B", this->phase_[1].power_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Reactive Power B", this->phase_[1].reactive_power_sensor_);
|
||||||
|
LOG_SENSOR(" ", "PF B", this->phase_[1].power_factor_sensor_);
|
||||||
LOG_SENSOR(" ", "Voltage C", this->phase_[2].voltage_sensor_);
|
LOG_SENSOR(" ", "Voltage C", this->phase_[2].voltage_sensor_);
|
||||||
LOG_SENSOR(" ", "Current C", this->phase_[2].current_sensor_);
|
LOG_SENSOR(" ", "Current C", this->phase_[2].current_sensor_);
|
||||||
LOG_SENSOR(" ", "Power C", this->phase_[2].power_sensor_);
|
LOG_SENSOR(" ", "Power C", this->phase_[2].power_sensor_);
|
||||||
LOG_SENSOR(" ", "Frequency", this->freq_sensor_)
|
LOG_SENSOR(" ", "Reactive Power C", this->phase_[2].reactive_power_sensor_);
|
||||||
|
LOG_SENSOR(" ", "PF C", this->phase_[2].power_factor_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
|
||||||
|
LOG_SENSOR(" ", "Chip Temp", this->chip_temperature_sensor_);
|
||||||
}
|
}
|
||||||
float ATM90E32Component::get_setup_priority() const { return setup_priority::DATA; }
|
float ATM90E32Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
@@ -180,9 +215,37 @@ float ATM90E32Component::get_active_power_c_() {
|
|||||||
int val = this->read32_(ATM90E32_REGISTER_PMEANC, ATM90E32_REGISTER_PMEANCLSB);
|
int val = this->read32_(ATM90E32_REGISTER_PMEANC, ATM90E32_REGISTER_PMEANCLSB);
|
||||||
return val * 0.00032f;
|
return val * 0.00032f;
|
||||||
}
|
}
|
||||||
|
float ATM90E32Component::get_reactive_power_a_() {
|
||||||
|
int val = this->read32_(ATM90E32_REGISTER_QMEANA, ATM90E32_REGISTER_QMEANALSB);
|
||||||
|
return val * 0.00032f;
|
||||||
|
}
|
||||||
|
float ATM90E32Component::get_reactive_power_b_() {
|
||||||
|
int val = this->read32_(ATM90E32_REGISTER_QMEANB, ATM90E32_REGISTER_QMEANBLSB);
|
||||||
|
return val * 0.00032f;
|
||||||
|
}
|
||||||
|
float ATM90E32Component::get_reactive_power_c_() {
|
||||||
|
int val = this->read32_(ATM90E32_REGISTER_QMEANC, ATM90E32_REGISTER_QMEANCLSB);
|
||||||
|
return val * 0.00032f;
|
||||||
|
}
|
||||||
|
float ATM90E32Component::get_power_factor_a_() {
|
||||||
|
int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANA);
|
||||||
|
return (float) pf / 1000;
|
||||||
|
}
|
||||||
|
float ATM90E32Component::get_power_factor_b_() {
|
||||||
|
int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANB);
|
||||||
|
return (float) pf / 1000;
|
||||||
|
}
|
||||||
|
float ATM90E32Component::get_power_factor_c_() {
|
||||||
|
int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANC);
|
||||||
|
return (float) pf / 1000;
|
||||||
|
}
|
||||||
float ATM90E32Component::get_frequency_() {
|
float ATM90E32Component::get_frequency_() {
|
||||||
uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);
|
uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);
|
||||||
return (float) freq / 100;
|
return (float) freq / 100;
|
||||||
}
|
}
|
||||||
|
float ATM90E32Component::get_chip_temperature_() {
|
||||||
|
uint16_t ctemp = this->read16_(ATM90E32_REGISTER_TEMP);
|
||||||
|
return (float) ctemp;
|
||||||
|
}
|
||||||
} // namespace atm90e32
|
} // namespace atm90e32
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|||||||
@@ -19,11 +19,17 @@ class ATM90E32Component : public PollingComponent,
|
|||||||
void set_voltage_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].voltage_sensor_ = obj; }
|
void set_voltage_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].voltage_sensor_ = obj; }
|
||||||
void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; }
|
void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; }
|
||||||
void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; }
|
void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; }
|
||||||
|
void set_reactive_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].reactive_power_sensor_ = obj; }
|
||||||
|
void set_power_factor_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_factor_sensor_ = obj; }
|
||||||
void set_volt_gain(int phase, uint16_t gain) { this->phase_[phase].volt_gain_ = gain; }
|
void set_volt_gain(int phase, uint16_t gain) { this->phase_[phase].volt_gain_ = gain; }
|
||||||
void set_ct_gain(int phase, uint16_t gain) { this->phase_[phase].ct_gain_ = gain; }
|
void set_ct_gain(int phase, uint16_t gain) { this->phase_[phase].ct_gain_ = gain; }
|
||||||
|
|
||||||
void set_freq_sensor(sensor::Sensor *freq_sensor) { freq_sensor_ = freq_sensor; }
|
void set_freq_sensor(sensor::Sensor *freq_sensor) { freq_sensor_ = freq_sensor; }
|
||||||
|
void set_chip_temperature_sensor(sensor::Sensor *chip_temperature_sensor) {
|
||||||
|
chip_temperature_sensor_ = chip_temperature_sensor;
|
||||||
|
}
|
||||||
void set_line_freq(int freq) { line_freq_ = freq; }
|
void set_line_freq(int freq) { line_freq_ = freq; }
|
||||||
|
void set_current_phases(int phases) { current_phases_ = phases; }
|
||||||
void set_pga_gain(uint16_t gain) { pga_gain_ = gain; }
|
void set_pga_gain(uint16_t gain) { pga_gain_ = gain; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -40,18 +46,29 @@ class ATM90E32Component : public PollingComponent,
|
|||||||
float get_active_power_a_();
|
float get_active_power_a_();
|
||||||
float get_active_power_b_();
|
float get_active_power_b_();
|
||||||
float get_active_power_c_();
|
float get_active_power_c_();
|
||||||
|
float get_reactive_power_a_();
|
||||||
|
float get_reactive_power_b_();
|
||||||
|
float get_reactive_power_c_();
|
||||||
|
float get_power_factor_a_();
|
||||||
|
float get_power_factor_b_();
|
||||||
|
float get_power_factor_c_();
|
||||||
float get_frequency_();
|
float get_frequency_();
|
||||||
|
float get_chip_temperature_();
|
||||||
|
|
||||||
struct ATM90E32Phase {
|
struct ATM90E32Phase {
|
||||||
uint16_t volt_gain_{41820};
|
uint16_t volt_gain_{7305};
|
||||||
uint16_t ct_gain_{25498};
|
uint16_t ct_gain_{27961};
|
||||||
sensor::Sensor *voltage_sensor_{nullptr};
|
sensor::Sensor *voltage_sensor_{nullptr};
|
||||||
sensor::Sensor *current_sensor_{nullptr};
|
sensor::Sensor *current_sensor_{nullptr};
|
||||||
sensor::Sensor *power_sensor_{nullptr};
|
sensor::Sensor *power_sensor_{nullptr};
|
||||||
|
sensor::Sensor *reactive_power_sensor_{nullptr};
|
||||||
|
sensor::Sensor *power_factor_sensor_{nullptr};
|
||||||
} phase_[3];
|
} phase_[3];
|
||||||
sensor::Sensor *freq_sensor_{nullptr};
|
sensor::Sensor *freq_sensor_{nullptr};
|
||||||
|
sensor::Sensor *chip_temperature_sensor_{nullptr};
|
||||||
uint16_t pga_gain_{0x15};
|
uint16_t pga_gain_{0x15};
|
||||||
int line_freq_{60};
|
int line_freq_{60};
|
||||||
|
int current_phases_{3};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace atm90e32
|
} // namespace atm90e32
|
||||||
|
|||||||
@@ -234,12 +234,12 @@ static const uint16_t ATM90E32_REGISTER_IRMSBLSB = 0xEE; // Lower Word (B RMS
|
|||||||
static const uint16_t ATM90E32_REGISTER_IRMSCLSB = 0xEF; // Lower Word (C RMS Current)
|
static const uint16_t ATM90E32_REGISTER_IRMSCLSB = 0xEF; // Lower Word (C RMS Current)
|
||||||
|
|
||||||
/* THD, FREQUENCY, ANGLE & TEMPTEMP REGISTERS*/
|
/* THD, FREQUENCY, ANGLE & TEMPTEMP REGISTERS*/
|
||||||
static const uint16_t ATM90E32_REGISTER_THDNUA = 0xF1; // A Voltage THD+N
|
static const uint16_t ATM90E32_REGISTER_UPEAKA = 0xF1; // A Voltage Peak
|
||||||
static const uint16_t ATM90E32_REGISTER_THDNUB = 0xF2; // B Voltage THD+N
|
static const uint16_t ATM90E32_REGISTER_UPEAKB = 0xF2; // B Voltage Peak
|
||||||
static const uint16_t ATM90E32_REGISTER_THDNUC = 0xF3; // C Voltage THD+N
|
static const uint16_t ATM90E32_REGISTER_UPEAKC = 0xF3; // C Voltage Peak
|
||||||
static const uint16_t ATM90E32_REGISTER_THDNIA = 0xF5; // A Current THD+N
|
static const uint16_t ATM90E32_REGISTER_IPEAKA = 0xF5; // A Current Peak
|
||||||
static const uint16_t ATM90E32_REGISTER_THDNIB = 0xF6; // B Current THD+N
|
static const uint16_t ATM90E32_REGISTER_IPEAKB = 0xF6; // B Current Peak
|
||||||
static const uint16_t ATM90E32_REGISTER_THDNIC = 0xF7; // C Current THD+N
|
static const uint16_t ATM90E32_REGISTER_IPEAKC = 0xF7; // C Current Peak
|
||||||
static const uint16_t ATM90E32_REGISTER_FREQ = 0xF8; // Frequency
|
static const uint16_t ATM90E32_REGISTER_FREQ = 0xF8; // Frequency
|
||||||
static const uint16_t ATM90E32_REGISTER_PANGLEA = 0xF9; // A Mean Phase Angle
|
static const uint16_t ATM90E32_REGISTER_PANGLEA = 0xF9; // A Mean Phase Angle
|
||||||
static const uint16_t ATM90E32_REGISTER_PANGLEB = 0xFA; // B Mean Phase Angle
|
static const uint16_t ATM90E32_REGISTER_PANGLEB = 0xFA; // B Mean Phase Angle
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user