ESPHome 2025.12.0-dev
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// https://stackoverflow.com/questions/7858817/unpacking-a-tuple-to-call-a-matching-function-pointer/7858971#7858971
15template<int...> struct seq {}; // NOLINT
16template<int N, int... S> struct gens : gens<N - 1, N - 1, S...> {}; // NOLINT
17template<int... S> struct gens<0, S...> { using type = seq<S...>; }; // NOLINT
18
19#define TEMPLATABLE_VALUE_(type, name) \
20 protected: \
21 TemplatableValue<type, Ts...> name##_{}; \
22\
23 public: \
24 template<typename V> void set_##name(V name) { this->name##_ = name; }
25
26#define TEMPLATABLE_VALUE(type, name) TEMPLATABLE_VALUE_(type, name)
27
28template<typename T, typename... X> class TemplatableValue {
29 public:
31
32 template<typename F> TemplatableValue(F value) requires(!std::invocable<F, X...>) : type_(VALUE) {
33 new (&this->value_) T(std::move(value));
34 }
35
36 // For stateless lambdas (convertible to function pointer): use function pointer
37 template<typename F>
38 TemplatableValue(F f) requires std::invocable<F, X...> && std::convertible_to<F, T (*)(X...)>
40 this->stateless_f_ = f; // Implicit conversion to function pointer
41 }
42
43 // For stateful lambdas (not convertible to function pointer): use std::function
44 template<typename F>
45 TemplatableValue(F f) requires std::invocable<F, X...> &&(!std::convertible_to<F, T (*)(X...)>) : type_(LAMBDA) {
46 this->f_ = new std::function<T(X...)>(std::move(f));
47 }
48
49 // Copy constructor
51 if (type_ == VALUE) {
52 new (&this->value_) T(other.value_);
53 } else if (type_ == LAMBDA) {
54 this->f_ = new std::function<T(X...)>(*other.f_);
55 } else if (type_ == STATELESS_LAMBDA) {
56 this->stateless_f_ = other.stateless_f_;
57 }
58 }
59
60 // Move constructor
61 TemplatableValue(TemplatableValue &&other) noexcept : type_(other.type_) {
62 if (type_ == VALUE) {
63 new (&this->value_) T(std::move(other.value_));
64 } else if (type_ == LAMBDA) {
65 this->f_ = other.f_;
66 other.f_ = nullptr;
67 } else if (type_ == STATELESS_LAMBDA) {
68 this->stateless_f_ = other.stateless_f_;
69 }
70 other.type_ = NONE;
71 }
72
73 // Assignment operators
75 if (this != &other) {
76 this->~TemplatableValue();
77 new (this) TemplatableValue(other);
78 }
79 return *this;
80 }
81
83 if (this != &other) {
84 this->~TemplatableValue();
85 new (this) TemplatableValue(std::move(other));
86 }
87 return *this;
88 }
89
91 if (type_ == VALUE) {
92 this->value_.~T();
93 } else if (type_ == LAMBDA) {
94 delete this->f_;
95 }
96 // STATELESS_LAMBDA/NONE: no cleanup needed (function pointer or empty, not heap-allocated)
97 }
98
99 bool has_value() { return this->type_ != NONE; }
100
101 T value(X... x) {
102 switch (this->type_) {
103 case STATELESS_LAMBDA:
104 return this->stateless_f_(x...); // Direct function pointer call
105 case LAMBDA:
106 return (*this->f_)(x...); // std::function call
107 case VALUE:
108 return this->value_;
109 case NONE:
110 default:
111 return T{};
112 }
113 }
114
116 if (!this->has_value()) {
117 return {};
118 }
119 return this->value(x...);
120 }
121
122 T value_or(X... x, T default_value) {
123 if (!this->has_value()) {
124 return default_value;
125 }
126 return this->value(x...);
127 }
128
129 protected:
130 enum : uint8_t {
136
137 union {
139 std::function<T(X...)> *f_;
140 T (*stateless_f_)(X...);
141 };
142};
143
148template<typename... Ts> class Condition {
149 public:
151 virtual bool check(const Ts &...x) = 0;
152
154 bool check_tuple(const std::tuple<Ts...> &tuple) {
155 return this->check_tuple_(tuple, typename gens<sizeof...(Ts)>::type());
156 }
157
158 protected:
159 template<int... S> bool check_tuple_(const std::tuple<Ts...> &tuple, seq<S...> /*unused*/) {
160 return this->check(std::get<S>(tuple)...);
161 }
162};
163
164template<typename... Ts> class Automation;
165
166template<typename... Ts> class Trigger {
167 public:
169 void trigger(const Ts &...x) {
170 if (this->automation_parent_ == nullptr)
171 return;
172 this->automation_parent_->trigger(x...);
173 }
174 void set_automation_parent(Automation<Ts...> *automation_parent) { this->automation_parent_ = automation_parent; }
175
177 void stop_action() {
178 if (this->automation_parent_ == nullptr)
179 return;
180 this->automation_parent_->stop();
181 }
184 if (this->automation_parent_ == nullptr)
185 return false;
186 return this->automation_parent_->is_running();
187 }
188
189 protected:
191};
192
193template<typename... Ts> class ActionList;
194
195template<typename... Ts> class Action {
196 public:
197 virtual void play_complex(const Ts &...x) {
198 this->num_running_++;
199 this->play(x...);
200 this->play_next_(x...);
201 }
202 virtual void stop_complex() {
203 if (num_running_) {
204 this->stop();
205 this->num_running_ = 0;
206 }
207 this->stop_next_();
208 }
210 virtual bool is_running() { return this->num_running_ > 0 || this->is_running_next_(); }
211
215 int total = this->num_running_;
216 if (this->next_ != nullptr)
217 total += this->next_->num_running_total();
218 return total;
219 }
220
221 protected:
222 friend ActionList<Ts...>;
223 template<typename... Us> friend class ContinuationAction;
224
225 virtual void play(const Ts &...x) = 0;
226 void play_next_(const Ts &...x) {
227 if (this->num_running_ > 0) {
228 this->num_running_--;
229 if (this->next_ != nullptr) {
230 this->next_->play_complex(x...);
231 }
232 }
233 }
234 template<int... S> void play_next_tuple_(const std::tuple<Ts...> &tuple, seq<S...> /*unused*/) {
235 this->play_next_(std::get<S>(tuple)...);
236 }
237 void play_next_tuple_(const std::tuple<Ts...> &tuple) {
238 this->play_next_tuple_(tuple, typename gens<sizeof...(Ts)>::type());
239 }
240
241 virtual void stop() {}
242 void stop_next_() {
243 if (this->next_ != nullptr) {
244 this->next_->stop_complex();
245 }
246 }
247
249 if (this->next_ == nullptr)
250 return false;
251 return this->next_->is_running();
252 }
253
254 Action<Ts...> *next_{nullptr};
255
259};
260
261template<typename... Ts> class ActionList {
262 public:
263 void add_action(Action<Ts...> *action) {
264 if (this->actions_end_ == nullptr) {
265 this->actions_begin_ = action;
266 } else {
267 this->actions_end_->next_ = action;
268 }
269 this->actions_end_ = action;
270 }
271 void add_actions(const std::initializer_list<Action<Ts...> *> &actions) {
272 for (auto *action : actions) {
273 this->add_action(action);
274 }
275 }
276 void play(const Ts &...x) {
277 if (this->actions_begin_ != nullptr)
278 this->actions_begin_->play_complex(x...);
279 }
280 void play_tuple(const std::tuple<Ts...> &tuple) { this->play_tuple_(tuple, typename gens<sizeof...(Ts)>::type()); }
281 void stop() {
282 if (this->actions_begin_ != nullptr)
284 }
285 bool empty() const { return this->actions_begin_ == nullptr; }
286
288 bool is_running() {
289 if (this->actions_begin_ == nullptr)
290 return false;
291 return this->actions_begin_->is_running();
292 }
295 if (this->actions_begin_ == nullptr)
296 return 0;
297 return this->actions_begin_->num_running_total();
298 }
299
300 protected:
301 template<int... S> void play_tuple_(const std::tuple<Ts...> &tuple, seq<S...> /*unused*/) {
302 this->play(std::get<S>(tuple)...);
303 }
304
305 Action<Ts...> *actions_begin_{nullptr};
306 Action<Ts...> *actions_end_{nullptr};
307};
308
309template<typename... Ts> class Automation {
310 public:
312
313 void add_action(Action<Ts...> *action) { this->actions_.add_action(action); }
314 void add_actions(const std::initializer_list<Action<Ts...> *> &actions) { this->actions_.add_actions(actions); }
315
316 void stop() { this->actions_.stop(); }
317
318 void trigger(const Ts &...x) { this->actions_.play(x...); }
319
320 bool is_running() { return this->actions_.is_running(); }
321
323 int num_running() { return this->actions_.num_running(); }
324
325 protected:
328};
329
330} // namespace esphome
virtual bool is_running()
Check if this or any of the following actions are currently running.
Definition automation.h:210
void play_next_tuple_(const std::tuple< Ts... > &tuple, seq< S... >)
Definition automation.h:234
Action< Ts... > * next_
Definition automation.h:254
void play_next_(const Ts &...x)
Definition automation.h:226
virtual void stop_complex()
Definition automation.h:202
virtual void play(const Ts &...x)=0
void play_next_tuple_(const std::tuple< Ts... > &tuple)
Definition automation.h:237
virtual void stop()
Definition automation.h:241
bool is_running_next_()
Definition automation.h:248
int num_running_
The number of instances of this sequence in the list of actions that is currently being executed.
Definition automation.h:258
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:214
virtual void play_complex(const Ts &...x)
Definition automation.h:197
void add_action(Action< Ts... > *action)
Definition automation.h:263
Action< Ts... > * actions_end_
Definition automation.h:306
void play(const Ts &...x)
Definition automation.h:276
void play_tuple(const std::tuple< Ts... > &tuple)
Definition automation.h:280
bool is_running()
Check if any action in this action list is currently running.
Definition automation.h:288
void play_tuple_(const std::tuple< Ts... > &tuple, seq< S... >)
Definition automation.h:301
bool empty() const
Definition automation.h:285
void add_actions(const std::initializer_list< Action< Ts... > * > &actions)
Definition automation.h:271
Action< Ts... > * actions_begin_
Definition automation.h:305
int num_running()
Return the number of actions in this action list that are currently running.
Definition automation.h:294
void add_action(Action< Ts... > *action)
Definition automation.h:313
void trigger(const Ts &...x)
Definition automation.h:318
Trigger< Ts... > * trigger_
Definition automation.h:326
int num_running()
Return the number of actions in the action part of this automation that are currently running.
Definition automation.h:323
Automation(Trigger< Ts... > *trigger)
Definition automation.h:311
void add_actions(const std::initializer_list< Action< Ts... > * > &actions)
Definition automation.h:314
ActionList< Ts... > actions_
Definition automation.h:327
Base class for all automation conditions.
Definition automation.h:148
bool check_tuple(const std::tuple< Ts... > &tuple)
Call check with a tuple of values as parameter.
Definition automation.h:154
bool check_tuple_(const std::tuple< Ts... > &tuple, seq< S... >)
Definition automation.h:159
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:50
std::function< T(X...)> * f_
Definition automation.h:139
TemplatableValue & operator=(TemplatableValue &&other) noexcept
Definition automation.h:82
enum esphome::TemplatableValue::@167 type_
TemplatableValue(TemplatableValue &&other) noexcept
Definition automation.h:61
TemplatableValue & operator=(const TemplatableValue &other)
Definition automation.h:74
T value_or(X... x, T default_value)
Definition automation.h:122
optional< T > optional_value(X... x)
Definition automation.h:115
void trigger(const Ts &...x)
Inform the parent automation that the event has triggered.
Definition automation.h:169
Automation< Ts... > * automation_parent_
Definition automation.h:190
void stop_action()
Stop any action connected to this trigger.
Definition automation.h:177
bool is_action_running()
Returns true if any action connected to this trigger is running.
Definition automation.h:183
void set_automation_parent(Automation< Ts... > *automation_parent)
Definition automation.h:174
uint16_t type
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint16_t x
Definition tt21100.cpp:5