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