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