Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
proto_frame.h File Reference

IO-Homecontrol 2W protocol definitions. More...

#include <cstdint>
#include <cstring>
#include <string>
Include dependency graph for proto_frame.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  esphome::home_io_control::IoFrame
 Parsed IO‑Homecontrol frame (CTRL0/1 + addresses + command + data). More...
struct  esphome::home_io_control::IoDevice
 Runtime state of a paired IO‑Homecontrol device. More...

Namespaces

namespace  esphome
namespace  esphome::home_io_control

Enumerations

enum class  esphome::home_io_control::DeviceType : uint8_t {
  esphome::home_io_control::UNKNOWN = 0x00 , esphome::home_io_control::VENETIAN_BLIND = 0x01 , esphome::home_io_control::ROLLER_SHUTTER = 0x02 , esphome::home_io_control::AWNING = 0x03 ,
  esphome::home_io_control::WINDOW_OPENER = 0x04 , esphome::home_io_control::GARAGE_OPENER = 0x05 , esphome::home_io_control::LIGHT = 0x06 , esphome::home_io_control::GATE_OPENER = 0x07 ,
  esphome::home_io_control::ROLLING_DOOR_OPENER = 0x08 , esphome::home_io_control::LOCK = 0x09 , esphome::home_io_control::BLIND = 0x0A , esphome::home_io_control::SCREEN = 0x0B ,
  esphome::home_io_control::BEACON = 0x0C , esphome::home_io_control::DUAL_SHUTTER = 0x0D , esphome::home_io_control::HEATING_TEMPERATURE_INTERFACE = 0x0E , esphome::home_io_control::ON_OFF_SWITCH = 0x0F ,
  esphome::home_io_control::HORIZONTAL_AWNING = 0x10 , esphome::home_io_control::EXTERNAL_VENETIAN_BLIND = 0x11 , esphome::home_io_control::LOUVRE_BLIND = 0x12 , esphome::home_io_control::CURTAIN_TRACK = 0x13 ,
  esphome::home_io_control::VENTILATION_POINT = 0x14 , esphome::home_io_control::EXTERIOR_HEATING = 0x15 , esphome::home_io_control::HEAT_PUMP = 0x16 , esphome::home_io_control::INTRUSION_ALARM = 0x17 ,
  esphome::home_io_control::SWINGING_SHUTTER = 0x18
}
 Device type identifiers reported by IO‑Homecontrol products. More...
enum class  esphome::home_io_control::DeviceCapabilityClass : uint8_t {
  esphome::home_io_control::UNKNOWN = 0x00 , esphome::home_io_control::COVER = 0x01 , esphome::home_io_control::LIGHT = 0x02 , esphome::home_io_control::SWITCH = 0x03 ,
  esphome::home_io_control::SENSOR = 0x04 , esphome::home_io_control::BEACON = 0x05 , esphome::home_io_control::CLIMATE = 0x06 , esphome::home_io_control::LOCK = 0x07
}
 High‑level capability class derived from DeviceType. More...

Functions

void esphome::home_io_control::init_frame (IoFrame &f, bool is_2w=true, bool start=false, bool end=false, bool low_power=false)
 Initialize an IoFrame header (ctrl0/ctrl1) with flags.
void esphome::home_io_control::set_dst (IoFrame &f, const uint8_t id[NODE_ID_SIZE])
 Set destination node ID.
void esphome::home_io_control::set_src (IoFrame &f, const uint8_t id[NODE_ID_SIZE])
 Set source node ID.
bool esphome::home_io_control::set_cmd (IoFrame &f, uint8_t cmd, const uint8_t *params=nullptr, uint8_t params_len=0)
 Set command and payload.
uint8_t esphome::home_io_control::frame_length (const IoFrame &f)
 Get total frame length from ctrl0.
bool esphome::home_io_control::is_start (const IoFrame &f)
 Check START flag.
bool esphome::home_io_control::is_end (const IoFrame &f)
 Check END flag.
uint8_t esphome::home_io_control::serialize (const IoFrame &f, uint8_t *buf, uint8_t buf_size)
 Serialize a parsed frame into a wire buffer (without CRC).
bool esphome::home_io_control::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 * esphome::home_io_control::device_type_name (DeviceType type)
 Convert a DeviceType to a lowercase string identifier.
DeviceCapabilityClass esphome::home_io_control::device_capability_class (DeviceType type)
 Map a raw IO‑Homecontrol type to the closest ESPHome/Home Assistant entity family.
const char * esphome::home_io_control::device_capability_class_name (DeviceType type)
 Get a human‑readable name for a capability class.
bool esphome::home_io_control::device_supports_position_control (DeviceType type)
 Does this device type support precise position control (0–100)?
bool esphome::home_io_control::device_supports_binary_control (DeviceType type)
 Does this device type support binary on/off control?
bool esphome::home_io_control::device_supports_status_requests (DeviceType type)
 Does this device type support status request commands (0x03)?
bool esphome::home_io_control::device_supports_tilt (DeviceType type)
 Does this device type support tilt (slat angle) control?
DeviceType esphome::home_io_control::decode_packed_device_type (uint8_t type_msb, uint8_t type_subtype)
 Decode a protocol-packed device type from two metadata bytes.
uint8_t esphome::home_io_control::decode_packed_device_subtype (uint8_t type_subtype)
 Decode a protocol-packed device subtype from the second metadata byte.
const char * esphome::home_io_control::device_operation_profile_name (DeviceType type)
 Human‑readable operation profile name for a device type.
bool esphome::home_io_control::hex_to_bytes (const std::string &hex, uint8_t *out, uint8_t len)
 Convert a hex string (e.g., "123ABC") to a byte array.
std::string esphome::home_io_control::node_id_to_string (const uint8_t id[NODE_ID_SIZE])
 Format a 3‑byte node ID as a 6‑character uppercase hex string.
bool esphome::home_io_control::default_inverted_for_type (DeviceType type)
 Determine whether a device type has inverted position mapping by default.
void esphome::home_io_control::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.
bool esphome::home_io_control::has_reached_target_position (float target, float position)
 Has the device reached its target within tolerance?
float esphome::home_io_control::decode_tilt_report (uint16_t tilt_raw)
 Decode tilt angle from raw 16‑bit value.
uint16_t esphome::home_io_control::crc_ccitt (const uint8_t *data, uint8_t len)
 CRC-CCITT used by the IO-Homecontrol protocol for frame validation.

Variables

static constexpr uint32_t esphome::home_io_control::FREQ_CH1 = 868250000
 The protocol uses 3 frequency channels in the 868 MHz ISM band.
static constexpr uint32_t esphome::home_io_control::FREQ_CH2 = 868950000
 Channel 2: 868.95 MHz (1W and 2W, TX channel).
static constexpr uint32_t esphome::home_io_control::FREQ_CH3 = 869850000
 Channel 3: 869.85 MHz (2W only).
static constexpr uint16_t esphome::home_io_control::LONG_PREAMBLE = 1024
 Preamble is a sequence of 0xAA bytes that precedes every frame.
static constexpr uint16_t esphome::home_io_control::SHORT_PREAMBLE = 8
 8 bytes for response/continuation frames
static constexpr uint16_t esphome::home_io_control::SX1262_AUTH_RESPONSE_PREAMBLE = 64
 SX1262-specific preamble for the outbound 0x3D challenge response.
static constexpr int32_t esphome::home_io_control::SX1262_EXCHANGE_RESPONSE_WAIT_SLICE_MS = 90
 SX1262-specific per-channel dwell while waiting for authenticated exchange responses.
static constexpr int32_t esphome::home_io_control::HOP_TIME_US = 2700
 Timing constants for frequency hopping and response waiting.
static constexpr int32_t esphome::home_io_control::RESPONSE_CHANNEL_WAIT_MS = 50
 Per-channel dwell while waiting for an exchange response.
static constexpr int32_t esphome::home_io_control::RESPONSE_WAIT_MS = 500
 Wait for response to non-start frame.
static constexpr int32_t esphome::home_io_control::RESPONSE_START_WAIT_MS = 300
 Wait for response to start frame (longer).
static constexpr int32_t esphome::home_io_control::RESPONSE_AUTH_WAIT_MS
 Wait for final response after challenge response.
static constexpr int32_t esphome::home_io_control::EXCHANGE_RETRY_DELAY_MS = 250
 Gap between retries within one HA command.
static constexpr uint8_t esphome::home_io_control::EXCHANGE_RETRY_COUNT = 3
 Attempts per command before reporting failure.
static constexpr int16_t esphome::home_io_control::LBT_RSSI_THRESHOLD_DBM = -90
 Listen-before-talk (LBT) parameters for ETSI EN 300 220 compliance.
static constexpr uint8_t esphome::home_io_control::LBT_MAX_RETRIES = 5
 Max carrier-sense attempts before TX anyway.
static constexpr uint8_t esphome::home_io_control::LBT_RETRY_DELAY_MS = 5
 Backoff between LBT checks (≥ 5ms per ETSI).
static constexpr uint8_t esphome::home_io_control::NODE_ID_SIZE = 3
 Device/node addresses are 3 bytes (e.g., "123ABC").
static constexpr uint8_t esphome::home_io_control::NODE_ID_STRING_SIZE = NODE_ID_SIZE * 2 + 1
 Uppercase hex node ID plus null terminator.
static constexpr uint8_t esphome::home_io_control::HMAC_SIZE = 6
 Authentication HMAC is 6 bytes (truncated AES output).
static constexpr uint8_t esphome::home_io_control::AES_KEY_SIZE = 16
 AES-128 key size.
static constexpr uint8_t esphome::home_io_control::AES_BLOCK_SIZE = 16
 AES block size.
static constexpr uint8_t esphome::home_io_control::IV_SIZE = 16
 Initialization vector size for AES.
static constexpr uint8_t esphome::home_io_control::IV_PADDING = 0x55
 Padding byte used in IV construction.
static constexpr uint8_t esphome::home_io_control::BITS_PER_BYTE = 8
 Number of bits in one protocol byte.
static constexpr uint8_t esphome::home_io_control::FRAME_MIN_SIZE = 9
 Minimum frame: CTRL0+CTRL1+DST(3)+SRC(3)+CMD(1).
static constexpr uint8_t esphome::home_io_control::FRAME_MAX_SIZE = 32
 Maximum frame size (9 header + 23 data).
static constexpr uint8_t esphome::home_io_control::FRAME_MAX_DATA_SIZE = 23
 Maximum data bytes after command ID.
static constexpr uint8_t esphome::home_io_control::CTRL0_END = 0x80
 Control byte 0 (CTRL0) bit definitions.
static constexpr uint8_t esphome::home_io_control::CTRL0_START = 0x40
 Bit 6: first frame in exchange (uses long preamble).
static constexpr uint8_t esphome::home_io_control::CTRL0_PROTOCOL_1W = 0x20
 Bit 5: 1=OneWay protocol, 0=TwoWay protocol.
static constexpr uint8_t esphome::home_io_control::CTRL0_LENGTH_MASK = 0x1F
 Bits [4:0]: frame length - 1.
static constexpr uint8_t esphome::home_io_control::CTRL1_LOW_POWER = 0x20
 Control byte 1 (CTRL1) bit definitions.
static constexpr uint8_t esphome::home_io_control::CMD_EXECUTE = 0x00
 Set position/open/close/stop — requires authentication.
static constexpr uint8_t esphome::home_io_control::CMD_PRIVATE = 0x03
 Get device status — no authentication needed.
static constexpr uint8_t esphome::home_io_control::CMD_PRIVATE_RESP = 0x04
 Response to 0x00 and 0x03 (contains position data).
static constexpr uint8_t esphome::home_io_control::CMD_DISCOVER_REQ = 0x28
 Broadcast discovery request.
static constexpr uint8_t esphome::home_io_control::CMD_DISCOVER_RESP = 0x29
 Device responds with its ID and type.
static constexpr uint8_t esphome::home_io_control::CMD_DISCOVER_SPE_REQ = 0x2A
 Discover sub-devices (e.g., light on garage door).
static constexpr uint8_t esphome::home_io_control::CMD_DISCOVER_SPE_RESP = 0x2B
 Sub-device response.
static constexpr uint8_t esphome::home_io_control::CMD_DISCOVER_CONFIRM = 0x2C
 Confirm discovery to device.
static constexpr uint8_t esphome::home_io_control::CMD_DISCOVER_CONFIRM_ACK = 0x2D
 Device acknowledges confirmation.
static constexpr uint8_t esphome::home_io_control::CMD_KEY_INIT = 0x31
 Initiate key transfer to device.
static constexpr uint8_t esphome::home_io_control::CMD_KEY_TRANSFER = 0x32
 Send encrypted system key to device.
static constexpr uint8_t esphome::home_io_control::CMD_KEY_CONFIRM = 0x33
 Device confirms key was received.
static constexpr uint8_t esphome::home_io_control::CMD_CHALLENGE_REQ = 0x3C
 Device sends 6-byte random challenge.
static constexpr uint8_t esphome::home_io_control::CMD_CHALLENGE_RESP = 0x3D
 Controller responds with HMAC proof.
static constexpr uint8_t esphome::home_io_control::CMD_GET_NAME = 0x50
 Request device name.
static constexpr uint8_t esphome::home_io_control::CMD_GET_NAME_RESP = 0x51
 Device name response.
static constexpr uint8_t esphome::home_io_control::CMD_GET_INFO2 = 0x56
 Request device type/model info.
static constexpr uint8_t esphome::home_io_control::CMD_GET_INFO2_RESP = 0x57
 Device type/model response.
static constexpr uint8_t esphome::home_io_control::CMD_SET_CONFIG1 = 0x6F
 Configure device to auto-send status updates.
static constexpr uint8_t esphome::home_io_control::CMD_SET_CONFIG1_RESP = 0x70
 Config response.
static constexpr uint8_t esphome::home_io_control::CMD_STATUS_UPDATE = 0x71
 Device-initiated status update (needs auth).
static constexpr uint8_t esphome::home_io_control::CMD_STATUS_UPDATE_RESP = 0x72
 Acknowledge status update.
static constexpr uint8_t esphome::home_io_control::CMD_ERROR_RESP = 0xFE
 Error response to any command.
static constexpr uint8_t esphome::home_io_control::POS_STOP = 0xD2
 Position values in the IO protocol.
static constexpr uint8_t esphome::home_io_control::POS_UNKNOWN = 0xD4
 Position unknown.
static constexpr uint8_t esphome::home_io_control::POS_FAVORITE = 0xD8
 Move to favorite/"My" position.
static constexpr uint16_t esphome::home_io_control::STATUS_POS_MAX = 0xC800
 In status responses, position is encoded as a 16-bit value where 0x0000 = fully open (0%) and 0xC800 = fully closed (100%).
static constexpr uint16_t esphome::home_io_control::STATUS_POS_TOLERANCE_RAW = 100
 Target-reached tolerance expressed in raw IO-homecontrol position units.
static constexpr uint8_t esphome::home_io_control::STATUS_STOPPED = 0x01
 Status byte flags in CMD_PRIVATE_RESP and CMD_STATUS_UPDATE.
static constexpr uint8_t esphome::home_io_control::STATUS_EXPECTED = 0x80
 Byte 1 bit 7: device will send auto status update.
static constexpr uint8_t esphome::home_io_control::STATUS_TILT_SELECTOR = 0x20
 Extended status payload marker for tilt-capable devices.
static constexpr uint8_t esphome::home_io_control::DEVICE_METADATA_SIZE = 2
 Packed device metadata uses two bytes where the high 8 bits carry the upper type bits and the low byte carries both the remaining type bits and the 6-bit manufacturer subtype.
static constexpr uint8_t esphome::home_io_control::DEVICE_TYPE_LOW_BITS_SHIFT = 2
static constexpr uint8_t esphome::home_io_control::DEVICE_TYPE_HIGH_BITS_SHIFT = 6
static constexpr uint8_t esphome::home_io_control::DEVICE_SUBTYPE_MASK = 0x3F
static constexpr uint8_t esphome::home_io_control::TRANSFER_KEY [AES_KEY_SIZE]
 The transfer key is a hardcoded key used ONLY during pairing to obfuscate the system key during over-the-air transfer.
static constexpr uint16_t esphome::home_io_control::CRC_POLYNOMIAL_REVERSED = 0x8408
 Reversed CRC-CCITT polynomial used by IO-homecontrol.
static constexpr uint16_t esphome::home_io_control::CRC_LSB_MASK = 0x0001
 Least-significant-bit mask for reflected CRC update.
static constexpr uint8_t esphome::home_io_control::BROADCAST_DISCOVER [NODE_ID_SIZE] = {0x00, 0x00, 0x3B}
 Broadcast address for device discovery (0x00003B).
static constexpr float esphome::home_io_control::UNKNOWN_POSITION = 212.0F
 Sentinel value meaning "position is not known yet".
static constexpr uint8_t esphome::home_io_control::DEVICE_NAME_BUFFER_SIZE = 32
 Device name storage including null terminator.

Detailed Description

IO-Homecontrol 2W protocol definitions.

IO-Homecontrol is a proprietary wireless protocol used by Somfy, Velux, and other manufacturers for controlling shutters, awnings, blinds, and similar devices. "2W" means two-way: the controller sends commands and receives status feedback.

The protocol uses FSK modulation at 868 MHz with frequency hopping across 3 channels. Communication is encrypted with AES-128 and authenticated with a 6-byte HMAC. Each installation has a unique 16-byte "system key" shared between controller and devices.

Definition in file proto_frame.h.