ESPHome 2025.5.0
Loading...
Searching...
No Matches
he60r.cpp
Go to the documentation of this file.
1#include "he60r.h"
2#include "esphome/core/hal.h"
3#include "esphome/core/log.h"
4
5#include <cinttypes>
6
7namespace esphome {
8namespace he60r {
9
10static const char *const TAG = "he60r.cover";
11static const uint8_t QUERY_BYTE = 0x38;
12static const uint8_t TOGGLE_BYTE = 0x30;
13
14using namespace esphome::cover;
15
17 auto restore = this->restore_state_();
18
19 if (restore.has_value()) {
20 restore->apply(this);
21 this->publish_state(false);
22 } else {
23 // if no other information, assume half open
24 this->position = 0.5f;
25 }
28 this->set_interval(300, [this]() { this->update_(); });
29}
30
32 auto traits = CoverTraits();
33 traits.set_supports_stop(true);
34 traits.set_supports_position(true);
35 traits.set_supports_toggle(true);
36 traits.set_is_assumed_state(false);
37 return traits;
38}
39
41 LOG_COVER("", "HE60R Cover", this);
43 ESP_LOGCONFIG(TAG, " Open Duration: %.1fs", this->open_duration_ / 1e3f);
44 ESP_LOGCONFIG(TAG, " Close Duration: %.1fs", this->close_duration_ / 1e3f);
45 auto restore = this->restore_state_();
46 if (restore.has_value())
47 ESP_LOGCONFIG(TAG, " Saved position %d%%", (int) (restore->position * 100.f));
48}
49
51 const uint32_t now = millis();
52
54 auto new_position = operation == COVER_OPERATION_OPENING ? COVER_OPEN : COVER_CLOSED;
55 if (new_position != this->position || this->current_operation != COVER_OPERATION_IDLE) {
56 this->position = new_position;
58 if (this->last_command_ == operation) {
59 float dur = (float) (now - this->start_dir_time_) / 1e3f;
60 ESP_LOGD(TAG, "'%s' - %s endstop reached. Took %.1fs.", this->name_.c_str(),
61 operation == COVER_OPERATION_OPENING ? "Open" : "Close", dur);
62 }
63 this->publish_state();
64 }
65}
66
68 if (this->current_operation != operation) {
69 this->current_operation = operation;
70 if (operation != COVER_OPERATION_IDLE)
72 }
73}
74
75void HE60rCover::process_rx_(uint8_t data) {
76 ESP_LOGV(TAG, "Process RX data %X", data);
77 if (!this->query_seen_) {
78 this->query_seen_ = data == QUERY_BYTE;
79 if (!this->query_seen_)
80 ESP_LOGD(TAG, "RX Byte %02X", data);
81 return;
82 }
83 switch (data) {
84 case 0xB5: // at closed endstop, jammed?
85 case 0xF5: // at closed endstop, jammed?
86 case 0x55: // at closed endstop
89 break;
90
91 case 0x52: // at opened endstop
94 break;
95
96 case 0x51: // travelling up after encountering obstacle
97 case 0x01: // travelling up
98 case 0x11: // travelling up, triggered by remote
101 break;
102
103 case 0x44: // travelling down
104 case 0x14: // travelling down, triggered by remote
107 break;
108
109 case 0x86: // Stopped, jammed?
110 case 0x16: // stopped midway while opening, by remote
111 case 0x06: // stopped midway while opening
114 break;
115
116 case 0x10: // stopped midway while closing, by remote
117 case 0x00: // stopped midway while closing
120 break;
121
122 default:
123 break;
124 }
125}
126
128 if (this->toggles_needed_ != 0) {
129 if ((this->counter_++ & 0x3) == 0) {
130 this->toggles_needed_--;
131 ESP_LOGD(TAG, "Writing byte 0x30, still needed=%u", this->toggles_needed_);
132 this->write_byte(TOGGLE_BYTE);
133 } else {
134 this->write_byte(QUERY_BYTE);
135 }
136 } else {
137 this->write_byte(QUERY_BYTE);
138 this->counter_ = 0;
139 }
141 this->recompute_position_();
142
143 // if we initiated the move, check if we reached the target position
144 if (this->last_command_ != COVER_OPERATION_IDLE) {
145 if (this->is_at_target_()) {
147 }
148 }
149 }
150}
151
153 uint8_t data;
154
155 while (this->available() > 0) {
156 if (this->read_byte(&data)) {
157 this->process_rx_(data);
158 }
159 }
160}
161
163 if (call.get_stop()) {
165 } else if (call.get_toggle().has_value()) {
166 // toggle action logic: OPEN - STOP - CLOSE
167 if (this->last_command_ != COVER_OPERATION_IDLE) {
169 } else {
170 this->toggles_needed_++;
171 }
172 } else if (call.get_position().has_value()) {
173 // go to position action
174 auto pos = *call.get_position();
175 // are we at the target?
176 if (pos == this->position) {
178 } else {
179 this->target_position_ = pos;
181 }
182 }
183}
184
191 // equality of floats is fraught with peril - this is reliable since the values are 0.0 or 1.0 which are
192 // exactly representable.
194 return false;
195 // aiming for an intermediate position - exact comparison here will not work and we need to allow for overshoot
196 switch (this->last_command_) {
198 return this->position >= this->target_position_;
200 return this->position <= this->target_position_;
203 default:
204 return true;
205 }
206}
208 this->last_command_ = dir;
209 if (this->current_operation == dir)
210 return;
211 ESP_LOGD(TAG, "'%s' - Direction '%s' requested.", this->name_.c_str(),
212 dir == COVER_OPERATION_OPENING ? "OPEN"
213 : dir == COVER_OPERATION_CLOSING ? "CLOSE"
214 : "STOP");
215
216 if (dir == this->next_direction_) {
217 // either moving and needs to stop, or stopped and will move correctly on one trigger
218 this->toggles_needed_ = 1;
219 } else {
221 // if stopped, but will go the wrong way, need 3 triggers.
222 this->toggles_needed_ = 3;
223 } else {
224 // just stop and reverse
225 this->toggles_needed_ = 2;
226 }
227 ESP_LOGD(TAG, "'%s' - Reversing direction.", this->name_.c_str());
228 }
229 this->start_dir_time_ = millis();
230}
231
234 return;
235
236 const uint32_t now = millis();
237 if (now > this->last_recompute_time_) {
238 auto diff = (unsigned) (now - last_recompute_time_);
239 float delta;
240 switch (this->current_operation) {
242 delta = (float) diff / (float) this->open_duration_;
243 break;
245 delta = -(float) diff / (float) this->close_duration_;
246 break;
247 default:
248 return;
249 }
250
251 // make sure our guesstimate never reaches full open or close.
252 auto new_position = clamp(delta + this->position, COVER_CLOSED + 0.01f, COVER_OPEN - 0.01f);
253 ESP_LOGD(TAG, "Recompute %ums, dir=%u, delta=%f, pos=%f", diff, this->current_operation, delta, new_position);
254 this->last_recompute_time_ = now;
255 if (this->position != new_position) {
256 this->position = new_position;
257 this->publish_state();
258 }
259 }
260}
261
262} // namespace he60r
263} // namespace esphome
void set_interval(const std::string &name, uint32_t interval, std::function< void()> &&f)
Set an interval function with a unique name.
Definition component.cpp:55
constexpr const char * c_str() const
Definition string_ref.h:68
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
bool is_at_target_() const
Check if the cover has reached or passed the target position.
Definition he60r.cpp:190
void control(const cover::CoverCall &call) override
Definition he60r.cpp:162
cover::CoverOperation last_command_
Definition he60r.h:37
cover::CoverTraits get_traits() override
Definition he60r.cpp:31
void setup() override
Definition he60r.cpp:16
void loop() override
Definition he60r.cpp:152
void endstop_reached_(cover::CoverOperation operation)
Definition he60r.cpp:50
uint32_t last_recompute_time_
Definition he60r.h:38
void set_current_operation_(cover::CoverOperation operation)
Definition he60r.cpp:67
void process_rx_(uint8_t data)
Definition he60r.cpp:75
cover::CoverOperation next_direction_
Definition he60r.h:36
void dump_config() override
Definition he60r.cpp:40
void start_direction_(cover::CoverOperation dir)
Definition he60r.cpp:207
bool has_value() const
Definition optional.h:87
void check_uart_settings(uint32_t baud_rate, uint8_t stop_bits=1, UARTParityOptions parity=UART_CONFIG_PARITY_NONE, uint8_t data_bits=8)
Check that the configuration of the UART bus matches the provided values and otherwise print a warnin...
Definition uart.cpp:13
bool read_byte(uint8_t *data)
Definition uart.h:29
void write_byte(uint8_t data)
Definition uart.h:19
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
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:27
constexpr const T & clamp(const T &v, const T &lo, const T &hi, Compare comp)
Definition helpers.h:101