|
Home IO Control
ESPHome add-on for IO-Homecontrol devices
|
Classes | |
| struct | OneWayDedupState |
| Key fields of the last processed 1W frame, used to collapse a remote's repeat burst. More... | |
Enumerations | |
| enum class | ExchangeFirstResponseDisposition : uint8_t { IGNORE_UNRELATED , COMPLETE_DIRECT , REQUIRE_AUTH } |
| Disposition for the first response in an authenticated exchange. More... | |
| enum class | ExchangeFinalResponseDisposition : uint8_t { IGNORE_UNRELATED , ACCEPT } |
| Disposition for the final response after authentication. More... | |
| enum class | PairingDiscoveryDisposition : uint8_t { NO_RESPONSE , INVALID , ACCEPT } |
| Disposition during pairing discovery phase. More... | |
| enum class | PairingKeyChallengeDisposition : uint8_t { IGNORE , ACCEPT } |
| Disposition during pairing key-challenge phase. More... | |
Functions | |
| bool | is_exchange_internal_command (uint8_t cmd) |
| Returns true for commands that are internal to an exchange handshake and carry no useful information for a passive observer (challenge request/response). | |
| bool | frame_matches_nodes (const IoFrame &frame, const uint8_t expected_src[NODE_ID_SIZE], const uint8_t expected_dst[NODE_ID_SIZE]) |
| Check if two frames have identical src/dst node IDs. | |
| bool | frame_matches_exchange_endpoints (const IoFrame &request, const IoFrame &candidate) |
| Check if candidate frame endpoints are the reverse of the request (dst==request.src, src==request.dst). | |
| ExchangeFirstResponseDisposition | classify_exchange_first_response (const IoFrame &request, const IoFrame &candidate) |
| Decide how to handle the first response packet in an authenticated exchange. | |
| ExchangeFinalResponseDisposition | classify_exchange_final_response (const IoFrame &request, const IoFrame &candidate) |
| Decide if a candidate frame is an acceptable final response after authentication. | |
| PairingDiscoveryDisposition | classify_pairing_discovery_response (const IoFrame &candidate, const uint8_t controller_id[NODE_ID_SIZE]) |
| Decide if a frame is a valid discovery response (0x29) during pairing. | |
| PairingKeyChallengeDisposition | classify_pairing_key_challenge (const IoFrame &candidate, const uint8_t device_id[NODE_ID_SIZE], const uint8_t controller_id[NODE_ID_SIZE]) |
| Decide if a frame is a valid key-challenge (0x3C) during pairing key exchange. | |
| bool | is_duplicate_1w_frame (const OneWayDedupState &last, const OneWayDedupState &incoming, uint32_t window_ms) |
| Decide whether an incoming 1W frame repeats the previous one inside the burst window. | |
| bool | defer_background_poll_for_1w_activity (bool next_op_is_background, uint32_t first_1w_activity_ms, uint32_t last_1w_activity_ms, uint32_t now, uint32_t quiet_ms, uint32_t max_defer_ms) |
| Decide whether to hold back a queued background poll because a 1W remote is still transmitting. | |
| bool | oneway_burst_started_fresh (uint32_t last_1w_activity_ms, uint32_t now, uint32_t quiet_ms) |
| Whether a 1W frame arriving at now starts a new burst rather than extending the current one — true if no frame has been seen yet, or the gap since the last one reached quiet_ms (the previous burst already released any deferred poll). | |
| uint32_t | response_wait_slice_ms (uint32_t remaining_ms) |
| Slice remaining wait time into bounded intervals to allow frequency hopping. | |
|
strong |
Disposition for the final response after authentication.
| Enumerator | |
|---|---|
| IGNORE_UNRELATED | Frame doesn't match endpoints — ignore. |
| ACCEPT | Frame matches expected response — exchange succeeds. |
Definition at line 31 of file hub_decisions.h.
|
strong |
Disposition for the first response in an authenticated exchange.
Definition at line 24 of file hub_decisions.h.
|
strong |
Disposition during pairing discovery phase.
| Enumerator | |
|---|---|
| NO_RESPONSE | No packets received on the channel within timeout. |
| INVALID | Packets seen but none were valid discovery (0x29) frames. |
| ACCEPT | Valid discovery response received. |
Definition at line 37 of file hub_decisions.h.
|
strong |
Disposition during pairing key-challenge phase.
| Enumerator | |
|---|---|
| IGNORE | Not a valid challenge (wrong cmd, length, or sender). |
| ACCEPT | Valid 0x3C challenge from target device. |
Definition at line 44 of file hub_decisions.h.
|
inline |
Decide if a candidate frame is an acceptable final response after authentication.
Only endpoint matching is checked here; command validity is encoded in the disposition mapping by the caller.
| request | Original outbound request frame. |
| candidate | Parsed IoFrame from the device. |
Definition at line 102 of file hub_decisions.h.
|
inline |
Decide how to handle the first response packet in an authenticated exchange.
Used by wait_for_first_response_() to determine whether the exchange:
| request | Original outbound request frame. |
| candidate | Parsed IoFrame from the device. |
Definition at line 83 of file hub_decisions.h.
|
inline |
Decide if a frame is a valid discovery response (0x29) during pairing.
Only the destination is checked, not the source: the discovery request goes out to a shared broadcast address, so a response arriving during the same window may be a device answering a different controller's concurrent discovery rather than ours — real hardware addresses its response back to the requesting controller's own node ID, so checking that is both possible and sufficient to reject it. The source can't be checked here — the device's node ID is exactly what discovery exists to learn, so there is nothing yet to compare it to.
| candidate | Parsed IoFrame. |
| controller_id | Node ID of this controller (expected destination). |
Definition at line 122 of file hub_decisions.h.
|
inline |
Decide if a frame is a valid key-challenge (0x3C) during pairing key exchange.
The challenge must:
| candidate | Parsed IoFrame. |
| device_id | Node ID of the device being paired (expected sender). |
| controller_id | Node ID of this controller (expected destination). |
Definition at line 141 of file hub_decisions.h.
|
inline |
Decide whether to hold back a queued background poll because a 1W remote is still transmitting.
The radio is half-duplex and an authenticated exchange blocks for 1–3 s, during which no frame can be received at all. A press on a linked remote schedules a status poll, so without this gate the hub's own poll can start on top of the burst that triggered it and go deaf to the rest of it.
Only background polls are deferred. A user command must never wait on a remote the user may not even own — 1W broadcasts carry no ownership marker, so the activity could be a neighbour's.
The hold re-arms on every 1W frame received while it is already active, so a real burst from one remote (~160 ms, well under quiet_ms) never gets cut short mid-transmission. Left unchecked that re-arming has no cap: sustained sub-quiet_ms 1W traffic from any source — including a neighbour's, since these broadcasts carry no ownership marker — would hold background polls back indefinitely. max_defer_ms bounds that: once that much time has passed since the burst started (not the most recent frame), the poll is let through regardless of ongoing traffic. The gate only ever delays a poll, never drops one — it stays queued and fires as soon as it is no longer deferred.
| next_op_is_background | True if the queue front is a REQUEST_STATUS / REQUEST_NAME. |
| first_1w_activity_ms | millis() of the first frame in the current 1W burst; 0 if none seen since boot. |
| last_1w_activity_ms | millis() of the most recent 1W frame; 0 if none seen since boot. |
| now | Current millis(). |
| quiet_ms | How long after 1W activity to hold background polls back. |
| max_defer_ms | Hard cap on total defer time, measured from first_1w_activity_ms. |
Definition at line 214 of file hub_decisions.h.
|
inline |
Check if candidate frame endpoints are the reverse of the request (dst==request.src, src==request.dst).
Definition at line 67 of file hub_decisions.h.
|
inline |
Check if two frames have identical src/dst node IDs.
Definition at line 60 of file hub_decisions.h.
|
inline |
Decide whether an incoming 1W frame repeats the previous one inside the burst window.
| last | State recorded for the previously processed 1W frame. |
| incoming | Candidate frame's key fields, with timestamp set to now. |
| window_ms | Burst-suppression window. |
Definition at line 178 of file hub_decisions.h.
|
inline |
Returns true for commands that are internal to an exchange handshake and carry no useful information for a passive observer (challenge request/response).
These frames appear in every authenticated exchange between other controllers and devices on the network, but contain only ephemeral cryptographic data.
Definition at line 55 of file hub_decisions.h.
|
inlinenodiscard |
Whether a 1W frame arriving at now starts a new burst rather than extending the current one — true if no frame has been seen yet, or the gap since the last one reached quiet_ms (the previous burst already released any deferred poll).
Callers use this to decide whether to reset a burst's start-time tracking; see defer_background_poll_for_1w_activity() for why the burst start (not just the latest frame) needs its own timestamp.
| last_1w_activity_ms | millis() of the most recent 1W frame before this one; 0 if none seen since boot. |
| now | Current millis() (this frame's arrival time). |
| quiet_ms | Gap after which a previous burst is considered over. |
Definition at line 234 of file hub_decisions.h.
|
inline |
Slice remaining wait time into bounded intervals to allow frequency hopping.
The wait loops (exchange and pairing) use this to avoid blocking the radio for too long without hopping. Each slice is at most RESPONSE_CHANNEL_WAIT_MS.
| remaining_ms | Total time left in the wait window. |
Definition at line 247 of file hub_decisions.h.