Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
pairing_advisor.h
Go to the documentation of this file.
1#pragma once
2
3/// @file pairing_advisor.h
4/// @brief Read-only advisor that turns PairingTelemetry into actionable diagnostics.
5/// @ingroup hioc_hub
6///
7/// PairingAdvisor is a pure, stateless consumer of a completed PairingTelemetry attempt: it
8/// never touches the radio or the pairing state machine, only inspects the events already
9/// recorded. It exists to convert the most common field failures (a 1W remote's PROG gesture
10/// overheard while no device answers, issues #27/#121; a busy channel; a foreign controller
11/// mid-pairing; dead RF)
12/// from a hex-dump exercise into a self-explaining WARN line.
13
14#include "pairing_telemetry.h"
15#include "proto_frame.h"
16
17#include <cstdint>
18#include <string>
19
20namespace esphome {
21namespace home_io_control {
22namespace advisor {
23
24/// @brief Actionable diagnosis derived from a completed pairing attempt's telemetry.
25enum class PairingAdviceCode : uint8_t {
26 NONE, ///< Nothing actionable to report.
27 ONE_WAY_PAIRING_TRAFFIC, ///< A 1W remote's PROG gesture was overheard. Proves a gesture happened,
28 ///< not the device's state: the same frames also precede successful
29 ///< pairings (issue #19).
30 CHANNEL_BUSY_LBT_DELAYED, ///< LBT retries were consumed while a source kept repeating; the
31 ///< reported subject_node is a best-effort correlation (LBT's
32 ///< carrier-sense doesn't parse frames, so the actual jammer may
33 ///< not be among the parsed events attributed here).
34 FOREIGN_CONTROLLER_PAIRING, ///< A discovery response was seen addressed to another controller.
35 RF_SILENT, ///< Nothing at all was heard during the whole window.
36 KEY_PUSH_NOT_ACCEPTED, ///< The device does not take a pushed key: it answered 0x32 with
37 ///< RESULT_COMMAND_UNKNOWN, or it answered discovery as a remote
38 ///< controller and the push failed. Suggests `pairing_key_transfer: pull` or `auto`.
39 KEY_TRANSFER_REFUSED, ///< The device answered the key transfer with any other CMD_ERROR_RESP.
40 KEY_PULL_NOT_OFFERED, ///< `pairing_key_transfer: pull` was refused with CMD_ERROR_RESP: the
41 ///< device does not hand out its key. Suggests forcing `pairing_key_transfer: push`.
42 KEY_PULL_UNPROVEN, ///< The device answered the pull with a key, but sent no 0x3D that
43 ///< verifies under it; the key is not printed.
44 PULLED_KEY_RECEIVED, ///< The pull got a different key that the device proved, and a roll-call
45 ///< under it found no registrable device (outcome KEY_RECEIVED). Says to
46 ///< put the key into the controlling entity's `system_key:`.
47 HUB_KEY_NO_RESPONDER, ///< The pull returned the hub's own key from a node no platform drives, and no
48 ///< device that holds it answered (outcome HUB_KEY_RETURNED). Says to run Scan
49 ///< Paired Devices.
50};
51
52/// @brief One piece of advice, with the node/RSSI it pertains to (if any).
55 uint8_t subject_node[NODE_ID_SIZE]{}; ///< Node the advice is about; zero if not applicable.
56 int16_t rssi{0}; ///< RSSI associated with the observation; zero if not applicable.
57 bool has_result_code{false}; ///< True when result_code carries the device's CMD_ERROR_RESP byte.
58 uint8_t result_code{0}; ///< CMD_ERROR_RESP byte the advice is about.
59 bool answered_as_controller{false}; ///< True when the device answered discovery as a remote controller.
60};
61
62/// @brief Maximum number of advice entries a single attempt can produce (each advice source adds at most one).
63static constexpr uint8_t PAIRING_ADVICE_MAX = 6;
64
65/// @brief Inspect a completed pairing attempt's telemetry and produce actionable advice.
66///
67/// Read-only: never mutates telemetry, never touches the radio or pairing state machine.
68/// @param telemetry Completed (or in-progress) telemetry for one pairing attempt.
69/// @param own_node_id This controller's node ID, used to detect discovery responses addressed
70/// to a different controller.
71/// @param out Output buffer, must hold at least PAIRING_ADVICE_MAX entries.
72/// @return Number of advice entries written to `out` (0 if nothing actionable was found).
73uint8_t analyze_pairing_telemetry(const PairingTelemetry &telemetry, const uint8_t own_node_id[NODE_ID_SIZE],
74 PairingAdvice out[PAIRING_ADVICE_MAX]);
75
76/// @return Short, stable code string for the `;advice=` result-sensor field (e.g. "1w_traffic").
78
79/// @return The full actionable, human-readable WARN message for one piece of advice.
80std::string pairing_advice_message(const PairingAdvice &advice);
81
82} // namespace advisor
83} // namespace home_io_control
84} // namespace esphome
Fixed-size per-attempt telemetry recorder for the pairing flow.
std::string pairing_advice_message(const PairingAdvice &advice)
static constexpr uint8_t PAIRING_ADVICE_MAX
Maximum number of advice entries a single attempt can produce (each advice source adds at most one).
const char * pairing_advice_code_name(PairingAdviceCode code)
PairingAdviceCode
Actionable diagnosis derived from a completed pairing attempt's telemetry.
@ PULLED_KEY_RECEIVED
The pull got a different key that the device proved, and a roll-call under it found no registrable de...
@ RF_SILENT
Nothing at all was heard during the whole window.
@ KEY_PULL_NOT_OFFERED
pairing_key_transfer: pull was refused with CMD_ERROR_RESP: the device does not hand out its key.
@ KEY_PULL_UNPROVEN
The device answered the pull with a key, but sent no 0x3D that verifies under it; the key is not prin...
@ KEY_PUSH_NOT_ACCEPTED
The device does not take a pushed key: it answered 0x32 with RESULT_COMMAND_UNKNOWN,...
@ KEY_TRANSFER_REFUSED
The device answered the key transfer with any other CMD_ERROR_RESP.
@ FOREIGN_CONTROLLER_PAIRING
A discovery response was seen addressed to another controller.
@ ONE_WAY_PAIRING_TRAFFIC
A 1W remote's PROG gesture was overheard.
@ HUB_KEY_NO_RESPONDER
The pull returned the hub's own key from a node no platform drives, and no device that holds it answe...
@ CHANNEL_BUSY_LBT_DELAYED
LBT retries were consumed while a source kept repeating; the reported subject_node is a best-effort c...
uint8_t analyze_pairing_telemetry(const PairingTelemetry &telemetry, const uint8_t own_node_id[NODE_ID_SIZE], PairingAdvice out[PAIRING_ADVICE_MAX])
Inspect a completed pairing attempt's telemetry and produce actionable advice.
Structured per-attempt telemetry recorder for the pairing flow.
IO-Homecontrol 2W frame container: control bytes, IoFrame and (de)serialization.
One piece of advice, with the node/RSSI it pertains to (if any).
uint8_t subject_node[NODE_ID_SIZE]
Node the advice is about; zero if not applicable.
bool has_result_code
True when result_code carries the device's CMD_ERROR_RESP byte.
bool answered_as_controller
True when the device answered discovery as a remote controller.
uint8_t result_code
CMD_ERROR_RESP byte the advice is about.
int16_t rssi
RSSI associated with the observation; zero if not applicable.