Compare commits
369 Commits
v1.17.1
...
socket-ref
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88632b22e2 | ||
|
|
44041d2526 | ||
|
|
7cfc36cb70 | ||
|
|
08dd72e716 | ||
|
|
7b7e5f7db5 | ||
|
|
c9b170eab9 | ||
|
|
1771e673d2 | ||
|
|
d258e06fd7 | ||
|
|
854f4a8896 | ||
|
|
f94c221a9a | ||
|
|
6a2f0f5143 | ||
|
|
183e2a8471 | ||
|
|
c6c2842bdb | ||
|
|
f26767b65e | ||
|
|
98d32876b5 | ||
|
|
e5d0f3c036 | ||
|
|
cc15aaacbb | ||
|
|
553df1d57b | ||
|
|
b92311402a | ||
|
|
93796491af | ||
|
|
c038cf27a7 | ||
|
|
1f42d32eb5 | ||
|
|
06bde559da | ||
|
|
922f7167f5 | ||
|
|
90c0d3e12f | ||
|
|
bf5f846fc6 | ||
|
|
926bcc71ae | ||
|
|
40dd9c5dce | ||
|
|
ea4a458214 | ||
|
|
b3ae3e1feb | ||
|
|
fe7af21c91 | ||
|
|
29f72037fe | ||
|
|
1d6b4bfcef | ||
|
|
5bfac5ec09 | ||
|
|
dfffaace26 | ||
|
|
1d5f628c7a | ||
|
|
cb8a6f66fa | ||
|
|
cb21c7c18d | ||
|
|
0d104776bc | ||
|
|
768c71830b | ||
|
|
ceb0564ebf | ||
|
|
20f7eb7327 | ||
|
|
441d5bd44d | ||
|
|
9fa19df2ff | ||
|
|
39f64f597e | ||
|
|
160429eb24 | ||
|
|
6516c64e67 | ||
|
|
4c8a703084 | ||
|
|
335210d788 | ||
|
|
9b04e657db | ||
|
|
f7311aa025 | ||
|
|
fb24e55c8d | ||
|
|
b58ca46a46 | ||
|
|
76991cdcc4 | ||
|
|
69c7cf783e | ||
|
|
f751c3828e | ||
|
|
5c65f9f9ad | ||
|
|
81ae6709e4 | ||
|
|
593a3d48fb | ||
|
|
a8b90283d8 | ||
|
|
80076f935d | ||
|
|
34e8979d40 | ||
|
|
2966a62429 | ||
|
|
5983ccc55c | ||
|
|
de382b704c | ||
|
|
16dbbfabc6 | ||
|
|
af8d04818d | ||
|
|
ee19ef1aac | ||
|
|
5e2d4e332a | ||
|
|
c6c857dfff | ||
|
|
8dbac20f8b | ||
|
|
513066ba52 | ||
|
|
316777f757 | ||
|
|
246950159d | ||
|
|
31d6a54b06 | ||
|
|
5c3a6164bb | ||
|
|
1652914d39 | ||
|
|
618cfd9ec5 | ||
|
|
f97cfe9916 | ||
|
|
b9259a0238 | ||
|
|
5abbe385c5 | ||
|
|
b0a3891498 | ||
|
|
2a9e3d84fd | ||
|
|
a3dcac62f9 | ||
|
|
6b535b11f8 | ||
|
|
d9f09a7523 | ||
|
|
159744e09e | ||
|
|
c2637a76f7 | ||
|
|
237edd75d1 | ||
|
|
a34d5e3901 | ||
|
|
1dd43a75f2 | ||
|
|
1f5cbca509 | ||
|
|
3749c11f21 | ||
|
|
66cdb761dc | ||
|
|
f0d9ad6a4e | ||
|
|
03e317d052 | ||
|
|
ba461e51a8 | ||
|
|
80949521b6 | ||
|
|
acbb8e9fd0 | ||
|
|
90394a50df | ||
|
|
5379794f16 | ||
|
|
0a32321c85 | ||
|
|
c9062599df | ||
|
|
fc42f14448 | ||
|
|
3e65e6c69a | ||
|
|
3b3297d269 | ||
|
|
fc0deb642a | ||
|
|
9f2b2f51ff | ||
|
|
01a4b4e82f | ||
|
|
766866197b | ||
|
|
9b5a3cbcd3 | ||
|
|
d2ed3b9bec | ||
|
|
99d2db42cd | ||
|
|
7619507e6c | ||
|
|
71d9d64a02 | ||
|
|
2e49fd7b48 | ||
|
|
06912b492f | ||
|
|
442e58b07a | ||
|
|
799f04efc0 | ||
|
|
cc7dbeada6 | ||
|
|
45d368e3a1 | ||
|
|
628a94bad3 | ||
|
|
0651716b96 | ||
|
|
0992609bf4 | ||
|
|
c399905675 | ||
|
|
5cb0c11feb | ||
|
|
08b67e7aea | ||
|
|
07ae8ec553 | ||
|
|
04c3a43c17 | ||
|
|
b632344596 | ||
|
|
fb8ec79a52 | ||
|
|
7dd16df846 | ||
|
|
551e9c6111 | ||
|
|
cc9f0b3f47 | ||
|
|
d77c3abdc0 | ||
|
|
dd37a4e04c | ||
|
|
1f5c79bd17 | ||
|
|
623570a117 | ||
|
|
cdbc146e5d | ||
|
|
7ae611256a | ||
|
|
b62c47fede | ||
|
|
99f497c3b8 | ||
|
|
4f88c2489b | ||
|
|
294ba1fca7 | ||
|
|
be61b38a2c | ||
|
|
f9797825ad | ||
|
|
fd4b7d4588 | ||
|
|
062cedc200 | ||
|
|
79b9d0579d | ||
|
|
ab31117bf3 | ||
|
|
d31040f5d8 | ||
|
|
52d19fa43d | ||
|
|
8ca34f7098 | ||
|
|
4c4099966a | ||
|
|
86ac7f3a59 | ||
|
|
9e400a7857 | ||
|
|
d5278351da | ||
|
|
36861595f1 | ||
|
|
d604321f37 | ||
|
|
964ab65497 | ||
|
|
3b940b1c04 | ||
|
|
5fca480921 | ||
|
|
7051f897bc | ||
|
|
2cb3015a28 | ||
|
|
d0859a7d33 | ||
|
|
61ebc629f6 | ||
|
|
32f2da77f8 | ||
|
|
bfca3f242a | ||
|
|
3dfff2930a | ||
|
|
027e0de48e | ||
|
|
c811141a4f | ||
|
|
871c0ee2a5 | ||
|
|
b8a7741c61 | ||
|
|
b6011b9353 | ||
|
|
40a5005d94 | ||
|
|
c5eba21ff6 | ||
|
|
4891cfef56 | ||
|
|
4395664547 | ||
|
|
04d926af39 | ||
|
|
f9a31c1abb | ||
|
|
dca1c0f160 | ||
|
|
2419bc3678 | ||
|
|
c19b3ecd43 | ||
|
|
ef1e91d838 | ||
|
|
e5929225eb | ||
|
|
607c3ae651 | ||
|
|
5591832b50 | ||
|
|
25b116048c | ||
|
|
24ba9eba46 | ||
|
|
424c34225f | ||
|
|
d781f3a11b | ||
|
|
a80f9ed336 | ||
|
|
92bbedfa5a | ||
|
|
86710ed483 | ||
|
|
da7eb9ac90 | ||
|
|
c411043681 | ||
|
|
93f8ee7e60 | ||
|
|
0efc1f06f2 | ||
|
|
9ad9d64ac7 | ||
|
|
5a2cfa2798 | ||
|
|
eb24da7c82 | ||
|
|
f93e261d75 | ||
|
|
501f88ca86 | ||
|
|
360effcb72 | ||
|
|
eb9bd69405 | ||
|
|
11b8210e36 | ||
|
|
d23376b81e | ||
|
|
99d90845b5 | ||
|
|
ea0127e42b | ||
|
|
97a12c0169 | ||
|
|
635916737b | ||
|
|
65c50e4f01 | ||
|
|
5cf18235e3 | ||
|
|
b80f3fdec9 | ||
|
|
0426be9280 | ||
|
|
7c678659d4 | ||
|
|
13fe9e83fa | ||
|
|
4711f36a1f | ||
|
|
01e2a51132 | ||
|
|
a70a205ace | ||
|
|
33625e2dd3 | ||
|
|
0277218319 | ||
|
|
18a8c727fa | ||
|
|
80ad784a4e | ||
|
|
ebadaa9660 | ||
|
|
7bc51582f0 | ||
|
|
11fb54c74e | ||
|
|
913ac8b7e8 | ||
|
|
2b9350ce76 | ||
|
|
3b18f1b87f | ||
|
|
c5c24c1989 | ||
|
|
c3938d04f3 | ||
|
|
f968713be8 | ||
|
|
7b11284008 | ||
|
|
f39c0d52ee | ||
|
|
a3756a9600 | ||
|
|
afa436fe8f | ||
|
|
48b5ea9e59 | ||
|
|
56974153f1 | ||
|
|
9a2cd71571 | ||
|
|
d1c6368283 | ||
|
|
5c3268b8d4 | ||
|
|
25af5ab7c6 | ||
|
|
4d586b1446 | ||
|
|
bb759d52c8 | ||
|
|
9a2cf05c5f | ||
|
|
c79d700d03 | ||
|
|
482a3aebc9 | ||
|
|
387f249363 | ||
|
|
3d917d0b7e | ||
|
|
824f3187ac | ||
|
|
e3c27a483c | ||
|
|
a33bb32874 | ||
|
|
93d9d4b50a | ||
|
|
2376a2c941 | ||
|
|
b92702a312 | ||
|
|
b11d5f6799 | ||
|
|
072dce340e | ||
|
|
cccb1a2c9e | ||
|
|
063d9c47a4 | ||
|
|
8d8d421286 | ||
|
|
0ce57e5a39 | ||
|
|
aebad04c0b | ||
|
|
514d11d46f | ||
|
|
96e46db272 | ||
|
|
76f78877f6 | ||
|
|
4ffa68b773 | ||
|
|
8eaffee160 | ||
|
|
557a622f71 | ||
|
|
e9c6556296 | ||
|
|
dce9d59dfe | ||
|
|
d3e291b442 | ||
|
|
d4686c0fb1 | ||
|
|
ae9b247f47 | ||
|
|
a59761d292 | ||
|
|
030c87d142 | ||
|
|
95ed3e9d46 | ||
|
|
d0eaebe19f | ||
|
|
9ecead2645 | ||
|
|
98166dfa66 | ||
|
|
5645be4e0f | ||
|
|
9a7a205510 | ||
|
|
7e3b8fd346 | ||
|
|
3d6dcc9eee | ||
|
|
4f6982fbc5 | ||
|
|
00c144daeb | ||
|
|
fddb05c845 | ||
|
|
3b13e62d3f | ||
|
|
9e7acd6f75 | ||
|
|
7d9c043f1d | ||
|
|
bafbeefb37 | ||
|
|
54660300e9 | ||
|
|
5dc40049be | ||
|
|
1e46b4073f | ||
|
|
29fc4af0fc | ||
|
|
4030a2e253 | ||
|
|
ea80cb751b | ||
|
|
6aa61dbce7 | ||
|
|
cdc9c99d40 | ||
|
|
07f2931841 | ||
|
|
616ad04131 | ||
|
|
b966e58f9e | ||
|
|
a546677b08 | ||
|
|
5c3af1d3f6 | ||
|
|
66b0b6feeb | ||
|
|
7665a220a0 | ||
|
|
4250af4dd9 | ||
|
|
73252ccd25 | ||
|
|
33bf78c369 | ||
|
|
f33c2a48eb | ||
|
|
96ded4e402 | ||
|
|
44d5be6e7b | ||
|
|
076124eb71 | ||
|
|
44562dbef1 | ||
|
|
cafdcaec29 | ||
|
|
b660e5a7fa | ||
|
|
3b4ea0ed0a | ||
|
|
403b6e32e3 | ||
|
|
229bf719a2 | ||
|
|
2225594ee8 | ||
|
|
f7232b199a | ||
|
|
ffc6fe9ca0 | ||
|
|
06b8e4df27 | ||
|
|
b103be99e8 | ||
|
|
28ed42d879 | ||
|
|
98f0d75180 | ||
|
|
dd4fb85170 | ||
|
|
07b3327102 | ||
|
|
02aa75f68c | ||
|
|
07db9319ad | ||
|
|
4ae4a4ee88 | ||
|
|
a7c648b60b | ||
|
|
4d7c1ae143 | ||
|
|
cc6d1e85cc | ||
|
|
7fb116d87d | ||
|
|
cc43e01e34 | ||
|
|
6d3ccf4df5 | ||
|
|
bb3d0706d3 | ||
|
|
820dedbcd2 | ||
|
|
aed6f2b1ea | ||
|
|
bf1885af3f | ||
|
|
d8e4f5d56b | ||
|
|
af616473aa | ||
|
|
2033ac34e5 | ||
|
|
5e239d3d88 | ||
|
|
30893afd67 | ||
|
|
a39bb7b92f | ||
|
|
b53f9f2a81 | ||
|
|
586e36906d | ||
|
|
e6f8e73705 | ||
|
|
eaf9735eda | ||
|
|
2e50e1f506 | ||
|
|
76a6c39f25 | ||
|
|
bf01c22e1f | ||
|
|
99f14e03d4 | ||
|
|
d92c8ccadf | ||
|
|
7964b724ed | ||
|
|
e0c5b45694 | ||
|
|
26407e001b | ||
|
|
3ecae3f16f | ||
|
|
5c359856ff | ||
|
|
2d618768d5 | ||
|
|
808e3be324 | ||
|
|
9e23987db8 | ||
|
|
ad76312f66 | ||
|
|
2028362fd5 | ||
|
|
13e0d6b9a1 | ||
|
|
a6255c31fe | ||
|
|
46356cbc4a |
@@ -49,7 +49,7 @@ ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
|
||||
53
.clang-tidy
53
.clang-tidy
@@ -4,14 +4,29 @@ Checks: >-
|
||||
-abseil-*,
|
||||
-android-*,
|
||||
-boost-*,
|
||||
-bugprone-macro-parentheses,
|
||||
-bugprone-branch-clone,
|
||||
-bugprone-narrowing-conversions,
|
||||
-bugprone-signed-char-misuse,
|
||||
-bugprone-too-small-loop-variable,
|
||||
-cert-dcl50-cpp,
|
||||
-cert-err58-cpp,
|
||||
-clang-analyzer-core.CallAndMessage,
|
||||
-cert-oop57-cpp,
|
||||
-cert-str34-c,
|
||||
-clang-analyzer-optin.cplusplus.UninitializedObject,
|
||||
-clang-analyzer-osx.*,
|
||||
-clang-analyzer-security.*,
|
||||
-clang-diagnostic-delete-abstract-non-virtual-dtor,
|
||||
-clang-diagnostic-delete-non-abstract-non-virtual-dtor,
|
||||
-clang-diagnostic-shadow-field,
|
||||
-clang-diagnostic-sign-compare,
|
||||
-clang-diagnostic-unused-variable,
|
||||
-clang-diagnostic-unused-const-variable,
|
||||
-cppcoreguidelines-avoid-c-arrays,
|
||||
-cppcoreguidelines-avoid-goto,
|
||||
-cppcoreguidelines-c-copy-assignment-signature,
|
||||
-cppcoreguidelines-avoid-magic-numbers,
|
||||
-cppcoreguidelines-init-variables,
|
||||
-cppcoreguidelines-macro-usage,
|
||||
-cppcoreguidelines-narrowing-conversions,
|
||||
-cppcoreguidelines-non-private-member-variables-in-classes,
|
||||
-cppcoreguidelines-owning-memory,
|
||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
||||
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||
@@ -24,42 +39,52 @@ Checks: >-
|
||||
-cppcoreguidelines-pro-type-union-access,
|
||||
-cppcoreguidelines-pro-type-vararg,
|
||||
-cppcoreguidelines-special-member-functions,
|
||||
-fuchsia-*,
|
||||
-fuchsia-default-arguments,
|
||||
-fuchsia-multiple-inheritance,
|
||||
-fuchsia-overloaded-operator,
|
||||
-fuchsia-statically-constructed-objects,
|
||||
-fuchsia-default-arguments-declarations,
|
||||
-fuchsia-default-arguments-calls,
|
||||
-google-build-using-namespace,
|
||||
-google-explicit-constructor,
|
||||
-google-readability-braces-around-statements,
|
||||
-google-readability-casting,
|
||||
-google-readability-todo,
|
||||
-google-runtime-int,
|
||||
-google-runtime-references,
|
||||
-hicpp-*,
|
||||
-llvm-else-after-return,
|
||||
-llvm-header-guard,
|
||||
-llvm-include-order,
|
||||
-misc-unconventional-assign-operator,
|
||||
-llvm-qualified-auto,
|
||||
-llvmlibc-*,
|
||||
-misc-non-private-member-variables-in-classes,
|
||||
-misc-no-recursion,
|
||||
-misc-unused-parameters,
|
||||
-modernize-deprecated-headers,
|
||||
-modernize-pass-by-value,
|
||||
-modernize-pass-by-value,
|
||||
-modernize-avoid-c-arrays,
|
||||
-modernize-return-braced-init-list,
|
||||
-modernize-use-auto,
|
||||
-modernize-use-default-member-init,
|
||||
-modernize-use-equals-default,
|
||||
-modernize-use-trailing-return-type,
|
||||
-mpi-*,
|
||||
-objc-*,
|
||||
-performance-unnecessary-value-param,
|
||||
-readability-braces-around-statements,
|
||||
-readability-const-return-type,
|
||||
-readability-convert-member-functions-to-static,
|
||||
-readability-else-after-return,
|
||||
-readability-implicit-bool-conversion,
|
||||
-readability-isolate-declaration,
|
||||
-readability-magic-numbers,
|
||||
-readability-make-member-function-const,
|
||||
-readability-named-parameter,
|
||||
-readability-qualified-auto,
|
||||
-readability-redundant-access-specifiers,
|
||||
-readability-redundant-member-init,
|
||||
-warnings-as-errors,
|
||||
-zircon-*
|
||||
-readability-redundant-string-init,
|
||||
-readability-uppercase-literal-suffix,
|
||||
-readability-use-anyofallof,
|
||||
-warnings-as-errors
|
||||
WarningsAsErrors: '*'
|
||||
HeaderFilterRegex: '^.*/src/esphome/.*'
|
||||
AnalyzeTemporaryDtors: false
|
||||
FormatStyle: google
|
||||
CheckOptions:
|
||||
|
||||
@@ -2,16 +2,29 @@
|
||||
"name": "ESPHome Dev",
|
||||
"context": "..",
|
||||
"dockerFile": "../docker/Dockerfile.dev",
|
||||
"postCreateCommand": "mkdir -p config && pip3 install -e .",
|
||||
"runArgs": ["--privileged", "-e", "ESPHOME_DASHBOARD_USE_PING=1"],
|
||||
"postCreateCommand": [
|
||||
"script/devcontainer-post-create"
|
||||
],
|
||||
"runArgs": [
|
||||
"--privileged",
|
||||
"-e",
|
||||
"ESPHOME_DASHBOARD_USE_PING=1"
|
||||
],
|
||||
"appPort": 6052,
|
||||
"extensions": [
|
||||
// python
|
||||
"ms-python.python",
|
||||
"visualstudioexptteam.vscodeintellicode",
|
||||
"redhat.vscode-yaml"
|
||||
// yaml
|
||||
"redhat.vscode-yaml",
|
||||
// cpp
|
||||
"ms-vscode.cpptools",
|
||||
// editorconfig
|
||||
"editorconfig.editorconfig",
|
||||
],
|
||||
"settings": {
|
||||
"python.pythonPath": "/usr/local/bin/python",
|
||||
"python.languageServer": "Pylance",
|
||||
"python.pythonPath": "/usr/bin/python3",
|
||||
"python.linting.pylintEnabled": true,
|
||||
"python.linting.enabled": true,
|
||||
"python.formatting.provider": "black",
|
||||
@@ -19,7 +32,7 @@
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnType": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"terminal.integrated.shell.linux": "/bin/bash",
|
||||
"terminal.integrated.defaultProfile.linux": "bash",
|
||||
"yaml.customTags": [
|
||||
"!secret scalar",
|
||||
"!lambda scalar",
|
||||
@@ -27,6 +40,18 @@
|
||||
"!include_dir_list scalar",
|
||||
"!include_dir_merge_list scalar",
|
||||
"!include_dir_merge_named scalar"
|
||||
]
|
||||
],
|
||||
"files.exclude": {
|
||||
"**/.git": true,
|
||||
"**/.DS_Store": true,
|
||||
"**/*.pyc": {
|
||||
"when": "$(basename).py"
|
||||
},
|
||||
"**/__pycache__": true
|
||||
},
|
||||
"files.associations": {
|
||||
"**/.vscode/*.json": "jsonc"
|
||||
},
|
||||
"C_Cpp.clang_format_path": "/usr/bin/clang-format-11",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,10 @@ venv.bak/
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
# PlatformIO
|
||||
.pio/
|
||||
|
||||
# ESPHome
|
||||
config/
|
||||
examples/
|
||||
Dockerfile
|
||||
|
||||
@@ -7,7 +7,7 @@ insert_final_newline = true
|
||||
charset = utf-8
|
||||
|
||||
# python
|
||||
[*.{py}]
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
@@ -25,4 +25,10 @@ indent_size = 2
|
||||
[*.{yaml,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
quote_type = single
|
||||
quote_type = single
|
||||
|
||||
# JSON
|
||||
[*.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
|
||||
7
.github/FUNDING.yml
vendored
7
.github/FUNDING.yml
vendored
@@ -1,8 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github:
|
||||
patreon: ottowinter
|
||||
open_collective:
|
||||
ko_fi:
|
||||
tidelift:
|
||||
custom: https://esphome.io/guides/supporters.html
|
||||
custom: https://www.nabucasa.com
|
||||
|
||||
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
16
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,25 +1,22 @@
|
||||
# What does this implement/fix?
|
||||
|
||||
Quick description
|
||||
Quick description and explanation of changes
|
||||
|
||||
## Types of changes
|
||||
|
||||
- [ ] Bugfix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Configuration change (this will require users to update their yaml configuration files to keep working)
|
||||
- [ ] Other
|
||||
|
||||
**Related issue or feature (if applicable):** fixes <link to issue>
|
||||
|
||||
**Pull request in [esphome-docs](https://github.com/esphome/esphome-docs) with documentation (if applicable):** esphome/esphome-docs#<esphome-docs PR number goes here>
|
||||
|
||||
# Test Environment
|
||||
|
||||
## Test Environment
|
||||
|
||||
- [ ] ESP32
|
||||
- [ ] ESP8266
|
||||
- [ ] Windows
|
||||
- [ ] Mac OS
|
||||
- [ ] Linux
|
||||
|
||||
## Example entry for `config.yaml`:
|
||||
<!--
|
||||
@@ -34,11 +31,6 @@ Quick description
|
||||
|
||||
```
|
||||
|
||||
# Explain your changes
|
||||
|
||||
Describe your changes here to communicate to the maintainers **why we should accept this pull request**.
|
||||
Very important to fill if no issue linked
|
||||
|
||||
## Checklist:
|
||||
- [ ] The code change is tested and works locally.
|
||||
- [ ] Tests have been added to verify that the new code works (under `tests/` folder).
|
||||
|
||||
49
.github/workflows/ci-docker.yml
vendored
49
.github/workflows/ci-docker.yml
vendored
@@ -3,7 +3,7 @@ name: CI for docker images
|
||||
# Only run when docker paths change
|
||||
on:
|
||||
push:
|
||||
branches: [dev, beta, master]
|
||||
branches: [dev, beta, release]
|
||||
paths:
|
||||
- 'docker/**'
|
||||
- '.github/workflows/**'
|
||||
@@ -18,38 +18,23 @@ jobs:
|
||||
name: Build docker containers
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch: [amd64, armv7, aarch64]
|
||||
build_type: ["hassio", "docker"]
|
||||
build_type: ["ha-addon", "docker", "lint"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up env variables
|
||||
run: |
|
||||
base_version="3.0.0"
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- name: Set TAG
|
||||
run: |
|
||||
echo "TAG=check" >> $GITHUB_ENV
|
||||
|
||||
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
||||
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
||||
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
|
||||
dockerfile="docker/Dockerfile.hassio"
|
||||
else
|
||||
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
|
||||
build_to="esphome/esphome-${{ matrix.arch }}"
|
||||
dockerfile="docker/Dockerfile"
|
||||
fi
|
||||
|
||||
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
|
||||
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
|
||||
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
|
||||
- name: Pull for cache
|
||||
run: |
|
||||
docker pull "${BUILD_TO}:dev" || true
|
||||
- name: Register QEMU binfmt
|
||||
run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
|
||||
- run: |
|
||||
docker build \
|
||||
--build-arg "BUILD_FROM=${BUILD_FROM}" \
|
||||
--build-arg "BUILD_VERSION=ci" \
|
||||
--cache-from "${BUILD_TO}:dev" \
|
||||
--file "${DOCKERFILE}" \
|
||||
.
|
||||
- name: Run build
|
||||
run: |
|
||||
docker/build.py \
|
||||
--tag "${TAG}" \
|
||||
--arch "${{ matrix.arch }}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
build
|
||||
|
||||
193
.github/workflows/ci.yml
vendored
193
.github/workflows/ci.yml
vendored
@@ -4,67 +4,98 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
# On dev branch release-dev already performs CI checks
|
||||
# On other branches the `pull_request` trigger will be used
|
||||
branches: [beta, master]
|
||||
branches: [dev, beta, release]
|
||||
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
lint-clang-format:
|
||||
ci-with-container:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ubuntu-latest
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: esphome/esphome-lint:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Set up the pio project so that the cpp checks know how files are compiled
|
||||
# (build flags, libraries etc)
|
||||
- name: Set up platformio environment
|
||||
run: pio init --ide atom
|
||||
|
||||
- name: Run clang-format
|
||||
run: script/clang-format -i
|
||||
- name: Suggest changes
|
||||
run: script/ci-suggest-changes
|
||||
|
||||
lint-clang-tidy:
|
||||
runs-on: ubuntu-latest
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: esphome/esphome-lint:latest
|
||||
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
split: [1, 2, 3, 4]
|
||||
include:
|
||||
- id: clang-format
|
||||
name: Run script/clang-format
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy 1/4
|
||||
split: 1
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy 2/4
|
||||
split: 2
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy 3/4
|
||||
split: 3
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy 4/4
|
||||
split: 4
|
||||
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: ghcr.io/esphome/esphome-lint:1.1
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Set up the pio project so that the cpp checks know how files are compiled
|
||||
# (build flags, libraries etc)
|
||||
- name: Set up platformio environment
|
||||
run: pio init --ide atom
|
||||
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
|
||||
# Also run git-diff-index so that the step is marked as failed on formatting errors,
|
||||
# since clang-format doesn't do anything but change files if -i is passed.
|
||||
- name: Run clang-format
|
||||
run: |
|
||||
script/clang-format -i
|
||||
git diff-index --quiet HEAD --
|
||||
if: ${{ matrix.id == 'clang-format' }}
|
||||
|
||||
- name: Run clang-tidy
|
||||
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
|
||||
- name: Suggest changes
|
||||
run: script/ci-suggest-changes
|
||||
if: ${{ matrix.id == 'clang-tidy' }}
|
||||
|
||||
lint-python:
|
||||
- name: Suggested changes
|
||||
run: script/ci-suggest-changes
|
||||
if: always()
|
||||
|
||||
ci:
|
||||
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
||||
# This way, all dependencies are cached via the cache action.
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- id: ci-custom
|
||||
name: Run script/ci-custom
|
||||
- id: lint-python
|
||||
name: Run script/lint-python
|
||||
- id: test
|
||||
file: tests/test1.yaml
|
||||
name: Test tests/test1.yaml
|
||||
- id: test
|
||||
file: tests/test2.yaml
|
||||
name: Test tests/test2.yaml
|
||||
- id: test
|
||||
file: tests/test3.yaml
|
||||
name: Test tests/test3.yaml
|
||||
- id: test
|
||||
file: tests/test4.yaml
|
||||
name: Test tests/test4.yaml
|
||||
- id: test
|
||||
file: tests/test5.yaml
|
||||
name: Test tests/test5.yaml
|
||||
- id: pytest
|
||||
name: Run pytest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
@@ -72,6 +103,17 @@ jobs:
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
|
||||
# Use per test platformio cache because tests have different platform versions
|
||||
- name: Cache ~/.platformio
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: test-home-platformio-${{ matrix.file }}-${{ hashFiles('esphome/core/config.py') }}
|
||||
restore-keys: |
|
||||
test-home-platformio-${{ matrix.file }}-
|
||||
if: ${{ matrix.id == 'test' }}
|
||||
|
||||
- name: Set up python environment
|
||||
run: script/setup
|
||||
|
||||
@@ -80,81 +122,22 @@ jobs:
|
||||
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/pytest.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
|
||||
- name: Lint Custom
|
||||
run: script/ci-custom.py
|
||||
run: |
|
||||
script/ci-custom.py
|
||||
script/build_codeowners.py --check
|
||||
if: ${{ matrix.id == 'ci-custom' }}
|
||||
- name: Lint Python
|
||||
run: script/lint-python
|
||||
- name: Lint CODEOWNERS
|
||||
run: script/build_codeowners.py --check
|
||||
if: ${{ matrix.id == 'lint-python' }}
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test:
|
||||
- test1
|
||||
- test2
|
||||
- test3
|
||||
- test4
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
# Use per test platformio cache because tests have different platform versions
|
||||
- name: Cache ~/.platformio
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core_config.py') }}
|
||||
restore-keys: |
|
||||
test-home-platformio-${{ matrix.test }}-
|
||||
- name: Set up environment
|
||||
run: script/setup
|
||||
- run: esphome compile ${{ matrix.file }}
|
||||
if: ${{ matrix.id == 'test' }}
|
||||
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- run: esphome tests/${{ matrix.test }}.yaml compile
|
||||
|
||||
pytest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
- name: Set up environment
|
||||
run: script/setup
|
||||
- name: Install Github Actions annotator
|
||||
run: pip install pytest-github-actions-annotate-failures
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- name: Run pytest
|
||||
run: |
|
||||
pytest \
|
||||
-qq \
|
||||
--durations=10 \
|
||||
-o console_output_style=count \
|
||||
tests
|
||||
pytest -vv --tb=native tests
|
||||
if: ${{ matrix.id == 'pytest' }}
|
||||
|
||||
100
.github/workflows/docker-lint-build.yml
vendored
Normal file
100
.github/workflows/docker-lint-build.yml
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
name: Build and publish lint docker image
|
||||
|
||||
# Only run when docker paths change
|
||||
on:
|
||||
push:
|
||||
branches: [dev]
|
||||
paths:
|
||||
- 'docker/Dockerfile.lint'
|
||||
- 'requirements.txt'
|
||||
- 'requirements_optional.txt'
|
||||
- 'requirements_test.txt'
|
||||
- 'platformio.ini'
|
||||
- '.github/workflows/docker-lint-build.yml'
|
||||
|
||||
jobs:
|
||||
deploy-docker:
|
||||
name: Build and publish docker containers
|
||||
if: github.repository == 'esphome/esphome'
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, armv7, aarch64]
|
||||
build_type: ["lint"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- name: Set TAG
|
||||
run: |
|
||||
echo "TAG=1.1" >> $GITHUB_ENV
|
||||
|
||||
- name: Run build
|
||||
run: |
|
||||
docker/build.py \
|
||||
--tag "${TAG}" \
|
||||
--arch "${{ matrix.arch }}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
build
|
||||
|
||||
- name: Log in to docker hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Log in to the GitHub container registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run push
|
||||
run: |
|
||||
docker/build.py \
|
||||
--tag "${TAG}" \
|
||||
--arch "${{ matrix.arch }}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
push
|
||||
|
||||
deploy-docker-manifest:
|
||||
if: github.repository == 'esphome/esphome'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy-docker]
|
||||
strategy:
|
||||
matrix:
|
||||
build_type: ["lint"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- name: Set TAG
|
||||
run: |
|
||||
echo "TAG=1.1" >> $GITHUB_ENV
|
||||
- name: Enable experimental manifest support
|
||||
run: |
|
||||
mkdir -p ~/.docker
|
||||
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
||||
|
||||
- name: Log in to docker hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Log in to the GitHub container registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Run manifest
|
||||
run: |
|
||||
docker/build.py \
|
||||
--tag "${TAG}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
manifest
|
||||
19
.github/workflows/matchers/pytest.json
vendored
Normal file
19
.github/workflows/matchers/pytest.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "pytest",
|
||||
"fileLocation": "absolute",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^\\s+File \"(.*)\", line (\\d+), in (.*)$",
|
||||
"file": 1,
|
||||
"line": 2
|
||||
},
|
||||
{
|
||||
"regexp": "^\\s+(.*)$",
|
||||
"message": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
246
.github/workflows/release-dev.yml
vendored
246
.github/workflows/release-dev.yml
vendored
@@ -1,246 +0,0 @@
|
||||
name: Publish dev releases to docker hub
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
# THE LINT/TEST JOBS ARE COPIED FROM ci.yaml
|
||||
|
||||
lint-clang-format:
|
||||
runs-on: ubuntu-latest
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: esphome/esphome-lint:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Set up the pio project so that the cpp checks know how files are compiled
|
||||
# (build flags, libraries etc)
|
||||
- name: Set up platformio environment
|
||||
run: pio init --ide atom
|
||||
|
||||
- name: Run clang-format
|
||||
run: script/clang-format -i
|
||||
- name: Suggest changes
|
||||
run: script/ci-suggest-changes
|
||||
|
||||
lint-clang-tidy:
|
||||
runs-on: ubuntu-latest
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: esphome/esphome-lint:latest
|
||||
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
split: [1, 2, 3, 4]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Set up the pio project so that the cpp checks know how files are compiled
|
||||
# (build flags, libraries etc)
|
||||
- name: Set up platformio environment
|
||||
run: pio init --ide atom
|
||||
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
- name: Run clang-tidy
|
||||
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
|
||||
- name: Suggest changes
|
||||
run: script/ci-suggest-changes
|
||||
|
||||
lint-python:
|
||||
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
||||
# This way, all dependencies are cached via the cache action.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
- name: Set up python environment
|
||||
run: script/setup
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- name: Lint Custom
|
||||
run: script/ci-custom.py
|
||||
- name: Lint Python
|
||||
run: script/lint-python
|
||||
- name: Lint CODEOWNERS
|
||||
run: script/build_codeowners.py --check
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test:
|
||||
- test1
|
||||
- test2
|
||||
- test3
|
||||
- test4
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
# Use per test platformio cache because tests have different platform versions
|
||||
- name: Cache ~/.platformio
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core_config.py') }}
|
||||
restore-keys: |
|
||||
test-home-platformio-${{ matrix.test }}-
|
||||
- name: Set up environment
|
||||
run: script/setup
|
||||
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- run: esphome tests/${{ matrix.test }}.yaml compile
|
||||
|
||||
pytest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
- name: Set up environment
|
||||
run: script/setup
|
||||
- name: Install Github Actions annotator
|
||||
run: pip install pytest-github-actions-annotate-failures
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- name: Run pytest
|
||||
run: |
|
||||
pytest \
|
||||
-qq \
|
||||
--durations=10 \
|
||||
-o console_output_style=count \
|
||||
tests
|
||||
|
||||
deploy-docker:
|
||||
name: Build and publish docker containers
|
||||
if: github.repository == 'esphome/esphome'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, armv7, aarch64]
|
||||
# Hassio dev image doesn't use esphome/esphome-hassio-$arch and uses base directly
|
||||
build_type: ["docker"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set TAG
|
||||
run: |
|
||||
TAG="${GITHUB_SHA:0:7}"
|
||||
echo "TAG=${TAG}" >> $GITHUB_ENV
|
||||
- name: Set up env variables
|
||||
run: |
|
||||
base_version="3.0.0"
|
||||
|
||||
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
||||
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
||||
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
|
||||
dockerfile="docker/Dockerfile.hassio"
|
||||
else
|
||||
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
|
||||
build_to="esphome/esphome-${{ matrix.arch }}"
|
||||
dockerfile="docker/Dockerfile"
|
||||
fi
|
||||
|
||||
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
|
||||
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
|
||||
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
|
||||
- name: Pull for cache
|
||||
run: |
|
||||
docker pull "${BUILD_TO}:dev" || true
|
||||
- name: Register QEMU binfmt
|
||||
run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
|
||||
- run: |
|
||||
docker build \
|
||||
--build-arg "BUILD_FROM=${BUILD_FROM}" \
|
||||
--build-arg "BUILD_VERSION=${TAG}" \
|
||||
--tag "${BUILD_TO}:${TAG}" \
|
||||
--tag "${BUILD_TO}:dev" \
|
||||
--cache-from "${BUILD_TO}:dev" \
|
||||
--file "${DOCKERFILE}" \
|
||||
.
|
||||
- name: Log in to docker hub
|
||||
env:
|
||||
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||
- run: |
|
||||
docker push "${BUILD_TO}:${TAG}"
|
||||
docker push "${BUILD_TO}:dev"
|
||||
|
||||
|
||||
deploy-docker-manifest:
|
||||
if: github.repository == 'esphome/esphome'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy-docker]
|
||||
steps:
|
||||
- name: Enable experimental manifest support
|
||||
run: |
|
||||
mkdir -p ~/.docker
|
||||
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
||||
- name: Set TAG
|
||||
run: |
|
||||
TAG="${GITHUB_SHA:0:7}"
|
||||
echo "TAG=${TAG}" >> $GITHUB_ENV
|
||||
- name: Log in to docker hub
|
||||
env:
|
||||
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||
- name: "Create the manifest"
|
||||
run: |
|
||||
docker manifest create esphome/esphome:${TAG} \
|
||||
esphome/esphome-aarch64:${TAG} \
|
||||
esphome/esphome-amd64:${TAG} \
|
||||
esphome/esphome-armv7:${TAG}
|
||||
docker manifest push esphome/esphome:${TAG}
|
||||
|
||||
docker manifest create esphome/esphome:dev \
|
||||
esphome/esphome-aarch64:${TAG} \
|
||||
esphome/esphome-amd64:${TAG} \
|
||||
esphome/esphome-armv7:${TAG}
|
||||
docker manifest push esphome/esphome:dev
|
||||
316
.github/workflows/release.yml
vendored
316
.github/workflows/release.yml
vendored
@@ -1,163 +1,35 @@
|
||||
name: Publish Release
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
types: [published]
|
||||
schedule:
|
||||
- cron: "0 2 * * *"
|
||||
|
||||
jobs:
|
||||
# THE LINT/TEST JOBS ARE COPIED FROM ci.yaml
|
||||
|
||||
lint-clang-format:
|
||||
init:
|
||||
name: Initialize build
|
||||
runs-on: ubuntu-latest
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: esphome/esphome-lint:latest
|
||||
outputs:
|
||||
tag: ${{ steps.tag.outputs.tag }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Set up the pio project so that the cpp checks know how files are compiled
|
||||
# (build flags, libraries etc)
|
||||
- name: Set up platformio environment
|
||||
run: pio init --ide atom
|
||||
|
||||
- name: Run clang-format
|
||||
run: script/clang-format -i
|
||||
- name: Suggest changes
|
||||
run: script/ci-suggest-changes
|
||||
|
||||
lint-clang-tidy:
|
||||
runs-on: ubuntu-latest
|
||||
# cpp lint job runs with esphome-lint docker image so that clang-format-*
|
||||
# doesn't have to be installed
|
||||
container: esphome/esphome-lint:latest
|
||||
# Split clang-tidy check into 4 jobs. Each one will check 1/4th of the .cpp files
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
split: [1, 2, 3, 4]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# Set up the pio project so that the cpp checks know how files are compiled
|
||||
# (build flags, libraries etc)
|
||||
- name: Set up platformio environment
|
||||
run: pio init --ide atom
|
||||
|
||||
|
||||
- name: Register problem matchers
|
||||
- name: Get tag
|
||||
id: tag
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/clang-tidy.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
- name: Run clang-tidy
|
||||
run: script/clang-tidy --all-headers --fix --split-num 4 --split-at ${{ matrix.split }}
|
||||
- name: Suggest changes
|
||||
run: script/ci-suggest-changes
|
||||
|
||||
lint-python:
|
||||
# Don't use the esphome-lint docker image because it may contain outdated requirements.
|
||||
# This way, all dependencies are cached via the cache action.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
- name: Set up python environment
|
||||
run: script/setup
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/ci-custom.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/lint-python.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- name: Lint Custom
|
||||
run: script/ci-custom.py
|
||||
- name: Lint Python
|
||||
run: script/lint-python
|
||||
- name: Lint CODEOWNERS
|
||||
run: script/build_codeowners.py --check
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
test:
|
||||
- test1
|
||||
- test2
|
||||
- test3
|
||||
- test4
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
# Use per test platformio cache because tests have different platform versions
|
||||
- name: Cache ~/.platformio
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.platformio
|
||||
key: test-home-platformio-${{ matrix.test }}-${{ hashFiles('esphome/core_config.py') }}
|
||||
restore-keys: |
|
||||
test-home-platformio-${{ matrix.test }}-
|
||||
- name: Set up environment
|
||||
run: script/setup
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/gcc.json"
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- run: esphome tests/${{ matrix.test }}.yaml compile
|
||||
|
||||
pytest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.7'
|
||||
- name: Cache pip modules
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: esphome-pip-3.7-${{ hashFiles('setup.py') }}
|
||||
restore-keys: |
|
||||
esphome-pip-3.7-
|
||||
- name: Set up environment
|
||||
run: script/setup
|
||||
- name: Install Github Actions annotator
|
||||
run: pip install pytest-github-actions-annotate-failures
|
||||
|
||||
- name: Register problem matchers
|
||||
run: |
|
||||
echo "::add-matcher::.github/workflows/matchers/python.json"
|
||||
- name: Run pytest
|
||||
run: |
|
||||
pytest \
|
||||
-qq \
|
||||
--durations=10 \
|
||||
-o console_output_style=count \
|
||||
tests
|
||||
if [[ "$GITHUB_EVENT_NAME" = "release" ]]; then
|
||||
TAG="${GITHUB_REF#refs/tags/v}"
|
||||
else
|
||||
TAG=$(cat esphome/const.py | sed -n -E "s/^__version__\s+=\s+\"(.+)\"$/\1/p")
|
||||
today="$(date --utc '+%Y%m%d')"
|
||||
TAG="${TAG}${today}"
|
||||
fi
|
||||
echo "::set-output name=tag::${TAG}"
|
||||
|
||||
deploy-pypi:
|
||||
name: Build and publish to PyPi
|
||||
if: github.repository == 'esphome/esphome'
|
||||
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
|
||||
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
@@ -181,119 +53,85 @@ jobs:
|
||||
name: Build and publish docker containers
|
||||
if: github.repository == 'esphome/esphome'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [lint-clang-format, lint-clang-tidy, lint-python, test, pytest]
|
||||
needs: [init]
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [amd64, armv7, aarch64]
|
||||
build_type: ["hassio", "docker"]
|
||||
build_type: ["ha-addon", "docker"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set TAG
|
||||
run: |
|
||||
TAG="${GITHUB_REF#refs/tags/v}"
|
||||
echo "TAG=${TAG}" >> $GITHUB_ENV
|
||||
- name: Set up env variables
|
||||
run: |
|
||||
base_version="3.0.0"
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
|
||||
if [[ "${{ matrix.build_type }}" == "hassio" ]]; then
|
||||
build_from="esphome/esphome-hassio-base-${{ matrix.arch }}:${base_version}"
|
||||
build_to="esphome/esphome-hassio-${{ matrix.arch }}"
|
||||
dockerfile="docker/Dockerfile.hassio"
|
||||
else
|
||||
build_from="esphome/esphome-base-${{ matrix.arch }}:${base_version}"
|
||||
build_to="esphome/esphome-${{ matrix.arch }}"
|
||||
dockerfile="docker/Dockerfile"
|
||||
fi
|
||||
- name: Run build
|
||||
run: |
|
||||
docker/build.py \
|
||||
--tag "${{ needs.init.outputs.tag }}" \
|
||||
--arch "${{ matrix.arch }}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
build
|
||||
|
||||
if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then
|
||||
cache_tag="beta"
|
||||
else
|
||||
cache_tag="latest"
|
||||
fi
|
||||
- name: Log in to docker hub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Log in to the GitHub container registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Set env variables so these values don't need to be calculated again
|
||||
echo "BUILD_FROM=${build_from}" >> $GITHUB_ENV
|
||||
echo "BUILD_TO=${build_to}" >> $GITHUB_ENV
|
||||
echo "DOCKERFILE=${dockerfile}" >> $GITHUB_ENV
|
||||
echo "CACHE_TAG=${cache_tag}" >> $GITHUB_ENV
|
||||
- name: Pull for cache
|
||||
run: |
|
||||
docker pull "${BUILD_TO}:${CACHE_TAG}" || true
|
||||
- name: Register QEMU binfmt
|
||||
run: docker run --rm --privileged multiarch/qemu-user-static:5.2.0-2 --reset -p yes
|
||||
- run: |
|
||||
docker build \
|
||||
--build-arg "BUILD_FROM=${BUILD_FROM}" \
|
||||
--build-arg "BUILD_VERSION=${TAG}" \
|
||||
--tag "${BUILD_TO}:${TAG}" \
|
||||
--cache-from "${BUILD_TO}:${CACHE_TAG}" \
|
||||
--file "${DOCKERFILE}" \
|
||||
.
|
||||
- name: Log in to docker hub
|
||||
env:
|
||||
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||
- run: docker push "${BUILD_TO}:${TAG}"
|
||||
|
||||
# Always publish to beta tag (also full releases)
|
||||
- name: Publish docker beta tag
|
||||
run: |
|
||||
docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:beta"
|
||||
docker push "${BUILD_TO}:beta"
|
||||
|
||||
- if: ${{ !github.event.release.prerelease }}
|
||||
name: Publish docker latest tag
|
||||
run: |
|
||||
docker tag "${BUILD_TO}:${TAG}" "${BUILD_TO}:latest"
|
||||
docker push "${BUILD_TO}:latest"
|
||||
- name: Run push
|
||||
run: |
|
||||
docker/build.py \
|
||||
--tag "${{ needs.init.outputs.tag }}" \
|
||||
--arch "${{ matrix.arch }}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
push
|
||||
|
||||
deploy-docker-manifest:
|
||||
if: github.repository == 'esphome/esphome'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy-docker]
|
||||
needs: [init, deploy-docker]
|
||||
strategy:
|
||||
matrix:
|
||||
build_type: ["ha-addon", "docker"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.9'
|
||||
- name: Enable experimental manifest support
|
||||
run: |
|
||||
mkdir -p ~/.docker
|
||||
echo "{\"experimental\": \"enabled\"}" > ~/.docker/config.json
|
||||
- name: Set TAG
|
||||
run: |
|
||||
TAG="${GITHUB_REF#refs/tags/v}"
|
||||
echo "TAG=${TAG}" >> $GITHUB_ENV
|
||||
|
||||
- name: Log in to docker hub
|
||||
env:
|
||||
DOCKER_USER: ${{ secrets.DOCKER_USER }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: docker login -u "${DOCKER_USER}" -p "${DOCKER_PASSWORD}"
|
||||
- name: "Create the manifest"
|
||||
run: |
|
||||
docker manifest create esphome/esphome:${TAG} \
|
||||
esphome/esphome-aarch64:${TAG} \
|
||||
esphome/esphome-amd64:${TAG} \
|
||||
esphome/esphome-armv7:${TAG}
|
||||
docker manifest push esphome/esphome:${TAG}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USER }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Log in to the GitHub container registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Publish docker beta tag
|
||||
- name: Run manifest
|
||||
run: |
|
||||
docker manifest create esphome/esphome:beta \
|
||||
esphome/esphome-aarch64:${TAG} \
|
||||
esphome/esphome-amd64:${TAG} \
|
||||
esphome/esphome-armv7:${TAG}
|
||||
docker manifest push esphome/esphome:beta
|
||||
|
||||
- name: Publish docker latest tag
|
||||
if: ${{ !github.event.release.prerelease }}
|
||||
run: |
|
||||
docker manifest create esphome/esphome:latest \
|
||||
esphome/esphome-aarch64:${TAG} \
|
||||
esphome/esphome-amd64:${TAG} \
|
||||
esphome/esphome-armv7:${TAG}
|
||||
docker manifest push esphome/esphome:latest
|
||||
docker/build.py \
|
||||
--tag "${{ needs.init.outputs.tag }}" \
|
||||
--build-type "${{ matrix.build_type }}" \
|
||||
manifest
|
||||
|
||||
deploy-hassio-repo:
|
||||
if: github.repository == 'esphome/esphome'
|
||||
if: github.repository == 'esphome/esphome' && github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
needs: [deploy-docker]
|
||||
steps:
|
||||
@@ -306,4 +144,4 @@ jobs:
|
||||
-X POST \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/esphome/hassio/actions/workflows/bump-version.yml/dispatches \
|
||||
-d "{\"ref\":\"master\",\"inputs\":{\"version\":\"$TAG\"}}"
|
||||
-d "{\"ref\":\"main\",\"inputs\":{\"version\":\"$TAG\"}}"
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -13,6 +13,9 @@ __pycache__/
|
||||
# Intellij Idea
|
||||
.idea
|
||||
|
||||
# Vim
|
||||
*.swp
|
||||
|
||||
# Hide some OS X stuff
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
@@ -100,6 +103,8 @@ CMakeLists.txt
|
||||
|
||||
# CMake
|
||||
cmake-build-debug/
|
||||
cmake-build-livingroom8266/
|
||||
cmake-build-livingroom32/
|
||||
cmake-build-release/
|
||||
|
||||
CMakeCache.txt
|
||||
@@ -120,4 +125,5 @@ config/
|
||||
tests/build/
|
||||
tests/.esphome/
|
||||
/.temp-clang-tidy.cpp
|
||||
/.temp/
|
||||
.pio/
|
||||
|
||||
@@ -23,5 +23,5 @@ repos:
|
||||
- id: no-commit-to-branch
|
||||
args:
|
||||
- --branch=dev
|
||||
- --branch=master
|
||||
- --branch=release
|
||||
- --branch=beta
|
||||
|
||||
35
.vscode/tasks.json
vendored
35
.vscode/tasks.json
vendored
@@ -1,11 +1,32 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "run",
|
||||
"type": "shell",
|
||||
"command": "python3 -m esphome dashboard config/",
|
||||
"problemMatcher": []
|
||||
},
|
||||
{
|
||||
"label": "clang-tidy",
|
||||
"type": "shell",
|
||||
"command": "./script/clang-tidy",
|
||||
"problemMatcher": [
|
||||
{
|
||||
"label": "run",
|
||||
"type": "shell",
|
||||
"command": "python3 -m esphome config dashboard",
|
||||
"problemMatcher": []
|
||||
"owner": "clang-tidy",
|
||||
"fileLocation": "absolute",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(.*):(\\d+):(\\d+):\\s+(error):\\s+(.*) \\[([a-z0-9,\\-]+)\\]\\s*$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"severity": 4,
|
||||
"message": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
42
CODEOWNERS
42
CODEOWNERS
@@ -15,29 +15,46 @@ esphome/components/ac_dimmer/* @glmnet
|
||||
esphome/components/adc/* @esphome/core
|
||||
esphome/components/addressable_light/* @justfalter
|
||||
esphome/components/animation/* @syndlex
|
||||
esphome/components/anova/* @buxtronix
|
||||
esphome/components/api/* @OttoWinter
|
||||
esphome/components/async_tcp/* @OttoWinter
|
||||
esphome/components/atc_mithermometer/* @ahpohl
|
||||
esphome/components/b_parasite/* @rbaron
|
||||
esphome/components/ballu/* @bazuchan
|
||||
esphome/components/bang_bang/* @OttoWinter
|
||||
esphome/components/binary_sensor/* @esphome/core
|
||||
esphome/components/ble_client/* @buxtronix
|
||||
esphome/components/bme680_bsec/* @trvrnrth
|
||||
esphome/components/canbus/* @danielschramm @mvturnho
|
||||
esphome/components/captive_portal/* @OttoWinter
|
||||
esphome/components/climate/* @esphome/core
|
||||
esphome/components/climate_ir/* @glmnet
|
||||
esphome/components/color_temperature/* @jesserockz
|
||||
esphome/components/coolix/* @glmnet
|
||||
esphome/components/cover/* @esphome/core
|
||||
esphome/components/cs5460a/* @balrog-kun
|
||||
esphome/components/ct_clamp/* @jesserockz
|
||||
esphome/components/debug/* @OttoWinter
|
||||
esphome/components/dfplayer/* @glmnet
|
||||
esphome/components/dht/* @OttoWinter
|
||||
esphome/components/ds1307/* @badbadc0ffee
|
||||
esphome/components/dsmr/* @glmnet @zuidwijk
|
||||
esphome/components/esp32_ble/* @jesserockz
|
||||
esphome/components/esp32_ble_server/* @jesserockz
|
||||
esphome/components/esp32_improv/* @jesserockz
|
||||
esphome/components/exposure_notifications/* @OttoWinter
|
||||
esphome/components/ezo/* @ssieb
|
||||
esphome/components/fastled_base/* @OttoWinter
|
||||
esphome/components/fingerprint_grow/* @OnFreund @loongyh
|
||||
esphome/components/globals/* @esphome/core
|
||||
esphome/components/gpio/* @esphome/core
|
||||
esphome/components/gps/* @coogle
|
||||
esphome/components/havells_solar/* @sourabhjaiswal
|
||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||
esphome/components/homeassistant/* @OttoWinter
|
||||
esphome/components/hrxl_maxsonar_wr/* @netmikey
|
||||
esphome/components/i2c/* @esphome/core
|
||||
esphome/components/improv/* @jesserockz
|
||||
esphome/components/inkbird_ibsth1_mini/* @fkirill
|
||||
esphome/components/inkplate6/* @jesserockz
|
||||
esphome/components/integration/* @OttoWinter
|
||||
@@ -58,26 +75,43 @@ esphome/components/mcp2515/* @danielschramm @mvturnho
|
||||
esphome/components/mcp9808/* @k7hpn
|
||||
esphome/components/midea_ac/* @dudanov
|
||||
esphome/components/midea_dongle/* @dudanov
|
||||
esphome/components/mitsubishi/* @RubyBailey
|
||||
esphome/components/network/* @esphome/core
|
||||
esphome/components/nextion/* @senexcrenshaw
|
||||
esphome/components/nextion/binary_sensor/* @senexcrenshaw
|
||||
esphome/components/nextion/sensor/* @senexcrenshaw
|
||||
esphome/components/nextion/switch/* @senexcrenshaw
|
||||
esphome/components/nextion/text_sensor/* @senexcrenshaw
|
||||
esphome/components/nfc/* @jesserockz
|
||||
esphome/components/number/* @esphome/core
|
||||
esphome/components/ota/* @esphome/core
|
||||
esphome/components/output/* @esphome/core
|
||||
esphome/components/pid/* @OttoWinter
|
||||
esphome/components/pmsa003i/* @sjtrny
|
||||
esphome/components/pn532/* @OttoWinter @jesserockz
|
||||
esphome/components/pn532_i2c/* @OttoWinter @jesserockz
|
||||
esphome/components/pn532_spi/* @OttoWinter @jesserockz
|
||||
esphome/components/power_supply/* @esphome/core
|
||||
esphome/components/pulse_meter/* @stevebaxter
|
||||
esphome/components/pvvx_mithermometer/* @pasiz
|
||||
esphome/components/rc522/* @glmnet
|
||||
esphome/components/rc522_i2c/* @glmnet
|
||||
esphome/components/rc522_spi/* @glmnet
|
||||
esphome/components/restart/* @esphome/core
|
||||
esphome/components/rf_bridge/* @jesserockz
|
||||
esphome/components/rgbct/* @jesserockz
|
||||
esphome/components/rtttl/* @glmnet
|
||||
esphome/components/script/* @esphome/core
|
||||
esphome/components/sdm_meter/* @jesserockz @polyfaces
|
||||
esphome/components/sdp3x/* @Azimath
|
||||
esphome/components/selec_meter/* @sourabhjaiswal
|
||||
esphome/components/select/* @esphome/core
|
||||
esphome/components/sensor/* @esphome/core
|
||||
esphome/components/sgp40/* @SenexCrenshaw
|
||||
esphome/components/sht4x/* @sjtrny
|
||||
esphome/components/shutdown/* @esphome/core
|
||||
esphome/components/sim800l/* @glmnet
|
||||
esphome/components/sm2135/* @BoukeHaarsma23
|
||||
esphome/components/spi/* @esphome/core
|
||||
esphome/components/ssd1322_base/* @kbx81
|
||||
esphome/components/ssd1322_spi/* @kbx81
|
||||
@@ -95,12 +129,19 @@ esphome/components/st7789v/* @kbx81
|
||||
esphome/components/substitutions/* @esphome/core
|
||||
esphome/components/sun/* @OttoWinter
|
||||
esphome/components/switch/* @esphome/core
|
||||
esphome/components/t6615/* @tylermenezes
|
||||
esphome/components/tca9548a/* @andreashergert1984
|
||||
esphome/components/tcl112/* @glmnet
|
||||
esphome/components/teleinfo/* @0hax
|
||||
esphome/components/thermostat/* @kbx81
|
||||
esphome/components/time/* @OttoWinter
|
||||
esphome/components/tlc5947/* @rnauber
|
||||
esphome/components/tm1637/* @glmnet
|
||||
esphome/components/tmp102/* @timsavage
|
||||
esphome/components/tmp117/* @Azimath
|
||||
esphome/components/tof10120/* @wstrzalka
|
||||
esphome/components/toshiba/* @kbx81
|
||||
esphome/components/tsl2591/* @wjcarpenter
|
||||
esphome/components/tuya/binary_sensor/* @jesserockz
|
||||
esphome/components/tuya/climate/* @jesserockz
|
||||
esphome/components/tuya/sensor/* @jesserockz
|
||||
@@ -112,3 +153,4 @@ esphome/components/web_server_base/* @OttoWinter
|
||||
esphome/components/whirlpool/* @glmnet
|
||||
esphome/components/xiaomi_lywsd03mmc/* @ahpohl
|
||||
esphome/components/xiaomi_mhoc401/* @vevsvevs
|
||||
esphome/components/xpt2046/* @numo68
|
||||
|
||||
@@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
- Using welcoming and inclusive language
|
||||
- Being respectful of differing viewpoints and experiences
|
||||
- Gracefully accepting constructive criticism
|
||||
- Focusing on what is best for the community
|
||||
- Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
@@ -34,7 +34,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@otto-winter.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at esphome@nabucasa.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ESPHome [](https://travis-ci.org/esphome/esphome) [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
|
||||
# ESPHome [](https://discord.gg/KhAMKrd) [](https://GitHub.com/esphome/esphome/releases/)
|
||||
|
||||
[](https://esphome.io/)
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
ARG BUILD_FROM=esphome/esphome-base-amd64:3.0.0
|
||||
ARG BUILD_FROM=esphome/esphome-base:latest
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
# First install requirements to leverage caching when requirements don't change
|
||||
COPY requirements.txt docker/platformio_install_deps.py platformio.ini /
|
||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
||||
RUN \
|
||||
pip3 install --no-cache-dir -r /requirements.txt \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
||||
&& /platformio_install_deps.py /platformio.ini
|
||||
|
||||
# Then copy esphome and install
|
||||
@@ -27,4 +27,4 @@ WORKDIR /config
|
||||
# in every docker command twice
|
||||
ENTRYPOINT ["esphome"]
|
||||
# When no arguments given, start the dashboard in the workdir
|
||||
CMD ["/config", "dashboard"]
|
||||
CMD ["dashboard", "/config"]
|
||||
|
||||
@@ -1,13 +1 @@
|
||||
FROM esphome/esphome-base-amd64:3.0.0
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
python3-wheel \
|
||||
net-tools \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /workspaces
|
||||
ENV SHELL /bin/bash
|
||||
FROM esphome/esphome-lint:1.1
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
ARG BUILD_FROM
|
||||
ARG BUILD_FROM=esphome/esphome-hassio-base:latest
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
# First install requirements to leverage caching when requirements don't change
|
||||
COPY requirements.txt docker/platformio_install_deps.py platformio.ini /
|
||||
COPY requirements.txt requirements_optional.txt docker/platformio_install_deps.py platformio.ini /
|
||||
RUN \
|
||||
pip3 install --no-cache-dir -r /requirements.txt \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt \
|
||||
&& /platformio_install_deps.py /platformio.ini
|
||||
|
||||
# Copy root filesystem
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
FROM esphome/esphome-lint-base:3.0.0
|
||||
ARG BUILD_FROM=esphome/esphome-lint-base:latest
|
||||
FROM ${BUILD_FROM}
|
||||
|
||||
COPY requirements.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
|
||||
COPY requirements.txt requirements_optional.txt requirements_test.txt docker/platformio_install_deps.py platformio.ini /
|
||||
RUN \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_test.txt \
|
||||
pip3 install --no-cache-dir -r /requirements.txt -r /requirements_optional.txt -r /requirements_test.txt \
|
||||
&& /platformio_install_deps.py /platformio.ini
|
||||
|
||||
VOLUME ["/esphome"]
|
||||
|
||||
177
docker/build.py
Executable file
177
docker/build.py
Executable file
@@ -0,0 +1,177 @@
|
||||
#!/usr/bin/env python3
|
||||
from dataclasses import dataclass
|
||||
import subprocess
|
||||
import argparse
|
||||
import platform
|
||||
import shlex
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
CHANNEL_DEV = 'dev'
|
||||
CHANNEL_BETA = 'beta'
|
||||
CHANNEL_RELEASE = 'release'
|
||||
CHANNELS = [CHANNEL_DEV, CHANNEL_BETA, CHANNEL_RELEASE]
|
||||
|
||||
ARCH_AMD64 = 'amd64'
|
||||
ARCH_ARMV7 = 'armv7'
|
||||
ARCH_AARCH64 = 'aarch64'
|
||||
ARCHS = [ARCH_AMD64, ARCH_ARMV7, ARCH_AARCH64]
|
||||
|
||||
TYPE_DOCKER = 'docker'
|
||||
TYPE_HA_ADDON = 'ha-addon'
|
||||
TYPE_LINT = 'lint'
|
||||
TYPES = [TYPE_DOCKER, TYPE_HA_ADDON, TYPE_LINT]
|
||||
|
||||
|
||||
BASE_VERSION = "3.6.0"
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--tag", type=str, required=True, help="The main docker tag to push to. If a version number also adds latest and/or beta tag")
|
||||
parser.add_argument("--arch", choices=ARCHS, required=False, help="The architecture to build for")
|
||||
parser.add_argument("--build-type", choices=TYPES, required=True, help="The type of build to run")
|
||||
parser.add_argument("--dry-run", action="store_true", help="Don't run any commands, just print them")
|
||||
subparsers = parser.add_subparsers(help="Action to perform", dest="command", required=True)
|
||||
build_parser = subparsers.add_parser("build", help="Build the image")
|
||||
push_parser = subparsers.add_parser("push", help="Tag the already built image and push it to docker hub")
|
||||
manifest_parser = subparsers.add_parser("manifest", help="Create a manifest from already pushed images")
|
||||
|
||||
|
||||
|
||||
# only lists some possibilities, doesn't have to be perfect
|
||||
# https://stackoverflow.com/a/45125525
|
||||
UNAME_TO_ARCH = {
|
||||
"x86_64": ARCH_AMD64,
|
||||
"aarch64": ARCH_AARCH64,
|
||||
"aarch64_be": ARCH_AARCH64,
|
||||
"arm": ARCH_ARMV7,
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DockerParams:
|
||||
build_from: str
|
||||
build_to: str
|
||||
manifest_to: str
|
||||
dockerfile: str
|
||||
|
||||
@classmethod
|
||||
def for_type_arch(cls, build_type, arch):
|
||||
prefix = {
|
||||
TYPE_DOCKER: "esphome/esphome",
|
||||
TYPE_HA_ADDON: "esphome/esphome-hassio",
|
||||
TYPE_LINT: "esphome/esphome-lint"
|
||||
}[build_type]
|
||||
build_from = f"ghcr.io/{prefix}-base-{arch}:{BASE_VERSION}"
|
||||
build_to = f"{prefix}-{arch}"
|
||||
dockerfile = {
|
||||
TYPE_DOCKER: "docker/Dockerfile",
|
||||
TYPE_HA_ADDON: "docker/Dockerfile.hassio",
|
||||
TYPE_LINT: "docker/Dockerfile.lint",
|
||||
}[build_type]
|
||||
return cls(
|
||||
build_from=build_from,
|
||||
build_to=build_to,
|
||||
manifest_to=prefix,
|
||||
dockerfile=dockerfile
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
args = parser.parse_args()
|
||||
|
||||
def run_command(*cmd, ignore_error: bool = False):
|
||||
print(f"$ {shlex.join(list(cmd))}")
|
||||
if not args.dry_run:
|
||||
rc = subprocess.call(list(cmd))
|
||||
if rc != 0 and not ignore_error:
|
||||
print("Command failed")
|
||||
sys.exit(1)
|
||||
|
||||
# detect channel from tag
|
||||
match = re.match(r'^\d+\.\d+(?:\.\d+)?(b\d+)?$', args.tag)
|
||||
if match is None:
|
||||
channel = CHANNEL_DEV
|
||||
elif match.group(1) is None:
|
||||
channel = CHANNEL_RELEASE
|
||||
else:
|
||||
channel = CHANNEL_BETA
|
||||
|
||||
tags_to_push = [args.tag]
|
||||
if channel == CHANNEL_DEV:
|
||||
tags_to_push.append("dev")
|
||||
elif channel == CHANNEL_BETA:
|
||||
tags_to_push.append("beta")
|
||||
elif channel == CHANNEL_RELEASE:
|
||||
# Additionally push to beta
|
||||
tags_to_push.append("beta")
|
||||
tags_to_push.append("latest")
|
||||
|
||||
if args.command == "build":
|
||||
# 1. pull cache image
|
||||
params = DockerParams.for_type_arch(args.build_type, args.arch)
|
||||
cache_tag = {
|
||||
CHANNEL_DEV: "dev",
|
||||
CHANNEL_BETA: "beta",
|
||||
CHANNEL_RELEASE: "latest",
|
||||
}[channel]
|
||||
cache_img = f"ghcr.io/{params.build_to}:{cache_tag}"
|
||||
run_command("docker", "pull", cache_img, ignore_error=True)
|
||||
|
||||
# 2. register QEMU binfmt (if not host arch)
|
||||
is_native = UNAME_TO_ARCH.get(platform.machine()) == args.arch
|
||||
if not is_native:
|
||||
run_command(
|
||||
"docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static:5.2.0-2",
|
||||
"--reset", "-p", "yes"
|
||||
)
|
||||
|
||||
# 3. build
|
||||
run_command(
|
||||
"docker", "build",
|
||||
"--build-arg", f"BUILD_FROM={params.build_from}",
|
||||
"--build-arg", f"BUILD_VERSION={args.tag}",
|
||||
"--tag", f"{params.build_to}:{args.tag}",
|
||||
"--cache-from", cache_img,
|
||||
"--file", params.dockerfile,
|
||||
"."
|
||||
)
|
||||
elif args.command == "push":
|
||||
params = DockerParams.for_type_arch(args.build_type, args.arch)
|
||||
imgs = [f"{params.build_to}:{tag}" for tag in tags_to_push]
|
||||
imgs += [f"ghcr.io/{params.build_to}:{tag}" for tag in tags_to_push]
|
||||
src = imgs[0]
|
||||
# 1. tag images
|
||||
for img in imgs[1:]:
|
||||
run_command(
|
||||
"docker", "tag", src, img
|
||||
)
|
||||
# 2. push images
|
||||
for img in imgs:
|
||||
run_command(
|
||||
"docker", "push", img
|
||||
)
|
||||
elif args.command == "manifest":
|
||||
manifest = DockerParams.for_type_arch(args.build_type, ARCH_AMD64).manifest_to
|
||||
|
||||
targets = [f"{manifest}:{tag}" for tag in tags_to_push]
|
||||
targets += [f"ghcr.io/{manifest}:{tag}" for tag in tags_to_push]
|
||||
# 1. Create manifests
|
||||
for target in targets:
|
||||
cmd = ["docker", "manifest", "create", target]
|
||||
for arch in ARCHS:
|
||||
src = f"{DockerParams.for_type_arch(args.build_type, arch).build_to}:{args.tag}"
|
||||
if target.startswith("ghcr.io"):
|
||||
src = f"ghcr.io/{src}"
|
||||
cmd.append(src)
|
||||
run_command(*cmd)
|
||||
# 2. Push manifests
|
||||
for target in targets:
|
||||
run_command(
|
||||
"docker", "manifest", "push", target
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# ==============================================================================
|
||||
# Community Hass.io Add-ons: ESPHome
|
||||
# This files installs the user ESPHome version if specified
|
||||
# ==============================================================================
|
||||
|
||||
declare esphome_version
|
||||
|
||||
if bashio::config.has_value 'esphome_version'; then
|
||||
esphome_version=$(bashio::config 'esphome_version')
|
||||
if [[ $esphome_version == *":"* ]]; then
|
||||
IFS=':' read -r -a array <<< "$esphome_version"
|
||||
username=${array[0]}
|
||||
ref=${array[1]}
|
||||
else
|
||||
username="esphome"
|
||||
ref=$esphome_version
|
||||
fi
|
||||
full_url="https://github.com/${username}/esphome/archive/${ref}.zip"
|
||||
bashio::log.info "Installing esphome version '${esphome_version}' (${full_url})..."
|
||||
pip3 install -U --no-cache-dir "${full_url}" \
|
||||
|| bashio::exit.nok "Failed installing esphome pinned version."
|
||||
fi
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/with-contenv bashio
|
||||
# ==============================================================================
|
||||
# Community Hass.io Add-ons: ESPHome
|
||||
# This files migrates the esphome config directory from the old path
|
||||
# ==============================================================================
|
||||
|
||||
if [[ ! -d /config/esphome && -d /config/esphomeyaml ]]; then
|
||||
echo "Moving config directory from /config/esphomeyaml to /config/esphome"
|
||||
mv /config/esphomeyaml /config/esphome
|
||||
mv /config/esphome/.esphomeyaml /config/esphome/.esphome
|
||||
fi
|
||||
@@ -23,4 +23,4 @@ if bashio::config.has_value 'relative_url'; then
|
||||
fi
|
||||
|
||||
bashio::log.info "Starting ESPHome dashboard..."
|
||||
exec esphome /config/esphome dashboard --socket /var/run/esphome.sock --hassio
|
||||
exec esphome dashboard /config/esphome --socket /var/run/esphome.sock --hassio
|
||||
|
||||
@@ -18,7 +18,7 @@ from esphome.const import (
|
||||
CONF_ESPHOME,
|
||||
CONF_PLATFORMIO_OPTIONS,
|
||||
)
|
||||
from esphome.core import CORE, EsphomeError, coroutine, coroutine_with_priority
|
||||
from esphome.core import CORE, EsphomeError, coroutine
|
||||
from esphome.helpers import indent
|
||||
from esphome.util import (
|
||||
run_external_command,
|
||||
@@ -127,15 +127,16 @@ def wrap_to_code(name, comp):
|
||||
coro = coroutine(comp.to_code)
|
||||
|
||||
@functools.wraps(comp.to_code)
|
||||
@coroutine_with_priority(coro.priority)
|
||||
def wrapped(conf):
|
||||
async def wrapped(conf):
|
||||
cg.add(cg.LineComment(f"{name}:"))
|
||||
if comp.config_schema is not None:
|
||||
conf_str = yaml_util.dump(conf)
|
||||
conf_str = conf_str.replace("//", "")
|
||||
cg.add(cg.LineComment(indent(conf_str)))
|
||||
yield coro(conf)
|
||||
await coro(conf)
|
||||
|
||||
if hasattr(coro, "priority"):
|
||||
wrapped.priority = coro.priority
|
||||
return wrapped
|
||||
|
||||
|
||||
@@ -267,7 +268,7 @@ def clean_mqtt(config, args):
|
||||
def command_wizard(args):
|
||||
from esphome import wizard
|
||||
|
||||
return wizard.wizard(args.configuration[0])
|
||||
return wizard.wizard(args.configuration)
|
||||
|
||||
|
||||
def command_config(args, config):
|
||||
@@ -283,7 +284,7 @@ def command_vscode(args):
|
||||
|
||||
logging.disable(logging.INFO)
|
||||
logging.disable(logging.WARNING)
|
||||
CORE.config_path = args.configuration[0]
|
||||
CORE.config_path = args.configuration
|
||||
vscode.read_config(args)
|
||||
|
||||
|
||||
@@ -303,7 +304,7 @@ def command_compile(args, config):
|
||||
|
||||
def command_upload(args, config):
|
||||
port = choose_upload_log_host(
|
||||
default=args.upload_port,
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
show_ota=True,
|
||||
show_mqtt=False,
|
||||
@@ -318,7 +319,7 @@ def command_upload(args, config):
|
||||
|
||||
def command_logs(args, config):
|
||||
port = choose_upload_log_host(
|
||||
default=args.serial_port,
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
show_ota=False,
|
||||
show_mqtt=True,
|
||||
@@ -336,7 +337,7 @@ def command_run(args, config):
|
||||
return exit_code
|
||||
_LOGGER.info("Successfully compiled program.")
|
||||
port = choose_upload_log_host(
|
||||
default=args.upload_port,
|
||||
default=args.device,
|
||||
check_default=None,
|
||||
show_ota=True,
|
||||
show_mqtt=False,
|
||||
@@ -349,7 +350,7 @@ def command_run(args, config):
|
||||
if args.no_logs:
|
||||
return 0
|
||||
port = choose_upload_log_host(
|
||||
default=args.upload_port,
|
||||
default=args.device,
|
||||
check_default=port,
|
||||
show_ota=False,
|
||||
show_mqtt=True,
|
||||
@@ -407,7 +408,7 @@ def command_update_all(args):
|
||||
print("-" * twidth)
|
||||
print()
|
||||
rc = run_external_process(
|
||||
"esphome", "--dashboard", f, "run", "--no-logs", "--upload-port", "OTA"
|
||||
"esphome", "--dashboard", "run", f, "--no-logs", "--device", "OTA"
|
||||
)
|
||||
if rc == 0:
|
||||
print_bar("[{}] {}".format(color(Fore.BOLD_GREEN, "SUCCESS"), f))
|
||||
@@ -452,15 +453,17 @@ POST_CONFIG_ACTIONS = {
|
||||
|
||||
|
||||
def parse_args(argv):
|
||||
parser = argparse.ArgumentParser(description=f"ESPHome v{const.__version__}")
|
||||
parser.add_argument(
|
||||
"-v", "--verbose", help="Enable verbose esphome logs.", action="store_true"
|
||||
options_parser = argparse.ArgumentParser(add_help=False)
|
||||
options_parser.add_argument(
|
||||
"-v", "--verbose", help="Enable verbose ESPHome logs.", action="store_true"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-q", "--quiet", help="Disable all esphome logs.", action="store_true"
|
||||
options_parser.add_argument(
|
||||
"-q", "--quiet", help="Disable all ESPHome logs.", action="store_true"
|
||||
)
|
||||
parser.add_argument("--dashboard", help=argparse.SUPPRESS, action="store_true")
|
||||
parser.add_argument(
|
||||
options_parser.add_argument(
|
||||
"--dashboard", help=argparse.SUPPRESS, action="store_true"
|
||||
)
|
||||
options_parser.add_argument(
|
||||
"-s",
|
||||
"--substitution",
|
||||
nargs=2,
|
||||
@@ -468,17 +471,101 @@ def parse_args(argv):
|
||||
help="Add a substitution",
|
||||
metavar=("key", "value"),
|
||||
)
|
||||
parser.add_argument(
|
||||
"configuration", help="Your YAML configuration file.", nargs="*"
|
||||
|
||||
# Keep backward compatibility with the old command line format of
|
||||
# esphome <config> <command>.
|
||||
#
|
||||
# Unfortunately this can't be done by adding another configuration argument to the
|
||||
# main config parser, as argparse is greedy when parsing arguments, so in regular
|
||||
# usage it'll eat the command as the configuration argument and error out out
|
||||
# because it can't parse the configuration as a command.
|
||||
#
|
||||
# Instead, construct an ad-hoc parser for the old format that doesn't actually
|
||||
# process the arguments, but parses them enough to let us figure out if the old
|
||||
# format is used. In that case, swap the command and configuration in the arguments
|
||||
# and continue on with the normal parser (after raising a deprecation warning).
|
||||
#
|
||||
# Disable argparse's built-in help option and add it manually to prevent this
|
||||
# parser from printing the help messagefor the old format when invoked with -h.
|
||||
compat_parser = argparse.ArgumentParser(parents=[options_parser], add_help=False)
|
||||
compat_parser.add_argument("-h", "--help")
|
||||
compat_parser.add_argument("configuration", nargs="*")
|
||||
compat_parser.add_argument(
|
||||
"command",
|
||||
choices=[
|
||||
"config",
|
||||
"compile",
|
||||
"upload",
|
||||
"logs",
|
||||
"run",
|
||||
"clean-mqtt",
|
||||
"wizard",
|
||||
"mqtt-fingerprint",
|
||||
"version",
|
||||
"clean",
|
||||
"dashboard",
|
||||
"vscode",
|
||||
"update-all",
|
||||
],
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(help="Commands", dest="command")
|
||||
# on Python 3.9+ we can simply set exit_on_error=False in the constructor
|
||||
def _raise(x):
|
||||
raise argparse.ArgumentError(None, x)
|
||||
|
||||
compat_parser.error = _raise
|
||||
|
||||
deprecated_argv_suggestion = None
|
||||
|
||||
if ["dashboard", "config"] == argv[1:3] or ["version"] == argv[1:2]:
|
||||
# this is most likely meant in new-style arg format. do not try compat parsing
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
result, unparsed = compat_parser.parse_known_args(argv[1:])
|
||||
last_option = len(argv) - len(unparsed) - 1 - len(result.configuration)
|
||||
unparsed = [
|
||||
"--device" if arg in ("--upload-port", "--serial-port") else arg
|
||||
for arg in unparsed
|
||||
]
|
||||
argv = (
|
||||
argv[0:last_option] + [result.command] + result.configuration + unparsed
|
||||
)
|
||||
deprecated_argv_suggestion = argv
|
||||
except argparse.ArgumentError:
|
||||
# This is not an old-style command line, so we don't have to do anything.
|
||||
pass
|
||||
|
||||
# And continue on with regular parsing
|
||||
parser = argparse.ArgumentParser(
|
||||
description=f"ESPHome v{const.__version__}", parents=[options_parser]
|
||||
)
|
||||
parser.set_defaults(deprecated_argv_suggestion=deprecated_argv_suggestion)
|
||||
|
||||
mqtt_options = argparse.ArgumentParser(add_help=False)
|
||||
mqtt_options.add_argument("--topic", help="Manually set the MQTT topic.")
|
||||
mqtt_options.add_argument("--username", help="Manually set the MQTT username.")
|
||||
mqtt_options.add_argument("--password", help="Manually set the MQTT password.")
|
||||
mqtt_options.add_argument("--client-id", help="Manually set the MQTT client id.")
|
||||
|
||||
subparsers = parser.add_subparsers(
|
||||
help="Command to run:", dest="command", metavar="command"
|
||||
)
|
||||
subparsers.required = True
|
||||
subparsers.add_parser("config", help="Validate the configuration and spit it out.")
|
||||
|
||||
parser_config = subparsers.add_parser(
|
||||
"config", help="Validate the configuration and spit it out."
|
||||
)
|
||||
parser_config.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
|
||||
parser_compile = subparsers.add_parser(
|
||||
"compile", help="Read the configuration and compile a program."
|
||||
)
|
||||
parser_compile.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
parser_compile.add_argument(
|
||||
"--only-generate",
|
||||
help="Only generate source code, do not compile.",
|
||||
@@ -486,106 +573,124 @@ def parse_args(argv):
|
||||
)
|
||||
|
||||
parser_upload = subparsers.add_parser(
|
||||
"upload", help="Validate the configuration " "and upload the latest binary."
|
||||
"upload", help="Validate the configuration and upload the latest binary."
|
||||
)
|
||||
parser_upload.add_argument(
|
||||
"--upload-port",
|
||||
help="Manually specify the upload port to use. "
|
||||
"For example /dev/cu.SLAB_USBtoUART.",
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
parser_upload.add_argument(
|
||||
"--device",
|
||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||
)
|
||||
|
||||
parser_logs = subparsers.add_parser(
|
||||
"logs", help="Validate the configuration " "and show all MQTT logs."
|
||||
"logs",
|
||||
help="Validate the configuration and show all logs.",
|
||||
parents=[mqtt_options],
|
||||
)
|
||||
parser_logs.add_argument("--topic", help="Manually set the topic to subscribe to.")
|
||||
parser_logs.add_argument("--username", help="Manually set the username.")
|
||||
parser_logs.add_argument("--password", help="Manually set the password.")
|
||||
parser_logs.add_argument("--client-id", help="Manually set the client id.")
|
||||
parser_logs.add_argument(
|
||||
"--serial-port",
|
||||
help="Manually specify a serial port to use"
|
||||
"For example /dev/cu.SLAB_USBtoUART.",
|
||||
"configuration", help="Your YAML configuration file.", nargs=1
|
||||
)
|
||||
parser_logs.add_argument(
|
||||
"--device",
|
||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||
)
|
||||
|
||||
parser_run = subparsers.add_parser(
|
||||
"run",
|
||||
help="Validate the configuration, create a binary, "
|
||||
"upload it, and start MQTT logs.",
|
||||
help="Validate the configuration, create a binary, upload it, and start logs.",
|
||||
parents=[mqtt_options],
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--upload-port",
|
||||
help="Manually specify the upload port/ip to use. "
|
||||
"For example /dev/cu.SLAB_USBtoUART.",
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--no-logs", help="Disable starting MQTT logs.", action="store_true"
|
||||
"--device",
|
||||
help="Manually specify the serial port/address to use, for example /dev/ttyUSB0.",
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--topic", help="Manually set the topic to subscribe to for logs."
|
||||
"--no-logs", help="Disable starting logs.", action="store_true"
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--username", help="Manually set the MQTT username for logs."
|
||||
)
|
||||
parser_run.add_argument(
|
||||
"--password", help="Manually set the MQTT password for logs."
|
||||
)
|
||||
parser_run.add_argument("--client-id", help="Manually set the client id for logs.")
|
||||
|
||||
parser_clean = subparsers.add_parser(
|
||||
"clean-mqtt", help="Helper to clear an MQTT topic from " "retain messages."
|
||||
"clean-mqtt",
|
||||
help="Helper to clear retained messages from an MQTT topic.",
|
||||
parents=[mqtt_options],
|
||||
)
|
||||
parser_clean.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
parser_clean.add_argument("--topic", help="Manually set the topic to subscribe to.")
|
||||
parser_clean.add_argument("--username", help="Manually set the username.")
|
||||
parser_clean.add_argument("--password", help="Manually set the password.")
|
||||
parser_clean.add_argument("--client-id", help="Manually set the client id.")
|
||||
|
||||
subparsers.add_parser(
|
||||
parser_wizard = subparsers.add_parser(
|
||||
"wizard",
|
||||
help="A helpful setup wizard that will guide "
|
||||
"you through setting up esphome.",
|
||||
help="A helpful setup wizard that will guide you through setting up ESPHome.",
|
||||
)
|
||||
parser_wizard.add_argument(
|
||||
"configuration",
|
||||
help="Your YAML configuration file.",
|
||||
)
|
||||
|
||||
subparsers.add_parser(
|
||||
parser_fingerprint = subparsers.add_parser(
|
||||
"mqtt-fingerprint", help="Get the SSL fingerprint from a MQTT broker."
|
||||
)
|
||||
parser_fingerprint.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
|
||||
subparsers.add_parser("version", help="Print the esphome version and exit.")
|
||||
subparsers.add_parser("version", help="Print the ESPHome version and exit.")
|
||||
|
||||
subparsers.add_parser("clean", help="Delete all temporary build files.")
|
||||
parser_clean = subparsers.add_parser(
|
||||
"clean", help="Delete all temporary build files."
|
||||
)
|
||||
parser_clean.add_argument(
|
||||
"configuration", help="Your YAML configuration file(s).", nargs="+"
|
||||
)
|
||||
|
||||
dashboard = subparsers.add_parser(
|
||||
parser_dashboard = subparsers.add_parser(
|
||||
"dashboard", help="Create a simple web server for a dashboard."
|
||||
)
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"configuration",
|
||||
help="Your YAML configuration file directory.",
|
||||
)
|
||||
parser_dashboard.add_argument(
|
||||
"--port",
|
||||
help="The HTTP port to open connections on. Defaults to 6052.",
|
||||
type=int,
|
||||
default=6052,
|
||||
)
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"--username",
|
||||
help="The optional username to require " "for authentication.",
|
||||
help="The optional username to require for authentication.",
|
||||
type=str,
|
||||
default="",
|
||||
)
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"--password",
|
||||
help="The optional password to require " "for authentication.",
|
||||
help="The optional password to require for authentication.",
|
||||
type=str,
|
||||
default="",
|
||||
)
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"--open-ui", help="Open the dashboard UI in a browser.", action="store_true"
|
||||
)
|
||||
dashboard.add_argument("--hassio", help=argparse.SUPPRESS, action="store_true")
|
||||
dashboard.add_argument(
|
||||
parser_dashboard.add_argument(
|
||||
"--hassio", help=argparse.SUPPRESS, action="store_true"
|
||||
)
|
||||
parser_dashboard.add_argument(
|
||||
"--socket", help="Make the dashboard serve under a unix socket", type=str
|
||||
)
|
||||
|
||||
vscode = subparsers.add_parser("vscode", help=argparse.SUPPRESS)
|
||||
vscode.add_argument("--ace", action="store_true")
|
||||
parser_vscode = subparsers.add_parser("vscode")
|
||||
parser_vscode.add_argument(
|
||||
"configuration", help="Your YAML configuration file.", nargs=1
|
||||
)
|
||||
parser_vscode.add_argument("--ace", action="store_true")
|
||||
|
||||
subparsers.add_parser("update-all", help=argparse.SUPPRESS)
|
||||
parser_update = subparsers.add_parser("update-all")
|
||||
parser_update.add_argument(
|
||||
"configuration", help="Your YAML configuration file directory.", nargs=1
|
||||
)
|
||||
|
||||
return parser.parse_args(argv[1:])
|
||||
|
||||
@@ -595,14 +700,18 @@ def run_esphome(argv):
|
||||
CORE.dashboard = args.dashboard
|
||||
|
||||
setup_log(args.verbose, args.quiet)
|
||||
if args.command != "version" and not args.configuration:
|
||||
_LOGGER.error("Missing configuration parameter, see esphome --help.")
|
||||
return 1
|
||||
if args.deprecated_argv_suggestion is not None and args.command != "vscode":
|
||||
_LOGGER.warning(
|
||||
"Calling ESPHome with the configuration before the command is deprecated "
|
||||
"and will be removed in the future. "
|
||||
)
|
||||
_LOGGER.warning("Please instead use:")
|
||||
_LOGGER.warning(" esphome %s", " ".join(args.deprecated_argv_suggestion[1:]))
|
||||
|
||||
if sys.version_info < (3, 6, 0):
|
||||
if sys.version_info < (3, 7, 0):
|
||||
_LOGGER.error(
|
||||
"You're running ESPHome with Python <3.6. ESPHome is no longer compatible "
|
||||
"with this Python version. Please reinstall ESPHome with Python 3.6+"
|
||||
"You're running ESPHome with Python <3.7. ESPHome is no longer compatible "
|
||||
"with this Python version. Please reinstall ESPHome with Python 3.7+"
|
||||
)
|
||||
return 1
|
||||
|
||||
@@ -610,7 +719,7 @@ def run_esphome(argv):
|
||||
try:
|
||||
return PRE_CONFIG_ACTIONS[args.command](args)
|
||||
except EsphomeError as e:
|
||||
_LOGGER.error(e)
|
||||
_LOGGER.error(e, exc_info=args.verbose)
|
||||
return 1
|
||||
|
||||
for conf_path in args.configuration:
|
||||
@@ -628,7 +737,7 @@ def run_esphome(argv):
|
||||
try:
|
||||
rc = POST_CONFIG_ACTIONS[args.command](args, config)
|
||||
except EsphomeError as e:
|
||||
_LOGGER.error(e)
|
||||
_LOGGER.error(e, exc_info=args.verbose)
|
||||
return 1
|
||||
if rc != 0:
|
||||
return rc
|
||||
|
||||
@@ -192,6 +192,11 @@ class APIClient(threading.Thread):
|
||||
self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self._socket.settimeout(10.0)
|
||||
self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
import ssl
|
||||
|
||||
context = ssl.SSLContext()
|
||||
self._socket = context.wrap_socket(self._socket)
|
||||
|
||||
try:
|
||||
self._socket.connect((ip, self._port))
|
||||
except OSError as err:
|
||||
|
||||
@@ -10,7 +10,6 @@ from esphome.const import (
|
||||
CONF_TYPE_ID,
|
||||
CONF_TIME,
|
||||
)
|
||||
from esphome.core import coroutine
|
||||
from esphome.jsonschema import jschema_extractor
|
||||
from esphome.util import Registry
|
||||
|
||||
@@ -142,27 +141,27 @@ NotCondition = cg.esphome_ns.class_("NotCondition", Condition)
|
||||
|
||||
|
||||
@register_condition("and", AndCondition, validate_condition_list)
|
||||
def and_condition_to_code(config, condition_id, template_arg, args):
|
||||
conditions = yield build_condition_list(config, template_arg, args)
|
||||
yield cg.new_Pvariable(condition_id, template_arg, conditions)
|
||||
async def and_condition_to_code(config, condition_id, template_arg, args):
|
||||
conditions = await build_condition_list(config, template_arg, args)
|
||||
return cg.new_Pvariable(condition_id, template_arg, conditions)
|
||||
|
||||
|
||||
@register_condition("or", OrCondition, validate_condition_list)
|
||||
def or_condition_to_code(config, condition_id, template_arg, args):
|
||||
conditions = yield build_condition_list(config, template_arg, args)
|
||||
yield cg.new_Pvariable(condition_id, template_arg, conditions)
|
||||
async def or_condition_to_code(config, condition_id, template_arg, args):
|
||||
conditions = await build_condition_list(config, template_arg, args)
|
||||
return cg.new_Pvariable(condition_id, template_arg, conditions)
|
||||
|
||||
|
||||
@register_condition("not", NotCondition, validate_potentially_and_condition)
|
||||
def not_condition_to_code(config, condition_id, template_arg, args):
|
||||
condition = yield build_condition(config, template_arg, args)
|
||||
yield cg.new_Pvariable(condition_id, template_arg, condition)
|
||||
async def not_condition_to_code(config, condition_id, template_arg, args):
|
||||
condition = await build_condition(config, template_arg, args)
|
||||
return cg.new_Pvariable(condition_id, template_arg, condition)
|
||||
|
||||
|
||||
@register_condition("lambda", LambdaCondition, cv.lambda_)
|
||||
def lambda_condition_to_code(config, condition_id, template_arg, args):
|
||||
lambda_ = yield cg.process_lambda(config, args, return_type=bool)
|
||||
yield cg.new_Pvariable(condition_id, template_arg, lambda_)
|
||||
@register_condition("lambda", LambdaCondition, cv.returning_lambda)
|
||||
async def lambda_condition_to_code(config, condition_id, template_arg, args):
|
||||
lambda_ = await cg.process_lambda(config, args, return_type=bool)
|
||||
return cg.new_Pvariable(condition_id, template_arg, lambda_)
|
||||
|
||||
|
||||
@register_condition(
|
||||
@@ -177,26 +176,26 @@ def lambda_condition_to_code(config, condition_id, template_arg, args):
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
)
|
||||
def for_condition_to_code(config, condition_id, template_arg, args):
|
||||
condition = yield build_condition(
|
||||
async def for_condition_to_code(config, condition_id, template_arg, args):
|
||||
condition = await build_condition(
|
||||
config[CONF_CONDITION], cg.TemplateArguments(), []
|
||||
)
|
||||
var = cg.new_Pvariable(condition_id, template_arg, condition)
|
||||
yield cg.register_component(var, config)
|
||||
templ = yield cg.templatable(config[CONF_TIME], args, cg.uint32)
|
||||
await cg.register_component(var, config)
|
||||
templ = await cg.templatable(config[CONF_TIME], args, cg.uint32)
|
||||
cg.add(var.set_time(templ))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
@register_action(
|
||||
"delay", DelayAction, cv.templatable(cv.positive_time_period_milliseconds)
|
||||
)
|
||||
def delay_action_to_code(config, action_id, template_arg, args):
|
||||
async def delay_action_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
yield cg.register_component(var, {})
|
||||
template_ = yield cg.templatable(config, args, cg.uint32)
|
||||
await cg.register_component(var, {})
|
||||
template_ = await cg.templatable(config, args, cg.uint32)
|
||||
cg.add(var.set_delay(template_))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
@register_action(
|
||||
@@ -211,16 +210,16 @@ def delay_action_to_code(config, action_id, template_arg, args):
|
||||
cv.has_at_least_one_key(CONF_THEN, CONF_ELSE),
|
||||
),
|
||||
)
|
||||
def if_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
async def if_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
||||
if CONF_THEN in config:
|
||||
actions = yield build_action_list(config[CONF_THEN], template_arg, args)
|
||||
actions = await build_action_list(config[CONF_THEN], template_arg, args)
|
||||
cg.add(var.add_then(actions))
|
||||
if CONF_ELSE in config:
|
||||
actions = yield build_action_list(config[CONF_ELSE], template_arg, args)
|
||||
actions = await build_action_list(config[CONF_ELSE], template_arg, args)
|
||||
cg.add(var.add_else(actions))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
@register_action(
|
||||
@@ -233,12 +232,12 @@ def if_action_to_code(config, action_id, template_arg, args):
|
||||
}
|
||||
),
|
||||
)
|
||||
def while_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
async def while_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
||||
actions = yield build_action_list(config[CONF_THEN], template_arg, args)
|
||||
actions = await build_action_list(config[CONF_THEN], template_arg, args)
|
||||
cg.add(var.add_then(actions))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
def validate_wait_until(value):
|
||||
@@ -253,17 +252,17 @@ def validate_wait_until(value):
|
||||
|
||||
|
||||
@register_action("wait_until", WaitUntilAction, validate_wait_until)
|
||||
def wait_until_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = yield build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
async def wait_until_action_to_code(config, action_id, template_arg, args):
|
||||
conditions = await build_condition(config[CONF_CONDITION], template_arg, args)
|
||||
var = cg.new_Pvariable(action_id, template_arg, conditions)
|
||||
yield cg.register_component(var, {})
|
||||
yield var
|
||||
await cg.register_component(var, {})
|
||||
return var
|
||||
|
||||
|
||||
@register_action("lambda", LambdaAction, cv.lambda_)
|
||||
def lambda_action_to_code(config, action_id, template_arg, args):
|
||||
lambda_ = yield cg.process_lambda(config, args, return_type=cg.void)
|
||||
yield cg.new_Pvariable(action_id, template_arg, lambda_)
|
||||
async def lambda_action_to_code(config, action_id, template_arg, args):
|
||||
lambda_ = await cg.process_lambda(config, args, return_type=cg.void)
|
||||
return cg.new_Pvariable(action_id, template_arg, lambda_)
|
||||
|
||||
|
||||
@register_action(
|
||||
@@ -275,54 +274,51 @@ def lambda_action_to_code(config, action_id, template_arg, args):
|
||||
}
|
||||
),
|
||||
)
|
||||
def component_update_action_to_code(config, action_id, template_arg, args):
|
||||
comp = yield cg.get_variable(config[CONF_ID])
|
||||
yield cg.new_Pvariable(action_id, template_arg, comp)
|
||||
async def component_update_action_to_code(config, action_id, template_arg, args):
|
||||
comp = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, comp)
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_action(full_config, template_arg, args):
|
||||
async def build_action(full_config, template_arg, args):
|
||||
registry_entry, config = cg.extract_registry_entry_config(
|
||||
ACTION_REGISTRY, full_config
|
||||
)
|
||||
action_id = full_config[CONF_TYPE_ID]
|
||||
builder = registry_entry.coroutine_fun
|
||||
yield builder(config, action_id, template_arg, args)
|
||||
ret = await builder(config, action_id, template_arg, args)
|
||||
return ret
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_action_list(config, templ, arg_type):
|
||||
async def build_action_list(config, templ, arg_type):
|
||||
actions = []
|
||||
for conf in config:
|
||||
action = yield build_action(conf, templ, arg_type)
|
||||
action = await build_action(conf, templ, arg_type)
|
||||
actions.append(action)
|
||||
yield actions
|
||||
return actions
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_condition(full_config, template_arg, args):
|
||||
async def build_condition(full_config, template_arg, args):
|
||||
registry_entry, config = cg.extract_registry_entry_config(
|
||||
CONDITION_REGISTRY, full_config
|
||||
)
|
||||
action_id = full_config[CONF_TYPE_ID]
|
||||
builder = registry_entry.coroutine_fun
|
||||
yield builder(config, action_id, template_arg, args)
|
||||
ret = await builder(config, action_id, template_arg, args)
|
||||
return ret
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_condition_list(config, templ, args):
|
||||
async def build_condition_list(config, templ, args):
|
||||
conditions = []
|
||||
for conf in config:
|
||||
condition = yield build_condition(conf, templ, args)
|
||||
condition = await build_condition(conf, templ, args)
|
||||
conditions.append(condition)
|
||||
yield conditions
|
||||
return conditions
|
||||
|
||||
|
||||
@coroutine
|
||||
def build_automation(trigger, args, config):
|
||||
async def build_automation(trigger, args, config):
|
||||
arg_types = [arg[0] for arg in args]
|
||||
templ = cg.TemplateArguments(*arg_types)
|
||||
obj = cg.new_Pvariable(config[CONF_AUTOMATION_ID], templ, trigger)
|
||||
actions = yield build_action_list(config[CONF_THEN], templ, args)
|
||||
actions = await build_action_list(config[CONF_THEN], templ, args)
|
||||
cg.add(obj.add_actions(actions))
|
||||
yield obj
|
||||
return obj
|
||||
|
||||
875
esphome/boards.py
Normal file
875
esphome/boards.py
Normal file
@@ -0,0 +1,875 @@
|
||||
ESP8266_BASE_PINS = {
|
||||
"A0": 17,
|
||||
"SS": 15,
|
||||
"MOSI": 13,
|
||||
"MISO": 12,
|
||||
"SCK": 14,
|
||||
"SDA": 4,
|
||||
"SCL": 5,
|
||||
"RX": 3,
|
||||
"TX": 1,
|
||||
}
|
||||
|
||||
ESP8266_BOARD_PINS = {
|
||||
"d1": {
|
||||
"D0": 3,
|
||||
"D1": 1,
|
||||
"D2": 16,
|
||||
"D3": 5,
|
||||
"D4": 4,
|
||||
"D5": 14,
|
||||
"D6": 12,
|
||||
"D7": 13,
|
||||
"D8": 0,
|
||||
"D9": 2,
|
||||
"D10": 15,
|
||||
"D11": 13,
|
||||
"D12": 14,
|
||||
"D13": 14,
|
||||
"D14": 4,
|
||||
"D15": 5,
|
||||
"LED": 2,
|
||||
},
|
||||
"d1_mini": {
|
||||
"D0": 16,
|
||||
"D1": 5,
|
||||
"D2": 4,
|
||||
"D3": 0,
|
||||
"D4": 2,
|
||||
"D5": 14,
|
||||
"D6": 12,
|
||||
"D7": 13,
|
||||
"D8": 15,
|
||||
"LED": 2,
|
||||
},
|
||||
"d1_mini_lite": "d1_mini",
|
||||
"d1_mini_pro": "d1_mini",
|
||||
"esp01": {},
|
||||
"esp01_1m": {},
|
||||
"esp07": {},
|
||||
"esp12e": {},
|
||||
"esp210": {},
|
||||
"esp8285": {},
|
||||
"esp_wroom_02": {},
|
||||
"espduino": {"LED": 16},
|
||||
"espectro": {"LED": 15, "BUTTON": 2},
|
||||
"espino": {"LED": 2, "LED_RED": 2, "LED_GREEN": 4, "LED_BLUE": 5, "BUTTON": 0},
|
||||
"espinotee": {"LED": 16},
|
||||
"espresso_lite_v1": {"LED": 16},
|
||||
"espresso_lite_v2": {"LED": 2},
|
||||
"gen4iod": {},
|
||||
"heltec_wifi_kit_8": "d1_mini",
|
||||
"huzzah": {
|
||||
"LED": 0,
|
||||
"LED_RED": 0,
|
||||
"LED_BLUE": 2,
|
||||
"D4": 4,
|
||||
"D5": 5,
|
||||
"D12": 12,
|
||||
"D13": 13,
|
||||
"D14": 14,
|
||||
"D15": 15,
|
||||
"D16": 16,
|
||||
},
|
||||
"inventone": {},
|
||||
"modwifi": {},
|
||||
"nodemcu": {
|
||||
"D0": 16,
|
||||
"D1": 5,
|
||||
"D2": 4,
|
||||
"D3": 0,
|
||||
"D4": 2,
|
||||
"D5": 14,
|
||||
"D6": 12,
|
||||
"D7": 13,
|
||||
"D8": 15,
|
||||
"D9": 3,
|
||||
"D10": 1,
|
||||
"LED": 16,
|
||||
},
|
||||
"nodemcuv2": "nodemcu",
|
||||
"oak": {
|
||||
"P0": 2,
|
||||
"P1": 5,
|
||||
"P2": 0,
|
||||
"P3": 3,
|
||||
"P4": 1,
|
||||
"P5": 4,
|
||||
"P6": 15,
|
||||
"P7": 13,
|
||||
"P8": 12,
|
||||
"P9": 14,
|
||||
"P10": 16,
|
||||
"P11": 17,
|
||||
"LED": 5,
|
||||
},
|
||||
"phoenix_v1": {"LED": 16},
|
||||
"phoenix_v2": {"LED": 2},
|
||||
"sparkfunBlynk": "thing",
|
||||
"thing": {"LED": 5, "SDA": 2, "SCL": 14},
|
||||
"thingdev": "thing",
|
||||
"wifi_slot": {"LED": 2},
|
||||
"wifiduino": {
|
||||
"D0": 3,
|
||||
"D1": 1,
|
||||
"D2": 2,
|
||||
"D3": 0,
|
||||
"D4": 4,
|
||||
"D5": 5,
|
||||
"D6": 16,
|
||||
"D7": 14,
|
||||
"D8": 12,
|
||||
"D9": 13,
|
||||
"D10": 15,
|
||||
"D11": 13,
|
||||
"D12": 12,
|
||||
"D13": 14,
|
||||
},
|
||||
"wifinfo": {
|
||||
"LED": 12,
|
||||
"D0": 16,
|
||||
"D1": 5,
|
||||
"D2": 4,
|
||||
"D3": 0,
|
||||
"D4": 2,
|
||||
"D5": 14,
|
||||
"D6": 12,
|
||||
"D7": 13,
|
||||
"D8": 15,
|
||||
"D9": 3,
|
||||
"D10": 1,
|
||||
},
|
||||
"wio_link": {"LED": 2, "GROVE": 15, "D0": 14, "D1": 12, "D2": 13, "BUTTON": 0},
|
||||
"wio_node": {"LED": 2, "GROVE": 15, "D0": 3, "D1": 5, "BUTTON": 0},
|
||||
"xinabox_cw01": {"SDA": 2, "SCL": 14, "LED": 5, "LED_RED": 12, "LED_GREEN": 13},
|
||||
}
|
||||
|
||||
FLASH_SIZE_1_MB = 2 ** 20
|
||||
FLASH_SIZE_512_KB = FLASH_SIZE_1_MB // 2
|
||||
FLASH_SIZE_2_MB = 2 * FLASH_SIZE_1_MB
|
||||
FLASH_SIZE_4_MB = 4 * FLASH_SIZE_1_MB
|
||||
FLASH_SIZE_16_MB = 16 * FLASH_SIZE_1_MB
|
||||
|
||||
ESP8266_FLASH_SIZES = {
|
||||
"d1": FLASH_SIZE_4_MB,
|
||||
"d1_mini": FLASH_SIZE_4_MB,
|
||||
"d1_mini_lite": FLASH_SIZE_1_MB,
|
||||
"d1_mini_pro": FLASH_SIZE_16_MB,
|
||||
"esp01": FLASH_SIZE_512_KB,
|
||||
"esp01_1m": FLASH_SIZE_1_MB,
|
||||
"esp07": FLASH_SIZE_4_MB,
|
||||
"esp12e": FLASH_SIZE_4_MB,
|
||||
"esp210": FLASH_SIZE_4_MB,
|
||||
"esp8285": FLASH_SIZE_1_MB,
|
||||
"esp_wroom_02": FLASH_SIZE_2_MB,
|
||||
"espduino": FLASH_SIZE_4_MB,
|
||||
"espectro": FLASH_SIZE_4_MB,
|
||||
"espino": FLASH_SIZE_4_MB,
|
||||
"espinotee": FLASH_SIZE_4_MB,
|
||||
"espresso_lite_v1": FLASH_SIZE_4_MB,
|
||||
"espresso_lite_v2": FLASH_SIZE_4_MB,
|
||||
"gen4iod": FLASH_SIZE_512_KB,
|
||||
"heltec_wifi_kit_8": FLASH_SIZE_4_MB,
|
||||
"huzzah": FLASH_SIZE_4_MB,
|
||||
"inventone": FLASH_SIZE_4_MB,
|
||||
"modwifi": FLASH_SIZE_2_MB,
|
||||
"nodemcu": FLASH_SIZE_4_MB,
|
||||
"nodemcuv2": FLASH_SIZE_4_MB,
|
||||
"oak": FLASH_SIZE_4_MB,
|
||||
"phoenix_v1": FLASH_SIZE_4_MB,
|
||||
"phoenix_v2": FLASH_SIZE_4_MB,
|
||||
"sparkfunBlynk": FLASH_SIZE_4_MB,
|
||||
"thing": FLASH_SIZE_512_KB,
|
||||
"thingdev": FLASH_SIZE_512_KB,
|
||||
"wifi_slot": FLASH_SIZE_1_MB,
|
||||
"wifiduino": FLASH_SIZE_4_MB,
|
||||
"wifinfo": FLASH_SIZE_1_MB,
|
||||
"wio_link": FLASH_SIZE_4_MB,
|
||||
"wio_node": FLASH_SIZE_4_MB,
|
||||
"xinabox_cw01": FLASH_SIZE_4_MB,
|
||||
}
|
||||
|
||||
ESP8266_LD_SCRIPTS = {
|
||||
FLASH_SIZE_512_KB: ("eagle.flash.512k0.ld", "eagle.flash.512k.ld"),
|
||||
FLASH_SIZE_1_MB: ("eagle.flash.1m0.ld", "eagle.flash.1m.ld"),
|
||||
FLASH_SIZE_2_MB: ("eagle.flash.2m.ld", "eagle.flash.2m.ld"),
|
||||
FLASH_SIZE_4_MB: ("eagle.flash.4m.ld", "eagle.flash.4m.ld"),
|
||||
FLASH_SIZE_16_MB: ("eagle.flash.16m.ld", "eagle.flash.16m14m.ld"),
|
||||
}
|
||||
|
||||
ESP32_BASE_PINS = {
|
||||
"TX": 1,
|
||||
"RX": 3,
|
||||
"SDA": 21,
|
||||
"SCL": 22,
|
||||
"SS": 5,
|
||||
"MOSI": 23,
|
||||
"MISO": 19,
|
||||
"SCK": 18,
|
||||
"A0": 36,
|
||||
"A3": 39,
|
||||
"A4": 32,
|
||||
"A5": 33,
|
||||
"A6": 34,
|
||||
"A7": 35,
|
||||
"A10": 4,
|
||||
"A11": 0,
|
||||
"A12": 2,
|
||||
"A13": 15,
|
||||
"A14": 13,
|
||||
"A15": 12,
|
||||
"A16": 14,
|
||||
"A17": 27,
|
||||
"A18": 25,
|
||||
"A19": 26,
|
||||
"T0": 4,
|
||||
"T1": 0,
|
||||
"T2": 2,
|
||||
"T3": 15,
|
||||
"T4": 13,
|
||||
"T5": 12,
|
||||
"T6": 14,
|
||||
"T7": 27,
|
||||
"T8": 33,
|
||||
"T9": 32,
|
||||
"DAC1": 25,
|
||||
"DAC2": 26,
|
||||
"SVP": 36,
|
||||
"SVN": 39,
|
||||
}
|
||||
|
||||
ESP32_BOARD_PINS = {
|
||||
"alksesp32": {
|
||||
"A0": 32,
|
||||
"A1": 33,
|
||||
"A2": 25,
|
||||
"A3": 26,
|
||||
"A4": 27,
|
||||
"A5": 14,
|
||||
"A6": 12,
|
||||
"A7": 15,
|
||||
"D0": 40,
|
||||
"D1": 41,
|
||||
"D10": 19,
|
||||
"D11": 21,
|
||||
"D12": 22,
|
||||
"D13": 23,
|
||||
"D2": 15,
|
||||
"D3": 2,
|
||||
"D4": 0,
|
||||
"D5": 4,
|
||||
"D6": 16,
|
||||
"D7": 17,
|
||||
"D8": 5,
|
||||
"D9": 18,
|
||||
"DHT_PIN": 26,
|
||||
"LED": 23,
|
||||
"L_B": 5,
|
||||
"L_G": 17,
|
||||
"L_R": 22,
|
||||
"L_RGB_B": 16,
|
||||
"L_RGB_G": 21,
|
||||
"L_RGB_R": 4,
|
||||
"L_Y": 23,
|
||||
"MISO": 22,
|
||||
"MOSI": 21,
|
||||
"PHOTO": 25,
|
||||
"PIEZO1": 19,
|
||||
"PIEZO2": 18,
|
||||
"POT1": 32,
|
||||
"POT2": 33,
|
||||
"S1": 4,
|
||||
"S2": 16,
|
||||
"S3": 18,
|
||||
"S4": 19,
|
||||
"S5": 21,
|
||||
"SCK": 23,
|
||||
"SCL": 14,
|
||||
"SDA": 27,
|
||||
"SS": 19,
|
||||
"SW1": 15,
|
||||
"SW2": 2,
|
||||
"SW3": 0,
|
||||
},
|
||||
"bpi-bit": {
|
||||
"BUTTON_A": 35,
|
||||
"BUTTON_B": 27,
|
||||
"BUZZER": 25,
|
||||
"LIGHT_SENSOR1": 36,
|
||||
"LIGHT_SENSOR2": 39,
|
||||
"MPU9250_INT": 0,
|
||||
"P0": 25,
|
||||
"P1": 32,
|
||||
"P10": 26,
|
||||
"P11": 27,
|
||||
"P12": 2,
|
||||
"P13": 18,
|
||||
"P14": 19,
|
||||
"P15": 23,
|
||||
"P16": 5,
|
||||
"P19": 22,
|
||||
"P2": 33,
|
||||
"P20": 21,
|
||||
"P3": 13,
|
||||
"P4": 15,
|
||||
"P5": 35,
|
||||
"P6": 12,
|
||||
"P7": 14,
|
||||
"P8": 16,
|
||||
"P9": 17,
|
||||
"RGB_LED": 4,
|
||||
"TEMPERATURE_SENSOR": 34,
|
||||
},
|
||||
"d-duino-32": {
|
||||
"D1": 5,
|
||||
"D10": 1,
|
||||
"D2": 4,
|
||||
"D3": 0,
|
||||
"D4": 2,
|
||||
"D5": 14,
|
||||
"D6": 12,
|
||||
"D7": 13,
|
||||
"D8": 15,
|
||||
"D9": 3,
|
||||
"MISO": 12,
|
||||
"MOSI": 13,
|
||||
"SCK": 14,
|
||||
"SCL": 4,
|
||||
"SDA": 5,
|
||||
"SS": 15,
|
||||
},
|
||||
"esp-wrover-kit": {},
|
||||
"esp32-devkitlipo": {},
|
||||
"esp32-evb": {
|
||||
"BUTTON": 34,
|
||||
"MISO": 15,
|
||||
"MOSI": 2,
|
||||
"SCK": 14,
|
||||
"SCL": 16,
|
||||
"SDA": 13,
|
||||
"SS": 17,
|
||||
},
|
||||
"esp32-gateway": {"BUTTON": 34, "LED": 33, "SCL": 16, "SDA": 32},
|
||||
"esp32-poe-iso": {
|
||||
"BUTTON": 34,
|
||||
"MISO": 15,
|
||||
"MOSI": 2,
|
||||
"SCK": 14,
|
||||
"SCL": 16,
|
||||
"SDA": 13,
|
||||
},
|
||||
"esp32-poe": {"BUTTON": 34, "MISO": 15, "MOSI": 2, "SCK": 14, "SCL": 16, "SDA": 13},
|
||||
"esp32-pro": {
|
||||
"BUTTON": 34,
|
||||
"MISO": 15,
|
||||
"MOSI": 2,
|
||||
"SCK": 14,
|
||||
"SCL": 16,
|
||||
"SDA": 13,
|
||||
"SS": 17,
|
||||
},
|
||||
"esp320": {
|
||||
"LED": 5,
|
||||
"MISO": 12,
|
||||
"MOSI": 13,
|
||||
"SCK": 14,
|
||||
"SCL": 14,
|
||||
"SDA": 2,
|
||||
"SS": 15,
|
||||
},
|
||||
"esp32cam": {},
|
||||
"esp32dev": {},
|
||||
"esp32doit-devkit-v1": {"LED": 2},
|
||||
"esp32thing": {"BUTTON": 0, "LED": 5, "SS": 2},
|
||||
"esp32vn-iot-uno": {},
|
||||
"espea32": {"BUTTON": 0, "LED": 5},
|
||||
"espectro32": {"LED": 15, "SD_SS": 33},
|
||||
"espino32": {"BUTTON": 0, "LED": 16},
|
||||
"featheresp32": {
|
||||
"A0": 26,
|
||||
"A1": 25,
|
||||
"A10": 27,
|
||||
"A11": 12,
|
||||
"A12": 13,
|
||||
"A13": 35,
|
||||
"A2": 34,
|
||||
"A4": 36,
|
||||
"A5": 4,
|
||||
"A6": 14,
|
||||
"A7": 32,
|
||||
"A8": 15,
|
||||
"A9": 33,
|
||||
"Ax": 2,
|
||||
"LED": 13,
|
||||
"MOSI": 18,
|
||||
"RX": 16,
|
||||
"SCK": 5,
|
||||
"SDA": 23,
|
||||
"SS": 33,
|
||||
"TX": 17,
|
||||
},
|
||||
"firebeetle32": {"LED": 2},
|
||||
"fm-devkit": {
|
||||
"D0": 34,
|
||||
"D1": 35,
|
||||
"D10": 0,
|
||||
"D2": 32,
|
||||
"D3": 33,
|
||||
"D4": 27,
|
||||
"D5": 14,
|
||||
"D6": 12,
|
||||
"D7": 13,
|
||||
"D8": 15,
|
||||
"D9": 23,
|
||||
"I2S_DOUT": 22,
|
||||
"I2S_LRCLK": 25,
|
||||
"I2S_MCLK": 2,
|
||||
"I2S_SCLK": 26,
|
||||
"LED": 5,
|
||||
"SCL": 17,
|
||||
"SDA": 16,
|
||||
"SW1": 4,
|
||||
"SW2": 18,
|
||||
"SW3": 19,
|
||||
"SW4": 21,
|
||||
},
|
||||
"frogboard": {},
|
||||
"heltec_wifi_kit_32": {
|
||||
"A1": 37,
|
||||
"A2": 38,
|
||||
"BUTTON": 0,
|
||||
"LED": 25,
|
||||
"RST_OLED": 16,
|
||||
"SCL_OLED": 15,
|
||||
"SDA_OLED": 4,
|
||||
"Vext": 21,
|
||||
},
|
||||
"heltec_wifi_lora_32": {
|
||||
"BUTTON": 0,
|
||||
"DIO0": 26,
|
||||
"DIO1": 33,
|
||||
"DIO2": 32,
|
||||
"LED": 25,
|
||||
"MOSI": 27,
|
||||
"RST_LoRa": 14,
|
||||
"RST_OLED": 16,
|
||||
"SCK": 5,
|
||||
"SCL_OLED": 15,
|
||||
"SDA_OLED": 4,
|
||||
"SS": 18,
|
||||
"Vext": 21,
|
||||
},
|
||||
"heltec_wifi_lora_32_V2": {
|
||||
"BUTTON": 0,
|
||||
"DIO0": 26,
|
||||
"DIO1": 35,
|
||||
"DIO2": 34,
|
||||
"LED": 25,
|
||||
"MOSI": 27,
|
||||
"RST_LoRa": 14,
|
||||
"RST_OLED": 16,
|
||||
"SCK": 5,
|
||||
"SCL_OLED": 15,
|
||||
"SDA_OLED": 4,
|
||||
"SS": 18,
|
||||
"Vext": 21,
|
||||
},
|
||||
"heltec_wireless_stick": {
|
||||
"BUTTON": 0,
|
||||
"DIO0": 26,
|
||||
"DIO1": 35,
|
||||
"DIO2": 34,
|
||||
"LED": 25,
|
||||
"MOSI": 27,
|
||||
"RST_LoRa": 14,
|
||||
"RST_OLED": 16,
|
||||
"SCK": 5,
|
||||
"SCL_OLED": 15,
|
||||
"SDA_OLED": 4,
|
||||
"SS": 18,
|
||||
"Vext": 21,
|
||||
},
|
||||
"hornbill32dev": {"BUTTON": 0, "LED": 13},
|
||||
"hornbill32minima": {"SS": 2},
|
||||
"intorobot": {
|
||||
"A1": 39,
|
||||
"A2": 35,
|
||||
"A3": 25,
|
||||
"A4": 26,
|
||||
"A5": 14,
|
||||
"A6": 12,
|
||||
"A7": 15,
|
||||
"A8": 13,
|
||||
"A9": 2,
|
||||
"BUTTON": 0,
|
||||
"D0": 19,
|
||||
"D1": 23,
|
||||
"D2": 18,
|
||||
"D3": 17,
|
||||
"D4": 16,
|
||||
"D5": 5,
|
||||
"D6": 4,
|
||||
"LED": 4,
|
||||
"MISO": 17,
|
||||
"MOSI": 16,
|
||||
"RGB_B_BUILTIN": 22,
|
||||
"RGB_G_BUILTIN": 21,
|
||||
"RGB_R_BUILTIN": 27,
|
||||
"SCL": 19,
|
||||
"SDA": 23,
|
||||
"T0": 19,
|
||||
"T1": 23,
|
||||
"T2": 18,
|
||||
"T3": 17,
|
||||
"T4": 16,
|
||||
"T5": 5,
|
||||
"T6": 4,
|
||||
},
|
||||
"iotaap_magnolia": {},
|
||||
"iotbusio": {},
|
||||
"iotbusproteus": {},
|
||||
"lolin32": {"LED": 5},
|
||||
"lolin32_lite": {"LED": 22},
|
||||
"lolin_d32": {"LED": 5, "_VBAT": 35},
|
||||
"lolin_d32_pro": {"LED": 5, "_VBAT": 35},
|
||||
"lopy": {
|
||||
"A1": 37,
|
||||
"A2": 38,
|
||||
"LED": 0,
|
||||
"MISO": 37,
|
||||
"MOSI": 22,
|
||||
"SCK": 13,
|
||||
"SCL": 13,
|
||||
"SDA": 12,
|
||||
"SS": 17,
|
||||
},
|
||||
"lopy4": {
|
||||
"A1": 37,
|
||||
"A2": 38,
|
||||
"LED": 0,
|
||||
"MISO": 37,
|
||||
"MOSI": 22,
|
||||
"SCK": 13,
|
||||
"SCL": 13,
|
||||
"SDA": 12,
|
||||
"SS": 18,
|
||||
},
|
||||
"m5stack-core-esp32": {
|
||||
"ADC1": 35,
|
||||
"ADC2": 36,
|
||||
"G0": 0,
|
||||
"G1": 1,
|
||||
"G12": 12,
|
||||
"G13": 13,
|
||||
"G15": 15,
|
||||
"G16": 16,
|
||||
"G17": 17,
|
||||
"G18": 18,
|
||||
"G19": 19,
|
||||
"G2": 2,
|
||||
"G21": 21,
|
||||
"G22": 22,
|
||||
"G23": 23,
|
||||
"G25": 25,
|
||||
"G26": 26,
|
||||
"G3": 3,
|
||||
"G34": 34,
|
||||
"G35": 35,
|
||||
"G36": 36,
|
||||
"G5": 5,
|
||||
"RXD2": 16,
|
||||
"TXD2": 17,
|
||||
},
|
||||
"m5stack-fire": {
|
||||
"ADC1": 35,
|
||||
"ADC2": 36,
|
||||
"G0": 0,
|
||||
"G1": 1,
|
||||
"G12": 12,
|
||||
"G13": 13,
|
||||
"G15": 15,
|
||||
"G16": 16,
|
||||
"G17": 17,
|
||||
"G18": 18,
|
||||
"G19": 19,
|
||||
"G2": 2,
|
||||
"G21": 21,
|
||||
"G22": 22,
|
||||
"G23": 23,
|
||||
"G25": 25,
|
||||
"G26": 26,
|
||||
"G3": 3,
|
||||
"G34": 34,
|
||||
"G35": 35,
|
||||
"G36": 36,
|
||||
"G5": 5,
|
||||
},
|
||||
"m5stack-grey": {
|
||||
"ADC1": 35,
|
||||
"ADC2": 36,
|
||||
"G0": 0,
|
||||
"G1": 1,
|
||||
"G12": 12,
|
||||
"G13": 13,
|
||||
"G15": 15,
|
||||
"G16": 16,
|
||||
"G17": 17,
|
||||
"G18": 18,
|
||||
"G19": 19,
|
||||
"G2": 2,
|
||||
"G21": 21,
|
||||
"G22": 22,
|
||||
"G23": 23,
|
||||
"G25": 25,
|
||||
"G26": 26,
|
||||
"G3": 3,
|
||||
"G34": 34,
|
||||
"G35": 35,
|
||||
"G36": 36,
|
||||
"G5": 5,
|
||||
"RXD2": 16,
|
||||
"TXD2": 17,
|
||||
},
|
||||
"m5stick-c": {
|
||||
"ADC1": 35,
|
||||
"ADC2": 36,
|
||||
"G0": 0,
|
||||
"G10": 10,
|
||||
"G26": 26,
|
||||
"G32": 32,
|
||||
"G33": 33,
|
||||
"G36": 36,
|
||||
"G37": 37,
|
||||
"G39": 39,
|
||||
"G9": 9,
|
||||
"MISO": 36,
|
||||
"MOSI": 15,
|
||||
"SCK": 13,
|
||||
"SCL": 33,
|
||||
"SDA": 32,
|
||||
},
|
||||
"magicbit": {
|
||||
"BLUE_LED": 17,
|
||||
"BUZZER": 25,
|
||||
"GREEN_LED": 16,
|
||||
"LDR": 36,
|
||||
"LED": 16,
|
||||
"LEFT_BUTTON": 35,
|
||||
"MOTOR1A": 27,
|
||||
"MOTOR1B": 18,
|
||||
"MOTOR2A": 16,
|
||||
"MOTOR2B": 17,
|
||||
"POT": 39,
|
||||
"RED_LED": 27,
|
||||
"RIGHT_PUTTON": 34,
|
||||
"YELLOW_LED": 18,
|
||||
},
|
||||
"mhetesp32devkit": {"LED": 2},
|
||||
"mhetesp32minikit": {"LED": 2},
|
||||
"microduino-core-esp32": {
|
||||
"A0": 12,
|
||||
"A1": 13,
|
||||
"A10": 25,
|
||||
"A11": 26,
|
||||
"A12": 27,
|
||||
"A13": 14,
|
||||
"A2": 15,
|
||||
"A3": 4,
|
||||
"A6": 38,
|
||||
"A7": 37,
|
||||
"A8": 32,
|
||||
"A9": 33,
|
||||
"D0": 3,
|
||||
"D1": 1,
|
||||
"D10": 5,
|
||||
"D11": 23,
|
||||
"D12": 19,
|
||||
"D13": 18,
|
||||
"D14": 12,
|
||||
"D15": 13,
|
||||
"D16": 15,
|
||||
"D17": 4,
|
||||
"D18": 22,
|
||||
"D19": 21,
|
||||
"D2": 16,
|
||||
"D20": 38,
|
||||
"D21": 37,
|
||||
"D3": 17,
|
||||
"D4": 32,
|
||||
"D5": 33,
|
||||
"D6": 25,
|
||||
"D7": 26,
|
||||
"D8": 27,
|
||||
"D9": 14,
|
||||
"SCL": 21,
|
||||
"SCL1": 13,
|
||||
"SDA": 22,
|
||||
"SDA1": 12,
|
||||
},
|
||||
"nano32": {"BUTTON": 0, "LED": 16},
|
||||
"nina_w10": {
|
||||
"D0": 3,
|
||||
"D1": 1,
|
||||
"D10": 5,
|
||||
"D11": 19,
|
||||
"D12": 23,
|
||||
"D13": 18,
|
||||
"D14": 13,
|
||||
"D15": 12,
|
||||
"D16": 32,
|
||||
"D17": 33,
|
||||
"D18": 21,
|
||||
"D19": 34,
|
||||
"D2": 26,
|
||||
"D20": 36,
|
||||
"D21": 39,
|
||||
"D3": 25,
|
||||
"D4": 35,
|
||||
"D5": 27,
|
||||
"D6": 22,
|
||||
"D7": 0,
|
||||
"D8": 15,
|
||||
"D9": 14,
|
||||
"LED_BLUE": 21,
|
||||
"LED_GREEN": 33,
|
||||
"LED_RED": 23,
|
||||
"SCL": 13,
|
||||
"SDA": 12,
|
||||
"SW1": 33,
|
||||
"SW2": 27,
|
||||
},
|
||||
"node32s": {},
|
||||
"nodemcu-32s": {"BUTTON": 0, "LED": 2},
|
||||
"odroid_esp32": {"ADC1": 35, "ADC2": 36, "LED": 2, "SCL": 4, "SDA": 15, "SS": 22},
|
||||
"onehorse32dev": {"A1": 37, "A2": 38, "BUTTON": 0, "LED": 5},
|
||||
"oroca_edubot": {
|
||||
"A0": 34,
|
||||
"A1": 39,
|
||||
"A2": 36,
|
||||
"A3": 33,
|
||||
"D0": 4,
|
||||
"D1": 16,
|
||||
"D2": 17,
|
||||
"D3": 22,
|
||||
"D4": 23,
|
||||
"D5": 5,
|
||||
"D6": 18,
|
||||
"D7": 19,
|
||||
"D8": 33,
|
||||
"LED": 13,
|
||||
"MOSI": 18,
|
||||
"RX": 16,
|
||||
"SCK": 5,
|
||||
"SDA": 23,
|
||||
"SS": 2,
|
||||
"TX": 17,
|
||||
"VBAT": 35,
|
||||
},
|
||||
"pico32": {},
|
||||
"pocket_32": {"LED": 16},
|
||||
"pycom_gpy": {
|
||||
"A1": 37,
|
||||
"A2": 38,
|
||||
"LED": 0,
|
||||
"MISO": 37,
|
||||
"MOSI": 22,
|
||||
"SCK": 13,
|
||||
"SCL": 13,
|
||||
"SDA": 12,
|
||||
"SS": 17,
|
||||
},
|
||||
"quantum": {},
|
||||
"sparkfun_lora_gateway_1-channel": {"MISO": 12, "MOSI": 13, "SCK": 14, "SS": 16},
|
||||
"tinypico": {},
|
||||
"ttgo-lora32-v1": {
|
||||
"A1": 37,
|
||||
"A2": 38,
|
||||
"BUTTON": 0,
|
||||
"LED": 2,
|
||||
"MOSI": 27,
|
||||
"SCK": 5,
|
||||
"SS": 18,
|
||||
},
|
||||
"ttgo-t-beam": {"BUTTON": 39, "LED": 14, "MOSI": 27, "SCK": 5, "SS": 18},
|
||||
"ttgo-t-watch": {"BUTTON": 36, "MISO": 2, "MOSI": 15, "SCK": 14, "SS": 13},
|
||||
"ttgo-t1": {"LED": 22, "MISO": 2, "MOSI": 15, "SCK": 14, "SCL": 23, "SS": 13},
|
||||
"ttgo-t7-v13-mini32": {"LED": 22},
|
||||
"ttgo-t7-v14-mini32": {"LED": 19},
|
||||
"turta_iot_node": {},
|
||||
"vintlabs-devkit-v1": {
|
||||
"LED": 2,
|
||||
"PWM0": 12,
|
||||
"PWM1": 13,
|
||||
"PWM2": 14,
|
||||
"PWM3": 15,
|
||||
"PWM4": 16,
|
||||
"PWM5": 17,
|
||||
"PWM6": 18,
|
||||
"PWM7": 19,
|
||||
},
|
||||
"wemos_d1_mini32": {
|
||||
"D0": 26,
|
||||
"D1": 22,
|
||||
"D2": 21,
|
||||
"D3": 17,
|
||||
"D4": 16,
|
||||
"D5": 18,
|
||||
"D6": 19,
|
||||
"D7": 23,
|
||||
"D8": 5,
|
||||
"LED": 2,
|
||||
"RXD": 3,
|
||||
"TXD": 1,
|
||||
"_VBAT": 35,
|
||||
},
|
||||
"wemosbat": {"LED": 16},
|
||||
"wesp32": {"MISO": 32, "SCL": 4, "SDA": 15},
|
||||
"widora-air": {
|
||||
"A1": 39,
|
||||
"A2": 35,
|
||||
"A3": 25,
|
||||
"A4": 26,
|
||||
"A5": 14,
|
||||
"A6": 12,
|
||||
"A7": 15,
|
||||
"A8": 13,
|
||||
"A9": 2,
|
||||
"BUTTON": 0,
|
||||
"D0": 19,
|
||||
"D1": 23,
|
||||
"D2": 18,
|
||||
"D3": 17,
|
||||
"D4": 16,
|
||||
"D5": 5,
|
||||
"D6": 4,
|
||||
"LED": 25,
|
||||
"MISO": 17,
|
||||
"MOSI": 16,
|
||||
"SCL": 19,
|
||||
"SDA": 23,
|
||||
"T0": 19,
|
||||
"T1": 23,
|
||||
"T2": 18,
|
||||
"T3": 17,
|
||||
"T4": 16,
|
||||
"T5": 5,
|
||||
"T6": 4,
|
||||
},
|
||||
"xinabox_cw02": {"LED": 27},
|
||||
}
|
||||
|
||||
ESP32_C3_BASE_PINS = {
|
||||
"TX": 21,
|
||||
"RX": 20,
|
||||
"ADC1_0": 0,
|
||||
"ADC1_1": 1,
|
||||
"ADC1_2": 2,
|
||||
"ADC1_3": 3,
|
||||
"ADC1_4": 4,
|
||||
"ADC2_0": 5,
|
||||
}
|
||||
|
||||
ESP32_C3_BOARD_PINS = {
|
||||
"esp32-c3-devkitm-1": {"LED": 8},
|
||||
"esp32-c3-devkitc-02": "esp32-c3-devkitm-1",
|
||||
}
|
||||
@@ -19,6 +19,7 @@ from esphome.cpp_generator import ( # noqa
|
||||
Statement,
|
||||
LineComment,
|
||||
progmem_array,
|
||||
static_const_array,
|
||||
statement,
|
||||
variable,
|
||||
new_variable,
|
||||
@@ -59,6 +60,7 @@ from esphome.cpp_types import ( # noqa
|
||||
uint8,
|
||||
uint16,
|
||||
uint32,
|
||||
uint64,
|
||||
int32,
|
||||
const_char_ptr,
|
||||
NAN,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace a4988 {
|
||||
|
||||
static const char *TAG = "a4988.stepper";
|
||||
static const char *const TAG = "a4988.stepper";
|
||||
|
||||
void A4988::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up A4988...");
|
||||
|
||||
@@ -18,16 +18,16 @@ CONFIG_SCHEMA = stepper.STEPPER_SCHEMA.extend(
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield stepper.register_stepper(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await stepper.register_stepper(var, config)
|
||||
|
||||
step_pin = yield cg.gpio_pin_expression(config[CONF_STEP_PIN])
|
||||
step_pin = await cg.gpio_pin_expression(config[CONF_STEP_PIN])
|
||||
cg.add(var.set_step_pin(step_pin))
|
||||
dir_pin = yield cg.gpio_pin_expression(config[CONF_DIR_PIN])
|
||||
dir_pin = await cg.gpio_pin_expression(config[CONF_DIR_PIN])
|
||||
cg.add(var.set_dir_pin(dir_pin))
|
||||
|
||||
if CONF_SLEEP_PIN in config:
|
||||
sleep_pin = yield cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
|
||||
sleep_pin = await cg.gpio_pin_expression(config[CONF_SLEEP_PIN])
|
||||
cg.add(var.set_sleep_pin(sleep_pin))
|
||||
|
||||
@@ -9,15 +9,15 @@
|
||||
namespace esphome {
|
||||
namespace ac_dimmer {
|
||||
|
||||
static const char *TAG = "ac_dimmer";
|
||||
static const char *const TAG = "ac_dimmer";
|
||||
|
||||
// Global array to store dimmer objects
|
||||
static AcDimmerDataStore *all_dimmers[32];
|
||||
static AcDimmerDataStore *all_dimmers[32]; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
/// Time in microseconds the gate should be held high
|
||||
/// 10µs should be long enough for most triacs
|
||||
/// For reference: BT136 datasheet says 2µs nominal (page 7)
|
||||
static uint32_t GATE_ENABLE_TIME = 10;
|
||||
static const uint32_t GATE_ENABLE_TIME = 10;
|
||||
|
||||
/// Function called from timer interrupt
|
||||
/// Input is current time in microseconds (micros())
|
||||
@@ -125,7 +125,7 @@ void ICACHE_RAM_ATTR HOT AcDimmerDataStore::gpio_intr() {
|
||||
}
|
||||
|
||||
void ICACHE_RAM_ATTR HOT AcDimmerDataStore::s_gpio_intr(AcDimmerDataStore *store) {
|
||||
// Attaching pin interrupts on the same pin will override the previous interupt
|
||||
// Attaching pin interrupts on the same pin will override the previous interrupt
|
||||
// However, the user expects that multiple dimmers sharing the same ZC pin will work.
|
||||
// We solve this in a bit of a hacky way: On each pin interrupt, we check all dimmers
|
||||
// if any of them are using the same ZC pin, and also trigger the interrupt for *them*.
|
||||
|
||||
@@ -32,18 +32,18 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
# override default min power to 10%
|
||||
if CONF_MIN_POWER not in config:
|
||||
config[CONF_MIN_POWER] = 0.1
|
||||
yield output.register_output(var, config)
|
||||
await output.register_output(var, config)
|
||||
|
||||
pin = yield cg.gpio_pin_expression(config[CONF_GATE_PIN])
|
||||
pin = await cg.gpio_pin_expression(config[CONF_GATE_PIN])
|
||||
cg.add(var.set_gate_pin(pin))
|
||||
pin = yield cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN])
|
||||
pin = await cg.gpio_pin_expression(config[CONF_ZERO_CROSS_PIN])
|
||||
cg.add(var.set_zero_cross_pin(pin))
|
||||
cg.add(var.set_init_with_half_cycle(config[CONF_INIT_WITH_HALF_CYCLE]))
|
||||
cg.add(var.set_method(config[CONF_METHOD]))
|
||||
|
||||
@@ -21,8 +21,7 @@ CONFIG_SCHEMA = cv.Schema({})
|
||||
"Adalight",
|
||||
{cv.GenerateID(CONF_UART_ID): cv.use_id(uart.UARTComponent)},
|
||||
)
|
||||
def adalight_light_effect_to_code(config, effect_id):
|
||||
async def adalight_light_effect_to_code(config, effect_id):
|
||||
effect = cg.new_Pvariable(effect_id, config[CONF_NAME])
|
||||
yield uart.register_uart_device(effect, config)
|
||||
|
||||
yield effect
|
||||
await uart.register_uart_device(effect, config)
|
||||
return effect
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace adalight {
|
||||
|
||||
static const char *TAG = "adalight_light_effect";
|
||||
static const char *const TAG = "adalight_light_effect";
|
||||
|
||||
static const uint32_t ADALIGHT_ACK_INTERVAL = 1000;
|
||||
static const uint32_t ADALIGHT_RECEIVE_TIMEOUT = 1000;
|
||||
@@ -42,7 +42,7 @@ void AdalightLightEffect::reset_frame_(light::AddressableLight &it) {
|
||||
|
||||
void AdalightLightEffect::blank_all_leds_(light::AddressableLight &it) {
|
||||
for (int led = it.size(); led-- > 0;) {
|
||||
it[led].set(COLOR_BLACK);
|
||||
it[led].set(Color::BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,50 @@ ADC_MODE(ADC_VCC)
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
|
||||
static const char *TAG = "adc";
|
||||
static const char *const TAG = "adc";
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
void ADCSensor::set_attenuation(adc_attenuation_t attenuation) { this->attenuation_ = attenuation; }
|
||||
void ADCSensor::set_attenuation(adc_atten_t attenuation) { this->attenuation_ = attenuation; }
|
||||
|
||||
inline adc1_channel_t gpio_to_adc1(uint8_t pin) {
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
switch (pin) {
|
||||
case 36:
|
||||
return ADC1_CHANNEL_0;
|
||||
case 37:
|
||||
return ADC1_CHANNEL_1;
|
||||
case 38:
|
||||
return ADC1_CHANNEL_2;
|
||||
case 39:
|
||||
return ADC1_CHANNEL_3;
|
||||
case 32:
|
||||
return ADC1_CHANNEL_4;
|
||||
case 33:
|
||||
return ADC1_CHANNEL_5;
|
||||
case 34:
|
||||
return ADC1_CHANNEL_6;
|
||||
case 35:
|
||||
return ADC1_CHANNEL_7;
|
||||
default:
|
||||
return ADC1_CHANNEL_MAX;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
|
||||
switch (pin) {
|
||||
case 0:
|
||||
return ADC1_CHANNEL_0;
|
||||
case 1:
|
||||
return ADC1_CHANNEL_1;
|
||||
case 2:
|
||||
return ADC1_CHANNEL_2;
|
||||
case 3:
|
||||
return ADC1_CHANNEL_3;
|
||||
case 4:
|
||||
return ADC1_CHANNEL_4;
|
||||
default:
|
||||
return ADC1_CHANNEL_MAX;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void ADCSensor::setup() {
|
||||
@@ -21,7 +61,11 @@ void ADCSensor::setup() {
|
||||
#endif
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
analogSetPinAttenuation(this->pin_, this->attenuation_);
|
||||
adc1_config_channel_atten(gpio_to_adc1(pin_), attenuation_);
|
||||
adc1_config_width(ADC_WIDTH_BIT_12);
|
||||
#if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2
|
||||
adc_gpio_init(ADC_UNIT_1, (adc_channel_t) gpio_to_adc1(pin_));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
void ADCSensor::dump_config() {
|
||||
@@ -36,18 +80,20 @@ void ADCSensor::dump_config() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
ESP_LOGCONFIG(TAG, " Pin: %u", this->pin_);
|
||||
switch (this->attenuation_) {
|
||||
case ADC_0db:
|
||||
case ADC_ATTEN_DB_0:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 0db (max 1.1V)");
|
||||
break;
|
||||
case ADC_2_5db:
|
||||
case ADC_ATTEN_DB_2_5:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 2.5db (max 1.5V)");
|
||||
break;
|
||||
case ADC_6db:
|
||||
case ADC_ATTEN_DB_6:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 6db (max 2.2V)");
|
||||
break;
|
||||
case ADC_11db:
|
||||
case ADC_ATTEN_DB_11:
|
||||
ESP_LOGCONFIG(TAG, " Attenuation: 11db (max 3.9V)");
|
||||
break;
|
||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
@@ -60,21 +106,43 @@ void ADCSensor::update() {
|
||||
}
|
||||
float ADCSensor::sample() {
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
float value_v = analogRead(this->pin_) / 4095.0f; // NOLINT
|
||||
int raw = adc1_get_raw(gpio_to_adc1(pin_));
|
||||
float value_v = raw / 4095.0f;
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
switch (this->attenuation_) {
|
||||
case ADC_0db:
|
||||
case ADC_ATTEN_DB_0:
|
||||
value_v *= 1.1;
|
||||
break;
|
||||
case ADC_2_5db:
|
||||
case ADC_ATTEN_DB_2_5:
|
||||
value_v *= 1.5;
|
||||
break;
|
||||
case ADC_6db:
|
||||
case ADC_ATTEN_DB_6:
|
||||
value_v *= 2.2;
|
||||
break;
|
||||
case ADC_11db:
|
||||
case ADC_ATTEN_DB_11:
|
||||
value_v *= 3.9;
|
||||
break;
|
||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||
break;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32H2
|
||||
switch (this->attenuation_) {
|
||||
case ADC_ATTEN_DB_0:
|
||||
value_v *= 0.84;
|
||||
break;
|
||||
case ADC_ATTEN_DB_2_5:
|
||||
value_v *= 1.13;
|
||||
break;
|
||||
case ADC_ATTEN_DB_6:
|
||||
value_v *= 1.56;
|
||||
break;
|
||||
case ADC_ATTEN_DB_11:
|
||||
value_v *= 3.0;
|
||||
break;
|
||||
default: // This is to satisfy the unused ADC_ATTEN_MAX
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
return value_v;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/voltage_sampler/voltage_sampler.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include "driver/adc.h"
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace adc {
|
||||
|
||||
@@ -13,7 +17,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
||||
public:
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
/// Set the attenuation for this pin. Only available on the ESP32.
|
||||
void set_attenuation(adc_attenuation_t attenuation);
|
||||
void set_attenuation(adc_atten_t attenuation);
|
||||
#endif
|
||||
|
||||
/// Update adc values.
|
||||
@@ -34,7 +38,7 @@ class ADCSensor : public sensor::Sensor, public PollingComponent, public voltage
|
||||
uint8_t pin_;
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
adc_attenuation_t attenuation_{ADC_0db};
|
||||
adc_atten_t attenuation_{ADC_ATTEN_DB_0};
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_PIN,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_VOLT,
|
||||
)
|
||||
|
||||
@@ -15,10 +15,10 @@ from esphome.const import (
|
||||
AUTO_LOAD = ["voltage_sampler"]
|
||||
|
||||
ATTENUATION_MODES = {
|
||||
"0db": cg.global_ns.ADC_0db,
|
||||
"2.5db": cg.global_ns.ADC_2_5db,
|
||||
"6db": cg.global_ns.ADC_6db,
|
||||
"11db": cg.global_ns.ADC_11db,
|
||||
"0db": cg.global_ns.ADC_ATTEN_DB_0,
|
||||
"2.5db": cg.global_ns.ADC_ATTEN_DB_2_5,
|
||||
"6db": cg.global_ns.ADC_ATTEN_DB_6,
|
||||
"11db": cg.global_ns.ADC_ATTEN_DB_11,
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,12 @@ ADCSensor = adc_ns.class_(
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE)
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ADCSensor),
|
||||
@@ -49,10 +54,10 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield sensor.register_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
if config[CONF_PIN] == "VCC":
|
||||
cg.add_define("USE_ADC_SENSOR_VCC")
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace addressable_light {
|
||||
|
||||
static const char* TAG = "addressable_light.display";
|
||||
static const char *const TAG = "addressable_light.display";
|
||||
|
||||
int AddressableLightDisplay::get_width_internal() { return this->width_; }
|
||||
int AddressableLightDisplay::get_height_internal() { return this->height_; }
|
||||
@@ -24,7 +24,7 @@ void AddressableLightDisplay::update() {
|
||||
void AddressableLightDisplay::display() {
|
||||
bool dirty = false;
|
||||
uint8_t old_r, old_g, old_b, old_w;
|
||||
Color* c;
|
||||
Color *c;
|
||||
|
||||
for (uint32_t offset = 0; offset < this->addressable_light_buffer_.size(); offset++) {
|
||||
c = &(this->addressable_light_buffer_[offset]);
|
||||
|
||||
@@ -38,18 +38,18 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
wrapped_light = yield cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID])
|
||||
wrapped_light = await cg.get_variable(config[CONF_ADDRESSABLE_LIGHT_ID])
|
||||
cg.add(var.set_width(config[CONF_WIDTH]))
|
||||
cg.add(var.set_height(config[CONF_HEIGHT]))
|
||||
cg.add(var.set_light(wrapped_light))
|
||||
|
||||
yield cg.register_component(var, config)
|
||||
yield display.register_display(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await display.register_display(var, config)
|
||||
|
||||
if CONF_PIXEL_MAPPER in config:
|
||||
pixel_mapper_template_ = yield cg.process_lambda(
|
||||
pixel_mapper_template_ = await cg.process_lambda(
|
||||
config[CONF_PIXEL_MAPPER],
|
||||
[(int, "x"), (int, "y")],
|
||||
return_type=cg.int_,
|
||||
@@ -57,7 +57,7 @@ def to_code(config):
|
||||
cg.add(var.set_pixel_mapper(pixel_mapper_template_))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = yield cg.process_lambda(
|
||||
lambda_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [(display.DisplayBufferRef, "it")], return_type=cg.void
|
||||
)
|
||||
cg.add(var.set_writer(lambda_))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace ade7953 {
|
||||
|
||||
static const char *TAG = "ade7953";
|
||||
static const char *const TAG = "ade7953";
|
||||
|
||||
void ADE7953::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "ADE7953:");
|
||||
@@ -21,8 +21,8 @@ void ADE7953::dump_config() {
|
||||
}
|
||||
|
||||
#define ADE_PUBLISH_(name, factor) \
|
||||
if (name && this->name##_sensor_) { \
|
||||
float value = *name / factor; \
|
||||
if ((name) && this->name##_sensor_) { \
|
||||
float value = *(name) / (factor); \
|
||||
this->name##_sensor_->publish_state(value); \
|
||||
}
|
||||
#define ADE_PUBLISH(name, factor) ADE_PUBLISH_(name, factor)
|
||||
|
||||
@@ -8,7 +8,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_VOLT,
|
||||
UNIT_AMPERE,
|
||||
UNIT_WATT,
|
||||
@@ -31,19 +31,34 @@ CONFIG_SCHEMA = (
|
||||
cv.GenerateID(): cv.declare_id(ADE7953),
|
||||
cv.Optional(CONF_IRQ_PIN): pins.input_pin,
|
||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CURRENT_A): sensor.sensor_schema(
|
||||
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CURRENT_B): sensor.sensor_schema(
|
||||
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ACTIVE_POWER_A): sensor.sensor_schema(
|
||||
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_ACTIVE_POWER_B): sensor.sensor_schema(
|
||||
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -52,10 +67,10 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_IRQ_PIN in config:
|
||||
cg.add(var.set_irq_pin(config[CONF_IRQ_PIN]))
|
||||
@@ -70,5 +85,5 @@ def to_code(config):
|
||||
if key not in config:
|
||||
continue
|
||||
conf = config[key]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(getattr(var, f"set_{key}_sensor")(sens))
|
||||
|
||||
@@ -23,9 +23,9 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_continuous_mode(config[CONF_CONTINUOUS_MODE]))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace ads1115 {
|
||||
|
||||
static const char *TAG = "ads1115";
|
||||
static const char *const TAG = "ads1115";
|
||||
static const uint8_t ADS1115_REGISTER_CONVERSION = 0x00;
|
||||
static const uint8_t ADS1115_REGISTER_CONFIG = 0x01;
|
||||
|
||||
@@ -64,11 +64,6 @@ void ADS1115Component::setup() {
|
||||
return;
|
||||
}
|
||||
this->prev_config_ = config;
|
||||
|
||||
for (auto *sensor : this->sensors_) {
|
||||
this->set_interval(sensor->get_name(), sensor->update_interval(),
|
||||
[this, sensor] { this->request_measurement(sensor); });
|
||||
}
|
||||
}
|
||||
void ADS1115Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up ADS1115...");
|
||||
@@ -107,17 +102,22 @@ float ADS1115Component::request_measurement(ADS1115Sensor *sensor) {
|
||||
}
|
||||
this->prev_config_ = config;
|
||||
|
||||
// about 1.6 ms with 860 samples per second
|
||||
// about 1.2 ms with 860 samples per second
|
||||
delay(2);
|
||||
|
||||
uint32_t start = millis();
|
||||
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
|
||||
if (millis() - start > 100) {
|
||||
ESP_LOGW(TAG, "Reading ADS1115 timed out");
|
||||
this->status_set_warning();
|
||||
return NAN;
|
||||
// in continuous mode, conversion will always be running, rely on the delay
|
||||
// to ensure conversion is taking place with the correct settings
|
||||
// can we use the rdy pin to trigger when a conversion is done?
|
||||
if (!this->continuous_mode_) {
|
||||
uint32_t start = millis();
|
||||
while (this->read_byte_16(ADS1115_REGISTER_CONFIG, &config) && (config >> 15) == 0) {
|
||||
if (millis() - start > 100) {
|
||||
ESP_LOGW(TAG, "Reading ADS1115 timed out");
|
||||
this->status_set_warning();
|
||||
return NAN;
|
||||
}
|
||||
yield();
|
||||
}
|
||||
yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ from esphome.const import (
|
||||
CONF_GAIN,
|
||||
CONF_MULTIPLEXER,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_VOLT,
|
||||
CONF_ID,
|
||||
)
|
||||
@@ -51,7 +51,12 @@ ADS1115Sensor = ads1115_ns.class_(
|
||||
|
||||
CONF_ADS1115_ID = "ads1115_id"
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE)
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ADS1115Sensor),
|
||||
@@ -64,11 +69,11 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
paren = yield cg.get_variable(config[CONF_ADS1115_ID])
|
||||
async def to_code(config):
|
||||
paren = await cg.get_variable(config[CONF_ADS1115_ID])
|
||||
var = cg.new_Pvariable(config[CONF_ID], paren)
|
||||
yield sensor.register_sensor(var, config)
|
||||
yield cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_multiplexer(config[CONF_MULTIPLEXER]))
|
||||
cg.add(var.set_gain(config[CONF_GAIN]))
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
//
|
||||
// According to the datasheet, the component is supposed to respond in more than 75ms. In fact, it can answer almost
|
||||
// immediately for temperature. But for humidity, it takes >90ms to get a valid data. From experience, we have best
|
||||
// results making successive requests; the current implementation make 3 attemps with a delay of 30ms each time.
|
||||
// results making successive requests; the current implementation makes 3 attempts with a delay of 30ms each time.
|
||||
|
||||
#include "aht10.h"
|
||||
#include "esphome/core/log.h"
|
||||
@@ -18,12 +18,12 @@
|
||||
namespace esphome {
|
||||
namespace aht10 {
|
||||
|
||||
static const char *TAG = "aht10";
|
||||
static const char *const TAG = "aht10";
|
||||
static const uint8_t AHT10_CALIBRATE_CMD[] = {0xE1};
|
||||
static const uint8_t AHT10_MEASURE_CMD[] = {0xAC, 0x33, 0x00};
|
||||
static const uint8_t AHT10_DEFAULT_DELAY = 5; // ms, for calibration and temperature measurement
|
||||
static const uint8_t AHT10_HUMIDITY_DELAY = 30; // ms
|
||||
static const uint8_t AHT10_ATTEMPS = 3; // safety margin, normally 3 attemps are enough: 3*30=90ms
|
||||
static const uint8_t AHT10_ATTEMPTS = 3; // safety margin, normally 3 attempts are enough: 3*30=90ms
|
||||
|
||||
void AHT10Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up AHT10...");
|
||||
@@ -58,8 +58,9 @@ void AHT10Component::update() {
|
||||
uint8_t delay = AHT10_DEFAULT_DELAY;
|
||||
if (this->humidity_sensor_ != nullptr)
|
||||
delay = AHT10_HUMIDITY_DELAY;
|
||||
for (int i = 0; i < AHT10_ATTEMPS; ++i) {
|
||||
ESP_LOGVV(TAG, "Attemps %u at %6ld", i, millis());
|
||||
for (int i = 0; i < AHT10_ATTEMPTS; ++i) {
|
||||
ESP_LOGVV(TAG, "Attempt %u at %6ld", i, millis());
|
||||
delay_microseconds_accurate(4);
|
||||
if (!this->read_bytes(0, data, 6, delay)) {
|
||||
ESP_LOGD(TAG, "Communication with AHT10 failed, waiting...");
|
||||
} else if ((data[0] & 0x80) == 0x80) { // Bit[7] = 0b1, device is busy
|
||||
|
||||
@@ -7,7 +7,7 @@ from esphome.const import (
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_PERCENT,
|
||||
)
|
||||
@@ -22,10 +22,16 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(AHT10Component),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS, ICON_EMPTY, 2, DEVICE_CLASS_TEMPERATURE
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||
UNIT_PERCENT, ICON_EMPTY, 2, DEVICE_CLASS_HUMIDITY
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -34,15 +40,15 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
|
||||
if CONF_HUMIDITY in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
cg.add(var.set_humidity_sensor(sens))
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
namespace esphome {
|
||||
namespace am2320 {
|
||||
|
||||
static const char *TAG = "am2320";
|
||||
static const char *const TAG = "am2320";
|
||||
|
||||
// ---=== Calc CRC16 ===---
|
||||
uint16_t crc_16(uint8_t *ptr, uint8_t length) {
|
||||
|
||||
@@ -7,8 +7,8 @@ from esphome.const import (
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
ICON_EMPTY,
|
||||
UNIT_PERCENT,
|
||||
)
|
||||
|
||||
@@ -24,10 +24,16 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(AM2320Component),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||
UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -36,15 +42,15 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
|
||||
if CONF_HUMIDITY in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
cg.add(var.set_humidity_sensor(sens))
|
||||
|
||||
@@ -34,7 +34,7 @@ CONFIG_SCHEMA = cv.All(font.validate_pillow_installed, ANIMATION_SCHEMA)
|
||||
CODEOWNERS = ["@syndlex"]
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
from PIL import Image
|
||||
|
||||
path = CORE.relative_config_path(config[CONF_FILE])
|
||||
|
||||
150
esphome/components/anova/anova.cpp
Normal file
150
esphome/components/anova/anova.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "anova.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace anova {
|
||||
|
||||
static const char *TAG = "anova";
|
||||
|
||||
using namespace esphome::climate;
|
||||
|
||||
void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); }
|
||||
|
||||
void Anova::setup() {
|
||||
this->codec_ = new AnovaCodec();
|
||||
this->current_request_ = 0;
|
||||
}
|
||||
|
||||
void Anova::loop() {}
|
||||
|
||||
void Anova::control(const ClimateCall &call) {
|
||||
if (call.get_mode().has_value()) {
|
||||
ClimateMode mode = *call.get_mode();
|
||||
AnovaPacket *pkt;
|
||||
switch (mode) {
|
||||
case climate::CLIMATE_MODE_OFF:
|
||||
pkt = this->codec_->get_stop_request();
|
||||
break;
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
pkt = this->codec_->get_start_request();
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unsupported mode: %d", mode);
|
||||
return;
|
||||
}
|
||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
||||
}
|
||||
if (call.get_target_temperature().has_value()) {
|
||||
auto pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature());
|
||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
||||
}
|
||||
}
|
||||
|
||||
void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
this->current_temperature = NAN;
|
||||
this->target_temperature = NAN;
|
||||
this->publish_state();
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
auto chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID);
|
||||
if (chr == nullptr) {
|
||||
ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str());
|
||||
ESP_LOGW(TAG, "[%s] Note, this component does not currently support Anova Nano.", this->get_name().c_str());
|
||||
break;
|
||||
}
|
||||
this->char_handle_ = chr->handle;
|
||||
|
||||
auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle);
|
||||
if (status) {
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
this->current_request_ = 0;
|
||||
this->update();
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (param->notify.handle != this->char_handle_)
|
||||
break;
|
||||
this->codec_->decode(param->notify.value, param->notify.value_len);
|
||||
if (this->codec_->has_target_temp()) {
|
||||
this->target_temperature = this->codec_->target_temp_;
|
||||
}
|
||||
if (this->codec_->has_current_temp()) {
|
||||
this->current_temperature = this->codec_->current_temp_;
|
||||
}
|
||||
if (this->codec_->has_running()) {
|
||||
this->mode = this->codec_->running_ ? climate::CLIMATE_MODE_HEAT : climate::CLIMATE_MODE_OFF;
|
||||
}
|
||||
if (this->codec_->has_unit()) {
|
||||
this->fahrenheit_ = (this->codec_->unit_ == 'f');
|
||||
ESP_LOGD(TAG, "Anova units is %s", this->fahrenheit_ ? "fahrenheit" : "celcius");
|
||||
this->current_request_++;
|
||||
}
|
||||
this->publish_state();
|
||||
|
||||
if (this->current_request_ > 1) {
|
||||
AnovaPacket *pkt = nullptr;
|
||||
switch (this->current_request_++) {
|
||||
case 2:
|
||||
pkt = this->codec_->get_read_target_temp_request();
|
||||
break;
|
||||
case 3:
|
||||
pkt = this->codec_->get_read_current_temp_request();
|
||||
break;
|
||||
default:
|
||||
this->current_request_ = 1;
|
||||
break;
|
||||
}
|
||||
if (pkt != nullptr) {
|
||||
auto status =
|
||||
esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length,
|
||||
pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(),
|
||||
status);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Anova::set_unit_of_measurement(const char *unit) { this->fahrenheit_ = !strncmp(unit, "f", 1); }
|
||||
|
||||
void Anova::update() {
|
||||
if (this->node_state != espbt::ClientState::Established)
|
||||
return;
|
||||
|
||||
if (this->current_request_ < 2) {
|
||||
auto pkt = this->codec_->get_read_device_status_request();
|
||||
if (this->current_request_ == 0)
|
||||
auto pkt = this->codec_->get_set_unit_request(this->fahrenheit_ ? 'f' : 'c');
|
||||
auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_,
|
||||
pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status)
|
||||
ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status);
|
||||
this->current_request_++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace anova
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
52
esphome/components/anova/anova.h
Normal file
52
esphome/components/anova/anova.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/climate/climate.h"
|
||||
#include "anova_base.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace anova {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
static const uint16_t ANOVA_SERVICE_UUID = 0xFFE0;
|
||||
static const uint16_t ANOVA_CHARACTERISTIC_UUID = 0xFFE1;
|
||||
|
||||
class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode, public PollingComponent {
|
||||
public:
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void update() override;
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
climate::ClimateTraits traits() {
|
||||
auto traits = climate::ClimateTraits();
|
||||
traits.set_supports_current_temperature(true);
|
||||
traits.set_supports_heat_mode(true);
|
||||
traits.set_visual_min_temperature(25.0);
|
||||
traits.set_visual_max_temperature(100.0);
|
||||
traits.set_visual_temperature_step(0.1);
|
||||
return traits;
|
||||
}
|
||||
void set_unit_of_measurement(const char *);
|
||||
|
||||
protected:
|
||||
AnovaCodec *codec_;
|
||||
void control(const climate::ClimateCall &call) override;
|
||||
uint16_t char_handle_;
|
||||
uint8_t current_request_;
|
||||
bool fahrenheit_;
|
||||
};
|
||||
|
||||
} // namespace anova
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
137
esphome/components/anova/anova_base.cpp
Normal file
137
esphome/components/anova/anova_base.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
#include "anova_base.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace anova {
|
||||
|
||||
float ftoc(float f) { return (f - 32.0) * (5.0f / 9.0f); }
|
||||
|
||||
float ctof(float c) { return (c * 9.0f / 5.0f) + 32.0; }
|
||||
|
||||
AnovaPacket *AnovaCodec::clean_packet_() {
|
||||
this->packet_.length = strlen((char *) this->packet_.data);
|
||||
this->packet_.data[this->packet_.length] = '\0';
|
||||
ESP_LOGV("anova", "SendPkt: %s\n", this->packet_.data);
|
||||
return &this->packet_;
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_read_device_status_request() {
|
||||
this->current_query_ = READ_DEVICE_STATUS;
|
||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_DEVICE_STATUS);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_read_target_temp_request() {
|
||||
this->current_query_ = READ_TARGET_TEMPERATURE;
|
||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_TARGET_TEMP);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_read_current_temp_request() {
|
||||
this->current_query_ = READ_CURRENT_TEMPERATURE;
|
||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_CURRENT_TEMP);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_read_unit_request() {
|
||||
this->current_query_ = READ_UNIT;
|
||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_UNIT);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_read_data_request() {
|
||||
this->current_query_ = READ_DATA;
|
||||
sprintf((char *) this->packet_.data, "%s", CMD_READ_DATA);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_set_target_temp_request(float temperature) {
|
||||
this->current_query_ = SET_TARGET_TEMPERATURE;
|
||||
if (this->fahrenheit_)
|
||||
temperature = ctof(temperature);
|
||||
sprintf((char *) this->packet_.data, CMD_SET_TARGET_TEMP, temperature);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_set_unit_request(char unit) {
|
||||
this->current_query_ = SET_UNIT;
|
||||
sprintf((char *) this->packet_.data, CMD_SET_TEMP_UNIT, unit);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_start_request() {
|
||||
this->current_query_ = START;
|
||||
sprintf((char *) this->packet_.data, CMD_START);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
AnovaPacket *AnovaCodec::get_stop_request() {
|
||||
this->current_query_ = STOP;
|
||||
sprintf((char *) this->packet_.data, CMD_STOP);
|
||||
return this->clean_packet_();
|
||||
}
|
||||
|
||||
void AnovaCodec::decode(const uint8_t *data, uint16_t length) {
|
||||
memset(this->buf_, 0, 32);
|
||||
strncpy(this->buf_, (char *) data, length);
|
||||
this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false;
|
||||
switch (this->current_query_) {
|
||||
case READ_DEVICE_STATUS: {
|
||||
if (!strncmp(this->buf_, "stopped", 7)) {
|
||||
this->has_running_ = true;
|
||||
this->running_ = false;
|
||||
}
|
||||
if (!strncmp(this->buf_, "running", 7)) {
|
||||
this->has_running_ = true;
|
||||
this->running_ = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case START: {
|
||||
if (!strncmp(this->buf_, "start", 5)) {
|
||||
this->has_running_ = true;
|
||||
this->running_ = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STOP: {
|
||||
if (!strncmp(this->buf_, "stop", 4)) {
|
||||
this->has_running_ = true;
|
||||
this->running_ = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case READ_TARGET_TEMPERATURE: {
|
||||
this->target_temp_ = strtof(this->buf_, nullptr);
|
||||
if (this->fahrenheit_)
|
||||
this->target_temp_ = ftoc(this->target_temp_);
|
||||
this->has_target_temp_ = true;
|
||||
break;
|
||||
}
|
||||
case SET_TARGET_TEMPERATURE: {
|
||||
this->target_temp_ = strtof(this->buf_, nullptr);
|
||||
if (this->fahrenheit_)
|
||||
this->target_temp_ = ftoc(this->target_temp_);
|
||||
this->has_target_temp_ = true;
|
||||
break;
|
||||
}
|
||||
case READ_CURRENT_TEMPERATURE: {
|
||||
this->current_temp_ = strtof(this->buf_, nullptr);
|
||||
if (this->fahrenheit_)
|
||||
this->current_temp_ = ftoc(this->current_temp_);
|
||||
this->has_current_temp_ = true;
|
||||
break;
|
||||
}
|
||||
case SET_UNIT:
|
||||
case READ_UNIT: {
|
||||
this->unit_ = this->buf_[0];
|
||||
this->fahrenheit_ = this->buf_[0] == 'f';
|
||||
this->has_unit_ = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace anova
|
||||
} // namespace esphome
|
||||
80
esphome/components/anova/anova_base.h
Normal file
80
esphome/components/anova/anova_base.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace anova {
|
||||
|
||||
enum CurrentQuery {
|
||||
NONE,
|
||||
READ_DEVICE_STATUS,
|
||||
READ_TARGET_TEMPERATURE,
|
||||
READ_CURRENT_TEMPERATURE,
|
||||
READ_DATA,
|
||||
READ_UNIT,
|
||||
SET_TARGET_TEMPERATURE,
|
||||
SET_UNIT,
|
||||
START,
|
||||
STOP,
|
||||
};
|
||||
|
||||
struct AnovaPacket {
|
||||
uint16_t length;
|
||||
uint8_t data[24];
|
||||
};
|
||||
|
||||
#define CMD_READ_DEVICE_STATUS "status\r"
|
||||
#define CMD_READ_TARGET_TEMP "read set temp\r"
|
||||
#define CMD_READ_CURRENT_TEMP "read temp\r"
|
||||
#define CMD_READ_UNIT "read unit\r"
|
||||
#define CMD_READ_DATA "read data\r"
|
||||
#define CMD_SET_TARGET_TEMP "set temp %.1f\r"
|
||||
#define CMD_SET_TEMP_UNIT "set unit %c\r"
|
||||
|
||||
#define CMD_START "start\r"
|
||||
#define CMD_STOP "stop\r"
|
||||
|
||||
class AnovaCodec {
|
||||
public:
|
||||
AnovaPacket *get_read_device_status_request();
|
||||
AnovaPacket *get_read_target_temp_request();
|
||||
AnovaPacket *get_read_current_temp_request();
|
||||
AnovaPacket *get_read_data_request();
|
||||
AnovaPacket *get_read_unit_request();
|
||||
|
||||
AnovaPacket *get_set_target_temp_request(float temperature);
|
||||
AnovaPacket *get_set_unit_request(char unit);
|
||||
|
||||
AnovaPacket *get_start_request();
|
||||
AnovaPacket *get_stop_request();
|
||||
|
||||
void decode(const uint8_t *data, uint16_t length);
|
||||
bool has_target_temp() { return this->has_target_temp_; }
|
||||
bool has_current_temp() { return this->has_current_temp_; }
|
||||
bool has_unit() { return this->has_unit_; }
|
||||
bool has_running() { return this->has_running_; }
|
||||
|
||||
union {
|
||||
float target_temp_;
|
||||
float current_temp_;
|
||||
char unit_;
|
||||
bool running_;
|
||||
};
|
||||
|
||||
protected:
|
||||
AnovaPacket *clean_packet_();
|
||||
AnovaPacket packet_;
|
||||
|
||||
bool has_target_temp_;
|
||||
bool has_current_temp_;
|
||||
bool has_unit_;
|
||||
bool has_running_;
|
||||
char buf_[32];
|
||||
bool fahrenheit_;
|
||||
|
||||
CurrentQuery current_query_;
|
||||
};
|
||||
|
||||
} // namespace anova
|
||||
} // namespace esphome
|
||||
36
esphome/components/anova/climate.py
Normal file
36
esphome/components/anova/climate.py
Normal file
@@ -0,0 +1,36 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import climate, ble_client
|
||||
from esphome.const import CONF_ID, CONF_UNIT_OF_MEASUREMENT
|
||||
|
||||
UNITS = {
|
||||
"f": "f",
|
||||
"c": "c",
|
||||
}
|
||||
|
||||
CODEOWNERS = ["@buxtronix"]
|
||||
DEPENDENCIES = ["ble_client"]
|
||||
|
||||
anova_ns = cg.esphome_ns.namespace("anova")
|
||||
Anova = anova_ns.class_(
|
||||
"Anova", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
climate.CLIMATE_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Anova),
|
||||
cv.Required(CONF_UNIT_OF_MEASUREMENT): cv.enum(UNITS),
|
||||
}
|
||||
)
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await climate.register_climate(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
|
||||
@@ -23,7 +23,7 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
namespace esphome {
|
||||
namespace apds9960 {
|
||||
|
||||
static const char *TAG = "apds9960";
|
||||
static const char *const TAG = "apds9960";
|
||||
|
||||
#define APDS9960_ERROR_CHECK(func) \
|
||||
if (!func) { \
|
||||
if (!(func)) { \
|
||||
this->mark_failed(); \
|
||||
return; \
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = yield cg.get_variable(config[CONF_APDS9960_ID])
|
||||
var = yield binary_sensor.new_binary_sensor(config)
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_APDS9960_ID])
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
func = getattr(hub, DIRECTIONS[config[CONF_DIRECTION]])
|
||||
cg.add(func(var))
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import CONF_TYPE, DEVICE_CLASS_EMPTY, UNIT_PERCENT, ICON_LIGHTBULB
|
||||
from esphome.const import (
|
||||
CONF_TYPE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_PERCENT,
|
||||
ICON_LIGHTBULB,
|
||||
)
|
||||
from . import APDS9960, CONF_APDS9960_ID
|
||||
|
||||
DEPENDENCIES = ["apds9960"]
|
||||
@@ -15,7 +20,10 @@ TYPES = {
|
||||
}
|
||||
|
||||
CONFIG_SCHEMA = sensor.sensor_schema(
|
||||
UNIT_PERCENT, ICON_LIGHTBULB, 1, DEVICE_CLASS_EMPTY
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
icon=ICON_LIGHTBULB,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Required(CONF_TYPE): cv.one_of(*TYPES, upper=True),
|
||||
@@ -24,8 +32,8 @@ CONFIG_SCHEMA = sensor.sensor_schema(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = yield cg.get_variable(config[CONF_APDS9960_ID])
|
||||
var = yield sensor.new_sensor(config)
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_APDS9960_ID])
|
||||
var = await sensor.new_sensor(config)
|
||||
func = getattr(hub, TYPES[config[CONF_TYPE]])
|
||||
cg.add(func(var))
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import base64
|
||||
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
@@ -6,6 +8,7 @@ from esphome.const import (
|
||||
CONF_DATA,
|
||||
CONF_DATA_TEMPLATE,
|
||||
CONF_ID,
|
||||
CONF_KEY,
|
||||
CONF_PASSWORD,
|
||||
CONF_PORT,
|
||||
CONF_REBOOT_TIMEOUT,
|
||||
@@ -19,7 +22,7 @@ from esphome.const import (
|
||||
from esphome.core import coroutine_with_priority
|
||||
|
||||
DEPENDENCIES = ["network"]
|
||||
AUTO_LOAD = ["async_tcp"]
|
||||
AUTO_LOAD = ["socket"]
|
||||
CODEOWNERS = ["@OttoWinter"]
|
||||
|
||||
api_ns = cg.esphome_ns.namespace("api")
|
||||
@@ -41,6 +44,22 @@ SERVICE_ARG_NATIVE_TYPES = {
|
||||
"float[]": cg.std_vector.template(float),
|
||||
"string[]": cg.std_vector.template(cg.std_string),
|
||||
}
|
||||
CONF_ENCRYPTION = "encryption"
|
||||
|
||||
|
||||
def validate_encryption_key(value):
|
||||
value = cv.string_strict(value)
|
||||
try:
|
||||
decoded = base64.b64decode(value, validate=True)
|
||||
except ValueError as err:
|
||||
raise cv.Invalid("Invalid key format, please check it's using base64") from err
|
||||
|
||||
if len(decoded) != 32:
|
||||
raise cv.Invalid("Encryption key must be base64 and 32 bytes long")
|
||||
|
||||
# Return original data for roundtrip conversion
|
||||
return value
|
||||
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
@@ -63,14 +82,19 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ENCRYPTION): cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_KEY): validate_encryption_key,
|
||||
}
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
@coroutine_with_priority(40.0)
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_port(config[CONF_PORT]))
|
||||
cg.add(var.set_password(config[CONF_PASSWORD]))
|
||||
@@ -90,7 +114,15 @@ def to_code(config):
|
||||
conf[CONF_TRIGGER_ID], templ, conf[CONF_SERVICE], service_arg_names
|
||||
)
|
||||
cg.add(var.register_user_service(trigger))
|
||||
yield automation.build_automation(trigger, func_args, conf)
|
||||
await automation.build_automation(trigger, func_args, conf)
|
||||
|
||||
if CONF_ENCRYPTION in config:
|
||||
conf = config[CONF_ENCRYPTION]
|
||||
decoded = base64.b64decode(conf[CONF_KEY])
|
||||
cg.add(var.set_noise_psk(list(decoded)))
|
||||
cg.add_define("USE_API_NOISE")
|
||||
else:
|
||||
cg.add_define("USE_API_PLAINTEXT")
|
||||
|
||||
cg.add_define("USE_API")
|
||||
cg.add_global(api_ns.using)
|
||||
@@ -116,21 +148,21 @@ HOMEASSISTANT_SERVICE_ACTION_SCHEMA = cv.Schema(
|
||||
HomeAssistantServiceCallAction,
|
||||
HOMEASSISTANT_SERVICE_ACTION_SCHEMA,
|
||||
)
|
||||
def homeassistant_service_to_code(config, action_id, template_arg, args):
|
||||
serv = yield cg.get_variable(config[CONF_ID])
|
||||
async def homeassistant_service_to_code(config, action_id, template_arg, args):
|
||||
serv = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, serv, False)
|
||||
templ = yield cg.templatable(config[CONF_SERVICE], args, None)
|
||||
templ = await cg.templatable(config[CONF_SERVICE], args, None)
|
||||
cg.add(var.set_service(templ))
|
||||
for key, value in config[CONF_DATA].items():
|
||||
templ = yield cg.templatable(value, args, None)
|
||||
templ = await cg.templatable(value, args, None)
|
||||
cg.add(var.add_data(key, templ))
|
||||
for key, value in config[CONF_DATA_TEMPLATE].items():
|
||||
templ = yield cg.templatable(value, args, None)
|
||||
templ = await cg.templatable(value, args, None)
|
||||
cg.add(var.add_data_template(key, templ))
|
||||
for key, value in config[CONF_VARIABLES].items():
|
||||
templ = yield cg.templatable(value, args, None)
|
||||
templ = await cg.templatable(value, args, None)
|
||||
cg.add(var.add_variable(key, templ))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
def validate_homeassistant_event(value):
|
||||
@@ -159,21 +191,21 @@ HOMEASSISTANT_EVENT_ACTION_SCHEMA = cv.Schema(
|
||||
HomeAssistantServiceCallAction,
|
||||
HOMEASSISTANT_EVENT_ACTION_SCHEMA,
|
||||
)
|
||||
def homeassistant_event_to_code(config, action_id, template_arg, args):
|
||||
serv = yield cg.get_variable(config[CONF_ID])
|
||||
async def homeassistant_event_to_code(config, action_id, template_arg, args):
|
||||
serv = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, serv, True)
|
||||
templ = yield cg.templatable(config[CONF_EVENT], args, None)
|
||||
templ = await cg.templatable(config[CONF_EVENT], args, None)
|
||||
cg.add(var.set_service(templ))
|
||||
for key, value in config[CONF_DATA].items():
|
||||
templ = yield cg.templatable(value, args, None)
|
||||
templ = await cg.templatable(value, args, None)
|
||||
cg.add(var.add_data(key, templ))
|
||||
for key, value in config[CONF_DATA_TEMPLATE].items():
|
||||
templ = yield cg.templatable(value, args, None)
|
||||
templ = await cg.templatable(value, args, None)
|
||||
cg.add(var.add_data_template(key, templ))
|
||||
for key, value in config[CONF_VARIABLES].items():
|
||||
templ = yield cg.templatable(value, args, None)
|
||||
templ = await cg.templatable(value, args, None)
|
||||
cg.add(var.add_variable(key, templ))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||
@@ -190,15 +222,15 @@ HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA = cv.maybe_simple_value(
|
||||
HomeAssistantServiceCallAction,
|
||||
HOMEASSISTANT_TAG_SCANNED_ACTION_SCHEMA,
|
||||
)
|
||||
def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
|
||||
serv = yield cg.get_variable(config[CONF_ID])
|
||||
async def homeassistant_tag_scanned_to_code(config, action_id, template_arg, args):
|
||||
serv = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, serv, True)
|
||||
cg.add(var.set_service("esphome.tag_scanned"))
|
||||
templ = yield cg.templatable(config[CONF_TAG], args, cg.std_string)
|
||||
templ = await cg.templatable(config[CONF_TAG], args, cg.std_string)
|
||||
cg.add(var.add_data("tag_id", templ))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_condition("api.connected", APIConnectedCondition, {})
|
||||
def api_connected_to_code(config, condition_id, template_arg, args):
|
||||
yield cg.new_Pvariable(condition_id, template_arg)
|
||||
async def api_connected_to_code(config, condition_id, template_arg, args):
|
||||
return cg.new_Pvariable(condition_id, template_arg)
|
||||
|
||||
@@ -38,6 +38,8 @@ service APIConnection {
|
||||
rpc switch_command (SwitchCommandRequest) returns (void) {}
|
||||
rpc camera_image (CameraImageRequest) returns (void) {}
|
||||
rpc climate_command (ClimateCommandRequest) returns (void) {}
|
||||
rpc number_command (NumberCommandRequest) returns (void) {}
|
||||
rpc select_command (SelectCommandRequest) returns (void) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -176,6 +178,10 @@ message DeviceInfoResponse {
|
||||
string model = 6;
|
||||
|
||||
bool has_deep_sleep = 7;
|
||||
|
||||
// The esphome project details if set
|
||||
string project_name = 8;
|
||||
string project_version = 9;
|
||||
}
|
||||
|
||||
message ListEntitiesRequest {
|
||||
@@ -208,6 +214,7 @@ message ListEntitiesBinarySensorResponse {
|
||||
|
||||
string device_class = 5;
|
||||
bool is_status_binary_sensor = 6;
|
||||
bool disabled_by_default = 7;
|
||||
}
|
||||
message BinarySensorStateResponse {
|
||||
option (id) = 21;
|
||||
@@ -237,6 +244,7 @@ message ListEntitiesCoverResponse {
|
||||
bool supports_position = 6;
|
||||
bool supports_tilt = 7;
|
||||
string device_class = 8;
|
||||
bool disabled_by_default = 9;
|
||||
}
|
||||
|
||||
enum LegacyCoverState {
|
||||
@@ -304,6 +312,7 @@ message ListEntitiesFanResponse {
|
||||
bool supports_speed = 6;
|
||||
bool supports_direction = 7;
|
||||
int32 supported_speed_count = 8;
|
||||
bool disabled_by_default = 9;
|
||||
}
|
||||
enum FanSpeed {
|
||||
FAN_SPEED_LOW = 0;
|
||||
@@ -347,6 +356,18 @@ message FanCommandRequest {
|
||||
}
|
||||
|
||||
// ==================== LIGHT ====================
|
||||
enum ColorMode {
|
||||
COLOR_MODE_UNKNOWN = 0;
|
||||
COLOR_MODE_ON_OFF = 1;
|
||||
COLOR_MODE_BRIGHTNESS = 2;
|
||||
COLOR_MODE_WHITE = 7;
|
||||
COLOR_MODE_COLOR_TEMPERATURE = 11;
|
||||
COLOR_MODE_COLD_WARM_WHITE = 19;
|
||||
COLOR_MODE_RGB = 35;
|
||||
COLOR_MODE_RGB_WHITE = 39;
|
||||
COLOR_MODE_RGB_COLOR_TEMPERATURE = 47;
|
||||
COLOR_MODE_RGB_COLD_WARM_WHITE = 51;
|
||||
}
|
||||
message ListEntitiesLightResponse {
|
||||
option (id) = 15;
|
||||
option (source) = SOURCE_SERVER;
|
||||
@@ -357,13 +378,16 @@ message ListEntitiesLightResponse {
|
||||
string name = 3;
|
||||
string unique_id = 4;
|
||||
|
||||
bool supports_brightness = 5;
|
||||
bool supports_rgb = 6;
|
||||
bool supports_white_value = 7;
|
||||
bool supports_color_temperature = 8;
|
||||
repeated ColorMode supported_color_modes = 12;
|
||||
// next four supports_* are for legacy clients, newer clients should use color modes
|
||||
bool legacy_supports_brightness = 5 [deprecated=true];
|
||||
bool legacy_supports_rgb = 6 [deprecated=true];
|
||||
bool legacy_supports_white_value = 7 [deprecated=true];
|
||||
bool legacy_supports_color_temperature = 8 [deprecated=true];
|
||||
float min_mireds = 9;
|
||||
float max_mireds = 10;
|
||||
repeated string effects = 11;
|
||||
bool disabled_by_default = 13;
|
||||
}
|
||||
message LightStateResponse {
|
||||
option (id) = 24;
|
||||
@@ -374,11 +398,15 @@ message LightStateResponse {
|
||||
fixed32 key = 1;
|
||||
bool state = 2;
|
||||
float brightness = 3;
|
||||
ColorMode color_mode = 11;
|
||||
float color_brightness = 10;
|
||||
float red = 4;
|
||||
float green = 5;
|
||||
float blue = 6;
|
||||
float white = 7;
|
||||
float color_temperature = 8;
|
||||
float cold_white = 12;
|
||||
float warm_white = 13;
|
||||
string effect = 9;
|
||||
}
|
||||
message LightCommandRequest {
|
||||
@@ -392,6 +420,10 @@ message LightCommandRequest {
|
||||
bool state = 3;
|
||||
bool has_brightness = 4;
|
||||
float brightness = 5;
|
||||
bool has_color_mode = 22;
|
||||
ColorMode color_mode = 23;
|
||||
bool has_color_brightness = 20;
|
||||
float color_brightness = 21;
|
||||
bool has_rgb = 6;
|
||||
float red = 7;
|
||||
float green = 8;
|
||||
@@ -400,6 +432,10 @@ message LightCommandRequest {
|
||||
float white = 11;
|
||||
bool has_color_temperature = 12;
|
||||
float color_temperature = 13;
|
||||
bool has_cold_white = 24;
|
||||
float cold_white = 25;
|
||||
bool has_warm_white = 26;
|
||||
float warm_white = 27;
|
||||
bool has_transition_length = 14;
|
||||
uint32 transition_length = 15;
|
||||
bool has_flash_length = 16;
|
||||
@@ -409,6 +445,17 @@ message LightCommandRequest {
|
||||
}
|
||||
|
||||
// ==================== SENSOR ====================
|
||||
enum SensorStateClass {
|
||||
STATE_CLASS_NONE = 0;
|
||||
STATE_CLASS_MEASUREMENT = 1;
|
||||
}
|
||||
|
||||
enum SensorLastResetType {
|
||||
LAST_RESET_NONE = 0;
|
||||
LAST_RESET_NEVER = 1;
|
||||
LAST_RESET_AUTO = 2;
|
||||
}
|
||||
|
||||
message ListEntitiesSensorResponse {
|
||||
option (id) = 16;
|
||||
option (source) = SOURCE_SERVER;
|
||||
@@ -424,6 +471,9 @@ message ListEntitiesSensorResponse {
|
||||
int32 accuracy_decimals = 7;
|
||||
bool force_update = 8;
|
||||
string device_class = 9;
|
||||
SensorStateClass state_class = 10;
|
||||
SensorLastResetType last_reset_type = 11;
|
||||
bool disabled_by_default = 12;
|
||||
}
|
||||
message SensorStateResponse {
|
||||
option (id) = 25;
|
||||
@@ -451,6 +501,7 @@ message ListEntitiesSwitchResponse {
|
||||
|
||||
string icon = 5;
|
||||
bool assumed_state = 6;
|
||||
bool disabled_by_default = 7;
|
||||
}
|
||||
message SwitchStateResponse {
|
||||
option (id) = 26;
|
||||
@@ -483,6 +534,7 @@ message ListEntitiesTextSensorResponse {
|
||||
string unique_id = 4;
|
||||
|
||||
string icon = 5;
|
||||
bool disabled_by_default = 6;
|
||||
}
|
||||
message TextSensorStateResponse {
|
||||
option (id) = 27;
|
||||
@@ -561,6 +613,7 @@ message SubscribeHomeAssistantStateResponse {
|
||||
option (id) = 39;
|
||||
option (source) = SOURCE_SERVER;
|
||||
string entity_id = 1;
|
||||
string attribute = 2;
|
||||
}
|
||||
|
||||
message HomeAssistantStateResponse {
|
||||
@@ -570,6 +623,7 @@ message HomeAssistantStateResponse {
|
||||
|
||||
string entity_id = 1;
|
||||
string state = 2;
|
||||
string attribute = 3;
|
||||
}
|
||||
|
||||
// ==================== IMPORT TIME ====================
|
||||
@@ -640,6 +694,7 @@ message ListEntitiesCameraResponse {
|
||||
fixed32 key = 2;
|
||||
string name = 3;
|
||||
string unique_id = 4;
|
||||
bool disabled_by_default = 5;
|
||||
}
|
||||
|
||||
message CameraImageResponse {
|
||||
@@ -664,11 +719,12 @@ message CameraImageRequest {
|
||||
// ==================== CLIMATE ====================
|
||||
enum ClimateMode {
|
||||
CLIMATE_MODE_OFF = 0;
|
||||
CLIMATE_MODE_AUTO = 1;
|
||||
CLIMATE_MODE_HEAT_COOL = 1;
|
||||
CLIMATE_MODE_COOL = 2;
|
||||
CLIMATE_MODE_HEAT = 3;
|
||||
CLIMATE_MODE_FAN_ONLY = 4;
|
||||
CLIMATE_MODE_DRY = 5;
|
||||
CLIMATE_MODE_AUTO = 6;
|
||||
}
|
||||
enum ClimateFanMode {
|
||||
CLIMATE_FAN_ON = 0;
|
||||
@@ -696,6 +752,16 @@ enum ClimateAction {
|
||||
CLIMATE_ACTION_DRYING = 5;
|
||||
CLIMATE_ACTION_FAN = 6;
|
||||
}
|
||||
enum ClimatePreset {
|
||||
CLIMATE_PRESET_NONE = 0;
|
||||
CLIMATE_PRESET_HOME = 1;
|
||||
CLIMATE_PRESET_AWAY = 2;
|
||||
CLIMATE_PRESET_BOOST = 3;
|
||||
CLIMATE_PRESET_COMFORT = 4;
|
||||
CLIMATE_PRESET_ECO = 5;
|
||||
CLIMATE_PRESET_SLEEP = 6;
|
||||
CLIMATE_PRESET_ACTIVITY = 7;
|
||||
}
|
||||
message ListEntitiesClimateResponse {
|
||||
option (id) = 46;
|
||||
option (source) = SOURCE_SERVER;
|
||||
@@ -712,10 +778,16 @@ message ListEntitiesClimateResponse {
|
||||
float visual_min_temperature = 8;
|
||||
float visual_max_temperature = 9;
|
||||
float visual_temperature_step = 10;
|
||||
bool supports_away = 11;
|
||||
// for older peer versions - in new system this
|
||||
// is if CLIMATE_PRESET_AWAY exists is supported_presets
|
||||
bool legacy_supports_away = 11;
|
||||
bool supports_action = 12;
|
||||
repeated ClimateFanMode supported_fan_modes = 13;
|
||||
repeated ClimateSwingMode supported_swing_modes = 14;
|
||||
repeated string supported_custom_fan_modes = 15;
|
||||
repeated ClimatePreset supported_presets = 16;
|
||||
repeated string supported_custom_presets = 17;
|
||||
bool disabled_by_default = 18;
|
||||
}
|
||||
message ClimateStateResponse {
|
||||
option (id) = 47;
|
||||
@@ -729,10 +801,14 @@ message ClimateStateResponse {
|
||||
float target_temperature = 4;
|
||||
float target_temperature_low = 5;
|
||||
float target_temperature_high = 6;
|
||||
bool away = 7;
|
||||
// For older peers, equal to preset == CLIMATE_PRESET_AWAY
|
||||
bool legacy_away = 7;
|
||||
ClimateAction action = 8;
|
||||
ClimateFanMode fan_mode = 9;
|
||||
ClimateSwingMode swing_mode = 10;
|
||||
string custom_fan_mode = 11;
|
||||
ClimatePreset preset = 12;
|
||||
string custom_preset = 13;
|
||||
}
|
||||
message ClimateCommandRequest {
|
||||
option (id) = 48;
|
||||
@@ -749,10 +825,93 @@ message ClimateCommandRequest {
|
||||
float target_temperature_low = 7;
|
||||
bool has_target_temperature_high = 8;
|
||||
float target_temperature_high = 9;
|
||||
bool has_away = 10;
|
||||
bool away = 11;
|
||||
// legacy, for older peers, newer ones should use CLIMATE_PRESET_AWAY in preset
|
||||
bool has_legacy_away = 10;
|
||||
bool legacy_away = 11;
|
||||
bool has_fan_mode = 12;
|
||||
ClimateFanMode fan_mode = 13;
|
||||
bool has_swing_mode = 14;
|
||||
ClimateSwingMode swing_mode = 15;
|
||||
bool has_custom_fan_mode = 16;
|
||||
string custom_fan_mode = 17;
|
||||
bool has_preset = 18;
|
||||
ClimatePreset preset = 19;
|
||||
bool has_custom_preset = 20;
|
||||
string custom_preset = 21;
|
||||
}
|
||||
|
||||
// ==================== NUMBER ====================
|
||||
message ListEntitiesNumberResponse {
|
||||
option (id) = 49;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_NUMBER";
|
||||
|
||||
string object_id = 1;
|
||||
fixed32 key = 2;
|
||||
string name = 3;
|
||||
string unique_id = 4;
|
||||
|
||||
string icon = 5;
|
||||
float min_value = 6;
|
||||
float max_value = 7;
|
||||
float step = 8;
|
||||
bool disabled_by_default = 9;
|
||||
}
|
||||
message NumberStateResponse {
|
||||
option (id) = 50;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_NUMBER";
|
||||
option (no_delay) = true;
|
||||
|
||||
fixed32 key = 1;
|
||||
float state = 2;
|
||||
// If the number does not have a valid state yet.
|
||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||
bool missing_state = 3;
|
||||
}
|
||||
message NumberCommandRequest {
|
||||
option (id) = 51;
|
||||
option (source) = SOURCE_CLIENT;
|
||||
option (ifdef) = "USE_NUMBER";
|
||||
option (no_delay) = true;
|
||||
|
||||
fixed32 key = 1;
|
||||
float state = 2;
|
||||
}
|
||||
|
||||
// ==================== SELECT ====================
|
||||
message ListEntitiesSelectResponse {
|
||||
option (id) = 52;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_SELECT";
|
||||
|
||||
string object_id = 1;
|
||||
fixed32 key = 2;
|
||||
string name = 3;
|
||||
string unique_id = 4;
|
||||
|
||||
string icon = 5;
|
||||
repeated string options = 6;
|
||||
bool disabled_by_default = 7;
|
||||
}
|
||||
message SelectStateResponse {
|
||||
option (id) = 53;
|
||||
option (source) = SOURCE_SERVER;
|
||||
option (ifdef) = "USE_SELECT";
|
||||
option (no_delay) = true;
|
||||
|
||||
fixed32 key = 1;
|
||||
string state = 2;
|
||||
// If the select does not have a valid state yet.
|
||||
// Equivalent to `!obj->has_state()` - inverse logic to make state packets smaller
|
||||
bool missing_state = 3;
|
||||
}
|
||||
message SelectCommandRequest {
|
||||
option (id) = 54;
|
||||
option (source) = SOURCE_CLIENT;
|
||||
option (ifdef) = "USE_SELECT";
|
||||
option (no_delay) = true;
|
||||
|
||||
fixed32 key = 1;
|
||||
string state = 2;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/core/version.h"
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef USE_DEEP_SLEEP
|
||||
#include "esphome/components/deep_sleep/deep_sleep_component.h"
|
||||
@@ -16,76 +17,38 @@
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
static const char *TAG = "api.connection";
|
||||
static const char *const TAG = "api.connection";
|
||||
|
||||
APIConnection::APIConnection(AsyncClient *client, APIServer *parent)
|
||||
: client_(client), parent_(parent), initial_state_iterator_(parent, this), list_entities_iterator_(parent, this) {
|
||||
this->client_->onError([](void *s, AsyncClient *c, int8_t error) { ((APIConnection *) s)->on_error_(error); }, this);
|
||||
this->client_->onDisconnect([](void *s, AsyncClient *c) { ((APIConnection *) s)->on_disconnect_(); }, this);
|
||||
this->client_->onTimeout([](void *s, AsyncClient *c, uint32_t time) { ((APIConnection *) s)->on_timeout_(time); },
|
||||
this);
|
||||
this->client_->onData([](void *s, AsyncClient *c, void *buf,
|
||||
size_t len) { ((APIConnection *) s)->on_data_(reinterpret_cast<uint8_t *>(buf), len); },
|
||||
this);
|
||||
APIConnection::APIConnection(std::unique_ptr<socket::Socket> sock, APIServer *parent)
|
||||
: parent_(parent),
|
||||
initial_state_iterator_(parent, this),
|
||||
list_entities_iterator_(parent, this) {
|
||||
this->proto_write_buffer_.reserve(64);
|
||||
|
||||
this->send_buffer_.reserve(64);
|
||||
this->recv_buffer_.reserve(32);
|
||||
this->client_info_ = this->client_->remoteIP().toString().c_str();
|
||||
#ifdef USE_API_NOISE
|
||||
helper_ = std::unique_ptr<APIFrameHelper>{new APINoiseFrameHelper(std::move(sock), parent->get_noise_ctx())};
|
||||
#elif defined(USE_API_PLAINTEXT)
|
||||
helper_ = std::unique_ptr<APIFrameHelper>{new APIPlaintextFrameHelper(std::move(sock))};
|
||||
#else
|
||||
#error "No api frame helper enabled"
|
||||
#endif
|
||||
|
||||
}
|
||||
void APIConnection::start() {
|
||||
this->last_traffic_ = millis();
|
||||
}
|
||||
APIConnection::~APIConnection() { delete this->client_; }
|
||||
void APIConnection::on_error_(int8_t error) { this->remove_ = true; }
|
||||
void APIConnection::on_disconnect_() { this->remove_ = true; }
|
||||
void APIConnection::on_timeout_(uint32_t time) { this->on_fatal_error(); }
|
||||
void APIConnection::on_data_(uint8_t *buf, size_t len) {
|
||||
if (len == 0 || buf == nullptr)
|
||||
|
||||
APIError err = helper_->init();
|
||||
if (err != APIError::OK) {
|
||||
ESP_LOGW(TAG, "Helper init failed: %d errno=%d", (int) err, errno);
|
||||
remove_ = true;
|
||||
return;
|
||||
this->recv_buffer_.insert(this->recv_buffer_.end(), buf, buf + len);
|
||||
}
|
||||
void APIConnection::parse_recv_buffer_() {
|
||||
if (this->recv_buffer_.empty() || this->remove_)
|
||||
return;
|
||||
|
||||
while (!this->recv_buffer_.empty()) {
|
||||
if (this->recv_buffer_[0] != 0x00) {
|
||||
ESP_LOGW(TAG, "Invalid preamble from %s", this->client_info_.c_str());
|
||||
this->on_fatal_error();
|
||||
return;
|
||||
}
|
||||
uint32_t i = 1;
|
||||
const uint32_t size = this->recv_buffer_.size();
|
||||
uint32_t consumed;
|
||||
auto msg_size_varint = ProtoVarInt::parse(&this->recv_buffer_[i], size - i, &consumed);
|
||||
if (!msg_size_varint.has_value())
|
||||
// not enough data there yet
|
||||
return;
|
||||
i += consumed;
|
||||
uint32_t msg_size = msg_size_varint->as_uint32();
|
||||
|
||||
auto msg_type_varint = ProtoVarInt::parse(&this->recv_buffer_[i], size - i, &consumed);
|
||||
if (!msg_type_varint.has_value())
|
||||
// not enough data there yet
|
||||
return;
|
||||
i += consumed;
|
||||
uint32_t msg_type = msg_type_varint->as_uint32();
|
||||
|
||||
if (size - i < msg_size)
|
||||
// message body not fully received
|
||||
return;
|
||||
|
||||
uint8_t *msg = &this->recv_buffer_[i];
|
||||
this->read_message(msg_size, msg_type, msg);
|
||||
if (this->remove_)
|
||||
return;
|
||||
// pop front
|
||||
uint32_t total = i + msg_size;
|
||||
this->recv_buffer_.erase(this->recv_buffer_.begin(), this->recv_buffer_.begin() + total);
|
||||
this->last_traffic_ = millis();
|
||||
}
|
||||
client_info_ = helper_->getpeername();
|
||||
helper_->set_log_info(client_info_);
|
||||
}
|
||||
|
||||
void APIConnection::disconnect_client() {
|
||||
this->client_->close();
|
||||
void APIConnection::force_disconnect_client() {
|
||||
this->helper_->close();
|
||||
this->remove_ = true;
|
||||
}
|
||||
|
||||
@@ -93,61 +56,78 @@ void APIConnection::loop() {
|
||||
if (this->remove_)
|
||||
return;
|
||||
|
||||
if (this->next_close_) {
|
||||
this->disconnect_client();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!network_is_connected()) {
|
||||
// when network is disconnected force disconnect immediately
|
||||
// don't wait for timeout
|
||||
this->on_fatal_error();
|
||||
return;
|
||||
}
|
||||
if (this->client_->disconnected()) {
|
||||
// failsafe for disconnect logic
|
||||
this->on_disconnect_();
|
||||
if (this->next_close_) {
|
||||
this->helper_->close();
|
||||
this->remove_ = true;
|
||||
return;
|
||||
}
|
||||
this->parse_recv_buffer_();
|
||||
|
||||
APIError err = helper_->loop();
|
||||
if (err != APIError::OK) {
|
||||
on_fatal_error();
|
||||
ESP_LOGW(TAG, "%s: Socket operation failed: %d", client_info_.c_str(), (int) err);
|
||||
return;
|
||||
}
|
||||
ReadPacketBuffer buffer;
|
||||
err = helper_->read_packet(&buffer);
|
||||
if (err == APIError::WOULD_BLOCK) {
|
||||
// pass
|
||||
} else if (err != APIError::OK) {
|
||||
on_fatal_error();
|
||||
ESP_LOGW(TAG, "%s: Reading failed: %d", client_info_.c_str(), (int) err);
|
||||
return;
|
||||
} else {
|
||||
this->last_traffic_ = millis();
|
||||
// read a packet
|
||||
this->read_message(
|
||||
buffer.data_len,
|
||||
buffer.type,
|
||||
&buffer.container[buffer.data_offset]
|
||||
);
|
||||
if (this->remove_)
|
||||
return;
|
||||
}
|
||||
|
||||
this->list_entities_iterator_.advance();
|
||||
this->initial_state_iterator_.advance();
|
||||
|
||||
const uint32_t keepalive = 60000;
|
||||
const uint32_t now = millis();
|
||||
if (this->sent_ping_) {
|
||||
// Disconnect if not responded within 2.5*keepalive
|
||||
if (millis() - this->last_traffic_ > (keepalive * 5) / 2) {
|
||||
if (now - this->last_traffic_ > (keepalive * 5) / 2) {
|
||||
this->force_disconnect_client();
|
||||
ESP_LOGW(TAG, "'%s' didn't respond to ping request in time. Disconnecting...", this->client_info_.c_str());
|
||||
this->disconnect_client();
|
||||
}
|
||||
} else if (millis() - this->last_traffic_ > keepalive) {
|
||||
} else if (now - this->last_traffic_ > keepalive) {
|
||||
this->sent_ping_ = true;
|
||||
this->send_ping_request(PingRequest());
|
||||
}
|
||||
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
if (this->image_reader_.available()) {
|
||||
uint32_t space = this->client_->space();
|
||||
// reserve 15 bytes for metadata, and at least 64 bytes of data
|
||||
if (space >= 15 + 64) {
|
||||
uint32_t to_send = std::min(space - 15, this->image_reader_.available());
|
||||
auto buffer = this->create_buffer();
|
||||
// fixed32 key = 1;
|
||||
buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash());
|
||||
// bytes data = 2;
|
||||
buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send);
|
||||
// bool done = 3;
|
||||
bool done = this->image_reader_.available() == to_send;
|
||||
buffer.encode_bool(3, done);
|
||||
bool success = this->send_buffer(buffer, 44);
|
||||
if (this->image_reader_.available() && this->helper_->can_write_without_blocking()) {
|
||||
uint32_t to_send = std::min((size_t) 1024, this->image_reader_.available());
|
||||
auto buffer = this->create_buffer();
|
||||
// fixed32 key = 1;
|
||||
buffer.encode_fixed32(1, esp32_camera::global_esp32_camera->get_object_id_hash());
|
||||
// bytes data = 2;
|
||||
buffer.encode_bytes(2, this->image_reader_.peek_data_buffer(), to_send);
|
||||
// bool done = 3;
|
||||
bool done = this->image_reader_.available() == to_send;
|
||||
buffer.encode_bool(3, done);
|
||||
bool success = this->send_buffer(buffer, 44);
|
||||
|
||||
if (success) {
|
||||
this->image_reader_.consume_data(to_send);
|
||||
}
|
||||
if (success && done) {
|
||||
this->image_reader_.return_image();
|
||||
}
|
||||
if (success) {
|
||||
this->image_reader_.consume_data(to_send);
|
||||
}
|
||||
if (success && done) {
|
||||
this->image_reader_.return_image();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -176,6 +156,7 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
|
||||
msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor);
|
||||
msg.device_class = binary_sensor->get_device_class();
|
||||
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
|
||||
msg.disabled_by_default = binary_sensor->is_disabled_by_default();
|
||||
return this->send_list_entities_binary_sensor_response(msg);
|
||||
}
|
||||
#endif
|
||||
@@ -207,6 +188,7 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
|
||||
msg.supports_position = traits.get_supports_position();
|
||||
msg.supports_tilt = traits.get_supports_tilt();
|
||||
msg.device_class = cover->get_device_class();
|
||||
msg.disabled_by_default = cover->is_disabled_by_default();
|
||||
return this->send_list_entities_cover_response(msg);
|
||||
}
|
||||
void APIConnection::cover_command(const CoverCommandRequest &msg) {
|
||||
@@ -268,6 +250,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
|
||||
msg.supports_speed = traits.supports_speed();
|
||||
msg.supports_direction = traits.supports_direction();
|
||||
msg.supported_speed_count = traits.supported_speed_count();
|
||||
msg.disabled_by_default = fan->is_disabled_by_default();
|
||||
return this->send_list_entities_fan_response(msg);
|
||||
}
|
||||
void APIConnection::fan_command(const FanCommandRequest &msg) {
|
||||
@@ -301,21 +284,28 @@ bool APIConnection::send_light_state(light::LightState *light) {
|
||||
|
||||
auto traits = light->get_traits();
|
||||
auto values = light->remote_values;
|
||||
auto color_mode = values.get_color_mode();
|
||||
LightStateResponse resp{};
|
||||
|
||||
resp.key = light->get_object_id_hash();
|
||||
resp.state = values.is_on();
|
||||
if (traits.get_supports_brightness())
|
||||
resp.color_mode = static_cast<enums::ColorMode>(color_mode);
|
||||
if (color_mode & light::ColorCapability::BRIGHTNESS)
|
||||
resp.brightness = values.get_brightness();
|
||||
if (traits.get_supports_rgb()) {
|
||||
if (color_mode & light::ColorCapability::RGB) {
|
||||
resp.color_brightness = values.get_color_brightness();
|
||||
resp.red = values.get_red();
|
||||
resp.green = values.get_green();
|
||||
resp.blue = values.get_blue();
|
||||
}
|
||||
if (traits.get_supports_rgb_white_value())
|
||||
if (color_mode & light::ColorCapability::WHITE)
|
||||
resp.white = values.get_white();
|
||||
if (traits.get_supports_color_temperature())
|
||||
if (color_mode & light::ColorCapability::COLOR_TEMPERATURE)
|
||||
resp.color_temperature = values.get_color_temperature();
|
||||
if (color_mode & light::ColorCapability::COLD_WARM_WHITE) {
|
||||
resp.cold_white = values.get_cold_white();
|
||||
resp.warm_white = values.get_warm_white();
|
||||
}
|
||||
if (light->supports_effects())
|
||||
resp.effect = light->get_effect_name();
|
||||
return this->send_light_state_response(resp);
|
||||
@@ -327,11 +317,21 @@ bool APIConnection::send_light_info(light::LightState *light) {
|
||||
msg.object_id = light->get_object_id();
|
||||
msg.name = light->get_name();
|
||||
msg.unique_id = get_default_unique_id("light", light);
|
||||
msg.supports_brightness = traits.get_supports_brightness();
|
||||
msg.supports_rgb = traits.get_supports_rgb();
|
||||
msg.supports_white_value = traits.get_supports_rgb_white_value();
|
||||
msg.supports_color_temperature = traits.get_supports_color_temperature();
|
||||
if (msg.supports_color_temperature) {
|
||||
|
||||
msg.disabled_by_default = light->is_disabled_by_default();
|
||||
|
||||
for (auto mode : traits.get_supported_color_modes())
|
||||
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
|
||||
|
||||
msg.legacy_supports_brightness = traits.supports_color_capability(light::ColorCapability::BRIGHTNESS);
|
||||
msg.legacy_supports_rgb = traits.supports_color_capability(light::ColorCapability::RGB);
|
||||
msg.legacy_supports_white_value =
|
||||
msg.legacy_supports_rgb && (traits.supports_color_capability(light::ColorCapability::WHITE) ||
|
||||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE));
|
||||
msg.legacy_supports_color_temperature = traits.supports_color_capability(light::ColorCapability::COLOR_TEMPERATURE) ||
|
||||
traits.supports_color_capability(light::ColorCapability::COLD_WARM_WHITE);
|
||||
|
||||
if (msg.legacy_supports_color_temperature) {
|
||||
msg.min_mireds = traits.get_min_mireds();
|
||||
msg.max_mireds = traits.get_max_mireds();
|
||||
}
|
||||
@@ -352,6 +352,10 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
|
||||
call.set_state(msg.state);
|
||||
if (msg.has_brightness)
|
||||
call.set_brightness(msg.brightness);
|
||||
if (msg.has_color_mode)
|
||||
call.set_color_mode(static_cast<light::ColorMode>(msg.color_mode));
|
||||
if (msg.has_color_brightness)
|
||||
call.set_color_brightness(msg.color_brightness);
|
||||
if (msg.has_rgb) {
|
||||
call.set_red(msg.red);
|
||||
call.set_green(msg.green);
|
||||
@@ -361,6 +365,10 @@ void APIConnection::light_command(const LightCommandRequest &msg) {
|
||||
call.set_white(msg.white);
|
||||
if (msg.has_color_temperature)
|
||||
call.set_color_temperature(msg.color_temperature);
|
||||
if (msg.has_cold_white)
|
||||
call.set_cold_white(msg.cold_white);
|
||||
if (msg.has_warm_white)
|
||||
call.set_warm_white(msg.warm_white);
|
||||
if (msg.has_transition_length)
|
||||
call.set_transition_length(msg.transition_length);
|
||||
if (msg.has_flash_length)
|
||||
@@ -395,6 +403,10 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) {
|
||||
msg.accuracy_decimals = sensor->get_accuracy_decimals();
|
||||
msg.force_update = sensor->get_force_update();
|
||||
msg.device_class = sensor->get_device_class();
|
||||
msg.state_class = static_cast<enums::SensorStateClass>(sensor->state_class);
|
||||
msg.last_reset_type = static_cast<enums::SensorLastResetType>(sensor->last_reset_type);
|
||||
msg.disabled_by_default = sensor->is_disabled_by_default();
|
||||
|
||||
return this->send_list_entities_sensor_response(msg);
|
||||
}
|
||||
#endif
|
||||
@@ -417,6 +429,7 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) {
|
||||
msg.unique_id = get_default_unique_id("switch", a_switch);
|
||||
msg.icon = a_switch->get_icon();
|
||||
msg.assumed_state = a_switch->assumed_state();
|
||||
msg.disabled_by_default = a_switch->is_disabled_by_default();
|
||||
return this->send_list_entities_switch_response(msg);
|
||||
}
|
||||
void APIConnection::switch_command(const SwitchCommandRequest &msg) {
|
||||
@@ -451,6 +464,7 @@ bool APIConnection::send_text_sensor_info(text_sensor::TextSensor *text_sensor)
|
||||
if (msg.unique_id.empty())
|
||||
msg.unique_id = get_default_unique_id("text_sensor", text_sensor);
|
||||
msg.icon = text_sensor->get_icon();
|
||||
msg.disabled_by_default = text_sensor->is_disabled_by_default();
|
||||
return this->send_list_entities_text_sensor_response(msg);
|
||||
}
|
||||
#endif
|
||||
@@ -473,10 +487,16 @@ bool APIConnection::send_climate_state(climate::Climate *climate) {
|
||||
} else {
|
||||
resp.target_temperature = climate->target_temperature;
|
||||
}
|
||||
if (traits.get_supports_away())
|
||||
resp.away = climate->away;
|
||||
if (traits.get_supports_fan_modes())
|
||||
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode);
|
||||
if (traits.get_supports_fan_modes() && climate->fan_mode.has_value())
|
||||
resp.fan_mode = static_cast<enums::ClimateFanMode>(climate->fan_mode.value());
|
||||
if (!traits.get_supported_custom_fan_modes().empty() && climate->custom_fan_mode.has_value())
|
||||
resp.custom_fan_mode = climate->custom_fan_mode.value();
|
||||
if (traits.get_supports_presets() && climate->preset.has_value()) {
|
||||
resp.preset = static_cast<enums::ClimatePreset>(climate->preset.value());
|
||||
resp.legacy_away = resp.preset == enums::CLIMATE_PRESET_AWAY;
|
||||
}
|
||||
if (!traits.get_supported_custom_presets().empty() && climate->custom_preset.has_value())
|
||||
resp.custom_preset = climate->custom_preset.value();
|
||||
if (traits.get_supports_swing_modes())
|
||||
resp.swing_mode = static_cast<enums::ClimateSwingMode>(climate->swing_mode);
|
||||
return this->send_climate_state_response(resp);
|
||||
@@ -488,29 +508,31 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
|
||||
msg.object_id = climate->get_object_id();
|
||||
msg.name = climate->get_name();
|
||||
msg.unique_id = get_default_unique_id("climate", climate);
|
||||
|
||||
msg.disabled_by_default = climate->is_disabled_by_default();
|
||||
|
||||
msg.supports_current_temperature = traits.get_supports_current_temperature();
|
||||
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
|
||||
for (auto mode : {climate::CLIMATE_MODE_AUTO, climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_COOL,
|
||||
climate::CLIMATE_MODE_HEAT, climate::CLIMATE_MODE_DRY, climate::CLIMATE_MODE_FAN_ONLY}) {
|
||||
if (traits.supports_mode(mode))
|
||||
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
|
||||
}
|
||||
|
||||
for (auto mode : traits.get_supported_modes())
|
||||
msg.supported_modes.push_back(static_cast<enums::ClimateMode>(mode));
|
||||
|
||||
msg.visual_min_temperature = traits.get_visual_min_temperature();
|
||||
msg.visual_max_temperature = traits.get_visual_max_temperature();
|
||||
msg.visual_temperature_step = traits.get_visual_temperature_step();
|
||||
msg.supports_away = traits.get_supports_away();
|
||||
msg.legacy_supports_away = traits.supports_preset(climate::CLIMATE_PRESET_AWAY);
|
||||
msg.supports_action = traits.get_supports_action();
|
||||
for (auto fan_mode : {climate::CLIMATE_FAN_ON, climate::CLIMATE_FAN_OFF, climate::CLIMATE_FAN_AUTO,
|
||||
climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM, climate::CLIMATE_FAN_HIGH,
|
||||
climate::CLIMATE_FAN_MIDDLE, climate::CLIMATE_FAN_FOCUS, climate::CLIMATE_FAN_DIFFUSE}) {
|
||||
if (traits.supports_fan_mode(fan_mode))
|
||||
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
|
||||
}
|
||||
for (auto swing_mode : {climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_BOTH, climate::CLIMATE_SWING_VERTICAL,
|
||||
climate::CLIMATE_SWING_HORIZONTAL}) {
|
||||
if (traits.supports_swing_mode(swing_mode))
|
||||
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
|
||||
}
|
||||
|
||||
for (auto fan_mode : traits.get_supported_fan_modes())
|
||||
msg.supported_fan_modes.push_back(static_cast<enums::ClimateFanMode>(fan_mode));
|
||||
for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes())
|
||||
msg.supported_custom_fan_modes.push_back(custom_fan_mode);
|
||||
for (auto preset : traits.get_supported_presets())
|
||||
msg.supported_presets.push_back(static_cast<enums::ClimatePreset>(preset));
|
||||
for (auto const &custom_preset : traits.get_supported_custom_presets())
|
||||
msg.supported_custom_presets.push_back(custom_preset);
|
||||
for (auto swing_mode : traits.get_supported_swing_modes())
|
||||
msg.supported_swing_modes.push_back(static_cast<enums::ClimateSwingMode>(swing_mode));
|
||||
return this->send_list_entities_climate_response(msg);
|
||||
}
|
||||
void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
||||
@@ -527,16 +549,95 @@ void APIConnection::climate_command(const ClimateCommandRequest &msg) {
|
||||
call.set_target_temperature_low(msg.target_temperature_low);
|
||||
if (msg.has_target_temperature_high)
|
||||
call.set_target_temperature_high(msg.target_temperature_high);
|
||||
if (msg.has_away)
|
||||
call.set_away(msg.away);
|
||||
if (msg.has_legacy_away)
|
||||
call.set_preset(msg.legacy_away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME);
|
||||
if (msg.has_fan_mode)
|
||||
call.set_fan_mode(static_cast<climate::ClimateFanMode>(msg.fan_mode));
|
||||
if (msg.has_custom_fan_mode)
|
||||
call.set_fan_mode(msg.custom_fan_mode);
|
||||
if (msg.has_preset)
|
||||
call.set_preset(static_cast<climate::ClimatePreset>(msg.preset));
|
||||
if (msg.has_custom_preset)
|
||||
call.set_preset(msg.custom_preset);
|
||||
if (msg.has_swing_mode)
|
||||
call.set_swing_mode(static_cast<climate::ClimateSwingMode>(msg.swing_mode));
|
||||
call.perform();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
bool APIConnection::send_number_state(number::Number *number, float state) {
|
||||
if (!this->state_subscription_)
|
||||
return false;
|
||||
|
||||
NumberStateResponse resp{};
|
||||
resp.key = number->get_object_id_hash();
|
||||
resp.state = state;
|
||||
resp.missing_state = !number->has_state();
|
||||
return this->send_number_state_response(resp);
|
||||
}
|
||||
bool APIConnection::send_number_info(number::Number *number) {
|
||||
ListEntitiesNumberResponse msg;
|
||||
msg.key = number->get_object_id_hash();
|
||||
msg.object_id = number->get_object_id();
|
||||
msg.name = number->get_name();
|
||||
msg.unique_id = get_default_unique_id("number", number);
|
||||
msg.icon = number->traits.get_icon();
|
||||
msg.disabled_by_default = number->is_disabled_by_default();
|
||||
|
||||
msg.min_value = number->traits.get_min_value();
|
||||
msg.max_value = number->traits.get_max_value();
|
||||
msg.step = number->traits.get_step();
|
||||
|
||||
return this->send_list_entities_number_response(msg);
|
||||
}
|
||||
void APIConnection::number_command(const NumberCommandRequest &msg) {
|
||||
number::Number *number = App.get_number_by_key(msg.key);
|
||||
if (number == nullptr)
|
||||
return;
|
||||
|
||||
auto call = number->make_call();
|
||||
call.set_value(msg.state);
|
||||
call.perform();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
bool APIConnection::send_select_state(select::Select *select, std::string state) {
|
||||
if (!this->state_subscription_)
|
||||
return false;
|
||||
|
||||
SelectStateResponse resp{};
|
||||
resp.key = select->get_object_id_hash();
|
||||
resp.state = std::move(state);
|
||||
resp.missing_state = !select->has_state();
|
||||
return this->send_select_state_response(resp);
|
||||
}
|
||||
bool APIConnection::send_select_info(select::Select *select) {
|
||||
ListEntitiesSelectResponse msg;
|
||||
msg.key = select->get_object_id_hash();
|
||||
msg.object_id = select->get_object_id();
|
||||
msg.name = select->get_name();
|
||||
msg.unique_id = get_default_unique_id("select", select);
|
||||
msg.icon = select->traits.get_icon();
|
||||
msg.disabled_by_default = select->is_disabled_by_default();
|
||||
|
||||
for (const auto &option : select->traits.get_options())
|
||||
msg.options.push_back(option);
|
||||
|
||||
return this->send_list_entities_select_response(msg);
|
||||
}
|
||||
void APIConnection::select_command(const SelectCommandRequest &msg) {
|
||||
select::Select *select = App.get_select_by_key(msg.key);
|
||||
if (select == nullptr)
|
||||
return;
|
||||
|
||||
auto call = select->make_call();
|
||||
call.set_option(msg.state);
|
||||
call.perform();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
void APIConnection::send_camera_state(std::shared_ptr<esp32_camera::CameraImage> image) {
|
||||
if (!this->state_subscription_)
|
||||
@@ -551,6 +652,7 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) {
|
||||
msg.object_id = camera->get_object_id();
|
||||
msg.name = camera->get_name();
|
||||
msg.unique_id = get_default_unique_id("camera", camera);
|
||||
msg.disabled_by_default = camera->is_disabled_by_default();
|
||||
return this->send_list_entities_camera_response(msg);
|
||||
}
|
||||
void APIConnection::camera_image(const CameraImageRequest &msg) {
|
||||
@@ -596,13 +698,13 @@ bool APIConnection::send_log_message(int level, const char *tag, const char *lin
|
||||
}
|
||||
|
||||
HelloResponse APIConnection::hello(const HelloRequest &msg) {
|
||||
this->client_info_ = msg.client_info + " (" + this->client_->remoteIP().toString().c_str();
|
||||
this->client_info_ += ")";
|
||||
this->client_info_ = msg.client_info + " (" + this->helper_->getpeername() + ")";
|
||||
this->helper_->set_log_info(client_info_);
|
||||
ESP_LOGV(TAG, "Hello from client: '%s'", this->client_info_.c_str());
|
||||
|
||||
HelloResponse resp;
|
||||
resp.api_version_major = 1;
|
||||
resp.api_version_minor = 4;
|
||||
resp.api_version_minor = 6;
|
||||
resp.server_info = App.get_name() + " (esphome v" ESPHOME_VERSION ")";
|
||||
this->connection_state_ = ConnectionState::CONNECTED;
|
||||
return resp;
|
||||
@@ -637,13 +739,18 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) {
|
||||
#endif
|
||||
#ifdef USE_DEEP_SLEEP
|
||||
resp.has_deep_sleep = deep_sleep::global_has_deep_sleep;
|
||||
#endif
|
||||
#ifdef ESPHOME_PROJECT_NAME
|
||||
resp.project_name = ESPHOME_PROJECT_NAME;
|
||||
resp.project_version = ESPHOME_PROJECT_VERSION;
|
||||
#endif
|
||||
return resp;
|
||||
}
|
||||
void APIConnection::on_home_assistant_state_response(const HomeAssistantStateResponse &msg) {
|
||||
for (auto &it : this->parent_->get_state_subs())
|
||||
if (it.entity_id == msg.entity_id)
|
||||
if (it.entity_id == msg.entity_id && it.attribute.value() == msg.attribute) {
|
||||
it.callback(msg.state);
|
||||
}
|
||||
}
|
||||
void APIConnection::execute_service(const ExecuteServiceRequest &msg) {
|
||||
bool found = false;
|
||||
@@ -660,6 +767,7 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
|
||||
for (auto &it : this->parent_->get_state_subs()) {
|
||||
SubscribeHomeAssistantStateResponse resp;
|
||||
resp.entity_id = it.entity_id;
|
||||
resp.attribute = it.attribute.value();
|
||||
if (!this->send_subscribe_home_assistant_state_response(resp)) {
|
||||
this->on_fatal_error();
|
||||
return;
|
||||
@@ -669,44 +777,35 @@ void APIConnection::subscribe_home_assistant_states(const SubscribeHomeAssistant
|
||||
bool APIConnection::send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) {
|
||||
if (this->remove_)
|
||||
return false;
|
||||
if (!this->helper_->can_write_without_blocking())
|
||||
return false;
|
||||
|
||||
std::vector<uint8_t> header;
|
||||
header.push_back(0x00);
|
||||
ProtoVarInt(buffer.get_buffer()->size()).encode(header);
|
||||
ProtoVarInt(message_type).encode(header);
|
||||
|
||||
size_t needed_space = buffer.get_buffer()->size() + header.size();
|
||||
|
||||
if (needed_space > this->client_->space()) {
|
||||
delay(0);
|
||||
if (needed_space > this->client_->space()) {
|
||||
// SubscribeLogsResponse
|
||||
if (message_type != 29) {
|
||||
ESP_LOGV(TAG, "Cannot send message because of TCP buffer space");
|
||||
}
|
||||
delay(0);
|
||||
return false;
|
||||
}
|
||||
APIError err = this->helper_->write_packet(
|
||||
message_type,
|
||||
buffer.get_buffer()->data(),
|
||||
buffer.get_buffer()->size()
|
||||
);
|
||||
if (err == APIError::WOULD_BLOCK)
|
||||
return false;
|
||||
if (err != APIError::OK) {
|
||||
on_fatal_error();
|
||||
ESP_LOGW(TAG, "%s: Packet write failed %d errno=%d", client_info_.c_str(), (int) err, errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->client_->add(reinterpret_cast<char *>(header.data()), header.size(),
|
||||
ASYNC_WRITE_FLAG_COPY | ASYNC_WRITE_FLAG_MORE);
|
||||
this->client_->add(reinterpret_cast<char *>(buffer.get_buffer()->data()), buffer.get_buffer()->size(),
|
||||
ASYNC_WRITE_FLAG_COPY);
|
||||
bool ret = this->client_->send();
|
||||
return ret;
|
||||
this->last_traffic_ = millis();
|
||||
return true;
|
||||
}
|
||||
void APIConnection::on_unauthenticated_access() {
|
||||
ESP_LOGD(TAG, "'%s' tried to access without authentication.", this->client_info_.c_str());
|
||||
this->on_fatal_error();
|
||||
ESP_LOGD(TAG, "'%s' tried to access without authentication.", this->client_info_.c_str());
|
||||
}
|
||||
void APIConnection::on_no_setup_connection() {
|
||||
ESP_LOGD(TAG, "'%s' tried to access without full connection.", this->client_info_.c_str());
|
||||
this->on_fatal_error();
|
||||
ESP_LOGD(TAG, "'%s' tried to access without full connection.", this->client_info_.c_str());
|
||||
}
|
||||
void APIConnection::on_fatal_error() {
|
||||
ESP_LOGV(TAG, "Error: Disconnecting %s", this->client_info_.c_str());
|
||||
this->client_->close();
|
||||
this->helper_->close();
|
||||
this->remove_ = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,16 +5,18 @@
|
||||
#include "api_pb2.h"
|
||||
#include "api_pb2_service.h"
|
||||
#include "api_server.h"
|
||||
#include "api_frame_helper.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
class APIConnection : public APIServerConnection {
|
||||
public:
|
||||
APIConnection(AsyncClient *client, APIServer *parent);
|
||||
virtual ~APIConnection();
|
||||
APIConnection(std::unique_ptr<socket::Socket> socket, APIServer *parent);
|
||||
virtual ~APIConnection() = default;
|
||||
|
||||
void disconnect_client();
|
||||
void start();
|
||||
void force_disconnect_client();
|
||||
void loop();
|
||||
|
||||
bool send_list_info_done() {
|
||||
@@ -62,6 +64,16 @@ class APIConnection : public APIServerConnection {
|
||||
bool send_climate_state(climate::Climate *climate);
|
||||
bool send_climate_info(climate::Climate *climate);
|
||||
void climate_command(const ClimateCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool send_number_state(number::Number *number, float state);
|
||||
bool send_number_info(number::Number *number);
|
||||
void number_command(const NumberCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool send_select_state(select::Select *select, std::string state);
|
||||
bool send_select_info(select::Select *select);
|
||||
void select_command(const SelectCommandRequest &msg) override;
|
||||
#endif
|
||||
bool send_log_message(int level, const char *tag, const char *line);
|
||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||
@@ -77,8 +89,8 @@ class APIConnection : public APIServerConnection {
|
||||
#endif
|
||||
|
||||
void on_disconnect_response(const DisconnectResponse &value) override {
|
||||
// we initiated disconnect_client
|
||||
this->next_close_ = true;
|
||||
this->helper_->close();
|
||||
this->remove_ = true;
|
||||
}
|
||||
void on_ping_response(const PingResponse &value) override {
|
||||
// we initiated ping
|
||||
@@ -92,6 +104,8 @@ class APIConnection : public APIServerConnection {
|
||||
ConnectResponse connect(const ConnectRequest &msg) override;
|
||||
DisconnectResponse disconnect(const DisconnectRequest &msg) override {
|
||||
// remote initiated disconnect_client
|
||||
// don't close yet, we still need to send the disconnect response
|
||||
// close will happen on next loop
|
||||
this->next_close_ = true;
|
||||
DisconnectResponse resp;
|
||||
return resp;
|
||||
@@ -125,19 +139,16 @@ class APIConnection : public APIServerConnection {
|
||||
void on_unauthenticated_access() override;
|
||||
void on_no_setup_connection() override;
|
||||
ProtoWriteBuffer create_buffer() override {
|
||||
this->send_buffer_.clear();
|
||||
return {&this->send_buffer_};
|
||||
// FIXME: ensure no recursive writes can happen
|
||||
this->proto_write_buffer_.clear();
|
||||
return {&this->proto_write_buffer_};
|
||||
}
|
||||
bool send_buffer(ProtoWriteBuffer buffer, uint32_t message_type) override;
|
||||
|
||||
protected:
|
||||
friend APIServer;
|
||||
|
||||
void on_error_(int8_t error);
|
||||
void on_disconnect_();
|
||||
void on_timeout_(uint32_t time);
|
||||
void on_data_(uint8_t *buf, size_t len);
|
||||
void parse_recv_buffer_();
|
||||
bool send_(const void *buf, size_t len, bool force);
|
||||
|
||||
enum class ConnectionState {
|
||||
WAITING_FOR_HELLO,
|
||||
@@ -147,8 +158,10 @@ class APIConnection : public APIServerConnection {
|
||||
|
||||
bool remove_{false};
|
||||
|
||||
std::vector<uint8_t> send_buffer_;
|
||||
std::vector<uint8_t> recv_buffer_;
|
||||
// Buffer used to encode proto messages
|
||||
// Re-use to prevent allocations
|
||||
std::vector<uint8_t> proto_write_buffer_;
|
||||
std::unique_ptr<APIFrameHelper> helper_;
|
||||
|
||||
std::string client_info_;
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
@@ -160,9 +173,7 @@ class APIConnection : public APIServerConnection {
|
||||
uint32_t last_traffic_;
|
||||
bool sent_ping_{false};
|
||||
bool service_call_subscription_{false};
|
||||
bool current_nodelay_{false};
|
||||
bool next_close_{false};
|
||||
AsyncClient *client_;
|
||||
bool next_close_ = false;
|
||||
APIServer *parent_;
|
||||
InitialStateIterator initial_state_iterator_;
|
||||
ListEntitiesIterator list_entities_iterator_;
|
||||
|
||||
907
esphome/components/api/api_frame_helper.cpp
Normal file
907
esphome/components/api/api_frame_helper.cpp
Normal file
@@ -0,0 +1,907 @@
|
||||
#include "api_frame_helper.h"
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "proto.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
static const char *const TAG = "api.socket";
|
||||
|
||||
/// Is the given return value (from read/write syscalls) a wouldblock error?
|
||||
bool is_would_block(ssize_t ret) {
|
||||
if (ret == -1) {
|
||||
return errno == EWOULDBLOCK || errno == EAGAIN;
|
||||
}
|
||||
return ret == 0;
|
||||
}
|
||||
|
||||
#define HELPER_LOG(msg, ...) ESP_LOGVV(TAG, "%s: " msg, info_.c_str(), ##__VA_ARGS__)
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
static const char *const PROLOGUE_INIT = "NoiseAPIInit";
|
||||
|
||||
/// Convert a noise error code to a readable error
|
||||
std::string noise_err_to_str(int err) {
|
||||
if (err == NOISE_ERROR_NO_MEMORY)
|
||||
return "NO_MEMORY";
|
||||
if (err == NOISE_ERROR_UNKNOWN_ID)
|
||||
return "UNKNOWN_ID";
|
||||
if (err == NOISE_ERROR_UNKNOWN_NAME)
|
||||
return "UNKNOWN_NAME";
|
||||
if (err == NOISE_ERROR_MAC_FAILURE)
|
||||
return "MAC_FAILURE";
|
||||
if (err == NOISE_ERROR_NOT_APPLICABLE)
|
||||
return "NOT_APPLICABLE";
|
||||
if (err == NOISE_ERROR_SYSTEM)
|
||||
return "SYSTEM";
|
||||
if (err == NOISE_ERROR_REMOTE_KEY_REQUIRED)
|
||||
return "REMOTE_KEY_REQUIRED";
|
||||
if (err == NOISE_ERROR_LOCAL_KEY_REQUIRED)
|
||||
return "LOCAL_KEY_REQUIRED";
|
||||
if (err == NOISE_ERROR_PSK_REQUIRED)
|
||||
return "PSK_REQUIRED";
|
||||
if (err == NOISE_ERROR_INVALID_LENGTH)
|
||||
return "INVALID_LENGTH";
|
||||
if (err == NOISE_ERROR_INVALID_PARAM)
|
||||
return "INVALID_PARAM";
|
||||
if (err == NOISE_ERROR_INVALID_STATE)
|
||||
return "INVALID_STATE";
|
||||
if (err == NOISE_ERROR_INVALID_NONCE)
|
||||
return "INVALID_NONCE";
|
||||
if (err == NOISE_ERROR_INVALID_PRIVATE_KEY)
|
||||
return "INVALID_PRIVATE_KEY";
|
||||
if (err == NOISE_ERROR_INVALID_PUBLIC_KEY)
|
||||
return "INVALID_PUBLIC_KEY";
|
||||
if (err == NOISE_ERROR_INVALID_FORMAT)
|
||||
return "INVALID_FORMAT";
|
||||
if (err == NOISE_ERROR_INVALID_SIGNATURE)
|
||||
return "INVALID_SIGNATURE";
|
||||
return to_string(err);
|
||||
}
|
||||
|
||||
/// Initialize the frame helper, returns OK if successful.
|
||||
APIError APINoiseFrameHelper::init() {
|
||||
if (state_ != State::INITIALIZE || socket_ == nullptr) {
|
||||
HELPER_LOG("Bad state for init %d", (int) state_);
|
||||
return APIError::BAD_STATE;
|
||||
}
|
||||
int err = socket_->setblocking(false);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
|
||||
return APIError::TCP_NONBLOCKING_FAILED;
|
||||
}
|
||||
int enable = 1;
|
||||
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Setting nodelay failed with errno %d", errno);
|
||||
return APIError::TCP_NODELAY_FAILED;
|
||||
}
|
||||
|
||||
// init prologue
|
||||
prologue_.insert(prologue_.end(), PROLOGUE_INIT, PROLOGUE_INIT + strlen(PROLOGUE_INIT));
|
||||
|
||||
state_ = State::CLIENT_HELLO;
|
||||
return APIError::OK;
|
||||
}
|
||||
/// Run through handshake messages (if in that phase)
|
||||
APIError APINoiseFrameHelper::loop() {
|
||||
APIError err = state_action_();
|
||||
if (err == APIError::WOULD_BLOCK)
|
||||
return APIError::OK;
|
||||
if (err != APIError::OK)
|
||||
return err;
|
||||
if (!tx_buf_.empty()) {
|
||||
err = try_send_tx_buf_();
|
||||
if (err != APIError::OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return APIError::OK;
|
||||
}
|
||||
|
||||
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
||||
*
|
||||
* @param frame: The struct to hold the frame information in.
|
||||
* msg_start: points to the start of the payload - this pointer is only valid until the next
|
||||
* try_receive_raw_ call
|
||||
*
|
||||
* @return 0 if a full packet is in rx_buf_
|
||||
* @return -1 if error, check errno.
|
||||
*
|
||||
* errno EWOULDBLOCK: Packet could not be read without blocking. Try again later.
|
||||
* errno ENOMEM: Not enough memory for reading packet.
|
||||
* errno API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
||||
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
|
||||
*/
|
||||
APIError APINoiseFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
||||
int err;
|
||||
APIError aerr;
|
||||
|
||||
if (frame == nullptr) {
|
||||
HELPER_LOG("Bad argument for try_read_frame_");
|
||||
return APIError::BAD_ARG;
|
||||
}
|
||||
|
||||
// read header
|
||||
if (rx_header_buf_len_ < 3) {
|
||||
// no header information yet
|
||||
size_t to_read = 3 - rx_header_buf_len_;
|
||||
ssize_t received = socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
|
||||
if (is_would_block(received)) {
|
||||
return APIError::WOULD_BLOCK;
|
||||
} else if (received == -1) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Socket read failed with errno %d", errno);
|
||||
return APIError::SOCKET_READ_FAILED;
|
||||
}
|
||||
rx_header_buf_len_ += received;
|
||||
if (received != to_read) {
|
||||
// not a full read
|
||||
return APIError::WOULD_BLOCK;
|
||||
}
|
||||
|
||||
// header reading done
|
||||
}
|
||||
|
||||
// read body
|
||||
uint8_t indicator = rx_header_buf_[0];
|
||||
if (indicator != 0x01) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Bad indicator byte %u", indicator);
|
||||
return APIError::BAD_INDICATOR;
|
||||
}
|
||||
|
||||
uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
|
||||
|
||||
if (state_ != State::DATA && msg_size > 128) {
|
||||
// for handshake message only permit up to 128 byte
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Bad packet len for handshake: %d", msg_size);
|
||||
return APIError::BAD_HANDSHAKE_PACKET_LEN;
|
||||
}
|
||||
|
||||
// reserve space for body
|
||||
if (rx_buf_.size() != msg_size) {
|
||||
rx_buf_.resize(msg_size);
|
||||
}
|
||||
|
||||
if (rx_buf_len_ < msg_size) {
|
||||
// more data to read
|
||||
size_t to_read = msg_size - rx_buf_len_;
|
||||
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
||||
if (is_would_block(received)) {
|
||||
return APIError::WOULD_BLOCK;
|
||||
} else if (received == -1) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Socket read failed with errno %d", errno);
|
||||
return APIError::SOCKET_READ_FAILED;
|
||||
}
|
||||
rx_buf_len_ += received;
|
||||
if (received != to_read) {
|
||||
// not all read
|
||||
return APIError::WOULD_BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
// uncomment for even more debugging
|
||||
// ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str());
|
||||
frame->msg = std::move(rx_buf_);
|
||||
// consume msg
|
||||
rx_buf_ = {};
|
||||
rx_buf_len_ = 0;
|
||||
rx_header_buf_len_ = 0;
|
||||
return APIError::OK;
|
||||
}
|
||||
|
||||
/** To be called from read/write methods.
|
||||
*
|
||||
* This method runs through the internal handshake methods, if in that state.
|
||||
*
|
||||
* If the handshake is still active when this method returns and a read/write can't take place at
|
||||
* the moment, returns WOULD_BLOCK.
|
||||
* If an error occured, returns that error. Only returns OK if the transport is ready for data
|
||||
* traffic.
|
||||
*/
|
||||
APIError APINoiseFrameHelper::state_action_() {
|
||||
int err;
|
||||
APIError aerr;
|
||||
if (state_ == State::INITIALIZE) {
|
||||
HELPER_LOG("Bad state for method: %d", (int) state_);
|
||||
return APIError::BAD_STATE;
|
||||
}
|
||||
if (state_ == State::CLIENT_HELLO) {
|
||||
// waiting for client hello
|
||||
ParsedFrame frame;
|
||||
aerr = try_read_frame_(&frame);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
// ignore contents, may be used in future for flags
|
||||
prologue_.push_back((uint8_t) (frame.msg.size() >> 8));
|
||||
prologue_.push_back((uint8_t) frame.msg.size());
|
||||
prologue_.insert(prologue_.end(), frame.msg.begin(), frame.msg.end());
|
||||
|
||||
state_ = State::SERVER_HELLO;
|
||||
}
|
||||
if (state_ == State::SERVER_HELLO) {
|
||||
// send server hello
|
||||
uint8_t msg[1];
|
||||
msg[0] = 0x01; // chosen proto
|
||||
aerr = write_frame_(msg, 1);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
|
||||
// start handshake
|
||||
aerr = init_handshake_();
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
|
||||
state_ = State::HANDSHAKE;
|
||||
}
|
||||
if (state_ == State::HANDSHAKE) {
|
||||
int action = noise_handshakestate_get_action(handshake_);
|
||||
if (action == NOISE_ACTION_READ_MESSAGE) {
|
||||
// waiting for handshake msg
|
||||
ParsedFrame frame;
|
||||
aerr = try_read_frame_(&frame);
|
||||
if (aerr == APIError::BAD_INDICATOR) {
|
||||
send_explicit_handshake_reject_("Bad indicator byte");
|
||||
return aerr;
|
||||
}
|
||||
if (frame.msg.size() < 1 || frame.msg[0] != 0x00) {
|
||||
aerr = APIError::BAD_HANDSHAKE_PACKET_LEN;
|
||||
}
|
||||
if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
|
||||
send_explicit_handshake_reject_("Bad handshake packet len");
|
||||
return aerr;
|
||||
}
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
|
||||
NoiseBuffer mbuf;
|
||||
noise_buffer_init(mbuf);
|
||||
noise_buffer_set_input(mbuf, frame.msg.data() + 1, frame.msg.size() - 1);
|
||||
err = noise_handshakestate_read_message(handshake_, &mbuf, nullptr);
|
||||
if (err != 0) {
|
||||
// TODO: explicit rejection
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_read_message failed: %s", noise_err_to_str(err).c_str());
|
||||
if (err == NOISE_ERROR_MAC_FAILURE) {
|
||||
send_explicit_handshake_reject_("Handshake MAC failure");
|
||||
} else {
|
||||
send_explicit_handshake_reject_("Handshake error");
|
||||
}
|
||||
return APIError::HANDSHAKESTATE_READ_FAILED;
|
||||
}
|
||||
|
||||
aerr = check_handshake_finished_();
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
} else if (action == NOISE_ACTION_WRITE_MESSAGE) {
|
||||
uint8_t buffer[65];
|
||||
NoiseBuffer mbuf;
|
||||
noise_buffer_init(mbuf);
|
||||
noise_buffer_set_output(mbuf, buffer + 1, sizeof(buffer) - 1);
|
||||
|
||||
err = noise_handshakestate_write_message(handshake_, &mbuf, nullptr);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_write_message failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::HANDSHAKESTATE_WRITE_FAILED;
|
||||
}
|
||||
buffer[0] = 0x00; // success
|
||||
|
||||
aerr = write_frame_(buffer, mbuf.size + 1);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
aerr = check_handshake_finished_();
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
} else {
|
||||
// bad state for action
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Bad action for handshake: %d", action);
|
||||
return APIError::HANDSHAKESTATE_BAD_STATE;
|
||||
}
|
||||
}
|
||||
if (state_ == State::CLOSED || state_ == State::FAILED) {
|
||||
return APIError::BAD_STATE;
|
||||
}
|
||||
return APIError::OK;
|
||||
}
|
||||
void APINoiseFrameHelper::send_explicit_handshake_reject_(const std::string &reason) {
|
||||
std::vector<uint8_t> data;
|
||||
data.reserve(reason.size() + 1);
|
||||
data[0] = 0x01; // failure
|
||||
for (size_t i = 0; i < reason.size(); i++) {
|
||||
data[i+1] = (uint8_t) reason[i];
|
||||
}
|
||||
write_frame_(data.data(), data.size());
|
||||
}
|
||||
|
||||
APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
||||
int err;
|
||||
APIError aerr;
|
||||
aerr = state_action_();
|
||||
if (aerr != APIError::OK) {
|
||||
return aerr;
|
||||
}
|
||||
|
||||
if (state_ != State::DATA) {
|
||||
return APIError::WOULD_BLOCK;
|
||||
}
|
||||
|
||||
ParsedFrame frame;
|
||||
aerr = try_read_frame_(&frame);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
|
||||
NoiseBuffer mbuf;
|
||||
noise_buffer_init(mbuf);
|
||||
noise_buffer_set_inout(mbuf, frame.msg.data(), frame.msg.size(), frame.msg.size());
|
||||
err = noise_cipherstate_decrypt(recv_cipher_, &mbuf);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_cipherstate_decrypt failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::CIPHERSTATE_DECRYPT_FAILED;
|
||||
}
|
||||
|
||||
size_t msg_size = mbuf.size;
|
||||
uint8_t *msg_data = frame.msg.data();
|
||||
if (msg_size < 4) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Bad data packet: size %d too short", msg_size);
|
||||
return APIError::BAD_DATA_PACKET;
|
||||
}
|
||||
|
||||
// uint16_t type;
|
||||
// uint16_t data_len;
|
||||
// uint8_t *data;
|
||||
// uint8_t *padding; zero or more bytes to fill up the rest of the packet
|
||||
uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
|
||||
uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
|
||||
if (data_len > msg_size - 4) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Bad data packet: data_len %u greater than msg_size %u", data_len, msg_size);
|
||||
return APIError::BAD_DATA_PACKET;
|
||||
}
|
||||
|
||||
buffer->container = std::move(frame.msg);
|
||||
buffer->data_offset = 4;
|
||||
buffer->data_len = data_len;
|
||||
buffer->type = type;
|
||||
return APIError::OK;
|
||||
}
|
||||
bool APINoiseFrameHelper::can_write_without_blocking() {
|
||||
return state_ == State::DATA && tx_buf_.empty();
|
||||
}
|
||||
APIError APINoiseFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
|
||||
int err;
|
||||
APIError aerr;
|
||||
aerr = state_action_();
|
||||
if (aerr != APIError::OK) {
|
||||
return aerr;
|
||||
}
|
||||
|
||||
if (state_ != State::DATA) {
|
||||
return APIError::WOULD_BLOCK;
|
||||
}
|
||||
|
||||
size_t padding = 0;
|
||||
size_t msg_len = 4 + payload_len + padding;
|
||||
size_t frame_len = 3 + msg_len + noise_cipherstate_get_mac_length(send_cipher_);
|
||||
auto tmpbuf = std::unique_ptr<uint8_t[]>{new (std::nothrow) uint8_t[frame_len]};
|
||||
if (tmpbuf == nullptr) {
|
||||
HELPER_LOG("Could not allocate for writing packet");
|
||||
return APIError::OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
tmpbuf[0] = 0x01; // indicator
|
||||
// tmpbuf[1], tmpbuf[2] to be set later
|
||||
const uint8_t msg_offset = 3;
|
||||
const uint8_t payload_offset = msg_offset + 4;
|
||||
tmpbuf[msg_offset + 0] = (uint8_t) (type >> 8); // type
|
||||
tmpbuf[msg_offset + 1] = (uint8_t) type;
|
||||
tmpbuf[msg_offset + 2] = (uint8_t) (payload_len >> 8); // data_len
|
||||
tmpbuf[msg_offset + 3] = (uint8_t) payload_len;
|
||||
// copy data
|
||||
std::copy(payload, payload + payload_len, &tmpbuf[payload_offset]);
|
||||
// fill padding with zeros
|
||||
std::fill(&tmpbuf[payload_offset + payload_len], &tmpbuf[frame_len], 0);
|
||||
|
||||
NoiseBuffer mbuf;
|
||||
noise_buffer_init(mbuf);
|
||||
noise_buffer_set_inout(mbuf, &tmpbuf[msg_offset], msg_len, frame_len - msg_offset);
|
||||
err = noise_cipherstate_encrypt(send_cipher_, &mbuf);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_cipherstate_encrypt failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::CIPHERSTATE_ENCRYPT_FAILED;
|
||||
}
|
||||
|
||||
size_t total_len = 3 + mbuf.size;
|
||||
tmpbuf[1] = (uint8_t) (mbuf.size >> 8);
|
||||
tmpbuf[2] = (uint8_t) mbuf.size;
|
||||
// write raw to not have two packets sent if NAGLE disabled
|
||||
aerr = write_raw_(&tmpbuf[0], total_len);
|
||||
if (aerr != APIError::OK) {
|
||||
return aerr;
|
||||
}
|
||||
return APIError::OK;
|
||||
}
|
||||
APIError APINoiseFrameHelper::try_send_tx_buf_() {
|
||||
// try send from tx_buf
|
||||
while (state_ != State::CLOSED && !tx_buf_.empty()) {
|
||||
ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size());
|
||||
if (sent == -1) {
|
||||
if (errno == EWOULDBLOCK || errno == EAGAIN)
|
||||
break;
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Socket write failed with errno %d", errno);
|
||||
return APIError::SOCKET_WRITE_FAILED;
|
||||
} else if (sent == 0) {
|
||||
break;
|
||||
}
|
||||
// TODO: inefficient if multiple packets in txbuf
|
||||
// replace with deque of buffers
|
||||
tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
|
||||
}
|
||||
|
||||
return APIError::OK;
|
||||
}
|
||||
/** Write the data to the socket, or buffer it a write would block
|
||||
*
|
||||
* @param data The data to write
|
||||
* @param len The length of data
|
||||
*/
|
||||
APIError APINoiseFrameHelper::write_raw_(const uint8_t *data, size_t len) {
|
||||
if (len == 0)
|
||||
return APIError::OK;
|
||||
int err;
|
||||
APIError aerr;
|
||||
|
||||
// uncomment for even more debugging
|
||||
// ESP_LOGVV(TAG, "Sending raw: %s", hexencode(data, len).c_str());
|
||||
|
||||
if (!tx_buf_.empty()) {
|
||||
// try to empty tx_buf_ first
|
||||
aerr = try_send_tx_buf_();
|
||||
if (aerr != APIError::OK && aerr != APIError::WOULD_BLOCK)
|
||||
return aerr;
|
||||
}
|
||||
|
||||
if (!tx_buf_.empty()) {
|
||||
// tx buf not empty, can't write now because then stream would be inconsistent
|
||||
tx_buf_.insert(tx_buf_.end(), data, data + len);
|
||||
return APIError::OK;
|
||||
}
|
||||
|
||||
ssize_t sent = socket_->write(data, len);
|
||||
if (is_would_block(sent)) {
|
||||
// operation would block, add buffer to tx_buf
|
||||
tx_buf_.insert(tx_buf_.end(), data, data + len);
|
||||
return APIError::OK;
|
||||
} else if (sent == -1) {
|
||||
// an error occured
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Socket write failed with errno %d", errno);
|
||||
return APIError::SOCKET_WRITE_FAILED;
|
||||
} else if (sent != len) {
|
||||
// partially sent, add end to tx_buf
|
||||
tx_buf_.insert(tx_buf_.end(), data + sent, data + len);
|
||||
return APIError::OK;
|
||||
}
|
||||
// fully sent
|
||||
return APIError::OK;
|
||||
}
|
||||
APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, size_t len) {
|
||||
APIError aerr;
|
||||
|
||||
uint8_t header[3];
|
||||
header[0] = 0x01; // indicator
|
||||
header[1] = (uint8_t) (len >> 8);
|
||||
header[2] = (uint8_t) len;
|
||||
|
||||
aerr = write_raw_(header, 3);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
aerr = write_raw_(data, len);
|
||||
return aerr;
|
||||
}
|
||||
|
||||
/** Initiate the data structures for the handshake.
|
||||
*
|
||||
* @return 0 on success, -1 on error (check errno)
|
||||
*/
|
||||
APIError APINoiseFrameHelper::init_handshake_() {
|
||||
int err;
|
||||
memset(&nid_, 0, sizeof(nid_));
|
||||
// const char *proto = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
|
||||
// err = noise_protocol_name_to_id(&nid_, proto, strlen(proto));
|
||||
nid_.pattern_id = NOISE_PATTERN_NN;
|
||||
nid_.cipher_id = NOISE_CIPHER_CHACHAPOLY;
|
||||
nid_.dh_id = NOISE_DH_CURVE25519;
|
||||
nid_.prefix_id = NOISE_PREFIX_STANDARD;
|
||||
nid_.hybrid_id = NOISE_DH_NONE;
|
||||
nid_.hash_id = NOISE_HASH_SHA256;
|
||||
nid_.modifier_ids[0] = NOISE_MODIFIER_PSK0;
|
||||
|
||||
err = noise_handshakestate_new_by_id(&handshake_, &nid_, NOISE_ROLE_RESPONDER);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_new_by_id failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::HANDSHAKESTATE_SETUP_FAILED;
|
||||
}
|
||||
|
||||
const auto &psk = ctx_->get_psk();
|
||||
err = noise_handshakestate_set_pre_shared_key(handshake_, psk.data(), psk.size());
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_set_pre_shared_key failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::HANDSHAKESTATE_SETUP_FAILED;
|
||||
}
|
||||
|
||||
err = noise_handshakestate_set_prologue(handshake_, prologue_.data(), prologue_.size());
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_set_prologue failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::HANDSHAKESTATE_SETUP_FAILED;
|
||||
}
|
||||
// set_prologue copies it into handshakestate, so we can get rid of it now
|
||||
prologue_ = {};
|
||||
|
||||
err = noise_handshakestate_start(handshake_);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_start failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::HANDSHAKESTATE_SETUP_FAILED;
|
||||
}
|
||||
return APIError::OK;
|
||||
}
|
||||
|
||||
APIError APINoiseFrameHelper::check_handshake_finished_() {
|
||||
assert(state_ == State::HANDSHAKE);
|
||||
|
||||
int action = noise_handshakestate_get_action(handshake_);
|
||||
if (action == NOISE_ACTION_READ_MESSAGE || action == NOISE_ACTION_WRITE_MESSAGE)
|
||||
return APIError::OK;
|
||||
if (action != NOISE_ACTION_SPLIT) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Bad action for handshake: %d", action);
|
||||
return APIError::HANDSHAKESTATE_BAD_STATE;
|
||||
}
|
||||
int err = noise_handshakestate_split(handshake_, &send_cipher_, &recv_cipher_);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("noise_handshakestate_split failed: %s", noise_err_to_str(err).c_str());
|
||||
return APIError::HANDSHAKESTATE_SPLIT_FAILED;
|
||||
}
|
||||
|
||||
HELPER_LOG("Handshake complete!");
|
||||
noise_handshakestate_free(handshake_);
|
||||
handshake_ = nullptr;
|
||||
state_ = State::DATA;
|
||||
return APIError::OK;
|
||||
}
|
||||
|
||||
APINoiseFrameHelper::~APINoiseFrameHelper() {
|
||||
if (handshake_ != nullptr) {
|
||||
noise_handshakestate_free(handshake_);
|
||||
handshake_ = nullptr;
|
||||
}
|
||||
if (send_cipher_ != nullptr) {
|
||||
noise_cipherstate_free(send_cipher_);
|
||||
send_cipher_ = nullptr;
|
||||
}
|
||||
if (recv_cipher_ != nullptr) {
|
||||
noise_cipherstate_free(recv_cipher_);
|
||||
recv_cipher_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
APIError APINoiseFrameHelper::close() {
|
||||
state_ = State::CLOSED;
|
||||
int err = socket_->close();
|
||||
if (err == -1)
|
||||
return APIError::CLOSE_FAILED;
|
||||
return APIError::OK;
|
||||
}
|
||||
APIError APINoiseFrameHelper::shutdown(int how) {
|
||||
int err = socket_->shutdown(how);
|
||||
if (err == -1)
|
||||
return APIError::SHUTDOWN_FAILED;
|
||||
if (how == SHUT_RDWR) {
|
||||
state_ = State::CLOSED;
|
||||
}
|
||||
return APIError::OK;
|
||||
}
|
||||
extern "C" {
|
||||
// declare how noise generates random bytes (here with a good HWRNG based on the RF system)
|
||||
void noise_rand_bytes(void *output, size_t len) {
|
||||
esphome::fill_random(reinterpret_cast<uint8_t *>(output), len);
|
||||
}
|
||||
}
|
||||
#endif // USE_API_NOISE
|
||||
|
||||
|
||||
#ifdef USE_API_PLAINTEXT
|
||||
|
||||
/// Initialize the frame helper, returns OK if successful.
|
||||
APIError APIPlaintextFrameHelper::init() {
|
||||
if (state_ != State::INITIALIZE || socket_ == nullptr) {
|
||||
HELPER_LOG("Bad state for init %d", (int) state_);
|
||||
return APIError::BAD_STATE;
|
||||
}
|
||||
int err = socket_->setblocking(false);
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Setting nonblocking failed with errno %d", errno);
|
||||
return APIError::TCP_NONBLOCKING_FAILED;
|
||||
}
|
||||
int enable = 1;
|
||||
err = socket_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(int));
|
||||
if (err != 0) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Setting nodelay failed with errno %d", errno);
|
||||
return APIError::TCP_NODELAY_FAILED;
|
||||
}
|
||||
|
||||
state_ = State::DATA;
|
||||
return APIError::OK;
|
||||
}
|
||||
/// Not used for plaintext
|
||||
APIError APIPlaintextFrameHelper::loop() {
|
||||
if (state_ != State::DATA) {
|
||||
return APIError::BAD_STATE;
|
||||
}
|
||||
// try send pending TX data
|
||||
if (!tx_buf_.empty()) {
|
||||
APIError err = try_send_tx_buf_();
|
||||
if (err != APIError::OK) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return APIError::OK;
|
||||
}
|
||||
|
||||
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
||||
*
|
||||
* @param frame: The struct to hold the frame information in.
|
||||
* msg: store the parsed frame in that struct
|
||||
*
|
||||
* @return See APIError
|
||||
*
|
||||
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
||||
*/
|
||||
APIError APIPlaintextFrameHelper::try_read_frame_(ParsedFrame *frame) {
|
||||
int err;
|
||||
APIError aerr;
|
||||
|
||||
if (frame == nullptr) {
|
||||
HELPER_LOG("Bad argument for try_read_frame_");
|
||||
return APIError::BAD_ARG;
|
||||
}
|
||||
|
||||
// read header
|
||||
while (!rx_header_parsed_) {
|
||||
uint8_t data;
|
||||
ssize_t received = socket_->read(&data, 1);
|
||||
if (is_would_block(received)) {
|
||||
return APIError::WOULD_BLOCK;
|
||||
} else if (received == -1) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Socket read failed with errno %d", errno);
|
||||
return APIError::SOCKET_READ_FAILED;
|
||||
}
|
||||
rx_header_buf_.push_back(data);
|
||||
|
||||
// try parse header
|
||||
if (rx_header_buf_[0] != 0x00) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
|
||||
return APIError::BAD_INDICATOR;
|
||||
}
|
||||
|
||||
size_t i = 1;
|
||||
size_t consumed = 0;
|
||||
auto msg_size_varint = ProtoVarInt::parse(&rx_header_buf_[i], rx_header_buf_.size() - i, &consumed);
|
||||
if (!msg_size_varint.has_value()) {
|
||||
// not enough data there yet
|
||||
continue;
|
||||
}
|
||||
|
||||
i += consumed;
|
||||
rx_header_parsed_len_ = msg_size_varint->as_uint32();
|
||||
|
||||
auto msg_type_varint = ProtoVarInt::parse(&rx_header_buf_[i], rx_header_buf_.size() - i, &consumed);
|
||||
if (!msg_type_varint.has_value()) {
|
||||
// not enough data there yet
|
||||
continue;
|
||||
}
|
||||
rx_header_parsed_type_ = msg_type_varint->as_uint32();
|
||||
rx_header_parsed_ = true;
|
||||
}
|
||||
// header reading done
|
||||
|
||||
// reserve space for body
|
||||
if (rx_buf_.size() != rx_header_parsed_len_) {
|
||||
rx_buf_.resize(rx_header_parsed_len_);
|
||||
}
|
||||
|
||||
if (rx_buf_len_ < rx_header_parsed_len_) {
|
||||
// more data to read
|
||||
size_t to_read = rx_header_parsed_len_ - rx_buf_len_;
|
||||
ssize_t received = socket_->read(&rx_buf_[rx_buf_len_], to_read);
|
||||
if (is_would_block(received)) {
|
||||
return APIError::WOULD_BLOCK;
|
||||
} else if (received == -1) {
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Socket read failed with errno %d", errno);
|
||||
return APIError::SOCKET_READ_FAILED;
|
||||
}
|
||||
rx_buf_len_ += received;
|
||||
if (received != to_read) {
|
||||
// not all read
|
||||
return APIError::WOULD_BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
// uncomment for even more debugging
|
||||
// ESP_LOGVV(TAG, "Received frame: %s", hexencode(rx_buf_).c_str());
|
||||
frame->msg = std::move(rx_buf_);
|
||||
// consume msg
|
||||
rx_buf_ = {};
|
||||
rx_buf_len_ = 0;
|
||||
rx_header_buf_.clear();
|
||||
rx_header_parsed_ = false;
|
||||
return APIError::OK;
|
||||
}
|
||||
|
||||
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
||||
int err;
|
||||
APIError aerr;
|
||||
|
||||
if (state_ != State::DATA) {
|
||||
return APIError::WOULD_BLOCK;
|
||||
}
|
||||
|
||||
ParsedFrame frame;
|
||||
aerr = try_read_frame_(&frame);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
|
||||
buffer->container = std::move(frame.msg);
|
||||
buffer->data_offset = 0;
|
||||
buffer->data_len = rx_header_parsed_len_;
|
||||
buffer->type = rx_header_parsed_type_;
|
||||
return APIError::OK;
|
||||
}
|
||||
bool APIPlaintextFrameHelper::can_write_without_blocking() {
|
||||
return state_ == State::DATA && tx_buf_.empty();
|
||||
}
|
||||
APIError APIPlaintextFrameHelper::write_packet(uint16_t type, const uint8_t *payload, size_t payload_len) {
|
||||
int err;
|
||||
APIError aerr;
|
||||
|
||||
if (state_ != State::DATA) {
|
||||
return APIError::BAD_STATE;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> header;
|
||||
header.push_back(0x00);
|
||||
ProtoVarInt(payload_len).encode(header);
|
||||
ProtoVarInt(type).encode(header);
|
||||
|
||||
aerr = write_raw_(&header[0], header.size());
|
||||
if (aerr != APIError::OK) {
|
||||
return aerr;
|
||||
}
|
||||
aerr = write_raw_(payload, payload_len);
|
||||
if (aerr != APIError::OK) {
|
||||
return aerr;
|
||||
}
|
||||
return APIError::OK;
|
||||
}
|
||||
APIError APIPlaintextFrameHelper::try_send_tx_buf_() {
|
||||
// try send from tx_buf
|
||||
while (state_ != State::CLOSED && !tx_buf_.empty()) {
|
||||
ssize_t sent = socket_->write(tx_buf_.data(), tx_buf_.size());
|
||||
if (sent == -1) {
|
||||
if (errno == EWOULDBLOCK || errno == EAGAIN)
|
||||
break;
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Socket write failed with errno %d", errno);
|
||||
return APIError::SOCKET_WRITE_FAILED;
|
||||
} else if (sent == 0) {
|
||||
break;
|
||||
}
|
||||
// TODO: inefficient if multiple packets in txbuf
|
||||
// replace with deque of buffers
|
||||
tx_buf_.erase(tx_buf_.begin(), tx_buf_.begin() + sent);
|
||||
}
|
||||
|
||||
return APIError::OK;
|
||||
}
|
||||
/** Write the data to the socket, or buffer it a write would block
|
||||
*
|
||||
* @param data The data to write
|
||||
* @param len The length of data
|
||||
*/
|
||||
APIError APIPlaintextFrameHelper::write_raw_(const uint8_t *data, size_t len) {
|
||||
if (len == 0)
|
||||
return APIError::OK;
|
||||
int err;
|
||||
APIError aerr;
|
||||
|
||||
// uncomment for even more debugging
|
||||
// ESP_LOGVV(TAG, "Sending raw: %s", hexencode(data, len).c_str());
|
||||
|
||||
if (!tx_buf_.empty()) {
|
||||
// try to empty tx_buf_ first
|
||||
aerr = try_send_tx_buf_();
|
||||
if (aerr != APIError::OK && aerr != APIError::WOULD_BLOCK)
|
||||
return aerr;
|
||||
}
|
||||
|
||||
if (!tx_buf_.empty()) {
|
||||
// tx buf not empty, can't write now because then stream would be inconsistent
|
||||
tx_buf_.insert(tx_buf_.end(), data, data + len);
|
||||
return APIError::OK;
|
||||
}
|
||||
|
||||
ssize_t sent = socket_->write(data, len);
|
||||
if (is_would_block(sent)) {
|
||||
// operation would block, add buffer to tx_buf
|
||||
tx_buf_.insert(tx_buf_.end(), data, data + len);
|
||||
return APIError::OK;
|
||||
} else if (sent == -1) {
|
||||
// an error occured
|
||||
state_ = State::FAILED;
|
||||
HELPER_LOG("Socket write failed with errno %d", errno);
|
||||
return APIError::SOCKET_WRITE_FAILED;
|
||||
} else if (sent != len) {
|
||||
// partially sent, add end to tx_buf
|
||||
tx_buf_.insert(tx_buf_.end(), data + sent, data + len);
|
||||
return APIError::OK;
|
||||
}
|
||||
// fully sent
|
||||
return APIError::OK;
|
||||
}
|
||||
APIError APIPlaintextFrameHelper::write_frame_(const uint8_t *data, size_t len) {
|
||||
APIError aerr;
|
||||
|
||||
uint8_t header[3];
|
||||
header[0] = 0x01; // indicator
|
||||
header[1] = (uint8_t) (len >> 8);
|
||||
header[2] = (uint8_t) len;
|
||||
|
||||
aerr = write_raw_(header, 3);
|
||||
if (aerr != APIError::OK)
|
||||
return aerr;
|
||||
aerr = write_raw_(data, len);
|
||||
return aerr;
|
||||
}
|
||||
|
||||
APIError APIPlaintextFrameHelper::close() {
|
||||
state_ = State::CLOSED;
|
||||
int err = socket_->close();
|
||||
if (err == -1)
|
||||
return APIError::CLOSE_FAILED;
|
||||
return APIError::OK;
|
||||
}
|
||||
APIError APIPlaintextFrameHelper::shutdown(int how) {
|
||||
int err = socket_->shutdown(how);
|
||||
if (err == -1)
|
||||
return APIError::SHUTDOWN_FAILED;
|
||||
if (how == SHUT_RDWR) {
|
||||
state_ = State::CLOSED;
|
||||
}
|
||||
return APIError::OK;
|
||||
}
|
||||
#endif // USE_API_PLAINTEXT
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
186
esphome/components/api/api_frame_helper.h
Normal file
186
esphome/components/api/api_frame_helper.h
Normal file
@@ -0,0 +1,186 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
#include "noise/protocol.h"
|
||||
#endif
|
||||
|
||||
#include "esphome/components/socket/socket.h"
|
||||
#include "api_noise_context.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
struct ReadPacketBuffer {
|
||||
std::vector<uint8_t> container;
|
||||
uint16_t type;
|
||||
size_t data_offset;
|
||||
size_t data_len;
|
||||
};
|
||||
|
||||
struct PacketBuffer {
|
||||
const std::vector<uint8_t> container;
|
||||
uint16_t type;
|
||||
uint8_t data_offset;
|
||||
uint8_t data_len;
|
||||
};
|
||||
|
||||
enum class APIError : int {
|
||||
OK = 0,
|
||||
WOULD_BLOCK = 1001,
|
||||
BAD_HANDSHAKE_PACKET_LEN = 1002,
|
||||
BAD_INDICATOR = 1003,
|
||||
BAD_DATA_PACKET = 1004,
|
||||
TCP_NODELAY_FAILED = 1005,
|
||||
TCP_NONBLOCKING_FAILED = 1006,
|
||||
CLOSE_FAILED = 1007,
|
||||
SHUTDOWN_FAILED = 1008,
|
||||
BAD_STATE = 1009,
|
||||
BAD_ARG = 1010,
|
||||
SOCKET_READ_FAILED = 1011,
|
||||
SOCKET_WRITE_FAILED = 1012,
|
||||
HANDSHAKESTATE_READ_FAILED = 1013,
|
||||
HANDSHAKESTATE_WRITE_FAILED = 1014,
|
||||
HANDSHAKESTATE_BAD_STATE = 1015,
|
||||
CIPHERSTATE_DECRYPT_FAILED = 1016,
|
||||
CIPHERSTATE_ENCRYPT_FAILED = 1017,
|
||||
OUT_OF_MEMORY = 1018,
|
||||
HANDSHAKESTATE_SETUP_FAILED = 1019,
|
||||
HANDSHAKESTATE_SPLIT_FAILED = 1020,
|
||||
};
|
||||
|
||||
class APIFrameHelper {
|
||||
public:
|
||||
virtual APIError init() = 0;
|
||||
virtual APIError loop() = 0;
|
||||
virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
|
||||
virtual bool can_write_without_blocking() = 0;
|
||||
virtual APIError write_packet(uint16_t type, const uint8_t *data, size_t len) = 0;
|
||||
virtual std::string getpeername() = 0;
|
||||
virtual APIError close() = 0;
|
||||
virtual APIError shutdown(int how) = 0;
|
||||
// Give this helper a name for logging
|
||||
virtual void set_log_info(std::string info) = 0;
|
||||
};
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
class APINoiseFrameHelper : public APIFrameHelper {
|
||||
public:
|
||||
APINoiseFrameHelper(std::unique_ptr<socket::Socket> socket, std::shared_ptr<APINoiseContext> ctx) : socket_(std::move(socket)), ctx_(ctx) {}
|
||||
~APINoiseFrameHelper();
|
||||
APIError init() override;
|
||||
APIError loop() override;
|
||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||
bool can_write_without_blocking() override;
|
||||
APIError write_packet(uint16_t type, const uint8_t *data, size_t len) override;
|
||||
std::string getpeername() override{
|
||||
return socket_->getpeername();
|
||||
}
|
||||
APIError close() override;
|
||||
APIError shutdown(int how) override;
|
||||
// Give this helper a name for logging
|
||||
void set_log_info(std::string info) override {
|
||||
info_ = std::move(info);
|
||||
}
|
||||
|
||||
protected:
|
||||
struct ParsedFrame {
|
||||
std::vector<uint8_t> msg;
|
||||
};
|
||||
|
||||
APIError state_action_();
|
||||
APIError try_read_frame_(ParsedFrame *frame);
|
||||
APIError try_send_tx_buf_();
|
||||
APIError write_frame_(const uint8_t *data, size_t len);
|
||||
APIError write_raw_(const uint8_t *data, size_t len);
|
||||
APIError init_handshake_();
|
||||
APIError check_handshake_finished_();
|
||||
void send_explicit_handshake_reject_(const std::string &reason);
|
||||
|
||||
std::unique_ptr<socket::Socket> socket_;
|
||||
|
||||
std::string info_;
|
||||
uint8_t rx_header_buf_[3];
|
||||
size_t rx_header_buf_len_ = 0;
|
||||
std::vector<uint8_t> rx_buf_;
|
||||
size_t rx_buf_len_ = 0;
|
||||
|
||||
std::vector<uint8_t> tx_buf_;
|
||||
std::vector<uint8_t> prologue_;
|
||||
|
||||
std::shared_ptr<APINoiseContext> ctx_;
|
||||
NoiseHandshakeState *handshake_ = nullptr;
|
||||
NoiseCipherState *send_cipher_ = nullptr;
|
||||
NoiseCipherState *recv_cipher_ = nullptr;
|
||||
NoiseProtocolId nid_;
|
||||
|
||||
enum class State {
|
||||
INITIALIZE = 1,
|
||||
CLIENT_HELLO = 2,
|
||||
SERVER_HELLO = 3,
|
||||
HANDSHAKE = 4,
|
||||
DATA = 5,
|
||||
CLOSED = 6,
|
||||
FAILED = 7,
|
||||
} state_ = State::INITIALIZE;
|
||||
};
|
||||
#endif // USE_API_NOISE
|
||||
|
||||
#ifdef USE_API_PLAINTEXT
|
||||
class APIPlaintextFrameHelper : public APIFrameHelper {
|
||||
public:
|
||||
APIPlaintextFrameHelper(std::unique_ptr<socket::Socket> socket) : socket_(std::move(socket)) {}
|
||||
~APIPlaintextFrameHelper() = default;
|
||||
APIError init() override;
|
||||
APIError loop() override;
|
||||
APIError read_packet(ReadPacketBuffer *buffer) override;
|
||||
bool can_write_without_blocking() override;
|
||||
APIError write_packet(uint16_t type, const uint8_t *data, size_t len) override;
|
||||
std::string getpeername() override {
|
||||
return socket_->getpeername();
|
||||
}
|
||||
APIError close() override;
|
||||
APIError shutdown(int how) override;
|
||||
// Give this helper a name for logging
|
||||
void set_log_info(std::string info) override {
|
||||
info_ = std::move(info);
|
||||
}
|
||||
|
||||
protected:
|
||||
struct ParsedFrame {
|
||||
std::vector<uint8_t> msg;
|
||||
};
|
||||
|
||||
APIError try_read_frame_(ParsedFrame *frame);
|
||||
APIError try_send_tx_buf_();
|
||||
APIError write_frame_(const uint8_t *data, size_t len);
|
||||
APIError write_raw_(const uint8_t *data, size_t len);
|
||||
|
||||
std::unique_ptr<socket::Socket> socket_;
|
||||
|
||||
std::string info_;
|
||||
std::vector<uint8_t> rx_header_buf_;
|
||||
bool rx_header_parsed_ = false;
|
||||
uint32_t rx_header_parsed_type_ = 0;
|
||||
uint32_t rx_header_parsed_len_ = 0;
|
||||
|
||||
std::vector<uint8_t> rx_buf_;
|
||||
size_t rx_buf_len_ = 0;
|
||||
|
||||
std::vector<uint8_t> tx_buf_;
|
||||
|
||||
enum class State {
|
||||
INITIALIZE = 1,
|
||||
DATA = 2,
|
||||
CLOSED = 3,
|
||||
FAILED = 4,
|
||||
} state_ = State::INITIALIZE;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
27
esphome/components/api/api_noise_context.h
Normal file
27
esphome/components/api/api_noise_context.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include "esphome/core/defines.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
using psk_t = std::array<uint8_t, 32>;
|
||||
|
||||
class APINoiseContext {
|
||||
public:
|
||||
void set_psk(psk_t psk) {
|
||||
psk_ = std::move(psk);
|
||||
}
|
||||
const psk_t &get_psk() const {
|
||||
return psk_;
|
||||
}
|
||||
|
||||
protected:
|
||||
psk_t psk_;
|
||||
};
|
||||
#endif // USE_API_NOISE
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
@@ -62,6 +62,54 @@ template<> const char *proto_enum_to_string<enums::FanDirection>(enums::FanDirec
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<enums::ColorMode>(enums::ColorMode value) {
|
||||
switch (value) {
|
||||
case enums::COLOR_MODE_UNKNOWN:
|
||||
return "COLOR_MODE_UNKNOWN";
|
||||
case enums::COLOR_MODE_ON_OFF:
|
||||
return "COLOR_MODE_ON_OFF";
|
||||
case enums::COLOR_MODE_BRIGHTNESS:
|
||||
return "COLOR_MODE_BRIGHTNESS";
|
||||
case enums::COLOR_MODE_WHITE:
|
||||
return "COLOR_MODE_WHITE";
|
||||
case enums::COLOR_MODE_COLOR_TEMPERATURE:
|
||||
return "COLOR_MODE_COLOR_TEMPERATURE";
|
||||
case enums::COLOR_MODE_COLD_WARM_WHITE:
|
||||
return "COLOR_MODE_COLD_WARM_WHITE";
|
||||
case enums::COLOR_MODE_RGB:
|
||||
return "COLOR_MODE_RGB";
|
||||
case enums::COLOR_MODE_RGB_WHITE:
|
||||
return "COLOR_MODE_RGB_WHITE";
|
||||
case enums::COLOR_MODE_RGB_COLOR_TEMPERATURE:
|
||||
return "COLOR_MODE_RGB_COLOR_TEMPERATURE";
|
||||
case enums::COLOR_MODE_RGB_COLD_WARM_WHITE:
|
||||
return "COLOR_MODE_RGB_COLD_WARM_WHITE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<enums::SensorStateClass>(enums::SensorStateClass value) {
|
||||
switch (value) {
|
||||
case enums::STATE_CLASS_NONE:
|
||||
return "STATE_CLASS_NONE";
|
||||
case enums::STATE_CLASS_MEASUREMENT:
|
||||
return "STATE_CLASS_MEASUREMENT";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<enums::SensorLastResetType>(enums::SensorLastResetType value) {
|
||||
switch (value) {
|
||||
case enums::LAST_RESET_NONE:
|
||||
return "LAST_RESET_NONE";
|
||||
case enums::LAST_RESET_NEVER:
|
||||
return "LAST_RESET_NEVER";
|
||||
case enums::LAST_RESET_AUTO:
|
||||
return "LAST_RESET_AUTO";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<enums::LogLevel>(enums::LogLevel value) {
|
||||
switch (value) {
|
||||
case enums::LOG_LEVEL_NONE:
|
||||
@@ -108,8 +156,8 @@ template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMo
|
||||
switch (value) {
|
||||
case enums::CLIMATE_MODE_OFF:
|
||||
return "CLIMATE_MODE_OFF";
|
||||
case enums::CLIMATE_MODE_AUTO:
|
||||
return "CLIMATE_MODE_AUTO";
|
||||
case enums::CLIMATE_MODE_HEAT_COOL:
|
||||
return "CLIMATE_MODE_HEAT_COOL";
|
||||
case enums::CLIMATE_MODE_COOL:
|
||||
return "CLIMATE_MODE_COOL";
|
||||
case enums::CLIMATE_MODE_HEAT:
|
||||
@@ -118,6 +166,8 @@ template<> const char *proto_enum_to_string<enums::ClimateMode>(enums::ClimateMo
|
||||
return "CLIMATE_MODE_FAN_ONLY";
|
||||
case enums::CLIMATE_MODE_DRY:
|
||||
return "CLIMATE_MODE_DRY";
|
||||
case enums::CLIMATE_MODE_AUTO:
|
||||
return "CLIMATE_MODE_AUTO";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@@ -178,6 +228,28 @@ template<> const char *proto_enum_to_string<enums::ClimateAction>(enums::Climate
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
template<> const char *proto_enum_to_string<enums::ClimatePreset>(enums::ClimatePreset value) {
|
||||
switch (value) {
|
||||
case enums::CLIMATE_PRESET_NONE:
|
||||
return "CLIMATE_PRESET_NONE";
|
||||
case enums::CLIMATE_PRESET_HOME:
|
||||
return "CLIMATE_PRESET_HOME";
|
||||
case enums::CLIMATE_PRESET_AWAY:
|
||||
return "CLIMATE_PRESET_AWAY";
|
||||
case enums::CLIMATE_PRESET_BOOST:
|
||||
return "CLIMATE_PRESET_BOOST";
|
||||
case enums::CLIMATE_PRESET_COMFORT:
|
||||
return "CLIMATE_PRESET_COMFORT";
|
||||
case enums::CLIMATE_PRESET_ECO:
|
||||
return "CLIMATE_PRESET_ECO";
|
||||
case enums::CLIMATE_PRESET_SLEEP:
|
||||
return "CLIMATE_PRESET_SLEEP";
|
||||
case enums::CLIMATE_PRESET_ACTIVITY:
|
||||
return "CLIMATE_PRESET_ACTIVITY";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -189,6 +261,7 @@ bool HelloRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value)
|
||||
}
|
||||
}
|
||||
void HelloRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->client_info); }
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void HelloRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("HelloRequest {\n");
|
||||
@@ -197,6 +270,7 @@ void HelloRequest::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool HelloResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -226,6 +300,7 @@ void HelloResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_uint32(2, this->api_version_minor);
|
||||
buffer.encode_string(3, this->server_info);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void HelloResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("HelloResponse {\n");
|
||||
@@ -244,6 +319,7 @@ void HelloResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ConnectRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -255,6 +331,7 @@ bool ConnectRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value
|
||||
}
|
||||
}
|
||||
void ConnectRequest::encode(ProtoWriteBuffer buffer) const { buffer.encode_string(1, this->password); }
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ConnectRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ConnectRequest {\n");
|
||||
@@ -263,6 +340,7 @@ void ConnectRequest::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ConnectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -274,6 +352,7 @@ bool ConnectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
}
|
||||
}
|
||||
void ConnectResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_bool(1, this->invalid_password); }
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ConnectResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ConnectResponse {\n");
|
||||
@@ -282,16 +361,27 @@ void ConnectResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
void DisconnectRequest::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void DisconnectRequest::dump_to(std::string &out) const { out.append("DisconnectRequest {}"); }
|
||||
#endif
|
||||
void DisconnectResponse::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void DisconnectResponse::dump_to(std::string &out) const { out.append("DisconnectResponse {}"); }
|
||||
#endif
|
||||
void PingRequest::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void PingRequest::dump_to(std::string &out) const { out.append("PingRequest {}"); }
|
||||
#endif
|
||||
void PingResponse::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void PingResponse::dump_to(std::string &out) const { out.append("PingResponse {}"); }
|
||||
#endif
|
||||
void DeviceInfoRequest::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void DeviceInfoRequest::dump_to(std::string &out) const { out.append("DeviceInfoRequest {}"); }
|
||||
#endif
|
||||
bool DeviceInfoResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -328,6 +418,14 @@ bool DeviceInfoResponse::decode_length(uint32_t field_id, ProtoLengthDelimited v
|
||||
this->model = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 8: {
|
||||
this->project_name = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 9: {
|
||||
this->project_version = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -340,7 +438,10 @@ void DeviceInfoResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(5, this->compilation_time);
|
||||
buffer.encode_string(6, this->model);
|
||||
buffer.encode_bool(7, this->has_deep_sleep);
|
||||
buffer.encode_string(8, this->project_name);
|
||||
buffer.encode_string(9, this->project_version);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void DeviceInfoResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("DeviceInfoResponse {\n");
|
||||
@@ -371,20 +472,39 @@ void DeviceInfoResponse::dump_to(std::string &out) const {
|
||||
out.append(" has_deep_sleep: ");
|
||||
out.append(YESNO(this->has_deep_sleep));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" project_name: ");
|
||||
out.append("'").append(this->project_name).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" project_version: ");
|
||||
out.append("'").append(this->project_version).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
void ListEntitiesRequest::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesRequest::dump_to(std::string &out) const { out.append("ListEntitiesRequest {}"); }
|
||||
#endif
|
||||
void ListEntitiesDoneResponse::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesDoneResponse::dump_to(std::string &out) const { out.append("ListEntitiesDoneResponse {}"); }
|
||||
#endif
|
||||
void SubscribeStatesRequest::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SubscribeStatesRequest::dump_to(std::string &out) const { out.append("SubscribeStatesRequest {}"); }
|
||||
#endif
|
||||
bool ListEntitiesBinarySensorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 6: {
|
||||
this->is_status_binary_sensor = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 7: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -428,7 +548,9 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(4, this->unique_id);
|
||||
buffer.encode_string(5, this->device_class);
|
||||
buffer.encode_bool(6, this->is_status_binary_sensor);
|
||||
buffer.encode_bool(7, this->disabled_by_default);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesBinarySensorResponse {\n");
|
||||
@@ -456,8 +578,13 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
|
||||
out.append(" is_status_binary_sensor: ");
|
||||
out.append(YESNO(this->is_status_binary_sensor));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool BinarySensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -487,6 +614,7 @@ void BinarySensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void BinarySensorStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("BinarySensorStateResponse {\n");
|
||||
@@ -504,6 +632,7 @@ void BinarySensorStateResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 5: {
|
||||
@@ -518,6 +647,10 @@ bool ListEntitiesCoverResponse::decode_varint(uint32_t field_id, ProtoVarInt val
|
||||
this->supports_tilt = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 9: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -563,7 +696,9 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(6, this->supports_position);
|
||||
buffer.encode_bool(7, this->supports_tilt);
|
||||
buffer.encode_string(8, this->device_class);
|
||||
buffer.encode_bool(9, this->disabled_by_default);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesCoverResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesCoverResponse {\n");
|
||||
@@ -599,8 +734,13 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
|
||||
out.append(" device_class: ");
|
||||
out.append("'").append(this->device_class).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool CoverStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -640,6 +780,7 @@ void CoverStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_float(4, this->tilt);
|
||||
buffer.encode_enum<enums::CoverOperation>(5, this->current_operation);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void CoverStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("CoverStateResponse {\n");
|
||||
@@ -667,6 +808,7 @@ void CoverStateResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool CoverCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -721,6 +863,7 @@ void CoverCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_float(7, this->tilt);
|
||||
buffer.encode_bool(8, this->stop);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void CoverCommandRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("CoverCommandRequest {\n");
|
||||
@@ -760,6 +903,7 @@ void CoverCommandRequest::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 5: {
|
||||
@@ -778,6 +922,10 @@ bool ListEntitiesFanResponse::decode_varint(uint32_t field_id, ProtoVarInt value
|
||||
this->supported_speed_count = value.as_int32();
|
||||
return true;
|
||||
}
|
||||
case 9: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -819,7 +967,9 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(6, this->supports_speed);
|
||||
buffer.encode_bool(7, this->supports_direction);
|
||||
buffer.encode_int32(8, this->supported_speed_count);
|
||||
buffer.encode_bool(9, this->disabled_by_default);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesFanResponse {\n");
|
||||
@@ -856,8 +1006,13 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
|
||||
sprintf(buffer, "%d", this->supported_speed_count);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool FanStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -902,6 +1057,7 @@ void FanStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_enum<enums::FanDirection>(5, this->direction);
|
||||
buffer.encode_int32(6, this->speed_level);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void FanStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("FanStateResponse {\n");
|
||||
@@ -932,6 +1088,7 @@ void FanStateResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool FanCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -1001,6 +1158,7 @@ void FanCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(10, this->has_speed_level);
|
||||
buffer.encode_int32(11, this->speed_level);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void FanCommandRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("FanCommandRequest {\n");
|
||||
@@ -1051,22 +1209,31 @@ void FanCommandRequest::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesLightResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 12: {
|
||||
this->supported_color_modes.push_back(value.as_enum<enums::ColorMode>());
|
||||
return true;
|
||||
}
|
||||
case 5: {
|
||||
this->supports_brightness = value.as_bool();
|
||||
this->legacy_supports_brightness = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 6: {
|
||||
this->supports_rgb = value.as_bool();
|
||||
this->legacy_supports_rgb = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 7: {
|
||||
this->supports_white_value = value.as_bool();
|
||||
this->legacy_supports_white_value = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 8: {
|
||||
this->supports_color_temperature = value.as_bool();
|
||||
this->legacy_supports_color_temperature = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 13: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
@@ -1118,16 +1285,21 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
buffer.encode_string(4, this->unique_id);
|
||||
buffer.encode_bool(5, this->supports_brightness);
|
||||
buffer.encode_bool(6, this->supports_rgb);
|
||||
buffer.encode_bool(7, this->supports_white_value);
|
||||
buffer.encode_bool(8, this->supports_color_temperature);
|
||||
for (auto &it : this->supported_color_modes) {
|
||||
buffer.encode_enum<enums::ColorMode>(12, it, true);
|
||||
}
|
||||
buffer.encode_bool(5, this->legacy_supports_brightness);
|
||||
buffer.encode_bool(6, this->legacy_supports_rgb);
|
||||
buffer.encode_bool(7, this->legacy_supports_white_value);
|
||||
buffer.encode_bool(8, this->legacy_supports_color_temperature);
|
||||
buffer.encode_float(9, this->min_mireds);
|
||||
buffer.encode_float(10, this->max_mireds);
|
||||
for (auto &it : this->effects) {
|
||||
buffer.encode_string(11, it, true);
|
||||
}
|
||||
buffer.encode_bool(13, this->disabled_by_default);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesLightResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesLightResponse {\n");
|
||||
@@ -1148,20 +1320,26 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
|
||||
out.append("'").append(this->unique_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" supports_brightness: ");
|
||||
out.append(YESNO(this->supports_brightness));
|
||||
for (const auto &it : this->supported_color_modes) {
|
||||
out.append(" supported_color_modes: ");
|
||||
out.append(proto_enum_to_string<enums::ColorMode>(it));
|
||||
out.append("\n");
|
||||
}
|
||||
|
||||
out.append(" legacy_supports_brightness: ");
|
||||
out.append(YESNO(this->legacy_supports_brightness));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" supports_rgb: ");
|
||||
out.append(YESNO(this->supports_rgb));
|
||||
out.append(" legacy_supports_rgb: ");
|
||||
out.append(YESNO(this->legacy_supports_rgb));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" supports_white_value: ");
|
||||
out.append(YESNO(this->supports_white_value));
|
||||
out.append(" legacy_supports_white_value: ");
|
||||
out.append(YESNO(this->legacy_supports_white_value));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" supports_color_temperature: ");
|
||||
out.append(YESNO(this->supports_color_temperature));
|
||||
out.append(" legacy_supports_color_temperature: ");
|
||||
out.append(YESNO(this->legacy_supports_color_temperature));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" min_mireds: ");
|
||||
@@ -1179,14 +1357,23 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
|
||||
out.append("'").append(it).append("'");
|
||||
out.append("\n");
|
||||
}
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool LightStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->state = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 11: {
|
||||
this->color_mode = value.as_enum<enums::ColorMode>();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -1211,6 +1398,10 @@ bool LightStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
this->brightness = value.as_float();
|
||||
return true;
|
||||
}
|
||||
case 10: {
|
||||
this->color_brightness = value.as_float();
|
||||
return true;
|
||||
}
|
||||
case 4: {
|
||||
this->red = value.as_float();
|
||||
return true;
|
||||
@@ -1231,6 +1422,14 @@ bool LightStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
this->color_temperature = value.as_float();
|
||||
return true;
|
||||
}
|
||||
case 12: {
|
||||
this->cold_white = value.as_float();
|
||||
return true;
|
||||
}
|
||||
case 13: {
|
||||
this->warm_white = value.as_float();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -1239,13 +1438,18 @@ void LightStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->state);
|
||||
buffer.encode_float(3, this->brightness);
|
||||
buffer.encode_enum<enums::ColorMode>(11, this->color_mode);
|
||||
buffer.encode_float(10, this->color_brightness);
|
||||
buffer.encode_float(4, this->red);
|
||||
buffer.encode_float(5, this->green);
|
||||
buffer.encode_float(6, this->blue);
|
||||
buffer.encode_float(7, this->white);
|
||||
buffer.encode_float(8, this->color_temperature);
|
||||
buffer.encode_float(12, this->cold_white);
|
||||
buffer.encode_float(13, this->warm_white);
|
||||
buffer.encode_string(9, this->effect);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void LightStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("LightStateResponse {\n");
|
||||
@@ -1263,6 +1467,15 @@ void LightStateResponse::dump_to(std::string &out) const {
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" color_mode: ");
|
||||
out.append(proto_enum_to_string<enums::ColorMode>(this->color_mode));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" color_brightness: ");
|
||||
sprintf(buffer, "%g", this->color_brightness);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" red: ");
|
||||
sprintf(buffer, "%g", this->red);
|
||||
out.append(buffer);
|
||||
@@ -1288,11 +1501,22 @@ void LightStateResponse::dump_to(std::string &out) const {
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" cold_white: ");
|
||||
sprintf(buffer, "%g", this->cold_white);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" warm_white: ");
|
||||
sprintf(buffer, "%g", this->warm_white);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" effect: ");
|
||||
out.append("'").append(this->effect).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -1307,6 +1531,18 @@ bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
this->has_brightness = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 22: {
|
||||
this->has_color_mode = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 23: {
|
||||
this->color_mode = value.as_enum<enums::ColorMode>();
|
||||
return true;
|
||||
}
|
||||
case 20: {
|
||||
this->has_color_brightness = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 6: {
|
||||
this->has_rgb = value.as_bool();
|
||||
return true;
|
||||
@@ -1319,6 +1555,14 @@ bool LightCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
this->has_color_temperature = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 24: {
|
||||
this->has_cold_white = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 26: {
|
||||
this->has_warm_white = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 14: {
|
||||
this->has_transition_length = value.as_bool();
|
||||
return true;
|
||||
@@ -1363,6 +1607,10 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
this->brightness = value.as_float();
|
||||
return true;
|
||||
}
|
||||
case 21: {
|
||||
this->color_brightness = value.as_float();
|
||||
return true;
|
||||
}
|
||||
case 7: {
|
||||
this->red = value.as_float();
|
||||
return true;
|
||||
@@ -1383,6 +1631,14 @@ bool LightCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
this->color_temperature = value.as_float();
|
||||
return true;
|
||||
}
|
||||
case 25: {
|
||||
this->cold_white = value.as_float();
|
||||
return true;
|
||||
}
|
||||
case 27: {
|
||||
this->warm_white = value.as_float();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -1393,6 +1649,10 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(3, this->state);
|
||||
buffer.encode_bool(4, this->has_brightness);
|
||||
buffer.encode_float(5, this->brightness);
|
||||
buffer.encode_bool(22, this->has_color_mode);
|
||||
buffer.encode_enum<enums::ColorMode>(23, this->color_mode);
|
||||
buffer.encode_bool(20, this->has_color_brightness);
|
||||
buffer.encode_float(21, this->color_brightness);
|
||||
buffer.encode_bool(6, this->has_rgb);
|
||||
buffer.encode_float(7, this->red);
|
||||
buffer.encode_float(8, this->green);
|
||||
@@ -1401,6 +1661,10 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_float(11, this->white);
|
||||
buffer.encode_bool(12, this->has_color_temperature);
|
||||
buffer.encode_float(13, this->color_temperature);
|
||||
buffer.encode_bool(24, this->has_cold_white);
|
||||
buffer.encode_float(25, this->cold_white);
|
||||
buffer.encode_bool(26, this->has_warm_white);
|
||||
buffer.encode_float(27, this->warm_white);
|
||||
buffer.encode_bool(14, this->has_transition_length);
|
||||
buffer.encode_uint32(15, this->transition_length);
|
||||
buffer.encode_bool(16, this->has_flash_length);
|
||||
@@ -1408,6 +1672,7 @@ void LightCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(18, this->has_effect);
|
||||
buffer.encode_string(19, this->effect);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void LightCommandRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("LightCommandRequest {\n");
|
||||
@@ -1433,6 +1698,23 @@ void LightCommandRequest::dump_to(std::string &out) const {
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_color_mode: ");
|
||||
out.append(YESNO(this->has_color_mode));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" color_mode: ");
|
||||
out.append(proto_enum_to_string<enums::ColorMode>(this->color_mode));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_color_brightness: ");
|
||||
out.append(YESNO(this->has_color_brightness));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" color_brightness: ");
|
||||
sprintf(buffer, "%g", this->color_brightness);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_rgb: ");
|
||||
out.append(YESNO(this->has_rgb));
|
||||
out.append("\n");
|
||||
@@ -1470,6 +1752,24 @@ void LightCommandRequest::dump_to(std::string &out) const {
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_cold_white: ");
|
||||
out.append(YESNO(this->has_cold_white));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" cold_white: ");
|
||||
sprintf(buffer, "%g", this->cold_white);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_warm_white: ");
|
||||
out.append(YESNO(this->has_warm_white));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" warm_white: ");
|
||||
sprintf(buffer, "%g", this->warm_white);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_transition_length: ");
|
||||
out.append(YESNO(this->has_transition_length));
|
||||
out.append("\n");
|
||||
@@ -1497,6 +1797,7 @@ void LightCommandRequest::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 7: {
|
||||
@@ -1507,6 +1808,18 @@ bool ListEntitiesSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt va
|
||||
this->force_update = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 10: {
|
||||
this->state_class = value.as_enum<enums::SensorStateClass>();
|
||||
return true;
|
||||
}
|
||||
case 11: {
|
||||
this->last_reset_type = value.as_enum<enums::SensorLastResetType>();
|
||||
return true;
|
||||
}
|
||||
case 12: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -1561,7 +1874,11 @@ void ListEntitiesSensorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_int32(7, this->accuracy_decimals);
|
||||
buffer.encode_bool(8, this->force_update);
|
||||
buffer.encode_string(9, this->device_class);
|
||||
buffer.encode_enum<enums::SensorStateClass>(10, this->state_class);
|
||||
buffer.encode_enum<enums::SensorLastResetType>(11, this->last_reset_type);
|
||||
buffer.encode_bool(12, this->disabled_by_default);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesSensorResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesSensorResponse {\n");
|
||||
@@ -1602,8 +1919,21 @@ void ListEntitiesSensorResponse::dump_to(std::string &out) const {
|
||||
out.append(" device_class: ");
|
||||
out.append("'").append(this->device_class).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" state_class: ");
|
||||
out.append(proto_enum_to_string<enums::SensorStateClass>(this->state_class));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" last_reset_type: ");
|
||||
out.append(proto_enum_to_string<enums::SensorLastResetType>(this->last_reset_type));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool SensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 3: {
|
||||
@@ -1633,6 +1963,7 @@ void SensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_float(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SensorStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("SensorStateResponse {\n");
|
||||
@@ -1651,12 +1982,17 @@ void SensorStateResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesSwitchResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 6: {
|
||||
this->assumed_state = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 7: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -1700,7 +2036,9 @@ void ListEntitiesSwitchResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(4, this->unique_id);
|
||||
buffer.encode_string(5, this->icon);
|
||||
buffer.encode_bool(6, this->assumed_state);
|
||||
buffer.encode_bool(7, this->disabled_by_default);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesSwitchResponse {\n");
|
||||
@@ -1728,8 +2066,13 @@ void ListEntitiesSwitchResponse::dump_to(std::string &out) const {
|
||||
out.append(" assumed_state: ");
|
||||
out.append(YESNO(this->assumed_state));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool SwitchStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -1754,6 +2097,7 @@ void SwitchStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->state);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SwitchStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("SwitchStateResponse {\n");
|
||||
@@ -1767,6 +2111,7 @@ void SwitchStateResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool SwitchCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -1791,6 +2136,7 @@ void SwitchCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_bool(2, this->state);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SwitchCommandRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("SwitchCommandRequest {\n");
|
||||
@@ -1804,6 +2150,17 @@ void SwitchCommandRequest::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesTextSensorResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 6: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ListEntitiesTextSensorResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -1842,7 +2199,9 @@ void ListEntitiesTextSensorResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(3, this->name);
|
||||
buffer.encode_string(4, this->unique_id);
|
||||
buffer.encode_string(5, this->icon);
|
||||
buffer.encode_bool(6, this->disabled_by_default);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesTextSensorResponse {\n");
|
||||
@@ -1866,8 +2225,13 @@ void ListEntitiesTextSensorResponse::dump_to(std::string &out) const {
|
||||
out.append(" icon: ");
|
||||
out.append("'").append(this->icon).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool TextSensorStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 3: {
|
||||
@@ -1903,6 +2267,7 @@ void TextSensorStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void TextSensorStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("TextSensorStateResponse {\n");
|
||||
@@ -1920,6 +2285,7 @@ void TextSensorStateResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool SubscribeLogsRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -1938,6 +2304,7 @@ void SubscribeLogsRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_enum<enums::LogLevel>(1, this->level);
|
||||
buffer.encode_bool(2, this->dump_config);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SubscribeLogsRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("SubscribeLogsRequest {\n");
|
||||
@@ -1950,6 +2317,7 @@ void SubscribeLogsRequest::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool SubscribeLogsResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -1984,6 +2352,7 @@ void SubscribeLogsResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(3, this->message);
|
||||
buffer.encode_bool(4, this->send_failed);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SubscribeLogsResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("SubscribeLogsResponse {\n");
|
||||
@@ -2004,10 +2373,13 @@ void SubscribeLogsResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
void SubscribeHomeassistantServicesRequest::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SubscribeHomeassistantServicesRequest::dump_to(std::string &out) const {
|
||||
out.append("SubscribeHomeassistantServicesRequest {}");
|
||||
}
|
||||
#endif
|
||||
bool HomeassistantServiceMap::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -2026,6 +2398,7 @@ void HomeassistantServiceMap::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->key);
|
||||
buffer.encode_string(2, this->value);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void HomeassistantServiceMap::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("HomeassistantServiceMap {\n");
|
||||
@@ -2038,6 +2411,7 @@ void HomeassistantServiceMap::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool HomeassistantServiceResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 5: {
|
||||
@@ -2083,6 +2457,7 @@ void HomeassistantServiceResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
}
|
||||
buffer.encode_bool(5, this->is_event);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void HomeassistantServiceResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("HomeassistantServiceResponse {\n");
|
||||
@@ -2113,31 +2488,45 @@ void HomeassistantServiceResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
void SubscribeHomeAssistantStatesRequest::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SubscribeHomeAssistantStatesRequest::dump_to(std::string &out) const {
|
||||
out.append("SubscribeHomeAssistantStatesRequest {}");
|
||||
}
|
||||
#endif
|
||||
bool SubscribeHomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->entity_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 2: {
|
||||
this->attribute = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void SubscribeHomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->entity_id);
|
||||
buffer.encode_string(2, this->attribute);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SubscribeHomeAssistantStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("SubscribeHomeAssistantStateResponse {\n");
|
||||
out.append(" entity_id: ");
|
||||
out.append("'").append(this->entity_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" attribute: ");
|
||||
out.append("'").append(this->attribute).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -2148,6 +2537,10 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel
|
||||
this->state = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->attribute = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2155,7 +2548,9 @@ bool HomeAssistantStateResponse::decode_length(uint32_t field_id, ProtoLengthDel
|
||||
void HomeAssistantStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->entity_id);
|
||||
buffer.encode_string(2, this->state);
|
||||
buffer.encode_string(3, this->attribute);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void HomeAssistantStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("HomeAssistantStateResponse {\n");
|
||||
@@ -2166,10 +2561,17 @@ void HomeAssistantStateResponse::dump_to(std::string &out) const {
|
||||
out.append(" state: ");
|
||||
out.append("'").append(this->state).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" attribute: ");
|
||||
out.append("'").append(this->attribute).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
void GetTimeRequest::encode(ProtoWriteBuffer buffer) const {}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void GetTimeRequest::dump_to(std::string &out) const { out.append("GetTimeRequest {}"); }
|
||||
#endif
|
||||
bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -2181,6 +2583,7 @@ bool GetTimeResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
}
|
||||
}
|
||||
void GetTimeResponse::encode(ProtoWriteBuffer buffer) const { buffer.encode_fixed32(1, this->epoch_seconds); }
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void GetTimeResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("GetTimeResponse {\n");
|
||||
@@ -2190,6 +2593,7 @@ void GetTimeResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesServicesArgument::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -2214,6 +2618,7 @@ void ListEntitiesServicesArgument::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->name);
|
||||
buffer.encode_enum<enums::ServiceArgType>(2, this->type);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesServicesArgument::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesServicesArgument {\n");
|
||||
@@ -2226,6 +2631,7 @@ void ListEntitiesServicesArgument::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesServicesResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -2257,6 +2663,7 @@ void ListEntitiesServicesResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_message<ListEntitiesServicesArgument>(3, it, true);
|
||||
}
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesServicesResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesServicesResponse {\n");
|
||||
@@ -2276,6 +2683,7 @@ void ListEntitiesServicesResponse::dump_to(std::string &out) const {
|
||||
}
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ExecuteServiceArgument::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -2349,6 +2757,7 @@ void ExecuteServiceArgument::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(9, it, true);
|
||||
}
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ExecuteServiceArgument::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ExecuteServiceArgument {\n");
|
||||
@@ -2402,6 +2811,7 @@ void ExecuteServiceArgument::dump_to(std::string &out) const {
|
||||
}
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ExecuteServiceRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -2428,6 +2838,7 @@ void ExecuteServiceRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_message<ExecuteServiceArgument>(2, it, true);
|
||||
}
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ExecuteServiceRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ExecuteServiceRequest {\n");
|
||||
@@ -2443,6 +2854,17 @@ void ExecuteServiceRequest::dump_to(std::string &out) const {
|
||||
}
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesCameraResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 5: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ListEntitiesCameraResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -2476,7 +2898,9 @@ void ListEntitiesCameraResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
buffer.encode_string(4, this->unique_id);
|
||||
buffer.encode_bool(5, this->disabled_by_default);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesCameraResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesCameraResponse {\n");
|
||||
@@ -2496,8 +2920,13 @@ void ListEntitiesCameraResponse::dump_to(std::string &out) const {
|
||||
out.append(" unique_id: ");
|
||||
out.append("'").append(this->unique_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool CameraImageResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 3: {
|
||||
@@ -2533,6 +2962,7 @@ void CameraImageResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(2, this->data);
|
||||
buffer.encode_bool(3, this->done);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void CameraImageResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("CameraImageResponse {\n");
|
||||
@@ -2550,6 +2980,7 @@ void CameraImageResponse::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool CameraImageRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
@@ -2568,6 +2999,7 @@ void CameraImageRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(1, this->single);
|
||||
buffer.encode_bool(2, this->stream);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void CameraImageRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("CameraImageRequest {\n");
|
||||
@@ -2580,6 +3012,7 @@ void CameraImageRequest::dump_to(std::string &out) const {
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 5: {
|
||||
@@ -2595,7 +3028,7 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
|
||||
return true;
|
||||
}
|
||||
case 11: {
|
||||
this->supports_away = value.as_bool();
|
||||
this->legacy_supports_away = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 12: {
|
||||
@@ -2610,6 +3043,14 @@ bool ListEntitiesClimateResponse::decode_varint(uint32_t field_id, ProtoVarInt v
|
||||
this->supported_swing_modes.push_back(value.as_enum<enums::ClimateSwingMode>());
|
||||
return true;
|
||||
}
|
||||
case 16: {
|
||||
this->supported_presets.push_back(value.as_enum<enums::ClimatePreset>());
|
||||
return true;
|
||||
}
|
||||
case 18: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2628,6 +3069,14 @@ bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDe
|
||||
this->unique_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 15: {
|
||||
this->supported_custom_fan_modes.push_back(value.as_string());
|
||||
return true;
|
||||
}
|
||||
case 17: {
|
||||
this->supported_custom_presets.push_back(value.as_string());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2667,7 +3116,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_float(8, this->visual_min_temperature);
|
||||
buffer.encode_float(9, this->visual_max_temperature);
|
||||
buffer.encode_float(10, this->visual_temperature_step);
|
||||
buffer.encode_bool(11, this->supports_away);
|
||||
buffer.encode_bool(11, this->legacy_supports_away);
|
||||
buffer.encode_bool(12, this->supports_action);
|
||||
for (auto &it : this->supported_fan_modes) {
|
||||
buffer.encode_enum<enums::ClimateFanMode>(13, it, true);
|
||||
@@ -2675,7 +3124,18 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
for (auto &it : this->supported_swing_modes) {
|
||||
buffer.encode_enum<enums::ClimateSwingMode>(14, it, true);
|
||||
}
|
||||
for (auto &it : this->supported_custom_fan_modes) {
|
||||
buffer.encode_string(15, it, true);
|
||||
}
|
||||
for (auto &it : this->supported_presets) {
|
||||
buffer.encode_enum<enums::ClimatePreset>(16, it, true);
|
||||
}
|
||||
for (auto &it : this->supported_custom_presets) {
|
||||
buffer.encode_string(17, it, true);
|
||||
}
|
||||
buffer.encode_bool(18, this->disabled_by_default);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesClimateResponse {\n");
|
||||
@@ -2725,8 +3185,8 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" supports_away: ");
|
||||
out.append(YESNO(this->supports_away));
|
||||
out.append(" legacy_supports_away: ");
|
||||
out.append(YESNO(this->legacy_supports_away));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" supports_action: ");
|
||||
@@ -2744,8 +3204,31 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
|
||||
out.append(proto_enum_to_string<enums::ClimateSwingMode>(it));
|
||||
out.append("\n");
|
||||
}
|
||||
|
||||
for (const auto &it : this->supported_custom_fan_modes) {
|
||||
out.append(" supported_custom_fan_modes: ");
|
||||
out.append("'").append(it).append("'");
|
||||
out.append("\n");
|
||||
}
|
||||
|
||||
for (const auto &it : this->supported_presets) {
|
||||
out.append(" supported_presets: ");
|
||||
out.append(proto_enum_to_string<enums::ClimatePreset>(it));
|
||||
out.append("\n");
|
||||
}
|
||||
|
||||
for (const auto &it : this->supported_custom_presets) {
|
||||
out.append(" supported_custom_presets: ");
|
||||
out.append("'").append(it).append("'");
|
||||
out.append("\n");
|
||||
}
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -2753,7 +3236,7 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
return true;
|
||||
}
|
||||
case 7: {
|
||||
this->away = value.as_bool();
|
||||
this->legacy_away = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 8: {
|
||||
@@ -2768,6 +3251,24 @@ bool ClimateStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
|
||||
return true;
|
||||
}
|
||||
case 12: {
|
||||
this->preset = value.as_enum<enums::ClimatePreset>();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ClimateStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 11: {
|
||||
this->custom_fan_mode = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 13: {
|
||||
this->custom_preset = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2805,11 +3306,15 @@ void ClimateStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_float(4, this->target_temperature);
|
||||
buffer.encode_float(5, this->target_temperature_low);
|
||||
buffer.encode_float(6, this->target_temperature_high);
|
||||
buffer.encode_bool(7, this->away);
|
||||
buffer.encode_bool(7, this->legacy_away);
|
||||
buffer.encode_enum<enums::ClimateAction>(8, this->action);
|
||||
buffer.encode_enum<enums::ClimateFanMode>(9, this->fan_mode);
|
||||
buffer.encode_enum<enums::ClimateSwingMode>(10, this->swing_mode);
|
||||
buffer.encode_string(11, this->custom_fan_mode);
|
||||
buffer.encode_enum<enums::ClimatePreset>(12, this->preset);
|
||||
buffer.encode_string(13, this->custom_preset);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ClimateStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ClimateStateResponse {\n");
|
||||
@@ -2842,8 +3347,8 @@ void ClimateStateResponse::dump_to(std::string &out) const {
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" away: ");
|
||||
out.append(YESNO(this->away));
|
||||
out.append(" legacy_away: ");
|
||||
out.append(YESNO(this->legacy_away));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" action: ");
|
||||
@@ -2857,8 +3362,21 @@ void ClimateStateResponse::dump_to(std::string &out) const {
|
||||
out.append(" swing_mode: ");
|
||||
out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" custom_fan_mode: ");
|
||||
out.append("'").append(this->custom_fan_mode).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" preset: ");
|
||||
out.append(proto_enum_to_string<enums::ClimatePreset>(this->preset));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" custom_preset: ");
|
||||
out.append("'").append(this->custom_preset).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
@@ -2882,11 +3400,11 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
|
||||
return true;
|
||||
}
|
||||
case 10: {
|
||||
this->has_away = value.as_bool();
|
||||
this->has_legacy_away = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 11: {
|
||||
this->away = value.as_bool();
|
||||
this->legacy_away = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 12: {
|
||||
@@ -2905,6 +3423,36 @@ bool ClimateCommandRequest::decode_varint(uint32_t field_id, ProtoVarInt value)
|
||||
this->swing_mode = value.as_enum<enums::ClimateSwingMode>();
|
||||
return true;
|
||||
}
|
||||
case 16: {
|
||||
this->has_custom_fan_mode = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 18: {
|
||||
this->has_preset = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
case 19: {
|
||||
this->preset = value.as_enum<enums::ClimatePreset>();
|
||||
return true;
|
||||
}
|
||||
case 20: {
|
||||
this->has_custom_preset = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ClimateCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 17: {
|
||||
this->custom_fan_mode = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 21: {
|
||||
this->custom_preset = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -2941,13 +3489,20 @@ void ClimateCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_float(7, this->target_temperature_low);
|
||||
buffer.encode_bool(8, this->has_target_temperature_high);
|
||||
buffer.encode_float(9, this->target_temperature_high);
|
||||
buffer.encode_bool(10, this->has_away);
|
||||
buffer.encode_bool(11, this->away);
|
||||
buffer.encode_bool(10, this->has_legacy_away);
|
||||
buffer.encode_bool(11, this->legacy_away);
|
||||
buffer.encode_bool(12, this->has_fan_mode);
|
||||
buffer.encode_enum<enums::ClimateFanMode>(13, this->fan_mode);
|
||||
buffer.encode_bool(14, this->has_swing_mode);
|
||||
buffer.encode_enum<enums::ClimateSwingMode>(15, this->swing_mode);
|
||||
buffer.encode_bool(16, this->has_custom_fan_mode);
|
||||
buffer.encode_string(17, this->custom_fan_mode);
|
||||
buffer.encode_bool(18, this->has_preset);
|
||||
buffer.encode_enum<enums::ClimatePreset>(19, this->preset);
|
||||
buffer.encode_bool(20, this->has_custom_preset);
|
||||
buffer.encode_string(21, this->custom_preset);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ClimateCommandRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ClimateCommandRequest {\n");
|
||||
@@ -2991,12 +3546,12 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_away: ");
|
||||
out.append(YESNO(this->has_away));
|
||||
out.append(" has_legacy_away: ");
|
||||
out.append(YESNO(this->has_legacy_away));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" away: ");
|
||||
out.append(YESNO(this->away));
|
||||
out.append(" legacy_away: ");
|
||||
out.append(YESNO(this->legacy_away));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_fan_mode: ");
|
||||
@@ -3014,8 +3569,414 @@ void ClimateCommandRequest::dump_to(std::string &out) const {
|
||||
out.append(" swing_mode: ");
|
||||
out.append(proto_enum_to_string<enums::ClimateSwingMode>(this->swing_mode));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_custom_fan_mode: ");
|
||||
out.append(YESNO(this->has_custom_fan_mode));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" custom_fan_mode: ");
|
||||
out.append("'").append(this->custom_fan_mode).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_preset: ");
|
||||
out.append(YESNO(this->has_preset));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" preset: ");
|
||||
out.append(proto_enum_to_string<enums::ClimatePreset>(this->preset));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" has_custom_preset: ");
|
||||
out.append(YESNO(this->has_custom_preset));
|
||||
out.append("\n");
|
||||
|
||||
out.append(" custom_preset: ");
|
||||
out.append("'").append(this->custom_preset).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesNumberResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 9: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ListEntitiesNumberResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->object_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->name = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 4: {
|
||||
this->unique_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 5: {
|
||||
this->icon = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ListEntitiesNumberResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
case 6: {
|
||||
this->min_value = value.as_float();
|
||||
return true;
|
||||
}
|
||||
case 7: {
|
||||
this->max_value = value.as_float();
|
||||
return true;
|
||||
}
|
||||
case 8: {
|
||||
this->step = value.as_float();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void ListEntitiesNumberResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
buffer.encode_string(4, this->unique_id);
|
||||
buffer.encode_string(5, this->icon);
|
||||
buffer.encode_float(6, this->min_value);
|
||||
buffer.encode_float(7, this->max_value);
|
||||
buffer.encode_float(8, this->step);
|
||||
buffer.encode_bool(9, this->disabled_by_default);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesNumberResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesNumberResponse {\n");
|
||||
out.append(" object_id: ");
|
||||
out.append("'").append(this->object_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%u", this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" name: ");
|
||||
out.append("'").append(this->name).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" unique_id: ");
|
||||
out.append("'").append(this->unique_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" icon: ");
|
||||
out.append("'").append(this->icon).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" min_value: ");
|
||||
sprintf(buffer, "%g", this->min_value);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" max_value: ");
|
||||
sprintf(buffer, "%g", this->max_value);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" step: ");
|
||||
sprintf(buffer, "%g", this->step);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool NumberStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 3: {
|
||||
this->missing_state = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool NumberStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
case 2: {
|
||||
this->state = value.as_float();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void NumberStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_float(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void NumberStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("NumberStateResponse {\n");
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%u", this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" state: ");
|
||||
sprintf(buffer, "%g", this->state);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" missing_state: ");
|
||||
out.append(YESNO(this->missing_state));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool NumberCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
case 2: {
|
||||
this->state = value.as_float();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void NumberCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_float(2, this->state);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void NumberCommandRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("NumberCommandRequest {\n");
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%u", this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" state: ");
|
||||
sprintf(buffer, "%g", this->state);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool ListEntitiesSelectResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 7: {
|
||||
this->disabled_by_default = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ListEntitiesSelectResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->object_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 3: {
|
||||
this->name = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 4: {
|
||||
this->unique_id = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 5: {
|
||||
this->icon = value.as_string();
|
||||
return true;
|
||||
}
|
||||
case 6: {
|
||||
this->options.push_back(value.as_string());
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ListEntitiesSelectResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void ListEntitiesSelectResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_string(1, this->object_id);
|
||||
buffer.encode_fixed32(2, this->key);
|
||||
buffer.encode_string(3, this->name);
|
||||
buffer.encode_string(4, this->unique_id);
|
||||
buffer.encode_string(5, this->icon);
|
||||
for (auto &it : this->options) {
|
||||
buffer.encode_string(6, it, true);
|
||||
}
|
||||
buffer.encode_bool(7, this->disabled_by_default);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void ListEntitiesSelectResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("ListEntitiesSelectResponse {\n");
|
||||
out.append(" object_id: ");
|
||||
out.append("'").append(this->object_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%u", this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" name: ");
|
||||
out.append("'").append(this->name).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" unique_id: ");
|
||||
out.append("'").append(this->unique_id).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" icon: ");
|
||||
out.append("'").append(this->icon).append("'");
|
||||
out.append("\n");
|
||||
|
||||
for (const auto &it : this->options) {
|
||||
out.append(" options: ");
|
||||
out.append("'").append(it).append("'");
|
||||
out.append("\n");
|
||||
}
|
||||
|
||||
out.append(" disabled_by_default: ");
|
||||
out.append(YESNO(this->disabled_by_default));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool SelectStateResponse::decode_varint(uint32_t field_id, ProtoVarInt value) {
|
||||
switch (field_id) {
|
||||
case 3: {
|
||||
this->missing_state = value.as_bool();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool SelectStateResponse::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->state = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool SelectStateResponse::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void SelectStateResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_string(2, this->state);
|
||||
buffer.encode_bool(3, this->missing_state);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SelectStateResponse::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("SelectStateResponse {\n");
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%u", this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" state: ");
|
||||
out.append("'").append(this->state).append("'");
|
||||
out.append("\n");
|
||||
|
||||
out.append(" missing_state: ");
|
||||
out.append(YESNO(this->missing_state));
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
bool SelectCommandRequest::decode_length(uint32_t field_id, ProtoLengthDelimited value) {
|
||||
switch (field_id) {
|
||||
case 2: {
|
||||
this->state = value.as_string();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool SelectCommandRequest::decode_32bit(uint32_t field_id, Proto32Bit value) {
|
||||
switch (field_id) {
|
||||
case 1: {
|
||||
this->key = value.as_fixed32();
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void SelectCommandRequest::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_fixed32(1, this->key);
|
||||
buffer.encode_string(2, this->state);
|
||||
}
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void SelectCommandRequest::dump_to(std::string &out) const {
|
||||
char buffer[64];
|
||||
out.append("SelectCommandRequest {\n");
|
||||
out.append(" key: ");
|
||||
sprintf(buffer, "%u", this->key);
|
||||
out.append(buffer);
|
||||
out.append("\n");
|
||||
|
||||
out.append(" state: ");
|
||||
out.append("'").append(this->state).append("'");
|
||||
out.append("\n");
|
||||
out.append("}");
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@@ -32,6 +32,27 @@ enum FanDirection : uint32_t {
|
||||
FAN_DIRECTION_FORWARD = 0,
|
||||
FAN_DIRECTION_REVERSE = 1,
|
||||
};
|
||||
enum ColorMode : uint32_t {
|
||||
COLOR_MODE_UNKNOWN = 0,
|
||||
COLOR_MODE_ON_OFF = 1,
|
||||
COLOR_MODE_BRIGHTNESS = 2,
|
||||
COLOR_MODE_WHITE = 7,
|
||||
COLOR_MODE_COLOR_TEMPERATURE = 11,
|
||||
COLOR_MODE_COLD_WARM_WHITE = 19,
|
||||
COLOR_MODE_RGB = 35,
|
||||
COLOR_MODE_RGB_WHITE = 39,
|
||||
COLOR_MODE_RGB_COLOR_TEMPERATURE = 47,
|
||||
COLOR_MODE_RGB_COLD_WARM_WHITE = 51,
|
||||
};
|
||||
enum SensorStateClass : uint32_t {
|
||||
STATE_CLASS_NONE = 0,
|
||||
STATE_CLASS_MEASUREMENT = 1,
|
||||
};
|
||||
enum SensorLastResetType : uint32_t {
|
||||
LAST_RESET_NONE = 0,
|
||||
LAST_RESET_NEVER = 1,
|
||||
LAST_RESET_AUTO = 2,
|
||||
};
|
||||
enum LogLevel : uint32_t {
|
||||
LOG_LEVEL_NONE = 0,
|
||||
LOG_LEVEL_ERROR = 1,
|
||||
@@ -53,11 +74,12 @@ enum ServiceArgType : uint32_t {
|
||||
};
|
||||
enum ClimateMode : uint32_t {
|
||||
CLIMATE_MODE_OFF = 0,
|
||||
CLIMATE_MODE_AUTO = 1,
|
||||
CLIMATE_MODE_HEAT_COOL = 1,
|
||||
CLIMATE_MODE_COOL = 2,
|
||||
CLIMATE_MODE_HEAT = 3,
|
||||
CLIMATE_MODE_FAN_ONLY = 4,
|
||||
CLIMATE_MODE_DRY = 5,
|
||||
CLIMATE_MODE_AUTO = 6,
|
||||
};
|
||||
enum ClimateFanMode : uint32_t {
|
||||
CLIMATE_FAN_ON = 0,
|
||||
@@ -84,25 +106,39 @@ enum ClimateAction : uint32_t {
|
||||
CLIMATE_ACTION_DRYING = 5,
|
||||
CLIMATE_ACTION_FAN = 6,
|
||||
};
|
||||
enum ClimatePreset : uint32_t {
|
||||
CLIMATE_PRESET_NONE = 0,
|
||||
CLIMATE_PRESET_HOME = 1,
|
||||
CLIMATE_PRESET_AWAY = 2,
|
||||
CLIMATE_PRESET_BOOST = 3,
|
||||
CLIMATE_PRESET_COMFORT = 4,
|
||||
CLIMATE_PRESET_ECO = 5,
|
||||
CLIMATE_PRESET_SLEEP = 6,
|
||||
CLIMATE_PRESET_ACTIVITY = 7,
|
||||
};
|
||||
|
||||
} // namespace enums
|
||||
|
||||
class HelloRequest : public ProtoMessage {
|
||||
public:
|
||||
std::string client_info{}; // NOLINT
|
||||
std::string client_info{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
};
|
||||
class HelloResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t api_version_major{0}; // NOLINT
|
||||
uint32_t api_version_minor{0}; // NOLINT
|
||||
std::string server_info{}; // NOLINT
|
||||
uint32_t api_version_major{0};
|
||||
uint32_t api_version_minor{0};
|
||||
std::string server_info{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
@@ -110,18 +146,22 @@ class HelloResponse : public ProtoMessage {
|
||||
};
|
||||
class ConnectRequest : public ProtoMessage {
|
||||
public:
|
||||
std::string password{}; // NOLINT
|
||||
std::string password{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
};
|
||||
class ConnectResponse : public ProtoMessage {
|
||||
public:
|
||||
bool invalid_password{false}; // NOLINT
|
||||
bool invalid_password{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
@@ -129,49 +169,63 @@ class ConnectResponse : public ProtoMessage {
|
||||
class DisconnectRequest : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class DisconnectResponse : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class PingRequest : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class PingResponse : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class DeviceInfoRequest : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class DeviceInfoResponse : public ProtoMessage {
|
||||
public:
|
||||
bool uses_password{false}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
std::string mac_address{}; // NOLINT
|
||||
std::string esphome_version{}; // NOLINT
|
||||
std::string compilation_time{}; // NOLINT
|
||||
std::string model{}; // NOLINT
|
||||
bool has_deep_sleep{false}; // NOLINT
|
||||
bool uses_password{false};
|
||||
std::string name{};
|
||||
std::string mac_address{};
|
||||
std::string esphome_version{};
|
||||
std::string compilation_time{};
|
||||
std::string model{};
|
||||
bool has_deep_sleep{false};
|
||||
std::string project_name{};
|
||||
std::string project_version{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
@@ -180,34 +234,43 @@ class DeviceInfoResponse : public ProtoMessage {
|
||||
class ListEntitiesRequest : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class ListEntitiesDoneResponse : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class SubscribeStatesRequest : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class ListEntitiesBinarySensorResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
std::string unique_id{}; // NOLINT
|
||||
std::string device_class{}; // NOLINT
|
||||
bool is_status_binary_sensor{false}; // NOLINT
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
std::string device_class{};
|
||||
bool is_status_binary_sensor{false};
|
||||
bool disabled_by_default{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -216,11 +279,13 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
|
||||
};
|
||||
class BinarySensorStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
bool missing_state{false}; // NOLINT
|
||||
uint32_t key{0};
|
||||
bool state{false};
|
||||
bool missing_state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -228,16 +293,19 @@ class BinarySensorStateResponse : public ProtoMessage {
|
||||
};
|
||||
class ListEntitiesCoverResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
std::string unique_id{}; // NOLINT
|
||||
bool assumed_state{false}; // NOLINT
|
||||
bool supports_position{false}; // NOLINT
|
||||
bool supports_tilt{false}; // NOLINT
|
||||
std::string device_class{}; // NOLINT
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
bool assumed_state{false};
|
||||
bool supports_position{false};
|
||||
bool supports_tilt{false};
|
||||
std::string device_class{};
|
||||
bool disabled_by_default{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -246,13 +314,15 @@ class ListEntitiesCoverResponse : public ProtoMessage {
|
||||
};
|
||||
class CoverStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
enums::LegacyCoverState legacy_state{}; // NOLINT
|
||||
float position{0.0f}; // NOLINT
|
||||
float tilt{0.0f}; // NOLINT
|
||||
enums::CoverOperation current_operation{}; // NOLINT
|
||||
uint32_t key{0};
|
||||
enums::LegacyCoverState legacy_state{};
|
||||
float position{0.0f};
|
||||
float tilt{0.0f};
|
||||
enums::CoverOperation current_operation{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -260,16 +330,18 @@ class CoverStateResponse : public ProtoMessage {
|
||||
};
|
||||
class CoverCommandRequest : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool has_legacy_command{false}; // NOLINT
|
||||
enums::LegacyCoverCommand legacy_command{}; // NOLINT
|
||||
bool has_position{false}; // NOLINT
|
||||
float position{0.0f}; // NOLINT
|
||||
bool has_tilt{false}; // NOLINT
|
||||
float tilt{0.0f}; // NOLINT
|
||||
bool stop{false}; // NOLINT
|
||||
uint32_t key{0};
|
||||
bool has_legacy_command{false};
|
||||
enums::LegacyCoverCommand legacy_command{};
|
||||
bool has_position{false};
|
||||
float position{0.0f};
|
||||
bool has_tilt{false};
|
||||
float tilt{0.0f};
|
||||
bool stop{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -277,16 +349,19 @@ class CoverCommandRequest : public ProtoMessage {
|
||||
};
|
||||
class ListEntitiesFanResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
std::string unique_id{}; // NOLINT
|
||||
bool supports_oscillation{false}; // NOLINT
|
||||
bool supports_speed{false}; // NOLINT
|
||||
bool supports_direction{false}; // NOLINT
|
||||
int32_t supported_speed_count{0}; // NOLINT
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
bool supports_oscillation{false};
|
||||
bool supports_speed{false};
|
||||
bool supports_direction{false};
|
||||
int32_t supported_speed_count{0};
|
||||
bool disabled_by_default{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -295,14 +370,16 @@ class ListEntitiesFanResponse : public ProtoMessage {
|
||||
};
|
||||
class FanStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
bool oscillating{false}; // NOLINT
|
||||
enums::FanSpeed speed{}; // NOLINT
|
||||
enums::FanDirection direction{}; // NOLINT
|
||||
int32_t speed_level{0}; // NOLINT
|
||||
uint32_t key{0};
|
||||
bool state{false};
|
||||
bool oscillating{false};
|
||||
enums::FanSpeed speed{};
|
||||
enums::FanDirection direction{};
|
||||
int32_t speed_level{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -310,19 +387,21 @@ class FanStateResponse : public ProtoMessage {
|
||||
};
|
||||
class FanCommandRequest : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool has_state{false}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
bool has_speed{false}; // NOLINT
|
||||
enums::FanSpeed speed{}; // NOLINT
|
||||
bool has_oscillating{false}; // NOLINT
|
||||
bool oscillating{false}; // NOLINT
|
||||
bool has_direction{false}; // NOLINT
|
||||
enums::FanDirection direction{}; // NOLINT
|
||||
bool has_speed_level{false}; // NOLINT
|
||||
int32_t speed_level{0}; // NOLINT
|
||||
uint32_t key{0};
|
||||
bool has_state{false};
|
||||
bool state{false};
|
||||
bool has_speed{false};
|
||||
enums::FanSpeed speed{};
|
||||
bool has_oscillating{false};
|
||||
bool oscillating{false};
|
||||
bool has_direction{false};
|
||||
enums::FanDirection direction{};
|
||||
bool has_speed_level{false};
|
||||
int32_t speed_level{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -330,19 +409,23 @@ class FanCommandRequest : public ProtoMessage {
|
||||
};
|
||||
class ListEntitiesLightResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
std::string unique_id{}; // NOLINT
|
||||
bool supports_brightness{false}; // NOLINT
|
||||
bool supports_rgb{false}; // NOLINT
|
||||
bool supports_white_value{false}; // NOLINT
|
||||
bool supports_color_temperature{false}; // NOLINT
|
||||
float min_mireds{0.0f}; // NOLINT
|
||||
float max_mireds{0.0f}; // NOLINT
|
||||
std::vector<std::string> effects{}; // NOLINT
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
std::vector<enums::ColorMode> supported_color_modes{};
|
||||
bool legacy_supports_brightness{false};
|
||||
bool legacy_supports_rgb{false};
|
||||
bool legacy_supports_white_value{false};
|
||||
bool legacy_supports_color_temperature{false};
|
||||
float min_mireds{0.0f};
|
||||
float max_mireds{0.0f};
|
||||
std::vector<std::string> effects{};
|
||||
bool disabled_by_default{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -351,17 +434,23 @@ class ListEntitiesLightResponse : public ProtoMessage {
|
||||
};
|
||||
class LightStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
float brightness{0.0f}; // NOLINT
|
||||
float red{0.0f}; // NOLINT
|
||||
float green{0.0f}; // NOLINT
|
||||
float blue{0.0f}; // NOLINT
|
||||
float white{0.0f}; // NOLINT
|
||||
float color_temperature{0.0f}; // NOLINT
|
||||
std::string effect{}; // NOLINT
|
||||
uint32_t key{0};
|
||||
bool state{false};
|
||||
float brightness{0.0f};
|
||||
enums::ColorMode color_mode{};
|
||||
float color_brightness{0.0f};
|
||||
float red{0.0f};
|
||||
float green{0.0f};
|
||||
float blue{0.0f};
|
||||
float white{0.0f};
|
||||
float color_temperature{0.0f};
|
||||
float cold_white{0.0f};
|
||||
float warm_white{0.0f};
|
||||
std::string effect{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -370,27 +459,37 @@ class LightStateResponse : public ProtoMessage {
|
||||
};
|
||||
class LightCommandRequest : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool has_state{false}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
bool has_brightness{false}; // NOLINT
|
||||
float brightness{0.0f}; // NOLINT
|
||||
bool has_rgb{false}; // NOLINT
|
||||
float red{0.0f}; // NOLINT
|
||||
float green{0.0f}; // NOLINT
|
||||
float blue{0.0f}; // NOLINT
|
||||
bool has_white{false}; // NOLINT
|
||||
float white{0.0f}; // NOLINT
|
||||
bool has_color_temperature{false}; // NOLINT
|
||||
float color_temperature{0.0f}; // NOLINT
|
||||
bool has_transition_length{false}; // NOLINT
|
||||
uint32_t transition_length{0}; // NOLINT
|
||||
bool has_flash_length{false}; // NOLINT
|
||||
uint32_t flash_length{0}; // NOLINT
|
||||
bool has_effect{false}; // NOLINT
|
||||
std::string effect{}; // NOLINT
|
||||
uint32_t key{0};
|
||||
bool has_state{false};
|
||||
bool state{false};
|
||||
bool has_brightness{false};
|
||||
float brightness{0.0f};
|
||||
bool has_color_mode{false};
|
||||
enums::ColorMode color_mode{};
|
||||
bool has_color_brightness{false};
|
||||
float color_brightness{0.0f};
|
||||
bool has_rgb{false};
|
||||
float red{0.0f};
|
||||
float green{0.0f};
|
||||
float blue{0.0f};
|
||||
bool has_white{false};
|
||||
float white{0.0f};
|
||||
bool has_color_temperature{false};
|
||||
float color_temperature{0.0f};
|
||||
bool has_cold_white{false};
|
||||
float cold_white{0.0f};
|
||||
bool has_warm_white{false};
|
||||
float warm_white{0.0f};
|
||||
bool has_transition_length{false};
|
||||
uint32_t transition_length{0};
|
||||
bool has_flash_length{false};
|
||||
uint32_t flash_length{0};
|
||||
bool has_effect{false};
|
||||
std::string effect{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -399,17 +498,22 @@ class LightCommandRequest : public ProtoMessage {
|
||||
};
|
||||
class ListEntitiesSensorResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
std::string unique_id{}; // NOLINT
|
||||
std::string icon{}; // NOLINT
|
||||
std::string unit_of_measurement{}; // NOLINT
|
||||
int32_t accuracy_decimals{0}; // NOLINT
|
||||
bool force_update{false}; // NOLINT
|
||||
std::string device_class{}; // NOLINT
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
std::string icon{};
|
||||
std::string unit_of_measurement{};
|
||||
int32_t accuracy_decimals{0};
|
||||
bool force_update{false};
|
||||
std::string device_class{};
|
||||
enums::SensorStateClass state_class{};
|
||||
enums::SensorLastResetType last_reset_type{};
|
||||
bool disabled_by_default{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -418,11 +522,13 @@ class ListEntitiesSensorResponse : public ProtoMessage {
|
||||
};
|
||||
class SensorStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
float state{0.0f}; // NOLINT
|
||||
bool missing_state{false}; // NOLINT
|
||||
uint32_t key{0};
|
||||
float state{0.0f};
|
||||
bool missing_state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -430,14 +536,17 @@ class SensorStateResponse : public ProtoMessage {
|
||||
};
|
||||
class ListEntitiesSwitchResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
std::string unique_id{}; // NOLINT
|
||||
std::string icon{}; // NOLINT
|
||||
bool assumed_state{false}; // NOLINT
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
std::string icon{};
|
||||
bool assumed_state{false};
|
||||
bool disabled_by_default{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -446,10 +555,12 @@ class ListEntitiesSwitchResponse : public ProtoMessage {
|
||||
};
|
||||
class SwitchStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
uint32_t key{0};
|
||||
bool state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -457,10 +568,12 @@ class SwitchStateResponse : public ProtoMessage {
|
||||
};
|
||||
class SwitchCommandRequest : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool state{false}; // NOLINT
|
||||
uint32_t key{0};
|
||||
bool state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -468,25 +581,31 @@ class SwitchCommandRequest : public ProtoMessage {
|
||||
};
|
||||
class ListEntitiesTextSensorResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
std::string unique_id{}; // NOLINT
|
||||
std::string icon{}; // NOLINT
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
std::string icon{};
|
||||
bool disabled_by_default{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class TextSensorStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string state{}; // NOLINT
|
||||
bool missing_state{false}; // NOLINT
|
||||
uint32_t key{0};
|
||||
std::string state{};
|
||||
bool missing_state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -495,22 +614,26 @@ class TextSensorStateResponse : public ProtoMessage {
|
||||
};
|
||||
class SubscribeLogsRequest : public ProtoMessage {
|
||||
public:
|
||||
enums::LogLevel level{}; // NOLINT
|
||||
bool dump_config{false}; // NOLINT
|
||||
enums::LogLevel level{};
|
||||
bool dump_config{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class SubscribeLogsResponse : public ProtoMessage {
|
||||
public:
|
||||
enums::LogLevel level{}; // NOLINT
|
||||
std::string tag{}; // NOLINT
|
||||
std::string message{}; // NOLINT
|
||||
bool send_failed{false}; // NOLINT
|
||||
enums::LogLevel level{};
|
||||
std::string tag{};
|
||||
std::string message{};
|
||||
bool send_failed{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
@@ -519,29 +642,35 @@ class SubscribeLogsResponse : public ProtoMessage {
|
||||
class SubscribeHomeassistantServicesRequest : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class HomeassistantServiceMap : public ProtoMessage {
|
||||
public:
|
||||
std::string key{}; // NOLINT
|
||||
std::string value{}; // NOLINT
|
||||
std::string key{};
|
||||
std::string value{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
};
|
||||
class HomeassistantServiceResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string service{}; // NOLINT
|
||||
std::vector<HomeassistantServiceMap> data{}; // NOLINT
|
||||
std::vector<HomeassistantServiceMap> data_template{}; // NOLINT
|
||||
std::vector<HomeassistantServiceMap> variables{}; // NOLINT
|
||||
bool is_event{false}; // NOLINT
|
||||
std::string service{};
|
||||
std::vector<HomeassistantServiceMap> data{};
|
||||
std::vector<HomeassistantServiceMap> data_template{};
|
||||
std::vector<HomeassistantServiceMap> variables{};
|
||||
bool is_event{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
@@ -550,25 +679,33 @@ class HomeassistantServiceResponse : public ProtoMessage {
|
||||
class SubscribeHomeAssistantStatesRequest : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class SubscribeHomeAssistantStateResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string entity_id{}; // NOLINT
|
||||
std::string entity_id{};
|
||||
std::string attribute{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
};
|
||||
class HomeAssistantStateResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string entity_id{}; // NOLINT
|
||||
std::string state{}; // NOLINT
|
||||
std::string entity_id{};
|
||||
std::string state{};
|
||||
std::string attribute{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
@@ -576,25 +713,31 @@ class HomeAssistantStateResponse : public ProtoMessage {
|
||||
class GetTimeRequest : public ProtoMessage {
|
||||
public:
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
};
|
||||
class GetTimeResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t epoch_seconds{0}; // NOLINT
|
||||
uint32_t epoch_seconds{0};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
};
|
||||
class ListEntitiesServicesArgument : public ProtoMessage {
|
||||
public:
|
||||
std::string name{}; // NOLINT
|
||||
enums::ServiceArgType type{}; // NOLINT
|
||||
std::string name{};
|
||||
enums::ServiceArgType type{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
@@ -602,11 +745,13 @@ class ListEntitiesServicesArgument : public ProtoMessage {
|
||||
};
|
||||
class ListEntitiesServicesResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string name{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::vector<ListEntitiesServicesArgument> args{}; // NOLINT
|
||||
std::string name{};
|
||||
uint32_t key{0};
|
||||
std::vector<ListEntitiesServicesArgument> args{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -614,17 +759,19 @@ class ListEntitiesServicesResponse : public ProtoMessage {
|
||||
};
|
||||
class ExecuteServiceArgument : public ProtoMessage {
|
||||
public:
|
||||
bool bool_{false}; // NOLINT
|
||||
int32_t legacy_int{0}; // NOLINT
|
||||
float float_{0.0f}; // NOLINT
|
||||
std::string string_{}; // NOLINT
|
||||
int32_t int_{0}; // NOLINT
|
||||
std::vector<bool> bool_array{}; // NOLINT
|
||||
std::vector<int32_t> int_array{}; // NOLINT
|
||||
std::vector<float> float_array{}; // NOLINT
|
||||
std::vector<std::string> string_array{}; // NOLINT
|
||||
bool bool_{false};
|
||||
int32_t legacy_int{0};
|
||||
float float_{0.0f};
|
||||
std::string string_{};
|
||||
int32_t int_{0};
|
||||
std::vector<bool> bool_array{};
|
||||
std::vector<int32_t> int_array{};
|
||||
std::vector<float> float_array{};
|
||||
std::vector<std::string> string_array{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -633,10 +780,12 @@ class ExecuteServiceArgument : public ProtoMessage {
|
||||
};
|
||||
class ExecuteServiceRequest : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::vector<ExecuteServiceArgument> args{}; // NOLINT
|
||||
uint32_t key{0};
|
||||
std::vector<ExecuteServiceArgument> args{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -644,24 +793,30 @@ class ExecuteServiceRequest : public ProtoMessage {
|
||||
};
|
||||
class ListEntitiesCameraResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
std::string unique_id{}; // NOLINT
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
bool disabled_by_default{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class CameraImageResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string data{}; // NOLINT
|
||||
bool done{false}; // NOLINT
|
||||
uint32_t key{0};
|
||||
std::string data{};
|
||||
bool done{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -670,32 +825,40 @@ class CameraImageResponse : public ProtoMessage {
|
||||
};
|
||||
class CameraImageRequest : public ProtoMessage {
|
||||
public:
|
||||
bool single{false}; // NOLINT
|
||||
bool stream{false}; // NOLINT
|
||||
bool single{false};
|
||||
bool stream{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class ListEntitiesClimateResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{}; // NOLINT
|
||||
uint32_t key{0}; // NOLINT
|
||||
std::string name{}; // NOLINT
|
||||
std::string unique_id{}; // NOLINT
|
||||
bool supports_current_temperature{false}; // NOLINT
|
||||
bool supports_two_point_target_temperature{false}; // NOLINT
|
||||
std::vector<enums::ClimateMode> supported_modes{}; // NOLINT
|
||||
float visual_min_temperature{0.0f}; // NOLINT
|
||||
float visual_max_temperature{0.0f}; // NOLINT
|
||||
float visual_temperature_step{0.0f}; // NOLINT
|
||||
bool supports_away{false}; // NOLINT
|
||||
bool supports_action{false}; // NOLINT
|
||||
std::vector<enums::ClimateFanMode> supported_fan_modes{}; // NOLINT
|
||||
std::vector<enums::ClimateSwingMode> supported_swing_modes{}; // NOLINT
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
bool supports_current_temperature{false};
|
||||
bool supports_two_point_target_temperature{false};
|
||||
std::vector<enums::ClimateMode> supported_modes{};
|
||||
float visual_min_temperature{0.0f};
|
||||
float visual_max_temperature{0.0f};
|
||||
float visual_temperature_step{0.0f};
|
||||
bool legacy_supports_away{false};
|
||||
bool supports_action{false};
|
||||
std::vector<enums::ClimateFanMode> supported_fan_modes{};
|
||||
std::vector<enums::ClimateSwingMode> supported_swing_modes{};
|
||||
std::vector<std::string> supported_custom_fan_modes{};
|
||||
std::vector<enums::ClimatePreset> supported_presets{};
|
||||
std::vector<std::string> supported_custom_presets{};
|
||||
bool disabled_by_default{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
@@ -704,47 +867,156 @@ class ListEntitiesClimateResponse : public ProtoMessage {
|
||||
};
|
||||
class ClimateStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
enums::ClimateMode mode{}; // NOLINT
|
||||
float current_temperature{0.0f}; // NOLINT
|
||||
float target_temperature{0.0f}; // NOLINT
|
||||
float target_temperature_low{0.0f}; // NOLINT
|
||||
float target_temperature_high{0.0f}; // NOLINT
|
||||
bool away{false}; // NOLINT
|
||||
enums::ClimateAction action{}; // NOLINT
|
||||
enums::ClimateFanMode fan_mode{}; // NOLINT
|
||||
enums::ClimateSwingMode swing_mode{}; // NOLINT
|
||||
uint32_t key{0};
|
||||
enums::ClimateMode mode{};
|
||||
float current_temperature{0.0f};
|
||||
float target_temperature{0.0f};
|
||||
float target_temperature_low{0.0f};
|
||||
float target_temperature_high{0.0f};
|
||||
bool legacy_away{false};
|
||||
enums::ClimateAction action{};
|
||||
enums::ClimateFanMode fan_mode{};
|
||||
enums::ClimateSwingMode swing_mode{};
|
||||
std::string custom_fan_mode{};
|
||||
enums::ClimatePreset preset{};
|
||||
std::string custom_preset{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class ClimateCommandRequest : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0}; // NOLINT
|
||||
bool has_mode{false}; // NOLINT
|
||||
enums::ClimateMode mode{}; // NOLINT
|
||||
bool has_target_temperature{false}; // NOLINT
|
||||
float target_temperature{0.0f}; // NOLINT
|
||||
bool has_target_temperature_low{false}; // NOLINT
|
||||
float target_temperature_low{0.0f}; // NOLINT
|
||||
bool has_target_temperature_high{false}; // NOLINT
|
||||
float target_temperature_high{0.0f}; // NOLINT
|
||||
bool has_away{false}; // NOLINT
|
||||
bool away{false}; // NOLINT
|
||||
bool has_fan_mode{false}; // NOLINT
|
||||
enums::ClimateFanMode fan_mode{}; // NOLINT
|
||||
bool has_swing_mode{false}; // NOLINT
|
||||
enums::ClimateSwingMode swing_mode{}; // NOLINT
|
||||
uint32_t key{0};
|
||||
bool has_mode{false};
|
||||
enums::ClimateMode mode{};
|
||||
bool has_target_temperature{false};
|
||||
float target_temperature{0.0f};
|
||||
bool has_target_temperature_low{false};
|
||||
float target_temperature_low{0.0f};
|
||||
bool has_target_temperature_high{false};
|
||||
float target_temperature_high{0.0f};
|
||||
bool has_legacy_away{false};
|
||||
bool legacy_away{false};
|
||||
bool has_fan_mode{false};
|
||||
enums::ClimateFanMode fan_mode{};
|
||||
bool has_swing_mode{false};
|
||||
enums::ClimateSwingMode swing_mode{};
|
||||
bool has_custom_fan_mode{false};
|
||||
std::string custom_fan_mode{};
|
||||
bool has_preset{false};
|
||||
enums::ClimatePreset preset{};
|
||||
bool has_custom_preset{false};
|
||||
std::string custom_preset{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class ListEntitiesNumberResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
std::string icon{};
|
||||
float min_value{0.0f};
|
||||
float max_value{0.0f};
|
||||
float step{0.0f};
|
||||
bool disabled_by_default{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class NumberStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0};
|
||||
float state{0.0f};
|
||||
bool missing_state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class NumberCommandRequest : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0};
|
||||
float state{0.0f};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
};
|
||||
class ListEntitiesSelectResponse : public ProtoMessage {
|
||||
public:
|
||||
std::string object_id{};
|
||||
uint32_t key{0};
|
||||
std::string name{};
|
||||
std::string unique_id{};
|
||||
std::string icon{};
|
||||
std::vector<std::string> options{};
|
||||
bool disabled_by_default{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class SelectStateResponse : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0};
|
||||
std::string state{};
|
||||
bool missing_state{false};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
bool decode_varint(uint32_t field_id, ProtoVarInt value) override;
|
||||
};
|
||||
class SelectCommandRequest : public ProtoMessage {
|
||||
public:
|
||||
uint32_t key{0};
|
||||
std::string state{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
void dump_to(std::string &out) const override;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
bool decode_32bit(uint32_t field_id, Proto32Bit value) override;
|
||||
bool decode_length(uint32_t field_id, ProtoLengthDelimited value) override;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@@ -6,61 +6,85 @@
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
static const char *TAG = "api.service";
|
||||
static const char *const TAG = "api.service";
|
||||
|
||||
bool APIServerConnectionBase::send_hello_response(const HelloResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_hello_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<HelloResponse>(msg, 2);
|
||||
}
|
||||
bool APIServerConnectionBase::send_connect_response(const ConnectResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_connect_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ConnectResponse>(msg, 4);
|
||||
}
|
||||
bool APIServerConnectionBase::send_disconnect_request(const DisconnectRequest &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_disconnect_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<DisconnectRequest>(msg, 5);
|
||||
}
|
||||
bool APIServerConnectionBase::send_disconnect_response(const DisconnectResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_disconnect_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<DisconnectResponse>(msg, 6);
|
||||
}
|
||||
bool APIServerConnectionBase::send_ping_request(const PingRequest &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_ping_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<PingRequest>(msg, 7);
|
||||
}
|
||||
bool APIServerConnectionBase::send_ping_response(const PingResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_ping_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<PingResponse>(msg, 8);
|
||||
}
|
||||
bool APIServerConnectionBase::send_device_info_response(const DeviceInfoResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_device_info_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<DeviceInfoResponse>(msg, 10);
|
||||
}
|
||||
bool APIServerConnectionBase::send_list_entities_done_response(const ListEntitiesDoneResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_done_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesDoneResponse>(msg, 19);
|
||||
}
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool APIServerConnectionBase::send_list_entities_binary_sensor_response(const ListEntitiesBinarySensorResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_binary_sensor_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesBinarySensorResponse>(msg, 12);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
bool APIServerConnectionBase::send_binary_sensor_state_response(const BinarySensorStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_binary_sensor_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<BinarySensorStateResponse>(msg, 21);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
bool APIServerConnectionBase::send_list_entities_cover_response(const ListEntitiesCoverResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_cover_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesCoverResponse>(msg, 13);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_COVER
|
||||
bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_cover_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<CoverStateResponse>(msg, 22);
|
||||
}
|
||||
#endif
|
||||
@@ -68,13 +92,17 @@ bool APIServerConnectionBase::send_cover_state_response(const CoverStateResponse
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
bool APIServerConnectionBase::send_list_entities_fan_response(const ListEntitiesFanResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_fan_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesFanResponse>(msg, 14);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_FAN
|
||||
bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_fan_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<FanStateResponse>(msg, 23);
|
||||
}
|
||||
#endif
|
||||
@@ -82,13 +110,17 @@ bool APIServerConnectionBase::send_fan_state_response(const FanStateResponse &ms
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
bool APIServerConnectionBase::send_list_entities_light_response(const ListEntitiesLightResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_light_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesLightResponse>(msg, 15);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_LIGHT
|
||||
bool APIServerConnectionBase::send_light_state_response(const LightStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_light_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<LightStateResponse>(msg, 24);
|
||||
}
|
||||
#endif
|
||||
@@ -96,25 +128,33 @@ bool APIServerConnectionBase::send_light_state_response(const LightStateResponse
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
bool APIServerConnectionBase::send_list_entities_sensor_response(const ListEntitiesSensorResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_sensor_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesSensorResponse>(msg, 16);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
bool APIServerConnectionBase::send_sensor_state_response(const SensorStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_sensor_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<SensorStateResponse>(msg, 25);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
bool APIServerConnectionBase::send_list_entities_switch_response(const ListEntitiesSwitchResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_switch_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesSwitchResponse>(msg, 17);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
bool APIServerConnectionBase::send_switch_state_response(const SwitchStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_switch_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<SwitchStateResponse>(msg, 26);
|
||||
}
|
||||
#endif
|
||||
@@ -122,13 +162,17 @@ bool APIServerConnectionBase::send_switch_state_response(const SwitchStateRespon
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool APIServerConnectionBase::send_list_entities_text_sensor_response(const ListEntitiesTextSensorResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_text_sensor_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesTextSensorResponse>(msg, 18);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
bool APIServerConnectionBase::send_text_sensor_state_response(const TextSensorStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_text_sensor_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<TextSensorStateResponse>(msg, 27);
|
||||
}
|
||||
#endif
|
||||
@@ -136,35 +180,49 @@ bool APIServerConnectionBase::send_subscribe_logs_response(const SubscribeLogsRe
|
||||
return this->send_message_<SubscribeLogsResponse>(msg, 29);
|
||||
}
|
||||
bool APIServerConnectionBase::send_homeassistant_service_response(const HomeassistantServiceResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_homeassistant_service_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<HomeassistantServiceResponse>(msg, 35);
|
||||
}
|
||||
bool APIServerConnectionBase::send_subscribe_home_assistant_state_response(
|
||||
const SubscribeHomeAssistantStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_subscribe_home_assistant_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<SubscribeHomeAssistantStateResponse>(msg, 39);
|
||||
}
|
||||
bool APIServerConnectionBase::send_get_time_request(const GetTimeRequest &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_get_time_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<GetTimeRequest>(msg, 36);
|
||||
}
|
||||
bool APIServerConnectionBase::send_get_time_response(const GetTimeResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_get_time_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<GetTimeResponse>(msg, 37);
|
||||
}
|
||||
bool APIServerConnectionBase::send_list_entities_services_response(const ListEntitiesServicesResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_services_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesServicesResponse>(msg, 41);
|
||||
}
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
bool APIServerConnectionBase::send_list_entities_camera_response(const ListEntitiesCameraResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_camera_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesCameraResponse>(msg, 43);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
bool APIServerConnectionBase::send_camera_image_response(const CameraImageResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_camera_image_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<CameraImageResponse>(msg, 44);
|
||||
}
|
||||
#endif
|
||||
@@ -172,87 +230,147 @@ bool APIServerConnectionBase::send_camera_image_response(const CameraImageRespon
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool APIServerConnectionBase::send_list_entities_climate_response(const ListEntitiesClimateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_climate_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesClimateResponse>(msg, 46);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool APIServerConnectionBase::send_climate_state_response(const ClimateStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_climate_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ClimateStateResponse>(msg, 47);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool APIServerConnectionBase::send_list_entities_number_response(const ListEntitiesNumberResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_number_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesNumberResponse>(msg, 49);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool APIServerConnectionBase::send_number_state_response(const NumberStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_number_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<NumberStateResponse>(msg, 50);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool APIServerConnectionBase::send_list_entities_select_response(const ListEntitiesSelectResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_list_entities_select_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<ListEntitiesSelectResponse>(msg, 52);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool APIServerConnectionBase::send_select_state_response(const SelectStateResponse &msg) {
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "send_select_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
return this->send_message_<SelectStateResponse>(msg, 53);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
#endif
|
||||
bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) {
|
||||
switch (msg_type) {
|
||||
case 1: {
|
||||
HelloRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_hello_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_hello_request(msg);
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
ConnectRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_connect_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_connect_request(msg);
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
DisconnectRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_disconnect_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_disconnect_request(msg);
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
DisconnectResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_disconnect_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_disconnect_response(msg);
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
PingRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_ping_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_ping_request(msg);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
PingResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_ping_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_ping_response(msg);
|
||||
break;
|
||||
}
|
||||
case 9: {
|
||||
DeviceInfoRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_device_info_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_device_info_request(msg);
|
||||
break;
|
||||
}
|
||||
case 11: {
|
||||
ListEntitiesRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_list_entities_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_list_entities_request(msg);
|
||||
break;
|
||||
}
|
||||
case 20: {
|
||||
SubscribeStatesRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_states_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_states_request(msg);
|
||||
break;
|
||||
}
|
||||
case 28: {
|
||||
SubscribeLogsRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_logs_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_logs_request(msg);
|
||||
break;
|
||||
}
|
||||
@@ -260,7 +378,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#ifdef USE_COVER
|
||||
CoverCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_cover_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_cover_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
@@ -269,7 +389,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#ifdef USE_FAN
|
||||
FanCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_fan_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_fan_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
@@ -278,7 +400,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#ifdef USE_LIGHT
|
||||
LightCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_light_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_light_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
@@ -287,7 +411,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#ifdef USE_SWITCH
|
||||
SwitchCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_switch_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_switch_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
@@ -295,42 +421,54 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
case 34: {
|
||||
SubscribeHomeassistantServicesRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_homeassistant_services_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_homeassistant_services_request(msg);
|
||||
break;
|
||||
}
|
||||
case 36: {
|
||||
GetTimeRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_get_time_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_get_time_request(msg);
|
||||
break;
|
||||
}
|
||||
case 37: {
|
||||
GetTimeResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_get_time_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_get_time_response(msg);
|
||||
break;
|
||||
}
|
||||
case 38: {
|
||||
SubscribeHomeAssistantStatesRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_subscribe_home_assistant_states_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_subscribe_home_assistant_states_request(msg);
|
||||
break;
|
||||
}
|
||||
case 40: {
|
||||
HomeAssistantStateResponse msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_home_assistant_state_response: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_home_assistant_state_response(msg);
|
||||
break;
|
||||
}
|
||||
case 42: {
|
||||
ExecuteServiceRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_execute_service_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_execute_service_request(msg);
|
||||
break;
|
||||
}
|
||||
@@ -338,7 +476,9 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#ifdef USE_ESP32_CAMERA
|
||||
CameraImageRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_camera_image_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_camera_image_request(msg);
|
||||
#endif
|
||||
break;
|
||||
@@ -347,8 +487,32 @@ bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type,
|
||||
#ifdef USE_CLIMATE
|
||||
ClimateCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_climate_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_climate_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 51: {
|
||||
#ifdef USE_NUMBER
|
||||
NumberCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_number_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_number_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case 54: {
|
||||
#ifdef USE_SELECT
|
||||
SelectCommandRequest msg;
|
||||
msg.decode(msg_data, msg_size);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
ESP_LOGVV(TAG, "on_select_command_request: %s", msg.dump().c_str());
|
||||
#endif
|
||||
this->on_select_command_request(msg);
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
@@ -547,6 +711,32 @@ void APIServerConnection::on_climate_command_request(const ClimateCommandRequest
|
||||
this->climate_command(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
void APIServerConnection::on_number_command_request(const NumberCommandRequest &msg) {
|
||||
if (!this->is_connection_setup()) {
|
||||
this->on_no_setup_connection();
|
||||
return;
|
||||
}
|
||||
if (!this->is_authenticated()) {
|
||||
this->on_unauthenticated_access();
|
||||
return;
|
||||
}
|
||||
this->number_command(msg);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
void APIServerConnection::on_select_command_request(const SelectCommandRequest &msg) {
|
||||
if (!this->is_connection_setup()) {
|
||||
this->on_no_setup_connection();
|
||||
return;
|
||||
}
|
||||
if (!this->is_authenticated()) {
|
||||
this->on_unauthenticated_access();
|
||||
return;
|
||||
}
|
||||
this->select_command(msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@@ -111,6 +111,24 @@ class APIServerConnectionBase : public ProtoService {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
virtual void on_climate_command_request(const ClimateCommandRequest &value){};
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool send_list_entities_number_response(const ListEntitiesNumberResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool send_number_state_response(const NumberStateResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
virtual void on_number_command_request(const NumberCommandRequest &value){};
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool send_list_entities_select_response(const ListEntitiesSelectResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool send_select_state_response(const SelectStateResponse &msg);
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
virtual void on_select_command_request(const SelectCommandRequest &value){};
|
||||
#endif
|
||||
protected:
|
||||
bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override;
|
||||
@@ -147,6 +165,12 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
virtual void climate_command(const ClimateCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
virtual void number_command(const NumberCommandRequest &msg) = 0;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
virtual void select_command(const SelectCommandRequest &msg) = 0;
|
||||
#endif
|
||||
protected:
|
||||
void on_hello_request(const HelloRequest &msg) override;
|
||||
@@ -179,6 +203,12 @@ class APIServerConnection : public APIServerConnectionBase {
|
||||
#ifdef USE_CLIMATE
|
||||
void on_climate_command_request(const ClimateCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
void on_number_command_request(const NumberCommandRequest &msg) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
void on_select_command_request(const SelectCommandRequest &msg) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "esphome/core/util.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/version.h"
|
||||
#include <errno.h>
|
||||
//#include <arpa/inet.h>
|
||||
|
||||
#ifdef USE_LOGGER
|
||||
#include "esphome/components/logger/logger.h"
|
||||
@@ -15,26 +17,60 @@
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
static const char *TAG = "api";
|
||||
static const char *const TAG = "api";
|
||||
|
||||
// APIServer
|
||||
void APIServer::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up Home Assistant API server...");
|
||||
this->setup_controller();
|
||||
this->server_ = AsyncServer(this->port_);
|
||||
this->server_.setNoDelay(false);
|
||||
this->server_.begin();
|
||||
this->server_.onClient(
|
||||
[](void *s, AsyncClient *client) {
|
||||
if (client == nullptr)
|
||||
return;
|
||||
socket_ = socket::socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (socket_ == nullptr) {
|
||||
ESP_LOGW(TAG, "Could not create socket.");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
int enable = 1;
|
||||
int err = socket_->setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int));
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to set reuseaddr: errno %d", err);
|
||||
// we can still continue
|
||||
}
|
||||
err = socket_->setblocking(false);
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to set nonblocking mode: errno %d", err);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
/*struct sockaddr_storage dest_addr;
|
||||
memset(&dest_addr, 0, sizeof(dest_addr));
|
||||
struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *) &dest_addr;
|
||||
dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
dest_addr_ip4->sin_family = AF_INET;
|
||||
dest_addr_ip4->sin_port = htons(this->port_);
|
||||
|
||||
err = socket_->bind((struct sockaddr *) &dest_addr, sizeof(dest_addr));*/
|
||||
|
||||
struct sockaddr_in server;
|
||||
memset(&server, 0, sizeof(server));
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_addr.s_addr = INADDR_ANY;
|
||||
server.sin_port = htons(this->port_);
|
||||
|
||||
err = socket_->bind((struct sockaddr *) &server, sizeof(server));
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to bind: errno %d", errno);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
err = socket_->listen(4);
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "Socket unable to listen: errno %d", errno);
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
// can't print here because in lwIP thread
|
||||
// ESP_LOGD(TAG, "New client connected from %s", client->remoteIP().toString().c_str());
|
||||
auto *a_this = (APIServer *) s;
|
||||
a_this->clients_.push_back(new APIConnection(client, a_this));
|
||||
},
|
||||
this);
|
||||
#ifdef USE_LOGGER
|
||||
if (logger::global_logger != nullptr) {
|
||||
logger::global_logger->add_on_log_callback([this](int level, const char *tag, const char *message) {
|
||||
@@ -59,6 +95,20 @@ void APIServer::setup() {
|
||||
#endif
|
||||
}
|
||||
void APIServer::loop() {
|
||||
// Accept new clients
|
||||
while (true) {
|
||||
struct sockaddr_storage source_addr;
|
||||
socklen_t addr_len = sizeof(source_addr);
|
||||
auto sock = socket_->accept((struct sockaddr *) &source_addr, &addr_len);
|
||||
if (!sock)
|
||||
break;
|
||||
ESP_LOGD(TAG, "Accepted %s", sock->getpeername().c_str());
|
||||
|
||||
auto *conn = new APIConnection(std::move(sock), this);
|
||||
clients_.push_back(conn);
|
||||
conn->start();
|
||||
}
|
||||
|
||||
// Partition clients into remove and active
|
||||
auto new_end =
|
||||
std::partition(this->clients_.begin(), this->clients_.end(), [](APIConnection *conn) { return !conn->remove_; });
|
||||
@@ -180,7 +230,7 @@ void APIServer::on_switch_update(switch_::Switch *obj, bool state) {
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) {
|
||||
void APIServer::on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
@@ -197,9 +247,27 @@ void APIServer::on_climate_update(climate::Climate *obj) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
void APIServer::on_number_update(number::Number *obj, float state) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
c->send_number_state(obj, state);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
void APIServer::on_select_update(select::Select *obj, const std::string &state) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto *c : this->clients_)
|
||||
c->send_select_state(obj, state);
|
||||
}
|
||||
#endif
|
||||
|
||||
float APIServer::get_setup_priority() const { return setup_priority::AFTER_WIFI; }
|
||||
void APIServer::set_port(uint16_t port) { this->port_ = port; }
|
||||
APIServer *global_api_server = nullptr;
|
||||
APIServer *global_api_server = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
void APIServer::set_password(const std::string &password) { this->password_ = password; }
|
||||
void APIServer::send_homeassistant_service_call(const HomeassistantServiceResponse &call) {
|
||||
@@ -208,9 +276,11 @@ void APIServer::send_homeassistant_service_call(const HomeassistantServiceRespon
|
||||
}
|
||||
}
|
||||
APIServer::APIServer() { global_api_server = this; }
|
||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f) {
|
||||
void APIServer::subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(std::string)> f) {
|
||||
this->state_subs_.push_back(HomeAssistantStateSubscription{
|
||||
.entity_id = std::move(entity_id),
|
||||
.attribute = std::move(attribute),
|
||||
.callback = std::move(f),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,20 +4,14 @@
|
||||
#include "esphome/core/controller.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/components/socket/socket.h"
|
||||
#include "api_pb2.h"
|
||||
#include "api_pb2_service.h"
|
||||
#include "util.h"
|
||||
#include "list_entities.h"
|
||||
#include "subscribe_state.h"
|
||||
#include "homeassistant_service.h"
|
||||
#include "user_services.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <AsyncTCP.h>
|
||||
#endif
|
||||
#ifdef ARDUINO_ARCH_ESP8266
|
||||
#include <ESPAsyncTCP.h>
|
||||
#endif
|
||||
#include "api_noise_context.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
@@ -36,6 +30,16 @@ class APIServer : public Component, public Controller {
|
||||
void set_port(uint16_t port);
|
||||
void set_password(const std::string &password);
|
||||
void set_reboot_timeout(uint32_t reboot_timeout);
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
void set_noise_psk(psk_t psk) {
|
||||
noise_ctx_->set_psk(std::move(psk));
|
||||
}
|
||||
std::shared_ptr<APINoiseContext> get_noise_ctx() {
|
||||
return noise_ctx_;
|
||||
}
|
||||
#endif // USE_API_NOISE
|
||||
|
||||
void handle_disconnect(APIConnection *conn);
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
void on_binary_sensor_update(binary_sensor::BinarySensor *obj, bool state) override;
|
||||
@@ -56,10 +60,16 @@ class APIServer : public Component, public Controller {
|
||||
void on_switch_update(switch_::Switch *obj, bool state) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void on_text_sensor_update(text_sensor::TextSensor *obj, std::string state) override;
|
||||
void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override;
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
void on_climate_update(climate::Climate *obj) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
void on_number_update(number::Number *obj, float state) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
void on_select_update(select::Select *obj, const std::string &state) override;
|
||||
#endif
|
||||
void send_homeassistant_service_call(const HomeassistantServiceResponse &call);
|
||||
void register_user_service(UserServiceDescriptor *descriptor) { this->user_services_.push_back(descriptor); }
|
||||
@@ -71,15 +81,17 @@ class APIServer : public Component, public Controller {
|
||||
|
||||
struct HomeAssistantStateSubscription {
|
||||
std::string entity_id;
|
||||
optional<std::string> attribute;
|
||||
std::function<void(std::string)> callback;
|
||||
};
|
||||
|
||||
void subscribe_home_assistant_state(std::string entity_id, std::function<void(std::string)> f);
|
||||
void subscribe_home_assistant_state(std::string entity_id, optional<std::string> attribute,
|
||||
std::function<void(std::string)> f);
|
||||
const std::vector<HomeAssistantStateSubscription> &get_state_subs() const;
|
||||
const std::vector<UserServiceDescriptor *> &get_user_services() const { return this->user_services_; }
|
||||
|
||||
protected:
|
||||
AsyncServer server_{0};
|
||||
std::unique_ptr<socket::Socket> socket_ = nullptr;
|
||||
uint16_t port_{6053};
|
||||
uint32_t reboot_timeout_{300000};
|
||||
uint32_t last_connected_{0};
|
||||
@@ -87,9 +99,13 @@ class APIServer : public Component, public Controller {
|
||||
std::string password_;
|
||||
std::vector<HomeAssistantStateSubscription> state_subs_;
|
||||
std::vector<UserServiceDescriptor *> user_services_;
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
std::shared_ptr<APINoiseContext> noise_ctx_ = std::make_shared<APINoiseContext>();
|
||||
#endif // USE_API_NOISE
|
||||
};
|
||||
|
||||
extern APIServer *global_api_server;
|
||||
extern APIServer *global_api_server; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
|
||||
public:
|
||||
|
||||
@@ -76,13 +76,13 @@ class CustomAPIDevice {
|
||||
global_api_server->register_user_service(service);
|
||||
}
|
||||
|
||||
/** Subscribe to the state of an entity from Home Assistant.
|
||||
/** Subscribe to the state (or attribute state) of an entity from Home Assistant.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* ```cpp
|
||||
* void setup() override {
|
||||
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
|
||||
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "climate.kitchen", "current_temperature");
|
||||
* }
|
||||
*
|
||||
* void on_state_changed(std::string state) {
|
||||
@@ -93,17 +93,19 @@ class CustomAPIDevice {
|
||||
* @tparam T The class type creating the service, automatically deduced from the function pointer.
|
||||
* @param callback The member function to call when the entity state changes.
|
||||
* @param entity_id The entity_id to track.
|
||||
* @param attribute The entity state attribute to track.
|
||||
*/
|
||||
template<typename T>
|
||||
void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id) {
|
||||
void subscribe_homeassistant_state(void (T::*callback)(std::string), const std::string &entity_id,
|
||||
const std::string &attribute = "") {
|
||||
auto f = std::bind(callback, (T *) this, std::placeholders::_1);
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, f);
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
|
||||
}
|
||||
|
||||
/** Subscribe to the state of an entity from Home Assistant.
|
||||
/** Subscribe to the state (or attribute state) of an entity from Home Assistant.
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
*å
|
||||
* ```cpp
|
||||
* void setup() override {
|
||||
* subscribe_homeassistant_state(&CustomNativeAPI::on_state_changed, "sensor.weather_forecast");
|
||||
@@ -117,11 +119,13 @@ class CustomAPIDevice {
|
||||
* @tparam T The class type creating the service, automatically deduced from the function pointer.
|
||||
* @param callback The member function to call when the entity state changes.
|
||||
* @param entity_id The entity_id to track.
|
||||
* @param attribute The entity state attribute to track.
|
||||
*/
|
||||
template<typename T>
|
||||
void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id) {
|
||||
void subscribe_homeassistant_state(void (T::*callback)(std::string, std::string), const std::string &entity_id,
|
||||
const std::string &attribute = "") {
|
||||
auto f = std::bind(callback, (T *) this, entity_id, std::placeholders::_1);
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, f);
|
||||
global_api_server->subscribe_home_assistant_state(entity_id, optional<std::string>(attribute), f);
|
||||
}
|
||||
|
||||
/** Call a Home Assistant service from ESPHome.
|
||||
|
||||
@@ -51,5 +51,13 @@ bool ListEntitiesIterator::on_camera(esp32_camera::ESP32Camera *camera) {
|
||||
bool ListEntitiesIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_info(climate); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
bool ListEntitiesIterator::on_number(number::Number *number) { return this->client_->send_number_info(number); }
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
bool ListEntitiesIterator::on_select(select::Select *select) { return this->client_->send_select_info(select); }
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@@ -39,6 +39,12 @@ class ListEntitiesIterator : public ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool on_climate(climate::Climate *climate) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool on_number(number::Number *number) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool on_select(select::Select *select) override;
|
||||
#endif
|
||||
bool on_end() override;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
static const char *TAG = "api.proto";
|
||||
static const char *const TAG = "api.proto";
|
||||
|
||||
void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
uint32_t i = 0;
|
||||
@@ -80,11 +80,13 @@ void ProtoMessage::decode(const uint8_t *buffer, size_t length) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
std::string ProtoMessage::dump() const {
|
||||
std::string out;
|
||||
this->dump_to(out);
|
||||
return out;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace api
|
||||
} // namespace esphome
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
|
||||
#ifdef ESPHOME_LOG_HAS_VERY_VERBOSE
|
||||
#define HAS_PROTO_MESSAGE_DUMP
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace api {
|
||||
|
||||
@@ -243,8 +248,10 @@ class ProtoMessage {
|
||||
public:
|
||||
virtual void encode(ProtoWriteBuffer buffer) const = 0;
|
||||
void decode(const uint8_t *buffer, size_t length);
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
std::string dump() const;
|
||||
virtual void dump_to(std::string &out) const = 0;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual bool decode_varint(uint32_t field_id, ProtoVarInt value) { return false; }
|
||||
|
||||
@@ -37,6 +37,16 @@ bool InitialStateIterator::on_text_sensor(text_sensor::TextSensor *text_sensor)
|
||||
#ifdef USE_CLIMATE
|
||||
bool InitialStateIterator::on_climate(climate::Climate *climate) { return this->client_->send_climate_state(climate); }
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool InitialStateIterator::on_number(number::Number *number) {
|
||||
return this->client_->send_number_state(number, number->state);
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool InitialStateIterator::on_select(select::Select *select) {
|
||||
return this->client_->send_select_state(select, select->state);
|
||||
}
|
||||
#endif
|
||||
InitialStateIterator::InitialStateIterator(APIServer *server, APIConnection *client)
|
||||
: ComponentIterator(server), client_(client) {}
|
||||
|
||||
|
||||
@@ -36,6 +36,12 @@ class InitialStateIterator : public ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
bool on_climate(climate::Climate *climate) override;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
bool on_number(number::Number *number) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
bool on_select(select::Select *select) override;
|
||||
#endif
|
||||
protected:
|
||||
APIConnection *client_;
|
||||
|
||||
@@ -15,7 +15,7 @@ template<> std::string get_execute_arg_value<std::string>(const ExecuteServiceAr
|
||||
template<> std::vector<bool> get_execute_arg_value<std::vector<bool>>(const ExecuteServiceArgument &arg) {
|
||||
return arg.bool_array;
|
||||
}
|
||||
template<> std::vector<int> get_execute_arg_value<std::vector<int>>(const ExecuteServiceArgument &arg) {
|
||||
template<> std::vector<int32_t> get_execute_arg_value<std::vector<int32_t>>(const ExecuteServiceArgument &arg) {
|
||||
return arg.int_array;
|
||||
}
|
||||
template<> std::vector<float> get_execute_arg_value<std::vector<float>>(const ExecuteServiceArgument &arg) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "api_pb2.h"
|
||||
@@ -20,8 +22,8 @@ template<typename T> enums::ServiceArgType to_service_arg_type();
|
||||
|
||||
template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
||||
public:
|
||||
UserServiceBase(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names)
|
||||
: name_(name), arg_names_(arg_names) {
|
||||
UserServiceBase(std::string name, const std::array<std::string, sizeof...(Ts)> &arg_names)
|
||||
: name_(std::move(name)), arg_names_(arg_names) {
|
||||
this->key_ = fnv1_hash(this->name_);
|
||||
}
|
||||
|
||||
|
||||
@@ -167,6 +167,36 @@ void ComponentIterator::advance() {
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
case IteratorState::NUMBER:
|
||||
if (this->at_ >= App.get_numbers().size()) {
|
||||
advance_platform = true;
|
||||
} else {
|
||||
auto *number = App.get_numbers()[this->at_];
|
||||
if (number->is_internal()) {
|
||||
success = true;
|
||||
break;
|
||||
} else {
|
||||
success = this->on_number(number);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
case IteratorState::SELECT:
|
||||
if (this->at_ >= App.get_selects().size()) {
|
||||
advance_platform = true;
|
||||
} else {
|
||||
auto *select = App.get_selects()[this->at_];
|
||||
if (select->is_internal()) {
|
||||
success = true;
|
||||
break;
|
||||
} else {
|
||||
success = this->on_select(select);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case IteratorState::MAX:
|
||||
if (this->on_end()) {
|
||||
|
||||
@@ -47,6 +47,12 @@ class ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
virtual bool on_climate(climate::Climate *climate) = 0;
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
virtual bool on_number(number::Number *number) = 0;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
virtual bool on_select(select::Select *select) = 0;
|
||||
#endif
|
||||
virtual bool on_end();
|
||||
|
||||
@@ -81,6 +87,12 @@ class ComponentIterator {
|
||||
#endif
|
||||
#ifdef USE_CLIMATE
|
||||
CLIMATE,
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
NUMBER,
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
SELECT,
|
||||
#endif
|
||||
MAX,
|
||||
} state_{IteratorState::NONE};
|
||||
|
||||
@@ -11,7 +11,6 @@ from esphome.const import (
|
||||
CONF_DIV_RATIO,
|
||||
CONF_CAPACITANCE,
|
||||
)
|
||||
from esphome.core import coroutine
|
||||
|
||||
AUTO_LOAD = ["sensor", "binary_sensor"]
|
||||
MULTI_CONF = True
|
||||
@@ -40,11 +39,10 @@ AS3935_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
@coroutine
|
||||
def setup_as3935(var, config):
|
||||
yield cg.register_component(var, config)
|
||||
async def setup_as3935(var, config):
|
||||
await cg.register_component(var, config)
|
||||
|
||||
irq_pin = yield cg.gpio_pin_expression(config[CONF_IRQ_PIN])
|
||||
irq_pin = await cg.gpio_pin_expression(config[CONF_IRQ_PIN])
|
||||
cg.add(var.set_irq_pin(irq_pin))
|
||||
cg.add(var.set_indoor(config[CONF_INDOOR]))
|
||||
cg.add(var.set_noise_level(config[CONF_NOISE_LEVEL]))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace as3935 {
|
||||
|
||||
static const char *TAG = "as3935";
|
||||
static const char *const TAG = "as3935";
|
||||
|
||||
void AS3935Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up AS3935...");
|
||||
|
||||
@@ -12,7 +12,7 @@ CONFIG_SCHEMA = binary_sensor.BINARY_SENSOR_SCHEMA.extend(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = yield cg.get_variable(config[CONF_AS3935_ID])
|
||||
var = yield binary_sensor.new_binary_sensor(config)
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_AS3935_ID])
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
cg.add(hub.set_thunder_alert_binary_sensor(var))
|
||||
|
||||
@@ -4,9 +4,8 @@ from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_DISTANCE,
|
||||
CONF_LIGHTNING_ENERGY,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
STATE_CLASS_NONE,
|
||||
UNIT_KILOMETER,
|
||||
UNIT_EMPTY,
|
||||
ICON_SIGNAL_DISTANCE_VARIANT,
|
||||
ICON_FLASH,
|
||||
)
|
||||
@@ -18,24 +17,29 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_AS3935_ID): cv.use_id(AS3935),
|
||||
cv.Optional(CONF_DISTANCE): sensor.sensor_schema(
|
||||
UNIT_KILOMETER, ICON_SIGNAL_DISTANCE_VARIANT, 1, DEVICE_CLASS_EMPTY
|
||||
unit_of_measurement=UNIT_KILOMETER,
|
||||
icon=ICON_SIGNAL_DISTANCE_VARIANT,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
),
|
||||
cv.Optional(CONF_LIGHTNING_ENERGY): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_FLASH, 1, DEVICE_CLASS_EMPTY
|
||||
icon=ICON_FLASH,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
hub = yield cg.get_variable(config[CONF_AS3935_ID])
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_AS3935_ID])
|
||||
|
||||
if CONF_DISTANCE in config:
|
||||
conf = config[CONF_DISTANCE]
|
||||
distance_sensor = yield sensor.new_sensor(conf)
|
||||
distance_sensor = await sensor.new_sensor(conf)
|
||||
cg.add(hub.set_distance_sensor(distance_sensor))
|
||||
|
||||
if CONF_LIGHTNING_ENERGY in config:
|
||||
conf = config[CONF_LIGHTNING_ENERGY]
|
||||
lightning_energy_sensor = yield sensor.new_sensor(conf)
|
||||
lightning_energy_sensor = await sensor.new_sensor(conf)
|
||||
cg.add(hub.set_energy_sensor(lightning_energy_sensor))
|
||||
|
||||
@@ -20,7 +20,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield as3935.setup_as3935(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await as3935.setup_as3935(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace as3935_i2c {
|
||||
|
||||
static const char *TAG = "as3935_i2c";
|
||||
static const char *const TAG = "as3935_i2c";
|
||||
|
||||
void I2CAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits, uint8_t start_pos) {
|
||||
uint8_t write_reg;
|
||||
|
||||
@@ -20,7 +20,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield as3935.setup_as3935(var, config)
|
||||
yield spi.register_spi_device(var, config)
|
||||
await as3935.setup_as3935(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace as3935_spi {
|
||||
|
||||
static const char *TAG = "as3935_spi";
|
||||
static const char *const TAG = "as3935_spi";
|
||||
|
||||
void SPIAS3935Component::setup() {
|
||||
ESP_LOGI(TAG, "SPIAS3935Component setup started!");
|
||||
@@ -33,7 +33,7 @@ void SPIAS3935Component::write_register(uint8_t reg, uint8_t mask, uint8_t bits,
|
||||
uint8_t SPIAS3935Component::read_register(uint8_t reg) {
|
||||
uint8_t value = 0;
|
||||
this->enable();
|
||||
this->write_byte(reg |= SPI_READ_M);
|
||||
this->write_byte(reg | SPI_READ_M);
|
||||
value = this->read_byte();
|
||||
// According to datsheet, the chip select must be written HIGH, LOW, HIGH
|
||||
// to correctly end the READ command.
|
||||
|
||||
@@ -6,10 +6,10 @@ CODEOWNERS = ["@OttoWinter"]
|
||||
|
||||
|
||||
@coroutine_with_priority(200.0)
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
if CORE.is_esp32:
|
||||
# https://github.com/OttoWinter/AsyncTCP/blob/master/library.json
|
||||
cg.add_library("AsyncTCP-esphome", "1.1.1")
|
||||
# https://github.com/esphome/AsyncTCP/blob/master/library.json
|
||||
cg.add_library("esphome/AsyncTCP-esphome", "1.2.2")
|
||||
elif CORE.is_esp8266:
|
||||
# https://github.com/OttoWinter/ESPAsyncTCP
|
||||
cg.add_library("ESPAsyncTCP-esphome", "1.2.3")
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace esphome {
|
||||
namespace atc_mithermometer {
|
||||
|
||||
static const char *TAG = "atc_mithermometer";
|
||||
static const char *const TAG = "atc_mithermometer";
|
||||
|
||||
void ATCMiThermometer::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "ATC MiThermometer");
|
||||
|
||||
@@ -12,7 +12,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_PERCENT,
|
||||
UNIT_VOLT,
|
||||
@@ -33,16 +33,28 @@ CONFIG_SCHEMA = (
|
||||
cv.GenerateID(): cv.declare_id(ATCMiThermometer),
|
||||
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||
UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_HUMIDITY
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_BATTERY_LEVEL): sensor.sensor_schema(
|
||||
UNIT_PERCENT, ICON_EMPTY, 0, DEVICE_CLASS_BATTERY
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_BATTERY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
|
||||
UNIT_VOLT, ICON_EMPTY, 3, DEVICE_CLASS_VOLTAGE
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -51,22 +63,22 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield esp32_ble_tracker.register_ble_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
sens = await sensor.new_sensor(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature(sens))
|
||||
if CONF_HUMIDITY in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
sens = await sensor.new_sensor(config[CONF_HUMIDITY])
|
||||
cg.add(var.set_humidity(sens))
|
||||
if CONF_BATTERY_LEVEL in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
||||
sens = await sensor.new_sensor(config[CONF_BATTERY_LEVEL])
|
||||
cg.add(var.set_battery_level(sens))
|
||||
if CONF_BATTERY_VOLTAGE in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_BATTERY_VOLTAGE])
|
||||
sens = await sensor.new_sensor(config[CONF_BATTERY_VOLTAGE])
|
||||
cg.add(var.set_battery_voltage(sens))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
namespace esphome {
|
||||
namespace atm90e32 {
|
||||
|
||||
static const char *TAG = "atm90e32";
|
||||
static const char *const TAG = "atm90e32";
|
||||
|
||||
void ATM90E32Component::update() {
|
||||
if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) {
|
||||
@@ -58,6 +58,24 @@ void ATM90E32Component::update() {
|
||||
if (this->phase_[2].power_factor_sensor_ != nullptr) {
|
||||
this->phase_[2].power_factor_sensor_->publish_state(this->get_power_factor_c_());
|
||||
}
|
||||
if (this->phase_[0].forward_active_energy_sensor_ != nullptr) {
|
||||
this->phase_[0].forward_active_energy_sensor_->publish_state(this->get_forward_active_energy_a_());
|
||||
}
|
||||
if (this->phase_[1].forward_active_energy_sensor_ != nullptr) {
|
||||
this->phase_[1].forward_active_energy_sensor_->publish_state(this->get_forward_active_energy_b_());
|
||||
}
|
||||
if (this->phase_[2].forward_active_energy_sensor_ != nullptr) {
|
||||
this->phase_[2].forward_active_energy_sensor_->publish_state(this->get_forward_active_energy_c_());
|
||||
}
|
||||
if (this->phase_[0].reverse_active_energy_sensor_ != nullptr) {
|
||||
this->phase_[0].reverse_active_energy_sensor_->publish_state(this->get_reverse_active_energy_a_());
|
||||
}
|
||||
if (this->phase_[1].reverse_active_energy_sensor_ != nullptr) {
|
||||
this->phase_[1].reverse_active_energy_sensor_->publish_state(this->get_reverse_active_energy_b_());
|
||||
}
|
||||
if (this->phase_[2].reverse_active_energy_sensor_ != nullptr) {
|
||||
this->phase_[2].reverse_active_energy_sensor_->publish_state(this->get_reverse_active_energy_c_());
|
||||
}
|
||||
if (this->freq_sensor_ != nullptr) {
|
||||
this->freq_sensor_->publish_state(this->get_frequency_());
|
||||
}
|
||||
@@ -119,16 +137,22 @@ void ATM90E32Component::dump_config() {
|
||||
LOG_SENSOR(" ", "Power A", this->phase_[0].power_sensor_);
|
||||
LOG_SENSOR(" ", "Reactive Power A", this->phase_[0].reactive_power_sensor_);
|
||||
LOG_SENSOR(" ", "PF A", this->phase_[0].power_factor_sensor_);
|
||||
LOG_SENSOR(" ", "Active Forward Energy A", this->phase_[0].forward_active_energy_sensor_);
|
||||
LOG_SENSOR(" ", "Active Reverse Energy A", this->phase_[0].reverse_active_energy_sensor_);
|
||||
LOG_SENSOR(" ", "Voltage B", this->phase_[1].voltage_sensor_);
|
||||
LOG_SENSOR(" ", "Current B", this->phase_[1].current_sensor_);
|
||||
LOG_SENSOR(" ", "Power B", this->phase_[1].power_sensor_);
|
||||
LOG_SENSOR(" ", "Reactive Power B", this->phase_[1].reactive_power_sensor_);
|
||||
LOG_SENSOR(" ", "PF B", this->phase_[1].power_factor_sensor_);
|
||||
LOG_SENSOR(" ", "Active Forward Energy B", this->phase_[1].forward_active_energy_sensor_);
|
||||
LOG_SENSOR(" ", "Active Reverse Energy B", this->phase_[1].reverse_active_energy_sensor_);
|
||||
LOG_SENSOR(" ", "Voltage C", this->phase_[2].voltage_sensor_);
|
||||
LOG_SENSOR(" ", "Current C", this->phase_[2].current_sensor_);
|
||||
LOG_SENSOR(" ", "Power C", this->phase_[2].power_sensor_);
|
||||
LOG_SENSOR(" ", "Reactive Power C", this->phase_[2].reactive_power_sensor_);
|
||||
LOG_SENSOR(" ", "PF C", this->phase_[2].power_factor_sensor_);
|
||||
LOG_SENSOR(" ", "Active Forward Energy C", this->phase_[2].forward_active_energy_sensor_);
|
||||
LOG_SENSOR(" ", "Active Reverse Energy C", this->phase_[2].reverse_active_energy_sensor_);
|
||||
LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
|
||||
LOG_SENSOR(" ", "Chip Temp", this->chip_temperature_sensor_);
|
||||
}
|
||||
@@ -239,6 +263,30 @@ float ATM90E32Component::get_power_factor_c_() {
|
||||
int16_t pf = this->read16_(ATM90E32_REGISTER_PFMEANC);
|
||||
return (float) pf / 1000;
|
||||
}
|
||||
float ATM90E32Component::get_forward_active_energy_a_() {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYA);
|
||||
return (float) val * 10 / 3200; // convert register value to WattHours
|
||||
}
|
||||
float ATM90E32Component::get_forward_active_energy_b_() {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYB);
|
||||
return (float) val * 10 / 3200;
|
||||
}
|
||||
float ATM90E32Component::get_forward_active_energy_c_() {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_APENERGYC);
|
||||
return (float) val * 10 / 3200;
|
||||
}
|
||||
float ATM90E32Component::get_reverse_active_energy_a_() {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYA);
|
||||
return (float) val * 10 / 3200;
|
||||
}
|
||||
float ATM90E32Component::get_reverse_active_energy_b_() {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYB);
|
||||
return (float) val * 10 / 3200;
|
||||
}
|
||||
float ATM90E32Component::get_reverse_active_energy_c_() {
|
||||
uint16_t val = this->read16_(ATM90E32_REGISTER_ANENERGYC);
|
||||
return (float) val * 10 / 3200;
|
||||
}
|
||||
float ATM90E32Component::get_frequency_() {
|
||||
uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ);
|
||||
return (float) freq / 100;
|
||||
|
||||
@@ -20,6 +20,12 @@ class ATM90E32Component : public PollingComponent,
|
||||
void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; }
|
||||
void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; }
|
||||
void set_reactive_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].reactive_power_sensor_ = obj; }
|
||||
void set_forward_active_energy_sensor(int phase, sensor::Sensor *obj) {
|
||||
this->phase_[phase].forward_active_energy_sensor_ = obj;
|
||||
}
|
||||
void set_reverse_active_energy_sensor(int phase, sensor::Sensor *obj) {
|
||||
this->phase_[phase].reverse_active_energy_sensor_ = obj;
|
||||
}
|
||||
void set_power_factor_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_factor_sensor_ = obj; }
|
||||
void set_volt_gain(int phase, uint16_t gain) { this->phase_[phase].volt_gain_ = gain; }
|
||||
void set_ct_gain(int phase, uint16_t gain) { this->phase_[phase].ct_gain_ = gain; }
|
||||
@@ -52,6 +58,12 @@ class ATM90E32Component : public PollingComponent,
|
||||
float get_power_factor_a_();
|
||||
float get_power_factor_b_();
|
||||
float get_power_factor_c_();
|
||||
float get_forward_active_energy_a_();
|
||||
float get_forward_active_energy_b_();
|
||||
float get_forward_active_energy_c_();
|
||||
float get_reverse_active_energy_a_();
|
||||
float get_reverse_active_energy_b_();
|
||||
float get_reverse_active_energy_c_();
|
||||
float get_frequency_();
|
||||
float get_chip_temperature_();
|
||||
|
||||
@@ -63,6 +75,8 @@ class ATM90E32Component : public PollingComponent,
|
||||
sensor::Sensor *power_sensor_{nullptr};
|
||||
sensor::Sensor *reactive_power_sensor_{nullptr};
|
||||
sensor::Sensor *power_factor_sensor_{nullptr};
|
||||
sensor::Sensor *forward_active_energy_sensor_{nullptr};
|
||||
sensor::Sensor *reverse_active_energy_sensor_{nullptr};
|
||||
} phase_[3];
|
||||
sensor::Sensor *freq_sensor_{nullptr};
|
||||
sensor::Sensor *chip_temperature_sensor_{nullptr};
|
||||
|
||||
@@ -39,7 +39,7 @@ static const uint16_t ATM90E32_STATUS_S0_OVPHASEBST = 1 << 11; // Over voltage
|
||||
static const uint16_t ATM90E32_STATUS_S0_OVPHASECST = 1 << 10; // Over voltage on phase C
|
||||
static const uint16_t ATM90E32_STATUS_S0_UREVWNST = 1 << 9; // Voltage Phase Sequence Error status
|
||||
static const uint16_t ATM90E32_STATUS_S0_IREVWNST = 1 << 8; // Current Phase Sequence Error status
|
||||
static const uint16_t ATM90E32_STATUS_S0_INOV0ST = 1 << 7; // Calculated N line current greater tha INWarnTh reg
|
||||
static const uint16_t ATM90E32_STATUS_S0_INOV0ST = 1 << 7; // Calculated N line current greater than INWarnTh reg
|
||||
static const uint16_t ATM90E32_STATUS_S0_TQNOLOADST = 1 << 6; // All phase sum reactive power no-load condition status
|
||||
static const uint16_t ATM90E32_STATUS_S0_TPNOLOADST = 1 << 5; // All phase sum active power no-load condition status
|
||||
static const uint16_t ATM90E32_STATUS_S0_TASNOLOADST = 1 << 4; // All phase sum apparent power no-load status
|
||||
|
||||
@@ -3,34 +3,37 @@ import esphome.config_validation as cv
|
||||
from esphome.components import sensor, spi
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_REACTIVE_POWER,
|
||||
CONF_VOLTAGE,
|
||||
CONF_CURRENT,
|
||||
CONF_POWER,
|
||||
CONF_POWER_FACTOR,
|
||||
CONF_FREQUENCY,
|
||||
CONF_FORWARD_ACTIVE_ENERGY,
|
||||
CONF_REVERSE_ACTIVE_ENERGY,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_POWER_FACTOR,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_EMPTY,
|
||||
ICON_LIGHTBULB,
|
||||
ICON_CURRENT_AC,
|
||||
LAST_RESET_TYPE_AUTO,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_HERTZ,
|
||||
UNIT_VOLT,
|
||||
UNIT_AMPERE,
|
||||
UNIT_WATT,
|
||||
UNIT_EMPTY,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_VOLT_AMPS_REACTIVE,
|
||||
UNIT_WATT_HOURS,
|
||||
)
|
||||
|
||||
CONF_PHASE_A = "phase_a"
|
||||
CONF_PHASE_B = "phase_b"
|
||||
CONF_PHASE_C = "phase_c"
|
||||
|
||||
CONF_REACTIVE_POWER = "reactive_power"
|
||||
CONF_LINE_FREQUENCY = "line_frequency"
|
||||
CONF_CHIP_TEMPERATURE = "chip_temperature"
|
||||
CONF_GAIN_PGA = "gain_pga"
|
||||
@@ -59,19 +62,47 @@ ATM90E32Component = atm90e32_ns.class_(
|
||||
ATM90E32_PHASE_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||
UNIT_VOLT, ICON_EMPTY, 2, DEVICE_CLASS_VOLTAGE
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
|
||||
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_POWER): sensor.sensor_schema(
|
||||
UNIT_WATT, ICON_EMPTY, 2, DEVICE_CLASS_POWER
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(
|
||||
UNIT_VOLT_AMPS_REACTIVE, ICON_LIGHTBULB, 2, DEVICE_CLASS_EMPTY
|
||||
unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
|
||||
icon=ICON_LIGHTBULB,
|
||||
accuracy_decimals=2,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_EMPTY, 2, DEVICE_CLASS_POWER_FACTOR
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_POWER_FACTOR,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_WATT_HOURS,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
last_reset_type=LAST_RESET_TYPE_AUTO,
|
||||
),
|
||||
cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_WATT_HOURS,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_ENERGY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
last_reset_type=LAST_RESET_TYPE_AUTO,
|
||||
),
|
||||
cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t,
|
||||
cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t,
|
||||
@@ -86,10 +117,16 @@ CONFIG_SCHEMA = (
|
||||
cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA,
|
||||
cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA,
|
||||
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
|
||||
UNIT_HERTZ, ICON_CURRENT_AC, 1, DEVICE_CLASS_EMPTY
|
||||
unit_of_measurement=UNIT_HERTZ,
|
||||
icon=ICON_CURRENT_AC,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True),
|
||||
cv.Optional(CONF_CURRENT_PHASES, default="3"): cv.enum(
|
||||
@@ -103,10 +140,10 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield spi.register_spi_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
|
||||
for i, phase in enumerate([CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]):
|
||||
if phase not in config:
|
||||
@@ -115,25 +152,31 @@ def to_code(config):
|
||||
cg.add(var.set_volt_gain(i, conf[CONF_GAIN_VOLTAGE]))
|
||||
cg.add(var.set_ct_gain(i, conf[CONF_GAIN_CT]))
|
||||
if CONF_VOLTAGE in conf:
|
||||
sens = yield sensor.new_sensor(conf[CONF_VOLTAGE])
|
||||
sens = await sensor.new_sensor(conf[CONF_VOLTAGE])
|
||||
cg.add(var.set_voltage_sensor(i, sens))
|
||||
if CONF_CURRENT in conf:
|
||||
sens = yield sensor.new_sensor(conf[CONF_CURRENT])
|
||||
sens = await sensor.new_sensor(conf[CONF_CURRENT])
|
||||
cg.add(var.set_current_sensor(i, sens))
|
||||
if CONF_POWER in conf:
|
||||
sens = yield sensor.new_sensor(conf[CONF_POWER])
|
||||
sens = await sensor.new_sensor(conf[CONF_POWER])
|
||||
cg.add(var.set_power_sensor(i, sens))
|
||||
if CONF_REACTIVE_POWER in conf:
|
||||
sens = yield sensor.new_sensor(conf[CONF_REACTIVE_POWER])
|
||||
sens = await sensor.new_sensor(conf[CONF_REACTIVE_POWER])
|
||||
cg.add(var.set_reactive_power_sensor(i, sens))
|
||||
if CONF_POWER_FACTOR in conf:
|
||||
sens = yield sensor.new_sensor(conf[CONF_POWER_FACTOR])
|
||||
sens = await sensor.new_sensor(conf[CONF_POWER_FACTOR])
|
||||
cg.add(var.set_power_factor_sensor(i, sens))
|
||||
if CONF_FORWARD_ACTIVE_ENERGY in conf:
|
||||
sens = await sensor.new_sensor(conf[CONF_FORWARD_ACTIVE_ENERGY])
|
||||
cg.add(var.set_forward_active_energy_sensor(i, sens))
|
||||
if CONF_REVERSE_ACTIVE_ENERGY in conf:
|
||||
sens = await sensor.new_sensor(conf[CONF_REVERSE_ACTIVE_ENERGY])
|
||||
cg.add(var.set_reverse_active_energy_sensor(i, sens))
|
||||
if CONF_FREQUENCY in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_FREQUENCY])
|
||||
sens = await sensor.new_sensor(config[CONF_FREQUENCY])
|
||||
cg.add(var.set_freq_sensor(sens))
|
||||
if CONF_CHIP_TEMPERATURE in config:
|
||||
sens = yield sensor.new_sensor(config[CONF_CHIP_TEMPERATURE])
|
||||
sens = await sensor.new_sensor(config[CONF_CHIP_TEMPERATURE])
|
||||
cg.add(var.set_chip_temperature_sensor(sens))
|
||||
cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
|
||||
cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))
|
||||
|
||||
82
esphome/components/b_parasite/b_parasite.cpp
Normal file
82
esphome/components/b_parasite/b_parasite.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "b_parasite.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace b_parasite {
|
||||
|
||||
static const char *const TAG = "b_parasite";
|
||||
|
||||
void BParasite::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "b_parasite");
|
||||
LOG_SENSOR(" ", "Battery Voltage", this->battery_voltage_);
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_);
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_);
|
||||
LOG_SENSOR(" ", "Soil Moisture", this->soil_moisture_);
|
||||
}
|
||||
|
||||
bool BParasite::parse_device(const esp32_ble_tracker::ESPBTDevice &device) {
|
||||
if (device.address_uint64() != address_) {
|
||||
ESP_LOGVV(TAG, "parse_device(): unknown MAC address.");
|
||||
return false;
|
||||
}
|
||||
ESP_LOGVV(TAG, "parse_device(): MAC address %s found.", device.address_str().c_str());
|
||||
const auto &service_datas = device.get_service_datas();
|
||||
if (service_datas.size() != 1) {
|
||||
ESP_LOGE(TAG, "Unexpected service_datas size (%d)", service_datas.size());
|
||||
return false;
|
||||
}
|
||||
const auto &service_data = service_datas[0];
|
||||
|
||||
ESP_LOGVV(TAG, "Service data:");
|
||||
for (const uint8_t byte : service_data.data) {
|
||||
ESP_LOGVV(TAG, "0x%02x", byte);
|
||||
}
|
||||
|
||||
const auto &data = service_data.data;
|
||||
|
||||
// Counter for deduplicating messages.
|
||||
uint8_t counter = data[1] & 0x0f;
|
||||
if (last_processed_counter_ == counter) {
|
||||
ESP_LOGVV(TAG, "Skipping already processed counter (%u)", counter);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Battery voltage in millivolts.
|
||||
uint16_t battery_millivolt = data[2] << 8 | data[3];
|
||||
float battery_voltage = battery_millivolt / 1000.0f;
|
||||
|
||||
// Temperature in 1000 * Celsius.
|
||||
uint16_t temp_millicelcius = data[4] << 8 | data[5];
|
||||
float temp_celcius = temp_millicelcius / 1000.0f;
|
||||
|
||||
// Relative air humidity in the range [0, 2^16).
|
||||
uint16_t humidity = data[6] << 8 | data[7];
|
||||
float humidity_percent = (100.0f * humidity) / (1 << 16);
|
||||
|
||||
// Relative soil moisture in [0 - 2^16).
|
||||
uint16_t soil_moisture = data[8] << 8 | data[9];
|
||||
float moisture_percent = (100.0f * soil_moisture) / (1 << 16);
|
||||
|
||||
if (battery_voltage_ != nullptr) {
|
||||
battery_voltage_->publish_state(battery_voltage);
|
||||
}
|
||||
if (temperature_ != nullptr) {
|
||||
temperature_->publish_state(temp_celcius);
|
||||
}
|
||||
if (humidity_ != nullptr) {
|
||||
humidity_->publish_state(humidity_percent);
|
||||
}
|
||||
if (soil_moisture_ != nullptr) {
|
||||
soil_moisture_->publish_state(moisture_percent);
|
||||
}
|
||||
|
||||
last_processed_counter_ = counter;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace b_parasite
|
||||
} // namespace esphome
|
||||
|
||||
#endif // ARDUINO_ARCH_ESP32
|
||||
40
esphome/components/b_parasite/b_parasite.h
Normal file
40
esphome/components/b_parasite/b_parasite.h
Normal file
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace b_parasite {
|
||||
|
||||
class BParasite : public Component, public esp32_ble_tracker::ESPBTDeviceListener {
|
||||
public:
|
||||
void set_address(uint64_t address) { address_ = address; };
|
||||
void set_bindkey(const std::string &bindkey);
|
||||
|
||||
bool parse_device(const esp32_ble_tracker::ESPBTDevice &device) override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
void set_battery_voltage(sensor::Sensor *battery_voltage) { battery_voltage_ = battery_voltage; }
|
||||
void set_temperature(sensor::Sensor *temperature) { temperature_ = temperature; }
|
||||
void set_humidity(sensor::Sensor *humidity) { humidity_ = humidity; }
|
||||
void set_soil_moisture(sensor::Sensor *soil_moisture) { soil_moisture_ = soil_moisture; }
|
||||
|
||||
protected:
|
||||
// The received advertisement packet contains an unsigned 4 bits wrap-around counter
|
||||
// for deduplicating messages.
|
||||
int8_t last_processed_counter_ = -1;
|
||||
uint64_t address_;
|
||||
sensor::Sensor *battery_voltage_{nullptr};
|
||||
sensor::Sensor *temperature_{nullptr};
|
||||
sensor::Sensor *humidity_{nullptr};
|
||||
sensor::Sensor *soil_moisture_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace b_parasite
|
||||
} // namespace esphome
|
||||
|
||||
#endif // ARDUINO_ARCH_ESP32
|
||||
80
esphome/components/b_parasite/sensor.py
Normal file
80
esphome/components/b_parasite/sensor.py
Normal file
@@ -0,0 +1,80 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, esp32_ble_tracker
|
||||
from esphome.const import (
|
||||
CONF_BATTERY_VOLTAGE,
|
||||
CONF_HUMIDITY,
|
||||
CONF_ID,
|
||||
CONF_MOISTURE,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_PERCENT,
|
||||
UNIT_VOLT,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@rbaron"]
|
||||
|
||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||
|
||||
b_parasite_ns = cg.esphome_ns.namespace("b_parasite")
|
||||
BParasite = b_parasite_ns.class_(
|
||||
"BParasite", esp32_ble_tracker.ESPBTDeviceListener, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BParasite),
|
||||
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_BATTERY_VOLTAGE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_MOISTURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
|
||||
for (config_key, setter) in [
|
||||
(CONF_TEMPERATURE, var.set_temperature),
|
||||
(CONF_HUMIDITY, var.set_humidity),
|
||||
(CONF_BATTERY_VOLTAGE, var.set_battery_voltage),
|
||||
(CONF_MOISTURE, var.set_soil_moisture),
|
||||
]:
|
||||
if config_key in config:
|
||||
sens = await sensor.new_sensor(config[config_key])
|
||||
cg.add(setter(sens))
|
||||
0
esphome/components/ballu/__init__.py
Normal file
0
esphome/components/ballu/__init__.py
Normal file
239
esphome/components/ballu/ballu.cpp
Normal file
239
esphome/components/ballu/ballu.cpp
Normal file
@@ -0,0 +1,239 @@
|
||||
#include "ballu.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ballu {
|
||||
|
||||
static const char *const TAG = "ballu.climate";
|
||||
|
||||
const uint16_t BALLU_HEADER_MARK = 9000;
|
||||
const uint16_t BALLU_HEADER_SPACE = 4500;
|
||||
const uint16_t BALLU_BIT_MARK = 575;
|
||||
const uint16_t BALLU_ONE_SPACE = 1675;
|
||||
const uint16_t BALLU_ZERO_SPACE = 550;
|
||||
|
||||
const uint32_t BALLU_CARRIER_FREQUENCY = 38000;
|
||||
|
||||
const uint8_t BALLU_STATE_LENGTH = 13;
|
||||
|
||||
const uint8_t BALLU_AUTO = 0;
|
||||
const uint8_t BALLU_COOL = 0x20;
|
||||
const uint8_t BALLU_DRY = 0x40;
|
||||
const uint8_t BALLU_HEAT = 0x80;
|
||||
const uint8_t BALLU_FAN = 0xc0;
|
||||
|
||||
const uint8_t BALLU_FAN_AUTO = 0xa0;
|
||||
const uint8_t BALLU_FAN_HIGH = 0x20;
|
||||
const uint8_t BALLU_FAN_MED = 0x40;
|
||||
const uint8_t BALLU_FAN_LOW = 0x60;
|
||||
|
||||
const uint8_t BALLU_SWING_VER = 0x07;
|
||||
const uint8_t BALLU_SWING_HOR = 0xe0;
|
||||
const uint8_t BALLU_POWER = 0x20;
|
||||
|
||||
void BalluClimate::transmit_state() {
|
||||
uint8_t remote_state[BALLU_STATE_LENGTH] = {0};
|
||||
|
||||
auto temp = (uint8_t) roundf(clamp(this->target_temperature, YKR_K_002E_TEMP_MIN, YKR_K_002E_TEMP_MAX));
|
||||
auto swing_ver =
|
||||
((this->swing_mode == climate::CLIMATE_SWING_VERTICAL) || (this->swing_mode == climate::CLIMATE_SWING_BOTH));
|
||||
auto swing_hor =
|
||||
((this->swing_mode == climate::CLIMATE_SWING_HORIZONTAL) || (this->swing_mode == climate::CLIMATE_SWING_BOTH));
|
||||
|
||||
remote_state[0] = 0xc3;
|
||||
remote_state[1] = ((temp - 8) << 3) | (swing_ver ? 0 : BALLU_SWING_VER);
|
||||
remote_state[2] = swing_hor ? 0 : BALLU_SWING_HOR;
|
||||
remote_state[9] = (this->mode == climate::CLIMATE_MODE_OFF) ? 0 : BALLU_POWER;
|
||||
remote_state[11] = 0x1e;
|
||||
|
||||
// Fan speed
|
||||
switch (this->fan_mode.value()) {
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
remote_state[4] |= BALLU_FAN_HIGH;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
remote_state[4] |= BALLU_FAN_MED;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
remote_state[4] |= BALLU_FAN_LOW;
|
||||
break;
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
remote_state[4] |= BALLU_FAN_AUTO;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Mode
|
||||
switch (this->mode) {
|
||||
case climate::CLIMATE_MODE_AUTO:
|
||||
remote_state[6] |= BALLU_AUTO;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
remote_state[6] |= BALLU_HEAT;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_COOL:
|
||||
remote_state[6] |= BALLU_COOL;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_DRY:
|
||||
remote_state[6] |= BALLU_DRY;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||
remote_state[6] |= BALLU_FAN;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_OFF:
|
||||
remote_state[6] |= BALLU_AUTO;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Checksum
|
||||
for (uint8_t i = 0; i < BALLU_STATE_LENGTH - 1; i++)
|
||||
remote_state[12] += remote_state[i];
|
||||
|
||||
ESP_LOGV(TAG, "Sending: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", remote_state[0],
|
||||
remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5], remote_state[6],
|
||||
remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11], remote_state[12]);
|
||||
|
||||
// Send code
|
||||
auto transmit = this->transmitter_->transmit();
|
||||
auto data = transmit.get_data();
|
||||
|
||||
data->set_carrier_frequency(38000);
|
||||
|
||||
// Header
|
||||
data->mark(BALLU_HEADER_MARK);
|
||||
data->space(BALLU_HEADER_SPACE);
|
||||
// Data
|
||||
for (uint8_t i : remote_state) {
|
||||
for (uint8_t j = 0; j < 8; j++) {
|
||||
data->mark(BALLU_BIT_MARK);
|
||||
bool bit = i & (1 << j);
|
||||
data->space(bit ? BALLU_ONE_SPACE : BALLU_ZERO_SPACE);
|
||||
}
|
||||
}
|
||||
// Footer
|
||||
data->mark(BALLU_BIT_MARK);
|
||||
|
||||
transmit.perform();
|
||||
}
|
||||
|
||||
bool BalluClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
// Validate header
|
||||
if (!data.expect_item(BALLU_HEADER_MARK, BALLU_HEADER_SPACE)) {
|
||||
ESP_LOGV(TAG, "Header fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t remote_state[BALLU_STATE_LENGTH] = {0};
|
||||
// Read all bytes.
|
||||
for (int i = 0; i < BALLU_STATE_LENGTH; i++) {
|
||||
// Read bit
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (data.expect_item(BALLU_BIT_MARK, BALLU_ONE_SPACE))
|
||||
remote_state[i] |= 1 << j;
|
||||
|
||||
else if (!data.expect_item(BALLU_BIT_MARK, BALLU_ZERO_SPACE)) {
|
||||
ESP_LOGV(TAG, "Byte %d bit %d fail", i, j);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGVV(TAG, "Byte %d %02X", i, remote_state[i]);
|
||||
}
|
||||
// Validate footer
|
||||
if (!data.expect_mark(BALLU_BIT_MARK)) {
|
||||
ESP_LOGV(TAG, "Footer fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t checksum = 0;
|
||||
// Calculate checksum and compare with signal value.
|
||||
for (uint8_t i = 0; i < BALLU_STATE_LENGTH - 1; i++)
|
||||
checksum += remote_state[i];
|
||||
|
||||
if (checksum != remote_state[BALLU_STATE_LENGTH - 1]) {
|
||||
ESP_LOGVV(TAG, "Checksum fail");
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Received: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", remote_state[0],
|
||||
remote_state[1], remote_state[2], remote_state[3], remote_state[4], remote_state[5], remote_state[6],
|
||||
remote_state[7], remote_state[8], remote_state[9], remote_state[10], remote_state[11], remote_state[12]);
|
||||
|
||||
// verify header remote code
|
||||
if (remote_state[0] != 0xc3)
|
||||
return false;
|
||||
|
||||
// powr on/off button
|
||||
ESP_LOGV(TAG, "Power: %02X", (remote_state[9] & BALLU_POWER));
|
||||
|
||||
if ((remote_state[9] & BALLU_POWER) != BALLU_POWER) {
|
||||
this->mode = climate::CLIMATE_MODE_OFF;
|
||||
} else {
|
||||
auto mode = remote_state[6] & 0xe0;
|
||||
ESP_LOGV(TAG, "Mode: %02X", mode);
|
||||
switch (mode) {
|
||||
case BALLU_HEAT:
|
||||
this->mode = climate::CLIMATE_MODE_HEAT;
|
||||
break;
|
||||
case BALLU_COOL:
|
||||
this->mode = climate::CLIMATE_MODE_COOL;
|
||||
break;
|
||||
case BALLU_DRY:
|
||||
this->mode = climate::CLIMATE_MODE_DRY;
|
||||
break;
|
||||
case BALLU_FAN:
|
||||
this->mode = climate::CLIMATE_MODE_FAN_ONLY;
|
||||
break;
|
||||
case BALLU_AUTO:
|
||||
this->mode = climate::CLIMATE_MODE_AUTO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set received temp
|
||||
int temp = remote_state[1] & 0xf8;
|
||||
ESP_LOGVV(TAG, "Temperature Raw: %02X", temp);
|
||||
temp = ((uint8_t) temp >> 3) + 8;
|
||||
ESP_LOGVV(TAG, "Temperature Climate: %u", temp);
|
||||
this->target_temperature = temp;
|
||||
|
||||
// Set received fan speed
|
||||
auto fan = remote_state[4] & 0xe0;
|
||||
ESP_LOGVV(TAG, "Fan: %02X", fan);
|
||||
switch (fan) {
|
||||
case BALLU_FAN_HIGH:
|
||||
this->fan_mode = climate::CLIMATE_FAN_HIGH;
|
||||
break;
|
||||
case BALLU_FAN_MED:
|
||||
this->fan_mode = climate::CLIMATE_FAN_MEDIUM;
|
||||
break;
|
||||
case BALLU_FAN_LOW:
|
||||
this->fan_mode = climate::CLIMATE_FAN_LOW;
|
||||
break;
|
||||
case BALLU_FAN_AUTO:
|
||||
default:
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
break;
|
||||
}
|
||||
|
||||
// Set received swing status
|
||||
ESP_LOGVV(TAG, "Swing status: %02X %02X", remote_state[1] & BALLU_SWING_VER, remote_state[2] & BALLU_SWING_HOR);
|
||||
if (((remote_state[1] & BALLU_SWING_VER) != BALLU_SWING_VER) &&
|
||||
((remote_state[2] & BALLU_SWING_HOR) != BALLU_SWING_HOR)) {
|
||||
this->swing_mode = climate::CLIMATE_SWING_BOTH;
|
||||
} else if ((remote_state[1] & BALLU_SWING_VER) != BALLU_SWING_VER) {
|
||||
this->swing_mode = climate::CLIMATE_SWING_VERTICAL;
|
||||
} else if ((remote_state[2] & BALLU_SWING_HOR) != BALLU_SWING_HOR) {
|
||||
this->swing_mode = climate::CLIMATE_SWING_HORIZONTAL;
|
||||
} else {
|
||||
this->swing_mode = climate::CLIMATE_SWING_OFF;
|
||||
}
|
||||
|
||||
this->publish_state();
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace ballu
|
||||
} // namespace esphome
|
||||
31
esphome/components/ballu/ballu.h
Normal file
31
esphome/components/ballu/ballu.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/climate_ir/climate_ir.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace ballu {
|
||||
|
||||
// Support for Ballu air conditioners with YKR-K/002E remote
|
||||
|
||||
// Temperature
|
||||
const float YKR_K_002E_TEMP_MIN = 16.0;
|
||||
const float YKR_K_002E_TEMP_MAX = 32.0;
|
||||
|
||||
class BalluClimate : public climate_ir::ClimateIR {
|
||||
public:
|
||||
BalluClimate()
|
||||
: climate_ir::ClimateIR(YKR_K_002E_TEMP_MIN, YKR_K_002E_TEMP_MAX, 1.0f, true, true,
|
||||
{climate::CLIMATE_FAN_AUTO, climate::CLIMATE_FAN_LOW, climate::CLIMATE_FAN_MEDIUM,
|
||||
climate::CLIMATE_FAN_HIGH},
|
||||
{climate::CLIMATE_SWING_OFF, climate::CLIMATE_SWING_VERTICAL,
|
||||
climate::CLIMATE_SWING_HORIZONTAL, climate::CLIMATE_SWING_BOTH}) {}
|
||||
|
||||
protected:
|
||||
/// Transmit via IR the state of this climate controller.
|
||||
void transmit_state() override;
|
||||
/// Handle received IR Buffer
|
||||
bool on_receive(remote_base::RemoteReceiveData data) override;
|
||||
};
|
||||
|
||||
} // namespace ballu
|
||||
} // namespace esphome
|
||||
21
esphome/components/ballu/climate.py
Normal file
21
esphome/components/ballu/climate.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import climate_ir
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
AUTO_LOAD = ["climate_ir"]
|
||||
CODEOWNERS = ["@bazuchan"]
|
||||
|
||||
ballu_ns = cg.esphome_ns.namespace("ballu")
|
||||
BalluClimate = ballu_ns.class_("BalluClimate", climate_ir.ClimateIR)
|
||||
|
||||
CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BalluClimate),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await climate_ir.register_climate_ir(var, config)
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace bang_bang {
|
||||
|
||||
static const char *TAG = "bang_bang.climate";
|
||||
static const char *const TAG = "bang_bang.climate";
|
||||
|
||||
void BangBangClimate::setup() {
|
||||
this->sensor_->add_on_state_callback([this](float state) {
|
||||
@@ -21,7 +21,12 @@ void BangBangClimate::setup() {
|
||||
restore->to_call(this).perform();
|
||||
} else {
|
||||
// restore from defaults, change_away handles those for us
|
||||
this->mode = climate::CLIMATE_MODE_AUTO;
|
||||
if (supports_cool_ && supports_heat_)
|
||||
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
|
||||
else if (supports_cool_)
|
||||
this->mode = climate::CLIMATE_MODE_COOL;
|
||||
else if (supports_heat_)
|
||||
this->mode = climate::CLIMATE_MODE_HEAT;
|
||||
this->change_away_(false);
|
||||
}
|
||||
}
|
||||
@@ -32,8 +37,8 @@ void BangBangClimate::control(const climate::ClimateCall &call) {
|
||||
this->target_temperature_low = *call.get_target_temperature_low();
|
||||
if (call.get_target_temperature_high().has_value())
|
||||
this->target_temperature_high = *call.get_target_temperature_high();
|
||||
if (call.get_away().has_value())
|
||||
this->change_away_(*call.get_away());
|
||||
if (call.get_preset().has_value())
|
||||
this->change_away_(*call.get_preset() == climate::CLIMATE_PRESET_AWAY);
|
||||
|
||||
this->compute_state_();
|
||||
this->publish_state();
|
||||
@@ -41,21 +46,27 @@ void BangBangClimate::control(const climate::ClimateCall &call) {
|
||||
climate::ClimateTraits BangBangClimate::traits() {
|
||||
auto traits = climate::ClimateTraits();
|
||||
traits.set_supports_current_temperature(true);
|
||||
traits.set_supports_auto_mode(true);
|
||||
traits.set_supports_cool_mode(this->supports_cool_);
|
||||
traits.set_supports_heat_mode(this->supports_heat_);
|
||||
traits.set_supported_modes({
|
||||
climate::CLIMATE_MODE_OFF,
|
||||
});
|
||||
if (supports_cool_)
|
||||
traits.add_supported_mode(climate::CLIMATE_MODE_COOL);
|
||||
if (supports_heat_)
|
||||
traits.add_supported_mode(climate::CLIMATE_MODE_HEAT);
|
||||
if (supports_cool_ && supports_heat_)
|
||||
traits.add_supported_mode(climate::CLIMATE_MODE_HEAT_COOL);
|
||||
traits.set_supports_two_point_target_temperature(true);
|
||||
traits.set_supports_away(this->supports_away_);
|
||||
if (supports_away_)
|
||||
traits.set_supported_presets({
|
||||
climate::CLIMATE_PRESET_HOME,
|
||||
climate::CLIMATE_PRESET_AWAY,
|
||||
});
|
||||
traits.set_supports_action(true);
|
||||
return traits;
|
||||
}
|
||||
void BangBangClimate::compute_state_() {
|
||||
if (this->mode != climate::CLIMATE_MODE_AUTO) {
|
||||
// in non-auto mode, switch directly to appropriate action
|
||||
// - HEAT mode -> HEATING action
|
||||
// - COOL mode -> COOLING action
|
||||
// - OFF mode -> OFF action (not IDLE!)
|
||||
this->switch_to_action_(static_cast<climate::ClimateAction>(this->mode));
|
||||
if (this->mode == climate::CLIMATE_MODE_OFF) {
|
||||
this->switch_to_action_(climate::CLIMATE_ACTION_OFF);
|
||||
return;
|
||||
}
|
||||
if (isnan(this->current_temperature) || isnan(this->target_temperature_low) || isnan(this->target_temperature_high)) {
|
||||
@@ -140,7 +151,7 @@ void BangBangClimate::change_away_(bool away) {
|
||||
this->target_temperature_low = this->away_config_.default_temperature_low;
|
||||
this->target_temperature_high = this->away_config_.default_temperature_high;
|
||||
}
|
||||
this->away = away;
|
||||
this->preset = away ? climate::CLIMATE_PRESET_AWAY : climate::CLIMATE_PRESET_HOME;
|
||||
}
|
||||
void BangBangClimate::set_normal_config(const BangBangClimateTargetTempConfig &normal_config) {
|
||||
this->normal_config_ = normal_config;
|
||||
|
||||
@@ -39,12 +39,12 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield climate.register_climate(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await climate.register_climate(var, config)
|
||||
|
||||
sens = yield cg.get_variable(config[CONF_SENSOR])
|
||||
sens = await cg.get_variable(config[CONF_SENSOR])
|
||||
cg.add(var.set_sensor(sens))
|
||||
|
||||
normal_config = BangBangClimateTargetTempConfig(
|
||||
@@ -53,17 +53,17 @@ def to_code(config):
|
||||
)
|
||||
cg.add(var.set_normal_config(normal_config))
|
||||
|
||||
yield automation.build_automation(
|
||||
await automation.build_automation(
|
||||
var.get_idle_trigger(), [], config[CONF_IDLE_ACTION]
|
||||
)
|
||||
|
||||
if CONF_COOL_ACTION in config:
|
||||
yield automation.build_automation(
|
||||
await automation.build_automation(
|
||||
var.get_cool_trigger(), [], config[CONF_COOL_ACTION]
|
||||
)
|
||||
cg.add(var.set_supports_cool(True))
|
||||
if CONF_HEAT_ACTION in config:
|
||||
yield automation.build_automation(
|
||||
await automation.build_automation(
|
||||
var.get_heat_trigger(), [], config[CONF_HEAT_ACTION]
|
||||
)
|
||||
cg.add(var.set_supports_heat(True))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace bh1750 {
|
||||
|
||||
static const char *TAG = "bh1750.sensor";
|
||||
static const char *const TAG = "bh1750.sensor";
|
||||
|
||||
static const uint8_t BH1750_COMMAND_POWER_ON = 0b00000001;
|
||||
static const uint8_t BH1750_COMMAND_MT_REG_HI = 0b01000000; // last 3 bits
|
||||
@@ -17,8 +17,8 @@ void BH1750Sensor::setup() {
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t mtreg_hi = (this->measurement_time_ >> 5) & 0b111;
|
||||
uint8_t mtreg_lo = (this->measurement_time_ >> 0) & 0b11111;
|
||||
uint8_t mtreg_hi = (this->measurement_duration_ >> 5) & 0b111;
|
||||
uint8_t mtreg_lo = (this->measurement_duration_ >> 0) & 0b11111;
|
||||
this->write_bytes(BH1750_COMMAND_MT_REG_HI | mtreg_hi, nullptr, 0);
|
||||
this->write_bytes(BH1750_COMMAND_MT_REG_LO | mtreg_lo, nullptr, 0);
|
||||
}
|
||||
@@ -77,7 +77,7 @@ void BH1750Sensor::read_data_() {
|
||||
}
|
||||
|
||||
float lx = float(raw_value) / 1.2f;
|
||||
lx *= 69.0f / this->measurement_time_;
|
||||
lx *= 69.0f / this->measurement_duration_;
|
||||
ESP_LOGD(TAG, "'%s': Got illuminance=%.1flx", this->get_name().c_str(), lx);
|
||||
this->publish_state(lx);
|
||||
this->status_clear_warning();
|
||||
|
||||
@@ -28,7 +28,7 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
|
||||
* @param resolution The new resolution of the sensor.
|
||||
*/
|
||||
void set_resolution(BH1750Resolution resolution);
|
||||
void set_measurement_time(uint8_t measurement_time) { measurement_time_ = measurement_time; }
|
||||
void set_measurement_duration(uint8_t measurement_duration) { measurement_duration_ = measurement_duration; }
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
@@ -41,7 +41,7 @@ class BH1750Sensor : public sensor::Sensor, public PollingComponent, public i2c:
|
||||
void read_data_();
|
||||
|
||||
BH1750Resolution resolution_{BH1750_RESOLUTION_0P5_LX};
|
||||
uint8_t measurement_time_;
|
||||
uint8_t measurement_duration_;
|
||||
};
|
||||
|
||||
} // namespace bh1750
|
||||
|
||||
@@ -5,8 +5,9 @@ from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_RESOLUTION,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_LUX,
|
||||
CONF_MEASUREMENT_DURATION,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["i2c"]
|
||||
@@ -25,16 +26,24 @@ BH1750Sensor = bh1750_ns.class_(
|
||||
|
||||
CONF_MEASUREMENT_TIME = "measurement_time"
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(UNIT_LUX, ICON_EMPTY, 1, DEVICE_CLASS_ILLUMINANCE)
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_LUX,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_ILLUMINANCE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BH1750Sensor),
|
||||
cv.Optional(CONF_RESOLUTION, default=0.5): cv.enum(
|
||||
BH1750_RESOLUTIONS, float=True
|
||||
),
|
||||
cv.Optional(CONF_MEASUREMENT_TIME, default=69): cv.int_range(
|
||||
cv.Optional(CONF_MEASUREMENT_DURATION, default=69): cv.int_range(
|
||||
min=31, max=254
|
||||
),
|
||||
cv.Optional(CONF_MEASUREMENT_TIME): cv.invalid(
|
||||
"The 'measurement_time' option has been replaced with 'measurement_duration' in 1.18.0"
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
@@ -42,11 +51,11 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield sensor.register_sensor(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_resolution(config[CONF_RESOLUTION]))
|
||||
cg.add(var.set_measurement_time(config[CONF_MEASUREMENT_TIME]))
|
||||
cg.add(var.set_measurement_duration(config[CONF_MEASUREMENT_DURATION]))
|
||||
|
||||
@@ -21,19 +21,19 @@ CONFIG_SCHEMA = fan.FAN_SCHEMA.extend(
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
||||
yield cg.register_component(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
fan_ = yield fan.create_fan_state(config)
|
||||
fan_ = await fan.create_fan_state(config)
|
||||
cg.add(var.set_fan(fan_))
|
||||
output_ = yield cg.get_variable(config[CONF_OUTPUT])
|
||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||
cg.add(var.set_output(output_))
|
||||
|
||||
if CONF_OSCILLATION_OUTPUT in config:
|
||||
oscillation_output = yield cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
|
||||
oscillation_output = await cg.get_variable(config[CONF_OSCILLATION_OUTPUT])
|
||||
cg.add(var.set_oscillating(oscillation_output))
|
||||
|
||||
if CONF_DIRECTION_OUTPUT in config:
|
||||
direction_output = yield cg.get_variable(config[CONF_DIRECTION_OUTPUT])
|
||||
direction_output = await cg.get_variable(config[CONF_DIRECTION_OUTPUT])
|
||||
cg.add(var.set_direction(direction_output))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace binary {
|
||||
|
||||
static const char *TAG = "binary.fan";
|
||||
static const char *const TAG = "binary.fan";
|
||||
|
||||
void binary::BinaryFan::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Fan '%s':", this->fan_->get_name().c_str());
|
||||
|
||||
@@ -14,9 +14,9 @@ CONFIG_SCHEMA = light.BINARY_LIGHT_SCHEMA.extend(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
||||
yield light.register_light(var, config)
|
||||
await light.register_light(var, config)
|
||||
|
||||
out = yield cg.get_variable(config[CONF_OUTPUT])
|
||||
out = await cg.get_variable(config[CONF_OUTPUT])
|
||||
cg.add(var.set_output(out))
|
||||
|
||||
@@ -12,7 +12,7 @@ class BinaryLightOutput : public light::LightOutput {
|
||||
void set_output(output::BinaryOutput *output) { output_ = output; }
|
||||
light::LightTraits get_traits() override {
|
||||
auto traits = light::LightTraits();
|
||||
traits.set_supports_brightness(false);
|
||||
traits.set_supported_color_modes({light::ColorMode::ON_OFF});
|
||||
return traits;
|
||||
}
|
||||
void write_state(light::LightState *state) override {
|
||||
|
||||
@@ -4,7 +4,9 @@ from esphome import automation, core
|
||||
from esphome.automation import Condition, maybe_simple_id
|
||||
from esphome.components import mqtt
|
||||
from esphome.const import (
|
||||
CONF_DELAY,
|
||||
CONF_DEVICE_CLASS,
|
||||
CONF_DISABLED_BY_DEFAULT,
|
||||
CONF_FILTERS,
|
||||
CONF_ID,
|
||||
CONF_INTERNAL,
|
||||
@@ -21,7 +23,6 @@ from esphome.const import (
|
||||
CONF_STATE,
|
||||
CONF_TIMING,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_FOR,
|
||||
CONF_NAME,
|
||||
CONF_MQTT_ID,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
@@ -50,7 +51,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_VIBRATION,
|
||||
DEVICE_CLASS_WINDOW,
|
||||
)
|
||||
from esphome.core import CORE, coroutine, coroutine_with_priority
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.util import Registry
|
||||
|
||||
CODEOWNERS = ["@esphome/core"]
|
||||
@@ -120,6 +121,7 @@ DelayedOnOffFilter = binary_sensor_ns.class_("DelayedOnOffFilter", Filter, cg.Co
|
||||
DelayedOnFilter = binary_sensor_ns.class_("DelayedOnFilter", Filter, cg.Component)
|
||||
DelayedOffFilter = binary_sensor_ns.class_("DelayedOffFilter", Filter, cg.Component)
|
||||
InvertFilter = binary_sensor_ns.class_("InvertFilter", Filter)
|
||||
AutorepeatFilter = binary_sensor_ns.class_("AutorepeatFilter", Filter, cg.Component)
|
||||
LambdaFilter = binary_sensor_ns.class_("LambdaFilter", Filter)
|
||||
|
||||
FILTER_REGISTRY = Registry()
|
||||
@@ -127,43 +129,88 @@ validate_filters = cv.validate_registry("filter", FILTER_REGISTRY)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register("invert", InvertFilter, {})
|
||||
def invert_filter_to_code(config, filter_id):
|
||||
yield cg.new_Pvariable(filter_id)
|
||||
async def invert_filter_to_code(config, filter_id):
|
||||
return cg.new_Pvariable(filter_id)
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register(
|
||||
"delayed_on_off", DelayedOnOffFilter, cv.positive_time_period_milliseconds
|
||||
)
|
||||
def delayed_on_off_filter_to_code(config, filter_id):
|
||||
async def delayed_on_off_filter_to_code(config, filter_id):
|
||||
var = cg.new_Pvariable(filter_id, config)
|
||||
yield cg.register_component(var, {})
|
||||
yield var
|
||||
await cg.register_component(var, {})
|
||||
return var
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register(
|
||||
"delayed_on", DelayedOnFilter, cv.positive_time_period_milliseconds
|
||||
)
|
||||
def delayed_on_filter_to_code(config, filter_id):
|
||||
async def delayed_on_filter_to_code(config, filter_id):
|
||||
var = cg.new_Pvariable(filter_id, config)
|
||||
yield cg.register_component(var, {})
|
||||
yield var
|
||||
await cg.register_component(var, {})
|
||||
return var
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register(
|
||||
"delayed_off", DelayedOffFilter, cv.positive_time_period_milliseconds
|
||||
)
|
||||
def delayed_off_filter_to_code(config, filter_id):
|
||||
async def delayed_off_filter_to_code(config, filter_id):
|
||||
var = cg.new_Pvariable(filter_id, config)
|
||||
yield cg.register_component(var, {})
|
||||
yield var
|
||||
await cg.register_component(var, {})
|
||||
return var
|
||||
|
||||
|
||||
CONF_TIME_OFF = "time_off"
|
||||
CONF_TIME_ON = "time_on"
|
||||
|
||||
DEFAULT_DELAY = "1s"
|
||||
DEFAULT_TIME_OFF = "100ms"
|
||||
DEFAULT_TIME_ON = "900ms"
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register(
|
||||
"autorepeat",
|
||||
AutorepeatFilter,
|
||||
cv.All(
|
||||
cv.ensure_list(
|
||||
{
|
||||
cv.Optional(
|
||||
CONF_DELAY, default=DEFAULT_DELAY
|
||||
): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(
|
||||
CONF_TIME_OFF, default=DEFAULT_TIME_OFF
|
||||
): cv.positive_time_period_milliseconds,
|
||||
cv.Optional(
|
||||
CONF_TIME_ON, default=DEFAULT_TIME_ON
|
||||
): cv.positive_time_period_milliseconds,
|
||||
}
|
||||
),
|
||||
),
|
||||
)
|
||||
async def autorepeat_filter_to_code(config, filter_id):
|
||||
timings = []
|
||||
if len(config) > 0:
|
||||
for conf in config:
|
||||
timings.append((conf[CONF_DELAY], conf[CONF_TIME_OFF], conf[CONF_TIME_ON]))
|
||||
else:
|
||||
timings.append(
|
||||
(
|
||||
cv.time_period_str_unit(DEFAULT_DELAY).total_milliseconds,
|
||||
cv.time_period_str_unit(DEFAULT_TIME_OFF).total_milliseconds,
|
||||
cv.time_period_str_unit(DEFAULT_TIME_ON).total_milliseconds,
|
||||
)
|
||||
)
|
||||
var = cg.new_Pvariable(filter_id, timings)
|
||||
await cg.register_component(var, {})
|
||||
return var
|
||||
|
||||
|
||||
@FILTER_REGISTRY.register("lambda", LambdaFilter, cv.returning_lambda)
|
||||
def lambda_filter_to_code(config, filter_id):
|
||||
lambda_ = yield cg.process_lambda(
|
||||
async def lambda_filter_to_code(config, filter_id):
|
||||
lambda_ = await cg.process_lambda(
|
||||
config, [(bool, "x")], return_type=cg.optional.template(bool)
|
||||
)
|
||||
yield cg.new_Pvariable(filter_id, lambda_)
|
||||
return cg.new_Pvariable(filter_id, lambda_)
|
||||
|
||||
|
||||
MULTI_CLICK_TIMING_SCHEMA = cv.Schema(
|
||||
@@ -269,7 +316,7 @@ def validate_multi_click_timing(value):
|
||||
|
||||
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
||||
|
||||
BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
|
||||
BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BinarySensor),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
|
||||
@@ -325,18 +372,13 @@ BINARY_SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(StateTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_INVERTED): cv.invalid(
|
||||
"The inverted binary_sensor property has been replaced by the "
|
||||
"new 'invert' binary sensor filter. Please see "
|
||||
"https://esphome.io/components/binary_sensor/index.html."
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@coroutine
|
||||
def setup_binary_sensor_core_(var, config):
|
||||
async def setup_binary_sensor_core_(var, config):
|
||||
cg.add(var.set_name(config[CONF_NAME]))
|
||||
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
|
||||
if CONF_INTERNAL in config:
|
||||
cg.add(var.set_internal(config[CONF_INTERNAL]))
|
||||
if CONF_DEVICE_CLASS in config:
|
||||
@@ -344,28 +386,28 @@ def setup_binary_sensor_core_(var, config):
|
||||
if CONF_INVERTED in config:
|
||||
cg.add(var.set_inverted(config[CONF_INVERTED]))
|
||||
if CONF_FILTERS in config:
|
||||
filters = yield cg.build_registry_list(FILTER_REGISTRY, config[CONF_FILTERS])
|
||||
filters = await cg.build_registry_list(FILTER_REGISTRY, config[CONF_FILTERS])
|
||||
cg.add(var.add_filters(filters))
|
||||
|
||||
for conf in config.get(CONF_ON_PRESS, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
yield automation.build_automation(trigger, [], conf)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_RELEASE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
yield automation.build_automation(trigger, [], conf)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_CLICK, []):
|
||||
trigger = cg.new_Pvariable(
|
||||
conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]
|
||||
)
|
||||
yield automation.build_automation(trigger, [], conf)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_DOUBLE_CLICK, []):
|
||||
trigger = cg.new_Pvariable(
|
||||
conf[CONF_TRIGGER_ID], var, conf[CONF_MIN_LENGTH], conf[CONF_MAX_LENGTH]
|
||||
)
|
||||
yield automation.build_automation(trigger, [], conf)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_MULTI_CLICK, []):
|
||||
timings = []
|
||||
@@ -381,40 +423,34 @@ def setup_binary_sensor_core_(var, config):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, timings)
|
||||
if CONF_INVALID_COOLDOWN in conf:
|
||||
cg.add(trigger.set_invalid_cooldown(conf[CONF_INVALID_COOLDOWN]))
|
||||
yield cg.register_component(trigger, conf)
|
||||
yield automation.build_automation(trigger, [], conf)
|
||||
await cg.register_component(trigger, conf)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_STATE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
yield automation.build_automation(trigger, [(bool, "x")], conf)
|
||||
await automation.build_automation(trigger, [(bool, "x")], conf)
|
||||
|
||||
if CONF_MQTT_ID in config:
|
||||
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
|
||||
yield mqtt.register_mqtt_component(mqtt_, config)
|
||||
await mqtt.register_mqtt_component(mqtt_, config)
|
||||
|
||||
|
||||
@coroutine
|
||||
def register_binary_sensor(var, config):
|
||||
async def register_binary_sensor(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_binary_sensor(var))
|
||||
yield setup_binary_sensor_core_(var, config)
|
||||
await setup_binary_sensor_core_(var, config)
|
||||
|
||||
|
||||
@coroutine
|
||||
def new_binary_sensor(config):
|
||||
async def new_binary_sensor(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME])
|
||||
yield register_binary_sensor(var, config)
|
||||
yield var
|
||||
await register_binary_sensor(var, config)
|
||||
return var
|
||||
|
||||
|
||||
BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(BinarySensor),
|
||||
cv.Optional(CONF_FOR): cv.invalid(
|
||||
"This option has been removed in 1.13, please use the "
|
||||
"'for' condition instead."
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -422,20 +458,20 @@ BINARY_SENSOR_CONDITION_SCHEMA = maybe_simple_id(
|
||||
@automation.register_condition(
|
||||
"binary_sensor.is_on", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA
|
||||
)
|
||||
def binary_sensor_is_on_to_code(config, condition_id, template_arg, args):
|
||||
paren = yield cg.get_variable(config[CONF_ID])
|
||||
yield cg.new_Pvariable(condition_id, template_arg, paren, True)
|
||||
async def binary_sensor_is_on_to_code(config, condition_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(condition_id, template_arg, paren, True)
|
||||
|
||||
|
||||
@automation.register_condition(
|
||||
"binary_sensor.is_off", BinarySensorCondition, BINARY_SENSOR_CONDITION_SCHEMA
|
||||
)
|
||||
def binary_sensor_is_off_to_code(config, condition_id, template_arg, args):
|
||||
paren = yield cg.get_variable(config[CONF_ID])
|
||||
yield cg.new_Pvariable(condition_id, template_arg, paren, False)
|
||||
async def binary_sensor_is_off_to_code(config, condition_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(condition_id, template_arg, paren, False)
|
||||
|
||||
|
||||
@coroutine_with_priority(100.0)
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
cg.add_define("USE_BINARY_SENSOR")
|
||||
cg.add_global(binary_sensor_ns.using)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace binary_sensor {
|
||||
|
||||
static const char *TAG = "binary_sensor.automation";
|
||||
static const char *const TAG = "binary_sensor.automation";
|
||||
|
||||
void binary_sensor::MultiClickTrigger::on_state_(bool state) {
|
||||
// Handle duplicate events
|
||||
@@ -80,6 +80,10 @@ void binary_sensor::MultiClickTrigger::schedule_cooldown_() {
|
||||
this->cancel_timeout("is_not_valid");
|
||||
}
|
||||
void binary_sensor::MultiClickTrigger::schedule_is_valid_(uint32_t min_length) {
|
||||
if (min_length == 0) {
|
||||
this->is_valid_ = true;
|
||||
return;
|
||||
}
|
||||
this->is_valid_ = false;
|
||||
this->set_timeout("is_valid", min_length, [this]() {
|
||||
ESP_LOGV(TAG, "Multi Click: You can now %s the button.", this->parent_->state ? "RELEASE" : "PRESS");
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
@@ -87,8 +89,8 @@ class DoubleClickTrigger : public Trigger<> {
|
||||
|
||||
class MultiClickTrigger : public Trigger<>, public Component {
|
||||
public:
|
||||
explicit MultiClickTrigger(BinarySensor *parent, const std::vector<MultiClickTriggerEvent> &timing)
|
||||
: parent_(parent), timing_(timing) {}
|
||||
explicit MultiClickTrigger(BinarySensor *parent, std::vector<MultiClickTriggerEvent> timing)
|
||||
: parent_(parent), timing_(std::move(timing)) {}
|
||||
|
||||
void setup() override {
|
||||
this->last_state_ = this->parent_->state;
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace esphome {
|
||||
|
||||
namespace binary_sensor {
|
||||
|
||||
static const char *TAG = "binary_sensor";
|
||||
static const char *const TAG = "binary_sensor";
|
||||
|
||||
void BinarySensor::add_on_state_callback(std::function<void(bool)> &&callback) {
|
||||
this->state_callback_.add(std::move(callback));
|
||||
@@ -61,7 +61,7 @@ void BinarySensor::add_filter(Filter *filter) {
|
||||
last_filter->next_ = filter;
|
||||
}
|
||||
}
|
||||
void BinarySensor::add_filters(std::vector<Filter *> filters) {
|
||||
void BinarySensor::add_filters(const std::vector<Filter *> &filters) {
|
||||
for (Filter *filter : filters) {
|
||||
this->add_filter(filter);
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ namespace esphome {
|
||||
namespace binary_sensor {
|
||||
|
||||
#define LOG_BINARY_SENSOR(prefix, type, obj) \
|
||||
if (obj != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \
|
||||
if (!obj->get_device_class().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
|
||||
if (!(obj)->get_device_class().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class BinarySensor : public Nameable {
|
||||
std::string get_device_class();
|
||||
|
||||
void add_filter(Filter *filter);
|
||||
void add_filters(std::vector<Filter *> filters);
|
||||
void add_filters(const std::vector<Filter *> &filters);
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#include "filter.h"
|
||||
|
||||
#include "binary_sensor.h"
|
||||
#include <utility>
|
||||
|
||||
namespace esphome {
|
||||
|
||||
namespace binary_sensor {
|
||||
|
||||
static const char *TAG = "sensor.filter";
|
||||
static const char *const TAG = "sensor.filter";
|
||||
|
||||
void Filter::output(bool value, bool is_initial) {
|
||||
if (!this->dedup_.next(value))
|
||||
@@ -64,7 +66,51 @@ float DelayedOffFilter::get_setup_priority() const { return setup_priority::HARD
|
||||
|
||||
optional<bool> InvertFilter::new_value(bool value, bool is_initial) { return !value; }
|
||||
|
||||
LambdaFilter::LambdaFilter(const std::function<optional<bool>(bool)> &f) : f_(f) {}
|
||||
AutorepeatFilter::AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings) : timings_(std::move(timings)) {}
|
||||
|
||||
optional<bool> AutorepeatFilter::new_value(bool value, bool is_initial) {
|
||||
if (value) {
|
||||
// Ignore if already running
|
||||
if (this->active_timing_ != 0)
|
||||
return {};
|
||||
|
||||
this->next_timing_();
|
||||
return true;
|
||||
} else {
|
||||
this->cancel_timeout("TIMING");
|
||||
this->cancel_timeout("ON_OFF");
|
||||
this->active_timing_ = 0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AutorepeatFilter::next_timing_() {
|
||||
// Entering this method
|
||||
// 1st time: starts waiting the first delay
|
||||
// 2nd time: starts waiting the second delay and starts toggling with the first time_off / _on
|
||||
// last time: no delay to start but have to bump the index to reflect the last
|
||||
if (this->active_timing_ < this->timings_.size())
|
||||
this->set_timeout("TIMING", this->timings_[this->active_timing_].delay, [this]() { this->next_timing_(); });
|
||||
|
||||
if (this->active_timing_ <= this->timings_.size()) {
|
||||
this->active_timing_++;
|
||||
}
|
||||
|
||||
if (this->active_timing_ == 2)
|
||||
this->next_value_(false);
|
||||
|
||||
// Leaving this method: if the toggling is started, it has to use [active_timing_ - 2] for the intervals
|
||||
}
|
||||
|
||||
void AutorepeatFilter::next_value_(bool val) {
|
||||
const AutorepeatFilterTiming &timing = this->timings_[this->active_timing_ - 2];
|
||||
this->output(val, false); // This is at least the second one so not initial
|
||||
this->set_timeout("ON_OFF", val ? timing.time_on : timing.time_off, [this, val]() { this->next_value_(!val); });
|
||||
}
|
||||
|
||||
float AutorepeatFilter::get_setup_priority() const { return setup_priority::HARDWARE; }
|
||||
|
||||
LambdaFilter::LambdaFilter(std::function<optional<bool>(bool)> f) : f_(std::move(f)) {}
|
||||
|
||||
optional<bool> LambdaFilter::new_value(bool value, bool is_initial) { return this->f_(value); }
|
||||
|
||||
|
||||
@@ -66,9 +66,36 @@ class InvertFilter : public Filter {
|
||||
optional<bool> new_value(bool value, bool is_initial) override;
|
||||
};
|
||||
|
||||
struct AutorepeatFilterTiming {
|
||||
AutorepeatFilterTiming(uint32_t delay, uint32_t off, uint32_t on) {
|
||||
this->delay = delay;
|
||||
this->time_off = off;
|
||||
this->time_on = on;
|
||||
}
|
||||
uint32_t delay;
|
||||
uint32_t time_off;
|
||||
uint32_t time_on;
|
||||
};
|
||||
|
||||
class AutorepeatFilter : public Filter, public Component {
|
||||
public:
|
||||
explicit AutorepeatFilter(std::vector<AutorepeatFilterTiming> timings);
|
||||
|
||||
optional<bool> new_value(bool value, bool is_initial) override;
|
||||
|
||||
float get_setup_priority() const override;
|
||||
|
||||
protected:
|
||||
void next_timing_();
|
||||
void next_value_(bool val);
|
||||
|
||||
std::vector<AutorepeatFilterTiming> timings_;
|
||||
uint8_t active_timing_{0};
|
||||
};
|
||||
|
||||
class LambdaFilter : public Filter {
|
||||
public:
|
||||
explicit LambdaFilter(const std::function<optional<bool>(bool)> &f);
|
||||
explicit LambdaFilter(std::function<optional<bool>(bool)> f);
|
||||
|
||||
optional<bool> new_value(bool value, bool is_initial) override;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace binary_sensor_map {
|
||||
|
||||
static const char *TAG = "binary_sensor_map";
|
||||
static const char *const TAG = "binary_sensor_map";
|
||||
|
||||
void BinarySensorMap::dump_config() { LOG_SENSOR(" ", "binary_sensor_map", this); }
|
||||
|
||||
|
||||
@@ -7,11 +7,10 @@ from esphome.const import (
|
||||
CONF_CHANNELS,
|
||||
CONF_VALUE,
|
||||
CONF_TYPE,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
UNIT_EMPTY,
|
||||
ICON_CHECK_CIRCLE_OUTLINE,
|
||||
CONF_BINARY_SENSOR,
|
||||
CONF_GROUP,
|
||||
STATE_CLASS_NONE,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["binary_sensor"]
|
||||
@@ -34,7 +33,9 @@ entry = {
|
||||
CONFIG_SCHEMA = cv.typed_schema(
|
||||
{
|
||||
CONF_GROUP: sensor.sensor_schema(
|
||||
UNIT_EMPTY, ICON_CHECK_CIRCLE_OUTLINE, 0, DEVICE_CLASS_EMPTY
|
||||
icon=ICON_CHECK_CIRCLE_OUTLINE,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BinarySensorMap),
|
||||
@@ -48,14 +49,14 @@ CONFIG_SCHEMA = cv.typed_schema(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield sensor.register_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
constant = SENSOR_MAP_TYPES[config[CONF_TYPE]]
|
||||
cg.add(var.set_sensor_type(constant))
|
||||
|
||||
for ch in config[CONF_CHANNELS]:
|
||||
input_var = yield cg.get_variable(ch[CONF_BINARY_SENSOR])
|
||||
input_var = await cg.get_variable(ch[CONF_BINARY_SENSOR])
|
||||
cg.add(var.add_channel(input_var, ch[CONF_VALUE]))
|
||||
|
||||
85
esphome/components/ble_client/__init__.py
Normal file
85
esphome/components/ble_client/__init__.py
Normal file
@@ -0,0 +1,85 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import esp32_ble_tracker
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_NAME,
|
||||
CONF_ON_CONNECT,
|
||||
CONF_ON_DISCONNECT,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
from esphome import automation
|
||||
|
||||
CODEOWNERS = ["@buxtronix"]
|
||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||
|
||||
ble_client_ns = cg.esphome_ns.namespace("ble_client")
|
||||
BLEClient = ble_client_ns.class_(
|
||||
"BLEClient", cg.Component, esp32_ble_tracker.ESPBTClient
|
||||
)
|
||||
BLEClientNode = ble_client_ns.class_("BLEClientNode")
|
||||
BLEClientNodeConstRef = BLEClientNode.operator("ref").operator("const")
|
||||
# Triggers
|
||||
BLEClientConnectTrigger = ble_client_ns.class_(
|
||||
"BLEClientConnectTrigger", automation.Trigger.template(BLEClientNodeConstRef)
|
||||
)
|
||||
BLEClientDisconnectTrigger = ble_client_ns.class_(
|
||||
"BLEClientDisconnectTrigger", automation.Trigger.template(BLEClientNodeConstRef)
|
||||
)
|
||||
|
||||
# Espressif platformio framework is built with MAX_BLE_CONN to 3, so
|
||||
# enforce this in yaml checks.
|
||||
MULTI_CONF = 3
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLEClient),
|
||||
cv.Required(CONF_MAC_ADDRESS): cv.mac_address,
|
||||
cv.Optional(CONF_NAME): cv.string,
|
||||
cv.Optional(CONF_ON_CONNECT): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
BLEClientConnectTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_DISCONNECT): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
BLEClientDisconnectTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(esp32_ble_tracker.ESP_BLE_DEVICE_SCHEMA)
|
||||
)
|
||||
|
||||
CONF_BLE_CLIENT_ID = "ble_client_id"
|
||||
|
||||
BLE_CLIENT_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_BLE_CLIENT_ID): cv.use_id(BLEClient),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def register_ble_node(var, config):
|
||||
parent = await cg.get_variable(config[CONF_BLE_CLIENT_ID])
|
||||
cg.add(parent.register_ble_node(var))
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await esp32_ble_tracker.register_client(var, config)
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
for conf in config.get(CONF_ON_CONNECT, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
for conf in config.get(CONF_ON_DISCONNECT, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
37
esphome/components/ble_client/automation.h
Normal file
37
esphome/components/ble_client/automation.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
class BLEClientConnectTrigger : public Trigger<>, public BLEClientNode {
|
||||
public:
|
||||
explicit BLEClientConnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
|
||||
void loop() override {}
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||
if (event == ESP_GATTC_OPEN_EVT && param->open.status == ESP_GATT_OK)
|
||||
this->trigger();
|
||||
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
}
|
||||
};
|
||||
|
||||
class BLEClientDisconnectTrigger : public Trigger<>, public BLEClientNode {
|
||||
public:
|
||||
explicit BLEClientDisconnectTrigger(BLEClient *parent) { parent->register_ble_node(this); }
|
||||
void loop() override {}
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||
if (event == ESP_GATTC_DISCONNECT_EVT && memcmp(param->disconnect.remote_bda, this->parent_->remote_bda, 6) == 0)
|
||||
this->trigger();
|
||||
if (event == ESP_GATTC_SEARCH_CMPL_EVT)
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
392
esphome/components/ble_client/ble_client.cpp
Normal file
392
esphome/components/ble_client/ble_client.cpp
Normal file
@@ -0,0 +1,392 @@
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "ble_client.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
static const char *const TAG = "ble_client";
|
||||
|
||||
void BLEClient::setup() {
|
||||
auto ret = esp_ble_gattc_app_register(this->app_id);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "gattc app register failed. app_id=%d code=%d", this->app_id, ret);
|
||||
this->mark_failed();
|
||||
}
|
||||
this->set_states(espbt::ClientState::Idle);
|
||||
this->enabled = true;
|
||||
}
|
||||
|
||||
void BLEClient::loop() {
|
||||
if (this->state() == espbt::ClientState::Discovered) {
|
||||
this->connect();
|
||||
}
|
||||
for (auto *node : this->nodes_)
|
||||
node->loop();
|
||||
}
|
||||
|
||||
void BLEClient::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "BLE Client:");
|
||||
ESP_LOGCONFIG(TAG, " Address: %s", this->address_str().c_str());
|
||||
}
|
||||
|
||||
bool BLEClient::parse_device(const espbt::ESPBTDevice &device) {
|
||||
if (!this->enabled)
|
||||
return false;
|
||||
if (device.address_uint64() != this->address)
|
||||
return false;
|
||||
if (this->state() != espbt::ClientState::Idle)
|
||||
return false;
|
||||
|
||||
ESP_LOGD(TAG, "Found device at MAC address [%s]", device.address_str().c_str());
|
||||
this->set_states(espbt::ClientState::Discovered);
|
||||
|
||||
auto addr = device.address_uint64();
|
||||
this->remote_bda[0] = (addr >> 40) & 0xFF;
|
||||
this->remote_bda[1] = (addr >> 32) & 0xFF;
|
||||
this->remote_bda[2] = (addr >> 24) & 0xFF;
|
||||
this->remote_bda[3] = (addr >> 16) & 0xFF;
|
||||
this->remote_bda[4] = (addr >> 8) & 0xFF;
|
||||
this->remote_bda[5] = (addr >> 0) & 0xFF;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string BLEClient::address_str() const {
|
||||
char buf[20];
|
||||
sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", (uint8_t)(this->address >> 40) & 0xff,
|
||||
(uint8_t)(this->address >> 32) & 0xff, (uint8_t)(this->address >> 24) & 0xff,
|
||||
(uint8_t)(this->address >> 16) & 0xff, (uint8_t)(this->address >> 8) & 0xff,
|
||||
(uint8_t)(this->address >> 0) & 0xff);
|
||||
std::string ret;
|
||||
ret = buf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BLEClient::set_enabled(bool enabled) {
|
||||
if (enabled == this->enabled)
|
||||
return;
|
||||
if (!enabled && this->state() != espbt::ClientState::Idle) {
|
||||
ESP_LOGI(TAG, "[%s] Disabling BLE client.", this->address_str().c_str());
|
||||
auto ret = esp_ble_gattc_close(this->gattc_if, this->conn_id);
|
||||
if (ret) {
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_close error, address=%s status=%d", this->address_str().c_str(), ret);
|
||||
}
|
||||
}
|
||||
this->enabled = enabled;
|
||||
}
|
||||
|
||||
void BLEClient::connect() {
|
||||
ESP_LOGI(TAG, "Attempting BLE connection to %s", this->address_str().c_str());
|
||||
auto ret = esp_ble_gattc_open(this->gattc_if, this->remote_bda, BLE_ADDR_TYPE_PUBLIC, true);
|
||||
if (ret) {
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_open error, address=%s status=%d", this->address_str().c_str(), ret);
|
||||
this->set_states(espbt::ClientState::Idle);
|
||||
} else {
|
||||
this->set_states(espbt::ClientState::Connecting);
|
||||
}
|
||||
}
|
||||
|
||||
void BLEClient::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t esp_gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) {
|
||||
if (event == ESP_GATTC_REG_EVT && this->app_id != param->reg.app_id)
|
||||
return;
|
||||
if (event != ESP_GATTC_REG_EVT && esp_gattc_if != ESP_GATT_IF_NONE && esp_gattc_if != this->gattc_if)
|
||||
return;
|
||||
|
||||
bool all_established = this->all_nodes_established();
|
||||
|
||||
switch (event) {
|
||||
case ESP_GATTC_REG_EVT: {
|
||||
if (param->reg.status == ESP_GATT_OK) {
|
||||
ESP_LOGV(TAG, "gattc registered app id %d", this->app_id);
|
||||
this->gattc_if = esp_gattc_if;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "gattc app registration failed id=%d code=%d", param->reg.app_id, param->reg.status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_OPEN_EVT: {
|
||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_OPEN_EVT", this->address_str().c_str());
|
||||
if (param->open.status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "connect to %s failed, status=%d", this->address_str().c_str(), param->open.status);
|
||||
this->set_states(espbt::ClientState::Idle);
|
||||
break;
|
||||
}
|
||||
this->conn_id = param->open.conn_id;
|
||||
auto ret = esp_ble_gattc_send_mtu_req(this->gattc_if, param->open.conn_id);
|
||||
if (ret) {
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_send_mtu_req failed, status=%d", ret);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_CFG_MTU_EVT: {
|
||||
if (param->cfg_mtu.status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "cfg_mtu to %s failed, status %d", this->address_str().c_str(), param->cfg_mtu.status);
|
||||
this->set_states(espbt::ClientState::Idle);
|
||||
break;
|
||||
}
|
||||
ESP_LOGV(TAG, "cfg_mtu status %d, mtu %d", param->cfg_mtu.status, param->cfg_mtu.mtu);
|
||||
esp_ble_gattc_search_service(esp_gattc_if, param->cfg_mtu.conn_id, NULL);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
if (memcmp(param->disconnect.remote_bda, this->remote_bda, 6) != 0) {
|
||||
return;
|
||||
}
|
||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_DISCONNECT_EVT", this->address_str().c_str());
|
||||
for (auto &svc : this->services_)
|
||||
delete svc;
|
||||
this->services_.clear();
|
||||
this->set_states(espbt::ClientState::Idle);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_SEARCH_RES_EVT: {
|
||||
BLEService *ble_service = new BLEService();
|
||||
ble_service->uuid = espbt::ESPBTUUID::from_uuid(param->search_res.srvc_id.uuid);
|
||||
ble_service->start_handle = param->search_res.start_handle;
|
||||
ble_service->end_handle = param->search_res.end_handle;
|
||||
ble_service->client = this;
|
||||
this->services_.push_back(ble_service);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_SEARCH_CMPL_EVT", this->address_str().c_str());
|
||||
for (auto &svc : this->services_) {
|
||||
ESP_LOGI(TAG, "Service UUID: %s", svc->uuid.to_string().c_str());
|
||||
ESP_LOGI(TAG, " start_handle: 0x%x end_handle: 0x%x", svc->start_handle, svc->end_handle);
|
||||
svc->parse_characteristics();
|
||||
}
|
||||
this->set_states(espbt::ClientState::Connected);
|
||||
this->set_state(espbt::ClientState::Established);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
auto descr = this->get_config_descriptor(param->reg_for_notify.handle);
|
||||
if (descr == nullptr) {
|
||||
ESP_LOGW(TAG, "No descriptor found for notify of handle 0x%x", param->reg_for_notify.handle);
|
||||
break;
|
||||
}
|
||||
if (descr->uuid.get_uuid().len != ESP_UUID_LEN_16 ||
|
||||
descr->uuid.get_uuid().uuid.uuid16 != ESP_GATT_UUID_CHAR_CLIENT_CONFIG) {
|
||||
ESP_LOGW(TAG, "Handle 0x%x (uuid %s) is not a client config char uuid", param->reg_for_notify.handle,
|
||||
descr->uuid.to_string().c_str());
|
||||
break;
|
||||
}
|
||||
uint8_t notify_en = 1;
|
||||
auto status = esp_ble_gattc_write_char_descr(this->gattc_if, this->conn_id, descr->handle, sizeof(notify_en),
|
||||
¬ify_en, ESP_GATT_WRITE_TYPE_RSP, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status) {
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_write_char_descr error, status=%d", status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
for (auto *node : this->nodes_)
|
||||
node->gattc_event_handler(event, esp_gattc_if, param);
|
||||
|
||||
// Delete characteristics after clients have used them to save RAM.
|
||||
if (!all_established && this->all_nodes_established()) {
|
||||
for (auto &svc : this->services_)
|
||||
delete svc;
|
||||
this->services_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Parse GATT values into a float for a sensor.
|
||||
// Ref: https://www.bluetooth.com/specifications/assigned-numbers/format-types/
|
||||
float BLEClient::parse_char_value(uint8_t *value, uint16_t length) {
|
||||
// A length of one means a single octet value.
|
||||
if (length == 0)
|
||||
return 0;
|
||||
if (length == 1)
|
||||
return (float) ((uint8_t) value[0]);
|
||||
|
||||
switch (value[0]) {
|
||||
case 0x1: // boolean.
|
||||
case 0x2: // 2bit.
|
||||
case 0x3: // nibble.
|
||||
case 0x4: // uint8.
|
||||
return (float) ((uint8_t) value[1]);
|
||||
case 0x5: // uint12.
|
||||
case 0x6: // uint16.
|
||||
if (length > 2) {
|
||||
return (float) ((uint16_t)(value[1] << 8) + (uint16_t) value[2]);
|
||||
}
|
||||
case 0x7: // uint24.
|
||||
if (length > 3) {
|
||||
return (float) ((uint32_t)(value[1] << 16) + (uint32_t)(value[2] << 8) + (uint32_t)(value[3]));
|
||||
}
|
||||
case 0x8: // uint32.
|
||||
if (length > 4) {
|
||||
return (float) ((uint32_t)(value[1] << 24) + (uint32_t)(value[2] << 16) + (uint32_t)(value[3] << 8) +
|
||||
(uint32_t)(value[4]));
|
||||
}
|
||||
case 0xC: // int8.
|
||||
return (float) ((int8_t) value[1]);
|
||||
case 0xD: // int12.
|
||||
case 0xE: // int16.
|
||||
if (length > 2) {
|
||||
return (float) ((int16_t)(value[1] << 8) + (int16_t) value[2]);
|
||||
}
|
||||
case 0xF: // int24.
|
||||
if (length > 3) {
|
||||
return (float) ((int32_t)(value[1] << 16) + (int32_t)(value[2] << 8) + (int32_t)(value[3]));
|
||||
}
|
||||
case 0x10: // int32.
|
||||
if (length > 4) {
|
||||
return (float) ((int32_t)(value[1] << 24) + (int32_t)(value[2] << 16) + (int32_t)(value[3] << 8) +
|
||||
(int32_t)(value[4]));
|
||||
}
|
||||
}
|
||||
ESP_LOGW(TAG, "Cannot parse characteristic value of type 0x%x length %d", value[0], length);
|
||||
return NAN;
|
||||
}
|
||||
|
||||
BLEService *BLEClient::get_service(espbt::ESPBTUUID uuid) {
|
||||
for (auto svc : this->services_)
|
||||
if (svc->uuid == uuid)
|
||||
return svc;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLEService *BLEClient::get_service(uint16_t uuid) { return this->get_service(espbt::ESPBTUUID::from_uint16(uuid)); }
|
||||
|
||||
BLECharacteristic *BLEClient::get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr) {
|
||||
auto svc = this->get_service(service);
|
||||
if (svc == nullptr)
|
||||
return nullptr;
|
||||
return svc->get_characteristic(chr);
|
||||
}
|
||||
|
||||
BLECharacteristic *BLEClient::get_characteristic(uint16_t service, uint16_t chr) {
|
||||
return this->get_characteristic(espbt::ESPBTUUID::from_uint16(service), espbt::ESPBTUUID::from_uint16(chr));
|
||||
}
|
||||
|
||||
BLEDescriptor *BLEClient::get_config_descriptor(uint16_t handle) {
|
||||
for (auto &svc : this->services_)
|
||||
for (auto &chr : svc->characteristics)
|
||||
if (chr->handle == handle)
|
||||
for (auto &desc : chr->descriptors)
|
||||
if (desc->uuid == espbt::ESPBTUUID::from_uint16(0x2902))
|
||||
return desc;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLECharacteristic *BLEService::get_characteristic(espbt::ESPBTUUID uuid) {
|
||||
for (auto &chr : this->characteristics)
|
||||
if (chr->uuid == uuid)
|
||||
return chr;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLECharacteristic *BLEService::get_characteristic(uint16_t uuid) {
|
||||
return this->get_characteristic(espbt::ESPBTUUID::from_uint16(uuid));
|
||||
}
|
||||
|
||||
BLEDescriptor *BLEClient::get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr) {
|
||||
auto svc = this->get_service(service);
|
||||
if (svc == nullptr)
|
||||
return nullptr;
|
||||
auto ch = svc->get_characteristic(chr);
|
||||
if (ch == nullptr)
|
||||
return nullptr;
|
||||
return ch->get_descriptor(descr);
|
||||
}
|
||||
|
||||
BLEDescriptor *BLEClient::get_descriptor(uint16_t service, uint16_t chr, uint16_t descr) {
|
||||
return this->get_descriptor(espbt::ESPBTUUID::from_uint16(service), espbt::ESPBTUUID::from_uint16(chr),
|
||||
espbt::ESPBTUUID::from_uint16(descr));
|
||||
}
|
||||
|
||||
BLEService::~BLEService() {
|
||||
for (auto &chr : this->characteristics)
|
||||
delete chr;
|
||||
}
|
||||
|
||||
void BLEService::parse_characteristics() {
|
||||
uint16_t offset = 0;
|
||||
esp_gattc_char_elem_t result;
|
||||
|
||||
while (true) {
|
||||
uint16_t count = 1;
|
||||
esp_gatt_status_t status = esp_ble_gattc_get_all_char(
|
||||
this->client->gattc_if, this->client->conn_id, this->start_handle, this->end_handle, &result, &count, offset);
|
||||
if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) {
|
||||
break;
|
||||
}
|
||||
if (status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_get_all_char error, status=%d", status);
|
||||
break;
|
||||
}
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
BLECharacteristic *characteristic = new BLECharacteristic();
|
||||
characteristic->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
|
||||
characteristic->properties = result.properties;
|
||||
characteristic->handle = result.char_handle;
|
||||
characteristic->service = this;
|
||||
this->characteristics.push_back(characteristic);
|
||||
ESP_LOGI(TAG, " characteristic %s, handle 0x%x, properties 0x%x", characteristic->uuid.to_string().c_str(),
|
||||
characteristic->handle, characteristic->properties);
|
||||
characteristic->parse_descriptors();
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
BLECharacteristic::~BLECharacteristic() {
|
||||
for (auto &desc : this->descriptors)
|
||||
delete desc;
|
||||
}
|
||||
|
||||
void BLECharacteristic::parse_descriptors() {
|
||||
uint16_t offset = 0;
|
||||
esp_gattc_descr_elem_t result;
|
||||
|
||||
while (true) {
|
||||
uint16_t count = 1;
|
||||
esp_gatt_status_t status = esp_ble_gattc_get_all_descr(
|
||||
this->service->client->gattc_if, this->service->client->conn_id, this->handle, &result, &count, offset);
|
||||
if (status == ESP_GATT_INVALID_OFFSET || status == ESP_GATT_NOT_FOUND) {
|
||||
break;
|
||||
}
|
||||
if (status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_get_all_descr error, status=%d", status);
|
||||
break;
|
||||
}
|
||||
if (count == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
BLEDescriptor *desc = new BLEDescriptor();
|
||||
desc->uuid = espbt::ESPBTUUID::from_uuid(result.uuid);
|
||||
desc->handle = result.handle;
|
||||
desc->characteristic = this;
|
||||
this->descriptors.push_back(desc);
|
||||
ESP_LOGV(TAG, " descriptor %s, handle 0x%x", desc->uuid.to_string().c_str(), desc->handle);
|
||||
offset++;
|
||||
}
|
||||
}
|
||||
|
||||
BLEDescriptor *BLECharacteristic::get_descriptor(espbt::ESPBTUUID uuid) {
|
||||
for (auto &desc : this->descriptors)
|
||||
if (desc->uuid == uuid)
|
||||
return desc;
|
||||
return nullptr;
|
||||
}
|
||||
BLEDescriptor *BLECharacteristic::get_descriptor(uint16_t uuid) {
|
||||
return this->get_descriptor(espbt::ESPBTUUID::from_uint16(uuid));
|
||||
}
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
140
esphome/components/ble_client/ble_client.h
Normal file
140
esphome/components/ble_client/ble_client.h
Normal file
@@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <esp_gap_ble_api.h>
|
||||
#include <esp_gattc_api.h>
|
||||
#include <esp_bt_defs.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
class BLEClient;
|
||||
class BLEService;
|
||||
class BLECharacteristic;
|
||||
|
||||
class BLEClientNode {
|
||||
public:
|
||||
virtual void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) = 0;
|
||||
virtual void loop() = 0;
|
||||
void set_address(uint64_t address) { address_ = address; }
|
||||
espbt::ESPBTClient *client;
|
||||
// This should be transitioned to Established once the node no longer needs
|
||||
// the services/descriptors/characteristics of the parent client. This will
|
||||
// allow some memory to be freed.
|
||||
espbt::ClientState node_state;
|
||||
|
||||
BLEClient *parent() { return this->parent_; }
|
||||
void set_ble_client_parent(BLEClient *parent) { this->parent_ = parent; }
|
||||
|
||||
protected:
|
||||
BLEClient *parent_;
|
||||
uint64_t address_;
|
||||
};
|
||||
|
||||
class BLEDescriptor {
|
||||
public:
|
||||
espbt::ESPBTUUID uuid;
|
||||
uint16_t handle;
|
||||
|
||||
BLECharacteristic *characteristic;
|
||||
};
|
||||
|
||||
class BLECharacteristic {
|
||||
public:
|
||||
~BLECharacteristic();
|
||||
espbt::ESPBTUUID uuid;
|
||||
uint16_t handle;
|
||||
esp_gatt_char_prop_t properties;
|
||||
std::vector<BLEDescriptor *> descriptors;
|
||||
void parse_descriptors();
|
||||
BLEDescriptor *get_descriptor(espbt::ESPBTUUID uuid);
|
||||
BLEDescriptor *get_descriptor(uint16_t uuid);
|
||||
|
||||
BLEService *service;
|
||||
};
|
||||
|
||||
class BLEService {
|
||||
public:
|
||||
~BLEService();
|
||||
espbt::ESPBTUUID uuid;
|
||||
uint16_t start_handle;
|
||||
uint16_t end_handle;
|
||||
std::vector<BLECharacteristic *> characteristics;
|
||||
BLEClient *client;
|
||||
void parse_characteristics();
|
||||
BLECharacteristic *get_characteristic(espbt::ESPBTUUID uuid);
|
||||
BLECharacteristic *get_characteristic(uint16_t uuid);
|
||||
};
|
||||
|
||||
class BLEClient : public espbt::ESPBTClient, public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
void loop() override;
|
||||
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
|
||||
bool parse_device(const espbt::ESPBTDevice &device) override;
|
||||
void on_scan_end() override {}
|
||||
void connect();
|
||||
|
||||
void set_address(uint64_t address) { this->address = address; }
|
||||
|
||||
void set_enabled(bool enabled);
|
||||
|
||||
void register_ble_node(BLEClientNode *node) {
|
||||
node->client = this;
|
||||
node->set_ble_client_parent(this);
|
||||
this->nodes_.push_back(node);
|
||||
}
|
||||
|
||||
BLEService *get_service(espbt::ESPBTUUID uuid);
|
||||
BLEService *get_service(uint16_t uuid);
|
||||
BLECharacteristic *get_characteristic(espbt::ESPBTUUID service, espbt::ESPBTUUID chr);
|
||||
BLECharacteristic *get_characteristic(uint16_t service, uint16_t chr);
|
||||
BLEDescriptor *get_descriptor(espbt::ESPBTUUID service, espbt::ESPBTUUID chr, espbt::ESPBTUUID descr);
|
||||
BLEDescriptor *get_descriptor(uint16_t service, uint16_t chr, uint16_t descr);
|
||||
// Get the configuration descriptor for the given characteristic handle.
|
||||
BLEDescriptor *get_config_descriptor(uint16_t handle);
|
||||
|
||||
float parse_char_value(uint8_t *value, uint16_t length);
|
||||
|
||||
int gattc_if;
|
||||
esp_bd_addr_t remote_bda;
|
||||
uint16_t conn_id;
|
||||
uint64_t address;
|
||||
bool enabled;
|
||||
std::string address_str() const;
|
||||
|
||||
protected:
|
||||
void set_states(espbt::ClientState st) {
|
||||
this->set_state(st);
|
||||
for (auto &node : nodes_)
|
||||
node->node_state = st;
|
||||
}
|
||||
bool all_nodes_established() {
|
||||
if (this->state() != espbt::ClientState::Established)
|
||||
return false;
|
||||
for (auto &node : nodes_)
|
||||
if (node->node_state != espbt::ClientState::Established)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<BLEClientNode *> nodes_;
|
||||
std::vector<BLEService *> services_;
|
||||
};
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
127
esphome/components/ble_client/sensor/__init__.py
Normal file
127
esphome/components/ble_client/sensor/__init__.py
Normal file
@@ -0,0 +1,127 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor, ble_client, esp32_ble_tracker
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
CONF_LAMBDA,
|
||||
STATE_CLASS_NONE,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_SERVICE_UUID,
|
||||
)
|
||||
from esphome import automation
|
||||
from .. import ble_client_ns
|
||||
|
||||
DEPENDENCIES = ["ble_client"]
|
||||
|
||||
CONF_CHARACTERISTIC_UUID = "characteristic_uuid"
|
||||
CONF_DESCRIPTOR_UUID = "descriptor_uuid"
|
||||
|
||||
CONF_NOTIFY = "notify"
|
||||
CONF_ON_NOTIFY = "on_notify"
|
||||
|
||||
adv_data_t = cg.std_vector.template(cg.uint8)
|
||||
adv_data_t_const_ref = adv_data_t.operator("ref").operator("const")
|
||||
|
||||
BLESensor = ble_client_ns.class_(
|
||||
"BLESensor", sensor.Sensor, cg.PollingComponent, ble_client.BLEClientNode
|
||||
)
|
||||
BLESensorNotifyTrigger = ble_client_ns.class_(
|
||||
"BLESensorNotifyTrigger", automation.Trigger.template(cg.float_)
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_NONE,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLESensor),
|
||||
cv.Required(CONF_SERVICE_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Required(CONF_CHARACTERISTIC_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Optional(CONF_DESCRIPTOR_UUID): esp32_ble_tracker.bt_uuid,
|
||||
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
|
||||
cv.Optional(CONF_NOTIFY, default=False): cv.boolean,
|
||||
cv.Optional(CONF_ON_NOTIFY): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
BLESensorNotifyTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(
|
||||
var.set_service_uuid16(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
|
||||
)
|
||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid32_format):
|
||||
cg.add(
|
||||
var.set_service_uuid32(esp32_ble_tracker.as_hex(config[CONF_SERVICE_UUID]))
|
||||
)
|
||||
elif len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid128_format):
|
||||
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_SERVICE_UUID])
|
||||
cg.add(var.set_service_uuid128(uuid128))
|
||||
|
||||
if len(config[CONF_CHARACTERISTIC_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(
|
||||
var.set_char_uuid16(
|
||||
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid32_format
|
||||
):
|
||||
cg.add(
|
||||
var.set_char_uuid32(
|
||||
esp32_ble_tracker.as_hex(config[CONF_CHARACTERISTIC_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_CHARACTERISTIC_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid128_format
|
||||
):
|
||||
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_CHARACTERISTIC_UUID])
|
||||
cg.add(var.set_char_uuid128(uuid128))
|
||||
|
||||
if CONF_DESCRIPTOR_UUID in config:
|
||||
if len(config[CONF_DESCRIPTOR_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(
|
||||
var.set_descr_uuid16(
|
||||
esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid32_format
|
||||
):
|
||||
cg.add(
|
||||
var.set_descr_uuid32(
|
||||
esp32_ble_tracker.as_hex(config[CONF_DESCRIPTOR_UUID])
|
||||
)
|
||||
)
|
||||
elif len(config[CONF_DESCRIPTOR_UUID]) == len(
|
||||
esp32_ble_tracker.bt_uuid128_format
|
||||
):
|
||||
uuid128 = esp32_ble_tracker.as_hex_array(config[CONF_DESCRIPTOR_UUID])
|
||||
cg.add(var.set_descr_uuid128(uuid128))
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [(adv_data_t_const_ref, "x")], return_type=cg.float_
|
||||
)
|
||||
cg.add(var.set_data_to_value(lambda_))
|
||||
|
||||
await cg.register_component(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
cg.add(var.set_enable_notify(config[CONF_NOTIFY]))
|
||||
await sensor.register_sensor(var, config)
|
||||
for conf in config.get(CONF_ON_NOTIFY, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await ble_client.register_ble_node(trigger, config)
|
||||
await automation.build_automation(trigger, [(float, "x")], conf)
|
||||
37
esphome/components/ble_client/sensor/automation.h
Normal file
37
esphome/components/ble_client/sensor/automation.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/ble_client/sensor/ble_sensor.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
class BLESensorNotifyTrigger : public Trigger<float>, public BLESensor {
|
||||
public:
|
||||
explicit BLESensorNotifyTrigger(BLESensor *sensor) { sensor_ = sensor; }
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
this->sensor_->node_state = espbt::ClientState::Established;
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (param->notify.conn_id != this->sensor_->parent()->conn_id || param->notify.handle != this->sensor_->handle)
|
||||
break;
|
||||
this->trigger(this->sensor_->parent()->parse_char_value(param->notify.value, param->notify.value_len));
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
BLESensor *sensor_;
|
||||
};
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
||||
138
esphome/components/ble_client/sensor/ble_sensor.cpp
Normal file
138
esphome/components/ble_client/sensor/ble_sensor.cpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#include "ble_sensor.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
static const char *const TAG = "ble_sensor";
|
||||
|
||||
uint32_t BLESensor::hash_base() { return 343459825UL; }
|
||||
|
||||
void BLESensor::loop() {}
|
||||
|
||||
void BLESensor::dump_config() {
|
||||
LOG_SENSOR("", "BLE Sensor", this);
|
||||
ESP_LOGCONFIG(TAG, " MAC address : %s", this->parent()->address_str().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Service UUID : %s", this->service_uuid_.to_string().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Characteristic UUID: %s", this->char_uuid_.to_string().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Descriptor UUID : %s", this->descr_uuid_.to_string().c_str());
|
||||
ESP_LOGCONFIG(TAG, " Notifications : %s", YESNO(this->notify_));
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
}
|
||||
|
||||
void BLESensor::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_OPEN_EVT: {
|
||||
if (param->open.status == ESP_GATT_OK) {
|
||||
ESP_LOGI(TAG, "[%s] Connected successfully!", this->get_name().c_str());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_DISCONNECT_EVT: {
|
||||
ESP_LOGW(TAG, "[%s] Disconnected!", this->get_name().c_str());
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_SEARCH_CMPL_EVT: {
|
||||
this->handle = 0;
|
||||
auto chr = this->parent()->get_characteristic(this->service_uuid_, this->char_uuid_);
|
||||
if (chr == nullptr) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
ESP_LOGW(TAG, "No sensor characteristic found at service %s char %s", this->service_uuid_.to_string().c_str(),
|
||||
this->char_uuid_.to_string().c_str());
|
||||
break;
|
||||
}
|
||||
this->handle = chr->handle;
|
||||
if (this->descr_uuid_.get_uuid().len > 0) {
|
||||
auto descr = chr->get_descriptor(this->descr_uuid_);
|
||||
if (descr == nullptr) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
ESP_LOGW(TAG, "No sensor descriptor found at service %s char %s descr %s",
|
||||
this->service_uuid_.to_string().c_str(), this->char_uuid_.to_string().c_str(),
|
||||
this->descr_uuid_.to_string().c_str());
|
||||
break;
|
||||
}
|
||||
this->handle = descr->handle;
|
||||
}
|
||||
if (this->notify_) {
|
||||
auto status =
|
||||
esp_ble_gattc_register_for_notify(this->parent()->gattc_if, this->parent()->remote_bda, chr->handle);
|
||||
if (status) {
|
||||
ESP_LOGW(TAG, "esp_ble_gattc_register_for_notify failed, status=%d", status);
|
||||
}
|
||||
} else {
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_READ_CHAR_EVT: {
|
||||
if (param->read.conn_id != this->parent()->conn_id)
|
||||
break;
|
||||
if (param->read.status != ESP_GATT_OK) {
|
||||
ESP_LOGW(TAG, "Error reading char at handle %d, status=%d", param->read.handle, param->read.status);
|
||||
break;
|
||||
}
|
||||
if (param->read.handle == this->handle) {
|
||||
this->status_clear_warning();
|
||||
this->publish_state(this->parse_data(param->read.value, param->read.value_len));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_NOTIFY_EVT: {
|
||||
if (param->notify.conn_id != this->parent()->conn_id || param->notify.handle != this->handle)
|
||||
break;
|
||||
ESP_LOGV(TAG, "[%s] ESP_GATTC_NOTIFY_EVT: handle=0x%x, value=0x%x", this->get_name().c_str(),
|
||||
param->notify.handle, param->notify.value[0]);
|
||||
this->publish_state(this->parse_data(param->notify.value, param->notify.value_len));
|
||||
break;
|
||||
}
|
||||
case ESP_GATTC_REG_FOR_NOTIFY_EVT: {
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float BLESensor::parse_data(uint8_t *value, uint16_t value_len) {
|
||||
if (this->data_to_value_func_.has_value()) {
|
||||
std::vector<uint8_t> data(value, value + value_len);
|
||||
return (*this->data_to_value_func_)(data);
|
||||
} else {
|
||||
return value[0];
|
||||
}
|
||||
}
|
||||
|
||||
void BLESensor::update() {
|
||||
if (this->node_state != espbt::ClientState::Established) {
|
||||
ESP_LOGW(TAG, "[%s] Cannot poll, not connected", this->get_name().c_str());
|
||||
return;
|
||||
}
|
||||
if (this->handle == 0) {
|
||||
ESP_LOGW(TAG, "[%s] Cannot poll, no service or characteristic found", this->get_name().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto status =
|
||||
esp_ble_gattc_read_char(this->parent()->gattc_if, this->parent()->conn_id, this->handle, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (status) {
|
||||
this->status_set_warning();
|
||||
this->publish_state(NAN);
|
||||
ESP_LOGW(TAG, "[%s] Error sending read request for sensor, status=%d", this->get_name().c_str(), status);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
#endif
|
||||
51
esphome/components/ble_client/sensor/ble_sensor.h
Normal file
51
esphome/components/ble_client/sensor/ble_sensor.h
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
using data_to_value_t = std::function<float(std::vector<uint8_t>)>;
|
||||
|
||||
class BLESensor : public sensor::Sensor, public PollingComponent, public BLEClientNode {
|
||||
public:
|
||||
void loop() override;
|
||||
void update() override;
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
void set_service_uuid16(uint16_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||
void set_service_uuid32(uint32_t uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||
void set_service_uuid128(uint8_t *uuid) { this->service_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
void set_char_uuid16(uint16_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||
void set_char_uuid32(uint32_t uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
void set_descr_uuid16(uint16_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint16(uuid); }
|
||||
void set_descr_uuid32(uint32_t uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_uint32(uuid); }
|
||||
void set_descr_uuid128(uint8_t *uuid) { this->descr_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
void set_data_to_value(data_to_value_t &&lambda_) { this->data_to_value_func_ = lambda_; }
|
||||
void set_enable_notify(bool notify) { this->notify_ = notify; }
|
||||
uint16_t handle;
|
||||
|
||||
protected:
|
||||
uint32_t hash_base() override;
|
||||
float parse_data(uint8_t *value, uint16_t value_len);
|
||||
optional<data_to_value_t> data_to_value_func_{};
|
||||
bool notify_;
|
||||
espbt::ESPBTUUID service_uuid_;
|
||||
espbt::ESPBTUUID char_uuid_;
|
||||
espbt::ESPBTUUID descr_uuid_;
|
||||
};
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
#endif
|
||||
30
esphome/components/ble_client/switch/__init__.py
Normal file
30
esphome/components/ble_client/switch/__init__.py
Normal file
@@ -0,0 +1,30 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import switch, ble_client
|
||||
from esphome.const import CONF_ICON, CONF_ID, CONF_INVERTED, ICON_BLUETOOTH
|
||||
from .. import ble_client_ns
|
||||
|
||||
BLEClientSwitch = ble_client_ns.class_(
|
||||
"BLEClientSwitch", switch.Switch, cg.Component, ble_client.BLEClientNode
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
switch.SWITCH_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLEClientSwitch),
|
||||
cv.Optional(CONF_INVERTED): cv.invalid(
|
||||
"BLE client switches do not support inverted mode!"
|
||||
),
|
||||
cv.Optional(CONF_ICON, default=ICON_BLUETOOTH): switch.icon,
|
||||
}
|
||||
)
|
||||
.extend(ble_client.BLE_CLIENT_SCHEMA)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await switch.register_switch(var, config)
|
||||
await ble_client.register_ble_node(var, config)
|
||||
39
esphome/components/ble_client/switch/ble_switch.cpp
Normal file
39
esphome/components/ble_client/switch/ble_switch.cpp
Normal file
@@ -0,0 +1,39 @@
|
||||
#include "ble_switch.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
static const char *const TAG = "ble_switch";
|
||||
|
||||
void BLEClientSwitch::write_state(bool state) {
|
||||
this->parent_->set_enabled(state);
|
||||
this->publish_state(state);
|
||||
}
|
||||
|
||||
void BLEClientSwitch::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) {
|
||||
switch (event) {
|
||||
case ESP_GATTC_REG_EVT:
|
||||
this->publish_state(this->parent_->enabled);
|
||||
break;
|
||||
case ESP_GATTC_OPEN_EVT:
|
||||
this->node_state = espbt::ClientState::Established;
|
||||
break;
|
||||
case ESP_GATTC_DISCONNECT_EVT:
|
||||
this->node_state = espbt::ClientState::Idle;
|
||||
this->publish_state(this->parent_->enabled);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BLEClientSwitch::dump_config() { LOG_SWITCH("", "BLE Client Switch", this); }
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
#endif
|
||||
30
esphome/components/ble_client/switch/ble_switch.h
Normal file
30
esphome/components/ble_client/switch/ble_switch.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/ble_client/ble_client.h"
|
||||
#include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
|
||||
#ifdef ARDUINO_ARCH_ESP32
|
||||
#include <esp_gattc_api.h>
|
||||
|
||||
namespace esphome {
|
||||
namespace ble_client {
|
||||
|
||||
namespace espbt = esphome::esp32_ble_tracker;
|
||||
|
||||
class BLEClientSwitch : public switch_::Switch, public Component, public BLEClientNode {
|
||||
public:
|
||||
void dump_config() override;
|
||||
void loop() override {}
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
};
|
||||
|
||||
} // namespace ble_client
|
||||
} // namespace esphome
|
||||
#endif
|
||||
@@ -27,11 +27,11 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield esp32_ble_tracker.register_ble_device(var, config)
|
||||
yield binary_sensor.register_binary_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
await binary_sensor.register_binary_sensor(var, config)
|
||||
|
||||
if CONF_MAC_ADDRESS in config:
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace esphome {
|
||||
namespace ble_presence {
|
||||
|
||||
static const char *TAG = "ble_presence";
|
||||
static const char *const TAG = "ble_presence";
|
||||
|
||||
void BLEPresenceDevice::dump_config() { LOG_BINARY_SENSOR("", "BLE Presence", this); }
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace esphome {
|
||||
namespace ble_rssi {
|
||||
|
||||
static const char *TAG = "ble_rssi";
|
||||
static const char *const TAG = "ble_rssi";
|
||||
|
||||
void BLERSSISensor::dump_config() { LOG_SENSOR("", "BLE RSSI Sensor", this); }
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ from esphome.const import (
|
||||
CONF_MAC_ADDRESS,
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_DECIBEL,
|
||||
ICON_EMPTY,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["esp32_ble_tracker"]
|
||||
@@ -18,7 +18,12 @@ BLERSSISensor = ble_rssi_ns.class_(
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
sensor.sensor_schema(UNIT_DECIBEL, ICON_EMPTY, 0, DEVICE_CLASS_SIGNAL_STRENGTH)
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_DECIBEL,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BLERSSISensor),
|
||||
@@ -32,11 +37,11 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield esp32_ble_tracker.register_ble_device(var, config)
|
||||
yield sensor.register_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
if CONF_MAC_ADDRESS in config:
|
||||
cg.add(var.set_address(config[CONF_MAC_ADDRESS].as_hex))
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace esphome {
|
||||
namespace ble_scanner {
|
||||
|
||||
static const char *TAG = "ble_scanner";
|
||||
static const char *const TAG = "ble_scanner";
|
||||
|
||||
void BLEScanner::dump_config() { LOG_TEXT_SENSOR("", "BLE Scanner", this); }
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ CONFIG_SCHEMA = cv.All(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield esp32_ble_tracker.register_ble_device(var, config)
|
||||
yield text_sensor.register_text_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await esp32_ble_tracker.register_ble_device(var, config)
|
||||
await text_sensor.register_text_sensor(var, config)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace bme280 {
|
||||
|
||||
static const char *TAG = "bme280.sensor";
|
||||
static const char *const TAG = "bme280.sensor";
|
||||
|
||||
static const uint8_t BME280_REGISTER_DIG_T1 = 0x88;
|
||||
static const uint8_t BME280_REGISTER_DIG_T2 = 0x8A;
|
||||
|
||||
@@ -11,7 +11,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_HECTOPASCAL,
|
||||
UNIT_PERCENT,
|
||||
@@ -48,7 +48,10 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BME280Component),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
||||
@@ -57,7 +60,10 @@ CONFIG_SCHEMA = (
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||
UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE
|
||||
unit_of_measurement=UNIT_HECTOPASCAL,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
||||
@@ -66,7 +72,10 @@ CONFIG_SCHEMA = (
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||
UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
||||
@@ -84,26 +93,26 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
conf = config[CONF_TEMPERATURE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
|
||||
|
||||
if CONF_PRESSURE in config:
|
||||
conf = config[CONF_PRESSURE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_pressure_sensor(sens))
|
||||
cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
|
||||
|
||||
if CONF_HUMIDITY in config:
|
||||
conf = config[CONF_HUMIDITY]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_humidity_sensor(sens))
|
||||
cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING]))
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace bme680 {
|
||||
|
||||
static const char *TAG = "bme680.sensor";
|
||||
static const char *const TAG = "bme680.sensor";
|
||||
|
||||
static const uint8_t BME680_REGISTER_COEFF1 = 0x89;
|
||||
static const uint8_t BME680_REGISTER_COEFF2 = 0xE1;
|
||||
|
||||
@@ -12,14 +12,13 @@ from esphome.const import (
|
||||
CONF_OVERSAMPLING,
|
||||
CONF_PRESSURE,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_OHM,
|
||||
ICON_GAS_CYLINDER,
|
||||
UNIT_CELSIUS,
|
||||
ICON_EMPTY,
|
||||
UNIT_HECTOPASCAL,
|
||||
UNIT_PERCENT,
|
||||
)
|
||||
@@ -58,7 +57,10 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BME680Component),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
||||
@@ -67,7 +69,10 @@ CONFIG_SCHEMA = (
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||
UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE
|
||||
unit_of_measurement=UNIT_HECTOPASCAL,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
||||
@@ -76,7 +81,10 @@ CONFIG_SCHEMA = (
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||
UNIT_PERCENT, ICON_EMPTY, 1, DEVICE_CLASS_HUMIDITY
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
||||
@@ -85,7 +93,10 @@ CONFIG_SCHEMA = (
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema(
|
||||
UNIT_OHM, ICON_GAS_CYLINDER, 1, DEVICE_CLASS_EMPTY
|
||||
unit_of_measurement=UNIT_OHM,
|
||||
icon=ICON_GAS_CYLINDER,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_IIR_FILTER, default="OFF"): cv.enum(
|
||||
IIR_FILTER_OPTIONS, upper=True
|
||||
@@ -114,32 +125,32 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
conf = config[CONF_TEMPERATURE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
|
||||
|
||||
if CONF_PRESSURE in config:
|
||||
conf = config[CONF_PRESSURE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_pressure_sensor(sens))
|
||||
cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
|
||||
|
||||
if CONF_HUMIDITY in config:
|
||||
conf = config[CONF_HUMIDITY]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_humidity_sensor(sens))
|
||||
cg.add(var.set_humidity_oversampling(conf[CONF_OVERSAMPLING]))
|
||||
|
||||
if CONF_GAS_RESISTANCE in config:
|
||||
conf = config[CONF_GAS_RESISTANCE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_gas_resistance_sensor(sens))
|
||||
|
||||
cg.add(var.set_iir_filter(IIR_FILTER_OPTIONS[config[CONF_IIR_FILTER]]))
|
||||
|
||||
64
esphome/components/bme680_bsec/__init__.py
Normal file
64
esphome/components/bme680_bsec/__init__.py
Normal file
@@ -0,0 +1,64 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import i2c
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
CODEOWNERS = ["@trvrnrth"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
AUTO_LOAD = ["sensor", "text_sensor"]
|
||||
|
||||
CONF_BME680_BSEC_ID = "bme680_bsec_id"
|
||||
CONF_TEMPERATURE_OFFSET = "temperature_offset"
|
||||
CONF_IAQ_MODE = "iaq_mode"
|
||||
CONF_SAMPLE_RATE = "sample_rate"
|
||||
CONF_STATE_SAVE_INTERVAL = "state_save_interval"
|
||||
|
||||
bme680_bsec_ns = cg.esphome_ns.namespace("bme680_bsec")
|
||||
|
||||
IAQMode = bme680_bsec_ns.enum("IAQMode")
|
||||
IAQ_MODE_OPTIONS = {
|
||||
"STATIC": IAQMode.IAQ_MODE_STATIC,
|
||||
"MOBILE": IAQMode.IAQ_MODE_MOBILE,
|
||||
}
|
||||
|
||||
SampleRate = bme680_bsec_ns.enum("SampleRate")
|
||||
SAMPLE_RATE_OPTIONS = {
|
||||
"LP": SampleRate.SAMPLE_RATE_LP,
|
||||
"ULP": SampleRate.SAMPLE_RATE_ULP,
|
||||
}
|
||||
|
||||
BME680BSECComponent = bme680_bsec_ns.class_(
|
||||
"BME680BSECComponent", cg.Component, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BME680BSECComponent),
|
||||
cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature,
|
||||
cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum(
|
||||
IAQ_MODE_OPTIONS, upper=True
|
||||
),
|
||||
cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum(
|
||||
SAMPLE_RATE_OPTIONS, upper=True
|
||||
),
|
||||
cv.Optional(
|
||||
CONF_STATE_SAVE_INTERVAL, default="6hours"
|
||||
): cv.positive_time_period_minutes,
|
||||
}
|
||||
).extend(i2c.i2c_device_schema(0x76))
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_temperature_offset(config[CONF_TEMPERATURE_OFFSET]))
|
||||
cg.add(var.set_iaq_mode(config[CONF_IAQ_MODE]))
|
||||
cg.add(var.set_sample_rate(config[CONF_SAMPLE_RATE]))
|
||||
cg.add(
|
||||
var.set_state_save_interval(config[CONF_STATE_SAVE_INTERVAL].total_milliseconds)
|
||||
)
|
||||
|
||||
cg.add_define("USE_BSEC")
|
||||
cg.add_library("BSEC Software Library", "1.6.1480")
|
||||
426
esphome/components/bme680_bsec/bme680_bsec.cpp
Normal file
426
esphome/components/bme680_bsec/bme680_bsec.cpp
Normal file
@@ -0,0 +1,426 @@
|
||||
#include "bme680_bsec.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include <string>
|
||||
|
||||
namespace esphome {
|
||||
namespace bme680_bsec {
|
||||
#ifdef USE_BSEC
|
||||
static const char *const TAG = "bme680_bsec.sensor";
|
||||
|
||||
static const std::string IAQ_ACCURACY_STATES[4] = {"Stabilizing", "Uncertain", "Calibrating", "Calibrated"};
|
||||
|
||||
BME680BSECComponent *BME680BSECComponent::instance;
|
||||
|
||||
void BME680BSECComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up BME680 via BSEC...");
|
||||
BME680BSECComponent::instance = this;
|
||||
|
||||
this->bsec_status_ = bsec_init();
|
||||
if (this->bsec_status_ != BSEC_OK) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->bme680_.dev_id = this->address_;
|
||||
this->bme680_.intf = BME680_I2C_INTF;
|
||||
this->bme680_.read = BME680BSECComponent::read_bytes_wrapper;
|
||||
this->bme680_.write = BME680BSECComponent::write_bytes_wrapper;
|
||||
this->bme680_.delay_ms = BME680BSECComponent::delay_ms;
|
||||
this->bme680_.amb_temp = 25;
|
||||
|
||||
this->bme680_status_ = bme680_init(&this->bme680_);
|
||||
if (this->bme680_status_ != BME680_OK) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this->sample_rate_ == SAMPLE_RATE_ULP) {
|
||||
const uint8_t bsec_config[] = {
|
||||
#include "config/generic_33v_300s_28d/bsec_iaq.txt"
|
||||
};
|
||||
this->set_config_(bsec_config);
|
||||
} else {
|
||||
const uint8_t bsec_config[] = {
|
||||
#include "config/generic_33v_3s_28d/bsec_iaq.txt"
|
||||
};
|
||||
this->set_config_(bsec_config);
|
||||
}
|
||||
this->update_subscription_();
|
||||
if (this->bsec_status_ != BSEC_OK) {
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
this->load_state_();
|
||||
}
|
||||
|
||||
void BME680BSECComponent::set_config_(const uint8_t *config) {
|
||||
uint8_t work_buffer[BSEC_MAX_WORKBUFFER_SIZE];
|
||||
this->bsec_status_ = bsec_set_configuration(config, BSEC_MAX_PROPERTY_BLOB_SIZE, work_buffer, sizeof(work_buffer));
|
||||
}
|
||||
|
||||
float BME680BSECComponent::calc_sensor_sample_rate_(SampleRate sample_rate) {
|
||||
if (sample_rate == SAMPLE_RATE_DEFAULT) {
|
||||
sample_rate = this->sample_rate_;
|
||||
}
|
||||
return sample_rate == SAMPLE_RATE_ULP ? BSEC_SAMPLE_RATE_ULP : BSEC_SAMPLE_RATE_LP;
|
||||
}
|
||||
|
||||
void BME680BSECComponent::update_subscription_() {
|
||||
bsec_sensor_configuration_t virtual_sensors[BSEC_NUMBER_OUTPUTS];
|
||||
int num_virtual_sensors = 0;
|
||||
|
||||
if (this->iaq_sensor_) {
|
||||
virtual_sensors[num_virtual_sensors].sensor_id =
|
||||
this->iaq_mode_ == IAQ_MODE_STATIC ? BSEC_OUTPUT_STATIC_IAQ : BSEC_OUTPUT_IAQ;
|
||||
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
|
||||
num_virtual_sensors++;
|
||||
}
|
||||
|
||||
if (this->co2_equivalent_sensor_) {
|
||||
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT;
|
||||
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
|
||||
num_virtual_sensors++;
|
||||
}
|
||||
|
||||
if (this->breath_voc_equivalent_sensor_) {
|
||||
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT;
|
||||
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
|
||||
num_virtual_sensors++;
|
||||
}
|
||||
|
||||
if (this->pressure_sensor_) {
|
||||
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_PRESSURE;
|
||||
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->pressure_sample_rate_);
|
||||
num_virtual_sensors++;
|
||||
}
|
||||
|
||||
if (this->gas_resistance_sensor_) {
|
||||
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_RAW_GAS;
|
||||
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(SAMPLE_RATE_DEFAULT);
|
||||
num_virtual_sensors++;
|
||||
}
|
||||
|
||||
if (this->temperature_sensor_) {
|
||||
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE;
|
||||
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->temperature_sample_rate_);
|
||||
num_virtual_sensors++;
|
||||
}
|
||||
|
||||
if (this->humidity_sensor_) {
|
||||
virtual_sensors[num_virtual_sensors].sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY;
|
||||
virtual_sensors[num_virtual_sensors].sample_rate = this->calc_sensor_sample_rate_(this->humidity_sample_rate_);
|
||||
num_virtual_sensors++;
|
||||
}
|
||||
|
||||
bsec_sensor_configuration_t sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
|
||||
uint8_t num_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
|
||||
this->bsec_status_ =
|
||||
bsec_update_subscription(virtual_sensors, num_virtual_sensors, sensor_settings, &num_sensor_settings);
|
||||
}
|
||||
|
||||
void BME680BSECComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "BME680 via BSEC:");
|
||||
|
||||
bsec_version_t version;
|
||||
bsec_get_version(&version);
|
||||
ESP_LOGCONFIG(TAG, " BSEC Version: %d.%d.%d.%d", version.major, version.minor, version.major_bugfix,
|
||||
version.minor_bugfix);
|
||||
|
||||
LOG_I2C_DEVICE(this);
|
||||
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication failed (BSEC Status: %d, BME680 Status: %d)", this->bsec_status_,
|
||||
this->bme680_status_);
|
||||
}
|
||||
|
||||
ESP_LOGCONFIG(TAG, " Temperature Offset: %.2f", this->temperature_offset_);
|
||||
ESP_LOGCONFIG(TAG, " IAQ Mode: %s", this->iaq_mode_ == IAQ_MODE_STATIC ? "Static" : "Mobile");
|
||||
ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->sample_rate_));
|
||||
ESP_LOGCONFIG(TAG, " State Save Interval: %ims", this->state_save_interval_ms_);
|
||||
|
||||
LOG_SENSOR(" ", "Temperature", this->temperature_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->temperature_sample_rate_));
|
||||
LOG_SENSOR(" ", "Pressure", this->pressure_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->pressure_sample_rate_));
|
||||
LOG_SENSOR(" ", "Humidity", this->humidity_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Sample Rate: %s", BME680_BSEC_SAMPLE_RATE_LOG(this->humidity_sample_rate_));
|
||||
LOG_SENSOR(" ", "Gas Resistance", this->gas_resistance_sensor_);
|
||||
LOG_SENSOR(" ", "IAQ", this->iaq_sensor_);
|
||||
LOG_SENSOR(" ", "Numeric IAQ Accuracy", this->iaq_accuracy_sensor_);
|
||||
LOG_TEXT_SENSOR(" ", "IAQ Accuracy", this->iaq_accuracy_text_sensor_);
|
||||
LOG_SENSOR(" ", "CO2 Equivalent", this->co2_equivalent_sensor_);
|
||||
LOG_SENSOR(" ", "Breath VOC Equivalent", this->breath_voc_equivalent_sensor_);
|
||||
}
|
||||
|
||||
float BME680BSECComponent::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
void BME680BSECComponent::loop() {
|
||||
this->run_();
|
||||
|
||||
if (this->bsec_status_ < BSEC_OK || this->bme680_status_ < BME680_OK) {
|
||||
this->status_set_error();
|
||||
} else {
|
||||
this->status_clear_error();
|
||||
}
|
||||
if (this->bsec_status_ > BSEC_OK || this->bme680_status_ > BME680_OK) {
|
||||
this->status_set_warning();
|
||||
} else {
|
||||
this->status_clear_warning();
|
||||
}
|
||||
}
|
||||
|
||||
void BME680BSECComponent::run_() {
|
||||
int64_t curr_time_ns = this->get_time_ns_();
|
||||
if (curr_time_ns < this->next_call_ns_) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Performing sensor run");
|
||||
|
||||
bsec_bme_settings_t bme680_settings;
|
||||
this->bsec_status_ = bsec_sensor_control(curr_time_ns, &bme680_settings);
|
||||
if (this->bsec_status_ < BSEC_OK) {
|
||||
ESP_LOGW(TAG, "Failed to fetch sensor control settings (BSEC Error Code %d)", this->bsec_status_);
|
||||
return;
|
||||
}
|
||||
this->next_call_ns_ = bme680_settings.next_call;
|
||||
|
||||
if (bme680_settings.trigger_measurement) {
|
||||
this->bme680_.tph_sett.os_temp = bme680_settings.temperature_oversampling;
|
||||
this->bme680_.tph_sett.os_pres = bme680_settings.pressure_oversampling;
|
||||
this->bme680_.tph_sett.os_hum = bme680_settings.humidity_oversampling;
|
||||
this->bme680_.gas_sett.run_gas = bme680_settings.run_gas;
|
||||
this->bme680_.gas_sett.heatr_temp = bme680_settings.heater_temperature;
|
||||
this->bme680_.gas_sett.heatr_dur = bme680_settings.heating_duration;
|
||||
this->bme680_.power_mode = BME680_FORCED_MODE;
|
||||
uint16_t desired_settings = BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL;
|
||||
this->bme680_status_ = bme680_set_sensor_settings(desired_settings, &this->bme680_);
|
||||
if (this->bme680_status_ != BME680_OK) {
|
||||
ESP_LOGW(TAG, "Failed to set sensor settings (BME680 Error Code %d)", this->bme680_status_);
|
||||
return;
|
||||
}
|
||||
|
||||
this->bme680_status_ = bme680_set_sensor_mode(&this->bme680_);
|
||||
if (this->bme680_status_ != BME680_OK) {
|
||||
ESP_LOGW(TAG, "Failed to set sensor mode (BME680 Error Code %d)", this->bme680_status_);
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t meas_dur = 0;
|
||||
bme680_get_profile_dur(&meas_dur, &this->bme680_);
|
||||
ESP_LOGV(TAG, "Queueing read in %ums", meas_dur);
|
||||
this->set_timeout("read", meas_dur,
|
||||
[this, curr_time_ns, bme680_settings]() { this->read_(curr_time_ns, bme680_settings); });
|
||||
} else {
|
||||
ESP_LOGV(TAG, "Measurement not required");
|
||||
this->read_(curr_time_ns, bme680_settings);
|
||||
}
|
||||
}
|
||||
|
||||
void BME680BSECComponent::read_(int64_t trigger_time_ns, bsec_bme_settings_t bme680_settings) {
|
||||
ESP_LOGV(TAG, "Reading data");
|
||||
|
||||
if (bme680_settings.trigger_measurement) {
|
||||
while (this->bme680_.power_mode != BME680_SLEEP_MODE) {
|
||||
this->bme680_status_ = bme680_get_sensor_mode(&this->bme680_);
|
||||
if (this->bme680_status_ != BME680_OK) {
|
||||
ESP_LOGW(TAG, "Failed to get sensor mode (BME680 Error Code %d)", this->bme680_status_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bme680_settings.process_data) {
|
||||
ESP_LOGV(TAG, "Data processing not required");
|
||||
return;
|
||||
}
|
||||
|
||||
struct bme680_field_data data;
|
||||
this->bme680_status_ = bme680_get_sensor_data(&data, &this->bme680_);
|
||||
|
||||
if (this->bme680_status_ != BME680_OK) {
|
||||
ESP_LOGW(TAG, "Failed to get sensor data (BME680 Error Code %d)", this->bme680_status_);
|
||||
return;
|
||||
}
|
||||
if (!(data.status & BME680_NEW_DATA_MSK)) {
|
||||
ESP_LOGD(TAG, "BME680 did not report new data");
|
||||
return;
|
||||
}
|
||||
|
||||
bsec_input_t inputs[BSEC_MAX_PHYSICAL_SENSOR]; // Temperature, Pressure, Humidity & Gas Resistance
|
||||
uint8_t num_inputs = 0;
|
||||
|
||||
if (bme680_settings.process_data & BSEC_PROCESS_TEMPERATURE) {
|
||||
inputs[num_inputs].sensor_id = BSEC_INPUT_TEMPERATURE;
|
||||
inputs[num_inputs].signal = data.temperature / 100.0f;
|
||||
inputs[num_inputs].time_stamp = trigger_time_ns;
|
||||
num_inputs++;
|
||||
|
||||
// Temperature offset from the real temperature due to external heat sources
|
||||
inputs[num_inputs].sensor_id = BSEC_INPUT_HEATSOURCE;
|
||||
inputs[num_inputs].signal = this->temperature_offset_;
|
||||
inputs[num_inputs].time_stamp = trigger_time_ns;
|
||||
num_inputs++;
|
||||
}
|
||||
if (bme680_settings.process_data & BSEC_PROCESS_HUMIDITY) {
|
||||
inputs[num_inputs].sensor_id = BSEC_INPUT_HUMIDITY;
|
||||
inputs[num_inputs].signal = data.humidity / 1000.0f;
|
||||
inputs[num_inputs].time_stamp = trigger_time_ns;
|
||||
num_inputs++;
|
||||
}
|
||||
if (bme680_settings.process_data & BSEC_PROCESS_PRESSURE) {
|
||||
inputs[num_inputs].sensor_id = BSEC_INPUT_PRESSURE;
|
||||
inputs[num_inputs].signal = data.pressure;
|
||||
inputs[num_inputs].time_stamp = trigger_time_ns;
|
||||
num_inputs++;
|
||||
}
|
||||
if (bme680_settings.process_data & BSEC_PROCESS_GAS) {
|
||||
if (data.status & BME680_GASM_VALID_MSK) {
|
||||
inputs[num_inputs].sensor_id = BSEC_INPUT_GASRESISTOR;
|
||||
inputs[num_inputs].signal = data.gas_resistance;
|
||||
inputs[num_inputs].time_stamp = trigger_time_ns;
|
||||
num_inputs++;
|
||||
} else {
|
||||
ESP_LOGD(TAG, "BME680 did not report gas data");
|
||||
}
|
||||
}
|
||||
if (num_inputs < 1) {
|
||||
ESP_LOGD(TAG, "No signal inputs available for BSEC");
|
||||
return;
|
||||
}
|
||||
|
||||
bsec_output_t outputs[BSEC_NUMBER_OUTPUTS];
|
||||
uint8_t num_outputs = BSEC_NUMBER_OUTPUTS;
|
||||
this->bsec_status_ = bsec_do_steps(inputs, num_inputs, outputs, &num_outputs);
|
||||
if (this->bsec_status_ != BSEC_OK) {
|
||||
ESP_LOGW(TAG, "BSEC failed to process signals (BSEC Error Code %d)", this->bsec_status_);
|
||||
return;
|
||||
}
|
||||
if (num_outputs < 1) {
|
||||
ESP_LOGD(TAG, "No signal outputs provided by BSEC");
|
||||
return;
|
||||
}
|
||||
|
||||
this->publish_(outputs, num_outputs);
|
||||
}
|
||||
|
||||
void BME680BSECComponent::publish_(const bsec_output_t *outputs, uint8_t num_outputs) {
|
||||
ESP_LOGV(TAG, "Publishing sensor states");
|
||||
for (uint8_t i = 0; i < num_outputs; i++) {
|
||||
switch (outputs[i].sensor_id) {
|
||||
case BSEC_OUTPUT_IAQ:
|
||||
case BSEC_OUTPUT_STATIC_IAQ:
|
||||
uint8_t accuracy;
|
||||
accuracy = outputs[i].accuracy;
|
||||
this->publish_sensor_state_(this->iaq_sensor_, outputs[i].signal);
|
||||
this->publish_sensor_state_(this->iaq_accuracy_text_sensor_, IAQ_ACCURACY_STATES[accuracy]);
|
||||
this->publish_sensor_state_(this->iaq_accuracy_sensor_, accuracy, true);
|
||||
|
||||
// Queue up an opportunity to save state
|
||||
this->defer("save_state", [this, accuracy]() { this->save_state_(accuracy); });
|
||||
break;
|
||||
case BSEC_OUTPUT_CO2_EQUIVALENT:
|
||||
this->publish_sensor_state_(this->co2_equivalent_sensor_, outputs[i].signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
|
||||
this->publish_sensor_state_(this->breath_voc_equivalent_sensor_, outputs[i].signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_PRESSURE:
|
||||
this->publish_sensor_state_(this->pressure_sensor_, outputs[i].signal / 100.0f);
|
||||
break;
|
||||
case BSEC_OUTPUT_RAW_GAS:
|
||||
this->publish_sensor_state_(this->gas_resistance_sensor_, outputs[i].signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
|
||||
this->publish_sensor_state_(this->temperature_sensor_, outputs[i].signal);
|
||||
break;
|
||||
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
|
||||
this->publish_sensor_state_(this->humidity_sensor_, outputs[i].signal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64_t BME680BSECComponent::get_time_ns_() {
|
||||
int64_t time_ms = millis();
|
||||
if (this->last_time_ms_ > time_ms) {
|
||||
this->millis_overflow_counter_++;
|
||||
}
|
||||
this->last_time_ms_ = time_ms;
|
||||
|
||||
return (time_ms + ((int64_t) this->millis_overflow_counter_ << 32)) * INT64_C(1000000);
|
||||
}
|
||||
|
||||
void BME680BSECComponent::publish_sensor_state_(sensor::Sensor *sensor, float value, bool change_only) {
|
||||
if (!sensor || (change_only && sensor->has_state() && sensor->state == value)) {
|
||||
return;
|
||||
}
|
||||
sensor->publish_state(value);
|
||||
}
|
||||
|
||||
void BME680BSECComponent::publish_sensor_state_(text_sensor::TextSensor *sensor, std::string value) {
|
||||
if (!sensor || (sensor->has_state() && sensor->state == value)) {
|
||||
return;
|
||||
}
|
||||
sensor->publish_state(value);
|
||||
}
|
||||
|
||||
int8_t BME680BSECComponent::read_bytes_wrapper(uint8_t address, uint8_t a_register, uint8_t *data, uint16_t len) {
|
||||
return BME680BSECComponent::instance->read_bytes(a_register, data, len) ? 0 : -1;
|
||||
}
|
||||
|
||||
int8_t BME680BSECComponent::write_bytes_wrapper(uint8_t address, uint8_t a_register, uint8_t *data, uint16_t len) {
|
||||
return BME680BSECComponent::instance->write_bytes(a_register, data, len) ? 0 : -1;
|
||||
}
|
||||
|
||||
void BME680BSECComponent::delay_ms(uint32_t period) {
|
||||
ESP_LOGV(TAG, "Delaying for %ums", period);
|
||||
delay(period);
|
||||
}
|
||||
|
||||
void BME680BSECComponent::load_state_() {
|
||||
uint32_t hash = fnv1_hash("bme680_bsec_state_" + to_string(this->address_));
|
||||
this->bsec_state_ = global_preferences.make_preference<uint8_t[BSEC_MAX_STATE_BLOB_SIZE]>(hash, true);
|
||||
|
||||
uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
|
||||
if (this->bsec_state_.load(&state)) {
|
||||
ESP_LOGV(TAG, "Loading state");
|
||||
uint8_t work_buffer[BSEC_MAX_WORKBUFFER_SIZE];
|
||||
this->bsec_status_ = bsec_set_state(state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer, sizeof(work_buffer));
|
||||
if (this->bsec_status_ != BSEC_OK) {
|
||||
ESP_LOGW(TAG, "Failed to load state (BSEC Error Code %d)", this->bsec_status_);
|
||||
}
|
||||
ESP_LOGI(TAG, "Loaded state");
|
||||
}
|
||||
}
|
||||
|
||||
void BME680BSECComponent::save_state_(uint8_t accuracy) {
|
||||
if (accuracy < 3 || (millis() - this->last_state_save_ms_ < this->state_save_interval_ms_)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Saving state");
|
||||
|
||||
uint8_t state[BSEC_MAX_STATE_BLOB_SIZE];
|
||||
uint8_t work_buffer[BSEC_MAX_STATE_BLOB_SIZE];
|
||||
uint32_t num_serialized_state = BSEC_MAX_STATE_BLOB_SIZE;
|
||||
|
||||
this->bsec_status_ =
|
||||
bsec_get_state(0, state, BSEC_MAX_STATE_BLOB_SIZE, work_buffer, BSEC_MAX_STATE_BLOB_SIZE, &num_serialized_state);
|
||||
if (this->bsec_status_ != BSEC_OK) {
|
||||
ESP_LOGW(TAG, "Failed fetch state for save (BSEC Error Code %d)", this->bsec_status_);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this->bsec_state_.save(&state)) {
|
||||
ESP_LOGW(TAG, "Failed to save state");
|
||||
return;
|
||||
}
|
||||
this->last_state_save_ms_ = millis();
|
||||
|
||||
ESP_LOGI(TAG, "Saved state");
|
||||
}
|
||||
#endif
|
||||
} // namespace bme680_bsec
|
||||
} // namespace esphome
|
||||
110
esphome/components/bme680_bsec/bme680_bsec.h
Normal file
110
esphome/components/bme680_bsec/bme680_bsec.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include <map>
|
||||
|
||||
#ifdef USE_BSEC
|
||||
#include <bsec.h>
|
||||
#endif
|
||||
|
||||
namespace esphome {
|
||||
namespace bme680_bsec {
|
||||
#ifdef USE_BSEC
|
||||
|
||||
enum IAQMode {
|
||||
IAQ_MODE_STATIC = 0,
|
||||
IAQ_MODE_MOBILE = 1,
|
||||
};
|
||||
|
||||
enum SampleRate {
|
||||
SAMPLE_RATE_LP = 0,
|
||||
SAMPLE_RATE_ULP = 1,
|
||||
SAMPLE_RATE_DEFAULT = 2,
|
||||
};
|
||||
|
||||
#define BME680_BSEC_SAMPLE_RATE_LOG(r) (r == SAMPLE_RATE_DEFAULT ? "Default" : (r == SAMPLE_RATE_ULP ? "ULP" : "LP"))
|
||||
|
||||
class BME680BSECComponent : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
void set_temperature_offset(float offset) { this->temperature_offset_ = offset; }
|
||||
void set_iaq_mode(IAQMode iaq_mode) { this->iaq_mode_ = iaq_mode; }
|
||||
void set_state_save_interval(uint32_t interval) { this->state_save_interval_ms_ = interval; }
|
||||
|
||||
void set_sample_rate(SampleRate sample_rate) { this->sample_rate_ = sample_rate; }
|
||||
void set_temperature_sample_rate(SampleRate sample_rate) { this->temperature_sample_rate_ = sample_rate; }
|
||||
void set_pressure_sample_rate(SampleRate sample_rate) { this->pressure_sample_rate_ = sample_rate; }
|
||||
void set_humidity_sample_rate(SampleRate sample_rate) { this->humidity_sample_rate_ = sample_rate; }
|
||||
|
||||
void set_temperature_sensor(sensor::Sensor *sensor) { this->temperature_sensor_ = sensor; }
|
||||
void set_pressure_sensor(sensor::Sensor *sensor) { this->pressure_sensor_ = sensor; }
|
||||
void set_humidity_sensor(sensor::Sensor *sensor) { this->humidity_sensor_ = sensor; }
|
||||
void set_gas_resistance_sensor(sensor::Sensor *sensor) { this->gas_resistance_sensor_ = sensor; }
|
||||
void set_iaq_sensor(sensor::Sensor *sensor) { this->iaq_sensor_ = sensor; }
|
||||
void set_iaq_accuracy_text_sensor(text_sensor::TextSensor *sensor) { this->iaq_accuracy_text_sensor_ = sensor; }
|
||||
void set_iaq_accuracy_sensor(sensor::Sensor *sensor) { this->iaq_accuracy_sensor_ = sensor; }
|
||||
void set_co2_equivalent_sensor(sensor::Sensor *sensor) { this->co2_equivalent_sensor_ = sensor; }
|
||||
void set_breath_voc_equivalent_sensor(sensor::Sensor *sensor) { this->breath_voc_equivalent_sensor_ = sensor; }
|
||||
|
||||
static BME680BSECComponent *instance;
|
||||
static int8_t read_bytes_wrapper(uint8_t address, uint8_t a_register, uint8_t *data, uint16_t len);
|
||||
static int8_t write_bytes_wrapper(uint8_t address, uint8_t a_register, uint8_t *data, uint16_t len);
|
||||
static void delay_ms(uint32_t period);
|
||||
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
void loop() override;
|
||||
|
||||
protected:
|
||||
void set_config_(const uint8_t *config);
|
||||
float calc_sensor_sample_rate_(SampleRate sample_rate);
|
||||
void update_subscription_();
|
||||
|
||||
void run_();
|
||||
void read_(int64_t trigger_time_ns, bsec_bme_settings_t bme680_settings);
|
||||
void publish_(const bsec_output_t *outputs, uint8_t num_outputs);
|
||||
int64_t get_time_ns_();
|
||||
|
||||
void publish_sensor_state_(sensor::Sensor *sensor, float value, bool change_only = false);
|
||||
void publish_sensor_state_(text_sensor::TextSensor *sensor, std::string value);
|
||||
|
||||
void load_state_();
|
||||
void save_state_(uint8_t accuracy);
|
||||
|
||||
struct bme680_dev bme680_;
|
||||
bsec_library_return_t bsec_status_{BSEC_OK};
|
||||
int8_t bme680_status_{BME680_OK};
|
||||
|
||||
int64_t last_time_ms_{0};
|
||||
uint32_t millis_overflow_counter_{0};
|
||||
int64_t next_call_ns_{0};
|
||||
|
||||
ESPPreferenceObject bsec_state_;
|
||||
uint32_t state_save_interval_ms_{21600000}; // 6 hours - 4 times a day
|
||||
uint32_t last_state_save_ms_ = 0;
|
||||
|
||||
float temperature_offset_{0};
|
||||
IAQMode iaq_mode_{IAQ_MODE_STATIC};
|
||||
|
||||
SampleRate sample_rate_{SAMPLE_RATE_LP}; // Core/gas sample rate
|
||||
SampleRate temperature_sample_rate_{SAMPLE_RATE_DEFAULT};
|
||||
SampleRate pressure_sample_rate_{SAMPLE_RATE_DEFAULT};
|
||||
SampleRate humidity_sample_rate_{SAMPLE_RATE_DEFAULT};
|
||||
|
||||
sensor::Sensor *temperature_sensor_;
|
||||
sensor::Sensor *pressure_sensor_;
|
||||
sensor::Sensor *humidity_sensor_;
|
||||
sensor::Sensor *gas_resistance_sensor_;
|
||||
sensor::Sensor *iaq_sensor_;
|
||||
text_sensor::TextSensor *iaq_accuracy_text_sensor_;
|
||||
sensor::Sensor *iaq_accuracy_sensor_;
|
||||
sensor::Sensor *co2_equivalent_sensor_;
|
||||
sensor::Sensor *breath_voc_equivalent_sensor_;
|
||||
};
|
||||
#endif
|
||||
} // namespace bme680_bsec
|
||||
} // namespace esphome
|
||||
126
esphome/components/bme680_bsec/sensor.py
Normal file
126
esphome/components/bme680_bsec/sensor.py
Normal file
@@ -0,0 +1,126 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import sensor
|
||||
from esphome.const import (
|
||||
CONF_GAS_RESISTANCE,
|
||||
CONF_HUMIDITY,
|
||||
CONF_PRESSURE,
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_HECTOPASCAL,
|
||||
UNIT_OHM,
|
||||
UNIT_PARTS_PER_MILLION,
|
||||
UNIT_PERCENT,
|
||||
ICON_GAS_CYLINDER,
|
||||
ICON_GAUGE,
|
||||
ICON_THERMOMETER,
|
||||
ICON_WATER_PERCENT,
|
||||
)
|
||||
from . import (
|
||||
BME680BSECComponent,
|
||||
CONF_BME680_BSEC_ID,
|
||||
CONF_SAMPLE_RATE,
|
||||
SAMPLE_RATE_OPTIONS,
|
||||
)
|
||||
|
||||
DEPENDENCIES = ["bme680_bsec"]
|
||||
|
||||
CONF_IAQ = "iaq"
|
||||
CONF_IAQ_ACCURACY = "iaq_accuracy"
|
||||
CONF_CO2_EQUIVALENT = "co2_equivalent"
|
||||
CONF_BREATH_VOC_EQUIVALENT = "breath_voc_equivalent"
|
||||
UNIT_IAQ = "IAQ"
|
||||
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
|
||||
ICON_TEST_TUBE = "mdi:test-tube"
|
||||
|
||||
TYPES = [
|
||||
CONF_TEMPERATURE,
|
||||
CONF_PRESSURE,
|
||||
CONF_HUMIDITY,
|
||||
CONF_GAS_RESISTANCE,
|
||||
CONF_IAQ,
|
||||
CONF_IAQ_ACCURACY,
|
||||
CONF_CO2_EQUIVALENT,
|
||||
CONF_BREATH_VOC_EQUIVALENT,
|
||||
]
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
icon=ICON_THERMOMETER,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
|
||||
),
|
||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_HECTOPASCAL,
|
||||
icon=ICON_GAUGE,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
|
||||
),
|
||||
cv.Optional(CONF_HUMIDITY): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PERCENT,
|
||||
icon=ICON_WATER_PERCENT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_HUMIDITY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{cv.Optional(CONF_SAMPLE_RATE): cv.enum(SAMPLE_RATE_OPTIONS, upper=True)}
|
||||
),
|
||||
cv.Optional(CONF_GAS_RESISTANCE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_OHM,
|
||||
icon=ICON_GAS_CYLINDER,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_IAQ): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_IAQ,
|
||||
icon=ICON_GAUGE,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_IAQ_ACCURACY): sensor.sensor_schema(
|
||||
icon=ICON_ACCURACY,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CO2_EQUIVALENT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
||||
icon=ICON_TEST_TUBE,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_BREATH_VOC_EQUIVALENT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
||||
icon=ICON_TEST_TUBE,
|
||||
accuracy_decimals=1,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def setup_conf(config, key, hub):
|
||||
if key in config:
|
||||
conf = config[key]
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(getattr(hub, f"set_{key}_sensor")(sens))
|
||||
if CONF_SAMPLE_RATE in conf:
|
||||
cg.add(getattr(hub, f"set_{key}_sample_rate")(conf[CONF_SAMPLE_RATE]))
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_BME680_BSEC_ID])
|
||||
for key in TYPES:
|
||||
await setup_conf(config, key, hub)
|
||||
38
esphome/components/bme680_bsec/text_sensor.py
Normal file
38
esphome/components/bme680_bsec/text_sensor.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import text_sensor
|
||||
from esphome.const import CONF_ID, CONF_ICON
|
||||
from . import BME680BSECComponent, CONF_BME680_BSEC_ID
|
||||
|
||||
DEPENDENCIES = ["bme680_bsec"]
|
||||
|
||||
CONF_IAQ_ACCURACY = "iaq_accuracy"
|
||||
ICON_ACCURACY = "mdi:checkbox-marked-circle-outline"
|
||||
|
||||
TYPES = [CONF_IAQ_ACCURACY]
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_BME680_BSEC_ID): cv.use_id(BME680BSECComponent),
|
||||
cv.Optional(CONF_IAQ_ACCURACY): text_sensor.TEXT_SENSOR_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(text_sensor.TextSensor),
|
||||
cv.Optional(CONF_ICON, default=ICON_ACCURACY): cv.icon,
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def setup_conf(config, key, hub):
|
||||
if key in config:
|
||||
conf = config[key]
|
||||
sens = cg.new_Pvariable(conf[CONF_ID])
|
||||
await text_sensor.register_text_sensor(sens, conf)
|
||||
cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_BME680_BSEC_ID])
|
||||
for key in TYPES:
|
||||
await setup_conf(config, key, hub)
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace bmp085 {
|
||||
|
||||
static const char *TAG = "bmp085.sensor";
|
||||
static const char *const TAG = "bmp085.sensor";
|
||||
|
||||
static const uint8_t BMP085_ADDRESS = 0x77;
|
||||
static const uint8_t BMP085_REGISTER_AC1_H = 0xAA;
|
||||
|
||||
@@ -7,8 +7,8 @@ from esphome.const import (
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
ICON_EMPTY,
|
||||
UNIT_HECTOPASCAL,
|
||||
)
|
||||
|
||||
@@ -24,10 +24,16 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BMP085Component),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||
UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE
|
||||
unit_of_measurement=UNIT_HECTOPASCAL,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
@@ -36,17 +42,17 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
conf = config[CONF_TEMPERATURE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_temperature(sens))
|
||||
|
||||
if CONF_PRESSURE in config:
|
||||
conf = config[CONF_PRESSURE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_pressure(sens))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace bmp280 {
|
||||
|
||||
static const char *TAG = "bmp280.sensor";
|
||||
static const char *const TAG = "bmp280.sensor";
|
||||
|
||||
static const uint8_t BMP280_REGISTER_STATUS = 0xF3;
|
||||
static const uint8_t BMP280_REGISTER_CONTROL = 0xF4;
|
||||
|
||||
@@ -7,8 +7,8 @@ from esphome.const import (
|
||||
CONF_TEMPERATURE,
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
ICON_EMPTY,
|
||||
UNIT_HECTOPASCAL,
|
||||
CONF_IIR_FILTER,
|
||||
CONF_OVERSAMPLING,
|
||||
@@ -45,7 +45,10 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BMP280Component),
|
||||
cv.Optional(CONF_TEMPERATURE): sensor.sensor_schema(
|
||||
UNIT_CELSIUS, ICON_EMPTY, 1, DEVICE_CLASS_TEMPERATURE
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
||||
@@ -54,7 +57,10 @@ CONFIG_SCHEMA = (
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_PRESSURE): sensor.sensor_schema(
|
||||
UNIT_HECTOPASCAL, ICON_EMPTY, 1, DEVICE_CLASS_PRESSURE
|
||||
unit_of_measurement=UNIT_HECTOPASCAL,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_PRESSURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
).extend(
|
||||
{
|
||||
cv.Optional(CONF_OVERSAMPLING, default="16X"): cv.enum(
|
||||
@@ -72,19 +78,19 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
conf = config[CONF_TEMPERATURE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_temperature_sensor(sens))
|
||||
cg.add(var.set_temperature_oversampling(conf[CONF_OVERSAMPLING]))
|
||||
|
||||
if CONF_PRESSURE in config:
|
||||
conf = config[CONF_PRESSURE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_pressure_sensor(sens))
|
||||
cg.add(var.set_pressure_oversampling(conf[CONF_OVERSAMPLING]))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import automation
|
||||
from esphome.core import CORE, coroutine
|
||||
from esphome.core import CORE
|
||||
from esphome.const import CONF_ID, CONF_TRIGGER_ID, CONF_DATA
|
||||
|
||||
CODEOWNERS = ["@mvturnho", "@danielschramm"]
|
||||
@@ -82,10 +82,9 @@ CANBUS_SCHEMA = cv.Schema(
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
@coroutine
|
||||
def setup_canbus_core_(var, config):
|
||||
async def setup_canbus_core_(var, config):
|
||||
validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID])
|
||||
yield cg.register_component(var, config)
|
||||
await cg.register_component(var, config)
|
||||
cg.add(var.set_can_id([config[CONF_CAN_ID]]))
|
||||
cg.add(var.set_use_extended_id([config[CONF_USE_EXTENDED_ID]]))
|
||||
cg.add(var.set_bitrate(CAN_SPEEDS[config[CONF_BIT_RATE]]))
|
||||
@@ -95,17 +94,16 @@ def setup_canbus_core_(var, config):
|
||||
ext_id = conf[CONF_USE_EXTENDED_ID]
|
||||
validate_id(can_id, ext_id)
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var, can_id, ext_id)
|
||||
yield cg.register_component(trigger, conf)
|
||||
yield automation.build_automation(
|
||||
await cg.register_component(trigger, conf)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.std_vector.template(cg.uint8), "x")], conf
|
||||
)
|
||||
|
||||
|
||||
@coroutine
|
||||
def register_canbus(var, config):
|
||||
async def register_canbus(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.new_Pvariable(config[CONF_ID], var)
|
||||
yield setup_canbus_core_(var, config)
|
||||
await setup_canbus_core_(var, config)
|
||||
|
||||
|
||||
# Actions
|
||||
@@ -122,16 +120,16 @@ def register_canbus(var, config):
|
||||
key=CONF_DATA,
|
||||
),
|
||||
)
|
||||
def canbus_action_to_code(config, action_id, template_arg, args):
|
||||
async def canbus_action_to_code(config, action_id, template_arg, args):
|
||||
validate_id(config[CONF_CAN_ID], config[CONF_USE_EXTENDED_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
yield cg.register_parented(var, config[CONF_CANBUS_ID])
|
||||
await cg.register_parented(var, config[CONF_CANBUS_ID])
|
||||
|
||||
if CONF_CAN_ID in config:
|
||||
can_id = yield cg.templatable(config[CONF_CAN_ID], args, cg.uint32)
|
||||
can_id = await cg.templatable(config[CONF_CAN_ID], args, cg.uint32)
|
||||
cg.add(var.set_can_id(can_id))
|
||||
|
||||
use_extended_id = yield cg.templatable(
|
||||
use_extended_id = await cg.templatable(
|
||||
config[CONF_USE_EXTENDED_ID], args, cg.uint32
|
||||
)
|
||||
cg.add(var.set_use_extended_id(use_extended_id))
|
||||
@@ -140,8 +138,8 @@ def canbus_action_to_code(config, action_id, template_arg, args):
|
||||
if isinstance(data, bytes):
|
||||
data = [int(x) for x in data]
|
||||
if cg.is_template(data):
|
||||
templ = yield cg.templatable(data, args, cg.std_vector.template(cg.uint8))
|
||||
templ = await cg.templatable(data, args, cg.std_vector.template(cg.uint8))
|
||||
cg.add(var.set_data_template(templ))
|
||||
else:
|
||||
cg.add(var.set_data_static(data))
|
||||
yield var
|
||||
return var
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace canbus {
|
||||
|
||||
static const char *TAG = "canbus";
|
||||
static const char *const TAG = "canbus";
|
||||
|
||||
void Canbus::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up Canbus...");
|
||||
|
||||
@@ -23,9 +23,9 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
@coroutine_with_priority(64.0)
|
||||
def to_code(config):
|
||||
paren = yield cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
|
||||
async def to_code(config):
|
||||
paren = await cg.get_variable(config[CONF_WEB_SERVER_BASE_ID])
|
||||
|
||||
var = cg.new_Pvariable(config[CONF_ID], paren)
|
||||
yield cg.register_component(var, config)
|
||||
await cg.register_component(var, config)
|
||||
cg.add_define("USE_CAPTIVE_PORTAL")
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace esphome {
|
||||
namespace captive_portal {
|
||||
|
||||
static const char *TAG = "captive_portal";
|
||||
static const char *const TAG = "captive_portal";
|
||||
|
||||
void CaptivePortal::handle_index(AsyncWebServerRequest *request) {
|
||||
AsyncResponseStream *stream = request->beginResponseStream("text/html");
|
||||
@@ -64,32 +64,11 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
|
||||
ESP_LOGI(TAG, "Captive Portal Requested WiFi Settings Change:");
|
||||
ESP_LOGI(TAG, " SSID='%s'", ssid.c_str());
|
||||
ESP_LOGI(TAG, " Password=" LOG_SECRET("'%s'"), psk.c_str());
|
||||
this->override_sta_(ssid, psk);
|
||||
wifi::global_wifi_component->save_wifi_sta(ssid, psk);
|
||||
request->redirect("/?save=true");
|
||||
}
|
||||
void CaptivePortal::override_sta_(const std::string &ssid, const std::string &password) {
|
||||
CaptivePortalSettings save{};
|
||||
strcpy(save.ssid, ssid.c_str());
|
||||
strcpy(save.password, password.c_str());
|
||||
this->pref_.save(&save);
|
||||
|
||||
wifi::WiFiAP sta{};
|
||||
sta.set_ssid(ssid);
|
||||
sta.set_password(password);
|
||||
wifi::global_wifi_component->set_sta(sta);
|
||||
}
|
||||
|
||||
void CaptivePortal::setup() {
|
||||
// Hash with compilation time
|
||||
// This ensures the AP override is not applied for OTA
|
||||
uint32_t hash = fnv1_hash(App.get_compilation_time());
|
||||
this->pref_ = global_preferences.make_preference<CaptivePortalSettings>(hash, true);
|
||||
|
||||
CaptivePortalSettings save{};
|
||||
if (this->pref_.load(&save)) {
|
||||
this->override_sta_(save.ssid, save.password);
|
||||
}
|
||||
}
|
||||
void CaptivePortal::setup() {}
|
||||
void CaptivePortal::start() {
|
||||
this->base_->init();
|
||||
if (!this->initialized_) {
|
||||
@@ -168,7 +147,7 @@ float CaptivePortal::get_setup_priority() const {
|
||||
}
|
||||
void CaptivePortal::dump_config() { ESP_LOGCONFIG(TAG, "Captive Portal:"); }
|
||||
|
||||
CaptivePortal *global_captive_portal = nullptr;
|
||||
CaptivePortal *global_captive_portal = nullptr; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace captive_portal
|
||||
} // namespace esphome
|
||||
|
||||
@@ -10,11 +10,6 @@ namespace esphome {
|
||||
|
||||
namespace captive_portal {
|
||||
|
||||
struct CaptivePortalSettings {
|
||||
char ssid[33];
|
||||
char password[65];
|
||||
} PACKED; // NOLINT
|
||||
|
||||
class CaptivePortal : public AsyncWebHandler, public Component {
|
||||
public:
|
||||
CaptivePortal(web_server_base::WebServerBase *base);
|
||||
@@ -67,16 +62,13 @@ class CaptivePortal : public AsyncWebHandler, public Component {
|
||||
void handleRequest(AsyncWebServerRequest *req) override;
|
||||
|
||||
protected:
|
||||
void override_sta_(const std::string &ssid, const std::string &password);
|
||||
|
||||
web_server_base::WebServerBase *base_;
|
||||
bool initialized_{false};
|
||||
bool active_{false};
|
||||
ESPPreferenceObject pref_;
|
||||
DNSServer *dns_server_{nullptr};
|
||||
};
|
||||
|
||||
extern CaptivePortal *global_captive_portal;
|
||||
extern CaptivePortal *global_captive_portal; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
} // namespace captive_portal
|
||||
} // namespace esphome
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace ccs811 {
|
||||
|
||||
static const char *TAG = "ccs811";
|
||||
static const char *const TAG = "ccs811";
|
||||
|
||||
// based on
|
||||
// - https://cdn.sparkfun.com/datasheets/BreakoutBoards/CCS811_Programming_Guide.pdf
|
||||
|
||||
@@ -3,10 +3,12 @@ import esphome.config_validation as cv
|
||||
from esphome.components import i2c, sensor
|
||||
from esphome.const import (
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_EMPTY,
|
||||
ICON_RADIATOR,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_PARTS_PER_MILLION,
|
||||
UNIT_PARTS_PER_BILLION,
|
||||
CONF_BASELINE,
|
||||
CONF_ECO2,
|
||||
CONF_TEMPERATURE,
|
||||
CONF_TVOC,
|
||||
CONF_HUMIDITY,
|
||||
@@ -20,18 +22,21 @@ CCS811Component = ccs811_ns.class_(
|
||||
"CCS811Component", cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONF_ECO2 = "eco2"
|
||||
CONF_BASELINE = "baseline"
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CCS811Component),
|
||||
cv.Required(CONF_ECO2): sensor.sensor_schema(
|
||||
UNIT_PARTS_PER_MILLION, ICON_MOLECULE_CO2, 0, DEVICE_CLASS_EMPTY
|
||||
unit_of_measurement=UNIT_PARTS_PER_MILLION,
|
||||
icon=ICON_MOLECULE_CO2,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Required(CONF_TVOC): sensor.sensor_schema(
|
||||
UNIT_PARTS_PER_BILLION, ICON_RADIATOR, 0, DEVICE_CLASS_EMPTY
|
||||
unit_of_measurement=UNIT_PARTS_PER_BILLION,
|
||||
icon=ICON_RADIATOR,
|
||||
accuracy_decimals=0,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_BASELINE): cv.hex_uint16_t,
|
||||
cv.Optional(CONF_TEMPERATURE): cv.use_id(sensor.Sensor),
|
||||
@@ -43,22 +48,22 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield i2c.register_i2c_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
sens = yield sensor.new_sensor(config[CONF_ECO2])
|
||||
sens = await sensor.new_sensor(config[CONF_ECO2])
|
||||
cg.add(var.set_co2(sens))
|
||||
sens = yield sensor.new_sensor(config[CONF_TVOC])
|
||||
sens = await sensor.new_sensor(config[CONF_TVOC])
|
||||
cg.add(var.set_tvoc(sens))
|
||||
|
||||
if CONF_BASELINE in config:
|
||||
cg.add(var.set_baseline(config[CONF_BASELINE]))
|
||||
|
||||
if CONF_TEMPERATURE in config:
|
||||
sens = yield cg.get_variable(config[CONF_TEMPERATURE])
|
||||
sens = await cg.get_variable(config[CONF_TEMPERATURE])
|
||||
cg.add(var.set_temperature(sens))
|
||||
if CONF_HUMIDITY in config:
|
||||
sens = yield cg.get_variable(config[CONF_HUMIDITY])
|
||||
sens = await cg.get_variable(config[CONF_HUMIDITY])
|
||||
cg.add(var.set_humidity(sens))
|
||||
|
||||
@@ -4,11 +4,15 @@ from esphome import automation
|
||||
from esphome.components import mqtt
|
||||
from esphome.const import (
|
||||
CONF_AWAY,
|
||||
CONF_CUSTOM_FAN_MODE,
|
||||
CONF_CUSTOM_PRESET,
|
||||
CONF_DISABLED_BY_DEFAULT,
|
||||
CONF_ID,
|
||||
CONF_INTERNAL,
|
||||
CONF_MAX_TEMPERATURE,
|
||||
CONF_MIN_TEMPERATURE,
|
||||
CONF_MODE,
|
||||
CONF_PRESET,
|
||||
CONF_TARGET_TEMPERATURE,
|
||||
CONF_TARGET_TEMPERATURE_HIGH,
|
||||
CONF_TARGET_TEMPERATURE_LOW,
|
||||
@@ -19,7 +23,7 @@ from esphome.const import (
|
||||
CONF_FAN_MODE,
|
||||
CONF_SWING_MODE,
|
||||
)
|
||||
from esphome.core import CORE, coroutine, coroutine_with_priority
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
@@ -33,11 +37,12 @@ ClimateTraits = climate_ns.class_("ClimateTraits")
|
||||
ClimateMode = climate_ns.enum("ClimateMode")
|
||||
CLIMATE_MODES = {
|
||||
"OFF": ClimateMode.CLIMATE_MODE_OFF,
|
||||
"AUTO": ClimateMode.CLIMATE_MODE_AUTO,
|
||||
"HEAT_COOL": ClimateMode.CLIMATE_MODE_HEAT_COOL,
|
||||
"COOL": ClimateMode.CLIMATE_MODE_COOL,
|
||||
"HEAT": ClimateMode.CLIMATE_MODE_HEAT,
|
||||
"DRY": ClimateMode.CLIMATE_MODE_DRY,
|
||||
"FAN_ONLY": ClimateMode.CLIMATE_MODE_FAN_ONLY,
|
||||
"AUTO": ClimateMode.CLIMATE_MODE_AUTO,
|
||||
}
|
||||
validate_climate_mode = cv.enum(CLIMATE_MODES, upper=True)
|
||||
|
||||
@@ -56,6 +61,19 @@ CLIMATE_FAN_MODES = {
|
||||
|
||||
validate_climate_fan_mode = cv.enum(CLIMATE_FAN_MODES, upper=True)
|
||||
|
||||
ClimatePreset = climate_ns.enum("ClimatePreset")
|
||||
CLIMATE_PRESETS = {
|
||||
"ECO": ClimatePreset.CLIMATE_PRESET_ECO,
|
||||
"AWAY": ClimatePreset.CLIMATE_PRESET_AWAY,
|
||||
"BOOST": ClimatePreset.CLIMATE_PRESET_BOOST,
|
||||
"COMFORT": ClimatePreset.CLIMATE_PRESET_COMFORT,
|
||||
"HOME": ClimatePreset.CLIMATE_PRESET_HOME,
|
||||
"SLEEP": ClimatePreset.CLIMATE_PRESET_SLEEP,
|
||||
"ACTIVITY": ClimatePreset.CLIMATE_PRESET_ACTIVITY,
|
||||
}
|
||||
|
||||
validate_climate_preset = cv.enum(CLIMATE_PRESETS, upper=True)
|
||||
|
||||
ClimateSwingMode = climate_ns.enum("ClimateSwingMode")
|
||||
CLIMATE_SWING_MODES = {
|
||||
"OFF": ClimateSwingMode.CLIMATE_SWING_OFF,
|
||||
@@ -69,7 +87,7 @@ validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True)
|
||||
# Actions
|
||||
ControlAction = climate_ns.class_("ControlAction", automation.Action)
|
||||
|
||||
CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
|
||||
CLIMATE_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Climate),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent),
|
||||
@@ -85,9 +103,9 @@ CLIMATE_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
|
||||
)
|
||||
|
||||
|
||||
@coroutine
|
||||
def setup_climate_core_(var, config):
|
||||
async def setup_climate_core_(var, config):
|
||||
cg.add(var.set_name(config[CONF_NAME]))
|
||||
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
|
||||
if CONF_INTERNAL in config:
|
||||
cg.add(var.set_internal(config[CONF_INTERNAL]))
|
||||
visual = config[CONF_VISUAL]
|
||||
@@ -100,15 +118,14 @@ def setup_climate_core_(var, config):
|
||||
|
||||
if CONF_MQTT_ID in config:
|
||||
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
|
||||
yield mqtt.register_mqtt_component(mqtt_, config)
|
||||
await mqtt.register_mqtt_component(mqtt_, config)
|
||||
|
||||
|
||||
@coroutine
|
||||
def register_climate(var, config):
|
||||
async def register_climate(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_climate(var))
|
||||
yield setup_climate_core_(var, config)
|
||||
await setup_climate_core_(var, config)
|
||||
|
||||
|
||||
CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
|
||||
@@ -119,7 +136,12 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_LOW): cv.templatable(cv.temperature),
|
||||
cv.Optional(CONF_TARGET_TEMPERATURE_HIGH): cv.templatable(cv.temperature),
|
||||
cv.Optional(CONF_AWAY): cv.templatable(cv.boolean),
|
||||
cv.Optional(CONF_FAN_MODE): cv.templatable(validate_climate_fan_mode),
|
||||
cv.Exclusive(CONF_FAN_MODE, "fan_mode"): cv.templatable(
|
||||
validate_climate_fan_mode
|
||||
),
|
||||
cv.Exclusive(CONF_CUSTOM_FAN_MODE, "fan_mode"): cv.string_strict,
|
||||
cv.Exclusive(CONF_PRESET, "preset"): cv.templatable(validate_climate_preset),
|
||||
cv.Exclusive(CONF_CUSTOM_PRESET, "preset"): cv.string_strict,
|
||||
cv.Optional(CONF_SWING_MODE): cv.templatable(validate_climate_swing_mode),
|
||||
}
|
||||
)
|
||||
@@ -128,40 +150,49 @@ CLIMATE_CONTROL_ACTION_SCHEMA = cv.Schema(
|
||||
@automation.register_action(
|
||||
"climate.control", ControlAction, CLIMATE_CONTROL_ACTION_SCHEMA
|
||||
)
|
||||
def climate_control_to_code(config, action_id, template_arg, args):
|
||||
paren = yield cg.get_variable(config[CONF_ID])
|
||||
async def climate_control_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
if CONF_MODE in config:
|
||||
template_ = yield cg.templatable(config[CONF_MODE], args, ClimateMode)
|
||||
template_ = await cg.templatable(config[CONF_MODE], args, ClimateMode)
|
||||
cg.add(var.set_mode(template_))
|
||||
if CONF_TARGET_TEMPERATURE in config:
|
||||
template_ = yield cg.templatable(config[CONF_TARGET_TEMPERATURE], args, float)
|
||||
template_ = await cg.templatable(config[CONF_TARGET_TEMPERATURE], args, float)
|
||||
cg.add(var.set_target_temperature(template_))
|
||||
if CONF_TARGET_TEMPERATURE_LOW in config:
|
||||
template_ = yield cg.templatable(
|
||||
template_ = await cg.templatable(
|
||||
config[CONF_TARGET_TEMPERATURE_LOW], args, float
|
||||
)
|
||||
cg.add(var.set_target_temperature_low(template_))
|
||||
if CONF_TARGET_TEMPERATURE_HIGH in config:
|
||||
template_ = yield cg.templatable(
|
||||
template_ = await cg.templatable(
|
||||
config[CONF_TARGET_TEMPERATURE_HIGH], args, float
|
||||
)
|
||||
cg.add(var.set_target_temperature_high(template_))
|
||||
if CONF_AWAY in config:
|
||||
template_ = yield cg.templatable(config[CONF_AWAY], args, bool)
|
||||
template_ = await cg.templatable(config[CONF_AWAY], args, bool)
|
||||
cg.add(var.set_away(template_))
|
||||
if CONF_FAN_MODE in config:
|
||||
template_ = yield cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode)
|
||||
template_ = await cg.templatable(config[CONF_FAN_MODE], args, ClimateFanMode)
|
||||
cg.add(var.set_fan_mode(template_))
|
||||
if CONF_CUSTOM_FAN_MODE in config:
|
||||
template_ = await cg.templatable(config[CONF_CUSTOM_FAN_MODE], args, str)
|
||||
cg.add(var.set_custom_fan_mode(template_))
|
||||
if CONF_PRESET in config:
|
||||
template_ = await cg.templatable(config[CONF_PRESET], args, ClimatePreset)
|
||||
cg.add(var.set_preset(template_))
|
||||
if CONF_CUSTOM_PRESET in config:
|
||||
template_ = await cg.templatable(config[CONF_CUSTOM_PRESET], args, str)
|
||||
cg.add(var.set_custom_preset(template_))
|
||||
if CONF_SWING_MODE in config:
|
||||
template_ = yield cg.templatable(
|
||||
template_ = await cg.templatable(
|
||||
config[CONF_SWING_MODE], args, ClimateSwingMode
|
||||
)
|
||||
cg.add(var.set_swing_mode(template_))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
@coroutine_with_priority(100.0)
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
cg.add_define("USE_CLIMATE")
|
||||
cg.add_global(climate_ns.using)
|
||||
|
||||
@@ -16,6 +16,9 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
|
||||
TEMPLATABLE_VALUE(float, target_temperature_high)
|
||||
TEMPLATABLE_VALUE(bool, away)
|
||||
TEMPLATABLE_VALUE(ClimateFanMode, fan_mode)
|
||||
TEMPLATABLE_VALUE(std::string, custom_fan_mode)
|
||||
TEMPLATABLE_VALUE(ClimatePreset, preset)
|
||||
TEMPLATABLE_VALUE(std::string, custom_preset)
|
||||
TEMPLATABLE_VALUE(ClimateSwingMode, swing_mode)
|
||||
|
||||
void play(Ts... x) override {
|
||||
@@ -24,8 +27,13 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
|
||||
call.set_target_temperature(this->target_temperature_.optional_value(x...));
|
||||
call.set_target_temperature_low(this->target_temperature_low_.optional_value(x...));
|
||||
call.set_target_temperature_high(this->target_temperature_high_.optional_value(x...));
|
||||
call.set_away(this->away_.optional_value(x...));
|
||||
if (away_.has_value()) {
|
||||
call.set_preset(away_.value(x...) ? CLIMATE_PRESET_AWAY : CLIMATE_PRESET_HOME);
|
||||
}
|
||||
call.set_fan_mode(this->fan_mode_.optional_value(x...));
|
||||
call.set_fan_mode(this->custom_fan_mode_.optional_value(x...));
|
||||
call.set_preset(this->preset_.optional_value(x...));
|
||||
call.set_preset(this->custom_preset_.optional_value(x...));
|
||||
call.set_swing_mode(this->swing_mode_.optional_value(x...));
|
||||
call.perform();
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#include "climate.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace climate {
|
||||
|
||||
static const char *TAG = "climate";
|
||||
static const char *const TAG = "climate";
|
||||
|
||||
void ClimateCall::perform() {
|
||||
ESP_LOGD(TAG, "'%s' - Setting", this->parent_->get_name().c_str());
|
||||
@@ -13,10 +12,24 @@ void ClimateCall::perform() {
|
||||
const char *mode_s = climate_mode_to_string(*this->mode_);
|
||||
ESP_LOGD(TAG, " Mode: %s", mode_s);
|
||||
}
|
||||
if (this->custom_fan_mode_.has_value()) {
|
||||
this->fan_mode_.reset();
|
||||
ESP_LOGD(TAG, " Custom Fan: %s", this->custom_fan_mode_.value().c_str());
|
||||
}
|
||||
if (this->fan_mode_.has_value()) {
|
||||
this->custom_fan_mode_.reset();
|
||||
const char *fan_mode_s = climate_fan_mode_to_string(*this->fan_mode_);
|
||||
ESP_LOGD(TAG, " Fan: %s", fan_mode_s);
|
||||
}
|
||||
if (this->custom_preset_.has_value()) {
|
||||
this->preset_.reset();
|
||||
ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset_.value().c_str());
|
||||
}
|
||||
if (this->preset_.has_value()) {
|
||||
this->custom_preset_.reset();
|
||||
const char *preset_s = climate_preset_to_string(*this->preset_);
|
||||
ESP_LOGD(TAG, " Preset: %s", preset_s);
|
||||
}
|
||||
if (this->swing_mode_.has_value()) {
|
||||
const char *swing_mode_s = climate_swing_mode_to_string(*this->swing_mode_);
|
||||
ESP_LOGD(TAG, " Swing: %s", swing_mode_s);
|
||||
@@ -30,9 +43,6 @@ void ClimateCall::perform() {
|
||||
if (this->target_temperature_high_.has_value()) {
|
||||
ESP_LOGD(TAG, " Target Temperature High: %.2f", *this->target_temperature_high_);
|
||||
}
|
||||
if (this->away_.has_value()) {
|
||||
ESP_LOGD(TAG, " Away Mode: %s", ONOFF(*this->away_));
|
||||
}
|
||||
this->parent_->control(*this);
|
||||
}
|
||||
void ClimateCall::validate_() {
|
||||
@@ -44,13 +54,32 @@ void ClimateCall::validate_() {
|
||||
this->mode_.reset();
|
||||
}
|
||||
}
|
||||
if (this->fan_mode_.has_value()) {
|
||||
if (this->custom_fan_mode_.has_value()) {
|
||||
auto custom_fan_mode = *this->custom_fan_mode_;
|
||||
if (!traits.supports_custom_fan_mode(custom_fan_mode)) {
|
||||
ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", custom_fan_mode.c_str());
|
||||
this->custom_fan_mode_.reset();
|
||||
}
|
||||
} else if (this->fan_mode_.has_value()) {
|
||||
auto fan_mode = *this->fan_mode_;
|
||||
if (!traits.supports_fan_mode(fan_mode)) {
|
||||
ESP_LOGW(TAG, " Fan Mode %s is not supported by this device!", climate_fan_mode_to_string(fan_mode));
|
||||
this->fan_mode_.reset();
|
||||
}
|
||||
}
|
||||
if (this->custom_preset_.has_value()) {
|
||||
auto custom_preset = *this->custom_preset_;
|
||||
if (!traits.supports_custom_preset(custom_preset)) {
|
||||
ESP_LOGW(TAG, " Preset %s is not supported by this device!", custom_preset.c_str());
|
||||
this->custom_preset_.reset();
|
||||
}
|
||||
} else if (this->preset_.has_value()) {
|
||||
auto preset = *this->preset_;
|
||||
if (!traits.supports_preset(preset)) {
|
||||
ESP_LOGW(TAG, " Preset %s is not supported by this device!", climate_preset_to_string(preset));
|
||||
this->preset_.reset();
|
||||
}
|
||||
}
|
||||
if (this->swing_mode_.has_value()) {
|
||||
auto swing_mode = *this->swing_mode_;
|
||||
if (!traits.supports_swing_mode(swing_mode)) {
|
||||
@@ -93,12 +122,6 @@ void ClimateCall::validate_() {
|
||||
this->target_temperature_high_.reset();
|
||||
}
|
||||
}
|
||||
if (this->away_.has_value()) {
|
||||
if (!traits.get_supports_away()) {
|
||||
ESP_LOGW(TAG, " Cannot set away mode for this device!");
|
||||
this->away_.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
ClimateCall &ClimateCall::set_mode(ClimateMode mode) {
|
||||
this->mode_ = mode;
|
||||
@@ -117,6 +140,8 @@ ClimateCall &ClimateCall::set_mode(const std::string &mode) {
|
||||
this->set_mode(CLIMATE_MODE_FAN_ONLY);
|
||||
} else if (str_equals_case_insensitive(mode, "DRY")) {
|
||||
this->set_mode(CLIMATE_MODE_DRY);
|
||||
} else if (str_equals_case_insensitive(mode, "HEAT_COOL")) {
|
||||
this->set_mode(CLIMATE_MODE_HEAT_COOL);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - Unrecognized mode %s", this->parent_->get_name().c_str(), mode.c_str());
|
||||
}
|
||||
@@ -124,6 +149,7 @@ ClimateCall &ClimateCall::set_mode(const std::string &mode) {
|
||||
}
|
||||
ClimateCall &ClimateCall::set_fan_mode(ClimateFanMode fan_mode) {
|
||||
this->fan_mode_ = fan_mode;
|
||||
this->custom_fan_mode_.reset();
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
|
||||
@@ -146,11 +172,57 @@ ClimateCall &ClimateCall::set_fan_mode(const std::string &fan_mode) {
|
||||
} else if (str_equals_case_insensitive(fan_mode, "DIFFUSE")) {
|
||||
this->set_fan_mode(CLIMATE_FAN_DIFFUSE);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str());
|
||||
if (this->parent_->get_traits().supports_custom_fan_mode(fan_mode)) {
|
||||
this->custom_fan_mode_ = fan_mode;
|
||||
this->fan_mode_.reset();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - Unrecognized fan mode %s", this->parent_->get_name().c_str(), fan_mode.c_str());
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_fan_mode(optional<std::string> fan_mode) {
|
||||
if (fan_mode.has_value()) {
|
||||
this->set_fan_mode(fan_mode.value());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_preset(ClimatePreset preset) {
|
||||
this->preset_ = preset;
|
||||
this->custom_preset_.reset();
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_preset(const std::string &preset) {
|
||||
if (str_equals_case_insensitive(preset, "ECO")) {
|
||||
this->set_preset(CLIMATE_PRESET_ECO);
|
||||
} else if (str_equals_case_insensitive(preset, "AWAY")) {
|
||||
this->set_preset(CLIMATE_PRESET_AWAY);
|
||||
} else if (str_equals_case_insensitive(preset, "BOOST")) {
|
||||
this->set_preset(CLIMATE_PRESET_BOOST);
|
||||
} else if (str_equals_case_insensitive(preset, "COMFORT")) {
|
||||
this->set_preset(CLIMATE_PRESET_COMFORT);
|
||||
} else if (str_equals_case_insensitive(preset, "HOME")) {
|
||||
this->set_preset(CLIMATE_PRESET_HOME);
|
||||
} else if (str_equals_case_insensitive(preset, "SLEEP")) {
|
||||
this->set_preset(CLIMATE_PRESET_SLEEP);
|
||||
} else if (str_equals_case_insensitive(preset, "ACTIVITY")) {
|
||||
this->set_preset(CLIMATE_PRESET_ACTIVITY);
|
||||
} else {
|
||||
if (this->parent_->get_traits().supports_custom_preset(preset)) {
|
||||
this->custom_preset_ = preset;
|
||||
this->preset_.reset();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - Unrecognized preset %s", this->parent_->get_name().c_str(), preset.c_str());
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_preset(optional<std::string> preset) {
|
||||
if (preset.has_value()) {
|
||||
this->set_preset(preset.value());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ClimateCall &ClimateCall::set_swing_mode(ClimateSwingMode swing_mode) {
|
||||
this->swing_mode_ = swing_mode;
|
||||
return *this;
|
||||
@@ -186,15 +258,23 @@ const optional<ClimateMode> &ClimateCall::get_mode() const { return this->mode_;
|
||||
const optional<float> &ClimateCall::get_target_temperature() const { return this->target_temperature_; }
|
||||
const optional<float> &ClimateCall::get_target_temperature_low() const { return this->target_temperature_low_; }
|
||||
const optional<float> &ClimateCall::get_target_temperature_high() const { return this->target_temperature_high_; }
|
||||
const optional<bool> &ClimateCall::get_away() const { return this->away_; }
|
||||
optional<bool> ClimateCall::get_away() const {
|
||||
if (!this->preset_.has_value())
|
||||
return {};
|
||||
return *this->preset_ == ClimatePreset::CLIMATE_PRESET_AWAY;
|
||||
}
|
||||
const optional<ClimateFanMode> &ClimateCall::get_fan_mode() const { return this->fan_mode_; }
|
||||
const optional<std::string> &ClimateCall::get_custom_fan_mode() const { return this->custom_fan_mode_; }
|
||||
const optional<ClimatePreset> &ClimateCall::get_preset() const { return this->preset_; }
|
||||
const optional<std::string> &ClimateCall::get_custom_preset() const { return this->custom_preset_; }
|
||||
const optional<ClimateSwingMode> &ClimateCall::get_swing_mode() const { return this->swing_mode_; }
|
||||
ClimateCall &ClimateCall::set_away(bool away) {
|
||||
this->away_ = away;
|
||||
this->preset_ = away ? CLIMATE_PRESET_AWAY : CLIMATE_PRESET_HOME;
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_away(optional<bool> away) {
|
||||
this->away_ = away;
|
||||
if (away.has_value())
|
||||
this->preset_ = *away ? CLIMATE_PRESET_AWAY : CLIMATE_PRESET_HOME;
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_target_temperature_high(optional<float> target_temperature_high) {
|
||||
@@ -215,6 +295,12 @@ ClimateCall &ClimateCall::set_mode(optional<ClimateMode> mode) {
|
||||
}
|
||||
ClimateCall &ClimateCall::set_fan_mode(optional<ClimateFanMode> fan_mode) {
|
||||
this->fan_mode_ = fan_mode;
|
||||
this->custom_fan_mode_.reset();
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_preset(optional<ClimatePreset> preset) {
|
||||
this->preset_ = preset;
|
||||
this->custom_preset_.reset();
|
||||
return *this;
|
||||
}
|
||||
ClimateCall &ClimateCall::set_swing_mode(optional<ClimateSwingMode> swing_mode) {
|
||||
@@ -226,8 +312,12 @@ void Climate::add_on_state_callback(std::function<void()> &&callback) {
|
||||
this->state_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
// Random 32bit value; If this changes existing restore preferences are invalidated
|
||||
static const uint32_t RESTORE_STATE_VERSION = 0x848EA6ADUL;
|
||||
|
||||
optional<ClimateDeviceRestoreState> Climate::restore_state_() {
|
||||
this->rtc_ = global_preferences.make_preference<ClimateDeviceRestoreState>(this->get_object_id_hash());
|
||||
this->rtc_ =
|
||||
global_preferences.make_preference<ClimateDeviceRestoreState>(this->get_object_id_hash() ^ RESTORE_STATE_VERSION);
|
||||
ClimateDeviceRestoreState recovered{};
|
||||
if (!this->rtc_.load(&recovered))
|
||||
return {};
|
||||
@@ -246,11 +336,32 @@ void Climate::save_state_() {
|
||||
} else {
|
||||
state.target_temperature = this->target_temperature;
|
||||
}
|
||||
if (traits.get_supports_away()) {
|
||||
state.away = this->away;
|
||||
if (traits.get_supports_fan_modes() && fan_mode.has_value()) {
|
||||
state.uses_custom_fan_mode = false;
|
||||
state.fan_mode = this->fan_mode.value();
|
||||
}
|
||||
if (traits.get_supports_fan_modes()) {
|
||||
state.fan_mode = this->fan_mode;
|
||||
if (!traits.get_supported_custom_fan_modes().empty() && custom_fan_mode.has_value()) {
|
||||
state.uses_custom_fan_mode = true;
|
||||
const auto &supported = traits.get_supported_custom_fan_modes();
|
||||
std::vector<std::string> vec{supported.begin(), supported.end()};
|
||||
auto it = std::find(vec.begin(), vec.end(), custom_fan_mode);
|
||||
if (it != vec.end()) {
|
||||
state.custom_fan_mode = std::distance(vec.begin(), it);
|
||||
}
|
||||
}
|
||||
if (traits.get_supports_presets() && preset.has_value()) {
|
||||
state.uses_custom_preset = false;
|
||||
state.preset = this->preset.value();
|
||||
}
|
||||
if (!traits.get_supported_custom_presets().empty() && custom_preset.has_value()) {
|
||||
state.uses_custom_preset = true;
|
||||
const auto &supported = traits.get_supported_custom_presets();
|
||||
std::vector<std::string> vec{supported.begin(), supported.end()};
|
||||
auto it = std::find(vec.begin(), vec.end(), custom_preset);
|
||||
// only set custom preset if value exists, otherwise leave it as is
|
||||
if (it != vec.cend()) {
|
||||
state.custom_preset = std::distance(vec.begin(), it);
|
||||
}
|
||||
}
|
||||
if (traits.get_supports_swing_modes()) {
|
||||
state.swing_mode = this->swing_mode;
|
||||
@@ -266,8 +377,17 @@ void Climate::publish_state() {
|
||||
if (traits.get_supports_action()) {
|
||||
ESP_LOGD(TAG, " Action: %s", climate_action_to_string(this->action));
|
||||
}
|
||||
if (traits.get_supports_fan_modes()) {
|
||||
ESP_LOGD(TAG, " Fan Mode: %s", climate_fan_mode_to_string(this->fan_mode));
|
||||
if (traits.get_supports_fan_modes() && this->fan_mode.has_value()) {
|
||||
ESP_LOGD(TAG, " Fan Mode: %s", climate_fan_mode_to_string(this->fan_mode.value()));
|
||||
}
|
||||
if (!traits.get_supported_custom_fan_modes().empty() && this->custom_fan_mode.has_value()) {
|
||||
ESP_LOGD(TAG, " Custom Fan Mode: %s", this->custom_fan_mode.value().c_str());
|
||||
}
|
||||
if (traits.get_supports_presets() && this->preset.has_value()) {
|
||||
ESP_LOGD(TAG, " Preset: %s", climate_preset_to_string(this->preset.value()));
|
||||
}
|
||||
if (!traits.get_supported_custom_presets().empty() && this->custom_preset.has_value()) {
|
||||
ESP_LOGD(TAG, " Custom Preset: %s", this->custom_preset.value().c_str());
|
||||
}
|
||||
if (traits.get_supports_swing_modes()) {
|
||||
ESP_LOGD(TAG, " Swing Mode: %s", climate_swing_mode_to_string(this->swing_mode));
|
||||
@@ -281,9 +401,6 @@ void Climate::publish_state() {
|
||||
} else {
|
||||
ESP_LOGD(TAG, " Target Temperature: %.2f°C", this->target_temperature);
|
||||
}
|
||||
if (traits.get_supports_away()) {
|
||||
ESP_LOGD(TAG, " Away: %s", ONOFF(this->away));
|
||||
}
|
||||
|
||||
// Send state to frontend
|
||||
this->state_callback_.call();
|
||||
@@ -329,12 +446,12 @@ ClimateCall ClimateDeviceRestoreState::to_call(Climate *climate) {
|
||||
} else {
|
||||
call.set_target_temperature(this->target_temperature);
|
||||
}
|
||||
if (traits.get_supports_away()) {
|
||||
call.set_away(this->away);
|
||||
}
|
||||
if (traits.get_supports_fan_modes()) {
|
||||
if (traits.get_supports_fan_modes() || !traits.get_supported_custom_fan_modes().empty()) {
|
||||
call.set_fan_mode(this->fan_mode);
|
||||
}
|
||||
if (traits.get_supports_presets() || !traits.get_supported_custom_presets().empty()) {
|
||||
call.set_preset(this->preset);
|
||||
}
|
||||
if (traits.get_supports_swing_modes()) {
|
||||
call.set_swing_mode(this->swing_mode);
|
||||
}
|
||||
@@ -349,12 +466,28 @@ void ClimateDeviceRestoreState::apply(Climate *climate) {
|
||||
} else {
|
||||
climate->target_temperature = this->target_temperature;
|
||||
}
|
||||
if (traits.get_supports_away()) {
|
||||
climate->away = this->away;
|
||||
}
|
||||
if (traits.get_supports_fan_modes()) {
|
||||
if (traits.get_supports_fan_modes() && !this->uses_custom_fan_mode) {
|
||||
climate->fan_mode = this->fan_mode;
|
||||
}
|
||||
if (!traits.get_supported_custom_fan_modes().empty() && this->uses_custom_fan_mode) {
|
||||
// std::set has consistent order (lexicographic for strings), so this is ok
|
||||
const auto &modes = traits.get_supported_custom_fan_modes();
|
||||
std::vector<std::string> modes_vec{modes.begin(), modes.end()};
|
||||
if (custom_fan_mode < modes_vec.size()) {
|
||||
climate->custom_fan_mode = modes_vec[this->custom_fan_mode];
|
||||
}
|
||||
}
|
||||
if (traits.get_supports_presets() && !this->uses_custom_preset) {
|
||||
climate->preset = this->preset;
|
||||
}
|
||||
if (!traits.get_supported_custom_presets().empty() && uses_custom_preset) {
|
||||
// std::set has consistent order (lexicographic for strings), so this is ok
|
||||
const auto &presets = traits.get_supported_custom_presets();
|
||||
std::vector<std::string> presets_vec{presets.begin(), presets.end()};
|
||||
if (custom_preset < presets_vec.size()) {
|
||||
climate->custom_preset = presets_vec[this->custom_preset];
|
||||
}
|
||||
}
|
||||
if (traits.get_supports_swing_modes()) {
|
||||
climate->swing_mode = this->swing_mode;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/preferences.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "climate_mode.h"
|
||||
#include "climate_traits.h"
|
||||
|
||||
@@ -10,8 +11,8 @@ namespace esphome {
|
||||
namespace climate {
|
||||
|
||||
#define LOG_CLIMATE(prefix, type, obj) \
|
||||
if (obj != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
|
||||
}
|
||||
|
||||
class Climate;
|
||||
@@ -62,7 +63,9 @@ class ClimateCall {
|
||||
* For climate devices with two point target temperature control
|
||||
*/
|
||||
ClimateCall &set_target_temperature_high(optional<float> target_temperature_high);
|
||||
ESPDEPRECATED("set_away() is deprecated, please use .set_preset(CLIMATE_PRESET_AWAY) instead", "v1.20")
|
||||
ClimateCall &set_away(bool away);
|
||||
ESPDEPRECATED("set_away() is deprecated, please use .set_preset(CLIMATE_PRESET_AWAY) instead", "v1.20")
|
||||
ClimateCall &set_away(optional<bool> away);
|
||||
/// Set the fan mode of the climate device.
|
||||
ClimateCall &set_fan_mode(ClimateFanMode fan_mode);
|
||||
@@ -70,12 +73,22 @@ class ClimateCall {
|
||||
ClimateCall &set_fan_mode(optional<ClimateFanMode> fan_mode);
|
||||
/// Set the fan mode of the climate device based on a string.
|
||||
ClimateCall &set_fan_mode(const std::string &fan_mode);
|
||||
/// Set the fan mode of the climate device based on a string.
|
||||
ClimateCall &set_fan_mode(optional<std::string> fan_mode);
|
||||
/// Set the swing mode of the climate device.
|
||||
ClimateCall &set_swing_mode(ClimateSwingMode swing_mode);
|
||||
/// Set the swing mode of the climate device.
|
||||
ClimateCall &set_swing_mode(optional<ClimateSwingMode> swing_mode);
|
||||
/// Set the swing mode of the climate device based on a string.
|
||||
ClimateCall &set_swing_mode(const std::string &swing_mode);
|
||||
/// Set the preset of the climate device.
|
||||
ClimateCall &set_preset(ClimatePreset preset);
|
||||
/// Set the preset of the climate device.
|
||||
ClimateCall &set_preset(optional<ClimatePreset> preset);
|
||||
/// Set the preset of the climate device based on a string.
|
||||
ClimateCall &set_preset(const std::string &preset);
|
||||
/// Set the preset of the climate device based on a string.
|
||||
ClimateCall &set_preset(optional<std::string> preset);
|
||||
|
||||
void perform();
|
||||
|
||||
@@ -83,9 +96,13 @@ class ClimateCall {
|
||||
const optional<float> &get_target_temperature() const;
|
||||
const optional<float> &get_target_temperature_low() const;
|
||||
const optional<float> &get_target_temperature_high() const;
|
||||
const optional<bool> &get_away() const;
|
||||
ESPDEPRECATED("get_away() is deprecated, please use .get_preset() instead", "v1.20")
|
||||
optional<bool> get_away() const;
|
||||
const optional<ClimateFanMode> &get_fan_mode() const;
|
||||
const optional<ClimateSwingMode> &get_swing_mode() const;
|
||||
const optional<std::string> &get_custom_fan_mode() const;
|
||||
const optional<ClimatePreset> &get_preset() const;
|
||||
const optional<std::string> &get_custom_preset() const;
|
||||
|
||||
protected:
|
||||
void validate_();
|
||||
@@ -95,16 +112,27 @@ class ClimateCall {
|
||||
optional<float> target_temperature_;
|
||||
optional<float> target_temperature_low_;
|
||||
optional<float> target_temperature_high_;
|
||||
optional<bool> away_;
|
||||
optional<ClimateFanMode> fan_mode_;
|
||||
optional<ClimateSwingMode> swing_mode_;
|
||||
optional<std::string> custom_fan_mode_;
|
||||
optional<ClimatePreset> preset_;
|
||||
optional<std::string> custom_preset_;
|
||||
};
|
||||
|
||||
/// Struct used to save the state of the climate device in restore memory.
|
||||
/// Make sure to update RESTORE_STATE_VERSION when changing the struct entries.
|
||||
struct ClimateDeviceRestoreState {
|
||||
ClimateMode mode;
|
||||
bool away;
|
||||
ClimateFanMode fan_mode;
|
||||
bool uses_custom_fan_mode{false};
|
||||
union {
|
||||
ClimateFanMode fan_mode;
|
||||
uint8_t custom_fan_mode;
|
||||
};
|
||||
bool uses_custom_preset{false};
|
||||
union {
|
||||
ClimatePreset preset;
|
||||
uint8_t custom_preset;
|
||||
};
|
||||
ClimateSwingMode swing_mode;
|
||||
union {
|
||||
float target_temperature;
|
||||
@@ -133,7 +161,7 @@ struct ClimateDeviceRestoreState {
|
||||
*
|
||||
* The entire state of the climate device is encoded in public properties of the base class (current_temperature,
|
||||
* mode etc). These are read-only for the user and rw for integrations. The reason these are public
|
||||
* is for simple access to them from lambdas `if (id(my_climate).mode == climate::CLIMATE_MODE_AUTO) ...`
|
||||
* is for simple access to them from lambdas `if (id(my_climate).mode == climate::CLIMATE_MODE_HEAT_COOL) ...`
|
||||
*/
|
||||
class Climate : public Nameable {
|
||||
public:
|
||||
@@ -165,14 +193,24 @@ class Climate : public Nameable {
|
||||
* Away allows climate devices to have two different target temperature configs:
|
||||
* one for normal mode and one for away mode.
|
||||
*/
|
||||
ESPDEPRECATED("away is deprecated, use preset instead", "v1.20")
|
||||
bool away{false};
|
||||
|
||||
/// The active fan mode of the climate device.
|
||||
ClimateFanMode fan_mode;
|
||||
optional<ClimateFanMode> fan_mode;
|
||||
|
||||
/// The active swing mode of the climate device.
|
||||
ClimateSwingMode swing_mode;
|
||||
|
||||
/// The active custom fan mode of the climate device.
|
||||
optional<std::string> custom_fan_mode;
|
||||
|
||||
/// The active preset of the climate device.
|
||||
optional<ClimatePreset> preset;
|
||||
|
||||
/// The active custom preset mode of the climate device.
|
||||
optional<std::string> custom_preset;
|
||||
|
||||
/** Add a callback for the climate device state, each time the state of the climate device is updated
|
||||
* (using publish_state), this callback will be called.
|
||||
*
|
||||
|
||||
@@ -17,6 +17,8 @@ const char *climate_mode_to_string(ClimateMode mode) {
|
||||
return "FAN_ONLY";
|
||||
case CLIMATE_MODE_DRY:
|
||||
return "DRY";
|
||||
case CLIMATE_MODE_HEAT_COOL:
|
||||
return "HEAT_COOL";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@@ -80,5 +82,28 @@ const char *climate_swing_mode_to_string(ClimateSwingMode swing_mode) {
|
||||
}
|
||||
}
|
||||
|
||||
const char *climate_preset_to_string(ClimatePreset preset) {
|
||||
switch (preset) {
|
||||
case climate::CLIMATE_PRESET_NONE:
|
||||
return "NONE";
|
||||
case climate::CLIMATE_PRESET_HOME:
|
||||
return "HOME";
|
||||
case climate::CLIMATE_PRESET_ECO:
|
||||
return "ECO";
|
||||
case climate::CLIMATE_PRESET_AWAY:
|
||||
return "AWAY";
|
||||
case climate::CLIMATE_PRESET_BOOST:
|
||||
return "BOOST";
|
||||
case climate::CLIMATE_PRESET_COMFORT:
|
||||
return "COMFORT";
|
||||
case climate::CLIMATE_PRESET_SLEEP:
|
||||
return "SLEEP";
|
||||
case climate::CLIMATE_PRESET_ACTIVITY:
|
||||
return "ACTIVITY";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace climate
|
||||
} // namespace esphome
|
||||
|
||||
@@ -7,37 +7,41 @@ namespace climate {
|
||||
|
||||
/// Enum for all modes a climate device can be in.
|
||||
enum ClimateMode : uint8_t {
|
||||
/// The climate device is off (not in auto, heat or cool mode)
|
||||
/// The climate device is off
|
||||
CLIMATE_MODE_OFF = 0,
|
||||
/// The climate device is set to automatically change the heating/cooling cycle
|
||||
CLIMATE_MODE_AUTO = 1,
|
||||
/// The climate device is manually set to cool mode (not in auto mode!)
|
||||
/// The climate device is set to heat/cool to reach the target temperature.
|
||||
CLIMATE_MODE_HEAT_COOL = 1,
|
||||
/// The climate device is set to cool to reach the target temperature
|
||||
CLIMATE_MODE_COOL = 2,
|
||||
/// The climate device is manually set to heat mode (not in auto mode!)
|
||||
/// The climate device is set to heat to reach the target temperature
|
||||
CLIMATE_MODE_HEAT = 3,
|
||||
/// The climate device is manually set to fan only mode
|
||||
/// The climate device only has the fan enabled, no heating or cooling is taking place
|
||||
CLIMATE_MODE_FAN_ONLY = 4,
|
||||
/// The climate device is manually set to dry mode
|
||||
/// The climate device is set to dry/humidity mode
|
||||
CLIMATE_MODE_DRY = 5,
|
||||
/** The climate device is adjusting the temperatre dynamically.
|
||||
* For example, the target temperature can be adjusted based on a schedule, or learned behavior.
|
||||
* The target temperature can't be adjusted when in this mode.
|
||||
*/
|
||||
CLIMATE_MODE_AUTO = 6
|
||||
};
|
||||
|
||||
/// Enum for the current action of the climate device. Values match those of ClimateMode.
|
||||
enum ClimateAction : uint8_t {
|
||||
/// The climate device is off (inactive or no power)
|
||||
CLIMATE_ACTION_OFF = 0,
|
||||
/// The climate device is actively cooling (usually in cool or auto mode)
|
||||
/// The climate device is actively cooling
|
||||
CLIMATE_ACTION_COOLING = 2,
|
||||
/// The climate device is actively heating (usually in heat or auto mode)
|
||||
/// The climate device is actively heating
|
||||
CLIMATE_ACTION_HEATING = 3,
|
||||
/// The climate device is idle (monitoring climate but no action needed)
|
||||
CLIMATE_ACTION_IDLE = 4,
|
||||
/// The climate device is drying (either mode DRY or AUTO)
|
||||
/// The climate device is drying
|
||||
CLIMATE_ACTION_DRYING = 5,
|
||||
/// The climate device is in fan only mode (either mode FAN_ONLY or AUTO)
|
||||
/// The climate device is in fan only mode
|
||||
CLIMATE_ACTION_FAN = 6,
|
||||
};
|
||||
|
||||
/// Enum for all modes a climate fan can be in
|
||||
enum ClimateFanMode : uint8_t {
|
||||
/// The fan mode is set to On
|
||||
CLIMATE_FAN_ON = 0,
|
||||
@@ -61,7 +65,7 @@ enum ClimateFanMode : uint8_t {
|
||||
|
||||
/// Enum for all modes a climate swing can be in
|
||||
enum ClimateSwingMode : uint8_t {
|
||||
/// The sing mode is set to Off
|
||||
/// The swing mode is set to Off
|
||||
CLIMATE_SWING_OFF = 0,
|
||||
/// The fan mode is set to Both
|
||||
CLIMATE_SWING_BOTH = 1,
|
||||
@@ -71,6 +75,26 @@ enum ClimateSwingMode : uint8_t {
|
||||
CLIMATE_SWING_HORIZONTAL = 3,
|
||||
};
|
||||
|
||||
/// Enum for all modes a climate swing can be in
|
||||
enum ClimatePreset : uint8_t {
|
||||
/// No preset is active
|
||||
CLIMATE_PRESET_NONE = 0,
|
||||
/// Device is in home preset
|
||||
CLIMATE_PRESET_HOME = 1,
|
||||
/// Device is in away preset
|
||||
CLIMATE_PRESET_AWAY = 2,
|
||||
/// Device is in boost preset
|
||||
CLIMATE_PRESET_BOOST = 3,
|
||||
/// Device is in comfort preset
|
||||
CLIMATE_PRESET_COMFORT = 4,
|
||||
/// Device is running an energy-saving preset
|
||||
CLIMATE_PRESET_ECO = 5,
|
||||
/// Device is prepared for sleep
|
||||
CLIMATE_PRESET_SLEEP = 6,
|
||||
/// Device is reacting to activity (e.g., movement sensors)
|
||||
CLIMATE_PRESET_ACTIVITY = 7,
|
||||
};
|
||||
|
||||
/// Convert the given ClimateMode to a human-readable string.
|
||||
const char *climate_mode_to_string(ClimateMode mode);
|
||||
|
||||
@@ -83,5 +107,8 @@ const char *climate_fan_mode_to_string(ClimateFanMode mode);
|
||||
/// Convert the given ClimateSwingMode to a human-readable string.
|
||||
const char *climate_swing_mode_to_string(ClimateSwingMode mode);
|
||||
|
||||
/// Convert the given ClimateSwingMode to a human-readable string.
|
||||
const char *climate_preset_to_string(ClimatePreset preset);
|
||||
|
||||
} // namespace climate
|
||||
} // namespace esphome
|
||||
|
||||
@@ -4,50 +4,6 @@
|
||||
namespace esphome {
|
||||
namespace climate {
|
||||
|
||||
bool ClimateTraits::supports_mode(ClimateMode mode) const {
|
||||
switch (mode) {
|
||||
case CLIMATE_MODE_OFF:
|
||||
return true;
|
||||
case CLIMATE_MODE_AUTO:
|
||||
return this->supports_auto_mode_;
|
||||
case CLIMATE_MODE_COOL:
|
||||
return this->supports_cool_mode_;
|
||||
case CLIMATE_MODE_HEAT:
|
||||
return this->supports_heat_mode_;
|
||||
case CLIMATE_MODE_FAN_ONLY:
|
||||
return this->supports_fan_only_mode_;
|
||||
case CLIMATE_MODE_DRY:
|
||||
return this->supports_dry_mode_;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ClimateTraits::get_supports_current_temperature() const { return supports_current_temperature_; }
|
||||
void ClimateTraits::set_supports_current_temperature(bool supports_current_temperature) {
|
||||
supports_current_temperature_ = supports_current_temperature;
|
||||
}
|
||||
bool ClimateTraits::get_supports_two_point_target_temperature() const { return supports_two_point_target_temperature_; }
|
||||
void ClimateTraits::set_supports_two_point_target_temperature(bool supports_two_point_target_temperature) {
|
||||
supports_two_point_target_temperature_ = supports_two_point_target_temperature;
|
||||
}
|
||||
void ClimateTraits::set_supports_auto_mode(bool supports_auto_mode) { supports_auto_mode_ = supports_auto_mode; }
|
||||
void ClimateTraits::set_supports_cool_mode(bool supports_cool_mode) { supports_cool_mode_ = supports_cool_mode; }
|
||||
void ClimateTraits::set_supports_heat_mode(bool supports_heat_mode) { supports_heat_mode_ = supports_heat_mode; }
|
||||
void ClimateTraits::set_supports_fan_only_mode(bool supports_fan_only_mode) {
|
||||
supports_fan_only_mode_ = supports_fan_only_mode;
|
||||
}
|
||||
void ClimateTraits::set_supports_dry_mode(bool supports_dry_mode) { supports_dry_mode_ = supports_dry_mode; }
|
||||
void ClimateTraits::set_supports_away(bool supports_away) { supports_away_ = supports_away; }
|
||||
void ClimateTraits::set_supports_action(bool supports_action) { supports_action_ = supports_action; }
|
||||
float ClimateTraits::get_visual_min_temperature() const { return visual_min_temperature_; }
|
||||
void ClimateTraits::set_visual_min_temperature(float visual_min_temperature) {
|
||||
visual_min_temperature_ = visual_min_temperature;
|
||||
}
|
||||
float ClimateTraits::get_visual_max_temperature() const { return visual_max_temperature_; }
|
||||
void ClimateTraits::set_visual_max_temperature(float visual_max_temperature) {
|
||||
visual_max_temperature_ = visual_max_temperature;
|
||||
}
|
||||
float ClimateTraits::get_visual_temperature_step() const { return visual_temperature_step_; }
|
||||
int8_t ClimateTraits::get_temperature_accuracy_decimals() const {
|
||||
// use printf %g to find number of digits based on temperature step
|
||||
char buf[32];
|
||||
@@ -59,95 +15,6 @@ int8_t ClimateTraits::get_temperature_accuracy_decimals() const {
|
||||
|
||||
return str.length() - dot_pos - 1;
|
||||
}
|
||||
void ClimateTraits::set_visual_temperature_step(float temperature_step) { visual_temperature_step_ = temperature_step; }
|
||||
bool ClimateTraits::get_supports_away() const { return supports_away_; }
|
||||
bool ClimateTraits::get_supports_action() const { return supports_action_; }
|
||||
|
||||
void ClimateTraits::set_supports_fan_mode_on(bool supports_fan_mode_on) {
|
||||
this->supports_fan_mode_on_ = supports_fan_mode_on;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_off(bool supports_fan_mode_off) {
|
||||
this->supports_fan_mode_off_ = supports_fan_mode_off;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_auto(bool supports_fan_mode_auto) {
|
||||
this->supports_fan_mode_auto_ = supports_fan_mode_auto;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_low(bool supports_fan_mode_low) {
|
||||
this->supports_fan_mode_low_ = supports_fan_mode_low;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_medium(bool supports_fan_mode_medium) {
|
||||
this->supports_fan_mode_medium_ = supports_fan_mode_medium;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_high(bool supports_fan_mode_high) {
|
||||
this->supports_fan_mode_high_ = supports_fan_mode_high;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_middle(bool supports_fan_mode_middle) {
|
||||
this->supports_fan_mode_middle_ = supports_fan_mode_middle;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_focus(bool supports_fan_mode_focus) {
|
||||
this->supports_fan_mode_focus_ = supports_fan_mode_focus;
|
||||
}
|
||||
void ClimateTraits::set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse) {
|
||||
this->supports_fan_mode_diffuse_ = supports_fan_mode_diffuse;
|
||||
}
|
||||
bool ClimateTraits::supports_fan_mode(ClimateFanMode fan_mode) const {
|
||||
switch (fan_mode) {
|
||||
case climate::CLIMATE_FAN_ON:
|
||||
return this->supports_fan_mode_on_;
|
||||
case climate::CLIMATE_FAN_OFF:
|
||||
return this->supports_fan_mode_off_;
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
return this->supports_fan_mode_auto_;
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
return this->supports_fan_mode_low_;
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
return this->supports_fan_mode_medium_;
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
return this->supports_fan_mode_high_;
|
||||
case climate::CLIMATE_FAN_MIDDLE:
|
||||
return this->supports_fan_mode_middle_;
|
||||
case climate::CLIMATE_FAN_FOCUS:
|
||||
return this->supports_fan_mode_focus_;
|
||||
case climate::CLIMATE_FAN_DIFFUSE:
|
||||
return this->supports_fan_mode_diffuse_;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ClimateTraits::get_supports_fan_modes() const {
|
||||
return this->supports_fan_mode_on_ || this->supports_fan_mode_off_ || this->supports_fan_mode_auto_ ||
|
||||
this->supports_fan_mode_low_ || this->supports_fan_mode_medium_ || this->supports_fan_mode_high_ ||
|
||||
this->supports_fan_mode_middle_ || this->supports_fan_mode_focus_ || this->supports_fan_mode_diffuse_;
|
||||
}
|
||||
void ClimateTraits::set_supports_swing_mode_off(bool supports_swing_mode_off) {
|
||||
this->supports_swing_mode_off_ = supports_swing_mode_off;
|
||||
}
|
||||
void ClimateTraits::set_supports_swing_mode_both(bool supports_swing_mode_both) {
|
||||
this->supports_swing_mode_both_ = supports_swing_mode_both;
|
||||
}
|
||||
void ClimateTraits::set_supports_swing_mode_vertical(bool supports_swing_mode_vertical) {
|
||||
this->supports_swing_mode_vertical_ = supports_swing_mode_vertical;
|
||||
}
|
||||
void ClimateTraits::set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal) {
|
||||
this->supports_swing_mode_horizontal_ = supports_swing_mode_horizontal;
|
||||
}
|
||||
bool ClimateTraits::supports_swing_mode(ClimateSwingMode swing_mode) const {
|
||||
switch (swing_mode) {
|
||||
case climate::CLIMATE_SWING_OFF:
|
||||
return this->supports_swing_mode_off_;
|
||||
case climate::CLIMATE_SWING_BOTH:
|
||||
return this->supports_swing_mode_both_;
|
||||
case climate::CLIMATE_SWING_VERTICAL:
|
||||
return this->supports_swing_mode_vertical_;
|
||||
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||
return this->supports_swing_mode_horizontal_;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
bool ClimateTraits::get_supports_swing_modes() const {
|
||||
return this->supports_swing_mode_off_ || this->supports_swing_mode_both_ || supports_swing_mode_vertical_ ||
|
||||
supports_swing_mode_horizontal_;
|
||||
}
|
||||
} // namespace climate
|
||||
} // namespace esphome
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "climate_mode.h"
|
||||
#include <set>
|
||||
|
||||
namespace esphome {
|
||||
namespace climate {
|
||||
@@ -23,8 +25,6 @@ namespace climate {
|
||||
* - heat mode (increases current temperature)
|
||||
* - dry mode (removes humidity from air)
|
||||
* - fan mode (only turns on fan)
|
||||
* - supports away - away mode means that the climate device supports two different
|
||||
* target temperature settings: one target temp setting for "away" mode and one for non-away mode.
|
||||
* - supports action - if the climate device supports reporting the active
|
||||
* current action of the device with the action property.
|
||||
* - supports fan modes - optionally, if it has a fan which can be configured in different ways:
|
||||
@@ -40,69 +40,147 @@ namespace climate {
|
||||
*/
|
||||
class ClimateTraits {
|
||||
public:
|
||||
bool get_supports_current_temperature() const;
|
||||
void set_supports_current_temperature(bool supports_current_temperature);
|
||||
bool get_supports_two_point_target_temperature() const;
|
||||
void set_supports_two_point_target_temperature(bool supports_two_point_target_temperature);
|
||||
void set_supports_auto_mode(bool supports_auto_mode);
|
||||
void set_supports_cool_mode(bool supports_cool_mode);
|
||||
void set_supports_heat_mode(bool supports_heat_mode);
|
||||
void set_supports_fan_only_mode(bool supports_fan_only_mode);
|
||||
void set_supports_dry_mode(bool supports_dry_mode);
|
||||
void set_supports_away(bool supports_away);
|
||||
bool get_supports_away() const;
|
||||
void set_supports_action(bool supports_action);
|
||||
bool get_supports_action() const;
|
||||
bool supports_mode(ClimateMode mode) const;
|
||||
void set_supports_fan_mode_on(bool supports_fan_mode_on);
|
||||
void set_supports_fan_mode_off(bool supports_fan_mode_off);
|
||||
void set_supports_fan_mode_auto(bool supports_fan_mode_auto);
|
||||
void set_supports_fan_mode_low(bool supports_fan_mode_low);
|
||||
void set_supports_fan_mode_medium(bool supports_fan_mode_medium);
|
||||
void set_supports_fan_mode_high(bool supports_fan_mode_high);
|
||||
void set_supports_fan_mode_middle(bool supports_fan_mode_middle);
|
||||
void set_supports_fan_mode_focus(bool supports_fan_mode_focus);
|
||||
void set_supports_fan_mode_diffuse(bool supports_fan_mode_diffuse);
|
||||
bool supports_fan_mode(ClimateFanMode fan_mode) const;
|
||||
bool get_supports_fan_modes() const;
|
||||
void set_supports_swing_mode_off(bool supports_swing_mode_off);
|
||||
void set_supports_swing_mode_both(bool supports_swing_mode_both);
|
||||
void set_supports_swing_mode_vertical(bool supports_swing_mode_vertical);
|
||||
void set_supports_swing_mode_horizontal(bool supports_swing_mode_horizontal);
|
||||
bool supports_swing_mode(ClimateSwingMode swing_mode) const;
|
||||
bool get_supports_swing_modes() const;
|
||||
bool get_supports_current_temperature() const { return supports_current_temperature_; }
|
||||
void set_supports_current_temperature(bool supports_current_temperature) {
|
||||
supports_current_temperature_ = supports_current_temperature;
|
||||
}
|
||||
bool get_supports_two_point_target_temperature() const { return supports_two_point_target_temperature_; }
|
||||
void set_supports_two_point_target_temperature(bool supports_two_point_target_temperature) {
|
||||
supports_two_point_target_temperature_ = supports_two_point_target_temperature;
|
||||
}
|
||||
void set_supported_modes(std::set<ClimateMode> modes) { supported_modes_ = std::move(modes); }
|
||||
void add_supported_mode(ClimateMode mode) { supported_modes_.insert(mode); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
|
||||
void set_supports_auto_mode(bool supports_auto_mode) { set_mode_support_(CLIMATE_MODE_AUTO, supports_auto_mode); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
|
||||
void set_supports_cool_mode(bool supports_cool_mode) { set_mode_support_(CLIMATE_MODE_COOL, supports_cool_mode); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
|
||||
void set_supports_heat_mode(bool supports_heat_mode) { set_mode_support_(CLIMATE_MODE_HEAT, supports_heat_mode); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
|
||||
void set_supports_heat_cool_mode(bool supported) { set_mode_support_(CLIMATE_MODE_HEAT_COOL, supported); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
|
||||
void set_supports_fan_only_mode(bool supports_fan_only_mode) {
|
||||
set_mode_support_(CLIMATE_MODE_FAN_ONLY, supports_fan_only_mode);
|
||||
}
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_modes() instead", "v1.20")
|
||||
void set_supports_dry_mode(bool supports_dry_mode) { set_mode_support_(CLIMATE_MODE_DRY, supports_dry_mode); }
|
||||
bool supports_mode(ClimateMode mode) const { return supported_modes_.count(mode); }
|
||||
const std::set<ClimateMode> get_supported_modes() const { return supported_modes_; }
|
||||
|
||||
float get_visual_min_temperature() const;
|
||||
void set_visual_min_temperature(float visual_min_temperature);
|
||||
float get_visual_max_temperature() const;
|
||||
void set_visual_max_temperature(float visual_max_temperature);
|
||||
float get_visual_temperature_step() const;
|
||||
void set_supports_action(bool supports_action) { supports_action_ = supports_action; }
|
||||
bool get_supports_action() const { return supports_action_; }
|
||||
|
||||
void set_supported_fan_modes(std::set<ClimateFanMode> modes) { supported_fan_modes_ = std::move(modes); }
|
||||
void add_supported_fan_mode(ClimateFanMode mode) { supported_fan_modes_.insert(mode); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
|
||||
void set_supports_fan_mode_on(bool supported) { set_fan_mode_support_(CLIMATE_FAN_ON, supported); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
|
||||
void set_supports_fan_mode_off(bool supported) { set_fan_mode_support_(CLIMATE_FAN_OFF, supported); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
|
||||
void set_supports_fan_mode_auto(bool supported) { set_fan_mode_support_(CLIMATE_FAN_AUTO, supported); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
|
||||
void set_supports_fan_mode_low(bool supported) { set_fan_mode_support_(CLIMATE_FAN_LOW, supported); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
|
||||
void set_supports_fan_mode_medium(bool supported) { set_fan_mode_support_(CLIMATE_FAN_MEDIUM, supported); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
|
||||
void set_supports_fan_mode_high(bool supported) { set_fan_mode_support_(CLIMATE_FAN_HIGH, supported); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
|
||||
void set_supports_fan_mode_middle(bool supported) { set_fan_mode_support_(CLIMATE_FAN_MIDDLE, supported); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
|
||||
void set_supports_fan_mode_focus(bool supported) { set_fan_mode_support_(CLIMATE_FAN_FOCUS, supported); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_fan_modes() instead", "v1.20")
|
||||
void set_supports_fan_mode_diffuse(bool supported) { set_fan_mode_support_(CLIMATE_FAN_DIFFUSE, supported); }
|
||||
bool supports_fan_mode(ClimateFanMode fan_mode) const { return supported_fan_modes_.count(fan_mode); }
|
||||
bool get_supports_fan_modes() const { return !supported_fan_modes_.empty() || !supported_custom_fan_modes_.empty(); }
|
||||
const std::set<ClimateFanMode> get_supported_fan_modes() const { return supported_fan_modes_; }
|
||||
|
||||
void set_supported_custom_fan_modes(std::set<std::string> supported_custom_fan_modes) {
|
||||
supported_custom_fan_modes_ = std::move(supported_custom_fan_modes);
|
||||
}
|
||||
const std::set<std::string> &get_supported_custom_fan_modes() const { return supported_custom_fan_modes_; }
|
||||
bool supports_custom_fan_mode(const std::string &custom_fan_mode) const {
|
||||
return supported_custom_fan_modes_.count(custom_fan_mode);
|
||||
}
|
||||
|
||||
void set_supported_presets(std::set<ClimatePreset> presets) { supported_presets_ = std::move(presets); }
|
||||
void add_supported_preset(ClimatePreset preset) { supported_presets_.insert(preset); }
|
||||
bool supports_preset(ClimatePreset preset) const { return supported_presets_.count(preset); }
|
||||
bool get_supports_presets() const { return !supported_presets_.empty(); }
|
||||
const std::set<climate::ClimatePreset> &get_supported_presets() const { return supported_presets_; }
|
||||
|
||||
void set_supported_custom_presets(std::set<std::string> supported_custom_presets) {
|
||||
supported_custom_presets_ = std::move(supported_custom_presets);
|
||||
}
|
||||
const std::set<std::string> &get_supported_custom_presets() const { return supported_custom_presets_; }
|
||||
bool supports_custom_preset(const std::string &custom_preset) const {
|
||||
return supported_custom_presets_.count(custom_preset);
|
||||
}
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_presets() instead", "v1.20")
|
||||
void set_supports_away(bool supports) {
|
||||
if (supports) {
|
||||
supported_presets_.insert(CLIMATE_PRESET_AWAY);
|
||||
supported_presets_.insert(CLIMATE_PRESET_HOME);
|
||||
}
|
||||
}
|
||||
ESPDEPRECATED("This method is deprecated, use supports_preset() instead", "v1.20")
|
||||
bool get_supports_away() const { return supports_preset(CLIMATE_PRESET_AWAY); }
|
||||
|
||||
void set_supported_swing_modes(std::set<ClimateSwingMode> modes) { supported_swing_modes_ = std::move(modes); }
|
||||
void add_supported_swing_mode(ClimateSwingMode mode) { supported_swing_modes_.insert(mode); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead", "v1.20")
|
||||
void set_supports_swing_mode_off(bool supported) { set_swing_mode_support_(CLIMATE_SWING_OFF, supported); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead", "v1.20")
|
||||
void set_supports_swing_mode_both(bool supported) { set_swing_mode_support_(CLIMATE_SWING_BOTH, supported); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead", "v1.20")
|
||||
void set_supports_swing_mode_vertical(bool supported) { set_swing_mode_support_(CLIMATE_SWING_VERTICAL, supported); }
|
||||
ESPDEPRECATED("This method is deprecated, use set_supported_swing_modes() instead", "v1.20")
|
||||
void set_supports_swing_mode_horizontal(bool supported) {
|
||||
set_swing_mode_support_(CLIMATE_SWING_HORIZONTAL, supported);
|
||||
}
|
||||
bool supports_swing_mode(ClimateSwingMode swing_mode) const { return supported_swing_modes_.count(swing_mode); }
|
||||
bool get_supports_swing_modes() const { return !supported_swing_modes_.empty(); }
|
||||
const std::set<ClimateSwingMode> get_supported_swing_modes() { return supported_swing_modes_; }
|
||||
|
||||
float get_visual_min_temperature() const { return visual_min_temperature_; }
|
||||
void set_visual_min_temperature(float visual_min_temperature) { visual_min_temperature_ = visual_min_temperature; }
|
||||
float get_visual_max_temperature() const { return visual_max_temperature_; }
|
||||
void set_visual_max_temperature(float visual_max_temperature) { visual_max_temperature_ = visual_max_temperature; }
|
||||
float get_visual_temperature_step() const { return visual_temperature_step_; }
|
||||
int8_t get_temperature_accuracy_decimals() const;
|
||||
void set_visual_temperature_step(float temperature_step);
|
||||
void set_visual_temperature_step(float temperature_step) { visual_temperature_step_ = temperature_step; }
|
||||
|
||||
protected:
|
||||
void set_mode_support_(climate::ClimateMode mode, bool supported) {
|
||||
if (supported) {
|
||||
supported_modes_.insert(mode);
|
||||
} else {
|
||||
supported_modes_.erase(mode);
|
||||
}
|
||||
}
|
||||
void set_fan_mode_support_(climate::ClimateFanMode mode, bool supported) {
|
||||
if (supported) {
|
||||
supported_fan_modes_.insert(mode);
|
||||
} else {
|
||||
supported_fan_modes_.erase(mode);
|
||||
}
|
||||
}
|
||||
void set_swing_mode_support_(climate::ClimateSwingMode mode, bool supported) {
|
||||
if (supported) {
|
||||
supported_swing_modes_.insert(mode);
|
||||
} else {
|
||||
supported_swing_modes_.erase(mode);
|
||||
}
|
||||
}
|
||||
|
||||
bool supports_current_temperature_{false};
|
||||
bool supports_two_point_target_temperature_{false};
|
||||
bool supports_auto_mode_{false};
|
||||
bool supports_cool_mode_{false};
|
||||
bool supports_heat_mode_{false};
|
||||
bool supports_fan_only_mode_{false};
|
||||
bool supports_dry_mode_{false};
|
||||
bool supports_away_{false};
|
||||
std::set<climate::ClimateMode> supported_modes_ = {climate::CLIMATE_MODE_OFF};
|
||||
bool supports_action_{false};
|
||||
bool supports_fan_mode_on_{false};
|
||||
bool supports_fan_mode_off_{false};
|
||||
bool supports_fan_mode_auto_{false};
|
||||
bool supports_fan_mode_low_{false};
|
||||
bool supports_fan_mode_medium_{false};
|
||||
bool supports_fan_mode_high_{false};
|
||||
bool supports_fan_mode_middle_{false};
|
||||
bool supports_fan_mode_focus_{false};
|
||||
bool supports_fan_mode_diffuse_{false};
|
||||
bool supports_swing_mode_off_{false};
|
||||
bool supports_swing_mode_both_{false};
|
||||
bool supports_swing_mode_vertical_{false};
|
||||
bool supports_swing_mode_horizontal_{false};
|
||||
std::set<climate::ClimateFanMode> supported_fan_modes_;
|
||||
std::set<climate::ClimateSwingMode> supported_swing_modes_;
|
||||
std::set<climate::ClimatePreset> supported_presets_;
|
||||
std::set<std::string> supported_custom_fan_modes_;
|
||||
std::set<std::string> supported_custom_presets_;
|
||||
|
||||
float visual_min_temperature_{10};
|
||||
float visual_max_temperature_{30};
|
||||
|
||||
@@ -9,7 +9,6 @@ from esphome.components import (
|
||||
)
|
||||
from esphome.components.remote_base import CONF_RECEIVER_ID, CONF_TRANSMITTER_ID
|
||||
from esphome.const import CONF_SUPPORTS_COOL, CONF_SUPPORTS_HEAT, CONF_SENSOR
|
||||
from esphome.core import coroutine
|
||||
|
||||
AUTO_LOAD = ["sensor", "remote_base"]
|
||||
CODEOWNERS = ["@glmnet"]
|
||||
@@ -39,19 +38,18 @@ CLIMATE_IR_WITH_RECEIVER_SCHEMA = CLIMATE_IR_SCHEMA.extend(
|
||||
)
|
||||
|
||||
|
||||
@coroutine
|
||||
def register_climate_ir(var, config):
|
||||
yield cg.register_component(var, config)
|
||||
yield climate.register_climate(var, config)
|
||||
async def register_climate_ir(var, config):
|
||||
await cg.register_component(var, config)
|
||||
await climate.register_climate(var, config)
|
||||
|
||||
cg.add(var.set_supports_cool(config[CONF_SUPPORTS_COOL]))
|
||||
cg.add(var.set_supports_heat(config[CONF_SUPPORTS_HEAT]))
|
||||
if CONF_SENSOR in config:
|
||||
sens = yield cg.get_variable(config[CONF_SENSOR])
|
||||
sens = await cg.get_variable(config[CONF_SENSOR])
|
||||
cg.add(var.set_sensor(sens))
|
||||
if CONF_RECEIVER_ID in config:
|
||||
receiver = yield cg.get_variable(config[CONF_RECEIVER_ID])
|
||||
receiver = await cg.get_variable(config[CONF_RECEIVER_ID])
|
||||
cg.add(receiver.register_listener(var))
|
||||
|
||||
transmitter = yield cg.get_variable(config[CONF_TRANSMITTER_ID])
|
||||
transmitter = await cg.get_variable(config[CONF_TRANSMITTER_ID])
|
||||
cg.add(var.set_transmitter(transmitter))
|
||||
|
||||
@@ -4,68 +4,27 @@
|
||||
namespace esphome {
|
||||
namespace climate_ir {
|
||||
|
||||
static const char *TAG = "climate_ir";
|
||||
static const char *const TAG = "climate_ir";
|
||||
|
||||
climate::ClimateTraits ClimateIR::traits() {
|
||||
auto traits = climate::ClimateTraits();
|
||||
traits.set_supports_current_temperature(this->sensor_ != nullptr);
|
||||
traits.set_supports_auto_mode(true);
|
||||
traits.set_supports_cool_mode(this->supports_cool_);
|
||||
traits.set_supports_heat_mode(this->supports_heat_);
|
||||
traits.set_supports_dry_mode(this->supports_dry_);
|
||||
traits.set_supports_fan_only_mode(this->supports_fan_only_);
|
||||
traits.set_supported_modes({climate::CLIMATE_MODE_OFF, climate::CLIMATE_MODE_HEAT_COOL});
|
||||
if (supports_cool_)
|
||||
traits.add_supported_mode(climate::CLIMATE_MODE_COOL);
|
||||
if (supports_heat_)
|
||||
traits.add_supported_mode(climate::CLIMATE_MODE_HEAT);
|
||||
if (supports_dry_)
|
||||
traits.add_supported_mode(climate::CLIMATE_MODE_DRY);
|
||||
if (supports_fan_only_)
|
||||
traits.add_supported_mode(climate::CLIMATE_MODE_FAN_ONLY);
|
||||
|
||||
traits.set_supports_two_point_target_temperature(false);
|
||||
traits.set_supports_away(false);
|
||||
traits.set_visual_min_temperature(this->minimum_temperature_);
|
||||
traits.set_visual_max_temperature(this->maximum_temperature_);
|
||||
traits.set_visual_temperature_step(this->temperature_step_);
|
||||
for (auto fan_mode : this->fan_modes_) {
|
||||
switch (fan_mode) {
|
||||
case climate::CLIMATE_FAN_AUTO:
|
||||
traits.set_supports_fan_mode_auto(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_DIFFUSE:
|
||||
traits.set_supports_fan_mode_diffuse(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_FOCUS:
|
||||
traits.set_supports_fan_mode_focus(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
traits.set_supports_fan_mode_high(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_LOW:
|
||||
traits.set_supports_fan_mode_low(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MEDIUM:
|
||||
traits.set_supports_fan_mode_medium(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_MIDDLE:
|
||||
traits.set_supports_fan_mode_middle(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_OFF:
|
||||
traits.set_supports_fan_mode_off(true);
|
||||
break;
|
||||
case climate::CLIMATE_FAN_ON:
|
||||
traits.set_supports_fan_mode_on(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (auto swing_mode : this->swing_modes_) {
|
||||
switch (swing_mode) {
|
||||
case climate::CLIMATE_SWING_OFF:
|
||||
traits.set_supports_swing_mode_off(true);
|
||||
break;
|
||||
case climate::CLIMATE_SWING_BOTH:
|
||||
traits.set_supports_swing_mode_both(true);
|
||||
break;
|
||||
case climate::CLIMATE_SWING_VERTICAL:
|
||||
traits.set_supports_swing_mode_vertical(true);
|
||||
break;
|
||||
case climate::CLIMATE_SWING_HORIZONTAL:
|
||||
traits.set_supports_swing_mode_horizontal(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
traits.set_supported_fan_modes(fan_modes_);
|
||||
traits.set_supported_swing_modes(swing_modes_);
|
||||
return traits;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/components/climate/climate.h"
|
||||
#include "esphome/components/remote_base/remote_base.h"
|
||||
#include "esphome/components/remote_transmitter/remote_transmitter.h"
|
||||
@@ -19,16 +21,15 @@ namespace climate_ir {
|
||||
class ClimateIR : public climate::Climate, public Component, public remote_base::RemoteReceiverListener {
|
||||
public:
|
||||
ClimateIR(float minimum_temperature, float maximum_temperature, float temperature_step = 1.0f,
|
||||
bool supports_dry = false, bool supports_fan_only = false,
|
||||
std::vector<climate::ClimateFanMode> fan_modes = {},
|
||||
std::vector<climate::ClimateSwingMode> swing_modes = {}) {
|
||||
bool supports_dry = false, bool supports_fan_only = false, std::set<climate::ClimateFanMode> fan_modes = {},
|
||||
std::set<climate::ClimateSwingMode> swing_modes = {}) {
|
||||
this->minimum_temperature_ = minimum_temperature;
|
||||
this->maximum_temperature_ = maximum_temperature;
|
||||
this->temperature_step_ = temperature_step;
|
||||
this->supports_dry_ = supports_dry;
|
||||
this->supports_fan_only_ = supports_fan_only;
|
||||
this->fan_modes_ = fan_modes;
|
||||
this->swing_modes_ = swing_modes;
|
||||
this->fan_modes_ = std::move(fan_modes);
|
||||
this->swing_modes_ = std::move(swing_modes);
|
||||
}
|
||||
|
||||
void setup() override;
|
||||
@@ -58,8 +59,8 @@ class ClimateIR : public climate::Climate, public Component, public remote_base:
|
||||
bool supports_heat_{true};
|
||||
bool supports_dry_{false};
|
||||
bool supports_fan_only_{false};
|
||||
std::vector<climate::ClimateFanMode> fan_modes_ = {};
|
||||
std::vector<climate::ClimateSwingMode> swing_modes_ = {};
|
||||
std::set<climate::ClimateFanMode> fan_modes_ = {};
|
||||
std::set<climate::ClimateSwingMode> swing_modes_ = {};
|
||||
|
||||
remote_transmitter::RemoteTransmitterComponent *transmitter_;
|
||||
sensor::Sensor *sensor_{nullptr};
|
||||
|
||||
@@ -36,9 +36,9 @@ CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield climate_ir.register_climate_ir(var, config)
|
||||
await climate_ir.register_climate_ir(var, config)
|
||||
|
||||
cg.add(var.set_header_high(config[CONF_HEADER_HIGH]))
|
||||
cg.add(var.set_header_low(config[CONF_HEADER_LOW]))
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace climate_ir_lg {
|
||||
|
||||
static const char *TAG = "climate.climate_ir_lg";
|
||||
static const char *const TAG = "climate.climate_ir_lg";
|
||||
|
||||
const uint32_t COMMAND_ON = 0x00000;
|
||||
const uint32_t COMMAND_ON_AI = 0x03000;
|
||||
@@ -39,7 +39,7 @@ void LgIrClimate::transmit_state() {
|
||||
send_swing_cmd_ = false;
|
||||
remote_state |= COMMAND_SWING;
|
||||
} else {
|
||||
if (mode_before_ == climate::CLIMATE_MODE_OFF && this->mode == climate::CLIMATE_MODE_AUTO) {
|
||||
if (mode_before_ == climate::CLIMATE_MODE_OFF && this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
|
||||
remote_state |= COMMAND_ON_AI;
|
||||
} else if (mode_before_ == climate::CLIMATE_MODE_OFF && this->mode != climate::CLIMATE_MODE_OFF) {
|
||||
remote_state |= COMMAND_ON;
|
||||
@@ -52,7 +52,7 @@ void LgIrClimate::transmit_state() {
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
remote_state |= COMMAND_HEAT;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_AUTO:
|
||||
case climate::CLIMATE_MODE_HEAT_COOL:
|
||||
remote_state |= COMMAND_AUTO;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_DRY:
|
||||
@@ -72,7 +72,7 @@ void LgIrClimate::transmit_state() {
|
||||
remote_state |= FAN_AUTO;
|
||||
} else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_DRY ||
|
||||
this->mode == climate::CLIMATE_MODE_HEAT) {
|
||||
switch (this->fan_mode) {
|
||||
switch (this->fan_mode.value()) {
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
remote_state |= FAN_MAX;
|
||||
break;
|
||||
@@ -89,12 +89,12 @@ void LgIrClimate::transmit_state() {
|
||||
}
|
||||
}
|
||||
|
||||
if (this->mode == climate::CLIMATE_MODE_AUTO) {
|
||||
if (this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
// remote_state |= FAN_MODE_AUTO_DRY;
|
||||
}
|
||||
if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT) {
|
||||
auto temp = (uint8_t) roundf(clamp(this->target_temperature, TEMP_MIN, TEMP_MAX));
|
||||
auto temp = (uint8_t) roundf(clamp<float>(this->target_temperature, TEMP_MIN, TEMP_MAX));
|
||||
remote_state |= ((temp - 15) << TEMP_SHIFT);
|
||||
}
|
||||
}
|
||||
@@ -128,7 +128,7 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
if ((remote_state & COMMAND_MASK) == COMMAND_ON) {
|
||||
this->mode = climate::CLIMATE_MODE_COOL;
|
||||
} else if ((remote_state & COMMAND_MASK) == COMMAND_ON_AI) {
|
||||
this->mode = climate::CLIMATE_MODE_AUTO;
|
||||
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
|
||||
}
|
||||
|
||||
if ((remote_state & COMMAND_MASK) == COMMAND_OFF) {
|
||||
@@ -138,7 +138,7 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
this->swing_mode == climate::CLIMATE_SWING_OFF ? climate::CLIMATE_SWING_VERTICAL : climate::CLIMATE_SWING_OFF;
|
||||
} else {
|
||||
if ((remote_state & COMMAND_MASK) == COMMAND_AUTO)
|
||||
this->mode = climate::CLIMATE_MODE_AUTO;
|
||||
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
|
||||
else if ((remote_state & COMMAND_MASK) == COMMAND_DRY_FAN)
|
||||
this->mode = climate::CLIMATE_MODE_DRY;
|
||||
else if ((remote_state & COMMAND_MASK) == COMMAND_HEAT) {
|
||||
@@ -152,7 +152,7 @@ bool LgIrClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
this->target_temperature = ((remote_state & TEMP_MASK) >> TEMP_SHIFT) + 15;
|
||||
|
||||
// Fan Speed
|
||||
if (this->mode == climate::CLIMATE_MODE_AUTO) {
|
||||
if (this->mode == climate::CLIMATE_MODE_HEAT_COOL) {
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
} else if (this->mode == climate::CLIMATE_MODE_COOL || this->mode == climate::CLIMATE_MODE_HEAT ||
|
||||
this->mode == climate::CLIMATE_MODE_DRY) {
|
||||
|
||||
@@ -26,7 +26,7 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
r = 0
|
||||
if CONF_RED in config:
|
||||
r = int(config[CONF_RED] * 255)
|
||||
|
||||
0
esphome/components/color_temperature/__init__.py
Normal file
0
esphome/components/color_temperature/__init__.py
Normal file
38
esphome/components/color_temperature/ct_light_output.h
Normal file
38
esphome/components/color_temperature/ct_light_output.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/light/light_output.h"
|
||||
#include "esphome/components/output/float_output.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace color_temperature {
|
||||
|
||||
class CTLightOutput : public light::LightOutput {
|
||||
public:
|
||||
void set_color_temperature(output::FloatOutput *color_temperature) { color_temperature_ = color_temperature; }
|
||||
void set_brightness(output::FloatOutput *brightness) { brightness_ = brightness; }
|
||||
void set_cold_white_temperature(float cold_white_temperature) { cold_white_temperature_ = cold_white_temperature; }
|
||||
void set_warm_white_temperature(float warm_white_temperature) { warm_white_temperature_ = warm_white_temperature; }
|
||||
light::LightTraits get_traits() override {
|
||||
auto traits = light::LightTraits();
|
||||
traits.set_supported_color_modes({light::ColorMode::COLOR_TEMPERATURE});
|
||||
traits.set_min_mireds(this->cold_white_temperature_);
|
||||
traits.set_max_mireds(this->warm_white_temperature_);
|
||||
return traits;
|
||||
}
|
||||
void write_state(light::LightState *state) override {
|
||||
float color_temperature, brightness;
|
||||
state->current_values_as_ct(&color_temperature, &brightness);
|
||||
this->color_temperature_->set_level(color_temperature);
|
||||
this->brightness_->set_level(brightness);
|
||||
}
|
||||
|
||||
protected:
|
||||
output::FloatOutput *color_temperature_;
|
||||
output::FloatOutput *brightness_;
|
||||
float cold_white_temperature_;
|
||||
float warm_white_temperature_;
|
||||
};
|
||||
|
||||
} // namespace color_temperature
|
||||
} // namespace esphome
|
||||
42
esphome/components/color_temperature/light.py
Normal file
42
esphome/components/color_temperature/light.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import light, output
|
||||
from esphome.const import (
|
||||
CONF_BRIGHTNESS,
|
||||
CONF_COLOR_TEMPERATURE,
|
||||
CONF_OUTPUT_ID,
|
||||
CONF_COLD_WHITE_COLOR_TEMPERATURE,
|
||||
CONF_WARM_WHITE_COLOR_TEMPERATURE,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
|
||||
color_temperature_ns = cg.esphome_ns.namespace("color_temperature")
|
||||
CTLightOutput = color_temperature_ns.class_("CTLightOutput", light.LightOutput)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
light.RGB_LIGHT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_OUTPUT_ID): cv.declare_id(CTLightOutput),
|
||||
cv.Required(CONF_COLOR_TEMPERATURE): cv.use_id(output.FloatOutput),
|
||||
cv.Required(CONF_BRIGHTNESS): cv.use_id(output.FloatOutput),
|
||||
cv.Required(CONF_COLD_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
|
||||
cv.Required(CONF_WARM_WHITE_COLOR_TEMPERATURE): cv.color_temperature,
|
||||
}
|
||||
),
|
||||
light.validate_color_temperature_channels,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_OUTPUT_ID])
|
||||
await light.register_light(var, config)
|
||||
|
||||
color_temperature = await cg.get_variable(config[CONF_COLOR_TEMPERATURE])
|
||||
cg.add(var.set_color_temperature(color_temperature))
|
||||
|
||||
brightness = await cg.get_variable(config[CONF_BRIGHTNESS])
|
||||
cg.add(var.set_brightness(brightness))
|
||||
|
||||
cg.add(var.set_cold_white_temperature(config[CONF_COLD_WHITE_COLOR_TEMPERATURE]))
|
||||
cg.add(var.set_warm_white_temperature(config[CONF_WARM_WHITE_COLOR_TEMPERATURE]))
|
||||
@@ -16,6 +16,6 @@ CONFIG_SCHEMA = climate_ir.CLIMATE_IR_WITH_RECEIVER_SCHEMA.extend(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield climate_ir.register_climate_ir(var, config)
|
||||
await climate_ir.register_climate_ir(var, config)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace coolix {
|
||||
|
||||
static const char *TAG = "coolix.climate";
|
||||
static const char *const TAG = "coolix.climate";
|
||||
|
||||
const uint32_t COOLIX_OFF = 0xB27BE0;
|
||||
const uint32_t COOLIX_SWING = 0xB26BE0;
|
||||
@@ -70,7 +70,7 @@ void CoolixClimate::transmit_state() {
|
||||
case climate::CLIMATE_MODE_HEAT:
|
||||
remote_state |= COOLIX_HEAT;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_AUTO:
|
||||
case climate::CLIMATE_MODE_HEAT_COOL:
|
||||
remote_state |= COOLIX_AUTO;
|
||||
break;
|
||||
case climate::CLIMATE_MODE_FAN_ONLY:
|
||||
@@ -84,16 +84,16 @@ void CoolixClimate::transmit_state() {
|
||||
}
|
||||
if (this->mode != climate::CLIMATE_MODE_OFF) {
|
||||
if (this->mode != climate::CLIMATE_MODE_FAN_ONLY) {
|
||||
auto temp = (uint8_t) roundf(clamp(this->target_temperature, COOLIX_TEMP_MIN, COOLIX_TEMP_MAX));
|
||||
auto temp = (uint8_t) roundf(clamp<float>(this->target_temperature, COOLIX_TEMP_MIN, COOLIX_TEMP_MAX));
|
||||
remote_state |= COOLIX_TEMP_MAP[temp - COOLIX_TEMP_MIN];
|
||||
} else {
|
||||
remote_state |= COOLIX_FAN_TEMP_CODE;
|
||||
}
|
||||
if (this->mode == climate::CLIMATE_MODE_AUTO || this->mode == climate::CLIMATE_MODE_DRY) {
|
||||
if (this->mode == climate::CLIMATE_MODE_HEAT_COOL || this->mode == climate::CLIMATE_MODE_DRY) {
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
remote_state |= COOLIX_FAN_MODE_AUTO_DRY;
|
||||
} else {
|
||||
switch (this->fan_mode) {
|
||||
switch (this->fan_mode.value()) {
|
||||
case climate::CLIMATE_FAN_HIGH:
|
||||
remote_state |= COOLIX_FAN_MAX;
|
||||
break;
|
||||
@@ -197,7 +197,7 @@ bool CoolixClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
if ((remote_state & COOLIX_MODE_MASK) == COOLIX_HEAT)
|
||||
this->mode = climate::CLIMATE_MODE_HEAT;
|
||||
else if ((remote_state & COOLIX_MODE_MASK) == COOLIX_AUTO)
|
||||
this->mode = climate::CLIMATE_MODE_AUTO;
|
||||
this->mode = climate::CLIMATE_MODE_HEAT_COOL;
|
||||
else if ((remote_state & COOLIX_MODE_MASK) == COOLIX_DRY_FAN) {
|
||||
if ((remote_state & COOLIX_FAN_MASK) == COOLIX_FAN_MODE_AUTO_DRY)
|
||||
this->mode = climate::CLIMATE_MODE_DRY;
|
||||
@@ -207,7 +207,7 @@ bool CoolixClimate::on_receive(remote_base::RemoteReceiveData data) {
|
||||
this->mode = climate::CLIMATE_MODE_COOL;
|
||||
|
||||
// Fan Speed
|
||||
if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || this->mode == climate::CLIMATE_MODE_AUTO ||
|
||||
if ((remote_state & COOLIX_FAN_AUTO) == COOLIX_FAN_AUTO || this->mode == climate::CLIMATE_MODE_HEAT_COOL ||
|
||||
this->mode == climate::CLIMATE_MODE_DRY)
|
||||
this->fan_mode = climate::CLIMATE_FAN_AUTO;
|
||||
else if ((remote_state & COOLIX_FAN_MIN) == COOLIX_FAN_MIN)
|
||||
|
||||
@@ -4,6 +4,7 @@ from esphome import automation
|
||||
from esphome.automation import maybe_simple_id, Condition
|
||||
from esphome.components import mqtt
|
||||
from esphome.const import (
|
||||
CONF_DISABLED_BY_DEFAULT,
|
||||
CONF_ID,
|
||||
CONF_INTERNAL,
|
||||
CONF_DEVICE_CLASS,
|
||||
@@ -14,7 +15,7 @@ from esphome.const import (
|
||||
CONF_MQTT_ID,
|
||||
CONF_NAME,
|
||||
)
|
||||
from esphome.core import CORE, coroutine, coroutine_with_priority
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
@@ -63,7 +64,7 @@ CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
|
||||
CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
|
||||
CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition)
|
||||
|
||||
COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
|
||||
COVER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(Cover),
|
||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
|
||||
@@ -73,9 +74,9 @@ COVER_SCHEMA = cv.MQTT_COMMAND_COMPONENT_SCHEMA.extend(
|
||||
)
|
||||
|
||||
|
||||
@coroutine
|
||||
def setup_cover_core_(var, config):
|
||||
async def setup_cover_core_(var, config):
|
||||
cg.add(var.set_name(config[CONF_NAME]))
|
||||
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
|
||||
if CONF_INTERNAL in config:
|
||||
cg.add(var.set_internal(config[CONF_INTERNAL]))
|
||||
if CONF_DEVICE_CLASS in config:
|
||||
@@ -83,15 +84,14 @@ def setup_cover_core_(var, config):
|
||||
|
||||
if CONF_MQTT_ID in config:
|
||||
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)
|
||||
yield mqtt.register_mqtt_component(mqtt_, config)
|
||||
await mqtt.register_mqtt_component(mqtt_, config)
|
||||
|
||||
|
||||
@coroutine
|
||||
def register_cover(var, config):
|
||||
async def register_cover(var, config):
|
||||
if not CORE.has_id(config[CONF_ID]):
|
||||
var = cg.Pvariable(config[CONF_ID], var)
|
||||
cg.add(cg.App.register_cover(var))
|
||||
yield setup_cover_core_(var, config)
|
||||
await setup_cover_core_(var, config)
|
||||
|
||||
|
||||
COVER_ACTION_SCHEMA = maybe_simple_id(
|
||||
@@ -102,21 +102,21 @@ COVER_ACTION_SCHEMA = maybe_simple_id(
|
||||
|
||||
|
||||
@automation.register_action("cover.open", OpenAction, COVER_ACTION_SCHEMA)
|
||||
def cover_open_to_code(config, action_id, template_arg, args):
|
||||
paren = yield cg.get_variable(config[CONF_ID])
|
||||
yield cg.new_Pvariable(action_id, template_arg, paren)
|
||||
async def cover_open_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action("cover.close", CloseAction, COVER_ACTION_SCHEMA)
|
||||
def cover_close_to_code(config, action_id, template_arg, args):
|
||||
paren = yield cg.get_variable(config[CONF_ID])
|
||||
yield cg.new_Pvariable(action_id, template_arg, paren)
|
||||
async def cover_close_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
@automation.register_action("cover.stop", StopAction, COVER_ACTION_SCHEMA)
|
||||
def cover_stop_to_code(config, action_id, template_arg, args):
|
||||
paren = yield cg.get_variable(config[CONF_ID])
|
||||
yield cg.new_Pvariable(action_id, template_arg, paren)
|
||||
async def cover_stop_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
|
||||
|
||||
COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
|
||||
@@ -131,25 +131,25 @@ COVER_CONTROL_ACTION_SCHEMA = cv.Schema(
|
||||
|
||||
|
||||
@automation.register_action("cover.control", ControlAction, COVER_CONTROL_ACTION_SCHEMA)
|
||||
def cover_control_to_code(config, action_id, template_arg, args):
|
||||
paren = yield cg.get_variable(config[CONF_ID])
|
||||
async def cover_control_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
if CONF_STOP in config:
|
||||
template_ = yield cg.templatable(config[CONF_STOP], args, bool)
|
||||
template_ = await cg.templatable(config[CONF_STOP], args, bool)
|
||||
cg.add(var.set_stop(template_))
|
||||
if CONF_STATE in config:
|
||||
template_ = yield cg.templatable(config[CONF_STATE], args, float)
|
||||
template_ = await cg.templatable(config[CONF_STATE], args, float)
|
||||
cg.add(var.set_position(template_))
|
||||
if CONF_POSITION in config:
|
||||
template_ = yield cg.templatable(config[CONF_POSITION], args, float)
|
||||
template_ = await cg.templatable(config[CONF_POSITION], args, float)
|
||||
cg.add(var.set_position(template_))
|
||||
if CONF_TILT in config:
|
||||
template_ = yield cg.templatable(config[CONF_TILT], args, float)
|
||||
template_ = await cg.templatable(config[CONF_TILT], args, float)
|
||||
cg.add(var.set_tilt(template_))
|
||||
yield var
|
||||
return var
|
||||
|
||||
|
||||
@coroutine_with_priority(100.0)
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
cg.add_define("USE_COVER")
|
||||
cg.add_global(cover_ns.using)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace cover {
|
||||
|
||||
static const char *TAG = "cover";
|
||||
static const char *const TAG = "cover";
|
||||
|
||||
const float COVER_OPEN = 1.0f;
|
||||
const float COVER_CLOSED = 0.0f;
|
||||
|
||||
@@ -12,14 +12,14 @@ const extern float COVER_OPEN;
|
||||
const extern float COVER_CLOSED;
|
||||
|
||||
#define LOG_COVER(prefix, type, obj) \
|
||||
if (obj != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \
|
||||
auto traits_ = obj->get_traits(); \
|
||||
if ((obj) != nullptr) { \
|
||||
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, (obj)->get_name().c_str()); \
|
||||
auto traits_ = (obj)->get_traits(); \
|
||||
if (traits_.get_is_assumed_state()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Assumed State: YES", prefix); \
|
||||
} \
|
||||
if (!obj->get_device_class().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); \
|
||||
if (!(obj)->get_device_class().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, (obj)->get_device_class().c_str()); \
|
||||
} \
|
||||
}
|
||||
|
||||
@@ -110,15 +110,12 @@ class Cover : public Nameable {
|
||||
|
||||
/// The current operation of the cover (idle, opening, closing).
|
||||
CoverOperation current_operation{COVER_OPERATION_IDLE};
|
||||
union {
|
||||
/** The position of the cover from 0.0 (fully closed) to 1.0 (fully open).
|
||||
*
|
||||
* For binary covers this is always equals to 0.0 or 1.0 (see also COVER_OPEN and
|
||||
* COVER_CLOSED constants).
|
||||
*/
|
||||
float position;
|
||||
ESPDEPRECATED("<cover>.state is deprecated, please use .position instead") float state;
|
||||
};
|
||||
/** The position of the cover from 0.0 (fully closed) to 1.0 (fully open).
|
||||
*
|
||||
* For binary covers this is always equals to 0.0 or 1.0 (see also COVER_OPEN and
|
||||
* COVER_CLOSED constants).
|
||||
*/
|
||||
float position;
|
||||
/// The current tilt value of the cover from 0.0 to 1.0.
|
||||
float tilt{COVER_OPEN};
|
||||
|
||||
|
||||
0
esphome/components/cs5460a/__init__.py
Normal file
0
esphome/components/cs5460a/__init__.py
Normal file
342
esphome/components/cs5460a/cs5460a.cpp
Normal file
342
esphome/components/cs5460a/cs5460a.cpp
Normal file
@@ -0,0 +1,342 @@
|
||||
#include "cs5460a.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace cs5460a {
|
||||
|
||||
static const char *const TAG = "cs5460a";
|
||||
|
||||
void CS5460AComponent::write_register_(enum CS5460ARegister addr, uint32_t value) {
|
||||
this->write_byte(CMD_WRITE | (addr << 1));
|
||||
this->write_byte(value >> 16);
|
||||
this->write_byte(value >> 8);
|
||||
this->write_byte(value >> 0);
|
||||
}
|
||||
|
||||
uint32_t CS5460AComponent::read_register_(uint8_t addr) {
|
||||
uint32_t value;
|
||||
|
||||
this->write_byte(CMD_READ | (addr << 1));
|
||||
value = (uint32_t) this->transfer_byte(CMD_SYNC0) << 16;
|
||||
value |= (uint32_t) this->transfer_byte(CMD_SYNC0) << 8;
|
||||
value |= this->transfer_byte(CMD_SYNC0) << 0;
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
bool CS5460AComponent::softreset_() {
|
||||
uint32_t pc = ((uint8_t) phase_offset_ & 0x3f) | (phase_offset_ < 0 ? 0x40 : 0);
|
||||
uint32_t config = (1 << 0) | /* K = 0b0001 */
|
||||
(current_hpf_ ? 1 << 5 : 0) | /* IHPF */
|
||||
(voltage_hpf_ ? 1 << 6 : 0) | /* VHPF */
|
||||
(pga_gain_ << 16) | /* Gi */
|
||||
(pc << 17); /* PC */
|
||||
int cnt = 0;
|
||||
|
||||
/* Serial resynchronization */
|
||||
this->write_byte(CMD_SYNC1);
|
||||
this->write_byte(CMD_SYNC1);
|
||||
this->write_byte(CMD_SYNC1);
|
||||
this->write_byte(CMD_SYNC0);
|
||||
|
||||
/* Reset */
|
||||
this->write_register_(REG_CONFIG, 1 << 7);
|
||||
delay(10);
|
||||
while (cnt++ < 50 && (this->read_register_(REG_CONFIG) & 0x81) != 0x000001)
|
||||
;
|
||||
if (cnt > 50)
|
||||
return false;
|
||||
|
||||
this->write_register_(REG_CONFIG, config);
|
||||
return true;
|
||||
}
|
||||
|
||||
void CS5460AComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up CS5460A...");
|
||||
|
||||
float current_full_scale = (pga_gain_ == CS5460A_PGA_GAIN_10X) ? 0.25 : 0.10;
|
||||
float voltage_full_scale = 0.25;
|
||||
current_multiplier_ = current_full_scale / (fabsf(current_gain_) * 0x1000000);
|
||||
voltage_multiplier_ = voltage_full_scale / (voltage_gain_ * 0x1000000);
|
||||
|
||||
/*
|
||||
* Calculate power from the Energy register because the Power register
|
||||
* stores instantaneous power which varies a lot in each AC cycle,
|
||||
* while the Energy value is accumulated over the "computation cycle"
|
||||
* which should be an integer number of AC cycles.
|
||||
*/
|
||||
power_multiplier_ =
|
||||
(current_full_scale * voltage_full_scale * 4096) / (current_gain_ * voltage_gain_ * samples_ * 0x800000);
|
||||
|
||||
pulse_freq_ =
|
||||
(current_full_scale * voltage_full_scale) / (fabsf(current_gain_) * voltage_gain_ * pulse_energy_wh_ * 3600);
|
||||
|
||||
hw_init_();
|
||||
}
|
||||
|
||||
void CS5460AComponent::hw_init_() {
|
||||
this->spi_setup();
|
||||
this->enable();
|
||||
|
||||
if (!this->softreset_()) {
|
||||
this->disable();
|
||||
ESP_LOGE(TAG, "CS5460A reset failed!");
|
||||
this->mark_failed();
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t status = this->read_register_(REG_STATUS);
|
||||
ESP_LOGCONFIG(TAG, " Version: %x", (status >> 6) & 7);
|
||||
|
||||
this->write_register_(REG_CYCLE_COUNT, samples_);
|
||||
this->write_register_(REG_PULSE_RATE, lroundf(pulse_freq_ * 32.0f));
|
||||
|
||||
/* Use one of the power saving features (assuming external oscillator), reset other CONTROL bits,
|
||||
* sometimes softreset_() is not enough */
|
||||
this->write_register_(REG_CONTROL, 0x000004);
|
||||
|
||||
this->restart_();
|
||||
this->disable();
|
||||
ESP_LOGCONFIG(TAG, " Init ok");
|
||||
}
|
||||
|
||||
/* Doesn't reset the register values etc., just restarts the "computation cycle" */
|
||||
void CS5460AComponent::restart_() {
|
||||
int cnt;
|
||||
|
||||
this->enable();
|
||||
/* Stop running conversion, wake up if needed */
|
||||
this->write_byte(CMD_POWER_UP);
|
||||
/* Start continuous conversion */
|
||||
this->write_byte(CMD_START_CONT);
|
||||
this->disable();
|
||||
|
||||
this->started_();
|
||||
}
|
||||
|
||||
void CS5460AComponent::started_() {
|
||||
/*
|
||||
* Try to guess when the next batch of results is going to be ready and
|
||||
* schedule next STATUS check some time before that moment. This assumes
|
||||
* two things:
|
||||
* * a new "computation cycle" started just now. If it started some
|
||||
* time ago we may be a late next time, but hopefully less late in each
|
||||
* iteration -- that's why we schedule the next check in some 0.8 of
|
||||
* the time we actually expect the next reading ready.
|
||||
* * MCLK rate is 4.096MHz and K == 1. If there's a CS5460A module in
|
||||
* use with a different clock this will need to be parametrised.
|
||||
*/
|
||||
expect_data_ts_ = millis() + samples_ * 1024 / 4096;
|
||||
|
||||
schedule_next_check_();
|
||||
}
|
||||
|
||||
void CS5460AComponent::schedule_next_check_() {
|
||||
int32_t time_left = expect_data_ts_ - millis();
|
||||
|
||||
/* First try at 0.8 of the actual expected time (if it's in the future) */
|
||||
if (time_left > 0)
|
||||
time_left -= time_left / 5;
|
||||
|
||||
if (time_left > -500) {
|
||||
/* But not sooner than in 30ms from now */
|
||||
if (time_left < 30)
|
||||
time_left = 30;
|
||||
} else {
|
||||
/*
|
||||
* If the measurement is more than 0.5s overdue start worrying. The
|
||||
* device may be stuck because of an overcurrent error or similar,
|
||||
* from now on just retry every 1s. After 15s try a reset, if it
|
||||
* fails we give up and mark the component "failed".
|
||||
*/
|
||||
if (time_left > -15000) {
|
||||
time_left = 1000;
|
||||
this->status_momentary_warning("warning", 1000);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, "Device officially stuck, resetting");
|
||||
this->cancel_timeout("status-check");
|
||||
this->hw_init_();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this->set_timeout("status-check", time_left, [this]() {
|
||||
if (!this->check_status_())
|
||||
this->schedule_next_check_();
|
||||
});
|
||||
}
|
||||
|
||||
bool CS5460AComponent::check_status_() {
|
||||
this->enable();
|
||||
uint32_t status = this->read_register_(REG_STATUS);
|
||||
|
||||
if (!(status & 0xcbf83c)) {
|
||||
this->disable();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t clear = 1 << 20;
|
||||
|
||||
/* TODO: Report if IC=0 but only once as it can't be cleared */
|
||||
|
||||
if (status & (1 << 2)) {
|
||||
clear |= 1 << 2;
|
||||
ESP_LOGE(TAG, "Low supply detected");
|
||||
this->status_momentary_warning("warning", 500);
|
||||
}
|
||||
|
||||
if (status & (1 << 3)) {
|
||||
clear |= 1 << 3;
|
||||
ESP_LOGE(TAG, "Modulator oscillation on current channel");
|
||||
this->status_momentary_warning("warning", 500);
|
||||
}
|
||||
|
||||
if (status & (1 << 4)) {
|
||||
clear |= 1 << 4;
|
||||
ESP_LOGE(TAG, "Modulator oscillation on voltage channel");
|
||||
this->status_momentary_warning("warning", 500);
|
||||
}
|
||||
|
||||
if (status & (1 << 5)) {
|
||||
clear |= 1 << 5;
|
||||
ESP_LOGE(TAG, "Watch-dog timeout");
|
||||
this->status_momentary_warning("warning", 500);
|
||||
}
|
||||
|
||||
if (status & (1 << 11)) {
|
||||
clear |= 1 << 11;
|
||||
ESP_LOGE(TAG, "EOUT Energy Accumulation Register out of range");
|
||||
this->status_momentary_warning("warning", 500);
|
||||
}
|
||||
|
||||
if (status & (1 << 12)) {
|
||||
clear |= 1 << 12;
|
||||
ESP_LOGE(TAG, "Energy out of range");
|
||||
this->status_momentary_warning("warning", 500);
|
||||
}
|
||||
|
||||
if (status & (1 << 13)) {
|
||||
clear |= 1 << 13;
|
||||
ESP_LOGE(TAG, "RMS voltage out of range");
|
||||
this->status_momentary_warning("warning", 500);
|
||||
}
|
||||
|
||||
if (status & (1 << 14)) {
|
||||
clear |= 1 << 14;
|
||||
ESP_LOGE(TAG, "RMS current out of range");
|
||||
this->status_momentary_warning("warning", 500);
|
||||
}
|
||||
|
||||
if (status & (1 << 15)) {
|
||||
clear |= 1 << 15;
|
||||
ESP_LOGE(TAG, "Power calculation out of range");
|
||||
this->status_momentary_warning("warning", 500);
|
||||
}
|
||||
|
||||
if (status & (1 << 16)) {
|
||||
clear |= 1 << 16;
|
||||
ESP_LOGE(TAG, "Voltage out of range");
|
||||
this->status_momentary_warning("warning", 500);
|
||||
}
|
||||
|
||||
if (status & (1 << 17)) {
|
||||
clear |= 1 << 17;
|
||||
ESP_LOGE(TAG, "Current out of range");
|
||||
this->status_momentary_warning("warning", 500);
|
||||
}
|
||||
|
||||
if (status & (1 << 19)) {
|
||||
clear |= 1 << 19;
|
||||
ESP_LOGE(TAG, "Divide overflowed");
|
||||
}
|
||||
|
||||
if (status & (1 << 22)) {
|
||||
bool dir = status & (1 << 21);
|
||||
if (current_gain_ < 0)
|
||||
dir = !dir;
|
||||
ESP_LOGI(TAG, "Energy counter %s pulse", dir ? "negative" : "positive");
|
||||
clear |= 1 << 22;
|
||||
}
|
||||
|
||||
uint32_t raw_current = 0; /* Calm the validators */
|
||||
uint32_t raw_voltage = 0;
|
||||
uint32_t raw_energy = 0;
|
||||
|
||||
if (status & (1 << 23)) {
|
||||
clear |= 1 << 23;
|
||||
|
||||
if (current_sensor_ != nullptr)
|
||||
raw_current = this->read_register_(REG_IRMS);
|
||||
|
||||
if (voltage_sensor_ != nullptr)
|
||||
raw_voltage = this->read_register_(REG_VRMS);
|
||||
}
|
||||
|
||||
if (status & ((1 << 23) | (1 << 5))) {
|
||||
/* Read to clear the WDT bit */
|
||||
raw_energy = this->read_register_(REG_E);
|
||||
}
|
||||
|
||||
this->write_register_(REG_STATUS, clear);
|
||||
this->disable();
|
||||
|
||||
/*
|
||||
* Schedule the next STATUS check assuming that DRDY was asserted very
|
||||
* recently, then publish the new values. Do this last for reentrancy in
|
||||
* case the publish triggers a restart() or for whatever reason needs to
|
||||
* cancel the timeout set in schedule_next_check_(), or needs to use SPI.
|
||||
* If the current or power values haven't changed one bit it may be that
|
||||
* the chip somehow forgot to update the registers -- seen happening very
|
||||
* rarely. In that case don't publish them because the user may have
|
||||
* the input connected to a multiplexer and may have switched channels
|
||||
* since the previous reading and we'd be publishing the stale value for
|
||||
* the new channel. If the value *was* updated it's very unlikely that
|
||||
* it wouldn't have changed, especially power/energy which are affected
|
||||
* by the noise on both the current and value channels (in case of energy,
|
||||
* accumulated over many conversion cycles.)
|
||||
*/
|
||||
if (status & (1 << 23)) {
|
||||
this->started_();
|
||||
|
||||
if (current_sensor_ != nullptr && raw_current != prev_raw_current_) {
|
||||
current_sensor_->publish_state(raw_current * current_multiplier_);
|
||||
prev_raw_current_ = raw_current;
|
||||
}
|
||||
|
||||
if (voltage_sensor_ != nullptr)
|
||||
voltage_sensor_->publish_state(raw_voltage * voltage_multiplier_);
|
||||
|
||||
if (power_sensor_ != nullptr && raw_energy != prev_raw_energy_) {
|
||||
int32_t raw = (int32_t)(raw_energy << 8) >> 8; /* Sign-extend */
|
||||
power_sensor_->publish_state(raw * power_multiplier_);
|
||||
prev_raw_energy_ = raw_energy;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void CS5460AComponent::dump_config() {
|
||||
uint32_t state = this->get_component_state();
|
||||
|
||||
ESP_LOGCONFIG(TAG, "CS5460A:");
|
||||
ESP_LOGCONFIG(TAG, " Init status: %s",
|
||||
state == COMPONENT_STATE_LOOP ? "OK" : (state == COMPONENT_STATE_FAILED ? "failed" : "other"));
|
||||
LOG_PIN(" CS Pin: ", cs_);
|
||||
ESP_LOGCONFIG(TAG, " Samples / cycle: %u", samples_);
|
||||
ESP_LOGCONFIG(TAG, " Phase offset: %i", phase_offset_);
|
||||
ESP_LOGCONFIG(TAG, " PGA Gain: %s", pga_gain_ == CS5460A_PGA_GAIN_50X ? "50x" : "10x");
|
||||
ESP_LOGCONFIG(TAG, " Current gain: %.5f", current_gain_);
|
||||
ESP_LOGCONFIG(TAG, " Voltage gain: %.5f", voltage_gain_);
|
||||
ESP_LOGCONFIG(TAG, " Current HPF: %s", current_hpf_ ? "enabled" : "disabled");
|
||||
ESP_LOGCONFIG(TAG, " Voltage HPF: %s", voltage_hpf_ ? "enabled" : "disabled");
|
||||
ESP_LOGCONFIG(TAG, " Pulse energy: %.2f Wh", pulse_energy_wh_);
|
||||
LOG_SENSOR(" ", "Voltage", voltage_sensor_);
|
||||
LOG_SENSOR(" ", "Current", current_sensor_);
|
||||
LOG_SENSOR(" ", "Power", power_sensor_);
|
||||
}
|
||||
|
||||
} // namespace cs5460a
|
||||
} // namespace esphome
|
||||
123
esphome/components/cs5460a/cs5460a.h
Normal file
123
esphome/components/cs5460a/cs5460a.h
Normal file
@@ -0,0 +1,123 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace cs5460a {
|
||||
|
||||
enum CS5460ACommand {
|
||||
CMD_SYNC0 = 0xfe,
|
||||
CMD_SYNC1 = 0xff,
|
||||
CMD_START_SINGLE = 0xe0,
|
||||
CMD_START_CONT = 0xe8,
|
||||
CMD_POWER_UP = 0xa0,
|
||||
CMD_POWER_STANDBY = 0x88,
|
||||
CMD_POWER_SLEEP = 0x90,
|
||||
CMD_CALIBRATION = 0xc0,
|
||||
CMD_READ = 0x00,
|
||||
CMD_WRITE = 0x40,
|
||||
};
|
||||
|
||||
enum CS5460ARegister {
|
||||
REG_CONFIG = 0x00,
|
||||
REG_IDCOFF = 0x01,
|
||||
REG_IGN = 0x02,
|
||||
REG_VDCOFF = 0x03,
|
||||
REG_VGN = 0x04,
|
||||
REG_CYCLE_COUNT = 0x05,
|
||||
REG_PULSE_RATE = 0x06,
|
||||
REG_I = 0x07,
|
||||
REG_V = 0x08,
|
||||
REG_P = 0x09,
|
||||
REG_E = 0x0a,
|
||||
REG_IRMS = 0x0b,
|
||||
REG_VRMS = 0x0c,
|
||||
REG_TBC = 0x0d,
|
||||
REG_POFF = 0x0e,
|
||||
REG_STATUS = 0x0f,
|
||||
REG_IACOFF = 0x10,
|
||||
REG_VACOFF = 0x11,
|
||||
REG_MASK = 0x1a,
|
||||
REG_CONTROL = 0x1c,
|
||||
};
|
||||
|
||||
/** Enum listing the current channel aplifiergain settings for the CS5460A.
|
||||
*/
|
||||
enum CS5460APGAGain {
|
||||
CS5460A_PGA_GAIN_10X = 0b0,
|
||||
CS5460A_PGA_GAIN_50X = 0b1,
|
||||
};
|
||||
|
||||
class CS5460AComponent : public Component,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
|
||||
spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_1MHZ> {
|
||||
public:
|
||||
void set_samples(uint32_t samples) { samples_ = samples; }
|
||||
void set_phase_offset(int8_t phase_offset) { phase_offset_ = phase_offset; }
|
||||
void set_pga_gain(CS5460APGAGain pga_gain) { pga_gain_ = pga_gain; }
|
||||
void set_gains(float current_gain, float voltage_gain) {
|
||||
current_gain_ = current_gain;
|
||||
voltage_gain_ = voltage_gain;
|
||||
}
|
||||
void set_hpf_enable(bool current_hpf, bool voltage_hpf) {
|
||||
current_hpf_ = current_hpf;
|
||||
voltage_hpf_ = voltage_hpf;
|
||||
}
|
||||
void set_pulse_energy_wh(float pulse_energy_wh) { pulse_energy_wh_ = pulse_energy_wh; }
|
||||
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
|
||||
void set_voltage_sensor(sensor::Sensor *voltage_sensor) { voltage_sensor_ = voltage_sensor; }
|
||||
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
|
||||
|
||||
void restart() { restart_(); }
|
||||
|
||||
void setup() override;
|
||||
void loop() override {}
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
uint32_t samples_;
|
||||
int8_t phase_offset_;
|
||||
CS5460APGAGain pga_gain_;
|
||||
float current_gain_;
|
||||
float voltage_gain_;
|
||||
bool current_hpf_;
|
||||
bool voltage_hpf_;
|
||||
float pulse_energy_wh_;
|
||||
sensor::Sensor *current_sensor_{nullptr};
|
||||
sensor::Sensor *voltage_sensor_{nullptr};
|
||||
sensor::Sensor *power_sensor_{nullptr};
|
||||
|
||||
void write_register_(enum CS5460ARegister addr, uint32_t value);
|
||||
uint32_t read_register_(uint8_t addr);
|
||||
bool softreset_();
|
||||
void hw_init_();
|
||||
void restart_();
|
||||
void started_();
|
||||
void schedule_next_check_();
|
||||
bool check_status_();
|
||||
|
||||
float current_multiplier_;
|
||||
float voltage_multiplier_;
|
||||
float power_multiplier_;
|
||||
float pulse_freq_;
|
||||
uint32_t expect_data_ts_;
|
||||
uint32_t prev_raw_current_{0};
|
||||
uint32_t prev_raw_energy_{0};
|
||||
};
|
||||
|
||||
template<typename... Ts> class CS5460ARestartAction : public Action<Ts...> {
|
||||
public:
|
||||
CS5460ARestartAction(CS5460AComponent *cs5460a) : cs5460a_(cs5460a) {}
|
||||
|
||||
void play(Ts... x) override { cs5460a_->restart(); }
|
||||
|
||||
protected:
|
||||
CS5460AComponent *cs5460a_;
|
||||
};
|
||||
|
||||
} // namespace cs5460a
|
||||
} // namespace esphome
|
||||
141
esphome/components/cs5460a/sensor.py
Normal file
141
esphome/components/cs5460a/sensor.py
Normal file
@@ -0,0 +1,141 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import spi, sensor
|
||||
from esphome.const import (
|
||||
CONF_CURRENT,
|
||||
CONF_ID,
|
||||
CONF_POWER,
|
||||
CONF_VOLTAGE,
|
||||
UNIT_VOLT,
|
||||
UNIT_AMPERE,
|
||||
UNIT_WATT,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
)
|
||||
from esphome import automation
|
||||
from esphome.automation import maybe_simple_id
|
||||
|
||||
CODEOWNERS = ["@balrog-kun"]
|
||||
DEPENDENCIES = ["spi"]
|
||||
|
||||
cs5460a_ns = cg.esphome_ns.namespace("cs5460a")
|
||||
CS5460APGAGain = cs5460a_ns.enum("CS5460APGAGain")
|
||||
PGA_GAIN_OPTIONS = {
|
||||
"10X": CS5460APGAGain.CS5460A_PGA_GAIN_10X,
|
||||
"50X": CS5460APGAGain.CS5460A_PGA_GAIN_50X,
|
||||
}
|
||||
|
||||
CS5460AComponent = cs5460a_ns.class_("CS5460AComponent", spi.SPIDevice, cg.Component)
|
||||
CS5460ARestartAction = cs5460a_ns.class_("CS5460ARestartAction", automation.Action)
|
||||
|
||||
CONF_SAMPLES = "samples"
|
||||
CONF_PHASE_OFFSET = "phase_offset"
|
||||
CONF_PGA_GAIN = "pga_gain"
|
||||
CONF_CURRENT_GAIN = "current_gain"
|
||||
CONF_VOLTAGE_GAIN = "voltage_gain"
|
||||
CONF_CURRENT_HPF = "current_hpf"
|
||||
CONF_VOLTAGE_HPF = "voltage_hpf"
|
||||
CONF_PULSE_ENERGY = "pulse_energy"
|
||||
|
||||
|
||||
def validate_config(config):
|
||||
current_gain = abs(config[CONF_CURRENT_GAIN]) * (
|
||||
1.0 if config[CONF_PGA_GAIN] == "10X" else 5.0
|
||||
)
|
||||
voltage_gain = config[CONF_VOLTAGE_GAIN]
|
||||
pulse_energy = config[CONF_PULSE_ENERGY]
|
||||
|
||||
if current_gain == 0.0 or voltage_gain == 0.0:
|
||||
raise cv.Invalid("The gains can't be zero")
|
||||
|
||||
max_energy = (0.25 * 0.25 / 3600 / (2 ** -4)) / (voltage_gain * current_gain)
|
||||
min_energy = (0.25 * 0.25 / 3600 / (2 ** 18)) / (voltage_gain * current_gain)
|
||||
mech_min_energy = (0.25 * 0.25 / 3600 / 7.8) / (voltage_gain * current_gain)
|
||||
if pulse_energy < min_energy or pulse_energy > max_energy:
|
||||
raise cv.Invalid(
|
||||
"For given current&voltage gains, the pulse energy must be between "
|
||||
f"{min_energy} Wh and {max_energy} Wh and in mechanical counter mode "
|
||||
f"between {mech_min_energy} Wh and {max_energy} Wh"
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
validate_energy = cv.float_with_unit("energy", "(Wh|WH|wh)?", optional_unit=True)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CS5460AComponent),
|
||||
cv.Optional(CONF_SAMPLES, default=4000): cv.int_range(min=1, max=0xFFFFFF),
|
||||
cv.Optional(CONF_PHASE_OFFSET, default=0): cv.int_range(min=-64, max=63),
|
||||
cv.Optional(CONF_PGA_GAIN, default="10X"): cv.enum(
|
||||
PGA_GAIN_OPTIONS, upper=True
|
||||
),
|
||||
cv.Optional(CONF_CURRENT_GAIN, default=0.001): cv.negative_one_to_one_float,
|
||||
cv.Optional(CONF_VOLTAGE_GAIN, default=0.001): cv.zero_to_one_float,
|
||||
cv.Optional(CONF_CURRENT_HPF, default=True): cv.boolean,
|
||||
cv.Optional(CONF_VOLTAGE_HPF, default=True): cv.boolean,
|
||||
cv.Optional(CONF_PULSE_ENERGY, default=10.0): validate_energy,
|
||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
),
|
||||
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
),
|
||||
cv.Optional(CONF_POWER): sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
accuracy_decimals=0,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(spi.spi_device_schema(cs_pin_required=False)),
|
||||
validate_config,
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
|
||||
cg.add(var.set_samples(config[CONF_SAMPLES]))
|
||||
cg.add(var.set_phase_offset(config[CONF_PHASE_OFFSET]))
|
||||
cg.add(var.set_pga_gain(config[CONF_PGA_GAIN]))
|
||||
cg.add(var.set_gains(config[CONF_CURRENT_GAIN], config[CONF_VOLTAGE_GAIN]))
|
||||
cg.add(var.set_hpf_enable(config[CONF_CURRENT_HPF], config[CONF_VOLTAGE_HPF]))
|
||||
cg.add(var.set_pulse_energy_wh(config[CONF_PULSE_ENERGY]))
|
||||
|
||||
if CONF_VOLTAGE in config:
|
||||
conf = config[CONF_VOLTAGE]
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_voltage_sensor(sens))
|
||||
if CONF_CURRENT in config:
|
||||
conf = config[CONF_CURRENT]
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_current_sensor(sens))
|
||||
if CONF_POWER in config:
|
||||
conf = config[CONF_POWER]
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_power_sensor(sens))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"cs5460a.restart",
|
||||
CS5460ARestartAction,
|
||||
maybe_simple_id(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(CS5460AComponent),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def restart_action_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
return cg.new_Pvariable(action_id, template_arg, paren)
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace cse7766 {
|
||||
|
||||
static const char *TAG = "cse7766";
|
||||
static const char *const TAG = "cse7766";
|
||||
|
||||
void CSE7766Component::loop() {
|
||||
const uint32_t now = millis();
|
||||
|
||||
@@ -9,7 +9,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_CURRENT,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_VOLT,
|
||||
UNIT_AMPERE,
|
||||
UNIT_WATT,
|
||||
@@ -27,35 +27,47 @@ CONFIG_SCHEMA = (
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CSE7766Component),
|
||||
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
|
||||
UNIT_VOLT, ICON_EMPTY, 1, DEVICE_CLASS_VOLTAGE
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
|
||||
UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_POWER): sensor.sensor_schema(
|
||||
UNIT_WATT, ICON_EMPTY, 1, DEVICE_CLASS_POWER
|
||||
unit_of_measurement=UNIT_WATT,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_POWER,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(uart.UART_DEVICE_SCHEMA)
|
||||
)
|
||||
FINAL_VALIDATE_SCHEMA = uart.final_validate_device_schema(
|
||||
"cse7766", baud_rate=4800, require_rx=True
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield uart.register_uart_device(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
|
||||
if CONF_VOLTAGE in config:
|
||||
conf = config[CONF_VOLTAGE]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_voltage_sensor(sens))
|
||||
if CONF_CURRENT in config:
|
||||
conf = config[CONF_CURRENT]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_current_sensor(sens))
|
||||
if CONF_POWER in config:
|
||||
conf = config[CONF_POWER]
|
||||
sens = yield sensor.new_sensor(conf)
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_power_sensor(sens))
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
namespace esphome {
|
||||
namespace ct_clamp {
|
||||
|
||||
static const char *TAG = "ct_clamp";
|
||||
static const char *const TAG = "ct_clamp";
|
||||
|
||||
void CTClampSensor::setup() {
|
||||
this->is_calibrating_offset_ = true;
|
||||
|
||||
@@ -5,7 +5,7 @@ from esphome.const import (
|
||||
CONF_SENSOR,
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_CURRENT,
|
||||
ICON_EMPTY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_AMPERE,
|
||||
)
|
||||
|
||||
@@ -18,7 +18,12 @@ ct_clamp_ns = cg.esphome_ns.namespace("ct_clamp")
|
||||
CTClampSensor = ct_clamp_ns.class_("CTClampSensor", sensor.Sensor, cg.PollingComponent)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
sensor.sensor_schema(UNIT_AMPERE, ICON_EMPTY, 2, DEVICE_CLASS_CURRENT)
|
||||
sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_AMPERE,
|
||||
accuracy_decimals=2,
|
||||
device_class=DEVICE_CLASS_CURRENT,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(CTClampSensor),
|
||||
@@ -32,11 +37,11 @@ CONFIG_SCHEMA = (
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
yield cg.register_component(var, config)
|
||||
yield sensor.register_sensor(var, config)
|
||||
await cg.register_component(var, config)
|
||||
await sensor.register_sensor(var, config)
|
||||
|
||||
sens = yield cg.get_variable(config[CONF_SENSOR])
|
||||
sens = await cg.get_variable(config[CONF_SENSOR])
|
||||
cg.add(var.set_source(sens))
|
||||
cg.add(var.set_sample_duration(config[CONF_SAMPLE_DURATION]))
|
||||
|
||||
@@ -17,8 +17,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
template_ = yield cg.process_lambda(
|
||||
async def to_code(config):
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA],
|
||||
[],
|
||||
return_type=cg.std_vector.template(binary_sensor.BinarySensorPtr),
|
||||
@@ -28,4 +28,4 @@ def to_code(config):
|
||||
custom = cg.variable(config[CONF_ID], rhs)
|
||||
for i, conf in enumerate(config[CONF_BINARY_SENSORS]):
|
||||
rhs = custom.Pget_binary_sensor(i)
|
||||
yield binary_sensor.register_binary_sensor(rhs, conf)
|
||||
await binary_sensor.register_binary_sensor(rhs, conf)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
namespace esphome {
|
||||
namespace custom {
|
||||
|
||||
static const char *TAG = "custom.binary_sensor";
|
||||
static const char *const TAG = "custom.binary_sensor";
|
||||
|
||||
void CustomBinarySensorConstructor::dump_config() {
|
||||
for (auto *child : this->binary_sensors_) {
|
||||
|
||||
@@ -16,8 +16,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
template_ = yield cg.process_lambda(
|
||||
async def to_code(config):
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA],
|
||||
[],
|
||||
return_type=cg.std_vector.template(climate.Climate.operator("ptr")),
|
||||
@@ -27,4 +27,4 @@ def to_code(config):
|
||||
custom = cg.variable(config[CONF_ID], rhs)
|
||||
for i, conf in enumerate(config[CONF_CLIMATES]):
|
||||
rhs = custom.Pget_climate(i)
|
||||
yield climate.register_climate(rhs, conf)
|
||||
await climate.register_climate(rhs, conf)
|
||||
|
||||
@@ -16,8 +16,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
template_ = yield cg.process_lambda(
|
||||
async def to_code(config):
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA],
|
||||
[],
|
||||
return_type=cg.std_vector.template(cover.Cover.operator("ptr")),
|
||||
@@ -27,4 +27,4 @@ def to_code(config):
|
||||
custom = cg.variable(config[CONF_ID], rhs)
|
||||
for i, conf in enumerate(config[CONF_COVERS]):
|
||||
rhs = custom.Pget_cover(i)
|
||||
yield cover.register_cover(rhs, conf)
|
||||
await cover.register_cover(rhs, conf)
|
||||
|
||||
@@ -16,8 +16,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
)
|
||||
|
||||
|
||||
def to_code(config):
|
||||
template_ = yield cg.process_lambda(
|
||||
async def to_code(config):
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA],
|
||||
[],
|
||||
return_type=cg.std_vector.template(light.LightOutput.operator("ptr")),
|
||||
@@ -27,4 +27,4 @@ def to_code(config):
|
||||
custom = cg.variable(config[CONF_ID], rhs)
|
||||
for i, conf in enumerate(config[CONF_LIGHTS]):
|
||||
rhs = custom.Pget_light(i)
|
||||
yield light.register_light(rhs, conf)
|
||||
await light.register_light(rhs, conf)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user