ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
base_automation.h
Go to the documentation of this file.
1#pragma once
2
5#include "esphome/core/hal.h"
11
12#include <array>
13#include <list>
14#include <tuple>
15#include <type_traits>
16#include <vector>
17
18namespace esphome {
19
20template<size_t N, typename... Ts> class AndCondition : public Condition<Ts...> {
21 public:
22 explicit AndCondition(std::initializer_list<Condition<Ts...> *> conditions) {
23 init_array_from(this->conditions_, conditions);
24 }
25 // Codegen passes each condition as its own argument; a list literal would sit in .rodata (RAM on ESP8266).
26 template<typename... Cs>
27 requires(sizeof...(Cs) == N)
28 explicit AndCondition(Cs *...conditions) : conditions_{conditions...} {}
29 bool check(const Ts &...x) override {
30 for (auto *condition : this->conditions_) {
31 if (!condition->check(x...))
32 return false;
33 }
34
35 return true;
36 }
37
38 protected:
39 std::array<Condition<Ts...> *, N> conditions_{};
40};
41
42template<size_t N, typename... Ts> class OrCondition : public Condition<Ts...> {
43 public:
44 explicit OrCondition(std::initializer_list<Condition<Ts...> *> conditions) {
45 init_array_from(this->conditions_, conditions);
46 }
47 // Codegen passes each condition as its own argument; a list literal would sit in .rodata (RAM on ESP8266).
48 template<typename... Cs>
49 requires(sizeof...(Cs) == N)
50 explicit OrCondition(Cs *...conditions) : conditions_{conditions...} {}
51 bool check(const Ts &...x) override {
52 for (auto *condition : this->conditions_) {
53 if (condition->check(x...))
54 return true;
55 }
56
57 return false;
58 }
59
60 protected:
61 std::array<Condition<Ts...> *, N> conditions_{};
62};
63
64template<typename... Ts> class NotCondition : public Condition<Ts...> {
65 public:
66 explicit NotCondition(Condition<Ts...> *condition) : condition_(condition) {}
67 bool check(const Ts &...x) override { return !this->condition_->check(x...); }
68
69 protected:
71};
72
73template<size_t N, typename... Ts> class XorCondition : public Condition<Ts...> {
74 public:
75 explicit XorCondition(std::initializer_list<Condition<Ts...> *> conditions) {
76 init_array_from(this->conditions_, conditions);
77 }
78 // Codegen passes each condition as its own argument; a list literal would sit in .rodata (RAM on ESP8266).
79 template<typename... Cs>
80 requires(sizeof...(Cs) == N)
81 explicit XorCondition(Cs *...conditions) : conditions_{conditions...} {}
82 bool check(const Ts &...x) override {
83 size_t result = 0;
84 for (auto *condition : this->conditions_) {
85 result += condition->check(x...);
86 }
87
88 return result == 1;
89 }
90
91 protected:
92 std::array<Condition<Ts...> *, N> conditions_{};
93};
94
95template<typename... Ts> class LambdaCondition : public Condition<Ts...> {
96 public:
97 explicit LambdaCondition(std::function<bool(Ts...)> &&f) : f_(std::move(f)) {}
98 bool check(const Ts &...x) override { return this->f_(x...); }
99
100 protected:
101 std::function<bool(Ts...)> f_;
102};
103
107template<typename... Ts> class StatelessLambdaCondition : public Condition<Ts...> {
108 public:
109 explicit StatelessLambdaCondition(bool (*f)(Ts...)) : f_(f) {}
110 bool check(const Ts &...x) override { return this->f_(x...); }
111
112 protected:
113 bool (*f_)(Ts...);
114};
115
116template<typename... Ts> class ForCondition : public Condition<Ts...>, public Component {
117 public:
118 explicit ForCondition(Condition<> *condition) : condition_(condition) {}
119
121
122 void loop() override {
123 // Safe to use cached time - only called from Application::loop()
125 }
126
127 float get_setup_priority() const override { return setup_priority::DATA; }
128
129 bool check(const Ts &...x) override {
130 auto now = millis();
131 if (!this->check_internal_(now))
132 return false;
133 return now - this->last_inactive_ >= this->time_.value(x...);
134 }
135
136 protected:
138 bool cond = this->condition_->check();
139 if (!cond)
140 this->last_inactive_ = now;
141 return cond;
142 }
143
146};
147
148class StartupTrigger : public Trigger<>, public Component {
149 public:
150 explicit StartupTrigger(float setup_priority) : setup_priority_(setup_priority) {}
151 void setup() override { this->trigger(); }
152 float get_setup_priority() const override { return this->setup_priority_; }
153
154 protected:
156};
157
158class ShutdownTrigger : public Trigger<>, public Component {
159 public:
160 explicit ShutdownTrigger(float setup_priority) : setup_priority_(setup_priority) {}
161 void on_shutdown() override { this->trigger(); }
162 float get_setup_priority() const override { return this->setup_priority_; }
163
164 protected:
166};
167
168class LoopTrigger : public Trigger<>, public Component {
169 public:
170 void loop() override { this->trigger(); }
171 float get_setup_priority() const override { return setup_priority::DATA; }
172};
173
174#ifdef ESPHOME_PROJECT_NAME
175class ProjectUpdateTrigger : public Trigger<std::string>, public Component {
176 public:
177 void setup() override {
178 uint32_t hash = fnv1_hash(ESPHOME_PROJECT_NAME);
179 ESPPreferenceObject pref = global_preferences->make_preference<char[30]>(hash, true);
180 char previous_version[30];
181 char current_version[30] = ESPHOME_PROJECT_VERSION_30;
182 if (pref.load(&previous_version)) {
183 int cmp = strcmp(previous_version, current_version);
184 if (cmp < 0) {
185 this->trigger(previous_version);
186 }
187 }
188 pref.save(&current_version);
190 }
191 float get_setup_priority() const override { return setup_priority::PROCESSOR; }
192};
193#endif
194
195template<typename... Ts> class DelayAction : public Action<Ts...> {
196 public:
197 // User provided, not "= default": `new(p) DelayAction()` would zero-fill .bss that is already zero.
198 // constexpr and noexcept keep the rest of the implicit constructor's contract.
199 constexpr explicit DelayAction() noexcept {}
200
202
203 void play_complex(const Ts &...x) override {
204 this->num_running_++;
205
206 // If num_running_ > 1, we have multiple instances running in parallel
207 // In single/restart/queued modes, only one instance runs at a time
208 // Parallel mode uses skip_cancel=true to allow multiple delays to coexist
209 // WARNING: This can accumulate delays if scripts are triggered faster than they complete!
210 // Users should set max_runs on parallel scripts to limit concurrent executions.
211 // Issue #10264: This is a workaround for parallel script delays interfering with each other.
212
213 // Optimization: For no-argument delays (most common case), use direct lambda
214 // to avoid overhead from capturing arguments by value
215 if constexpr (sizeof...(Ts) == 0) {
216 auto f = [this]() { this->play_next_(); };
217 App.scheduler.set_timer_common_(
218 /* component= */ nullptr, Scheduler::SchedulerItem::TIMEOUT, Scheduler::NameType::SELF_POINTER,
219 /* static_name= */ reinterpret_cast<const char *>(this), /* hash_or_id= */ 0, this->delay_.value(),
220 std::move(f),
221 /* skip_cancel= */ this->num_running_ > 1,
222 // Record the owning script (if any) so the blocking warning can name it; propagates across
223 // chained delays via the scheduler.
224 /* source= */ App.get_current_source());
225 } else {
226 // For delays with arguments, capture by value to preserve argument values
227 // Arguments must be copied because original references may be invalid after delay
228 // `mutable` is required so captured copies of non-const reference args (e.g. std::string&)
229 // are passed as non-const lvalues to play_next_(const Ts&...) where Ts may be `T&`
230 auto f = [this, x...]() mutable { this->play_next_(x...); };
231 App.scheduler.set_timer_common_(
232 /* component= */ nullptr, Scheduler::SchedulerItem::TIMEOUT, Scheduler::NameType::SELF_POINTER,
233 /* static_name= */ reinterpret_cast<const char *>(this), /* hash_or_id= */ 0, this->delay_.value(x...),
234 std::move(f),
235 /* skip_cancel= */ this->num_running_ > 1,
236 // See the no-argument branch above: record the owning script for log attribution.
237 /* source= */ App.get_current_source());
238 }
239 }
240
241 void play(const Ts &...x) override { /* ignore - see play_complex */ }
242
243 void stop() override { App.scheduler.cancel_timeout(this); }
244};
245
246template<typename... Ts> class LambdaAction : public Action<Ts...> {
247 public:
248 explicit LambdaAction(std::function<void(Ts...)> &&f) : f_(std::move(f)) {}
249
250 void play(const Ts &...x) override { this->f_(x...); }
251
252 protected:
253 std::function<void(Ts...)> f_;
254};
255
259template<typename... Ts> class StatelessLambdaAction : public Action<Ts...> {
260 public:
261 explicit StatelessLambdaAction(void (*f)(Ts...)) : f_(f) {}
262
263 void play(const Ts &...x) override { this->f_(x...); }
264
265 protected:
266 void (*f_)(Ts...);
267};
268
273template<auto Fn, typename... Ts> class ApplyAction final : public Action<Ts...> {
274 public:
275 void play(const Ts &...x) override { Fn(x...); }
276};
277
279template<auto Fn, typename... Ts> class ApplyCondition final : public Condition<Ts...> {
280 public:
281 bool check(const Ts &...x) override { return Fn(x...); }
282};
283
284template<bool HasElse, typename... Ts> class IfAction : public Action<Ts...> {
285 public:
286 explicit IfAction(Condition<Ts...> *condition) : condition_(condition) {}
287
288 // Precondition: build each branch either with one add_then/add_else call, or with
289 // add_then_action/add_else_action calls followed by one finish_then/finish_else, never both.
290 // set_owner() closes the list; appending after it is a codegen bug.
291 void add_then(const std::initializer_list<Action<Ts...> *> &actions) {
292 this->then_.add_actions(actions);
293 this->then_.set_owner(this);
294 }
295
296 void add_else(const std::initializer_list<Action<Ts...> *> &actions)
297 requires(HasElse)
298 {
299 this->else_.add_actions(actions);
300 this->else_.set_owner(this);
301 }
302
303 // Codegen appends branch actions one at a time, then finishes each branch once; a list literal
304 // would be a constant array in .rodata, which is RAM on ESP8266.
305 void add_then_action(Action<Ts...> *action) { this->then_.add_action(action); }
306 void finish_then() { this->then_.set_owner(this); }
308 requires(HasElse)
309 {
310 this->else_.add_action(action);
311 }
313 requires(HasElse)
314 {
315 this->else_.set_owner(this);
316 }
317
318 void play_complex(const Ts &...x) final {
319 this->num_running_++;
320 // The condition may stop this automation (e.g. script.stop in a lambda)
321 if (this->condition_->check(x...)) {
322 if (!this->then_.empty() && this->num_running_ > 0) {
323 this->then_.play(x...);
324 return;
325 }
326 } else if constexpr (HasElse) {
327 if (!this->else_.empty() && this->num_running_ > 0) {
328 this->else_.play(x...);
329 return;
330 }
331 }
332 this->play_next_(x...);
333 }
334
335 // Branch-end resume hook
336 void play(const Ts &...x) override { this->play_next_(x...); }
337
338 void stop() override {
339 this->then_.stop();
340 if constexpr (HasElse) {
341 this->else_.stop();
342 }
343 }
344
345 protected:
348 struct NoElse {};
349 [[no_unique_address]] std::conditional_t<HasElse, ActionList<Ts...>, NoElse> else_;
350};
351
352template<typename... Ts> class WhileAction : public Action<Ts...> {
353 public:
354 WhileAction(Condition<Ts...> *condition) : condition_(condition) {}
355
356 // Precondition: use either add_then or add_then_action plus one finish_then, never both
357 // (see IfAction::add_then).
358 void add_then(const std::initializer_list<Action<Ts...> *> &actions) {
359 this->then_.add_actions(actions);
360 this->then_.set_owner(this);
361 }
362 void add_then_action(Action<Ts...> *action) { this->then_.add_action(action); }
363 void finish_then() { this->then_.set_owner(this); }
364
365 void play_complex(const Ts &...x) final {
366 this->num_running_++;
367 // Initial condition check
368 if (!this->condition_->check(x...)) {
369 // If new condition check failed, stop loop if running
370 this->then_.stop();
371 this->play_next_(x...);
372 return;
373 }
374
375 // The condition may have stopped this automation
376 if (this->num_running_ == 0)
377 return;
378 // An empty body cannot resume the loop
379 if (this->then_.empty()) {
380 this->play_next_(x...);
381 return;
382 }
383 this->then_.play(x...);
384 }
385
386 // Branch-end resume hook: loop again or continue
387 void play(const Ts &...x) override {
388 // The condition may stop this automation
389 if (this->num_running_ > 0 && this->condition_->check(x...) && this->num_running_ > 0) {
390 this->then_.play(x...);
391 } else {
392 this->play_next_(x...);
393 }
394 }
395
396 void stop() override { this->then_.stop(); }
397
398 protected:
401};
402
403// Forward declaration for RepeatLoopContinuation
404template<typename... Ts> class RepeatAction;
405
408template<typename... Ts> class RepeatLoopContinuation : public Action<uint32_t, Ts...> {
409 public:
411
412 void play(const uint32_t &iteration, const Ts &...x) override;
413
414 protected:
416};
417
418template<typename... Ts> class RepeatAction : public Action<Ts...> {
419 public:
421
422 // Precondition: use either add_then or add_then_action plus one finish_then, never both
423 // (see IfAction::add_then); appending the continuation twice forms a self-loop.
424 void add_then(const std::initializer_list<Action<uint32_t, Ts...> *> &actions) {
425 this->then_.add_actions(actions);
426 this->then_.add_action(&this->loop_continuation_);
427 }
430
431 friend class RepeatLoopContinuation<Ts...>;
432
433 void play_complex(const Ts &...x) override {
434 this->num_running_++;
435 if (this->count_.value(x...) > 0) {
436 this->then_.play(0, x...);
437 } else {
438 this->play_next_(x...);
439 }
440 }
441
442 void play(const Ts &...x) override { /* ignore - see play_complex */ }
443
444 void stop() override { this->then_.stop(); }
445
446 protected:
449};
450
451// Implementation of RepeatLoopContinuation::play
452template<typename... Ts> void RepeatLoopContinuation<Ts...>::play(const uint32_t &iteration, const Ts &...x) {
453 uint32_t next_iteration = iteration + 1;
454 if (next_iteration >= this->parent_->count_.value(x...)) {
455 this->parent_->play_next_(x...);
456 } else {
457 this->parent_->then_.play(next_iteration, x...);
458 }
459}
460
468template<typename... Ts> class WaitUntilAction : public Action<Ts...>, public Component {
469 public:
470 WaitUntilAction(Condition<Ts...> *condition) : condition_(condition) {}
471
473
474 void setup() override {
475 // Start with loop disabled - only enable when there's work to do
476 // IMPORTANT: Only disable if num_running_ is 0, otherwise play_complex() was already
477 // called before our setup() (e.g., from on_boot trigger at same priority level)
478 // and we must not undo its enable_loop() call
479 if (this->num_running_ == 0) {
480 this->disable_loop();
481 }
482 }
483
484 void play_complex(const Ts &...x) override {
485 this->num_running_++;
486 // Check if we can continue immediately.
487 if (this->condition_->check(x...)) {
488 if (this->num_running_ > 0) {
489 this->play_next_(x...);
490 }
491 return;
492 }
493
494 // Store for later processing
495 auto now = millis();
496 auto timeout = this->timeout_value_.optional_value(x...);
497 this->var_queue_.emplace_back(now, timeout, x...);
498
499 // Do immediate check with fresh timestamp - don't call loop() synchronously!
500 // Let the event loop call it to avoid reentrancy issues
501 if (this->process_queue_(now)) {
502 // Only enable loop if we still have pending items
503 this->enable_loop();
504 }
505 }
506
507 void loop() override {
508 // Safe to use cached time - only called from Application::loop()
510 // If queue is now empty, disable loop until next play_complex
511 this->disable_loop();
512 }
513 }
514
515 void stop() override {
516 this->var_queue_.clear();
517 // Tell any process_queue_() call further down the stack that the items it is
518 // still holding were cancelled
519 this->stop_generation_++;
520 this->disable_loop();
521 }
522
523 float get_setup_priority() const override { return setup_priority::DATA; }
524
525 void play(const Ts &...x) override { /* ignore - see play_complex */ }
526
527 protected:
528 struct QueueItem {
529 QueueItem(uint32_t start, optional<uint32_t> timeout, const Ts &...x)
530 : start(start), timeout(timeout), args(x...) {}
532 optional<uint32_t> timeout;
533 std::tuple<stored_trigger_arg_t<Ts>...> args;
534 };
535
536 // Helper: Process queue, triggering completed items and removing them
537 // Returns true if queue still has pending items
539 // Completed items run the rest of the action chain synchronously, and that chain
540 // can re-enter this same action (e.g. a script with mode: restart that executes
541 // itself) and add to or clear var_queue_. Iterating the member list directly would
542 // then corrupt it, so move it aside and iterate a local list instead.
543 std::list<QueueItem> queue;
544 queue.swap(this->var_queue_);
545 std::list<QueueItem> pending;
546 while (!queue.empty()) {
547 auto it = queue.begin();
548 auto start = it->start;
549 auto timeout = it->timeout;
550
551 // Check if timeout has expired
552 auto expired = timeout && (now - start) >= *timeout;
553
554 // Keep waiting if not expired and condition not met
555 if (!expired && !std::apply([this](auto &...args) { return this->condition_->check(args...); }, it->args)) {
556 pending.splice(pending.end(), queue, it);
557 continue;
558 }
559
560 // Condition met or timed out - trigger the next action. Keep the item in a local
561 // holder so its arguments stay valid while the chain runs, without any nested
562 // process_queue_() call being able to see (and fire) it again.
563 std::list<QueueItem> completed;
564 completed.splice(completed.begin(), queue, it);
565 uint8_t generation = this->stop_generation_;
566 std::apply([this](auto &...args) { this->play_next_(args...); }, completed.front().args);
567 if (generation != this->stop_generation_) {
568 // stop() ran inside the chain - the items still held locally were cancelled
569 pending.clear();
570 break;
571 }
572 }
573
574 // Re-entrant continuations may have enqueued new waits into var_queue_; put the
575 // older still-waiting items back in front of them to keep FIFO firing order
576 this->var_queue_.splice(this->var_queue_.begin(), pending);
577
578 return !this->var_queue_.empty();
579 }
580
582 std::list<QueueItem> var_queue_{};
583 // Bumped by stop() so process_queue_() can detect a stop from inside play_next_()
585};
586
587template<typename... Ts> class UpdateComponentAction : public Action<Ts...> {
588 public:
590
591 void play(const Ts &...x) override {
592 if (!this->component_->is_ready())
593 return;
594 this->component_->update();
595 }
596
597 protected:
599};
600
601template<typename... Ts> class SuspendComponentAction : public Action<Ts...> {
602 public:
604
605 void play(const Ts &...x) override {
606 if (!this->component_->is_ready())
607 return;
608 this->component_->stop_poller();
609 }
610
611 protected:
613};
614
615template<typename... Ts> class ResumeComponentAction : public Action<Ts...> {
616 public:
618 TEMPLATABLE_VALUE(uint32_t, update_interval)
619
620 void play(const Ts &...x) override {
621 if (!this->component_->is_ready()) {
622 return;
623 }
624 optional<uint32_t> update_interval = this->update_interval_.optional_value(x...);
625 if (update_interval.has_value()) {
626 this->component_->set_update_interval(update_interval.value());
627 }
628 this->component_->start_poller();
629 }
630
631 protected:
633};
634
635} // namespace esphome
void play_next_(const Ts &...x)
Definition automation.h:613
virtual void play(const Ts &...x)=0
virtual void play_complex(const Ts &...x)
Definition automation.h:584
void add_action(Action< Ts... > *action)
Definition automation.h:664
bool empty() const
Definition automation.h:696
void add_actions(const std::initializer_list< Action< Ts... > * > &actions)
Definition automation.h:668
void play(const Ts &...x) ESPHOME_ALWAYS_INLINE
Definition automation.h:685
void set_owner(Action< Ts... > *owner)
Resume owner via play() when the list finishes; call once, after adding actions.
Definition automation.h:677
AndCondition(std::initializer_list< Condition< Ts... > * > conditions)
bool check(const Ts &...x) override
std::array< Condition< Ts... > *, N > conditions_
AndCondition(Cs *...conditions)
const LogString * get_current_source()
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
Runs one codegen-generated function that has the parent and every field baked in.
void play(const Ts &...x) override
Condition counterpart of ApplyAction: one codegen-generated predicate with the parent baked in.
bool check(const Ts &...x) override
virtual void setup()
Where the component's initialization should happen.
Definition component.cpp:84
bool is_ready() const
void enable_loop()
Enable this component's loop.
Definition component.h:248
void disable_loop()
Disable this component's loop.
Base class for all automation conditions.
Definition automation.h:533
virtual bool check(const Ts &...x)=0
Check whether this condition passes. This condition check must be instant, and not cause any delays.
void play(const Ts &...x) override
TEMPLATABLE_VALUE(uint32_t, delay) void play_complex(const Ts &...x) override
constexpr DelayAction() noexcept
bool check_internal_(uint32_t now)
ForCondition(Condition<> *condition)
float get_setup_priority() const override
bool check(const Ts &...x) override
TEMPLATABLE_VALUE(uint32_t, time)
ActionList< Ts... > then_
Condition< Ts... > * condition_
void play(const Ts &...x) override
void add_then(const std::initializer_list< Action< Ts... > * > &actions)
void stop() override
void play_complex(const Ts &...x) final
std::conditional_t< HasElse, ActionList< Ts... >, NoElse > else_
void add_else_action(Action< Ts... > *action)
void add_then_action(Action< Ts... > *action)
IfAction(Condition< Ts... > *condition)
void add_else(const std::initializer_list< Action< Ts... > * > &actions)
LambdaAction(std::function< void(Ts...)> &&f)
void play(const Ts &...x) override
std::function< void(Ts...)> f_
bool check(const Ts &...x) override
LambdaCondition(std::function< bool(Ts...)> &&f)
std::function< bool(Ts...)> f_
float get_setup_priority() const override
Condition< Ts... > * condition_
bool check(const Ts &...x) override
NotCondition(Condition< Ts... > *condition)
OrCondition(std::initializer_list< Condition< Ts... > * > conditions)
OrCondition(Cs *...conditions)
bool check(const Ts &...x) override
std::array< Condition< Ts... > *, N > conditions_
This class simplifies creating components that periodically check a state.
Definition component.h:519
void set_update_interval(uint32_t update_interval)
Manually set the update interval in ms for this polling object.
Definition component.h:533
virtual void update()=0
float get_setup_priority() const override
ActionList< uint32_t, Ts... > then_
TEMPLATABLE_VALUE(uint32_t, count) void add_then(const std
void add_then_action(Action< uint32_t, Ts... > *action)
void play_complex(const Ts &...x) override
void play(const Ts &...x) override
RepeatLoopContinuation< Ts... > loop_continuation_
Loop continuation for RepeatAction that increments iteration and repeats or continues.
RepeatLoopContinuation(RepeatAction< Ts... > *parent)
RepeatAction< Ts... > * parent_
void play(const uint32_t &iteration, const Ts &...x) override
ResumeComponentAction(PollingComponent *component)
TEMPLATABLE_VALUE(uint32_t, update_interval) void play(const Ts &...x) override
ShutdownTrigger(float setup_priority)
float get_setup_priority() const override
float get_setup_priority() const override
StartupTrigger(float setup_priority)
Optimized lambda action for stateless lambdas (no capture).
void play(const Ts &...x) override
Optimized lambda condition for stateless lambdas (no capture).
bool check(const Ts &...x) override
SuspendComponentAction(PollingComponent *component)
void play(const Ts &...x) override
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Definition automation.h:556
void play(const Ts &...x) override
UpdateComponentAction(PollingComponent *component)
Wait until a condition is true to continue execution.
WaitUntilAction(Condition< Ts... > *condition)
std::list< QueueItem > var_queue_
TEMPLATABLE_VALUE(uint32_t, timeout_value) void setup() override
void play_complex(const Ts &...x) override
void play(const Ts &...x) override
bool process_queue_(uint32_t now)
Condition< Ts... > * condition_
float get_setup_priority() const override
void add_then(const std::initializer_list< Action< Ts... > * > &actions)
void play_complex(const Ts &...x) final
WhileAction(Condition< Ts... > *condition)
void play(const Ts &...x) override
void add_then_action(Action< Ts... > *action)
Condition< Ts... > * condition_
ActionList< Ts... > then_
std::array< Condition< Ts... > *, N > conditions_
XorCondition(Cs *...conditions)
bool check(const Ts &...x) override
XorCondition(std::initializer_list< Condition< Ts... > * > conditions)
const Component * component
Definition component.cpp:34
unsigned count
Definition gif.cpp:51
constexpr float DATA
For components that import data from directly connected sensors like DHT.
Definition component.h:45
constexpr float PROCESSOR
For components that use data from sensors like displays.
Definition component.h:47
ESPPreferences * global_preferences
uint32_t fnv1_hash(const char *str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:169
void init_array_from(std::array< T, N > &dest, std::initializer_list< T > src)
Initialize a std::array from an initializer_list.
Definition helpers.h:599
void HOT delay(uint32_t ms)
Definition hal.cpp:85
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
Application App
Global storage of Application pointer - only one Application can exist.
STL namespace.
static void uint32_t
ESPPreferenceObject make_preference(size_t, uint32_t, bool)
Definition preferences.h:24
bool sync()
Commit pending writes to flash.
Definition preferences.h:33
std::tuple< stored_trigger_arg_t< Ts >... > args
QueueItem(uint32_t start, optional< uint32_t > timeout, const Ts &...x)
uint16_t x
Definition tt21100.cpp:5