ESPHome  2022.6.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_is_assumed_state(false);
17  return traits;
18 }
20  if (call.get_stop()) {
21  this->direction_idle_();
22  }
23  if (call.get_position().has_value()) {
24  auto pos = *call.get_position();
25  if (pos == this->position) {
26  // already at target
27  } else {
29  this->target_position_ = pos;
30  this->start_direction_(op);
31  }
32  }
33 }
35  auto restore = this->restore_state_();
36  if (restore.has_value()) {
37  restore->apply(this);
38  } else {
39  this->position = 0.5f;
40  }
41 }
42 
44  if (this->current_operation == COVER_OPERATION_IDLE)
45  return;
46 
47  const uint32_t now = millis();
48 
49  if (this->current_operation == COVER_OPERATION_OPENING) {
50  if (this->malfunction_detection_ && this->is_closing_()) { // Malfunction
51  this->direction_idle_();
52  this->malfunction_trigger_->trigger();
53  ESP_LOGI(TAG, "'%s' - Malfunction detected during opening. Current flow detected in close circuit",
54  this->name_.c_str());
55  } else if (this->is_opening_blocked_()) { // Blocked
56  ESP_LOGD(TAG, "'%s' - Obstacle detected during opening.", this->name_.c_str());
57  this->direction_idle_();
58  if (this->obstacle_rollback_ != 0) {
59  this->set_timeout("rollback", 300, [this]() {
60  ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str());
61  this->target_position_ = clamp(this->position - this->obstacle_rollback_, 0.0F, 1.0F);
62  this->start_direction_(COVER_OPERATION_CLOSING);
63  });
64  }
65  } else if (this->is_initial_delay_finished_() && !this->is_opening_()) { // End reached
66  auto dur = (now - this->start_dir_time_) / 1e3f;
67  ESP_LOGD(TAG, "'%s' - Open position reached. Took %.1fs.", this->name_.c_str(), dur);
68  this->direction_idle_(COVER_OPEN);
69  }
70  } else if (this->current_operation == COVER_OPERATION_CLOSING) {
71  if (this->malfunction_detection_ && this->is_opening_()) { // Malfunction
72  this->direction_idle_();
73  this->malfunction_trigger_->trigger();
74  ESP_LOGI(TAG, "'%s' - Malfunction detected during closing. Current flow detected in open circuit",
75  this->name_.c_str());
76  } else if (this->is_closing_blocked_()) { // Blocked
77  ESP_LOGD(TAG, "'%s' - Obstacle detected during closing.", this->name_.c_str());
78  this->direction_idle_();
79  if (this->obstacle_rollback_ != 0) {
80  this->set_timeout("rollback", 300, [this]() {
81  ESP_LOGD(TAG, "'%s' - Rollback.", this->name_.c_str());
82  this->target_position_ = clamp(this->position + this->obstacle_rollback_, 0.0F, 1.0F);
83  this->start_direction_(COVER_OPERATION_OPENING);
84  });
85  }
86  } else if (this->is_initial_delay_finished_() && !this->is_closing_()) { // End reached
87  auto dur = (now - this->start_dir_time_) / 1e3f;
88  ESP_LOGD(TAG, "'%s' - Close position reached. Took %.1fs.", this->name_.c_str(), dur);
89  this->direction_idle_(COVER_CLOSED);
90  }
91  } else if (now - this->start_dir_time_ > this->max_duration_) {
92  ESP_LOGD(TAG, "'%s' - Max duration reached. Stopping cover.", this->name_.c_str());
93  this->direction_idle_();
94  }
95 
96  // Recompute position every loop cycle
97  this->recompute_position_();
98 
99  if (this->current_operation != COVER_OPERATION_IDLE && this->is_at_target_()) {
100  this->direction_idle_();
101  }
102 
103  // Send current position every second
104  if (this->current_operation != COVER_OPERATION_IDLE && now - this->last_publish_time_ > 1000) {
105  this->publish_state(false);
106  this->last_publish_time_ = now;
107  }
108 }
109 
110 void CurrentBasedCover::direction_idle_(float new_position) {
111  this->start_direction_(COVER_OPERATION_IDLE);
112  if (new_position != FLT_MAX) {
113  this->position = new_position;
114  }
115  this->publish_state();
116 }
117 
119  LOG_COVER("", "Endstop Cover", this);
120  LOG_SENSOR(" ", "Open Sensor", this->open_sensor_);
121  ESP_LOGCONFIG(TAG, " Open moving current threshold: %.11fA", this->open_moving_current_threshold_);
122  if (this->open_obstacle_current_threshold_ != FLT_MAX) {
123  ESP_LOGCONFIG(TAG, " Open obstacle current threshold: %.11fA", this->open_obstacle_current_threshold_);
124  }
125  ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f);
126  LOG_SENSOR(" ", "Close Sensor", this->close_sensor_);
127  ESP_LOGCONFIG(TAG, " Close moving current threshold: %.11fA", this->close_moving_current_threshold_);
128  if (this->close_obstacle_current_threshold_ != FLT_MAX) {
129  ESP_LOGCONFIG(TAG, " Close obstacle current threshold: %.11fA", this->close_obstacle_current_threshold_);
130  }
131  ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f);
132  ESP_LOGCONFIG(TAG, "Obstacle Rollback: %.1f%%", this->obstacle_rollback_ * 100);
133  if (this->max_duration_ != UINT32_MAX) {
134  ESP_LOGCONFIG(TAG, "Maximun duration: %.1fs", this->max_duration_ / 1e3f);
135  }
136  ESP_LOGCONFIG(TAG, "Start sensing delay: %.1fs", this->start_sensing_delay_ / 1e3f);
137  ESP_LOGCONFIG(TAG, "Malfunction detection: %s", YESNO(this->malfunction_detection_));
138 }
139 
142  if (this->prev_command_trigger_ != nullptr) {
143  this->prev_command_trigger_->stop_action();
144  this->prev_command_trigger_ = nullptr;
145  }
146 }
147 
149  return this->open_sensor_->get_state() > this->open_moving_current_threshold_;
150 }
151 
153  if (this->open_obstacle_current_threshold_ == FLT_MAX) {
154  return false;
155  }
156  return this->open_sensor_->get_state() > this->open_obstacle_current_threshold_;
157 }
158 
160  return this->close_sensor_->get_state() > this->close_moving_current_threshold_;
161 }
162 
164  if (this->close_obstacle_current_threshold_ == FLT_MAX) {
165  return false;
166  }
167  return this->open_sensor_->get_state() > this->open_obstacle_current_threshold_;
168 }
170  return millis() - this->start_dir_time_ > this->start_sensing_delay_;
171 }
172 
174  switch (this->current_operation) {
176  if (this->target_position_ == COVER_OPEN) {
177  if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed
178  return false;
179  return !this->is_opening_();
180  }
181  return this->position >= this->target_position_;
183  if (this->target_position_ == COVER_CLOSED) {
184  if (!this->is_initial_delay_finished_()) // During initial delay, state is assumed
185  return false;
186  return !this->is_closing_();
187  }
188  return this->position <= this->target_position_;
190  default:
191  return true;
192  }
193 }
195  if (dir == this->current_operation)
196  return;
197 
198  this->recompute_position_();
199  Trigger<> *trig;
200  switch (dir) {
202  trig = this->stop_trigger_;
203  break;
205  trig = this->open_trigger_;
206  break;
208  trig = this->close_trigger_;
209  break;
210  default:
211  return;
212  }
213 
214  this->current_operation = dir;
215 
216  this->stop_prev_trigger_();
217  trig->trigger();
218  this->prev_command_trigger_ = trig;
219 
220  const auto now = millis();
221  this->start_dir_time_ = now;
222  this->last_recompute_time_ = now;
223 }
225  if (this->current_operation == COVER_OPERATION_IDLE)
226  return;
227 
228  float dir;
229  float action_dur;
230  switch (this->current_operation) {
232  dir = 1.0F;
233  action_dur = this->open_duration_;
234  break;
236  dir = -1.0F;
237  action_dur = this->close_duration_;
238  break;
239  default:
240  return;
241  }
242 
243  const auto now = millis();
244  this->position += dir * (now - this->last_recompute_time_) / action_dur;
245  this->position = clamp(this->position, 0.0F, 1.0F);
246 
247  this->last_recompute_time_ = now;
248 }
249 
250 } // namespace current_based
251 } // 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 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