10#include "esphome/core/log.h"
15static constexpr const char *
TAG =
"home_io_control";
20 if (devices_.count(device_id) != 0)
24 ESP_LOGW(
TAG,
"Ignoring invalid device ID %s", device_id.c_str());
34 devices_[device_id] = dev;
40 auto it = devices_.find(device_id);
41 return (it != devices_.end()) ? &it->second :
nullptr;
57 auto it = devices_.find(device_id);
58 if (it == devices_.end())
60 for (
auto &cb : callbacks_)
61 cb(device_id, it->second);
65 linked_remotes_[remote_id].push_back(device_id);
69 auto it = linked_remotes_.find(remote_id);
70 return (it != linked_remotes_.end()) ? &it->second :
nullptr;
74 linked_remote_classes_[type].push_back(device_id);
78 auto it = linked_remote_classes_.find(type);
79 return (it != linked_remote_classes_.end()) ? &it->second :
nullptr;
83 auto it = devices_.find(device_id);
84 if (it == devices_.end() || !it->second.optimistic_state)
90 ESP_LOGD(
TAG,
"Device %s: optimistic target=%.0f (inverted=%s, last reported position=unknown)", device_id.c_str(),
91 target_io_position, YESNO(it->second.inverted));
93 ESP_LOGD(
TAG,
"Device %s: optimistic target=%.0f (inverted=%s, last reported position=%.0f)", device_id.c_str(),
94 target_io_position, YESNO(it->second.inverted), it->second.position);
96 it->second.optimistic.target = target_io_position;
103 auto it = devices_.find(device_id);
104 if (it == devices_.end() || !it->second.optimistic_state)
106 ESP_LOGD(
TAG,
"Device %s: optimistic stop", device_id.c_str());
114 auto it = devices_.find(device_id);
115 if (it == devices_.end() || !it->second.optimistic_state)
125 ESP_LOGD(
TAG,
"Device %s: optimistic tilt=%.0f (last reported tilt=unknown)", device_id.c_str(), tilt_percent);
127 ESP_LOGD(
TAG,
"Device %s: optimistic tilt=%.0f (last reported tilt=%.0f)", device_id.c_str(), tilt_percent,
130 it->second.optimistic.tilt = tilt_percent;
136 auto it = devices_.find(device_id);
137 if (it == devices_.end() || it->second.optimistic.empty())
139 ESP_LOGD(
TAG,
"Device %s: optimistic rollback (command failed)", device_id.c_str());
140 it->second.optimistic.clear();
146 const std::function<
void(
const std::string &,
const std::vector<std::string> &)> &fn)
const {
147 for (
const auto &pair : linked_remotes_)
148 fn(pair.first, pair.second);
const std::vector< std::string > * linked_devices(const std::string &remote_id) const
Retrieve the list of device IDs linked to a remote.
void set_dimmable(const std::string &device_id, bool dimmable)
Set a device's dimmable flag (see IoDevice::dimmable).
bool apply_optimistic_stop(const std::string &device_id)
Predict that a device has stopped (e.g.
bool apply_optimistic_tilt(const std::string &device_id, float tilt_percent)
Set an optimistic slat angle ahead of a confirming poll, and notify.
const std::vector< std::string > * linked_devices_for_class(DeviceType type) const
Retrieve the list of device IDs linked to a device class.
void add_linked_remote_class(DeviceType type, const std::string &device_id)
Record that a remote's typed-broadcast presses (e.g.
void put(const std::string &device_id, IoDevice device)
Insert or overwrite a device entry without deduplication or hex-validation checks.
bool apply_optimistic_target(const std::string &device_id, float target_io_position)
Set an optimistic target position ahead of a confirming poll/response, and notify.
void set_silent(const std::string &device_id, bool silent)
Select a device's travel profile.
void notify(const std::string &device_id)
Invoke all registered callbacks for device_id.
void add_linked_remote(const std::string &remote_id, const std::string &device_id)
Record that a remote node controls a registered device.
void subscribe(DeviceUpdateCallback cb)
Register a callback that fires whenever a device's state changes.
IoDevice * get(const std::string &device_id)
Retrieve a registered device by ID.
bool rollback_optimistic(const std::string &device_id)
Withdraw every prediction for a device after the command that produced them failed,...
void for_each_linked_remote(const std::function< void(const std::string &, const std::vector< std::string > &)> &fn) const
Invoke fn(remote_id, device_id_list) for every linked-remote entry.
void add(const std::string &device_id)
Register a device by ID with default metadata (UNKNOWN type, subtype 0, not inverted).
Per-hub device table, update-callback fan-out, and linked-remote map.
static constexpr float UNKNOWN_POSITION
Sentinel value meaning "position is not known yet".
static constexpr uint8_t NODE_ID_SIZE
Device/node addresses are 3 bytes (e.g., "123ABC").
DeviceType
Device type identifiers reported by IO‑Homecontrol products.
static constexpr const char * TAG
std::function< void(const std::string &device_id, const IoDevice &device)> DeviceUpdateCallback
Callback type invoked when a device's state changes.
bool device_supports_tilt(DeviceType type)
Does this device type support tilt (slat angle) control?
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.
IO-Homecontrol 2W frame container: control bytes, IoFrame and (de)serialization.
Fundamental IO-Homecontrol frame and crypto size constants.
YAML-declared device metadata for registration; defaults match an undeclared device.
bool optimistic_state
Whether apply_optimistic_target/apply_optimistic_stop may fire.
bool inverted
Position-inversion flag.
bool low_power
Target is a low-power / duty-cycled receiver: directed frames set CTRL1_LOW_POWER and use the long wa...
uint8_t subtype
Device subtype byte.
bool silent
Send position moves in "silent operation" (slower) mode.
DeviceType type
Device type from YAML declaration.
Runtime state of a paired IO‑Homecontrol device.
bool inverted
True if open/close positions are swapped (e.g., horizontal awning).
bool silent
True to send position moves with the reference hub's "silent operation" extended block,...
bool low_power
YAML-declared: this target is a low-power / duty-cycled receiver, so directed frames set CTRL1_LOW_PO...
uint8_t subtype
Device subtype (manufacturer‑specific).
uint8_t node_id[NODE_ID_SIZE]
Device's 3‑byte radio address.
bool dimmable
True for a LIGHT-class device configured dimmable: true in YAML.
bool optimistic_state
True if target may be set ahead of a confirming poll/response.
DeviceType type
Device type (shutter, awning, etc.).
@ STOPPED
Predicted to be at rest (a STOP was issued).
@ MOVING
Predicted to be travelling (a position command was issued).