ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
current_based_cover.cpp
Go to the documentation of this file.
2#include "esphome/core/hal.h"
3#include "esphome/core/log.h"
5#include <cfloat>
6
8
9ESPHOME_LOG_TAG(TAG, "current_based.cover");
10
11static constexpr uint32_t ROLLBACK_TIMEOUT_ID = 0;
12
13using namespace esphome::cover;
14
16 auto traits = CoverTraits();
17 traits.set_supports_stop(true);
18 traits.set_supports_position(true);
19 traits.set_supports_toggle(true);
20 traits.set_is_assumed_state(false);
21 return traits;
22}
24 if (call.get_stop()) {
25 this->direction_idle_();
26 }
27 if (call.get_toggle().has_value()) {
30 this->publish_state();
31 } else {
32 if (this->position == COVER_CLOSED || this->last_operation_ == COVER_OPERATION_CLOSING) {
33 this->target_position_ = COVER_OPEN;
35 } else {
36 this->target_position_ = COVER_CLOSED;
38 }
39 }
40 }
41 auto opt_pos = call.get_position();
42 if (opt_pos.has_value()) {
43 auto pos = *opt_pos;
44 if (fabsf(this->position - pos) < 0.01f) {
45 // already at target
46 } else {
48 this->target_position_ = pos;
49 this->start_direction_(op);
50 }
51 }
52}
54 auto restore = this->restore_state_();
55 if (restore.has_value()) {
56 restore->apply(this);
57 } else {
58 this->position = 0.5f;
59 }
60}
61
64 return;
65
67
69 if (this->malfunction_detection_ && this->is_closing_()) { // Malfunction
70 this->direction_idle_();
72 ESP_LOGI(TAG, "'%s' - Malfunction detected during opening. Current flow detected in close circuit",
73 LOG_STR_ARG(this->get_log_name()));
74 } else if (this->is_opening_blocked_()) { // Blocked
75 ESP_LOGD(TAG, "'%s' - Obstacle detected during opening.", LOG_STR_ARG(this->get_log_name()));
76 this->direction_idle_();
77 if (this->obstacle_rollback_ != 0) {
78 this->set_timeout(ROLLBACK_TIMEOUT_ID, 300, [this]() {
79 ESP_LOGD(TAG, "'%s' - Rollback.", LOG_STR_ARG(this->get_log_name()));
80 this->target_position_ = clamp(this->position - this->obstacle_rollback_, 0.0F, 1.0F);
82 });
83 }
84 } else if (this->is_initial_delay_finished_() && !this->is_opening_()) { // End reached
85 auto dur = (now - this->start_dir_time_) / 1e3f;
86 ESP_LOGD(TAG, "'%s' - Open position reached. Took %.1fs.", LOG_STR_ARG(this->get_log_name()), dur);
87 this->direction_idle_(COVER_OPEN);
88 }
89 } else if (this->current_operation == COVER_OPERATION_CLOSING) {
90 if (this->malfunction_detection_ && this->is_opening_()) { // Malfunction
91 this->direction_idle_();
93 ESP_LOGI(TAG, "'%s' - Malfunction detected during closing. Current flow detected in open circuit",
94 LOG_STR_ARG(this->get_log_name()));
95 } else if (this->is_closing_blocked_()) { // Blocked
96 ESP_LOGD(TAG, "'%s' - Obstacle detected during closing.", LOG_STR_ARG(this->get_log_name()));
97 this->direction_idle_();
98 if (this->obstacle_rollback_ != 0) {
99 this->set_timeout(ROLLBACK_TIMEOUT_ID, 300, [this]() {
100 ESP_LOGD(TAG, "'%s' - Rollback.", LOG_STR_ARG(this->get_log_name()));
101 this->target_position_ = clamp(this->position + this->obstacle_rollback_, 0.0F, 1.0F);
103 });
104 }
105 } else if (this->is_initial_delay_finished_() && !this->is_closing_()) { // End reached
106 auto dur = (now - this->start_dir_time_) / 1e3f;
107 ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", LOG_STR_ARG(this->get_log_name()), dur);
108 this->direction_idle_(COVER_CLOSED);
109 }
110 }
111 if (now - this->start_dir_time_ > this->max_duration_) {
112 ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", LOG_STR_ARG(this->get_log_name()));
113 this->direction_idle_();
114 }
115
116 // Recompute position every loop cycle
117 this->recompute_position_();
118
119 if (this->current_operation != COVER_OPERATION_IDLE && this->is_at_target_()) {
120 this->direction_idle_();
121 }
122
123 // Send current position every second
124 if (this->current_operation != COVER_OPERATION_IDLE && now - this->last_publish_time_ > 1000) {
125 this->publish_state(false);
126 this->last_publish_time_ = now;
127 }
128}
129
130void CurrentBasedCover::direction_idle_(float new_position) {
132 if (new_position != FLT_MAX) {
133 this->position = new_position;
134 }
135 this->publish_state();
136}
137
139 LOG_COVER("", "Endstop Cover", this);
140 LOG_SENSOR(" ", "Open Sensor", this->open_sensor_);
141 ESP_LOGCONFIG(TAG, " Open moving current threshold: %.11fA", this->open_moving_current_threshold_);
142 if (this->open_obstacle_current_threshold_ != FLT_MAX) {
143 ESP_LOGCONFIG(TAG, " Open obstacle current threshold: %.11fA", this->open_obstacle_current_threshold_);
144 }
145 ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f);
146 LOG_SENSOR(" ", "Close Sensor", this->close_sensor_);
147 ESP_LOGCONFIG(TAG, " Close moving current threshold: %.11fA", this->close_moving_current_threshold_);
148 if (this->close_obstacle_current_threshold_ != FLT_MAX) {
149 ESP_LOGCONFIG(TAG, " Close obstacle current threshold: %.11fA", this->close_obstacle_current_threshold_);
150 }
151 ESP_LOGCONFIG(TAG,
152 " Close Duration: %.1fs\n"
153 " Obstacle Rollback: %.1f%%",
154 this->close_duration_ / 1e3f, this->obstacle_rollback_ * 100);
155 if (this->max_duration_ != UINT32_MAX) {
156 ESP_LOGCONFIG(TAG, " Maximum duration: %.1fs", this->max_duration_ / 1e3f);
157 }
158 ESP_LOGCONFIG(TAG,
159 " Start sensing delay: %.1fs\n"
160 " Malfunction detection: %s",
161 this->start_sensing_delay_ / 1e3f, YESNO(this->malfunction_detection_));
162}
163
165 if (this->prev_command_trigger_ != nullptr) {
167 this->prev_command_trigger_ = nullptr;
168 }
169}
170
174
176 if (this->open_obstacle_current_threshold_ == FLT_MAX) {
177 return false;
178 }
180}
181
185
187 if (this->close_obstacle_current_threshold_ == FLT_MAX) {
188 return false;
189 }
191}
195
197 switch (this->current_operation) {
199 if (this->target_position_ == COVER_OPEN) {
200 if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed
201 return false;
202 return !this->is_opening_();
203 }
204 return this->position >= this->target_position_;
206 if (this->target_position_ == COVER_CLOSED) {
207 if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed
208 return false;
209 return !this->is_closing_();
210 }
211 return this->position <= this->target_position_;
213 default:
214 return true;
215 }
216}
218 if (dir == this->current_operation)
219 return;
220
221 this->recompute_position_();
222 Trigger<> *trig;
223 switch (dir) {
225 trig = &this->stop_trigger_;
226 break;
228 this->last_operation_ = dir;
229 trig = &this->open_trigger_;
230 break;
232 this->last_operation_ = dir;
233 trig = &this->close_trigger_;
234 break;
235 default:
236 return;
237 }
238
239 this->current_operation = dir;
240
241 this->stop_prev_trigger_();
242 trig->trigger();
243 this->prev_command_trigger_ = trig;
244
245 const auto now = millis();
246 this->start_dir_time_ = now;
247 this->last_recompute_time_ = now;
248}
251 return;
252
253 float dir;
254 float action_dur;
255 switch (this->current_operation) {
257 dir = 1.0F;
258 action_dur = this->open_duration_;
259 break;
261 dir = -1.0F;
262 action_dur = this->close_duration_;
263 break;
264 default:
265 return;
266 }
267
268 const auto now = millis();
269 this->position += dir * (now - this->last_recompute_time_) / action_dur;
270 this->position = clamp(this->position, 0.0F, 1.0F);
271
272 this->last_recompute_time_ = now;
273}
274
275} // namespace esphome::current_based
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.
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
const LogString * get_log_name() const
Get the name for a "%s" log argument (LOG_STR_ARG).
void stop_action()
Stop any action connected to this trigger.
Definition automation.h:564
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
Inform the parent automation that the event has triggered.
Definition automation.h:556
const optional< bool > & get_toggle() const
Definition cover.cpp:101
CoverOperation current_operation
The current operation of the cover (idle, opening, closing).
Definition cover.h:120
optional< CoverRestoreState > restore_state_()
Definition cover.cpp:195
void publish_state(bool save=true)
Publish the current state of the cover.
Definition cover.cpp:158
float position
The position of the cover from 0.0 (fully closed) to 1.0 (fully open).
Definition cover.h:126
void start_direction_(cover::CoverOperation dir)
void control(const cover::CoverCall &call) override
void direction_idle_(float new_position=FLT_MAX)
float get_state() const
Getter-syntax for .state.
Definition sensor.h:98
ESPHOME_LOG_TAG(TAG, "cover")
CoverOperation
Enum encoding the current operation of a cover.
Definition cover.h:84
@ COVER_OPERATION_OPENING
The cover is currently opening.
Definition cover.h:88
@ COVER_OPERATION_CLOSING
The cover is currently closing.
Definition cover.h:90
@ COVER_OPERATION_IDLE
The cover is currently idle (not moving)
Definition cover.h:86
size_t size_t pos
Definition helpers.h:1255
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t