23#include "esphome/core/component.h"
24#include "esphome/core/hal.h"
25#include "esphome/components/spi/spi.h"
26#include "esphome/components/button/button.h"
58 public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_LOW,
59 spi::CLOCK_PHASE_LEADING, spi::DATA_RATE_8MHZ>,
63 void setup()
override;
80 uint8_t
spi_transfer(uint8_t data)
override {
return this->transfer_byte(data); }
83 void spi_write(uint8_t data)
override { this->write_byte(data); }
86 uint8_t
spi_read()
override {
return this->read_byte(); }
132 virtual void add_device(
const std::string &device_id);
138 virtual void add_device(
const std::string &device_id,
DeviceType type, uint8_t subtype,
bool inverted);
161 virtual bool set_device_tilt(
const std::string &device_id, uint8_t tilt_percent);
242 uint32_t retry_after_fail_ms = 0);
415 bool all_zero =
true;
418 all_zero = all_zero &&
id[i] == 0;
419 all_ff = all_ff &&
id[i] == UINT8_MAX;
421 return !all_zero && !all_ff;
432 snprintf(buf,
sizeof(buf),
"%.0f%%", pos);
The main IO-Homecontrol component.
InternalGPIOPin * fem_en_pin_
Front-end module enable.
InternalGPIOPin * fem_pa_pin_
Front-end module PA switch.
std::map< std::string, IoDevice > devices_
void set_tx_power(uint8_t power)
Set transmit power (dBm).
InternalGPIOPin * dio4_pin_
SX1276 DIO4 preamble detect (optional).
void reset_exchange_debug_(uint8_t request_cmd)
void set_node_id(const std::string &id)
Set the controller's node ID (hex string).
virtual bool set_device_tilt(const std::string &device_id, uint8_t tilt_percent)
Send a tilt command to a tilt‑capable cover.
InternalGPIOPin * dio1_pin_
SX1262 DIO1 interrupt.
bool send_and_receive_(const IoFrame &request, IoFrame &response, uint32_t freq)
Main request/response exchange with retry and automatic authentication.
virtual bool set_switch_state(const std::string &device_id, bool on)
Semantic binary helper for switch entities.
bool transmit_request_(const IoFrame &request, uint32_t freq, uint16_t preamble, exchange::OutboundExchangeContext &ctx)
Wrap transmit_frame_ and mark context failed on error.
void set_rst_pin(InternalGPIOPin *pin)
Set the radio reset pin.
decisions::PairingDiscoveryDisposition run_discovery_phase_(pairing::PairingContext &context)
Phase 1: broadcast discovery (0x28) and wait for a device response (0x29).
virtual void queue_set_device_tilt(const std::string &device_id, uint8_t tilt_percent)
Queue an async tilt update; returns immediately, executed in loop().
void begin_status_poll_tracking_(const std::string &device_id, uint32_t initial_delay_ms)
Begin bounded follow-up polling for a device after a command or overheard remote activity.
void dump_config() override
Dump configuration and radio debug info to the log.
InternalGPIOPin * rst_pin_
virtual void register_device_callback(DeviceUpdateCallback cb)
Register a callback invoked when any device updates.
void set_pa_pin(uint8_t pa_pin)
Set PA boost pin configuration.
void record_exchange_debug_(const char *stage, uint8_t tries, bool saw_challenge)
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 spi_enable() override
Enable the SPI bus.
virtual void add_device(const std::string &device_id)
Add a device to the registry by device ID only (legacy/delegating overload).
void set_fem_en_pin(InternalGPIOPin *pin)
Set the front‑end module enable pin.
InternalGPIOPin * vfem_pin_
Front-end module power.
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,...
void schedule_status_poll_(const std::string &device_id, uint32_t delay_ms)
Schedule a delayed status poll for a registered device using the Component timeout API.
void hop_frequency_()
Hop to the next channel in the 3‑channel sequence: CH1 → CH2 → CH3 → CH1.
void add_linked_remote(const std::string &remote_id, const std::string &device_id)
Declare that a remote (identified by its node ID) controls a registered device.
bool finalize_pairing_configuration_(pairing::PairingContext &context)
Phase 3: send SetConfig1 (0x6F) to finalize device configuration.
bool execute_request_and_update_(const std::string &device_id, const IoFrame &request, bool warn_on_no_response, uint32_t retry_after_fail_ms=0)
Shared request/response helper for high-level operations.
void set_dio1_pin(InternalGPIOPin *pin)
Set the DIO1 interrupt pin (SX1262).
decisions::ExchangeFirstResponseDisposition wait_for_first_response_(const IoFrame &request, exchange::OutboundExchangeContext &ctx)
Wait loop for the first response packet; classifies via decisions::classify_exchange_first_response.
PendingOperationType
Type of queued pending operation for the main loop.
@ SET_TILT
Queue a set_device_tilt call (tilt percentage 0–100).
@ SET_LIGHT_STATE
Queue a set_light_state call (binary on/off).
@ SET_SWITCH_STATE
Queue a set_switch_state call (binary on/off).
@ DISCOVER_AND_PAIR
Queue a discover_and_pair call (starts 3‑phase pairing flow).
@ REQUEST_STATUS
Queue a request_device_status call (poll for current position).
@ SET_POSITION
Queue a set_device_position call (position 0–100 or special values).
void set_radio_type(const std::string &type)
Set radio type ("sx1276" or "sx1262"); empty string means auto‑detect.
void loop() override
Main loop: process pending operations and drive radio state machine.
void log_exchange_debug_(const char *device_id) const
bool wait_for_key_challenge_(uint32_t timeout_ms, RadioRxPacket &packet, IoFrame &challenge_frame, const uint8_t device_node_id[NODE_ID_SIZE])
Wait for a key-challenge (0x3C) from target device during pairing key exchange.
decisions::ExchangeFinalResponseDisposition wait_for_final_response_(const IoFrame &request, exchange::OutboundExchangeContext &ctx)
Wait loop for the final authenticated response; uses is_valid_final_response().
std::string radio_type_
"sx1276", "sx1262", or "" (auto-detect)
std::vector< DeviceUpdateCallback > callbacks_
uint8_t system_key_[AES_KEY_SIZE]
float get_setup_priority() const override
Get setup priority (HARDWARE to initialize early).
bool run_key_exchange_phase_(pairing::PairingContext &context)
Phase 2: authenticated key exchange (0x31 → 0x3C → 0x32 → 0x33).
ExchangeDebugInfo last_exchange_debug_
void set_busy_pin(InternalGPIOPin *pin)
Set the BUSY pin (SX1262).
std::deque< PendingOperation > pending_operations_
virtual bool discover_and_pair()
Discover and pair a device that is in pairing mode.
uint8_t spi_read() override
Read one byte (MISO only).
bool transmit_frame_(const IoFrame &frame, uint32_t freq, uint16_t preamble)
Transmit a raw IoFrame on the current frequency with given preamble length.
void set_dio0_pin(InternalGPIOPin *pin)
Set the DIO0 interrupt pin (SX1276).
decisions::PairingDiscoveryDisposition wait_for_discovery_response_(uint32_t timeout_ms, RadioRxPacket &packet, IoFrame &response_frame)
Wait for a discovery response (0x29) during pairing.
void set_vfem_pin(InternalGPIOPin *pin)
Set the VFEM power pin.
void update_device_status_(const IoFrame &frame)
Extract position/status info from a status or status-update frame and merge into device record.
virtual bool set_light_state(const std::string &device_id, bool on)
Semantic binary helper for light entities.
InternalGPIOPin * dio0_pin_
SX1276 DIO0 interrupt.
void set_system_key(const std::string &key)
Set the system key (hex string).
virtual void queue_set_light_state(const std::string &device_id, bool on)
Async form of set_light_state() that keeps radio work serialized on the main loop.
void spi_write(uint8_t data) override
Write one byte (MOSI only).
void set_tcxo_voltage(uint8_t voltage)
Set TCXO voltage for SX1262 (1.8V / 3.3V).
bool handle_authentication_(const IoFrame &request, uint32_t freq, exchange::OutboundExchangeContext &ctx)
Perform challenge-response (TX auth response) after a 0x3C is received.
void notify_device_update_(const std::string &id)
Fire all registered device update callbacks for the given device ID.
std::string system_key_str_
virtual void queue_set_device_position(const std::string &device_id, uint8_t position)
Queue an async position update; returns immediately, executed in loop().
virtual void queue_set_switch_state(const std::string &device_id, bool on)
Async form of set_switch_state() that keeps radio work serialized on the main loop.
void set_dio4_pin(InternalGPIOPin *pin)
Set the DIO4 preamble‑detect pin (SX1276, optional).
virtual bool set_device_position(const std::string &device_id, uint8_t position)
Send a position command to a device.
std::map< std::string, std::vector< std::string > > linked_remotes_
Maps remote node IDs to lists of device IDs they control.
void setup() override
Initialize hardware (radio and device registry).
uint8_t node_id_[NODE_ID_SIZE]
uint8_t spi_transfer(uint8_t data) override
Transfer one byte full‑duplex.
uint8_t tcxo_voltage_
SX1262 TCXO voltage setting (default 1.8 V).
void process_received_packet_(const RadioRxPacket &packet)
Parse received packet, update device state if it's a status frame, and notify covers.
bool authenticate_request_(const IoFrame &request, uint32_t freq)
Handle an inbound authenticated command from a device (status updates, etc.).
void spi_disable() override
Disable the SPI bus.
InternalGPIOPin * busy_pin_
SX1262 BUSY pin.
virtual void queue_discover_and_pair()
Queue a pairing operation; executed in loop() when radio idle.
static void parse_device_from_discovery(const IoFrame &frame, IoDevice &device, std::string &device_id)
Parse a discovery response frame into device metadata and ID.
virtual bool request_device_status(const std::string &device_id)
Request current status from a device.
void set_fem_pa_pin(InternalGPIOPin *pin)
Set the FEM PA switch pin.
Abstract radio driver for IO-Homecontrol.
Interface for SPI bus access.
Pure transition helpers for hub-owned exchange and pairing frame decisions.
Internal exchange-state model for hub-owned authenticated non‑pairing flows.
Internal pairing-state model for hub‑owned discovery and key‑exchange flows.
PairingDiscoveryDisposition
Disposition during pairing discovery phase.
ExchangeFinalResponseDisposition
Disposition for the final response after authentication.
ExchangeFirstResponseDisposition
Disposition for the first response in an authenticated exchange.
static constexpr float UNKNOWN_POSITION
Sentinel value meaning "position is not known yet".
static constexpr uint8_t NODE_ID_SIZE
Device/node addresses are 3 bytes (e.g., "123ABC").
DeviceType
Device type identifiers reported by IO‑Homecontrol products.
constexpr size_t POSITION_TEXT_BUFFER_SIZE
Buffer for formatted position strings such as "100%".
std::function< void(const std::string &device_id, const IoDevice &device)> DeviceUpdateCallback
Callback type for notifying covers of device state changes.
std::string format_position(float pos)
Format a position float as a human‑readable string (e.g.
bool stored_node_id_is_valid(const uint8_t id[NODE_ID_SIZE])
Check if a stored node ID is valid (not all-zero, not all-0xFF).
constexpr uint8_t DEFAULT_TCXO_VOLTAGE_SETTING_1P8V
SX1262 DIO3 setting value for a 1.8 V TCXO.
static constexpr uint8_t AES_KEY_SIZE
AES-128 key size.
constexpr uint8_t DEFAULT_PA_PIN_PA_BOOST
SX1276 PA_CONFIG selector for the PA_BOOST output path.
constexpr uint8_t DEFAULT_TX_POWER_DBM
Default TX power used unless YAML overrides it.
IO-Homecontrol 2W protocol definitions.
Radio abstraction layer for IO-Homecontrol.
Debug snapshot of the last exchange attempt.
uint8_t request_cmd
Command ID of the original request (e.g., CMD_EXECUTE=0x00).
uint8_t tries
Try number (1‑based; increments on each retry within EXCHANGE_RETRY_COUNT).
bool capture_rx_done
True if RxDone interrupt fired (packet fully received).
const char * stage
Current stage name (e.g., "TX_REQUEST", "WAIT_FIRST_RESPONSE", "FAILED").
uint8_t capture_frame_len
Length of the parsed protocol frame after recovery/UART decoding.
uint8_t capture_reported_len
Length reported by the radio's packet engine.
uint16_t capture_irq_status
Raw IRQ status register value from the radio chip.
bool capture_crc_error
True if CRC error flagged (SX1262 only; SX1276 IoHomeOn filters in hardware).
uint8_t capture_packet_status
Packet status byte (chip-specific; SX1262 includes CRC flag).
int16_t capture_rssi_dbm
Received signal strength of the captured packet (dBm, negative).
bool capture_valid
True if radio capture data is valid for the last packet seen.
bool saw_challenge
True if a challenge (0x3C) was seen during the exchange.
uint32_t capture_freq_hz
RF frequency of the captured packet (Hz).
A single queued operation to be processed in loop().
std::string device_id
Target device ID (hex string, e.g., "123ABC").
PendingOperationType type
Operation type (determines which queue handler to invoke).
uint8_t position
Position/tilt value (0–100) or binary state (ON=0, OFF=100 for lights/switches).
Runtime state of a paired IO‑Homecontrol device.
Parsed IO‑Homecontrol frame (CTRL0/1 + addresses + command + data).
Raw packet received from the radio.
Context carried across one outbound authenticated exchange.
Context object that lives for the duration of a single pairing attempt.