mirror of
https://github.com/esphome/esphome.git
synced 2026-06-24 16:56:44 +00:00
Compare commits
414 Commits
scheduler-
...
2026.5.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3d1a614e55 | ||
|
|
03e2eb4b4a | ||
|
|
ddd353d105 | ||
|
|
9a34a6aabb | ||
|
|
0babc52472 | ||
|
|
adde7681e8 | ||
|
|
8f6ea62628 | ||
|
|
4e7bc92061 | ||
|
|
1f4a061572 | ||
|
|
59db9a4673 | ||
|
|
7ae5566472 | ||
|
|
f247def4ac | ||
|
|
27d53ec117 | ||
|
|
0c94a173b6 | ||
|
|
ae2e372762 | ||
|
|
e6ed275746 | ||
|
|
878027ff50 | ||
|
|
858cfd5b94 | ||
|
|
5225416347 | ||
|
|
615d5aa827 | ||
|
|
e92a4c9472 | ||
|
|
32fa856bf0 | ||
|
|
cc88456ce7 | ||
|
|
79539cb85d | ||
|
|
16b6509a03 | ||
|
|
15c546b809 | ||
|
|
104c8bed41 | ||
|
|
ca859de212 | ||
|
|
de783e72d5 | ||
|
|
cd7e2d79c4 | ||
|
|
ecf823b871 | ||
|
|
9fdad68138 | ||
|
|
b79a306d02 | ||
|
|
94badfcb19 | ||
|
|
19c4da2aa5 | ||
|
|
e4c8d1f430 | ||
|
|
302938f875 | ||
|
|
65e1e210de | ||
|
|
43cc9fc879 | ||
|
|
41ad2ba763 | ||
|
|
25739091da | ||
|
|
bbf5fe8450 | ||
|
|
e9ef58d99d | ||
|
|
e1793a1eff | ||
|
|
9bb70d568d | ||
|
|
213df0412d | ||
|
|
cdf74c180e | ||
|
|
df31c72e4e | ||
|
|
4f188bf9bb | ||
|
|
20f92ad5e9 | ||
|
|
f301e90fd9 | ||
|
|
2dbaaf1efd | ||
|
|
da237b5070 | ||
|
|
6a8f24b951 | ||
|
|
26907f17f5 | ||
|
|
c6a74222f1 | ||
|
|
5ec0879a10 | ||
|
|
50495c7085 | ||
|
|
25dbef83de | ||
|
|
4f895425ca | ||
|
|
c037058c19 | ||
|
|
ecac6b64ec | ||
|
|
3831aa809f | ||
|
|
da8286f554 | ||
|
|
d5c6efb2fe | ||
|
|
dd1818661c | ||
|
|
fb659f9ac4 | ||
|
|
ab273a1f8f | ||
|
|
84b5931299 | ||
|
|
c863d58999 | ||
|
|
59847d56e8 | ||
|
|
09a926fa13 | ||
|
|
a3b6f92433 | ||
|
|
06786da7dd | ||
|
|
910cc38dd7 | ||
|
|
c8aba6913b | ||
|
|
ce8810bc42 | ||
|
|
1c6966b761 | ||
|
|
03f5e4775c | ||
|
|
445d841229 | ||
|
|
d7b00047bd | ||
|
|
3fee97ae5a | ||
|
|
cb520cda6b | ||
|
|
8bce32ec35 | ||
|
|
b866525437 | ||
|
|
0e4922a340 | ||
|
|
45a4811bb4 | ||
|
|
65ea29b44a | ||
|
|
480c23012c | ||
|
|
1dfd3fe9c2 | ||
|
|
f94735dc62 | ||
|
|
65b53692bd | ||
|
|
3df0527c1f | ||
|
|
dc95b22c76 | ||
|
|
1c2043e054 | ||
|
|
7f37ee3c53 | ||
|
|
8b6cbc9f2b | ||
|
|
531367d7e1 | ||
|
|
cb2dbcd70d | ||
|
|
45a8bd49c3 | ||
|
|
9195b9898e | ||
|
|
aec48cf231 | ||
|
|
907ae46aba | ||
|
|
057fc4c1a8 | ||
|
|
76d3433425 | ||
|
|
b512cc42a8 | ||
|
|
66e4a1dfa8 | ||
|
|
ee72efa760 | ||
|
|
f54480ec48 | ||
|
|
34f69e0d6e | ||
|
|
57893a8eb1 | ||
|
|
c511dddf2a | ||
|
|
76ce45c59e | ||
|
|
727c74da3f | ||
|
|
365ed19319 | ||
|
|
4ff946ac15 | ||
|
|
b5e50144e3 | ||
|
|
49df1bd30e | ||
|
|
7dce58c58d | ||
|
|
a232aedebd | ||
|
|
4e31b71304 | ||
|
|
55ef66cc26 | ||
|
|
e479e8b641 | ||
|
|
ee8ca2a3bf | ||
|
|
a52ca4f80a | ||
|
|
4ac7bc4606 | ||
|
|
8cf0eba043 | ||
|
|
30e2f7e8e9 | ||
|
|
a7299cb95b | ||
|
|
2edb7ca5c2 | ||
|
|
68534ea24d | ||
|
|
105842366e | ||
|
|
4d9d6e02e5 | ||
|
|
b967adeb9d | ||
|
|
e4d9786f00 | ||
|
|
267836d098 | ||
|
|
c82adc3892 | ||
|
|
fe66f9ba41 | ||
|
|
5dadfe6367 | ||
|
|
c4e85fbfc1 | ||
|
|
e9cc10fedc | ||
|
|
66e2dcffc4 | ||
|
|
17080ddce6 | ||
|
|
930d539969 | ||
|
|
3c042e2e44 | ||
|
|
ed10fbea3e | ||
|
|
1365251365 | ||
|
|
3abf2c99a2 | ||
|
|
70b9edfabe | ||
|
|
88c2a1c096 | ||
|
|
3d8fffbea9 | ||
|
|
eb52ca61fe | ||
|
|
a970f05b69 | ||
|
|
7b6e2589f1 | ||
|
|
696a654733 | ||
|
|
08b17c9da1 | ||
|
|
e152c6155b | ||
|
|
6ffcb821ca | ||
|
|
ded83812f4 | ||
|
|
ab1d2de78e | ||
|
|
cbe192df49 | ||
|
|
56ef357162 | ||
|
|
4e4e4b4411 | ||
|
|
06bd92c388 | ||
|
|
71193e2b2c | ||
|
|
9301f76482 | ||
|
|
004aa49131 | ||
|
|
3b6250bcee | ||
|
|
a7b4a2006a | ||
|
|
09dc41435c | ||
|
|
5283cdec12 | ||
|
|
d9835c8705 | ||
|
|
b89c71c1ea | ||
|
|
7f6aef4f33 | ||
|
|
016b509b55 | ||
|
|
d2bbaeccf3 | ||
|
|
1e58e8729a | ||
|
|
6173656bf8 | ||
|
|
0d94ffe15d | ||
|
|
4da62067cf | ||
|
|
9f49e3f80e | ||
|
|
fc25ab0246 | ||
|
|
a4a57a540d | ||
|
|
cfd2c9182c | ||
|
|
2864922ac0 | ||
|
|
90693fb39a | ||
|
|
6e1a59da3e | ||
|
|
545ee03f42 | ||
|
|
caaa1aefc7 | ||
|
|
ff0c5f575e | ||
|
|
f06ad8c436 | ||
|
|
29db5fa4bb | ||
|
|
85f33978e7 | ||
|
|
79786f1cc7 | ||
|
|
febf8815c7 | ||
|
|
6f6d991dd2 | ||
|
|
e9f7579910 | ||
|
|
a99c1b3e08 | ||
|
|
2d6af1f7e5 | ||
|
|
700676b340 | ||
|
|
bf1c339dc1 | ||
|
|
f5c1b8839d | ||
|
|
4404dd68ba | ||
|
|
67491c3194 | ||
|
|
39b2b901f7 | ||
|
|
f30ad588ea | ||
|
|
be82e8faeb | ||
|
|
57397a318a | ||
|
|
87a705b1cc | ||
|
|
edbb9f7b28 | ||
|
|
efff8fe8be | ||
|
|
f248302370 | ||
|
|
6fda5f41b2 | ||
|
|
ea2b2b3920 | ||
|
|
f33d137669 | ||
|
|
d28498ac2c | ||
|
|
556783b95b | ||
|
|
857e529803 | ||
|
|
197d4dac8e | ||
|
|
2d7f9dc48d | ||
|
|
be84e6c9f4 | ||
|
|
0418f2138a | ||
|
|
d9c22d6b56 | ||
|
|
60a94fd109 | ||
|
|
9371ec319a | ||
|
|
ce466c6b60 | ||
|
|
a460f5343c | ||
|
|
7c2a63bf82 | ||
|
|
690a197346 | ||
|
|
24d4da1021 | ||
|
|
53c4e6f386 | ||
|
|
1d63158480 | ||
|
|
013dee44eb | ||
|
|
9ddb828da3 | ||
|
|
120d1e51fb | ||
|
|
844a36f7a1 | ||
|
|
72a75f2d3f | ||
|
|
b5eb444015 | ||
|
|
15ab5422c7 | ||
|
|
33f88619da | ||
|
|
df1200629f | ||
|
|
41bd570d30 | ||
|
|
cf223674e5 | ||
|
|
af74b639cf | ||
|
|
4108b27197 | ||
|
|
7cfab58a05 | ||
|
|
85e1e4b95e | ||
|
|
0f174ee626 | ||
|
|
8046ff7e1e | ||
|
|
5e9db1c8c6 | ||
|
|
81d147ff9e | ||
|
|
58cb7effd4 | ||
|
|
3dd60c5713 | ||
|
|
f073c1cabe | ||
|
|
5cc447e0da | ||
|
|
0980630f68 | ||
|
|
b8dfffdf06 | ||
|
|
f6e39d305d | ||
|
|
08e5cb5576 | ||
|
|
faa61696e0 | ||
|
|
9999913d07 | ||
|
|
92aa98f680 | ||
|
|
3d69169141 | ||
|
|
24fdfcf1a1 | ||
|
|
550444dc34 | ||
|
|
ba7c06785a | ||
|
|
b708d1a826 | ||
|
|
148d478dec | ||
|
|
45e78e4114 | ||
|
|
3b3e003aa3 | ||
|
|
2f3e16b482 | ||
|
|
e085cb50d9 | ||
|
|
2fbfb4c385 | ||
|
|
61261b4a59 | ||
|
|
d48aad8c4d | ||
|
|
f1d3be4bda | ||
|
|
2758aa5517 | ||
|
|
a8b0133ec1 | ||
|
|
1398dcebb4 | ||
|
|
096d0c4279 | ||
|
|
e127268dac | ||
|
|
f0bffed3c0 | ||
|
|
1a871e231d | ||
|
|
47765bd2d0 | ||
|
|
8066325e0b | ||
|
|
b8d24c9e49 | ||
|
|
9b1f5c59bb | ||
|
|
e4b33fddf5 | ||
|
|
77da64a367 | ||
|
|
cecccebc64 | ||
|
|
53b682e48f | ||
|
|
14910e65d9 | ||
|
|
813964714c | ||
|
|
5a146ab6b7 | ||
|
|
61a41402df | ||
|
|
59b4cfd07c | ||
|
|
c41f38e16d | ||
|
|
0ad8a071a7 | ||
|
|
985dba9332 | ||
|
|
ca3f7251d4 | ||
|
|
44cabc191d | ||
|
|
e5b1991cf7 | ||
|
|
7fba57ce51 | ||
|
|
69a33d8ac0 | ||
|
|
ce61dcf387 | ||
|
|
bae6b51652 | ||
|
|
557c3d4436 | ||
|
|
bacee89bca | ||
|
|
2157d11913 | ||
|
|
42b8597719 | ||
|
|
2bd28eee9d | ||
|
|
0a497d3c22 | ||
|
|
79da2b9704 | ||
|
|
ae5b211c89 | ||
|
|
8ceada8d04 | ||
|
|
49c7a6928e | ||
|
|
2fce71e0d4 | ||
|
|
80251c54be | ||
|
|
0d51a122d0 | ||
|
|
5a33c50015 | ||
|
|
0d150dc57e | ||
|
|
d287876d8d | ||
|
|
592486ae9a | ||
|
|
c3bd38af77 | ||
|
|
eec770d622 | ||
|
|
d7b21a84a3 | ||
|
|
f05243bd9d | ||
|
|
35cb28edfe | ||
|
|
1363f661e6 | ||
|
|
8af499b591 | ||
|
|
1a57d9bc2f | ||
|
|
9768380856 | ||
|
|
676f26919e | ||
|
|
29d3a3a498 | ||
|
|
77b76ac48a | ||
|
|
0b5835284a | ||
|
|
15df477472 | ||
|
|
be0ee73847 | ||
|
|
a241c9e622 | ||
|
|
2f433c78bd | ||
|
|
e39c474577 | ||
|
|
a62e3fe4fc | ||
|
|
7d6b9bee19 | ||
|
|
ab6bda50e4 | ||
|
|
3d195d748c | ||
|
|
16cf4fb5e8 | ||
|
|
70503442f4 | ||
|
|
594b269dba | ||
|
|
8157c721a5 | ||
|
|
9af557de6d | ||
|
|
1f4136e76f | ||
|
|
c8dffcc9b8 | ||
|
|
44fbb7f5a9 | ||
|
|
eb01d43feb | ||
|
|
7891fd5cf1 | ||
|
|
4ee9cc432b | ||
|
|
42ff10afe5 | ||
|
|
6b3df66bdc | ||
|
|
968878a62d | ||
|
|
daf3f4d2f1 | ||
|
|
52e8c50f45 | ||
|
|
0a4d9b430f | ||
|
|
0759a3c681 | ||
|
|
8921e3bb3f | ||
|
|
52f80618d4 | ||
|
|
876c8c4c2a | ||
|
|
41458d72e0 | ||
|
|
49d3df2698 | ||
|
|
792f2e8363 | ||
|
|
42c9fdc87e | ||
|
|
5f6bbb98ce | ||
|
|
4e0509435a | ||
|
|
a03de7cea2 | ||
|
|
95b5ab7e78 | ||
|
|
3ac0939f55 | ||
|
|
191d3bc7e4 | ||
|
|
a186f6fea9 | ||
|
|
aea88aef5e | ||
|
|
433bbdb016 | ||
|
|
4137d93cbf | ||
|
|
6a5919ee87 | ||
|
|
b753ee4e94 | ||
|
|
c26ea52620 | ||
|
|
39a69385fb | ||
|
|
a34836c290 | ||
|
|
01ac223913 | ||
|
|
7198c912c7 | ||
|
|
24c6a0d711 | ||
|
|
dec5d0449b | ||
|
|
79b741b8dc | ||
|
|
112646a9c4 | ||
|
|
2e096bb036 | ||
|
|
e87e78c544 | ||
|
|
0f25d91e68 | ||
|
|
8dbdcfc128 | ||
|
|
8950afc3c4 | ||
|
|
04d067196d | ||
|
|
502c010465 | ||
|
|
180105bb4b | ||
|
|
4c0dfb0e0d | ||
|
|
df987a7ffb | ||
|
|
c8d4420408 | ||
|
|
b084fa4490 | ||
|
|
68625a1b76 | ||
|
|
dc57969afd | ||
|
|
f092e619d8 | ||
|
|
58f6ad2d0c | ||
|
|
bc33260c61 | ||
|
|
4cab262ef8 | ||
|
|
9ad820c921 | ||
|
|
4f8feb86f0 | ||
|
|
b5ccd55f4e | ||
|
|
a437b3086b | ||
|
|
c27f9e512b |
35
.clang-tidy
35
.clang-tidy
@@ -5,24 +5,30 @@ Checks: >-
|
||||
-altera-*,
|
||||
-android-*,
|
||||
-boost-*,
|
||||
-bugprone-derived-method-shadowing-base-method,
|
||||
-bugprone-easily-swappable-parameters,
|
||||
-bugprone-implicit-widening-of-multiplication-result,
|
||||
-bugprone-invalid-enum-default-initialization,
|
||||
-bugprone-multi-level-implicit-pointer-conversion,
|
||||
-bugprone-narrowing-conversions,
|
||||
-bugprone-tagged-union-member-count,
|
||||
-bugprone-signed-char-misuse,
|
||||
-bugprone-switch-missing-default-case,
|
||||
-cert-dcl50-cpp,
|
||||
-cert-err33-c,
|
||||
-cert-err58-cpp,
|
||||
-cert-int09-c,
|
||||
-cert-oop57-cpp,
|
||||
-cert-str34-c,
|
||||
-clang-analyzer-optin.core.EnumCastOutOfRange,
|
||||
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
||||
-clang-analyzer-osx.*,
|
||||
-clang-analyzer-security.ArrayBound,
|
||||
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
||||
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
||||
-clang-diagnostic-deprecated-declarations,
|
||||
-clang-diagnostic-ignored-optimization-argument,
|
||||
-clang-diagnostic-missing-designated-field-initializers,
|
||||
-clang-diagnostic-missing-field-initializers,
|
||||
-clang-diagnostic-shadow-field,
|
||||
-clang-diagnostic-unused-const-variable,
|
||||
@@ -42,6 +48,7 @@ Checks: >-
|
||||
-cppcoreguidelines-owning-memory,
|
||||
-cppcoreguidelines-prefer-member-initializer,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
||||
-cppcoreguidelines-pro-type-const-cast,
|
||||
@@ -54,12 +61,13 @@ Checks: >-
|
||||
-cppcoreguidelines-rvalue-reference-param-not-moved,
|
||||
-cppcoreguidelines-special-member-functions,
|
||||
-cppcoreguidelines-use-default-member-init,
|
||||
-cppcoreguidelines-use-enum-class,
|
||||
-cppcoreguidelines-virtual-class-destructor,
|
||||
-fuchsia-default-arguments-calls,
|
||||
-fuchsia-default-arguments-declarations,
|
||||
-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,
|
||||
@@ -71,49 +79,64 @@ Checks: >-
|
||||
-llvm-else-after-return,
|
||||
-llvm-header-guard,
|
||||
-llvm-include-order,
|
||||
-llvm-prefer-static-over-anonymous-namespace,
|
||||
-llvm-qualified-auto,
|
||||
-llvm-use-ranges,
|
||||
-llvmlibc-*,
|
||||
-misc-const-correctness,
|
||||
-misc-include-cleaner,
|
||||
-misc-multiple-inheritance,
|
||||
-misc-no-recursion,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-misc-override-with-different-visibility,
|
||||
-misc-unused-parameters,
|
||||
-misc-use-anonymous-namespace,
|
||||
-misc-use-internal-linkage,
|
||||
-modernize-avoid-bind,
|
||||
-modernize-avoid-variadic-functions,
|
||||
-modernize-avoid-c-arrays,
|
||||
-modernize-concat-nested-namespaces,
|
||||
-modernize-avoid-c-style-cast,
|
||||
-modernize-macro-to-enum,
|
||||
-modernize-return-braced-init-list,
|
||||
-modernize-type-traits,
|
||||
-modernize-use-auto,
|
||||
-modernize-use-constraints,
|
||||
-modernize-use-default-member-init,
|
||||
-modernize-use-designated-initializers,
|
||||
-modernize-use-equals-default,
|
||||
-modernize-use-integer-sign-comparison,
|
||||
-modernize-use-nodiscard,
|
||||
-modernize-use-nullptr,
|
||||
-modernize-use-nodiscard,
|
||||
-modernize-use-nullptr,
|
||||
-modernize-use-ranges,
|
||||
-modernize-use-trailing-return-type,
|
||||
-mpi-*,
|
||||
-objc-*,
|
||||
-performance-enum-size,
|
||||
-portability-avoid-pragma-once,
|
||||
-portability-template-virtual-member-function,
|
||||
-readability-ambiguous-smartptr-reset-call,
|
||||
-readability-avoid-nested-conditional-operator,
|
||||
-readability-container-contains,
|
||||
-readability-container-data-pointer,
|
||||
-readability-convert-member-functions-to-static,
|
||||
-readability-else-after-return,
|
||||
-readability-enum-initial-value,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-implicit-bool-conversion,
|
||||
-readability-isolate-declaration,
|
||||
-readability-magic-numbers,
|
||||
-readability-make-member-function-const,
|
||||
-readability-math-missing-parentheses,
|
||||
-readability-named-parameter,
|
||||
-readability-redundant-casting,
|
||||
-readability-redundant-inline-specifier,
|
||||
-readability-redundant-member-init,
|
||||
-readability-redundant-string-init,
|
||||
-readability-redundant-parentheses,
|
||||
-readability-redundant-typename,
|
||||
-readability-uppercase-literal-suffix,
|
||||
-readability-use-anyofallof,
|
||||
-readability-use-std-min-max,
|
||||
-readability-use-concise-preprocessor-directives,
|
||||
WarningsAsErrors: '*'
|
||||
FormatStyle: google
|
||||
CheckOptions:
|
||||
|
||||
@@ -1 +1 @@
|
||||
1b1ce6324c50c4595703c7df0a8a479b4fe84b71ff1a8793cce1a16f17a33324
|
||||
593fd53fa09944a59af3f38521e31d87fe10b60326b8d82bb76413c5149b312c
|
||||
|
||||
@@ -115,4 +115,4 @@ examples/
|
||||
Dockerfile
|
||||
.git/
|
||||
tests/
|
||||
.*
|
||||
.?*
|
||||
|
||||
2
.github/copilot-instructions.md
vendored
2
.github/copilot-instructions.md
vendored
@@ -1 +1 @@
|
||||
../.ai/instructions.md
|
||||
../AGENTS.md
|
||||
105
.github/scripts/auto-label-pr/detectors.js
vendored
105
.github/scripts/auto-label-pr/detectors.js
vendored
@@ -1,4 +1,3 @@
|
||||
const fs = require('fs');
|
||||
const { DOCS_PR_PATTERNS } = require('./constants');
|
||||
const {
|
||||
COMPONENT_REGEX,
|
||||
@@ -9,6 +8,31 @@ const {
|
||||
} = require('../detect-tags');
|
||||
const { loadCodeowners, getEffectiveOwners } = require('../codeowners');
|
||||
|
||||
// Top-level `CONFIG_SCHEMA = ...` (assignment) or `CONFIG_SCHEMA: ConfigType = ...` (annotation).
|
||||
// Ruff/Black enforce exactly one space around `=` and no space before `:`,
|
||||
// so we can match strictly: `CONFIG_SCHEMA ` or `CONFIG_SCHEMA:`.
|
||||
const CONFIG_SCHEMA_REGEX = /^CONFIG_SCHEMA[ :]/m;
|
||||
|
||||
// Fetch a file's contents from the PR head SHA via the GitHub API.
|
||||
// The auto-label workflow runs on `pull_request_target`, which checks out the
|
||||
// base branch — files added by the PR don't exist in the workspace, so we have
|
||||
// to fetch them from the head SHA. Returns null if the file can't be fetched.
|
||||
async function fetchPrFileContent(github, context, path) {
|
||||
try {
|
||||
const { owner, repo } = context.repo;
|
||||
const { data } = await github.rest.repos.getContent({
|
||||
owner,
|
||||
repo,
|
||||
path,
|
||||
ref: context.payload.pull_request.head.sha,
|
||||
});
|
||||
return Buffer.from(data.content, 'base64').toString('utf8');
|
||||
} catch (error) {
|
||||
console.log(`Failed to fetch ${path} from PR head:`, error.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Strategy: Merge branch detection
|
||||
async function detectMergeBranch(context) {
|
||||
const labels = new Set();
|
||||
@@ -45,52 +69,64 @@ async function detectComponentPlatforms(changedFiles, apiData) {
|
||||
}
|
||||
|
||||
// Strategy: New component detection
|
||||
async function detectNewComponents(prFiles) {
|
||||
async function detectNewComponents(github, context, prFiles) {
|
||||
const labels = new Set();
|
||||
let hasYamlLoadable = false;
|
||||
const addedFiles = prFiles.filter(file => file.status === 'added').map(file => file.filename);
|
||||
|
||||
for (const file of addedFiles) {
|
||||
const componentMatch = file.match(/^esphome\/components\/([^\/]+)\/__init__\.py$/);
|
||||
if (componentMatch) {
|
||||
try {
|
||||
const content = fs.readFileSync(file, 'utf8');
|
||||
if (content.includes('IS_TARGET_PLATFORM = True')) {
|
||||
labels.add('new-target-platform');
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Failed to read content of ${file}:`, error.message);
|
||||
}
|
||||
labels.add('new-component');
|
||||
if (!componentMatch) continue;
|
||||
|
||||
labels.add('new-component');
|
||||
const content = await fetchPrFileContent(github, context, file);
|
||||
if (content === null) {
|
||||
// Safe default: assume YAML-loadable so needs-docs behaviour is unchanged on fetch failure
|
||||
hasYamlLoadable = true;
|
||||
continue;
|
||||
}
|
||||
if (content.includes('IS_TARGET_PLATFORM = True')) {
|
||||
labels.add('new-target-platform');
|
||||
}
|
||||
if (CONFIG_SCHEMA_REGEX.test(content)) {
|
||||
hasYamlLoadable = true;
|
||||
}
|
||||
}
|
||||
|
||||
return labels;
|
||||
return { labels, hasYamlLoadable };
|
||||
}
|
||||
|
||||
// Strategy: New platform detection
|
||||
async function detectNewPlatforms(prFiles, apiData) {
|
||||
async function detectNewPlatforms(github, context, prFiles, apiData) {
|
||||
const labels = new Set();
|
||||
let hasYamlLoadable = false;
|
||||
const addedFiles = prFiles.filter(file => file.status === 'added').map(file => file.filename);
|
||||
|
||||
for (const file of addedFiles) {
|
||||
const platformFileMatch = file.match(/^esphome\/components\/([^\/]+)\/([^\/]+)\.py$/);
|
||||
if (platformFileMatch) {
|
||||
const [, component, platform] = platformFileMatch;
|
||||
if (apiData.platformComponents.includes(platform)) {
|
||||
labels.add('new-platform');
|
||||
}
|
||||
}
|
||||
const platformPathPatterns = [
|
||||
/^esphome\/components\/([^\/]+)\/([^\/]+)\.py$/,
|
||||
/^esphome\/components\/([^\/]+)\/([^\/]+)\/__init__\.py$/,
|
||||
];
|
||||
|
||||
const platformDirMatch = file.match(/^esphome\/components\/([^\/]+)\/([^\/]+)\/__init__\.py$/);
|
||||
if (platformDirMatch) {
|
||||
const [, component, platform] = platformDirMatch;
|
||||
if (apiData.platformComponents.includes(platform)) {
|
||||
labels.add('new-platform');
|
||||
for (const file of addedFiles) {
|
||||
for (const re of platformPathPatterns) {
|
||||
const match = file.match(re);
|
||||
if (!match) continue;
|
||||
const platform = match[2];
|
||||
if (!apiData.platformComponents.includes(platform)) break;
|
||||
|
||||
labels.add('new-platform');
|
||||
const content = await fetchPrFileContent(github, context, file);
|
||||
if (content === null) {
|
||||
// Safe default: assume YAML-loadable so needs-docs behaviour is unchanged on fetch failure
|
||||
hasYamlLoadable = true;
|
||||
} else if (CONFIG_SCHEMA_REGEX.test(content)) {
|
||||
hasYamlLoadable = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return labels;
|
||||
return { labels, hasYamlLoadable };
|
||||
}
|
||||
|
||||
// Strategy: Core files detection
|
||||
@@ -300,7 +336,7 @@ function detectMaintainerAccess(context) {
|
||||
}
|
||||
|
||||
// Strategy: Requirements detection
|
||||
async function detectRequirements(allLabels, prFiles, context) {
|
||||
async function detectRequirements(allLabels, prFiles, context, hasYamlLoadable) {
|
||||
const labels = new Set();
|
||||
|
||||
// Check for missing tests
|
||||
@@ -308,8 +344,15 @@ async function detectRequirements(allLabels, prFiles, context) {
|
||||
labels.add('needs-tests');
|
||||
}
|
||||
|
||||
// Check for missing docs
|
||||
if (allLabels.has('new-component') || allLabels.has('new-platform') || allLabels.has('new-feature')) {
|
||||
// Check for missing docs.
|
||||
// `new-feature` (PR-body checkbox) always counts. `new-component` / `new-platform`
|
||||
// only count when at least one newly added file defines a top-level CONFIG_SCHEMA,
|
||||
// i.e. the new component/platform is actually loadable from YAML.
|
||||
const docsEligible =
|
||||
allLabels.has('new-feature') ||
|
||||
((allLabels.has('new-component') || allLabels.has('new-platform')) && hasYamlLoadable);
|
||||
|
||||
if (docsEligible) {
|
||||
const prBody = context.payload.pull_request.body || '';
|
||||
const hasDocsLink = DOCS_PR_PATTERNS.some(pattern => pattern.test(prBody));
|
||||
|
||||
|
||||
17
.github/scripts/auto-label-pr/index.js
vendored
17
.github/scripts/auto-label-pr/index.js
vendored
@@ -106,8 +106,8 @@ module.exports = async ({ github, context }) => {
|
||||
const [
|
||||
branchLabels,
|
||||
componentLabels,
|
||||
newComponentLabels,
|
||||
newPlatformLabels,
|
||||
newComponentResult,
|
||||
newPlatformResult,
|
||||
coreLabels,
|
||||
sizeLabels,
|
||||
dashboardLabels,
|
||||
@@ -120,8 +120,8 @@ module.exports = async ({ github, context }) => {
|
||||
] = await Promise.all([
|
||||
detectMergeBranch(context),
|
||||
detectComponentPlatforms(changedFiles, apiData),
|
||||
detectNewComponents(prFiles),
|
||||
detectNewPlatforms(prFiles, apiData),
|
||||
detectNewComponents(github, context, prFiles),
|
||||
detectNewPlatforms(github, context, prFiles, apiData),
|
||||
detectCoreChanges(changedFiles),
|
||||
detectPRSize(prFiles, totalAdditions, totalDeletions, totalChanges, isMegaPR, SMALL_PR_THRESHOLD, MEDIUM_PR_THRESHOLD, TOO_BIG_THRESHOLD),
|
||||
detectDashboardChanges(changedFiles),
|
||||
@@ -133,6 +133,13 @@ module.exports = async ({ github, context }) => {
|
||||
detectMaintainerAccess(context)
|
||||
]);
|
||||
|
||||
// Extract new-component / new-platform results
|
||||
const newComponentLabels = newComponentResult.labels;
|
||||
const newPlatformLabels = newPlatformResult.labels;
|
||||
// Eligible for needs-docs only if any newly added component or platform file
|
||||
// defines a top-level CONFIG_SCHEMA (i.e. is actually loadable from YAML).
|
||||
const hasYamlLoadable = newComponentResult.hasYamlLoadable || newPlatformResult.hasYamlLoadable;
|
||||
|
||||
// Extract deprecated component info
|
||||
const deprecatedLabels = deprecatedResult.labels;
|
||||
const deprecatedInfo = deprecatedResult.deprecatedInfo;
|
||||
@@ -154,7 +161,7 @@ module.exports = async ({ github, context }) => {
|
||||
]);
|
||||
|
||||
// Detect requirements based on all other labels
|
||||
const requirementLabels = await detectRequirements(allLabels, prFiles, context);
|
||||
const requirementLabels = await detectRequirements(allLabels, prFiles, context, hasYamlLoadable);
|
||||
for (const label of requirementLabels) {
|
||||
allLabels.add(label);
|
||||
}
|
||||
|
||||
13
.github/workflows/auto-label-pr.yml
vendored
13
.github/workflows/auto-label-pr.yml
vendored
@@ -6,9 +6,10 @@ on:
|
||||
pull_request_target:
|
||||
types: [labeled, opened, reopened, synchronize, edited]
|
||||
|
||||
# All PR/label/review writes are performed with the App token minted below,
|
||||
# so the workflow's GITHUB_TOKEN only needs read access for checkout.
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
contents: read # actions/checkout reads the workflow source
|
||||
|
||||
env:
|
||||
SMALL_PR_THRESHOLD: 30
|
||||
@@ -27,10 +28,14 @@ jobs:
|
||||
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v2
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
app-id: ${{ secrets.ESPHOME_GITHUB_APP_ID }}
|
||||
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
|
||||
# Scope the minted App token to the minimum needed by auto-label-pr/*.js.
|
||||
permission-contents: read # repos.getContent for CODEOWNERS and file lookups in detectors.js
|
||||
permission-issues: write # listLabelsOnIssue, addLabels, removeLabel, list/createComment
|
||||
permission-pull-requests: write # pulls.listFiles, list/create/update/dismissReview
|
||||
|
||||
- name: Auto Label PR
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
|
||||
4
.github/workflows/ci-api-proto.yml
vendored
4
.github/workflows/ci-api-proto.yml
vendored
@@ -12,8 +12,8 @@ on:
|
||||
- ".github/workflows/ci-api-proto.yml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
contents: read # actions/checkout for the PR head
|
||||
pull-requests: write # pulls.createReview / listReviews / dismissReview when generated proto files are stale
|
||||
|
||||
jobs:
|
||||
check:
|
||||
|
||||
4
.github/workflows/ci-clang-tidy-hash.yml
vendored
4
.github/workflows/ci-clang-tidy-hash.yml
vendored
@@ -12,8 +12,8 @@ on:
|
||||
- ".github/workflows/ci-clang-tidy-hash.yml"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
contents: read # actions/checkout for the PR head
|
||||
pull-requests: write # pulls.createReview / listReviews / dismissReview when the clang-tidy hash is out of date
|
||||
|
||||
jobs:
|
||||
verify-hash:
|
||||
|
||||
3
.github/workflows/ci-docker.yml
vendored
3
.github/workflows/ci-docker.yml
vendored
@@ -22,8 +22,7 @@ on:
|
||||
- "script/platformio_install_deps.py"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read
|
||||
contents: read # actions/checkout only; the build does not push images
|
||||
|
||||
concurrency:
|
||||
# yamllint disable-line rule:line-length
|
||||
|
||||
@@ -7,9 +7,9 @@ on:
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
actions: read
|
||||
contents: read # actions/checkout of the base repo at the PR's target branch
|
||||
pull-requests: write # gh api to look up the PR by head SHA and post/update the memory-impact comment
|
||||
actions: read # gh run download for the memory-analysis artifacts produced by the CI workflow run
|
||||
|
||||
jobs:
|
||||
memory-impact-comment:
|
||||
|
||||
258
.github/workflows/ci.yml
vendored
258
.github/workflows/ci.yml
vendored
@@ -17,7 +17,7 @@ on:
|
||||
merge_group:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
contents: read # actions/checkout for all jobs; individual jobs add their own scopes when they need to write
|
||||
|
||||
env:
|
||||
DEFAULT_PYTHON: "3.11"
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Generate cache-key
|
||||
id: cache-key
|
||||
run: echo key="${{ hashFiles('requirements.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT
|
||||
run: echo key="${{ hashFiles('requirements.txt', 'requirements_dev.txt', 'requirements_test.txt', '.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT
|
||||
- name: Set up Python ${{ env.DEFAULT_PYTHON }}
|
||||
id: python
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
python -m venv venv
|
||||
. venv/bin/activate
|
||||
python --version
|
||||
pip install -r requirements.txt -r requirements_test.txt pre-commit
|
||||
pip install -r requirements.txt -r requirements_dev.txt -r requirements_test.txt pre-commit
|
||||
pip install -e .
|
||||
|
||||
pylint:
|
||||
@@ -108,6 +108,81 @@ jobs:
|
||||
script/generate-esp32-boards.py --check
|
||||
script/generate-rp2040-boards.py --check
|
||||
|
||||
import-time:
|
||||
name: Check import esphome.__main__ time
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
- determine-jobs
|
||||
if: needs.determine-jobs.outputs.import-time == 'true'
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Restore Python
|
||||
uses: ./.github/actions/restore-python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||
- name: Check import time against budget and write waterfall HAR
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
script/check_import_time.py --check --har importtime.har
|
||||
- name: Upload waterfall HAR
|
||||
if: always()
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: import-time-waterfall
|
||||
path: importtime.har
|
||||
if-no-files-found: ignore
|
||||
retention-days: 14
|
||||
|
||||
device-builder:
|
||||
name: Test downstream esphome/device-builder
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
- determine-jobs
|
||||
if: needs.determine-jobs.outputs.device-builder == 'true'
|
||||
steps:
|
||||
- name: Check out esphome (this PR)
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
path: esphome
|
||||
- name: Check out esphome/device-builder
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
with:
|
||||
repository: esphome/device-builder
|
||||
ref: main
|
||||
path: device-builder
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: "3.13"
|
||||
- name: Set up uv
|
||||
# Mirrors the install shape device-builder's own CI uses
|
||||
# (esphome/device-builder#192): uv replaces pip for the
|
||||
# install step (order-of-magnitude faster on cold boots,
|
||||
# with its own wheel cache). actions/setup-python still
|
||||
# provides the interpreter.
|
||||
uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
|
||||
with:
|
||||
enable-cache: true
|
||||
- name: Install device-builder + esphome from PR
|
||||
# Install device-builder with its esphome + test extras
|
||||
# first so its pinned versions of pytest/etc. land, then
|
||||
# overlay the PR's esphome so the downstream tests run
|
||||
# against this PR's Python code. ``--system`` installs into
|
||||
# the runner's Python instead of a venv.
|
||||
run: |
|
||||
uv pip install --system -e './device-builder[esphome,test]'
|
||||
uv pip install --system -e ./esphome
|
||||
- name: Run device-builder pytest
|
||||
# ``-n auto`` runs under pytest-xdist (matches device-builder's
|
||||
# own CI). No ``--cov`` here -- this is purely a downstream
|
||||
# smoke check against this PR's esphome code.
|
||||
working-directory: device-builder
|
||||
run: pytest -q -n auto --maxfail=5 --durations=10 --no-cov --ignore=tests/benchmarks
|
||||
|
||||
pytest:
|
||||
name: Run pytest
|
||||
strategy:
|
||||
@@ -171,11 +246,14 @@ jobs:
|
||||
- common
|
||||
outputs:
|
||||
integration-tests: ${{ steps.determine.outputs.integration-tests }}
|
||||
integration-tests-run-all: ${{ steps.determine.outputs.integration-tests-run-all }}
|
||||
integration-test-files: ${{ steps.determine.outputs.integration-test-files }}
|
||||
integration-test-buckets: ${{ steps.determine.outputs.integration-test-buckets }}
|
||||
clang-tidy: ${{ steps.determine.outputs.clang-tidy }}
|
||||
clang-tidy-mode: ${{ steps.determine.outputs.clang-tidy-mode }}
|
||||
python-linters: ${{ steps.determine.outputs.python-linters }}
|
||||
import-time: ${{ steps.determine.outputs.import-time }}
|
||||
device-builder: ${{ steps.determine.outputs.device-builder }}
|
||||
native-idf: ${{ steps.determine.outputs.native-idf }}
|
||||
native-idf-components: ${{ steps.determine.outputs.native-idf-components }}
|
||||
changed-components: ${{ steps.determine.outputs.changed-components }}
|
||||
changed-components-with-tests: ${{ steps.determine.outputs.changed-components-with-tests }}
|
||||
directly-changed-components-with-tests: ${{ steps.determine.outputs.directly-changed-components-with-tests }}
|
||||
@@ -185,6 +263,7 @@ jobs:
|
||||
cpp-unit-tests-run-all: ${{ steps.determine.outputs.cpp-unit-tests-run-all }}
|
||||
cpp-unit-tests-components: ${{ steps.determine.outputs.cpp-unit-tests-components }}
|
||||
component-test-batches: ${{ steps.determine.outputs.component-test-batches }}
|
||||
validate-only-components: ${{ steps.determine.outputs.validate-only-components }}
|
||||
benchmarks: ${{ steps.determine.outputs.benchmarks }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
@@ -214,11 +293,14 @@ jobs:
|
||||
|
||||
# Extract individual fields
|
||||
echo "integration-tests=$(echo "$output" | jq -r '.integration_tests')" >> $GITHUB_OUTPUT
|
||||
echo "integration-tests-run-all=$(echo "$output" | jq -r '.integration_tests_run_all')" >> $GITHUB_OUTPUT
|
||||
echo "integration-test-files=$(echo "$output" | jq -c '.integration_test_files')" >> $GITHUB_OUTPUT
|
||||
echo "integration-test-buckets=$(echo "$output" | jq -c '.integration_test_buckets')" >> $GITHUB_OUTPUT
|
||||
echo "clang-tidy=$(echo "$output" | jq -r '.clang_tidy')" >> $GITHUB_OUTPUT
|
||||
echo "clang-tidy-mode=$(echo "$output" | jq -r '.clang_tidy_mode')" >> $GITHUB_OUTPUT
|
||||
echo "python-linters=$(echo "$output" | jq -r '.python_linters')" >> $GITHUB_OUTPUT
|
||||
echo "import-time=$(echo "$output" | jq -r '.import_time')" >> $GITHUB_OUTPUT
|
||||
echo "device-builder=$(echo "$output" | jq -r '.device_builder')" >> $GITHUB_OUTPUT
|
||||
echo "native-idf=$(echo "$output" | jq -r '.native_idf')" >> $GITHUB_OUTPUT
|
||||
echo "native-idf-components=$(echo "$output" | jq -r '.native_idf_components')" >> $GITHUB_OUTPUT
|
||||
echo "changed-components=$(echo "$output" | jq -c '.changed_components')" >> $GITHUB_OUTPUT
|
||||
echo "changed-components-with-tests=$(echo "$output" | jq -c '.changed_components_with_tests')" >> $GITHUB_OUTPUT
|
||||
echo "directly-changed-components-with-tests=$(echo "$output" | jq -c '.directly_changed_components_with_tests')" >> $GITHUB_OUTPUT
|
||||
@@ -228,6 +310,7 @@ jobs:
|
||||
echo "cpp-unit-tests-run-all=$(echo "$output" | jq -r '.cpp_unit_tests_run_all')" >> $GITHUB_OUTPUT
|
||||
echo "cpp-unit-tests-components=$(echo "$output" | jq -c '.cpp_unit_tests_components')" >> $GITHUB_OUTPUT
|
||||
echo "component-test-batches=$(echo "$output" | jq -c '.component_test_batches')" >> $GITHUB_OUTPUT
|
||||
echo "validate-only-components=$(echo "$output" | jq -c '.validate_only_components')" >> $GITHUB_OUTPUT
|
||||
echo "benchmarks=$(echo "$output" | jq -r '.benchmarks')" >> $GITHUB_OUTPUT
|
||||
- name: Save components graph cache
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
@@ -237,12 +320,16 @@ jobs:
|
||||
key: components-graph-${{ hashFiles('esphome/components/**/*.py') }}
|
||||
|
||||
integration-tests:
|
||||
name: Run integration tests
|
||||
name: Run integration tests (${{ matrix.bucket.name }})
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- common
|
||||
- determine-jobs
|
||||
if: needs.determine-jobs.outputs.integration-tests == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
bucket: ${{ fromJson(needs.determine-jobs.outputs.integration-test-buckets) }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
@@ -269,19 +356,14 @@ jobs:
|
||||
run: echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
||||
- name: Run integration tests
|
||||
env:
|
||||
INTEGRATION_TEST_FILES: ${{ needs.determine-jobs.outputs.integration-test-files }}
|
||||
INTEGRATION_TESTS_RUN_ALL: ${{ needs.determine-jobs.outputs.integration-tests-run-all }}
|
||||
# JSON array of test paths; parsed into a bash array below to avoid
|
||||
# shell word-splitting / glob hazards.
|
||||
BUCKET_TESTS: ${{ toJson(matrix.bucket.tests) }}
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
if [[ "$INTEGRATION_TESTS_RUN_ALL" == "true" ]]; then
|
||||
echo "Running all integration tests"
|
||||
pytest -vv --no-cov --tb=native -n auto tests/integration/
|
||||
else
|
||||
# Parse JSON array into bash array to avoid shell expansion issues
|
||||
mapfile -t test_files < <(echo "$INTEGRATION_TEST_FILES" | jq -r '.[]')
|
||||
echo "Running ${#test_files[@]} specific integration tests"
|
||||
pytest -vv --no-cov --tb=native -n auto "${test_files[@]}"
|
||||
fi
|
||||
mapfile -t test_files < <(echo "$BUCKET_TESTS" | jq -r '.[]')
|
||||
echo "Bucket ${{ matrix.bucket.name }}: running ${#test_files[@]} integration tests"
|
||||
pytest -vv --no-cov --tb=native -n auto "${test_files[@]}"
|
||||
|
||||
cpp-unit-tests:
|
||||
name: Run C++ unit tests
|
||||
@@ -339,9 +421,12 @@ jobs:
|
||||
echo "binary=$BINARY" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Run CodSpeed benchmarks
|
||||
uses: CodSpeedHQ/action@658a901452bb54c799643e060733b7afe9121b8d # v4.14.0
|
||||
uses: CodSpeedHQ/action@3194d9a39c4d46684cb44bf7207fc56626aad8fd # v4.15.1
|
||||
with:
|
||||
run: ${{ steps.build.outputs.binary }}
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
${{ steps.build.outputs.binary }}
|
||||
pytest tests/benchmarks/python/ --codspeed --no-cov
|
||||
mode: simulation
|
||||
|
||||
clang-tidy-single:
|
||||
@@ -699,13 +784,134 @@ jobs:
|
||||
echo "Config validation passed! Starting compilation..."
|
||||
echo ""
|
||||
|
||||
# Compute the compile-stage component list. Components whose only
|
||||
# changes are validate.*.yaml files are config-only -- their source
|
||||
# and test fixtures didn't move, so rebuilding firmware adds no
|
||||
# signal. Subtract them from this batch before invoking compile.
|
||||
validate_only_json='${{ needs.determine-jobs.outputs.validate-only-components }}'
|
||||
if [ -z "$validate_only_json" ]; then
|
||||
validate_only_json='[]'
|
||||
fi
|
||||
if ! validate_only_csv=$(echo "$validate_only_json" | jq -r 'join(",")'); then
|
||||
echo "::error::Failed to render validate-only-components as CSV from: $validate_only_json"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$validate_only_csv" ]; then
|
||||
compile_csv="$components_csv"
|
||||
else
|
||||
components_sorted=$(echo "$components_csv" | tr ',' '\n' | sort -u)
|
||||
validate_sorted=$(echo "$validate_only_csv" | tr ',' '\n' | sort -u)
|
||||
if ! diff_out=$(comm -23 <(echo "$components_sorted") <(echo "$validate_sorted")); then
|
||||
echo "::error::Failed to compute compile component subset."
|
||||
exit 1
|
||||
fi
|
||||
compile_csv=$(echo "$diff_out" | paste -sd ',' -)
|
||||
skipped=$(comm -12 <(echo "$components_sorted") <(echo "$validate_sorted") | paste -sd ',' -)
|
||||
if [ -n "$skipped" ]; then
|
||||
echo "Validate-only components in this batch (skipping compile): $skipped"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Show disk space before compilation
|
||||
echo "Disk space before compilation:"
|
||||
df -h
|
||||
echo ""
|
||||
|
||||
# Run compilation with grouping and isolation
|
||||
python3 script/test_build_components.py -e compile -c "$components_csv" -f --isolate "$directly_changed_csv"
|
||||
if [ -n "$compile_csv" ]; then
|
||||
# Run compilation with grouping and isolation
|
||||
python3 script/test_build_components.py -e compile -c "$compile_csv" -f --isolate "$directly_changed_csv"
|
||||
else
|
||||
echo "All components in this batch are validate-only -- skipping compile stage."
|
||||
fi
|
||||
|
||||
test-native-idf:
|
||||
name: Test components with native ESP-IDF
|
||||
runs-on: ubuntu-24.04
|
||||
needs:
|
||||
- common
|
||||
- determine-jobs
|
||||
if: github.event_name == 'pull_request' && needs.determine-jobs.outputs.native-idf == 'true'
|
||||
env:
|
||||
ESPHOME_ESP_IDF_PREFIX: ~/.esphome-idf
|
||||
# Comma-joined subset of the native-IDF representative component list,
|
||||
# computed by script/determine-jobs.py (native_idf_components_to_test).
|
||||
# Single source of truth -- the full list lives in
|
||||
# script/determine-jobs.py::NATIVE_IDF_TEST_COMPONENTS.
|
||||
TEST_COMPONENTS: ${{ needs.determine-jobs.outputs.native-idf-components }}
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Restore Python
|
||||
uses: ./.github/actions/restore-python
|
||||
with:
|
||||
python-version: ${{ env.DEFAULT_PYTHON }}
|
||||
cache-key: ${{ needs.common.outputs.cache-key }}
|
||||
|
||||
- name: Cache ESPHome
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ~/.esphome-idf
|
||||
key: ${{ runner.os }}-esphome-${{ needs.common.outputs.cache-key }}
|
||||
|
||||
- name: Run native ESP-IDF compile test
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
|
||||
# Check if /mnt has more free space than / before bind mounting
|
||||
# Extract available space in KB for comparison
|
||||
root_avail=$(df -k / | awk 'NR==2 {print $4}')
|
||||
mnt_avail=$(df -k /mnt 2>/dev/null | awk 'NR==2 {print $4}')
|
||||
|
||||
echo "Available space: / has ${root_avail}KB, /mnt has ${mnt_avail}KB"
|
||||
|
||||
# Only use /mnt if it has more space than /
|
||||
if [ -n "$mnt_avail" ] && [ "$mnt_avail" -gt "$root_avail" ]; then
|
||||
echo "Using /mnt for build files (more space available)"
|
||||
# Bind mount PlatformIO directory to /mnt (tools, packages, build cache all go there)
|
||||
sudo mkdir -p /mnt/esphome-idf
|
||||
sudo chown $USER:$USER /mnt/esphome-idf
|
||||
mkdir -p ~/.esphome-idf
|
||||
sudo mount --bind /mnt/esphome-idf ~/.esphome-idf
|
||||
|
||||
# Bind mount test build directory to /mnt
|
||||
sudo mkdir -p /mnt/test_build_components_build
|
||||
sudo chown $USER:$USER /mnt/test_build_components_build
|
||||
mkdir -p tests/test_build_components/build
|
||||
sudo mount --bind /mnt/test_build_components_build tests/test_build_components/build
|
||||
else
|
||||
echo "Using / for build files (more space available than /mnt or /mnt unavailable)"
|
||||
fi
|
||||
|
||||
echo "Testing components: $TEST_COMPONENTS"
|
||||
echo ""
|
||||
|
||||
# Show disk space before validation (after bind mounts setup)
|
||||
echo "Disk space before config validation:"
|
||||
df -h
|
||||
echo ""
|
||||
|
||||
# Run config validation (auto-grouped by test_build_components.py)
|
||||
python3 script/test_build_components.py -e config -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain esp-idf
|
||||
|
||||
echo ""
|
||||
echo "Config validation passed! Starting compilation..."
|
||||
echo ""
|
||||
|
||||
# Show disk space before compilation
|
||||
echo "Disk space before compilation:"
|
||||
df -h
|
||||
echo ""
|
||||
|
||||
# Run compilation (auto-grouped by test_build_components.py)
|
||||
python3 script/test_build_components.py -e compile -t esp32-idf -c "$TEST_COMPONENTS" -f --toolchain esp-idf
|
||||
|
||||
- name: Save ESPHome cache
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ~/.esphome-idf
|
||||
key: ${{ runner.os }}-esphome-${{ needs.common.outputs.cache-key }}
|
||||
|
||||
pre-commit-ci-lite:
|
||||
name: pre-commit.ci lite
|
||||
@@ -986,8 +1192,8 @@ jobs:
|
||||
- memory-impact-pr-branch
|
||||
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && fromJSON(needs.determine-jobs.outputs.memory_impact).should_run == 'true' && needs.memory-impact-target-branch.outputs.skip != 'true'
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
contents: read # actions/checkout to load the comment-posting script
|
||||
pull-requests: write # ci_memory_impact_comment.py posts/updates the memory-impact comment on the PR
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
steps:
|
||||
@@ -1036,7 +1242,9 @@ jobs:
|
||||
- clang-tidy-nosplit
|
||||
- clang-tidy-split
|
||||
- determine-jobs
|
||||
- device-builder
|
||||
- test-build-components-split
|
||||
- test-native-idf
|
||||
- pre-commit-ci-lite
|
||||
- memory-impact-target-branch
|
||||
- memory-impact-pr-branch
|
||||
|
||||
@@ -6,8 +6,8 @@ on:
|
||||
types: [opened, reopened]
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
pull-requests: write # pulls.update to close the PR opened from a fork's default branch
|
||||
issues: write # issues.createComment to explain to the contributor why the PR was closed
|
||||
|
||||
jobs:
|
||||
close:
|
||||
|
||||
@@ -15,9 +15,9 @@ on:
|
||||
- beta
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: read
|
||||
contents: read
|
||||
issues: write # issues.addLabels / removeLabel to manage the 'code-owner-approved' label on the PR
|
||||
pull-requests: read # listReviews to determine whether a codeowner has approved
|
||||
contents: read # actions/checkout to read CODEOWNERS and the shared codeowners.js helper
|
||||
|
||||
jobs:
|
||||
codeowner-approved:
|
||||
|
||||
16
.github/workflows/codeowner-review-request.yml
vendored
16
.github/workflows/codeowner-review-request.yml
vendored
@@ -17,9 +17,10 @@ on:
|
||||
- release
|
||||
- beta
|
||||
|
||||
# PR/review writes (requestReviewers, issues.createComment) are performed with the App token minted below,
|
||||
# so the workflow's GITHUB_TOKEN only needs read access for checkout.
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: read
|
||||
contents: read # actions/checkout to read CODEOWNERS and the shared codeowners.js helper
|
||||
|
||||
jobs:
|
||||
request-codeowner-reviews:
|
||||
@@ -32,9 +33,20 @@ jobs:
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.base.sha }}
|
||||
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
|
||||
# Scope the minted App token to the minimum needed by the github-script step below.
|
||||
permission-pull-requests: write # pulls.listFiles, pulls.get, pulls.listReviews, pulls.requestReviewers
|
||||
permission-issues: write # issues.listComments and issues.createComment (PR comments use the issues API)
|
||||
|
||||
- name: Request reviews from component codeowners
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ steps.generate-token.outputs.token }}
|
||||
script: |
|
||||
const { loadCodeowners, getEffectiveOwners } = require('./.github/scripts/codeowners.js');
|
||||
|
||||
|
||||
20
.github/workflows/codeql.yml
vendored
20
.github/workflows/codeql.yml
vendored
@@ -16,6 +16,9 @@ on:
|
||||
schedule:
|
||||
- cron: "30 18 * * 4"
|
||||
|
||||
# Deny by default; the analyze job opts in to exactly what it needs.
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
@@ -26,15 +29,10 @@ jobs:
|
||||
# Consider using larger runners or machines with greater resources for possible analysis time improvements.
|
||||
runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }}
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write # upload CodeQL SARIF results to the Code Scanning API
|
||||
packages: read # fetch internal or private CodeQL query packs
|
||||
actions: read # required by codeql-action when run from a private repo
|
||||
contents: read # actions/checkout to scan the repository
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
@@ -58,7 +56,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
|
||||
uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
@@ -86,6 +84,6 @@ jobs:
|
||||
exit 1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@95e58e9a2cdfd71adc6e0353d5c52f41a045d225 # v4.35.2
|
||||
uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
113
.github/workflows/dashboard-deprecation-comment.yml
vendored
Normal file
113
.github/workflows/dashboard-deprecation-comment.yml
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
name: Add Dashboard Deprecation Comment
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize]
|
||||
|
||||
# All API calls (pulls.listFiles + issues.{list,create,update}Comment) are performed with
|
||||
# the App token minted below, so the workflow's GITHUB_TOKEN does not need any scopes.
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
dashboard-deprecation-comment:
|
||||
name: Dashboard deprecation comment
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
|
||||
# pulls.listFiles + issues.{list,create,update}Comment on PRs. For PR resources
|
||||
# the issues.*Comment APIs require the pull-requests scope, not issues.
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Add dashboard deprecation comment
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ steps.generate-token.outputs.token }}
|
||||
script: |
|
||||
const commentMarker = "<!-- This comment was generated automatically by the dashboard-deprecation-comment workflow. -->";
|
||||
|
||||
const commentBody = `Thanks for opening this PR!
|
||||
|
||||
Heads up: the legacy ESPHome dashboard (\`esphome/dashboard/\` and \`tests/dashboard/\`) is **deprecated** and is being replaced by [ESPHome Device Builder](https://github.com/esphome/device-builder). We are not adding new features to the legacy dashboard and it will eventually be removed from this repository.
|
||||
|
||||
What this means for your PR:
|
||||
|
||||
- **New features / enhancements**: please port the change to [esphome/device-builder](https://github.com/esphome/device-builder) instead. We are unlikely to review or merge new dashboard features here.
|
||||
- **Bug fixes**: small fixes may still be considered, but please check first whether the same issue exists in Device Builder, where the fix will have a longer life.
|
||||
- **Security issues**: please do not file a public PR. Report privately via [GitHub security advisories](https://github.com/esphome/esphome/security/advisories/new) so we can coordinate a fix.
|
||||
|
||||
We appreciate the contribution and apologize for the friction; flagging this early so your time isn't spent on a change that may not land.
|
||||
|
||||
---
|
||||
(Added by the PR bot)
|
||||
|
||||
${commentMarker}`;
|
||||
|
||||
async function getDashboardChanges(github, owner, repo, prNumber) {
|
||||
const changedFiles = await github.paginate(
|
||||
github.rest.pulls.listFiles,
|
||||
{
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
pull_number: prNumber,
|
||||
per_page: 100,
|
||||
}
|
||||
);
|
||||
|
||||
return changedFiles.filter(file =>
|
||||
file.filename.startsWith('esphome/dashboard/') ||
|
||||
file.filename.startsWith('tests/dashboard/')
|
||||
);
|
||||
}
|
||||
|
||||
async function findBotComment(github, owner, repo, prNumber) {
|
||||
const comments = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
issue_number: prNumber,
|
||||
per_page: 100,
|
||||
}
|
||||
);
|
||||
|
||||
return comments.find(comment =>
|
||||
comment.body.includes(commentMarker) && comment.user.type === "Bot"
|
||||
);
|
||||
}
|
||||
|
||||
const prNumber = context.payload.pull_request.number;
|
||||
const { owner, repo } = context.repo;
|
||||
|
||||
const dashboardChanges = await getDashboardChanges(github, owner, repo, prNumber);
|
||||
const existingComment = await findBotComment(github, owner, repo, prNumber);
|
||||
|
||||
if (dashboardChanges.length === 0) {
|
||||
// PR doesn't (or no longer) touches the legacy dashboard. If we previously
|
||||
// commented (e.g. files were removed in a later push), leave the comment in
|
||||
// place for history rather than thrash on edit/delete.
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingComment) {
|
||||
if (existingComment.body === commentBody) {
|
||||
return;
|
||||
}
|
||||
await github.rest.issues.updateComment({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
comment_id: existingComment.id,
|
||||
body: commentBody,
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
issue_number: prNumber,
|
||||
body: commentBody,
|
||||
});
|
||||
}
|
||||
19
.github/workflows/external-component-bot.yml
vendored
19
.github/workflows/external-component-bot.yml
vendored
@@ -4,20 +4,29 @@ on:
|
||||
pull_request_target:
|
||||
types: [opened, synchronize]
|
||||
|
||||
permissions:
|
||||
contents: read # Needed to fetch PR details
|
||||
issues: write # Needed to create and update comments (PR comments are managed via the issues REST API)
|
||||
pull-requests: write # also needed?
|
||||
# All API calls (pulls.listFiles + issues.{list,create,update}Comment) are performed with
|
||||
# the App token minted below, so the workflow's GITHUB_TOKEN does not need any scopes.
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
external-comment:
|
||||
name: External component comment
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
|
||||
# pulls.listFiles + issues.{list,create,update}Comment on PRs. For PR resources
|
||||
# the issues.*Comment APIs require the pull-requests scope, not issues.
|
||||
permission-pull-requests: write
|
||||
|
||||
- name: Add external component comment
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
github-token: ${{ steps.generate-token.outputs.token }}
|
||||
script: |
|
||||
// Generate external component usage instructions
|
||||
function generateExternalComponentInstructions(prNumber, componentNames, owner, repo) {
|
||||
|
||||
4
.github/workflows/issue-codeowner-notify.yml
vendored
4
.github/workflows/issue-codeowner-notify.yml
vendored
@@ -9,8 +9,8 @@ on:
|
||||
types: [labeled]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
issues: write # issues.createComment to mention component codeowners on the newly labelled issue
|
||||
contents: read # repos.getContent to fetch CODEOWNERS from the default branch
|
||||
|
||||
jobs:
|
||||
notify-codeowners:
|
||||
|
||||
8
.github/workflows/lock.yml
vendored
8
.github/workflows/lock.yml
vendored
@@ -6,6 +6,12 @@ on:
|
||||
- cron: "30 0 * * *" # Run daily at 00:30 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
# Deny by default; the lock job opts in to exactly what the reusable workflow needs.
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
lock:
|
||||
uses: esphome/workflows/.github/workflows/lock.yml@3c4e8446aa1029f1c346a482034b3ee1489077ca # 2026.4.0
|
||||
permissions:
|
||||
issues: write # issues.lock on closed issues
|
||||
pull-requests: write # issues.lock on closed pull requests
|
||||
uses: esphome/workflows/.github/workflows/lock.yml@025a1e6255610c498ed590403b7e510b69e474df # 2026.4.1
|
||||
|
||||
4
.github/workflows/pr-title-check.yml
vendored
4
.github/workflows/pr-title-check.yml
vendored
@@ -8,8 +8,8 @@ on:
|
||||
- beta
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
contents: read # actions/checkout to load detect-tags.js
|
||||
pull-requests: read # pulls.listFiles to map changed files to component/core/dashboard/ci tags
|
||||
|
||||
jobs:
|
||||
check:
|
||||
|
||||
87
.github/workflows/release.yml
vendored
87
.github/workflows/release.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
- cron: "0 2 * * *"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
contents: read # actions/checkout for all jobs; deploy jobs add their own scopes when they need to write
|
||||
|
||||
jobs:
|
||||
init:
|
||||
@@ -57,8 +57,8 @@ jobs:
|
||||
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
contents: read # actions/checkout to build the sdist/wheel
|
||||
id-token: write # OIDC token for PyPI Trusted Publishing (pypa/gh-action-pypi-publish)
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- name: Set up Python
|
||||
@@ -78,8 +78,8 @@ jobs:
|
||||
name: Build ESPHome ${{ matrix.platform.arch }}
|
||||
if: github.repository == 'esphome/esphome'
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
contents: read # actions/checkout to load Dockerfile and build context
|
||||
packages: write # docker/login-action + build-push-action push image digests to ghcr.io
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
needs: [init]
|
||||
strategy:
|
||||
@@ -152,8 +152,8 @@ jobs:
|
||||
- deploy-docker
|
||||
if: github.repository == 'esphome/esphome'
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
contents: read # actions/checkout to load Dockerfile and build context
|
||||
packages: write # docker/login-action + build-push-action push image digests to ghcr.io
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -212,72 +212,6 @@ jobs:
|
||||
docker buildx imagetools create $(jq -Rcnr 'inputs | . / "," | map("-t " + .) | join(" ")' <<< "${{ steps.tags.outputs.tags}}") \
|
||||
$(printf '${{ steps.tags.outputs.image }}@sha256:%s ' *)
|
||||
|
||||
deploy-ha-addon-repo:
|
||||
if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false'
|
||||
runs-on: ubuntu-latest
|
||||
needs:
|
||||
- init
|
||||
- deploy-manifest
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
||||
with:
|
||||
app-id: ${{ secrets.ESPHOME_GITHUB_APP_ID }}
|
||||
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
|
||||
owner: esphome
|
||||
repositories: home-assistant-addon
|
||||
|
||||
- name: Trigger Workflow
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ steps.generate-token.outputs.token }}
|
||||
script: |
|
||||
let description = "ESPHome";
|
||||
if (context.eventName == "release") {
|
||||
description = ${{ toJSON(github.event.release.body) }};
|
||||
}
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: "esphome",
|
||||
repo: "home-assistant-addon",
|
||||
workflow_id: "bump-version.yml",
|
||||
ref: "main",
|
||||
inputs: {
|
||||
version: "${{ needs.init.outputs.tag }}",
|
||||
content: description
|
||||
}
|
||||
})
|
||||
|
||||
deploy-esphome-schema:
|
||||
if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [init]
|
||||
environment: ${{ needs.init.outputs.deploy_env }}
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
||||
with:
|
||||
app-id: ${{ secrets.ESPHOME_GITHUB_APP_ID }}
|
||||
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
|
||||
owner: esphome
|
||||
repositories: esphome-schema
|
||||
|
||||
- name: Trigger Workflow
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ steps.generate-token.outputs.token }}
|
||||
script: |
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: "esphome",
|
||||
repo: "esphome-schema",
|
||||
workflow_id: "generate-schemas.yml",
|
||||
ref: "main",
|
||||
inputs: {
|
||||
version: "${{ needs.init.outputs.tag }}",
|
||||
}
|
||||
})
|
||||
|
||||
version-notifier:
|
||||
if: github.repository == 'esphome/esphome' && needs.init.outputs.branch_build == 'false'
|
||||
runs-on: ubuntu-latest
|
||||
@@ -287,19 +221,20 @@ jobs:
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
app-id: ${{ secrets.ESPHOME_GITHUB_APP_ID }}
|
||||
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
|
||||
owner: esphome
|
||||
repositories: version-notifier
|
||||
permission-actions: write # actions.createWorkflowDispatch on the target repo (only API call made with this token)
|
||||
|
||||
- name: Trigger Workflow
|
||||
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
|
||||
with:
|
||||
github-token: ${{ steps.generate-token.outputs.token }}
|
||||
script: |
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
await github.rest.actions.createWorkflowDispatch({
|
||||
owner: "esphome",
|
||||
repo: "version-notifier",
|
||||
workflow_id: "notify.yml",
|
||||
|
||||
4
.github/workflows/stale.yml
vendored
4
.github/workflows/stale.yml
vendored
@@ -7,8 +7,8 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
issues: write # actions/stale labels, comments on, and closes stale issues
|
||||
pull-requests: write # actions/stale labels, comments on, and closes stale pull requests
|
||||
|
||||
concurrency:
|
||||
group: lock
|
||||
|
||||
3
.github/workflows/status-check-labels.yml
vendored
3
.github/workflows/status-check-labels.yml
vendored
@@ -4,6 +4,9 @@ on:
|
||||
pull_request:
|
||||
types: [opened, reopened, labeled, unlabeled, synchronize]
|
||||
|
||||
permissions:
|
||||
pull-requests: read # issues.listLabelsOnIssue to detect blocking labels (needs-docs, merge-after-release, chained-pr)
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
17
.github/workflows/sync-device-classes.yml
vendored
17
.github/workflows/sync-device-classes.yml
vendored
@@ -6,12 +6,27 @@ on:
|
||||
schedule:
|
||||
- cron: "45 6 * * *"
|
||||
|
||||
# Repo writes (branch push, PR open) happen via the App token minted below,
|
||||
# so the workflow's GITHUB_TOKEN does not need any write scopes.
|
||||
permissions:
|
||||
contents: read # actions/checkout for this repo and home-assistant/core
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
name: Sync Device Classes
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'esphome/esphome'
|
||||
steps:
|
||||
- name: Generate a token
|
||||
id: generate-token
|
||||
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
|
||||
with:
|
||||
client-id: ${{ vars.ESPHOME_GITHUB_APP_CLIENT_ID }}
|
||||
private-key: ${{ secrets.ESPHOME_GITHUB_APP_PRIVATE_KEY }}
|
||||
# Scope the minted App token to the minimum needed by peter-evans/create-pull-request.
|
||||
permission-contents: write # git.createCommit + refs.create/update to push the sync/device-classes branch
|
||||
permission-pull-requests: write # pulls.create / pulls.update to open or refresh the sync PR
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
@@ -50,4 +65,4 @@ jobs:
|
||||
delete-branch: true
|
||||
title: "Synchronise Device Classes from Home Assistant"
|
||||
body-path: .github/PULL_REQUEST_TEMPLATE.md
|
||||
token: ${{ secrets.DEVICE_CLASS_SYNC_TOKEN }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -146,5 +146,6 @@ sdkconfig.*
|
||||
|
||||
/components
|
||||
/managed_components
|
||||
/dependencies.lock
|
||||
|
||||
api-docs/
|
||||
|
||||
@@ -55,7 +55,7 @@ repos:
|
||||
hooks:
|
||||
- id: pylint
|
||||
name: pylint
|
||||
entry: python3 script/run-in-env.py pylint
|
||||
entry: python script/run-in-env.py pylint
|
||||
language: system
|
||||
types: [python]
|
||||
files: ^esphome/.+\.py$
|
||||
@@ -68,5 +68,5 @@ repos:
|
||||
additional_dependencies: []
|
||||
- id: ci-custom
|
||||
name: ci-custom
|
||||
entry: python3 script/run-in-env.py script/ci-custom.py
|
||||
entry: python script/run-in-env.py script/ci-custom.py
|
||||
language: system
|
||||
|
||||
@@ -398,13 +398,23 @@ This document provides essential context for AI models interacting with this pro
|
||||
│ ├── i2c/ # I2C bus
|
||||
│ └── spi/ # SPI bus
|
||||
└── components/[component]/
|
||||
├── common.yaml # Component-only config (no bus definitions)
|
||||
├── test.esp32-idf.yaml
|
||||
├── test.esp8266-ard.yaml
|
||||
└── test.rp2040-ard.yaml
|
||||
├── common.yaml # Component-only config (no bus definitions)
|
||||
├── test.esp32-idf.yaml # config + compile
|
||||
├── test.esp8266-ard.yaml # config + compile
|
||||
├── test-variant.esp32-idf.yaml # variant test, config + compile
|
||||
├── validate.esp32-idf.yaml # config-only (never compiled)
|
||||
└── validate-legacy.esp32-idf.yaml # config-only variant
|
||||
```
|
||||
Run them using `script/test_build_components`. Use `-c <component>` to test specific components and `-t <target>` for specific platforms.
|
||||
|
||||
* **Config-only test files (`validate.*.yaml`):** Use this prefix when a YAML file only needs to exercise schema/validation paths and does not need to be compiled. CI runs `validate.*.yaml` files with `esphome config` only and skips them during compile. The grammar mirrors `test.*.yaml`:
|
||||
- `validate.<platform>.yaml` — base config-only test
|
||||
- `validate-<variant>.<platform>.yaml` — config-only variant
|
||||
|
||||
Use this for things like deprecated-syntax migration tests, schema edge cases, or platform-specific validation branches where building firmware adds no signal. A component may have any mix of `test.*.yaml` and `validate.*.yaml` files. Validate files never participate in bus-grouping; each one runs as its own `esphome config` invocation.
|
||||
|
||||
When a PR's only edits to a component are `validate.*.yaml` files (no source changes, no `test.*.yaml` changes, and the component isn't pulled in as a dependency of another changed component), CI skips the compile stage for that component entirely and only runs config validation. This is decided in `script/determine-jobs.py` via `_component_change_is_validate_only` and surfaced as the `validate_only_components` output that the `test-build-components-split` job consumes.
|
||||
|
||||
* **Test Grouping with Packages:** Components that use shared bus packages can be grouped together in CI to reduce build count. **Never define buses (uart, i2c, spi, modbus) directly in test YAML files** — always use packages from `test_build_components/common/`:
|
||||
```yaml
|
||||
# test.esp32-idf.yaml — use packages for buses
|
||||
@@ -347,6 +347,7 @@ esphome/components/modbus_controller/select/* @martgras @stegm
|
||||
esphome/components/modbus_controller/sensor/* @martgras
|
||||
esphome/components/modbus_controller/switch/* @martgras
|
||||
esphome/components/modbus_controller/text_sensor/* @martgras
|
||||
esphome/components/modbus_server/* @exciton
|
||||
esphome/components/mopeka_ble/* @Fabian-Schmidt @spbrogan
|
||||
esphome/components/mopeka_pro_check/* @spbrogan
|
||||
esphome/components/mopeka_std_check/* @Fabian-Schmidt
|
||||
@@ -415,6 +416,7 @@ esphome/components/resampler/speaker/* @kahrendt
|
||||
esphome/components/restart/* @esphome/core
|
||||
esphome/components/rf_bridge/* @jesserockz
|
||||
esphome/components/rgbct/* @jesserockz
|
||||
esphome/components/ring_buffer/* @kahrendt
|
||||
esphome/components/rp2040/* @jesserockz
|
||||
esphome/components/rp2040_ble/* @bdraco
|
||||
esphome/components/rp2040_pio_led_strip/* @Papa-DMan
|
||||
|
||||
2
Doxyfile
2
Doxyfile
@@ -48,7 +48,7 @@ PROJECT_NAME = ESPHome
|
||||
# could be handy for archiving the generated documentation or if some version
|
||||
# control system is used.
|
||||
|
||||
PROJECT_NUMBER = 2026.5.0-dev
|
||||
PROJECT_NUMBER = 2026.5.1
|
||||
|
||||
# Using the PROJECT_BRIEF tag one can provide an optional one line description
|
||||
# for a project that appears at the top of each page and should give viewer a
|
||||
|
||||
@@ -13,12 +13,16 @@ RUN git config --system --add safe.directory "*" \
|
||||
&& git config --system advice.detachedHead false
|
||||
|
||||
# Install build tools for Python packages that require compilation
|
||||
# (e.g., ruamel.yaml.clibz used by ESP-IDF's idf-component-manager)
|
||||
# (e.g., ruamel.yaml.clib used by ESP-IDF's idf-component-manager).
|
||||
# Also install libusb-1.0 at runtime so the ESP-IDF tools installer can
|
||||
# validate openocd-esp32 (it dynamically links libusb-1.0.so.0); without
|
||||
# it idf_tools.py rejects the openocd install with exit 127 and aborts
|
||||
# the whole framework setup.
|
||||
RUN if command -v apk > /dev/null; then \
|
||||
apk add --no-cache build-base; \
|
||||
apk add --no-cache build-base libusb; \
|
||||
else \
|
||||
apt-get update \
|
||||
&& apt-get install -y --no-install-recommends build-essential \
|
||||
&& apt-get install -y --no-install-recommends build-essential libusb-1.0-0 \
|
||||
&& rm -rf /var/lib/apt/lists/*; \
|
||||
fi
|
||||
|
||||
|
||||
22
docker/ha-addon-rootfs/etc/cont-init.d/40-device-builder.sh
Executable file
22
docker/ha-addon-rootfs/etc/cont-init.d/40-device-builder.sh
Executable file
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# ==============================================================================
|
||||
# Installs the latest prerelease of esphome-device-builder when the
|
||||
# `use_new_device_builder` config option is enabled.
|
||||
# This is a temporary install-on-boot step until esphome-device-builder
|
||||
# becomes a direct dependency of esphome.
|
||||
# ==============================================================================
|
||||
|
||||
if ! bashio::config.true 'use_new_device_builder'; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
bashio::log.info "Installing latest prerelease of esphome-device-builder..."
|
||||
if command -v uv > /dev/null; then
|
||||
uv pip install --system --no-cache-dir --prerelease=allow --upgrade \
|
||||
esphome-device-builder ||
|
||||
bashio::exit.nok "Failed installing esphome-device-builder."
|
||||
else
|
||||
pip install --no-cache-dir --pre --upgrade esphome-device-builder ||
|
||||
bashio::exit.nok "Failed installing esphome-device-builder."
|
||||
fi
|
||||
bashio::log.info "Installed esphome-device-builder."
|
||||
@@ -49,5 +49,12 @@ if bashio::fs.directory_exists '/config/esphome/.esphome'; then
|
||||
rm -rf /config/esphome/.esphome
|
||||
fi
|
||||
|
||||
if bashio::config.true 'use_new_device_builder'; then
|
||||
bashio::log.info "Starting ESPHome Device Builder..."
|
||||
exec esphome-device-builder /config/esphome \
|
||||
--ha-addon \
|
||||
--ingress-port "$(bashio::addon.ingress_port)"
|
||||
fi
|
||||
|
||||
bashio::log.info "Starting ESPHome dashboard..."
|
||||
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --ha-addon
|
||||
|
||||
@@ -4,6 +4,14 @@
|
||||
# Community Hass.io Add-ons: ESPHome
|
||||
# Configures NGINX for use with ESPHome
|
||||
# ==============================================================================
|
||||
|
||||
# When the new device builder is enabled it serves HA ingress directly,
|
||||
# so nginx is not used at all -- skip configuration.
|
||||
if bashio::config.true 'use_new_device_builder'; then
|
||||
bashio::log.info "Skipping NGINX setup: new device builder serves ingress directly."
|
||||
bashio::exit.ok
|
||||
fi
|
||||
|
||||
mkdir -p /var/log/nginx
|
||||
|
||||
# Generate Ingress configuration
|
||||
|
||||
@@ -5,6 +5,14 @@
|
||||
# Runs the NGINX proxy
|
||||
# ==============================================================================
|
||||
|
||||
# The new device builder handles HA ingress itself, so nginx is bypassed.
|
||||
# Block the longrun forever so s6 keeps the dependency satisfied and does
|
||||
# not respawn it.
|
||||
if bashio::config.true 'use_new_device_builder'; then
|
||||
bashio::log.info "NGINX bypassed: new device builder serves ingress directly."
|
||||
exec sleep infinity
|
||||
fi
|
||||
|
||||
bashio::log.info "Waiting for ESPHome dashboard to come up..."
|
||||
|
||||
while [[ ! -S /var/run/esphome.sock ]]; do
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,7 +24,7 @@ from .helpers import (
|
||||
from .toolchain import find_tool, resolve_tool_path, run_tool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from esphome.platformio_api import IDEData
|
||||
from esphome.platformio.toolchain import IDEData
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -793,8 +793,11 @@ class MemoryAnalyzer:
|
||||
"""Scan ESPHome source object files to map extern "C" symbols to components.
|
||||
|
||||
When no linker map file is available, this uses ``nm`` to scan ``.o`` files
|
||||
under ``src/esphome/`` and build a symbol-to-component mapping. This catches
|
||||
``extern "C"`` functions and other symbols that lack C++ namespace prefixes.
|
||||
under ``src/`` (including ``src/main.cpp.o`` and everything beneath
|
||||
``src/esphome/``) and build a symbol-to-component mapping. This catches
|
||||
``extern "C"`` functions, the ESPHome-generated ``setup()``/``loop()``
|
||||
entry points in ``main.cpp``, and other symbols that lack C++ namespace
|
||||
prefixes.
|
||||
|
||||
Skips scanning if ``_source_symbol_map`` was already populated by
|
||||
``_parse_map_file()``.
|
||||
@@ -806,12 +809,12 @@ class MemoryAnalyzer:
|
||||
if obj_dir is None:
|
||||
return
|
||||
|
||||
# Find ESPHome source object files
|
||||
esphome_src_dir = obj_dir / "src" / "esphome"
|
||||
if not esphome_src_dir.is_dir():
|
||||
# Scan all ESPHome-owned source object files: src/main.cpp.o and src/esphome/...
|
||||
src_dir = obj_dir / "src"
|
||||
if not src_dir.is_dir():
|
||||
return
|
||||
|
||||
obj_files = sorted(esphome_src_dir.rglob("*.o"))
|
||||
obj_files = sorted(src_dir.rglob("*.o"))
|
||||
if not obj_files:
|
||||
return
|
||||
|
||||
@@ -1064,6 +1067,10 @@ class MemoryAnalyzer:
|
||||
if component_name in self.external_components:
|
||||
return f"{_COMPONENT_PREFIX_EXTERNAL}{component_name}"
|
||||
|
||||
# ESPHome-generated entry point: src/main.cpp.o (contains setup()/loop())
|
||||
if len(parts) >= 2 and parts[-2:] == ("src", "main.cpp.o"):
|
||||
return _COMPONENT_CORE
|
||||
|
||||
# ESPHome core: src/esphome/core/... or src/esphome/...
|
||||
if "core" in parts and "esphome" in parts:
|
||||
return _COMPONENT_CORE
|
||||
|
||||
@@ -739,7 +739,7 @@ def main():
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from esphome.platformio_api import IDEData
|
||||
from esphome.platformio.toolchain import IDEData
|
||||
|
||||
build_path = Path(build_dir)
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ def validate_potentially_or_condition(value):
|
||||
return validate_condition(value)
|
||||
|
||||
|
||||
DelayAction = cg.esphome_ns.class_("DelayAction", Action, cg.Component)
|
||||
DelayAction = cg.esphome_ns.class_("DelayAction", Action)
|
||||
LambdaAction = cg.esphome_ns.class_("LambdaAction", Action)
|
||||
StatelessLambdaAction = cg.esphome_ns.class_("StatelessLambdaAction", Action)
|
||||
IfAction = cg.esphome_ns.class_("IfAction", Action)
|
||||
@@ -396,7 +396,6 @@ async def delay_action_to_code(
|
||||
args: TemplateArgsType,
|
||||
) -> MockObj:
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_component(var, {})
|
||||
template_ = await cg.templatable(config, args, cg.uint32)
|
||||
cg.add(var.set_delay(template_))
|
||||
return var
|
||||
@@ -597,7 +596,7 @@ async def component_resume_action_to_code(
|
||||
comp = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, comp)
|
||||
if CONF_UPDATE_INTERVAL in config:
|
||||
template_ = await cg.templatable(config[CONF_UPDATE_INTERVAL], args, int)
|
||||
template_ = await cg.templatable(config[CONF_UPDATE_INTERVAL], args, cg.uint32)
|
||||
cg.add(var.set_update_interval(template_))
|
||||
return var
|
||||
|
||||
|
||||
@@ -3,17 +3,22 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from esphome.components.esp32 import get_esp32_variant
|
||||
from esphome.components.esp32 import get_esp32_variant, idf_version
|
||||
import esphome.config_validation as cv
|
||||
from esphome.core import CORE
|
||||
from esphome.helpers import mkdir_p, write_file_if_changed
|
||||
from esphome.writer import update_storage_json
|
||||
|
||||
|
||||
def get_available_components() -> list[str] | None:
|
||||
"""Get list of available ESP-IDF components from project_description.json.
|
||||
"""Get list of built-in ESP-IDF components from project_description.json.
|
||||
|
||||
Returns only internal ESP-IDF components, excluding external/managed
|
||||
components (from idf_component.yml).
|
||||
Excludes ``src``, IDF-managed components (``managed_components/``), and
|
||||
converted PIO libs (``pio_components/``). Returns ``None`` if the build
|
||||
dir or ``project_description.json`` isn't ready yet.
|
||||
"""
|
||||
if CORE.build_path is None:
|
||||
return None
|
||||
project_desc = Path(CORE.build_path) / "build" / "project_description.json"
|
||||
if not project_desc.exists():
|
||||
return None
|
||||
@@ -30,9 +35,9 @@ def get_available_components() -> list[str] | None:
|
||||
if name == "src":
|
||||
continue
|
||||
|
||||
# Exclude managed/external components
|
||||
# Exclude IDF-managed and converted-PIO components (external).
|
||||
comp_dir = info.get("dir", "")
|
||||
if "managed_components" in comp_dir:
|
||||
if "managed_components" in comp_dir or "pio_components" in comp_dir:
|
||||
continue
|
||||
|
||||
result.append(name)
|
||||
@@ -47,23 +52,90 @@ def has_discovered_components() -> bool:
|
||||
return get_available_components() is not None
|
||||
|
||||
|
||||
def get_project_cmakelists() -> str:
|
||||
"""Generate the top-level CMakeLists.txt for ESP-IDF project."""
|
||||
def get_project_cmakelists(minimal: bool = False) -> str:
|
||||
"""Generate the top-level CMakeLists.txt for ESP-IDF project.
|
||||
|
||||
When ``minimal`` is true, omit ``ESPHOME_PROJECT_BUILTIN_COMPONENTS``
|
||||
since ``project_description.json`` may be stale on the first write.
|
||||
"""
|
||||
# Get IDF target from ESP32 variant (e.g., ESP32S3 -> esp32s3)
|
||||
variant = get_esp32_variant()
|
||||
idf_target = variant.lower().replace("-", "")
|
||||
|
||||
# Extract compile definitions from build flags (-DXXX -> XXX)
|
||||
compile_defs = [flag for flag in CORE.build_flags if flag.startswith("-D")]
|
||||
# esp_idf_size 2.x (bundled with IDF >=6.0) made NG the default and
|
||||
# removed the --ng flag; on 1.x (IDF 5.5) --ng is required to get
|
||||
# --format=raw because the legacy mode doesn't support it.
|
||||
size_ng_flag = "--ng" if idf_version() < cv.Version(6, 0, 0) else ""
|
||||
|
||||
# Project-wide compile options: -D defines and -W warning flags (skip
|
||||
# -Wl, linker flags — those go on the src component via
|
||||
# target_link_options below). Emitted via idf_build_set_property so the
|
||||
# flags propagate to every IDF component (including managed ones like
|
||||
# esphome__micro-mp3) rather than just src/. Required so suppressions
|
||||
# like ``-Wno-error=maybe-uninitialized`` actually silence warnings in
|
||||
# third-party components we don't author.
|
||||
project_compile_opts = [
|
||||
flag
|
||||
for flag in sorted(CORE.build_flags)
|
||||
if flag.startswith("-D")
|
||||
or (flag.startswith("-W") and not flag.startswith("-Wl,"))
|
||||
]
|
||||
extra_compile_options = "\n".join(
|
||||
f'idf_build_set_property(COMPILE_OPTIONS "{compile_def}" APPEND)'
|
||||
for compile_def in compile_defs
|
||||
f'idf_build_set_property(COMPILE_OPTIONS "{flag}" APPEND)'
|
||||
for flag in project_compile_opts
|
||||
)
|
||||
|
||||
# Per-project list exposed as a CMake variable so converted PIO libs
|
||||
# can reference ${ESPHOME_PROJECT_MANAGED_COMPONENTS} without baking
|
||||
# project-specific names into their cached CMakeLists.
|
||||
#
|
||||
# Emit via idf_build_set_property (not plain set()) so the value is
|
||||
# serialised into build_properties.temp.cmake and visible to IDF's
|
||||
# early requirements-expansion pass (component_get_requirements.cmake
|
||||
# runs as a separate CMake script invocation that doesn't load the
|
||||
# project's top-level CMakeLists; without this, ${ESPHOME_PROJECT_
|
||||
# MANAGED_COMPONENTS} in a converted-lib REQUIRES expands to empty).
|
||||
from esphome.components.esp32 import get_managed_component_require_names
|
||||
|
||||
managed_components_property = "\n".join(
|
||||
f"idf_build_set_property(ESPHOME_PROJECT_MANAGED_COMPONENTS {name} APPEND)"
|
||||
for name in get_managed_component_require_names()
|
||||
)
|
||||
|
||||
# Built-in IDF components exposed via our own property (not IDF's
|
||||
# __COMPONENT_REQUIRES_COMMON, which would append them to every
|
||||
# component's REQUIRES including real IDF components). Referenced by
|
||||
# src/CMakeLists and by each converted PIO lib's CMakeLists. Skipped
|
||||
# on minimal writes because project_description.json may be stale.
|
||||
builtin_components_property = (
|
||||
""
|
||||
if minimal
|
||||
else "\n".join(
|
||||
f"idf_build_set_property(ESPHOME_PROJECT_BUILTIN_COMPONENTS {name} APPEND)"
|
||||
for name in sorted(get_available_components() or [])
|
||||
)
|
||||
)
|
||||
|
||||
return f"""\
|
||||
# Auto-generated by ESPHome
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
# On Windows, Ninja can fail with:
|
||||
# "CreateProcess: The parameter is incorrect (is the command line too long?)"
|
||||
# when compiler/linker command lines exceed the OS length limit.
|
||||
#
|
||||
# The following settings force CMake/Ninja to use *response files* (@file.rsp)
|
||||
# to pass long lists of includes, objects, and other arguments indirectly,
|
||||
# avoiding command-line length limits and fixing the build failure.
|
||||
#
|
||||
# This is especially useful for large ESP-IDF / ESPHome projects with many
|
||||
# source files or include directories.
|
||||
set(CMAKE_C_USE_RESPONSE_FILE_FOR_INCLUDES 1)
|
||||
set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_INCLUDES 1)
|
||||
set(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 1)
|
||||
set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 1)
|
||||
set(CMAKE_NINJA_FORCE_RESPONSE_FILE 1)
|
||||
|
||||
set(IDF_TARGET {idf_target})
|
||||
set(EXTRA_COMPONENT_DIRS ${{CMAKE_SOURCE_DIR}}/src)
|
||||
|
||||
@@ -71,50 +143,67 @@ include($ENV{{IDF_PATH}}/tools/cmake/project.cmake)
|
||||
|
||||
{extra_compile_options}
|
||||
|
||||
{managed_components_property}
|
||||
|
||||
{builtin_components_property}
|
||||
|
||||
project({CORE.name})
|
||||
|
||||
# Emit raw JSON size data for ESPHome to read post-build.
|
||||
add_custom_command(
|
||||
TARGET ${{CMAKE_PROJECT_NAME}}.elf POST_BUILD
|
||||
COMMAND ${{PYTHON}} -m esp_idf_size {size_ng_flag} --format=raw
|
||||
-o ${{CMAKE_BINARY_DIR}}/esp_idf_size.json
|
||||
${{CMAKE_PROJECT_NAME}}.map
|
||||
WORKING_DIRECTORY ${{CMAKE_BINARY_DIR}}
|
||||
VERBATIM
|
||||
)
|
||||
"""
|
||||
|
||||
|
||||
def get_component_cmakelists(minimal: bool = False) -> str:
|
||||
"""Generate the main component CMakeLists.txt."""
|
||||
idf_requires = [] if minimal else (get_available_components() or [])
|
||||
requires_str = " ".join(idf_requires)
|
||||
def get_component_cmakelists() -> str:
|
||||
"""Generate the main component CMakeLists.txt.
|
||||
|
||||
# Extract compile options (-W flags, excluding linker flags)
|
||||
compile_opts = [
|
||||
flag
|
||||
for flag in CORE.build_flags
|
||||
if flag.startswith("-W") and not flag.startswith("-Wl,")
|
||||
]
|
||||
compile_opts_str = "\n ".join(sorted(compile_opts)) if compile_opts else ""
|
||||
|
||||
# Extract linker options (-Wl, flags)
|
||||
REQUIRES pulls in the discovered built-in IDF components via the
|
||||
project-level variables set in the top-level CMakeLists.
|
||||
"""
|
||||
# Extract linker options (-Wl, flags). Compile flags (-D, -W) are
|
||||
# emitted project-wide via idf_build_set_property in
|
||||
# get_project_cmakelists so they reach every component, not just src/.
|
||||
link_opts = [flag for flag in CORE.build_flags if flag.startswith("-Wl,")]
|
||||
link_opts_str = "\n ".join(sorted(link_opts)) if link_opts else ""
|
||||
|
||||
return f"""\
|
||||
# Auto-generated by ESPHome
|
||||
file(GLOB_RECURSE app_sources
|
||||
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.cpp"
|
||||
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.c"
|
||||
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cpp"
|
||||
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.c"
|
||||
)
|
||||
# CONFIGURE_DEPENDS asks CMake to re-check the glob each build so test
|
||||
# runs that reuse the build dir don't compile stale source paths. It's
|
||||
# invalid in script mode (cmake -P), which is how IDF's
|
||||
# component_get_requirements.cmake includes us, so skip it there.
|
||||
if(CMAKE_SCRIPT_MODE_FILE)
|
||||
file(GLOB_RECURSE app_sources
|
||||
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.cpp"
|
||||
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.c"
|
||||
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cpp"
|
||||
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.c"
|
||||
)
|
||||
else()
|
||||
file(GLOB_RECURSE app_sources CONFIGURE_DEPENDS
|
||||
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.cpp"
|
||||
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.c"
|
||||
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cpp"
|
||||
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.c"
|
||||
)
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${{app_sources}}
|
||||
INCLUDE_DIRS "." "esphome"
|
||||
REQUIRES {requires_str}
|
||||
REQUIRES ${{ESPHOME_PROJECT_BUILTIN_COMPONENTS}}
|
||||
)
|
||||
|
||||
# Apply C++ standard
|
||||
target_compile_features(${{COMPONENT_LIB}} PUBLIC cxx_std_20)
|
||||
|
||||
# ESPHome compile options
|
||||
target_compile_options(${{COMPONENT_LIB}} PUBLIC
|
||||
{compile_opts_str}
|
||||
)
|
||||
|
||||
# ESPHome linker options
|
||||
target_link_options(${{COMPONENT_LIB}} PUBLIC
|
||||
{link_opts_str}
|
||||
@@ -124,17 +213,22 @@ target_link_options(${{COMPONENT_LIB}} PUBLIC
|
||||
|
||||
def write_project(minimal: bool = False) -> None:
|
||||
"""Write ESP-IDF project files."""
|
||||
# Refresh <data_dir>/storage/<name>.yaml.json so the dashboard's
|
||||
# /info and /downloads endpoints can locate the build (they 404
|
||||
# otherwise). This mirrors the PlatformIO build-gen path's call
|
||||
# in build_gen/platformio.py:write_ini().
|
||||
update_storage_json()
|
||||
mkdir_p(CORE.build_path)
|
||||
mkdir_p(CORE.relative_src_path())
|
||||
|
||||
# Write top-level CMakeLists.txt
|
||||
write_file_if_changed(
|
||||
CORE.relative_build_path("CMakeLists.txt"),
|
||||
get_project_cmakelists(),
|
||||
get_project_cmakelists(minimal=minimal),
|
||||
)
|
||||
|
||||
# Write component CMakeLists.txt in src/
|
||||
write_file_if_changed(
|
||||
CORE.relative_src_path("CMakeLists.txt"),
|
||||
get_component_cmakelists(minimal=minimal),
|
||||
get_component_cmakelists(),
|
||||
)
|
||||
|
||||
@@ -98,11 +98,13 @@ _KNOWN_FILE_EXTENSIONS = frozenset(
|
||||
)
|
||||
|
||||
|
||||
# Matches !secret references in YAML text. This is intentionally a simple
|
||||
# regex scan rather than a YAML parse — it may match inside comments or
|
||||
# multi-line strings, which is the conservative direction (include more
|
||||
# secrets rather than fewer).
|
||||
_SECRET_RE = re.compile(r"!secret\s+(\S+)")
|
||||
# Matches !secret references in YAML text. An optional surrounding
|
||||
# quote pair around the key is allowed and ignored: YAML treats
|
||||
# ``!secret 'foo'`` and ``!secret foo`` as the same key. This is
|
||||
# intentionally a simple regex scan rather than a YAML parse — it may
|
||||
# match inside comments or multi-line strings, which is the conservative
|
||||
# direction (include more secrets rather than fewer).
|
||||
_SECRET_RE = re.compile(r"""!secret\s+['"]?([^\s'"]+)""")
|
||||
|
||||
|
||||
def _find_used_secret_keys(yaml_files: list[Path]) -> set[str]:
|
||||
|
||||
76
esphome/compiled_config.py
Normal file
76
esphome/compiled_config.py
Normal file
@@ -0,0 +1,76 @@
|
||||
"""Validated-config cache for the upload/logs fast path.
|
||||
|
||||
compile dumps the validated config to <data_dir>/storage/<file>.validated.yaml;
|
||||
the next upload/logs for that YAML reuses it instead of running the full
|
||||
read_config pipeline. YAML round-trip (yaml_util.dump/load_yaml) keeps
|
||||
!lambda/!include/IDs/paths intact; mtime gates staleness.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from esphome.core import CORE
|
||||
from esphome.helpers import write_file
|
||||
from esphome.storage_json import StorageJSON, ext_storage_path
|
||||
from esphome.types import ConfigType
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def compiled_config_path(config_filename: str) -> Path:
|
||||
"""Path to the cached validated config alongside the storage sidecar."""
|
||||
return CORE.data_dir / "storage" / f"{config_filename}.validated.yaml"
|
||||
|
||||
|
||||
def _cache_is_fresh(cache_path: Path, source_path: Path) -> bool:
|
||||
"""True iff the cache file exists and isn't older than the source."""
|
||||
try:
|
||||
return cache_path.stat().st_mtime >= source_path.stat().st_mtime
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
|
||||
def save_compiled_config(config: ConfigType) -> None:
|
||||
"""Write the validated-config cache. Always-write so mtime stays fresh.
|
||||
|
||||
Mode 0600 because show_secrets=True resolves !secret inline.
|
||||
Failures are non-fatal: the fast path falls back to read_config.
|
||||
"""
|
||||
from esphome import yaml_util
|
||||
|
||||
try:
|
||||
rendered = yaml_util.dump(config, show_secrets=True)
|
||||
write_file(compiled_config_path(CORE.config_filename), rendered, private=True)
|
||||
except Exception as err: # pylint: disable=broad-except
|
||||
_LOGGER.debug("Skipping compiled config cache write: %s", err)
|
||||
|
||||
|
||||
def load_compiled_config(conf_path: Path) -> ConfigType | None:
|
||||
"""Load the cached validated config and apply storage metadata to CORE.
|
||||
|
||||
Returns None (caller falls back to read_config) when the cache is
|
||||
missing, older than the source YAML, unparseable, or the sidecar
|
||||
is incomplete.
|
||||
"""
|
||||
cache_path = compiled_config_path(conf_path.name)
|
||||
if not _cache_is_fresh(cache_path, conf_path):
|
||||
return None
|
||||
|
||||
from esphome import yaml_util
|
||||
|
||||
try:
|
||||
config = yaml_util.load_yaml(cache_path, clear_secrets=False)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
return None
|
||||
|
||||
storage = StorageJSON.load(ext_storage_path(conf_path.name))
|
||||
if storage is None:
|
||||
return None
|
||||
# apply_to_core assumes a real compile wrote the sidecar; wizard-only
|
||||
# sidecars leave both of these unset and can't drive upload/logs.
|
||||
if not storage.core_platform and not storage.target_platform:
|
||||
return None
|
||||
storage.apply_to_core()
|
||||
return config
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace a01nyub {
|
||||
namespace esphome::a01nyub {
|
||||
|
||||
static const char *const TAG = "a01nyub.sensor";
|
||||
|
||||
@@ -42,5 +41,4 @@ void A01nyubComponent::check_buffer_() {
|
||||
|
||||
void A01nyubComponent::dump_config() { LOG_SENSOR("", "A01nyub Sensor", this); }
|
||||
|
||||
} // namespace a01nyub
|
||||
} // namespace esphome
|
||||
} // namespace esphome::a01nyub
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace a01nyub {
|
||||
namespace esphome::a01nyub {
|
||||
|
||||
class A01nyubComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
|
||||
public:
|
||||
@@ -23,5 +22,4 @@ class A01nyubComponent : public sensor::Sensor, public Component, public uart::U
|
||||
std::vector<uint8_t> buffer_;
|
||||
};
|
||||
|
||||
} // namespace a01nyub
|
||||
} // namespace esphome
|
||||
} // namespace esphome::a01nyub
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace a02yyuw {
|
||||
namespace esphome::a02yyuw {
|
||||
|
||||
static const char *const TAG = "a02yyuw.sensor";
|
||||
|
||||
@@ -41,5 +40,4 @@ void A02yyuwComponent::check_buffer_() {
|
||||
|
||||
void A02yyuwComponent::dump_config() { LOG_SENSOR("", "A02yyuw Sensor", this); }
|
||||
|
||||
} // namespace a02yyuw
|
||||
} // namespace esphome
|
||||
} // namespace esphome::a02yyuw
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace a02yyuw {
|
||||
namespace esphome::a02yyuw {
|
||||
|
||||
class A02yyuwComponent : public sensor::Sensor, public Component, public uart::UARTDevice {
|
||||
public:
|
||||
@@ -23,5 +22,4 @@ class A02yyuwComponent : public sensor::Sensor, public Component, public uart::U
|
||||
std::vector<uint8_t> buffer_;
|
||||
};
|
||||
|
||||
} // namespace a02yyuw
|
||||
} // namespace esphome
|
||||
} // namespace esphome::a02yyuw
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "a4988.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace a4988 {
|
||||
namespace esphome::a4988 {
|
||||
|
||||
static const char *const TAG = "a4988.stepper";
|
||||
|
||||
@@ -51,5 +50,4 @@ void A4988::loop() {
|
||||
this->step_pin_->digital_write(false);
|
||||
}
|
||||
|
||||
} // namespace a4988
|
||||
} // namespace esphome
|
||||
} // namespace esphome::a4988
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/stepper/stepper.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace a4988 {
|
||||
namespace esphome::a4988 {
|
||||
|
||||
class A4988 : public stepper::Stepper, public Component {
|
||||
public:
|
||||
@@ -25,5 +24,4 @@ class A4988 : public stepper::Stepper, public Component {
|
||||
HighFrequencyLoopRequester high_freq_;
|
||||
};
|
||||
|
||||
} // namespace a4988
|
||||
} // namespace esphome
|
||||
} // namespace esphome::a4988
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "adalight_light_effect.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace adalight {
|
||||
namespace esphome::adalight {
|
||||
|
||||
static const char *const TAG = "adalight_light_effect";
|
||||
|
||||
@@ -129,7 +128,7 @@ AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableL
|
||||
uint8_t *led_data = &frame_[6];
|
||||
|
||||
for (int led = 0; led < accepted_led_count; led++, led_data += 3) {
|
||||
auto white = std::min(std::min(led_data[0], led_data[1]), led_data[2]);
|
||||
auto white = std::min({led_data[0], led_data[1], led_data[2]});
|
||||
|
||||
it[led].set(Color(led_data[0], led_data[1], led_data[2], white));
|
||||
}
|
||||
@@ -138,5 +137,4 @@ AdalightLightEffect::Frame AdalightLightEffect::parse_frame_(light::AddressableL
|
||||
return CONSUMED;
|
||||
}
|
||||
|
||||
} // namespace adalight
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adalight
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace adalight {
|
||||
namespace esphome::adalight {
|
||||
|
||||
class AdalightLightEffect : public light::AddressableLightEffect, public uart::UARTDevice {
|
||||
public:
|
||||
@@ -35,5 +34,4 @@ class AdalightLightEffect : public light::AddressableLightEffect, public uart::U
|
||||
std::vector<uint8_t> frame_;
|
||||
};
|
||||
|
||||
} // namespace adalight
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adalight
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
#include <zephyr/drivers/adc.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
namespace esphome::adc {
|
||||
|
||||
#ifdef USE_ESP32
|
||||
// clang-format off
|
||||
@@ -162,5 +161,4 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace adc
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adc
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "adc_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
namespace esphome::adc {
|
||||
|
||||
static const char *const TAG = "adc.common";
|
||||
|
||||
@@ -79,5 +78,4 @@ void ADCSensor::set_sample_count(uint8_t sample_count) {
|
||||
|
||||
void ADCSensor::set_sampling_mode(SamplingMode sampling_mode) { this->sampling_mode_ = sampling_mode; }
|
||||
|
||||
} // namespace adc
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adc
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
namespace esphome::adc {
|
||||
|
||||
static const char *const TAG = "adc.esp32";
|
||||
|
||||
@@ -364,7 +363,6 @@ float ADCSensor::sample_autorange_() {
|
||||
return final_result;
|
||||
}
|
||||
|
||||
} // namespace adc
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adc
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -11,8 +11,7 @@ ADC_MODE(ADC_VCC)
|
||||
#include <Arduino.h>
|
||||
#endif // USE_ADC_SENSOR_VCC
|
||||
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
namespace esphome::adc {
|
||||
|
||||
static const char *const TAG = "adc.esp8266";
|
||||
|
||||
@@ -55,7 +54,6 @@ float ADCSensor::sample() {
|
||||
return aggr.aggregate() / 1024.0f;
|
||||
}
|
||||
|
||||
} // namespace adc
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adc
|
||||
|
||||
#endif // USE_ESP8266
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
#include "adc_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
namespace esphome::adc {
|
||||
|
||||
static const char *const TAG = "adc.libretiny";
|
||||
|
||||
@@ -48,7 +47,6 @@ float ADCSensor::sample() {
|
||||
return aggr.aggregate() / 1000.0f;
|
||||
}
|
||||
|
||||
} // namespace adc
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adc
|
||||
|
||||
#endif // USE_LIBRETINY
|
||||
|
||||
@@ -15,8 +15,7 @@
|
||||
#define PICO_VSYS_PIN 29 // NOLINT(cppcoreguidelines-macro-usage)
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
namespace esphome::adc {
|
||||
|
||||
static const char *const TAG = "adc.rp2040";
|
||||
|
||||
@@ -98,7 +97,6 @@ float ADCSensor::sample() {
|
||||
return aggr.aggregate() * 3.3f / 4096.0f * coeff;
|
||||
}
|
||||
|
||||
} // namespace adc
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adc
|
||||
|
||||
#endif // USE_RP2040
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
#include "hal/nrf_saadc.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
namespace esphome::adc {
|
||||
|
||||
static const char *const TAG = "adc.zephyr";
|
||||
|
||||
@@ -202,6 +201,5 @@ float ADCSensor::sample() {
|
||||
return val_mv / 1000.0f;
|
||||
}
|
||||
|
||||
} // namespace adc
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adc
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "adc128s102.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace adc128s102 {
|
||||
namespace esphome::adc128s102 {
|
||||
|
||||
static const char *const TAG = "adc128s102";
|
||||
|
||||
@@ -28,5 +27,4 @@ uint16_t ADC128S102::read_data(uint8_t channel) {
|
||||
return digital_value;
|
||||
}
|
||||
|
||||
} // namespace adc128s102
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adc128s102
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace adc128s102 {
|
||||
namespace esphome::adc128s102 {
|
||||
|
||||
class ADC128S102 : public Component,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW, spi::CLOCK_PHASE_LEADING,
|
||||
@@ -19,5 +18,4 @@ class ADC128S102 : public Component,
|
||||
uint16_t read_data(uint8_t channel);
|
||||
};
|
||||
|
||||
} // namespace adc128s102
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adc128s102
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace adc128s102 {
|
||||
namespace esphome::adc128s102 {
|
||||
|
||||
static const char *const TAG = "adc128s102.sensor";
|
||||
|
||||
@@ -18,5 +17,4 @@ void ADC128S102Sensor::dump_config() {
|
||||
float ADC128S102Sensor::sample() { return this->parent_->read_data(this->channel_); }
|
||||
void ADC128S102Sensor::update() { this->publish_state(this->sample()); }
|
||||
|
||||
} // namespace adc128s102
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adc128s102
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
|
||||
#include "../adc128s102.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace adc128s102 {
|
||||
namespace esphome::adc128s102 {
|
||||
|
||||
class ADC128S102Sensor : public PollingComponent,
|
||||
public Parented<ADC128S102>,
|
||||
@@ -24,5 +23,4 @@ class ADC128S102Sensor : public PollingComponent,
|
||||
protected:
|
||||
uint8_t channel_;
|
||||
};
|
||||
} // namespace adc128s102
|
||||
} // namespace esphome
|
||||
} // namespace esphome::adc128s102
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "addressable_light_display.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace addressable_light {
|
||||
namespace esphome::addressable_light {
|
||||
|
||||
static const char *const TAG = "addressable_light.display";
|
||||
|
||||
@@ -66,5 +65,4 @@ void HOT AddressableLightDisplay::draw_absolute_pixel_internal(int x, int y, Col
|
||||
this->addressable_light_buffer_[y * this->get_width_internal() + x] = color;
|
||||
}
|
||||
}
|
||||
} // namespace addressable_light
|
||||
} // namespace esphome
|
||||
} // namespace esphome::addressable_light
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace addressable_light {
|
||||
namespace esphome::addressable_light {
|
||||
|
||||
class AddressableLightDisplay : public display::DisplayBuffer {
|
||||
public:
|
||||
@@ -61,5 +60,4 @@ class AddressableLightDisplay : public display::DisplayBuffer {
|
||||
optional<uint32_t> last_effect_index_;
|
||||
optional<std::function<int(int, int)>> pixel_mapper_f_;
|
||||
};
|
||||
} // namespace addressable_light
|
||||
} // namespace esphome
|
||||
} // namespace esphome::addressable_light
|
||||
|
||||
@@ -13,8 +13,7 @@
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace ade7880 {
|
||||
namespace esphome::ade7880 {
|
||||
|
||||
static const char *const TAG = "ade7880";
|
||||
|
||||
@@ -313,5 +312,4 @@ void ADE7880::reset_device_() {
|
||||
this->store_.reset_pending = true;
|
||||
}
|
||||
|
||||
} // namespace ade7880
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ade7880
|
||||
|
||||
@@ -16,8 +16,7 @@
|
||||
|
||||
#include "ade7880_registers.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ade7880 {
|
||||
namespace esphome::ade7880 {
|
||||
|
||||
struct NeutralChannel {
|
||||
void set_current(sensor::Sensor *sens) { this->current = sens; }
|
||||
@@ -125,5 +124,4 @@ class ADE7880 : public i2c::I2CDevice, public PollingComponent {
|
||||
void write_u32_register16_(uint16_t a_register, uint32_t value);
|
||||
};
|
||||
|
||||
} // namespace ade7880
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ade7880
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
|
||||
#include "ade7880.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ade7880 {
|
||||
namespace esphome::ade7880 {
|
||||
|
||||
// adapted from https://stackoverflow.com/a/55912127/1886371
|
||||
template<size_t Bits, typename T> inline T sign_extend(const T &v) noexcept {
|
||||
@@ -97,5 +96,4 @@ void ADE7880::write_u32_register16_(uint16_t a_register, uint32_t value) {
|
||||
this->write_register16(a_register, reinterpret_cast<uint8_t *>(&out), sizeof(out));
|
||||
}
|
||||
|
||||
} // namespace ade7880
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ade7880
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
// Source: https://www.analog.com/media/en/technical-documentation/application-notes/AN-1127.pdf
|
||||
|
||||
namespace esphome {
|
||||
namespace ade7880 {
|
||||
namespace esphome::ade7880 {
|
||||
|
||||
// DSP Data Memory RAM registers
|
||||
constexpr uint16_t AIGAIN = 0x4380;
|
||||
@@ -242,5 +241,4 @@ constexpr uint8_t DSPWP_SET_RO = (1 << 7);
|
||||
// DSPWP_SEL Register Bits
|
||||
constexpr uint8_t DSPWP_SEL_SET = 0xAD;
|
||||
|
||||
} // namespace ade7880
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ade7880
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace ade7953_base {
|
||||
namespace esphome::ade7953_base {
|
||||
|
||||
static const char *const TAG = "ade7953";
|
||||
|
||||
@@ -160,5 +159,4 @@ void ADE7953::update() {
|
||||
ADE_PUBLISH(frequency, 223750.0f, 1 + val_16);
|
||||
}
|
||||
|
||||
} // namespace ade7953_base
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ade7953_base
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace ade7953_base {
|
||||
namespace esphome::ade7953_base {
|
||||
|
||||
static constexpr uint8_t PGA_V_8 =
|
||||
0x007; // PGA_V, (R/W) Default: 0x00, Unsigned, Voltage channel gain configuration (Bits[2:0])
|
||||
@@ -131,5 +130,4 @@ class ADE7953 : public PollingComponent, public sensor::Sensor {
|
||||
virtual bool ade_read_32(uint16_t reg, uint32_t *value) = 0;
|
||||
};
|
||||
|
||||
} // namespace ade7953_base
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ade7953_base
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ade7953_i2c {
|
||||
namespace esphome::ade7953_i2c {
|
||||
|
||||
static const char *const TAG = "ade7953";
|
||||
|
||||
@@ -76,5 +75,4 @@ bool AdE7953I2c::ade_read_32(uint16_t reg, uint32_t *value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ade7953_i2c
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ade7953_i2c
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace ade7953_i2c {
|
||||
namespace esphome::ade7953_i2c {
|
||||
|
||||
class AdE7953I2c : public ade7953_base::ADE7953, public i2c::I2CDevice {
|
||||
public:
|
||||
@@ -24,5 +23,4 @@ class AdE7953I2c : public ade7953_base::ADE7953, public i2c::I2CDevice {
|
||||
bool ade_read_32(uint16_t reg, uint32_t *value) override;
|
||||
};
|
||||
|
||||
} // namespace ade7953_i2c
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ade7953_i2c
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ade7953_spi {
|
||||
namespace esphome::ade7953_spi {
|
||||
|
||||
static const char *const TAG = "ade7953";
|
||||
|
||||
@@ -83,5 +82,4 @@ bool AdE7953Spi::ade_read_32(uint16_t reg, uint32_t *value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace ade7953_spi
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ade7953_spi
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace ade7953_spi {
|
||||
namespace esphome::ade7953_spi {
|
||||
|
||||
class AdE7953Spi : public ade7953_base::ADE7953,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, spi::CLOCK_PHASE_TRAILING,
|
||||
@@ -28,5 +27,4 @@ class AdE7953Spi : public ade7953_base::ADE7953,
|
||||
bool ade_read_32(uint16_t reg, uint32_t *value) override;
|
||||
};
|
||||
|
||||
} // namespace ade7953_spi
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ade7953_spi
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ads1115 {
|
||||
namespace esphome::ads1115 {
|
||||
|
||||
static const char *const TAG = "ads1115";
|
||||
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
|
||||
@@ -208,5 +207,4 @@ float ADS1115Component::request_measurement(ADS1115Multiplexer multiplexer, ADS1
|
||||
return millivolts / 1e3f;
|
||||
}
|
||||
|
||||
} // namespace ads1115
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ads1115
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace esphome {
|
||||
namespace ads1115 {
|
||||
namespace esphome::ads1115 {
|
||||
|
||||
enum ADS1115Multiplexer {
|
||||
ADS1115_MULTIPLEXER_P0_N1 = 0b000,
|
||||
@@ -60,5 +59,4 @@ class ADS1115Component : public Component, public i2c::I2CDevice {
|
||||
bool continuous_mode_;
|
||||
};
|
||||
|
||||
} // namespace ads1115
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ads1115
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ads1115 {
|
||||
namespace esphome::ads1115 {
|
||||
|
||||
static const char *const TAG = "ads1115.sensor";
|
||||
|
||||
@@ -29,5 +28,4 @@ void ADS1115Sensor::dump_config() {
|
||||
this->multiplexer_, this->gain_, this->resolution_, this->samplerate_);
|
||||
}
|
||||
|
||||
} // namespace ads1115
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ads1115
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
#include "../ads1115.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ads1115 {
|
||||
namespace esphome::ads1115 {
|
||||
|
||||
/// Internal holder class that is in instance of Sensor so that the hub can create individual sensors.
|
||||
class ADS1115Sensor : public sensor::Sensor,
|
||||
@@ -33,5 +32,4 @@ class ADS1115Sensor : public sensor::Sensor,
|
||||
ADS1115Samplerate samplerate_;
|
||||
};
|
||||
|
||||
} // namespace ads1115
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ads1115
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ads1118 {
|
||||
namespace esphome::ads1118 {
|
||||
|
||||
static const char *const TAG = "ads1118";
|
||||
static const uint8_t ADS1118_DATA_RATE_860_SPS = 0b111;
|
||||
@@ -122,5 +121,4 @@ float ADS1118::request_measurement(ADS1118Multiplexer multiplexer, ADS1118Gain g
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ads1118
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ads1118
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ads1118 {
|
||||
namespace esphome::ads1118 {
|
||||
|
||||
enum ADS1118Multiplexer {
|
||||
ADS1118_MULTIPLEXER_P0_N1 = 0b000,
|
||||
@@ -41,5 +40,4 @@ class ADS1118 : public Component,
|
||||
uint16_t config_{0};
|
||||
};
|
||||
|
||||
} // namespace ads1118
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ads1118
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ads1118 {
|
||||
namespace esphome::ads1118 {
|
||||
|
||||
static const char *const TAG = "ads1118.sensor";
|
||||
|
||||
@@ -27,5 +26,4 @@ void ADS1118Sensor::update() {
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ads1118
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ads1118
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
#include "../ads1118.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ads1118 {
|
||||
namespace esphome::ads1118 {
|
||||
|
||||
class ADS1118Sensor : public PollingComponent,
|
||||
public sensor::Sensor,
|
||||
@@ -32,5 +31,4 @@ class ADS1118Sensor : public PollingComponent,
|
||||
bool temperature_mode_;
|
||||
};
|
||||
|
||||
} // namespace ads1118
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ads1118
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome {
|
||||
namespace ags10 {
|
||||
namespace esphome::ags10 {
|
||||
static const char *const TAG = "ags10";
|
||||
|
||||
// Data acquisition.
|
||||
@@ -192,5 +191,4 @@ template<size_t N> optional<std::array<uint8_t, N>> AGS10Component::read_and_che
|
||||
|
||||
return data;
|
||||
}
|
||||
} // namespace ags10
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ags10
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ags10 {
|
||||
namespace esphome::ags10 {
|
||||
|
||||
class AGS10Component : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
@@ -136,5 +135,4 @@ template<typename... Ts> class AGS10SetZeroPointAction : public Action<Ts...>, p
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace ags10
|
||||
} // namespace esphome
|
||||
} // namespace esphome::ags10
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace aht10 {
|
||||
namespace esphome::aht10 {
|
||||
|
||||
static const char *const TAG = "aht10";
|
||||
static const uint8_t AHT10_INITIALIZE_CMD[] = {0xE1, 0x08, 0x00};
|
||||
@@ -160,5 +159,4 @@ void AHT10Component::dump_config() {
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||
}
|
||||
|
||||
} // namespace aht10
|
||||
} // namespace esphome
|
||||
} // namespace esphome::aht10
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace aht10 {
|
||||
namespace esphome::aht10 {
|
||||
|
||||
enum AHT10Variant { AHT10, AHT20 };
|
||||
|
||||
@@ -31,5 +30,4 @@ class AHT10Component : public PollingComponent, public i2c::I2CDevice {
|
||||
uint32_t start_time_{};
|
||||
};
|
||||
|
||||
} // namespace aht10
|
||||
} // namespace esphome
|
||||
} // namespace esphome::aht10
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace aic3204 {
|
||||
namespace esphome::aic3204 {
|
||||
|
||||
static const char *const TAG = "aic3204";
|
||||
|
||||
@@ -167,5 +166,4 @@ bool AIC3204::write_volume_() {
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace aic3204
|
||||
} // namespace esphome
|
||||
} // namespace esphome::aic3204
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/hal.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace aic3204 {
|
||||
namespace esphome::aic3204 {
|
||||
|
||||
// TLV320AIC3204 Register Addresses
|
||||
// Page 0
|
||||
@@ -83,5 +82,4 @@ class AIC3204 : public audio_dac::AudioDac, public Component, public i2c::I2CDev
|
||||
float volume_{0};
|
||||
};
|
||||
|
||||
} // namespace aic3204
|
||||
} // namespace esphome
|
||||
} // namespace esphome::aic3204
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "aic3204.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace aic3204 {
|
||||
namespace esphome::aic3204 {
|
||||
|
||||
template<typename... Ts> class SetAutoMuteAction : public Action<Ts...> {
|
||||
public:
|
||||
@@ -19,5 +18,4 @@ template<typename... Ts> class SetAutoMuteAction : public Action<Ts...> {
|
||||
AIC3204 *aic3204_;
|
||||
};
|
||||
|
||||
} // namespace aic3204
|
||||
} // namespace esphome
|
||||
} // namespace esphome::aic3204
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_ble {
|
||||
namespace esphome::airthings_ble {
|
||||
|
||||
static const char *const TAG = "airthings_ble";
|
||||
|
||||
@@ -29,7 +28,6 @@ bool AirthingsListener::parse_device(const esp32_ble_tracker::ESPBTDevice &devic
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace airthings_ble
|
||||
} // namespace esphome
|
||||
} // namespace esphome::airthings_ble
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,15 +5,13 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_ble {
|
||||
namespace esphome::airthings_ble {
|
||||
|
||||
class AirthingsListener : public esp32_ble_tracker::ESPBTDeviceListener {
|
||||
public:
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||
};
|
||||
|
||||
} // namespace airthings_ble
|
||||
} // namespace esphome
|
||||
} // namespace esphome::airthings_ble
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_wave_base {
|
||||
namespace esphome::airthings_wave_base {
|
||||
|
||||
static const char *const TAG = "airthings_wave_base";
|
||||
|
||||
@@ -211,7 +210,6 @@ void AirthingsWaveBase::set_response_timeout_() {
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace airthings_wave_base
|
||||
} // namespace esphome
|
||||
} // namespace esphome::airthings_wave_base
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -14,8 +14,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_wave_base {
|
||||
namespace esphome::airthings_wave_base {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
@@ -84,7 +83,6 @@ class AirthingsWaveBase : public PollingComponent, public ble_client::BLEClientN
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace airthings_wave_base
|
||||
} // namespace esphome
|
||||
} // namespace esphome::airthings_wave_base
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_wave_mini {
|
||||
namespace esphome::airthings_wave_mini {
|
||||
|
||||
static const char *const TAG = "airthings_wave_mini";
|
||||
|
||||
@@ -49,7 +48,6 @@ AirthingsWaveMini::AirthingsWaveMini() {
|
||||
espbt::ESPBTUUID::from_raw(ACCESS_CONTROL_POINT_CHARACTERISTIC_UUID);
|
||||
}
|
||||
|
||||
} // namespace airthings_wave_mini
|
||||
} // namespace esphome
|
||||
} // namespace esphome::airthings_wave_mini
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
#include "esphome/components/airthings_wave_base/airthings_wave_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_wave_mini {
|
||||
namespace esphome::airthings_wave_mini {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
@@ -34,7 +33,6 @@ class AirthingsWaveMini : public airthings_wave_base::AirthingsWaveBase {
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace airthings_wave_mini
|
||||
} // namespace esphome
|
||||
} // namespace esphome::airthings_wave_mini
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_wave_plus {
|
||||
namespace esphome::airthings_wave_plus {
|
||||
|
||||
static const char *const TAG = "airthings_wave_plus";
|
||||
|
||||
@@ -98,7 +97,6 @@ void AirthingsWavePlus::setup() {
|
||||
espbt::ESPBTUUID::from_raw(access_control_point_characteristic_uuid);
|
||||
}
|
||||
|
||||
} // namespace airthings_wave_plus
|
||||
} // namespace esphome
|
||||
} // namespace esphome::airthings_wave_plus
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
|
||||
#include "esphome/components/airthings_wave_base/airthings_wave_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace airthings_wave_plus {
|
||||
namespace esphome::airthings_wave_plus {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
@@ -58,7 +57,6 @@ class AirthingsWavePlus : public airthings_wave_base::AirthingsWaveBase {
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace airthings_wave_plus
|
||||
} // namespace esphome
|
||||
} // namespace esphome::airthings_wave_plus
|
||||
|
||||
#endif // USE_ESP32
|
||||
|
||||
@@ -13,7 +13,11 @@ from esphome.const import (
|
||||
CONF_WEB_SERVER,
|
||||
)
|
||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||
from esphome.core.entity_helpers import entity_duplicate_validator, setup_entity
|
||||
from esphome.core.entity_helpers import (
|
||||
entity_duplicate_validator,
|
||||
queue_entity_register,
|
||||
setup_entity,
|
||||
)
|
||||
from esphome.cpp_generator import MockObjClass
|
||||
|
||||
CODEOWNERS = ["@grahambrown11", "@hwstar"]
|
||||
@@ -181,7 +185,7 @@ async def setup_alarm_control_panel_core_(var, config):
|
||||
async def register_alarm_control_panel(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_alarm_control_panel(var))
|
||||
queue_entity_register("alarm_control_panel", config)
|
||||
CORE.register_platform_component("alarm_control_panel", var)
|
||||
await setup_alarm_control_panel_core_(var, config)
|
||||
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
|
||||
#ifdef USE_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace alpha3 {
|
||||
namespace esphome::alpha3 {
|
||||
|
||||
static const char *const TAG = "alpha3";
|
||||
|
||||
@@ -185,7 +184,6 @@ void Alpha3::update() {
|
||||
delay(25); // need to wait between requests
|
||||
}
|
||||
}
|
||||
} // namespace alpha3
|
||||
} // namespace esphome
|
||||
} // namespace esphome::alpha3
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user