Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
esphome::home_io_control::pairing_responder Namespace Reference

Classes

struct  ResponderContext
 Context for one key-extraction arm cycle. More...

Enumerations

enum class  ResponderState : uint8_t {
  DISARMED , ARMED_IDLE , SENT_DISCOVER_RESP , SENT_CONFIRM_ACK ,
  SENT_CHALLENGE , EXTRACTED
}
 State machine for the device-role key-extraction responder. More...

Functions

const char * responder_stage_name (ResponderState state)
 Get a short, log/telemetry-friendly name for a responder state.
bool on_discover_request (ResponderContext &ctx)
 Decide how to react to an inbound CMD_DISCOVER_REQ (0x28) while armed.
bool on_discover_confirm (ResponderContext &ctx)
 Decide how to react to an inbound CMD_DISCOVER_CONFIRM (0x2C) addressed to our throwaway ID.
bool on_key_init (ResponderContext &ctx, const uint8_t challenge[HMAC_SIZE], const uint8_t hub_node_id[NODE_ID_SIZE])
 Decide how to react to an inbound CMD_KEY_INIT (0x31) addressed to our throwaway ID.
bool on_key_transfer (ResponderContext &ctx, const uint8_t transfer_payload[AES_KEY_SIZE])
 Decide how to react to an inbound CMD_KEY_TRANSFER (0x32) while armed.

Enumeration Type Documentation

◆ ResponderState

State machine for the device-role key-extraction responder.

Enumerator
DISARMED 

Not armed; 0x28/0x2C/0x31/0x32 traffic is ignored.

ARMED_IDLE 

Armed, listening for a discovery request (0x28).

SENT_DISCOVER_RESP 

Replied to discovery (0x29); waiting for discovery-confirm (0x2C) or key-init (0x31).

SENT_CONFIRM_ACK 

Acknowledged discovery-confirm (0x2D); waiting for key-init (0x31).

SENT_CHALLENGE 

Replied to key-init with our challenge (0x3C); waiting for key-transfer (0x32).

EXTRACTED 

System key recovered from a valid 0x32; the hub disarms immediately after.

Definition at line 29 of file pairing_responder.h.

Function Documentation

◆ on_discover_confirm()

bool esphome::home_io_control::pairing_responder::on_discover_confirm ( ResponderContext & ctx)

Decide how to react to an inbound CMD_DISCOVER_CONFIRM (0x2C) addressed to our throwaway ID.

A hub sends 0x2C directly to a device it just discovered and, in general, will not proceed to the key exchange until that device answers with CMD_DISCOVER_CONFIRM_ACK (0x2D) — the step between discovery and key-init in a real pairing (tests/corpus/captures/velux_kux100/pairing_full.yaml, and this project's own responder answering a real hub's 0x2C in tests/corpus/captures/issues/issue_45_velux_kig300_key_extraction_success.yaml — the fixed, successful retest of issue_45_velux_kig300_key_extraction_stall.yaml's stalled session, where that same hub's 0x2C previously went unanswered). Hub strictness varies: some retry 0x2C indefinitely without it, others eventually send 0x31 anyway, which is why on_key_init() also accepts a key-init straight from SENT_DISCOVER_RESP.

Valid from SENT_DISCOVER_RESP (first confirm) and SENT_CONFIRM_ACK (a hub retry after missing our 0x2D — resend the same bare ack, nothing is regenerated). No-op from any other state: like on_discover_request(), an early-phase frame must never pull an exchange already past this point back down.

Parameters
ctxResponder context (mutated: state only).
Returns
true if the caller should build and send a CMD_DISCOVER_CONFIRM_ACK (0x2D) reply.

Definition at line 40 of file pairing_responder.cpp.

◆ on_discover_request()

bool esphome::home_io_control::pairing_responder::on_discover_request ( ResponderContext & ctx)

Decide how to react to an inbound CMD_DISCOVER_REQ (0x28) while armed.

Valid from ARMED_IDLE (first response — replies using the throwaway ID/advertised type already stored in ctx by the caller at arm time) and SENT_DISCOVER_RESP (a hub retry — resend the same values without regenerating anything). No-op from any later state: a discovery request must never re-arm or restart an exchange already past this phase. That silence is what a real device does too — in tests/corpus/captures/velux_kux100/pairing_full.yaml the hub re-broadcasts 0x28 right after the confirm-ack and the device pointedly does not answer, before the hub moves on to the key exchange. A second, independent hub does the same thing in tests/corpus/captures/issues/issue_45_velux_kig300_key_extraction_success.yaml (re-broadcast omitted from that capture's frame list as redundant, per its own notes).

Parameters
ctxResponder context (mutated: state only).
Returns
true if the caller should build and send a CMD_DISCOVER_RESP (0x29) reply.

Definition at line 33 of file pairing_responder.cpp.

◆ on_key_init()

bool esphome::home_io_control::pairing_responder::on_key_init ( ResponderContext & ctx,
const uint8_t challenge[HMAC_SIZE],
const uint8_t hub_node_id[NODE_ID_SIZE] )

Decide how to react to an inbound CMD_KEY_INIT (0x31) addressed to our throwaway ID.

Valid from SENT_DISCOVER_RESP and SENT_CONFIRM_ACK (first key-init: stores challenge and hub_node_id, then advances — accepted from both because not every hub waits for our 0x2D, see on_discover_confirm()) and SENT_CHALLENGE (a hub retry after missing our 0x3C — challenge is discarded and the previously-stored one is reused instead, since the hub's eventual 0x32 must be decrypted with the exact challenge bytes it actually received; SX1262's slow TX→RX turnaround makes this retry path the expected common case, not an edge case). No-op from any other state.

Parameters
ctxResponder context (mutated: state, and on the first call challenge/hub_node_id).
challengeCaller-generated 6 random bytes (crypto::generate_challenge()); only consumed on the first 0x31 of an attempt.
hub_node_idThe inbound frame's src (the foreign hub's real node ID).
Returns
true if the caller should build and send a CMD_CHALLENGE_REQ (0x3C) reply using ctx.challenge (not challenge directly — they may differ on a retry).

Definition at line 47 of file pairing_responder.cpp.

◆ on_key_transfer()

bool esphome::home_io_control::pairing_responder::on_key_transfer ( ResponderContext & ctx,
const uint8_t transfer_payload[AES_KEY_SIZE] )

Decide how to react to an inbound CMD_KEY_TRANSFER (0x32) while armed.

Valid only from SENT_CHALLENGE. Recovers the system key from transfer_payload using ctx.challenge via recover_system_key_from_transfer() (proto_commands.h) and, on success, transitions to EXTRACTED. No-op (state unchanged) from any other state, or if decoding fails.

Parameters
ctxResponder context (mutated: state, and on success recovered_key).
transfer_payload16-byte CMD_KEY_TRANSFER payload (frame.data).
Returns
true if the key was recovered and the caller should build and send a CMD_KEY_CONFIRM (0x33) reply and report ctx.recovered_key to the user.

Definition at line 61 of file pairing_responder.cpp.

Here is the call graph for this function:

◆ responder_stage_name()

const char * esphome::home_io_control::pairing_responder::responder_stage_name ( ResponderState state)

Get a short, log/telemetry-friendly name for a responder state.

Parameters
stateResponder state.
Returns
Null-terminated lowercase string such as "sent_challenge".

Definition at line 15 of file pairing_responder.cpp.