18constexpr uint8_t POSITION_PERCENT_MAX = 100;
20constexpr uint8_t EXECUTE_USER_ORIGINATOR = 0x01;
22constexpr uint8_t EXECUTE_POSITION_ACEI = 0x67;
24constexpr size_t EXECUTE_PAYLOAD_SIZE = 8;
26constexpr uint8_t EXECUTE_POSITION_LAYOUT_FLAG = 0x80;
28constexpr uint8_t EXECUTE_POSITION_PROFILE = 0x06;
30constexpr uint8_t EXECUTE_TILT_ACEI = 0xE7;
32constexpr size_t EXECUTE_SPECIAL_PAYLOAD_SIZE = 6;
34constexpr uint8_t PRIVATE_GET_POSITION_STATUS = 0x03;
36constexpr uint8_t STATUS_UPDATE_ACK_PAYLOAD[] = {0x05, 0x00};
38constexpr uint8_t SET_CONFIG1_STATUS_BROADCAST_PAYLOAD[] = {0xE0, 0x10, 0x0A, 0x08, 0x00};
49 if (position <= POSITION_PERCENT_MAX) {
51 const uint8_t payload[EXECUTE_PAYLOAD_SIZE] = {
52 EXECUTE_USER_ORIGINATOR, EXECUTE_POSITION_ACEI,
static_cast<uint8_t
>(2 * position), 0x00,
53 EXECUTE_POSITION_LAYOUT_FLAG,
POS_FAVORITE, EXECUTE_POSITION_PROFILE, 0x00};
58 const uint8_t payload[EXECUTE_SPECIAL_PAYLOAD_SIZE] = {
59 EXECUTE_USER_ORIGINATOR, EXECUTE_POSITION_ACEI, position, 0x00, 0x00, 0x00};
70 uint8_t d[3] = {PRIVATE_GET_POSITION_STATUS, 0x00, 0x00};
80 auto const tilt_value =
81 static_cast<uint16_t
>((POSITION_PERCENT_MAX - tilt_percent) *
STATUS_POS_MAX / POSITION_PERCENT_MAX);
82 uint8_t d[EXECUTE_PAYLOAD_SIZE] = {EXECUTE_USER_ORIGINATOR,
88 static_cast<uint8_t
>(tilt_value),
149 const IoFrame &origin,
const uint8_t *key) {
156 frame_data[0] = origin.
cmd;
183 sizeof(SET_CONFIG1_STATUS_BROADCAST_PAYLOAD));
void generate_challenge(uint8_t out[HMAC_SIZE])
Generate 6 random bytes for a challenge using the ESP32 hardware RNG.
bool create_hmac(const uint8_t *data, uint8_t len, const uint8_t challenge[HMAC_SIZE], const uint8_t key[AES_KEY_SIZE], uint8_t hmac[HMAC_SIZE])
Create a 6-byte HMAC for authentication (proprietary IO-Homecontrol scheme).
bool crypt_key(const uint8_t *data, uint8_t len, const uint8_t challenge[HMAC_SIZE], const uint8_t in[AES_KEY_SIZE], uint8_t out[AES_KEY_SIZE])
Encrypt or decrypt a system key during pairing.
bool set_cmd(IoFrame &f, uint8_t cmd, const uint8_t *params, uint8_t params_len)
Set command and payload.
static constexpr uint8_t BITS_PER_BYTE
Number of bits in one protocol byte.
static constexpr uint8_t CMD_DISCOVER_REQ
Broadcast discovery request.
static constexpr uint8_t CMD_SET_CONFIG1
Configure device to auto-send status updates.
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 CMD_KEY_TRANSFER
Send encrypted system key to device.
bool create_get_status(IoFrame &f, const uint8_t *own, const uint8_t *dst)
Build a get-status request (0x03). The device responds with its current position.
static constexpr uint8_t POS_UNKNOWN
Position unknown.
bool create_execute(IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint8_t position)
Build an execute command (0x00) to control a device.
static constexpr uint8_t HMAC_SIZE
Authentication HMAC is 6 bytes (truncated AES output).
void init_frame(IoFrame &f, bool is_2w, bool start, bool end, bool low_power)
Initialize an IoFrame header (ctrl0/ctrl1) with flags.
static constexpr uint8_t FRAME_MAX_SIZE
Maximum frame size (9 header + 23 data).
static constexpr uint8_t CMD_KEY_INIT
Initiate key transfer to device.
bool create_execute_tilt(IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint8_t tilt_percent)
Build a tilt execute command (0x00) for devices that support slat angle control.
bool create_set_config1(IoFrame &f, const uint8_t *own, const uint8_t *dst)
Build a set-config command (0x6F) to tell the device to automatically send status updates when contro...
bool create_challenge_resp(IoFrame &f, const uint8_t *dst, const uint8_t *src, const uint8_t challenge[HMAC_SIZE], const IoFrame &origin, const uint8_t *key)
Build a challenge response (0x3D) proving we know the system key.
void set_dst(IoFrame &f, const uint8_t id[NODE_ID_SIZE])
Set destination node ID.
bool create_key_init(IoFrame &f, const uint8_t *own, const uint8_t *dst)
Build a key-init request (0x31) to start the pairing key exchange with a discovered device.
static constexpr uint8_t CMD_EXECUTE
Set position/open/close/stop — requires authentication.
static constexpr uint8_t CMD_CHALLENGE_REQ
Device sends 6-byte random challenge.
static constexpr uint8_t CMD_STATUS_UPDATE_RESP
Acknowledge status update.
bool create_challenge_req(IoFrame &f, const uint8_t *dst, const uint8_t *src)
Build a challenge request (0x3C) containing 6 random bytes.
bool create_discover(IoFrame &f, const uint8_t *own)
Build a discovery broadcast (0x28).
static constexpr uint8_t POS_FAVORITE
Move to favorite/"My" position.
static constexpr uint8_t BROADCAST_DISCOVER[NODE_ID_SIZE]
Broadcast address for device discovery (0x00003B).
static constexpr uint8_t STATUS_TILT_SELECTOR
Extended status payload marker for tilt-capable devices.
static constexpr uint8_t CMD_PRIVATE
Get device status — no authentication needed.
static constexpr uint8_t AES_KEY_SIZE
AES-128 key size.
static constexpr uint8_t CMD_CHALLENGE_RESP
Controller responds with HMAC proof.
bool create_get_status_tilt(IoFrame &f, const uint8_t *own, const uint8_t *dst)
Build a tilt-aware get-status request (0x03) that returns the extended 16-byte tilt payload.
bool create_status_update_resp(IoFrame &f, const uint8_t *own, const uint8_t *dst)
Build a status-update acknowledgment (0x72).
bool create_key_transfer(IoFrame &f, IoFrame &old_frame, const uint8_t *dst, const uint8_t *src, const uint8_t key[AES_KEY_SIZE], const uint8_t challenge[HMAC_SIZE])
Build a key-transfer frame (0x32) containing the system key encrypted with the transfer key.
void set_src(IoFrame &f, const uint8_t id[NODE_ID_SIZE])
Set source node ID.
Command builders for the IO‑Homecontrol protocol.
Cryptographic helpers for the IO‑Homecontrol protocol.
Parsed IO‑Homecontrol frame (CTRL0/1 + addresses + command + data).
uint8_t data[FRAME_MAX_DATA_SIZE]
Command parameters (0–23 bytes).
uint8_t data_len
Actual length of data.