mirror of
https://github.com/esphome/esphome.git
synced 2025-11-01 15:41:52 +00:00
Compare commits
471 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5086cd716f | ||
|
|
4937af0cd9 | ||
|
|
877a5fda41 | ||
|
|
1fac91a659 | ||
|
|
0a4837c1f0 | ||
|
|
e7404183a0 | ||
|
|
44f8dcfb6e | ||
|
|
481e0e98f8 | ||
|
|
9de40c26eb | ||
|
|
ad953f02d1 | ||
|
|
3869e56521 | ||
|
|
63d87b17aa | ||
|
|
2e59ad90cc | ||
|
|
1b8c9edcde | ||
|
|
d4c2a85f9c | ||
|
|
8c75b87e94 | ||
|
|
409d4b9d47 | ||
|
|
4e3b95d120 | ||
|
|
61a9c9fa33 | ||
|
|
9c605f2d46 | ||
|
|
44bb5a89c8 | ||
|
|
cbdb96f105 | ||
|
|
9ee3463d07 | ||
|
|
8bf0448f41 | ||
|
|
14e04eb231 | ||
|
|
1be9bac3a9 | ||
|
|
02b5a3efb8 | ||
|
|
bd457f64d8 | ||
|
|
9efeea14f2 | ||
|
|
d2cd65f5db | ||
|
|
2735f96516 | ||
|
|
6847645782 | ||
|
|
b0bc898278 | ||
|
|
c0f6af7213 | ||
|
|
5edebaf468 | ||
|
|
d436409153 | ||
|
|
8c41fc2b1d | ||
|
|
46f17bea66 | ||
|
|
11477dbc03 | ||
|
|
947c104eff | ||
|
|
e5366dbbe7 | ||
|
|
d3375193a9 | ||
|
|
6144ce1fe0 | ||
|
|
1771e673d2 | ||
|
|
d258e06fd7 | ||
|
|
854f4a8896 | ||
|
|
f94c221a9a | ||
|
|
6a2f0f5143 | ||
|
|
183e2a8471 | ||
|
|
c6c2842bdb | ||
|
|
f26767b65e | ||
|
|
98d32876b5 | ||
|
|
e5d0f3c036 | ||
|
|
cc15aaacbb | ||
|
|
553df1d57b | ||
|
|
b92311402a | ||
|
|
93796491af | ||
|
|
c038cf27a7 | ||
|
|
1f42d32eb5 | ||
|
|
06bde559da | ||
|
|
922f7167f5 | ||
|
|
90c0d3e12f | ||
|
|
bf5f846fc6 | ||
|
|
926bcc71ae | ||
|
|
ea4a458214 | ||
|
|
b3ae3e1feb | ||
|
|
fe7af21c91 | ||
|
|
29f72037fe | ||
|
|
1d6b4bfcef | ||
|
|
5bfac5ec09 | ||
|
|
dfffaace26 | ||
|
|
1d5f628c7a | ||
|
|
cb8a6f66fa | ||
|
|
cb21c7c18d | ||
|
|
0d104776bc | ||
|
|
5f27757039 | ||
|
|
532907219b | ||
|
|
eeaba74553 | ||
|
|
dd637582a4 | ||
|
|
b0d12aeea1 | ||
|
|
bdbd813455 | ||
|
|
a6fac2b175 | ||
|
|
5ce923ea90 | ||
|
|
29f0508dc2 | ||
|
|
3ffa59f0cd | ||
|
|
790d6ef94c | ||
|
|
7828f48b9a | ||
|
|
768c71830b | ||
|
|
ceb0564ebf | ||
|
|
20f7eb7327 | ||
|
|
441d5bd44d | ||
|
|
9fa19df2ff | ||
|
|
39f64f597e | ||
|
|
160429eb24 | ||
|
|
6516c64e67 | ||
|
|
4c8a703084 | ||
|
|
335210d788 | ||
|
|
9b04e657db | ||
|
|
f7311aa025 | ||
|
|
fb24e55c8d | ||
|
|
b58ca46a46 | ||
|
|
76991cdcc4 | ||
|
|
69c7cf783e | ||
|
|
f751c3828e | ||
|
|
5c65f9f9ad | ||
|
|
81ae6709e4 | ||
|
|
593a3d48fb | ||
|
|
a8b90283d8 | ||
|
|
80076f935d | ||
|
|
9fbb3659a6 | ||
|
|
fee446c28a | ||
|
|
1d56f0b035 | ||
|
|
34e8979d40 | ||
|
|
2966a62429 | ||
|
|
5983ccc55c | ||
|
|
de382b704c | ||
|
|
16dbbfabc6 | ||
|
|
af8d04818d | ||
|
|
ee19ef1aac | ||
|
|
5e2d4e332a | ||
|
|
c6c857dfff | ||
|
|
8dbac20f8b | ||
|
|
341fddb9aa | ||
|
|
456824669f | ||
|
|
62f3039d82 | ||
|
|
be4c718859 | ||
|
|
c2f9ed7c59 | ||
|
|
bfac6607d1 | ||
|
|
513066ba52 | ||
|
|
316777f757 | ||
|
|
246950159d | ||
|
|
31d6a54b06 | ||
|
|
5c3a6164bb | ||
|
|
1652914d39 | ||
|
|
618cfd9ec5 | ||
|
|
f97cfe9916 | ||
|
|
b9259a0238 | ||
|
|
5abbe385c5 | ||
|
|
e43dcded62 | ||
|
|
887081fd71 | ||
|
|
71ded24fce | ||
|
|
1e2a9e8348 | ||
|
|
64a3aa7092 | ||
|
|
fda8dd4ce3 | ||
|
|
1efabd27d8 | ||
|
|
caa651e55b | ||
|
|
b0a3891498 | ||
|
|
2a9e3d84fd | ||
|
|
a3dcac62f9 | ||
|
|
6b535b11f8 | ||
|
|
d9f09a7523 | ||
|
|
159744e09e | ||
|
|
c2637a76f7 | ||
|
|
237edd75d1 | ||
|
|
a34d5e3901 | ||
|
|
1dd43a75f2 | ||
|
|
1f5cbca509 | ||
|
|
3749c11f21 | ||
|
|
66cdb761dc | ||
|
|
f0d9ad6a4e | ||
|
|
03e317d052 | ||
|
|
ba461e51a8 | ||
|
|
80949521b6 | ||
|
|
acbb8e9fd0 | ||
|
|
90394a50df | ||
|
|
5379794f16 | ||
|
|
0a32321c85 | ||
|
|
c9062599df | ||
|
|
10a6e9b4ee | ||
|
|
4b8ec44262 | ||
|
|
bd74ed4bc0 | ||
|
|
fc42f14448 | ||
|
|
d01f296420 | ||
|
|
27112e2ace | ||
|
|
837930234f | ||
|
|
e19aa3bbe0 | ||
|
|
35b5c1ed56 | ||
|
|
3e65e6c69a | ||
|
|
3b3297d269 | ||
|
|
fc0deb642a | ||
|
|
9f2b2f51ff | ||
|
|
c9d93ff685 | ||
|
|
fa72990a63 | ||
|
|
e5afb1c4ea | ||
|
|
73ead5f328 | ||
|
|
5c57b51378 | ||
|
|
e25935ef21 | ||
|
|
c7a52c3894 | ||
|
|
01a4b4e82f | ||
|
|
766866197b | ||
|
|
9b5a3cbcd3 | ||
|
|
d2ed3b9bec | ||
|
|
99d2db42cd | ||
|
|
7619507e6c | ||
|
|
53a4689ed1 | ||
|
|
0a82e6e792 | ||
|
|
98855e4123 | ||
|
|
71d9d64a02 | ||
|
|
6a09d7c49b | ||
|
|
46e50ba53f | ||
|
|
f1e3ff2ed2 | ||
|
|
7787fa8f29 | ||
|
|
70902029f8 | ||
|
|
4f9a56c884 | ||
|
|
3715ba030b | ||
|
|
2e49fd7b48 | ||
|
|
06912b492f | ||
|
|
442e58b07a | ||
|
|
799f04efc0 | ||
|
|
cc7dbeada6 | ||
|
|
45d368e3a1 | ||
|
|
628a94bad3 | ||
|
|
0c93be97a9 | ||
|
|
54eb6070fb | ||
|
|
4dbf1c521e | ||
|
|
0651716b96 | ||
|
|
f30b8f6b3c | ||
|
|
0992609bf4 | ||
|
|
18c08f24ad | ||
|
|
a7f53aea0e | ||
|
|
c399905675 | ||
|
|
5cb0c11feb | ||
|
|
08b67e7aea | ||
|
|
07ae8ec553 | ||
|
|
04c3a43c17 | ||
|
|
b632344596 | ||
|
|
fb8ec79a52 | ||
|
|
7dd16df846 | ||
|
|
551e9c6111 | ||
|
|
cc9f0b3f47 | ||
|
|
d77c3abdc0 | ||
|
|
dd37a4e04c | ||
|
|
1f5c79bd17 | ||
|
|
623570a117 | ||
|
|
cdbc146e5d | ||
|
|
7ae611256a | ||
|
|
b62c47fede | ||
|
|
99f497c3b8 | ||
|
|
4f88c2489b | ||
|
|
294ba1fca7 | ||
|
|
be61b38a2c | ||
|
|
f9797825ad | ||
|
|
fd4b7d4588 | ||
|
|
062cedc200 | ||
|
|
79b9d0579d | ||
|
|
ab31117bf3 | ||
|
|
d31040f5d8 | ||
|
|
52d19fa43d | ||
|
|
8ca34f7098 | ||
|
|
4c4099966a | ||
|
|
86ac7f3a59 | ||
|
|
9e400a7857 | ||
|
|
d5278351da | ||
|
|
36861595f1 | ||
|
|
d604321f37 | ||
|
|
964ab65497 | ||
|
|
3b940b1c04 | ||
|
|
a91e6a6bdf | ||
|
|
8600620305 | ||
|
|
96721f305f | ||
|
|
2bf70d7d00 | ||
|
|
1d8c170f48 | ||
|
|
6009c7edb4 | ||
|
|
e3f36c033e | ||
|
|
d4eb0f1655 | ||
|
|
5fca480921 | ||
|
|
7051f897bc | ||
|
|
2cb3015a28 | ||
|
|
d0859a7d33 | ||
|
|
e20ec00071 | ||
|
|
150114d774 | ||
|
|
89dfa5ea82 | ||
|
|
61ebc629f6 | ||
|
|
32f2da77f8 | ||
|
|
bfca3f242a | ||
|
|
3dfff2930a | ||
|
|
027e0de48e | ||
|
|
c811141a4f | ||
|
|
871c0ee2a5 | ||
|
|
b8a7741c61 | ||
|
|
97aa930ad2 | ||
|
|
2a5def10e7 | ||
|
|
969834e037 | ||
|
|
d73a44c504 | ||
|
|
8aec092ab6 | ||
|
|
4fa959ba45 | ||
|
|
b6011b9353 | ||
|
|
40a5005d94 | ||
|
|
c5eba21ff6 | ||
|
|
4891cfef56 | ||
|
|
4395664547 | ||
|
|
04d926af39 | ||
|
|
f9a31c1abb | ||
|
|
b43712d78d | ||
|
|
01904a0f10 | ||
|
|
dd875e7529 | ||
|
|
f1dcf0f0b8 | ||
|
|
a045d001bf | ||
|
|
066c1022d0 | ||
|
|
dca1c0f160 | ||
|
|
2419bc3678 | ||
|
|
c19b3ecd43 | ||
|
|
ef1e91d838 | ||
|
|
e5929225eb | ||
|
|
59c192becc | ||
|
|
a800816750 | ||
|
|
99d9ab4e40 | ||
|
|
f310ca1b74 | ||
|
|
f763daa577 | ||
|
|
607c3ae651 | ||
|
|
970563e07b | ||
|
|
e006045f59 | ||
|
|
fff3645901 | ||
|
|
a5383fd208 | ||
|
|
5591832b50 | ||
|
|
9ce3a2059f | ||
|
|
69e6cf2c0c | ||
|
|
28635124f9 | ||
|
|
25b116048c | ||
|
|
035be87a83 | ||
|
|
e8bdbc45a9 | ||
|
|
429caccefa | ||
|
|
744ca1af7c | ||
|
|
106f0d611f | ||
|
|
d826416684 | ||
|
|
24ba9eba46 | ||
|
|
424c34225f | ||
|
|
d781f3a11b | ||
|
|
a80f9ed336 | ||
|
|
92bbedfa5a | ||
|
|
86710ed483 | ||
|
|
da7eb9ac90 | ||
|
|
c411043681 | ||
|
|
93f8ee7e60 | ||
|
|
0efc1f06f2 | ||
|
|
81959804df | ||
|
|
75b524ddc4 | ||
|
|
f599c36272 | ||
|
|
9bb64315f3 | ||
|
|
575badc690 | ||
|
|
4b91cfb7f9 | ||
|
|
9ad9d64ac7 | ||
|
|
5a2cfa2798 | ||
|
|
eb24da7c82 | ||
|
|
f93e261d75 | ||
|
|
501f88ca86 | ||
|
|
360effcb72 | ||
|
|
eb9bd69405 | ||
|
|
11b8210e36 | ||
|
|
a57a842f7b | ||
|
|
a8c253a2a5 | ||
|
|
8b737aabd9 | ||
|
|
0db4815f3d | ||
|
|
139db58a66 | ||
|
|
d23376b81e | ||
|
|
99d90845b5 | ||
|
|
ea0127e42b | ||
|
|
c32fec7432 | ||
|
|
8bd23dd457 | ||
|
|
97a12c0169 | ||
|
|
635916737b | ||
|
|
65c50e4f01 | ||
|
|
5cf18235e3 | ||
|
|
b80f3fdec9 | ||
|
|
0426be9280 | ||
|
|
7c678659d4 | ||
|
|
13fe9e83fa | ||
|
|
4711f36a1f | ||
|
|
01e2a51132 | ||
|
|
a70a205ace | ||
|
|
33625e2dd3 | ||
|
|
0277218319 | ||
|
|
18a8c727fa | ||
|
|
80ad784a4e | ||
|
|
ebadaa9660 | ||
|
|
7bc51582f0 | ||
|
|
11fb54c74e | ||
|
|
913ac8b7e8 | ||
|
|
2b9350ce76 | ||
|
|
3b18f1b87f | ||
|
|
c5c24c1989 | ||
|
|
c3938d04f3 | ||
|
|
f968713be8 | ||
|
|
7b11284008 | ||
|
|
f39c0d52ee | ||
|
|
a3756a9600 | ||
|
|
afa436fe8f | ||
|
|
48b5ea9e59 | ||
|
|
56974153f1 | ||
|
|
9a2cd71571 | ||
|
|
d1c6368283 | ||
|
|
5c3268b8d4 | ||
|
|
25af5ab7c6 | ||
|
|
4d586b1446 | ||
|
|
bb759d52c8 | ||
|
|
9a2cf05c5f | ||
|
|
c79d700d03 | ||
|
|
482a3aebc9 | ||
|
|
387f249363 | ||
|
|
3d917d0b7e | ||
|
|
824f3187ac | ||
|
|
e3c27a483c | ||
|
|
a33bb32874 | ||
|
|
93d9d4b50a | ||
|
|
2376a2c941 | ||
|
|
b92702a312 | ||
|
|
b11d5f6799 | ||
|
|
072dce340e | ||
|
|
cccb1a2c9e | ||
|
|
063d9c47a4 | ||
|
|
8d8d421286 | ||
|
|
0ce57e5a39 | ||
|
|
aebad04c0b | ||
|
|
514d11d46f | ||
|
|
96e46db272 | ||
|
|
76f78877f6 | ||
|
|
4ffa68b773 | ||
|
|
8eaffee160 | ||
|
|
557a622f71 | ||
|
|
e9c6556296 | ||
|
|
dce9d59dfe | ||
|
|
d3e291b442 | ||
|
|
d4686c0fb1 | ||
|
|
ae9b247f47 | ||
|
|
a59761d292 | ||
|
|
030c87d142 | ||
|
|
95ed3e9d46 | ||
|
|
d0eaebe19f | ||
|
|
9ecead2645 | ||
|
|
98166dfa66 | ||
|
|
5645be4e0f | ||
|
|
9a7a205510 | ||
|
|
7e3b8fd346 | ||
|
|
3d6dcc9eee | ||
|
|
4f6982fbc5 | ||
|
|
00c144daeb | ||
|
|
54660300e9 | ||
|
|
5dc40049be | ||
|
|
1e46b4073f | ||
|
|
29fc4af0fc | ||
|
|
4030a2e253 | ||
|
|
ea80cb751b | ||
|
|
6aa61dbce7 | ||
|
|
cdc9c99d40 | ||
|
|
a546677b08 | ||
|
|
5c3af1d3f6 | ||
|
|
66b0b6feeb | ||
|
|
7665a220a0 | ||
|
|
4250af4dd9 | ||
|
|
73252ccd25 | ||
|
|
33bf78c369 | ||
|
|
96ded4e402 | ||
|
|
076124eb71 | ||
|
|
b660e5a7fa | ||
|
|
3b4ea0ed0a | ||
|
|
403b6e32e3 | ||
|
|
b91a1aa027 | ||
|
|
13dbdd9b16 | ||
|
|
37bc0b3b5a | ||
|
|
be70a96651 | ||
|
|
d83d214497 | ||
|
|
6ec0f80b76 | ||
|
|
06f566346d | ||
|
|
b680649113 | ||
|
|
5ab2ef4079 | ||
|
|
392ed64375 | ||
|
|
566c129435 | ||
|
|
c903eb2d01 | ||
|
|
69c78651d5 | ||
|
|
34487c9de4 | ||
|
|
822377be8b |
@@ -49,7 +49,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
|
||||
53
.clang-tidy
53
.clang-tidy
@@ -4,14 +4,29 @@ Checks: >-
|
||||
-abseil-*,
|
||||
-android-*,
|
||||
-boost-*,
|
||||
-bugprone-macro-parentheses,
|
||||
-bugprone-branch-clone,
|
||||
-bugprone-narrowing-conversions,
|
||||
-bugprone-signed-char-misuse,
|
||||
-bugprone-too-small-loop-variable,
|
||||
-cert-dcl50-cpp,
|
||||
-cert-err58-cpp,
|
||||
-clang-analyzer-core.CallAndMessage,
|
||||
-cert-oop57-cpp,
|
||||
-cert-str34-c,
|
||||
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
||||
-clang-analyzer-osx.*,
|
||||
-clang-analyzer-security.*,
|
||||
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
||||
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
||||
-clang-diagnostic-shadow-field,
|
||||
-clang-diagnostic-sign-compare,
|
||||
-clang-diagnostic-unused-variable,
|
||||
-clang-diagnostic-unused-const-variable,
|
||||
-cppcoreguidelines-avoid-c-arrays,
|
||||
-cppcoreguidelines-avoid-goto,
|
||||
-cppcoreguidelines-c-copy-assignment-signature,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-init-variables,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-narrowing-conversions,
|
||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||
-cppcoreguidelines-owning-memory,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
@@ -24,42 +39,52 @@ Checks: >-
|
||||
-cppcoreguidelines-pro-type-union-access,
|
||||
-cppcoreguidelines-pro-type-vararg,
|
||||
-cppcoreguidelines-special-member-functions,
|
||||
-fuchsia-*,
|
||||
-fuchsia-default-arguments,
|
||||
-fuchsia-multiple-inheritance,
|
||||
-fuchsia-overloaded-operator,
|
||||
-fuchsia-statically-constructed-objects,
|
||||
-fuchsia-default-arguments-declarations,
|
||||
-fuchsia-default-arguments-calls,
|
||||
-google-build-using-namespace,
|
||||
-google-explicit-constructor,
|
||||
-google-readability-braces-around-statements,
|
||||
-google-readability-casting,
|
||||
-google-readability-todo,
|
||||
-google-runtime-int,
|
||||
-google-runtime-references,
|
||||
-hicpp-*,
|
||||
-llvm-else-after-return,
|
||||
-llvm-header-guard,
|
||||
-llvm-include-order,
|
||||
-misc-unconventional-assign-operator,
|
||||
-llvm-qualified-auto,
|
||||
-llvmlibc-*,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-misc-no-recursion,
|
||||
-misc-unused-parameters,
|
||||
-modernize-deprecated-headers,
|
||||
-modernize-pass-by-value,
|
||||
-modernize-pass-by-value,
|
||||
-modernize-avoid-c-arrays,
|
||||
-modernize-return-braced-init-list,
|
||||
-modernize-use-auto,
|
||||
-modernize-use-default-member-init,
|
||||
-modernize-use-equals-default,
|
||||
-modernize-use-trailing-return-type,
|
||||
-mpi-*,
|
||||
-objc-*,
|
||||
-performance-unnecessary-value-param,
|
||||
-readability-braces-around-statements,
|
||||
-readability-const-return-type,
|
||||
-readability-convert-member-functions-to-static,
|
||||
-readability-else-after-return,
|
||||
-readability-implicit-bool-conversion,
|
||||
-readability-isolate-declaration,
|
||||
-readability-magic-numbers,
|
||||
-readability-make-member-function-const,
|
||||
-readability-named-parameter,
|
||||
-readability-qualified-auto,
|
||||
-readability-redundant-access-specifiers,
|
||||
-readability-redundant-member-init,
|
||||
-warnings-as-errors,
|
||||
-zircon-*
|
||||
-readability-redundant-string-init,
|
||||
-readability-uppercase-literal-suffix,
|
||||
-readability-use-anyofallof,
|
||||
-warnings-as-errors
|
||||
WarningsAsErrors: '*'
|
||||
HeaderFilterRegex: '^.*/src/esphome/.*'
|
||||
AnalyzeTemporaryDtors: false
|
||||
FormatStyle: google
|
||||
CheckOptions:
|
||||
|
||||
@@ -2,16 +2,29 @@
|
||||
"name": "ESPHome Dev",
|
||||
"context": "..",
|
||||
"dockerFile": "../docker/Dockerfile.dev",
|
||||
"postCreateCommand": "mkdir -p config && pip3 install -e .",
|
||||
"runArgs": ["--privileged", "-e", "ESPHOME_DASHBOARD_USE_PING=1"],
|
||||
"postCreateCommand": [
|
||||
"script/devcontainer-post-create"
|
||||
],
|
||||
"runArgs": [
|
||||
"--privileged",
|
||||
"-e",
|
||||
"ESPHOME_DASHBOARD_USE_PING=1"
|
||||
],
|
||||
"appPort": 6052,
|
||||
"extensions": [
|
||||
// python
|
||||
"ms-python.python",
|
||||
"visualstudioexptteam.vscodeintellicode",
|
||||
"redhat.vscode-yaml"
|
||||
// yaml
|
||||
"redhat.vscode-yaml",
|
||||
// cpp
|
||||
"ms-vscode.cpptools",
|
||||
// editorconfig
|
||||
"editorconfig.editorconfig",
|
||||
],
|
||||
"settings": {
|
||||
"python.pythonPath": "/usr/local/bin/python",
|
||||
"python.languageServer": "Pylance",
|
||||
"python.pythonPath": "/usr/bin/python3",
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.enabled": true,
|
||||
"python.formatting.provider": "black",
|
||||
@@ -19,7 +32,7 @@
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnType": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"terminal.integrated.shell.linux": "/bin/bash",
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"yaml.customTags": [
|
||||
"!secret scalar",
|
||||
"!lambda scalar",
|
||||
@@ -27,6 +40,18 @@
|
||||
"!include_dir_list scalar",
|
||||
"!include_dir_merge_list scalar",
|
||||
"!include_dir_merge_named scalar"
|
||||
]
|
||||
],
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/*.pyc": {
|
||||
"when": "$(basename).py"
|
||||
},
|
||||
"**/__pycache__": true
|
||||
},
|
||||
"files.associations": {
|
||||
"**/.vscode/*.json": "jsonc"
|
||||
},
|
||||
"C_Cpp.clang_format_path": "/usr/bin/clang-format-11",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,10 @@ venv.bak/
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
# PlatformIO
|
||||
.pio/
|
||||
|
||||
# ESPHome
|
||||
config/
|
||||
examples/
|
||||
Dockerfile
|
||||
|
||||
@@ -7,7 +7,7 @@ insert_final_newline = true
|
||||
charset = utf-8
|
||||
|
||||
# python
|
||||
[*.{py}]
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
@@ -25,4 +25,10 @@ indent_size = 2
|
||||
[*.{yaml,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
quote_type = single
|
||||
quote_type = single
|
||||
|
||||
# JSON
|
||||
[*.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
|
||||
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,25 +1,22 @@
|
||||
# What does this implement/fix?
|
||||
|
||||
Quick description
|
||||
Quick description and explanation of changes
|
||||
|
||||
## Types of changes
|
||||
|
||||
- [ ] Bugfix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Configuration change (this will require users to update their yaml configuration files to keep working)
|
||||
- [ ] Other
|
||||
|
||||
**Related issue or feature (if applicable):** fixes <link to issue>
|
||||
|
||||
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>
|
||||
|
||||
# Test Environment
|
||||
|
||||
## Test Environment
|
||||
|
||||
- [ ] ESP32
|
||||
- [ ] ESP8266
|
||||
- [ ] Windows
|
||||
- [ ] Mac OS
|
||||
- [ ] Linux
|
||||
|
||||
## Example entry for `config.yaml`:
|
||||
<!--
|
||||
@@ -34,11 +31,6 @@ Quick description
|
||||
|
||||
```
|
||||
|
||||
# Explain your changes
|
||||
|
||||
Describe your changes here to communicate to the maintainers **why we should accept this pull request**.
|
||||
Very important to fill if no issue linked
|
||||
|
||||
## Checklist:
|
||||
- [ ] The code change is tested and works locally.
|
||||
- [ ] Tests have been added to verify that the new code works (under `tests/` folder).
|
||||
|
||||
49
.github/workflows/ci-docker.yml
vendored
49
.github/workflows/ci-docker.yml
vendored
@@ -3,7 +3,7 @@ name: CI for docker images
|
||||
# Only run when docker paths change
|
||||
on:
|
||||
push:
|
||||
branches: [dev, beta, master]
|
||||
branches: [dev, beta, release]
|
||||
paths:
|
||||
- 'docker/**'
|
||||
- '.github/workflows/**'
|
||||
@@ -18,38 +18,23 @@ jobs:
|
||||
name: Build docker containers
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [amd64, armv7, aarch64]
|
||||
build_type: ["hassio", "docker"]
|
||||
build_type: ["ha-addon", "docker", "lint"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up env variables
|
||||
run: |
|
||||
base_version="3.0.0"
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- name: Set TAG
|
||||
run: |
|
||||
echo "TAG=check" >> $GITHUB_ENV
|
||||
|
||||
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
||||
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
||||
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
|
||||
dockerfile="docker/Dockerfile.hassio"
|
||||
else
|
||||
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
|
||||
build_to="esphome/esphome-${{ matrix.arch }}"
|
||||
dockerfile="docker/Dockerfile"
|
||||
fi
|
||||
|
||||
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
|
||||
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
|
||||
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
|
||||
- name: Pull for cache
|
||||
run: |
|
||||
docker pull "${BUILD_TO}:dev" || true
|
||||
- name: Register QEMU binfmt
|
||||
run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
|
||||
- run: |
|
||||
docker build \
|
||||
--build-arg "BUILD_FROM=${BUILD_FROM}" \
|
||||
--build-arg "BUILD_VERSION=ci" \
|
||||
--cache-from "${BUILD_TO}:dev" \
|
||||
--file "${DOCKERFILE}" \
|
||||
.
|
||||
- name: Run build
|
||||
run: |
|
||||
docker/build.py \
|
||||
--tag "${TAG}" \
|
||||
--arch "${{ matrix.arch }}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
build
|
||||
|
||||
193
.github/workflows/ci.yml
vendored
193
.github/workflows/ci.yml
vendored
@@ -4,67 +4,98 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
# On dev branch release-dev already performs CI checks
|
||||
# On other branches the `pull_request` trigger will be used
|
||||
branches: [beta, master]
|
||||
branches: [dev, beta, release]
|
||||
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint-clang-format:
|
||||
ci-with-container:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ubuntu-latest
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: esphome/esphome-lint:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Set up the pio project so that the cpp checks know how files are compiled
|
||||
# (build flags, libraries etc)
|
||||
- name: Set up platformio environment
|
||||
run: pio init --ide atom
|
||||
|
||||
- name: Run clang-format
|
||||
run: script/clang-format -i
|
||||
- name: Suggest changes
|
||||
run: script/ci-suggest-changes
|
||||
|
||||
lint-clang-tidy:
|
||||
runs-on: ubuntu-latest
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: esphome/esphome-lint:latest
|
||||
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
split: [1, 2, 3, 4]
|
||||
include:
|
||||
- id: clang-format
|
||||
name: Run script/clang-format
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy 1/4
|
||||
split: 1
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy 2/4
|
||||
split: 2
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy 3/4
|
||||
split: 3
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy 4/4
|
||||
split: 4
|
||||
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: ghcr.io/esphome/esphome-lint:1.1
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Set up the pio project so that the cpp checks know how files are compiled
|
||||
# (build flags, libraries etc)
|
||||
- name: Set up platformio environment
|
||||
run: pio init --ide atom
|
||||
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
|
||||
# Also run git-diff-index so that the step is marked as failed on formatting errors,
|
||||
# since clang-format doesn't do anything but change files if -i is passed.
|
||||
- name: Run clang-format
|
||||
run: |
|
||||
script/clang-format -i
|
||||
git diff-index --quiet HEAD --
|
||||
if: ${{ matrix.id == 'clang-format' }}
|
||||
|
||||
- name: Run clang-tidy
|
||||
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
|
||||
- name: Suggest changes
|
||||
run: script/ci-suggest-changes
|
||||
if: ${{ matrix.id == 'clang-tidy' }}
|
||||
|
||||
lint-python:
|
||||
- name: Suggested changes
|
||||
run: script/ci-suggest-changes
|
||||
if: always()
|
||||
|
||||
ci:
|
||||
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
||||
# This way, all dependencies are cached via the cache action.
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- id: ci-custom
|
||||
name: Run script/ci-custom
|
||||
- id: lint-python
|
||||
name: Run script/lint-python
|
||||
- id: test
|
||||
file: tests/test1.yaml
|
||||
name: Test tests/test1.yaml
|
||||
- id: test
|
||||
file: tests/test2.yaml
|
||||
name: Test tests/test2.yaml
|
||||
- id: test
|
||||
file: tests/test3.yaml
|
||||
name: Test tests/test3.yaml
|
||||
- id: test
|
||||
file: tests/test4.yaml
|
||||
name: Test tests/test4.yaml
|
||||
- id: test
|
||||
file: tests/test5.yaml
|
||||
name: Test tests/test5.yaml
|
||||
- id: pytest
|
||||
name: Run pytest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
@@ -72,6 +103,17 @@ jobs:
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
|
||||
# Use per test platformio cache because tests have different platform versions
|
||||
- name: Cache ~/.platformio
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: test-home-platformio-${{ matrix.file }}-${{ hashFiles('esphome/core/config.py') }}
|
||||
restore-keys: |
|
||||
test-home-platformio-${{ matrix.file }}-
|
||||
if: ${{ matrix.id == 'test' }}
|
||||
|
||||
- name: Set up python environment
|
||||
run: script/setup
|
||||
|
||||
@@ -80,81 +122,22 @@ jobs:
|
||||
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
|
||||
- name: Lint Custom
|
||||
run: script/ci-custom.py
|
||||
run: |
|
||||
script/ci-custom.py
|
||||
script/build_codeowners.py --check
|
||||
if: ${{ matrix.id == 'ci-custom' }}
|
||||
- name: Lint Python
|
||||
run: script/lint-python
|
||||
- name: Lint CODEOWNERS
|
||||
run: script/build_codeowners.py --check
|
||||
if: ${{ matrix.id == 'lint-python' }}
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test:
|
||||
- test1
|
||||
- test2
|
||||
- test3
|
||||
- test4
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
# Use per test platformio cache because tests have different platform versions
|
||||
- name: Cache ~/.platformio
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core/config.py') }}
|
||||
restore-keys: |
|
||||
test-home-platformio-${{ matrix.test }}-
|
||||
- name: Set up environment
|
||||
run: script/setup
|
||||
- run: esphome compile ${{ matrix.file }}
|
||||
if: ${{ matrix.id == 'test' }}
|
||||
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- run: esphome tests/${{ matrix.test }}.yaml compile
|
||||
|
||||
pytest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
- name: Set up environment
|
||||
run: script/setup
|
||||
- name: Install Github Actions annotator
|
||||
run: pip install pytest-github-actions-annotate-failures
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- name: Run pytest
|
||||
run: |
|
||||
pytest \
|
||||
-qq \
|
||||
--durations=10 \
|
||||
-o console_output_style=count \
|
||||
tests
|
||||
pytest -vv --tb=native tests
|
||||
if: ${{ matrix.id == 'pytest' }}
|
||||
|
||||
104
.github/workflows/docker-lint-build.yml
vendored
104
.github/workflows/docker-lint-build.yml
vendored
@@ -7,30 +7,94 @@ on:
|
||||
paths:
|
||||
- 'docker/Dockerfile.lint'
|
||||
- 'requirements.txt'
|
||||
- 'requirements_optional.txt'
|
||||
- 'requirements_test.txt'
|
||||
- 'platformio.ini'
|
||||
- '.github/workflows/docker-lint-build.yml'
|
||||
|
||||
jobs:
|
||||
publish-docker-lint-iage:
|
||||
name: Build docker containers
|
||||
deploy-docker:
|
||||
name: Build and publish docker containers
|
||||
if: github.repository == 'esphome/esphome'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, armv7, aarch64]
|
||||
build_type: ["lint"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Pull for cache
|
||||
run: |
|
||||
docker pull "esphome/esphome-lint:latest" || true
|
||||
- name: Build
|
||||
run: |
|
||||
docker build \
|
||||
--cache-from "esphome/esphome-lint:latest" \
|
||||
--file "docker/Dockerfile.lint" \
|
||||
--tag "esphome/esphome-lint:latest" \
|
||||
.
|
||||
- name: Log in to docker hub
|
||||
env:
|
||||
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||
- run: |
|
||||
docker push "esphome/esphome-lint:latest"
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- name: Set TAG
|
||||
run: |
|
||||
echo "TAG=1.1" >> $GITHUB_ENV
|
||||
|
||||
- name: Run build
|
||||
run: |
|
||||
docker/build.py \
|
||||
--tag "${TAG}" \
|
||||
--arch "${{ matrix.arch }}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
build
|
||||
|
||||
- name: Log in to docker hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Log in to the GitHub container registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run push
|
||||
run: |
|
||||
docker/build.py \
|
||||
--tag "${TAG}" \
|
||||
--arch "${{ matrix.arch }}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
push
|
||||
|
||||
deploy-docker-manifest:
|
||||
if: github.repository == 'esphome/esphome'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy-docker]
|
||||
strategy:
|
||||
matrix:
|
||||
build_type: ["lint"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- name: Set TAG
|
||||
run: |
|
||||
echo "TAG=1.1" >> $GITHUB_ENV
|
||||
- name: Enable experimental manifest support
|
||||
run: |
|
||||
mkdir -p ~/.docker
|
||||
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
||||
|
||||
- name: Log in to docker hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Log in to the GitHub container registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run manifest
|
||||
run: |
|
||||
docker/build.py \
|
||||
--tag "${TAG}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
manifest
|
||||
|
||||
19
.github/workflows/matchers/pytest.json
vendored
Normal file
19
.github/workflows/matchers/pytest.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "pytest",
|
||||
"fileLocation": "absolute",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^\\s+File \"(.*)\", line (\\d+), in (.*)$",
|
||||
"file": 1,
|
||||
"line": 2
|
||||
},
|
||||
{
|
||||
"regexp": "^\\s+(.*)$",
|
||||
"message": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
246
.github/workflows/release-dev.yml
vendored
246
.github/workflows/release-dev.yml
vendored
@@ -1,246 +0,0 @@
|
||||
name: Publish dev releases to docker hub
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
# THE LINT/TEST JOBS ARE COPIED FROM ci.yaml
|
||||
|
||||
lint-clang-format:
|
||||
runs-on: ubuntu-latest
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: esphome/esphome-lint:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Set up the pio project so that the cpp checks know how files are compiled
|
||||
# (build flags, libraries etc)
|
||||
- name: Set up platformio environment
|
||||
run: pio init --ide atom
|
||||
|
||||
- name: Run clang-format
|
||||
run: script/clang-format -i
|
||||
- name: Suggest changes
|
||||
run: script/ci-suggest-changes
|
||||
|
||||
lint-clang-tidy:
|
||||
runs-on: ubuntu-latest
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: esphome/esphome-lint:latest
|
||||
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
split: [1, 2, 3, 4]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Set up the pio project so that the cpp checks know how files are compiled
|
||||
# (build flags, libraries etc)
|
||||
- name: Set up platformio environment
|
||||
run: pio init --ide atom
|
||||
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
- name: Run clang-tidy
|
||||
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
|
||||
- name: Suggest changes
|
||||
run: script/ci-suggest-changes
|
||||
|
||||
lint-python:
|
||||
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
||||
# This way, all dependencies are cached via the cache action.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
- name: Set up python environment
|
||||
run: script/setup
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- name: Lint Custom
|
||||
run: script/ci-custom.py
|
||||
- name: Lint Python
|
||||
run: script/lint-python
|
||||
- name: Lint CODEOWNERS
|
||||
run: script/build_codeowners.py --check
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test:
|
||||
- test1
|
||||
- test2
|
||||
- test3
|
||||
- test4
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
# Use per test platformio cache because tests have different platform versions
|
||||
- name: Cache ~/.platformio
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core/config.py') }}
|
||||
restore-keys: |
|
||||
test-home-platformio-${{ matrix.test }}-
|
||||
- name: Set up environment
|
||||
run: script/setup
|
||||
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- run: esphome tests/${{ matrix.test }}.yaml compile
|
||||
|
||||
pytest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
- name: Set up environment
|
||||
run: script/setup
|
||||
- name: Install Github Actions annotator
|
||||
run: pip install pytest-github-actions-annotate-failures
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- name: Run pytest
|
||||
run: |
|
||||
pytest \
|
||||
-qq \
|
||||
--durations=10 \
|
||||
-o console_output_style=count \
|
||||
tests
|
||||
|
||||
deploy-docker:
|
||||
name: Build and publish docker containers
|
||||
if: github.repository == 'esphome/esphome'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, armv7, aarch64]
|
||||
# Hassio dev image doesn't use esphome/esphome-hassio-$arch and uses base directly
|
||||
build_type: ["docker"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set TAG
|
||||
run: |
|
||||
TAG="${GITHUB_SHA:0:7}"
|
||||
echo "TAG=${TAG}" >> $GITHUB_ENV
|
||||
- name: Set up env variables
|
||||
run: |
|
||||
base_version="3.0.0"
|
||||
|
||||
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
||||
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
||||
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
|
||||
dockerfile="docker/Dockerfile.hassio"
|
||||
else
|
||||
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
|
||||
build_to="esphome/esphome-${{ matrix.arch }}"
|
||||
dockerfile="docker/Dockerfile"
|
||||
fi
|
||||
|
||||
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
|
||||
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
|
||||
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
|
||||
- name: Pull for cache
|
||||
run: |
|
||||
docker pull "${BUILD_TO}:dev" || true
|
||||
- name: Register QEMU binfmt
|
||||
run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
|
||||
- run: |
|
||||
docker build \
|
||||
--build-arg "BUILD_FROM=${BUILD_FROM}" \
|
||||
--build-arg "BUILD_VERSION=${TAG}" \
|
||||
--tag "${BUILD_TO}:${TAG}" \
|
||||
--tag "${BUILD_TO}:dev" \
|
||||
--cache-from "${BUILD_TO}:dev" \
|
||||
--file "${DOCKERFILE}" \
|
||||
.
|
||||
- name: Log in to docker hub
|
||||
env:
|
||||
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||
- run: |
|
||||
docker push "${BUILD_TO}:${TAG}"
|
||||
docker push "${BUILD_TO}:dev"
|
||||
|
||||
|
||||
deploy-docker-manifest:
|
||||
if: github.repository == 'esphome/esphome'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy-docker]
|
||||
steps:
|
||||
- name: Enable experimental manifest support
|
||||
run: |
|
||||
mkdir -p ~/.docker
|
||||
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
||||
- name: Set TAG
|
||||
run: |
|
||||
TAG="${GITHUB_SHA:0:7}"
|
||||
echo "TAG=${TAG}" >> $GITHUB_ENV
|
||||
- name: Log in to docker hub
|
||||
env:
|
||||
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||
- name: "Create the manifest"
|
||||
run: |
|
||||
docker manifest create esphome/esphome:${TAG} \
|
||||
esphome/esphome-aarch64:${TAG} \
|
||||
esphome/esphome-amd64:${TAG} \
|
||||
esphome/esphome-armv7:${TAG}
|
||||
docker manifest push esphome/esphome:${TAG}
|
||||
|
||||
docker manifest create esphome/esphome:dev \
|
||||
esphome/esphome-aarch64:${TAG} \
|
||||
esphome/esphome-amd64:${TAG} \
|
||||
esphome/esphome-armv7:${TAG}
|
||||
docker manifest push esphome/esphome:dev
|
||||
318
.github/workflows/release.yml
vendored
318
.github/workflows/release.yml
vendored
@@ -1,163 +1,35 @@
|
||||
name: Publish Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
schedule:
|
||||
- cron: "0 2 * * *"
|
||||
|
||||
jobs:
|
||||
# THE LINT/TEST JOBS ARE COPIED FROM ci.yaml
|
||||
|
||||
lint-clang-format:
|
||||
init:
|
||||
name: Initialize build
|
||||
runs-on: ubuntu-latest
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: esphome/esphome-lint:latest
|
||||
outputs:
|
||||
tag: ${{ steps.tag.outputs.tag }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Set up the pio project so that the cpp checks know how files are compiled
|
||||
# (build flags, libraries etc)
|
||||
- name: Set up platformio environment
|
||||
run: pio init --ide atom
|
||||
|
||||
- name: Run clang-format
|
||||
run: script/clang-format -i
|
||||
- name: Suggest changes
|
||||
run: script/ci-suggest-changes
|
||||
|
||||
lint-clang-tidy:
|
||||
runs-on: ubuntu-latest
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: esphome/esphome-lint:latest
|
||||
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
split: [1, 2, 3, 4]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Set up the pio project so that the cpp checks know how files are compiled
|
||||
# (build flags, libraries etc)
|
||||
- name: Set up platformio environment
|
||||
run: pio init --ide atom
|
||||
|
||||
|
||||
- name: Register problem matchers
|
||||
- name: Get tag
|
||||
id: tag
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
- name: Run clang-tidy
|
||||
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
|
||||
- name: Suggest changes
|
||||
run: script/ci-suggest-changes
|
||||
|
||||
lint-python:
|
||||
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
||||
# This way, all dependencies are cached via the cache action.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
- name: Set up python environment
|
||||
run: script/setup
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- name: Lint Custom
|
||||
run: script/ci-custom.py
|
||||
- name: Lint Python
|
||||
run: script/lint-python
|
||||
- name: Lint CODEOWNERS
|
||||
run: script/build_codeowners.py --check
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test:
|
||||
- test1
|
||||
- test2
|
||||
- test3
|
||||
- test4
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
# Use per test platformio cache because tests have different platform versions
|
||||
- name: Cache ~/.platformio
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core/config.py') }}
|
||||
restore-keys: |
|
||||
test-home-platformio-${{ matrix.test }}-
|
||||
- name: Set up environment
|
||||
run: script/setup
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- run: esphome tests/${{ matrix.test }}.yaml compile
|
||||
|
||||
pytest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
- name: Set up environment
|
||||
run: script/setup
|
||||
- name: Install Github Actions annotator
|
||||
run: pip install pytest-github-actions-annotate-failures
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- name: Run pytest
|
||||
run: |
|
||||
pytest \
|
||||
-qq \
|
||||
--durations=10 \
|
||||
-o console_output_style=count \
|
||||
tests
|
||||
if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
else
|
||||
TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p")
|
||||
today="$(date --utc '+%Y%m%d')"
|
||||
TAG="${TAG}${today}"
|
||||
fi
|
||||
echo "::set-output name=tag::${TAG}"
|
||||
|
||||
deploy-pypi:
|
||||
name: Build and publish to PyPi
|
||||
if: github.repository == 'esphome/esphome'
|
||||
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
|
||||
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -181,129 +53,95 @@ jobs:
|
||||
name: Build and publish docker containers
|
||||
if: github.repository == 'esphome/esphome'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
|
||||
needs: [init]
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, armv7, aarch64]
|
||||
build_type: ["hassio", "docker"]
|
||||
build_type: ["ha-addon", "docker"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set TAG
|
||||
run: |
|
||||
TAG="${GITHUB_REF#refs/tags/v}"
|
||||
echo "TAG=${TAG}" >> $GITHUB_ENV
|
||||
- name: Set up env variables
|
||||
run: |
|
||||
base_version="3.0.0"
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
|
||||
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
||||
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
||||
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
|
||||
dockerfile="docker/Dockerfile.hassio"
|
||||
else
|
||||
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
|
||||
build_to="esphome/esphome-${{ matrix.arch }}"
|
||||
dockerfile="docker/Dockerfile"
|
||||
fi
|
||||
- name: Run build
|
||||
run: |
|
||||
docker/build.py \
|
||||
--tag "${{ needs.init.outputs.tag }}" \
|
||||
--arch "${{ matrix.arch }}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
build
|
||||
|
||||
if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then
|
||||
cache_tag="beta"
|
||||
else
|
||||
cache_tag="latest"
|
||||
fi
|
||||
- name: Log in to docker hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Log in to the GitHub container registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Set env variables so these values don't need to be calculated again
|
||||
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
|
||||
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
|
||||
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
|
||||
echo "CACHE_TAG=${cache_tag}" >> $GITHUB_ENV
|
||||
- name: Pull for cache
|
||||
run: |
|
||||
docker pull "${BUILD_TO}:${CACHE_TAG}" || true
|
||||
- name: Register QEMU binfmt
|
||||
run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
|
||||
- run: |
|
||||
docker build \
|
||||
--build-arg "BUILD_FROM=${BUILD_FROM}" \
|
||||
--build-arg "BUILD_VERSION=${TAG}" \
|
||||
--tag "${BUILD_TO}:${TAG}" \
|
||||
--cache-from "${BUILD_TO}:${CACHE_TAG}" \
|
||||
--file "${DOCKERFILE}" \
|
||||
.
|
||||
- name: Log in to docker hub
|
||||
env:
|
||||
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||
- run: docker push "${BUILD_TO}:${TAG}"
|
||||
|
||||
# Always publish to beta tag (also full releases)
|
||||
- name: Publish docker beta tag
|
||||
run: |
|
||||
docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:beta"
|
||||
docker push "${BUILD_TO}:beta"
|
||||
|
||||
- if: ${{ !github.event.release.prerelease }}
|
||||
name: Publish docker latest tag
|
||||
run: |
|
||||
docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:latest"
|
||||
docker push "${BUILD_TO}:latest"
|
||||
- name: Run push
|
||||
run: |
|
||||
docker/build.py \
|
||||
--tag "${{ needs.init.outputs.tag }}" \
|
||||
--arch "${{ matrix.arch }}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
push
|
||||
|
||||
deploy-docker-manifest:
|
||||
if: github.repository == 'esphome/esphome'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy-docker]
|
||||
needs: [init, deploy-docker]
|
||||
strategy:
|
||||
matrix:
|
||||
build_type: ["ha-addon", "docker"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- name: Enable experimental manifest support
|
||||
run: |
|
||||
mkdir -p ~/.docker
|
||||
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
||||
- name: Set TAG
|
||||
run: |
|
||||
TAG="${GITHUB_REF#refs/tags/v}"
|
||||
echo "TAG=${TAG}" >> $GITHUB_ENV
|
||||
|
||||
- name: Log in to docker hub
|
||||
env:
|
||||
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||
- name: "Create the manifest"
|
||||
run: |
|
||||
docker manifest create esphome/esphome:${TAG} \
|
||||
esphome/esphome-aarch64:${TAG} \
|
||||
esphome/esphome-amd64:${TAG} \
|
||||
esphome/esphome-armv7:${TAG}
|
||||
docker manifest push esphome/esphome:${TAG}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Log in to the GitHub container registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Publish docker beta tag
|
||||
- name: Run manifest
|
||||
run: |
|
||||
docker manifest create esphome/esphome:beta \
|
||||
esphome/esphome-aarch64:${TAG} \
|
||||
esphome/esphome-amd64:${TAG} \
|
||||
esphome/esphome-armv7:${TAG}
|
||||
docker manifest push esphome/esphome:beta
|
||||
|
||||
- name: Publish docker latest tag
|
||||
if: ${{ !github.event.release.prerelease }}
|
||||
run: |
|
||||
docker manifest create esphome/esphome:latest \
|
||||
esphome/esphome-aarch64:${TAG} \
|
||||
esphome/esphome-amd64:${TAG} \
|
||||
esphome/esphome-armv7:${TAG}
|
||||
docker manifest push esphome/esphome:latest
|
||||
docker/build.py \
|
||||
--tag "${{ needs.init.outputs.tag }}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
manifest
|
||||
|
||||
deploy-hassio-repo:
|
||||
if: github.repository == 'esphome/esphome'
|
||||
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy-docker]
|
||||
steps:
|
||||
- env:
|
||||
TOKEN: ${{ secrets.DEPLOY_HASSIO_TOKEN }}
|
||||
run: |
|
||||
TAG="${GITHUB_REF#refs/tags/v}"
|
||||
TAG="${GITHUB_REF#refs/tags/}"
|
||||
curl \
|
||||
-u ":$TOKEN" \
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \
|
||||
-d "{\"ref\":\"master\",\"inputs\":{\"version\":\"$TAG\"}}"
|
||||
-d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -13,6 +13,9 @@ __pycache__/
|
||||
# Intellij Idea
|
||||
.idea
|
||||
|
||||
# Vim
|
||||
*.swp
|
||||
|
||||
# Hide some OS X stuff
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
@@ -100,6 +103,8 @@ CMakeLists.txt
|
||||
|
||||
# CMake
|
||||
cmake-build-debug/
|
||||
cmake-build-livingroom8266/
|
||||
cmake-build-livingroom32/
|
||||
cmake-build-release/
|
||||
|
||||
CMakeCache.txt
|
||||
@@ -120,4 +125,5 @@ config/
|
||||
tests/build/
|
||||
tests/.esphome/
|
||||
/.temp-clang-tidy.cpp
|
||||
/.temp/
|
||||
.pio/
|
||||
|
||||
@@ -23,5 +23,5 @@ repos:
|
||||
- id: no-commit-to-branch
|
||||
args:
|
||||
- --branch=dev
|
||||
- --branch=master
|
||||
- --branch=release
|
||||
- --branch=beta
|
||||
|
||||
35
.vscode/tasks.json
vendored
35
.vscode/tasks.json
vendored
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "run",
|
||||
"type": "shell",
|
||||
"command": "python3 -m esphome dashboard config/",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "clang-tidy",
|
||||
"type": "shell",
|
||||
"command": "./script/clang-tidy",
|
||||
"problemMatcher": [
|
||||
{
|
||||
"label": "run",
|
||||
"type": "shell",
|
||||
"command": "python3 -m esphome config dashboard",
|
||||
"problemMatcher": []
|
||||
"owner": "clang-tidy",
|
||||
"fileLocation": "absolute",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(.*):(\\d+):(\\d+):\\s+(error):\\s+(.*) \\[([a-z0-9,\\-]+)\\]\\s*$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"severity": 4,
|
||||
"message": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
35
CODEOWNERS
35
CODEOWNERS
@@ -14,11 +14,15 @@ esphome/core/* @esphome/core
|
||||
esphome/components/ac_dimmer/* @glmnet
|
||||
esphome/components/adc/* @esphome/core
|
||||
esphome/components/addressable_light/* @justfalter
|
||||
esphome/components/am43/* @buxtronix
|
||||
esphome/components/am43/cover/* @buxtronix
|
||||
esphome/components/animation/* @syndlex
|
||||
esphome/components/anova/* @buxtronix
|
||||
esphome/components/api/* @OttoWinter
|
||||
esphome/components/async_tcp/* @OttoWinter
|
||||
esphome/components/atc_mithermometer/* @ahpohl
|
||||
esphome/components/b_parasite/* @rbaron
|
||||
esphome/components/ballu/* @bazuchan
|
||||
esphome/components/bang_bang/* @OttoWinter
|
||||
esphome/components/binary_sensor/* @esphome/core
|
||||
esphome/components/ble_client/* @buxtronix
|
||||
@@ -27,13 +31,19 @@ esphome/components/canbus/* @danielschramm @mvturnho
|
||||
esphome/components/captive_portal/* @OttoWinter
|
||||
esphome/components/climate/* @esphome/core
|
||||
esphome/components/climate_ir/* @glmnet
|
||||
esphome/components/color_temperature/* @jesserockz
|
||||
esphome/components/coolix/* @glmnet
|
||||
esphome/components/cover/* @esphome/core
|
||||
esphome/components/cs5460a/* @balrog-kun
|
||||
esphome/components/ct_clamp/* @jesserockz
|
||||
esphome/components/debug/* @OttoWinter
|
||||
esphome/components/dfplayer/* @glmnet
|
||||
esphome/components/dht/* @OttoWinter
|
||||
esphome/components/ds1307/* @badbadc0ffee
|
||||
esphome/components/dsmr/* @glmnet @zuidwijk
|
||||
esphome/components/esp32_ble/* @jesserockz
|
||||
esphome/components/esp32_ble_server/* @jesserockz
|
||||
esphome/components/esp32_improv/* @jesserockz
|
||||
esphome/components/exposure_notifications/* @OttoWinter
|
||||
esphome/components/ezo/* @ssieb
|
||||
esphome/components/fastled_base/* @OttoWinter
|
||||
@@ -41,8 +51,12 @@ esphome/components/fingerprint_grow/* @OnFreund @loongyh
|
||||
esphome/components/globals/* @esphome/core
|
||||
esphome/components/gpio/* @esphome/core
|
||||
esphome/components/gps/* @coogle
|
||||
esphome/components/havells_solar/* @sourabhjaiswal
|
||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||
esphome/components/homeassistant/* @OttoWinter
|
||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
||||
esphome/components/i2c/* @esphome/core
|
||||
esphome/components/improv/* @jesserockz
|
||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
||||
esphome/components/inkplate6/* @jesserockz
|
||||
esphome/components/integration/* @OttoWinter
|
||||
@@ -63,23 +77,38 @@ esphome/components/mcp2515/* @danielschramm @mvturnho
|
||||
esphome/components/mcp9808/* @k7hpn
|
||||
esphome/components/midea_ac/* @dudanov
|
||||
esphome/components/midea_dongle/* @dudanov
|
||||
esphome/components/mitsubishi/* @RubyBailey
|
||||
esphome/components/network/* @esphome/core
|
||||
esphome/components/nextion/* @senexcrenshaw
|
||||
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
||||
esphome/components/nextion/sensor/* @senexcrenshaw
|
||||
esphome/components/nextion/switch/* @senexcrenshaw
|
||||
esphome/components/nextion/text_sensor/* @senexcrenshaw
|
||||
esphome/components/nfc/* @jesserockz
|
||||
esphome/components/number/* @esphome/core
|
||||
esphome/components/ota/* @esphome/core
|
||||
esphome/components/output/* @esphome/core
|
||||
esphome/components/pid/* @OttoWinter
|
||||
esphome/components/pipsolar/* @andreashergert1984
|
||||
esphome/components/pmsa003i/* @sjtrny
|
||||
esphome/components/pn532/* @OttoWinter @jesserockz
|
||||
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
||||
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
||||
esphome/components/power_supply/* @esphome/core
|
||||
esphome/components/pulse_meter/* @stevebaxter
|
||||
esphome/components/pvvx_mithermometer/* @pasiz
|
||||
esphome/components/rc522/* @glmnet
|
||||
esphome/components/rc522_i2c/* @glmnet
|
||||
esphome/components/rc522_spi/* @glmnet
|
||||
esphome/components/restart/* @esphome/core
|
||||
esphome/components/rf_bridge/* @jesserockz
|
||||
esphome/components/rgbct/* @jesserockz
|
||||
esphome/components/rtttl/* @glmnet
|
||||
esphome/components/script/* @esphome/core
|
||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
||||
esphome/components/sdp3x/* @Azimath
|
||||
esphome/components/selec_meter/* @sourabhjaiswal
|
||||
esphome/components/select/* @esphome/core
|
||||
esphome/components/sensor/* @esphome/core
|
||||
esphome/components/sgp40/* @SenexCrenshaw
|
||||
esphome/components/sht4x/* @sjtrny
|
||||
@@ -103,14 +132,19 @@ esphome/components/st7789v/* @kbx81
|
||||
esphome/components/substitutions/* @esphome/core
|
||||
esphome/components/sun/* @OttoWinter
|
||||
esphome/components/switch/* @esphome/core
|
||||
esphome/components/t6615/* @tylermenezes
|
||||
esphome/components/tca9548a/* @andreashergert1984
|
||||
esphome/components/tcl112/* @glmnet
|
||||
esphome/components/teleinfo/* @0hax
|
||||
esphome/components/thermostat/* @kbx81
|
||||
esphome/components/time/* @OttoWinter
|
||||
esphome/components/tlc5947/* @rnauber
|
||||
esphome/components/tm1637/* @glmnet
|
||||
esphome/components/tmp102/* @timsavage
|
||||
esphome/components/tmp117/* @Azimath
|
||||
esphome/components/tof10120/* @wstrzalka
|
||||
esphome/components/toshiba/* @kbx81
|
||||
esphome/components/tsl2591/* @wjcarpenter
|
||||
esphome/components/tuya/binary_sensor/* @jesserockz
|
||||
esphome/components/tuya/climate/* @jesserockz
|
||||
esphome/components/tuya/sensor/* @jesserockz
|
||||
@@ -122,3 +156,4 @@ esphome/components/web_server_base/* @OttoWinter
|
||||
esphome/components/whirlpool/* @glmnet
|
||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
||||
esphome/components/xpt2046/* @numo68
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ESPHome [](https://travis-ci.org/esphome/esphome) [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
|
||||
# ESPHome [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
|
||||
|
||||
[](https://esphome.io/)
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
ARG BUILD_FROM=esphome/esphome-base-amd64:3.0.0
|
||||
ARG BUILD_FROM=esphome/esphome-base:latest
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
# First install requirements to leverage caching when requirements don't change
|
||||
COPY requirements.txt docker/platformio_install_deps.py platformio.ini /
|
||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
||||
RUN \
|
||||
pip3 install --no-cache-dir -r /requirements.txt \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
||||
&& /platformio_install_deps.py /platformio.ini
|
||||
|
||||
# Then copy esphome and install
|
||||
@@ -27,4 +27,4 @@ WORKDIR /config
|
||||
# in every docker command twice
|
||||
ENTRYPOINT ["esphome"]
|
||||
# When no arguments given, start the dashboard in the workdir
|
||||
CMD ["/config", "dashboard"]
|
||||
CMD ["dashboard", "/config"]
|
||||
|
||||
@@ -1,13 +1 @@
|
||||
FROM esphome/esphome-base-amd64:3.0.0
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
python3-wheel \
|
||||
net-tools \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /workspaces
|
||||
ENV SHELL /bin/bash
|
||||
FROM esphome/esphome-lint:1.1
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
ARG BUILD_FROM
|
||||
ARG BUILD_FROM=esphome/esphome-hassio-base:latest
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
# First install requirements to leverage caching when requirements don't change
|
||||
COPY requirements.txt docker/platformio_install_deps.py platformio.ini /
|
||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
||||
RUN \
|
||||
pip3 install --no-cache-dir -r /requirements.txt \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
||||
&& /platformio_install_deps.py /platformio.ini
|
||||
|
||||
# Copy root filesystem
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
FROM esphome/esphome-lint-base:3.0.0
|
||||
ARG BUILD_FROM=esphome/esphome-lint-base:latest
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
COPY requirements.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
|
||||
COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
|
||||
RUN \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
|
||||
&& /platformio_install_deps.py /platformio.ini
|
||||
|
||||
VOLUME ["/esphome"]
|
||||
|
||||
177
docker/build.py
Executable file
177
docker/build.py
Executable file
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
from dataclasses import dataclass
|
||||
import subprocess
|
||||
import argparse
|
||||
import platform
|
||||
import shlex
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
CHANNEL_DEV = 'dev'
|
||||
CHANNEL_BETA = 'beta'
|
||||
CHANNEL_RELEASE = 'release'
|
||||
CHANNELS = [CHANNEL_DEV, CHANNEL_BETA, CHANNEL_RELEASE]
|
||||
|
||||
ARCH_AMD64 = 'amd64'
|
||||
ARCH_ARMV7 = 'armv7'
|
||||
ARCH_AARCH64 = 'aarch64'
|
||||
ARCHS = [ARCH_AMD64, ARCH_ARMV7, ARCH_AARCH64]
|
||||
|
||||
TYPE_DOCKER = 'docker'
|
||||
TYPE_HA_ADDON = 'ha-addon'
|
||||
TYPE_LINT = 'lint'
|
||||
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
|
||||
|
||||
|
||||
BASE_VERSION = "3.6.0"
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--tag", type=str, required=True, help="The main docker tag to push to. If a version number also adds latest and/or beta tag")
|
||||
parser.add_argument("--arch", choices=ARCHS, required=False, help="The architecture to build for")
|
||||
parser.add_argument("--build-type", choices=TYPES, required=True, help="The type of build to run")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Don't run any commands, just print them")
|
||||
subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True)
|
||||
build_parser = subparsers.add_parser("build", help="Build the image")
|
||||
push_parser = subparsers.add_parser("push", help="Tag the already built image and push it to docker hub")
|
||||
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
|
||||
|
||||
|
||||
|
||||
# only lists some possibilities, doesn't have to be perfect
|
||||
# https://stackoverflow.com/a/45125525
|
||||
UNAME_TO_ARCH = {
|
||||
"x86_64": ARCH_AMD64,
|
||||
"aarch64": ARCH_AARCH64,
|
||||
"aarch64_be": ARCH_AARCH64,
|
||||
"arm": ARCH_ARMV7,
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DockerParams:
|
||||
build_from: str
|
||||
build_to: str
|
||||
manifest_to: str
|
||||
dockerfile: str
|
||||
|
||||
@classmethod
|
||||
def for_type_arch(cls, build_type, arch):
|
||||
prefix = {
|
||||
TYPE_DOCKER: "esphome/esphome",
|
||||
TYPE_HA_ADDON: "esphome/esphome-hassio",
|
||||
TYPE_LINT: "esphome/esphome-lint"
|
||||
}[build_type]
|
||||
build_from = f"ghcr.io/{prefix}-base-{arch}:{BASE_VERSION}"
|
||||
build_to = f"{prefix}-{arch}"
|
||||
dockerfile = {
|
||||
TYPE_DOCKER: "docker/Dockerfile",
|
||||
TYPE_HA_ADDON: "docker/Dockerfile.hassio",
|
||||
TYPE_LINT: "docker/Dockerfile.lint",
|
||||
}[build_type]
|
||||
return cls(
|
||||
build_from=build_from,
|
||||
build_to=build_to,
|
||||
manifest_to=prefix,
|
||||
dockerfile=dockerfile
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
args = parser.parse_args()
|
||||
|
||||
def run_command(*cmd, ignore_error: bool = False):
|
||||
print(f"$ {shlex.join(list(cmd))}")
|
||||
if not args.dry_run:
|
||||
rc = subprocess.call(list(cmd))
|
||||
if rc != 0 and not ignore_error:
|
||||
print("Command failed")
|
||||
sys.exit(1)
|
||||
|
||||
# detect channel from tag
|
||||
match = re.match(r'^\d+\.\d+(?:\.\d+)?(b\d+)?$', args.tag)
|
||||
if match is None:
|
||||
channel = CHANNEL_DEV
|
||||
elif match.group(1) is None:
|
||||
channel = CHANNEL_RELEASE
|
||||
else:
|
||||
channel = CHANNEL_BETA
|
||||
|
||||
tags_to_push = [args.tag]
|
||||
if channel == CHANNEL_DEV:
|
||||
tags_to_push.append("dev")
|
||||
elif channel == CHANNEL_BETA:
|
||||
tags_to_push.append("beta")
|
||||
elif channel == CHANNEL_RELEASE:
|
||||
# Additionally push to beta
|
||||
tags_to_push.append("beta")
|
||||
tags_to_push.append("latest")
|
||||
|
||||
if args.command == "build":
|
||||
# 1. pull cache image
|
||||
params = DockerParams.for_type_arch(args.build_type, args.arch)
|
||||
cache_tag = {
|
||||
CHANNEL_DEV: "dev",
|
||||
CHANNEL_BETA: "beta",
|
||||
CHANNEL_RELEASE: "latest",
|
||||
}[channel]
|
||||
cache_img = f"ghcr.io/{params.build_to}:{cache_tag}"
|
||||
run_command("docker", "pull", cache_img, ignore_error=True)
|
||||
|
||||
# 2. register QEMU binfmt (if not host arch)
|
||||
is_native = UNAME_TO_ARCH.get(platform.machine()) == args.arch
|
||||
if not is_native:
|
||||
run_command(
|
||||
"docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static:5.2.0-2",
|
||||
"--reset", "-p", "yes"
|
||||
)
|
||||
|
||||
# 3. build
|
||||
run_command(
|
||||
"docker", "build",
|
||||
"--build-arg", f"BUILD_FROM={params.build_from}",
|
||||
"--build-arg", f"BUILD_VERSION={args.tag}",
|
||||
"--tag", f"{params.build_to}:{args.tag}",
|
||||
"--cache-from", cache_img,
|
||||
"--file", params.dockerfile,
|
||||
"."
|
||||
)
|
||||
elif args.command == "push":
|
||||
params = DockerParams.for_type_arch(args.build_type, args.arch)
|
||||
imgs = [f"{params.build_to}:{tag}" for tag in tags_to_push]
|
||||
imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push]
|
||||
src = imgs[0]
|
||||
# 1. tag images
|
||||
for img in imgs[1:]:
|
||||
run_command(
|
||||
"docker", "tag", src, img
|
||||
)
|
||||
# 2. push images
|
||||
for img in imgs:
|
||||
run_command(
|
||||
"docker", "push", img
|
||||
)
|
||||
elif args.command == "manifest":
|
||||
manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to
|
||||
|
||||
targets = [f"{manifest}:{tag}" for tag in tags_to_push]
|
||||
targets += [f"ghcr.io/{manifest}:{tag}" for tag in tags_to_push]
|
||||
# 1. Create manifests
|
||||
for target in targets:
|
||||
cmd = ["docker", "manifest", "create", target]
|
||||
for arch in ARCHS:
|
||||
src = f"{DockerParams.for_type_arch(args.build_type, arch).build_to}:{args.tag}"
|
||||
if target.startswith("ghcr.io"):
|
||||
src = f"ghcr.io/{src}"
|
||||
cmd.append(src)
|
||||
run_command(*cmd)
|
||||
# 2. Push manifests
|
||||
for target in targets:
|
||||
run_command(
|
||||
"docker", "manifest", "push", target
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# ==============================================================================
|
||||
# Community Hass.io Add-ons: ESPHome
|
||||
# This files installs the user ESPHome version if specified
|
||||
# ==============================================================================
|
||||
|
||||
declare esphome_version
|
||||
|
||||
if bashio::config.has_value 'esphome_version'; then
|
||||
esphome_version=$(bashio::config 'esphome_version')
|
||||
if [[ $esphome_version == *":"* ]]; then
|
||||
IFS=':' read -r -a array <<< "$esphome_version"
|
||||
username=${array[0]}
|
||||
ref=${array[1]}
|
||||
else
|
||||
username="esphome"
|
||||
ref=$esphome_version
|
||||
fi
|
||||
full_url="https://github.com/${username}/esphome/archive/${ref}.zip"
|
||||
bashio::log.info "Installing esphome version '${esphome_version}' (${full_url})..."
|
||||
pip3 install -U --no-cache-dir "${full_url}" \
|
||||
|| bashio::exit.nok "Failed installing esphome pinned version."
|
||||
fi
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# ==============================================================================
|
||||
# Community Hass.io Add-ons: ESPHome
|
||||
# This files migrates the esphome config directory from the old path
|
||||
# ==============================================================================
|
||||
|
||||
if [[ ! -d /config/esphome && -d /config/esphomeyaml ]]; then
|
||||
echo "Moving config directory from /config/esphomeyaml to /config/esphome"
|
||||
mv /config/esphomeyaml /config/esphome
|
||||
mv /config/esphome/.esphomeyaml /config/esphome/.esphome
|
||||
fi
|
||||
@@ -23,4 +23,4 @@ if bashio::config.has_value 'relative_url'; then
|
||||
fi
|
||||
|
||||
bashio::log.info "Starting ESPHome dashboard..."
|
||||
exec esphome /config/esphome dashboard --socket /var/run/esphome.sock --hassio
|
||||
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio
|
||||
|
||||
@@ -11,6 +11,7 @@ from esphome.config import iter_components, read_config, strip_default_ids
|
||||
from esphome.const import (
|
||||
CONF_BAUD_RATE,
|
||||
CONF_BROKER,
|
||||
CONF_DEASSERT_RTS_DTR,
|
||||
CONF_LOGGER,
|
||||
CONF_OTA,
|
||||
CONF_PASSWORD,
|
||||
@@ -18,7 +19,7 @@ from esphome.const import (
|
||||
CONF_ESPHOME,
|
||||
CONF_PLATFORMIO_OPTIONS,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
|
||||
from esphome.core import CORE, EsphomeError, coroutine
|
||||
from esphome.helpers import indent
|
||||
from esphome.util import (
|
||||
run_external_command,
|
||||
@@ -99,10 +100,21 @@ def run_miniterm(config, port):
|
||||
baud_rate = config["logger"][CONF_BAUD_RATE]
|
||||
if baud_rate == 0:
|
||||
_LOGGER.info("UART logging is disabled (baud_rate=0). Not starting UART logs.")
|
||||
return
|
||||
_LOGGER.info("Starting log output from %s with baud rate %s", port, baud_rate)
|
||||
|
||||
backtrace_state = False
|
||||
with serial.Serial(port, baudrate=baud_rate) as ser:
|
||||
ser = serial.Serial()
|
||||
ser.baudrate = baud_rate
|
||||
ser.port = port
|
||||
|
||||
# We can't set to False by default since it leads to toggling and hence
|
||||
# ESP32 resets on some platforms.
|
||||
if config["logger"][CONF_DEASSERT_RTS_DTR]:
|
||||
ser.dtr = False
|
||||
ser.rts = False
|
||||
|
||||
with ser:
|
||||
while True:
|
||||
try:
|
||||
raw = ser.readline()
|
||||
@@ -127,15 +139,16 @@ def wrap_to_code(name, comp):
|
||||
coro = coroutine(comp.to_code)
|
||||
|
||||
@functools.wraps(comp.to_code)
|
||||
@coroutine_with_priority(coro.priority)
|
||||
def wrapped(conf):
|
||||
async def wrapped(conf):
|
||||
cg.add(cg.LineComment(f"{name}:"))
|
||||
if comp.config_schema is not None:
|
||||
conf_str = yaml_util.dump(conf)
|
||||
conf_str = conf_str.replace("//", "")
|
||||
cg.add(cg.LineComment(indent(conf_str)))
|
||||
yield coro(conf)
|
||||
await coro(conf)
|
||||
|
||||
if hasattr(coro, "priority"):
|
||||
wrapped.priority = coro.priority
|
||||
return wrapped
|
||||
|
||||
|
||||
@@ -267,7 +280,7 @@ def clean_mqtt(config, args):
|
||||
def command_wizard(args):
|
||||
from esphome import wizard
|
||||
|
||||
return wizard.wizard(args.configuration[0])
|
||||
return wizard.wizard(args.configuration)
|
||||
|
||||
|
||||
def command_config(args, config):
|
||||
@@ -283,7 +296,6 @@ def command_vscode(args):
|
||||
|
||||
logging.disable(logging.INFO)
|
||||
logging.disable(logging.WARNING)
|
||||
CORE.config_path = args.configuration[0]
|
||||
vscode.read_config(args)
|
||||
|
||||
|
||||
@@ -303,7 +315,7 @@ def command_compile(args, config):
|
||||
|
||||
def command_upload(args, config):
|
||||
port = choose_upload_log_host(
|
||||
default=args.upload_port,
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
show_ota=True,
|
||||
show_mqtt=False,
|
||||
@@ -318,7 +330,7 @@ def command_upload(args, config):
|
||||
|
||||
def command_logs(args, config):
|
||||
port = choose_upload_log_host(
|
||||
default=args.serial_port,
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
show_ota=False,
|
||||
show_mqtt=True,
|
||||
@@ -336,7 +348,7 @@ def command_run(args, config):
|
||||
return exit_code
|
||||
_LOGGER.info("Successfully compiled program.")
|
||||
port = choose_upload_log_host(
|
||||
default=args.upload_port,
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
show_ota=True,
|
||||
show_mqtt=False,
|
||||
@@ -349,7 +361,7 @@ def command_run(args, config):
|
||||
if args.no_logs:
|
||||
return 0
|
||||
port = choose_upload_log_host(
|
||||
default=args.upload_port,
|
||||
default=args.device,
|
||||
check_default=port,
|
||||
show_ota=False,
|
||||
show_mqtt=True,
|
||||
@@ -393,7 +405,7 @@ def command_update_all(args):
|
||||
import click
|
||||
|
||||
success = {}
|
||||
files = list_yaml_files(args.configuration[0])
|
||||
files = list_yaml_files(args.configuration)
|
||||
twidth = 60
|
||||
|
||||
def print_bar(middle_text):
|
||||
@@ -407,7 +419,7 @@ def command_update_all(args):
|
||||
print("-" * twidth)
|
||||
print()
|
||||
rc = run_external_process(
|
||||
"esphome", "--dashboard", f, "run", "--no-logs", "--upload-port", "OTA"
|
||||
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
|
||||
)
|
||||
if rc == 0:
|
||||
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
|
||||
@@ -452,15 +464,17 @@ POST_CONFIG_ACTIONS = {
|
||||
|
||||
|
||||
def parse_args(argv):
|
||||
parser = argparse.ArgumentParser(description=f"ESPHome v{const.__version__}")
|
||||
parser.add_argument(
|
||||
"-v", "--verbose", help="Enable verbose esphome logs.", action="store_true"
|
||||
options_parser = argparse.ArgumentParser(add_help=False)
|
||||
options_parser.add_argument(
|
||||
"-v", "--verbose", help="Enable verbose ESPHome logs.", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q", "--quiet", help="Disable all esphome logs.", action="store_true"
|
||||
options_parser.add_argument(
|
||||
"-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
|
||||
)
|
||||
parser.add_argument("--dashboard", help=argparse.SUPPRESS, action="store_true")
|
||||
parser.add_argument(
|
||||
options_parser.add_argument(
|
||||
"--dashboard", help=argparse.SUPPRESS, action="store_true"
|
||||
)
|
||||
options_parser.add_argument(
|
||||
"-s",
|
||||
"--substitution",
|
||||
nargs=2,
|
||||
@@ -468,17 +482,101 @@ def parse_args(argv):
|
||||
help="Add a substitution",
|
||||
metavar=("key", "value"),
|
||||
)
|
||||
parser.add_argument(
|
||||
"configuration", help="Your YAML configuration file.", nargs="*"
|
||||
|
||||
# Keep backward compatibility with the old command line format of
|
||||
# esphome <config> <command>.
|
||||
#
|
||||
# Unfortunately this can't be done by adding another configuration argument to the
|
||||
# main config parser, as argparse is greedy when parsing arguments, so in regular
|
||||
# usage it'll eat the command as the configuration argument and error out out
|
||||
# because it can't parse the configuration as a command.
|
||||
#
|
||||
# Instead, construct an ad-hoc parser for the old format that doesn't actually
|
||||
# process the arguments, but parses them enough to let us figure out if the old
|
||||
# format is used. In that case, swap the command and configuration in the arguments
|
||||
# and continue on with the normal parser (after raising a deprecation warning).
|
||||
#
|
||||
# Disable argparse's built-in help option and add it manually to prevent this
|
||||
# parser from printing the help messagefor the old format when invoked with -h.
|
||||
compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False)
|
||||
compat_parser.add_argument("-h", "--help")
|
||||
compat_parser.add_argument("configuration", nargs="*")
|
||||
compat_parser.add_argument(
|
||||
"command",
|
||||
choices=[
|
||||
"config",
|
||||
"compile",
|
||||
"upload",
|
||||
"logs",
|
||||
"run",
|
||||
"clean-mqtt",
|
||||
"wizard",
|
||||
"mqtt-fingerprint",
|
||||
"version",
|
||||
"clean",
|
||||
"dashboard",
|
||||
"vscode",
|
||||
"update-all",
|
||||
],
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(help="Commands", dest="command")
|
||||
# on Python 3.9+ we can simply set exit_on_error=False in the constructor
|
||||
def _raise(x):
|
||||
raise argparse.ArgumentError(None, x)
|
||||
|
||||
compat_parser.error = _raise
|
||||
|
||||
deprecated_argv_suggestion = None
|
||||
|
||||
if ["dashboard", "config"] == argv[1:3] or ["version"] == argv[1:2]:
|
||||
# this is most likely meant in new-style arg format. do not try compat parsing
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
result, unparsed = compat_parser.parse_known_args(argv[1:])
|
||||
last_option = len(argv) - len(unparsed) - 1 - len(result.configuration)
|
||||
unparsed = [
|
||||
"--device" if arg in ("--upload-port", "--serial-port") else arg
|
||||
for arg in unparsed
|
||||
]
|
||||
argv = (
|
||||
argv[0:last_option] + [result.command] + result.configuration + unparsed
|
||||
)
|
||||
deprecated_argv_suggestion = argv
|
||||
except argparse.ArgumentError:
|
||||
# This is not an old-style command line, so we don't have to do anything.
|
||||
pass
|
||||
|
||||
# And continue on with regular parsing
|
||||
parser = argparse.ArgumentParser(
|
||||
description=f"ESPHome v{const.__version__}", parents=[options_parser]
|
||||
)
|
||||
parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
|
||||
|
||||
mqtt_options = argparse.ArgumentParser(add_help=False)
|
||||
mqtt_options.add_argument("--topic", help="Manually set the MQTT topic.")
|
||||
mqtt_options.add_argument("--username", help="Manually set the MQTT username.")
|
||||
mqtt_options.add_argument("--password", help="Manually set the MQTT password.")
|
||||
mqtt_options.add_argument("--client-id", help="Manually set the MQTT client id.")
|
||||
|
||||
subparsers = parser.add_subparsers(
|
||||
help="Command to run:", dest="command", metavar="command"
|
||||
)
|
||||
subparsers.required = True
|
||||
subparsers.add_parser("config", help="Validate the configuration and spit it out.")
|
||||
|
||||
parser_config = subparsers.add_parser(
|
||||
"config", help="Validate the configuration and spit it out."
|
||||
)
|
||||
parser_config.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
|
||||
parser_compile = subparsers.add_parser(
|
||||
"compile", help="Read the configuration and compile a program."
|
||||
)
|
||||
parser_compile.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
parser_compile.add_argument(
|
||||
"--only-generate",
|
||||
help="Only generate source code, do not compile.",
|
||||
@@ -486,106 +584,122 @@ def parse_args(argv):
|
||||
)
|
||||
|
||||
parser_upload = subparsers.add_parser(
|
||||
"upload", help="Validate the configuration " "and upload the latest binary."
|
||||
"upload", help="Validate the configuration and upload the latest binary."
|
||||
)
|
||||
parser_upload.add_argument(
|
||||
"--upload-port",
|
||||
help="Manually specify the upload port to use. "
|
||||
"For example /dev/cu.SLAB_USBtoUART.",
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
parser_upload.add_argument(
|
||||
"--device",
|
||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||
)
|
||||
|
||||
parser_logs = subparsers.add_parser(
|
||||
"logs", help="Validate the configuration " "and show all MQTT logs."
|
||||
"logs",
|
||||
help="Validate the configuration and show all logs.",
|
||||
parents=[mqtt_options],
|
||||
)
|
||||
parser_logs.add_argument("--topic", help="Manually set the topic to subscribe to.")
|
||||
parser_logs.add_argument("--username", help="Manually set the username.")
|
||||
parser_logs.add_argument("--password", help="Manually set the password.")
|
||||
parser_logs.add_argument("--client-id", help="Manually set the client id.")
|
||||
parser_logs.add_argument(
|
||||
"--serial-port",
|
||||
help="Manually specify a serial port to use"
|
||||
"For example /dev/cu.SLAB_USBtoUART.",
|
||||
"configuration", help="Your YAML configuration file.", nargs=1
|
||||
)
|
||||
parser_logs.add_argument(
|
||||
"--device",
|
||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||
)
|
||||
|
||||
parser_run = subparsers.add_parser(
|
||||
"run",
|
||||
help="Validate the configuration, create a binary, "
|
||||
"upload it, and start MQTT logs.",
|
||||
help="Validate the configuration, create a binary, upload it, and start logs.",
|
||||
parents=[mqtt_options],
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--upload-port",
|
||||
help="Manually specify the upload port/ip to use. "
|
||||
"For example /dev/cu.SLAB_USBtoUART.",
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--no-logs", help="Disable starting MQTT logs.", action="store_true"
|
||||
"--device",
|
||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--topic", help="Manually set the topic to subscribe to for logs."
|
||||
"--no-logs", help="Disable starting logs.", action="store_true"
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--username", help="Manually set the MQTT username for logs."
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--password", help="Manually set the MQTT password for logs."
|
||||
)
|
||||
parser_run.add_argument("--client-id", help="Manually set the client id for logs.")
|
||||
|
||||
parser_clean = subparsers.add_parser(
|
||||
"clean-mqtt", help="Helper to clear an MQTT topic from " "retain messages."
|
||||
"clean-mqtt",
|
||||
help="Helper to clear retained messages from an MQTT topic.",
|
||||
parents=[mqtt_options],
|
||||
)
|
||||
parser_clean.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
parser_clean.add_argument("--topic", help="Manually set the topic to subscribe to.")
|
||||
parser_clean.add_argument("--username", help="Manually set the username.")
|
||||
parser_clean.add_argument("--password", help="Manually set the password.")
|
||||
parser_clean.add_argument("--client-id", help="Manually set the client id.")
|
||||
|
||||
subparsers.add_parser(
|
||||
parser_wizard = subparsers.add_parser(
|
||||
"wizard",
|
||||
help="A helpful setup wizard that will guide "
|
||||
"you through setting up esphome.",
|
||||
help="A helpful setup wizard that will guide you through setting up ESPHome.",
|
||||
)
|
||||
parser_wizard.add_argument(
|
||||
"configuration",
|
||||
help="Your YAML configuration file.",
|
||||
)
|
||||
|
||||
subparsers.add_parser(
|
||||
parser_fingerprint = subparsers.add_parser(
|
||||
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
|
||||
)
|
||||
parser_fingerprint.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
|
||||
subparsers.add_parser("version", help="Print the esphome version and exit.")
|
||||
subparsers.add_parser("version", help="Print the ESPHome version and exit.")
|
||||
|
||||
subparsers.add_parser("clean", help="Delete all temporary build files.")
|
||||
parser_clean = subparsers.add_parser(
|
||||
"clean", help="Delete all temporary build files."
|
||||
)
|
||||
parser_clean.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
|
||||
dashboard = subparsers.add_parser(
|
||||
parser_dashboard = subparsers.add_parser(
|
||||
"dashboard", help="Create a simple web server for a dashboard."
|
||||
)
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"configuration",
|
||||
help="Your YAML configuration file directory.",
|
||||
)
|
||||
parser_dashboard.add_argument(
|
||||
"--port",
|
||||
help="The HTTP port to open connections on. Defaults to 6052.",
|
||||
type=int,
|
||||
default=6052,
|
||||
)
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"--username",
|
||||
help="The optional username to require " "for authentication.",
|
||||
help="The optional username to require for authentication.",
|
||||
type=str,
|
||||
default="",
|
||||
)
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"--password",
|
||||
help="The optional password to require " "for authentication.",
|
||||
help="The optional password to require for authentication.",
|
||||
type=str,
|
||||
default="",
|
||||
)
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
|
||||
)
|
||||
dashboard.add_argument("--hassio", help=argparse.SUPPRESS, action="store_true")
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"--hassio", help=argparse.SUPPRESS, action="store_true"
|
||||
)
|
||||
parser_dashboard.add_argument(
|
||||
"--socket", help="Make the dashboard serve under a unix socket", type=str
|
||||
)
|
||||
|
||||
vscode = subparsers.add_parser("vscode", help=argparse.SUPPRESS)
|
||||
vscode.add_argument("--ace", action="store_true")
|
||||
parser_vscode = subparsers.add_parser("vscode")
|
||||
parser_vscode.add_argument("configuration", help="Your YAML configuration file.")
|
||||
parser_vscode.add_argument("--ace", action="store_true")
|
||||
|
||||
subparsers.add_parser("update-all", help=argparse.SUPPRESS)
|
||||
parser_update = subparsers.add_parser("update-all")
|
||||
parser_update.add_argument(
|
||||
"configuration", help="Your YAML configuration file directories.", nargs="+"
|
||||
)
|
||||
|
||||
return parser.parse_args(argv[1:])
|
||||
|
||||
@@ -595,9 +709,13 @@ def run_esphome(argv):
|
||||
CORE.dashboard = args.dashboard
|
||||
|
||||
setup_log(args.verbose, args.quiet)
|
||||
if args.command != "version" and not args.configuration:
|
||||
_LOGGER.error("Missing configuration parameter, see esphome --help.")
|
||||
return 1
|
||||
if args.deprecated_argv_suggestion is not None and args.command != "vscode":
|
||||
_LOGGER.warning(
|
||||
"Calling ESPHome with the configuration before the command is deprecated "
|
||||
"and will be removed in the future. "
|
||||
)
|
||||
_LOGGER.warning("Please instead use:")
|
||||
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion[1:]))
|
||||
|
||||
if sys.version_info < (3, 7, 0):
|
||||
_LOGGER.error(
|
||||
@@ -610,7 +728,7 @@ def run_esphome(argv):
|
||||
try:
|
||||
return PRE_CONFIG_ACTIONS[args.command](args)
|
||||
except EsphomeError as e:
|
||||
_LOGGER.error(e)
|
||||
_LOGGER.error(e, exc_info=args.verbose)
|
||||
return 1
|
||||
|
||||
for conf_path in args.configuration:
|
||||
@@ -628,7 +746,7 @@ def run_esphome(argv):
|
||||
try:
|
||||
rc = POST_CONFIG_ACTIONS[args.command](args, config)
|
||||
except EsphomeError as e:
|
||||
_LOGGER.error(e)
|
||||
_LOGGER.error(e, exc_info=args.verbose)
|
||||
return 1
|
||||
if rc != 0:
|
||||
return rc
|
||||
|
||||
@@ -10,7 +10,6 @@ from esphome.const import (
|
||||
CONF_TYPE_ID,
|
||||
CONF_TIME,
|
||||
)
|
||||
from esphome.core import coroutine
|
||||
from esphome.jsonschema import jschema_extractor
|
||||
from esphome.util import Registry
|
||||
|
||||
@@ -142,27 +141,27 @@ NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
|
||||
|
||||
|
||||
@register_condition("and", AndCondition, validate_condition_list)
|
||||
def and_condition_to_code(config, condition_id, template_arg, args):
|
||||
conditions = yield build_condition_list(config, template_arg, args)
|
||||
yield cg.new_Pvariable(condition_id, template_arg, conditions)
|
||||
async def and_condition_to_code(config, condition_id, template_arg, args):
|
||||
conditions = await build_condition_list(config, template_arg, args)
|
||||
return cg.new_Pvariable(condition_id, template_arg, conditions)
|
||||
|
||||
|
||||
@register_condition("or", OrCondition, validate_condition_list)
|
||||
def or_condition_to_code(config, condition_id, template_arg, args):
|
||||
conditions = yield build_condition_list(config, template_arg, args)
|
||||
yield cg.new_Pvariable(condition_id, template_arg, conditions)
|
||||
async def or_condition_to_code(config, condition_id, template_arg, args):
|
||||
conditions = await build_condition_list(config, template_arg, args)
|
||||
return cg.new_Pvariable(condition_id, template_arg, conditions)
|
||||
|
||||
|
||||
@register_condition("not", NotCondition, validate_potentially_and_condition)
|
||||
def not_condition_to_code(config, condition_id, template_arg, args):
|
||||
condition = yield build_condition(config, template_arg, args)
|
||||
yield cg.new_Pvariable(condition_id, template_arg, condition)
|
||||
async def not_condition_to_code(config, condition_id, template_arg, args):
|
||||
condition = await build_condition(config, template_arg, args)
|
||||
return cg.new_Pvariable(condition_id, template_arg, condition)
|
||||
|
||||
|
||||
@register_condition("lambda", LambdaCondition, cv.lambda_)
|
||||
def lambda_condition_to_code(config, condition_id, template_arg, args):
|
||||
lambda_ = yield cg.process_lambda(config, args, return_type=bool)
|
||||
yield cg.new_Pvariable(condition_id, template_arg, lambda_)
|
||||
@register_condition("lambda", LambdaCondition, cv.returning_lambda)
|
||||
async def lambda_condition_to_code(config, condition_id, template_arg, args):
|
||||
lambda_ = await cg.process_lambda(config, args, return_type=bool)
|
||||
return cg.new_Pvariable(condition_id, template_arg, lambda_)
|
||||
|
||||
|
||||
@register_condition(
|
||||
@@ -177,26 +176,26 @@ def lambda_condition_to_code(config, condition_id, template_arg, args):
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
)
|
||||
def for_condition_to_code(config, condition_id, template_arg, args):
|
||||
condition = yield build_condition(
|
||||
async def for_condition_to_code(config, condition_id, template_arg, args):
|
||||
condition = await build_condition(
|
||||
config[CONF_CONDITION], cg.TemplateArguments(), []
|
||||
)
|
||||
var = cg.new_Pvariable(condition_id, template_arg, condition)
|
||||
yield cg.register_component(var, config)
|
||||
templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32)
|
||||
await cg.register_component(var, config)
|
||||
templ = await cg.templatable(config[CONF_TIME], args, cg.uint32)
|
||||
cg.add(var.set_time(templ))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
@register_action(
|
||||
"delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds)
|
||||
)
|
||||
def delay_action_to_code(config, action_id, template_arg, args):
|
||||
async def delay_action_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
yield cg.register_component(var, {})
|
||||
template_ = yield cg.templatable(config, args, cg.uint32)
|
||||
await cg.register_component(var, {})
|
||||
template_ = await cg.templatable(config, args, cg.uint32)
|
||||
cg.add(var.set_delay(template_))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
@register_action(
|
||||
@@ -211,16 +210,16 @@ def delay_action_to_code(config, action_id, template_arg, args):
|
||||
cv.has_at_least_one_key(CONF_THEN, CONF_ELSE),
|
||||
),
|
||||
)
|
||||
def if_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
async def if_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
||||
if CONF_THEN in config:
|
||||
actions = yield build_action_list(config[CONF_THEN], template_arg, args)
|
||||
actions = await build_action_list(config[CONF_THEN], template_arg, args)
|
||||
cg.add(var.add_then(actions))
|
||||
if CONF_ELSE in config:
|
||||
actions = yield build_action_list(config[CONF_ELSE], template_arg, args)
|
||||
actions = await build_action_list(config[CONF_ELSE], template_arg, args)
|
||||
cg.add(var.add_else(actions))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
@register_action(
|
||||
@@ -233,12 +232,12 @@ def if_action_to_code(config, action_id, template_arg, args):
|
||||
}
|
||||
),
|
||||
)
|
||||
def while_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
async def while_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
||||
actions = yield build_action_list(config[CONF_THEN], template_arg, args)
|
||||
actions = await build_action_list(config[CONF_THEN], template_arg, args)
|
||||
cg.add(var.add_then(actions))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
def validate_wait_until(value):
|
||||
@@ -253,17 +252,17 @@ def validate_wait_until(value):
|
||||
|
||||
|
||||
@register_action("wait_until", WaitUntilAction, validate_wait_until)
|
||||
def wait_until_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
async def wait_until_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
||||
yield cg.register_component(var, {})
|
||||
yield var
|
||||
await cg.register_component(var, {})
|
||||
return var
|
||||
|
||||
|
||||
@register_action("lambda", LambdaAction, cv.lambda_)
|
||||
def lambda_action_to_code(config, action_id, template_arg, args):
|
||||
lambda_ = yield cg.process_lambda(config, args, return_type=cg.void)
|
||||
yield cg.new_Pvariable(action_id, template_arg, lambda_)
|
||||
async def lambda_action_to_code(config, action_id, template_arg, args):
|
||||
lambda_ = await cg.process_lambda(config, args, return_type=cg.void)
|
||||
return cg.new_Pvariable(action_id, template_arg, lambda_)
|
||||
|
||||
|
||||
@register_action(
|
||||
@@ -275,54 +274,51 @@ def lambda_action_to_code(config, action_id, template_arg, args):
|
||||
}
|
||||
),
|
||||
)
|
||||
def component_update_action_to_code(config, action_id, template_arg, args):
|
||||
comp = yield cg.get_variable(config[CONF_ID])
|
||||
yield cg.new_Pvariable(action_id, template_arg, comp)
|
||||
async def component_update_action_to_code(config, action_id, template_arg, args):
|
||||
comp = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, comp)
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_action(full_config, template_arg, args):
|
||||
async def build_action(full_config, template_arg, args):
|
||||
registry_entry, config = cg.extract_registry_entry_config(
|
||||
ACTION_REGISTRY, full_config
|
||||
)
|
||||
action_id = full_config[CONF_TYPE_ID]
|
||||
builder = registry_entry.coroutine_fun
|
||||
yield builder(config, action_id, template_arg, args)
|
||||
ret = await builder(config, action_id, template_arg, args)
|
||||
return ret
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_action_list(config, templ, arg_type):
|
||||
async def build_action_list(config, templ, arg_type):
|
||||
actions = []
|
||||
for conf in config:
|
||||
action = yield build_action(conf, templ, arg_type)
|
||||
action = await build_action(conf, templ, arg_type)
|
||||
actions.append(action)
|
||||
yield actions
|
||||
return actions
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_condition(full_config, template_arg, args):
|
||||
async def build_condition(full_config, template_arg, args):
|
||||
registry_entry, config = cg.extract_registry_entry_config(
|
||||
CONDITION_REGISTRY, full_config
|
||||
)
|
||||
action_id = full_config[CONF_TYPE_ID]
|
||||
builder = registry_entry.coroutine_fun
|
||||
yield builder(config, action_id, template_arg, args)
|
||||
ret = await builder(config, action_id, template_arg, args)
|
||||
return ret
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_condition_list(config, templ, args):
|
||||
async def build_condition_list(config, templ, args):
|
||||
conditions = []
|
||||
for conf in config:
|
||||
condition = yield build_condition(conf, templ, args)
|
||||
condition = await build_condition(conf, templ, args)
|
||||
conditions.append(condition)
|
||||
yield conditions
|
||||
return conditions
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_automation(trigger, args, config):
|
||||
async def build_automation(trigger, args, config):
|
||||
arg_types = [arg[0] for arg in args]
|
||||
templ = cg.TemplateArguments(*arg_types)
|
||||
obj = cg.new_Pvariable(config[CONF_AUTOMATION_ID], templ, trigger)
|
||||
actions = yield build_action_list(config[CONF_THEN], templ, args)
|
||||
actions = await build_action_list(config[CONF_THEN], templ, args)
|
||||
cg.add(obj.add_actions(actions))
|
||||
yield obj
|
||||
return obj
|
||||
|
||||
875
esphome/boards.py
Normal file
875
esphome/boards.py
Normal file
@@ -0,0 +1,875 @@
|
||||
ESP8266_BASE_PINS = {
|
||||
"A0": 17,
|
||||
"SS": 15,
|
||||
"MOSI": 13,
|
||||
"MISO": 12,
|
||||
"SCK": 14,
|
||||
"SDA": 4,
|
||||
"SCL": 5,
|
||||
"RX": 3,
|
||||
"TX": 1,
|
||||
}
|
||||
|
||||
ESP8266_BOARD_PINS = {
|
||||
"d1": {
|
||||
"D0": 3,
|
||||
"D1": 1,
|
||||
"D2": 16,
|
||||
"D3": 5,
|
||||
"D4": 4,
|
||||
"D5": 14,
|
||||
"D6": 12,
|
||||
"D7": 13,
|
||||
"D8": 0,
|
||||
"D9": 2,
|
||||
"D10": 15,
|
||||
"D11": 13,
|
||||
"D12": 14,
|
||||
"D13": 14,
|
||||
"D14": 4,
|
||||
"D15": 5,
|
||||
"LED": 2,
|
||||
},
|
||||
"d1_mini": {
|
||||
"D0": 16,
|
||||
"D1": 5,
|
||||
"D2": 4,
|
||||
"D3": 0,
|
||||
"D4": 2,
|
||||
"D5": 14,
|
||||
"D6": 12,
|
||||
"D7": 13,
|
||||
"D8": 15,
|
||||
"LED": 2,
|
||||
},
|
||||
"d1_mini_lite": "d1_mini",
|
||||
"d1_mini_pro": "d1_mini",
|
||||
"esp01": {},
|
||||
"esp01_1m": {},
|
||||
"esp07": {},
|
||||
"esp12e": {},
|
||||
"esp210": {},
|
||||
"esp8285": {},
|
||||
"esp_wroom_02": {},
|
||||
"espduino": {"LED": 16},
|
||||
"espectro": {"LED": 15, "BUTTON": 2},
|
||||
"espino": {"LED": 2, "LED_RED": 2, "LED_GREEN": 4, "LED_BLUE": 5, "BUTTON": 0},
|
||||
"espinotee": {"LED": 16},
|
||||
"espresso_lite_v1": {"LED": 16},
|
||||
"espresso_lite_v2": {"LED": 2},
|
||||
"gen4iod": {},
|
||||
"heltec_wifi_kit_8": "d1_mini",
|
||||
"huzzah": {
|
||||
"LED": 0,
|
||||
"LED_RED": 0,
|
||||
"LED_BLUE": 2,
|
||||
"D4": 4,
|
||||
"D5": 5,
|
||||
"D12": 12,
|
||||
"D13": 13,
|
||||
"D14": 14,
|
||||
"D15": 15,
|
||||
"D16": 16,
|
||||
},
|
||||
"inventone": {},
|
||||
"modwifi": {},
|
||||
"nodemcu": {
|
||||
"D0": 16,
|
||||
"D1": 5,
|
||||
"D2": 4,
|
||||
"D3": 0,
|
||||
"D4": 2,
|
||||
"D5": 14,
|
||||
"D6": 12,
|
||||
"D7": 13,
|
||||
"D8": 15,
|
||||
"D9": 3,
|
||||
"D10": 1,
|
||||
"LED": 16,
|
||||
},
|
||||
"nodemcuv2": "nodemcu",
|
||||
"oak": {
|
||||
"P0": 2,
|
||||
"P1": 5,
|
||||
"P2": 0,
|
||||
"P3": 3,
|
||||
"P4": 1,
|
||||
"P5": 4,
|
||||
"P6": 15,
|
||||
"P7": 13,
|
||||
"P8": 12,
|
||||
"P9": 14,
|
||||
"P10": 16,
|
||||
"P11": 17,
|
||||
"LED": 5,
|
||||
},
|
||||
"phoenix_v1": {"LED": 16},
|
||||
"phoenix_v2": {"LED": 2},
|
||||
"sparkfunBlynk": "thing",
|
||||
"thing": {"LED": 5, "SDA": 2, "SCL": 14},
|
||||
"thingdev": "thing",
|
||||
"wifi_slot": {"LED": 2},
|
||||
"wifiduino": {
|
||||
"D0": 3,
|
||||
"D1": 1,
|
||||
"D2": 2,
|
||||
"D3": 0,
|
||||
"D4": 4,
|
||||
"D5": 5,
|
||||
"D6": 16,
|
||||
"D7": 14,
|
||||
"D8": 12,
|
||||
"D9": 13,
|
||||
"D10": 15,
|
||||
"D11": 13,
|
||||
"D12": 12,
|
||||
"D13": 14,
|
||||
},
|
||||
"wifinfo": {
|
||||
"LED": 12,
|
||||
"D0": 16,
|
||||
"D1": 5,
|
||||
"D2": 4,
|
||||
"D3": 0,
|
||||
"D4": 2,
|
||||
"D5": 14,
|
||||
"D6": 12,
|
||||
"D7": 13,
|
||||
"D8": 15,
|
||||
"D9": 3,
|
||||
"D10": 1,
|
||||
},
|
||||
"wio_link": {"LED": 2, "GROVE": 15, "D0": 14, "D1": 12, "D2": 13, "BUTTON": 0},
|
||||
"wio_node": {"LED": 2, "GROVE": 15, "D0": 3, "D1": 5, "BUTTON": 0},
|
||||
"xinabox_cw01": {"SDA": 2, "SCL": 14, "LED": 5, "LED_RED": 12, "LED_GREEN": 13},
|
||||
}
|
||||
|
||||
FLASH_SIZE_1_MB = 2 ** 20
|
||||
FLASH_SIZE_512_KB = FLASH_SIZE_1_MB // 2
|
||||
FLASH_SIZE_2_MB = 2 * FLASH_SIZE_1_MB
|
||||
FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB
|
||||
FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB
|
||||
|
||||
ESP8266_FLASH_SIZES = {
|
||||
"d1": FLASH_SIZE_4_MB,
|
||||
"d1_mini": FLASH_SIZE_4_MB,
|
||||
"d1_mini_lite": FLASH_SIZE_1_MB,
|
||||
"d1_mini_pro": FLASH_SIZE_16_MB,
|
||||
"esp01": FLASH_SIZE_512_KB,
|
||||
"esp01_1m": FLASH_SIZE_1_MB,
|
||||
"esp07": FLASH_SIZE_4_MB,
|
||||
"esp12e": FLASH_SIZE_4_MB,
|
||||
"esp210": FLASH_SIZE_4_MB,
|
||||
"esp8285": FLASH_SIZE_1_MB,
|
||||
"esp_wroom_02": FLASH_SIZE_2_MB,
|
||||
"espduino": FLASH_SIZE_4_MB,
|
||||
"espectro": FLASH_SIZE_4_MB,
|
||||
"espino": FLASH_SIZE_4_MB,
|
||||
"espinotee": FLASH_SIZE_4_MB,
|
||||
"espresso_lite_v1": FLASH_SIZE_4_MB,
|
||||
"espresso_lite_v2": FLASH_SIZE_4_MB,
|
||||
"gen4iod": FLASH_SIZE_512_KB,
|
||||
"heltec_wifi_kit_8": FLASH_SIZE_4_MB,
|
||||
"huzzah": FLASH_SIZE_4_MB,
|
||||
"inventone": FLASH_SIZE_4_MB,
|
||||
"modwifi": FLASH_SIZE_2_MB,
|
||||
"nodemcu": FLASH_SIZE_4_MB,
|
||||
"nodemcuv2": FLASH_SIZE_4_MB,
|
||||
"oak": FLASH_SIZE_4_MB,
|
||||
"phoenix_v1": FLASH_SIZE_4_MB,
|
||||
"phoenix_v2": FLASH_SIZE_4_MB,
|
||||
"sparkfunBlynk": FLASH_SIZE_4_MB,
|
||||
"thing": FLASH_SIZE_512_KB,
|
||||
"thingdev": FLASH_SIZE_512_KB,
|
||||
"wifi_slot": FLASH_SIZE_1_MB,
|
||||
"wifiduino": FLASH_SIZE_4_MB,
|
||||
"wifinfo": FLASH_SIZE_1_MB,
|
||||
"wio_link": FLASH_SIZE_4_MB,
|
||||
"wio_node": FLASH_SIZE_4_MB,
|
||||
"xinabox_cw01": FLASH_SIZE_4_MB,
|
||||
}
|
||||
|
||||
ESP8266_LD_SCRIPTS = {
|
||||
FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"),
|
||||
FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"),
|
||||
FLASH_SIZE_2_MB: ("eagle.flash.2m.ld", "eagle.flash.2m.ld"),
|
||||
FLASH_SIZE_4_MB: ("eagle.flash.4m.ld", "eagle.flash.4m.ld"),
|
||||
FLASH_SIZE_16_MB: ("eagle.flash.16m.ld", "eagle.flash.16m14m.ld"),
|
||||
}
|
||||
|
||||
ESP32_BASE_PINS = {
|
||||
"TX": 1,
|
||||
"RX": 3,
|
||||
"SDA": 21,
|
||||
"SCL": 22,
|
||||
"SS": 5,
|
||||
"MOSI": 23,
|
||||
"MISO": 19,
|
||||
"SCK": 18,
|
||||
"A0": 36,
|
||||
"A3": 39,
|
||||
"A4": 32,
|
||||
"A5": 33,
|
||||
"A6": 34,
|
||||
"A7": 35,
|
||||
"A10": 4,
|
||||
"A11": 0,
|
||||
"A12": 2,
|
||||
"A13": 15,
|
||||
"A14": 13,
|
||||
"A15": 12,
|
||||
"A16": 14,
|
||||
"A17": 27,
|
||||
"A18": 25,
|
||||
"A19": 26,
|
||||
"T0": 4,
|
||||
"T1": 0,
|
||||
"T2": 2,
|
||||
"T3": 15,
|
||||
"T4": 13,
|
||||
"T5": 12,
|
||||
"T6": 14,
|
||||
"T7": 27,
|
||||
"T8": 33,
|
||||
"T9": 32,
|
||||
"DAC1": 25,
|
||||
"DAC2": 26,
|
||||
"SVP": 36,
|
||||
"SVN": 39,
|
||||
}
|
||||
|
||||
ESP32_BOARD_PINS = {
|
||||
"alksesp32": {
|
||||
"A0": 32,
|
||||
"A1": 33,
|
||||
"A2": 25,
|
||||
"A3": 26,
|
||||
"A4": 27,
|
||||
"A5": 14,
|
||||
"A6": 12,
|
||||
"A7": 15,
|
||||
"D0": 40,
|
||||
"D1": 41,
|
||||
"D10": 19,
|
||||
"D11": 21,
|
||||
"D12": 22,
|
||||
"D13": 23,
|
||||
"D2": 15,
|
||||
"D3": 2,
|
||||
"D4": 0,
|
||||
"D5": 4,
|
||||
"D6": 16,
|
||||
"D7": 17,
|
||||
"D8": 5,
|
||||
"D9": 18,
|
||||
"DHT_PIN": 26,
|
||||
"LED": 23,
|
||||
"L_B": 5,
|
||||
"L_G": 17,
|
||||
"L_R": 22,
|
||||
"L_RGB_B": 16,
|
||||
"L_RGB_G": 21,
|
||||
"L_RGB_R": 4,
|
||||
"L_Y": 23,
|
||||
"MISO": 22,
|
||||
"MOSI": 21,
|
||||
"PHOTO": 25,
|
||||
"PIEZO1": 19,
|
||||
"PIEZO2": 18,
|
||||
"POT1": 32,
|
||||
"POT2": 33,
|
||||
"S1": 4,
|
||||
"S2": 16,
|
||||
"S3": 18,
|
||||
"S4": 19,
|
||||
"S5": 21,
|
||||
"SCK": 23,
|
||||
"SCL": 14,
|
||||
"SDA": 27,
|
||||
"SS": 19,
|
||||
"SW1": 15,
|
||||
"SW2": 2,
|
||||
"SW3": 0,
|
||||
},
|
||||
"bpi-bit": {
|
||||
"BUTTON_A": 35,
|
||||
"BUTTON_B": 27,
|
||||
"BUZZER": 25,
|
||||
"LIGHT_SENSOR1": 36,
|
||||
"LIGHT_SENSOR2": 39,
|
||||
"MPU9250_INT": 0,
|
||||
"P0": 25,
|
||||
"P1": 32,
|
||||
"P10": 26,
|
||||
"P11": 27,
|
||||
"P12": 2,
|
||||
"P13": 18,
|
||||
"P14": 19,
|
||||
"P15": 23,
|
||||
"P16": 5,
|
||||
"P19": 22,
|
||||
"P2": 33,
|
||||
"P20": 21,
|
||||
"P3": 13,
|
||||
"P4": 15,
|
||||
"P5": 35,
|
||||
"P6": 12,
|
||||
"P7": 14,
|
||||
"P8": 16,
|
||||
"P9": 17,
|
||||
"RGB_LED": 4,
|
||||
"TEMPERATURE_SENSOR": 34,
|
||||
},
|
||||
"d-duino-32": {
|
||||
"D1": 5,
|
||||
"D10": 1,
|
||||
"D2": 4,
|
||||
"D3": 0,
|
||||
"D4": 2,
|
||||
"D5": 14,
|
||||
"D6": 12,
|
||||
"D7": 13,
|
||||
"D8": 15,
|
||||
"D9": 3,
|
||||
"MISO": 12,
|
||||
"MOSI": 13,
|
||||
"SCK": 14,
|
||||
"SCL": 4,
|
||||
"SDA": 5,
|
||||
"SS": 15,
|
||||
},
|
||||
"esp-wrover-kit": {},
|
||||
"esp32-devkitlipo": {},
|
||||
"esp32-evb": {
|
||||
"BUTTON": 34,
|
||||
"MISO": 15,
|
||||
"MOSI": 2,
|
||||
"SCK": 14,
|
||||
"SCL": 16,
|
||||
"SDA": 13,
|
||||
"SS": 17,
|
||||
},
|
||||
"esp32-gateway": {"BUTTON": 34, "LED": 33, "SCL": 16, "SDA": 32},
|
||||
"esp32-poe-iso": {
|
||||
"BUTTON": 34,
|
||||
"MISO": 15,
|
||||
"MOSI": 2,
|
||||
"SCK": 14,
|
||||
"SCL": 16,
|
||||
"SDA": 13,
|
||||
},
|
||||
"esp32-poe": {"BUTTON": 34, "MISO": 15, "MOSI": 2, "SCK": 14, "SCL": 16, "SDA": 13},
|
||||
"esp32-pro": {
|
||||
"BUTTON": 34,
|
||||
"MISO": 15,
|
||||
"MOSI": 2,
|
||||
"SCK": 14,
|
||||
"SCL": 16,
|
||||
"SDA": 13,
|
||||
"SS": 17,
|
||||
},
|
||||
"esp320": {
|
||||
"LED": 5,
|
||||
"MISO": 12,
|
||||
"MOSI": 13,
|
||||
"SCK": 14,
|
||||
"SCL": 14,
|
||||
"SDA": 2,
|
||||
"SS": 15,
|
||||
},
|
||||
"esp32cam": {},
|
||||
"esp32dev": {},
|
||||
"esp32doit-devkit-v1": {"LED": 2},
|
||||
"esp32thing": {"BUTTON": 0, "LED": 5, "SS": 2},
|
||||
"esp32vn-iot-uno": {},
|
||||
"espea32": {"BUTTON": 0, "LED": 5},
|
||||
"espectro32": {"LED": 15, "SD_SS": 33},
|
||||
"espino32": {"BUTTON": 0, "LED": 16},
|
||||
"featheresp32": {
|
||||
"A0": 26,
|
||||
"A1": 25,
|
||||
"A10": 27,
|
||||
"A11": 12,
|
||||
"A12": 13,
|
||||
"A13": 35,
|
||||
"A2": 34,
|
||||
"A4": 36,
|
||||
"A5": 4,
|
||||
"A6": 14,
|
||||
"A7": 32,
|
||||
"A8": 15,
|
||||
"A9": 33,
|
||||
"Ax": 2,
|
||||
"LED": 13,
|
||||
"MOSI": 18,
|
||||
"RX": 16,
|
||||
"SCK": 5,
|
||||
"SDA": 23,
|
||||
"SS": 33,
|
||||
"TX": 17,
|
||||
},
|
||||
"firebeetle32": {"LED": 2},
|
||||
"fm-devkit": {
|
||||
"D0": 34,
|
||||
"D1": 35,
|
||||
"D10": 0,
|
||||
"D2": 32,
|
||||
"D3": 33,
|
||||
"D4": 27,
|
||||
"D5": 14,
|
||||
"D6": 12,
|
||||
"D7": 13,
|
||||
"D8": 15,
|
||||
"D9": 23,
|
||||
"I2S_DOUT": 22,
|
||||
"I2S_LRCLK": 25,
|
||||
"I2S_MCLK": 2,
|
||||
"I2S_SCLK": 26,
|
||||
"LED": 5,
|
||||
"SCL": 17,
|
||||
"SDA": 16,
|
||||
"SW1": 4,
|
||||
"SW2": 18,
|
||||
"SW3": 19,
|
||||
"SW4": 21,
|
||||
},
|
||||
"frogboard": {},
|
||||
"heltec_wifi_kit_32": {
|
||||
"A1": 37,
|
||||
"A2": 38,
|
||||
"BUTTON": 0,
|
||||
"LED": 25,
|
||||
"RST_OLED": 16,
|
||||
"SCL_OLED": 15,
|
||||
"SDA_OLED": 4,
|
||||
"Vext": 21,
|
||||
},
|
||||
"heltec_wifi_lora_32": {
|
||||
"BUTTON": 0,
|
||||
"DIO0": 26,
|
||||
"DIO1": 33,
|
||||
"DIO2": 32,
|
||||
"LED": 25,
|
||||
"MOSI": 27,
|
||||
"RST_LoRa": 14,
|
||||
"RST_OLED": 16,
|
||||
"SCK": 5,
|
||||
"SCL_OLED": 15,
|
||||
"SDA_OLED": 4,
|
||||
"SS": 18,
|
||||
"Vext": 21,
|
||||
},
|
||||
"heltec_wifi_lora_32_V2": {
|
||||
"BUTTON": 0,
|
||||
"DIO0": 26,
|
||||
"DIO1": 35,
|
||||
"DIO2": 34,
|
||||
"LED": 25,
|
||||
"MOSI": 27,
|
||||
"RST_LoRa": 14,
|
||||
"RST_OLED": 16,
|
||||
"SCK": 5,
|
||||
"SCL_OLED": 15,
|
||||
"SDA_OLED": 4,
|
||||
"SS": 18,
|
||||
"Vext": 21,
|
||||
},
|
||||
"heltec_wireless_stick": {
|
||||
"BUTTON": 0,
|
||||
"DIO0": 26,
|
||||
"DIO1": 35,
|
||||
"DIO2": 34,
|
||||
"LED": 25,
|
||||
"MOSI": 27,
|
||||
"RST_LoRa": 14,
|
||||
"RST_OLED": 16,
|
||||
"SCK": 5,
|
||||
"SCL_OLED": 15,
|
||||
"SDA_OLED": 4,
|
||||
"SS": 18,
|
||||
"Vext": 21,
|
||||
},
|
||||
"hornbill32dev": {"BUTTON": 0, "LED": 13},
|
||||
"hornbill32minima": {"SS": 2},
|
||||
"intorobot": {
|
||||
"A1": 39,
|
||||
"A2": 35,
|
||||
"A3": 25,
|
||||
"A4": 26,
|
||||
"A5": 14,
|
||||
"A6": 12,
|
||||
"A7": 15,
|
||||
"A8": 13,
|
||||
"A9": 2,
|
||||
"BUTTON": 0,
|
||||
"D0": 19,
|
||||
"D1": 23,
|
||||
"D2": 18,
|
||||
"D3": 17,
|
||||
"D4": 16,
|
||||
"D5": 5,
|
||||
"D6": 4,
|
||||
"LED": 4,
|
||||
"MISO": 17,
|
||||
"MOSI": 16,
|
||||
"RGB_B_BUILTIN": 22,
|
||||
"RGB_G_BUILTIN": 21,
|
||||
"RGB_R_BUILTIN": 27,
|
||||
"SCL": 19,
|
||||
"SDA": 23,
|
||||
"T0": 19,
|
||||
"T1": 23,
|
||||
"T2": 18,
|
||||
"T3": 17,
|
||||
"T4": 16,
|
||||
"T5": 5,
|
||||
"T6": 4,
|
||||
},
|
||||
"iotaap_magnolia": {},
|
||||
"iotbusio": {},
|
||||
"iotbusproteus": {},
|
||||
"lolin32": {"LED": 5},
|
||||
"lolin32_lite": {"LED": 22},
|
||||
"lolin_d32": {"LED": 5, "_VBAT": 35},
|
||||
"lolin_d32_pro": {"LED": 5, "_VBAT": 35},
|
||||
"lopy": {
|
||||
"A1": 37,
|
||||
"A2": 38,
|
||||
"LED": 0,
|
||||
"MISO": 37,
|
||||
"MOSI": 22,
|
||||
"SCK": 13,
|
||||
"SCL": 13,
|
||||
"SDA": 12,
|
||||
"SS": 17,
|
||||
},
|
||||
"lopy4": {
|
||||
"A1": 37,
|
||||
"A2": 38,
|
||||
"LED": 0,
|
||||
"MISO": 37,
|
||||
"MOSI": 22,
|
||||
"SCK": 13,
|
||||
"SCL": 13,
|
||||
"SDA": 12,
|
||||
"SS": 18,
|
||||
},
|
||||
"m5stack-core-esp32": {
|
||||
"ADC1": 35,
|
||||
"ADC2": 36,
|
||||
"G0": 0,
|
||||
"G1": 1,
|
||||
"G12": 12,
|
||||
"G13": 13,
|
||||
"G15": 15,
|
||||
"G16": 16,
|
||||
"G17": 17,
|
||||
"G18": 18,
|
||||
"G19": 19,
|
||||
"G2": 2,
|
||||
"G21": 21,
|
||||
"G22": 22,
|
||||
"G23": 23,
|
||||
"G25": 25,
|
||||
"G26": 26,
|
||||
"G3": 3,
|
||||
"G34": 34,
|
||||
"G35": 35,
|
||||
"G36": 36,
|
||||
"G5": 5,
|
||||
"RXD2": 16,
|
||||
"TXD2": 17,
|
||||
},
|
||||
"m5stack-fire": {
|
||||
"ADC1": 35,
|
||||
"ADC2": 36,
|
||||
"G0": 0,
|
||||
"G1": 1,
|
||||
"G12": 12,
|
||||
"G13": 13,
|
||||
"G15": 15,
|
||||
"G16": 16,
|
||||
"G17": 17,
|
||||
"G18": 18,
|
||||
"G19": 19,
|
||||
"G2": 2,
|
||||
"G21": 21,
|
||||
"G22": 22,
|
||||
"G23": 23,
|
||||
"G25": 25,
|
||||
"G26": 26,
|
||||
"G3": 3,
|
||||
"G34": 34,
|
||||
"G35": 35,
|
||||
"G36": 36,
|
||||
"G5": 5,
|
||||
},
|
||||
"m5stack-grey": {
|
||||
"ADC1": 35,
|
||||
"ADC2": 36,
|
||||
"G0": 0,
|
||||
"G1": 1,
|
||||
"G12": 12,
|
||||
"G13": 13,
|
||||
"G15": 15,
|
||||
"G16": 16,
|
||||
"G17": 17,
|
||||
"G18": 18,
|
||||
"G19": 19,
|
||||
"G2": 2,
|
||||
"G21": 21,
|
||||
"G22": 22,
|
||||
"G23": 23,
|
||||
"G25": 25,
|
||||
"G26": 26,
|
||||
"G3": 3,
|
||||
"G34": 34,
|
||||
"G35": 35,
|
||||
"G36": 36,
|
||||
"G5": 5,
|
||||
"RXD2": 16,
|
||||
"TXD2": 17,
|
||||
},
|
||||
"m5stick-c": {
|
||||
"ADC1": 35,
|
||||
"ADC2": 36,
|
||||
"G0": 0,
|
||||
"G10": 10,
|
||||
"G26": 26,
|
||||
"G32": 32,
|
||||
"G33": 33,
|
||||
"G36": 36,
|
||||
"G37": 37,
|
||||
"G39": 39,
|
||||
"G9": 9,
|
||||
"MISO": 36,
|
||||
"MOSI": 15,
|
||||
"SCK": 13,
|
||||
"SCL": 33,
|
||||
"SDA": 32,
|
||||
},
|
||||
"magicbit": {
|
||||
"BLUE_LED": 17,
|
||||
"BUZZER": 25,
|
||||
"GREEN_LED": 16,
|
||||
"LDR": 36,
|
||||
"LED": 16,
|
||||
"LEFT_BUTTON": 35,
|
||||
"MOTOR1A": 27,
|
||||
"MOTOR1B": 18,
|
||||
"MOTOR2A": 16,
|
||||
"MOTOR2B": 17,
|
||||
"POT": 39,
|
||||
"RED_LED": 27,
|
||||
"RIGHT_PUTTON": 34,
|
||||
"YELLOW_LED": 18,
|
||||
},
|
||||
"mhetesp32devkit": {"LED": 2},
|
||||
"mhetesp32minikit": {"LED": 2},
|
||||
"microduino-core-esp32": {
|
||||
"A0": 12,
|
||||
"A1": 13,
|
||||
"A10": 25,
|
||||
"A11": 26,
|
||||
"A12": 27,
|
||||
"A13": 14,
|
||||
"A2": 15,
|
||||
"A3": 4,
|
||||
"A6": 38,
|
||||
"A7": 37,
|
||||
"A8": 32,
|
||||
"A9": 33,
|
||||
"D0": 3,
|
||||
"D1": 1,
|
||||
"D10": 5,
|
||||
"D11": 23,
|
||||
"D12": 19,
|
||||
"D13": 18,
|
||||
"D14": 12,
|
||||
"D15": 13,
|
||||
"D16": 15,
|
||||
"D17": 4,
|
||||
"D18": 22,
|
||||
"D19": 21,
|
||||
"D2": 16,
|
||||
"D20": 38,
|
||||
"D21": 37,
|
||||
"D3": 17,
|
||||
"D4": 32,
|
||||
"D5": 33,
|
||||
"D6": 25,
|
||||
"D7": 26,
|
||||
"D8": 27,
|
||||
"D9": 14,
|
||||
"SCL": 21,
|
||||
"SCL1": 13,
|
||||
"SDA": 22,
|
||||
"SDA1": 12,
|
||||
},
|
||||
"nano32": {"BUTTON": 0, "LED": 16},
|
||||
"nina_w10": {
|
||||
"D0": 3,
|
||||
"D1": 1,
|
||||
"D10": 5,
|
||||
"D11": 19,
|
||||
"D12": 23,
|
||||
"D13": 18,
|
||||
"D14": 13,
|
||||
"D15": 12,
|
||||
"D16": 32,
|
||||
"D17": 33,
|
||||
"D18": 21,
|
||||
"D19": 34,
|
||||
"D2": 26,
|
||||
"D20": 36,
|
||||
"D21": 39,
|
||||
"D3": 25,
|
||||
"D4": 35,
|
||||
"D5": 27,
|
||||
"D6": 22,
|
||||
"D7": 0,
|
||||
"D8": 15,
|
||||
"D9": 14,
|
||||
"LED_BLUE": 21,
|
||||
"LED_GREEN": 33,
|
||||
"LED_RED": 23,
|
||||
"SCL": 13,
|
||||
"SDA": 12,
|
||||
"SW1": 33,
|
||||
"SW2": 27,
|
||||
},
|
||||
"node32s": {},
|
||||
"nodemcu-32s": {"BUTTON": 0, "LED": 2},
|
||||
"odroid_esp32": {"ADC1": 35, "ADC2": 36, "LED": 2, "SCL": 4, "SDA": 15, "SS": 22},
|
||||
"onehorse32dev": {"A1": 37, "A2": 38, "BUTTON": 0, "LED": 5},
|
||||
"oroca_edubot": {
|
||||
"A0": 34,
|
||||
"A1": 39,
|
||||
"A2": 36,
|
||||
"A3": 33,
|
||||
"D0": 4,
|
||||
"D1": 16,
|
||||
"D2": 17,
|
||||
"D3": 22,
|
||||
"D4": 23,
|
||||
"D5": 5,
|
||||
"D6": 18,
|
||||
"D7": 19,
|
||||
"D8": 33,
|
||||
"LED": 13,
|
||||
"MOSI": 18,
|
||||
"RX": 16,
|
||||
"SCK": 5,
|
||||
"SDA": 23,
|
||||
"SS": 2,
|
||||
"TX": 17,
|
||||
"VBAT": 35,
|
||||
},
|
||||
"pico32": {},
|
||||
"pocket_32": {"LED": 16},
|
||||
"pycom_gpy": {
|
||||
"A1": 37,
|
||||
"A2": 38,
|
||||
"LED": 0,
|
||||
"MISO": 37,
|
||||
"MOSI": 22,
|
||||
"SCK": 13,
|
||||
"SCL": 13,
|
||||
"SDA": 12,
|
||||
"SS": 17,
|
||||
},
|
||||
"quantum": {},
|
||||
"sparkfun_lora_gateway_1-channel": {"MISO": 12, "MOSI": 13, "SCK": 14, "SS": 16},
|
||||
"tinypico": {},
|
||||
"ttgo-lora32-v1": {
|
||||
"A1": 37,
|
||||
"A2": 38,
|
||||
"BUTTON": 0,
|
||||
"LED": 2,
|
||||
"MOSI": 27,
|
||||
"SCK": 5,
|
||||
"SS": 18,
|
||||
},
|
||||
"ttgo-t-beam": {"BUTTON": 39, "LED": 14, "MOSI": 27, "SCK": 5, "SS": 18},
|
||||
"ttgo-t-watch": {"BUTTON": 36, "MISO": 2, "MOSI": 15, "SCK": 14, "SS": 13},
|
||||
"ttgo-t1": {"LED": 22, "MISO": 2, "MOSI": 15, "SCK": 14, "SCL": 23, "SS": 13},
|
||||
"ttgo-t7-v13-mini32": {"LED": 22},
|
||||
"ttgo-t7-v14-mini32": {"LED": 19},
|
||||
"turta_iot_node": {},
|
||||
"vintlabs-devkit-v1": {
|
||||
"LED": 2,
|
||||
"PWM0": 12,
|
||||
"PWM1": 13,
|
||||
"PWM2": 14,
|
||||
"PWM3": 15,
|
||||
"PWM4": 16,
|
||||
"PWM5": 17,
|
||||
"PWM6": 18,
|
||||
"PWM7": 19,
|
||||
},
|
||||
"wemos_d1_mini32": {
|
||||
"D0": 26,
|
||||
"D1": 22,
|
||||
"D2": 21,
|
||||
"D3": 17,
|
||||
"D4": 16,
|
||||
"D5": 18,
|
||||
"D6": 19,
|
||||
"D7": 23,
|
||||
"D8": 5,
|
||||
"LED": 2,
|
||||
"RXD": 3,
|
||||
"TXD": 1,
|
||||
"_VBAT": 35,
|
||||
},
|
||||
"wemosbat": {"LED": 16},
|
||||
"wesp32": {"MISO": 32, "SCL": 4, "SDA": 15},
|
||||
"widora-air": {
|
||||
"A1": 39,
|
||||
"A2": 35,
|
||||
"A3": 25,
|
||||
"A4": 26,
|
||||
"A5": 14,
|
||||
"A6": 12,
|
||||
"A7": 15,
|
||||
"A8": 13,
|
||||
"A9": 2,
|
||||
"BUTTON": 0,
|
||||
"D0": 19,
|
||||
"D1": 23,
|
||||
"D2": 18,
|
||||
"D3": 17,
|
||||
"D4": 16,
|
||||
"D5": 5,
|
||||
"D6": 4,
|
||||
"LED": 25,
|
||||
"MISO": 17,
|
||||
"MOSI": 16,
|
||||
"SCL": 19,
|
||||
"SDA": 23,
|
||||
"T0": 19,
|
||||
"T1": 23,
|
||||
"T2": 18,
|
||||
"T3": 17,
|
||||
"T4": 16,
|
||||
"T5": 5,
|
||||
"T6": 4,
|
||||
},
|
||||
"xinabox_cw02": {"LED": 27},
|
||||
}
|
||||
|
||||
ESP32_C3_BASE_PINS = {
|
||||
"TX": 21,
|
||||
"RX": 20,
|
||||
"ADC1_0": 0,
|
||||
"ADC1_1": 1,
|
||||
"ADC1_2": 2,
|
||||
"ADC1_3": 3,
|
||||
"ADC1_4": 4,
|
||||
"ADC2_0": 5,
|
||||
}
|
||||
|
||||
ESP32_C3_BOARD_PINS = {
|
||||
"esp32-c3-devkitm-1": {"LED": 8},
|
||||
"esp32-c3-devkitc-02": "esp32-c3-devkitm-1",
|
||||
}
|
||||
@@ -19,6 +19,7 @@ from esphome.cpp_generator import ( # noqa
|
||||
Statement,
|
||||
LineComment,
|
||||
progmem_array,
|
||||
static_const_array,
|
||||
statement,
|
||||
variable,
|
||||
new_variable,
|
||||
@@ -59,6 +60,7 @@ from esphome.cpp_types import ( # noqa
|
||||
uint8,
|
||||
uint16,
|
||||
uint32,
|
||||
uint64,
|
||||
int32,
|
||||
const_char_ptr,
|
||||
NAN,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace a4988 {
|
||||
|
||||
static const char *TAG = "a4988.stepper";
|
||||
static const char *const TAG = "a4988.stepper";
|
||||
|
||||
void A4988::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up A4988...");
|
||||
|
||||
@@ -18,16 +18,16 @@ CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend(
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield stepper.register_stepper(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await stepper.register_stepper(var, config)
|
||||
|
||||
step_pin = yield cg.gpio_pin_expression(config[CONF_STEP_PIN])
|
||||
step_pin = await cg.gpio_pin_expression(config[CONF_STEP_PIN])
|
||||
cg.add(var.set_step_pin(step_pin))
|
||||
dir_pin = yield cg.gpio_pin_expression(config[CONF_DIR_PIN])
|
||||
dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
|
||||
cg.add(var.set_dir_pin(dir_pin))
|
||||
|
||||
if CONF_SLEEP_PIN in config:
|
||||
sleep_pin = yield cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
|
||||
sleep_pin = await cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
|
||||
cg.add(var.set_sleep_pin(sleep_pin))
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
namespace esphome {
|
||||
namespace ac_dimmer {
|
||||
|
||||
static const char *TAG = "ac_dimmer";
|
||||
static const char *const TAG = "ac_dimmer";
|
||||
|
||||
// Global array to store dimmer objects
|
||||
static AcDimmerDataStore *all_dimmers[32];
|
||||
static AcDimmerDataStore *all_dimmers[32]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
/// Time in microseconds the gate should be held high
|
||||
/// 10µs should be long enough for most triacs
|
||||
/// For reference: BT136 datasheet says 2µs nominal (page 7)
|
||||
static uint32_t GATE_ENABLE_TIME = 10;
|
||||
static const uint32_t GATE_ENABLE_TIME = 10;
|
||||
|
||||
/// Function called from timer interrupt
|
||||
/// Input is current time in microseconds (micros())
|
||||
@@ -125,7 +125,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
|
||||
// Attaching pin interrupts on the same pin will override the previous interupt
|
||||
// Attaching pin interrupts on the same pin will override the previous interrupt
|
||||
// However, the user expects that multiple dimmers sharing the same ZC pin will work.
|
||||
// We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
|
||||
// if any of them are using the same ZC pin, and also trigger the interrupt for *them*.
|
||||
|
||||
@@ -32,18 +32,18 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
# override default min power to 10%
|
||||
if CONF_MIN_POWER not in config:
|
||||
config[CONF_MIN_POWER] = 0.1
|
||||
yield output.register_output(var, config)
|
||||
await output.register_output(var, config)
|
||||
|
||||
pin = yield cg.gpio_pin_expression(config[CONF_GATE_PIN])
|
||||
pin = await cg.gpio_pin_expression(config[CONF_GATE_PIN])
|
||||
cg.add(var.set_gate_pin(pin))
|
||||
pin = yield cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN])
|
||||
pin = await cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN])
|
||||
cg.add(var.set_zero_cross_pin(pin))
|
||||
cg.add(var.set_init_with_half_cycle(config[CONF_INIT_WITH_HALF_CYCLE]))
|
||||
cg.add(var.set_method(config[CONF_METHOD]))
|
||||
|
||||
@@ -21,8 +21,7 @@ CONFIG_SCHEMA = cv.Schema({})
|
||||
"Adalight",
|
||||
{cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)},
|
||||
)
|
||||
def adalight_light_effect_to_code(config, effect_id):
|
||||
async def adalight_light_effect_to_code(config, effect_id):
|
||||
effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
|
||||
yield uart.register_uart_device(effect, config)
|
||||
|
||||
yield effect
|
||||
await uart.register_uart_device(effect, config)
|
||||
return effect
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace adalight {
|
||||
|
||||
static const char *TAG = "adalight_light_effect";
|
||||
static const char *const TAG = "adalight_light_effect";
|
||||
|
||||
static const uint32_t ADALIGHT_ACK_INTERVAL = 1000;
|
||||
static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000;
|
||||
@@ -42,7 +42,7 @@ void AdalightLightEffect::reset_frame_(light::AddressableLight &it) {
|
||||
|
||||
void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) {
|
||||
for (int led = it.size(); led-- > 0;) {
|
||||
it[led].set(COLOR_BLACK);
|
||||
it[led].set(Color::BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,50 @@ ADC_MODE(ADC_VCC)
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
|
||||
static const char *TAG = "adc";
|
||||
static const char *const TAG = "adc";
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuation_ = attenuation; }
|
||||
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
|
||||
|
||||
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
switch (pin) {
|
||||
case 36:
|
||||
return ADC1_CHANNEL_0;
|
||||
case 37:
|
||||
return ADC1_CHANNEL_1;
|
||||
case 38:
|
||||
return ADC1_CHANNEL_2;
|
||||
case 39:
|
||||
return ADC1_CHANNEL_3;
|
||||
case 32:
|
||||
return ADC1_CHANNEL_4;
|
||||
case 33:
|
||||
return ADC1_CHANNEL_5;
|
||||
case 34:
|
||||
return ADC1_CHANNEL_6;
|
||||
case 35:
|
||||
return ADC1_CHANNEL_7;
|
||||
default:
|
||||
return ADC1_CHANNEL_MAX;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
|
||||
switch (pin) {
|
||||
case 0:
|
||||
return ADC1_CHANNEL_0;
|
||||
case 1:
|
||||
return ADC1_CHANNEL_1;
|
||||
case 2:
|
||||
return ADC1_CHANNEL_2;
|
||||
case 3:
|
||||
return ADC1_CHANNEL_3;
|
||||
case 4:
|
||||
return ADC1_CHANNEL_4;
|
||||
default:
|
||||
return ADC1_CHANNEL_MAX;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void ADCSensor::setup() {
|
||||
@@ -21,7 +61,11 @@ void ADCSensor::setup() {
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
analogSetPinAttenuation(this->pin_, this->attenuation_);
|
||||
adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_);
|
||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2
|
||||
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
void ADCSensor::dump_config() {
|
||||
@@ -36,18 +80,20 @@ void ADCSensor::dump_config() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
|
||||
switch (this->attenuation_) {
|
||||
case ADC_0db:
|
||||
case ADC_ATTEN_DB_0:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
|
||||
break;
|
||||
case ADC_2_5db:
|
||||
case ADC_ATTEN_DB_2_5:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
|
||||
break;
|
||||
case ADC_6db:
|
||||
case ADC_ATTEN_DB_6:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
|
||||
break;
|
||||
case ADC_11db:
|
||||
case ADC_ATTEN_DB_11:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
|
||||
break;
|
||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
@@ -60,21 +106,43 @@ void ADCSensor::update() {
|
||||
}
|
||||
float ADCSensor::sample() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
float value_v = analogRead(this->pin_) / 4095.0f; // NOLINT
|
||||
int raw = adc1_get_raw(gpio_to_adc1(pin_));
|
||||
float value_v = raw / 4095.0f;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
switch (this->attenuation_) {
|
||||
case ADC_0db:
|
||||
case ADC_ATTEN_DB_0:
|
||||
value_v *= 1.1;
|
||||
break;
|
||||
case ADC_2_5db:
|
||||
case ADC_ATTEN_DB_2_5:
|
||||
value_v *= 1.5;
|
||||
break;
|
||||
case ADC_6db:
|
||||
case ADC_ATTEN_DB_6:
|
||||
value_v *= 2.2;
|
||||
break;
|
||||
case ADC_11db:
|
||||
case ADC_ATTEN_DB_11:
|
||||
value_v *= 3.9;
|
||||
break;
|
||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||
break;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
|
||||
switch (this->attenuation_) {
|
||||
case ADC_ATTEN_DB_0:
|
||||
value_v *= 0.84;
|
||||
break;
|
||||
case ADC_ATTEN_DB_2_5:
|
||||
value_v *= 1.13;
|
||||
break;
|
||||
case ADC_ATTEN_DB_6:
|
||||
value_v *= 1.56;
|
||||
break;
|
||||
case ADC_ATTEN_DB_11:
|
||||
value_v *= 3.0;
|
||||
break;
|
||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return value_v;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "driver/adc.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
|
||||
@@ -13,7 +17,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
||||
public:
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
/// Set the attenuation for this pin. Only available on the ESP32.
|
||||
void set_attenuation(adc_attenuation_t attenuation);
|
||||
void set_attenuation(adc_atten_t attenuation);
|
||||
#endif
|
||||
|
||||
/// Update adc values.
|
||||
@@ -34,7 +38,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
||||
uint8_t pin_;
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
adc_attenuation_t attenuation_{ADC_0db};
|
||||
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_PIN,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_VOLT,
|
||||
)
|
||||
|
||||
@@ -15,10 +15,10 @@ from esphome.const import (
|
||||
AUTO_LOAD = ["voltage_sampler"]
|
||||
|
||||
ATTENUATION_MODES = {
|
||||
"0db": cg.global_ns.ADC_0db,
|
||||
"2.5db": cg.global_ns.ADC_2_5db,
|
||||
"6db": cg.global_ns.ADC_6db,
|
||||
"11db": cg.global_ns.ADC_11db,
|
||||
"0db": cg.global_ns.ADC_ATTEN_DB_0,
|
||||
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
|
||||
"6db": cg.global_ns.ADC_ATTEN_DB_6,
|
||||
"11db": cg.global_ns.ADC_ATTEN_DB_11,
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,12 @@ ADCSensor = adc_ns.class_(
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE)
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ADCSensor),
|
||||
@@ -49,10 +54,10 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield sensor.register_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
if config[CONF_PIN] == "VCC":
|
||||
cg.add_define("USE_ADC_SENSOR_VCC")
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace addressable_light {
|
||||
|
||||
static const char* TAG = "addressable_light.display";
|
||||
static const char *const TAG = "addressable_light.display";
|
||||
|
||||
int AddressableLightDisplay::get_width_internal() { return this->width_; }
|
||||
int AddressableLightDisplay::get_height_internal() { return this->height_; }
|
||||
@@ -24,7 +24,7 @@ void AddressableLightDisplay::update() {
|
||||
void AddressableLightDisplay::display() {
|
||||
bool dirty = false;
|
||||
uint8_t old_r, old_g, old_b, old_w;
|
||||
Color* c;
|
||||
Color *c;
|
||||
|
||||
for (uint32_t offset = 0; offset < this->addressable_light_buffer_.size(); offset++) {
|
||||
c = &(this->addressable_light_buffer_[offset]);
|
||||
|
||||
@@ -38,18 +38,18 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
wrapped_light = yield cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID])
|
||||
wrapped_light = await cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID])
|
||||
cg.add(var.set_width(config[CONF_WIDTH]))
|
||||
cg.add(var.set_height(config[CONF_HEIGHT]))
|
||||
cg.add(var.set_light(wrapped_light))
|
||||
|
||||
yield cg.register_component(var, config)
|
||||
yield display.register_display(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await display.register_display(var, config)
|
||||
|
||||
if CONF_PIXEL_MAPPER in config:
|
||||
pixel_mapper_template_ = yield cg.process_lambda(
|
||||
pixel_mapper_template_ = await cg.process_lambda(
|
||||
config[CONF_PIXEL_MAPPER],
|
||||
[(int, "x"), (int, "y")],
|
||||
return_type=cg.int_,
|
||||
@@ -57,7 +57,7 @@ def to_code(config):
|
||||
cg.add(var.set_pixel_mapper(pixel_mapper_template_))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = yield cg.process_lambda(
|
||||
lambda_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
|
||||
)
|
||||
cg.add(var.set_writer(lambda_))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace ade7953 {
|
||||
|
||||
static const char *TAG = "ade7953";
|
||||
static const char *const TAG = "ade7953";
|
||||
|
||||
void ADE7953::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "ADE7953:");
|
||||
@@ -21,8 +21,8 @@ void ADE7953::dump_config() {
|
||||
}
|
||||
|
||||
#define ADE_PUBLISH_(name, factor) \
|
||||
if (name && this->name##_sensor_) { \
|
||||
float value = *name / factor; \
|
||||
if ((name) && this->name##_sensor_) { \
|
||||
float value = *(name) / (factor); \
|
||||
this->name##_sensor_->publish_state(value); \
|
||||
}
|
||||
#define ADE_PUBLISH(name, factor) ADE_PUBLISH_(name, factor)
|
||||
|
||||
@@ -8,7 +8,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_VOLT,
|
||||
UNIT_AMPERE,
|
||||
UNIT_WATT,
|
||||
@@ -31,19 +31,34 @@ CONFIG_SCHEMA = (
|
||||
cv.GenerateID(): cv.declare_id(ADE7953),
|
||||
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
|
||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(
|
||||
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
|
||||
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
|
||||
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
|
||||
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -52,10 +67,10 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_IRQ_PIN in config:
|
||||
cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
|
||||
@@ -70,5 +85,5 @@ def to_code(config):
|
||||
if key not in config:
|
||||
continue
|
||||
conf = config[key]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(getattr(var, f"set_{key}_sensor")(sens))
|
||||
|
||||
@@ -23,9 +23,9 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace ads1115 {
|
||||
|
||||
static const char *TAG = "ads1115";
|
||||
static const char *const TAG = "ads1115";
|
||||
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
|
||||
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
|
||||
|
||||
@@ -64,11 +64,6 @@ void ADS1115Component::setup() {
|
||||
return;
|
||||
}
|
||||
this->prev_config_ = config;
|
||||
|
||||
for (auto *sensor : this->sensors_) {
|
||||
this->set_interval(sensor->get_name(), sensor->update_interval(),
|
||||
[this, sensor] { this->request_measurement(sensor); });
|
||||
}
|
||||
}
|
||||
void ADS1115Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
|
||||
@@ -107,17 +102,22 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
|
||||
}
|
||||
this->prev_config_ = config;
|
||||
|
||||
// about 1.6 ms with 860 samples per second
|
||||
// about 1.2 ms with 860 samples per second
|
||||
delay(2);
|
||||
|
||||
uint32_t start = millis();
|
||||
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
|
||||
if (millis() - start > 100) {
|
||||
ESP_LOGW(TAG, "Reading ADS1115 timed out");
|
||||
this->status_set_warning();
|
||||
return NAN;
|
||||
// in continuous mode, conversion will always be running, rely on the delay
|
||||
// to ensure conversion is taking place with the correct settings
|
||||
// can we use the rdy pin to trigger when a conversion is done?
|
||||
if (!this->continuous_mode_) {
|
||||
uint32_t start = millis();
|
||||
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
|
||||
if (millis() - start > 100) {
|
||||
ESP_LOGW(TAG, "Reading ADS1115 timed out");
|
||||
this->status_set_warning();
|
||||
return NAN;
|
||||
}
|
||||
yield();
|
||||
}
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from esphome.const import (
|
||||
CONF_GAIN,
|
||||
CONF_MULTIPLEXER,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_VOLT,
|
||||
CONF_ID,
|
||||
)
|
||||
@@ -51,7 +51,12 @@ ADS1115Sensor = ads1115_ns.class_(
|
||||
|
||||
CONF_ADS1115_ID = "ads1115_id"
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE)
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ADS1115Sensor),
|
||||
@@ -64,11 +69,11 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
paren = yield cg.get_variable(config[CONF_ADS1115_ID])
|
||||
async def to_code(config):
|
||||
paren = await cg.get_variable(config[CONF_ADS1115_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], paren)
|
||||
yield sensor.register_sensor(var, config)
|
||||
yield cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
|
||||
cg.add(var.set_gain(config[CONF_GAIN]))
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
//
|
||||
// According to the datasheet, the component is supposed to respond in more than 75ms. In fact, it can answer almost
|
||||
// immediately for temperature. But for humidity, it takes >90ms to get a valid data. From experience, we have best
|
||||
// results making successive requests; the current implementation make 3 attemps with a delay of 30ms each time.
|
||||
// results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time.
|
||||
|
||||
#include "aht10.h"
|
||||
#include "esphome/core/log.h"
|
||||
@@ -18,12 +18,12 @@
|
||||
namespace esphome {
|
||||
namespace aht10 {
|
||||
|
||||
static const char *TAG = "aht10";
|
||||
static const char *const TAG = "aht10";
|
||||
static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1};
|
||||
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
|
||||
static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement
|
||||
static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms
|
||||
static const uint8_t AHT10_ATTEMPS = 3; // safety margin, normally 3 attemps are enough: 3*30=90ms
|
||||
static const uint8_t AHT10_ATTEMPTS = 3; // safety margin, normally 3 attempts are enough: 3*30=90ms
|
||||
|
||||
void AHT10Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up AHT10...");
|
||||
@@ -58,8 +58,9 @@ void AHT10Component::update() {
|
||||
uint8_t delay = AHT10_DEFAULT_DELAY;
|
||||
if (this->humidity_sensor_ != nullptr)
|
||||
delay = AHT10_HUMIDITY_DELAY;
|
||||
for (int i = 0; i < AHT10_ATTEMPS; ++i) {
|
||||
ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis());
|
||||
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
|
||||
ESP_LOGVV(TAG, "Attempt %u at %6ld", i, millis());
|
||||
delay_microseconds_accurate(4);
|
||||
if (!this->read_bytes(0, data, 6, delay)) {
|
||||
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
||||
} else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
|
||||
|
||||
@@ -7,7 +7,7 @@ from esphome.const import (
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_PERCENT,
|
||||
)
|
||||
@@ -22,10 +22,16 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(AHT10Component),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||
UNIT_PERCENT, ICON_EMPTY, 2, DEVICE_CLASS_HUMIDITY
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -34,15 +40,15 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
|
||||
if CONF_HUMIDITY in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
cg.add(var.set_humidity_sensor(sens))
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace esphome {
|
||||
namespace am2320 {
|
||||
|
||||
static const char *TAG = "am2320";
|
||||
static const char *const TAG = "am2320";
|
||||
|
||||
// ---=== Calc CRC16 ===---
|
||||
uint16_t crc_16(uint8_t *ptr, uint8_t length) {
|
||||
|
||||
@@ -7,8 +7,8 @@ from esphome.const import (
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
ICON_EMPTY,
|
||||
UNIT_PERCENT,
|
||||
)
|
||||
|
||||
@@ -24,10 +24,16 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(AM2320Component),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||
UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -36,15 +42,15 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
|
||||
if CONF_HUMIDITY in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
cg.add(var.set_humidity_sensor(sens))
|
||||
|
||||
115
esphome/components/am43/am43.cpp
Normal file
115
esphome/components/am43/am43.cpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#include "am43.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace am43 {
|
||||
|
||||
static const char *TAG = "am43";
|
||||
|
||||
void Am43::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "AM43");
|
||||
LOG_SENSOR(" ", "Battery", this->battery_);
|
||||
LOG_SENSOR(" ", "Illuminance", this->illuminance_);
|
||||
}
|
||||
|
||||
void Am43::setup() {
|
||||
this->encoder_ = new Am43Encoder();
|
||||
this->decoder_ = new Am43Decoder();
|
||||
this->logged_in_ = false;
|
||||
this->last_battery_update_ = 0;
|
||||
this->current_sensor_ = 0;
|
||||
}
|
||||
|
||||
void Am43::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_OPEN_EVT: {
|
||||
this->logged_in_ = false;
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
this->logged_in_ = false;
|
||||
this->node_state = espbt::ClientState::Idle;
|
||||
if (this->battery_ != nullptr)
|
||||
this->battery_->publish_state(NAN);
|
||||
if (this->illuminance_ != nullptr)
|
||||
this->illuminance_->publish_state(NAN);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
auto chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
|
||||
if (chr == nullptr) {
|
||||
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
|
||||
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.",
|
||||
this->parent_->address_str().c_str());
|
||||
} else {
|
||||
ESP_LOGE(TAG, "[%s] No control service found at device, not an AM43..?",
|
||||
this->parent_->address_str().c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
this->char_handle_ = chr->handle;
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
this->update();
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (param->notify.handle != this->char_handle_)
|
||||
break;
|
||||
this->decoder_->decode(param->notify.value, param->notify.value_len);
|
||||
|
||||
if (this->battery_ != nullptr && this->decoder_->has_battery_level() &&
|
||||
millis() - this->last_battery_update_ > 10000) {
|
||||
this->battery_->publish_state(this->decoder_->battery_level_);
|
||||
this->last_battery_update_ = millis();
|
||||
}
|
||||
|
||||
if (this->illuminance_ != nullptr && this->decoder_->has_light_level()) {
|
||||
this->illuminance_->publish_state(this->decoder_->light_level_);
|
||||
}
|
||||
|
||||
if (this->current_sensor_ > 0) {
|
||||
if (this->illuminance_ != nullptr) {
|
||||
auto packet = this->encoder_->get_light_level_request();
|
||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
|
||||
ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
|
||||
status);
|
||||
}
|
||||
this->current_sensor_ = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Am43::update() {
|
||||
if (this->node_state != espbt::ClientState::Established) {
|
||||
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->parent_->address_str().c_str());
|
||||
return;
|
||||
}
|
||||
if (this->current_sensor_ == 0) {
|
||||
if (this->battery_ != nullptr) {
|
||||
auto packet = this->encoder_->get_battery_level_request();
|
||||
auto status =
|
||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
||||
}
|
||||
this->current_sensor_++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace am43
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
45
esphome/components/am43/am43.h
Normal file
45
esphome/components/am43/am43.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/am43/am43_base.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace am43 {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
class Am43 : public esphome::ble_client::BLEClientNode, public PollingComponent {
|
||||
public:
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
void set_battery(sensor::Sensor *battery) { battery_ = battery; }
|
||||
void set_illuminance(sensor::Sensor *illuminance) { illuminance_ = illuminance; }
|
||||
|
||||
protected:
|
||||
uint16_t char_handle_;
|
||||
Am43Encoder *encoder_;
|
||||
Am43Decoder *decoder_;
|
||||
bool logged_in_;
|
||||
sensor::Sensor *battery_{nullptr};
|
||||
sensor::Sensor *illuminance_{nullptr};
|
||||
uint8_t current_sensor_;
|
||||
// The AM43 often gets into a state where it spams loads of battery update
|
||||
// notifications. Here we will limit to no more than every 10s.
|
||||
uint8_t last_battery_update_;
|
||||
};
|
||||
|
||||
} // namespace am43
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
142
esphome/components/am43/am43_base.cpp
Normal file
142
esphome/components/am43/am43_base.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "am43_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace am43 {
|
||||
|
||||
const uint8_t START_PACKET[5] = {0x00, 0xff, 0x00, 0x00, 0x9a};
|
||||
|
||||
std::string pkt_to_hex(const uint8_t *data, uint16_t len) {
|
||||
char buf[64];
|
||||
memset(buf, 0, 64);
|
||||
for (int i = 0; i < len; i++)
|
||||
sprintf(&buf[i * 2], "%02x", data[i]);
|
||||
std::string ret = buf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
Am43Packet *Am43Encoder::get_battery_level_request() {
|
||||
uint8_t data = 0x1;
|
||||
return this->encode_(0xA2, &data, 1);
|
||||
}
|
||||
|
||||
Am43Packet *Am43Encoder::get_light_level_request() {
|
||||
uint8_t data = 0x1;
|
||||
return this->encode_(0xAA, &data, 1);
|
||||
}
|
||||
|
||||
Am43Packet *Am43Encoder::get_position_request() {
|
||||
uint8_t data = 0x1;
|
||||
return this->encode_(CMD_GET_POSITION, &data, 1);
|
||||
}
|
||||
|
||||
Am43Packet *Am43Encoder::get_send_pin_request(uint16_t pin) {
|
||||
uint8_t data[2];
|
||||
data[0] = (pin & 0xFF00) >> 8;
|
||||
data[1] = pin & 0xFF;
|
||||
return this->encode_(CMD_SEND_PIN, data, 2);
|
||||
}
|
||||
|
||||
Am43Packet *Am43Encoder::get_open_request() {
|
||||
uint8_t data = 0xDD;
|
||||
return this->encode_(CMD_SET_STATE, &data, 1);
|
||||
}
|
||||
|
||||
Am43Packet *Am43Encoder::get_close_request() {
|
||||
uint8_t data = 0xEE;
|
||||
return this->encode_(CMD_SET_STATE, &data, 1);
|
||||
}
|
||||
|
||||
Am43Packet *Am43Encoder::get_stop_request() {
|
||||
uint8_t data = 0xCC;
|
||||
return this->encode_(CMD_SET_STATE, &data, 1);
|
||||
}
|
||||
|
||||
Am43Packet *Am43Encoder::get_set_position_request(uint8_t position) {
|
||||
return this->encode_(CMD_SET_POSITION, &position, 1);
|
||||
}
|
||||
|
||||
void Am43Encoder::checksum_() {
|
||||
uint8_t checksum = 0;
|
||||
int i = 0;
|
||||
for (i = 0; i < this->packet_.length; i++)
|
||||
checksum = checksum ^ this->packet_.data[i];
|
||||
this->packet_.data[i] = checksum ^ 0xff;
|
||||
this->packet_.length++;
|
||||
}
|
||||
|
||||
Am43Packet *Am43Encoder::encode_(uint8_t command, uint8_t *data, uint8_t length) {
|
||||
memcpy(this->packet_.data, START_PACKET, 5);
|
||||
this->packet_.data[5] = command;
|
||||
this->packet_.data[6] = length;
|
||||
memcpy(&this->packet_.data[7], data, length);
|
||||
this->packet_.length = length + 7;
|
||||
this->checksum_();
|
||||
ESP_LOGV("am43", "ENC(%d): 0x%s", packet_.length, pkt_to_hex(packet_.data, packet_.length).c_str());
|
||||
return &this->packet_;
|
||||
}
|
||||
|
||||
#define VERIFY_MIN_LENGTH(x) \
|
||||
if (length < (x)) \
|
||||
return;
|
||||
|
||||
void Am43Decoder::decode(const uint8_t *data, uint16_t length) {
|
||||
this->has_battery_level_ = false;
|
||||
this->has_light_level_ = false;
|
||||
this->has_set_position_response_ = false;
|
||||
this->has_set_state_response_ = false;
|
||||
this->has_position_ = false;
|
||||
this->has_pin_response_ = false;
|
||||
ESP_LOGV("am43", "DEC(%d): 0x%s", length, pkt_to_hex(data, length).c_str());
|
||||
|
||||
if (length < 2 || data[0] != 0x9a)
|
||||
return;
|
||||
switch (data[1]) {
|
||||
case CMD_GET_BATTERY_LEVEL: {
|
||||
VERIFY_MIN_LENGTH(8);
|
||||
this->battery_level_ = data[7];
|
||||
this->has_battery_level_ = true;
|
||||
break;
|
||||
}
|
||||
case CMD_GET_LIGHT_LEVEL: {
|
||||
VERIFY_MIN_LENGTH(5);
|
||||
this->light_level_ = 100 * ((float) data[4] / 9);
|
||||
this->has_light_level_ = true;
|
||||
break;
|
||||
}
|
||||
case CMD_GET_POSITION: {
|
||||
VERIFY_MIN_LENGTH(6);
|
||||
this->position_ = data[5];
|
||||
this->has_position_ = true;
|
||||
break;
|
||||
}
|
||||
case CMD_NOTIFY_POSITION: {
|
||||
VERIFY_MIN_LENGTH(5);
|
||||
this->position_ = data[4];
|
||||
this->has_position_ = true;
|
||||
break;
|
||||
}
|
||||
case CMD_SEND_PIN: {
|
||||
VERIFY_MIN_LENGTH(4);
|
||||
this->pin_ok_ = data[3] == RESPONSE_ACK;
|
||||
this->has_pin_response_ = true;
|
||||
break;
|
||||
}
|
||||
case CMD_SET_POSITION: {
|
||||
VERIFY_MIN_LENGTH(4);
|
||||
this->set_position_ok_ = data[3] == RESPONSE_ACK;
|
||||
this->has_set_position_response_ = true;
|
||||
break;
|
||||
}
|
||||
case CMD_SET_STATE: {
|
||||
VERIFY_MIN_LENGTH(4);
|
||||
this->set_state_ok_ = data[3] == RESPONSE_ACK;
|
||||
this->has_set_state_response_ = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace am43
|
||||
} // namespace esphome
|
||||
78
esphome/components/am43/am43_base.h
Normal file
78
esphome/components/am43/am43_base.h
Normal file
@@ -0,0 +1,78 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace am43 {
|
||||
|
||||
static const uint16_t AM43_SERVICE_UUID = 0xFE50;
|
||||
static const uint16_t AM43_CHARACTERISTIC_UUID = 0xFE51;
|
||||
//
|
||||
// Tuya identifiers, only to detect and warn users as they are incompatible.
|
||||
static const uint16_t AM43_TUYA_SERVICE_UUID = 0x1910;
|
||||
static const uint16_t AM43_TUYA_CHARACTERISTIC_UUID = 0x2b11;
|
||||
|
||||
struct Am43Packet {
|
||||
uint8_t length;
|
||||
uint8_t data[24];
|
||||
};
|
||||
|
||||
static const uint8_t CMD_GET_BATTERY_LEVEL = 0xA2;
|
||||
static const uint8_t CMD_GET_LIGHT_LEVEL = 0xAA;
|
||||
static const uint8_t CMD_GET_POSITION = 0xA7;
|
||||
static const uint8_t CMD_SEND_PIN = 0x17;
|
||||
static const uint8_t CMD_SET_STATE = 0x0A;
|
||||
static const uint8_t CMD_SET_POSITION = 0x0D;
|
||||
static const uint8_t CMD_NOTIFY_POSITION = 0xA1;
|
||||
|
||||
static const uint8_t RESPONSE_ACK = 0x5A;
|
||||
static const uint8_t RESPONSE_NACK = 0xA5;
|
||||
|
||||
class Am43Encoder {
|
||||
public:
|
||||
Am43Packet *get_battery_level_request();
|
||||
Am43Packet *get_light_level_request();
|
||||
Am43Packet *get_position_request();
|
||||
Am43Packet *get_send_pin_request(uint16_t pin);
|
||||
Am43Packet *get_open_request();
|
||||
Am43Packet *get_close_request();
|
||||
Am43Packet *get_stop_request();
|
||||
Am43Packet *get_set_position_request(uint8_t position);
|
||||
|
||||
protected:
|
||||
void checksum_();
|
||||
Am43Packet *encode_(uint8_t command, uint8_t *data, uint8_t length);
|
||||
Am43Packet packet_;
|
||||
};
|
||||
|
||||
class Am43Decoder {
|
||||
public:
|
||||
void decode(const uint8_t *data, uint16_t length);
|
||||
bool has_battery_level() { return this->has_battery_level_; }
|
||||
bool has_light_level() { return this->has_light_level_; }
|
||||
bool has_set_position_response() { return this->has_set_position_response_; }
|
||||
bool has_set_state_response() { return this->has_set_state_response_; }
|
||||
bool has_position() { return this->has_position_; }
|
||||
bool has_pin_response() { return this->has_pin_response_; }
|
||||
|
||||
union {
|
||||
uint8_t position_;
|
||||
uint8_t battery_level_;
|
||||
float light_level_;
|
||||
uint8_t set_position_ok_;
|
||||
uint8_t set_state_ok_;
|
||||
uint8_t pin_ok_;
|
||||
};
|
||||
|
||||
protected:
|
||||
bool has_battery_level_;
|
||||
bool has_light_level_;
|
||||
bool has_set_position_response_;
|
||||
bool has_set_state_response_;
|
||||
bool has_position_;
|
||||
bool has_pin_response_;
|
||||
};
|
||||
|
||||
} // namespace am43
|
||||
} // namespace esphome
|
||||
36
esphome/components/am43/cover/__init__.py
Normal file
36
esphome/components/am43/cover/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import cover, ble_client
|
||||
from esphome.const import CONF_ID, CONF_PIN
|
||||
|
||||
CODEOWNERS = ["@buxtronix"]
|
||||
DEPENDENCIES = ["ble_client"]
|
||||
AUTO_LOAD = ["am43"]
|
||||
|
||||
CONF_INVERT_POSITION = "invert_position"
|
||||
|
||||
am43_ns = cg.esphome_ns.namespace("am43")
|
||||
Am43Component = am43_ns.class_(
|
||||
"Am43Component", cover.Cover, ble_client.BLEClientNode, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cover.COVER_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Am43Component),
|
||||
cv.Optional(CONF_PIN, default=8888): cv.int_range(min=0, max=0xFFFF),
|
||||
cv.Optional(CONF_INVERT_POSITION, default=False): cv.boolean,
|
||||
}
|
||||
)
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
cg.add(var.set_pin(config[CONF_PIN]))
|
||||
cg.add(var.set_invert_position(config[CONF_INVERT_POSITION]))
|
||||
yield cg.register_component(var, config)
|
||||
yield cover.register_cover(var, config)
|
||||
yield ble_client.register_ble_node(var, config)
|
||||
149
esphome/components/am43/cover/am43_cover.cpp
Normal file
149
esphome/components/am43/cover/am43_cover.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
#include "am43_cover.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace am43 {
|
||||
|
||||
static const char *TAG = "am43_cover";
|
||||
|
||||
using namespace esphome::cover;
|
||||
|
||||
void Am43Component::dump_config() {
|
||||
LOG_COVER("", "AM43 Cover", this);
|
||||
ESP_LOGCONFIG(TAG, " Device Pin: %d", this->pin_);
|
||||
ESP_LOGCONFIG(TAG, " Invert Position: %d", (int) this->invert_position_);
|
||||
}
|
||||
|
||||
void Am43Component::setup() {
|
||||
this->position = COVER_OPEN;
|
||||
this->encoder_ = new Am43Encoder();
|
||||
this->decoder_ = new Am43Decoder();
|
||||
this->logged_in_ = false;
|
||||
}
|
||||
|
||||
void Am43Component::loop() {
|
||||
if (this->node_state == espbt::ClientState::Established && !this->logged_in_) {
|
||||
auto packet = this->encoder_->get_send_pin_request(this->pin_);
|
||||
auto status =
|
||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
ESP_LOGI(TAG, "[%s] Logging into AM43", this->get_name().c_str());
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] Error writing set_pin to device, error = %d", this->get_name().c_str(), status);
|
||||
else
|
||||
this->logged_in_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
CoverTraits Am43Component::get_traits() {
|
||||
auto traits = CoverTraits();
|
||||
traits.set_supports_position(true);
|
||||
traits.set_supports_tilt(false);
|
||||
traits.set_is_assumed_state(false);
|
||||
return traits;
|
||||
}
|
||||
|
||||
void Am43Component::control(const CoverCall &call) {
|
||||
if (this->node_state != espbt::ClientState::Established) {
|
||||
ESP_LOGW(TAG, "[%s] Cannot send cover control, not connected", this->get_name().c_str());
|
||||
return;
|
||||
}
|
||||
if (call.get_stop()) {
|
||||
auto packet = this->encoder_->get_stop_request();
|
||||
auto status =
|
||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] Error writing stop command to device, error = %d", this->get_name().c_str(), status);
|
||||
}
|
||||
if (call.get_position().has_value()) {
|
||||
auto pos = *call.get_position();
|
||||
|
||||
if (this->invert_position_)
|
||||
pos = 1 - pos;
|
||||
auto packet = this->encoder_->get_set_position_request(100 - (uint8_t)(pos * 100));
|
||||
auto status =
|
||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, packet->length,
|
||||
packet->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] Error writing set_position command to device, error = %d", this->get_name().c_str(), status);
|
||||
}
|
||||
}
|
||||
|
||||
void Am43Component::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
this->logged_in_ = false;
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
auto chr = this->parent_->get_characteristic(AM43_SERVICE_UUID, AM43_CHARACTERISTIC_UUID);
|
||||
if (chr == nullptr) {
|
||||
if (this->parent_->get_characteristic(AM43_TUYA_SERVICE_UUID, AM43_TUYA_CHARACTERISTIC_UUID) != nullptr) {
|
||||
ESP_LOGE(TAG, "[%s] Detected a Tuya AM43 which is not supported, sorry.", this->get_name().c_str());
|
||||
} else {
|
||||
ESP_LOGE(TAG, "[%s] No control service found at device, not an AM43..?", this->get_name().c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
this->char_handle_ = chr->handle;
|
||||
|
||||
auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle);
|
||||
if (status) {
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (param->notify.handle != this->char_handle_)
|
||||
break;
|
||||
this->decoder_->decode(param->notify.value, param->notify.value_len);
|
||||
|
||||
if (this->decoder_->has_position()) {
|
||||
this->position = ((float) this->decoder_->position_ / 100.0);
|
||||
if (!this->invert_position_)
|
||||
this->position = 1 - this->position;
|
||||
if (this->position > 0.97)
|
||||
this->position = 1.0;
|
||||
if (this->position < 0.02)
|
||||
this->position = 0.0;
|
||||
this->publish_state();
|
||||
}
|
||||
|
||||
if (this->decoder_->has_pin_response()) {
|
||||
if (this->decoder_->pin_ok_) {
|
||||
ESP_LOGI(TAG, "[%s] AM43 pin accepted.", this->get_name().c_str());
|
||||
auto packet = this->encoder_->get_position_request();
|
||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||
packet->length, packet->data, ESP_GATT_WRITE_TYPE_NO_RSP,
|
||||
ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] Error writing set_position to device, error = %d", this->get_name().c_str(), status);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "[%s] AM43 pin rejected!", this->get_name().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
if (this->decoder_->has_set_position_response() && !this->decoder_->set_position_ok_)
|
||||
ESP_LOGW(TAG, "[%s] Got nack after sending set_position. Bad pin?", this->get_name().c_str());
|
||||
|
||||
if (this->decoder_->has_set_state_response() && !this->decoder_->set_state_ok_)
|
||||
ESP_LOGW(TAG, "[%s] Got nack after sending set_state. Bad pin?", this->get_name().c_str());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace am43
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
45
esphome/components/am43/cover/am43_cover.h
Normal file
45
esphome/components/am43/cover/am43_cover.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/cover/cover.h"
|
||||
#include "esphome/components/am43/am43_base.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace am43 {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
class Am43Component : public cover::Cover, public esphome::ble_client::BLEClientNode, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
cover::CoverTraits get_traits() override;
|
||||
void set_pin(uint16_t pin) { this->pin_ = pin; }
|
||||
void set_invert_position(bool invert_position) { this->invert_position_ = invert_position; }
|
||||
|
||||
protected:
|
||||
void control(const cover::CoverCall &call) override;
|
||||
uint16_t char_handle_;
|
||||
uint16_t pin_;
|
||||
bool invert_position_;
|
||||
Am43Encoder *encoder_;
|
||||
Am43Decoder *decoder_;
|
||||
bool logged_in_;
|
||||
|
||||
float position_;
|
||||
};
|
||||
|
||||
} // namespace am43
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
46
esphome/components/am43/sensor.py
Normal file
46
esphome/components/am43/sensor.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, ble_client
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_BATTERY_LEVEL,
|
||||
ICON_BATTERY,
|
||||
CONF_ILLUMINANCE,
|
||||
ICON_BRIGHTNESS_5,
|
||||
UNIT_PERCENT,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@buxtronix"]
|
||||
|
||||
am43_ns = cg.esphome_ns.namespace("am43")
|
||||
Am43 = am43_ns.class_("Am43", ble_client.BLEClientNode, cg.PollingComponent)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Am43),
|
||||
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
|
||||
UNIT_PERCENT, ICON_BATTERY, 0
|
||||
),
|
||||
cv.Optional(CONF_ILLUMINANCE): sensor.sensor_schema(
|
||||
UNIT_PERCENT, ICON_BRIGHTNESS_5, 0
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
.extend(cv.polling_component_schema("120s"))
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield ble_client.register_ble_node(var, config)
|
||||
|
||||
if CONF_BATTERY_LEVEL in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
||||
cg.add(var.set_battery(sens))
|
||||
|
||||
if CONF_ILLUMINANCE in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_ILLUMINANCE])
|
||||
cg.add(var.set_illuminance(sens))
|
||||
@@ -34,7 +34,7 @@ CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA)
|
||||
CODEOWNERS = ["@syndlex"]
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
from PIL import Image
|
||||
|
||||
path = CORE.relative_config_path(config[CONF_FILE])
|
||||
|
||||
150
esphome/components/anova/anova.cpp
Normal file
150
esphome/components/anova/anova.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "anova.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace anova {
|
||||
|
||||
static const char *TAG = "anova";
|
||||
|
||||
using namespace esphome::climate;
|
||||
|
||||
void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
|
||||
|
||||
void Anova::setup() {
|
||||
this->codec_ = new AnovaCodec();
|
||||
this->current_request_ = 0;
|
||||
}
|
||||
|
||||
void Anova::loop() {}
|
||||
|
||||
void Anova::control(const ClimateCall &call) {
|
||||
if (call.get_mode().has_value()) {
|
||||
ClimateMode mode = *call.get_mode();
|
||||
AnovaPacket *pkt;
|
||||
switch (mode) {
|
||||
case climate::CLIMATE_MODE_OFF:
|
||||
pkt = this->codec_->get_stop_request();
|
||||
break;
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
pkt = this->codec_->get_start_request();
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unsupported mode: %d", mode);
|
||||
return;
|
||||
}
|
||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
||||
}
|
||||
if (call.get_target_temperature().has_value()) {
|
||||
auto pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature());
|
||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
||||
}
|
||||
}
|
||||
|
||||
void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
this->current_temperature = NAN;
|
||||
this->target_temperature = NAN;
|
||||
this->publish_state();
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
auto chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
|
||||
if (chr == nullptr) {
|
||||
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
|
||||
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
|
||||
break;
|
||||
}
|
||||
this->char_handle_ = chr->handle;
|
||||
|
||||
auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle);
|
||||
if (status) {
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
this->current_request_ = 0;
|
||||
this->update();
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (param->notify.handle != this->char_handle_)
|
||||
break;
|
||||
this->codec_->decode(param->notify.value, param->notify.value_len);
|
||||
if (this->codec_->has_target_temp()) {
|
||||
this->target_temperature = this->codec_->target_temp_;
|
||||
}
|
||||
if (this->codec_->has_current_temp()) {
|
||||
this->current_temperature = this->codec_->current_temp_;
|
||||
}
|
||||
if (this->codec_->has_running()) {
|
||||
this->mode = this->codec_->running_ ? climate::CLIMATE_MODE_HEAT : climate::CLIMATE_MODE_OFF;
|
||||
}
|
||||
if (this->codec_->has_unit()) {
|
||||
this->fahrenheit_ = (this->codec_->unit_ == 'f');
|
||||
ESP_LOGD(TAG, "Anova units is %s", this->fahrenheit_ ? "fahrenheit" : "celcius");
|
||||
this->current_request_++;
|
||||
}
|
||||
this->publish_state();
|
||||
|
||||
if (this->current_request_ > 1) {
|
||||
AnovaPacket *pkt = nullptr;
|
||||
switch (this->current_request_++) {
|
||||
case 2:
|
||||
pkt = this->codec_->get_read_target_temp_request();
|
||||
break;
|
||||
case 3:
|
||||
pkt = this->codec_->get_read_current_temp_request();
|
||||
break;
|
||||
default:
|
||||
this->current_request_ = 1;
|
||||
break;
|
||||
}
|
||||
if (pkt != nullptr) {
|
||||
auto status =
|
||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length,
|
||||
pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
|
||||
status);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); }
|
||||
|
||||
void Anova::update() {
|
||||
if (this->node_state != espbt::ClientState::Established)
|
||||
return;
|
||||
|
||||
if (this->current_request_ < 2) {
|
||||
auto pkt = this->codec_->get_read_device_status_request();
|
||||
if (this->current_request_ == 0)
|
||||
auto pkt = this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
|
||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
||||
this->current_request_++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace anova
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
52
esphome/components/anova/anova.h
Normal file
52
esphome/components/anova/anova.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/climate/climate.h"
|
||||
#include "anova_base.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace anova {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
static const uint16_t ANOVA_SERVICE_UUID = 0xFFE0;
|
||||
static const uint16_t ANOVA_CHARACTERISTIC_UUID = 0xFFE1;
|
||||
|
||||
class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode, public PollingComponent {
|
||||
public:
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void update() override;
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
climate::ClimateTraits traits() {
|
||||
auto traits = climate::ClimateTraits();
|
||||
traits.set_supports_current_temperature(true);
|
||||
traits.set_supports_heat_mode(true);
|
||||
traits.set_visual_min_temperature(25.0);
|
||||
traits.set_visual_max_temperature(100.0);
|
||||
traits.set_visual_temperature_step(0.1);
|
||||
return traits;
|
||||
}
|
||||
void set_unit_of_measurement(const char *);
|
||||
|
||||
protected:
|
||||
AnovaCodec *codec_;
|
||||
void control(const climate::ClimateCall &call) override;
|
||||
uint16_t char_handle_;
|
||||
uint8_t current_request_;
|
||||
bool fahrenheit_;
|
||||
};
|
||||
|
||||
} // namespace anova
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
137
esphome/components/anova/anova_base.cpp
Normal file
137
esphome/components/anova/anova_base.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "anova_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace anova {
|
||||
|
||||
float ftoc(float f) { return (f - 32.0) * (5.0f / 9.0f); }
|
||||
|
||||
float ctof(float c) { return (c * 9.0f / 5.0f) + 32.0; }
|
||||
|
||||
AnovaPacket *AnovaCodec::clean_packet_() {
|
||||
this->packet_.length = strlen((char *) this->packet_.data);
|
||||
this->packet_.data[this->packet_.length] = '\0';
|
||||
ESP_LOGV("anova", "SendPkt: %s\n", this->packet_.data);
|
||||
return &this->packet_;
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_read_device_status_request() {
|
||||
this->current_query_ = READ_DEVICE_STATUS;
|
||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_DEVICE_STATUS);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_read_target_temp_request() {
|
||||
this->current_query_ = READ_TARGET_TEMPERATURE;
|
||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_TARGET_TEMP);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_read_current_temp_request() {
|
||||
this->current_query_ = READ_CURRENT_TEMPERATURE;
|
||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_CURRENT_TEMP);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_read_unit_request() {
|
||||
this->current_query_ = READ_UNIT;
|
||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_UNIT);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_read_data_request() {
|
||||
this->current_query_ = READ_DATA;
|
||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_DATA);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_set_target_temp_request(float temperature) {
|
||||
this->current_query_ = SET_TARGET_TEMPERATURE;
|
||||
if (this->fahrenheit_)
|
||||
temperature = ctof(temperature);
|
||||
sprintf((char *) this->packet_.data, CMD_SET_TARGET_TEMP, temperature);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_set_unit_request(char unit) {
|
||||
this->current_query_ = SET_UNIT;
|
||||
sprintf((char *) this->packet_.data, CMD_SET_TEMP_UNIT, unit);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_start_request() {
|
||||
this->current_query_ = START;
|
||||
sprintf((char *) this->packet_.data, CMD_START);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_stop_request() {
|
||||
this->current_query_ = STOP;
|
||||
sprintf((char *) this->packet_.data, CMD_STOP);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
||||
memset(this->buf_, 0, 32);
|
||||
strncpy(this->buf_, (char *) data, length);
|
||||
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
|
||||
switch (this->current_query_) {
|
||||
case READ_DEVICE_STATUS: {
|
||||
if (!strncmp(this->buf_, "stopped", 7)) {
|
||||
this->has_running_ = true;
|
||||
this->running_ = false;
|
||||
}
|
||||
if (!strncmp(this->buf_, "running", 7)) {
|
||||
this->has_running_ = true;
|
||||
this->running_ = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case START: {
|
||||
if (!strncmp(this->buf_, "start", 5)) {
|
||||
this->has_running_ = true;
|
||||
this->running_ = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STOP: {
|
||||
if (!strncmp(this->buf_, "stop", 4)) {
|
||||
this->has_running_ = true;
|
||||
this->running_ = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case READ_TARGET_TEMPERATURE: {
|
||||
this->target_temp_ = strtof(this->buf_, nullptr);
|
||||
if (this->fahrenheit_)
|
||||
this->target_temp_ = ftoc(this->target_temp_);
|
||||
this->has_target_temp_ = true;
|
||||
break;
|
||||
}
|
||||
case SET_TARGET_TEMPERATURE: {
|
||||
this->target_temp_ = strtof(this->buf_, nullptr);
|
||||
if (this->fahrenheit_)
|
||||
this->target_temp_ = ftoc(this->target_temp_);
|
||||
this->has_target_temp_ = true;
|
||||
break;
|
||||
}
|
||||
case READ_CURRENT_TEMPERATURE: {
|
||||
this->current_temp_ = strtof(this->buf_, nullptr);
|
||||
if (this->fahrenheit_)
|
||||
this->current_temp_ = ftoc(this->current_temp_);
|
||||
this->has_current_temp_ = true;
|
||||
break;
|
||||
}
|
||||
case SET_UNIT:
|
||||
case READ_UNIT: {
|
||||
this->unit_ = this->buf_[0];
|
||||
this->fahrenheit_ = this->buf_[0] == 'f';
|
||||
this->has_unit_ = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace anova
|
||||
} // namespace esphome
|
||||
80
esphome/components/anova/anova_base.h
Normal file
80
esphome/components/anova/anova_base.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace anova {
|
||||
|
||||
enum CurrentQuery {
|
||||
NONE,
|
||||
READ_DEVICE_STATUS,
|
||||
READ_TARGET_TEMPERATURE,
|
||||
READ_CURRENT_TEMPERATURE,
|
||||
READ_DATA,
|
||||
READ_UNIT,
|
||||
SET_TARGET_TEMPERATURE,
|
||||
SET_UNIT,
|
||||
START,
|
||||
STOP,
|
||||
};
|
||||
|
||||
struct AnovaPacket {
|
||||
uint16_t length;
|
||||
uint8_t data[24];
|
||||
};
|
||||
|
||||
#define CMD_READ_DEVICE_STATUS "status\r"
|
||||
#define CMD_READ_TARGET_TEMP "read set temp\r"
|
||||
#define CMD_READ_CURRENT_TEMP "read temp\r"
|
||||
#define CMD_READ_UNIT "read unit\r"
|
||||
#define CMD_READ_DATA "read data\r"
|
||||
#define CMD_SET_TARGET_TEMP "set temp %.1f\r"
|
||||
#define CMD_SET_TEMP_UNIT "set unit %c\r"
|
||||
|
||||
#define CMD_START "start\r"
|
||||
#define CMD_STOP "stop\r"
|
||||
|
||||
class AnovaCodec {
|
||||
public:
|
||||
AnovaPacket *get_read_device_status_request();
|
||||
AnovaPacket *get_read_target_temp_request();
|
||||
AnovaPacket *get_read_current_temp_request();
|
||||
AnovaPacket *get_read_data_request();
|
||||
AnovaPacket *get_read_unit_request();
|
||||
|
||||
AnovaPacket *get_set_target_temp_request(float temperature);
|
||||
AnovaPacket *get_set_unit_request(char unit);
|
||||
|
||||
AnovaPacket *get_start_request();
|
||||
AnovaPacket *get_stop_request();
|
||||
|
||||
void decode(const uint8_t *data, uint16_t length);
|
||||
bool has_target_temp() { return this->has_target_temp_; }
|
||||
bool has_current_temp() { return this->has_current_temp_; }
|
||||
bool has_unit() { return this->has_unit_; }
|
||||
bool has_running() { return this->has_running_; }
|
||||
|
||||
union {
|
||||
float target_temp_;
|
||||
float current_temp_;
|
||||
char unit_;
|
||||
bool running_;
|
||||
};
|
||||
|
||||
protected:
|
||||
AnovaPacket *clean_packet_();
|
||||
AnovaPacket packet_;
|
||||
|
||||
bool has_target_temp_;
|
||||
bool has_current_temp_;
|
||||
bool has_unit_;
|
||||
bool has_running_;
|
||||
char buf_[32];
|
||||
bool fahrenheit_;
|
||||
|
||||
CurrentQuery current_query_;
|
||||
};
|
||||
|
||||
} // namespace anova
|
||||
} // namespace esphome
|
||||
36
esphome/components/anova/climate.py
Normal file
36
esphome/components/anova/climate.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import climate, ble_client
|
||||
from esphome.const import CONF_ID, CONF_UNIT_OF_MEASUREMENT
|
||||
|
||||
UNITS = {
|
||||
"f": "f",
|
||||
"c": "c",
|
||||
}
|
||||
|
||||
CODEOWNERS = ["@buxtronix"]
|
||||
DEPENDENCIES = ["ble_client"]
|
||||
|
||||
anova_ns = cg.esphome_ns.namespace("anova")
|
||||
Anova = anova_ns.class_(
|
||||
"Anova", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
climate.CLIMATE_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Anova),
|
||||
cv.Required(CONF_UNIT_OF_MEASUREMENT): cv.enum(UNITS),
|
||||
}
|
||||
)
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await climate.register_climate(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
|
||||
@@ -23,7 +23,7 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
namespace esphome {
|
||||
namespace apds9960 {
|
||||
|
||||
static const char *TAG = "apds9960";
|
||||
static const char *const TAG = "apds9960";
|
||||
|
||||
#define APDS9960_ERROR_CHECK(func) \
|
||||
if (!func) { \
|
||||
if (!(func)) { \
|
||||
this->mark_failed(); \
|
||||
return; \
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = yield cg.get_variable(config[CONF_APDS9960_ID])
|
||||
var = yield binary_sensor.new_binary_sensor(config)
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_APDS9960_ID])
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]])
|
||||
cg.add(func(var))
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import CONF_TYPE, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_LIGHTBULB
|
||||
from esphome.const import (
|
||||
CONF_TYPE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_PERCENT,
|
||||
ICON_LIGHTBULB,
|
||||
)
|
||||
from . import APDS9960, CONF_APDS9960_ID
|
||||
|
||||
DEPENDENCIES = ["apds9960"]
|
||||
@@ -15,7 +20,10 @@ TYPES = {
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = sensor.sensor_schema(
|
||||
UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
icon=ICON_LIGHTBULB,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
|
||||
@@ -24,8 +32,8 @@ CONFIG_SCHEMA = sensor.sensor_schema(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = yield cg.get_variable(config[CONF_APDS9960_ID])
|
||||
var = yield sensor.new_sensor(config)
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_APDS9960_ID])
|
||||
var = await sensor.new_sensor(config)
|
||||
func = getattr(hub, TYPES[config[CONF_TYPE]])
|
||||
cg.add(func(var))
|
||||
|
||||
@@ -68,9 +68,9 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
@coroutine_with_priority(40.0)
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_port(config[CONF_PORT]))
|
||||
cg.add(var.set_password(config[CONF_PASSWORD]))
|
||||
@@ -90,7 +90,7 @@ def to_code(config):
|
||||
conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names
|
||||
)
|
||||
cg.add(var.register_user_service(trigger))
|
||||
yield automation.build_automation(trigger, func_args, conf)
|
||||
await automation.build_automation(trigger, func_args, conf)
|
||||
|
||||
cg.add_define("USE_API")
|
||||
cg.add_global(api_ns.using)
|
||||
@@ -116,21 +116,21 @@ HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema(
|
||||
HomeAssistantServiceCallAction,
|
||||
HOMEASSISTANT_SERVICE_ACTION_SCHEMA,
|
||||
)
|
||||
def homeassistant_service_to_code(config, action_id, template_arg, args):
|
||||
serv = yield cg.get_variable(config[CONF_ID])
|
||||
async def homeassistant_service_to_code(config, action_id, template_arg, args):
|
||||
serv = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, serv, False)
|
||||
templ = yield cg.templatable(config[CONF_SERVICE], args, None)
|
||||
templ = await cg.templatable(config[CONF_SERVICE], args, None)
|
||||
cg.add(var.set_service(templ))
|
||||
for key, value in config[CONF_DATA].items():
|
||||
templ = yield cg.templatable(value, args, None)
|
||||
templ = await cg.templatable(value, args, None)
|
||||
cg.add(var.add_data(key, templ))
|
||||
for key, value in config[CONF_DATA_TEMPLATE].items():
|
||||
templ = yield cg.templatable(value, args, None)
|
||||
templ = await cg.templatable(value, args, None)
|
||||
cg.add(var.add_data_template(key, templ))
|
||||
for key, value in config[CONF_VARIABLES].items():
|
||||
templ = yield cg.templatable(value, args, None)
|
||||
templ = await cg.templatable(value, args, None)
|
||||
cg.add(var.add_variable(key, templ))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
def validate_homeassistant_event(value):
|
||||
@@ -159,21 +159,21 @@ HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema(
|
||||
HomeAssistantServiceCallAction,
|
||||
HOMEASSISTANT_EVENT_ACTION_SCHEMA,
|
||||
)
|
||||
def homeassistant_event_to_code(config, action_id, template_arg, args):
|
||||
serv = yield cg.get_variable(config[CONF_ID])
|
||||
async def homeassistant_event_to_code(config, action_id, template_arg, args):
|
||||
serv = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, serv, True)
|
||||
templ = yield cg.templatable(config[CONF_EVENT], args, None)
|
||||
templ = await cg.templatable(config[CONF_EVENT], args, None)
|
||||
cg.add(var.set_service(templ))
|
||||
for key, value in config[CONF_DATA].items():
|
||||
templ = yield cg.templatable(value, args, None)
|
||||
templ = await cg.templatable(value, args, None)
|
||||
cg.add(var.add_data(key, templ))
|
||||
for key, value in config[CONF_DATA_TEMPLATE].items():
|
||||
templ = yield cg.templatable(value, args, None)
|
||||
templ = await cg.templatable(value, args, None)
|
||||
cg.add(var.add_data_template(key, templ))
|
||||
for key, value in config[CONF_VARIABLES].items():
|
||||
templ = yield cg.templatable(value, args, None)
|
||||
templ = await cg.templatable(value, args, None)
|
||||
cg.add(var.add_variable(key, templ))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||
@@ -190,15 +190,15 @@ HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||
HomeAssistantServiceCallAction,
|
||||
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA,
|
||||
)
|
||||
def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
|
||||
serv = yield cg.get_variable(config[CONF_ID])
|
||||
async def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
|
||||
serv = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, serv, True)
|
||||
cg.add(var.set_service("esphome.tag_scanned"))
|
||||
templ = yield cg.templatable(config[CONF_TAG], args, cg.std_string)
|
||||
templ = await cg.templatable(config[CONF_TAG], args, cg.std_string)
|
||||
cg.add(var.add_data("tag_id", templ))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_condition("api.connected", APIConnectedCondition, {})
|
||||
def api_connected_to_code(config, condition_id, template_arg, args):
|
||||
yield cg.new_Pvariable(condition_id, template_arg)
|
||||
async def api_connected_to_code(config, condition_id, template_arg, args):
|
||||
return cg.new_Pvariable(condition_id, template_arg)
|
||||
|
||||
@@ -38,6 +38,8 @@ service APIConnection {
|
||||
rpc switch_command (SwitchCommandRequest) returns (void) {}
|
||||
rpc camera_image (CameraImageRequest) returns (void) {}
|
||||
rpc climate_command (ClimateCommandRequest) returns (void) {}
|
||||
rpc number_command (NumberCommandRequest) returns (void) {}
|
||||
rpc select_command (SelectCommandRequest) returns (void) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +178,10 @@ message DeviceInfoResponse {
|
||||
string model = 6;
|
||||
|
||||
bool has_deep_sleep = 7;
|
||||
|
||||
// The esphome project details if set
|
||||
string project_name = 8;
|
||||
string project_version = 9;
|
||||
}
|
||||
|
||||
message ListEntitiesRequest {
|
||||
@@ -208,6 +214,7 @@ message ListEntitiesBinarySensorResponse {
|
||||
|
||||
string device_class = 5;
|
||||
bool is_status_binary_sensor = 6;
|
||||
bool disabled_by_default = 7;
|
||||
}
|
||||
message BinarySensorStateResponse {
|
||||
option (id) = 21;
|
||||
@@ -237,6 +244,7 @@ message ListEntitiesCoverResponse {
|
||||
bool supports_position = 6;
|
||||
bool supports_tilt = 7;
|
||||
string device_class = 8;
|
||||
bool disabled_by_default = 9;
|
||||
}
|
||||
|
||||
enum LegacyCoverState {
|
||||
@@ -304,6 +312,7 @@ message ListEntitiesFanResponse {
|
||||
bool supports_speed = 6;
|
||||
bool supports_direction = 7;
|
||||
int32 supported_speed_count = 8;
|
||||
bool disabled_by_default = 9;
|
||||
}
|
||||
enum FanSpeed {
|
||||
FAN_SPEED_LOW = 0;
|
||||
@@ -347,6 +356,18 @@ message FanCommandRequest {
|
||||
}
|
||||
|
||||
// ==================== LIGHT ====================
|
||||
enum ColorMode {
|
||||
COLOR_MODE_UNKNOWN = 0;
|
||||
COLOR_MODE_ON_OFF = 1;
|
||||
COLOR_MODE_BRIGHTNESS = 2;
|
||||
COLOR_MODE_WHITE = 7;
|
||||
COLOR_MODE_COLOR_TEMPERATURE = 11;
|
||||
COLOR_MODE_COLD_WARM_WHITE = 19;
|
||||
COLOR_MODE_RGB = 35;
|
||||
COLOR_MODE_RGB_WHITE = 39;
|
||||
COLOR_MODE_RGB_COLOR_TEMPERATURE = 47;
|
||||
COLOR_MODE_RGB_COLD_WARM_WHITE = 51;
|
||||
}
|
||||
message ListEntitiesLightResponse {
|
||||
option (id) = 15;
|
||||
option (source) = SOURCE_SERVER;
|
||||
@@ -357,13 +378,16 @@ message ListEntitiesLightResponse {
|
||||
string name = 3;
|
||||
string unique_id = 4;
|
||||
|
||||
bool supports_brightness = 5;
|
||||
bool supports_rgb = 6;
|
||||
bool supports_white_value = 7;
|
||||
bool supports_color_temperature = 8;
|
||||
repeated ColorMode supported_color_modes = 12;
|
||||
// next four supports_* are for legacy clients, newer clients should use color modes
|
||||
bool legacy_supports_brightness = 5 [deprecated=true];
|
||||
bool legacy_supports_rgb = 6 [deprecated=true];
|
||||
bool legacy_supports_white_value = 7 [deprecated=true];
|
||||
bool legacy_supports_color_temperature = 8 [deprecated=true];
|
||||
float min_mireds = 9;
|
||||
float max_mireds = 10;
|
||||
repeated string effects = 11;
|
||||
bool disabled_by_default = 13;
|
||||
}
|
||||
message LightStateResponse {
|
||||
option (id) = 24;
|
||||
@@ -374,11 +398,15 @@ message LightStateResponse {
|
||||
fixed32 key = 1;
|
||||
bool state = 2;
|
||||
float brightness = 3;
|
||||
ColorMode color_mode = 11;
|
||||
float color_brightness = 10;
|
||||
float red = 4;
|
||||
float green = 5;
|
||||
float blue = 6;
|
||||
float white = 7;
|
||||
float color_temperature = 8;
|
||||
float cold_white = 12;
|
||||
float warm_white = 13;
|
||||
string effect = 9;
|
||||
}
|
||||
message LightCommandRequest {
|
||||
@@ -392,6 +420,10 @@ message LightCommandRequest {
|
||||
bool state = 3;
|
||||
bool has_brightness = 4;
|
||||
float brightness = 5;
|
||||
bool has_color_mode = 22;
|
||||
ColorMode color_mode = 23;
|
||||
bool has_color_brightness = 20;
|
||||
float color_brightness = 21;
|
||||
bool has_rgb = 6;
|
||||
float red = 7;
|
||||
float green = 8;
|
||||
@@ -400,6 +432,10 @@ message LightCommandRequest {
|
||||
float white = 11;
|
||||
bool has_color_temperature = 12;
|
||||
float color_temperature = 13;
|
||||
bool has_cold_white = 24;
|
||||
float cold_white = 25;
|
||||
bool has_warm_white = 26;
|
||||
float warm_white = 27;
|
||||
bool has_transition_length = 14;
|
||||
uint32 transition_length = 15;
|
||||
bool has_flash_length = 16;
|
||||
@@ -409,6 +445,18 @@ message LightCommandRequest {
|
||||
}
|
||||
|
||||
// ==================== SENSOR ====================
|
||||
enum SensorStateClass {
|
||||
STATE_CLASS_NONE = 0;
|
||||
STATE_CLASS_MEASUREMENT = 1;
|
||||
STATE_CLASS_TOTAL_INCREASING = 2;
|
||||
}
|
||||
|
||||
enum SensorLastResetType {
|
||||
LAST_RESET_NONE = 0;
|
||||
LAST_RESET_NEVER = 1;
|
||||
LAST_RESET_AUTO = 2;
|
||||
}
|
||||
|
||||
message ListEntitiesSensorResponse {
|
||||
option (id) = 16;
|
||||
option (source) = SOURCE_SERVER;
|
||||
@@ -424,6 +472,9 @@ message ListEntitiesSensorResponse {
|
||||
int32 accuracy_decimals = 7;
|
||||
bool force_update = 8;
|
||||
string device_class = 9;
|
||||
SensorStateClass state_class = 10;
|
||||
SensorLastResetType last_reset_type = 11;
|
||||
bool disabled_by_default = 12;
|
||||
}
|
||||
message SensorStateResponse {
|
||||
option (id) = 25;
|
||||
@@ -451,6 +502,7 @@ message ListEntitiesSwitchResponse {
|
||||
|
||||
string icon = 5;
|
||||
bool assumed_state = 6;
|
||||
bool disabled_by_default = 7;
|
||||
}
|
||||
message SwitchStateResponse {
|
||||
option (id) = 26;
|
||||
@@ -483,6 +535,7 @@ message ListEntitiesTextSensorResponse {
|
||||
string unique_id = 4;
|
||||
|
||||
string icon = 5;
|
||||
bool disabled_by_default = 6;
|
||||
}
|
||||
message TextSensorStateResponse {
|
||||
option (id) = 27;
|
||||
@@ -503,9 +556,10 @@ enum LogLevel {
|
||||
LOG_LEVEL_ERROR = 1;
|
||||
LOG_LEVEL_WARN = 2;
|
||||
LOG_LEVEL_INFO = 3;
|
||||
LOG_LEVEL_DEBUG = 4;
|
||||
LOG_LEVEL_VERBOSE = 5;
|
||||
LOG_LEVEL_VERY_VERBOSE = 6;
|
||||
LOG_LEVEL_CONFIG = 4;
|
||||
LOG_LEVEL_DEBUG = 5;
|
||||
LOG_LEVEL_VERBOSE = 6;
|
||||
LOG_LEVEL_VERY_VERBOSE = 7;
|
||||
}
|
||||
message SubscribeLogsRequest {
|
||||
option (id) = 28;
|
||||
@@ -520,7 +574,6 @@ message SubscribeLogsResponse {
|
||||
option (no_delay) = false;
|
||||
|
||||
LogLevel level = 1;
|
||||
string tag = 2;
|
||||
string message = 3;
|
||||
bool send_failed = 4;
|
||||
}
|
||||
@@ -561,6 +614,7 @@ message SubscribeHomeAssistantStateResponse {
|
||||
option (id) = 39;
|
||||
option (source) = SOURCE_SERVER;
|
||||
string entity_id = 1;
|
||||
string attribute = 2;
|
||||
}
|
||||
|
||||
message HomeAssistantStateResponse {
|
||||
@@ -570,6 +624,7 @@ message HomeAssistantStateResponse {
|
||||
|
||||
string entity_id = 1;
|
||||
string state = 2;
|
||||
string attribute = 3;
|
||||
}
|
||||
|
||||
// ==================== IMPORT TIME ====================
|
||||
@@ -640,6 +695,7 @@ message ListEntitiesCameraResponse {
|
||||
fixed32 key = 2;
|
||||
string name = 3;
|
||||
string unique_id = 4;
|
||||
bool disabled_by_default = 5;
|
||||
}
|
||||
|
||||
message CameraImageResponse {
|
||||
@@ -664,11 +720,12 @@ message CameraImageRequest {
|
||||
// ==================== CLIMATE ====================
|
||||
enum ClimateMode {
|
||||
CLIMATE_MODE_OFF = 0;
|
||||
CLIMATE_MODE_AUTO = 1;
|
||||
CLIMATE_MODE_HEAT_COOL = 1;
|
||||
CLIMATE_MODE_COOL = 2;
|
||||
CLIMATE_MODE_HEAT = 3;
|
||||
CLIMATE_MODE_FAN_ONLY = 4;
|
||||
CLIMATE_MODE_DRY = 5;
|
||||
CLIMATE_MODE_AUTO = 6;
|
||||
}
|
||||
enum ClimateFanMode {
|
||||
CLIMATE_FAN_ON = 0;
|
||||
@@ -696,6 +753,16 @@ enum ClimateAction {
|
||||
CLIMATE_ACTION_DRYING = 5;
|
||||
CLIMATE_ACTION_FAN = 6;
|
||||
}
|
||||
enum ClimatePreset {
|
||||
CLIMATE_PRESET_NONE = 0;
|
||||
CLIMATE_PRESET_HOME = 1;
|
||||
CLIMATE_PRESET_AWAY = 2;
|
||||
CLIMATE_PRESET_BOOST = 3;
|
||||
CLIMATE_PRESET_COMFORT = 4;
|
||||
CLIMATE_PRESET_ECO = 5;
|
||||
CLIMATE_PRESET_SLEEP = 6;
|
||||
CLIMATE_PRESET_ACTIVITY = 7;
|
||||
}
|
||||
message ListEntitiesClimateResponse {
|
||||
option (id) = 46;
|
||||
option (source) = SOURCE_SERVER;
|
||||
@@ -712,10 +779,16 @@ message ListEntitiesClimateResponse {
|
||||
float visual_min_temperature = 8;
|
||||
float visual_max_temperature = 9;
|
||||
float visual_temperature_step = 10;
|
||||
bool supports_away = 11;
|
||||
// for older peer versions - in new system this
|
||||
// is if CLIMATE_PRESET_AWAY exists is supported_presets
|
||||
bool legacy_supports_away = 11;
|
||||
bool supports_action = 12;
|
||||
repeated ClimateFanMode supported_fan_modes = 13;
|
||||
repeated ClimateSwingMode supported_swing_modes = 14;
|
||||
repeated string supported_custom_fan_modes = 15;
|
||||
repeated ClimatePreset supported_presets = 16;
|
||||
repeated string supported_custom_presets = 17;
|
||||
bool disabled_by_default = 18;
|
||||
}
|
||||
message ClimateStateResponse {
|
||||
option (id) = 47;
|
||||
@@ -729,10 +802,14 @@ message ClimateStateResponse {
|
||||
float target_temperature = 4;
|
||||
float target_temperature_low = 5;
|
||||
float target_temperature_high = 6;
|
||||
bool away = 7;
|
||||
// For older peers, equal to preset == CLIMATE_PRESET_AWAY
|
||||
bool legacy_away = 7;
|
||||
ClimateAction action = 8;
|
||||
ClimateFanMode fan_mode = 9;
|
||||
ClimateSwingMode swing_mode = 10;
|
||||
string custom_fan_mode = 11;
|
||||
ClimatePreset preset = 12;
|
||||
string custom_preset = 13;
|
||||
}
|
||||
message ClimateCommandRequest {
|
||||
option (id) = 48;
|
||||
@@ -749,10 +826,93 @@ message ClimateCommandRequest {
|
||||
float target_temperature_low = 7;
|
||||
bool has_target_temperature_high = 8;
|
||||
float target_temperature_high = 9;
|
||||
bool has_away = 10;
|
||||
bool away = 11;
|
||||
// legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
|
||||
bool has_legacy_away = 10;
|
||||
bool legacy_away = 11;
|
||||
bool has_fan_mode = 12;
|
||||
ClimateFanMode fan_mode = 13;
|
||||
bool has_swing_mode = 14;
|
||||
ClimateSwingMode swing_mode = 15;
|
||||
bool has_custom_fan_mode = 16;
|
||||
string custom_fan_mode = 17;
|
||||
bool has_preset = 18;
|
||||
ClimatePreset preset = 19;
|
||||
bool has_custom_preset = 20;
|
||||
string custom_preset = 21;
|
||||
}
|
||||
|
||||
// ==================== NUMBER ====================
|
||||
message ListEntitiesNumberResponse {
|
||||
option (id) = 49;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_NUMBER";
|
||||
|
||||
string object_id = 1;
|
||||
fixed32 key = 2;
|
||||
string name = 3;
|
||||
string unique_id = 4;
|
||||
|
||||
string icon = 5;
|
||||
float min_value = 6;
|
||||
float max_value = 7;
|
||||
float step = 8;
|
||||
bool disabled_by_default = 9;
|
||||
}
|
||||
message NumberStateResponse {
|
||||
option (id) = 50;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_NUMBER";
|
||||
option (no_delay) = true;
|
||||
|
||||
fixed32 key = 1;
|
||||
float state = 2;
|
||||
// If the number does not have a valid state yet.
|
||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||
bool missing_state = 3;
|
||||
}
|
||||
message NumberCommandRequest {
|
||||
option (id) = 51;
|
||||
option (source) = SOURCE_CLIENT;
|
||||
option (ifdef) = "USE_NUMBER";
|
||||
option (no_delay) = true;
|
||||
|
||||
fixed32 key = 1;
|
||||
float state = 2;
|
||||
}
|
||||
|
||||
// ==================== SELECT ====================
|
||||
message ListEntitiesSelectResponse {
|
||||
option (id) = 52;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_SELECT";
|
||||
|
||||
string object_id = 1;
|
||||
fixed32 key = 2;
|
||||
string name = 3;
|
||||
string unique_id = 4;
|
||||
|
||||
string icon = 5;
|
||||
repeated string options = 6;
|
||||
bool disabled_by_default = 7;
|
||||
}
|
||||
message SelectStateResponse {
|
||||
option (id) = 53;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_SELECT";
|
||||
option (no_delay) = true;
|
||||
|
||||
fixed32 key = 1;
|
||||
string state = 2;
|
||||
// If the select does not have a valid state yet.
|
||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||
bool missing_state = 3;
|
||||
}
|
||||
message SelectCommandRequest {
|
||||
option (id) = 54;
|
||||
option (source) = SOURCE_CLIENT;
|
||||
option (ifdef) = "USE_SELECT";
|
||||
option (no_delay) = true;
|
||||
|
||||
fixed32 key = 1;
|
||||
string state = 2;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
static const char *TAG = "api.connection";
|
||||
static const char *const TAG = "api.connection";
|
||||
|
||||
APIConnection::APIConnection(AsyncClient *client, APIServer *parent)
|
||||
: client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
|
||||
@@ -176,6 +176,7 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
|
||||
msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
|
||||
msg.device_class = binary_sensor->get_device_class();
|
||||
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
||||
msg.disabled_by_default = binary_sensor->is_disabled_by_default();
|
||||
return this->send_list_entities_binary_sensor_response(msg);
|
||||
}
|
||||
#endif
|
||||
@@ -207,6 +208,7 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
|
||||
msg.supports_position = traits.get_supports_position();
|
||||
msg.supports_tilt = traits.get_supports_tilt();
|
||||
msg.device_class = cover->get_device_class();
|
||||
msg.disabled_by_default = cover->is_disabled_by_default();
|
||||
return this->send_list_entities_cover_response(msg);
|
||||
}
|
||||
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
||||
@@ -268,6 +270,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
|
||||
msg.supports_speed = traits.supports_speed();
|
||||
msg.supports_direction = traits.supports_direction();
|
||||
msg.supported_speed_count = traits.supported_speed_count();
|
||||
msg.disabled_by_default = fan->is_disabled_by_default();
|
||||
return this->send_list_entities_fan_response(msg);
|
||||
}
|
||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
||||
@@ -301,21 +304,21 @@ bool APIConnection::send_light_state(light::LightState *light) {
|
||||
|
||||
auto traits = light->get_traits();
|
||||
auto values = light->remote_values;
|
||||
auto color_mode = values.get_color_mode();
|
||||
LightStateResponse resp{};
|
||||
|
||||
resp.key = light->get_object_id_hash();
|
||||
resp.state = values.is_on();
|
||||
if (traits.get_supports_brightness())
|
||||
resp.brightness = values.get_brightness();
|
||||
if (traits.get_supports_rgb()) {
|
||||
resp.red = values.get_red();
|
||||
resp.green = values.get_green();
|
||||
resp.blue = values.get_blue();
|
||||
}
|
||||
if (traits.get_supports_rgb_white_value())
|
||||
resp.white = values.get_white();
|
||||
if (traits.get_supports_color_temperature())
|
||||
resp.color_temperature = values.get_color_temperature();
|
||||
resp.color_mode = static_cast<enums::ColorMode>(color_mode);
|
||||
resp.brightness = values.get_brightness();
|
||||
resp.color_brightness = values.get_color_brightness();
|
||||
resp.red = values.get_red();
|
||||
resp.green = values.get_green();
|
||||
resp.blue = values.get_blue();
|
||||
resp.white = values.get_white();
|
||||
resp.color_temperature = values.get_color_temperature();
|
||||
resp.cold_white = values.get_cold_white();
|
||||
resp.warm_white = values.get_warm_white();
|
||||
if (light->supports_effects())
|
||||
resp.effect = light->get_effect_name();
|
||||
return this->send_light_state_response(resp);
|
||||
@@ -327,11 +330,21 @@ bool APIConnection::send_light_info(light::LightState *light) {
|
||||
msg.object_id = light->get_object_id();
|
||||
msg.name = light->get_name();
|
||||
msg.unique_id = get_default_unique_id("light", light);
|
||||
msg.supports_brightness = traits.get_supports_brightness();
|
||||
msg.supports_rgb = traits.get_supports_rgb();
|
||||
msg.supports_white_value = traits.get_supports_rgb_white_value();
|
||||
msg.supports_color_temperature = traits.get_supports_color_temperature();
|
||||
if (msg.supports_color_temperature) {
|
||||
|
||||
msg.disabled_by_default = light->is_disabled_by_default();
|
||||
|
||||
for (auto mode : traits.get_supported_color_modes())
|
||||
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
|
||||
|
||||
msg.legacy_supports_brightness = traits.supports_color_capability(light::ColorCapability::BRIGHTNESS);
|
||||
msg.legacy_supports_rgb = traits.supports_color_capability(light::ColorCapability::RGB);
|
||||
msg.legacy_supports_white_value =
|
||||
msg.legacy_supports_rgb && (traits.supports_color_capability(light::ColorCapability::WHITE) ||
|
||||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE));
|
||||
msg.legacy_supports_color_temperature = traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
|
||||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE);
|
||||
|
||||
if (msg.legacy_supports_color_temperature) {
|
||||
msg.min_mireds = traits.get_min_mireds();
|
||||
msg.max_mireds = traits.get_max_mireds();
|
||||
}
|
||||
@@ -352,6 +365,10 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
|
||||
call.set_state(msg.state);
|
||||
if (msg.has_brightness)
|
||||
call.set_brightness(msg.brightness);
|
||||
if (msg.has_color_mode)
|
||||
call.set_color_mode(static_cast<light::ColorMode>(msg.color_mode));
|
||||
if (msg.has_color_brightness)
|
||||
call.set_color_brightness(msg.color_brightness);
|
||||
if (msg.has_rgb) {
|
||||
call.set_red(msg.red);
|
||||
call.set_green(msg.green);
|
||||
@@ -361,6 +378,10 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
|
||||
call.set_white(msg.white);
|
||||
if (msg.has_color_temperature)
|
||||
call.set_color_temperature(msg.color_temperature);
|
||||
if (msg.has_cold_white)
|
||||
call.set_cold_white(msg.cold_white);
|
||||
if (msg.has_warm_white)
|
||||
call.set_warm_white(msg.warm_white);
|
||||
if (msg.has_transition_length)
|
||||
call.set_transition_length(msg.transition_length);
|
||||
if (msg.has_flash_length)
|
||||
@@ -395,6 +416,10 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
||||
msg.accuracy_decimals = sensor->get_accuracy_decimals();
|
||||
msg.force_update = sensor->get_force_update();
|
||||
msg.device_class = sensor->get_device_class();
|
||||
msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
|
||||
msg.last_reset_type = static_cast<enums::SensorLastResetType>(sensor->last_reset_type);
|
||||
msg.disabled_by_default = sensor->is_disabled_by_default();
|
||||
|
||||
return this->send_list_entities_sensor_response(msg);
|
||||
}
|
||||
#endif
|
||||
@@ -417,6 +442,7 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
|
||||
msg.unique_id = get_default_unique_id("switch", a_switch);
|
||||
msg.icon = a_switch->get_icon();
|
||||
msg.assumed_state = a_switch->assumed_state();
|
||||
msg.disabled_by_default = a_switch->is_disabled_by_default();
|
||||
return this->send_list_entities_switch_response(msg);
|
||||
}
|
||||
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
|
||||
@@ -451,6 +477,7 @@ bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor)
|
||||
if (msg.unique_id.empty())
|
||||
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
|
||||
msg.icon = text_sensor->get_icon();
|
||||
msg.disabled_by_default = text_sensor->is_disabled_by_default();
|
||||
return this->send_list_entities_text_sensor_response(msg);
|
||||
}
|
||||
#endif
|
||||
@@ -473,10 +500,16 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
|
||||
} else {
|
||||
resp.target_temperature = climate->target_temperature;
|
||||
}
|
||||
if (traits.get_supports_away())
|
||||
resp.away = climate->away;
|
||||
if (traits.get_supports_fan_modes())
|
||||
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode);
|
||||
if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
|
||||
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
|
||||
if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
|
||||
resp.custom_fan_mode = climate->custom_fan_mode.value();
|
||||
if (traits.get_supports_presets() && climate->preset.has_value()) {
|
||||
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
|
||||
resp.legacy_away = resp.preset == enums::CLIMATE_PRESET_AWAY;
|
||||
}
|
||||
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
|
||||
resp.custom_preset = climate->custom_preset.value();
|
||||
if (traits.get_supports_swing_modes())
|
||||
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
|
||||
return this->send_climate_state_response(resp);
|
||||
@@ -488,29 +521,31 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
|
||||
msg.object_id = climate->get_object_id();
|
||||
msg.name = climate->get_name();
|
||||
msg.unique_id = get_default_unique_id("climate", climate);
|
||||
|
||||
msg.disabled_by_default = climate->is_disabled_by_default();
|
||||
|
||||
msg.supports_current_temperature = traits.get_supports_current_temperature();
|
||||
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
|
||||
for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL,
|
||||
climate::CLIMATE_MODE_HEAT, climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY}) {
|
||||
if (traits.supports_mode(mode))
|
||||
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
|
||||
}
|
||||
|
||||
for (auto mode : traits.get_supported_modes())
|
||||
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
|
||||
|
||||
msg.visual_min_temperature = traits.get_visual_min_temperature();
|
||||
msg.visual_max_temperature = traits.get_visual_max_temperature();
|
||||
msg.visual_temperature_step = traits.get_visual_temperature_step();
|
||||
msg.supports_away = traits.get_supports_away();
|
||||
msg.legacy_supports_away = traits.supports_preset(climate::CLIMATE_PRESET_AWAY);
|
||||
msg.supports_action = traits.get_supports_action();
|
||||
for (auto fan_mode : {climate::CLIMATE_FAN_ON, climate::CLIMATE_FAN_OFF, climate::CLIMATE_FAN_AUTO,
|
||||
climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH,
|
||||
climate::CLIMATE_FAN_MIDDLE, climate::CLIMATE_FAN_FOCUS, climate::CLIMATE_FAN_DIFFUSE}) {
|
||||
if (traits.supports_fan_mode(fan_mode))
|
||||
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
|
||||
}
|
||||
for (auto swing_mode : {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL,
|
||||
climate::CLIMATE_SWING_HORIZONTAL}) {
|
||||
if (traits.supports_swing_mode(swing_mode))
|
||||
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
|
||||
}
|
||||
|
||||
for (auto fan_mode : traits.get_supported_fan_modes())
|
||||
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
|
||||
for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
|
||||
msg.supported_custom_fan_modes.push_back(custom_fan_mode);
|
||||
for (auto preset : traits.get_supported_presets())
|
||||
msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
|
||||
for (auto const &custom_preset : traits.get_supported_custom_presets())
|
||||
msg.supported_custom_presets.push_back(custom_preset);
|
||||
for (auto swing_mode : traits.get_supported_swing_modes())
|
||||
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
|
||||
return this->send_list_entities_climate_response(msg);
|
||||
}
|
||||
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
||||
@@ -527,16 +562,95 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
||||
call.set_target_temperature_low(msg.target_temperature_low);
|
||||
if (msg.has_target_temperature_high)
|
||||
call.set_target_temperature_high(msg.target_temperature_high);
|
||||
if (msg.has_away)
|
||||
call.set_away(msg.away);
|
||||
if (msg.has_legacy_away)
|
||||
call.set_preset(msg.legacy_away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME);
|
||||
if (msg.has_fan_mode)
|
||||
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
|
||||
if (msg.has_custom_fan_mode)
|
||||
call.set_fan_mode(msg.custom_fan_mode);
|
||||
if (msg.has_preset)
|
||||
call.set_preset(static_cast<climate::ClimatePreset>(msg.preset));
|
||||
if (msg.has_custom_preset)
|
||||
call.set_preset(msg.custom_preset);
|
||||
if (msg.has_swing_mode)
|
||||
call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode));
|
||||
call.perform();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
bool APIConnection::send_number_state(number::Number *number, float state) {
|
||||
if (!this->state_subscription_)
|
||||
return false;
|
||||
|
||||
NumberStateResponse resp{};
|
||||
resp.key = number->get_object_id_hash();
|
||||
resp.state = state;
|
||||
resp.missing_state = !number->has_state();
|
||||
return this->send_number_state_response(resp);
|
||||
}
|
||||
bool APIConnection::send_number_info(number::Number *number) {
|
||||
ListEntitiesNumberResponse msg;
|
||||
msg.key = number->get_object_id_hash();
|
||||
msg.object_id = number->get_object_id();
|
||||
msg.name = number->get_name();
|
||||
msg.unique_id = get_default_unique_id("number", number);
|
||||
msg.icon = number->traits.get_icon();
|
||||
msg.disabled_by_default = number->is_disabled_by_default();
|
||||
|
||||
msg.min_value = number->traits.get_min_value();
|
||||
msg.max_value = number->traits.get_max_value();
|
||||
msg.step = number->traits.get_step();
|
||||
|
||||
return this->send_list_entities_number_response(msg);
|
||||
}
|
||||
void APIConnection::number_command(const NumberCommandRequest &msg) {
|
||||
number::Number *number = App.get_number_by_key(msg.key);
|
||||
if (number == nullptr)
|
||||
return;
|
||||
|
||||
auto call = number->make_call();
|
||||
call.set_value(msg.state);
|
||||
call.perform();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
bool APIConnection::send_select_state(select::Select *select, std::string state) {
|
||||
if (!this->state_subscription_)
|
||||
return false;
|
||||
|
||||
SelectStateResponse resp{};
|
||||
resp.key = select->get_object_id_hash();
|
||||
resp.state = std::move(state);
|
||||
resp.missing_state = !select->has_state();
|
||||
return this->send_select_state_response(resp);
|
||||
}
|
||||
bool APIConnection::send_select_info(select::Select *select) {
|
||||
ListEntitiesSelectResponse msg;
|
||||
msg.key = select->get_object_id_hash();
|
||||
msg.object_id = select->get_object_id();
|
||||
msg.name = select->get_name();
|
||||
msg.unique_id = get_default_unique_id("select", select);
|
||||
msg.icon = select->traits.get_icon();
|
||||
msg.disabled_by_default = select->is_disabled_by_default();
|
||||
|
||||
for (const auto &option : select->traits.get_options())
|
||||
msg.options.push_back(option);
|
||||
|
||||
return this->send_list_entities_select_response(msg);
|
||||
}
|
||||
void APIConnection::select_command(const SelectCommandRequest &msg) {
|
||||
select::Select *select = App.get_select_by_key(msg.key);
|
||||
if (select == nullptr)
|
||||
return;
|
||||
|
||||
auto call = select->make_call();
|
||||
call.set_option(msg.state);
|
||||
call.perform();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
|
||||
if (!this->state_subscription_)
|
||||
@@ -551,6 +665,7 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
|
||||
msg.object_id = camera->get_object_id();
|
||||
msg.name = camera->get_name();
|
||||
msg.unique_id = get_default_unique_id("camera", camera);
|
||||
msg.disabled_by_default = camera->is_disabled_by_default();
|
||||
return this->send_list_entities_camera_response(msg);
|
||||
}
|
||||
void APIConnection::camera_image(const CameraImageRequest &msg) {
|
||||
@@ -579,8 +694,6 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin
|
||||
auto buffer = this->create_buffer();
|
||||
// LogLevel level = 1;
|
||||
buffer.encode_uint32(1, static_cast<uint32_t>(level));
|
||||
// string tag = 2;
|
||||
// buffer.encode_string(2, tag, strlen(tag));
|
||||
// string message = 3;
|
||||
buffer.encode_string(3, line, strlen(line));
|
||||
// SubscribeLogsResponse - 29
|
||||
@@ -602,7 +715,7 @@ HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
||||
|
||||
HelloResponse resp;
|
||||
resp.api_version_major = 1;
|
||||
resp.api_version_minor = 4;
|
||||
resp.api_version_minor = 6;
|
||||
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
||||
this->connection_state_ = ConnectionState::CONNECTED;
|
||||
return resp;
|
||||
@@ -637,13 +750,18 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
||||
#endif
|
||||
#ifdef USE_DEEP_SLEEP
|
||||
resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
|
||||
#endif
|
||||
#ifdef ESPHOME_PROJECT_NAME
|
||||
resp.project_name = ESPHOME_PROJECT_NAME;
|
||||
resp.project_version = ESPHOME_PROJECT_VERSION;
|
||||
#endif
|
||||
return resp;
|
||||
}
|
||||
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
|
||||
for (auto &it : this->parent_->get_state_subs())
|
||||
if (it.entity_id == msg.entity_id)
|
||||
if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
|
||||
it.callback(msg.state);
|
||||
}
|
||||
}
|
||||
void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
|
||||
bool found = false;
|
||||
@@ -660,6 +778,7 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
|
||||
for (auto &it : this->parent_->get_state_subs()) {
|
||||
SubscribeHomeAssistantStateResponse resp;
|
||||
resp.entity_id = it.entity_id;
|
||||
resp.attribute = it.attribute.value();
|
||||
if (!this->send_subscribe_home_assistant_state_response(resp)) {
|
||||
this->on_fatal_error();
|
||||
return;
|
||||
|
||||
@@ -62,6 +62,16 @@ class APIConnection : public APIServerConnection {
|
||||
bool send_climate_state(climate::Climate *climate);
|
||||
bool send_climate_info(climate::Climate *climate);
|
||||
void climate_command(const ClimateCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool send_number_state(number::Number *number, float state);
|
||||
bool send_number_info(number::Number *number);
|
||||
void number_command(const NumberCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool send_select_state(select::Select *select, std::string state);
|
||||
bool send_select_info(select::Select *select);
|
||||
void select_command(const SelectCommandRequest &msg) override;
|
||||
#endif
|
||||
bool send_log_message(int level, const char *tag, const char *line);
|
||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -6,61 +6,85 @@
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
static const char *TAG = "api.service";
|
||||
static const char *const TAG = "api.service";
|
||||
|
||||
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<HelloResponse>(msg, 2);
|
||||
}
|
||||
bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ConnectResponse>(msg, 4);
|
||||
}
|
||||
bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<DisconnectRequest>(msg, 5);
|
||||
}
|
||||
bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<DisconnectResponse>(msg, 6);
|
||||
}
|
||||
bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<PingRequest>(msg, 7);
|
||||
}
|
||||
bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<PingResponse>(msg, 8);
|
||||
}
|
||||
bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<DeviceInfoResponse>(msg, 10);
|
||||
}
|
||||
bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
|
||||
}
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<BinarySensorStateResponse>(msg, 21);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<CoverStateResponse>(msg, 22);
|
||||
}
|
||||
#endif
|
||||
@@ -68,13 +92,17 @@ bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesFanResponse>(msg, 14);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<FanStateResponse>(msg, 23);
|
||||
}
|
||||
#endif
|
||||
@@ -82,13 +110,17 @@ bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &ms
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesLightResponse>(msg, 15);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<LightStateResponse>(msg, 24);
|
||||
}
|
||||
#endif
|
||||
@@ -96,25 +128,33 @@ bool APIServerConnectionBase::send_light_state_response(const LightStateResponse
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<SensorStateResponse>(msg, 25);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<SwitchStateResponse>(msg, 26);
|
||||
}
|
||||
#endif
|
||||
@@ -122,13 +162,17 @@ bool APIServerConnectionBase::send_switch_state_response(const SwitchStateRespon
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<TextSensorStateResponse>(msg, 27);
|
||||
}
|
||||
#endif
|
||||
@@ -136,35 +180,49 @@ bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsRe
|
||||
return this->send_message_<SubscribeLogsResponse>(msg, 29);
|
||||
}
|
||||
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<HomeassistantServiceResponse>(msg, 35);
|
||||
}
|
||||
bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
|
||||
const SubscribeHomeAssistantStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
|
||||
}
|
||||
bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<GetTimeRequest>(msg, 36);
|
||||
}
|
||||
bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<GetTimeResponse>(msg, 37);
|
||||
}
|
||||
bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
|
||||
}
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<CameraImageResponse>(msg, 44);
|
||||
}
|
||||
#endif
|
||||
@@ -172,87 +230,147 @@ bool APIServerConnectionBase::send_camera_image_response(const CameraImageRespon
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ClimateStateResponse>(msg, 47);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesNumberResponse>(msg, 49);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<NumberStateResponse>(msg, 50);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool APIServerConnectionBase::send_list_entities_select_response(const ListEntitiesSelectResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_select_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesSelectResponse>(msg, 52);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool APIServerConnectionBase::send_select_state_response(const SelectStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_select_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<SelectStateResponse>(msg, 53);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
#endif
|
||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
||||
switch (msg_type) {
|
||||
case 1: {
|
||||
HelloRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_hello_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_hello_request(msg);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
ConnectRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_connect_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_connect_request(msg);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
DisconnectRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_disconnect_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_disconnect_request(msg);
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
DisconnectResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_disconnect_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_disconnect_response(msg);
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
PingRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_ping_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_ping_request(msg);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
PingResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_ping_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_ping_response(msg);
|
||||
break;
|
||||
}
|
||||
case 9: {
|
||||
DeviceInfoRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_device_info_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_device_info_request(msg);
|
||||
break;
|
||||
}
|
||||
case 11: {
|
||||
ListEntitiesRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_list_entities_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_list_entities_request(msg);
|
||||
break;
|
||||
}
|
||||
case 20: {
|
||||
SubscribeStatesRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_states_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_states_request(msg);
|
||||
break;
|
||||
}
|
||||
case 28: {
|
||||
SubscribeLogsRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_logs_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_logs_request(msg);
|
||||
break;
|
||||
}
|
||||
@@ -260,7 +378,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#ifdef USE_COVER
|
||||
CoverCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_cover_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_cover_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
@@ -269,7 +389,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#ifdef USE_FAN
|
||||
FanCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_fan_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_fan_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
@@ -278,7 +400,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#ifdef USE_LIGHT
|
||||
LightCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_light_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_light_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
@@ -287,7 +411,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#ifdef USE_SWITCH
|
||||
SwitchCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_switch_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_switch_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
@@ -295,42 +421,54 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
case 34: {
|
||||
SubscribeHomeassistantServicesRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_homeassistant_services_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_homeassistant_services_request(msg);
|
||||
break;
|
||||
}
|
||||
case 36: {
|
||||
GetTimeRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_get_time_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_get_time_request(msg);
|
||||
break;
|
||||
}
|
||||
case 37: {
|
||||
GetTimeResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_get_time_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_get_time_response(msg);
|
||||
break;
|
||||
}
|
||||
case 38: {
|
||||
SubscribeHomeAssistantStatesRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_home_assistant_states_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_home_assistant_states_request(msg);
|
||||
break;
|
||||
}
|
||||
case 40: {
|
||||
HomeAssistantStateResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_home_assistant_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_home_assistant_state_response(msg);
|
||||
break;
|
||||
}
|
||||
case 42: {
|
||||
ExecuteServiceRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_execute_service_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_execute_service_request(msg);
|
||||
break;
|
||||
}
|
||||
@@ -338,7 +476,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
CameraImageRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_camera_image_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_camera_image_request(msg);
|
||||
#endif
|
||||
break;
|
||||
@@ -347,8 +487,32 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#ifdef USE_CLIMATE
|
||||
ClimateCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_climate_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 51: {
|
||||
#ifdef USE_NUMBER
|
||||
NumberCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_number_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_number_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 54: {
|
||||
#ifdef USE_SELECT
|
||||
SelectCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_select_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -547,6 +711,32 @@ void APIServerConnection::on_climate_command_request(const ClimateCommandRequest
|
||||
this->climate_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
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@@ -111,6 +111,24 @@ class APIServerConnectionBase : public ProtoService {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
virtual void on_climate_command_request(const ClimateCommandRequest &value){};
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool send_list_entities_number_response(const ListEntitiesNumberResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool send_number_state_response(const NumberStateResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
virtual void on_number_command_request(const NumberCommandRequest &value){};
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool send_list_entities_select_response(const ListEntitiesSelectResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool send_select_state_response(const SelectStateResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
virtual void on_select_command_request(const SelectCommandRequest &value){};
|
||||
#endif
|
||||
protected:
|
||||
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||
@@ -147,6 +165,12 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
virtual void climate_command(const ClimateCommandRequest &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
|
||||
protected:
|
||||
void on_hello_request(const HelloRequest &msg) override;
|
||||
@@ -179,6 +203,12 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#ifdef USE_CLIMATE
|
||||
void on_climate_command_request(const ClimateCommandRequest &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
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
static const char *TAG = "api";
|
||||
static const char *const TAG = "api";
|
||||
|
||||
// APIServer
|
||||
void APIServer::setup() {
|
||||
@@ -180,7 +180,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) {
|
||||
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
@@ -197,9 +197,27 @@ void APIServer::on_climate_update(climate::Climate *obj) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
void APIServer::on_number_update(number::Number *obj, float state) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
c->send_number_state(obj, state);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
void APIServer::on_select_update(select::Select *obj, const std::string &state) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
c->send_select_state(obj, state);
|
||||
}
|
||||
#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;
|
||||
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) {
|
||||
@@ -208,9 +226,11 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
|
||||
}
|
||||
}
|
||||
APIServer::APIServer() { global_api_server = this; }
|
||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f) {
|
||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(std::string)> f) {
|
||||
this->state_subs_.push_back(HomeAssistantStateSubscription{
|
||||
.entity_id = std::move(entity_id),
|
||||
.attribute = std::move(attribute),
|
||||
.callback = std::move(f),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "util.h"
|
||||
#include "list_entities.h"
|
||||
#include "subscribe_state.h"
|
||||
#include "homeassistant_service.h"
|
||||
#include "user_services.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
@@ -56,10 +55,16 @@ class APIServer : public Component, public Controller {
|
||||
void on_switch_update(switch_::Switch *obj, bool state) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) override;
|
||||
void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override;
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
void on_climate_update(climate::Climate *obj) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
void on_number_update(number::Number *obj, float state) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
void on_select_update(select::Select *obj, const std::string &state) override;
|
||||
#endif
|
||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
|
||||
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
|
||||
@@ -71,10 +76,12 @@ class APIServer : public Component, public Controller {
|
||||
|
||||
struct HomeAssistantStateSubscription {
|
||||
std::string entity_id;
|
||||
optional<std::string> attribute;
|
||||
std::function<void(std::string)> callback;
|
||||
};
|
||||
|
||||
void subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f);
|
||||
void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(std::string)> f);
|
||||
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
|
||||
const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
|
||||
|
||||
@@ -89,7 +96,7 @@ class APIServer : public Component, public Controller {
|
||||
std::vector<UserServiceDescriptor *> user_services_;
|
||||
};
|
||||
|
||||
extern APIServer *global_api_server;
|
||||
extern APIServer *global_api_server; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
|
||||
public:
|
||||
|
||||
@@ -76,13 +76,13 @@ class CustomAPIDevice {
|
||||
global_api_server->register_user_service(service);
|
||||
}
|
||||
|
||||
/** Subscribe to the state of an entity from Home Assistant.
|
||||
/** Subscribe to the state (or attribute state) of an entity from Home Assistant.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* ```cpp
|
||||
* void setup() override {
|
||||
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
|
||||
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "climate.kitchen", "current_temperature");
|
||||
* }
|
||||
*
|
||||
* void on_state_changed(std::string state) {
|
||||
@@ -93,17 +93,19 @@ class CustomAPIDevice {
|
||||
* @tparam T The class type creating the service, automatically deduced from the function pointer.
|
||||
* @param callback The member function to call when the entity state changes.
|
||||
* @param entity_id The entity_id to track.
|
||||
* @param attribute The entity state attribute to track.
|
||||
*/
|
||||
template<typename T>
|
||||
void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id) {
|
||||
void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id,
|
||||
const std::string &attribute = "") {
|
||||
auto f = std::bind(callback, (T *) this, std::placeholders::_1);
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, f);
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
|
||||
}
|
||||
|
||||
/** Subscribe to the state of an entity from Home Assistant.
|
||||
/** Subscribe to the state (or attribute state) of an entity from Home Assistant.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
*å
|
||||
* ```cpp
|
||||
* void setup() override {
|
||||
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
|
||||
@@ -117,11 +119,13 @@ class CustomAPIDevice {
|
||||
* @tparam T The class type creating the service, automatically deduced from the function pointer.
|
||||
* @param callback The member function to call when the entity state changes.
|
||||
* @param entity_id The entity_id to track.
|
||||
* @param attribute The entity state attribute to track.
|
||||
*/
|
||||
template<typename T>
|
||||
void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id) {
|
||||
void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id,
|
||||
const std::string &attribute = "") {
|
||||
auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, f);
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
|
||||
}
|
||||
|
||||
/** Call a Home Assistant service from ESPHome.
|
||||
|
||||
@@ -51,5 +51,13 @@ bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) {
|
||||
bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); }
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@@ -39,6 +39,12 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool on_climate(climate::Climate *climate) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool on_number(number::Number *number) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool on_select(select::Select *select) override;
|
||||
#endif
|
||||
bool on_end() override;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
static const char *TAG = "api.proto";
|
||||
static const char *const TAG = "api.proto";
|
||||
|
||||
void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
uint32_t i = 0;
|
||||
@@ -80,11 +80,13 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
std::string ProtoMessage::dump() const {
|
||||
std::string out;
|
||||
this->dump_to(out);
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
#define HAS_PROTO_MESSAGE_DUMP
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
@@ -243,8 +248,10 @@ class ProtoMessage {
|
||||
public:
|
||||
virtual void encode(ProtoWriteBuffer buffer) const = 0;
|
||||
void decode(const uint8_t *buffer, size_t length);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
std::string dump() const;
|
||||
virtual void dump_to(std::string &out) const = 0;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }
|
||||
|
||||
@@ -37,6 +37,16 @@ bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor)
|
||||
#ifdef USE_CLIMATE
|
||||
bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); }
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool InitialStateIterator::on_number(number::Number *number) {
|
||||
return this->client_->send_number_state(number, number->state);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool InitialStateIterator::on_select(select::Select *select) {
|
||||
return this->client_->send_select_state(select, select->state);
|
||||
}
|
||||
#endif
|
||||
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
|
||||
: ComponentIterator(server), client_(client) {}
|
||||
|
||||
|
||||
@@ -36,6 +36,12 @@ class InitialStateIterator : public ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool on_climate(climate::Climate *climate) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool on_number(number::Number *number) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool on_select(select::Select *select) override;
|
||||
#endif
|
||||
protected:
|
||||
APIConnection *client_;
|
||||
|
||||
@@ -15,7 +15,7 @@ template<> std::string get_execute_arg_value<std::string>(const ExecuteServiceAr
|
||||
template<> std::vector<bool> get_execute_arg_value<std::vector<bool>>(const ExecuteServiceArgument &arg) {
|
||||
return arg.bool_array;
|
||||
}
|
||||
template<> std::vector<int> get_execute_arg_value<std::vector<int>>(const ExecuteServiceArgument &arg) {
|
||||
template<> std::vector<int32_t> get_execute_arg_value<std::vector<int32_t>>(const ExecuteServiceArgument &arg) {
|
||||
return arg.int_array;
|
||||
}
|
||||
template<> std::vector<float> get_execute_arg_value<std::vector<float>>(const ExecuteServiceArgument &arg) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "api_pb2.h"
|
||||
@@ -20,8 +22,8 @@ template<typename T> enums::ServiceArgType to_service_arg_type();
|
||||
|
||||
template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
||||
public:
|
||||
UserServiceBase(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names)
|
||||
: name_(name), arg_names_(arg_names) {
|
||||
UserServiceBase(std::string name, const std::array<std::string, sizeof...(Ts)> &arg_names)
|
||||
: name_(std::move(name)), arg_names_(arg_names) {
|
||||
this->key_ = fnv1_hash(this->name_);
|
||||
}
|
||||
|
||||
|
||||
@@ -167,6 +167,36 @@ void ComponentIterator::advance() {
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
case IteratorState::NUMBER:
|
||||
if (this->at_ >= App.get_numbers().size()) {
|
||||
advance_platform = true;
|
||||
} else {
|
||||
auto *number = App.get_numbers()[this->at_];
|
||||
if (number->is_internal()) {
|
||||
success = true;
|
||||
break;
|
||||
} else {
|
||||
success = this->on_number(number);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
case IteratorState::SELECT:
|
||||
if (this->at_ >= App.get_selects().size()) {
|
||||
advance_platform = true;
|
||||
} else {
|
||||
auto *select = App.get_selects()[this->at_];
|
||||
if (select->is_internal()) {
|
||||
success = true;
|
||||
break;
|
||||
} else {
|
||||
success = this->on_select(select);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case IteratorState::MAX:
|
||||
if (this->on_end()) {
|
||||
|
||||
@@ -47,6 +47,12 @@ class ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
virtual bool on_climate(climate::Climate *climate) = 0;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
virtual bool on_number(number::Number *number) = 0;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
virtual bool on_select(select::Select *select) = 0;
|
||||
#endif
|
||||
virtual bool on_end();
|
||||
|
||||
@@ -81,6 +87,12 @@ class ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
CLIMATE,
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
NUMBER,
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
SELECT,
|
||||
#endif
|
||||
MAX,
|
||||
} state_{IteratorState::NONE};
|
||||
|
||||
@@ -11,7 +11,6 @@ from esphome.const import (
|
||||
CONF_DIV_RATIO,
|
||||
CONF_CAPACITANCE,
|
||||
)
|
||||
from esphome.core import coroutine
|
||||
|
||||
AUTO_LOAD = ["sensor", "binary_sensor"]
|
||||
MULTI_CONF = True
|
||||
@@ -40,11 +39,10 @@ AS3935_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
@coroutine
|
||||
def setup_as3935(var, config):
|
||||
yield cg.register_component(var, config)
|
||||
async def setup_as3935(var, config):
|
||||
await cg.register_component(var, config)
|
||||
|
||||
irq_pin = yield cg.gpio_pin_expression(config[CONF_IRQ_PIN])
|
||||
irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
|
||||
cg.add(var.set_irq_pin(irq_pin))
|
||||
cg.add(var.set_indoor(config[CONF_INDOOR]))
|
||||
cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL]))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace as3935 {
|
||||
|
||||
static const char *TAG = "as3935";
|
||||
static const char *const TAG = "as3935";
|
||||
|
||||
void AS3935Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up AS3935...");
|
||||
|
||||
@@ -12,7 +12,7 @@ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = yield cg.get_variable(config[CONF_AS3935_ID])
|
||||
var = yield binary_sensor.new_binary_sensor(config)
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_AS3935_ID])
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
cg.add(hub.set_thunder_alert_binary_sensor(var))
|
||||
|
||||
@@ -4,9 +4,8 @@ from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_DISTANCE,
|
||||
CONF_LIGHTNING_ENERGY,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_NONE,
|
||||
UNIT_KILOMETER,
|
||||
UNIT_EMPTY,
|
||||
ICON_SIGNAL_DISTANCE_VARIANT,
|
||||
ICON_FLASH,
|
||||
)
|
||||
@@ -18,24 +17,29 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
|
||||
cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
|
||||
UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1, DEVICE_CLASS_EMPTY
|
||||
unit_of_measurement=UNIT_KILOMETER,
|
||||
icon=ICON_SIGNAL_DISTANCE_VARIANT,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
),
|
||||
cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY
|
||||
icon=ICON_FLASH,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = yield cg.get_variable(config[CONF_AS3935_ID])
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_AS3935_ID])
|
||||
|
||||
if CONF_DISTANCE in config:
|
||||
conf = config[CONF_DISTANCE]
|
||||
distance_sensor = yield sensor.new_sensor(conf)
|
||||
distance_sensor = await sensor.new_sensor(conf)
|
||||
cg.add(hub.set_distance_sensor(distance_sensor))
|
||||
|
||||
if CONF_LIGHTNING_ENERGY in config:
|
||||
conf = config[CONF_LIGHTNING_ENERGY]
|
||||
lightning_energy_sensor = yield sensor.new_sensor(conf)
|
||||
lightning_energy_sensor = await sensor.new_sensor(conf)
|
||||
cg.add(hub.set_energy_sensor(lightning_energy_sensor))
|
||||
|
||||
@@ -20,7 +20,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield as3935.setup_as3935(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await as3935.setup_as3935(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace as3935_i2c {
|
||||
|
||||
static const char *TAG = "as3935_i2c";
|
||||
static const char *const TAG = "as3935_i2c";
|
||||
|
||||
void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_pos) {
|
||||
uint8_t write_reg;
|
||||
|
||||
@@ -20,7 +20,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield as3935.setup_as3935(var, config)
|
||||
yield spi.register_spi_device(var, config)
|
||||
await as3935.setup_as3935(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace as3935_spi {
|
||||
|
||||
static const char *TAG = "as3935_spi";
|
||||
static const char *const TAG = "as3935_spi";
|
||||
|
||||
void SPIAS3935Component::setup() {
|
||||
ESP_LOGI(TAG, "SPIAS3935Component setup started!");
|
||||
@@ -33,7 +33,7 @@ void SPIAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits,
|
||||
uint8_t SPIAS3935Component::read_register(uint8_t reg) {
|
||||
uint8_t value = 0;
|
||||
this->enable();
|
||||
this->write_byte(reg |= SPI_READ_M);
|
||||
this->write_byte(reg | SPI_READ_M);
|
||||
value = this->read_byte();
|
||||
// According to datsheet, the chip select must be written HIGH, LOW, HIGH
|
||||
// to correctly end the READ command.
|
||||
|
||||
@@ -6,7 +6,7 @@ CODEOWNERS = ["@OttoWinter"]
|
||||
|
||||
|
||||
@coroutine_with_priority(200.0)
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
if CORE.is_esp32:
|
||||
# https://github.com/esphome/AsyncTCP/blob/master/library.json
|
||||
cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace esphome {
|
||||
namespace atc_mithermometer {
|
||||
|
||||
static const char *TAG = "atc_mithermometer";
|
||||
static const char *const TAG = "atc_mithermometer";
|
||||
|
||||
void ATCMiThermometer::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "ATC MiThermometer");
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user