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

Classes

struct  LastCommandRecord
 One decoded last-command record. More...

Functions

uint8_t round_percent (float fraction)
 Convert a 0.0-1.0 HA fraction (position, tilt, or brightness) to a 0-100 IO percent.
bool is_binary_entity_position (uint8_t position)
 Is the given position value an on/off binary encoding?
bool known_device_matches_entity_class (const IoDevice &dev, DeviceCapabilityClass expected)
 Does the device's type match the expected HA entity class?
bool known_device_supports_status_requests (const IoDevice &dev)
 Does the device support status requests?
bool known_device_accepts_execute_position (const IoDevice &dev, uint8_t position)
 Can this device accept an execute (position) command?
bool known_device_accepts_execute_tilt (const IoDevice &dev)
 Can this device accept a tilt command?
void log_rejected_operation (const std::string &device_id, const IoDevice &dev, const char *operation, const char *expected)
 Log a rejected operation with capability mismatch details.
void log_component_capture (const RadioDriver *radio, const char *stage, const uint8_t *buf, uint8_t len, const IoFrame *frame=nullptr)
 Log a frame at the "io_capture" tag with structured fields.
void log_frame_issue (IOHomeControlComponent *component, const char *direction, const char *reason, const IoFrame &frame, uint8_t len)
 Log a frame‑level issue (unregistered endpoints, unsupported commands).
void log_1w_remote_frame (const OneWayFrameInfo &info, const std::vector< std::string > *linked_devices=nullptr)
 Log an already-decoded 1W remote frame at DEBUG level.
bool is_exposed_sender (const std::vector< std::string > &exposed_senders, const std::string &sender_id)
 Whether a 1W sender is on the exposed_senders allowlist for the sender HA event.
std::string format_name_and_hex (const char *name, uint8_t value)
 Format a name/value pair as "name(0xXX)", e.g. "execute(0x00)".
std::string describe_learned_device_type (DeviceType type)
 Build the YAML line to add once a device's type is learned at runtime.
std::map< std::string, std::string > build_sender_event_data (const OneWayFrameInfo &info, bool linked)
 Build the Home Assistant event data map for a decoded 1W sender frame.
std::string format_key_hex (const uint8_t key[AES_KEY_SIZE])
 Format a 16-byte key as an uppercase, unseparated hex string for display.
void log_multiline_result (const char *tag, bool is_warning, const std::string &prefix, const std::string &message)
 Log prefix followed by message, one line per log call rather than one call for the whole (possibly multi-line) string.
const char * oneway_mac_status_name (OneWayMacStatus status)
 Human-readable name for a decoded 0x30's MAC-verification outcome.
std::string build_oneway_adoption_report (const OneWayAdoptedKey &adopted, bool observed_type_known, DeviceType observed_type)
 Build the full 1W controller-key-adoption report: MAC-verification status, the own-address transmission rationale, and the ready-to-paste oneway_controllers: YAML block.
std::string build_key_extraction_report (const uint8_t node_id[NODE_ID_SIZE], const uint8_t key[AES_KEY_SIZE])
 Build the ready-to-paste 2W system-key-extraction report: node_id:/system_key: as a home_io_control: YAML block.
void normalize_stopped_state (IoDevice &dev)
 Normalize stopped state: some devices briefly report stopped before target/current converge.
void update_link_health (IoDevice &dev, RadioDriver *radio)
 Update per-device link-health stats from the radio's last capture.
void record_exchange_timeout (IoDevice &dev, uint8_t tries)
 Record that an outbound exchange to this device timed out (no valid response).
LastCommandRecord decode_last_command_record (const IoFrame &frame, uint8_t base)
 Decode the last-command record at base from a status-bearing payload.
void apply_last_command_record (IoDevice &dev, const LastCommandRecord &record)
 Store a decoded record on the device, if it is valid.
std::string describe_last_commander (const IoDevice &dev, const uint8_t *hub_node_id)
 Render the "Last Commanded By" sensor string.
std::string describe_last_command_source (const IoDevice &dev)
 Render the "Last Command Source" sensor string.
std::string describe_status_update_originator (const IoFrame &frame)
 Describe a 0x71 status update's Command Originator as "name(0xXX)".
std::string describe_prediction (const IoDevice &dev)
 Describe the hub's live optimistic predictions where they disagree with the observation.
void log_status_update (const std::string &id, const IoDevice &dev, const char *suffix="")
 Log a concise status‑update line used by inbound handlers.
void log_command_result (const std::string &id, uint8_t result, uint8_t request_cmd=0, bool include_request_cmd=false)
 Log a decoded CMD_ERROR_RESP result with optional request-command context.
void record_command_result (IoDevice &dev, const std::string &id, uint8_t result, uint8_t request_cmd=0, bool include_request_cmd=false)
 Store a decoded CMD_ERROR_RESP result on the device and log it.
void clear_command_result (IoDevice &dev)
 Clear a previously recorded CMD_ERROR_RESP result, if any.

Variables

constexpr const char * TAG = "home_io_control"
 Shared log tag for hub-level messages.
constexpr uint32_t ONEWAY_DEDUP_WINDOW_MS = 2000
 Suppress a repeated 1W log/poll for the same remote and the same intent within this window.
constexpr float BINARY_ENTITY_ON_POSITION_THRESHOLD
 Shared 0-100 cutoff: values below this mean binary "on".
constexpr const char * ONEWAY_SENDER_EVENT = "esphome.home_io_control_sender_event"
 Home Assistant event fired when a decoded 1W frame carries a command intent from an exposed sender (a physical remote button press, or a wind/rain sensor's triggered command).
constexpr size_t NAME_AND_HEX_BUFFER_SIZE = 40
 Buffer size for format_name_and_hex(): longest command name plus "(0xXX)" and a margin.
constexpr size_t LEARNED_DEVICE_TYPE_HEX_BUFFER_SIZE = 24
 Buffer size for describe_learned_device_type()'s hex fallback: "io_device_type: 0xXX" plus margin.
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.

Function Documentation

◆ apply_last_command_record()

void esphome::home_io_control::detail::apply_last_command_record ( IoDevice & dev,
const LastCommandRecord & record )
inline

Store a decoded record on the device, if it is valid.

A short or unpopulated payload leaves whatever was last learned in place rather than clearing it: the record is inherently last-writer-wins and only refreshes when something commands the device, so a stale value is the honest answer and a blanked one is not.

Definition at line 604 of file hub_internal.h.

◆ build_key_extraction_report()

std::string esphome::home_io_control::detail::build_key_extraction_report ( const uint8_t node_id[NODE_ID_SIZE],
const uint8_t key[AES_KEY_SIZE] )
inline

Build the ready-to-paste 2W system-key-extraction report: node_id:/system_key: as a home_io_control: YAML block.

Pure — takes already-decoded values, performs no I/O — so it is directly unit-testable without a live radio, mirroring build_oneway_adoption_report() above; the two features end up sharing report structure as well as format_key_hex(). The caller (key_extraction_responder.cpp) logs the result through log_multiline_result() and nowhere else — this is the single intentional place the recovered system_key is formatted for display, a deliberate exception to redaction.h's masking.

The emitted keys must track the hub's own CONFIG_SCHEMA (__init__.py) by hand. make yaml-emitter-sync (scripts/check-yaml-emitters.py) catches drift between the two by cross-referencing this function's emitted key names against that schema statically.

Parameters
node_idRecovered hub node_id, 3 bytes.
keyRecovered system key, 16 bytes.
Returns
Multi-line report text, ready to pass to log_multiline_result().

Definition at line 463 of file hub_internal.h.

Here is the call graph for this function:

◆ build_oneway_adoption_report()

std::string esphome::home_io_control::detail::build_oneway_adoption_report ( const OneWayAdoptedKey & adopted,
bool observed_type_known,
DeviceType observed_type )
inline

Build the full 1W controller-key-adoption report: MAC-verification status, the own-address transmission rationale, and the ready-to-paste oneway_controllers: YAML block.

Pure — takes already-decoded values, performs no I/O — so it is directly unit-testable without a live radio or a captured log line (ESP_LOG's host stub discards its arguments). This is the single intentional place adopted.system_key is formatted for display (via format_key_hex() above); the caller (oneway_key_adoption.cpp) passes the returned text to one ESP_LOGW(...,"%s",...) call and nowhere else.

node_id is deliberately never mentioned as something to fill in — a later step derives one from the hub's own node ID, and the report says so rather than asking the user to invent a 3-byte address. The report also explains that the hub always transmits under its own address: impersonating the sender would hijack that remote's rolling sequence counter and break it.

The emitted keys must track ONEWAY_CONTROLLER_SCHEMA (__init__.py) by hand — a newly required schema key needs a matching line here too. make yaml-emitter-sync (scripts/check-yaml-emitters.py) catches drift between the two statically; it does not tell you what to add here.

Parameters
adoptedDecoded controller identity from decode_1w_add_controller() (proto_codecs.h).
observed_type_knownTrue if this sender's other 1W traffic was observed while armed (see OnewayKeyAdoption::record_observed_class()); false prints a commented-out fallback pointing at the DEBUG log line that would reveal it instead.
observed_typeThe observed target class; only meaningful when observed_type_known.
Returns
Multi-line report text, ready to pass straight to a single ESP_LOGW(...,"%s",...) call.

Definition at line 397 of file hub_internal.h.

Here is the call graph for this function:

◆ build_sender_event_data()

std::map< std::string, std::string > esphome::home_io_control::detail::build_sender_event_data ( const OneWayFrameInfo & info,
bool linked )
inline

Build the Home Assistant event data map for a decoded 1W sender frame.

Only meaningful when info.has_intent is true (the caller gates emission on that); the intent field is only populated by decode_1w_frame() in that case.

Parameters
infoAlready-decoded 1W frame info (see decode_1w_frame()).
linkedTrue if this sender is linked to at least one registered device.
Returns
Event data map ready for fire_homeassistant_event().

Definition at line 279 of file hub_internal.h.

Here is the call graph for this function:

◆ clear_command_result()

void esphome::home_io_control::detail::clear_command_result ( IoDevice & dev)
inline

Clear a previously recorded CMD_ERROR_RESP result, if any.

A stale limitation reason (e.g. a rain lockout from an hour ago) is worse than none once the device has since replied normally, so every successful status/command reply for a device clears it. Called from the CMD_PRIVATE_RESP and CMD_STATUS_UPDATE branches of update_device_status_() — not from CMD_GET_NAME_RESP/CMD_GET_INFO2_RESP, which are metadata lookups unrelated to whether the device's last movement command succeeded. Like record_command_result(), does not notify subscribers itself — both existing call sites clear before their own notify_device_update_() call, which is what actually publishes this change.

Parameters
devDevice to clear.

Definition at line 747 of file hub_internal.h.

◆ decode_last_command_record()

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

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

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

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

Definition at line 587 of file hub_internal.h.

◆ describe_last_command_source()

std::string esphome::home_io_control::detail::describe_last_command_source ( const IoDevice & dev)
inline

Render the "Last Command Source" sensor string.

Uses the same "name(0xXX)" rendering as describe_status_update_originator(), so a byte with no ORIGINATOR_* case reads "unknown(0x0A)" rather than being silently dropped or mislabelled. The decode is field-validated for roller shutters (a clean 0x00/0x01 split, remote vs. motor button); gates, lights and multi-channel units are not validated and are expected to surface undecoded values here — which is the point of keeping the raw hex in the string.

Parameters
devDevice record to read.
Returns
e.g. "user_remote(0x01)"; empty before the first record.

Definition at line 643 of file hub_internal.h.

Here is the call graph for this function:

◆ describe_last_commander()

std::string esphome::home_io_control::detail::describe_last_commander ( const IoDevice & dev,
const uint8_t * hub_node_id )
inline

Render the "Last Commanded By" sensor string.

Always leads with the raw node ID — that is the diagnostic value, and the only thing a user can match against a remote they own. The qualifier is additive, never a substitute: a device naming its own ID is NOT reliably "the button on the motor" (the one non-shutter this project has data on, a mains gate, names its own ID with an undefined originator), so the cause belongs to the separate originator sensor, not to this one's wording.

Parameters
devDevice record to read.
hub_node_idThis hub's own 3-byte node ID.
Returns
e.g. "3B74DC", "C0FFEE (this hub)", "2FE2D2 (this device)"; empty before the first record.

Definition at line 622 of file hub_internal.h.

Here is the call graph for this function:

◆ describe_learned_device_type()

std::string esphome::home_io_control::detail::describe_learned_device_type ( DeviceType type)
inline

Build the YAML line to add once a device's type is learned at runtime.

Logged when io_device_type was left unset in YAML and an INFO2 response just resolved it for the first time this boot (ADR 0018: nothing persists, so this repeats on every reboot until the user copies the line in). Reuses yaml_device_type_name() so the exact syntax always matches what the pairing snippet and the Python schema accept.

Parameters
typeThe now-known device type. Must not be DeviceType::UNKNOWN.
Returns
The YAML line to add, e.g. io_device_type: "venetian_blind" or io_device_type: 0x11.

Definition at line 263 of file hub_internal.h.

Here is the call graph for this function:

◆ describe_prediction()

std::string esphome::home_io_control::detail::describe_prediction ( const IoDevice & dev)
inline

Describe the hub's live optimistic predictions where they disagree with the observation.

Pure rather than inlined into log_status_update() so it is testable (ESP_LOG* is a no-op stub in host tests). log_status_update() runs from the inbound frame handlers and reports what the device said; a hub-side prediction must never be substituted into those observed fields, so it is appended as a clearly-labelled annotation instead. Only terms that actually differ are rendered — a prediction that merely confirms the observation adds nothing.

Parameters
devDevice record to read.
Returns
e.g. " [predicted: target=100% stopped]", or an empty string when no prediction stands or every prediction agrees with what the device reported.

Definition at line 674 of file hub_internal.h.

Here is the call graph for this function:

◆ describe_status_update_originator()

std::string esphome::home_io_control::detail::describe_status_update_originator ( const IoFrame & frame)
inline

Describe a 0x71 status update's Command Originator as "name(0xXX)".

Pure rather than inlined into the log line it feeds, so the offset is testable: ESP_LOG* is a no-op stub in host tests, which makes the rendered line itself unobservable.

Parameters
frameA CMD_STATUS_UPDATE frame.
Returns
"name(0xXX)", e.g. "user_remote(0x01)", or an empty string when the payload is too short to carry the byte. Shorter frames still carry usable position data — the branch gate is STATUS_UPDATE_MIN_DATA_LEN (11) — they just have no originator to report.

Definition at line 657 of file hub_internal.h.

Here is the call graph for this function:

◆ format_key_hex()

std::string esphome::home_io_control::detail::format_key_hex ( const uint8_t key[AES_KEY_SIZE])
inline

Format a 16-byte key as an uppercase, unseparated hex string for display.

The one deliberate place system-key bytes are formatted for display, shared by both key-recovery features so neither forks its own copy: 2W "Accept Foreign Pairing" (key_extraction_responder.cpp::KeyExtractionResponder::log_result_()) and 1W controller-key adoption (build_oneway_adoption_report() below). See redaction.h for the masking rules this intentionally does not apply to — both callers are the deliberate exception, not a loosening of it.

Parameters
keyPointer to AES_KEY_SIZE key bytes.
Returns
Uppercase hex string, e.g. "0102030405060708090A0B0C0D0E0F10".

Definition at line 306 of file hub_internal.h.

◆ format_name_and_hex()

std::string esphome::home_io_control::detail::format_name_and_hex ( const char * name,
uint8_t value )
inline

Format a name/value pair as "name(0xXX)", e.g. "execute(0x00)".

Definition at line 246 of file hub_internal.h.

◆ is_binary_entity_position()

bool esphome::home_io_control::detail::is_binary_entity_position ( uint8_t position)
inline

Is the given position value an on/off binary encoding?

Parameters
positionPosition value to test.
Returns
true if position equals BINARY_ENTITY_ON_POSITION or BINARY_ENTITY_OFF_POSITION.

Definition at line 66 of file hub_internal.h.

◆ is_exposed_sender()

bool esphome::home_io_control::detail::is_exposed_sender ( const std::vector< std::string > & exposed_senders,
const std::string & sender_id )
inline

Whether a 1W sender is on the exposed_senders allowlist for the sender HA event.

"Sender" covers both remotes and wind/rain sensors — they use the identical 1W broadcast mechanism and differ only in the originator byte inside the payload, not in addressing. Overheard 1W traffic is always DEBUG-logged regardless of this check (see log_1w_remote_frame()); this only gates whether the event reaches Home Assistant. Deliberately separate from linked_devices — a sender can be event-enabled without controlling any registered device (e.g. to trigger an HA automation with no matching cover/light/switch), or vice versa.

Parameters
exposed_sendersConfigured allowlist (exposed_senders YAML key, empty by default).
sender_idNode ID of the 1W sender that sent the frame.
Returns
true if the sender is in the allowlist.

Definition at line 238 of file hub_internal.h.

◆ known_device_accepts_execute_position()

bool esphome::home_io_control::detail::known_device_accepts_execute_position ( const IoDevice & dev,
uint8_t position )
inline

Can this device accept an execute (position) command?

Checks capability and, for unknown types, allows binary positions for light/switch.

Parameters
devIoDevice to check.
positionPosition value being sent.
Returns
true if operation is appropriate for this device type.

Definition at line 92 of file hub_internal.h.

Here is the call graph for this function:

◆ known_device_accepts_execute_tilt()

bool esphome::home_io_control::detail::known_device_accepts_execute_tilt ( const IoDevice & dev)
inline

Can this device accept a tilt command?

Parameters
devIoDevice to check.
Returns
true only if device type is known to support tilt.

Definition at line 110 of file hub_internal.h.

Here is the call graph for this function:

◆ known_device_matches_entity_class()

bool esphome::home_io_control::detail::known_device_matches_entity_class ( const IoDevice & dev,
DeviceCapabilityClass expected )
inline

Does the device's type match the expected HA entity class?

UNKNOWN devices always match to keep imported/discovered devices working.

Parameters
devIoDevice to check.
expectedDesired capability class (COVER, LIGHT, SWITCH, etc.).
Returns
true if device type matches or is UNKNOWN.

Definition at line 75 of file hub_internal.h.

Here is the call graph for this function:

◆ known_device_supports_status_requests()

bool esphome::home_io_control::detail::known_device_supports_status_requests ( const IoDevice & dev)
inline

Does the device support status requests?

UNKNOWN devices pass through.

Parameters
devIoDevice to check.
Returns
true if device type supports status requests or is UNKNOWN.

Definition at line 83 of file hub_internal.h.

Here is the call graph for this function:

◆ log_1w_remote_frame()

void esphome::home_io_control::detail::log_1w_remote_frame ( const OneWayFrameInfo & info,
const std::vector< std::string > * linked_devices = nullptr )
inline

Log an already-decoded 1W remote frame at DEBUG level.

Formats a concise DEBUG log line showing remote ID, target type, command intent, and priority. When the remote is linked to devices, appends the linked device IDs. Takes the already-decoded OneWayFrameInfo so callers that also build a HA event (see build_sender_event_data()) decode the frame once, not twice.

Parameters
infoAlready-decoded 1W frame info (see decode_1w_frame()).
linked_devicesOptional pointer to device IDs this remote is linked to.

Definition at line 193 of file hub_internal.h.

Here is the call graph for this function:

◆ log_command_result()

void esphome::home_io_control::detail::log_command_result ( const std::string & id,
uint8_t result,
uint8_t request_cmd = 0,
bool include_request_cmd = false )
inline

Log a decoded CMD_ERROR_RESP result with optional request-command context.

Parameters
idDevice ID.
resultResult byte from CMD_ERROR_RESP data[0].
request_cmdOriginal outbound request command when known.
include_request_cmdTrue to include request_cmd in the log line.

Definition at line 706 of file hub_internal.h.

Here is the call graph for this function:

◆ log_component_capture()

void esphome::home_io_control::detail::log_component_capture ( const RadioDriver * radio,
const char * stage,
const uint8_t * buf,
uint8_t len,
const IoFrame * frame = nullptr )
inline

Log a frame at the "io_capture" tag with structured fields.

Used for protocol‑level debugging (phases: component, tx, rx, parse_ok/parse_fail).

Parameters
radioRadio driver instance (provides chip name and capture).
stageString label for the current phase.
bufRaw bytes being logged.
lenLength of buf.
frameOptional parsed IoFrame for decoded fields (cmd, src, dst).

Definition at line 137 of file hub_internal.h.

Here is the call graph for this function:

◆ log_frame_issue()

void esphome::home_io_control::detail::log_frame_issue ( IOHomeControlComponent * component,
const char * direction,
const char * reason,
const IoFrame & frame,
uint8_t len )
inline

Log a frame‑level issue (unregistered endpoints, unsupported commands).

Parameters
componentPointer to the component (for device lookup).
direction"tx" or "rx".
reasonShort issue label (e.g., "unregistered_device").
frameParsed frame.
lenSerialized length.

Definition at line 163 of file hub_internal.h.

Here is the call graph for this function:

◆ log_multiline_result()

void esphome::home_io_control::detail::log_multiline_result ( const char * tag,
bool is_warning,
const std::string & prefix,
const std::string & message )
inline

Log prefix followed by message, one line per log call rather than one call for the whole (possibly multi-line) string.

ESPHome formats each log call into a fixed 512-byte buffer (ESPHOME_LOGGER_TX_BUFFER_SIZE, esphome/core/defines.h) and silently truncates anything longer; a multi-line report (a YAML snippet plus explanatory prose) routinely exceeds that and truncates mid-line if logged as a single call — confirmed on real hardware for both call sites this function serves: scan_paired_devices()'s report (a multi-device report cut off mid-snippet) and 1W controller-key adoption's report (the recovered system_key line itself never made it into the log at all). Splitting by line keeps every individual call's payload small regardless of how long the full message is. Shared rather than duplicated a third time — a second private copy is exactly how the 1W path ended up with the bug this fixes.

Parameters
tagLog tag.
is_warningTrue to log at WARN, false for INFO.
prefixPrepended to the message's first line only (e.g. "Management action X: ").
messageMessage to log; may contain embedded \n line breaks.

Definition at line 333 of file hub_internal.h.

◆ log_rejected_operation()

void esphome::home_io_control::detail::log_rejected_operation ( const std::string & device_id,
const IoDevice & dev,
const char * operation,
const char * expected )
inline

Log a rejected operation with capability mismatch details.

Parameters
device_idDevice ID string.
devIoDevice that rejected the command.
operationHuman‑readable operation name (e.g., "set position").
expectedExpected capability class or profile name.

Definition at line 123 of file hub_internal.h.

Here is the call graph for this function:

◆ log_status_update()

void esphome::home_io_control::detail::log_status_update ( const std::string & id,
const IoDevice & dev,
const char * suffix = "" )
inline

Log a concise status‑update line used by inbound handlers.

The position/target/motion fields report what the device observed — never a hub prediction. A diverging live prediction is appended by describe_prediction(), clearly labelled, not merged in.

Parameters
idDevice ID.
devCurrent device state.
suffixOptional suffix added after the state string (e.g., " (status update)").

Definition at line 695 of file hub_internal.h.

Here is the call graph for this function:

◆ normalize_stopped_state()

void esphome::home_io_control::detail::normalize_stopped_state ( IoDevice & dev)
inline

Normalize stopped state: some devices briefly report stopped before target/current converge.

Deliberately reads and writes observed fields only — never effective_*(). Its job is "the device said stopped but its own reported target and current disagree", a statement about observations; feeding it a prediction would push a guess back into an observed field. On the execute-ack path (trust_position = false) dev.target does not hold the commanded value, so this function does not use it to flip is_stopped back to false — effective_is_stopped() owns that.

Parameters
devDevice record to update (may clear is_stopped if positions differ).

Definition at line 486 of file hub_internal.h.

Here is the call graph for this function:

◆ oneway_mac_status_name()

const char * esphome::home_io_control::detail::oneway_mac_status_name ( OneWayMacStatus status)
inline

Human-readable name for a decoded 0x30's MAC-verification outcome.

Parameters
statusOutcome from decode_1w_add_controller() (see OneWayAdoptedKey::mac_status).
Returns
Short uppercase-style label used in both the summary log line and the report below.

Definition at line 360 of file hub_internal.h.

◆ record_command_result()

void esphome::home_io_control::detail::record_command_result ( IoDevice & dev,
const std::string & id,
uint8_t result,
uint8_t request_cmd = 0,
bool include_request_cmd = false )
inline

Store a decoded CMD_ERROR_RESP result on the device and log it.

Single place both CMD_ERROR_RESP call sites (the unsolicited status path and the reply to our own EXECUTE) route through, so the store-and-log policy cannot drift between them. Does not notify subscribers itself — callers already call notify_device_update_() once per handled frame; call it after this.

Parameters
devDevice that returned the result.
idDevice ID (for the log line).
resultResult byte from CMD_ERROR_RESP data[0].
request_cmdOriginal outbound request command when known.
include_request_cmdTrue to include request_cmd context in the log line.

Definition at line 730 of file hub_internal.h.

Here is the call graph for this function:

◆ record_exchange_timeout()

void esphome::home_io_control::detail::record_exchange_timeout ( IoDevice & dev,
uint8_t tries )
inline

Record that an outbound exchange to this device timed out (no valid response).

Called once per failed exchange from execute_request_and_update_()'s "no valid response" branch — the single place every device-directed exchange already reads ExchangeEngine::DebugInfo. Both counters saturate at UINT16_MAX instead of wrapping, matching PairingTelemetry's counters.

Parameters
devDevice the failed exchange was addressed to.
triesNumber of attempts the failed exchange made (DebugInfo::tries, 1-based).

Definition at line 539 of file hub_internal.h.

◆ round_percent()

uint8_t esphome::home_io_control::detail::round_percent ( float fraction)
inline

Convert a 0.0-1.0 HA fraction (position, tilt, or brightness) to a 0-100 IO percent.

Rounds rather than truncates: HA quantizes call values to 0-255 before they ever reach us, so its "50%" is 128/255=0.50196, not exactly 0.5 — a truncating cast compounds that quantization into a consistent ~1% bias, caught on real hardware in both platform_cover.cpp (position and tilt) and platform_light.cpp (brightness). Callers apply their own invert/complement logic (e.g. 1.0F - fraction) before calling this; it only owns the rounding.

Parameters
fractionValue in [0.0, 1.0].
Returns
Rounded 0-100 percent.

Definition at line 57 of file hub_internal.h.

◆ update_link_health()

void esphome::home_io_control::detail::update_link_health ( IoDevice & dev,
RadioDriver * radio )
inline

Update per-device link-health stats from the radio's last capture.

Called for every frame whose src is a registered device, regardless of command type or whether that command's own payload was well-formed — any such frame is real evidence the device is reachable and at this signal strength. The two call sites cover both ways such a frame arrives: update_device_status_() (inbound status path) and execute_request_and_update_()'s explicit-refusal branch (a CMD_ERROR_RESP reply to our own request, which returns before reaching the status path). Always stamps last_seen_ms; only touches the RSSI fields when radio is non-null and its last capture is valid (real drivers always populate a valid capture before a frame is handed off, but tests calling this path directly without a radio, or without exercising RX through it, must not crash or fabricate an RSSI).

Parameters
devDevice that sent the frame.
radioRadio driver to read the last capture from; may be nullptr.

Definition at line 509 of file hub_internal.h.

Here is the call graph for this function:

Variable Documentation

◆ BINARY_ENTITY_ON_POSITION_THRESHOLD

float esphome::home_io_control::detail::BINARY_ENTITY_ON_POSITION_THRESHOLD
inlineconstexpr
Initial value:
=
50.0F

Shared 0-100 cutoff: values below this mean binary "on".

Definition at line 41 of file hub_internal.h.

◆ LEARNED_DEVICE_TYPE_HEX_BUFFER_SIZE

size_t esphome::home_io_control::detail::LEARNED_DEVICE_TYPE_HEX_BUFFER_SIZE = 24
inlineconstexpr

Buffer size for describe_learned_device_type()'s hex fallback: "io_device_type: 0xXX" plus margin.

Definition at line 253 of file hub_internal.h.

◆ NAME_AND_HEX_BUFFER_SIZE

size_t esphome::home_io_control::detail::NAME_AND_HEX_BUFFER_SIZE = 40
inlineconstexpr

Buffer size for format_name_and_hex(): longest command name plus "(0xXX)" and a margin.

Definition at line 243 of file hub_internal.h.

◆ ONEWAY_DEDUP_WINDOW_MS

uint32_t esphome::home_io_control::detail::ONEWAY_DEDUP_WINDOW_MS = 2000
inlineconstexpr

Suppress a repeated 1W log/poll for the same remote and the same intent within this window.

Wide on purpose: it collapses both the 4×/40ms reliability burst and a held button into one logical press. A different intent from the same remote (a stop after a move) is not a duplicate and passes through immediately — see decisions::is_duplicate_1w_frame().

Definition at line 40 of file hub_internal.h.

◆ ONEWAY_SENDER_EVENT

const char* esphome::home_io_control::detail::ONEWAY_SENDER_EVENT = "esphome.home_io_control_sender_event"
inlineconstexpr

Home Assistant event fired when a decoded 1W frame carries a command intent from an exposed sender (a physical remote button press, or a wind/rain sensor's triggered command).

Definition at line 224 of file hub_internal.h.

◆ PRIVATE_RESPONSE_LAST_COMMAND_OFFSET

uint8_t esphome::home_io_control::detail::PRIVATE_RESPONSE_LAST_COMMAND_OFFSET = 8
inlineconstexpr

Offset of the last-command record within each status-bearing payload.

Both status-bearing frame types carry the same 4-byte record — three bytes of node ID for the controller that last commanded the device, then that command's Command Originator byte — and 0x71's whole payload is shifted +3 relative to 0x04's, exactly as its target/current position fields already are (PRIVATE_RESPONSE_TARGET_OFFSET vs STATUS_UPDATE_TARGET_OFFSET in hub_status.cpp). 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 557 of file hub_internal.h.

◆ STATUS_UPDATE_LAST_COMMAND_OFFSET

uint8_t esphome::home_io_control::detail::STATUS_UPDATE_LAST_COMMAND_OFFSET = 11
inlineconstexpr

Definition at line 558 of file hub_internal.h.

◆ STATUS_UPDATE_ORIGINATOR_OFFSET

uint8_t esphome::home_io_control::detail::STATUS_UPDATE_ORIGINATOR_OFFSET = 14
inlineconstexpr

Offset of the Command Originator byte in a CMD_STATUS_UPDATE (0x71) payload.

Deliberately not offset 1: data[1] on a 0x71 is the status byte (0x60/0x61, bit 0 = current position unknown), which matches no ORIGINATOR_* value, so reading it as an originator rendered "unknown" on every frame this project has ever captured. Every captured 0x71 carries 0x01 (ORIGINATOR_USER_REMOTE) here.

Definition at line 566 of file hub_internal.h.

◆ TAG

const char* esphome::home_io_control::detail::TAG = "home_io_control"
inlineconstexpr

Shared log tag for hub-level messages.

Definition at line 35 of file hub_internal.h.