mirror of
https://github.com/esphome/esphome.git
synced 2025-11-04 09:01:49 +00:00
Compare commits
933 Commits
2024.11.3
...
2025.5.0b1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e20440c8e | ||
|
|
0630244195 | ||
|
|
183659f527 | ||
|
|
4ea63af796 | ||
|
|
0aa7911b1b | ||
|
|
032949bc77 | ||
|
|
6f8ee65919 | ||
|
|
c5654b4cb2 | ||
|
|
410b6353fe | ||
|
|
a36e1aab8e | ||
|
|
864ae7a56c | ||
|
|
2560d2b9d0 | ||
|
|
0cf9b05afd | ||
|
|
8b65d1673a | ||
|
|
5e164b107a | ||
|
|
a83959d738 | ||
|
|
0ccc5bf714 | ||
|
|
bc0956019b | ||
|
|
49f631d6c5 | ||
|
|
a9d5eb8470 | ||
|
|
7c0546c9f0 | ||
|
|
f4eb75e4e0 | ||
|
|
5b2c19bc86 | ||
|
|
185b84b8b2 | ||
|
|
facf94699e | ||
|
|
58104229e2 | ||
|
|
50c88b7aa7 | ||
|
|
81bae96109 | ||
|
|
a3ed090594 | ||
|
|
cff1820772 | ||
|
|
bdd2774544 | ||
|
|
38790793dd | ||
|
|
dcd786d21c | ||
|
|
71e88fe9b2 | ||
|
|
11dcaf7383 | ||
|
|
dded81d622 | ||
|
|
8324b3244c | ||
|
|
401c090edd | ||
|
|
8757957e17 | ||
|
|
e2c8a5b638 | ||
|
|
7bb899bfa1 | ||
|
|
3e2359ddff | ||
|
|
04147a7f27 | ||
|
|
cae3c030d2 | ||
|
|
d7c615ec43 | ||
|
|
1294e8ccd5 | ||
|
|
37a2cb07d1 | ||
|
|
2af3994f79 | ||
|
|
0c0fe81814 | ||
|
|
82c8614315 | ||
|
|
a85dc65038 | ||
|
|
290b8bdca0 | ||
|
|
a96ed0b70a | ||
|
|
cdc1a7c646 | ||
|
|
7f59aff157 | ||
|
|
cdce59f7f9 | ||
|
|
ff1c3cb52e | ||
|
|
bec9d91419 | ||
|
|
8399d894c1 | ||
|
|
e1732c4945 | ||
|
|
ca221d6cb2 | ||
|
|
8a90ce882a | ||
|
|
b3400a1308 | ||
|
|
23fb1bed61 | ||
|
|
2b3757dff8 | ||
|
|
1da8e99d27 | ||
|
|
e94e71ded8 | ||
|
|
00f20c1e55 | ||
|
|
45d019a7e4 | ||
|
|
8465017db9 | ||
|
|
782d748210 | ||
|
|
b01d85a974 | ||
|
|
797a4c61f2 | ||
|
|
8e29437900 | ||
|
|
9e64e71cdf | ||
|
|
ef2621aa54 | ||
|
|
882273cb56 | ||
|
|
ad2b74d9b4 | ||
|
|
26669bd1b6 | ||
|
|
54ead9a6b4 | ||
|
|
d60e1f02c0 | ||
|
|
213648564c | ||
|
|
8bdbde9732 | ||
|
|
e988762576 | ||
|
|
75496849eb | ||
|
|
39b119e9cc | ||
|
|
4d43caf6c1 | ||
|
|
ce5e1a6294 | ||
|
|
88be14aaa3 | ||
|
|
1ac56b06c5 | ||
|
|
8bbc509b0b | ||
|
|
6f35d0ac88 | ||
|
|
3b8a5db97c | ||
|
|
b8d83d0765 | ||
|
|
e7a2b395fd | ||
|
|
ad99d7fb45 | ||
|
|
0b032e5c19 | ||
|
|
c7523ace78 | ||
|
|
2a6827e1d2 | ||
|
|
125aff79ec | ||
|
|
a31d8ec309 | ||
|
|
3ed03edfec | ||
|
|
4dc6cbe2d7 | ||
|
|
524cd4b4e3 | ||
|
|
84ebbf0762 | ||
|
|
670ad7192c | ||
|
|
bc6ee20270 | ||
|
|
e869a3aec3 | ||
|
|
8aff6d2fdd | ||
|
|
8d33c6de36 | ||
|
|
f4b5f32cb4 | ||
|
|
2eb9582d0f | ||
|
|
db97440b04 | ||
|
|
ced7ae1d7a | ||
|
|
d6699fa3c0 | ||
|
|
836e5ffa43 | ||
|
|
c7f597bc75 | ||
|
|
e215fafebe | ||
|
|
da9c755f67 | ||
|
|
087ff865a7 | ||
|
|
8cd62c0308 | ||
|
|
f5241ff777 | ||
|
|
1aa2b79311 | ||
|
|
2dca2d5f85 | ||
|
|
f03b42ced5 | ||
|
|
0f8a0af244 | ||
|
|
62646f5f32 | ||
|
|
71f81d2f18 | ||
|
|
4ec8414050 | ||
|
|
807925fd38 | ||
|
|
b597565165 | ||
|
|
9a9b91b180 | ||
|
|
9dcf295df8 | ||
|
|
e8a3de2642 | ||
|
|
d2b4dba51f | ||
|
|
bf527b0331 | ||
|
|
cdc77506de | ||
|
|
6de6a0c82c | ||
|
|
20062576a3 | ||
|
|
07ba9fdf8f | ||
|
|
caa255f5d1 | ||
|
|
c0be2c14f3 | ||
|
|
9f629dcaa2 | ||
|
|
0fe6c65ba3 | ||
|
|
c756bb3b3e | ||
|
|
ecb91b0101 | ||
|
|
5f9a509bdc | ||
|
|
dc6dd9fe0d | ||
|
|
5baa034d0d | ||
|
|
b8ba26787e | ||
|
|
844569e96b | ||
|
|
43580739ac | ||
|
|
c9f7ab6948 | ||
|
|
7900660bb8 | ||
|
|
f096567ac7 | ||
|
|
5bfb5ccc34 | ||
|
|
1c60038111 | ||
|
|
b940db6549 | ||
|
|
aa6e172e14 | ||
|
|
86033b6612 | ||
|
|
59b4a1f554 | ||
|
|
b5bdfb3089 | ||
|
|
a31a5e74bd | ||
|
|
629481a526 | ||
|
|
3291a11824 | ||
|
|
d2ee2d3b23 | ||
|
|
253e3ec6f6 | ||
|
|
fdc4ec8a57 | ||
|
|
1da0dff8b1 | ||
|
|
38dae8489e | ||
|
|
22c0e1079e | ||
|
|
2d3f141140 | ||
|
|
e49252ca3d | ||
|
|
c9d1476ae0 | ||
|
|
ee646d7324 | ||
|
|
e557bca420 | ||
|
|
adcd6517db | ||
|
|
4c8f5275f9 | ||
|
|
526db0102c | ||
|
|
8a3fe9ce4c | ||
|
|
fb97ef33a8 | ||
|
|
805a6d85a5 | ||
|
|
8f9fbb15b8 | ||
|
|
3d24dea455 | ||
|
|
666d5374ea | ||
|
|
6792ff6d58 | ||
|
|
f29ccb9e75 | ||
|
|
911bd54765 | ||
|
|
89b1b12993 | ||
|
|
33d79e03d9 | ||
|
|
991f3d3a10 | ||
|
|
97823ddd16 | ||
|
|
6ff180152a | ||
|
|
dbb7cbed3e | ||
|
|
fbf00f0af4 | ||
|
|
82c6a40371 | ||
|
|
0242ac56df | ||
|
|
b82666002d | ||
|
|
e11883e431 | ||
|
|
ff5b9df607 | ||
|
|
e5b7e3039a | ||
|
|
31ed1eb6f0 | ||
|
|
0c3daab649 | ||
|
|
816371e3e9 | ||
|
|
3c7bb65a23 | ||
|
|
4a65fd76b3 | ||
|
|
2704db5eef | ||
|
|
f10bc73d31 | ||
|
|
55e099450c | ||
|
|
248dbd32a5 | ||
|
|
a7b676231a | ||
|
|
2fd5f9ac58 | ||
|
|
ca4838a5f4 | ||
|
|
1b72550236 | ||
|
|
71afd49e3e | ||
|
|
e5d718d1b1 | ||
|
|
61f33d6401 | ||
|
|
af9b568778 | ||
|
|
4a1eec567f | ||
|
|
5706b8476f | ||
|
|
8981a86793 | ||
|
|
a7fd6dc382 | ||
|
|
cb0a87c1f9 | ||
|
|
b913a0b178 | ||
|
|
214454ff51 | ||
|
|
3677ef71d1 | ||
|
|
7e133171e0 | ||
|
|
bc56d319b5 | ||
|
|
c423a6fb61 | ||
|
|
4034bf4f04 | ||
|
|
477abc05ae | ||
|
|
ff2b93a3e4 | ||
|
|
a52d6388a9 | ||
|
|
6259ca9ded | ||
|
|
f6ef50505b | ||
|
|
b4cf437761 | ||
|
|
1d9f5f1f1e | ||
|
|
e47489708e | ||
|
|
8e1bdcd211 | ||
|
|
3432d73584 | ||
|
|
2bb86641f8 | ||
|
|
6ca72a3a26 | ||
|
|
c215098cb7 | ||
|
|
566968b6be | ||
|
|
fe51ee6257 | ||
|
|
2c499b326a | ||
|
|
7c4ab7abfe | ||
|
|
3c242b7296 | ||
|
|
00dd5b8339 | ||
|
|
a007a8237a | ||
|
|
9b86cc37f0 | ||
|
|
2dfcf950fa | ||
|
|
5908b93e82 | ||
|
|
995db1f961 | ||
|
|
abcc656a6f | ||
|
|
4a9f323d92 | ||
|
|
34a4e70cc5 | ||
|
|
fb5d697c22 | ||
|
|
df4642208e | ||
|
|
264e234efc | ||
|
|
ca78dd44b5 | ||
|
|
7edf458898 | ||
|
|
d9873e24a7 | ||
|
|
645bd490ba | ||
|
|
27f6d00e7a | ||
|
|
f9d668eeca | ||
|
|
92d1557efd | ||
|
|
6b930595e2 | ||
|
|
4a1cbfc533 | ||
|
|
1f7a84cc8e | ||
|
|
8c5adfb33f | ||
|
|
399c9ba4be | ||
|
|
a866370a2e | ||
|
|
6240bfff97 | ||
|
|
1c72fd4674 | ||
|
|
2291a1dc39 | ||
|
|
8269e2c961 | ||
|
|
23dec912ad | ||
|
|
9637ef35bd | ||
|
|
23e5cdb30e | ||
|
|
f3b1b11eba | ||
|
|
5ceba618f6 | ||
|
|
99d5ca3266 | ||
|
|
219ba6152c | ||
|
|
ef0f969604 | ||
|
|
82adcd656f | ||
|
|
fe35eee8df | ||
|
|
864dd69038 | ||
|
|
79f198ebff | ||
|
|
4b0622aa23 | ||
|
|
4ecc72ed54 | ||
|
|
791740e554 | ||
|
|
6bccc7e389 | ||
|
|
655075e71b | ||
|
|
1df1e3cf48 | ||
|
|
05e52cae2b | ||
|
|
be60d9be9b | ||
|
|
e3eb3ee5d2 | ||
|
|
0812b3dd70 | ||
|
|
28a9f12595 | ||
|
|
36b75c3faa | ||
|
|
584c5bd5be | ||
|
|
bc372dbeb2 | ||
|
|
37a03de849 | ||
|
|
c8395cdf0a | ||
|
|
79c8a55459 | ||
|
|
36d6fe29f2 | ||
|
|
e1868ddecb | ||
|
|
6151644b96 | ||
|
|
a4914eb5b7 | ||
|
|
57a57f0d6a | ||
|
|
7e9f93a290 | ||
|
|
402ada07b5 | ||
|
|
9aa9abfc08 | ||
|
|
4c1f83614b | ||
|
|
d1763f9831 | ||
|
|
c49391427f | ||
|
|
c42343be3a | ||
|
|
ffc233d99d | ||
|
|
5ed0046bdd | ||
|
|
2e16dd788c | ||
|
|
58fe8b39b2 | ||
|
|
ccd55a8e84 | ||
|
|
4bb59ce1d1 | ||
|
|
bb988604c8 | ||
|
|
573088aadb | ||
|
|
031b1c8bd0 | ||
|
|
f95b2ba898 | ||
|
|
ea4b573f9a | ||
|
|
8fcbd57f2f | ||
|
|
f131186e6b | ||
|
|
20c7778524 | ||
|
|
2d8e86324b | ||
|
|
2dfd28ba3e | ||
|
|
63221d7a1f | ||
|
|
fb9a15f0af | ||
|
|
ce2e966005 | ||
|
|
e7d1072c85 | ||
|
|
bc999b50b3 | ||
|
|
6cfe3ac44d | ||
|
|
6787730aa4 | ||
|
|
48a7927a60 | ||
|
|
8ea4d8402f | ||
|
|
2c53408cfc | ||
|
|
33dce6e522 | ||
|
|
e213932b7c | ||
|
|
42fb0e2809 | ||
|
|
acce0bc45b | ||
|
|
f5885de6f1 | ||
|
|
17e3bb7324 | ||
|
|
d891521ce2 | ||
|
|
3320e4112b | ||
|
|
c0e4701e1d | ||
|
|
f3390ff7f5 | ||
|
|
dfbfb2a2bb | ||
|
|
874026ca8f | ||
|
|
4adda632bb | ||
|
|
6ea89644e7 | ||
|
|
64ff62c005 | ||
|
|
c4de9e87e4 | ||
|
|
918924d697 | ||
|
|
43805e6c56 | ||
|
|
38bbfaccc6 | ||
|
|
56b32aae11 | ||
|
|
e2c16b4baa | ||
|
|
10a9162f48 | ||
|
|
fbc884772c | ||
|
|
cbf68f1fd2 | ||
|
|
cf227d6f32 | ||
|
|
54e3153f27 | ||
|
|
c2e0a01106 | ||
|
|
d2c2439b97 | ||
|
|
a8d33dd26a | ||
|
|
da41a9204e | ||
|
|
f993bb08c7 | ||
|
|
afa481aeea | ||
|
|
dfb162e7a6 | ||
|
|
098921b88f | ||
|
|
5c6368b6b8 | ||
|
|
9bd7060f6b | ||
|
|
fb1d178abc | ||
|
|
90c96a0a0f | ||
|
|
c63a545750 | ||
|
|
89f82be4cd | ||
|
|
c336dd9436 | ||
|
|
fa25cebed5 | ||
|
|
7679c716b3 | ||
|
|
225e2585e8 | ||
|
|
1bdf0fdc57 | ||
|
|
4d95ff2ae0 | ||
|
|
f36d400058 | ||
|
|
c63cf9d151 | ||
|
|
0a02c1461e | ||
|
|
b3a69c6c05 | ||
|
|
dd113f2972 | ||
|
|
3c5a0091ee | ||
|
|
bf65b73569 | ||
|
|
a2b123a29a | ||
|
|
3575f52cdf | ||
|
|
c90185854e | ||
|
|
7d8c39d295 | ||
|
|
59d282489a | ||
|
|
f9a0a63290 | ||
|
|
00000e0ea8 | ||
|
|
bd853e6883 | ||
|
|
64d1d93fe0 | ||
|
|
266c2ef337 | ||
|
|
35199c9b96 | ||
|
|
0a29138045 | ||
|
|
52269305ec | ||
|
|
04dc0ed129 | ||
|
|
37fabd7c0a | ||
|
|
4aa7ad1e33 | ||
|
|
42e432754e | ||
|
|
2379f02008 | ||
|
|
d3145dd95b | ||
|
|
ab77dd691b | ||
|
|
b54c0fd60a | ||
|
|
75d1eeeffe | ||
|
|
10cea51739 | ||
|
|
83e090cc7e | ||
|
|
583f8f598a | ||
|
|
3e9556c6c2 | ||
|
|
83cba0d7bd | ||
|
|
1d6d0d66dc | ||
|
|
4ed78023b6 | ||
|
|
323209523b | ||
|
|
46a4f4eba9 | ||
|
|
53fda0e96d | ||
|
|
0350eafc1e | ||
|
|
7b8e68c73a | ||
|
|
db666e44a7 | ||
|
|
903d033e0f | ||
|
|
19d938ce48 | ||
|
|
653318479a | ||
|
|
2af5fd5210 | ||
|
|
29e388b231 | ||
|
|
d9e23fdb5c | ||
|
|
10eacaccba | ||
|
|
23687b2afd | ||
|
|
f11ad9ad5b | ||
|
|
74a25a7e76 | ||
|
|
23e04e18f8 | ||
|
|
aed5020a83 | ||
|
|
476f1b701b | ||
|
|
7c3a7b68d3 | ||
|
|
75dc0d3fb7 | ||
|
|
9bc4f68d87 | ||
|
|
1029202848 | ||
|
|
a831905bba | ||
|
|
faffd79545 | ||
|
|
7714147071 | ||
|
|
4da42dedc8 | ||
|
|
28f283d545 | ||
|
|
3048f303c5 | ||
|
|
63a7234767 | ||
|
|
c19621e238 | ||
|
|
bc96eb9d52 | ||
|
|
7375dde39c | ||
|
|
1b7111affb | ||
|
|
a511926aed | ||
|
|
6b36cb95c9 | ||
|
|
c13174c318 | ||
|
|
d5da341138 | ||
|
|
8fa157581e | ||
|
|
7114d6bdd1 | ||
|
|
eca0c21966 | ||
|
|
20c9c410af | ||
|
|
79af437f48 | ||
|
|
6e27003787 | ||
|
|
b7b2f3e61c | ||
|
|
9448737a92 | ||
|
|
6f2bf4ec4c | ||
|
|
54cea6c41e | ||
|
|
e754d0a58b | ||
|
|
5e44a035a3 | ||
|
|
c424fea524 | ||
|
|
6aba1dbd73 | ||
|
|
422fb8f1a5 | ||
|
|
2988bbb8ce | ||
|
|
59299bffc8 | ||
|
|
3410aee42e | ||
|
|
96682f5cbe | ||
|
|
bfa3254d6c | ||
|
|
990d1e3bb0 | ||
|
|
755b0bbfc7 | ||
|
|
c281351732 | ||
|
|
9f603a474f | ||
|
|
bf739506c3 | ||
|
|
3020083564 | ||
|
|
31e90e5544 | ||
|
|
7c9726859f | ||
|
|
7529fb10b4 | ||
|
|
ba79e2d7b1 | ||
|
|
7006bd24a5 | ||
|
|
58311c9a0d | ||
|
|
ae65f76dfe | ||
|
|
4d380214df | ||
|
|
c5ebf7683e | ||
|
|
a973adda67 | ||
|
|
d9b419eaf5 | ||
|
|
02bf33c548 | ||
|
|
b3db04a3d3 | ||
|
|
56034e3e79 | ||
|
|
abbd72e802 | ||
|
|
1257640e48 | ||
|
|
2bc9782ce7 | ||
|
|
6583e17810 | ||
|
|
64c8bcef2e | ||
|
|
f9da8dbfb8 | ||
|
|
74f7197543 | ||
|
|
c21b8bd417 | ||
|
|
1eb658cc5b | ||
|
|
8b251efb75 | ||
|
|
26d25464da | ||
|
|
78b55e22ee | ||
|
|
9ee5227fe0 | ||
|
|
e89603fe3b | ||
|
|
c0804d665d | ||
|
|
a67b85eabf | ||
|
|
a47e27885f | ||
|
|
2e66b33672 | ||
|
|
e21ef22706 | ||
|
|
93c2878c21 | ||
|
|
b3ad6a03e6 | ||
|
|
6e45a7c9af | ||
|
|
e17582544e | ||
|
|
daa7960031 | ||
|
|
6999cc0581 | ||
|
|
92ad6286aa | ||
|
|
1111aa167f | ||
|
|
143b0d3de4 | ||
|
|
788c41e6f4 | ||
|
|
46b6dcdfbf | ||
|
|
d05f641dd0 | ||
|
|
897873496a | ||
|
|
b0f6dd7d9c | ||
|
|
be5639faf1 | ||
|
|
e9a537784e | ||
|
|
35d303809e | ||
|
|
4740f12ce8 | ||
|
|
c8e7e275a4 | ||
|
|
077ee5b714 | ||
|
|
fa029e8fc7 | ||
|
|
ace953bd50 | ||
|
|
e190ef9e9b | ||
|
|
2868210d46 | ||
|
|
72f6461871 | ||
|
|
4a95468fd2 | ||
|
|
43319d4c8a | ||
|
|
3b7a7a2262 | ||
|
|
de2d21862b | ||
|
|
3d48eb26cd | ||
|
|
ab0d38fbda | ||
|
|
2b75e34719 | ||
|
|
0b6c416680 | ||
|
|
7bb2c3c496 | ||
|
|
88cfdc33d4 | ||
|
|
a2f1b90238 | ||
|
|
0401ee9428 | ||
|
|
14d7931bd6 | ||
|
|
6b3f3e1da6 | ||
|
|
33f9d66e81 | ||
|
|
46d19d82c2 | ||
|
|
c9e7562aff | ||
|
|
8b7aa4c110 | ||
|
|
b667ceaced | ||
|
|
abdf215d3a | ||
|
|
84836f15db | ||
|
|
8be9f02693 | ||
|
|
1ab1768b6a | ||
|
|
0d13e2040d | ||
|
|
fd24b1423c | ||
|
|
66c35a9432 | ||
|
|
45b8810ab8 | ||
|
|
ff7d232ee6 | ||
|
|
0cd3af2fcd | ||
|
|
8897a9866d | ||
|
|
dc8646cda6 | ||
|
|
353924257a | ||
|
|
da3d007d7b | ||
|
|
9e3359cdf2 | ||
|
|
7e626b04f2 | ||
|
|
4eb551864d | ||
|
|
e337bd7beb | ||
|
|
57739b8bb0 | ||
|
|
65ca000e6d | ||
|
|
bf6874b52e | ||
|
|
cecce0f3cb | ||
|
|
4d8f58db94 | ||
|
|
977333a73c | ||
|
|
1215d2ffeb | ||
|
|
9b56f9cc6d | ||
|
|
2e61229aed | ||
|
|
55203143df | ||
|
|
4e4566361f | ||
|
|
4273449003 | ||
|
|
f8fae676b1 | ||
|
|
211aee91e5 | ||
|
|
6e3527a88b | ||
|
|
06f9764f51 | ||
|
|
693d813c4b | ||
|
|
61ad2510fc | ||
|
|
53c15f6716 | ||
|
|
d4ac2d3c7e | ||
|
|
6f4e8f1fbf | ||
|
|
847cff06b3 | ||
|
|
bd34697715 | ||
|
|
6b55df36c7 | ||
|
|
b8f9eaecd8 | ||
|
|
c8bbc2e84c | ||
|
|
5108b9a8b7 | ||
|
|
8de5af4eec | ||
|
|
6e5e681055 | ||
|
|
f6cf99384b | ||
|
|
2b711e532b | ||
|
|
72c6f04a97 | ||
|
|
03e2701bd0 | ||
|
|
051fa3a49f | ||
|
|
7392397630 | ||
|
|
714e2d3e56 | ||
|
|
12d6c1bbca | ||
|
|
7727879f01 | ||
|
|
334e952a34 | ||
|
|
f9856135d0 | ||
|
|
ba3e5e8ecb | ||
|
|
67ccd0eb7f | ||
|
|
619ce93dec | ||
|
|
9957840dfc | ||
|
|
a23ce416ea | ||
|
|
2489f95107 | ||
|
|
7dab1a6082 | ||
|
|
f7f8bf4da4 | ||
|
|
dd18a219db | ||
|
|
dbf4c2c4da | ||
|
|
fc847c1de8 | ||
|
|
7fccc9ff86 | ||
|
|
dee1d84979 | ||
|
|
65b2d48a6f | ||
|
|
8aeb08f868 | ||
|
|
d4857a1727 | ||
|
|
0c032bc431 | ||
|
|
5a103543c4 | ||
|
|
01ab6d3ddc | ||
|
|
f2170c633a | ||
|
|
c2e52f4b11 | ||
|
|
4843bbd38a | ||
|
|
78ce8f014a | ||
|
|
b454f63b36 | ||
|
|
db644542ed | ||
|
|
716a8b87e1 | ||
|
|
0f4e274e52 | ||
|
|
576dbd6f0c | ||
|
|
c3d00b45f7 | ||
|
|
98b872abc7 | ||
|
|
75026be951 | ||
|
|
47a0ec467a | ||
|
|
9e40d4cf45 | ||
|
|
fecae2f740 | ||
|
|
5a01670803 | ||
|
|
c2423b18cb | ||
|
|
2363b3dfd6 | ||
|
|
628e47f670 | ||
|
|
7666581c54 | ||
|
|
03c36920ff | ||
|
|
abdd6b232f | ||
|
|
07be7ad7e2 | ||
|
|
820e3488d0 | ||
|
|
8c6c45e6c1 | ||
|
|
16bf56b0f9 | ||
|
|
49c01c26f1 | ||
|
|
b4a804cc77 | ||
|
|
df26ace0f1 | ||
|
|
e779a8bcb2 | ||
|
|
c458fd18df | ||
|
|
98817a5bbf | ||
|
|
c43d8460bd | ||
|
|
17b88f2e3e | ||
|
|
dac9768f6a | ||
|
|
e8d2ad4ce8 | ||
|
|
c3412df169 | ||
|
|
fc2b15e307 | ||
|
|
bdb1094b47 | ||
|
|
6262fb8fcf | ||
|
|
f319472066 | ||
|
|
b4a2b50ee0 | ||
|
|
30bb806f26 | ||
|
|
9874d17613 | ||
|
|
13909b7994 | ||
|
|
df50e57409 | ||
|
|
3fa67fad32 | ||
|
|
8fbd512952 | ||
|
|
528d3672b4 | ||
|
|
fef50afef8 | ||
|
|
aa1879082c | ||
|
|
d8c943972b | ||
|
|
f3ebb4eb39 | ||
|
|
f1c0570e3b | ||
|
|
aa87c60717 | ||
|
|
92a8ebe1f8 | ||
|
|
dd3ffc7f29 | ||
|
|
aac3841991 | ||
|
|
fb87a1c0bc | ||
|
|
4409471cd1 | ||
|
|
739edce268 | ||
|
|
f25f3334d1 | ||
|
|
571935fb3b | ||
|
|
7c39422692 | ||
|
|
731fb1d172 | ||
|
|
40bee2a854 | ||
|
|
d69926485c | ||
|
|
fe80750743 | ||
|
|
109d737d5d | ||
|
|
bd17ee8e33 | ||
|
|
f1712cffa8 | ||
|
|
0df6a913b3 | ||
|
|
8a98b69a57 | ||
|
|
4530e4d60f | ||
|
|
4d7c6b28e1 | ||
|
|
de603c7565 | ||
|
|
a498fb5dcf | ||
|
|
78543e1e15 | ||
|
|
5e72b7196b | ||
|
|
a0615a92f0 | ||
|
|
dc5b408748 | ||
|
|
387bde665e | ||
|
|
45beea68eb | ||
|
|
c457d8835e | ||
|
|
4b51ba3fa4 | ||
|
|
499953e3f4 | ||
|
|
69f1a81e1d | ||
|
|
37fcccbb1c | ||
|
|
f3cb179f54 | ||
|
|
ba2edbc189 | ||
|
|
f33b4a714e | ||
|
|
85d863601b | ||
|
|
fe0700166a | ||
|
|
d28cf011d1 | ||
|
|
434879ea04 | ||
|
|
7da07303c9 | ||
|
|
b33b4481ea | ||
|
|
ac631711ab | ||
|
|
265b6ec445 | ||
|
|
61499dbdd8 | ||
|
|
0aaef9293b | ||
|
|
0f0b829bc6 | ||
|
|
a9d883b65a | ||
|
|
d330e73c1e | ||
|
|
7554e954fe | ||
|
|
752af94a75 | ||
|
|
561d92d402 | ||
|
|
1a69236473 | ||
|
|
c86ea99145 | ||
|
|
7661609049 | ||
|
|
c38826824f | ||
|
|
e890486043 | ||
|
|
ccc9fd4a3f | ||
|
|
54fbf5184e | ||
|
|
759df7ae6c | ||
|
|
3d56397e58 | ||
|
|
9f6c64afa6 | ||
|
|
663e18310d | ||
|
|
1a89aa8fbf | ||
|
|
e04743e381 | ||
|
|
a6957b9d3b | ||
|
|
9816c27031 | ||
|
|
ea06740b46 | ||
|
|
9a5ec1b9e6 | ||
|
|
6dcbd1a8ae | ||
|
|
63b0930ae8 | ||
|
|
5382bd2a97 | ||
|
|
de1fbd390b | ||
|
|
af23357dca | ||
|
|
0fbe6c0d8b | ||
|
|
4e1ff31342 | ||
|
|
df4224e779 | ||
|
|
5877c57a35 | ||
|
|
7f2ca800c1 | ||
|
|
ce7ff15c8a | ||
|
|
88742e0399 | ||
|
|
c187cb547c | ||
|
|
42bc960a36 | ||
|
|
ba63d266d8 | ||
|
|
90baba4db7 | ||
|
|
1656ced351 | ||
|
|
1dfd15e607 | ||
|
|
5dcaf1241f | ||
|
|
7aa54b6879 | ||
|
|
444e162c92 | ||
|
|
bb27eaaf1e | ||
|
|
517f659da8 | ||
|
|
5a92e24662 | ||
|
|
437b236a4d | ||
|
|
14eac3dbce | ||
|
|
132a096ae7 | ||
|
|
440080a753 | ||
|
|
f15e3cfb9b | ||
|
|
9d000e9abf | ||
|
|
97fd7493b5 | ||
|
|
749a5e3348 | ||
|
|
b0e3ac01e8 | ||
|
|
58123845ff | ||
|
|
bfd75d736c | ||
|
|
4e3195b474 | ||
|
|
d3a71a1d45 | ||
|
|
555bdac604 | ||
|
|
acc8d24a32 | ||
|
|
f3cc1e541a | ||
|
|
ece72c6b18 | ||
|
|
4e839d42d0 | ||
|
|
d429aa8bb8 | ||
|
|
472402745d | ||
|
|
016fac2496 | ||
|
|
79478cdb8a | ||
|
|
dbed74b50d | ||
|
|
d00ec7e544 | ||
|
|
a37ff2dbd9 | ||
|
|
00ddb0a427 | ||
|
|
c95887a14a | ||
|
|
dc5942a59b | ||
|
|
584dbf2668 | ||
|
|
9c8976be13 | ||
|
|
e08a9cc3a3 | ||
|
|
b79a3d6727 | ||
|
|
fb96e3588d | ||
|
|
edd847ea40 | ||
|
|
83d6834e27 | ||
|
|
8f69d07061 | ||
|
|
30477c764d | ||
|
|
217a80a178 | ||
|
|
5486b40aab | ||
|
|
beb8ab50e2 | ||
|
|
7cdf5b55ef | ||
|
|
c9b0490305 | ||
|
|
d305870284 | ||
|
|
ff5004d7db | ||
|
|
7aa3a1a1cc | ||
|
|
e124151e5c | ||
|
|
e229ed0da3 | ||
|
|
12cdeca48a | ||
|
|
a825ef59d4 | ||
|
|
65a5216d17 | ||
|
|
567256bd62 | ||
|
|
4da57c35d0 | ||
|
|
f2e8e655ba | ||
|
|
8439232b11 | ||
|
|
e6c730ab10 | ||
|
|
e49df765d2 | ||
|
|
a3ef2ed7fd | ||
|
|
3a8b41daa3 | ||
|
|
921be1a17c | ||
|
|
e3d673d16c | ||
|
|
39f3f795e2 | ||
|
|
53691d28a8 | ||
|
|
3730b0310b | ||
|
|
2b9013699d | ||
|
|
be78827274 | ||
|
|
cd1ee96606 | ||
|
|
2fa8d907b3 | ||
|
|
4c383906c4 | ||
|
|
bdc6302ea1 | ||
|
|
31c13e4c16 | ||
|
|
6b59f55a50 | ||
|
|
e6bd2238ce | ||
|
|
2d4688a206 | ||
|
|
536bcab5de | ||
|
|
1c2d2bce5a | ||
|
|
2eac8b6c46 | ||
|
|
6e50e2aa65 | ||
|
|
841d278224 | ||
|
|
11076e4614 | ||
|
|
72df3d1606 | ||
|
|
ae6736311a | ||
|
|
c0dcecc465 | ||
|
|
d9d368d38e | ||
|
|
a70cee1dc1 | ||
|
|
f4766ab74f | ||
|
|
4fbf41472a | ||
|
|
6ee02c47c2 | ||
|
|
140d77061b | ||
|
|
d6f4f05090 | ||
|
|
bdb91112ea | ||
|
|
b027b6a711 | ||
|
|
89ecfc2004 | ||
|
|
cf835d1580 | ||
|
|
17a09cd221 | ||
|
|
1bd2d41ffd | ||
|
|
aa6cea6f7e | ||
|
|
ebf895990b | ||
|
|
46a435f5f2 | ||
|
|
6c548a1596 | ||
|
|
7f75f2135d | ||
|
|
c49f7293fe | ||
|
|
71496574e9 | ||
|
|
b95b4a0694 | ||
|
|
59653ec785 | ||
|
|
e02f3cdac7 | ||
|
|
d4d630823c | ||
|
|
9fc1377b44 | ||
|
|
e3e3d92347 | ||
|
|
13077095c2 | ||
|
|
4001d82ca2 | ||
|
|
4936ca1700 | ||
|
|
2ecd5cff07 | ||
|
|
dea297c8d7 | ||
|
|
ef7c5c6055 | ||
|
|
122ff731ef | ||
|
|
3232866dc3 | ||
|
|
ccf2854b61 | ||
|
|
03ae6b2c1b | ||
|
|
6bcbbcce02 | ||
|
|
fbb9967117 | ||
|
|
6d4f787f67 | ||
|
|
5e27a8df1f | ||
|
|
846b091aac | ||
|
|
372d68a177 | ||
|
|
4fc19902ab | ||
|
|
c857f98557 | ||
|
|
cf63d627fe | ||
|
|
49e9c43339 | ||
|
|
f1dc9537ff | ||
|
|
6e41c22e9d | ||
|
|
e81191ebd2 | ||
|
|
b29c119408 | ||
|
|
e819185de1 | ||
|
|
0b51ec2c88 | ||
|
|
5e62c489b0 | ||
|
|
d015088855 | ||
|
|
39c889e662 | ||
|
|
c7c8711c9c | ||
|
|
0a92405f2d | ||
|
|
a2cab960a9 | ||
|
|
1f7f03f563 |
36
.clang-tidy
36
.clang-tidy
@@ -7,28 +7,39 @@ Checks: >-
|
|||||||
-boost-*,
|
-boost-*,
|
||||||
-bugprone-easily-swappable-parameters,
|
-bugprone-easily-swappable-parameters,
|
||||||
-bugprone-implicit-widening-of-multiplication-result,
|
-bugprone-implicit-widening-of-multiplication-result,
|
||||||
|
-bugprone-multi-level-implicit-pointer-conversion,
|
||||||
-bugprone-narrowing-conversions,
|
-bugprone-narrowing-conversions,
|
||||||
-bugprone-signed-char-misuse,
|
-bugprone-signed-char-misuse,
|
||||||
|
-bugprone-switch-missing-default-case,
|
||||||
-cert-dcl50-cpp,
|
-cert-dcl50-cpp,
|
||||||
-cert-err33-c,
|
-cert-err33-c,
|
||||||
-cert-err58-cpp,
|
-cert-err58-cpp,
|
||||||
-cert-oop57-cpp,
|
-cert-oop57-cpp,
|
||||||
-cert-str34-c,
|
-cert-str34-c,
|
||||||
|
-clang-analyzer-optin.core.EnumCastOutOfRange,
|
||||||
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
||||||
-clang-analyzer-osx.*,
|
-clang-analyzer-osx.*,
|
||||||
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
||||||
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
||||||
|
-clang-diagnostic-deprecated-declarations,
|
||||||
-clang-diagnostic-ignored-optimization-argument,
|
-clang-diagnostic-ignored-optimization-argument,
|
||||||
|
-clang-diagnostic-missing-field-initializers,
|
||||||
-clang-diagnostic-shadow-field,
|
-clang-diagnostic-shadow-field,
|
||||||
-clang-diagnostic-unused-const-variable,
|
-clang-diagnostic-unused-const-variable,
|
||||||
-clang-diagnostic-unused-parameter,
|
-clang-diagnostic-unused-parameter,
|
||||||
|
-clang-diagnostic-vla-cxx-extension,
|
||||||
-concurrency-*,
|
-concurrency-*,
|
||||||
-cppcoreguidelines-avoid-c-arrays,
|
-cppcoreguidelines-avoid-c-arrays,
|
||||||
|
-cppcoreguidelines-avoid-const-or-ref-data-members,
|
||||||
|
-cppcoreguidelines-avoid-do-while,
|
||||||
-cppcoreguidelines-avoid-magic-numbers,
|
-cppcoreguidelines-avoid-magic-numbers,
|
||||||
-cppcoreguidelines-init-variables,
|
-cppcoreguidelines-init-variables,
|
||||||
|
-cppcoreguidelines-macro-to-enum,
|
||||||
-cppcoreguidelines-macro-usage,
|
-cppcoreguidelines-macro-usage,
|
||||||
|
-cppcoreguidelines-missing-std-forward,
|
||||||
-cppcoreguidelines-narrowing-conversions,
|
-cppcoreguidelines-narrowing-conversions,
|
||||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||||
|
-cppcoreguidelines-owning-memory,
|
||||||
-cppcoreguidelines-prefer-member-initializer,
|
-cppcoreguidelines-prefer-member-initializer,
|
||||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||||
@@ -40,7 +51,9 @@ Checks: >-
|
|||||||
-cppcoreguidelines-pro-type-static-cast-downcast,
|
-cppcoreguidelines-pro-type-static-cast-downcast,
|
||||||
-cppcoreguidelines-pro-type-union-access,
|
-cppcoreguidelines-pro-type-union-access,
|
||||||
-cppcoreguidelines-pro-type-vararg,
|
-cppcoreguidelines-pro-type-vararg,
|
||||||
|
-cppcoreguidelines-rvalue-reference-param-not-moved,
|
||||||
-cppcoreguidelines-special-member-functions,
|
-cppcoreguidelines-special-member-functions,
|
||||||
|
-cppcoreguidelines-use-default-member-init,
|
||||||
-cppcoreguidelines-virtual-class-destructor,
|
-cppcoreguidelines-virtual-class-destructor,
|
||||||
-fuchsia-multiple-inheritance,
|
-fuchsia-multiple-inheritance,
|
||||||
-fuchsia-overloaded-operator,
|
-fuchsia-overloaded-operator,
|
||||||
@@ -60,20 +73,32 @@ Checks: >-
|
|||||||
-llvm-include-order,
|
-llvm-include-order,
|
||||||
-llvm-qualified-auto,
|
-llvm-qualified-auto,
|
||||||
-llvmlibc-*,
|
-llvmlibc-*,
|
||||||
-misc-non-private-member-variables-in-classes,
|
-misc-const-correctness,
|
||||||
|
-misc-include-cleaner,
|
||||||
-misc-no-recursion,
|
-misc-no-recursion,
|
||||||
|
-misc-non-private-member-variables-in-classes,
|
||||||
-misc-unused-parameters,
|
-misc-unused-parameters,
|
||||||
-modernize-avoid-c-arrays,
|
-misc-use-anonymous-namespace,
|
||||||
-modernize-avoid-bind,
|
-modernize-avoid-bind,
|
||||||
|
-modernize-avoid-c-arrays,
|
||||||
-modernize-concat-nested-namespaces,
|
-modernize-concat-nested-namespaces,
|
||||||
|
-modernize-macro-to-enum,
|
||||||
-modernize-return-braced-init-list,
|
-modernize-return-braced-init-list,
|
||||||
|
-modernize-type-traits,
|
||||||
-modernize-use-auto,
|
-modernize-use-auto,
|
||||||
|
-modernize-use-constraints,
|
||||||
-modernize-use-default-member-init,
|
-modernize-use-default-member-init,
|
||||||
-modernize-use-equals-default,
|
-modernize-use-equals-default,
|
||||||
-modernize-use-trailing-return-type,
|
|
||||||
-modernize-use-nodiscard,
|
-modernize-use-nodiscard,
|
||||||
|
-modernize-use-nullptr,
|
||||||
|
-modernize-use-nodiscard,
|
||||||
|
-modernize-use-nullptr,
|
||||||
|
-modernize-use-trailing-return-type,
|
||||||
-mpi-*,
|
-mpi-*,
|
||||||
-objc-*,
|
-objc-*,
|
||||||
|
-performance-enum-size,
|
||||||
|
-readability-avoid-nested-conditional-operator,
|
||||||
|
-readability-container-contains,
|
||||||
-readability-container-data-pointer,
|
-readability-container-data-pointer,
|
||||||
-readability-convert-member-functions-to-static,
|
-readability-convert-member-functions-to-static,
|
||||||
-readability-else-after-return,
|
-readability-else-after-return,
|
||||||
@@ -82,11 +107,14 @@ Checks: >-
|
|||||||
-readability-isolate-declaration,
|
-readability-isolate-declaration,
|
||||||
-readability-magic-numbers,
|
-readability-magic-numbers,
|
||||||
-readability-make-member-function-const,
|
-readability-make-member-function-const,
|
||||||
|
-readability-named-parameter,
|
||||||
|
-readability-redundant-casting,
|
||||||
|
-readability-redundant-inline-specifier,
|
||||||
|
-readability-redundant-member-init,
|
||||||
-readability-redundant-string-init,
|
-readability-redundant-string-init,
|
||||||
-readability-uppercase-literal-suffix,
|
-readability-uppercase-literal-suffix,
|
||||||
-readability-use-anyofallof,
|
-readability-use-anyofallof,
|
||||||
WarningsAsErrors: '*'
|
WarningsAsErrors: '*'
|
||||||
AnalyzeTemporaryDtors: false
|
|
||||||
FormatStyle: google
|
FormatStyle: google
|
||||||
CheckOptions:
|
CheckOptions:
|
||||||
- key: google-readability-function-size.StatementThreshold
|
- key: google-readability-function-size.StatementThreshold
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
"ms-python.python",
|
"ms-python.python",
|
||||||
"ms-python.pylint",
|
"ms-python.pylint",
|
||||||
"ms-python.flake8",
|
"ms-python.flake8",
|
||||||
"ms-python.black-formatter",
|
"charliermarsh.ruff",
|
||||||
"visualstudioexptteam.vscodeintellicode",
|
"visualstudioexptteam.vscodeintellicode",
|
||||||
// yaml
|
// yaml
|
||||||
"redhat.vscode-yaml",
|
"redhat.vscode-yaml",
|
||||||
@@ -49,14 +49,11 @@
|
|||||||
"flake8.args": [
|
"flake8.args": [
|
||||||
"--config=${workspaceFolder}/.flake8"
|
"--config=${workspaceFolder}/.flake8"
|
||||||
],
|
],
|
||||||
"black-formatter.args": [
|
"ruff.configuration": "${workspaceFolder}/pyproject.toml",
|
||||||
"--config",
|
|
||||||
"${workspaceFolder}/pyproject.toml"
|
|
||||||
],
|
|
||||||
"[python]": {
|
"[python]": {
|
||||||
// VS will say "Value is not accepted" before building the devcontainer, but the warning
|
// VS will say "Value is not accepted" before building the devcontainer, but the warning
|
||||||
// should go away after build is completed.
|
// should go away after build is completed.
|
||||||
"editor.defaultFormatter": "ms-python.black-formatter"
|
"editor.defaultFormatter": "charliermarsh.ruff"
|
||||||
},
|
},
|
||||||
"editor.formatOnPaste": false,
|
"editor.formatOnPaste": false,
|
||||||
"editor.formatOnSave": true,
|
"editor.formatOnSave": true,
|
||||||
|
|||||||
@@ -114,4 +114,5 @@ config/
|
|||||||
examples/
|
examples/
|
||||||
Dockerfile
|
Dockerfile
|
||||||
.git/
|
.git/
|
||||||
tests/build/
|
tests/
|
||||||
|
.*
|
||||||
|
|||||||
4
.github/FUNDING.yml
vendored
4
.github/FUNDING.yml
vendored
@@ -1,4 +0,0 @@
|
|||||||
---
|
|
||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
custom: https://www.nabucasa.com
|
|
||||||
33
.github/actions/build-image/action.yaml
vendored
33
.github/actions/build-image/action.yaml
vendored
@@ -1,15 +1,11 @@
|
|||||||
name: Build Image
|
name: Build Image
|
||||||
inputs:
|
inputs:
|
||||||
platform:
|
|
||||||
description: "Platform to build for"
|
|
||||||
required: true
|
|
||||||
example: "linux/amd64"
|
|
||||||
target:
|
target:
|
||||||
description: "Target to build"
|
description: "Target to build"
|
||||||
required: true
|
required: true
|
||||||
example: "docker"
|
example: "docker"
|
||||||
baseimg:
|
build_type:
|
||||||
description: "Base image type"
|
description: "Build type"
|
||||||
required: true
|
required: true
|
||||||
example: "docker"
|
example: "docker"
|
||||||
suffix:
|
suffix:
|
||||||
@@ -19,6 +15,11 @@ inputs:
|
|||||||
description: "Version to build"
|
description: "Version to build"
|
||||||
required: true
|
required: true
|
||||||
example: "2023.12.0"
|
example: "2023.12.0"
|
||||||
|
base_os:
|
||||||
|
description: "Base OS to use"
|
||||||
|
required: false
|
||||||
|
default: "debian"
|
||||||
|
example: "debian"
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
@@ -46,52 +47,52 @@ runs:
|
|||||||
|
|
||||||
- name: Build and push to ghcr by digest
|
- name: Build and push to ghcr by digest
|
||||||
id: build-ghcr
|
id: build-ghcr
|
||||||
uses: docker/build-push-action@v6.9.0
|
uses: docker/build-push-action@v6.16.0
|
||||||
env:
|
env:
|
||||||
DOCKER_BUILD_SUMMARY: false
|
DOCKER_BUILD_SUMMARY: false
|
||||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./docker/Dockerfile
|
file: ./docker/Dockerfile
|
||||||
platforms: ${{ inputs.platform }}
|
|
||||||
target: ${{ inputs.target }}
|
target: ${{ inputs.target }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: ${{ steps.cache-to.outputs.value }}
|
cache-to: ${{ steps.cache-to.outputs.value }}
|
||||||
build-args: |
|
build-args: |
|
||||||
BASEIMGTYPE=${{ inputs.baseimg }}
|
BUILD_TYPE=${{ inputs.build_type }}
|
||||||
BUILD_VERSION=${{ inputs.version }}
|
BUILD_VERSION=${{ inputs.version }}
|
||||||
|
BUILD_OS=${{ inputs.base_os }}
|
||||||
outputs: |
|
outputs: |
|
||||||
type=image,name=ghcr.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true
|
type=image,name=ghcr.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true
|
||||||
|
|
||||||
- name: Export ghcr digests
|
- name: Export ghcr digests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
mkdir -p /tmp/digests/${{ inputs.target }}/ghcr
|
mkdir -p /tmp/digests/${{ inputs.build_type }}/ghcr
|
||||||
digest="${{ steps.build-ghcr.outputs.digest }}"
|
digest="${{ steps.build-ghcr.outputs.digest }}"
|
||||||
touch "/tmp/digests/${{ inputs.target }}/ghcr/${digest#sha256:}"
|
touch "/tmp/digests/${{ inputs.build_type }}/ghcr/${digest#sha256:}"
|
||||||
|
|
||||||
- name: Build and push to dockerhub by digest
|
- name: Build and push to dockerhub by digest
|
||||||
id: build-dockerhub
|
id: build-dockerhub
|
||||||
uses: docker/build-push-action@v6.9.0
|
uses: docker/build-push-action@v6.16.0
|
||||||
env:
|
env:
|
||||||
DOCKER_BUILD_SUMMARY: false
|
DOCKER_BUILD_SUMMARY: false
|
||||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./docker/Dockerfile
|
file: ./docker/Dockerfile
|
||||||
platforms: ${{ inputs.platform }}
|
|
||||||
target: ${{ inputs.target }}
|
target: ${{ inputs.target }}
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: ${{ steps.cache-to.outputs.value }}
|
cache-to: ${{ steps.cache-to.outputs.value }}
|
||||||
build-args: |
|
build-args: |
|
||||||
BASEIMGTYPE=${{ inputs.baseimg }}
|
BUILD_TYPE=${{ inputs.build_type }}
|
||||||
BUILD_VERSION=${{ inputs.version }}
|
BUILD_VERSION=${{ inputs.version }}
|
||||||
|
BUILD_OS=${{ inputs.base_os }}
|
||||||
outputs: |
|
outputs: |
|
||||||
type=image,name=docker.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true
|
type=image,name=docker.io/${{ steps.tags.outputs.image_name }},push-by-digest=true,name-canonical=true,push=true
|
||||||
|
|
||||||
- name: Export dockerhub digests
|
- name: Export dockerhub digests
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
mkdir -p /tmp/digests/${{ inputs.target }}/dockerhub
|
mkdir -p /tmp/digests/${{ inputs.build_type }}/dockerhub
|
||||||
digest="${{ steps.build-dockerhub.outputs.digest }}"
|
digest="${{ steps.build-dockerhub.outputs.digest }}"
|
||||||
touch "/tmp/digests/${{ inputs.target }}/dockerhub/${digest#sha256:}"
|
touch "/tmp/digests/${{ inputs.build_type }}/dockerhub/${digest#sha256:}"
|
||||||
|
|||||||
8
.github/actions/restore-python/action.yml
vendored
8
.github/actions/restore-python/action.yml
vendored
@@ -17,12 +17,12 @@ runs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Set up Python ${{ inputs.python-version }}
|
- name: Set up Python ${{ inputs.python-version }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ inputs.python-version }}
|
python-version: ${{ inputs.python-version }}
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
id: cache-venv
|
id: cache-venv
|
||||||
uses: actions/cache/restore@v4.1.2
|
uses: actions/cache/restore@v4.2.3
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
@@ -34,7 +34,7 @@ runs:
|
|||||||
python -m venv venv
|
python -m venv venv
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
python --version
|
python --version
|
||||||
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
pip install -r requirements.txt -r requirements_test.txt
|
||||||
pip install -e .
|
pip install -e .
|
||||||
- name: Create Python virtual environment
|
- name: Create Python virtual environment
|
||||||
if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os == 'Windows'
|
if: steps.cache-venv.outputs.cache-hit != 'true' && runner.os == 'Windows'
|
||||||
@@ -43,5 +43,5 @@ runs:
|
|||||||
python -m venv venv
|
python -m venv venv
|
||||||
./venv/Scripts/activate
|
./venv/Scripts/activate
|
||||||
python --version
|
python --version
|
||||||
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
pip install -r requirements.txt -r requirements_test.txt
|
||||||
pip install -e .
|
pip install -e .
|
||||||
|
|||||||
1
.github/dependabot.yml
vendored
1
.github/dependabot.yml
vendored
@@ -17,7 +17,6 @@ updates:
|
|||||||
docker-actions:
|
docker-actions:
|
||||||
applies-to: version-updates
|
applies-to: version-updates
|
||||||
patterns:
|
patterns:
|
||||||
- "docker/setup-qemu-action"
|
|
||||||
- "docker/login-action"
|
- "docker/login-action"
|
||||||
- "docker/setup-buildx-action"
|
- "docker/setup-buildx-action"
|
||||||
- package-ecosystem: github-actions
|
- package-ecosystem: github-actions
|
||||||
|
|||||||
13
.github/workflows/ci-api-proto.yml
vendored
13
.github/workflows/ci-api-proto.yml
vendored
@@ -23,7 +23,7 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.1.7
|
uses: actions/checkout@v4.1.7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
@@ -57,6 +57,17 @@ jobs:
|
|||||||
event: 'REQUEST_CHANGES',
|
event: 'REQUEST_CHANGES',
|
||||||
body: 'You have altered the generated proto files but they do not match what is expected.\nPlease run "script/api_protobuf/api_protobuf.py" and commit the changes.'
|
body: 'You have altered the generated proto files but they do not match what is expected.\nPlease run "script/api_protobuf/api_protobuf.py" and commit the changes.'
|
||||||
})
|
})
|
||||||
|
- if: failure()
|
||||||
|
name: Show changes
|
||||||
|
run: git diff
|
||||||
|
- if: failure()
|
||||||
|
name: Archive artifacts
|
||||||
|
uses: actions/upload-artifact@v4.6.2
|
||||||
|
with:
|
||||||
|
name: generated-proto-files
|
||||||
|
path: |
|
||||||
|
esphome/components/api/api_pb2.*
|
||||||
|
esphome/components/api/api_pb2_service.*
|
||||||
- if: success()
|
- if: success()
|
||||||
name: Dismiss review
|
name: Dismiss review
|
||||||
uses: actions/github-script@v7.0.1
|
uses: actions/github-script@v7.0.1
|
||||||
|
|||||||
17
.github/workflows/ci-docker.yml
vendored
17
.github/workflows/ci-docker.yml
vendored
@@ -33,22 +33,23 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
check-docker:
|
check-docker:
|
||||||
name: Build docker containers
|
name: Build docker containers
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
arch: [amd64, armv7, aarch64]
|
os: ["ubuntu-24.04", "ubuntu-24.04-arm"]
|
||||||
build_type: ["ha-addon", "docker", "lint"]
|
build_type:
|
||||||
|
- "ha-addon"
|
||||||
|
- "docker"
|
||||||
|
# - "lint"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v4.1.7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.7.1
|
uses: docker/setup-buildx-action@v3.10.0
|
||||||
- name: Set up QEMU
|
|
||||||
uses: docker/setup-qemu-action@v3.2.0
|
|
||||||
|
|
||||||
- name: Set TAG
|
- name: Set TAG
|
||||||
run: |
|
run: |
|
||||||
@@ -58,6 +59,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
docker/build.py \
|
docker/build.py \
|
||||||
--tag "${TAG}" \
|
--tag "${TAG}" \
|
||||||
--arch "${{ matrix.arch }}" \
|
--arch "${{ matrix.os == 'ubuntu-24.04-arm' && 'aarch64' || 'amd64' }}" \
|
||||||
--build-type "${{ matrix.build_type }}" \
|
--build-type "${{ matrix.build_type }}" \
|
||||||
build
|
build
|
||||||
|
|||||||
68
.github/workflows/ci.yml
vendored
68
.github/workflows/ci.yml
vendored
@@ -13,6 +13,7 @@ on:
|
|||||||
- ".github/workflows/ci.yml"
|
- ".github/workflows/ci.yml"
|
||||||
- "!.yamllint"
|
- "!.yamllint"
|
||||||
- "!.github/dependabot.yml"
|
- "!.github/dependabot.yml"
|
||||||
|
- "!docker/**"
|
||||||
merge_group:
|
merge_group:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -30,7 +31,7 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
common:
|
common:
|
||||||
name: Create common environment
|
name: Create common environment
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
outputs:
|
outputs:
|
||||||
cache-key: ${{ steps.cache-key.outputs.key }}
|
cache-key: ${{ steps.cache-key.outputs.key }}
|
||||||
steps:
|
steps:
|
||||||
@@ -38,15 +39,15 @@ jobs:
|
|||||||
uses: actions/checkout@v4.1.7
|
uses: actions/checkout@v4.1.7
|
||||||
- name: Generate cache-key
|
- name: Generate cache-key
|
||||||
id: cache-key
|
id: cache-key
|
||||||
run: echo key="${{ hashFiles('requirements.txt', 'requirements_optional.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
|
run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt') }}" >> $GITHUB_OUTPUT
|
||||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||||
id: python
|
id: python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
- name: Restore Python virtual environment
|
- name: Restore Python virtual environment
|
||||||
id: cache-venv
|
id: cache-venv
|
||||||
uses: actions/cache@v4.1.2
|
uses: actions/cache@v4.2.3
|
||||||
with:
|
with:
|
||||||
path: venv
|
path: venv
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
@@ -57,12 +58,12 @@ jobs:
|
|||||||
python -m venv venv
|
python -m venv venv
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
python --version
|
python --version
|
||||||
pip install -r requirements.txt -r requirements_optional.txt -r requirements_test.txt
|
pip install -r requirements.txt -r requirements_test.txt
|
||||||
pip install -e .
|
pip install -e .
|
||||||
|
|
||||||
black:
|
ruff:
|
||||||
name: Check black
|
name: Check ruff
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
@@ -73,17 +74,17 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||||
- name: Run black
|
- name: Run Ruff
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
black --verbose esphome tests
|
ruff format esphome tests
|
||||||
- name: Suggested changes
|
- name: Suggested changes
|
||||||
run: script/ci-suggest-changes
|
run: script/ci-suggest-changes
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
flake8:
|
flake8:
|
||||||
name: Check flake8
|
name: Check flake8
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
@@ -104,7 +105,7 @@ jobs:
|
|||||||
|
|
||||||
pylint:
|
pylint:
|
||||||
name: Check pylint
|
name: Check pylint
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
@@ -125,7 +126,7 @@ jobs:
|
|||||||
|
|
||||||
pyupgrade:
|
pyupgrade:
|
||||||
name: Check pyupgrade
|
name: Check pyupgrade
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
@@ -146,7 +147,7 @@ jobs:
|
|||||||
|
|
||||||
ci-custom:
|
ci-custom:
|
||||||
name: Run script/ci-custom
|
name: Run script/ci-custom
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
@@ -164,6 +165,7 @@ jobs:
|
|||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
script/ci-custom.py
|
script/ci-custom.py
|
||||||
script/build_codeowners.py --check
|
script/build_codeowners.py --check
|
||||||
|
script/build_language_schema.py --check
|
||||||
|
|
||||||
pytest:
|
pytest:
|
||||||
name: Run pytest
|
name: Run pytest
|
||||||
@@ -219,13 +221,13 @@ jobs:
|
|||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
pytest -vv --cov-report=xml --tb=native tests
|
pytest -vv --cov-report=xml --tb=native tests
|
||||||
- name: Upload coverage to Codecov
|
- name: Upload coverage to Codecov
|
||||||
uses: codecov/codecov-action@v4
|
uses: codecov/codecov-action@v5.4.2
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
clang-format:
|
clang-format:
|
||||||
name: Check clang-format
|
name: Check clang-format
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
steps:
|
steps:
|
||||||
@@ -251,10 +253,10 @@ jobs:
|
|||||||
|
|
||||||
clang-tidy:
|
clang-tidy:
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
- black
|
- ruff
|
||||||
- ci-custom
|
- ci-custom
|
||||||
- clang-format
|
- clang-format
|
||||||
- flake8
|
- flake8
|
||||||
@@ -290,6 +292,11 @@ jobs:
|
|||||||
name: Run script/clang-tidy for ESP32 IDF
|
name: Run script/clang-tidy for ESP32 IDF
|
||||||
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
|
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
|
||||||
pio_cache_key: tidyesp32-idf
|
pio_cache_key: tidyesp32-idf
|
||||||
|
- id: clang-tidy
|
||||||
|
name: Run script/clang-tidy for ZEPHYR
|
||||||
|
options: --environment nrf52-tidy --grep USE_ZEPHYR
|
||||||
|
pio_cache_key: tidy-zephyr
|
||||||
|
ignore_errors: true
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
@@ -302,23 +309,18 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
if: github.ref == 'refs/heads/dev'
|
if: github.ref == 'refs/heads/dev'
|
||||||
uses: actions/cache@v4.1.2
|
uses: actions/cache@v4.2.3
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-${{ matrix.pio_cache_key }}
|
key: platformio-${{ matrix.pio_cache_key }}
|
||||||
|
|
||||||
- name: Cache platformio
|
- name: Cache platformio
|
||||||
if: github.ref != 'refs/heads/dev'
|
if: github.ref != 'refs/heads/dev'
|
||||||
uses: actions/cache/restore@v4.1.2
|
uses: actions/cache/restore@v4.2.3
|
||||||
with:
|
with:
|
||||||
path: ~/.platformio
|
path: ~/.platformio
|
||||||
key: platformio-${{ matrix.pio_cache_key }}
|
key: platformio-${{ matrix.pio_cache_key }}
|
||||||
|
|
||||||
- name: Install clang-tidy
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install clang-tidy-14
|
|
||||||
|
|
||||||
- name: Register problem matchers
|
- name: Register problem matchers
|
||||||
run: |
|
run: |
|
||||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||||
@@ -334,18 +336,18 @@ jobs:
|
|||||||
- name: Run clang-tidy
|
- name: Run clang-tidy
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
script/clang-tidy --all-headers --fix ${{ matrix.options }}
|
script/clang-tidy --all-headers --fix ${{ matrix.options }} ${{ matrix.ignore_errors && '|| true' || '' }}
|
||||||
env:
|
env:
|
||||||
# Also cache libdeps, store them in a ~/.platformio subfolder
|
# Also cache libdeps, store them in a ~/.platformio subfolder
|
||||||
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
|
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
|
||||||
|
|
||||||
- name: Suggested changes
|
- name: Suggested changes
|
||||||
run: script/ci-suggest-changes
|
run: script/ci-suggest-changes ${{ matrix.ignore_errors && '|| true' || '' }}
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
list-components:
|
list-components:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
if: github.event_name == 'pull_request'
|
if: github.event_name == 'pull_request'
|
||||||
@@ -387,7 +389,7 @@ jobs:
|
|||||||
|
|
||||||
test-build-components:
|
test-build-components:
|
||||||
name: Component test ${{ matrix.file }}
|
name: Component test ${{ matrix.file }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
- list-components
|
- list-components
|
||||||
@@ -421,7 +423,7 @@ jobs:
|
|||||||
|
|
||||||
test-build-components-splitter:
|
test-build-components-splitter:
|
||||||
name: Split components for testing into 20 groups maximum
|
name: Split components for testing into 20 groups maximum
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
- list-components
|
- list-components
|
||||||
@@ -439,7 +441,7 @@ jobs:
|
|||||||
|
|
||||||
test-build-components-split:
|
test-build-components-split:
|
||||||
name: Test split components
|
name: Test split components
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
- list-components
|
- list-components
|
||||||
@@ -483,10 +485,10 @@ jobs:
|
|||||||
|
|
||||||
ci-status:
|
ci-status:
|
||||||
name: CI Status
|
name: CI Status
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-24.04
|
||||||
needs:
|
needs:
|
||||||
- common
|
- common
|
||||||
- black
|
- ruff
|
||||||
- ci-custom
|
- ci-custom
|
||||||
- clang-format
|
- clang-format
|
||||||
- flake8
|
- flake8
|
||||||
|
|||||||
4
.github/workflows/matchers/lint-python.json
vendored
4
.github/workflows/matchers/lint-python.json
vendored
@@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"problemMatcher": [
|
"problemMatcher": [
|
||||||
{
|
{
|
||||||
"owner": "black",
|
"owner": "ruff",
|
||||||
"severity": "error",
|
"severity": "error",
|
||||||
"pattern": [
|
"pattern": [
|
||||||
{
|
{
|
||||||
"regexp": "^(.*): (Please format this file with the black formatter)",
|
"regexp": "^(.*): (Please format this file with the ruff formatter)",
|
||||||
"file": 1,
|
"file": 1,
|
||||||
"message": 2
|
"message": 2
|
||||||
}
|
}
|
||||||
|
|||||||
109
.github/workflows/release.yml
vendored
109
.github/workflows/release.yml
vendored
@@ -53,7 +53,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v4.1.7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.x"
|
python-version: "3.x"
|
||||||
- name: Set up python environment
|
- name: Set up python environment
|
||||||
@@ -65,43 +65,42 @@ jobs:
|
|||||||
pip3 install build
|
pip3 install build
|
||||||
python3 -m build
|
python3 -m build
|
||||||
- name: Publish
|
- name: Publish
|
||||||
uses: pypa/gh-action-pypi-publish@v1.12.2
|
uses: pypa/gh-action-pypi-publish@v1.12.4
|
||||||
|
|
||||||
deploy-docker:
|
deploy-docker:
|
||||||
name: Build ESPHome ${{ matrix.platform }}
|
name: Build ESPHome ${{ matrix.platform.arch }}
|
||||||
if: github.repository == 'esphome/esphome'
|
if: github.repository == 'esphome/esphome'
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ matrix.platform.os }}
|
||||||
needs: [init]
|
needs: [init]
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
platform:
|
platform:
|
||||||
- linux/amd64
|
- arch: amd64
|
||||||
- linux/arm/v7
|
os: "ubuntu-24.04"
|
||||||
- linux/arm64
|
- arch: arm64
|
||||||
|
os: "ubuntu-24.04-arm"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v4.1.7
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.7.1
|
uses: docker/setup-buildx-action@v3.10.0
|
||||||
- name: Set up QEMU
|
|
||||||
if: matrix.platform != 'linux/amd64'
|
|
||||||
uses: docker/setup-qemu-action@v3.2.0
|
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
uses: docker/login-action@v3.3.0
|
uses: docker/login-action@v3.4.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
uses: docker/login-action@v3.3.0
|
uses: docker/login-action@v3.4.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -110,45 +109,36 @@ jobs:
|
|||||||
- name: Build docker
|
- name: Build docker
|
||||||
uses: ./.github/actions/build-image
|
uses: ./.github/actions/build-image
|
||||||
with:
|
with:
|
||||||
platform: ${{ matrix.platform }}
|
target: final
|
||||||
target: docker
|
build_type: docker
|
||||||
baseimg: docker
|
|
||||||
suffix: ""
|
suffix: ""
|
||||||
version: ${{ needs.init.outputs.tag }}
|
version: ${{ needs.init.outputs.tag }}
|
||||||
|
|
||||||
- name: Build ha-addon
|
- name: Build ha-addon
|
||||||
uses: ./.github/actions/build-image
|
uses: ./.github/actions/build-image
|
||||||
with:
|
with:
|
||||||
platform: ${{ matrix.platform }}
|
target: final
|
||||||
target: hassio
|
build_type: ha-addon
|
||||||
baseimg: hassio
|
|
||||||
suffix: "hassio"
|
suffix: "hassio"
|
||||||
version: ${{ needs.init.outputs.tag }}
|
version: ${{ needs.init.outputs.tag }}
|
||||||
|
|
||||||
- name: Build lint
|
# - name: Build lint
|
||||||
uses: ./.github/actions/build-image
|
# uses: ./.github/actions/build-image
|
||||||
with:
|
# with:
|
||||||
platform: ${{ matrix.platform }}
|
# target: lint
|
||||||
target: lint
|
# build_type: lint
|
||||||
baseimg: docker
|
# suffix: lint
|
||||||
suffix: lint
|
# version: ${{ needs.init.outputs.tag }}
|
||||||
version: ${{ needs.init.outputs.tag }}
|
|
||||||
|
|
||||||
- name: Sanitize platform name
|
|
||||||
id: sanitize
|
|
||||||
run: |
|
|
||||||
echo "${{ matrix.platform }}" | sed 's|/|-|g' > /tmp/platform
|
|
||||||
echo name=$(cat /tmp/platform) >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
- name: Upload digests
|
- name: Upload digests
|
||||||
uses: actions/upload-artifact@v4.4.3
|
uses: actions/upload-artifact@v4.6.2
|
||||||
with:
|
with:
|
||||||
name: digests-${{ steps.sanitize.outputs.name }}
|
name: digests-${{ matrix.platform.arch }}
|
||||||
path: /tmp/digests
|
path: /tmp/digests
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
deploy-manifest:
|
deploy-manifest:
|
||||||
name: Publish ESPHome ${{ matrix.image.title }} to ${{ matrix.registry }}
|
name: Publish ESPHome ${{ matrix.image.build_type }} to ${{ matrix.registry }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs:
|
needs:
|
||||||
- init
|
- init
|
||||||
@@ -161,15 +151,12 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
image:
|
image:
|
||||||
- title: "ha-addon"
|
- build_type: "docker"
|
||||||
target: "hassio"
|
|
||||||
suffix: "hassio"
|
|
||||||
- title: "docker"
|
|
||||||
target: "docker"
|
|
||||||
suffix: ""
|
suffix: ""
|
||||||
- title: "lint"
|
- build_type: "ha-addon"
|
||||||
target: "lint"
|
suffix: "hassio"
|
||||||
suffix: "lint"
|
# - build_type: "lint"
|
||||||
|
# suffix: "lint"
|
||||||
registry:
|
registry:
|
||||||
- ghcr
|
- ghcr
|
||||||
- dockerhub
|
- dockerhub
|
||||||
@@ -177,24 +164,24 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.1.7
|
- uses: actions/checkout@v4.1.7
|
||||||
|
|
||||||
- name: Download digests
|
- name: Download digests
|
||||||
uses: actions/download-artifact@v4.1.8
|
uses: actions/download-artifact@v4.3.0
|
||||||
with:
|
with:
|
||||||
pattern: digests-*
|
pattern: digests-*
|
||||||
path: /tmp/digests
|
path: /tmp/digests
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
uses: docker/setup-buildx-action@v3.7.1
|
uses: docker/setup-buildx-action@v3.10.0
|
||||||
|
|
||||||
- name: Log in to docker hub
|
- name: Log in to docker hub
|
||||||
if: matrix.registry == 'dockerhub'
|
if: matrix.registry == 'dockerhub'
|
||||||
uses: docker/login-action@v3.3.0
|
uses: docker/login-action@v3.4.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.DOCKER_USER }}
|
username: ${{ secrets.DOCKER_USER }}
|
||||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
- name: Log in to the GitHub container registry
|
- name: Log in to the GitHub container registry
|
||||||
if: matrix.registry == 'ghcr'
|
if: matrix.registry == 'ghcr'
|
||||||
uses: docker/login-action@v3.3.0
|
uses: docker/login-action@v3.4.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@@ -213,7 +200,7 @@ jobs:
|
|||||||
done
|
done
|
||||||
|
|
||||||
- name: Create manifest list and push
|
- name: Create manifest list and push
|
||||||
working-directory: /tmp/digests/${{ matrix.image.target }}/${{ matrix.registry }}
|
working-directory: /tmp/digests/${{ matrix.image.build_type }}/${{ matrix.registry }}
|
||||||
run: |
|
run: |
|
||||||
docker buildx imagetools create $(jq -Rcnr 'inputs | . / "," | map("-t " + .) | join(" ")' <<< "${{ steps.tags.outputs.tags}}") \
|
docker buildx imagetools create $(jq -Rcnr 'inputs | . / "," | map("-t " + .) | join(" ")' <<< "${{ steps.tags.outputs.tags}}") \
|
||||||
$(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *)
|
$(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *)
|
||||||
@@ -244,3 +231,25 @@ jobs:
|
|||||||
content: description
|
content: description
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
deploy-esphome-schema:
|
||||||
|
if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- init
|
||||||
|
- deploy-manifest
|
||||||
|
steps:
|
||||||
|
- name: Trigger Workflow
|
||||||
|
uses: actions/github-script@v7.0.1
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.DEPLOY_ESPHOME_SCHEMA_REPO_TOKEN }}
|
||||||
|
script: |
|
||||||
|
github.rest.actions.createWorkflowDispatch({
|
||||||
|
owner: "esphome",
|
||||||
|
repo: "esphome-schema",
|
||||||
|
workflow_id: "generate-schemas.yml",
|
||||||
|
ref: "main",
|
||||||
|
inputs: {
|
||||||
|
version: "${{ needs.init.outputs.tag }}",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|||||||
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9.0.0
|
- uses: actions/stale@v9.1.0
|
||||||
with:
|
with:
|
||||||
days-before-pr-stale: 90
|
days-before-pr-stale: 90
|
||||||
days-before-pr-close: 7
|
days-before-pr-close: 7
|
||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
close-issues:
|
close-issues:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v9.0.0
|
- uses: actions/stale@v9.1.0
|
||||||
with:
|
with:
|
||||||
days-before-pr-stale: -1
|
days-before-pr-stale: -1
|
||||||
days-before-pr-close: -1
|
days-before-pr-close: -1
|
||||||
|
|||||||
8
.github/workflows/sync-device-classes.yml
vendored
8
.github/workflows/sync-device-classes.yml
vendored
@@ -22,7 +22,7 @@ jobs:
|
|||||||
path: lib/home-assistant
|
path: lib/home-assistant
|
||||||
|
|
||||||
- name: Setup Python
|
- name: Setup Python
|
||||||
uses: actions/setup-python@v5.3.0
|
uses: actions/setup-python@v5.6.0
|
||||||
with:
|
with:
|
||||||
python-version: 3.12
|
python-version: 3.12
|
||||||
|
|
||||||
@@ -36,11 +36,11 @@ jobs:
|
|||||||
python ./script/sync-device_class.py
|
python ./script/sync-device_class.py
|
||||||
|
|
||||||
- name: Commit changes
|
- name: Commit changes
|
||||||
uses: peter-evans/create-pull-request@v7.0.5
|
uses: peter-evans/create-pull-request@v7.0.8
|
||||||
with:
|
with:
|
||||||
commit-message: "Synchronise Device Classes from Home Assistant"
|
commit-message: "Synchronise Device Classes from Home Assistant"
|
||||||
committer: esphomebot <esphome@nabucasa.com>
|
committer: esphomebot <esphome@openhomefoundation.org>
|
||||||
author: esphomebot <esphome@nabucasa.com>
|
author: esphomebot <esphome@openhomefoundation.org>
|
||||||
branch: sync/device-classes
|
branch: sync/device-classes
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
title: "Synchronise Device Classes from Home Assistant"
|
title: "Synchronise Device Classes from Home Assistant"
|
||||||
|
|||||||
@@ -4,27 +4,19 @@
|
|||||||
repos:
|
repos:
|
||||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||||
# Ruff version.
|
# Ruff version.
|
||||||
rev: v0.5.4
|
rev: v0.11.9
|
||||||
hooks:
|
hooks:
|
||||||
# Run the linter.
|
# Run the linter.
|
||||||
- id: ruff
|
- id: ruff
|
||||||
args: [--fix]
|
args: [--fix]
|
||||||
# Run the formatter.
|
# Run the formatter.
|
||||||
- id: ruff-format
|
- id: ruff-format
|
||||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
|
||||||
rev: 24.4.2
|
|
||||||
hooks:
|
|
||||||
- id: black
|
|
||||||
args:
|
|
||||||
- --safe
|
|
||||||
- --quiet
|
|
||||||
files: ^((esphome|script|tests)/.+)?[^/]+\.py$
|
|
||||||
- repo: https://github.com/PyCQA/flake8
|
- repo: https://github.com/PyCQA/flake8
|
||||||
rev: 6.1.0
|
rev: 7.2.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- flake8-docstrings==1.5.0
|
- flake8-docstrings==1.7.0
|
||||||
- pydocstyle==5.1.1
|
- pydocstyle==5.1.1
|
||||||
files: ^(esphome|tests)/.+\.py$
|
files: ^(esphome|tests)/.+\.py$
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
@@ -41,7 +33,7 @@ repos:
|
|||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
args: [--py39-plus]
|
args: [--py39-plus]
|
||||||
- repo: https://github.com/adrienverge/yamllint.git
|
- repo: https://github.com/adrienverge/yamllint.git
|
||||||
rev: v1.35.1
|
rev: v1.37.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: yamllint
|
- id: yamllint
|
||||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||||
@@ -53,6 +45,6 @@ repos:
|
|||||||
hooks:
|
hooks:
|
||||||
- id: pylint
|
- id: pylint
|
||||||
name: pylint
|
name: pylint
|
||||||
entry: script/run-in-env.sh pylint
|
entry: python3 script/run-in-env.py pylint
|
||||||
language: script
|
language: system
|
||||||
types: [python]
|
types: [python]
|
||||||
|
|||||||
35
CODEOWNERS
35
CODEOWNERS
@@ -49,6 +49,7 @@ esphome/components/atc_mithermometer/* @ahpohl
|
|||||||
esphome/components/atm90e26/* @danieltwagner
|
esphome/components/atm90e26/* @danieltwagner
|
||||||
esphome/components/atm90e32/* @circuitsetup @descipher
|
esphome/components/atm90e32/* @circuitsetup @descipher
|
||||||
esphome/components/audio/* @kahrendt
|
esphome/components/audio/* @kahrendt
|
||||||
|
esphome/components/audio_adc/* @kbx81
|
||||||
esphome/components/audio_dac/* @kbx81
|
esphome/components/audio_dac/* @kbx81
|
||||||
esphome/components/axs15231/* @clydebarrow
|
esphome/components/axs15231/* @clydebarrow
|
||||||
esphome/components/b_parasite/* @rbaron
|
esphome/components/b_parasite/* @rbaron
|
||||||
@@ -92,10 +93,12 @@ esphome/components/captive_portal/* @OttoWinter
|
|||||||
esphome/components/ccs811/* @habbie
|
esphome/components/ccs811/* @habbie
|
||||||
esphome/components/cd74hc4067/* @asoehlke
|
esphome/components/cd74hc4067/* @asoehlke
|
||||||
esphome/components/ch422g/* @clydebarrow @jesterret
|
esphome/components/ch422g/* @clydebarrow @jesterret
|
||||||
|
esphome/components/chsc6x/* @kkosik20
|
||||||
esphome/components/climate/* @esphome/core
|
esphome/components/climate/* @esphome/core
|
||||||
esphome/components/climate_ir/* @glmnet
|
esphome/components/climate_ir/* @glmnet
|
||||||
esphome/components/color_temperature/* @jesserockz
|
esphome/components/color_temperature/* @jesserockz
|
||||||
esphome/components/combination/* @Cat-Ion @kahrendt
|
esphome/components/combination/* @Cat-Ion @kahrendt
|
||||||
|
esphome/components/const/* @esphome/core
|
||||||
esphome/components/coolix/* @glmnet
|
esphome/components/coolix/* @glmnet
|
||||||
esphome/components/copy/* @OttoWinter
|
esphome/components/copy/* @OttoWinter
|
||||||
esphome/components/cover/* @esphome/core
|
esphome/components/cover/* @esphome/core
|
||||||
@@ -131,6 +134,9 @@ esphome/components/ens160_base/* @latonita @vincentscode
|
|||||||
esphome/components/ens160_i2c/* @latonita
|
esphome/components/ens160_i2c/* @latonita
|
||||||
esphome/components/ens160_spi/* @latonita
|
esphome/components/ens160_spi/* @latonita
|
||||||
esphome/components/ens210/* @itn3rd77
|
esphome/components/ens210/* @itn3rd77
|
||||||
|
esphome/components/es7210/* @kahrendt
|
||||||
|
esphome/components/es7243e/* @kbx81
|
||||||
|
esphome/components/es8156/* @kbx81
|
||||||
esphome/components/es8311/* @kahrendt @kroimon
|
esphome/components/es8311/* @kahrendt @kroimon
|
||||||
esphome/components/esp32/* @esphome/core
|
esphome/components/esp32/* @esphome/core
|
||||||
esphome/components/esp32_ble/* @Rapsssito @jesserockz
|
esphome/components/esp32_ble/* @Rapsssito @jesserockz
|
||||||
@@ -144,6 +150,7 @@ esphome/components/esp32_rmt_led_strip/* @jesserockz
|
|||||||
esphome/components/esp8266/* @esphome/core
|
esphome/components/esp8266/* @esphome/core
|
||||||
esphome/components/ethernet_info/* @gtjadsonsantos
|
esphome/components/ethernet_info/* @gtjadsonsantos
|
||||||
esphome/components/event/* @nohat
|
esphome/components/event/* @nohat
|
||||||
|
esphome/components/event_emitter/* @Rapsssito
|
||||||
esphome/components/exposure_notifications/* @OttoWinter
|
esphome/components/exposure_notifications/* @OttoWinter
|
||||||
esphome/components/ezo/* @ssieb
|
esphome/components/ezo/* @ssieb
|
||||||
esphome/components/ezo_pmp/* @carlos-sarmiento
|
esphome/components/ezo_pmp/* @carlos-sarmiento
|
||||||
@@ -179,6 +186,7 @@ esphome/components/haier/text_sensor/* @paveldn
|
|||||||
esphome/components/havells_solar/* @sourabhjaiswal
|
esphome/components/havells_solar/* @sourabhjaiswal
|
||||||
esphome/components/hbridge/fan/* @WeekendWarrior
|
esphome/components/hbridge/fan/* @WeekendWarrior
|
||||||
esphome/components/hbridge/light/* @DotNetDann
|
esphome/components/hbridge/light/* @DotNetDann
|
||||||
|
esphome/components/hbridge/switch/* @dwmw2
|
||||||
esphome/components/he60r/* @clydebarrow
|
esphome/components/he60r/* @clydebarrow
|
||||||
esphome/components/heatpumpir/* @rob-deutsch
|
esphome/components/heatpumpir/* @rob-deutsch
|
||||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||||
@@ -228,6 +236,7 @@ esphome/components/kuntze/* @ssieb
|
|||||||
esphome/components/lcd_menu/* @numo68
|
esphome/components/lcd_menu/* @numo68
|
||||||
esphome/components/ld2410/* @regevbr @sebcaps
|
esphome/components/ld2410/* @regevbr @sebcaps
|
||||||
esphome/components/ld2420/* @descipher
|
esphome/components/ld2420/* @descipher
|
||||||
|
esphome/components/ld2450/* @hareeshmu
|
||||||
esphome/components/ledc/* @OttoWinter
|
esphome/components/ledc/* @OttoWinter
|
||||||
esphome/components/libretiny/* @kuba2k2
|
esphome/components/libretiny/* @kuba2k2
|
||||||
esphome/components/libretiny_pwm/* @kuba2k2
|
esphome/components/libretiny_pwm/* @kuba2k2
|
||||||
@@ -236,11 +245,13 @@ esphome/components/lightwaverf/* @max246
|
|||||||
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
esphome/components/lilygo_t5_47/touchscreen/* @jesserockz
|
||||||
esphome/components/lock/* @esphome/core
|
esphome/components/lock/* @esphome/core
|
||||||
esphome/components/logger/* @esphome/core
|
esphome/components/logger/* @esphome/core
|
||||||
|
esphome/components/logger/select/* @clydebarrow
|
||||||
esphome/components/ltr390/* @latonita @sjtrny
|
esphome/components/ltr390/* @latonita @sjtrny
|
||||||
esphome/components/ltr501/* @latonita
|
esphome/components/ltr501/* @latonita
|
||||||
esphome/components/ltr_als_ps/* @latonita
|
esphome/components/ltr_als_ps/* @latonita
|
||||||
esphome/components/lvgl/* @clydebarrow
|
esphome/components/lvgl/* @clydebarrow
|
||||||
esphome/components/m5stack_8angle/* @rnauber
|
esphome/components/m5stack_8angle/* @rnauber
|
||||||
|
esphome/components/mapping/* @clydebarrow
|
||||||
esphome/components/matrix_keypad/* @ssieb
|
esphome/components/matrix_keypad/* @ssieb
|
||||||
esphome/components/max17043/* @blacknell
|
esphome/components/max17043/* @blacknell
|
||||||
esphome/components/max31865/* @DAVe3283
|
esphome/components/max31865/* @DAVe3283
|
||||||
@@ -257,6 +268,7 @@ esphome/components/mcp23x17_base/* @jesserockz
|
|||||||
esphome/components/mcp23xxx_base/* @jesserockz
|
esphome/components/mcp23xxx_base/* @jesserockz
|
||||||
esphome/components/mcp2515/* @danielschramm @mvturnho
|
esphome/components/mcp2515/* @danielschramm @mvturnho
|
||||||
esphome/components/mcp3204/* @rsumner
|
esphome/components/mcp3204/* @rsumner
|
||||||
|
esphome/components/mcp4461/* @p1ngb4ck
|
||||||
esphome/components/mcp4728/* @berfenger
|
esphome/components/mcp4728/* @berfenger
|
||||||
esphome/components/mcp47a1/* @jesserockz
|
esphome/components/mcp47a1/* @jesserockz
|
||||||
esphome/components/mcp9600/* @mreditor97
|
esphome/components/mcp9600/* @mreditor97
|
||||||
@@ -266,11 +278,13 @@ esphome/components/mdns/* @esphome/core
|
|||||||
esphome/components/media_player/* @jesserockz
|
esphome/components/media_player/* @jesserockz
|
||||||
esphome/components/micro_wake_word/* @jesserockz @kahrendt
|
esphome/components/micro_wake_word/* @jesserockz @kahrendt
|
||||||
esphome/components/micronova/* @jorre05
|
esphome/components/micronova/* @jorre05
|
||||||
esphome/components/microphone/* @jesserockz
|
esphome/components/microphone/* @jesserockz @kahrendt
|
||||||
esphome/components/mics_4514/* @jesserockz
|
esphome/components/mics_4514/* @jesserockz
|
||||||
esphome/components/midea/* @dudanov
|
esphome/components/midea/* @dudanov
|
||||||
esphome/components/midea_ir/* @dudanov
|
esphome/components/midea_ir/* @dudanov
|
||||||
|
esphome/components/mipi_spi/* @clydebarrow
|
||||||
esphome/components/mitsubishi/* @RubyBailey
|
esphome/components/mitsubishi/* @RubyBailey
|
||||||
|
esphome/components/mixer/speaker/* @kahrendt
|
||||||
esphome/components/mlx90393/* @functionpointer
|
esphome/components/mlx90393/* @functionpointer
|
||||||
esphome/components/mlx90614/* @jesserockz
|
esphome/components/mlx90614/* @jesserockz
|
||||||
esphome/components/mmc5603/* @benhoff
|
esphome/components/mmc5603/* @benhoff
|
||||||
@@ -289,6 +303,7 @@ esphome/components/mopeka_std_check/* @Fabian-Schmidt
|
|||||||
esphome/components/mpl3115a2/* @kbickar
|
esphome/components/mpl3115a2/* @kbickar
|
||||||
esphome/components/mpu6886/* @fabaff
|
esphome/components/mpu6886/* @fabaff
|
||||||
esphome/components/ms8607/* @e28eta
|
esphome/components/ms8607/* @e28eta
|
||||||
|
esphome/components/msa3xx/* @latonita
|
||||||
esphome/components/nau7802/* @cujomalainey
|
esphome/components/nau7802/* @cujomalainey
|
||||||
esphome/components/network/* @esphome/core
|
esphome/components/network/* @esphome/core
|
||||||
esphome/components/nextion/* @edwardtfn @senexcrenshaw
|
esphome/components/nextion/* @edwardtfn @senexcrenshaw
|
||||||
@@ -301,10 +316,11 @@ esphome/components/noblex/* @AGalfra
|
|||||||
esphome/components/npi19/* @bakerkj
|
esphome/components/npi19/* @bakerkj
|
||||||
esphome/components/number/* @esphome/core
|
esphome/components/number/* @esphome/core
|
||||||
esphome/components/one_wire/* @ssieb
|
esphome/components/one_wire/* @ssieb
|
||||||
esphome/components/online_image/* @guillempages
|
esphome/components/online_image/* @clydebarrow @guillempages
|
||||||
esphome/components/opentherm/* @olegtarasov
|
esphome/components/opentherm/* @olegtarasov
|
||||||
esphome/components/ota/* @esphome/core
|
esphome/components/ota/* @esphome/core
|
||||||
esphome/components/output/* @esphome/core
|
esphome/components/output/* @esphome/core
|
||||||
|
esphome/components/packet_transport/* @clydebarrow
|
||||||
esphome/components/pca6416a/* @Mat931
|
esphome/components/pca6416a/* @Mat931
|
||||||
esphome/components/pca9554/* @clydebarrow @hwstar
|
esphome/components/pca9554/* @clydebarrow @hwstar
|
||||||
esphome/components/pcf85063/* @brogon
|
esphome/components/pcf85063/* @brogon
|
||||||
@@ -312,7 +328,9 @@ esphome/components/pcf8563/* @KoenBreeman
|
|||||||
esphome/components/pid/* @OttoWinter
|
esphome/components/pid/* @OttoWinter
|
||||||
esphome/components/pipsolar/* @andreashergert1984
|
esphome/components/pipsolar/* @andreashergert1984
|
||||||
esphome/components/pm1006/* @habbie
|
esphome/components/pm1006/* @habbie
|
||||||
|
esphome/components/pm2005/* @andrewjswan
|
||||||
esphome/components/pmsa003i/* @sjtrny
|
esphome/components/pmsa003i/* @sjtrny
|
||||||
|
esphome/components/pmsx003/* @ximex
|
||||||
esphome/components/pmwcs3/* @SeByDocKy
|
esphome/components/pmwcs3/* @SeByDocKy
|
||||||
esphome/components/pn532/* @OttoWinter @jesserockz
|
esphome/components/pn532/* @OttoWinter @jesserockz
|
||||||
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
||||||
@@ -337,7 +355,7 @@ esphome/components/radon_eye_rd200/* @jeffeb3
|
|||||||
esphome/components/rc522/* @glmnet
|
esphome/components/rc522/* @glmnet
|
||||||
esphome/components/rc522_i2c/* @glmnet
|
esphome/components/rc522_i2c/* @glmnet
|
||||||
esphome/components/rc522_spi/* @glmnet
|
esphome/components/rc522_spi/* @glmnet
|
||||||
esphome/components/resistance_sampler/* @jesserockz
|
esphome/components/resampler/speaker/* @kahrendt
|
||||||
esphome/components/restart/* @esphome/core
|
esphome/components/restart/* @esphome/core
|
||||||
esphome/components/rf_bridge/* @jesserockz
|
esphome/components/rf_bridge/* @jesserockz
|
||||||
esphome/components/rgbct/* @jesserockz
|
esphome/components/rgbct/* @jesserockz
|
||||||
@@ -350,10 +368,12 @@ esphome/components/rtttl/* @glmnet
|
|||||||
esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
|
esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
|
||||||
esphome/components/scd4x/* @martgras @sjtrny
|
esphome/components/scd4x/* @martgras @sjtrny
|
||||||
esphome/components/script/* @esphome/core
|
esphome/components/script/* @esphome/core
|
||||||
esphome/components/sdl/* @clydebarrow
|
esphome/components/sdl/* @bdm310 @clydebarrow
|
||||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
||||||
esphome/components/sdp3x/* @Azimath
|
esphome/components/sdp3x/* @Azimath
|
||||||
esphome/components/seeed_mr24hpc1/* @limengdu
|
esphome/components/seeed_mr24hpc1/* @limengdu
|
||||||
|
esphome/components/seeed_mr60bha2/* @limengdu
|
||||||
|
esphome/components/seeed_mr60fda2/* @limengdu
|
||||||
esphome/components/selec_meter/* @sourabhjaiswal
|
esphome/components/selec_meter/* @sourabhjaiswal
|
||||||
esphome/components/select/* @esphome/core
|
esphome/components/select/* @esphome/core
|
||||||
esphome/components/sen0321/* @notjj
|
esphome/components/sen0321/* @notjj
|
||||||
@@ -379,7 +399,9 @@ esphome/components/smt100/* @piechade
|
|||||||
esphome/components/sn74hc165/* @jesserockz
|
esphome/components/sn74hc165/* @jesserockz
|
||||||
esphome/components/socket/* @esphome/core
|
esphome/components/socket/* @esphome/core
|
||||||
esphome/components/sonoff_d1/* @anatoly-savchenkov
|
esphome/components/sonoff_d1/* @anatoly-savchenkov
|
||||||
|
esphome/components/sound_level/* @kahrendt
|
||||||
esphome/components/speaker/* @jesserockz @kahrendt
|
esphome/components/speaker/* @jesserockz @kahrendt
|
||||||
|
esphome/components/speaker/media_player/* @kahrendt @synesthesiam
|
||||||
esphome/components/spi/* @clydebarrow @esphome/core
|
esphome/components/spi/* @clydebarrow @esphome/core
|
||||||
esphome/components/spi_device/* @clydebarrow
|
esphome/components/spi_device/* @clydebarrow
|
||||||
esphome/components/spi_led_strip/* @clydebarrow
|
esphome/components/spi_led_strip/* @clydebarrow
|
||||||
@@ -408,6 +430,8 @@ esphome/components/substitutions/* @esphome/core
|
|||||||
esphome/components/sun/* @OttoWinter
|
esphome/components/sun/* @OttoWinter
|
||||||
esphome/components/sun_gtil2/* @Mat931
|
esphome/components/sun_gtil2/* @Mat931
|
||||||
esphome/components/switch/* @esphome/core
|
esphome/components/switch/* @esphome/core
|
||||||
|
esphome/components/switch/binary_sensor/* @ssieb
|
||||||
|
esphome/components/syslog/* @clydebarrow
|
||||||
esphome/components/t6615/* @tylermenezes
|
esphome/components/t6615/* @tylermenezes
|
||||||
esphome/components/tc74/* @sethgirvan
|
esphome/components/tc74/* @sethgirvan
|
||||||
esphome/components/tca9548a/* @andreashergert1984
|
esphome/components/tca9548a/* @andreashergert1984
|
||||||
@@ -433,6 +457,7 @@ esphome/components/tmp102/* @timsavage
|
|||||||
esphome/components/tmp1075/* @sybrenstuvel
|
esphome/components/tmp1075/* @sybrenstuvel
|
||||||
esphome/components/tmp117/* @Azimath
|
esphome/components/tmp117/* @Azimath
|
||||||
esphome/components/tof10120/* @wstrzalka
|
esphome/components/tof10120/* @wstrzalka
|
||||||
|
esphome/components/tormatic/* @ti-mo
|
||||||
esphome/components/toshiba/* @kbx81
|
esphome/components/toshiba/* @kbx81
|
||||||
esphome/components/touchscreen/* @jesserockz @nielsnl68
|
esphome/components/touchscreen/* @jesserockz @nielsnl68
|
||||||
esphome/components/tsl2591/* @wjcarpenter
|
esphome/components/tsl2591/* @wjcarpenter
|
||||||
@@ -446,6 +471,7 @@ esphome/components/tuya/switch/* @jesserockz
|
|||||||
esphome/components/tuya/text_sensor/* @dentra
|
esphome/components/tuya/text_sensor/* @dentra
|
||||||
esphome/components/uart/* @esphome/core
|
esphome/components/uart/* @esphome/core
|
||||||
esphome/components/uart/button/* @ssieb
|
esphome/components/uart/button/* @ssieb
|
||||||
|
esphome/components/uart/packet_transport/* @clydebarrow
|
||||||
esphome/components/udp/* @clydebarrow
|
esphome/components/udp/* @clydebarrow
|
||||||
esphome/components/ufire_ec/* @pvizeli
|
esphome/components/ufire_ec/* @pvizeli
|
||||||
esphome/components/ufire_ise/* @pvizeli
|
esphome/components/ufire_ise/* @pvizeli
|
||||||
@@ -489,5 +515,6 @@ esphome/components/xiaomi_mhoc401/* @vevsvevs
|
|||||||
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
esphome/components/xiaomi_rtcgq02lm/* @jesserockz
|
||||||
esphome/components/xl9535/* @mreditor97
|
esphome/components/xl9535/* @mreditor97
|
||||||
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
|
esphome/components/xpt2046/touchscreen/* @nielsnl68 @numo68
|
||||||
|
esphome/components/xxtea/* @clydebarrow
|
||||||
esphome/components/zhlt01/* @cfeenstra1024
|
esphome/components/zhlt01/* @cfeenstra1024
|
||||||
esphome/components/zio_ultrasonic/* @kahrendt
|
esphome/components/zio_ultrasonic/* @kahrendt
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
|
|||||||
|
|
||||||
## Enforcement
|
## Enforcement
|
||||||
|
|
||||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at esphome@nabucasa.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at esphome@openhomefoundation.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
# Contributing to ESPHome
|
# Contributing to ESPHome [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
|
||||||
|
|
||||||
For a detailed guide, please see https://esphome.io/guides/contributing.html#contributing-to-esphome
|
We welcome contributions to the ESPHome suite of code and documentation!
|
||||||
|
|
||||||
Things to note when contributing:
|
Please read our [contributing guide](https://esphome.io/guides/contributing.html) if you wish to contribute to the
|
||||||
|
project and be sure to join us on [Discord](https://discord.gg/KhAMKrd).
|
||||||
|
|
||||||
- Please test your changes :)
|
**See also:**
|
||||||
- If a new feature is added or an existing user-facing feature is changed, you should also
|
|
||||||
update the [docs](https://github.com/esphome/esphome-docs). See [contributing to esphome-docs](https://esphome.io/guides/contributing.html#contributing-to-esphomedocs)
|
[Documentation](https://esphome.io) -- [Issues](https://github.com/esphome/issues/issues) -- [Feature requests](https://github.com/esphome/feature-requests/issues)
|
||||||
for more information.
|
|
||||||
- Please also update the tests in the `tests/` folder. You can do so by just adding a line in one of the YAML files
|
---
|
||||||
which checks if your new feature compiles correctly.
|
|
||||||
|
[](https://www.openhomefoundation.org/)
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -1,11 +1,16 @@
|
|||||||
# ESPHome [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
|
# ESPHome [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
|
||||||
|
|
||||||
[](https://esphome.io/)
|
<a href="https://esphome.io/">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://esphome.io/_static/logo-text-on-dark.svg", alt="ESPHome Logo">
|
||||||
|
<img src="https://esphome.io/_static/logo-text-on-light.svg" alt="ESPHome Logo">
|
||||||
|
</picture>
|
||||||
|
</a>
|
||||||
|
|
||||||
**Documentation:** https://esphome.io/
|
---
|
||||||
|
|
||||||
For issues, please go to [the issue tracker](https://github.com/esphome/issues/issues).
|
[Documentation](https://esphome.io) -- [Issues](https://github.com/esphome/issues/issues) -- [Feature requests](https://github.com/esphome/feature-requests/issues)
|
||||||
|
|
||||||
For feature requests, please see [feature requests](https://github.com/esphome/feature-requests/issues).
|
---
|
||||||
|
|
||||||
[](https://www.openhomefoundation.org/)
|
[](https://www.openhomefoundation.org/)
|
||||||
|
|||||||
@@ -1,155 +1,54 @@
|
|||||||
# Build these with the build.py script
|
ARG BUILD_VERSION=dev
|
||||||
# Example:
|
ARG BUILD_OS=alpine
|
||||||
# python3 docker/build.py --tag dev --arch amd64 --build-type docker build
|
ARG BUILD_BASE_VERSION=2025.04.0
|
||||||
|
ARG BUILD_TYPE=docker
|
||||||
|
|
||||||
# One of "docker", "hassio"
|
FROM ghcr.io/esphome/docker-base:${BUILD_OS}-${BUILD_BASE_VERSION} AS base-source-docker
|
||||||
ARG BASEIMGTYPE=docker
|
FROM ghcr.io/esphome/docker-base:${BUILD_OS}-ha-addon-${BUILD_BASE_VERSION} AS base-source-ha-addon
|
||||||
|
|
||||||
|
ARG BUILD_TYPE
|
||||||
|
FROM base-source-${BUILD_TYPE} AS base
|
||||||
|
|
||||||
# https://github.com/hassio-addons/addon-debian-base/releases
|
RUN git config --system --add safe.directory "*"
|
||||||
FROM ghcr.io/hassio-addons/debian-base:7.2.0 AS base-hassio
|
|
||||||
# https://hub.docker.com/_/debian?tab=tags&page=1&name=bookworm
|
|
||||||
FROM debian:12.2-slim AS base-docker
|
|
||||||
|
|
||||||
FROM base-${BASEIMGTYPE} AS base
|
RUN pip install uv==0.6.14
|
||||||
|
|
||||||
|
COPY requirements.txt /
|
||||||
ARG TARGETARCH
|
|
||||||
ARG TARGETVARIANT
|
|
||||||
|
|
||||||
|
|
||||||
# Note that --break-system-packages is used below because
|
|
||||||
# https://peps.python.org/pep-0668/ added a safety check that prevents
|
|
||||||
# installing packages with the same name as a system package. This is
|
|
||||||
# not a problem for us because we are not concerned about overwriting
|
|
||||||
# system packages because we are running in an isolated container.
|
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
apt-get update \
|
uv pip install --no-cache-dir \
|
||||||
# Use pinned versions so that we get updates with build caching
|
-r /requirements.txt
|
||||||
&& apt-get install -y --no-install-recommends \
|
|
||||||
python3-pip=23.0.1+dfsg-1 \
|
|
||||||
python3-setuptools=66.1.1-1 \
|
|
||||||
python3-venv=3.11.2-1+b1 \
|
|
||||||
python3-wheel=0.38.4-2 \
|
|
||||||
iputils-ping=3:20221126-1+deb12u1 \
|
|
||||||
git=1:2.39.5-0+deb12u1 \
|
|
||||||
curl=7.88.1-10+deb12u8 \
|
|
||||||
openssh-client=1:9.2p1-2+deb12u3 \
|
|
||||||
python3-cffi=1.15.1-5 \
|
|
||||||
libcairo2=1.16.0-7 \
|
|
||||||
libmagic1=1:5.44-3 \
|
|
||||||
patch=2.7.6-7 \
|
|
||||||
&& rm -rf \
|
|
||||||
/tmp/* \
|
|
||||||
/var/{cache,log}/* \
|
|
||||||
/var/lib/apt/lists/*
|
|
||||||
|
|
||||||
ENV \
|
|
||||||
# Fix click python3 lang warning https://click.palletsprojects.com/en/7.x/python3/
|
|
||||||
LANG=C.UTF-8 LC_ALL=C.UTF-8 \
|
|
||||||
# Store globally installed pio libs in /piolibs
|
|
||||||
PLATFORMIO_GLOBALLIB_DIR=/piolibs
|
|
||||||
|
|
||||||
# Support legacy binaries on Debian multiarch system. There is no "correct" way
|
|
||||||
# to do this, other than using properly built toolchains...
|
|
||||||
# See: https://unix.stackexchange.com/questions/553743/correct-way-to-add-lib-ld-linux-so-3-in-debian
|
|
||||||
RUN \
|
|
||||||
if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
|
|
||||||
ln -s /lib/arm-linux-gnueabihf/ld-linux-armhf.so.3 /lib/ld-linux.so.3; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
# Ubuntu python3-pip is missing wheel
|
platformio settings set enable_telemetry No \
|
||||||
if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
|
|
||||||
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
|
|
||||||
fi; \
|
|
||||||
pip3 install \
|
|
||||||
--break-system-packages --no-cache-dir \
|
|
||||||
# Keep platformio version in sync with requirements.txt
|
|
||||||
platformio==6.1.16 \
|
|
||||||
# Change some platformio settings
|
|
||||||
&& platformio settings set enable_telemetry No \
|
|
||||||
&& platformio settings set check_platformio_interval 1000000 \
|
&& platformio settings set check_platformio_interval 1000000 \
|
||||||
&& mkdir -p /piolibs
|
&& mkdir -p /piolibs
|
||||||
|
|
||||||
|
|
||||||
# First install requirements to leverage caching when requirements don't change
|
|
||||||
# tmpfs is for https://github.com/rust-lang/cargo/issues/8719
|
|
||||||
|
|
||||||
COPY requirements.txt requirements_optional.txt /
|
|
||||||
RUN --mount=type=tmpfs,target=/root/.cargo <<END-OF-RUN
|
|
||||||
# Fail on any non-zero status
|
|
||||||
set -e
|
|
||||||
|
|
||||||
if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]
|
|
||||||
then
|
|
||||||
curl -L https://www.piwheels.org/cp311/cryptography-43.0.0-cp37-abi3-linux_armv7l.whl -o /tmp/cryptography-43.0.0-cp37-abi3-linux_armv7l.whl
|
|
||||||
pip3 install --break-system-packages --no-cache-dir /tmp/cryptography-43.0.0-cp37-abi3-linux_armv7l.whl
|
|
||||||
rm /tmp/cryptography-43.0.0-cp37-abi3-linux_armv7l.whl
|
|
||||||
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple";
|
|
||||||
fi
|
|
||||||
|
|
||||||
# install build tools in case wheels are not available
|
|
||||||
BUILD_DEPS="
|
|
||||||
build-essential=12.9
|
|
||||||
python3-dev=3.11.2-1+b1
|
|
||||||
zlib1g-dev=1:1.2.13.dfsg-1
|
|
||||||
libjpeg-dev=1:2.1.5-2
|
|
||||||
libfreetype-dev=2.12.1+dfsg-5+deb12u3
|
|
||||||
libssl-dev=3.0.15-1~deb12u1
|
|
||||||
libffi-dev=3.4.4-1
|
|
||||||
cargo=0.66.0+ds1-1
|
|
||||||
pkg-config=1.8.1-1
|
|
||||||
"
|
|
||||||
LIB_DEPS="
|
|
||||||
libtiff6=4.5.0-6+deb12u1
|
|
||||||
libopenjp2-7=2.5.0-2
|
|
||||||
"
|
|
||||||
if [ "$TARGETARCH$TARGETVARIANT" = "arm64" ] || [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]
|
|
||||||
then
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y --no-install-recommends $BUILD_DEPS $LIB_DEPS
|
|
||||||
fi
|
|
||||||
|
|
||||||
CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse CARGO_HOME=/root/.cargo
|
|
||||||
pip3 install --break-system-packages --no-cache-dir -r /requirements.txt -r /requirements_optional.txt
|
|
||||||
|
|
||||||
if [ "$TARGETARCH$TARGETVARIANT" = "arm64" ] || [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]
|
|
||||||
then
|
|
||||||
apt-get remove -y --purge --auto-remove $BUILD_DEPS
|
|
||||||
rm -rf /tmp/* /var/{cache,log}/* /var/lib/apt/lists/*
|
|
||||||
fi
|
|
||||||
END-OF-RUN
|
|
||||||
|
|
||||||
|
|
||||||
COPY script/platformio_install_deps.py platformio.ini /
|
COPY script/platformio_install_deps.py platformio.ini /
|
||||||
RUN /platformio_install_deps.py /platformio.ini --libraries
|
RUN /platformio_install_deps.py /platformio.ini --libraries
|
||||||
|
|
||||||
# Avoid unsafe git error when container user and file config volume permissions don't match
|
ARG BUILD_VERSION
|
||||||
RUN git config --system --add safe.directory '*'
|
|
||||||
|
LABEL \
|
||||||
|
org.opencontainers.image.authors="The ESPHome Authors" \
|
||||||
|
org.opencontainers.image.title="ESPHome" \
|
||||||
|
org.opencontainers.image.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \
|
||||||
|
org.opencontainers.image.url="https://esphome.io/" \
|
||||||
|
org.opencontainers.image.documentation="https://esphome.io/" \
|
||||||
|
org.opencontainers.image.source="https://github.com/esphome/esphome" \
|
||||||
|
org.opencontainers.image.licenses="ESPHome" \
|
||||||
|
org.opencontainers.image.version=${BUILD_VERSION}
|
||||||
|
|
||||||
|
|
||||||
# ======================= docker-type image =======================
|
# ======================= docker-type image =======================
|
||||||
FROM base AS docker
|
FROM base AS base-docker
|
||||||
|
|
||||||
# Copy esphome and install
|
|
||||||
COPY . /esphome
|
|
||||||
RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
|
|
||||||
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
|
|
||||||
fi; \
|
|
||||||
pip3 install \
|
|
||||||
--break-system-packages --no-cache-dir -e /esphome
|
|
||||||
|
|
||||||
# Settings for dashboard
|
|
||||||
ENV USERNAME="" PASSWORD=""
|
|
||||||
|
|
||||||
# Expose the dashboard to Docker
|
# Expose the dashboard to Docker
|
||||||
EXPOSE 6052
|
EXPOSE 6052
|
||||||
|
|
||||||
# Run healthcheck (heartbeat)
|
# Run healthcheck (heartbeat)
|
||||||
HEALTHCHECK --interval=30s --timeout=30s \
|
HEALTHCHECK --interval=30s --timeout=30s \
|
||||||
CMD curl --fail http://localhost:6052/version -A "HealthCheck" || exit 1
|
CMD curl --fail http://localhost:6052/version -A "HealthCheck" || exit 1
|
||||||
|
|
||||||
COPY docker/docker_entrypoint.sh /entrypoint.sh
|
COPY docker/docker_entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
@@ -163,73 +62,23 @@ ENTRYPOINT ["/entrypoint.sh"]
|
|||||||
CMD ["dashboard", "/config"]
|
CMD ["dashboard", "/config"]
|
||||||
|
|
||||||
|
|
||||||
|
# ======================= ha-addon-type image =======================
|
||||||
|
FROM base AS base-ha-addon
|
||||||
# ======================= hassio-type image =======================
|
|
||||||
FROM base AS hassio
|
|
||||||
|
|
||||||
RUN \
|
|
||||||
apt-get update \
|
|
||||||
# Use pinned versions so that we get updates with build caching
|
|
||||||
&& apt-get install -y --no-install-recommends \
|
|
||||||
nginx-light=1.22.1-9 \
|
|
||||||
&& rm -rf \
|
|
||||||
/tmp/* \
|
|
||||||
/var/{cache,log}/* \
|
|
||||||
/var/lib/apt/lists/*
|
|
||||||
|
|
||||||
ARG BUILD_VERSION=dev
|
|
||||||
|
|
||||||
# Copy root filesystem
|
# Copy root filesystem
|
||||||
COPY docker/ha-addon-rootfs/ /
|
COPY docker/ha-addon-rootfs/ /
|
||||||
|
|
||||||
# Copy esphome and install
|
ARG BUILD_VERSION
|
||||||
COPY . /esphome
|
|
||||||
RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
|
|
||||||
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
|
|
||||||
fi; \
|
|
||||||
pip3 install \
|
|
||||||
--break-system-packages --no-cache-dir -e /esphome
|
|
||||||
|
|
||||||
# Labels
|
|
||||||
LABEL \
|
LABEL \
|
||||||
io.hass.name="ESPHome" \
|
io.hass.name="ESPHome" \
|
||||||
io.hass.description="Manage and program ESP8266/ESP32 microcontrollers through YAML configuration files" \
|
io.hass.description="ESPHome is a system to configure your microcontrollers by simple yet powerful configuration files and control them remotely through Home Automation systems" \
|
||||||
io.hass.type="addon" \
|
io.hass.type="addon" \
|
||||||
io.hass.version="${BUILD_VERSION}"
|
io.hass.version="${BUILD_VERSION}"
|
||||||
# io.hass.arch is inherited from addon-debian-base
|
# io.hass.arch is inherited from addon-debian-base
|
||||||
|
|
||||||
|
ARG BUILD_TYPE
|
||||||
|
FROM base-${BUILD_TYPE} AS final
|
||||||
|
|
||||||
|
# Copy esphome and install
|
||||||
|
COPY . /esphome
|
||||||
# ======================= lint-type image =======================
|
RUN uv pip install --no-cache-dir -e /esphome
|
||||||
FROM base AS lint
|
|
||||||
|
|
||||||
ENV \
|
|
||||||
PLATFORMIO_CORE_DIR=/esphome/.temp/platformio
|
|
||||||
|
|
||||||
RUN \
|
|
||||||
apt-get update \
|
|
||||||
# Use pinned versions so that we get updates with build caching
|
|
||||||
&& apt-get install -y --no-install-recommends \
|
|
||||||
clang-format-13=1:13.0.1-11+b2 \
|
|
||||||
clang-tidy-14=1:14.0.6-12 \
|
|
||||||
patch=2.7.6-7 \
|
|
||||||
software-properties-common=0.99.30-4.1~deb12u1 \
|
|
||||||
nano=7.2-1+deb12u1 \
|
|
||||||
build-essential=12.9 \
|
|
||||||
python3-dev=3.11.2-1+b1 \
|
|
||||||
&& rm -rf \
|
|
||||||
/tmp/* \
|
|
||||||
/var/{cache,log}/* \
|
|
||||||
/var/lib/apt/lists/*
|
|
||||||
|
|
||||||
COPY requirements_test.txt /
|
|
||||||
RUN if [ "$TARGETARCH$TARGETVARIANT" = "armv7" ]; then \
|
|
||||||
export PIP_EXTRA_INDEX_URL="https://www.piwheels.org/simple"; \
|
|
||||||
fi; \
|
|
||||||
pip3 install \
|
|
||||||
--break-system-packages --no-cache-dir -r /requirements_test.txt
|
|
||||||
|
|
||||||
VOLUME ["/esphome"]
|
|
||||||
WORKDIR /esphome
|
|
||||||
|
|||||||
@@ -1,22 +1,19 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from dataclasses import dataclass
|
|
||||||
import subprocess
|
|
||||||
import argparse
|
import argparse
|
||||||
from platform import machine
|
from dataclasses import dataclass
|
||||||
import shlex
|
|
||||||
import re
|
import re
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
CHANNEL_DEV = "dev"
|
CHANNEL_DEV = "dev"
|
||||||
CHANNEL_BETA = "beta"
|
CHANNEL_BETA = "beta"
|
||||||
CHANNEL_RELEASE = "release"
|
CHANNEL_RELEASE = "release"
|
||||||
CHANNELS = [CHANNEL_DEV, CHANNEL_BETA, CHANNEL_RELEASE]
|
CHANNELS = [CHANNEL_DEV, CHANNEL_BETA, CHANNEL_RELEASE]
|
||||||
|
|
||||||
ARCH_AMD64 = "amd64"
|
ARCH_AMD64 = "amd64"
|
||||||
ARCH_ARMV7 = "armv7"
|
|
||||||
ARCH_AARCH64 = "aarch64"
|
ARCH_AARCH64 = "aarch64"
|
||||||
ARCHS = [ARCH_AMD64, ARCH_ARMV7, ARCH_AARCH64]
|
ARCHS = [ARCH_AMD64, ARCH_AARCH64]
|
||||||
|
|
||||||
TYPE_DOCKER = "docker"
|
TYPE_DOCKER = "docker"
|
||||||
TYPE_HA_ADDON = "ha-addon"
|
TYPE_HA_ADDON = "ha-addon"
|
||||||
@@ -57,7 +54,7 @@ manifest_parser = subparsers.add_parser(
|
|||||||
class DockerParams:
|
class DockerParams:
|
||||||
build_to: str
|
build_to: str
|
||||||
manifest_to: str
|
manifest_to: str
|
||||||
baseimgtype: str
|
build_type: str
|
||||||
platform: str
|
platform: str
|
||||||
target: str
|
target: str
|
||||||
|
|
||||||
@@ -69,25 +66,19 @@ class DockerParams:
|
|||||||
TYPE_LINT: "esphome/esphome-lint",
|
TYPE_LINT: "esphome/esphome-lint",
|
||||||
}[build_type]
|
}[build_type]
|
||||||
build_to = f"{prefix}-{arch}"
|
build_to = f"{prefix}-{arch}"
|
||||||
baseimgtype = {
|
|
||||||
TYPE_DOCKER: "docker",
|
|
||||||
TYPE_HA_ADDON: "hassio",
|
|
||||||
TYPE_LINT: "docker",
|
|
||||||
}[build_type]
|
|
||||||
platform = {
|
platform = {
|
||||||
ARCH_AMD64: "linux/amd64",
|
ARCH_AMD64: "linux/amd64",
|
||||||
ARCH_ARMV7: "linux/arm/v7",
|
|
||||||
ARCH_AARCH64: "linux/arm64",
|
ARCH_AARCH64: "linux/arm64",
|
||||||
}[arch]
|
}[arch]
|
||||||
target = {
|
target = {
|
||||||
TYPE_DOCKER: "docker",
|
TYPE_DOCKER: "final",
|
||||||
TYPE_HA_ADDON: "hassio",
|
TYPE_HA_ADDON: "final",
|
||||||
TYPE_LINT: "lint",
|
TYPE_LINT: "lint",
|
||||||
}[build_type]
|
}[build_type]
|
||||||
return cls(
|
return cls(
|
||||||
build_to=build_to,
|
build_to=build_to,
|
||||||
manifest_to=prefix,
|
manifest_to=prefix,
|
||||||
baseimgtype=baseimgtype,
|
build_type=build_type,
|
||||||
platform=platform,
|
platform=platform,
|
||||||
target=target,
|
target=target,
|
||||||
)
|
)
|
||||||
@@ -149,7 +140,7 @@ def main():
|
|||||||
"buildx",
|
"buildx",
|
||||||
"build",
|
"build",
|
||||||
"--build-arg",
|
"--build-arg",
|
||||||
f"BASEIMGTYPE={params.baseimgtype}",
|
f"BUILD_TYPE={params.build_type}",
|
||||||
"--build-arg",
|
"--build-arg",
|
||||||
f"BUILD_VERSION={args.tag}",
|
f"BUILD_VERSION={args.tag}",
|
||||||
"--cache-from",
|
"--cache-from",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import re
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import re
|
||||||
|
|
||||||
CHANNEL_DEV = "dev"
|
CHANNEL_DEV = "dev"
|
||||||
CHANNEL_BETA = "beta"
|
CHANNEL_BETA = "beta"
|
||||||
|
|||||||
@@ -23,10 +23,6 @@ if bashio::config.true 'streamer_mode'; then
|
|||||||
export ESPHOME_STREAMER_MODE=true
|
export ESPHOME_STREAMER_MODE=true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if bashio::config.true 'status_use_ping'; then
|
|
||||||
export ESPHOME_DASHBOARD_USE_PING=true
|
|
||||||
fi
|
|
||||||
|
|
||||||
if bashio::config.has_value 'relative_url'; then
|
if bashio::config.has_value 'relative_url'; then
|
||||||
export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
|
export ESPHOME_DASHBOARD_RELATIVE_URL=$(bashio::config 'relative_url')
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import argparse
|
import argparse
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import functools
|
import functools
|
||||||
|
import importlib
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -66,7 +67,7 @@ def choose_prompt(options, purpose: str = None):
|
|||||||
return options[0][1]
|
return options[0][1]
|
||||||
|
|
||||||
safe_print(
|
safe_print(
|
||||||
f'Found multiple options{f" for {purpose}" if purpose else ""}, please choose one:'
|
f"Found multiple options{f' for {purpose}' if purpose else ''}, please choose one:"
|
||||||
)
|
)
|
||||||
for i, (desc, _) in enumerate(options):
|
for i, (desc, _) in enumerate(options):
|
||||||
safe_print(f" [{i + 1}] {desc}")
|
safe_print(f" [{i + 1}] {desc}")
|
||||||
@@ -132,7 +133,7 @@ def get_port_type(port):
|
|||||||
return "NETWORK"
|
return "NETWORK"
|
||||||
|
|
||||||
|
|
||||||
def run_miniterm(config, port):
|
def run_miniterm(config, port, args):
|
||||||
import serial
|
import serial
|
||||||
|
|
||||||
from esphome import platformio_api
|
from esphome import platformio_api
|
||||||
@@ -153,7 +154,7 @@ def run_miniterm(config, port):
|
|||||||
|
|
||||||
# We can't set to False by default since it leads to toggling and hence
|
# We can't set to False by default since it leads to toggling and hence
|
||||||
# ESP32 resets on some platforms.
|
# ESP32 resets on some platforms.
|
||||||
if config["logger"][CONF_DEASSERT_RTS_DTR]:
|
if config["logger"][CONF_DEASSERT_RTS_DTR] or args.reset:
|
||||||
ser.dtr = False
|
ser.dtr = False
|
||||||
ser.rts = False
|
ser.rts = False
|
||||||
|
|
||||||
@@ -243,11 +244,11 @@ def compile_program(args, config):
|
|||||||
return 0 if idedata is not None else 1
|
return 0 if idedata is not None else 1
|
||||||
|
|
||||||
|
|
||||||
def upload_using_esptool(config, port, file):
|
def upload_using_esptool(config, port, file, speed):
|
||||||
from esphome import platformio_api
|
from esphome import platformio_api
|
||||||
|
|
||||||
first_baudrate = config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
|
first_baudrate = speed or config[CONF_ESPHOME][CONF_PLATFORMIO_OPTIONS].get(
|
||||||
"upload_speed", 460800
|
"upload_speed", os.getenv("ESPHOME_UPLOAD_SPEED", "460800")
|
||||||
)
|
)
|
||||||
|
|
||||||
if file is not None:
|
if file is not None:
|
||||||
@@ -336,11 +337,18 @@ def check_permissions(port):
|
|||||||
|
|
||||||
|
|
||||||
def upload_program(config, args, host):
|
def upload_program(config, args, host):
|
||||||
|
try:
|
||||||
|
module = importlib.import_module("esphome.components." + CORE.target_platform)
|
||||||
|
if getattr(module, "upload_program")(config, args, host):
|
||||||
|
return 0
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
if get_port_type(host) == "SERIAL":
|
if get_port_type(host) == "SERIAL":
|
||||||
check_permissions(host)
|
check_permissions(host)
|
||||||
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
|
if CORE.target_platform in (PLATFORM_ESP32, PLATFORM_ESP8266):
|
||||||
file = getattr(args, "file", None)
|
file = getattr(args, "file", None)
|
||||||
return upload_using_esptool(config, host, file)
|
return upload_using_esptool(config, host, file, args.upload_speed)
|
||||||
|
|
||||||
if CORE.target_platform in (PLATFORM_RP2040):
|
if CORE.target_platform in (PLATFORM_RP2040):
|
||||||
return upload_using_platformio(config, args.device)
|
return upload_using_platformio(config, args.device)
|
||||||
@@ -367,10 +375,12 @@ def upload_program(config, args, host):
|
|||||||
password = ota_conf.get(CONF_PASSWORD, "")
|
password = ota_conf.get(CONF_PASSWORD, "")
|
||||||
|
|
||||||
if (
|
if (
|
||||||
not is_ip_address(CORE.address) # pylint: disable=too-many-boolean-expressions
|
CONF_MQTT in config # pylint: disable=too-many-boolean-expressions
|
||||||
and (get_port_type(host) == "MQTT" or config[CONF_MDNS][CONF_DISABLED])
|
|
||||||
and CONF_MQTT in config
|
|
||||||
and (not args.device or args.device in ("MQTT", "OTA"))
|
and (not args.device or args.device in ("MQTT", "OTA"))
|
||||||
|
and (
|
||||||
|
((config[CONF_MDNS][CONF_DISABLED]) and not is_ip_address(CORE.address))
|
||||||
|
or get_port_type(host) == "MQTT"
|
||||||
|
)
|
||||||
):
|
):
|
||||||
from esphome import mqtt
|
from esphome import mqtt
|
||||||
|
|
||||||
@@ -389,7 +399,7 @@ def show_logs(config, args, port):
|
|||||||
raise EsphomeError("Logger is not configured!")
|
raise EsphomeError("Logger is not configured!")
|
||||||
if get_port_type(port) == "SERIAL":
|
if get_port_type(port) == "SERIAL":
|
||||||
check_permissions(port)
|
check_permissions(port)
|
||||||
return run_miniterm(config, port)
|
return run_miniterm(config, port, args)
|
||||||
if get_port_type(port) == "NETWORK" and "api" in config:
|
if get_port_type(port) == "NETWORK" and "api" in config:
|
||||||
if config[CONF_MDNS][CONF_DISABLED] and CONF_MQTT in config:
|
if config[CONF_MDNS][CONF_DISABLED] and CONF_MQTT in config:
|
||||||
from esphome import mqtt
|
from esphome import mqtt
|
||||||
@@ -758,6 +768,14 @@ def parse_args(argv):
|
|||||||
options_parser.add_argument(
|
options_parser.add_argument(
|
||||||
"-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
|
"-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
|
||||||
)
|
)
|
||||||
|
options_parser.add_argument(
|
||||||
|
"-l",
|
||||||
|
"--log-level",
|
||||||
|
help="Set the log level.",
|
||||||
|
default=os.getenv("ESPHOME_LOG_LEVEL", "INFO"),
|
||||||
|
action="store",
|
||||||
|
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
||||||
|
)
|
||||||
options_parser.add_argument(
|
options_parser.add_argument(
|
||||||
"--dashboard", help=argparse.SUPPRESS, action="store_true"
|
"--dashboard", help=argparse.SUPPRESS, action="store_true"
|
||||||
)
|
)
|
||||||
@@ -826,6 +844,10 @@ def parse_args(argv):
|
|||||||
"--device",
|
"--device",
|
||||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||||
)
|
)
|
||||||
|
parser_upload.add_argument(
|
||||||
|
"--upload_speed",
|
||||||
|
help="Override the default or configured upload speed.",
|
||||||
|
)
|
||||||
parser_upload.add_argument(
|
parser_upload.add_argument(
|
||||||
"--file",
|
"--file",
|
||||||
help="Manually specify the binary file to upload.",
|
help="Manually specify the binary file to upload.",
|
||||||
@@ -844,6 +866,13 @@ def parse_args(argv):
|
|||||||
"--device",
|
"--device",
|
||||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||||
)
|
)
|
||||||
|
parser_logs.add_argument(
|
||||||
|
"--reset",
|
||||||
|
"-r",
|
||||||
|
action="store_true",
|
||||||
|
help="Reset the device before starting serial logs.",
|
||||||
|
default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"),
|
||||||
|
)
|
||||||
|
|
||||||
parser_discover = subparsers.add_parser(
|
parser_discover = subparsers.add_parser(
|
||||||
"discover",
|
"discover",
|
||||||
@@ -866,9 +895,20 @@ def parse_args(argv):
|
|||||||
"--device",
|
"--device",
|
||||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||||
)
|
)
|
||||||
|
parser_run.add_argument(
|
||||||
|
"--upload_speed",
|
||||||
|
help="Override the default or configured upload speed.",
|
||||||
|
)
|
||||||
parser_run.add_argument(
|
parser_run.add_argument(
|
||||||
"--no-logs", help="Disable starting logs.", action="store_true"
|
"--no-logs", help="Disable starting logs.", action="store_true"
|
||||||
)
|
)
|
||||||
|
parser_run.add_argument(
|
||||||
|
"--reset",
|
||||||
|
"-r",
|
||||||
|
action="store_true",
|
||||||
|
help="Reset the device before starting serial logs.",
|
||||||
|
default=os.getenv("ESPHOME_SERIAL_LOGGING_RESET"),
|
||||||
|
)
|
||||||
|
|
||||||
parser_clean = subparsers.add_parser(
|
parser_clean = subparsers.add_parser(
|
||||||
"clean-mqtt",
|
"clean-mqtt",
|
||||||
@@ -987,11 +1027,16 @@ def run_esphome(argv):
|
|||||||
args = parse_args(argv)
|
args = parse_args(argv)
|
||||||
CORE.dashboard = args.dashboard
|
CORE.dashboard = args.dashboard
|
||||||
|
|
||||||
|
# Override log level if verbose is set
|
||||||
|
if args.verbose:
|
||||||
|
args.log_level = "DEBUG"
|
||||||
|
elif args.quiet:
|
||||||
|
args.log_level = "CRITICAL"
|
||||||
|
|
||||||
setup_log(
|
setup_log(
|
||||||
args.verbose,
|
log_level=args.log_level,
|
||||||
args.quiet,
|
|
||||||
# Show timestamp for dashboard access logs
|
# Show timestamp for dashboard access logs
|
||||||
args.command == "dashboard",
|
include_timestamp=args.command == "dashboard",
|
||||||
)
|
)
|
||||||
|
|
||||||
if args.command in PRE_CONFIG_ACTIONS:
|
if args.command in PRE_CONFIG_ACTIONS:
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import sensor, uart
|
from esphome.components import sensor, uart
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
DEVICE_CLASS_DISTANCE,
|
||||||
|
ICON_ARROW_EXPAND_VERTICAL,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_METER,
|
UNIT_METER,
|
||||||
ICON_ARROW_EXPAND_VERTICAL,
|
|
||||||
DEVICE_CLASS_DISTANCE,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
CODEOWNERS = ["@MrSuicideParrot"]
|
CODEOWNERS = ["@MrSuicideParrot"]
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import sensor, uart
|
from esphome.components import sensor, uart
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
ICON_ARROW_EXPAND_VERTICAL,
|
|
||||||
DEVICE_CLASS_DISTANCE,
|
DEVICE_CLASS_DISTANCE,
|
||||||
|
ICON_ARROW_EXPAND_VERTICAL,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_MILLIMETER,
|
UNIT_MILLIMETER,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
from esphome import pins
|
from esphome import pins
|
||||||
|
import esphome.codegen as cg
|
||||||
from esphome.components import stepper
|
from esphome.components import stepper
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
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)
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import sensor
|
from esphome.components import sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_EQUATION,
|
||||||
CONF_HUMIDITY,
|
CONF_HUMIDITY,
|
||||||
CONF_TEMPERATURE,
|
CONF_TEMPERATURE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
CONF_EQUATION,
|
|
||||||
ICON_WATER,
|
ICON_WATER,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_GRAMS_PER_CUBIC_METER,
|
UNIT_GRAMS_PER_CUBIC_METER,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -114,13 +114,14 @@ void IRAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
|||||||
// fully off, disable output immediately
|
// fully off, disable output immediately
|
||||||
this->gate_pin.digital_write(false);
|
this->gate_pin.digital_write(false);
|
||||||
} else {
|
} else {
|
||||||
|
auto min_us = this->cycle_time_us * this->min_power / 1000;
|
||||||
if (this->method == DIM_METHOD_TRAILING) {
|
if (this->method == DIM_METHOD_TRAILING) {
|
||||||
this->enable_time_us = 1; // cannot be 0
|
this->enable_time_us = 1; // cannot be 0
|
||||||
this->disable_time_us = std::max((uint32_t) 10, this->value * this->cycle_time_us / 65535);
|
// calculate time until disable in µs with integer arithmetic and take into account min_power
|
||||||
|
this->disable_time_us = std::max((uint32_t) 10, this->value * (this->cycle_time_us - min_us) / 65535 + min_us);
|
||||||
} else {
|
} else {
|
||||||
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
|
// calculate time until enable in µs: (1.0-value)*cycle_time, but with integer arithmetic
|
||||||
// also take into account min_power
|
// also take into account min_power
|
||||||
auto min_us = this->cycle_time_us * this->min_power / 1000;
|
|
||||||
this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
|
this->enable_time_us = std::max((uint32_t) 1, ((65535 - this->value) * (this->cycle_time_us - min_us)) / 65535);
|
||||||
|
|
||||||
if (this->method == DIM_METHOD_LEADING_PULSE) {
|
if (this->method == DIM_METHOD_LEADING_PULSE) {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
|
import esphome.codegen as cg
|
||||||
from esphome.components import output
|
from esphome.components import output
|
||||||
from esphome.const import CONF_ID, CONF_MIN_POWER, CONF_METHOD
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import CONF_ID, CONF_METHOD, CONF_MIN_POWER
|
||||||
|
|
||||||
CODEOWNERS = ["@glmnet"]
|
CODEOWNERS = ["@glmnet"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import uart
|
from esphome.components import uart
|
||||||
from esphome.components.light.types import AddressableLightEffect
|
|
||||||
from esphome.components.light.effects import register_addressable_effect
|
from esphome.components.light.effects import register_addressable_effect
|
||||||
|
from esphome.components.light.types import AddressableLightEffect
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_NAME, CONF_UART_ID
|
from esphome.const import CONF_NAME, CONF_UART_ID
|
||||||
|
|
||||||
DEPENDENCIES = ["uart"]
|
DEPENDENCIES = ["uart"]
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER
|
import esphome.codegen as cg
|
||||||
|
|
||||||
from esphome.core import CORE
|
|
||||||
from esphome.components.esp32 import get_esp32_variant
|
from esphome.components.esp32 import get_esp32_variant
|
||||||
from esphome.const import PLATFORM_ESP8266
|
|
||||||
from esphome.components.esp32.const import (
|
from esphome.components.esp32.const import (
|
||||||
VARIANT_ESP32,
|
VARIANT_ESP32,
|
||||||
VARIANT_ESP32C2,
|
VARIANT_ESP32C2,
|
||||||
@@ -15,6 +10,9 @@ from esphome.components.esp32.const import (
|
|||||||
VARIANT_ESP32S2,
|
VARIANT_ESP32S2,
|
||||||
VARIANT_ESP32S3,
|
VARIANT_ESP32S3,
|
||||||
)
|
)
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import CONF_ANALOG, CONF_INPUT, CONF_NUMBER, PLATFORM_ESP8266
|
||||||
|
from esphome.core import CORE
|
||||||
|
|
||||||
CODEOWNERS = ["@esphome/core"]
|
CODEOWNERS = ["@esphome/core"]
|
||||||
|
|
||||||
@@ -38,12 +36,21 @@ ATTENUATION_MODES = {
|
|||||||
"auto": "auto",
|
"auto": "auto",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sampling_mode = adc_ns.enum("SamplingMode", is_class=True)
|
||||||
|
|
||||||
|
SAMPLING_MODES = {
|
||||||
|
"avg": sampling_mode.AVG,
|
||||||
|
"min": sampling_mode.MIN,
|
||||||
|
"max": sampling_mode.MAX,
|
||||||
|
}
|
||||||
|
|
||||||
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
|
adc1_channel_t = cg.global_ns.enum("adc1_channel_t")
|
||||||
adc2_channel_t = cg.global_ns.enum("adc2_channel_t")
|
adc2_channel_t = cg.global_ns.enum("adc2_channel_t")
|
||||||
|
|
||||||
# From https://github.com/espressif/esp-idf/blob/master/components/driver/include/driver/adc_common.h
|
|
||||||
# pin to adc1 channel mapping
|
# pin to adc1 channel mapping
|
||||||
|
# https://github.com/espressif/esp-idf/blob/v4.4.8/components/driver/include/driver/adc.h
|
||||||
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
|
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/adc_channel.h
|
||||||
VARIANT_ESP32: {
|
VARIANT_ESP32: {
|
||||||
36: adc1_channel_t.ADC1_CHANNEL_0,
|
36: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
37: adc1_channel_t.ADC1_CHANNEL_1,
|
37: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
@@ -54,6 +61,41 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
|
|||||||
34: adc1_channel_t.ADC1_CHANNEL_6,
|
34: adc1_channel_t.ADC1_CHANNEL_6,
|
||||||
35: adc1_channel_t.ADC1_CHANNEL_7,
|
35: adc1_channel_t.ADC1_CHANNEL_7,
|
||||||
},
|
},
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c2/include/soc/adc_channel.h
|
||||||
|
VARIANT_ESP32C2: {
|
||||||
|
0: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
1: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
2: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
3: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
4: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
},
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c3/include/soc/adc_channel.h
|
||||||
|
VARIANT_ESP32C3: {
|
||||||
|
0: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
1: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
2: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
3: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
4: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
},
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c6/include/soc/adc_channel.h
|
||||||
|
VARIANT_ESP32C6: {
|
||||||
|
0: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
1: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
2: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
3: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
4: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
5: adc1_channel_t.ADC1_CHANNEL_5,
|
||||||
|
6: adc1_channel_t.ADC1_CHANNEL_6,
|
||||||
|
},
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32h2/include/soc/adc_channel.h
|
||||||
|
VARIANT_ESP32H2: {
|
||||||
|
1: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
|
2: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
|
3: adc1_channel_t.ADC1_CHANNEL_2,
|
||||||
|
4: adc1_channel_t.ADC1_CHANNEL_3,
|
||||||
|
5: adc1_channel_t.ADC1_CHANNEL_4,
|
||||||
|
},
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s2/include/soc/adc_channel.h
|
||||||
VARIANT_ESP32S2: {
|
VARIANT_ESP32S2: {
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_0,
|
1: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
2: adc1_channel_t.ADC1_CHANNEL_1,
|
2: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
@@ -66,6 +108,7 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
|
|||||||
9: adc1_channel_t.ADC1_CHANNEL_8,
|
9: adc1_channel_t.ADC1_CHANNEL_8,
|
||||||
10: adc1_channel_t.ADC1_CHANNEL_9,
|
10: adc1_channel_t.ADC1_CHANNEL_9,
|
||||||
},
|
},
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/adc_channel.h
|
||||||
VARIANT_ESP32S3: {
|
VARIANT_ESP32S3: {
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_0,
|
1: adc1_channel_t.ADC1_CHANNEL_0,
|
||||||
2: adc1_channel_t.ADC1_CHANNEL_1,
|
2: adc1_channel_t.ADC1_CHANNEL_1,
|
||||||
@@ -78,40 +121,12 @@ ESP32_VARIANT_ADC1_PIN_TO_CHANNEL = {
|
|||||||
9: adc1_channel_t.ADC1_CHANNEL_8,
|
9: adc1_channel_t.ADC1_CHANNEL_8,
|
||||||
10: adc1_channel_t.ADC1_CHANNEL_9,
|
10: adc1_channel_t.ADC1_CHANNEL_9,
|
||||||
},
|
},
|
||||||
VARIANT_ESP32C3: {
|
|
||||||
0: adc1_channel_t.ADC1_CHANNEL_0,
|
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_1,
|
|
||||||
2: adc1_channel_t.ADC1_CHANNEL_2,
|
|
||||||
3: adc1_channel_t.ADC1_CHANNEL_3,
|
|
||||||
4: adc1_channel_t.ADC1_CHANNEL_4,
|
|
||||||
},
|
|
||||||
VARIANT_ESP32C2: {
|
|
||||||
0: adc1_channel_t.ADC1_CHANNEL_0,
|
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_1,
|
|
||||||
2: adc1_channel_t.ADC1_CHANNEL_2,
|
|
||||||
3: adc1_channel_t.ADC1_CHANNEL_3,
|
|
||||||
4: adc1_channel_t.ADC1_CHANNEL_4,
|
|
||||||
},
|
|
||||||
VARIANT_ESP32C6: {
|
|
||||||
0: adc1_channel_t.ADC1_CHANNEL_0,
|
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_1,
|
|
||||||
2: adc1_channel_t.ADC1_CHANNEL_2,
|
|
||||||
3: adc1_channel_t.ADC1_CHANNEL_3,
|
|
||||||
4: adc1_channel_t.ADC1_CHANNEL_4,
|
|
||||||
5: adc1_channel_t.ADC1_CHANNEL_5,
|
|
||||||
6: adc1_channel_t.ADC1_CHANNEL_6,
|
|
||||||
},
|
|
||||||
VARIANT_ESP32H2: {
|
|
||||||
0: adc1_channel_t.ADC1_CHANNEL_0,
|
|
||||||
1: adc1_channel_t.ADC1_CHANNEL_1,
|
|
||||||
2: adc1_channel_t.ADC1_CHANNEL_2,
|
|
||||||
3: adc1_channel_t.ADC1_CHANNEL_3,
|
|
||||||
4: adc1_channel_t.ADC1_CHANNEL_4,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# pin to adc2 channel mapping
|
||||||
|
# https://github.com/espressif/esp-idf/blob/v4.4.8/components/driver/include/driver/adc.h
|
||||||
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
|
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
|
||||||
# TODO: add other variants
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32/include/soc/adc_channel.h
|
||||||
VARIANT_ESP32: {
|
VARIANT_ESP32: {
|
||||||
4: adc2_channel_t.ADC2_CHANNEL_0,
|
4: adc2_channel_t.ADC2_CHANNEL_0,
|
||||||
0: adc2_channel_t.ADC2_CHANNEL_1,
|
0: adc2_channel_t.ADC2_CHANNEL_1,
|
||||||
@@ -124,6 +139,19 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
|
|||||||
25: adc2_channel_t.ADC2_CHANNEL_8,
|
25: adc2_channel_t.ADC2_CHANNEL_8,
|
||||||
26: adc2_channel_t.ADC2_CHANNEL_9,
|
26: adc2_channel_t.ADC2_CHANNEL_9,
|
||||||
},
|
},
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c2/include/soc/adc_channel.h
|
||||||
|
VARIANT_ESP32C2: {
|
||||||
|
5: adc2_channel_t.ADC2_CHANNEL_0,
|
||||||
|
},
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c3/include/soc/adc_channel.h
|
||||||
|
VARIANT_ESP32C3: {
|
||||||
|
5: adc2_channel_t.ADC2_CHANNEL_0,
|
||||||
|
},
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32c6/include/soc/adc_channel.h
|
||||||
|
VARIANT_ESP32C6: {}, # no ADC2
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32h2/include/soc/adc_channel.h
|
||||||
|
VARIANT_ESP32H2: {}, # no ADC2
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s2/include/soc/adc_channel.h
|
||||||
VARIANT_ESP32S2: {
|
VARIANT_ESP32S2: {
|
||||||
11: adc2_channel_t.ADC2_CHANNEL_0,
|
11: adc2_channel_t.ADC2_CHANNEL_0,
|
||||||
12: adc2_channel_t.ADC2_CHANNEL_1,
|
12: adc2_channel_t.ADC2_CHANNEL_1,
|
||||||
@@ -136,6 +164,7 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
|
|||||||
19: adc2_channel_t.ADC2_CHANNEL_8,
|
19: adc2_channel_t.ADC2_CHANNEL_8,
|
||||||
20: adc2_channel_t.ADC2_CHANNEL_9,
|
20: adc2_channel_t.ADC2_CHANNEL_9,
|
||||||
},
|
},
|
||||||
|
# https://github.com/espressif/esp-idf/blob/master/components/soc/esp32s3/include/soc/adc_channel.h
|
||||||
VARIANT_ESP32S3: {
|
VARIANT_ESP32S3: {
|
||||||
11: adc2_channel_t.ADC2_CHANNEL_0,
|
11: adc2_channel_t.ADC2_CHANNEL_0,
|
||||||
12: adc2_channel_t.ADC2_CHANNEL_1,
|
12: adc2_channel_t.ADC2_CHANNEL_1,
|
||||||
@@ -148,12 +177,6 @@ ESP32_VARIANT_ADC2_PIN_TO_CHANNEL = {
|
|||||||
19: adc2_channel_t.ADC2_CHANNEL_8,
|
19: adc2_channel_t.ADC2_CHANNEL_8,
|
||||||
20: adc2_channel_t.ADC2_CHANNEL_9,
|
20: adc2_channel_t.ADC2_CHANNEL_9,
|
||||||
},
|
},
|
||||||
VARIANT_ESP32C3: {
|
|
||||||
5: adc2_channel_t.ADC2_CHANNEL_0,
|
|
||||||
},
|
|
||||||
VARIANT_ESP32C2: {},
|
|
||||||
VARIANT_ESP32C6: {},
|
|
||||||
VARIANT_ESP32H2: {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,12 @@
|
|||||||
#include "esphome/components/sensor/sensor.h"
|
#include "esphome/components/sensor/sensor.h"
|
||||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/defines.h"
|
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
#include <esp_adc_cal.h>
|
#include <esp_adc_cal.h>
|
||||||
#include "driver/adc.h"
|
#include "driver/adc.h"
|
||||||
#endif
|
#endif // USE_ESP32
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace adc {
|
namespace adc {
|
||||||
@@ -29,6 +28,21 @@ static const adc_atten_t ADC_ATTEN_DB_12_COMPAT = ADC_ATTEN_DB_11;
|
|||||||
#endif
|
#endif
|
||||||
#endif // USE_ESP32
|
#endif // USE_ESP32
|
||||||
|
|
||||||
|
enum class SamplingMode : uint8_t { AVG = 0, MIN = 1, MAX = 2 };
|
||||||
|
const LogString *sampling_mode_to_str(SamplingMode mode);
|
||||||
|
|
||||||
|
class Aggregator {
|
||||||
|
public:
|
||||||
|
void add_sample(uint32_t value);
|
||||||
|
uint32_t aggregate();
|
||||||
|
Aggregator(SamplingMode mode);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
SamplingMode mode_{SamplingMode::AVG};
|
||||||
|
uint32_t aggr_{0};
|
||||||
|
uint32_t samples_{0};
|
||||||
|
};
|
||||||
|
|
||||||
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage_sampler::VoltageSampler {
|
||||||
public:
|
public:
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
@@ -43,7 +57,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||||||
this->channel1_ = ADC1_CHANNEL_MAX;
|
this->channel1_ = ADC1_CHANNEL_MAX;
|
||||||
}
|
}
|
||||||
void set_autorange(bool autorange) { this->autorange_ = autorange; }
|
void set_autorange(bool autorange) { this->autorange_ = autorange; }
|
||||||
#endif
|
#endif // USE_ESP32
|
||||||
|
|
||||||
/// Update ADC values
|
/// Update ADC values
|
||||||
void update() override;
|
void update() override;
|
||||||
@@ -55,24 +69,26 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||||||
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
|
void set_pin(InternalGPIOPin *pin) { this->pin_ = pin; }
|
||||||
void set_output_raw(bool output_raw) { this->output_raw_ = output_raw; }
|
void set_output_raw(bool output_raw) { this->output_raw_ = output_raw; }
|
||||||
void set_sample_count(uint8_t sample_count);
|
void set_sample_count(uint8_t sample_count);
|
||||||
|
void set_sampling_mode(SamplingMode sampling_mode);
|
||||||
float sample() override;
|
float sample() override;
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
#ifdef USE_ESP8266
|
||||||
std::string unique_id() override;
|
std::string unique_id() override;
|
||||||
#endif
|
#endif // USE_ESP8266
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
#ifdef USE_RP2040
|
||||||
void set_is_temperature() { this->is_temperature_ = true; }
|
void set_is_temperature() { this->is_temperature_ = true; }
|
||||||
#endif
|
#endif // USE_RP2040
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
InternalGPIOPin *pin_;
|
InternalGPIOPin *pin_;
|
||||||
bool output_raw_{false};
|
bool output_raw_{false};
|
||||||
uint8_t sample_count_{1};
|
uint8_t sample_count_{1};
|
||||||
|
SamplingMode sampling_mode_{SamplingMode::AVG};
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
#ifdef USE_RP2040
|
||||||
bool is_temperature_{false};
|
bool is_temperature_{false};
|
||||||
#endif
|
#endif // USE_RP2040
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
#ifdef USE_ESP32
|
||||||
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
||||||
@@ -83,8 +99,8 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
|||||||
esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {};
|
esp_adc_cal_characteristics_t cal_characteristics_[SOC_ADC_ATTEN_NUM] = {};
|
||||||
#else
|
#else
|
||||||
esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {};
|
esp_adc_cal_characteristics_t cal_characteristics_[ADC_ATTEN_MAX] = {};
|
||||||
#endif
|
#endif // ESP_IDF_VERSION_MAJOR
|
||||||
#endif
|
#endif // USE_ESP32
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace adc
|
} // namespace adc
|
||||||
|
|||||||
79
esphome/components/adc/adc_sensor_common.cpp
Normal file
79
esphome/components/adc/adc_sensor_common.cpp
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#include "adc_sensor.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace adc {
|
||||||
|
|
||||||
|
static const char *const TAG = "adc.common";
|
||||||
|
|
||||||
|
const LogString *sampling_mode_to_str(SamplingMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case SamplingMode::AVG:
|
||||||
|
return LOG_STR("average");
|
||||||
|
case SamplingMode::MIN:
|
||||||
|
return LOG_STR("minimum");
|
||||||
|
case SamplingMode::MAX:
|
||||||
|
return LOG_STR("maximum");
|
||||||
|
}
|
||||||
|
return LOG_STR("unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
Aggregator::Aggregator(SamplingMode mode) {
|
||||||
|
this->mode_ = mode;
|
||||||
|
// set to max uint if mode is "min"
|
||||||
|
if (mode == SamplingMode::MIN) {
|
||||||
|
this->aggr_ = UINT32_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Aggregator::add_sample(uint32_t value) {
|
||||||
|
this->samples_ += 1;
|
||||||
|
|
||||||
|
switch (this->mode_) {
|
||||||
|
case SamplingMode::AVG:
|
||||||
|
this->aggr_ += value;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SamplingMode::MIN:
|
||||||
|
if (value < this->aggr_) {
|
||||||
|
this->aggr_ = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SamplingMode::MAX:
|
||||||
|
if (value > this->aggr_) {
|
||||||
|
this->aggr_ = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Aggregator::aggregate() {
|
||||||
|
if (this->mode_ == SamplingMode::AVG) {
|
||||||
|
if (this->samples_ == 0) {
|
||||||
|
return this->aggr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this->aggr_ + (this->samples_ >> 1)) / this->samples_; // NOLINT(clang-analyzer-core.DivideZero)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->aggr_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADCSensor::update() {
|
||||||
|
float value_v = this->sample();
|
||||||
|
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
|
||||||
|
this->publish_state(value_v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADCSensor::set_sample_count(uint8_t sample_count) {
|
||||||
|
if (sample_count != 0) {
|
||||||
|
this->sample_count_ = sample_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADCSensor::set_sampling_mode(SamplingMode sampling_mode) { this->sampling_mode_ = sampling_mode; }
|
||||||
|
|
||||||
|
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
|
} // namespace adc
|
||||||
|
} // namespace esphome
|
||||||
@@ -1,30 +1,13 @@
|
|||||||
|
#ifdef USE_ESP32
|
||||||
|
|
||||||
#include "adc_sensor.h"
|
#include "adc_sensor.h"
|
||||||
#include "esphome/core/helpers.h"
|
|
||||||
#include "esphome/core/log.h"
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
|
||||||
#include <Esp.h>
|
|
||||||
ADC_MODE(ADC_VCC)
|
|
||||||
#else
|
|
||||||
#include <Arduino.h>
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
|
||||||
#ifdef CYW43_USES_VSYS_PIN
|
|
||||||
#include "pico/cyw43_arch.h"
|
|
||||||
#endif
|
|
||||||
#include <hardware/adc.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace adc {
|
namespace adc {
|
||||||
|
|
||||||
static const char *const TAG = "adc";
|
static const char *const TAG = "adc.esp32";
|
||||||
|
|
||||||
// 13-bit for S2, 12-bit for all other ESP32 variants
|
|
||||||
#ifdef USE_ESP32
|
|
||||||
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
|
static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_width_t>(ADC_WIDTH_MAX - 1);
|
||||||
|
|
||||||
#ifndef SOC_ADC_RTC_MAX_BITWIDTH
|
#ifndef SOC_ADC_RTC_MAX_BITWIDTH
|
||||||
@@ -32,24 +15,15 @@ static const adc_bits_width_t ADC_WIDTH_MAX_SOC_BITS = static_cast<adc_bits_widt
|
|||||||
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13;
|
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 13;
|
||||||
#else
|
#else
|
||||||
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12;
|
static const int32_t SOC_ADC_RTC_MAX_BITWIDTH = 12;
|
||||||
#endif
|
#endif // USE_ESP32_VARIANT_ESP32S2
|
||||||
#endif
|
#endif // SOC_ADC_RTC_MAX_BITWIDTH
|
||||||
|
|
||||||
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1; // 4095 (12 bit) or 8191 (13 bit)
|
static const int ADC_MAX = (1 << SOC_ADC_RTC_MAX_BITWIDTH) - 1;
|
||||||
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1; // 2048 (12 bit) or 4096 (13 bit)
|
static const int ADC_HALF = (1 << SOC_ADC_RTC_MAX_BITWIDTH) >> 1;
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
void ADCSensor::setup() {
|
||||||
extern "C"
|
|
||||||
#endif
|
|
||||||
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());
|
||||||
#if !defined(USE_ADC_SENSOR_VCC) && !defined(USE_RP2040)
|
|
||||||
this->pin_->setup();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
|
||||||
if (this->channel1_ != ADC1_CHANNEL_MAX) {
|
if (this->channel1_ != ADC1_CHANNEL_MAX) {
|
||||||
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
|
adc1_config_width(ADC_WIDTH_MAX_SOC_BITS);
|
||||||
if (!this->autorange_) {
|
if (!this->autorange_) {
|
||||||
@@ -61,7 +35,6 @@ extern "C"
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// load characteristics for each attenuation
|
|
||||||
for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) {
|
for (int32_t i = 0; i <= ADC_ATTEN_DB_12_COMPAT; i++) {
|
||||||
auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
|
auto adc_unit = this->channel1_ != ADC1_CHANNEL_MAX ? ADC_UNIT_1 : ADC_UNIT_2;
|
||||||
auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
|
auto cal_value = esp_adc_cal_characterize(adc_unit, (adc_atten_t) i, ADC_WIDTH_MAX_SOC_BITS,
|
||||||
@@ -79,31 +52,10 @@ extern "C"
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // USE_ESP32
|
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
|
||||||
static bool initialized = false;
|
|
||||||
if (!initialized) {
|
|
||||||
adc_init();
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ESP_LOGCONFIG(TAG, "ADC '%s' setup finished!", this->get_name().c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADCSensor::dump_config() {
|
void ADCSensor::dump_config() {
|
||||||
LOG_SENSOR("", "ADC Sensor", this);
|
LOG_SENSOR("", "ADC Sensor", this);
|
||||||
#if defined(USE_ESP8266) || defined(USE_LIBRETINY)
|
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
|
||||||
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
|
||||||
#else
|
|
||||||
LOG_PIN(" Pin: ", this->pin_);
|
|
||||||
#endif
|
|
||||||
#endif // USE_ESP8266 || USE_LIBRETINY
|
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
|
||||||
LOG_PIN(" Pin: ", this->pin_);
|
LOG_PIN(" Pin: ", this->pin_);
|
||||||
if (this->autorange_) {
|
if (this->autorange_) {
|
||||||
ESP_LOGCONFIG(TAG, " Attenuation: auto");
|
ESP_LOGCONFIG(TAG, " Attenuation: auto");
|
||||||
@@ -125,58 +77,15 @@ void ADCSensor::dump_config() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // USE_ESP32
|
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
|
||||||
if (this->is_temperature_) {
|
|
||||||
ESP_LOGCONFIG(TAG, " Pin: Temperature");
|
|
||||||
} else {
|
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
|
||||||
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
|
||||||
#else
|
|
||||||
LOG_PIN(" Pin: ", this->pin_);
|
|
||||||
#endif // USE_ADC_SENSOR_VCC
|
|
||||||
}
|
|
||||||
#endif // USE_RP2040
|
|
||||||
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
|
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
|
||||||
LOG_UPDATE_INTERVAL(this);
|
LOG_UPDATE_INTERVAL(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
float ADCSensor::get_setup_priority() const { return setup_priority::DATA; }
|
|
||||||
void ADCSensor::update() {
|
|
||||||
float value_v = this->sample();
|
|
||||||
ESP_LOGV(TAG, "'%s': Got voltage=%.4fV", this->get_name().c_str(), value_v);
|
|
||||||
this->publish_state(value_v);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ADCSensor::set_sample_count(uint8_t sample_count) {
|
|
||||||
if (sample_count != 0) {
|
|
||||||
this->sample_count_ = sample_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
|
||||||
float ADCSensor::sample() {
|
|
||||||
uint32_t raw = 0;
|
|
||||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
|
||||||
#ifdef USE_ADC_SENSOR_VCC
|
|
||||||
raw += ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
|
|
||||||
#else
|
|
||||||
raw += analogRead(this->pin_->get_pin()); // NOLINT
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
|
|
||||||
if (this->output_raw_) {
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
return raw / 1024.0f;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_ESP32
|
|
||||||
float ADCSensor::sample() {
|
float ADCSensor::sample() {
|
||||||
if (!this->autorange_) {
|
if (!this->autorange_) {
|
||||||
uint32_t sum = 0;
|
auto aggr = Aggregator(this->sampling_mode_);
|
||||||
|
|
||||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
||||||
int raw = -1;
|
int raw = -1;
|
||||||
if (this->channel1_ != ADC1_CHANNEL_MAX) {
|
if (this->channel1_ != ADC1_CHANNEL_MAX) {
|
||||||
@@ -187,13 +96,14 @@ float ADCSensor::sample() {
|
|||||||
if (raw == -1) {
|
if (raw == -1) {
|
||||||
return NAN;
|
return NAN;
|
||||||
}
|
}
|
||||||
sum += raw;
|
|
||||||
|
aggr.add_sample(raw);
|
||||||
}
|
}
|
||||||
sum = (sum + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
|
|
||||||
if (this->output_raw_) {
|
if (this->output_raw_) {
|
||||||
return sum;
|
return aggr.aggregate();
|
||||||
}
|
}
|
||||||
uint32_t mv = esp_adc_cal_raw_to_voltage(sum, &this->cal_characteristics_[(int32_t) this->attenuation_]);
|
uint32_t mv =
|
||||||
|
esp_adc_cal_raw_to_voltage(aggr.aggregate(), &this->cal_characteristics_[(int32_t) this->attenuation_]);
|
||||||
return mv / 1000.0f;
|
return mv / 1000.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,93 +150,17 @@ float ADCSensor::sample() {
|
|||||||
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
|
uint32_t mv2 = esp_adc_cal_raw_to_voltage(raw2, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_2_5]);
|
||||||
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
|
uint32_t mv0 = esp_adc_cal_raw_to_voltage(raw0, &this->cal_characteristics_[(int32_t) ADC_ATTEN_DB_0]);
|
||||||
|
|
||||||
// Contribution of each value, in range 0-2048 (12 bit ADC) or 0-4096 (13 bit ADC)
|
|
||||||
uint32_t c12 = std::min(raw12, ADC_HALF);
|
uint32_t c12 = std::min(raw12, ADC_HALF);
|
||||||
uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
|
uint32_t c6 = ADC_HALF - std::abs(raw6 - ADC_HALF);
|
||||||
uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
|
uint32_t c2 = ADC_HALF - std::abs(raw2 - ADC_HALF);
|
||||||
uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
|
uint32_t c0 = std::min(ADC_MAX - raw0, ADC_HALF);
|
||||||
// max theoretical csum value is 4096*4 = 16384
|
|
||||||
uint32_t csum = c12 + c6 + c2 + c0;
|
uint32_t csum = c12 + c6 + c2 + c0;
|
||||||
|
|
||||||
// each mv is max 3900; so max value is 3900*4096*4, fits in unsigned32
|
|
||||||
uint32_t mv_scaled = (mv12 * c12) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
|
uint32_t mv_scaled = (mv12 * c12) + (mv6 * c6) + (mv2 * c2) + (mv0 * c0);
|
||||||
return mv_scaled / (float) (csum * 1000U);
|
return mv_scaled / (float) (csum * 1000U);
|
||||||
}
|
}
|
||||||
#endif // USE_ESP32
|
|
||||||
|
|
||||||
#ifdef USE_RP2040
|
|
||||||
float ADCSensor::sample() {
|
|
||||||
if (this->is_temperature_) {
|
|
||||||
adc_set_temp_sensor_enabled(true);
|
|
||||||
delay(1);
|
|
||||||
adc_select_input(4);
|
|
||||||
uint32_t raw = 0;
|
|
||||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
|
||||||
raw += adc_read();
|
|
||||||
}
|
|
||||||
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
|
|
||||||
adc_set_temp_sensor_enabled(false);
|
|
||||||
if (this->output_raw_) {
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
return raw * 3.3f / 4096.0f;
|
|
||||||
} else {
|
|
||||||
uint8_t pin = this->pin_->get_pin();
|
|
||||||
#ifdef CYW43_USES_VSYS_PIN
|
|
||||||
if (pin == PICO_VSYS_PIN) {
|
|
||||||
// Measuring VSYS on Raspberry Pico W needs to be wrapped with
|
|
||||||
// `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in
|
|
||||||
// https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and
|
|
||||||
// VSYS ADC both share GPIO29
|
|
||||||
cyw43_thread_enter();
|
|
||||||
}
|
|
||||||
#endif // CYW43_USES_VSYS_PIN
|
|
||||||
|
|
||||||
adc_gpio_init(pin);
|
|
||||||
adc_select_input(pin - 26);
|
|
||||||
|
|
||||||
uint32_t raw = 0;
|
|
||||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
|
||||||
raw += adc_read();
|
|
||||||
}
|
|
||||||
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
|
|
||||||
|
|
||||||
#ifdef CYW43_USES_VSYS_PIN
|
|
||||||
if (pin == PICO_VSYS_PIN) {
|
|
||||||
cyw43_thread_exit();
|
|
||||||
}
|
|
||||||
#endif // CYW43_USES_VSYS_PIN
|
|
||||||
|
|
||||||
if (this->output_raw_) {
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
float coeff = pin == PICO_VSYS_PIN ? 3.0 : 1.0;
|
|
||||||
return raw * 3.3f / 4096.0f * coeff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_LIBRETINY
|
|
||||||
float ADCSensor::sample() {
|
|
||||||
uint32_t raw = 0;
|
|
||||||
if (this->output_raw_) {
|
|
||||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
|
||||||
raw += analogRead(this->pin_->get_pin()); // NOLINT
|
|
||||||
}
|
|
||||||
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
|
||||||
raw += analogReadVoltage(this->pin_->get_pin()); // NOLINT
|
|
||||||
}
|
|
||||||
raw = (raw + (this->sample_count_ >> 1)) / this->sample_count_; // NOLINT(clang-analyzer-core.DivideZero)
|
|
||||||
return raw / 1000.0f;
|
|
||||||
}
|
|
||||||
#endif // USE_LIBRETINY
|
|
||||||
|
|
||||||
#ifdef USE_ESP8266
|
|
||||||
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace adc
|
} // namespace adc
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ESP32
|
||||||
62
esphome/components/adc/adc_sensor_esp8266.cpp
Normal file
62
esphome/components/adc/adc_sensor_esp8266.cpp
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#ifdef USE_ESP8266
|
||||||
|
|
||||||
|
#include "adc_sensor.h"
|
||||||
|
#include "esphome/core/helpers.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
#include <Esp.h>
|
||||||
|
ADC_MODE(ADC_VCC)
|
||||||
|
#else
|
||||||
|
#include <Arduino.h>
|
||||||
|
#endif // USE_ADC_SENSOR_VCC
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace adc {
|
||||||
|
|
||||||
|
static const char *const TAG = "adc.esp8266";
|
||||||
|
|
||||||
|
void ADCSensor::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
||||||
|
#ifndef USE_ADC_SENSOR_VCC
|
||||||
|
this->pin_->setup();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADCSensor::dump_config() {
|
||||||
|
LOG_SENSOR("", "ADC Sensor", this);
|
||||||
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
||||||
|
#else
|
||||||
|
LOG_PIN(" Pin: ", this->pin_);
|
||||||
|
#endif // USE_ADC_SENSOR_VCC
|
||||||
|
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ADCSensor::sample() {
|
||||||
|
auto aggr = Aggregator(this->sampling_mode_);
|
||||||
|
|
||||||
|
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
||||||
|
uint32_t raw = 0;
|
||||||
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
raw = ESP.getVcc(); // NOLINT(readability-static-accessed-through-instance)
|
||||||
|
#else
|
||||||
|
raw = analogRead(this->pin_->get_pin()); // NOLINT
|
||||||
|
#endif // USE_ADC_SENSOR_VCC
|
||||||
|
aggr.add_sample(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->output_raw_) {
|
||||||
|
return aggr.aggregate();
|
||||||
|
}
|
||||||
|
return aggr.aggregate() / 1024.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ADCSensor::unique_id() { return get_mac_address() + "-adc"; }
|
||||||
|
|
||||||
|
} // namespace adc
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_ESP8266
|
||||||
53
esphome/components/adc/adc_sensor_libretiny.cpp
Normal file
53
esphome/components/adc/adc_sensor_libretiny.cpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#ifdef USE_LIBRETINY
|
||||||
|
|
||||||
|
#include "adc_sensor.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace adc {
|
||||||
|
|
||||||
|
static const char *const TAG = "adc.libretiny";
|
||||||
|
|
||||||
|
void ADCSensor::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
||||||
|
#ifndef USE_ADC_SENSOR_VCC
|
||||||
|
this->pin_->setup();
|
||||||
|
#endif // !USE_ADC_SENSOR_VCC
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADCSensor::dump_config() {
|
||||||
|
LOG_SENSOR("", "ADC Sensor", this);
|
||||||
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
||||||
|
#else // USE_ADC_SENSOR_VCC
|
||||||
|
LOG_PIN(" Pin: ", this->pin_);
|
||||||
|
#endif // USE_ADC_SENSOR_VCC
|
||||||
|
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ADCSensor::sample() {
|
||||||
|
uint32_t raw = 0;
|
||||||
|
auto aggr = Aggregator(this->sampling_mode_);
|
||||||
|
|
||||||
|
if (this->output_raw_) {
|
||||||
|
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
||||||
|
raw = analogRead(this->pin_->get_pin()); // NOLINT
|
||||||
|
aggr.add_sample(raw);
|
||||||
|
}
|
||||||
|
return aggr.aggregate();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
||||||
|
raw = analogReadVoltage(this->pin_->get_pin()); // NOLINT
|
||||||
|
aggr.add_sample(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
return aggr.aggregate() / 1000.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace adc
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_LIBRETINY
|
||||||
96
esphome/components/adc/adc_sensor_rp2040.cpp
Normal file
96
esphome/components/adc/adc_sensor_rp2040.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#ifdef USE_RP2040
|
||||||
|
|
||||||
|
#include "adc_sensor.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#ifdef CYW43_USES_VSYS_PIN
|
||||||
|
#include "pico/cyw43_arch.h"
|
||||||
|
#endif // CYW43_USES_VSYS_PIN
|
||||||
|
#include <hardware/adc.h>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace adc {
|
||||||
|
|
||||||
|
static const char *const TAG = "adc.rp2040";
|
||||||
|
|
||||||
|
void ADCSensor::setup() {
|
||||||
|
ESP_LOGCONFIG(TAG, "Setting up ADC '%s'...", this->get_name().c_str());
|
||||||
|
static bool initialized = false;
|
||||||
|
if (!initialized) {
|
||||||
|
adc_init();
|
||||||
|
initialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADCSensor::dump_config() {
|
||||||
|
LOG_SENSOR("", "ADC Sensor", this);
|
||||||
|
if (this->is_temperature_) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Pin: Temperature");
|
||||||
|
} else {
|
||||||
|
#ifdef USE_ADC_SENSOR_VCC
|
||||||
|
ESP_LOGCONFIG(TAG, " Pin: VCC");
|
||||||
|
#else
|
||||||
|
LOG_PIN(" Pin: ", this->pin_);
|
||||||
|
#endif // USE_ADC_SENSOR_VCC
|
||||||
|
}
|
||||||
|
ESP_LOGCONFIG(TAG, " Samples: %i", this->sample_count_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Sampling mode: %s", LOG_STR_ARG(sampling_mode_to_str(this->sampling_mode_)));
|
||||||
|
LOG_UPDATE_INTERVAL(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ADCSensor::sample() {
|
||||||
|
uint32_t raw = 0;
|
||||||
|
auto aggr = Aggregator(this->sampling_mode_);
|
||||||
|
|
||||||
|
if (this->is_temperature_) {
|
||||||
|
adc_set_temp_sensor_enabled(true);
|
||||||
|
delay(1);
|
||||||
|
adc_select_input(4);
|
||||||
|
|
||||||
|
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
||||||
|
raw = adc_read();
|
||||||
|
aggr.add_sample(raw);
|
||||||
|
}
|
||||||
|
adc_set_temp_sensor_enabled(false);
|
||||||
|
if (this->output_raw_) {
|
||||||
|
return aggr.aggregate();
|
||||||
|
}
|
||||||
|
return aggr.aggregate() * 3.3f / 4096.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t pin = this->pin_->get_pin();
|
||||||
|
#ifdef CYW43_USES_VSYS_PIN
|
||||||
|
if (pin == PICO_VSYS_PIN) {
|
||||||
|
// Measuring VSYS on Raspberry Pico W needs to be wrapped with
|
||||||
|
// `cyw43_thread_enter()`/`cyw43_thread_exit()` as discussed in
|
||||||
|
// https://github.com/raspberrypi/pico-sdk/issues/1222, since Wifi chip and
|
||||||
|
// VSYS ADC both share GPIO29
|
||||||
|
cyw43_thread_enter();
|
||||||
|
}
|
||||||
|
#endif // CYW43_USES_VSYS_PIN
|
||||||
|
|
||||||
|
adc_gpio_init(pin);
|
||||||
|
adc_select_input(pin - 26);
|
||||||
|
|
||||||
|
for (uint8_t sample = 0; sample < this->sample_count_; sample++) {
|
||||||
|
raw = adc_read();
|
||||||
|
aggr.add_sample(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CYW43_USES_VSYS_PIN
|
||||||
|
if (pin == PICO_VSYS_PIN) {
|
||||||
|
cyw43_thread_exit();
|
||||||
|
}
|
||||||
|
#endif // CYW43_USES_VSYS_PIN
|
||||||
|
|
||||||
|
if (this->output_raw_) {
|
||||||
|
return aggr.aggregate();
|
||||||
|
}
|
||||||
|
float coeff = pin == PICO_VSYS_PIN ? 3.0f : 1.0f;
|
||||||
|
return aggr.aggregate() * 3.3f / 4096.0f * coeff;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace adc
|
||||||
|
} // namespace esphome
|
||||||
|
|
||||||
|
#endif // USE_RP2040
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
import esphome.final_validate as fv
|
|
||||||
from esphome.core import CORE
|
|
||||||
from esphome.components import sensor, voltage_sampler
|
from esphome.components import sensor, voltage_sampler
|
||||||
from esphome.components.esp32 import get_esp32_variant
|
from esphome.components.esp32 import get_esp32_variant
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ATTENUATION,
|
CONF_ATTENUATION,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
@@ -17,10 +15,14 @@ from esphome.const import (
|
|||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
)
|
)
|
||||||
|
from esphome.core import CORE
|
||||||
|
import esphome.final_validate as fv
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
ATTENUATION_MODES,
|
ATTENUATION_MODES,
|
||||||
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL,
|
ESP32_VARIANT_ADC1_PIN_TO_CHANNEL,
|
||||||
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL,
|
ESP32_VARIANT_ADC2_PIN_TO_CHANNEL,
|
||||||
|
SAMPLING_MODES,
|
||||||
adc_ns,
|
adc_ns,
|
||||||
validate_adc_pin,
|
validate_adc_pin,
|
||||||
)
|
)
|
||||||
@@ -30,9 +32,11 @@ _LOGGER = logging.getLogger(__name__)
|
|||||||
AUTO_LOAD = ["voltage_sampler"]
|
AUTO_LOAD = ["voltage_sampler"]
|
||||||
|
|
||||||
CONF_SAMPLES = "samples"
|
CONF_SAMPLES = "samples"
|
||||||
|
CONF_SAMPLING_MODE = "sampling_mode"
|
||||||
|
|
||||||
|
|
||||||
_attenuation = cv.enum(ATTENUATION_MODES, lower=True)
|
_attenuation = cv.enum(ATTENUATION_MODES, lower=True)
|
||||||
|
_sampling_mode = cv.enum(SAMPLING_MODES, lower=True)
|
||||||
|
|
||||||
|
|
||||||
def validate_config(config):
|
def validate_config(config):
|
||||||
@@ -88,6 +92,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.only_on_esp32, _attenuation
|
cv.only_on_esp32, _attenuation
|
||||||
),
|
),
|
||||||
cv.Optional(CONF_SAMPLES, default=1): cv.int_range(min=1, max=255),
|
cv.Optional(CONF_SAMPLES, default=1): cv.int_range(min=1, max=255),
|
||||||
|
cv.Optional(CONF_SAMPLING_MODE, default="avg"): _sampling_mode,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.polling_component_schema("60s")),
|
.extend(cv.polling_component_schema("60s")),
|
||||||
@@ -112,6 +117,7 @@ async def to_code(config):
|
|||||||
|
|
||||||
cg.add(var.set_output_raw(config[CONF_RAW]))
|
cg.add(var.set_output_raw(config[CONF_RAW]))
|
||||||
cg.add(var.set_sample_count(config[CONF_SAMPLES]))
|
cg.add(var.set_sample_count(config[CONF_SAMPLES]))
|
||||||
|
cg.add(var.set_sampling_mode(config[CONF_SAMPLING_MODE]))
|
||||||
|
|
||||||
if attenuation := config.get(CONF_ATTENUATION):
|
if attenuation := config.get(CONF_ATTENUATION):
|
||||||
if attenuation == "auto":
|
if attenuation == "auto":
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import spi
|
from esphome.components import spi
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
DEPENDENCIES = ["spi"]
|
DEPENDENCIES = ["spi"]
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
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_ID, CONF_CHANNEL
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||||
|
|
||||||
from .. import adc128s102_ns, ADC128S102
|
from .. import ADC128S102, adc128s102_ns
|
||||||
|
|
||||||
AUTO_LOAD = ["voltage_sampler"]
|
AUTO_LOAD = ["voltage_sampler"]
|
||||||
DEPENDENCIES = ["adc128s102"]
|
DEPENDENCIES = ["adc128s102"]
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import display, light
|
from esphome.components import display, light
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_ADDRESSABLE_LIGHT_ID,
|
||||||
|
CONF_HEIGHT,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_LAMBDA,
|
CONF_LAMBDA,
|
||||||
CONF_PAGES,
|
CONF_PAGES,
|
||||||
CONF_ADDRESSABLE_LIGHT_ID,
|
|
||||||
CONF_HEIGHT,
|
|
||||||
CONF_WIDTH,
|
|
||||||
CONF_UPDATE_INTERVAL,
|
|
||||||
CONF_PIXEL_MAPPER,
|
CONF_PIXEL_MAPPER,
|
||||||
|
CONF_UPDATE_INTERVAL,
|
||||||
|
CONF_WIDTH,
|
||||||
)
|
)
|
||||||
|
|
||||||
CODEOWNERS = ["@justfalter"]
|
CODEOWNERS = ["@justfalter"]
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import sensor, i2c
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import i2c, sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ACTIVE_POWER,
|
CONF_ACTIVE_POWER,
|
||||||
CONF_APPARENT_POWER,
|
CONF_APPARENT_POWER,
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import sensor
|
|
||||||
from esphome import pins
|
from esphome import pins
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_FREQUENCY,
|
||||||
CONF_IRQ_PIN,
|
CONF_IRQ_PIN,
|
||||||
CONF_VOLTAGE,
|
CONF_VOLTAGE,
|
||||||
CONF_FREQUENCY,
|
|
||||||
CONF_VOLTAGE_GAIN,
|
CONF_VOLTAGE_GAIN,
|
||||||
DEVICE_CLASS_CURRENT,
|
|
||||||
DEVICE_CLASS_APPARENT_POWER,
|
DEVICE_CLASS_APPARENT_POWER,
|
||||||
DEVICE_CLASS_POWER,
|
DEVICE_CLASS_CURRENT,
|
||||||
DEVICE_CLASS_REACTIVE_POWER,
|
|
||||||
DEVICE_CLASS_POWER_FACTOR,
|
|
||||||
DEVICE_CLASS_VOLTAGE,
|
|
||||||
DEVICE_CLASS_FREQUENCY,
|
DEVICE_CLASS_FREQUENCY,
|
||||||
|
DEVICE_CLASS_POWER,
|
||||||
|
DEVICE_CLASS_POWER_FACTOR,
|
||||||
|
DEVICE_CLASS_REACTIVE_POWER,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_VOLT,
|
|
||||||
UNIT_HERTZ,
|
|
||||||
UNIT_AMPERE,
|
UNIT_AMPERE,
|
||||||
UNIT_VOLT_AMPS,
|
UNIT_HERTZ,
|
||||||
UNIT_WATT,
|
|
||||||
UNIT_VOLT_AMPS_REACTIVE,
|
|
||||||
UNIT_PERCENT,
|
UNIT_PERCENT,
|
||||||
|
UNIT_VOLT,
|
||||||
|
UNIT_VOLT_AMPS,
|
||||||
|
UNIT_VOLT_AMPS_REACTIVE,
|
||||||
|
UNIT_WATT,
|
||||||
)
|
)
|
||||||
|
|
||||||
CONF_CURRENT_A = "current_a"
|
CONF_CURRENT_A = "current_a"
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import ade7953_base, i2c
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import i2c, ade7953_base
|
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
AUTO_LOAD = ["ade7953_base"]
|
AUTO_LOAD = ["ade7953_base"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import ade7953_base, spi
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import spi, ade7953_base
|
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
|
|
||||||
DEPENDENCIES = ["spi"]
|
DEPENDENCIES = ["spi"]
|
||||||
AUTO_LOAD = ["ade7953_base"]
|
AUTO_LOAD = ["ade7953_base"]
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import i2c
|
from esphome.components import i2c
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ static const char *const TAG = "ads1115";
|
|||||||
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
|
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
|
||||||
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
|
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
|
||||||
|
|
||||||
static const uint8_t ADS1115_DATA_RATE_860_SPS = 0b111; // 3300_SPS for ADS1015
|
|
||||||
|
|
||||||
void ADS1115Component::setup() {
|
void ADS1115Component::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
|
ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
|
||||||
uint16_t value;
|
uint16_t value;
|
||||||
@@ -43,9 +41,9 @@ void ADS1115Component::setup() {
|
|||||||
config |= 0b0000000100000000;
|
config |= 0b0000000100000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set data rate - 860 samples per second (we're in singleshot mode)
|
// Set data rate - 860 samples per second
|
||||||
// 0bxxxxxxxx100xxxxx
|
// 0bxxxxxxxx100xxxxx
|
||||||
config |= ADS1115_DATA_RATE_860_SPS << 5;
|
config |= ADS1115_860SPS << 5;
|
||||||
|
|
||||||
// Set comparator mode - hysteresis
|
// Set comparator mode - hysteresis
|
||||||
// 0bxxxxxxxxxxx0xxxx
|
// 0bxxxxxxxxxxx0xxxx
|
||||||
@@ -77,7 +75,7 @@ void ADS1115Component::dump_config() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain,
|
float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain,
|
||||||
ADS1115Resolution resolution) {
|
ADS1115Resolution resolution, ADS1115Samplerate samplerate) {
|
||||||
uint16_t config = this->prev_config_;
|
uint16_t config = this->prev_config_;
|
||||||
// Multiplexer
|
// Multiplexer
|
||||||
// 0bxBBBxxxxxxxxxxxx
|
// 0bxBBBxxxxxxxxxxxx
|
||||||
@@ -89,6 +87,11 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1
|
|||||||
config &= 0b1111000111111111;
|
config &= 0b1111000111111111;
|
||||||
config |= (gain & 0b111) << 9;
|
config |= (gain & 0b111) << 9;
|
||||||
|
|
||||||
|
// Sample rate
|
||||||
|
// 0bxxxxxxxxBBBxxxxx
|
||||||
|
config &= 0b1111111100011111;
|
||||||
|
config |= (samplerate & 0b111) << 5;
|
||||||
|
|
||||||
if (!this->continuous_mode_) {
|
if (!this->continuous_mode_) {
|
||||||
// Start conversion
|
// Start conversion
|
||||||
config |= 0b1000000000000000;
|
config |= 0b1000000000000000;
|
||||||
@@ -101,8 +104,54 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1
|
|||||||
}
|
}
|
||||||
this->prev_config_ = config;
|
this->prev_config_ = config;
|
||||||
|
|
||||||
// about 1.2 ms with 860 samples per second
|
// Delay calculated as: ceil((1000/SPS)+.5)
|
||||||
delay(2);
|
if (resolution == ADS1015_12_BITS) {
|
||||||
|
switch (samplerate) {
|
||||||
|
case ADS1115_8SPS:
|
||||||
|
delay(9);
|
||||||
|
break;
|
||||||
|
case ADS1115_16SPS:
|
||||||
|
delay(5);
|
||||||
|
break;
|
||||||
|
case ADS1115_32SPS:
|
||||||
|
delay(3);
|
||||||
|
break;
|
||||||
|
case ADS1115_64SPS:
|
||||||
|
case ADS1115_128SPS:
|
||||||
|
delay(2);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
delay(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (samplerate) {
|
||||||
|
case ADS1115_8SPS:
|
||||||
|
delay(126); // NOLINT
|
||||||
|
break;
|
||||||
|
case ADS1115_16SPS:
|
||||||
|
delay(63); // NOLINT
|
||||||
|
break;
|
||||||
|
case ADS1115_32SPS:
|
||||||
|
delay(32);
|
||||||
|
break;
|
||||||
|
case ADS1115_64SPS:
|
||||||
|
delay(17);
|
||||||
|
break;
|
||||||
|
case ADS1115_128SPS:
|
||||||
|
delay(9);
|
||||||
|
break;
|
||||||
|
case ADS1115_250SPS:
|
||||||
|
delay(5);
|
||||||
|
break;
|
||||||
|
case ADS1115_475SPS:
|
||||||
|
delay(3);
|
||||||
|
break;
|
||||||
|
case ADS1115_860SPS:
|
||||||
|
delay(2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// in continuous mode, conversion will always be running, rely on the delay
|
// in continuous mode, conversion will always be running, rely on the delay
|
||||||
// to ensure conversion is taking place with the correct settings
|
// to ensure conversion is taking place with the correct settings
|
||||||
|
|||||||
@@ -33,6 +33,17 @@ enum ADS1115Resolution {
|
|||||||
ADS1015_12_BITS = 12,
|
ADS1015_12_BITS = 12,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ADS1115Samplerate {
|
||||||
|
ADS1115_8SPS = 0b000,
|
||||||
|
ADS1115_16SPS = 0b001,
|
||||||
|
ADS1115_32SPS = 0b010,
|
||||||
|
ADS1115_64SPS = 0b011,
|
||||||
|
ADS1115_128SPS = 0b100,
|
||||||
|
ADS1115_250SPS = 0b101,
|
||||||
|
ADS1115_475SPS = 0b110,
|
||||||
|
ADS1115_860SPS = 0b111
|
||||||
|
};
|
||||||
|
|
||||||
class ADS1115Component : public Component, public i2c::I2CDevice {
|
class ADS1115Component : public Component, public i2c::I2CDevice {
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
@@ -42,7 +53,8 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
|
|||||||
void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
|
void set_continuous_mode(bool continuous_mode) { continuous_mode_ = continuous_mode; }
|
||||||
|
|
||||||
/// Helper method to request a measurement from a sensor.
|
/// Helper method to request a measurement from a sensor.
|
||||||
float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution);
|
float request_measurement(ADS1115Multiplexer multiplexer, ADS1115Gain gain, ADS1115Resolution resolution,
|
||||||
|
ADS1115Samplerate samplerate);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint16_t prev_config_{0};
|
uint16_t prev_config_{0};
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import sensor, voltage_sampler
|
from esphome.components import sensor, voltage_sampler
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_GAIN,
|
CONF_GAIN,
|
||||||
|
CONF_ID,
|
||||||
CONF_MULTIPLEXER,
|
CONF_MULTIPLEXER,
|
||||||
CONF_RESOLUTION,
|
CONF_RESOLUTION,
|
||||||
|
CONF_SAMPLE_RATE,
|
||||||
DEVICE_CLASS_VOLTAGE,
|
DEVICE_CLASS_VOLTAGE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
CONF_ID,
|
|
||||||
)
|
)
|
||||||
from .. import ads1115_ns, ADS1115Component, CONF_ADS1115_ID
|
|
||||||
|
from .. import CONF_ADS1115_ID, ADS1115Component, ads1115_ns
|
||||||
|
|
||||||
AUTO_LOAD = ["voltage_sampler"]
|
AUTO_LOAD = ["voltage_sampler"]
|
||||||
DEPENDENCIES = ["ads1115"]
|
DEPENDENCIES = ["ads1115"]
|
||||||
@@ -43,6 +45,17 @@ RESOLUTION = {
|
|||||||
"12_BITS": ADS1115Resolution.ADS1015_12_BITS,
|
"12_BITS": ADS1115Resolution.ADS1015_12_BITS,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ADS1115Samplerate = ads1115_ns.enum("ADS1115Samplerate")
|
||||||
|
SAMPLERATE = {
|
||||||
|
"8": ADS1115Samplerate.ADS1115_8SPS,
|
||||||
|
"16": ADS1115Samplerate.ADS1115_16SPS,
|
||||||
|
"32": ADS1115Samplerate.ADS1115_32SPS,
|
||||||
|
"64": ADS1115Samplerate.ADS1115_64SPS,
|
||||||
|
"128": ADS1115Samplerate.ADS1115_128SPS,
|
||||||
|
"250": ADS1115Samplerate.ADS1115_250SPS,
|
||||||
|
"475": ADS1115Samplerate.ADS1115_475SPS,
|
||||||
|
"860": ADS1115Samplerate.ADS1115_860SPS,
|
||||||
|
}
|
||||||
|
|
||||||
ADS1115Sensor = ads1115_ns.class_(
|
ADS1115Sensor = ads1115_ns.class_(
|
||||||
"ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
|
"ADS1115Sensor", sensor.Sensor, cg.PollingComponent, voltage_sampler.VoltageSampler
|
||||||
@@ -64,6 +77,9 @@ CONFIG_SCHEMA = (
|
|||||||
cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum(
|
cv.Optional(CONF_RESOLUTION, default="16_BITS"): cv.enum(
|
||||||
RESOLUTION, upper=True, space="_"
|
RESOLUTION, upper=True, space="_"
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_SAMPLE_RATE, default="860"): cv.enum(
|
||||||
|
SAMPLERATE, string=True
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.polling_component_schema("60s"))
|
.extend(cv.polling_component_schema("60s"))
|
||||||
@@ -79,3 +95,4 @@ async def to_code(config):
|
|||||||
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
|
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
|
||||||
cg.add(var.set_gain(config[CONF_GAIN]))
|
cg.add(var.set_gain(config[CONF_GAIN]))
|
||||||
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
|
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
|
||||||
|
cg.add(var.set_samplerate(config[CONF_SAMPLE_RATE]))
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace ads1115 {
|
|||||||
static const char *const TAG = "ads1115.sensor";
|
static const char *const TAG = "ads1115.sensor";
|
||||||
|
|
||||||
float ADS1115Sensor::sample() {
|
float ADS1115Sensor::sample() {
|
||||||
return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_);
|
return this->parent_->request_measurement(this->multiplexer_, this->gain_, this->resolution_, this->samplerate_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ADS1115Sensor::update() {
|
void ADS1115Sensor::update() {
|
||||||
@@ -24,6 +24,7 @@ void ADS1115Sensor::dump_config() {
|
|||||||
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
|
ESP_LOGCONFIG(TAG, " Multiplexer: %u", this->multiplexer_);
|
||||||
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
|
ESP_LOGCONFIG(TAG, " Gain: %u", this->gain_);
|
||||||
ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_);
|
ESP_LOGCONFIG(TAG, " Resolution: %u", this->resolution_);
|
||||||
|
ESP_LOGCONFIG(TAG, " Sample rate: %u", this->samplerate_);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ads1115
|
} // namespace ads1115
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class ADS1115Sensor : public sensor::Sensor,
|
|||||||
void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
|
void set_multiplexer(ADS1115Multiplexer multiplexer) { this->multiplexer_ = multiplexer; }
|
||||||
void set_gain(ADS1115Gain gain) { this->gain_ = gain; }
|
void set_gain(ADS1115Gain gain) { this->gain_ = gain; }
|
||||||
void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; }
|
void set_resolution(ADS1115Resolution resolution) { this->resolution_ = resolution; }
|
||||||
|
void set_samplerate(ADS1115Samplerate samplerate) { this->samplerate_ = samplerate; }
|
||||||
float sample() override;
|
float sample() override;
|
||||||
|
|
||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
@@ -29,6 +30,7 @@ class ADS1115Sensor : public sensor::Sensor,
|
|||||||
ADS1115Multiplexer multiplexer_;
|
ADS1115Multiplexer multiplexer_;
|
||||||
ADS1115Gain gain_;
|
ADS1115Gain gain_;
|
||||||
ADS1115Resolution resolution_;
|
ADS1115Resolution resolution_;
|
||||||
|
ADS1115Samplerate samplerate_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ads1115
|
} // namespace ads1115
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import spi
|
from esphome.components import spi
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
CODEOWNERS = ["@solomondg1"]
|
CODEOWNERS = ["@solomondg1"]
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import sensor, voltage_sampler
|
from esphome.components import sensor, voltage_sampler
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_GAIN,
|
CONF_GAIN,
|
||||||
CONF_MULTIPLEXER,
|
CONF_MULTIPLEXER,
|
||||||
DEVICE_CLASS_VOLTAGE,
|
CONF_TYPE,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_CELSIUS,
|
UNIT_CELSIUS,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
CONF_TYPE,
|
|
||||||
)
|
)
|
||||||
from .. import ads1118_ns, ADS1118, CONF_ADS1118_ID
|
|
||||||
|
from .. import ADS1118, CONF_ADS1118_ID, ads1118_ns
|
||||||
|
|
||||||
AUTO_LOAD = ["voltage_sampler"]
|
AUTO_LOAD = ["voltage_sampler"]
|
||||||
DEPENDENCIES = ["ads1118"]
|
DEPENDENCIES = ["ads1118"]
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
import esphome.codegen as cg
|
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
import esphome.config_validation as cv
|
import esphome.codegen as cg
|
||||||
from esphome.components import i2c, sensor
|
from esphome.components import i2c, sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
|
CONF_ADDRESS,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
ICON_RADIATOR,
|
CONF_MODE,
|
||||||
ICON_RESTART,
|
CONF_TVOC,
|
||||||
|
CONF_VALUE,
|
||||||
|
CONF_VERSION,
|
||||||
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
||||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
|
ICON_RADIATOR,
|
||||||
|
ICON_RESTART,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_OHM,
|
UNIT_OHM,
|
||||||
UNIT_PARTS_PER_BILLION,
|
UNIT_PARTS_PER_BILLION,
|
||||||
CONF_ADDRESS,
|
|
||||||
CONF_TVOC,
|
|
||||||
CONF_VERSION,
|
|
||||||
CONF_MODE,
|
|
||||||
CONF_VALUE,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
CONF_RESISTANCE = "resistance"
|
CONF_RESISTANCE = "resistance"
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import i2c, sensor
|
from esphome.components import i2c, sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_HUMIDITY,
|
CONF_HUMIDITY,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_TEMPERATURE,
|
CONF_TEMPERATURE,
|
||||||
|
CONF_VARIANT,
|
||||||
DEVICE_CLASS_HUMIDITY,
|
DEVICE_CLASS_HUMIDITY,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_CELSIUS,
|
UNIT_CELSIUS,
|
||||||
UNIT_PERCENT,
|
UNIT_PERCENT,
|
||||||
CONF_VARIANT,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import esp32_ble_tracker
|
from esphome.components import esp32_ble_tracker
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import ble_client, sensor
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import sensor, ble_client
|
|
||||||
|
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_BATTERY_VOLTAGE,
|
CONF_BATTERY_VOLTAGE,
|
||||||
CONF_HUMIDITY,
|
CONF_HUMIDITY,
|
||||||
CONF_PRESSURE,
|
CONF_PRESSURE,
|
||||||
CONF_TEMPERATURE,
|
CONF_TEMPERATURE,
|
||||||
CONF_TVOC,
|
CONF_TVOC,
|
||||||
DEVICE_CLASS_VOLTAGE,
|
|
||||||
DEVICE_CLASS_HUMIDITY,
|
DEVICE_CLASS_HUMIDITY,
|
||||||
DEVICE_CLASS_PRESSURE,
|
DEVICE_CLASS_PRESSURE,
|
||||||
DEVICE_CLASS_TEMPERATURE,
|
DEVICE_CLASS_TEMPERATURE,
|
||||||
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
DEVICE_CLASS_VOLATILE_ORGANIC_COMPOUNDS_PARTS,
|
||||||
|
DEVICE_CLASS_VOLTAGE,
|
||||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_CELSIUS,
|
UNIT_CELSIUS,
|
||||||
@@ -35,7 +34,7 @@ AirthingsWaveBase = airthings_wave_base_ns.class_(
|
|||||||
|
|
||||||
|
|
||||||
BASE_SCHEMA = (
|
BASE_SCHEMA = (
|
||||||
sensor.SENSOR_SCHEMA.extend(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||||
unit_of_measurement=UNIT_PERCENT,
|
unit_of_measurement=UNIT_PERCENT,
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import airthings_wave_base
|
from esphome.components import airthings_wave_base
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import CONF_ID
|
||||||
CONF_ID,
|
|
||||||
)
|
|
||||||
|
|
||||||
DEPENDENCIES = airthings_wave_base.DEPENDENCIES
|
DEPENDENCIES = airthings_wave_base.DEPENDENCIES
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import airthings_wave_base, sensor
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import sensor, airthings_wave_base
|
|
||||||
|
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
DEVICE_CLASS_CARBON_DIOXIDE,
|
CONF_CO2,
|
||||||
STATE_CLASS_MEASUREMENT,
|
|
||||||
ICON_RADIOACTIVE,
|
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
CONF_ILLUMINANCE,
|
||||||
CONF_RADON,
|
CONF_RADON,
|
||||||
CONF_RADON_LONG_TERM,
|
CONF_RADON_LONG_TERM,
|
||||||
CONF_CO2,
|
DEVICE_CLASS_CARBON_DIOXIDE,
|
||||||
UNIT_BECQUEREL_PER_CUBIC_METER,
|
|
||||||
UNIT_PARTS_PER_MILLION,
|
|
||||||
CONF_ILLUMINANCE,
|
|
||||||
UNIT_LUX,
|
|
||||||
DEVICE_CLASS_ILLUMINANCE,
|
DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
ICON_RADIOACTIVE,
|
||||||
|
STATE_CLASS_MEASUREMENT,
|
||||||
|
UNIT_BECQUEREL_PER_CUBIC_METER,
|
||||||
|
UNIT_LUX,
|
||||||
|
UNIT_PARTS_PER_MILLION,
|
||||||
)
|
)
|
||||||
|
|
||||||
DEPENDENCIES = airthings_wave_base.DEPENDENCIES
|
DEPENDENCIES = airthings_wave_base.DEPENDENCIES
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ from esphome.components import mqtt, web_server
|
|||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_CODE,
|
CONF_CODE,
|
||||||
|
CONF_ENTITY_CATEGORY,
|
||||||
|
CONF_ICON,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
CONF_MQTT_ID,
|
CONF_MQTT_ID,
|
||||||
CONF_ON_STATE,
|
CONF_ON_STATE,
|
||||||
@@ -12,6 +14,7 @@ from esphome.const import (
|
|||||||
CONF_WEB_SERVER,
|
CONF_WEB_SERVER,
|
||||||
)
|
)
|
||||||
from esphome.core import CORE, coroutine_with_priority
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
|
from esphome.cpp_generator import MockObjClass
|
||||||
from esphome.cpp_helpers import setup_entity
|
from esphome.cpp_helpers import setup_entity
|
||||||
|
|
||||||
CODEOWNERS = ["@grahambrown11", "@hwstar"]
|
CODEOWNERS = ["@grahambrown11", "@hwstar"]
|
||||||
@@ -78,12 +81,11 @@ AlarmControlPanelCondition = alarm_control_panel_ns.class_(
|
|||||||
"AlarmControlPanelCondition", automation.Condition
|
"AlarmControlPanelCondition", automation.Condition
|
||||||
)
|
)
|
||||||
|
|
||||||
ALARM_CONTROL_PANEL_SCHEMA = (
|
_ALARM_CONTROL_PANEL_SCHEMA = (
|
||||||
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
.extend(
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(AlarmControlPanel),
|
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
||||||
mqtt.MQTTAlarmControlPanelComponent
|
mqtt.MQTTAlarmControlPanelComponent
|
||||||
),
|
),
|
||||||
@@ -146,6 +148,33 @@ ALARM_CONTROL_PANEL_SCHEMA = (
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def alarm_control_panel_schema(
|
||||||
|
class_: MockObjClass,
|
||||||
|
*,
|
||||||
|
entity_category: str = cv.UNDEFINED,
|
||||||
|
icon: str = cv.UNDEFINED,
|
||||||
|
) -> cv.Schema:
|
||||||
|
schema = {
|
||||||
|
cv.GenerateID(): cv.declare_id(class_),
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, default, validator in [
|
||||||
|
(CONF_ENTITY_CATEGORY, entity_category, cv.entity_category),
|
||||||
|
(CONF_ICON, icon, cv.icon),
|
||||||
|
]:
|
||||||
|
if default is not cv.UNDEFINED:
|
||||||
|
schema[cv.Optional(key, default=default)] = validator
|
||||||
|
|
||||||
|
return _ALARM_CONTROL_PANEL_SCHEMA.extend(schema)
|
||||||
|
|
||||||
|
|
||||||
|
# Remove before 2025.11.0
|
||||||
|
ALARM_CONTROL_PANEL_SCHEMA = alarm_control_panel_schema(AlarmControlPanel)
|
||||||
|
ALARM_CONTROL_PANEL_SCHEMA.add_extra(
|
||||||
|
cv.deprecated_schema_constant("alarm_control_panel")
|
||||||
|
)
|
||||||
|
|
||||||
ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id(
|
ALARM_CONTROL_PANEL_ACTION_SCHEMA = maybe_simple_id(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.use_id(AlarmControlPanel),
|
cv.GenerateID(): cv.use_id(AlarmControlPanel),
|
||||||
@@ -209,6 +238,12 @@ async def register_alarm_control_panel(var, config):
|
|||||||
await setup_alarm_control_panel_core_(var, config)
|
await setup_alarm_control_panel_core_(var, config)
|
||||||
|
|
||||||
|
|
||||||
|
async def new_alarm_control_panel(config, *args):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID], *args)
|
||||||
|
await register_alarm_control_panel(var, config)
|
||||||
|
return var
|
||||||
|
|
||||||
|
|
||||||
@automation.register_action(
|
@automation.register_action(
|
||||||
"alarm_control_panel.arm_away", ArmAwayAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
|
"alarm_control_panel.arm_away", ArmAwayAction, ALARM_CONTROL_PANEL_ACTION_SCHEMA
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -72,10 +72,9 @@ void AlarmControlPanelCall::validate_() {
|
|||||||
this->state_.reset();
|
this->state_.reset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (state == ACP_STATE_DISARMED &&
|
if (state == ACP_STATE_DISARMED && !this->parent_->is_state_armed(this->parent_->get_state()) &&
|
||||||
!(this->parent_->is_state_armed(this->parent_->get_state()) ||
|
this->parent_->get_state() != ACP_STATE_PENDING && this->parent_->get_state() != ACP_STATE_ARMING &&
|
||||||
this->parent_->get_state() == ACP_STATE_PENDING || this->parent_->get_state() == ACP_STATE_ARMING ||
|
this->parent_->get_state() != ACP_STATE_TRIGGERED) {
|
||||||
this->parent_->get_state() == ACP_STATE_TRIGGERED)) {
|
|
||||||
ESP_LOGW(TAG, "Cannot disarm when not armed");
|
ESP_LOGW(TAG, "Cannot disarm when not armed");
|
||||||
this->state_.reset();
|
this->state_.reset();
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import ble_client, sensor
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import sensor, ble_client
|
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
|
||||||
CONF_CURRENT,
|
CONF_CURRENT,
|
||||||
CONF_FLOW,
|
CONF_FLOW,
|
||||||
CONF_HEAD,
|
CONF_HEAD,
|
||||||
|
CONF_ID,
|
||||||
CONF_POWER,
|
CONF_POWER,
|
||||||
CONF_SPEED,
|
CONF_SPEED,
|
||||||
CONF_VOLTAGE,
|
CONF_VOLTAGE,
|
||||||
UNIT_AMPERE,
|
UNIT_AMPERE,
|
||||||
|
UNIT_CUBIC_METER_PER_HOUR,
|
||||||
|
UNIT_METER,
|
||||||
|
UNIT_REVOLUTIONS_PER_MINUTE,
|
||||||
UNIT_VOLT,
|
UNIT_VOLT,
|
||||||
UNIT_WATT,
|
UNIT_WATT,
|
||||||
UNIT_METER,
|
|
||||||
UNIT_CUBIC_METER_PER_HOUR,
|
|
||||||
UNIT_REVOLUTIONS_PER_MINUTE,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
alpha3_ns = cg.esphome_ns.namespace("alpha3")
|
alpha3_ns = cg.esphome_ns.namespace("alpha3")
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ void AM2315C::update() {
|
|||||||
data[2] = 0x00;
|
data[2] = 0x00;
|
||||||
if (this->write(data, 3) != i2c::ERROR_OK) {
|
if (this->write(data, 3) != i2c::ERROR_OK) {
|
||||||
ESP_LOGE(TAG, "Write failed!");
|
ESP_LOGE(TAG, "Write failed!");
|
||||||
this->mark_failed();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,12 +138,12 @@ void AM2315C::update() {
|
|||||||
uint8_t status = 0;
|
uint8_t status = 0;
|
||||||
if (this->read(&status, 1) != i2c::ERROR_OK) {
|
if (this->read(&status, 1) != i2c::ERROR_OK) {
|
||||||
ESP_LOGE(TAG, "Read failed!");
|
ESP_LOGE(TAG, "Read failed!");
|
||||||
this->mark_failed();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((status & 0x80) == 0x80) {
|
if ((status & 0x80) == 0x80) {
|
||||||
ESP_LOGE(TAG, "HW still busy!");
|
ESP_LOGE(TAG, "HW still busy!");
|
||||||
this->mark_failed();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ void AM2315C::update() {
|
|||||||
uint8_t data[7];
|
uint8_t data[7];
|
||||||
if (this->read(data, 7) != i2c::ERROR_OK) {
|
if (this->read(data, 7) != i2c::ERROR_OK) {
|
||||||
ESP_LOGE(TAG, "Read failed!");
|
ESP_LOGE(TAG, "Read failed!");
|
||||||
this->mark_failed();
|
this->status_set_warning();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import i2c, sensor
|
from esphome.components import i2c, sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_HUMIDITY,
|
CONF_HUMIDITY,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import i2c, sensor
|
from esphome.components import i2c, sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_HUMIDITY,
|
CONF_HUMIDITY,
|
||||||
CONF_ID,
|
CONF_ID,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import ble_client, cover
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import cover, ble_client
|
from esphome.const import CONF_PIN
|
||||||
from esphome.const import CONF_ID, CONF_PIN
|
|
||||||
|
|
||||||
CODEOWNERS = ["@buxtronix"]
|
CODEOWNERS = ["@buxtronix"]
|
||||||
DEPENDENCIES = ["ble_client"]
|
DEPENDENCIES = ["ble_client"]
|
||||||
@@ -15,9 +15,9 @@ Am43Component = am43_ns.class_(
|
|||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = (
|
CONFIG_SCHEMA = (
|
||||||
cover.COVER_SCHEMA.extend(
|
cover.cover_schema(Am43Component)
|
||||||
|
.extend(
|
||||||
{
|
{
|
||||||
cv.GenerateID(): cv.declare_id(Am43Component),
|
|
||||||
cv.Optional(CONF_PIN, default=8888): cv.int_range(min=0, max=0xFFFF),
|
cv.Optional(CONF_PIN, default=8888): cv.int_range(min=0, max=0xFFFF),
|
||||||
cv.Optional(CONF_INVERT_POSITION, default=False): cv.boolean,
|
cv.Optional(CONF_INVERT_POSITION, default=False): cv.boolean,
|
||||||
}
|
}
|
||||||
@@ -28,9 +28,8 @@ CONFIG_SCHEMA = (
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = await cover.new_cover(config)
|
||||||
cg.add(var.set_pin(config[CONF_PIN]))
|
cg.add(var.set_pin(config[CONF_PIN]))
|
||||||
cg.add(var.set_invert_position(config[CONF_INVERT_POSITION]))
|
cg.add(var.set_invert_position(config[CONF_INVERT_POSITION]))
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, config)
|
||||||
await cover.register_cover(var, config)
|
|
||||||
await ble_client.register_ble_node(var, config)
|
await ble_client.register_ble_node(var, config)
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import ble_client, sensor
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import sensor, ble_client
|
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ID,
|
|
||||||
CONF_BATTERY_LEVEL,
|
CONF_BATTERY_LEVEL,
|
||||||
|
CONF_ID,
|
||||||
|
CONF_ILLUMINANCE,
|
||||||
DEVICE_CLASS_BATTERY,
|
DEVICE_CLASS_BATTERY,
|
||||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||||
CONF_ILLUMINANCE,
|
|
||||||
ICON_BRIGHTNESS_5,
|
ICON_BRIGHTNESS_5,
|
||||||
UNIT_PERCENT,
|
UNIT_PERCENT,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ void AnalogThresholdBinarySensor::setup() {
|
|||||||
if (std::isnan(sensor_value)) {
|
if (std::isnan(sensor_value)) {
|
||||||
this->publish_initial_state(false);
|
this->publish_initial_state(false);
|
||||||
} else {
|
} else {
|
||||||
this->publish_initial_state(sensor_value >= (this->lower_threshold_ + this->upper_threshold_) / 2.0f);
|
this->publish_initial_state(sensor_value >=
|
||||||
|
(this->lower_threshold_.value() + this->upper_threshold_.value()) / 2.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +25,8 @@ void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) {
|
|||||||
this->sensor_->add_on_state_callback([this](float sensor_value) {
|
this->sensor_->add_on_state_callback([this](float sensor_value) {
|
||||||
// if there is an invalid sensor reading, ignore the change and keep the current state
|
// if there is an invalid sensor reading, ignore the change and keep the current state
|
||||||
if (!std::isnan(sensor_value)) {
|
if (!std::isnan(sensor_value)) {
|
||||||
this->publish_state(sensor_value >= (this->state ? this->lower_threshold_ : this->upper_threshold_));
|
this->publish_state(sensor_value >=
|
||||||
|
(this->state ? this->lower_threshold_.value() : this->upper_threshold_.value()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -32,8 +34,8 @@ void AnalogThresholdBinarySensor::set_sensor(sensor::Sensor *analog_sensor) {
|
|||||||
void AnalogThresholdBinarySensor::dump_config() {
|
void AnalogThresholdBinarySensor::dump_config() {
|
||||||
LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this);
|
LOG_BINARY_SENSOR("", "Analog Threshold Binary Sensor", this);
|
||||||
LOG_SENSOR(" ", "Sensor", this->sensor_);
|
LOG_SENSOR(" ", "Sensor", this->sensor_);
|
||||||
ESP_LOGCONFIG(TAG, " Upper threshold: %.11f", this->upper_threshold_);
|
ESP_LOGCONFIG(TAG, " Upper threshold: %.11f", this->upper_threshold_.value());
|
||||||
ESP_LOGCONFIG(TAG, " Lower threshold: %.11f", this->lower_threshold_);
|
ESP_LOGCONFIG(TAG, " Lower threshold: %.11f", this->lower_threshold_.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace analog_threshold
|
} // namespace analog_threshold
|
||||||
|
|||||||
@@ -15,14 +15,13 @@ class AnalogThresholdBinarySensor : public Component, public binary_sensor::Bina
|
|||||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||||
|
|
||||||
void set_sensor(sensor::Sensor *analog_sensor);
|
void set_sensor(sensor::Sensor *analog_sensor);
|
||||||
void set_upper_threshold(float threshold) { this->upper_threshold_ = threshold; }
|
template<typename T> void set_upper_threshold(T upper_threshold) { this->upper_threshold_ = upper_threshold; }
|
||||||
void set_lower_threshold(float threshold) { this->lower_threshold_ = threshold; }
|
template<typename T> void set_lower_threshold(T lower_threshold) { this->lower_threshold_ = lower_threshold; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
sensor::Sensor *sensor_{nullptr};
|
sensor::Sensor *sensor_{nullptr};
|
||||||
|
TemplatableValue<float> upper_threshold_{};
|
||||||
float upper_threshold_;
|
TemplatableValue<float> lower_threshold_{};
|
||||||
float lower_threshold_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace analog_threshold
|
} // namespace analog_threshold
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import binary_sensor, sensor
|
from esphome.components import binary_sensor, sensor
|
||||||
from esphome.const import (
|
import esphome.config_validation as cv
|
||||||
CONF_SENSOR_ID,
|
from esphome.const import CONF_SENSOR_ID, CONF_THRESHOLD
|
||||||
CONF_THRESHOLD,
|
|
||||||
)
|
|
||||||
|
|
||||||
analog_threshold_ns = cg.esphome_ns.namespace("analog_threshold")
|
analog_threshold_ns = cg.esphome_ns.namespace("analog_threshold")
|
||||||
|
|
||||||
@@ -21,11 +18,11 @@ CONFIG_SCHEMA = (
|
|||||||
{
|
{
|
||||||
cv.Required(CONF_SENSOR_ID): cv.use_id(sensor.Sensor),
|
cv.Required(CONF_SENSOR_ID): cv.use_id(sensor.Sensor),
|
||||||
cv.Required(CONF_THRESHOLD): cv.Any(
|
cv.Required(CONF_THRESHOLD): cv.Any(
|
||||||
cv.float_,
|
cv.templatable(cv.float_),
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
cv.Required(CONF_UPPER): cv.float_,
|
cv.Required(CONF_UPPER): cv.templatable(cv.float_),
|
||||||
cv.Required(CONF_LOWER): cv.float_,
|
cv.Required(CONF_LOWER): cv.templatable(cv.float_),
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -42,9 +39,11 @@ async def to_code(config):
|
|||||||
sens = await cg.get_variable(config[CONF_SENSOR_ID])
|
sens = await cg.get_variable(config[CONF_SENSOR_ID])
|
||||||
cg.add(var.set_sensor(sens))
|
cg.add(var.set_sensor(sens))
|
||||||
|
|
||||||
if isinstance(config[CONF_THRESHOLD], float):
|
if isinstance(config[CONF_THRESHOLD], dict):
|
||||||
cg.add(var.set_upper_threshold(config[CONF_THRESHOLD]))
|
lower = await cg.templatable(config[CONF_THRESHOLD][CONF_LOWER], [], float)
|
||||||
cg.add(var.set_lower_threshold(config[CONF_THRESHOLD]))
|
upper = await cg.templatable(config[CONF_THRESHOLD][CONF_UPPER], [], float)
|
||||||
else:
|
else:
|
||||||
cg.add(var.set_upper_threshold(config[CONF_THRESHOLD][CONF_UPPER]))
|
lower = await cg.templatable(config[CONF_THRESHOLD], [], float)
|
||||||
cg.add(var.set_lower_threshold(config[CONF_THRESHOLD][CONF_LOWER]))
|
upper = lower
|
||||||
|
cg.add(var.set_upper_threshold(upper))
|
||||||
|
cg.add(var.set_lower_threshold(lower))
|
||||||
|
|||||||
@@ -1,29 +1,10 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from esphome import automation, core
|
from esphome import automation
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import font
|
|
||||||
import esphome.components.image as espImage
|
import esphome.components.image as espImage
|
||||||
from esphome.components.image import (
|
|
||||||
CONF_USE_TRANSPARENCY,
|
|
||||||
LOCAL_SCHEMA,
|
|
||||||
SOURCE_LOCAL,
|
|
||||||
SOURCE_WEB,
|
|
||||||
WEB_SCHEMA,
|
|
||||||
)
|
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import CONF_ID, CONF_REPEAT
|
||||||
CONF_FILE,
|
|
||||||
CONF_ID,
|
|
||||||
CONF_PATH,
|
|
||||||
CONF_RAW_DATA_ID,
|
|
||||||
CONF_REPEAT,
|
|
||||||
CONF_RESIZE,
|
|
||||||
CONF_SOURCE,
|
|
||||||
CONF_TYPE,
|
|
||||||
CONF_URL,
|
|
||||||
)
|
|
||||||
from esphome.core import CORE, HexInt
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -31,6 +12,7 @@ AUTO_LOAD = ["image"]
|
|||||||
CODEOWNERS = ["@syndlex"]
|
CODEOWNERS = ["@syndlex"]
|
||||||
DEPENDENCIES = ["display"]
|
DEPENDENCIES = ["display"]
|
||||||
MULTI_CONF = True
|
MULTI_CONF = True
|
||||||
|
MULTI_CONF_NO_DEFAULT = True
|
||||||
|
|
||||||
CONF_LOOP = "loop"
|
CONF_LOOP = "loop"
|
||||||
CONF_START_FRAME = "start_frame"
|
CONF_START_FRAME = "start_frame"
|
||||||
@@ -52,86 +34,19 @@ SetFrameAction = animation_ns.class_(
|
|||||||
"AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_)
|
"AnimationSetFrameAction", automation.Action, cg.Parented.template(Animation_)
|
||||||
)
|
)
|
||||||
|
|
||||||
TYPED_FILE_SCHEMA = cv.typed_schema(
|
CONFIG_SCHEMA = espImage.IMAGE_SCHEMA.extend(
|
||||||
{
|
{
|
||||||
SOURCE_LOCAL: LOCAL_SCHEMA,
|
cv.Required(CONF_ID): cv.declare_id(Animation_),
|
||||||
SOURCE_WEB: WEB_SCHEMA,
|
cv.Optional(CONF_LOOP): cv.All(
|
||||||
},
|
|
||||||
key=CONF_SOURCE,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _file_schema(value):
|
|
||||||
if isinstance(value, str):
|
|
||||||
return validate_file_shorthand(value)
|
|
||||||
return TYPED_FILE_SCHEMA(value)
|
|
||||||
|
|
||||||
|
|
||||||
FILE_SCHEMA = cv.Schema(_file_schema)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_file_shorthand(value):
|
|
||||||
value = cv.string_strict(value)
|
|
||||||
if value.startswith("http://") or value.startswith("https://"):
|
|
||||||
return FILE_SCHEMA(
|
|
||||||
{
|
{
|
||||||
CONF_SOURCE: SOURCE_WEB,
|
cv.Optional(CONF_START_FRAME, default=0): cv.positive_int,
|
||||||
CONF_URL: value,
|
cv.Optional(CONF_END_FRAME): cv.positive_int,
|
||||||
|
cv.Optional(CONF_REPEAT): cv.positive_int,
|
||||||
}
|
}
|
||||||
)
|
),
|
||||||
return FILE_SCHEMA(
|
},
|
||||||
{
|
|
||||||
CONF_SOURCE: SOURCE_LOCAL,
|
|
||||||
CONF_PATH: value,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def validate_cross_dependencies(config):
|
|
||||||
"""
|
|
||||||
Validate fields whose possible values depend on other fields.
|
|
||||||
For example, validate that explicitly transparent image types
|
|
||||||
have "use_transparency" set to True.
|
|
||||||
Also set the default value for those kind of dependent fields.
|
|
||||||
"""
|
|
||||||
image_type = config[CONF_TYPE]
|
|
||||||
is_transparent_type = image_type in ["TRANSPARENT_BINARY", "RGBA"]
|
|
||||||
# If the use_transparency option was not specified, set the default depending on the image type
|
|
||||||
if CONF_USE_TRANSPARENCY not in config:
|
|
||||||
config[CONF_USE_TRANSPARENCY] = is_transparent_type
|
|
||||||
|
|
||||||
if is_transparent_type and not config[CONF_USE_TRANSPARENCY]:
|
|
||||||
raise cv.Invalid(f"Image type {image_type} must always be transparent.")
|
|
||||||
|
|
||||||
return config
|
|
||||||
|
|
||||||
|
|
||||||
ANIMATION_SCHEMA = cv.Schema(
|
|
||||||
cv.All(
|
|
||||||
{
|
|
||||||
cv.Required(CONF_ID): cv.declare_id(Animation_),
|
|
||||||
cv.Required(CONF_FILE): FILE_SCHEMA,
|
|
||||||
cv.Optional(CONF_RESIZE): cv.dimensions,
|
|
||||||
cv.Optional(CONF_TYPE, default="BINARY"): cv.enum(
|
|
||||||
espImage.IMAGE_TYPE, upper=True
|
|
||||||
),
|
|
||||||
# Not setting default here on purpose; the default depends on the image type,
|
|
||||||
# and thus will be set in the "validate_cross_dependencies" validator.
|
|
||||||
cv.Optional(CONF_USE_TRANSPARENCY): cv.boolean,
|
|
||||||
cv.Optional(CONF_LOOP): cv.All(
|
|
||||||
{
|
|
||||||
cv.Optional(CONF_START_FRAME, default=0): cv.positive_int,
|
|
||||||
cv.Optional(CONF_END_FRAME): cv.positive_int,
|
|
||||||
cv.Optional(CONF_REPEAT): cv.positive_int,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
cv.GenerateID(CONF_RAW_DATA_ID): cv.declare_id(cg.uint8),
|
|
||||||
},
|
|
||||||
validate_cross_dependencies,
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA)
|
|
||||||
|
|
||||||
NEXT_FRAME_SCHEMA = automation.maybe_simple_id(
|
NEXT_FRAME_SCHEMA = automation.maybe_simple_id(
|
||||||
{
|
{
|
||||||
@@ -165,180 +80,26 @@ async def animation_action_to_code(config, action_id, template_arg, args):
|
|||||||
|
|
||||||
|
|
||||||
async def to_code(config):
|
async def to_code(config):
|
||||||
from PIL import Image
|
(
|
||||||
|
prog_arr,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
image_type,
|
||||||
|
trans_value,
|
||||||
|
frame_count,
|
||||||
|
) = await espImage.write_image(config, all_frames=True)
|
||||||
|
|
||||||
conf_file = config[CONF_FILE]
|
|
||||||
if conf_file[CONF_SOURCE] == SOURCE_LOCAL:
|
|
||||||
path = CORE.relative_config_path(conf_file[CONF_PATH])
|
|
||||||
elif conf_file[CONF_SOURCE] == SOURCE_WEB:
|
|
||||||
path = espImage.compute_local_image_path(conf_file).as_posix()
|
|
||||||
else:
|
|
||||||
raise core.EsphomeError(f"Unknown animation source: {conf_file[CONF_SOURCE]}")
|
|
||||||
|
|
||||||
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:
|
|
||||||
new_width_max, new_height_max = config[CONF_RESIZE]
|
|
||||||
ratio = min(new_width_max / width, new_height_max / height)
|
|
||||||
width, height = int(width * ratio), int(height * ratio)
|
|
||||||
elif width > 500 or height > 500:
|
|
||||||
_LOGGER.warning(
|
|
||||||
'The image "%s" you requested is very big. Please consider'
|
|
||||||
" using the resize parameter.",
|
|
||||||
path,
|
|
||||||
)
|
|
||||||
|
|
||||||
transparent = config[CONF_USE_TRANSPARENCY]
|
|
||||||
|
|
||||||
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("LA", dither=Image.Dither.NONE)
|
|
||||||
if CONF_RESIZE in config:
|
|
||||||
frame = frame.resize([width, height])
|
|
||||||
pixels = list(frame.getdata())
|
|
||||||
if len(pixels) != height * width:
|
|
||||||
raise core.EsphomeError(
|
|
||||||
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
|
|
||||||
)
|
|
||||||
for pix, a in pixels:
|
|
||||||
if transparent:
|
|
||||||
if pix == 1:
|
|
||||||
pix = 0
|
|
||||||
if a < 0x80:
|
|
||||||
pix = 1
|
|
||||||
|
|
||||||
data[pos] = pix
|
|
||||||
pos += 1
|
|
||||||
|
|
||||||
elif config[CONF_TYPE] == "RGBA":
|
|
||||||
data = [0 for _ in range(height * width * 4 * frames)]
|
|
||||||
pos = 0
|
|
||||||
for frameIndex in range(frames):
|
|
||||||
image.seek(frameIndex)
|
|
||||||
frame = image.convert("RGBA")
|
|
||||||
if CONF_RESIZE in config:
|
|
||||||
frame = frame.resize([width, height])
|
|
||||||
pixels = list(frame.getdata())
|
|
||||||
if len(pixels) != height * width:
|
|
||||||
raise core.EsphomeError(
|
|
||||||
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
|
|
||||||
)
|
|
||||||
for pix in pixels:
|
|
||||||
data[pos] = pix[0]
|
|
||||||
pos += 1
|
|
||||||
data[pos] = pix[1]
|
|
||||||
pos += 1
|
|
||||||
data[pos] = pix[2]
|
|
||||||
pos += 1
|
|
||||||
data[pos] = pix[3]
|
|
||||||
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("RGBA")
|
|
||||||
if CONF_RESIZE in config:
|
|
||||||
frame = frame.resize([width, height])
|
|
||||||
pixels = list(frame.getdata())
|
|
||||||
if len(pixels) != height * width:
|
|
||||||
raise core.EsphomeError(
|
|
||||||
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
|
|
||||||
)
|
|
||||||
for r, g, b, a in pixels:
|
|
||||||
if transparent:
|
|
||||||
if r == 0 and g == 0 and b == 1:
|
|
||||||
b = 0
|
|
||||||
if a < 0x80:
|
|
||||||
r = 0
|
|
||||||
g = 0
|
|
||||||
b = 1
|
|
||||||
|
|
||||||
data[pos] = r
|
|
||||||
pos += 1
|
|
||||||
data[pos] = g
|
|
||||||
pos += 1
|
|
||||||
data[pos] = b
|
|
||||||
pos += 1
|
|
||||||
|
|
||||||
elif config[CONF_TYPE] in ["RGB565", "TRANSPARENT_IMAGE"]:
|
|
||||||
bytes_per_pixel = 3 if transparent else 2
|
|
||||||
data = [0 for _ in range(height * width * bytes_per_pixel * frames)]
|
|
||||||
pos = 0
|
|
||||||
for frameIndex in range(frames):
|
|
||||||
image.seek(frameIndex)
|
|
||||||
frame = image.convert("RGBA")
|
|
||||||
if CONF_RESIZE in config:
|
|
||||||
frame = frame.resize([width, height])
|
|
||||||
pixels = list(frame.getdata())
|
|
||||||
if len(pixels) != height * width:
|
|
||||||
raise core.EsphomeError(
|
|
||||||
f"Unexpected number of pixels in {path} frame {frameIndex}: ({len(pixels)} != {height * width})"
|
|
||||||
)
|
|
||||||
for r, g, b, a in pixels:
|
|
||||||
R = r >> 3
|
|
||||||
G = g >> 2
|
|
||||||
B = b >> 3
|
|
||||||
rgb = (R << 11) | (G << 5) | B
|
|
||||||
data[pos] = rgb >> 8
|
|
||||||
pos += 1
|
|
||||||
data[pos] = rgb & 0xFF
|
|
||||||
pos += 1
|
|
||||||
if transparent:
|
|
||||||
data[pos] = a
|
|
||||||
pos += 1
|
|
||||||
|
|
||||||
elif config[CONF_TYPE] in ["BINARY", "TRANSPARENT_BINARY"]:
|
|
||||||
width8 = ((width + 7) // 8) * 8
|
|
||||||
data = [0 for _ in range((height * width8 // 8) * frames)]
|
|
||||||
for frameIndex in range(frames):
|
|
||||||
image.seek(frameIndex)
|
|
||||||
if transparent:
|
|
||||||
alpha = image.split()[-1]
|
|
||||||
has_alpha = alpha.getextrema()[0] < 0xFF
|
|
||||||
else:
|
|
||||||
has_alpha = False
|
|
||||||
frame = image.convert("1", dither=Image.Dither.NONE)
|
|
||||||
if CONF_RESIZE in config:
|
|
||||||
frame = frame.resize([width, height])
|
|
||||||
if transparent:
|
|
||||||
alpha = alpha.resize([width, height])
|
|
||||||
for x, y in [(i, j) for i in range(width) for j in range(height)]:
|
|
||||||
if transparent and has_alpha:
|
|
||||||
if not alpha.getpixel((x, y)):
|
|
||||||
continue
|
|
||||||
elif frame.getpixel((x, y)):
|
|
||||||
continue
|
|
||||||
|
|
||||||
pos = x + y * width8 + (height * width8 * frameIndex)
|
|
||||||
data[pos // 8] |= 0x80 >> (pos % 8)
|
|
||||||
else:
|
|
||||||
raise core.EsphomeError(
|
|
||||||
f"Animation f{config[CONF_ID]} has not supported type {config[CONF_TYPE]}."
|
|
||||||
)
|
|
||||||
|
|
||||||
rhs = [HexInt(x) for x in data]
|
|
||||||
prog_arr = cg.progmem_array(config[CONF_RAW_DATA_ID], rhs)
|
|
||||||
var = cg.new_Pvariable(
|
var = cg.new_Pvariable(
|
||||||
config[CONF_ID],
|
config[CONF_ID],
|
||||||
prog_arr,
|
prog_arr,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
frames,
|
frame_count,
|
||||||
espImage.IMAGE_TYPE[config[CONF_TYPE]],
|
image_type,
|
||||||
|
trans_value,
|
||||||
)
|
)
|
||||||
cg.add(var.set_transparency(transparent))
|
|
||||||
if loop_config := config.get(CONF_LOOP):
|
if loop_config := config.get(CONF_LOOP):
|
||||||
start = loop_config[CONF_START_FRAME]
|
start = loop_config[CONF_START_FRAME]
|
||||||
end = loop_config.get(CONF_END_FRAME, frames)
|
end = loop_config.get(CONF_END_FRAME, frame_count)
|
||||||
count = loop_config.get(CONF_REPEAT, -1)
|
count = loop_config.get(CONF_REPEAT, -1)
|
||||||
cg.add(var.set_loop(start, end, count))
|
cg.add(var.set_loop(start, end, count))
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ namespace esphome {
|
|||||||
namespace animation {
|
namespace animation {
|
||||||
|
|
||||||
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count,
|
Animation::Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count,
|
||||||
image::ImageType type)
|
image::ImageType type, image::Transparency transparent)
|
||||||
: Image(data_start, width, height, type),
|
: Image(data_start, width, height, type, transparent),
|
||||||
animation_data_start_(data_start),
|
animation_data_start_(data_start),
|
||||||
current_frame_(0),
|
current_frame_(0),
|
||||||
animation_frame_count_(animation_frame_count),
|
animation_frame_count_(animation_frame_count),
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ namespace animation {
|
|||||||
|
|
||||||
class Animation : public image::Image {
|
class Animation : public image::Image {
|
||||||
public:
|
public:
|
||||||
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type);
|
Animation(const uint8_t *data_start, int width, int height, uint32_t animation_frame_count, image::ImageType type,
|
||||||
|
image::Transparency transparent);
|
||||||
|
|
||||||
uint32_t get_animation_frame_count() const;
|
uint32_t get_animation_frame_count() const;
|
||||||
int get_current_frame() const;
|
int get_current_frame() const;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import ble_client, climate
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.components import climate, ble_client
|
|
||||||
from esphome.const import CONF_ID, CONF_UNIT_OF_MEASUREMENT
|
from esphome.const import CONF_ID, CONF_UNIT_OF_MEASUREMENT
|
||||||
|
|
||||||
UNITS = {
|
UNITS = {
|
||||||
|
|||||||
@@ -122,7 +122,8 @@ void APDS9306::update() {
|
|||||||
|
|
||||||
this->status_clear_warning();
|
this->status_clear_warning();
|
||||||
|
|
||||||
if (!(status &= 0b00001000)) { // No new data
|
status &= 0b00001000;
|
||||||
|
if (!status) { // No new data
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
# https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf
|
# https://www.mouser.ca/datasheet/2/678/AVGO_S_A0002854364_1-2574547.pdf
|
||||||
|
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import i2c, sensor
|
from esphome.components import i2c, sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_GAIN,
|
CONF_GAIN,
|
||||||
DEVICE_CLASS_ILLUMINANCE,
|
DEVICE_CLASS_ILLUMINANCE,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import i2c
|
from esphome.components import i2c
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID
|
from esphome.const import CONF_ID
|
||||||
|
|
||||||
DEPENDENCIES = ["i2c"]
|
DEPENDENCIES = ["i2c"]
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import binary_sensor
|
from esphome.components import binary_sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_DIRECTION, DEVICE_CLASS_MOVING
|
from esphome.const import CONF_DIRECTION, DEVICE_CLASS_MOVING
|
||||||
|
|
||||||
from . import APDS9960, CONF_APDS9960_ID
|
from . import APDS9960, CONF_APDS9960_ID
|
||||||
|
|
||||||
DEPENDENCIES = ["apds9960"]
|
DEPENDENCIES = ["apds9960"]
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
import esphome.config_validation as cv
|
|
||||||
from esphome.components import sensor
|
from esphome.components import sensor
|
||||||
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_TYPE,
|
CONF_TYPE,
|
||||||
|
ICON_LIGHTBULB,
|
||||||
STATE_CLASS_MEASUREMENT,
|
STATE_CLASS_MEASUREMENT,
|
||||||
UNIT_PERCENT,
|
UNIT_PERCENT,
|
||||||
ICON_LIGHTBULB,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import APDS9960, CONF_APDS9960_ID
|
from . import APDS9960, CONF_APDS9960_ID
|
||||||
|
|
||||||
DEPENDENCIES = ["apds9960"]
|
DEPENDENCIES = ["apds9960"]
|
||||||
|
|||||||
@@ -82,6 +82,19 @@ ACTIONS_SCHEMA = automation.validate_automation(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ENCRYPTION_SCHEMA = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.Optional(CONF_KEY): validate_encryption_key,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _encryption_schema(config):
|
||||||
|
if config is None:
|
||||||
|
config = {}
|
||||||
|
return ENCRYPTION_SCHEMA(config)
|
||||||
|
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
@@ -95,11 +108,7 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
CONF_SERVICES, group_of_exclusion=CONF_ACTIONS
|
CONF_SERVICES, group_of_exclusion=CONF_ACTIONS
|
||||||
): ACTIONS_SCHEMA,
|
): ACTIONS_SCHEMA,
|
||||||
cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
|
cv.Exclusive(CONF_ACTIONS, group_of_exclusion=CONF_ACTIONS): ACTIONS_SCHEMA,
|
||||||
cv.Optional(CONF_ENCRYPTION): cv.Schema(
|
cv.Optional(CONF_ENCRYPTION): _encryption_schema,
|
||||||
{
|
|
||||||
cv.Required(CONF_KEY): validate_encryption_key,
|
|
||||||
}
|
|
||||||
),
|
|
||||||
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
|
cv.Optional(CONF_ON_CLIENT_CONNECTED): automation.validate_automation(
|
||||||
single=True
|
single=True
|
||||||
),
|
),
|
||||||
@@ -151,9 +160,17 @@ async def to_code(config):
|
|||||||
config[CONF_ON_CLIENT_DISCONNECTED],
|
config[CONF_ON_CLIENT_DISCONNECTED],
|
||||||
)
|
)
|
||||||
|
|
||||||
if encryption_config := config.get(CONF_ENCRYPTION):
|
if (encryption_config := config.get(CONF_ENCRYPTION, None)) is not None:
|
||||||
decoded = base64.b64decode(encryption_config[CONF_KEY])
|
if key := encryption_config.get(CONF_KEY):
|
||||||
cg.add(var.set_noise_psk(list(decoded)))
|
decoded = base64.b64decode(key)
|
||||||
|
cg.add(var.set_noise_psk(list(decoded)))
|
||||||
|
else:
|
||||||
|
# No key provided, but encryption desired
|
||||||
|
# This will allow a plaintext client to provide a noise key,
|
||||||
|
# send it to the device, and then switch to noise.
|
||||||
|
# The key will be saved in flash and used for future connections
|
||||||
|
# and plaintext disabled. Only a factory reset can remove it.
|
||||||
|
cg.add_define("USE_API_PLAINTEXT")
|
||||||
cg.add_define("USE_API_NOISE")
|
cg.add_define("USE_API_NOISE")
|
||||||
cg.add_library("esphome/noise-c", "0.1.6")
|
cg.add_library("esphome/noise-c", "0.1.6")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -31,24 +31,26 @@ service APIConnection {
|
|||||||
option (needs_authentication) = false;
|
option (needs_authentication) = false;
|
||||||
}
|
}
|
||||||
rpc execute_service (ExecuteServiceRequest) returns (void) {}
|
rpc execute_service (ExecuteServiceRequest) returns (void) {}
|
||||||
|
rpc noise_encryption_set_key (NoiseEncryptionSetKeyRequest) returns (NoiseEncryptionSetKeyResponse) {}
|
||||||
|
|
||||||
rpc cover_command (CoverCommandRequest) returns (void) {}
|
rpc button_command (ButtonCommandRequest) returns (void) {}
|
||||||
rpc fan_command (FanCommandRequest) returns (void) {}
|
|
||||||
rpc light_command (LightCommandRequest) returns (void) {}
|
|
||||||
rpc switch_command (SwitchCommandRequest) returns (void) {}
|
|
||||||
rpc camera_image (CameraImageRequest) returns (void) {}
|
rpc camera_image (CameraImageRequest) returns (void) {}
|
||||||
rpc climate_command (ClimateCommandRequest) returns (void) {}
|
rpc climate_command (ClimateCommandRequest) returns (void) {}
|
||||||
rpc number_command (NumberCommandRequest) returns (void) {}
|
rpc cover_command (CoverCommandRequest) returns (void) {}
|
||||||
rpc text_command (TextCommandRequest) returns (void) {}
|
|
||||||
rpc select_command (SelectCommandRequest) returns (void) {}
|
|
||||||
rpc button_command (ButtonCommandRequest) returns (void) {}
|
|
||||||
rpc lock_command (LockCommandRequest) returns (void) {}
|
|
||||||
rpc valve_command (ValveCommandRequest) returns (void) {}
|
|
||||||
rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
|
|
||||||
rpc date_command (DateCommandRequest) returns (void) {}
|
rpc date_command (DateCommandRequest) returns (void) {}
|
||||||
rpc time_command (TimeCommandRequest) returns (void) {}
|
|
||||||
rpc datetime_command (DateTimeCommandRequest) returns (void) {}
|
rpc datetime_command (DateTimeCommandRequest) returns (void) {}
|
||||||
|
rpc fan_command (FanCommandRequest) returns (void) {}
|
||||||
|
rpc light_command (LightCommandRequest) returns (void) {}
|
||||||
|
rpc lock_command (LockCommandRequest) returns (void) {}
|
||||||
|
rpc media_player_command (MediaPlayerCommandRequest) returns (void) {}
|
||||||
|
rpc number_command (NumberCommandRequest) returns (void) {}
|
||||||
|
rpc select_command (SelectCommandRequest) returns (void) {}
|
||||||
|
rpc siren_command (SirenCommandRequest) returns (void) {}
|
||||||
|
rpc switch_command (SwitchCommandRequest) returns (void) {}
|
||||||
|
rpc text_command (TextCommandRequest) returns (void) {}
|
||||||
|
rpc time_command (TimeCommandRequest) returns (void) {}
|
||||||
rpc update_command (UpdateCommandRequest) returns (void) {}
|
rpc update_command (UpdateCommandRequest) returns (void) {}
|
||||||
|
rpc valve_command (ValveCommandRequest) returns (void) {}
|
||||||
|
|
||||||
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
rpc subscribe_bluetooth_le_advertisements(SubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
||||||
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
|
rpc bluetooth_device_request(BluetoothDeviceRequest) returns (void) {}
|
||||||
@@ -60,6 +62,7 @@ service APIConnection {
|
|||||||
rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {}
|
rpc bluetooth_gatt_notify(BluetoothGATTNotifyRequest) returns (void) {}
|
||||||
rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {}
|
rpc subscribe_bluetooth_connections_free(SubscribeBluetoothConnectionsFreeRequest) returns (BluetoothConnectionsFreeResponse) {}
|
||||||
rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
rpc unsubscribe_bluetooth_le_advertisements(UnsubscribeBluetoothLEAdvertisementsRequest) returns (void) {}
|
||||||
|
rpc bluetooth_scanner_set_mode(BluetoothScannerSetModeRequest) returns (void) {}
|
||||||
|
|
||||||
rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {}
|
rpc subscribe_voice_assistant(SubscribeVoiceAssistantRequest) returns (void) {}
|
||||||
rpc voice_assistant_get_configuration(VoiceAssistantConfigurationRequest) returns (VoiceAssistantConfigurationResponse) {}
|
rpc voice_assistant_get_configuration(VoiceAssistantConfigurationRequest) returns (VoiceAssistantConfigurationResponse) {}
|
||||||
@@ -227,6 +230,12 @@ message DeviceInfoResponse {
|
|||||||
uint32 voice_assistant_feature_flags = 17;
|
uint32 voice_assistant_feature_flags = 17;
|
||||||
|
|
||||||
string suggested_area = 16;
|
string suggested_area = 16;
|
||||||
|
|
||||||
|
// The Bluetooth mac address of the device. For example "AC:BC:32:89:0E:AA"
|
||||||
|
string bluetooth_mac_address = 18;
|
||||||
|
|
||||||
|
// Supports receiving and saving api encryption key
|
||||||
|
bool api_encryption_supported = 19;
|
||||||
}
|
}
|
||||||
|
|
||||||
message ListEntitiesRequest {
|
message ListEntitiesRequest {
|
||||||
@@ -647,10 +656,27 @@ message SubscribeLogsResponse {
|
|||||||
option (no_delay) = false;
|
option (no_delay) = false;
|
||||||
|
|
||||||
LogLevel level = 1;
|
LogLevel level = 1;
|
||||||
string message = 3;
|
bytes message = 3;
|
||||||
bool send_failed = 4;
|
bool send_failed = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== NOISE ENCRYPTION ====================
|
||||||
|
message NoiseEncryptionSetKeyRequest {
|
||||||
|
option (id) = 124;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_API_NOISE";
|
||||||
|
|
||||||
|
bytes key = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NoiseEncryptionSetKeyResponse {
|
||||||
|
option (id) = 125;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_API_NOISE";
|
||||||
|
|
||||||
|
bool success = 1;
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== HOMEASSISTANT.SERVICE ====================
|
// ==================== HOMEASSISTANT.SERVICE ====================
|
||||||
message SubscribeHomeassistantServicesRequest {
|
message SubscribeHomeassistantServicesRequest {
|
||||||
option (id) = 34;
|
option (id) = 34;
|
||||||
@@ -886,6 +912,7 @@ message ClimateStateResponse {
|
|||||||
float target_temperature = 4;
|
float target_temperature = 4;
|
||||||
float target_temperature_low = 5;
|
float target_temperature_low = 5;
|
||||||
float target_temperature_high = 6;
|
float target_temperature_high = 6;
|
||||||
|
// For older peers, equal to preset == CLIMATE_PRESET_AWAY
|
||||||
bool unused_legacy_away = 7;
|
bool unused_legacy_away = 7;
|
||||||
ClimateAction action = 8;
|
ClimateAction action = 8;
|
||||||
ClimateFanMode fan_mode = 9;
|
ClimateFanMode fan_mode = 9;
|
||||||
@@ -911,6 +938,7 @@ message ClimateCommandRequest {
|
|||||||
float target_temperature_low = 7;
|
float target_temperature_low = 7;
|
||||||
bool has_target_temperature_high = 8;
|
bool has_target_temperature_high = 8;
|
||||||
float target_temperature_high = 9;
|
float target_temperature_high = 9;
|
||||||
|
// legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
|
||||||
bool unused_has_legacy_away = 10;
|
bool unused_has_legacy_away = 10;
|
||||||
bool unused_legacy_away = 11;
|
bool unused_legacy_away = 11;
|
||||||
bool has_fan_mode = 12;
|
bool has_fan_mode = 12;
|
||||||
@@ -1013,6 +1041,49 @@ message SelectCommandRequest {
|
|||||||
string state = 2;
|
string state = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== SIREN ====================
|
||||||
|
message ListEntitiesSirenResponse {
|
||||||
|
option (id) = 55;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_SIREN";
|
||||||
|
|
||||||
|
string object_id = 1;
|
||||||
|
fixed32 key = 2;
|
||||||
|
string name = 3;
|
||||||
|
string unique_id = 4;
|
||||||
|
|
||||||
|
string icon = 5;
|
||||||
|
bool disabled_by_default = 6;
|
||||||
|
repeated string tones = 7;
|
||||||
|
bool supports_duration = 8;
|
||||||
|
bool supports_volume = 9;
|
||||||
|
EntityCategory entity_category = 10;
|
||||||
|
}
|
||||||
|
message SirenStateResponse {
|
||||||
|
option (id) = 56;
|
||||||
|
option (source) = SOURCE_SERVER;
|
||||||
|
option (ifdef) = "USE_SIREN";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
bool state = 2;
|
||||||
|
}
|
||||||
|
message SirenCommandRequest {
|
||||||
|
option (id) = 57;
|
||||||
|
option (source) = SOURCE_CLIENT;
|
||||||
|
option (ifdef) = "USE_SIREN";
|
||||||
|
option (no_delay) = true;
|
||||||
|
|
||||||
|
fixed32 key = 1;
|
||||||
|
bool has_state = 2;
|
||||||
|
bool state = 3;
|
||||||
|
bool has_tone = 4;
|
||||||
|
string tone = 5;
|
||||||
|
bool has_duration = 6;
|
||||||
|
uint32 duration = 7;
|
||||||
|
bool has_volume = 8;
|
||||||
|
float volume = 9;
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== LOCK ====================
|
// ==================== LOCK ====================
|
||||||
enum LockState {
|
enum LockState {
|
||||||
@@ -1182,8 +1253,8 @@ message SubscribeBluetoothLEAdvertisementsRequest {
|
|||||||
|
|
||||||
message BluetoothServiceData {
|
message BluetoothServiceData {
|
||||||
string uuid = 1;
|
string uuid = 1;
|
||||||
repeated uint32 legacy_data = 2 [deprecated = true];
|
repeated uint32 legacy_data = 2 [deprecated = true]; // Removed in api version 1.7
|
||||||
bytes data = 3; // Changed in proto version 1.7
|
bytes data = 3; // Added in api version 1.7
|
||||||
}
|
}
|
||||||
message BluetoothLEAdvertisementResponse {
|
message BluetoothLEAdvertisementResponse {
|
||||||
option (id) = 67;
|
option (id) = 67;
|
||||||
@@ -1192,7 +1263,7 @@ message BluetoothLEAdvertisementResponse {
|
|||||||
option (no_delay) = true;
|
option (no_delay) = true;
|
||||||
|
|
||||||
uint64 address = 1;
|
uint64 address = 1;
|
||||||
string name = 2;
|
bytes name = 2;
|
||||||
sint32 rssi = 3;
|
sint32 rssi = 3;
|
||||||
|
|
||||||
repeated string service_uuids = 4;
|
repeated string service_uuids = 4;
|
||||||
@@ -1381,6 +1452,7 @@ message BluetoothConnectionsFreeResponse {
|
|||||||
|
|
||||||
uint32 free = 1;
|
uint32 free = 1;
|
||||||
uint32 limit = 2;
|
uint32 limit = 2;
|
||||||
|
repeated uint64 allocated = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BluetoothGATTErrorResponse {
|
message BluetoothGATTErrorResponse {
|
||||||
@@ -1447,7 +1519,38 @@ message BluetoothDeviceClearCacheResponse {
|
|||||||
int32 error = 3;
|
int32 error = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== PUSH TO TALK ====================
|
enum BluetoothScannerState {
|
||||||
|
BLUETOOTH_SCANNER_STATE_IDLE = 0;
|
||||||
|
BLUETOOTH_SCANNER_STATE_STARTING = 1;
|
||||||
|
BLUETOOTH_SCANNER_STATE_RUNNING = 2;
|
||||||
|
BLUETOOTH_SCANNER_STATE_FAILED = 3;
|
||||||
|
BLUETOOTH_SCANNER_STATE_STOPPING = 4;
|
||||||
|
BLUETOOTH_SCANNER_STATE_STOPPED = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum BluetoothScannerMode {
|
||||||
|
BLUETOOTH_SCANNER_MODE_PASSIVE = 0;
|
||||||
|
BLUETOOTH_SCANNER_MODE_ACTIVE = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BluetoothScannerStateResponse {
|
||||||
|
option(id) = 126;
|
||||||
|
option(source) = SOURCE_SERVER;
|
||||||
|
option(ifdef) = "USE_BLUETOOTH_PROXY";
|
||||||
|
|
||||||
|
BluetoothScannerState state = 1;
|
||||||
|
BluetoothScannerMode mode = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BluetoothScannerSetModeRequest {
|
||||||
|
option(id) = 127;
|
||||||
|
option(source) = SOURCE_CLIENT;
|
||||||
|
option(ifdef) = "USE_BLUETOOTH_PROXY";
|
||||||
|
|
||||||
|
BluetoothScannerMode mode = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== VOICE ASSISTANT ====================
|
||||||
enum VoiceAssistantSubscribeFlag {
|
enum VoiceAssistantSubscribeFlag {
|
||||||
VOICE_ASSISTANT_SUBSCRIBE_NONE = 0;
|
VOICE_ASSISTANT_SUBSCRIBE_NONE = 0;
|
||||||
VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO = 1;
|
VOICE_ASSISTANT_SUBSCRIBE_API_AUDIO = 1;
|
||||||
@@ -1563,6 +1666,8 @@ message VoiceAssistantAnnounceRequest {
|
|||||||
|
|
||||||
string media_id = 1;
|
string media_id = 1;
|
||||||
string text = 2;
|
string text = 2;
|
||||||
|
string preannounce_media_id = 3;
|
||||||
|
bool start_conversation = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
message VoiceAssistantAnnounceFinished {
|
message VoiceAssistantAnnounceFinished {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -14,6 +14,46 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
|
using send_message_t = bool(APIConnection *, void *);
|
||||||
|
|
||||||
|
/*
|
||||||
|
This class holds a pointer to the source component that wants to publish a message, and a pointer to a function that
|
||||||
|
will lazily publish that message. The two pointers allow dedup in the deferred queue if multiple publishes for the
|
||||||
|
same component are backed up, and take up only 8 bytes of memory. The entry in the deferred queue (a std::vector) is
|
||||||
|
the DeferredMessage instance itself (not a pointer to one elsewhere in heap) so still only 8 bytes per entry. Even
|
||||||
|
100 backed up messages (you'd have to have at least 100 sensors publishing because of dedup) would take up only 0.8
|
||||||
|
kB.
|
||||||
|
*/
|
||||||
|
class DeferredMessageQueue {
|
||||||
|
struct DeferredMessage {
|
||||||
|
friend class DeferredMessageQueue;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void *source_;
|
||||||
|
send_message_t *send_message_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
DeferredMessage(void *source, send_message_t *send_message) : source_(source), send_message_(send_message) {}
|
||||||
|
bool operator==(const DeferredMessage &test) const {
|
||||||
|
return (source_ == test.source_ && send_message_ == test.send_message_);
|
||||||
|
}
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// vector is used very specifically for its zero memory overhead even though items are popped from the front (memory
|
||||||
|
// footprint is more important than speed here)
|
||||||
|
std::vector<DeferredMessage> deferred_queue_;
|
||||||
|
APIConnection *api_connection_;
|
||||||
|
|
||||||
|
// helper for allowing only unique entries in the queue
|
||||||
|
void dmq_push_back_with_dedup_(void *source, send_message_t *send_message);
|
||||||
|
|
||||||
|
public:
|
||||||
|
DeferredMessageQueue(APIConnection *api_connection) : api_connection_(api_connection) {}
|
||||||
|
void process_queue();
|
||||||
|
void defer(void *source, send_message_t *send_message);
|
||||||
|
};
|
||||||
|
|
||||||
class APIConnection : public APIServerConnection {
|
class APIConnection : public APIServerConnection {
|
||||||
public:
|
public:
|
||||||
APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent);
|
APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent);
|
||||||
@@ -28,96 +68,140 @@ class APIConnection : public APIServerConnection {
|
|||||||
}
|
}
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state);
|
bool send_binary_sensor_state(binary_sensor::BinarySensor *binary_sensor, bool state);
|
||||||
bool send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor);
|
void send_binary_sensor_info(binary_sensor::BinarySensor *binary_sensor);
|
||||||
|
static bool try_send_binary_sensor_state(APIConnection *api, void *v_binary_sensor);
|
||||||
|
static bool try_send_binary_sensor_state(APIConnection *api, binary_sensor::BinarySensor *binary_sensor, bool state);
|
||||||
|
static bool try_send_binary_sensor_info(APIConnection *api, void *v_binary_sensor);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
bool send_cover_state(cover::Cover *cover);
|
bool send_cover_state(cover::Cover *cover);
|
||||||
bool send_cover_info(cover::Cover *cover);
|
void send_cover_info(cover::Cover *cover);
|
||||||
|
static bool try_send_cover_state(APIConnection *api, void *v_cover);
|
||||||
|
static bool try_send_cover_info(APIConnection *api, void *v_cover);
|
||||||
void cover_command(const CoverCommandRequest &msg) override;
|
void cover_command(const CoverCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool send_fan_state(fan::Fan *fan);
|
bool send_fan_state(fan::Fan *fan);
|
||||||
bool send_fan_info(fan::Fan *fan);
|
void send_fan_info(fan::Fan *fan);
|
||||||
|
static bool try_send_fan_state(APIConnection *api, void *v_fan);
|
||||||
|
static bool try_send_fan_info(APIConnection *api, void *v_fan);
|
||||||
void fan_command(const FanCommandRequest &msg) override;
|
void fan_command(const FanCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool send_light_state(light::LightState *light);
|
bool send_light_state(light::LightState *light);
|
||||||
bool send_light_info(light::LightState *light);
|
void send_light_info(light::LightState *light);
|
||||||
|
static bool try_send_light_state(APIConnection *api, void *v_light);
|
||||||
|
static bool try_send_light_info(APIConnection *api, void *v_light);
|
||||||
void light_command(const LightCommandRequest &msg) override;
|
void light_command(const LightCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
bool send_sensor_state(sensor::Sensor *sensor, float state);
|
bool send_sensor_state(sensor::Sensor *sensor, float state);
|
||||||
bool send_sensor_info(sensor::Sensor *sensor);
|
void send_sensor_info(sensor::Sensor *sensor);
|
||||||
|
static bool try_send_sensor_state(APIConnection *api, void *v_sensor);
|
||||||
|
static bool try_send_sensor_state(APIConnection *api, sensor::Sensor *sensor, float state);
|
||||||
|
static bool try_send_sensor_info(APIConnection *api, void *v_sensor);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
bool send_switch_state(switch_::Switch *a_switch, bool state);
|
bool send_switch_state(switch_::Switch *a_switch, bool state);
|
||||||
bool send_switch_info(switch_::Switch *a_switch);
|
void send_switch_info(switch_::Switch *a_switch);
|
||||||
|
static bool try_send_switch_state(APIConnection *api, void *v_a_switch);
|
||||||
|
static bool try_send_switch_state(APIConnection *api, switch_::Switch *a_switch, bool state);
|
||||||
|
static bool try_send_switch_info(APIConnection *api, void *v_a_switch);
|
||||||
void switch_command(const SwitchCommandRequest &msg) override;
|
void switch_command(const SwitchCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state);
|
bool send_text_sensor_state(text_sensor::TextSensor *text_sensor, std::string state);
|
||||||
bool send_text_sensor_info(text_sensor::TextSensor *text_sensor);
|
void send_text_sensor_info(text_sensor::TextSensor *text_sensor);
|
||||||
|
static bool try_send_text_sensor_state(APIConnection *api, void *v_text_sensor);
|
||||||
|
static bool try_send_text_sensor_state(APIConnection *api, text_sensor::TextSensor *text_sensor, std::string state);
|
||||||
|
static bool try_send_text_sensor_info(APIConnection *api, void *v_text_sensor);
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_ESP32_CAMERA
|
||||||
void send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image);
|
void set_camera_state(std::shared_ptr<esp32_camera::CameraImage> image);
|
||||||
bool send_camera_info(esp32_camera::ESP32Camera *camera);
|
void send_camera_info(esp32_camera::ESP32Camera *camera);
|
||||||
|
static bool try_send_camera_info(APIConnection *api, void *v_camera);
|
||||||
void camera_image(const CameraImageRequest &msg) override;
|
void camera_image(const CameraImageRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
bool send_climate_state(climate::Climate *climate);
|
bool send_climate_state(climate::Climate *climate);
|
||||||
bool send_climate_info(climate::Climate *climate);
|
void send_climate_info(climate::Climate *climate);
|
||||||
|
static bool try_send_climate_state(APIConnection *api, void *v_climate);
|
||||||
|
static bool try_send_climate_info(APIConnection *api, void *v_climate);
|
||||||
void climate_command(const ClimateCommandRequest &msg) override;
|
void climate_command(const ClimateCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
bool send_number_state(number::Number *number, float state);
|
bool send_number_state(number::Number *number, float state);
|
||||||
bool send_number_info(number::Number *number);
|
void send_number_info(number::Number *number);
|
||||||
|
static bool try_send_number_state(APIConnection *api, void *v_number);
|
||||||
|
static bool try_send_number_state(APIConnection *api, number::Number *number, float state);
|
||||||
|
static bool try_send_number_info(APIConnection *api, void *v_number);
|
||||||
void number_command(const NumberCommandRequest &msg) override;
|
void number_command(const NumberCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
bool send_date_state(datetime::DateEntity *date);
|
bool send_date_state(datetime::DateEntity *date);
|
||||||
bool send_date_info(datetime::DateEntity *date);
|
void send_date_info(datetime::DateEntity *date);
|
||||||
|
static bool try_send_date_state(APIConnection *api, void *v_date);
|
||||||
|
static bool try_send_date_info(APIConnection *api, void *v_date);
|
||||||
void date_command(const DateCommandRequest &msg) override;
|
void date_command(const DateCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_TIME
|
#ifdef USE_DATETIME_TIME
|
||||||
bool send_time_state(datetime::TimeEntity *time);
|
bool send_time_state(datetime::TimeEntity *time);
|
||||||
bool send_time_info(datetime::TimeEntity *time);
|
void send_time_info(datetime::TimeEntity *time);
|
||||||
|
static bool try_send_time_state(APIConnection *api, void *v_time);
|
||||||
|
static bool try_send_time_info(APIConnection *api, void *v_time);
|
||||||
void time_command(const TimeCommandRequest &msg) override;
|
void time_command(const TimeCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATETIME
|
#ifdef USE_DATETIME_DATETIME
|
||||||
bool send_datetime_state(datetime::DateTimeEntity *datetime);
|
bool send_datetime_state(datetime::DateTimeEntity *datetime);
|
||||||
bool send_datetime_info(datetime::DateTimeEntity *datetime);
|
void send_datetime_info(datetime::DateTimeEntity *datetime);
|
||||||
|
static bool try_send_datetime_state(APIConnection *api, void *v_datetime);
|
||||||
|
static bool try_send_datetime_info(APIConnection *api, void *v_datetime);
|
||||||
void datetime_command(const DateTimeCommandRequest &msg) override;
|
void datetime_command(const DateTimeCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool send_text_state(text::Text *text, std::string state);
|
bool send_text_state(text::Text *text, std::string state);
|
||||||
bool send_text_info(text::Text *text);
|
void send_text_info(text::Text *text);
|
||||||
|
static bool try_send_text_state(APIConnection *api, void *v_text);
|
||||||
|
static bool try_send_text_state(APIConnection *api, text::Text *text, std::string state);
|
||||||
|
static bool try_send_text_info(APIConnection *api, void *v_text);
|
||||||
void text_command(const TextCommandRequest &msg) override;
|
void text_command(const TextCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
bool send_select_state(select::Select *select, std::string state);
|
bool send_select_state(select::Select *select, std::string state);
|
||||||
bool send_select_info(select::Select *select);
|
void send_select_info(select::Select *select);
|
||||||
|
static bool try_send_select_state(APIConnection *api, void *v_select);
|
||||||
|
static bool try_send_select_state(APIConnection *api, select::Select *select, std::string state);
|
||||||
|
static bool try_send_select_info(APIConnection *api, void *v_select);
|
||||||
void select_command(const SelectCommandRequest &msg) override;
|
void select_command(const SelectCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
bool send_button_info(button::Button *button);
|
void send_button_info(button::Button *button);
|
||||||
|
static bool try_send_button_info(APIConnection *api, void *v_button);
|
||||||
void button_command(const ButtonCommandRequest &msg) override;
|
void button_command(const ButtonCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
|
bool send_lock_state(lock::Lock *a_lock, lock::LockState state);
|
||||||
bool send_lock_info(lock::Lock *a_lock);
|
void send_lock_info(lock::Lock *a_lock);
|
||||||
|
static bool try_send_lock_state(APIConnection *api, void *v_a_lock);
|
||||||
|
static bool try_send_lock_state(APIConnection *api, lock::Lock *a_lock, lock::LockState state);
|
||||||
|
static bool try_send_lock_info(APIConnection *api, void *v_a_lock);
|
||||||
void lock_command(const LockCommandRequest &msg) override;
|
void lock_command(const LockCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
bool send_valve_state(valve::Valve *valve);
|
bool send_valve_state(valve::Valve *valve);
|
||||||
bool send_valve_info(valve::Valve *valve);
|
void send_valve_info(valve::Valve *valve);
|
||||||
|
static bool try_send_valve_state(APIConnection *api, void *v_valve);
|
||||||
|
static bool try_send_valve_info(APIConnection *api, void *v_valve);
|
||||||
void valve_command(const ValveCommandRequest &msg) override;
|
void valve_command(const ValveCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
bool send_media_player_state(media_player::MediaPlayer *media_player);
|
bool send_media_player_state(media_player::MediaPlayer *media_player);
|
||||||
bool send_media_player_info(media_player::MediaPlayer *media_player);
|
void send_media_player_info(media_player::MediaPlayer *media_player);
|
||||||
|
static bool try_send_media_player_state(APIConnection *api, void *v_media_player);
|
||||||
|
static bool try_send_media_player_info(APIConnection *api, void *v_media_player);
|
||||||
void media_player_command(const MediaPlayerCommandRequest &msg) override;
|
void media_player_command(const MediaPlayerCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
bool send_log_message(int level, const char *tag, const char *line);
|
bool try_send_log_message(int level, const char *tag, const char *line);
|
||||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||||
if (!this->service_call_subscription_)
|
if (!this->service_call_subscription_)
|
||||||
return;
|
return;
|
||||||
@@ -137,6 +221,7 @@ class APIConnection : public APIServerConnection {
|
|||||||
void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override;
|
void bluetooth_gatt_notify(const BluetoothGATTNotifyRequest &msg) override;
|
||||||
BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
|
BluetoothConnectionsFreeResponse subscribe_bluetooth_connections_free(
|
||||||
const SubscribeBluetoothConnectionsFreeRequest &msg) override;
|
const SubscribeBluetoothConnectionsFreeRequest &msg) override;
|
||||||
|
void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) override;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_HOMEASSISTANT_TIME
|
#ifdef USE_HOMEASSISTANT_TIME
|
||||||
@@ -160,18 +245,25 @@ class APIConnection : public APIServerConnection {
|
|||||||
|
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
|
bool send_alarm_control_panel_state(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
|
||||||
bool send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
|
void send_alarm_control_panel_info(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel);
|
||||||
|
static bool try_send_alarm_control_panel_state(APIConnection *api, void *v_a_alarm_control_panel);
|
||||||
|
static bool try_send_alarm_control_panel_info(APIConnection *api, void *v_a_alarm_control_panel);
|
||||||
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
|
void alarm_control_panel_command(const AlarmControlPanelCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
bool send_event(event::Event *event, std::string event_type);
|
void send_event(event::Event *event, std::string event_type);
|
||||||
bool send_event_info(event::Event *event);
|
void send_event_info(event::Event *event);
|
||||||
|
static bool try_send_event(APIConnection *api, void *v_event);
|
||||||
|
static bool try_send_event(APIConnection *api, event::Event *event, std::string event_type);
|
||||||
|
static bool try_send_event_info(APIConnection *api, void *v_event);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
bool send_update_state(update::UpdateEntity *update);
|
bool send_update_state(update::UpdateEntity *update);
|
||||||
bool send_update_info(update::UpdateEntity *update);
|
void send_update_info(update::UpdateEntity *update);
|
||||||
|
static bool try_send_update_state(APIConnection *api, void *v_update);
|
||||||
|
static bool try_send_update_info(APIConnection *api, void *v_update);
|
||||||
void update_command(const UpdateCommandRequest &msg) override;
|
void update_command(const UpdateCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -209,6 +301,9 @@ class APIConnection : public APIServerConnection {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
void execute_service(const ExecuteServiceRequest &msg) override;
|
void execute_service(const ExecuteServiceRequest &msg) override;
|
||||||
|
#ifdef USE_API_NOISE
|
||||||
|
NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; }
|
bool is_authenticated() override { return this->connection_state_ == ConnectionState::AUTHENTICATED; }
|
||||||
bool is_connection_setup() override {
|
bool is_connection_setup() override {
|
||||||
@@ -217,9 +312,10 @@ class APIConnection : public APIServerConnection {
|
|||||||
void on_fatal_error() override;
|
void on_fatal_error() override;
|
||||||
void on_unauthenticated_access() override;
|
void on_unauthenticated_access() override;
|
||||||
void on_no_setup_connection() override;
|
void on_no_setup_connection() override;
|
||||||
ProtoWriteBuffer create_buffer() override {
|
ProtoWriteBuffer create_buffer(uint32_t reserve_size) override {
|
||||||
// FIXME: ensure no recursive writes can happen
|
// FIXME: ensure no recursive writes can happen
|
||||||
this->proto_write_buffer_.clear();
|
this->proto_write_buffer_.clear();
|
||||||
|
this->proto_write_buffer_.reserve(reserve_size);
|
||||||
return {&this->proto_write_buffer_};
|
return {&this->proto_write_buffer_};
|
||||||
}
|
}
|
||||||
bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override;
|
bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override;
|
||||||
@@ -262,6 +358,7 @@ class APIConnection : public APIServerConnection {
|
|||||||
bool service_call_subscription_{false};
|
bool service_call_subscription_{false};
|
||||||
bool next_close_ = false;
|
bool next_close_ = false;
|
||||||
APIServer *parent_;
|
APIServer *parent_;
|
||||||
|
DeferredMessageQueue deferred_message_queue_;
|
||||||
InitialStateIterator initial_state_iterator_;
|
InitialStateIterator initial_state_iterator_;
|
||||||
ListEntitiesIterator list_entities_iterator_;
|
ListEntitiesIterator list_entities_iterator_;
|
||||||
int state_subs_at_ = -1;
|
int state_subs_at_ = -1;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "esphome/core/helpers.h"
|
#include "esphome/core/helpers.h"
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
|
#include "api_pb2_size.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
@@ -72,6 +73,91 @@ const char *api_error_to_str(APIError err) {
|
|||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Common implementation for writing raw data to socket
|
||||||
|
template<typename StateEnum>
|
||||||
|
APIError APIFrameHelper::write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket,
|
||||||
|
std::vector<uint8_t> &tx_buf, const std::string &info, StateEnum &state,
|
||||||
|
StateEnum failed_state) {
|
||||||
|
// This method writes data to socket or buffers it
|
||||||
|
// Returns APIError::OK if successful (or would block, but data has been buffered)
|
||||||
|
// Returns APIError::SOCKET_WRITE_FAILED if socket write failed, and sets state to failed_state
|
||||||
|
|
||||||
|
if (iovcnt == 0)
|
||||||
|
return APIError::OK; // Nothing to do, success
|
||||||
|
|
||||||
|
size_t total_write_len = 0;
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
#ifdef HELPER_LOG_PACKETS
|
||||||
|
ESP_LOGVV(TAG, "Sending raw: %s",
|
||||||
|
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
||||||
|
#endif
|
||||||
|
total_write_len += iov[i].iov_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tx_buf.empty()) {
|
||||||
|
// try to empty tx_buf first
|
||||||
|
while (!tx_buf.empty()) {
|
||||||
|
ssize_t sent = socket->write(tx_buf.data(), tx_buf.size());
|
||||||
|
if (is_would_block(sent)) {
|
||||||
|
break;
|
||||||
|
} else if (sent == -1) {
|
||||||
|
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", info.c_str(), errno);
|
||||||
|
state = failed_state;
|
||||||
|
return APIError::SOCKET_WRITE_FAILED; // Socket write failed
|
||||||
|
}
|
||||||
|
// TODO: inefficient if multiple packets in txbuf
|
||||||
|
// replace with deque of buffers
|
||||||
|
tx_buf.erase(tx_buf.begin(), tx_buf.begin() + sent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tx_buf.empty()) {
|
||||||
|
// tx buf not empty, can't write now because then stream would be inconsistent
|
||||||
|
// Reserve space upfront to avoid multiple reallocations
|
||||||
|
tx_buf.reserve(tx_buf.size() + total_write_len);
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
tx_buf.insert(tx_buf.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
|
||||||
|
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
||||||
|
}
|
||||||
|
return APIError::OK; // Success, data buffered
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t sent = socket->writev(iov, iovcnt);
|
||||||
|
if (is_would_block(sent)) {
|
||||||
|
// operation would block, add buffer to tx_buf
|
||||||
|
// Reserve space upfront to avoid multiple reallocations
|
||||||
|
tx_buf.reserve(tx_buf.size() + total_write_len);
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
tx_buf.insert(tx_buf.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
|
||||||
|
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
||||||
|
}
|
||||||
|
return APIError::OK; // Success, data buffered
|
||||||
|
} else if (sent == -1) {
|
||||||
|
// an error occurred
|
||||||
|
ESP_LOGVV(TAG, "%s: Socket write failed with errno %d", info.c_str(), errno);
|
||||||
|
state = failed_state;
|
||||||
|
return APIError::SOCKET_WRITE_FAILED; // Socket write failed
|
||||||
|
} else if ((size_t) sent != total_write_len) {
|
||||||
|
// partially sent, add end to tx_buf
|
||||||
|
size_t remaining = total_write_len - sent;
|
||||||
|
// Reserve space upfront to avoid multiple reallocations
|
||||||
|
tx_buf.reserve(tx_buf.size() + remaining);
|
||||||
|
|
||||||
|
size_t to_consume = sent;
|
||||||
|
for (int i = 0; i < iovcnt; i++) {
|
||||||
|
if (to_consume >= iov[i].iov_len) {
|
||||||
|
to_consume -= iov[i].iov_len;
|
||||||
|
} else {
|
||||||
|
tx_buf.insert(tx_buf.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
|
||||||
|
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
||||||
|
to_consume = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return APIError::OK; // Success, data buffered
|
||||||
|
}
|
||||||
|
return APIError::OK; // Success, all data sent
|
||||||
|
}
|
||||||
|
|
||||||
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, info_.c_str(), ##__VA_ARGS__)
|
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, info_.c_str(), ##__VA_ARGS__)
|
||||||
// uncomment to log raw packets
|
// uncomment to log raw packets
|
||||||
//#define HELPER_LOG_PACKETS
|
//#define HELPER_LOG_PACKETS
|
||||||
@@ -311,6 +397,10 @@ APIError APINoiseFrameHelper::state_action_() {
|
|||||||
const std::string &name = App.get_name();
|
const std::string &name = App.get_name();
|
||||||
const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
|
const uint8_t *name_ptr = reinterpret_cast<const uint8_t *>(name.c_str());
|
||||||
msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
|
msg.insert(msg.end(), name_ptr, name_ptr + name.size() + 1);
|
||||||
|
// node mac, terminated by null byte
|
||||||
|
const std::string &mac = get_mac_address();
|
||||||
|
const uint8_t *mac_ptr = reinterpret_cast<const uint8_t *>(mac.c_str());
|
||||||
|
msg.insert(msg.end(), mac_ptr, mac_ptr + mac.size() + 1);
|
||||||
|
|
||||||
aerr = write_frame_(msg.data(), msg.size());
|
aerr = write_frame_(msg.data(), msg.size());
|
||||||
if (aerr != APIError::OK)
|
if (aerr != APIError::OK)
|
||||||
@@ -542,71 +632,6 @@ APIError APINoiseFrameHelper::try_send_tx_buf_() {
|
|||||||
|
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
/** Write the data to the socket, or buffer it a write would block
|
|
||||||
*
|
|
||||||
* @param data The data to write
|
|
||||||
* @param len The length of data
|
|
||||||
*/
|
|
||||||
APIError APINoiseFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
|
||||||
if (iovcnt == 0)
|
|
||||||
return APIError::OK;
|
|
||||||
APIError aerr;
|
|
||||||
|
|
||||||
size_t total_write_len = 0;
|
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
|
||||||
#ifdef HELPER_LOG_PACKETS
|
|
||||||
ESP_LOGVV(TAG, "Sending raw: %s",
|
|
||||||
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
|
||||||
#endif
|
|
||||||
total_write_len += iov[i].iov_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tx_buf_.empty()) {
|
|
||||||
// try to empty tx_buf_ first
|
|
||||||
aerr = try_send_tx_buf_();
|
|
||||||
if (aerr != APIError::OK && aerr != APIError::WOULD_BLOCK)
|
|
||||||
return aerr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tx_buf_.empty()) {
|
|
||||||
// tx buf not empty, can't write now because then stream would be inconsistent
|
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
|
||||||
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
|
|
||||||
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
|
||||||
}
|
|
||||||
return APIError::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t sent = socket_->writev(iov, iovcnt);
|
|
||||||
if (is_would_block(sent)) {
|
|
||||||
// operation would block, add buffer to tx_buf
|
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
|
||||||
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
|
|
||||||
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
|
||||||
}
|
|
||||||
return APIError::OK;
|
|
||||||
} else if (sent == -1) {
|
|
||||||
// an error occurred
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Socket write failed with errno %d", errno);
|
|
||||||
return APIError::SOCKET_WRITE_FAILED;
|
|
||||||
} else if ((size_t) sent != total_write_len) {
|
|
||||||
// partially sent, add end to tx_buf
|
|
||||||
size_t to_consume = sent;
|
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
|
||||||
if (to_consume >= iov[i].iov_len) {
|
|
||||||
to_consume -= iov[i].iov_len;
|
|
||||||
} else {
|
|
||||||
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
|
|
||||||
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
|
||||||
to_consume = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return APIError::OK;
|
|
||||||
}
|
|
||||||
// fully sent
|
|
||||||
return APIError::OK;
|
|
||||||
}
|
|
||||||
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
|
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
|
||||||
uint8_t header[3];
|
uint8_t header[3];
|
||||||
header[0] = 0x01; // indicator
|
header[0] = 0x01; // indicator
|
||||||
@@ -740,6 +765,11 @@ void noise_rand_bytes(void *output, size_t len) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Explicit template instantiation for Noise
|
||||||
|
template APIError APIFrameHelper::write_raw_<APINoiseFrameHelper::State>(
|
||||||
|
const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf_, const std::string &info,
|
||||||
|
APINoiseFrameHelper::State &state, APINoiseFrameHelper::State failed_state);
|
||||||
#endif // USE_API_NOISE
|
#endif // USE_API_NOISE
|
||||||
|
|
||||||
#ifdef USE_API_PLAINTEXT
|
#ifdef USE_API_PLAINTEXT
|
||||||
@@ -893,8 +923,28 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|||||||
|
|
||||||
ParsedFrame frame;
|
ParsedFrame frame;
|
||||||
aerr = try_read_frame_(&frame);
|
aerr = try_read_frame_(&frame);
|
||||||
if (aerr != APIError::OK)
|
if (aerr != APIError::OK) {
|
||||||
|
if (aerr == APIError::BAD_INDICATOR) {
|
||||||
|
// Make sure to tell the remote that we don't
|
||||||
|
// understand the indicator byte so it knows
|
||||||
|
// we do not support it.
|
||||||
|
struct iovec iov[1];
|
||||||
|
// The \x00 first byte is the marker for plaintext.
|
||||||
|
//
|
||||||
|
// The remote will know how to handle the indicator byte,
|
||||||
|
// but it likely won't understand the rest of the message.
|
||||||
|
//
|
||||||
|
// We must send at least 3 bytes to be read, so we add
|
||||||
|
// a message after the indicator byte to ensures its long
|
||||||
|
// enough and can aid in debugging.
|
||||||
|
const char msg[] = "\x00"
|
||||||
|
"Bad indicator byte";
|
||||||
|
iov[0].iov_base = (void *) msg;
|
||||||
|
iov[0].iov_len = 19;
|
||||||
|
write_raw_(iov, 1);
|
||||||
|
}
|
||||||
return aerr;
|
return aerr;
|
||||||
|
}
|
||||||
|
|
||||||
buffer->container = std::move(frame.msg);
|
buffer->container = std::move(frame.msg);
|
||||||
buffer->data_offset = 0;
|
buffer->data_offset = 0;
|
||||||
@@ -909,6 +959,8 @@ APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *pay
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> header;
|
std::vector<uint8_t> header;
|
||||||
|
header.reserve(1 + api::ProtoSize::varint(static_cast<uint32_t>(payload_len)) +
|
||||||
|
api::ProtoSize::varint(static_cast<uint32_t>(type)));
|
||||||
header.push_back(0x00);
|
header.push_back(0x00);
|
||||||
ProtoVarInt(payload_len).encode(header);
|
ProtoVarInt(payload_len).encode(header);
|
||||||
ProtoVarInt(type).encode(header);
|
ProtoVarInt(type).encode(header);
|
||||||
@@ -942,71 +994,6 @@ APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
|
|||||||
|
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
/** Write the data to the socket, or buffer it a write would block
|
|
||||||
*
|
|
||||||
* @param data The data to write
|
|
||||||
* @param len The length of data
|
|
||||||
*/
|
|
||||||
APIError APIPlaintextFrameHelper::write_raw_(const struct iovec *iov, int iovcnt) {
|
|
||||||
if (iovcnt == 0)
|
|
||||||
return APIError::OK;
|
|
||||||
APIError aerr;
|
|
||||||
|
|
||||||
size_t total_write_len = 0;
|
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
|
||||||
#ifdef HELPER_LOG_PACKETS
|
|
||||||
ESP_LOGVV(TAG, "Sending raw: %s",
|
|
||||||
format_hex_pretty(reinterpret_cast<uint8_t *>(iov[i].iov_base), iov[i].iov_len).c_str());
|
|
||||||
#endif
|
|
||||||
total_write_len += iov[i].iov_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tx_buf_.empty()) {
|
|
||||||
// try to empty tx_buf_ first
|
|
||||||
aerr = try_send_tx_buf_();
|
|
||||||
if (aerr != APIError::OK && aerr != APIError::WOULD_BLOCK)
|
|
||||||
return aerr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!tx_buf_.empty()) {
|
|
||||||
// tx buf not empty, can't write now because then stream would be inconsistent
|
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
|
||||||
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
|
|
||||||
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
|
||||||
}
|
|
||||||
return APIError::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t sent = socket_->writev(iov, iovcnt);
|
|
||||||
if (is_would_block(sent)) {
|
|
||||||
// operation would block, add buffer to tx_buf
|
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
|
||||||
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base),
|
|
||||||
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
|
||||||
}
|
|
||||||
return APIError::OK;
|
|
||||||
} else if (sent == -1) {
|
|
||||||
// an error occurred
|
|
||||||
state_ = State::FAILED;
|
|
||||||
HELPER_LOG("Socket write failed with errno %d", errno);
|
|
||||||
return APIError::SOCKET_WRITE_FAILED;
|
|
||||||
} else if ((size_t) sent != total_write_len) {
|
|
||||||
// partially sent, add end to tx_buf
|
|
||||||
size_t to_consume = sent;
|
|
||||||
for (int i = 0; i < iovcnt; i++) {
|
|
||||||
if (to_consume >= iov[i].iov_len) {
|
|
||||||
to_consume -= iov[i].iov_len;
|
|
||||||
} else {
|
|
||||||
tx_buf_.insert(tx_buf_.end(), reinterpret_cast<uint8_t *>(iov[i].iov_base) + to_consume,
|
|
||||||
reinterpret_cast<uint8_t *>(iov[i].iov_base) + iov[i].iov_len);
|
|
||||||
to_consume = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return APIError::OK;
|
|
||||||
}
|
|
||||||
// fully sent
|
|
||||||
return APIError::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
APIError APIPlaintextFrameHelper::close() {
|
APIError APIPlaintextFrameHelper::close() {
|
||||||
state_ = State::CLOSED;
|
state_ = State::CLOSED;
|
||||||
@@ -1024,6 +1011,11 @@ APIError APIPlaintextFrameHelper::shutdown(int how) {
|
|||||||
}
|
}
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Explicit template instantiation for Plaintext
|
||||||
|
template APIError APIFrameHelper::write_raw_<APIPlaintextFrameHelper::State>(
|
||||||
|
const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf_, const std::string &info,
|
||||||
|
APIPlaintextFrameHelper::State &state, APIPlaintextFrameHelper::State failed_state);
|
||||||
#endif // USE_API_PLAINTEXT
|
#endif // USE_API_PLAINTEXT
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
|||||||
@@ -72,6 +72,12 @@ class APIFrameHelper {
|
|||||||
virtual APIError shutdown(int how) = 0;
|
virtual APIError shutdown(int how) = 0;
|
||||||
// Give this helper a name for logging
|
// Give this helper a name for logging
|
||||||
virtual void set_log_info(std::string info) = 0;
|
virtual void set_log_info(std::string info) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Common implementation for writing raw data to socket
|
||||||
|
template<typename StateEnum>
|
||||||
|
APIError write_raw_(const struct iovec *iov, int iovcnt, socket::Socket *socket, std::vector<uint8_t> &tx_buf,
|
||||||
|
const std::string &info, StateEnum &state, StateEnum failed_state);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
@@ -103,7 +109,9 @@ class APINoiseFrameHelper : public APIFrameHelper {
|
|||||||
APIError try_read_frame_(ParsedFrame *frame);
|
APIError try_read_frame_(ParsedFrame *frame);
|
||||||
APIError try_send_tx_buf_();
|
APIError try_send_tx_buf_();
|
||||||
APIError write_frame_(const uint8_t *data, size_t len);
|
APIError write_frame_(const uint8_t *data, size_t len);
|
||||||
APIError write_raw_(const struct iovec *iov, int iovcnt);
|
inline APIError write_raw_(const struct iovec *iov, int iovcnt) {
|
||||||
|
return APIFrameHelper::write_raw_(iov, iovcnt, socket_.get(), tx_buf_, info_, state_, State::FAILED);
|
||||||
|
}
|
||||||
APIError init_handshake_();
|
APIError init_handshake_();
|
||||||
APIError check_handshake_finished_();
|
APIError check_handshake_finished_();
|
||||||
void send_explicit_handshake_reject_(const std::string &reason);
|
void send_explicit_handshake_reject_(const std::string &reason);
|
||||||
@@ -164,7 +172,9 @@ class APIPlaintextFrameHelper : public APIFrameHelper {
|
|||||||
|
|
||||||
APIError try_read_frame_(ParsedFrame *frame);
|
APIError try_read_frame_(ParsedFrame *frame);
|
||||||
APIError try_send_tx_buf_();
|
APIError try_send_tx_buf_();
|
||||||
APIError write_raw_(const struct iovec *iov, int iovcnt);
|
inline APIError write_raw_(const struct iovec *iov, int iovcnt) {
|
||||||
|
return APIFrameHelper::write_raw_(iov, iovcnt, socket_.get(), tx_buf_, info_, state_, State::FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<socket::Socket> socket_;
|
std::unique_ptr<socket::Socket> socket_;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <cstdint>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
#include "esphome/core/defines.h"
|
#include "esphome/core/defines.h"
|
||||||
|
|
||||||
namespace esphome {
|
namespace esphome {
|
||||||
@@ -11,11 +11,20 @@ using psk_t = std::array<uint8_t, 32>;
|
|||||||
|
|
||||||
class APINoiseContext {
|
class APINoiseContext {
|
||||||
public:
|
public:
|
||||||
void set_psk(psk_t psk) { psk_ = psk; }
|
void set_psk(psk_t psk) {
|
||||||
const psk_t &get_psk() const { return psk_; }
|
this->psk_ = psk;
|
||||||
|
bool has_psk = false;
|
||||||
|
for (auto i : psk) {
|
||||||
|
has_psk |= i;
|
||||||
|
}
|
||||||
|
this->has_psk_ = has_psk;
|
||||||
|
}
|
||||||
|
const psk_t &get_psk() const { return this->psk_; }
|
||||||
|
bool has_psk() const { return this->has_psk_; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
psk_t psk_;
|
psk_t psk_{};
|
||||||
|
bool has_psk_{false};
|
||||||
};
|
};
|
||||||
#endif // USE_API_NOISE
|
#endif // USE_API_NOISE
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
|||||||
// This file was automatically generated with a tool.
|
// This file was automatically generated with a tool.
|
||||||
// See scripts/api_protobuf/api_protobuf.py
|
// See script/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"
|
||||||
|
|
||||||
@@ -179,6 +179,16 @@ bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorSt
|
|||||||
bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
|
bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsResponse &msg) {
|
||||||
return this->send_message_<SubscribeLogsResponse>(msg, 29);
|
return this->send_message_<SubscribeLogsResponse>(msg, 29);
|
||||||
}
|
}
|
||||||
|
#ifdef USE_API_NOISE
|
||||||
|
#endif
|
||||||
|
#ifdef USE_API_NOISE
|
||||||
|
bool APIServerConnectionBase::send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_noise_encryption_set_key_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<NoiseEncryptionSetKeyResponse>(msg, 125);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
|
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
|
||||||
@@ -282,6 +292,24 @@ bool APIServerConnectionBase::send_select_state_response(const SelectStateRespon
|
|||||||
#endif
|
#endif
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SIREN
|
||||||
|
bool APIServerConnectionBase::send_list_entities_siren_response(const ListEntitiesSirenResponse &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_list_entities_siren_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<ListEntitiesSirenResponse>(msg, 55);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SIREN
|
||||||
|
bool APIServerConnectionBase::send_siren_state_response(const SirenStateResponse &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_siren_state_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<SirenStateResponse>(msg, 56);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SIREN
|
||||||
|
#endif
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
bool APIServerConnectionBase::send_list_entities_lock_response(const ListEntitiesLockResponse &msg) {
|
bool APIServerConnectionBase::send_list_entities_lock_response(const ListEntitiesLockResponse &msg) {
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
@@ -462,6 +490,16 @@ bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const B
|
|||||||
return this->send_message_<BluetoothDeviceClearCacheResponse>(msg, 88);
|
return this->send_message_<BluetoothDeviceClearCacheResponse>(msg, 88);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
|
bool APIServerConnectionBase::send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg) {
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "send_bluetooth_scanner_state_response: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
return this->send_message_<BluetoothScannerStateResponse>(msg, 126);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
@@ -883,6 +921,17 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
this->on_select_command_request(msg);
|
this->on_select_command_request(msg);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 57: {
|
||||||
|
#ifdef USE_SIREN
|
||||||
|
SirenCommandRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "on_siren_command_request: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
this->on_siren_command_request(msg);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1191,6 +1240,28 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
|||||||
ESP_LOGVV(TAG, "on_voice_assistant_set_configuration: %s", msg.dump().c_str());
|
ESP_LOGVV(TAG, "on_voice_assistant_set_configuration: %s", msg.dump().c_str());
|
||||||
#endif
|
#endif
|
||||||
this->on_voice_assistant_set_configuration(msg);
|
this->on_voice_assistant_set_configuration(msg);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 124: {
|
||||||
|
#ifdef USE_API_NOISE
|
||||||
|
NoiseEncryptionSetKeyRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "on_noise_encryption_set_key_request: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
this->on_noise_encryption_set_key_request(msg);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 127: {
|
||||||
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
|
BluetoothScannerSetModeRequest msg;
|
||||||
|
msg.decode(msg_data, msg_size);
|
||||||
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
|
ESP_LOGVV(TAG, "on_bluetooth_scanner_set_mode_request: %s", msg.dump().c_str());
|
||||||
|
#endif
|
||||||
|
this->on_bluetooth_scanner_set_mode_request(msg);
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1311,8 +1382,8 @@ void APIServerConnection::on_execute_service_request(const ExecuteServiceRequest
|
|||||||
}
|
}
|
||||||
this->execute_service(msg);
|
this->execute_service(msg);
|
||||||
}
|
}
|
||||||
#ifdef USE_COVER
|
#ifdef USE_API_NOISE
|
||||||
void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) {
|
void APIServerConnection::on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (!this->is_connection_setup()) {
|
||||||
this->on_no_setup_connection();
|
this->on_no_setup_connection();
|
||||||
return;
|
return;
|
||||||
@@ -1321,11 +1392,14 @@ void APIServerConnection::on_cover_command_request(const CoverCommandRequest &ms
|
|||||||
this->on_unauthenticated_access();
|
this->on_unauthenticated_access();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->cover_command(msg);
|
NoiseEncryptionSetKeyResponse ret = this->noise_encryption_set_key(msg);
|
||||||
|
if (!this->send_noise_encryption_set_key_response(ret)) {
|
||||||
|
this->on_fatal_error();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_BUTTON
|
||||||
void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) {
|
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (!this->is_connection_setup()) {
|
||||||
this->on_no_setup_connection();
|
this->on_no_setup_connection();
|
||||||
return;
|
return;
|
||||||
@@ -1334,33 +1408,7 @@ void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) {
|
|||||||
this->on_unauthenticated_access();
|
this->on_unauthenticated_access();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->fan_command(msg);
|
this->button_command(msg);
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_LIGHT
|
|
||||||
void APIServerConnection::on_light_command_request(const LightCommandRequest &msg) {
|
|
||||||
if (!this->is_connection_setup()) {
|
|
||||||
this->on_no_setup_connection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->light_command(msg);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SWITCH
|
|
||||||
void APIServerConnection::on_switch_command_request(const SwitchCommandRequest &msg) {
|
|
||||||
if (!this->is_connection_setup()) {
|
|
||||||
this->on_no_setup_connection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->switch_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_ESP32_CAMERA
|
||||||
@@ -1389,8 +1437,8 @@ void APIServerConnection::on_climate_command_request(const ClimateCommandRequest
|
|||||||
this->climate_command(msg);
|
this->climate_command(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_COVER
|
||||||
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) {
|
void APIServerConnection::on_cover_command_request(const CoverCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (!this->is_connection_setup()) {
|
||||||
this->on_no_setup_connection();
|
this->on_no_setup_connection();
|
||||||
return;
|
return;
|
||||||
@@ -1399,85 +1447,7 @@ void APIServerConnection::on_number_command_request(const NumberCommandRequest &
|
|||||||
this->on_unauthenticated_access();
|
this->on_unauthenticated_access();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this->number_command(msg);
|
this->cover_command(msg);
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_TEXT
|
|
||||||
void APIServerConnection::on_text_command_request(const TextCommandRequest &msg) {
|
|
||||||
if (!this->is_connection_setup()) {
|
|
||||||
this->on_no_setup_connection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->text_command(msg);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SELECT
|
|
||||||
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) {
|
|
||||||
if (!this->is_connection_setup()) {
|
|
||||||
this->on_no_setup_connection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->select_command(msg);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_BUTTON
|
|
||||||
void APIServerConnection::on_button_command_request(const ButtonCommandRequest &msg) {
|
|
||||||
if (!this->is_connection_setup()) {
|
|
||||||
this->on_no_setup_connection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->button_command(msg);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_LOCK
|
|
||||||
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) {
|
|
||||||
if (!this->is_connection_setup()) {
|
|
||||||
this->on_no_setup_connection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->lock_command(msg);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_VALVE
|
|
||||||
void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) {
|
|
||||||
if (!this->is_connection_setup()) {
|
|
||||||
this->on_no_setup_connection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->valve_command(msg);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_MEDIA_PLAYER
|
|
||||||
void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) {
|
|
||||||
if (!this->is_connection_setup()) {
|
|
||||||
this->on_no_setup_connection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->media_player_command(msg);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
@@ -1493,19 +1463,6 @@ void APIServerConnection::on_date_command_request(const DateCommandRequest &msg)
|
|||||||
this->date_command(msg);
|
this->date_command(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_TIME
|
|
||||||
void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) {
|
|
||||||
if (!this->is_connection_setup()) {
|
|
||||||
this->on_no_setup_connection();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!this->is_authenticated()) {
|
|
||||||
this->on_unauthenticated_access();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this->time_command(msg);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef USE_DATETIME_DATETIME
|
#ifdef USE_DATETIME_DATETIME
|
||||||
void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) {
|
void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (!this->is_connection_setup()) {
|
||||||
@@ -1519,6 +1476,136 @@ void APIServerConnection::on_date_time_command_request(const DateTimeCommandRequ
|
|||||||
this->datetime_command(msg);
|
this->datetime_command(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_FAN
|
||||||
|
void APIServerConnection::on_fan_command_request(const FanCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->fan_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_LIGHT
|
||||||
|
void APIServerConnection::on_light_command_request(const LightCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->light_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_LOCK
|
||||||
|
void APIServerConnection::on_lock_command_request(const LockCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->lock_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_MEDIA_PLAYER
|
||||||
|
void APIServerConnection::on_media_player_command_request(const MediaPlayerCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->media_player_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->number_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->select_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SIREN
|
||||||
|
void APIServerConnection::on_siren_command_request(const SirenCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->siren_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
void APIServerConnection::on_switch_command_request(const SwitchCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->switch_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_TEXT
|
||||||
|
void APIServerConnection::on_text_command_request(const TextCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->text_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void APIServerConnection::on_time_command_request(const TimeCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->time_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
void APIServerConnection::on_update_command_request(const UpdateCommandRequest &msg) {
|
void APIServerConnection::on_update_command_request(const UpdateCommandRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (!this->is_connection_setup()) {
|
||||||
@@ -1532,6 +1619,19 @@ void APIServerConnection::on_update_command_request(const UpdateCommandRequest &
|
|||||||
this->update_command(msg);
|
this->update_command(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_VALVE
|
||||||
|
void APIServerConnection::on_valve_command_request(const ValveCommandRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->valve_command(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
void APIServerConnection::on_subscribe_bluetooth_le_advertisements_request(
|
||||||
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
const SubscribeBluetoothLEAdvertisementsRequest &msg) {
|
||||||
@@ -1668,6 +1768,19 @@ void APIServerConnection::on_unsubscribe_bluetooth_le_advertisements_request(
|
|||||||
this->unsubscribe_bluetooth_le_advertisements(msg);
|
this->unsubscribe_bluetooth_le_advertisements(msg);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
|
void APIServerConnection::on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) {
|
||||||
|
if (!this->is_connection_setup()) {
|
||||||
|
this->on_no_setup_connection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this->is_authenticated()) {
|
||||||
|
this->on_unauthenticated_access();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->bluetooth_scanner_set_mode(msg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
|
void APIServerConnection::on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) {
|
||||||
if (!this->is_connection_setup()) {
|
if (!this->is_connection_setup()) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// This file was automatically generated with a tool.
|
// This file was automatically generated with a tool.
|
||||||
// See scripts/api_protobuf/api_protobuf.py
|
// See script/api_protobuf/api_protobuf.py
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "api_pb2.h"
|
#include "api_pb2.h"
|
||||||
@@ -83,6 +83,12 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
#endif
|
#endif
|
||||||
virtual void on_subscribe_logs_request(const SubscribeLogsRequest &value){};
|
virtual void on_subscribe_logs_request(const SubscribeLogsRequest &value){};
|
||||||
bool send_subscribe_logs_response(const SubscribeLogsResponse &msg);
|
bool send_subscribe_logs_response(const SubscribeLogsResponse &msg);
|
||||||
|
#ifdef USE_API_NOISE
|
||||||
|
virtual void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &value){};
|
||||||
|
#endif
|
||||||
|
#ifdef USE_API_NOISE
|
||||||
|
bool send_noise_encryption_set_key_response(const NoiseEncryptionSetKeyResponse &msg);
|
||||||
|
#endif
|
||||||
virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
|
virtual void on_subscribe_homeassistant_services_request(const SubscribeHomeassistantServicesRequest &value){};
|
||||||
bool send_homeassistant_service_response(const HomeassistantServiceResponse &msg);
|
bool send_homeassistant_service_response(const HomeassistantServiceResponse &msg);
|
||||||
virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
|
virtual void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &value){};
|
||||||
@@ -130,6 +136,15 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
virtual void on_select_command_request(const SelectCommandRequest &value){};
|
virtual void on_select_command_request(const SelectCommandRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_SIREN
|
||||||
|
bool send_list_entities_siren_response(const ListEntitiesSirenResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SIREN
|
||||||
|
bool send_siren_state_response(const SirenStateResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SIREN
|
||||||
|
virtual void on_siren_command_request(const SirenCommandRequest &value){};
|
||||||
|
#endif
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
bool send_list_entities_lock_response(const ListEntitiesLockResponse &msg);
|
bool send_list_entities_lock_response(const ListEntitiesLockResponse &msg);
|
||||||
#endif
|
#endif
|
||||||
@@ -228,6 +243,12 @@ class APIServerConnectionBase : public ProtoService {
|
|||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg);
|
bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
|
bool send_bluetooth_scanner_state_response(const BluetoothScannerStateResponse &msg);
|
||||||
|
#endif
|
||||||
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
|
virtual void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &value){};
|
||||||
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){};
|
virtual void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &value){};
|
||||||
#endif
|
#endif
|
||||||
@@ -349,17 +370,11 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
|
virtual void subscribe_home_assistant_states(const SubscribeHomeAssistantStatesRequest &msg) = 0;
|
||||||
virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0;
|
virtual GetTimeResponse get_time(const GetTimeRequest &msg) = 0;
|
||||||
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
|
virtual void execute_service(const ExecuteServiceRequest &msg) = 0;
|
||||||
#ifdef USE_COVER
|
#ifdef USE_API_NOISE
|
||||||
virtual void cover_command(const CoverCommandRequest &msg) = 0;
|
virtual NoiseEncryptionSetKeyResponse noise_encryption_set_key(const NoiseEncryptionSetKeyRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_BUTTON
|
||||||
virtual void fan_command(const FanCommandRequest &msg) = 0;
|
virtual void button_command(const ButtonCommandRequest &msg) = 0;
|
||||||
#endif
|
|
||||||
#ifdef USE_LIGHT
|
|
||||||
virtual void light_command(const LightCommandRequest &msg) = 0;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SWITCH
|
|
||||||
virtual void switch_command(const SwitchCommandRequest &msg) = 0;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_ESP32_CAMERA
|
||||||
virtual void camera_image(const CameraImageRequest &msg) = 0;
|
virtual void camera_image(const CameraImageRequest &msg) = 0;
|
||||||
@@ -367,39 +382,51 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
virtual void climate_command(const ClimateCommandRequest &msg) = 0;
|
virtual void climate_command(const ClimateCommandRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_COVER
|
||||||
virtual void number_command(const NumberCommandRequest &msg) = 0;
|
virtual void cover_command(const CoverCommandRequest &msg) = 0;
|
||||||
#endif
|
|
||||||
#ifdef USE_TEXT
|
|
||||||
virtual void text_command(const TextCommandRequest &msg) = 0;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SELECT
|
|
||||||
virtual void select_command(const SelectCommandRequest &msg) = 0;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_BUTTON
|
|
||||||
virtual void button_command(const ButtonCommandRequest &msg) = 0;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_LOCK
|
|
||||||
virtual void lock_command(const LockCommandRequest &msg) = 0;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_VALVE
|
|
||||||
virtual void valve_command(const ValveCommandRequest &msg) = 0;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_MEDIA_PLAYER
|
|
||||||
virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
virtual void date_command(const DateCommandRequest &msg) = 0;
|
virtual void date_command(const DateCommandRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_TIME
|
|
||||||
virtual void time_command(const TimeCommandRequest &msg) = 0;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_DATETIME_DATETIME
|
#ifdef USE_DATETIME_DATETIME
|
||||||
virtual void datetime_command(const DateTimeCommandRequest &msg) = 0;
|
virtual void datetime_command(const DateTimeCommandRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_FAN
|
||||||
|
virtual void fan_command(const FanCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_LIGHT
|
||||||
|
virtual void light_command(const LightCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_LOCK
|
||||||
|
virtual void lock_command(const LockCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_MEDIA_PLAYER
|
||||||
|
virtual void media_player_command(const MediaPlayerCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
virtual void number_command(const NumberCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
virtual void select_command(const SelectCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SIREN
|
||||||
|
virtual void siren_command(const SirenCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
virtual void switch_command(const SwitchCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_TEXT
|
||||||
|
virtual void text_command(const TextCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
virtual void time_command(const TimeCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
virtual void update_command(const UpdateCommandRequest &msg) = 0;
|
virtual void update_command(const UpdateCommandRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_VALVE
|
||||||
|
virtual void valve_command(const ValveCommandRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
virtual void subscribe_bluetooth_le_advertisements(const SubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -431,6 +458,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
virtual void unsubscribe_bluetooth_le_advertisements(const UnsubscribeBluetoothLEAdvertisementsRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
|
virtual void bluetooth_scanner_set_mode(const BluetoothScannerSetModeRequest &msg) = 0;
|
||||||
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
|
virtual void subscribe_voice_assistant(const SubscribeVoiceAssistantRequest &msg) = 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -457,17 +487,11 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
|
void on_subscribe_home_assistant_states_request(const SubscribeHomeAssistantStatesRequest &msg) override;
|
||||||
void on_get_time_request(const GetTimeRequest &msg) override;
|
void on_get_time_request(const GetTimeRequest &msg) override;
|
||||||
void on_execute_service_request(const ExecuteServiceRequest &msg) override;
|
void on_execute_service_request(const ExecuteServiceRequest &msg) override;
|
||||||
#ifdef USE_COVER
|
#ifdef USE_API_NOISE
|
||||||
void on_cover_command_request(const CoverCommandRequest &msg) override;
|
void on_noise_encryption_set_key_request(const NoiseEncryptionSetKeyRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_BUTTON
|
||||||
void on_fan_command_request(const FanCommandRequest &msg) override;
|
void on_button_command_request(const ButtonCommandRequest &msg) override;
|
||||||
#endif
|
|
||||||
#ifdef USE_LIGHT
|
|
||||||
void on_light_command_request(const LightCommandRequest &msg) override;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SWITCH
|
|
||||||
void on_switch_command_request(const SwitchCommandRequest &msg) override;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_ESP32_CAMERA
|
||||||
void on_camera_image_request(const CameraImageRequest &msg) override;
|
void on_camera_image_request(const CameraImageRequest &msg) override;
|
||||||
@@ -475,39 +499,51 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
void on_climate_command_request(const ClimateCommandRequest &msg) override;
|
void on_climate_command_request(const ClimateCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_COVER
|
||||||
void on_number_command_request(const NumberCommandRequest &msg) override;
|
void on_cover_command_request(const CoverCommandRequest &msg) override;
|
||||||
#endif
|
|
||||||
#ifdef USE_TEXT
|
|
||||||
void on_text_command_request(const TextCommandRequest &msg) override;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_SELECT
|
|
||||||
void on_select_command_request(const SelectCommandRequest &msg) override;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_BUTTON
|
|
||||||
void on_button_command_request(const ButtonCommandRequest &msg) override;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_LOCK
|
|
||||||
void on_lock_command_request(const LockCommandRequest &msg) override;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_VALVE
|
|
||||||
void on_valve_command_request(const ValveCommandRequest &msg) override;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_MEDIA_PLAYER
|
|
||||||
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
void on_date_command_request(const DateCommandRequest &msg) override;
|
void on_date_command_request(const DateCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_DATETIME_TIME
|
|
||||||
void on_time_command_request(const TimeCommandRequest &msg) override;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_DATETIME_DATETIME
|
#ifdef USE_DATETIME_DATETIME
|
||||||
void on_date_time_command_request(const DateTimeCommandRequest &msg) override;
|
void on_date_time_command_request(const DateTimeCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_FAN
|
||||||
|
void on_fan_command_request(const FanCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_LIGHT
|
||||||
|
void on_light_command_request(const LightCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_LOCK
|
||||||
|
void on_lock_command_request(const LockCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_MEDIA_PLAYER
|
||||||
|
void on_media_player_command_request(const MediaPlayerCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_NUMBER
|
||||||
|
void on_number_command_request(const NumberCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SELECT
|
||||||
|
void on_select_command_request(const SelectCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SIREN
|
||||||
|
void on_siren_command_request(const SirenCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_SWITCH
|
||||||
|
void on_switch_command_request(const SwitchCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_TEXT
|
||||||
|
void on_text_command_request(const TextCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
|
#ifdef USE_DATETIME_TIME
|
||||||
|
void on_time_command_request(const TimeCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
void on_update_command_request(const UpdateCommandRequest &msg) override;
|
void on_update_command_request(const UpdateCommandRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_VALVE
|
||||||
|
void on_valve_command_request(const ValveCommandRequest &msg) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_BLUETOOTH_PROXY
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
void on_subscribe_bluetooth_le_advertisements_request(const SubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
@@ -539,6 +575,9 @@ class APIServerConnection : public APIServerConnectionBase {
|
|||||||
void on_unsubscribe_bluetooth_le_advertisements_request(
|
void on_unsubscribe_bluetooth_le_advertisements_request(
|
||||||
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
const UnsubscribeBluetoothLEAdvertisementsRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef USE_BLUETOOTH_PROXY
|
||||||
|
void on_bluetooth_scanner_set_mode_request(const BluetoothScannerSetModeRequest &msg) override;
|
||||||
|
#endif
|
||||||
#ifdef USE_VOICE_ASSISTANT
|
#ifdef USE_VOICE_ASSISTANT
|
||||||
void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
|
void on_subscribe_voice_assistant_request(const SubscribeVoiceAssistantRequest &msg) override;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
361
esphome/components/api/api_pb2_size.h
Normal file
361
esphome/components/api/api_pb2_size.h
Normal file
@@ -0,0 +1,361 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "proto.h"
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace api {
|
||||||
|
|
||||||
|
class ProtoSize {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief ProtoSize class for Protocol Buffer serialization size calculation
|
||||||
|
*
|
||||||
|
* This class provides static methods to calculate the exact byte counts needed
|
||||||
|
* for encoding various Protocol Buffer field types. All methods are designed to be
|
||||||
|
* efficient for the common case where many fields have default values.
|
||||||
|
*
|
||||||
|
* Implements Protocol Buffer encoding size calculation according to:
|
||||||
|
* https://protobuf.dev/programming-guides/encoding/
|
||||||
|
*
|
||||||
|
* Key features:
|
||||||
|
* - Early-return optimization for zero/default values
|
||||||
|
* - Direct total_size updates to avoid unnecessary additions
|
||||||
|
* - Specialized handling for different field types according to protobuf spec
|
||||||
|
* - Templated helpers for repeated fields and messages
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the size in bytes needed to encode a uint32_t value as a varint
|
||||||
|
*
|
||||||
|
* @param value The uint32_t value to calculate size for
|
||||||
|
* @return The number of bytes needed to encode the value
|
||||||
|
*/
|
||||||
|
static inline uint32_t varint(uint32_t value) {
|
||||||
|
// Optimized varint size calculation using leading zeros
|
||||||
|
// Each 7 bits requires one byte in the varint encoding
|
||||||
|
if (value < 128)
|
||||||
|
return 1; // 7 bits, common case for small values
|
||||||
|
|
||||||
|
// For larger values, count bytes needed based on the position of the highest bit set
|
||||||
|
if (value < 16384) {
|
||||||
|
return 2; // 14 bits
|
||||||
|
} else if (value < 2097152) {
|
||||||
|
return 3; // 21 bits
|
||||||
|
} else if (value < 268435456) {
|
||||||
|
return 4; // 28 bits
|
||||||
|
} else {
|
||||||
|
return 5; // 32 bits (maximum for uint32_t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the size in bytes needed to encode a uint64_t value as a varint
|
||||||
|
*
|
||||||
|
* @param value The uint64_t value to calculate size for
|
||||||
|
* @return The number of bytes needed to encode the value
|
||||||
|
*/
|
||||||
|
static inline uint32_t varint(uint64_t value) {
|
||||||
|
// Handle common case of values fitting in uint32_t (vast majority of use cases)
|
||||||
|
if (value <= UINT32_MAX) {
|
||||||
|
return varint(static_cast<uint32_t>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For larger values, determine size based on highest bit position
|
||||||
|
if (value < (1ULL << 35)) {
|
||||||
|
return 5; // 35 bits
|
||||||
|
} else if (value < (1ULL << 42)) {
|
||||||
|
return 6; // 42 bits
|
||||||
|
} else if (value < (1ULL << 49)) {
|
||||||
|
return 7; // 49 bits
|
||||||
|
} else if (value < (1ULL << 56)) {
|
||||||
|
return 8; // 56 bits
|
||||||
|
} else if (value < (1ULL << 63)) {
|
||||||
|
return 9; // 63 bits
|
||||||
|
} else {
|
||||||
|
return 10; // 64 bits (maximum for uint64_t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the size in bytes needed to encode an int32_t value as a varint
|
||||||
|
*
|
||||||
|
* Special handling is needed for negative values, which are sign-extended to 64 bits
|
||||||
|
* in Protocol Buffers, resulting in a 10-byte varint.
|
||||||
|
*
|
||||||
|
* @param value The int32_t value to calculate size for
|
||||||
|
* @return The number of bytes needed to encode the value
|
||||||
|
*/
|
||||||
|
static inline uint32_t varint(int32_t value) {
|
||||||
|
// Negative values are sign-extended to 64 bits in protocol buffers,
|
||||||
|
// which always results in a 10-byte varint for negative int32
|
||||||
|
if (value < 0) {
|
||||||
|
return 10; // Negative int32 is always 10 bytes long
|
||||||
|
}
|
||||||
|
// For non-negative values, use the uint32_t implementation
|
||||||
|
return varint(static_cast<uint32_t>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the size in bytes needed to encode an int64_t value as a varint
|
||||||
|
*
|
||||||
|
* @param value The int64_t value to calculate size for
|
||||||
|
* @return The number of bytes needed to encode the value
|
||||||
|
*/
|
||||||
|
static inline uint32_t varint(int64_t value) {
|
||||||
|
// For int64_t, we convert to uint64_t and calculate the size
|
||||||
|
// This works because the bit pattern determines the encoding size,
|
||||||
|
// and we've handled negative int32 values as a special case above
|
||||||
|
return varint(static_cast<uint64_t>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates the size in bytes needed to encode a field ID and wire type
|
||||||
|
*
|
||||||
|
* @param field_id The field identifier
|
||||||
|
* @param type The wire type value (from the WireType enum in the protobuf spec)
|
||||||
|
* @return The number of bytes needed to encode the field ID and wire type
|
||||||
|
*/
|
||||||
|
static inline uint32_t field(uint32_t field_id, uint32_t type) {
|
||||||
|
uint32_t tag = (field_id << 3) | (type & 0b111);
|
||||||
|
return varint(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Common parameters for all add_*_field methods
|
||||||
|
*
|
||||||
|
* All add_*_field methods follow these common patterns:
|
||||||
|
*
|
||||||
|
* @param total_size Reference to the total message size to update
|
||||||
|
* @param field_id_size Pre-calculated size of the field ID in bytes
|
||||||
|
* @param value The value to calculate size for (type varies)
|
||||||
|
* @param force Whether to calculate size even if the value is default/zero/empty
|
||||||
|
*
|
||||||
|
* Each method follows this implementation pattern:
|
||||||
|
* 1. Skip calculation if value is default (0, false, empty) and not forced
|
||||||
|
* 2. Calculate the size based on the field's encoding rules
|
||||||
|
* 3. Add the field_id_size + calculated value size to total_size
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of an int32 field to the total message size
|
||||||
|
*/
|
||||||
|
static inline void add_int32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value, bool force = false) {
|
||||||
|
// Skip calculation if value is zero and not forced
|
||||||
|
if (value == 0 && !force) {
|
||||||
|
return; // No need to update total_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate and directly add to total_size
|
||||||
|
if (value < 0) {
|
||||||
|
// Negative values are encoded as 10-byte varints in protobuf
|
||||||
|
total_size += field_id_size + 10;
|
||||||
|
} else {
|
||||||
|
// For non-negative values, use the standard varint size
|
||||||
|
total_size += field_id_size + varint(static_cast<uint32_t>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of a uint32 field to the total message size
|
||||||
|
*/
|
||||||
|
static inline void add_uint32_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value,
|
||||||
|
bool force = false) {
|
||||||
|
// Skip calculation if value is zero and not forced
|
||||||
|
if (value == 0 && !force) {
|
||||||
|
return; // No need to update total_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate and directly add to total_size
|
||||||
|
total_size += field_id_size + varint(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of a boolean field to the total message size
|
||||||
|
*/
|
||||||
|
static inline void add_bool_field(uint32_t &total_size, uint32_t field_id_size, bool value, bool force = false) {
|
||||||
|
// Skip calculation if value is false and not forced
|
||||||
|
if (!value && !force) {
|
||||||
|
return; // No need to update total_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boolean fields always use 1 byte when true
|
||||||
|
total_size += field_id_size + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of a fixed field to the total message size
|
||||||
|
*
|
||||||
|
* Fixed fields always take exactly N bytes (4 for fixed32/float, 8 for fixed64/double).
|
||||||
|
*
|
||||||
|
* @tparam NumBytes The number of bytes for this fixed field (4 or 8)
|
||||||
|
* @param is_nonzero Whether the value is non-zero
|
||||||
|
*/
|
||||||
|
template<uint32_t NumBytes>
|
||||||
|
static inline void add_fixed_field(uint32_t &total_size, uint32_t field_id_size, bool is_nonzero,
|
||||||
|
bool force = false) {
|
||||||
|
// Skip calculation if value is zero and not forced
|
||||||
|
if (!is_nonzero && !force) {
|
||||||
|
return; // No need to update total_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fixed fields always take exactly NumBytes
|
||||||
|
total_size += field_id_size + NumBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of an enum field to the total message size
|
||||||
|
*
|
||||||
|
* Enum fields are encoded as uint32 varints.
|
||||||
|
*/
|
||||||
|
static inline void add_enum_field(uint32_t &total_size, uint32_t field_id_size, uint32_t value, bool force = false) {
|
||||||
|
// Skip calculation if value is zero and not forced
|
||||||
|
if (value == 0 && !force) {
|
||||||
|
return; // No need to update total_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enums are encoded as uint32
|
||||||
|
total_size += field_id_size + varint(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of a sint32 field to the total message size
|
||||||
|
*
|
||||||
|
* Sint32 fields use ZigZag encoding, which is more efficient for negative values.
|
||||||
|
*/
|
||||||
|
static inline void add_sint32_field(uint32_t &total_size, uint32_t field_id_size, int32_t value, bool force = false) {
|
||||||
|
// Skip calculation if value is zero and not forced
|
||||||
|
if (value == 0 && !force) {
|
||||||
|
return; // No need to update total_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZigZag encoding for sint32: (n << 1) ^ (n >> 31)
|
||||||
|
uint32_t zigzag = (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
|
||||||
|
total_size += field_id_size + varint(zigzag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of an int64 field to the total message size
|
||||||
|
*/
|
||||||
|
static inline void add_int64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value, bool force = false) {
|
||||||
|
// Skip calculation if value is zero and not forced
|
||||||
|
if (value == 0 && !force) {
|
||||||
|
return; // No need to update total_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate and directly add to total_size
|
||||||
|
total_size += field_id_size + varint(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of a uint64 field to the total message size
|
||||||
|
*/
|
||||||
|
static inline void add_uint64_field(uint32_t &total_size, uint32_t field_id_size, uint64_t value,
|
||||||
|
bool force = false) {
|
||||||
|
// Skip calculation if value is zero and not forced
|
||||||
|
if (value == 0 && !force) {
|
||||||
|
return; // No need to update total_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate and directly add to total_size
|
||||||
|
total_size += field_id_size + varint(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of a sint64 field to the total message size
|
||||||
|
*
|
||||||
|
* Sint64 fields use ZigZag encoding, which is more efficient for negative values.
|
||||||
|
*/
|
||||||
|
static inline void add_sint64_field(uint32_t &total_size, uint32_t field_id_size, int64_t value, bool force = false) {
|
||||||
|
// Skip calculation if value is zero and not forced
|
||||||
|
if (value == 0 && !force) {
|
||||||
|
return; // No need to update total_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZigZag encoding for sint64: (n << 1) ^ (n >> 63)
|
||||||
|
uint64_t zigzag = (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
|
||||||
|
total_size += field_id_size + varint(zigzag);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of a string/bytes field to the total message size
|
||||||
|
*/
|
||||||
|
static inline void add_string_field(uint32_t &total_size, uint32_t field_id_size, const std::string &str,
|
||||||
|
bool force = false) {
|
||||||
|
// Skip calculation if string is empty and not forced
|
||||||
|
if (str.empty() && !force) {
|
||||||
|
return; // No need to update total_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate and directly add to total_size
|
||||||
|
const uint32_t str_size = static_cast<uint32_t>(str.size());
|
||||||
|
total_size += field_id_size + varint(str_size) + str_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of a nested message field to the total message size
|
||||||
|
*
|
||||||
|
* This helper function directly updates the total_size reference if the nested size
|
||||||
|
* is greater than zero or force is true.
|
||||||
|
*
|
||||||
|
* @param nested_size The pre-calculated size of the nested message
|
||||||
|
*/
|
||||||
|
static inline void add_message_field(uint32_t &total_size, uint32_t field_id_size, uint32_t nested_size,
|
||||||
|
bool force = false) {
|
||||||
|
// Skip calculation if nested message is empty and not forced
|
||||||
|
if (nested_size == 0 && !force) {
|
||||||
|
return; // No need to update total_size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate and directly add to total_size
|
||||||
|
// Field ID + length varint + nested message content
|
||||||
|
total_size += field_id_size + varint(nested_size) + nested_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the size of a nested message field to the total message size
|
||||||
|
*
|
||||||
|
* This templated version directly takes a message object, calculates its size internally,
|
||||||
|
* and updates the total_size reference. This eliminates the need for a temporary variable
|
||||||
|
* at the call site.
|
||||||
|
*
|
||||||
|
* @tparam MessageType The type of the nested message (inferred from parameter)
|
||||||
|
* @param message The nested message object
|
||||||
|
*/
|
||||||
|
template<typename MessageType>
|
||||||
|
static inline void add_message_object(uint32_t &total_size, uint32_t field_id_size, const MessageType &message,
|
||||||
|
bool force = false) {
|
||||||
|
uint32_t nested_size = 0;
|
||||||
|
message.calculate_size(nested_size);
|
||||||
|
|
||||||
|
// Use the base implementation with the calculated nested_size
|
||||||
|
add_message_field(total_size, field_id_size, nested_size, force);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Calculates and adds the sizes of all messages in a repeated field to the total message size
|
||||||
|
*
|
||||||
|
* This helper processes a vector of message objects, calculating the size for each message
|
||||||
|
* and adding it to the total size.
|
||||||
|
*
|
||||||
|
* @tparam MessageType The type of the nested messages in the vector
|
||||||
|
* @param messages Vector of message objects
|
||||||
|
*/
|
||||||
|
template<typename MessageType>
|
||||||
|
static inline void add_repeated_message(uint32_t &total_size, uint32_t field_id_size,
|
||||||
|
const std::vector<MessageType> &messages) {
|
||||||
|
// Skip if the vector is empty
|
||||||
|
if (messages.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For repeated fields, always use force=true
|
||||||
|
for (const auto &message : messages) {
|
||||||
|
add_message_object(total_size, field_id_size, message, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace api
|
||||||
|
} // namespace esphome
|
||||||
@@ -22,22 +22,40 @@ namespace api {
|
|||||||
static const char *const TAG = "api";
|
static const char *const TAG = "api";
|
||||||
|
|
||||||
// APIServer
|
// APIServer
|
||||||
|
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
|
APIServer::APIServer() { global_api_server = this; }
|
||||||
|
|
||||||
void APIServer::setup() {
|
void APIServer::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server...");
|
ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server...");
|
||||||
this->setup_controller();
|
this->setup_controller();
|
||||||
socket_ = socket::socket_ip(SOCK_STREAM, 0);
|
|
||||||
if (socket_ == nullptr) {
|
#ifdef USE_API_NOISE
|
||||||
ESP_LOGW(TAG, "Could not create socket.");
|
uint32_t hash = 88491486UL;
|
||||||
|
|
||||||
|
this->noise_pref_ = global_preferences->make_preference<SavedNoisePsk>(hash, true);
|
||||||
|
|
||||||
|
SavedNoisePsk noise_pref_saved{};
|
||||||
|
if (this->noise_pref_.load(&noise_pref_saved)) {
|
||||||
|
ESP_LOGD(TAG, "Loaded saved Noise PSK");
|
||||||
|
|
||||||
|
this->set_noise_psk(noise_pref_saved.psk);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
this->socket_ = socket::socket_ip(SOCK_STREAM, 0);
|
||||||
|
if (this->socket_ == nullptr) {
|
||||||
|
ESP_LOGW(TAG, "Could not create socket");
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int enable = 1;
|
int enable = 1;
|
||||||
int err = socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
int err = this->socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
|
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
|
||||||
// we can still continue
|
// we can still continue
|
||||||
}
|
}
|
||||||
err = socket_->setblocking(false);
|
err = this->socket_->setblocking(false);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
|
ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
@@ -53,14 +71,14 @@ void APIServer::setup() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = socket_->bind((struct sockaddr *) &server, sl);
|
err = this->socket_->bind((struct sockaddr *) &server, sl);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
|
ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = socket_->listen(4);
|
err = this->socket_->listen(4);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno);
|
ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno);
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
@@ -72,7 +90,7 @@ void APIServer::setup() {
|
|||||||
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
|
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
|
||||||
for (auto &c : this->clients_) {
|
for (auto &c : this->clients_) {
|
||||||
if (!c->remove_)
|
if (!c->remove_)
|
||||||
c->send_log_message(level, tag, message);
|
c->try_send_log_message(level, tag, message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -86,40 +104,51 @@ void APIServer::setup() {
|
|||||||
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
|
[this](const std::shared_ptr<esp32_camera::CameraImage> &image) {
|
||||||
for (auto &c : this->clients_) {
|
for (auto &c : this->clients_) {
|
||||||
if (!c->remove_)
|
if (!c->remove_)
|
||||||
c->send_camera_state(image);
|
c->set_camera_state(image);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void APIServer::loop() {
|
void APIServer::loop() {
|
||||||
// Accept new clients
|
// Accept new clients
|
||||||
while (true) {
|
while (true) {
|
||||||
struct sockaddr_storage source_addr;
|
struct sockaddr_storage source_addr;
|
||||||
socklen_t addr_len = sizeof(source_addr);
|
socklen_t addr_len = sizeof(source_addr);
|
||||||
auto sock = socket_->accept((struct sockaddr *) &source_addr, &addr_len);
|
auto sock = this->socket_->accept((struct sockaddr *) &source_addr, &addr_len);
|
||||||
if (!sock)
|
if (!sock)
|
||||||
break;
|
break;
|
||||||
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
|
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
|
||||||
|
|
||||||
auto *conn = new APIConnection(std::move(sock), this);
|
auto *conn = new APIConnection(std::move(sock), this);
|
||||||
clients_.emplace_back(conn);
|
this->clients_.emplace_back(conn);
|
||||||
conn->start();
|
conn->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Partition clients into remove and active
|
// Process clients and remove disconnected ones in a single pass
|
||||||
auto new_end = std::partition(this->clients_.begin(), this->clients_.end(),
|
if (!this->clients_.empty()) {
|
||||||
[](const std::unique_ptr<APIConnection> &conn) { return !conn->remove_; });
|
size_t client_index = 0;
|
||||||
// print disconnection messages
|
while (client_index < this->clients_.size()) {
|
||||||
for (auto it = new_end; it != this->clients_.end(); ++it) {
|
auto &client = this->clients_[client_index];
|
||||||
this->client_disconnected_trigger_->trigger((*it)->client_info_, (*it)->client_peername_);
|
|
||||||
ESP_LOGV(TAG, "Removing connection to %s", (*it)->client_info_.c_str());
|
|
||||||
}
|
|
||||||
// resize vector
|
|
||||||
this->clients_.erase(new_end, this->clients_.end());
|
|
||||||
|
|
||||||
for (auto &client : this->clients_) {
|
if (client->remove_) {
|
||||||
client->loop();
|
// Handle disconnection
|
||||||
|
this->client_disconnected_trigger_->trigger(client->client_info_, client->client_peername_);
|
||||||
|
ESP_LOGV(TAG, "Removing connection to %s", client->client_info_.c_str());
|
||||||
|
|
||||||
|
// Swap with the last element and pop (avoids expensive vector shifts)
|
||||||
|
if (client_index < this->clients_.size() - 1) {
|
||||||
|
std::swap(this->clients_[client_index], this->clients_.back());
|
||||||
|
}
|
||||||
|
this->clients_.pop_back();
|
||||||
|
// Don't increment client_index since we need to process the swapped element
|
||||||
|
} else {
|
||||||
|
// Process active client
|
||||||
|
client->loop();
|
||||||
|
client_index++; // Move to next client
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->reboot_timeout_ != 0) {
|
if (this->reboot_timeout_ != 0) {
|
||||||
@@ -136,16 +165,22 @@ void APIServer::loop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void APIServer::dump_config() {
|
void APIServer::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG, "API Server:");
|
ESP_LOGCONFIG(TAG, "API Server:");
|
||||||
ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_);
|
ESP_LOGCONFIG(TAG, " Address: %s:%u", network::get_use_address().c_str(), this->port_);
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
ESP_LOGCONFIG(TAG, " Using noise encryption: YES");
|
ESP_LOGCONFIG(TAG, " Using noise encryption: %s", YESNO(this->noise_ctx_->has_psk()));
|
||||||
|
if (!this->noise_ctx_->has_psk()) {
|
||||||
|
ESP_LOGCONFIG(TAG, " Supports noise encryption: YES");
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
ESP_LOGCONFIG(TAG, " Using noise encryption: NO");
|
ESP_LOGCONFIG(TAG, " Using noise encryption: NO");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool APIServer::uses_password() const { return !this->password_.empty(); }
|
bool APIServer::uses_password() const { return !this->password_.empty(); }
|
||||||
|
|
||||||
bool APIServer::check_password(const std::string &password) const {
|
bool APIServer::check_password(const std::string &password) const {
|
||||||
// depend only on input password length
|
// depend only on input password length
|
||||||
const char *a = this->password_.c_str();
|
const char *a = this->password_.c_str();
|
||||||
@@ -174,7 +209,9 @@ bool APIServer::check_password(const std::string &password) const {
|
|||||||
|
|
||||||
return result == 0;
|
return result == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void APIServer::handle_disconnect(APIConnection *conn) {}
|
void APIServer::handle_disconnect(APIConnection *conn) {}
|
||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
|
void APIServer::on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
@@ -342,57 +379,6 @@ void APIServer::on_update(update::UpdateEntity *obj) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
|
|
||||||
void APIServer::set_port(uint16_t port) { this->port_ = port; }
|
|
||||||
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
|
||||||
|
|
||||||
void APIServer::set_password(const std::string &password) { this->password_ = password; }
|
|
||||||
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
|
||||||
for (auto &client : this->clients_) {
|
|
||||||
client->send_homeassistant_service_call(call);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
APIServer::APIServer() { global_api_server = this; }
|
|
||||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
|
||||||
std::function<void(std::string)> f) {
|
|
||||||
this->state_subs_.push_back(HomeAssistantStateSubscription{
|
|
||||||
.entity_id = std::move(entity_id),
|
|
||||||
.attribute = std::move(attribute),
|
|
||||||
.callback = std::move(f),
|
|
||||||
.once = false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
void APIServer::get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
|
||||||
std::function<void(std::string)> f) {
|
|
||||||
this->state_subs_.push_back(HomeAssistantStateSubscription{
|
|
||||||
.entity_id = std::move(entity_id),
|
|
||||||
.attribute = std::move(attribute),
|
|
||||||
.callback = std::move(f),
|
|
||||||
.once = true,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const std::vector<APIServer::HomeAssistantStateSubscription> &APIServer::get_state_subs() const {
|
|
||||||
return this->state_subs_;
|
|
||||||
}
|
|
||||||
uint16_t APIServer::get_port() const { return this->port_; }
|
|
||||||
void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
|
|
||||||
#ifdef USE_HOMEASSISTANT_TIME
|
|
||||||
void APIServer::request_time() {
|
|
||||||
for (auto &client : this->clients_) {
|
|
||||||
if (!client->remove_ && client->is_authenticated())
|
|
||||||
client->send_time_request();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
bool APIServer::is_connected() const { return !this->clients_.empty(); }
|
|
||||||
void APIServer::on_shutdown() {
|
|
||||||
for (auto &c : this->clients_) {
|
|
||||||
c->send_disconnect_request(DisconnectRequest());
|
|
||||||
}
|
|
||||||
delay(10);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
|
void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) {
|
||||||
if (obj->is_internal())
|
if (obj->is_internal())
|
||||||
@@ -402,6 +388,96 @@ void APIServer::on_alarm_control_panel_update(alarm_control_panel::AlarmControlP
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
|
||||||
|
|
||||||
|
void APIServer::set_port(uint16_t port) { this->port_ = port; }
|
||||||
|
|
||||||
|
void APIServer::set_password(const std::string &password) { this->password_ = password; }
|
||||||
|
|
||||||
|
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||||
|
for (auto &client : this->clients_) {
|
||||||
|
client->send_homeassistant_service_call(call);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||||
|
std::function<void(std::string)> f) {
|
||||||
|
this->state_subs_.push_back(HomeAssistantStateSubscription{
|
||||||
|
.entity_id = std::move(entity_id),
|
||||||
|
.attribute = std::move(attribute),
|
||||||
|
.callback = std::move(f),
|
||||||
|
.once = false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void APIServer::get_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||||
|
std::function<void(std::string)> f) {
|
||||||
|
this->state_subs_.push_back(HomeAssistantStateSubscription{
|
||||||
|
.entity_id = std::move(entity_id),
|
||||||
|
.attribute = std::move(attribute),
|
||||||
|
.callback = std::move(f),
|
||||||
|
.once = true,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::vector<APIServer::HomeAssistantStateSubscription> &APIServer::get_state_subs() const {
|
||||||
|
return this->state_subs_;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t APIServer::get_port() const { return this->port_; }
|
||||||
|
|
||||||
|
void APIServer::set_reboot_timeout(uint32_t reboot_timeout) { this->reboot_timeout_ = reboot_timeout; }
|
||||||
|
|
||||||
|
#ifdef USE_API_NOISE
|
||||||
|
bool APIServer::save_noise_psk(psk_t psk, bool make_active) {
|
||||||
|
auto &old_psk = this->noise_ctx_->get_psk();
|
||||||
|
if (std::equal(old_psk.begin(), old_psk.end(), psk.begin())) {
|
||||||
|
ESP_LOGW(TAG, "New PSK matches old");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SavedNoisePsk new_saved_psk{psk};
|
||||||
|
if (!this->noise_pref_.save(&new_saved_psk)) {
|
||||||
|
ESP_LOGW(TAG, "Failed to save Noise PSK");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// ensure it's written immediately
|
||||||
|
if (!global_preferences->sync()) {
|
||||||
|
ESP_LOGW(TAG, "Failed to sync preferences");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP_LOGD(TAG, "Noise PSK saved");
|
||||||
|
if (make_active) {
|
||||||
|
this->set_timeout(100, [this, psk]() {
|
||||||
|
ESP_LOGW(TAG, "Disconnecting all clients to reset connections");
|
||||||
|
this->set_noise_psk(psk);
|
||||||
|
for (auto &c : this->clients_) {
|
||||||
|
c->send_disconnect_request(DisconnectRequest());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_HOMEASSISTANT_TIME
|
||||||
|
void APIServer::request_time() {
|
||||||
|
for (auto &client : this->clients_) {
|
||||||
|
if (!client->remove_ && client->is_authenticated())
|
||||||
|
client->send_time_request();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool APIServer::is_connected() const { return !this->clients_.empty(); }
|
||||||
|
|
||||||
|
void APIServer::on_shutdown() {
|
||||||
|
for (auto &c : this->clients_) {
|
||||||
|
c->send_disconnect_request(DisconnectRequest());
|
||||||
|
}
|
||||||
|
delay(10);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -19,6 +19,12 @@
|
|||||||
namespace esphome {
|
namespace esphome {
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
|
#ifdef USE_API_NOISE
|
||||||
|
struct SavedNoisePsk {
|
||||||
|
psk_t psk;
|
||||||
|
} PACKED; // NOLINT
|
||||||
|
#endif
|
||||||
|
|
||||||
class APIServer : public Component, public Controller {
|
class APIServer : public Component, public Controller {
|
||||||
public:
|
public:
|
||||||
APIServer();
|
APIServer();
|
||||||
@@ -35,6 +41,7 @@ class APIServer : public Component, public Controller {
|
|||||||
void set_reboot_timeout(uint32_t reboot_timeout);
|
void set_reboot_timeout(uint32_t reboot_timeout);
|
||||||
|
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
|
bool save_noise_psk(psk_t psk, bool make_active = true);
|
||||||
void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); }
|
void set_noise_psk(psk_t psk) { noise_ctx_->set_psk(psk); }
|
||||||
std::shared_ptr<APINoiseContext> get_noise_ctx() { return noise_ctx_; }
|
std::shared_ptr<APINoiseContext> get_noise_ctx() { return noise_ctx_; }
|
||||||
#endif // USE_API_NOISE
|
#endif // USE_API_NOISE
|
||||||
@@ -142,6 +149,7 @@ class APIServer : public Component, public Controller {
|
|||||||
|
|
||||||
#ifdef USE_API_NOISE
|
#ifdef USE_API_NOISE
|
||||||
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();
|
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();
|
||||||
|
ESPPreferenceObject noise_pref_;
|
||||||
#endif // USE_API_NOISE
|
#endif // USE_API_NOISE
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import Any
|
import logging
|
||||||
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from aioesphomeapi import APIClient
|
from aioesphomeapi import APIClient
|
||||||
from aioesphomeapi.api_pb2 import SubscribeLogsResponse
|
|
||||||
from aioesphomeapi.log_runner import async_run
|
from aioesphomeapi.log_runner import async_run
|
||||||
|
|
||||||
from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__
|
from esphome.const import CONF_KEY, CONF_PASSWORD, CONF_PORT, __version__
|
||||||
@@ -14,6 +13,12 @@ from esphome.core import CORE
|
|||||||
|
|
||||||
from . import CONF_ENCRYPTION
|
from . import CONF_ENCRYPTION
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from aioesphomeapi.api_pb2 import (
|
||||||
|
SubscribeLogsResponse, # pylint: disable=no-name-in-module
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,37 +10,63 @@ namespace api {
|
|||||||
|
|
||||||
#ifdef USE_BINARY_SENSOR
|
#ifdef USE_BINARY_SENSOR
|
||||||
bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
|
bool ListEntitiesIterator::on_binary_sensor(binary_sensor::BinarySensor *binary_sensor) {
|
||||||
return this->client_->send_binary_sensor_info(binary_sensor);
|
this->client_->send_binary_sensor_info(binary_sensor);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_COVER
|
#ifdef USE_COVER
|
||||||
bool ListEntitiesIterator::on_cover(cover::Cover *cover) { return this->client_->send_cover_info(cover); }
|
bool ListEntitiesIterator::on_cover(cover::Cover *cover) {
|
||||||
|
this->client_->send_cover_info(cover);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_FAN
|
#ifdef USE_FAN
|
||||||
bool ListEntitiesIterator::on_fan(fan::Fan *fan) { return this->client_->send_fan_info(fan); }
|
bool ListEntitiesIterator::on_fan(fan::Fan *fan) {
|
||||||
|
this->client_->send_fan_info(fan);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
bool ListEntitiesIterator::on_light(light::LightState *light) { return this->client_->send_light_info(light); }
|
bool ListEntitiesIterator::on_light(light::LightState *light) {
|
||||||
|
this->client_->send_light_info(light);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SENSOR
|
#ifdef USE_SENSOR
|
||||||
bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) { return this->client_->send_sensor_info(sensor); }
|
bool ListEntitiesIterator::on_sensor(sensor::Sensor *sensor) {
|
||||||
|
this->client_->send_sensor_info(sensor);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_SWITCH
|
#ifdef USE_SWITCH
|
||||||
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) { return this->client_->send_switch_info(a_switch); }
|
bool ListEntitiesIterator::on_switch(switch_::Switch *a_switch) {
|
||||||
|
this->client_->send_switch_info(a_switch);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_BUTTON
|
#ifdef USE_BUTTON
|
||||||
bool ListEntitiesIterator::on_button(button::Button *button) { return this->client_->send_button_info(button); }
|
bool ListEntitiesIterator::on_button(button::Button *button) {
|
||||||
|
this->client_->send_button_info(button);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_TEXT_SENSOR
|
#ifdef USE_TEXT_SENSOR
|
||||||
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
|
bool ListEntitiesIterator::on_text_sensor(text_sensor::TextSensor *text_sensor) {
|
||||||
return this->client_->send_text_sensor_info(text_sensor);
|
this->client_->send_text_sensor_info(text_sensor);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_LOCK
|
#ifdef USE_LOCK
|
||||||
bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) { return this->client_->send_lock_info(a_lock); }
|
bool ListEntitiesIterator::on_lock(lock::Lock *a_lock) {
|
||||||
|
this->client_->send_lock_info(a_lock);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_VALVE
|
#ifdef USE_VALVE
|
||||||
bool ListEntitiesIterator::on_valve(valve::Valve *valve) { return this->client_->send_valve_info(valve); }
|
bool ListEntitiesIterator::on_valve(valve::Valve *valve) {
|
||||||
|
this->client_->send_valve_info(valve);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
|
bool ListEntitiesIterator::on_end() { return this->client_->send_list_info_done(); }
|
||||||
@@ -52,55 +78,83 @@ bool ListEntitiesIterator::on_service(UserServiceDescriptor *service) {
|
|||||||
|
|
||||||
#ifdef USE_ESP32_CAMERA
|
#ifdef USE_ESP32_CAMERA
|
||||||
bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) {
|
bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) {
|
||||||
return this->client_->send_camera_info(camera);
|
this->client_->send_camera_info(camera);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_CLIMATE
|
#ifdef USE_CLIMATE
|
||||||
bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); }
|
bool ListEntitiesIterator::on_climate(climate::Climate *climate) {
|
||||||
|
this->client_->send_climate_info(climate);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_NUMBER
|
#ifdef USE_NUMBER
|
||||||
bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); }
|
bool ListEntitiesIterator::on_number(number::Number *number) {
|
||||||
|
this->client_->send_number_info(number);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_DATETIME_DATE
|
#ifdef USE_DATETIME_DATE
|
||||||
bool ListEntitiesIterator::on_date(datetime::DateEntity *date) { return this->client_->send_date_info(date); }
|
bool ListEntitiesIterator::on_date(datetime::DateEntity *date) {
|
||||||
|
this->client_->send_date_info(date);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_DATETIME_TIME
|
#ifdef USE_DATETIME_TIME
|
||||||
bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) { return this->client_->send_time_info(time); }
|
bool ListEntitiesIterator::on_time(datetime::TimeEntity *time) {
|
||||||
|
this->client_->send_time_info(time);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_DATETIME_DATETIME
|
#ifdef USE_DATETIME_DATETIME
|
||||||
bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *datetime) {
|
bool ListEntitiesIterator::on_datetime(datetime::DateTimeEntity *datetime) {
|
||||||
return this->client_->send_datetime_info(datetime);
|
this->client_->send_datetime_info(datetime);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_TEXT
|
#ifdef USE_TEXT
|
||||||
bool ListEntitiesIterator::on_text(text::Text *text) { return this->client_->send_text_info(text); }
|
bool ListEntitiesIterator::on_text(text::Text *text) {
|
||||||
|
this->client_->send_text_info(text);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_SELECT
|
#ifdef USE_SELECT
|
||||||
bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); }
|
bool ListEntitiesIterator::on_select(select::Select *select) {
|
||||||
|
this->client_->send_select_info(select);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_MEDIA_PLAYER
|
#ifdef USE_MEDIA_PLAYER
|
||||||
bool ListEntitiesIterator::on_media_player(media_player::MediaPlayer *media_player) {
|
bool ListEntitiesIterator::on_media_player(media_player::MediaPlayer *media_player) {
|
||||||
return this->client_->send_media_player_info(media_player);
|
this->client_->send_media_player_info(media_player);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_ALARM_CONTROL_PANEL
|
#ifdef USE_ALARM_CONTROL_PANEL
|
||||||
bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
bool ListEntitiesIterator::on_alarm_control_panel(alarm_control_panel::AlarmControlPanel *a_alarm_control_panel) {
|
||||||
return this->client_->send_alarm_control_panel_info(a_alarm_control_panel);
|
this->client_->send_alarm_control_panel_info(a_alarm_control_panel);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_EVENT
|
#ifdef USE_EVENT
|
||||||
bool ListEntitiesIterator::on_event(event::Event *event) { return this->client_->send_event_info(event); }
|
bool ListEntitiesIterator::on_event(event::Event *event) {
|
||||||
|
this->client_->send_event_info(event);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
bool ListEntitiesIterator::on_update(update::UpdateEntity *update) { return this->client_->send_update_info(update); }
|
bool ListEntitiesIterator::on_update(update::UpdateEntity *update) {
|
||||||
|
this->client_->send_update_info(update);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ class ListEntitiesIterator : public ComponentIterator {
|
|||||||
bool on_update(update::UpdateEntity *update) override;
|
bool on_update(update::UpdateEntity *update) override;
|
||||||
#endif
|
#endif
|
||||||
bool on_end() override;
|
bool on_end() override;
|
||||||
|
bool completed() { return this->state_ == IteratorState::NONE; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
APIConnection *client_;
|
APIConnection *client_;
|
||||||
|
|||||||
@@ -149,6 +149,18 @@ class ProtoWriteBuffer {
|
|||||||
void write(uint8_t value) { this->buffer_->push_back(value); }
|
void write(uint8_t value) { this->buffer_->push_back(value); }
|
||||||
void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); }
|
void encode_varint_raw(ProtoVarInt value) { value.encode(*this->buffer_); }
|
||||||
void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); }
|
void encode_varint_raw(uint32_t value) { this->encode_varint_raw(ProtoVarInt(value)); }
|
||||||
|
/**
|
||||||
|
* Encode a field key (tag/wire type combination).
|
||||||
|
*
|
||||||
|
* @param field_id Field number (tag) in the protobuf message
|
||||||
|
* @param type Wire type value:
|
||||||
|
* - 0: Varint (int32, int64, uint32, uint64, sint32, sint64, bool, enum)
|
||||||
|
* - 1: 64-bit (fixed64, sfixed64, double)
|
||||||
|
* - 2: Length-delimited (string, bytes, embedded messages, packed repeated fields)
|
||||||
|
* - 5: 32-bit (fixed32, sfixed32, float)
|
||||||
|
*
|
||||||
|
* Following https://protobuf.dev/programming-guides/encoding/#structure
|
||||||
|
*/
|
||||||
void encode_field_raw(uint32_t field_id, uint32_t type) {
|
void encode_field_raw(uint32_t field_id, uint32_t type) {
|
||||||
uint32_t val = (field_id << 3) | (type & 0b111);
|
uint32_t val = (field_id << 3) | (type & 0b111);
|
||||||
this->encode_varint_raw(val);
|
this->encode_varint_raw(val);
|
||||||
@@ -157,7 +169,7 @@ class ProtoWriteBuffer {
|
|||||||
if (len == 0 && !force)
|
if (len == 0 && !force)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this->encode_field_raw(field_id, 2);
|
this->encode_field_raw(field_id, 2); // type 2: Length-delimited string
|
||||||
this->encode_varint_raw(len);
|
this->encode_varint_raw(len);
|
||||||
auto *data = reinterpret_cast<const uint8_t *>(string);
|
auto *data = reinterpret_cast<const uint8_t *>(string);
|
||||||
this->buffer_->insert(this->buffer_->end(), data, data + len);
|
this->buffer_->insert(this->buffer_->end(), data, data + len);
|
||||||
@@ -171,26 +183,26 @@ class ProtoWriteBuffer {
|
|||||||
void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) {
|
void encode_uint32(uint32_t field_id, uint32_t value, bool force = false) {
|
||||||
if (value == 0 && !force)
|
if (value == 0 && !force)
|
||||||
return;
|
return;
|
||||||
this->encode_field_raw(field_id, 0);
|
this->encode_field_raw(field_id, 0); // type 0: Varint - uint32
|
||||||
this->encode_varint_raw(value);
|
this->encode_varint_raw(value);
|
||||||
}
|
}
|
||||||
void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) {
|
void encode_uint64(uint32_t field_id, uint64_t value, bool force = false) {
|
||||||
if (value == 0 && !force)
|
if (value == 0 && !force)
|
||||||
return;
|
return;
|
||||||
this->encode_field_raw(field_id, 0);
|
this->encode_field_raw(field_id, 0); // type 0: Varint - uint64
|
||||||
this->encode_varint_raw(ProtoVarInt(value));
|
this->encode_varint_raw(ProtoVarInt(value));
|
||||||
}
|
}
|
||||||
void encode_bool(uint32_t field_id, bool value, bool force = false) {
|
void encode_bool(uint32_t field_id, bool value, bool force = false) {
|
||||||
if (!value && !force)
|
if (!value && !force)
|
||||||
return;
|
return;
|
||||||
this->encode_field_raw(field_id, 0);
|
this->encode_field_raw(field_id, 0); // type 0: Varint - bool
|
||||||
this->write(0x01);
|
this->write(0x01);
|
||||||
}
|
}
|
||||||
void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
|
void encode_fixed32(uint32_t field_id, uint32_t value, bool force = false) {
|
||||||
if (value == 0 && !force)
|
if (value == 0 && !force)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this->encode_field_raw(field_id, 5);
|
this->encode_field_raw(field_id, 5); // type 5: 32-bit fixed32
|
||||||
this->write((value >> 0) & 0xFF);
|
this->write((value >> 0) & 0xFF);
|
||||||
this->write((value >> 8) & 0xFF);
|
this->write((value >> 8) & 0xFF);
|
||||||
this->write((value >> 16) & 0xFF);
|
this->write((value >> 16) & 0xFF);
|
||||||
@@ -200,7 +212,7 @@ class ProtoWriteBuffer {
|
|||||||
if (value == 0 && !force)
|
if (value == 0 && !force)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this->encode_field_raw(field_id, 5);
|
this->encode_field_raw(field_id, 1); // type 1: 64-bit fixed64
|
||||||
this->write((value >> 0) & 0xFF);
|
this->write((value >> 0) & 0xFF);
|
||||||
this->write((value >> 8) & 0xFF);
|
this->write((value >> 8) & 0xFF);
|
||||||
this->write((value >> 16) & 0xFF);
|
this->write((value >> 16) & 0xFF);
|
||||||
@@ -254,7 +266,7 @@ class ProtoWriteBuffer {
|
|||||||
this->encode_uint64(field_id, uvalue, force);
|
this->encode_uint64(field_id, uvalue, force);
|
||||||
}
|
}
|
||||||
template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) {
|
template<class C> void encode_message(uint32_t field_id, const C &value, bool force = false) {
|
||||||
this->encode_field_raw(field_id, 2);
|
this->encode_field_raw(field_id, 2); // type 2: Length-delimited message
|
||||||
size_t begin = this->buffer_->size();
|
size_t begin = this->buffer_->size();
|
||||||
|
|
||||||
value.encode(*this);
|
value.encode(*this);
|
||||||
@@ -276,6 +288,7 @@ class ProtoMessage {
|
|||||||
virtual ~ProtoMessage() = default;
|
virtual ~ProtoMessage() = default;
|
||||||
virtual void encode(ProtoWriteBuffer buffer) const = 0;
|
virtual void encode(ProtoWriteBuffer buffer) const = 0;
|
||||||
void decode(const uint8_t *buffer, size_t length);
|
void decode(const uint8_t *buffer, size_t length);
|
||||||
|
virtual void calculate_size(uint32_t &total_size) const = 0;
|
||||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||||
std::string dump() const;
|
std::string dump() const;
|
||||||
virtual void dump_to(std::string &out) const = 0;
|
virtual void dump_to(std::string &out) const = 0;
|
||||||
@@ -298,13 +311,29 @@ class ProtoService {
|
|||||||
virtual void on_fatal_error() = 0;
|
virtual void on_fatal_error() = 0;
|
||||||
virtual void on_unauthenticated_access() = 0;
|
virtual void on_unauthenticated_access() = 0;
|
||||||
virtual void on_no_setup_connection() = 0;
|
virtual void on_no_setup_connection() = 0;
|
||||||
virtual ProtoWriteBuffer create_buffer() = 0;
|
/**
|
||||||
|
* Create a buffer with a reserved size.
|
||||||
|
* @param reserve_size The number of bytes to pre-allocate in the buffer. This is a hint
|
||||||
|
* to optimize memory usage and avoid reallocations during encoding.
|
||||||
|
* Implementations should aim to allocate at least this size.
|
||||||
|
* @return A ProtoWriteBuffer object with the reserved size.
|
||||||
|
*/
|
||||||
|
virtual ProtoWriteBuffer create_buffer(uint32_t reserve_size) = 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;
|
||||||
|
|
||||||
|
// Optimized method that pre-allocates buffer based on message size
|
||||||
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();
|
uint32_t msg_size = 0;
|
||||||
|
msg.calculate_size(msg_size);
|
||||||
|
|
||||||
|
// Create a pre-sized buffer
|
||||||
|
auto buffer = this->create_buffer(msg_size);
|
||||||
|
|
||||||
|
// Encode message into the buffer
|
||||||
msg.encode(buffer);
|
msg.encode(buffer);
|
||||||
|
|
||||||
|
// Send the buffer
|
||||||
return this->send_buffer(buffer, message_type);
|
return this->send_buffer(buffer, message_type);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ class InitialStateIterator : public ComponentIterator {
|
|||||||
#ifdef USE_UPDATE
|
#ifdef USE_UPDATE
|
||||||
bool on_update(update::UpdateEntity *update) override;
|
bool on_update(update::UpdateEntity *update) override;
|
||||||
#endif
|
#endif
|
||||||
|
bool completed() { return this->state_ == IteratorState::NONE; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
APIConnection *client_;
|
APIConnection *client_;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
|
from esphome import pins
|
||||||
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.const import (
|
from esphome.const import (
|
||||||
|
CONF_CALIBRATION,
|
||||||
CONF_CAPACITANCE,
|
CONF_CAPACITANCE,
|
||||||
CONF_DIV_RATIO,
|
CONF_DIV_RATIO,
|
||||||
CONF_INDOOR,
|
CONF_INDOOR,
|
||||||
CONF_IRQ_PIN,
|
CONF_IRQ_PIN,
|
||||||
CONF_LIGHTNING_THRESHOLD,
|
CONF_LIGHTNING_THRESHOLD,
|
||||||
CONF_MASK_DISTURBER,
|
CONF_MASK_DISTURBER,
|
||||||
CONF_CALIBRATION,
|
|
||||||
CONF_TUNE_ANTENNA,
|
|
||||||
CONF_NOISE_LEVEL,
|
CONF_NOISE_LEVEL,
|
||||||
CONF_SPIKE_REJECTION,
|
CONF_SPIKE_REJECTION,
|
||||||
|
CONF_TUNE_ANTENNA,
|
||||||
CONF_WATCHDOG_THRESHOLD,
|
CONF_WATCHDOG_THRESHOLD,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user