ESPHome 2026.6.0-dev
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::he60r {
8
9static const char *const TAG = "he60r.cover";
10static const uint8_t QUERY_BYTE = 0x38;
11static const uint8_t TOGGLE_BYTE = 0x30;
12
13using namespace esphome::cover;
14
16 auto restore = this->restore_state_();
17
18 if (restore.has_value()) {
19 restore->apply(this);
20 this->publish_state(false);
21 } else {
22 // if no other information, assume half open
23 this->position = 0.5f;
24 }
27 this->set_interval(300, [this]() { this->update_(); });
28}
29
31 auto traits = CoverTraits();
32 traits.set_supports_stop(true);
33 traits.set_supports_position(true);
34 traits.set_supports_toggle(true);
35 traits.set_is_assumed_state(false);
36 return traits;
37}
38
40 LOG_COVER("", "HE60R Cover", this);
42 ESP_LOGCONFIG(TAG,
43 " Open Duration: %.1fs\n"
44 " Close Duration: %.1fs",
45 this->open_duration_ / 1e3f, this->close_duration_ / 1e3f);
46 auto restore = this->restore_state_();
47 if (restore.has_value())
48 ESP_LOGCONFIG(TAG, " Saved position %d%%", (int) (restore->position * 100.f));
49}
50
52 const uint32_t now = millis();
53
55 auto new_position = operation == COVER_OPERATION_OPENING ? COVER_OPEN : COVER_CLOSED;
56 if (new_position != this->position || this->current_operation != COVER_OPERATION_IDLE) {
57 this->position = new_position;
59 if (this->last_command_ == operation) {
60 float dur = (float) (now - this->start_dir_time_) / 1e3f;
61 ESP_LOGD(TAG, "'%s' - %s endstop reached. Took %.1fs.", this->name_.c_str(),
62 operation == COVER_OPERATION_OPENING ? "Open" : "Close", dur);
63 }
64 this->publish_state();
65 }
66}
67
69 if (this->current_operation != operation) {
70 this->current_operation = operation;
71 if (operation != COVER_OPERATION_IDLE)
73 }
74}
75
76void HE60rCover::process_rx_(uint8_t data) {
77 ESP_LOGV(TAG, "Process RX data %X", data);
78 if (!this->query_seen_) {
79 this->query_seen_ = data == QUERY_BYTE;
80 if (!this->query_seen_)
81 ESP_LOGD(TAG, "RX Byte %02X", data);
82 return;
83 }
84 switch (data) {
85 case 0xB5: // at closed endstop, jammed?
86 case 0xF5: // at closed endstop, jammed?
87 case 0x55: // at closed endstop
90 break;
91
92 case 0x52: // at opened endstop
95 break;
96
97 case 0x51: // travelling up after encountering obstacle
98 case 0x01: // travelling up
99 case 0x11: // travelling up, triggered by remote
102 break;
103
104 case 0x44: // travelling down
105 case 0x14: // travelling down, triggered by remote
108 break;
109
110 case 0x86: // Stopped, jammed?
111 case 0x16: // stopped midway while opening, by remote
112 case 0x06: // stopped midway while opening
115 break;
116
117 case 0x10: // stopped midway while closing, by remote
118 case 0x00: // stopped midway while closing
121 break;
122
123 default:
124 break;
125 }
126}
127
129 if (this->toggles_needed_ != 0) {
130 if ((this->counter_++ & 0x3) == 0) {
131 this->toggles_needed_--;
132 ESP_LOGD(TAG, "Writing byte 0x30, still needed=%u", this->toggles_needed_);
133 this->write_byte(TOGGLE_BYTE);
134 } else {
135 this->write_byte(QUERY_BYTE);
136 }
137 } else {
138 this->write_byte(QUERY_BYTE);
139 this->counter_ = 0;
140 }
142 this->recompute_position_();
143
144 // if we initiated the move, check if we reached the target position
145 if (this->last_command_ != COVER_OPERATION_IDLE) {
146 if (this->is_at_target_()) {
148 }
149 }
150 }
151}
152
154 uint8_t data;
155
156 while (this->available() > 0) {
157 if (this->read_byte(&data)) {
158 this->process_rx_(data);
159 }
160 }
161}
162
164 if (call.get_stop()) {
166 } else if (call.get_toggle().has_value()) {
167 // toggle action logic: OPEN - STOP - CLOSE
168 if (this->last_command_ != COVER_OPERATION_IDLE) {
170 } else {
171 this->toggles_needed_++;
172 }
173 } else {
174 auto pos_opt = call.get_position();
175 if (!pos_opt.has_value())
176 return;
177 // go to position action
178 auto pos = *pos_opt;
179 // are we at the target?
180 if (pos == this->position) {
182 } else {
183 this->target_position_ = pos;
185 }
186 }
187}
188
195 // equality of floats is fraught with peril - this is reliable since the values are 0.0 or 1.0 which are
196 // exactly representable.
197 if (this->target_position_ == COVER_OPEN || this->target_position_ == COVER_CLOSED)
198 return false;
199 // aiming for an intermediate position - exact comparison here will not work and we need to allow for overshoot
200 switch (this->last_command_) {
202 return this->position >= this->target_position_;
204 return this->position <= this->target_position_;
207 default:
208 return true;
209 }
210}
212 this->last_command_ = dir;
213 if (this->current_operation == dir)
214 return;
215 ESP_LOGD(TAG, "'%s' - Direction '%s' requested.", this->name_.c_str(),
216 dir == COVER_OPERATION_OPENING ? "OPEN"
217 : dir == COVER_OPERATION_CLOSING ? "CLOSE"
218 : "STOP");
219
220 if (dir == this->next_direction_) {
221 // either moving and needs to stop, or stopped and will move correctly on one trigger
222 this->toggles_needed_ = 1;
223 } else {
225 // if stopped, but will go the wrong way, need 3 triggers.
226 this->toggles_needed_ = 3;
227 } else {
228 // just stop and reverse
229 this->toggles_needed_ = 2;
230 }
231 ESP_LOGD(TAG, "'%s' - Reversing direction.", this->name_.c_str());
232 }
233 this->start_dir_time_ = millis();
234}
235
238 return;
239
240 const uint32_t now = millis();
241 if (now != this->last_recompute_time_) {
242 auto diff = (unsigned) (now - last_recompute_time_);
243 float delta;
244 switch (this->current_operation) {
246 delta = (float) diff / (float) this->open_duration_;
247 break;
249 delta = -(float) diff / (float) this->close_duration_;
250 break;
251 default:
252 return;
253 }
254
255 // make sure our guesstimate never reaches full open or close.
256 auto new_position = clamp(delta + this->position, COVER_CLOSED + 0.01f, COVER_OPEN - 0.01f);
257 ESP_LOGD(TAG, "Recompute %ums, dir=%u, delta=%f, pos=%f", diff, this->current_operation, delta, new_position);
258 this->last_recompute_time_ = now;
259 if (this->position != new_position) {
260 this->position = new_position;
261 this->publish_state();
262 }
263 }
264}
265
266} // namespace esphome::he60r
ESPDEPRECATED("Use const char* or uint32_t overload instead. Removed in 2026.7.0", "2026.1.0") void set_interval(const std voi set_interval)(const char *name, uint32_t interval, std::function< void()> &&f)
Set an interval function with a unique name.
Definition component.h:400
constexpr const char * c_str() const
Definition string_ref.h:73
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:179
void publish_state(bool save=true)
Publish the current state of the cover.
Definition cover.cpp:142
float position
The position of the cover from 0.0 (fully closed) to 1.0 (fully open).
Definition cover.h:121
bool is_at_target_() const
Check if the cover has reached or passed the target position.
Definition he60r.cpp:194
void control(const cover::CoverCall &call) override
Definition he60r.cpp:163
cover::CoverOperation last_command_
Definition he60r.h:35
cover::CoverTraits get_traits() override
Definition he60r.cpp:30
void setup() override
Definition he60r.cpp:15
void loop() override
Definition he60r.cpp:153
void endstop_reached_(cover::CoverOperation operation)
Definition he60r.cpp:51
uint32_t last_recompute_time_
Definition he60r.h:36
void set_current_operation_(cover::CoverOperation operation)
Definition he60r.cpp:68
void process_rx_(uint8_t data)
Definition he60r.cpp:76
cover::CoverOperation next_direction_
Definition he60r.h:34
void dump_config() override
Definition he60r.cpp:39
void start_direction_(cover::CoverOperation dir)
Definition he60r.cpp:211
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:16
bool read_byte(uint8_t *data)
Definition uart.h:34
void write_byte(uint8_t data)
Definition uart.h:18
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
size_t size_t pos
Definition helpers.h:1038
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:28
static void uint32_t