Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
platform_hub_controls.h
Go to the documentation of this file.
1#pragma once
2
3/// @file platform_hub_controls.h
4/// @brief Hub-level control entities: the discovery button, the scan-paired-devices button, the
5/// two arming switches, and the pairing-result diagnostic text sensor.
6/// @ingroup hioc_platforms
7///
8/// These act on the hub as a whole and have no device to bind to (HubBoundEntity), and are
9/// created from the `home_io_control:` block or a dedicated `button:` entry rather than a
10/// device-bound platform entry — for the shared reason, see HubBoundEntity in
11/// platform_entity_base.h. They are created dynamically the same way `tuning: {ui_controls: true}`
12/// creates its number/select entities, and `set_parent()` is called with the very hub instance
13/// being built.
14
15#include <functional>
16#include <utility>
17
18#include "esphome/components/button/button.h"
19#include "esphome/components/switch/switch.h"
20#include "esphome/components/text_sensor/text_sensor.h"
21#include "esphome/core/component.h"
22#include "hub_core.h"
24
25namespace esphome {
26namespace home_io_control {
27
28/// @brief Shared body for the hub-level arming switches.
29///
30/// Both arming switches (2W key extraction, 1W controller-key adoption) have the same three
31/// behaviours: forward the toggle to the hub, publish the resulting state, and mirror the hub's
32/// own disarm events (auto-off timeout, or a successful recovery) so the entity never shows
33/// "on" for a window that already closed. Only the two hub calls differ, so those are the two
34/// hooks; everything else lives here once.
35///
36/// Deliberately NOT one concrete class parameterized by an enum or a std::function: the two
37/// switches arm independent security-sensitive listeners, and keeping them distinct C++ types
38/// means "wire the wrong one" is a compile error rather than a codegen bug.
39/// @ingroup hioc_platforms
40class HubArmingSwitch : public switch_::Switch, public Component, public HubBoundEntity {
41 public:
42 /// @brief Register the armed-state callback so this entity mirrors the hub's own disarm events.
43 /// No-op when no parent is wired (codegen always wires one before setup() runs).
44 void setup() final;
45
46 /// @brief Get setup priority so the parent hub is available first.
47 /// @return setup_priority::DATA.
48 [[nodiscard]] float get_setup_priority() const final { return setup_priority::DATA; }
49
50 protected:
51 /// @brief Forward the toggle to the hub and publish the state. Publishing still happens with no
52 /// parent wired, so an unwired switch reads as off rather than unknown in Home Assistant.
53 void write_state(bool state) final;
54
55 /// @brief Arm or disarm this switch's listener on the hub.
56 virtual void arm(bool state) = 0;
57 /// @brief Subscribe @p callback to the hub's armed-state changes for this listener.
58 virtual void subscribe_armed(std::function<void(bool)> callback) = 0;
59};
60
61/// @brief Hub-level switch entity: ON arms the key-extraction responder for 10 minutes, OFF
62/// disarms it immediately. Publishes its own state changes when the hub disarms itself
63/// (successful extraction or auto-off timeout), not just on a user-initiated toggle.
64///
65/// Created dynamically from `home_io_control.accept_foreign_pairing: true` (see `__init__.py`'s
66/// `_create_hub_arming_switch()`), not through a `switch:` platform entry — see the file header
67/// for why. See key_extraction_responder.cpp for what arming actually does.
68///
69/// @note Hardware-confirmed on real RF hardware, but not yet against a third-party hub — see
70/// key_extraction_responder.cpp.
71/// @ingroup hioc_platforms
73 protected:
74 void arm(bool state) override { this->parent_->set_key_extraction_armed(state); }
75 void subscribe_armed(std::function<void(bool)> callback) override {
76 this->parent_->set_key_extraction_armed_callback(std::move(callback));
77 }
78
79 /// @brief Dump configuration to the log.
80 void dump_config() override;
81};
82
83/// @brief Hub-level switch entity: ON arms the 1W key-recovery listener, OFF disarms it
84/// immediately. Publishes its own state changes when the hub disarms itself (after a key is
85/// recovered, or on auto-off timeout), not just on a user-initiated toggle.
86///
87/// The one-way sibling of IOHomeAcceptForeignPairingSwitch: same hub-level, non-device-bound
88/// shape, created dynamically from `home_io_control.recover_oneway_key: true`. See
89/// oneway_key_adoption.cpp for what arming actually does.
90///
91/// The two features are deliberately independent: 2W key extraction impersonates an unpaired
92/// device so a foreign hub pairs *to* us, while this one only listens for a key a 1W device
93/// broadcasts of its own accord. Arming one never arms the other.
94/// @ingroup hioc_platforms
96 protected:
97 void arm(bool state) override { this->parent_->set_oneway_key_adoption_armed(state); }
98 void subscribe_armed(std::function<void(bool)> callback) override {
99 this->parent_->set_oneway_key_adoption_armed_callback(std::move(callback));
100 }
101
102 /// @brief Dump configuration to the log.
103 void dump_config() override;
104};
105
106/// @brief Button entity that triggers device discovery and pairing when pressed in Home Assistant.
107/// @ingroup hioc_platforms
108class IOHomeDiscoverButton : public button::Button, public Component, public HubBoundEntity {
109 public:
110 void dump_config() override {}
111
112 protected:
113 /// @brief When button is pressed, queue a discovery/pair operation.
114 void press_action() override { this->parent_->queue_discover_and_pair(); }
115};
116
117/// @brief Button entity that runs the `scan_paired_devices` roll-call when pressed.
118///
119/// The same operation as the native API action of that name, which is unchanged and still
120/// registered — this is an additional trigger path, not a replacement. It exists because the
121/// roll-call is the fastest bring-up route for the common case of a user who already holds a
122/// system key extracted from a hub that has paired all its devices: every device that trusts the
123/// key answers with a ready-to-paste YAML snippet, with no pairing handshake at all. Reaching that
124/// through Developer Tools was a speed bump on a one-shot step.
125///
126/// Output stays where the action puts it: the multi-line log and the
127/// `esphome.home_io_control_action_result` event. Deliberately no companion result sensor — unlike
128/// the pairing button's "Last Pairing Result", the roll-call report is a multi-device document,
129/// not a state.
130///
131/// Created only when `home_io_control.scan_paired_devices_button: true` (see `__init__.py`'s
132/// `_create_scan_paired_devices_button()`), unlike IOHomeDiscoverButton which is an unconditional
133/// `button:` platform entry — see the file header for why hub entities come from the hub block.
134/// @ingroup hioc_platforms
135class IOHomeScanPairedDevicesButton : public button::Button, public Component, public HubBoundEntity {
136 public:
137 void dump_config() override {}
138
139 protected:
140 /// @brief When pressed, run the roll-call — see
141 /// IOHomeControlComponent::trigger_scan_paired_devices() for the busy-guard rationale.
142 void press_action() override { this->parent_->trigger_scan_paired_devices(); }
143};
144
145/// @brief Diagnostic text sensor that publishes PairingTelemetry::result_sensor_string()
146/// after every pairing attempt.
147///
148/// The published string is the frozen `v1;...` format documented on
149/// PairingTelemetry::result_sensor_string() — the Phase 2 automated-rig read-back contract.
150/// Nothing is published before the first pairing attempt of this boot.
151/// @ingroup hioc_platforms
152class IOHomePairingResultTextSensor : public text_sensor::TextSensor, public Component, public HubBoundEntity {
153 public:
154 /// @brief Register the pairing-result callback.
155 void setup() override;
156
157 /// @brief Dump text-sensor configuration to the log.
158 void dump_config() override;
159
160 /// @brief Get setup priority so the parent hub is available first.
161 /// @return setup_priority::DATA.
162 [[nodiscard]] float get_setup_priority() const override { return setup_priority::DATA; }
163
164 protected:
165 /// @brief Publish the latest pairing telemetry result string.
166 void on_pairing_result_();
167};
168
169} // namespace home_io_control
170} // namespace esphome
Shared body for the hub-level arming switches.
float get_setup_priority() const final
Get setup priority so the parent hub is available first.
virtual void arm(bool state)=0
Arm or disarm this switch's listener on the hub.
void write_state(bool state) final
Forward the toggle to the hub and publish the state.
virtual void subscribe_armed(std::function< void(bool)> callback)=0
Subscribe callback to the hub's armed-state changes for this listener.
void setup() final
Register the armed-state callback so this entity mirrors the hub's own disarm events.
Mixin for entities bound to the hub itself rather than to one device.
Hub-level switch entity: ON arms the key-extraction responder for 10 minutes, OFF disarms it immediat...
void arm(bool state) override
Arm or disarm this switch's listener on the hub.
void subscribe_armed(std::function< void(bool)> callback) override
Subscribe callback to the hub's armed-state changes for this listener.
void dump_config() override
Dump configuration to the log.
Button entity that triggers device discovery and pairing when pressed in Home Assistant.
void press_action() override
When button is pressed, queue a discovery/pair operation.
Diagnostic text sensor that publishes PairingTelemetry::result_sensor_string() after every pairing at...
void setup() override
Register the pairing-result callback.
void on_pairing_result_()
Publish the latest pairing telemetry result string.
float get_setup_priority() const override
Get setup priority so the parent hub is available first.
void dump_config() override
Dump text-sensor configuration to the log.
Hub-level switch entity: ON arms the 1W key-recovery listener, OFF disarms it immediately.
void subscribe_armed(std::function< void(bool)> callback) override
Subscribe callback to the hub's armed-state changes for this listener.
void arm(bool state) override
Arm or disarm this switch's listener on the hub.
void dump_config() override
Dump configuration to the log.
Button entity that runs the scan_paired_devices roll-call when pressed.
void press_action() override
When pressed, run the roll-call — see IOHomeControlComponent::trigger_scan_paired_devices() for the b...
IO-Homecontrol ESPHome component — protocol controller.
Shared device-binding mixins for IO-Homecontrol entity platforms.