Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
esphome::home_io_control Namespace Reference

Namespaces

namespace  advisor
namespace  crypto
namespace  decisions
namespace  detail
namespace  device_feature
 Capability bits carried by a DeviceTypeInfo row and resolved onto a device record.
namespace  device_type_flags
 Bit flags carried by a DeviceTypeInfo row: identity facts. Capabilities are DeviceFeatures.
namespace  exchange
namespace  heating_alias
 Setpoint aliases (low byte of the main parameter, prefix HEATING_ALIAS_MP_PREFIX).
namespace  heating_fp16
 Values of functional parameter 16 on the heat pump: the on/off state.
namespace  heating_level
 Heating levels (low byte of the main parameter, prefix HEATING_LEVEL_MP_PREFIX).
namespace  heating_mode_flags
 Values of functional parameter 9 on the heating temperature interface: the active mode.
namespace  pairing
namespace  pairing_responder

Classes

struct  BandwidthOption
 One selectable RX bandwidth: the chip's register byte and its nominal kHz value. More...
struct  BandwidthTableView
 A pointer/size view over one chip's RX-bandwidth option table. More...
class  DeviceBoundCompanion
 Mixin holding the parent + device-id binding shared by per-device entities that are not full entity platforms: the auto-generated companion diagnostic sensors (device name, active issue, RSSI, last contact, exchange failures, last commanded by, last command source) and the per-device auxiliary control entities (cover favorite/vent buttons, cover silent switch). More...
class  DeviceBoundEntity
 Mixin holding the hub-device binding shared by all IO-Homecontrol entity platforms. More...
struct  DeviceConfig
 YAML-declared device metadata for registration; defaults match an undeclared device. More...
struct  DeviceErrorBit
 One named bit of a chip's device-error word. More...
class  DeviceRegistry
 Owns the per-hub device table, update callbacks, and linked-remote associations. More...
struct  DeviceTypeInfo
 One row of the node-type / subtype table. More...
struct  DhwHeatingState
 What register 0x0021 says is heating. More...
struct  DhwOperatingFeatureWire
 The wire facts of one DhwOperatingFeature: what FP15, FP16 and the duration registers say about it. More...
struct  DhwOperatingStatus
 The operating-mode part of the status read DHW_STATUS_FPI1 / DHW_STATUS_FPI2 (its main parameter, the target, is decode_dhw_status_target()'s). More...
struct  DhwReadings
 What the hub has read from a domestic hot water heater. More...
class  DiagnosticProbes
 Sends diagnostic probe frames to already-paired devices and reports the raw replies. More...
struct  DiagnosticProbesHub
 The only three things a probe needs from the hub, injected by ManagementActions. More...
struct  DiscoveryResponseInfo
 Extended discovery-response fields (manufacturer, Multi Information Byte, backbone address, timestamp) plus flags recording how much of the payload was actually present. More...
class  ExchangeEngine
struct  FemRxGain
 Receive-side gain of a FEM profile's LNA, in the SX1262's RSSI scale. More...
struct  FemTxEstimate
 Antenna-port power estimate for a configured tx_power through a FEM profile's chain. More...
struct  ForeignTraffic
 Per-device bookkeeping of overheard foreign traffic. More...
class  FrameLogBuffer
 Keeps frame-log lines back while the hub answers a device, and prints them afterwards. More...
struct  HeatingFp
 A functional parameter and its value. More...
struct  HeatingModeEntry
 One HVAC mode the card offers and the wire value that selects it. More...
struct  HeatingNamedValue
 A value token that maps to a fixed float (used by set_mode / set_presence / set_open_window_detection). More...
struct  HeatingObservedState
 What a device-reported wire value shows on the card. More...
struct  HeatingPresetEntry
 One preset the card offers. More...
struct  HeatingProfile
 One heater kind. More...
struct  HeatingProfileFeatures
 Companion entities and traits a profile has. More...
struct  HeatingProgrammePeriod
 How a heater's own weekly programme shows which period runs: the registers that tell, and the presets that name the periods. More...
struct  HeatingReadings
 What the hub has read from one heater so far. More...
class  HubArmingSwitch
 Shared body for the hub-level arming switches. More...
class  HubBoundEntity
 Mixin for entities bound to the hub itself rather than to one device. More...
struct  IoDevice
 Runtime state of a paired IO‑Homecontrol device. More...
struct  IoFrame
 Parsed IO‑Homecontrol frame (CTRL0/1 + addresses + command + data). More...
class  IOHomeAcceptForeignPairingSwitch
 Hub-level switch entity: ON arms the key-extraction responder for 10 minutes, OFF disarms it immediately. More...
class  IOHomeActiveIssueTextSensor
 Diagnostic text sensor that names what is currently limiting a device (e.g. More...
class  IOHomeBatteryLevelSensor
 Sensor (device_class: battery, %) publishing the level a device answers its battery level poll with. More...
class  IOHomeBatteryLowBinarySensor
 Binary sensor (device_class: battery) that is ON while the device reports a low battery. More...
class  IOHomeBatteryTextSensor
 Diagnostic text sensor naming the device's coarse battery level: "full", "normal" or "low". More...
class  IOHomeClimate
 Climate entity for IO-Homecontrol heating devices. More...
class  IOHomeCompanionSensor
 Shared base for the numeric per-device diagnostic companions. More...
class  IOHomeCompanionTextSensor
 Shared base for the textual per-device diagnostic companions. More...
class  IOHomeControlComponent
 The main IO-Homecontrol component. More...
class  IOHomeCover
 Cover entity representing an IO‑Homecontrol shutter/awning/blind. More...
class  IOHomeCoverCommandButton
 Button entity that sends one non-positional cover command on press. More...
class  IOHomeCoverSilentSwitch
 Switch entity that selects a cover's travel profile at runtime. More...
class  IOHomeCoverTravelSpeedSelect
 Select entity that sets a cover's travel speed at runtime: default, silent or fast. More...
class  IOHomeDeviceCounterSensor
 Shared base for the companions that publish one cumulative uint16_t counter held on IoDevice. More...
class  IOHomeDeviceEventTextSensor
 Diagnostic text sensor that publishes IOHomeControlComponent::last_device_event() after every recorded device event. More...
class  IOHomeDeviceNameTextSensor
 Diagnostic text sensor that publishes the cached device name. More...
class  IOHomeDhwBinarySensor
 Binary sensor of one heating state of the heater: heat pump running or electric backup running. More...
class  IOHomeDhwBoostSwitch
 Switch that starts and ends the heater's boost. More...
class  IOHomeDhwEnergySensor
 Sensor of the heater's energy since installation, in kWh (total_increasing). More...
class  IOHomeDiscoverButton
 Button entity that triggers device discovery and pairing when pressed in Home Assistant. More...
class  IOHomeExchangeFailuresSensor
 Diagnostic sensor that publishes a device's cumulative count of outbound exchanges that timed out (no valid response) — see detail::record_exchange_timeout() in hub_internal.h. More...
class  IOHomeFan
 Fan entity for IO-Homecontrol exterior heating. More...
class  IOHomeHeaterNightLight
 Dimmable light for a heater's night light (night_light: on a heater with io_model heater_with_light). More...
class  IOHomeLastCommandedByTextSensor
 Diagnostic text sensor naming the controller that last commanded this device. More...
class  IOHomeLastCommandSourceTextSensor
 Diagnostic text sensor naming what kind of source issued that last command. More...
class  IOHomeLastContactSensor
 Diagnostic sensor that publishes seconds elapsed since the last frame received from a device (see detail::update_link_health() in hub_internal.h). More...
class  IOHomeLight
 Binary or dimmable light entity for IO‑Homecontrol devices. More...
class  IOHomeLock
 Lock entity for IO-Homecontrol lock devices. More...
class  IOHomeOneWayCommandButton
 Button entity that sends one 1W command as a configured controller identity. More...
class  IOHomeOneWayEnrollButton
 Button entity that registers a configured controller identity as a 1W controller. More...
class  IOHomeOneWayLastCommandTextSensor
 Diagnostic text sensor: what this identity last put on air. More...
class  IOHomeOpenWindowDetectionSwitch
 Switch that turns the heater's open-window detection on and off. More...
class  IOHomePairingResultTextSensor
 Diagnostic text sensor that publishes PairingTelemetry::result_sensor_string() after every pairing attempt. More...
class  IOHomeRainBinarySensor
 Binary sensor that reports whether the window currently limits its own opening for rain. More...
class  IOHomeRecoverOneWayKeySwitch
 Hub-level switch entity: ON arms the 1W key-recovery listener, OFF disarms it immediately. More...
class  IOHomeRssiSensor
 Diagnostic sensor that publishes a device's smoothed (EMA) RSSI in dBm. More...
class  IOHomeScanPairedDevicesButton
 Button entity that runs the scan_paired_devices roll-call when pressed. More...
class  IOHomeSunLevelSensor
 Sensor publishing the raw sun value a device answers its sun level poll with. More...
class  IOHomeSwitch
 Binary switch entity for IO‑Homecontrol on/off devices. More...
class  IOHomeTuningNumber
 Runtime-tunable number entity. More...
class  IOHomeTuningSelect
 Runtime-tunable select entity. More...
class  IOHomeUnconfirmedExchangesSensor
 Diagnostic sensor that publishes a device's cumulative count of exchanges it authenticated and then never closed — see detail::record_exchange_outcome() in hub_internal.h. More...
class  IOHomeWaterHeater
 Water heater entity for a domestic hot water heater. More...
class  IOHomeWindowBinarySensor
 Binary sensor (device_class: window) that is ON while the heater's window sensor reports open. More...
class  KeyExtractionResponder
 Device-role responder for the "Recover System Key" feature. More...
struct  LastCommandRecord
 One decoded last-command record. More...
struct  LimitationStatus
 One decoded CMD_LIMITATION_STATUS_RESP payload. More...
struct  ListenSpec
 How one listen window is to be spent — everything ExchangeEngine::listen() needs; everything else is the handler's business. More...
struct  ListenStats
 What a listen heard without accepting it — filled by ExchangeEngine::listen() when a caller passes one in ListenSpec::stats. More...
struct  Lr1121BootloaderRequirement
 One (target firmware, required bootloader) pairing from Semtech's published compatibility matrix. More...
struct  ManagementActionResult
 Result of a hub-level management action such as rename. More...
class  ManagementActions
 Encapsulates hub-level management operations exposed as Home Assistant actions. More...
struct  NamedPosition
 One named position: its feature bit, its main-parameter value and the command that sends it. More...
struct  OneWayActionEncoding
 How a OneWayButtonAction reaches the wire. More...
struct  OneWayAdoptedKey
 Recovered controller identity from a decoded CMD_ONEWAY_ADD_CONTROLLER (0x30) frame. More...
struct  OneWayCommandReport
 What a 1W command attempt did — the only feedback this feature can ever produce. More...
class  OneWayControllerBound
 Mixin for the hub-level entities that additionally scope themselves to one oneway_controllers: identity: the 1W command buttons, the 1W enrollment button, and the per-identity "Last 1W Command" text sensor. More...
struct  OneWayControllerIdentity
 One configured 1W controller identity. More...
class  OneWayControllerRegistry
 The configured 1W controller identities, in YAML declaration order. More...
struct  OneWayCopyShape
 The preamble and CTRL1 shape one copy of a 1W burst gets. More...
struct  OneWayFrameInfo
 Decoded representation of a 1W remote frame. More...
class  OnewayKeyAdoption
 Opt-in, receive-only listener that adopts an overheard 1W controller key. More...
class  OneWaySequenceStore
 Per-controller-identity rolling sequence counters, persisted across reboots. More...
class  OneWayTransmitter
 Sends 1W commands as the repeated bursts real remotes send. More...
struct  OneWayWireProfile
 Vendor-divergent 1W wire settings for a controller identity. More...
class  OperationQueue
 Serialized pending-operation queue with coalescing, deduplication, and two-band ordering. More...
struct  OptimisticState
 What the hub predicted ahead of confirmation, kept apart from what the device reported. More...
class  PairingEngine
 Owns and drives all three phases of the IO-Homecontrol device pairing flow. More...
class  PairingTelemetry
 Fixed-size per-attempt telemetry recorder for the pairing flow. More...
struct  PairingTelemetryEvent
 One recorded telemetry event. More...
struct  PendingOperation
 A single queued operation to be dispatched from loop(). More...
class  PeriodicPollPolicy
 Per-device periodic poll schedule, miss retry and miss counter for one poll kind. More...
struct  PollResult
 Outcome of poll_until. More...
struct  PollTracking
 Per-device poll scheduling state. More...
struct  ProgrammePeriodState
 What a heater's programme period resolves to, from its readings. More...
struct  PulledKeyReportInfo
 What build_pulled_key_report() says about the device that handed over a key. More...
struct  RadioCaptureInfo
 Diagnostic capture from a radio operation. More...
class  RadioDriver
 Abstract radio driver for IO-Homecontrol. More...
class  RadioLR1121
 LR1121 implementation of RadioDriver. More...
struct  RadioRxPacket
 Raw packet received from the radio. More...
class  RadioSX1262
 SX1262 implementation of RadioDriver. More...
class  RadioSX1276
 SX1276 implementation of RadioDriver. More...
struct  RadioTxConfig
 Configuration for transmitting a packet: carrier frequency, preamble length, wake-up level and clear-channel check. More...
struct  RecentOneWayPairingSighting
 A 1W pairing-gesture frame observed on the hub's normal passive RX path, remembered so a fresh discover_and_pair() attempt can seed its telemetry with it (issue #27/#65: the discovery telemetry window only starts recording at PairingTelemetry::begin(), so a PROG press completed just before "Discover & Pair" is pressed could otherwise be invisible to the pairing advisor even though the radio heard it just fine). More...
struct  RollCallPass
 One roll-call frame shape, and how to listen for its replies. More...
struct  RollCallReplyKeys
 The keys a responder line needs: the one a responder answered under, and the hub's. More...
struct  ScanResponder
 One roll-call responder, decoded on arrival. More...
struct  ScanState
 Accumulated state of one scan, threaded through every sweep. More...
class  SoftPhyDriverBase
 Shared RX/TX driver flow for the software-PHY radios (SX1262, LR1121). More...
class  SpiAccess
 Interface for SPI bus access. More...
class  StatusPollPolicy
 Per-hub poll scheduling and failure-backoff policy. More...
struct  StatusUpdateEvent
 The event header of a well-formed CMD_STATUS_UPDATE payload. More...
struct  StopCandidate
 The last "stopped" report a device sent away from its own target, kept to confirm the next. More...
struct  TransferObject
 One object a CMD_TRANSFER_OPEN can name: its class byte and its 16-bit id. More...
struct  TransferOpenReply
 One decoded CMD_TRANSFER_OPEN_RESP payload. More...
class  TransmitObserver
 Receives transmit events from ExchangeEngine::transmit_frame(). More...
struct  TuningConfig
 All runtime tunable parameters for pairing and radio diagnostics. More...
struct  TuningNumberParam
 One numeric tuning parameter: its wire name plus accessors over TuningConfig. More...
struct  TuningSelectParam
 One select tuning parameter: its wire name plus string accessors over TuningConfig. More...
struct  TxResult
 What send_packet() did with a frame. More...
struct  UartProbeResult
 Result of the UART probe: best candidate frame within a raw capture. More...

Typedefs

using DeviceUpdateCallback = std::function<void(const std::string &device_id, const IoDevice &device)>
 Callback type invoked when a device's state changes.
using ReplyHandler = std::function<ReplyDisposition(const IoFrame *parsed, const RadioRxPacket &packet)>
 Invoked for every packet the radio delivers during a listen, before the listen decides whether to keep waiting.
using NamedTimeoutFn = std::function<void(const char *name, uint32_t delay_ms, std::function<void()> callback)>
 Schedules a named, replace-on-same-name timeout on the hub's ESPHome scheduler.
using TransmitFrameFn = std::function<bool(const IoFrame &frame, uint32_t freq_hz, uint16_t preamble)>
 Puts a frame on air on a given channel via the hub's protected transmit_frame_().
using BeginBlockingExcursionFn = std::function<void()>
 Raises the hub's "operation took a long time" warning threshold for a blocking radio excursion — writes Component::warn_if_blocking_over_, which is protected on ESPHome's Component.
using ScanKeys = std::vector<std::array<uint8_t, AES_KEY_SIZE>>
 The keys one scan sweeps with: slot 0 is the hub key, the rest are the distinct per-device keys.
using OneWayTransmitFn = std::function<bool(const IoFrame &frame, uint32_t freq, uint16_t preamble)>
 How the transmitter puts a frame on air.
using OneWayCommandReportFn = std::function<void(const OneWayCommandReport &report)>
 Invoked once per attempted 1W command, successful or not.
using DeviceFeatures = uint16_t
 Capability bits of a device kind; see device_feature.
using DeviceErrorFormatter = void (*)(uint16_t errors, char *buf, size_t buf_size)
 A chip's own decoder, as passed to SoftPhyDriverBase::dump_init_device_errors_().

Enumerations

enum class  ExchangeOutcome : uint8_t { FAILED , SUCCESS_WITH_RESPONSE , SUCCESS_UNCONFIRMED }
 Authenticated exchange engine — outbound and inbound protocol flows. More...
enum class  ListenPolicy : uint8_t { HOLD_REQUEST_CHANNEL , ROTATE_ALL_CHANNELS , ROTATE_SKIPPING_REQUEST }
 Which channels a listen covers. More...
enum class  ReplyDisposition : uint8_t { ACCEPT , IGNORE , ABORT }
 What the caller wants done with the frame a listen just received. More...
enum class  ListenOutcome : uint8_t { ACCEPTED , ABORTED , TIMED_OUT }
 How one call to ExchangeEngine::listen() ended. More...
enum class  FrameLogLevel : uint8_t { WARN , INFO , DEBUG }
 Log level of one frame-log line, kept with the line while it is held. More...
enum class  BootloaderSupport : uint8_t { SUPPORTED , UNSUPPORTED , UNKNOWN_TARGET }
 Whether a target firmware version is known to work with a given bootloader version. More...
enum class  BootloaderMismatch : uint8_t { NONE , TARGET_NEEDS_NEWER , TARGET_NEEDS_OLDER }
 Direction of a bootloader/target mismatch, for messaging. More...
enum class  BootloaderUpgradePath : uint8_t { NOT_APPLICABLE , AVAILABLE , BLOCKED_UNKNOWN_TARGET , BLOCKED_BOOTLOADER_NEWER }
 Whether the three-stage bootloader-rewrite sequence (ADR 0021) is applicable, and if not, why. More...
enum class  FlashDecision : uint8_t {
  PROCEED , ALREADY_INSTALLED , NEEDS_CONFIRMATION , REJECT_WRONG_CHIP ,
  REJECT_BOOTLOADER_TOO_OLD
}
 Outcome of lr1121_flash_decision(). More...
enum class  OneWayPowerClass : uint8_t { LEGACY_LONG , ALWAYS_ALIVE , LOW_POWER }
 Which preamble/CTRL1 shape a 1W identity's bursts actually go out with. More...
enum class  OneWayPreamble : uint8_t { WAKE , NORMAL }
 Which wake-up shape one copy of a 1W burst gets. More...
enum class  EnrollGesture : uint8_t { SOMFY , VELUX_KLI }
 Which 1W enrollment gesture a manufacturer's actuators expect. More...
enum class  OneWayButtonAction : uint8_t {
  OneWayButtonAction::OPEN , OneWayButtonAction::CLOSE , OneWayButtonAction::STOP , OneWayButtonAction::VENT ,
  OneWayButtonAction::FAVORITE
}
 The command a generated 1W button sends. More...
enum class  PendingOperationType : uint8_t {
  SET_POSITION , SET_TILT , SET_POSITION_AND_TILT , DEVICE_COMMAND ,
  SET_LIGHT_STATE , SET_LOCK_STATE , SET_SWITCH_STATE , SET_FAN_POSITION ,
  ONEWAY_COMMAND , ONEWAY_POSITION , ONEWAY_ENROLL , ONEWAY_UNENROLL ,
  REQUEST_STATUS , REQUEST_NAME , REQUEST_LIMITATION , REQUEST_SUN_LEVEL ,
  REQUEST_BATTERY_LEVEL , DISCOVER_AND_PAIR
}
 Discriminator for entries in the pending-operation deque. More...
enum class  PairingTelemetryEventKind : uint8_t {
  TX , RX , RX_REJECT , LBT_DEFER ,
  PHASE
}
 Kind of a recorded telemetry event. More...
enum class  PairingOutcome : uint8_t {
  NONE , PAIRED , NO_RESPONSE , INVALID_RESPONSE ,
  KEY_EXCHANGE_FAILED , KEY_TRANSFER_REFUSED , KEY_RECEIVED , HUB_KEY_RETURNED
}
 Final disposition of a pairing attempt, used by the result sensor string. More...
enum class  DeviceNameValidationError : uint8_t {
  NONE = 0x00 , EMPTY = 0x01 , TOO_LONG = 0x02 , INVALID_UTF8 = 0x03 ,
  UNSUPPORTED_CHAR = 0x04
}
 Validation result for outbound device-name writes. More...
enum class  AddressClass : uint8_t {
  UNICAST = 0 , BROADCAST_ALL , BROADCAST_TYPE , DISCOVERY ,
  UNKNOWN_BROADCAST
}
 Address classification categories for diagnostic purposes. More...
enum class  OneWayMacStatus : uint8_t { NOT_PRESENT = 0 , VERIFIED = 1 , FAILED = 2 }
 Outcome of checking the out-of-length MAC trailer (IoFrame::has_mac) on a decoded CMD_ONEWAY_ADD_CONTROLLER frame. More...
enum class  OneWayAddControllerDecodeError : uint8_t {
  NONE = 0 , NOT_ONEWAY = 1 , WRONG_COMMAND = 2 , BAD_LENGTH = 3 ,
  KEY_UNWRAP_FAILED = 4
}
 Decoding outcome for decode_1w_add_controller(). More...
enum class  DeviceType : uint16_t {
  UNKNOWN = 0 , INTERIOR_VENETIAN_BLIND = 1 , ROLLER_SHUTTER = 2 , VERTICAL_EXTERIOR_AWNING = 3 ,
  WINDOW_OPENER = 4 , GARAGE_OPENER = 5 , LIGHT = 6 , GATE_OPENER = 7 ,
  ROLLING_DOOR_OPENER = 8 , LOCK = 9 , VERTICAL_INTERIOR_BLIND = 10 , SECURE_CONFIGURATION_DEVICE = 11 ,
  BEACON = 12 , DUAL_ROLLER_SHUTTER = 13 , HEATING_TEMPERATURE_INTERFACE = 14 , ON_OFF_SWITCH = 15 ,
  HORIZONTAL_AWNING = 16 , EXTERIOR_VENETIAN_BLIND = 17 , LOUVRE_BLIND = 18 , CURTAIN_TRACK = 19 ,
  VENTILATION_POINT = 20 , EXTERIOR_HEATING = 21 , HEAT_PUMP = 22 , INTRUSION_ALARM = 23 ,
  SWINGING_SHUTTER = 24 , SLIDING_WINDOW = 27 , ZONE_CONTROL = 28 , BIOCLIMATIC_PERGOLA = 29 ,
  SIREN = 30 , DOMESTIC_HOT_WATER = 51 , ELECTRICAL_HEATER = 52 , HEAT_RECOVERY_VENTILATION = 53 ,
  CENTRAL_HOUSE_CONTROL = 255 , TEST_AND_EVALUATION = 1008 , REMOTE_CONTROLLER = 1023
}
 IO-Homecontrol node types. More...
enum class  DeviceModel : uint8_t { NONE = 0 , TOWEL_DRYER = 1 , HEATER_WITH_LIGHT = 2 }
 A product model below a (node type, subtype): for products that report the same wire type but need different handling. More...
enum class  DeviceCapabilityClass : uint8_t {
  UNKNOWN = 0x00 , COVER = 0x01 , LIGHT = 0x02 , SWITCH = 0x03 ,
  SENSOR = 0x04 , BEACON = 0x05 , CLIMATE = 0x06 , LOCK = 0x07 ,
  HEATER_LEVEL = 0x08 , WATER_HEATER = 0x09
}
 High‑level capability class derived from DeviceType. More...
enum class  CoverCommand : uint8_t {
  STOP = 0 , FAVORITE = 1 , VENT = 2 , FORCE_OPEN = 3 ,
  PARTIAL = 4 , PEDESTRIAN = 5
}
 Named device commands for cover-type actuators. More...
enum class  RainSensorState : uint8_t { UNKNOWN = 0 , DRY = 1 , RAIN = 2 }
 What the hub's latest minimum-limitation read says about a rain-protection limit. More...
enum class  TravelSpeed : uint8_t { MOTOR_DEFAULT = 0 , SILENT = 1 , FAST = 2 }
 The speed a cover's position moves ask the motor for (ADR 0065). More...
enum class  ExecutionState : uint8_t {
  NON_EXECUTING = 0 , EXECUTION_ERROR = 1 , WAITING_FOR_EXECUTING = 2 , WAITING_FOR_POWER = 3 ,
  EXECUTING = 4 , DONE = 5 , UNDEFINED = 0xFF
}
 What a device says it is doing, from bits [2:0] of byte 0 of a status reply (CMD_PRIVATE_RESP, CMD_STATUS_UPDATE). More...
enum class  BatteryState : uint8_t { NOT_REPORTED = 0 , LOW_CHARGE = 1 , NORMAL_CHARGE = 2 , FULL_CHARGE = 3 }
 A device's coarse battery level, as it reports it in byte 1 of its status payload. More...
enum class  DhwOperatingFeature : uint8_t { BOOST , AWAY }
 The two features the operating-mode word switches; dhw_operating_feature_wire() holds their wire facts. More...
enum class  DhwMode : uint8_t { AUTO = 0 , MANUAL_ECO_ON = 1 , MANUAL_ECO_OFF = 2 }
 Operating mode as register 0x0006 holds it. More...
enum class  HeatingFunction : uint8_t {
  POWER_ON , SET_TEMPERATURE , SET_MODE , SET_PRESENCE ,
  SET_OPEN_WINDOW_DETECTION , MIDNIGHT_SYNC , ROOM_TEMPERATURE , REGULATOR_STATE ,
  MAX_SETPOINT , WINDOW_OPEN , TOWEL_MODE , TEMPORARY_STATE ,
  WORKING_RATE , TEMPERATURE_IN_FORCE , ECO_SETBACK , NIGHT_LIGHT ,
  NIGHT_LIGHT_BRIGHTNESS
}
 Registers and functions of a 2W heater, one row of the descriptor table each. More...
enum class  HeatingMode : uint8_t {
  AUTO = 0x00 , MANUAL = 0x01 , PROG = 0x02 , EXTERNAL_SETPOINT = 0x03 ,
  OFF = 0x04
}
 Operating modes of the mode register (HeatingFunction::SET_MODE). More...
enum class  TowelDryerMode : uint8_t { OFF = 0x00 , AUTO = 0x01 , PROG = 0x02 , MANUAL = 0x03 }
 Modes of a towel dryer's mode register (HeatingFunction::TOWEL_MODE). More...
enum class  HeatingTemporaryState : uint8_t { NONE = 0x00 , BOOST = 0x01 , DRYING = 0x02 }
 Temporary states of a towel dryer (HeatingFunction::TEMPORARY_STATE): a timed run on top of the mode, which the mode register does not show. More...
enum class  HeatingRegulatorState : uint8_t { NONE = 0x00 , STANDBY = 0x01 , HEATING = 0x02 }
 Regulator states of the regulator register (HeatingFunction::REGULATOR_STATE). More...
enum class  HeatingControlPath : uint8_t { PRIVATE_REGISTERS , HEATING_LEVEL , TEMPERATURE_EXECUTE }
 How a heater is commanded and read. More...
enum class  HeatingHvacMode : uint8_t { OFF , HEAT , AUTO }
 HVAC modes a profile can offer. More...
enum class  HeatingPresetId : uint8_t {
  NONE , AWAY , COMFORT , ECO ,
  BOOST , CUSTOM
}
 Presets a profile can offer. CUSTOM entries carry their own name. More...
enum class  HeatingFpKind : uint8_t { NONE , FP9 , FP16 }
 Which functional parameter, if any, accompanies a TEMPERATURE_EXECUTE command. More...
enum class  HeatingActionSource : uint8_t { REGULATOR , WORKING_RATE }
 Where a register heater's "heating" / "idle" comes from. More...
enum class  TxWake : uint8_t { NONE , SHORT , LONG }
 How hard a transmission has to work to be heard: the wake-up level the receiver needs. More...
enum class  FemProfile : uint8_t { NONE = 0 , GC1109 , KCT8103L , XY16P35 }
 Which RF front-end module (if any) sits between the SX1262 and the antenna. More...
enum class  RollCallAttemptResult : uint8_t { OK , BUILD_FAILED }
 Outcome of one roll-call attempt or sweep, so the caller can surface a build failure. More...
enum class  ScanAddResult : uint8_t { ADDED , DUPLICATE , FULL }
 Outcome of add_scan_responder(), so callers can tell a harmless repeat from real loss. More...
enum class  RollCallKeyDisplay : uint8_t { HIDDEN , BANNERED , PLAIN }
 How a responder line shows the key it answered under. More...
enum class  ForeignFrameKind : uint8_t { REQUEST_TO_DEVICE , DEVICE_CHALLENGE_TO_OTHER }
 What an overheard frame of another controller is. More...
enum class  ForeignAction : uint8_t { IGNORE , ARM , PUSH_PENDING }
 What the hub does about an overheard foreign frame. More...
enum class  MovingReportAction : uint8_t { FOLLOW , OPEN , END , NONE }
 What the hub does with a status report that reads as moving. More...
enum class  SX1262RxBandwidth : uint8_t {
  BW_39_0_KHZ = 0x1C , BW_46_9_KHZ = 0x14 , BW_58_6_KHZ = 0x0C , BW_78_2_KHZ = 0x1B ,
  BW_117_3_KHZ = 0x0B , BW_156_2_KHZ = 0x1A , BW_187_2_KHZ = 0x12
}
 Valid SX1262 RX bandwidth options (kHz register values). More...
enum class  SX1276RxBandwidth : uint8_t {
  BW_20_8_KHZ = 0x14 , BW_41_7_KHZ = 0x13 , BW_62_5_KHZ = 0x03 , BW_83_3_KHZ = 0x12 ,
  BW_125_0_KHZ = 0x02
}
 Valid SX1276 RX bandwidth options (RegRxBw register bytes). More...
enum class  LR1121RxBandwidth : uint8_t {
  BW_39_0_KHZ = 0x1C , BW_46_9_KHZ = 0x14 , BW_58_6_KHZ = 0x0C , BW_78_2_KHZ = 0x1B ,
  BW_117_3_KHZ = 0x0B , BW_156_2_KHZ = 0x1A , BW_187_2_KHZ = 0x12
}
 Valid LR1121 RX bandwidth options (register values). More...
enum class  ScanPowerClasses : uint8_t { BOTH , ALWAYS_ALIVE , LOW_POWER }
 Which power classes scan_paired_devices() calls. More...
enum class  DiscoverConfirmMode : uint8_t { SKIP , SEND , SEND_WITH_ACK }
 Whether and how PairingEngine sends CMD_DISCOVER_CONFIRM (0x2C) to a freshly-discovered device before proceeding to the key exchange (0x31). More...
enum class  DiscoveryListenChannels : uint8_t { SKIP_REQUEST , ALL }
 Which channels PairingEngine's discovery wait listens on. More...
enum class  KeyTransferMode : uint8_t { AUTO , PUSH , PULL }
 Which direction PairingEngine moves the system key in after discovery. More...
enum class  DiscoveryCommand : uint8_t { DISCOVER = 0x28 , DISCOVER_SPE = 0x2A , DISCOVER_ALT = 0x2E }
 Discovery request command codes. More...

Functions

int render_exchange_debug (char *buf, size_t buf_size, const char *device_id, const ExchangeEngine::DebugInfo &d)
 Render the structured field list shared by both exchange-debug log lines.
std::string format_position (float pos)
 Format a position float as a human‑readable string (e.g.
const char * pairing_stage_name (pairing::PairingState state)
 Get a short, log/telemetry-friendly name for a pairing state.
static void log_overheard_limitation_read (const IoFrame &frame)
 Log an overheard limitation request/reply decoded at DEBUG; any other frame is ignored.
static constexpr uint32_t key_extraction_session_window_ms (uint8_t multi_info)
 How long after receiving a frame it answers the responder stays on that frame's channel (SESSION_HOLD_BASE_MS plus the turnaround time the responder advertises in its Multi Information Byte).
void bytes_to_hex (const uint8_t *data, uint8_t len, char *out, size_t out_size)
void decode_ctrl1_flags (uint8_t ctrl1, char *out, size_t out_size)
 Render decoded CTRL1 flags into a short annotation string.
void render_frame_hex_redacted (const uint8_t *data, uint8_t len, char *out, size_t out_size)
 Render a frame's bytes as spaced hex text, masking the payload when the command carries key material (see command_carries_key_material()).
void frame_log_hold ()
void frame_log_flush ()
void frame_log_release ()
constexpr bool lr1121_bootloader_is_lr1121 (uint16_t bootloader_version)
constexpr const char * lr1121_chip_family_for_bootloader (uint16_t bootloader_version)
 Human-readable chip family for a bootloader version that is not one of the two LR1121 values above, for an actionable REJECT_WRONG_CHIP message.
constexpr const char * lr1121_chip_family_for_device_type (uint8_t device_type)
 Human-readable chip family for a normal-mode device_type that is not the LR1121 value above, for an actionable REJECT_WRONG_CHIP message (the layer-3 counterpart of lr1121_chip_family_for_bootloader() above).
constexpr BootloaderSupport lr1121_bootloader_supports_target (uint16_t target_fw, uint16_t bootloader_version)
 Look up whether target_fw is known to require bootloader_version.
constexpr uint16_t lr1121_required_bootloader_for (uint16_t target_fw)
 Required bootloader for a known target firmware version.
constexpr BootloaderMismatch lr1121_bootloader_mismatch_kind (uint16_t target_fw, uint16_t bootloader_version)
 Classify a bootloader/target mismatch by direction; see BootloaderMismatch.
constexpr BootloaderUpgradePath lr1121_bootloader_upgrade_path (bool block_present, bool bootloader_version_known, uint16_t bootloader_version, uint16_t loader_fw, uint16_t target_fw)
 Whether the three-stage bootloader upgrade is applicable for the current cached state.
constexpr FlashDecision lr1121_flash_decision (uint8_t device_type, uint8_t bootloader_chip_type, uint16_t bootloader_version, uint16_t installed_fw, uint16_t target_fw, bool already_confirmed)
 The single decision point for whether/how to flash target_fw.
static std::string bool_to_string (bool value)
static std::string normalize_lower_argument (const std::string &value)
 Lowercase + ASCII-trim a native-API string argument.
static bool match_heating_named_value (const std::string &token, const HeatingNamedValue *table, size_t table_len, float &out)
 Match token against table; on a hit set out and return true.
static bool parse_heating_temperature (const std::string &value, float &value_out, std::string &error)
 Parse set_temperature's value into degrees Celsius, range-checked.
static bool parse_heating_arguments (const std::string &function, const std::string &value, HeatingFunction &fn_out, float &value_out, std::string &error)
 Parse the (function, value) argument pair of the heating_control action.
static ScanKeys collect_scan_keys (const uint8_t *hub_key, DeviceRegistry &registry)
 Collect the keys to sweep: the hub key, then each per-device key that differs from it and from every key already collected.
static size_t count_devices_using_key (DeviceRegistry &registry, const uint8_t *hub_key, const uint8_t *key)
 How many registered devices exchange with key: those with it as their own key, or, for the hub key, those with no key of their own (or one equal to the hub's).
static std::string build_scan_report (const ScanState &state, DeviceRegistry &registry, ScanPowerClasses selection, const ScanKeys &keys, bool reveal_keys)
 Build the final report: known/unknown grouping, header, the selection NOTE, and the truncation NOTE.
std::string normalize_device_id_argument (const std::string &device_id)
 Trim and uppercase a device-ID argument as received from the native API.
std::string format_hex_byte (uint8_t value)
 Format a byte as two uppercase hex digits, no prefix.
ManagementActionResult make_management_result (const std::string &action, const std::string &device_id)
 A result that carries only the action name and the target device ID.
bool apply_error_response (const IoFrame &response, ManagementActionResult &result)
 Decode a CMD_ERROR_RESP frame's result code into result.
IoDevice * resolve_management_device (const char *action, const std::string &device_id, bool initialized, DeviceRegistry &registry, ManagementActionResult &result)
 Shared preamble for every device-addressed action: validate the hub and device ID, then resolve the target device.
const char * oneway_power_class_name (OneWayPowerClass power_class)
 Human-readable name for a OneWayPowerClass, as it appears in the boot log.
const char * oneway_button_action_name (OneWayButtonAction action)
 Human-readable name for a button action, as it appears in the diagnostic sensor.
OneWayCopyShape oneway_burst_copy_shape (OneWayPowerClass power_class, uint8_t copy_index)
 Resolve which preamble/CTRL1 shape one copy of a burst gets, from the identity's power class and the copy's position in the burst.
OneWayWireProfile resolve_oneway_wire_profile (uint8_t manufacturer)
 Resolve an identity's 1W wire profile from its manufacturer byte.
std::array< DeviceType, 3 > effective_enrollment_classes (const OneWayControllerIdentity &identity)
 The device classes this identity's 0x30 enrollment sweep will actually target.
uint8_t effective_execute_acei (const OneWayControllerIdentity &identity)
 The ACEI byte a given identity will put on air for a 1W EXECUTE frame.
bool has_execute_acei_override (const OneWayControllerIdentity &identity)
 Whether this identity's ACEI comes from an explicit execute_acei: rather than the profile.
OneWayPowerClass effective_power_class (const OneWayControllerIdentity &identity)
 The burst shape this identity actually transmits with.
bool has_power_class_override (const OneWayControllerIdentity &identity)
 Whether this identity's burst shape comes from an explicit low_power: rather than the profile.
bool effective_inverted (const OneWayControllerIdentity &identity)
 Whether this identity's OPEN/CLOSE/position values are mirrored on the wire.
bool has_invert_override (const OneWayControllerIdentity &identity)
 Whether this identity's inversion comes from an explicit invert_position: rather than the kind.
OneWayActionEncoding encode_oneway_action (OneWayButtonAction action)
 Resolve a button action to the call that sends it.
uint16_t oneway_copy_preamble_bytes (OneWayCopyShape shape, uint16_t normal_start_preamble)
 Resolve one copy's shape (oneway_burst_copy_shape()) to the actual preamble byte count.
std::string format_oneway_preamble_list (OneWayPowerClass power_class, uint16_t normal_start_preamble)
 Render the preamble bytes each copy of a burst will use, e.g.
static const char * experimental_note (DeviceType type, uint8_t subtype)
 Suffix for a log line that names a device kind no capture or report has confirmed on a real device.
static void log_discovered_device (const pairing::PairingContext &context)
 Log what discovery accepted: the device kind, and a warning when it answered as a remote controller.
uint32_t periodic_poll_first_delay_ms (uint32_t random)
 Delay of a device's first poll after its interval is set.
uint32_t periodic_poll_jittered_interval_ms (uint32_t interval_ms, uint32_t random)
 One poll interval moved by a uniform offset of up to PERIODIC_POLL_JITTER_PERCENT either way.
void publish_light_reading (light::LightState *state, bool on, std::optional< float > brightness, bool &suppress_write)
 Show a device's light state on its LightState without sending it back to the device.
std::string format_oneway_command_report (const OneWayCommandReport &report)
 Build the sensor string for one command report.
water_heater::WaterHeaterMode water_heater_mode_for (DhwMode mode)
 ESPHome water heater mode for a heater mode.
bool dhw_mode_for (water_heater::WaterHeaterMode mode, DhwMode &out)
 Heater mode for an ESPHome water heater mode.
std::string trim_ascii_whitespace (const std::string &value)
 Trim leading and trailing ASCII whitespace from a string.
std::string decode_device_name_payload (const uint8_t *data, uint8_t len)
 Decode a device-name payload from IO-homecontrol's Latin-1 wire format into UTF-8.
DeviceNameValidationError encode_device_name_payload (const std::string &name, uint8_t payload[DEVICE_NAME_WRITE_PAYLOAD_SIZE], std::string &normalized_name)
 Validate and encode a user-supplied UTF-8 device name into the fixed Latin-1 write payload.
const char * device_name_validation_error_name (DeviceNameValidationError error)
 Return a stable symbolic name for a device-name validation result.
const char * device_name_validation_error_description (DeviceNameValidationError error)
 Return a human-readable explanation for a device-name validation result.
AddressClass classify_address (const uint8_t addr[NODE_ID_SIZE])
 Classify an IO-Homecontrol 3-byte address.
const char * address_class_name (AddressClass address_class)
 Get a human-readable name for an address classification.
DeviceType broadcast_target_type (const uint8_t addr[NODE_ID_SIZE])
 Extract the target device type from a typed broadcast address.
DeviceType discovery_target_type (const uint8_t addr[NODE_ID_SIZE])
 Extract the device type a discovery request is restricted to.
uint8_t discovery_slot_count (const uint8_t addr[NODE_ID_SIZE])
 Number of answer slots a discovery request opens.
void encode_broadcast_address (DeviceType type, uint8_t out[NODE_ID_SIZE])
 Encode a device type into its typed-broadcast destination address — the exact inverse of broadcast_target_type(), kept immediately beside it so the pair cannot drift apart.
void encode_discovery_address (DeviceType type, uint8_t out[NODE_ID_SIZE])
 Encode a device type into its typed discovery address, the inverse of discovery_target_type().
void decode_1w_main_intent (uint8_t main0, uint8_t main1, char *out, size_t out_size)
 Decode the "main" position/command bytes from a 1W execute payload.
std::optional< float > oneway_intent_to_target (uint8_t main0, uint8_t main1)
 Resolve a 1W main-byte pair to an optimistic IO target position, if unambiguous.
OneWayFrameInfo decode_1w_frame (const IoFrame &frame)
 Decode a parsed 1W frame into a structured OneWayFrameInfo.
const char * oneway_target_label (const OneWayFrameInfo &info)
 The destination label a decoded 1W frame renders as in a log line.
DiscoveryResponseInfo decode_discovery_response (const IoFrame &frame, IoDevice &device, std::string &device_id)
 Decode a discovery-response payload (CMD_DISCOVER_RESP 0x29 or CMD_DISCOVER_SPE_RESP 0x2B — both carry the identical DISCOVERY_RESP_FULL_SIZE layout) into device metadata.
OneWayAddControllerDecodeError decode_1w_add_controller (const IoFrame &frame, OneWayAdoptedKey &out)
 Decode a CMD_ONEWAY_ADD_CONTROLLER (0x30) frame into a recovered controller identity.
LastCommandRecord decode_last_command_record (const IoFrame &frame, uint8_t base)
 Decode the last-command record at base from a status-bearing payload.
bool decode_status_update_event (const IoFrame &frame, StatusUpdateEvent &out)
 Decode the event header of a CMD_STATUS_UPDATE.
bool decode_sun_level_event (const IoFrame &frame, uint16_t &out)
 Decode the sun value of an EVENT_LOCAL_SENSOR report.
bool decode_battery_level_event (const IoFrame &frame, uint8_t &out)
 Decode the level of an EVENT_BATTERY_LEVEL_CHANGE report as a percentage.
bool decode_transfer_open_reply (const IoFrame &f, TransferOpenReply &out)
 Decode a transfer-open reply (layout verified against a VELUX KLR 200 copy session).
uint16_t transfer_chunk_count (uint16_t length)
 Number of CMD_UNKNOWN4A_REQ chunks that cover length bytes: ceil(length / TRANSFER_BLOCK_SIZE).
bool decode_transfer_block (const IoFrame &f, uint8_t handle, uint16_t chunk, const uint8_t *&out, uint8_t &out_len)
 Decode one CMD_UNKNOWN4A_RESP data block.
bool decode_limitation_status (const IoFrame &f, LimitationStatus &out)
 Decode a limitation reply (assumed layout, see LimitationStatus).
bool decode_limitation_request (const IoFrame &f, LimitationType &type, uint8_t &parameter_id)
 Decode a limitation request (assumed layout [selector] [parameter id] [00]).
uint32_t limitation_time_seconds (uint8_t time_raw)
 Remaining time of a limitation in seconds.
bool create_execute_position (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint8_t position, bool silent)
 Build a position execute command (0x00) to move a device to a numeric position.
bool create_execute_command (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, CoverCommand cmd, bool silent)
 Build a named-command execute frame (0x00) for STOP or a named position (NAMED_POSITIONS).
bool create_force_open (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint8_t open_position)
 Build a force-open execute frame (0x00): an ordinary position command to the device's wire-scale "fully open" value, sent at elevated ACEI priority (see EXECUTE_ACEI_FORCE_OPEN).
bool create_execute_main_parameter (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint8_t position, bool force_open_priority)
 Build a main-parameter-only position execute frame (0x00). See proto_commands.h.
bool create_execute_heating_main_parameter (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint16_t main_parameter)
 Build a temperature / alias execute frame (0x00), main parameter only. See proto_commands.h.
static bool build_execute_with_second_bitmap_fp (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint8_t acei, uint16_t main_parameter, uint8_t second_bitmap, uint16_t value)
 Build an execute frame (0x00) with a main parameter (a temperature, an alias or a heating level) and one functional parameter of the second bitmap (FP9-FP16).
bool create_execute_heating_with_mode_flags (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint16_t main_parameter, uint16_t mode_flags)
 Build a temperature / alias execute frame (0x00) carrying functional parameter 9. See proto_commands.h.
bool create_execute_heating_with_fp16 (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint16_t main_parameter, uint16_t fp16)
 Build a temperature / alias execute frame (0x00) carrying functional parameter 16. See proto_commands.h.
bool create_execute_heating_level (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint8_t level)
 Build a heating-level execute frame (0x00). See proto_commands.h.
bool create_1w_execute_position (IoFrame &f, const uint8_t src[NODE_ID_SIZE], DeviceType target_type, uint8_t position, uint16_t sequence, const uint8_t controller_key[AES_KEY_SIZE], uint8_t acei, bool broadcast_all)
 Build a 1W position execute frame (CMD 0x00) targeting a device class.
bool create_1w_execute_command (IoFrame &f, const uint8_t src[NODE_ID_SIZE], DeviceType target_type, CoverCommand cmd, uint16_t sequence, const uint8_t controller_key[AES_KEY_SIZE], uint8_t acei, bool broadcast_all)
 Build a 1W named-command execute frame (CMD 0x00) targeting a device class.
bool create_1w_add_controller (IoFrame &f, const uint8_t src[NODE_ID_SIZE], DeviceType target_type, uint8_t manufacturer, uint16_t sequence, const uint8_t controller_key[AES_KEY_SIZE], bool with_mac)
 Build a 1W add-controller frame (CMD 0x30).
bool create_1w_remove_controller (IoFrame &f, const uint8_t src[NODE_ID_SIZE], DeviceType target_type, uint16_t sequence, const uint8_t controller_key[AES_KEY_SIZE])
 Build a 1W remove-controller frame (CMD 0x39).
bool create_private_function (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint8_t function_id, uint8_t sub_index)
 Build a CMD_PRIVATE (0x03) request for an arbitrary function ID.
bool create_get_status (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power)
 Build a get-status request (0x03). The device responds with its current position.
bool create_get_name (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power)
 Build a get-name request (0x50).
bool create_general_info3 (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power)
 Build a CMD_GET_GENERAL_INFO3 (0x58) request.
bool create_get_info1 (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power)
 Build a CMD_GET_INFO1 (0x54) request. No payload. See proto_commands.h for the evidence note.
bool create_get_info2 (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power)
 Build a CMD_GET_INFO2 (0x56) request. No payload. See proto_commands.h for the evidence note.
bool create_set_name (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, const uint8_t payload[DEVICE_NAME_WRITE_PAYLOAD_SIZE])
 Build an authenticated set-name request (0x52) using a fixed zero-padded Latin-1 payload.
bool create_identify (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power)
 Build an authenticated device-identify request (0x1E).
bool create_write_private (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, const uint8_t *payload, size_t payload_len)
 Build a generic CMD_WRITE_PRIVATE (0x20) frame around a caller-supplied payload — the one builder behind every heating/climate function.
bool create_execute_tilt (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint8_t tilt_percent)
 Build a tilt execute command (0x00) for devices that support slat angle control.
bool create_execute_position_and_tilt (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint8_t position, uint8_t tilt_percent)
 Build a combined position-and-tilt execute command (0x00) — setClosureAndOrientation.
bool create_get_status_extended (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint8_t selector, uint8_t block, uint8_t function_id)
 Build an extended CMD_PRIVATE (0x03) request with a selector/block pair — the shape real hubs use for both the tilt block (selector STATUS_TILT_SELECTOR) and the field-observed selector 0x80 (tests/corpus/captures/probe/multi_somfy_probe_extended_private_both_selectors.yaml), which this codebase has never decoded.
static uint8_t count_bits (uint8_t v)
 Number of set bits in a bitmap byte (the component does not use the C++20 bit header yet).
bool create_status_mp_fp_read (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, uint8_t fpi1, uint8_t fpi2)
 Build the KLF-style status read of the main parameter plus functional parameters (see the header for the evidence).
bool create_limitation_status_read (IoFrame &f, const uint8_t *own, const uint8_t *dst, LimitationType type, bool low_power)
 Build the limitation read (see the header for the evidence and the read-only guarantee).
bool create_subscribe_event (IoFrame &f, const uint8_t *own, const uint8_t *dst, uint16_t event_id, bool low_power)
 Build the subscribe request (see the header for the allowlist).
bool create_unsubscribe_event (IoFrame &f, const uint8_t *own, const uint8_t *dst, uint16_t event_id, bool low_power)
 Build the unsubscribe request (see the header: the action value is untested).
bool create_transfer_open (IoFrame &f, const uint8_t *own, const uint8_t *dst, const TransferObject &object, bool low_power)
 Build the transfer-open request (see the header for the allowlist and the byte layout).
bool create_transfer_read (IoFrame &f, const uint8_t *own, const uint8_t *dst, uint8_t handle, uint16_t chunk, bool low_power)
 Build the transfer-read request (see the header: exactly handle and chunk, no data).
bool create_get_status_tilt (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power)
 Build a tilt-aware get-status request (0x03) that returns the extended 16-byte tilt payload.
bool create_private2_read (IoFrame &f, const uint8_t *own, const uint8_t *dst, uint8_t modifier, bool long_form, bool low_power)
 Build a CMD_PRIVATE2 (0x0C) request in either of the two field-observed shapes.
bool create_discover (IoFrame &f, const uint8_t *own)
 Build a discovery broadcast (0x28).
bool create_discovery_request (IoFrame &f, const uint8_t *own, uint8_t command, const uint8_t *dst, bool low_power, bool ack_capable, bool payload_enabled, uint8_t payload, const uint8_t *system_key)
 Build a configurable discovery request command (0x28, 0x2A, or 0x2E).
bool create_discover_resp (IoFrame &f, const uint8_t *own, const uint8_t *dst, DeviceType type, uint8_t subtype, uint8_t manufacturer_id, uint8_t flags)
 Build a discovery response (0x29) — device side, used only by the key-extraction responder.
static bool create_device_terminal_ack (IoFrame &f, const uint8_t *own, const uint8_t *dst, uint8_t cmd)
 Build a bare device→hub terminal acknowledgement: no payload, END set, START and LOW_POWER clear.
bool create_key_confirm (IoFrame &f, const uint8_t *own, const uint8_t *dst)
 Build a key-confirm frame (0x33) — device side, used only by the key-extraction responder.
static bool create_controller_start_frame (IoFrame &f, const uint8_t *own, const uint8_t *dst, uint8_t cmd, bool low_power, bool ack)
 Build a bare controller→device start frame: no payload, START set, END clear, LOW_POWER and ACK as the caller decides.
bool create_discover_confirm (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power, bool ack)
 Build a discovery-confirm request (0x2C) — controller side, sent directly to a freshly-discovered device before the key exchange.
bool create_discover_confirm_ack (IoFrame &f, const uint8_t *own, const uint8_t *dst)
 Build a discovery-confirm acknowledgement (0x2D) — device side, used only by the key-extraction responder.
bool recover_system_key_from_transfer (const uint8_t transfer_payload[AES_KEY_SIZE], const uint8_t challenge[HMAC_SIZE], uint8_t out_key[AES_KEY_SIZE])
 Recover the system key from a CMD_KEY_TRANSFER payload.
bool create_launch_key_transfer (IoFrame &f, const uint8_t *own, const uint8_t *dst, const uint8_t challenge[HMAC_SIZE])
 Build a key transfer request (0x38). See proto_commands.h for the frame shape.
static uint8_t frame_transcript (const IoFrame &frame, uint8_t out[FRAME_MAX_SIZE])
 Copy a frame's command byte and payload into out: the bytes a challenge response (0x3D) authenticates, and the IV data of a pulled key.
bool recover_key_from_pull (const IoFrame &request, const uint8_t transfer_payload[AES_KEY_SIZE], uint8_t out_key[AES_KEY_SIZE])
 Recover a device's key from its answer to our 0x38.
bool create_key_init (IoFrame &f, const uint8_t *own, const uint8_t *dst)
 Build a key-init request (0x31) to start the pairing key exchange with a discovered device.
bool create_key_transfer (IoFrame &f, IoFrame &old_frame, const uint8_t *dst, const uint8_t *src, const uint8_t key[AES_KEY_SIZE], const uint8_t challenge[HMAC_SIZE])
 Build a key-transfer frame (0x32) containing the system key encrypted with the transfer key.
static bool create_challenge_req_framed (IoFrame &f, const uint8_t *dst, const uint8_t *src, const uint8_t challenge[HMAC_SIZE], bool start, bool low_power)
 Build a challenge request (0x3C) with caller-chosen framing bits.
bool create_challenge_req (IoFrame &f, const uint8_t *dst, const uint8_t *src, const uint8_t challenge[HMAC_SIZE])
 Build a challenge request (0x3C) using a caller-supplied challenge.
bool create_challenge_req (IoFrame &f, const uint8_t *dst, const uint8_t *src)
 Build a challenge request (0x3C) containing 6 random bytes.
bool create_challenge_req_device_role (IoFrame &f, const uint8_t *dst, const uint8_t *src, const uint8_t challenge[HMAC_SIZE])
 Build a device-role challenge request (0x3C) — device side, used only by the key-extraction responder.
static bool create_challenge_resp_framed (IoFrame &f, const uint8_t *dst, const uint8_t *src, const uint8_t challenge[HMAC_SIZE], const IoFrame &origin, const uint8_t *key, bool end, bool low_power)
 Build a challenge response (0x3D) with caller-chosen framing bits.
bool challenge_resp_hmac (const IoFrame &origin, const uint8_t challenge[HMAC_SIZE], const uint8_t *key, uint8_t hmac[HMAC_SIZE])
 The 0x3D MAC for origin.
bool verify_challenge_resp (const IoFrame &origin, const uint8_t challenge[HMAC_SIZE], const uint8_t mac[HMAC_SIZE], const uint8_t *key)
 Check a device's 0x3D for origin, in constant time (crypto::verify_hmac()).
bool create_challenge_resp (IoFrame &f, const uint8_t *dst, const uint8_t *src, const uint8_t challenge[HMAC_SIZE], const IoFrame &origin, const uint8_t *key)
 Build a challenge response (0x3D) proving we know the system key.
bool create_node_verify_resp_device_role (IoFrame &f, const uint8_t *own, const uint8_t *dst)
 Build an address response (0x37) — device side, used only by the key-extraction responder.
bool create_challenge_resp_device_role (IoFrame &f, const uint8_t *dst, const uint8_t *src, const uint8_t challenge[HMAC_SIZE], const IoFrame &origin, const uint8_t *key)
 Build a device-role challenge response (0x3D) — device side, used only by the key-extraction responder answering a hub-issued 0x3C challenging our own 0x37.
bool create_status_update_resp (IoFrame &f, const uint8_t *own, const uint8_t *dst)
 Build a status-update acknowledgment (0x72).
bool create_set_config1 (IoFrame &f, const uint8_t *own, const uint8_t *dst)
 Build a set-config command (0x6F) to tell the device to automatically send status updates when controlled by any remote (not just us).
const char * command_name (uint8_t cmd)
 Get a human-readable name for any IO-Homecontrol command ID.
const char * manufacturer_name (uint8_t id)
 Get a human-readable manufacturer name from the protocol manufacturer byte.
const char * att_class_name (uint8_t att_class)
 Get a human-readable turnaround time string for an ATT class value.
const char * power_save_mode_name (uint8_t mode)
 Get a human-readable power save mode name.
const char * originator_name (uint8_t originator)
 Get a human-readable name for a command originator byte.
const char * acei_level_name (uint8_t level)
 Get a human-readable name for an ACEI priority level (0–7).
const char * command_result_name (uint8_t result)
 Return a stable symbolic name for a CMD_ERROR_RESP result code.
const char * command_result_description (uint8_t result)
 Return a human-readable explanation for a CMD_ERROR_RESP result code.
bool is_limitation_result (uint8_t result)
 Check whether a result code represents an environmental or control limitation.
constexpr uint8_t fp_bitmap_bit (uint8_t fp)
 Bit of functional parameter fp (1-16) in its bitmap: FPI1 holds FP1-FP8 and FPI2 FP9-FP16, each with the lowest-numbered parameter in bit 7.
constexpr uint8_t discovery_att_class (uint8_t flags)
 Extract the ATT class field from a discovery response's Multi Information Byte.
constexpr uint32_t att_class_ms (uint8_t att_class)
 Turnaround time of an ATT class in milliseconds: 5, 10, 20 or 40 (each class doubles it).
uint8_t discovery_power_save_mode (uint8_t flags)
 Extract the power save mode field from a discovery response's Multi Information Byte.
constexpr uint8_t error_resp_code (uint8_t result)
 Result code (bits [7:3]) of a CMD_ERROR_RESP byte.
constexpr uint8_t error_resp_state (uint8_t result)
 Execution state (bits [2:0]) of a CMD_ERROR_RESP byte.
constexpr bool command_result_is_transient (uint8_t result)
 Check whether a result code describes one exchange rather than the device's state.
constexpr bool has_feature (DeviceFeatures set, DeviceFeatures bit)
 True when every bit of bit is set in set.
const char * rain_sensor_state_name (RainSensorState state)
 Get a human-readable name for a RainSensorState.
ExecutionState execution_state_of (uint8_t status_byte)
 Read the execution state out of byte 0 of a status reply.
const char * execution_state_name (ExecutionState state)
 Get a human-readable name for an ExecutionState.
const char * battery_state_name (BatteryState state)
 Get the name a battery level is published under.
const char * travel_speed_name (TravelSpeed speed)
 Get the name a travel speed is configured and published under.
bool parse_travel_speed (const std::string &name, TravelSpeed &speed)
 Look up a travel speed by the name travel_speed_name() gives it.
BatteryState decode_battery_state (uint8_t status_byte)
 Decode the coarse battery level from byte 1 of a status payload (CMD_PRIVATE_RESP to a status poll, or CMD_STATUS_UPDATE).
const char * cover_command_name (CoverCommand cmd)
 Get a human-readable name for a CoverCommand.
DeviceType decode_packed_device_type (uint8_t type_msb, uint8_t type_subtype)
 Decode the 10-bit node type from two packed metadata bytes.
uint8_t decode_packed_device_subtype (uint8_t type_subtype)
 Decode a protocol-packed device subtype from the second metadata byte.
void encode_packed_device_type (DeviceType type, uint8_t subtype, uint8_t &type_msb, uint8_t &type_subtype)
 Encode a DeviceType/subtype pair into the two-byte packed metadata format used by discovery responses — the inverse of decode_packed_device_type()/decode_packed_device_subtype().
void decode_position_report (uint16_t target_raw, uint16_t current_raw, bool is_stopped, float &target, float &position)
 Decode target/current position values from a status frame.
bool has_reached_target_position (float target, float position)
 Has the device reached its target within tolerance?
float decode_tilt_report (uint16_t tilt_raw)
 Decode tilt angle from raw 16‑bit value.
bool is_known_device_type (uint16_t raw)
 True when a raw 10-bit device-type value is a named DeviceType enumerator.
const char * device_type_name (DeviceType type)
 Convert a DeviceType to a lowercase string identifier.
const char * yaml_device_type_name (DeviceType type)
 Return the YAML-friendly device-type name for types exposed in the Python schema.
DeviceCapabilityClass device_capability_class (DeviceType type)
 Map a raw IO‑Homecontrol type to the closest ESPHome/Home Assistant entity family.
const char * device_capability_class_name (DeviceType type)
 Get a human‑readable name for a capability class.
bool device_supports_position_control (DeviceType type)
 Does this device type support precise position control (0–100)?
bool device_supports_binary_control (DeviceType type)
 Does this device type support binary on/off control?
bool device_supports_lock_control (DeviceType type)
 Does this device type support binary lock/unlock control via execute commands?
bool device_supports_heat_level_control (DeviceType type)
 Does this device take a heat level as its position (exterior heating)?
bool device_has_no_motion (DeviceType type, uint8_t subtype)
 Does this device never move, so a status reply must not start the movement settle loop?
bool device_supports_climate_control (DeviceType type, uint8_t subtype)
 Can a climate entity drive this device?
bool device_takes_execute_position (DeviceType type)
 Does this device type take position EXECUTEs (a percent, on / off, or stop in the main parameter)?
bool device_supports_status_requests (DeviceType type)
 Does this device type support status request commands (0x03)?
const char * device_operation_profile_name (DeviceType type, uint8_t subtype)
 Human‑readable operation profile name for a device kind.
void resolve_device_features (IoDevice &dev)
 Resolve a device's capability bits and inversion from its kind and its YAML overrides.
std::string format_device_type_diagnostic (DeviceType type)
 Human-readable device type string for diagnostics, including the raw numeric value.
std::string format_device_type_for_yaml (DeviceType type)
 Build the YAML value for a device's io_device_type key.
std::string build_device_yaml_snippet (DeviceType type, uint8_t subtype, const std::string &device_id, bool metadata_complete, bool low_power, const std::string &system_key_hex=std::string())
 Build the ready-to-paste YAML block describing a device, for both a fully-decoded device and one whose type/subtype wasn't reported.
void note_moving_evidence (IoDevice &dev, uint32_t now_ms)
 Record that a device is (believed to be) travelling right now.
void note_moving_until (IoDevice &dev, uint32_t until_ms)
 Record when the travel the device is reporting should be over, from its own remaining time.
void clear_moving_evidence (IoDevice &dev)
 Forget that a device was travelling: it reported itself stopped (including in the ack to a STOP), a linked remote was heard stopping it, or it stayed silent to an exchange sent while it was believed moving.
int16_t rssi_scaled_to_dbm (int16_t scaled)
 Convert an rssi_ema_scaled fixed-point value to whole dBm (round half away from zero).
int16_t device_rssi_ema_dbm (const IoDevice &dev)
 A device's smoothed RSSI in whole dBm.
float effective_target (const IoDevice &dev)
 The main-position target a consumer should act on: the prediction when one stands, otherwise the device's own last reported target.
float effective_tilt (const IoDevice &dev)
 The slat angle a consumer should act on, prediction first.
bool effective_is_stopped (const IoDevice &dev)
 Whether a consumer should treat the device as at rest, prediction first.
const DeviceTypeInfo * find_exact_device_type_info (DeviceType type, uint8_t subtype)
 Look up the table row with exactly this (type, subtype), without falling back.
const DeviceTypeInfo * find_device_model_info (DeviceType type, uint8_t subtype, DeviceModel model)
 Look up the model row with exactly this (type, subtype, model), without falling back.
bool device_offers_models (DeviceType type, uint8_t subtype)
 Does this exact (type, subtype) have model rows below it?
const DeviceTypeInfo * find_device_type_info (DeviceType type, uint8_t subtype, DeviceModel model)
 Look up the table row for a (type, subtype, model).
bool device_has_platform (DeviceType type, uint8_t subtype)
 Can an ESPHome platform drive this (type, subtype) today?
const char * device_platform_name (DeviceType type, uint8_t subtype)
 Name of the ESPHome platform that drives this (type, subtype).
bool device_is_experimental (DeviceType type, uint8_t subtype)
 Is this (type, subtype) a kind no capture or report has confirmed on a real device?
DeviceCapabilityClass device_capability_class (DeviceType type, uint8_t subtype)
 Capability class of a (type, subtype), subtype-aware.
DeviceFeatures device_features (DeviceType type, uint8_t subtype, DeviceModel model)
 Capability bits of a (type, subtype, model).
DeviceFeatures undeclared_kind_features (DeviceCapabilityClass entity_class)
 Features of a device kind that has no table row, or that is not declared at all.
std::string format_device_kind (DeviceType type, uint8_t subtype)
 Human-readable description of a (type, subtype) for logs and reports.
std::string format_device_ids (DeviceType type, uint8_t subtype)
 Wire values of a (type, subtype), for logs that must stay readable without the table.
constexpr DeviceFeatures main_parameter_only_variant (DeviceFeatures type_features)
 The features of a subtype-58 variant of a kind.
constexpr uint16_t main_parameter_of (uint8_t high, uint8_t low=0)
 Combine the two bytes of a main parameter as the EXECUTE payload carries them.
const NamedPosition * find_named_position (CoverCommand command)
 The named position a command sends.
bool command_main_parameter (CoverCommand command, uint16_t &main_parameter)
 The main parameter a named command sends.
constexpr bool device_accepts_main_parameter (DeviceFeatures features, uint16_t main_parameter)
 True when a device with these features accepts this main-parameter value in an EXECUTE.
constexpr uint16_t position_main_parameter (uint8_t position)
 The main parameter set_device_position() sends for a percent position or the POS_STOP sentinel.
size_t encode_dhw_register_read (uint8_t index, uint8_t out[HEATING_PAYLOAD_MAX_SIZE])
 Encode a read of one family-0x00 register: 0C 60 00 <index>.
size_t encode_dhw_mode_write (uint8_t mode, uint8_t out[HEATING_PAYLOAD_MAX_SIZE])
 Encode a write of the operating mode: 0C 61 00 06 <mode>.
const DhwOperatingFeatureWire & dhw_operating_feature_wire (DhwOperatingFeature which)
 The wire facts of which: the one place each feature's FP15 bit, FP16 field and duration live.
size_t encode_dhw_duration_write (DhwOperatingFeature which, uint8_t out[HEATING_PAYLOAD_MAX_SIZE])
 Encode the duration write a start of which sends before its FP16 EXECUTE: 0C 61 00 00 01 for boost (DHW_BOOST_START_DURATION), 0C 61 00 05 FF for away (DHW_AWAY_UNTIL_TURNED_OFF).
bool encode_dhw_target (float celsius, uint16_t &main_parameter)
 Encode a target temperature as a main parameter over DHW_TARGET_MIN_C..DHW_TARGET_MAX_C.
bool decode_dhw_register_reply (const IoFrame &reply, uint8_t index, const uint8_t *&value, size_t &len)
 The value bytes of a reply to a family-0x00 register read.
bool decode_dhw_water_temperature (const uint8_t *value, size_t len, float &celsius)
 Water temperature from the value of register 0x0017: signed 16-bit big-endian tenths.
bool decode_dhw_mode (const uint8_t *value, size_t len, DhwMode &mode)
 Operating mode from the value of register 0x0006.
bool decode_dhw_heating_state (const uint8_t *value, size_t len, DhwHeatingState &state)
 What is heating, from the value of register 0x0021.
bool decode_dhw_energy_wh (const uint8_t *value, size_t len, uint32_t &wh)
 Energy since installation from the value of register 0x0004: unsigned 32-bit big-endian, Wh.
bool decode_dhw_duration (const uint8_t *value, size_t len, uint8_t &duration)
 A duration from the value of register 0x0000 or 0x0005: one byte.
bool decode_dhw_target (uint16_t main_parameter, float &celsius)
 Target temperature from a main parameter.
bool decode_dhw_status_target (const IoFrame &reply, float &celsius)
 Target temperature from a status reply (CMD_PRIVATE_RESP) of a domestic hot water heater.
bool decode_dhw_operating_status (const IoFrame &reply, DhwOperatingStatus &status)
 Capabilities and operating-mode state from the reply to the status read DHW_STATUS_FPI1 / DHW_STATUS_FPI2.
static int hex_nibble (char ch)
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.
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.
uint16_t crc_ccitt (const uint8_t *data, uint8_t len)
 CRC-CCITT used by the IO-Homecontrol protocol for frame validation.
void init_frame (IoFrame &f, bool is_2w=true, bool start=false, bool end=false, bool low_power=false)
 Initialize an IoFrame header (ctrl0/ctrl1) with flags.
void set_dst (IoFrame &f, const uint8_t id[NODE_ID_SIZE])
 Set destination node ID.
void set_src (IoFrame &f, const uint8_t id[NODE_ID_SIZE])
 Set source node ID.
bool set_cmd (IoFrame &f, uint8_t cmd, const uint8_t *params=nullptr, uint8_t params_len=0)
 Set command and payload.
uint8_t frame_length (const IoFrame &f)
 Get total frame length from ctrl0.
bool is_start (const IoFrame &f)
 Check START flag.
bool is_end (const IoFrame &f)
 Check END flag.
bool frame_carries_mac_trailer (uint8_t cmd)
 Whether wire frames for a command carry the out-of-length MAC trailer described on IoFrame::has_mac/IoFrame::mac.
uint8_t serialize (const IoFrame &f, uint8_t *buf, uint8_t buf_size)
 Serialize a parsed frame into a wire buffer (without CRC).
bool parse (const uint8_t *buf, uint8_t buf_len, IoFrame &f)
 Parse a wire buffer into a parsed IoFrame (validates length and CTRL0).
bool stored_node_id_is_valid (const uint8_t id[NODE_ID_SIZE])
 Check whether a node ID is usable as an address: not all-zero and not all-0xFF, the two patterns blank storage and unset fields produce.
const char * heating_function_name (HeatingFunction fn)
 Stable lowercase name for a heating function ("power_on", "set_temperature", ...).
bool heating_function_is_writable (HeatingFunction fn)
 Whether the register behind a function can be written.
bool heating_function_is_readable (HeatingFunction fn)
 Whether the register behind a function can be read.
bool heating_function_register (HeatingFunction fn, uint8_t &register_low)
 Low byte of the 0x01xx register behind a function.
bool heating_reply_echoes_register (const IoFrame &reply, uint8_t reg_hi, uint8_t reg_lo)
 Is this frame a register reply that echoes a request for this register?
size_t encode_heating_read (HeatingFunction fn, uint8_t out[HEATING_PAYLOAD_MAX_SIZE])
 Encode a read ("get") of one register into a CMD_WRITE_PRIVATE (0x20) payload.
size_t encode_heating_register_read (uint8_t reg_hi, uint8_t reg_lo, uint8_t out[HEATING_PAYLOAD_MAX_SIZE])
 Encode a read ("get") of an arbitrary register into a CMD_WRITE_PRIVATE (0x20) payload.
size_t encode_heating_write (HeatingFunction fn, float value, uint8_t out[HEATING_PAYLOAD_MAX_SIZE])
 Encode a write ("set") of one register into a CMD_WRITE_PRIVATE (0x20) payload.
size_t encode_heating_payload (HeatingFunction fn, float value, uint8_t out[HEATING_PAYLOAD_MAX_SIZE])
 Encode the function the heating_control action runs: a read for a read-only register, a write for every other.
bool decode_heating_reply (const IoFrame &reply, HeatingFunction fn, float &value)
 Decode the value a heater answered a register read or write with.
const HeatingProfile * find_heating_profile (DeviceType type, uint8_t subtype, DeviceModel model)
 Look up the heating profile for a (type, subtype, model).
const HeatingProfile * find_heating_profile (const IoDevice &dev)
 Look up the heating profile of a device record: its type, subtype and declared model.
bool device_uses_heating_registers (DeviceType type, uint8_t subtype)
 Does a climate entity drive this device over the private register protocol?
bool device_accepts_register_probe (DeviceType type, uint8_t subtype)
 May the diagnostic register-read probes (register_00, register_01) be sent to this device?
bool device_uses_heating_levels (DeviceType type, uint8_t subtype)
 Does a climate entity drive this device with heating levels?
bool device_uses_heating_temperature (DeviceType type, uint8_t subtype)
 Does a climate entity drive this device with a temperature or an alias in the main parameter?
bool encode_temperature_main_parameter (float celsius, float min_c, float max_c, uint16_t &main_parameter)
 Encode a temperature as a main parameter over an encoding range.
bool decode_temperature_main_parameter (uint16_t main_parameter, float min_c, float max_c, float &celsius)
 Decode a main parameter as a temperature over an encoding range (the inverse of encode_temperature_main_parameter()).
bool decode_heating_level (uint16_t main_parameter, uint8_t &level)
 Read a heating level out of a main parameter.
const HeatingObservedState * find_observed_state (const HeatingProfile &profile, uint8_t wire)
 Find what a device-reported wire value shows on the card.
const HeatingObservedState * find_observed_preset (const HeatingProfile &profile, uint8_t wire)
 Find what a device-reported value of the preset register (preset_fn) shows on the card.
bool programme_period_active (const HeatingProfile &profile, const HeatingReadings &readings)
 Does the heater's own programme run right now, with no timed run on top of it?
ProgrammePeriodState resolve_programme_period (const HeatingProfile &profile, const HeatingReadings &readings)
 Which period of its programme a heater is in, and the temperature it heats to.
void clear_inactive_programme_reading (const HeatingProfile &profile, HeatingReadings &readings)
 Drop the temperature in force from the readings when the programme does not run.
constexpr uint16_t heating_level_main_parameter (uint8_t level)
 Main parameter of a heating level, as it travels in an EXECUTE and in a status reply.
constexpr uint16_t heating_prefixed_main_parameter (uint8_t prefix, uint8_t low)
 Main parameter of a prefixed value (a level or an alias).
constexpr bool is_wake_preamble (uint16_t len)
 True when a preamble this long is the wake-up burst for a duty-cycled receiver.
constexpr TxWake tx_wake_for (bool start, uint16_t preamble)
 The wake-up level of a frame, from the two facts that decide it: whether it starts an exchange (a continuation reaches a receiver that is already listening) and whether its preamble is the wake-up burst.
const char * tcxo_voltage_label (uint8_t code)
 Human-readable voltage for a tcxo_voltage code, for the config dump.
void lr1121_format_device_errors (uint16_t errors, char *buf, size_t buf_size)
 Expand a GetErrors word into a human-readable NAME|NAME|... string.
const char * lr1121_chip_mode_name (uint8_t stat2)
 Name of the chip mode in a Stat2 byte (bits 3:1, LR1121 user manual "Status Registers").
constexpr bool lr1121_firmware_is_outdated (uint8_t fw_major, uint8_t fw_minor)
 Pure comparison against the known-latest constants above — no I/O, host-testable.
template<typename Ready>
PollResult poll_until (Ready ready, uint32_t timeout_ms)
 Poll ready() until it is true or timeout_ms has passed, feeding the watchdog between samples.
uint8_t decode_uart_probe (const uint8_t *raw, uint8_t raw_len, uint8_t bit_offset, uint8_t *decoded, uint8_t decoded_max_len)
 Decode a raw UART‑encoded bitstream into bytes.
bool is_known_io_command (uint8_t cmd)
 This list missing CMD_GET_GENERAL_INFO3_RESP (0x59) is exactly what turned a real Q2 probe reply into a false "no reply" timeout on real hardware (2026-08-16); the same audit also found CMD_IDENTIFY and CMD_WRITE_PRIVATE/CMD_WRITE_PRIVATE_ACK missing, unrelated to that probe but affecting the already-shipped identify_device() action (and climate writes) on SX1262/LR1121.
static std::pair< uint8_t, uint8_t > find_crc_valid_frame (const uint8_t *decoded, uint8_t decoded_len)
 Try to find a CRC-valid IO-Homecontrol frame within a decoded UART byte stream.
UartProbeResult find_uart_probe (const uint8_t *raw, uint8_t raw_len)
 Search raw RX buffer for the best CRC-validated IO-Homecontrol frame.
uint8_t soft_phy_raw_bytes_for_frame (uint8_t frame_len)
 Raw on-air bytes needed to carry a whole frame: frame_len protocol bytes plus the two trailing CRC bytes, each UART-packed into a 10-bit cell.
uint32_t io868_tx_air_time_us (uint16_t preamble_len, uint8_t frame_len)
 Time on air of one 868 MHz transmission: preamble, sync word, and the frame plus its CRC, each byte of the latter a 10-bit UART cell, at the protocol's line rate.
uint8_t soft_phy_peek_frame_length (const uint8_t *raw, uint8_t raw_len)
 Recover a frame's total length from the very first UART cell of a reception.
uint8_t uart_encode_packet (const uint8_t *data, uint8_t len, uint8_t *encoded, uint8_t encoded_max_len)
 UART-encode a buffer of bytes (start bit 0, 8 data bits LSB-first, stop bit 1).
constexpr uint32_t soft_phy_air_time_us (uint32_t raw_bytes)
 On-air time in microseconds for raw_bytes bytes at the protocol's line rate.
constexpr uint32_t tx_done_timeout_ms (uint32_t air_time_us)
 How long a driver waits for TX-done after starting a transmission.
void format_device_error_bits (uint16_t errors, const DeviceErrorBit *bits, size_t bit_count, char *buf, size_t buf_size)
 Expand a device-error word into a human-readable NAME|NAME|... string.
template<size_t N>
void format_device_error_bits (uint16_t errors, const DeviceErrorBit(&bits)[N], char *buf, size_t buf_size)
 Expand a device-error word into a human-readable NAME|NAME|... string.
FemRxGain sx1262_fem_rx_gain (FemProfile profile)
 The receive gain to remove from RSSI for a FEM profile.
FemTxEstimate sx1262_fem_tx_estimate (FemProfile profile, uint8_t tx_power)
 Estimate what a FEM profile radiates for a configured tx_power.
const char * sx1262_chip_mode_name (uint8_t chip_status)
 Name of the chip mode in a GetStatus byte (bits 6:4).
uint8_t sx1262_command_status (uint8_t chip_status)
 Command status field of a GetStatus byte (bits 3:1).
void sx1262_format_device_errors (uint16_t errors, char *buf, size_t buf_size)
 Expand a GetDeviceErrors word into a human-readable NAME|NAME|... string.
bool command_carries_key_material (uint8_t cmd)
 Whether a frame command's payload carries key material that must never be logged or reported verbatim.
bool contains_key_material (const uint8_t *buf, size_t len, const uint8_t *system_key)
 Whether a buffer contains the system key as a contiguous run of bytes.
ScanAddResult add_scan_responder (ScanResponder *responders, uint8_t &count, uint8_t capacity, const IoFrame &frame, int16_t rssi_dbm, uint8_t key_slot, const ScanResponder *&entry)
 Record a responder unless its address is already present.
uint8_t count_enabled_passes (ScanPowerClasses selection)
 How many of ROLL_CALL_PASSES selection actually calls.
RollCallAttemptResult run_roll_call_sweep (ExchangeEngine &engine, const uint8_t *node_id, const uint8_t *key, uint8_t key_slot, const TuningConfig &tuning, ScanState &state, uint8_t &attempt, uint8_t total_attempts)
 Run the full roll-call sweep under one key: every enabled pass on every channel.
std::string format_roll_call_responder (const ScanResponder &responder, const std::string &device_id, const IoDevice *registered, const RollCallReplyKeys &keys, RollCallKeyDisplay display)
 Format one roll-call responder's report line(s).
std::string build_hub_key_pull_report (const PulledKeyReportInfo &info, const ScanState &state, DeviceRegistry &registry)
 Build the report for a pull that returned the hub's own key from a node no platform drives.
std::string build_pulled_key_report (const PulledKeyReportInfo &info, const ScanState &state, DeviceRegistry &registry)
 Build the one report a proven key pull prints after looking for the devices that hold the key.
constexpr bool no_roll_call_pass_holds_request_channel ()
 A broadcast has no pinned conversation to hold a channel for, so HOLD_REQUEST_CHANNEL can never be a valid roll-call listen policy — checked at compile time rather than at every call site.
uint32_t settle_delay_ms (uint32_t interval_ms, uint32_t remaining_delay_ms, bool cap_for_stop)
 Resolve the follow-up settle-poll delay while a device may still be moving.
float bandwidth_to_khz (const BandwidthOption *table, size_t n, uint8_t reg, float fallback)
 Look up the kHz value for a register byte in a bandwidth table.
std::string bandwidth_to_string (float khz)
 Format a kHz value as its YAML/UI option string (bare number, one decimal, e.g. "117.3").
std::optional< uint8_t > bandwidth_from_string (const BandwidthOption *table, size_t n, const std::string &value)
 Parse a YAML/UI bandwidth string against a table, returning the matching register byte.
BandwidthTableView sx1262_bandwidth_table ()
 The SX1262 RX-bandwidth option table.
BandwidthTableView sx1276_bandwidth_table ()
 The SX1276 RX-bandwidth option table (see sx1262_bandwidth_table()).
BandwidthTableView lr1121_bandwidth_table ()
 The LR1121 RX-bandwidth option table (see sx1262_bandwidth_table()).
float sx1262_bandwidth_to_khz (SX1262RxBandwidth bw)
 Convert a bandwidth enum to the numeric kHz value used in YAML/logs.
std::string sx1262_bandwidth_to_string (SX1262RxBandwidth bw)
 Format a bandwidth enum as its YAML/UI option string (bare kHz number, e.g.
std::optional< SX1262RxBandwidth > sx1262_bandwidth_from_string (const std::string &value)
 Convert a YAML bandwidth string to the enum value.
float sx1276_bandwidth_to_khz (SX1276RxBandwidth bw)
 Convert an SX1276 bandwidth enum to the numeric kHz value used in YAML/logs.
std::string sx1276_bandwidth_to_string (SX1276RxBandwidth bw)
 Format an SX1276 bandwidth enum as its YAML/UI option string (bare kHz number).
std::optional< SX1276RxBandwidth > sx1276_bandwidth_from_string (const std::string &value)
 Convert a YAML SX1276 bandwidth string to the enum value.
float lr1121_bandwidth_to_khz (LR1121RxBandwidth bw)
 Convert an LR1121 bandwidth enum to the numeric kHz value used in YAML/logs.
std::string lr1121_bandwidth_to_string (LR1121RxBandwidth bw)
 Format an LR1121 bandwidth enum as its YAML/UI option string (bare kHz number).
std::optional< LR1121RxBandwidth > lr1121_bandwidth_from_string (const std::string &value)
 Convert a YAML LR1121 bandwidth string to the enum value.
std::optional< DiscoveryCommand > discovery_command_from_string (const std::string &value)
 Parse a discovery command string (e.g., "0x28") into the enum.
std::string discovery_command_to_string (DiscoveryCommand cmd)
 Format a discovery command enum for YAML/logs.
std::string discovery_commands_to_csv (const std::vector< DiscoveryCommand > &commands)
 Format the ordered discovery command list as a UI/preset option string.
std::string discovery_commands_to_string (const std::vector< DiscoveryCommand > &commands)
 Format the ordered discovery command list for logs.
const uint8_t * resolve_discovery_destination (uint8_t command, bool destination_auto, const uint8_t destination[NODE_ID_SIZE])
 Resolve the destination address for a discovery command.
std::string discovery_destination_to_string (bool destination_auto, const uint8_t destination[NODE_ID_SIZE])
 Format a destination option for YAML/logs.
std::string discovery_payload_to_string (bool payload_enabled, uint8_t payload)
 Format a payload option for YAML/logs.
std::string scan_power_classes_to_string (ScanPowerClasses value)
 Format a ScanPowerClasses value for YAML/logs.
std::optional< ScanPowerClasses > scan_power_classes_from_string (const std::string &value)
 Parse a scan_power_classes string into the enum.
bool scan_power_classes_include (ScanPowerClasses selection, uint8_t power_save_mode)
 Whether selection calls the roll-call pass for power_save_mode.
std::string discover_confirm_mode_to_string (DiscoverConfirmMode value)
 Format a DiscoverConfirmMode value for YAML/logs.
std::optional< DiscoverConfirmMode > discover_confirm_mode_from_string (const std::string &value)
 Parse a pairing_discover_confirm string into the enum.
std::string discovery_listen_channels_to_string (DiscoveryListenChannels value)
 Render a DiscoveryListenChannels as its YAML/select option string.
std::optional< DiscoveryListenChannels > discovery_listen_channels_from_string (const std::string &value)
 Parse a DiscoveryListenChannels from its YAML/select option string.
std::string key_transfer_mode_to_string (KeyTransferMode value)
 Render a KeyTransferMode as its YAML/select option string.
std::optional< KeyTransferMode > key_transfer_mode_from_string (const std::string &value)
 Parse a KeyTransferMode from its YAML/select option string.
std::string tuning_update_log_line (const std::string &name, const std::string &value)
 Format a single tuning update for the log.
std::string tuning_config_snapshot (const TuningConfig &cfg)
 Format the current tuning configuration as a one-line YAML-compatible snapshot.
std::string tuning_config_full_snapshot (const TuningConfig &cfg)
 Format the current tuning configuration as a full one-line snapshot.
uint16_t effective_discovery_preamble (const TuningConfig &cfg)
 The preamble length (bytes) of the pairing discovery broadcast (0x28, 0x2E).
template<bool TuningConfig::* Field>
std::string bool_select_get (const TuningConfig &t)
template<bool TuningConfig::* Field>
bool bool_select_set (TuningConfig &t, const std::string &v)
const TuningNumberParam * find_tuning_number (const std::string &name)
 Look up a numeric tuning parameter by name; returns nullptr if unknown.
const TuningSelectParam * find_tuning_select (const std::string &name)
 Look up a select tuning parameter by name; returns nullptr if unknown.
Table iteration

Range accessors over the full parameter tables, for enumeration and tests.

const TuningNumberParam * tuning_number_params_begin ()
const TuningNumberParam * tuning_number_params_end ()
const TuningSelectParam * tuning_select_params_begin ()
const TuningSelectParam * tuning_select_params_end ()

Variables

static constexpr const char * TAG = "home_io_control"
constexpr const char * MANAGEMENT_ACTION_PROBE_DEVICE = "probe_device"
 Native API action names of the two probe actions, shared with ManagementActions' registration table.
constexpr const char * MANAGEMENT_ACTION_PROBE_SWEEP = "probe_sweep"
static const char *const TAG = "home_io_control.exchange"
static constexpr uint32_t US_PER_MS = 1000
 Microseconds per millisecond, for rounding a driver's transmit-time estimate up to whole ms.
static constexpr size_t EXCHANGE_DEBUG_LINE_SIZE = 384
 Longest rendered exchange-debug field list, plus headroom for a long command name.
static const char *const TAG = detail::TAG
static constexpr uint32_t KEY_EXTRACTION_SCAN_SLICE_MS = 200
constexpr uint8_t DEFAULT_TX_POWER_DBM = 17
 Default TX power used unless YAML overrides it.
constexpr uint8_t DEFAULT_PA_PIN_PA_BOOST = 0x80
 SX1276 PA_CONFIG selector for the PA_BOOST output path.
constexpr uint8_t DEFAULT_TCXO_VOLTAGE_SETTING_1P8V = 0x02
 0-based TCXO voltage code for 1.8 V, passed verbatim to the SX1262/LR1121 chip.
constexpr size_t POSITION_TEXT_BUFFER_SIZE = 16
 Buffer for formatted position strings such as "100%".
static constexpr uint32_t PAIRING_STALE_PRESS_MS = 1000
 How long after a pairing attempt ends a Discover & Pair press counts as made during it.
static constexpr uint32_t KEY_EXTRACTION_SESSION_WINDOW_MS
 The session window for the default Multi Information Byte (KEY_EXTRACTION_DISCOVER_RESP_FLAGS).
constexpr size_t FRAME_LOG_HEX_BUFFER_SIZE = 220
 Fits a full 32-byte frame rendered as spaced hex text.
constexpr size_t FRAME_LOG_FLAGS_BUFFER_SIZE = 32
 Buffer for decoded CTRL1 flag annotations.
constexpr size_t FRAME_LOG_HELD_LINES = 6
 Lines one hold can keep.
constexpr size_t FRAME_LOG_NOTE_SIZE = 64
 The overflow note flush() prints when lines did not fit.
constexpr size_t FRAME_LOG_LINE_SIZE = FRAME_LOG_HEX_BUFFER_SIZE + 96
 One rendered frame-log line: the hex dump plus its prefix, frequency, command and flags.
constexpr uint8_t LR1121_DEVICE_TYPE_FOR_FIRMWARE_DECISIONS = 0x03
 LR1121 GetVersion type byte in normal mode — must match radio_lr1121.h's LR1121_DEVICE_TYPE (0x03).
constexpr uint8_t LR1110_DEVICE_TYPE_FOR_FIRMWARE_DECISIONS = 0x01
 Normal-mode GetVersion type bytes for the two chips an LR1121 is most easily confused with — companions to LR1121_DEVICE_TYPE_FOR_FIRMWARE_DECISIONS above, used only to name the chip family in a REJECT_WRONG_CHIP message.
constexpr uint8_t LR1120_DEVICE_TYPE_FOR_FIRMWARE_DECISIONS = 0x02
constexpr uint8_t LR1121_BOOTLOADER_TYPE_FOR_FIRMWARE_DECISIONS = 0xDF
 LR1121 GetVersion type byte reported while running the bootloader (LR11XX_TYPE_PRODUCTION_MODE) — must match radio_lr1121_firmware_updater.h's LR1121_UPDATER_BOOTLOADER_TYPE (0xDF).
constexpr uint16_t LR1121_BOOTLOADER_2100 = 0x2100
 LR1121 bootloader versions, from Semtech's published compatibility matrix.
constexpr uint16_t LR1121_BOOTLOADER_2101 = 0x2101
constexpr uint16_t LR1120_BOOTLOADER_2000 = 0x2000
 Bootloader versions reported by the two chips an LR1121 is most easily confused with — used only to name the chip family in a REJECT_WRONG_CHIP message.
constexpr uint16_t LR1120_BOOTLOADER_2001 = 0x2001
constexpr uint16_t LR1110_BOOTLOADER_6500 = 0x6500
constexpr uint16_t LR1110_BOOTLOADER_1001 = 0x1001
constexpr uint16_t LR1121_LOADER_2100 = 0x2100
 Version the lr1121_loader_2100.bin bootloader-*loader* image reports of itself.
constexpr Lr1121BootloaderRequirement LR1121_KNOWN_BOOTLOADER_REQUIREMENTS []
 Bootloader requirements we know about, copied from Semtech's published pairings (SWTL001/application/src/lr11xx_update_utils.c :: compatibility_matrix[]): 0x0101/0x0102/0x0103 need bootloader 0x2100, 0x0104 needs 0x2101.
constexpr size_t RESULT_CODE_BUFFER_SIZE = 5
 Size of the scratch buffer format_hex_byte() prints into: two digits plus the terminator, with headroom.
static constexpr std::array< DeviceType, 3 > VELUX_KLI_ENROLLMENT_CLASSES
 The default 0x30 sweep for manufacturer: velux: the exterior-shading classes a KLI 310/313 names — roller shutter, vertical exterior awning, dual roller shutter (issue #74 capture, decoded with broadcast_target_type()).
static constexpr uint8_t ONEWAY_POSITION_FULLY_OPEN = 0
 User-scale position meaning "fully closed" (0 means fully open).
static constexpr uint8_t ONEWAY_POSITION_FULLY_CLOSED = 100
static constexpr uint16_t ONEWAY_SEQUENCE_ACCEPTANCE_WINDOW = 1000
 How far ahead of its stored high-water mark a device will still accept a jump.
static constexpr uint16_t ONEWAY_SEQUENCE_STRIDE = 8
 How many sequences one flash write reserves.
static constexpr uint16_t ONEWAY_SEQUENCE_REBOOT_HEADROOM = 8
 How many consecutive unclean reboots the stride must survive while staying inside a device's acceptance window.
static constexpr uint8_t BINARY_ENTITY_ON_POSITION = 0
 Position value written for binary ON commands (light on, switch on, lock unlock).
static constexpr uint8_t BINARY_ENTITY_OFF_POSITION = 100
 Position value written for binary OFF commands (light off, switch off, lock lock).
static constexpr uint8_t PAIRING_TELEMETRY_MAX_EVENTS = 32
 Maximum number of events recorded per pairing attempt.
static constexpr uint32_t PERIODIC_POLL_INITIAL_DELAY_MS = 15000
 Earliest first poll after boot.
static constexpr uint32_t PERIODIC_POLL_INITIAL_SPREAD_MS = 30000
 Width of the random window added to PERIODIC_POLL_INITIAL_DELAY_MS, so devices do not start in step.
static constexpr uint32_t PERIODIC_POLL_JITTER_PERCENT = 10
 Per-poll offset, as a percentage of the interval, applied symmetrically around it.
static constexpr uint32_t PERIODIC_POLL_MOVING_RETRY_MS = 30000
 Delay before asking again when the device is moving.
static constexpr uint32_t PERIODIC_POLL_MISS_RETRY_MS = 60000
 Delay before asking once more after a poll the device left unanswered (silence, or a challenge with no final answer).
static constexpr uint8_t PERIODIC_POLL_MAX_TRIES = 2
 Tries per poll.
static constexpr uint8_t PERIODIC_POLL_FAILURES_BEFORE_UNKNOWN = 3
 Consecutive unanswered polls after which the last reading is no longer usable.
static const char *const TAG = "home_io_control.climate"
static const char *const TAG_WINDOW = "home_io_control.window"
static const char *const TAG_DETECTION = "home_io_control.window_detection"
static const char *const TAG_NIGHT_LIGHT = "home_io_control.night_light"
static constexpr uint8_t NIGHT_LIGHT_MIN_ON_PERCENT = 1
 Brightness register value for a light switched on at a brightness that rounds to 0 %: the lowest level that is still on.
constexpr const char * ACTIVE_ISSUE_RADIO_STOPPED_RESPONDING = "RADIO_STOPPED_RESPONDING"
 Active Issue value while the hub's radio has stopped responding, in the style of the symbolic result names the sensor otherwise publishes.
constexpr const char * ACTIVE_ISSUE_NOT_EXECUTING = "NOT_EXECUTING"
 Active Issue value while a device's status reports execution state 0: it cannot run its last command under the current conditions (example: temperature too high).
constexpr const char * ACTIVE_ISSUE_EXECUTION_ERROR = "EXECUTION_ERROR"
 Active Issue value while a device's status reports execution state 1: an error occurred during its last move.
static const char *const TAG = "home_io_control.cover"
static const char *const TAG_COMMAND_BUTTON = "home_io_control.cover_command_button"
static const char *const TAG_SILENT = "home_io_control.cover.silent"
static const char *const TAG_TRAVEL_SPEED = "home_io_control.cover.travel_speed"
static const char *const TAG_RAIN = "home_io_control.cover.rain_sensor"
static const char *const TAG_BATTERY = "home_io_control.cover.battery"
static const char *const TAG_SUN_LEVEL = "home_io_control.cover.sun_level"
static const char *const TAG_BATTERY_LEVEL = "home_io_control.cover.battery_level"
static const char *const TAG = "home_io_control.fan"
static const char *const TAG = "home_io_control.light"
static const char *const TAG = "home_io_control.lock"
static const char *const TAG = "home_io_control.switch"
static const char *const TAG = "home_io_control.water_heater"
static constexpr uint8_t ONEWAY_EXECUTE_MIN_DATA_LEN = 4
 Minimum data bytes for decode of execute/activate‑mode intent fields.
static constexpr uint8_t DEVICE_NAME_WRITE_CHAR_LIMIT = 15
 Reference write limit before the trailing null.
static constexpr uint8_t DEVICE_NAME_WRITE_PAYLOAD_SIZE
 Fixed write payload: 15 visible chars plus trailing null/padding.
static constexpr uint16_t LATIN1_CODEPOINT_MAX = 0x00FF
 Highest Unicode code point representable in Latin-1.
static constexpr uint8_t ADDRESS_SUFFIX_DISCOVERY_64 = 0x3C
 Discovery suffix opening 64 slots.
static constexpr uint8_t ADDRESS_SUFFIX_DISCOVERY_128 = 0x3D
 Discovery suffix opening 128 slots.
static constexpr uint8_t ADDRESS_SUFFIX_DISCOVERY_255 = 0x3E
 Discovery suffix opening 255 slots.
static constexpr uint8_t DISCOVERY_SLOTS_32 = 32
 Window of ADDRESS_SUFFIX_DISCOVERY, and the fallback.
static constexpr uint8_t DISCOVERY_SLOTS_64 = 64
 Window of ADDRESS_SUFFIX_DISCOVERY_64.
static constexpr uint8_t DISCOVERY_SLOTS_128 = 128
 Window of ADDRESS_SUFFIX_DISCOVERY_128.
static constexpr uint8_t DISCOVERY_SLOTS_255 = 255
 Window of ADDRESS_SUFFIX_DISCOVERY_255.
static constexpr size_t ONEWAY_INTENT_BUFFER_SIZE = 24
 Buffer size for the decoded 1W main-intent string.
constexpr uint8_t PRIVATE_RESPONSE_LAST_COMMAND_OFFSET = 8
 Offset of the last-command record within each status-bearing payload.
constexpr uint8_t STATUS_UPDATE_LAST_COMMAND_OFFSET = 11
constexpr uint8_t STATUS_UPDATE_ORIGINATOR_OFFSET = 14
 Offset of the Command Originator byte in a CMD_STATUS_UPDATE (0x71) payload.
static constexpr uint8_t HEATING_LEVEL_MAX = 0x3F
 Highest heating level low byte create_execute_heating_level() will frame (the secured level).
static constexpr uint8_t CMD_EXECUTE = 0x00
 Set position/open/close/stop — requires authentication.
static constexpr uint8_t CMD_ACTIVATE_MODE = 0x01
 Activate device mode (scene, ventilation) — requires auth.
static constexpr uint8_t CMD_PRIVATE = 0x03
 Get device status — no authentication needed.
static constexpr uint8_t CMD_PRIVATE_RESP = 0x04
 Response to 0x00 and 0x03 (contains position data).
static constexpr uint8_t CMD_PRIVATE2
 Content otherwise undecoded by the wire parser.
static constexpr uint8_t CMD_PRIVATE2_RESP = 0x0D
 Response to CMD_PRIVATE2. See CMD_PRIVATE2's comment.
static constexpr uint8_t CMD_PRIORITY_LEVEL_REQ
 Priority-level (lock) query: a one-byte priority level in, the device's lock state for that level out (CMD_PRIORITY_LEVEL_RESP).
static constexpr uint8_t CMD_PRIORITY_LEVEL_RESP = 0x1A
 Reply to CMD_PRIORITY_LEVEL_REQ.
static constexpr uint8_t CMD_IDENTIFY = 0x1E
 Device physical identification / jog — requires authentication.
static constexpr uint8_t CMD_WRITE_PRIVATE = 0x20
 Write private register (climate/heating devices).
static constexpr uint8_t CMD_WRITE_PRIVATE_ACK = 0x21
 Acknowledgment to CMD_WRITE_PRIVATE.
static constexpr uint8_t CMD_LIMITATION_STATUS_REQ
 Limitation read: asks a device for its resulting minimum or maximum position limit, the value a protective function such as rain protection imposes (CMD_LIMITATION_STATUS_RESP).
static constexpr uint8_t CMD_LIMITATION_STATUS_RESP
 Reply to CMD_LIMITATION_STATUS_REQ.
static constexpr uint8_t CMD_DISCOVER_REQ = 0x28
 Broadcast discovery request.
static constexpr uint8_t CMD_DISCOVER_RESP = 0x29
 Device responds with its ID and type.
static constexpr uint8_t CMD_DISCOVER_SPE_REQ
 Broadcast roll-call answered by every device that already holds this controller's system key, regardless of device type.
static constexpr uint8_t CMD_DISCOVER_SPE_RESP
 Roll-call reply to CMD_DISCOVER_SPE_REQ, sent only by devices that already hold the requesting controller's system key.
static constexpr uint8_t CMD_DISCOVER_CONFIRM = 0x2C
 Confirm discovery to device.
static constexpr uint8_t CMD_DISCOVER_CONFIRM_ACK = 0x2D
 Device acknowledges confirmation.
static constexpr uint8_t CMD_DISCOVER_ALT_REQ
 Alternate discovery.
static constexpr uint8_t CMD_DISCOVER_ALT_RESP
 Reply to an addressed (non-broadcast) CMD_DISCOVER_ALT_REQ, following a 0x3C/0x3D challenge-response.
static constexpr uint8_t CMD_ONEWAY_ADD_CONTROLLER
 1W "add controller" — a 1W device broadcasts this while its key-copy gesture is active, handing its network's wrapped system key to whichever controller is listening.
static constexpr uint8_t CMD_ONEWAY_REMOVE = 0x39
 1W "remove controller" (un-pair a 1W remote from a device); same payload shape as 0x2E.
static constexpr uint8_t CMD_KEY_INIT = 0x31
 Initiate key transfer to device.
static constexpr uint8_t CMD_KEY_TRANSFER = 0x32
 Send encrypted system key to device.
static constexpr uint8_t CMD_KEY_CONFIRM = 0x33
 Device confirms key was received.
static constexpr uint8_t CMD_NODE_VERIFY_REQ
 Node verification request: a controller checks that a node belongs to its system.
static constexpr uint8_t CMD_NODE_VERIFY_RESP
 Node verification response: the device returns its own 3-byte backbone address, byte-identical to the one it reported at data[2..4] (DISCOVERY_RESP_BACKBONE_OFFSET) of its CMD_DISCOVER_RESP earlier in the same session — an independent confirmation of that offset.
static constexpr uint8_t CMD_LAUNCH_KEY_TRANSFER
 Key transfer request ("pull"): a controller asks a device for its system key.
static constexpr uint8_t CMD_CHALLENGE_REQ
 6-byte random challenge.
static constexpr uint8_t CMD_CHALLENGE_RESP
 HMAC proof answering a 0x3C.
static constexpr uint8_t CMD_TRANSFER_OPEN
 Opens a read of one object: nine payload bytes <originator> <class> 00 <id hi> <id lo> < 00 00 00 00.
static constexpr uint8_t CMD_TRANSFER_OPEN_RESP
 Answer to CMD_TRANSFER_OPEN: <handle> <status> <length u16 BE>.
static constexpr uint8_t CMD_UNKNOWN4A_REQ
 Reads chunk <handle><chunk u16> of a buffer opened by CMD_TRANSFER_OPEN, counting down from ceil(length/18) to 1, then <handle> 00 00 closes it.
static constexpr uint8_t CMD_UNKNOWN4A_RESP
 Echoes <handle><chunk> followed by up to 18 bytes of data (none in the close echo).
static constexpr TransferObject TRANSFER_READABLE_OBJECTS []
 Every object the read_object probe can open, in probe-index order (ADR 0062).
static constexpr uint8_t TRANSFER_READABLE_OBJECT_COUNT
 Number of entries in TRANSFER_READABLE_OBJECTS.
static constexpr uint8_t CMD_GET_NAME = 0x50
 Request device name.
static constexpr uint8_t CMD_GET_NAME_RESP = 0x51
 Device name response.
static constexpr uint8_t CMD_SET_NAME = 0x52
 Set device name (authenticated).
static constexpr uint8_t CMD_SET_NAME_RESP = 0x53
 Device-name write response.
static constexpr uint8_t CMD_GET_INFO1
 Request device general info 1.
static constexpr uint8_t CMD_GET_INFO1_RESP
 Device general info 1 response.
static constexpr uint8_t CMD_GET_INFO2 = 0x56
 Request device type/model info.
static constexpr uint8_t CMD_GET_INFO2_RESP = 0x57
 Device type/model response.
static constexpr uint8_t CMD_GET_GENERAL_INFO3
 Observed on the wire (tests/corpus/captures/probe/velux_kig300_probe_capability_burst.yaml) with no payload.
static constexpr uint8_t CMD_GET_GENERAL_INFO3_RESP
 Never captured on our own wire.
static constexpr uint8_t CMD_SET_CONFIG1 = 0x6F
 Configure device to auto-send status updates.
static constexpr uint8_t CMD_SET_CONFIG1_RESP
 Config response; meaning undecoded, never captured.
static constexpr uint8_t CMD_SUBSCRIBE_EVENT
 Subscribe a device to one event: payload <action> <event hi> <event lo>, action EVENT_SUBSCRIPTION_SUBSCRIBE or EVENT_SUBSCRIPTION_UNSUBSCRIBE.
static constexpr uint8_t CMD_STATUS_UPDATE = 0x71
 Device status update, sent by the device on its own (authenticated, acknowledged with CMD_STATUS_UPDATE_RESP) or as the answer to a CMD_SUBSCRIBE_EVENT.
static constexpr uint8_t CMD_STATUS_UPDATE_RESP = 0x72
 Acknowledge status update.
static constexpr uint8_t SUBSCRIBE_EVENT_DATA_SIZE = 3
 CMD_SUBSCRIBE_EVENT payload size: the action byte and the big-endian event id.
static constexpr uint16_t EVENT_STATUS_CHANGE = 0x100A
 The event a position-bearing CMD_STATUS_UPDATE carries (every captured one, e.g.
static constexpr uint8_t CMD_SEND_RAW_MESSAGE
 Named "Send Raw Message" / "Find Hardware" — two candidate names, neither settled.
static constexpr uint8_t CMD_READ_GROUPS
 Named "Actuator: Read Groups" / "ActuatorAnyConfigIsLocal" (uncertain) / "Service ACK" — three candidate names, one itself flagged uncertain.
static constexpr uint8_t CMD_REBOOT
 Named "Reboot" / "Service Status" — two candidate names, one of them destructive-sounding, on no field evidence at all.
static constexpr uint8_t CMD_SERVICE_STATUS_ACK = 0xF3
 No description available at all for this opcode, not even a hedge.
static constexpr uint8_t CMD_ERROR_RESP = 0xFE
 Error response to any command.
static constexpr uint8_t ERROR_RESP_CODE_SHIFT = 3
 Result code sits in bits [7:3] of the 0xFE byte.
static constexpr uint8_t ERROR_RESP_STATE_MASK = 0x07
 Execution state sits in bits [2:0] of the 0xFE byte.
static constexpr uint8_t ERROR_RESP_CODE_MAX = 0x11
 Highest named result code (17, "already configured").
static constexpr uint8_t RESULT_UNKNOWN_STATUS_REPLY = 0x00
 Device returned an unknown status reply.
static constexpr uint8_t RESULT_COMMAND_COMPLETED_OK = 0x01
 No errors detected.
static constexpr uint8_t RESULT_NO_CONTACT = 0x02
 No communication to node.
static constexpr uint8_t RESULT_MANUALLY_OPERATED = 0x03
 Manually operated by a user.
static constexpr uint8_t RESULT_BLOCKED = 0x04
 Node blocked by an object.
static constexpr uint8_t RESULT_WRONG_SYSTEMKEY = 0x05
 Node contains the wrong system key.
static constexpr uint8_t RESULT_PRIORITY_LEVEL_LOCKED = 0x06
 Node is locked on this priority level.
static constexpr uint8_t RESULT_REACHED_WRONG_POSITION = 0x07
 Node stopped in another position than expected.
static constexpr uint8_t RESULT_NO_EXECUTION = 0x09
 Node did not move.
static constexpr uint8_t RESULT_CALIBRATING = 0x0A
 Node is calibrating.
static constexpr uint8_t RESULT_POWER_CONSUMPTION_TOO_HIGH = 0x0B
 Node power consumption is too high.
static constexpr uint8_t RESULT_POWER_CONSUMPTION_TOO_LOW = 0x0C
 Node power consumption is too low.
static constexpr uint8_t RESULT_LOCK_POSITION_OPEN = 0x0D
 Lock command failed because the door is open.
static constexpr uint8_t RESULT_MOTION_TIME_TOO_LONG = 0x0E
 Target was not reached in time.
static constexpr uint8_t RESULT_THERMAL_PROTECTION = 0x0F
 Node entered thermal protection mode.
static constexpr uint8_t RESULT_FILTER_MAINTENANCE_NEEDED = 0x11
 Filter needs maintenance.
static constexpr uint8_t RESULT_BATTERY_LEVEL = 0x12
 Battery level is low.
static constexpr uint8_t RESULT_TARGET_MODIFIED = 0x13
 Node modified the requested target value.
static constexpr uint8_t RESULT_MODE_NOT_IMPLEMENTED = 0x14
 Mode is not supported by the node.
static constexpr uint8_t RESULT_COMMAND_INCOMPATIBLE_TO_MOVEMENT = 0x15
 Command cannot move the node that way.
static constexpr uint8_t RESULT_USER_ACTION = 0x16
 User action overrode the command.
static constexpr uint8_t RESULT_DEAD_BOLT_ERROR = 0x17
 Dead bolt error.
static constexpr uint8_t RESULT_WRONG_LOAD_CONNECTED = 0x19
 Wrong load connected to node.
static constexpr uint8_t RESULT_COLOUR_NOT_REACHABLE = 0x1A
 Requested colour not reachable.
static constexpr uint8_t RESULT_TARGET_NOT_REACHABLE = 0x1B
 Requested target not reachable.
static constexpr uint8_t RESULT_BAD_INDEX_RECEIVED = 0x1C
 Invalid index received.
static constexpr uint8_t RESULT_COMMAND_OVERRULED = 0x1D
 Command was overruled by a newer command.
static constexpr uint8_t RESULT_NODE_WAITING_FOR_POWER = 0x1E
 Node is waiting for power.
static constexpr uint8_t RESULT_WRONG_POSITION = 0x21
 Node reports wrong position.
static constexpr uint8_t RESULT_LIMITS_NOT_SET = 0x22
 Device limits are not set.
static constexpr uint8_t RESULT_IP_NOT_SET = 0x23
 Intermediate position is not set.
static constexpr uint8_t RESULT_OUT_OF_RANGE = 0x24
 Requested value is out of range.
static constexpr uint8_t RESULT_COMMAND_UNKNOWN = 0x08
 Code 1: the device does not offer this command in its current state or class.
static constexpr uint8_t RESULT_MODE_OR_STATE_UNKNOWN = 0x10
 Code 2: mode or state number unknown.
static constexpr uint8_t RESULT_BAD_MAC = 0x18
 Code 3: challenge answer (MAC) did not verify.
static constexpr uint8_t RESULT_SETUP_REQUIRED = 0x20
 Code 4: the device needs setting up first.
static constexpr uint8_t RESULT_PARAMETER_UNKNOWN = 0x28
 Code 5: parameter, alias, access method or scenario unknown.
static constexpr uint8_t RESULT_ORIGINATOR_DISABLED = 0x30
 Code 6: command originator disabled.
static constexpr uint8_t RESULT_PRIORITY_LOCKED = 0x38
 Code 7: priority locked; the command's priority level is too low to run.
static constexpr uint8_t RESULT_PRIORITY_SERVICE_NOT_SUPPORTED = 0x40
 Code 8: priority service not supported.
static constexpr uint8_t RESULT_PARAMETER_LIMITATION_ADJUSTMENT = 0x48
 Code 9: parameter limitation adjustment.
static constexpr uint8_t RESULT_PARAMETER_INCOHERENCE_ADJUSTMENT
 Code 10: parameter incoherence adjustment.
static constexpr uint8_t RESULT_TOTAL_INCOHERENCE = 0x58
 Code 11: the request does not fit what the device implements.
static constexpr uint8_t RESULT_SERVICE_FULL = 0x60
 Code 12: service full.
static constexpr uint8_t RESULT_ALIAS_INCOHERENCE = 0x68
 Code 13: alias incoherence.
static constexpr uint8_t RESULT_SESSION_INTERRUPTED = 0x70
 Code 14: session interrupted.
static constexpr uint8_t RESULT_NOT_IMPLEMENTED = 0x78
 Code 15: not implemented.
static constexpr uint8_t RESULT_COMMAND_REJECTED = 0x80
 Code 16: command rejected.
static constexpr uint8_t RESULT_ALREADY_CONFIGURED = 0x88
 Code 17: already configured.
static constexpr uint8_t RESULT_INFORMATION_CODE = 0xDF
 Information-only code with unknown semantics.
static constexpr uint8_t RESULT_PARAMETER_LIMITED = 0xE0
 Parameter limited by an unknown device.
static constexpr uint8_t RESULT_LIMITATION_BY_LOCAL_USER = 0xE1
 Parameter limited by local button.
static constexpr uint8_t RESULT_LIMITATION_BY_USER = 0xE2
 Parameter limited by a remote control.
static constexpr uint8_t RESULT_LIMITATION_BY_RAIN = 0xE3
 Parameter limited by a rain sensor.
static constexpr uint8_t RESULT_LIMITATION_BY_TIMER = 0xE4
 Parameter limited by a timer.
static constexpr uint8_t RESULT_LIMITATION_BY_SCD = 0xE5
 Parameter limited by a security actuator.
static constexpr uint8_t RESULT_LIMITATION_BY_UPS = 0xE6
 Parameter limited by a power supply.
static constexpr uint8_t RESULT_LIMITATION_BY_UNKNOWN_DEVICE = 0xE7
 Parameter limited by an unknown device.
static constexpr uint8_t RESULT_LIMITATION_BY_SAAC = 0xEA
 Parameter limited by a standalone automatic controller.
static constexpr uint8_t RESULT_LIMITATION_BY_WIND = 0xEB
 Parameter limited by a wind sensor.
static constexpr uint8_t RESULT_LIMITATION_BY_MYSELF = 0xEC
 Parameter limited by the node itself.
static constexpr uint8_t RESULT_LIMITATION_BY_AUTOMATIC_CYCLE = 0xED
 Parameter limited by an automatic cycle.
static constexpr uint8_t RESULT_LIMITATION_BY_EMERGENCY = 0xEE
 Parameter limited by an emergency.
static constexpr uint8_t POS_STOP = 0xD2
 Position values in the IO protocol.
static constexpr uint8_t POS_UNKNOWN = 0xD4
 Wire value: position unknown / keep current.
static constexpr uint8_t POS_FAVORITE = 0xD8
 Wire value: move to favorite/"My" position.
static constexpr uint16_t MAIN_PARAMETER_KEEP = static_cast<uint16_t>(POS_UNKNOWN) << BITS_PER_BYTE
 16-bit main parameter D400 (POS_UNKNOWN in the high byte): keep the current value.
static constexpr uint8_t EXECUTE_MAIN_BYTE_OFFSET = 2
 Offset of the main position/command byte in a CMD_EXECUTE payload — after the originator (first byte) and the ACEI (second byte).
static constexpr uint8_t POS_SECURED_TARGET = 0xD1
 Wire value for the secured target position command.
static constexpr uint8_t POS_DEFAULT = 0xD3
 Wire value for the default position command.
static constexpr uint8_t POS_FORCE_OPEN = 0x64
 Ambiguous wire value used only for passive 1W-traffic intent decoding (decode_1w_main_intent() / oneway_intent_to_target() in proto_codecs.cpp).
static constexpr uint8_t POS_VENT_MODIFIER = 0x03
 Modifier byte for the ventilation command.
static constexpr uint8_t POS_PEDESTRIAN_MODIFIER = 0x07
 Modifier byte for the pedestrian-position command (POS_FAVORITE main byte, as for ventilation).
static constexpr uint8_t POS_PARTIAL_MODIFIER = 0x09
 Modifier byte for the partial-position command (POS_FAVORITE main byte, as for ventilation).
static constexpr uint8_t POSITION_WIRE_SCALE = 2
 Scale factor between a 0-100 percent position and its CMD_EXECUTE main-byte wire value.
static constexpr uint8_t POSITION_WIRE_MAX = 200
 Highest doubled-position wire value: 100% * POSITION_WIRE_SCALE.
static constexpr uint8_t STATUS_STOPPED = 0x01
 Status byte flags in CMD_PRIVATE_RESP and CMD_STATUS_UPDATE.
static constexpr uint8_t STATUS_EXPECTED = 0x80
 Byte 1 bit 7: device will send auto status update.
static constexpr uint8_t STATUS_BATTERY_MASK = 0x60
 Byte 1 bits 5-6: the device's coarse battery level (BatteryState).
static constexpr uint8_t STATUS_BATTERY_SHIFT = 5
 Right shift that brings STATUS_BATTERY_MASK down to 0-3.
static constexpr uint8_t STATUS_TILT_SELECTOR
 Extended status payload marker for tilt-capable devices: FPI1 with bit 5 set = functional parameter FP3 (FPI1 bit 7 = FP1 ... bit 0 = FP8).
static constexpr uint8_t PRIVATE_GET_POSITION_STATUS = 0x03
 CMD_PRIVATE (0x03) function ID for a position-status request — data[0] of the payload.
static constexpr uint8_t TRANSFER_KEY [AES_KEY_SIZE]
 The transfer key is a hardcoded key used ONLY during pairing to obfuscate the system key during over-the-air transfer.
static constexpr uint16_t CRC_POLYNOMIAL_REVERSED = 0x8408
 Reversed CRC-CCITT polynomial used by IO-homecontrol.
static constexpr uint16_t CRC_LSB_MASK = 0x0001
 Least-significant-bit mask for reflected CRC update.
static constexpr uint8_t BROADCAST_DISCOVER [NODE_ID_SIZE] = {0x00, 0x00, 0x3B}
 Broadcast address for device discovery (0x00003B).
static constexpr uint8_t BROADCAST_DISCOVER_ALT [NODE_ID_SIZE] = {0x00, 0x00, 0x3F}
 Alternate discovery / 1W broadcast address (0x00003F).
static constexpr uint8_t MANUFACTURER_ID_MAX = 12
 Maximum manufacturer ID with a known name in the lookup table.
static constexpr uint8_t ONEWAY_EXECUTE_ACEI
 ACEI byte for a 1W CMD_EXECUTE frame — the Somfy-shaped default.
static constexpr uint8_t ONEWAY_EXECUTE_ACEI_VELUX = (ACEI_LEVEL_USER_DEFAULT << ACEI_LEVEL_SHIFT) | ACEI_VALID_BIT
 ACEI byte for a 1W CMD_EXECUTE frame from a VELUX KLI-class remote.
static constexpr uint32_t ATT_CLASS_5MS_TURNAROUND_MS = 5
 Turnaround time of ATT_CLASS_5MS, the shortest class; att_class_ms() doubles it per class.
static constexpr uint8_t KEY_EXTRACTION_DISCOVER_RESP_FLAGS = 0xDD
 Default Multi Information Byte for our device-role CMD_DISCOVER_RESP (0x29): the turnaround-time class and power-save mode the key-extraction responder advertises to a foreign hub.
static constexpr uint16_t KEY_EXTRACTION_DISCOVER_RESP_TYPE_WORD = 0x0080
 Default packed device type word (type in bits [15:6], subtype in bits [5:0]) the key-extraction responder advertises in its 0x29 to an open discovery: 0x0080, roller shutter subtype 0, the same word a real Somfy RS100 io sends (tests/corpus/captures/pairing/somfy_rs100_pairing_key_transfer_timeout.yaml).
static constexpr uint16_t STATUS_POS_MAX = 0xC800
 In status responses, position is encoded as a 16-bit value where 0x0000 = fully open (0%) and 0xC800 = fully closed (100%).
static constexpr uint16_t STATUS_POS_TOLERANCE_RAW = 100
 Target-reached tolerance expressed in raw IO-homecontrol position units.
static constexpr uint8_t DEVICE_METADATA_SIZE = 2
 Packed device metadata uses two bytes where the high 8 bits carry the upper type bits and the low byte carries both the remaining type bits and the 6-bit manufacturer subtype.
static constexpr uint8_t DEVICE_TYPE_LOW_BITS_SHIFT = 2
static constexpr uint8_t DEVICE_TYPE_HIGH_BITS_SHIFT = 6
static constexpr uint16_t DEVICE_TYPE_WIRE_MASK = 0x3FF
 The node-type wire field is ten bits wide.
static constexpr uint8_t DEVICE_SUBTYPE_MASK = 0x3F
static constexpr float UNKNOWN_POSITION = 212.0F
 Sentinel value meaning "position is not known yet".
static constexpr uint8_t DEVICE_NAME_BUFFER_SIZE = 32
 Device name storage including null terminator.
static constexpr int16_t RSSI_UNKNOWN_DBM = INT16_MIN
 Sentinel value meaning "no RSSI sample recorded yet" for last_rssi_dbm/rssi_ema_scaled.
static constexpr int16_t RSSI_EMA_SCALE = 8
 EMA weight denominator and fixed-point scale for IoDevice::rssi_ema_scaled.
static constexpr uint8_t SUBTYPE_MAIN_PARAMETER_ONLY = 58
 Subtype value that marks a device taking the main parameter only, on any node type.
static constexpr uint8_t SUBTYPE_HEATER_WITH_SETPOINT = 1
 Subtype of the electrical heater (node type 52) with an adjustable setpoint, the one driven over the private register protocol (proto_heating.h).
static constexpr uint16_t MAIN_PARAMETER_OPEN = 0x0000
 Main parameter that opens (or switches off) a device: percent 0.
static constexpr uint16_t MAIN_PARAMETER_CLOSED = STATUS_POS_MAX
 Main parameter that closes (or switches on) a device: 100 percent, the top of the position scale.
static constexpr uint16_t MAIN_PARAMETER_STOP = main_parameter_of(POS_STOP)
 Main parameter that stops a movement.
static constexpr uint16_t MAIN_PARAMETER_INVALID = 0xFFFF
 What position_main_parameter() returns for a value that is no position; no device accepts it.
constexpr NamedPosition NAMED_POSITIONS []
 The named positions.
static constexpr uint8_t DHW_REGISTER_HIGH_BYTE = 0x00
 High byte of every register of a domestic hot water heater (family 0x00).
static constexpr uint8_t DHW_REG_BOOST_DURATION = 0x00
 Register 0x0000: boost duration, one byte.
static constexpr uint8_t DHW_REG_AWAY_DURATION = 0x05
 Register 0x0005: away (holiday) duration, one byte: the days the holiday still runs.
static constexpr uint8_t DHW_BOOST_START_DURATION = 1
 Duration a boost start writes to register 0x0000: the value read during a boost started on the display, the only non-zero value seen.
static constexpr uint8_t DHW_AWAY_UNTIL_TURNED_OFF = 255
 Duration an away start writes to register 0x0005: 255, which the heater itself holds for a holiday with no end date (issue #198), as Home Assistant's away mode (no end date) means.
static constexpr uint8_t DHW_REG_ENERGY = 0x04
 Register 0x0004: energy consumed since the heater was installed, 32-bit big-endian, Wh.
static constexpr uint8_t DHW_REG_MODE = 0x06
 Register 0x0006: operating mode (DhwMode), one byte. Followed the display in every run.
static constexpr uint8_t DHW_REG_BRAND = 0x07
 Register 0x0007: one ASCII letter naming the brand (41 = 'A' on the Calypso).
static constexpr uint8_t DHW_REG_WATER_TEMPERATURE = 0x17
 Register 0x0017: water temperature, signed 16-bit big-endian tenths of a degree in the first two value bytes.
static constexpr uint8_t DHW_REG_HEATING = 0x21
 Register 0x0021: what is heating, eight bytes (DhwHeatingState).
static constexpr float DHW_TARGET_MIN_C = 20.0F
 Lower end of the target-temperature scale: main parameter 0 is 20 C.
static constexpr float DHW_TARGET_MAX_C = 62.0F
 Upper end of the target-temperature scale: main parameter 0xC800 is 62 C, the highest target the Calypso's display offers.
static constexpr uint8_t DHW_STATUS_MAIN_PARAMETER_OFFSET = 2
 Offset of the main parameter (MSB) in the data of a status reply (CMD_PRIVATE_RESP): the target slot of a cover's reply.
static constexpr uint16_t DHW_OPERATING_MODE_BOOST_ON = 0x4900
 FP16 that starts a boost and ends away. Bit 14 is set as the heater reports it.
static constexpr uint16_t DHW_OPERATING_MODE_OFF = 0x4A00
 FP16 that ends a boost and ends away: both fields off.
static constexpr uint16_t DHW_OPERATING_MODE_AWAY_ON = 0x4600
 FP16 that starts away (holiday) and ends a boost.
static constexpr uint8_t DHW_STATUS_FPI1 = 0x00
 Status-read FPI1 that asks for no FP1-FP8 (the heater holds none of them).
static constexpr uint8_t DHW_FP_CAPABILITIES = 15
 Functional parameter that carries the operating-mode capabilities (DHW_CAPABILITY_* bits).
static constexpr uint8_t DHW_FP_OPERATING_MODE = 16
 Functional parameter that carries the operating-mode word (DHW_OPERATING_MODE_*).
static constexpr uint8_t DHW_FP_ALSO_ASKED = 10
 Functional parameter 10: no decision uses it; the status read asks for it so the read is exactly the one the heater has been seen answering (FPI2 43, issue #198).
static constexpr uint8_t DHW_STATUS_FPI2
 Status-read FPI2 (43): FP10, FP15 and FP16.
static constexpr uint16_t DHW_CAPABILITY_AWAY = 1U << 2U
 FP15 bit 2: the heater supports away (absence).
static constexpr uint16_t DHW_CAPABILITY_BOOST = 1U << 3U
 FP15 bit 3: the heater supports boost (relaunch).
static constexpr uint8_t CTRL0_END = 0x80
 Control byte 0 (CTRL0) bit definitions.
static constexpr uint8_t CTRL0_START = 0x40
 Bit 6: first frame in exchange.
static constexpr uint8_t CTRL0_PROTOCOL_1W = 0x20
 Bit 5: 1=OneWay protocol, 0=TwoWay protocol.
static constexpr uint8_t CTRL0_LENGTH_MASK = 0x1F
 Bits [4:0]: frame length - 1.
static constexpr uint8_t CTRL1_VERSION_MASK = 0x03
 Ties FRAME_MAX_DECLARED_SIZE (proto_sizes.h) to the mask that actually defines it.
static constexpr uint8_t CTRL1_PRIORITY = 0x04
 Bit 2: high-priority frame.
static constexpr uint8_t CTRL1_ACK = 0x10
 Bit 4: sender can handle 2W responses (ACK-capable).
static constexpr uint8_t CTRL1_LOW_POWER = 0x20
 Bit 5: low-power device (e.g., solar-powered).
static constexpr uint8_t CTRL1_ROUTED = 0x40
 Bit 6: frame was relayed through a repeater.
static constexpr uint8_t CTRL1_BEACON = 0x80
 Bit 7: beacon announcement frame.
constexpr float HEATING_TEMP_MIN_C = 7.0F
 Lowest setpoint this codec will encode.
constexpr float HEATING_TEMP_MAX_C = 28.0F
 Highest setpoint this codec will encode.
constexpr size_t HEATING_PAYLOAD_MAX_SIZE = 6
 Largest payload any function produces: a set of a 16-bit value (4-byte header + 2 value bytes).
constexpr uint8_t HEATING_PAYLOAD_PREFIX = 0x0C
 Leading payload byte, common to every function.
constexpr uint8_t HEATING_REGISTER_HIGH_BYTE = 0x01
 Payload byte at index 2: the high byte of the 16-bit register number, constant 0x01.
constexpr uint8_t HEATING_DIRECTION_GET = 0x60
 Payload byte at index 1 of a read ("get") request.
constexpr uint8_t HEATING_DIRECTION_SET = 0x61
 Payload byte at index 1 of a write ("set") request.
constexpr int16_t HEATING_SENSOR_FAULT_RAW = -1
 Measured value in a reading when the heater reports a sensor fault (raw -1).
static constexpr float HEATING_TEMPERATURE_RANGE_MIN_C = -40.0F
 Default encoding range of a temperature main parameter, in degrees Celsius.
static constexpr float HEATING_TEMPERATURE_RANGE_MAX_C = 80.0F
 See HEATING_TEMPERATURE_RANGE_MIN_C.
constexpr uint8_t HEATING_NIGHT_LIGHT_BRIGHTNESS_MAX = 100
 Highest night light brightness (HeatingFunction::NIGHT_LIGHT_BRIGHTNESS): a percentage, so this value is full brightness.
constexpr size_t HEATING_REPLY_VALUE_OFFSET = 4
 Offset of the first value byte in a register reply: the four header bytes it echoes.
constexpr uint8_t HEATING_REGISTER_MODEL_NAME = 0x19
 Register 0x0119: the heater's model name, ASCII, not terminated ("ADELIS 2 DIGIT" on an Atlantic Adelis, corpus atlantic_adelis_probe_register_01_sweep).
static constexpr uint8_t HEATING_LEVEL_MP_PREFIX = 0xFC
 Main-parameter prefix byte of a heating level: the level occupies the low byte.
static constexpr uint8_t HEATING_ALIAS_MP_PREFIX = 0xD8
 Main-parameter prefix byte of a setpoint alias: the alias occupies the low byte.
static constexpr uint16_t HEATING_OFF_MP = MAIN_PARAMETER_KEEP
 Main parameter of a heat pump's off command: MAIN_PARAMETER_KEEP, which the command pairs with the off value of a functional parameter.
static constexpr uint16_t HEATING_MP_FROM_TARGET = 0xFFFF
 Sentinel for HeatingModeEntry::main_parameter: send the entity's target temperature, encoded over the device's range, as the main parameter.
static constexpr uint16_t HEATING_TEMPERATURE_MP_SPAN = 51200
 Number of main-parameter steps across the encoding range (the full position scale, 0xC800).
static constexpr uint8_t NODE_ID_SIZE = 3
 Device/node addresses are 3 bytes (e.g., "123ABC").
static constexpr uint8_t NODE_ID_STRING_SIZE = (NODE_ID_SIZE * 2) + 1
 Uppercase hex node ID plus null terminator.
static constexpr uint8_t HMAC_SIZE = 6
 Authentication HMAC is 6 bytes (truncated AES output).
static constexpr uint8_t AES_KEY_SIZE = 16
 AES-128 key size.
static constexpr uint8_t AES_BLOCK_SIZE = 16
 AES block size.
static constexpr uint8_t IV_SIZE = 16
 Initialization vector size for AES.
static constexpr uint8_t IV_PADDING = 0x55
 Padding byte used in IV construction.
static constexpr uint8_t BITS_PER_BYTE = 8
 Number of bits in one protocol byte.
static constexpr uint8_t FRAME_MIN_SIZE = 9
 Minimum frame: CTRL0+CTRL1+DST(3)+SRC(3)+CMD(1).
static constexpr uint8_t FRAME_MAX_DECLARED_SIZE = 32
 Largest frame length CTRL0's 5-bit length field (bits [4:0], length - 1) can express.
static constexpr uint8_t FRAME_MAX_SIZE = FRAME_MAX_DECLARED_SIZE
 Historical name for FRAME_MAX_DECLARED_SIZE, kept as an alias rather than a second literal so the two names can never disagree — most call sites (set_cmd, serialize, parse, every fixed-size frame buffer that only ever holds a declared-length frame) predate the trailer/wire distinction and still read most naturally as "the max frame size".
static constexpr uint8_t FRAME_MAX_DATA_SIZE = 23
 Maximum data bytes after command ID (declared length - header).
static constexpr uint8_t FRAME_CMD_OFFSET = 8
 Byte offset of the command ID in a raw wire buffer.
static constexpr uint8_t FRAME_CRC_SIZE = 2
 Size of the on-air CRC-CCITT trailer appended after every frame (declared bytes, plus the out-of-length MAC trailer when present).
static constexpr uint8_t FRAME_MAX_TRAILER_SIZE = HMAC_SIZE
 Largest out-of-length authenticator a frame can carry.
static constexpr uint8_t FRAME_MAX_WIRE_SIZE = FRAME_MAX_DECLARED_SIZE + FRAME_MAX_TRAILER_SIZE + FRAME_CRC_SIZE
 Largest number of bytes a buffer must hold to receive or transmit any frame this project knows about, trailer and CRC included: FRAME_MAX_DECLARED_SIZE (what CTRL0 can declare).
static constexpr uint32_t FREQ_CH1 = 868250000
 The protocol uses 3 frequency channels in the 868 MHz ISM band.
static constexpr uint32_t FREQ_CH2 = 868950000
 Channel 2: 868.95 MHz (1W and 2W, TX channel).
static constexpr uint32_t FREQ_CH3 = 869850000
 Channel 3: 869.85 MHz (2W only).
static constexpr uint32_t DISCOVERY_SLOT_MS = 41
 Length of one discovery answer slot.
static constexpr uint16_t LONG_PREAMBLE = 1024
 Preamble is a sequence of 0xAA bytes that precedes every frame.
static constexpr uint16_t SHORT_PREAMBLE = 8
 Protocol default and floor for continuation frames.
static constexpr uint16_t COLD_BROADCAST_REPLY_PREAMBLE = 80
 Default for TuningConfig::cold_broadcast_reply_preamble — preamble for a broadcast reply to a frame the peer caught via a rotating/hopping listen (currently: only the key-extraction responder's 0x29, the sole start=true frame any device-role builder in this codebase produces) — long enough to be reliably caught by a hopping receiver, short enough that sending it doesn't block the loop the way LONG_PREAMBLE does.
static constexpr uint16_t NORMAL_START_PREAMBLE = 32
 Default for TuningConfig::normal_start_preamble — the preamble in front of a directed start frame whose target is not a low-power / duty-cycled device (CTRL1_LOW_POWER clear).
static constexpr uint32_t PREAMBLE_LINGER_DWELL_MS = 15
 Preamble/sync linger extension for a rotating listen (ListenSpec::linger_dwell_ms): how much longer to stay on a channel once a frame is visibly incoming, so a hop doesn't cut it off mid-reception.
static constexpr uint32_t SESSION_HOLD_BASE_MS = 35
 Base of how long a node acting as a device holds the session's channel after answering a controller, before it scans all channels again.
static constexpr int32_t HOP_TIME_US = 2700
 Timing constants for frequency hopping and response waiting.
static constexpr int32_t RESPONSE_WAIT_MS = 500
 Wait for response to non-start frame.
static constexpr int32_t RESPONSE_START_WAIT_MS = 400
 Wait for a response to a start frame — the first frame of an exchange, and the one a sleeping device has just been woken by.
static constexpr int32_t RESPONSE_AUTH_WAIT_MS
 Wait for final response after challenge response.
static constexpr int32_t EXCHANGE_RETRY_DELAY_MS = 250
 Gap between retries within one HA command.
static constexpr uint8_t EXCHANGE_RETRY_COUNT = 3
 Attempts per exchange for every caller that has not asked for an attempt plan (decisions::attempt_plan()): a command that is not repeat-safe (decisions::is_repeat_safe_command()), heater registers, management actions and the pairing loops.
static constexpr uint8_t EXCHANGE_MAX_ATTEMPTS = 9
 Most attempts any exchange makes, whatever the budget allows: a plan with a long exchange_total_budget_ms still stops here, so a dead device is not hammered with 25 copies of a read.
static constexpr uint8_t LOW_POWER_SETTLING_ATTEMPTS = 3
 Attempts a settling read makes to a low-power receiver.
static constexpr uint16_t LOW_POWER_SHORT_LEAD_WINDOW_MS = 100
 How long the short lead attempt of a read to a low-power receiver in an unknown state listens after the end of its own transmission (decisions::WakeBelief::UNKNOWN, ADR 0079).
static constexpr uint16_t LOW_POWER_RESPONSE_WAIT_MS = 550
 How long a read listens for a low-power receiver's reply after the end of its own transmission, on every attempt.
static constexpr uint16_t EXCHANGE_EARLY_RESPONSE_WAIT_MS = 200
 How long an attempt listens for a mains device's reply when another attempt follows it, after the end of its own transmission.
static constexpr uint32_t EXCHANGE_ATTEMPT_OVERHEAD_MS = 10
 Time an attempt costs beyond its transmission and its listen: the channel-busy check, the frequency hop and the SPI traffic around each transmit.
static constexpr uint8_t UNCONFIRMED_EXECUTE_MAX_RESENDS = 1
 Re-sends allowed for a CMD_EXECUTE the target accepted without a closing reply, within the same exchange and its retry budget.
static constexpr uint8_t UNCONFIRMED_SUBSCRIBE_MAX_RESENDS = 1
 Re-sends allowed for a CMD_SUBSCRIBE_EVENT the target accepted without a closing reply.
static constexpr uint32_t UNCONFIRMED_EXECUTE_RESEND_DELAY_MS = 750
 Gap before that re-send, in place of the plan's retry gap (decisions::AttemptPlan::gap_ms).
static constexpr uint32_t LOW_POWER_MAX_TRAVEL_MS = 120000
 How long evidence that a low-power receiver is moving keeps it believed awake enough to hear the short start preamble first.
static constexpr uint8_t PAIRING_SET_CONFIG1_MAX_TRIES = 1
 Tries for pairing's phase-3 SetConfig1 (0x6F).
static constexpr uint8_t SCHEDULED_POLL_MAX_TRIES = 1
 Exchange tries for a status poll the scheduler owns that only looks at a device that is probably still busy: every poll of a tracked device that is not a settling read (decisions::scheduled_poll_read_kind()).
static constexpr uint8_t SCHEDULED_POLL_RETRY_GRACE_FIRST_FAILURE = 1
 Ladder positions at which a scheduler-owned status poll is a settling read again.
static constexpr uint8_t SCHEDULED_POLL_RETRY_GRACE_LAST_FAILURE = 3
static constexpr uint32_t REMOTE_ACTIVITY_STATUS_POLL_DELAY_MS = 2000
 Delay before polling after overheard remote traffic: another controller's frame to a registered device, and, by default, a linked-remote press (its remote_activity_poll_delay_ms tuning default; ADR 0080).
static constexpr uint16_t EXCHANGE_TOTAL_BUDGET_MS = 2500
 Wall-clock ceiling on one whole exchange, retries included.
static constexpr uint8_t ONEWAY_BURST_REPEATS = 4
 One-way (1W) transmit cadence.
static constexpr uint32_t ONEWAY_BURST_INTERVAL_MS = 40
 Gap between those copies.
static constexpr uint32_t RAIN_EVIDENCE_HOLD_MS = 2UL * 60UL * 60UL * 1000UL
 How long a status reply that named the rain sensor as the last commander stays usable as evidence that rain protection is active (see decisions::is_rain_limited(), rule b).
static constexpr int16_t LBT_RSSI_THRESHOLD_DBM = -90
 Listen-before-talk (LBT) parameters: conservative defaults that keep the hub from opening an exchange over traffic it can hear.
static constexpr uint8_t LBT_MAX_RETRIES = 5
 Max carrier-sense attempts before TX anyway.
static constexpr uint8_t LBT_RETRY_DELAY_MS = 5
 Backoff between LBT checks.
static constexpr uint16_t PAIRING_DISCOVERY_WAIT_MS = 2000
 Canonical defaults for the chip-neutral runtime-tunable pairing/discovery parameters.
static constexpr uint16_t PAIRING_DISCOVERY_INITIAL_DWELL_MS = 300
 Dwell on CH2 before discovery hopping begins.
static constexpr uint8_t PAIRING_KEY_EXCHANGE_RETRIES = 3
 Retries for the authenticated key-exchange phase.
static constexpr uint16_t PAIRING_KEY_INIT_DELAY_MS = 300
 Default for TuningConfig::pairing_key_init_delay_ms — pause after the discover-confirm step (whether it was ACKed, refused, or silent) and before CMD_KEY_INIT (0x31).
static const char *const TAG = "home_io_control.radio"
constexpr uint8_t RADIO_PACKET_BUFFER_SIZE
 Scratch buffer size for raw radio packets and recovered frames.
constexpr uint32_t RX_HOP_HOLDOFF_US = 12000
 Longest a frame arriving on the current channel may hold off an idle-path channel hop, in microseconds.
constexpr uint8_t TCXO_VOLTAGE_NONE = 0xFF
 Sentinel tcxo_voltage code (YAML none) for a board with a bare crystal and no TCXO.
static const char *const TAG = "home_io_control.lr1121"
static constexpr uint16_t LR1121_CMD_GET_STATUS = 0x0100
 cross-checked
static constexpr uint16_t LR1121_CMD_GET_VERSION = 0x0101
 cross-checked
static constexpr uint16_t LR1121_CMD_GET_ERRORS = 0x010D
 cross-checked (2-byte response)
static constexpr uint16_t LR1121_CMD_CLEAR_ERRORS
 hardware-verified (called on every init/RX cycle, never rejected — see log_command_status_())
static constexpr uint16_t LR1121_CMD_WRITE_BUFFER = 0x0109
 cross-checked
static constexpr uint16_t LR1121_CMD_READ_BUFFER = 0x010A
 cross-checked
static constexpr uint16_t LR1121_CMD_WRITE_REG_MEM_MASK32
 cross-checked (Semtech SWDR001 lr11xx_radio.c / RadioLib LR11x0_commands.h)
static constexpr uint16_t LR1121_CMD_CALIBRATE = 0x010F
 cross-checked
static constexpr uint16_t LR1121_CMD_CALIBRATE_IMAGE = 0x0111
 cross-checked (RadioLib calibrateImageRejection)
static constexpr uint16_t LR1121_CMD_SET_DIO_AS_RF_SWITCH = 0x0112
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_DIO_IRQ_PARAMS = 0x0113
 cross-checked
static constexpr uint16_t LR1121_CMD_CLEAR_IRQ = 0x0114
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_TCXO_MODE = 0x0117
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_STANDBY = 0x011C
 cross-checked
static constexpr uint16_t LR1121_CMD_GET_RX_BUFFER_STATUS = 0x0203
 cross-checked
static constexpr uint16_t LR1121_CMD_GET_PKT_STATUS
 cross-checked (Semtech SWDR001 lr11xx_radio.c :: lr11xx_radio_get_gfsk_pkt_status — 0 request params, 4-byte GFSK response: [rssi_sync, rssi_avg, rx_len, status_flags], rssi_sync/avg both decode via -(raw>>1) dBm, same formula as LR1121_CMD_GET_RSSI_INST)
static constexpr uint16_t LR1121_CMD_GET_RSSI_INST = 0x0205
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_GFSK_SYNC_WORD = 0x0206
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_RX = 0x0209
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_TX = 0x020A
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_RF_FREQUENCY = 0x020B
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_PACKET_TYPE = 0x020E
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_MODULATION_PARAMS = 0x020F
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_PACKET_PARAMS = 0x0210
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_TX_PARAMS = 0x0211
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_RX_TX_FALLBACK_MODE = 0x0213
 cross-checked
static constexpr uint16_t LR1121_CMD_SET_PA_CONFIG = 0x0215
 cross-checked
static constexpr uint32_t LR1121_IRQ_TX_DONE = 1UL << 2
static constexpr uint32_t LR1121_IRQ_RX_DONE = 1UL << 3
static constexpr uint32_t LR1121_IRQ_PREAMBLE_DETECTED = 1UL << 4
static constexpr uint32_t LR1121_IRQ_SYNC_WORD_VALID = 1UL << 5
static constexpr uint32_t LR1121_IRQ_CRC_ERR = 1UL << 7
static constexpr uint32_t LR1121_IRQ_TIMEOUT = 1UL << 10
static constexpr uint32_t LR1121_IRQ_ACTIVITY_MASK
 IRQ bits that represent a terminal radio event — a frame finished decoding, an outbound frame completed, or the chip gave up on its own.
static constexpr uint32_t LR1121_IRQ_DIO_ENABLE_MASK
 DIO-routed IRQ enable mask: TxDone|RxDone|PreambleDetected|SyncWordValid|Timeout.
static constexpr uint8_t LR1121_DEVICE_TYPE = 0x03
 cross-checked against RadioLib and the datasheet
static constexpr uint16_t LR1121_ERR_LF_RC_CALIB = 0x0001
 Low-frequency RC oscillator calibration failed.
static constexpr uint16_t LR1121_ERR_HF_RC_CALIB = 0x0002
 High-frequency RC oscillator calibration failed.
static constexpr uint16_t LR1121_ERR_ADC_CALIB = 0x0004
 ADC calibration failed.
static constexpr uint16_t LR1121_ERR_PLL_CALIB = 0x0008
 PLL calibration failed.
static constexpr uint16_t LR1121_ERR_IMG_CALIB = 0x0010
 Image calibration failed.
static constexpr uint16_t LR1121_ERR_HF_XOSC_START = 0x0020
 High-frequency crystal/TCXO failed to start.
static constexpr uint16_t LR1121_ERR_LF_XOSC_START = 0x0040
 Low-frequency crystal failed to start.
static constexpr uint16_t LR1121_ERR_PLL_LOCK = 0x0080
 PLL failed to lock.
static constexpr uint8_t LR1121_KNOWN_LATEST_FW_MAJOR = 0x01
 Newest LR1121 transceiver firmware known at the time this file was last updated, per the version-numbered filenames and CHANGELOG.md at https://github.com/Lora-net/radio_firmware_images/tree/master/lr1121/transceiver — the same two bytes GetVersion reports as fw_major/fw_minor (e.g.
static constexpr uint8_t LR1121_KNOWN_LATEST_FW_MINOR = 0x04
static constexpr uint8_t LR1121_PACKET_TYPE_GFSK = 0x01
 cross-checked against RadioLib
static constexpr uint8_t LR1121_GFSK_CRC_OFF = 0x01
 cross-checked (same encoding as SX126x)
static constexpr uint8_t LR1121_GFSK_PACKET_FIXED_LENGTH = 0x00
 cross-checked (same encoding as SX126x)
static constexpr uint8_t LR1121_PREAMBLE_DETECTOR_16_BIT = 0x05
 Preamble detector length selector: 16 bits.
static constexpr uint8_t LR1121_SYNC_WORD_PARAM_24_BITS = 0x18
 Sync word length: 24 bits, encoded as a literal bit count — cross-checked, identical encoding to SX1262's SX1262_SYNC_WORD_PARAM_24_BITS.
static constexpr uint8_t LR1121_CALIBRATE_ALL_BLOCKS = 0x3F
 Calibrate "all blocks" bitmask.
static constexpr uint8_t LR1121_FALLBACK_STDBY_XOSC = 0x02
 SetRxTxFallbackMode value for STDBY_XOSC.
static constexpr uint8_t LR1121_RX_BUFFER_BASE = 0x00
 Offset a reception is written to in the LR1121's data buffer.
static constexpr uint32_t LR1121_REG_HIGH_ACP_WORKAROUND_ADDR = 0x00F30054
 High-ACP (adjacent channel power) TX-quality erratum: clear bit 30 of this register before every SetRx/SetTx.
static constexpr uint32_t LR1121_REG_HIGH_ACP_WORKAROUND_MASK = 1UL << 30
static constexpr uint32_t LR1121_REG_HIGH_ACP_WORKAROUND_VALUE = 0x00000000
static constexpr uint32_t LR1121_REG_GFSK_WORKAROUND_1_ADDR = 0x00F20344
 GFSK modulation workaround register trio, standard (non-0.6/1.2kbps) values for our 38.4kbps config — applied after every modulation-params write, mirroring RadioLib's workaroundGFSK() ("always the first step, even when resetting" per its own comment on the first write).
static constexpr uint32_t LR1121_REG_GFSK_WORKAROUND_1_MASK = 0x00000030
static constexpr uint32_t LR1121_REG_GFSK_WORKAROUND_1_VALUE = 0x00000010
static constexpr uint32_t LR1121_REG_GFSK_WORKAROUND_2_ADDR = 0x00F20348
static constexpr uint32_t LR1121_REG_GFSK_WORKAROUND_2_MASK = 0x00000005
static constexpr uint32_t LR1121_REG_GFSK_WORKAROUND_2_VALUE = 0x00000001
static constexpr uint32_t LR1121_REG_GFSK_WORKAROUND_3_ADDR = 0x00F20244
static constexpr uint32_t LR1121_REG_GFSK_WORKAROUND_3_MASK = 0x0001FF03
static constexpr uint32_t LR1121_REG_GFSK_WORKAROUND_3_VALUE = 0x00000A01
static constexpr uint8_t LR1121_IMAGE_CAL_FREQ1 = 0xD7
 Banded image calibration for 868.25-869.85MHz +/-4MHz (~860-876MHz), matching RadioLib's setFrequency() margin.
static constexpr uint8_t LR1121_IMAGE_CAL_FREQ2 = 0xDB
static constexpr uint8_t LR1121_RFSWITCH_ENABLE_DIO5_DIO6 = 0x03
 DIO5 + DIO6 are switch pins.
static constexpr uint8_t LR1121_RFSWITCH_STANDBY = 0x00
 Both low.
static constexpr uint8_t LR1121_RFSWITCH_RX = 0x01
 DIO5 high.
static constexpr uint8_t LR1121_RFSWITCH_TX = 0x02
 DIO6 high (LP PA mode; unused).
static constexpr uint8_t LR1121_RFSWITCH_TX_HP = 0x02
 DIO6 high (HP PA, the only TX path used).
static constexpr uint8_t LR1121_RFSWITCH_TX_HF = 0x00
 2.4GHz path unused; both low.
static constexpr uint8_t LR1121_RFSWITCH_GNSS = 0x00
 Unused; both low.
static constexpr uint8_t LR1121_RFSWITCH_WIFI = 0x00
 Unused; both low.
static constexpr uint32_t LR1121_BUSY_TIMEOUT_MS = 3000
 How long SoftPhyDriverBase::wait_busy_() waits for BUSY to drop before declaring the chip failed.
static constexpr uint8_t LR1121_TCXO_STARTUP_DELAY_TICKS_MSB = 0x00
 LR1121 TCXO voltage on the T3-S3 board — 3.0V, confirmed on real hardware.
static constexpr uint8_t LR1121_TCXO_STARTUP_DELAY_TICKS_MID = 0x01
static constexpr uint8_t LR1121_TCXO_STARTUP_DELAY_TICKS_LSB = 0x40
 0x140 ticks at 30.52us/tick (32.768kHz RTC) is ~9.8ms (the SX1262's tick base differs, so the same tick count means a different time there) — harmless either way, since a longer startup is safe.
static constexpr uint8_t UART_CELL_BITS = 10
 Bits an on-air UART cell spends per protocol byte: start(1) + data(8) + stop(1).
static constexpr uint8_t UART_PROBE_MAX_BIT_OFFSET = 10
 Number of leading bit alignments the UART probe tries when locating a frame start.
static constexpr uint32_t SOFT_PHY_LINE_RATE_BPS = 38400
 Protocol line rate. The same 38400 bps every driver programs into its own bitrate register.
static constexpr uint32_t SOFT_PHY_US_PER_SECOND = 1000000
 Microseconds in a second, for the air-time arithmetic below.
static constexpr uint16_t IO868_SYNC_WORD_BYTES = 3
 Sync word bytes on air after the preamble (55 FF 33 on the SX1276, the UART-coded equivalent on the software PHY): three on every 868 MHz chip.
static constexpr uint32_t TX_DONE_GUARD_MS = 250
 Slack a driver allows past a transmission's own air time before it gives up waiting for the chip's TX-done interrupt.
static const char *const TAG = "home_io_control.soft_phy"
static constexpr uint8_t SOFT_PHY_RX_PROBE_PACKET_LEN = 48
 Fixed raw-RX probe length.
static constexpr uint32_t SOFT_PHY_ALL_IRQ_BITS = 0xFFFFFFFF
 Sentinel meaning "every IRQ bit counts as activity" — the default for SoftPhyDriverBase::activity_irq_mask.
static constexpr uint8_t SOFT_PHY_EARLY_HEADER_RAW_BYTES = 3
 Raw bytes read in the first stage of a length-driven receive — enough to hold CTRL0's UART cell (10 bits) at any of the probe's bit alignments (up to 9 bits of slack).
static constexpr uint8_t SOFT_PHY_EARLY_READ_MARGIN_BYTES = 2
 Air-time margin added before every mid-reception buffer read, in raw bytes.
static constexpr uint32_t SOFT_PHY_EARLY_POLL_US = 100
 Poll interval while waiting out a frame's remaining air time, in microseconds.
static constexpr uint32_t SOFT_PHY_EARLY_MIN_WINDOW_MS = 12
 Smallest receive window a length-driven receive will be attempted in, in milliseconds.
static constexpr uint32_t SOFT_PHY_IDLE_RX_COMPLETION_BUDGET_MS = 20
 Blocking budget SoftPhyDriverBase::check_for_packet gives a length-driven receive, in milliseconds — the timeout_ms passed to try_early_completion_() from the non-blocking idle-loop RX path (issue #81).
static constexpr uint32_t SOFT_PHY_CCA_SETTLE_US = 1000
 Wait after retuning to the TX channel before the clear-channel RSSI reading.
static constexpr uint32_t SOFT_PHY_PREAMBLE_HOLD_MS = 30
 Longest a detected preamble keeps the receiver running across dwell timeouts, in milliseconds.
static constexpr uint32_t SOFT_PHY_US_PER_MS = 1000
 Microseconds per millisecond, for the air-time arithmetic below.
static constexpr uint32_t SOFT_PHY_POLL_MARGIN_MS = 2
 Polling granularity added on top of an air time: the receive loops poll once per millisecond.
static constexpr uint32_t SOFT_PHY_POST_SYNC_GRACE_MS
 Extra time a reception gets once its sync word is seen, past the caller's deadline, in milliseconds: the fixed-length probe's own air time after sync (48 raw bytes, 10 ms) plus the polling margin.
static constexpr uint16_t SOFT_PHY_MAX_WIRE_FRAME_BYTES
 Largest real IO-Homecontrol frame the fixed-length RX probe has to recover intact: a 1W CMD 0x30 "add controller".
static constexpr uint16_t SOFT_PHY_MAX_WIRE_FRAME_RAW_BYTES
 Raw on-air bytes that frame occupies once its 2-byte CRC is appended and the whole thing is UART-packed into 10-bit cells (start + 8 data + stop), rounded up to whole bytes — the same arithmetic as soft_phy_raw_bytes_for_frame(), spelled out here because that helper is not constexpr.
static constexpr size_t DEVICE_ERROR_STR_SIZE = 160
 Buffer size that always fits format_device_error_bits()'s longest output for either chip (every named bit, |-joined, plus an UNKNOWN_0x%04X tail).
static const char *const TAG = "home_io_control.sx1262"
static const uint8_t SX1262_SYNC_WORD_PARAM_24_BITS = 0x18
static constexpr uint8_t SX1262_SET_STANDBY = 0x80
static constexpr uint8_t SX1262_SET_RX = 0x82
static constexpr uint8_t SX1262_SET_TX = 0x83
static constexpr uint8_t SX1262_SET_RF_FREQUENCY = 0x86
static constexpr uint8_t SX1262_SET_RX_TX_FALLBACK_MODE = 0x93
static constexpr uint8_t SX1262_WRITE_BUFFER = 0x0E
static constexpr uint8_t SX1262_READ_BUFFER = 0x1E
static constexpr uint8_t SX1262_SET_DIO_IRQ_PARAMS = 0x08
static constexpr uint8_t SX1262_GET_IRQ_STATUS = 0x12
static constexpr uint8_t SX1262_GET_PACKET_STATUS = 0x14
static constexpr uint8_t SX1262_GET_DEVICE_ERRORS = 0x17
static constexpr uint8_t SX1262_CLEAR_IRQ_STATUS = 0x02
static constexpr uint8_t SX1262_CLEAR_DEVICE_ERRORS = 0x07
static constexpr uint8_t SX1262_SET_PACKET_TYPE = 0x8A
static constexpr uint8_t SX1262_SET_MODULATION_PARAMS = 0x8B
static constexpr uint8_t SX1262_SET_PACKET_PARAMS = 0x8C
static constexpr uint8_t SX1262_SET_BUFFER_BASE_ADDRESS = 0x8F
static constexpr uint8_t SX1262_SET_PA_CONFIG = 0x95
static constexpr uint8_t SX1262_SET_TX_PARAMS = 0x8E
static constexpr uint8_t SX1262_SET_DIO2_AS_RF_SWITCH_CTRL = 0x9D
static constexpr uint8_t SX1262_SET_DIO3_AS_TCXO_CTRL = 0x97
static constexpr uint8_t SX1262_CALIBRATE = 0x89
static constexpr uint8_t SX1262_CALIBRATE_IMAGE = 0x98
static constexpr uint8_t SX1262_GET_RX_BUFFER_STATUS = 0x13
static constexpr uint8_t SX1262_GET_RSSI_INST = 0x15
static constexpr uint8_t SX1262_SET_REGULATOR_MODE = 0x96
static constexpr uint8_t SX1262_GET_STATUS = 0xC0
static constexpr uint8_t SX1262_WRITE_REGISTER = 0x0D
static constexpr uint8_t SX1262_READ_REGISTER = 0x1D
static constexpr uint16_t SX1262_IRQ_TX_DONE = 0x0001
static constexpr uint16_t SX1262_IRQ_RX_DONE = 0x0002
static constexpr uint16_t SX1262_IRQ_PREAMBLE_DETECTED = 0x0004
static constexpr uint16_t SX1262_IRQ_SYNC_WORD_VALID = 0x0008
static constexpr uint16_t SX1262_IRQ_CRC_ERR = 0x0040
static constexpr uint16_t SX1262_IRQ_ACTIVITY_MASK
 IRQ bits that represent a terminal radio event — a frame finished decoding, an outbound frame completed, or the chip reported a bad CRC.
static constexpr uint16_t SX1262_DEV_ERR_RC64K_CALIB = 0x0001
 64 kHz RC oscillator calibration failed.
static constexpr uint16_t SX1262_DEV_ERR_RC13M_CALIB = 0x0002
 13 MHz RC oscillator calibration failed.
static constexpr uint16_t SX1262_DEV_ERR_PLL_CALIB = 0x0004
 PLL calibration failed.
static constexpr uint16_t SX1262_DEV_ERR_ADC_CALIB = 0x0008
 ADC calibration failed.
static constexpr uint16_t SX1262_DEV_ERR_IMG_CALIB = 0x0010
 Image calibration failed.
static constexpr uint16_t SX1262_DEV_ERR_XOSC_START = 0x0020
 Crystal/TCXO failed to start.
static constexpr uint16_t SX1262_DEV_ERR_PLL_LOCK = 0x0040
 PLL failed to lock.
static constexpr uint16_t SX1262_DEV_ERR_PA_RAMP = 0x0100
 PA ramping failed (bit 0x0080 is unused).
static constexpr uint16_t SX1262_REG_SYNC_WORD = 0x06C0
static constexpr uint16_t SX1262_REG_RX_GAIN = 0x08AC
static constexpr uint16_t SX1262_REG_TX_CLAMP_CONFIG = 0x08D8
static constexpr uint16_t SX1262_REG_TX_MODULATION = 0x0889
 TX modulation-quality erratum register (SX1262 datasheet §15.1, "Modulation Quality with 500 kHz LoRa Bandwidth" — the title names LoRa, but the workaround table covers every modulation).
static constexpr uint8_t SX1262_TX_MODULATION_GFSK_BIT = 0x04
 Bit 2 of SX1262_REG_TX_MODULATION — the (G)FSK-correct value is 1.
static constexpr uint8_t SX1262_TX_BUFFER_BASE = 0x00
 Data-buffer split programmed by configure_buffer_base(): TX packets build from 0x00, RX packets land at 0x80.
static constexpr uint8_t SX1262_RX_BUFFER_BASE = 0x80
static constexpr uint8_t SX1262_GFSK_PACKET_TYPE_KNOWN_LENGTH = 0x00
static constexpr uint8_t SX1262_GFSK_CRC_OFF = 0x01
static constexpr uint8_t SX1262_PREAMBLE_DETECTOR_16_BIT = 0x05
 Preamble detector length selector: 16 bits (0x04 = 8, 0x05 = 16, 0x06 = 24, 0x07 = 32 bits).
static constexpr uint8_t SX1262_FALLBACK_STDBY_XOSC = 0x30
static constexpr uint32_t SX1262_BUSY_TIMEOUT_MS = 25
 Ceiling on how long SoftPhyDriverBase::wait_busy_() waits for BUSY to drop before declaring the chip failed.
static const char *const TAG = "home_io_control.sx1276"
static constexpr uint8_t SX1276_PA_SELECT_PA_BOOST = 0x80
 pa_pin value that routes the output through the PA_BOOST pin; any other value selects the RFO pin.
static constexpr uint32_t SX1276_CCA_SETTLE_US = 250
 Wait after retuning to the TX channel before the clear-channel RSSI reading.
static constexpr uint8_t REG_FIFO = 0x00
 FIFO read/write access.
static constexpr uint8_t REG_OP_MODE = 0x01
 Operating mode (sleep/standby/tx/rx).
static constexpr uint8_t REG_BITRATE_MSB = 0x02
 Bit rate MSB = FXOSC / bitrate.
static constexpr uint8_t REG_BITRATE_LSB = 0x03
static constexpr uint8_t REG_FDEV_MSB = 0x04
 Frequency deviation MSB.
static constexpr uint8_t REG_FDEV_LSB = 0x05
static constexpr uint8_t REG_FRF_MSB = 0x06
 Carrier frequency MSB (freq = FRF * FXOSC / 2^19).
static constexpr uint8_t REG_FRF_MID = 0x07
static constexpr uint8_t REG_FRF_LSB = 0x08
static constexpr uint8_t REG_PA_CONFIG = 0x09
 Power amplifier config (pin select + power level).
static constexpr uint8_t REG_PA_RAMP = 0x0A
 PA ramp time and modulation shaping.
static constexpr uint8_t REG_LNA = 0x0C
 Low noise amplifier gain and boost.
static constexpr uint8_t REG_RX_CONFIG = 0x0D
 Receiver configuration (AFC, AGC, trigger).
static constexpr uint8_t REG_RSSI_CONFIG = 0x0E
 RSSI smoothing.
static constexpr uint8_t REG_RX_BW = 0x12
 Receiver bandwidth.
static constexpr uint8_t REG_AFC_BW = 0x13
 AFC bandwidth.
static constexpr uint8_t REG_AFC_FEI = 0x1A
 AFC auto clear.
static constexpr uint8_t REG_PREAMBLE_DETECT = 0x1F
 Preamble detector config.
static constexpr uint8_t REG_OSC = 0x24
 Oscillator / clock output.
static constexpr uint8_t REG_RSSI_VALUE = 0x11
 Instant RSSI value in FSK mode.
static constexpr uint8_t REG_PREAMBLE_MSB = 0x25
 TX preamble length MSB.
static constexpr uint8_t REG_PREAMBLE_LSB = 0x26
static constexpr uint8_t REG_SYNC_CONFIG = 0x27
 Sync word config (size, polarity, enable).
static constexpr uint8_t REG_SYNC_VALUE1 = 0x28
 Sync word byte 1 (registers 0x28-0x2F for bytes 1-8).
static constexpr uint8_t REG_PACKET_CONFIG1 = 0x30
 Packet format, CRC, encoding.
static constexpr uint8_t REG_PACKET_CONFIG2 = 0x31
 Packet mode, IoHomeOn, PowerFrame.
static constexpr uint8_t REG_PAYLOAD_LENGTH = 0x32
 Max payload length.
static constexpr uint8_t REG_FIFO_THRESH = 0x35
 FIFO threshold for TX start condition.
static constexpr uint8_t REG_IRQ_FLAGS1 = 0x3E
 IRQ flags: mode ready, preamble detect, etc.
static constexpr uint8_t REG_IRQ_FLAGS2 = 0x3F
 IRQ flags: FIFO full/empty, payload ready, CRC ok.
static constexpr uint8_t REG_DIO_MAPPING1 = 0x40
 DIO0-DIO3 pin mapping.
static constexpr uint8_t REG_DIO_MAPPING2 = 0x41
 DIO4-DIO5 pin mapping.
static constexpr uint8_t REG_VERSION = 0x42
 Chip version (should read 0x12 for SX1276).
static constexpr uint8_t REG_PLLHOP = 0x44
 PLL hop: fast frequency change without standby.
static constexpr uint8_t REG_IMAGE_CAL = 0x3B
 Image calibration.
static constexpr uint8_t MODE_SLEEP = 0x00
static constexpr uint8_t MODE_STDBY = 0x01
static constexpr uint8_t MODE_TX = 0x03
static constexpr uint8_t MODE_RX = 0x05
static constexpr uint8_t MODE_MASK = 0x07
static constexpr uint8_t RX_CONFIG_VALUE = 0x9E
 REG_RX_CONFIG as configured: RestartRxOnCollision | AfcAutoOn | AgcAutoOn | RxTrigger=PreambleDetect.
static constexpr uint8_t RX_CONFIG_RESTART_RX_WITH_PLL_LOCK = 0x20
 REG_RX_CONFIG bit 5, RestartRxWithPllLock: restarts the receiver chain after a frequency change, which also clears PreambleDetect and re-arms AGC/AFC for the new channel.
static constexpr uint32_t SX1276_PREAMBLE_HOLD_US = 250000
 How long reception_in_progress() holds the idle hop on a channel once PreambleDetect is set: the longest start preamble (LONG_PREAMBLE, 1024 bytes = 213 ms at 38400 bps) plus margin, so the sync word at the end of a wake-up preamble arrives while the receiver is still there.
static constexpr uint32_t FXOSC = 32000000U
 SX1276 crystal oscillator frequency (32 MHz).
constexpr uint8_t SCAN_MAX_REPLIES = 24
 Maximum distinct responders reported by one scan_paired_devices() call.
constexpr uint32_t SCAN_CHANNELS [] = {FREQ_CH2, FREQ_CH1, FREQ_CH3}
 Channels scan_paired_devices() transmits its roll-call request on, one attempt per channel, per pass (see ROLL_CALL_PASSES).
constexpr uint8_t SCAN_CHANNEL_COUNT = sizeof(SCAN_CHANNELS) / sizeof(SCAN_CHANNELS[0])
constexpr RollCallPass ROLL_CALL_PASSES []
 The roll-call's two fixed frame shapes, low-power first.
static constexpr uint8_t MOVING_POLL_MISSES_SKIPPED = 2
 Consecutive failed polls of a moving device that are skipped rather than backed off.
static constexpr uint32_t MAX_TRACKED_STATUS_POLL_WINDOW_MS = 240000
 Hard stop for bounded follow-up polling after a command, remote activity or the first moving report, measured from the moment the window opens (ADR 0061, amended by ADR 0079).
static constexpr uint32_t INITIAL_STATUS_REQUEST_DELAY_MS = 5000
 Delay before the first post-boot status request from an entity.
static constexpr uint32_t HEATING_POLL_DEFAULT_INTERVAL_MS = 300000
 Interval between regular polls of a heater (its status_poll_interval overrides it).
static constexpr uint32_t HEATING_POST_WRITE_POLL_DELAY_MS = 45000
 Delay of the extra heater poll after a register write the heater acknowledged.
static constexpr uint32_t FOREIGN_READ_RESYNC_MS = 600000
 How long a device's authenticated status or this hub's own read-triggered poll keeps a further overheard read from triggering another poll (0 = never polled, so the first read polls).
static constexpr uint32_t FOREIGN_CHALLENGE_AFTER_REQUEST_MS = 100
 A device challenges a request 2.5-3.3 ms after it; 100 ms is far above that and far below the next request.
static constexpr uint32_t ONEWAY_QUIET_PERIOD_MS = 700
 Hold queued background polls back for this long after any 1W frame, so a poll the hub itself scheduled does not occupy the (half-duplex) radio while the remote is still transmitting.
static constexpr uint32_t ONEWAY_POLL_DEFER_CAP_MS = 5000
 Hard cap on how long sustained 1W traffic may hold a background poll back in total, measured from the start of the burst rather than the most recent frame.
static constexpr uint32_t DEFAULT_SETTLE_POLL_DELAY_MS = 3000
 Default follow-up settle-poll delay used while a device may still be moving, when no explicit poll interval is configured (interval_ms == 0).
static constexpr uint32_t STOP_SETTLE_POLL_CAP_MS = 1000
 Upper bound on the settle-poll delay after a STOP command.
static constexpr uint16_t SX1262_RESPONSE_PREAMBLE = 12
 SX1262-specific preamble for response/continuation frames within an exchange.
static constexpr uint16_t SX1262_POST_TX_SETTLE_US = 500
 SX1262-specific post-TX settling delay before re-entering RX.
static constexpr uint16_t SX1276_RESPONSE_PREAMBLE = 12
 SX1276 preamble for response/continuation frames within an exchange.
static constexpr uint16_t SX1276_DISCOVERY_HOP_SLICE_MS = 5
 Per-channel dwell while SX1276 pairing discovery hops across channels.
static constexpr uint16_t SX1262_DISCOVERY_HOP_SLICE_MS = 7
 Per-channel dwell while SX1262 pairing discovery hops across channels.
static constexpr uint16_t LR1121_RESPONSE_PREAMBLE = SX1262_RESPONSE_PREAMBLE
 LR1121-specific preamble for response/continuation frames within an exchange.
static constexpr uint16_t LR1121_POST_TX_SETTLE_US = SX1262_POST_TX_SETTLE_US
 LR1121-specific post-TX settling delay before re-entering RX.
static constexpr uint16_t LR1121_DISCOVERY_HOP_SLICE_MS = 7
 Per-channel dwell while LR1121 pairing discovery hops across channels.
static constexpr uint16_t SOFT_PHY_START_PREAMBLE = 48
 Default directed start preamble for drivers built on the shared software PHY (SX1262, LR1121).
static constexpr TuningNumberParam NUMBER_PARAMS []
static constexpr TuningSelectParam SELECT_PARAMS []
Pairing timing constants

Timeouts and retry limits for the pairing flow's blocking waits.

constexpr uint32_t PAIRING_DISCOVERY_RESPONSE_TIMEOUT_MS = 2000
 Discovery wait window after sending 0x28.
constexpr uint8_t PAIRING_DISCOVERY_MAX_ATTEMPTS = 3
 Retry discovery TX up to this many times.
constexpr uint32_t PAIRING_KEY_CHALLENGE_TIMEOUT_MS = 500
 Wait window for the device's 0x3C challenge.
constexpr uint32_t PAIRING_KEY_CONFIRM_TIMEOUT_MS = 500
 Wait for 0x33 key confirm after sending 0x32.
constexpr uint8_t PAIRING_DISCOVER_CONFIRM_TRIES = 3
 Max tries for the discover-confirm (0x2C) step.
constexpr uint32_t PAIRING_DISCOVER_CONFIRM_ACK_TIMEOUT_MS = 1500
 Wait window per discover-confirm try.
constexpr uint8_t PAIRING_KEY_VERIFY_MAX_TRIES = 2
 Tries for the authenticated name read that checks whether a device took our key although the key exchange did not confirm.
constexpr uint32_t PAIRING_KEY_PULL_REPLY_TIMEOUT_MS = PAIRING_KEY_CONFIRM_TIMEOUT_MS
 Wait for the device's key transfer (0x32) after our key transfer request (0x38).
constexpr uint32_t PAIRING_RECENT_ONE_WAY_SIGHTING_WINDOW_MS = 15000
 How recent a RecentOneWayPairingSighting has to be, relative to discover_and_pair() starting, to still count as evidence for this attempt.
static constexpr uint8_t ADDRESS_SUFFIX_MASK = 0x3F
 Well-known address suffix values in the broadcast address space.
static constexpr uint8_t ADDRESS_SUFFIX_BROADCAST = 0x3F
 Suffix for "all devices of this type" broadcast.
static constexpr uint8_t ADDRESS_SUFFIX_DISCOVERY = 0x3B
 Suffix for discovery-related broadcasts.
CMD_STATUS_UPDATE (0x71) payload header

A 0x71 payload is <flags> <status> <event hi> <event lo> <length> <event data...>.

Every captured position update carries EVENT_STATUS_CHANGE with 11 data bytes (the target/current/record offsets above sit inside them), and the one other report captured carries EVENT_LOCAL_SENSOR with 5 (tests/corpus/captures/probe/somfy_rs100_probe_subscribe_event_sx1262.yaml). The length byte matches the frame exactly in all of them.

constexpr uint8_t STATUS_UPDATE_EVENT_ID_OFFSET = 2
 Event id, big-endian, 2 bytes.
constexpr uint8_t STATUS_UPDATE_EVENT_LENGTH_OFFSET = 4
 Number of event-data bytes that follow.
constexpr uint8_t STATUS_UPDATE_EVENT_DATA_OFFSET = 5
 First event-data byte.
Objects the <tt>read_object</tt> probe can open (CMD_TRANSFER_OPEN)

Object class 0 ids.

Allowlisted: create_transfer_open() refuses every other id.

The actuator capacity record a Somfy controller reads after pairing (protocol version 4 or higher).

Todo
Byte layout unknown: it names the events the device can report, the sensor types it carries and its radio channels. Read to learn it.
static constexpr uint16_t TRANSFER_OBJECT_ACTUATOR_CAPACITY = 0x8100
static constexpr uint16_t TRANSFER_OBJECT_ACTUATOR_STATE = 0x8103
 The actuator state record read beside the capacity record.
static constexpr uint16_t TRANSFER_OBJECT_NAME = 0x8003
 A 16-byte name record: on a Somfy Izymo dimmer 00 followed by the ASCII text "MODULE DIMMER" and zero padding, read behind an authenticated block read.
static constexpr uint16_t TRANSFER_OBJECT_GENERAL_INFO1 = 0x8001
 Two further class-0 records beside the name record.
static constexpr uint16_t TRANSFER_OBJECT_GENERAL_INFO2 = 0x8002
Data transport payload layout (CMD_TRANSFER_OPEN .. CMD_UNKNOWN4A_RESP)
static constexpr uint8_t TRANSFER_OPEN_DATA_SIZE = 9
 CMD_TRANSFER_OPEN payload bytes.
static constexpr uint8_t TRANSFER_OBJECT_CLASS_DEVICE = 0
 Object class of the TRANSFER_OBJECT_* ids.
static constexpr uint8_t TRANSFER_OBJECT_CLASS_SOMFY = 2
 Object class of the Somfy manufacturer objects in TRANSFER_READABLE_OBJECTS.
static constexpr uint8_t TRANSFER_OPEN_RESP_DATA_SIZE = 4
 CMD_TRANSFER_OPEN_RESP payload bytes.
static constexpr uint8_t TRANSFER_READ_REQ_DATA_SIZE = 3
 CMD_UNKNOWN4A_REQ payload bytes: handle + chunk.
static constexpr uint8_t TRANSFER_BLOCK_SIZE = 18
 Data bytes per CMD_UNKNOWN4A_RESP; 18 fills a 32-byte frame.
static constexpr uint8_t TRANSFER_STATUS_ACCEPTED = 0
 CMD_TRANSFER_OPEN_RESP status: request accepted.
static constexpr uint8_t TRANSFER_STATUS_ADAPTED = 1
 CMD_TRANSFER_OPEN_RESP status: accepted with changes.
static constexpr uint8_t TRANSFER_STATUS_NOTHING_TO_READ = 7
 CMD_TRANSFER_OPEN_RESP status 7 with length 0: nothing to send for this object.
static constexpr uint16_t TRANSFER_CHUNK_CLOSE = 0
 Chunk number of the closing CMD_UNKNOWN4A_REQ.
Subscription action (first CMD_SUBSCRIBE_EVENT payload byte)
static constexpr uint8_t EVENT_SUBSCRIPTION_SUBSCRIBE = 0x01
 Ask the device to report the event.
static constexpr uint8_t EVENT_SUBSCRIPTION_UNSUBSCRIBE = 0x00
 Ask the device to stop reporting the event.
Subscribable device events (CMD_SUBSCRIBE_EVENT payload)

The only two event ids the subscribe_event probe can send; create_subscribe_event() refuses every other id.

A battery-powered device reports its charge level. An RS100 Solar io answered the subscription with this event and one data byte, 100, while its status byte read full (tests/corpus/captures/probe/somfy_rs100_probe_subscribe_event_battery_sx1262.yaml); see decode_battery_level_event().

Todo
Percent is a reading of that one value: no level below 100 has been captured.
static constexpr uint16_t EVENT_BATTERY_LEVEL_CHANGE = 0x2001
static constexpr uint16_t EVENT_LOCAL_SENSOR = 0x2005
 A device reports a sensor built into it (sun, window, ...).
Event data of the subscribable events

Layouts captured from one RS100 Solar io (issue #156); a report of any other shape is not decoded.

static constexpr uint8_t LOCAL_SENSOR_EVENT_DATA_LEN = 5
 EVENT_LOCAL_SENSOR data: 06 xx <hi> <lo> yy.
static constexpr uint8_t LOCAL_SENSOR_TYPE_SUN = 0x06
 First EVENT_LOCAL_SENSOR data byte on every capture.
static constexpr uint8_t LOCAL_SENSOR_VALUE_OFFSET = 2
 Offset of the big-endian value inside the event data.
static constexpr uint8_t BATTERY_LEVEL_EVENT_DATA_LEN = 1
 EVENT_BATTERY_LEVEL_CHANGE data: one level byte.
static constexpr uint8_t BATTERY_LEVEL_MAX_PERCENT = 100
 Highest level read as a percentage.
CMD_PRIVATE status read of the main parameter plus functional parameters (MP + FP1-FP7)

The payload create_status_mp_fp_read() sends is a replay of the status read a VELUX KLF 200 gateway sends to its devices (KLF 200 API specification v3.18 §10.3.1.4: status type 1 = current position, FPI1 selecting FP1-FP7).

The bytes are on-air evidence, not a decoded request: the reply layout is not interpreted by this codebase.

static constexpr uint8_t PRIVATE_STATUS_MP_FP = 0x01
 CMD_PRIVATE function ID at data[0].
static constexpr uint8_t STATUS_MP_FP_FPI1_MASK = 0xFE
 FPI1: FP1..FP7 requested (bit 7 = FP1 ... bit 1 = FP7).
static constexpr uint8_t STATUS_MP_FP_PARAM_TYPE = 0x01
 Status type per requested parameter: current value.
static constexpr uint8_t STATUS_MP_FP_PARAM_COUNT
 Largest selection seen on air (FP1-FP7), kept as a conservative cap across both bitmaps.
static constexpr uint8_t STATUS_MP_FP_FPI2
 FPI2: no FP9-FP16 requested (bit 7 = FP9 ... bit 0 = FP16 otherwise).
static constexpr uint8_t MANUFACTURER_VELUX = 1
 IO-Homecontrol manufacturer ID constants.
static constexpr uint8_t MANUFACTURER_SOMFY = 2
 Somfy (shutters, awnings, blinds).
static constexpr uint8_t MANUFACTURER_HONEYWELL = 3
 Honeywell.
static constexpr uint8_t MANUFACTURER_HORMANN = 4
 Hörmann (garage doors, gates).
static constexpr uint8_t MANUFACTURER_ASSA_ABLOY = 5
 ASSA ABLOY (locks, access).
static constexpr uint8_t MANUFACTURER_NIKO = 6
 Niko (switches, home automation).
static constexpr uint8_t MANUFACTURER_WINDOW_MASTER = 7
 WINDOW MASTER (ventilation).
static constexpr uint8_t MANUFACTURER_RENSON = 8
 Renson (ventilation, sun protection).
static constexpr uint8_t MANUFACTURER_CIAT = 9
 CIAT (HVAC).
static constexpr uint8_t MANUFACTURER_SECUYOU = 10
 Secuyou (security).
static constexpr uint8_t MANUFACTURER_OVERKIZ = 11
 OVERKIZ (Somfy connectivity platform).
static constexpr uint8_t MANUFACTURER_ATLANTIC_GROUP = 12
 Atlantic Group (heating, hot water).
static constexpr uint8_t ORIGINATOR_LOCAL_USER = 0x00
 Command originator codes indicating what or who triggered a command.
static constexpr uint8_t ORIGINATOR_USER_REMOTE = 0x01
 User sent command from a remote control.
static constexpr uint8_t ORIGINATOR_RAIN_SENSOR = 0x02
 Rain sensor triggered the movement.
static constexpr uint8_t ORIGINATOR_TIMER = 0x03
 Timer or schedule triggered the movement.
static constexpr uint8_t ORIGINATOR_SECURITY = 0x04
 Security controlling device (SCD) action.
static constexpr uint8_t ORIGINATOR_UPS = 0x05
 Uninterruptible power supply action.
static constexpr uint8_t ORIGINATOR_SMART_CONTROLLER = 0x06
 Smart function controller.
static constexpr uint8_t ORIGINATOR_LIFESTYLE = 0x07
 Lifestyle scenario controller.
static constexpr uint8_t ORIGINATOR_SAAC = 0x08
 Stand-alone automatic controller (SAAC).
static constexpr uint8_t ORIGINATOR_WIND_SENSOR = 0x09
 Wind sensor triggered the movement.
static constexpr uint8_t ORIGINATOR_LOAD_SHEDDING = 0x0B
 Load-shedding manager.
static constexpr uint8_t ORIGINATOR_LOCAL_LIGHT = 0x0C
 Local light sensor.
static constexpr uint8_t ORIGINATOR_ENVIRONMENT = 0x0D
 Unspecified environment sensor.
static constexpr uint8_t ORIGINATOR_MYSELF = 0x10
 Actuator decided to move by itself.
static constexpr uint8_t ORIGINATOR_AUTOMATIC_CYCLE = 0xFE
 Automatic cycle / external access.
static constexpr uint8_t ORIGINATOR_EMERGENCY = 0xFF
 Emergency command (never disabled).
static constexpr uint8_t ACEI_VALID_BIT = 0x01
 ACEI byte bit-field definitions.
static constexpr uint8_t ACEI_EXTENDED_MASK = 0x06
 Bits [2:1]: extended field.
static constexpr uint8_t ACEI_EXTENDED_SHIFT = 1
 Shift for extended field extraction.
static constexpr uint8_t ACEI_SERVICE_MASK = 0x18
 Bits [4:3]: service type.
static constexpr uint8_t ACEI_SERVICE_SHIFT = 3
 Shift for service field extraction.
static constexpr uint8_t ACEI_LEVEL_MASK = 0xE0
 Bits [7:5]: priority level (0–7).
static constexpr uint8_t ACEI_LEVEL_SHIFT = 5
 Shift for priority level extraction.
static constexpr uint8_t ACEI_LEVEL_PROTECTION_HUMAN = 0
 ACEI priority level values (0–7).
static constexpr uint8_t ACEI_LEVEL_PROTECTION_SENSOR = 1
 Goods/environment protection via sensors.
static constexpr uint8_t ACEI_LEVEL_USER_HIGH = 2
 High-priority user controller.
static constexpr uint8_t ACEI_LEVEL_USER_DEFAULT = 3
 Default remote controller priority.
static constexpr uint8_t ACEI_LEVEL_COMFORT_1 = 4
 Comfort automation level 1.
static constexpr uint8_t ACEI_LEVEL_COMFORT_2 = 5
 Comfort automation level 2.
static constexpr uint8_t ACEI_LEVEL_AUTO_SAAC = 6
 Stand-alone automatic controller.
static constexpr uint8_t ACEI_LEVEL_AUTO_DEFAULT = 7
 Default automatic level (lowest).
static constexpr uint8_t DISCOVERY_RESP_BACKBONE_OFFSET = 2
 Byte offsets within CMD_DISCOVER_RESP (0x29) payload data.
static constexpr uint8_t DISCOVERY_RESP_MANUFACTURER_OFFSET = 5
 Manufacturer ID at data[5].
static constexpr uint8_t DISCOVERY_RESP_FLAGS_OFFSET = 6
 Flags byte at data[6].
static constexpr uint8_t DISCOVERY_RESP_TIMESTAMP_OFFSET = 7
 Timestamp starts at data[7] (2 bytes).
static constexpr uint8_t DISCOVERY_RESP_FULL_SIZE = 9
 Full discovery response payload size.
static constexpr uint8_t DISCOVERY_FLAGS_ATT_MASK = 0xC0
 Bit masks and shifts for the Multi Information Byte fields.
static constexpr uint8_t DISCOVERY_FLAGS_ATT_SHIFT = 6
 Shift for ATT field extraction.
static constexpr uint8_t DISCOVERY_FLAGS_SYNC_CTRL_GRP = 0x20
 Bit 5: supports sync control group.
static constexpr uint8_t DISCOVERY_FLAGS_RF_SUPPORT
 Bit 3: 1 = the node has its own RF, 0 = it sits on a wired backbone only (VELUX KLF 200 API v3.18 §7.4.1.2.4-7, Table 52).
static constexpr uint8_t DISCOVERY_FLAGS_IO_MEMBERSHIP
 Bit 2: io-homecontrol membership; always 1 (same table).
static constexpr uint8_t DISCOVERY_FLAGS_POWER_SAVE_MASK = 0x03
 Bits [1:0]: power save mode.
static constexpr uint8_t ATT_CLASS_5MS = 0
 Actuator Turnaround Time (ATT) class values.
static constexpr uint8_t ATT_CLASS_10MS = 1
 Response within 10 ms.
static constexpr uint8_t ATT_CLASS_20MS = 2
 Response within 20 ms.
static constexpr uint8_t ATT_CLASS_40MS = 3
 Response within 40 ms.
static constexpr uint8_t POWER_SAVE_ALWAYS_ALIVE = 0
 Power save mode values from the Multi Information Byte.
static constexpr uint8_t POWER_SAVE_LOW_POWER = 1
 Device sleeps — needs long preamble to wake.
Background-poll retry delays after silent failures (no reply received)
static constexpr uint32_t STATUS_RETRY_AFTER_FAIL_MS = 5000
 First retry after a silent failure.
static constexpr uint32_t STATUS_RETRY_AFTER_FAIL_STEP2_MS = 15000
 Second retry after a silent failure.
static constexpr uint32_t STATUS_RETRY_AFTER_FAIL_STEP3_MS = 30000
 Third retry after a silent failure.
static constexpr uint32_t STATUS_RETRY_AFTER_FAIL_STEP4_MS = 60000
 Fourth retry after a silent failure.
static constexpr uint32_t STATUS_RETRY_AFTER_FAIL_MAX_MS = 300000
 Steady-state backoff after many silent failures.
Background-poll retry delays after auth-shaped failures (0x3C challenge seen)

A device that challenges the poll is awake and heard it.

A single such miss is usually a closing reply lost while the device switched its load — on a cover, the motor stopping at the end of its travel (issue #178) — so the first retry waits no longer than after a silent miss. Only a repeated one, which is what a wrong key looks like, backs off far: there every further try is a full authenticated exchange that cannot succeed.

static constexpr uint32_t STATUS_AUTH_RETRY_AFTER_FAIL_MS
 First retry after a challenge-seen failure.
static constexpr uint32_t STATUS_AUTH_RETRY_AFTER_FAIL_STEP2_MS = 30000
 Second retry.
static constexpr uint32_t STATUS_AUTH_RETRY_AFTER_FAIL_STEP3_MS = 120000
 Third retry.
static constexpr uint32_t STATUS_AUTH_RETRY_AFTER_FAIL_MAX_MS = 300000
 Steady-state after repeated auth failures.
Predicted-end read

A moving cover that reports how long it has left is read once more, with the settling plan, a second after that time has run out: the position it reports then is the final one.

That holds for every count, a short one or 0 included: the read at the end is the one most likely to meet the second in which the motor switches its load and a reply is lost. The ordinary progress reads keep running in between and stay single tries. The aim, the dropped progress read and the final-position read at count + 1 s are confirmed on a Somfy Sunea io awning (tests/corpus/captures/statuspoll/somfy_awning_statuspoll_close_with_remaining_time_predicted_end_read_sx1262.yaml).

Todo

Experimental: the stale-count rule has not met a frozen count on hardware, and a battery-powered cover's read at the end of its count has not run on one (ADR 0076).

The predicted-end read is aimed this long after the remaining time has run out. The motor takes up to about a second after its count reaches zero to stop and switch its load.

static constexpr uint32_t PREDICTED_END_MARGIN_MS = 1000
static constexpr uint32_t PREDICTED_END_DROPS_PROGRESS_WITHIN_MS = 1000
 A progress read due within this long before the predicted-end read is dropped: the settling read follows at once and a read of its own in front of it only fills the air.
static constexpr uint8_t PREDICTED_END_STALE_REPORTS = 3
 Reports in a row whose remaining time did not fall below the previous one before the count is taken as frozen and the predicted-end read is given up (progress reads only).
Frozen-count back-off

A count that has stopped falling is a cover that is waiting or stuck, not one that is nearly there, so the progress reads that follow back off instead of returning to the 3 s cadence (ADR 0079, amending ADR 0076).

The steps grow with each further report whose count did not fall and stop at the last; a falling count starts over. The back-off only begins after PREDICTED_END_STALE_REPORTS reports in a row that did not fall, and the slowest falling counts seen drop at every poll (3 s between two polls on a VELUX SML, 1 s per second on a Somfy awning), so a slow but alive count is not taken as frozen. 10 s, 15 s and 20 s keep a cover that starts after waiting within 20 s of being seen, while a stuck one costs a read per 20 s instead of one per 3 s.

Todo
Experimental: no frozen count has been captured on hardware yet.
static constexpr uint32_t FROZEN_COUNT_BACKOFF_FIRST_MS = 10000
 Delay after the first frozen report.
static constexpr uint32_t FROZEN_COUNT_BACKOFF_SECOND_MS = 15000
 Delay after the second.
static constexpr uint32_t FROZEN_COUNT_BACKOFF_MAX_MS = 20000
 Delay after the third and every later one.

Limitation read (CMD_LIMITATION_STATUS_REQ / CMD_LIMITATION_STATUS_RESP)

The layout is a hypothesis built from frames a VELUX KLF 200 was heard sending (issue #98) and the KLF 200 API specification v3.18, section 10.5 (get limitation: parameter id and limitation type, 0 = resulting minimum, 1 = resulting maximum).

Confirm it against a reply captured under an active limitation before building on it.

enum class  LimitationType : uint8_t { MINIMUM = 0x80 , MAXIMUM = 0xC0 }
 On-air selector byte (request data[0]) choosing which resulting limit to read. More...
static constexpr uint8_t LIMITATION_PARAM_MP = 0x00
 Parameter id of the main parameter.
static constexpr uint8_t LIMITATION_PARAM_FP_FIRST = 1
 Parameter id of FP1.
static constexpr uint8_t LIMITATION_PARAM_FP_LAST = 16
 Parameter id of FP16.
static constexpr uint8_t LIMITATION_REQ_DATA_SIZE = 3
 Request data: selector, parameter id, 0x00.
static constexpr uint8_t LIMITATION_RESP_DATA_SIZE = 5
 Reply data: parameter id, value (2), originator, time.
static constexpr uint8_t LIMITATION_TIME_MAX_COUNTED = 252
 Remaining-time codes of a limitation reply's last byte (KLF 200 API specification v3.18, table 200): codes 0-252 count (n + 1) * 30 seconds, 253 means no time limit, and 254/255 are the timer values the set command uses to delete an entry (never a duration).
static constexpr uint8_t LIMITATION_TIME_UNLIMITED = 253
 Limitation without a time limit.
static constexpr uint32_t LIMITATION_TIME_STEP_S = 30
 Seconds per time-code step.

Typedef Documentation

◆ BeginBlockingExcursionFn

Raises the hub's "operation took a long time" warning threshold for a blocking radio excursion — writes Component::warn_if_blocking_over_, which is protected on ESPHome's Component.

Wired to a lambda in the hub's initializer list, which has protected access.

Definition at line 39 of file hub_hooks.h.

◆ DeviceErrorFormatter

using esphome::home_io_control::DeviceErrorFormatter = void (*)(uint16_t errors, char *buf, size_t buf_size)

A chip's own decoder, as passed to SoftPhyDriverBase::dump_init_device_errors_().

Definition at line 201 of file radio_soft_phy_driver_base.h.

◆ DeviceFeatures

Capability bits of a device kind; see device_feature.

Definition at line 21 of file proto_device_features.h.

◆ DeviceUpdateCallback

using esphome::home_io_control::DeviceUpdateCallback = std::function<void(const std::string &device_id, const IoDevice &device)>

Callback type invoked when a device's state changes.

Definition at line 24 of file device_registry.h.

◆ NamedTimeoutFn

using esphome::home_io_control::NamedTimeoutFn = std::function<void(const char *name, uint32_t delay_ms, std::function<void()> callback)>

Schedules a named, replace-on-same-name timeout on the hub's ESPHome scheduler.

Parameters
nameTimer name; a later call with the same name replaces the pending callback.
delay_msDelay before the callback runs.
callbackWork to run in loop() context after the delay.

Definition at line 27 of file hub_hooks.h.

◆ OneWayCommandReportFn

using esphome::home_io_control::OneWayCommandReportFn = std::function<void(const OneWayCommandReport &report)>

Invoked once per attempted 1W command, successful or not.

Definition at line 54 of file oneway_transmitter.h.

◆ OneWayTransmitFn

using esphome::home_io_control::OneWayTransmitFn = std::function<bool(const IoFrame &frame, uint32_t freq, uint16_t preamble)>

How the transmitter puts a frame on air.

Injected rather than taken as a collaborator reference so this class depends on the ability to transmit rather than on whichever object currently owns the radio. The hub wires it to its own transmit_frame_; a test wires it to a recorder and needs no radio at all.

Parameters
frameFrame to serialize and transmit.
freqRF channel frequency in Hz.
preamblePreamble length in bytes.
Returns
true if the frame reached the radio.

Definition at line 36 of file oneway_transmitter.h.

◆ ReplyHandler

using esphome::home_io_control::ReplyHandler = std::function<ReplyDisposition(const IoFrame *parsed, const RadioRxPacket &packet)>

Invoked for every packet the radio delivers during a listen, before the listen decides whether to keep waiting.

Parameters
parsedPoints at the caller's own output frame, already filled by parse(); nullptr when parse() rejected the packet (the exchange waits log that case, the pairing waits do not).
packetThe raw packet, for length/frequency logging.
Returns
What the listen should do next — see ReplyDisposition.

Keep captures to a few pointers: small callables avoid std::function's heap fallback on the implementations this project builds against.

Definition at line 144 of file hub_exchange.h.

◆ ScanKeys

using esphome::home_io_control::ScanKeys = std::vector<std::array<uint8_t, AES_KEY_SIZE>>

The keys one scan sweeps with: slot 0 is the hub key, the rest are the distinct per-device keys.

Definition at line 708 of file management_actions.cpp.

◆ TransmitFrameFn

using esphome::home_io_control::TransmitFrameFn = std::function<bool(const IoFrame &frame, uint32_t freq_hz, uint16_t preamble)>

Puts a frame on air on a given channel via the hub's protected transmit_frame_().

Parameters
frameFrame to serialize and transmit.
freq_hzRF channel frequency in Hz.
preamblePreamble length in bytes.
Returns
true if the frame reached the radio.

Definition at line 34 of file hub_hooks.h.

Enumeration Type Documentation

◆ AddressClass

enum class esphome::home_io_control::AddressClass : uint8_t
strong

Address classification categories for diagnostic purposes.

IO-Homecontrol uses the target address field to encode both unicast device addresses and broadcast targets. The first byte being 0x00 indicates a broadcast; the remaining bytes encode the target device type.

Enumerator
UNICAST 

Normal device-to-device unicast address (first byte != 0x00).

BROADCAST_ALL 

Broadcast to all devices of a type (address suffix 0x3F).

BROADCAST_TYPE 

Broadcast to specific device type with non-standard suffix.

DISCOVERY 

Discovery-related broadcast (address suffix 0x3B).

UNKNOWN_BROADCAST 

Broadcast pattern that does not match known suffixes.

Definition at line 96 of file proto_codecs.h.

◆ BatteryState

enum class esphome::home_io_control::BatteryState : uint8_t
strong

A device's coarse battery level, as it reports it in byte 1 of its status payload.

Three levels, not a percentage. Only FULL_CHARGE has been seen on real hardware so far (every solar Somfy capture in the corpus, in daylight); LOW_CHARGE and NORMAL_CHARGE are the two values below it and have not been observed. Mains devices report NOT_REPORTED. The names avoid a bare LOW, which the Arduino core defines as a macro.

Enumerator
NOT_REPORTED 

The device does not report a battery (mains powered), or no status was read yet.

LOW_CHARGE 

Battery low.

NORMAL_CHARGE 

Battery neither low nor full.

FULL_CHARGE 

Battery full.

Definition at line 358 of file proto_device_model.h.

◆ BootloaderMismatch

enum class esphome::home_io_control::BootloaderMismatch : uint8_t
strong

Direction of a bootloader/target mismatch, for messaging.

lr1121_flash_decision()'s REJECT_BOOTLOADER_TOO_OLD verdict fires whenever lr1121_bootloader_supports_target() returns UNSUPPORTED, which covers both directions: the target needs a newer bootloader than this chip has (the only direction reachable before the bootloader-update feature existed), and the target needs an OLDER bootloader than this chip has – a downgrade, unreachable until a chip can actually be running 0x2101. Used for messaging only, in both the transceiver-only and bootloader-update builds – it never changes lr1121_flash_decision()'s own verdict.

Enumerator
NONE 

target_fw is unknown, or its required bootloader matches bootloader_version.

TARGET_NEEDS_NEWER 

The "too old" direction – what REJECT_BOOTLOADER_TOO_OLD has always meant until now.

TARGET_NEEDS_OLDER 

The "too new" direction – a downgrade, newly reachable once this feature ships.

Definition at line 155 of file lr1121_firmware_decisions.h.

◆ BootloaderSupport

enum class esphome::home_io_control::BootloaderSupport : uint8_t
strong

Whether a target firmware version is known to work with a given bootloader version.

Enumerator
SUPPORTED 

Both versions known, and the pairing is in LR1121_KNOWN_BOOTLOADER_REQUIREMENTS.

UNSUPPORTED 

target_fw is known, but not paired with this bootloader_version.

UNKNOWN_TARGET 

target_fw does not appear in LR1121_KNOWN_BOOTLOADER_REQUIREMENTS at all.

Definition at line 111 of file lr1121_firmware_decisions.h.

◆ BootloaderUpgradePath

Whether the three-stage bootloader-rewrite sequence (ADR 0021) is applicable, and if not, why.

Enumerator
NOT_APPLICABLE 

No block, or no upgrade needed/possible to evaluate. Keep the original verdict.

AVAILABLE 

Three-stage is possible. Still requires the arming switch to actually run.

BLOCKED_UNKNOWN_TARGET 

Block present, but this build cannot know what the target needs.

BLOCKED_BOOTLOADER_NEWER 

Target needs an OLDER bootloader. Not reachable today, likely never.

Definition at line 173 of file lr1121_firmware_decisions.h.

◆ CoverCommand

enum class esphome::home_io_control::CoverCommand : uint8_t
strong

Named device commands for cover-type actuators.

These represent the discrete non-positional actions a controller can send to a cover device. Each maps to a specific wire encoding in the CMD_EXECUTE payload. Using this enum avoids conflating numeric positions (0–100) with command codes.

Enumerator
STOP 

Stop movement immediately.

FAVORITE 

Move to stored favorite/"My" position.

VENT 

Move to ventilation position (window-type devices).

FORCE_OPEN 

Move to fully open at elevated priority; intended to bypass soft locks and environmental limits (confirmed on real hardware to move correctly; bypassing an active lock is still unconfirmed — see create_force_open() in proto_commands.cpp).

PARTIAL 

Move to the stored partial position (garage openers; issue #163).

PEDESTRIAN 

Move to the pedestrian position (gate openers); experimental.

Todo
Confirm on a real gate opener that 0xD807 opens it to a pedestrian position.

Definition at line 268 of file proto_device_model.h.

◆ DeviceCapabilityClass

High‑level capability class derived from DeviceType.

Enumerator
UNKNOWN 

Unknown capability.

COVER 

Position‑controlled cover (shutter/blind/awning).

LIGHT 

Binary on/off light.

SWITCH 

Binary on/off switch.

SENSOR 

Sensor device.

BEACON 

Beacon.

CLIMATE 

Climate device (heating/cooling).

LOCK 

Lock.

HEATER_LEVEL 

Heater with a heat level in the position field (exterior heating).

WATER_HEATER 

Domestic hot water heater (device_feature::WATER_HEATER).

Definition at line 89 of file proto_device_model.h.

◆ DeviceModel

enum class esphome::home_io_control::DeviceModel : uint8_t
strong

A product model below a (node type, subtype): for products that report the same wire type but need different handling.

The wire carries no model, so it comes from the YAML (io_model:) and is never learned. NONE is the (type, subtype) row itself, so every device declared without a model behaves as before. Each other value has a model row in the node-type table (proto_device_types.h) under the one (type, subtype) that offers it; the row's name is the YAML spelling. Values are stable: hub_validators.py DEVICE_MODEL_OPTIONS mirrors them (device_features_sync_test.cpp).

Enumerator
NONE 

No model: the (type, subtype) row.

TOWEL_DRYER 

Towel dryer under the electrical heater with an adjustable setpoint (52/1).

HEATER_WITH_LIGHT 

Heater with a night light under the electrical heater with an adjustable setpoint (52/1).

Definition at line 82 of file proto_device_model.h.

◆ DeviceNameValidationError

Validation result for outbound device-name writes.

Enumerator
NONE 

Name is valid and encodable.

EMPTY 

Name is empty after normalization.

TOO_LONG 

Name exceeds the 15-character write limit.

INVALID_UTF8 

Name contains malformed UTF-8 bytes.

UNSUPPORTED_CHAR 

Name contains characters outside Latin-1.

Definition at line 35 of file proto_codecs.h.

◆ DeviceType

enum class esphome::home_io_control::DeviceType : uint16_t
strong

IO-Homecontrol node types.

The numeric values follow the specification's 10-bit node-type field, so the enum is 16 bits wide: a remote controller (1023) and the test type (1008) do not fit in 8 bits. Do not reassign or reorder these. Names, subtype names and capabilities live in the table in proto_device_types.h.

Enumerator
UNKNOWN 

Unknown/unspecified device.

INTERIOR_VENETIAN_BLIND 

Interior venetian blind.

ROLLER_SHUTTER 

Roller shutter.

VERTICAL_EXTERIOR_AWNING 

Vertical exterior awning.

WINDOW_OPENER 

Window opening actuator.

GARAGE_OPENER 

Garage door opener.

LIGHT 

Light.

GATE_OPENER 

Gate opener.

ROLLING_DOOR_OPENER 

Rolling door opener.

LOCK 

Lock.

VERTICAL_INTERIOR_BLIND 

Vertical interior blind.

SECURE_CONFIGURATION_DEVICE 

Secure configuration device (a commissioning tool, not an actuator).

BEACON 

Beacon (unpaired/announcement).

Not YAML-selectable: a discovery pseudo-type, not a configurable actuator.

DUAL_ROLLER_SHUTTER 

Dual-section roller shutter.

HEATING_TEMPERATURE_INTERFACE 

Heating temperature interface.

ON_OFF_SWITCH 

Generic on/off switch.

HORIZONTAL_AWNING 

Horizontal awning (open/close inverted).

EXTERIOR_VENETIAN_BLIND 

Exterior venetian blind.

LOUVRE_BLIND 

Louvre blind.

CURTAIN_TRACK 

Curtain track.

VENTILATION_POINT 

Ventilation point.

EXTERIOR_HEATING 

Exterior heating.

HEAT_PUMP 

Heat pump.

INTRUSION_ALARM 

Intrusion alarm.

SWINGING_SHUTTER 

Swinging shutter.

SLIDING_WINDOW 

Sliding window.

ZONE_CONTROL 

Zone control.

BIOCLIMATIC_PERGOLA 

Bioclimatic pergola (louvre opening as the position).

SIREN 

Siren.

DOMESTIC_HOT_WATER 

Domestic hot water.

ELECTRICAL_HEATER 

Electrical heater.

HEAT_RECOVERY_VENTILATION 

Heat recovery ventilation.

CENTRAL_HOUSE_CONTROL 

Central house control.

TEST_AND_EVALUATION 

Test and evaluation equipment.

REMOTE_CONTROLLER 

Remote controller (all ten type bits set, packed bytes FF C0).

An Atlantic AGILIA IO radiator answers discovery with this type while in its display's pairing mode, and then refuses a pushed key with RESULT_COMMAND_UNKNOWN (tests/corpus/captures/pairing/ atlantic_agilia_pairing_key_transfer_command_unknown.yaml).

Definition at line 31 of file proto_device_model.h.

◆ DhwMode

enum class esphome::home_io_control::DhwMode : uint8_t
strong

Operating mode as register 0x0006 holds it.

Enumerator
AUTO 

The heater's automatic mode (its own schedule and target).

MANUAL_ECO_ON 

Manual mode with ECO on (heat pump only).

MANUAL_ECO_OFF 

Manual mode with ECO off (the electric element may help).

Definition at line 145 of file proto_dhw.h.

◆ DhwOperatingFeature

enum class esphome::home_io_control::DhwOperatingFeature : uint8_t
strong

The two features the operating-mode word switches; dhw_operating_feature_wire() holds their wire facts.

Enumerator
BOOST 

Relaunch.

AWAY 

Absence (holiday).

Definition at line 115 of file proto_dhw.h.

◆ DiscoverConfirmMode

enum class esphome::home_io_control::DiscoverConfirmMode : uint8_t
strong

Whether and how PairingEngine sends CMD_DISCOVER_CONFIRM (0x2C) to a freshly-discovered device before proceeding to the key exchange (0x31).

Every real controller in this project's corpus sends 0x2C between the device's 0x29 and its own 0x31. An enum, not a bool reusing pairing_discovery_ack_capable: that knob's contract is scoped to the discovery broadcast only (see its own doc), and skip/send/send_with_ack avoid on/off, which YAML 1.1 would otherwise turn into booleans. The step never fails a pairing attempt in any mode — see PairingEngine::run_discover_confirm_step_()'s doc.

Enumerator
SKIP 

Kill switch: sends no 0x2C and applies no post-step pause.

SEND 

Default.

Sends 0x2C with CTRL1_ACK clear either way — 0x00 to an always-alive target, 0x20 (CTRL1_LOW_POWER only) to a low-power one. It is CTRL1_LOW_POWER, not CTRL1_ACK, that follows the target's power class — see create_discover_confirm()'s ack param doc and PairingEngine::run_discover_confirm_step_() for where that split lives. Hardware-validated on a Somfy Izymo dimmer (always-alive): it answers the 0x00 shape with 0x2D within ~25 ms.

SEND_WITH_ACK 

Like SEND, but also sets CTRL1_ACK for an always-alive target (0x10) — the shape every corpus hub (VELUX KLR200/KIG300, Somfy Connectivity Kit) uses there.

A low-power target still gets 0x20, identical to SEND. Not the default: the Somfy Izymo dimmer never answers the 0x10 shape with 0x2D (pairing still completes, after the full no-answer wait). Kept for an always-alive device from a hub ecosystem that sends this shape and does not answer SEND's.

Definition at line 103 of file tuning_config.h.

◆ DiscoveryCommand

enum class esphome::home_io_control::DiscoveryCommand : uint8_t
strong

Discovery request command codes.

Enumerator
DISCOVER 

Standard broadcast discovery request (to 0x00003B).

DISCOVER_SPE 

SPE roll-call request (self-authenticating; see CMD_DISCOVER_SPE_REQ).

Deliberately not offered as a pairing_discovery_commands preset in tuning.py: only devices that already hold the system key answer it, so it can never help reach a device in learning mode. Retained here because the command byte is still needed to send a roll-call, and because discovery_command_from_string() stays permissive enough to parse it — do not remove either as dead code.

DISCOVER_ALT 

Alternate broadcast discovery (to 0x00003F), with optional payload byte.

Definition at line 158 of file tuning_config.h.

◆ DiscoveryListenChannels

Which channels PairingEngine's discovery wait listens on.

A broadcast's answers are not pinned to the channel the request went out on: every device that replies is continuing its own hopping rather than joining a pinned conversation (see ListenPolicy). Somfy always-alive roll-call replies land back on the request channel about 1 in 149 times, so skipping it buys a third more dwell on the two channels that carry almost all of them — hence the default. all exists because that measurement covers responders we have already heard from: a device that answers discovery only on the request channel is indistinguishable, from our side, from one that never answers at all.

Enumerator
SKIP_REQUEST 

Default. The two channels that are not the request channel.

ALL 

CH1->CH2->CH3, including the request channel.

Costs a third of the dwell on the other two; use it to rule out a responder that only answers on CH2.

Definition at line 130 of file tuning_config.h.

◆ EnrollGesture

enum class esphome::home_io_control::EnrollGesture : uint8_t
strong

Which 1W enrollment gesture a manufacturer's actuators expect.

SOMFY: one 0x30 add-controller burst to the identity's own io_device_type (the shape this project has hardware-validated). VELUX_KLI: a 0x39 clear to the all-devices address, then a 0x30 burst to each class in OneWayWireProfile::enrollment_classes, then a STOP+DOWN EXECUTE follow-up — the gesture a real KLI remote produces (issue #74 capture + the KLI manual), confirmed on a VELUX SML roller shutter and on KLI 312 interior blinds. See ADR 0032.

Enumerator
SOMFY 
VELUX_KLI 

Definition at line 162 of file oneway_controller.h.

◆ ExchangeOutcome

enum class esphome::home_io_control::ExchangeOutcome : uint8_t
strong

Authenticated exchange engine — outbound and inbound protocol flows.

All timing constants (retry count/delay, response windows) come from proto_timing.h; per-chip dwell overrides are queried from the RadioDriver.

What an outbound exchange actually achieved.

Deliberately not a bool: "the device accepted the command" and "the device told us what happened" are different facts, and some devices only ever deliver the first.

Some devices challenge a command, authenticate it, execute it, and then transmit nothing for several seconds — up to a dozen — reporting via an asynchronous status update later instead of closing the exchange with a synchronous reply, all well outside the exchange's own response window. Other devices on the same protocol close the exchange properly with a synchronous 0x04 (see tests/corpus/captures/exchange/somfy_awning_exchange_open_sx1276.yaml), so the four-frame exchange is real — just not universal, and a caller cannot assume either shape from the command alone.

SUCCESS_UNCONFIRMED exists so that silence after a real authentication is not treated the same as a request the device may never have heard at all: the two need different retry rules (see decisions::retry_after_unconfirmed_accept_is_safe()) and different reporting to the caller. Silence is not always harmless, though: a device that got the request may still have missed our challenge answer and not acted, which is why a CMD_EXECUTE to a device that normally confirms is sent once more before this outcome is returned.

Enumerator
FAILED 

No usable reply; the device may never have heard the request.

SUCCESS_WITH_RESPONSE 

Device replied; the caller's response frame is populated.

SUCCESS_UNCONFIRMED 

Device authenticated the request — so it received and accepted it — but sent no final response.

response is NOT populated. Callers that need payload (key exchange) must treat this as failure; callers that only need "the command landed" should treat it as success. For every command but CMD_EXECUTE, this outcome is only returned after the full retry budget is spent; a CMD_EXECUTE gets at most one re-send, and only to a device that normally confirms — see retry_after_unconfirmed_accept_is_safe().

Definition at line 62 of file exchange_engine.h.

◆ ExecutionState

enum class esphome::home_io_control::ExecutionState : uint8_t
strong

What a device says it is doing, from bits [2:0] of byte 0 of a status reply (CMD_PRIVATE_RESP, CMD_STATUS_UPDATE).

The numbering is the public KLF 200 API's node state (v3.18, Table 105 "State"). The byte packs the state under a result code, (code << 3) | state, the same layout as a CMD_ERROR_RESP result byte (error_resp_state()); every ordinary status reply in the corpus carries 4 while a cover travels and 5 once it is at rest.

Todo
Experimental: WAITING_FOR_POWER and WAITING_FOR_EXECUTING are not yet captured in an ordinary status reply, only WAITING_FOR_POWER in a main-parameter read of a shutter queued behind another on one supply. Following such a cover rests on that and on the API; it needs a captured ordinary status reply from a waiting cover.
Enumerator
NON_EXECUTING 

Unable to execute under the given conditions (example: temperature too high).

EXECUTION_ERROR 

An error occurred during the previous execution; cleared when the next starts.

WAITING_FOR_EXECUTING 

Accepted, waiting for its turn to run.

WAITING_FOR_POWER 

Accepted, waiting for power (queued behind another device on a shared supply).

EXECUTING 

Running.

DONE 

Not executing, no error, ready.

UNDEFINED 

Bits [2:0] hold 6 or 7, which no source defines.

Definition at line 332 of file proto_device_model.h.

◆ FemProfile

enum class esphome::home_io_control::FemProfile : uint8_t
strong

Which RF front-end module (if any) sits between the SX1262 and the antenna.

Selects two things: which of vfem_pin/fem_en_pin/fem_pa_pin the SX1262 driver's config-time validation requires present (see FEM_REQUIRED_PINS, components/home_io_control/hub_validators.py), and which level fem_pa_pin is driven to for the duration of a transmission (fem_tx_active_level_(), radio_sx1262.cpp). It never supplies a GPIO number – every board package spells its own pins out explicitly, the same as every other radio pin in this schema. GC1109 and KCT8103L share one control polarity and one code path with nothing distinguishing them but their TX-gain estimate; XY16P35's LNA-control pin is active-HIGH-on-*receive* (the inverse sense), the one place this enum's value actually changes what gets written to a pin, via a single polarity flip rather than a structural branch. NONE is the default: no FEM behaviour is applied, and a configured fem_pa_pin (if any) is simply strapped HIGH once at init and never touched again – the shape every raw-pin config on a non-FEM board already used before this enum existed.

Enumerator
NONE 

No FEM, or a board with the FEM pins wired but no behaviour profile set – see the NONE-specific note in fem_set_tx_mode_()'s doc comment.

GC1109 

Heltec WiFi LoRa 32 V4.2 – Geo-chip GC1109. Mode pin active-HIGH during TX.

KCT8103L 

Heltec WiFi LoRa 32 V4.3 / V4 R8 – Kangxi KCT8103L. Mode pin active-HIGH during TX.

XY16P35 

LilyGO T-Beam 1W SX1262 (868 MHz).

Names the RF module, not a bare FEM IC – no single chip inside it is independently identifiable as "the FEM chip" from anything published. Mode pin active-LOW during TX – the one profile with inverted polarity; see fem_tx_active_level_().

Definition at line 74 of file radio_interface.h.

◆ FlashDecision

enum class esphome::home_io_control::FlashDecision : uint8_t
strong

Outcome of lr1121_flash_decision().

Enumerator
PROCEED 

Safe to erase and write.

ALREADY_INSTALLED 

target_fw == installed_fw (both known) — the post-success state.

NEEDS_CONFIRMATION 

Not unsafe, but not an unambiguous "yes" either — needs a second press.

REJECT_WRONG_CHIP 

device_type or bootloader_version doesn't identify an LR1121.

REJECT_BOOTLOADER_TOO_OLD 

target_fw is known and positively incompatible with this bootloader.

Definition at line 235 of file lr1121_firmware_decisions.h.

◆ ForeignAction

enum class esphome::home_io_control::ForeignAction : uint8_t
strong

What the hub does about an overheard foreign frame.

Enumerator
IGNORE 

Nothing: no poll, no tracking change.

ARM 

Schedule a poll REMOTE_ACTIVITY_STATUS_POLL_DELAY_MS from now and begin tracking.

PUSH_PENDING 

A poll armed by foreign traffic is pending: move its timer out, change nothing else.

Definition at line 206 of file status_poll_policy.h.

◆ ForeignFrameKind

enum class esphome::home_io_control::ForeignFrameKind : uint8_t
strong

What an overheard frame of another controller is.

Enumerator
REQUEST_TO_DEVICE 

Another controller's command to a registered device.

DEVICE_CHALLENGE_TO_OTHER 

A registered device's challenge (0x3C) addressed to another controller.

Definition at line 200 of file status_poll_policy.h.

◆ FrameLogLevel

enum class esphome::home_io_control::FrameLogLevel : uint8_t
strong

Log level of one frame-log line, kept with the line while it is held.

Enumerator
WARN 
INFO 
DEBUG 

Definition at line 93 of file log_frame.h.

◆ HeatingActionSource

enum class esphome::home_io_control::HeatingActionSource : uint8_t
strong

Where a register heater's "heating" / "idle" comes from.

Enumerator
REGULATOR 

The regulator register (HeatingFunction::REGULATOR_STATE): heating at state 2.

WORKING_RATE 

The working rate (HeatingFunction::WORKING_RATE): heating above 0 %.

A towel dryer's regulator reads 0 during boost and drying, while the element runs at 100 %.

Definition at line 157 of file proto_heating_profiles.h.

◆ HeatingControlPath

enum class esphome::home_io_control::HeatingControlPath : uint8_t
strong

How a heater is commanded and read.

Enumerator
PRIVATE_REGISTERS 

CMD_WRITE_PRIVATE register reads and writes (proto_heating.h).

HEATING_LEVEL 

Standard EXECUTE with a heating level as the main parameter; read with 0x03.

TEMPERATURE_EXECUTE 

Standard EXECUTE with a temperature or an alias as the main parameter; read with 0x03.

Definition at line 34 of file proto_heating_profiles.h.

◆ HeatingFpKind

enum class esphome::home_io_control::HeatingFpKind : uint8_t
strong

Which functional parameter, if any, accompanies a TEMPERATURE_EXECUTE command.

Enumerator
NONE 

A main-parameter-only command.

FP9 

Functional parameter 9, one byte.

FP16 

Functional parameter 16, two bytes.

Definition at line 114 of file proto_heating_profiles.h.

◆ HeatingFunction

enum class esphome::home_io_control::HeatingFunction : uint8_t
strong

Registers and functions of a 2W heater, one row of the descriptor table each.

The SET_* names are the functions of the heating_control action; the same row is also the register's read path (reading SET_TEMPERATURE reads the comfort setpoint).

Enumerator
POWER_ON 

Register 0x012C, read: the paired-device list. Logged, never decoded.

SET_TEMPERATURE 

Register 0x0103, read/write: comfort setpoint, tenths of a degree.

SET_MODE 

Register 0x0100, read/write: operating mode (HeatingMode).

SET_PRESENCE 

Register 0x0110, write-only: someone is in the room (1) or not (0).

SET_OPEN_WINDOW_DETECTION 

Register 0x0106, read/write: open-window detection on (1) or off (0).

MIDNIGHT_SYNC 

Register 0x0130, read: the comfort/eco/auto setpoint block. Logged, never decoded.

ROOM_TEMPERATURE 

Register 0x0115, read-only: measured room temperature, signed hundredths of a degree.

REGULATOR_STATE 

Register 0x0117, read-only: 0 none, 1 standby, 2 heating.

MAX_SETPOINT 

Register 0x0102, read-only: highest setpoint the heater accepts, tenths of a degree.

WINDOW_OPEN 

Register 0x010E, read-only: the heater's window sensor, 0 closed / 1 open.

TOWEL_MODE 

Register 0x0136, read/write: a towel dryer's mode (TowelDryerMode).

TEMPORARY_STATE 

Register 0x013E, read/write: a towel dryer's temporary state (HeatingTemporaryState).

WORKING_RATE 

Register 0x0126, read-only: share of the time the element heats, 0-100 %.

TEMPERATURE_IN_FORCE 

Register 0x0137, read-only: the temperature a towel dryer holds right now, tenths of a degree.

Only a temperature while no boost or drying runs (it reads 1 and 2 there).

Todo
Confirm on a second towel dryer (the reads so far are from one unit).
ECO_SETBACK 

Register 0x0104, read-only: how far below the comfort setpoint a towel dryer's eco period heats, tenths of a kelvin.

NIGHT_LIGHT 

Register 0x0144, read/write: a heater's night light on (1) or off (0).

NIGHT_LIGHT_BRIGHTNESS 

Register 0x0142, read/write: the night light's brightness, 0-100 %, 16-bit.

Todo
Confirm on a real heater that the value is a percentage and that a write dims the light (issue #222).

Definition at line 48 of file proto_heating.h.

◆ HeatingHvacMode

enum class esphome::home_io_control::HeatingHvacMode : uint8_t
strong

HVAC modes a profile can offer.

Enumerator
OFF 

Heater off.

HEAT 

Heating on.

AUTO 

The device decides.

Definition at line 41 of file proto_heating_profiles.h.

◆ HeatingMode

enum class esphome::home_io_control::HeatingMode : uint8_t
strong

Operating modes of the mode register (HeatingFunction::SET_MODE).

Enumerator
AUTO 

Automatic mode; the device manages the setpoint itself.

MANUAL 

Manual mode; follows the last written setpoint.

PROG 

Program mode; the device runs its own stored weekly schedule.

EXTERNAL_SETPOINT 

Setpoint comes from an external controller.

Readable, never written by this component. Not named EXTERNAL: the Arduino core defines that as a macro.

OFF 

Off / standby.

Definition at line 74 of file proto_heating.h.

◆ HeatingPresetId

enum class esphome::home_io_control::HeatingPresetId : uint8_t
strong

Presets a profile can offer. CUSTOM entries carry their own name.

Enumerator
NONE 

No preset: selecting it ends the one that runs (a towel dryer's boost or drying).

AWAY 

Nobody is in the room (the frost-protection level on a level heater).

COMFORT 

Comfort level.

ECO 

Economy level.

BOOST 

Boost level.

CUSTOM 

Named by HeatingPresetEntry::custom_name.

Definition at line 48 of file proto_heating_profiles.h.

◆ HeatingRegulatorState

Regulator states of the regulator register (HeatingFunction::REGULATOR_STATE).

Enumerator
NONE 

No regulation active.

STANDBY 

Regulating, the element is idle.

HEATING 

The element is heating.

Definition at line 102 of file proto_heating.h.

◆ HeatingTemporaryState

Temporary states of a towel dryer (HeatingFunction::TEMPORARY_STATE): a timed run on top of the mode, which the mode register does not show.

Enumerator
NONE 

No temporary state: the mode applies.

BOOST 

Boost: full power for the boost duration.

DRYING 

Drying: full power for the drying duration.

Definition at line 95 of file proto_heating.h.

◆ KeyTransferMode

enum class esphome::home_io_control::KeyTransferMode : uint8_t
strong

Which direction PairingEngine moves the system key in after discovery.

auto (the default) picks the direction from the device's discovery answer: a device that answers as a remote controller (node type 1023) is pulled, any other device is pushed. There is no switch to the other direction inside one attempt: a refusal repeats, and a retry costs pairing time. push and pull force the direction.

push is the usual pairing: we send our key to the device (0x31 key init, the device's 0x3C, our 0x32, its 0x33). pull asks the device for its own key instead: we send a 0x38 key-transfer request with six random bytes, the device answers with a 0x32 holding its key, and we challenge it (0x3C) to prove it holds that key before trusting it. An Atlantic AGILIA IO radiator in its display's pairing mode answers a pushed key with RESULT_COMMAND_UNKNOWN (tests/corpus/captures/ pairing/atlantic_agilia_pairing_key_transfer_command_unknown.yaml); auto pulls such a device. A pulled key is carried by the devices that answer a roll-call under it, never adopted as the hub's: see PairingEngine::run_key_pull_phase_() and register_pulled_key_devices_().

Enumerator
AUTO 

Default. Pull from a device that answered discovery as a remote controller, push to any other.

PUSH 

Send our system key to the device.

PULL 

Ask the device for its key, then run a roll-call under it to find the devices that hold it.

Definition at line 151 of file tuning_config.h.

◆ LimitationType

enum class esphome::home_io_control::LimitationType : uint8_t
strong

On-air selector byte (request data[0]) choosing which resulting limit to read.

Exactly two values exist on purpose: the same opcode probably also carries the limitation write of section 10.5.2, so no other selector may be constructible. The minimum/maximum assignment rests on request order only (the minimum read always came first, with 0x80).

Enumerator
MINIMUM 

Resulting minimum limit; rain protection raises it (a floor on the position value).

MAXIMUM 

Resulting maximum limit.

Definition at line 649 of file proto_constants.h.

◆ ListenOutcome

enum class esphome::home_io_control::ListenOutcome : uint8_t
strong

How one call to ExchangeEngine::listen() ended.

Enumerator
ACCEPTED 

The handler returned ReplyDisposition::ACCEPT for some received frame.

ABORTED 

The handler returned ReplyDisposition::ABORT for some received frame.

TIMED_OUT 

spec.window_ms elapsed with no ACCEPT/ABORT.

Definition at line 127 of file hub_exchange.h.

◆ ListenPolicy

enum class esphome::home_io_control::ListenPolicy : uint8_t
strong

Which channels a listen covers.

A property of what is being waited for, not of the radio: a unicast exchange is a conversation pinned to the channel the request went out on, so its reply always lands there too and there is nothing to hop for. A broadcast has no single recipient, though — every device that answers is continuing its own independent channel-hopping rather than joining a pinned conversation, so the channel it happens to be on when it replies is largely decoupled from whichever channel carried the request. Whether "largely" still means "skip the request channel" depends on the replying population: Somfy always-alive roll-call replies land on the request channel about 1 in 149 times (skipping it costs almost nothing), while real VELUX low-power roll-call replies have been observed landing there. Shared by ExchangeEngine::listen() and, prospectively, any other caller that waits for a radio reply — hence living beside the primitive rather than folded into one loop's local logic.

Enumerator
HOLD_REQUEST_CHANNEL 

Never retunes, never slices. Unicast replies.

ROTATE_ALL_CHANNELS 

CH1->CH2->CH3->CH1.

Broadcast whose replies may use any channel, including the request channel (the low-power roll-call pass).

ROTATE_SKIPPING_REQUEST 

The two channels that are not the request channel.

Discovery and the always-alive roll-call pass.

Definition at line 111 of file hub_exchange.h.

◆ LR1121RxBandwidth

enum class esphome::home_io_control::LR1121RxBandwidth : uint8_t
strong

Valid LR1121 RX bandwidth options (register values).

Byte-for-byte identical to SX1262RxBandwidth — both chips use the same Semtech GFSK bandwidth grid — and kept as a distinct enum only so each driver's options can diverge if a future chip's table does. See Sx1262AndLr1121BandwidthTablesAgree, which pins the two tables together; if they ever need to differ for a real chip difference, say why here.

Enumerator
BW_39_0_KHZ 

39.0 kHz — narrowest; half the 76.8 kHz sizing floor, for probing only.

BW_46_9_KHZ 

46.9 kHz — narrow.

BW_58_6_KHZ 

58.6 kHz.

BW_78_2_KHZ 

78.2 kHz.

BW_117_3_KHZ 

117.3 kHz — default.

BW_156_2_KHZ 

156.2 kHz — wider tolerance for peer carrier offset.

BW_187_2_KHZ 

187.2 kHz — widest selectable option.

Definition at line 72 of file tuning_config.h.

◆ MovingReportAction

enum class esphome::home_io_control::MovingReportAction : uint8_t
strong

What the hub does with a status report that reads as moving.

Enumerator
FOLLOW 

A window is open: schedule the next poll as usual.

OPEN 

No window: one was opened now; schedule the next poll as usual.

END 

The window ran out: it is closed and reported ended; no further poll.

NONE 

No window and none may be opened: no poll.

Definition at line 226 of file status_poll_policy.h.

◆ OneWayAddControllerDecodeError

Decoding outcome for decode_1w_add_controller().

Enumerator
NONE 

Decode succeeded; out is populated.

NOT_ONEWAY 

CTRL0_PROTOCOL_1W is not set — not a 1W frame at all.

WRONG_COMMAND 

frame.cmd is not CMD_ONEWAY_ADD_CONTROLLER.

BAD_LENGTH 

Declared payload is not exactly the expected 20 bytes.

KEY_UNWRAP_FAILED 

crypto::crypt_1w_key() itself reported failure.

Definition at line 314 of file proto_codecs.h.

◆ OneWayMacStatus

enum class esphome::home_io_control::OneWayMacStatus : uint8_t
strong

Outcome of checking the out-of-length MAC trailer (IoFrame::has_mac) on a decoded CMD_ONEWAY_ADD_CONTROLLER frame.

Kept distinct from a bare bool because a caller-facing report needs to tell these three situations apart: no MAC was ever present to check (real remotes send the 29-byte form without a trailer, so this is a normal, non-error outcome — not every 0x30 on the wire carries one), a MAC was present and matched, or a MAC was present and did not match (evidence the decrypted key is wrong, or the frame was corrupted/forged).

Enumerator
NOT_PRESENT 

frame.has_mac was false; nothing to verify.

VERIFIED 

frame.has_mac was true and the MAC verified under the recovered key.

FAILED 

frame.has_mac was true and the MAC did NOT verify under the recovered key.

Definition at line 290 of file proto_codecs.h.

◆ OneWayPowerClass

enum class esphome::home_io_control::OneWayPowerClass : uint8_t
strong

Which preamble/CTRL1 shape a 1W identity's bursts actually go out with.

Every enumerator is a real on-air shape, so a value of this type is always transmittable — "not configured" is not one of them. That state lives in OneWayControllerIdentity::power_class_override, an empty optional, and is resolved to one of these by effective_power_class() before any of it reaches the transmitter. Keeping the two apart is what lets oneway_burst_copy_shape() switch exhaustively with no unreachable case.

See ADR 0038 for the shapes and ADR 0041 for how an unset key resolves.

Enumerator
LEGACY_LONG 

LONG_PREAMBLE on every copy, CTRL1 0x00 everywhere.

ALWAYS_ALIVE 

low_power: false: normal start preamble on every copy, CTRL1 0x00.

LOW_POWER 

low_power: true: copy 1 LONG_PREAMBLE + CTRL1_LOW_POWER, repeats normal.

Definition at line 46 of file oneway_controller.h.

◆ OneWayPreamble

enum class esphome::home_io_control::OneWayPreamble : uint8_t
strong

Which wake-up shape one copy of a 1W burst gets.

Enumerator
WAKE 

The long wake-up preamble (LONG_PREAMBLE), paired with CTRL1_LOW_POWER set.

NORMAL 

The runtime-tunable normal_start_preamble, CTRL1's low-power bit clear.

Definition at line 58 of file oneway_controller.h.

◆ PairingOutcome

enum class esphome::home_io_control::PairingOutcome : uint8_t
strong

Final disposition of a pairing attempt, used by the result sensor string.

Enumerator
NONE 

No attempt has completed yet (initial state).

PAIRED 

The key exchange completed and the device is registered.

NO_RESPONSE 

No device responded to discovery.

INVALID_RESPONSE 

Discovery saw traffic but nothing valid.

KEY_EXCHANGE_FAILED 

Discovery succeeded but the key exchange did not complete.

KEY_TRANSFER_REFUSED 

The device answered our key transfer with CMD_ERROR_RESP; the code is in PairingTelemetry::key_refusal_code().

KEY_RECEIVED 

pairing_key_transfer: pull received a key that differs from ours and the device

proved it holds it; the device is not registered (see PairingEngine::run_key_pull_phase_()).

HUB_KEY_RETURNED 

A pull returned the hub's own key from a node no platform drives (an Atlantic radiator's pairing node), and no device that holds the key answered; nothing is registered.

Definition at line 80 of file pairing_telemetry.h.

◆ PairingTelemetryEventKind

Kind of a recorded telemetry event.

Enumerator
TX 

We transmitted a frame.

RX 

We received and accepted a frame for the current wait (including a seeded pre-window sighting — see PairingTelemetryEvent::aux).

RX_REJECT 

We received a frame that parsed but was rejected (wrong source, wrong command, etc.).

LBT_DEFER 

A listen-before-talk check deferred a transmit because the channel was busy.

PHASE 

The pairing state machine advanced to a new phase.

Definition at line 30 of file pairing_telemetry.h.

◆ PendingOperationType

Discriminator for entries in the pending-operation deque.

Enumerator
SET_POSITION 

set_device_position call (position 0–100 or special values).

SET_TILT 

set_device_tilt call (tilt percentage 0–100).

SET_POSITION_AND_TILT 

Combined set_device_position_and_tilt call.

DEVICE_COMMAND 

Named device command (STOP, FAVORITE, VENT).

SET_LIGHT_STATE 

set_light_state call (binary on/off).

SET_LOCK_STATE 

set_lock_state call (locked/unlocked).

SET_SWITCH_STATE 

set_switch_state call (binary on/off).

SET_FAN_POSITION 

set_fan_position call (heat level of an exterior heater as an IO position).

ONEWAY_COMMAND 

1W named command sent as a controller identity.

ONEWAY_POSITION 

1W numeric position sent as a controller identity.

ONEWAY_ENROLL 

1W add-controller (0x30) registering an identity.

ONEWAY_UNENROLL 

1W remove-controller (0x39) un-registering an identity.

REQUEST_STATUS 

request_device_status call (poll for current position).

REQUEST_NAME 

request_device_name call (poll for stored device name).

REQUEST_LIMITATION 

request_device_limitation call (read the device's minimum limitation).

REQUEST_SUN_LEVEL 

request_device_sun_level call (subscribe to EVENT_LOCAL_SENSOR, read the answer).

REQUEST_BATTERY_LEVEL 

request_device_battery_level call (subscribe to EVENT_BATTERY_LEVEL_CHANGE, read the answer).

DISCOVER_AND_PAIR 

discover_and_pair call (starts 3-phase pairing flow).

Definition at line 27 of file operation_queue.h.

◆ RainSensorState

enum class esphome::home_io_control::RainSensorState : uint8_t
strong

What the hub's latest minimum-limitation read says about a rain-protection limit.

Enumerator
UNKNOWN 

No usable reading: never read, or the last reads went unanswered.

DRY 

The device answered and reports no rain-sized opening limit.

RAIN 

The device answered and reports an opening limit of rain size.

Definition at line 287 of file proto_device_model.h.

◆ ReplyDisposition

enum class esphome::home_io_control::ReplyDisposition : uint8_t
strong

What the caller wants done with the frame a listen just received.

Enumerator
ACCEPT 

This is the frame the caller was waiting for — stop listening, return ACCEPTED.

IGNORE 

Unparsable / not ours / wrong exchange — keep listening.

ABORT 

An explicit refusal (e.g. CMD_ERROR_RESP) — stop listening, return ABORTED.

Definition at line 120 of file hub_exchange.h.

◆ RollCallAttemptResult

Outcome of one roll-call attempt or sweep, so the caller can surface a build failure.

Enumerator
OK 

Request built and transmitted (with or without replies).

BUILD_FAILED 

create_discovery_request() failed; the caller should abort the whole scan.

Definition at line 122 of file roll_call.h.

◆ RollCallKeyDisplay

enum class esphome::home_io_control::RollCallKeyDisplay : uint8_t
strong

How a responder line shows the key it answered under.

Enumerator
HIDDEN 

Never print the key: its check value only. For anything Home Assistant records.

BANNERED 

Print the key in a snippet, wrapped in the line's own DO-NOT-SHARE block.

PLAIN 

Print the key in a snippet with no framing: the caller's report supplies one banner.

Definition at line 191 of file roll_call.h.

◆ ScanAddResult

enum class esphome::home_io_control::ScanAddResult : uint8_t
strong

Outcome of add_scan_responder(), so callers can tell a harmless repeat from real loss.

Enumerator
ADDED 

New responder recorded.

DUPLICATE 

Already recorded from an earlier reply; nothing changed.

FULL 

Dropped: SCAN_MAX_REPLIES distinct responders already recorded.

Definition at line 139 of file roll_call.h.

◆ ScanPowerClasses

enum class esphome::home_io_control::ScanPowerClasses : uint8_t
strong

Which power classes scan_paired_devices() calls.

The roll-call calls both classes by default; this narrows it for bisecting an installation in the field (does a device answer the low-power call but not the always-alive one, or vice versa?) or for restoring the shorter single-pass scan on an install with no solar/battery devices. Values mirror power_save_mode_name() (proto_constants.h) so the report, this tunable, and a responder's own self-reported power-save byte share one vocabulary.

Enumerator
BOTH 

Low-power pass, then always-alive pass (default).

ALWAYS_ALIVE 

Always-alive pass only: the single-shape roll-call, ~6 s.

LOW_POWER 

Low-power pass only.

Definition at line 89 of file tuning_config.h.

◆ SX1262RxBandwidth

enum class esphome::home_io_control::SX1262RxBandwidth : uint8_t
strong

Valid SX1262 RX bandwidth options (kHz register values).

The numeric values are the register-encoded (double-sideband) bandwidth selectors used by RadioSX1262::set_rx_bandwidth() — a regular (mantissa, exponent) grid, with the bandwidth roughly doubling per group.

Semtech's sizing rule for GFSK is BW_DSB >= bitrate + 2 * Fdev + carrier offset, which is 76.8 kHz for the IO-Homecontrol waveform (38.4 kbps, 19.2 kHz deviation) before any offset. The SX1262 has no AFC in GFSK mode, so this filter alone has to absorb the peer's carrier offset. These values are not comparable to SX1276RxBandwidth, whose values are single-sideband.

Byte-for-byte identical to LR1121RxBandwidth below, since both chips share the same Semtech GFSK bandwidth grid; the Sx1262AndLr1121BandwidthTablesAgree test pins that. If these two tables ever need to diverge for a real chip difference, say why here.

Enumerator
BW_39_0_KHZ 

39.0 kHz — narrowest; half the 76.8 kHz sizing floor, for probing only.

BW_46_9_KHZ 

46.9 kHz — narrow.

BW_58_6_KHZ 

58.6 kHz — default; the narrowest value validated on real hardware here.

BW_78_2_KHZ 

78.2 kHz — just above the 76.8 kHz sizing floor.

BW_117_3_KHZ 

117.3 kHz — clears the sizing floor with ~20 kHz of offset margin.

BW_156_2_KHZ 

156.2 kHz — the value a Somfy LightVar_Wh_io dimmer needs.

BW_187_2_KHZ 

187.2 kHz — widest selectable option.

Definition at line 39 of file tuning_config.h.

◆ SX1276RxBandwidth

enum class esphome::home_io_control::SX1276RxBandwidth : uint8_t
strong

Valid SX1276 RX bandwidth options (RegRxBw register bytes).

The numeric values are the SX1276 RegRxBw encodings (RxBwMant in bits[4:3], RxBwExp in bits[2:0]) written verbatim to both REG_RX_BW and REG_AFC_BW. Single-sideband bandwidth = FXOSC / (RxBwMant * 2^(RxBwExp+2)) with FXOSC = 32 MHz; the filter's total (double-sideband) width is twice that, so the 41.7 kHz default spans 83.4 kHz — just above the 76.8 kHz sizing floor for this waveform. The SX1276's AFC re-centres the receiver on each packet's carrier. Narrower rejects more out-of-band noise (higher sensitivity); wider tolerates more LO frequency offset.

Enumerator
BW_20_8_KHZ 

20.8 kHz — narrowest; maximal noise rejection, least LO-offset tolerance.

BW_41_7_KHZ 

41.7 kHz — default (validated against real devices).

BW_62_5_KHZ 

62.5 kHz.

BW_83_3_KHZ 

83.3 kHz.

BW_125_0_KHZ 

125.0 kHz — widest selectable option.

Definition at line 58 of file tuning_config.h.

◆ TowelDryerMode

enum class esphome::home_io_control::TowelDryerMode : uint8_t
strong

Modes of a towel dryer's mode register (HeatingFunction::TOWEL_MODE).

OFF, PROG and MANUAL are the values two Atlantic Adelis units report with their display showing off, PROG and MANUEL (issue #202, corpus atlantic_adelis_probe_mode_reads).

Enumerator
OFF 

Off (standby).

AUTO 

Defined by the register's range, never seen on a device; never written here.

PROG 

Program mode: the device runs its own weekly program.

MANUAL 

Manual mode: the device holds the comfort setpoint.

Definition at line 86 of file proto_heating.h.

◆ TravelSpeed

enum class esphome::home_io_control::TravelSpeed : uint8_t
strong

The speed a cover's position moves ask the motor for (ADR 0065).

The 8-byte execute frame carries a speed profile byte; the 6-byte frame a wall pad sends carries none and leaves the speed to the motor. Like inverted, a YAML-declared preference: nothing on the wire reports which one a device is moved with. The enumerator for "the motor's own speed" is not a bare DEFAULT, which the Arduino core defines as a macro on some targets.

Enumerator
MOTOR_DEFAULT 

The 6-byte frame with no profile: the motor's own speed, as its wall pad moves it.

SILENT 

Profile 0x05, the manufacturer app's silent operation: slower than the motor's own.

FAST 

Profile 0x06, what the hub sends unless a cover says otherwise.

Definition at line 304 of file proto_device_model.h.

◆ TxWake

enum class esphome::home_io_control::TxWake : uint8_t
strong

How hard a transmission has to work to be heard: the wake-up level the receiver needs.

A radio that wakes receivers by preamble length gets the same information from RadioTxConfig::preamble_len and may ignore this; a radio that wakes them some other way (a train of frames, for example) reads it instead of guessing from a preamble length.

Enumerator
NONE 

The receiver is already listening on this channel: the frame continues an exchange.

SHORT 

The receiver is awake, but may be scanning channels.

LONG 

The receiver may be duty-cycling.

Definition at line 77 of file proto_timing.h.

Function Documentation

◆ acei_level_name()

const char * esphome::home_io_control::acei_level_name ( uint8_t level)

Get a human-readable name for an ACEI priority level (0–7).

Priority levels form a hierarchy: level 0 (human protection) is highest and overrides all others. Level 3 is the default for remote controllers.

Parameters
levelPriority level value (0–7).
Returns
Null-terminated string such as "user_default" or "protection_sensor".

Definition at line 223 of file proto_constants.cpp.

Here is the call graph for this function:

◆ add_scan_responder()

ScanAddResult esphome::home_io_control::add_scan_responder ( ScanResponder * responders,
uint8_t & count,
uint8_t capacity,
const IoFrame & frame,
int16_t rssi_dbm,
uint8_t key_slot,
const ScanResponder *& entry )

Record a responder unless its address is already present.

Deduplication is by node ID across the whole scan (all keys, all passes), so a device that answers several of the attempts still yields one entry. The duplicate check runs before the capacity check so that repeat replies from already-recorded devices never look like overflow once the array is full.

Parameters
respondersAccumulated array, appended to in place.
countIn: entries already present. Out: updated count.
capacityMaximum entries responders can hold.
frameReply frame to decode and store.
rssi_dbmRSSI of that reply.
key_slotThe key slot of the sweep the reply arrived in.
entryOut: the newly recorded entry (ADDED), the already-recorded entry with the same src (DUPLICATE), or nullptr (FULL) — lets a caller read back the stored manufacturer/flags without decoding the frame a second time.
Returns
Which of the three outcomes occurred.

Definition at line 17 of file roll_call.cpp.

Here is the call graph for this function:

◆ address_class_name()

const char * esphome::home_io_control::address_class_name ( AddressClass address_class)

Get a human-readable name for an address classification.

Parameters
address_classClassification returned by classify_address().
Returns
Null-terminated string such as "broadcast_all" or "unicast".

Definition at line 220 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ apply_error_response()

bool esphome::home_io_control::apply_error_response ( const IoFrame & response,
ManagementActionResult & result )
inline

Decode a CMD_ERROR_RESP frame's result code into result.

Populates has_result_code/result_code but deliberately leaves result.message untouched: rename and identify_device report different wording for the same decoded code, so message composition stays with each caller. On an empty error response, sets a stock message itself (there is no code to report) and returns false; callers should treat that the same way as a decoded code, just without result-code-specific wording.

Parameters
responseFrame whose cmd is CMD_ERROR_RESP.
resultResult to populate.
Returns
true if a result code was decoded, false if the response carried no data.

Definition at line 105 of file management_helpers.h.

Here is the call graph for this function:

◆ att_class_ms()

uint32_t esphome::home_io_control::att_class_ms ( uint8_t att_class)
constexpr

Turnaround time of an ATT class in milliseconds: 5, 10, 20 or 40 (each class doubles it).

Parameters
att_classATT class (0–3), e.g. from discovery_att_class().
Returns
The class's turnaround time in milliseconds.

Definition at line 953 of file proto_constants.h.

Here is the call graph for this function:

◆ att_class_name()

const char * esphome::home_io_control::att_class_name ( uint8_t att_class)

Get a human-readable turnaround time string for an ATT class value.

Parameters
att_classATT class (0–3) extracted from the Multi Information Byte.
Returns
Null-terminated string such as "5ms", "10ms", "20ms", or "40ms".

Definition at line 158 of file proto_constants.cpp.

Here is the call graph for this function:

◆ bandwidth_from_string()

std::optional< uint8_t > esphome::home_io_control::bandwidth_from_string ( const BandwidthOption * table,
size_t n,
const std::string & value )

Parse a YAML/UI bandwidth string against a table, returning the matching register byte.

Accepts surrounding whitespace and a trailing "kHz" suffix in any case, and accepts both the one-decimal spelling ("39.0") and the integer-truncated spelling ("39") of every table entry.

Returns
The register byte, or std::nullopt when the string matches no entry.

Definition at line 90 of file tuning_config.cpp.

Here is the call graph for this function:

◆ bandwidth_to_khz()

float esphome::home_io_control::bandwidth_to_khz ( const BandwidthOption * table,
size_t n,
uint8_t reg,
float fallback )

Look up the kHz value for a register byte in a bandwidth table.

Parameters
tableBandwidth option table.
nNumber of entries in table.
regRegister byte to look up.
fallbackkHz value returned when reg is not in the table.

Definition at line 76 of file tuning_config.cpp.

Here is the call graph for this function:

◆ bandwidth_to_string()

std::string esphome::home_io_control::bandwidth_to_string ( float khz)

Format a kHz value as its YAML/UI option string (bare number, one decimal, e.g. "117.3").

Definition at line 84 of file tuning_config.cpp.

Here is the call graph for this function:

◆ battery_state_name()

const char * esphome::home_io_control::battery_state_name ( BatteryState state)

Get the name a battery level is published under.

Parameters
stateThe level to name.
Returns
"low", "normal" or "full", or "not_reported" for NOT_REPORTED.

Definition at line 70 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ bool_select_get()

template<bool TuningConfig::* Field>
std::string esphome::home_io_control::bool_select_get ( const TuningConfig & t)

Definition at line 115 of file tuning_registry.cpp.

Here is the call graph for this function:

◆ bool_select_set()

template<bool TuningConfig::* Field>
bool esphome::home_io_control::bool_select_set ( TuningConfig & t,
const std::string & v )

Definition at line 118 of file tuning_registry.cpp.

Here is the call graph for this function:

◆ bool_to_string()

std::string esphome::home_io_control::bool_to_string ( bool value)
static

Definition at line 162 of file management_actions.cpp.

Here is the call graph for this function:

◆ broadcast_target_type()

DeviceType esphome::home_io_control::broadcast_target_type ( const uint8_t addr[NODE_ID_SIZE])

Extract the target device type from a typed broadcast address.

Broadcast addresses encode the device type in bits [9:2] of the combined address bytes 1–2. This function extracts that type. Returns UNKNOWN if the address is not a broadcast (first byte != 0x00).

Parameters
addrThree-byte broadcast address.
Returns
DeviceType encoded in the address, or DeviceType::UNKNOWN for unicast.

Definition at line 236 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ build_device_yaml_snippet()

std::string esphome::home_io_control::build_device_yaml_snippet ( DeviceType type,
uint8_t subtype,
const std::string & device_id,
bool metadata_complete,
bool low_power,
const std::string & system_key_hex = std::string() )

Build the ready-to-paste YAML block describing a device, for both a fully-decoded device and one whose type/subtype wasn't reported.

With metadata_complete == false, the type/subtype are unknown, so the returned snippet uses a <cover|light|switch|lock> platform placeholder and a commented-out io_device_type explanation; type and subtype are ignored in this case.

With metadata_complete == true, the snippet names the concrete ESPHome platform for type and fills in io_subtype, invert_position: true for a kind inverted by default and dimmable: true for a light subtype with intermediate levels (device_feature::INVERTED and device_feature::LEVELS of the kind). If the (type, subtype) has no ESPHome platform yet (device_has_platform() in proto_device_types.h), an empty string is returned instead — the caller decides what to say when there's no snippet to show.

low_power: true is emitted in both shapes when low_power is set (the device self-reported POWER_SAVE_LOW_POWER in its discovery Multi Information Byte); it is omitted entirely otherwise, since its absence is a valid, correct config.

The emitted keys must track the device-bound platform schemas (platform_schema_extension() in platform_common.py, plus each of cover.py/light.py/switch.py/lock.py's own extra keys) by hand. make yaml-emitter-sync (scripts/check-yaml-emitters.py) catches drift between the two statically.

Parameters
typeDecoded device type.
subtypeDecoded device subtype; only used when metadata_complete is true.
device_idHex device ID string (e.g. "38B4A1").
metadata_completeWhether the discovery response included type/subtype metadata.
low_powerWhether the device self-reported a low-power / duty-cycled class; emits low_power: true when set, in both snippet shapes.
system_key_hexThe device's own system key as 32 hex characters, or empty when it uses the hub's. Emits system_key: "<hex>" in both snippet shapes. Key material: a caller that passes one must print the snippet only inside a DO-NOT-SHARE block.
Returns
Multi-line YAML snippet, or an empty string when metadata_complete is true but the (type, subtype) maps to no ESPHome platform.

Definition at line 340 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ build_execute_with_second_bitmap_fp()

bool esphome::home_io_control::build_execute_with_second_bitmap_fp ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint8_t acei,
uint16_t main_parameter,
uint8_t second_bitmap,
uint16_t value )
static

Build an execute frame (0x00) with a main parameter (a temperature, an alias or a heating level) and one functional parameter of the second bitmap (FP9-FP16).

A functional-parameter value is two bytes, high byte first, like the main parameter.

Definition at line 371 of file proto_commands.cpp.

Here is the call graph for this function:

◆ build_hub_key_pull_report()

std::string esphome::home_io_control::build_hub_key_pull_report ( const PulledKeyReportInfo & info,
const ScanState & state,
DeviceRegistry & registry )

Build the report for a pull that returned the hub's own key from a node no platform drives.

The devices that hold the hub key were found by a roll-call under it, or by a direct read of the address after an Atlantic pairing node. Their blocks need no key and no banner, so they read like a scan's unknown responders.

Parameters
infoThe device that sent the key.
stateThe devices found under the hub key.
registryDevice registry before any registration: a responder the YAML declares is known; one an earlier pairing in this boot added is not (DeviceRegistry::get_declared()).
Returns
The report, or an empty string when nothing answered.

Definition at line 244 of file roll_call.cpp.

Here is the call graph for this function:

◆ build_pulled_key_report()

std::string esphome::home_io_control::build_pulled_key_report ( const PulledKeyReportInfo & info,
const ScanState & state,
DeviceRegistry & registry )

Build the one report a proven key pull prints after looking for the devices that hold the key.

One DO-NOT-SHARE banner frames the whole report (device log only; it carries the key). Each responder gets its summary line and a paste-ready block with system_key: added (RollCallKeyDisplay::PLAIN); a responder of a type no platform drives yet gets the "not supported yet" line and its key. With no responder, the report gives the key once for the pairing node itself. Nothing here registers a device.

Parameters
infoThe device that sent the key.
stateThe devices found under that key (roll-call, or the direct read of the next address).
registryDevice registry as it stands before any registration: a responder the YAML declares is reported as known; one an earlier pairing in this boot added gets its block again.

Definition at line 263 of file roll_call.cpp.

Here is the call graph for this function:

◆ build_scan_report()

std::string esphome::home_io_control::build_scan_report ( const ScanState & state,
DeviceRegistry & registry,
ScanPowerClasses selection,
const ScanKeys & keys,
bool reveal_keys )
static

Build the final report: known/unknown grouping, header, the selection NOTE, and the truncation NOTE.

Definition at line 746 of file management_actions.cpp.

Here is the call graph for this function:

◆ bytes_to_hex()

void esphome::home_io_control::bytes_to_hex ( const uint8_t * data,
uint8_t len,
char * out,
size_t out_size )
inline

Definition at line 23 of file log_frame.h.

Here is the call graph for this function:

◆ challenge_resp_hmac()

bool esphome::home_io_control::challenge_resp_hmac ( const IoFrame & origin,
const uint8_t challenge[HMAC_SIZE],
const uint8_t * key,
uint8_t hmac[HMAC_SIZE] )

The 0x3D MAC for origin.

Compute the 6-byte MAC a challenge response (0x3D) carries for origin: create_hmac() over the challenged frame's command byte and payload.

The transcript is the challenged frame, not the 0x3D wrapper: using the origin command byte and payload was one of the key interoperability findings.

Used for our own 0x3D (create_challenge_resp()).

Parameters
originThe challenged frame.
challenge6-byte challenge from the 0x3C.
key16-byte key.
hmacOutput: 6-byte MAC.
Returns
true on success.

Definition at line 1033 of file proto_commands.cpp.

Here is the call graph for this function:

◆ classify_address()

AddressClass esphome::home_io_control::classify_address ( const uint8_t addr[NODE_ID_SIZE])

Classify an IO-Homecontrol 3-byte address.

Determines whether an address is unicast, a typed broadcast, or a discovery-related broadcast based on protocol addressing rules.

Parameters
addrThree-byte node address to classify.
Returns
Classification indicating the address type.

Definition at line 196 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ clear_inactive_programme_reading()

void esphome::home_io_control::clear_inactive_programme_reading ( const HeatingProfile & profile,
HeatingReadings & readings )

Drop the temperature in force from the readings when the programme does not run.

Keeps a value from an earlier run of the programme from being shown when the heater returns to it.

Parameters
profileThe device's profile.
[in,out]readingsWhat the hub has read from the heater.

Definition at line 540 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ clear_moving_evidence()

void esphome::home_io_control::clear_moving_evidence ( IoDevice & dev)
inline

Forget that a device was travelling: it reported itself stopped (including in the ack to a STOP), a linked remote was heard stopping it, or it stayed silent to an exchange sent while it was believed moving.

The counterpart of note_moving_evidence(). Without it a receiver that has demonstrably stopped would keep reading as "moving" for the rest of LOW_POWER_MAX_TRAVEL_MS, although it goes back to duty-cycling within seconds and then hears only the wake-up preamble.

Parameters
devDevice to clear.

Definition at line 669 of file proto_device_model.h.

Here is the call graph for this function:

◆ collect_scan_keys()

ScanKeys esphome::home_io_control::collect_scan_keys ( const uint8_t * hub_key,
DeviceRegistry & registry )
static

Collect the keys to sweep: the hub key, then each per-device key that differs from it and from every key already collected.

A per-device key equal to the hub key changes nothing, so it never earns a sweep of its own.

Definition at line 713 of file management_actions.cpp.

Here is the call graph for this function:

◆ command_carries_key_material()

bool esphome::home_io_control::command_carries_key_material ( uint8_t cmd)
inline

Whether a frame command's payload carries key material that must never be logged or reported verbatim.

CMD_KEY_TRANSFER (0x32) carries the system key encrypted with the pairing transfer key — still key material, so callers rendering frame bytes must mask this payload rather than print it.

CMD_CHALLENGE_REQ (0x3C) and CMD_CHALLENGE_RESP (0x3D) are masked for the same reason even though neither carries the key itself: create_hmac() builds the 0x3D payload by AES-128-ECB encrypting an IV built from the 0x3C challenge and the challenged frame's own (plaintext) transcript under the system key, then truncating to 6 bytes (proto_crypto.cpp). Logging both frames publishes a known-plaintext/known-ciphertext pair for that block cipher under the system key, so they get the same treatment as literal key material.

CMD_ONEWAY_ADD_CONTROLLER (0x30) is the 1W equivalent of CMD_KEY_TRANSFER. Its payload is not literally the key — it is the key wrapped with the public TRANSFER_KEY — but the unwrap needs nothing secret: the IV derives only from the sender's node address, which is plaintext in the same frame's header. So publishing those bytes is equivalent to publishing the key, and they are masked for the same reason 0x32 is. The one deliberate exception is the adoption report in oneway_key_adoption.cpp, which formats the decoded key directly rather than going through any frame-rendering path.

Parameters
cmdFrame command byte.
Returns
true if the payload of a frame with this command must be masked.

Definition at line 46 of file redaction.h.

Here is the call graph for this function:

◆ command_main_parameter()

bool esphome::home_io_control::command_main_parameter ( CoverCommand command,
uint16_t & main_parameter )
inline

The main parameter a named command sends.

STOP is the one command that is a main-parameter value without being a named position; every other value comes from NAMED_POSITIONS. The frame builders for 2W and 1W both read their bytes here, so a value is written down once.

Parameters
commandCover command.
main_parameterReceives the value on success.
Returns
true for STOP and for every command in NAMED_POSITIONS; false for any other command (FORCE_OPEN is built from a position, not from a named value).

Definition at line 241 of file proto_device_types.h.

Here is the call graph for this function:

◆ command_name()

const char * esphome::home_io_control::command_name ( uint8_t cmd)

Get a human-readable name for any IO-Homecontrol command ID.

Returns a short uppercase identifier suitable for log lines (e.g., "EXECUTE", "DISCOVER_REQ", "CHALLENGE_RESP"). Unknown commands return "UNKNOWN_CMD".

Parameters
cmdCommand byte from the frame header.
Returns
Null-terminated string.

Definition at line 10 of file proto_constants.cpp.

Here is the call graph for this function:

◆ command_result_description()

const char * esphome::home_io_control::command_result_description ( uint8_t result)

Return a human-readable explanation for a CMD_ERROR_RESP result code.

Parameters
resultResult byte from CMD_ERROR_RESP data[0].
Returns
Short description suitable for warn-level logs.

Definition at line 379 of file proto_constants.cpp.

Here is the call graph for this function:

◆ command_result_is_transient()

bool esphome::home_io_control::command_result_is_transient ( uint8_t result)
constexpr

Check whether a result code describes one exchange rather than the device's state.

SESSION_INTERRUPTED is the one such code: a garage drive answered three status reads with it right after a command it did not confirm, and the following exchanges worked normally (issue #163). It is logged and returned to an action like any other result, but a lasting view of the device (the Active Issue sensor) treats it as no result.

Parameters
resultResult byte from CMD_ERROR_RESP data[0].
Returns
true for a result that says nothing about the device once the exchange is over.

Definition at line 1005 of file proto_constants.h.

Here is the call graph for this function:

◆ command_result_name()

const char * esphome::home_io_control::command_result_name ( uint8_t result)

Return a stable symbolic name for a CMD_ERROR_RESP result code.

A byte with state 0 and a named code reads as a packed result code (RESULT_COMMAND_UNKNOWN .. RESULT_ALREADY_CONFIGURED); every other byte is looked up in the run-status / limitation table.

Parameters
resultResult byte from CMD_ERROR_RESP data[0].
Returns
Uppercase symbolic name, or "UNKNOWN_RESULT_CODE" when unmapped.

Definition at line 246 of file proto_constants.cpp.

Here is the call graph for this function:

◆ contains_key_material()

bool esphome::home_io_control::contains_key_material ( const uint8_t * buf,
size_t len,
const uint8_t * system_key )
inline

Whether a buffer contains the system key as a contiguous run of bytes.

A last-line-of-defense scan for accidental key leaks in ad hoc debug or report text, independent of which command produced the buffer.

Parameters
bufBuffer to scan.
lenLength of buf in bytes.
system_keyPointer to the AES_KEY_SIZE-byte system key.
Returns
true if system_key appears anywhere in buf.

Definition at line 59 of file redaction.h.

Here is the call graph for this function:

◆ count_bits()

uint8_t esphome::home_io_control::count_bits ( uint8_t v)
static

Number of set bits in a bitmap byte (the component does not use the C++20 bit header yet).

Definition at line 642 of file proto_commands.cpp.

Here is the call graph for this function:

◆ count_devices_using_key()

size_t esphome::home_io_control::count_devices_using_key ( DeviceRegistry & registry,
const uint8_t * hub_key,
const uint8_t * key )
static

How many registered devices exchange with key: those with it as their own key, or, for the hub key, those with no key of their own (or one equal to the hub's).

Definition at line 732 of file management_actions.cpp.

Here is the call graph for this function:

◆ count_enabled_passes()

uint8_t esphome::home_io_control::count_enabled_passes ( ScanPowerClasses selection)

How many of ROLL_CALL_PASSES selection actually calls.

Parameters
selectionThe scan_power_classes tunable.

Definition at line 52 of file roll_call.cpp.

Here is the call graph for this function:

◆ cover_command_name()

const char * esphome::home_io_control::cover_command_name ( CoverCommand cmd)

Get a human-readable name for a CoverCommand.

Parameters
cmdThe cover command to name.
Returns
Null-terminated string such as "STOP", "FAVORITE", "VENT", "PARTIAL" or "PEDESTRIAN".

Definition at line 111 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ crc_ccitt()

uint16_t esphome::home_io_control::crc_ccitt ( const uint8_t * data,
uint8_t len )

CRC-CCITT used by the IO-Homecontrol protocol for frame validation.

Compute CRC‑CCITT (poly 0x1021, init 0x0000) over a buffer.

Polynomial: 0x1021 (reversed 0x8408), initial value: 0x0000. Radio chips with native IO-Homecontrol framing compute this in hardware; drivers for other chips call this helper instead.

Used by radio drivers without hardware IO-Homecontrol CRC support and by frame validation in tests.

Parameters
dataPointer to data bytes.
lenNumber of bytes.
Returns
16‑bit CRC value.

Definition at line 60 of file proto_frame.cpp.

Here is the call graph for this function:

◆ create_1w_add_controller()

bool esphome::home_io_control::create_1w_add_controller ( IoFrame & f,
const uint8_t src[NODE_ID_SIZE],
DeviceType target_type,
uint8_t manufacturer,
uint16_t sequence,
const uint8_t controller_key[AES_KEY_SIZE],
bool with_mac )

Build a 1W add-controller frame (CMD 0x30).

Build a 1W add-controller frame (CMD 0x30) that registers this identity as a controller on every device of target_type currently in association mode — a physical 2 s PROG hold on the receiver, which only the device's owner can trigger (see ADR 0026).

See proto_commands.h for the full contract, including why the MAC is an optional, out-of-length trailer here and not inside the declared payload, and why with_mac exists at all (real hardware omits it; the published vector doesn't).

The payload wraps controller_key for transmission rather than sending it in the clear: enc_key = crypto::crypt_1w_key(src, controller_key), the same self-inverse primitive decode_1w_add_controller() uses to unwrap an overheard 0x30 (crypt_1w_key() is its own inverse, so this call and that one are literally the same function). The wrap key is the public TRANSFER_KEY (proto_constants.h) and the wrap IV derives only from src — see crypt_1w_key()'s doxygen for why that is not a weakness: the receiver decrypts enc_key first, then checks this frame's MAC under the key it just recovered, which is what makes the MAC meaningful (it proves the sender holds the key it just sent), not the wrap's secrecy.

Declared payload (20 bytes): enc_key[16] + manufacturer[1] + data[1]=0x01 + sequence[2]. When with_mac is true, the MAC is an out-of-length trailer, not part of the declared payload — CTRL0's 5-bit length field cannot express 29 declared bytes plus a 6-byte MAC together (see IoFrame::has_mac, frame_carries_mac_trailer() in proto_frame.h), so this builder sets f.has_mac = true and fills f.mac[] directly; serialize() appends it after the declared length and folds it into the length it returns, so a caller's CRC (computed over that return value) covers it automatically. When present, this is the opposite placement from create_1w_remove_controller() below, whose MAC sits inside the declared payload — copying one builder's shape to make the other is the most likely bug a future edit introduces here.

Warning
Real hardware and the published documentation vector disagree on whether the MAC trailer exists at all — this is why with_mac is a parameter, not a fixed choice. The published known-answer vector (tests/corpus/captures/enrollment/reference_1w_enrollment_add_controller_kat.yaml, with_mac=true, 35 bytes) is what with_mac defaults to, for compatibility with that vector's own pinning tests. Most real hardware captures instead show 29 bytes with no trailer at all — see tests/corpus/captures/enrollment/somfy_smoove_enrollment_add_and_remove_controller_sx1276.yaml. The enroll button calls this with with_mac=false to match that shape, but this is a preference rather than a hardware requirement: a real Somfy Izymo dimmer has separately been shown to accept the with_mac=true form as well, enrolling successfully and remaining controllable afterward — both shapes are safe to send.
The key-wrap IV comes from src, not the destination: a broadcast destination carries no identity, and src is the only value that reproduces the published frame this builder is pinned against. That frame's MAC is genuine and verifies, so a missing trailer on real hardware is not evidence that the frame is unauthenticated.
Parameters
fIoFrame to populate.
srcOur 3-byte controller node address (the 1W controller identity's node_id) — also the key-wrap IV's only input, so this must be the identity actually being registered.
target_typeDevice class to address; encoded via encode_broadcast_address(). Only devices of this class currently in association mode react.
manufacturerManufacturer ID byte to advertise (man_id); echoed back verbatim by anything that later decodes this frame with decode_1w_add_controller().
sequence2-byte rolling sequence for this transmission (big-endian on wire); the caller's identity/sequence store owns incrementing and persisting this, not this builder.
controller_key16-byte key this identity registers itself with — normally the hub's own system_key, wrapped here for transmission, never sent in the clear.
with_macTrue to append the out-of-length MAC trailer (the published vector's shape, and this parameter's default for backward compatibility); false to omit it entirely (the shape most real hardware captures this project holds actually use, and what the enroll button passes). Real hardware has been shown to accept both — see the @warning above.
Returns
true on success; false if crypto::crypt_1w_key() fails, or (when with_mac is true) crypto::create_1w_hmac() fails — no partially-populated frame is left behind on failure.

Definition at line 449 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_1w_execute_command()

bool esphome::home_io_control::create_1w_execute_command ( IoFrame & f,
const uint8_t src[NODE_ID_SIZE],
DeviceType target_type,
CoverCommand cmd,
uint16_t sequence,
const uint8_t controller_key[AES_KEY_SIZE],
uint8_t acei,
bool broadcast_all )

Build a 1W named-command execute frame (CMD 0x00) targeting a device class.

See proto_commands.h for the full contract, including why 1W has no FORCE_OPEN: the only known wire encoding for the label (POS_FORCE_OPEN, main=0x64) was hardware-tested as an ordinary move-to-50% command, not a lock bypass — see the POS_FORCE_OPEN doc comment in proto_constants.h. Passing CoverCommand::FORCE_OPEN here falls through to default and returns false, matching 2W's create_execute_command().

Covers three of CoverCommand's values — STOP, FAVORITE, VENT; PARTIAL and PEDESTRIAN have no 1W encoding and return false like FORCE_OPEN. FORCE_OPEN is not handled here: the only wire code this project has for that label, POS_FORCE_OPEN (main=0x64), was hardware-tested as an outbound CMD_EXECUTE command and found to move a real device to 50% open, not bypass anything (see POS_FORCE_OPEN in proto_constants.h). There is no known 1W force-open encoding, so passing CoverCommand::FORCE_OPEN here falls to default and returns false, the same way create_execute_command()'s 2W dispatch rejects it.

  • STOP: main=POS_STOP (0xD2), modifier=0x00
  • FAVORITE: main=POS_FAVORITE (0xD8), modifier=0x00
  • VENT: main=POS_FAVORITE (0xD8), modifier=POS_VENT_MODIFIER (0x03)
Warning
These three do not rest on the same evidence, even though they read as a uniform list. STOP is the only one a real frame pins: own captures (tests/corpus/captures/oneway/somfy_smoove_oneway_stop_sx1276.yaml) and the published IV vector (tests/corpus/captures/oneway/reference_1w_oneway_execute_iv_vector.yaml) carry main=0xD2. VENT is not captured anywhere in this project, so main=0xD8/mod=0x03 is assumed. FAVORITE has no support either: main=0xD8/mod=0x00 here is extrapolated purely by analogy with the 2W builder's FAVORITE encoding. Worse, the one real capture this project has of an actual My/favorite button press — tests/corpus/captures/oneway/somfy_smoove_oneway_favorite_sx1276.yaml, pinned by OneWayCommands.FavoriteButtonCaptureIsWritePrivateNotExecute in tests/oneway/oneway_commands_test.cpp — contradicts it directly: that remote's My button is CMD_WRITE_PRIVATE (0x20) with a 16-byte payload, not CMD_EXECUTE with main=0xD8.

A real enrolled device does act on main=0xD8/mod=0x00, though: it changed the brightness of a Somfy Izymo dimmer, so the byte is accepted, not ignored. What position it targets — a stored "My" position vs. some fixed value — is unconfirmed; a 2W status-poll readback would settle it. Treat FAVORITE as "does something, target unverified", not "plausibly wrong".

FORCE_OPEN is deliberately absent rather than merely untested. Its natural encoding would be main=0x64/mod=0x00 — the same bytes this project labels POS_FORCE_OPEN — but this project hardware-tested that exact main byte as an outbound CMD_EXECUTE and confirmed it moves a real device to 50% open (see POS_FORCE_OPEN in proto_constants.h), not past any lock. There is no known 1W encoding for a real force-open, so it is unimplemented rather than shipped on a mislabeled byte.

The MAC span is the 7 bytes cmd, origin, acei, main0, main1, fp1, fp2 — the command byte through fp2, stopping before the sequence, which is not frame data and never enters the MAC. Pinned by the published IV vector at tests/corpus/captures/oneway/reference_1w_oneway_execute_iv_vector.yaml. This span is command-specific and does not generalise: CMD 0x30's span is cmd + enc_key only — see crypto::create_1w_hmac()'s @warning.

Parameters
fIoFrame to populate.
srcOur 3-byte controller node address (the 1W controller identity's node_id).
target_typeDevice class to address; encoded via encode_broadcast_address().
cmdNamed command to execute (STOP, FAVORITE, or VENT). CoverCommand::FORCE_OPEN returns false — see the
Warning
above.
Parameters
sequence2-byte rolling sequence for this transmission (big-endian on wire); the caller's identity/sequence store owns incrementing and persisting this, not this builder — see the section note above.
controller_key16-byte key held by the transmitting controller identity: the hub's own system_key for its own network, or a foreign key adopted via CMD 0x30 (Phase 3A "key adoption") when transmitting as an adopted identity.
aceiACEI byte for payload[1] — see create_1w_execute_position().
broadcast_allAddress 00 00 3F instead of the typed class — see create_1w_execute_position().
Returns
true on success; false if cmd is CoverCommand::FORCE_OPEN (no 1W encoding exists) or if crypto::create_1w_hmac() fails.

Definition at line 430 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_1w_execute_position()

bool esphome::home_io_control::create_1w_execute_position ( IoFrame & f,
const uint8_t src[NODE_ID_SIZE],
DeviceType target_type,
uint8_t position,
uint16_t sequence,
const uint8_t controller_key[AES_KEY_SIZE],
uint8_t acei,
bool broadcast_all )

Build a 1W position execute frame (CMD 0x00) targeting a device class.

See proto_commands.h for the full contract.

Encodes a 0–100 position into the 2-byte main field (wire value is 2 * position, matching the 2W numeric encoding) inside 1W's 6-byte "special" payload form — 1W uses this short form even for numeric positions, not the 8-byte layout create_execute_position() (2W) uses. The MAC span is command-specific (see create_1w_execute_command()'s doxygen); there is no default, so this builder assembles it itself via the shared internal helper.

Note
Position 50 encodes as 2 * 50 = 0x64 on the wire, the same byte decode_1w_main_intent() labels POS_FORCE_OPEN when reading overheard traffic. That label is a decode-side diagnostic choice only (see POS_FORCE_OPEN in proto_constants.h) — it does not change what this builder sends or what a device does with it. Position 50 is an ordinary position command.
Parameters
fIoFrame to populate.
srcOur 3-byte controller node address (the 1W controller identity's node_id).
target_typeDevice class to address; encoded via encode_broadcast_address().
positionDesired position 0–100 (0=fully open, 100=fully closed).
sequence2-byte rolling sequence for this transmission (big-endian on wire); the caller's identity/sequence store owns incrementing and persisting this, not this builder — see the section note above.
controller_key16-byte key held by the transmitting controller identity: the hub's own system_key for its own network, or a foreign key adopted via CMD 0x30 (Phase 3A "key adoption") when transmitting as an adopted identity.
aceiACEI byte to place at payload[1] — ONEWAY_EXECUTE_ACEI (Somfy-shaped) by default, ONEWAY_EXECUTE_ACEI_VELUX for a VELUX identity. Resolved per identity by oneway_controller.h's effective_execute_acei(); this builder just writes it.
broadcast_allWhen true, address the all-devices broadcast 00 00 3F instead of the typed target_type class — what a handheld cover remote does (execute_broadcast: all).
Returns
true on success; false if position > 100 or if crypto::create_1w_hmac() fails — no partially-populated frame is left behind on failure.

Definition at line 415 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_1w_remove_controller()

bool esphome::home_io_control::create_1w_remove_controller ( IoFrame & f,
const uint8_t src[NODE_ID_SIZE],
DeviceType target_type,
uint16_t sequence,
const uint8_t controller_key[AES_KEY_SIZE] )

Build a 1W remove-controller frame (CMD 0x39).

Build a 1W remove-controller frame (CMD 0x39) that un-registers this identity from every device of target_type currently in association mode — the un-enroll counterpart to create_1w_add_controller(), and (the 0x39 → 0x30 flow real remotes send) also the prelude OneWayTransmitter::send_enrollment() fires immediately before it.

See proto_commands.h for the full contract, including the

Warning
that no vector pins this command's MAC span.

Declared payload (9 bytes), shape: data[1]=0x00 + sequence[2] + mac[6]. The MAC sits inside the declared payload here — the opposite placement from create_1w_add_controller() above, whose MAC is an out-of-length trailer because its longer payload has no room left in CTRL0's 5-bit length field. f.has_mac stays false.

The MAC span is cmd + data (2 bytes) — the same "everything before the sequence" shape CMD 0x00's span follows, though spans are command-specific and do not generalise on their own (see create_1w_hmac()'s @warning; CMD 0x30's span is cmd + enc_key, a different rule). This one is verified against real captures, not merely plausible: every 0x39 frame in tests/corpus/captures/enrollment/somfy_smoove_enrollment_add_and_remove_controller_sx1276.yaml verifies under this exact span, and scripts/corpus/validate.py re-checks that on every corpus validation run — a regression here would fail make corpus-validate, not just look wrong.

Parameters
fIoFrame to populate.
srcOur 3-byte controller node address (the 1W controller identity's node_id).
target_typeDevice class to address; encoded via encode_broadcast_address().
sequence2-byte rolling sequence for this transmission (big-endian on wire); the caller's identity/sequence store owns incrementing and persisting this, not this builder.
controller_key16-byte key held by the identity being removed — the same key create_1w_execute_command()/create_1w_add_controller() would use for this identity.
Returns
true on success; false if crypto::create_1w_hmac() fails — no partially-populated frame is left behind on failure.

Definition at line 495 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_challenge_req() [1/2]

bool esphome::home_io_control::create_challenge_req ( IoFrame & f,
const uint8_t * dst,
const uint8_t * src )

Build a challenge request (0x3C) containing 6 random bytes.

Used when WE need to authenticate an incoming request from a device.

Used when we need to authenticate an incoming request from a device.

Parameters
fIoFrame to populate.
dstTarget device node ID (device we're challenging).
srcController node ID.
Returns
true on success.

Definition at line 1002 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_challenge_req() [2/2]

bool esphome::home_io_control::create_challenge_req ( IoFrame & f,
const uint8_t * dst,
const uint8_t * src,
const uint8_t challenge[HMAC_SIZE] )

Build a challenge request (0x3C) using a caller-supplied challenge.

Build a challenge request (0x3C) using a caller-supplied challenge instead of generating a fresh one internally.

See proto_commands.h.

Framed exactly like the device-role builder below (0E 00, no START, no LOW_POWER) — matches every 0x3C observed on air across multiple actuators and vendors. Kept as a separate entry point from create_challenge_req_device_role() because the call sites and rationale differ (inbound authentication vs the key-extraction responder). If devices ever stop answering our challenges, START/LOW_POWER framing is the first thing to try restoring here.

The no-challenge overload above generates its own random bytes and does not expose them, which is fine for the normal inbound-auth path (the challenge is only ever needed once, to build this same frame). A caller that needs the exact bytes again later — the key-extraction responder decrypting the corresponding CMD_KEY_TRANSFER (0x32) — generates the challenge itself and passes it in here, keeping the transmitted 0x3C and the later decrypt on one source of truth. Note that responder uses create_challenge_req_device_role() below, not this overload.

Parameters
fIoFrame to populate.
dstTarget device node ID (device we're challenging).
srcOur own node ID.
challengeCaller-supplied 6-byte challenge (e.g. from crypto::generate_challenge()).
Returns
true on success.

Definition at line 996 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_challenge_req_device_role()

bool esphome::home_io_control::create_challenge_req_device_role ( IoFrame & f,
const uint8_t * dst,
const uint8_t * src,
const uint8_t challenge[HMAC_SIZE] )

Build a device-role challenge request (0x3C) — device side, used only by the key-extraction responder.

Build a challenge request (0x3C) in the device direction — used only by the key-extraction responder to challenge a foreign hub that sent us CMD_KEY_INIT (0x31).

See proto_commands.h for why the framing bits differ from the controller-role builders above.

Same command and payload as the controller-role builders above, but framed the way a real device frames it: START clear and LOW_POWER clear. Both controller-role overloads set both bits, which is correct for their direction — LOW_POWER describes the target of a controller-originated frame (a device that may be battery/solar powered, see this header's convention note), and the controller's 0x3C opens its own inbound-auth exchange. Neither holds for a device answering a hub's key-init: real devices' pairing 0x3C frames in tests/corpus/captures/pairing/somfy_izymo_dimmer_pairing_full_sx1276.yaml (0E 00 …) and tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml carry neither bit, because the frame is a continuation of the hub's already-open exchange and is addressed to a mains-powered hub — and this exact builder, exercised against a real hub, produces the identical 0E 00 shape in tests/corpus/captures/pairing/velux_kig300_pairing_key_extraction_success.yaml.

Parameters
fIoFrame to populate.
dstThe foreign hub's node ID (from the inbound 0x31's src).
srcOur advertised (throwaway) node ID.
challengeCaller-supplied 6-byte challenge, retained for the later 0x32 decrypt.
Returns
true on success.

Definition at line 1011 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_challenge_req_framed()

bool esphome::home_io_control::create_challenge_req_framed ( IoFrame & f,
const uint8_t * dst,
const uint8_t * src,
const uint8_t challenge[HMAC_SIZE],
bool start,
bool low_power )
static

Build a challenge request (0x3C) with caller-chosen framing bits.

Shared by the controller-role and device-role builders below, which differ only in those bits.

Definition at line 981 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_challenge_resp()

bool esphome::home_io_control::create_challenge_resp ( IoFrame & f,
const uint8_t * dst,
const uint8_t * src,
const uint8_t challenge[HMAC_SIZE],
const IoFrame & origin,
const uint8_t * key )

Build a challenge response (0x3D) proving we know the system key.

The HMAC is computed over [original_command_id + original_data] using the challenge.

HMAC is computed over [original_command_id + original_data] using the challenge.

Parameters
fIoFrame to populate.
dstTarget device node ID.
srcController node ID.
challenge6‑byte challenge from the device.
originOriginal request frame that triggered the challenge.
keySystem key (16 bytes).
Note
The HMAC derivation uses the challenge as IV salt; see create_hmac() in proto_crypto.h for the exact construction. This frame authenticates the controller to the device for the current exchange.
Returns
true on success.

Definition at line 1050 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_challenge_resp_device_role()

bool esphome::home_io_control::create_challenge_resp_device_role ( IoFrame & f,
const uint8_t * dst,
const uint8_t * src,
const uint8_t challenge[HMAC_SIZE],
const IoFrame & origin,
const uint8_t * key )

Build a device-role challenge response (0x3D) — device side, used only by the key-extraction responder answering a hub-issued 0x3C challenging our own 0x37.

Build a challenge response (0x3D) in the device direction — used only by the key-extraction responder to answer a hub-issued CMD_CHALLENGE_REQ (0x3C) challenging our own CMD_NODE_VERIFY_RESP (0x37).

See proto_commands.h for why the framing bits differ from the controller-role builder above.

Same transcript rule as create_challenge_resp() above (the challenged party HMACs its own preceding frame's cmd+data), but END is set: this 0x3D closes the node-verification round the hub opened with 0x36 (tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml's 8E 08 …, END set). LOW_POWER is clear because CTRL1_LOW_POWER describes a controller-originated frame's target, not the sender — irrelevant here, since this is a device-role frame sent to a mains-powered hub. Unrelated to the power-save bits of create_discover_resp()'s Multi Information Byte (KEY_EXTRACTION_DISCOVER_RESP_FLAGS), which describe the responder itself — the two bits answer different questions and are not expected to match. Neither bit is a blanket "device role" convention — real devices also send mid-exchange 0x3D frames with END clear and LOW_POWER set (somfy_oximo40_statuspoll_sx1262.yaml) — so this framing is specific to the terminal shape this feature needs, not a general device-role rule.

Parameters
fIoFrame to populate.
dstThe hub's node ID (from the inbound 0x3C's src).
srcOur advertised (throwaway) node ID.
challenge6-byte challenge from the hub's 0x3C.
originOur own preceding CMD_NODE_VERIFY_RESP (0x37) frame — its cmd+data is the transcript.
keySystem key (16 bytes).
Returns
true on success.

Definition at line 1080 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_challenge_resp_framed()

bool esphome::home_io_control::create_challenge_resp_framed ( IoFrame & f,
const uint8_t * dst,
const uint8_t * src,
const uint8_t challenge[HMAC_SIZE],
const IoFrame & origin,
const uint8_t * key,
bool end,
bool low_power )
static

Build a challenge response (0x3D) with caller-chosen framing bits.

Shared by the controller-role and device-role builders below, which differ only in those bits — see create_challenge_req_framed() above for the identical pattern on the request side.

Definition at line 1019 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_controller_start_frame()

bool esphome::home_io_control::create_controller_start_frame ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
uint8_t cmd,
bool low_power,
bool ack )
static

Build a bare controller→device start frame: no payload, START set, END clear, LOW_POWER and ACK as the caller decides.

Shared by create_discover_confirm() and create_key_init(), which are the same frame shape and differ only in command byte and in which CTRL1 bits they pass — the controller-side counterpart of create_device_terminal_ack() above.

Definition at line 891 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_device_terminal_ack()

bool esphome::home_io_control::create_device_terminal_ack ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
uint8_t cmd )
static

Build a bare device→hub terminal acknowledgement: no payload, END set, START and LOW_POWER clear.

Shared by create_key_confirm() and create_discover_confirm_ack(), which are the same frame shape and differ only in command byte — real captures of both (tests/corpus/captures/pairing/somfy_izymo_dimmer_pairing_full_sx1276.yaml's 0x33 88 00 …, velux_kux100_pairing_full.yaml's 0x2D 88 08 …, and this project's own key-extraction responder against a real hub in tests/corpus/captures/pairing/velux_kig300_pairing_key_extraction_success.yaml, both 0x2D and 0x33 as 88 00 …) show a device closing its half of a two-frame handshake this way. LOW_POWER stays clear because that bit describes the target of a controller-originated frame (see the header's convention note); a device does not flag a frame it sends to the hub as low-power.

Definition at line 874 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_discover()

bool esphome::home_io_control::create_discover ( IoFrame & f,
const uint8_t * own )

Build a discovery broadcast (0x28).

Sent to the broadcast address 0x00003B. Only devices in pairing mode (PROG button pressed) will respond.

Sent to the broadcast address; only devices in pairing mode (PROG button pressed) will respond.

Parameters
fIoFrame to populate.
ownController node ID.
Note
Destination is BROADCAST_DISCOVER (0x00003B). The device responds with CMD_DISCOVER_RESP (0x29) containing its node ID and type/subtype. The controller then switches to point‑to‑point communication for phases 2 and 3.
Returns
true on success.

Definition at line 780 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_discover_confirm()

bool esphome::home_io_control::create_discover_confirm ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
bool ack )

Build a discovery-confirm request (0x2C) — controller side, sent directly to a freshly-discovered device before the key exchange.

Build a discovery-confirm request (0x2C) — sent by a controller directly to a freshly-discovered device, before proceeding to the key exchange (0x31).

See proto_commands.h for the full contract and the real-capture cross-check.

Every real controller in this project's corpus sends 0x2C between the device's discovery response (0x29) and its own key-init (0x31): KLR200→KUX100 (tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml), TaHoma→VELUX SSL (tests/corpus/captures/discovery/velux_ssl_discovery_tahoma_pairing.yaml), and this project's own key-extraction responder answering KIG300/KLR200/KLR300/Somfy TaHoma/Somfy Connectivity Kit (tests/corpus/captures/pairing/velux_kig300_pairing_key_extraction_success.yaml and siblings). Every one of those real hubs sets CTRL1_ACK for an always-alive target (48 10) and clears it for a low-power one (48 20, CTRL1_LOW_POWER only, no ACK) — see the ack param doc for what this builder does with that split. Gated by the pairing_discover_confirm tuning knob (DiscoverConfirmMode::SKIP/SEND/SEND_WITH_ACK, tuning_config.h) — skip reproduces the sequence without this step: no 0x2C, no post-step pause.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstThe freshly-discovered device's 3-byte node ID (from its 0x29's src).
low_powerSet CTRL1_LOW_POWER. See this header's "Low-power flag and preamble handling" note above: unlike most pairing builders, this one is NOT a fixed value — pass the device's own self-reported power-save class from discovery (pairing::PairingContext::discovery_low_power).
ackSet CTRL1_ACK ("sender can handle 2W responses"). The builder is literal — it does not derive ack from low_power itself; that mirror rule belongs to the caller (PairingEngine::run_discover_confirm_step_()), which never asks for ack when low_power is true. Every corpus hub's own 0x2C sets CTRL1_ACK for an always-alive target (48 10) and clears it for a low-power one (48 20); this codebase's send default instead clears it for an always-alive target too (48 00, no corpus hub sends that shape, but a Somfy Izymo dimmer answers it with 0x2D and ignores 48 10), and send_with_ack switches to the hub-observed 48 10.
Returns
true on success.

Definition at line 907 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_discover_confirm_ack()

bool esphome::home_io_control::create_discover_confirm_ack ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst )

Build a discovery-confirm acknowledgement (0x2D) — device side, used only by the key-extraction responder.

Build a discovery-confirm acknowledgement (0x2D) — the device's answer to a hub's CMD_DISCOVER_CONFIRM (0x2C), which a hub sends directly to a freshly-discovered device before it will proceed to the key exchange.

See proto_commands.h for the full contract.

Device-side only, like create_discover_resp()/create_key_confirm() above. Its controller-role counterpart is create_discover_confirm() just above. No payload and END set, matching real devices' 0x2D in tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml; for a second independent hub, tests/corpus/captures/pairing/somfy_connectivity_kit_pairing_key_extraction_stall.yaml, where an already-paired device answers the same hub the key-extraction responder was talking to; and for this exact builder exercised against a real hub, tests/corpus/captures/pairing/velux_kig300_pairing_key_extraction_success.yaml.

Parameters
fIoFrame to populate.
ownOur advertised (throwaway) node ID.
dstDestination node ID (the hub that sent the discovery confirm).
Returns
true on success.

Definition at line 913 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_discover_resp()

bool esphome::home_io_control::create_discover_resp ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
DeviceType type,
uint8_t subtype,
uint8_t manufacturer_id,
uint8_t flags )

Build a discovery response (0x29) — device side, used only by the key-extraction responder.

Build a discovery response (0x29) — the device side of discovery, used by the key-extraction responder (see pairing_responder.h) to emulate an unpaired device.

See proto_commands.h for the full contract and the real-capture cross-check.

Almost every builder in this file speaks the controller side of the protocol; this one, create_key_confirm(), create_discover_confirm_ack(), and create_challenge_req_device_role() below speak the device side, needed only for that one reverse-role feature. Device-side frames never set CTRL1_LOW_POWER: that bit describes the target of a controller-originated frame, and a device's replies are addressed to a mains-powered hub. Payload layout matches the full 9-byte discovery response format documented at DISCOVERY_RESP_BACKBONE_OFFSET/_MANUFACTURER_OFFSET/_FLAGS_OFFSET/_TIMESTAMP_OFFSET (proto_constants.h), cross-checked against a real Somfy actuator's captured 0x29 (tests/corpus/captures/discovery/somfy_awning_discovery_lab_response.yaml): backbone address equals the device's own node ID, start+end set, low_power clear.

Parameters
fIoFrame to populate.
ownOur advertised (throwaway) node ID — used as both src and the backbone address.
dstDestination node ID (the discovering hub's real node ID, from its 0x28's src).
typeDevice type to advertise.
subtypeDevice subtype to advertise.
manufacturer_idManufacturer ID to advertise (see MANUFACTURER_* in proto_constants.h).
Note
The timestamp is a best-effort placeholder copied from a real captured device; see KEY_EXTRACTION_DISCOVER_RESP_TIMESTAMP (proto_commands.cpp).
Parameters
flagsMulti-information byte to advertise (turnaround class, power-save mode); default KEY_EXTRACTION_DISCOVER_RESP_FLAGS, the key_extraction_multi_info tuning value in use.
Returns
true on success.

Definition at line 842 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_discovery_request()

bool esphome::home_io_control::create_discovery_request ( IoFrame & f,
const uint8_t * own,
uint8_t command,
const uint8_t * dst,
bool low_power,
bool ack_capable,
bool payload_enabled,
uint8_t payload,
const uint8_t * system_key )

Build a configurable discovery request command (0x28, 0x2A, or 0x2E).

Build a discovery request with configurable command, destination, and payload.

For 0x2A (Discover SPE), the payload is a 6-byte random nonce followed by a 6-byte HMAC over the command byte alone, using that nonce as the challenge and the supplied system key — one frame carrying a whole challenge-response, which is what lets a broadcast be authenticated. This requires a valid system key; it will not work for a motor that has never been paired with this controller's key.

Supports the command codes 0x28 (DISCOVER_REQ), 0x2A (DISCOVER_SPE_REQ), and 0x2E (DISCOVER_ALT_REQ, alternate discovery). For 0x2A the payload is a 6-byte random nonce followed by a 6-byte HMAC computed over the command byte alone, using the nonce as the challenge and the supplied system key.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
commandDiscovery command code (0x28, 0x2A, or 0x2E).
dstDestination node ID (broadcast or explicit).
low_powerTrue to set the LOW_POWER flag in CTRL1.
ack_capableTrue to set the ACK flag (CTRL1_ACK) in CTRL1 for this one broadcast. Each caller decides per frame: the pairing_discovery_ack_capable tuning knob gates it for Discover & Pair's discovery broadcast (opt-in, default off — see the init_frame() doc in proto_frame.h for why CTRL1_ACK must never become an unconditional default), and the SPE roll-call's low-power frame shape (CTRL1 = LOW_POWER | ACK) always passes true.
payload_enabledTrue when the optional payload byte is enabled.
payloadOptional payload byte (only used when command requires a payload).
system_key16-byte system key; only used for 0x2A HMAC computation.
Returns
true on success; false for unsupported command or missing key for 0x2A.

Definition at line 795 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_execute_command()

bool esphome::home_io_control::create_execute_command ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
CoverCommand cmd,
bool silent )

Build a named-command execute frame (0x00) for STOP or a named position (NAMED_POSITIONS).

Build a named-command execute frame (0x00) for STOP or a named position.

FORCE_OPEN is deliberately not handled here — unlike these, it needs to know the device's wire-scale "fully open" position (0 or 100 depending on IoDevice::inverted, e.g. horizontal awnings), which this builder has no way to know. Use create_force_open() instead; see its comments for why.

The named positions are the entries of NAMED_POSITIONS (proto_device_types.h); each maps to a specific wire encoding in the 6-byte special CMD_EXECUTE payload:

  • STOP: main=0xD2, modifier=0x00
  • FAVORITE: main=0xD8, modifier=0x00
  • VENT: main=0xD8, modifier=0x03
  • PEDESTRIAN: main=0xD8, modifier=0x07 (experimental, gate openers)
  • PARTIAL: main=0xD8, modifier=0x09 (garage openers)

This cleanly separates "move to position X" from "execute named action" without overloading a single numeric parameter.

FORCE_OPEN is NOT handled here — see create_force_open() instead. Unlike these, it needs a device-specific "fully open" wire position (0 or 100 depending on inversion), which this generic dispatch has no way to supply; passing CoverCommand::FORCE_OPEN returns false.

Parameters
fIoFrame to populate.
ownController's 3‑byte node ID (source address).
dstTarget device's 3‑byte node ID (destination address).
low_powerTrue if target is battery/solar‑powered (sets CTRL1_LOW_POWER).
cmdNamed command to execute (STOP or a named position).
Returns
true on success; false for invalid/unsupported command.
Parameters
silentUse the silent (slower) travel profile. Honoured for FAVORITE only — STOP has no travel speed, and nothing has been captured for the other named positions.

Definition at line 307 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_execute_heating_level()

bool esphome::home_io_control::create_execute_heating_level ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint8_t level )

Build a heating-level execute frame (0x00). See proto_commands.h.

Build an EXECUTE (0x00) that sets a heater's heating level.

The heating level sits in the main parameter (high byte HEATING_LEVEL_MP_PREFIX, low byte the level; see proto_heating_profiles.h). The frame has the eight data bytes a vendor bridge sends with every heating level (corpus thermor_pilot_wire_interface_exchange_heating_level_execute, issue #228): ACEI 0xE7, FP11 present in the FP9-FP16 bitmap, FP11 = 0xFFFF. That evidence does not establish what FP11 = 0xFFFF means; it is sent as the bridge sends it.

Todo
Experimental: no level change has been seen on a heater's display or read back yet, so the frame stays unconfirmed on hardware until a reporter's heater does.
Parameters
fIoFrame to populate.
ownController's 3-byte node ID (source address).
dstTarget device's 3-byte node ID (destination address).
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
levelHeating level low byte (heating_level::*).
Returns
true on success; false for a level above HEATING_LEVEL_MAX.

Definition at line 405 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_execute_heating_main_parameter()

bool esphome::home_io_control::create_execute_heating_main_parameter ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint16_t main_parameter )

Build a temperature / alias execute frame (0x00), main parameter only. See proto_commands.h.

Build a main-parameter-only EXECUTE (0x00) whose main parameter is a temperature or an alias.

Same 6-byte shape as create_execute_main_parameter(); the caller encodes the value (encode_temperature_main_parameter(), or heating_prefixed_main_parameter() for an alias). Experimental: unconfirmed on hardware.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID (source address).
dstTarget device's 3-byte node ID (destination address).
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
main_parameterThe 16-bit main parameter.
Returns
true on success.

Definition at line 361 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_execute_heating_with_fp16()

bool esphome::home_io_control::create_execute_heating_with_fp16 ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint16_t main_parameter,
uint16_t fp16 )

Build a temperature / alias execute frame (0x00) carrying functional parameter 16. See proto_commands.h.

Build an EXECUTE (0x00) with a temperature or alias main parameter and functional parameter 16.

Payload: originator, ACEI, MP(2), 00 (no FP1-FP8), 01 (FP16 present, the last bit of the second bitmap), FP16 (two bytes, big-endian like the main parameter). No other functional parameter is sent. Experimental: unconfirmed on hardware.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID (source address).
dstTarget device's 3-byte node ID (destination address).
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
main_parameterThe 16-bit main parameter.
fp16FP16 value.
Returns
true on success.

Definition at line 398 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_execute_heating_with_mode_flags()

bool esphome::home_io_control::create_execute_heating_with_mode_flags ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint16_t main_parameter,
uint16_t mode_flags )

Build a temperature / alias execute frame (0x00) carrying functional parameter 9. See proto_commands.h.

Build an EXECUTE (0x00) with a temperature or alias main parameter and functional parameter 9.

Payload: originator, ACEI, MP(2), 00 (no FP1-FP8), 80 (FP9 present), FP9 as two bytes, high byte first (every functional-parameter value is two bytes wide, like the main parameter). FP9 carries the active mode (heating_mode_flags::*). No other functional parameter is sent. Experimental: unconfirmed on hardware.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID (source address).
dstTarget device's 3-byte node ID (destination address).
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
main_parameterThe 16-bit main parameter.
mode_flagsFP9 value.
Returns
true on success.

Definition at line 391 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_execute_main_parameter()

bool esphome::home_io_control::create_execute_main_parameter ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint8_t position,
bool force_open_priority = false )

Build a main-parameter-only position execute frame (0x00). See proto_commands.h.

Build a position execute frame (0x00) that carries the main parameter only: the 6-byte form originator, ACEI, MP, MP, 00, 00, with no functional-parameter block.

For devices that refuse the block create_execute_position() appends. A Wisniowski / Somfy MOTO 1000 io garage opener answers that block with RESULT_PARAMETER_UNKNOWN before any challenge, accepts a 6-byte STOP, and is opened by its own 1W remote with exactly this shape (01 43 00 00 00 00, issue #163). Sent to devices without device_feature::EXTENDED_POSITION_FRAME, and to covers set to TravelSpeed::MOTOR_DEFAULT: a VELUX KLR 200 control pad moves a window with this form, and the window then travels at its own speed rather than at a profile's (ADR 0065).

Parameters
fIoFrame to populate.
ownController's 3‑byte node ID (source address).
dstTarget device's 3‑byte node ID (destination address).
low_powerTrue if target is battery/solar‑powered (sets CTRL1_LOW_POWER).
positionTarget position 0..100 (IO scale: 0 = open, 100 = closed).
force_open_priorityTrue to send at the force-open ACEI (see create_force_open()) instead of the normal one.
Returns
true on success; false for a position above 100.

Definition at line 351 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_execute_position()

bool esphome::home_io_control::create_execute_position ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint8_t position,
bool silent = false )

Build a position execute command (0x00) to move a device to a numeric position.

Encodes a 0–100 position value into the standard 8-byte execute payload with originator, ACEI, and functional parameter fields. The wire encoding doubles the position value (0→0x00, 100→0xC8).

Parameters
fIoFrame to populate.
ownController's 3‑byte node ID (source address).
dstTarget device's 3‑byte node ID (destination address).
low_powerTrue if target is battery/solar‑powered (sets CTRL1_LOW_POWER).
positionDesired position 0–100 (0=fully open, 100=fully closed).
Returns
true on success; false if position > 100.
Parameters
silentSend the reference hub's "silent operation" extended block, which makes the motor travel more slowly. Applies to position moves only — STOP has no speed, and no capture yet shows what the toggle does to FAVORITE/VENT or tilt, so those are left alone rather than guessed at.

Definition at line 290 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_execute_position_and_tilt()

bool esphome::home_io_control::create_execute_position_and_tilt ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint8_t position,
uint8_t tilt_percent )

Build a combined position-and-tilt execute command (0x00) — setClosureAndOrientation.

Build a combined position‑and‑tilt execute command (0x00).

Sets both the cover position and the slat angle atomically in one frame, corresponding to the protocol's setClosureAndOrientation use case.

Parameters
fIoFrame to populate.
ownController node ID.
dstTarget device node ID.
low_powerTrue if target is battery/solar‑powered (sets CTRL1_LOW_POWER).
positionDesired position 0–100 (open→closed).
tilt_percent0 = fully closed, 100 = fully open.
Returns
true on success; false if position exceeds limits.

Definition at line 603 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_execute_tilt()

bool esphome::home_io_control::create_execute_tilt ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint8_t tilt_percent )

Build a tilt execute command (0x00) for devices that support slat angle control.

Build an execute‑tilt command (0x00) for slat angle control.

Parameters
fIoFrame to populate.
ownController node ID.
dstTarget device node ID.
low_powerTrue if target is battery/solar‑powered (sets CTRL1_LOW_POWER).
tilt_percent0 = fully closed, 100 = fully open.
Note
This uses the same command (0x00) as position control but with a different payload format indicating a tilt operation. The receiver infers tilt from the payload structure. Only devices with device_feature::TILT will honor this.
Returns
true on success.

Definition at line 584 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_force_open()

bool esphome::home_io_control::create_force_open ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint8_t open_position )

Build a force-open execute frame (0x00): an ordinary position command to the device's wire-scale "fully open" value, sent at elevated ACEI priority (see EXECUTE_ACEI_FORCE_OPEN).

Build a force-open execute frame (0x00): move to the device's wire-scale "fully open" position at elevated ACEI priority (level 0, protection_human) instead of the usual user_high level — see EXECUTE_ACEI_FORCE_OPEN in proto_commands.cpp for why priority elevation, not a special position byte, is the protocol's real mechanism for getting past an environmental soft lock.

Takes the target position explicitly rather than assuming 0, because "fully open" is not always wire-position 0: IoDevice::inverted devices (e.g. horizontal awnings) have open/close swapped, so their fully-open wire position is 100. Hardcoding 0 here would, on a real inverted awning, target its already-*closed* resting position — a confirmed no-op rather than a lock bypass. The caller (execute_device_command_() in hub_operations.cpp) is responsible for resolving the correct value from the target IoDevice.

Parameters
fIoFrame to populate.
ownController's 3‑byte node ID (source address).
dstTarget device's 3‑byte node ID (destination address).
low_powerTrue if target is battery/solar‑powered (sets CTRL1_LOW_POWER).
open_positionThe device's wire-scale position value that means "fully open": 0 for ordinary devices, 100 for IoDevice::inverted ones (e.g. horizontal awnings) — the caller must resolve this from the target device, this builder does not have access to device state. Getting this wrong sends an ordinary, harmless-looking position command to the device's already-resting position instead of moving it anywhere.
Returns
true on success.
Note
The elevated-priority override has not yet been confirmed against a real active environmental lock.

Definition at line 342 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_general_info3()

bool esphome::home_io_control::create_general_info3 ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power )

Build a CMD_GET_GENERAL_INFO3 (0x58) request.

No payload — delegates to the shared create_no_payload_request() helper, the shape every device-info read uses.

No payload.

Shares the no-payload addressed-request shape with create_get_name(), create_get_info1() and create_get_info2() via a common file-local helper (proto_commands.cpp).

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
low_powerTrue if target is battery/solar-powered (sets CTRL1_LOW_POWER).
Returns
true on success.

Definition at line 539 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_get_info1()

bool esphome::home_io_control::create_get_info1 ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power )

Build a CMD_GET_INFO1 (0x54) request. No payload. See proto_commands.h for the evidence note.

Build a CMD_GET_INFO1 (0x54) request.

No payload.

The request is field-observed — a real hub sends it — but no 0x55 answer has ever been captured. Diagnostic probe only; nothing in this codebase decodes a 0x55.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
low_powerTrue if target is battery/solar-powered (sets CTRL1_LOW_POWER).
Returns
true on success.

Definition at line 544 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_get_info2()

bool esphome::home_io_control::create_get_info2 ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power )

Build a CMD_GET_INFO2 (0x56) request. No payload. See proto_commands.h for the evidence note.

Build a CMD_GET_INFO2 (0x56) request.

No payload.

The request has never been captured on air; it rests on the even=request / odd=answer pairing rule, and on the fact that a 0x57 answer is captured (carrying a leading ASCII reference string plus the packed type/subtype bytes hub_status.cpp already reads at offsets 10/11).

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
low_powerTrue if target is battery/solar-powered (sets CTRL1_LOW_POWER).
Returns
true on success.

Definition at line 549 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_get_name()

bool esphome::home_io_control::create_get_name ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power )

Build a get-name request (0x50).

The device responds with its stored display name.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
low_powerTrue if target is battery/solar-powered (sets CTRL1_LOW_POWER).
Returns
true on success.

Definition at line 533 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_get_status()

bool esphome::home_io_control::create_get_status ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power )

Build a get-status request (0x03). The device responds with its current position.

Build a get‑status request (0x03).

The device responds with its current position.

Parameters
fIoFrame to populate.
ownController's 3‑byte node ID.
dstTarget device's 3‑byte node ID.
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
Returns
true on success.

Definition at line 529 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_get_status_extended()

bool esphome::home_io_control::create_get_status_extended ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint8_t selector,
uint8_t block,
uint8_t function_id )

Build an extended CMD_PRIVATE (0x03) request with a selector/block pair — the shape real hubs use for both the tilt block (selector STATUS_TILT_SELECTOR) and the field-observed selector 0x80 (tests/corpus/captures/probe/multi_somfy_probe_extended_private_both_selectors.yaml), which this codebase has never decoded.

Build an extended CMD_PRIVATE (0x03) request with a selector/block pair.

block is the field-observed name for the byte that varies (0x00/0x01) for selector 0x80; create_get_status_tilt() below is this builder frozen at selector = STATUS_TILT_SELECTOR, block = 0x01. function_id defaults to PRIVATE_GET_POSITION_STATUS (0x03) — the only value ever seen on air in this shape; other values are diagnostic probes into an undecoded function ID.

The shape real hubs use for both the tilt block (selector STATUS_TILT_SELECTOR: FPI1 bit 5 = FP3, see proto_constants.h) and the field-observed selector 0x80 (tests/corpus/captures/probe/multi_somfy_probe_extended_private_both_selectors.yaml), which this codebase has never decoded. create_get_status_tilt() below is this builder frozen at selector = STATUS_TILT_SELECTOR, block = 0x01.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
selectorExtended-status selector byte (data[1]).
blockSelector-specific block/index byte (data[2]) — the field-observed name for this byte is "N" for selector 0x80, where the corpus has only ever observed 0x00/0x01.
function_idCMD_PRIVATE function ID at data[0]. Defaults to PRIVATE_GET_POSITION_STATUS (0x03) — the only value ever observed on air in this 4-byte extended shape. Other values are diagnostic probes: the shape 03 80 00 00 / 03 80 01 00 is field-observed from a real hub, but no other function ID has ever been seen in it.
Returns
true on success.

Definition at line 632 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_get_status_tilt()

bool esphome::home_io_control::create_get_status_tilt ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power )

Build a tilt-aware get-status request (0x03) that returns the extended 16-byte tilt payload.

Build a tilt‑aware get‑status request (0x03 with extended payload) that returns the 16‑byte tilt block in the response.

Parameters
fIoFrame to populate.
ownController node ID.
dstTarget device node ID.
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
Returns
true on success.

Definition at line 749 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_identify()

bool esphome::home_io_control::create_identify ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power )

Build an authenticated device-identify request (0x1E).

Build an authenticated device-identify request (0x1E) that makes a device physically identify itself (brief jog / flash).

Parameters
fIoFrame to populate.
ownController's 3-byte node ID (source address).
dstTarget device's 3-byte node ID (destination address).
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
Note
The device may reply with CMD_ERROR_RESP instead of a dedicated identify response; callers should treat that reply as an expected, non-fatal outcome rather than a failure.
Returns
true on success.

Definition at line 562 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_key_confirm()

bool esphome::home_io_control::create_key_confirm ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst )

Build a key-confirm frame (0x33) — device side, used only by the key-extraction responder.

Build a key-confirm frame (0x33) — the device's acknowledgement that it received and installed the system key, sent after decrypting a CMD_KEY_TRANSFER (0x32).

See proto_commands.h for the full contract and the real-capture cross-check.

Device-side counterpart to create_key_transfer(); used only by the key-extraction responder (see create_discover_resp() above for why this direction exists at all). No payload and END set, matching real devices' 0x33 in tests/corpus/captures/pairing/somfy_izymo_dimmer_pairing_full_sx1276.yaml, tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml, and (this project's own key-extraction responder against a real hub) tests/corpus/captures/pairing/velux_kig300_pairing_key_extraction_success.yaml — 0x33 closes the key-exchange sequence that CMD_KEY_INIT (0x31) opened with START.

Parameters
fIoFrame to populate.
ownOur advertised (throwaway) node ID.
dstDestination node ID (the hub that sent the key transfer).
Returns
true on success.

Definition at line 883 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_key_init()

bool esphome::home_io_control::create_key_init ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst )

Build a key-init request (0x31) to start the pairing key exchange with a discovered device.

Build a key‑init request (0x31) to start pairing key exchange with a discovered device.

Parameters
fIoFrame to populate.
ownController node ID.
dstDiscovered device node ID.
Returns
true on success.

Definition at line 957 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_key_transfer()

bool esphome::home_io_control::create_key_transfer ( IoFrame & f,
IoFrame & old_frame,
const uint8_t * dst,
const uint8_t * src,
const uint8_t key[AES_KEY_SIZE],
const uint8_t challenge[HMAC_SIZE] )

Build a key-transfer frame (0x32) containing the system key encrypted with the transfer key.

Build a key‑transfer frame (0x32) containing the system key encrypted with the transfer key.

Parameters
fIoFrame to populate.
old_frameThe key‑init frame (used to derive the encryption IV).
dstTarget device node ID.
srcController node ID.
keyThe 16‑byte system key to transfer.
challenge6‑byte challenge received from device in its 0x3C response.
Note
The system key is obfuscated via the XOR‑AES construction in crypt_key(). The transfer key (hardcoded in proto_frame.h) is the same for all IO‑Homecontrol devices worldwide; its purpose is to protect the system key in transit during initial pairing. Once transferred, the device uses the system key for all subsequent authenticated exchanges.
Returns
true on success.

Definition at line 964 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_launch_key_transfer()

bool esphome::home_io_control::create_launch_key_transfer ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
const uint8_t challenge[HMAC_SIZE] )

Build a key transfer request (0x38). See proto_commands.h for the frame shape.

Build a key transfer request (0x38, "pull") asking a discovered device for its key.

START set, END clear, CTRL1_LOW_POWER clear, CTRL1 bit 2 (CTRL1_PRIORITY) set: the shape vendor controllers use for this request. An Atlantic AGILIA IO radiator answers it with a CMD_KEY_TRANSFER (0x32) (issue #165); recover_key_from_pull() decrypts that.

Parameters
fIoFrame to populate.
ownController node ID.
dstDiscovered device node ID.
challenge6 random bytes, kept by the caller for the decrypt.
Returns
true on success.

Definition at line 927 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_limitation_status_read()

bool esphome::home_io_control::create_limitation_status_read ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
LimitationType type,
bool low_power )

Build the limitation read (see the header for the evidence and the read-only guarantee).

Build the read of a device's resulting limitation: an unauthenticated CMD_LIMITATION_STATUS_REQ (0x25) with data {selector, 0x00, 0x00}, byte for byte what a VELUX KLF 200 gateway was heard sending (issue #98; public reference: KLF 200 API specification v3.18 section 10.5).

The reply's value layout is confirmed under rain on a VELUX window (minimum 93 %); its originator and remaining-time bytes read 00 even then, so they stay unverified. Read-only: the selector is an enum with two values, so this builder cannot produce a limitation write.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
typeWhich resulting limit to read.
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
Returns
true on success.

Definition at line 674 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_node_verify_resp_device_role()

bool esphome::home_io_control::create_node_verify_resp_device_role ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst )

Build an address response (0x37) — device side, used only by the key-extraction responder.

Build a node verification response (0x37) — device side, answering a hub's CMD_NODE_VERIFY_REQ (0x36).

See proto_commands.h for the full contract, including why the payload (our own node ID, not a separately-tracked backbone identity) is a known simplification rather than a confirmed match to real-device behavior.

TODO(hardware-verify): ctrl1 is 0x00 here (no CTRL1_PRIORITY), matching every other device-role builder in this file, but the one real capture of this command (tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml line 87) shows CTRL1_PRIORITY set on the KLR200's 0x37. In that same capture the device also mirrors CTRL1_PRIORITY from whatever the hub's preceding request set, and its 0x36 request is the one request in the whole exchange that sets PRIORITY — but every other device-role builder here also emits ctrl1=0 against frames that same capture shows with reserved/other bits set, and those builders are hardware-confirmed working (issue #45's own captures), so this one bit's necessity is unproven rather than known missing. Not mirroring the request's PRIORITY bit until a second real capture settles it either way.

Payload is our own advertised node ID — the only identity this emulated device has to offer — the same value create_discover_resp() reports at DISCOVERY_RESP_BACKBONE_OFFSET. CorpusDeviceRoleBuilders.NodeVerifyRespPayloadMatchesOwnDiscoverRespBackboneAddress (tests/corpus/corpus_device_role_builder_test.cpp) pins that these two builders agree with each other, not that this matches a real device's own backbone value: the one real capture of this exchange (tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml) shows a genuine device whose 0x37 payload — a persistent identity the io-homecontrol wire format tracks separately from a device's node/session address — does NOT equal that device's own node ID. Our emulated device only ever generates one identity per arm cycle, so it structurally cannot reproduce a real device's separate backbone value; whether any real hub requires the two to differ, or even inspects this field at all rather than treating it as informational, is unconfirmed. See docs/key-extraction.md's "Known limitations" for the field-facing version of this note. Also unverified: the full CTRL1 framing. This builder's ctrl1 is 0x00 (see init_frame() call in the .cpp), but the KLR200 capture's 0x37 carries CTRL1_PRIORITY set. See the TODO(hardware-verify) on the .cpp definition for why that bit is not mirrored here. No controller-role counterpart exists to share with: this codebase has never sent 0x36, so there is no analogous controller-role code that receives a 0x37 to keep in sync with.

Parameters
fIoFrame to populate.
ownOur advertised (throwaway) node ID — used as both src and the payload.
dstDestination node ID (the hub that sent the node verification request, from its 0x36's src).
Returns
true on success.

Definition at line 1070 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_private2_read()

bool esphome::home_io_control::create_private2_read ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
uint8_t modifier,
bool long_form,
bool low_power )

Build a CMD_PRIVATE2 (0x0C) request in either of the two field-observed shapes.

The payload is CMD_EXECUTE's POS_FAVORITE/POS_VENT_MODIFIER stored-position selector with the execution prefix stripped — modifier is that same selector byte (e.g. POS_VENT_MODIFIER for vent).

low_power is a separate parameter, not derived from long_form: the two captured fixtures (tests/corpus/captures/probe/multi_somfy_probe_private2_{long_form,short_form}.yaml) do carry CTRL1_LOW_POWER set on the long-form request and clear on the short-form ones, but that tracks the target device's power class in each capture (a solar shutter vs. a mains-powered switch), not the payload shape — the same relationship every other device-addressed builder in this file has to low_power. Deriving it from long_form would silently clear the flag on a short-form probe sent to a solar device.

The payload is CMD_EXECUTE's POS_FAVORITE/POS_VENT_MODIFIER stored-position selector with the execution prefix stripped. low_power is an explicit parameter like every other device-addressed builder in this file, not derived from long_form: the two captures this builder is pinned against happen to carry CTRL1_LOW_POWER set on the long-form request and clear on the short-form one, but that tracks each capture's target device's power class (solar shutter vs. mains switch), not the payload shape — see proto_commands.cpp for the full reasoning.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
modifierThe POS_FAVORITE/POS_VENT_MODIFIER-family selector byte to read back.
long_formTrue for the 6-byte long form (data = POS_UNKNOWN, 0x00, 0x80, POS_FAVORITE, modifier, 0x00); false for the 4-byte short form (data = POS_FAVORITE, modifier, 0x00, 0x00).
low_powerTrue if target is battery/solar-powered (sets CTRL1_LOW_POWER).
Returns
true on success.

Definition at line 764 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_private_function()

bool esphome::home_io_control::create_private_function ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint8_t function_id,
uint8_t sub_index )

Build a CMD_PRIVATE (0x03) request for an arbitrary function ID.

function_id = 0x06/0x09 reads battery state; create_get_status() below is this builder frozen at function_id = PRIVATE_GET_POSITION_STATUS (0x03), the only function ID this codebase has ever captured on its own wire.

function_id = 0x06 (battery-status) or 0x09 (battery-state) is reported elsewhere to read the CMD_PRIVATE_RESP reply as data[1]==0x60 => battery/solar powered, value = data[2]<<8|data[3]. Unverified here – both devices this project has probed are mains-powered and answered data[1]==0x00 (tests/corpus/captures/probe/somfy_awning_probe_private_fn_lr1121.yaml, tests/corpus/captures/probe/somfy_izymo_dimmer_probe_private_fn_lr1121.yaml). create_get_status() below is this builder frozen at function_id = PRIVATE_GET_POSITION_STATUS (0x03), the only function ID this codebase has ever captured on its own wire.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER; the exchange layer then also uses the long wake-up preamble).
function_idPrivate function ID (data[0] of the CMD_PRIVATE payload).
sub_indexSecond payload byte (data[1]). Defaults to 0x00 — the only value any project has ever transmitted in this 3-byte form, and what every existing capture of ours contains. A non-zero value is a diagnostic probe into an undecoded parameter encoding: reference material describes a two-field (main parameter, functional parameter) addressing scheme here, but the field->byte mapping is not known — at least three encodings fit every payload observed to date equally well, because every one of them has both fields zero.
Returns
true on success.

Definition at line 519 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_set_config1()

bool esphome::home_io_control::create_set_config1 ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst )

Build a set-config command (0x6F) to tell the device to automatically send status updates when controlled by any remote (not just us).

Build a set‑config command (0x6F) telling the device to automatically send status updates when controlled by any remote.

Not all devices support this.

Parameters
fIoFrame to populate.
ownController node ID.
dstTarget device node ID.
Note
This configures the device to emit CMD_STATUS_UPDATE (0x71) frames whenever it is controlled by any remote (including the paired controller). This enables HA to receive unsolicited position updates. The controller must still authenticate the status update using the inbound auth flow (hub_exchange.h).
Todo
Confirm on real hardware which device families actually honor this SetConfig1 payload and emit unsolicited status updates after pairing.
Returns
true on success.

Definition at line 1098 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_set_name()

bool esphome::home_io_control::create_set_name ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
const uint8_t payload[DEVICE_NAME_WRITE_PAYLOAD_SIZE] )

Build an authenticated set-name request (0x52) using a fixed zero-padded Latin-1 payload.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
payloadPre-validated fixed payload produced by encode_device_name_payload().
Returns
true on success.

Definition at line 553 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_status_mp_fp_read()

bool esphome::home_io_control::create_status_mp_fp_read ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
uint8_t fpi1,
uint8_t fpi2 )

Build the KLF-style status read of the main parameter plus functional parameters (see the header for the evidence).

Build the status read of the main parameter plus functional parameters: an unauthenticated CMD_PRIVATE (0x03) request with function ID 0x01, the FPI1 bitmap (bit 7 = FP1 ... bit 0 = FP8), one "current value" status type per FPI1 bit, the FPI2 bitmap (bit 7 = FP9 ... bit 0 = FP16) and one status type per FPI2 bit (spec order: each type run follows its bitmap).

Read-only and unauthenticated.

With the default arguments this is the exact 10 bytes a VELUX KLF 200 gateway sends (public reference: KLF 200 API specification v3.18 §10.3.1.4; the same bytes were heard on air from a live KLF 200 at 868 MHz and 2.4 GHz). A non-default FPI2 follows the same layout but has not been seen on air. The reply layout is not decoded here.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
fpi1FP1-FP8 bitmap; default requests FP1-FP7.
fpi2FP9-FP16 bitmap; default requests none.
Returns
true on success; false (nothing built) when no parameter is requested or more than STATUS_MP_FP_PARAM_COUNT are.

Definition at line 651 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_status_update_resp()

bool esphome::home_io_control::create_status_update_resp ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst )

Build a status-update acknowledgment (0x72).

Build a status‑update acknowledgment (0x72).

Sent after authenticating a device's status update. The response is sent on all 3 channels to ensure the device receives it.

Sent after authenticating a device's status update; broadcast on all 3 channels for reliability.

Parameters
fIoFrame to populate.
ownController node ID.
dstDevice node ID that sent the update.
Returns
true on success.

Definition at line 1087 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_subscribe_event()

bool esphome::home_io_control::create_subscribe_event ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
uint16_t event_id,
bool low_power )

Build the subscribe request (see the header for the allowlist).

Build a subscribe-to-event request (0x73) for the subscribe_event diagnostic probe.

The payload is 01 <event hi> <event lo>. This is the one builder here whose request changes state in the device (a stored subscription), so it accepts only the two allowlisted ids, EVENT_BATTERY_LEVEL_CHANGE and EVENT_LOCAL_SENSOR, and returns false for anything else.

Todo
The request shape is unconfirmed on hardware: no device has been captured answering it.
Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
event_idEVENT_BATTERY_LEVEL_CHANGE or EVENT_LOCAL_SENSOR.
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
Returns
true on success, false for an event id outside the allowlist.

Definition at line 702 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_transfer_open()

bool esphome::home_io_control::create_transfer_open ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
const TransferObject & object,
bool low_power )

Build the transfer-open request (see the header for the allowlist and the byte layout).

Build the request that opens a read of one device object (CMD_TRANSFER_OPEN 0x46) for the read_object diagnostic probe (ADR 0062).

The payload is <ORIGINATOR_SAAC> <class> 00 <id hi> <id lo> 00 00 00 00, the form Somfy controllers use to read a whole object; Somfy actuators answered it for class-0 objects (issue #156). Only the class/id pairs in TRANSFER_READABLE_OBJECTS are accepted, so this builder cannot name any other object, and there is no builder for the write twin (0x48).

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
objectThe class and id to open; must be an entry of TRANSFER_READABLE_OBJECTS.
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
Returns
true on success, false for an object outside the allowlist.

Definition at line 712 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_transfer_read()

bool esphome::home_io_control::create_transfer_read ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
uint8_t handle,
uint16_t chunk,
bool low_power )

Build the transfer-read request (see the header: exactly handle and chunk, no data).

Build the request that reads one chunk of an opened transfer (CMD_UNKNOWN4A_REQ 0x4A): <handle> <chunk hi> <chunk lo>, nothing else.

Chunk numbers count down from ceil(length/18) to 1; TRANSFER_CHUNK_CLOSE closes the transfer. The payload is fixed at these three bytes, so the write direction of the block (which carries data) cannot be built from here.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
handleHandle byte from the CMD_TRANSFER_OPEN_RESP.
chunkChunk number, or TRANSFER_CHUNK_CLOSE.
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
Returns
true on success.

Definition at line 738 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_unsubscribe_event()

bool esphome::home_io_control::create_unsubscribe_event ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
uint16_t event_id,
bool low_power )

Build the unsubscribe request (see the header: the action value is untested).

Build an unsubscribe request for the unsubscribe_event diagnostic probe: CMD_SUBSCRIBE_EVENT (0x73) with payload 00 <event hi> <event lo>, the subscribe request with its action byte cleared.

Same two-id allowlist as create_subscribe_event().

Todo
The action value 0x00 is a guess from the frame's shape: no device has been seen to stop reporting after it. It exists to test whether a subscription can be undone.
Parameters
fIoFrame to populate.
ownController's 3-byte node ID.
dstTarget device's 3-byte node ID.
event_idEVENT_BATTERY_LEVEL_CHANGE or EVENT_LOCAL_SENSOR.
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER).
Returns
true on success, false for an event id outside the allowlist.

Definition at line 707 of file proto_commands.cpp.

Here is the call graph for this function:

◆ create_write_private()

bool esphome::home_io_control::create_write_private ( IoFrame & f,
const uint8_t * own,
const uint8_t * dst,
bool low_power,
const uint8_t * payload,
size_t payload_len )

Build a generic CMD_WRITE_PRIVATE (0x20) frame around a caller-supplied payload — the one builder behind every heating/climate function.

Build a generic CMD_WRITE_PRIVATE (0x20) frame carrying a caller-supplied payload.

Framing (start=1, end=0) matches every real heater write in the corpus (atlantic_agilia_exchange_*.yaml: start, not end).

The single builder behind every heating/climate register read and write (power, setpoint, mode, presence, open-window detection, the measurement and sensor reads). The payload is produced by encode_heating_payload(), encode_heating_read() or encode_heating_write() in proto_heating.h; this builder only frames it. Framing (StartFrame=1, EndFrame=0, Protocol=0) is cross-checked against real Atlantic AGILIA heater writes (tests/corpus/captures/exchange/atlantic_agilia_exchange_setpoint_and_off_read_back.yaml: start, not end). The device answers with CMD_WRITE_PRIVATE_ACK (0x21) after an authenticated 0x3C/0x3D leg the exchange engine handles transparently.

Parameters
fIoFrame to populate.
ownController's 3-byte node ID (source address).
dstTarget device's 3-byte node ID (destination address).
low_powerTrue if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER; the exchange layer then also uses the long wake-up preamble). Per-device, from the target's YAML low_power: class (ADR 0029) — not hardcoded.
payloadPayload bytes (from encode_heating_payload(), encode_heating_read() or encode_heating_write()).
payload_lenPayload length in bytes. Rejected if 0 or > FRAME_MAX_DATA_SIZE.
Returns
true on success; false if payload_len is out of range or set_cmd() fails.

Definition at line 573 of file proto_commands.cpp.

Here is the call graph for this function:

◆ decode_1w_add_controller()

OneWayAddControllerDecodeError esphome::home_io_control::decode_1w_add_controller ( const IoFrame & frame,
OneWayAdoptedKey & out )

Decode a CMD_ONEWAY_ADD_CONTROLLER (0x30) frame into a recovered controller identity.

A 1W device broadcasts this frame while its key-copy gesture is active, handing its network's wrapped system key to whichever controller is listening (see CMD_ONEWAY_ADD_CONTROLLER's Doxygen in proto_constants.h). Three facts a future reader cannot re-derive from the code alone:

  • The AES key that wraps the payload is the public TRANSFER_KEY (proto_constants.h), not the recovered system key — the system key is the output of unwrapping, not an input.
  • The unwrap IV is derived only from the sender's own node address (frame.src), which is plaintext in the frame header — no secret or challenge is needed to begin unwrapping.
  • The wrap is self-inverse (crypto::crypt_1w_key()'s XOR-with-AES-keystream construction), so the same call that would encrypt a plaintext key for transmission also decrypts an overheard ciphertext; there is no direction flag to get wrong.

Validates before decrypting rather than decrypting garbage: the 1W protocol bit must be set, the command must be CMD_ONEWAY_ADD_CONTROLLER, and the declared payload must be exactly the expected 20 bytes (enc_key[16] + man_id[1] + data[1] + sequence[2]) — any mismatch is rejected before crypto::crypt_1w_key() is ever called.

When frame.has_mac is true (the out-of-length MAC trailer modeled by IoFrame::mac, see proto_frame.h), the MAC is verified under the recovered key with span cmd + enc_key (17 bytes) — the only span CMD_ONEWAY_ADD_CONTROLLER authenticates (see crypto::create_1w_hmac()'s @warning). A verifying MAC is strong evidence the decryption recovered the correct key: it is the decisive self-check available at adoption time, before any device is ever commanded. A frame with no MAC is not an error — real remotes send the 29-byte form without one — so decoding still succeeds and out.mac_status simply reports NOT_PRESENT.

Pure function of the frame: no logging, no timers, no I/O (ADR 0005). Never transmits.

Parameters
frameParsed IoFrame, expected to be a CMD_ONEWAY_ADD_CONTROLLER 1W frame.
outOutput: recovered controller identity. Only meaningfully populated when the return value is NONE; left default-constructed (zeroed) otherwise.
Returns
NONE on success; otherwise the specific validation/decode failure.
Warning
out carries real key material once populated. See OneWayAdoptedKey's warning.

Definition at line 425 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_1w_frame()

OneWayFrameInfo esphome::home_io_control::decode_1w_frame ( const IoFrame & frame)

Decode a parsed 1W frame into a structured OneWayFrameInfo.

Extracts target device type from the broadcast address. For execute/activate-mode commands, also decodes originator, ACEI priority, and position/command intent.

Parameters
frameParsed IoFrame with CTRL0_PROTOCOL_1W set.
Returns
Populated OneWayFrameInfo.

Definition at line 358 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_1w_main_intent()

void esphome::home_io_control::decode_1w_main_intent ( uint8_t main0,
uint8_t main1,
char * out,
size_t out_size )

Decode the "main" position/command bytes from a 1W execute payload.

1W remotes encode their command intent in a 2-byte main field:

  • main[0]: position (0–200 mapped to 0–100%), or a special command code.
  • main[1]: modifier byte (0x03 = ventilation for POS_FAVORITE).
Parameters
main0First main byte (position or special code).
main1Second main byte (modifier).
outBuffer to write the decoded string into (e.g., "CLOSE", "position 75%").
out_sizeSize of the output buffer.

Definition at line 285 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_battery_level_event()

bool esphome::home_io_control::decode_battery_level_event ( const IoFrame & frame,
uint8_t & out )

Decode the level of an EVENT_BATTERY_LEVEL_CHANGE report as a percentage.

Accepts only a well-formed CMD_STATUS_UPDATE carrying EVENT_BATTERY_LEVEL_CHANGE with BATTERY_LEVEL_EVENT_DATA_LEN data byte, at most BATTERY_LEVEL_MAX_PERCENT (tests/corpus/captures/probe/somfy_rs100_probe_subscribe_event_battery_sx1262.yaml).

Todo
Percent rests on one captured value (100, with the status byte reading full).
Parameters
frameA parsed frame.
outReceives the level on success.
Returns
false for any other frame, event, shape or a value above BATTERY_LEVEL_MAX_PERCENT.

Definition at line 513 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_battery_state()

BatteryState esphome::home_io_control::decode_battery_state ( uint8_t status_byte)

Decode the coarse battery level from byte 1 of a status payload (CMD_PRIVATE_RESP to a status poll, or CMD_STATUS_UPDATE).

Parameters
status_bytedata[1] of the payload.
Returns
The level held in bits 5-6 (STATUS_BATTERY_MASK); the other bits are ignored.

Definition at line 106 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ decode_ctrl1_flags()

void esphome::home_io_control::decode_ctrl1_flags ( uint8_t ctrl1,
char * out,
size_t out_size )
inline

Render decoded CTRL1 flags into a short annotation string.

Parameters
ctrl1The CTRL1 byte from a frame.
outBuffer to write into (at least FRAME_LOG_FLAGS_BUFFER_SIZE bytes).
out_sizeSize of the output buffer.

Definition at line 36 of file log_frame.h.

Here is the call graph for this function:

◆ decode_device_name_payload()

std::string esphome::home_io_control::decode_device_name_payload ( const uint8_t * data,
uint8_t len )

Decode a device-name payload from IO-homecontrol's Latin-1 wire format into UTF-8.

Some devices prepend an extra byte before the first character and many pad the payload with trailing 0x00 or 0x20 bytes. This helper normalizes those quirks and truncates the result to fit DEVICE_NAME_BUFFER_SIZE - 1 bytes when copied into IoDevice::name.

Parameters
dataRaw payload pointer from CMD_GET_NAME_RESP.
lenRaw payload length in bytes.
Returns
Normalized UTF-8 name, or an empty string when the payload carries no usable characters.

Definition at line 88 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_dhw_duration()

bool esphome::home_io_control::decode_dhw_duration ( const uint8_t * value,
size_t len,
uint8_t & duration )

A duration from the value of register 0x0000 or 0x0005: one byte.

Parameters
valueValue bytes.
lenNumber of value bytes; at least 1.
[out]durationThe byte; untouched when false is returned.
Returns
false for an empty value.

Definition at line 145 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ decode_dhw_energy_wh()

bool esphome::home_io_control::decode_dhw_energy_wh ( const uint8_t * value,
size_t len,
uint32_t & wh )

Energy since installation from the value of register 0x0004: unsigned 32-bit big-endian, Wh.

Parameters
valueValue bytes.
lenNumber of value bytes; at least 4.
[out]whEnergy in Wh; untouched when false is returned.
Returns
false for a value shorter than four bytes.

Definition at line 136 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ decode_dhw_heating_state()

bool esphome::home_io_control::decode_dhw_heating_state ( const uint8_t * value,
size_t len,
DhwHeatingState & state )

What is heating, from the value of register 0x0021.

Parameters
valueValue bytes.
lenNumber of value bytes; at least 5.
[out]stateThe decoded state; untouched when false is returned.
Returns
false for a value shorter than five bytes.

Definition at line 127 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ decode_dhw_mode()

bool esphome::home_io_control::decode_dhw_mode ( const uint8_t * value,
size_t len,
DhwMode & mode )

Operating mode from the value of register 0x0006.

Parameters
valueValue bytes.
lenNumber of value bytes; at least 1.
[out]modeThe mode; untouched when false is returned.
Returns
false for an empty value or a byte other than 0, 1 or 2.

Definition at line 120 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ decode_dhw_operating_status()

bool esphome::home_io_control::decode_dhw_operating_status ( const IoFrame & reply,
DhwOperatingStatus & status )

Capabilities and operating-mode state from the reply to the status read DHW_STATUS_FPI1 / DHW_STATUS_FPI2.

Layout of the data: [status][b1][MP 2][FPI1][FPI2][one 2-byte value per FPI2 bit, bit 7 first]. The echo must carry FPI1 00 and both the FP15 and FP16 bits; other bits (the heater echoes FP10 when asked for 43) are skipped. The main parameter is left to decode_dhw_status_target().

Parameters
replyThe heater's reply to the status read.
[out]statusThe decoded fields; untouched when false is returned.
Returns
false for another command, a reply too short for the values its echo announces, an FPI1 echo other than 00, or an FPI2 echo without FP15 or FP16.

Definition at line 168 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ decode_dhw_register_reply()

bool esphome::home_io_control::decode_dhw_register_reply ( const IoFrame & reply,
uint8_t index,
const uint8_t *& value,
size_t & len )

The value bytes of a reply to a family-0x00 register read.

Accepts a CMD_WRITE_PRIVATE_ACK whose data starts with 0C 60 00 <index> (or 0C 61 00 <index>, the ack of a write). The value may be empty: the heater answers an index it does not hold so.

Parameters
replyThe heater's reply.
indexThe register the request addressed.
[out]valueFirst value byte (inside reply); untouched when false is returned.
[out]lenNumber of value bytes; untouched when false is returned.
Returns
false for another command, another register or another family.

Definition at line 104 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ decode_dhw_status_target()

bool esphome::home_io_control::decode_dhw_status_target ( const IoFrame & reply,
float & celsius )

Target temperature from a status reply (CMD_PRIVATE_RESP) of a domestic hot water heater.

Parameters
replyThe heater's reply to a status read.
[out]celsiusTarget; untouched when false is returned.
Returns
false for another command, a reply too short to carry the main parameter, or a main parameter that is not a temperature.

Definition at line 162 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ decode_dhw_target()

bool esphome::home_io_control::decode_dhw_target ( uint16_t main_parameter,
float & celsius )

Target temperature from a main parameter.

Parameters
main_parameterThe main parameter as the status reply carries it.
[out]celsiusTarget, rounded to a tenth of a degree; untouched when false is returned.
Returns
false for a main parameter beyond 0xC800 (an alias such as 0xD8xx is not a temperature).

Definition at line 152 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ decode_dhw_water_temperature()

bool esphome::home_io_control::decode_dhw_water_temperature ( const uint8_t * value,
size_t len,
float & celsius )

Water temperature from the value of register 0x0017: signed 16-bit big-endian tenths.

Parameters
valueValue bytes.
lenNumber of value bytes; at least 2.
[out]celsiusTemperature; untouched when false is returned.
Returns
false for a value shorter than two bytes.

Definition at line 112 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ decode_discovery_response()

DiscoveryResponseInfo esphome::home_io_control::decode_discovery_response ( const IoFrame & frame,
IoDevice & device,
std::string & device_id )

Decode a discovery-response payload (CMD_DISCOVER_RESP 0x29 or CMD_DISCOVER_SPE_RESP 0x2B — both carry the identical DISCOVERY_RESP_FULL_SIZE layout) into device metadata.

Pure: no logging, no side effects. Extended fields (manufacturer, flags, timestamp) are returned rather than logged so each caller can present them its own way. Sets device.node_id/type/subtype/position/target/is_stopped and device_id exactly as a discovery reply implies: type/subtype come from the packed metadata bytes when present, position/target default to the unknown sentinel, and the device is assumed stopped. Every field read is guarded on frame.data_len, so a short or truncated payload degrades gracefully instead of reading past the end.

Parameters
frameParsed discovery-response frame.
deviceOutput: device record populated from the frame.
device_idOutput: hex device ID string derived from frame.src.
Returns
Extended discovery fields (manufacturer/flags/timestamp) and length flags.

Definition at line 385 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_heating_level()

bool esphome::home_io_control::decode_heating_level ( uint16_t main_parameter,
uint8_t & level )

Read a heating level out of a main parameter.

Parameters
main_parameterThe 16-bit value from a status reply.
[out]levelLevel low byte.
Returns
true when the value is a heating level (high byte HEATING_LEVEL_MP_PREFIX).

Definition at line 468 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ decode_heating_reply()

bool esphome::home_io_control::decode_heating_reply ( const IoFrame & reply,
HeatingFunction fn,
float & value )

Decode the value a heater answered a register read or write with.

Accepts a CMD_WRITE_PRIVATE_ACK (0x21) whose data starts with 0C 60 01 <reg> or 0C 61 01 <reg> for this function's register, followed by at least the register's value bytes. Anything else (another command, another register, a truncated value, a function without a typed decode such as POWER_ON) is "no value".

The value is returned in natural units: degrees Celsius for the temperature registers, the raw byte for the others. A byte above its register's range (a mode, a state, a percentage) is "no value". A raw -1 on the room-temperature register is a sensor fault: it decodes as NaN with true returned, because the heater did answer.

Parameters
replyThe device's reply frame.
fnRegister the request addressed.
[out]valueDecoded value; untouched when false is returned.
Returns
true when the reply carries a value for this register.

Definition at line 276 of file proto_heating.cpp.

Here is the call graph for this function:

◆ decode_last_command_record()

LastCommandRecord esphome::home_io_control::decode_last_command_record ( const IoFrame & frame,
uint8_t base )

Decode the last-command record at base from a status-bearing payload.

Pure, so it is testable against corpus bytes directly. An all-zero commander is reported as invalid: 00 00 00 is not a node ID any observed controller uses, so a device that pads this field rather than implementing it publishes nothing instead of a fabricated address.

Parameters
frameA CMD_PRIVATE_RESP or CMD_STATUS_UPDATE frame.
basePRIVATE_RESPONSE_LAST_COMMAND_OFFSET or STATUS_UPDATE_LAST_COMMAND_OFFSET.
Returns
The decoded record, or valid == false.

Definition at line 478 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_limitation_request()

bool esphome::home_io_control::decode_limitation_request ( const IoFrame & f,
LimitationType & type,
uint8_t & parameter_id )

Decode a limitation request (assumed layout [selector] [parameter id] [00]).

Parameters
fA parsed frame.
typeReceives the limit the request asks for.
parameter_idReceives the parameter id (data[1]).
Returns
false unless f is a CMD_LIMITATION_STATUS_REQ carrying exactly LIMITATION_REQ_DATA_SIZE bytes with one of the two known selectors.

Definition at line 564 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_limitation_status()

bool esphome::home_io_control::decode_limitation_status ( const IoFrame & f,
LimitationStatus & out )

Decode a limitation reply (assumed layout, see LimitationStatus).

Parameters
fA parsed frame.
outReceives the decoded fields on success.
Returns
false unless f is a CMD_LIMITATION_STATUS_RESP carrying exactly LIMITATION_RESP_DATA_SIZE bytes.

Definition at line 554 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_packed_device_subtype()

uint8_t esphome::home_io_control::decode_packed_device_subtype ( uint8_t type_subtype)

Decode a protocol-packed device subtype from the second metadata byte.

Parameters
type_subtypeSecond metadata byte containing subtype in bits [5:0].
Returns
Manufacturer-specific subtype.

Definition at line 129 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ decode_packed_device_type()

DeviceType esphome::home_io_control::decode_packed_device_type ( uint8_t type_msb,
uint8_t type_subtype )

Decode the 10-bit node type from two packed metadata bytes.

Parameters
type_msbFirst metadata byte.
type_subtypeSecond metadata byte containing the remaining type bits and subtype.
Returns
Decoded node type; a value the enum has no enumerator for is returned as is.

Definition at line 124 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ decode_position_report()

void esphome::home_io_control::decode_position_report ( uint16_t target_raw,
uint16_t current_raw,
bool is_stopped,
float & target,
float & position )

Decode target/current position values from a status frame.

Parameters
target_raw16‑bit raw target value.
current_raw16‑bit raw current value.
is_stoppedTrue if device reports stopped.
targetOutput target position (0–100 or UNKNOWN_POSITION).
positionOutput current position (0–100 or UNKNOWN_POSITION).

Definition at line 139 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ decode_status_update_event()

bool esphome::home_io_control::decode_status_update_event ( const IoFrame & frame,
StatusUpdateEvent & out )

Decode the event header of a CMD_STATUS_UPDATE.

Well formed means: long enough for the header, and its length byte names exactly the bytes that follow. A payload that is not well formed is left to the caller's older fixed-offset handling, so a 0x71 of a shape never captured is not reinterpreted.

Parameters
frameA parsed frame.
outReceives the header on success.
Returns
false unless frame is a well-formed CMD_STATUS_UPDATE.

Definition at line 490 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_sun_level_event()

bool esphome::home_io_control::decode_sun_level_event ( const IoFrame & frame,
uint16_t & out )

Decode the sun value of an EVENT_LOCAL_SENSOR report.

Accepts only the captured shape: a well-formed CMD_STATUS_UPDATE carrying EVENT_LOCAL_SENSOR with LOCAL_SENSOR_EVENT_DATA_LEN data bytes, the first one LOCAL_SENSOR_TYPE_SUN. The value is the big-endian 16-bit word at LOCAL_SENSOR_VALUE_OFFSET, published raw: 0 at night, 744 in sun on an RS100 Solar io (tests/corpus/captures/probe/somfy_rs100_probe_subscribe_event_daylight_sx1262.yaml).

Todo
Unit and scale unknown; confirmed on one RS100 Solar io only.
Parameters
frameA parsed frame.
outReceives the raw value on success.
Returns
false for any other frame, event or shape.

Definition at line 503 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_temperature_main_parameter()

bool esphome::home_io_control::decode_temperature_main_parameter ( uint16_t main_parameter,
float min_c,
float max_c,
float & celsius )

Decode a main parameter as a temperature over an encoding range (the inverse of encode_temperature_main_parameter()).

Parameters
main_parameterThe 16-bit value from a status reply.
min_cLower end of the device's encoding range.
max_cUpper end of the device's encoding range; must exceed min_c.
[out]celsiusThe temperature.
Returns
false when the value is above the span (an alias or a marker, not a temperature) or the range is empty.

Definition at line 459 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ decode_tilt_report()

float esphome::home_io_control::decode_tilt_report ( uint16_t tilt_raw)

Decode tilt angle from raw 16‑bit value.

Parameters
tilt_rawRaw tilt value from status frame.
Returns
Tilt percentage (0 = closed, 100 = open) or UNKNOWN_POSITION.

Definition at line 172 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ decode_transfer_block()

bool esphome::home_io_control::decode_transfer_block ( const IoFrame & f,
uint8_t handle,
uint16_t chunk,
const uint8_t *& out,
uint8_t & out_len )

Decode one CMD_UNKNOWN4A_RESP data block.

The reply echoes <handle> <chunk> and appends up to TRANSFER_BLOCK_SIZE data bytes; it is accepted only when the echo equals what was asked for, so a late reply to an earlier chunk is never taken for the current one.

Parameters
fA parsed frame.
handleHandle the request carried.
chunkChunk number the request carried.
outReceives a pointer into f.data (valid while f lives); may be empty for the close echo.
out_lenReceives the data byte count.
Returns
false unless f is a CMD_UNKNOWN4A_RESP echoing exactly handle and chunk.

Definition at line 538 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_transfer_open_reply()

bool esphome::home_io_control::decode_transfer_open_reply ( const IoFrame & f,
TransferOpenReply & out )

Decode a transfer-open reply (layout verified against a VELUX KLR 200 copy session).

Returns
false unless f is a CMD_TRANSFER_OPEN_RESP of exactly TRANSFER_OPEN_RESP_DATA_SIZE bytes.

Definition at line 525 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ decode_uart_probe()

uint8_t esphome::home_io_control::decode_uart_probe ( const uint8_t * raw,
uint8_t raw_len,
uint8_t bit_offset,
uint8_t * decoded,
uint8_t decoded_max_len )

Decode a raw UART‑encoded bitstream into bytes.

Decode a UART-encoded bitstream from the given bit offset.

IO‑Homecontrol uses a UART‑like encoding over the air: each byte is represented by a 10‑bit sequence (start bit 0, 8 data bits LSB‑first, stop bit 1). This function slides a window across the raw bitstream and attempts to recover the original bytes. It stops when the sync pattern (0 followed by 1) is not found.

Parameters
rawRaw bytes from the radio buffer.
raw_lenNumber of raw bytes available.
bit_offsetInitial bit position to start decoding (probe offset).
decodedOutput buffer for decoded bytes.
decoded_max_lenCapacity of decoded buffer.
Returns
Number of bytes successfully decoded.

Definition at line 45 of file radio_soft_phy.cpp.

Here is the call graph for this function:

◆ device_accepts_main_parameter()

bool esphome::home_io_control::device_accepts_main_parameter ( DeviceFeatures features,
uint16_t main_parameter )
constexpr

True when a device with these features accepts this main-parameter value in an EXECUTE.

Open (0x0000), closed (0xC800) and stop (0xD200) always; 0x0001-0xC7FF only with LEVELS; a named position only with its bit; anything else never.

Parameters
featuresThe device's resolved features.
main_parameterThe main parameter an EXECUTE would carry.

Definition at line 258 of file proto_device_types.h.

Here is the call graph for this function:

◆ device_accepts_register_probe()

bool esphome::home_io_control::device_accepts_register_probe ( DeviceType type,
uint8_t subtype )

May the diagnostic register-read probes (register_00, register_01) be sent to this device?

A read of a register is the request climate support already sends to register-protocol heaters, so those are allowed, and so is a water heater (device_feature::WATER_HEATER), whose readings are family-0x00 registers. Every other kind – covers, lights, switches, locks, the standard-EXECUTE heaters – is refused. This gates the probe and the register reads only; a heater's register writes stay behind device_uses_heating_registers(), a water heater's behind its WATER_HEATER bit.

Parameters
typeNode type.
subtypeSubtype.
Returns
true for a register-protocol heater or a water heater.

Definition at line 435 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ device_capability_class() [1/2]

DeviceCapabilityClass esphome::home_io_control::device_capability_class ( DeviceType type)

Map a raw IO‑Homecontrol type to the closest ESPHome/Home Assistant entity family.

Parameters
typeRaw device type.
Returns
Capability class (COVER, LIGHT, SWITCH, etc.).

Definition at line 193 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ device_capability_class() [2/2]

DeviceCapabilityClass esphome::home_io_control::device_capability_class ( DeviceType type,
uint8_t subtype )

Capability class of a (type, subtype), subtype-aware.

device_capability_class(DeviceType) answers for the type alone; this overload lets a subtype row differ from its type (for example a heater with an adjustable setpoint).

Parameters
typeNode type.
subtypeSubtype.
Returns
The resolved row's class, or DeviceCapabilityClass::UNKNOWN when the type is not in the table.

Definition at line 327 of file proto_device_types.cpp.

Here is the call graph for this function:

◆ device_capability_class_name()

const char * esphome::home_io_control::device_capability_class_name ( DeviceType type)

Get a human‑readable name for a capability class.

Parameters
typeDevice type (unused, kept for signature compatibility).
Returns
String like "cover", "light", "switch", "unknown".

Definition at line 195 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ device_features()

DeviceFeatures esphome::home_io_control::device_features ( DeviceType type,
uint8_t subtype,
DeviceModel model )

Capability bits of a (type, subtype, model).

The model row; else the exact (type, subtype) row; else, for subtype SUBTYPE_MAIN_PARAMETER_ONLY, main_parameter_only_variant() of the type's row (of no features when the type has no row); else the type's own row; else no features. Callers resolve a device's bits once, onto the device record, and read them from there; this lookup serves the resolver and checks that run before a device exists.

Parameters
typeNode type.
subtypeSubtype.
modelDeclared model (IoDevice::model); DeviceModel::NONE where no model is declared, such as discovery and the paste-ready YAML, which only know the wire type.
Returns
The feature word.

Definition at line 332 of file proto_device_types.cpp.

Here is the call graph for this function:

◆ device_has_no_motion()

bool esphome::home_io_control::device_has_no_motion ( DeviceType type,
uint8_t subtype )

Does this device never move, so a status reply must not start the movement settle loop?

Heaters commanded with an EXECUTE (heating levels or temperatures; climate profile) and exterior heating (a heat level in the position field) have no motion; their status "stopped" flag is not consulted.

Parameters
typeDevice type.
subtypeDevice subtype.
Returns
true when the device has no motion.

Definition at line 238 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ device_has_platform()

bool esphome::home_io_control::device_has_platform ( DeviceType type,
uint8_t subtype )

Can an ESPHome platform drive this (type, subtype) today?

Parameters
typeNode type.
subtypeSubtype.
Returns
true when the resolved row carries device_type_flags::HAS_PLATFORM.

Definition at line 290 of file proto_device_types.cpp.

Here is the call graph for this function:

◆ device_is_experimental()

bool esphome::home_io_control::device_is_experimental ( DeviceType type,
uint8_t subtype )

Is this (type, subtype) a kind no capture or report has confirmed on a real device?

Parameters
typeNode type.
subtypeSubtype.
Returns
true when the exact row carries device_type_flags::EXPERIMENTAL; a subtype with no row of its own is not experimental, because it behaves like its (confirmed) type.

Definition at line 322 of file proto_device_types.cpp.

Here is the call graph for this function:

◆ device_name_validation_error_description()

const char * esphome::home_io_control::device_name_validation_error_description ( DeviceNameValidationError error)

Return a human-readable explanation for a device-name validation result.

Parameters
errorValidation result.
Returns
Short message suitable for logs and Home Assistant action events.

Definition at line 179 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ device_name_validation_error_name()

const char * esphome::home_io_control::device_name_validation_error_name ( DeviceNameValidationError error)

Return a stable symbolic name for a device-name validation result.

Parameters
errorValidation result.
Returns
Uppercase symbolic name.

Definition at line 162 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ device_offers_models()

bool esphome::home_io_control::device_offers_models ( DeviceType type,
uint8_t subtype )

Does this exact (type, subtype) have model rows below it?

True where products that share the wire type need telling apart; the hub then checks a device's declared model against what the device answers (a WARN-only hint).

Parameters
typeNode type.
subtypeSubtype.
Returns
true when at least one model row names this (type, subtype).

Definition at line 273 of file proto_device_types.cpp.

Here is the call graph for this function:

◆ device_operation_profile_name()

const char * esphome::home_io_control::device_operation_profile_name ( DeviceType type,
uint8_t subtype )

Human‑readable operation profile name for a device kind.

Used for logging and diagnostics.

Parameters
typeDevice type.
subtypeDevice subtype; a cover is "cover_position_tilt" only when its (type, subtype) has a slat angle, and "cover_discrete" when it has no intermediate positions.
Returns
String such as "cover_position", "cover_position_tilt", "cover_discrete", "binary_on_off", "lock", etc.

Definition at line 260 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ device_platform_name()

const char * esphome::home_io_control::device_platform_name ( DeviceType type,
uint8_t subtype )

Name of the ESPHome platform that drives this (type, subtype).

Parameters
typeNode type.
subtypeSubtype.
Returns
"cover", "light", "switch", "lock", "climate" or "fan", or nullptr when no platform drives it yet.

Definition at line 295 of file proto_device_types.cpp.

Here is the call graph for this function:

◆ device_rssi_ema_dbm()

int16_t esphome::home_io_control::device_rssi_ema_dbm ( const IoDevice & dev)
inline

A device's smoothed RSSI in whole dBm.

Parameters
devDevice record to read.
Returns
Rounded EMA in dBm, or RSSI_UNKNOWN_DBM when no sample has been recorded yet.

Definition at line 685 of file proto_device_model.h.

Here is the call graph for this function:

◆ device_supports_binary_control()

bool esphome::home_io_control::device_supports_binary_control ( DeviceType type)

Does this device type support binary on/off control?

Parameters
typeDevice type.
Returns
true for lights and switches.

Definition at line 225 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ device_supports_climate_control()

bool esphome::home_io_control::device_supports_climate_control ( DeviceType type,
uint8_t subtype )

Can a climate entity drive this device?

True exactly when a heating profile exists for (type, subtype) (proto_heating_profiles.h): the electrical heater with an adjustable setpoint (52/1, private registers) and the plain electrical heater (52/0, heating levels). This only says a climate entity may bind; whether the private register protocol may be sent to the device is device_uses_heating_registers().

Parameters
typeDevice type.
subtypeDevice subtype.
Returns
true for devices that have a heating profile.

Definition at line 248 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ device_supports_heat_level_control()

bool esphome::home_io_control::device_supports_heat_level_control ( DeviceType type)

Does this device take a heat level as its position (exterior heating)?

Parameters
typeDevice type.
Returns
true for the HEATER_LEVEL capability class.

Definition at line 234 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ device_supports_lock_control()

bool esphome::home_io_control::device_supports_lock_control ( DeviceType type)

Does this device type support binary lock/unlock control via execute commands?

Parameters
typeDevice type.
Returns
true for lock devices.

Definition at line 230 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ device_supports_position_control()

bool esphome::home_io_control::device_supports_position_control ( DeviceType type)

Does this device type support precise position control (0–100)?

Parameters
typeDevice type.
Returns
true for cover‑family devices.

Definition at line 221 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ device_supports_status_requests()

bool esphome::home_io_control::device_supports_status_requests ( DeviceType type)

Does this device type support status request commands (0x03)?

Every kind that takes position EXECUTEs reports its state with a status request.

Parameters
typeDevice type.
Returns
true exactly where device_takes_execute_position() is.

Definition at line 258 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ device_takes_execute_position()

bool esphome::home_io_control::device_takes_execute_position ( DeviceType type)

Does this device type take position EXECUTEs (a percent, on / off, or stop in the main parameter)?

Parameters
typeDevice type.
Returns
true for covers, lights, switches, locks and heat-level heaters.

Definition at line 253 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ device_type_name()

const char * esphome::home_io_control::device_type_name ( DeviceType type)

Convert a DeviceType to a lowercase string identifier.

Parameters
typeDevice type enum.
Returns
Null‑terminated string name (e.g., "roller_shutter"), or "unknown" for a value the table does not name.

Definition at line 183 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ device_uses_heating_levels()

bool esphome::home_io_control::device_uses_heating_levels ( DeviceType type,
uint8_t subtype )

Does a climate entity drive this device with heating levels?

Parameters
typeNode type.
subtypeSubtype.
Returns
true when the profile's control path is HeatingControlPath::HEATING_LEVEL.

Definition at line 441 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ device_uses_heating_registers()

bool esphome::home_io_control::device_uses_heating_registers ( DeviceType type,
uint8_t subtype )

Does a climate entity drive this device over the private register protocol?

The gate for every CMD_WRITE_PRIVATE frame the hub sends: a heater that takes the standard EXECUTE must never be sent a register write. The control path is a fact of the (type, subtype): a model's profile keeps the path of the profile above it (checked by heating_profiles_test.cpp), so this and the other path predicates take no model.

Parameters
typeNode type.
subtypeSubtype.
Returns
true when the profile's control path is HeatingControlPath::PRIVATE_REGISTERS.

Definition at line 430 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ device_uses_heating_temperature()

bool esphome::home_io_control::device_uses_heating_temperature ( DeviceType type,
uint8_t subtype )

Does a climate entity drive this device with a temperature or an alias in the main parameter?

Parameters
typeNode type.
subtypeSubtype.
Returns
true when the profile's control path is HeatingControlPath::TEMPERATURE_EXECUTE.

Definition at line 446 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ dhw_mode_for()

bool esphome::home_io_control::dhw_mode_for ( water_heater::WaterHeaterMode mode,
DhwMode & out )

Heater mode for an ESPHome water heater mode.

Parameters
modeESPHome mode.
[out]outThe heater mode; untouched when false is returned.
Returns
false for a mode the heater does not offer (OFF, ELECTRIC, HIGH_DEMAND, GAS).

Definition at line 50 of file platform_water_heater.cpp.

Here is the call graph for this function:

◆ dhw_operating_feature_wire()

const DhwOperatingFeatureWire & esphome::home_io_control::dhw_operating_feature_wire ( DhwOperatingFeature which)

The wire facts of which: the one place each feature's FP15 bit, FP16 field and duration live.

Parameters
whichBoost or away.
Returns
Its row.

Definition at line 88 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ discover_confirm_mode_from_string()

std::optional< DiscoverConfirmMode > esphome::home_io_control::discover_confirm_mode_from_string ( const std::string & value)

Parse a pairing_discover_confirm string into the enum.

Exact, case-sensitive match against the same three strings discover_confirm_mode_to_string() produces.

Parameters
valueString to parse.
Returns
The matching mode, or std::nullopt on invalid input.

Definition at line 271 of file tuning_config.cpp.

Here is the call graph for this function:

◆ discover_confirm_mode_to_string()

std::string esphome::home_io_control::discover_confirm_mode_to_string ( DiscoverConfirmMode value)

Format a DiscoverConfirmMode value for YAML/logs.

Parameters
valueSelection to format.
Returns
"skip", "send", or "send_with_ack".

Definition at line 259 of file tuning_config.cpp.

Here is the call graph for this function:

◆ discovery_att_class()

uint8_t esphome::home_io_control::discovery_att_class ( uint8_t flags)
constexpr

Extract the ATT class field from a discovery response's Multi Information Byte.

Parameters
flagsMulti Information Byte (data[DISCOVERY_RESP_FLAGS_OFFSET]).
Returns
ATT class value (0–3); pass to att_class_name() for a human-readable string.

Definition at line 946 of file proto_constants.h.

Here is the call graph for this function:

◆ discovery_command_from_string()

std::optional< DiscoveryCommand > esphome::home_io_control::discovery_command_from_string ( const std::string & value)

Parse a discovery command string (e.g., "0x28") into the enum.

Parameters
valueString to parse.
Returns
Discovery command enum, or std::nullopt on invalid input.

Definition at line 159 of file tuning_config.cpp.

Here is the call graph for this function:

◆ discovery_command_to_string()

std::string esphome::home_io_control::discovery_command_to_string ( DiscoveryCommand cmd)

Format a discovery command enum for YAML/logs.

Parameters
cmdDiscovery command enum.
Returns
Lowercase hex string such as "0x28".

Definition at line 172 of file tuning_config.cpp.

Here is the call graph for this function:

◆ discovery_commands_to_csv()

std::string esphome::home_io_control::discovery_commands_to_csv ( const std::vector< DiscoveryCommand > & commands)

Format the ordered discovery command list as a UI/preset option string.

Matches the comma-separated select option labels (no brackets) so a boot-time snapshot round-trips to a selectable dropdown value.

Parameters
commandsOrdered list of discovery commands.
Returns
Comma-separated list such as "0x28,0x2E" (empty string when the list is empty).

Definition at line 178 of file tuning_config.cpp.

Here is the call graph for this function:

◆ discovery_commands_to_string()

std::string esphome::home_io_control::discovery_commands_to_string ( const std::vector< DiscoveryCommand > & commands)

Format the ordered discovery command list for logs.

Parameters
commandsOrdered list of discovery commands.
Returns
Bracketed comma-separated list such as "[0x28,0x2E]".

Definition at line 188 of file tuning_config.cpp.

Here is the call graph for this function:

◆ discovery_destination_to_string()

std::string esphome::home_io_control::discovery_destination_to_string ( bool destination_auto,
const uint8_t destination[NODE_ID_SIZE] )

Format a destination option for YAML/logs.

Parameters
destination_autoWhether automatic destination mapping is used.
destinationExplicit 3-byte destination when auto is false.
Returns
"auto" or a hex address such as "0x00003B".

Definition at line 209 of file tuning_config.cpp.

Here is the call graph for this function:

◆ discovery_listen_channels_from_string()

std::optional< DiscoveryListenChannels > esphome::home_io_control::discovery_listen_channels_from_string ( const std::string & value)

Parse a DiscoveryListenChannels from its YAML/select option string.

Exact, case-sensitive match against the same strings discovery_listen_channels_to_string() emits, so the two stay each other's inverse.

Parameters
valueOption string to parse.
Returns
The selection, or std::nullopt if value is not one of the options.

Definition at line 291 of file tuning_config.cpp.

Here is the call graph for this function:

◆ discovery_listen_channels_to_string()

std::string esphome::home_io_control::discovery_listen_channels_to_string ( DiscoveryListenChannels value)

Render a DiscoveryListenChannels as its YAML/select option string.

Parameters
valueSelection to render.
Returns
"skip_request" or "all".

Definition at line 281 of file tuning_config.cpp.

Here is the call graph for this function:

◆ discovery_payload_to_string()

std::string esphome::home_io_control::discovery_payload_to_string ( bool payload_enabled,
uint8_t payload )

Format a payload option for YAML/logs.

Parameters
payload_enabledWhether the optional payload is enabled.
payloadThe payload byte when enabled.
Returns
"none" or a hex byte such as "0x00".

Definition at line 217 of file tuning_config.cpp.

Here is the call graph for this function:

◆ discovery_power_save_mode()

uint8_t esphome::home_io_control::discovery_power_save_mode ( uint8_t flags)
inline

Extract the power save mode field from a discovery response's Multi Information Byte.

Parameters
flagsMulti Information Byte (data[DISCOVERY_RESP_FLAGS_OFFSET]).
Returns
Power save mode value (0–1); pass to power_save_mode_name() for a human-readable string.

Definition at line 960 of file proto_constants.h.

Here is the call graph for this function:

◆ discovery_slot_count()

uint8_t esphome::home_io_control::discovery_slot_count ( const uint8_t addr[NODE_ID_SIZE])

Number of answer slots a discovery request opens.

The low bits of a discovery address's last byte select the window: 3B = 32 slots, 3C = 64, 3D = 128, 3E = 255 (the type bits above them do not matter). Any other suffix is read as the smallest window. 3B and 3D are seen on air (corpus discovery captures; issue #215's Freebox searches 3D with a ~5.3 s repeat).

Todo
3C = 64 and 3E = 255 slots are unconfirmed: no capture of a search to either suffix yet.
Parameters
addrThree-byte destination of a discovery request.
Returns
Slot count, 32 to 255.

Definition at line 257 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ discovery_target_type()

DeviceType esphome::home_io_control::discovery_target_type ( const uint8_t addr[NODE_ID_SIZE])

Extract the device type a discovery request is restricted to.

A discovery address (suffix 0x3B, see classify_address()) carries the same ten type bits as a typed broadcast: 00 00 3B is the open 32-slot window and 00 FF FB asks only type-1023 nodes to answer.

Parameters
addrThree-byte destination of a discovery request.
Returns
The requested DeviceType, or DeviceType::UNKNOWN when the address is not a discovery address, carries no type bits, or names an undefined type.

Definition at line 251 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ effective_discovery_preamble()

uint16_t esphome::home_io_control::effective_discovery_preamble ( const TuningConfig & cfg)
inline

The preamble length (bytes) of the pairing discovery broadcast (0x28, 0x2E).

Discovery has no length of its own: it is a first frame like any other, so it goes out behind the same preamble as a directed start frame to a target of the same power class. Order of precedence:

  1. the user's pairing_discovery_preamble, when set (any value, up to the wake-up burst);
  2. low_power_wake_preamble when pairing_discovery_low_power flags the discovery frame, since a first frame carrying the low-power flag takes the wake-up preamble;
  3. normal_start_preamble, resolved from the radio driver (a short preamble that always-awake receivers lock onto, where some never lock onto the wake-up burst).

Read at send time, so a live change of normal_start_preamble moves discovery with it. PairingEngine also caps the pairing START frames that follow at this length, so the frames to the discovered device never outlast the discovery it answered.

Definition at line 393 of file tuning_config.h.

Here is the call graph for this function:

◆ effective_enrollment_classes()

std::array< DeviceType, 3 > esphome::home_io_control::effective_enrollment_classes ( const OneWayControllerIdentity & identity)
inline

The device classes this identity's 0x30 enrollment sweep will actually target.

Parameters
identityThe controller identity.
Returns
enrollment_classes when the identity overrode it (any entry non-UNKNOWN), else the manufacturer profile's list. UNKNOWN entries are skipped by the caller.

Definition at line 220 of file oneway_controller.h.

Here is the call graph for this function:

◆ effective_execute_acei()

uint8_t esphome::home_io_control::effective_execute_acei ( const OneWayControllerIdentity & identity)
inline

The ACEI byte a given identity will put on air for a 1W EXECUTE frame.

Parameters
identityThe controller identity.
Returns
execute_acei when overridden (non-zero), else the manufacturer's profile default.

Definition at line 231 of file oneway_controller.h.

Here is the call graph for this function:

◆ effective_inverted()

bool esphome::home_io_control::effective_inverted ( const OneWayControllerIdentity & identity)
inline

Whether this identity's OPEN/CLOSE/position values are mirrored on the wire.

An inverted device kind (a horizontal awning, for example) treats wire position 0 as closed, the same way the 2W cover path handles IoDevice::inverted. An explicit invert_position: always wins; with the key absent the identity's io_device_type decides.

Todo
Confirm on a second inverted-kind 1W receiver.
Parameters
identityThe controller identity.
Returns
invert_override when set, else the kind's INVERTED capability bit.

Definition at line 267 of file oneway_controller.h.

Here is the call graph for this function:

◆ effective_is_stopped()

bool esphome::home_io_control::effective_is_stopped ( const IoDevice & dev)
inline

Whether a consumer should treat the device as at rest, prediction first.

Parameters
devDevice record to read.
Returns
The predicted movement state when one stands, otherwise dev.is_stopped.

Definition at line 707 of file proto_device_model.h.

Here is the call graph for this function:

◆ effective_power_class()

OneWayPowerClass esphome::home_io_control::effective_power_class ( const OneWayControllerIdentity & identity)
inline

The burst shape this identity actually transmits with.

The one place an absent low_power: is turned into a real shape, and therefore the only function the transmitter and the boot log may ask. An explicit low_power: always wins; with the key absent the manufacturer profile decides (ADR 0041).

Parameters
identityThe controller identity.
Returns
power_class_override when set, else the manufacturer profile's default_power_class.

Definition at line 248 of file oneway_controller.h.

Here is the call graph for this function:

◆ effective_target()

float esphome::home_io_control::effective_target ( const IoDevice & dev)
inline

The main-position target a consumer should act on: the prediction when one stands, otherwise the device's own last reported target.

Parameters
devDevice record to read.
Returns
The predicted target when set, otherwise dev.target.

Definition at line 693 of file proto_device_model.h.

Here is the call graph for this function:

◆ effective_tilt()

float esphome::home_io_control::effective_tilt ( const IoDevice & dev)
inline

The slat angle a consumer should act on, prediction first.

Parameters
devDevice record to read.
Returns
The predicted tilt when set, otherwise dev.tilt.

Definition at line 700 of file proto_device_model.h.

Here is the call graph for this function:

◆ encode_broadcast_address()

void esphome::home_io_control::encode_broadcast_address ( DeviceType type,
uint8_t out[NODE_ID_SIZE] )

Encode a device type into its typed-broadcast destination address — the exact inverse of broadcast_target_type(), kept immediately beside it so the pair cannot drift apart.

The class occupies bits [9:2] and therefore spans two bytes: out[0] is always 0, out[1] = raw >> DEVICE_TYPE_LOW_BITS_SHIFT carries the high bits, and out[2] = (raw << DEVICE_TYPE_HIGH_BITS_SHIFT) | DEVICE_SUBTYPE_MASK carries the low bits plus the all-ones subtype field that makes the address a broadcast ("any subtype of this class"). A single-byte encoding looks right for classes 0–3 and silently produces the wrong class from 4 upward — e.g. the light class (0x06) encodes to 00 01 BF, not 00 00 BF. This is the addressing primitive 1W execute frames use: 1W commands a device class, never an individual node, so this is the only destination a 1W builder ever needs.

Parameters
typeDevice class to address.
outOutput: 3-byte typed-broadcast destination address.

Definition at line 270 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ encode_device_name_payload()

DeviceNameValidationError esphome::home_io_control::encode_device_name_payload ( const std::string & name,
uint8_t payload[DEVICE_NAME_WRITE_PAYLOAD_SIZE],
std::string & normalized_name )

Validate and encode a user-supplied UTF-8 device name into the fixed Latin-1 write payload.

The outbound payload is a fixed 16-byte field: up to 15 Latin-1 characters followed by trailing zero padding. Leading and trailing ASCII whitespace are trimmed before validation so write-back verification against the device's padded storage is deterministic.

Parameters
nameUser-supplied UTF-8 device name.
payloadOutput buffer for the fixed write payload (16 bytes, zero-padded on success).
normalized_nameOutput normalized UTF-8 name used for later verification/logging.
Returns
Validation status indicating success or the reason the name cannot be written.

Definition at line 107 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ encode_dhw_duration_write()

size_t esphome::home_io_control::encode_dhw_duration_write ( DhwOperatingFeature which,
uint8_t out[HEATING_PAYLOAD_MAX_SIZE] )

Encode the duration write a start of which sends before its FP16 EXECUTE: 0C 61 00 00 01 for boost (DHW_BOOST_START_DURATION), 0C 61 00 05 FF for away (DHW_AWAY_UNTIL_TURNED_OFF).

The heater has been seen to ignore the boost duration on its own; the write is part of the sequence so its acknowledgement shows what the heater keeps.

Todo
Experimental: confirm on the heater what these durations do (issue #198).
Parameters
whichBoost or away.
outOutput buffer of HEATING_PAYLOAD_MAX_SIZE bytes.
Returns
Always 5.

Definition at line 92 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ encode_dhw_mode_write()

size_t esphome::home_io_control::encode_dhw_mode_write ( uint8_t mode,
uint8_t out[HEATING_PAYLOAD_MAX_SIZE] )

Encode a write of the operating mode: 0C 61 00 06 <mode>.

Only the three values the heater has reported (DhwMode) are encoded; anything else is refused, so a byte the heater has never shown is never written to it.

Parameters
modeMode value.
outOutput buffer of HEATING_PAYLOAD_MAX_SIZE bytes.
Returns
5, or 0 for a value other than 0, 1 or 2.

Definition at line 82 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ encode_dhw_register_read()

size_t esphome::home_io_control::encode_dhw_register_read ( uint8_t index,
uint8_t out[HEATING_PAYLOAD_MAX_SIZE] )

Encode a read of one family-0x00 register: 0C 60 00 <index>.

Parameters
indexLow byte of the register number.
outOutput buffer of HEATING_PAYLOAD_MAX_SIZE bytes.
Returns
Always 4.

Definition at line 78 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ encode_dhw_target()

bool esphome::home_io_control::encode_dhw_target ( float celsius,
uint16_t & main_parameter )

Encode a target temperature as a main parameter over DHW_TARGET_MIN_C..DHW_TARGET_MAX_C.

Parameters
celsiusTarget; must be finite and within the range (nothing outside it is ever sent).
[out]main_parameterThe encoded value; untouched when false is returned.
Returns
false for a non-finite or out-of-range target.

Definition at line 97 of file proto_dhw.cpp.

Here is the call graph for this function:

◆ encode_discovery_address()

void esphome::home_io_control::encode_discovery_address ( DeviceType type,
uint8_t out[NODE_ID_SIZE] )

Encode a device type into its typed discovery address, the inverse of discovery_target_type().

The same ten type bits as encode_broadcast_address(), with the discovery suffix (ADDRESS_SUFFIX_DISCOVERY) in place of the all-subtypes mask: light (0x06) gives 00 01 BB, remote controller (1023) gives 00 FF FB.

Parameters
typeDevice class the discovery asks to answer.
outOutput: 3-byte discovery destination address.

Definition at line 279 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ encode_heating_payload()

size_t esphome::home_io_control::encode_heating_payload ( HeatingFunction fn,
float value,
uint8_t out[HEATING_PAYLOAD_MAX_SIZE] )

Encode the function the heating_control action runs: a read for a read-only register, a write for every other.

POWER_ON and MIDNIGHT_SYNC are reads (their value is ignored); the measurement registers are reads too; the others are writes as in encode_heating_write().

Parameters
fnFunction to encode.
valueFunction-specific value (ignored for a read).
outOutput buffer of HEATING_PAYLOAD_MAX_SIZE bytes.
Returns
Number of payload bytes written (4, 5 or 6), or 0 on invalid input.

Definition at line 269 of file proto_heating.cpp.

Here is the call graph for this function:

◆ encode_heating_read()

size_t esphome::home_io_control::encode_heating_read ( HeatingFunction fn,
uint8_t out[HEATING_PAYLOAD_MAX_SIZE] )

Encode a read ("get") of one register into a CMD_WRITE_PRIVATE (0x20) payload.

Parameters
fnRegister to read.
outOutput buffer of HEATING_PAYLOAD_MAX_SIZE bytes.
Returns
4 (the payload is always 0C 60 01 <reg>), or 0 for a write-only register or an out-of-range value.

Definition at line 190 of file proto_heating.cpp.

Here is the call graph for this function:

◆ encode_heating_register_read()

size_t esphome::home_io_control::encode_heating_register_read ( uint8_t reg_hi,
uint8_t reg_lo,
uint8_t out[HEATING_PAYLOAD_MAX_SIZE] )

Encode a read ("get") of an arbitrary register into a CMD_WRITE_PRIVATE (0x20) payload.

The diagnostic register_00 / register_01 probes ask a device for a register no HeatingFunction names. The request is always 0C 60 <reg_hi> <reg_lo>: the direction byte is HEATING_DIRECTION_GET and this function has no parameter that could change it, so the write direction (HEATING_DIRECTION_SET) cannot be produced from here for any input.

Todo
Register family 0x00xx is untested on hardware. Family 0x01xx is the one encode_heating_read() already sends to Atlantic radiators.
Parameters
reg_hiHigh byte of the 16-bit register number.
reg_loLow byte of the 16-bit register number.
outOutput buffer of HEATING_PAYLOAD_MAX_SIZE bytes.
Returns
Always 4 (0C 60 <reg_hi> <reg_lo>).

Definition at line 198 of file proto_heating.cpp.

Here is the call graph for this function:

◆ encode_heating_write()

size_t esphome::home_io_control::encode_heating_write ( HeatingFunction fn,
float value,
uint8_t out[HEATING_PAYLOAD_MAX_SIZE] )

Encode a write ("set") of one register into a CMD_WRITE_PRIVATE (0x20) payload.

value is interpreted by the register's encoding:

  • SET_TEMPERATURE: degrees Celsius within [HEATING_TEMP_MIN_C, HEATING_TEMP_MAX_C], encoded as a 16-bit little-endian value in tenths of a degree, round-half-away-from-zero(10 * value).
  • SET_MODE: a HeatingMode value widened to float; must be AUTO, MANUAL, PROG or OFF.
  • SET_PRESENCE, SET_OPEN_WINDOW_DETECTION: 0 or 1.
  • TOWEL_MODE: a TowelDryerMode value widened to float (0-3).
  • TEMPORARY_STATE: a HeatingTemporaryState value widened to float (0-2).
  • NIGHT_LIGHT: 0 or 1.
  • NIGHT_LIGHT_BRIGHTNESS: an integral percentage 0-100, encoded as a 16-bit little-endian value. A read-only register (including POWER_ON, MIDNIGHT_SYNC and the measurement registers) is never encoded as a write, and out-of-range, non-integral (for enum/binary encodings) or non-finite input is rejected: a byte derived from a truncating or wrapping cast is never emitted.
    Parameters
    fnRegister to write.
    valueRegister-specific value (see above).
    outOutput buffer of HEATING_PAYLOAD_MAX_SIZE bytes.
    Returns
    Number of payload bytes written (5 or 6), or 0 on invalid input or a read-only register.

Definition at line 208 of file proto_heating.cpp.

Here is the call graph for this function:

◆ encode_packed_device_type()

void esphome::home_io_control::encode_packed_device_type ( DeviceType type,
uint8_t subtype,
uint8_t & type_msb,
uint8_t & type_subtype )

Encode a DeviceType/subtype pair into the two-byte packed metadata format used by discovery responses — the inverse of decode_packed_device_type()/decode_packed_device_subtype().

Parameters
typeDevice type to encode.
subtypeManufacturer-specific subtype; only bits [5:0] are used.
type_msbOutput: first metadata byte. Only the low ten bits of type fit the wire field; higher bits are dropped.
type_subtypeOutput: second metadata byte (top 2 type bits + 6-bit subtype).

Definition at line 131 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ encode_temperature_main_parameter()

bool esphome::home_io_control::encode_temperature_main_parameter ( float celsius,
float min_c,
float max_c,
uint16_t & main_parameter )

Encode a temperature as a main parameter over an encoding range.

round((celsius - min) / (max - min) * HEATING_TEMPERATURE_MP_SPAN), clamped to the span.

Parameters
celsiusTemperature in degrees Celsius; must be finite.
min_cLower end of the device's encoding range.
max_cUpper end of the device's encoding range; must exceed min_c.
[out]main_parameterThe encoded value.
Returns
false for a non-finite temperature or an empty range; main_parameter is then untouched.

Definition at line 451 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ error_resp_code()

uint8_t esphome::home_io_control::error_resp_code ( uint8_t result)
constexpr

Result code (bits [7:3]) of a CMD_ERROR_RESP byte.

Parameters
resultResult byte from CMD_ERROR_RESP data[0].
Returns
Code 0..31; codes 1..ERROR_RESP_CODE_MAX are named (see the RESULT_* block).

Definition at line 979 of file proto_constants.h.

Here is the call graph for this function:

◆ error_resp_state()

uint8_t esphome::home_io_control::error_resp_state ( uint8_t result)
constexpr

Execution state (bits [2:0]) of a CMD_ERROR_RESP byte.

Parameters
resultResult byte from CMD_ERROR_RESP data[0].
Returns
State 0..7; 0 means the command was not executed.

Definition at line 984 of file proto_constants.h.

Here is the call graph for this function:

◆ execution_state_name()

const char * esphome::home_io_control::execution_state_name ( ExecutionState state)

Get a human-readable name for an ExecutionState.

Parameters
stateThe state to name.
Returns
Null-terminated lowercase string such as "executing", "waiting_for_power" or "undefined".

Definition at line 50 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ execution_state_of()

ExecutionState esphome::home_io_control::execution_state_of ( uint8_t status_byte)

Read the execution state out of byte 0 of a status reply.

Parameters
status_byteByte 0 of the reply's payload.
Returns
The state, or ExecutionState::UNDEFINED when bits [2:0] hold 6 or 7.

Definition at line 44 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ experimental_note()

const char * esphome::home_io_control::experimental_note ( DeviceType type,
uint8_t subtype )
static

Suffix for a log line that names a device kind no capture or report has confirmed on a real device.

Definition at line 408 of file pairing_engine.cpp.

Here is the call graph for this function:

◆ find_crc_valid_frame()

std::pair< uint8_t, uint8_t > esphome::home_io_control::find_crc_valid_frame ( const uint8_t * decoded,
uint8_t decoded_len )
static

Try to find a CRC-valid IO-Homecontrol frame within a decoded UART byte stream.

Parameters
decodedDecoded byte buffer from UART probe.
decoded_lenNumber of decoded bytes.
Returns
Frame start index and length if found, or {0, 0} if no valid frame.

Definition at line 159 of file radio_soft_phy.cpp.

Here is the call graph for this function:

◆ find_device_model_info()

const DeviceTypeInfo * esphome::home_io_control::find_device_model_info ( DeviceType type,
uint8_t subtype,
DeviceModel model )

Look up the model row with exactly this (type, subtype, model), without falling back.

Parameters
typeNode type.
subtypeSubtype.
modelModel; DeviceModel::NONE has no model row.
Returns
The model row, or nullptr when the (type, subtype) does not offer this model.

Definition at line 263 of file proto_device_types.cpp.

Here is the call graph for this function:

◆ find_device_type_info()

const DeviceTypeInfo * esphome::home_io_control::find_device_type_info ( DeviceType type,
uint8_t subtype,
DeviceModel model )

Look up the table row for a (type, subtype, model).

Parameters
typeNode type.
subtypeSubtype; a subtype with no row of its own falls back to the type's row (subtype 0).
modelDeclared model; a model the (type, subtype) has no row for falls back to the (type, subtype) row. DeviceModel::NONE for a device declared without one.
Returns
The matching row, or nullptr when the type is not in the table.

Definition at line 280 of file proto_device_types.cpp.

Here is the call graph for this function:

◆ find_exact_device_type_info()

const DeviceTypeInfo * esphome::home_io_control::find_exact_device_type_info ( DeviceType type,
uint8_t subtype )

Look up the table row with exactly this (type, subtype), without falling back.

Parameters
typeNode type.
subtypeSubtype.
Returns
The (type, subtype) row itself (never a model row), or nullptr when there is none.

Definition at line 255 of file proto_device_types.cpp.

Here is the call graph for this function:

◆ find_heating_profile() [1/2]

const HeatingProfile * esphome::home_io_control::find_heating_profile ( const IoDevice & dev)

Look up the heating profile of a device record: its type, subtype and declared model.

Parameters
devThe device.
Returns
The profile, or nullptr when no climate entity drives this device.

Definition at line 424 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ find_heating_profile() [2/2]

const HeatingProfile * esphome::home_io_control::find_heating_profile ( DeviceType type,
uint8_t subtype,
DeviceModel model )

Look up the heating profile for a (type, subtype, model).

Parameters
typeNode type.
subtypeSubtype; one with no profile of its own falls back to the type's profile (subtype 0), the same rule as find_device_type_info().
modelDeclared model. A model with a profile of its own gets it; any other falls back to the (type, subtype) profile. No default: a caller that forgot the model would silently drive a towel dryer as a radiator.
Returns
The profile, or nullptr when no climate entity drives this device.

Definition at line 410 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ find_named_position()

const NamedPosition * esphome::home_io_control::find_named_position ( CoverCommand command)
inline

The named position a command sends.

Parameters
commandCover command.
Returns
The NAMED_POSITIONS entry, or nullptr for a command that is not a named position.

Definition at line 225 of file proto_device_types.h.

Here is the call graph for this function:

◆ find_observed_preset()

const HeatingObservedState * esphome::home_io_control::find_observed_preset ( const HeatingProfile & profile,
uint8_t wire )

Find what a device-reported value of the preset register (preset_fn) shows on the card.

Parameters
profileThe device's profile.
wireValue of the profile's preset register.
Returns
The matching entry, or nullptr when the profile has no preset table or does not map the value.

Definition at line 492 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ find_observed_state()

const HeatingObservedState * esphome::home_io_control::find_observed_state ( const HeatingProfile & profile,
uint8_t wire )

Find what a device-reported wire value shows on the card.

Parameters
profileThe device's profile.
wireMode register value or level low byte.
Returns
The matching entry, or nullptr when the value is not one the profile maps.

Definition at line 488 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ find_tuning_number()

const TuningNumberParam * esphome::home_io_control::find_tuning_number ( const std::string & name)

Look up a numeric tuning parameter by name; returns nullptr if unknown.

Definition at line 292 of file tuning_registry.cpp.

Here is the call graph for this function:

◆ find_tuning_select()

const TuningSelectParam * esphome::home_io_control::find_tuning_select ( const std::string & name)

Look up a select tuning parameter by name; returns nullptr if unknown.

Definition at line 300 of file tuning_registry.cpp.

Here is the call graph for this function:

◆ find_uart_probe()

UartProbeResult esphome::home_io_control::find_uart_probe ( const uint8_t * raw,
uint8_t raw_len )

Search raw RX buffer for the best CRC-validated IO-Homecontrol frame.

Definition at line 190 of file radio_soft_phy.cpp.

Here is the call graph for this function:

◆ format_device_error_bits() [1/2]

void esphome::home_io_control::format_device_error_bits ( uint16_t errors,
const DeviceErrorBit * bits,
size_t bit_count,
char * buf,
size_t buf_size )

Expand a device-error word into a human-readable NAME|NAME|... string.

Parameters
errorsRaw device-error bitmask.
bitsThe chip's bit → name table, in output order.
bit_countNumber of rows in bits.
bufCaller-owned output buffer; always NUL-terminated on return.
buf_sizeSize of buf. Use DEVICE_ERROR_STR_SIZE.

Writes "none" when errors is zero, and appends UNKNOWN_0x%04X for any set bit with no name in the table so an undocumented flag still shows up in the log.

Definition at line 623 of file radio_soft_phy_driver_base.cpp.

Here is the call graph for this function:

◆ format_device_error_bits() [2/2]

template<size_t N>
void esphome::home_io_control::format_device_error_bits ( uint16_t errors,
const DeviceErrorBit(&) bits[N],
char * buf,
size_t buf_size )

Expand a device-error word into a human-readable NAME|NAME|... string.

Template Parameters
NRow count of the chip's table, deduced from the array so no caller counts it by hand.

Definition at line 196 of file radio_soft_phy_driver_base.h.

Here is the call graph for this function:

◆ format_device_ids()

std::string esphome::home_io_control::format_device_ids ( DeviceType type,
uint8_t subtype )

Wire values of a (type, subtype), for logs that must stay readable without the table.

Parameters
typeNode type.
subtypeSubtype.
Returns
"0xTT/S", for example "0x34/1".

Definition at line 389 of file proto_device_types.cpp.

Here is the call graph for this function:

◆ format_device_kind()

std::string esphome::home_io_control::format_device_kind ( DeviceType type,
uint8_t subtype )

Human-readable description of a (type, subtype) for logs and reports.

Parameters
typeNode type.
subtypeSubtype.
Returns
"<type name>, <subtype name>" (for example "electrical heater, with adjustable setpoint"), "<type name>, subtype N" when the subtype has no name, the bare type name for subtype 0, and "unknown type 0xNN" when the type is not in the table.

Definition at line 366 of file proto_device_types.cpp.

Here is the call graph for this function:

◆ format_device_type_diagnostic()

std::string esphome::home_io_control::format_device_type_diagnostic ( DeviceType type)

Human-readable device type string for diagnostics, including the raw numeric value.

Parameters
typeDevice type.
Returns
String such as "horizontal_awning (0x10)", or the raw hex form ("0x1A") when the type has no symbolic name. The value is printed in full ("0x3FF").

Definition at line 323 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ format_device_type_for_yaml()

std::string esphome::home_io_control::format_device_type_for_yaml ( DeviceType type)

Build the YAML value for a device's io_device_type key.

Parameters
typeDevice type.
Returns
A quoted symbolic name (e.g. "horizontal_awning") when one exists, otherwise the raw hex form (e.g. 0x1A, 0x3FF) for a type with no YAML alias.

Definition at line 332 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ format_hex_byte()

std::string esphome::home_io_control::format_hex_byte ( uint8_t value)
inline

Format a byte as two uppercase hex digits, no prefix.

Named neutrally rather than after any one caller: used for CMD_ERROR_RESP result codes, probe reply/index command bytes, and sweep index values alike – none of those is a "result code" except the first.

Definition at line 81 of file management_helpers.h.

Here is the call graph for this function:

◆ format_oneway_command_report()

std::string esphome::home_io_control::format_oneway_command_report ( const OneWayCommandReport & report)

Build the sensor string for one command report.

Pure and free-standing so the wording is unit-testable: the host ESP_LOG stub discards its arguments, so a formatter buried in a publish call could not be asserted on at all.

Parameters
reportThe attempt to describe.
Returns
Human-readable summary, e.g. STOP -> awning seq 1234 or STOP -> awning seq 1234 (not sent).

Definition at line 12 of file platform_oneway_entities.cpp.

Here is the call graph for this function:

◆ format_position()

std::string esphome::home_io_control::format_position ( float pos)
inline

Format a position float as a human‑readable string (e.g.

"50%", "unknown").

Parameters
posPosition value (0–100 or UNKNOWN_POSITION).
Returns
String like "50%" or "unknown".

Definition at line 1685 of file hub_core.h.

Here is the call graph for this function:

◆ format_roll_call_responder()

std::string esphome::home_io_control::format_roll_call_responder ( const ScanResponder & responder,
const std::string & device_id,
const IoDevice * registered,
const RollCallReplyKeys & keys,
RollCallKeyDisplay display )

Format one roll-call responder's report line(s).

Known responders get a single summary line, plus an indented hint: line when their self-reported power-save class disagrees with the registered device's YAML low_power property (the responder must carry extended metadata for the comparison to be possible at all), or when they answered under a key other than the one their YAML uses. Unknown responders get the same summary line plus a lead-in sentence and a ready-to-paste YAML block (or, if the decoded type has no ESPHome platform yet, a "not supported yet" line instead of a blank) — the same "paste this in" framing a successful pairing prints. A responder found under a per-device key gets that key's check value on its summary line; display decides whether its snippet carries the key itself.

Parameters
responderDecoded responder record.
device_idHex device ID string for this responder, rebuilt from responder.src.
registeredThe matching registry entry, or nullptr if this responder is not known.
keysWhich key the responder answered under and what the hub key is.
displayWhether and how the key is printed.
Returns
The report text, ending in a newline.

Definition at line 220 of file roll_call.cpp.

Here is the call graph for this function:

◆ fp_bitmap_bit()

uint8_t esphome::home_io_control::fp_bitmap_bit ( uint8_t fp)
constexpr

Bit of functional parameter fp (1-16) in its bitmap: FPI1 holds FP1-FP8 and FPI2 FP9-FP16, each with the lowest-numbered parameter in bit 7.

One rule for EXECUTE and status bitmaps alike.

Parameters
fpFunctional parameter number, 1-16.
Returns
The single bit of fp in its bitmap.

Definition at line 633 of file proto_constants.h.

Here is the call graph for this function:

◆ frame_carries_mac_trailer()

bool esphome::home_io_control::frame_carries_mac_trailer ( uint8_t cmd)

Whether wire frames for a command carry the out-of-length MAC trailer described on IoFrame::has_mac/IoFrame::mac.

This exists because CTRL0's 5-bit length field cannot describe every command's declared payload plus a 6-byte authenticator in one span — one command's authenticator is carried outside the declared length instead (see the command's own Doxygen in proto_constants.h for why). parse() consults this before it will accept the wider declared_len + HMAC_SIZE buffer shape for a given command, so an unrelated frame that merely happens to arrive with 6 extra trailing bytes is never mistaken for a trailer-bearing one — only a command that genuinely carries a trailer gets the wider shape considered at all.

Parameters
cmdCommand byte (IoFrame::cmd, or the raw byte at FRAME_CMD_OFFSET in a wire buffer).
Returns
true if cmd's wire frames carry the mac trailer after the declared length.

Definition at line 107 of file proto_frame.cpp.

Here is the call graph for this function:

◆ frame_length()

uint8_t esphome::home_io_control::frame_length ( const IoFrame & f)

Get total frame length from ctrl0.

Parameters
fParsed frame.
Returns
Length in bytes.

Definition at line 103 of file proto_frame.cpp.

Here is the call graph for this function:

◆ frame_log_flush()

void esphome::home_io_control::frame_log_flush ( )
inline

Definition at line 248 of file log_frame.h.

Here is the call graph for this function:

◆ frame_log_hold()

void esphome::home_io_control::frame_log_hold ( )
inline

Definition at line 247 of file log_frame.h.

Here is the call graph for this function:

◆ frame_log_release()

void esphome::home_io_control::frame_log_release ( )
inline

Definition at line 249 of file log_frame.h.

Here is the call graph for this function:

◆ frame_transcript()

uint8_t esphome::home_io_control::frame_transcript ( const IoFrame & frame,
uint8_t out[FRAME_MAX_SIZE] )
static

Copy a frame's command byte and payload into out: the bytes a challenge response (0x3D) authenticates, and the IV data of a pulled key.

Returns the length.

Definition at line 938 of file proto_commands.cpp.

Here is the call graph for this function:

◆ has_execute_acei_override()

bool esphome::home_io_control::has_execute_acei_override ( const OneWayControllerIdentity & identity)
inline

Whether this identity's ACEI comes from an explicit execute_acei: rather than the profile.

Parameters
identityThe controller identity.
Returns
true when execute_acei: was set (non-zero) and overrides the manufacturer profile default.

Definition at line 239 of file oneway_controller.h.

Here is the call graph for this function:

◆ has_feature()

bool esphome::home_io_control::has_feature ( DeviceFeatures set,
DeviceFeatures bit )
constexpr

True when every bit of bit is set in set.

Parameters
setA feature word.
bitOne bit, or several that must all be set.

Definition at line 70 of file proto_device_features.h.

Here is the call graph for this function:

◆ has_invert_override()

bool esphome::home_io_control::has_invert_override ( const OneWayControllerIdentity & identity)
inline

Whether this identity's inversion comes from an explicit invert_position: rather than the kind.

Parameters
identityThe controller identity.
Returns
true when invert_position: was set in YAML.

Definition at line 275 of file oneway_controller.h.

Here is the call graph for this function:

◆ has_power_class_override()

bool esphome::home_io_control::has_power_class_override ( const OneWayControllerIdentity & identity)
inline

Whether this identity's burst shape comes from an explicit low_power: rather than the profile.

Parameters
identityThe controller identity.
Returns
true when low_power: was set in YAML and overrides the manufacturer profile default.

Definition at line 255 of file oneway_controller.h.

Here is the call graph for this function:

◆ has_reached_target_position()

bool esphome::home_io_control::has_reached_target_position ( float target,
float position )

Has the device reached its target within tolerance?

Parameters
targetTarget position (0–100 or UNKNOWN_POSITION).
positionCurrent position (0–100 or UNKNOWN_POSITION).
Returns
true if positions match within STATUS_POS_TOLERANCE_RAW.

Definition at line 165 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ heating_function_is_readable()

bool esphome::home_io_control::heating_function_is_readable ( HeatingFunction fn)

Whether the register behind a function can be read.

Parameters
fnFunction.
Returns
true for a read/write or read-only register; false for a write-only one or an out-of-range value.

Definition at line 168 of file proto_heating.cpp.

Here is the call graph for this function:

◆ heating_function_is_writable()

bool esphome::home_io_control::heating_function_is_writable ( HeatingFunction fn)

Whether the register behind a function can be written.

Parameters
fnFunction.
Returns
true for a read/write or write-only register; false for a read-only one or an out-of-range value.

Definition at line 163 of file proto_heating.cpp.

Here is the call graph for this function:

◆ heating_function_name()

const char * esphome::home_io_control::heating_function_name ( HeatingFunction fn)

Stable lowercase name for a heating function ("power_on", "set_temperature", ...).

Parameters
fnFunction.
Returns
Null-terminated string; "unknown" for an out-of-range value.

Definition at line 158 of file proto_heating.cpp.

Here is the call graph for this function:

◆ heating_function_register()

bool esphome::home_io_control::heating_function_register ( HeatingFunction fn,
uint8_t & register_low )

Low byte of the 0x01xx register behind a function.

Parameters
fnFunction.
[out]register_lowThe register's low byte; untouched when false is returned.
Returns
false for an out-of-range value.

Definition at line 173 of file proto_heating.cpp.

Here is the call graph for this function:

◆ heating_level_main_parameter()

uint16_t esphome::home_io_control::heating_level_main_parameter ( uint8_t level)
constexpr

Main parameter of a heating level, as it travels in an EXECUTE and in a status reply.

Parameters
levelLevel low byte (heating_level::*).
Returns
HEATING_LEVEL_MP_PREFIX in the high byte, level in the low byte.

Definition at line 318 of file proto_heating_profiles.h.

Here is the call graph for this function:

◆ heating_prefixed_main_parameter()

uint16_t esphome::home_io_control::heating_prefixed_main_parameter ( uint8_t prefix,
uint8_t low )
constexpr

Main parameter of a prefixed value (a level or an alias).

Parameters
prefixHigh byte (HEATING_LEVEL_MP_PREFIX or HEATING_ALIAS_MP_PREFIX).
lowLow byte.
Returns
The 16-bit main parameter.

Definition at line 326 of file proto_heating_profiles.h.

Here is the call graph for this function:

◆ heating_reply_echoes_register()

bool esphome::home_io_control::heating_reply_echoes_register ( const IoFrame & reply,
uint8_t reg_hi,
uint8_t reg_lo )

Is this frame a register reply that echoes a request for this register?

A CMD_WRITE_PRIVATE_ACK whose data starts with 0C 60 <reg_hi> <reg_lo> (a read) or 0C 61 <reg_hi> <reg_lo> (a write). The value bytes, if any, follow at HEATING_REPLY_VALUE_OFFSET; a register the device does not hold is answered with none.

Parameters
replyThe device's reply frame.
reg_hiHigh byte of the register the request addressed.
reg_loLow byte of the register the request addressed.
Returns
true when the reply is the register's echo.

Definition at line 181 of file proto_heating.cpp.

Here is the call graph for this function:

◆ hex_nibble()

int esphome::home_io_control::hex_nibble ( char ch)
static

Definition at line 22 of file proto_frame.cpp.

Here is the call graph for this function:

◆ hex_to_bytes()

bool esphome::home_io_control::hex_to_bytes ( const std::string & hex,
uint8_t * out,
uint8_t len )

Convert a hex string (e.g., "123ABC") to a byte array.

Parameters
hexHex string (must be exactly len*2 characters).
outOutput buffer (at least len bytes).
lenNumber of bytes to produce.
Returns
true on success; false if hex length mismatch or non‑hex characters.

Definition at line 31 of file proto_frame.cpp.

Here is the call graph for this function:

◆ init_frame()

void esphome::home_io_control::init_frame ( IoFrame & f,
bool is_2w = true,
bool start = false,
bool end = false,
bool low_power = false )

Initialize an IoFrame header (ctrl0/ctrl1) with flags.

Note: CTRL1_ACK is NOT automatically set on outbound frames. Some real-world devices reject frames with unexpected CTRL1 bits, causing total communication failure. The ACK constant is retained for inbound frame parsing and logging, and for three scoped exceptions: create_discovery_request()'s ack_capable parameter (proto_commands.h) can set it on the discovery broadcast, opt-in and gated by the pairing_discovery_ack_capable tuning knob (default off, tuning_config.h); the SPE roll-call's low-power frame shape (CTRL1 = LOW_POWER | ACK) sets it unconditionally, matching the header a real VELUX hub uses for its own roll-call; and create_discover_confirm()'s ack parameter, opt-in and gated by the pairing_discover_confirm tuning knob (send_with_ack, default off).

Parameters
fFrame to initialize.
is_2wTrue for 2‑way (default), false for 1‑way.
startSet START flag (first frame in exchange).
endSet END flag (final frame in exchange).
low_powerSet LOW_POWER flag.

Definition at line 70 of file proto_frame.cpp.

Here is the call graph for this function:

◆ io868_tx_air_time_us()

uint32_t esphome::home_io_control::io868_tx_air_time_us ( uint16_t preamble_len,
uint8_t frame_len )

Time on air of one 868 MHz transmission: preamble, sync word, and the frame plus its CRC, each byte of the latter a 10-bit UART cell, at the protocol's line rate.

The same line coding applies to every 868 MHz chip (the SX1276's IoHomeOn coder and the software PHY produce the same waveform), so every 868 driver's tx_air_time_us() is this.

Parameters
preamble_lenPreamble length in bytes.
frame_lenFrame length in bytes, without the CRC.
Returns
Air time in microseconds, rounded up.

Definition at line 237 of file radio_soft_phy.cpp.

Here is the call graph for this function:

◆ is_end()

bool esphome::home_io_control::is_end ( const IoFrame & f)

Check END flag.

Parameters
fParsed frame.
Returns
true if END flag is set.

Definition at line 105 of file proto_frame.cpp.

Here is the call graph for this function:

◆ is_known_device_type()

bool esphome::home_io_control::is_known_device_type ( uint16_t raw)

True when a raw 10-bit device-type value is a named DeviceType enumerator.

The enum is not contiguous (25 and 26 are undefined), so a range check is not enough. UNKNOWN (0) is not "known".

Parameters
rawRaw type value as decoded from an address or info frame.

Definition at line 178 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ is_known_io_command()

bool esphome::home_io_control::is_known_io_command ( uint8_t cmd)

This list missing CMD_GET_GENERAL_INFO3_RESP (0x59) is exactly what turned a real Q2 probe reply into a false "no reply" timeout on real hardware (2026-08-16); the same audit also found CMD_IDENTIFY and CMD_WRITE_PRIVATE/CMD_WRITE_PRIVATE_ACK missing, unrelated to that probe but affecting the already-shipped identify_device() action (and climate writes) on SX1262/LR1121.

Check if a command ID is one of the known IO-Homecontrol commands.

Add every new opcode this codebase sends a request for, or expects a reply to, here as well as in proto_constants.h. Deliberately excludes CMD_TRANSFER_OPEN (0x46) and CMD_UNKNOWN4A_REQ (0x4A): this codebase only sends them, and nothing it sends draws one back, so there is no exchange for a received one to unblock. Their replies CMD_TRANSFER_OPEN_RESP (0x47) and CMD_UNKNOWN4A_RESP (0x4B) are included (the read_object probe, ADR 0062; 0x4B also answers CMD_GET_GENERAL_INFO3 on some devices). Declared in radio_soft_phy.h so tests can enumerate every accepted command directly.

This is a gate, not a directory: find_uart_probe() only accepts a CRC-valid candidate whose cmd passes this check (or whose CTRL0_PROTOCOL_1W bit is set), so a command missing here makes every SX1262/LR1121 reception of that opcode silently unrecoverable on this software PHY — the frame is on air, its CRC matches, and it still never reaches the parser. Exposed (out of radio_soft_phy.cpp's anonymous namespace) so tests can iterate every accepted command directly instead of hand-maintaining a parallel list that can drift out of sync with this one.

Parameters
cmdCommand byte.
Returns
true if cmd matches a known command constant.

Definition at line 81 of file radio_soft_phy.cpp.

Here is the call graph for this function:

◆ is_limitation_result()

bool esphome::home_io_control::is_limitation_result ( uint8_t result)

Check whether a result code represents an environmental or control limitation.

Parameters
resultResult byte from CMD_ERROR_RESP data[0].
Returns
true when the response reports a limitation rather than a generic execution error.

Definition at line 512 of file proto_constants.cpp.

Here is the call graph for this function:

◆ is_start()

bool esphome::home_io_control::is_start ( const IoFrame & f)

Check START flag.

Parameters
fParsed frame.
Returns
true if START flag is set.

Definition at line 104 of file proto_frame.cpp.

Here is the call graph for this function:

◆ is_wake_preamble()

bool esphome::home_io_control::is_wake_preamble ( uint16_t len)
constexpr

True when a preamble this long is the wake-up burst for a duty-cycled receiver.

Every preamble tunable except pairing_discovery_preamble is capped well below LONG_PREAMBLE, and that one is capped at it (an explicit opt-in to the wake-up burst for discovery), so only a real wake-up burst reaches the threshold. The discovery preamble has no compiled default: it follows normal_start_preamble (see effective_discovery_preamble() in tuning_config.h).

Definition at line 88 of file proto_timing.h.

Here is the call graph for this function:

◆ key_extraction_session_window_ms()

constexpr uint32_t esphome::home_io_control::key_extraction_session_window_ms ( uint8_t multi_info)
staticconstexpr

How long after receiving a frame it answers the responder stays on that frame's channel (SESSION_HOLD_BASE_MS plus the turnaround time the responder advertises in its Multi Information Byte).

The peer's in-session frame (the 0x32 after our 0x3C, the 0x3C after our 0x37) comes on that channel within it, with a short preamble. After it the responder scans all three channels again, as a device does once its session ends: the peer's next request is a new transmission that may go out on any channel.

Todo
Measure on hardware how soon, and on which channel, a third-party hub's in-session frame follows our reply. Two passive community captures show a controller's 0x3D on another channel than the device's 0x3C; a hub that does so would be heard only once the hold ends.
Parameters
multi_infoThe Multi Information Byte the responder advertises.
Returns
The session window in milliseconds.

Definition at line 43 of file key_extraction_responder.h.

Here is the call graph for this function:

◆ key_transfer_mode_from_string()

std::optional< KeyTransferMode > esphome::home_io_control::key_transfer_mode_from_string ( const std::string & value)

Parse a KeyTransferMode from its YAML/select option string.

Exact, case-sensitive match against the strings key_transfer_mode_to_string() emits.

Parameters
valueOption string to parse.
Returns
The selection, or std::nullopt if value is not one of the options.

Definition at line 311 of file tuning_config.cpp.

Here is the call graph for this function:

◆ key_transfer_mode_to_string()

std::string esphome::home_io_control::key_transfer_mode_to_string ( KeyTransferMode value)

Render a KeyTransferMode as its YAML/select option string.

Parameters
valueSelection to render.
Returns
"push" or "pull".

Definition at line 299 of file tuning_config.cpp.

Here is the call graph for this function:

◆ limitation_time_seconds()

uint32_t esphome::home_io_control::limitation_time_seconds ( uint8_t time_raw)

Remaining time of a limitation in seconds.

Parameters
time_rawTime code (LimitationStatus::time_raw).
Returns
(n + 1) * 30 for codes 0..252, UINT32_MAX for LIMITATION_TIME_UNLIMITED, and 0 for the timer-delete codes 254/255, which are not durations.

Definition at line 578 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ log_discovered_device()

void esphome::home_io_control::log_discovered_device ( const pairing::PairingContext & context)
static

Log what discovery accepted: the device kind, and a warning when it answered as a remote controller.

Definition at line 413 of file pairing_engine.cpp.

Here is the call graph for this function:

◆ log_overheard_limitation_read()

void esphome::home_io_control::log_overheard_limitation_read ( const IoFrame & frame)
static

Log an overheard limitation request/reply decoded at DEBUG; any other frame is ignored.

Definition at line 867 of file hub_status.cpp.

Here is the call graph for this function:

◆ lr1121_bandwidth_from_string()

std::optional< LR1121RxBandwidth > esphome::home_io_control::lr1121_bandwidth_from_string ( const std::string & value)

Convert a YAML LR1121 bandwidth string to the enum value.

Parameters
valueYAML string such as "117.3" or "39.0kHz".
Returns
Bandwidth enum, or std::nullopt on invalid input.

Definition at line 153 of file tuning_config.cpp.

Here is the call graph for this function:

◆ lr1121_bandwidth_table()

BandwidthTableView esphome::home_io_control::lr1121_bandwidth_table ( )

The LR1121 RX-bandwidth option table (see sx1262_bandwidth_table()).

Definition at line 112 of file tuning_config.cpp.

Here is the call graph for this function:

◆ lr1121_bandwidth_to_khz()

float esphome::home_io_control::lr1121_bandwidth_to_khz ( LR1121RxBandwidth bw)

Convert an LR1121 bandwidth enum to the numeric kHz value used in YAML/logs.

Parameters
bwLR1121 bandwidth enum.
Returns
Floating-point kHz value (39.0, 46.9, 58.6, 78.2, 117.3, 156.2, or 187.2).

Definition at line 144 of file tuning_config.cpp.

Here is the call graph for this function:

◆ lr1121_bandwidth_to_string()

std::string esphome::home_io_control::lr1121_bandwidth_to_string ( LR1121RxBandwidth bw)

Format an LR1121 bandwidth enum as its YAML/UI option string (bare kHz number).

Parameters
bwLR1121 bandwidth enum.
Returns
Bandwidth rendered with one decimal place (e.g. "117.3").

Definition at line 149 of file tuning_config.cpp.

Here is the call graph for this function:

◆ lr1121_bootloader_is_lr1121()

bool esphome::home_io_control::lr1121_bootloader_is_lr1121 ( uint16_t bootloader_version)
nodiscardconstexpr
Returns
true if bootloader_version is one of the two bootloader versions an LR1121 (as opposed to an LR1120 or LR1110) can report.

Definition at line 62 of file lr1121_firmware_decisions.h.

Here is the call graph for this function:

◆ lr1121_bootloader_mismatch_kind()

BootloaderMismatch esphome::home_io_control::lr1121_bootloader_mismatch_kind ( uint16_t target_fw,
uint16_t bootloader_version )
nodiscardconstexpr

Classify a bootloader/target mismatch by direction; see BootloaderMismatch.

Definition at line 162 of file lr1121_firmware_decisions.h.

Here is the call graph for this function:

◆ lr1121_bootloader_supports_target()

BootloaderSupport esphome::home_io_control::lr1121_bootloader_supports_target ( uint16_t target_fw,
uint16_t bootloader_version )
nodiscardconstexpr

Look up whether target_fw is known to require bootloader_version.

A target absent from LR1121_KNOWN_BOOTLOADER_REQUIREMENTS is UNKNOWN_TARGET, never UNSUPPORTED — the table is a snapshot of what Semtech had published when this file was last updated, not an allow-list; a future firmware version this build has never heard of must not be rejected on that basis alone (see lr1121_flash_decision()'s three-way rule).

Definition at line 123 of file lr1121_firmware_decisions.h.

Here is the call graph for this function:

◆ lr1121_bootloader_upgrade_path()

BootloaderUpgradePath esphome::home_io_control::lr1121_bootloader_upgrade_path ( bool block_present,
bool bootloader_version_known,
uint16_t bootloader_version,
uint16_t loader_fw,
uint16_t target_fw )
nodiscardconstexpr

Whether the three-stage bootloader upgrade is applicable for the current cached state.

A post-filter, consulted only when lr1121_flash_decision() has already returned REJECT_BOOTLOADER_TOO_OLD – it never runs earlier and never changes that function's verdict (see this header's file comment and lr1121_flash_decision()'s doc comment). The evaluation order below is the specification – each rule exists to close a specific way an irreversible write could be justified on insufficient evidence:

  1. !block_present -> NOT_APPLICABLE – feature not built in.
  2. !bootloader_version_known -> NOT_APPLICABLE – an unknown current bootloader cannot justify an irreversible write. Note Lr1121FirmwareUpdateController::run_flash_sequence_() adopts a fresh reading at flash time for the ordinary transceiver path; that allowance must not extend to here.
  3. bootloader_version doesn't identify an LR1121 -> NOT_APPLICABLE – REJECT_WRONG_CHIP owns wrong-chip messaging, this function does not duplicate it.
  4. loader_fw != bootloader_version -> NOT_APPLICABLE – Semtech's equality rule for the loader image (see LR1121_LOADER_2100); also the "chip is already on 0x2101" case, since no 0x2101-chip can equal the 0x2100 loader.
  5. target not in the compatibility table (required == 0) -> BLOCKED_UNKNOWN_TARGET – never gamble an irreversible write on an unrecognised target.
  6. required == bootloader_version -> NOT_APPLICABLE – no upgrade needed.
  7. required > bootloader_version -> AVAILABLE – the one path that proceeds.
  8. else (required < bootloader_version) -> BLOCKED_BOOTLOADER_NEWER – a downgrade. The 0x8101 report includes an anti-rollback check, whose exact semantics Semtech does not document, but which most likely makes this permanently impossible; ADR 0021 records how far that is inference.
    Parameters
    block_presentWhether a bootloader: sub-block is configured (the build flag).
    bootloader_version_knownWhether the boot-time excursion successfully read a bootloader version – same "unknown is never evidence" sentinel rule as lr1121_flash_decision().
    bootloader_versionBootloader version read at boot; meaningless if !bootloader_version_known.
    loader_fwVersion parsed from the bootloader: sub-block's loader source: image.
    target_fwConfigured target firmware version (0 if unknown).

Definition at line 211 of file lr1121_firmware_decisions.h.

Here is the call graph for this function:

◆ lr1121_chip_family_for_bootloader()

const char * esphome::home_io_control::lr1121_chip_family_for_bootloader ( uint16_t bootloader_version)
nodiscardconstexpr

Human-readable chip family for a bootloader version that is not one of the two LR1121 values above, for an actionable REJECT_WRONG_CHIP message.

Moved here from hub wiring so the LR1120/LR1110 bootloader IDs live in one place (next to the LR1121 ones they are compared against) and the mapping is host-testable without a hub.

Definition at line 70 of file lr1121_firmware_decisions.h.

Here is the call graph for this function:

◆ lr1121_chip_family_for_device_type()

const char * esphome::home_io_control::lr1121_chip_family_for_device_type ( uint8_t device_type)
nodiscardconstexpr

Human-readable chip family for a normal-mode device_type that is not the LR1121 value above, for an actionable REJECT_WRONG_CHIP message (the layer-3 counterpart of lr1121_chip_family_for_bootloader() above).

Definition at line 81 of file lr1121_firmware_decisions.h.

Here is the call graph for this function:

◆ lr1121_chip_mode_name()

const char * esphome::home_io_control::lr1121_chip_mode_name ( uint8_t stat2)

Name of the chip mode in a Stat2 byte (bits 3:1, LR1121 user manual "Status Registers").

Parameters
stat2The Stat2 byte GetStatus returned.
Returns
SLEEP, STDBY_RC, STDBY_XOSC, FS, RX, TX, LOC (Wi-Fi/GNSS scan), or ?.

Definition at line 47 of file radio_lr1121.cpp.

Here is the call graph for this function:

◆ lr1121_firmware_is_outdated()

bool esphome::home_io_control::lr1121_firmware_is_outdated ( uint8_t fw_major,
uint8_t fw_minor )
nodiscardconstexpr

Pure comparison against the known-latest constants above — no I/O, host-testable.

Only ever flags a version as outdated when it is strictly older than what this file knows about; versions equal to or newer than LR1121_KNOWN_LATEST_FW_* (including any real future firmware this snapshot predates) are never flagged, so a stale baked-in constant fails silent rather than crying wolf.

Definition at line 161 of file radio_lr1121.h.

Here is the call graph for this function:

◆ lr1121_flash_decision()

FlashDecision esphome::home_io_control::lr1121_flash_decision ( uint8_t device_type,
uint8_t bootloader_chip_type,
uint16_t bootloader_version,
uint16_t installed_fw,
uint16_t target_fw,
bool already_confirmed )
nodiscardconstexpr

The single decision point for whether/how to flash target_fw.

Order of checks. Layers 3 and 4 below are the two chip-identity checks (normal-mode device_type and bootloader-mode version); everything else exists to make sure an absent read is never mistaken for evidence:

  1. Boot excursion never completed at all (bootloader_version unknown) – nothing below can be evaluated, so this is checked first and short-circuits straight to NEEDS_CONFIRMATION.
  2. Layer 4 – bootloader-mode identity: type must be the production-silicon byte AND the bootloader version must be one an LR1121 actually reports (as opposed to an LR1120 or LR1110, which also answer bootloader-mode GetVersion, just with different values).
  3. Normal-mode read never happened (device_type unknown, e.g. after a failed init()) – unknown is not evidence of the wrong chip, so this is checked before layer 3 can turn it into a false REJECT_WRONG_CHIP.
  4. Layer 3 – normal-mode chip identity: distinguishes LR1121 from LR1110/LR1120 while normal-mode firmware can still answer.
  5. Bootloader/target compatibility (the three-way rule – known-compatible proceeds, known-incompatible refuses, unknown-target asks for confirmation rather than refusing so a future firmware release keeps working without a code change here).
  6. Only once the pairing is positively known-good: the not-newer-so-confirm version compare.

Every input below has its own "unknown" sentinel, and every one of them routes to NEEDS_CONFIRMATION rather than a rejection or a false PROCEED – an absent read is never evidence of anything, safe or unsafe:

  • bootloader_version == 0: the boot-time excursion never successfully read one. 0x0000 is not a value any real LR11xx bootloader reports.
  • device_type == 0: the normal-mode read never happened at all (failed init(), or the read itself failed). Deliberately still allowed to reach a flash after confirmation – a radio that failed to initialize is exactly the case reflashing is meant to recover.
  • target_fw == 0: the build could not derive a version from the filename and none was configured; can never match a LR1121_KNOWN_BOOTLOADER_REQUIREMENTS entry (none uses 0), so it always resolves to BootloaderSupport::UNKNOWN_TARGET.
  • installed_fw == 0: the installed version is unknown (device_type known-good but the firmware-version bytes could not be read) – treated exactly like "not newer", never as "older than everything" the way a naive target_fw > installed_fw would.
Parameters
device_typeChip identity byte from a normal-mode GetVersion (layer 3); 0 if that read never happened.
bootloader_chip_typetype byte from the bootloader-mode GetVersion read at boot (layer 4); 0xDF (LR1121_BOOTLOADER_TYPE_FOR_FIRMWARE_DECISIONS) for production silicon of any LR11xx family, or 0 if the boot excursion never completed.
bootloader_versionBootloader version read at boot; one of LR1121_BOOTLOADER_2100/2101 for a genuine LR1121, or 0 if never successfully read.
installed_fwCurrently-installed transceiver firmware version (0 if unknown, e.g. after a failed init()).
target_fwConfigured target firmware version (0 if unknown — see above).
already_confirmedTrue on a second button press within the confirmation window; allows every non-hard-reject outcome to proceed instead of asking again.
Returns
The decision; see FlashDecision.

Definition at line 291 of file lr1121_firmware_decisions.h.

Here is the call graph for this function:

◆ lr1121_format_device_errors()

void esphome::home_io_control::lr1121_format_device_errors ( uint16_t errors,
char * buf,
size_t buf_size )

Expand a GetErrors word into a human-readable NAME|NAME|... string.

Parameters
errorsRaw GetErrors bitmask.
bufCaller-owned output buffer; always NUL-terminated on return.
buf_sizeSize of buf. Use DEVICE_ERROR_STR_SIZE.

Writes "none" when errors is zero, and appends UNKNOWN_0x%04X for any set bit with no name in the table so an undocumented flag still shows up in the log.

Definition at line 43 of file radio_lr1121.cpp.

Here is the call graph for this function:

◆ lr1121_required_bootloader_for()

uint16_t esphome::home_io_control::lr1121_required_bootloader_for ( uint16_t target_fw)
nodiscardconstexpr

Required bootloader for a known target firmware version.

Returns
The paired bootloader from LR1121_KNOWN_BOOTLOADER_REQUIREMENTS, or 0 when target_fw is not in the table – an "unverified", not "incompatible", target (see that table's comment), and never a real requirement value since no table entry uses 0.

Definition at line 138 of file lr1121_firmware_decisions.h.

Here is the call graph for this function:

◆ main_parameter_of()

uint16_t esphome::home_io_control::main_parameter_of ( uint8_t high,
uint8_t low = 0 )
constexpr

Combine the two bytes of a main parameter as the EXECUTE payload carries them.

Parameters
highFirst (high) byte.
lowSecond (low) byte, the modifier of a named position.

Definition at line 182 of file proto_device_types.h.

Here is the call graph for this function:

◆ main_parameter_only_variant()

DeviceFeatures esphome::home_io_control::main_parameter_only_variant ( DeviceFeatures type_features)
constexpr

The features of a subtype-58 variant of a kind.

VELUX KLF 200 API specification Appendix 2 defines subtype 58 as "main parameter only" for a few node types; this component applies the same rule to every type and, from issue #163, also reads it as "no intermediate values". The variant clears LEVELS, TILT and EXTENDED_POSITION_FRAME (each needs a functional parameter or an intermediate value) and sets MAIN_PARAMETER_ONLY. Named positions are main-parameter values of the family's actuator profile (KLF 200 section 14.2) and are kept. Used by the 58 rows and by the lookup fallback.

Parameters
type_featuresThe features of the kind the variant derives from.
Returns
The variant's features.

Definition at line 171 of file proto_device_types.h.

Here is the call graph for this function:

◆ make_management_result()

ManagementActionResult esphome::home_io_control::make_management_result ( const std::string & action,
const std::string & device_id )
inline

A result that carries only the action name and the target device ID.

Definition at line 88 of file management_helpers.h.

Here is the call graph for this function:

◆ manufacturer_name()

const char * esphome::home_io_control::manufacturer_name ( uint8_t id)

Get a human-readable manufacturer name from the protocol manufacturer byte.

The manufacturer ID is a 1-based index assigned by the IO-Homecontrol alliance. IDs outside the known range return "unknown". When an unknown ID appears at runtime, the pairing flow logs a warning suggesting the user file a GitHub issue.

Warning
Display-only — do not use this for YAML. Four of the twelve names do not round-trip through .strip().lower() to their manufacturer: YAML token (MANUFACTURER_OPTIONS, hub_validators.py): "Hörmann" has an umlaut the YAML token (hormann) drops, and "ASSA ABLOY"/"WINDOW MASTER"/"Atlantic Group" use a space where the YAML token uses _. There is currently no YAML-token accessor for manufacturers — see yaml_device_type_name() (proto_device_model.h) for the pattern this would follow if one is ever added.
Parameters
idManufacturer ID byte (1–12 for known manufacturers).
Returns
Null-terminated lowercase string such as "unknown", or mixed-case name like "Somfy".

Definition at line 127 of file proto_constants.cpp.

Here is the call graph for this function:

◆ match_heating_named_value()

bool esphome::home_io_control::match_heating_named_value ( const std::string & token,
const HeatingNamedValue * table,
size_t table_len,
float & out )
static

Match token against table; on a hit set out and return true.

Definition at line 180 of file management_actions.cpp.

Here is the call graph for this function:

◆ no_roll_call_pass_holds_request_channel()

bool esphome::home_io_control::no_roll_call_pass_holds_request_channel ( )
constexpr

A broadcast has no pinned conversation to hold a channel for, so HOLD_REQUEST_CHANNEL can never be a valid roll-call listen policy — checked at compile time rather than at every call site.

Definition at line 113 of file roll_call.h.

Here is the call graph for this function:

◆ node_id_to_string()

std::string esphome::home_io_control::node_id_to_string ( const uint8_t id[NODE_ID_SIZE])

Format a 3‑byte node ID as a 6‑character uppercase hex string.

Parameters
id3‑byte node ID.
Returns
Hex string (e.g., "123ABC").

Definition at line 50 of file proto_frame.cpp.

Here is the call graph for this function:

◆ normalize_device_id_argument()

std::string esphome::home_io_control::normalize_device_id_argument ( const std::string & device_id)
inline

Trim and uppercase a device-ID argument as received from the native API.

Definition at line 71 of file management_helpers.h.

Here is the call graph for this function:

◆ normalize_lower_argument()

std::string esphome::home_io_control::normalize_lower_argument ( const std::string & value)
static

Lowercase + ASCII-trim a native-API string argument.

Definition at line 165 of file management_actions.cpp.

Here is the call graph for this function:

◆ note_moving_evidence()

void esphome::home_io_control::note_moving_evidence ( IoDevice & dev,
uint32_t now_ms )
inline

Record that a device is (believed to be) travelling right now.

With clear_moving_evidence(), one of the two writers of IoDevice::last_moving_evidence_ms. Called wherever the hub learns a receiver is moving because of something that reached it: a decoded status that says "not stopped", a movement command the device accepted, or an overheard linked-remote movement command. Deliberately NOT called for the hub's own optimistic prediction: that is applied before the command is even sent, so treating it as proof of movement would make a resting receiver look awake to the very command that starts it. A low-power receiver that is moving ignores the long wake-up preamble, so this stamp steers the exchange engine to the short one first (decisions::wake_belief()).

Parameters
devDevice to stamp.
now_msCurrent millis(). Must be non-zero in practice; 0 would read as "never".

Definition at line 647 of file proto_device_model.h.

Here is the call graph for this function:

◆ note_moving_until()

void esphome::home_io_control::note_moving_until ( IoDevice & dev,
uint32_t until_ms )
inline

Record when the travel the device is reporting should be over, from its own remaining time.

Call after note_moving_evidence() for the same report: that call forgets an earlier end. While the end has not been reached the receiver is still travelling by its own account, so a silent exchange is not taken as proof that it has come to rest (decisions::moving_until_holds()).

Parameters
devDevice to stamp.
until_msmillis() at which the reported remaining time, plus a margin, has run out. Non-zero.

Definition at line 659 of file proto_device_model.h.

Here is the call graph for this function:

◆ oneway_burst_copy_shape()

OneWayCopyShape esphome::home_io_control::oneway_burst_copy_shape ( OneWayPowerClass power_class,
uint8_t copy_index )
inline

Resolve which preamble/CTRL1 shape one copy of a burst gets, from the identity's power class and the copy's position in the burst.

Pure: no radio, no tuning, testable on its own. Implements the table in ADR 0038 — LEGACY_LONG and ALWAYS_ALIVE are uniform across every copy; only LOW_POWER varies by position, putting the wake-up shape on copy 0 alone — what a real remote's GEAR/EXECUTE burst does, and the mirror of how ExchangeEngine (ADR 0029) already picks a 2W start frame's preamble from the target's own low-power declaration.

Parameters
power_classThe identity's resolved power class, from effective_power_class().
copy_index0-based position of this copy within the burst.
Returns
The preamble and CTRL1 shape for that copy.

Definition at line 85 of file oneway_controller.h.

Here is the call graph for this function:

◆ oneway_intent_to_target()

std::optional< float > esphome::home_io_control::oneway_intent_to_target ( uint8_t main0,
uint8_t main1 )

Resolve a 1W main-byte pair to an optimistic IO target position, if unambiguous.

Shares decode_1w_main_intent()'s special-code checks so the two never disagree. Returns empty for POS_STOP (the caller must clear any optimistic target instead — stop is not a target) and for codes with no settled position (FAVORITE/VENT/FORCE_OPEN/SECURED_TARGET/ DEFAULT/UNKNOWN) — those cases still get a confirmation poll, just no optimistic claim.

Parameters
main0First main byte (position or special code).
main1Second main byte (modifier); unused by every branch that resolves a target.
Returns
IO target position (0=open, 100=closed) if resolvable; empty otherwise.

Definition at line 338 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ oneway_power_class_name()

const char * esphome::home_io_control::oneway_power_class_name ( OneWayPowerClass power_class)

Human-readable name for a OneWayPowerClass, as it appears in the boot log.

Parameters
power_classPower class to name.
Returns
Null-terminated name such as "always-alive".

Definition at line 10 of file oneway_controller.cpp.

Here is the call graph for this function:

◆ oneway_target_label()

const char * esphome::home_io_control::oneway_target_label ( const OneWayFrameInfo & info)

The destination label a decoded 1W frame renders as in a log line.

Shared by the RX path (hub_internal.h's log_1w_remote_frame()) and the TX path (OneWayTransmitter::send_burst()) so the two cannot drift into naming the same address differently. The all-devices broadcast decodes to the sentinel DeviceType::UNKNOWN, whose bare device_type_name() would render as unknown; this special-cases it to all instead.

Parameters
infoAlready-decoded frame info (see decode_1w_frame()).
Returns
"all" for the all-devices broadcast, else the target class's name.

Definition at line 381 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ originator_name()

const char * esphome::home_io_control::originator_name ( uint8_t originator)

Get a human-readable name for a command originator byte.

Parameters
originatorOriginator code from the first data byte of CMD_EXECUTE.
Returns
Null-terminated string such as "rain_sensor" or "user_remote".

Definition at line 184 of file proto_constants.cpp.

Here is the call graph for this function:

◆ pairing_stage_name()

const char * esphome::home_io_control::pairing_stage_name ( pairing::PairingState state)
inline

Get a short, log/telemetry-friendly name for a pairing state.

Parameters
statePairing state.
Returns
Null-terminated lowercase string such as "wait_key_challenge".

Definition at line 106 of file hub_pairing.h.

Here is the call graph for this function:

◆ parse()

bool esphome::home_io_control::parse ( const uint8_t * buf,
uint8_t buf_len,
IoFrame & f )

Parse a wire buffer into a parsed IoFrame (validates length and CTRL0).

Parameters
bufRaw byte buffer.
buf_lenNumber of bytes in buf. Accepted shapes: exactly the CTRL0-declared length (has_mac comes out false), or — only for a command where frame_carries_mac_trailer() is true — that length plus HMAC_SIZE (the trailing bytes are copied into f.mac and has_mac comes out true). Any other length, or that same wider length for a command that doesn't carry a trailer, is rejected. data_len is always buf_len's declared portion minus FRAME_MIN_SIZE — the trailer is never data.
fOutput parsed frame.
Returns
true if parse succeeded; false otherwise.

Definition at line 154 of file proto_frame.cpp.

Here is the call graph for this function:

◆ parse_heating_arguments()

bool esphome::home_io_control::parse_heating_arguments ( const std::string & function,
const std::string & value,
HeatingFunction & fn_out,
float & value_out,
std::string & error )
static

Parse the (function, value) argument pair of the heating_control action.

Every native-API argument is a string (ManagementServiceDescriptor hardcodes SERVICE_ARG_TYPE_STRING), so this turns the two strings into a HeatingFunction plus the float encode_heating_payload() expects: degrees C for set_temperature, a HeatingMode value for set_mode, 0/1 for set_presence / set_open_window_detection, and an ignored 0 for the register reads (power_on, midnight_sync, window_open). On any malformed input it fills error with a caller-facing message and returns false rather than coercing a value. Kept next to the action (not in proto_heating) because the climate entity receives typed enums from Home Assistant and needs no string parsing.

Parameters
functionFunction name argument.
valueValue argument.
fn_outParsed function on success.
value_outParsed value on success (0 for the value-less functions).
errorCaller-facing message on failure.
Returns
true on a fully valid pair.

Definition at line 229 of file management_actions.cpp.

Here is the call graph for this function:

◆ parse_heating_temperature()

bool esphome::home_io_control::parse_heating_temperature ( const std::string & value,
float & value_out,
std::string & error )
static

Parse set_temperature's value into degrees Celsius, range-checked.

Definition at line 192 of file management_actions.cpp.

Here is the call graph for this function:

◆ parse_travel_speed()

bool esphome::home_io_control::parse_travel_speed ( const std::string & name,
TravelSpeed & speed )

Look up a travel speed by the name travel_speed_name() gives it.

Parameters
name"default", "silent" or "fast".
speedSet to the matching speed on success, untouched otherwise.
Returns
true when the name is one of the three.

Definition at line 96 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ periodic_poll_first_delay_ms()

uint32_t esphome::home_io_control::periodic_poll_first_delay_ms ( uint32_t random)
inlinenodiscard

Delay of a device's first poll after its interval is set.

Parameters
randomA uniformly distributed 32-bit random number.
Returns
PERIODIC_POLL_INITIAL_DELAY_MS plus an offset in [0, PERIODIC_POLL_INITIAL_SPREAD_MS].

Definition at line 50 of file periodic_poll_policy.h.

Here is the call graph for this function:

◆ periodic_poll_jittered_interval_ms()

uint32_t esphome::home_io_control::periodic_poll_jittered_interval_ms ( uint32_t interval_ms,
uint32_t random )
inlinenodiscard

One poll interval moved by a uniform offset of up to PERIODIC_POLL_JITTER_PERCENT either way.

The offset scales random over the whole 32-bit range, so 0 gives the lower bound, UINT32_MAX the upper bound and the middle of the range the unmodified interval. Computed in 64 bit so a 24 hour interval cannot overflow.

Parameters
interval_msThe configured interval.
randomA uniformly distributed 32-bit random number.
Returns
The delay until the next poll, in [interval - jitter, interval + jitter].

Definition at line 62 of file periodic_poll_policy.h.

Here is the call graph for this function:

◆ poll_until()

template<typename Ready>
PollResult esphome::home_io_control::poll_until ( Ready ready,
uint32_t timeout_ms )

Poll ready() until it is true or timeout_ms has passed, feeding the watchdog between samples.

The decision to give up is always taken on a sample read after the deadline was seen to pass, so a task preempted for longer than the timeout can only make the wait longer; it can never make a condition that became true look false. A caller can tell such an overrun from a clean success by ready && elapsed_ms > timeout_ms.

The start time is the only clock read before the first sample, and each loop iteration reads the clock once, so an already-satisfied condition costs exactly one millis() call (this runs before every SPI transaction on the software-PHY chips, and host tests on the counting clock rely on it).

Template Parameters
ReadyCallable returning bool; true means the condition holds.
Parameters
readyCondition to sample.
timeout_msWindow in milliseconds; the wait gives up once more than this has elapsed.

Definition at line 40 of file radio_poll.h.

Here is the call graph for this function:

◆ position_main_parameter()

uint16_t esphome::home_io_control::position_main_parameter ( uint8_t position)
constexpr

The main parameter set_device_position() sends for a percent position or the POS_STOP sentinel.

The position guard converts with this and never compares a percent against wire values.

Parameters
positionPercent 0-100 (IO scale), or POS_STOP.
Returns
POS_STOP gives MAIN_PARAMETER_STOP; 0-100 gives the percent on the doubled wire scale; anything else gives MAIN_PARAMETER_INVALID, which no device accepts.

Definition at line 276 of file proto_device_types.h.

Here is the call graph for this function:

◆ power_save_mode_name()

const char * esphome::home_io_control::power_save_mode_name ( uint8_t mode)

Get a human-readable power save mode name.

Parameters
modePower save value (0–1) extracted from the Multi Information Byte.
Returns
Null-terminated string such as "always_alive" or "low_power".

Definition at line 173 of file proto_constants.cpp.

Here is the call graph for this function:

◆ programme_period_active()

bool esphome::home_io_control::programme_period_active ( const HeatingProfile & profile,
const HeatingReadings & readings )

Does the heater's own programme run right now, with no timed run on top of it?

True when the profile has a programme period, its mode register reads the programme value and its preset register reads the idle value. An unread register is not the programme.

Parameters
profileThe device's profile.
readingsWhat the hub has read from the heater.
Returns
false for a profile without a programme period.

Definition at line 498 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ rain_sensor_state_name()

const char * esphome::home_io_control::rain_sensor_state_name ( RainSensorState state)

Get a human-readable name for a RainSensorState.

Parameters
stateThe state to name.
Returns
Null-terminated lowercase string: "unknown", "dry" or "rain".

Definition at line 32 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ recover_key_from_pull()

bool esphome::home_io_control::recover_key_from_pull ( const IoFrame & request,
const uint8_t transfer_payload[AES_KEY_SIZE],
uint8_t out_key[AES_KEY_SIZE] )

Recover a device's key from its answer to our 0x38.

Recover a device's key from the CMD_KEY_TRANSFER (0x32) it sent in answer to our CMD_LAUNCH_KEY_TRANSFER (0x38).

The IV data is the whole request, {CMD_LAUNCH_KEY_TRANSFER, r0..r5} (7 bytes); a 0x31 has no payload, which is why the push direction's IV is the command byte alone.

Same construction as recover_system_key_from_transfer(), with the IV taken from the request that asked for the key: its data is the whole request, {CMD_LAUNCH_KEY_TRANSFER, r0..r5}, and its challenge the request's 6 bytes. Checked against a documented pull exchange in proto_crypto_test.cpp, and on an Atlantic AGILIA IO radiator, whose 0x3D proved the key (issue #165).

Parameters
requestOur 0x38, as built by create_launch_key_transfer().
transfer_payload16-byte CMD_KEY_TRANSFER payload from the device.
out_keyOutput: the device's 16-byte key.
Returns
true on success; false when request carries fewer than 6 bytes or AES fails.

Definition at line 947 of file proto_commands.cpp.

Here is the call graph for this function:

◆ recover_system_key_from_transfer()

bool esphome::home_io_control::recover_system_key_from_transfer ( const uint8_t transfer_payload[AES_KEY_SIZE],
const uint8_t challenge[HMAC_SIZE],
uint8_t out_key[AES_KEY_SIZE] )

Recover the system key from a CMD_KEY_TRANSFER payload.

Recover the system key from an inbound CMD_KEY_TRANSFER (0x32) payload — the decode counterpart to create_key_transfer()'s encode.

See proto_commands.h for the full contract; this is the single place the IV-data convention ({CMD_KEY_INIT}, len 1) lives for the decode direction, mirroring create_key_transfer()'s encode side below.

Centralizes the IV-data convention in one place: create_key_transfer() derives its IV from the preceding CMD_KEY_INIT (0x31) command byte only (see its own doxygen), so decoding must use that same single-byte {CMD_KEY_INIT} — not the 0x32 frame's own command byte, and not the discovery frame. crypt_key() is symmetric, so this is the same primitive in reverse.

Parameters
transfer_payload16-byte CMD_KEY_TRANSFER payload (frame.data).
challengeThe 6-byte challenge we generated and sent in our own CMD_CHALLENGE_REQ (0x3C) — the far side mixes this into its IV, so decoding requires the exact same bytes.
out_keyOutput: recovered 16-byte system key.
Returns
true on success (crypt_key() AES failure is the only false case).

Definition at line 920 of file proto_commands.cpp.

Here is the call graph for this function:

◆ render_exchange_debug()

int esphome::home_io_control::render_exchange_debug ( char * buf,
size_t buf_size,
const char * device_id,
const ExchangeEngine::DebugInfo & d )

Render the structured field list shared by both exchange-debug log lines.

Split out of the log calls so the rendering is testable on host: the test log macros discard their arguments, so a formatter that writes into a caller's buffer is the only shape whose output a test can assert on.

Parameters
bufDestination buffer, EXCHANGE_DEBUG_LINE_SIZE is always enough.
buf_sizeSize of buf.
device_idHuman-readable device identifier.
dSnapshot to render.
Returns
snprintf()'s return value: the length the full line would have, so a caller (or a test) can detect truncation by comparing it against buf_size.

Definition at line 90 of file exchange_engine.cpp.

Here is the call graph for this function:

◆ render_frame_hex_redacted()

void esphome::home_io_control::render_frame_hex_redacted ( const uint8_t * data,
uint8_t len,
char * out,
size_t out_size )
inline

Render a frame's bytes as spaced hex text, masking the payload when the command carries key material (see command_carries_key_material()).

Kept outside the IOHOME_FRAME_LOG guard so the redaction behavior is unit-testable independent of the firmware-only logging build flag.

Warning
IOHOME_UNSAFE_LOG_KEY_MATERIAL disables the masking below entirely. It exists solely so a maintainer can capture a genuine pairing exchange for tests/corpus/ (see tests/corpus/README.md's key-hygiene section) when the normal ingest.py --rekey flow has no other way to obtain the raw bytes. Never set it outside a deliberate, local, own-hardware capture session: build with it, capture the one exchange you need, --rekey immediately, then rebuild without it. Never commit a config with it enabled, never use it on a device whose logs anyone else can see, and never paste output captured under it into an issue/chat before re-keying. hub_core.cpp's setup() logs a loud warning every boot while this is defined specifically so an accidentally-left-on build can't stay quiet.
Parameters
dataRaw serialized frame bytes (header + payload, no CRC).
lenTotal length of data.
outBuffer to write the rendered text into.
out_sizeSize of out.

Definition at line 72 of file log_frame.h.

Here is the call graph for this function:

◆ resolve_device_features()

void esphome::home_io_control::resolve_device_features ( IoDevice & dev)

Resolve a device's capability bits and inversion from its kind and its YAML overrides.

The one place a device's features are decided. The base is the (type, subtype, model) row (device_features(), with the declared IoDevice::model); a kind with no row, or an undeclared one, takes undeclared_kind_features() of the declaring entity's class (the type's own class when no entity declared the device), made main-parameter-only at subtype 58 like any other kind. A light's LEVELS then come from the YAML dimmable: flag alone, and never on a kind that takes the main parameter only. inverted is the YAML override, else the kind's default. Called wherever a record gets or changes its kind: DeviceRegistry::add() and put(), and when a status reply teaches an undeclared device its type.

Parameters
devDevice to resolve; reads type, subtype, model, entity_class, dimmable and invert_override and writes features and inverted.

Definition at line 295 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ resolve_discovery_destination()

const uint8_t * esphome::home_io_control::resolve_discovery_destination ( uint8_t command,
bool destination_auto,
const uint8_t destination[NODE_ID_SIZE] )

Resolve the destination address for a discovery command.

When destination_auto is true, returns the conventional address for the given command code. Otherwise returns the configured destination value.

Parameters
commandDiscovery command code.
destination_autoWhether to use the automatic mapping.
destinationExplicit 3-byte destination when auto is false.
Returns
Pointer to the resolved 3-byte node ID.

Definition at line 192 of file tuning_config.cpp.

Here is the call graph for this function:

◆ resolve_management_device()

IoDevice * esphome::home_io_control::resolve_management_device ( const char * action,
const std::string & device_id,
bool initialized,
DeviceRegistry & registry,
ManagementActionResult & result )
inline

Shared preamble for every device-addressed action: validate the hub and device ID, then resolve the target device.

Replaces result with the action name and normalized device ID, then checks (in order) that the hub is initialized, that device_id is a well-formed 6-hex-digit ID, and that it names a device registered on this hub. On any failure, sets result.message to a stable, caller-facing string and returns nullptr; callers should return result immediately.

Parameters
actionAction name to record on result.
device_idRequested device ID (hex string, case-insensitive, as received from HA).
initializedThe hub's initialized flag.
registryDevice registry to look the device up in.
resultPopulated with the action name and normalized device ID, plus an error message on failure.
Returns
Pointer to the resolved device, or nullptr if any validation step failed.

Definition at line 129 of file management_helpers.h.

Here is the call graph for this function:

◆ resolve_oneway_wire_profile()

OneWayWireProfile esphome::home_io_control::resolve_oneway_wire_profile ( uint8_t manufacturer)
inline

Resolve an identity's 1W wire profile from its manufacturer byte.

Pure. Somfy (0x02) and unset (0x00) both map to the historical Somfy-shaped default (ONEWAY_EXECUTE_ACEI, EnrollGesture::SOMFY); only VELUX (0x01) is special so far (ONEWAY_EXECUTE_ACEI_VELUX, EnrollGesture::VELUX_KLI, the class sweep). Any other explicitly-set manufacturer returns the Somfy default with profile_is_a_guess=true so the Python schema can warn (oneway_controllers.py validate_oneway_controllers() — keep the {somfy, velux} set here in sync with the warning there; there is no automated check).

Parameters
manufacturerThe identity's manufacturer byte (MANUFACTURER_*, or a raw value).

Definition at line 200 of file oneway_controller.h.

Here is the call graph for this function:

◆ resolve_programme_period()

ProgrammePeriodState esphome::home_io_control::resolve_programme_period ( const HeatingProfile & profile,
const HeatingReadings & readings )

Which period of its programme a heater is in, and the temperature it heats to.

Compares in whole tenths of a degree: the temperature in force equals the comfort setpoint (comfort period) or the comfort setpoint minus the setback (eco period). Anything else, or a register not read yet, names no period but still reports the temperature in force.

Parameters
profileThe device's profile.
readingsWhat the hub has read from the heater.
Returns
active false when the programme does not run; otherwise the period's preset index and the temperature in force.

Definition at line 516 of file proto_heating_profiles.cpp.

Here is the call graph for this function:

◆ rssi_scaled_to_dbm()

int16_t esphome::home_io_control::rssi_scaled_to_dbm ( int16_t scaled)
inline

Convert an rssi_ema_scaled fixed-point value to whole dBm (round half away from zero).

Parameters
scaledFixed-point EMA value in 1/RSSI_EMA_SCALE dBm units (not the sentinel).
Returns
Rounded dBm value.

Definition at line 677 of file proto_device_model.h.

Here is the call graph for this function:

◆ run_roll_call_sweep()

RollCallAttemptResult esphome::home_io_control::run_roll_call_sweep ( ExchangeEngine & engine,
const uint8_t * node_id,
const uint8_t * key,
uint8_t key_slot,
const TuningConfig & tuning,
ScanState & state,
uint8_t & attempt,
uint8_t total_attempts )

Run the full roll-call sweep under one key: every enabled pass on every channel.

Outer loop over passes (low-power first, see ROLL_CALL_PASSES), skipping any pass the scan_power_classes tunable excludes; inner loop over SCAN_CHANNELS. Every attempt always runs, even once the array is full: stopping early would silently skip channels/passes, which is exactly the single-channel behaviour the repeated attempts exist to avoid.

Parameters
engineExchange engine that transmits and collects the replies.
node_idThe hub's node ID.
keyThe AES_KEY_SIZE-byte key the request is built with; only devices holding it answer.
key_slotSlot recorded on every responder this sweep adds (0 = hub key).
tuningLive tuning (reply window, power-class selection).
stateAccumulated scan state, appended to.
attemptIn/out: the global 1-based attempt counter across sweeps (0 before the first).
total_attemptsAttempts the whole scan will run, for the "n/N" log line.
Returns
BUILD_FAILED if a request could not be built; OK otherwise.

Definition at line 137 of file roll_call.cpp.

Here is the call graph for this function:

◆ scan_power_classes_from_string()

std::optional< ScanPowerClasses > esphome::home_io_control::scan_power_classes_from_string ( const std::string & value)

Parse a scan_power_classes string into the enum.

Exact, case-sensitive match against the same three strings scan_power_classes_to_string() produces.

Parameters
valueString to parse.
Returns
The matching selection, or std::nullopt on invalid input.

Definition at line 237 of file tuning_config.cpp.

Here is the call graph for this function:

◆ scan_power_classes_include()

bool esphome::home_io_control::scan_power_classes_include ( ScanPowerClasses selection,
uint8_t power_save_mode )

Whether selection calls the roll-call pass for power_save_mode.

Pure predicate, used both by the sweep loop (which pass to skip) and by the report (which class to name in the selection NOTE) — one source of truth for "does this selection include that class" instead of two.

Parameters
selectionCurrent scan_power_classes tuning value.
power_save_modePOWER_SAVE_ALWAYS_ALIVE or POWER_SAVE_LOW_POWER.
Returns
true if selection calls that class; false for an unrecognized power_save_mode.

Definition at line 247 of file tuning_config.cpp.

Here is the call graph for this function:

◆ scan_power_classes_to_string()

std::string esphome::home_io_control::scan_power_classes_to_string ( ScanPowerClasses value)

Format a ScanPowerClasses value for YAML/logs.

BOTH is its own literal; the two single-class values return power_save_mode_name(POWER_SAVE_ALWAYS_ALIVE) / power_save_mode_name(POWER_SAVE_LOW_POWER) so the roll-call report and this tunable share one vocabulary rather than a second spelling.

Parameters
valueSelection to format.
Returns
"both", "always_alive", or "low_power".

Definition at line 225 of file tuning_config.cpp.

Here is the call graph for this function:

◆ serialize()

uint8_t esphome::home_io_control::serialize ( const IoFrame & f,
uint8_t * buf,
uint8_t buf_size )

Serialize a parsed frame into a wire buffer (without CRC).

Parameters
fParsed frame. When f.has_mac, the 6-byte mac trailer is appended after the declared payload and counted in the returned length, so a caller's CRC (computed over the returned length) covers it.
bufOutput buffer (must be at least frame_length(f) bytes, or +HMAC_SIZE when f.has_mac).
buf_sizeSize of buf.
Returns
Number of bytes written (declared length, plus HMAC_SIZE when f.has_mac), or 0 on failure.

Definition at line 114 of file proto_frame.cpp.

Here is the call graph for this function:

◆ set_cmd()

bool esphome::home_io_control::set_cmd ( IoFrame & f,
uint8_t cmd,
const uint8_t * params = nullptr,
uint8_t params_len = 0 )

Set command and payload.

Parameters
fFrame to modify.
cmdCommand ID.
paramsPointer to payload bytes (may be nullptr for zero‑length).
params_lenPayload length (0–23).
Returns
true if frame fits within size limits; false otherwise.

Definition at line 85 of file proto_frame.cpp.

Here is the call graph for this function:

◆ set_dst()

void esphome::home_io_control::set_dst ( IoFrame & f,
const uint8_t id[NODE_ID_SIZE] )

Set destination node ID.

Parameters
fFrame to modify.
id3‑byte destination address.

Definition at line 82 of file proto_frame.cpp.

Here is the call graph for this function:

◆ set_src()

void esphome::home_io_control::set_src ( IoFrame & f,
const uint8_t id[NODE_ID_SIZE] )

Set source node ID.

Parameters
fFrame to modify.
id3‑byte source address.

Definition at line 83 of file proto_frame.cpp.

Here is the call graph for this function:

◆ settle_delay_ms()

uint32_t esphome::home_io_control::settle_delay_ms ( uint32_t interval_ms,
uint32_t remaining_delay_ms,
bool cap_for_stop )
inlinenodiscard

Resolve the follow-up settle-poll delay while a device may still be moving.

This is the single source of truth for the motion-tracking cadence. The delay is the configured interval_ms, or DEFAULT_SETTLE_POLL_DELAY_MS when no interval is configured (interval_ms == 0). The device's remaining travel time plus a margin (remaining_delay_ms, 0 when absent) and a STOP command (cap_for_stop) may only shorten the delay, never lengthen it.

Note: this resolves the settle delay only — it is intentionally independent of whether a device keeps polling after coming to rest. Periodic monitoring after rest remains opt-in and is gated on interval_ms != 0 by the command paths, so defaulting the settle delay here does not turn every device into a periodic poller.

Returns
Settle delay in milliseconds (always non-zero).

Definition at line 171 of file status_poll_policy.h.

Here is the call graph for this function:

◆ soft_phy_air_time_us()

uint32_t esphome::home_io_control::soft_phy_air_time_us ( uint32_t raw_bytes)
constexpr

On-air time in microseconds for raw_bytes bytes at the protocol's line rate.

One byte is 8 / 38400 s = 208.333 µs. Computed as an integer division rounded up, so the result never falls short of a whole byte's air time and a caller that waits on it never reads the chip's buffer early. The numerator is 64-bit: in 32 bits it would wrap above 536 bytes, and a 1024-byte wake-up preamble is a real transmission length.

Definition at line 81 of file radio_soft_phy.h.

Here is the call graph for this function:

◆ soft_phy_peek_frame_length()

uint8_t esphome::home_io_control::soft_phy_peek_frame_length ( const uint8_t * raw,
uint8_t raw_len )

Recover a frame's total length from the very first UART cell of a reception.

CTRL0 bits [4:0] hold frame_length - 1 (see proto_frame.h), and CTRL0 is the first byte after the sync word — so ten bits of air time are enough to learn how long the whole frame will be. Alignment is not yet known at that point, so every probe offset is tried and the largest plausible answer wins: over-waiting by a few bytes costs a little latency, whereas under-waiting would truncate the frame.

Parameters
rawRaw bytes read from the chip's data buffer, starting at the reception's own offset.
raw_lenNumber of raw bytes available (three is enough at any alignment).
Returns
Plausible frame length in bytes, or 0 when no offset yields one.

Definition at line 242 of file radio_soft_phy.cpp.

Here is the call graph for this function:

◆ soft_phy_raw_bytes_for_frame()

uint8_t esphome::home_io_control::soft_phy_raw_bytes_for_frame ( uint8_t frame_len)

Raw on-air bytes needed to carry a whole frame: frame_len protocol bytes plus the two trailing CRC bytes, each UART-packed into a 10-bit cell.

This is what makes a length-driven receive possible at all: a frame's own size is knowable from its first decoded byte, so the raw byte count it will occupy is knowable too — no chip needs to tell us where the frame ends.

Parameters
frame_lenProtocol frame length in bytes (CTRL0's own length field, +1).
Returns
Raw byte count, rounded up to whole bytes.

Definition at line 229 of file radio_soft_phy.cpp.

Here is the call graph for this function:

◆ stored_node_id_is_valid()

bool esphome::home_io_control::stored_node_id_is_valid ( const uint8_t id[NODE_ID_SIZE])
inline

Check whether a node ID is usable as an address: not all-zero and not all-0xFF, the two patterns blank storage and unset fields produce.

Parameters
id3‑byte node ID buffer.
Returns
true if the ID is non-zero and non-0xFF.

Definition at line 200 of file proto_frame.h.

Here is the call graph for this function:

◆ sx1262_bandwidth_from_string()

std::optional< SX1262RxBandwidth > esphome::home_io_control::sx1262_bandwidth_from_string ( const std::string & value)

Convert a YAML bandwidth string to the enum value.

Parameters
valueYAML string such as "117.3kHz" or "156.2kHz".
Returns
Bandwidth enum, or std::nullopt on invalid input.

Definition at line 123 of file tuning_config.cpp.

Here is the call graph for this function:

◆ sx1262_bandwidth_table()

BandwidthTableView esphome::home_io_control::sx1262_bandwidth_table ( )

The SX1262 RX-bandwidth option table.

Exposed so tests derive their legal-option lists from the same source production uses rather than re-listing them by hand.

Definition at line 110 of file tuning_config.cpp.

Here is the call graph for this function:

◆ sx1262_bandwidth_to_khz()

float esphome::home_io_control::sx1262_bandwidth_to_khz ( SX1262RxBandwidth bw)

Convert a bandwidth enum to the numeric kHz value used in YAML/logs.

Parameters
bwSX1262 bandwidth enum.
Returns
Floating-point kHz value (39.0, 46.9, 58.6, 78.2, 117.3, 156.2, or 187.2).

Definition at line 114 of file tuning_config.cpp.

Here is the call graph for this function:

◆ sx1262_bandwidth_to_string()

std::string esphome::home_io_control::sx1262_bandwidth_to_string ( SX1262RxBandwidth bw)

Format a bandwidth enum as its YAML/UI option string (bare kHz number, e.g.

"117.3").

Parameters
bwSX1262 bandwidth enum.
Returns
Bandwidth rendered with one decimal place and a "kHz" suffix.

Definition at line 119 of file tuning_config.cpp.

Here is the call graph for this function:

◆ sx1262_chip_mode_name()

const char * esphome::home_io_control::sx1262_chip_mode_name ( uint8_t chip_status)

Name of the chip mode in a GetStatus byte (bits 6:4).

Parameters
chip_statusThe byte GetStatus returned.
Returns
STDBY_RC, STDBY_XOSC, FS, RX, TX, or ? for a reserved or unused value.

Definition at line 188 of file radio_sx1262.cpp.

Here is the call graph for this function:

◆ sx1262_command_status()

uint8_t esphome::home_io_control::sx1262_command_status ( uint8_t chip_status)

Command status field of a GetStatus byte (bits 3:1).

Parameters
chip_statusThe byte GetStatus returned.
Returns
The 3-bit field (2 = data available, 3 = command timeout, 4 = processing error, 5 = execution failure, 6 = TX done).

Definition at line 206 of file radio_sx1262.cpp.

Here is the call graph for this function:

◆ sx1262_fem_rx_gain()

FemRxGain esphome::home_io_control::sx1262_fem_rx_gain ( FemProfile profile)

The receive gain to remove from RSSI for a FEM profile.

Parameters
profileAny profile; FemProfile::NONE has no front end and yields 0.

Definition at line 169 of file radio_sx1262.cpp.

Here is the call graph for this function:

◆ sx1262_fem_tx_estimate()

FemTxEstimate esphome::home_io_control::sx1262_fem_tx_estimate ( FemProfile profile,
uint8_t tx_power )

Estimate what a FEM profile radiates for a configured tx_power.

Parameters
profileA real FEM profile (not FemProfile::NONE, which has no gain table).
tx_powerConfigured power setting; clamped like the value written to SetTxParams.

The gain tables behind it carry an uncertainty of about ±5–7 dB for the Heltec profiles; see docs/adr/0035-fem-support-is-a-behaviour-profile-boards-always-supply-pins.md.

Definition at line 183 of file radio_sx1262.cpp.

Here is the call graph for this function:

◆ sx1262_format_device_errors()

void esphome::home_io_control::sx1262_format_device_errors ( uint16_t errors,
char * buf,
size_t buf_size )

Expand a GetDeviceErrors word into a human-readable NAME|NAME|... string.

Parameters
errorsRaw device-error bitmask from GetDeviceErrors.
bufCaller-owned output buffer; always NUL-terminated on return.
buf_sizeSize of buf. Use DEVICE_ERROR_STR_SIZE.

Writes "none" when errors is zero, and appends UNKNOWN_0x%04X for any set bit with no name in the table so an undocumented flag still shows up in the log.

Definition at line 211 of file radio_sx1262.cpp.

Here is the call graph for this function:

◆ sx1276_bandwidth_from_string()

std::optional< SX1276RxBandwidth > esphome::home_io_control::sx1276_bandwidth_from_string ( const std::string & value)

Convert a YAML SX1276 bandwidth string to the enum value.

Parameters
valueYAML string such as "41.7" or "83.3kHz".
Returns
Bandwidth enum, or std::nullopt on invalid input.

Definition at line 138 of file tuning_config.cpp.

Here is the call graph for this function:

◆ sx1276_bandwidth_table()

BandwidthTableView esphome::home_io_control::sx1276_bandwidth_table ( )

The SX1276 RX-bandwidth option table (see sx1262_bandwidth_table()).

Definition at line 111 of file tuning_config.cpp.

Here is the call graph for this function:

◆ sx1276_bandwidth_to_khz()

float esphome::home_io_control::sx1276_bandwidth_to_khz ( SX1276RxBandwidth bw)

Convert an SX1276 bandwidth enum to the numeric kHz value used in YAML/logs.

Parameters
bwSX1276 bandwidth enum.
Returns
Floating-point kHz value (20.8, 41.7, 62.5, 83.3, or 125.0).

Definition at line 129 of file tuning_config.cpp.

Here is the call graph for this function:

◆ sx1276_bandwidth_to_string()

std::string esphome::home_io_control::sx1276_bandwidth_to_string ( SX1276RxBandwidth bw)

Format an SX1276 bandwidth enum as its YAML/UI option string (bare kHz number).

Parameters
bwSX1276 bandwidth enum.
Returns
Bandwidth rendered with one decimal place (e.g. "41.7").

Definition at line 134 of file tuning_config.cpp.

Here is the call graph for this function:

◆ tcxo_voltage_label()

const char * esphome::home_io_control::tcxo_voltage_label ( uint8_t code)
inlinenodiscard

Human-readable voltage for a tcxo_voltage code, for the config dump.

Parameters
code0-based chip voltage code (0x00 = 1.6 V .. 0x07 = 3.3 V) or TCXO_VOLTAGE_NONE.
Returns
A static string; "unknown" for a code outside the table.

Mirrors TCXO_VOLTAGE_OPTIONS in hub_validators.py, the YAML-side source of truth for the codes.

Definition at line 52 of file radio_interface.h.

Here is the call graph for this function:

◆ transfer_chunk_count()

uint16_t esphome::home_io_control::transfer_chunk_count ( uint16_t length)

Number of CMD_UNKNOWN4A_REQ chunks that cover length bytes: ceil(length / TRANSFER_BLOCK_SIZE).

Definition at line 534 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ travel_speed_name()

const char * esphome::home_io_control::travel_speed_name ( TravelSpeed speed)

Get the name a travel speed is configured and published under.

Parameters
speedThe speed to name.
Returns
"default", "silent" or "fast".

Definition at line 84 of file proto_device_model.cpp.

Here is the call graph for this function:

◆ trim_ascii_whitespace()

std::string esphome::home_io_control::trim_ascii_whitespace ( const std::string & value)

Trim leading and trailing ASCII whitespace from a string.

This is shared by device-name validation and management actions so both paths normalize input consistently before comparing or writing device metadata.

Parameters
valueInput string.
Returns
Copy of the string with leading and trailing ASCII whitespace removed.

Definition at line 76 of file proto_codecs.cpp.

Here is the call graph for this function:

◆ tuning_config_full_snapshot()

std::string esphome::home_io_control::tuning_config_full_snapshot ( const TuningConfig & cfg)

Format the current tuning configuration as a full one-line snapshot.

Emits every non-default value, regardless of whether it was changed from YAML or from the HA UI. This is logged at the start of every pairing attempt.

Parameters
cfgCurrent tuning configuration.
Returns
One-line snapshot string, or a "defaults" marker when nothing is overridden.

Definition at line 413 of file tuning_config.cpp.

Here is the call graph for this function:

◆ tuning_config_snapshot()

std::string esphome::home_io_control::tuning_config_snapshot ( const TuningConfig & cfg)

Format the current tuning configuration as a one-line YAML-compatible snapshot.

Only values that differ from the default are emitted, so the line can be copied back into YAML with minimal editing. When all values are default, an empty string is returned.

Parameters
cfgCurrent tuning configuration.
Returns
One-line snapshot string, or empty when no overrides are active.

Definition at line 355 of file tuning_config.cpp.

Here is the call graph for this function:

◆ tuning_number_params_begin()

const TuningNumberParam * esphome::home_io_control::tuning_number_params_begin ( )

Definition at line 308 of file tuning_registry.cpp.

Here is the call graph for this function:

◆ tuning_number_params_end()

const TuningNumberParam * esphome::home_io_control::tuning_number_params_end ( )

Definition at line 309 of file tuning_registry.cpp.

Here is the call graph for this function:

◆ tuning_select_params_begin()

const TuningSelectParam * esphome::home_io_control::tuning_select_params_begin ( )

Definition at line 310 of file tuning_registry.cpp.

Here is the call graph for this function:

◆ tuning_select_params_end()

const TuningSelectParam * esphome::home_io_control::tuning_select_params_end ( )

Definition at line 311 of file tuning_registry.cpp.

Here is the call graph for this function:

◆ tuning_update_log_line()

std::string esphome::home_io_control::tuning_update_log_line ( const std::string & name,
const std::string & value )

Format a single tuning update for the log.

Parameters
nameYAML key name of the updated parameter.
valueYAML-compatible string representation of the new value.
Returns
One-line log string suitable for ESP_LOGI.

Definition at line 321 of file tuning_config.cpp.

Here is the call graph for this function:

◆ tx_done_timeout_ms()

uint32_t esphome::home_io_control::tx_done_timeout_ms ( uint32_t air_time_us)
constexpr

How long a driver waits for TX-done after starting a transmission.

Parameters
air_time_usThe transmission's air time (RadioDriver::tx_air_time_us()).
Returns
The air time rounded up to whole milliseconds, plus TX_DONE_GUARD_MS.

Definition at line 114 of file radio_soft_phy.h.

Here is the call graph for this function:

◆ tx_wake_for()

TxWake esphome::home_io_control::tx_wake_for ( bool start,
uint16_t preamble )
constexpr

The wake-up level of a frame, from the two facts that decide it: whether it starts an exchange (a continuation reaches a receiver that is already listening) and whether its preamble is the wake-up burst.

Definition at line 93 of file proto_timing.h.

Here is the call graph for this function:

◆ uart_encode_packet()

uint8_t esphome::home_io_control::uart_encode_packet ( const uint8_t * data,
uint8_t len,
uint8_t * encoded,
uint8_t encoded_max_len )

UART-encode a buffer of bytes (start bit 0, 8 data bits LSB-first, stop bit 1).

Returns
Number of encoded bytes, or 0 if the output buffer is too small.

Definition at line 258 of file radio_soft_phy.cpp.

Here is the call graph for this function:

◆ undeclared_kind_features()

DeviceFeatures esphome::home_io_control::undeclared_kind_features ( DeviceCapabilityClass entity_class)

Features of a device kind that has no table row, or that is not declared at all.

An undeclared device keeps the permissive behaviour its entity gives it until the type is learned and a row takes over: a cover accepts a slider, favourite and vent and sends the 8-byte position frame; a light, switch or lock sends the 8-byte frame; an exterior-heating fan takes levels in that frame. No class has tilt.

Parameters
entity_classCapability class of the entity that registered the device.
Returns
The feature word, or no features for a class with no EXECUTE position path.

Definition at line 344 of file proto_device_types.cpp.

Here is the call graph for this function:

◆ verify_challenge_resp()

bool esphome::home_io_control::verify_challenge_resp ( const IoFrame & origin,
const uint8_t challenge[HMAC_SIZE],
const uint8_t mac[HMAC_SIZE],
const uint8_t * key )

Check a device's 0x3D for origin, in constant time (crypto::verify_hmac()).

Check a device's challenge response (0x3D) for origin, the frame it sent and we challenged.

The device signs its own frame, the same transcript as challenge_resp_hmac().

Parameters
originThe device's challenged frame.
challengeThe 6 bytes we sent in the 0x3C.
macThe 6 bytes of the device's 0x3D.
key16-byte key the MAC must verify under.
Returns
true if the MAC matches.

Definition at line 1041 of file proto_commands.cpp.

Here is the call graph for this function:

◆ water_heater_mode_for()

water_heater::WaterHeaterMode esphome::home_io_control::water_heater_mode_for ( DhwMode mode)

ESPHome water heater mode for a heater mode.

Parameters
modeMode as register 0x0006 holds it.
Returns
The mapped ESPHome mode (see the file comment).

Definition at line 42 of file platform_water_heater.cpp.

Here is the call graph for this function:

◆ yaml_device_type_name()

const char * esphome::home_io_control::yaml_device_type_name ( DeviceType type)

Return the YAML-friendly device-type name for types exposed in the Python schema.

Parameters
typeDevice type enum.
Returns
Null‑terminated string (e.g., "exterior_venetian_blind"), or nullptr if the type has no symbolic YAML alias (user must use a raw numeric value).

Definition at line 188 of file proto_device_model.cpp.

Here is the call graph for this function:

Variable Documentation

◆ ACEI_EXTENDED_MASK

uint8_t esphome::home_io_control::ACEI_EXTENDED_MASK = 0x06
staticconstexpr

Bits [2:1]: extended field.

Definition at line 798 of file proto_constants.h.

◆ ACEI_EXTENDED_SHIFT

uint8_t esphome::home_io_control::ACEI_EXTENDED_SHIFT = 1
staticconstexpr

Shift for extended field extraction.

Definition at line 799 of file proto_constants.h.

◆ ACEI_LEVEL_AUTO_DEFAULT

uint8_t esphome::home_io_control::ACEI_LEVEL_AUTO_DEFAULT = 7
staticconstexpr

Default automatic level (lowest).

Definition at line 817 of file proto_constants.h.

◆ ACEI_LEVEL_AUTO_SAAC

uint8_t esphome::home_io_control::ACEI_LEVEL_AUTO_SAAC = 6
staticconstexpr

Stand-alone automatic controller.

Definition at line 816 of file proto_constants.h.

◆ ACEI_LEVEL_COMFORT_1

uint8_t esphome::home_io_control::ACEI_LEVEL_COMFORT_1 = 4
staticconstexpr

Comfort automation level 1.

Definition at line 814 of file proto_constants.h.

◆ ACEI_LEVEL_COMFORT_2

uint8_t esphome::home_io_control::ACEI_LEVEL_COMFORT_2 = 5
staticconstexpr

Comfort automation level 2.

Definition at line 815 of file proto_constants.h.

◆ ACEI_LEVEL_MASK

uint8_t esphome::home_io_control::ACEI_LEVEL_MASK = 0xE0
staticconstexpr

Bits [7:5]: priority level (0–7).

Definition at line 802 of file proto_constants.h.

◆ ACEI_LEVEL_PROTECTION_HUMAN

uint8_t esphome::home_io_control::ACEI_LEVEL_PROTECTION_HUMAN = 0
staticconstexpr

ACEI priority level values (0–7).

These values are extracted from the ACEI byte via (acei & ACEI_LEVEL_MASK) >> ACEI_LEVEL_SHIFT. Personal safety (highest, overrides all).

Definition at line 810 of file proto_constants.h.

◆ ACEI_LEVEL_PROTECTION_SENSOR

uint8_t esphome::home_io_control::ACEI_LEVEL_PROTECTION_SENSOR = 1
staticconstexpr

Goods/environment protection via sensors.

Definition at line 811 of file proto_constants.h.

◆ ACEI_LEVEL_SHIFT

uint8_t esphome::home_io_control::ACEI_LEVEL_SHIFT = 5
staticconstexpr

Shift for priority level extraction.

Definition at line 803 of file proto_constants.h.

◆ ACEI_LEVEL_USER_DEFAULT

uint8_t esphome::home_io_control::ACEI_LEVEL_USER_DEFAULT = 3
staticconstexpr

Default remote controller priority.

Definition at line 813 of file proto_constants.h.

◆ ACEI_LEVEL_USER_HIGH

uint8_t esphome::home_io_control::ACEI_LEVEL_USER_HIGH = 2
staticconstexpr

High-priority user controller.

Definition at line 812 of file proto_constants.h.

◆ ACEI_SERVICE_MASK

uint8_t esphome::home_io_control::ACEI_SERVICE_MASK = 0x18
staticconstexpr

Bits [4:3]: service type.

Definition at line 800 of file proto_constants.h.

◆ ACEI_SERVICE_SHIFT

uint8_t esphome::home_io_control::ACEI_SERVICE_SHIFT = 3
staticconstexpr

Shift for service field extraction.

Definition at line 801 of file proto_constants.h.

◆ ACEI_VALID_BIT

uint8_t esphome::home_io_control::ACEI_VALID_BIT = 0x01
staticconstexpr

ACEI byte bit-field definitions.

The ACEI byte is the second byte of the CMD_EXECUTE payload. It encodes the priority level and service class of the command, controlling which commands can override others. Devices reject commands with lower priority than their current locked level (resulting in RESULT_PRIORITY_LEVEL_LOCKED). Bit 0: command validity flag.

Definition at line 797 of file proto_constants.h.

◆ ACTIVE_ISSUE_EXECUTION_ERROR

const char* esphome::home_io_control::ACTIVE_ISSUE_EXECUTION_ERROR = "EXECUTION_ERROR"
inlineconstexpr

Active Issue value while a device's status reports execution state 1: an error occurred during its last move.

Shown until its next state 2..5.

Definition at line 169 of file platform_companion_sensors.h.

◆ ACTIVE_ISSUE_NOT_EXECUTING

const char* esphome::home_io_control::ACTIVE_ISSUE_NOT_EXECUTING = "NOT_EXECUTING"
inlineconstexpr

Active Issue value while a device's status reports execution state 0: it cannot run its last command under the current conditions (example: temperature too high).

Shown until its next state 2..5.

Definition at line 166 of file platform_companion_sensors.h.

◆ ACTIVE_ISSUE_RADIO_STOPPED_RESPONDING

const char* esphome::home_io_control::ACTIVE_ISSUE_RADIO_STOPPED_RESPONDING = "RADIO_STOPPED_RESPONDING"
inlineconstexpr

Active Issue value while the hub's radio has stopped responding, in the style of the symbolic result names the sensor otherwise publishes.

Definition at line 162 of file platform_companion_sensors.h.

◆ ADDRESS_SUFFIX_BROADCAST

uint8_t esphome::home_io_control::ADDRESS_SUFFIX_BROADCAST = 0x3F
staticconstexpr

Suffix for "all devices of this type" broadcast.

Definition at line 87 of file proto_codecs.h.

◆ ADDRESS_SUFFIX_DISCOVERY

uint8_t esphome::home_io_control::ADDRESS_SUFFIX_DISCOVERY = 0x3B
staticconstexpr

Suffix for discovery-related broadcasts.

Definition at line 88 of file proto_codecs.h.

◆ ADDRESS_SUFFIX_DISCOVERY_128

uint8_t esphome::home_io_control::ADDRESS_SUFFIX_DISCOVERY_128 = 0x3D
staticconstexpr

Discovery suffix opening 128 slots.

Definition at line 138 of file proto_codecs.h.

◆ ADDRESS_SUFFIX_DISCOVERY_255

uint8_t esphome::home_io_control::ADDRESS_SUFFIX_DISCOVERY_255 = 0x3E
staticconstexpr

Discovery suffix opening 255 slots.

Definition at line 139 of file proto_codecs.h.

◆ ADDRESS_SUFFIX_DISCOVERY_64

uint8_t esphome::home_io_control::ADDRESS_SUFFIX_DISCOVERY_64 = 0x3C
staticconstexpr

Discovery suffix opening 64 slots.

Definition at line 137 of file proto_codecs.h.

◆ ADDRESS_SUFFIX_MASK

uint8_t esphome::home_io_control::ADDRESS_SUFFIX_MASK = 0x3F
staticconstexpr

Well-known address suffix values in the broadcast address space.

When the first byte of a 3-byte IO-Homecontrol address is 0x00, the low 6 bits of the third byte carry a suffix that identifies the broadcast category. Mask to extract the 6-bit suffix from addr[2].

Definition at line 86 of file proto_codecs.h.

◆ AES_BLOCK_SIZE

uint8_t esphome::home_io_control::AES_BLOCK_SIZE = 16
staticconstexpr

AES block size.

Definition at line 24 of file proto_sizes.h.

◆ AES_KEY_SIZE

uint8_t esphome::home_io_control::AES_KEY_SIZE = 16
staticconstexpr

AES-128 key size.

Definition at line 23 of file proto_sizes.h.

◆ ATT_CLASS_10MS

uint8_t esphome::home_io_control::ATT_CLASS_10MS = 1
staticconstexpr

Response within 10 ms.

Definition at line 896 of file proto_constants.h.

◆ ATT_CLASS_20MS

uint8_t esphome::home_io_control::ATT_CLASS_20MS = 2
staticconstexpr

Response within 20 ms.

Definition at line 897 of file proto_constants.h.

◆ ATT_CLASS_40MS

uint8_t esphome::home_io_control::ATT_CLASS_40MS = 3
staticconstexpr

Response within 40 ms.

Definition at line 898 of file proto_constants.h.

◆ ATT_CLASS_5MS

uint8_t esphome::home_io_control::ATT_CLASS_5MS = 0
staticconstexpr

Actuator Turnaround Time (ATT) class values.

The time within which each node must respond after receiving a command (VELUX KLF 200 API v3.18 §7.4.1.2.4-7, Table 51: "Actuator Turnaround time"). The unit is milliseconds. Extracted from the Multi Information Byte via (flags & DISCOVERY_FLAGS_ATT_MASK) >> DISCOVERY_FLAGS_ATT_SHIFT. Response within 5 ms.

Definition at line 895 of file proto_constants.h.

◆ ATT_CLASS_5MS_TURNAROUND_MS

uint32_t esphome::home_io_control::ATT_CLASS_5MS_TURNAROUND_MS = 5
staticconstexpr

Turnaround time of ATT_CLASS_5MS, the shortest class; att_class_ms() doubles it per class.

Definition at line 902 of file proto_constants.h.

◆ BATTERY_LEVEL_EVENT_DATA_LEN

uint8_t esphome::home_io_control::BATTERY_LEVEL_EVENT_DATA_LEN = 1
staticconstexpr

EVENT_BATTERY_LEVEL_CHANGE data: one level byte.

Definition at line 402 of file proto_constants.h.

◆ BATTERY_LEVEL_MAX_PERCENT

uint8_t esphome::home_io_control::BATTERY_LEVEL_MAX_PERCENT = 100
staticconstexpr

Highest level read as a percentage.

Definition at line 403 of file proto_constants.h.

◆ BINARY_ENTITY_OFF_POSITION

uint8_t esphome::home_io_control::BINARY_ENTITY_OFF_POSITION = 100
staticconstexpr

Position value written for binary OFF commands (light off, switch off, lock lock).

Definition at line 24 of file operation_queue.h.

◆ BINARY_ENTITY_ON_POSITION

uint8_t esphome::home_io_control::BINARY_ENTITY_ON_POSITION = 0
staticconstexpr

Position value written for binary ON commands (light on, switch on, lock unlock).

Definition at line 22 of file operation_queue.h.

◆ BITS_PER_BYTE

uint8_t esphome::home_io_control::BITS_PER_BYTE = 8
staticconstexpr

Number of bits in one protocol byte.

Definition at line 27 of file proto_sizes.h.

◆ BROADCAST_DISCOVER

uint8_t esphome::home_io_control::BROADCAST_DISCOVER[NODE_ID_SIZE] = {0x00, 0x00, 0x3B}
staticconstexpr

Broadcast address for device discovery (0x00003B).

Used as destination in CMD_DISCOVER_REQ frames to trigger all pairable devices to respond.

Definition at line 682 of file proto_constants.h.

◆ BROADCAST_DISCOVER_ALT

uint8_t esphome::home_io_control::BROADCAST_DISCOVER_ALT[NODE_ID_SIZE] = {0x00, 0x00, 0x3F}
staticconstexpr

Alternate discovery / 1W broadcast address (0x00003F).

Used as destination for CMD_DISCOVER_ALT_REQ (0x2E) alternate discovery, and the address on which devices in 1W-triggered pairing mode listen. Distinct from the 2W discovery broadcast BROADCAST_DISCOVER (0x00003B).

Definition at line 688 of file proto_constants.h.

◆ CMD_ACTIVATE_MODE

uint8_t esphome::home_io_control::CMD_ACTIVATE_MODE = 0x01
staticconstexpr

Activate device mode (scene, ventilation) — requires auth.

Definition at line 25 of file proto_constants.h.

◆ CMD_CHALLENGE_REQ

uint8_t esphome::home_io_control::CMD_CHALLENGE_REQ
staticconstexpr
Initial value:
=
0x3C

6-byte random challenge.

Usually a device challenging a controller's command, but the protocol is symmetric and controllers challenge devices too: in tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml a KLR200 issues 0x3C against the device's own CMD_NODE_VERIFY_RESP. The key-extraction responder now answers exactly that inbound direction (KeyExtractionResponder::handle_node_verify_challenge_() in key_extraction_responder.cpp), so both directions are implemented, not just the outbound one.

Definition at line 178 of file proto_constants.h.

◆ CMD_CHALLENGE_RESP

uint8_t esphome::home_io_control::CMD_CHALLENGE_RESP
staticconstexpr
Initial value:
=
0x3D

HMAC proof answering a 0x3C.

Whoever is challenged authenticates its own preceding frame: the transcript is [cmd, data...] of the challenged party's last frame (create_challenge_resp()), never the challenger's. That holds in both directions — the device-side 0x3D in velux_kux100_pairing_full.yaml (over its own 0x37) was recomputed under that installation's recovered key and confirmed before the capture was re-keyed, so it is measured, not assumed by symmetry.

Definition at line 186 of file proto_constants.h.

◆ CMD_DISCOVER_ALT_REQ

uint8_t esphome::home_io_control::CMD_DISCOVER_ALT_REQ
staticconstexpr
Initial value:
=
0x2E

Alternate discovery.

Broadcast (to 0x00003F) draws no response at all on every device this project has real evidence for — a Somfy Izymo dimmer (tests/corpus/captures/discovery/somfy_izymo_dimmer_discovery_alt_no_response.yaml) and a Velux KLR200/KUX100 pair (tests/corpus/captures/discovery/velux_kux100_discovery_alt_broadcast_no_response.yaml) both went unanswered; the older "response is 0x29" guess never had real evidence and appears to have been wrong. Directly addressed to a known device instead of broadcast, it does draw a response, but a 0x3C/0x3D challenge-response followed by CMD_DISCOVER_ALT_RESP (0x2F), not 0x29 — see tests/corpus/captures/discovery/velux_kux100_discovery_alt_addressed_challenge_response.yaml.

Definition at line 114 of file proto_constants.h.

◆ CMD_DISCOVER_ALT_RESP

uint8_t esphome::home_io_control::CMD_DISCOVER_ALT_RESP
staticconstexpr
Initial value:
=
0x2F

Reply to an addressed (non-broadcast) CMD_DISCOVER_ALT_REQ, following a 0x3C/0x3D challenge-response.

See CMD_DISCOVER_ALT_REQ's comment and tests/corpus/captures/discovery/velux_kux100_discovery_alt_addressed_challenge_response.yaml — the only capture this project has of it. Not otherwise used anywhere in this codebase (no dispatch logic added).

Definition at line 125 of file proto_constants.h.

◆ CMD_DISCOVER_CONFIRM

uint8_t esphome::home_io_control::CMD_DISCOVER_CONFIRM = 0x2C
staticconstexpr

Confirm discovery to device.

Definition at line 112 of file proto_constants.h.

◆ CMD_DISCOVER_CONFIRM_ACK

uint8_t esphome::home_io_control::CMD_DISCOVER_CONFIRM_ACK = 0x2D
staticconstexpr

Device acknowledges confirmation.

Definition at line 113 of file proto_constants.h.

◆ CMD_DISCOVER_REQ

uint8_t esphome::home_io_control::CMD_DISCOVER_REQ = 0x28
staticconstexpr

Broadcast discovery request.

Definition at line 74 of file proto_constants.h.

◆ CMD_DISCOVER_RESP

uint8_t esphome::home_io_control::CMD_DISCOVER_RESP = 0x29
staticconstexpr

Device responds with its ID and type.

Definition at line 75 of file proto_constants.h.

◆ CMD_DISCOVER_SPE_REQ

uint8_t esphome::home_io_control::CMD_DISCOVER_SPE_REQ
staticconstexpr
Initial value:
=
0x2A

Broadcast roll-call answered by every device that already holds this controller's system key, regardless of device type.

A device that holds no key yet — one in learning mode, mid-pairing — has nothing to authenticate the request against and stays silent, so this enumerates already-managed devices and cannot discover new ones. Do not offer it as a pairing-discovery command: it can only ever add replies from devices already paired, never help reach an unpaired one. The 12-byte payload authenticates itself in a single frame — 6 random challenge bytes followed by a 6-byte HMAC over the command byte alone — instead of the usual 0x3C/0x3D round trip, which is what lets it be broadcast; create_discovery_request() (proto_commands.cpp) builds it to match. Real captured bytes: tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml (request shape, HMAC recomputed under that installation's key before the capture was re-keyed) and tests/corpus/captures/discovery/somfy_awning_discovery_spe_paired_rollcall.yaml plus tests/corpus/captures/discovery/somfy_izymo_dimmer_discovery_spe_paired_rollcall.yaml (two awnings and a dimmer answering one broadcast). No dispatch path consumes the reply yet; classify_pairing_discovery_response() accepts only 0x29 and must not be extended to accept 0x2B, because a roll-call reply from an already-paired device is not a newly-discovered one and must never enter the pairing flow. See CMD_DISCOVER_SPE_RESP.

Definition at line 76 of file proto_constants.h.

◆ CMD_DISCOVER_SPE_RESP

uint8_t esphome::home_io_control::CMD_DISCOVER_SPE_RESP
staticconstexpr
Initial value:
=
0x2B

Roll-call reply to CMD_DISCOVER_SPE_REQ, sent only by devices that already hold the requesting controller's system key.

The payload is the same DISCOVERY_RESP_FULL_SIZE layout as a CMD_DISCOVER_RESP (0x29), so every DISCOVERY_RESP_*_OFFSET constant below applies unchanged — packed type/subtype at data[0..1], backbone address at DISCOVERY_RESP_BACKBONE_OFFSET, manufacturer at DISCOVERY_RESP_MANUFACTURER_OFFSET, Multi Information Byte at DISCOVERY_RESP_FLAGS_OFFSET, timestamp at DISCOVERY_RESP_TIMESTAMP_OFFSET — and PairingEngine::parse_device_from_discovery() decodes a 0x2B correctly with no special-casing. Real captured replies (two awnings and a dimmer) are in tests/corpus/captures/discovery/somfy_awning_discovery_spe_paired_rollcall.yaml and tests/corpus/captures/discovery/somfy_izymo_dimmer_discovery_spe_paired_rollcall.yaml. The timestamp is the field that advances between successive replies from one device; it is not a response to the request's random challenge, since that HMAC covers only the constant command byte. Treat a reply as self-description, not proof of identity: nothing in it is bound to the request, so report it, never act on it. No dispatch path consumes a 0x2B yet.

Definition at line 96 of file proto_constants.h.

◆ CMD_ERROR_RESP

uint8_t esphome::home_io_control::CMD_ERROR_RESP = 0xFE
staticconstexpr

Error response to any command.

Definition at line 423 of file proto_constants.h.

◆ CMD_EXECUTE

uint8_t esphome::home_io_control::CMD_EXECUTE = 0x00
staticconstexpr

Set position/open/close/stop — requires authentication.

Definition at line 24 of file proto_constants.h.

◆ CMD_GET_GENERAL_INFO3

uint8_t esphome::home_io_control::CMD_GET_GENERAL_INFO3
staticconstexpr
Initial value:
=
0x58

Observed on the wire (tests/corpus/captures/probe/velux_kig300_probe_capability_burst.yaml) with no payload.

Content undecoded. Not sent or handled anywhere in this codebase.

Definition at line 332 of file proto_constants.h.

◆ CMD_GET_GENERAL_INFO3_RESP

uint8_t esphome::home_io_control::CMD_GET_GENERAL_INFO3_RESP
staticconstexpr
Initial value:
=
0x59

Never captured on our own wire.

This constant exists so a received 0x59 frame renders by name instead of as UNKNOWN_CMD. Not sent or handled anywhere in this codebase.

Definition at line 335 of file proto_constants.h.

◆ CMD_GET_INFO1

uint8_t esphome::home_io_control::CMD_GET_INFO1
staticconstexpr
Initial value:
=
0x54

Request device general info 1.

Sent by the get_info1 diagnostic probe (docs/diagnostic-probes.md, ADR 0024); field-observed on air from a real hub.

Definition at line 317 of file proto_constants.h.

◆ CMD_GET_INFO1_RESP

uint8_t esphome::home_io_control::CMD_GET_INFO1_RESP
staticconstexpr
Initial value:
=
0x55

Device general info 1 response.

One capture in the corpus (tests/corpus/captures/probe/velux_window_probe_metadata_replies.yaml); nothing decodes it. Accepted by the soft-PHY so a probe reply is not dropped as an unknown command.

Definition at line 320 of file proto_constants.h.

◆ CMD_GET_INFO2

uint8_t esphome::home_io_control::CMD_GET_INFO2 = 0x56
staticconstexpr

Request device type/model info.

The only thing that sends it is the get_info2 diagnostic probe; the corpus has no fixture for the request itself – every 0x57 capture on hand is a reply to someone else's request. Closing this needs a fresh device-add on owned hardware, ingested with --rekey (tests/corpus/README.md).

Definition at line 325 of file proto_constants.h.

◆ CMD_GET_INFO2_RESP

uint8_t esphome::home_io_control::CMD_GET_INFO2_RESP = 0x57
staticconstexpr

Device type/model response.

Definition at line 331 of file proto_constants.h.

◆ CMD_GET_NAME

uint8_t esphome::home_io_control::CMD_GET_NAME = 0x50
staticconstexpr

Request device name.

Definition at line 313 of file proto_constants.h.

◆ CMD_GET_NAME_RESP

uint8_t esphome::home_io_control::CMD_GET_NAME_RESP = 0x51
staticconstexpr

Device name response.

Definition at line 314 of file proto_constants.h.

◆ CMD_IDENTIFY

uint8_t esphome::home_io_control::CMD_IDENTIFY = 0x1E
staticconstexpr

Device physical identification / jog — requires authentication.

Definition at line 53 of file proto_constants.h.

◆ CMD_KEY_CONFIRM

uint8_t esphome::home_io_control::CMD_KEY_CONFIRM = 0x33
staticconstexpr

Device confirms key was received.

Definition at line 146 of file proto_constants.h.

◆ CMD_KEY_INIT

uint8_t esphome::home_io_control::CMD_KEY_INIT = 0x31
staticconstexpr

Initiate key transfer to device.

Definition at line 144 of file proto_constants.h.

◆ CMD_KEY_TRANSFER

uint8_t esphome::home_io_control::CMD_KEY_TRANSFER = 0x32
staticconstexpr

Send encrypted system key to device.

Definition at line 145 of file proto_constants.h.

◆ CMD_LAUNCH_KEY_TRANSFER

uint8_t esphome::home_io_control::CMD_LAUNCH_KEY_TRANSFER
staticconstexpr
Initial value:
=
0x38

Key transfer request ("pull"): a controller asks a device for its system key.

Payload is 6 random bytes; the device answers with a CMD_KEY_TRANSFER (0x32) holding its key under the transfer key, the IV built from {0x38} and those 6 bytes. Sent only by pairing_key_transfer: pull (create_launch_key_transfer()); not yet seen on air in this project's corpus.

Definition at line 170 of file proto_constants.h.

◆ CMD_LIMITATION_STATUS_REQ

uint8_t esphome::home_io_control::CMD_LIMITATION_STATUS_REQ
staticconstexpr
Initial value:
=
0x25

Limitation read: asks a device for its resulting minimum or maximum position limit, the value a protective function such as rain protection imposes (CMD_LIMITATION_STATUS_RESP).

Heard from a VELUX KLF 200 gateway to VELUX windows (issue #98) every five minutes, minimum first, then maximum; its purpose follows the KLF 200 API specification v3.18, section 10.5 (get limitation / limitation status). The three data bytes are assumed to be [selector] [parameter id] [00] (see LimitationType); the layout is not yet confirmed under an active limitation. Build it with create_limitation_status_read().

Definition at line 59 of file proto_constants.h.

◆ CMD_LIMITATION_STATUS_RESP

uint8_t esphome::home_io_control::CMD_LIMITATION_STATUS_RESP
staticconstexpr
Initial value:
=
0x26

Reply to CMD_LIMITATION_STATUS_REQ.

Five data bytes, assumed to be [parameter id] [value hi] [value lo] [originator] [time]; decode_limitation_status() documents the layout. Captured: the unlimited case (0 % and 100 %) and a rain-limited window (minimum 93 %, with originator and remaining time both 00).

Definition at line 67 of file proto_constants.h.

◆ CMD_NODE_VERIFY_REQ

uint8_t esphome::home_io_control::CMD_NODE_VERIFY_REQ
staticconstexpr
Initial value:
=
0x36

Node verification request: a controller checks that a node belongs to its system.

The node answers with its address (CMD_NODE_VERIFY_RESP) and the controller then challenges that answer, so only a node holding the system key passes. It is what closes a key transfer (tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml, a Velux KLR200) and what a controller sends to nodes it already knows, with no pairing in progress (a Velux KIG300 probing a Somfy dimmer in tests/corpus/captures/probe/velux_kig300_probe_capability_burst.yaml; a KLR300 checking each node after a roll-call in tests/corpus/captures/discovery/velux_klr300_discovery_rollcall_node_verification.yaml). Answered by the key-extraction responder's create_node_verify_resp_device_role() (handle_node_verify_req_() in key_extraction_responder.cpp).

Definition at line 149 of file proto_constants.h.

◆ CMD_NODE_VERIFY_RESP

uint8_t esphome::home_io_control::CMD_NODE_VERIFY_RESP
staticconstexpr
Initial value:
=
0x37

Node verification response: the device returns its own 3-byte backbone address, byte-identical to the one it reported at data[2..4] (DISCOVERY_RESP_BACKBONE_OFFSET) of its CMD_DISCOVER_RESP earlier in the same session — an independent confirmation of that offset.

In tests/corpus/captures/pairing/velux_kux100_pairing_full.yaml a Velux KLR200 closes pairing with 0x36 and then challenges the 0x37 it gets back (see CMD_CHALLENGE_REQ). Sent by create_node_verify_resp_device_role() (proto_commands.h/.cpp), the key-extraction responder's answer to CMD_NODE_VERIFY_REQ.

Definition at line 161 of file proto_constants.h.

◆ CMD_ONEWAY_ADD_CONTROLLER

uint8_t esphome::home_io_control::CMD_ONEWAY_ADD_CONTROLLER
staticconstexpr
Initial value:
=
0x30

1W "add controller" — a 1W device broadcasts this while its key-copy gesture is active, handing its network's wrapped system key to whichever controller is listening.

Its 20-byte declared payload (enc_key[16] + man_id[1] + data[1] + sequence[2]) plus a genuine 6-byte MAC does not fit inside CTRL0's 5-bit length field together (29 + 6 = 35, unrepresentable in 5 bits), so the MAC rides after the declared length instead, still under the CRC — see IoFrame::has_mac and frame_carries_mac_trailer() (proto_frame.h). Reference: tests/corpus/captures/enrollment/reference_1w_enrollment_add_controller_kat.yaml.

Definition at line 131 of file proto_constants.h.

◆ CMD_ONEWAY_REMOVE

uint8_t esphome::home_io_control::CMD_ONEWAY_REMOVE = 0x39
staticconstexpr

1W "remove controller" (un-pair a 1W remote from a device); same payload shape as 0x2E.

Definition at line 140 of file proto_constants.h.

◆ CMD_PRIORITY_LEVEL_REQ

uint8_t esphome::home_io_control::CMD_PRIORITY_LEVEL_REQ
staticconstexpr
Initial value:
=
0x19

Priority-level (lock) query: a one-byte priority level in, the device's lock state for that level out (CMD_PRIORITY_LEVEL_RESP).

io-homecontrol arbitrates control by priority level (0-7), and protective functions such as wind protection hold an actuator by locking it at a level. The only capture is tests/corpus/captures/exchange/somfy_awning_exchange_priority_level_sx1276.yaml (issue #27): a real Somfy TaHoma Switch sends 0x02 and then 0x04 to a Sunea io screen it owns, ~212 ms apart. That level-by-level walk fits a lock query and fits it better than the "inject sensor value" reading this opcode was once filed under. Still a working interpretation: no reply has been captured, so the payload semantics are unconfirmed. No builder or dispatch path exists in this codebase.

Definition at line 38 of file proto_constants.h.

◆ CMD_PRIORITY_LEVEL_RESP

uint8_t esphome::home_io_control::CMD_PRIORITY_LEVEL_RESP = 0x1A
staticconstexpr

Reply to CMD_PRIORITY_LEVEL_REQ.

Never observed on the wire.

Definition at line 49 of file proto_constants.h.

◆ CMD_PRIVATE

uint8_t esphome::home_io_control::CMD_PRIVATE = 0x03
staticconstexpr

Get device status — no authentication needed.

Definition at line 26 of file proto_constants.h.

◆ CMD_PRIVATE2

uint8_t esphome::home_io_control::CMD_PRIVATE2
staticconstexpr
Initial value:
=
0x0C

Content otherwise undecoded by the wire parser.

Its request payload matches CMD_EXECUTE's POS_FAVORITE/POS_VENT_MODIFIER stored-position selector with the execution prefix stripped, so it reads like a stored-position readback rather than a live telemetry poll — see the somfy_rs100_* / somfy_oximo40_* captures and multi_somfy_probe_private2_{long_form,short_form}.yaml for real request/response pairs. Not handled by any dispatch path in this codebase.

Definition at line 28 of file proto_constants.h.

◆ CMD_PRIVATE2_RESP

uint8_t esphome::home_io_control::CMD_PRIVATE2_RESP = 0x0D
staticconstexpr

Response to CMD_PRIVATE2. See CMD_PRIVATE2's comment.

Definition at line 35 of file proto_constants.h.

◆ CMD_PRIVATE_RESP

uint8_t esphome::home_io_control::CMD_PRIVATE_RESP = 0x04
staticconstexpr

Response to 0x00 and 0x03 (contains position data).

Definition at line 27 of file proto_constants.h.

◆ CMD_READ_GROUPS

uint8_t esphome::home_io_control::CMD_READ_GROUPS
staticconstexpr
Initial value:
=
0xF1

Named "Actuator: Read Groups" / "ActuatorAnyConfigIsLocal" (uncertain) / "Service ACK" — three candidate names, one itself flagged uncertain.

Never observed in our corpus or in any field log, and not sent or handled anywhere in this codebase.

Definition at line 410 of file proto_constants.h.

◆ CMD_REBOOT

uint8_t esphome::home_io_control::CMD_REBOOT
staticconstexpr
Initial value:
=
0xF2

Named "Reboot" / "Service Status" — two candidate names, one of them destructive-sounding, on no field evidence at all.

Never observed in our corpus or in any field log, and not sent or handled anywhere in this codebase; treat the "reboot" reading with particular caution — it is a guess.

Definition at line 414 of file proto_constants.h.

◆ CMD_SEND_RAW_MESSAGE

uint8_t esphome::home_io_control::CMD_SEND_RAW_MESSAGE
staticconstexpr
Initial value:
=
0xF0

Named "Send Raw Message" / "Find Hardware" — two candidate names, neither settled.

Never observed in our corpus or in any field log, and not sent or handled anywhere in this codebase.

Definition at line 406 of file proto_constants.h.

◆ CMD_SERVICE_STATUS_ACK

uint8_t esphome::home_io_control::CMD_SERVICE_STATUS_ACK = 0xF3
staticconstexpr

No description available at all for this opcode, not even a hedge.

Never observed in our corpus or in any field log, and not sent or handled anywhere in this codebase.

Definition at line 419 of file proto_constants.h.

◆ CMD_SET_CONFIG1

uint8_t esphome::home_io_control::CMD_SET_CONFIG1 = 0x6F
staticconstexpr

Configure device to auto-send status updates.

Definition at line 341 of file proto_constants.h.

◆ CMD_SET_CONFIG1_RESP

uint8_t esphome::home_io_control::CMD_SET_CONFIG1_RESP
staticconstexpr
Initial value:
=
0x70

Config response; meaning undecoded, never captured.

Never applied to device state: the device-event recorder (IOHomeControlComponent::record_device_event_()) logs it raw. Logged under the name SET_CONFIG1_RESP whatever it carries.

Definition at line 342 of file proto_constants.h.

◆ CMD_SET_NAME

uint8_t esphome::home_io_control::CMD_SET_NAME = 0x52
staticconstexpr

Set device name (authenticated).

Definition at line 315 of file proto_constants.h.

◆ CMD_SET_NAME_RESP

uint8_t esphome::home_io_control::CMD_SET_NAME_RESP = 0x53
staticconstexpr

Device-name write response.

Definition at line 316 of file proto_constants.h.

◆ CMD_STATUS_UPDATE

uint8_t esphome::home_io_control::CMD_STATUS_UPDATE = 0x71
staticconstexpr

Device status update, sent by the device on its own (authenticated, acknowledged with CMD_STATUS_UPDATE_RESP) or as the answer to a CMD_SUBSCRIBE_EVENT.

The payload is a flags byte, a status byte (battery in STATUS_BATTERY_MASK), a big-endian event id, the length of the event data, then the event data: see decode_status_update_event(). Position updates carry EVENT_STATUS_CHANGE.

Definition at line 359 of file proto_constants.h.

◆ CMD_STATUS_UPDATE_RESP

uint8_t esphome::home_io_control::CMD_STATUS_UPDATE_RESP = 0x72
staticconstexpr

Acknowledge status update.

Definition at line 360 of file proto_constants.h.

◆ CMD_SUBSCRIBE_EVENT

uint8_t esphome::home_io_control::CMD_SUBSCRIBE_EVENT
staticconstexpr
Initial value:
=
0x73

Subscribe a device to one event: payload <action> <event hi> <event lo>, action EVENT_SUBSCRIPTION_SUBSCRIBE or EVENT_SUBSCRIPTION_UNSUBSCRIBE.

Sent by the subscribe_event / unsubscribe_event diagnostic probes (docs/diagnostic-probes.md, ADR 0060) and by the opt-in sun level and battery level polls (ADR 0064), which read the answer. An RS100 Solar io answered a subscription to EVENT_LOCAL_SENSOR with a CMD_STATUS_UPDATE carrying that event (tests/corpus/captures/probe/somfy_rs100_probe_subscribe_event_sx1262.yaml).

Definition at line 346 of file proto_constants.h.

◆ CMD_TRANSFER_OPEN

uint8_t esphome::home_io_control::CMD_TRANSFER_OPEN
staticconstexpr
Initial value:
=
0x46

Opens a read of one object: nine payload bytes <originator> <class> 00 <id hi> <id lo> < 00 00 00 00.

A Somfy Izymo answered it unauthenticated and challenged the block reads. Sent only by the read_object diagnostic probe (ADR 0062) and only for the objects in TRANSFER_OBJECT_*; create_transfer_open() refuses every other id. The sibling 0x48 opens a write and has no builder anywhere in this codebase.

Definition at line 195 of file proto_constants.h.

◆ CMD_TRANSFER_OPEN_RESP

uint8_t esphome::home_io_control::CMD_TRANSFER_OPEN_RESP
staticconstexpr
Initial value:
=
0x47

Answer to CMD_TRANSFER_OPEN: <handle> <status> <length u16 BE>.

Status 0 = accepted, 1 = adapted, anything else an error; length 0 means there is nothing to read.

Definition at line 201 of file proto_constants.h.

◆ CMD_UNKNOWN4A_REQ

uint8_t esphome::home_io_control::CMD_UNKNOWN4A_REQ
staticconstexpr
Initial value:
=
0x4A

Reads chunk <handle><chunk u16> of a buffer opened by CMD_TRANSFER_OPEN, counting down from ceil(length/18) to 1, then <handle> 00 00 closes it.

Seen on air between two VELUX KLR 200 controllers in a copy session. Sent only by the read_object probe, through create_transfer_read(), whose payload is exactly these three bytes: a 0x4A that carries data is the write direction of the block and cannot be built here (ADR 0062).

Definition at line 204 of file proto_constants.h.

◆ CMD_UNKNOWN4A_RESP

uint8_t esphome::home_io_control::CMD_UNKNOWN4A_RESP
staticconstexpr
Initial value:
=
0x4B

Echoes <handle><chunk> followed by up to 18 bytes of data (none in the close echo).

Also seen answering an ON_OFF_SWITCH-type device's traffic (tests/corpus/captures/probe/velux_kig300_probe_capability_burst.yaml), where the 01 00 00 payload has the shape of a close echo.

Definition at line 210 of file proto_constants.h.

◆ CMD_WRITE_PRIVATE

uint8_t esphome::home_io_control::CMD_WRITE_PRIVATE = 0x20
staticconstexpr

Write private register (climate/heating devices).

Definition at line 55 of file proto_constants.h.

◆ CMD_WRITE_PRIVATE_ACK

uint8_t esphome::home_io_control::CMD_WRITE_PRIVATE_ACK = 0x21
staticconstexpr

Acknowledgment to CMD_WRITE_PRIVATE.

Definition at line 56 of file proto_constants.h.

◆ COLD_BROADCAST_REPLY_PREAMBLE

uint16_t esphome::home_io_control::COLD_BROADCAST_REPLY_PREAMBLE = 80
staticconstexpr

Default for TuningConfig::cold_broadcast_reply_preamble — preamble for a broadcast reply to a frame the peer caught via a rotating/hopping listen (currently: only the key-extraction responder's 0x29, the sole start=true frame any device-role builder in this codebase produces) — long enough to be reliably caught by a hopping receiver, short enough that sending it doesn't block the loop the way LONG_PREAMBLE does.

Sized above the ~13.9 ms real-device discovery-reply preamble this project's own SX1262/LR1121 discovery hop-slice tuning was measured against (issue #65 SDR analysis) — that measurement is what "reliably caught by a hopping receiver" is calibrated to here. 80 bytes ≈ 16.7 ms at the protocol's 38400 bps line rate (soft_phy_air_time_us()), budgeted as a starting point, not yet hardware-validated for this exact chip/scenario combination. Runtime-tunable via cold_broadcast_reply_preamble for exactly that reason.

Definition at line 58 of file proto_timing.h.

◆ CRC_LSB_MASK

uint16_t esphome::home_io_control::CRC_LSB_MASK = 0x0001
staticconstexpr

Least-significant-bit mask for reflected CRC update.

Definition at line 678 of file proto_constants.h.

◆ CRC_POLYNOMIAL_REVERSED

uint16_t esphome::home_io_control::CRC_POLYNOMIAL_REVERSED = 0x8408
staticconstexpr

Reversed CRC-CCITT polynomial used by IO-homecontrol.

Definition at line 677 of file proto_constants.h.

◆ CTRL0_END

uint8_t esphome::home_io_control::CTRL0_END = 0x80
staticconstexpr

Control byte 0 (CTRL0) bit definitions.

CTRL0 encodes frame flags and the total frame length. Bits [4:0] = frame_length - 1 (so 0x08 means 9 bytes total).

  • START (bit 6): first frame in an exchange. Its TX preamble depends on CTRL1_LOW_POWER, not on START alone: a low-power target gets the 1024-byte wake-up burst (unless it is believed awake, see ADR 0040), others a normal preamble.
  • END (bit 7): last frame in an exchange; set on responses and command completions.
  • 1W (bit 5): 1=OneWay protocol (no response expected), 0=TwoWay (response expected). For 2W operation, the controller sets START on initial command and device replies with END; subsequent frames in an authenticated exchange also carry END. Bit 7: last frame in exchange

Definition at line 42 of file proto_frame.h.

◆ CTRL0_LENGTH_MASK

uint8_t esphome::home_io_control::CTRL0_LENGTH_MASK = 0x1F
staticconstexpr

Bits [4:0]: frame length - 1.

Definition at line 45 of file proto_frame.h.

◆ CTRL0_PROTOCOL_1W

uint8_t esphome::home_io_control::CTRL0_PROTOCOL_1W = 0x20
staticconstexpr

Bit 5: 1=OneWay protocol, 0=TwoWay protocol.

Definition at line 44 of file proto_frame.h.

◆ CTRL0_START

uint8_t esphome::home_io_control::CTRL0_START = 0x40
staticconstexpr

Bit 6: first frame in exchange.

Definition at line 43 of file proto_frame.h.

◆ CTRL1_ACK

uint8_t esphome::home_io_control::CTRL1_ACK = 0x10
staticconstexpr

Bit 4: sender can handle 2W responses (ACK-capable).

Definition at line 70 of file proto_frame.h.

◆ CTRL1_BEACON

uint8_t esphome::home_io_control::CTRL1_BEACON = 0x80
staticconstexpr

Bit 7: beacon announcement frame.

Definition at line 73 of file proto_frame.h.

◆ CTRL1_LOW_POWER

uint8_t esphome::home_io_control::CTRL1_LOW_POWER = 0x20
staticconstexpr

Bit 5: low-power device (e.g., solar-powered).

Definition at line 71 of file proto_frame.h.

◆ CTRL1_PRIORITY

uint8_t esphome::home_io_control::CTRL1_PRIORITY = 0x04
staticconstexpr

Bit 2: high-priority frame.

Definition at line 69 of file proto_frame.h.

◆ CTRL1_ROUTED

uint8_t esphome::home_io_control::CTRL1_ROUTED = 0x40
staticconstexpr

Bit 6: frame was relayed through a repeater.

Definition at line 72 of file proto_frame.h.

◆ CTRL1_VERSION_MASK

uint8_t esphome::home_io_control::CTRL1_VERSION_MASK = 0x03
staticconstexpr

Ties FRAME_MAX_DECLARED_SIZE (proto_sizes.h) to the mask that actually defines it.

The two can't be expressed as one expression across the header boundary (proto_sizes.h can't include this header back without a cycle), so this assert is the drift guard instead. Control byte 1 (CTRL1) bit definitions. CTRL1 carries protocol metadata flags that describe the frame's routing, power mode, and priority characteristics.

  • VERSION (bits [1:0]): protocol version number (usually 0 for current devices).
  • PRIORITY (bit 2): marks a high-priority frame (e.g., discovery, security commands).
  • ACK (bit 4): sender can handle 2W responses. NOT set by default on any outbound frame — see init_frame() below for why. Scoped exceptions: the discovery broadcast phase, gated by the opt-in pairing_discovery_ack_capable tuning knob (tuning_config.h), off by default; and create_discover_confirm()'s ack parameter, gated by the pairing_discover_confirm tuning knob (send_with_ack, off by default).
  • LOW_POWER (bit 5): device is battery/solar powered; may sleep and requires long preamble to wake. Set from the target's per-device YAML low_power class (default false); drives both this bit and the start-frame preamble (see proto_commands.h, exchange_engine.cpp).
  • ROUTED (bit 6): frame was relayed through a repeater node rather than direct.
  • BEACON (bit 7): beacon announcement frame (device presence advertisement). Bits [1:0]: protocol version (usually 0).

Definition at line 68 of file proto_frame.h.

◆ DEFAULT_PA_PIN_PA_BOOST

uint8_t esphome::home_io_control::DEFAULT_PA_PIN_PA_BOOST = 0x80
inlineconstexpr

SX1276 PA_CONFIG selector for the PA_BOOST output path.

Definition at line 64 of file hub_core.h.

◆ DEFAULT_SETTLE_POLL_DELAY_MS

uint32_t esphome::home_io_control::DEFAULT_SETTLE_POLL_DELAY_MS = 3000
staticconstexpr

Default follow-up settle-poll delay used while a device may still be moving, when no explicit poll interval is configured (interval_ms == 0).

Short on purpose so devices without a remaining time are re-checked promptly and the motion-tracking loop terminates soon after the device comes to rest.

Definition at line 111 of file status_poll_policy.h.

◆ DEFAULT_TCXO_VOLTAGE_SETTING_1P8V

uint8_t esphome::home_io_control::DEFAULT_TCXO_VOLTAGE_SETTING_1P8V = 0x02
inlineconstexpr

0-based TCXO voltage code for 1.8 V, passed verbatim to the SX1262/LR1121 chip.

Definition at line 65 of file hub_core.h.

◆ DEFAULT_TX_POWER_DBM

uint8_t esphome::home_io_control::DEFAULT_TX_POWER_DBM = 17
inlineconstexpr

Default TX power used unless YAML overrides it.

Definition at line 63 of file hub_core.h.

◆ DEVICE_ERROR_STR_SIZE

size_t esphome::home_io_control::DEVICE_ERROR_STR_SIZE = 160
staticconstexpr

Buffer size that always fits format_device_error_bits()'s longest output for either chip (every named bit, |-joined, plus an UNKNOWN_0x%04X tail).

Definition at line 179 of file radio_soft_phy_driver_base.h.

◆ DEVICE_METADATA_SIZE

uint8_t esphome::home_io_control::DEVICE_METADATA_SIZE = 2
staticconstexpr

Packed device metadata uses two bytes where the high 8 bits carry the upper type bits and the low byte carries both the remaining type bits and the 6-bit manufacturer subtype.

Definition at line 390 of file proto_device_model.h.

◆ DEVICE_NAME_BUFFER_SIZE

uint8_t esphome::home_io_control::DEVICE_NAME_BUFFER_SIZE = 32
staticconstexpr

Device name storage including null terminator.

Definition at line 403 of file proto_device_model.h.

◆ DEVICE_NAME_WRITE_CHAR_LIMIT

uint8_t esphome::home_io_control::DEVICE_NAME_WRITE_CHAR_LIMIT = 15
staticconstexpr

Reference write limit before the trailing null.

Definition at line 29 of file proto_codecs.h.

◆ DEVICE_NAME_WRITE_PAYLOAD_SIZE

uint8_t esphome::home_io_control::DEVICE_NAME_WRITE_PAYLOAD_SIZE
staticconstexpr
Initial value:
=
static constexpr uint8_t DEVICE_NAME_WRITE_CHAR_LIMIT
Reference write limit before the trailing null.

Fixed write payload: 15 visible chars plus trailing null/padding.

Definition at line 30 of file proto_codecs.h.

◆ DEVICE_SUBTYPE_MASK

uint8_t esphome::home_io_control::DEVICE_SUBTYPE_MASK = 0x3F
staticconstexpr

Definition at line 394 of file proto_device_model.h.

◆ DEVICE_TYPE_HIGH_BITS_SHIFT

uint8_t esphome::home_io_control::DEVICE_TYPE_HIGH_BITS_SHIFT = 6
staticconstexpr

Definition at line 392 of file proto_device_model.h.

◆ DEVICE_TYPE_LOW_BITS_SHIFT

uint8_t esphome::home_io_control::DEVICE_TYPE_LOW_BITS_SHIFT = 2
staticconstexpr

Definition at line 391 of file proto_device_model.h.

◆ DEVICE_TYPE_WIRE_MASK

uint16_t esphome::home_io_control::DEVICE_TYPE_WIRE_MASK = 0x3FF
staticconstexpr

The node-type wire field is ten bits wide.

Definition at line 393 of file proto_device_model.h.

◆ DHW_AWAY_UNTIL_TURNED_OFF

uint8_t esphome::home_io_control::DHW_AWAY_UNTIL_TURNED_OFF = 255
staticconstexpr

Duration an away start writes to register 0x0005: 255, which the heater itself holds for a holiday with no end date (issue #198), as Home Assistant's away mode (no end date) means.

Definition at line 63 of file proto_dhw.h.

◆ DHW_BOOST_START_DURATION

uint8_t esphome::home_io_control::DHW_BOOST_START_DURATION = 1
staticconstexpr

Duration a boost start writes to register 0x0000: the value read during a boost started on the display, the only non-zero value seen.

Definition at line 60 of file proto_dhw.h.

◆ DHW_CAPABILITY_AWAY

uint16_t esphome::home_io_control::DHW_CAPABILITY_AWAY = 1U << 2U
staticconstexpr

FP15 bit 2: the heater supports away (absence).

Definition at line 110 of file proto_dhw.h.

◆ DHW_CAPABILITY_BOOST

uint16_t esphome::home_io_control::DHW_CAPABILITY_BOOST = 1U << 3U
staticconstexpr

FP15 bit 3: the heater supports boost (relaunch).

Definition at line 112 of file proto_dhw.h.

◆ DHW_FP_ALSO_ASKED

uint8_t esphome::home_io_control::DHW_FP_ALSO_ASKED = 10
staticconstexpr

Functional parameter 10: no decision uses it; the status read asks for it so the read is exactly the one the heater has been seen answering (FPI2 43, issue #198).

The decoder skips its value.

Definition at line 104 of file proto_dhw.h.

◆ DHW_FP_CAPABILITIES

uint8_t esphome::home_io_control::DHW_FP_CAPABILITIES = 15
staticconstexpr

Functional parameter that carries the operating-mode capabilities (DHW_CAPABILITY_* bits).

Definition at line 99 of file proto_dhw.h.

◆ DHW_FP_OPERATING_MODE

uint8_t esphome::home_io_control::DHW_FP_OPERATING_MODE = 16
staticconstexpr

Functional parameter that carries the operating-mode word (DHW_OPERATING_MODE_*).

Definition at line 101 of file proto_dhw.h.

◆ DHW_OPERATING_MODE_AWAY_ON

uint16_t esphome::home_io_control::DHW_OPERATING_MODE_AWAY_ON = 0x4600
staticconstexpr

FP16 that starts away (holiday) and ends a boost.

Definition at line 95 of file proto_dhw.h.

◆ DHW_OPERATING_MODE_BOOST_ON

uint16_t esphome::home_io_control::DHW_OPERATING_MODE_BOOST_ON = 0x4900
staticconstexpr

FP16 that starts a boost and ends away. Bit 14 is set as the heater reports it.

Definition at line 91 of file proto_dhw.h.

◆ DHW_OPERATING_MODE_OFF

uint16_t esphome::home_io_control::DHW_OPERATING_MODE_OFF = 0x4A00
staticconstexpr

FP16 that ends a boost and ends away: both fields off.

Definition at line 93 of file proto_dhw.h.

◆ DHW_REG_AWAY_DURATION

uint8_t esphome::home_io_control::DHW_REG_AWAY_DURATION = 0x05
staticconstexpr

Register 0x0005: away (holiday) duration, one byte: the days the holiday still runs.

It read 0 at rest and with a holiday scheduled for later, 4 and 2 four and two days before the end, and 255 for a holiday set on the display with no end date (issue #198).

Definition at line 57 of file proto_dhw.h.

◆ DHW_REG_BOOST_DURATION

uint8_t esphome::home_io_control::DHW_REG_BOOST_DURATION = 0x00
staticconstexpr

Register 0x0000: boost duration, one byte.

It read 1 during a boost started on the display and 0 before and after it (issue #198). A duration is not the state: the state is FP16 (DhwOperatingStatus).

Definition at line 53 of file proto_dhw.h.

◆ DHW_REG_BRAND

uint8_t esphome::home_io_control::DHW_REG_BRAND = 0x07
staticconstexpr

Register 0x0007: one ASCII letter naming the brand (41 = 'A' on the Calypso).

Definition at line 70 of file proto_dhw.h.

◆ DHW_REG_ENERGY

uint8_t esphome::home_io_control::DHW_REG_ENERGY = 0x04
staticconstexpr

Register 0x0004: energy consumed since the heater was installed, 32-bit big-endian, Wh.

Matches the display's "since start" total to the kWh, and rose while the heat pump ran.

Definition at line 66 of file proto_dhw.h.

◆ DHW_REG_HEATING

uint8_t esphome::home_io_control::DHW_REG_HEATING = 0x21
staticconstexpr

Register 0x0021: what is heating, eight bytes (DhwHeatingState).

Definition at line 75 of file proto_dhw.h.

◆ DHW_REG_MODE

uint8_t esphome::home_io_control::DHW_REG_MODE = 0x06
staticconstexpr

Register 0x0006: operating mode (DhwMode), one byte. Followed the display in every run.

Definition at line 68 of file proto_dhw.h.

◆ DHW_REG_WATER_TEMPERATURE

uint8_t esphome::home_io_control::DHW_REG_WATER_TEMPERATURE = 0x17
staticconstexpr

Register 0x0017: water temperature, signed 16-bit big-endian tenths of a degree in the first two value bytes.

Four more bytes follow that never changed; they are not decoded.

Definition at line 73 of file proto_dhw.h.

◆ DHW_REGISTER_HIGH_BYTE

uint8_t esphome::home_io_control::DHW_REGISTER_HIGH_BYTE = 0x00
staticconstexpr

High byte of every register of a domestic hot water heater (family 0x00).

Definition at line 49 of file proto_dhw.h.

◆ DHW_STATUS_FPI1

uint8_t esphome::home_io_control::DHW_STATUS_FPI1 = 0x00
staticconstexpr

Status-read FPI1 that asks for no FP1-FP8 (the heater holds none of them).

Definition at line 97 of file proto_dhw.h.

◆ DHW_STATUS_FPI2

uint8_t esphome::home_io_control::DHW_STATUS_FPI2
staticconstexpr
Initial value:
=
static constexpr uint8_t DHW_FP_OPERATING_MODE
Functional parameter that carries the operating-mode word (DHW_OPERATING_MODE_*).
Definition proto_dhw.h:101
static constexpr uint8_t DHW_FP_ALSO_ASKED
Functional parameter 10: no decision uses it; the status read asks for it so the read is exactly the ...
Definition proto_dhw.h:104
static constexpr uint8_t DHW_FP_CAPABILITIES
Functional parameter that carries the operating-mode capabilities (DHW_CAPABILITY_* bits).
Definition proto_dhw.h:99
constexpr uint8_t fp_bitmap_bit(uint8_t fp)
Bit of functional parameter fp (1-16) in its bitmap: FPI1 holds FP1-FP8 and FPI2 FP9-FP16,...

Status-read FPI2 (43): FP10, FP15 and FP16.

The capability and operating-mode words come with the main parameter in one exchange; FP10 keeps the read identical to the one the heater answered.

Definition at line 107 of file proto_dhw.h.

◆ DHW_STATUS_MAIN_PARAMETER_OFFSET

uint8_t esphome::home_io_control::DHW_STATUS_MAIN_PARAMETER_OFFSET = 2
staticconstexpr

Offset of the main parameter (MSB) in the data of a status reply (CMD_PRIVATE_RESP): the target slot of a cover's reply.

The two bytes after it read 00 00 in every reply seen.

Definition at line 86 of file proto_dhw.h.

◆ DHW_TARGET_MAX_C

float esphome::home_io_control::DHW_TARGET_MAX_C = 62.0F
staticconstexpr

Upper end of the target-temperature scale: main parameter 0xC800 is 62 C, the highest target the Calypso's display offers.

Definition at line 83 of file proto_dhw.h.

◆ DHW_TARGET_MIN_C

float esphome::home_io_control::DHW_TARGET_MIN_C = 20.0F
staticconstexpr

Lower end of the target-temperature scale: main parameter 0 is 20 C.

Definition at line 80 of file proto_dhw.h.

◆ DISCOVERY_FLAGS_ATT_MASK

uint8_t esphome::home_io_control::DISCOVERY_FLAGS_ATT_MASK = 0xC0
staticconstexpr

Bit masks and shifts for the Multi Information Byte fields.

Bits [7:6]: actuator turnaround time class.

Definition at line 877 of file proto_constants.h.

◆ DISCOVERY_FLAGS_ATT_SHIFT

uint8_t esphome::home_io_control::DISCOVERY_FLAGS_ATT_SHIFT = 6
staticconstexpr

Shift for ATT field extraction.

Definition at line 878 of file proto_constants.h.

◆ DISCOVERY_FLAGS_IO_MEMBERSHIP

uint8_t esphome::home_io_control::DISCOVERY_FLAGS_IO_MEMBERSHIP
staticconstexpr
Initial value:
=
0x04

Bit 2: io-homecontrol membership; always 1 (same table).

Definition at line 883 of file proto_constants.h.

◆ DISCOVERY_FLAGS_POWER_SAVE_MASK

uint8_t esphome::home_io_control::DISCOVERY_FLAGS_POWER_SAVE_MASK = 0x03
staticconstexpr

Bits [1:0]: power save mode.

Definition at line 885 of file proto_constants.h.

◆ DISCOVERY_FLAGS_RF_SUPPORT

uint8_t esphome::home_io_control::DISCOVERY_FLAGS_RF_SUPPORT
staticconstexpr
Initial value:
=
0x08

Bit 3: 1 = the node has its own RF, 0 = it sits on a wired backbone only (VELUX KLF 200 API v3.18 §7.4.1.2.4-7, Table 52).

Definition at line 880 of file proto_constants.h.

◆ DISCOVERY_FLAGS_SYNC_CTRL_GRP

uint8_t esphome::home_io_control::DISCOVERY_FLAGS_SYNC_CTRL_GRP = 0x20
staticconstexpr

Bit 5: supports sync control group.

Definition at line 879 of file proto_constants.h.

◆ DISCOVERY_RESP_BACKBONE_OFFSET

uint8_t esphome::home_io_control::DISCOVERY_RESP_BACKBONE_OFFSET = 2
staticconstexpr

Byte offsets within CMD_DISCOVER_RESP (0x29) payload data.

The full discovery response carries up to 9 bytes of device metadata: bytes 0–1 hold the packed device type/subtype (already parsed by decode_packed_device_type()), and bytes 2–8 hold additional fields. Backbone address starts at data[2] (3 bytes); cross-confirmed by CMD_NODE_VERIFY_RESP (0x37), which returns the same 3 bytes for the same device (see that constant's comment).

Definition at line 861 of file proto_constants.h.

◆ DISCOVERY_RESP_FLAGS_OFFSET

uint8_t esphome::home_io_control::DISCOVERY_RESP_FLAGS_OFFSET = 6
staticconstexpr

Flags byte at data[6].

Definition at line 866 of file proto_constants.h.

◆ DISCOVERY_RESP_FULL_SIZE

uint8_t esphome::home_io_control::DISCOVERY_RESP_FULL_SIZE = 9
staticconstexpr

Full discovery response payload size.

Definition at line 868 of file proto_constants.h.

◆ DISCOVERY_RESP_MANUFACTURER_OFFSET

uint8_t esphome::home_io_control::DISCOVERY_RESP_MANUFACTURER_OFFSET = 5
staticconstexpr

Manufacturer ID at data[5].

Definition at line 865 of file proto_constants.h.

◆ DISCOVERY_RESP_TIMESTAMP_OFFSET

uint8_t esphome::home_io_control::DISCOVERY_RESP_TIMESTAMP_OFFSET = 7
staticconstexpr

Timestamp starts at data[7] (2 bytes).

Definition at line 867 of file proto_constants.h.

◆ DISCOVERY_SLOT_MS

uint32_t esphome::home_io_control::DISCOVERY_SLOT_MS = 41
staticconstexpr

Length of one discovery answer slot.

A discovery request opens a window of N such slots; the key-extraction responder answers once, in a random slot from 1 to N, so it never answers at once; the controller collects answers for the whole window before it confirms one.

Evidence: in our corpus's discovery captures, devices answer a 32-slot search between 13 and 1353 ms after it, inside 33 × 41 ms; a Freebox Delta repeats its 128-slot search about every 5.3 s, which is 128 × 41 ms plus its own turnaround (issue #215).

Definition at line 32 of file proto_timing.h.

◆ DISCOVERY_SLOTS_128

uint8_t esphome::home_io_control::DISCOVERY_SLOTS_128 = 128
staticconstexpr

Window of ADDRESS_SUFFIX_DISCOVERY_128.

Definition at line 142 of file proto_codecs.h.

◆ DISCOVERY_SLOTS_255

uint8_t esphome::home_io_control::DISCOVERY_SLOTS_255 = 255
staticconstexpr

Window of ADDRESS_SUFFIX_DISCOVERY_255.

Definition at line 143 of file proto_codecs.h.

◆ DISCOVERY_SLOTS_32

uint8_t esphome::home_io_control::DISCOVERY_SLOTS_32 = 32
staticconstexpr

Window of ADDRESS_SUFFIX_DISCOVERY, and the fallback.

Definition at line 140 of file proto_codecs.h.

◆ DISCOVERY_SLOTS_64

uint8_t esphome::home_io_control::DISCOVERY_SLOTS_64 = 64
staticconstexpr

Window of ADDRESS_SUFFIX_DISCOVERY_64.

Definition at line 141 of file proto_codecs.h.

◆ ERROR_RESP_CODE_MAX

uint8_t esphome::home_io_control::ERROR_RESP_CODE_MAX = 0x11
staticconstexpr

Highest named result code (17, "already configured").

Definition at line 441 of file proto_constants.h.

◆ ERROR_RESP_CODE_SHIFT

uint8_t esphome::home_io_control::ERROR_RESP_CODE_SHIFT = 3
staticconstexpr

Result code sits in bits [7:3] of the 0xFE byte.

Definition at line 439 of file proto_constants.h.

◆ ERROR_RESP_STATE_MASK

uint8_t esphome::home_io_control::ERROR_RESP_STATE_MASK = 0x07
staticconstexpr

Execution state sits in bits [2:0] of the 0xFE byte.

Definition at line 440 of file proto_constants.h.

◆ EVENT_BATTERY_LEVEL_CHANGE

uint16_t esphome::home_io_control::EVENT_BATTERY_LEVEL_CHANGE = 0x2001
staticconstexpr

Definition at line 386 of file proto_constants.h.

◆ EVENT_LOCAL_SENSOR

uint16_t esphome::home_io_control::EVENT_LOCAL_SENSOR = 0x2005
staticconstexpr

A device reports a sensor built into it (sun, window, ...).

An RS100 Solar io accepted the subscription and answered with this event: first data byte LOCAL_SENSOR_TYPE_SUN, then a byte, a big-endian 16-bit value and a byte; the value is 0 at night and rises with sun on the panel (tests/corpus/captures/probe/somfy_rs100_probe_subscribe_event_sx1262.yaml and somfy_rs100_probe_subscribe_event_daylight_sx1262.yaml); see decode_sun_level_event().

Todo
The value's unit and scale are unknown; the surrounding two bytes are undecoded.

Definition at line 393 of file proto_constants.h.

◆ EVENT_STATUS_CHANGE

uint16_t esphome::home_io_control::EVENT_STATUS_CHANGE = 0x100A
staticconstexpr

The event a position-bearing CMD_STATUS_UPDATE carries (every captured one, e.g.

tests/corpus/captures/statuspoll/somfy_rs100_statuspoll_kig300_sx1276.yaml). The same two bytes follow the first byte of the CMD_SET_CONFIG1 payload the hub sends at the end of pairing. A CMD_STATUS_UPDATE carrying any other event is a device event report, never a position update.

Definition at line 376 of file proto_constants.h.

◆ EVENT_SUBSCRIPTION_SUBSCRIBE

uint8_t esphome::home_io_control::EVENT_SUBSCRIPTION_SUBSCRIBE = 0x01
staticconstexpr

Ask the device to report the event.

Definition at line 364 of file proto_constants.h.

◆ EVENT_SUBSCRIPTION_UNSUBSCRIBE

uint8_t esphome::home_io_control::EVENT_SUBSCRIPTION_UNSUBSCRIBE = 0x00
staticconstexpr

Ask the device to stop reporting the event.

Todo
A guess from the frame's shape: no device has been seen to act on it. Kept only to test whether a subscription can be undone.

Definition at line 367 of file proto_constants.h.

◆ EXCHANGE_ATTEMPT_OVERHEAD_MS

uint32_t esphome::home_io_control::EXCHANGE_ATTEMPT_OVERHEAD_MS = 10
staticconstexpr

Time an attempt costs beyond its transmission and its listen: the channel-busy check, the frequency hop and the SPI traffic around each transmit.

Measured on the bench boards, one per radio the hub drives: a read to an address no device answers made attempts 422 ms apart where the transmit estimate and the 400 ms window add up to 412 ms. The budget guard and the attempt plan both add it to every attempt (decisions::attempt_fits()), so the plan promises exactly the attempts the guard admits.

Definition at line 200 of file proto_timing.h.

◆ EXCHANGE_DEBUG_LINE_SIZE

size_t esphome::home_io_control::EXCHANGE_DEBUG_LINE_SIZE = 384
staticconstexpr

Longest rendered exchange-debug field list, plus headroom for a long command name.

Stays well under ESP-IDF's 512-byte log line together with the longest message prefix, so the trailing fields are never cut off on hardware.

Definition at line 697 of file exchange_engine.h.

◆ EXCHANGE_EARLY_RESPONSE_WAIT_MS

uint16_t esphome::home_io_control::EXCHANGE_EARLY_RESPONSE_WAIT_MS = 200
staticconstexpr

How long an attempt listens for a mains device's reply when another attempt follows it, after the end of its own transmission.

A mains-powered device answers within a few tens of milliseconds or not at all. Across the corpus, 47 of the 52 start requests this hub sent to a device that is not declared low-power drew their first reply within 98 ms (median 66 ms, scripts/corpus/reply_latency.py –mains); the other five are a stalled key extraction and replies that belong to a later request. On the bench, an Izymo dimmer answered 36 to 42 ms after the start of the hub's transmission (400 exchanges, none retried), its carrier coming up within about 0.1 ms of the end of our frame; a Sunea awning, on every radio the hub drives, answered its challenged status reads 33 to 49 ms after it. 200 ms is twice the slowest of those corpus replies and about four times the bench maximum, and the largest round value for which nine attempts still fit EXCHANGE_TOTAL_BUDGET_MS: the early attempts listen this long and the last one listens the whole start window, so a device that does answer late still gets one full-length listen. Used by decisions::attempt_plan() for mains targets only (a low-power target keeps one window for every attempt).

Definition at line 193 of file proto_timing.h.

◆ EXCHANGE_MAX_ATTEMPTS

uint8_t esphome::home_io_control::EXCHANGE_MAX_ATTEMPTS = 9
staticconstexpr

Most attempts any exchange makes, whatever the budget allows: a plan with a long exchange_total_budget_ms still stops here, so a dead device is not hammered with 25 copies of a read.

Definition at line 153 of file proto_timing.h.

◆ EXCHANGE_RETRY_COUNT

uint8_t esphome::home_io_control::EXCHANGE_RETRY_COUNT = 3
staticconstexpr

Attempts per exchange for every caller that has not asked for an attempt plan (decisions::attempt_plan()): a command that is not repeat-safe (decisions::is_repeat_safe_command()), heater registers, management actions and the pairing loops.

It is also the ceiling of the max_tries overload of ExchangeEngine::send_and_receive(), and the most copies a challenged request that never closes is followed by, whatever its plan.

Definition at line 149 of file proto_timing.h.

◆ EXCHANGE_RETRY_DELAY_MS

int32_t esphome::home_io_control::EXCHANGE_RETRY_DELAY_MS = 250
staticconstexpr

Gap between retries within one HA command.

Definition at line 143 of file proto_timing.h.

◆ EXCHANGE_TOTAL_BUDGET_MS

uint16_t esphome::home_io_control::EXCHANGE_TOTAL_BUDGET_MS = 2500
staticconstexpr

Wall-clock ceiling on one whole exchange, retries included.

The attempt count times (long preamble + response window + retry gap) is what determines how long a failing command blocks the ESPHome loop, and that blocking also starves the receive path the rest of the exchange depends on. ESPHome itself warns when one operation takes longer than 2550 ms (ADR 0013); this budget must stay under that threshold.

So the attempt count is a maximum, not a promise: an attempt only starts if it can finish inside the budget. Elapsed time, plus the gap before the attempt, plus the radio's estimate of its transmit time (RadioDriver::tx_air_time_us()), plus the window it then listens, plus the final reply window when the target has already challenged in this exchange, must stay below the budget. Counting the window and the final wait keeps a slow listen from running an exchange past the ceiling; counting the transmission keeps a radio with a long wake-up from starting an attempt it cannot finish in time. At the default 400 ms window three attempts with the retry gap still fit (~2.3 s); an exchange the target challenges does not always (ADR 0073). A request to a mains device that makes several attempts with no gap listens EXCHANGE_EARLY_RESPONSE_WAIT_MS on all but the last and fits nine (decisions::attempt_plan(), ADR 0075).

Definition at line 299 of file proto_timing.h.

◆ EXECUTE_MAIN_BYTE_OFFSET

uint8_t esphome::home_io_control::EXECUTE_MAIN_BYTE_OFFSET = 2
staticconstexpr

Offset of the main position/command byte in a CMD_EXECUTE payload — after the originator (first byte) and the ACEI (second byte).

Holds a 0-100 position scale value or one of the POS_* codes.

Definition at line 536 of file proto_constants.h.

◆ FOREIGN_CHALLENGE_AFTER_REQUEST_MS

uint32_t esphome::home_io_control::FOREIGN_CHALLENGE_AFTER_REQUEST_MS = 100
staticconstexpr

A device challenges a request 2.5-3.3 ms after it; 100 ms is far above that and far below the next request.

A challenge within this window of a heard request from the same controller answers that request; a challenge with no such request means the request frame was missed.

Definition at line 94 of file status_poll_policy.h.

◆ FOREIGN_READ_RESYNC_MS

uint32_t esphome::home_io_control::FOREIGN_READ_RESYNC_MS = 600000
staticconstexpr

How long a device's authenticated status or this hub's own read-triggered poll keeps a further overheard read from triggering another poll (0 = never polled, so the first read polls).

Covers have no background poll unless they opt into status_refresh_interval, so a read by another controller is the cue that something the hub did not hear (a rain closure, a local button) may have changed.

Definition at line 90 of file status_poll_policy.h.

◆ FRAME_CMD_OFFSET

uint8_t esphome::home_io_control::FRAME_CMD_OFFSET = 8
staticconstexpr

Byte offset of the command ID in a raw wire buffer.

Definition at line 51 of file proto_sizes.h.

◆ FRAME_CRC_SIZE

uint8_t esphome::home_io_control::FRAME_CRC_SIZE = 2
staticconstexpr

Size of the on-air CRC-CCITT trailer appended after every frame (declared bytes, plus the out-of-length MAC trailer when present).

Definition at line 55 of file proto_sizes.h.

◆ FRAME_LOG_FLAGS_BUFFER_SIZE

size_t esphome::home_io_control::FRAME_LOG_FLAGS_BUFFER_SIZE = 32
inlineconstexpr

Buffer for decoded CTRL1 flag annotations.

Definition at line 21 of file log_frame.h.

◆ FRAME_LOG_HELD_LINES

size_t esphome::home_io_control::FRAME_LOG_HELD_LINES = 6
inlineconstexpr

Lines one hold can keep.

A held window covers one received frame and the answer to it: the driver's reception lines (at most four, on a failed decode) plus the answer's TX line and the driver's re-arm line.

Definition at line 98 of file log_frame.h.

◆ FRAME_LOG_HEX_BUFFER_SIZE

size_t esphome::home_io_control::FRAME_LOG_HEX_BUFFER_SIZE = 220
inlineconstexpr

Fits a full 32-byte frame rendered as spaced hex text.

Definition at line 20 of file log_frame.h.

◆ FRAME_LOG_LINE_SIZE

size_t esphome::home_io_control::FRAME_LOG_LINE_SIZE = FRAME_LOG_HEX_BUFFER_SIZE + 96
inlineconstexpr

One rendered frame-log line: the hex dump plus its prefix, frequency, command and flags.

Definition at line 102 of file log_frame.h.

◆ FRAME_LOG_NOTE_SIZE

size_t esphome::home_io_control::FRAME_LOG_NOTE_SIZE = 64
inlineconstexpr

The overflow note flush() prints when lines did not fit.

Definition at line 100 of file log_frame.h.

◆ FRAME_MAX_DATA_SIZE

uint8_t esphome::home_io_control::FRAME_MAX_DATA_SIZE = 23
staticconstexpr

Maximum data bytes after command ID (declared length - header).

Definition at line 50 of file proto_sizes.h.

◆ FRAME_MAX_DECLARED_SIZE

uint8_t esphome::home_io_control::FRAME_MAX_DECLARED_SIZE = 32
staticconstexpr

Largest frame length CTRL0's 5-bit length field (bits [4:0], length - 1) can express.

This is the wire-format bound on the declared portion of a frame — the part CTRL0's length bits describe and parse()/serialize()/set_cmd() size against. It is not expressed here as CTRL0_LENGTH_MASK + 1 because CTRL0_LENGTH_MASK lives in proto_frame.h, which includes this header — referencing it back here would be a circular include. proto_frame.h carries a static_assert tying the two together right next to CTRL0_LENGTH_MASK's declaration, so this literal cannot silently drift from the mask that actually defines it.

Definition at line 38 of file proto_sizes.h.

◆ FRAME_MAX_SIZE

uint8_t esphome::home_io_control::FRAME_MAX_SIZE = FRAME_MAX_DECLARED_SIZE
staticconstexpr

Historical name for FRAME_MAX_DECLARED_SIZE, kept as an alias rather than a second literal so the two names can never disagree — most call sites (set_cmd, serialize, parse, every fixed-size frame buffer that only ever holds a declared-length frame) predate the trailer/wire distinction and still read most naturally as "the max frame size".

Definition at line 48 of file proto_sizes.h.

◆ FRAME_MAX_TRAILER_SIZE

uint8_t esphome::home_io_control::FRAME_MAX_TRAILER_SIZE = HMAC_SIZE
staticconstexpr

Largest out-of-length authenticator a frame can carry.

A 1W CMD 0x30 "add controller" payload (enc_key[16] + man_id[1] + data[1] + sequence[2] = 20 bytes) plus its 9-byte header is 29 bytes — already representable in CTRL0's 5-bit field — but its 6-byte MAC does not fit inside the same field's remaining headroom (29 declared + 6 MAC = 35, unrepresentable in 5 bits), so the MAC rides after the declared length instead, still under the CRC. Real remotes send the 29-byte form with no trailer (somfy_smoove_enrollment_add_and_remove_controller_sx1276); the trailer form is the published vector's.

Definition at line 64 of file proto_sizes.h.

◆ FRAME_MAX_WIRE_SIZE

uint8_t esphome::home_io_control::FRAME_MAX_WIRE_SIZE = FRAME_MAX_DECLARED_SIZE + FRAME_MAX_TRAILER_SIZE + FRAME_CRC_SIZE
staticconstexpr

Largest number of bytes a buffer must hold to receive or transmit any frame this project knows about, trailer and CRC included: FRAME_MAX_DECLARED_SIZE (what CTRL0 can declare).

  • FRAME_MAX_TRAILER_SIZE (the out-of-length MAC, when present) + FRAME_CRC_SIZE. This is the wire bound — distinct from FRAME_MAX_DECLARED_SIZE, which is the declared bound set_cmd() still enforces. A buffer sized to the declared bound alone truncates a MAC-bearing frame; use this constant for any buffer that must survive one intact.

Definition at line 72 of file proto_sizes.h.

◆ FRAME_MIN_SIZE

uint8_t esphome::home_io_control::FRAME_MIN_SIZE = 9
staticconstexpr

Minimum frame: CTRL0+CTRL1+DST(3)+SRC(3)+CMD(1).

Definition at line 29 of file proto_sizes.h.

◆ FREQ_CH1

uint32_t esphome::home_io_control::FREQ_CH1 = 868250000
staticconstexpr

The protocol uses 3 frequency channels in the 868 MHz ISM band.

IO-Homecontrol uses 3 channels in the 868 MHz SRD band. In 1W (one-way) mode, only CH2 is used. In 2W (two-way) mode, the controller hops across all three channels every ~2.7ms when idle. Commands are sent on CH2; responses may arrive on any channel within the exchange wait window. Channel 1: 868.25 MHz (2W only)

Definition at line 21 of file proto_timing.h.

◆ FREQ_CH2

uint32_t esphome::home_io_control::FREQ_CH2 = 868950000
staticconstexpr

Channel 2: 868.95 MHz (1W and 2W, TX channel).

Definition at line 22 of file proto_timing.h.

◆ FREQ_CH3

uint32_t esphome::home_io_control::FREQ_CH3 = 869850000
staticconstexpr

Channel 3: 869.85 MHz (2W only).

Definition at line 23 of file proto_timing.h.

◆ FROZEN_COUNT_BACKOFF_FIRST_MS

uint32_t esphome::home_io_control::FROZEN_COUNT_BACKOFF_FIRST_MS = 10000
staticconstexpr

Delay after the first frozen report.

Definition at line 153 of file status_poll_policy.h.

◆ FROZEN_COUNT_BACKOFF_MAX_MS

uint32_t esphome::home_io_control::FROZEN_COUNT_BACKOFF_MAX_MS = 20000
staticconstexpr

Delay after the third and every later one.

Definition at line 155 of file status_poll_policy.h.

◆ FROZEN_COUNT_BACKOFF_SECOND_MS

uint32_t esphome::home_io_control::FROZEN_COUNT_BACKOFF_SECOND_MS = 15000
staticconstexpr

Delay after the second.

Definition at line 154 of file status_poll_policy.h.

◆ FXOSC

uint32_t esphome::home_io_control::FXOSC = 32000000U
staticconstexpr

SX1276 crystal oscillator frequency (32 MHz).

Used to calculate register values for bitrate, frequency deviation, and carrier frequency.

Definition at line 95 of file radio_sx1276.h.

◆ HEATING_ALIAS_MP_PREFIX

uint8_t esphome::home_io_control::HEATING_ALIAS_MP_PREFIX = 0xD8
staticconstexpr

Main-parameter prefix byte of a setpoint alias: the alias occupies the low byte.

Definition at line 73 of file proto_heating_profiles.h.

◆ HEATING_DIRECTION_GET

uint8_t esphome::home_io_control::HEATING_DIRECTION_GET = 0x60
constexpr

Payload byte at index 1 of a read ("get") request.

Definition at line 132 of file proto_heating.h.

◆ HEATING_DIRECTION_SET

uint8_t esphome::home_io_control::HEATING_DIRECTION_SET = 0x61
constexpr

Payload byte at index 1 of a write ("set") request.

Definition at line 135 of file proto_heating.h.

◆ HEATING_LEVEL_MAX

uint8_t esphome::home_io_control::HEATING_LEVEL_MAX = 0x3F
staticconstexpr

Highest heating level low byte create_execute_heating_level() will frame (the secured level).

Definition at line 143 of file proto_commands.h.

◆ HEATING_LEVEL_MP_PREFIX

uint8_t esphome::home_io_control::HEATING_LEVEL_MP_PREFIX = 0xFC
staticconstexpr

Main-parameter prefix byte of a heating level: the level occupies the low byte.

Definition at line 58 of file proto_heating_profiles.h.

◆ HEATING_MP_FROM_TARGET

uint16_t esphome::home_io_control::HEATING_MP_FROM_TARGET = 0xFFFF
staticconstexpr

Sentinel for HeatingModeEntry::main_parameter: send the entity's target temperature, encoded over the device's range, as the main parameter.

Definition at line 91 of file proto_heating_profiles.h.

◆ HEATING_NIGHT_LIGHT_BRIGHTNESS_MAX

uint8_t esphome::home_io_control::HEATING_NIGHT_LIGHT_BRIGHTNESS_MAX = 100
constexpr

Highest night light brightness (HeatingFunction::NIGHT_LIGHT_BRIGHTNESS): a percentage, so this value is full brightness.

Definition at line 149 of file proto_heating.h.

◆ HEATING_OFF_MP

uint16_t esphome::home_io_control::HEATING_OFF_MP = MAIN_PARAMETER_KEEP
staticconstexpr

Main parameter of a heat pump's off command: MAIN_PARAMETER_KEEP, which the command pairs with the off value of a functional parameter.

Definition at line 87 of file proto_heating_profiles.h.

◆ HEATING_PAYLOAD_MAX_SIZE

size_t esphome::home_io_control::HEATING_PAYLOAD_MAX_SIZE = 6
constexpr

Largest payload any function produces: a set of a 16-bit value (4-byte header + 2 value bytes).

Definition at line 122 of file proto_heating.h.

◆ HEATING_PAYLOAD_PREFIX

uint8_t esphome::home_io_control::HEATING_PAYLOAD_PREFIX = 0x0C
constexpr

Leading payload byte, common to every function.

Its meaning is unconfirmed; do not read a semantic (for example "device class") into it.

Definition at line 126 of file proto_heating.h.

◆ HEATING_POLL_DEFAULT_INTERVAL_MS

uint32_t esphome::home_io_control::HEATING_POLL_DEFAULT_INTERVAL_MS = 300000
staticconstexpr

Interval between regular polls of a heater (its status_poll_interval overrides it).

Slow on purpose: a heater's state moves over minutes, and a poll costs one exchange per register.

Definition at line 74 of file status_poll_policy.h.

◆ HEATING_POST_WRITE_POLL_DELAY_MS

uint32_t esphome::home_io_control::HEATING_POST_WRITE_POLL_DELAY_MS = 45000
staticconstexpr

Delay of the extra heater poll after a register write the heater acknowledged.

The write is read back at once, but whether the heater now heats shows only in its regulator register, which the regular poll reads. Without this extra poll the card's heating / idle trails a setpoint change by up to one poll interval, and longer when a poll fails (issue #165). The regular cadence restarts from this poll.

Definition at line 82 of file status_poll_policy.h.

◆ HEATING_REGISTER_HIGH_BYTE

uint8_t esphome::home_io_control::HEATING_REGISTER_HIGH_BYTE = 0x01
constexpr

Payload byte at index 2: the high byte of the 16-bit register number, constant 0x01.

Definition at line 129 of file proto_heating.h.

◆ HEATING_REGISTER_MODEL_NAME

uint8_t esphome::home_io_control::HEATING_REGISTER_MODEL_NAME = 0x19
constexpr

Register 0x0119: the heater's model name, ASCII, not terminated ("ADELIS 2 DIGIT" on an Atlantic Adelis, corpus atlantic_adelis_probe_register_01_sweep).

Read raw for a log line; no HeatingFunction decodes it.

Definition at line 157 of file proto_heating.h.

◆ HEATING_REPLY_VALUE_OFFSET

size_t esphome::home_io_control::HEATING_REPLY_VALUE_OFFSET = 4
constexpr

Offset of the first value byte in a register reply: the four header bytes it echoes.

Definition at line 152 of file proto_heating.h.

◆ HEATING_SENSOR_FAULT_RAW

int16_t esphome::home_io_control::HEATING_SENSOR_FAULT_RAW = -1
constexpr

Measured value in a reading when the heater reports a sensor fault (raw -1).

A reading stores it as NaN, the same as "never read".

Definition at line 139 of file proto_heating.h.

◆ HEATING_TEMP_MAX_C

float esphome::home_io_control::HEATING_TEMP_MAX_C = 28.0F
constexpr

Highest setpoint this codec will encode.

The setpoint register is a 16-bit little-endian value in tenths of a degree; 28.0 °C (18 01) is the ceiling Atlantic radiator manuals document and the value an AGILIA IO radiator reports in its maximum setpoint register (tests/corpus/captures/exchange/atlantic_agilia_exchange_status_read.yaml). No setpoint above 25.5 °C (a non-zero high byte) has been written on hardware yet.

Definition at line 119 of file proto_heating.h.

◆ HEATING_TEMP_MIN_C

float esphome::home_io_control::HEATING_TEMP_MIN_C = 7.0F
constexpr

Lowest setpoint this codec will encode.

The conventional frost-protection setpoint, safely representable as wire value 70.

Definition at line 111 of file proto_heating.h.

◆ HEATING_TEMPERATURE_MP_SPAN

uint16_t esphome::home_io_control::HEATING_TEMPERATURE_MP_SPAN = 51200
staticconstexpr

Number of main-parameter steps across the encoding range (the full position scale, 0xC800).

Definition at line 127 of file proto_heating_profiles.h.

◆ HEATING_TEMPERATURE_RANGE_MAX_C

float esphome::home_io_control::HEATING_TEMPERATURE_RANGE_MAX_C = 80.0F
staticconstexpr

See HEATING_TEMPERATURE_RANGE_MIN_C.

Definition at line 145 of file proto_heating.h.

◆ HEATING_TEMPERATURE_RANGE_MIN_C

float esphome::home_io_control::HEATING_TEMPERATURE_RANGE_MIN_C = -40.0F
staticconstexpr

Default encoding range of a temperature main parameter, in degrees Celsius.

The main parameter spans HEATING_TEMPERATURE_MP_SPAN steps over [min, max]; this range is the one a device is assumed to use unless the YAML says otherwise (min_temperature: / max_temperature:).

Definition at line 144 of file proto_heating.h.

◆ HMAC_SIZE

uint8_t esphome::home_io_control::HMAC_SIZE = 6
staticconstexpr

Authentication HMAC is 6 bytes (truncated AES output).

Definition at line 22 of file proto_sizes.h.

◆ HOP_TIME_US

int32_t esphome::home_io_control::HOP_TIME_US = 2700
staticconstexpr

Timing constants for frequency hopping and response waiting.

Time per channel when hopping (2.7ms)

Definition at line 123 of file proto_timing.h.

◆ INITIAL_STATUS_REQUEST_DELAY_MS

uint32_t esphome::home_io_control::INITIAL_STATUS_REQUEST_DELAY_MS = 5000
staticconstexpr

Delay before the first post-boot status request from an entity.

Definition at line 71 of file status_poll_policy.h.

◆ IO868_SYNC_WORD_BYTES

uint16_t esphome::home_io_control::IO868_SYNC_WORD_BYTES = 3
staticconstexpr

Sync word bytes on air after the preamble (55 FF 33 on the SX1276, the UART-coded equivalent on the software PHY): three on every 868 MHz chip.

Definition at line 88 of file radio_soft_phy.h.

◆ IV_PADDING

uint8_t esphome::home_io_control::IV_PADDING = 0x55
staticconstexpr

Padding byte used in IV construction.

Definition at line 26 of file proto_sizes.h.

◆ IV_SIZE

uint8_t esphome::home_io_control::IV_SIZE = 16
staticconstexpr

Initialization vector size for AES.

Definition at line 25 of file proto_sizes.h.

◆ KEY_EXTRACTION_DISCOVER_RESP_FLAGS

uint8_t esphome::home_io_control::KEY_EXTRACTION_DISCOVER_RESP_FLAGS = 0xDD
staticconstexpr

Default Multi Information Byte for our device-role CMD_DISCOVER_RESP (0x29): the turnaround-time class and power-save mode the key-extraction responder advertises to a foreign hub.

The key_extraction_multi_info tuning option replaces it.

0xDD = 1101_1101: bits [7:6] ATT_CLASS_40MS, bit [3] DISCOVERY_FLAGS_RF_SUPPORT, bit [2] DISCOVERY_FLAGS_IO_MEMBERSHIP (always 1), bit [4] with no named constant (not in the VELUX KLF 200 API table), power-save bits [1:0] POWER_SAVE_LOW_POWER. All but the power-save bits copy the byte a real Somfy Izymo dimmer sends, 0xDC (tests/corpus/captures/pairing/somfy_izymo_dimmer_pairing_full_sx1276.yaml): copying a real device's whole byte is safer than guessing at bits with no documented meaning.

ATT_CLASS_40MS is the honest class for this responder: it scans channels on the ESPHome loop cadence and is deaf while it transmits, so it cannot promise a 5 ms turnaround. Low power for the same reason: a controller wakes a low-power device with a long preamble, which the scanning responder catches reliably. A VELUX KLR 200, a KLF 200 and a Somfy TaHoma have handed over their key to this byte.

A controller that reads the power-save bit may skip a sleeping device: a Freebox Delta confirmed only answers with the Izymo's always-alive byte 0xDC (issue #215). Such a hub needs the tuning option set to 0xDC.

Definition at line 933 of file proto_constants.h.

◆ KEY_EXTRACTION_DISCOVER_RESP_TYPE_WORD

uint16_t esphome::home_io_control::KEY_EXTRACTION_DISCOVER_RESP_TYPE_WORD = 0x0080
staticconstexpr

Default packed device type word (type in bits [15:6], subtype in bits [5:0]) the key-extraction responder advertises in its 0x29 to an open discovery: 0x0080, roller shutter subtype 0, the same word a real Somfy RS100 io sends (tests/corpus/captures/pairing/somfy_rs100_pairing_key_transfer_timeout.yaml).

The key_extraction_type_word tuning option replaces it; a typed discovery is always answered with the type it asks for.

Definition at line 941 of file proto_constants.h.

◆ KEY_EXTRACTION_SCAN_SLICE_MS

uint32_t esphome::home_io_control::KEY_EXTRACTION_SCAN_SLICE_MS = 200
staticconstexpr

Definition at line 487 of file hub_core.cpp.

◆ KEY_EXTRACTION_SESSION_WINDOW_MS

uint32_t esphome::home_io_control::KEY_EXTRACTION_SESSION_WINDOW_MS
staticconstexpr
Initial value:
=
key_extraction_session_window_ms(KEY_EXTRACTION_DISCOVER_RESP_FLAGS)
static constexpr uint32_t key_extraction_session_window_ms(uint8_t multi_info)
How long after receiving a frame it answers the responder stays on that frame's channel (SESSION_HOLD...

The session window for the default Multi Information Byte (KEY_EXTRACTION_DISCOVER_RESP_FLAGS).

Definition at line 48 of file key_extraction_responder.h.

◆ LATIN1_CODEPOINT_MAX

uint16_t esphome::home_io_control::LATIN1_CODEPOINT_MAX = 0x00FF
staticconstexpr

Highest Unicode code point representable in Latin-1.

Definition at line 32 of file proto_codecs.h.

◆ LBT_MAX_RETRIES

uint8_t esphome::home_io_control::LBT_MAX_RETRIES = 5
staticconstexpr

Max carrier-sense attempts before TX anyway.

Definition at line 348 of file proto_timing.h.

◆ LBT_RETRY_DELAY_MS

uint8_t esphome::home_io_control::LBT_RETRY_DELAY_MS = 5
staticconstexpr

Backoff between LBT checks.

Definition at line 349 of file proto_timing.h.

◆ LBT_RSSI_THRESHOLD_DBM

int16_t esphome::home_io_control::LBT_RSSI_THRESHOLD_DBM = -90
staticconstexpr

Listen-before-talk (LBT) parameters: conservative defaults that keep the hub from opening an exchange over traffic it can hear.

Before transmitting a frame that opens a session (START set), the radio checks that the channel RSSI is below the threshold. If the channel is busy, TX is deferred by LBT_RETRY_DELAY_MS up to LBT_MAX_RETRIES times. A frame without START answers within a session and is sent at once, unchecked. Channel-free threshold

Definition at line 347 of file proto_timing.h.

◆ LIMITATION_PARAM_FP_FIRST

uint8_t esphome::home_io_control::LIMITATION_PARAM_FP_FIRST = 1
staticconstexpr

Parameter id of FP1.

Definition at line 655 of file proto_constants.h.

◆ LIMITATION_PARAM_FP_LAST

uint8_t esphome::home_io_control::LIMITATION_PARAM_FP_LAST = 16
staticconstexpr

Parameter id of FP16.

Definition at line 656 of file proto_constants.h.

◆ LIMITATION_PARAM_MP

uint8_t esphome::home_io_control::LIMITATION_PARAM_MP = 0x00
staticconstexpr

Parameter id of the main parameter.

Definition at line 654 of file proto_constants.h.

◆ LIMITATION_REQ_DATA_SIZE

uint8_t esphome::home_io_control::LIMITATION_REQ_DATA_SIZE = 3
staticconstexpr

Request data: selector, parameter id, 0x00.

Definition at line 657 of file proto_constants.h.

◆ LIMITATION_RESP_DATA_SIZE

uint8_t esphome::home_io_control::LIMITATION_RESP_DATA_SIZE = 5
staticconstexpr

Reply data: parameter id, value (2), originator, time.

Definition at line 658 of file proto_constants.h.

◆ LIMITATION_TIME_MAX_COUNTED

uint8_t esphome::home_io_control::LIMITATION_TIME_MAX_COUNTED = 252
staticconstexpr

Remaining-time codes of a limitation reply's last byte (KLF 200 API specification v3.18, table 200): codes 0-252 count (n + 1) * 30 seconds, 253 means no time limit, and 254/255 are the timer values the set command uses to delete an entry (never a duration).

Highest code that encodes a duration.

Definition at line 663 of file proto_constants.h.

◆ LIMITATION_TIME_STEP_S

uint32_t esphome::home_io_control::LIMITATION_TIME_STEP_S = 30
staticconstexpr

Seconds per time-code step.

Definition at line 665 of file proto_constants.h.

◆ LIMITATION_TIME_UNLIMITED

uint8_t esphome::home_io_control::LIMITATION_TIME_UNLIMITED = 253
staticconstexpr

Limitation without a time limit.

Definition at line 664 of file proto_constants.h.

◆ LOCAL_SENSOR_EVENT_DATA_LEN

uint8_t esphome::home_io_control::LOCAL_SENSOR_EVENT_DATA_LEN = 5
staticconstexpr

EVENT_LOCAL_SENSOR data: 06 xx <hi> <lo> yy.

Definition at line 399 of file proto_constants.h.

◆ LOCAL_SENSOR_TYPE_SUN

uint8_t esphome::home_io_control::LOCAL_SENSOR_TYPE_SUN = 0x06
staticconstexpr

First EVENT_LOCAL_SENSOR data byte on every capture.

Definition at line 400 of file proto_constants.h.

◆ LOCAL_SENSOR_VALUE_OFFSET

uint8_t esphome::home_io_control::LOCAL_SENSOR_VALUE_OFFSET = 2
staticconstexpr

Offset of the big-endian value inside the event data.

Definition at line 401 of file proto_constants.h.

◆ LONG_PREAMBLE

uint16_t esphome::home_io_control::LONG_PREAMBLE = 1024
staticconstexpr

Preamble is a sequence of 0xAA bytes that precedes every frame.

Any start frame carrying CTRL1_LOW_POWER — a directed frame to a low-power target, or the SPE roll-call's low-power frame shape — uses the long preamble (1024 bytes = 8192 bits) as a wake-up burst for a duty-cycled receiver; every other start frame uses the runtime-tunable normal_start_preamble, which an always-listening receiver detects fine. Every continuation frame (a reply, our challenge or challenge answer, a status-update ACK) uses the radio driver's response_preamble(), since both sides are already on the same channel; SHORT_PREAMBLE is that value's protocol default and the tuning floor. The exchange engine derives all of this from the frame (exchange_engine.cpp). 1024 bytes: wake-up burst for a low-power start frame

Definition at line 43 of file proto_timing.h.

◆ LOW_POWER_MAX_TRAVEL_MS

uint32_t esphome::home_io_control::LOW_POWER_MAX_TRAVEL_MS = 120000
staticconstexpr

How long evidence that a low-power receiver is moving keeps it believed awake enough to hear the short start preamble first.

A moving VELUX solar receiver ignores the 1024-byte wake-up preamble but answers the short one, so a command, STOP or poll sent mid-travel must lead with the short one. Sized above the longest cover travel this project has measured; a first estimate that field logs will correct. Past it the receiver is presumed to have finished and fallen back asleep.

Definition at line 229 of file proto_timing.h.

◆ LOW_POWER_RESPONSE_WAIT_MS

uint16_t esphome::home_io_control::LOW_POWER_RESPONSE_WAIT_MS = 550
staticconstexpr

How long a read listens for a low-power receiver's reply after the end of its own transmission, on every attempt.

Of the 93 low-power-flagged requests this hub transmitted in the corpus (83 captures, see scripts/corpus/reply_latency.py), the median answer came 248 ms after the end of the request, the 90th percentile 299 ms, the 95th 354 ms; 89 came within 400 ms, and the four slower ones took 647 ms (a third-party gateway's pairing), 819 ms and 1639 ms (an RS100 io motor's key exchange) and 4375 ms (a stalled key extraction). 550 ms sits about 1.5 times above the 95th percentile and below every slower sample, so one attempt's window covers a reply that arrives late without waiting for the outliers. Three such attempts with their wake-up preambles still fit EXCHANGE_TOTAL_BUDGET_MS when nothing is waited between them.

Definition at line 177 of file proto_timing.h.

◆ LOW_POWER_SETTLING_ATTEMPTS

uint8_t esphome::home_io_control::LOW_POWER_SETTLING_ATTEMPTS = 3
staticconstexpr

Attempts a settling read makes to a low-power receiver.

Each one leads with the wake-up preamble (~0.24 s on air), so three already fill the exchange budget.

Definition at line 156 of file proto_timing.h.

◆ LOW_POWER_SHORT_LEAD_WINDOW_MS

uint16_t esphome::home_io_control::LOW_POWER_SHORT_LEAD_WINDOW_MS = 100
staticconstexpr

How long the short lead attempt of a read to a low-power receiver in an unknown state listens after the end of its own transmission (decisions::WakeBelief::UNKNOWN, ADR 0079).

The lead attempt is aimed at a receiver that is travelling: such a VELUX screen answered a short start frame 41 to 47 ms after it (issue #114), and a resting one does not answer it at all, so a longer wait is spent on silence. 100 ms is about twice the slowest of those answers, and keeps the cost of the lead (its transmission, this window and EXCHANGE_ATTEMPT_OVERHEAD_MS) near 0.12 s.

Definition at line 165 of file proto_timing.h.

◆ LR1110_BOOTLOADER_1001

uint16_t esphome::home_io_control::LR1110_BOOTLOADER_1001 = 0x1001
inlineconstexpr

Definition at line 51 of file lr1121_firmware_decisions.h.

◆ LR1110_BOOTLOADER_6500

uint16_t esphome::home_io_control::LR1110_BOOTLOADER_6500 = 0x6500
inlineconstexpr

Definition at line 50 of file lr1121_firmware_decisions.h.

◆ LR1110_DEVICE_TYPE_FOR_FIRMWARE_DECISIONS

uint8_t esphome::home_io_control::LR1110_DEVICE_TYPE_FOR_FIRMWARE_DECISIONS = 0x01
inlineconstexpr

Normal-mode GetVersion type bytes for the two chips an LR1121 is most easily confused with — companions to LR1121_DEVICE_TYPE_FOR_FIRMWARE_DECISIONS above, used only to name the chip family in a REJECT_WRONG_CHIP message.

Last checked 2026-08-05.

Definition at line 28 of file lr1121_firmware_decisions.h.

◆ LR1120_BOOTLOADER_2000

uint16_t esphome::home_io_control::LR1120_BOOTLOADER_2000 = 0x2000
inlineconstexpr

Bootloader versions reported by the two chips an LR1121 is most easily confused with — used only to name the chip family in a REJECT_WRONG_CHIP message.

Same snapshot discipline as LR1121_BOOTLOADER_2100/2101; last checked 2026-08-05.

Definition at line 48 of file lr1121_firmware_decisions.h.

◆ LR1120_BOOTLOADER_2001

uint16_t esphome::home_io_control::LR1120_BOOTLOADER_2001 = 0x2001
inlineconstexpr

Definition at line 49 of file lr1121_firmware_decisions.h.

◆ LR1120_DEVICE_TYPE_FOR_FIRMWARE_DECISIONS

uint8_t esphome::home_io_control::LR1120_DEVICE_TYPE_FOR_FIRMWARE_DECISIONS = 0x02
inlineconstexpr

Definition at line 29 of file lr1121_firmware_decisions.h.

◆ LR1121_BOOTLOADER_2100

uint16_t esphome::home_io_control::LR1121_BOOTLOADER_2100 = 0x2100
inlineconstexpr

LR1121 bootloader versions, from Semtech's published compatibility matrix.

An LR1121 reports one of these two; LR1120 reports 0x2000/0x2001 and LR1110 0x6500/0x1001, which is what makes checking against these two a chip-family check as well as a capability check.

Definition at line 42 of file lr1121_firmware_decisions.h.

◆ LR1121_BOOTLOADER_2101

uint16_t esphome::home_io_control::LR1121_BOOTLOADER_2101 = 0x2101
inlineconstexpr

Definition at line 43 of file lr1121_firmware_decisions.h.

◆ LR1121_BOOTLOADER_TYPE_FOR_FIRMWARE_DECISIONS

uint8_t esphome::home_io_control::LR1121_BOOTLOADER_TYPE_FOR_FIRMWARE_DECISIONS = 0xDF
inlineconstexpr

LR1121 GetVersion type byte reported while running the bootloader (LR11XX_TYPE_PRODUCTION_MODE) — must match radio_lr1121_firmware_updater.h's LR1121_UPDATER_BOOTLOADER_TYPE (0xDF).

Kept as a separate constant for the same dependency-free reason as LR1121_DEVICE_TYPE_FOR_FIRMWARE_DECISIONS above. Note this byte identifies production silicon, not chip family — bootloader type is 0xDF on an LR1120 or LR1110 too, which is why chip family is decided from the bootloader version, not this byte.

Definition at line 37 of file lr1121_firmware_decisions.h.

◆ LR1121_BUSY_TIMEOUT_MS

uint32_t esphome::home_io_control::LR1121_BUSY_TIMEOUT_MS = 3000
staticconstexpr

How long SoftPhyDriverBase::wait_busy_() waits for BUSY to drop before declaring the chip failed.

The LR11xx runs an internal boot ROM after reset before BUSY is meaningful — cross-checked against RadioLib's LRxxxx::reset(), which waits ~300ms ("typical transition duration should be 273 ms") and then polls BUSY with a 3s timeout; matched here exactly rather than picking an arbitrary shorter value, since the only cost of a longer timeout is on the already-failing path (a genuinely dead chip takes a few seconds longer to report failure). Much longer than SX1262's (see SX1262_BUSY_TIMEOUT_MS in radio_sx1262.h) — different chips, different boot behavior, not a value that should be unified.

Definition at line 254 of file radio_lr1121.h.

◆ LR1121_CALIBRATE_ALL_BLOCKS

uint8_t esphome::home_io_control::LR1121_CALIBRATE_ALL_BLOCKS = 0x3F
staticconstexpr

Calibrate "all blocks" bitmask.

LR11xx calibration blocks are LF-RC(0)/HF-RC(1)/PLL(2)/ ADC(3)/IMG(4)/PLL-TX(5), so "all" = 0x3F — NOT the SX126x value (0x7F), which uses a different bit layout for a different chip.

Definition at line 180 of file radio_lr1121.h.

◆ LR1121_CMD_CALIBRATE

uint16_t esphome::home_io_control::LR1121_CMD_CALIBRATE = 0x010F
staticconstexpr

cross-checked

Definition at line 52 of file radio_lr1121.h.

◆ LR1121_CMD_CALIBRATE_IMAGE

uint16_t esphome::home_io_control::LR1121_CMD_CALIBRATE_IMAGE = 0x0111
staticconstexpr

cross-checked (RadioLib calibrateImageRejection)

Definition at line 53 of file radio_lr1121.h.

◆ LR1121_CMD_CLEAR_ERRORS

uint16_t esphome::home_io_control::LR1121_CMD_CLEAR_ERRORS
staticconstexpr
Initial value:
=
0x010E

hardware-verified (called on every init/RX cycle, never rejected — see log_command_status_())

Definition at line 46 of file radio_lr1121.h.

◆ LR1121_CMD_CLEAR_IRQ

uint16_t esphome::home_io_control::LR1121_CMD_CLEAR_IRQ = 0x0114
staticconstexpr

cross-checked

Definition at line 56 of file radio_lr1121.h.

◆ LR1121_CMD_GET_ERRORS

uint16_t esphome::home_io_control::LR1121_CMD_GET_ERRORS = 0x010D
staticconstexpr

cross-checked (2-byte response)

Definition at line 45 of file radio_lr1121.h.

◆ LR1121_CMD_GET_PKT_STATUS

uint16_t esphome::home_io_control::LR1121_CMD_GET_PKT_STATUS
staticconstexpr
Initial value:
=
0x0204

cross-checked (Semtech SWDR001 lr11xx_radio.c :: lr11xx_radio_get_gfsk_pkt_status — 0 request params, 4-byte GFSK response: [rssi_sync, rssi_avg, rx_len, status_flags], rssi_sync/avg both decode via -(raw>>1) dBm, same formula as LR1121_CMD_GET_RSSI_INST)

Definition at line 60 of file radio_lr1121.h.

◆ LR1121_CMD_GET_RSSI_INST

uint16_t esphome::home_io_control::LR1121_CMD_GET_RSSI_INST = 0x0205
staticconstexpr

cross-checked

Definition at line 64 of file radio_lr1121.h.

◆ LR1121_CMD_GET_RX_BUFFER_STATUS

uint16_t esphome::home_io_control::LR1121_CMD_GET_RX_BUFFER_STATUS = 0x0203
staticconstexpr

cross-checked

Definition at line 59 of file radio_lr1121.h.

◆ LR1121_CMD_GET_STATUS

uint16_t esphome::home_io_control::LR1121_CMD_GET_STATUS = 0x0100
staticconstexpr

cross-checked

Definition at line 43 of file radio_lr1121.h.

◆ LR1121_CMD_GET_VERSION

uint16_t esphome::home_io_control::LR1121_CMD_GET_VERSION = 0x0101
staticconstexpr

cross-checked

Definition at line 44 of file radio_lr1121.h.

◆ LR1121_CMD_READ_BUFFER

uint16_t esphome::home_io_control::LR1121_CMD_READ_BUFFER = 0x010A
staticconstexpr

cross-checked

Definition at line 49 of file radio_lr1121.h.

◆ LR1121_CMD_SET_DIO_AS_RF_SWITCH

uint16_t esphome::home_io_control::LR1121_CMD_SET_DIO_AS_RF_SWITCH = 0x0112
staticconstexpr

cross-checked

Definition at line 54 of file radio_lr1121.h.

◆ LR1121_CMD_SET_DIO_IRQ_PARAMS

uint16_t esphome::home_io_control::LR1121_CMD_SET_DIO_IRQ_PARAMS = 0x0113
staticconstexpr

cross-checked

Definition at line 55 of file radio_lr1121.h.

◆ LR1121_CMD_SET_GFSK_SYNC_WORD

uint16_t esphome::home_io_control::LR1121_CMD_SET_GFSK_SYNC_WORD = 0x0206
staticconstexpr

cross-checked

Definition at line 65 of file radio_lr1121.h.

◆ LR1121_CMD_SET_MODULATION_PARAMS

uint16_t esphome::home_io_control::LR1121_CMD_SET_MODULATION_PARAMS = 0x020F
staticconstexpr

cross-checked

Definition at line 70 of file radio_lr1121.h.

◆ LR1121_CMD_SET_PA_CONFIG

uint16_t esphome::home_io_control::LR1121_CMD_SET_PA_CONFIG = 0x0215
staticconstexpr

cross-checked

Definition at line 74 of file radio_lr1121.h.

◆ LR1121_CMD_SET_PACKET_PARAMS

uint16_t esphome::home_io_control::LR1121_CMD_SET_PACKET_PARAMS = 0x0210
staticconstexpr

cross-checked

Definition at line 71 of file radio_lr1121.h.

◆ LR1121_CMD_SET_PACKET_TYPE

uint16_t esphome::home_io_control::LR1121_CMD_SET_PACKET_TYPE = 0x020E
staticconstexpr

cross-checked

Definition at line 69 of file radio_lr1121.h.

◆ LR1121_CMD_SET_RF_FREQUENCY

uint16_t esphome::home_io_control::LR1121_CMD_SET_RF_FREQUENCY = 0x020B
staticconstexpr

cross-checked

Definition at line 68 of file radio_lr1121.h.

◆ LR1121_CMD_SET_RX

uint16_t esphome::home_io_control::LR1121_CMD_SET_RX = 0x0209
staticconstexpr

cross-checked

Definition at line 66 of file radio_lr1121.h.

◆ LR1121_CMD_SET_RX_TX_FALLBACK_MODE

uint16_t esphome::home_io_control::LR1121_CMD_SET_RX_TX_FALLBACK_MODE = 0x0213
staticconstexpr

cross-checked

Definition at line 73 of file radio_lr1121.h.

◆ LR1121_CMD_SET_STANDBY

uint16_t esphome::home_io_control::LR1121_CMD_SET_STANDBY = 0x011C
staticconstexpr

cross-checked

Definition at line 58 of file radio_lr1121.h.

◆ LR1121_CMD_SET_TCXO_MODE

uint16_t esphome::home_io_control::LR1121_CMD_SET_TCXO_MODE = 0x0117
staticconstexpr

cross-checked

Definition at line 57 of file radio_lr1121.h.

◆ LR1121_CMD_SET_TX

uint16_t esphome::home_io_control::LR1121_CMD_SET_TX = 0x020A
staticconstexpr

cross-checked

Definition at line 67 of file radio_lr1121.h.

◆ LR1121_CMD_SET_TX_PARAMS

uint16_t esphome::home_io_control::LR1121_CMD_SET_TX_PARAMS = 0x0211
staticconstexpr

cross-checked

Definition at line 72 of file radio_lr1121.h.

◆ LR1121_CMD_WRITE_BUFFER

uint16_t esphome::home_io_control::LR1121_CMD_WRITE_BUFFER = 0x0109
staticconstexpr

cross-checked

Definition at line 48 of file radio_lr1121.h.

◆ LR1121_CMD_WRITE_REG_MEM_MASK32

uint16_t esphome::home_io_control::LR1121_CMD_WRITE_REG_MEM_MASK32
staticconstexpr
Initial value:
=
0x010C

cross-checked (Semtech SWDR001 lr11xx_radio.c / RadioLib LR11x0_commands.h)

Definition at line 50 of file radio_lr1121.h.

◆ LR1121_DEVICE_TYPE

uint8_t esphome::home_io_control::LR1121_DEVICE_TYPE = 0x03
staticconstexpr

cross-checked against RadioLib and the datasheet

Definition at line 108 of file radio_lr1121.h.

◆ LR1121_DEVICE_TYPE_FOR_FIRMWARE_DECISIONS

uint8_t esphome::home_io_control::LR1121_DEVICE_TYPE_FOR_FIRMWARE_DECISIONS = 0x03
inlineconstexpr

LR1121 GetVersion type byte in normal mode — must match radio_lr1121.h's LR1121_DEVICE_TYPE (0x03).

Kept as a separate constant, not a shared header include, so this header stays dependency-free; see the file header for why. A static_assert in tests/radio/radio_lr1121_firmware_updater_test.cpp (a translation unit that already includes both headers) guards the two from silently drifting apart.

Definition at line 23 of file lr1121_firmware_decisions.h.

◆ LR1121_DISCOVERY_HOP_SLICE_MS

uint16_t esphome::home_io_control::LR1121_DISCOVERY_HOP_SLICE_MS = 7
staticconstexpr

Per-channel dwell while LR1121 pairing discovery hops across channels.

Measured independently on LR1121 hardware rather than inherited from SX1262_DISCOVERY_HOP_SLICE_MS — that constant's short-dwell reasoning applies equally here, but the two chips are validated separately and could in principle diverge, so this stays its own literal rather than an alias.

Definition at line 247 of file tuning_config.h.

◆ LR1121_ERR_ADC_CALIB

uint16_t esphome::home_io_control::LR1121_ERR_ADC_CALIB = 0x0004
staticconstexpr

ADC calibration failed.

Definition at line 113 of file radio_lr1121.h.

◆ LR1121_ERR_HF_RC_CALIB

uint16_t esphome::home_io_control::LR1121_ERR_HF_RC_CALIB = 0x0002
staticconstexpr

High-frequency RC oscillator calibration failed.

Definition at line 112 of file radio_lr1121.h.

◆ LR1121_ERR_HF_XOSC_START

uint16_t esphome::home_io_control::LR1121_ERR_HF_XOSC_START = 0x0020
staticconstexpr

High-frequency crystal/TCXO failed to start.

Definition at line 116 of file radio_lr1121.h.

◆ LR1121_ERR_IMG_CALIB

uint16_t esphome::home_io_control::LR1121_ERR_IMG_CALIB = 0x0010
staticconstexpr

Image calibration failed.

Definition at line 115 of file radio_lr1121.h.

◆ LR1121_ERR_LF_RC_CALIB

uint16_t esphome::home_io_control::LR1121_ERR_LF_RC_CALIB = 0x0001
staticconstexpr

Low-frequency RC oscillator calibration failed.

Definition at line 111 of file radio_lr1121.h.

◆ LR1121_ERR_LF_XOSC_START

uint16_t esphome::home_io_control::LR1121_ERR_LF_XOSC_START = 0x0040
staticconstexpr

Low-frequency crystal failed to start.

Definition at line 117 of file radio_lr1121.h.

◆ LR1121_ERR_PLL_CALIB

uint16_t esphome::home_io_control::LR1121_ERR_PLL_CALIB = 0x0008
staticconstexpr

PLL calibration failed.

Definition at line 114 of file radio_lr1121.h.

◆ LR1121_ERR_PLL_LOCK

uint16_t esphome::home_io_control::LR1121_ERR_PLL_LOCK = 0x0080
staticconstexpr

PLL failed to lock.

Definition at line 118 of file radio_lr1121.h.

◆ LR1121_FALLBACK_STDBY_XOSC

uint8_t esphome::home_io_control::LR1121_FALLBACK_STDBY_XOSC = 0x02
staticconstexpr

SetRxTxFallbackMode value for STDBY_XOSC.

LR11xx fallback-mode is a small sequential enum (FS=0x00, STDBY_RC=0x01, STDBY_XOSC=0x02), unlike SX126x's raw standby-mode byte (0x30) reused directly in that field.

Definition at line 185 of file radio_lr1121.h.

◆ LR1121_GFSK_CRC_OFF

uint8_t esphome::home_io_control::LR1121_GFSK_CRC_OFF = 0x01
staticconstexpr

cross-checked (same encoding as SX126x)

Definition at line 167 of file radio_lr1121.h.

◆ LR1121_GFSK_PACKET_FIXED_LENGTH

uint8_t esphome::home_io_control::LR1121_GFSK_PACKET_FIXED_LENGTH = 0x00
staticconstexpr

cross-checked (same encoding as SX126x)

Definition at line 168 of file radio_lr1121.h.

◆ LR1121_IMAGE_CAL_FREQ1

uint8_t esphome::home_io_control::LR1121_IMAGE_CAL_FREQ1 = 0xD7
staticconstexpr

Banded image calibration for 868.25-869.85MHz +/-4MHz (~860-876MHz), matching RadioLib's setFrequency() margin.

CalibImage params are {floor((fmin-1)/4), ceil((fmax+1)/4)}.

Definition at line 225 of file radio_lr1121.h.

◆ LR1121_IMAGE_CAL_FREQ2

uint8_t esphome::home_io_control::LR1121_IMAGE_CAL_FREQ2 = 0xDB
staticconstexpr

Definition at line 226 of file radio_lr1121.h.

◆ LR1121_IRQ_ACTIVITY_MASK

uint32_t esphome::home_io_control::LR1121_IRQ_ACTIVITY_MASK
staticconstexpr
Initial value:
=
static constexpr uint32_t LR1121_IRQ_TIMEOUT
static constexpr uint32_t LR1121_IRQ_TX_DONE
static constexpr uint32_t LR1121_IRQ_CRC_ERR
static constexpr uint32_t LR1121_IRQ_SYNC_WORD_VALID
static constexpr uint32_t LR1121_IRQ_RX_DONE

IRQ bits that represent a terminal radio event — a frame finished decoding, an outbound frame completed, or the chip gave up on its own.

Deliberately excludes PREAMBLE_DETECTED, even though it's part of LR1121_IRQ_DIO_ENABLE_MASK: a preamble alone means a frame may still be arriving, so SoftPhyDriverBase's poll_until_activity_()/check_for_packet() (gated by RadioLR1121::activity_irq_mask) must not treat it as terminal — doing so would tear down RX mid-reception.

Definition at line 94 of file radio_lr1121.h.

◆ LR1121_IRQ_CRC_ERR

uint32_t esphome::home_io_control::LR1121_IRQ_CRC_ERR = 1UL << 7
staticconstexpr

Definition at line 85 of file radio_lr1121.h.

◆ LR1121_IRQ_DIO_ENABLE_MASK

uint32_t esphome::home_io_control::LR1121_IRQ_DIO_ENABLE_MASK
staticconstexpr
Initial value:

DIO-routed IRQ enable mask: TxDone|RxDone|PreambleDetected|SyncWordValid|Timeout.

Deliberately excludes CrcErr from the DIO-routed set, unlike SX1262; CrcErr is still visible in the raw status word for capture diagnostics.

Definition at line 100 of file radio_lr1121.h.

◆ LR1121_IRQ_PREAMBLE_DETECTED

uint32_t esphome::home_io_control::LR1121_IRQ_PREAMBLE_DETECTED = 1UL << 4
staticconstexpr

Definition at line 83 of file radio_lr1121.h.

◆ LR1121_IRQ_RX_DONE

uint32_t esphome::home_io_control::LR1121_IRQ_RX_DONE = 1UL << 3
staticconstexpr

Definition at line 82 of file radio_lr1121.h.

◆ LR1121_IRQ_SYNC_WORD_VALID

uint32_t esphome::home_io_control::LR1121_IRQ_SYNC_WORD_VALID = 1UL << 5
staticconstexpr

Definition at line 84 of file radio_lr1121.h.

◆ LR1121_IRQ_TIMEOUT

uint32_t esphome::home_io_control::LR1121_IRQ_TIMEOUT = 1UL << 10
staticconstexpr

Definition at line 86 of file radio_lr1121.h.

◆ LR1121_IRQ_TX_DONE

uint32_t esphome::home_io_control::LR1121_IRQ_TX_DONE = 1UL << 2
staticconstexpr

Definition at line 81 of file radio_lr1121.h.

◆ LR1121_KNOWN_BOOTLOADER_REQUIREMENTS

Lr1121BootloaderRequirement esphome::home_io_control::LR1121_KNOWN_BOOTLOADER_REQUIREMENTS[]
inlineconstexpr
Initial value:
= {
}
constexpr uint16_t LR1121_BOOTLOADER_2100
LR1121 bootloader versions, from Semtech's published compatibility matrix.
constexpr uint16_t LR1121_BOOTLOADER_2101

Bootloader requirements we know about, copied from Semtech's published pairings (SWTL001/application/src/lr11xx_update_utils.c :: compatibility_matrix[]): 0x0101/0x0102/0x0103 need bootloader 0x2100, 0x0104 needs 0x2101.

This is an ADVISORY list of pairs we can warn about, not a compatibility authority — it is a point-in-time snapshot and a target absent from it is "unverified", never "incompatible". Extending this when Semtech publishes a new image is a one-line edit; it belongs next to LR1121_KNOWN_LATEST_FW_* (radio_lr1121.h) as the same kind of snapshot, with the same staleness discipline. Last checked 2026-08-05.

Definition at line 103 of file lr1121_firmware_decisions.h.

◆ LR1121_KNOWN_LATEST_FW_MAJOR

uint8_t esphome::home_io_control::LR1121_KNOWN_LATEST_FW_MAJOR = 0x01
staticconstexpr

Newest LR1121 transceiver firmware known at the time this file was last updated, per the version-numbered filenames and CHANGELOG.md at https://github.com/Lora-net/radio_firmware_images/tree/master/lr1121/transceiver — the same two bytes GetVersion reports as fw_major/fw_minor (e.g.

lr1121_transceiver_0104.bin is major=0x01, minor=0x04). Deliberately NOT queried live (baked in, per design decision) — this is a point-in-time snapshot that will go stale as Semtech ships new firmware, and needs a manual refresh here when it does.

Last checked 2026-08-05: latest is 0x0104 (2026-04-01), which per that changelog fixes CVE-2025-14857/-14858/-14859 (see https://www.semtech.com/company/security/security-bulletins) on top of earlier feature/bugfix releases — not just a cosmetic version bump.

Reachability caveat: this project's lr1121_firmware_update: feature (docs/lr1121-firmware.md) can flash any published image, but 0x0104 requires bootloader 0x2101. Reaching it from the common 0x2100 bootloader needs the separate, opt-in bootloader rewrite (ADR 0021) — an irreversible operation gated behind its own arming switch — so without that opted in, 0x0103 is the practical ceiling. The "update available" line below can therefore point at a version that is not reachable on a given chip as currently configured; the flash feature's own boot-time bootloader-version report is what tells a user which case they are in.

Definition at line 153 of file radio_lr1121.h.

◆ LR1121_KNOWN_LATEST_FW_MINOR

uint8_t esphome::home_io_control::LR1121_KNOWN_LATEST_FW_MINOR = 0x04
staticconstexpr

Definition at line 154 of file radio_lr1121.h.

◆ LR1121_LOADER_2100

uint16_t esphome::home_io_control::LR1121_LOADER_2100 = 0x2100
inlineconstexpr

Version the lr1121_loader_2100.bin bootloader-*loader* image reports of itself.

Numerically identical to LR1121_BOOTLOADER_2100, but a distinct concept: this is the loader firmware's own self-reported version, which Semtech's compatibility rule requires to equal the bootloader version currently running before the loader may be used – not a "requires bootloader >= X" rule like LR1121_KNOWN_BOOTLOADER_REQUIREMENTS below.

Definition at line 58 of file lr1121_firmware_decisions.h.

◆ LR1121_PACKET_TYPE_GFSK

uint8_t esphome::home_io_control::LR1121_PACKET_TYPE_GFSK = 0x01
staticconstexpr

cross-checked against RadioLib

Definition at line 166 of file radio_lr1121.h.

◆ LR1121_POST_TX_SETTLE_US

uint16_t esphome::home_io_control::LR1121_POST_TX_SETTLE_US = SX1262_POST_TX_SETTLE_US
staticconstexpr

LR1121-specific post-TX settling delay before re-entering RX.

Seeded from the SX1262-validated default; same rationale as LR1121_RESPONSE_PREAMBLE.

Definition at line 239 of file tuning_config.h.

◆ LR1121_PREAMBLE_DETECTOR_16_BIT

uint8_t esphome::home_io_control::LR1121_PREAMBLE_DETECTOR_16_BIT = 0x05
staticconstexpr

Preamble detector length selector: 16 bits.

Shares the SX126x GFSK preamble-detector enum (0x00 off, 0x04=8bit, 0x05=16bit, 0x06=24bit, 0x07=32bit) — the LR11xx sub-GHz GFSK modem descends from the same core.

Definition at line 172 of file radio_lr1121.h.

◆ LR1121_REG_GFSK_WORKAROUND_1_ADDR

uint32_t esphome::home_io_control::LR1121_REG_GFSK_WORKAROUND_1_ADDR = 0x00F20344
staticconstexpr

GFSK modulation workaround register trio, standard (non-0.6/1.2kbps) values for our 38.4kbps config — applied after every modulation-params write, mirroring RadioLib's workaroundGFSK() ("always the first step, even when resetting" per its own comment on the first write).

Definition at line 213 of file radio_lr1121.h.

◆ LR1121_REG_GFSK_WORKAROUND_1_MASK

uint32_t esphome::home_io_control::LR1121_REG_GFSK_WORKAROUND_1_MASK = 0x00000030
staticconstexpr

Definition at line 214 of file radio_lr1121.h.

◆ LR1121_REG_GFSK_WORKAROUND_1_VALUE

uint32_t esphome::home_io_control::LR1121_REG_GFSK_WORKAROUND_1_VALUE = 0x00000010
staticconstexpr

Definition at line 215 of file radio_lr1121.h.

◆ LR1121_REG_GFSK_WORKAROUND_2_ADDR

uint32_t esphome::home_io_control::LR1121_REG_GFSK_WORKAROUND_2_ADDR = 0x00F20348
staticconstexpr

Definition at line 216 of file radio_lr1121.h.

◆ LR1121_REG_GFSK_WORKAROUND_2_MASK

uint32_t esphome::home_io_control::LR1121_REG_GFSK_WORKAROUND_2_MASK = 0x00000005
staticconstexpr

Definition at line 217 of file radio_lr1121.h.

◆ LR1121_REG_GFSK_WORKAROUND_2_VALUE

uint32_t esphome::home_io_control::LR1121_REG_GFSK_WORKAROUND_2_VALUE = 0x00000001
staticconstexpr

Definition at line 218 of file radio_lr1121.h.

◆ LR1121_REG_GFSK_WORKAROUND_3_ADDR

uint32_t esphome::home_io_control::LR1121_REG_GFSK_WORKAROUND_3_ADDR = 0x00F20244
staticconstexpr

Definition at line 219 of file radio_lr1121.h.

◆ LR1121_REG_GFSK_WORKAROUND_3_MASK

uint32_t esphome::home_io_control::LR1121_REG_GFSK_WORKAROUND_3_MASK = 0x0001FF03
staticconstexpr

Definition at line 220 of file radio_lr1121.h.

◆ LR1121_REG_GFSK_WORKAROUND_3_VALUE

uint32_t esphome::home_io_control::LR1121_REG_GFSK_WORKAROUND_3_VALUE = 0x00000A01
staticconstexpr

Definition at line 221 of file radio_lr1121.h.

◆ LR1121_REG_HIGH_ACP_WORKAROUND_ADDR

uint32_t esphome::home_io_control::LR1121_REG_HIGH_ACP_WORKAROUND_ADDR = 0x00F30054
staticconstexpr

High-ACP (adjacent channel power) TX-quality erratum: clear bit 30 of this register before every SetRx/SetTx.

Without it the chip's TX spectrum has excess spectral regrowth — plausibly why our own (tolerant) monitors decode our frames byte-exact while the awning's spec-compliant receiver mostly can't.

Definition at line 206 of file radio_lr1121.h.

◆ LR1121_REG_HIGH_ACP_WORKAROUND_MASK

uint32_t esphome::home_io_control::LR1121_REG_HIGH_ACP_WORKAROUND_MASK = 1UL << 30
staticconstexpr

Definition at line 207 of file radio_lr1121.h.

◆ LR1121_REG_HIGH_ACP_WORKAROUND_VALUE

uint32_t esphome::home_io_control::LR1121_REG_HIGH_ACP_WORKAROUND_VALUE = 0x00000000
staticconstexpr

Definition at line 208 of file radio_lr1121.h.

◆ LR1121_RESPONSE_PREAMBLE

uint16_t esphome::home_io_control::LR1121_RESPONSE_PREAMBLE = SX1262_RESPONSE_PREAMBLE
staticconstexpr

LR1121-specific preamble for response/continuation frames within an exchange.

Follows SX1262_RESPONSE_PREAMBLE (12 bytes), on the principle that a validated timing value encodes protocol-side reality more than a chip quirk; see there for the evidence. A value measured by loopback tuning on LR1121 hardware would fold back here.

Definition at line 234 of file tuning_config.h.

◆ LR1121_RFSWITCH_ENABLE_DIO5_DIO6

uint8_t esphome::home_io_control::LR1121_RFSWITCH_ENABLE_DIO5_DIO6 = 0x03
staticconstexpr

DIO5 + DIO6 are switch pins.

Definition at line 237 of file radio_lr1121.h.

◆ LR1121_RFSWITCH_GNSS

uint8_t esphome::home_io_control::LR1121_RFSWITCH_GNSS = 0x00
staticconstexpr

Unused; both low.

Definition at line 243 of file radio_lr1121.h.

◆ LR1121_RFSWITCH_RX

uint8_t esphome::home_io_control::LR1121_RFSWITCH_RX = 0x01
staticconstexpr

DIO5 high.

Definition at line 239 of file radio_lr1121.h.

◆ LR1121_RFSWITCH_STANDBY

uint8_t esphome::home_io_control::LR1121_RFSWITCH_STANDBY = 0x00
staticconstexpr

Both low.

Definition at line 238 of file radio_lr1121.h.

◆ LR1121_RFSWITCH_TX

uint8_t esphome::home_io_control::LR1121_RFSWITCH_TX = 0x02
staticconstexpr

DIO6 high (LP PA mode; unused).

Definition at line 240 of file radio_lr1121.h.

◆ LR1121_RFSWITCH_TX_HF

uint8_t esphome::home_io_control::LR1121_RFSWITCH_TX_HF = 0x00
staticconstexpr

2.4GHz path unused; both low.

Definition at line 242 of file radio_lr1121.h.

◆ LR1121_RFSWITCH_TX_HP

uint8_t esphome::home_io_control::LR1121_RFSWITCH_TX_HP = 0x02
staticconstexpr

DIO6 high (HP PA, the only TX path used).

Definition at line 241 of file radio_lr1121.h.

◆ LR1121_RFSWITCH_WIFI

uint8_t esphome::home_io_control::LR1121_RFSWITCH_WIFI = 0x00
staticconstexpr

Unused; both low.

Definition at line 244 of file radio_lr1121.h.

◆ LR1121_RX_BUFFER_BASE

uint8_t esphome::home_io_control::LR1121_RX_BUFFER_BASE = 0x00
staticconstexpr

Offset a reception is written to in the LR1121's data buffer.

Unlike the SX1262 there is no SetBufferBaseAddress on this chip — the whole 256-byte buffer is one area whose pointers the chip manages, and a fresh reception after SetRx starts at the bottom of it (the same buffer model RadioLR1121::write_buffer_'s own doc comment records for the TX direction). Used as the read offset for the length-driven receive; see RadioLR1121::early_rx_read_offset.

Definition at line 194 of file radio_lr1121.h.

◆ LR1121_SYNC_WORD_PARAM_24_BITS

uint8_t esphome::home_io_control::LR1121_SYNC_WORD_PARAM_24_BITS = 0x18
staticconstexpr

Sync word length: 24 bits, encoded as a literal bit count — cross-checked, identical encoding to SX1262's SX1262_SYNC_WORD_PARAM_24_BITS.

Definition at line 175 of file radio_lr1121.h.

◆ LR1121_TCXO_STARTUP_DELAY_TICKS_LSB

uint8_t esphome::home_io_control::LR1121_TCXO_STARTUP_DELAY_TICKS_LSB = 0x40
staticconstexpr

0x140 ticks at 30.52us/tick (32.768kHz RTC) is ~9.8ms (the SX1262's tick base differs, so the same tick count means a different time there) — harmless either way, since a longer startup is safe.

Definition at line 261 of file radio_lr1121.h.

◆ LR1121_TCXO_STARTUP_DELAY_TICKS_MID

uint8_t esphome::home_io_control::LR1121_TCXO_STARTUP_DELAY_TICKS_MID = 0x01
staticconstexpr

Definition at line 258 of file radio_lr1121.h.

◆ LR1121_TCXO_STARTUP_DELAY_TICKS_MSB

uint8_t esphome::home_io_control::LR1121_TCXO_STARTUP_DELAY_TICKS_MSB = 0x00
staticconstexpr

LR1121 TCXO voltage on the T3-S3 board — 3.0V, confirmed on real hardware.

Definition at line 257 of file radio_lr1121.h.

◆ MAIN_PARAMETER_CLOSED

uint16_t esphome::home_io_control::MAIN_PARAMETER_CLOSED = STATUS_POS_MAX
staticconstexpr

Main parameter that closes (or switches on) a device: 100 percent, the top of the position scale.

Definition at line 189 of file proto_device_types.h.

◆ MAIN_PARAMETER_INVALID

uint16_t esphome::home_io_control::MAIN_PARAMETER_INVALID = 0xFFFF
staticconstexpr

What position_main_parameter() returns for a value that is no position; no device accepts it.

Definition at line 193 of file proto_device_types.h.

◆ MAIN_PARAMETER_KEEP

uint16_t esphome::home_io_control::MAIN_PARAMETER_KEEP = static_cast<uint16_t>(POS_UNKNOWN) << BITS_PER_BYTE
staticconstexpr

16-bit main parameter D400 (POS_UNKNOWN in the high byte): keep the current value.

Sent with a functional parameter that carries the actual change (a heat pump's off, a water heater's operating mode).

Definition at line 532 of file proto_constants.h.

◆ MAIN_PARAMETER_OPEN

uint16_t esphome::home_io_control::MAIN_PARAMETER_OPEN = 0x0000
staticconstexpr

Main parameter that opens (or switches off) a device: percent 0.

Definition at line 187 of file proto_device_types.h.

◆ MAIN_PARAMETER_STOP

uint16_t esphome::home_io_control::MAIN_PARAMETER_STOP = main_parameter_of(POS_STOP)
staticconstexpr

Main parameter that stops a movement.

Definition at line 191 of file proto_device_types.h.

◆ MANAGEMENT_ACTION_PROBE_DEVICE

const char* esphome::home_io_control::MANAGEMENT_ACTION_PROBE_DEVICE = "probe_device"
inlineconstexpr

Native API action names of the two probe actions, shared with ManagementActions' registration table.

Definition at line 32 of file diagnostic_probes.h.

◆ MANAGEMENT_ACTION_PROBE_SWEEP

const char* esphome::home_io_control::MANAGEMENT_ACTION_PROBE_SWEEP = "probe_sweep"
inlineconstexpr

Definition at line 33 of file diagnostic_probes.h.

◆ MANUFACTURER_ASSA_ABLOY

uint8_t esphome::home_io_control::MANUFACTURER_ASSA_ABLOY = 5
staticconstexpr

ASSA ABLOY (locks, access).

Definition at line 718 of file proto_constants.h.

◆ MANUFACTURER_ATLANTIC_GROUP

uint8_t esphome::home_io_control::MANUFACTURER_ATLANTIC_GROUP = 12
staticconstexpr

Atlantic Group (heating, hot water).

Definition at line 725 of file proto_constants.h.

◆ MANUFACTURER_CIAT

uint8_t esphome::home_io_control::MANUFACTURER_CIAT = 9
staticconstexpr

CIAT (HVAC).

Definition at line 722 of file proto_constants.h.

◆ MANUFACTURER_HONEYWELL

uint8_t esphome::home_io_control::MANUFACTURER_HONEYWELL = 3
staticconstexpr

Honeywell.

Definition at line 716 of file proto_constants.h.

◆ MANUFACTURER_HORMANN

uint8_t esphome::home_io_control::MANUFACTURER_HORMANN = 4
staticconstexpr

Hörmann (garage doors, gates).

Definition at line 717 of file proto_constants.h.

◆ MANUFACTURER_ID_MAX

uint8_t esphome::home_io_control::MANUFACTURER_ID_MAX = 12
staticconstexpr

Maximum manufacturer ID with a known name in the lookup table.

Definition at line 707 of file proto_constants.h.

◆ MANUFACTURER_NIKO

uint8_t esphome::home_io_control::MANUFACTURER_NIKO = 6
staticconstexpr

Niko (switches, home automation).

Definition at line 719 of file proto_constants.h.

◆ MANUFACTURER_OVERKIZ

uint8_t esphome::home_io_control::MANUFACTURER_OVERKIZ = 11
staticconstexpr

OVERKIZ (Somfy connectivity platform).

Definition at line 724 of file proto_constants.h.

◆ MANUFACTURER_RENSON

uint8_t esphome::home_io_control::MANUFACTURER_RENSON = 8
staticconstexpr

Renson (ventilation, sun protection).

Definition at line 721 of file proto_constants.h.

◆ MANUFACTURER_SECUYOU

uint8_t esphome::home_io_control::MANUFACTURER_SECUYOU = 10
staticconstexpr

Secuyou (security).

Definition at line 723 of file proto_constants.h.

◆ MANUFACTURER_SOMFY

uint8_t esphome::home_io_control::MANUFACTURER_SOMFY = 2
staticconstexpr

Somfy (shutters, awnings, blinds).

Definition at line 715 of file proto_constants.h.

◆ MANUFACTURER_VELUX

uint8_t esphome::home_io_control::MANUFACTURER_VELUX = 1
staticconstexpr

IO-Homecontrol manufacturer ID constants.

These 1-based identifiers are assigned by the IO-Homecontrol alliance and appear in the discovery response payload at DISCOVERY_RESP_MANUFACTURER_OFFSET. VELUX (roof windows, skylights).

Definition at line 714 of file proto_constants.h.

◆ MANUFACTURER_WINDOW_MASTER

uint8_t esphome::home_io_control::MANUFACTURER_WINDOW_MASTER = 7
staticconstexpr

WINDOW MASTER (ventilation).

Definition at line 720 of file proto_constants.h.

◆ MAX_TRACKED_STATUS_POLL_WINDOW_MS

uint32_t esphome::home_io_control::MAX_TRACKED_STATUS_POLL_WINDOW_MS = 240000
staticconstexpr

Hard stop for bounded follow-up polling after a command, remote activity or the first moving report, measured from the moment the window opens (ADR 0061, amended by ADR 0079).

The longest cover travel measured is about 65 s, a VELUX roof window at its silent speed (docs/configuration/cover.md, travel speeds). A cover queued behind another on a shared supply runs after that one, so the worst case is two such travels, about 130 s: 240 s covers it with almost twice the margin, and a cover that is still reporting moving after that is shown at rest.

Definition at line 69 of file status_poll_policy.h.

◆ MODE_MASK

uint8_t esphome::home_io_control::MODE_MASK = 0x07
staticconstexpr

Definition at line 76 of file radio_sx1276.h.

◆ MODE_RX

uint8_t esphome::home_io_control::MODE_RX = 0x05
staticconstexpr

Definition at line 75 of file radio_sx1276.h.

◆ MODE_SLEEP

uint8_t esphome::home_io_control::MODE_SLEEP = 0x00
staticconstexpr

Definition at line 72 of file radio_sx1276.h.

◆ MODE_STDBY

uint8_t esphome::home_io_control::MODE_STDBY = 0x01
staticconstexpr

Definition at line 73 of file radio_sx1276.h.

◆ MODE_TX

uint8_t esphome::home_io_control::MODE_TX = 0x03
staticconstexpr

Definition at line 74 of file radio_sx1276.h.

◆ MOVING_POLL_MISSES_SKIPPED

uint8_t esphome::home_io_control::MOVING_POLL_MISSES_SKIPPED = 2
staticconstexpr

Consecutive failed polls of a moving device that are skipped rather than backed off.

A poll while a device is believed travelling is a single try (SCHEDULED_POLL_MAX_TRIES), so one lost frame fails it. Waiting the ladders' first step only leaves a gap in the position updates (issue #178). The first misses in a row keep the normal cadence instead (never slower than the ladder's first step); only the next one enters the ladder, which still catches a device that is really gone or answers with the wrong key, two polls later. The skipped misses do not count as ladder failures, so the next poll stays a single try.

Definition at line 60 of file status_poll_policy.h.

◆ NAMED_POSITIONS

NamedPosition esphome::home_io_control::NAMED_POSITIONS[]
inlineconstexpr
Initial value:
= {
{device_feature::FAVORITE, main_parameter_of(POS_FAVORITE), CoverCommand::FAVORITE, "FAVORITE"},
{device_feature::VENT, main_parameter_of(POS_FAVORITE, POS_VENT_MODIFIER), CoverCommand::VENT, "VENT"},
{device_feature::PEDESTRIAN, main_parameter_of(POS_FAVORITE, POS_PEDESTRIAN_MODIFIER), CoverCommand::PEDESTRIAN,
"PEDESTRIAN"},
{device_feature::PARTIAL, main_parameter_of(POS_FAVORITE, POS_PARTIAL_MODIFIER), CoverCommand::PARTIAL, "PARTIAL"},
}
constexpr uint16_t main_parameter_of(uint8_t high, uint8_t low=0)
Combine the two bytes of a main parameter as the EXECUTE payload carries them.
@ FAVORITE
Move to stored favorite/"My" position.
@ VENT
Move to ventilation position (window-type devices).
@ PEDESTRIAN
Move to the pedestrian position (gate openers); experimental.
@ PARTIAL
Move to the stored partial position (garage openers; issue #163).

The named positions.

PARTIAL (0xD809) runs a garage opener's stored partial position (issue #163). PEDESTRIAN is experimental: its value rests on no public source. A motor without the position refuses either (PARAMETER_UNKNOWN) and nothing moves.

Todo
Confirm 0xD807 on a real gate opener. Adding one is a bit (with its evidence), a row here, a CoverCommand value, the rows that have it in the type table, and a companion button in the Python codegen.

Definition at line 214 of file proto_device_types.h.

◆ NIGHT_LIGHT_MIN_ON_PERCENT

uint8_t esphome::home_io_control::NIGHT_LIGHT_MIN_ON_PERCENT = 1
staticconstexpr

Brightness register value for a light switched on at a brightness that rounds to 0 %: the lowest level that is still on.

Definition at line 23 of file platform_climate_controls.cpp.

◆ NODE_ID_SIZE

uint8_t esphome::home_io_control::NODE_ID_SIZE = 3
staticconstexpr

Device/node addresses are 3 bytes (e.g., "123ABC").

Definition at line 20 of file proto_sizes.h.

◆ NODE_ID_STRING_SIZE

uint8_t esphome::home_io_control::NODE_ID_STRING_SIZE = (NODE_ID_SIZE * 2) + 1
staticconstexpr

Uppercase hex node ID plus null terminator.

Definition at line 21 of file proto_sizes.h.

◆ NORMAL_START_PREAMBLE

uint16_t esphome::home_io_control::NORMAL_START_PREAMBLE = 32
staticconstexpr

Default for TuningConfig::normal_start_preamble — the preamble in front of a directed start frame whose target is not a low-power / duty-cycled device (CTRL1_LOW_POWER clear).

An always-listening receiver does not need the ~213 ms LONG_PREAMBLE wake-up burst, and some receivers never lock onto one that long; a normal start frame gets this shorter preamble instead, matching what a reference hub sends to an always-alive device.

32 bytes = 256 bits is well above the ~12-byte response preamble a short-turnaround chip uses, ~6.7 ms of air time, and two orders of magnitude below the 1024-byte burst. 8 bytes is a proven lower bound against paired always-alive devices but nothing bounds where a start frame stops being heard, so 32 is the defensible middle — and normal_start_preamble is a live tuning knob so a wrong guess costs a number change, not a rebuild.

Definition at line 71 of file proto_timing.h.

◆ NUMBER_PARAMS

TuningNumberParam esphome::home_io_control::NUMBER_PARAMS[]
staticconstexpr

Definition at line 24 of file tuning_registry.cpp.

◆ ONEWAY_BURST_INTERVAL_MS

uint32_t esphome::home_io_control::ONEWAY_BURST_INTERVAL_MS = 40
staticconstexpr

Gap between those copies.

Three gaps between four copies is 3 * 40 = 120 ms of pure delay, fixed regardless of the identity's power class. What that adds up to on the wire depends on each copy's own airtime, which is a per-identity, per-copy choice (OneWayPowerClass, oneway_controller.h; ADR 0038): a burst with the long preamble on every copy runs well over a second, one with the short preamble on every copy a few hundred ms. send_burst()'s doxygen (oneway_transmitter.h) has both real shapes.

Definition at line 329 of file proto_timing.h.

◆ ONEWAY_BURST_REPEATS

uint8_t esphome::home_io_control::ONEWAY_BURST_REPEATS = 4
staticconstexpr

One-way (1W) transmit cadence.

A 1W command is fire-and-forget: nothing replies, so there is no acknowledgement to retry on and no way to learn a frame was missed. Repetition is the reliability mechanism — real remotes send the same frame four times, and a receiving device treats the set as one command because all four carry the same sequence. These are protocol values shared by every 1W transmitter, not radio tuning: they do not vary by chip and must not be moved into a driver or a TuningConfig field.

Both values are assumed, not measured on this project's hardware: four copies, 40 ms apart. Receive-side timestamps of consecutive copies in a capture log can show a smaller gap (roughly 25 ms), which is not a contradiction: they measure when the receiver heard each copy, not the configured gap. Do not "correct" 40 down to 25 on the strength of them.

Erring long would in any case be the safe direction — a device needs only one of the four copies to land, so a wider spacing costs nothing and leaves more room on a shared band.

The resulting burst duration — see ONEWAY_BURST_INTERVAL_MS's own comment and send_burst()'s doxygen (oneway_transmitter.h) for what it decomposes into per power class — is what ONEWAY_QUIET_PERIOD_MS (status_poll_policy.h) is sized against when it holds background polls back during 1W activity. Copies of each 1W command sent per press.

Definition at line 322 of file proto_timing.h.

◆ ONEWAY_EXECUTE_ACEI

uint8_t esphome::home_io_control::ONEWAY_EXECUTE_ACEI
staticconstexpr
Initial value:
=
static constexpr uint8_t ACEI_LEVEL_USER_HIGH
High-priority user controller.
static constexpr uint8_t ACEI_EXTENDED_SHIFT
Shift for extended field extraction.
static constexpr uint8_t ACEI_LEVEL_SHIFT
Shift for priority level extraction.
static constexpr uint8_t ACEI_VALID_BIT
ACEI byte bit-field definitions.

ACEI byte for a 1W CMD_EXECUTE frame — the Somfy-shaped default.

Level 2 (user_high), extended-info bit set. The 2W EXECUTE_ACEI (proto_commands.cpp) pins level 3 to a real captured 2W hub; this level-2 value is what the published 1W reference vector (tests/corpus/captures/oneway/reference_1w_oneway_execute_iv_vector.yaml) and every Somfy 1W remote frame in the corpus (7+ frames across several nodes) carry. Lives here (not proto_commands.cpp) so oneway_controller.h's resolve_oneway_wire_profile() can name it without the protocol layer depending on the controller layer. Composition: (ACEI_LEVEL_USER_HIGH << 5) | (1 << 1) | 1 = 0x43.

Definition at line 837 of file proto_constants.h.

◆ ONEWAY_EXECUTE_ACEI_VELUX

uint8_t esphome::home_io_control::ONEWAY_EXECUTE_ACEI_VELUX = (ACEI_LEVEL_USER_DEFAULT << ACEI_LEVEL_SHIFT) | ACEI_VALID_BIT
staticconstexpr

ACEI byte for a 1W CMD_EXECUTE frame from a VELUX KLI-class remote.

Level 3 (user_default), extended-info bits clear. Confidence: n=1 — one corpus frame (tests/corpus/captures/oneway/velux_kli313_oneway_stop.yaml). An unidentified remote's frames also carry 0x61 (tests/corpus/captures/oneway/unidentified_1w_remote_oneway_execute_open_and_stop_acei_61_sx1276.yaml), but its manufacturer is unknown, so they can't corroborate the VELUX attribution. execute_acei: on the identity is the escape hatch. Composition: (ACEI_LEVEL_USER_DEFAULT << 5) | ACEI_VALID_BIT = 0x61.

Definition at line 849 of file proto_constants.h.

◆ ONEWAY_EXECUTE_MIN_DATA_LEN

uint8_t esphome::home_io_control::ONEWAY_EXECUTE_MIN_DATA_LEN = 4
staticconstexpr

Minimum data bytes for decode of execute/activate‑mode intent fields.

Definition at line 356 of file proto_codecs.cpp.

◆ ONEWAY_INTENT_BUFFER_SIZE

size_t esphome::home_io_control::ONEWAY_INTENT_BUFFER_SIZE = 24
staticconstexpr

Buffer size for the decoded 1W main-intent string.

Definition at line 209 of file proto_codecs.h.

◆ ONEWAY_POLL_DEFER_CAP_MS

uint32_t esphome::home_io_control::ONEWAY_POLL_DEFER_CAP_MS = 5000
staticconstexpr

Hard cap on how long sustained 1W traffic may hold a background poll back in total, measured from the start of the burst rather than the most recent frame.

ONEWAY_QUIET_PERIOD_MS re-arms on every frame, so without this cap traffic arriving faster than the quiet period apart — a stuck remote, a chatty neighbour's sensor — could starve background polls, and therefore device state freshness, indefinitely. Comfortably above a legitimate ~160ms burst; bounds the worst case without meaningfully affecting normal operation.

Definition at line 106 of file status_poll_policy.h.

◆ ONEWAY_POSITION_FULLY_CLOSED

uint8_t esphome::home_io_control::ONEWAY_POSITION_FULLY_CLOSED = 100
staticconstexpr

Definition at line 285 of file oneway_controller.h.

◆ ONEWAY_POSITION_FULLY_OPEN

uint8_t esphome::home_io_control::ONEWAY_POSITION_FULLY_OPEN = 0
staticconstexpr

User-scale position meaning "fully closed" (0 means fully open).

Named here because the two values are what OPEN and CLOSE are before OneWayTransmitter::send_position() mirrors them for an inverted identity — see encode_oneway_action() and effective_inverted().

Definition at line 284 of file oneway_controller.h.

◆ ONEWAY_QUIET_PERIOD_MS

uint32_t esphome::home_io_control::ONEWAY_QUIET_PERIOD_MS = 700
staticconstexpr

Hold queued background polls back for this long after any 1W frame, so a poll the hub itself scheduled does not occupy the (half-duplex) radio while the remote is still transmitting.

Comfortably longer than the ~160ms reliability burst; short enough to barely shift poll timing otherwise. Only background polls yield — see decisions::defer_background_poll_for_1w_activity().

Definition at line 99 of file status_poll_policy.h.

◆ ONEWAY_SEQUENCE_ACCEPTANCE_WINDOW

uint16_t esphome::home_io_control::ONEWAY_SEQUENCE_ACCEPTANCE_WINDOW = 1000
staticconstexpr

How far ahead of its stored high-water mark a device will still accept a jump.

1000 is the assumed acceptance window (not measured on hardware), and it is the ceiling every forward skip in this file has to stay under: a jump past it fails exactly like a stale counter, and just as silently.

Definition at line 41 of file oneway_sequence_store.h.

◆ ONEWAY_SEQUENCE_REBOOT_HEADROOM

uint16_t esphome::home_io_control::ONEWAY_SEQUENCE_REBOOT_HEADROOM = 8
staticconstexpr

How many consecutive unclean reboots the stride must survive while staying inside a device's acceptance window.

Eight is far past plausible; the point is headroom, not precision.

Definition at line 53 of file oneway_sequence_store.h.

◆ ONEWAY_SEQUENCE_STRIDE

uint16_t esphome::home_io_control::ONEWAY_SEQUENCE_STRIDE = 8
staticconstexpr

How many sequences one flash write reserves.

Persisting on every command would write flash on every button press. Reserving a block instead trades that for a bounded forward skip: an unclean reboot forfeits the block's unused remainder, which is the safe direction. Small on purpose — see the static_assert below for the bound, and raise it only if that bound still holds.

Definition at line 49 of file oneway_sequence_store.h.

◆ ORIGINATOR_AUTOMATIC_CYCLE

uint8_t esphome::home_io_control::ORIGINATOR_AUTOMATIC_CYCLE = 0xFE
staticconstexpr

Automatic cycle / external access.

Definition at line 776 of file proto_constants.h.

◆ ORIGINATOR_EMERGENCY

uint8_t esphome::home_io_control::ORIGINATOR_EMERGENCY = 0xFF
staticconstexpr

Emergency command (never disabled).

Definition at line 777 of file proto_constants.h.

◆ ORIGINATOR_ENVIRONMENT

uint8_t esphome::home_io_control::ORIGINATOR_ENVIRONMENT = 0x0D
staticconstexpr

Unspecified environment sensor.

Definition at line 774 of file proto_constants.h.

◆ ORIGINATOR_LIFESTYLE

uint8_t esphome::home_io_control::ORIGINATOR_LIFESTYLE = 0x07
staticconstexpr

Lifestyle scenario controller.

Definition at line 769 of file proto_constants.h.

◆ ORIGINATOR_LOAD_SHEDDING

uint8_t esphome::home_io_control::ORIGINATOR_LOAD_SHEDDING = 0x0B
staticconstexpr

Load-shedding manager.

Definition at line 772 of file proto_constants.h.

◆ ORIGINATOR_LOCAL_LIGHT

uint8_t esphome::home_io_control::ORIGINATOR_LOCAL_LIGHT = 0x0C
staticconstexpr

Local light sensor.

Definition at line 773 of file proto_constants.h.

◆ ORIGINATOR_LOCAL_USER

uint8_t esphome::home_io_control::ORIGINATOR_LOCAL_USER = 0x00
staticconstexpr

Command originator codes indicating what or who triggered a command.

The originator byte is the first byte of the CMD_EXECUTE payload. It tells the actuator (and any eavesdropping controller) who initiated the movement. This is useful for understanding device-initiated status updates.

ORIGINATOR_WIND_SENSOR and ORIGINATOR_RAIN_SENSOR are both field-confirmed on real hardware — a single combined wind/rain protection station (issue #27, community capture) broadcasting both values minutes apart: tests/corpus/captures/oneway/wind_sensor_oneway_favorite_wind_originator_sx1276.yaml and tests/corpus/captures/oneway/wind_sensor_oneway_favorite_rain_originator_sx1276.yaml. The latter is this project's first real-hardware confirmation of ORIGINATOR_RAIN_SENSOR at all — every prior wind/rain-station capture used ORIGINATOR_WIND_SENSOR only. User pressed a button on the actuator.

Definition at line 762 of file proto_constants.h.

◆ ORIGINATOR_MYSELF

uint8_t esphome::home_io_control::ORIGINATOR_MYSELF = 0x10
staticconstexpr

Actuator decided to move by itself.

Definition at line 775 of file proto_constants.h.

◆ ORIGINATOR_RAIN_SENSOR

uint8_t esphome::home_io_control::ORIGINATOR_RAIN_SENSOR = 0x02
staticconstexpr

Rain sensor triggered the movement.

Definition at line 764 of file proto_constants.h.

◆ ORIGINATOR_SAAC

uint8_t esphome::home_io_control::ORIGINATOR_SAAC = 0x08
staticconstexpr

Stand-alone automatic controller (SAAC).

Definition at line 770 of file proto_constants.h.

◆ ORIGINATOR_SECURITY

uint8_t esphome::home_io_control::ORIGINATOR_SECURITY = 0x04
staticconstexpr

Security controlling device (SCD) action.

Definition at line 766 of file proto_constants.h.

◆ ORIGINATOR_SMART_CONTROLLER

uint8_t esphome::home_io_control::ORIGINATOR_SMART_CONTROLLER = 0x06
staticconstexpr

Smart function controller.

Definition at line 768 of file proto_constants.h.

◆ ORIGINATOR_TIMER

uint8_t esphome::home_io_control::ORIGINATOR_TIMER = 0x03
staticconstexpr

Timer or schedule triggered the movement.

Definition at line 765 of file proto_constants.h.

◆ ORIGINATOR_UPS

uint8_t esphome::home_io_control::ORIGINATOR_UPS = 0x05
staticconstexpr

Uninterruptible power supply action.

Definition at line 767 of file proto_constants.h.

◆ ORIGINATOR_USER_REMOTE

uint8_t esphome::home_io_control::ORIGINATOR_USER_REMOTE = 0x01
staticconstexpr

User sent command from a remote control.

Definition at line 763 of file proto_constants.h.

◆ ORIGINATOR_WIND_SENSOR

uint8_t esphome::home_io_control::ORIGINATOR_WIND_SENSOR = 0x09
staticconstexpr

Wind sensor triggered the movement.

Definition at line 771 of file proto_constants.h.

◆ PAIRING_DISCOVER_CONFIRM_ACK_TIMEOUT_MS

uint32_t esphome::home_io_control::PAIRING_DISCOVER_CONFIRM_ACK_TIMEOUT_MS = 1500
inlineconstexpr

Wait window per discover-confirm try.

Definition at line 60 of file pairing_engine.h.

◆ PAIRING_DISCOVER_CONFIRM_TRIES

uint8_t esphome::home_io_control::PAIRING_DISCOVER_CONFIRM_TRIES = 3
inlineconstexpr

Max tries for the discover-confirm (0x2C) step.

Definition at line 59 of file pairing_engine.h.

◆ PAIRING_DISCOVERY_INITIAL_DWELL_MS

uint16_t esphome::home_io_control::PAIRING_DISCOVERY_INITIAL_DWELL_MS = 300
staticconstexpr

Dwell on CH2 before discovery hopping begins.

Definition at line 359 of file proto_timing.h.

◆ PAIRING_DISCOVERY_MAX_ATTEMPTS

uint8_t esphome::home_io_control::PAIRING_DISCOVERY_MAX_ATTEMPTS = 3
inlineconstexpr

Retry discovery TX up to this many times.

Definition at line 56 of file pairing_engine.h.

◆ PAIRING_DISCOVERY_RESPONSE_TIMEOUT_MS

uint32_t esphome::home_io_control::PAIRING_DISCOVERY_RESPONSE_TIMEOUT_MS = 2000
inlineconstexpr

Discovery wait window after sending 0x28.

Definition at line 55 of file pairing_engine.h.

◆ PAIRING_DISCOVERY_WAIT_MS

uint16_t esphome::home_io_control::PAIRING_DISCOVERY_WAIT_MS = 2000
staticconstexpr

Canonical defaults for the chip-neutral runtime-tunable pairing/discovery parameters.

These are the single source of truth for the diagnostics tuning layer: TuningConfig initializes its fields from them, and the ESPHome YAML schema falls back to them when a key is omitted. Adjust a value here and both the compiled default and the documented YAML default follow. Chip-specific tuning defaults live in tuning_config.h instead. See Pairing/Radio Diagnostics Tuning. Wait window after sending each discovery command.

Definition at line 358 of file proto_timing.h.

◆ PAIRING_KEY_CHALLENGE_TIMEOUT_MS

uint32_t esphome::home_io_control::PAIRING_KEY_CHALLENGE_TIMEOUT_MS = 500
inlineconstexpr

Wait window for the device's 0x3C challenge.

Definition at line 57 of file pairing_engine.h.

◆ PAIRING_KEY_CONFIRM_TIMEOUT_MS

uint32_t esphome::home_io_control::PAIRING_KEY_CONFIRM_TIMEOUT_MS = 500
inlineconstexpr

Wait for 0x33 key confirm after sending 0x32.

Definition at line 58 of file pairing_engine.h.

◆ PAIRING_KEY_EXCHANGE_RETRIES

uint8_t esphome::home_io_control::PAIRING_KEY_EXCHANGE_RETRIES = 3
staticconstexpr

Retries for the authenticated key-exchange phase.

Definition at line 360 of file proto_timing.h.

◆ PAIRING_KEY_INIT_DELAY_MS

uint16_t esphome::home_io_control::PAIRING_KEY_INIT_DELAY_MS = 300
staticconstexpr

Default for TuningConfig::pairing_key_init_delay_ms — pause after the discover-confirm step (whether it was ACKed, refused, or silent) and before CMD_KEY_INIT (0x31).

No capture precedent for 300 ms specifically: real hubs take several seconds longer here, and appear to spend that time re-broadcasting 0x28 to look for more devices, which this project's single-device discovery phase has no equivalent of. 300 ms is the short end: a Somfy Izymo dimmer pairs with both 300 ms and 5000 ms here, so the default keeps pairing fast, and pairing_key_init_delay_ms can lengthen it for a device that needs a later 0x31.

Definition at line 369 of file proto_timing.h.

◆ PAIRING_KEY_PULL_REPLY_TIMEOUT_MS

uint32_t esphome::home_io_control::PAIRING_KEY_PULL_REPLY_TIMEOUT_MS = PAIRING_KEY_CONFIRM_TIMEOUT_MS
inlineconstexpr

Wait for the device's key transfer (0x32) after our key transfer request (0x38).

Same window as the 0x33 wait: both are a unicast reply to a unicast pairing frame.

Definition at line 67 of file pairing_engine.h.

◆ PAIRING_KEY_VERIFY_MAX_TRIES

uint8_t esphome::home_io_control::PAIRING_KEY_VERIFY_MAX_TRIES = 2
inlineconstexpr

Tries for the authenticated name read that checks whether a device took our key although the key exchange did not confirm.

Two, not EXCHANGE_RETRY_COUNT: the device answered moments ago, so one retry covers a lost frame, and the check runs only on an attempt that is already failing.

Definition at line 64 of file pairing_engine.h.

◆ PAIRING_RECENT_ONE_WAY_SIGHTING_WINDOW_MS

uint32_t esphome::home_io_control::PAIRING_RECENT_ONE_WAY_SIGHTING_WINDOW_MS = 15000
inlineconstexpr

How recent a RecentOneWayPairingSighting has to be, relative to discover_and_pair() starting, to still count as evidence for this attempt.

Generous relative to the doc's "a few seconds" PROG-then-press guidance: real field reports (issue #27) show gaps up to ~4-7 s between the PROG gesture and pressing "Discover & Pair" in the app, so a tight window would reintroduce the same miss it's meant to fix. Not tied to ONEWAY_QUIET_PERIOD_MS (status_poll_policy.h, 700 ms) — that constant is about collapsing one remote's repeat burst, a different, much shorter timescale than "how long ago did the user press PROG."

Definition at line 75 of file pairing_engine.h.

◆ PAIRING_SET_CONFIG1_MAX_TRIES

uint8_t esphome::home_io_control::PAIRING_SET_CONFIG1_MAX_TRIES = 1
staticconstexpr

Tries for pairing's phase-3 SetConfig1 (0x6F).

One: no device on record accepts it (a Somfy Izymo answers FE 28, a VELUX SSL solar roller shutter stays silent) and no real controller sends it, so a retry only adds 0.7–0.9 s of blocking and a frame of airtime to every pairing with a silent device. A challenged try still completes its 0x3C/0x3D round within the one try.

Definition at line 235 of file proto_timing.h.

◆ PAIRING_STALE_PRESS_MS

uint32_t esphome::home_io_control::PAIRING_STALE_PRESS_MS = 1000
staticconstexpr

How long after a pairing attempt ends a Discover & Pair press counts as made during it.

Pairing blocks loop() for up to about 20 s, so a press made meanwhile is handled a few milliseconds after the attempt ends (issue #165: the queued second attempt found nothing, the radiator had left its pairing mode). A press a person makes after reading the result comes seconds later.

Definition at line 84 of file hub_core.h.

◆ PAIRING_TELEMETRY_MAX_EVENTS

uint8_t esphome::home_io_control::PAIRING_TELEMETRY_MAX_EVENTS = 32
staticconstexpr

Maximum number of events recorded per pairing attempt.

Events beyond this bound are still counted in PairingTelemetry::heard_count() but not stored — the array is a fixed-size ring-free buffer (first N events), not a true ring.

Definition at line 45 of file pairing_telemetry.h.

◆ PERIODIC_POLL_FAILURES_BEFORE_UNKNOWN

uint8_t esphome::home_io_control::PERIODIC_POLL_FAILURES_BEFORE_UNKNOWN = 3
staticconstexpr

Consecutive unanswered polls after which the last reading is no longer usable.

Definition at line 45 of file periodic_poll_policy.h.

◆ PERIODIC_POLL_INITIAL_DELAY_MS

uint32_t esphome::home_io_control::PERIODIC_POLL_INITIAL_DELAY_MS = 15000
staticconstexpr

Earliest first poll after boot.

Sits after the entities' initial status request (INITIAL_STATUS_REQUEST_DELAY_MS) so the boot burst is spread over time.

Definition at line 25 of file periodic_poll_policy.h.

◆ PERIODIC_POLL_INITIAL_SPREAD_MS

uint32_t esphome::home_io_control::PERIODIC_POLL_INITIAL_SPREAD_MS = 30000
staticconstexpr

Width of the random window added to PERIODIC_POLL_INITIAL_DELAY_MS, so devices do not start in step.

Definition at line 27 of file periodic_poll_policy.h.

◆ PERIODIC_POLL_JITTER_PERCENT

uint32_t esphome::home_io_control::PERIODIC_POLL_JITTER_PERCENT = 10
staticconstexpr

Per-poll offset, as a percentage of the interval, applied symmetrically around it.

At the one-minute minimum that is six seconds either way — more than one exchange lasts — so two devices sharing an interval drift apart within a few polls. Symmetric on purpose: the mean stays the configured interval, so the transmit time per hour in the documentation stays true.

Definition at line 32 of file periodic_poll_policy.h.

◆ PERIODIC_POLL_MAX_TRIES

uint8_t esphome::home_io_control::PERIODIC_POLL_MAX_TRIES = 2
staticconstexpr

Tries per poll.

A missed poll only costs one interval of latency, so the full retry budget is not worth its transmit time here.

Definition at line 43 of file periodic_poll_policy.h.

◆ PERIODIC_POLL_MISS_RETRY_MS

uint32_t esphome::home_io_control::PERIODIC_POLL_MISS_RETRY_MS = 60000
staticconstexpr

Delay before asking once more after a poll the device left unanswered (silence, or a challenge with no final answer).

A solar motor misses about one poll in four in the field (issue #156) and answers a second try within seconds as often as a first, so one quick retry keeps a reading from going stale over a whole interval. Only an unanswered retry counts as a miss.

Definition at line 40 of file periodic_poll_policy.h.

◆ PERIODIC_POLL_MOVING_RETRY_MS

uint32_t esphome::home_io_control::PERIODIC_POLL_MOVING_RETRY_MS = 30000
staticconstexpr

Delay before asking again when the device is moving.

Keeps the poll out of a running manoeuvre without waiting a whole interval.

Definition at line 35 of file periodic_poll_policy.h.

◆ POS_DEFAULT

uint8_t esphome::home_io_control::POS_DEFAULT = 0xD3
staticconstexpr

Wire value for the default position command.

Moves the actuator to its factory or user-configured default position.

Definition at line 548 of file proto_constants.h.

◆ POS_FAVORITE

uint8_t esphome::home_io_control::POS_FAVORITE = 0xD8
staticconstexpr

Wire value: move to favorite/"My" position.

Definition at line 529 of file proto_constants.h.

◆ POS_FORCE_OPEN

uint8_t esphome::home_io_control::POS_FORCE_OPEN = 0x64
staticconstexpr

Ambiguous wire value used only for passive 1W-traffic intent decoding (decode_1w_main_intent() / oneway_intent_to_target() in proto_codecs.cpp).

0x64 (100) is simultaneously the ordinary doubled-position wire value for 50% and a value some physical 1W remotes send for their "force open" button — the protocol has no dedicated override code, so the two are indistinguishable on the wire. For passively decoding someone else's remote traffic, "FORCE_OPEN" is the more useful diagnostic label (physical remotes rarely send a numeric 50%). This is NOT used by any outbound builder in this codebase: real-hardware testing confirmed that sending main=0x64 as an outbound 2W CMD_EXECUTE command makes a real device move to 50% open, not bypass anything — see create_force_open() in proto_commands.cpp for the actual (ACEI-priority-based) force-open implementation.

Definition at line 562 of file proto_constants.h.

◆ POS_PARTIAL_MODIFIER

uint8_t esphome::home_io_control::POS_PARTIAL_MODIFIER = 0x09
staticconstexpr

Modifier byte for the partial-position command (POS_FAVORITE main byte, as for ventilation).

Sent by the Partial Position button on garage openers, which run their stored partial position (issue #163).

Definition at line 579 of file proto_constants.h.

◆ POS_PEDESTRIAN_MODIFIER

uint8_t esphome::home_io_control::POS_PEDESTRIAN_MODIFIER = 0x07
staticconstexpr

Modifier byte for the pedestrian-position command (POS_FAVORITE main byte, as for ventilation).

Sent by the Pedestrian Position button on gate openers.

Todo
Confirm on a real gate opener that main=0xD8, modifier=0x07 opens it to a pedestrian position (experimental).

Definition at line 574 of file proto_constants.h.

◆ POS_SECURED_TARGET

uint8_t esphome::home_io_control::POS_SECURED_TARGET = 0xD1
staticconstexpr

Wire value for the secured target position command.

Moves the actuator to its pre-programmed secured/safety position from the Execution Parameter Buffer. Typically sent by environmental sensors (wind, rain) to retract an awning or close a shutter to a wind-safe state.

Definition at line 543 of file proto_constants.h.

◆ POS_STOP

uint8_t esphome::home_io_control::POS_STOP = 0xD2
staticconstexpr

Position values in the IO protocol.

Normal positions are 0-100 (0=fully open, 100=fully closed). Special values above 100 are control commands encoded as the "main" parameter byte in CMD_EXECUTE payloads. These are internal wire constants — callers should prefer CoverCommand for type-safe command dispatch. Wire value: stop movement.

Definition at line 527 of file proto_constants.h.

◆ POS_UNKNOWN

uint8_t esphome::home_io_control::POS_UNKNOWN = 0xD4
staticconstexpr

Wire value: position unknown / keep current.

Definition at line 528 of file proto_constants.h.

◆ POS_VENT_MODIFIER

uint8_t esphome::home_io_control::POS_VENT_MODIFIER = 0x03
staticconstexpr

Modifier byte for the ventilation command.

Both favorite and ventilation use POS_FAVORITE (0xD8) as the main parameter byte, but ventilation sets the secondary byte (main[1]) to 0x03 while favorite leaves it 0x00.

Definition at line 568 of file proto_constants.h.

◆ POSITION_TEXT_BUFFER_SIZE

size_t esphome::home_io_control::POSITION_TEXT_BUFFER_SIZE = 16
inlineconstexpr

Buffer for formatted position strings such as "100%".

Definition at line 67 of file hub_core.h.

◆ POSITION_WIRE_MAX

uint8_t esphome::home_io_control::POSITION_WIRE_MAX = 200
staticconstexpr

Highest doubled-position wire value: 100% * POSITION_WIRE_SCALE.

A main byte at or below this is an ordinary position; above it, one of the POS_* special codes.

Definition at line 590 of file proto_constants.h.

◆ POSITION_WIRE_SCALE

uint8_t esphome::home_io_control::POSITION_WIRE_SCALE = 2
staticconstexpr

Scale factor between a 0-100 percent position and its CMD_EXECUTE main-byte wire value.

Percent 0-100 maps to wire 0-200 (wire = percent * POSITION_WIRE_SCALE), leaving 201-255 free for the POS_* special codes above. Builders multiply by this to encode a position; decoders divide by it to recover one — both directions belong on this one constant so they cannot silently drift apart into two different bare "2"s.

Definition at line 587 of file proto_constants.h.

◆ POWER_SAVE_ALWAYS_ALIVE

uint8_t esphome::home_io_control::POWER_SAVE_ALWAYS_ALIVE = 0
staticconstexpr

Power save mode values from the Multi Information Byte.

Extracted from flags & DISCOVERY_FLAGS_POWER_SAVE_MASK. Devices in low-power mode require long preamble (1024 bytes) to wake their receiver. Device is always listening — short preamble works.

Definition at line 909 of file proto_constants.h.

◆ POWER_SAVE_LOW_POWER

uint8_t esphome::home_io_control::POWER_SAVE_LOW_POWER = 1
staticconstexpr

Device sleeps — needs long preamble to wake.

Definition at line 910 of file proto_constants.h.

◆ PREAMBLE_LINGER_DWELL_MS

uint32_t esphome::home_io_control::PREAMBLE_LINGER_DWELL_MS = 15
staticconstexpr

Preamble/sync linger extension for a rotating listen (ListenSpec::linger_dwell_ms): how much longer to stay on a channel once a frame is visibly incoming, so a hop doesn't cut it off mid-reception.

Sized to a frame's air time, not to a hop slice, so it does not need to change when a chip's hop slice does. Shared by every rotating listen (pairing discovery, broadcast roll-call): both wait for the same class of short protocol frame, so there is no measured reason for them to differ.

Definition at line 105 of file proto_timing.h.

◆ PREDICTED_END_DROPS_PROGRESS_WITHIN_MS

uint32_t esphome::home_io_control::PREDICTED_END_DROPS_PROGRESS_WITHIN_MS = 1000
staticconstexpr

A progress read due within this long before the predicted-end read is dropped: the settling read follows at once and a read of its own in front of it only fills the air.

Definition at line 135 of file status_poll_policy.h.

◆ PREDICTED_END_MARGIN_MS

uint32_t esphome::home_io_control::PREDICTED_END_MARGIN_MS = 1000
staticconstexpr

Definition at line 132 of file status_poll_policy.h.

◆ PREDICTED_END_STALE_REPORTS

uint8_t esphome::home_io_control::PREDICTED_END_STALE_REPORTS = 3
staticconstexpr

Reports in a row whose remaining time did not fall below the previous one before the count is taken as frozen and the predicted-end read is given up (progress reads only).

Definition at line 138 of file status_poll_policy.h.

◆ PRIVATE_GET_POSITION_STATUS

uint8_t esphome::home_io_control::PRIVATE_GET_POSITION_STATUS = 0x03
staticconstexpr

CMD_PRIVATE (0x03) function ID for a position-status request — data[0] of the payload.

The only function ID this codebase has ever captured on its own wire: it is what create_get_status() freezes create_private_function() at, and the default function_id of create_get_status_extended(). Lives here rather than in proto_commands.cpp because that default argument is spelled in proto_commands.h.

Definition at line 612 of file proto_constants.h.

◆ PRIVATE_RESPONSE_LAST_COMMAND_OFFSET

uint8_t esphome::home_io_control::PRIVATE_RESPONSE_LAST_COMMAND_OFFSET = 8
inlineconstexpr

Offset of the last-command record within each status-bearing payload.

Both status-bearing frame types carry the same 4-byte record — three bytes of node ID for the controller that last commanded the device, then that command's Command Originator byte — and 0x71's whole payload is shifted +3 relative to 0x04's, exactly as its target/current position fields are. Confirmed on one device in one session across both frame types: tests/corpus/captures/statuspoll/somfy_rs100_statuspoll_kig300_sx1276.yaml, device E461E9, which names controller BE FE DB at 0x04 data[8..10] and 0x71 data[11..13] in the same capture — including in a 0x71 addressed to a different controller, which is what rules out "this is just the destination echoed back".

Definition at line 372 of file proto_codecs.h.

◆ PRIVATE_STATUS_MP_FP

uint8_t esphome::home_io_control::PRIVATE_STATUS_MP_FP = 0x01
staticconstexpr

CMD_PRIVATE function ID at data[0].

Definition at line 620 of file proto_constants.h.

◆ RADIO_PACKET_BUFFER_SIZE

uint8_t esphome::home_io_control::RADIO_PACKET_BUFFER_SIZE
inlineconstexpr
Initial value:
=
64

Scratch buffer size for raw radio packets and recovered frames.

Definition at line 24 of file radio_interface.h.

◆ RAIN_EVIDENCE_HOLD_MS

uint32_t esphome::home_io_control::RAIN_EVIDENCE_HOLD_MS = 2UL * 60UL * 60UL * 1000UL
staticconstexpr

How long a status reply that named the rain sensor as the last commander stays usable as evidence that rain protection is active (see decisions::is_rain_limited(), rule b).

It is the memory for recognising a clamped position command: while it is fresh, a stopped device that ended up somewhere other than the commanded target is labelled as limited by rain. Too short, and a long shower is no longer recognised for the second and later open attempts; too long, and an unrelated clamp (end stop, obstacle, another protection) after the rain has stopped is mislabelled. A first estimate: the window's own rain-limitation timer is not known, so this is 2 hours.

Definition at line 339 of file proto_timing.h.

◆ REG_AFC_BW

uint8_t esphome::home_io_control::REG_AFC_BW = 0x13
staticconstexpr

AFC bandwidth.

Definition at line 50 of file radio_sx1276.h.

◆ REG_AFC_FEI

uint8_t esphome::home_io_control::REG_AFC_FEI = 0x1A
staticconstexpr

AFC auto clear.

Definition at line 51 of file radio_sx1276.h.

◆ REG_BITRATE_LSB

uint8_t esphome::home_io_control::REG_BITRATE_LSB = 0x03
staticconstexpr

Definition at line 38 of file radio_sx1276.h.

◆ REG_BITRATE_MSB

uint8_t esphome::home_io_control::REG_BITRATE_MSB = 0x02
staticconstexpr

Bit rate MSB = FXOSC / bitrate.

Definition at line 37 of file radio_sx1276.h.

◆ REG_DIO_MAPPING1

uint8_t esphome::home_io_control::REG_DIO_MAPPING1 = 0x40
staticconstexpr

DIO0-DIO3 pin mapping.

Definition at line 65 of file radio_sx1276.h.

◆ REG_DIO_MAPPING2

uint8_t esphome::home_io_control::REG_DIO_MAPPING2 = 0x41
staticconstexpr

DIO4-DIO5 pin mapping.

Definition at line 66 of file radio_sx1276.h.

◆ REG_FDEV_LSB

uint8_t esphome::home_io_control::REG_FDEV_LSB = 0x05
staticconstexpr

Definition at line 40 of file radio_sx1276.h.

◆ REG_FDEV_MSB

uint8_t esphome::home_io_control::REG_FDEV_MSB = 0x04
staticconstexpr

Frequency deviation MSB.

Definition at line 39 of file radio_sx1276.h.

◆ REG_FIFO

uint8_t esphome::home_io_control::REG_FIFO = 0x00
staticconstexpr

FIFO read/write access.

Definition at line 35 of file radio_sx1276.h.

◆ REG_FIFO_THRESH

uint8_t esphome::home_io_control::REG_FIFO_THRESH = 0x35
staticconstexpr

FIFO threshold for TX start condition.

Definition at line 62 of file radio_sx1276.h.

◆ REG_FRF_LSB

uint8_t esphome::home_io_control::REG_FRF_LSB = 0x08
staticconstexpr

Definition at line 43 of file radio_sx1276.h.

◆ REG_FRF_MID

uint8_t esphome::home_io_control::REG_FRF_MID = 0x07
staticconstexpr

Definition at line 42 of file radio_sx1276.h.

◆ REG_FRF_MSB

uint8_t esphome::home_io_control::REG_FRF_MSB = 0x06
staticconstexpr

Carrier frequency MSB (freq = FRF * FXOSC / 2^19).

Definition at line 41 of file radio_sx1276.h.

◆ REG_IMAGE_CAL

uint8_t esphome::home_io_control::REG_IMAGE_CAL = 0x3B
staticconstexpr

Image calibration.

Definition at line 69 of file radio_sx1276.h.

◆ REG_IRQ_FLAGS1

uint8_t esphome::home_io_control::REG_IRQ_FLAGS1 = 0x3E
staticconstexpr

IRQ flags: mode ready, preamble detect, etc.

Definition at line 63 of file radio_sx1276.h.

◆ REG_IRQ_FLAGS2

uint8_t esphome::home_io_control::REG_IRQ_FLAGS2 = 0x3F
staticconstexpr

IRQ flags: FIFO full/empty, payload ready, CRC ok.

Definition at line 64 of file radio_sx1276.h.

◆ REG_LNA

uint8_t esphome::home_io_control::REG_LNA = 0x0C
staticconstexpr

Low noise amplifier gain and boost.

Definition at line 46 of file radio_sx1276.h.

◆ REG_OP_MODE

uint8_t esphome::home_io_control::REG_OP_MODE = 0x01
staticconstexpr

Operating mode (sleep/standby/tx/rx).

Definition at line 36 of file radio_sx1276.h.

◆ REG_OSC

uint8_t esphome::home_io_control::REG_OSC = 0x24
staticconstexpr

Oscillator / clock output.

Definition at line 53 of file radio_sx1276.h.

◆ REG_PA_CONFIG

uint8_t esphome::home_io_control::REG_PA_CONFIG = 0x09
staticconstexpr

Power amplifier config (pin select + power level).

Definition at line 44 of file radio_sx1276.h.

◆ REG_PA_RAMP

uint8_t esphome::home_io_control::REG_PA_RAMP = 0x0A
staticconstexpr

PA ramp time and modulation shaping.

Definition at line 45 of file radio_sx1276.h.

◆ REG_PACKET_CONFIG1

uint8_t esphome::home_io_control::REG_PACKET_CONFIG1 = 0x30
staticconstexpr

Packet format, CRC, encoding.

Definition at line 59 of file radio_sx1276.h.

◆ REG_PACKET_CONFIG2

uint8_t esphome::home_io_control::REG_PACKET_CONFIG2 = 0x31
staticconstexpr

Packet mode, IoHomeOn, PowerFrame.

Definition at line 60 of file radio_sx1276.h.

◆ REG_PAYLOAD_LENGTH

uint8_t esphome::home_io_control::REG_PAYLOAD_LENGTH = 0x32
staticconstexpr

Max payload length.

Definition at line 61 of file radio_sx1276.h.

◆ REG_PLLHOP

uint8_t esphome::home_io_control::REG_PLLHOP = 0x44
staticconstexpr

PLL hop: fast frequency change without standby.

Definition at line 68 of file radio_sx1276.h.

◆ REG_PREAMBLE_DETECT

uint8_t esphome::home_io_control::REG_PREAMBLE_DETECT = 0x1F
staticconstexpr

Preamble detector config.

Definition at line 52 of file radio_sx1276.h.

◆ REG_PREAMBLE_LSB

uint8_t esphome::home_io_control::REG_PREAMBLE_LSB = 0x26
staticconstexpr

Definition at line 56 of file radio_sx1276.h.

◆ REG_PREAMBLE_MSB

uint8_t esphome::home_io_control::REG_PREAMBLE_MSB = 0x25
staticconstexpr

TX preamble length MSB.

Definition at line 55 of file radio_sx1276.h.

◆ REG_RSSI_CONFIG

uint8_t esphome::home_io_control::REG_RSSI_CONFIG = 0x0E
staticconstexpr

RSSI smoothing.

Definition at line 48 of file radio_sx1276.h.

◆ REG_RSSI_VALUE

uint8_t esphome::home_io_control::REG_RSSI_VALUE = 0x11
staticconstexpr

Instant RSSI value in FSK mode.

Definition at line 54 of file radio_sx1276.h.

◆ REG_RX_BW

uint8_t esphome::home_io_control::REG_RX_BW = 0x12
staticconstexpr

Receiver bandwidth.

Definition at line 49 of file radio_sx1276.h.

◆ REG_RX_CONFIG

uint8_t esphome::home_io_control::REG_RX_CONFIG = 0x0D
staticconstexpr

Receiver configuration (AFC, AGC, trigger).

Definition at line 47 of file radio_sx1276.h.

◆ REG_SYNC_CONFIG

uint8_t esphome::home_io_control::REG_SYNC_CONFIG = 0x27
staticconstexpr

Sync word config (size, polarity, enable).

Definition at line 57 of file radio_sx1276.h.

◆ REG_SYNC_VALUE1

uint8_t esphome::home_io_control::REG_SYNC_VALUE1 = 0x28
staticconstexpr

Sync word byte 1 (registers 0x28-0x2F for bytes 1-8).

Definition at line 58 of file radio_sx1276.h.

◆ REG_VERSION

uint8_t esphome::home_io_control::REG_VERSION = 0x42
staticconstexpr

Chip version (should read 0x12 for SX1276).

Definition at line 67 of file radio_sx1276.h.

◆ REMOTE_ACTIVITY_STATUS_POLL_DELAY_MS

uint32_t esphome::home_io_control::REMOTE_ACTIVITY_STATUS_POLL_DELAY_MS = 2000
staticconstexpr

Delay before polling after overheard remote traffic: another controller's frame to a registered device, and, by default, a linked-remote press (its remote_activity_poll_delay_ms tuning default; ADR 0080).

Definition at line 280 of file proto_timing.h.

◆ RESPONSE_AUTH_WAIT_MS

int32_t esphome::home_io_control::RESPONSE_AUTH_WAIT_MS
staticconstexpr
Initial value:
=
static constexpr int32_t RESPONSE_WAIT_MS
Wait for response to non-start frame.

Wait for final response after challenge response.

Definition at line 141 of file proto_timing.h.

◆ RESPONSE_START_WAIT_MS

int32_t esphome::home_io_control::RESPONSE_START_WAIT_MS = 400
staticconstexpr

Wait for a response to a start frame — the first frame of an exchange, and the one a sleeping device has just been woken by.

This device class replies within a few milliseconds of the carrier dropping, or not at all — it is fast-or-never, not slow. A failure therefore shows up as saw_challenge=0 with no frame received at all, rather than as a late arrival, so a longer window cannot fix a device that genuinely fails to answer.

400 ms sits comfortably above every directly measured reply while keeping a failed exchange inside EXCHANGE_TOTAL_BUDGET_MS, so a dead device does not block the ESPHome loop past its own warning threshold (ADR 0013). Raise exchange_start_response_wait_ms from YAML if a device ever genuinely answers late — but check wait_ms in the logs first, since a fast-or-never device is a turnaround problem that a longer window cannot fix.

Definition at line 139 of file proto_timing.h.

◆ RESPONSE_WAIT_MS

int32_t esphome::home_io_control::RESPONSE_WAIT_MS = 500
staticconstexpr

Wait for response to non-start frame.

Definition at line 124 of file proto_timing.h.

◆ RESULT_ALIAS_INCOHERENCE

uint8_t esphome::home_io_control::RESULT_ALIAS_INCOHERENCE = 0x68
staticconstexpr

Code 13: alias incoherence.

Definition at line 497 of file proto_constants.h.

◆ RESULT_ALREADY_CONFIGURED

uint8_t esphome::home_io_control::RESULT_ALREADY_CONFIGURED = 0x88
staticconstexpr

Code 17: already configured.

Definition at line 502 of file proto_constants.h.

◆ RESULT_BAD_INDEX_RECEIVED

uint8_t esphome::home_io_control::RESULT_BAD_INDEX_RECEIVED = 0x1C
staticconstexpr

Invalid index received.

Definition at line 469 of file proto_constants.h.

◆ RESULT_BAD_MAC

uint8_t esphome::home_io_control::RESULT_BAD_MAC = 0x18
staticconstexpr

Code 3: challenge answer (MAC) did not verify.

Definition at line 480 of file proto_constants.h.

◆ RESULT_BATTERY_LEVEL

uint8_t esphome::home_io_control::RESULT_BATTERY_LEVEL = 0x12
staticconstexpr

Battery level is low.

Definition at line 460 of file proto_constants.h.

◆ RESULT_BLOCKED

uint8_t esphome::home_io_control::RESULT_BLOCKED = 0x04
staticconstexpr

Node blocked by an object.

Definition at line 448 of file proto_constants.h.

◆ RESULT_CALIBRATING

uint8_t esphome::home_io_control::RESULT_CALIBRATING = 0x0A
staticconstexpr

Node is calibrating.

Definition at line 453 of file proto_constants.h.

◆ RESULT_CODE_BUFFER_SIZE

size_t esphome::home_io_control::RESULT_CODE_BUFFER_SIZE = 5
inlineconstexpr

Size of the scratch buffer format_hex_byte() prints into: two digits plus the terminator, with headroom.

Definition at line 68 of file management_helpers.h.

◆ RESULT_COLOUR_NOT_REACHABLE

uint8_t esphome::home_io_control::RESULT_COLOUR_NOT_REACHABLE = 0x1A
staticconstexpr

Requested colour not reachable.

Definition at line 467 of file proto_constants.h.

◆ RESULT_COMMAND_COMPLETED_OK

uint8_t esphome::home_io_control::RESULT_COMMAND_COMPLETED_OK = 0x01
staticconstexpr

No errors detected.

Definition at line 445 of file proto_constants.h.

◆ RESULT_COMMAND_INCOMPATIBLE_TO_MOVEMENT

uint8_t esphome::home_io_control::RESULT_COMMAND_INCOMPATIBLE_TO_MOVEMENT = 0x15
staticconstexpr

Command cannot move the node that way.

Definition at line 463 of file proto_constants.h.

◆ RESULT_COMMAND_OVERRULED

uint8_t esphome::home_io_control::RESULT_COMMAND_OVERRULED = 0x1D
staticconstexpr

Command was overruled by a newer command.

Definition at line 470 of file proto_constants.h.

◆ RESULT_COMMAND_REJECTED

uint8_t esphome::home_io_control::RESULT_COMMAND_REJECTED = 0x80
staticconstexpr

Code 16: command rejected.

A Somfy Sunilus and a Somfy J406io answered a key transfer (0x32) with it.

Definition at line 500 of file proto_constants.h.

◆ RESULT_COMMAND_UNKNOWN

uint8_t esphome::home_io_control::RESULT_COMMAND_UNKNOWN = 0x08
staticconstexpr

Code 1: the device does not offer this command in its current state or class.

Definition at line 477 of file proto_constants.h.

◆ RESULT_DEAD_BOLT_ERROR

uint8_t esphome::home_io_control::RESULT_DEAD_BOLT_ERROR = 0x17
staticconstexpr

Dead bolt error.

Definition at line 465 of file proto_constants.h.

◆ RESULT_FILTER_MAINTENANCE_NEEDED

uint8_t esphome::home_io_control::RESULT_FILTER_MAINTENANCE_NEEDED = 0x11
staticconstexpr

Filter needs maintenance.

Definition at line 459 of file proto_constants.h.

◆ RESULT_INFORMATION_CODE

uint8_t esphome::home_io_control::RESULT_INFORMATION_CODE = 0xDF
staticconstexpr

Information-only code with unknown semantics.

Definition at line 503 of file proto_constants.h.

◆ RESULT_IP_NOT_SET

uint8_t esphome::home_io_control::RESULT_IP_NOT_SET = 0x23
staticconstexpr

Intermediate position is not set.

Definition at line 474 of file proto_constants.h.

◆ RESULT_LIMITATION_BY_AUTOMATIC_CYCLE

uint8_t esphome::home_io_control::RESULT_LIMITATION_BY_AUTOMATIC_CYCLE = 0xED
staticconstexpr

Parameter limited by an automatic cycle.

Definition at line 515 of file proto_constants.h.

◆ RESULT_LIMITATION_BY_EMERGENCY

uint8_t esphome::home_io_control::RESULT_LIMITATION_BY_EMERGENCY = 0xEE
staticconstexpr

Parameter limited by an emergency.

Definition at line 516 of file proto_constants.h.

◆ RESULT_LIMITATION_BY_LOCAL_USER

uint8_t esphome::home_io_control::RESULT_LIMITATION_BY_LOCAL_USER = 0xE1
staticconstexpr

Parameter limited by local button.

Definition at line 505 of file proto_constants.h.

◆ RESULT_LIMITATION_BY_MYSELF

uint8_t esphome::home_io_control::RESULT_LIMITATION_BY_MYSELF = 0xEC
staticconstexpr

Parameter limited by the node itself.

Definition at line 514 of file proto_constants.h.

◆ RESULT_LIMITATION_BY_RAIN

uint8_t esphome::home_io_control::RESULT_LIMITATION_BY_RAIN = 0xE3
staticconstexpr

Parameter limited by a rain sensor.

Definition at line 507 of file proto_constants.h.

◆ RESULT_LIMITATION_BY_SAAC

uint8_t esphome::home_io_control::RESULT_LIMITATION_BY_SAAC = 0xEA
staticconstexpr

Parameter limited by a standalone automatic controller.

Definition at line 512 of file proto_constants.h.

◆ RESULT_LIMITATION_BY_SCD

uint8_t esphome::home_io_control::RESULT_LIMITATION_BY_SCD = 0xE5
staticconstexpr

Parameter limited by a security actuator.

Definition at line 509 of file proto_constants.h.

◆ RESULT_LIMITATION_BY_TIMER

uint8_t esphome::home_io_control::RESULT_LIMITATION_BY_TIMER = 0xE4
staticconstexpr

Parameter limited by a timer.

Definition at line 508 of file proto_constants.h.

◆ RESULT_LIMITATION_BY_UNKNOWN_DEVICE

uint8_t esphome::home_io_control::RESULT_LIMITATION_BY_UNKNOWN_DEVICE = 0xE7
staticconstexpr

Parameter limited by an unknown device.

Definition at line 511 of file proto_constants.h.

◆ RESULT_LIMITATION_BY_UPS

uint8_t esphome::home_io_control::RESULT_LIMITATION_BY_UPS = 0xE6
staticconstexpr

Parameter limited by a power supply.

Definition at line 510 of file proto_constants.h.

◆ RESULT_LIMITATION_BY_USER

uint8_t esphome::home_io_control::RESULT_LIMITATION_BY_USER = 0xE2
staticconstexpr

Parameter limited by a remote control.

Definition at line 506 of file proto_constants.h.

◆ RESULT_LIMITATION_BY_WIND

uint8_t esphome::home_io_control::RESULT_LIMITATION_BY_WIND = 0xEB
staticconstexpr

Parameter limited by a wind sensor.

Definition at line 513 of file proto_constants.h.

◆ RESULT_LIMITS_NOT_SET

uint8_t esphome::home_io_control::RESULT_LIMITS_NOT_SET = 0x22
staticconstexpr

Device limits are not set.

Definition at line 473 of file proto_constants.h.

◆ RESULT_LOCK_POSITION_OPEN

uint8_t esphome::home_io_control::RESULT_LOCK_POSITION_OPEN = 0x0D
staticconstexpr

Lock command failed because the door is open.

Definition at line 456 of file proto_constants.h.

◆ RESULT_MANUALLY_OPERATED

uint8_t esphome::home_io_control::RESULT_MANUALLY_OPERATED = 0x03
staticconstexpr

Manually operated by a user.

Definition at line 447 of file proto_constants.h.

◆ RESULT_MODE_NOT_IMPLEMENTED

uint8_t esphome::home_io_control::RESULT_MODE_NOT_IMPLEMENTED = 0x14
staticconstexpr

Mode is not supported by the node.

Definition at line 462 of file proto_constants.h.

◆ RESULT_MODE_OR_STATE_UNKNOWN

uint8_t esphome::home_io_control::RESULT_MODE_OR_STATE_UNKNOWN = 0x10
staticconstexpr

Code 2: mode or state number unknown.

Definition at line 479 of file proto_constants.h.

◆ RESULT_MOTION_TIME_TOO_LONG

uint8_t esphome::home_io_control::RESULT_MOTION_TIME_TOO_LONG = 0x0E
staticconstexpr

Target was not reached in time.

Definition at line 457 of file proto_constants.h.

◆ RESULT_NO_CONTACT

uint8_t esphome::home_io_control::RESULT_NO_CONTACT = 0x02
staticconstexpr

No communication to node.

Definition at line 446 of file proto_constants.h.

◆ RESULT_NO_EXECUTION

uint8_t esphome::home_io_control::RESULT_NO_EXECUTION = 0x09
staticconstexpr

Node did not move.

Definition at line 452 of file proto_constants.h.

◆ RESULT_NODE_WAITING_FOR_POWER

uint8_t esphome::home_io_control::RESULT_NODE_WAITING_FOR_POWER = 0x1E
staticconstexpr

Node is waiting for power.

Definition at line 471 of file proto_constants.h.

◆ RESULT_NOT_IMPLEMENTED

uint8_t esphome::home_io_control::RESULT_NOT_IMPLEMENTED = 0x78
staticconstexpr

Code 15: not implemented.

Definition at line 499 of file proto_constants.h.

◆ RESULT_ORIGINATOR_DISABLED

uint8_t esphome::home_io_control::RESULT_ORIGINATOR_DISABLED = 0x30
staticconstexpr

Code 6: command originator disabled.

Definition at line 485 of file proto_constants.h.

◆ RESULT_OUT_OF_RANGE

uint8_t esphome::home_io_control::RESULT_OUT_OF_RANGE = 0x24
staticconstexpr

Requested value is out of range.

Definition at line 475 of file proto_constants.h.

◆ RESULT_PARAMETER_INCOHERENCE_ADJUSTMENT

uint8_t esphome::home_io_control::RESULT_PARAMETER_INCOHERENCE_ADJUSTMENT
staticconstexpr
Initial value:
=
0x50

Code 10: parameter incoherence adjustment.

Definition at line 490 of file proto_constants.h.

◆ RESULT_PARAMETER_LIMITATION_ADJUSTMENT

uint8_t esphome::home_io_control::RESULT_PARAMETER_LIMITATION_ADJUSTMENT = 0x48
staticconstexpr

Code 9: parameter limitation adjustment.

Definition at line 489 of file proto_constants.h.

◆ RESULT_PARAMETER_LIMITED

uint8_t esphome::home_io_control::RESULT_PARAMETER_LIMITED = 0xE0
staticconstexpr

Parameter limited by an unknown device.

Definition at line 504 of file proto_constants.h.

◆ RESULT_PARAMETER_UNKNOWN

uint8_t esphome::home_io_control::RESULT_PARAMETER_UNKNOWN = 0x28
staticconstexpr

Code 5: parameter, alias, access method or scenario unknown.

The Somfy Izymo answers our SetConfig1 (0x6F) with it during pairing.

Definition at line 482 of file proto_constants.h.

◆ RESULT_POWER_CONSUMPTION_TOO_HIGH

uint8_t esphome::home_io_control::RESULT_POWER_CONSUMPTION_TOO_HIGH = 0x0B
staticconstexpr

Node power consumption is too high.

Definition at line 454 of file proto_constants.h.

◆ RESULT_POWER_CONSUMPTION_TOO_LOW

uint8_t esphome::home_io_control::RESULT_POWER_CONSUMPTION_TOO_LOW = 0x0C
staticconstexpr

Node power consumption is too low.

Definition at line 455 of file proto_constants.h.

◆ RESULT_PRIORITY_LEVEL_LOCKED

uint8_t esphome::home_io_control::RESULT_PRIORITY_LEVEL_LOCKED = 0x06
staticconstexpr

Node is locked on this priority level.

Definition at line 450 of file proto_constants.h.

◆ RESULT_PRIORITY_LOCKED

uint8_t esphome::home_io_control::RESULT_PRIORITY_LOCKED = 0x38
staticconstexpr

Code 7: priority locked; the command's priority level is too low to run.

Definition at line 486 of file proto_constants.h.

◆ RESULT_PRIORITY_SERVICE_NOT_SUPPORTED

uint8_t esphome::home_io_control::RESULT_PRIORITY_SERVICE_NOT_SUPPORTED = 0x40
staticconstexpr

Code 8: priority service not supported.

Definition at line 488 of file proto_constants.h.

◆ RESULT_REACHED_WRONG_POSITION

uint8_t esphome::home_io_control::RESULT_REACHED_WRONG_POSITION = 0x07
staticconstexpr

Node stopped in another position than expected.

Definition at line 451 of file proto_constants.h.

◆ RESULT_SERVICE_FULL

uint8_t esphome::home_io_control::RESULT_SERVICE_FULL = 0x60
staticconstexpr

Code 12: service full.

Definition at line 496 of file proto_constants.h.

◆ RESULT_SESSION_INTERRUPTED

uint8_t esphome::home_io_control::RESULT_SESSION_INTERRUPTED = 0x70
staticconstexpr

Code 14: session interrupted.

Definition at line 498 of file proto_constants.h.

◆ RESULT_SETUP_REQUIRED

uint8_t esphome::home_io_control::RESULT_SETUP_REQUIRED = 0x20
staticconstexpr

Code 4: the device needs setting up first.

Definition at line 481 of file proto_constants.h.

◆ RESULT_TARGET_MODIFIED

uint8_t esphome::home_io_control::RESULT_TARGET_MODIFIED = 0x13
staticconstexpr

Node modified the requested target value.

Definition at line 461 of file proto_constants.h.

◆ RESULT_TARGET_NOT_REACHABLE

uint8_t esphome::home_io_control::RESULT_TARGET_NOT_REACHABLE = 0x1B
staticconstexpr

Requested target not reachable.

Definition at line 468 of file proto_constants.h.

◆ RESULT_THERMAL_PROTECTION

uint8_t esphome::home_io_control::RESULT_THERMAL_PROTECTION = 0x0F
staticconstexpr

Node entered thermal protection mode.

Definition at line 458 of file proto_constants.h.

◆ RESULT_TOTAL_INCOHERENCE

uint8_t esphome::home_io_control::RESULT_TOTAL_INCOHERENCE = 0x58
staticconstexpr

Code 11: the request does not fit what the device implements.

Seen cross-vendor (Somfy Sunea awning + dimmer, Velux window) when a diagnostic probe walks past the last supported function index or block.

Definition at line 492 of file proto_constants.h.

◆ RESULT_UNKNOWN_STATUS_REPLY

uint8_t esphome::home_io_control::RESULT_UNKNOWN_STATUS_REPLY = 0x00
staticconstexpr

Device returned an unknown status reply.

Definition at line 444 of file proto_constants.h.

◆ RESULT_USER_ACTION

uint8_t esphome::home_io_control::RESULT_USER_ACTION = 0x16
staticconstexpr

User action overrode the command.

Definition at line 464 of file proto_constants.h.

◆ RESULT_WRONG_LOAD_CONNECTED

uint8_t esphome::home_io_control::RESULT_WRONG_LOAD_CONNECTED = 0x19
staticconstexpr

Wrong load connected to node.

Definition at line 466 of file proto_constants.h.

◆ RESULT_WRONG_POSITION

uint8_t esphome::home_io_control::RESULT_WRONG_POSITION = 0x21
staticconstexpr

Node reports wrong position.

Definition at line 472 of file proto_constants.h.

◆ RESULT_WRONG_SYSTEMKEY

uint8_t esphome::home_io_control::RESULT_WRONG_SYSTEMKEY = 0x05
staticconstexpr

Node contains the wrong system key.

Definition at line 449 of file proto_constants.h.

◆ ROLL_CALL_PASSES

RollCallPass esphome::home_io_control::ROLL_CALL_PASSES[]
inlineconstexpr
Initial value:
= {
{POWER_SAVE_LOW_POWER, true, ListenPolicy::ROTATE_ALL_CHANNELS},
{POWER_SAVE_ALWAYS_ALIVE, false, ListenPolicy::ROTATE_SKIPPING_REQUEST},
}
@ ROTATE_SKIPPING_REQUEST
The two channels that are not the request channel.

The roll-call's two fixed frame shapes, low-power first.

Low-power pass first: a sleeper that answers late is still collected by the later always-alive windows (collect_broadcast_responses() accepts any 0x2B addressed to us, regardless of which pass is currently running); in the opposite order a late low-power reply would fall off the end of the scan with nothing left to catch it.

Low-power pass, CTRL1 = 0x30 (LOW_POWER | ACK): the exact header a real VELUX KLR300 uses for its own roll-call, whose low-power shutters answered it. Setting CTRL1_LOW_POWER selects LONG_PREAMBLE through the shared start-preamble rule (request_preamble_for()) — no preamble is picked here. ROTATE_ALL_CHANNELS: those VELUX low-power replies were observed landing on the request channel, which a skipping listen would miss entirely.

Always-alive pass, CTRL1 = 0x00 at normal_start_preamble: an always-alive VELUX installation ignores a 1024-byte start preamble and answers the 32-byte one; Somfy roll-call replies land on the request channel about 1 in 149 times, so ROTATE_SKIPPING_REQUEST keeps the window on the channels replies actually use.

Both shapes are fixed, named constants: pairing_discovery_ack_capable belongs to Discover & Pair and does not influence the roll-call either way.

Definition at line 105 of file roll_call.h.

◆ RSSI_EMA_SCALE

int16_t esphome::home_io_control::RSSI_EMA_SCALE = 8
staticconstexpr

EMA weight denominator and fixed-point scale for IoDevice::rssi_ema_scaled.

One constant serves both roles by construction: the update S += x − round(S/N) blends each new sample x in at weight 1/N while keeping S = N × EMA.

Definition at line 413 of file proto_device_model.h.

◆ RSSI_UNKNOWN_DBM

int16_t esphome::home_io_control::RSSI_UNKNOWN_DBM = INT16_MIN
staticconstexpr

Sentinel value meaning "no RSSI sample recorded yet" for last_rssi_dbm/rssi_ema_scaled.

A real RSSI reading from these radios is always well above INT16_MIN — and so is any real rssi_ema_scaled fixed-point value.

Definition at line 408 of file proto_device_model.h.

◆ RX_CONFIG_RESTART_RX_WITH_PLL_LOCK

uint8_t esphome::home_io_control::RX_CONFIG_RESTART_RX_WITH_PLL_LOCK = 0x20
staticconstexpr

REG_RX_CONFIG bit 5, RestartRxWithPllLock: restarts the receiver chain after a frequency change, which also clears PreambleDetect and re-arms AGC/AFC for the new channel.

Definition at line 82 of file radio_sx1276.h.

◆ RX_CONFIG_VALUE

uint8_t esphome::home_io_control::RX_CONFIG_VALUE = 0x9E
staticconstexpr

REG_RX_CONFIG as configured: RestartRxOnCollision | AfcAutoOn | AgcAutoOn | RxTrigger=PreambleDetect.

Definition at line 79 of file radio_sx1276.h.

◆ RX_HOP_HOLDOFF_US

uint32_t esphome::home_io_control::RX_HOP_HOLDOFF_US = 12000
inlineconstexpr

Longest a frame arriving on the current channel may hold off an idle-path channel hop, in microseconds.

Sized to outlast the slowest thing a hop could destroy. On the software-PHY chips that is the fixed-length RX_DONE, which lands 48 raw bytes = 10.0 ms after the sync word (SOFT_PHY_RX_PROBE_PACKET_LEN at 38400 bps; a static_assert in radio_soft_phy_driver_base.h ties this constant to that arithmetic, since neither header can see the other's constants). On the SX1276 it is the frame's own air time, at most ~9.4 ms for the longest possible frame. 12 ms covers both with margin for poll granularity.

It is a bound, not a target: every mechanism that sets the holdoff is expected to clear it early, and the bound exists only so that a sync detection with no frame behind it — noise, a truncated burst, a peer that gave up — cannot wedge channel hopping permanently.

Definition at line 40 of file radio_interface.h.

◆ SCAN_CHANNEL_COUNT

uint8_t esphome::home_io_control::SCAN_CHANNEL_COUNT = sizeof(SCAN_CHANNELS) / sizeof(SCAN_CHANNELS[0])
inlineconstexpr

Definition at line 71 of file roll_call.h.

◆ SCAN_CHANNELS

uint32_t esphome::home_io_control::SCAN_CHANNELS[] = {FREQ_CH2, FREQ_CH1, FREQ_CH3}
inlineconstexpr

Channels scan_paired_devices() transmits its roll-call request on, one attempt per channel, per pass (see ROLL_CALL_PASSES).

A single broadcast on one fixed channel only reaches a paired device that happens to be awake and listening on that exact channel at that exact instant — real hardware testing found paired devices duty-cycle across all three channels independently of the hub, so a one-shot broadcast on CH2 alone misses whichever devices are elsewhere in their cycle right then. Retrying the same request on the other two channels gives every device up to three chances per pass to be listening when the hub transmits. CH2 first since it is the protocol's designated TX channel (see FREQ_CH2's doc comment) and therefore the most likely to catch a reply on the first attempt.

This TX retry is a duty-cycle workaround, not a receive-side fix. The hub's listen path separately extends its dwell on a detected preamble/sync rather than hopping mid-frame, which is what keeps it from dropping replies it does hear.

Definition at line 70 of file roll_call.h.

◆ SCAN_MAX_REPLIES

uint8_t esphome::home_io_control::SCAN_MAX_REPLIES = 24
inlineconstexpr

Maximum distinct responders reported by one scan_paired_devices() call.

Sized against the loop task's stack, which is where this runs: ESPHome spawns its own loopTask with ESPHOME_LOOP_TASK_STACK_SIZE (8192 B) — not the 3.5 KB ESP-IDF main task. ScanResponder is 12 B, so 24 slots cost ≈288 B (~3.5 % of that stack) in a single array; replies are decoded on arrival rather than buffered as whole 33 B IoFrames, which is what makes a limit this size cheap. Installs with 20+ actuators are real, so 8 (the original value, chosen when whole frames were retained) was too low to be useful. Overflow beyond this is reported in the scan's own output, never silent.

Definition at line 34 of file roll_call.h.

◆ SCHEDULED_POLL_MAX_TRIES

uint8_t esphome::home_io_control::SCHEDULED_POLL_MAX_TRIES = 1
staticconstexpr

Exchange tries for a status poll the scheduler owns that only looks at a device that is probably still busy: every poll of a tracked device that is not a settling read (decisions::scheduled_poll_read_kind()).

Such a poll's failure is re-armed by the backoff ladder (STATUS_RETRY_AFTER_FAIL_MS and its successors), so the ladder is its retry mechanism; stacking blocking in-exchange tries on top of it buys no freshness and costs blocked loop() time while a device is unresponsive (e.g. an actuator mid-manoeuvre). The progress poll fires seconds after a command, squarely inside the manoeuvre, so keeping it a single try is what lets a STOP a user presses mid-move dispatch promptly, and it spares the air a burst of requests the device cannot answer yet. This is a deliberate airtime choice, not a default. A heater's poll takes this count outside the ladder band too (decisions::scheduled_poll_max_tries()).

Definition at line 249 of file proto_timing.h.

◆ SCHEDULED_POLL_RETRY_GRACE_FIRST_FAILURE

uint8_t esphome::home_io_control::SCHEDULED_POLL_RETRY_GRACE_FIRST_FAILURE = 1
staticconstexpr

Ladder positions at which a scheduler-owned status poll is a settling read again.

The single try above is right at both ends of the backoff ladder and wrong in the middle. At the first slot after a command the device is still executing the manoeuvre: its silence is expected, retries cannot change that, and blocking loop() would delay a STOP the user presses mid-move. Once a device has missed several slots in a row it is unreachable rather than merely asleep, and retries are just as pointless. In between sits the slot where the manoeuvre has just ended and the device is awake again but duty-cycled: one listen samples its receive window once, while a settling read (decisions::attempt_plan() with decisions::ReadKind::SETTLING) samples it as often as the exchange budget allows, and a success there also clears the failure streak and ends the backoff.

Counted in consecutive silent failures already recorded when the poll is dispatched, so 0 is the post-command settle poll and 1..3 are the ~5 s / ~15 s / ~30 s ladder slots after it, roughly t+8 s to t+53 s, spanning every cover travel time this project has measured. For a device believed travelling the ladder, and so this band, starts only after its first MOVING_POLL_MISSES_SKIPPED misses, which keep the normal cadence. An auth-shaped streak is excluded entirely: a device that answers with a 0x3C challenge is awake, so extra attempts buy no wake-up, and an auth try is the most expensive shape the engine runs.

The poll after an accepted STOP and the poll confirming a stop off target are settling reads whatever the counters say: nothing is moving then, and the user's next action waits on exactly that reading.

Definition at line 274 of file proto_timing.h.

◆ SCHEDULED_POLL_RETRY_GRACE_LAST_FAILURE

uint8_t esphome::home_io_control::SCHEDULED_POLL_RETRY_GRACE_LAST_FAILURE = 3
staticconstexpr

Definition at line 275 of file proto_timing.h.

◆ SELECT_PARAMS

TuningSelectParam esphome::home_io_control::SELECT_PARAMS[]
staticconstexpr

Definition at line 130 of file tuning_registry.cpp.

◆ SESSION_HOLD_BASE_MS

uint32_t esphome::home_io_control::SESSION_HOLD_BASE_MS = 35
staticconstexpr

Base of how long a node acting as a device holds the session's channel after answering a controller, before it scans all channels again.

The full hold adds the turnaround time the node advertises in its 0x29 (att_class_ms()), so a controller that waits out that turnaround still finds the node listening. 35 ms is our margin, not a measured device timer: with the default advertised turnaround the hold is 75 ms, above the 16-52 ms in which third-party controllers answered a device's challenge in our corpus (somfy_oximo40_statuspoll_sx1262, somfy_rs100_statuspoll_kig300_sx1276).

Definition at line 114 of file proto_timing.h.

◆ SHORT_PREAMBLE

uint16_t esphome::home_io_control::SHORT_PREAMBLE = 8
staticconstexpr

Protocol default and floor for continuation frames.

Definition at line 44 of file proto_timing.h.

◆ SOFT_PHY_ALL_IRQ_BITS

uint32_t esphome::home_io_control::SOFT_PHY_ALL_IRQ_BITS = 0xFFFFFFFF
staticconstexpr

Sentinel meaning "every IRQ bit counts as activity" — the default for SoftPhyDriverBase::activity_irq_mask.

Neither concrete driver uses it any more: both SX1262 and LR1121 unmask PreambleDetected at the hardware level and override this to exclude it (see SX1262_IRQ_ACTIVITY_MASK / LR1121_IRQ_ACTIVITY_MASK). Kept as the base-class default for a hypothetical future driver that never unmasks PreambleDetected in the first place, where "any bit" is genuinely safe again.

Definition at line 50 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_CCA_SETTLE_US

uint32_t esphome::home_io_control::SOFT_PHY_CCA_SETTLE_US = 1000
staticconstexpr

Wait after retuning to the TX channel before the clear-channel RSSI reading.

A retune on these chips is standby → SetRfFrequency → SetRx (the TCXO stays on in XOSC standby), after which the PLL has to relock and the instantaneous RSSI has to average over the new channel; 1 ms covers that with margin and costs nothing next to a preamble.

Todo
Confirm on the bench (Heltec V3, T3-S3 LR1121): time the retune and compare the reading against one taken after a long dwell on the same channel.

Definition at line 89 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_EARLY_HEADER_RAW_BYTES

uint8_t esphome::home_io_control::SOFT_PHY_EARLY_HEADER_RAW_BYTES = 3
staticconstexpr

Raw bytes read in the first stage of a length-driven receive — enough to hold CTRL0's UART cell (10 bits) at any of the probe's bit alignments (up to 9 bits of slack).

Definition at line 54 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_EARLY_MIN_WINDOW_MS

uint32_t esphome::home_io_control::SOFT_PHY_EARLY_MIN_WINDOW_MS = 12
staticconstexpr

Smallest receive window a length-driven receive will be attempted in, in milliseconds.

The longest possible frame (FRAME_MAX_SIZE + CRC, UART-packed) occupies ~9.4 ms of air time, so a caller with less than this left cannot finish one either way. Declining up front keeps the early path from spending a short window's whole budget on a receive it cannot complete.

Definition at line 71 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_EARLY_POLL_US

uint32_t esphome::home_io_control::SOFT_PHY_EARLY_POLL_US = 100
staticconstexpr

Poll interval while waiting out a frame's remaining air time, in microseconds.

Definition at line 64 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_EARLY_READ_MARGIN_BYTES

uint8_t esphome::home_io_control::SOFT_PHY_EARLY_READ_MARGIN_BYTES = 2
staticconstexpr

Air-time margin added before every mid-reception buffer read, in raw bytes.

Covers the lag between a byte finishing on air and the chip having it in its data buffer, plus the granularity of the polled sync-word observation. Two byte-times is generous at this line rate and still leaves a length-driven receive far ahead of the fixed-length RX_DONE.

Definition at line 61 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_IDLE_RX_COMPLETION_BUDGET_MS

uint32_t esphome::home_io_control::SOFT_PHY_IDLE_RX_COMPLETION_BUDGET_MS = 20
staticconstexpr

Blocking budget SoftPhyDriverBase::check_for_packet gives a length-driven receive, in milliseconds — the timeout_ms passed to try_early_completion_() from the non-blocking idle-loop RX path (issue #81).

The real bound on how long that call can block is the frame's own air time (~9.4 ms worst case, see SOFT_PHY_EARLY_MIN_WINDOW_MS), not this number; this value only has to be large enough not to cut a genuine frame short. It is not itself a tight bound on loop() latency — the actual air time is what stays well inside a loop() tick (~16-30 ms) — and either way it is dwarfed by the 1-3 s a blocking exchange already costs loop().

Definition at line 81 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_LINE_RATE_BPS

uint32_t esphome::home_io_control::SOFT_PHY_LINE_RATE_BPS = 38400
staticconstexpr

Protocol line rate. The same 38400 bps every driver programs into its own bitrate register.

Definition at line 71 of file radio_soft_phy.h.

◆ SOFT_PHY_MAX_WIRE_FRAME_BYTES

uint16_t esphome::home_io_control::SOFT_PHY_MAX_WIRE_FRAME_BYTES
staticconstexpr
Initial value:
=
FRAME_MIN_SIZE + AES_KEY_SIZE + 1 + 1 + 2 + HMAC_SIZE

Largest real IO-Homecontrol frame the fixed-length RX probe has to recover intact: a 1W CMD 0x30 "add controller".

Its 9-byte header + 20-byte payload (wrapped key[16] + manufacturer[1] + data[1] + sequence[2]) = 29 declared bytes, and it carries a 6-byte authenticator as an out-of-length trailer after the declared length but still under the CRC — so the probe must hold 29 + 6 = 35 wire bytes. This is larger than any frame CTRL0's length field alone describes, which is why it, not FRAME_MAX_DECLARED_SIZE, is the binding case.

Definition at line 134 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_MAX_WIRE_FRAME_RAW_BYTES

uint16_t esphome::home_io_control::SOFT_PHY_MAX_WIRE_FRAME_RAW_BYTES
staticconstexpr
Initial value:
=
((SOFT_PHY_MAX_WIRE_FRAME_BYTES + FRAME_CRC_SIZE) * UART_CELL_BITS + (UART_PROBE_MAX_BIT_OFFSET - 1) +
BITS_PER_BYTE - 1) /
BITS_PER_BYTE
static constexpr uint16_t SOFT_PHY_MAX_WIRE_FRAME_BYTES
Largest real IO-Homecontrol frame the fixed-length RX probe has to recover intact: a 1W CMD 0x30 "add...

Raw on-air bytes that frame occupies once its 2-byte CRC is appended and the whole thing is UART-packed into 10-bit cells (start + 8 data + stop), rounded up to whole bytes — the same arithmetic as soft_phy_raw_bytes_for_frame(), spelled out here because that helper is not constexpr.

Plus UART_PROBE_MAX_BIT_OFFSET - 1 bits: the probe locates the frame start by sweeping leading alignments, so a recovered frame can begin that many bits into the raw buffer. 35 + 2 = 37 cells = 370 bits, + 9 slack bits = 379 → 48 raw bytes, exactly the probe length.

Definition at line 143 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_POLL_MARGIN_MS

uint32_t esphome::home_io_control::SOFT_PHY_POLL_MARGIN_MS = 2
staticconstexpr

Polling granularity added on top of an air time: the receive loops poll once per millisecond.

Definition at line 105 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_POST_SYNC_GRACE_MS

uint32_t esphome::home_io_control::SOFT_PHY_POST_SYNC_GRACE_MS
staticconstexpr
Initial value:
=
static constexpr uint8_t SOFT_PHY_RX_PROBE_PACKET_LEN
Fixed raw-RX probe length.
constexpr uint32_t soft_phy_air_time_us(uint32_t raw_bytes)
On-air time in microseconds for raw_bytes bytes at the protocol's line rate.
static constexpr uint32_t SOFT_PHY_POLL_MARGIN_MS
Polling granularity added on top of an air time: the receive loops poll once per millisecond.
static constexpr uint32_t SOFT_PHY_US_PER_MS
Microseconds per millisecond, for the air-time arithmetic below.

Extra time a reception gets once its sync word is seen, past the caller's deadline, in milliseconds: the fixed-length probe's own air time after sync (48 raw bytes, 10 ms) plus the polling margin.

Without it a frame whose sync lands near the end of a dwell or linger is abandoned mid-reception and lost.

Definition at line 111 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_PREAMBLE_HOLD_MS

uint32_t esphome::home_io_control::SOFT_PHY_PREAMBLE_HOLD_MS = 30
staticconstexpr

Longest a detected preamble keeps the receiver running across dwell timeouts, in milliseconds.

A dwell that times out while PreambleDetected is latched leaves RX and the latched bit alone, so the caller's linger guard keeps the channel (see SoftPhyDriverBase::preamble_hold_since_ms_). Restarting RX there throws the preamble away and, measured on the bench, lost frames whose sync arrived within a millisecond of the restart. The cap covers a preamble detected at the start of a dwell (one dwell, ~9 ms) plus the longest preamble-to-sync measured on air from a discovery answer (21 ms, the key-extraction responder's 80-byte answer; real devices ~14 ms), and bounds what a false detection on noise can cost.

Definition at line 100 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_RX_PROBE_PACKET_LEN

uint8_t esphome::home_io_control::SOFT_PHY_RX_PROBE_PACKET_LEN = 48
staticconstexpr

Fixed raw-RX probe length.

Typical traffic is 23-25 byte protocol frames (34 raw bytes packed with CRC); the binding case is the largest frame the probe must still recover intact — a 1W CMD 0x30 add-controller with its out-of-length MAC trailer, which packs to 47 raw bytes and needs up to 9 more bits of headroom for the probe's leading bit-offset sweep. 48 bytes covers that (see the SOFT_PHY_MAX_WIRE_FRAME_RAW_BYTES static_assert below) without relying on either chip's variable-length engine. This is a protocol-frame-size property, not a chip quirk, so both drivers share one value — used here for the raw-probe threshold in SoftPhyDriverBase::read_rx_packet and by each driver's own set_rx_packet_params() for the configured RX payload length.

Definition at line 42 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_START_PREAMBLE

uint16_t esphome::home_io_control::SOFT_PHY_START_PREAMBLE = 48
staticconstexpr

Default directed start preamble for drivers built on the shared software PHY (SX1262, LR1121).

48 bytes rather than the protocol's documented 32. Measured: on a Somfy Izymo dimmer an SX1262 answers 56/71 = 78.9% of first tries at 32 bytes and 60/60 at 48, 64, 128 and 256 — a step, not a ramp — while an SX1276 sending the same programmed 32 bytes answers 60/60. The deficit tracks the shared PHY rather than the chip, the link or the device, so the compensation belongs with the PHY. The cause of the shortfall is not identified; see ADR 0042.

Definition at line 256 of file tuning_config.h.

◆ SOFT_PHY_US_PER_MS

uint32_t esphome::home_io_control::SOFT_PHY_US_PER_MS = 1000
staticconstexpr

Microseconds per millisecond, for the air-time arithmetic below.

Definition at line 103 of file radio_soft_phy_driver_base.h.

◆ SOFT_PHY_US_PER_SECOND

uint32_t esphome::home_io_control::SOFT_PHY_US_PER_SECOND = 1000000
staticconstexpr

Microseconds in a second, for the air-time arithmetic below.

Definition at line 73 of file radio_soft_phy.h.

◆ STATUS_AUTH_RETRY_AFTER_FAIL_MAX_MS

uint32_t esphome::home_io_control::STATUS_AUTH_RETRY_AFTER_FAIL_MAX_MS = 300000
staticconstexpr

Steady-state after repeated auth failures.

Like STATUS_RETRY_AFTER_FAIL_MAX_MS, it ends a follow-up window rather than wait inside it.

Definition at line 49 of file status_poll_policy.h.

◆ STATUS_AUTH_RETRY_AFTER_FAIL_MS

uint32_t esphome::home_io_control::STATUS_AUTH_RETRY_AFTER_FAIL_MS
staticconstexpr
Initial value:
=
static constexpr uint32_t STATUS_RETRY_AFTER_FAIL_MS
First retry after a silent failure.

First retry after a challenge-seen failure.

Definition at line 43 of file status_poll_policy.h.

◆ STATUS_AUTH_RETRY_AFTER_FAIL_STEP2_MS

uint32_t esphome::home_io_control::STATUS_AUTH_RETRY_AFTER_FAIL_STEP2_MS = 30000
staticconstexpr

Second retry.

Definition at line 45 of file status_poll_policy.h.

◆ STATUS_AUTH_RETRY_AFTER_FAIL_STEP3_MS

uint32_t esphome::home_io_control::STATUS_AUTH_RETRY_AFTER_FAIL_STEP3_MS = 120000
staticconstexpr

Third retry.

Definition at line 46 of file status_poll_policy.h.

◆ STATUS_BATTERY_MASK

uint8_t esphome::home_io_control::STATUS_BATTERY_MASK = 0x60
staticconstexpr

Byte 1 bits 5-6: the device's coarse battery level (BatteryState).

Every solar Somfy actuator captured reports 3 here and every mains one 0 (tests/corpus/captures/statuspoll/ somfy_rs100_statuspoll_kig300_sx1276.yaml, somfy_oximo40_statuspoll_sx1262.yaml, somfy_awning_statuspoll_success_sx1262.yaml).

Definition at line 599 of file proto_constants.h.

◆ STATUS_BATTERY_SHIFT

uint8_t esphome::home_io_control::STATUS_BATTERY_SHIFT = 5
staticconstexpr

Right shift that brings STATUS_BATTERY_MASK down to 0-3.

Definition at line 600 of file proto_constants.h.

◆ STATUS_EXPECTED

uint8_t esphome::home_io_control::STATUS_EXPECTED = 0x80
staticconstexpr

Byte 1 bit 7: device will send auto status update.

Definition at line 594 of file proto_constants.h.

◆ STATUS_MP_FP_FPI1_MASK

uint8_t esphome::home_io_control::STATUS_MP_FP_FPI1_MASK = 0xFE
staticconstexpr

FPI1: FP1..FP7 requested (bit 7 = FP1 ... bit 1 = FP7).

Definition at line 621 of file proto_constants.h.

◆ STATUS_MP_FP_FPI2

uint8_t esphome::home_io_control::STATUS_MP_FP_FPI2
staticconstexpr
Initial value:
=
0x00

FPI2: no FP9-FP16 requested (bit 7 = FP9 ... bit 0 = FP16 otherwise).

Definition at line 625 of file proto_constants.h.

◆ STATUS_MP_FP_PARAM_COUNT

uint8_t esphome::home_io_control::STATUS_MP_FP_PARAM_COUNT
staticconstexpr
Initial value:
=
7

Largest selection seen on air (FP1-FP7), kept as a conservative cap across both bitmaps.

Definition at line 623 of file proto_constants.h.

◆ STATUS_MP_FP_PARAM_TYPE

uint8_t esphome::home_io_control::STATUS_MP_FP_PARAM_TYPE = 0x01
staticconstexpr

Status type per requested parameter: current value.

Definition at line 622 of file proto_constants.h.

◆ STATUS_POS_MAX

uint16_t esphome::home_io_control::STATUS_POS_MAX = 0xC800
staticconstexpr

In status responses, position is encoded as a 16-bit value where 0x0000 = fully open (0%) and 0xC800 = fully closed (100%).

Definition at line 382 of file proto_device_model.h.

◆ STATUS_POS_TOLERANCE_RAW

uint16_t esphome::home_io_control::STATUS_POS_TOLERANCE_RAW = 100
staticconstexpr

Target-reached tolerance expressed in raw IO-homecontrol position units.

100 raw units out of 51200 full-scale is about 0.195%, so this only absorbs tiny target/current mismatches from device rounding or early stopped flags.

Definition at line 386 of file proto_device_model.h.

◆ STATUS_RETRY_AFTER_FAIL_MAX_MS

uint32_t esphome::home_io_control::STATUS_RETRY_AFTER_FAIL_MAX_MS = 300000
staticconstexpr

Steady-state backoff after many silent failures.

Longer than what is left of a follow-up window (MAX_TRACKED_STATUS_POLL_WINDOW_MS) by the time it is reached, so inside a window this step ends the window (StatusPollPolicy::on_exchange_failed()).

Definition at line 32 of file status_poll_policy.h.

◆ STATUS_RETRY_AFTER_FAIL_MS

uint32_t esphome::home_io_control::STATUS_RETRY_AFTER_FAIL_MS = 5000
staticconstexpr

First retry after a silent failure.

Definition at line 25 of file status_poll_policy.h.

◆ STATUS_RETRY_AFTER_FAIL_STEP2_MS

uint32_t esphome::home_io_control::STATUS_RETRY_AFTER_FAIL_STEP2_MS = 15000
staticconstexpr

Second retry after a silent failure.

Definition at line 26 of file status_poll_policy.h.

◆ STATUS_RETRY_AFTER_FAIL_STEP3_MS

uint32_t esphome::home_io_control::STATUS_RETRY_AFTER_FAIL_STEP3_MS = 30000
staticconstexpr

Third retry after a silent failure.

Definition at line 27 of file status_poll_policy.h.

◆ STATUS_RETRY_AFTER_FAIL_STEP4_MS

uint32_t esphome::home_io_control::STATUS_RETRY_AFTER_FAIL_STEP4_MS = 60000
staticconstexpr

Fourth retry after a silent failure.

Definition at line 28 of file status_poll_policy.h.

◆ STATUS_STOPPED

uint8_t esphome::home_io_control::STATUS_STOPPED = 0x01
staticconstexpr

Status byte flags in CMD_PRIVATE_RESP and CMD_STATUS_UPDATE.

Byte 0 bit 0: device is not moving

Definition at line 593 of file proto_constants.h.

◆ STATUS_TILT_SELECTOR

uint8_t esphome::home_io_control::STATUS_TILT_SELECTOR
staticconstexpr
Initial value:
=
0x20

Extended status payload marker for tilt-capable devices: FPI1 with bit 5 set = functional parameter FP3 (FPI1 bit 7 = FP1 ... bit 0 = FP8).

The actuator echoes FPI1/FPI2 in its ack (VELUX KLF 200 API v3.18 §10.1.1.5). The 0x80 form of the extended 0x03 request selects FP1.

Definition at line 601 of file proto_constants.h.

◆ STATUS_UPDATE_EVENT_DATA_OFFSET

uint8_t esphome::home_io_control::STATUS_UPDATE_EVENT_DATA_OFFSET = 5
inlineconstexpr

First event-data byte.

Definition at line 416 of file proto_codecs.h.

◆ STATUS_UPDATE_EVENT_ID_OFFSET

uint8_t esphome::home_io_control::STATUS_UPDATE_EVENT_ID_OFFSET = 2
inlineconstexpr

Event id, big-endian, 2 bytes.

Definition at line 414 of file proto_codecs.h.

◆ STATUS_UPDATE_EVENT_LENGTH_OFFSET

uint8_t esphome::home_io_control::STATUS_UPDATE_EVENT_LENGTH_OFFSET = 4
inlineconstexpr

Number of event-data bytes that follow.

Definition at line 415 of file proto_codecs.h.

◆ STATUS_UPDATE_LAST_COMMAND_OFFSET

uint8_t esphome::home_io_control::STATUS_UPDATE_LAST_COMMAND_OFFSET = 11
inlineconstexpr

Definition at line 373 of file proto_codecs.h.

◆ STATUS_UPDATE_ORIGINATOR_OFFSET

uint8_t esphome::home_io_control::STATUS_UPDATE_ORIGINATOR_OFFSET = 14
inlineconstexpr

Offset of the Command Originator byte in a CMD_STATUS_UPDATE (0x71) payload.

Deliberately not offset 1: data[1] on a 0x71 is the status byte (0x60/0x61, bit 0 = current position unknown), which matches no ORIGINATOR_* value, so reading it as an originator rendered "unknown" on every frame this project has ever captured. Every captured 0x71 carries 0x01 (ORIGINATOR_USER_REMOTE) here.

Definition at line 381 of file proto_codecs.h.

◆ STOP_SETTLE_POLL_CAP_MS

uint32_t esphome::home_io_control::STOP_SETTLE_POLL_CAP_MS = 1000
staticconstexpr

Upper bound on the settle-poll delay after a STOP command.

STOP should confirm the resting position quickly, so it always settles faster than a normal move, regardless of the configured interval or remaining time.

Definition at line 115 of file status_poll_policy.h.

◆ SUBSCRIBE_EVENT_DATA_SIZE

uint8_t esphome::home_io_control::SUBSCRIBE_EVENT_DATA_SIZE = 3
staticconstexpr

CMD_SUBSCRIBE_EVENT payload size: the action byte and the big-endian event id.

Definition at line 370 of file proto_constants.h.

◆ SUBTYPE_HEATER_WITH_SETPOINT

uint8_t esphome::home_io_control::SUBTYPE_HEATER_WITH_SETPOINT = 1
staticconstexpr

Subtype of the electrical heater (node type 52) with an adjustable setpoint, the one driven over the private register protocol (proto_heating.h).

Definition at line 37 of file proto_device_types.h.

◆ SUBTYPE_MAIN_PARAMETER_ONLY

uint8_t esphome::home_io_control::SUBTYPE_MAIN_PARAMETER_ONLY = 58
staticconstexpr

Subtype value that marks a device taking the main parameter only, on any node type.

The subtype is defined that way in the public VELUX KLF 200 API specification (Appendix 2).

Definition at line 33 of file proto_device_types.h.

◆ SX1262_BUSY_TIMEOUT_MS

uint32_t esphome::home_io_control::SX1262_BUSY_TIMEOUT_MS = 25
staticconstexpr

Ceiling on how long SoftPhyDriverBase::wait_busy_() waits for BUSY to drop before declaring the chip failed.

A healthy chip never waits for it, and the wait is judged on a fresh sample, so a task preemption cannot trip it; this is headroom for the RC-oscillator-clocked commands (unlike LR1121's post-reset boot ROM, see LR1121_BUSY_TIMEOUT_MS in radio_lr1121.h). It must stay below the smallest TCXO bring-up rung budget (30 ms) so every rung still widens it.

Definition at line 182 of file radio_sx1262.h.

◆ SX1262_CALIBRATE

uint8_t esphome::home_io_control::SX1262_CALIBRATE = 0x89
staticconstexpr

Definition at line 52 of file radio_sx1262.h.

◆ SX1262_CALIBRATE_IMAGE

uint8_t esphome::home_io_control::SX1262_CALIBRATE_IMAGE = 0x98
staticconstexpr

Definition at line 53 of file radio_sx1262.h.

◆ SX1262_CLEAR_DEVICE_ERRORS

uint8_t esphome::home_io_control::SX1262_CLEAR_DEVICE_ERRORS = 0x07
staticconstexpr

Definition at line 43 of file radio_sx1262.h.

◆ SX1262_CLEAR_IRQ_STATUS

uint8_t esphome::home_io_control::SX1262_CLEAR_IRQ_STATUS = 0x02
staticconstexpr

Definition at line 42 of file radio_sx1262.h.

◆ SX1262_DEV_ERR_ADC_CALIB

uint16_t esphome::home_io_control::SX1262_DEV_ERR_ADC_CALIB = 0x0008
staticconstexpr

ADC calibration failed.

Definition at line 91 of file radio_sx1262.h.

◆ SX1262_DEV_ERR_IMG_CALIB

uint16_t esphome::home_io_control::SX1262_DEV_ERR_IMG_CALIB = 0x0010
staticconstexpr

Image calibration failed.

Definition at line 92 of file radio_sx1262.h.

◆ SX1262_DEV_ERR_PA_RAMP

uint16_t esphome::home_io_control::SX1262_DEV_ERR_PA_RAMP = 0x0100
staticconstexpr

PA ramping failed (bit 0x0080 is unused).

Definition at line 95 of file radio_sx1262.h.

◆ SX1262_DEV_ERR_PLL_CALIB

uint16_t esphome::home_io_control::SX1262_DEV_ERR_PLL_CALIB = 0x0004
staticconstexpr

PLL calibration failed.

Definition at line 90 of file radio_sx1262.h.

◆ SX1262_DEV_ERR_PLL_LOCK

uint16_t esphome::home_io_control::SX1262_DEV_ERR_PLL_LOCK = 0x0040
staticconstexpr

PLL failed to lock.

Definition at line 94 of file radio_sx1262.h.

◆ SX1262_DEV_ERR_RC13M_CALIB

uint16_t esphome::home_io_control::SX1262_DEV_ERR_RC13M_CALIB = 0x0002
staticconstexpr

13 MHz RC oscillator calibration failed.

Definition at line 89 of file radio_sx1262.h.

◆ SX1262_DEV_ERR_RC64K_CALIB

uint16_t esphome::home_io_control::SX1262_DEV_ERR_RC64K_CALIB = 0x0001
staticconstexpr

64 kHz RC oscillator calibration failed.

Definition at line 88 of file radio_sx1262.h.

◆ SX1262_DEV_ERR_XOSC_START

uint16_t esphome::home_io_control::SX1262_DEV_ERR_XOSC_START = 0x0020
staticconstexpr

Crystal/TCXO failed to start.

Definition at line 93 of file radio_sx1262.h.

◆ SX1262_DISCOVERY_HOP_SLICE_MS

uint16_t esphome::home_io_control::SX1262_DISCOVERY_HOP_SLICE_MS = 7
staticconstexpr

Per-channel dwell while SX1262 pairing discovery hops across channels.

SX1262 frequency changes require a standby→SetRfFrequency→RX cycle, unlike the SX1276's FastHop, but that changes only how much a single retune costs, not how long the radio should then sit still: a short dwell fits more retunes into the listen window than a long one, so the receiver is more often already parked on the right channel by the time a reply starts. The preamble/sync linger guard (ListenSpec::linger_on_preamble) is what makes a dwell this short safe — it keeps a caught reply from being cut off mid-reception by extending the dwell instead of hopping away. A dwell in the low single-digit milliseconds performs best; shorter than that, coverage degrades gradually and only truly collapses at 0, where wait_for_packet(..., 0) returns before any guard can observe activity at all. 7 is the best-performing value found in that range.

Definition at line 227 of file tuning_config.h.

◆ SX1262_FALLBACK_STDBY_XOSC

uint8_t esphome::home_io_control::SX1262_FALLBACK_STDBY_XOSC = 0x30
staticconstexpr

Definition at line 175 of file radio_sx1262.h.

◆ SX1262_GET_DEVICE_ERRORS

uint8_t esphome::home_io_control::SX1262_GET_DEVICE_ERRORS = 0x17
staticconstexpr

Definition at line 41 of file radio_sx1262.h.

◆ SX1262_GET_IRQ_STATUS

uint8_t esphome::home_io_control::SX1262_GET_IRQ_STATUS = 0x12
staticconstexpr

Definition at line 39 of file radio_sx1262.h.

◆ SX1262_GET_PACKET_STATUS

uint8_t esphome::home_io_control::SX1262_GET_PACKET_STATUS = 0x14
staticconstexpr

Definition at line 40 of file radio_sx1262.h.

◆ SX1262_GET_RSSI_INST

uint8_t esphome::home_io_control::SX1262_GET_RSSI_INST = 0x15
staticconstexpr

Definition at line 55 of file radio_sx1262.h.

◆ SX1262_GET_RX_BUFFER_STATUS

uint8_t esphome::home_io_control::SX1262_GET_RX_BUFFER_STATUS = 0x13
staticconstexpr

Definition at line 54 of file radio_sx1262.h.

◆ SX1262_GET_STATUS

uint8_t esphome::home_io_control::SX1262_GET_STATUS = 0xC0
staticconstexpr

Definition at line 57 of file radio_sx1262.h.

◆ SX1262_GFSK_CRC_OFF

uint8_t esphome::home_io_control::SX1262_GFSK_CRC_OFF = 0x01
staticconstexpr

Definition at line 167 of file radio_sx1262.h.

◆ SX1262_GFSK_PACKET_TYPE_KNOWN_LENGTH

uint8_t esphome::home_io_control::SX1262_GFSK_PACKET_TYPE_KNOWN_LENGTH = 0x00
staticconstexpr

Definition at line 166 of file radio_sx1262.h.

◆ SX1262_IRQ_ACTIVITY_MASK

uint16_t esphome::home_io_control::SX1262_IRQ_ACTIVITY_MASK
staticconstexpr
Initial value:
=
static constexpr uint16_t SX1262_IRQ_SYNC_WORD_VALID
static constexpr uint16_t SX1262_IRQ_CRC_ERR
static constexpr uint16_t SX1262_IRQ_RX_DONE
static constexpr uint16_t SX1262_IRQ_TX_DONE

IRQ bits that represent a terminal radio event — a frame finished decoding, an outbound frame completed, or the chip reported a bad CRC.

Deliberately excludes PREAMBLE_DETECTED even though configure_radio_() now includes it in irqMask: a preamble alone means a frame may still be arriving, so SoftPhyDriverBase's poll_until_activity_()/check_for_packet() (gated by RadioSX1262::activity_irq_mask) must not treat it as terminal — doing so would tear down RX mid-reception. Same trap, same fix shape as LR1121_IRQ_ACTIVITY_MASK.

Definition at line 79 of file radio_sx1262.h.

◆ SX1262_IRQ_CRC_ERR

uint16_t esphome::home_io_control::SX1262_IRQ_CRC_ERR = 0x0040
staticconstexpr

Definition at line 71 of file radio_sx1262.h.

◆ SX1262_IRQ_PREAMBLE_DETECTED

uint16_t esphome::home_io_control::SX1262_IRQ_PREAMBLE_DETECTED = 0x0004
staticconstexpr

Definition at line 69 of file radio_sx1262.h.

◆ SX1262_IRQ_RX_DONE

uint16_t esphome::home_io_control::SX1262_IRQ_RX_DONE = 0x0002
staticconstexpr

Definition at line 68 of file radio_sx1262.h.

◆ SX1262_IRQ_SYNC_WORD_VALID

uint16_t esphome::home_io_control::SX1262_IRQ_SYNC_WORD_VALID = 0x0008
staticconstexpr

Definition at line 70 of file radio_sx1262.h.

◆ SX1262_IRQ_TX_DONE

uint16_t esphome::home_io_control::SX1262_IRQ_TX_DONE = 0x0001
staticconstexpr

Definition at line 67 of file radio_sx1262.h.

◆ SX1262_POST_TX_SETTLE_US

uint16_t esphome::home_io_control::SX1262_POST_TX_SETTLE_US = 500
staticconstexpr

SX1262-specific post-TX settling delay before re-entering RX.

The SX1262 GFSK demodulator needs time to stabilize after TX before it can reliably receive the next frame. A 500 µs delay was validated as the minimum that prevents challenge-byte corruption during pairing and tight-turnaround authenticated exchanges.

Definition at line 200 of file tuning_config.h.

◆ SX1262_PREAMBLE_DETECTOR_16_BIT

uint8_t esphome::home_io_control::SX1262_PREAMBLE_DETECTOR_16_BIT = 0x05
staticconstexpr

Preamble detector length selector: 16 bits (0x04 = 8, 0x05 = 16, 0x06 = 24, 0x07 = 32 bits).

The 8-bit detector fired on noise 3-4 times per second of listening on a Heltec V3, and each false detection held a rotating listen on an empty channel for 25-35 ms; at 16 bits those were gone and every answer was still caught. A real preamble runs 14 ms or more (500+ bits), so 16 bits costs it nothing. 24 bits stopped all sync detection on the same bench (cause not known), so do not raise this further without measuring. The LR1121 driver uses the same 16-bit value.

Definition at line 174 of file radio_sx1262.h.

◆ SX1262_READ_BUFFER

uint8_t esphome::home_io_control::SX1262_READ_BUFFER = 0x1E
staticconstexpr

Definition at line 37 of file radio_sx1262.h.

◆ SX1262_READ_REGISTER

uint8_t esphome::home_io_control::SX1262_READ_REGISTER = 0x1D
staticconstexpr

Definition at line 61 of file radio_sx1262.h.

◆ SX1262_REG_RX_GAIN

uint16_t esphome::home_io_control::SX1262_REG_RX_GAIN = 0x08AC
staticconstexpr

Definition at line 143 of file radio_sx1262.h.

◆ SX1262_REG_SYNC_WORD

uint16_t esphome::home_io_control::SX1262_REG_SYNC_WORD = 0x06C0
staticconstexpr

Definition at line 142 of file radio_sx1262.h.

◆ SX1262_REG_TX_CLAMP_CONFIG

uint16_t esphome::home_io_control::SX1262_REG_TX_CLAMP_CONFIG = 0x08D8
staticconstexpr

Definition at line 144 of file radio_sx1262.h.

◆ SX1262_REG_TX_MODULATION

uint16_t esphome::home_io_control::SX1262_REG_TX_MODULATION = 0x0889
staticconstexpr

TX modulation-quality erratum register (SX1262 datasheet §15.1, "Modulation Quality with 500 kHz LoRa Bandwidth" — the title names LoRa, but the workaround table covers every modulation).

Bit 2 must be cleared only for LoRa at BW 500 kHz and set to 1 for everything else, explicitly including any (G)FSK configuration — which is all this driver ever uses. Semtech's own driver and RadioLib apply it as TxModulation / fixModulationQuality(). Leaving it at its reset value degrades transmitted modulation quality, which on this protocol shows up as a peer that intermittently fails to decode an otherwise strong frame. Counterpart to the already-applied TxClamp erratum (SX1262_REG_TX_CLAMP_CONFIG).

Definition at line 154 of file radio_sx1262.h.

◆ SX1262_RESPONSE_PREAMBLE

uint16_t esphome::home_io_control::SX1262_RESPONSE_PREAMBLE = 12
staticconstexpr

SX1262-specific preamble for response/continuation frames within an exchange.

Applies to any tight-turnaround frame sent immediately after receiving from the device — 0x3D challenge responses, 0x32 key transfers after receiving 0x3C, and any future protocol frame in that position — giving the peer's receiver time to lock back on after the SX1262's own TX→RX turnaround.

Defaults to 12 bytes, which puts about 102 bits on air (8 bits per byte plus 6 of soft-PHY framing, ~2.6 ms): the continuation preamble a real controller was observed sending on air (101 bits). It also matches SX1276_RESPONSE_PREAMBLE. In a field run (issue #119) a Somfy light's closing reply was lost after 13 of 38 challenges with an 8-byte preamble against 4 of 35 with 12. The cost is about 0.8 ms of extra delay before the reply. The protocol's own nominal SHORT_PREAMBLE (8 bytes) stays the floor for the runtime override.

This constant is byte-denominated, like every other preamble value in this codebase (LONG_PREAMBLE, SHORT_PREAMBLE) — RadioSX1262::set_packet_params_() is the one place that converts to the chip's bit-denominated SetPacketParams field, right before the value leaves for the wire.

Definition at line 192 of file tuning_config.h.

◆ SX1262_RX_BUFFER_BASE

uint8_t esphome::home_io_control::SX1262_RX_BUFFER_BASE = 0x80
staticconstexpr

Definition at line 164 of file radio_sx1262.h.

◆ SX1262_SET_BUFFER_BASE_ADDRESS

uint8_t esphome::home_io_control::SX1262_SET_BUFFER_BASE_ADDRESS = 0x8F
staticconstexpr

Definition at line 47 of file radio_sx1262.h.

◆ SX1262_SET_DIO2_AS_RF_SWITCH_CTRL

uint8_t esphome::home_io_control::SX1262_SET_DIO2_AS_RF_SWITCH_CTRL = 0x9D
staticconstexpr

Definition at line 50 of file radio_sx1262.h.

◆ SX1262_SET_DIO3_AS_TCXO_CTRL

uint8_t esphome::home_io_control::SX1262_SET_DIO3_AS_TCXO_CTRL = 0x97
staticconstexpr

Definition at line 51 of file radio_sx1262.h.

◆ SX1262_SET_DIO_IRQ_PARAMS

uint8_t esphome::home_io_control::SX1262_SET_DIO_IRQ_PARAMS = 0x08
staticconstexpr

Definition at line 38 of file radio_sx1262.h.

◆ SX1262_SET_MODULATION_PARAMS

uint8_t esphome::home_io_control::SX1262_SET_MODULATION_PARAMS = 0x8B
staticconstexpr

Definition at line 45 of file radio_sx1262.h.

◆ SX1262_SET_PA_CONFIG

uint8_t esphome::home_io_control::SX1262_SET_PA_CONFIG = 0x95
staticconstexpr

Definition at line 48 of file radio_sx1262.h.

◆ SX1262_SET_PACKET_PARAMS

uint8_t esphome::home_io_control::SX1262_SET_PACKET_PARAMS = 0x8C
staticconstexpr

Definition at line 46 of file radio_sx1262.h.

◆ SX1262_SET_PACKET_TYPE

uint8_t esphome::home_io_control::SX1262_SET_PACKET_TYPE = 0x8A
staticconstexpr

Definition at line 44 of file radio_sx1262.h.

◆ SX1262_SET_REGULATOR_MODE

uint8_t esphome::home_io_control::SX1262_SET_REGULATOR_MODE = 0x96
staticconstexpr

Definition at line 56 of file radio_sx1262.h.

◆ SX1262_SET_RF_FREQUENCY

uint8_t esphome::home_io_control::SX1262_SET_RF_FREQUENCY = 0x86
staticconstexpr

Definition at line 34 of file radio_sx1262.h.

◆ SX1262_SET_RX

uint8_t esphome::home_io_control::SX1262_SET_RX = 0x82
staticconstexpr

Definition at line 32 of file radio_sx1262.h.

◆ SX1262_SET_RX_TX_FALLBACK_MODE

uint8_t esphome::home_io_control::SX1262_SET_RX_TX_FALLBACK_MODE = 0x93
staticconstexpr

Definition at line 35 of file radio_sx1262.h.

◆ SX1262_SET_STANDBY

uint8_t esphome::home_io_control::SX1262_SET_STANDBY = 0x80
staticconstexpr

Definition at line 31 of file radio_sx1262.h.

◆ SX1262_SET_TX

uint8_t esphome::home_io_control::SX1262_SET_TX = 0x83
staticconstexpr

Definition at line 33 of file radio_sx1262.h.

◆ SX1262_SET_TX_PARAMS

uint8_t esphome::home_io_control::SX1262_SET_TX_PARAMS = 0x8E
staticconstexpr

Definition at line 49 of file radio_sx1262.h.

◆ SX1262_SYNC_WORD_PARAM_24_BITS

const uint8_t esphome::home_io_control::SX1262_SYNC_WORD_PARAM_24_BITS = 0x18
static

Definition at line 31 of file radio_sx1262.cpp.

◆ SX1262_TX_BUFFER_BASE

uint8_t esphome::home_io_control::SX1262_TX_BUFFER_BASE = 0x00
staticconstexpr

Data-buffer split programmed by configure_buffer_base(): TX packets build from 0x00, RX packets land at 0x80.

The RX base doubles as the read offset for a length-driven receive — a single in-flight packet always starts exactly there.

Definition at line 163 of file radio_sx1262.h.

◆ SX1262_TX_MODULATION_GFSK_BIT

uint8_t esphome::home_io_control::SX1262_TX_MODULATION_GFSK_BIT = 0x04
staticconstexpr

Bit 2 of SX1262_REG_TX_MODULATION — the (G)FSK-correct value is 1.

Definition at line 158 of file radio_sx1262.h.

◆ SX1262_WRITE_BUFFER

uint8_t esphome::home_io_control::SX1262_WRITE_BUFFER = 0x0E
staticconstexpr

Definition at line 36 of file radio_sx1262.h.

◆ SX1262_WRITE_REGISTER

uint8_t esphome::home_io_control::SX1262_WRITE_REGISTER = 0x0D
staticconstexpr

Definition at line 60 of file radio_sx1262.h.

◆ SX1276_CCA_SETTLE_US

uint32_t esphome::home_io_control::SX1276_CCA_SETTLE_US = 250
staticconstexpr

Wait after retuning to the TX channel before the clear-channel RSSI reading.

The fast-hop PLL relocks in tens of µs, and with RssiSmoothing = 8 samples at the 50 kHz RX bandwidth one RSSI update takes about 40 µs; 250 µs covers both with margin and costs nothing next to a preamble.

Todo
Confirm on the bench (Heltec V2): time the retune and compare the reading against one taken after a long dwell on the same channel.

Definition at line 28 of file radio_sx1276.h.

◆ SX1276_DISCOVERY_HOP_SLICE_MS

uint16_t esphome::home_io_control::SX1276_DISCOVERY_HOP_SLICE_MS = 5
staticconstexpr

Per-channel dwell while SX1276 pairing discovery hops across channels.

The SX1276 supports FastHop (no standby transition), so a short slice keeps discovery sweeping all three channels quickly.

Definition at line 213 of file tuning_config.h.

◆ SX1276_PA_SELECT_PA_BOOST

uint8_t esphome::home_io_control::SX1276_PA_SELECT_PA_BOOST = 0x80
staticconstexpr

pa_pin value that routes the output through the PA_BOOST pin; any other value selects the RFO pin.

Doubles as the PaSelect bit of REG_PA_CONFIG.

Definition at line 21 of file radio_sx1276.h.

◆ SX1276_PREAMBLE_HOLD_US

uint32_t esphome::home_io_control::SX1276_PREAMBLE_HOLD_US = 250000
staticconstexpr

How long reception_in_progress() holds the idle hop on a channel once PreambleDetect is set: the longest start preamble (LONG_PREAMBLE, 1024 bytes = 213 ms at 38400 bps) plus margin, so the sync word at the end of a wake-up preamble arrives while the receiver is still there.

Also the bound on a preamble detection with no frame behind it (noise): after it the hop goes on, and its receiver restart clears the flag.

Definition at line 89 of file radio_sx1276.h.

◆ SX1276_RESPONSE_PREAMBLE

uint16_t esphome::home_io_control::SX1276_RESPONSE_PREAMBLE = 12
staticconstexpr

SX1276 preamble for response/continuation frames within an exchange.

Defaults to 12 bytes — longer than the protocol's 8-byte SHORT_PREAMBLE. On real hardware the extra length improves the peer device's lock-on without measurably affecting exchange timing. Runtime-tunable down to SHORT_PREAMBLE or up for a marginal-range install.

Definition at line 207 of file tuning_config.h.

◆ TAG [1/15]

const char* esphome::home_io_control::TAG = "home_io_control"
staticconstexpr

Definition at line 18 of file device_registry.cpp.

◆ TAG [2/15]

const char* const esphome::home_io_control::TAG = "home_io_control.exchange"
static

Definition at line 28 of file exchange_engine.cpp.

◆ TAG [3/15]

const char* const esphome::home_io_control::TAG = detail::TAG
static

Definition at line 58 of file hub_core.cpp.

◆ TAG [4/15]

const char* const esphome::home_io_control::TAG = "home_io_control.climate"
static

Definition at line 20 of file platform_climate.cpp.

◆ TAG [5/15]

const char* const esphome::home_io_control::TAG = "home_io_control.cover"
static

Definition at line 12 of file platform_cover.cpp.

◆ TAG [6/15]

const char* const esphome::home_io_control::TAG = "home_io_control.fan"
static

Definition at line 18 of file platform_fan.cpp.

◆ TAG [7/15]

const char* const esphome::home_io_control::TAG = "home_io_control.light"
static

Definition at line 12 of file platform_light.cpp.

◆ TAG [8/15]

const char* const esphome::home_io_control::TAG = "home_io_control.lock"
static

Definition at line 13 of file platform_lock.cpp.

◆ TAG [9/15]

const char* const esphome::home_io_control::TAG = "home_io_control.switch"
static

Definition at line 12 of file platform_switch.cpp.

◆ TAG [10/15]

const char* const esphome::home_io_control::TAG = "home_io_control.water_heater"
static

Definition at line 18 of file platform_water_heater.cpp.

◆ TAG [11/15]

const char* const esphome::home_io_control::TAG = "home_io_control.radio"
static

Definition at line 14 of file radio_interface.cpp.

◆ TAG [12/15]

const char* const esphome::home_io_control::TAG = "home_io_control.lr1121"
static

Definition at line 27 of file radio_lr1121.cpp.

◆ TAG [13/15]

const char* const esphome::home_io_control::TAG = "home_io_control.soft_phy"
static

Definition at line 26 of file radio_soft_phy_driver_base.cpp.

◆ TAG [14/15]

const char* const esphome::home_io_control::TAG = "home_io_control.sx1262"
static

Definition at line 30 of file radio_sx1262.cpp.

◆ TAG [15/15]

const char* const esphome::home_io_control::TAG = "home_io_control.sx1276"
static

Definition at line 21 of file radio_sx1276.cpp.

◆ TAG_BATTERY

const char* const esphome::home_io_control::TAG_BATTERY = "home_io_control.cover.battery"
static

Definition at line 22 of file platform_cover_controls.cpp.

◆ TAG_BATTERY_LEVEL

const char* const esphome::home_io_control::TAG_BATTERY_LEVEL = "home_io_control.cover.battery_level"
static

Definition at line 24 of file platform_cover_controls.cpp.

◆ TAG_COMMAND_BUTTON

const char* const esphome::home_io_control::TAG_COMMAND_BUTTON = "home_io_control.cover_command_button"
static

Definition at line 18 of file platform_cover_controls.cpp.

◆ TAG_DETECTION

const char* const esphome::home_io_control::TAG_DETECTION = "home_io_control.window_detection"
static

Definition at line 18 of file platform_climate_controls.cpp.

◆ TAG_NIGHT_LIGHT

const char* const esphome::home_io_control::TAG_NIGHT_LIGHT = "home_io_control.night_light"
static

Definition at line 19 of file platform_climate_controls.cpp.

◆ TAG_RAIN

const char* const esphome::home_io_control::TAG_RAIN = "home_io_control.cover.rain_sensor"
static

Definition at line 21 of file platform_cover_controls.cpp.

◆ TAG_SILENT

const char* const esphome::home_io_control::TAG_SILENT = "home_io_control.cover.silent"
static

Definition at line 19 of file platform_cover_controls.cpp.

◆ TAG_SUN_LEVEL

const char* const esphome::home_io_control::TAG_SUN_LEVEL = "home_io_control.cover.sun_level"
static

Definition at line 23 of file platform_cover_controls.cpp.

◆ TAG_TRAVEL_SPEED

const char* const esphome::home_io_control::TAG_TRAVEL_SPEED = "home_io_control.cover.travel_speed"
static

Definition at line 20 of file platform_cover_controls.cpp.

◆ TAG_WINDOW

const char* const esphome::home_io_control::TAG_WINDOW = "home_io_control.window"
static

Definition at line 17 of file platform_climate_controls.cpp.

◆ TCXO_VOLTAGE_NONE

uint8_t esphome::home_io_control::TCXO_VOLTAGE_NONE = 0xFF
inlineconstexpr

Sentinel tcxo_voltage code (YAML none) for a board with a bare crystal and no TCXO.

The SX1262 and LR1121 drivers skip all DIO3/TCXO programming when they see it and calibrate off the plain crystal. Cannot collide with the real 0-based voltage codes (0x00-0x07).

Definition at line 45 of file radio_interface.h.

◆ TRANSFER_BLOCK_SIZE

uint8_t esphome::home_io_control::TRANSFER_BLOCK_SIZE = 18
staticconstexpr

Data bytes per CMD_UNKNOWN4A_RESP; 18 fills a 32-byte frame.

Definition at line 248 of file proto_constants.h.

◆ TRANSFER_CHUNK_CLOSE

uint16_t esphome::home_io_control::TRANSFER_CHUNK_CLOSE = 0
staticconstexpr

Chunk number of the closing CMD_UNKNOWN4A_REQ.

Definition at line 255 of file proto_constants.h.

◆ TRANSFER_KEY

uint8_t esphome::home_io_control::TRANSFER_KEY[AES_KEY_SIZE]
staticconstexpr
Initial value:
= {0x34, 0xC3, 0x46, 0x6E, 0xD8, 0x8F, 0x4E, 0x8E,
0x16, 0xAA, 0x47, 0x39, 0x49, 0x88, 0x43, 0x73}

The transfer key is a hardcoded key used ONLY during pairing to obfuscate the system key during over-the-air transfer.

It is NOT the system key. This is the same across all IO-Homecontrol devices worldwide.

Definition at line 675 of file proto_constants.h.

◆ TRANSFER_OBJECT_ACTUATOR_CAPACITY

uint16_t esphome::home_io_control::TRANSFER_OBJECT_ACTUATOR_CAPACITY = 0x8100
staticconstexpr

Definition at line 222 of file proto_constants.h.

◆ TRANSFER_OBJECT_ACTUATOR_STATE

uint16_t esphome::home_io_control::TRANSFER_OBJECT_ACTUATOR_STATE = 0x8103
staticconstexpr

The actuator state record read beside the capacity record.

Todo
Byte layout unknown.

Definition at line 224 of file proto_constants.h.

◆ TRANSFER_OBJECT_CLASS_DEVICE

uint8_t esphome::home_io_control::TRANSFER_OBJECT_CLASS_DEVICE = 0
staticconstexpr

Object class of the TRANSFER_OBJECT_* ids.

Definition at line 242 of file proto_constants.h.

◆ TRANSFER_OBJECT_CLASS_SOMFY

uint8_t esphome::home_io_control::TRANSFER_OBJECT_CLASS_SOMFY = 2
staticconstexpr

Object class of the Somfy manufacturer objects in TRANSFER_READABLE_OBJECTS.

Todo
No device has been captured answering an open of this class yet.

Definition at line 245 of file proto_constants.h.

◆ TRANSFER_OBJECT_GENERAL_INFO1

uint16_t esphome::home_io_control::TRANSFER_OBJECT_GENERAL_INFO1 = 0x8001
staticconstexpr

Two further class-0 records beside the name record.

A Somfy Izymo dimmer returned the same bytes from them as from CMD_GET_INFO1 (0x8001, 14 bytes) and CMD_GET_INFO2 (0x8002, 16 bytes), so they are the object-transport form of those two answers.

Definition at line 234 of file proto_constants.h.

◆ TRANSFER_OBJECT_GENERAL_INFO2

uint16_t esphome::home_io_control::TRANSFER_OBJECT_GENERAL_INFO2 = 0x8002
staticconstexpr

Definition at line 235 of file proto_constants.h.

◆ TRANSFER_OBJECT_NAME

uint16_t esphome::home_io_control::TRANSFER_OBJECT_NAME = 0x8003
staticconstexpr

A 16-byte name record: on a Somfy Izymo dimmer 00 followed by the ASCII text "MODULE DIMMER" and zero padding, read behind an authenticated block read.

Whether it always equals the CMD_GET_NAME text is not established. It is the object that shows the open/read/close flow works on a device before the two records above are read.

Definition at line 229 of file proto_constants.h.

◆ TRANSFER_OPEN_DATA_SIZE

uint8_t esphome::home_io_control::TRANSFER_OPEN_DATA_SIZE = 9
staticconstexpr

CMD_TRANSFER_OPEN payload bytes.

Definition at line 241 of file proto_constants.h.

◆ TRANSFER_OPEN_RESP_DATA_SIZE

uint8_t esphome::home_io_control::TRANSFER_OPEN_RESP_DATA_SIZE = 4
staticconstexpr

CMD_TRANSFER_OPEN_RESP payload bytes.

Definition at line 246 of file proto_constants.h.

◆ TRANSFER_READ_REQ_DATA_SIZE

uint8_t esphome::home_io_control::TRANSFER_READ_REQ_DATA_SIZE = 3
staticconstexpr

CMD_UNKNOWN4A_REQ payload bytes: handle + chunk.

Definition at line 247 of file proto_constants.h.

◆ TRANSFER_READABLE_OBJECT_COUNT

uint8_t esphome::home_io_control::TRANSFER_READABLE_OBJECT_COUNT
staticconstexpr
Initial value:
=
static constexpr TransferObject TRANSFER_READABLE_OBJECTS[]
Every object the read_object probe can open, in probe-index order (ADR 0062).

Number of entries in TRANSFER_READABLE_OBJECTS.

Definition at line 309 of file proto_constants.h.

◆ TRANSFER_READABLE_OBJECTS

TransferObject esphome::home_io_control::TRANSFER_READABLE_OBJECTS[]
staticconstexpr

Every object the read_object probe can open, in probe-index order (ADR 0062).

create_transfer_open() refuses any class/id pair not in this table, so the table is the allowlist. Indexes 0 to 4 are the five named objects above. Indexes 5 to 9 are the other class-0 ids a Somfy RS100 Solar io lists in its own capacity record (issue #156); they have not been read yet. Indexes 10 to 29 are Somfy manufacturer objects to read on the same motor; a device that does not keep an object refuses the open (status 3, length 0) and nothing more is sent. Every entry is a plain read of a whole object. Appending keeps the existing indexes stable; never reorder.

Todo
The layouts of entries 5 to 29 are unknown: read them to learn what they hold.

Definition at line 273 of file proto_constants.h.

◆ TRANSFER_STATUS_ACCEPTED

uint8_t esphome::home_io_control::TRANSFER_STATUS_ACCEPTED = 0
staticconstexpr

CMD_TRANSFER_OPEN_RESP status: request accepted.

Definition at line 249 of file proto_constants.h.

◆ TRANSFER_STATUS_ADAPTED

uint8_t esphome::home_io_control::TRANSFER_STATUS_ADAPTED = 1
staticconstexpr

CMD_TRANSFER_OPEN_RESP status: accepted with changes.

Definition at line 250 of file proto_constants.h.

◆ TRANSFER_STATUS_NOTHING_TO_READ

uint8_t esphome::home_io_control::TRANSFER_STATUS_NOTHING_TO_READ = 7
staticconstexpr

CMD_TRANSFER_OPEN_RESP status 7 with length 0: nothing to send for this object.

An RS100 Solar io answered its object 0x6105 this way twice, with no other transfer open, so it reads as an empty object rather than a refusal (issue #156).

Definition at line 254 of file proto_constants.h.

◆ TX_DONE_GUARD_MS

uint32_t esphome::home_io_control::TX_DONE_GUARD_MS = 250
staticconstexpr

Slack a driver allows past a transmission's own air time before it gives up waiting for the chip's TX-done interrupt.

Covers what happens around the air time on every 868 MHz chip: PLL lock and PA ramp before the first bit, the front-end switch, the 100 µs poll step and task preemption on the ESP32 loop. Each of these is milliseconds at most, so a chip that has not reported TX-done this long after its frame should have ended has stopped transmitting (reset, brown-out, lost interrupt mapping) and waiting longer only blocks the loop: a 1W command sends four copies, each of which would otherwise wait out a fixed multi-second guard.

Definition at line 109 of file radio_soft_phy.h.

◆ UART_CELL_BITS

uint8_t esphome::home_io_control::UART_CELL_BITS = 10
staticconstexpr

Bits an on-air UART cell spends per protocol byte: start(1) + data(8) + stop(1).

Definition at line 50 of file radio_soft_phy.h.

◆ UART_PROBE_MAX_BIT_OFFSET

uint8_t esphome::home_io_control::UART_PROBE_MAX_BIT_OFFSET = 10
staticconstexpr

Number of leading bit alignments the UART probe tries when locating a frame start.

The chip hands us raw bytes with no guaranteed cell alignment, so decode_uart_probe() sweeps starting positions 0..UART_PROBE_MAX_BIT_OFFSET-1. A recovered frame can therefore begin up to UART_PROBE_MAX_BIT_OFFSET - 1 bits into the raw buffer — buffer-sizing asserts must budget for that slack on top of the frame's own packed length.

Definition at line 58 of file radio_soft_phy.h.

◆ UNCONFIRMED_EXECUTE_MAX_RESENDS

uint8_t esphome::home_io_control::UNCONFIRMED_EXECUTE_MAX_RESENDS = 1
staticconstexpr

Re-sends allowed for a CMD_EXECUTE the target accepted without a closing reply, within the same exchange and its retry budget.

One: a second silent try says the loss is not a one-off, and a third copy of a movement command would only lengthen the blocked loop. See decisions::retry_after_unconfirmed_accept_is_safe().

Definition at line 205 of file proto_timing.h.

◆ UNCONFIRMED_EXECUTE_RESEND_DELAY_MS

uint32_t esphome::home_io_control::UNCONFIRMED_EXECUTE_RESEND_DELAY_MS = 750
staticconstexpr

Gap before that re-send, in place of the plan's retry gap (decisions::AttemptPlan::gap_ms).

A device that did act on the first copy is often deaf for about a second while its motor or load switches: on a Somfy awning, a re-send 0.77 s after the first copy drew no challenge at all in 3 of 14 cases. With this gap the re-send goes out about 1.3 s after the first copy, and the whole exchange still fits EXCHANGE_TOTAL_BUDGET_MS. The gap is the total wait: the engine sleeps only the part the plan's own gap does not already cover, so a plan without a gap still re-sends at 750 ms.

Definition at line 219 of file proto_timing.h.

◆ UNCONFIRMED_SUBSCRIBE_MAX_RESENDS

uint8_t esphome::home_io_control::UNCONFIRMED_SUBSCRIBE_MAX_RESENDS = 1
staticconstexpr

Re-sends allowed for a CMD_SUBSCRIBE_EVENT the target accepted without a closing reply.

The answer carries the reading (an RS100 Solar io answers the subscription with the event's current value), so a lost answer is worth one more copy; the device accepts repeated subscriptions (issue #156). One, as for an EXECUTE: a second silent try is not a one-off. See decisions::retry_after_unconfirmed_accept_is_safe().

Definition at line 211 of file proto_timing.h.

◆ UNKNOWN_POSITION

float esphome::home_io_control::UNKNOWN_POSITION = 212.0F
staticconstexpr

Sentinel value meaning "position is not known yet".

Matches POS_UNKNOWN (0xD4 = 212 decimal) for easy debugging.

Definition at line 402 of file proto_device_model.h.

◆ US_PER_MS

uint32_t esphome::home_io_control::US_PER_MS = 1000
staticconstexpr

Microseconds per millisecond, for rounding a driver's transmit-time estimate up to whole ms.

Definition at line 31 of file exchange_engine.cpp.

◆ VELUX_KLI_ENROLLMENT_CLASSES

std::array<DeviceType, 3> esphome::home_io_control::VELUX_KLI_ENROLLMENT_CLASSES
staticconstexpr
Initial value:

The default 0x30 sweep for manufacturer: velux: the exterior-shading classes a KLI 310/313 names — roller shutter, vertical exterior awning, dual roller shutter (issue #74 capture, decoded with broadcast_target_type()).

Not universal: a KLI 312 interior blind uses vertical interior blind + interior venetian blind, so those identities set enrollment_classes: from the classes their remote's own 0x2E names.

Definition at line 188 of file oneway_controller.h.