48constexpr uint32_t ONEWAY_KEY_ADOPTION_AUTO_OFF_MS = 10 * 60 * 1000;
49constexpr const char *ONEWAY_KEY_ADOPTION_TIMEOUT_NAME =
"oneway_key_adoption_auto_off";
58 std::transform(sender_hex_lower.begin(), sender_hex_lower.end(), sender_hex_lower.begin(),
59 [](
unsigned char c) { return std::tolower(c); });
65 mac_line =
"MAC VERIFIED: this frame's MAC checked out under the recovered key -- the strongest evidence "
66 "available on the spot that it is correct.";
69 mac_line =
"MAC FAILED: this frame's MAC did NOT check out under the recovered key -- it is probably wrong. "
70 "Re-arm and repeat the key-copy gesture closer to the hub.";
74 mac_line =
"MAC not present: this frame carried no MAC trailer to verify against -- treat this key as "
75 "unconfirmed until tested.";
80 constexpr size_t manufacturer_line_size = 32;
81 char manufacturer_line[manufacturer_line_size];
82 snprintf(manufacturer_line,
sizeof(manufacturer_line),
" manufacturer: 0x%02X",
85 std::string type_lines;
86 if (observed_type_known) {
88 " # observed from this sender's traffic; verify\n";
90 type_lines =
" # io_device_type: unknown -- no other 1W traffic was observed from this sender while armed;\n"
91 " # check the DEBUG \"rx 1W remote ...\" log line once you see this sender transmit again.\n";
95 "\nThe hub always transmits under its own node_id, never the sender's -- copying the sender's address "
96 "would hijack its rolling sequence counter and break its existing remote.\n"
97 "Copy the block below into your hub's YAML.\n"
98 "oneway_controllers:\n"
99 " # node_id omitted -> derived from your hub node_id; see the boot log\n"
101 sender_hex_lower +
"\n" +
" system_key: \"" + key_hex +
"\"\n" + manufacturer_line +
"\n" + type_lines +
102 " commands: [open, close, stop]";
114 this->armed_ =
false;
115 ESP_LOGI(
detail::TAG,
"1W key adoption: disarmed");
116 if (this->armed_callback_)
117 this->armed_callback_(
false);
126 "1W key adoption: ARMED for 10 minutes. Trigger the key-copy gesture on your existing 1W remote now "
127 "(the remote-to-remote copy mode described in its manual). Receive-only — nothing is transmitted.");
129 this->schedule_auto_off_(ONEWAY_KEY_ADOPTION_TIMEOUT_NAME, ONEWAY_KEY_ADOPTION_AUTO_OFF_MS, [
this]() {
134 ESP_LOGW(
detail::TAG,
"1W key adoption: window expired, no add-controller frame seen. Disarming.");
138 if (this->armed_callback_)
139 this->armed_callback_(
true);
150 memcpy(this->observed_class_.node, info.
src, NODE_ID_SIZE);
152 this->observed_class_.valid =
true;
158 if (frame.
cmd != CMD_ONEWAY_ADD_CONTROLLER)
166 ESP_LOGW(
detail::TAG,
"1W key adoption: heard an add-controller frame from %s but could not decode it (error %u)",
174 const bool observed_known =
175 this->observed_class_.valid && memcmp(this->observed_class_.node, adopted.
sender_node, NODE_ID_SIZE) == 0;
187 ESP_LOGW(
detail::TAG,
"========================================");
188 ESP_LOGW(
detail::TAG,
"1W CONTROLLER KEY ADOPTED FROM %s -- DO NOT SHARE THIS KEY",
192 ESP_LOGW(
detail::TAG,
"========================================");
void try_adopt(const IoFrame &frame)
Decode an inbound CMD_ONEWAY_ADD_CONTROLLER (0x30) while armed, report the result,...
bool armed() const
Whether the listener is currently armed.
void set_armed(bool armed)
Arm or disarm the 1W controller-key adoption listener.
void record_observed_class(const OneWayFrameInfo &info)
Remember the most recent 1W target device class observed from info.src, for the adoption report's io_...
Hub-layer log tag and log/format helpers shared by the hub and its collaborators.
constexpr const char * TAG
Shared log tag for hub-level messages.
void log_multiline_result(const char *tag, bool is_warning, const std::string &prefix, const std::string &message)
Log prefix followed by message, one line per log call rather than one call for the whole (possibly mu...
std::string format_key_hex(const uint8_t key[AES_KEY_SIZE])
Format a 16-byte key as an uppercase, unseparated hex string for display.
std::string build_oneway_adoption_report(const OneWayAdoptedKey &adopted, bool observed_type_known, DeviceType observed_type)
Build the full 1W controller-key-adoption report: MAC-verification status, the own-address transmissi...
std::string format_device_type_for_yaml(DeviceType type)
Build the YAML value for a device's io_device_type key.
@ VERIFIED
frame.has_mac was true and the MAC verified under the recovered key.
@ NOT_PRESENT
frame.has_mac was false; nothing to verify.
@ FAILED
frame.has_mac was true and the MAC did NOT verify under the recovered key.
DeviceType
Device type identifiers reported by IO‑Homecontrol products.
@ UNKNOWN
Unknown/unspecified device.
OneWayAddControllerDecodeError decode_1w_add_controller(const IoFrame &frame, OneWayAdoptedKey &out)
Decode a CMD_ONEWAY_ADD_CONTROLLER (0x30) frame into a recovered controller identity.
OneWayAddControllerDecodeError
Decoding outcome for decode_1w_add_controller().
@ NONE
Decode succeeded; out is populated.
std::string node_id_to_string(const uint8_t id[NODE_ID_SIZE])
Format a 3‑byte node ID as a 6‑character uppercase hex string.
Opt-in, receive-only adoption of a 1W installation's controller key.
Device-name, address-classification and 1W-frame codecs.
Parsed IO‑Homecontrol frame (CTRL0/1 + addresses + command + data).
uint8_t src[NODE_ID_SIZE]
Source node ID (3 bytes).
Recovered controller identity from a decoded CMD_ONEWAY_ADD_CONTROLLER (0x30) frame.
uint8_t manufacturer
Manufacturer ID byte from the payload (man_id).
OneWayMacStatus mac_status
MAC-verification outcome; see OneWayMacStatus.
uint8_t system_key[AES_KEY_SIZE]
Recovered network system key (crypto::crypt_1w_key() output).
uint8_t sender_node[NODE_ID_SIZE]
Sender's node address (frame.src) — the new identity's node.
Decoded representation of a 1W remote frame.
DeviceType target_type
Target device class from broadcast address.
uint8_t src[NODE_ID_SIZE]
Remote source node ID (3 bytes).
Most recent 1W target device class observed while armed.