ESPHome 2026.3.0
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
7namespace esphome {
8namespace current_based {
9
10static const char *const TAG = "current_based.cover";
11
12using namespace esphome::cover;
13
15 auto traits = CoverTraits();
16 traits.set_supports_stop(true);
17 traits.set_supports_position(true);
18 traits.set_supports_toggle(true);
19 traits.set_is_assumed_state(false);
20 return traits;
21}
23 if (call.get_stop()) {
24 this->direction_idle_();
25 }
26 if (call.get_toggle().has_value()) {
29 this->publish_state();
30 } else {
31 if (this->position == COVER_CLOSED || this->last_operation_ == COVER_OPERATION_CLOSING) {
32 this->target_position_ = COVER_OPEN;
34 } else {
35 this->target_position_ = COVER_CLOSED;
37 }
38 }
39 }
40 auto opt_pos = call.get_position();
41 if (opt_pos.has_value()) {
42 auto pos = *opt_pos;
43 if (fabsf(this->position - pos) < 0.01) {
44 // already at target
45 } else {
47 this->target_position_ = pos;
48 this->start_direction_(op);
49 }
50 }
51}
53 auto restore = this->restore_state_();
54 if (restore.has_value()) {
55 restore->apply(this);
56 } else {
57 this->position = 0.5f;
58 }
59}
60
63 return;
64
66
68 if (this->malfunction_detection_ && this->is_closing_()) { // Malfunction
69 this->direction_idle_();
71 ESP_LOGI(TAG, "'%s' - Malfunction detected during opening. Current flow detected in close circuit",
72 this->name_.c_str());
73 } else if (this->is_opening_blocked_()) { // Blocked
74 ESP_LOGD(TAG, "'%s' - Obstacle detected during opening.", this->name_.c_str());
75 this->direction_idle_();
76 if (this->obstacle_rollback_ != 0) {
77 this->set_timeout("rollback", 300, [this]() {
78 ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str());
79 this->target_position_ = clamp(this->position - this->obstacle_rollback_, 0.0F, 1.0F);
81 });
82 }
83 } else if (this->is_initial_delay_finished_() && !this->is_opening_()) { // End reached
84 auto dur = (now - this->start_dir_time_) / 1e3f;
85 ESP_LOGD(TAG, "'%s' - Open position reached. Took %.1fs.", this->name_.c_str(), dur);
86 this->direction_idle_(COVER_OPEN);
87 }
88 } else if (this->current_operation == COVER_OPERATION_CLOSING) {
89 if (this->malfunction_detection_ && this->is_opening_()) { // Malfunction
90 this->direction_idle_();
92 ESP_LOGI(TAG, "'%s' - Malfunction detected during closing. Current flow detected in open circuit",
93 this->name_.c_str());
94 } else if (this->is_closing_blocked_()) { // Blocked
95 ESP_LOGD(TAG, "'%s' - Obstacle detected during closing.", this->name_.c_str());
96 this->direction_idle_();
97 if (this->obstacle_rollback_ != 0) {
98 this->set_timeout("rollback", 300, [this]() {
99 ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str());
100 this->target_position_ = clamp(this->position + this->obstacle_rollback_, 0.0F, 1.0F);
102 });
103 }
104 } else if (this->is_initial_delay_finished_() && !this->is_closing_()) { // End reached
105 auto dur = (now - this->start_dir_time_) / 1e3f;
106 ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", this->name_.c_str(), dur);
107 this->direction_idle_(COVER_CLOSED);
108 }
109 }
110 if (now - this->start_dir_time_ > this->max_duration_) {
111 ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str());
112 this->direction_idle_();
113 }
114
115 // Recompute position every loop cycle
116 this->recompute_position_();
117
118 if (this->current_operation != COVER_OPERATION_IDLE && this->is_at_target_()) {
119 this->direction_idle_();
120 }
121
122 // Send current position every second
123 if (this->current_operation != COVER_OPERATION_IDLE && now - this->last_publish_time_ > 1000) {
124 this->publish_state(false);
125 this->last_publish_time_ = now;
126 }
127}
128
129void CurrentBasedCover::direction_idle_(float new_position) {
131 if (new_position != FLT_MAX) {
132 this->position = new_position;
133 }
134 this->publish_state();
135}
136
138 LOG_COVER("", "Endstop Cover", this);
139 LOG_SENSOR(" ", "Open Sensor", this->open_sensor_);
140 ESP_LOGCONFIG(TAG, " Open moving current threshold: %.11fA", this->open_moving_current_threshold_);
141 if (this->open_obstacle_current_threshold_ != FLT_MAX) {
142 ESP_LOGCONFIG(TAG, " Open obstacle current threshold: %.11fA", this->open_obstacle_current_threshold_);
143 }
144 ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f);
145 LOG_SENSOR(" ", "Close Sensor", this->close_sensor_);
146 ESP_LOGCONFIG(TAG, " Close moving current threshold: %.11fA", this->close_moving_current_threshold_);
147 if (this->close_obstacle_current_threshold_ != FLT_MAX) {
148 ESP_LOGCONFIG(TAG, " Close obstacle current threshold: %.11fA", this->close_obstacle_current_threshold_);
149 }
150 ESP_LOGCONFIG(TAG,
151 " Close Duration: %.1fs\n"
152 " Obstacle Rollback: %.1f%%",
153 this->close_duration_ / 1e3f, this->obstacle_rollback_ * 100);
154 if (this->max_duration_ != UINT32_MAX) {
155 ESP_LOGCONFIG(TAG, " Maximum duration: %.1fs", this->max_duration_ / 1e3f);
156 }
157 ESP_LOGCONFIG(TAG,
158 " Start sensing delay: %.1fs\n"
159 " Malfunction detection: %s",
160 this->start_sensing_delay_ / 1e3f, YESNO(this->malfunction_detection_));
161}
162
164 if (this->prev_command_trigger_ != nullptr) {
166 this->prev_command_trigger_ = nullptr;
167 }
168}
169
173
175 if (this->open_obstacle_current_threshold_ == FLT_MAX) {
176 return false;
177 }
179}
180
184
186 if (this->close_obstacle_current_threshold_ == FLT_MAX) {
187 return false;
188 }
190}
194
196 switch (this->current_operation) {
198 if (this->target_position_ == COVER_OPEN) {
199 if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed
200 return false;
201 return !this->is_opening_();
202 }
203 return this->position >= this->target_position_;
205 if (this->target_position_ == COVER_CLOSED) {
206 if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed
207 return false;
208 return !this->is_closing_();
209 }
210 return this->position <= this->target_position_;
212 default:
213 return true;
214 }
215}
217 if (dir == this->current_operation)
218 return;
219
220 this->recompute_position_();
221 Trigger<> *trig;
222 switch (dir) {
224 trig = &this->stop_trigger_;
225 break;
227 this->last_operation_ = dir;
228 trig = &this->open_trigger_;
229 break;
231 this->last_operation_ = dir;
232 trig = &this->close_trigger_;
233 break;
234 default:
235 return;
236 }
237
238 this->current_operation = dir;
239
240 this->stop_prev_trigger_();
241 trig->trigger();
242 this->prev_command_trigger_ = trig;
243
244 const auto now = millis();
245 this->start_dir_time_ = now;
246 this->last_recompute_time_ = now;
247}
250 return;
251
252 float dir;
253 float action_dur;
254 switch (this->current_operation) {
256 dir = 1.0F;
257 action_dur = this->open_duration_;
258 break;
260 dir = -1.0F;
261 action_dur = this->close_duration_;
262 break;
263 default:
264 return;
265 }
266
267 const auto now = millis();
268 this->position += dir * (now - this->last_recompute_time_) / action_dur;
269 this->position = clamp(this->position, 0.0F, 1.0F);
270
271 this->last_recompute_time_ = now;
272}
273
274} // namespace current_based
275} // namespace esphome
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.
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:451
constexpr const char * c_str() const
Definition string_ref.h:73
void trigger(const Ts &...x)
Inform the parent automation that the event has triggered.
Definition automation.h:325
void stop_action()
Stop any action connected to this trigger.
Definition automation.h:333
const optional< bool > & get_toggle() const
Definition cover.cpp:94
CoverOperation current_operation
The current operation of the cover (idle, opening, closing).
Definition cover.h:115
optional< CoverRestoreState > restore_state_()
Definition cover.cpp:180
void publish_state(bool save=true)
Publish the current state of the cover.
Definition cover.cpp:143
float position
The position of the cover from 0.0 (fully closed) to 1.0 (fully open).
Definition cover.h:121
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.cpp:118
CoverOperation
Enum encoding the current operation of a cover.
Definition cover.h:79
@ COVER_OPERATION_OPENING
The cover is currently opening.
Definition cover.h:83
@ COVER_OPERATION_CLOSING
The cover is currently closing.
Definition cover.h:85
@ COVER_OPERATION_IDLE
The cover is currently idle (not moving)
Definition cover.h:81
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
size_t size_t pos
Definition helpers.h:929
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:26
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t