Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
tuning_registry.cpp
Go to the documentation of this file.
1/// @file tuning_registry.cpp
2/// @brief Parameter tables and lookup helpers for the tuning registry.
3/// @ingroup hioc_tuning
4///
5/// The getter/setter lambdas below carry all per-parameter parsing (bandwidth enum
6/// decode, the comma-separated command list with whitespace trimming, the
7/// destination/payload/low-power mapping). Keeping them here makes the hub's tuning
8/// dispatch methods pure table lookups.
9
10#include "tuning_registry.h"
11
12#include "proto_codecs.h" // ADDRESS_SUFFIX_DISCOVERY / ADDRESS_SUFFIX_BROADCAST
13
14#include <cstddef>
15#include <iterator>
16
17namespace esphome {
18namespace home_io_control {
19
20// === Numeric parameters (19) ===================================================
21// Each row narrows the incoming float to the field's storage type, matching the
22// original static_cast in update_tuning_number().
23static constexpr TuningNumberParam NUMBER_PARAMS[] = {
24 {"sx1262_response_preamble", [](const TuningConfig &t) { return static_cast<float>(t.sx1262_response_preamble); },
25 [](TuningConfig &t, float v) { t.sx1262_response_preamble = static_cast<uint16_t>(v); }, true},
26 {"sx1262_post_tx_settle_us", [](const TuningConfig &t) { return static_cast<float>(t.sx1262_post_tx_settle_us); },
27 [](TuningConfig &t, float v) { t.sx1262_post_tx_settle_us = static_cast<uint16_t>(v); }, true},
28 {"sx1276_response_preamble", [](const TuningConfig &t) { return static_cast<float>(t.sx1276_response_preamble); },
29 [](TuningConfig &t, float v) { t.sx1276_response_preamble = static_cast<uint16_t>(v); }, true},
30 {"sx1276_discovery_hop_slice_ms",
31 [](const TuningConfig &t) { return static_cast<float>(t.sx1276_discovery_hop_slice_ms); },
32 [](TuningConfig &t, float v) { t.sx1276_discovery_hop_slice_ms = static_cast<uint16_t>(v); }, false},
33 {"sx1262_discovery_hop_slice_ms",
34 [](const TuningConfig &t) { return static_cast<float>(t.sx1262_discovery_hop_slice_ms); },
35 [](TuningConfig &t, float v) { t.sx1262_discovery_hop_slice_ms = static_cast<uint16_t>(v); }, false},
36 {"lr1121_response_preamble", [](const TuningConfig &t) { return static_cast<float>(t.lr1121_response_preamble); },
37 [](TuningConfig &t, float v) { t.lr1121_response_preamble = static_cast<uint16_t>(v); }, true},
38 {"lr1121_post_tx_settle_us", [](const TuningConfig &t) { return static_cast<float>(t.lr1121_post_tx_settle_us); },
39 [](TuningConfig &t, float v) { t.lr1121_post_tx_settle_us = static_cast<uint16_t>(v); }, true},
40 {"lr1121_discovery_hop_slice_ms",
41 [](const TuningConfig &t) { return static_cast<float>(t.lr1121_discovery_hop_slice_ms); },
42 [](TuningConfig &t, float v) { t.lr1121_discovery_hop_slice_ms = static_cast<uint16_t>(v); }, false},
43 // false: unlike sx1262/sx1276/lr1121_response_preamble, this field is not chip-specific and
44 // is never cached into a radio driver via apply_tuning() — key_extraction_responder.cpp reads it
45 // straight out of TuningConfig at TX time, the same way the *_discovery_hop_slice_ms fields
46 // above are read live rather than applied.
47 {"cold_broadcast_reply_preamble",
48 [](const TuningConfig &t) { return static_cast<float>(t.cold_broadcast_reply_preamble); },
49 [](TuningConfig &t, float v) { t.cold_broadcast_reply_preamble = static_cast<uint16_t>(v); }, false},
50 // false: not chip-specific and never cached into a radio driver via apply_tuning() — the
51 // exchange engine reads it straight out of TuningConfig at TX time, the same as
52 // cold_broadcast_reply_preamble above.
53 {"normal_start_preamble", [](const TuningConfig &t) { return static_cast<float>(t.normal_start_preamble); },
54 [](TuningConfig &t, float v) { t.normal_start_preamble = static_cast<uint16_t>(v); }, false},
55 {"lbt_max_retries", [](const TuningConfig &t) { return static_cast<float>(t.lbt_max_retries); },
56 [](TuningConfig &t, float v) { t.lbt_max_retries = static_cast<uint8_t>(v); }, false},
57 {"lbt_rssi_threshold_dbm", [](const TuningConfig &t) { return static_cast<float>(t.lbt_rssi_threshold_dbm); },
58 [](TuningConfig &t, float v) { t.lbt_rssi_threshold_dbm = static_cast<int16_t>(v); }, false},
59 {"exchange_start_response_wait_ms",
60 [](const TuningConfig &t) { return static_cast<float>(t.exchange_start_response_wait_ms); },
61 [](TuningConfig &t, float v) { t.exchange_start_response_wait_ms = static_cast<uint16_t>(v); }, false},
62 {"exchange_response_wait_ms", [](const TuningConfig &t) { return static_cast<float>(t.exchange_response_wait_ms); },
63 [](TuningConfig &t, float v) { t.exchange_response_wait_ms = static_cast<uint16_t>(v); }, false},
64 {"exchange_total_budget_ms", [](const TuningConfig &t) { return static_cast<float>(t.exchange_total_budget_ms); },
65 [](TuningConfig &t, float v) { t.exchange_total_budget_ms = static_cast<uint16_t>(v); }, false},
66 // false: read straight out of TuningConfig at TX time by PairingEngine::run_discovery_phase_(),
67 // the same as normal_start_preamble/cold_broadcast_reply_preamble above.
68 {"pairing_discovery_preamble",
69 [](const TuningConfig &t) { return static_cast<float>(t.pairing_discovery_preamble); },
70 [](TuningConfig &t, float v) { t.pairing_discovery_preamble = static_cast<uint16_t>(v); }, false},
71 {"pairing_discovery_wait_ms", [](const TuningConfig &t) { return static_cast<float>(t.pairing_discovery_wait_ms); },
72 [](TuningConfig &t, float v) { t.pairing_discovery_wait_ms = static_cast<uint16_t>(v); }, false},
73 {"pairing_discovery_initial_dwell_ms",
74 [](const TuningConfig &t) { return static_cast<float>(t.pairing_discovery_initial_dwell_ms); },
75 [](TuningConfig &t, float v) { t.pairing_discovery_initial_dwell_ms = static_cast<uint16_t>(v); }, false},
76 {"pairing_key_exchange_retries",
77 [](const TuningConfig &t) { return static_cast<float>(t.pairing_key_exchange_retries); },
78 [](TuningConfig &t, float v) { t.pairing_key_exchange_retries = static_cast<uint8_t>(v); }, false},
79};
80
81// === Select parameters (7) =====================================================
82// The setters return false only when the option string is unparseable AND leaving the
83// value untouched is the intended behavior (currently just the bandwidth enum). The
84// destination/payload setters return false for unrecognized values so no radio re-apply
85// is triggered; that has no observable effect since neither applies to the radio.
86static constexpr TuningSelectParam SELECT_PARAMS[] = {
87 {"sx1262_rx_bandwidth", [](const TuningConfig &t) { return sx1262_bandwidth_to_string(t.sx1262_rx_bandwidth); },
88 [](TuningConfig &t, const std::string &v) -> bool {
90 if (!bw.has_value())
91 return false;
92 t.sx1262_rx_bandwidth = bw.value();
93 return true;
94 },
95 true},
96 {"sx1276_rx_bandwidth", [](const TuningConfig &t) { return sx1276_bandwidth_to_string(t.sx1276_rx_bandwidth); },
97 [](TuningConfig &t, const std::string &v) -> bool {
99 if (!bw.has_value())
100 return false;
101 t.sx1276_rx_bandwidth = bw.value();
102 return true;
103 },
104 true},
105 {"lr1121_rx_bandwidth", [](const TuningConfig &t) { return lr1121_bandwidth_to_string(t.lr1121_rx_bandwidth); },
106 [](TuningConfig &t, const std::string &v) -> bool {
107 auto bw = lr1121_bandwidth_from_string(v);
108 if (!bw.has_value())
109 return false;
110 t.lr1121_rx_bandwidth = bw.value();
111 return true;
112 },
113 true},
114 {"pairing_discovery_commands",
115 [](const TuningConfig &t) { return discovery_commands_to_csv(t.pairing_discovery_commands); },
116 [](TuningConfig &t, const std::string &v) -> bool {
118 // Support both individual commands and comma-separated preset strings.
119 std::size_t start = 0;
120 while (start < v.size()) {
121 const std::size_t comma = v.find(',', start);
122 std::string token = v.substr(start, comma - start);
123 // Trim whitespace.
124 token.erase(0, token.find_first_not_of(" \t"));
125 token.erase(token.find_last_not_of(" \t") + 1);
126 auto cmd = discovery_command_from_string(token);
127 if (cmd.has_value())
128 t.pairing_discovery_commands.push_back(cmd.value());
129 if (comma == std::string::npos)
130 break;
131 start = comma + 1;
132 }
133 return true;
134 },
135 false},
136 {"pairing_discovery_destination",
137 [](const TuningConfig &t) {
138 return discovery_destination_to_string(t.pairing_discovery_destination_auto,
139 t.pairing_discovery_destination.data());
140 },
141 [](TuningConfig &t, const std::string &v) -> bool {
142 if (v == "auto") {
144 return true;
145 }
146 if (v == "0x00003B") {
149 return true;
150 }
151 if (v == "0x00003F") {
154 return true;
155 }
156 return false;
157 },
158 false},
159 {"pairing_discovery_payload",
160 [](const TuningConfig &t) {
161 return discovery_payload_to_string(t.pairing_discovery_payload_enabled, t.pairing_discovery_payload);
162 },
163 [](TuningConfig &t, const std::string &v) -> bool {
164 if (v == "none") {
166 return true;
167 }
168 if (v == "0x00") {
171 return true;
172 }
173 return false;
174 },
175 false},
176 {"pairing_discovery_low_power",
177 [](const TuningConfig &t) -> std::string { return t.pairing_discovery_low_power ? "On" : "Off"; },
178 [](TuningConfig &t, const std::string &v) -> bool {
179 t.pairing_discovery_low_power = (v == "On");
180 return true;
181 },
182 false},
183};
184
185const TuningNumberParam *find_tuning_number(const std::string &name) {
186 for (const auto &param : NUMBER_PARAMS) {
187 if (name == param.name)
188 return &param;
189 }
190 return nullptr;
191}
192
193const TuningSelectParam *find_tuning_select(const std::string &name) {
194 for (const auto &param : SELECT_PARAMS) {
195 if (name == param.name)
196 return &param;
197 }
198 return nullptr;
199}
200
205
206} // namespace home_io_control
207} // namespace esphome
std::string discovery_commands_to_csv(const std::vector< DiscoveryCommand > &commands)
Format the ordered discovery command list as a UI/preset option string.
std::optional< LR1121RxBandwidth > lr1121_bandwidth_from_string(const std::string &value)
Convert a YAML LR1121 bandwidth string to the enum value.
const TuningNumberParam * tuning_number_params_end()
const TuningSelectParam * tuning_select_params_end()
std::string discovery_payload_to_string(bool payload_enabled, uint8_t payload)
Format a payload option for YAML/logs.
static constexpr uint8_t ADDRESS_SUFFIX_BROADCAST
Suffix for "all devices of this type" broadcast.
std::optional< DiscoveryCommand > discovery_command_from_string(const std::string &value)
Parse a discovery command string (e.g., "0x28") into the enum.
static constexpr uint8_t ADDRESS_SUFFIX_DISCOVERY
Suffix for discovery-related broadcasts.
std::string lr1121_bandwidth_to_string(LR1121RxBandwidth bw)
Format an LR1121 bandwidth enum as its YAML/UI option string (bare kHz number).
std::optional< SX1262RxBandwidth > sx1262_bandwidth_from_string(const std::string &value)
Convert a YAML bandwidth string to the enum value.
std::string sx1276_bandwidth_to_string(SX1276RxBandwidth bw)
Format an SX1276 bandwidth enum as its YAML/UI option string (bare kHz number).
const TuningNumberParam * tuning_number_params_begin()
std::string sx1262_bandwidth_to_string(SX1262RxBandwidth bw)
Format a bandwidth enum as its YAML/UI option string (bare kHz number, e.g.
const TuningSelectParam * find_tuning_select(const std::string &name)
Look up a select tuning parameter by name; returns nullptr if unknown.
const TuningSelectParam * tuning_select_params_begin()
static constexpr TuningSelectParam SELECT_PARAMS[]
const TuningNumberParam * find_tuning_number(const std::string &name)
Look up a numeric tuning parameter by name; returns nullptr if unknown.
static constexpr TuningNumberParam NUMBER_PARAMS[]
std::optional< SX1276RxBandwidth > sx1276_bandwidth_from_string(const std::string &value)
Convert a YAML SX1276 bandwidth string to the enum value.
std::string discovery_destination_to_string(bool destination_auto, const uint8_t destination[NODE_ID_SIZE])
Format a destination option for YAML/logs.
Device-name, address-classification and 1W-frame codecs.
All runtime tunable parameters for pairing and radio diagnostics.
uint16_t pairing_discovery_initial_dwell_ms
Initial dwell on CH2 before discovery hopping begins.
bool pairing_discovery_destination_auto
When true, map commands to conventional addresses.
SX1276RxBandwidth sx1276_rx_bandwidth
SX1276 RX bandwidth selector.
uint16_t normal_start_preamble
Preamble for a directed start frame to a non-low-power target.
int16_t lbt_rssi_threshold_dbm
LBT channel-free threshold (dBm).
SX1262RxBandwidth sx1262_rx_bandwidth
SX1262 RX bandwidth selector.
uint16_t cold_broadcast_reply_preamble
Preamble for a start-flagged key-extraction broadcast reply (0x29).
std::vector< uint8_t > pairing_discovery_destination
Destination when auto=false.
std::vector< DiscoveryCommand > pairing_discovery_commands
Ordered discovery commands.
uint16_t lr1121_discovery_hop_slice_ms
Per-channel dwell while LR1121 discovery hops.
uint16_t sx1276_response_preamble
SX1276 response preamble in bytes.
uint16_t exchange_response_wait_ms
Reply window for continuation frames and the post-auth final response.
uint16_t sx1262_post_tx_settle_us
Delay after SX1262 TX before RX (µs).
uint8_t pairing_key_exchange_retries
Retries for the authenticated key exchange phase.
bool pairing_discovery_low_power
Set LOW_POWER flag in discovery frames.
uint16_t sx1276_discovery_hop_slice_ms
Per-channel dwell while SX1276 discovery hops.
uint16_t exchange_start_response_wait_ms
Reply window for a start frame (wakes a sleeping device).
uint16_t sx1262_response_preamble
SX1262 response preamble in bytes.
uint16_t pairing_discovery_preamble
Preamble for the discovery broadcast (0x28/0x2E) start frame.
uint16_t pairing_discovery_wait_ms
Total wait window after sending discovery commands.
bool pairing_discovery_payload_enabled
Whether the optional payload is enabled.
LR1121RxBandwidth lr1121_rx_bandwidth
LR1121 RX bandwidth selector.
uint16_t lr1121_post_tx_settle_us
Delay after LR1121 TX before RX (µs).
uint8_t lbt_max_retries
LBT retries before forced TX.
uint16_t sx1262_discovery_hop_slice_ms
Per-channel dwell while SX1262 discovery hops.
uint16_t exchange_total_budget_ms
Ceiling on one whole exchange, retries included.
uint16_t lr1121_response_preamble
LR1121 response preamble in bytes.
uint8_t pairing_discovery_payload
Optional payload byte (used for 0x2E).
One numeric tuning parameter: its wire name plus accessors over TuningConfig.
One select tuning parameter: its wire name plus string accessors over TuningConfig.
Table-driven registry of runtime tuning parameters.