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:`` YAML schema, shared validators, and the
6## generated C++ hub component wiring used by the platform modules.
7
8import esphome.codegen as cg
9import esphome.config_validation as cv
10from esphome import pins
11from esphome.components import spi
12from esphome.const import CONF_ID
13
14DEPENDENCIES = ["api", "spi"]
15AUTO_LOAD = ["button", "cover", "light", "lock", "switch", "text_sensor"]
16MULTI_CONF = False
17
18CONF_HOME_IO_CONTROL_ID = "home_io_control_id"
19CONF_RST_PIN = "rst_pin"
20CONF_DIO0_PIN = "dio0_pin"
21CONF_DIO4_PIN = "dio4_pin"
22CONF_DIO1_PIN = "dio1_pin"
23CONF_BUSY_PIN = "busy_pin"
24CONF_NODE_ID = "node_id"
25CONF_SYSTEM_KEY = "system_key"
26CONF_TX_POWER = "tx_power"
27CONF_PA_PIN = "pa_pin"
28CONF_RADIO_TYPE = "radio_type"
29CONF_FEM_EN_PIN = "fem_en_pin"
30CONF_VFEM_PIN = "vfem_pin"
31CONF_FEM_PA_PIN = "fem_pa_pin"
32CONF_TCXO_VOLTAGE = "tcxo_voltage"
33MIN_STATUS_POLL_INTERVAL_MS = 500
34
35home_io_control_ns = cg.esphome_ns.namespace("home_io_control")
36IOHomeControlComponent = home_io_control_ns.class_(
37 "IOHomeControlComponent", cg.Component, spi.SPIDevice
38)
39
40PA_PIN_OPTIONS = {
41 "BOOST": 0x80,
42 "RFO": 0x00,
43}
44
45RADIO_TYPE_OPTIONS = {
46 "sx1276": "sx1276",
47 "sx1262": "sx1262",
48}
49
50TCXO_VOLTAGE_OPTIONS = {
51 "1_6V": 0x01,
52 "1_7V": 0x02,
53 "1_8V": 0x03,
54 "2_2V": 0x04,
55 "2_4V": 0x05,
56 "2_7V": 0x06,
57 "3_0V": 0x07,
58 "3_3V": 0x08,
59}
60
61DEVICE_TYPE_OPTIONS = {
62 "unknown": 0x00,
63 "venetian_blind": 0x01,
64 "roller_shutter": 0x02,
65 "awning": 0x03,
66 "window_opener": 0x04,
67 "garage_opener": 0x05,
68 "light": 0x06,
69 "gate_opener": 0x07,
70 "rolling_door_opener": 0x08,
71 "lock": 0x09,
72 "blind": 0x0A,
73 "screen": 0x0B,
74 "dual_shutter": 0x0D,
75 "heating_temperature_interface": 0x0E,
76 "on_off_switch": 0x0F,
77 "horizontal_awning": 0x10,
78 "external_venetian_blind": 0x11,
79 "louvre_blind": 0x12,
80 "curtain_track": 0x13,
81 "intrusion_alarm": 0x17,
82 "swinging_shutter": 0x18,
83}
84
85
87 """Validate io_device_type as a named string or integer 0-255."""
88 if isinstance(value, int):
89 return cv.int_range(min=0, max=0xFF)(value)
90
91 if isinstance(value, str):
92 normalized = cv.string_strict(value).strip().lower()
93 if normalized in DEVICE_TYPE_OPTIONS:
94 return DEVICE_TYPE_OPTIONS[normalized]
95 try:
96 return cv.int_range(min=0, max=0xFF)(int(normalized, 0))
97 except ValueError as err:
98 raise cv.Invalid(
99 "Device type must be a known name or an integer in the range 0..255 (for example 0x11)"
100 ) from err
101
102 raise cv.Invalid(
103 "Device type must be a known name or an integer in the range 0..255"
104 )
105
106
108 """Generate a C++ static_cast expression for a validated device type."""
109 return cg.RawExpression(
110 f"static_cast<esphome::home_io_control::DeviceType>(0x{value:02X})"
111 )
112
113
115 """Validate node_id as exactly 6 hex characters (3 bytes)."""
116 value = cv.string_strict(value).upper()
117 if len(value) != 6:
118 raise cv.Invalid("Node ID must be exactly 6 hex characters (3 bytes)")
119 try:
120 int(value, 16)
121 except ValueError:
122 raise cv.Invalid("Node ID must be valid hexadecimal")
123 return value
124
125
127 """Validate system_key as exactly 32 hex characters (16 bytes)."""
128 value = cv.string_strict(value).upper()
129 if len(value) != 32:
130 raise cv.Invalid("System key must be exactly 32 hex characters (16 bytes)")
131 try:
132 int(value, 16)
133 except ValueError:
134 raise cv.Invalid("System key must be valid hexadecimal")
135 return value
136
137
139 """Validate io_device_id as exactly 6 hex characters (3 bytes)."""
140 value = cv.string_strict(value).upper()
141 if len(value) != 6:
142 raise cv.Invalid("Device ID must be exactly 6 hex characters (3 bytes)")
143 try:
144 int(value, 16)
145 except ValueError:
146 raise cv.Invalid("Device ID must be valid hexadecimal")
147 return value
148
149
151 """Validate status_poll_interval is at least MIN_STATUS_POLL_INTERVAL_MS."""
152 value = cv.positive_time_period_milliseconds(value)
153 if value.total_milliseconds < MIN_STATUS_POLL_INTERVAL_MS:
154 raise cv.Invalid(
155 f"status_poll_interval must be at least {MIN_STATUS_POLL_INTERVAL_MS}ms"
156 )
157 return value
158
159
160CONFIG_SCHEMA = (
161 cv.Schema(
162 {
163 cv.GenerateID(): cv.declare_id(IOHomeControlComponent),
164 cv.Required(CONF_RST_PIN): pins.internal_gpio_output_pin_schema,
165 cv.Optional(CONF_DIO0_PIN): pins.internal_gpio_input_pin_schema,
166 cv.Optional(CONF_DIO4_PIN): pins.internal_gpio_input_pin_schema,
167 cv.Optional(CONF_DIO1_PIN): pins.internal_gpio_input_pin_schema,
168 cv.Optional(CONF_BUSY_PIN): pins.internal_gpio_input_pin_schema,
169 cv.Required(CONF_NODE_ID): validate_node_id,
170 cv.Required(CONF_SYSTEM_KEY): validate_system_key,
171 cv.Optional(CONF_TX_POWER, default=17): cv.int_range(min=0, max=22),
172 cv.Optional(CONF_PA_PIN, default="BOOST"): cv.enum(
173 PA_PIN_OPTIONS, upper=True
174 ),
175 cv.Optional(CONF_RADIO_TYPE): cv.enum(RADIO_TYPE_OPTIONS, lower=True),
176 cv.Optional(CONF_FEM_EN_PIN): pins.internal_gpio_output_pin_schema,
177 cv.Optional(CONF_VFEM_PIN): pins.internal_gpio_output_pin_schema,
178 cv.Optional(CONF_FEM_PA_PIN): pins.internal_gpio_output_pin_schema,
179 cv.Optional(CONF_TCXO_VOLTAGE, default="1_8V"): cv.enum(
180 TCXO_VOLTAGE_OPTIONS, upper=True
181 ),
182 }
183 )
184 .extend(cv.COMPONENT_SCHEMA)
185 .extend(spi.spi_device_schema(True, 8e6, "mode0"))
186)
187
188
189async def to_code(config):
190 # Hub-level management actions and result events are compiled behind native API
191 # feature flags. Home IO Control enables the required compile-time switches here
192 # so users only need a normal `api:` block in YAML.
193 # ESPHome 2026.x additionally gates user-defined actions behind
194 # USE_API_USER_DEFINED_ACTIONS.
195 cg.add_define("USE_API_USER_DEFINED_ACTIONS")
196 cg.add_define("USE_API_CUSTOM_SERVICES")
197 cg.add_define("USE_API_HOMEASSISTANT_SERVICES")
198
199 var = cg.new_Pvariable(config[CONF_ID])
200 await cg.register_component(var, config)
201 await spi.register_spi_device(var, config)
202
203 rst_pin = await cg.gpio_pin_expression(config[CONF_RST_PIN])
204 cg.add(var.set_rst_pin(rst_pin))
205
206 if CONF_DIO0_PIN in config:
207 dio0_pin = await cg.gpio_pin_expression(config[CONF_DIO0_PIN])
208 cg.add(var.set_dio0_pin(dio0_pin))
209
210 if CONF_DIO4_PIN in config:
211 dio4_pin = await cg.gpio_pin_expression(config[CONF_DIO4_PIN])
212 cg.add(var.set_dio4_pin(dio4_pin))
213
214 if CONF_DIO1_PIN in config:
215 dio1_pin = await cg.gpio_pin_expression(config[CONF_DIO1_PIN])
216 cg.add(var.set_dio1_pin(dio1_pin))
217
218 if CONF_BUSY_PIN in config:
219 busy_pin = await cg.gpio_pin_expression(config[CONF_BUSY_PIN])
220 cg.add(var.set_busy_pin(busy_pin))
221
222 if CONF_FEM_EN_PIN in config:
223 fem_en_pin = await cg.gpio_pin_expression(config[CONF_FEM_EN_PIN])
224 cg.add(var.set_fem_en_pin(fem_en_pin))
225
226 if CONF_VFEM_PIN in config:
227 vfem_pin = await cg.gpio_pin_expression(config[CONF_VFEM_PIN])
228 cg.add(var.set_vfem_pin(vfem_pin))
229
230 if CONF_FEM_PA_PIN in config:
231 fem_pa_pin = await cg.gpio_pin_expression(config[CONF_FEM_PA_PIN])
232 cg.add(var.set_fem_pa_pin(fem_pa_pin))
233
234 cg.add(var.set_node_id(config[CONF_NODE_ID]))
235 cg.add(var.set_system_key(config[CONF_SYSTEM_KEY]))
236 cg.add(var.set_tx_power(config[CONF_TX_POWER]))
237 cg.add(var.set_pa_pin(config[CONF_PA_PIN]))
238
239 if CONF_RADIO_TYPE in config:
240 cg.add(var.set_radio_type(config[CONF_RADIO_TYPE]))
241
242 cg.add(var.set_tcxo_voltage(config[CONF_TCXO_VOLTAGE]))
device_type_expression(value)
Definition __init__.py:107
validate_device_type(value)
Definition __init__.py:86
to_code(config)
Definition __init__.py:189
validate_node_id(value)
Definition __init__.py:114
validate_status_poll_interval(value)
Definition __init__.py:150
validate_device_id(value)
Definition __init__.py:138
validate_system_key(value)
Definition __init__.py:126