ESPHome 2025.10.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"
10
11#include <vector>
12
13namespace esphome {
14
15template<typename... Ts> class AndCondition : public Condition<Ts...> {
16 public:
17 explicit AndCondition(const std::vector<Condition<Ts...> *> &conditions) : conditions_(conditions) {}
18 bool check(Ts... x) override {
19 for (auto *condition : this->conditions_) {
20 if (!condition->check(x...))
21 return false;
22 }
23
24 return true;
25 }
26
27 protected:
28 std::vector<Condition<Ts...> *> conditions_;
29};
30
31template<typename... Ts> class OrCondition : public Condition<Ts...> {
32 public:
33 explicit OrCondition(const std::vector<Condition<Ts...> *> &conditions) : conditions_(conditions) {}
34 bool check(Ts... x) override {
35 for (auto *condition : this->conditions_) {
36 if (condition->check(x...))
37 return true;
38 }
39
40 return false;
41 }
42
43 protected:
44 std::vector<Condition<Ts...> *> conditions_;
45};
46
47template<typename... Ts> class NotCondition : public Condition<Ts...> {
48 public:
49 explicit NotCondition(Condition<Ts...> *condition) : condition_(condition) {}
50 bool check(Ts... x) override { return !this->condition_->check(x...); }
51
52 protected:
54};
55
56template<typename... Ts> class XorCondition : public Condition<Ts...> {
57 public:
58 explicit XorCondition(const std::vector<Condition<Ts...> *> &conditions) : conditions_(conditions) {}
59 bool check(Ts... x) override {
60 size_t result = 0;
61 for (auto *condition : this->conditions_) {
62 result += condition->check(x...);
63 }
64
65 return result == 1;
66 }
67
68 protected:
69 std::vector<Condition<Ts...> *> conditions_;
70};
71
72template<typename... Ts> class LambdaCondition : public Condition<Ts...> {
73 public:
74 explicit LambdaCondition(std::function<bool(Ts...)> &&f) : f_(std::move(f)) {}
75 bool check(Ts... x) override { return this->f_(x...); }
76
77 protected:
78 std::function<bool(Ts...)> f_;
79};
80
81template<typename... Ts> class ForCondition : public Condition<Ts...>, public Component {
82 public:
83 explicit ForCondition(Condition<> *condition) : condition_(condition) {}
84
85 TEMPLATABLE_VALUE(uint32_t, time);
86
87 void loop() override { this->check_internal(); }
88 float get_setup_priority() const override { return setup_priority::DATA; }
90 bool cond = this->condition_->check();
91 if (!cond)
92 this->last_inactive_ = millis();
93 return cond;
94 }
95
96 bool check(Ts... x) override {
97 if (!this->check_internal())
98 return false;
99 return millis() - this->last_inactive_ >= this->time_.value(x...);
100 }
101
102 protected:
104 uint32_t last_inactive_{0};
105};
106
107class StartupTrigger : public Trigger<>, public Component {
108 public:
109 explicit StartupTrigger(float setup_priority) : setup_priority_(setup_priority) {}
110 void setup() override { this->trigger(); }
111 float get_setup_priority() const override { return this->setup_priority_; }
112
113 protected:
115};
116
117class ShutdownTrigger : public Trigger<>, public Component {
118 public:
119 explicit ShutdownTrigger(float setup_priority) : setup_priority_(setup_priority) {}
120 void on_shutdown() override { this->trigger(); }
121 float get_setup_priority() const override { return this->setup_priority_; }
122
123 protected:
125};
126
127class LoopTrigger : public Trigger<>, public Component {
128 public:
129 void loop() override { this->trigger(); }
130 float get_setup_priority() const override { return setup_priority::DATA; }
131};
132
133#ifdef ESPHOME_PROJECT_NAME
134class ProjectUpdateTrigger : public Trigger<std::string>, public Component {
135 public:
136 void setup() override {
137 uint32_t hash = fnv1_hash(ESPHOME_PROJECT_NAME);
138 ESPPreferenceObject pref = global_preferences->make_preference<char[30]>(hash, true);
139 char previous_version[30];
140 char current_version[30] = ESPHOME_PROJECT_VERSION_30;
141 if (pref.load(&previous_version)) {
142 int cmp = strcmp(previous_version, current_version);
143 if (cmp < 0) {
144 this->trigger(previous_version);
145 }
146 }
147 pref.save(&current_version);
149 }
150 float get_setup_priority() const override { return setup_priority::PROCESSOR; }
151};
152#endif
153
154template<typename... Ts> class DelayAction : public Action<Ts...>, public Component {
155 public:
156 explicit DelayAction() = default;
157
159
160 void play_complex(Ts... x) override {
161 auto f = std::bind(&DelayAction<Ts...>::play_next_, this, x...);
162 this->num_running_++;
163
164 // If num_running_ > 1, we have multiple instances running in parallel
165 // In single/restart/queued modes, only one instance runs at a time
166 // Parallel mode uses skip_cancel=true to allow multiple delays to coexist
167 // WARNING: This can accumulate delays if scripts are triggered faster than they complete!
168 // Users should set max_runs on parallel scripts to limit concurrent executions.
169 // Issue #10264: This is a workaround for parallel script delays interfering with each other.
170 App.scheduler.set_timer_common_(this, Scheduler::SchedulerItem::TIMEOUT,
171 /* is_static_string= */ true, "delay", this->delay_.value(x...), std::move(f),
172 /* is_retry= */ false, /* skip_cancel= */ this->num_running_ > 1);
173 }
174 float get_setup_priority() const override { return setup_priority::HARDWARE; }
175
176 void play(Ts... x) override { /* ignore - see play_complex */
177 }
178
179 void stop() override { this->cancel_timeout("delay"); }
180};
181
182template<typename... Ts> class LambdaAction : public Action<Ts...> {
183 public:
184 explicit LambdaAction(std::function<void(Ts...)> &&f) : f_(std::move(f)) {}
185
186 void play(Ts... x) override { this->f_(x...); }
187
188 protected:
189 std::function<void(Ts...)> f_;
190};
191
192template<typename... Ts> class IfAction : public Action<Ts...> {
193 public:
194 explicit IfAction(Condition<Ts...> *condition) : condition_(condition) {}
195
196 void add_then(const std::vector<Action<Ts...> *> &actions) {
197 this->then_.add_actions(actions);
198 this->then_.add_action(new LambdaAction<Ts...>([this](Ts... x) { this->play_next_(x...); }));
199 }
200
201 void add_else(const std::vector<Action<Ts...> *> &actions) {
202 this->else_.add_actions(actions);
203 this->else_.add_action(new LambdaAction<Ts...>([this](Ts... x) { this->play_next_(x...); }));
204 }
205
206 void play_complex(Ts... x) override {
207 this->num_running_++;
208 bool res = this->condition_->check(x...);
209 if (res) {
210 if (this->then_.empty()) {
211 this->play_next_(x...);
212 } else if (this->num_running_ > 0) {
213 this->then_.play(x...);
214 }
215 } else {
216 if (this->else_.empty()) {
217 this->play_next_(x...);
218 } else if (this->num_running_ > 0) {
219 this->else_.play(x...);
220 }
221 }
222 }
223
224 void play(Ts... x) override { /* ignore - see play_complex */
225 }
226
227 void stop() override {
228 this->then_.stop();
229 this->else_.stop();
230 }
231
232 protected:
236};
237
238template<typename... Ts> class WhileAction : public Action<Ts...> {
239 public:
240 WhileAction(Condition<Ts...> *condition) : condition_(condition) {}
241
242 void add_then(const std::vector<Action<Ts...> *> &actions) {
243 this->then_.add_actions(actions);
244 this->then_.add_action(new LambdaAction<Ts...>([this](Ts... x) {
245 if (this->num_running_ > 0 && this->condition_->check_tuple(this->var_)) {
246 // play again
247 if (this->num_running_ > 0) {
248 this->then_.play_tuple(this->var_);
249 }
250 } else {
251 // condition false, play next
252 this->play_next_tuple_(this->var_);
253 }
254 }));
255 }
256
257 void play_complex(Ts... x) override {
258 this->num_running_++;
259 // Store loop parameters
260 this->var_ = std::make_tuple(x...);
261 // Initial condition check
262 if (!this->condition_->check_tuple(this->var_)) {
263 // If new condition check failed, stop loop if running
264 this->then_.stop();
265 this->play_next_tuple_(this->var_);
266 return;
267 }
268
269 if (this->num_running_ > 0) {
270 this->then_.play_tuple(this->var_);
271 }
272 }
273
274 void play(Ts... x) override { /* ignore - see play_complex */
275 }
276
277 void stop() override { this->then_.stop(); }
278
279 protected:
282 std::tuple<Ts...> var_{};
283};
284
285template<typename... Ts> class RepeatAction : public Action<Ts...> {
286 public:
287 TEMPLATABLE_VALUE(uint32_t, count)
288
289 void add_then(const std::vector<Action<uint32_t, Ts...> *> &actions) {
290 this->then_.add_actions(actions);
291 this->then_.add_action(new LambdaAction<uint32_t, Ts...>([this](uint32_t iteration, Ts... x) {
292 iteration++;
293 if (iteration >= this->count_.value(x...)) {
294 this->play_next_tuple_(this->var_);
295 } else {
296 this->then_.play(iteration, x...);
297 }
298 }));
299 }
300
301 void play_complex(Ts... x) override {
302 this->num_running_++;
303 this->var_ = std::make_tuple(x...);
304 if (this->count_.value(x...) > 0) {
305 this->then_.play(0, x...);
306 } else {
307 this->play_next_tuple_(this->var_);
308 }
309 }
310
311 void play(Ts... x) override { /* ignore - see play_complex */
312 }
313
314 void stop() override { this->then_.stop(); }
315
316 protected:
317 ActionList<uint32_t, Ts...> then_;
318 std::tuple<Ts...> var_;
319};
320
321template<typename... Ts> class WaitUntilAction : public Action<Ts...>, public Component {
322 public:
323 WaitUntilAction(Condition<Ts...> *condition) : condition_(condition) {}
324
325 TEMPLATABLE_VALUE(uint32_t, timeout_value)
326
327 void play_complex(Ts... x) override {
328 this->num_running_++;
329 // Check if we can continue immediately.
330 if (this->condition_->check(x...)) {
331 if (this->num_running_ > 0) {
332 this->play_next_(x...);
333 }
334 return;
335 }
336 this->var_ = std::make_tuple(x...);
337
338 if (this->timeout_value_.has_value()) {
339 auto f = std::bind(&WaitUntilAction<Ts...>::play_next_, this, x...);
340 this->set_timeout("timeout", this->timeout_value_.value(x...), f);
341 }
342
343 this->loop();
344 }
345
346 void loop() override {
347 if (this->num_running_ == 0)
348 return;
349
350 if (!this->condition_->check_tuple(this->var_)) {
351 return;
352 }
353
354 this->cancel_timeout("timeout");
355
356 this->play_next_tuple_(this->var_);
357 }
358
359 float get_setup_priority() const override { return setup_priority::DATA; }
360
361 void play(Ts... x) override { /* ignore - see play_complex */
362 }
363
364 void stop() override { this->cancel_timeout("timeout"); }
365
366 protected:
368 std::tuple<Ts...> var_{};
369};
370
371template<typename... Ts> class UpdateComponentAction : public Action<Ts...> {
372 public:
373 UpdateComponentAction(PollingComponent *component) : component_(component) {}
374
375 void play(Ts... x) override {
376 if (!this->component_->is_ready())
377 return;
378 this->component_->update();
379 }
380
381 protected:
383};
384
385template<typename... Ts> class SuspendComponentAction : public Action<Ts...> {
386 public:
387 SuspendComponentAction(PollingComponent *component) : component_(component) {}
388
389 void play(Ts... x) override {
390 if (!this->component_->is_ready())
391 return;
392 this->component_->stop_poller();
393 }
394
395 protected:
397};
398
399template<typename... Ts> class ResumeComponentAction : public Action<Ts...> {
400 public:
401 ResumeComponentAction(PollingComponent *component) : component_(component) {}
402 TEMPLATABLE_VALUE(uint32_t, update_interval)
403
404 void play(Ts... x) override {
405 if (!this->component_->is_ready()) {
406 return;
407 }
408 optional<uint32_t> update_interval = this->update_interval_.optional_value(x...);
409 if (update_interval.has_value()) {
410 this->component_->set_update_interval(update_interval.value());
411 }
412 this->component_->start_poller();
413 }
414
415 protected:
417};
418
419} // namespace esphome
virtual void play_complex(Ts... x)
Definition automation.h:173
void play_next_(Ts... x)
Definition automation.h:201
void add_action(Action< Ts... > *action)
Definition automation.h:238
void play(Ts... x)
Definition automation.h:251
void add_actions(const std::vector< Action< Ts... > * > &actions)
Definition automation.h:246
bool empty() const
Definition automation.h:260
AndCondition(const std::vector< Condition< Ts... > * > &conditions)
std::vector< Condition< Ts... > * > conditions_
bool check(Ts... x) override
bool cancel_timeout(const std::string &name)
Cancel a timeout function.
Base class for all automation conditions.
Definition automation.h:124
virtual bool check(Ts... x)=0
Check whether this condition passes. This condition check must be instant, and not cause any delays.
void play(Ts... x) override
float get_setup_priority() const override
TEMPLATABLE_VALUE(uint32_t, delay) void play_complex(Ts... x) override
bool save(const T *src)
Definition preferences.h:21
virtual bool sync()=0
Commit pending writes to flash.
virtual ESPPreferenceObject make_preference(size_t length, uint32_t type, bool in_flash)=0
ForCondition(Condition<> *condition)
float get_setup_priority() const override
bool check(Ts... x) override
TEMPLATABLE_VALUE(uint32_t, time)
Condition< Ts... > * condition_
void play_complex(Ts... x) override
void stop() override
ActionList< Ts... > else_
void add_else(const std::vector< Action< Ts... > * > &actions)
void play(Ts... x) override
void add_then(const std::vector< Action< Ts... > * > &actions)
ActionList< Ts... > then_
IfAction(Condition< Ts... > *condition)
LambdaAction(std::function< void(Ts...)> &&f)
std::function< void(Ts...)> f_
void play(Ts... x) override
bool check(Ts... x) override
LambdaCondition(std::function< bool(Ts...)> &&f)
std::function< bool(Ts...)> f_
float get_setup_priority() const override
bool check(Ts... x) override
Condition< Ts... > * condition_
NotCondition(Condition< Ts... > *condition)
std::vector< Condition< Ts... > * > conditions_
bool check(Ts... x) override
OrCondition(const std::vector< Condition< Ts... > * > &conditions)
This class simplifies creating components that periodically check a state.
Definition component.h:429
float get_setup_priority() const override
ActionList< uint32_t, Ts... > then_
TEMPLATABLE_VALUE(uint32_t, count) void add_then(const std
void play(Ts... x) override
std::tuple< Ts... > var_
void play_complex(Ts... x) override
ResumeComponentAction(PollingComponent *component)
TEMPLATABLE_VALUE(uint32_t, update_interval) void play(Ts... x) override
ShutdownTrigger(float setup_priority)
float get_setup_priority() const override
float get_setup_priority() const override
StartupTrigger(float setup_priority)
SuspendComponentAction(PollingComponent *component)
void trigger(Ts... x)
Definition automation.h:145
void play(Ts... x) override
UpdateComponentAction(PollingComponent *component)
WaitUntilAction(Condition< Ts... > *condition)
void play(Ts... x) override
Condition< Ts... > * condition_
TEMPLATABLE_VALUE(uint32_t, timeout_value) void play_complex(Ts... x) override
float get_setup_priority() const override
WhileAction(Condition< Ts... > *condition)
void play(Ts... x) override
void play_complex(Ts... x) override
void add_then(const std::vector< Action< Ts... > * > &actions)
Condition< Ts... > * condition_
ActionList< Ts... > then_
bool check(Ts... x) override
std::vector< Condition< Ts... > * > conditions_
XorCondition(const std::vector< Condition< Ts... > * > &conditions)
bool has_value() const
Definition optional.h:92
value_type const & value() const
Definition optional.h:94
void loop()
const float DATA
For components that import data from directly connected sensors like DHT.
Definition component.cpp:49
const float HARDWARE
For components that deal with hardware and are very important like GPIO switch.
Definition component.cpp:48
const float PROCESSOR
For components that use data from sensors like displays.
Definition component.cpp:50
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
ESPPreferences * global_preferences
uint32_t fnv1_hash(const char *str)
Calculate a FNV-1 hash of str.
Definition helpers.cpp:145
void IRAM_ATTR HOT delay(uint32_t ms)
Definition core.cpp:29
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28
Application App
Global storage of Application pointer - only one Application can exist.
uint16_t x
Definition tt21100.cpp:5