mirror of
https://github.com/esphome/esphome.git
synced 2025-11-17 23:35:47 +00:00
Compare commits
442 Commits
memory_api
...
memory_api
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
217e5a4833 | ||
|
|
fd3689dd64 | ||
|
|
c166e063f9 | ||
|
|
8b6400f24c | ||
|
|
bbff660499 | ||
|
|
e7409ac5cd | ||
|
|
661920c51e | ||
|
|
a6b905e148 | ||
|
|
366e95f8d8 | ||
|
|
326edd5082 | ||
|
|
d74fc6347b | ||
|
|
dbbc4f741d | ||
|
|
2d63b69ac1 | ||
|
|
a14e2d4d08 | ||
|
|
300bd420f8 | ||
|
|
2d2472c50b | ||
|
|
2e115baf56 | ||
|
|
b58b706bd6 | ||
|
|
d389ed585e | ||
|
|
1b30346c1e | ||
|
|
6b45debcba | ||
|
|
e42b29659b | ||
|
|
aba9ffccdf | ||
|
|
8e29ae416e | ||
|
|
75c220eeb6 | ||
|
|
a6b7c1f18c | ||
|
|
d2e1fbd76b | ||
|
|
3bb7639470 | ||
|
|
72a6051f0d | ||
|
|
649e27bf62 | ||
|
|
77436b85b2 | ||
|
|
5f0957c81a | ||
|
|
bee174150b | ||
|
|
262f28aec5 | ||
|
|
bf312ad9ec | ||
|
|
55cf0adb18 | ||
|
|
941feeedbe | ||
|
|
4565b126e2 | ||
|
|
64651b5a07 | ||
|
|
f3007a5245 | ||
|
|
0e62c8b3fb | ||
|
|
4160157457 | ||
|
|
75d7578491 | ||
|
|
f28566545f | ||
|
|
dc37321aa9 | ||
|
|
89abd9c817 | ||
|
|
d4d44a5c08 | ||
|
|
b8e4efc1cd | ||
|
|
cf66c4cd3e | ||
|
|
4b60012814 | ||
|
|
c38df0af85 | ||
|
|
bb51c6b6d5 | ||
|
|
b8f972b6f6 | ||
|
|
d87063865c | ||
|
|
682b6711f3 | ||
|
|
066674df19 | ||
|
|
48a33611a1 | ||
|
|
caf6045485 | ||
|
|
bd7d103813 | ||
|
|
6631e2ffb2 | ||
|
|
b80b0eb864 | ||
|
|
7a700ca077 | ||
|
|
130a8b853d | ||
|
|
0f02c75f66 | ||
|
|
e4c3ae4b16 | ||
|
|
677f65c38c | ||
|
|
80e4eefc4c | ||
|
|
0be86aa946 | ||
|
|
1539b43074 | ||
|
|
5dc914268c | ||
|
|
126a9ef303 | ||
|
|
463a00b1ac | ||
|
|
f9ef8af18e | ||
|
|
82692d7053 | ||
|
|
1cccfdd2b9 | ||
|
|
855aa32f54 | ||
|
|
e263b3194e | ||
|
|
bf18751136 | ||
|
|
a7674cd0e8 | ||
|
|
0f8332fe3c | ||
|
|
40e2976ba2 | ||
|
|
4f411dc4f2 | ||
|
|
4964fdc1b0 | ||
|
|
f275a31c3a | ||
|
|
d7cef22ddb | ||
|
|
23b8139d24 | ||
|
|
3fd5e87379 | ||
|
|
e46300828e | ||
|
|
8d284ea90c | ||
|
|
8c5b964722 | ||
|
|
43eafbccb3 | ||
|
|
5a67d2b20b | ||
|
|
f84cdad93c | ||
|
|
f32b69b8f1 | ||
|
|
2a16653642 | ||
|
|
16075e37f5 | ||
|
|
848ff22871 | ||
|
|
057aede0cd | ||
|
|
4258e47c68 | ||
|
|
f19bbbd1c5 | ||
|
|
0f136a888c | ||
|
|
6feaa8dd13 | ||
|
|
4c3931363f | ||
|
|
b47e89a7d5 | ||
|
|
c17a31a8f8 | ||
|
|
fbbdad75f6 | ||
|
|
7abb6d4998 | ||
|
|
1dabe83d04 | ||
|
|
0d735dc259 | ||
|
|
7b86e1feb0 | ||
|
|
d516627957 | ||
|
|
fb1c67490a | ||
|
|
8b9600b930 | ||
|
|
cbb98c4050 | ||
|
|
e7ff56f1cd | ||
|
|
7705a5de06 | ||
|
|
77ab096b59 | ||
|
|
26a3ec41d6 | ||
|
|
3bcbfe8d97 | ||
|
|
870b2c4f84 | ||
|
|
5f9c7a70ff | ||
|
|
f7179d4255 | ||
|
|
eb0558ca3f | ||
|
|
5585355263 | ||
|
|
e468ca4881 | ||
|
|
4c078dea2c | ||
|
|
783dbd1e6b | ||
|
|
b49619d9bf | ||
|
|
a290b88cd6 | ||
|
|
ff329a1476 | ||
|
|
a2e237e080 | ||
|
|
bde4937192 | ||
|
|
40f0be2d0f | ||
|
|
ff04a6da4b | ||
|
|
cb4d10442f | ||
|
|
9abef44ac0 | ||
|
|
8cc89ea7ab | ||
|
|
c853a5bc2b | ||
|
|
62484d7e1f | ||
|
|
1a997cbe2f | ||
|
|
123bc17a66 | ||
|
|
7a3cf0209f | ||
|
|
5fb62325b1 | ||
|
|
30c578ac16 | ||
|
|
c16cd3bab5 | ||
|
|
88bbea7566 | ||
|
|
bdaeb2cf2e | ||
|
|
21d0c8b549 | ||
|
|
845fae7716 | ||
|
|
a5c9988c5d | ||
|
|
729304af01 | ||
|
|
db8b96f257 | ||
|
|
f0062117c4 | ||
|
|
0341e4baba | ||
|
|
59485c1d2b | ||
|
|
2e5dc57ce4 | ||
|
|
353ea5674d | ||
|
|
5b8827d47a | ||
|
|
0709c21b8c | ||
|
|
c66142e5d6 | ||
|
|
f10d46252e | ||
|
|
0cbfd16e88 | ||
|
|
f00d3d0cae | ||
|
|
5c7369788b | ||
|
|
d728a42416 | ||
|
|
d1089d26e9 | ||
|
|
efc49d05ad | ||
|
|
772340cdc3 | ||
|
|
d29882e4ad | ||
|
|
a239460724 | ||
|
|
a89ffda69f | ||
|
|
99c60bfa42 | ||
|
|
ecf7de7743 | ||
|
|
68f9ce9b47 | ||
|
|
5310512123 | ||
|
|
2cac99dafa | ||
|
|
2c835ffb79 | ||
|
|
a67a433627 | ||
|
|
ba82d968eb | ||
|
|
716e641fc4 | ||
|
|
a6feea5415 | ||
|
|
17df008092 | ||
|
|
89e88f77f2 | ||
|
|
93a57831f4 | ||
|
|
dad5a88ecf | ||
|
|
a9837c90ba | ||
|
|
4b143e1f3d | ||
|
|
d9503344e3 | ||
|
|
c101e22041 | ||
|
|
0d4a6fa350 | ||
|
|
0ab8ce2bdc | ||
|
|
f4a7f40b8e | ||
|
|
b380a70aa8 | ||
|
|
c5014321a6 | ||
|
|
f6bf6bd8ee | ||
|
|
c8d9232bd3 | ||
|
|
6632daba2d | ||
|
|
041ff7c113 | ||
|
|
e7e091b48c | ||
|
|
b61027607f | ||
|
|
f55c872180 | ||
|
|
fac05dab35 | ||
|
|
f6100a55bc | ||
|
|
c2abf363b6 | ||
|
|
62f43d3353 | ||
|
|
7e96f10a79 | ||
|
|
9a2fc8aa51 | ||
|
|
6b9cb4289a | ||
|
|
b264c6caac | ||
|
|
a6c669ff51 | ||
|
|
c15290e386 | ||
|
|
1329d1af88 | ||
|
|
f4eca3872d | ||
|
|
e3fb074a60 | ||
|
|
6e7f66d393 | ||
|
|
ac85949f17 | ||
|
|
0962024d99 | ||
|
|
327543303c | ||
|
|
8229e3a471 | ||
|
|
1b6471f4b0 | ||
|
|
c87d07ba70 | ||
|
|
fc8dc33023 | ||
|
|
c0e4f415f1 | ||
|
|
871c5ddb4e | ||
|
|
6ef2763cab | ||
|
|
929279dc23 | ||
|
|
6fa0f1e290 | ||
|
|
51eb8ea1d0 | ||
|
|
cbdd663fbf | ||
|
|
c77bb3b269 | ||
|
|
f1009a7468 | ||
|
|
295fe8da04 | ||
|
|
0bf2dff056 | ||
|
|
1b218fc155 | ||
|
|
f0bcea7749 | ||
|
|
79d1a558af | ||
|
|
9b3f9ee70d | ||
|
|
9158b1e6d6 | ||
|
|
e2d949c287 | ||
|
|
51a238f3d2 | ||
|
|
f4fea1a00f | ||
|
|
a823fd322e | ||
|
|
499ffd84a7 | ||
|
|
fca80d81c8 | ||
|
|
dc3c18974e | ||
|
|
a5bf55b6ac | ||
|
|
85d2565f25 | ||
|
|
4f08f0750a | ||
|
|
3c41e080c5 | ||
|
|
7c30d57391 | ||
|
|
182e106bfa | ||
|
|
d0b399d771 | ||
|
|
5d20e3a3b4 | ||
|
|
ba5fa7c10a | ||
|
|
5cdb891b58 | ||
|
|
cf8b88b335 | ||
|
|
43cd4a6b93 | ||
|
|
448e6432ea | ||
|
|
f3c9ab7cb4 | ||
|
|
d46d6f08bd | ||
|
|
d6528f906e | ||
|
|
d96e8a9c4b | ||
|
|
282f6e04b3 | ||
|
|
148cbc03db | ||
|
|
c6da4e4777 | ||
|
|
81bc2d82d6 | ||
|
|
0893de4f29 | ||
|
|
3b9570d916 | ||
|
|
b4b24c500c | ||
|
|
0a741007bf | ||
|
|
bf52b9fe06 | ||
|
|
6e685f1b2d | ||
|
|
400a18fddc | ||
|
|
90feecb7bf | ||
|
|
2e1fd30ea0 | ||
|
|
03c5655201 | ||
|
|
df35036c8d | ||
|
|
936a6cb71e | ||
|
|
df1ffbaf5d | ||
|
|
645820304f | ||
|
|
f4d2b000da | ||
|
|
a0b273c6f3 | ||
|
|
cde767d83d | ||
|
|
8a92791887 | ||
|
|
7041c3324b | ||
|
|
7d48df9fe1 | ||
|
|
541e0cfde8 | ||
|
|
38cf003bf3 | ||
|
|
47874ef516 | ||
|
|
8eb509f8f0 | ||
|
|
db0b1e0b5c | ||
|
|
0eafe5259f | ||
|
|
b366bc8dba | ||
|
|
b74f415509 | ||
|
|
0044c51474 | ||
|
|
1fb233e22f | ||
|
|
e4a56c6bc9 | ||
|
|
ebda7dace0 | ||
|
|
d8b419b60c | ||
|
|
190668c25f | ||
|
|
ef680933dc | ||
|
|
4439b45fba | ||
|
|
703b1cf314 | ||
|
|
047773e62f | ||
|
|
57a88e8211 | ||
|
|
6d958a6640 | ||
|
|
37620e61f9 | ||
|
|
d38703c18a | ||
|
|
5543acf3ab | ||
|
|
25ef0043d2 | ||
|
|
60d6144574 | ||
|
|
2c110a9e7e | ||
|
|
083f41c43f | ||
|
|
27fb72a1d3 | ||
|
|
03fd2eef2f | ||
|
|
bfca9cb6c2 | ||
|
|
7d4b3ff3a6 | ||
|
|
e7e2df5c6d | ||
|
|
670d85090c | ||
|
|
4500006aab | ||
|
|
34317ab343 | ||
|
|
13ee597ce0 | ||
|
|
378e591e70 | ||
|
|
20f2d409f7 | ||
|
|
398b5337c2 | ||
|
|
a5e1136eda | ||
|
|
9168d5e422 | ||
|
|
d80822573a | ||
|
|
1e58c400ea | ||
|
|
e17b69c20d | ||
|
|
fbc3413ed9 | ||
|
|
754eaab3be | ||
|
|
ffd5b12324 | ||
|
|
2ddfabe09e | ||
|
|
26607713bb | ||
|
|
f0e52227f2 | ||
|
|
6c09b16b38 | ||
|
|
ef19d7bb24 | ||
|
|
39b63ae87e | ||
|
|
70d947fab9 | ||
|
|
895d76ca03 | ||
|
|
74187845b7 | ||
|
|
822eacfd77 | ||
|
|
ab5d8f67ae | ||
|
|
83f30a64ed | ||
|
|
5eea7bdb44 | ||
|
|
bdfd88441a | ||
|
|
afe4ef17b7 | ||
|
|
dc3f4007df | ||
|
|
8fded918b7 | ||
|
|
784dc358f0 | ||
|
|
99f5018dd2 | ||
|
|
ce4f9db778 | ||
|
|
b3fdef3ac4 | ||
|
|
d7c0ea22c4 | ||
|
|
15c167b5ce | ||
|
|
ab6cb2dee6 | ||
|
|
bd0705cdc0 | ||
|
|
4810c36141 | ||
|
|
351262aea9 | ||
|
|
89bd9d8d91 | ||
|
|
aaee3f2899 | ||
|
|
20b6e0d5c2 | ||
|
|
f8aee13a3a | ||
|
|
c83e5e076b | ||
|
|
e331056500 | ||
|
|
ce5e608863 | ||
|
|
aa5795c019 | ||
|
|
00c0854323 | ||
|
|
6d390d5b88 | ||
|
|
8514fbcf71 | ||
|
|
be006ecadd | ||
|
|
b08419fa47 | ||
|
|
0d2cdc5ce5 | ||
|
|
e8c7f74abd | ||
|
|
d25ff00af4 | ||
|
|
1fce2918fb | ||
|
|
d36ef050a9 | ||
|
|
6e4b99db73 | ||
|
|
c9feb5cf65 | ||
|
|
90e4d15fd9 | ||
|
|
df53ff7afe | ||
|
|
ab128fe84a | ||
|
|
6ccea58ee2 | ||
|
|
7f4f4033ee | ||
|
|
2352114757 | ||
|
|
cf209e3694 | ||
|
|
ee229bd8f2 | ||
|
|
8c1bebb3f4 | ||
|
|
d056db42fa | ||
|
|
2accba4e0c | ||
|
|
ed0d9e60b8 | ||
|
|
4c5533b2ea | ||
|
|
4c097616ae | ||
|
|
5dc8bfcf13 | ||
|
|
a78767c714 | ||
|
|
af61fe3ac3 | ||
|
|
14eadb3ccd | ||
|
|
d663ea56b0 | ||
|
|
b7838671ae | ||
|
|
2c9fdb33e6 | ||
|
|
5372eca46e | ||
|
|
e648c0315e | ||
|
|
c29dbcca5f | ||
|
|
479f8dd85c | ||
|
|
6e2dbbf636 | ||
|
|
6b522dfee6 | ||
|
|
3a947a205a | ||
|
|
558bee8a09 | ||
|
|
d77f63eff5 | ||
|
|
d7ea53a44f | ||
|
|
c70d154276 | ||
|
|
358296a57e | ||
|
|
6ba0d6b54b | ||
|
|
e0831abcd3 | ||
|
|
34208138c1 | ||
|
|
bbe9169975 | ||
|
|
5855f3ce33 | ||
|
|
f420a8f32d | ||
|
|
f262c671a8 | ||
|
|
a0755829bf | ||
|
|
009d6a15f6 | ||
|
|
28eb79b17b | ||
|
|
209091e6a4 | ||
|
|
bf83b70a18 | ||
|
|
c588d52bec | ||
|
|
d70fe126f6 | ||
|
|
829455ac43 | ||
|
|
0e3f2d3302 | ||
|
|
32975c9d8b | ||
|
|
1446e7174a | ||
|
|
64f8963566 | ||
|
|
6f7e54c3f3 | ||
|
|
c7ae424613 | ||
|
|
c5e5609e92 | ||
|
|
885508775f | ||
|
|
531b27582a | ||
|
|
aed7505f53 | ||
|
|
191a88c2dc | ||
|
|
968df6cb3f | ||
|
|
71fa88c9d4 | ||
|
|
84f7cacef9 |
@@ -51,7 +51,79 @@ This document provides essential context for AI models interacting with this pro
|
||||
|
||||
* **Naming Conventions:**
|
||||
* **Python:** Follows PEP 8. Use clear, descriptive names following snake_case.
|
||||
* **C++:** Follows the Google C++ Style Guide.
|
||||
* **C++:** Follows the Google C++ Style Guide with these specifics (following clang-tidy conventions):
|
||||
- Function, method, and variable names: `lower_snake_case`
|
||||
- Class/struct/enum names: `UpperCamelCase`
|
||||
- Top-level constants (global/namespace scope): `UPPER_SNAKE_CASE`
|
||||
- Function-local constants: `lower_snake_case`
|
||||
- Protected/private fields: `lower_snake_case_with_trailing_underscore_`
|
||||
- Favor descriptive names over abbreviations
|
||||
|
||||
* **C++ Field Visibility:**
|
||||
* **Prefer `protected`:** Use `protected` for most class fields to enable extensibility and testing. Fields should be `lower_snake_case_with_trailing_underscore_`.
|
||||
* **Use `private` for safety-critical cases:** Use `private` visibility when direct field access could introduce bugs or violate invariants:
|
||||
1. **Pointer lifetime issues:** When setters validate and store pointers from known lists to prevent dangling references.
|
||||
```cpp
|
||||
// Helper to find matching string in vector and return its pointer
|
||||
inline const char *vector_find(const std::vector<const char *> &vec, const char *value) {
|
||||
for (const char *item : vec) {
|
||||
if (strcmp(item, value) == 0)
|
||||
return item;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
class ClimateDevice {
|
||||
public:
|
||||
void set_custom_fan_modes(std::initializer_list<const char *> modes) {
|
||||
this->custom_fan_modes_ = modes;
|
||||
this->active_custom_fan_mode_ = nullptr; // Reset when modes change
|
||||
}
|
||||
bool set_custom_fan_mode(const char *mode) {
|
||||
// Find mode in supported list and store that pointer (not the input pointer)
|
||||
const char *validated_mode = vector_find(this->custom_fan_modes_, mode);
|
||||
if (validated_mode != nullptr) {
|
||||
this->active_custom_fan_mode_ = validated_mode;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
std::vector<const char *> custom_fan_modes_; // Pointers to string literals in flash
|
||||
const char *active_custom_fan_mode_{nullptr}; // Must point to entry in custom_fan_modes_
|
||||
};
|
||||
```
|
||||
2. **Invariant coupling:** When multiple fields must remain synchronized to prevent buffer overflows or data corruption.
|
||||
```cpp
|
||||
class Buffer {
|
||||
public:
|
||||
void resize(size_t new_size) {
|
||||
auto new_data = std::make_unique<uint8_t[]>(new_size);
|
||||
if (this->data_) {
|
||||
std::memcpy(new_data.get(), this->data_.get(), std::min(this->size_, new_size));
|
||||
}
|
||||
this->data_ = std::move(new_data);
|
||||
this->size_ = new_size; // Must stay in sync with data_
|
||||
}
|
||||
private:
|
||||
std::unique_ptr<uint8_t[]> data_;
|
||||
size_t size_{0}; // Must match allocated size of data_
|
||||
};
|
||||
```
|
||||
3. **Resource management:** When setters perform cleanup or registration operations that derived classes might skip.
|
||||
* **Provide `protected` accessor methods:** When derived classes need controlled access to `private` members.
|
||||
|
||||
* **C++ Preprocessor Directives:**
|
||||
* **Avoid `#define` for constants:** Using `#define` for constants is discouraged and should be replaced with `const` variables or enums.
|
||||
* **Use `#define` only for:**
|
||||
- Conditional compilation (`#ifdef`, `#ifndef`)
|
||||
- Compile-time sizes calculated during Python code generation (e.g., configuring `std::array` or `StaticVector` dimensions via `cg.add_define()`)
|
||||
|
||||
* **C++ Additional Conventions:**
|
||||
* **Member access:** Prefix all class member access with `this->` (e.g., `this->value_` not `value_`)
|
||||
* **Indentation:** Use spaces (two per indentation level), not tabs
|
||||
* **Type aliases:** Prefer `using type_t = int;` over `typedef int type_t;`
|
||||
* **Line length:** Wrap lines at no more than 120 characters
|
||||
|
||||
* **Component Structure:**
|
||||
* **Standard Files:**
|
||||
@@ -100,8 +172,7 @@ This document provides essential context for AI models interacting with this pro
|
||||
|
||||
* **C++ Class Pattern:**
|
||||
```cpp
|
||||
namespace esphome {
|
||||
namespace my_component {
|
||||
namespace esphome::my_component {
|
||||
|
||||
class MyComponent : public Component {
|
||||
public:
|
||||
@@ -117,8 +188,7 @@ This document provides essential context for AI models interacting with this pro
|
||||
int param_{0};
|
||||
};
|
||||
|
||||
} // namespace my_component
|
||||
} // namespace esphome
|
||||
} // namespace esphome::my_component
|
||||
```
|
||||
|
||||
* **Common Component Examples:**
|
||||
@@ -368,3 +438,45 @@ This document provides essential context for AI models interacting with this pro
|
||||
* **Python:** When adding a new Python dependency, add it to the appropriate `requirements*.txt` file and `pyproject.toml`.
|
||||
* **C++ / PlatformIO:** When adding a new C++ dependency, add it to `platformio.ini` and use `cg.add_library`.
|
||||
* **Build Flags:** Use `cg.add_build_flag(...)` to add compiler flags.
|
||||
|
||||
## 8. Public API and Breaking Changes
|
||||
|
||||
* **Public C++ API:**
|
||||
* **Components**: Only documented features at [esphome.io](https://esphome.io) are public API. Undocumented `public` members are internal.
|
||||
* **Core/Base Classes** (`esphome/core/`, `Component`, `Sensor`, etc.): All `public` members are public API.
|
||||
* **Components with Global Accessors** (`global_api_server`, etc.): All `public` members are public API (except config setters).
|
||||
|
||||
* **Public Python API:**
|
||||
* All documented configuration options at [esphome.io](https://esphome.io) are public API.
|
||||
* Python code in `esphome/core/` actively used by existing core components is considered stable API.
|
||||
* Other Python code is internal unless explicitly documented for external component use.
|
||||
|
||||
* **Breaking Changes Policy:**
|
||||
* Aim for **6-month deprecation window** when possible
|
||||
* Clean breaks allowed for: signature changes, deep refactorings, resource constraints
|
||||
* Must document migration path in PR description (generates release notes)
|
||||
* Blog post required for core/base class changes or significant architectural changes
|
||||
* Full details: https://developers.esphome.io/contributing/code/#public-api-and-breaking-changes
|
||||
|
||||
* **Breaking Change Checklist:**
|
||||
- [ ] Clear justification (RAM/flash savings, architectural improvement)
|
||||
- [ ] Explored non-breaking alternatives
|
||||
- [ ] Added deprecation warnings if possible (use `ESPDEPRECATED` macro for C++)
|
||||
- [ ] Documented migration path in PR description with before/after examples
|
||||
- [ ] Updated all internal usage and esphome-docs
|
||||
- [ ] Tested backward compatibility during deprecation period
|
||||
|
||||
* **Deprecation Pattern (C++):**
|
||||
```cpp
|
||||
// Remove before 2026.6.0
|
||||
ESPDEPRECATED("Use new_method() instead. Removed in 2026.6.0", "2025.12.0")
|
||||
void old_method() { this->new_method(); }
|
||||
```
|
||||
|
||||
* **Deprecation Pattern (Python):**
|
||||
```python
|
||||
# Remove before 2026.6.0
|
||||
if CONF_OLD_KEY in config:
|
||||
_LOGGER.warning(f"'{CONF_OLD_KEY}' deprecated, use '{CONF_NEW_KEY}'. Removed in 2026.6.0")
|
||||
config[CONF_NEW_KEY] = config.pop(CONF_OLD_KEY) # Auto-migrate
|
||||
```
|
||||
|
||||
@@ -21,7 +21,7 @@ permissions:
|
||||
jobs:
|
||||
request-codeowner-reviews:
|
||||
name: Run
|
||||
if: ${{ !github.event.pull_request.draft }}
|
||||
if: ${{ github.repository == 'esphome/esphome' && !github.event.pull_request.draft }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Request reviews from component codeowners
|
||||
|
||||
@@ -11,7 +11,7 @@ ci:
|
||||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: v0.14.3
|
||||
rev: v0.14.4
|
||||
hooks:
|
||||
# Run the linter.
|
||||
- id: ruff
|
||||
|
||||
@@ -181,7 +181,7 @@ esphome/components/gdk101/* @Szewcson
|
||||
esphome/components/gl_r01_i2c/* @pkejval
|
||||
esphome/components/globals/* @esphome/core
|
||||
esphome/components/gp2y1010au0f/* @zry98
|
||||
esphome/components/gp8403/* @jesserockz
|
||||
esphome/components/gp8403/* @jesserockz @sebydocky
|
||||
esphome/components/gpio/* @esphome/core
|
||||
esphome/components/gpio/one_wire/* @ssieb
|
||||
esphome/components/gps/* @coogle @ximex
|
||||
@@ -206,6 +206,7 @@ esphome/components/hdc2010/* @optimusprimespace @ssieb
|
||||
esphome/components/he60r/* @clydebarrow
|
||||
esphome/components/heatpumpir/* @rob-deutsch
|
||||
esphome/components/hitachi_ac424/* @sourabhjaiswal
|
||||
esphome/components/hlk_fm22x/* @OnFreund
|
||||
esphome/components/hm3301/* @freekode
|
||||
esphome/components/hmac_md5/* @dwmw2
|
||||
esphome/components/homeassistant/* @esphome/core @OttoWinter
|
||||
@@ -290,6 +291,7 @@ esphome/components/mcp23x17_base/* @jesserockz
|
||||
esphome/components/mcp23xxx_base/* @jesserockz
|
||||
esphome/components/mcp2515/* @danielschramm @mvturnho
|
||||
esphome/components/mcp3204/* @rsumner
|
||||
esphome/components/mcp3221/* @philippderdiedas
|
||||
esphome/components/mcp4461/* @p1ngb4ck
|
||||
esphome/components/mcp4728/* @berfenger
|
||||
esphome/components/mcp47a1/* @jesserockz
|
||||
@@ -394,6 +396,7 @@ esphome/components/rpi_dpi_rgb/* @clydebarrow
|
||||
esphome/components/rtl87xx/* @kuba2k2
|
||||
esphome/components/rtttl/* @glmnet
|
||||
esphome/components/runtime_stats/* @bdraco
|
||||
esphome/components/rx8130/* @beormund
|
||||
esphome/components/safe_mode/* @jsuanet @kbx81 @paulmonigatti
|
||||
esphome/components/scd4x/* @martgras @sjtrny
|
||||
esphome/components/script/* @esphome/core
|
||||
|
||||
@@ -105,7 +105,7 @@ template<typename... Ts> class AGS10NewI2cAddressAction : public Action<Ts...>,
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, new_address)
|
||||
|
||||
void play(Ts... x) override { this->parent_->new_i2c_address(this->new_address_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->parent_->new_i2c_address(this->new_address_.value(x...)); }
|
||||
};
|
||||
|
||||
enum AGS10SetZeroPointActionMode {
|
||||
@@ -122,7 +122,7 @@ template<typename... Ts> class AGS10SetZeroPointAction : public Action<Ts...>, p
|
||||
TEMPLATABLE_VALUE(uint16_t, value)
|
||||
TEMPLATABLE_VALUE(AGS10SetZeroPointActionMode, mode)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
switch (this->mode_.value(x...)) {
|
||||
case FACTORY_DEFAULT:
|
||||
this->parent_->set_zero_point_with_factory_defaults();
|
||||
|
||||
@@ -13,7 +13,7 @@ template<typename... Ts> class SetAutoMuteAction : public Action<Ts...> {
|
||||
|
||||
TEMPLATABLE_VALUE(uint8_t, auto_mute_mode)
|
||||
|
||||
void play(Ts... x) override { this->aic3204_->set_auto_mute_mode(this->auto_mute_mode_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->aic3204_->set_auto_mute_mode(this->auto_mute_mode_.value(x...)); }
|
||||
|
||||
protected:
|
||||
AIC3204 *aic3204_;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <utility>
|
||||
|
||||
#include "alarm_control_panel.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
@@ -34,6 +36,9 @@ void AlarmControlPanel::publish_state(AlarmControlPanelState state) {
|
||||
LOG_STR_ARG(alarm_control_panel_state_to_string(prev_state)));
|
||||
this->current_state_ = state;
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_ALARM_CONTROL_PANEL) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_alarm_control_panel_update(this);
|
||||
#endif
|
||||
if (state == ACP_STATE_TRIGGERED) {
|
||||
this->triggered_callback_.call();
|
||||
} else if (state == ACP_STATE_ARMING) {
|
||||
|
||||
@@ -89,7 +89,7 @@ template<typename... Ts> class ArmAwayAction : public Action<Ts...> {
|
||||
|
||||
TEMPLATABLE_VALUE(std::string, code)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto call = this->alarm_control_panel_->make_call();
|
||||
auto code = this->code_.optional_value(x...);
|
||||
if (code.has_value()) {
|
||||
@@ -109,7 +109,7 @@ template<typename... Ts> class ArmHomeAction : public Action<Ts...> {
|
||||
|
||||
TEMPLATABLE_VALUE(std::string, code)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto call = this->alarm_control_panel_->make_call();
|
||||
auto code = this->code_.optional_value(x...);
|
||||
if (code.has_value()) {
|
||||
@@ -129,7 +129,7 @@ template<typename... Ts> class ArmNightAction : public Action<Ts...> {
|
||||
|
||||
TEMPLATABLE_VALUE(std::string, code)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto call = this->alarm_control_panel_->make_call();
|
||||
auto code = this->code_.optional_value(x...);
|
||||
if (code.has_value()) {
|
||||
@@ -149,7 +149,7 @@ template<typename... Ts> class DisarmAction : public Action<Ts...> {
|
||||
|
||||
TEMPLATABLE_VALUE(std::string, code)
|
||||
|
||||
void play(Ts... x) override { this->alarm_control_panel_->disarm(this->code_.optional_value(x...)); }
|
||||
void play(const Ts &...x) override { this->alarm_control_panel_->disarm(this->code_.optional_value(x...)); }
|
||||
|
||||
protected:
|
||||
AlarmControlPanel *alarm_control_panel_;
|
||||
@@ -159,7 +159,7 @@ template<typename... Ts> class PendingAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit PendingAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
|
||||
|
||||
void play(Ts... x) override { this->alarm_control_panel_->make_call().pending().perform(); }
|
||||
void play(const Ts &...x) override { this->alarm_control_panel_->make_call().pending().perform(); }
|
||||
|
||||
protected:
|
||||
AlarmControlPanel *alarm_control_panel_;
|
||||
@@ -169,7 +169,7 @@ template<typename... Ts> class TriggeredAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit TriggeredAction(AlarmControlPanel *alarm_control_panel) : alarm_control_panel_(alarm_control_panel) {}
|
||||
|
||||
void play(Ts... x) override { this->alarm_control_panel_->make_call().triggered().perform(); }
|
||||
void play(const Ts &...x) override { this->alarm_control_panel_->make_call().triggered().perform(); }
|
||||
|
||||
protected:
|
||||
AlarmControlPanel *alarm_control_panel_;
|
||||
@@ -178,7 +178,7 @@ template<typename... Ts> class TriggeredAction : public Action<Ts...> {
|
||||
template<typename... Ts> class AlarmControlPanelCondition : public Condition<Ts...> {
|
||||
public:
|
||||
AlarmControlPanelCondition(AlarmControlPanel *parent) : parent_(parent) {}
|
||||
bool check(Ts... x) override {
|
||||
bool check(const Ts &...x) override {
|
||||
return this->parent_->is_state_armed(this->parent_->get_state()) ||
|
||||
this->parent_->get_state() == ACP_STATE_PENDING || this->parent_->get_state() == ACP_STATE_TRIGGERED;
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ class Animation : public image::Image {
|
||||
template<typename... Ts> class AnimationNextFrameAction : public Action<Ts...> {
|
||||
public:
|
||||
AnimationNextFrameAction(Animation *parent) : parent_(parent) {}
|
||||
void play(Ts... x) override { this->parent_->next_frame(); }
|
||||
void play(const Ts &...x) override { this->parent_->next_frame(); }
|
||||
|
||||
protected:
|
||||
Animation *parent_;
|
||||
@@ -48,7 +48,7 @@ template<typename... Ts> class AnimationNextFrameAction : public Action<Ts...> {
|
||||
template<typename... Ts> class AnimationPrevFrameAction : public Action<Ts...> {
|
||||
public:
|
||||
AnimationPrevFrameAction(Animation *parent) : parent_(parent) {}
|
||||
void play(Ts... x) override { this->parent_->prev_frame(); }
|
||||
void play(const Ts &...x) override { this->parent_->prev_frame(); }
|
||||
|
||||
protected:
|
||||
Animation *parent_;
|
||||
@@ -58,7 +58,7 @@ template<typename... Ts> class AnimationSetFrameAction : public Action<Ts...> {
|
||||
public:
|
||||
AnimationSetFrameAction(Animation *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(uint16_t, frame)
|
||||
void play(Ts... x) override { this->parent_->set_frame(this->frame_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->parent_->set_frame(this->frame_.value(x...)); }
|
||||
|
||||
protected:
|
||||
Animation *parent_;
|
||||
|
||||
@@ -227,6 +227,7 @@ CONFIG_SCHEMA = cv.All(
|
||||
esp32=8, # More RAM, can buffer more
|
||||
rp2040=5, # Limited RAM
|
||||
bk72xx=8, # Moderate RAM
|
||||
nrf52=8, # Moderate RAM
|
||||
rtl87xx=8, # Moderate RAM
|
||||
host=16, # Abundant resources
|
||||
ln882x=8, # Moderate RAM
|
||||
@@ -244,6 +245,9 @@ async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
|
||||
# Track controller registration for StaticVector sizing
|
||||
CORE.register_controller()
|
||||
|
||||
cg.add(var.set_port(config[CONF_PORT]))
|
||||
if config[CONF_PASSWORD]:
|
||||
cg.add_define("USE_API_PASSWORD")
|
||||
|
||||
@@ -2147,7 +2147,7 @@ message ListEntitiesEventResponse {
|
||||
EntityCategory entity_category = 7;
|
||||
string device_class = 8;
|
||||
|
||||
repeated string event_types = 9;
|
||||
repeated string event_types = 9 [(container_pointer_no_template) = "FixedVector<const char *>"];
|
||||
uint32 device_id = 10 [(field_ifdef) = "USE_DEVICES"];
|
||||
}
|
||||
message EventResponse {
|
||||
|
||||
@@ -1294,11 +1294,11 @@ void APIConnection::alarm_control_panel_command(const AlarmControlPanelCommandRe
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void APIConnection::send_event(event::Event *event, const std::string &event_type) {
|
||||
void APIConnection::send_event(event::Event *event, const char *event_type) {
|
||||
this->schedule_message_(event, MessageCreator(event_type), EventResponse::MESSAGE_TYPE,
|
||||
EventResponse::ESTIMATED_SIZE);
|
||||
}
|
||||
uint16_t APIConnection::try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
|
||||
uint16_t APIConnection::try_send_event_response(event::Event *event, const char *event_type, APIConnection *conn,
|
||||
uint32_t remaining_size, bool is_single) {
|
||||
EventResponse resp;
|
||||
resp.set_event_type(StringRef(event_type));
|
||||
@@ -1310,8 +1310,7 @@ uint16_t APIConnection::try_send_event_info(EntityBase *entity, APIConnection *c
|
||||
auto *event = static_cast<event::Event *>(entity);
|
||||
ListEntitiesEventResponse msg;
|
||||
msg.set_device_class(event->get_device_class_ref());
|
||||
for (const auto &event_type : event->get_event_types())
|
||||
msg.event_types.push_back(event_type);
|
||||
msg.event_types = &event->get_event_types();
|
||||
return fill_and_encode_entity_info(event, msg, ListEntitiesEventResponse::MESSAGE_TYPE, conn, remaining_size,
|
||||
is_single);
|
||||
}
|
||||
@@ -1468,6 +1467,8 @@ bool APIConnection::send_device_info_response(const DeviceInfoRequest &msg) {
|
||||
static constexpr auto MANUFACTURER = StringRef::from_lit("Beken");
|
||||
#elif defined(USE_LN882X)
|
||||
static constexpr auto MANUFACTURER = StringRef::from_lit("Lightning");
|
||||
#elif defined(USE_NRF52)
|
||||
static constexpr auto MANUFACTURER = StringRef::from_lit("Nordic Semiconductor");
|
||||
#elif defined(USE_RTL87XX)
|
||||
static constexpr auto MANUFACTURER = StringRef::from_lit("Realtek");
|
||||
#elif defined(USE_HOST)
|
||||
@@ -1649,9 +1650,7 @@ void APIConnection::DeferredBatch::add_item(EntityBase *entity, MessageCreator c
|
||||
// O(n) but optimized for RAM and not performance.
|
||||
for (auto &item : items) {
|
||||
if (item.entity == entity && item.message_type == message_type) {
|
||||
// Clean up old creator before replacing
|
||||
item.creator.cleanup(message_type);
|
||||
// Move assign the new creator
|
||||
// Replace with new creator
|
||||
item.creator = std::move(creator);
|
||||
return;
|
||||
}
|
||||
@@ -1821,7 +1820,7 @@ void APIConnection::process_batch_() {
|
||||
|
||||
// Handle remaining items more efficiently
|
||||
if (items_processed < this->deferred_batch_.size()) {
|
||||
// Remove processed items from the beginning with proper cleanup
|
||||
// Remove processed items from the beginning
|
||||
this->deferred_batch_.remove_front(items_processed);
|
||||
// Reschedule for remaining items
|
||||
this->schedule_batch_();
|
||||
@@ -1834,10 +1833,10 @@ void APIConnection::process_batch_() {
|
||||
uint16_t APIConnection::MessageCreator::operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size,
|
||||
bool is_single, uint8_t message_type) const {
|
||||
#ifdef USE_EVENT
|
||||
// Special case: EventResponse uses string pointer
|
||||
// Special case: EventResponse uses const char * pointer
|
||||
if (message_type == EventResponse::MESSAGE_TYPE) {
|
||||
auto *e = static_cast<event::Event *>(entity);
|
||||
return APIConnection::try_send_event_response(e, *data_.string_ptr, conn, remaining_size, is_single);
|
||||
return APIConnection::try_send_event_response(e, data_.const_char_ptr, conn, remaining_size, is_single);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ class APIConnection final : public APIServerConnection {
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
void send_event(event::Event *event, const std::string &event_type);
|
||||
void send_event(event::Event *event, const char *event_type);
|
||||
#endif
|
||||
|
||||
#ifdef USE_UPDATE
|
||||
@@ -450,7 +450,7 @@ class APIConnection final : public APIServerConnection {
|
||||
bool is_single);
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
static uint16_t try_send_event_response(event::Event *event, const std::string &event_type, APIConnection *conn,
|
||||
static uint16_t try_send_event_response(event::Event *event, const char *event_type, APIConnection *conn,
|
||||
uint32_t remaining_size, bool is_single);
|
||||
static uint16_t try_send_event_info(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single);
|
||||
#endif
|
||||
@@ -508,10 +508,8 @@ class APIConnection final : public APIServerConnection {
|
||||
// Constructor for function pointer
|
||||
MessageCreator(MessageCreatorPtr ptr) { data_.function_ptr = ptr; }
|
||||
|
||||
// Constructor for string state capture
|
||||
explicit MessageCreator(const std::string &str_value) { data_.string_ptr = new std::string(str_value); }
|
||||
|
||||
// No destructor - cleanup must be called explicitly with message_type
|
||||
// Constructor for const char * (Event types - no allocation needed)
|
||||
explicit MessageCreator(const char *str_value) { data_.const_char_ptr = str_value; }
|
||||
|
||||
// Delete copy operations - MessageCreator should only be moved
|
||||
MessageCreator(const MessageCreator &other) = delete;
|
||||
@@ -523,8 +521,6 @@ class APIConnection final : public APIServerConnection {
|
||||
// Move assignment
|
||||
MessageCreator &operator=(MessageCreator &&other) noexcept {
|
||||
if (this != &other) {
|
||||
// IMPORTANT: Caller must ensure cleanup() was called if this contains a string!
|
||||
// In our usage, this happens in add_item() deduplication and vector::erase()
|
||||
data_ = other.data_;
|
||||
other.data_.function_ptr = nullptr;
|
||||
}
|
||||
@@ -535,20 +531,10 @@ class APIConnection final : public APIServerConnection {
|
||||
uint16_t operator()(EntityBase *entity, APIConnection *conn, uint32_t remaining_size, bool is_single,
|
||||
uint8_t message_type) const;
|
||||
|
||||
// Manual cleanup method - must be called before destruction for string types
|
||||
void cleanup(uint8_t message_type) {
|
||||
#ifdef USE_EVENT
|
||||
if (message_type == EventResponse::MESSAGE_TYPE && data_.string_ptr != nullptr) {
|
||||
delete data_.string_ptr;
|
||||
data_.string_ptr = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
union Data {
|
||||
MessageCreatorPtr function_ptr;
|
||||
std::string *string_ptr;
|
||||
const char *const_char_ptr;
|
||||
} data_; // 4 bytes on 32-bit, 8 bytes on 64-bit - same as before
|
||||
};
|
||||
|
||||
@@ -568,42 +554,24 @@ class APIConnection final : public APIServerConnection {
|
||||
std::vector<BatchItem> items;
|
||||
uint32_t batch_start_time{0};
|
||||
|
||||
private:
|
||||
// Helper to cleanup items from the beginning
|
||||
void cleanup_items_(size_t count) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
items[i].creator.cleanup(items[i].message_type);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
DeferredBatch() {
|
||||
// Pre-allocate capacity for typical batch sizes to avoid reallocation
|
||||
items.reserve(8);
|
||||
}
|
||||
|
||||
~DeferredBatch() {
|
||||
// Ensure cleanup of any remaining items
|
||||
clear();
|
||||
}
|
||||
|
||||
// Add item to the batch
|
||||
void add_item(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size);
|
||||
// Add item to the front of the batch (for high priority messages like ping)
|
||||
void add_item_front(EntityBase *entity, MessageCreator creator, uint8_t message_type, uint8_t estimated_size);
|
||||
|
||||
// Clear all items with proper cleanup
|
||||
// Clear all items
|
||||
void clear() {
|
||||
cleanup_items_(items.size());
|
||||
items.clear();
|
||||
batch_start_time = 0;
|
||||
}
|
||||
|
||||
// Remove processed items from the front with proper cleanup
|
||||
void remove_front(size_t count) {
|
||||
cleanup_items_(count);
|
||||
items.erase(items.begin(), items.begin() + count);
|
||||
}
|
||||
// Remove processed items from the front
|
||||
void remove_front(size_t count) { items.erase(items.begin(), items.begin() + count); }
|
||||
|
||||
bool empty() const { return items.empty(); }
|
||||
size_t size() const { return items.size(); }
|
||||
|
||||
@@ -2877,8 +2877,8 @@ void ListEntitiesEventResponse::encode(ProtoWriteBuffer buffer) const {
|
||||
buffer.encode_bool(6, this->disabled_by_default);
|
||||
buffer.encode_uint32(7, static_cast<uint32_t>(this->entity_category));
|
||||
buffer.encode_string(8, this->device_class_ref_);
|
||||
for (auto &it : this->event_types) {
|
||||
buffer.encode_string(9, it, true);
|
||||
for (const char *it : *this->event_types) {
|
||||
buffer.encode_string(9, it, strlen(it), true);
|
||||
}
|
||||
#ifdef USE_DEVICES
|
||||
buffer.encode_uint32(10, this->device_id);
|
||||
@@ -2894,9 +2894,9 @@ void ListEntitiesEventResponse::calculate_size(ProtoSize &size) const {
|
||||
size.add_bool(1, this->disabled_by_default);
|
||||
size.add_uint32(1, static_cast<uint32_t>(this->entity_category));
|
||||
size.add_length(1, this->device_class_ref_.size());
|
||||
if (!this->event_types.empty()) {
|
||||
for (const auto &it : this->event_types) {
|
||||
size.add_length_force(1, it.size());
|
||||
if (!this->event_types->empty()) {
|
||||
for (const char *it : *this->event_types) {
|
||||
size.add_length_force(1, strlen(it));
|
||||
}
|
||||
}
|
||||
#ifdef USE_DEVICES
|
||||
|
||||
@@ -2788,7 +2788,7 @@ class ListEntitiesEventResponse final : public InfoResponseProtoMessage {
|
||||
#endif
|
||||
StringRef device_class_ref_{};
|
||||
void set_device_class(const StringRef &ref) { this->device_class_ref_ = ref; }
|
||||
std::vector<std::string> event_types{};
|
||||
const FixedVector<const char *> *event_types{};
|
||||
void encode(ProtoWriteBuffer buffer) const override;
|
||||
void calculate_size(ProtoSize &size) const override;
|
||||
#ifdef HAS_PROTO_MESSAGE_DUMP
|
||||
|
||||
@@ -2053,7 +2053,7 @@ void ListEntitiesEventResponse::dump_to(std::string &out) const {
|
||||
dump_field(out, "disabled_by_default", this->disabled_by_default);
|
||||
dump_field(out, "entity_category", static_cast<enums::EntityCategory>(this->entity_category));
|
||||
dump_field(out, "device_class", this->device_class_ref_);
|
||||
for (const auto &it : this->event_types) {
|
||||
for (const auto &it : *this->event_types) {
|
||||
dump_field(out, "event_types", it, 4);
|
||||
}
|
||||
#ifdef USE_DEVICES
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "esphome/components/network/util.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/util.h"
|
||||
@@ -34,7 +35,7 @@ APIServer::APIServer() {
|
||||
}
|
||||
|
||||
void APIServer::setup() {
|
||||
this->setup_controller();
|
||||
ControllerRegistry::register_controller(this);
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
uint32_t hash = 88491486UL;
|
||||
@@ -269,7 +270,7 @@ bool APIServer::check_password(const uint8_t *password_data, size_t password_len
|
||||
|
||||
void APIServer::handle_disconnect(APIConnection *conn) {}
|
||||
|
||||
// Macro for entities without extra parameters
|
||||
// Macro for controller update dispatch
|
||||
#define API_DISPATCH_UPDATE(entity_type, entity_name) \
|
||||
void APIServer::on_##entity_name##_update(entity_type *obj) { /* NOLINT(bugprone-macro-parentheses) */ \
|
||||
if (obj->is_internal()) \
|
||||
@@ -278,15 +279,6 @@ void APIServer::handle_disconnect(APIConnection *conn) {}
|
||||
c->send_##entity_name##_state(obj); \
|
||||
}
|
||||
|
||||
// Macro for entities with extra parameters (but parameters not used in send)
|
||||
#define API_DISPATCH_UPDATE_IGNORE_PARAMS(entity_type, entity_name, ...) \
|
||||
void APIServer::on_##entity_name##_update(entity_type *obj, __VA_ARGS__) { /* NOLINT(bugprone-macro-parentheses) */ \
|
||||
if (obj->is_internal()) \
|
||||
return; \
|
||||
for (auto &c : this->clients_) \
|
||||
c->send_##entity_name##_state(obj); \
|
||||
}
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
API_DISPATCH_UPDATE(binary_sensor::BinarySensor, binary_sensor)
|
||||
#endif
|
||||
@@ -304,15 +296,15 @@ API_DISPATCH_UPDATE(light::LightState, light)
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
API_DISPATCH_UPDATE_IGNORE_PARAMS(sensor::Sensor, sensor, float state)
|
||||
API_DISPATCH_UPDATE(sensor::Sensor, sensor)
|
||||
#endif
|
||||
|
||||
#ifdef USE_SWITCH
|
||||
API_DISPATCH_UPDATE_IGNORE_PARAMS(switch_::Switch, switch, bool state)
|
||||
API_DISPATCH_UPDATE(switch_::Switch, switch)
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
API_DISPATCH_UPDATE_IGNORE_PARAMS(text_sensor::TextSensor, text_sensor, const std::string &state)
|
||||
API_DISPATCH_UPDATE(text_sensor::TextSensor, text_sensor)
|
||||
#endif
|
||||
|
||||
#ifdef USE_CLIMATE
|
||||
@@ -320,7 +312,7 @@ API_DISPATCH_UPDATE(climate::Climate, climate)
|
||||
#endif
|
||||
|
||||
#ifdef USE_NUMBER
|
||||
API_DISPATCH_UPDATE_IGNORE_PARAMS(number::Number, number, float state)
|
||||
API_DISPATCH_UPDATE(number::Number, number)
|
||||
#endif
|
||||
|
||||
#ifdef USE_DATETIME_DATE
|
||||
@@ -336,11 +328,11 @@ API_DISPATCH_UPDATE(datetime::DateTimeEntity, datetime)
|
||||
#endif
|
||||
|
||||
#ifdef USE_TEXT
|
||||
API_DISPATCH_UPDATE_IGNORE_PARAMS(text::Text, text, const std::string &state)
|
||||
API_DISPATCH_UPDATE(text::Text, text)
|
||||
#endif
|
||||
|
||||
#ifdef USE_SELECT
|
||||
API_DISPATCH_UPDATE_IGNORE_PARAMS(select::Select, select, const std::string &state, size_t index)
|
||||
API_DISPATCH_UPDATE(select::Select, select)
|
||||
#endif
|
||||
|
||||
#ifdef USE_LOCK
|
||||
@@ -356,12 +348,13 @@ API_DISPATCH_UPDATE(media_player::MediaPlayer, media_player)
|
||||
#endif
|
||||
|
||||
#ifdef USE_EVENT
|
||||
// Event is a special case - it's the only entity that passes extra parameters to the send method
|
||||
void APIServer::on_event(event::Event *obj, const std::string &event_type) {
|
||||
// Event is a special case - unlike other entities with simple state fields,
|
||||
// events store their state in a member accessed via obj->get_last_event_type()
|
||||
void APIServer::on_event(event::Event *obj) {
|
||||
if (obj->is_internal())
|
||||
return;
|
||||
for (auto &c : this->clients_)
|
||||
c->send_event(obj, event_type);
|
||||
c->send_event(obj, obj->get_last_event_type());
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -72,19 +72,19 @@ class APIServer : public Component, public Controller {
|
||||
void on_light_update(light::LightState *obj) override;
|
||||
#endif
|
||||
#ifdef USE_SENSOR
|
||||
void on_sensor_update(sensor::Sensor *obj, float state) override;
|
||||
void on_sensor_update(sensor::Sensor *obj) override;
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
void on_switch_update(switch_::Switch *obj, bool state) override;
|
||||
void on_switch_update(switch_::Switch *obj) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
void on_text_sensor_update(text_sensor::TextSensor *obj, const std::string &state) override;
|
||||
void on_text_sensor_update(text_sensor::TextSensor *obj) 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;
|
||||
void on_number_update(number::Number *obj) override;
|
||||
#endif
|
||||
#ifdef USE_DATETIME_DATE
|
||||
void on_date_update(datetime::DateEntity *obj) override;
|
||||
@@ -96,10 +96,10 @@ class APIServer : public Component, public Controller {
|
||||
void on_datetime_update(datetime::DateTimeEntity *obj) override;
|
||||
#endif
|
||||
#ifdef USE_TEXT
|
||||
void on_text_update(text::Text *obj, const std::string &state) override;
|
||||
void on_text_update(text::Text *obj) override;
|
||||
#endif
|
||||
#ifdef USE_SELECT
|
||||
void on_select_update(select::Select *obj, const std::string &state, size_t index) override;
|
||||
void on_select_update(select::Select *obj) override;
|
||||
#endif
|
||||
#ifdef USE_LOCK
|
||||
void on_lock_update(lock::Lock *obj) override;
|
||||
@@ -141,7 +141,7 @@ class APIServer : public Component, public Controller {
|
||||
void on_alarm_control_panel_update(alarm_control_panel::AlarmControlPanel *obj) override;
|
||||
#endif
|
||||
#ifdef USE_EVENT
|
||||
void on_event(event::Event *obj, const std::string &event_type) override;
|
||||
void on_event(event::Event *obj) override;
|
||||
#endif
|
||||
#ifdef USE_UPDATE
|
||||
void on_update(update::UpdateEntity *obj) override;
|
||||
@@ -237,7 +237,7 @@ extern APIServer *global_api_server; // NOLINT(cppcoreguidelines-avoid-non-cons
|
||||
|
||||
template<typename... Ts> class APIConnectedCondition : public Condition<Ts...> {
|
||||
public:
|
||||
bool check(Ts... x) override { return global_api_server->is_connected(); }
|
||||
bool check(const Ts &...x) override { return global_api_server->is_connected(); }
|
||||
};
|
||||
|
||||
} // namespace esphome::api
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
namespace esphome::api {
|
||||
|
||||
#ifdef USE_API_SERVICES
|
||||
template<typename T, typename... Ts> class CustomAPIDeviceService : public UserServiceBase<Ts...> {
|
||||
template<typename T, typename... Ts> class CustomAPIDeviceService : public UserServiceDynamic<Ts...> {
|
||||
public:
|
||||
CustomAPIDeviceService(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names, T *obj,
|
||||
void (T::*callback)(Ts...))
|
||||
: UserServiceBase<Ts...>(name, arg_names), obj_(obj), callback_(callback) {}
|
||||
: UserServiceDynamic<Ts...>(name, arg_names), obj_(obj), callback_(callback) {}
|
||||
|
||||
protected:
|
||||
void execute(Ts... x) override { (this->obj_->*this->callback_)(x...); } // NOLINT
|
||||
|
||||
@@ -133,7 +133,7 @@ template<typename... Ts> class HomeAssistantServiceCallAction : public Action<Ts
|
||||
Trigger<std::string, Ts...> *get_error_trigger() const { return this->error_trigger_; }
|
||||
#endif // USE_API_HOMEASSISTANT_ACTION_RESPONSES
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
HomeassistantActionRequest resp;
|
||||
std::string service_value = this->service_.value(x...);
|
||||
resp.set_service(StringRef(service_value));
|
||||
|
||||
@@ -23,11 +23,13 @@ template<typename T> T get_execute_arg_value(const ExecuteServiceArgument &arg);
|
||||
|
||||
template<typename T> enums::ServiceArgType to_service_arg_type();
|
||||
|
||||
// Base class for YAML-defined services (most common case)
|
||||
// Stores only pointers to string literals in flash - no heap allocation
|
||||
template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
||||
public:
|
||||
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_);
|
||||
UserServiceBase(const char *name, const std::array<const char *, sizeof...(Ts)> &arg_names)
|
||||
: name_(name), arg_names_(arg_names) {
|
||||
this->key_ = fnv1_hash(name);
|
||||
}
|
||||
|
||||
ListEntitiesServicesResponse encode_list_service_response() override {
|
||||
@@ -47,7 +49,7 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
||||
bool execute_service(const ExecuteServiceRequest &req) override {
|
||||
if (req.key != this->key_)
|
||||
return false;
|
||||
if (req.args.size() != this->arg_names_.size())
|
||||
if (req.args.size() != sizeof...(Ts))
|
||||
return false;
|
||||
this->execute_(req.args, typename gens<sizeof...(Ts)>::type());
|
||||
return true;
|
||||
@@ -59,14 +61,60 @@ template<typename... Ts> class UserServiceBase : public UserServiceDescriptor {
|
||||
this->execute((get_execute_arg_value<Ts>(args[S]))...);
|
||||
}
|
||||
|
||||
std::string name_;
|
||||
// Pointers to string literals in flash - no heap allocation
|
||||
const char *name_;
|
||||
std::array<const char *, sizeof...(Ts)> arg_names_;
|
||||
uint32_t key_{0};
|
||||
};
|
||||
|
||||
// Separate class for custom_api_device services (rare case)
|
||||
// Stores copies of runtime-generated names
|
||||
template<typename... Ts> class UserServiceDynamic : public UserServiceDescriptor {
|
||||
public:
|
||||
UserServiceDynamic(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_.c_str());
|
||||
}
|
||||
|
||||
ListEntitiesServicesResponse encode_list_service_response() override {
|
||||
ListEntitiesServicesResponse msg;
|
||||
msg.set_name(StringRef(this->name_));
|
||||
msg.key = this->key_;
|
||||
std::array<enums::ServiceArgType, sizeof...(Ts)> arg_types = {to_service_arg_type<Ts>()...};
|
||||
msg.args.init(sizeof...(Ts));
|
||||
for (size_t i = 0; i < sizeof...(Ts); i++) {
|
||||
auto &arg = msg.args.emplace_back();
|
||||
arg.type = arg_types[i];
|
||||
arg.set_name(StringRef(this->arg_names_[i]));
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
bool execute_service(const ExecuteServiceRequest &req) override {
|
||||
if (req.key != this->key_)
|
||||
return false;
|
||||
if (req.args.size() != sizeof...(Ts))
|
||||
return false;
|
||||
this->execute_(req.args, typename gens<sizeof...(Ts)>::type());
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void execute(Ts... x) = 0;
|
||||
template<typename ArgsContainer, int... S> void execute_(const ArgsContainer &args, seq<S...> type) {
|
||||
this->execute((get_execute_arg_value<Ts>(args[S]))...);
|
||||
}
|
||||
|
||||
// Heap-allocated strings for runtime-generated names
|
||||
std::string name_;
|
||||
std::array<std::string, sizeof...(Ts)> arg_names_;
|
||||
uint32_t key_{0};
|
||||
};
|
||||
|
||||
template<typename... Ts> class UserServiceTrigger : public UserServiceBase<Ts...>, public Trigger<Ts...> {
|
||||
public:
|
||||
UserServiceTrigger(const std::string &name, const std::array<std::string, sizeof...(Ts)> &arg_names)
|
||||
// Constructor for static names (YAML-defined services - used by code generator)
|
||||
UserServiceTrigger(const char *name, const std::array<const char *, sizeof...(Ts)> &arg_names)
|
||||
: UserServiceBase<Ts...>(name, arg_names) {}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace at581x {
|
||||
|
||||
template<typename... Ts> class AT581XResetAction : public Action<Ts...>, public Parented<AT581XComponent> {
|
||||
public:
|
||||
void play(Ts... x) { this->parent_->reset_hardware_frontend(); }
|
||||
void play(const Ts &...x) { this->parent_->reset_hardware_frontend(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class AT581XSettingsAction : public Action<Ts...>, public Parented<AT581XComponent> {
|
||||
@@ -25,7 +25,7 @@ template<typename... Ts> class AT581XSettingsAction : public Action<Ts...>, publ
|
||||
TEMPLATABLE_VALUE(int, trigger_keep)
|
||||
TEMPLATABLE_VALUE(int, stage_gain)
|
||||
|
||||
void play(Ts... x) {
|
||||
void play(const Ts &...x) {
|
||||
if (this->frequency_.has_value()) {
|
||||
int v = this->frequency_.value(x...);
|
||||
this->parent_->set_frequency(v);
|
||||
|
||||
@@ -13,7 +13,7 @@ template<typename... Ts> class SetMicGainAction : public Action<Ts...> {
|
||||
|
||||
TEMPLATABLE_VALUE(float, mic_gain)
|
||||
|
||||
void play(Ts... x) override { this->audio_adc_->set_mic_gain(this->mic_gain_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->audio_adc_->set_mic_gain(this->mic_gain_.value(x...)); }
|
||||
|
||||
protected:
|
||||
AudioAdc *audio_adc_;
|
||||
|
||||
@@ -11,7 +11,7 @@ template<typename... Ts> class MuteOffAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit MuteOffAction(AudioDac *audio_dac) : audio_dac_(audio_dac) {}
|
||||
|
||||
void play(Ts... x) override { this->audio_dac_->set_mute_off(); }
|
||||
void play(const Ts &...x) override { this->audio_dac_->set_mute_off(); }
|
||||
|
||||
protected:
|
||||
AudioDac *audio_dac_;
|
||||
@@ -21,7 +21,7 @@ template<typename... Ts> class MuteOnAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit MuteOnAction(AudioDac *audio_dac) : audio_dac_(audio_dac) {}
|
||||
|
||||
void play(Ts... x) override { this->audio_dac_->set_mute_on(); }
|
||||
void play(const Ts &...x) override { this->audio_dac_->set_mute_on(); }
|
||||
|
||||
protected:
|
||||
AudioDac *audio_dac_;
|
||||
@@ -33,7 +33,7 @@ template<typename... Ts> class SetVolumeAction : public Action<Ts...> {
|
||||
|
||||
TEMPLATABLE_VALUE(float, volume)
|
||||
|
||||
void play(Ts... x) override { this->audio_dac_->set_volume(this->volume_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->audio_dac_->set_volume(this->volume_.value(x...)); }
|
||||
|
||||
protected:
|
||||
AudioDac *audio_dac_;
|
||||
|
||||
@@ -141,7 +141,7 @@ class StateChangeTrigger : public Trigger<optional<bool>, optional<bool> > {
|
||||
template<typename... Ts> class BinarySensorCondition : public Condition<Ts...> {
|
||||
public:
|
||||
BinarySensorCondition(BinarySensor *parent, bool state) : parent_(parent), state_(state) {}
|
||||
bool check(Ts... x) override { return this->parent_->state == this->state_; }
|
||||
bool check(const Ts &...x) override { return this->parent_->state == this->state_; }
|
||||
|
||||
protected:
|
||||
BinarySensor *parent_;
|
||||
@@ -153,7 +153,7 @@ template<typename... Ts> class BinarySensorPublishAction : public Action<Ts...>
|
||||
explicit BinarySensorPublishAction(BinarySensor *sensor) : sensor_(sensor) {}
|
||||
TEMPLATABLE_VALUE(bool, state)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto val = this->state_.value(x...);
|
||||
this->sensor_->publish_state(val);
|
||||
}
|
||||
@@ -166,7 +166,7 @@ template<typename... Ts> class BinarySensorInvalidateAction : public Action<Ts..
|
||||
public:
|
||||
explicit BinarySensorInvalidateAction(BinarySensor *sensor) : sensor_(sensor) {}
|
||||
|
||||
void play(Ts... x) override { this->sensor_->invalidate_state(); }
|
||||
void play(const Ts &...x) override { this->sensor_->invalidate_state(); }
|
||||
|
||||
protected:
|
||||
BinarySensor *sensor_;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "binary_sensor.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -37,6 +39,9 @@ void BinarySensor::send_state_internal(bool new_state) {
|
||||
// Note that set_state_ de-dups and will only trigger callbacks if the state has actually changed
|
||||
if (this->set_state_(new_state)) {
|
||||
ESP_LOGD(TAG, "'%s': New state is %s", this->get_name().c_str(), ONOFF(new_state));
|
||||
#if defined(USE_BINARY_SENSOR) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_binary_sensor_update(this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class BL0906 : public PollingComponent, public uart::UARTDevice {
|
||||
|
||||
template<typename... Ts> class ResetEnergyAction : public Action<Ts...>, public Parented<BL0906> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->enqueue_action_(&BL0906::reset_energy_); }
|
||||
void play(const Ts &...x) override { this->parent_->enqueue_action_(&BL0906::reset_energy_); }
|
||||
};
|
||||
|
||||
} // namespace bl0906
|
||||
|
||||
@@ -9,7 +9,7 @@ static const char *const TAG = "bl0940.number";
|
||||
void CalibrationNumber::setup() {
|
||||
float value = 0.0f;
|
||||
if (this->restore_value_) {
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_object_id_hash());
|
||||
this->pref_ = global_preferences->make_preference<float>(this->get_preference_hash());
|
||||
if (!this->pref_.load(&value)) {
|
||||
value = 0.0f;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ from esphome.const import (
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_VALUE,
|
||||
)
|
||||
from esphome.core import ID
|
||||
|
||||
AUTO_LOAD = ["esp32_ble_client"]
|
||||
CODEOWNERS = ["@buxtronix", "@clydebarrow"]
|
||||
@@ -198,7 +199,12 @@ async def ble_write_to_code(config, action_id, template_arg, args):
|
||||
templ = await cg.templatable(value, args, cg.std_vector.template(cg.uint8))
|
||||
cg.add(var.set_value_template(templ))
|
||||
else:
|
||||
cg.add(var.set_value_simple(value))
|
||||
# Generate static array in flash to avoid RAM copy
|
||||
if isinstance(value, bytes):
|
||||
value = list(value)
|
||||
arr_id = ID(f"{action_id}_data", is_declaration=True, type=cg.uint8)
|
||||
arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*value))
|
||||
cg.add(var.set_value_simple(arr, len(value)))
|
||||
|
||||
if len(config[CONF_SERVICE_UUID]) == len(esp32_ble_tracker.bt_uuid16_format):
|
||||
cg.add(
|
||||
|
||||
@@ -96,11 +96,8 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
|
||||
BLEClientWriteAction(BLEClient *ble_client) {
|
||||
ble_client->register_ble_node(this);
|
||||
ble_client_ = ble_client;
|
||||
this->construct_simple_value_();
|
||||
}
|
||||
|
||||
~BLEClientWriteAction() { this->destroy_simple_value_(); }
|
||||
|
||||
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); }
|
||||
@@ -110,27 +107,34 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
|
||||
void set_char_uuid128(uint8_t *uuid) { this->char_uuid_ = espbt::ESPBTUUID::from_raw(uuid); }
|
||||
|
||||
void set_value_template(std::vector<uint8_t> (*func)(Ts...)) {
|
||||
this->destroy_simple_value_();
|
||||
this->value_.template_func = func;
|
||||
this->has_simple_value_ = false;
|
||||
this->value_.func = func;
|
||||
this->len_ = -1; // Sentinel value indicates template mode
|
||||
}
|
||||
|
||||
void set_value_simple(const std::vector<uint8_t> &value) {
|
||||
if (!this->has_simple_value_) {
|
||||
this->construct_simple_value_();
|
||||
}
|
||||
this->value_.simple = value;
|
||||
this->has_simple_value_ = true;
|
||||
// Store pointer to static data in flash (no RAM copy)
|
||||
void set_value_simple(const uint8_t *data, size_t len) {
|
||||
this->value_.data = data;
|
||||
this->len_ = len; // Length >= 0 indicates static mode
|
||||
}
|
||||
|
||||
void play(Ts... x) override {}
|
||||
void play(const Ts &...x) override {}
|
||||
|
||||
void play_complex(Ts... x) override {
|
||||
void play_complex(const Ts &...x) override {
|
||||
this->num_running_++;
|
||||
this->var_ = std::make_tuple(x...);
|
||||
auto value = this->has_simple_value_ ? this->value_.simple : this->value_.template_func(x...);
|
||||
|
||||
bool result;
|
||||
if (this->len_ >= 0) {
|
||||
// Static mode: write directly from flash pointer
|
||||
result = this->write(this->value_.data, this->len_);
|
||||
} else {
|
||||
// Template mode: call function and write the vector
|
||||
std::vector<uint8_t> value = this->value_.func(x...);
|
||||
result = this->write(value);
|
||||
}
|
||||
|
||||
// on write failure, continue the automation chain rather than stopping so that e.g. disconnect can work.
|
||||
if (!write(value))
|
||||
if (!result)
|
||||
this->play_next_(x...);
|
||||
}
|
||||
|
||||
@@ -143,15 +147,15 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
|
||||
* errors.
|
||||
*/
|
||||
// initiate the write. Return true if all went well, will be followed by a WRITE_CHAR event.
|
||||
bool write(const std::vector<uint8_t> &value) {
|
||||
bool write(const uint8_t *data, size_t len) {
|
||||
if (this->node_state != espbt::ClientState::ESTABLISHED) {
|
||||
esph_log_w(Automation::TAG, "Cannot write to BLE characteristic - not connected");
|
||||
return false;
|
||||
}
|
||||
esph_log_vv(Automation::TAG, "Will write %d bytes: %s", value.size(), format_hex_pretty(value).c_str());
|
||||
esp_err_t err = esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(),
|
||||
this->char_handle_, value.size(), const_cast<uint8_t *>(value.data()),
|
||||
this->write_type_, ESP_GATT_AUTH_REQ_NONE);
|
||||
esph_log_vv(Automation::TAG, "Will write %d bytes: %s", len, format_hex_pretty(data, len).c_str());
|
||||
esp_err_t err =
|
||||
esp_ble_gattc_write_char(this->parent()->get_gattc_if(), this->parent()->get_conn_id(), this->char_handle_, len,
|
||||
const_cast<uint8_t *>(data), this->write_type_, ESP_GATT_AUTH_REQ_NONE);
|
||||
if (err != ESP_OK) {
|
||||
esph_log_e(Automation::TAG, "Error writing to characteristic: %s!", esp_err_to_name(err));
|
||||
return false;
|
||||
@@ -159,6 +163,8 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
|
||||
return true;
|
||||
}
|
||||
|
||||
bool write(const std::vector<uint8_t> &value) { return this->write(value.data(), value.size()); }
|
||||
|
||||
void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if,
|
||||
esp_ble_gattc_cb_param_t *param) override {
|
||||
switch (event) {
|
||||
@@ -201,21 +207,11 @@ template<typename... Ts> class BLEClientWriteAction : public Action<Ts...>, publ
|
||||
}
|
||||
|
||||
private:
|
||||
void construct_simple_value_() { new (&this->value_.simple) std::vector<uint8_t>(); }
|
||||
|
||||
void destroy_simple_value_() {
|
||||
if (this->has_simple_value_) {
|
||||
this->value_.simple.~vector();
|
||||
}
|
||||
}
|
||||
|
||||
BLEClient *ble_client_;
|
||||
bool has_simple_value_ = true;
|
||||
ssize_t len_{-1}; // -1 = template mode, >=0 = static mode with length
|
||||
union Value {
|
||||
std::vector<uint8_t> simple;
|
||||
std::vector<uint8_t> (*template_func)(Ts...);
|
||||
Value() {} // trivial constructor
|
||||
~Value() {} // trivial destructor - we manage lifetime via discriminator
|
||||
std::vector<uint8_t> (*func)(Ts...); // Function pointer (stateless lambdas)
|
||||
const uint8_t *data; // Pointer to static data in flash
|
||||
} value_;
|
||||
espbt::ESPBTUUID service_uuid_;
|
||||
espbt::ESPBTUUID char_uuid_;
|
||||
@@ -229,7 +225,7 @@ template<typename... Ts> class BLEClientPasskeyReplyAction : public Action<Ts...
|
||||
public:
|
||||
BLEClientPasskeyReplyAction(BLEClient *ble_client) { parent_ = ble_client; }
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
uint32_t passkey;
|
||||
if (has_simple_value_) {
|
||||
passkey = this->value_.simple;
|
||||
@@ -266,7 +262,7 @@ template<typename... Ts> class BLEClientNumericComparisonReplyAction : public Ac
|
||||
public:
|
||||
BLEClientNumericComparisonReplyAction(BLEClient *ble_client) { parent_ = ble_client; }
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
esp_bd_addr_t remote_bda;
|
||||
memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
|
||||
if (has_simple_value_) {
|
||||
@@ -299,7 +295,7 @@ template<typename... Ts> class BLEClientRemoveBondAction : public Action<Ts...>
|
||||
public:
|
||||
BLEClientRemoveBondAction(BLEClient *ble_client) { parent_ = ble_client; }
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
esp_bd_addr_t remote_bda;
|
||||
memcpy(remote_bda, parent_->get_remote_bda(), sizeof(esp_bd_addr_t));
|
||||
esp_ble_remove_bond_device(remote_bda);
|
||||
@@ -334,9 +330,9 @@ template<typename... Ts> class BLEClientConnectAction : public Action<Ts...>, pu
|
||||
}
|
||||
|
||||
// not used since we override play_complex_
|
||||
void play(Ts... x) override {}
|
||||
void play(const Ts &...x) override {}
|
||||
|
||||
void play_complex(Ts... x) override {
|
||||
void play_complex(const Ts &...x) override {
|
||||
// it makes no sense to have multiple instances of this running at the same time.
|
||||
// this would occur only if the same automation was re-triggered while still
|
||||
// running. So just cancel the second chain if this is detected.
|
||||
@@ -379,9 +375,9 @@ template<typename... Ts> class BLEClientDisconnectAction : public Action<Ts...>,
|
||||
}
|
||||
|
||||
// not used since we override play_complex_
|
||||
void play(Ts... x) override {}
|
||||
void play(const Ts &...x) override {}
|
||||
|
||||
void play_complex(Ts... x) override {
|
||||
void play_complex(const Ts &...x) override {
|
||||
this->num_running_++;
|
||||
if (this->node_state == espbt::ClientState::IDLE) {
|
||||
this->play_next_(x...);
|
||||
|
||||
@@ -11,7 +11,7 @@ template<typename... Ts> class PressAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit PressAction(Button *button) : button_(button) {}
|
||||
|
||||
void play(Ts... x) override { this->button_->press(); }
|
||||
void play(const Ts &...x) override { this->button_->press(); }
|
||||
|
||||
protected:
|
||||
Button *button_;
|
||||
|
||||
@@ -4,7 +4,7 @@ from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_DATA, CONF_ID, CONF_TRIGGER_ID
|
||||
from esphome.core import CORE
|
||||
from esphome.core import CORE, ID
|
||||
|
||||
CODEOWNERS = ["@mvturnho", "@danielschramm"]
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
@@ -176,5 +176,8 @@ async def canbus_action_to_code(config, action_id, template_arg, args):
|
||||
else:
|
||||
if isinstance(data, bytes):
|
||||
data = [int(x) for x in data]
|
||||
cg.add(var.set_data_static(data))
|
||||
# Generate static array in flash to avoid RAM copy
|
||||
arr_id = ID(f"{action_id}_data", is_declaration=True, type=cg.uint8)
|
||||
arr = cg.static_const_array(arr_id, cg.ArrayInitializer(*data))
|
||||
cg.add(var.set_data_static(arr, len(data)))
|
||||
return var
|
||||
|
||||
@@ -112,13 +112,16 @@ class Canbus : public Component {
|
||||
|
||||
template<typename... Ts> class CanbusSendAction : public Action<Ts...>, public Parented<Canbus> {
|
||||
public:
|
||||
void set_data_template(const std::function<std::vector<uint8_t>(Ts...)> func) {
|
||||
this->data_func_ = func;
|
||||
this->static_ = false;
|
||||
void set_data_template(std::vector<uint8_t> (*func)(Ts...)) {
|
||||
// Stateless lambdas (generated by ESPHome) implicitly convert to function pointers
|
||||
this->data_.func = func;
|
||||
this->len_ = -1; // Sentinel value indicates template mode
|
||||
}
|
||||
void set_data_static(const std::vector<uint8_t> &data) {
|
||||
this->data_static_ = data;
|
||||
this->static_ = true;
|
||||
|
||||
// Store pointer to static data in flash (no RAM copy)
|
||||
void set_data_static(const uint8_t *data, size_t len) {
|
||||
this->data_.data = data;
|
||||
this->len_ = len; // Length >= 0 indicates static mode
|
||||
}
|
||||
|
||||
void set_can_id(uint32_t can_id) { this->can_id_ = can_id; }
|
||||
@@ -129,25 +132,30 @@ template<typename... Ts> class CanbusSendAction : public Action<Ts...>, public P
|
||||
this->remote_transmission_request_ = remote_transmission_request;
|
||||
}
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto can_id = this->can_id_.has_value() ? *this->can_id_ : this->parent_->can_id_;
|
||||
auto use_extended_id =
|
||||
this->use_extended_id_.has_value() ? *this->use_extended_id_ : this->parent_->use_extended_id_;
|
||||
if (this->static_) {
|
||||
this->parent_->send_data(can_id, use_extended_id, this->remote_transmission_request_, this->data_static_);
|
||||
std::vector<uint8_t> data;
|
||||
if (this->len_ >= 0) {
|
||||
// Static mode: copy from flash to vector
|
||||
data.assign(this->data_.data, this->data_.data + this->len_);
|
||||
} else {
|
||||
auto val = this->data_func_(x...);
|
||||
this->parent_->send_data(can_id, use_extended_id, this->remote_transmission_request_, val);
|
||||
// Template mode: call function
|
||||
data = this->data_.func(x...);
|
||||
}
|
||||
this->parent_->send_data(can_id, use_extended_id, this->remote_transmission_request_, data);
|
||||
}
|
||||
|
||||
protected:
|
||||
optional<uint32_t> can_id_{};
|
||||
optional<bool> use_extended_id_{};
|
||||
bool remote_transmission_request_{false};
|
||||
bool static_{false};
|
||||
std::function<std::vector<uint8_t>(Ts...)> data_func_{};
|
||||
std::vector<uint8_t> data_static_{};
|
||||
ssize_t len_{-1}; // -1 = template mode, >=0 = static mode with length
|
||||
union Data {
|
||||
std::vector<uint8_t> (*func)(Ts...); // Function pointer (stateless lambdas)
|
||||
const uint8_t *data; // Pointer to static data in flash
|
||||
} data_;
|
||||
};
|
||||
|
||||
class CanbusTrigger : public Trigger<std::vector<uint8_t>, uint32_t, bool>, public Component {
|
||||
|
||||
@@ -22,7 +22,7 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
|
||||
TEMPLATABLE_VALUE(std::string, custom_preset)
|
||||
TEMPLATABLE_VALUE(ClimateSwingMode, swing_mode)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto call = this->climate_->make_call();
|
||||
call.set_mode(this->mode_.optional_value(x...));
|
||||
call.set_target_temperature(this->target_temperature_.optional_value(x...));
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "climate.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/macros.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -463,6 +465,9 @@ void Climate::publish_state() {
|
||||
|
||||
// Send state to frontend
|
||||
this->state_callback_.call(*this);
|
||||
#if defined(USE_CLIMATE) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_climate_update(this);
|
||||
#endif
|
||||
// Save state
|
||||
this->save_state_();
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ template<typename... Ts> class CM1106CalibrateZeroAction : public Action<Ts...>
|
||||
public:
|
||||
CM1106CalibrateZeroAction(CM1106Component *cm1106) : cm1106_(cm1106) {}
|
||||
|
||||
void play(Ts... x) override { this->cm1106_->calibrate_zero(400); }
|
||||
void play(const Ts &...x) override { this->cm1106_->calibrate_zero(400); }
|
||||
|
||||
protected:
|
||||
CM1106Component *cm1106_;
|
||||
|
||||
@@ -8,6 +8,8 @@ BYTE_ORDER_BIG = "big_endian"
|
||||
|
||||
CONF_COLOR_DEPTH = "color_depth"
|
||||
CONF_DRAW_ROUNDING = "draw_rounding"
|
||||
CONF_ENABLED = "enabled"
|
||||
CONF_IGNORE_NOT_FOUND = "ignore_not_found"
|
||||
CONF_ON_RECEIVE = "on_receive"
|
||||
CONF_ON_STATE_CHANGE = "on_state_change"
|
||||
CONF_REQUEST_HEADERS = "request_headers"
|
||||
|
||||
@@ -11,7 +11,7 @@ template<typename... Ts> class OpenAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit OpenAction(Cover *cover) : cover_(cover) {}
|
||||
|
||||
void play(Ts... x) override { this->cover_->make_call().set_command_open().perform(); }
|
||||
void play(const Ts &...x) override { this->cover_->make_call().set_command_open().perform(); }
|
||||
|
||||
protected:
|
||||
Cover *cover_;
|
||||
@@ -21,7 +21,7 @@ template<typename... Ts> class CloseAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit CloseAction(Cover *cover) : cover_(cover) {}
|
||||
|
||||
void play(Ts... x) override { this->cover_->make_call().set_command_close().perform(); }
|
||||
void play(const Ts &...x) override { this->cover_->make_call().set_command_close().perform(); }
|
||||
|
||||
protected:
|
||||
Cover *cover_;
|
||||
@@ -31,7 +31,7 @@ template<typename... Ts> class StopAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit StopAction(Cover *cover) : cover_(cover) {}
|
||||
|
||||
void play(Ts... x) override { this->cover_->make_call().set_command_stop().perform(); }
|
||||
void play(const Ts &...x) override { this->cover_->make_call().set_command_stop().perform(); }
|
||||
|
||||
protected:
|
||||
Cover *cover_;
|
||||
@@ -41,7 +41,7 @@ template<typename... Ts> class ToggleAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit ToggleAction(Cover *cover) : cover_(cover) {}
|
||||
|
||||
void play(Ts... x) override { this->cover_->make_call().set_command_toggle().perform(); }
|
||||
void play(const Ts &...x) override { this->cover_->make_call().set_command_toggle().perform(); }
|
||||
|
||||
protected:
|
||||
Cover *cover_;
|
||||
@@ -55,7 +55,7 @@ template<typename... Ts> class ControlAction : public Action<Ts...> {
|
||||
TEMPLATABLE_VALUE(float, position)
|
||||
TEMPLATABLE_VALUE(float, tilt)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto call = this->cover_->make_call();
|
||||
if (this->stop_.has_value())
|
||||
call.set_stop(this->stop_.value(x...));
|
||||
@@ -77,7 +77,7 @@ template<typename... Ts> class CoverPublishAction : public Action<Ts...> {
|
||||
TEMPLATABLE_VALUE(float, tilt)
|
||||
TEMPLATABLE_VALUE(CoverOperation, current_operation)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
if (this->position_.has_value())
|
||||
this->cover_->position = this->position_.value(x...);
|
||||
if (this->tilt_.has_value())
|
||||
@@ -94,7 +94,7 @@ template<typename... Ts> class CoverPublishAction : public Action<Ts...> {
|
||||
template<typename... Ts> class CoverIsOpenCondition : public Condition<Ts...> {
|
||||
public:
|
||||
CoverIsOpenCondition(Cover *cover) : cover_(cover) {}
|
||||
bool check(Ts... x) override { return this->cover_->is_fully_open(); }
|
||||
bool check(const Ts &...x) override { return this->cover_->is_fully_open(); }
|
||||
|
||||
protected:
|
||||
Cover *cover_;
|
||||
@@ -103,7 +103,7 @@ template<typename... Ts> class CoverIsOpenCondition : public Condition<Ts...> {
|
||||
template<typename... Ts> class CoverIsClosedCondition : public Condition<Ts...> {
|
||||
public:
|
||||
CoverIsClosedCondition(Cover *cover) : cover_(cover) {}
|
||||
bool check(Ts... x) override { return this->cover_->is_fully_closed(); }
|
||||
bool check(const Ts &...x) override { return this->cover_->is_fully_closed(); }
|
||||
|
||||
protected:
|
||||
Cover *cover_;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
#include "cover.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
|
||||
#include <strings.h>
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -169,6 +173,9 @@ void Cover::publish_state(bool save) {
|
||||
ESP_LOGD(TAG, " Current Operation: %s", cover_operation_to_str(this->current_operation));
|
||||
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_COVER) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_cover_update(this);
|
||||
#endif
|
||||
|
||||
if (save) {
|
||||
CoverRestoreState restore{};
|
||||
|
||||
@@ -114,7 +114,7 @@ template<typename... Ts> class CS5460ARestartAction : public Action<Ts...> {
|
||||
public:
|
||||
CS5460ARestartAction(CS5460AComponent *cs5460a) : cs5460a_(cs5460a) {}
|
||||
|
||||
void play(Ts... x) override { cs5460a_->restart(); }
|
||||
void play(const Ts &...x) override { cs5460a_->restart(); }
|
||||
|
||||
protected:
|
||||
CS5460AComponent *cs5460a_;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "date_entity.h"
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#ifdef USE_DATETIME_DATE
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
@@ -32,6 +33,9 @@ void DateEntity::publish_state() {
|
||||
this->set_has_state(true);
|
||||
ESP_LOGD(TAG, "'%s': Sending date %d-%d-%d", this->get_name().c_str(), this->year_, this->month_, this->day_);
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_DATETIME_DATE) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_date_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
DateCall DateEntity::make_call() { return DateCall(this); }
|
||||
|
||||
@@ -101,7 +101,7 @@ template<typename... Ts> class DateSetAction : public Action<Ts...>, public Pare
|
||||
public:
|
||||
TEMPLATABLE_VALUE(ESPTime, date)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto call = this->parent_->make_call();
|
||||
|
||||
if (this->date_.has_value()) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "datetime_entity.h"
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#ifdef USE_DATETIME_DATETIME
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
@@ -48,6 +49,9 @@ void DateTimeEntity::publish_state() {
|
||||
ESP_LOGD(TAG, "'%s': Sending datetime %04u-%02u-%02u %02d:%02d:%02d", this->get_name().c_str(), this->year_,
|
||||
this->month_, this->day_, this->hour_, this->minute_, this->second_);
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_DATETIME_DATETIME) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_datetime_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
DateTimeCall DateTimeEntity::make_call() { return DateTimeCall(this); }
|
||||
|
||||
@@ -124,7 +124,7 @@ template<typename... Ts> class DateTimeSetAction : public Action<Ts...>, public
|
||||
public:
|
||||
TEMPLATABLE_VALUE(ESPTime, datetime)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto call = this->parent_->make_call();
|
||||
|
||||
if (this->datetime_.has_value()) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "time_entity.h"
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#ifdef USE_DATETIME_TIME
|
||||
|
||||
#include "esphome/core/log.h"
|
||||
@@ -29,6 +30,9 @@ void TimeEntity::publish_state() {
|
||||
ESP_LOGD(TAG, "'%s': Sending time %02d:%02d:%02d", this->get_name().c_str(), this->hour_, this->minute_,
|
||||
this->second_);
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_DATETIME_TIME) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_time_update(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
TimeCall TimeEntity::make_call() { return TimeCall(this); }
|
||||
|
||||
@@ -103,7 +103,7 @@ template<typename... Ts> class TimeSetAction : public Action<Ts...>, public Pare
|
||||
public:
|
||||
TEMPLATABLE_VALUE(ESPTime, time)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto call = this->parent_->make_call();
|
||||
|
||||
if (this->time_.has_value()) {
|
||||
|
||||
@@ -59,6 +59,7 @@ async def to_code(config):
|
||||
zephyr_add_prj_conf("SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL", True)
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
cg.add_define("USE_DEBUG")
|
||||
|
||||
|
||||
FILTER_SOURCE_FILES = filter_source_files_from_platform(
|
||||
|
||||
@@ -148,7 +148,7 @@ template<typename... Ts> class EnterDeepSleepAction : public Action<Ts...> {
|
||||
void set_time(time::RealTimeClock *time) { this->time_ = time; }
|
||||
#endif
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
if (this->sleep_duration_.has_value()) {
|
||||
this->deep_sleep_->set_sleep_duration(this->sleep_duration_.value(x...));
|
||||
}
|
||||
@@ -207,12 +207,12 @@ template<typename... Ts> class EnterDeepSleepAction : public Action<Ts...> {
|
||||
|
||||
template<typename... Ts> class PreventDeepSleepAction : public Action<Ts...>, public Parented<DeepSleepComponent> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->prevent_deep_sleep(); }
|
||||
void play(const Ts &...x) override { this->parent_->prevent_deep_sleep(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class AllowDeepSleepAction : public Action<Ts...>, public Parented<DeepSleepComponent> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->allow_deep_sleep(); }
|
||||
void play(const Ts &...x) override { this->parent_->allow_deep_sleep(); }
|
||||
};
|
||||
|
||||
} // namespace deep_sleep
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace demo {
|
||||
|
||||
class DemoSelect : public select::Select, public Component {
|
||||
protected:
|
||||
void control(const std::string &value) override { this->publish_state(value); }
|
||||
void control(size_t index) override { this->publish_state(index); }
|
||||
};
|
||||
|
||||
} // namespace demo
|
||||
|
||||
@@ -77,7 +77,7 @@ class DFPlayer : public uart::UARTDevice, public Component {
|
||||
class ACTION_CLASS : /* NOLINT */ \
|
||||
public Action<Ts...>, \
|
||||
public Parented<DFPlayer> { \
|
||||
void play(Ts... x) override { this->parent_->ACTION_METHOD(); } \
|
||||
void play(const Ts &...x) override { this->parent_->ACTION_METHOD(); } \
|
||||
};
|
||||
|
||||
DFPLAYER_SIMPLE_ACTION(NextAction, next)
|
||||
@@ -87,7 +87,7 @@ template<typename... Ts> class PlayMp3Action : public Action<Ts...>, public Pare
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint16_t, file)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto file = this->file_.value(x...);
|
||||
this->parent_->play_mp3(file);
|
||||
}
|
||||
@@ -98,7 +98,7 @@ template<typename... Ts> class PlayFileAction : public Action<Ts...>, public Par
|
||||
TEMPLATABLE_VALUE(uint16_t, file)
|
||||
TEMPLATABLE_VALUE(bool, loop)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto file = this->file_.value(x...);
|
||||
auto loop = this->loop_.value(x...);
|
||||
if (loop) {
|
||||
@@ -115,7 +115,7 @@ template<typename... Ts> class PlayFolderAction : public Action<Ts...>, public P
|
||||
TEMPLATABLE_VALUE(uint16_t, file)
|
||||
TEMPLATABLE_VALUE(bool, loop)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto folder = this->folder_.value(x...);
|
||||
auto file = this->file_.value(x...);
|
||||
auto loop = this->loop_.value(x...);
|
||||
@@ -131,7 +131,7 @@ template<typename... Ts> class SetDeviceAction : public Action<Ts...>, public Pa
|
||||
public:
|
||||
TEMPLATABLE_VALUE(Device, device)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto device = this->device_.value(x...);
|
||||
this->parent_->set_device(device);
|
||||
}
|
||||
@@ -141,7 +141,7 @@ template<typename... Ts> class SetVolumeAction : public Action<Ts...>, public Pa
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, volume)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto volume = this->volume_.value(x...);
|
||||
this->parent_->set_volume(volume);
|
||||
}
|
||||
@@ -151,7 +151,7 @@ template<typename... Ts> class SetEqAction : public Action<Ts...>, public Parent
|
||||
public:
|
||||
TEMPLATABLE_VALUE(EqPreset, eq)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto eq = this->eq_.value(x...);
|
||||
this->parent_->set_eq(eq);
|
||||
}
|
||||
@@ -168,7 +168,7 @@ DFPLAYER_SIMPLE_ACTION(VolumeDownAction, volume_down)
|
||||
|
||||
template<typename... Ts> class DFPlayerIsPlayingCondition : public Condition<Ts...>, public Parented<DFPlayer> {
|
||||
public:
|
||||
bool check(Ts... x) override { return this->parent_->is_playing(); }
|
||||
bool check(const Ts &...x) override { return this->parent_->is_playing(); }
|
||||
};
|
||||
|
||||
class DFPlayerFinishedPlaybackTrigger : public Trigger<> {
|
||||
|
||||
@@ -11,7 +11,7 @@ namespace dfrobot_sen0395 {
|
||||
template<typename... Ts>
|
||||
class DfrobotSen0395ResetAction : public Action<Ts...>, public Parented<DfrobotSen0395Component> {
|
||||
public:
|
||||
void play(Ts... x) { this->parent_->enqueue(make_unique<ResetSystemCommand>()); }
|
||||
void play(const Ts &...x) { this->parent_->enqueue(make_unique<ResetSystemCommand>()); }
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
@@ -33,7 +33,7 @@ class DfrobotSen0395SettingsAction : public Action<Ts...>, public Parented<Dfrob
|
||||
TEMPLATABLE_VALUE(float, det_min4)
|
||||
TEMPLATABLE_VALUE(float, det_max4)
|
||||
|
||||
void play(Ts... x) {
|
||||
void play(const Ts &...x) {
|
||||
this->parent_->enqueue(make_unique<PowerCommand>(0));
|
||||
if (this->factory_reset_.has_value() && this->factory_reset_.value(x...) == true) {
|
||||
this->parent_->enqueue(make_unique<FactoryResetCommand>());
|
||||
|
||||
@@ -176,7 +176,117 @@ class Display;
|
||||
class DisplayPage;
|
||||
class DisplayOnPageChangeTrigger;
|
||||
|
||||
using display_writer_t = std::function<void(Display &)>;
|
||||
/** Optimized display writer that uses function pointers for stateless lambdas.
|
||||
*
|
||||
* Similar to TemplatableValue but specialized for display writer callbacks.
|
||||
* Saves ~8 bytes per stateless lambda on 32-bit platforms (16 bytes std::function → ~8 bytes discriminator+pointer).
|
||||
*
|
||||
* Supports both:
|
||||
* - Stateless lambdas (from YAML) → function pointer (4 bytes)
|
||||
* - Stateful lambdas/std::function (from C++ code) → std::function* (heap allocated)
|
||||
*
|
||||
* @tparam T The display type (e.g., Display, Nextion, GPIOLCDDisplay)
|
||||
*/
|
||||
template<typename T> class DisplayWriter {
|
||||
public:
|
||||
DisplayWriter() : type_(NONE) {}
|
||||
|
||||
// For stateless lambdas (convertible to function pointer): use function pointer (4 bytes)
|
||||
template<typename F>
|
||||
DisplayWriter(F f) requires std::invocable<F, T &> && std::convertible_to<F, void (*)(T &)>
|
||||
: type_(STATELESS_LAMBDA) {
|
||||
this->stateless_f_ = f; // Implicit conversion to function pointer
|
||||
}
|
||||
|
||||
// For stateful lambdas and std::function (not convertible to function pointer): use std::function* (heap allocated)
|
||||
// This handles backwards compatibility with external components
|
||||
template<typename F>
|
||||
DisplayWriter(F f) requires std::invocable<F, T &> &&(!std::convertible_to<F, void (*)(T &)>) : type_(LAMBDA) {
|
||||
this->f_ = new std::function<void(T &)>(std::move(f));
|
||||
}
|
||||
|
||||
// Copy constructor
|
||||
DisplayWriter(const DisplayWriter &other) : type_(other.type_) {
|
||||
if (type_ == LAMBDA) {
|
||||
this->f_ = new std::function<void(T &)>(*other.f_);
|
||||
} else if (type_ == STATELESS_LAMBDA) {
|
||||
this->stateless_f_ = other.stateless_f_;
|
||||
}
|
||||
}
|
||||
|
||||
// Move constructor
|
||||
DisplayWriter(DisplayWriter &&other) noexcept : type_(other.type_) {
|
||||
if (type_ == LAMBDA) {
|
||||
this->f_ = other.f_;
|
||||
other.f_ = nullptr;
|
||||
} else if (type_ == STATELESS_LAMBDA) {
|
||||
this->stateless_f_ = other.stateless_f_;
|
||||
}
|
||||
other.type_ = NONE;
|
||||
}
|
||||
|
||||
// Assignment operators
|
||||
DisplayWriter &operator=(const DisplayWriter &other) {
|
||||
if (this != &other) {
|
||||
this->~DisplayWriter();
|
||||
new (this) DisplayWriter(other);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
DisplayWriter &operator=(DisplayWriter &&other) noexcept {
|
||||
if (this != &other) {
|
||||
this->~DisplayWriter();
|
||||
new (this) DisplayWriter(std::move(other));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
~DisplayWriter() {
|
||||
if (type_ == LAMBDA) {
|
||||
delete this->f_;
|
||||
}
|
||||
// STATELESS_LAMBDA/NONE: no cleanup needed (function pointer or empty)
|
||||
}
|
||||
|
||||
bool has_value() const { return this->type_ != NONE; }
|
||||
|
||||
void call(T &display) const {
|
||||
switch (this->type_) {
|
||||
case STATELESS_LAMBDA:
|
||||
this->stateless_f_(display); // Direct function pointer call
|
||||
break;
|
||||
case LAMBDA:
|
||||
(*this->f_)(display); // std::function call
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Operator() for convenience
|
||||
void operator()(T &display) const { this->call(display); }
|
||||
|
||||
// Operator* for backwards compatibility with (*writer_)(*this) pattern
|
||||
DisplayWriter &operator*() { return *this; }
|
||||
const DisplayWriter &operator*() const { return *this; }
|
||||
|
||||
protected:
|
||||
enum : uint8_t {
|
||||
NONE,
|
||||
LAMBDA,
|
||||
STATELESS_LAMBDA,
|
||||
} type_;
|
||||
|
||||
union {
|
||||
std::function<void(T &)> *f_;
|
||||
void (*stateless_f_)(T &);
|
||||
};
|
||||
};
|
||||
|
||||
// Type alias for Display writer - uses optimized DisplayWriter instead of std::function
|
||||
using display_writer_t = DisplayWriter<Display>;
|
||||
|
||||
#define LOG_DISPLAY(prefix, type, obj) \
|
||||
if ((obj) != nullptr) { \
|
||||
@@ -678,7 +788,7 @@ class Display : public PollingComponent {
|
||||
void sort_triangle_points_by_y_(int *x1, int *y1, int *x2, int *y2, int *x3, int *y3);
|
||||
|
||||
DisplayRotation rotation_{DISPLAY_ROTATION_0_DEGREES};
|
||||
optional<display_writer_t> writer_{};
|
||||
display_writer_t writer_{};
|
||||
DisplayPage *page_{nullptr};
|
||||
DisplayPage *previous_page_{nullptr};
|
||||
std::vector<DisplayOnPageChangeTrigger *> on_page_change_triggers_;
|
||||
@@ -709,7 +819,7 @@ template<typename... Ts> class DisplayPageShowAction : public Action<Ts...> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(DisplayPage *, page)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto *page = this->page_.value(x...);
|
||||
if (page != nullptr) {
|
||||
page->show();
|
||||
@@ -721,7 +831,7 @@ template<typename... Ts> class DisplayPageShowNextAction : public Action<Ts...>
|
||||
public:
|
||||
DisplayPageShowNextAction(Display *buffer) : buffer_(buffer) {}
|
||||
|
||||
void play(Ts... x) override { this->buffer_->show_next_page(); }
|
||||
void play(const Ts &...x) override { this->buffer_->show_next_page(); }
|
||||
|
||||
Display *buffer_;
|
||||
};
|
||||
@@ -730,7 +840,7 @@ template<typename... Ts> class DisplayPageShowPrevAction : public Action<Ts...>
|
||||
public:
|
||||
DisplayPageShowPrevAction(Display *buffer) : buffer_(buffer) {}
|
||||
|
||||
void play(Ts... x) override { this->buffer_->show_prev_page(); }
|
||||
void play(const Ts &...x) override { this->buffer_->show_prev_page(); }
|
||||
|
||||
Display *buffer_;
|
||||
};
|
||||
@@ -740,7 +850,7 @@ template<typename... Ts> class DisplayIsDisplayingPageCondition : public Conditi
|
||||
DisplayIsDisplayingPageCondition(Display *parent) : parent_(parent) {}
|
||||
|
||||
void set_page(DisplayPage *page) { this->page_ = page; }
|
||||
bool check(Ts... x) override { return this->parent_->get_active_page() == this->page_; }
|
||||
bool check(const Ts &...x) override { return this->parent_->get_active_page() == this->page_; }
|
||||
|
||||
protected:
|
||||
Display *parent_;
|
||||
|
||||
@@ -10,7 +10,7 @@ template<typename... Ts> class UpAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit UpAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(Ts... x) override { this->menu_->up(); }
|
||||
void play(const Ts &...x) override { this->menu_->up(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
@@ -20,7 +20,7 @@ template<typename... Ts> class DownAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit DownAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(Ts... x) override { this->menu_->down(); }
|
||||
void play(const Ts &...x) override { this->menu_->down(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
@@ -30,7 +30,7 @@ template<typename... Ts> class LeftAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit LeftAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(Ts... x) override { this->menu_->left(); }
|
||||
void play(const Ts &...x) override { this->menu_->left(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
@@ -40,7 +40,7 @@ template<typename... Ts> class RightAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit RightAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(Ts... x) override { this->menu_->right(); }
|
||||
void play(const Ts &...x) override { this->menu_->right(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
@@ -50,7 +50,7 @@ template<typename... Ts> class EnterAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit EnterAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(Ts... x) override { this->menu_->enter(); }
|
||||
void play(const Ts &...x) override { this->menu_->enter(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
@@ -60,7 +60,7 @@ template<typename... Ts> class ShowAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit ShowAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(Ts... x) override { this->menu_->show(); }
|
||||
void play(const Ts &...x) override { this->menu_->show(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
@@ -70,7 +70,7 @@ template<typename... Ts> class HideAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit HideAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(Ts... x) override { this->menu_->hide(); }
|
||||
void play(const Ts &...x) override { this->menu_->hide(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
@@ -80,7 +80,7 @@ template<typename... Ts> class ShowMainAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit ShowMainAction(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
|
||||
void play(Ts... x) override { this->menu_->show_main(); }
|
||||
void play(const Ts &...x) override { this->menu_->show_main(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
@@ -88,7 +88,7 @@ template<typename... Ts> class ShowMainAction : public Action<Ts...> {
|
||||
template<typename... Ts> class IsActiveCondition : public Condition<Ts...> {
|
||||
public:
|
||||
explicit IsActiveCondition(DisplayMenuComponent *menu) : menu_(menu) {}
|
||||
bool check(Ts... x) override { return this->menu_->is_active(); }
|
||||
bool check(const Ts &...x) override { return this->menu_->is_active(); }
|
||||
|
||||
protected:
|
||||
DisplayMenuComponent *menu_;
|
||||
|
||||
@@ -23,7 +23,7 @@ void DS1307Component::dump_config() {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
ESP_LOGCONFIG(TAG, " Timezone: '%s'", this->timezone_.c_str());
|
||||
RealTimeClock::dump_config();
|
||||
}
|
||||
|
||||
float DS1307Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
|
||||
@@ -59,12 +59,12 @@ class DS1307Component : public time::RealTimeClock, public i2c::I2CDevice {
|
||||
|
||||
template<typename... Ts> class WriteAction : public Action<Ts...>, public Parented<DS1307Component> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->write_time(); }
|
||||
void play(const Ts &...x) override { this->parent_->write_time(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class ReadAction : public Action<Ts...>, public Parented<DS1307Component> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->read_time(); }
|
||||
void play(const Ts &...x) override { this->parent_->read_time(); }
|
||||
};
|
||||
} // namespace ds1307
|
||||
} // namespace esphome
|
||||
|
||||
@@ -51,15 +51,15 @@ class DutyTimeSensor : public sensor::Sensor, public PollingComponent {
|
||||
template<typename... Ts> class BaseAction : public Action<Ts...>, public Parented<DutyTimeSensor> {};
|
||||
|
||||
template<typename... Ts> class StartAction : public BaseAction<Ts...> {
|
||||
void play(Ts... x) override { this->parent_->start(); }
|
||||
void play(const Ts &...x) override { this->parent_->start(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class StopAction : public BaseAction<Ts...> {
|
||||
void play(Ts... x) override { this->parent_->stop(); }
|
||||
void play(const Ts &...x) override { this->parent_->stop(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class ResetAction : public BaseAction<Ts...> {
|
||||
void play(Ts... x) override { this->parent_->reset(); }
|
||||
void play(const Ts &...x) override { this->parent_->reset(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class RunningCondition : public Condition<Ts...>, public Parented<DutyTimeSensor> {
|
||||
@@ -67,7 +67,7 @@ template<typename... Ts> class RunningCondition : public Condition<Ts...>, publi
|
||||
explicit RunningCondition(DutyTimeSensor *parent, bool state) : Parented(parent), state_(state) {}
|
||||
|
||||
protected:
|
||||
bool check(Ts... x) override { return this->parent_->is_running() == this->state_; }
|
||||
bool check(const Ts &...x) override { return this->parent_->is_running() == this->state_; }
|
||||
bool state_;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace esphome {
|
||||
namespace es8388 {
|
||||
|
||||
void ADCInputMicSelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
this->parent_->set_adc_input_mic(static_cast<AdcInputMicLine>(this->index_of(value).value()));
|
||||
void ADCInputMicSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_adc_input_mic(static_cast<AdcInputMicLine>(index));
|
||||
}
|
||||
|
||||
} // namespace es8388
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace es8388 {
|
||||
|
||||
class ADCInputMicSelect : public select::Select, public Parented<ES8388> {
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace es8388
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
namespace esphome {
|
||||
namespace es8388 {
|
||||
|
||||
void DacOutputSelect::control(const std::string &value) {
|
||||
this->publish_state(value);
|
||||
this->parent_->set_dac_output(static_cast<DacOutputLine>(this->index_of(value).value()));
|
||||
void DacOutputSelect::control(size_t index) {
|
||||
this->publish_state(index);
|
||||
this->parent_->set_dac_output(static_cast<DacOutputLine>(index));
|
||||
}
|
||||
|
||||
} // namespace es8388
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace es8388 {
|
||||
|
||||
class DacOutputSelect : public select::Select, public Parented<ES8388> {
|
||||
protected:
|
||||
void control(const std::string &value) override;
|
||||
void control(size_t index) override;
|
||||
};
|
||||
|
||||
} // namespace es8388
|
||||
|
||||
@@ -215,17 +215,17 @@ extern ESP32BLE *global_ble;
|
||||
|
||||
template<typename... Ts> class BLEEnabledCondition : public Condition<Ts...> {
|
||||
public:
|
||||
bool check(Ts... x) override { return global_ble->is_active(); }
|
||||
bool check(const Ts &...x) override { return global_ble->is_active(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class BLEEnableAction : public Action<Ts...> {
|
||||
public:
|
||||
void play(Ts... x) override { global_ble->enable(); }
|
||||
void play(const Ts &...x) override { global_ble->enable(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class BLEDisableAction : public Action<Ts...> {
|
||||
public:
|
||||
void play(Ts... x) override { global_ble->disable(); }
|
||||
void play(const Ts &...x) override { global_ble->disable(); }
|
||||
};
|
||||
|
||||
} // namespace esphome::esp32_ble
|
||||
|
||||
@@ -71,7 +71,7 @@ template<typename... Ts> class BLECharacteristicSetValueAction : public Action<T
|
||||
BLECharacteristicSetValueAction(BLECharacteristic *characteristic) : parent_(characteristic) {}
|
||||
TEMPLATABLE_VALUE(std::vector<uint8_t>, buffer)
|
||||
void set_buffer(ByteBuffer buffer) { this->set_buffer(buffer.get_data()); }
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
// If the listener is already set, do nothing
|
||||
if (BLECharacteristicSetValueActionManager::get_instance()->has_listener(this->parent_))
|
||||
return;
|
||||
@@ -96,7 +96,7 @@ template<typename... Ts> class BLECharacteristicSetValueAction : public Action<T
|
||||
template<typename... Ts> class BLECharacteristicNotifyAction : public Action<Ts...> {
|
||||
public:
|
||||
BLECharacteristicNotifyAction(BLECharacteristic *characteristic) : parent_(characteristic) {}
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
#ifdef USE_ESP32_BLE_SERVER_SET_VALUE_ACTION
|
||||
// Call the pre-notify event
|
||||
BLECharacteristicSetValueActionManager::get_instance()->emit_pre_notify(this->parent_);
|
||||
@@ -116,7 +116,7 @@ template<typename... Ts> class BLEDescriptorSetValueAction : public Action<Ts...
|
||||
BLEDescriptorSetValueAction(BLEDescriptor *descriptor) : parent_(descriptor) {}
|
||||
TEMPLATABLE_VALUE(std::vector<uint8_t>, buffer)
|
||||
void set_buffer(ByteBuffer buffer) { this->set_buffer(buffer.get_data()); }
|
||||
void play(Ts... x) override { this->parent_->set_value(this->buffer_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->parent_->set_value(this->buffer_.value(x...)); }
|
||||
|
||||
protected:
|
||||
BLEDescriptor *parent_;
|
||||
|
||||
@@ -96,7 +96,7 @@ template<typename... Ts> class ESP32BLEStartScanAction : public Action<Ts...> {
|
||||
public:
|
||||
ESP32BLEStartScanAction(ESP32BLETracker *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(bool, continuous)
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
this->parent_->set_scan_continuous(this->continuous_.value(x...));
|
||||
this->parent_->start_scan();
|
||||
}
|
||||
@@ -107,7 +107,7 @@ template<typename... Ts> class ESP32BLEStartScanAction : public Action<Ts...> {
|
||||
|
||||
template<typename... Ts> class ESP32BLEStopScanAction : public Action<Ts...>, public Parented<ESP32BLETracker> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->stop_scan(); }
|
||||
void play(const Ts &...x) override { this->parent_->stop_scan(); }
|
||||
};
|
||||
|
||||
} // namespace esphome::esp32_ble_tracker
|
||||
|
||||
@@ -40,7 +40,7 @@ template<typename... Ts> class SetFrequencyAction : public Action<Ts...> {
|
||||
SetFrequencyAction(ESP8266PWM *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(float, frequency);
|
||||
|
||||
void play(Ts... x) {
|
||||
void play(const Ts &...x) {
|
||||
float freq = this->frequency_.value(x...);
|
||||
this->parent_->update_frequency(freq);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ template<typename... Ts> class AdjustAction : public Action<Ts...> {
|
||||
|
||||
TEMPLATABLE_VALUE(float, voltage)
|
||||
|
||||
void play(Ts... x) override { this->ldo_->adjust_voltage(this->voltage_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->ldo_->adjust_voltage(this->voltage_.value(x...)); }
|
||||
|
||||
protected:
|
||||
EspLdo *ldo_;
|
||||
|
||||
@@ -36,7 +36,7 @@ template<typename... Ts> class SendAction : public Action<Ts...>, public Parente
|
||||
void set_wait_for_sent(bool wait_for_sent) { this->flags_.wait_for_sent = wait_for_sent; }
|
||||
void set_continue_on_error(bool continue_on_error) { this->flags_.continue_on_error = continue_on_error; }
|
||||
|
||||
void play_complex(Ts... x) override {
|
||||
void play_complex(const Ts &...x) override {
|
||||
this->num_running_++;
|
||||
send_callback_t send_callback = [this, x...](esp_err_t status) {
|
||||
if (status == ESP_OK) {
|
||||
@@ -67,7 +67,7 @@ template<typename... Ts> class SendAction : public Action<Ts...>, public Parente
|
||||
}
|
||||
}
|
||||
|
||||
void play(Ts... x) override { /* ignore - see play_complex */
|
||||
void play(const Ts &...x) override { /* ignore - see play_complex */
|
||||
}
|
||||
|
||||
void stop() override {
|
||||
@@ -90,7 +90,7 @@ template<typename... Ts> class AddPeerAction : public Action<Ts...>, public Pare
|
||||
TEMPLATABLE_VALUE(peer_address_t, address);
|
||||
|
||||
public:
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
peer_address_t address = this->address_.value(x...);
|
||||
this->parent_->add_peer(address.data());
|
||||
}
|
||||
@@ -100,7 +100,7 @@ template<typename... Ts> class DeletePeerAction : public Action<Ts...>, public P
|
||||
TEMPLATABLE_VALUE(peer_address_t, address);
|
||||
|
||||
public:
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
peer_address_t address = this->address_.value(x...);
|
||||
this->parent_->del_peer(address.data());
|
||||
}
|
||||
@@ -109,7 +109,7 @@ template<typename... Ts> class DeletePeerAction : public Action<Ts...>, public P
|
||||
template<typename... Ts> class SetChannelAction : public Action<Ts...>, public Parented<ESPNowComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, channel)
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
if (this->parent_->is_wifi_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -383,6 +383,7 @@ async def to_code(config):
|
||||
cg.add(var.set_use_address(config[CONF_USE_ADDRESS]))
|
||||
|
||||
if CONF_MANUAL_IP in config:
|
||||
cg.add_define("USE_ETHERNET_MANUAL_IP")
|
||||
cg.add(var.set_manual_ip(manual_ip(config[CONF_MANUAL_IP])))
|
||||
|
||||
# Add compile-time define for PHY types with specific code
|
||||
|
||||
@@ -418,8 +418,6 @@ void EthernetComponent::dump_config() {
|
||||
|
||||
float EthernetComponent::get_setup_priority() const { return setup_priority::WIFI; }
|
||||
|
||||
bool EthernetComponent::can_proceed() { return this->is_connected(); }
|
||||
|
||||
network::IPAddresses EthernetComponent::get_ip_addresses() {
|
||||
network::IPAddresses addresses;
|
||||
esp_netif_ip_info_t ip;
|
||||
@@ -552,11 +550,14 @@ void EthernetComponent::start_connect_() {
|
||||
}
|
||||
|
||||
esp_netif_ip_info_t info;
|
||||
#ifdef USE_ETHERNET_MANUAL_IP
|
||||
if (this->manual_ip_.has_value()) {
|
||||
info.ip = this->manual_ip_->static_ip;
|
||||
info.gw = this->manual_ip_->gateway;
|
||||
info.netmask = this->manual_ip_->subnet;
|
||||
} else {
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
info.ip.addr = 0;
|
||||
info.gw.addr = 0;
|
||||
info.netmask.addr = 0;
|
||||
@@ -577,6 +578,7 @@ void EthernetComponent::start_connect_() {
|
||||
err = esp_netif_set_ip_info(this->eth_netif_, &info);
|
||||
ESPHL_ERROR_CHECK(err, "DHCPC set IP info error");
|
||||
|
||||
#ifdef USE_ETHERNET_MANUAL_IP
|
||||
if (this->manual_ip_.has_value()) {
|
||||
LwIPLock lock;
|
||||
if (this->manual_ip_->dns1.is_set()) {
|
||||
@@ -589,7 +591,9 @@ void EthernetComponent::start_connect_() {
|
||||
d = this->manual_ip_->dns2;
|
||||
dns_setserver(1, &d);
|
||||
}
|
||||
} else {
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
err = esp_netif_dhcpc_start(this->eth_netif_);
|
||||
if (err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) {
|
||||
ESPHL_ERROR_CHECK(err, "DHCPC start error");
|
||||
@@ -687,7 +691,9 @@ void EthernetComponent::set_clk_mode(emac_rmii_clock_mode_t clk_mode) { this->cl
|
||||
void EthernetComponent::add_phy_register(PHYRegister register_value) { this->phy_registers_.push_back(register_value); }
|
||||
#endif
|
||||
void EthernetComponent::set_type(EthernetType type) { this->type_ = type; }
|
||||
#ifdef USE_ETHERNET_MANUAL_IP
|
||||
void EthernetComponent::set_manual_ip(const ManualIP &manual_ip) { this->manual_ip_ = manual_ip; }
|
||||
#endif
|
||||
|
||||
// set_use_address() is guaranteed to be called during component setup by Python code generation,
|
||||
// so use_address_ will always be valid when get_use_address() is called - no fallback needed.
|
||||
|
||||
@@ -58,7 +58,6 @@ class EthernetComponent : public Component {
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
bool can_proceed() override;
|
||||
void on_powerdown() override { powerdown(); }
|
||||
bool is_connected();
|
||||
|
||||
@@ -83,7 +82,9 @@ class EthernetComponent : public Component {
|
||||
void add_phy_register(PHYRegister register_value);
|
||||
#endif
|
||||
void set_type(EthernetType type);
|
||||
#ifdef USE_ETHERNET_MANUAL_IP
|
||||
void set_manual_ip(const ManualIP &manual_ip);
|
||||
#endif
|
||||
void set_fixed_mac(const std::array<uint8_t, 6> &mac) { this->fixed_mac_ = mac; }
|
||||
|
||||
network::IPAddresses get_ip_addresses();
|
||||
@@ -138,7 +139,9 @@ class EthernetComponent : public Component {
|
||||
uint8_t mdc_pin_{23};
|
||||
uint8_t mdio_pin_{18};
|
||||
#endif
|
||||
#ifdef USE_ETHERNET_MANUAL_IP
|
||||
optional<ManualIP> manual_ip_{};
|
||||
#endif
|
||||
uint32_t connect_begin_;
|
||||
|
||||
// Group all uint8_t types together (enums and bools)
|
||||
|
||||
@@ -11,7 +11,7 @@ template<typename... Ts> class TriggerEventAction : public Action<Ts...>, public
|
||||
public:
|
||||
TEMPLATABLE_VALUE(std::string, event_type)
|
||||
|
||||
void play(Ts... x) override { this->parent_->trigger(this->event_type_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->parent_->trigger(this->event_type_.value(x...)); }
|
||||
};
|
||||
|
||||
class EventTrigger : public Trigger<std::string> {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "event.h"
|
||||
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -8,11 +9,11 @@ namespace event {
|
||||
static const char *const TAG = "event";
|
||||
|
||||
void Event::trigger(const std::string &event_type) {
|
||||
// Linear search - faster than std::set for small datasets (1-5 items typical)
|
||||
const std::string *found = nullptr;
|
||||
for (const auto &type : this->types_) {
|
||||
if (type == event_type) {
|
||||
found = &type;
|
||||
// Linear search with strcmp - faster than std::set for small datasets (1-5 items typical)
|
||||
const char *found = nullptr;
|
||||
for (const char *type : this->types_) {
|
||||
if (strcmp(type, event_type.c_str()) == 0) {
|
||||
found = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -20,9 +21,28 @@ void Event::trigger(const std::string &event_type) {
|
||||
ESP_LOGE(TAG, "'%s': invalid event type for trigger(): %s", this->get_name().c_str(), event_type.c_str());
|
||||
return;
|
||||
}
|
||||
last_event_type = found;
|
||||
ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), last_event_type->c_str());
|
||||
this->last_event_type_ = found;
|
||||
ESP_LOGD(TAG, "'%s' Triggered event '%s'", this->get_name().c_str(), this->last_event_type_);
|
||||
this->event_callback_.call(event_type);
|
||||
#if defined(USE_EVENT) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_event(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Event::set_event_types(const FixedVector<const char *> &event_types) {
|
||||
this->types_.init(event_types.size());
|
||||
for (const char *type : event_types) {
|
||||
this->types_.push_back(type);
|
||||
}
|
||||
this->last_event_type_ = nullptr; // Reset when types change
|
||||
}
|
||||
|
||||
void Event::set_event_types(const std::vector<const char *> &event_types) {
|
||||
this->types_.init(event_types.size());
|
||||
for (const char *type : event_types) {
|
||||
this->types_.push_back(type);
|
||||
}
|
||||
this->last_event_type_ = nullptr; // Reset when types change
|
||||
}
|
||||
|
||||
void Event::add_on_event_callback(std::function<void(const std::string &event_type)> &&callback) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/entity_base.h"
|
||||
@@ -22,16 +24,39 @@ namespace event {
|
||||
|
||||
class Event : public EntityBase, public EntityBase_DeviceClass {
|
||||
public:
|
||||
const std::string *last_event_type;
|
||||
|
||||
void trigger(const std::string &event_type);
|
||||
void set_event_types(const std::initializer_list<std::string> &event_types) { this->types_ = event_types; }
|
||||
const FixedVector<std::string> &get_event_types() const { return this->types_; }
|
||||
|
||||
/// Set the event types supported by this event (from initializer list).
|
||||
void set_event_types(std::initializer_list<const char *> event_types) {
|
||||
this->types_ = event_types;
|
||||
this->last_event_type_ = nullptr; // Reset when types change
|
||||
}
|
||||
/// Set the event types supported by this event (from FixedVector).
|
||||
void set_event_types(const FixedVector<const char *> &event_types);
|
||||
/// Set the event types supported by this event (from vector).
|
||||
void set_event_types(const std::vector<const char *> &event_types);
|
||||
|
||||
// Deleted overloads to catch incorrect std::string usage at compile time with clear error messages
|
||||
void set_event_types(std::initializer_list<std::string> event_types) = delete;
|
||||
void set_event_types(const FixedVector<std::string> &event_types) = delete;
|
||||
void set_event_types(const std::vector<std::string> &event_types) = delete;
|
||||
|
||||
/// Return the event types supported by this event.
|
||||
const FixedVector<const char *> &get_event_types() const { return this->types_; }
|
||||
|
||||
/// Return the last triggered event type (pointer to string in types_), or nullptr if no event triggered yet.
|
||||
const char *get_last_event_type() const { return this->last_event_type_; }
|
||||
|
||||
void add_on_event_callback(std::function<void(const std::string &event_type)> &&callback);
|
||||
|
||||
protected:
|
||||
CallbackManager<void(const std::string &event_type)> event_callback_;
|
||||
FixedVector<std::string> types_;
|
||||
FixedVector<const char *> types_;
|
||||
|
||||
private:
|
||||
/// Last triggered event type - must point to entry in types_ to ensure valid lifetime.
|
||||
/// Set by trigger() after validation, reset to nullptr when types_ changes.
|
||||
const char *last_event_type_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace event
|
||||
|
||||
@@ -17,35 +17,35 @@ class LedTrigger : public Trigger<bool> {
|
||||
class CustomTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
explicit CustomTrigger(EZOSensor *ezo) {
|
||||
ezo->add_custom_callback([this](std::string value) { this->trigger(std::move(value)); });
|
||||
ezo->add_custom_callback([this](const std::string &value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
|
||||
class TTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
explicit TTrigger(EZOSensor *ezo) {
|
||||
ezo->add_t_callback([this](std::string value) { this->trigger(std::move(value)); });
|
||||
ezo->add_t_callback([this](const std::string &value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
|
||||
class CalibrationTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
explicit CalibrationTrigger(EZOSensor *ezo) {
|
||||
ezo->add_calibration_callback([this](std::string value) { this->trigger(std::move(value)); });
|
||||
ezo->add_calibration_callback([this](const std::string &value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
|
||||
class SlopeTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
explicit SlopeTrigger(EZOSensor *ezo) {
|
||||
ezo->add_slope_callback([this](std::string value) { this->trigger(std::move(value)); });
|
||||
ezo->add_slope_callback([this](const std::string &value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
|
||||
class DeviceInformationTrigger : public Trigger<std::string> {
|
||||
public:
|
||||
explicit DeviceInformationTrigger(EZOSensor *ezo) {
|
||||
ezo->add_device_infomation_callback([this](std::string value) { this->trigger(std::move(value)); });
|
||||
ezo->add_device_infomation_callback([this](const std::string &value) { this->trigger(value); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ template<typename... Ts> class EzoPMPFindAction : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPFindAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(Ts... x) override { this->ezopmp_->find(); }
|
||||
void play(const Ts &...x) override { this->ezopmp_->find(); }
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
@@ -129,7 +129,7 @@ template<typename... Ts> class EzoPMPDoseContinuouslyAction : public Action<Ts..
|
||||
public:
|
||||
EzoPMPDoseContinuouslyAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(Ts... x) override { this->ezopmp_->dose_continuously(); }
|
||||
void play(const Ts &...x) override { this->ezopmp_->dose_continuously(); }
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
@@ -139,7 +139,7 @@ template<typename... Ts> class EzoPMPDoseVolumeAction : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPDoseVolumeAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(Ts... x) override { this->ezopmp_->dose_volume(this->volume_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->ezopmp_->dose_volume(this->volume_.value(x...)); }
|
||||
TEMPLATABLE_VALUE(double, volume)
|
||||
|
||||
protected:
|
||||
@@ -150,7 +150,7 @@ template<typename... Ts> class EzoPMPDoseVolumeOverTimeAction : public Action<Ts
|
||||
public:
|
||||
EzoPMPDoseVolumeOverTimeAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
this->ezopmp_->dose_volume_over_time(this->volume_.value(x...), this->duration_.value(x...));
|
||||
}
|
||||
TEMPLATABLE_VALUE(double, volume)
|
||||
@@ -164,7 +164,7 @@ template<typename... Ts> class EzoPMPDoseWithConstantFlowRateAction : public Act
|
||||
public:
|
||||
EzoPMPDoseWithConstantFlowRateAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
this->ezopmp_->dose_with_constant_flow_rate(this->volume_.value(x...), this->duration_.value(x...));
|
||||
}
|
||||
TEMPLATABLE_VALUE(double, volume)
|
||||
@@ -178,7 +178,7 @@ template<typename... Ts> class EzoPMPSetCalibrationVolumeAction : public Action<
|
||||
public:
|
||||
EzoPMPSetCalibrationVolumeAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(Ts... x) override { this->ezopmp_->set_calibration_volume(this->volume_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->ezopmp_->set_calibration_volume(this->volume_.value(x...)); }
|
||||
TEMPLATABLE_VALUE(double, volume)
|
||||
|
||||
protected:
|
||||
@@ -189,7 +189,7 @@ template<typename... Ts> class EzoPMPClearTotalVolumeDispensedAction : public Ac
|
||||
public:
|
||||
EzoPMPClearTotalVolumeDispensedAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(Ts... x) override { this->ezopmp_->clear_total_volume_dosed(); }
|
||||
void play(const Ts &...x) override { this->ezopmp_->clear_total_volume_dosed(); }
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
@@ -199,7 +199,7 @@ template<typename... Ts> class EzoPMPClearCalibrationAction : public Action<Ts..
|
||||
public:
|
||||
EzoPMPClearCalibrationAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(Ts... x) override { this->ezopmp_->clear_calibration(); }
|
||||
void play(const Ts &...x) override { this->ezopmp_->clear_calibration(); }
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
@@ -209,7 +209,7 @@ template<typename... Ts> class EzoPMPPauseDosingAction : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPPauseDosingAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(Ts... x) override { this->ezopmp_->pause_dosing(); }
|
||||
void play(const Ts &...x) override { this->ezopmp_->pause_dosing(); }
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
@@ -219,7 +219,7 @@ template<typename... Ts> class EzoPMPStopDosingAction : public Action<Ts...> {
|
||||
public:
|
||||
EzoPMPStopDosingAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(Ts... x) override { this->ezopmp_->stop_dosing(); }
|
||||
void play(const Ts &...x) override { this->ezopmp_->stop_dosing(); }
|
||||
|
||||
protected:
|
||||
EzoPMP *ezopmp_;
|
||||
@@ -229,7 +229,7 @@ template<typename... Ts> class EzoPMPChangeI2CAddressAction : public Action<Ts..
|
||||
public:
|
||||
EzoPMPChangeI2CAddressAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(Ts... x) override { this->ezopmp_->change_i2c_address(this->address_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->ezopmp_->change_i2c_address(this->address_.value(x...)); }
|
||||
TEMPLATABLE_VALUE(int, address)
|
||||
|
||||
protected:
|
||||
@@ -240,7 +240,7 @@ template<typename... Ts> class EzoPMPArbitraryCommandAction : public Action<Ts..
|
||||
public:
|
||||
EzoPMPArbitraryCommandAction(EzoPMP *ezopmp) : ezopmp_(ezopmp) {}
|
||||
|
||||
void play(Ts... x) override { this->ezopmp_->exec_arbitrary_command(this->command_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->ezopmp_->exec_arbitrary_command(this->command_.value(x...)); }
|
||||
TEMPLATABLE_VALUE(std::string, command)
|
||||
|
||||
protected:
|
||||
|
||||
@@ -15,7 +15,7 @@ template<typename... Ts> class TurnOnAction : public Action<Ts...> {
|
||||
TEMPLATABLE_VALUE(int, speed)
|
||||
TEMPLATABLE_VALUE(FanDirection, direction)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto call = this->state_->turn_on();
|
||||
if (this->oscillating_.has_value()) {
|
||||
call.set_oscillating(this->oscillating_.value(x...));
|
||||
@@ -36,7 +36,7 @@ template<typename... Ts> class TurnOffAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit TurnOffAction(Fan *state) : state_(state) {}
|
||||
|
||||
void play(Ts... x) override { this->state_->turn_off().perform(); }
|
||||
void play(const Ts &...x) override { this->state_->turn_off().perform(); }
|
||||
|
||||
Fan *state_;
|
||||
};
|
||||
@@ -45,7 +45,7 @@ template<typename... Ts> class ToggleAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit ToggleAction(Fan *state) : state_(state) {}
|
||||
|
||||
void play(Ts... x) override { this->state_->toggle().perform(); }
|
||||
void play(const Ts &...x) override { this->state_->toggle().perform(); }
|
||||
|
||||
Fan *state_;
|
||||
};
|
||||
@@ -56,7 +56,7 @@ template<typename... Ts> class CycleSpeedAction : public Action<Ts...> {
|
||||
|
||||
TEMPLATABLE_VALUE(bool, no_off_cycle)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
// check to see if fan supports speeds and is on
|
||||
if (this->state_->get_traits().supported_speed_count()) {
|
||||
if (this->state_->state) {
|
||||
@@ -97,7 +97,7 @@ template<typename... Ts> class CycleSpeedAction : public Action<Ts...> {
|
||||
template<typename... Ts> class FanIsOnCondition : public Condition<Ts...> {
|
||||
public:
|
||||
explicit FanIsOnCondition(Fan *state) : state_(state) {}
|
||||
bool check(Ts... x) override { return this->state_->state; }
|
||||
bool check(const Ts &...x) override { return this->state_->state; }
|
||||
|
||||
protected:
|
||||
Fan *state_;
|
||||
@@ -105,7 +105,7 @@ template<typename... Ts> class FanIsOnCondition : public Condition<Ts...> {
|
||||
template<typename... Ts> class FanIsOffCondition : public Condition<Ts...> {
|
||||
public:
|
||||
explicit FanIsOffCondition(Fan *state) : state_(state) {}
|
||||
bool check(Ts... x) override { return !this->state_->state; }
|
||||
bool check(const Ts &...x) override { return !this->state_->state; }
|
||||
|
||||
protected:
|
||||
Fan *state_;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "fan.h"
|
||||
#include "esphome/core/defines.h"
|
||||
#include "esphome/core/controller_registry.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -181,6 +183,9 @@ void Fan::publish_state() {
|
||||
ESP_LOGD(TAG, " Preset Mode: %s", preset);
|
||||
}
|
||||
this->state_callback_.call();
|
||||
#if defined(USE_FAN) && defined(USE_CONTROLLER_REGISTRY)
|
||||
ControllerRegistry::notify_fan_update(this);
|
||||
#endif
|
||||
this->save_state_();
|
||||
}
|
||||
|
||||
@@ -231,7 +236,7 @@ void Fan::save_state_() {
|
||||
state.direction = this->direction;
|
||||
|
||||
const char *preset = this->get_preset_mode();
|
||||
if (traits.supports_preset_modes() && preset != nullptr) {
|
||||
if (preset != nullptr) {
|
||||
const auto &preset_modes = traits.supported_preset_modes();
|
||||
// Find index of current preset mode (pointer comparison is safe since preset is from traits)
|
||||
for (size_t i = 0; i < preset_modes.size(); i++) {
|
||||
|
||||
@@ -273,7 +273,7 @@ template<typename... Ts> class EnrollmentAction : public Action<Ts...>, public P
|
||||
TEMPLATABLE_VALUE(uint16_t, finger_id)
|
||||
TEMPLATABLE_VALUE(uint8_t, num_scans)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto finger_id = this->finger_id_.value(x...);
|
||||
auto num_scans = this->num_scans_.value(x...);
|
||||
if (num_scans) {
|
||||
@@ -287,14 +287,14 @@ template<typename... Ts> class EnrollmentAction : public Action<Ts...>, public P
|
||||
template<typename... Ts>
|
||||
class CancelEnrollmentAction : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->finish_enrollment(1); }
|
||||
void play(const Ts &...x) override { this->parent_->finish_enrollment(1); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class DeleteAction : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint16_t, finger_id)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto finger_id = this->finger_id_.value(x...);
|
||||
this->parent_->delete_fingerprint(finger_id);
|
||||
}
|
||||
@@ -302,14 +302,14 @@ template<typename... Ts> class DeleteAction : public Action<Ts...>, public Paren
|
||||
|
||||
template<typename... Ts> class DeleteAllAction : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->delete_all_fingerprints(); }
|
||||
void play(const Ts &...x) override { this->parent_->delete_all_fingerprints(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class LEDControlAction : public Action<Ts...>, public Parented<FingerprintGrowComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(bool, state)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto state = this->state_.value(x...);
|
||||
this->parent_->led_control(state);
|
||||
}
|
||||
@@ -322,7 +322,7 @@ template<typename... Ts> class AuraLEDControlAction : public Action<Ts...>, publ
|
||||
TEMPLATABLE_VALUE(uint8_t, color)
|
||||
TEMPLATABLE_VALUE(uint8_t, count)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto state = this->state_.value(x...);
|
||||
auto speed = this->speed_.value(x...);
|
||||
auto color = this->color_.value(x...);
|
||||
|
||||
@@ -62,7 +62,6 @@ void GDK101Component::dump_config() {
|
||||
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
|
||||
}
|
||||
#ifdef USE_SENSOR
|
||||
LOG_SENSOR(" ", "Firmware Version", this->fw_version_sensor_);
|
||||
LOG_SENSOR(" ", "Average Radaition Dose per 1 minute", this->rad_1m_sensor_);
|
||||
LOG_SENSOR(" ", "Average Radaition Dose per 10 minutes", this->rad_10m_sensor_);
|
||||
LOG_SENSOR(" ", "Status", this->status_sensor_);
|
||||
@@ -72,6 +71,10 @@ void GDK101Component::dump_config() {
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
LOG_BINARY_SENSOR(" ", "Vibration Status", this->vibration_binary_sensor_);
|
||||
#endif // USE_BINARY_SENSOR
|
||||
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
LOG_TEXT_SENSOR(" ", "Firmware Version", this->fw_version_text_sensor_);
|
||||
#endif // USE_TEXT_SENSOR
|
||||
}
|
||||
|
||||
float GDK101Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||
@@ -153,18 +156,18 @@ bool GDK101Component::read_status_(uint8_t *data) {
|
||||
}
|
||||
|
||||
bool GDK101Component::read_fw_version_(uint8_t *data) {
|
||||
#ifdef USE_SENSOR
|
||||
if (this->fw_version_sensor_ != nullptr) {
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
if (this->fw_version_text_sensor_ != nullptr) {
|
||||
if (!this->read_bytes(GDK101_REG_READ_FIRMWARE, data, 2)) {
|
||||
ESP_LOGE(TAG, "Updating GDK101 failed!");
|
||||
return false;
|
||||
}
|
||||
|
||||
const float fw_version = data[0] + (data[1] / 10.0f);
|
||||
const std::string fw_version_str = str_sprintf("%d.%d", data[0], data[1]);
|
||||
|
||||
this->fw_version_sensor_->publish_state(fw_version);
|
||||
this->fw_version_text_sensor_->publish_state(fw_version_str);
|
||||
}
|
||||
#endif // USE_SENSOR
|
||||
#endif // USE_TEXT_SENSOR
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#endif // USE_BINARY_SENSOR
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#endif // USE_TEXT_SENSOR
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
|
||||
namespace esphome {
|
||||
@@ -25,12 +28,14 @@ class GDK101Component : public PollingComponent, public i2c::I2CDevice {
|
||||
SUB_SENSOR(rad_1m)
|
||||
SUB_SENSOR(rad_10m)
|
||||
SUB_SENSOR(status)
|
||||
SUB_SENSOR(fw_version)
|
||||
SUB_SENSOR(measurement_duration)
|
||||
#endif // USE_SENSOR
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
SUB_BINARY_SENSOR(vibration)
|
||||
#endif // USE_BINARY_SENSOR
|
||||
#ifdef USE_TEXT_SENSOR
|
||||
SUB_TEXT_SENSOR(fw_version)
|
||||
#endif // USE_TEXT_SENSOR
|
||||
|
||||
public:
|
||||
void setup() override;
|
||||
|
||||
@@ -40,9 +40,8 @@ CONFIG_SCHEMA = cv.Schema(
|
||||
device_class=DEVICE_CLASS_EMPTY,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
cv.Optional(CONF_VERSION): sensor.sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
accuracy_decimals=1,
|
||||
cv.Optional(CONF_VERSION): cv.invalid(
|
||||
"The 'version' option has been moved to the `text_sensor` component."
|
||||
),
|
||||
cv.Optional(CONF_STATUS): sensor.sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
@@ -71,10 +70,6 @@ async def to_code(config):
|
||||
sens = await sensor.new_sensor(radiation_dose_per_10m)
|
||||
cg.add(hub.set_rad_10m_sensor(sens))
|
||||
|
||||
if version_config := config.get(CONF_VERSION):
|
||||
sens = await sensor.new_sensor(version_config)
|
||||
cg.add(hub.set_fw_version_sensor(sens))
|
||||
|
||||
if status_config := config.get(CONF_STATUS):
|
||||
sens = await sensor.new_sensor(status_config)
|
||||
cg.add(hub.set_status_sensor(sens))
|
||||
|
||||
23
esphome/components/gdk101/text_sensor.py
Normal file
23
esphome/components/gdk101/text_sensor.py
Normal file
@@ -0,0 +1,23 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_VERSION, ENTITY_CATEGORY_DIAGNOSTIC, ICON_CHIP
|
||||
|
||||
from . import CONF_GDK101_ID, GDK101Component
|
||||
|
||||
DEPENDENCIES = ["gdk101"]
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_GDK101_ID): cv.use_id(GDK101Component),
|
||||
cv.Required(CONF_VERSION): text_sensor.text_sensor_schema(
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC, icon=ICON_CHIP
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_GDK101_ID])
|
||||
var = await text_sensor.new_text_sensor(config[CONF_VERSION])
|
||||
cg.add(hub.set_fw_version_text_sensor(var))
|
||||
@@ -134,7 +134,7 @@ template<class C, typename... Ts> class GlobalVarSetAction : public Action<Ts...
|
||||
|
||||
TEMPLATABLE_VALUE(T, value);
|
||||
|
||||
void play(Ts... x) override { this->parent_->value() = this->value_.value(x...); }
|
||||
void play(const Ts &...x) override { this->parent_->value() = this->value_.value(x...); }
|
||||
|
||||
protected:
|
||||
C *parent_;
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_VOLTAGE
|
||||
from esphome.const import CONF_ID, CONF_MODEL, CONF_VOLTAGE
|
||||
|
||||
CODEOWNERS = ["@jesserockz"]
|
||||
CODEOWNERS = ["@jesserockz", "@sebydocky"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
MULTI_CONF = True
|
||||
|
||||
gp8403_ns = cg.esphome_ns.namespace("gp8403")
|
||||
GP8403 = gp8403_ns.class_("GP8403", cg.Component, i2c.I2CDevice)
|
||||
GP8403Component = gp8403_ns.class_("GP8403Component", cg.Component, i2c.I2CDevice)
|
||||
|
||||
GP8403Voltage = gp8403_ns.enum("GP8403Voltage")
|
||||
GP8403Model = gp8403_ns.enum("GP8403Model")
|
||||
|
||||
CONF_GP8403_ID = "gp8403_id"
|
||||
|
||||
MODELS = {
|
||||
"GP8403": GP8403Model.GP8403,
|
||||
"GP8413": GP8403Model.GP8413,
|
||||
}
|
||||
|
||||
VOLTAGES = {
|
||||
"5V": GP8403Voltage.GP8403_VOLTAGE_5V,
|
||||
"10V": GP8403Voltage.GP8403_VOLTAGE_10V,
|
||||
@@ -22,7 +28,8 @@ VOLTAGES = {
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(GP8403),
|
||||
cv.GenerateID(): cv.declare_id(GP8403Component),
|
||||
cv.Optional(CONF_MODEL, default="GP8403"): cv.enum(MODELS, upper=True),
|
||||
cv.Required(CONF_VOLTAGE): cv.enum(VOLTAGES, upper=True),
|
||||
}
|
||||
)
|
||||
@@ -35,5 +42,5 @@ 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_model(config[CONF_MODEL]))
|
||||
cg.add(var.set_voltage(config[CONF_VOLTAGE]))
|
||||
|
||||
@@ -8,16 +8,48 @@ namespace gp8403 {
|
||||
static const char *const TAG = "gp8403";
|
||||
|
||||
static const uint8_t RANGE_REGISTER = 0x01;
|
||||
static const uint8_t OUTPUT_REGISTER = 0x02;
|
||||
|
||||
void GP8403::setup() { this->write_register(RANGE_REGISTER, (uint8_t *) (&this->voltage_), 1); }
|
||||
const LogString *model_to_string(GP8403Model model) {
|
||||
switch (model) {
|
||||
case GP8403Model::GP8403:
|
||||
return LOG_STR("GP8403");
|
||||
case GP8403Model::GP8413:
|
||||
return LOG_STR("GP8413");
|
||||
}
|
||||
return LOG_STR("Unknown");
|
||||
};
|
||||
|
||||
void GP8403::dump_config() {
|
||||
void GP8403Component::setup() { this->write_register(RANGE_REGISTER, (uint8_t *) (&this->voltage_), 1); }
|
||||
|
||||
void GP8403Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"GP8403:\n"
|
||||
" Voltage: %dV",
|
||||
this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10);
|
||||
" Voltage: %dV\n"
|
||||
" Model: %s",
|
||||
this->voltage_ == GP8403_VOLTAGE_5V ? 5 : 10, LOG_STR_ARG(model_to_string(this->model_)));
|
||||
LOG_I2C_DEVICE(this);
|
||||
}
|
||||
|
||||
void GP8403Component::write_state(float state, uint8_t channel) {
|
||||
uint16_t val = 0;
|
||||
switch (this->model_) {
|
||||
case GP8403Model::GP8403:
|
||||
val = ((uint16_t) (4095 * state)) << 4;
|
||||
break;
|
||||
case GP8403Model::GP8413:
|
||||
val = ((uint16_t) (32767 * state)) << 1;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unknown model %s", LOG_STR_ARG(model_to_string(this->model_)));
|
||||
return;
|
||||
}
|
||||
ESP_LOGV(TAG, "Calculated DAC value: %" PRIu16, val);
|
||||
i2c::ErrorCode err = this->write_register(OUTPUT_REGISTER + (2 * channel), (uint8_t *) &val, 2);
|
||||
if (err != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Error writing to %s, code %d", LOG_STR_ARG(model_to_string(this->model_)), err);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace gp8403
|
||||
} // namespace esphome
|
||||
|
||||
@@ -11,15 +11,24 @@ enum GP8403Voltage {
|
||||
GP8403_VOLTAGE_10V = 0x11,
|
||||
};
|
||||
|
||||
class GP8403 : public Component, public i2c::I2CDevice {
|
||||
enum GP8403Model {
|
||||
GP8403,
|
||||
GP8413,
|
||||
};
|
||||
|
||||
class GP8403Component : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
|
||||
float get_setup_priority() const override { return setup_priority::DATA; }
|
||||
void set_model(GP8403Model model) { this->model_ = model; }
|
||||
void set_voltage(gp8403::GP8403Voltage voltage) { this->voltage_ = voltage; }
|
||||
|
||||
void write_state(float state, uint8_t channel);
|
||||
|
||||
protected:
|
||||
GP8403Voltage voltage_;
|
||||
GP8403Model model_{GP8403Model::GP8403};
|
||||
};
|
||||
|
||||
} // namespace gp8403
|
||||
|
||||
@@ -3,7 +3,7 @@ from esphome.components import i2c, output
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
|
||||
from .. import CONF_GP8403_ID, GP8403, gp8403_ns
|
||||
from .. import CONF_GP8403_ID, GP8403Component, gp8403_ns
|
||||
|
||||
DEPENDENCIES = ["gp8403"]
|
||||
|
||||
@@ -14,7 +14,7 @@ GP8403Output = gp8403_ns.class_(
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(GP8403Output),
|
||||
cv.GenerateID(CONF_GP8403_ID): cv.use_id(GP8403),
|
||||
cv.GenerateID(CONF_GP8403_ID): cv.use_id(GP8403Component),
|
||||
cv.Required(CONF_CHANNEL): cv.int_range(min=0, max=1),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA)
|
||||
|
||||
@@ -7,8 +7,6 @@ namespace gp8403 {
|
||||
|
||||
static const char *const TAG = "gp8403.output";
|
||||
|
||||
static const uint8_t OUTPUT_REGISTER = 0x02;
|
||||
|
||||
void GP8403Output::dump_config() {
|
||||
ESP_LOGCONFIG(TAG,
|
||||
"GP8403 Output:\n"
|
||||
@@ -16,13 +14,7 @@ void GP8403Output::dump_config() {
|
||||
this->channel_);
|
||||
}
|
||||
|
||||
void GP8403Output::write_state(float state) {
|
||||
uint16_t value = ((uint16_t) (state * 4095)) << 4;
|
||||
i2c::ErrorCode err = this->parent_->write_register(OUTPUT_REGISTER + (2 * this->channel_), (uint8_t *) &value, 2);
|
||||
if (err != i2c::ERROR_OK) {
|
||||
ESP_LOGE(TAG, "Error writing to GP8403, code %d", err);
|
||||
}
|
||||
}
|
||||
void GP8403Output::write_state(float state) { this->parent_->write_state(state, this->channel_); }
|
||||
|
||||
} // namespace gp8403
|
||||
} // namespace esphome
|
||||
|
||||
@@ -8,13 +8,11 @@
|
||||
namespace esphome {
|
||||
namespace gp8403 {
|
||||
|
||||
class GP8403Output : public Component, public output::FloatOutput, public Parented<GP8403> {
|
||||
class GP8403Output : public Component, public output::FloatOutput, public Parented<GP8403Component> {
|
||||
public:
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::DATA - 1; }
|
||||
|
||||
void set_channel(uint8_t channel) { this->channel_ = channel; }
|
||||
|
||||
void write_state(float state) override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -235,7 +235,7 @@ void GraphLegend::init(Graph *g) {
|
||||
std::string valstr =
|
||||
value_accuracy_to_string(trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals());
|
||||
if (this->units_) {
|
||||
valstr += trace->sensor_->get_unit_of_measurement();
|
||||
valstr += trace->sensor_->get_unit_of_measurement_ref();
|
||||
}
|
||||
this->font_value_->measure(valstr.c_str(), &fw, &fos, &fbl, &fh);
|
||||
if (fw > valw)
|
||||
@@ -371,7 +371,7 @@ void Graph::draw_legend(display::Display *buff, uint16_t x_offset, uint16_t y_of
|
||||
std::string valstr =
|
||||
value_accuracy_to_string(trace->sensor_->get_state(), trace->sensor_->get_accuracy_decimals());
|
||||
if (legend_->units_) {
|
||||
valstr += trace->sensor_->get_unit_of_measurement();
|
||||
valstr += trace->sensor_->get_unit_of_measurement_ref();
|
||||
}
|
||||
buff->printf(xv, yv, legend_->font_value_, trace->get_line_color(), TextAlign::TOP_CENTER, "%s", valstr.c_str());
|
||||
ESP_LOGV(TAG, " value: %s", valstr.c_str());
|
||||
|
||||
@@ -168,7 +168,7 @@ class GROVETB6612FNGMotorRunAction : public Action<Ts...>, public Parented<Grove
|
||||
TEMPLATABLE_VALUE(uint8_t, channel)
|
||||
TEMPLATABLE_VALUE(uint16_t, speed)
|
||||
|
||||
void play(Ts... x) override {
|
||||
void play(const Ts &...x) override {
|
||||
auto channel = this->channel_.value(x...);
|
||||
auto speed = this->speed_.value(x...);
|
||||
this->parent_->dc_motor_run(channel, speed);
|
||||
@@ -180,7 +180,7 @@ class GROVETB6612FNGMotorBrakeAction : public Action<Ts...>, public Parented<Gro
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, channel)
|
||||
|
||||
void play(Ts... x) override { this->parent_->dc_motor_brake(this->channel_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->parent_->dc_motor_brake(this->channel_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
@@ -188,19 +188,19 @@ class GROVETB6612FNGMotorStopAction : public Action<Ts...>, public Parented<Grov
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, channel)
|
||||
|
||||
void play(Ts... x) override { this->parent_->dc_motor_stop(this->channel_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->parent_->dc_motor_stop(this->channel_.value(x...)); }
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class GROVETB6612FNGMotorStandbyAction : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->standby(); }
|
||||
void play(const Ts &...x) override { this->parent_->standby(); }
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
class GROVETB6612FNGMotorNoStandbyAction : public Action<Ts...>, public Parented<GroveMotorDriveTB6612FNG> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->not_standby(); }
|
||||
void play(const Ts &...x) override { this->parent_->not_standby(); }
|
||||
};
|
||||
|
||||
template<typename... Ts>
|
||||
@@ -208,7 +208,7 @@ class GROVETB6612FNGMotorChangeAddressAction : public Action<Ts...>, public Pare
|
||||
public:
|
||||
TEMPLATABLE_VALUE(uint8_t, address)
|
||||
|
||||
void play(Ts... x) override { this->parent_->set_i2c_addr(this->address_.value(x...)); }
|
||||
void play(const Ts &...x) override { this->parent_->set_i2c_addr(this->address_.value(x...)); }
|
||||
};
|
||||
|
||||
} // namespace grove_tb6612fng
|
||||
|
||||
@@ -34,8 +34,8 @@ void GT911Touchscreen::setup() {
|
||||
this->interrupt_pin_->digital_write(false);
|
||||
}
|
||||
delay(2);
|
||||
this->reset_pin_->digital_write(true); // wait 50ms after reset
|
||||
this->set_timeout(50, [this] { this->setup_internal_(); });
|
||||
this->reset_pin_->digital_write(true); // wait at least T3+T4 ms as per the datasheet
|
||||
this->set_timeout(5 + 50 + 1, [this] { this->setup_internal_(); });
|
||||
return;
|
||||
}
|
||||
this->setup_internal_();
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace haier {
|
||||
template<typename... Ts> class DisplayOnAction : public Action<Ts...> {
|
||||
public:
|
||||
DisplayOnAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->set_display_state(true); }
|
||||
void play(const Ts &...x) { this->parent_->set_display_state(true); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
@@ -19,7 +19,7 @@ template<typename... Ts> class DisplayOnAction : public Action<Ts...> {
|
||||
template<typename... Ts> class DisplayOffAction : public Action<Ts...> {
|
||||
public:
|
||||
DisplayOffAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->set_display_state(false); }
|
||||
void play(const Ts &...x) { this->parent_->set_display_state(false); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
@@ -28,7 +28,7 @@ template<typename... Ts> class DisplayOffAction : public Action<Ts...> {
|
||||
template<typename... Ts> class BeeperOnAction : public Action<Ts...> {
|
||||
public:
|
||||
BeeperOnAction(HonClimate *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->set_beeper_state(true); }
|
||||
void play(const Ts &...x) { this->parent_->set_beeper_state(true); }
|
||||
|
||||
protected:
|
||||
HonClimate *parent_;
|
||||
@@ -37,7 +37,7 @@ template<typename... Ts> class BeeperOnAction : public Action<Ts...> {
|
||||
template<typename... Ts> class BeeperOffAction : public Action<Ts...> {
|
||||
public:
|
||||
BeeperOffAction(HonClimate *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->set_beeper_state(false); }
|
||||
void play(const Ts &...x) { this->parent_->set_beeper_state(false); }
|
||||
|
||||
protected:
|
||||
HonClimate *parent_;
|
||||
@@ -47,7 +47,7 @@ template<typename... Ts> class VerticalAirflowAction : public Action<Ts...> {
|
||||
public:
|
||||
VerticalAirflowAction(HonClimate *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(hon_protocol::VerticalSwingMode, direction)
|
||||
void play(Ts... x) { this->parent_->set_vertical_airflow(this->direction_.value(x...)); }
|
||||
void play(const Ts &...x) { this->parent_->set_vertical_airflow(this->direction_.value(x...)); }
|
||||
|
||||
protected:
|
||||
HonClimate *parent_;
|
||||
@@ -57,7 +57,7 @@ template<typename... Ts> class HorizontalAirflowAction : public Action<Ts...> {
|
||||
public:
|
||||
HorizontalAirflowAction(HonClimate *parent) : parent_(parent) {}
|
||||
TEMPLATABLE_VALUE(hon_protocol::HorizontalSwingMode, direction)
|
||||
void play(Ts... x) { this->parent_->set_horizontal_airflow(this->direction_.value(x...)); }
|
||||
void play(const Ts &...x) { this->parent_->set_horizontal_airflow(this->direction_.value(x...)); }
|
||||
|
||||
protected:
|
||||
HonClimate *parent_;
|
||||
@@ -66,7 +66,7 @@ template<typename... Ts> class HorizontalAirflowAction : public Action<Ts...> {
|
||||
template<typename... Ts> class HealthOnAction : public Action<Ts...> {
|
||||
public:
|
||||
HealthOnAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->set_health_mode(true); }
|
||||
void play(const Ts &...x) { this->parent_->set_health_mode(true); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
@@ -75,7 +75,7 @@ template<typename... Ts> class HealthOnAction : public Action<Ts...> {
|
||||
template<typename... Ts> class HealthOffAction : public Action<Ts...> {
|
||||
public:
|
||||
HealthOffAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->set_health_mode(false); }
|
||||
void play(const Ts &...x) { this->parent_->set_health_mode(false); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
@@ -84,7 +84,7 @@ template<typename... Ts> class HealthOffAction : public Action<Ts...> {
|
||||
template<typename... Ts> class StartSelfCleaningAction : public Action<Ts...> {
|
||||
public:
|
||||
StartSelfCleaningAction(HonClimate *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->start_self_cleaning(); }
|
||||
void play(const Ts &...x) { this->parent_->start_self_cleaning(); }
|
||||
|
||||
protected:
|
||||
HonClimate *parent_;
|
||||
@@ -93,7 +93,7 @@ template<typename... Ts> class StartSelfCleaningAction : public Action<Ts...> {
|
||||
template<typename... Ts> class StartSteriCleaningAction : public Action<Ts...> {
|
||||
public:
|
||||
StartSteriCleaningAction(HonClimate *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->start_steri_cleaning(); }
|
||||
void play(const Ts &...x) { this->parent_->start_steri_cleaning(); }
|
||||
|
||||
protected:
|
||||
HonClimate *parent_;
|
||||
@@ -102,7 +102,7 @@ template<typename... Ts> class StartSteriCleaningAction : public Action<Ts...> {
|
||||
template<typename... Ts> class PowerOnAction : public Action<Ts...> {
|
||||
public:
|
||||
PowerOnAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->send_power_on_command(); }
|
||||
void play(const Ts &...x) { this->parent_->send_power_on_command(); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
@@ -111,7 +111,7 @@ template<typename... Ts> class PowerOnAction : public Action<Ts...> {
|
||||
template<typename... Ts> class PowerOffAction : public Action<Ts...> {
|
||||
public:
|
||||
PowerOffAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->send_power_off_command(); }
|
||||
void play(const Ts &...x) { this->parent_->send_power_off_command(); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
@@ -120,7 +120,7 @@ template<typename... Ts> class PowerOffAction : public Action<Ts...> {
|
||||
template<typename... Ts> class PowerToggleAction : public Action<Ts...> {
|
||||
public:
|
||||
PowerToggleAction(HaierClimateBase *parent) : parent_(parent) {}
|
||||
void play(Ts... x) { this->parent_->toggle_power(); }
|
||||
void play(const Ts &...x) { this->parent_->toggle_power(); }
|
||||
|
||||
protected:
|
||||
HaierClimateBase *parent_;
|
||||
|
||||
@@ -49,7 +49,7 @@ template<typename... Ts> class BrakeAction : public Action<Ts...> {
|
||||
public:
|
||||
explicit BrakeAction(HBridgeFan *parent) : parent_(parent) {}
|
||||
|
||||
void play(Ts... x) override { this->parent_->brake(); }
|
||||
void play(const Ts &...x) override { this->parent_->brake(); }
|
||||
|
||||
HBridgeFan *parent_;
|
||||
};
|
||||
|
||||
247
esphome/components/hlk_fm22x/__init__.py
Normal file
247
esphome/components/hlk_fm22x/__init__.py
Normal file
@@ -0,0 +1,247 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import uart
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_DIRECTION,
|
||||
CONF_ID,
|
||||
CONF_NAME,
|
||||
CONF_ON_ENROLLMENT_DONE,
|
||||
CONF_ON_ENROLLMENT_FAILED,
|
||||
CONF_TRIGGER_ID,
|
||||
)
|
||||
|
||||
CODEOWNERS = ["@OnFreund"]
|
||||
DEPENDENCIES = ["uart"]
|
||||
AUTO_LOAD = ["binary_sensor", "sensor", "text_sensor"]
|
||||
MULTI_CONF = True
|
||||
|
||||
CONF_HLK_FM22X_ID = "hlk_fm22x_id"
|
||||
CONF_FACE_ID = "face_id"
|
||||
CONF_ON_FACE_SCAN_MATCHED = "on_face_scan_matched"
|
||||
CONF_ON_FACE_SCAN_UNMATCHED = "on_face_scan_unmatched"
|
||||
CONF_ON_FACE_SCAN_INVALID = "on_face_scan_invalid"
|
||||
CONF_ON_FACE_INFO = "on_face_info"
|
||||
|
||||
hlk_fm22x_ns = cg.esphome_ns.namespace("hlk_fm22x")
|
||||
HlkFm22xComponent = hlk_fm22x_ns.class_(
|
||||
"HlkFm22xComponent", cg.PollingComponent, uart.UARTDevice
|
||||
)
|
||||
|
||||
FaceScanMatchedTrigger = hlk_fm22x_ns.class_(
|
||||
"FaceScanMatchedTrigger", automation.Trigger.template(cg.int16, cg.std_string)
|
||||
)
|
||||
|
||||
FaceScanUnmatchedTrigger = hlk_fm22x_ns.class_(
|
||||
"FaceScanUnmatchedTrigger", automation.Trigger.template()
|
||||
)
|
||||
|
||||
FaceScanInvalidTrigger = hlk_fm22x_ns.class_(
|
||||
"FaceScanInvalidTrigger", automation.Trigger.template(cg.uint8)
|
||||
)
|
||||
|
||||
FaceInfoTrigger = hlk_fm22x_ns.class_(
|
||||
"FaceInfoTrigger",
|
||||
automation.Trigger.template(
|
||||
cg.int16, cg.int16, cg.int16, cg.int16, cg.int16, cg.int16, cg.int16, cg.int16
|
||||
),
|
||||
)
|
||||
|
||||
EnrollmentDoneTrigger = hlk_fm22x_ns.class_(
|
||||
"EnrollmentDoneTrigger", automation.Trigger.template(cg.int16, cg.uint8)
|
||||
)
|
||||
|
||||
EnrollmentFailedTrigger = hlk_fm22x_ns.class_(
|
||||
"EnrollmentFailedTrigger", automation.Trigger.template(cg.uint8)
|
||||
)
|
||||
|
||||
EnrollmentAction = hlk_fm22x_ns.class_("EnrollmentAction", automation.Action)
|
||||
DeleteAction = hlk_fm22x_ns.class_("DeleteAction", automation.Action)
|
||||
DeleteAllAction = hlk_fm22x_ns.class_("DeleteAllAction", automation.Action)
|
||||
ScanAction = hlk_fm22x_ns.class_("ScanAction", automation.Action)
|
||||
ResetAction = hlk_fm22x_ns.class_("ResetAction", automation.Action)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(HlkFm22xComponent),
|
||||
cv.Optional(CONF_ON_FACE_SCAN_MATCHED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FaceScanMatchedTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FACE_SCAN_UNMATCHED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FaceScanUnmatchedTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FACE_SCAN_INVALID): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
FaceScanInvalidTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_FACE_INFO): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(FaceInfoTrigger),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ENROLLMENT_DONE): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
EnrollmentDoneTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
cv.Optional(CONF_ON_ENROLLMENT_FAILED): automation.validate_automation(
|
||||
{
|
||||
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
|
||||
EnrollmentFailedTrigger
|
||||
),
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("50ms"))
|
||||
.extend(uart.UART_DEVICE_SCHEMA),
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await uart.register_uart_device(var, config)
|
||||
|
||||
for conf in config.get(CONF_ON_FACE_SCAN_MATCHED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.int16, "face_id"), (cg.std_string, "name")], conf
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_FACE_SCAN_UNMATCHED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_FACE_SCAN_INVALID, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.uint8, "error")], conf)
|
||||
|
||||
for conf in config.get(CONF_ON_FACE_INFO, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger,
|
||||
[
|
||||
(cg.int16, "status"),
|
||||
(cg.int16, "left"),
|
||||
(cg.int16, "top"),
|
||||
(cg.int16, "right"),
|
||||
(cg.int16, "bottom"),
|
||||
(cg.int16, "yaw"),
|
||||
(cg.int16, "pitch"),
|
||||
(cg.int16, "roll"),
|
||||
],
|
||||
conf,
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_ENROLLMENT_DONE, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(
|
||||
trigger, [(cg.int16, "face_id"), (cg.uint8, "direction")], conf
|
||||
)
|
||||
|
||||
for conf in config.get(CONF_ON_ENROLLMENT_FAILED, []):
|
||||
trigger = cg.new_Pvariable(conf[CONF_TRIGGER_ID], var)
|
||||
await automation.build_automation(trigger, [(cg.uint8, "error")], conf)
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"hlk_fm22x.enroll",
|
||||
EnrollmentAction,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(HlkFm22xComponent),
|
||||
cv.Required(CONF_NAME): cv.templatable(cv.string),
|
||||
cv.Required(CONF_DIRECTION): cv.templatable(cv.uint8_t),
|
||||
},
|
||||
key=CONF_NAME,
|
||||
),
|
||||
)
|
||||
async def hlk_fm22x_enroll_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
template_ = await cg.templatable(config[CONF_NAME], args, cg.std_string)
|
||||
cg.add(var.set_name(template_))
|
||||
template_ = await cg.templatable(config[CONF_DIRECTION], args, cg.uint8)
|
||||
cg.add(var.set_direction(template_))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"hlk_fm22x.delete",
|
||||
DeleteAction,
|
||||
cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(HlkFm22xComponent),
|
||||
cv.Required(CONF_FACE_ID): cv.templatable(cv.uint16_t),
|
||||
},
|
||||
key=CONF_FACE_ID,
|
||||
),
|
||||
)
|
||||
async def hlk_fm22x_delete_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
|
||||
template_ = await cg.templatable(config[CONF_FACE_ID], args, cg.int16)
|
||||
cg.add(var.set_face_id(template_))
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"hlk_fm22x.delete_all",
|
||||
DeleteAllAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(HlkFm22xComponent),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def hlk_fm22x_delete_all_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"hlk_fm22x.scan",
|
||||
ScanAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(HlkFm22xComponent),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def hlk_fm22x_scan_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"hlk_fm22x.reset",
|
||||
ResetAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(HlkFm22xComponent),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def hlk_fm22x_reset_to_code(config, action_id, template_arg, args):
|
||||
var = cg.new_Pvariable(action_id, template_arg)
|
||||
await cg.register_parented(var, config[CONF_ID])
|
||||
return var
|
||||
21
esphome/components/hlk_fm22x/binary_sensor.py
Normal file
21
esphome/components/hlk_fm22x/binary_sensor.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import binary_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ICON, ICON_KEY_PLUS
|
||||
|
||||
from . import CONF_HLK_FM22X_ID, HlkFm22xComponent
|
||||
|
||||
DEPENDENCIES = ["hlk_fm22x"]
|
||||
|
||||
CONFIG_SCHEMA = binary_sensor.binary_sensor_schema().extend(
|
||||
{
|
||||
cv.GenerateID(CONF_HLK_FM22X_ID): cv.use_id(HlkFm22xComponent),
|
||||
cv.Optional(CONF_ICON, default=ICON_KEY_PLUS): cv.icon,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_HLK_FM22X_ID])
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
cg.add(hub.set_enrolling_binary_sensor(var))
|
||||
325
esphome/components/hlk_fm22x/hlk_fm22x.cpp
Normal file
325
esphome/components/hlk_fm22x/hlk_fm22x.cpp
Normal file
@@ -0,0 +1,325 @@
|
||||
#include "hlk_fm22x.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include <array>
|
||||
#include <cinttypes>
|
||||
|
||||
namespace esphome::hlk_fm22x {
|
||||
|
||||
static const char *const TAG = "hlk_fm22x";
|
||||
|
||||
void HlkFm22xComponent::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up HLK-FM22X...");
|
||||
this->set_enrolling_(false);
|
||||
while (this->available()) {
|
||||
this->read();
|
||||
}
|
||||
this->defer([this]() { this->send_command_(HlkFm22xCommand::GET_STATUS); });
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::update() {
|
||||
if (this->active_command_ != HlkFm22xCommand::NONE) {
|
||||
if (this->wait_cycles_ > 600) {
|
||||
ESP_LOGE(TAG, "Command 0x%.2X timed out", this->active_command_);
|
||||
if (HlkFm22xCommand::RESET == this->active_command_) {
|
||||
this->mark_failed();
|
||||
} else {
|
||||
this->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
this->recv_command_();
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::enroll_face(const std::string &name, HlkFm22xFaceDirection direction) {
|
||||
if (name.length() > 31) {
|
||||
ESP_LOGE(TAG, "enroll_face(): name too long '%s'", name.c_str());
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "Starting enrollment for %s", name.c_str());
|
||||
std::array<uint8_t, 35> data{};
|
||||
data[0] = 0; // admin
|
||||
std::copy(name.begin(), name.end(), data.begin() + 1);
|
||||
// Remaining bytes are already zero-initialized
|
||||
data[33] = (uint8_t) direction;
|
||||
data[34] = 10; // timeout
|
||||
this->send_command_(HlkFm22xCommand::ENROLL, data.data(), data.size());
|
||||
this->set_enrolling_(true);
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::scan_face() {
|
||||
ESP_LOGI(TAG, "Verify face");
|
||||
static const uint8_t DATA[] = {0, 0};
|
||||
this->send_command_(HlkFm22xCommand::VERIFY, DATA, sizeof(DATA));
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::delete_face(int16_t face_id) {
|
||||
ESP_LOGI(TAG, "Deleting face in slot %d", face_id);
|
||||
const uint8_t data[] = {(uint8_t) (face_id >> 8), (uint8_t) (face_id & 0xFF)};
|
||||
this->send_command_(HlkFm22xCommand::DELETE_FACE, data, sizeof(data));
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::delete_all_faces() {
|
||||
ESP_LOGI(TAG, "Deleting all stored faces");
|
||||
this->send_command_(HlkFm22xCommand::DELETE_ALL_FACES);
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::get_face_count_() {
|
||||
ESP_LOGD(TAG, "Getting face count");
|
||||
this->send_command_(HlkFm22xCommand::GET_ALL_FACE_IDS);
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::reset() {
|
||||
ESP_LOGI(TAG, "Resetting module");
|
||||
this->active_command_ = HlkFm22xCommand::NONE;
|
||||
this->wait_cycles_ = 0;
|
||||
this->set_enrolling_(false);
|
||||
this->send_command_(HlkFm22xCommand::RESET);
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::send_command_(HlkFm22xCommand command, const uint8_t *data, size_t size) {
|
||||
ESP_LOGV(TAG, "Send command: 0x%.2X", command);
|
||||
if (this->active_command_ != HlkFm22xCommand::NONE) {
|
||||
ESP_LOGW(TAG, "Command 0x%.2X already active", this->active_command_);
|
||||
return;
|
||||
}
|
||||
this->wait_cycles_ = 0;
|
||||
this->active_command_ = command;
|
||||
while (this->available())
|
||||
this->read();
|
||||
this->write((uint8_t) (START_CODE >> 8));
|
||||
this->write((uint8_t) (START_CODE & 0xFF));
|
||||
this->write((uint8_t) command);
|
||||
uint16_t data_size = size;
|
||||
this->write((uint8_t) (data_size >> 8));
|
||||
this->write((uint8_t) (data_size & 0xFF));
|
||||
|
||||
uint8_t checksum = 0;
|
||||
checksum ^= (uint8_t) command;
|
||||
checksum ^= (data_size >> 8);
|
||||
checksum ^= (data_size & 0xFF);
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
this->write(data[i]);
|
||||
checksum ^= data[i];
|
||||
}
|
||||
|
||||
this->write(checksum);
|
||||
this->active_command_ = command;
|
||||
this->wait_cycles_ = 0;
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::recv_command_() {
|
||||
uint8_t byte, checksum = 0;
|
||||
uint16_t length = 0;
|
||||
|
||||
if (this->available() < 7) {
|
||||
++this->wait_cycles_;
|
||||
return;
|
||||
}
|
||||
this->wait_cycles_ = 0;
|
||||
|
||||
if ((this->read() != (uint8_t) (START_CODE >> 8)) || (this->read() != (uint8_t) (START_CODE & 0xFF))) {
|
||||
ESP_LOGE(TAG, "Invalid start code");
|
||||
return;
|
||||
}
|
||||
|
||||
byte = this->read();
|
||||
checksum ^= byte;
|
||||
HlkFm22xResponseType response_type = (HlkFm22xResponseType) byte;
|
||||
|
||||
byte = this->read();
|
||||
checksum ^= byte;
|
||||
length = byte << 8;
|
||||
byte = this->read();
|
||||
checksum ^= byte;
|
||||
length |= byte;
|
||||
|
||||
std::vector<uint8_t> data;
|
||||
data.reserve(length);
|
||||
for (uint16_t idx = 0; idx < length; ++idx) {
|
||||
byte = this->read();
|
||||
checksum ^= byte;
|
||||
data.push_back(byte);
|
||||
}
|
||||
|
||||
ESP_LOGV(TAG, "Recv type: 0x%.2X, data: %s", response_type, format_hex_pretty(data).c_str());
|
||||
|
||||
byte = this->read();
|
||||
if (byte != checksum) {
|
||||
ESP_LOGE(TAG, "Invalid checksum for data. Calculated: 0x%.2X, Received: 0x%.2X", checksum, byte);
|
||||
return;
|
||||
}
|
||||
switch (response_type) {
|
||||
case HlkFm22xResponseType::NOTE:
|
||||
this->handle_note_(data);
|
||||
break;
|
||||
case HlkFm22xResponseType::REPLY:
|
||||
this->handle_reply_(data);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unexpected response type: 0x%.2X", response_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::handle_note_(const std::vector<uint8_t> &data) {
|
||||
switch (data[0]) {
|
||||
case HlkFm22xNoteType::FACE_STATE:
|
||||
if (data.size() < 17) {
|
||||
ESP_LOGE(TAG, "Invalid face note data size: %u", data.size());
|
||||
break;
|
||||
}
|
||||
{
|
||||
int16_t info[8];
|
||||
uint8_t offset = 1;
|
||||
for (int16_t &i : info) {
|
||||
i = ((int16_t) data[offset + 1] << 8) | data[offset];
|
||||
offset += 2;
|
||||
}
|
||||
ESP_LOGV(TAG, "Face state: status: %d, left: %d, top: %d, right: %d, bottom: %d, yaw: %d, pitch: %d, roll: %d",
|
||||
info[0], info[1], info[2], info[3], info[4], info[5], info[6], info[7]);
|
||||
this->face_info_callback_.call(info[0], info[1], info[2], info[3], info[4], info[5], info[6], info[7]);
|
||||
}
|
||||
break;
|
||||
case HlkFm22xNoteType::READY:
|
||||
ESP_LOGE(TAG, "Command 0x%.2X timed out", this->active_command_);
|
||||
switch (this->active_command_) {
|
||||
case HlkFm22xCommand::ENROLL:
|
||||
this->set_enrolling_(false);
|
||||
this->enrollment_failed_callback_.call(HlkFm22xResult::FAILED4_TIMEOUT);
|
||||
break;
|
||||
case HlkFm22xCommand::VERIFY:
|
||||
this->face_scan_invalid_callback_.call(HlkFm22xResult::FAILED4_TIMEOUT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
this->active_command_ = HlkFm22xCommand::NONE;
|
||||
this->wait_cycles_ = 0;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unhandled note: 0x%.2X", data[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::handle_reply_(const std::vector<uint8_t> &data) {
|
||||
auto expected = this->active_command_;
|
||||
this->active_command_ = HlkFm22xCommand::NONE;
|
||||
if (data[0] != (uint8_t) expected) {
|
||||
ESP_LOGE(TAG, "Unexpected response command. Expected: 0x%.2X, Received: 0x%.2X", expected, data[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[1] != HlkFm22xResult::SUCCESS) {
|
||||
ESP_LOGE(TAG, "Command <0x%.2X> failed. Error: 0x%.2X", data[0], data[1]);
|
||||
switch (expected) {
|
||||
case HlkFm22xCommand::ENROLL:
|
||||
this->set_enrolling_(false);
|
||||
this->enrollment_failed_callback_.call(data[1]);
|
||||
break;
|
||||
case HlkFm22xCommand::VERIFY:
|
||||
if (data[1] == HlkFm22xResult::REJECTED) {
|
||||
this->face_scan_unmatched_callback_.call();
|
||||
} else {
|
||||
this->face_scan_invalid_callback_.call(data[1]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
switch (expected) {
|
||||
case HlkFm22xCommand::VERIFY: {
|
||||
int16_t face_id = ((int16_t) data[2] << 8) | data[3];
|
||||
std::string name(data.begin() + 4, data.begin() + 36);
|
||||
ESP_LOGD(TAG, "Face verified. ID: %d, name: %s", face_id, name.c_str());
|
||||
if (this->last_face_id_sensor_ != nullptr) {
|
||||
this->last_face_id_sensor_->publish_state(face_id);
|
||||
}
|
||||
if (this->last_face_name_text_sensor_ != nullptr) {
|
||||
this->last_face_name_text_sensor_->publish_state(name);
|
||||
}
|
||||
this->face_scan_matched_callback_.call(face_id, name);
|
||||
break;
|
||||
}
|
||||
case HlkFm22xCommand::ENROLL: {
|
||||
int16_t face_id = ((int16_t) data[2] << 8) | data[3];
|
||||
HlkFm22xFaceDirection direction = (HlkFm22xFaceDirection) data[4];
|
||||
ESP_LOGI(TAG, "Face enrolled. ID: %d, Direction: 0x%.2X", face_id, direction);
|
||||
this->enrollment_done_callback_.call(face_id, (uint8_t) direction);
|
||||
this->set_enrolling_(false);
|
||||
this->defer([this]() { this->get_face_count_(); });
|
||||
break;
|
||||
}
|
||||
case HlkFm22xCommand::GET_STATUS:
|
||||
if (this->status_sensor_ != nullptr) {
|
||||
this->status_sensor_->publish_state(data[2]);
|
||||
}
|
||||
this->defer([this]() { this->send_command_(HlkFm22xCommand::GET_VERSION); });
|
||||
break;
|
||||
case HlkFm22xCommand::GET_VERSION:
|
||||
if (this->version_text_sensor_ != nullptr) {
|
||||
std::string version(data.begin() + 2, data.end());
|
||||
this->version_text_sensor_->publish_state(version);
|
||||
}
|
||||
this->defer([this]() { this->get_face_count_(); });
|
||||
break;
|
||||
case HlkFm22xCommand::GET_ALL_FACE_IDS:
|
||||
if (this->face_count_sensor_ != nullptr) {
|
||||
this->face_count_sensor_->publish_state(data[2]);
|
||||
}
|
||||
break;
|
||||
case HlkFm22xCommand::DELETE_FACE:
|
||||
ESP_LOGI(TAG, "Deleted face");
|
||||
break;
|
||||
case HlkFm22xCommand::DELETE_ALL_FACES:
|
||||
ESP_LOGI(TAG, "Deleted all faces");
|
||||
break;
|
||||
case HlkFm22xCommand::RESET:
|
||||
ESP_LOGI(TAG, "Module reset");
|
||||
this->defer([this]() { this->send_command_(HlkFm22xCommand::GET_STATUS); });
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unhandled command: 0x%.2X", this->active_command_);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::set_enrolling_(bool enrolling) {
|
||||
if (this->enrolling_binary_sensor_ != nullptr) {
|
||||
this->enrolling_binary_sensor_->publish_state(enrolling);
|
||||
}
|
||||
}
|
||||
|
||||
void HlkFm22xComponent::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "HLK_FM22X:");
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
if (this->version_text_sensor_) {
|
||||
LOG_TEXT_SENSOR(" ", "Version", this->version_text_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Current Value: %s", this->version_text_sensor_->get_state().c_str());
|
||||
}
|
||||
if (this->enrolling_binary_sensor_) {
|
||||
LOG_BINARY_SENSOR(" ", "Enrolling", this->enrolling_binary_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Current Value: %s", this->enrolling_binary_sensor_->state ? "ON" : "OFF");
|
||||
}
|
||||
if (this->face_count_sensor_) {
|
||||
LOG_SENSOR(" ", "Face Count", this->face_count_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Current Value: %u", (uint16_t) this->face_count_sensor_->get_state());
|
||||
}
|
||||
if (this->status_sensor_) {
|
||||
LOG_SENSOR(" ", "Status", this->status_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Current Value: %u", (uint8_t) this->status_sensor_->get_state());
|
||||
}
|
||||
if (this->last_face_id_sensor_) {
|
||||
LOG_SENSOR(" ", "Last Face ID", this->last_face_id_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Current Value: %u", (int16_t) this->last_face_id_sensor_->get_state());
|
||||
}
|
||||
if (this->last_face_name_text_sensor_) {
|
||||
LOG_TEXT_SENSOR(" ", "Last Face Name", this->last_face_name_text_sensor_);
|
||||
ESP_LOGCONFIG(TAG, " Current Value: %s", this->last_face_name_text_sensor_->get_state().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::hlk_fm22x
|
||||
224
esphome/components/hlk_fm22x/hlk_fm22x.h
Normal file
224
esphome/components/hlk_fm22x/hlk_fm22x.h
Normal file
@@ -0,0 +1,224 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
#include "esphome/components/text_sensor/text_sensor.h"
|
||||
#include "esphome/components/uart/uart.h"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace esphome::hlk_fm22x {
|
||||
|
||||
static const uint16_t START_CODE = 0xEFAA;
|
||||
enum HlkFm22xCommand {
|
||||
NONE = 0x00,
|
||||
RESET = 0x10,
|
||||
GET_STATUS = 0x11,
|
||||
VERIFY = 0x12,
|
||||
ENROLL = 0x13,
|
||||
DELETE_FACE = 0x20,
|
||||
DELETE_ALL_FACES = 0x21,
|
||||
GET_ALL_FACE_IDS = 0x24,
|
||||
GET_VERSION = 0x30,
|
||||
GET_SERIAL_NUMBER = 0x93,
|
||||
};
|
||||
|
||||
enum HlkFm22xResponseType {
|
||||
REPLY = 0x00,
|
||||
NOTE = 0x01,
|
||||
IMAGE = 0x02,
|
||||
};
|
||||
|
||||
enum HlkFm22xNoteType {
|
||||
READY = 0x00,
|
||||
FACE_STATE = 0x01,
|
||||
};
|
||||
|
||||
enum HlkFm22xResult {
|
||||
SUCCESS = 0x00,
|
||||
REJECTED = 0x01,
|
||||
ABORTED = 0x02,
|
||||
FAILED4_CAMERA = 0x04,
|
||||
FAILED4_UNKNOWNREASON = 0x05,
|
||||
FAILED4_INVALIDPARAM = 0x06,
|
||||
FAILED4_NOMEMORY = 0x07,
|
||||
FAILED4_UNKNOWNUSER = 0x08,
|
||||
FAILED4_MAXUSER = 0x09,
|
||||
FAILED4_FACEENROLLED = 0x0A,
|
||||
FAILED4_LIVENESSCHECK = 0x0C,
|
||||
FAILED4_TIMEOUT = 0x0D,
|
||||
FAILED4_AUTHORIZATION = 0x0E,
|
||||
FAILED4_READ_FILE = 0x13,
|
||||
FAILED4_WRITE_FILE = 0x14,
|
||||
FAILED4_NO_ENCRYPT = 0x15,
|
||||
FAILED4_NO_RGBIMAGE = 0x17,
|
||||
FAILED4_JPGPHOTO_LARGE = 0x18,
|
||||
FAILED4_JPGPHOTO_SMALL = 0x19,
|
||||
};
|
||||
|
||||
enum HlkFm22xFaceDirection {
|
||||
FACE_DIRECTION_UNDEFINED = 0x00,
|
||||
FACE_DIRECTION_MIDDLE = 0x01,
|
||||
FACE_DIRECTION_RIGHT = 0x02,
|
||||
FACE_DIRECTION_LEFT = 0x04,
|
||||
FACE_DIRECTION_DOWN = 0x08,
|
||||
FACE_DIRECTION_UP = 0x10,
|
||||
};
|
||||
|
||||
class HlkFm22xComponent : public PollingComponent, public uart::UARTDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
|
||||
void set_face_count_sensor(sensor::Sensor *face_count_sensor) { this->face_count_sensor_ = face_count_sensor; }
|
||||
void set_status_sensor(sensor::Sensor *status_sensor) { this->status_sensor_ = status_sensor; }
|
||||
void set_last_face_id_sensor(sensor::Sensor *last_face_id_sensor) {
|
||||
this->last_face_id_sensor_ = last_face_id_sensor;
|
||||
}
|
||||
void set_last_face_name_text_sensor(text_sensor::TextSensor *last_face_name_text_sensor) {
|
||||
this->last_face_name_text_sensor_ = last_face_name_text_sensor;
|
||||
}
|
||||
void set_enrolling_binary_sensor(binary_sensor::BinarySensor *enrolling_binary_sensor) {
|
||||
this->enrolling_binary_sensor_ = enrolling_binary_sensor;
|
||||
}
|
||||
void set_version_text_sensor(text_sensor::TextSensor *version_text_sensor) {
|
||||
this->version_text_sensor_ = version_text_sensor;
|
||||
}
|
||||
void add_on_face_scan_matched_callback(std::function<void(int16_t, std::string)> callback) {
|
||||
this->face_scan_matched_callback_.add(std::move(callback));
|
||||
}
|
||||
void add_on_face_scan_unmatched_callback(std::function<void()> callback) {
|
||||
this->face_scan_unmatched_callback_.add(std::move(callback));
|
||||
}
|
||||
void add_on_face_scan_invalid_callback(std::function<void(uint8_t)> callback) {
|
||||
this->face_scan_invalid_callback_.add(std::move(callback));
|
||||
}
|
||||
void add_on_face_info_callback(
|
||||
std::function<void(int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t)> callback) {
|
||||
this->face_info_callback_.add(std::move(callback));
|
||||
}
|
||||
void add_on_enrollment_done_callback(std::function<void(int16_t, uint8_t)> callback) {
|
||||
this->enrollment_done_callback_.add(std::move(callback));
|
||||
}
|
||||
void add_on_enrollment_failed_callback(std::function<void(uint8_t)> callback) {
|
||||
this->enrollment_failed_callback_.add(std::move(callback));
|
||||
}
|
||||
|
||||
void enroll_face(const std::string &name, HlkFm22xFaceDirection direction);
|
||||
void scan_face();
|
||||
void delete_face(int16_t face_id);
|
||||
void delete_all_faces();
|
||||
void reset();
|
||||
|
||||
protected:
|
||||
void get_face_count_();
|
||||
void send_command_(HlkFm22xCommand command, const uint8_t *data = nullptr, size_t size = 0);
|
||||
void recv_command_();
|
||||
void handle_note_(const std::vector<uint8_t> &data);
|
||||
void handle_reply_(const std::vector<uint8_t> &data);
|
||||
void set_enrolling_(bool enrolling);
|
||||
|
||||
HlkFm22xCommand active_command_ = HlkFm22xCommand::NONE;
|
||||
uint16_t wait_cycles_ = 0;
|
||||
sensor::Sensor *face_count_sensor_{nullptr};
|
||||
sensor::Sensor *status_sensor_{nullptr};
|
||||
sensor::Sensor *last_face_id_sensor_{nullptr};
|
||||
binary_sensor::BinarySensor *enrolling_binary_sensor_{nullptr};
|
||||
text_sensor::TextSensor *last_face_name_text_sensor_{nullptr};
|
||||
text_sensor::TextSensor *version_text_sensor_{nullptr};
|
||||
CallbackManager<void(uint8_t)> face_scan_invalid_callback_;
|
||||
CallbackManager<void(int16_t, std::string)> face_scan_matched_callback_;
|
||||
CallbackManager<void()> face_scan_unmatched_callback_;
|
||||
CallbackManager<void(int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t)> face_info_callback_;
|
||||
CallbackManager<void(int16_t, uint8_t)> enrollment_done_callback_;
|
||||
CallbackManager<void(uint8_t)> enrollment_failed_callback_;
|
||||
};
|
||||
|
||||
class FaceScanMatchedTrigger : public Trigger<int16_t, std::string> {
|
||||
public:
|
||||
explicit FaceScanMatchedTrigger(HlkFm22xComponent *parent) {
|
||||
parent->add_on_face_scan_matched_callback(
|
||||
[this](int16_t face_id, const std::string &name) { this->trigger(face_id, name); });
|
||||
}
|
||||
};
|
||||
|
||||
class FaceScanUnmatchedTrigger : public Trigger<> {
|
||||
public:
|
||||
explicit FaceScanUnmatchedTrigger(HlkFm22xComponent *parent) {
|
||||
parent->add_on_face_scan_unmatched_callback([this]() { this->trigger(); });
|
||||
}
|
||||
};
|
||||
|
||||
class FaceScanInvalidTrigger : public Trigger<uint8_t> {
|
||||
public:
|
||||
explicit FaceScanInvalidTrigger(HlkFm22xComponent *parent) {
|
||||
parent->add_on_face_scan_invalid_callback([this](uint8_t error) { this->trigger(error); });
|
||||
}
|
||||
};
|
||||
|
||||
class FaceInfoTrigger : public Trigger<int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t, int16_t> {
|
||||
public:
|
||||
explicit FaceInfoTrigger(HlkFm22xComponent *parent) {
|
||||
parent->add_on_face_info_callback(
|
||||
[this](int16_t status, int16_t left, int16_t top, int16_t right, int16_t bottom, int16_t yaw, int16_t pitch,
|
||||
int16_t roll) { this->trigger(status, left, top, right, bottom, yaw, pitch, roll); });
|
||||
}
|
||||
};
|
||||
|
||||
class EnrollmentDoneTrigger : public Trigger<int16_t, uint8_t> {
|
||||
public:
|
||||
explicit EnrollmentDoneTrigger(HlkFm22xComponent *parent) {
|
||||
parent->add_on_enrollment_done_callback(
|
||||
[this](int16_t face_id, uint8_t direction) { this->trigger(face_id, direction); });
|
||||
}
|
||||
};
|
||||
|
||||
class EnrollmentFailedTrigger : public Trigger<uint8_t> {
|
||||
public:
|
||||
explicit EnrollmentFailedTrigger(HlkFm22xComponent *parent) {
|
||||
parent->add_on_enrollment_failed_callback([this](uint8_t error) { this->trigger(error); });
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class EnrollmentAction : public Action<Ts...>, public Parented<HlkFm22xComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(std::string, name)
|
||||
TEMPLATABLE_VALUE(uint8_t, direction)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto name = this->name_.value(x...);
|
||||
auto direction = (HlkFm22xFaceDirection) this->direction_.value(x...);
|
||||
this->parent_->enroll_face(name, direction);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class DeleteAction : public Action<Ts...>, public Parented<HlkFm22xComponent> {
|
||||
public:
|
||||
TEMPLATABLE_VALUE(int16_t, face_id)
|
||||
|
||||
void play(Ts... x) override {
|
||||
auto face_id = this->face_id_.value(x...);
|
||||
this->parent_->delete_face(face_id);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Ts> class DeleteAllAction : public Action<Ts...>, public Parented<HlkFm22xComponent> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->delete_all_faces(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class ScanAction : public Action<Ts...>, public Parented<HlkFm22xComponent> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->scan_face(); }
|
||||
};
|
||||
|
||||
template<typename... Ts> class ResetAction : public Action<Ts...>, public Parented<HlkFm22xComponent> {
|
||||
public:
|
||||
void play(Ts... x) override { this->parent_->reset(); }
|
||||
};
|
||||
|
||||
} // namespace esphome::hlk_fm22x
|
||||
47
esphome/components/hlk_fm22x/sensor.py
Normal file
47
esphome/components/hlk_fm22x/sensor.py
Normal file
@@ -0,0 +1,47 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_STATUS, ENTITY_CATEGORY_DIAGNOSTIC, ICON_ACCOUNT
|
||||
|
||||
from . import CONF_HLK_FM22X_ID, HlkFm22xComponent
|
||||
|
||||
DEPENDENCIES = ["hlk_fm22x"]
|
||||
|
||||
CONF_FACE_COUNT = "face_count"
|
||||
CONF_LAST_FACE_ID = "last_face_id"
|
||||
ICON_FACE = "mdi:face-recognition"
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_HLK_FM22X_ID): cv.use_id(HlkFm22xComponent),
|
||||
cv.Optional(CONF_FACE_COUNT): sensor.sensor_schema(
|
||||
icon=ICON_FACE,
|
||||
accuracy_decimals=0,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_STATUS): sensor.sensor_schema(
|
||||
accuracy_decimals=0,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_LAST_FACE_ID): sensor.sensor_schema(
|
||||
icon=ICON_ACCOUNT,
|
||||
accuracy_decimals=0,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_HLK_FM22X_ID])
|
||||
|
||||
for key in [
|
||||
CONF_FACE_COUNT,
|
||||
CONF_STATUS,
|
||||
CONF_LAST_FACE_ID,
|
||||
]:
|
||||
if key not in config:
|
||||
continue
|
||||
conf = config[key]
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(getattr(hub, f"set_{key}_sensor")(sens))
|
||||
42
esphome/components/hlk_fm22x/text_sensor.py
Normal file
42
esphome/components/hlk_fm22x/text_sensor.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import text_sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_VERSION,
|
||||
ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
ICON_ACCOUNT,
|
||||
ICON_RESTART,
|
||||
)
|
||||
|
||||
from . import CONF_HLK_FM22X_ID, HlkFm22xComponent
|
||||
|
||||
DEPENDENCIES = ["hlk_fm22x"]
|
||||
|
||||
CONF_LAST_FACE_NAME = "last_face_name"
|
||||
|
||||
CONFIG_SCHEMA = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_HLK_FM22X_ID): cv.use_id(HlkFm22xComponent),
|
||||
cv.Optional(CONF_VERSION): text_sensor.text_sensor_schema(
|
||||
icon=ICON_RESTART,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
cv.Optional(CONF_LAST_FACE_NAME): text_sensor.text_sensor_schema(
|
||||
icon=ICON_ACCOUNT,
|
||||
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
hub = await cg.get_variable(config[CONF_HLK_FM22X_ID])
|
||||
for key in [
|
||||
CONF_VERSION,
|
||||
CONF_LAST_FACE_NAME,
|
||||
]:
|
||||
if key not in config:
|
||||
continue
|
||||
conf = config[key]
|
||||
sens = await text_sensor.new_text_sensor(conf)
|
||||
cg.add(getattr(hub, f"set_{key}_text_sensor")(sens))
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
#include "abstract_aqi_calculator.h"
|
||||
// https://www.airnow.gov/sites/default/files/2020-05/aqi-technical-assistance-document-sept2018.pdf
|
||||
// https://document.airnow.gov/technical-assistance-document-for-the-reporting-of-daily-air-quailty.pdf
|
||||
|
||||
namespace esphome {
|
||||
namespace hm3301 {
|
||||
@@ -16,16 +16,15 @@ class AQICalculator : public AbstractAQICalculator {
|
||||
}
|
||||
|
||||
protected:
|
||||
static const int AMOUNT_OF_LEVELS = 7;
|
||||
static const int AMOUNT_OF_LEVELS = 6;
|
||||
|
||||
int index_grid_[AMOUNT_OF_LEVELS][2] = {{0, 50}, {51, 100}, {101, 150}, {151, 200},
|
||||
{201, 300}, {301, 400}, {401, 500}};
|
||||
int index_grid_[AMOUNT_OF_LEVELS][2] = {{0, 50}, {51, 100}, {101, 150}, {151, 200}, {201, 300}, {301, 500}};
|
||||
|
||||
int pm2_5_calculation_grid_[AMOUNT_OF_LEVELS][2] = {{0, 12}, {13, 35}, {36, 55}, {56, 150},
|
||||
{151, 250}, {251, 350}, {351, 500}};
|
||||
int pm2_5_calculation_grid_[AMOUNT_OF_LEVELS][2] = {{0, 9}, {10, 35}, {36, 55},
|
||||
{56, 125}, {126, 225}, {226, INT_MAX}};
|
||||
|
||||
int pm10_0_calculation_grid_[AMOUNT_OF_LEVELS][2] = {{0, 54}, {55, 154}, {155, 254}, {255, 354},
|
||||
{355, 424}, {425, 504}, {505, 604}};
|
||||
int pm10_0_calculation_grid_[AMOUNT_OF_LEVELS][2] = {{0, 54}, {55, 154}, {155, 254},
|
||||
{255, 354}, {355, 424}, {425, INT_MAX}};
|
||||
|
||||
int calculate_index_(uint16_t value, int array[AMOUNT_OF_LEVELS][2]) {
|
||||
int grid_index = get_grid_index_(value, array);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user