17#include "esphome/core/log.h"
49 return position == BINARY_ENTITY_ON_POSITION || position == BINARY_ENTITY_OFF_POSITION;
84 return position <= BINARY_ENTITY_OFF_POSITION;
106 const char *expected) {
107 ESP_LOGW(
TAG,
"Rejecting %s for device %s: type=%s (%u) class=%s profile=%s expected=%s", operation,
119 const IoFrame &frame, uint8_t len) {
122 const bool src_registered = component->
get_device(src_id) !=
nullptr;
123 const bool dst_registered = component->
get_device(dst_id) !=
nullptr;
125 if (src_registered || dst_registered) {
126 ESP_LOGW(
TAG,
"%s issue=%s cmd=%s(0x%02X) src=%s%s dst=%s%s len=%u data_len=%u", direction, reason,
127 command_name(frame.
cmd), frame.
cmd, src_id.c_str(), src_registered ?
" (registered)" :
"", dst_id.c_str(),
128 dst_registered ?
" (registered)" :
"", len, frame.
data_len);
132 ESP_LOGD(
TAG,
"%s issue=%s cmd=%s(0x%02X) src=%s dst=%s len=%u data_len=%u", direction, reason,
165 if (linked_devices !=
nullptr && !linked_devices->empty()) {
166 suffix =
" (linked →";
167 for (
const auto &dev_id : *linked_devices) {
175 ESP_LOGD(
TAG,
"rx 1W remote %s dst-class=%s: %s(0x%02X) %s originator=%s priority=%s%s", src_id.c_str(),
181 ESP_LOGD(
TAG,
"rx 1W remote %s dst-class=%s: %s(0x%02X) data_len=%u%s", src_id.c_str(), target_label,
201inline bool is_exposed_sender(
const std::vector<std::string> &exposed_senders,
const std::string &sender_id) {
202 return std::find(exposed_senders.begin(), exposed_senders.end(), sender_id) != exposed_senders.end();
219 return std::string(
"io_device_type: \"") + name +
"\"";
220 std::array<char, LEARNED_DEVICE_TYPE_HEX_BUFFER_SIZE> buffer{};
221 std::snprintf(buffer.data(), buffer.size(),
"io_device_type: 0x%02X",
static_cast<uint8_t
>(type));
222 return std::string(buffer.data());
241 {
"linked", linked ?
"true" :
"false"},
283 if (radio ==
nullptr)
315 static_cast<uint16_t
>(std::min<uint32_t>(
static_cast<uint32_t
>(dev.
exchange_attempt_count) + tries, UINT16_MAX));
379 if (!target_differs && !motion_differs)
381 std::string out =
" [predicted:";
409 bool include_request_cmd =
false) {
411 if (include_request_cmd) {
412 ESP_LOGW(
TAG,
"Device %s: %s (0x%02X) returned %s result=0x%02X %s (%s)",
id.c_str(),
command_name(request_cmd),
417 ESP_LOGW(
TAG,
"Device %s: explicit %s result=0x%02X %s (%s)",
id.c_str(), kind, result,
command_result_name(result),
433 bool include_request_cmd =
false) {
The main IO-Homecontrol component.
virtual IoDevice * get_device(const std::string &device_id)
Retrieve a device by ID; returns nullptr if not found.
Abstract radio driver for IO-Homecontrol.
const RadioCaptureInfo & get_last_capture() const
Get the most recent radio capture info.
Conversions and renderers shared by the hub and its Home Assistant entities.
IO-Homecontrol ESPHome component — protocol controller.
Shared frame logging helpers for IO-Homecontrol.
Hub-layer log tag and log/format helpers shared by the hub and its collaborators.
bool known_device_accepts_execute_tilt(const IoDevice &dev)
Can this device accept a tilt command?
bool known_device_matches_entity_class(const IoDevice &dev, DeviceCapabilityClass expected)
Does the device's type match the expected HA entity class?
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.
bool known_device_accepts_execute_position(const IoDevice &dev, uint8_t position)
Can this device accept an execute (position) command?
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::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.
constexpr const char * TAG
Shared log tag for hub-level messages.
std::string describe_learned_device_type(DeviceType type)
Build the YAML line to add once a device's type is learned at runtime.
bool known_device_supports_status_requests(const IoDevice &dev)
Does the device support status requests?
void apply_last_command_record(IoDevice &dev, const LastCommandRecord &record)
Store a decoded record on the device, if it is valid.
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.
void record_exchange_timeout(IoDevice &dev, uint8_t tries)
Record that an outbound exchange to this device timed out (no valid response).
std::string describe_status_update_originator(const IoFrame &frame)
Describe a 0x71 status update's Command Originator as "name(0xXX)".
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.
constexpr const char * ONEWAY_SENDER_EVENT
Home Assistant event fired when a decoded 1W frame carries a command intent from an exposed sender (a...
void update_link_health(IoDevice &dev, RadioDriver *radio)
Update per-device link-health stats from the radio's last capture.
void record_exchange_outcome(IoDevice &dev, uint8_t request_cmd, ExchangeOutcome outcome)
Record what an outbound exchange's ending says about the device, whichever way the caller then classi...
void normalize_stopped_state(IoDevice &dev)
Normalize stopped state: some devices briefly report stopped before target/current converge.
bool is_binary_entity_position(uint8_t position)
Is the given position value an on/off binary encoding?
constexpr uint32_t ONEWAY_DEDUP_WINDOW_MS
Suppress a repeated 1W log/poll for the same remote and the same intent within this window.
void log_status_update(const std::string &id, const IoDevice &dev, const char *suffix="")
Log a concise status‑update line used by inbound handlers.
constexpr size_t LEARNED_DEVICE_TYPE_HEX_BUFFER_SIZE
Buffer size for describe_learned_device_type()'s hex fallback: "io_device_type: 0xXX" plus margin.
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).
std::string describe_prediction(const IoDevice &dev)
Describe the hub's live optimistic predictions where they disagree with the observation.
void clear_command_result(IoDevice &dev)
Clear a previously recorded CMD_ERROR_RESP result, if any.
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.
std::string format_originator(uint8_t originator)
Render a Command Originator byte as "name(0xXX)", e.g.
const char * device_operation_profile_name(DeviceType type)
Human‑readable operation profile name for a device type.
DeviceType
Device type identifiers reported by IO‑Homecontrol products.
@ UNKNOWN
Unknown/unspecified device.
DeviceCapabilityClass device_capability_class(DeviceType type)
Map a raw IO‑Homecontrol type to the closest ESPHome/Home Assistant entity family.
float effective_target(const IoDevice &dev)
The main-position target a consumer should act on: the prediction when one stands,...
bool device_supports_position_control(DeviceType type)
Does this device type support precise position control (0–100)?
const char * oneway_target_label(const OneWayFrameInfo &info)
The destination label a decoded 1W frame renders as in a log line.
const char * command_name(uint8_t cmd)
Get a human-readable name for any IO-Homecontrol command ID.
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).
const char * device_type_name(DeviceType type)
Convert a DeviceType to a lowercase string identifier.
bool device_supports_binary_control(DeviceType type)
Does this device type support binary on/off control?
constexpr uint8_t STATUS_UPDATE_ORIGINATOR_OFFSET
Offset of the Command Originator byte in a CMD_STATUS_UPDATE (0x71) payload.
std::string format_position(float pos)
Format a position float as a human‑readable string (e.g.
bool device_supports_lock_control(DeviceType type)
Does this device type support binary lock/unlock control via execute commands?
const char * command_result_description(uint8_t result)
Return a human-readable explanation for a CMD_ERROR_RESP result code.
const char * command_result_name(uint8_t result)
Return a stable symbolic name for a CMD_ERROR_RESP result code.
ExchangeOutcome
Authenticated exchange engine — outbound and inbound protocol flows.
@ SUCCESS_WITH_RESPONSE
Device replied; the caller's response frame is populated.
@ SUCCESS_UNCONFIRMED
Device authenticated the request — so it received and accepted it — but sent no final response.
const char * device_capability_class_name(DeviceType type)
Get a human‑readable name for a capability class.
bool effective_is_stopped(const IoDevice &dev)
Whether a consumer should treat the device as at rest, prediction first.
const char * acei_level_name(uint8_t level)
Get a human-readable name for an ACEI priority level (0–7).
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.
const char * address_class_name(AddressClass address_class)
Get a human-readable name for an address classification.
bool device_supports_status_requests(DeviceType type)
Does this device type support status request commands (0x03)?
bool has_reached_target_position(float target, float position)
Has the device reached its target within tolerance?
const char * yaml_device_type_name(DeviceType type)
Return the YAML-friendly device-type name for types exposed in the Python schema.
DeviceCapabilityClass
High‑level capability class derived from DeviceType.
@ LIGHT
Binary on/off light.
bool is_limitation_result(uint8_t result)
Check whether a result code represents an environmental or control limitation.
const char * originator_name(uint8_t originator)
Get a human-readable name for a command originator byte.
bool device_supports_tilt(DeviceType type)
Does this device type support tilt (slat angle) control?
Device-name, address-classification and 1W-frame codecs.
Runtime state of a paired IO‑Homecontrol device.
uint32_t last_result_at_ms
millis() timestamp of last_result_code, 0 when none recorded.
float target
Target position the device is moving toward.
uint16_t exchange_attempt_count
Cumulative attempts (ExchangeEngine::DebugInfo::tries, 1-based per exchange) across those timed-out e...
uint16_t exchange_unconfirmed_count
Cumulative count of exchanges this device authenticated and then never closed: it answered with a 0x3...
uint8_t last_result_code
Last CMD_ERROR_RESP result byte (0 = none recorded).
uint8_t last_commander[NODE_ID_SIZE]
Node ID of the controller that last commanded this device, as reported verbatim by the device in its ...
uint8_t last_command_originator
That command's Command Originator byte (ORIGINATOR_* in proto_constants.h).
int16_t last_rssi_dbm
Most recent raw RSSI sample (dBm), or RSSI_UNKNOWN_DBM.
float position
Current position: 0=open, 100=closed, or UNKNOWN_POSITION.
uint32_t last_seen_ms
millis() of the last frame received from this device (any command), 0 = never.
int16_t rssi_ema_scaled
Smoothed RSSI as fixed point in 1/RSSI_EMA_SCALE dBm — read through device_rssi_ema_dbm(),...
bool has_last_command
True once a status reply carried a well-formed last-command record.
DeviceType type
Device type (shutter, awning, etc.).
uint16_t exchange_timeout_count
Cumulative count of outbound exchanges to this device with no valid response (see detail::record_exch...
bool is_stopped
True if device is not moving.
bool confirms_execute
True once this device has answered a CMD_EXECUTE with a reply that closed the exchange (a status,...
Parsed IO‑Homecontrol frame (CTRL0/1 + addresses + command + data).
uint8_t data[FRAME_MAX_DATA_SIZE]
Command parameters (0–23 bytes). Never includes mac.
uint8_t src[NODE_ID_SIZE]
Source node ID (3 bytes).
uint8_t dst[NODE_ID_SIZE]
Destination node ID (3 bytes).
uint8_t data_len
Actual length of data.
One decoded last-command record.
uint8_t commander[NODE_ID_SIZE]
Controller that last commanded the device.
bool valid
False when the payload was too short, or the record was unpopulated.
uint8_t originator
That command's Command Originator (ORIGINATOR_*).
Decoded representation of a 1W remote frame.
bool has_intent
True if originator/ACEI/intent fields were decoded.
uint8_t originator
Command originator byte (e.g., ORIGINATOR_USER_REMOTE).
DeviceType target_type
Target device class from broadcast address.
uint8_t acei_level
ACEI priority level (0–7).
uint8_t cmd
Command ID (e.g., CMD_EXECUTE, CMD_ACTIVATE_MODE).
AddressClass address_class
Classification of the broadcast address.
char intent[ONEWAY_INTENT_BUFFER_SIZE]
Human-readable command intent (e.g., "CLOSE").
uint8_t src[NODE_ID_SIZE]
Remote source node ID (3 bytes).
uint8_t data_len
Raw data length (for commands without decoded intent).
Diagnostic capture from a radio operation.
bool valid
True if capture is valid.
int16_t rssi_dbm
Received signal strength (dBm).