Home IO Control
ESPHome add-on for IO-Homecontrol devices
Loading...
Searching...
No Matches
__init__.py
Go to the documentation of this file.
1## @file
2## @brief ESPHome hub schema and code generation for Home IO Control.
3## @ingroup hioc_codegen
4##
5## Defines the top-level ``home_io_control:`` schema (CONFIG_SCHEMA, FINAL_VALIDATE_SCHEMA)
6## and the hub's to_code(). The parts it composes live in leaf modules that never import
7## this file: hub_names.py (YAML keys, C++ handles), hub_validators.py (option tables and
8## field validators), oneway_controllers.py, hub_entities.py and lr1121_update_codegen.py.
9## ESPHome requires the component's schema, to_code() and metadata to live here.
10
11import esphome.codegen as cg
12import esphome.config_validation as cv
13from esphome import pins
14from esphome.components import spi
15from esphome.const import CONF_ID
16
17from . import tuning as tuning_module
18from .hub_names import (
19 CONF_ACCEPT_FOREIGN_PAIRING,
20 CONF_ACCEPT_FOREIGN_PAIRING_SWITCH_ID,
21 CONF_BUSY_PIN,
22 CONF_DIAGNOSTIC_PROBES,
23 CONF_DIO0_PIN,
24 CONF_DIO1_PIN,
25 CONF_DIO4_PIN,
26 CONF_DISCOVER_AND_PAIR_BUTTON,
27 CONF_EXPOSED_SENDERS,
28 CONF_FEM,
29 CONF_FEM_EN_PIN,
30 CONF_FEM_PA_PIN,
31 CONF_LR1121_FIRMWARE_UPDATE,
32 CONF_NODE_ID,
33 CONF_ONEWAY_CONTROLLERS,
34 CONF_PA_PIN,
35 CONF_RADIO_TYPE,
36 CONF_RECOVER_ONEWAY_KEY,
37 CONF_RECOVER_ONEWAY_KEY_SWITCH_ID,
38 CONF_RST_PIN,
39 CONF_SCAN_PAIRED_DEVICES_BUTTON,
40 CONF_SYSTEM_KEY,
41 CONF_TCXO_VOLTAGE,
42 CONF_TX_POWER,
43 CONF_VFEM_PIN,
44 IOHomeAcceptForeignPairingSwitch,
45 IOHomeControlComponent,
46 IOHomeRecoverOneWayKeySwitch,
47)
48from .hub_validators import (
49 FEM_PROFILES,
50 PA_PIN_OPTIONS,
51 RADIO_TYPE_OPTIONS,
52 SPI_RADIO_TYPES,
53 TCXO_VOLTAGE_OPTIONS,
54 validate_fem,
55 validate_radio_transport,
56 validate_device_id,
57 validate_node_id,
58 validate_system_key,
59)
60from .lr1121_update_codegen import (
61 LR1121_FIRMWARE_UPDATE_SCHEMA,
62 create_lr1121_firmware_update,
63 validate_lr1121_firmware_update,
64)
65from .oneway_controllers import (
66 ONEWAY_CONTROLLER_SCHEMA,
67 create_oneway_controller_entities,
68 final_validate_oneway_controller_addresses,
69 validate_oneway_controllers,
70 oneway_controller_expression,
71)
72from .hub_entities import (
73 create_device_event_sensor,
74 create_discover_and_pair_button,
75 create_hub_arming_switch,
76 create_scan_paired_devices_button,
77 inject_accept_foreign_pairing_switch_id,
78 inject_device_event_sensor_id,
79 inject_discover_and_pair_button_id,
80 inject_discover_and_pair_result_sensor_id,
81 inject_recover_oneway_key_switch_id,
82 inject_scan_paired_devices_button_id,
83)
84
85# Re-exported for the platform modules (cover.py, platform_common.py, ...), which
86# import these names from the package itself. IOHomeControlComponent and validate_device_id are
87# re-exported too; this file already imports them above for its own use.
88from .hub_names import ( # noqa: F401
89 CONF_HOME_IO_CONTROL_ID,
90 CONF_LOW_POWER,
91 home_io_control_ns,
92)
93from .hub_validators import ( # noqa: F401
94 device_type_expression,
95 inherit_esphome_device,
96 validate_device_type,
97 validate_linked_remote_entry,
98 validate_status_poll_interval,
99)
100
101DEPENDENCIES = ["api", "spi"]
102AUTO_LOAD = [
103 "binary_sensor",
104 "button",
105 "climate",
106 "cover",
107 "fan",
108 "light",
109 "lock",
110 "number",
111 "select",
112 "sensor",
113 "switch",
114 "text_sensor",
115 "water_heater",
116]
117MULTI_CONF = False
118
119
120FINAL_VALIDATE_SCHEMA = final_validate_oneway_controller_addresses
121
122
123CONFIG_SCHEMA = cv.All(
124 cv.Schema(
125 {
126 cv.GenerateID(): cv.declare_id(IOHomeControlComponent),
127 cv.Required(CONF_RST_PIN): pins.internal_gpio_output_pin_schema,
128 cv.Optional(CONF_DIO0_PIN): pins.internal_gpio_input_pin_schema,
129 cv.Optional(CONF_DIO4_PIN): pins.internal_gpio_input_pin_schema,
130 # The chip's IRQ line: SX1262's DIO1, or LR1121's DIO9
131 cv.Optional(CONF_DIO1_PIN): pins.internal_gpio_input_pin_schema,
132 cv.Optional(CONF_BUSY_PIN): pins.internal_gpio_input_pin_schema,
133 cv.Required(CONF_NODE_ID): validate_node_id,
134 cv.Required(CONF_SYSTEM_KEY): cv.sensitive(validate_system_key),
135 cv.Optional(CONF_TX_POWER, default=17): cv.int_range(min=0, max=22),
136 cv.Optional(CONF_PA_PIN, default="BOOST"): cv.enum(
137 PA_PIN_OPTIONS, upper=True
138 ),
139 cv.Required(CONF_RADIO_TYPE): cv.enum(RADIO_TYPE_OPTIONS, lower=True),
140 cv.Optional(CONF_FEM_EN_PIN): pins.internal_gpio_output_pin_schema,
141 cv.Optional(CONF_VFEM_PIN): pins.internal_gpio_output_pin_schema,
142 cv.Optional(CONF_FEM_PA_PIN): pins.internal_gpio_output_pin_schema,
143 cv.Optional(CONF_FEM, default="none"): cv.one_of(*FEM_PROFILES, lower=True),
144 cv.Optional(CONF_TCXO_VOLTAGE, default="1_8V"): cv.enum(
145 TCXO_VOLTAGE_OPTIONS, upper=True
146 ),
147 cv.Optional(CONF_EXPOSED_SENDERS, default=[]): cv.ensure_list(
148 validate_device_id
149 ),
150 cv.Optional(CONF_ACCEPT_FOREIGN_PAIRING, default=False): cv.boolean,
151 cv.Optional(CONF_RECOVER_ONEWAY_KEY, default=False): cv.boolean,
152 cv.Optional(CONF_SCAN_PAIRED_DEVICES_BUTTON, default=False): cv.boolean,
153 cv.Optional(CONF_DISCOVER_AND_PAIR_BUTTON, default=False): cv.boolean,
154 cv.Optional(CONF_ONEWAY_CONTROLLERS, default=[]): cv.ensure_list(
155 ONEWAY_CONTROLLER_SCHEMA
156 ),
157 cv.Optional(CONF_DIAGNOSTIC_PROBES, default=False): cv.boolean,
158 cv.Optional(CONF_LR1121_FIRMWARE_UPDATE): LR1121_FIRMWARE_UPDATE_SCHEMA,
159 cv.Optional(tuning_module.CONF_TUNING): tuning_module.TUNING_CONFIG_SCHEMA,
160 }
161 )
162 .extend(cv.COMPONENT_SCHEMA)
163 .extend(spi.spi_device_schema(False, 8e6, "mode0")),
164 inject_accept_foreign_pairing_switch_id,
165 inject_recover_oneway_key_switch_id,
166 inject_scan_paired_devices_button_id,
167 inject_discover_and_pair_button_id,
168 inject_discover_and_pair_result_sensor_id,
169 inject_device_event_sensor_id,
170 validate_oneway_controllers,
171 validate_lr1121_firmware_update,
172 validate_radio_transport,
173 validate_fem,
174)
175
176
177async def to_code(config):
178 # Hub-level management actions and result events are compiled behind native API
179 # feature flags. Home IO Control enables the required compile-time switches here
180 # so users only need a normal `api:` block in YAML.
181 # ESPHome 2026.x additionally gates user-defined actions behind
182 # USE_API_USER_DEFINED_ACTIONS.
183 cg.add_define("USE_API_USER_DEFINED_ACTIONS")
184 cg.add_define("USE_API_CUSTOM_SERVICES")
185 cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
186
187 var = cg.new_Pvariable(config[CONF_ID])
188 await cg.register_component(var, config)
189 if config[CONF_RADIO_TYPE] in SPI_RADIO_TYPES:
190 await spi.register_spi_device(var, config)
191
192 rst_pin = await cg.gpio_pin_expression(config[CONF_RST_PIN])
193 cg.add(var.set_rst_pin(rst_pin))
194
195 if CONF_DIO0_PIN in config:
196 dio0_pin = await cg.gpio_pin_expression(config[CONF_DIO0_PIN])
197 cg.add(var.set_dio0_pin(dio0_pin))
198
199 if CONF_DIO4_PIN in config:
200 dio4_pin = await cg.gpio_pin_expression(config[CONF_DIO4_PIN])
201 cg.add(var.set_dio4_pin(dio4_pin))
202
203 if CONF_DIO1_PIN in config:
204 dio1_pin = await cg.gpio_pin_expression(config[CONF_DIO1_PIN])
205 cg.add(var.set_dio1_pin(dio1_pin))
206
207 if CONF_BUSY_PIN in config:
208 busy_pin = await cg.gpio_pin_expression(config[CONF_BUSY_PIN])
209 cg.add(var.set_busy_pin(busy_pin))
210
211 if CONF_FEM_EN_PIN in config:
212 fem_en_pin = await cg.gpio_pin_expression(config[CONF_FEM_EN_PIN])
213 cg.add(var.set_fem_en_pin(fem_en_pin))
214
215 if CONF_VFEM_PIN in config:
216 vfem_pin = await cg.gpio_pin_expression(config[CONF_VFEM_PIN])
217 cg.add(var.set_vfem_pin(vfem_pin))
218
219 if CONF_FEM_PA_PIN in config:
220 fem_pa_pin = await cg.gpio_pin_expression(config[CONF_FEM_PA_PIN])
221 cg.add(var.set_fem_pa_pin(fem_pa_pin))
222
223 cg.add(var.set_fem_profile(FEM_PROFILES[config[CONF_FEM]]))
224
225 cg.add(var.set_node_id(config[CONF_NODE_ID]))
226 cg.add(var.set_system_key(config[CONF_SYSTEM_KEY]))
227 cg.add(var.set_tx_power(config[CONF_TX_POWER]))
228 cg.add(var.set_pa_pin(config[CONF_PA_PIN]))
229
230 cg.add(var.set_radio_type(config[CONF_RADIO_TYPE]))
231
232 cg.add(var.set_tcxo_voltage(config[CONF_TCXO_VOLTAGE]))
233
234 for sender_id in config[CONF_EXPOSED_SENDERS]:
235 cg.add(var.add_exposed_sender(sender_id))
236
237 for identity in config[CONF_ONEWAY_CONTROLLERS]:
238 cg.add(
239 var.add_oneway_controller(
240 oneway_controller_expression(identity, config[CONF_NODE_ID])
241 )
242 )
243 await create_oneway_controller_entities(identity, var)
244
245 if config[CONF_ACCEPT_FOREIGN_PAIRING]:
246 await create_hub_arming_switch(
247 config,
248 var,
249 cls=IOHomeAcceptForeignPairingSwitch,
250 id_key=CONF_ACCEPT_FOREIGN_PAIRING_SWITCH_ID,
251 name="Recover System Key",
252 )
253
254 if config[CONF_RECOVER_ONEWAY_KEY]:
255 await create_hub_arming_switch(
256 config,
257 var,
258 cls=IOHomeRecoverOneWayKeySwitch,
259 id_key=CONF_RECOVER_ONEWAY_KEY_SWITCH_ID,
260 name="Recover 1W Controller Key",
261 )
262
263 if config[CONF_SCAN_PAIRED_DEVICES_BUTTON]:
264 await create_scan_paired_devices_button(config, var)
265
266 if config[CONF_DISCOVER_AND_PAIR_BUTTON]:
267 await create_discover_and_pair_button(config, var)
268
269 cg.add(var.set_diagnostic_probes_enabled(config[CONF_DIAGNOSTIC_PROBES]))
270 if config[CONF_DIAGNOSTIC_PROBES]:
271 await create_device_event_sensor(config, var)
272
273 if CONF_LR1121_FIRMWARE_UPDATE in config:
274 await create_lr1121_firmware_update(config, var)
275
276 if tuning_module.CONF_TUNING in config:
277 await tuning_module.to_code(config[tuning_module.CONF_TUNING], var)
to_code(config)
Definition __init__.py:177