Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
entity_helpers.h
Go to the documentation of this file.
1#pragma once
2
3/// @file entity_helpers.h
4/// @brief Conversions and renderers shared by the hub and its Home Assistant entities.
5/// @ingroup hioc_hub
6///
7/// Carries no dependency on the hub itself, so an entity can include this header instead of the
8/// hub's private helpers (make include-graph enforces that split).
9
10#include "log_helpers.h"
11#include "proto_constants.h"
12#include "proto_device_model.h"
13#include "proto_frame.h"
14#include "proto_sizes.h"
15
16#include <cmath>
17#include <cstdint>
18#include <cstring>
19#include <string>
20
21namespace esphome {
22namespace home_io_control {
23namespace detail {
24
25// ============================================================================
26// Shared constants
27// ============================================================================
28
30 50.0F; ///< Shared 0-100 cutoff: values below this mean binary "on".
31
32// ============================================================================
33// Percent conversion helpers
34// ============================================================================
35
36/// @brief Convert a 0.0-1.0 HA fraction (position, tilt, or brightness) to a 0-100 IO percent.
37///
38/// Rounds rather than truncates: HA quantizes call values to 0-255 before they ever reach us, so
39/// its "50%" is 128/255=0.50196, not exactly 0.5 — a truncating cast compounds that quantization
40/// into a consistent ~1% bias, caught on real hardware in both platform_cover.cpp (position and
41/// tilt) and platform_light.cpp (brightness). Callers apply their own invert/complement logic
42/// (e.g. `1.0F - fraction`) before calling this; it only owns the rounding.
43/// @param fraction Value in [0.0, 1.0].
44/// @return Rounded 0-100 percent.
45inline uint8_t round_percent(float fraction) { return static_cast<uint8_t>(std::lround(fraction * 100.0F)); }
46
47// ============================================================================
48// Last-command record rendering
49// ============================================================================
50
51/// @brief Render a Command Originator byte as "name(0xXX)", e.g. "user_remote(0x01)".
52///
53/// The one rendering every originator display shares (the "Last Command Source" sensor and the 0x71
54/// status-update log line), so a byte with no ORIGINATOR_* case reads "unknown(0x0A)" everywhere
55/// rather than being silently dropped or mislabelled.
56/// @param originator Command Originator byte (ORIGINATOR_*).
57/// @return "name(0xXX)".
58inline std::string format_originator(uint8_t originator) {
59 return format_name_and_hex(originator_name(originator), originator);
60}
61
62/// @brief Render the "Last Commanded By" sensor string.
63///
64/// Always leads with the raw node ID — that is the diagnostic value, and the only thing a user can
65/// match against a remote they own. The qualifier is additive, never a substitute: a device naming
66/// its own ID is NOT reliably "the button on the motor" (the one non-shutter this project has data
67/// on, a mains gate, names its own ID with an undefined originator), so the cause belongs to the
68/// separate originator sensor, not to this one's wording.
69/// @param dev Device record to read.
70/// @param hub_node_id This hub's own 3-byte node ID.
71/// @return e.g. "3B74DC", "C0FFEE (this hub)", "2FE2D2 (this device)"; empty before the first record.
72inline std::string describe_last_commander(const IoDevice &dev, const uint8_t *hub_node_id) {
73 if (!dev.has_last_command)
74 return {};
75 std::string out = node_id_to_string(dev.last_commander);
76 if (memcmp(dev.last_commander, hub_node_id, NODE_ID_SIZE) == 0) {
77 out += " (this hub)";
78 } else if (memcmp(dev.last_commander, dev.node_id, NODE_ID_SIZE) == 0) {
79 out += " (this device)";
80 }
81 return out;
82}
83
84/// @brief Render the "Last Command Source" sensor string.
85///
86/// Rendered by format_originator(), so an undecoded byte keeps its raw hex. The decode is
87/// field-validated for roller shutters (a clean 0x00/0x01 split, remote vs. motor
88/// button); gates, lights and multi-channel units are not validated and are expected to surface
89/// undecoded values here — which is the point of keeping the raw hex in the string.
90/// @param dev Device record to read.
91/// @return e.g. "user_remote(0x01)"; empty before the first record.
92inline std::string describe_last_command_source(const IoDevice &dev) {
93 if (!dev.has_last_command)
94 return {};
96}
97
98} // namespace detail
99} // namespace home_io_control
100} // namespace esphome
Hub-layer log tag and log/format helpers shared by the hub and its collaborators.
std::string format_name_and_hex(const char *name, uint8_t value)
Format a name/value pair as "name(0xXX)", e.g. "execute(0x00)".
Definition log_helpers.h:41
constexpr float BINARY_ENTITY_ON_POSITION_THRESHOLD
Shared 0-100 cutoff: values below this mean binary "on".
std::string describe_last_command_source(const IoDevice &dev)
Render the "Last Command Source" sensor string.
uint8_t round_percent(float fraction)
Convert a 0.0-1.0 HA fraction (position, tilt, or brightness) to a 0-100 IO percent.
std::string describe_last_commander(const IoDevice &dev, const uint8_t *hub_node_id)
Render the "Last Commanded By" sensor string.
std::string format_originator(uint8_t originator)
Render a Command Originator byte as "name(0xXX)", e.g.
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 * originator_name(uint8_t originator)
Get a human-readable name for a command originator byte.
IO-Homecontrol command IDs, result codes and protocol enumerations.
IO-Homecontrol device-type model, capabilities and runtime device state.
IO-Homecontrol 2W frame container: control bytes, IoFrame and (de)serialization.
Fundamental IO-Homecontrol frame and crypto size constants.
Runtime state of a paired IO‑Homecontrol device.
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).
uint8_t node_id[NODE_ID_SIZE]
Device's 3‑byte radio address.
bool has_last_command
True once a status reply carried a well-formed last-command record.