ESPHome 2026.9.0-dev
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 names the buttons that do
26// not fit into the first.
28 const char *name;
29 uint16_t pressed_value;
31 uint16_t pressed_value_2{0x0000};
32 uint16_t released_value_2{0x0000};
33 // A door command supersedes a half-open target; the lamp has no bearing on where the door is going.
34 bool clears_target{true};
35};
36
38 public:
39 void update() override;
40 void dump_config() override;
41
42 // Registered by child entities to be notified when the door state changes.
43 template<typename F> void add_on_state_callback(F &&callback) {
44 this->state_callback_.add(std::forward<F>(callback));
45 }
46
47 // Modbus server callbacks. The bus controller pushes commands and polls state with 0x17 (the hub runs the write
48 // half first, storing the command register that the read half echoes back) and broadcasts status with 0x10.
49 modbus::ResponseStatus on_write_registers(uint16_t start_address, const modbus::RegisterValues &registers) override;
50 modbus::ResponseStatus on_read_holding_registers(uint16_t start_address, uint16_t number_of_registers,
51 modbus::RegisterValues &registers) override;
52
53 // Positions follow the cover convention: 0.0 is fully closed, 1.0 fully open. These return false when the bus
54 // controller cannot be asked right now, so the caller can react.
55 bool open_door();
56 bool close_door();
57 bool impulse_door();
58 // The door drives to these intermediate positions on its own, so neither takes a target to be stopped at.
59 bool vent_door();
60 bool half_open_door();
61 bool stop_door();
62 bool set_position(float position);
63 bool toggle_light();
64
65 DoorState get_door_state() const { return this->door_state_; }
66 float get_current_position() const { return this->current_position_; }
67 bool is_valid() const { return this->valid_; }
68 bool is_light_on() const { return this->light_on_; }
69 // False until a broadcast has actually carried the lamp register. Bus traffic alone makes the connection
70 // valid without saying anything about the lamp, so is_light_on() would still be its default.
71 bool is_light_known() const { return this->light_seen_; }
72 // Where the lamp ends up once every toggle on its way has landed, each of which inverts it. Until then the
73 // lamp still reads as its old self, so this is what a request has to be judged against.
74 bool is_light_heading_on() const { return this->light_on_ != (this->light_toggles_in_flight_ % 2 != 0); }
75 // Drops a lamp toggle the controller has not started reading, so a reversing request cancels it outright
76 // instead of fighting it. Returns false if there is nothing to cancel.
78
79 protected:
80 // True while a lamp toggle is queued but not yet fetched, so the lamp is about to invert.
81 bool is_light_toggle_pending_() const;
82 // Toggles the door has not been shown yet, which is at most the one still waiting in the command slot.
83 uint8_t unsent_light_toggles_() const;
84 void record_response_();
85 // Returns false when the bus controller has not fetched the previous command yet.
86 bool queue_command_(const HoermannHcpCommand &command);
87 // Throws away the pending command, taking any armed target with it unless the command was the lamp toggle.
88 void drop_command_();
89 // One outstanding toggle reached the lamp, was withdrawn, or was thrown away.
91 // Stops expecting the toggles the door has already been shown to reach the lamp.
93 // Appends the two key-press registers and advances the pending command's press/release state.
95 void on_position_reg_(uint16_t value);
96 void on_state_reg_(uint16_t value);
97 void on_light_reg_(uint16_t value);
98
99 void set_valid_(bool valid);
101 // Recomputes the reported position from position_raw_ and the current door state.
103 bool has_target_() const { return this->target_position_ != 0.0f; }
104 void clear_target_();
105 void set_light_on_(bool on);
106 void set_light_seen_(bool seen);
107
109
110 float current_position_{0.0f};
111 // Position the door was told to travel to; 0.0 means no target is armed.
112 float target_position_{0.0f};
113
114 // Pending command / key-press state machine.
117 // Separate from command_queued_at_ so an unrelated command cannot extend the target's start deadline.
121 // When the door was last handed a lamp key press. It reports the lamp a moment later, so this bounds the
122 // wait. Queueing another toggle deliberately leaves it alone, so the one already sent keeps its deadline.
124
125 // A command is "pressed" for this long before its end value is sent.
126 uint16_t key_press_delay_ms_{100};
127 // Drop the "connected" flag if the bus controller has not polled us for this long.
129 // The state starts on a value the bus controller never reports, so the first broadcast is decoded even when
130 // it reads 0x0000.
131 uint16_t prev_state_reg_{0xFFFF};
132 // 0x17 write half: command register last written to COMMAND_REG. The read half echoes its high-byte message
133 // counter and low-byte command back from STATE_REG.
135
137 // Direction the door was started in for the current target. A target armed while the door is still travelling
138 // the other way must not be judged by the reported direction until the door has turned around.
140 // Position as reported by the bus controller, 0..200 across the full travel.
141 uint8_t position_raw_{0};
143 bool target_started_{false};
144 bool valid_{false};
145 bool changed_{false};
146 bool light_on_{false};
147 bool light_seen_{false};
149};
150
151} // 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:306
bool valid
static void uint32_t