Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
platform_entity_base.h
Go to the documentation of this file.
1#pragma once
2
3/// @file platform_entity_base.h
4/// @brief Shared device-binding mixins for IO-Homecontrol entity platforms.
5/// @ingroup hioc_platforms
6///
7/// IOHomeCover, IOHomeLight, IOHomeSwitch and IOHomeLock all bind an ESPHome entity to a hub
8/// device: the same five YAML setters, the same registration ritual in setup(), and the same
9/// poll-interval dump_config line. DeviceBoundEntity centralizes exactly that shared state and
10/// wiring. The auto-generated companion diagnostic sensors share a smaller, observe-only
11/// binding; DeviceBoundCompanion centralizes that one.
12///
13/// Both are intentionally NOT ESPHome base classes — they are plain mixins the entities inherit
14/// alongside their real ESPHome base (cover::Cover, light::LightOutput, switch_::Switch,
15/// lock::Lock, text_sensor::TextSensor, sensor::Sensor). Entity-specific state mapping (cover
16/// position/tilt/movement inference, binary on/off decoding, each companion's value rendering)
17/// deliberately stays in the entities; only the device-binding plumbing lives here.
18
19#include "hub_internal.h"
20
21#include "esphome/core/application.h"
22
23#include <cinttypes>
24#include <functional>
25#include <string>
26#include <utility>
27
28namespace esphome {
29namespace home_io_control {
30
31/// @brief Mixin holding the hub-device binding shared by all IO-Homecontrol entity platforms.
32/// @ingroup hioc_platforms
34 public:
35 /// @brief Set the parent controller component.
36 /// @param parent Pointer to the IOHomeControlComponent instance.
37 void set_parent(IOHomeControlComponent *parent) { this->parent_ = parent; }
38 /// @brief Set the unique IO-homecontrol device ID (from YAML).
39 /// @param id Hexadecimal node ID string (e.g., "123ABC").
40 void set_device_id(const std::string &id) { this->device_id_ = id; }
41 /// @brief Set the declared device type (from YAML).
42 /// @param type Device type enum.
43 void set_device_type(DeviceType type) { this->device_type_ = type; }
44 /// @brief Set the declared device subtype (from YAML).
45 /// @param subtype Subtype value.
46 void set_subtype(uint8_t subtype) { this->subtype_ = subtype; }
47 /// @brief Enable or disable optimistic target updates for this device (from YAML).
48 /// Only covers expose this in YAML today; other platforms keep the default (true), which is
49 /// inert for entity types that never read `IoDevice.target`/`is_stopped`.
50 /// @param optimistic_state False to disable optimistic state; default true.
51 void set_optimistic_state(bool optimistic_state) { this->optimistic_state_ = optimistic_state; }
52 /// @brief Configure bounded follow-up polling while a state change is expected.
53 /// @param poll_interval_ms Poll interval in milliseconds; zero keeps the default single settle poll only.
54 void set_status_poll_interval(uint32_t poll_interval_ms) { this->status_poll_interval_ms_ = poll_interval_ms; }
55
56 protected:
57 /// @brief Perform the shared setup() registration ritual.
58 ///
59 /// Registers the device with the controller, sets its status-poll interval, subscribes the
60 /// entity's update callback, and schedules the delayed initial status request — in the exact
61 /// order every entity used before.
62 /// @param self The entity itself; used for set_timeout(), since DeviceBoundEntity is not a Component.
63 /// @param inverted Initial inversion flag passed to add_device (covers compute it; others pass false).
64 /// @param on_update The entity's device-update callback.
65 void register_device_binding_(Component *self, bool inverted,
66 std::function<void(const std::string &, const IoDevice &)> on_update) {
67 this->parent_->add_device(this->device_id_,
68 DeviceConfig{this->device_type_, this->subtype_, inverted, this->optimistic_state_});
69 this->parent_->set_device_status_poll_interval(this->device_id_, this->status_poll_interval_ms_);
70 this->parent_->register_device_callback(std::move(on_update));
71 // Schedule the delayed initial poll through the public scheduler: Component::set_timeout is
72 // protected, and DeviceBoundEntity is a mixin, not a Component subclass, so it cannot call it
73 // through `self`. App.scheduler.set_timeout is the public entry point set_timeout wraps.
74 App.scheduler.set_timeout(self, "init_status", INITIAL_STATUS_REQUEST_DELAY_MS,
75 [this]() { this->parent_->queue_request_device_status(this->device_id_); });
76 }
77
78 /// @brief Shared inbound-update guard for binary endpoints.
79 ///
80 /// Matches this device and accepts only a settled (stopped) status with a known position.
81 /// Covers and locks intentionally use their own richer guards instead of this filter.
82 [[nodiscard]] bool passes_binary_update_filter_(const std::string &id, const IoDevice &dev) const {
83 return id == this->device_id_ && dev.position != UNKNOWN_POSITION && dev.is_stopped;
84 }
85
86 /// @brief Emit the shared two-branch poll-interval line for dump_config().
87 /// @param tag Log tag of the calling entity.
88 void log_poll_interval_config_(const char *tag) const {
89 if (this->status_poll_interval_ms_ == 0) {
90 ESP_LOGCONFIG(tag, " Status Poll Interval: device-hinted settle polling (no fixed interval)");
91 } else {
92 ESP_LOGCONFIG(tag, " Status Poll Interval: %" PRIu32 " ms", this->status_poll_interval_ms_);
93 }
94 }
95
97 std::string device_id_;
99 uint8_t subtype_{0};
102};
103
104/// @brief Mixin holding the hub/device binding shared by the auto-generated per-device
105/// companion diagnostic sensors (device name, active issue, RSSI, last contact, exchange
106/// failures).
107/// @ingroup hioc_platforms
108///
109/// Companions differ from the main entity platforms (DeviceBoundEntity above) in that they only
110/// *observe* a device the main entity already registered: they never call add_device() or
111/// configure polling, they just subscribe to update notifications and republish their one
112/// value. This mixin centralizes that subscribe-and-republish ritual; each sensor class keeps
113/// only its value rendering (including its decision whether a given device state is
114/// publishable at all).
116 public:
117 /// @brief Set the parent controller component.
118 /// @param parent Pointer to the IOHomeControlComponent instance.
119 void set_parent(IOHomeControlComponent *parent) { this->parent_ = parent; }
120 /// @brief Set the device ID whose state this companion sensor exposes.
121 /// @param id Hexadecimal node ID string (for example "123ABC").
122 void set_device_id(const std::string &id) { this->device_id_ = id; }
123
124 protected:
125 /// @brief Shared setup() body: subscribe to this device's updates and publish the initial state.
126 ///
127 /// No-op when no parent is wired (codegen always wires one before setup() runs).
128 /// @param publish Renders and publishes the sensor's value from a device record — or skips
129 /// publishing when the record has no meaningful value yet. Runs once immediately when the
130 /// device is already registered, then again on every update notification for this device.
131 void register_companion_binding_(const std::function<void(const IoDevice &)> &publish) {
132 if (this->parent_ == nullptr)
133 return;
134
135 this->parent_->register_device_callback([this, publish](const std::string &id, const IoDevice &dev) {
136 if (id == this->device_id_)
137 publish(dev);
138 });
139
140 if (const auto *dev = this->parent_->get_device(this->device_id_); dev != nullptr)
141 publish(*dev);
142 }
143
145 std::string device_id_;
146};
147
148} // namespace home_io_control
149} // namespace esphome
Mixin holding the hub/device binding shared by the auto-generated per-device companion diagnostic sen...
void set_parent(IOHomeControlComponent *parent)
Set the parent controller component.
void register_companion_binding_(const std::function< void(const IoDevice &)> &publish)
Shared setup() body: subscribe to this device's updates and publish the initial state.
void set_device_id(const std::string &id)
Set the device ID whose state this companion sensor exposes.
Mixin holding the hub-device binding shared by all IO-Homecontrol entity platforms.
void register_device_binding_(Component *self, bool inverted, std::function< void(const std::string &, const IoDevice &)> on_update)
Perform the shared setup() registration ritual.
void set_status_poll_interval(uint32_t poll_interval_ms)
Configure bounded follow-up polling while a state change is expected.
void set_optimistic_state(bool optimistic_state)
Enable or disable optimistic target updates for this device (from YAML).
void log_poll_interval_config_(const char *tag) const
Emit the shared two-branch poll-interval line for dump_config().
void set_device_type(DeviceType type)
Set the declared device type (from YAML).
void set_subtype(uint8_t subtype)
Set the declared device subtype (from YAML).
void set_device_id(const std::string &id)
Set the unique IO-homecontrol device ID (from YAML).
void set_parent(IOHomeControlComponent *parent)
Set the parent controller component.
bool passes_binary_update_filter_(const std::string &id, const IoDevice &dev) const
Shared inbound-update guard for binary endpoints.
The main IO-Homecontrol component.
Definition hub_core.h:74
Internal helpers shared by the hub implementation .cpp files.
static constexpr float UNKNOWN_POSITION
Sentinel value meaning "position is not known yet".
DeviceType
Device type identifiers reported by IO‑Homecontrol products.
@ UNKNOWN
Unknown/unspecified device.
static constexpr uint32_t INITIAL_STATUS_REQUEST_DELAY_MS
Delay before the first post-boot status request from an entity.
YAML-declared device metadata for registration; defaults match an undeclared device.
Runtime state of a paired IO‑Homecontrol device.
float position
Current position: 0=open, 100=closed, or UNKNOWN_POSITION.
bool is_stopped
True if device is not moving.