10import esphome.codegen
as cg
11import esphome.config_validation
as cv
13from esphome.components
import button
as button_component
14from esphome.components
import switch
as switch_component
15from esphome.components
import text_sensor
as text_sensor_component
16from esphome.const
import (
19 ENTITY_CATEGORY_CONFIG,
20 ENTITY_CATEGORY_DIAGNOSTIC,
22from esphome.core
import ID
24from .hub_names
import (
25 CONF_ACCEPT_FOREIGN_PAIRING,
26 CONF_ACCEPT_FOREIGN_PAIRING_SWITCH_ID,
27 CONF_DISCOVER_AND_PAIR_BUTTON,
28 CONF_DISCOVER_AND_PAIR_BUTTON_ID,
29 CONF_DISCOVER_AND_PAIR_RESULT_SENSOR_ID,
30 CONF_RECOVER_ONEWAY_KEY,
31 CONF_RECOVER_ONEWAY_KEY_SWITCH_ID,
32 CONF_SCAN_PAIRED_DEVICES_BUTTON,
33 CONF_SCAN_PAIRED_DEVICES_BUTTON_ID,
34 IOHomeAcceptForeignPairingSwitch,
36 IOHomePairingResultTextSensor,
37 IOHomeRecoverOneWayKeySwitch,
38 IOHomeScanPairedDevicesButton,
43 """Shared body for the hub-level entities gated by a bare boolean flag in the
44 `home_io_control:` block (accept_foreign_pairing, recover_oneway_key,
45 scan_paired_devices_button, discover_and_pair_button): declare the entity's ID during
46 validation, under the `{hub_id}_{suffix}` name, only when its flag is set. See
47 companion_id_base() in platform_common.py for why this must happen at validation time rather
50 if not config[flag_key]:
52 parent_id = config[CONF_ID]
53 base = parent_id.id
if parent_id.id
else "home_io_control"
54 config[id_key] = ID(f
"{base}_{suffix}", is_declaration=
True, type=cls)
61 flag_key=CONF_ACCEPT_FOREIGN_PAIRING,
62 id_key=CONF_ACCEPT_FOREIGN_PAIRING_SWITCH_ID,
63 suffix=
"accept_foreign_pairing_switch",
64 cls=IOHomeAcceptForeignPairingSwitch,
71 flag_key=CONF_RECOVER_ONEWAY_KEY,
72 id_key=CONF_RECOVER_ONEWAY_KEY_SWITCH_ID,
73 suffix=
"recover_oneway_key_switch",
74 cls=IOHomeRecoverOneWayKeySwitch,
81 flag_key=CONF_SCAN_PAIRED_DEVICES_BUTTON,
82 id_key=CONF_SCAN_PAIRED_DEVICES_BUTTON_ID,
83 suffix=
"scan_paired_devices_button",
84 cls=IOHomeScanPairedDevicesButton,
91 flag_key=CONF_DISCOVER_AND_PAIR_BUTTON,
92 id_key=CONF_DISCOVER_AND_PAIR_BUTTON_ID,
93 suffix=
"discover_and_pair_button",
94 cls=IOHomeDiscoverButton,
99 """Second ID off the same flag: the button always ships with its "Last Pairing Result" sensor,
100 so both IDs are gated on CONF_DISCOVER_AND_PAIR_BUTTON. _inject_hub_entity_id() already no-ops
101 when the flag is false, so no extra guard is needed here.
105 flag_key=CONF_DISCOVER_AND_PAIR_BUTTON,
106 id_key=CONF_DISCOVER_AND_PAIR_RESULT_SENSOR_ID,
107 suffix=
"pairing_result_sensor",
108 cls=IOHomePairingResultTextSensor,
113 """Create one hub-level entity from its declared ID and a fixed name, bound to the hub.
115 A bare {id, name} dict is run through the platform's entity schema plus COMPONENT_SCHEMA, so
116 it carries the entity/component defaults new_*()/register_component() require, instead of a
117 hand-assembled config dict of its own (tuning.py's _create_number()/_create_select() do the
118 same for the tuning entities).
120 entity_config = schema.extend(cv.COMPONENT_SCHEMA)({CONF_ID: entity_id, CONF_NAME: name})
121 entity = await new_entity(entity_config)
122 await cg.register_component(entity, entity_config)
123 cg.add(entity.set_parent(var))
128 """Create a hub-level arming switch (key extraction or key adoption).
130 ALWAYS_OFF is a security property, not a UX default: every switch built here arms a window
131 (foreign-key extraction or 1W key adoption) that must never come back armed after a reboot.
133 schema = switch_component.switch_schema(
135 default_restore_mode=
"ALWAYS_OFF",
136 entity_category=ENTITY_CATEGORY_CONFIG,
138 await
_create_hub_entity(schema, switch_component.new_switch, config[id_key], name, var)
142 """Create the hub-level "Scan Paired Devices" button.
144 The `scan_paired_devices` native API action is registered independently in C++
145 (ManagementActions::register_actions()) and is unaffected by this key -- the button is an extra
146 trigger onto the same method, not a replacement.
148 schema = button_component.button_schema(IOHomeScanPairedDevicesButton, entity_category=ENTITY_CATEGORY_CONFIG)
150 schema, button_component.new_button, config[CONF_SCAN_PAIRED_DEVICES_BUTTON_ID],
"Scan Paired Devices", var
155 """Create the hub-level "Discover & Pair" button and its "Last Pairing Result" sensor.
157 The two are always created together: the sensor is the only place a pairing attempt's
158 machine-readable outcome ever appears, so a button without it would be a button whose result
161 inherit_esphome_device() is deliberately NOT called: the hub's own config has no `device_id:`
162 slot for these to inherit -- see that function's docstring. This is the one behaviour the
163 deprecated `button:` platform (button.py) had that this flag form cannot reproduce.
165 button_schema = button_component.button_schema(IOHomeDiscoverButton, entity_category=ENTITY_CATEGORY_CONFIG)
167 button_schema, button_component.new_button, config[CONF_DISCOVER_AND_PAIR_BUTTON_ID],
"Discover & Pair", var
169 sensor_schema = text_sensor_component.text_sensor_schema(
170 IOHomePairingResultTextSensor, entity_category=ENTITY_CATEGORY_DIAGNOSTIC
174 text_sensor_component.new_text_sensor,
175 config[CONF_DISCOVER_AND_PAIR_RESULT_SENSOR_ID],
176 "Last Pairing Result",
inject_discover_and_pair_button_id(config)
inject_recover_oneway_key_switch_id(config)
create_scan_paired_devices_button(config, var)
inject_scan_paired_devices_button_id(config)
create_hub_arming_switch(config, var, *, cls, id_key, name)
_inject_hub_entity_id(config, *, flag_key, id_key, suffix, cls)
inject_discover_and_pair_result_sensor_id(config)
create_discover_and_pair_button(config, var)
inject_accept_foreign_pairing_switch_id(config)
_create_hub_entity(schema, new_entity, entity_id, name, var)