15constexpr int HEX_ALPHA_OFFSET = 10;
20 if (ch >=
'0' && ch <=
'9')
22 ch =
static_cast<char>(std::toupper(
static_cast<unsigned char>(ch)));
23 if (ch >=
'A' && ch <=
'F')
24 return HEX_ALPHA_OFFSET + (ch -
'A');
28bool hex_to_bytes(
const std::string &hex, uint8_t *out, uint8_t len) {
33 if (hex.length() !=
static_cast<size_t>(len) * 2)
36 for (uint8_t i = 0; i < len; i++) {
39 if (high < 0 || low < 0)
41 out[i] =
static_cast<uint8_t
>((high << 4) | low);
49 snprintf(buf,
sizeof(buf),
"%02X%02X%02X",
id[0],
id[1],
id[2]);
50 return std::string(buf);
70 }
else if (is_stopped && current_valid) {
74 target = decoded_current;
80 position = decoded_current;
81 }
else if (is_stopped && target_valid) {
92 return std::fabs(target - position) <= tolerance;
106 uint16_t crc = 0x0000;
107 for (uint8_t i = 0; i < len; i++) {
116 memset(&f, 0,
sizeof(
IoFrame));
130bool set_cmd(
IoFrame &f, uint8_t cmd,
const uint8_t *params, uint8_t params_len) {
135 if (params !=
nullptr && params_len > 0)
136 memcpy(f.
data, params, params_len);
165 buf[offset++] = f.
ctrl0;
166 buf[offset++] = f.
ctrl1;
171 buf[offset++] = f.
cmd;
182 memset(&f, 0,
sizeof(
IoFrame));
184 f.
ctrl0 = buf[offset++];
185 f.
ctrl1 = buf[offset++];
199 if (offset >= buf_len)
201 f.
cmd = buf[offset++];
216 return "venetian_blind";
218 return "roller_shutter";
224 return "window_opener";
226 return "garage_opener";
230 return "gate_opener";
232 return "rolling_door_opener";
236 return "dual_shutter";
238 return "on_off_switch";
240 return "horizontal_awning";
242 return "external_venetian_blind";
244 return "louvre_blind";
246 return "curtain_track";
248 return "swinging_shutter";
254 return "heating_temperature_interface";
256 return "ventilation_point";
258 return "exterior_heating";
262 return "intrusion_alarm";
364 return "binary_on_off";
bool set_cmd(IoFrame &f, uint8_t cmd, const uint8_t *params, uint8_t params_len)
Set command and payload.
const char * device_operation_profile_name(DeviceType type)
Human‑readable operation profile name for a device type.
static constexpr uint8_t BITS_PER_BYTE
Number of bits in one protocol byte.
static constexpr float UNKNOWN_POSITION
Sentinel value meaning "position is not known yet".
static constexpr uint8_t NODE_ID_SIZE
Device/node addresses are 3 bytes (e.g., "123ABC").
static constexpr uint16_t STATUS_POS_MAX
In status responses, position is encoded as a 16-bit value where 0x0000 = fully open (0%) and 0xC800 ...
static constexpr uint8_t FRAME_MIN_SIZE
Minimum frame: CTRL0+CTRL1+DST(3)+SRC(3)+CMD(1).
static constexpr uint8_t DEVICE_TYPE_HIGH_BITS_SHIFT
DeviceType
Device type identifiers reported by IO‑Homecontrol products.
@ LOUVRE_BLIND
Louvre blind.
@ GATE_OPENER
Gate opener.
@ DUAL_SHUTTER
Dual-section shutter.
@ HEATING_TEMPERATURE_INTERFACE
Heating temperature interface.
@ BEACON
Beacon (unpaired/announcement).
@ EXTERNAL_VENETIAN_BLIND
External venetian blind.
@ UNKNOWN
Unknown/unspecified device.
@ INTRUSION_ALARM
Intrusion alarm.
@ WINDOW_OPENER
Window opening actuator.
@ SWINGING_SHUTTER
Swinging shutter.
@ HORIZONTAL_AWNING
Horizontal awning (open/close inverted).
@ ROLLING_DOOR_OPENER
Rolling door opener.
@ ON_OFF_SWITCH
Generic on/off switch.
@ SCREEN
Insect/privacy screen.
@ EXTERIOR_HEATING
Exterior heating.
@ VENTILATION_POINT
Ventilation point.
@ VENETIAN_BLIND
Venetian blind.
@ ROLLER_SHUTTER
Roller shutter.
@ CURTAIN_TRACK
Curtain track.
@ GARAGE_OPENER
Garage door opener.
static constexpr uint8_t CTRL0_END
Control byte 0 (CTRL0) bit definitions.
static constexpr uint8_t FRAME_MAX_DATA_SIZE
Maximum data bytes after command ID.
static constexpr uint16_t STATUS_POS_TOLERANCE_RAW
Target-reached tolerance expressed in raw IO-homecontrol position units.
DeviceCapabilityClass device_capability_class(DeviceType type)
Map a raw IO‑Homecontrol type to the closest ESPHome/Home Assistant entity family.
bool default_inverted_for_type(DeviceType type)
Determine whether a device type has inverted position mapping by default.
bool is_start(const IoFrame &f)
Check START flag.
bool device_supports_position_control(DeviceType type)
Does this device type support precise position control (0–100)?
static constexpr uint8_t CTRL0_PROTOCOL_1W
Bit 5: 1=OneWay protocol, 0=TwoWay protocol.
const char * device_type_name(DeviceType type)
Convert a DeviceType to a lowercase string identifier.
static constexpr uint8_t CTRL0_START
Bit 6: first frame in exchange (uses long preamble).
bool device_supports_binary_control(DeviceType type)
Does this device type support binary on/off control?
uint16_t crc_ccitt(const uint8_t *data, uint8_t len)
CRC-CCITT used by the IO-Homecontrol protocol for frame validation.
void init_frame(IoFrame &f, bool is_2w, bool start, bool end, bool low_power)
Initialize an IoFrame header (ctrl0/ctrl1) with flags.
float decode_tilt_report(uint16_t tilt_raw)
Decode tilt angle from raw 16‑bit value.
uint8_t frame_length(const IoFrame &f)
Get total frame length from ctrl0.
static constexpr uint8_t FRAME_MAX_SIZE
Maximum frame size (9 header + 23 data).
void set_dst(IoFrame &f, const uint8_t id[NODE_ID_SIZE])
Set destination node ID.
DeviceType decode_packed_device_type(uint8_t type_msb, uint8_t type_subtype)
Decode a protocol-packed device type from two metadata bytes.
static constexpr uint8_t NODE_ID_STRING_SIZE
Uppercase hex node ID plus null terminator.
bool is_end(const IoFrame &f)
Check END flag.
static constexpr uint16_t CRC_LSB_MASK
Least-significant-bit mask for reflected CRC update.
bool parse(const uint8_t *buf, uint8_t buf_len, IoFrame &f)
Parse a wire buffer into a parsed IoFrame (validates length and CTRL0).
const char * device_capability_class_name(DeviceType type)
Get a human‑readable name for a capability class.
std::string node_id_to_string(const uint8_t id[NODE_ID_SIZE])
Format a 3‑byte node ID as a 6‑character uppercase hex string.
uint8_t decode_packed_device_subtype(uint8_t type_subtype)
Decode a protocol-packed device subtype from the second metadata byte.
bool device_supports_status_requests(DeviceType type)
Does this device type support status request commands (0x03)?
static constexpr uint8_t DEVICE_SUBTYPE_MASK
static constexpr uint8_t CTRL0_LENGTH_MASK
Bits [4:0]: frame length - 1.
bool has_reached_target_position(float target, float position)
Has the device reached its target within tolerance?
DeviceCapabilityClass
High‑level capability class derived from DeviceType.
@ CLIMATE
Climate device (heating/cooling).
@ SWITCH
Binary on/off switch.
@ UNKNOWN
Unknown capability.
@ COVER
Position‑controlled cover (shutter/blind/awning).
@ LIGHT
Binary on/off light.
static constexpr uint8_t DEVICE_TYPE_LOW_BITS_SHIFT
static int hex_nibble(char ch)
void decode_position_report(uint16_t target_raw, uint16_t current_raw, bool is_stopped, float &target, float &position)
Decode target/current position values from a status frame.
static constexpr uint8_t CTRL1_LOW_POWER
Control byte 1 (CTRL1) bit definitions.
static constexpr uint16_t CRC_POLYNOMIAL_REVERSED
Reversed CRC-CCITT polynomial used by IO-homecontrol.
bool device_supports_tilt(DeviceType type)
Does this device type support tilt (slat angle) control?
bool hex_to_bytes(const std::string &hex, uint8_t *out, uint8_t len)
Convert a hex string (e.g., "123ABC") to a byte array.
uint8_t serialize(const IoFrame &f, uint8_t *buf, uint8_t buf_size)
Serialize a parsed frame into a wire buffer (without CRC).
void set_src(IoFrame &f, const uint8_t id[NODE_ID_SIZE])
Set source node ID.
IO-Homecontrol 2W protocol definitions.
Parsed IO‑Homecontrol frame (CTRL0/1 + addresses + command + data).
uint8_t data[FRAME_MAX_DATA_SIZE]
Command parameters (0–23 bytes).
uint8_t ctrl0
Control byte 0: flags + length.
uint8_t src[NODE_ID_SIZE]
Source node ID (3 bytes).
uint8_t dst[NODE_ID_SIZE]
Destination node ID (3 bytes).
uint8_t data_len
Actual length of data.
uint8_t ctrl1
Control byte 1: low power, beacon, etc.