ESPHome 2026.8.0b4
Loading...
Searching...
No Matches
hoermann_hcp.h
Go to the documentation of this file.
1#pragma once
2
3#include <utility>
4
8
9namespace esphome::hoermann_hcp {
10
11// Door state as reported by the Hoermann bus controller.
12enum class DoorState : uint8_t {
13 OPEN,
14 OPENING,
15 CLOSED,
16 CLOSING,
19 VENT,
21 STOPPED,
22};
23
24// A HCP command is a simulated key press: the pressed value is presented to the bus controller, then after a
25// short delay the released value. Each half also carries a second register, which only the lamp command uses.
27 const char *name;
28 uint16_t pressed_value;
30 uint16_t pressed_value_2{0x0000};
31 uint16_t released_value_2{0x0000};
32 // A door command supersedes a half-open target; the lamp has no bearing on where the door is going.
33 bool clears_target{true};
34};
35
37 public:
38 void update() override;
39 void dump_config() override;
40
41 // Registered by child entities to be notified when the door state changes.
42 template<typename F> void add_on_state_callback(F &&callback) {
43 this->state_callback_.add(std::forward<F>(callback));
44 }
45
46 // Modbus server callbacks. The bus controller pushes commands and polls state with 0x17 (the hub runs the write
47 // half first, storing the command register that the read half echoes back) and broadcasts status with 0x10.
48 modbus::ResponseStatus on_write_registers(uint16_t start_address, const modbus::RegisterValues &registers) override;
49 modbus::ResponseStatus on_read_holding_registers(uint16_t start_address, uint16_t number_of_registers,
50 modbus::RegisterValues &registers) override;
51
52 // Positions follow the cover convention: 0.0 is fully closed, 1.0 fully open. These return false when the bus
53 // controller cannot be asked right now, so the caller can react.
54 bool open_door();
55 bool close_door();
56 bool impulse_door();
57 bool stop_door();
58 bool set_position(float position);
59 bool toggle_light();
60
61 DoorState get_door_state() const { return this->door_state_; }
62 float get_current_position() const { return this->current_position_; }
63 bool is_valid() const { return this->valid_; }
64 bool is_light_on() const { return this->light_on_; }
65 // False until a broadcast has actually carried the lamp register. Bus traffic alone makes the connection
66 // valid without saying anything about the lamp, so is_light_on() would still be its default.
67 bool is_light_known() const { return this->light_seen_; }
68 // Where the lamp ends up once every toggle on its way has landed, each of which inverts it. Until then the
69 // lamp still reads as its old self, so this is what a request has to be judged against.
70 bool is_light_heading_on() const { return this->light_on_ != (this->light_toggles_in_flight_ % 2 != 0); }
71 // Drops a lamp toggle the controller has not started reading, so a reversing request cancels it outright
72 // instead of fighting it. Returns false if there is nothing to cancel.
74
75 protected:
76 // True while a lamp toggle is queued but not yet fetched, so the lamp is about to invert.
77 bool is_light_toggle_pending_() const;
78 // Toggles the door has not been shown yet, which is at most the one still waiting in the command slot.
79 uint8_t unsent_light_toggles_() const;
80 void record_response_();
81 // Returns false when the bus controller has not fetched the previous command yet.
82 bool queue_command_(const HoermannHcpCommand &command);
83 // Throws away the pending command, taking any armed target with it unless the command was the lamp toggle.
84 void drop_command_();
85 // One outstanding toggle reached the lamp, was withdrawn, or was thrown away.
87 // Stops expecting the toggles the door has already been shown to reach the lamp.
89 // Appends the two key-press registers and advances the pending command's press/release state.
91 void on_position_reg_(uint16_t value);
92 void on_state_reg_(uint16_t value);
93 void on_light_reg_(uint16_t value);
94
95 void set_valid_(bool valid);
97 // Recomputes the reported position from position_raw_ and the current door state.
99 bool has_target_() const { return this->target_position_ != 0.0f; }
100 void clear_target_();
101 void set_light_on_(bool on);
102 void set_light_seen_(bool seen);
103
105
106 float current_position_{0.0f};
107 // Position the door was told to travel to; 0.0 means no target is armed.
108 float target_position_{0.0f};
109
110 // Pending command / key-press state machine.
113 // Separate from command_queued_at_ so an unrelated command cannot extend the target's start deadline.
117 // When the door was last handed a lamp key press. It reports the lamp a moment later, so this bounds the
118 // wait. Queueing another toggle deliberately leaves it alone, so the one already sent keeps its deadline.
120
121 // A command is "pressed" for this long before its end value is sent.
122 uint16_t key_press_delay_ms_{100};
123 // Drop the "connected" flag if the bus controller has not polled us for this long.
125 // The state starts on a value the bus controller never reports, so the first broadcast is decoded even when
126 // it reads 0x0000.
127 uint16_t prev_state_reg_{0xFFFF};
128 // 0x17 write half: command register last written to COMMAND_REG. The read half echoes its high-byte message
129 // counter and low-byte command back from STATE_REG.
131
133 // Direction the door was started in for the current target. A target armed while the door is still travelling
134 // the other way must not be judged by the reported direction until the door has turned around.
136 // Position as reported by the bus controller, 0..200 across the full travel.
137 uint8_t position_raw_{0};
139 bool target_started_{false};
140 bool valid_{false};
141 bool changed_{false};
142 bool light_on_{false};
143 bool light_seen_{false};
145};
146
147} // namespace esphome::hoermann_hcp
This class simplifies creating components that periodically check a state.
Definition component.h:510
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:227
void set_door_state_(DoorState state)
modbus::ResponseStatus on_read_holding_registers(uint16_t start_address, uint16_t number_of_registers, modbus::RegisterValues &registers) override
CallbackManager< void()> state_callback_
modbus::ResponseStatus on_write_registers(uint16_t start_address, const modbus::RegisterValues &registers) override
const HoermannHcpCommand * next_command_
void push_command_registers_(modbus::RegisterValues &registers)
void add_on_state_callback(F &&callback)
bool queue_command_(const HoermannHcpCommand &command)
void on_position_reg_(uint16_t value)
float position
Definition cover.h:0
bool state
Definition fan.h:2
std::optional< ExceptionCode > ResponseStatus
Definition modbus.h:336
bool valid
static void uint32_t