ESPHome 2026.6.0-dev
Loading...
Searching...
No Matches
automation.cpp
Go to the documentation of this file.
1#include "automation.h"
2#include "esphome/core/log.h"
3
5
6static const char *const TAG = "binary_sensor.automation";
7
8// MultiClickTrigger timeout IDs.
9// MultiClickTrigger is its own Component instance, so the scheduler scopes
10// IDs by component pointer — no risk of collisions between instances.
15
17 // Handle duplicate events
18 if (state == this->last_state_) {
19 return;
20 }
21 this->last_state_ = state;
22
23 // Cooldown: Do not immediately try matching after having invalid timing
24 if (this->is_in_cooldown_) {
25 return;
26 }
27
28 if (!this->at_index_.has_value()) {
29 // Start matching
30 MultiClickTriggerEvent evt = this->timing_[0];
31 if (evt.state == state) {
32 ESP_LOGV(TAG, "START min=%" PRIu32 " max=%" PRIu32, evt.min_length, evt.max_length);
33 ESP_LOGV(TAG, "Multi Click: Starting multi click action!");
34 this->at_index_ = 1;
35 if (this->timing_count_ == 1 && evt.max_length == 4294967294UL) {
36 this->set_timeout(MULTICLICK_TRIGGER_ID, evt.min_length, [this]() { this->trigger_(); });
37 } else {
40 }
41 } else {
42 ESP_LOGV(TAG, "Multi Click: action not started because first level does not match!");
43 }
44
45 return;
46 }
47
48 if (!this->is_valid_) {
49 this->schedule_cooldown_();
50 return;
51 }
52
53 // at_index_ has a value here (the !has_value() branch above returns).
54 size_t at_index = *this->at_index_;
55 if (at_index == this->timing_count_) {
56 this->trigger_();
57 return;
58 }
59
60 MultiClickTriggerEvent evt = this->timing_[at_index];
61
62 if (evt.max_length != 4294967294UL) {
63 ESP_LOGV(TAG, "A i=%zu min=%" PRIu32 " max=%" PRIu32, at_index, evt.min_length, evt.max_length); // NOLINT
66 } else if (at_index + 1 != this->timing_count_) {
67 ESP_LOGV(TAG, "B i=%zu min=%" PRIu32, at_index, evt.min_length); // NOLINT
68 this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
70 } else {
71 ESP_LOGV(TAG, "C i=%zu min=%" PRIu32, at_index, evt.min_length); // NOLINT
72 this->is_valid_ = false;
73 this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
74 this->set_timeout(MULTICLICK_TRIGGER_ID, evt.min_length, [this]() { this->trigger_(); });
75 }
76
77 this->at_index_ = at_index + 1;
78}
80 ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms", this->invalid_cooldown_);
81 this->is_in_cooldown_ = true;
82 this->set_timeout(MULTICLICK_COOLDOWN_ID, this->invalid_cooldown_, [this]() {
83 ESP_LOGV(TAG, "Multi Click: Cooldown ended, matching is now enabled again.");
84 this->is_in_cooldown_ = false;
85 });
86 this->at_index_.reset();
87 this->cancel_timeout(MULTICLICK_TRIGGER_ID);
88 this->cancel_timeout(MULTICLICK_IS_VALID_ID);
89 this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
90}
92 if (min_length == 0) {
93 this->is_valid_ = true;
94 return;
95 }
96 this->is_valid_ = false;
97 this->set_timeout(MULTICLICK_IS_VALID_ID, min_length, [this]() {
98 ESP_LOGV(TAG, "Multi Click: You can now %s the button.", this->parent_->state ? "RELEASE" : "PRESS");
99 this->is_valid_ = true;
100 });
101}
103 this->set_timeout(MULTICLICK_IS_NOT_VALID_ID, max_length, [this]() {
104 ESP_LOGV(TAG, "Multi Click: You waited too long to %s.", this->parent_->state ? "RELEASE" : "PRESS");
105 this->is_valid_ = false;
106 this->schedule_cooldown_();
107 });
108}
110 ESP_LOGV(TAG, "Multi Click: Sequence explicitly cancelled.");
111 this->is_valid_ = false;
112 this->schedule_cooldown_();
113}
115 ESP_LOGV(TAG, "Multi Click: Hooray, multi click is valid. Triggering!");
116 this->at_index_.reset();
117 this->cancel_timeout(MULTICLICK_TRIGGER_ID);
118 this->cancel_timeout(MULTICLICK_IS_VALID_ID);
119 this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
120 this->trigger();
121}
122
123bool match_interval(uint32_t min_length, uint32_t max_length, uint32_t length) {
124 if (max_length == 0) {
125 return length >= min_length;
126 } else {
127 return length >= min_length && length <= max_length;
128 }
129}
130} // namespace esphome::binary_sensor
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") void set_timeout(const std voi set_timeout)(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition component.h:493
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") bool cancel_timeout(const std boo cancel_timeout)(const char *name)
Cancel a timeout function.
Definition component.h:515
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Definition automation.h:461
bool state
The current state of this binary sensor. Also used as the backing storage for StatefulEntityBase.
void schedule_is_not_valid_(uint32_t max_length)
const MultiClickTriggerEvent * timing_
Definition automation.h:119
void schedule_is_valid_(uint32_t min_length)
bool state
Definition fan.h:2
constexpr uint32_t MULTICLICK_COOLDOWN_ID
constexpr uint32_t MULTICLICK_IS_NOT_VALID_ID
constexpr uint32_t MULTICLICK_IS_VALID_ID
bool match_interval(uint32_t min_length, uint32_t max_length, uint32_t length)
constexpr uint32_t MULTICLICK_TRIGGER_ID
static void uint32_t
uint16_t length
Definition tt21100.cpp:0