|
Home IO Control
ESPHome add-on for IO-Homecontrol devices
|
Device-role responder for the "Recover System Key" feature. More...
#include <key_extraction_responder.h>
Public Member Functions | |
| KeyExtractionResponder (const uint8_t *node_id, RadioDriver **radio, const TuningConfig *tuning, DeviceRegistry ®istry, TransmitFrameFn transmit, NamedTimeoutFn schedule_auto_off) | |
| KeyExtractionResponder (const KeyExtractionResponder &)=delete | |
| Non-copyable — holds injected callbacks and references into hub member addresses. | |
| KeyExtractionResponder & | operator= (const KeyExtractionResponder &)=delete |
| void | set_armed (bool armed) |
| Arm or disarm the "Recover System Key" (key extraction) responder. | |
| void | set_armed_callback (std::function< void(bool)> cb) |
| Register a callback invoked whenever the key-extraction armed state changes — manual toggle, successful extraction, or auto-off timeout — so the switch entity can keep its displayed state in sync when the responder disarms itself rather than the user. | |
| bool | try_handle_frame (const IoFrame &frame, uint32_t rx_freq_hz=0) |
| Dispatch a frame to the responder if it's one of its 0x28/0x2C/0x31/0x32/0x36/0x3C frames and the responder is armed. | |
| uint32_t | session_freq () const |
| Channel of the session the responder last replied in: the channel the frame it answered came in on, CH2 until it has answered anything. | |
| bool | in_session_hold () const |
| True while awaiting_reply() and the frame the responder last answered was received less than the session window ago (key_extraction_session_window_ms() of the byte the last discovery answer advertised): loop() then stays on session_freq() for the peer's in-session frame. | |
| uint32_t | session_window_ms () const |
| Session window for the Multi Information Byte the last discovery answer advertised. | |
| bool | discover_reply_pending () const |
| True while a discovery answer waits for its slot (schedule_discover_reply_()). | |
| uint32_t | ms_until_discover_reply () const |
| Milliseconds until the pending discovery answer's slot, 0 once it is due. | |
| void | generate_throwaway_id (uint8_t out[NODE_ID_SIZE]) |
| Generate a random throwaway node ID for one key-extraction arm cycle, avoiding collisions with the broadcast addresses, this hub's own real node ID, and any registered device. | |
| void | arm_post_extraction_grace () |
| (Re)arm the post-extraction grace window that replaces the old immediate disarm-on-extraction: called once when the key is first recovered, and again on every sign of hub progress after that (an inbound 0x36, an outbound 0x3D) so a slow multi-retry hub isn't cut off mid-round. | |
| bool | awaiting_reply () const |
| True whenever the responder has replied at least once, is waiting on the hub's next step, and that wait is still within its bounded hold window — i.e. | |
Public Attributes | |
| pairing_responder::ResponderContext | key_extraction_ctx_ |
| State for the current "Accept Foreign Pairing" (key-extraction) arm cycle; DISARMED by default so a fresh boot never responds to foreign pairing traffic. | |
| uint32_t | key_extraction_hold_deadline_ms_ {0} |
| Deadline (millis()) until which loop() listens for the key-extraction responder's peer, deliberately independent of key_extraction_ctx_.state itself. | |
Device-role responder for the "Recover System Key" feature.
Constructed once by IOHomeControlComponent; non-copyable because it is wired with injected callbacks and references into hub member addresses (mirrors ManagementActions / ExchangeEngine).
Definition at line 61 of file key_extraction_responder.h.
| esphome::home_io_control::KeyExtractionResponder::KeyExtractionResponder | ( | const uint8_t * | node_id, |
| RadioDriver ** | radio, | ||
| const TuningConfig * | tuning, | ||
| DeviceRegistry & | registry, | ||
| TransmitFrameFn | transmit, | ||
| NamedTimeoutFn | schedule_auto_off ) |
| node_id | Hub's real 3-byte node ID (throwaway-ID collision check). |
| radio | Double pointer to the hub's active radio driver, so a test's comp.radio_ = &mock propagates (mirrors ExchangeEngine). |
| tuning | Runtime tuning config, owned by the hub (cold_broadcast_reply_preamble). |
| registry | Device registry, for the throwaway-ID collision check only. |
| transmit | How to put a reply frame on air (see TransmitFrameFn). |
| schedule_auto_off | Named-timeout scheduler for the 10-minute arm window and the post-extraction grace window (see NamedTimeoutFn). |
Definition at line 131 of file key_extraction_responder.cpp.
|
delete |
Non-copyable — holds injected callbacks and references into hub member addresses.
| void esphome::home_io_control::KeyExtractionResponder::arm_post_extraction_grace | ( | ) |
(Re)arm the post-extraction grace window that replaces the old immediate disarm-on-extraction: called once when the key is first recovered, and again on every sign of hub progress after that (an inbound 0x36, an outbound 0x3D) so a slow multi-retry hub isn't cut off mid-round.
Uses the same named-timer replace-on-reschedule idiom as the 10-minute auto-off timer — see key_extraction_responder.cpp for why a naive "only disarm if DISARMED" guard inside the callback is not enough once a manual disarm-and-rearm can happen inside the window.
Definition at line 493 of file key_extraction_responder.cpp.
|
inlinenodiscard |
True whenever the responder has replied at least once, is waiting on the hub's next step, and that wait is still within its bounded hold window — i.e.
key_extraction_ctx_.state is neither DISARMED (feature unused) nor ARMED_IDLE (armed, but no discovery request seen yet), AND key_extraction_hold_deadline_ms_ has not yet passed. While it is true, loop() defers background status polls and, instead of the generic idle hop, listens for the peer's next frame: on session_freq() during in_session_hold(), then with a rotating listen over all three channels.
The deadline is a plain timestamp, not a named set_timeout() timer: ending this listen is purely a radio-scheduling optimization (see key_extraction_hold_deadline_ms_'s own doc comment for why it is deliberately decoupled from key_extraction_ctx_.state itself), so nothing needs to fire a callback when it lapses — the next loop() iteration simply stops taking the responder-listen branch on its own. Default-constructed, the deadline is 0, which is always in the past relative to any real millis() reading once the device has been running — so a mid-exchange state reached without the deadline having been (re)set (e.g. a reply-builder failure between the state guard and the deadline update) safely never takes the radio, rather than holding it unboundedly.
Defined inline: defer_background_poll_() calls this every loop() iteration.
Definition at line 170 of file key_extraction_responder.h.
|
inlinenodiscard |
True while a discovery answer waits for its slot (schedule_discover_reply_()).
Definition at line 125 of file key_extraction_responder.h.
| void esphome::home_io_control::KeyExtractionResponder::generate_throwaway_id | ( | uint8_t | out[NODE_ID_SIZE] | ) |
Generate a random throwaway node ID for one key-extraction arm cycle, avoiding collisions with the broadcast addresses, this hub's own real node ID, and any registered device.
| out | Output: 3-byte node ID. |
Definition at line 141 of file key_extraction_responder.cpp.
|
inlinenodiscard |
True while awaiting_reply() and the frame the responder last answered was received less than the session window ago (key_extraction_session_window_ms() of the byte the last discovery answer advertised): loop() then stays on session_freq() for the peer's in-session frame.
Once false (while still awaiting a reply), loop() scans all three channels.
Definition at line 117 of file key_extraction_responder.h.
|
inlinenodiscard |
Milliseconds until the pending discovery answer's slot, 0 once it is due.
The answer goes out from the scheduler, which runs only between loop() passes, so a blocking listen must end by then or the answer slips past its slot and, for the last slots, out of the searcher's window. Meaningful only while discover_reply_pending().
Definition at line 134 of file key_extraction_responder.h.
|
delete |
|
inlinenodiscard |
Channel of the session the responder last replied in: the channel the frame it answered came in on, CH2 until it has answered anything.
Replies go out on it (the 0x29 ends on it), and loop() stays on it while in_session_hold().
Definition at line 111 of file key_extraction_responder.h.
|
inlinenodiscard |
Session window for the Multi Information Byte the last discovery answer advertised.
Definition at line 122 of file key_extraction_responder.h.
| void esphome::home_io_control::KeyExtractionResponder::set_armed | ( | bool | armed | ) |
Arm or disarm the "Recover System Key" (key extraction) responder.
Arming picks a fresh throwaway node ID, resets the pairing_responder state machine to ARMED_IDLE, and schedules a 10-minute auto-off. While armed, the 0x28/0x2C/0x31/0x32 branches in process_received_packet_() emulate an unpaired device so a user's existing hub can pair to it and hand over its node_id/system_key (see pairing_responder.h). Disarming — manual, via the HA switch, on successful extraction, or on auto-off — immediately stops those branches from responding; it never touches the real device registry or the hub's own node_id_/ system_key_. This is the body that was IOHomeControlComponent::set_key_extraction_armed().
| armed | Desired state. |
Definition at line 161 of file key_extraction_responder.cpp.
|
inline |
Register a callback invoked whenever the key-extraction armed state changes — manual toggle, successful extraction, or auto-off timeout — so the switch entity can keep its displayed state in sync when the responder disarms itself rather than the user.
Single-slot.
| cb | Callable receiving the new armed state. |
Definition at line 95 of file key_extraction_responder.h.
|
nodiscard |
Dispatch a frame to the responder if it's one of its 0x28/0x2C/0x31/0x32/0x36/0x3C frames and the responder is armed.
Kept a separate function (rather than inlined into process_received_packet_()) purely to keep that function's cognitive complexity under the clang-tidy threshold, mirroring PairingEngine::record_discovery_rx_telemetry_()'s reason for existing.
| frame | Parsed inbound frame. |
| rx_freq_hz | Channel the frame was received on (RadioRxPacket::freq_hz); 0 if unknown, which counts as CH2. Answering the frame makes it the session channel. |
Definition at line 206 of file key_extraction_responder.cpp.
| pairing_responder::ResponderContext esphome::home_io_control::KeyExtractionResponder::key_extraction_ctx_ |
State for the current "Accept Foreign Pairing" (key-extraction) arm cycle; DISARMED by default so a fresh boot never responds to foreign pairing traffic.
See pairing_responder.h. Public so the host tests that script individual RX branches can preset and inspect it.
Definition at line 179 of file key_extraction_responder.h.
| uint32_t esphome::home_io_control::KeyExtractionResponder::key_extraction_hold_deadline_ms_ {0} |
Deadline (millis()) until which loop() listens for the key-extraction responder's peer, deliberately independent of key_extraction_ctx_.state itself.
Set (not "armed" — this is a plain timestamp, not a named set_timeout() timer) on every sign of hub progress: the three pre-extraction reply handlers (handle_discover_(), handle_discover_confirm_(), handle_key_init_(), key_extraction_responder.cpp) push it out by KEY_EXTRACTION_MID_ATTEMPT_TIMEOUT_MS, and arm_post_extraction_grace() pushes it out by KEY_EXTRACTION_POST_EXTRACT_GRACE_MS so the hold also covers the (much longer) post-extraction node-verification phase it governs.
Deliberately independent of key_extraction_ctx_.state: the pure guards in pairing_responder.cpp decide whether an inbound frame is accepted by checking state alone, never this deadline, so a real (slower) hub's next frame still completes the exchange correctly even if it arrives after the hold has expired. Coupling the two — letting the deadline also force state back to ARMED_IDLE — would silently discard that live protocol progress instead of just releasing the radio hold. See awaiting_reply()'s doc comment for how this is consumed.
Definition at line 196 of file key_extraction_responder.h.