ESPHome 2025.6.3
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 {
34 } else {
37 }
38 }
39 }
40 if (call.get_position().has_value()) {
41 auto pos = *call.get_position();
42 if (fabsf(this->position - pos) < 0.01) {
43 // already at target
44 } else {
46 this->target_position_ = pos;
47 this->start_direction_(op);
48 }
49 }
50}
52 auto restore = this->restore_state_();
53 if (restore.has_value()) {
54 restore->apply(this);
55 } else {
56 this->position = 0.5f;
57 }
58}
59
62 return;
63
64 const uint32_t now = App.get_loop_component_start_time();
65
67 if (this->malfunction_detection_ && this->is_closing_()) { // Malfunction
68 this->direction_idle_();
70 ESP_LOGI(TAG, "'%s' - Malfunction detected during opening. Current flow detected in close circuit",
71 this->name_.c_str());
72 } else if (this->is_opening_blocked_()) { // Blocked
73 ESP_LOGD(TAG, "'%s' - Obstacle detected during opening.", this->name_.c_str());
74 this->direction_idle_();
75 if (this->obstacle_rollback_ != 0) {
76 this->set_timeout("rollback", 300, [this]() {
77 ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str());
78 this->target_position_ = clamp(this->position - this->obstacle_rollback_, 0.0F, 1.0F);
80 });
81 }
82 } else if (this->is_initial_delay_finished_() && !this->is_opening_()) { // End reached
83 auto dur = (now - this->start_dir_time_) / 1e3f;
84 ESP_LOGD(TAG, "'%s' - Open position reached. Took %.1fs.", this->name_.c_str(), dur);
86 }
87 } else if (this->current_operation == COVER_OPERATION_CLOSING) {
88 if (this->malfunction_detection_ && this->is_opening_()) { // Malfunction
89 this->direction_idle_();
91 ESP_LOGI(TAG, "'%s' - Malfunction detected during closing. Current flow detected in open circuit",
92 this->name_.c_str());
93 } else if (this->is_closing_blocked_()) { // Blocked
94 ESP_LOGD(TAG, "'%s' - Obstacle detected during closing.", this->name_.c_str());
95 this->direction_idle_();
96 if (this->obstacle_rollback_ != 0) {
97 this->set_timeout("rollback", 300, [this]() {
98 ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str());
99 this->target_position_ = clamp(this->position + this->obstacle_rollback_, 0.0F, 1.0F);
101 });
102 }
103 } else if (this->is_initial_delay_finished_() && !this->is_closing_()) { // End reached
104 auto dur = (now - this->start_dir_time_) / 1e3f;
105 ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", this->name_.c_str(), dur);
107 }
108 }
109 if (now - this->start_dir_time_ > this->max_duration_) {
110 ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str());
111 this->direction_idle_();
112 }
113
114 // Recompute position every loop cycle
115 this->recompute_position_();
116
117 if (this->current_operation != COVER_OPERATION_IDLE && this->is_at_target_()) {
118 this->direction_idle_();
119 }
120
121 // Send current position every second
122 if (this->current_operation != COVER_OPERATION_IDLE && now - this->last_publish_time_ > 1000) {
123 this->publish_state(false);
124 this->last_publish_time_ = now;
125 }
126}
127
128void CurrentBasedCover::direction_idle_(float new_position) {
130 if (new_position != FLT_MAX) {
131 this->position = new_position;
132 }
133 this->publish_state();
134}
135
137 LOG_COVER("", "Endstop Cover", this);
138 LOG_SENSOR(" ", "Open Sensor", this->open_sensor_);
139 ESP_LOGCONFIG(TAG, " Open moving current threshold: %.11fA", this->open_moving_current_threshold_);
140 if (this->open_obstacle_current_threshold_ != FLT_MAX) {
141 ESP_LOGCONFIG(TAG, " Open obstacle current threshold: %.11fA", this->open_obstacle_current_threshold_);
142 }
143 ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f);
144 LOG_SENSOR(" ", "Close Sensor", this->close_sensor_);
145 ESP_LOGCONFIG(TAG, " Close moving current threshold: %.11fA", this->close_moving_current_threshold_);
146 if (this->close_obstacle_current_threshold_ != FLT_MAX) {
147 ESP_LOGCONFIG(TAG, " Close obstacle current threshold: %.11fA", this->close_obstacle_current_threshold_);
148 }
149 ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f);
150 ESP_LOGCONFIG(TAG, "Obstacle Rollback: %.1f%%", this->obstacle_rollback_ * 100);
151 if (this->max_duration_ != UINT32_MAX) {
152 ESP_LOGCONFIG(TAG, "Maximum duration: %.1fs", this->max_duration_ / 1e3f);
153 }
154 ESP_LOGCONFIG(TAG,
155 "Start sensing delay: %.1fs\n"
156 "Malfunction detection: %s",
157 this->start_sensing_delay_ / 1e3f, YESNO(this->malfunction_detection_));
158}
159
162 if (this->prev_command_trigger_ != nullptr) {
164 this->prev_command_trigger_ = nullptr;
165 }
166}
167
171
173 if (this->open_obstacle_current_threshold_ == FLT_MAX) {
174 return false;
175 }
177}
178
182
184 if (this->close_obstacle_current_threshold_ == FLT_MAX) {
185 return false;
186 }
188}
192
194 switch (this->current_operation) {
196 if (this->target_position_ == COVER_OPEN) {
197 if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed
198 return false;
199 return !this->is_opening_();
200 }
201 return this->position >= this->target_position_;
203 if (this->target_position_ == COVER_CLOSED) {
204 if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed
205 return false;
206 return !this->is_closing_();
207 }
208 return this->position <= this->target_position_;
210 default:
211 return true;
212 }
213}
215 if (dir == this->current_operation)
216 return;
217
218 this->recompute_position_();
219 Trigger<> *trig;
220 switch (dir) {
222 trig = this->stop_trigger_;
223 break;
225 this->last_operation_ = dir;
226 trig = this->open_trigger_;
227 break;
229 this->last_operation_ = dir;
230 trig = this->close_trigger_;
231 break;
232 default:
233 return;
234 }
235
236 this->current_operation = dir;
237
238 this->stop_prev_trigger_();
239 trig->trigger();
240 this->prev_command_trigger_ = trig;
241
242 const auto now = millis();
243 this->start_dir_time_ = now;
244 this->last_recompute_time_ = now;
245}
248 return;
249
250 float dir;
251 float action_dur;
252 switch (this->current_operation) {
254 dir = 1.0F;
255 action_dur = this->open_duration_;
256 break;
258 dir = -1.0F;
259 action_dur = this->close_duration_;
260 break;
261 default:
262 return;
263 }
264
265 const auto now = millis();
266 this->position += dir * (now - this->last_recompute_time_) / action_dur;
267 this->position = clamp(this->position, 0.0F, 1.0F);
268
269 this->last_recompute_time_ = now;
270}
271
272} // namespace current_based
273} // 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.
void set_timeout(const std::string &name, uint32_t timeout, std::function< void()> &&f)
Set a timeout function with a unique name.
Definition component.cpp:75
constexpr const char * c_str() const
Definition string_ref.h:69
void stop_action()
Stop any action connected to this trigger.
Definition automation.h:104
void trigger(Ts... x)
Inform the parent automation that the event has triggered.
Definition automation.h:96
const optional< bool > & get_toggle() const
Definition cover.cpp:99
const optional< float > & get_position() const
Definition cover.cpp:97
CoverOperation current_operation
The current operation of the cover (idle, opening, closing).
Definition cover.h:116
optional< CoverRestoreState > restore_state_()
Definition cover.cpp:200
void publish_state(bool save=true)
Publish the current state of the cover.
Definition cover.cpp:166
float position
The position of the cover from 0.0 (fully closed) to 1.0 (fully open).
Definition cover.h:122
void start_direction_(cover::CoverOperation dir)
void control(const cover::CoverCall &call) override
void direction_idle_(float new_position=FLT_MAX)
bool has_value() const
Definition optional.h:87
float get_state() const
Getter-syntax for .state.
Definition sensor.cpp:91
const float COVER_CLOSED
Definition cover.cpp:10
const float COVER_OPEN
Definition cover.cpp:9
CoverOperation
Enum encoding the current operation of a cover.
Definition cover.h:80
@ COVER_OPERATION_OPENING
The cover is currently opening.
Definition cover.h:84
@ COVER_OPERATION_CLOSING
The cover is currently closing.
Definition cover.h:86
@ COVER_OPERATION_IDLE
The cover is currently idle (not moving)
Definition cover.h:82
const float DATA
For components that import data from directly connected sensors like DHT.
Definition component.cpp:20
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28
Application App
Global storage of Application pointer - only one Application can exist.
constexpr const T & clamp(const T &v, const T &lo, const T &hi, Compare comp)
Definition helpers.h:102