ESPHome  2022.11.3
current_based_cover.cpp
Go to the documentation of this file.
1 #include "current_based_cover.h"
2 #include "esphome/core/hal.h"
3 #include "esphome/core/log.h"
4 #include <cfloat>
5 
6 namespace esphome {
7 namespace current_based {
8 
9 static const char *const TAG = "current_based.cover";
10 
11 using namespace esphome::cover;
12 
14  auto traits = CoverTraits();
15  traits.set_supports_position(true);
16  traits.set_supports_toggle(true);
17  traits.set_is_assumed_state(false);
18  return traits;
19 }
21  if (call.get_stop()) {
22  this->direction_idle_();
23  }
24  if (call.get_toggle().has_value()) {
25  if (this->current_operation != COVER_OPERATION_IDLE) {
26  this->start_direction_(COVER_OPERATION_IDLE);
27  this->publish_state();
28  } else {
29  if (this->position == COVER_CLOSED || this->last_operation_ == COVER_OPERATION_CLOSING) {
30  this->target_position_ = COVER_OPEN;
31  this->start_direction_(COVER_OPERATION_OPENING);
32  } else {
33  this->target_position_ = COVER_CLOSED;
34  this->start_direction_(COVER_OPERATION_CLOSING);
35  }
36  }
37  }
38  if (call.get_position().has_value()) {
39  auto pos = *call.get_position();
40  if (pos == this->position) {
41  // already at target
42  } else {
44  this->target_position_ = pos;
45  this->start_direction_(op);
46  }
47  }
48 }
50  auto restore = this->restore_state_();
51  if (restore.has_value()) {
52  restore->apply(this);
53  } else {
54  this->position = 0.5f;
55  }
56 }
57 
59  if (this->current_operation == COVER_OPERATION_IDLE)
60  return;
61 
62  const uint32_t now = millis();
63 
64  if (this->current_operation == COVER_OPERATION_OPENING) {
65  if (this->malfunction_detection_ && this->is_closing_()) { // Malfunction
66  this->direction_idle_();
67  this->malfunction_trigger_->trigger();
68  ESP_LOGI(TAG, "'%s' - Malfunction detected during opening. Current flow detected in close circuit",
69  this->name_.c_str());
70  } else if (this->is_opening_blocked_()) { // Blocked
71  ESP_LOGD(TAG, "'%s' - Obstacle detected during opening.", this->name_.c_str());
72  this->direction_idle_();
73  if (this->obstacle_rollback_ != 0) {
74  this->set_timeout("rollback", 300, [this]() {
75  ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str());
76  this->target_position_ = clamp(this->position - this->obstacle_rollback_, 0.0F, 1.0F);
77  this->start_direction_(COVER_OPERATION_CLOSING);
78  });
79  }
80  } else if (this->is_initial_delay_finished_() && !this->is_opening_()) { // End reached
81  auto dur = (now - this->start_dir_time_) / 1e3f;
82  ESP_LOGD(TAG, "'%s' - Open position reached. Took %.1fs.", this->name_.c_str(), dur);
83  this->direction_idle_(COVER_OPEN);
84  }
85  } else if (this->current_operation == COVER_OPERATION_CLOSING) {
86  if (this->malfunction_detection_ && this->is_opening_()) { // Malfunction
87  this->direction_idle_();
88  this->malfunction_trigger_->trigger();
89  ESP_LOGI(TAG, "'%s' - Malfunction detected during closing. Current flow detected in open circuit",
90  this->name_.c_str());
91  } else if (this->is_closing_blocked_()) { // Blocked
92  ESP_LOGD(TAG, "'%s' - Obstacle detected during closing.", this->name_.c_str());
93  this->direction_idle_();
94  if (this->obstacle_rollback_ != 0) {
95  this->set_timeout("rollback", 300, [this]() {
96  ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str());
97  this->target_position_ = clamp(this->position + this->obstacle_rollback_, 0.0F, 1.0F);
98  this->start_direction_(COVER_OPERATION_OPENING);
99  });
100  }
101  } else if (this->is_initial_delay_finished_() && !this->is_closing_()) { // End reached
102  auto dur = (now - this->start_dir_time_) / 1e3f;
103  ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", this->name_.c_str(), dur);
104  this->direction_idle_(COVER_CLOSED);
105  }
106  } else if (now - this->start_dir_time_ > this->max_duration_) {
107  ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str());
108  this->direction_idle_();
109  }
110 
111  // Recompute position every loop cycle
112  this->recompute_position_();
113 
114  if (this->current_operation != COVER_OPERATION_IDLE && this->is_at_target_()) {
115  this->direction_idle_();
116  }
117 
118  // Send current position every second
119  if (this->current_operation != COVER_OPERATION_IDLE && now - this->last_publish_time_ > 1000) {
120  this->publish_state(false);
121  this->last_publish_time_ = now;
122  }
123 }
124 
125 void CurrentBasedCover::direction_idle_(float new_position) {
126  this->start_direction_(COVER_OPERATION_IDLE);
127  if (new_position != FLT_MAX) {
128  this->position = new_position;
129  }
130  this->publish_state();
131 }
132 
134  LOG_COVER("", "Endstop Cover", this);
135  LOG_SENSOR(" ", "Open Sensor", this->open_sensor_);
136  ESP_LOGCONFIG(TAG, " Open moving current threshold: %.11fA", this->open_moving_current_threshold_);
137  if (this->open_obstacle_current_threshold_ != FLT_MAX) {
138  ESP_LOGCONFIG(TAG, " Open obstacle current threshold: %.11fA", this->open_obstacle_current_threshold_);
139  }
140  ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f);
141  LOG_SENSOR(" ", "Close Sensor", this->close_sensor_);
142  ESP_LOGCONFIG(TAG, " Close moving current threshold: %.11fA", this->close_moving_current_threshold_);
143  if (this->close_obstacle_current_threshold_ != FLT_MAX) {
144  ESP_LOGCONFIG(TAG, " Close obstacle current threshold: %.11fA", this->close_obstacle_current_threshold_);
145  }
146  ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f);
147  ESP_LOGCONFIG(TAG, "Obstacle Rollback: %.1f%%", this->obstacle_rollback_ * 100);
148  if (this->max_duration_ != UINT32_MAX) {
149  ESP_LOGCONFIG(TAG, "Maximum duration: %.1fs", this->max_duration_ / 1e3f);
150  }
151  ESP_LOGCONFIG(TAG, "Start sensing delay: %.1fs", this->start_sensing_delay_ / 1e3f);
152  ESP_LOGCONFIG(TAG, "Malfunction detection: %s", YESNO(this->malfunction_detection_));
153 }
154 
157  if (this->prev_command_trigger_ != nullptr) {
158  this->prev_command_trigger_->stop_action();
159  this->prev_command_trigger_ = nullptr;
160  }
161 }
162 
164  return this->open_sensor_->get_state() > this->open_moving_current_threshold_;
165 }
166 
168  if (this->open_obstacle_current_threshold_ == FLT_MAX) {
169  return false;
170  }
171  return this->open_sensor_->get_state() > this->open_obstacle_current_threshold_;
172 }
173 
175  return this->close_sensor_->get_state() > this->close_moving_current_threshold_;
176 }
177 
179  if (this->close_obstacle_current_threshold_ == FLT_MAX) {
180  return false;
181  }
182  return this->open_sensor_->get_state() > this->open_obstacle_current_threshold_;
183 }
185  return millis() - this->start_dir_time_ > this->start_sensing_delay_;
186 }
187 
189  switch (this->current_operation) {
191  if (this->target_position_ == COVER_OPEN) {
192  if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed
193  return false;
194  return !this->is_opening_();
195  }
196  return this->position >= this->target_position_;
198  if (this->target_position_ == COVER_CLOSED) {
199  if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed
200  return false;
201  return !this->is_closing_();
202  }
203  return this->position <= this->target_position_;
205  default:
206  return true;
207  }
208 }
210  if (dir == this->current_operation)
211  return;
212 
213  this->recompute_position_();
214  Trigger<> *trig;
215  switch (dir) {
217  trig = this->stop_trigger_;
218  break;
220  this->last_operation_ = dir;
221  trig = this->open_trigger_;
222  break;
224  this->last_operation_ = dir;
225  trig = this->close_trigger_;
226  break;
227  default:
228  return;
229  }
230 
231  this->current_operation = dir;
232 
233  this->stop_prev_trigger_();
234  trig->trigger();
235  this->prev_command_trigger_ = trig;
236 
237  const auto now = millis();
238  this->start_dir_time_ = now;
239  this->last_recompute_time_ = now;
240 }
242  if (this->current_operation == COVER_OPERATION_IDLE)
243  return;
244 
245  float dir;
246  float action_dur;
247  switch (this->current_operation) {
249  dir = 1.0F;
250  action_dur = this->open_duration_;
251  break;
253  dir = -1.0F;
254  action_dur = this->close_duration_;
255  break;
256  default:
257  return;
258  }
259 
260  const auto now = millis();
261  this->position += dir * (now - this->last_recompute_time_) / action_dur;
262  this->position = clamp(this->position, 0.0F, 1.0F);
263 
264  this->last_recompute_time_ = now;
265 }
266 
267 } // namespace current_based
268 } // namespace esphome
void start_direction_(cover::CoverOperation dir)
const float DATA
For components that import data from directly connected sensors like DHT.
Definition: component.cpp:18
void direction_idle_(float new_position=FLT_MAX)
CoverOperation
Enum encoding the current operation of a cover.
Definition: cover.h:80
The cover is currently closing.
Definition: cover.h:86
const float COVER_CLOSED
Definition: cover.cpp:10
bool has_value() const
Definition: optional.h:87
constexpr const T & clamp(const T &v, const T &lo, const T &hi, Compare comp)
Definition: helpers.h:84
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:26
void trigger(Ts... x)
Inform the parent automation that the event has triggered.
Definition: automation.h:95
const optional< bool > & get_toggle() const
Definition: cover.cpp:99
const float COVER_OPEN
Definition: cover.cpp:9
Definition: a4988.cpp:4
float position
Definition: cover.h:14
The cover is currently opening.
Definition: cover.h:84
const optional< float > & get_position() const
Definition: cover.cpp:97
void control(const cover::CoverCall &call) override
bool get_stop() const
Definition: cover.cpp:147