Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
proto_heating_profiles.h
Go to the documentation of this file.
1#pragma once
2
3/// @file proto_heating_profiles.h
4/// @brief Heating profiles: what a climate card offers for each kind of heater, and how.
5/// @ingroup hioc_protocol
6///
7/// A profile is one row per (node type, subtype) that a `climate:` entity can drive. It names the
8/// control path (the private register protocol of proto_heating.h, or the standard EXECUTE with a
9/// heating level as the main parameter), the HVAC modes and presets the card shows, how each maps to
10/// a wire value in both directions, which registers a poll reads and which companion entities exist.
11/// The platform asks the profile and never branches on a device type.
12///
13/// The enums here are protocol-side: they carry no ESPHome type. The platform maps HeatingHvacMode
14/// and HeatingPresetId onto ESPHome's climate modes and presets.
15///
16/// \todo Heating-level control of the plain electrical heater (node type 52, subtype 0) has no capture
17/// or hardware report yet. Confirm with one before calling it supported.
18/// \todo Every 52/1 seen is an Atlantic radiator. A non-Atlantic 52/1 would need the heating-level
19/// path instead; the profile keys on (type, subtype) only.
20
21#include "proto_device_model.h"
22#include "proto_heating.h"
23#include "proto_sizes.h"
24
25#include <cstddef>
26#include <cstdint>
27
28namespace esphome {
29namespace home_io_control {
30
31/// @brief How a heater is commanded and read.
32enum class HeatingControlPath : uint8_t {
33 PRIVATE_REGISTERS, ///< CMD_WRITE_PRIVATE register reads and writes (proto_heating.h).
34 HEATING_LEVEL, ///< Standard EXECUTE with a heating level as the main parameter; read with 0x03.
35 TEMPERATURE_EXECUTE, ///< Standard EXECUTE with a temperature or an alias as the main parameter; read with 0x03.
36};
37
38/// @brief HVAC modes a profile can offer.
39enum class HeatingHvacMode : uint8_t {
40 OFF, ///< Heater off.
41 HEAT, ///< Heating on.
42 AUTO, ///< The device decides.
43};
44
45/// @brief Presets a profile can offer. CUSTOM entries carry their own name.
46enum class HeatingPresetId : uint8_t {
47 HOME, ///< Someone is in the room.
48 AWAY, ///< Nobody is in the room (the frost-protection level on a level heater).
49 COMFORT, ///< Comfort level.
50 ECO, ///< Economy level.
51 BOOST, ///< Boost level.
52 CUSTOM, ///< Named by HeatingPresetEntry::custom_name.
53};
54
55/// @brief Main-parameter prefix byte of a heating level: the level occupies the low byte.
56static constexpr uint8_t HEATING_LEVEL_MP_PREFIX = 0xFC;
57
58/// @brief Heating levels (low byte of the main parameter, prefix HEATING_LEVEL_MP_PREFIX).
59namespace heating_level {
60static constexpr uint8_t OFF = 0x00; ///< Off.
61static constexpr uint8_t FROST_PROTECTION = 0x01; ///< Frost protection.
62static constexpr uint8_t ECO = 0x02; ///< Economy.
63static constexpr uint8_t COMFORT_MINUS_2 = 0x03; ///< Comfort, two degrees lower.
64static constexpr uint8_t COMFORT_MINUS_1 = 0x04; ///< Comfort, one degree lower.
65static constexpr uint8_t COMFORT = 0x05; ///< Comfort.
66static constexpr uint8_t BOOST = 0x07; ///< Boost.
67static constexpr uint8_t SECURED = 0x3F; ///< Secured; reported by the device, never commanded.
68} // namespace heating_level
69
70/// @brief Main-parameter prefix byte of a setpoint alias: the alias occupies the low byte.
71static constexpr uint8_t HEATING_ALIAS_MP_PREFIX = 0xD8;
72
73/// @brief Setpoint aliases (low byte of the main parameter, prefix HEATING_ALIAS_MP_PREFIX).
74namespace heating_alias {
75static constexpr uint8_t COMFORT = 0x0F; ///< The device's comfort setpoint.
76static constexpr uint8_t THERMOSTAT_ECO = 0x0E; ///< Heating temperature interface: the economy setpoint.
77static constexpr uint8_t SECURED = 0x0A; ///< Heating temperature interface: the secured setpoint.
78static constexpr uint8_t HEAT_PUMP_SETBACK = 0x0E; ///< Heat pump: the setback setpoint.
79static constexpr uint8_t HEAT_PUMP_ECO = 0x12; ///< Heat pump: the economy setpoint.
80static constexpr uint8_t HEAT_PUMP_HALTED = 0x13; ///< Heat pump: halted.
81} // namespace heating_alias
82
83/// @brief Main parameter that means "off": the unknown-position marker, which a command uses together
84/// with a functional parameter to switch a device off.
85static constexpr uint16_t HEATING_OFF_MP = 0xD400;
86
87/// @brief Sentinel for HeatingModeEntry::main_parameter: send the entity's target temperature, encoded
88/// over the device's range, as the main parameter.
89static constexpr uint16_t HEATING_MP_FROM_TARGET = 0xFFFF;
90
91/// @brief Values of functional parameter 9 on the heating temperature interface: the active mode.
92/// @details The heating / cooling bits (`4`, `8`) belong to a separate operating-mode command (main
93/// parameter `0xD400` with FP9 `4`, `8` or `0xC`); they are never combined into these values and
94/// this component sends no operating-mode command.
96static constexpr uint8_t OFF = 0x00; ///< Off.
97static constexpr uint8_t AUTO = 0x01; ///< The device follows its own schedule.
98static constexpr uint8_t MANUAL = 0x02; ///< The device follows the commanded setpoint.
99} // namespace heating_mode_flags
100
101/// @brief Values of functional parameter 16 on the heat pump: the on/off state.
102/// @details FP16 is the last functional parameter (the last bit of the second bitmap). FP15 reports the
103/// device's capabilities (`1` heating, `2` cooling, `4` away, `8` pool, `0x10` hot water) and
104/// FP16's other bits report its state; both are read-only here: no command selects cooling,
105/// and this component does not read FP15.
106namespace heating_fp16 {
107static constexpr uint16_t ON = 0x4000; ///< On: sent with a setpoint alias to select it.
108static constexpr uint16_t OFF = 0x8000; ///< Off: sent with HEATING_OFF_MP.
109} // namespace heating_fp16
110
111/// @brief Which functional parameter, if any, accompanies a TEMPERATURE_EXECUTE command.
112enum class HeatingFpKind : uint8_t {
113 NONE, ///< A main-parameter-only command.
114 FP9, ///< Functional parameter 9, one byte.
115 FP16, ///< Functional parameter 16, two bytes.
116};
117
118/// @brief A functional parameter and its value.
119struct HeatingFp {
120 HeatingFpKind kind{HeatingFpKind::NONE}; ///< Which parameter; NONE sends none.
121 uint16_t value{0}; ///< Its value (one byte for FP9, two for FP16).
122};
123
124/// @brief Number of main-parameter steps across the encoding range (the full position scale, 0xC800).
125static constexpr uint16_t HEATING_TEMPERATURE_MP_SPAN = 51200;
126
127/// @brief One HVAC mode the card offers and the wire value that selects it.
129 HeatingHvacMode hvac; ///< Mode shown on the card.
130 uint8_t wire; ///< PRIVATE_REGISTERS: mode register value. HEATING_LEVEL: level low byte.
131 ///< TEMPERATURE_EXECUTE: unused.
132 uint16_t main_parameter{0}; ///< TEMPERATURE_EXECUTE: the main parameter the mode sends, or
133 ///< HEATING_MP_FROM_TARGET.
134 HeatingFp fp{}; ///< TEMPERATURE_EXECUTE: the functional parameter the mode sends.
135};
136
137/// @brief One preset the card offers.
139 HeatingPresetId id; ///< Standard preset, or CUSTOM.
140 const char *custom_name; ///< Name of a CUSTOM preset; nullptr otherwise.
141 HeatingFunction write_fn; ///< PRIVATE_REGISTERS: register the preset writes. Unused for levels.
142 uint8_t wire; ///< Value written: register value, or the low byte of a level or alias.
143 bool commandable; ///< False for a preset the device only reports (secured).
144 bool observed; ///< False when no register reports it back (presence): the card then
145 ///< shows the last command.
146 HeatingFp fp{}; ///< TEMPERATURE_EXECUTE: the functional parameter sent with the alias.
147};
148
149/// @brief What a device-reported wire value shows on the card.
151 uint8_t wire; ///< Mode register value, or level low byte, as the device reports it.
152 HeatingHvacMode hvac; ///< HVAC mode shown.
153 int8_t preset; ///< Index into the profile's presets shown with it, or -1 for none.
154};
155
156/// @brief Companion entities and traits a profile has.
158 bool target_temperature; ///< The card has a setpoint.
159 bool current_temperature; ///< The card shows a measured temperature.
160 bool action; ///< The card shows heating / idle.
161 bool window_sensor; ///< A Window binary sensor exists (0x010E).
162 bool open_window_detection; ///< An open-window-detection switch exists (0x0106).
163 bool mode_observed; ///< A device reading decides the card's mode; false when the mode is the
164 ///< last command (nothing reads it back).
165};
166
167/// @brief One heater kind.
169 DeviceType type; ///< Node type.
170 uint8_t subtype; ///< Subtype (0 = the type's own row).
171 HeatingControlPath path; ///< How the heater is commanded and read.
172 HeatingProfileFeatures features; ///< What the card and its companions offer.
173 uint8_t mp_prefix; ///< High byte of a level or alias main parameter (0 for registers).
174 float visual_min_c; ///< Lowest target temperature the card offers (0 without a setpoint).
175 float visual_max_c; ///< Highest target temperature the card offers (0 without a setpoint).
176 HeatingFp setpoint_fp; ///< TEMPERATURE_EXECUTE: the functional parameter sent with a setpoint.
177 bool fixed_range; ///< TEMPERATURE_EXECUTE: the temperature range is fixed, so the YAML
178 ///< `min_temperature:` / `max_temperature:` do not apply.
179 const HeatingModeEntry *modes; ///< HVAC modes offered, with the value each writes.
180 size_t mode_count; ///< Number of entries in `modes`.
181 const HeatingPresetEntry *presets; ///< Presets offered.
182 size_t preset_count; ///< Number of entries in `presets`.
183 const HeatingObservedState *observed; ///< Device-reported value -> card state.
184 size_t observed_count; ///< Number of entries in `observed`.
185 const HeatingFunction *poll_registers; ///< PRIVATE_REGISTERS: registers every poll reads, in order.
186 size_t poll_register_count; ///< Number of entries in `poll_registers`.
187 const HeatingFunction *boot_registers; ///< PRIVATE_REGISTERS: registers read once until they have been read.
188 size_t boot_register_count; ///< Number of entries in `boot_registers`.
189};
190
191/// @brief Look up the heating profile for a (type, subtype).
192/// @param type Node type.
193/// @param subtype Subtype; one with no profile of its own falls back to the type's profile (subtype 0),
194/// the same rule as find_device_type_info().
195/// @return The profile, or nullptr when no climate entity drives this device.
196const HeatingProfile *find_heating_profile(DeviceType type, uint8_t subtype);
197
198/// @brief Does a climate entity drive this device over the private register protocol?
199/// @details The gate for every CMD_WRITE_PRIVATE frame the hub sends: a heater that takes the standard
200/// EXECUTE must never be sent a register write.
201/// @param type Node type.
202/// @param subtype Subtype.
203/// @return true when the profile's control path is HeatingControlPath::PRIVATE_REGISTERS.
204bool device_uses_heating_registers(DeviceType type, uint8_t subtype);
205
206/// @brief Does a climate entity drive this device with heating levels?
207/// @param type Node type.
208/// @param subtype Subtype.
209/// @return true when the profile's control path is HeatingControlPath::HEATING_LEVEL.
210bool device_uses_heating_levels(DeviceType type, uint8_t subtype);
211
212/// @brief Does a climate entity drive this device with a temperature or an alias in the main parameter?
213/// @param type Node type.
214/// @param subtype Subtype.
215/// @return true when the profile's control path is HeatingControlPath::TEMPERATURE_EXECUTE.
216bool device_uses_heating_temperature(DeviceType type, uint8_t subtype);
217
218/// @brief Encode a temperature as a main parameter over an encoding range.
219/// @details `round((celsius - min) / (max - min) * HEATING_TEMPERATURE_MP_SPAN)`, clamped to the span.
220/// @param celsius Temperature in degrees Celsius; must be finite.
221/// @param min_c Lower end of the device's encoding range.
222/// @param max_c Upper end of the device's encoding range; must exceed `min_c`.
223/// @param[out] main_parameter The encoded value.
224/// @return false for a non-finite temperature or an empty range; `main_parameter` is then untouched.
225bool encode_temperature_main_parameter(float celsius, float min_c, float max_c, uint16_t &main_parameter);
226
227/// @brief Decode a main parameter as a temperature over an encoding range (the inverse of
228/// encode_temperature_main_parameter()).
229/// @param main_parameter The 16-bit value from a status reply.
230/// @param min_c Lower end of the device's encoding range.
231/// @param max_c Upper end of the device's encoding range; must exceed `min_c`.
232/// @param[out] celsius The temperature.
233/// @return false when the value is above the span (an alias or a marker, not a temperature) or the
234/// range is empty.
235bool decode_temperature_main_parameter(uint16_t main_parameter, float min_c, float max_c, float &celsius);
236
237/// @brief Main parameter of a heating level, as it travels in an EXECUTE and in a status reply.
238/// @param level Level low byte (heating_level::*).
239/// @return HEATING_LEVEL_MP_PREFIX in the high byte, `level` in the low byte.
240constexpr uint16_t heating_level_main_parameter(uint8_t level) {
241 return static_cast<uint16_t>((HEATING_LEVEL_MP_PREFIX << BITS_PER_BYTE) | level);
242}
243
244/// @brief Main parameter of a prefixed value (a level or an alias).
245/// @param prefix High byte (HEATING_LEVEL_MP_PREFIX or HEATING_ALIAS_MP_PREFIX).
246/// @param low Low byte.
247/// @return The 16-bit main parameter.
248constexpr uint16_t heating_prefixed_main_parameter(uint8_t prefix, uint8_t low) {
249 return static_cast<uint16_t>((prefix << BITS_PER_BYTE) | low);
250}
251
252/// @brief Read a heating level out of a main parameter.
253/// @param main_parameter The 16-bit value from a status reply.
254/// @param[out] level Level low byte.
255/// @return true when the value is a heating level (high byte HEATING_LEVEL_MP_PREFIX).
256bool decode_heating_level(uint16_t main_parameter, uint8_t &level);
257
258/// @brief Find what a device-reported wire value shows on the card.
259/// @param profile The device's profile.
260/// @param wire Mode register value or level low byte.
261/// @return The matching entry, or nullptr when the value is not one the profile maps.
262const HeatingObservedState *find_observed_state(const HeatingProfile &profile, uint8_t wire);
263
264} // namespace home_io_control
265} // namespace esphome
Setpoint aliases (low byte of the main parameter, prefix HEATING_ALIAS_MP_PREFIX).
static constexpr uint8_t HEAT_PUMP_HALTED
Heat pump: halted.
static constexpr uint8_t COMFORT
The device's comfort setpoint.
static constexpr uint8_t HEAT_PUMP_ECO
Heat pump: the economy setpoint.
static constexpr uint8_t HEAT_PUMP_SETBACK
Heat pump: the setback setpoint.
static constexpr uint8_t THERMOSTAT_ECO
Heating temperature interface: the economy setpoint.
static constexpr uint8_t SECURED
Heating temperature interface: the secured setpoint.
Values of functional parameter 16 on the heat pump: the on/off state.
static constexpr uint16_t ON
On: sent with a setpoint alias to select it.
static constexpr uint16_t OFF
Off: sent with HEATING_OFF_MP.
Heating levels (low byte of the main parameter, prefix HEATING_LEVEL_MP_PREFIX).
static constexpr uint8_t COMFORT_MINUS_2
Comfort, two degrees lower.
static constexpr uint8_t COMFORT
Comfort.
static constexpr uint8_t FROST_PROTECTION
Frost protection.
static constexpr uint8_t SECURED
Secured; reported by the device, never commanded.
static constexpr uint8_t COMFORT_MINUS_1
Comfort, one degree lower.
Values of functional parameter 9 on the heating temperature interface: the active mode.
static constexpr uint8_t AUTO
The device follows its own schedule.
static constexpr uint8_t MANUAL
The device follows the commanded setpoint.
static constexpr uint8_t HEATING_LEVEL_MP_PREFIX
Main-parameter prefix byte of a heating level: the level occupies the low byte.
const HeatingObservedState * find_observed_state(const HeatingProfile &profile, uint8_t wire)
Find what a device-reported wire value shows on the card.
bool encode_temperature_main_parameter(float celsius, float min_c, float max_c, uint16_t &main_parameter)
Encode a temperature as a main parameter over an encoding range.
HeatingFpKind
Which functional parameter, if any, accompanies a TEMPERATURE_EXECUTE command.
@ FP9
Functional parameter 9, one byte.
@ FP16
Functional parameter 16, two bytes.
HeatingFunction
Registers and functions of a 2W heater, one row of the descriptor table each.
const HeatingProfile * find_heating_profile(DeviceType type, uint8_t subtype)
Look up the heating profile for a (type, subtype).
bool decode_temperature_main_parameter(uint16_t main_parameter, float min_c, float max_c, float &celsius)
Decode a main parameter as a temperature over an encoding range (the inverse of encode_temperature_ma...
HeatingPresetId
Presets a profile can offer. CUSTOM entries carry their own name.
@ CUSTOM
Named by HeatingPresetEntry::custom_name.
@ AWAY
Nobody is in the room (the frost-protection level on a level heater).
constexpr uint16_t heating_level_main_parameter(uint8_t level)
Main parameter of a heating level, as it travels in an EXECUTE and in a status reply.
DeviceType
IO-Homecontrol node types.
HeatingControlPath
How a heater is commanded and read.
@ TEMPERATURE_EXECUTE
Standard EXECUTE with a temperature or an alias as the main parameter; read with 0x03.
@ PRIVATE_REGISTERS
CMD_WRITE_PRIVATE register reads and writes (proto_heating.h).
@ HEATING_LEVEL
Standard EXECUTE with a heating level as the main parameter; read with 0x03.
static constexpr uint8_t HEATING_ALIAS_MP_PREFIX
Main-parameter prefix byte of a setpoint alias: the alias occupies the low byte.
static constexpr uint16_t HEATING_TEMPERATURE_MP_SPAN
Number of main-parameter steps across the encoding range (the full position scale,...
static constexpr uint16_t HEATING_OFF_MP
Main parameter that means "off": the unknown-position marker, which a command uses together with a fu...
bool device_uses_heating_levels(DeviceType type, uint8_t subtype)
Does a climate entity drive this device with heating levels?
bool decode_heating_level(uint16_t main_parameter, uint8_t &level)
Read a heating level out of a main parameter.
static constexpr uint16_t HEATING_MP_FROM_TARGET
Sentinel for HeatingModeEntry::main_parameter: send the entity's target temperature,...
bool device_uses_heating_registers(DeviceType type, uint8_t subtype)
Does a climate entity drive this device over the private register protocol?
bool device_uses_heating_temperature(DeviceType type, uint8_t subtype)
Does a climate entity drive this device with a temperature or an alias in the main parameter?
constexpr uint16_t heating_prefixed_main_parameter(uint8_t prefix, uint8_t low)
Main parameter of a prefixed value (a level or an alias).
HeatingHvacMode
HVAC modes a profile can offer.
IO-Homecontrol device-type model, capabilities and runtime device state.
Pure codec for the private register protocol of 2W heaters (CMD_WRITE_PRIVATE 0x20).
Fundamental IO-Homecontrol frame and crypto size constants.
A functional parameter and its value.
uint16_t value
Its value (one byte for FP9, two for FP16).
HeatingFpKind kind
Which parameter; NONE sends none.
One HVAC mode the card offers and the wire value that selects it.
HeatingFp fp
TEMPERATURE_EXECUTE: the functional parameter the mode sends.
uint16_t main_parameter
TEMPERATURE_EXECUTE: the main parameter the mode sends, or HEATING_MP_FROM_TARGET.
HeatingHvacMode hvac
Mode shown on the card.
uint8_t wire
PRIVATE_REGISTERS: mode register value.
What a device-reported wire value shows on the card.
uint8_t wire
Mode register value, or level low byte, as the device reports it.
int8_t preset
Index into the profile's presets shown with it, or -1 for none.
const char * custom_name
Name of a CUSTOM preset; nullptr otherwise.
HeatingFp fp
TEMPERATURE_EXECUTE: the functional parameter sent with the alias.
uint8_t wire
Value written: register value, or the low byte of a level or alias.
bool observed
False when no register reports it back (presence): the card then shows the last command.
HeatingPresetId id
Standard preset, or CUSTOM.
HeatingFunction write_fn
PRIVATE_REGISTERS: register the preset writes. Unused for levels.
bool commandable
False for a preset the device only reports (secured).
Companion entities and traits a profile has.
bool mode_observed
A device reading decides the card's mode; false when the mode is the last command (nothing reads it b...
bool open_window_detection
An open-window-detection switch exists (0x0106).
bool window_sensor
A Window binary sensor exists (0x010E).
bool current_temperature
The card shows a measured temperature.
HeatingProfileFeatures features
What the card and its companions offer.
bool fixed_range
TEMPERATURE_EXECUTE: the temperature range is fixed, so the YAML min_temperature: / max_temperature: ...
const HeatingModeEntry * modes
HVAC modes offered, with the value each writes.
uint8_t mp_prefix
High byte of a level or alias main parameter (0 for registers).
const HeatingFunction * boot_registers
PRIVATE_REGISTERS: registers read once until they have been read.
const HeatingObservedState * observed
Device-reported value -> card state.
size_t boot_register_count
Number of entries in boot_registers.
const HeatingFunction * poll_registers
PRIVATE_REGISTERS: registers every poll reads, in order.
size_t poll_register_count
Number of entries in poll_registers.
HeatingControlPath path
How the heater is commanded and read.
float visual_max_c
Highest target temperature the card offers (0 without a setpoint).
uint8_t subtype
Subtype (0 = the type's own row).
float visual_min_c
Lowest target temperature the card offers (0 without a setpoint).
size_t observed_count
Number of entries in observed.
size_t mode_count
Number of entries in modes.
const HeatingPresetEntry * presets
Presets offered.
size_t preset_count
Number of entries in presets.
HeatingFp setpoint_fp
TEMPERATURE_EXECUTE: the functional parameter sent with a setpoint.