ESPHome 2025.12.1
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 public:
47
48 // For const char* when T is std::string: store pointer directly, no heap allocation
49 // String remains in flash and is only converted to std::string when value() is called
50 TemplatableValue(const char *str) requires std::same_as<T, std::string> : type_(STATIC_STRING) {
51 this->static_str_ = str;
52 }
53
54 template<typename F> TemplatableValue(F value) requires(!std::invocable<F, X...>) : type_(VALUE) {
55 new (&this->value_) T(std::move(value));
56 }
57
58 // For stateless lambdas (convertible to function pointer): use function pointer
59 template<typename F>
60 TemplatableValue(F f) requires std::invocable<F, X...> && std::convertible_to<F, T (*)(X...)>
62 this->stateless_f_ = f; // Implicit conversion to function pointer
63 }
64
65 // For stateful lambdas (not convertible to function pointer): use std::function
66 template<typename F>
67 TemplatableValue(F f) requires std::invocable<F, X...> &&(!std::convertible_to<F, T (*)(X...)>) : type_(LAMBDA) {
68 this->f_ = new std::function<T(X...)>(std::move(f));
69 }
70
71 // Copy constructor
73 if (this->type_ == VALUE) {
74 new (&this->value_) T(other.value_);
75 } else if (this->type_ == LAMBDA) {
76 this->f_ = new std::function<T(X...)>(*other.f_);
77 } else if (this->type_ == STATELESS_LAMBDA) {
78 this->stateless_f_ = other.stateless_f_;
79 } else if (this->type_ == STATIC_STRING) {
80 this->static_str_ = other.static_str_;
81 }
82 }
83
84 // Move constructor
85 TemplatableValue(TemplatableValue &&other) noexcept : type_(other.type_) {
86 if (this->type_ == VALUE) {
87 new (&this->value_) T(std::move(other.value_));
88 } else if (this->type_ == LAMBDA) {
89 this->f_ = other.f_;
90 other.f_ = nullptr;
91 } else if (this->type_ == STATELESS_LAMBDA) {
92 this->stateless_f_ = other.stateless_f_;
93 } else if (this->type_ == STATIC_STRING) {
94 this->static_str_ = other.static_str_;
95 }
96 other.type_ = NONE;
97 }
98
99 // Assignment operators
101 if (this != &other) {
102 this->~TemplatableValue();
103 new (this) TemplatableValue(other);
104 }
105 return *this;
106 }
107
109 if (this != &other) {
110 this->~TemplatableValue();
111 new (this) TemplatableValue(std::move(other));
112 }
113 return *this;
114 }
115
117 if (this->type_ == VALUE) {
118 this->value_.~T();
119 } else if (this->type_ == LAMBDA) {
120 delete this->f_;
121 }
122 // STATELESS_LAMBDA/STATIC_STRING/NONE: no cleanup needed (pointers, not heap-allocated)
123 }
124
125 bool has_value() { return this->type_ != NONE; }
126
127 T value(X... x) {
128 switch (this->type_) {
129 case STATELESS_LAMBDA:
130 return this->stateless_f_(x...); // Direct function pointer call
131 case LAMBDA:
132 return (*this->f_)(x...); // std::function call
133 case VALUE:
134 return this->value_;
135 case STATIC_STRING:
136 // if constexpr required: code must compile for all T, but STATIC_STRING
137 // can only be set when T is std::string (enforced by constructor constraint)
138 if constexpr (std::same_as<T, std::string>) {
139 return std::string(this->static_str_);
140 }
141 __builtin_unreachable();
142 case NONE:
143 default:
144 return T{};
145 }
146 }
147
149 if (!this->has_value()) {
150 return {};
151 }
152 return this->value(x...);
153 }
154
155 T value_or(X... x, T default_value) {
156 if (!this->has_value()) {
157 return default_value;
158 }
159 return this->value(x...);
160 }
161
162 protected:
163 enum : uint8_t {
168 STATIC_STRING, // For const char* when T is std::string - avoids heap allocation
170
171 union {
173 std::function<T(X...)> *f_;
174 T (*stateless_f_)(X...);
175 const char *static_str_; // For STATIC_STRING type
176 };
177};
178
183template<typename... Ts> class Condition {
184 public:
186 virtual bool check(const Ts &...x) = 0;
187
189 bool check_tuple(const std::tuple<Ts...> &tuple) {
190 return this->check_tuple_(tuple, std::make_index_sequence<sizeof...(Ts)>{});
191 }
192
193 protected:
194 template<size_t... S> bool check_tuple_(const std::tuple<Ts...> &tuple, std::index_sequence<S...> /*unused*/) {
195 return this->check(std::get<S>(tuple)...);
196 }
197};
198
199template<typename... Ts> class Automation;
200
201template<typename... Ts> class Trigger {
202 public:
204 void trigger(const Ts &...x) {
205 if (this->automation_parent_ == nullptr)
206 return;
207 this->automation_parent_->trigger(x...);
208 }
209 void set_automation_parent(Automation<Ts...> *automation_parent) { this->automation_parent_ = automation_parent; }
210
212 void stop_action() {
213 if (this->automation_parent_ == nullptr)
214 return;
215 this->automation_parent_->stop();
216 }
219 if (this->automation_parent_ == nullptr)
220 return false;
221 return this->automation_parent_->is_running();
222 }
223
224 protected:
226};
227
228template<typename... Ts> class ActionList;
229
230template<typename... Ts> class Action {
231 public:
232 virtual void play_complex(const Ts &...x) {
233 this->num_running_++;
234 this->play(x...);
235 this->play_next_(x...);
236 }
237 virtual void stop_complex() {
238 if (num_running_) {
239 this->stop();
240 this->num_running_ = 0;
241 }
242 this->stop_next_();
243 }
245 virtual bool is_running() { return this->num_running_ > 0 || this->is_running_next_(); }
246
250 int total = this->num_running_;
251 if (this->next_ != nullptr)
252 total += this->next_->num_running_total();
253 return total;
254 }
255
256 protected:
257 friend ActionList<Ts...>;
258 template<typename... Us> friend class ContinuationAction;
259
260 virtual void play(const Ts &...x) = 0;
261 void play_next_(const Ts &...x) {
262 if (this->num_running_ > 0) {
263 this->num_running_--;
264 if (this->next_ != nullptr) {
265 this->next_->play_complex(x...);
266 }
267 }
268 }
269 template<size_t... S> void play_next_tuple_(const std::tuple<Ts...> &tuple, std::index_sequence<S...> /*unused*/) {
270 this->play_next_(std::get<S>(tuple)...);
271 }
272 void play_next_tuple_(const std::tuple<Ts...> &tuple) {
273 this->play_next_tuple_(tuple, std::make_index_sequence<sizeof...(Ts)>{});
274 }
275
276 virtual void stop() {}
277 void stop_next_() {
278 if (this->next_ != nullptr) {
279 this->next_->stop_complex();
280 }
281 }
282
284 if (this->next_ == nullptr)
285 return false;
286 return this->next_->is_running();
287 }
288
289 Action<Ts...> *next_{nullptr};
290
294};
295
296template<typename... Ts> class ActionList {
297 public:
298 void add_action(Action<Ts...> *action) {
299 if (this->actions_end_ == nullptr) {
300 this->actions_begin_ = action;
301 } else {
302 this->actions_end_->next_ = action;
303 }
304 this->actions_end_ = action;
305 }
306 void add_actions(const std::initializer_list<Action<Ts...> *> &actions) {
307 for (auto *action : actions) {
308 this->add_action(action);
309 }
310 }
311 void play(const Ts &...x) {
312 if (this->actions_begin_ != nullptr)
313 this->actions_begin_->play_complex(x...);
314 }
315 void play_tuple(const std::tuple<Ts...> &tuple) {
316 this->play_tuple_(tuple, std::make_index_sequence<sizeof...(Ts)>{});
317 }
318 void stop() {
319 if (this->actions_begin_ != nullptr)
321 }
322 bool empty() const { return this->actions_begin_ == nullptr; }
323
325 bool is_running() {
326 if (this->actions_begin_ == nullptr)
327 return false;
328 return this->actions_begin_->is_running();
329 }
332 if (this->actions_begin_ == nullptr)
333 return 0;
334 return this->actions_begin_->num_running_total();
335 }
336
337 protected:
338 template<size_t... S> void play_tuple_(const std::tuple<Ts...> &tuple, std::index_sequence<S...> /*unused*/) {
339 this->play(std::get<S>(tuple)...);
340 }
341
342 Action<Ts...> *actions_begin_{nullptr};
343 Action<Ts...> *actions_end_{nullptr};
344};
345
346template<typename... Ts> class Automation {
347 public:
349
350 void add_action(Action<Ts...> *action) { this->actions_.add_action(action); }
351 void add_actions(const std::initializer_list<Action<Ts...> *> &actions) { this->actions_.add_actions(actions); }
352
353 void stop() { this->actions_.stop(); }
354
355 void trigger(const Ts &...x) { this->actions_.play(x...); }
356
357 bool is_running() { return this->actions_.is_running(); }
358
360 int num_running() { return this->actions_.num_running(); }
361
362 protected:
365};
366
367} // namespace esphome
virtual bool is_running()
Check if this or any of the following actions are currently running.
Definition automation.h:245
Action< Ts... > * next_
Definition automation.h:289
void play_next_(const Ts &...x)
Definition automation.h:261
virtual void stop_complex()
Definition automation.h:237
virtual void play(const Ts &...x)=0
void play_next_tuple_(const std::tuple< Ts... > &tuple)
Definition automation.h:272
virtual void stop()
Definition automation.h:276
bool is_running_next_()
Definition automation.h:283
int num_running_
The number of instances of this sequence in the list of actions that is currently being executed.
Definition automation.h:293
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:249
void play_next_tuple_(const std::tuple< Ts... > &tuple, std::index_sequence< S... >)
Definition automation.h:269
virtual void play_complex(const Ts &...x)
Definition automation.h:232
void add_action(Action< Ts... > *action)
Definition automation.h:298
Action< Ts... > * actions_end_
Definition automation.h:343
void play(const Ts &...x)
Definition automation.h:311
void play_tuple(const std::tuple< Ts... > &tuple)
Definition automation.h:315
bool is_running()
Check if any action in this action list is currently running.
Definition automation.h:325
bool empty() const
Definition automation.h:322
void add_actions(const std::initializer_list< Action< Ts... > * > &actions)
Definition automation.h:306
Action< Ts... > * actions_begin_
Definition automation.h:342
void play_tuple_(const std::tuple< Ts... > &tuple, std::index_sequence< S... >)
Definition automation.h:338
int num_running()
Return the number of actions in this action list that are currently running.
Definition automation.h:331
void add_action(Action< Ts... > *action)
Definition automation.h:350
void trigger(const Ts &...x)
Definition automation.h:355
Trigger< Ts... > * trigger_
Definition automation.h:363
int num_running()
Return the number of actions in the action part of this automation that are currently running.
Definition automation.h:360
Automation(Trigger< Ts... > *trigger)
Definition automation.h:348
void add_actions(const std::initializer_list< Action< Ts... > * > &actions)
Definition automation.h:351
ActionList< Ts... > actions_
Definition automation.h:364
Base class for all automation conditions.
Definition automation.h:183
bool check_tuple_(const std::tuple< Ts... > &tuple, std::index_sequence< S... >)
Definition automation.h:194
bool check_tuple(const std::tuple< Ts... > &tuple)
Call check with a tuple of values as parameter.
Definition automation.h:189
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:72
std::function< T(X...)> * f_
Definition automation.h:173
TemplatableValue(const char *str)
Definition automation.h:50
TemplatableValue & operator=(TemplatableValue &&other) noexcept
Definition automation.h:108
enum esphome::TemplatableValue::@170 type_
TemplatableValue(TemplatableValue &&other) noexcept
Definition automation.h:85
TemplatableValue & operator=(const TemplatableValue &other)
Definition automation.h:100
T value_or(X... x, T default_value)
Definition automation.h:155
optional< T > optional_value(X... x)
Definition automation.h:148
void trigger(const Ts &...x)
Inform the parent automation that the event has triggered.
Definition automation.h:204
Automation< Ts... > * automation_parent_
Definition automation.h:225
void stop_action()
Stop any action connected to this trigger.
Definition automation.h:212
bool is_action_running()
Returns true if any action connected to this trigger is running.
Definition automation.h:218
void set_automation_parent(Automation< Ts... > *automation_parent)
Definition automation.h:209
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