32constexpr uint32_t BLOCKING_WARNING_THRESHOLD_MS =
60 this->warn_if_blocking_over_ = BLOCKING_WARNING_THRESHOLD_MS;
61#ifdef IOHOME_UNSAFE_LOG_KEY_MATERIAL
65 ESP_LOGE(
detail::TAG,
"########################################");
66 ESP_LOGE(
detail::TAG,
"IOHOME_UNSAFE_LOG_KEY_MATERIAL IS ENABLED -- FRAME LOGS EXPOSE YOUR SYSTEM KEY");
67 ESP_LOGE(
detail::TAG,
"This build is NOT safe to run normally or share logs from. Rebuild without this");
68 ESP_LOGE(
detail::TAG,
"flag as soon as you are done capturing.");
69 ESP_LOGE(
detail::TAG,
"########################################");
75 this->mark_failed(LOG_STR(
"Invalid node_id or system_key configuration"));
81 const char *chip_name_for_log =
nullptr;
83 if (this->
radio_ ==
nullptr) {
85 this->mark_failed(LOG_STR(
"Radio driver selection/allocation failed (see earlier log for details)"));
89#ifdef IOHOME_LR1121_FIRMWARE_UPDATE
93 this->run_lr1121_boot_time_bootloader_read_();
96 if (!this->
radio_->init()) {
99#ifdef IOHOME_LR1121_FIRMWARE_UPDATE
103 this->cache_lr1121_flash_verdict_();
106 this->mark_failed(LOG_STR(
"Radio hardware initialization failed (see earlier log for details)"));
110#ifdef IOHOME_LR1121_FIRMWARE_UPDATE
111 this->cache_lr1121_flash_verdict_();
131 *chip_name_out =
"LR1121";
133 ESP_LOGE(
detail::TAG,
"LR1121 requires busy_pin and dio1_pin (dio1_pin carries the chip's DIO9 IRQ line)");
136 auto *radio =
new (std::nothrow)
138 if (radio ==
nullptr)
139 ESP_LOGE(
detail::TAG,
"Failed to allocate LR1121 radio driver");
144 *chip_name_out =
"SX1262";
146 ESP_LOGE(
detail::TAG,
"SX1262 requires busy_pin and dio1_pin");
152 if (radio ==
nullptr)
153 ESP_LOGE(
detail::TAG,
"Failed to allocate SX1262 radio driver");
158 *chip_name_out =
"SX1276";
163 auto *radio =
new (std::nothrow)
165 if (radio ==
nullptr)
166 ESP_LOGE(
detail::TAG,
"Failed to allocate SX1276 radio driver");
170 *chip_name_out =
"unknown";
183 if (this->
radio_ ==
nullptr)
196 if (param ==
nullptr) {
197 ESP_LOGW(
detail::TAG,
"Unknown tuning number parameter: %s", name.c_str());
213 if (param ==
nullptr) {
214 ESP_LOGW(
detail::TAG,
"Unknown tuning select parameter: %s", name.c_str());
231 if (param ==
nullptr) {
232 ESP_LOGW(
detail::TAG,
"Unknown tuning number parameter: %s", name.c_str());
246 if (param ==
nullptr) {
247 ESP_LOGW(
detail::TAG,
"Unknown tuning select parameter: %s", name.c_str());
266 bool const ok = this->
exchange_engine_.send_and_receive(request, response, freq);
283 this->
poll_policy_.set_interval(device_id, poll_interval_ms);
287 uint32_t
const now = millis();
288 uint32_t
const backoff_ms = this->
poll_policy_.on_exchange_failed(device_id, auth_like, now);
289 if (backoff_ms > 0) {
291 "Background status poll backoff for device %s: delay=%" PRIu32
292 " ms auth_like=%s status_failures=%u auth_failures=%u",
293 device_id.c_str(), backoff_ms, YESNO(auth_like), this->
poll_policy_.get_status_poll_failures(device_id),
294 this->poll_policy_.get_auth_poll_failures(device_id));
307 this->
registry_.set_dimmable(device_id, dimmable);
321 if (this->
radio_->check_for_packet(packet))
352 LOG_PIN(
" RST Pin: ", this->
rst_pin_);
362 if (this->
registry_.linked_remote_count() > 0) {
364 this->
registry_.for_each_linked_remote([](
const std::string &remote_id,
const std::vector<std::string> &devices) {
365 for (
const auto &device_id : devices)
366 ESP_LOGCONFIG(
"home_io_control",
" - remote %s -> device %s", remote_id.c_str(), device_id.c_str());
370 if (this->
radio_ !=
nullptr)
371 this->
radio_->dump_debug();
373#ifdef IOHOME_LR1121_FIRMWARE_UPDATE
374 this->dump_lr1121_firmware_update_debug_();
InternalGPIOPin * fem_en_pin_
Front-end module enable.
InternalGPIOPin * fem_pa_pin_
Front-end module PA switch.
InternalGPIOPin * dio4_pin_
SX1276 DIO4 preamble detect (optional).
InternalGPIOPin * dio1_pin_
SX1262 DIO1 interrupt; also carries the LR1121's DIO9 IRQ line.
bool send_and_receive_(const IoFrame &request, IoFrame &response, uint32_t freq)
Main request/response exchange with retry and automatic authentication.
void dump_config() override
Dump configuration and radio debug info to the log.
void update_tuning_select(const std::string &name, const std::string &value)
Receive a select tuning update from a HA select entity.
InternalGPIOPin * rst_pin_
virtual IoDevice * get_device(const std::string &device_id)
Retrieve a device by ID; returns nullptr if not found.
virtual void queue_request_device_status(const std::string &device_id)
Queue an async status request; returns immediately, executed in loop().
void update_tuning_number(const std::string &name, float value)
Receive a numeric tuning update from a HA number entity.
virtual void add_device(const std::string &device_id)
Add a device to the registry by device ID only (undeclared/legacy path).
InternalGPIOPin * vfem_pin_
Front-end module power.
TuningConfig tuning_
Runtime tuning overrides.
virtual void set_device_status_poll_interval(const std::string &device_id, uint32_t poll_interval_ms)
Configure the optional follow-up polling interval for a registered device.
void process_pending_operation_()
Pop next pending operation from the queue and execute it (set position, request status,...
StatusPollPolicy poll_policy_
void hop_frequency_()
Delegate channel hop to ExchangeEngine (which owns last_hop_us_).
virtual void set_device_dimmable(const std::string &device_id, bool dimmable)
Set a device's dimmable flag (see IoDevice::dimmable).
float get_tuning_number_value(const std::string &name) const
Current value of a numeric tuning parameter, used to seed a HA number entity on boot.
bool defer_background_poll_() const
Whether loop() should skip dispatching the queue this iteration because the pending work is a backgro...
void loop() override
Main loop: process pending operations and drive radio state machine.
void schedule_background_poll_backoff_(const std::string &device_id, bool auth_like)
Apply backoff after a failed background status poll and log the result.
std::string radio_type_
"sx1276", "sx1262", or "lr1121"; required by the YAML schema.
uint8_t system_key_[AES_KEY_SIZE]
RadioDriver * select_and_construct_radio_(const char **chip_name_out)
Select and construct the radio driver named by the required radio_type config field.
bool transmit_frame_(const IoFrame &frame, uint32_t freq, uint16_t preamble)
Transmit a raw IoFrame on the current frequency with given preamble length.
bool radio_test_mode_
When true, loop() is suspended for loopback testing.
ExchangeEngine exchange_engine_
Owns all authenticated exchange and LBT/hop logic.
InternalGPIOPin * dio0_pin_
SX1276 DIO0 interrupt.
void notify_device_update_(const std::string &id)
Fire all registered device update callbacks for the given device ID.
std::string system_key_str_
std::string get_tuning_select_value(const std::string &name) const
Current option string of a select tuning parameter, used to seed a HA select entity on boot.
void setup() override
Initialize hardware (radio and device registry).
uint8_t node_id_[NODE_ID_SIZE]
uint8_t tcxo_voltage_
SX1262/LR1121 TCXO voltage setting (default 1.8 V).
void process_received_packet_(const RadioRxPacket &packet)
Parse a received frame, merge supported device state or metadata, and notify callbacks.
bool authenticate_request_(const IoFrame &request, uint32_t freq)
Handle an inbound authenticated command from a device (status updates, etc.).
InternalGPIOPin * busy_pin_
SX1262/LR1121 BUSY pin.
void register_management_actions_()
Register hub-level Home Assistant actions; called from setup().
void apply_tuning_to_radio_()
Apply the current tuning configuration to the active radio driver.
Abstract radio driver for IO-Homecontrol.
LR1121 implementation of RadioDriver.
SX1262 implementation of RadioDriver.
SX1276 implementation of RadioDriver.
Internal helpers shared by the hub implementation .cpp files.
constexpr const char * TAG
Shared log tag for hub-level messages.
static constexpr uint8_t NODE_ID_SIZE
Device/node addresses are 3 bytes (e.g., "123ABC").
static constexpr const char * TAG
std::string tuning_config_full_snapshot(const TuningConfig &cfg)
Format the current tuning configuration as a full one-line snapshot.
const TuningSelectParam * find_tuning_select(const std::string &name)
Look up a select tuning parameter by name; returns nullptr if unknown.
std::string tuning_update_log_line(const std::string &name, const std::string &value)
Format a single tuning update for the log.
const TuningNumberParam * find_tuning_number(const std::string &name)
Look up a numeric tuning parameter by name; returns nullptr if unknown.
static constexpr uint8_t AES_KEY_SIZE
AES-128 key size.
bool hex_to_bytes(const std::string &hex, uint8_t *out, uint8_t len)
Convert a hex string (e.g., "123ABC") to a byte array.
LR1121 radio driver for IO-Homecontrol.
SX1262 radio driver for IO-Homecontrol.
SX1276 radio driver for IO-Homecontrol.
YAML-declared device metadata for registration; defaults match an undeclared device.
Runtime state of a paired IO‑Homecontrol device.
Parsed IO‑Homecontrol frame (CTRL0/1 + addresses + command + data).
Raw packet received from the radio.
One numeric tuning parameter: its wire name plus accessors over TuningConfig.
float(* get)(const TuningConfig &)
Read the current value as a float.
bool applies_to_radio
True if changes must be re-applied to the radio.
void(* set)(TuningConfig &, float)
Write a new value (narrowed to the field type).
One select tuning parameter: its wire name plus string accessors over TuningConfig.
std::string(* get)(const TuningConfig &)
Read the current option string.
bool(* set)(TuningConfig &, const std::string &)
Parse/apply an option; false if unparseable.
bool applies_to_radio
True if changes must be re-applied to the radio.
Runtime tuning configuration for pairing and radio diagnostics.
Table-driven registry of runtime tuning parameters.