|
Home IO Control
ESPHome add-on for IO-Homecontrol devices
|
Getting a device onto your hub. There are two routes, and picking the right one first saves more time than anything else on this page.
flowchart TD
A[Do you have a working<br/>two-way IO-Homecontrol hub today?<br/>TaHoma, KLF200, KLR200,<br/>Connexoon, KIG300… — not just<br/>a 1W wall switch or remote] -->|Yes| B[Use key extraction]
A -->|No| C[Has a two-way hub ever<br/>controlled the device?]
C -->|No — it is new, or only ever<br/>driven by 1W remotes<br/>or wall switches| D[Use Discover & Pair]
C -->|Yes, a hub I<br/>no longer have| E[Factory reset it as its<br/>manual describes, then<br/>register its local remote<br/>again, if it has one]
E --> D
D --> F{Does it answer?}
F -->|Yes| I[Copy the YAML snippet<br/>from the log]
F -->|No| G[See The device is<br/>never found in<br/>Troubleshooting]
B --> H[Then press Scan Paired Devices<br/>to list everything on the key]
Key extraction is the route whenever you already own a two-way hub — one that runs its own "add a device" wizard, such as a TaHoma, Connexoon, Connectivity Kit, KLF200, KLR200 or KIG300. It recovers the same node_id/system_key your devices already trust, so nothing leaves its paired state and every device on that installation appears at once. Most field reports of a device that "never responds to discovery" have been resolved this way. A 1W-only wall switch or remote (a VELUX KLI, a Somfy Smoove) does not run that wizard and has nothing key extraction can recover from it — see Supported devices for which controls qualify. See Key extraction.
Discover & Pair is the route for a device that is genuinely unclaimed: new, or factory reset, with no hub holding its key.
A flag in the home_io_control: block adds a Home Assistant button that starts discovery and pairing:
home_io_control: # ... discover_and_pair_button: true
Some devices keep a key of their own and refuse a pushed one. An Atlantic AGILIA IO radiator in its display's pairing mode answers discovery as a remote controller, so pairing_key_transfer: auto (the default, see Radio tuning) pulls its key instead of pushing yours. After the device proves the key, the hub reads the address after the pairing node, where an AGILIA's heater usually sits, and runs a roll-call under the key when that read gets no answer. It logs one report between a DEVICE KEY RECEIVED -- DO NOT SHARE IT line and an END OF DEVICE KEY REPORT line. Atlantic AGILIA IO has the radiator's menu steps. Each device gets a paste-ready block that includes its own system_key::
Paste the block into your YAML, move the key into secrets.yaml (system_key: !secret ...), and flash. Until you reboot the hub already controls the registered devices with that key. The key appears in the device log only: the Last Pairing Result sensor and the Home Assistant events show the key check value at most, so it is safe to share those. Remove the key from any log you post. Each radiator has its own key, so pair each one in turn.
Between the device answering discovery and the key exchange starting, the hub sends a discover-confirm frame directly to that device and waits up to a few seconds for its acknowledgement — the same handshake every real controller in this project's corpus performs before proceeding. It never blocks pairing on its own: a refusal, a timeout, or nothing at all still lets the attempt continue. If a specific device seems to dislike this step, pairing_discover_confirm: skip in the tuning: block reverts to sending nothing there at all — see Radio tuning.
Expect this to take a few goes. The hub retries discovery three times per press, and it is still common to need two or three presses because the timing between PROG and the button matters. Press PROG again and repeat before you change any settings.
If the device never answers, read The device is never found before touching Radio tuning. A device that already belongs to a hub has nothing left to answer a discovery with, and no tuning changes that; key extraction does.
discover_and_pair_button: true also creates a companion "Last Pairing Result" diagnostic text sensor alongside the button. It updates after every attempt with a frozen, machine-readable summary:
| Field | Meaning |
|---|---|
| outcome | paired, no_response, invalid_response, key_exchange_failed, key_transfer_refused (the device answered the key with an error, see the advisor), key_received (a key pull got a different key and the device proved it, but no device that holds it could be registered; see Radio tuning), or hub_key_returned (a key pull got the hub's own key from a node no platform drives, and no device that holds the key answered). |
| phase | The furthest stage the pairing state machine reached. |
| node / type | The node ID and type of the device that answered discovery, or - if nothing was paired. After a key pull this is the node that handed the key over; an AGILIA's heater appears only in the log report. |
| attempts | Number of discovery command retries sent. |
| lbt | Listen-before-talk retries consumed across the whole attempt; a channel-busy indicator. |
| dur_ms | Attempt duration in milliseconds. |
| heard | Total RX events seen, including ones the pairing classifiers rejected and a 1W gesture overheard just before the window opened (see the advisor below). |
| advice | Comma-separated advisor codes (see below), or none. |
The format is stable and versioned (the v1; prefix), so an automation can alert when outcome is not paired.
Discover & Pair blocks the hub while it runs, so a press made during an attempt is handled only after it ends. The hub ignores a press it handles within a second of an attempt ending and logs that it did: put the device back into pairing mode and press again.
The log also gets a full human-readable summary of every TX, RX, rejected RX, listen-before-talk deferral and phase event, plus a total channel-hop count. When the overheard traffic tells a story, one or more pairing advisor WARN lines turn it into a diagnosis. The advisor also considers the 15 seconds before the button press: a PROG press completed just ahead of it is 1W traffic, not RF silence, and is reported as 1w_traffic.
| Advice code | When it fires | What it means |
|---|---|---|
| 1w_traffic | A 1W remote was seen performing 1W pairing (a broadcast to 00003F with a 1W pairing command byte). The broadcast does not identify a target, so this fires on any 1W pairing gesture in range. | A remote's PROG gesture was heard, and no device answered. That shows a gesture happened, not what state the device is in: the same frames have come just before a successful pairing, too. Check in order. 1. If a two-way hub already controls the device (a TaHoma, KLF200, KLR200, KIG300, and similar; not a 1W wall switch or remote), it is almost certainly paired to that hub: use key extraction. 2. Was the PROG press on a remote already registered to this device, released at the first jog? A longer hold can remove the remote, or reset the device straight after a power cut. On such a remote a PROG press is also the add/remove step, so give each attempt one press: a second press while the device is still in pairing mode can close the window the first press opened. 3. Was this the only device in pairing mode? One remote often drives several devices. 4. A Double Power Cut or factory reset is not a pairing gesture on its own. Afterwards a device with a local remote expects that remote to be registered again, as its manual describes, so do that first, then repeat 2. Some families document a separate gesture: a VELUX SSL has a physical P button that opens a 10-minute 2W registration window (see VELUX INTEGRA). |
| channel_busy | Listen-before-talk retries were exhausted and the same source was heard repeatedly during the wait. | A repeating beacon (usually a nearby remote or sensor) is flooding the channel and delaying discovery transmissions. Try again, or tune lbt_max_retries/lbt_rssi_threshold_dbm — see Radio tuning. |
| foreign_controller | A discovery response (0x29) was seen addressed to a node ID that is not this hub's. | Another controller (a TaHoma, say) is pairing the same device right now. Wait for it to finish, or make sure yours is the only controller with the device in pairing mode. |
| key_push_not_accepted | The device answered the hub's key with COMMAND_UNKNOWN, or it answered discovery as a remote controller (node type 1023) and the key step failed. | The device does not take a pushed key in this mode. It may hand over its own key instead: set pairing_key_transfer to pull or back to auto, described in Radio tuning. An Atlantic AGILIA IO radiator does this in its display's pairing mode, and auto pulls it (issue #165). |
| key_refused | The device answered the hub's key with any other error. The message names the code. | If the device was paired before, run Scan Paired Devices before retrying: it may already hold a key. |
| key_pull_not_offered | The hub pulled (auto for a remote controller, or pull), and the device answered the key request with an error. | The device does not hand out its key. Set pairing_key_transfer: push to pair it normally. A Somfy Izymo dimmer answers this way. |
| key_pull_unproven | The hub pulled, and the device sent a key but did not prove it holds that key. | The hub prints no key. Post the Key pull check line in your issue: it contains no key. |
| pulled_key_received | The hub pulled, the device sent a different key and proved it holds it, and a roll-call under that key found no device the hub could register. | The log prints the key once. Put it into the system_key: of the entity that controls the device, flash, and run Scan Paired Devices: the log prints its YAML snippet. Each device of this kind keeps its own key, so compare their Key pull check values. |
| hub_key_no_responder | The hub pulled, the device sent the hub's own key, it has no platform of its own, and no device that holds the key answered. | The device that pairs is not always the one to control: an Atlantic radiator's heater sits at the next address. Press Scan Paired Devices, and again if nothing answers; it prints the YAML block of each device that holds the key. |
| rf_silent | Nothing at all was heard on any channel during the whole discovery window. | Either the radio is not receiving (antenna, wiring, wrong tuning), or the channel was simply quiet for those few seconds. Look at the rest of the log first. If it shows other IO-Homecontrol frames being received (your remotes, a neighbour's devices), the radio works, and this reads as "the device did not answer". Only if nothing is ever received, check the antenna and radio tuning before pressing PROG again. |
Once you hold a system key — recovered from another hub, or established by pairing — a roll-call lists every device that trusts it, whether or not you have a YAML entity for it. Each device you have no entity for answers with a ready-to-paste snippet, and no pairing handshake is involved. This is the fastest way to bring up an installation after key extraction.
Enable it from the hub block, not as a button: entry:
home_io_control: # ... radio pins, node_id, system_key ... scan_paired_devices_button: true
The button and the scan_paired_devices action run the same roll-call. The action is always available; the button saves a trip to Developer Tools.
It takes no fields. Like the other actions it is named esphome.<node_name>_scan_paired_devices; for a config with name: hioc-heltec-v2:
action: esphome.hioc_heltec_v2_scan_paired_devices
The hub broadcasts a roll-call (CMD_DISCOVER_SPE_REQ, 0x2A) and listens for every device that holds its system key. Responders are grouped into a Known: section (already in your YAML) and an Unknown: section, and each unknown one gets a paste-ready block. Either section is omitted when it would be empty. Each line names the device as <type>, <subtype> (for example electrical heater, with adjustable setpoint, or subtype N when the subtype has no name; the node types table lists them). A responder whose type no platform drives yet gets not supported yet in place of the block.