|
Home IO Control
ESPHome add-on for IO-Homecontrol devices
|
Namespaces | |
| namespace | advisor |
| namespace | crypto |
| namespace | decisions |
| namespace | detail |
| namespace | device_type_flags |
| Bit flags carried by a DeviceTypeInfo row. | |
| 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 | 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 | 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 | 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 | 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 | IOHomeDeviceCounterSensor |
| Shared base for the companions that publish one cumulative uint16_t counter held on IoDevice. More... | |
| class | IOHomeDeviceNameTextSensor |
| Diagnostic text sensor that publishes the cached device name. 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 | 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 | 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 | 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 | 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... | |
| struct | PollTracking |
| Per-device poll scheduling state. 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... | |
| class | RainPollPolicy |
| Per-device periodic limitation-read schedule and miss counter. 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... | |
| 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 | 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 | 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 , 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 | DeviceCapabilityClass : uint8_t { UNKNOWN = 0x00 , COVER = 0x01 , LIGHT = 0x02 , SWITCH = 0x03 , SENSOR = 0x04 , BEACON = 0x05 , CLIMATE = 0x06 , LOCK = 0x07 , HEATER_LEVEL = 0x08 } |
| High‑level capability class derived from DeviceType. More... | |
| enum class | CoverCommand : uint8_t { STOP = 0 , FAVORITE = 1 , VENT = 2 , FORCE_OPEN = 3 } |
| 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 | 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 | 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 } |
| 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 | 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 { HOME , 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 | 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 | 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. | |
| 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()). | |
| 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 | normalize_device_id_argument (const std::string &device_id) |
| static std::string | bool_to_string (bool value) |
| static std::string | format_hex_byte (uint8_t value) |
| Format a byte as two uppercase hex digits, no prefix. | |
| static ManagementActionResult | make_management_result (const std::string &action, const std::string &device_id) |
| static bool | apply_error_response (const IoFrame &response, ManagementActionResult &result) |
| Decode a CMD_ERROR_RESP frame's result code into result. | |
| static bool | parse_probe_index (const std::string &text, uint8_t &out) |
| Parse a probe_device()/probe_sweep() index argument into a byte. | |
| 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 ®istry) |
| 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 ®istry, 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 ®istry, ScanPowerClasses selection, const ScanKeys &keys, bool reveal_keys) |
| Build the final report: known/unknown grouping, header, the selection NOTE, and the truncation NOTE. | |
| 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. | |
| 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 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. | |
| std::string | format_oneway_command_report (const OneWayCommandReport &report) |
| Build the sensor string for one command report. | |
| 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. | |
| 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 | 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_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 ¶meter_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, FAVORITE, or VENT. | |
| 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, uint16_t main_parameter, uint8_t second_bitmap, uint16_t value) |
| Build a temperature / alias execute frame (0x00) carrying 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. | |
| bool | create_status_mp_fp_read (IoFrame &f, const uint8_t *own, const uint8_t *dst, bool low_power) |
| Build the KLF-style status read of the main parameter plus FP1-FP7 (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_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) |
| 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. | |
| uint8_t | discovery_att_class (uint8_t flags) |
| Extract the ATT class field from a discovery response's Multi Information Byte. | |
| 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. | |
| bool | default_inverted_for_type (DeviceType type) |
| Determine whether a device type has inverted position mapping by default. | |
| const char * | rain_sensor_state_name (RainSensorState state) |
| Get a human-readable name for a RainSensorState. | |
| const char * | battery_state_name (BatteryState state) |
| Get the name a battery level is published under. | |
| 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_supports_status_requests (DeviceType type) |
| Does this device type support status request commands (0x03)? | |
| bool | device_supports_tilt (DeviceType type) |
| Does this device type support tilt (slat angle) control? | |
| bool | device_takes_main_parameter_only (DeviceType type, uint8_t subtype) |
| Does this device take movement commands as the main parameter only? | |
| bool | device_supports_vent (DeviceType type) |
| Does this device type support the ventilation position command? | |
| const char * | device_operation_profile_name (DeviceType type) |
| Human‑readable operation profile name for a device type. | |
| 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 inverted, 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 | 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_type_info (DeviceType type, uint8_t subtype) |
| Look up the table row for a (type, subtype) pair. | |
| 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). | |
| DeviceCapabilityClass | device_capability_class (DeviceType type, uint8_t subtype) |
| Capability class of a (type, subtype), subtype-aware. | |
| bool | device_light_is_brightness_only (DeviceType type, uint8_t subtype) |
| Is this light limited to brightness (no colour, no colour temperature)? | |
| 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. | |
| 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. | |
| 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_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) |
| Look up the heating profile for a (type, subtype). | |
| bool | device_uses_heating_registers (DeviceType type, uint8_t subtype) |
| Does a climate entity drive this device over the private register protocol? | |
| 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. | |
| 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. | |
| 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. | |
| 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. | |
| 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. | |
| 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. | |
| uint32_t | rain_poll_first_delay_ms (uint32_t random) |
| Delay of a device's first poll after its interval is set. | |
| uint32_t | rain_poll_jittered_interval_ms (uint32_t interval_ms, uint32_t random) |
| One poll interval moved by a uniform offset of up to RAIN_POLL_JITTER_PERCENT either way. | |
| 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 ®istry) |
| 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 ®istry) |
| 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 hint_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. | |
| 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" |
| 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 |
| 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. | |
| 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 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. | |
| 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(); matches samr037/iohc-flipper's PAIR_DST_{WINDOW,SHUTTER,OTHER}). | |
| static constexpr uint8_t | ONEWAY_POSITION_FULLY_OPEN = 0 |
| Wire-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 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 = "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_RAIN = "home_io_control.cover.rain_sensor" |
| static const char *const | TAG_BATTERY = "home_io_control.cover.battery" |
| 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 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 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_UNKNOWN4A_REQ |
| Reads chunk <handle><chunk u16> of a buffer opened by a 0x46/0x47 exchange; seen on air between two controllers in a VELUX KLR 200 copy session. | |
| static constexpr uint8_t | CMD_UNKNOWN4A_RESP |
| Echoes <handle><chunk> followed by up to 18 bytes of data. | |
| 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 = 0x55 |
| 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, otherwise undocumented. | |
| static constexpr uint8_t | CMD_STATUS_UPDATE = 0x71 |
| Device-initiated status update (needs auth). | |
| static constexpr uint8_t | CMD_STATUS_UPDATE_RESP = 0x72 |
| Acknowledge status update. | |
| 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 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 | 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 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 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. | |
| 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 = 0xD400 |
| Main parameter that means "off": the unknown-position marker, which a command uses together with a functional parameter to switch a device off. | |
| 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 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 broadcasting it across all 3 channels 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 uint16_t | PAIRING_DISCOVERY_PREAMBLE = LONG_PREAMBLE |
| Default for TuningConfig::pairing_discovery_preamble — the preamble on the pairing discovery broadcast (CMD_DISCOVER_REQ/CMD_DISCOVER_ALT_REQ, 0x28/0x2E). | |
| 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 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 command before reporting failure. | |
| 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 uint32_t | UNCONFIRMED_EXECUTE_RESEND_DELAY_MS = 750 |
| Gap before that re-send, in place of EXCHANGE_RETRY_DELAY_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 — every status poll issued while StatusPollPolicy is tracking the device, which today is every status poll this component can produce (there is no user-facing "refresh status" button or action; if one is ever added, it must not take this branch). | |
| static constexpr uint8_t | SCHEDULED_POLL_RETRY_GRACE_FIRST_FAILURE = 1 |
| Ladder positions at which a scheduler-owned status poll gets the full EXCHANGE_RETRY_COUNT back. | |
| static constexpr uint8_t | SCHEDULED_POLL_RETRY_GRACE_LAST_FAILURE = 3 |
| static constexpr uint8_t | STOP_SETTLE_POLL_TRIES = EXCHANGE_RETRY_COUNT |
| Exchange tries for the settle poll after an accepted STOP. | |
| 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 for ETSI EN 300 220 compliance. | |
| 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 (≥ 5ms per ETSI). | |
| 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 (both LP and HP PA). | |
| static constexpr uint8_t | LR1121_RFSWITCH_TX_HP = 0x02 |
| Same as TX (LP PA only used today). | |
| 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 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 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_FALLBACK_STDBY_XOSC = 0x30 |
| static constexpr uint32_t | SX1262_BUSY_TIMEOUT_MS = 10 |
| 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 uint32_t | FXOSC = 32000000U |
| SX1276 crystal oscillator frequency (32 MHz). | |
| static constexpr uint32_t | RAIN_POLL_INITIAL_DELAY_MS = 15000 |
| Earliest first poll after boot. | |
| static constexpr uint32_t | RAIN_POLL_INITIAL_SPREAD_MS = 30000 |
| Width of the random window added to RAIN_POLL_INITIAL_DELAY_MS, so devices do not start in step. | |
| static constexpr uint32_t | RAIN_POLL_JITTER_PERCENT = 10 |
| Per-poll offset, as a percentage of the interval, applied symmetrically around it. | |
| static constexpr uint32_t | RAIN_POLL_MOVING_RETRY_MS = 30000 |
| Delay before asking again when the device is moving. | |
| static constexpr uint8_t | RAIN_POLL_MAX_TRIES = 2 |
| Tries per poll. | |
| static constexpr uint8_t | RAIN_POLL_FAILURES_BEFORE_UNKNOWN = 3 |
| Consecutive unanswered polls after which the last reading is no longer usable. | |
| 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 uint32_t | MAX_TRACKED_STATUS_POLL_WINDOW_MS = 600000 |
| Hard stop for bounded follow-up polling after a command or remote activity. | |
| 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 | REMOTE_ACTIVITY_STATUS_POLL_DELAY_MS = 2000 |
| Delay before polling after overheard remote traffic. | |
| 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 = 8 |
| 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_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 = 7 |
| FP1-FP7: the most one request can select. | |
| static constexpr uint8_t | STATUS_MP_FP_FPI2 = 0x00 |
| FPI2: no FP9-FP16 requested. | |
| 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 |
| Steady-state backoff after many silent failures. | |
Background-poll retry delays after auth-shaped failures (0x3C challenge seen) | |
| static constexpr uint32_t | STATUS_AUTH_RETRY_AFTER_FAIL_MS = 30000 |
| First retry after a challenge-seen failure. | |
| static constexpr uint32_t | STATUS_AUTH_RETRY_AFTER_FAIL_STEP2_MS = 120000 |
| Second retry. | |
| static constexpr uint32_t | STATUS_AUTH_RETRY_AFTER_FAIL_MAX_MS = 300000 |
| Steady-state after repeated auth failures. | |
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. | |
| using esphome::home_io_control::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.
Wired to a lambda in the hub's initializer list, which has protected access.
Definition at line 39 of file hub_hooks.h.
| 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 177 of file radio_soft_phy_driver_base.h.
| 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 23 of file device_registry.h.
| 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.
| name | Timer name; a later call with the same name replaces the pending callback. |
| delay_ms | Delay before the callback runs. |
| callback | Work to run in loop() context after the delay. |
Definition at line 27 of file hub_hooks.h.
| 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.
| 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.
| frame | Frame to serialize and transmit. |
| freq | RF channel frequency in Hz. |
| preamble | Preamble length in bytes. |
Definition at line 36 of file oneway_transmitter.h.
| 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.
| parsed | Points 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). |
| packet | The raw packet, for length/frequency logging. |
Keep captures to a few pointers: small callables avoid std::function's heap fallback on the implementations this project builds against.
Definition at line 143 of file hub_exchange.h.
| 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 964 of file management_actions.cpp.
| 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_().
| frame | Frame to serialize and transmit. |
| freq_hz | RF channel frequency in Hz. |
| preamble | Preamble length in bytes. |
Definition at line 34 of file hub_hooks.h.
|
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.
Definition at line 96 of file proto_codecs.h.
|
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 303 of file proto_device_model.h.
|
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.
Definition at line 155 of file lr1121_firmware_decisions.h.
|
strong |
Whether a target firmware version is known to work with a given bootloader version.
Definition at line 111 of file lr1121_firmware_decisions.h.
|
strong |
Whether the three-stage bootloader-rewrite sequence (ADR 0021) is applicable, and if not, why.
Definition at line 173 of file lr1121_firmware_decisions.h.
|
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). |
Definition at line 270 of file proto_device_model.h.
|
strong |
High‑level capability class derived from DeviceType.
Definition at line 73 of file proto_device_model.h.
|
strong |
Validation result for outbound device-name writes.
Definition at line 35 of file proto_codecs.h.
|
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.
Definition at line 28 of file proto_device_model.h.
|
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 102 of file tuning_config.h.
|
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 157 of file tuning_config.h.
|
strong |
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.
Definition at line 129 of file tuning_config.h.
|
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 156 of file oneway_controller.h.
|
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.
Definition at line 61 of file exchange_engine.h.
|
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.
Definition at line 74 of file radio_interface.h.
|
strong |
Outcome of lr1121_flash_decision().
Definition at line 235 of file lr1121_firmware_decisions.h.
|
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 32 of file proto_heating_profiles.h.
|
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 112 of file proto_heating_profiles.h.
|
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. |
Definition at line 38 of file proto_heating.h.
|
strong |
HVAC modes a profile can offer.
| Enumerator | |
|---|---|
| OFF | Heater off. |
| HEAT | Heating on. |
| AUTO | The device decides. |
Definition at line 39 of file proto_heating_profiles.h.
|
strong |
Operating modes of the mode register (HeatingFunction::SET_MODE).
Definition at line 52 of file proto_heating.h.
|
strong |
Presets a profile can offer. CUSTOM entries carry their own name.
| Enumerator | |
|---|---|
| HOME | Someone is in the room. |
| 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 46 of file proto_heating_profiles.h.
|
strong |
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 62 of file proto_heating.h.
|
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_().
Definition at line 150 of file tuning_config.h.
|
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 463 of file proto_constants.h.
|
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 126 of file hub_exchange.h.
|
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.
Definition at line 110 of file hub_exchange.h.
|
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.
Definition at line 71 of file tuning_config.h.
|
strong |
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 274 of file proto_codecs.h.
|
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 (the reference implementation's own _p0x30 struct omits the MAC field entirely, 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).
Definition at line 250 of file proto_codecs.h.
|
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.
Definition at line 45 of file oneway_controller.h.
|
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 57 of file oneway_controller.h.
|
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.
|
strong |
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.
|
strong |
Discriminator for entries in the pending-operation deque.
Definition at line 27 of file operation_queue.h.
|
strong |
What the hub's latest minimum-limitation read says about a rain-protection limit.
Definition at line 286 of file proto_device_model.h.
|
strong |
What the caller wants done with the frame a listen just received.
Definition at line 119 of file hub_exchange.h.
|
strong |
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 123 of file roll_call.h.
|
strong |
How a responder line shows the key it answered under.
Definition at line 192 of file roll_call.h.
|
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 140 of file roll_call.h.
|
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 88 of file tuning_config.h.
|
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.
Definition at line 38 of file tuning_config.h.
|
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.
Definition at line 57 of file tuning_config.h.
|
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 81 of file proto_timing.h.
| 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.
| level | Priority level value (0–7). |
Definition at line 217 of file proto_constants.cpp.
| 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.
| responders | Accumulated array, appended to in place. |
| count | In: entries already present. Out: updated count. |
| capacity | Maximum entries responders can hold. |
| frame | Reply frame to decode and store. |
| rssi_dbm | RSSI of that reply. |
| key_slot | The key slot of the sweep the reply arrived in. |
| entry | Out: 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. |
Definition at line 17 of file roll_call.cpp.
| const char * esphome::home_io_control::address_class_name | ( | AddressClass | address_class | ) |
Get a human-readable name for an address classification.
| address_class | Classification returned by classify_address(). |
Definition at line 220 of file proto_codecs.cpp.
|
static |
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.
| response | Frame whose cmd is CMD_ERROR_RESP. |
| result | Result to populate. |
Definition at line 361 of file management_actions.cpp.
| 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.
| att_class | ATT class (0–3) extracted from the Multi Information Byte. |
Definition at line 152 of file proto_constants.cpp.
| 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.
Definition at line 89 of file tuning_config.cpp.
| 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.
| table | Bandwidth option table. |
| n | Number of entries in table. |
| reg | Register byte to look up. |
| fallback | kHz value returned when reg is not in the table. |
Definition at line 75 of file tuning_config.cpp.
| 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 83 of file tuning_config.cpp.
| const char * esphome::home_io_control::battery_state_name | ( | BatteryState | state | ) |
Get the name a battery level is published under.
| state | The level to name. |
Definition at line 46 of file proto_device_model.cpp.
| std::string esphome::home_io_control::bool_select_get | ( | const TuningConfig & | t | ) |
| bool esphome::home_io_control::bool_select_set | ( | TuningConfig & | t, |
| const std::string & | v ) |
|
static |
| 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).
| addr | Three-byte broadcast address. |
Definition at line 236 of file proto_codecs.cpp.
| std::string esphome::home_io_control::build_device_yaml_snippet | ( | DeviceType | type, |
| uint8_t | subtype, | ||
| const std::string & | device_id, | ||
| bool | metadata_complete, | ||
| bool | inverted, | ||
| 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, subtype, and inverted 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 an inverted cover and dimmable: true for a brightness-only light subtype. 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.
| type | Decoded device type. |
| subtype | Decoded device subtype; only used when metadata_complete is true. |
| device_id | Hex device ID string (e.g. "38B4A1"). |
| metadata_complete | Whether the discovery response included type/subtype metadata. |
| inverted | Whether the device's open/close positions are swapped; only used when metadata_complete is true and type is a cover. |
| low_power | Whether the device self-reported a low-power / duty-cycled class; emits low_power: true when set, in both snippet shapes. |
| system_key_hex | The 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. |
Definition at line 351 of file proto_device_model.cpp.
|
static |
Build a temperature / alias execute frame (0x00) carrying 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 388 of file proto_commands.cpp.
| 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.
| info | The device that sent the key. |
| state | The devices found under the hub key. |
| registry | Device registry before any registration: a responder the YAML declares is known; one an earlier pairing in this boot added is not (DeviceRegistry::get_declared()). |
Definition at line 245 of file roll_call.cpp.
| 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.
| info | The device that sent the key. |
| state | The devices found under that key (roll-call, or the direct read of the next address). |
| registry | Device 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 264 of file roll_call.cpp.
|
static |
Build the final report: known/unknown grouping, header, the selection NOTE, and the truncation NOTE.
Definition at line 1002 of file management_actions.cpp.
|
inline |
| 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()).
| origin | The challenged frame. |
| challenge | 6-byte challenge from the 0x3C. |
| key | 16-byte key. |
| hmac | Output: 6-byte MAC. |
Definition at line 968 of file proto_commands.cpp.
| 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.
| addr | Three-byte node address to classify. |
Definition at line 196 of file proto_codecs.cpp.
|
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.
| dev | Device to clear. |
Definition at line 542 of file proto_device_model.h.
|
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 969 of file management_actions.cpp.
|
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.
| cmd | Frame command byte. |
Definition at line 46 of file redaction.h.
| 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".
| cmd | Command byte from the frame header. |
Definition at line 10 of file proto_constants.cpp.
| const char * esphome::home_io_control::command_result_description | ( | uint8_t | result | ) |
Return a human-readable explanation for a CMD_ERROR_RESP result code.
| result | Result byte from CMD_ERROR_RESP data[0]. |
Definition at line 373 of file proto_constants.cpp.
| 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.
| result | Result byte from CMD_ERROR_RESP data[0]. |
Definition at line 240 of file proto_constants.cpp.
|
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.
| buf | Buffer to scan. |
| len | Length of buf in bytes. |
| system_key | Pointer to the AES_KEY_SIZE-byte system key. |
Definition at line 59 of file redaction.h.
|
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 988 of file management_actions.cpp.
| uint8_t esphome::home_io_control::count_enabled_passes | ( | ScanPowerClasses | selection | ) |
How many of ROLL_CALL_PASSES selection actually calls.
| selection | The scan_power_classes tunable. |
Definition at line 53 of file roll_call.cpp.
| const char * esphome::home_io_control::cover_command_name | ( | CoverCommand | cmd | ) |
Get a human-readable name for a CoverCommand.
| cmd | The cover command to name. |
Definition at line 65 of file proto_device_model.cpp.
| 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.
| data | Pointer to data bytes. |
| len | Number of bytes. |
Definition at line 60 of file proto_frame.cpp.
| 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.
| f | IoFrame to populate. |
| src | Our 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_type | Device class to address; encoded via encode_broadcast_address(). Only devices of this class currently in association mode react. |
| manufacturer | Manufacturer ID byte to advertise (man_id); echoed back verbatim by anything that later decodes this frame with decode_1w_add_controller(). |
| sequence | 2-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_key | 16-byte key this identity registers itself with — normally the hub's own system_key, wrapped here for transmission, never sent in the clear. |
| with_mac | True 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. |
Definition at line 468 of file proto_commands.cpp.
| 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. 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.
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. The reference remote's RemoteButton::ForceOpen emits main=0x64/mod=0x00 — the same bytes this project labels POS_FORCE_OPEN — so it would "match the reference implementation byte-for-byte" the same way VENT does above. But matching the reference is not evidence it works: 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 and by the reference implementation's own 1W-remote span (toAdd = 6 + 1). 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.
| f | IoFrame to populate. |
| src | Our 3-byte controller node address (the 1W controller identity's node_id). |
| target_type | Device class to address; encoded via encode_broadcast_address(). |
| cmd | Named command to execute (STOP, FAVORITE, or VENT). CoverCommand::FORCE_OPEN returns false — see the |
| sequence | 2-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_key | 16-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. |
| acei | ACEI byte for payload[1] — see create_1w_execute_position(). |
| broadcast_all | Address 00 00 3F instead of the typed class — see create_1w_execute_position(). |
Definition at line 442 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| src | Our 3-byte controller node address (the 1W controller identity's node_id). |
| target_type | Device class to address; encoded via encode_broadcast_address(). |
| position | Desired position 0–100 (0=fully open, 100=fully closed). |
| sequence | 2-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_key | 16-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. |
| acei | ACEI 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_all | When 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). |
Definition at line 427 of file proto_commands.cpp.
| 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 (per the documented 0x39 → 0x30 flow, linklayer.md:396) also the prelude OneWayTransmitter::send_enrollment() fires immediately before it.
See proto_commands.h for the full contract, including the
Declared payload (9 bytes), shape matching the reference _p0x2e struct: 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.
| f | IoFrame to populate. |
| src | Our 3-byte controller node address (the 1W controller identity's node_id). |
| target_type | Device class to address; encoded via encode_broadcast_address(). |
| sequence | 2-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_key | 16-byte key held by the identity being removed — the same key create_1w_execute_command()/create_1w_add_controller() would use for this identity. |
Definition at line 514 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| dst | Target device node ID (device we're challenging). |
| src | Controller node ID. |
Definition at line 937 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| dst | Target device node ID (device we're challenging). |
| src | Our own node ID. |
| challenge | Caller-supplied 6-byte challenge (e.g. from crypto::generate_challenge()). |
Definition at line 931 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| dst | The foreign hub's node ID (from the inbound 0x31's src). |
| src | Our advertised (throwaway) node ID. |
| challenge | Caller-supplied 6-byte challenge, retained for the later 0x32 decrypt. |
Definition at line 946 of file proto_commands.cpp.
|
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 916 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| dst | Target device node ID. |
| src | Controller node ID. |
| challenge | 6‑byte challenge from the device. |
| origin | Original request frame that triggered the challenge. |
| key | System key (16 bytes). |
Definition at line 985 of file proto_commands.cpp.
| 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 create_discover_resp()'s own Multi Information Byte, which as of KEY_EXTRACTION_DISCOVER_RESP_FLAGS (proto_commands.cpp) deliberately advertises POWER_SAVE_LOW_POWER for this responder's own (different) power-save self-description — 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.
| f | IoFrame to populate. |
| dst | The hub's node ID (from the inbound 0x3C's src). |
| src | Our advertised (throwaway) node ID. |
| challenge | 6-byte challenge from the hub's 0x3C. |
| origin | Our own preceding CMD_NODE_VERIFY_RESP (0x37) frame — its cmd+data is the transcript. |
| key | System key (16 bytes). |
Definition at line 1015 of file proto_commands.cpp.
|
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 954 of file proto_commands.cpp.
|
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 826 of file proto_commands.cpp.
|
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 809 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller node ID. |
Definition at line 716 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID. |
| dst | The freshly-discovered device's 3-byte node ID (from its 0x29's src). |
| low_power | Set 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). |
| ack | Set 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. |
Definition at line 842 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Our advertised (throwaway) node ID. |
| dst | Destination node ID (the hub that sent the discovery confirm). |
Definition at line 848 of file proto_commands.cpp.
| 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 ) |
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.
| f | IoFrame to populate. |
| own | Our advertised (throwaway) node ID — used as both src and the backbone address. |
| dst | Destination node ID (the discovering hub's real node ID, from its 0x28's src). |
| type | Device type to advertise. |
| subtype | Device subtype to advertise. |
| manufacturer_id | Manufacturer ID to advertise (see MANUFACTURER_* in proto_constants.h). |
Definition at line 778 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID. |
| command | Discovery command code (0x28, 0x2A, or 0x2E). |
| dst | Destination node ID (broadcast or explicit). |
| low_power | True to set the LOW_POWER flag in CTRL1. |
| ack_capable | True 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_enabled | True when the optional payload byte is enabled. |
| payload | Optional payload byte (only used when command requires a payload). |
| system_key | 16-byte system key; only used for 0x2A HMAC computation. |
Definition at line 731 of file proto_commands.cpp.
| 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, FAVORITE, or VENT.
FORCE_OPEN is deliberately not handled here — unlike these three, 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.
Each maps to a specific wire encoding in the 6-byte special CMD_EXECUTE payload:
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 three, 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.
| f | IoFrame to populate. |
| own | Controller's 3‑byte node ID (source address). |
| dst | Target device's 3‑byte node ID (destination address). |
| low_power | True if target is battery/solar‑powered (sets CTRL1_LOW_POWER). |
| cmd | Named command to execute (STOP, FAVORITE, or VENT). |
| silent | Use the silent (slower) travel profile. Honoured for FAVORITE only — STOP has no travel speed, and nothing has been captured for VENT. |
Definition at line 312 of file proto_commands.cpp.
| 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 a main-parameter-only EXECUTE (0x00) that sets a heater's heating level.
Same 6-byte shape as create_execute_main_parameter(), with the heating level in the main parameter (high byte HEATING_LEVEL_MP_PREFIX, low byte the level; see proto_heating_profiles.h). Experimental: unconfirmed on hardware until a capture or a reporter's heater confirms it.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID (source address). |
| dst | Target device's 3-byte node ID (destination address). |
| low_power | True if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER). |
| level | Heating level low byte (heating_level::*). |
Definition at line 419 of file proto_commands.cpp.
| 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_heating_level(); the caller encodes the value (encode_temperature_main_parameter(), or heating_prefixed_main_parameter() for an alias). Experimental: unconfirmed on hardware.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID (source address). |
| dst | Target device's 3-byte node ID (destination address). |
| low_power | True if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER). |
| main_parameter | The 16-bit main parameter. |
Definition at line 379 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID (source address). |
| dst | Target device's 3-byte node ID (destination address). |
| low_power | True if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER). |
| main_parameter | The 16-bit main parameter. |
| fp16 | FP16 value. |
Definition at line 413 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID (source address). |
| dst | Target device's 3-byte node ID (destination address). |
| low_power | True if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER). |
| main_parameter | The 16-bit main parameter. |
| mode_flags | FP9 value. |
Definition at line 407 of file proto_commands.cpp.
| 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). See device_takes_main_parameter_only().
| f | IoFrame to populate. |
| own | Controller's 3‑byte node ID (source address). |
| dst | Target device's 3‑byte node ID (destination address). |
| low_power | True if target is battery/solar‑powered (sets CTRL1_LOW_POWER). |
| position | Target position 0..100 (IO scale: 0 = open, 100 = closed). |
| force_open_priority | True to send at the force-open ACEI (see create_force_open()) instead of the normal one. |
Definition at line 369 of file proto_commands.cpp.
| 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).
| f | IoFrame to populate. |
| own | Controller's 3‑byte node ID (source address). |
| dst | Target device's 3‑byte node ID (destination address). |
| low_power | True if target is battery/solar‑powered (sets CTRL1_LOW_POWER). |
| position | Desired position 0–100 (0=fully open, 100=fully closed). |
| silent | Send 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 295 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller node ID. |
| dst | Target device node ID. |
| low_power | True if target is battery/solar‑powered (sets CTRL1_LOW_POWER). |
| position | Desired position 0–100 (open→closed). |
| tilt_percent | 0 = fully closed, 100 = fully open. |
Definition at line 623 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller node ID. |
| dst | Target device node ID. |
| low_power | True if target is battery/solar‑powered (sets CTRL1_LOW_POWER). |
| tilt_percent | 0 = fully closed, 100 = fully open. |
Definition at line 604 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3‑byte node ID (source address). |
| dst | Target device's 3‑byte node ID (destination address). |
| low_power | True if target is battery/solar‑powered (sets CTRL1_LOW_POWER). |
| open_position | The 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. |
Definition at line 360 of file proto_commands.cpp.
| 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).
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID. |
| dst | Target device's 3-byte node ID. |
| low_power | True if target is battery/solar-powered (sets CTRL1_LOW_POWER). |
Definition at line 558 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID. |
| dst | Target device's 3-byte node ID. |
| low_power | True if target is battery/solar-powered (sets CTRL1_LOW_POWER). |
Definition at line 563 of file proto_commands.cpp.
| 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).
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID. |
| dst | Target device's 3-byte node ID. |
| low_power | True if target is battery/solar-powered (sets CTRL1_LOW_POWER). |
Definition at line 568 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID. |
| dst | Target device's 3-byte node ID. |
| low_power | True if target is battery/solar-powered (sets CTRL1_LOW_POWER). |
Definition at line 552 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3‑byte node ID. |
| dst | Target device's 3‑byte node ID. |
| low_power | True if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER). |
Definition at line 548 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID. |
| dst | Target device's 3-byte node ID. |
| low_power | True if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER). |
| selector | Extended-status selector byte (data[1]). |
| block | Selector-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_id | CMD_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. |
Definition at line 652 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller node ID. |
| dst | Target device node ID. |
| low_power | True if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER). |
Definition at line 685 of file proto_commands.cpp.
| 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).
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID (source address). |
| dst | Target device's 3-byte node ID (destination address). |
| low_power | True if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER). |
Definition at line 581 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Our advertised (throwaway) node ID. |
| dst | Destination node ID (the hub that sent the key transfer). |
Definition at line 818 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller node ID. |
| dst | Discovered device node ID. |
Definition at line 892 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| old_frame | The key‑init frame (used to derive the encryption IV). |
| dst | Target device node ID. |
| src | Controller node ID. |
| key | The 16‑byte system key to transfer. |
| challenge | 6‑byte challenge received from device in its 0x3C response. |
Definition at line 899 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller node ID. |
| dst | Discovered device node ID. |
| challenge | 6 random bytes, kept by the caller for the decrypt. |
Definition at line 862 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID. |
| dst | Target device's 3-byte node ID. |
| type | Which resulting limit to read. |
| low_power | True if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER). |
Definition at line 675 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Our advertised (throwaway) node ID — used as both src and the payload. |
| dst | Destination node ID (the hub that sent the node verification request, from its 0x36's src). |
Definition at line 1005 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID. |
| dst | Target device's 3-byte node ID. |
| modifier | The POS_FAVORITE/POS_VENT_MODIFIER-family selector byte to read back. |
| long_form | True 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_power | True if target is battery/solar-powered (sets CTRL1_LOW_POWER). |
Definition at line 700 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID. |
| dst | Target device's 3-byte node ID. |
| low_power | True 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_id | Private function ID (data[0] of the CMD_PRIVATE payload). |
| sub_index | Second 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. |
Definition at line 538 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller node ID. |
| dst | Target device node ID. |
Definition at line 1034 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID. |
| dst | Target device's 3-byte node ID. |
| low_power | True if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER). |
| payload | Pre-validated fixed payload produced by encode_device_name_payload(). |
Definition at line 572 of file proto_commands.cpp.
| bool esphome::home_io_control::create_status_mp_fp_read | ( | IoFrame & | f, |
| const uint8_t * | own, | ||
| const uint8_t * | dst, | ||
| bool | low_power ) |
Build the KLF-style status read of the main parameter plus FP1-FP7 (see the header for the evidence).
Build the status read of the main parameter plus functional parameters FP1-FP7: an unauthenticated CMD_PRIVATE (0x03) request with function ID 0x01, FPI1 mask 0xFE, one "current value" status type per parameter and FPI2 0x00 — 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).
Read-only and unauthenticated.
The reply layout is not decoded here.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID. |
| dst | Target device's 3-byte node ID. |
| low_power | True if the target is a low-power / duty-cycled device (sets CTRL1_LOW_POWER). |
Definition at line 663 of file proto_commands.cpp.
| 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.
| f | IoFrame to populate. |
| own | Controller node ID. |
| dst | Device node ID that sent the update. |
Definition at line 1022 of file proto_commands.cpp.
| 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 per the iohomecontrol reference implementation's forgePacket() (start=1, end=0) and the atlantic_thermor_exchange_write_private_param.yaml capture (frame 1: 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 the real Atlantic Thermor exchange capture tests/corpus/captures/exchange/atlantic_thermor_exchange_write_private_param.yaml (frame 1: start=true, end=false). The device answers with CMD_WRITE_PRIVATE_ACK (0x21) after an authenticated 0x3C/0x3D leg the exchange engine handles transparently.
| f | IoFrame to populate. |
| own | Controller's 3-byte node ID (source address). |
| dst | Target device's 3-byte node ID (destination address). |
| low_power | True 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. |
| payload | Payload bytes (from encode_heating_payload(), encode_heating_read() or encode_heating_write()). |
| payload_len | Payload length in bytes. Rejected if 0 or > FRAME_MAX_DATA_SIZE. |
Definition at line 593 of file proto_commands.cpp.
| 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:
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 — the reference implementation's own _p0x30 struct omits the MAC field entirely — 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.
| frame | Parsed IoFrame, expected to be a CMD_ONEWAY_ADD_CONTROLLER 1W frame. |
| out | Output: recovered controller identity. Only meaningfully populated when the return value is NONE; left default-constructed (zeroed) otherwise. |
Definition at line 402 of file proto_codecs.cpp.
| 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.
| frame | Parsed IoFrame with CTRL0_PROTOCOL_1W set. |
Definition at line 333 of file proto_codecs.cpp.
| 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:
| main0 | First main byte (position or special code). |
| main1 | Second main byte (modifier). |
| out | Buffer to write the decoded string into (e.g., "CLOSE", "position 75%"). |
| out_size | Size of the output buffer. |
Definition at line 260 of file proto_codecs.cpp.
| 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).
| status_byte | data[1] of the payload. |
Definition at line 60 of file proto_device_model.cpp.
|
inline |
Render decoded CTRL1 flags into a short annotation string.
| ctrl1 | The CTRL1 byte from a frame. |
| out | Buffer to write into (at least FRAME_LOG_FLAGS_BUFFER_SIZE bytes). |
| out_size | Size of the output buffer. |
Definition at line 35 of file log_frame.h.
| 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.
| data | Raw payload pointer from CMD_GET_NAME_RESP. |
| len | Raw payload length in bytes. |
Definition at line 88 of file proto_codecs.cpp.
| 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/inverted/position/target/is_stopped and device_id exactly as a discovery reply implies: type/subtype and inversion 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.
| frame | Parsed discovery-response frame. |
| device | Output: device record populated from the frame. |
| device_id | Output: hex device ID string derived from frame.src. |
Definition at line 360 of file proto_codecs.cpp.
| bool esphome::home_io_control::decode_heating_level | ( | uint16_t | main_parameter, |
| uint8_t & | level ) |
Read a heating level out of a main parameter.
| main_parameter | The 16-bit value from a status reply. | |
| [out] | level | Level low byte. |
Definition at line 288 of file proto_heating_profiles.cpp.
| 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 raw -1 on the room-temperature register is a sensor fault: it decodes as NaN with true returned, because the heater did answer.
| reply | The device's reply frame. | |
| fn | Register the request addressed. | |
| [out] | value | Decoded value; untouched when false is returned. |
Definition at line 197 of file proto_heating.cpp.
| 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.
| frame | A CMD_PRIVATE_RESP or CMD_STATUS_UPDATE frame. |
| base | PRIVATE_RESPONSE_LAST_COMMAND_OFFSET or STATUS_UPDATE_LAST_COMMAND_OFFSET. |
Definition at line 455 of file proto_codecs.cpp.
| 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]).
| f | A parsed frame. |
| type | Receives the limit the request asks for. |
| parameter_id | Receives the parameter id (data[1]). |
Definition at line 477 of file proto_codecs.cpp.
| bool esphome::home_io_control::decode_limitation_status | ( | const IoFrame & | f, |
| LimitationStatus & | out ) |
Decode a limitation reply (assumed layout, see LimitationStatus).
| f | A parsed frame. |
| out | Receives the decoded fields on success. |
Definition at line 467 of file proto_codecs.cpp.
| 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.
| type_subtype | Second metadata byte containing subtype in bits [5:0]. |
Definition at line 85 of file proto_device_model.cpp.
| 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.
| type_msb | First metadata byte. |
| type_subtype | Second metadata byte containing the remaining type bits and subtype. |
Definition at line 80 of file proto_device_model.cpp.
| 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.
| target_raw | 16‑bit raw target value. |
| current_raw | 16‑bit raw current value. |
| is_stopped | True if device reports stopped. |
| target | Output target position (0–100 or UNKNOWN_POSITION). |
| position | Output current position (0–100 or UNKNOWN_POSITION). |
Definition at line 95 of file proto_device_model.cpp.
| 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()).
| main_parameter | The 16-bit value from a status reply. | |
| min_c | Lower end of the device's encoding range. | |
| max_c | Upper end of the device's encoding range; must exceed min_c. | |
| [out] | celsius | The temperature. |
Definition at line 279 of file proto_heating_profiles.cpp.
| float esphome::home_io_control::decode_tilt_report | ( | uint16_t | tilt_raw | ) |
Decode tilt angle from raw 16‑bit value.
| tilt_raw | Raw tilt value from status frame. |
Definition at line 128 of file proto_device_model.cpp.
| 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.
| raw | Raw bytes from the radio buffer. |
| raw_len | Number of raw bytes available. |
| bit_offset | Initial bit position to start decoding (probe offset). |
| decoded | Output buffer for decoded bytes. |
| decoded_max_len | Capacity of decoded buffer. |
Definition at line 45 of file radio_soft_phy.cpp.
| bool esphome::home_io_control::default_inverted_for_type | ( | DeviceType | type | ) |
Determine whether a device type has inverted position mapping by default.
| type | Device type. |
Definition at line 32 of file proto_device_model.cpp.
| DeviceCapabilityClass esphome::home_io_control::device_capability_class | ( | DeviceType | type | ) |
Map a raw IO‑Homecontrol type to the closest ESPHome/Home Assistant entity family.
| type | Raw device type. |
Definition at line 149 of file proto_device_model.cpp.
| 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).
| type | Node type. |
| subtype | Subtype. |
Definition at line 196 of file proto_device_types.cpp.
| const char * esphome::home_io_control::device_capability_class_name | ( | DeviceType | type | ) |
Get a human‑readable name for a capability class.
| type | Device type (unused, kept for signature compatibility). |
Definition at line 151 of file proto_device_model.cpp.
| 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.
| type | Device type. |
| subtype | Device subtype. |
Definition at line 192 of file proto_device_model.cpp.
| bool esphome::home_io_control::device_has_platform | ( | DeviceType | type, |
| uint8_t | subtype ) |
Can an ESPHome platform drive this (type, subtype) today?
| type | Node type. |
| subtype | Subtype. |
Definition at line 166 of file proto_device_types.cpp.
| bool esphome::home_io_control::device_light_is_brightness_only | ( | DeviceType | type, |
| uint8_t | subtype ) |
Is this light limited to brightness (no colour, no colour temperature)?
| type | Node type. |
| subtype | Subtype. |
Definition at line 201 of file proto_device_types.cpp.
| const char * esphome::home_io_control::device_name_validation_error_description | ( | DeviceNameValidationError | error | ) |
Return a human-readable explanation for a device-name validation result.
| error | Validation result. |
Definition at line 179 of file proto_codecs.cpp.
| const char * esphome::home_io_control::device_name_validation_error_name | ( | DeviceNameValidationError | error | ) |
Return a stable symbolic name for a device-name validation result.
| error | Validation result. |
Definition at line 162 of file proto_codecs.cpp.
| const char * esphome::home_io_control::device_operation_profile_name | ( | DeviceType | type | ) |
Human‑readable operation profile name for a device type.
Used for logging and diagnostics.
| type | Device type. |
Definition at line 306 of file proto_device_model.cpp.
| const char * esphome::home_io_control::device_platform_name | ( | DeviceType | type, |
| uint8_t | subtype ) |
Name of the ESPHome platform that drives this (type, subtype).
| type | Node type. |
| subtype | Subtype. |
Definition at line 171 of file proto_device_types.cpp.
|
inline |
A device's smoothed RSSI in whole dBm.
| dev | Device record to read. |
Definition at line 555 of file proto_device_model.h.
| bool esphome::home_io_control::device_supports_binary_control | ( | DeviceType | type | ) |
Does this device type support binary on/off control?
| type | Device type. |
Definition at line 179 of file proto_device_model.cpp.
| 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().
| type | Device type. |
| subtype | Device subtype. |
Definition at line 200 of file proto_device_model.cpp.
| bool esphome::home_io_control::device_supports_heat_level_control | ( | DeviceType | type | ) |
Does this device take a heat level as its position (exterior heating)?
| type | Device type. |
Definition at line 188 of file proto_device_model.cpp.
| bool esphome::home_io_control::device_supports_lock_control | ( | DeviceType | type | ) |
Does this device type support binary lock/unlock control via execute commands?
| type | Device type. |
Definition at line 184 of file proto_device_model.cpp.
| bool esphome::home_io_control::device_supports_position_control | ( | DeviceType | type | ) |
Does this device type support precise position control (0–100)?
| type | Device type. |
Definition at line 175 of file proto_device_model.cpp.
| bool esphome::home_io_control::device_supports_status_requests | ( | DeviceType | type | ) |
Does this device type support status request commands (0x03)?
| type | Device type. |
Definition at line 204 of file proto_device_model.cpp.
| bool esphome::home_io_control::device_supports_tilt | ( | DeviceType | type | ) |
Does this device type support tilt (slat angle) control?
| type | Device type. |
Definition at line 209 of file proto_device_model.cpp.
| bool esphome::home_io_control::device_supports_vent | ( | DeviceType | type | ) |
Does this device type support the ventilation position command?
The ventilation command moves window-type actuators to a predefined partially-open position suitable for air exchange without fully opening.
| type | Device type. |
Definition at line 261 of file proto_device_model.cpp.
| bool esphome::home_io_control::device_takes_main_parameter_only | ( | DeviceType | type, |
| uint8_t | subtype ) |
Does this device take movement commands as the main parameter only?
True for every garage opener: a Wisniowski / Somfy MOTO 1000 io refuses the functional-parameter block of the regular position execute (the travel-speed profile roller motors use) and the "favourite" alias with RESULT_PARAMETER_UNKNOWN, and accepts a frame without them (issue #163). True for subtype 58 of any node type too: the public KLF 200 API specification defines it as the position-only variant. Other gate and rolling-door openers may behave the same but have no field report yet, so they keep the regular frame.
| type | Device type. |
| subtype | Device subtype. |
Definition at line 254 of file proto_device_model.cpp.
| const char * esphome::home_io_control::device_type_name | ( | DeviceType | type | ) |
Convert a DeviceType to a lowercase string identifier.
| type | Device type enum. |
Definition at line 139 of file proto_device_model.cpp.
| bool esphome::home_io_control::device_uses_heating_levels | ( | DeviceType | type, |
| uint8_t | subtype ) |
Does a climate entity drive this device with heating levels?
| type | Node type. |
| subtype | Subtype. |
Definition at line 261 of file proto_heating_profiles.cpp.
| 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.
| type | Node type. |
| subtype | Subtype. |
Definition at line 256 of file proto_heating_profiles.cpp.
| 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?
| type | Node type. |
| subtype | Subtype. |
Definition at line 266 of file proto_heating_profiles.cpp.
| 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.
| value | String to parse. |
Definition at line 270 of file tuning_config.cpp.
| std::string esphome::home_io_control::discover_confirm_mode_to_string | ( | DiscoverConfirmMode | value | ) |
Format a DiscoverConfirmMode value for YAML/logs.
| value | Selection to format. |
Definition at line 258 of file tuning_config.cpp.
|
inline |
Extract the ATT class field from a discovery response's Multi Information Byte.
| flags | Multi Information Byte (data[DISCOVERY_RESP_FLAGS_OFFSET]). |
Definition at line 727 of file proto_constants.h.
| 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.
| value | String to parse. |
Definition at line 158 of file tuning_config.cpp.
| std::string esphome::home_io_control::discovery_command_to_string | ( | DiscoveryCommand | cmd | ) |
Format a discovery command enum for YAML/logs.
| cmd | Discovery command enum. |
Definition at line 171 of file tuning_config.cpp.
| 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.
| commands | Ordered list of discovery commands. |
Definition at line 177 of file tuning_config.cpp.
| std::string esphome::home_io_control::discovery_commands_to_string | ( | const std::vector< DiscoveryCommand > & | commands | ) |
Format the ordered discovery command list for logs.
| commands | Ordered list of discovery commands. |
Definition at line 187 of file tuning_config.cpp.
| 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.
| destination_auto | Whether automatic destination mapping is used. |
| destination | Explicit 3-byte destination when auto is false. |
Definition at line 208 of file tuning_config.cpp.
| 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.
| value | Option string to parse. |
Definition at line 290 of file tuning_config.cpp.
| std::string esphome::home_io_control::discovery_listen_channels_to_string | ( | DiscoveryListenChannels | value | ) |
Render a DiscoveryListenChannels as its YAML/select option string.
| value | Selection to render. |
Definition at line 280 of file tuning_config.cpp.
| std::string esphome::home_io_control::discovery_payload_to_string | ( | bool | payload_enabled, |
| uint8_t | payload ) |
Format a payload option for YAML/logs.
| payload_enabled | Whether the optional payload is enabled. |
| payload | The payload byte when enabled. |
Definition at line 216 of file tuning_config.cpp.
|
inline |
Extract the power save mode field from a discovery response's Multi Information Byte.
| flags | Multi Information Byte (data[DISCOVERY_RESP_FLAGS_OFFSET]). |
Definition at line 734 of file proto_constants.h.
|
inline |
The device classes this identity's 0x30 enrollment sweep will actually target.
| identity | The controller identity. |
Definition at line 214 of file oneway_controller.h.
|
inline |
The ACEI byte a given identity will put on air for a 1W EXECUTE frame.
| identity | The controller identity. |
Definition at line 225 of file oneway_controller.h.
|
inline |
Whether a consumer should treat the device as at rest, prediction first.
| dev | Device record to read. |
Definition at line 577 of file proto_device_model.h.
|
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).
| identity | The controller identity. |
Definition at line 242 of file oneway_controller.h.
|
inline |
The main-position target a consumer should act on: the prediction when one stands, otherwise the device's own last reported target.
| dev | Device record to read. |
Definition at line 563 of file proto_device_model.h.
|
inline |
The slat angle a consumer should act on, prediction first.
| dev | Device record to read. |
Definition at line 570 of file proto_device_model.h.
| 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.
| type | Device class to address. |
| out | Output: 3-byte typed-broadcast destination address. |
Definition at line 251 of file proto_codecs.cpp.
| 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.
| name | User-supplied UTF-8 device name. |
| payload | Output buffer for the fixed write payload (16 bytes, zero-padded on success). |
| normalized_name | Output normalized UTF-8 name used for later verification/logging. |
Definition at line 107 of file proto_codecs.cpp.
| 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().
| fn | Function to encode. |
| value | Function-specific value (ignored for a read). |
| out | Output buffer of HEATING_PAYLOAD_MAX_SIZE bytes. |
Definition at line 190 of file proto_heating.cpp.
| 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.
| fn | Register to read. |
| out | Output buffer of HEATING_PAYLOAD_MAX_SIZE bytes. |
Definition at line 130 of file proto_heating.cpp.
| 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:
| fn | Register to write. |
| value | Register-specific value (see above). |
| out | Output buffer of HEATING_PAYLOAD_MAX_SIZE bytes. |
Definition at line 138 of file proto_heating.cpp.
| 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().
| type | Device type to encode. |
| subtype | Manufacturer-specific subtype; only bits [5:0] are used. |
| type_msb | Output: first metadata byte. Only the low ten bits of type fit the wire field; higher bits are dropped. |
| type_subtype | Output: second metadata byte (top 2 type bits + 6-bit subtype). |
Definition at line 87 of file proto_device_model.cpp.
| 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.
| celsius | Temperature in degrees Celsius; must be finite. | |
| min_c | Lower end of the device's encoding range. | |
| max_c | Upper end of the device's encoding range; must exceed min_c. | |
| [out] | main_parameter | The encoded value. |
Definition at line 271 of file proto_heating_profiles.cpp.
|
constexpr |
Result code (bits [7:3]) of a CMD_ERROR_RESP byte.
| result | Result byte from CMD_ERROR_RESP data[0]. |
Definition at line 753 of file proto_constants.h.
|
constexpr |
Execution state (bits [2:0]) of a CMD_ERROR_RESP byte.
| result | Result byte from CMD_ERROR_RESP data[0]. |
Definition at line 758 of file proto_constants.h.
|
static |
Try to find a CRC-valid IO-Homecontrol frame within a decoded UART byte stream.
| decoded | Decoded byte buffer from UART probe. |
| decoded_len | Number of decoded bytes. |
Definition at line 157 of file radio_soft_phy.cpp.
| const DeviceTypeInfo * esphome::home_io_control::find_device_type_info | ( | DeviceType | type, |
| uint8_t | subtype ) |
Look up the table row for a (type, subtype) pair.
| type | Node type. |
| subtype | Subtype; a subtype with no row of its own falls back to the type's row (subtype 0). |
Definition at line 161 of file proto_device_types.cpp.
| 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.
| type | Node type. |
| subtype | Subtype. |
Definition at line 153 of file proto_device_types.cpp.
| const HeatingProfile * esphome::home_io_control::find_heating_profile | ( | DeviceType | type, |
| uint8_t | subtype ) |
Look up the heating profile for a (type, subtype).
| type | Node type. |
| subtype | Subtype; one with no profile of its own falls back to the type's profile (subtype 0), the same rule as find_device_type_info(). |
Definition at line 247 of file proto_heating_profiles.cpp.
| 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.
| profile | The device's profile. |
| wire | Mode register value or level low byte. |
Definition at line 295 of file proto_heating_profiles.cpp.
| 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 257 of file tuning_registry.cpp.
| 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 265 of file tuning_registry.cpp.
| 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 188 of file radio_soft_phy.cpp.
| 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.
| errors | Raw device-error bitmask. |
| bits | The chip's bit → name table, in output order. |
| bit_count | Number of rows in bits. |
| buf | Caller-owned output buffer; always NUL-terminated on return. |
| buf_size | Size 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 588 of file radio_soft_phy_driver_base.cpp.
| 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.
| N | Row count of the chip's table, deduced from the array so no caller counts it by hand. |
Definition at line 172 of file radio_soft_phy_driver_base.h.
| 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.
| type | Node type. |
| subtype | Subtype. |
Definition at line 229 of file proto_device_types.cpp.
| 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.
| type | Node type. |
| subtype | Subtype. |
Definition at line 206 of file proto_device_types.cpp.
| std::string esphome::home_io_control::format_device_type_diagnostic | ( | DeviceType | type | ) |
Human-readable device type string for diagnostics, including the raw numeric value.
| type | Device type. |
Definition at line 334 of file proto_device_model.cpp.
| 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.
| type | Device type. |
Definition at line 343 of file proto_device_model.cpp.
|
static |
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 338 of file management_actions.cpp.
| 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.
| report | The attempt to describe. |
Definition at line 12 of file platform_oneway_entities.cpp.
|
inline |
Format a position float as a human‑readable string (e.g.
"50%", "unknown").
| pos | Position value (0–100 or UNKNOWN_POSITION). |
Definition at line 1329 of file hub_core.h.
| 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.
| responder | Decoded responder record. |
| device_id | Hex device ID string for this responder, rebuilt from responder.src. |
| registered | The matching registry entry, or nullptr if this responder is not known. |
| keys | Which key the responder answered under and what the hub key is. |
| display | Whether and how the key is printed. |
Definition at line 221 of file roll_call.cpp.
| 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.
| cmd | Command byte (IoFrame::cmd, or the raw byte at FRAME_CMD_OFFSET in a wire buffer). |
Definition at line 107 of file proto_frame.cpp.
| uint8_t esphome::home_io_control::frame_length | ( | const IoFrame & | f | ) |
Get total frame length from ctrl0.
| f | Parsed frame. |
Definition at line 103 of file proto_frame.cpp.
|
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 873 of file proto_commands.cpp.
|
inline |
Whether this identity's ACEI comes from an explicit execute_acei: rather than the profile.
| identity | The controller identity. |
Definition at line 233 of file oneway_controller.h.
|
inline |
Whether this identity's burst shape comes from an explicit low_power: rather than the profile.
| identity | The controller identity. |
Definition at line 249 of file oneway_controller.h.
| bool esphome::home_io_control::has_reached_target_position | ( | float | target, |
| float | position ) |
Has the device reached its target within tolerance?
| target | Target position (0–100 or UNKNOWN_POSITION). |
| position | Current position (0–100 or UNKNOWN_POSITION). |
Definition at line 121 of file proto_device_model.cpp.
| bool esphome::home_io_control::heating_function_is_readable | ( | HeatingFunction | fn | ) |
Whether the register behind a function can be read.
| fn | Function. |
Definition at line 125 of file proto_heating.cpp.
| bool esphome::home_io_control::heating_function_is_writable | ( | HeatingFunction | fn | ) |
Whether the register behind a function can be written.
| fn | Function. |
Definition at line 120 of file proto_heating.cpp.
| const char * esphome::home_io_control::heating_function_name | ( | HeatingFunction | fn | ) |
Stable lowercase name for a heating function ("power_on", "set_temperature", ...).
| fn | Function. |
Definition at line 115 of file proto_heating.cpp.
|
constexpr |
Main parameter of a heating level, as it travels in an EXECUTE and in a status reply.
| level | Level low byte (heating_level::*). |
Definition at line 240 of file proto_heating_profiles.h.
|
constexpr |
Main parameter of a prefixed value (a level or an alias).
| prefix | High byte (HEATING_LEVEL_MP_PREFIX or HEATING_ALIAS_MP_PREFIX). |
| low | Low byte. |
Definition at line 248 of file proto_heating_profiles.h.
|
static |
| 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.
| hex | Hex string (must be exactly len*2 characters). |
| out | Output buffer (at least len bytes). |
| len | Number of bytes to produce. |
Definition at line 31 of file proto_frame.cpp.
| 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).
| f | Frame to initialize. |
| is_2w | True for 2‑way (default), false for 1‑way. |
| start | Set START flag (first frame in exchange). |
| end | Set END flag (final frame in exchange). |
| low_power | Set LOW_POWER flag. |
Definition at line 70 of file proto_frame.cpp.
| 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.
| preamble_len | Preamble length in bytes. |
| frame_len | Frame length in bytes, without the CRC. |
Definition at line 235 of file radio_soft_phy.cpp.
| bool esphome::home_io_control::is_end | ( | const IoFrame & | f | ) |
Check END flag.
| f | Parsed frame. |
Definition at line 105 of file proto_frame.cpp.
| 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".
| raw | Raw type value as decoded from an address or info frame. |
Definition at line 134 of file proto_device_model.cpp.
| 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_UNKNOWN4A_REQ (0x4A, see ADR 0024) — recognizing a received 0x4A would not violate the "never transmitted" rule, but nothing in this codebase currently sends anything that would draw one, so there is no exchange for it to unblock; CMD_UNKNOWN4A_RESP (0x4B) is included because it is a plausible reply to CMD_GET_GENERAL_INFO3 (0x58), which this codebase does send. 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.
| cmd | Command byte. |
Definition at line 81 of file radio_soft_phy.cpp.
| bool esphome::home_io_control::is_limitation_result | ( | uint8_t | result | ) |
Check whether a result code represents an environmental or control limitation.
| result | Result byte from CMD_ERROR_RESP data[0]. |
Definition at line 506 of file proto_constants.cpp.
| bool esphome::home_io_control::is_start | ( | const IoFrame & | f | ) |
Check START flag.
| f | Parsed frame. |
Definition at line 104 of file proto_frame.cpp.
|
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, so only a real wake-up burst reaches the threshold.
Definition at line 90 of file proto_timing.h.
| 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.
| value | Option string to parse. |
Definition at line 310 of file tuning_config.cpp.
| std::string esphome::home_io_control::key_transfer_mode_to_string | ( | KeyTransferMode | value | ) |
Render a KeyTransferMode as its YAML/select option string.
| value | Selection to render. |
Definition at line 298 of file tuning_config.cpp.
| uint32_t esphome::home_io_control::limitation_time_seconds | ( | uint8_t | time_raw | ) |
Remaining time of a limitation in seconds.
| time_raw | Time code (LimitationStatus::time_raw). |
Definition at line 491 of file proto_codecs.cpp.
|
static |
Log what discovery accepted: the device kind, and a warning when it answered as a remote controller.
Definition at line 406 of file pairing_engine.cpp.
|
static |
Log an overheard limitation request/reply decoded at DEBUG; any other frame is ignored.
Definition at line 555 of file hub_status.cpp.
| 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.
| value | YAML string such as "117.3" or "39.0kHz". |
Definition at line 152 of file tuning_config.cpp.
| BandwidthTableView esphome::home_io_control::lr1121_bandwidth_table | ( | ) |
The LR1121 RX-bandwidth option table (see sx1262_bandwidth_table()).
Definition at line 111 of file tuning_config.cpp.
| 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.
| bw | LR1121 bandwidth enum. |
Definition at line 143 of file tuning_config.cpp.
| 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).
| bw | LR1121 bandwidth enum. |
Definition at line 148 of file tuning_config.cpp.
|
nodiscardconstexpr |
Definition at line 62 of file lr1121_firmware_decisions.h.
|
nodiscardconstexpr |
Classify a bootloader/target mismatch by direction; see BootloaderMismatch.
Definition at line 162 of file lr1121_firmware_decisions.h.
|
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.
|
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:
| block_present | Whether a bootloader: sub-block is configured (the build flag). |
| bootloader_version_known | Whether the boot-time excursion successfully read a bootloader version – same "unknown is never evidence" sentinel rule as lr1121_flash_decision(). |
| bootloader_version | Bootloader version read at boot; meaningless if !bootloader_version_known. |
| loader_fw | Version parsed from the bootloader: sub-block's loader source: image. |
| target_fw | Configured target firmware version (0 if unknown). |
Definition at line 211 of file lr1121_firmware_decisions.h.
|
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.
|
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.
|
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 156 of file radio_lr1121.h.
|
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:
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:
| device_type | Chip identity byte from a normal-mode GetVersion (layer 3); 0 if that read never happened. |
| bootloader_chip_type | type 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_version | Bootloader version read at boot; one of LR1121_BOOTLOADER_2100/2101 for a genuine LR1121, or 0 if never successfully read. |
| installed_fw | Currently-installed transceiver firmware version (0 if unknown, e.g. after a failed init()). |
| target_fw | Configured target firmware version (0 if unknown — see above). |
| already_confirmed | True on a second button press within the confirmation window; allows every non-hard-reject outcome to proceed instead of asking again. |
Definition at line 291 of file lr1121_firmware_decisions.h.
| 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.
| errors | Raw GetErrors bitmask. |
| buf | Caller-owned output buffer; always NUL-terminated on return. |
| buf_size | Size 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.
|
nodiscardconstexpr |
Required bootloader for a known target firmware version.
Definition at line 138 of file lr1121_firmware_decisions.h.
|
static |
| 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.
| id | Manufacturer ID byte (1–12 for known manufacturers). |
Definition at line 121 of file proto_constants.cpp.
|
static |
Match token against table; on a hit set out and return true.
Definition at line 420 of file management_actions.cpp.
|
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 114 of file roll_call.h.
| 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.
| id | 3‑byte node ID. |
Definition at line 50 of file proto_frame.cpp.
|
static |
|
static |
Lowercase + ASCII-trim a native-API string argument.
Definition at line 405 of file management_actions.cpp.
|
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()).
| dev | Device to stamp. |
| now_ms | Current millis(). Must be non-zero in practice; 0 would read as "never". |
Definition at line 532 of file proto_device_model.h.
|
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.
| power_class | The identity's resolved power class, from effective_power_class(). |
| copy_index | 0-based position of this copy within the burst. |
Definition at line 84 of file oneway_controller.h.
| 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.
| main0 | First main byte (position or special code). |
| main1 | Second main byte (modifier); unused by every branch that resolves a target. |
Definition at line 313 of file proto_codecs.cpp.
| 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.
| power_class | Power class to name. |
Definition at line 10 of file oneway_controller.cpp.
| 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.
| info | Already-decoded frame info (see decode_1w_frame()). |
Definition at line 356 of file proto_codecs.cpp.
| const char * esphome::home_io_control::originator_name | ( | uint8_t | originator | ) |
Get a human-readable name for a command originator byte.
| originator | Originator code from the first data byte of CMD_EXECUTE. |
Definition at line 178 of file proto_constants.cpp.
|
inline |
Get a short, log/telemetry-friendly name for a pairing state.
| state | Pairing state. |
Definition at line 106 of file hub_pairing.h.
| 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).
| buf | Raw byte buffer. |
| buf_len | Number 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. |
| f | Output parsed frame. |
Definition at line 154 of file proto_frame.cpp.
|
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.
| function | Function name argument. |
| value | Value argument. |
| fn_out | Parsed function on success. |
| value_out | Parsed value on success (0 for the value-less functions). |
| error | Caller-facing message on failure. |
Definition at line 469 of file management_actions.cpp.
|
static |
Parse set_temperature's value into degrees Celsius, range-checked.
Definition at line 432 of file management_actions.cpp.
|
static |
Parse a probe_device()/probe_sweep() index argument into a byte.
Accepts both a bare decimal string ("6") and a "0x"-prefixed hex string ("0x06") — the two shapes a Home Assistant user is likely to type when copying a value out of a captured frame dump. Rejects anything else (empty string, trailing garbage, out-of-range value) rather than silently defaulting to 0, since a wrong index silently sent as 0 would misrepresent what was actually probed.
| text | Argument as received from the native API call. |
| out | Parsed byte on success; left untouched on failure. |
Definition at line 381 of file management_actions.cpp.
| const char * esphome::home_io_control::power_save_mode_name | ( | uint8_t | mode | ) |
Get a human-readable power save mode name.
| mode | Power save value (0–1) extracted from the Multi Information Byte. |
Definition at line 167 of file proto_constants.cpp.
|
inlinenodiscard |
Delay of a device's first poll after its interval is set.
| random | A uniformly distributed 32-bit random number. |
Definition at line 42 of file rain_poll_policy.h.
|
inlinenodiscard |
One poll interval moved by a uniform offset of up to RAIN_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.
| interval_ms | The configured interval. |
| random | A uniformly distributed 32-bit random number. |
Definition at line 54 of file rain_poll_policy.h.
| const char * esphome::home_io_control::rain_sensor_state_name | ( | RainSensorState | state | ) |
Get a human-readable name for a RainSensorState.
| state | The state to name. |
Definition at line 34 of file proto_device_model.cpp.
| 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).
| request | Our 0x38, as built by create_launch_key_transfer(). |
| transfer_payload | 16-byte CMD_KEY_TRANSFER payload from the device. |
| out_key | Output: the device's 16-byte key. |
Definition at line 882 of file proto_commands.cpp.
| 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.
| transfer_payload | 16-byte CMD_KEY_TRANSFER payload (frame.data). |
| challenge | The 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_key | Output: recovered 16-byte system key. |
Definition at line 855 of file proto_commands.cpp.
| 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.
| buf | Destination buffer, EXCHANGE_DEBUG_LINE_SIZE is always enough. |
| buf_size | Size of buf. |
| device_id | Human-readable device identifier. |
| d | Snapshot to render. |
Definition at line 90 of file exchange_engine.cpp.
|
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.
| data | Raw serialized frame bytes (header + payload, no CRC). |
| len | Total length of data. |
| out | Buffer to write the rendered text into. |
| out_size | Size of out. |
Definition at line 71 of file log_frame.h.
| 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.
| command | Discovery command code. |
| destination_auto | Whether to use the automatic mapping. |
| destination | Explicit 3-byte destination when auto is false. |
Definition at line 191 of file tuning_config.cpp.
|
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).
| manufacturer | The identity's manufacturer byte (MANUFACTURER_*, or a raw value). |
Definition at line 194 of file oneway_controller.h.
|
inline |
Convert an rssi_ema_scaled fixed-point value to whole dBm (round half away from zero).
| scaled | Fixed-point EMA value in 1/RSSI_EMA_SCALE dBm units (not the sentinel). |
Definition at line 547 of file proto_device_model.h.
| 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.
| engine | Exchange engine that transmits and collects the replies. |
| node_id | The hub's node ID. |
| key | The AES_KEY_SIZE-byte key the request is built with; only devices holding it answer. |
| key_slot | Slot recorded on every responder this sweep adds (0 = hub key). |
| tuning | Live tuning (reply window, power-class selection). |
| state | Accumulated scan state, appended to. |
| attempt | In/out: the global 1-based attempt counter across sweeps (0 before the first). |
| total_attempts | Attempts the whole scan will run, for the "n/N" log line. |
Definition at line 138 of file roll_call.cpp.
| 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.
| value | String to parse. |
Definition at line 236 of file tuning_config.cpp.
| 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.
| selection | Current scan_power_classes tuning value. |
| power_save_mode | POWER_SAVE_ALWAYS_ALIVE or POWER_SAVE_LOW_POWER. |
Definition at line 246 of file tuning_config.cpp.
| 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.
| value | Selection to format. |
Definition at line 224 of file tuning_config.cpp.
| 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).
| f | Parsed 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. |
| buf | Output buffer (must be at least frame_length(f) bytes, or +HMAC_SIZE when f.has_mac). |
| buf_size | Size of buf. |
Definition at line 114 of file proto_frame.cpp.
| 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.
| f | Frame to modify. |
| cmd | Command ID. |
| params | Pointer to payload bytes (may be nullptr for zero‑length). |
| params_len | Payload length (0–23). |
Definition at line 85 of file proto_frame.cpp.
| void esphome::home_io_control::set_dst | ( | IoFrame & | f, |
| const uint8_t | id[NODE_ID_SIZE] ) |
Set destination node ID.
| f | Frame to modify. |
| id | 3‑byte destination address. |
Definition at line 82 of file proto_frame.cpp.
| void esphome::home_io_control::set_src | ( | IoFrame & | f, |
| const uint8_t | id[NODE_ID_SIZE] ) |
Set source node ID.
| f | Frame to modify. |
| id | 3‑byte source address. |
Definition at line 83 of file proto_frame.cpp.
|
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). A device-provided byte-7 hint (hint_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.
Definition at line 88 of file status_poll_policy.h.
|
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.
| 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.
| raw | Raw bytes read from the chip's data buffer, starting at the reception's own offset. |
| raw_len | Number of raw bytes available (three is enough at any alignment). |
Definition at line 240 of file radio_soft_phy.cpp.
| 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.
| frame_len | Protocol frame length in bytes (CTRL0's own length field, +1). |
Definition at line 227 of file radio_soft_phy.cpp.
|
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.
| id | 3‑byte node ID buffer. |
Definition at line 200 of file proto_frame.h.
| std::optional< SX1262RxBandwidth > esphome::home_io_control::sx1262_bandwidth_from_string | ( | const std::string & | value | ) |
Convert a YAML bandwidth string to the enum value.
| value | YAML string such as "117.3kHz" or "156.2kHz". |
Definition at line 122 of file tuning_config.cpp.
| 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 109 of file tuning_config.cpp.
| float esphome::home_io_control::sx1262_bandwidth_to_khz | ( | SX1262RxBandwidth | bw | ) |
Convert a bandwidth enum to the numeric kHz value used in YAML/logs.
| bw | SX1262 bandwidth enum. |
Definition at line 113 of file tuning_config.cpp.
| 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").
| bw | SX1262 bandwidth enum. |
Definition at line 118 of file tuning_config.cpp.
| FemRxGain esphome::home_io_control::sx1262_fem_rx_gain | ( | FemProfile | profile | ) |
The receive gain to remove from RSSI for a FEM profile.
| profile | Any profile; FemProfile::NONE has no front end and yields 0. |
Definition at line 169 of file radio_sx1262.cpp.
| 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.
| profile | A real FEM profile (not FemProfile::NONE, which has no gain table). |
| tx_power | Configured 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.
| 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.
| errors | Raw device-error bitmask from GetDeviceErrors. |
| buf | Caller-owned output buffer; always NUL-terminated on return. |
| buf_size | Size 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 188 of file radio_sx1262.cpp.
| 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.
| value | YAML string such as "41.7" or "83.3kHz". |
Definition at line 137 of file tuning_config.cpp.
| BandwidthTableView esphome::home_io_control::sx1276_bandwidth_table | ( | ) |
The SX1276 RX-bandwidth option table (see sx1262_bandwidth_table()).
Definition at line 110 of file tuning_config.cpp.
| 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.
| bw | SX1276 bandwidth enum. |
Definition at line 128 of file tuning_config.cpp.
| 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).
| bw | SX1276 bandwidth enum. |
Definition at line 133 of file tuning_config.cpp.
|
inlinenodiscard |
Human-readable voltage for a tcxo_voltage code, for the config dump.
| code | 0-based chip voltage code (0x00 = 1.6 V .. 0x07 = 3.3 V) or TCXO_VOLTAGE_NONE. |
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.
| 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.
| value | Input string. |
Definition at line 76 of file proto_codecs.cpp.
| 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.
| cfg | Current tuning configuration. |
Definition at line 395 of file tuning_config.cpp.
| 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.
| cfg | Current tuning configuration. |
Definition at line 324 of file tuning_config.cpp.
| const TuningNumberParam * esphome::home_io_control::tuning_number_params_begin | ( | ) |
| const TuningNumberParam * esphome::home_io_control::tuning_number_params_end | ( | ) |
| const TuningSelectParam * esphome::home_io_control::tuning_select_params_begin | ( | ) |
| const TuningSelectParam * esphome::home_io_control::tuning_select_params_end | ( | ) |
| 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.
| name | YAML key name of the updated parameter. |
| value | YAML-compatible string representation of the new value. |
Definition at line 320 of file tuning_config.cpp.
|
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 95 of file proto_timing.h.
| 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).
Definition at line 256 of file radio_soft_phy.cpp.
| 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().
| origin | The device's challenged frame. |
| challenge | The 6 bytes we sent in the 0x3C. |
| mac | The 6 bytes of the device's 0x3D. |
| key | 16-byte key the MAC must verify under. |
Definition at line 976 of file proto_commands.cpp.
| 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.
| type | Device type enum. |
Definition at line 144 of file proto_device_model.cpp.
|
staticconstexpr |
Bits [2:1]: extended field.
Definition at line 612 of file proto_constants.h.
|
staticconstexpr |
Shift for extended field extraction.
Definition at line 613 of file proto_constants.h.
|
staticconstexpr |
Default automatic level (lowest).
Definition at line 631 of file proto_constants.h.
|
staticconstexpr |
Stand-alone automatic controller.
Definition at line 630 of file proto_constants.h.
|
staticconstexpr |
Comfort automation level 1.
Definition at line 628 of file proto_constants.h.
|
staticconstexpr |
Comfort automation level 2.
Definition at line 629 of file proto_constants.h.
|
staticconstexpr |
Bits [7:5]: priority level (0–7).
Definition at line 616 of file proto_constants.h.
|
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 624 of file proto_constants.h.
|
staticconstexpr |
Goods/environment protection via sensors.
Definition at line 625 of file proto_constants.h.
|
staticconstexpr |
Shift for priority level extraction.
Definition at line 617 of file proto_constants.h.
|
staticconstexpr |
Default remote controller priority.
Definition at line 627 of file proto_constants.h.
|
staticconstexpr |
High-priority user controller.
Definition at line 626 of file proto_constants.h.
|
staticconstexpr |
Bits [4:3]: service type.
Definition at line 614 of file proto_constants.h.
|
staticconstexpr |
Shift for service field extraction.
Definition at line 615 of file proto_constants.h.
|
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 611 of file proto_constants.h.
|
staticconstexpr |
Suffix for "all devices of this type" broadcast.
Definition at line 87 of file proto_codecs.h.
|
staticconstexpr |
Suffix for discovery-related broadcasts.
Definition at line 88 of file proto_codecs.h.
|
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.
|
staticconstexpr |
AES block size.
Definition at line 24 of file proto_sizes.h.
|
staticconstexpr |
AES-128 key size.
Definition at line 23 of file proto_sizes.h.
|
staticconstexpr |
Response within 10 ms.
Definition at line 710 of file proto_constants.h.
|
staticconstexpr |
Response within 20 ms.
Definition at line 711 of file proto_constants.h.
|
staticconstexpr |
Response within 40 ms.
Definition at line 712 of file proto_constants.h.
|
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 709 of file proto_constants.h.
|
staticconstexpr |
Position value written for binary OFF commands (light off, switch off, lock lock).
Definition at line 24 of file operation_queue.h.
|
staticconstexpr |
Position value written for binary ON commands (light on, switch on, lock unlock).
Definition at line 22 of file operation_queue.h.
|
staticconstexpr |
Number of bits in one protocol byte.
Definition at line 27 of file proto_sizes.h.
|
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 496 of file proto_constants.h.
|
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 502 of file proto_constants.h.
|
staticconstexpr |
Activate device mode (scene, ventilation) — requires auth.
Definition at line 25 of file proto_constants.h.
|
staticconstexpr |
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.
|
staticconstexpr |
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.
|
staticconstexpr |
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.
|
staticconstexpr |
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.
|
staticconstexpr |
Confirm discovery to device.
Definition at line 112 of file proto_constants.h.
|
staticconstexpr |
Device acknowledges confirmation.
Definition at line 113 of file proto_constants.h.
|
staticconstexpr |
Broadcast discovery request.
Definition at line 74 of file proto_constants.h.
|
staticconstexpr |
Device responds with its ID and type.
Definition at line 75 of file proto_constants.h.
|
staticconstexpr |
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.
|
staticconstexpr |
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.
|
staticconstexpr |
Error response to any command.
Definition at line 262 of file proto_constants.h.
|
staticconstexpr |
Set position/open/close/stop — requires authentication.
Definition at line 24 of file proto_constants.h.
|
staticconstexpr |
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 228 of file proto_constants.h.
|
staticconstexpr |
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 231 of file proto_constants.h.
|
staticconstexpr |
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 214 of file proto_constants.h.
|
staticconstexpr |
Device general info 1 response.
Still never captured on our wire or in any field log, and nothing decodes it — accepted by the soft-PHY only so a probe reply is not dropped as an unknown command.
Definition at line 217 of file proto_constants.h.
|
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 221 of file proto_constants.h.
|
staticconstexpr |
Device type/model response.
Definition at line 227 of file proto_constants.h.
|
staticconstexpr |
Request device name.
Definition at line 210 of file proto_constants.h.
|
staticconstexpr |
Device name response.
Definition at line 211 of file proto_constants.h.
|
staticconstexpr |
Device physical identification / jog — requires authentication.
Definition at line 53 of file proto_constants.h.
|
staticconstexpr |
Device confirms key was received.
Definition at line 146 of file proto_constants.h.
|
staticconstexpr |
Initiate key transfer to device.
Definition at line 144 of file proto_constants.h.
|
staticconstexpr |
Send encrypted system key to device.
Definition at line 145 of file proto_constants.h.
|
staticconstexpr |
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.
|
staticconstexpr |
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.
|
staticconstexpr |
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.
|
staticconstexpr |
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.
|
staticconstexpr |
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.
|
staticconstexpr |
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.
|
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.
|
staticconstexpr |
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.
|
staticconstexpr |
Reply to CMD_PRIORITY_LEVEL_REQ.
Never observed on the wire.
Definition at line 49 of file proto_constants.h.
|
staticconstexpr |
Get device status — no authentication needed.
Definition at line 26 of file proto_constants.h.
|
staticconstexpr |
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.
|
staticconstexpr |
Response to CMD_PRIVATE2. See CMD_PRIVATE2's comment.
Definition at line 35 of file proto_constants.h.
|
staticconstexpr |
Response to 0x00 and 0x03 (contains position data).
Definition at line 27 of file proto_constants.h.
|
staticconstexpr |
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 249 of file proto_constants.h.
|
staticconstexpr |
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 253 of file proto_constants.h.
|
staticconstexpr |
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 245 of file proto_constants.h.
|
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 258 of file proto_constants.h.
|
staticconstexpr |
Configure device to auto-send status updates.
Definition at line 237 of file proto_constants.h.
|
staticconstexpr |
Config response, otherwise undocumented.
Never observed in our corpus or in any field log, and not sent or handled anywhere in this codebase beyond the generic "is this a known command byte" check in radio_soft_phy.cpp.
Definition at line 238 of file proto_constants.h.
|
staticconstexpr |
Set device name (authenticated).
Definition at line 212 of file proto_constants.h.
|
staticconstexpr |
Device-name write response.
Definition at line 213 of file proto_constants.h.
|
staticconstexpr |
Device-initiated status update (needs auth).
Definition at line 242 of file proto_constants.h.
|
staticconstexpr |
Acknowledge status update.
Definition at line 243 of file proto_constants.h.
|
staticconstexpr |
Reads chunk <handle><chunk u16> of a buffer opened by a 0x46/0x47 exchange; seen on air between two controllers in a VELUX KLR 200 copy session.
What it does to an actuator is unknown, and the surrounding 0x46-0x4B block includes write commands. This constant exists solely so a received 0x4A frame renders by name in the log instead of as UNKNOWN_CMD; it must never be sent, and no builder for it exists anywhere in this codebase — see CMD_ONEWAY_ADD_CONTROLLER for the same "named but never sent" precedent, and docs/adr/ for the standing decision not to add one.
Definition at line 195 of file proto_constants.h.
|
staticconstexpr |
Echoes <handle><chunk> followed by up to 18 bytes of data.
Observed on the wire (tests/corpus/captures/probe/velux_kig300_probe_capability_burst.yaml) answering an ON_OFF_SWITCH-type device's traffic; the 01 00 00 payload there has the shape of a close echo. Not sent or handled anywhere in this codebase.
Definition at line 203 of file proto_constants.h.
|
staticconstexpr |
Write private register (climate/heating devices).
Definition at line 55 of file proto_constants.h.
|
staticconstexpr |
Acknowledgment to CMD_WRITE_PRIVATE.
Definition at line 56 of file proto_constants.h.
|
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 broadcasting it across all 3 channels 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 49 of file proto_timing.h.
|
staticconstexpr |
Least-significant-bit mask for reflected CRC update.
Definition at line 492 of file proto_constants.h.
|
staticconstexpr |
Reversed CRC-CCITT polynomial used by IO-homecontrol.
Definition at line 491 of file proto_constants.h.
|
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).
Definition at line 42 of file proto_frame.h.
|
staticconstexpr |
Bits [4:0]: frame length - 1.
Definition at line 45 of file proto_frame.h.
|
staticconstexpr |
Bit 5: 1=OneWay protocol, 0=TwoWay protocol.
Definition at line 44 of file proto_frame.h.
|
staticconstexpr |
Bit 6: first frame in exchange.
Definition at line 43 of file proto_frame.h.
|
staticconstexpr |
Bit 4: sender can handle 2W responses (ACK-capable).
Definition at line 70 of file proto_frame.h.
|
staticconstexpr |
Bit 7: beacon announcement frame.
Definition at line 73 of file proto_frame.h.
|
staticconstexpr |
Bit 5: low-power device (e.g., solar-powered).
Definition at line 71 of file proto_frame.h.
|
staticconstexpr |
Bit 2: high-priority frame.
Definition at line 69 of file proto_frame.h.
|
staticconstexpr |
Bit 6: frame was relayed through a repeater.
Definition at line 72 of file proto_frame.h.
|
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.
Definition at line 68 of file proto_frame.h.
|
inlineconstexpr |
SX1276 PA_CONFIG selector for the PA_BOOST output path.
Definition at line 64 of file hub_core.h.
|
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 hint-less devices are re-checked promptly and the motion-tracking loop terminates soon after the device comes to rest.
Definition at line 69 of file status_poll_policy.h.
|
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.
|
inlineconstexpr |
Default TX power used unless YAML overrides it.
Definition at line 63 of file hub_core.h.
|
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 155 of file radio_soft_phy_driver_base.h.
|
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 335 of file proto_device_model.h.
|
staticconstexpr |
Device name storage including null terminator.
Definition at line 348 of file proto_device_model.h.
|
staticconstexpr |
Reference write limit before the trailing null.
Definition at line 29 of file proto_codecs.h.
|
staticconstexpr |
Fixed write payload: 15 visible chars plus trailing null/padding.
Definition at line 30 of file proto_codecs.h.
|
staticconstexpr |
Definition at line 339 of file proto_device_model.h.
|
staticconstexpr |
Definition at line 337 of file proto_device_model.h.
|
staticconstexpr |
Definition at line 336 of file proto_device_model.h.
|
staticconstexpr |
The node-type wire field is ten bits wide.
Definition at line 338 of file proto_device_model.h.
|
staticconstexpr |
Bit masks and shifts for the Multi Information Byte fields.
Bits [7:6]: actuator turnaround time class.
Definition at line 691 of file proto_constants.h.
|
staticconstexpr |
Shift for ATT field extraction.
Definition at line 692 of file proto_constants.h.
|
staticconstexpr |
Bit 2: io-homecontrol membership; always 1 (same table).
Definition at line 697 of file proto_constants.h.
|
staticconstexpr |
Bits [1:0]: power save mode.
Definition at line 699 of file proto_constants.h.
|
staticconstexpr |
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 694 of file proto_constants.h.
|
staticconstexpr |
Bit 5: supports sync control group.
Definition at line 693 of file proto_constants.h.
|
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 675 of file proto_constants.h.
|
staticconstexpr |
Flags byte at data[6].
Definition at line 680 of file proto_constants.h.
|
staticconstexpr |
Full discovery response payload size.
Definition at line 682 of file proto_constants.h.
|
staticconstexpr |
Manufacturer ID at data[5].
Definition at line 679 of file proto_constants.h.
|
staticconstexpr |
Timestamp starts at data[7] (2 bytes).
Definition at line 681 of file proto_constants.h.
|
staticconstexpr |
Highest named result code (17, "already configured").
Definition at line 280 of file proto_constants.h.
|
staticconstexpr |
Result code sits in bits [7:3] of the 0xFE byte.
Definition at line 278 of file proto_constants.h.
|
staticconstexpr |
Execution state sits in bits [2:0] of the 0xFE byte.
Definition at line 279 of file proto_constants.h.
|
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 596 of file exchange_engine.h.
|
staticconstexpr |
Attempts per command before reporting failure.
Definition at line 136 of file proto_timing.h.
|
staticconstexpr |
Gap between retries within one HA command.
Definition at line 135 of file proto_timing.h.
|
staticconstexpr |
Wall-clock ceiling on one whole exchange, retries included.
EXCHANGE_RETRY_COUNT tries x (long preamble + response window + retry gap) is what actually 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 retry count is a maximum, not a promise: a try only starts if its transmission ends inside the budget — elapsed time, plus the gap before the try, plus the radio's estimate of the try's transmit time (RadioDriver::tx_air_time_us()), must stay below it. Counting the transmission keeps a radio with a long wake-up from starting a try it cannot finish in time. At the current 400 ms response window all three tries still fit (~2.3 s); raising the window well past the default is what starts trimming retries, since three full tries stop being affordable at that point — one long listen is the better trade there anyway.
Definition at line 224 of file proto_timing.h.
|
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 372 of file proto_constants.h.
|
staticconstexpr |
Byte offset of the command ID in a raw wire buffer.
Definition at line 51 of file proto_sizes.h.
|
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.
|
inlineconstexpr |
Buffer for decoded CTRL1 flag annotations.
Definition at line 20 of file log_frame.h.
|
inlineconstexpr |
Fits a full 32-byte frame rendered as spaced hex text.
Definition at line 19 of file log_frame.h.
|
staticconstexpr |
Maximum data bytes after command ID (declared length - header).
Definition at line 50 of file proto_sizes.h.
|
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.
|
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.
|
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. The iohomecontrol reference implementation's _p0x30 packet struct omits an hmac field entirely (unlike its _p0x2e, whose MAC sits inside the declared length).
Definition at line 64 of file proto_sizes.h.
|
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).
Definition at line 72 of file proto_sizes.h.
|
staticconstexpr |
Minimum frame: CTRL0+CTRL1+DST(3)+SRC(3)+CMD(1).
Definition at line 29 of file proto_sizes.h.
|
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.
|
staticconstexpr |
Channel 2: 868.95 MHz (1W and 2W, TX channel).
Definition at line 22 of file proto_timing.h.
|
staticconstexpr |
Channel 3: 869.85 MHz (2W only).
Definition at line 23 of file proto_timing.h.
|
staticconstexpr |
SX1276 crystal oscillator frequency (32 MHz).
Used to calculate register values for bitrate, frequency deviation, and carrier frequency.
Definition at line 79 of file radio_sx1276.h.
|
staticconstexpr |
Main-parameter prefix byte of a setpoint alias: the alias occupies the low byte.
Definition at line 71 of file proto_heating_profiles.h.
|
constexpr |
Payload byte at index 1 of a read ("get") request.
Definition at line 92 of file proto_heating.h.
|
constexpr |
Payload byte at index 1 of a write ("set") request.
Definition at line 95 of file proto_heating.h.
|
staticconstexpr |
Highest heating level low byte create_execute_heating_level() will frame (the secured level).
Definition at line 138 of file proto_commands.h.
|
staticconstexpr |
Main-parameter prefix byte of a heating level: the level occupies the low byte.
Definition at line 56 of file proto_heating_profiles.h.
|
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 89 of file proto_heating_profiles.h.
|
staticconstexpr |
Main parameter that means "off": the unknown-position marker, which a command uses together with a functional parameter to switch a device off.
Definition at line 85 of file proto_heating_profiles.h.
|
constexpr |
Largest payload any function produces: a set of a 16-bit value (4-byte header + 2 value bytes).
Definition at line 82 of file proto_heating.h.
|
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 86 of file proto_heating.h.
|
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 42 of file status_poll_policy.h.
|
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 50 of file status_poll_policy.h.
|
constexpr |
Payload byte at index 2: the high byte of the 16-bit register number, constant 0x01.
Definition at line 89 of file proto_heating.h.
|
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 99 of file proto_heating.h.
|
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 79 of file proto_heating.h.
|
constexpr |
Lowest setpoint this codec will encode.
The conventional frost-protection setpoint, safely representable as wire value 70.
Definition at line 71 of file proto_heating.h.
|
staticconstexpr |
Number of main-parameter steps across the encoding range (the full position scale, 0xC800).
Definition at line 125 of file proto_heating_profiles.h.
|
staticconstexpr |
See HEATING_TEMPERATURE_RANGE_MIN_C.
Definition at line 105 of file proto_heating.h.
|
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 104 of file proto_heating.h.
|
staticconstexpr |
Authentication HMAC is 6 bytes (truncated AES output).
Definition at line 22 of file proto_sizes.h.
|
staticconstexpr |
Timing constants for frequency hopping and response waiting.
Time per channel when hopping (2.7ms)
Definition at line 115 of file proto_timing.h.
|
staticconstexpr |
Delay before the first post-boot status request from an entity.
Definition at line 39 of file status_poll_policy.h.
|
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.
|
staticconstexpr |
Padding byte used in IV construction.
Definition at line 26 of file proto_sizes.h.
|
staticconstexpr |
Initialization vector size for AES.
Definition at line 25 of file proto_sizes.h.
|
staticconstexpr |
Highest Unicode code point representable in Latin-1.
Definition at line 32 of file proto_codecs.h.
|
staticconstexpr |
Max carrier-sense attempts before TX anyway.
Definition at line 274 of file proto_timing.h.
|
staticconstexpr |
Backoff between LBT checks (≥ 5ms per ETSI).
Definition at line 275 of file proto_timing.h.
|
staticconstexpr |
Listen-before-talk (LBT) parameters for ETSI EN 300 220 compliance.
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 (ETSI: ≤ -90 dBm)
Definition at line 273 of file proto_timing.h.
|
staticconstexpr |
Parameter id of FP1.
Definition at line 469 of file proto_constants.h.
|
staticconstexpr |
Parameter id of FP16.
Definition at line 470 of file proto_constants.h.
|
staticconstexpr |
Parameter id of the main parameter.
Definition at line 468 of file proto_constants.h.
|
staticconstexpr |
Request data: selector, parameter id, 0x00.
Definition at line 471 of file proto_constants.h.
|
staticconstexpr |
Reply data: parameter id, value (2), originator, time.
Definition at line 472 of file proto_constants.h.
|
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 477 of file proto_constants.h.
|
staticconstexpr |
Seconds per time-code step.
Definition at line 479 of file proto_constants.h.
|
staticconstexpr |
Limitation without a time limit.
Definition at line 478 of file proto_constants.h.
|
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 34 of file proto_timing.h.
|
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 157 of file proto_timing.h.
|
inlineconstexpr |
Definition at line 51 of file lr1121_firmware_decisions.h.
|
inlineconstexpr |
Definition at line 50 of file lr1121_firmware_decisions.h.
|
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.
|
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.
|
inlineconstexpr |
Definition at line 49 of file lr1121_firmware_decisions.h.
|
inlineconstexpr |
Definition at line 29 of file lr1121_firmware_decisions.h.
|
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.
|
inlineconstexpr |
Definition at line 43 of file lr1121_firmware_decisions.h.
|
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.
|
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 249 of file radio_lr1121.h.
|
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 175 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 52 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked (RadioLib calibrateImageRejection)
Definition at line 53 of file radio_lr1121.h.
|
staticconstexpr |
hardware-verified (called on every init/RX cycle, never rejected — see log_command_status_())
Definition at line 46 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 56 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked (2-byte response)
Definition at line 45 of file radio_lr1121.h.
|
staticconstexpr |
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.
|
staticconstexpr |
cross-checked
Definition at line 64 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 59 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 43 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 44 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 49 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 54 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 55 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 65 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 70 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 74 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 71 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 69 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 68 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 66 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 73 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 58 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 57 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 67 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 72 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked
Definition at line 48 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked (Semtech SWDR001 lr11xx_radio.c / RadioLib LR11x0_commands.h)
Definition at line 50 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked against RadioLib and the datasheet
Definition at line 108 of file radio_lr1121.h.
|
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.
|
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 243 of file tuning_config.h.
|
staticconstexpr |
ADC calibration failed.
Definition at line 113 of file radio_lr1121.h.
|
staticconstexpr |
High-frequency RC oscillator calibration failed.
Definition at line 112 of file radio_lr1121.h.
|
staticconstexpr |
High-frequency crystal/TCXO failed to start.
Definition at line 116 of file radio_lr1121.h.
|
staticconstexpr |
Image calibration failed.
Definition at line 115 of file radio_lr1121.h.
|
staticconstexpr |
Low-frequency RC oscillator calibration failed.
Definition at line 111 of file radio_lr1121.h.
|
staticconstexpr |
Low-frequency crystal failed to start.
Definition at line 117 of file radio_lr1121.h.
|
staticconstexpr |
PLL calibration failed.
Definition at line 114 of file radio_lr1121.h.
|
staticconstexpr |
PLL failed to lock.
Definition at line 118 of file radio_lr1121.h.
|
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 180 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked (same encoding as SX126x)
Definition at line 162 of file radio_lr1121.h.
|
staticconstexpr |
cross-checked (same encoding as SX126x)
Definition at line 163 of file radio_lr1121.h.
|
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 220 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 221 of file radio_lr1121.h.
|
staticconstexpr |
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.
|
staticconstexpr |
Definition at line 85 of file radio_lr1121.h.
|
staticconstexpr |
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.
|
staticconstexpr |
Definition at line 83 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 82 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 84 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 86 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 81 of file radio_lr1121.h.
|
inlineconstexpr |
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.
|
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 148 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 149 of file radio_lr1121.h.
|
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.
|
staticconstexpr |
cross-checked against RadioLib
Definition at line 161 of file radio_lr1121.h.
|
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 235 of file tuning_config.h.
|
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 167 of file radio_lr1121.h.
|
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 208 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 209 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 210 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 211 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 212 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 213 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 214 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 215 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 216 of file radio_lr1121.h.
|
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 201 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 202 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 203 of file radio_lr1121.h.
|
staticconstexpr |
LR1121-specific preamble for response/continuation frames within an exchange.
Seeded from the SX1262-validated default, on the principle that a validated timing value encodes protocol-side reality more than a chip quirk. Not yet independently validated on LR1121 hardware; a value measured by loopback tuning would fold back here.
Definition at line 230 of file tuning_config.h.
|
staticconstexpr |
DIO5 + DIO6 are switch pins.
Definition at line 232 of file radio_lr1121.h.
|
staticconstexpr |
Unused; both low.
Definition at line 238 of file radio_lr1121.h.
|
staticconstexpr |
DIO5 high.
Definition at line 234 of file radio_lr1121.h.
|
staticconstexpr |
Both low.
Definition at line 233 of file radio_lr1121.h.
|
staticconstexpr |
DIO6 high (both LP and HP PA).
Definition at line 235 of file radio_lr1121.h.
|
staticconstexpr |
2.4GHz path unused; both low.
Definition at line 237 of file radio_lr1121.h.
|
staticconstexpr |
Same as TX (LP PA only used today).
Definition at line 236 of file radio_lr1121.h.
|
staticconstexpr |
Unused; both low.
Definition at line 239 of file radio_lr1121.h.
|
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 189 of file radio_lr1121.h.
|
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 170 of file radio_lr1121.h.
|
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 256 of file radio_lr1121.h.
|
staticconstexpr |
Definition at line 253 of file radio_lr1121.h.
|
staticconstexpr |
LR1121 TCXO voltage on the T3-S3 board — 3.0V, confirmed on real hardware.
Definition at line 252 of file radio_lr1121.h.
|
staticconstexpr |
ASSA ABLOY (locks, access).
Definition at line 532 of file proto_constants.h.
|
staticconstexpr |
Atlantic Group (heating, hot water).
Definition at line 539 of file proto_constants.h.
|
staticconstexpr |
CIAT (HVAC).
Definition at line 536 of file proto_constants.h.
|
staticconstexpr |
Honeywell.
Definition at line 530 of file proto_constants.h.
|
staticconstexpr |
Hörmann (garage doors, gates).
Definition at line 531 of file proto_constants.h.
|
staticconstexpr |
Maximum manufacturer ID with a known name in the lookup table.
Definition at line 521 of file proto_constants.h.
|
staticconstexpr |
Niko (switches, home automation).
Definition at line 533 of file proto_constants.h.
|
staticconstexpr |
OVERKIZ (Somfy connectivity platform).
Definition at line 538 of file proto_constants.h.
|
staticconstexpr |
Renson (ventilation, sun protection).
Definition at line 535 of file proto_constants.h.
|
staticconstexpr |
Secuyou (security).
Definition at line 537 of file proto_constants.h.
|
staticconstexpr |
Somfy (shutters, awnings, blinds).
Definition at line 529 of file proto_constants.h.
|
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 528 of file proto_constants.h.
|
staticconstexpr |
WINDOW MASTER (ventilation).
Definition at line 534 of file proto_constants.h.
|
staticconstexpr |
Hard stop for bounded follow-up polling after a command or remote activity.
Definition at line 37 of file status_poll_policy.h.
|
staticconstexpr |
Definition at line 75 of file radio_sx1276.h.
|
staticconstexpr |
Definition at line 74 of file radio_sx1276.h.
|
staticconstexpr |
Definition at line 71 of file radio_sx1276.h.
|
staticconstexpr |
Definition at line 72 of file radio_sx1276.h.
|
staticconstexpr |
Definition at line 73 of file radio_sx1276.h.
|
staticconstexpr |
Device/node addresses are 3 bytes (e.g., "123ABC").
Definition at line 20 of file proto_sizes.h.
|
staticconstexpr |
Uppercase hex node ID plus null terminator.
Definition at line 21 of file proto_sizes.h.
|
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 sits inside the preamble band the iown-homecontrol documentation describes (256 bits in its radio notes; 128 bits is the "Long PPDU" preamble in its link-layer notes), 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 64 of file proto_timing.h.
|
staticconstexpr |
Definition at line 24 of file tuning_registry.cpp.
|
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 256 of file proto_timing.h.
|
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 come from the reference implementation, which sets them on adjacent lines when it forges a 1W packet: packet->repeat = 4 and packet->repeatTime = 40 in its 1W remote. The capture logs embedded in that same source show consecutive copies of one burst arriving roughly 25 ms apart, which is not a contradiction: those are receive-side timestamps of a burst whose configured gap is 40 ms, so they measure something else. 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 249 of file proto_timing.h.
|
staticconstexpr |
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 3 nodes: 9D6085, 485B37, 7B8240) 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 651 of file proto_constants.h.
|
staticconstexpr |
ACEI byte for a 1W CMD_EXECUTE frame from a VELUX KLI-class remote.
Level 3 (user_default), extended-info bits clear. This is also iown-homecontrol's generic ACEI_DEFAULT. Confidence: n=1 — one corpus frame (tests/corpus/captures/oneway/velux_kli313_oneway_stop.yaml) plus samr037/iohc-flipper's README, which names cmd 0x00 payload[1] the "vendor byte" (0x43 Somfy / 0x61 Velux). The unidentified_1w_remote_* frames also carry 0x61 but their 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 663 of file proto_constants.h.
|
staticconstexpr |
Minimum data bytes for decode of execute/activate‑mode intent fields.
Definition at line 331 of file proto_codecs.cpp.
|
staticconstexpr |
Buffer size for the decoded 1W main-intent string.
Definition at line 169 of file proto_codecs.h.
|
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 64 of file status_poll_policy.h.
|
staticconstexpr |
Definition at line 258 of file oneway_controller.h.
|
staticconstexpr |
Wire-scale position meaning "fully closed" (0 means fully open).
Named here because the two values are what OPEN and CLOSE actually are — see encode_oneway_action().
Definition at line 257 of file oneway_controller.h.
|
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 57 of file status_poll_policy.h.
|
staticconstexpr |
How far ahead of its stored high-water mark a device will still accept a jump.
1000 is the window the iohomecontrol reference receiver applies, 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.
|
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.
|
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.
|
staticconstexpr |
Automatic cycle / external access.
Definition at line 590 of file proto_constants.h.
|
staticconstexpr |
Emergency command (never disabled).
Definition at line 591 of file proto_constants.h.
|
staticconstexpr |
Unspecified environment sensor.
Definition at line 588 of file proto_constants.h.
|
staticconstexpr |
Lifestyle scenario controller.
Definition at line 583 of file proto_constants.h.
|
staticconstexpr |
Load-shedding manager.
Definition at line 586 of file proto_constants.h.
|
staticconstexpr |
Local light sensor.
Definition at line 587 of file proto_constants.h.
|
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 576 of file proto_constants.h.
|
staticconstexpr |
Actuator decided to move by itself.
Definition at line 589 of file proto_constants.h.
|
staticconstexpr |
Rain sensor triggered the movement.
Definition at line 578 of file proto_constants.h.
|
staticconstexpr |
Stand-alone automatic controller (SAAC).
Definition at line 584 of file proto_constants.h.
|
staticconstexpr |
Security controlling device (SCD) action.
Definition at line 580 of file proto_constants.h.
|
staticconstexpr |
Smart function controller.
Definition at line 582 of file proto_constants.h.
|
staticconstexpr |
Timer or schedule triggered the movement.
Definition at line 579 of file proto_constants.h.
|
staticconstexpr |
Uninterruptible power supply action.
Definition at line 581 of file proto_constants.h.
|
staticconstexpr |
User sent command from a remote control.
Definition at line 577 of file proto_constants.h.
|
staticconstexpr |
Wind sensor triggered the movement.
Definition at line 585 of file proto_constants.h.
|
inlineconstexpr |
Wait window per discover-confirm try.
Definition at line 60 of file pairing_engine.h.
|
inlineconstexpr |
Max tries for the discover-confirm (0x2C) step.
Definition at line 59 of file pairing_engine.h.
|
staticconstexpr |
Dwell on CH2 before discovery hopping begins.
Definition at line 285 of file proto_timing.h.
|
inlineconstexpr |
Retry discovery TX up to this many times.
Definition at line 56 of file pairing_engine.h.
|
staticconstexpr |
Default for TuningConfig::pairing_discovery_preamble — the preamble on the pairing discovery broadcast (CMD_DISCOVER_REQ/CMD_DISCOVER_ALT_REQ, 0x28/0x2E).
LONG_PREAMBLE, because a device in learning mode may be a duty-cycled receiver that needs the wake-up burst. Unlike a directed start frame (see NORMAL_START_PREAMBLE), the discovery broadcast can't follow the target's power class: discovery runs before anything is known about the device. Some awake receivers never lock onto a preamble this long — a VELUX SSL solar roller shutter answers discovery only at a short preamble — so the value is runtime-tunable via pairing_discovery_preamble, and pairing reuses the tuned value for the frames it then sends to the discovered device (PairingEngine::pairing_start_preamble_()).
Definition at line 75 of file proto_timing.h.
|
inlineconstexpr |
Discovery wait window after sending 0x28.
Definition at line 55 of file pairing_engine.h.
|
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 284 of file proto_timing.h.
|
inlineconstexpr |
Wait window for the device's 0x3C challenge.
Definition at line 57 of file pairing_engine.h.
|
inlineconstexpr |
Wait for 0x33 key confirm after sending 0x32.
Definition at line 58 of file pairing_engine.h.
|
staticconstexpr |
Retries for the authenticated key-exchange phase.
Definition at line 286 of file proto_timing.h.
|
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 295 of file proto_timing.h.
|
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.
|
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.
|
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.
|
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 163 of file proto_timing.h.
|
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.
|
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.
|
staticconstexpr |
Wire value for the default position command.
Moves the actuator to its factory or user-configured default position.
Definition at line 384 of file proto_constants.h.
|
staticconstexpr |
Wire value: move to favorite/"My" position.
Definition at line 368 of file proto_constants.h.
|
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 398 of file proto_constants.h.
|
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 379 of file proto_constants.h.
|
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 366 of file proto_constants.h.
|
staticconstexpr |
Wire value: position unknown / keep current.
Definition at line 367 of file proto_constants.h.
|
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 404 of file proto_constants.h.
|
inlineconstexpr |
Buffer for formatted position strings such as "100%".
Definition at line 67 of file hub_core.h.
|
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 415 of file proto_constants.h.
|
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 412 of file proto_constants.h.
|
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 720 of file proto_constants.h.
|
staticconstexpr |
Device sleeps — needs long preamble to wake.
Definition at line 721 of file proto_constants.h.
|
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 107 of file proto_timing.h.
|
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 437 of file proto_constants.h.
|
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 332 of file proto_codecs.h.
|
staticconstexpr |
CMD_PRIVATE function ID at data[0].
Definition at line 445 of file proto_constants.h.
|
inlineconstexpr |
Scratch buffer size for raw radio packets and recovered frames.
Definition at line 24 of file radio_interface.h.
|
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 266 of file proto_timing.h.
|
staticconstexpr |
Consecutive unanswered polls after which the last reading is no longer usable.
Definition at line 37 of file rain_poll_policy.h.
|
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 22 of file rain_poll_policy.h.
|
staticconstexpr |
Width of the random window added to RAIN_POLL_INITIAL_DELAY_MS, so devices do not start in step.
Definition at line 24 of file rain_poll_policy.h.
|
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 29 of file rain_poll_policy.h.
|
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 35 of file rain_poll_policy.h.
|
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 32 of file rain_poll_policy.h.
|
staticconstexpr |
AFC bandwidth.
Definition at line 49 of file radio_sx1276.h.
|
staticconstexpr |
AFC auto clear.
Definition at line 50 of file radio_sx1276.h.
|
staticconstexpr |
Definition at line 37 of file radio_sx1276.h.
|
staticconstexpr |
Bit rate MSB = FXOSC / bitrate.
Definition at line 36 of file radio_sx1276.h.
|
staticconstexpr |
DIO0-DIO3 pin mapping.
Definition at line 64 of file radio_sx1276.h.
|
staticconstexpr |
DIO4-DIO5 pin mapping.
Definition at line 65 of file radio_sx1276.h.
|
staticconstexpr |
Definition at line 39 of file radio_sx1276.h.
|
staticconstexpr |
Frequency deviation MSB.
Definition at line 38 of file radio_sx1276.h.
|
staticconstexpr |
FIFO read/write access.
Definition at line 34 of file radio_sx1276.h.
|
staticconstexpr |
FIFO threshold for TX start condition.
Definition at line 61 of file radio_sx1276.h.
|
staticconstexpr |
Definition at line 42 of file radio_sx1276.h.
|
staticconstexpr |
Definition at line 41 of file radio_sx1276.h.
|
staticconstexpr |
Carrier frequency MSB (freq = FRF * FXOSC / 2^19).
Definition at line 40 of file radio_sx1276.h.
|
staticconstexpr |
Image calibration.
Definition at line 68 of file radio_sx1276.h.
|
staticconstexpr |
IRQ flags: mode ready, preamble detect, etc.
Definition at line 62 of file radio_sx1276.h.
|
staticconstexpr |
IRQ flags: FIFO full/empty, payload ready, CRC ok.
Definition at line 63 of file radio_sx1276.h.
|
staticconstexpr |
Low noise amplifier gain and boost.
Definition at line 45 of file radio_sx1276.h.
|
staticconstexpr |
Operating mode (sleep/standby/tx/rx).
Definition at line 35 of file radio_sx1276.h.
|
staticconstexpr |
Oscillator / clock output.
Definition at line 52 of file radio_sx1276.h.
|
staticconstexpr |
Power amplifier config (pin select + power level).
Definition at line 43 of file radio_sx1276.h.
|
staticconstexpr |
PA ramp time and modulation shaping.
Definition at line 44 of file radio_sx1276.h.
|
staticconstexpr |
Packet format, CRC, encoding.
Definition at line 58 of file radio_sx1276.h.
|
staticconstexpr |
Packet mode, IoHomeOn, PowerFrame.
Definition at line 59 of file radio_sx1276.h.
|
staticconstexpr |
Max payload length.
Definition at line 60 of file radio_sx1276.h.
|
staticconstexpr |
PLL hop: fast frequency change without standby.
Definition at line 67 of file radio_sx1276.h.
|
staticconstexpr |
Preamble detector config.
Definition at line 51 of file radio_sx1276.h.
|
staticconstexpr |
Definition at line 55 of file radio_sx1276.h.
|
staticconstexpr |
TX preamble length MSB.
Definition at line 54 of file radio_sx1276.h.
|
staticconstexpr |
RSSI smoothing.
Definition at line 47 of file radio_sx1276.h.
|
staticconstexpr |
Instant RSSI value in FSK mode.
Definition at line 53 of file radio_sx1276.h.
|
staticconstexpr |
Receiver bandwidth.
Definition at line 48 of file radio_sx1276.h.
|
staticconstexpr |
Receiver configuration (AFC, AGC, trigger).
Definition at line 46 of file radio_sx1276.h.
|
staticconstexpr |
Sync word config (size, polarity, enable).
Definition at line 56 of file radio_sx1276.h.
|
staticconstexpr |
Sync word byte 1 (registers 0x28-0x2F for bytes 1-8).
Definition at line 57 of file radio_sx1276.h.
|
staticconstexpr |
Chip version (should read 0x12 for SX1276).
Definition at line 66 of file radio_sx1276.h.
|
staticconstexpr |
Delay before polling after overheard remote traffic.
Definition at line 52 of file status_poll_policy.h.
|
staticconstexpr |
Wait for final response after challenge response.
Definition at line 133 of file proto_timing.h.
|
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 131 of file proto_timing.h.
|
staticconstexpr |
Wait for response to non-start frame.
Definition at line 116 of file proto_timing.h.
|
staticconstexpr |
Code 13: alias incoherence.
Definition at line 336 of file proto_constants.h.
|
staticconstexpr |
Code 17: already configured.
Definition at line 341 of file proto_constants.h.
|
staticconstexpr |
Invalid index received.
Definition at line 308 of file proto_constants.h.
|
staticconstexpr |
Code 3: challenge answer (MAC) did not verify.
Definition at line 319 of file proto_constants.h.
|
staticconstexpr |
Battery level is low.
Definition at line 299 of file proto_constants.h.
|
staticconstexpr |
Node blocked by an object.
Definition at line 287 of file proto_constants.h.
|
staticconstexpr |
Node is calibrating.
Definition at line 292 of file proto_constants.h.
|
staticconstexpr |
Requested colour not reachable.
Definition at line 306 of file proto_constants.h.
|
staticconstexpr |
No errors detected.
Definition at line 284 of file proto_constants.h.
|
staticconstexpr |
Command cannot move the node that way.
Definition at line 302 of file proto_constants.h.
|
staticconstexpr |
Command was overruled by a newer command.
Definition at line 309 of file proto_constants.h.
|
staticconstexpr |
Code 16: command rejected.
A Somfy Sunilus and a Somfy J406io answered a key transfer (0x32) with it.
Definition at line 339 of file proto_constants.h.
|
staticconstexpr |
Code 1: the device does not offer this command in its current state or class.
Definition at line 316 of file proto_constants.h.
|
staticconstexpr |
Dead bolt error.
Definition at line 304 of file proto_constants.h.
|
staticconstexpr |
Filter needs maintenance.
Definition at line 298 of file proto_constants.h.
|
staticconstexpr |
Information-only code with unknown semantics.
Definition at line 342 of file proto_constants.h.
|
staticconstexpr |
Intermediate position is not set.
Definition at line 313 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by an automatic cycle.
Definition at line 354 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by an emergency.
Definition at line 355 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by local button.
Definition at line 344 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by the node itself.
Definition at line 353 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by a rain sensor.
Definition at line 346 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by a standalone automatic controller.
Definition at line 351 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by a security actuator.
Definition at line 348 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by a timer.
Definition at line 347 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by an unknown device.
Definition at line 350 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by a power supply.
Definition at line 349 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by a remote control.
Definition at line 345 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by a wind sensor.
Definition at line 352 of file proto_constants.h.
|
staticconstexpr |
Device limits are not set.
Definition at line 312 of file proto_constants.h.
|
staticconstexpr |
Lock command failed because the door is open.
Definition at line 295 of file proto_constants.h.
|
staticconstexpr |
Manually operated by a user.
Definition at line 286 of file proto_constants.h.
|
staticconstexpr |
Mode is not supported by the node.
Definition at line 301 of file proto_constants.h.
|
staticconstexpr |
Code 2: mode or state number unknown.
Definition at line 318 of file proto_constants.h.
|
staticconstexpr |
Target was not reached in time.
Definition at line 296 of file proto_constants.h.
|
staticconstexpr |
No communication to node.
Definition at line 285 of file proto_constants.h.
|
staticconstexpr |
Node did not move.
Definition at line 291 of file proto_constants.h.
|
staticconstexpr |
Node is waiting for power.
Definition at line 310 of file proto_constants.h.
|
staticconstexpr |
Code 15: not implemented.
Definition at line 338 of file proto_constants.h.
|
staticconstexpr |
Code 6: command originator disabled.
Definition at line 324 of file proto_constants.h.
|
staticconstexpr |
Requested value is out of range.
Definition at line 314 of file proto_constants.h.
|
staticconstexpr |
Code 10: parameter incoherence adjustment.
Definition at line 329 of file proto_constants.h.
|
staticconstexpr |
Code 9: parameter limitation adjustment.
Definition at line 328 of file proto_constants.h.
|
staticconstexpr |
Parameter limited by an unknown device.
Definition at line 343 of file proto_constants.h.
|
staticconstexpr |
Code 5: parameter, alias, access method or scenario unknown.
The Somfy Izymo answers our SetConfig1 (0x6F) with it during pairing.
Definition at line 321 of file proto_constants.h.
|
staticconstexpr |
Node power consumption is too high.
Definition at line 293 of file proto_constants.h.
|
staticconstexpr |
Node power consumption is too low.
Definition at line 294 of file proto_constants.h.
|
staticconstexpr |
Node is locked on this priority level.
Definition at line 289 of file proto_constants.h.
|
staticconstexpr |
Code 7: priority locked; the command's priority level is too low to run.
Definition at line 325 of file proto_constants.h.
|
staticconstexpr |
Code 8: priority service not supported.
Definition at line 327 of file proto_constants.h.
|
staticconstexpr |
Node stopped in another position than expected.
Definition at line 290 of file proto_constants.h.
|
staticconstexpr |
Code 12: service full.
Definition at line 335 of file proto_constants.h.
|
staticconstexpr |
Code 14: session interrupted.
Definition at line 337 of file proto_constants.h.
|
staticconstexpr |
Code 4: the device needs setting up first.
Definition at line 320 of file proto_constants.h.
|
staticconstexpr |
Node modified the requested target value.
Definition at line 300 of file proto_constants.h.
|
staticconstexpr |
Requested target not reachable.
Definition at line 307 of file proto_constants.h.
|
staticconstexpr |
Node entered thermal protection mode.
Definition at line 297 of file proto_constants.h.
|
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 331 of file proto_constants.h.
|
staticconstexpr |
Device returned an unknown status reply.
Definition at line 283 of file proto_constants.h.
|
staticconstexpr |
User action overrode the command.
Definition at line 303 of file proto_constants.h.
|
staticconstexpr |
Wrong load connected to node.
Definition at line 305 of file proto_constants.h.
|
staticconstexpr |
Node reports wrong position.
Definition at line 311 of file proto_constants.h.
|
staticconstexpr |
Node contains the wrong system key.
Definition at line 288 of file proto_constants.h.
|
inlineconstexpr |
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 106 of file roll_call.h.
|
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 358 of file proto_device_model.h.
|
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 353 of file proto_device_model.h.
|
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.
|
inlineconstexpr |
Definition at line 72 of file roll_call.h.
|
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 71 of file roll_call.h.
|
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.
|
staticconstexpr |
Exchange tries for a status poll the scheduler owns — every status poll issued while StatusPollPolicy is tracking the device, which today is every status poll this component can produce (there is no user-facing "refresh status" button or action; if one is ever added, it must not take this branch).
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 EXCHANGE_RETRY_COUNT blocking in-exchange tries on top of it buys no freshness and costs ~1.6 s of blocked loop() while a device is unresponsive (e.g. an actuator mid-manoeuvre) — the settle 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. A poll with no ladder behind it keeps the full EXCHANGE_RETRY_COUNT. SCHEDULED_POLL_RETRY_GRACE_FIRST_FAILURE and STOP_SETTLE_POLL_TRIES below are the two places this trade-off is deliberately bought back.
Definition at line 178 of file proto_timing.h.
|
staticconstexpr |
Ladder positions at which a scheduler-owned status poll gets the full EXCHANGE_RETRY_COUNT back.
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() for the full retry product 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 400 ms listen samples its receive window once; EXCHANGE_RETRY_COUNT tries sample it three times, ~870 ms apart, 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. An auth-shaped streak is excluded entirely: a device that answers with a 0x3C challenge is awake, so extra tries buy no wake-up, and an auth try is the most expensive shape the engine runs.
Definition at line 196 of file proto_timing.h.
|
staticconstexpr |
Definition at line 197 of file proto_timing.h.
|
staticconstexpr |
Definition at line 107 of file tuning_registry.cpp.
|
staticconstexpr |
Protocol default and floor for continuation frames.
Definition at line 35 of file proto_timing.h.
|
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.
|
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.
Definition at line 89 of file radio_soft_phy_driver_base.h.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
staticconstexpr |
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 110 of file radio_soft_phy_driver_base.h.
|
staticconstexpr |
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 119 of file radio_soft_phy_driver_base.h.
|
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.
|
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 252 of file tuning_config.h.
|
staticconstexpr |
Microseconds in a second, for the air-time arithmetic below.
Definition at line 73 of file radio_soft_phy.h.
|
staticconstexpr |
Steady-state after repeated auth failures.
Definition at line 33 of file status_poll_policy.h.
|
staticconstexpr |
First retry after a challenge-seen failure.
Definition at line 31 of file status_poll_policy.h.
|
staticconstexpr |
Second retry.
Definition at line 32 of file status_poll_policy.h.
|
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 424 of file proto_constants.h.
|
staticconstexpr |
Right shift that brings STATUS_BATTERY_MASK down to 0-3.
Definition at line 425 of file proto_constants.h.
|
staticconstexpr |
Byte 1 bit 7: device will send auto status update.
Definition at line 419 of file proto_constants.h.
|
staticconstexpr |
FPI1: FP1..FP7 requested (bit 7 = FP1 ... bit 1 = FP7).
Definition at line 446 of file proto_constants.h.
|
staticconstexpr |
FPI2: no FP9-FP16 requested.
Definition at line 449 of file proto_constants.h.
|
staticconstexpr |
FP1-FP7: the most one request can select.
Definition at line 448 of file proto_constants.h.
|
staticconstexpr |
Status type per requested parameter: current value.
Definition at line 447 of file proto_constants.h.
|
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 327 of file proto_device_model.h.
|
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 331 of file proto_device_model.h.
|
staticconstexpr |
Steady-state backoff after many silent failures.
Definition at line 25 of file status_poll_policy.h.
|
staticconstexpr |
First retry after a silent failure.
Definition at line 21 of file status_poll_policy.h.
|
staticconstexpr |
Second retry after a silent failure.
Definition at line 22 of file status_poll_policy.h.
|
staticconstexpr |
Third retry after a silent failure.
Definition at line 23 of file status_poll_policy.h.
|
staticconstexpr |
Fourth retry after a silent failure.
Definition at line 24 of file status_poll_policy.h.
|
staticconstexpr |
Status byte flags in CMD_PRIVATE_RESP and CMD_STATUS_UPDATE.
Byte 0 bit 0: device is not moving
Definition at line 418 of file proto_constants.h.
|
staticconstexpr |
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 426 of file proto_constants.h.
|
inlineconstexpr |
Definition at line 333 of file proto_codecs.h.
|
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 341 of file proto_codecs.h.
|
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 device hint.
Definition at line 73 of file status_poll_policy.h.
|
staticconstexpr |
Exchange tries for the settle poll after an accepted STOP.
The single-try rule above protects a STOP pressed during a manoeuvre from a blocking poll. After an accepted STOP nothing is moving, and the user's next action — reversing the cover — waits on exactly this poll, because until it answers the entity still shows the last reported position. A single try bets that on one sample of a receiver whose state right after a STOP is uncertain (still running down, awake, or already back on its duty cycle); a miss costs the full STATUS_RETRY_AFTER_FAIL_MS backoff before the next look. The full budget samples it up to three times within one exchange (under EXCHANGE_TOTAL_BUDGET_MS), and blocks only when all of them miss.
Definition at line 208 of file proto_timing.h.
|
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 31 of file proto_device_types.h.
|
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 27 of file proto_device_types.h.
|
staticconstexpr |
How long SoftPhyDriverBase::wait_busy_() waits for BUSY to drop before declaring the chip failed.
Short: this chip's RC-oscillator-clocked commands settle quickly, unlike LR1121's post-reset boot ROM (see LR1121_BUSY_TIMEOUT_MS in radio_lr1121.h).
Definition at line 162 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 52 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 53 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 43 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 42 of file radio_sx1262.h.
|
staticconstexpr |
ADC calibration failed.
Definition at line 91 of file radio_sx1262.h.
|
staticconstexpr |
Image calibration failed.
Definition at line 92 of file radio_sx1262.h.
|
staticconstexpr |
PA ramping failed (bit 0x0080 is unused).
Definition at line 95 of file radio_sx1262.h.
|
staticconstexpr |
PLL calibration failed.
Definition at line 90 of file radio_sx1262.h.
|
staticconstexpr |
PLL failed to lock.
Definition at line 94 of file radio_sx1262.h.
|
staticconstexpr |
13 MHz RC oscillator calibration failed.
Definition at line 89 of file radio_sx1262.h.
|
staticconstexpr |
64 kHz RC oscillator calibration failed.
Definition at line 88 of file radio_sx1262.h.
|
staticconstexpr |
Crystal/TCXO failed to start.
Definition at line 93 of file radio_sx1262.h.
|
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 223 of file tuning_config.h.
|
staticconstexpr |
Definition at line 157 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 41 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 39 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 40 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 55 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 54 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 57 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 156 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 155 of file radio_sx1262.h.
|
staticconstexpr |
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.
|
staticconstexpr |
Definition at line 71 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 69 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 68 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 70 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 67 of file radio_sx1262.h.
|
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 196 of file tuning_config.h.
|
staticconstexpr |
Definition at line 37 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 61 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 132 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 131 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 133 of file radio_sx1262.h.
|
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 143 of file radio_sx1262.h.
|
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.
8 bytes was validated on real hardware (Heltec V3.2 ↔ Device actuator) as the minimum that gives reliable lock-on without perturbing exchange timing; it matches the protocol's own nominal SHORT_PREAMBLE floor.
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 188 of file tuning_config.h.
|
staticconstexpr |
Definition at line 153 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 47 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 50 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 51 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 38 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 45 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 48 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 46 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 44 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 56 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 34 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 32 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 35 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 31 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 33 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 49 of file radio_sx1262.h.
|
static |
Definition at line 31 of file radio_sx1262.cpp.
|
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 152 of file radio_sx1262.h.
|
staticconstexpr |
Bit 2 of SX1262_REG_TX_MODULATION — the (G)FSK-correct value is 1.
Definition at line 147 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 36 of file radio_sx1262.h.
|
staticconstexpr |
Definition at line 60 of file radio_sx1262.h.
|
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.
Definition at line 27 of file radio_sx1276.h.
|
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 209 of file tuning_config.h.
|
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 20 of file radio_sx1276.h.
|
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 203 of file tuning_config.h.
|
staticconstexpr |
Definition at line 17 of file device_registry.cpp.
|
static |
Definition at line 28 of file exchange_engine.cpp.
|
static |
Definition at line 47 of file hub_core.cpp.
|
static |
Definition at line 20 of file platform_climate.cpp.
|
static |
Definition at line 12 of file platform_cover.cpp.
|
static |
Definition at line 18 of file platform_fan.cpp.
|
static |
Definition at line 12 of file platform_light.cpp.
|
static |
Definition at line 13 of file platform_lock.cpp.
|
static |
Definition at line 12 of file platform_switch.cpp.
|
static |
Definition at line 14 of file radio_interface.cpp.
|
static |
Definition at line 27 of file radio_lr1121.cpp.
|
static |
Definition at line 24 of file radio_soft_phy_driver_base.cpp.
|
static |
Definition at line 30 of file radio_sx1262.cpp.
|
static |
Definition at line 20 of file radio_sx1276.cpp.
|
static |
Definition at line 18 of file platform_cover_controls.cpp.
|
static |
Definition at line 15 of file platform_cover_controls.cpp.
|
static |
Definition at line 14 of file platform_climate_controls.cpp.
|
static |
Definition at line 17 of file platform_cover_controls.cpp.
|
static |
Definition at line 16 of file platform_cover_controls.cpp.
|
static |
Definition at line 13 of file platform_climate_controls.cpp.
|
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.
|
staticconstexpr |
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 489 of file proto_constants.h.
|
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.
|
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.
|
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 141 of file proto_timing.h.
|
staticconstexpr |
Gap before that re-send, in place of EXCHANGE_RETRY_DELAY_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.
Definition at line 147 of file proto_timing.h.
|
staticconstexpr |
Sentinel value meaning "position is not known yet".
Matches POS_UNKNOWN (0xD4 = 212 decimal) for easy debugging.
Definition at line 347 of file proto_device_model.h.
|
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.
|
staticconstexpr |
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(); matches samr037/iohc-flipper's PAIR_DST_{WINDOW,SHUTTER,OTHER}).
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 182 of file oneway_controller.h.