ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
automation.cpp
Go to the documentation of this file.
2#if defined(USE_BINARY_SENSOR_CLICK_TRIGGER) || defined(USE_BINARY_SENSOR_MULTI_CLICK_TRIGGER)
3
4#include "automation.h"
5#include "esphome/core/log.h"
6
8
9#ifdef USE_BINARY_SENSOR_MULTI_CLICK_TRIGGER
10
11ESPHOME_LOG_TAG(TAG, "binary_sensor.automation");
12
13// MultiClickTrigger timeout IDs.
14// MultiClickTrigger is its own Component instance, so the scheduler scopes
15// IDs by component pointer — no risk of collisions between instances.
20
22 // Handle duplicate events
23 if (state == this->last_state_) {
24 return;
25 }
26 this->last_state_ = state;
27
28 // Cooldown: Do not immediately try matching after having invalid timing
29 if (this->is_in_cooldown_) {
30 return;
31 }
32
33 if (this->at_index_ == 0) {
34 // Start matching
35 MultiClickTriggerEvent evt = this->timing_[0];
36 if (evt.state == state) {
37 ESP_LOGV(TAG, "START min=%" PRIu32 " max=%" PRIu32, evt.min_length, evt.max_length);
38 ESP_LOGV(TAG, "Multi Click: Starting multi click action!");
39 this->at_index_ = 1;
40 if (this->timing_count_ == 1 && evt.max_length == 4294967294UL) {
41 this->set_timeout(MULTICLICK_TRIGGER_ID, evt.min_length, [this]() { this->trigger_(); });
42 } else {
45 }
46 } else {
47 ESP_LOGV(TAG, "Multi Click: action not started because first level does not match!");
48 }
49
50 return;
51 }
52
53 if (!this->is_valid_) {
54 this->schedule_cooldown_();
55 return;
56 }
57
58 uint8_t at_index = this->at_index_;
59 if (at_index == this->timing_count_) {
60 this->trigger_();
61 return;
62 }
63
64 MultiClickTriggerEvent evt = this->timing_[at_index];
65
66 if (evt.max_length != 4294967294UL) {
67 ESP_LOGV(TAG, "A i=%u min=%" PRIu32 " max=%" PRIu32, at_index, evt.min_length, evt.max_length);
70 } else if (at_index + 1 != this->timing_count_) {
71 ESP_LOGV(TAG, "B i=%u min=%" PRIu32, at_index, evt.min_length);
72 this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
74 } else {
75 ESP_LOGV(TAG, "C i=%u min=%" PRIu32, at_index, evt.min_length);
76 this->is_valid_ = false;
77 this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
78 this->set_timeout(MULTICLICK_TRIGGER_ID, evt.min_length, [this]() { this->trigger_(); });
79 }
80
81 this->at_index_ = at_index + 1;
82}
84 ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms", this->invalid_cooldown_);
85 this->is_in_cooldown_ = true;
86 this->set_timeout(MULTICLICK_COOLDOWN_ID, this->invalid_cooldown_, [this]() {
87 ESP_LOGV(TAG, "Multi Click: Cooldown ended, matching is now enabled again.");
88 this->is_in_cooldown_ = false;
89 });
90 this->at_index_ = 0;
91 this->cancel_timeout(MULTICLICK_TRIGGER_ID);
92 this->cancel_timeout(MULTICLICK_IS_VALID_ID);
93 this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
94}
96 if (min_length == 0) {
97 this->is_valid_ = true;
98 return;
99 }
100 this->is_valid_ = false;
101 this->set_timeout(MULTICLICK_IS_VALID_ID, min_length, [this]() {
102 ESP_LOGV(TAG, "Multi Click: You can now %s the button.",
103 this->parent_->state ? LOG_STR_LITERAL("RELEASE") : LOG_STR_LITERAL("PRESS"));
104 this->is_valid_ = true;
105 });
106}
108 this->set_timeout(MULTICLICK_IS_NOT_VALID_ID, max_length, [this]() {
109 ESP_LOGV(TAG, "Multi Click: You waited too long to %s.",
110 this->parent_->state ? LOG_STR_LITERAL("RELEASE") : LOG_STR_LITERAL("PRESS"));
111 this->is_valid_ = false;
112 this->schedule_cooldown_();
113 });
114}
116 ESP_LOGV(TAG, "Multi Click: Sequence explicitly cancelled.");
117 this->is_valid_ = false;
118 this->schedule_cooldown_();
119}
121 ESP_LOGV(TAG, "Multi Click: Hooray, multi click is valid. Triggering!");
122 this->at_index_ = 0;
123 this->cancel_timeout(MULTICLICK_TRIGGER_ID);
124 this->cancel_timeout(MULTICLICK_IS_VALID_ID);
125 this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
126 this->trigger();
127}
128
129#endif // USE_BINARY_SENSOR_MULTI_CLICK_TRIGGER
130
131#ifdef USE_BINARY_SENSOR_CLICK_TRIGGER
132bool match_interval(uint32_t min_length, uint32_t max_length, uint32_t length) {
133 if (max_length == 0) {
134 return length >= min_length;
135 } else {
136 return length >= min_length && length <= max_length;
137 }
138}
139#endif // USE_BINARY_SENSOR_CLICK_TRIGGER
140
141} // namespace esphome::binary_sensor
142
143#endif // USE_BINARY_SENSOR_CLICK_TRIGGER || USE_BINARY_SENSOR_MULTI_CLICK_TRIGGER
bool cancel_timeout(const char *name)
Cancel a timeout function.
void set_timeout(const char *name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a const char* name.
Definition component.cpp:96
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Definition automation.h:556
bool state
The current state of this binary sensor. Also used as the backing storage for StatefulEntityBase.
const MultiClickTriggerEvent * timing_
Definition automation.h:104
void schedule_is_not_valid_(uint32_t max_length)
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)
ESPHOME_LOG_TAG(TAG, "binary_sensor.automation")
constexpr uint32_t MULTICLICK_TRIGGER_ID
static void uint32_t
uint16_t length
Definition tt21100.cpp:0