Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
platform_companion_sensors.h
Go to the documentation of this file.
1#pragma once
2
3/// @file platform_companion_sensors.h
4/// @brief The auto-generated per-device diagnostic companion sensors.
5/// @ingroup hioc_platforms
6///
7/// Every device-bound platform (cover, light, switch, lock) gets the same read-only companions
8/// generated alongside it by platform_common.py: smoothed RSSI, seconds since last contact,
9/// cumulative exchange-failure and unconfirmed-exchange counts, the stored device name, the
10/// currently outstanding CMD_ERROR_RESP reason, and the last-command record's
11/// commander/originator. They share the DeviceBoundCompanion binding (observe-only: no
12/// add_device(), no polling) and an all-but-identical setup()/dump_config() skeleton, so they
13/// live together here rather than in a file pair each.
14///
15/// What each one still owns is its dump_config() label and whatever its setup() has to do beyond
16/// binding; everything they share sits in the three bases below.
17
18#include "esphome/components/sensor/sensor.h"
19#include "esphome/components/text_sensor/text_sensor.h"
20#include "esphome/core/component.h"
22
23namespace esphome {
24namespace home_io_control {
25
26/// @brief Shared base for the numeric per-device diagnostic companions.
27///
28/// Carries the three-base inheritance and the setup priority every one of them needs. DATA keeps
29/// them behind the hub, so `parent_` is usable by the time their setup() runs.
30/// @ingroup hioc_platforms
31class IOHomeCompanionSensor : public sensor::Sensor, public Component, public DeviceBoundCompanion {
32 public:
33 /// @brief Get setup priority so the parent hub is available first.
34 /// @return setup_priority::DATA.
35 [[nodiscard]] float get_setup_priority() const override { return setup_priority::DATA; }
36};
37
38/// @brief Shared base for the textual per-device diagnostic companions.
39///
40/// The text-sensor counterpart of IOHomeCompanionSensor; same reasoning.
41/// @ingroup hioc_platforms
42class IOHomeCompanionTextSensor : public text_sensor::TextSensor, public Component, public DeviceBoundCompanion {
43 public:
44 /// @brief Get setup priority so the parent hub is available first.
45 /// @return setup_priority::DATA.
46 [[nodiscard]] float get_setup_priority() const override { return setup_priority::DATA; }
47};
48
49/// @brief Shared base for the companions that publish one cumulative `uint16_t` counter held on
50/// IoDevice.
51///
52/// These differ only in which field they read and what they call themselves, so the binding lives
53/// here once and each subclass supplies the field through counter_value(). Zero is a meaningful
54/// reading for all of them — "none yet", not "no data" — so unlike RSSI and Last Contact they
55/// publish unconditionally on setup.
56/// @ingroup hioc_platforms
58 public:
59 /// @brief Register the device-update subscription and publish the initial cached count.
60 void setup() final;
61
62 protected:
63 /// @brief Return the counter this sensor publishes.
64 /// @param dev Device record to read the count from.
65 [[nodiscard]] virtual uint16_t counter_value(const IoDevice &dev) const = 0;
66};
67
68/// @brief Diagnostic sensor that publishes a device's smoothed (EMA) RSSI in dBm.
69///
70/// Publishes nothing until the first RX from this device seeds the EMA (see
71/// detail::update_link_health() in hub_internal.h) — Home Assistant shows the entity as
72/// unavailable until then, rather than a misleading 0 dBm.
73/// @ingroup hioc_platforms
75 public:
76 /// @brief Register the device-update subscription and publish the initial cached state.
77 void setup() override;
78
79 /// @brief Dump sensor configuration to the log.
80 void dump_config() override;
81};
82
83/// @brief Diagnostic sensor that publishes seconds elapsed since the last frame received from a
84/// device (see detail::update_link_health() in hub_internal.h).
85///
86/// This is an age, not a timestamp: it resets to ~0 on every frame from the device — including
87/// replies to the hub's own status polls and commands, not just traffic the device sends
88/// unprompted — and counts up from there. A periodic heartbeat (see HEARTBEAT_INTERVAL_MS in the
89/// .cpp) re-publishes it even when the device stays quiet, so the value keeps advancing in Home
90/// Assistant instead of freezing at whatever it was at the last frame. Publishes nothing until
91/// the first frame is seen.
92/// @ingroup hioc_platforms
94 public:
95 /// @brief Register the device-update subscription, start the heartbeat, and publish the
96 /// initial cached state.
97 void setup() override;
98
99 /// @brief Dump sensor configuration to the log.
100 void dump_config() override;
101
102 protected:
103 /// @brief Compute and publish seconds since `dev.last_seen_ms`; no-op before the first frame.
104 /// @param dev Device to read `last_seen_ms` from.
105 void publish_age_(const IoDevice &dev);
106};
107
108/// @brief Diagnostic sensor that publishes a device's cumulative count of outbound exchanges
109/// that timed out (no valid response) — see detail::record_exchange_timeout() in
110/// hub_internal.h.
111///
112/// Unlike the RSSI and Last Contact sensors, zero is a meaningful value here (no failures yet),
113/// so this publishes on setup unconditionally.
114/// @ingroup hioc_platforms
116 public:
117 /// @brief Dump sensor configuration to the log.
118 void dump_config() override;
119
120 protected:
121 /// @copydoc IOHomeDeviceCounterSensor::counter_value
122 [[nodiscard]] uint16_t counter_value(const IoDevice &dev) const override { return dev.exchange_timeout_count; }
123};
124
125/// @brief Diagnostic sensor that publishes a device's cumulative count of exchanges it
126/// authenticated and then never closed — see detail::record_exchange_outcome() in
127/// hub_internal.h.
128///
129/// Read it against Exchange Failures: that counter rising alone means the device is not hearing
130/// the hub, while this one rising means it hears the hub's request and then either the hub's
131/// challenge answer or the device's closing reply is lost. For a movement command this outcome is
132/// reported as success, so this sensor is the only place it shows up.
133///
134/// Zero is meaningful here (none yet), so this publishes on setup unconditionally.
135/// @ingroup hioc_platforms
137 public:
138 /// @brief Dump sensor configuration to the log.
139 void dump_config() override;
140
141 protected:
142 /// @copydoc IOHomeDeviceCounterSensor::counter_value
143 [[nodiscard]] uint16_t counter_value(const IoDevice &dev) const override { return dev.exchange_unconfirmed_count; }
144};
145
146/// @brief Diagnostic text sensor that publishes the cached device name.
147///
148/// Beyond the shared companion behavior it also queues one boot-time GET_NAME request so the
149/// cache gets populated without waiting for unrelated traffic.
150/// @ingroup hioc_platforms
152 public:
153 /// @brief Register the device-update subscription and schedule an initial name fetch.
154 void setup() override;
155
156 /// @brief Dump text-sensor configuration to the log.
157 void dump_config() override;
158};
159
160/// @brief Diagnostic text sensor that publishes the symbolic name of a device's most recent
161/// CMD_ERROR_RESP result code (e.g. "LIMITATION_BY_RAIN"), letting a "nothing happened" command
162/// self-explain instead of only showing up in the log. Shared by every device-bound platform
163/// (cover, light, switch, lock) via platform_common.py's companion-sensor codegen.
164///
165/// Not a per-operation outcome — it does not get set on every command, only on an explicit
166/// CMD_ERROR_RESP. Publishes an empty string until the first one is seen, and again after any
167/// subsequent successful status/command reply clears it (see detail::clear_command_result()), so
168/// a non-empty value always means "this is still going on" rather than "this is what happened
169/// last."
170/// @ingroup hioc_platforms
172 public:
173 /// @brief Register the device-update subscription and publish the initial cached state.
174 void setup() override;
175
176 /// @brief Dump text-sensor configuration to the log.
177 void dump_config() override;
178};
179
180/// @brief Diagnostic text sensor naming the controller that last commanded this device.
181///
182/// Read from bytes the device already includes in every status reply — no extra radio traffic and
183/// no probe. Last-writer-wins and inherently stale: it only changes when something actually
184/// commands the device, and a foreign controller's node ID has no name unless the user recognises
185/// it. Publishes an empty string until the first status reply carrying the record arrives.
186/// @ingroup hioc_platforms
188 public:
189 void setup() override;
190 void dump_config() override;
191};
192
193/// @brief Diagnostic text sensor naming what kind of source issued that last command.
194///
195/// The device's own Command Originator byte, rendered "name(0xXX)". Field-validated as a clean
196/// remote-vs-motor-button split on roller shutters only; other device classes may report values
197/// with no ORIGINATOR_* name, which surface as "unknown(0xXX)" rather than being dropped.
198/// @ingroup hioc_platforms
200 public:
201 void setup() override;
202 void dump_config() override;
203};
204
205} // namespace home_io_control
206} // namespace esphome
Mixin holding the parent + device-id binding shared by per-device entities that are not full entity p...
Diagnostic text sensor that publishes the symbolic name of a device's most recent CMD_ERROR_RESP resu...
void setup() override
Register the device-update subscription and publish the initial cached state.
void dump_config() override
Dump text-sensor configuration to the log.
Shared base for the numeric per-device diagnostic companions.
float get_setup_priority() const override
Get setup priority so the parent hub is available first.
Shared base for the textual per-device diagnostic companions.
float get_setup_priority() const override
Get setup priority so the parent hub is available first.
Shared base for the companions that publish one cumulative uint16_t counter held on IoDevice.
void setup() final
Register the device-update subscription and publish the initial cached count.
virtual uint16_t counter_value(const IoDevice &dev) const =0
Return the counter this sensor publishes.
Diagnostic text sensor that publishes the cached device name.
void setup() override
Register the device-update subscription and schedule an initial name fetch.
void dump_config() override
Dump text-sensor configuration to the log.
Diagnostic sensor that publishes a device's cumulative count of outbound exchanges that timed out (no...
uint16_t counter_value(const IoDevice &dev) const override
Return the counter this sensor publishes.
void dump_config() override
Dump sensor configuration to the log.
Diagnostic text sensor naming what kind of source issued that last command.
Diagnostic text sensor naming the controller that last commanded this device.
Diagnostic sensor that publishes seconds elapsed since the last frame received from a device (see det...
void dump_config() override
Dump sensor configuration to the log.
void publish_age_(const IoDevice &dev)
Compute and publish seconds since dev.last_seen_ms; no-op before the first frame.
void setup() override
Register the device-update subscription, start the heartbeat, and publish the initial cached state.
Diagnostic sensor that publishes a device's smoothed (EMA) RSSI in dBm.
void setup() override
Register the device-update subscription and publish the initial cached state.
void dump_config() override
Dump sensor configuration to the log.
Diagnostic sensor that publishes a device's cumulative count of exchanges it authenticated and then n...
uint16_t counter_value(const IoDevice &dev) const override
Return the counter this sensor publishes.
void dump_config() override
Dump sensor configuration to the log.
Shared device-binding mixins for IO-Homecontrol entity platforms.
Runtime state of a paired IO‑Homecontrol device.
uint16_t exchange_unconfirmed_count
Cumulative count of exchanges this device authenticated and then never closed: it answered with a 0x3...
uint16_t exchange_timeout_count
Cumulative count of outbound exchanges to this device with no valid response (see detail::record_exch...