Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
platform_companion_sensors.cpp
Go to the documentation of this file.
1/// @file platform_companion_sensors.cpp
2/// @brief The auto-generated per-device diagnostic companion sensors.
3/// @ingroup hioc_platforms
4
6
7#include "entity_helpers.h"
8#include "proto_constants.h"
9#include "esphome/core/hal.h"
10#include "esphome/core/log.h"
11
12namespace esphome {
13namespace home_io_control {
14
15// Each dump_config() keeps its own log tag as a `static` function-local constant: the ESPHome
16// LOG_SENSOR / LOG_TEXT_SENSOR macros expand to a bare `TAG` identifier, so one shared file-scope
17// constant could not carry a different value per sensor. Values are unchanged from the former per-sensor
18// files. (`static` so the name resolves as a StaticConstant, i.e. UPPER_CASE, under clang-tidy's
19// identifier-naming check rather than as a lower_case local.)
20
21namespace {
22
23constexpr float MS_PER_SECOND = 1000.0F;
24// How often the Last Contact heartbeat re-publishes the age while the device stays quiet.
25// Frame-driven updates alone would freeze the entity's value at whatever it was the moment
26// traffic stopped; without this it can't count up in Home Assistant between frames.
27constexpr uint32_t HEARTBEAT_INTERVAL_MS = 60000;
28
29/// What a device's execution state adds to the Active Issue, or nullptr when it is no issue (waiting
30/// for its turn, running and done are the normal course of a move).
31const char *execution_state_issue(ExecutionState state) {
32 switch (state) {
34 return command_result_name(RESULT_NODE_WAITING_FOR_POWER);
39 default:
40 return nullptr;
41 }
42}
43
44} // namespace
45
47 this->register_companion_binding_([this](const IoDevice &dev) {
48 if (const int16_t ema_dbm = device_rssi_ema_dbm(dev); ema_dbm != RSSI_UNKNOWN_DBM)
49 this->publish_state(ema_dbm);
50 });
51}
52
54 static const char *const TAG = "home_io_control.rssi";
55 LOG_SENSOR("", "IO-Homecontrol RSSI", this);
56 ESP_LOGCONFIG(TAG, " Device ID: %s", this->device_id_.c_str());
57}
58
60 this->register_companion_binding_([this](const IoDevice &dev) { this->publish_age_(dev); });
61
62 this->set_interval("heartbeat", HEARTBEAT_INTERVAL_MS, [this]() {
63 if (this->parent_ == nullptr)
64 return;
65 if (const auto *dev = this->parent_->get_device(this->device_id_); dev != nullptr)
66 this->publish_age_(*dev);
67 });
68}
69
71 if (dev.last_seen_ms != 0)
72 this->publish_state(static_cast<float>(millis() - dev.last_seen_ms) / MS_PER_SECOND);
73}
74
76 static const char *const TAG = "home_io_control.last_contact";
77 LOG_SENSOR("", "IO-Homecontrol Last Contact", this);
78 ESP_LOGCONFIG(TAG, " Device ID: %s", this->device_id_.c_str());
79}
80
83 [this](const IoDevice &dev) { this->publish_state(static_cast<float>(this->counter_value(dev))); });
84}
85
87 static const char *const TAG = "home_io_control.exchange_failures";
88 LOG_SENSOR("", "IO-Homecontrol Exchange Failures", this);
89 ESP_LOGCONFIG(TAG, " Device ID: %s", this->device_id_.c_str());
90}
91
93 static const char *const TAG = "home_io_control.unconfirmed_exchanges";
94 LOG_SENSOR("", "IO-Homecontrol Unconfirmed Exchanges", this);
95 ESP_LOGCONFIG(TAG, " Device ID: %s", this->device_id_.c_str());
96}
97
99 if (this->parent_ == nullptr)
100 return;
101
102 this->register_companion_binding_([this](const IoDevice &dev) { this->publish_state(dev.name); });
103
104 this->set_timeout("init_name", INITIAL_STATUS_REQUEST_DELAY_MS,
105 [this]() { this->parent_->queue_request_device_name(this->device_id_); });
106}
107
109 static const char *const TAG = "home_io_control.device_name";
110 LOG_TEXT_SENSOR("", "IO-Homecontrol Device Name", this);
111 ESP_LOGCONFIG(TAG, " Device ID: %s", this->device_id_.c_str());
112}
113
115 this->register_companion_binding_([this](const IoDevice &dev) {
116 // A radio that stopped responding wins: nothing reaches any device until a reboot. Then an
117 // explicit CMD_ERROR_RESP result; otherwise the rain limitation the hub derived from the
118 // window's own status replies; otherwise what the device's execution state says (waiting for
119 // power, unable to run, error during the last move); otherwise empty. A result about one
120 // exchange only (command_result_is_transient()) is not an issue of the device.
121 if (this->parent_->radio_stopped_responding()) {
122 this->publish_state(ACTIVE_ISSUE_RADIO_STOPPED_RESPONDING);
124 this->publish_state(command_result_name(dev.last_result_code));
125 } else if (dev.limited_by_rain) {
126 this->publish_state(command_result_name(RESULT_LIMITATION_BY_RAIN));
127 } else if (const char *issue = execution_state_issue(dev.execution_state)) {
128 this->publish_state(issue);
129 } else {
130 this->publish_state("");
131 }
132 });
133}
134
136 static const char *const TAG = "home_io_control.active_issue";
137 LOG_TEXT_SENSOR("", "IO-Homecontrol Active Issue", this);
138 ESP_LOGCONFIG(TAG, " Device ID: %s", this->device_id_.c_str());
139}
140
142 // No parent_ == nullptr guard needed here (unlike IOHomeDeviceNameTextSensor, which dereferences
143 // parent_ a second time outside the binding, in a set_timeout lambda): register_companion_binding_()
144 // already no-ops on a null parent before this lambda is ever invoked, so parent_ is guaranteed
145 // non-null every time it runs.
147 [this](const IoDevice &dev) { this->publish_state(this->parent_->describe_last_commander(dev)); });
148}
149
151 static const char *const TAG = "home_io_control.last_commanded_by";
152 LOG_TEXT_SENSOR("", "IO-Homecontrol Last Commanded By", this);
153 ESP_LOGCONFIG(TAG, " Device ID: %s", this->device_id_.c_str());
154}
155
158 [this](const IoDevice &dev) { this->publish_state(detail::describe_last_command_source(dev)); });
159}
160
162 static const char *const TAG = "home_io_control.last_command_source";
163 LOG_TEXT_SENSOR("", "IO-Homecontrol Last Command Source", this);
164 ESP_LOGCONFIG(TAG, " Device ID: %s", this->device_id_.c_str());
165}
166
167} // namespace home_io_control
168} // namespace esphome
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 setup() override
Register the device-update subscription and publish the initial cached state.
void dump_config() override
Dump text-sensor configuration to the log.
std::string describe_last_commander(const IoDevice &dev) const
Render a device's "last commanded by" string, resolving this hub's own node ID.
virtual IoDevice * get_device(const std::string &device_id)
Retrieve a device by ID; returns nullptr if not found.
Definition hub_core.cpp:402
virtual void queue_request_device_name(const std::string &device_id)
Queue an async device-name request; returns immediately, executed in loop().
bool radio_stopped_responding() const
True once loop() has seen the radio latch a failure after setup (a runtime BUSY timeout,...
Definition hub_core.h:513
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.
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.
void dump_config() override
Dump sensor configuration to the log.
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.
void setup() override
Register the device-update subscription and publish the initial cached state.
void dump_config() override
Dump sensor configuration to the log.
void dump_config() override
Dump sensor configuration to the log.
Conversions and renderers shared by the hub and its Home Assistant entities.
std::string describe_last_command_source(const IoDevice &dev)
Render the "Last Command Source" sensor string.
constexpr const char * ACTIVE_ISSUE_RADIO_STOPPED_RESPONDING
Active Issue value while the hub's radio has stopped responding, in the style of the symbolic result ...
const char * command_result_name(uint8_t result)
Return a stable symbolic name for a CMD_ERROR_RESP result code.
ExecutionState
What a device says it is doing, from bits [2:0] of byte 0 of a status reply (CMD_PRIVATE_RESP,...
@ WAITING_FOR_POWER
Accepted, waiting for power (queued behind another device on a shared supply).
@ NON_EXECUTING
Unable to execute under the given conditions (example: temperature too high).
@ EXECUTION_ERROR
An error occurred during the previous execution; cleared when the next starts.
constexpr const char * ACTIVE_ISSUE_NOT_EXECUTING
Active Issue value while a device's status reports execution state 0: it cannot run its last command ...
constexpr bool command_result_is_transient(uint8_t result)
Check whether a result code describes one exchange rather than the device's state.
int16_t device_rssi_ema_dbm(const IoDevice &dev)
A device's smoothed RSSI in whole dBm.
constexpr const char * ACTIVE_ISSUE_EXECUTION_ERROR
Active Issue value while a device's status reports execution state 1: an error occurred during its la...
The auto-generated per-device diagnostic companion sensors.
IO-Homecontrol command IDs, result codes and protocol enumerations.
Runtime state of a paired IO‑Homecontrol device.
ExecutionState execution_state
Execution state of the last status report that carried one; UNDEFINED until then, and for a device th...
uint8_t last_result_code
Last CMD_ERROR_RESP result byte (0 = none recorded).
char name[DEVICE_NAME_BUFFER_SIZE]
Cached UTF-8 device name decoded from Latin-1 wire payloads.
bool limited_by_rain
Derived from observed status replies, never from a prediction (decisions::is_rain_limited(); ADR 0030...
uint32_t last_seen_ms
millis() of the last frame received from this device (any command), 0 = never.