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

Owns and drives all three phases of the IO-Homecontrol device pairing flow. More...

#include <pairing_engine.h>

Collaboration diagram for esphome::home_io_control::PairingEngine:

Public Member Functions

 PairingEngine (RadioDriver **radio_ptr, const uint8_t *node_id, const uint8_t *system_key, const TuningConfig *tuning, ExchangeEngine &engine, DeviceRegistry &registry, PairingTelemetry &telemetry)
 Construct the engine with all required collaborators.
 PairingEngine (const PairingEngine &)=delete
 Non-copyable — stores double-pointer and references into hub member addresses.
PairingEngineoperator= (const PairingEngine &)=delete
bool discover_and_pair ()
 Discover and pair a device currently in pairing mode (three-phase orchestrator).

Static Public Member Functions

static void parse_device_from_discovery (const IoFrame &frame, IoDevice &device, std::string &device_id)
 Extract node ID, device type, and subtype from a CMD_DISCOVER_RESP frame.

Protected Member Functions

decisions::PairingDiscoveryDisposition run_discovery_phase_ (pairing::PairingContext &context)
 Phase 1: broadcast discovery command(s) and wait for a device response (0x29).
bool run_key_exchange_phase_ (pairing::PairingContext &context)
 Phase 2: authenticated key exchange (0x31 → 0x3C → 0x32 → 0x33).
bool finalize_pairing_configuration_ (pairing::PairingContext &context)
 Phase 3: send SetConfig1 (0x6F) to enable automatic status updates; best-effort.
decisions::PairingDiscoveryDisposition wait_for_discovery_response_ (uint32_t timeout_ms, RadioRxPacket &packet, IoFrame &response_frame)
 Wait for a discovery response (0x29) within timeout_ms with per-chip frequency hopping.
bool wait_for_key_challenge_ (uint32_t timeout_ms, RadioRxPacket &packet, IoFrame &challenge_frame, const uint8_t device_node_id[NODE_ID_SIZE])
 Wait for a key-challenge (0x3C) or direct key-confirm (0x33) from the target device.
bool wait_for_key_confirm_ (pairing::PairingContext &context)
 Transmit the 0x32 key transfer and wait for the 0x33 key confirm with retry.

Detailed Description

Owns and drives all three phases of the IO-Homecontrol device pairing flow.

Constructed once by IOHomeControlComponent; collaborators (radio, exchange engine, device registry) are injected as pointers/references so the engine never outlives them.

Definition at line 60 of file pairing_engine.h.

Constructor & Destructor Documentation

◆ PairingEngine() [1/2]

esphome::home_io_control::PairingEngine::PairingEngine ( RadioDriver ** radio_ptr,
const uint8_t * node_id,
const uint8_t * system_key,
const TuningConfig * tuning,
ExchangeEngine & engine,
DeviceRegistry & registry,
PairingTelemetry & telemetry )

Construct the engine with all required collaborators.

Parameters
radio_ptrDouble pointer into the hub's radio_ member — survives driver replacement in tests.
node_idController 3-byte node ID buffer, owned by the hub.
system_key16-byte AES system key buffer, owned by the hub.
tuningTuning configuration, owned by the hub.
engineShared exchange engine for transmit/receive operations.
registryDevice registry where paired devices are permanently registered.
telemetryPer-attempt telemetry recorder, owned by the hub.

Definition at line 53 of file pairing_engine.cpp.

◆ PairingEngine() [2/2]

esphome::home_io_control::PairingEngine::PairingEngine ( const PairingEngine & )
delete

Non-copyable — stores double-pointer and references into hub member addresses.

Here is the call graph for this function:

Member Function Documentation

◆ discover_and_pair()

bool esphome::home_io_control::PairingEngine::discover_and_pair ( )

Discover and pair a device currently in pairing mode (three-phase orchestrator).

Pairing orchestrator — high-level three-phase flow.

Returns
true if all three phases completed successfully; false otherwise.

Phase 1: run_discovery_phase_() finds a device in pairing mode. Phase 2: run_key_exchange_phase_() performs authenticated key establishment. Phase 3: finalize_pairing_configuration_() sends SetConfig1 (best-effort).

On success the device is added to the registry and a YAML snippet is printed to the log. The hub's thin wrapper manages the busy_ flag before and after this call.

Definition at line 412 of file pairing_engine.cpp.

Here is the call graph for this function:

◆ finalize_pairing_configuration_()

bool esphome::home_io_control::PairingEngine::finalize_pairing_configuration_ ( pairing::PairingContext & context)
protected

Phase 3: send SetConfig1 (0x6F) to enable automatic status updates; best-effort.

Phase 3: send SetConfig1 (0x6F) to enable automatic status updates. Best-effort.

Pairing always proceeds regardless of the outcome — the return value is informational only, used to distinguish PairingOutcome::PAIRED from PairingOutcome::CONFIG_FAILED in telemetry; it never causes discover_and_pair() to report failure.

Parameters
contextPairing context with device information from phases 1 and 2.
Returns
true if the SetConfig1 exchange completed; false if it was skipped or failed.

Definition at line 396 of file pairing_engine.cpp.

Here is the call graph for this function:

◆ operator=()

PairingEngine & esphome::home_io_control::PairingEngine::operator= ( const PairingEngine & )
delete
Here is the call graph for this function:

◆ parse_device_from_discovery()

void esphome::home_io_control::PairingEngine::parse_device_from_discovery ( const IoFrame & frame,
IoDevice & device,
std::string & device_id )
static

Extract node ID, device type, and subtype from a CMD_DISCOVER_RESP frame.

Parse a discovery response frame into device metadata and ID.

Decodes node ID, device type, subtype, and the extended fields (manufacturer, backbone, Multi Information Byte) via decode_discovery_response(), then emits pairing's diagnostic log lines for whichever extended fields the payload actually included. The inversion flag is derived from the type via default_inverted_for_type().

Definition at line 223 of file pairing_engine.cpp.

Here is the call graph for this function:

◆ run_discovery_phase_()

decisions::PairingDiscoveryDisposition esphome::home_io_control::PairingEngine::run_discovery_phase_ ( pairing::PairingContext & context)
protected

Phase 1: broadcast discovery command(s) and wait for a device response (0x29).

Parameters
contextPairing context updated on success.
Returns
ACCEPT on success; NO_RESPONSE or INVALID otherwise.

Sends each configured discovery command in order, waiting up to pairing_discovery_wait_ms for a valid response after each TX. Retries up to PAIRING_DISCOVERY_MAX_ATTEMPTS times per command.

Definition at line 260 of file pairing_engine.cpp.

Here is the call graph for this function:

◆ run_key_exchange_phase_()

bool esphome::home_io_control::PairingEngine::run_key_exchange_phase_ ( pairing::PairingContext & context)
protected

Phase 2: authenticated key exchange (0x31 → 0x3C → 0x32 → 0x33).

Parameters
contextPairing context populated by run_discovery_phase_().
Returns
true if key exchange completes; false on any failure.

Steps:

  1. Transmit CMD_KEY_INIT (0x31)
  2. Wait for device challenge (0x3C)
  3. Transmit CMD_KEY_TRANSFER (0x32) with encrypted system key
  4. Wait for CMD_KEY_CONFIRM (0x33)

Step 4 depends on the driver's TX→RX turnaround: fast-turnaround radios await the 0x33 through the standard send_and_receive() exchange; slow-turnaround radios use the dedicated wait_for_key_confirm_() path with a key-init re-trigger, because the 0x33 would otherwise arrive while the receiver is still settling (see RadioDriver::has_fast_tx_rx_turnaround()).

Definition at line 327 of file pairing_engine.cpp.

Here is the call graph for this function:

◆ wait_for_discovery_response_()

decisions::PairingDiscoveryDisposition esphome::home_io_control::PairingEngine::wait_for_discovery_response_ ( uint32_t timeout_ms,
RadioRxPacket & packet,
IoFrame & response_frame )
protected

Wait for a discovery response (0x29) within timeout_ms with per-chip frequency hopping.

Wait for a valid discovery response (0x29) within timeout_ms.

Parameters
timeout_msMaximum wait window.
packetOutput: raw RadioRxPacket of the accepted discovery frame.
response_frameOutput: parsed IoFrame of the accepted discovery frame.
Returns
ACCEPT on success; NO_RESPONSE (no traffic) or INVALID (wrong frames) otherwise.

Listens with per-chip frequency hopping between slices. Distinguishes between NO_RESPONSE (no packets at all) and INVALID (packets seen but none valid).

Frequency hopping: hops between the 3 IO-homecontrol channels after each slice. The slice length comes from RadioDriver::discovery_hop_slice_ms() — chips that retune slowly need a much longer dwell than fast-hopping chips. When preamble or sync detection fires, the dwell extends by PREAMBLE_DWELL_MS so the incoming frame can complete without interruption.

Definition at line 76 of file pairing_engine.cpp.

Here is the call graph for this function:

◆ wait_for_key_challenge_()

bool esphome::home_io_control::PairingEngine::wait_for_key_challenge_ ( uint32_t timeout_ms,
RadioRxPacket & packet,
IoFrame & challenge_frame,
const uint8_t device_node_id[NODE_ID_SIZE] )
protected

Wait for a key-challenge (0x3C) or direct key-confirm (0x33) from the target device.

Wait for a key-challenge (0x3C) or direct key-confirm (0x33) from target device.

Parameters
timeout_msMaximum wait window.
packetOutput: raw RadioRxPacket of the accepted frame.
challenge_frameOutput: parsed IoFrame.
device_node_idExpected source node ID (devices paired to).
Returns
true if a valid challenge or confirm was received; false on timeout.

During key exchange the device typically responds to 0x31 with a random 6-byte challenge (0x3C). Some devices skip the challenge and send 0x33 directly — indicating immediate key acceptance (observed mostly when the controller's TX→RX turnaround is slow enough that the 0x3C is missed). Both are accepted.

Definition at line 129 of file pairing_engine.cpp.

Here is the call graph for this function:

◆ wait_for_key_confirm_()

bool esphome::home_io_control::PairingEngine::wait_for_key_confirm_ ( pairing::PairingContext & context)
protected

Transmit the 0x32 key transfer and wait for the 0x33 key confirm with retry.

Transmit the 0x32 key transfer and wait for 0x33 key confirm (with retry).

Uses a dedicated wait loop with frequency hopping and the driver's response_preamble() (drivers whose TX waveform needs more lock-on margin return a longer preamble). Retries up to EXCHANGE_RETRY_COUNT times on timeout.

Definition at line 165 of file pairing_engine.cpp.

Here is the call graph for this function:

The documentation for this class was generated from the following files: