ESPHome 2026.2.3
Loading...
Searching...
No Matches
automation.h
Go to the documentation of this file.
1#pragma once
2
8#include <concepts>
9#include <functional>
10#include <utility>
11#include <vector>
12
13namespace esphome {
14
15// C++20 std::index_sequence is now used for tuple unpacking
16// Legacy seq<>/gens<> pattern deprecated but kept for backwards compatibility
17// https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer/7858971#7858971
18// Remove before 2026.6.0
19// NOLINTBEGIN(readability-identifier-naming)
20#if defined(__GNUC__) || defined(__clang__)
21#pragma GCC diagnostic push
22#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
23#endif
24
25template<int...> struct ESPDEPRECATED("Use std::index_sequence instead. Removed in 2026.6.0", "2025.12.0") seq {};
26template<int N, int... S>
27struct ESPDEPRECATED("Use std::make_index_sequence instead. Removed in 2026.6.0", "2025.12.0") gens
28 : gens<N - 1, N - 1, S...> {};
29template<int... S> struct gens<0, S...> { using type = seq<S...>; };
30
31#if defined(__GNUC__) || defined(__clang__)
32#pragma GCC diagnostic pop
33#endif
34// NOLINTEND(readability-identifier-naming)
35
36#define TEMPLATABLE_VALUE_(type, name) \
37 protected: \
38 TemplatableValue<type, Ts...> name##_{}; \
39\
40 public: \
41 template<typename V> void set_##name(V name) { this->name##_ = name; }
42
43#define TEMPLATABLE_VALUE(type, name) TEMPLATABLE_VALUE_(type, name)
44
45template<typename T, typename... X> class TemplatableValue {
46 // For std::string, store pointer to heap-allocated string to keep union pointer-sized.
47 // For other types, store value inline.
48 static constexpr bool USE_HEAP_STORAGE = std::same_as<T, std::string>;
49
50 public:
52
53 // For const char* when T is std::string: store pointer directly, no heap allocation
54 // String remains in flash and is only converted to std::string when value() is called
55 TemplatableValue(const char *str) requires std::same_as<T, std::string> : type_(STATIC_STRING) {
56 this->static_str_ = str;
57 }
58
59 template<typename F> TemplatableValue(F value) requires(!std::invocable<F, X...>) : type_(VALUE) {
60 if constexpr (USE_HEAP_STORAGE) {
61 this->value_ = new T(std::move(value));
62 } else {
63 new (&this->value_) T(std::move(value));
64 }
65 }
66
67 // For stateless lambdas (convertible to function pointer): use function pointer
68 template<typename F>
69 TemplatableValue(F f) requires std::invocable<F, X...> && std::convertible_to<F, T (*)(X...)>
71 this->stateless_f_ = f; // Implicit conversion to function pointer
72 }
73
74 // For stateful lambdas (not convertible to function pointer): use std::function
75 template<typename F>
76 TemplatableValue(F f) requires std::invocable<F, X...> &&(!std::convertible_to<F, T (*)(X...)>) : type_(LAMBDA) {
77 this->f_ = new std::function<T(X...)>(std::move(f));
78 }
79
80 // Copy constructor
82 if (this->type_ == VALUE) {
83 if constexpr (USE_HEAP_STORAGE) {
84 this->value_ = new T(*other.value_);
85 } else {
86 new (&this->value_) T(other.value_);
87 }
88 } else if (this->type_ == LAMBDA) {
89 this->f_ = new std::function<T(X...)>(*other.f_);
90 } else if (this->type_ == STATELESS_LAMBDA) {
91 this->stateless_f_ = other.stateless_f_;
92 } else if (this->type_ == STATIC_STRING) {
93 this->static_str_ = other.static_str_;
94 }
95 }
96
97 // Move constructor
98 TemplatableValue(TemplatableValue &&other) noexcept : type_(other.type_) {
99 if (this->type_ == VALUE) {
100 if constexpr (USE_HEAP_STORAGE) {
101 this->value_ = other.value_;
102 other.value_ = nullptr;
103 } else {
104 new (&this->value_) T(std::move(other.value_));
105 }
106 } else if (this->type_ == LAMBDA) {
107 this->f_ = other.f_;
108 other.f_ = nullptr;
109 } else if (this->type_ == STATELESS_LAMBDA) {
110 this->stateless_f_ = other.stateless_f_;
111 } else if (this->type_ == STATIC_STRING) {
112 this->static_str_ = other.static_str_;
113 }
114 other.type_ = NONE;
115 }
116
117 // Assignment operators
119 if (this != &other) {
120 this->~TemplatableValue();
121 new (this) TemplatableValue(other);
122 }
123 return *this;
124 }
125
127 if (this != &other) {
128 this->~TemplatableValue();
129 new (this) TemplatableValue(std::move(other));
130 }
131 return *this;
132 }
133
135 if (this->type_ == VALUE) {
136 if constexpr (USE_HEAP_STORAGE) {
137 delete this->value_;
138 } else {
139 this->value_.~T();
140 }
141 } else if (this->type_ == LAMBDA) {
142 delete this->f_;
143 }
144 // STATELESS_LAMBDA/STATIC_STRING/NONE: no cleanup needed (pointers, not heap-allocated)
145 }
146
147 bool has_value() const { return this->type_ != NONE; }
148
149 T value(X... x) const {
150 switch (this->type_) {
151 case STATELESS_LAMBDA:
152 return this->stateless_f_(x...); // Direct function pointer call
153 case LAMBDA:
154 return (*this->f_)(x...); // std::function call
155 case VALUE:
156 if constexpr (USE_HEAP_STORAGE) {
157 return *this->value_;
158 } else {
159 return this->value_;
160 }
161 case STATIC_STRING:
162 // if constexpr required: code must compile for all T, but STATIC_STRING
163 // can only be set when T is std::string (enforced by constructor constraint)
164 if constexpr (std::same_as<T, std::string>) {
165 return std::string(this->static_str_);
166 }
167 __builtin_unreachable();
168 case NONE:
169 default:
170 return T{};
171 }
172 }
173
175 if (!this->has_value()) {
176 return {};
177 }
178 return this->value(x...);
179 }
180
181 T value_or(X... x, T default_value) {
182 if (!this->has_value()) {
183 return default_value;
184 }
185 return this->value(x...);
186 }
187
189 bool is_static_string() const { return this->type_ == STATIC_STRING; }
190
192 const char *get_static_string() const { return this->static_str_; }
193
197 bool is_empty() const requires std::same_as<T, std::string> {
198 switch (this->type_) {
199 case NONE:
200 return true;
201 case STATIC_STRING:
202 return this->static_str_ == nullptr || this->static_str_[0] == '\0';
203 case VALUE:
204 return this->value_->empty();
205 default: // LAMBDA/STATELESS_LAMBDA - must call value()
206 return this->value().empty();
207 }
208 }
209
216 StringRef ref_or_copy_to(char *lambda_buf, size_t lambda_buf_size) const requires std::same_as<T, std::string> {
217 switch (this->type_) {
218 case NONE:
219 return StringRef();
220 case STATIC_STRING:
221 if (this->static_str_ == nullptr)
222 return StringRef();
223 return StringRef(this->static_str_, strlen(this->static_str_));
224 case VALUE:
225 return StringRef(this->value_->data(), this->value_->size());
226 default: { // LAMBDA/STATELESS_LAMBDA - must call value() and copy
227 std::string result = this->value();
228 size_t copy_len = std::min(result.size(), lambda_buf_size - 1);
229 memcpy(lambda_buf, result.data(), copy_len);
230 lambda_buf[copy_len] = '\0';
231 return StringRef(lambda_buf, copy_len);
232 }
233 }
234 }
235
236 protected : enum : uint8_t {
241 STATIC_STRING, // For const char* when T is std::string - avoids heap allocation
243 // For std::string, use heap pointer to minimize union size (4 bytes vs 12+).
244 // For other types, store value inline as before.
245 using ValueStorage = std::conditional_t<USE_HEAP_STORAGE, T *, T>;
246 union {
247 ValueStorage value_; // T for inline storage, T* for heap storage
248 std::function<T(X...)> *f_;
249 T (*stateless_f_)(X...);
250 const char *static_str_; // For STATIC_STRING type
251 };
252};
253
258template<typename... Ts> class Condition {
259 public:
261 virtual bool check(const Ts &...x) = 0;
262
264 bool check_tuple(const std::tuple<Ts...> &tuple) {
265 return this->check_tuple_(tuple, std::make_index_sequence<sizeof...(Ts)>{});
266 }
267
268 protected:
269 template<size_t... S> bool check_tuple_(const std::tuple<Ts...> &tuple, std::index_sequence<S...> /*unused*/) {
270 return this->check(std::get<S>(tuple)...);
271 }
272};
273
274template<typename... Ts> class Automation;
275
276template<typename... Ts> class Trigger {
277 public:
279 void trigger(const Ts &...x) {
280 if (this->automation_parent_ == nullptr)
281 return;
282 this->automation_parent_->trigger(x...);
283 }
284 void set_automation_parent(Automation<Ts...> *automation_parent) { this->automation_parent_ = automation_parent; }
285
287 void stop_action() {
288 if (this->automation_parent_ == nullptr)
289 return;
290 this->automation_parent_->stop();
291 }
294 if (this->automation_parent_ == nullptr)
295 return false;
296 return this->automation_parent_->is_running();
297 }
298
299 protected:
301};
302
303template<typename... Ts> class ActionList;
304
305template<typename... Ts> class Action {
306 public:
307 virtual void play_complex(const Ts &...x) {
308 this->num_running_++;
309 this->play(x...);
310 this->play_next_(x...);
311 }
312 virtual void stop_complex() {
313 if (num_running_) {
314 this->stop();
315 this->num_running_ = 0;
316 }
317 this->stop_next_();
318 }
320 virtual bool is_running() { return this->num_running_ > 0 || this->is_running_next_(); }
321
325 int total = this->num_running_;
326 if (this->next_ != nullptr)
327 total += this->next_->num_running_total();
328 return total;
329 }
330
331 protected:
332 friend ActionList<Ts...>;
333 template<typename... Us> friend class ContinuationAction;
334
335 virtual void play(const Ts &...x) = 0;
336 void play_next_(const Ts &...x) {
337 if (this->num_running_ > 0) {
338 this->num_running_--;
339 if (this->next_ != nullptr) {
340 this->next_->play_complex(x...);
341 }
342 }
343 }
344 template<size_t... S> void play_next_tuple_(const std::tuple<Ts...> &tuple, std::index_sequence<S...> /*unused*/) {
345 this->play_next_(std::get<S>(tuple)...);
346 }
347 void play_next_tuple_(const std::tuple<Ts...> &tuple) {
348 this->play_next_tuple_(tuple, std::make_index_sequence<sizeof...(Ts)>{});
349 }
350
351 virtual void stop() {}
352 void stop_next_() {
353 if (this->next_ != nullptr) {
354 this->next_->stop_complex();
355 }
356 }
357
359 if (this->next_ == nullptr)
360 return false;
361 return this->next_->is_running();
362 }
363
364 Action<Ts...> *next_{nullptr};
365
369};
370
371template<typename... Ts> class ActionList {
372 public:
373 void add_action(Action<Ts...> *action) {
374 if (this->actions_end_ == nullptr) {
375 this->actions_begin_ = action;
376 } else {
377 this->actions_end_->next_ = action;
378 }
379 this->actions_end_ = action;
380 }
381 void add_actions(const std::initializer_list<Action<Ts...> *> &actions) {
382 for (auto *action : actions) {
383 this->add_action(action);
384 }
385 }
386 void play(const Ts &...x) {
387 if (this->actions_begin_ != nullptr)
388 this->actions_begin_->play_complex(x...);
389 }
390 void play_tuple(const std::tuple<Ts...> &tuple) {
391 this->play_tuple_(tuple, std::make_index_sequence<sizeof...(Ts)>{});
392 }
393 void stop() {
394 if (this->actions_begin_ != nullptr)
396 }
397 bool empty() const { return this->actions_begin_ == nullptr; }
398
400 bool is_running() {
401 if (this->actions_begin_ == nullptr)
402 return false;
403 return this->actions_begin_->is_running();
404 }
407 if (this->actions_begin_ == nullptr)
408 return 0;
409 return this->actions_begin_->num_running_total();
410 }
411
412 protected:
413 template<size_t... S> void play_tuple_(const std::tuple<Ts...> &tuple, std::index_sequence<S...> /*unused*/) {
414 this->play(std::get<S>(tuple)...);
415 }
416
417 Action<Ts...> *actions_begin_{nullptr};
418 Action<Ts...> *actions_end_{nullptr};
419};
420
421template<typename... Ts> class Automation {
422 public:
424
425 void add_action(Action<Ts...> *action) { this->actions_.add_action(action); }
426 void add_actions(const std::initializer_list<Action<Ts...> *> &actions) { this->actions_.add_actions(actions); }
427
428 void stop() { this->actions_.stop(); }
429
430 void trigger(const Ts &...x) { this->actions_.play(x...); }
431
432 bool is_running() { return this->actions_.is_running(); }
433
435 int num_running() { return this->actions_.num_running(); }
436
437 protected:
440};
441
442} // namespace esphome
virtual bool is_running()
Check if this or any of the following actions are currently running.
Definition automation.h:320
Action< Ts... > * next_
Definition automation.h:364
void play_next_(const Ts &...x)
Definition automation.h:336
virtual void stop_complex()
Definition automation.h:312
virtual void play(const Ts &...x)=0
void play_next_tuple_(const std::tuple< Ts... > &tuple)
Definition automation.h:347
virtual void stop()
Definition automation.h:351
bool is_running_next_()
Definition automation.h:358
int num_running_
The number of instances of this sequence in the list of actions that is currently being executed.
Definition automation.h:368
int num_running_total()
The total number of actions that are currently running in this plus any of the following actions in t...
Definition automation.h:324
void play_next_tuple_(const std::tuple< Ts... > &tuple, std::index_sequence< S... >)
Definition automation.h:344
virtual void play_complex(const Ts &...x)
Definition automation.h:307
void add_action(Action< Ts... > *action)
Definition automation.h:373
Action< Ts... > * actions_end_
Definition automation.h:418
void play(const Ts &...x)
Definition automation.h:386
void play_tuple(const std::tuple< Ts... > &tuple)
Definition automation.h:390
bool is_running()
Check if any action in this action list is currently running.
Definition automation.h:400
bool empty() const
Definition automation.h:397
void add_actions(const std::initializer_list< Action< Ts... > * > &actions)
Definition automation.h:381
Action< Ts... > * actions_begin_
Definition automation.h:417
void play_tuple_(const std::tuple< Ts... > &tuple, std::index_sequence< S... >)
Definition automation.h:413
int num_running()
Return the number of actions in this action list that are currently running.
Definition automation.h:406
void add_action(Action< Ts... > *action)
Definition automation.h:425
void trigger(const Ts &...x)
Definition automation.h:430
Trigger< Ts... > * trigger_
Definition automation.h:438
int num_running()
Return the number of actions in the action part of this automation that are currently running.
Definition automation.h:435
Automation(Trigger< Ts... > *trigger)
Definition automation.h:423
void add_actions(const std::initializer_list< Action< Ts... > * > &actions)
Definition automation.h:426
ActionList< Ts... > actions_
Definition automation.h:439
Base class for all automation conditions.
Definition automation.h:258
bool check_tuple_(const std::tuple< Ts... > &tuple, std::index_sequence< S... >)
Definition automation.h:269
bool check_tuple(const std::tuple< Ts... > &tuple)
Call check with a tuple of values as parameter.
Definition automation.h:264
virtual bool check(const Ts &...x)=0
Check whether this condition passes. This condition check must be instant, and not cause any delays.
Simple continuation action that calls play_next_ on a parent action.
StringRef is a reference to a string owned by something else.
Definition string_ref.h:26
TemplatableValue(const TemplatableValue &other)
Definition automation.h:81
bool is_empty() const
Check if the string value is empty without allocating (for std::string specialization).
Definition automation.h:197
std::function< T(X...)> * f_
Definition automation.h:248
TemplatableValue(const char *str)
Definition automation.h:55
TemplatableValue & operator=(TemplatableValue &&other) noexcept
Definition automation.h:126
StringRef ref_or_copy_to(char *lambda_buf, size_t lambda_buf_size) const
Get a StringRef to the string value without heap allocation when possible.
Definition automation.h:216
enum esphome::TemplatableValue::@186 type_
TemplatableValue(TemplatableValue &&other) noexcept
Definition automation.h:98
T value(X... x) const
Definition automation.h:149
bool is_static_string() const
Check if this holds a static string (const char* stored without allocation)
Definition automation.h:189
const char * get_static_string() const
Get the static string pointer (only valid if is_static_string() returns true)
Definition automation.h:192
TemplatableValue & operator=(const TemplatableValue &other)
Definition automation.h:118
T value_or(X... x, T default_value)
Definition automation.h:181
optional< T > optional_value(X... x)
Definition automation.h:174
std::conditional_t< USE_HEAP_STORAGE, T *, T > ValueStorage
Definition automation.h:245
void trigger(const Ts &...x)
Inform the parent automation that the event has triggered.
Definition automation.h:279
Automation< Ts... > * automation_parent_
Definition automation.h:300
void stop_action()
Stop any action connected to this trigger.
Definition automation.h:287
bool is_action_running()
Returns true if any action connected to this trigger is running.
Definition automation.h:293
void set_automation_parent(Automation< Ts... > *automation_parent)
Definition automation.h:284
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
struct ESPDEPRECATED("Use std::index_sequence instead. Removed in 2026.6.0", "2025.12.0") seq
Definition automation.h:25
uint16_t seq
uint16_t x
Definition tt21100.cpp:5