ESPHome 2026.10.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
11static const char *const 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_.has_value()) {
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 // at_index_ has a value here (the !has_value() branch above returns).
59 size_t at_index = *this->at_index_;
60 if (at_index == this->timing_count_) {
61 this->trigger_();
62 return;
63 }
64
65 MultiClickTriggerEvent evt = this->timing_[at_index];
66
67 if (evt.max_length != 4294967294UL) {
68 ESP_LOGV(TAG, "A i=%zu min=%" PRIu32 " max=%" PRIu32, at_index, evt.min_length, evt.max_length); // NOLINT
71 } else if (at_index + 1 != this->timing_count_) {
72 ESP_LOGV(TAG, "B i=%zu min=%" PRIu32, at_index, evt.min_length); // NOLINT
73 this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
75 } else {
76 ESP_LOGV(TAG, "C i=%zu min=%" PRIu32, at_index, evt.min_length); // NOLINT
77 this->is_valid_ = false;
78 this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
79 this->set_timeout(MULTICLICK_TRIGGER_ID, evt.min_length, [this]() { this->trigger_(); });
80 }
81
82 this->at_index_ = at_index + 1;
83}
85 ESP_LOGV(TAG, "Multi Click: Invalid length of press, starting cooldown of %" PRIu32 " ms", this->invalid_cooldown_);
86 this->is_in_cooldown_ = true;
87 this->set_timeout(MULTICLICK_COOLDOWN_ID, this->invalid_cooldown_, [this]() {
88 ESP_LOGV(TAG, "Multi Click: Cooldown ended, matching is now enabled again.");
89 this->is_in_cooldown_ = false;
90 });
91 this->at_index_.reset();
92 this->cancel_timeout(MULTICLICK_TRIGGER_ID);
93 this->cancel_timeout(MULTICLICK_IS_VALID_ID);
94 this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
95}
97 if (min_length == 0) {
98 this->is_valid_ = true;
99 return;
100 }
101 this->is_valid_ = false;
102 this->set_timeout(MULTICLICK_IS_VALID_ID, min_length, [this]() {
103 ESP_LOGV(TAG, "Multi Click: You can now %s the button.",
104 this->parent_->state ? LOG_STR_LITERAL("RELEASE") : LOG_STR_LITERAL("PRESS"));
105 this->is_valid_ = true;
106 });
107}
109 this->set_timeout(MULTICLICK_IS_NOT_VALID_ID, max_length, [this]() {
110 ESP_LOGV(TAG, "Multi Click: You waited too long to %s.",
111 this->parent_->state ? LOG_STR_LITERAL("RELEASE") : LOG_STR_LITERAL("PRESS"));
112 this->is_valid_ = false;
113 this->schedule_cooldown_();
114 });
115}
117 ESP_LOGV(TAG, "Multi Click: Sequence explicitly cancelled.");
118 this->is_valid_ = false;
119 this->schedule_cooldown_();
120}
122 ESP_LOGV(TAG, "Multi Click: Hooray, multi click is valid. Triggering!");
123 this->at_index_.reset();
124 this->cancel_timeout(MULTICLICK_TRIGGER_ID);
125 this->cancel_timeout(MULTICLICK_IS_VALID_ID);
126 this->cancel_timeout(MULTICLICK_IS_NOT_VALID_ID);
127 this->trigger();
128}
129
130#endif // USE_BINARY_SENSOR_MULTI_CLICK_TRIGGER
131
132#ifdef USE_BINARY_SENSOR_CLICK_TRIGGER
133bool match_interval(uint32_t min_length, uint32_t max_length, uint32_t length) {
134 if (max_length == 0) {
135 return length >= min_length;
136 } else {
137 return length >= min_length && length <= max_length;
138 }
139}
140#endif // USE_BINARY_SENSOR_CLICK_TRIGGER
141
142} // namespace esphome::binary_sensor
143
144#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: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