10import esphome.codegen
as cg
11import esphome.config_validation
as cv
12from esphome.components
import number, select
13from esphome.const
import CONF_ID, CONF_NAME, ENTITY_CATEGORY_CONFIG
14from esphome.core
import ID
16home_io_control_ns = cg.esphome_ns.namespace(
"home_io_control")
19CONF_UI_CONTROLS =
"ui_controls"
22_COMPANION_ID_BASE =
"home_io_control"
25CONF_SX1262_RX_BANDWIDTH =
"sx1262_rx_bandwidth"
26CONF_SX1262_RESPONSE_PREAMBLE =
"sx1262_response_preamble"
27CONF_SX1262_POST_TX_SETTLE_US =
"sx1262_post_tx_settle_us"
28CONF_SX1276_RX_BANDWIDTH =
"sx1276_rx_bandwidth"
29CONF_SX1276_RESPONSE_PREAMBLE =
"sx1276_response_preamble"
30CONF_SX1276_DISCOVERY_HOP_SLICE_MS =
"sx1276_discovery_hop_slice_ms"
31CONF_SX1262_DISCOVERY_HOP_SLICE_MS =
"sx1262_discovery_hop_slice_ms"
32CONF_LR1121_RX_BANDWIDTH =
"lr1121_rx_bandwidth"
33CONF_LR1121_RESPONSE_PREAMBLE =
"lr1121_response_preamble"
34CONF_LR1121_POST_TX_SETTLE_US =
"lr1121_post_tx_settle_us"
35CONF_LR1121_DISCOVERY_HOP_SLICE_MS =
"lr1121_discovery_hop_slice_ms"
36CONF_COLD_BROADCAST_REPLY_PREAMBLE =
"cold_broadcast_reply_preamble"
37CONF_NORMAL_START_PREAMBLE =
"normal_start_preamble"
38CONF_LBT_MAX_RETRIES =
"lbt_max_retries"
39CONF_LBT_RSSI_THRESHOLD_DBM =
"lbt_rssi_threshold_dbm"
40CONF_LOW_POWER_WAKE_BELIEF =
"low_power_wake_belief"
41CONF_LOW_POWER_WAKE_PREAMBLE =
"low_power_wake_preamble"
42CONF_EXCHANGE_START_RESPONSE_WAIT_MS =
"exchange_start_response_wait_ms"
43CONF_EXCHANGE_RESPONSE_WAIT_MS =
"exchange_response_wait_ms"
44CONF_EXCHANGE_TOTAL_BUDGET_MS =
"exchange_total_budget_ms"
47CONF_PAIRING_DISCOVERY_COMMANDS =
"pairing_discovery_commands"
48CONF_PAIRING_DISCOVERY_DESTINATION =
"pairing_discovery_destination"
49CONF_PAIRING_DISCOVERY_PAYLOAD =
"pairing_discovery_payload"
50CONF_PAIRING_DISCOVERY_LOW_POWER =
"pairing_discovery_low_power"
51CONF_PAIRING_DISCOVERY_ACK_CAPABLE =
"pairing_discovery_ack_capable"
52CONF_PAIRING_DISCOVERY_PREAMBLE =
"pairing_discovery_preamble"
53CONF_PAIRING_DISCOVERY_WAIT_MS =
"pairing_discovery_wait_ms"
54CONF_PAIRING_DISCOVERY_INITIAL_DWELL_MS =
"pairing_discovery_initial_dwell_ms"
55CONF_PAIRING_KEY_EXCHANGE_RETRIES =
"pairing_key_exchange_retries"
56CONF_SCAN_POWER_CLASSES =
"scan_power_classes"
57CONF_PAIRING_DISCOVER_CONFIRM =
"pairing_discover_confirm"
58CONF_PAIRING_DISCOVERY_LISTEN_CHANNELS =
"pairing_discovery_listen_channels"
59CONF_PAIRING_KEY_INIT_DELAY_MS =
"pairing_key_init_delay_ms"
60CONF_PAIRING_KEY_TRANSFER =
"pairing_key_transfer"
63TuningConfig = home_io_control_ns.class_(
"TuningConfig")
64IOHomeTuningNumber = home_io_control_ns.class_(
65 "IOHomeTuningNumber", number.Number, cg.Component
67IOHomeTuningSelect = home_io_control_ns.class_(
68 "IOHomeTuningSelect", select.Select, cg.Component
74SX1262RxBandwidth = home_io_control_ns.enum(
"SX1262RxBandwidth", is_class=
True)
75SX1276RxBandwidth = home_io_control_ns.enum(
"SX1276RxBandwidth", is_class=
True)
76LR1121RxBandwidth = home_io_control_ns.enum(
"LR1121RxBandwidth", is_class=
True)
77DiscoveryCommand = home_io_control_ns.enum(
"DiscoveryCommand", is_class=
True)
78ScanPowerClasses = home_io_control_ns.enum(
"ScanPowerClasses", is_class=
True)
79DiscoverConfirmMode = home_io_control_ns.enum(
"DiscoverConfirmMode", is_class=
True)
80DiscoveryListenChannels = home_io_control_ns.enum(
"DiscoveryListenChannels", is_class=
True)
81KeyTransferMode = home_io_control_ns.enum(
"KeyTransferMode", is_class=
True)
85SX1262_BANDWIDTH_OPTIONS = {
86 "39.0": SX1262RxBandwidth.BW_39_0_KHZ,
87 "46.9": SX1262RxBandwidth.BW_46_9_KHZ,
88 "58.6": SX1262RxBandwidth.BW_58_6_KHZ,
89 "78.2": SX1262RxBandwidth.BW_78_2_KHZ,
90 "117.3": SX1262RxBandwidth.BW_117_3_KHZ,
91 "156.2": SX1262RxBandwidth.BW_156_2_KHZ,
92 "187.2": SX1262RxBandwidth.BW_187_2_KHZ,
95SX1276_BANDWIDTH_OPTIONS = {
96 "20.8": SX1276RxBandwidth.BW_20_8_KHZ,
97 "41.7": SX1276RxBandwidth.BW_41_7_KHZ,
98 "62.5": SX1276RxBandwidth.BW_62_5_KHZ,
99 "83.3": SX1276RxBandwidth.BW_83_3_KHZ,
100 "125.0": SX1276RxBandwidth.BW_125_0_KHZ,
110LR1121_BANDWIDTH_OPTIONS = {
111 "39.0": LR1121RxBandwidth.BW_39_0_KHZ,
112 "46.9": LR1121RxBandwidth.BW_46_9_KHZ,
113 "58.6": LR1121RxBandwidth.BW_58_6_KHZ,
114 "78.2": LR1121RxBandwidth.BW_78_2_KHZ,
115 "117.3": LR1121RxBandwidth.BW_117_3_KHZ,
116 "156.2": LR1121RxBandwidth.BW_156_2_KHZ,
117 "187.2": LR1121RxBandwidth.BW_187_2_KHZ,
120DISCOVERY_COMMAND_OPTIONS = {
121 "0x28": DiscoveryCommand.DISCOVER,
122 "0x2E": DiscoveryCommand.DISCOVER_ALT,
133DISCOVERY_COMMAND_PRESETS = [
152DISCOVERY_DESTINATION_OPTIONS = [
160PAIRING_DISCOVERY_PAYLOAD_OPTIONS = [
"none",
"0x00"]
167SCAN_POWER_CLASSES_OPTIONS = {
168 "both": ScanPowerClasses.BOTH,
169 "always_alive": ScanPowerClasses.ALWAYS_ALIVE,
170 "low_power": ScanPowerClasses.LOW_POWER,
178DISCOVER_CONFIRM_MODE_OPTIONS = {
179 "skip": DiscoverConfirmMode.SKIP,
180 "send": DiscoverConfirmMode.SEND,
181 "send_with_ack": DiscoverConfirmMode.SEND_WITH_ACK,
188DISCOVERY_LISTEN_CHANNELS_OPTIONS = {
189 "skip_request": DiscoveryListenChannels.SKIP_REQUEST,
190 "all": DiscoveryListenChannels.ALL,
197KEY_TRANSFER_MODE_OPTIONS = {
198 "auto": KeyTransferMode.AUTO,
199 "push": KeyTransferMode.PUSH,
200 "pull": KeyTransferMode.PULL,
205 """Config-dict key under which a parameter's injected companion entity ID is stored."""
206 return f
"_{param_key}_id"
213 CONF_SX1262_RX_BANDWIDTH:
"Radio SX1262 RX Bandwidth (kHz)",
214 CONF_SX1262_RESPONSE_PREAMBLE:
"Radio SX1262 Response Preamble",
215 CONF_SX1262_POST_TX_SETTLE_US:
"Radio SX1262 Post-TX Settle",
216 CONF_SX1276_RX_BANDWIDTH:
"Radio SX1276 RX Bandwidth (kHz)",
217 CONF_SX1276_RESPONSE_PREAMBLE:
"Radio SX1276 Response Preamble",
218 CONF_SX1276_DISCOVERY_HOP_SLICE_MS:
"Radio SX1276 Discovery Hop Slice",
219 CONF_SX1262_DISCOVERY_HOP_SLICE_MS:
"Radio SX1262 Discovery Hop Slice",
220 CONF_LR1121_RX_BANDWIDTH:
"Radio LR1121 RX Bandwidth (kHz)",
221 CONF_LR1121_RESPONSE_PREAMBLE:
"Radio LR1121 Response Preamble",
222 CONF_LR1121_POST_TX_SETTLE_US:
"Radio LR1121 Post-TX Settle",
223 CONF_LR1121_DISCOVERY_HOP_SLICE_MS:
"Radio LR1121 Discovery Hop Slice",
224 CONF_COLD_BROADCAST_REPLY_PREAMBLE:
"Radio Cold Broadcast Reply Preamble",
225 CONF_NORMAL_START_PREAMBLE:
"Radio Normal Start Preamble",
226 CONF_LBT_MAX_RETRIES:
"Radio LBT Max Retries",
227 CONF_LBT_RSSI_THRESHOLD_DBM:
"Radio LBT RSSI Threshold",
228 CONF_LOW_POWER_WAKE_BELIEF:
"Radio Low Power Wake Belief",
229 CONF_LOW_POWER_WAKE_PREAMBLE:
"Radio Low Power Wake Preamble",
230 CONF_EXCHANGE_START_RESPONSE_WAIT_MS:
"Exchange Start Response Wait",
231 CONF_EXCHANGE_RESPONSE_WAIT_MS:
"Exchange Response Wait",
232 CONF_EXCHANGE_TOTAL_BUDGET_MS:
"Exchange Total Budget",
233 CONF_PAIRING_DISCOVERY_COMMANDS:
"Pairing Discovery Commands",
234 CONF_PAIRING_DISCOVERY_DESTINATION:
"Pairing Discovery Destination",
235 CONF_PAIRING_DISCOVERY_PAYLOAD:
"Pairing Discovery Payload",
236 CONF_PAIRING_DISCOVERY_LOW_POWER:
"Pairing Discovery Low Power",
237 CONF_PAIRING_DISCOVERY_ACK_CAPABLE:
"Pairing Discovery ACK Capable",
238 CONF_PAIRING_DISCOVERY_PREAMBLE:
"Pairing Discovery Preamble",
239 CONF_PAIRING_DISCOVERY_WAIT_MS:
"Pairing Discovery Wait",
240 CONF_PAIRING_DISCOVERY_INITIAL_DWELL_MS:
"Pairing Discovery Initial Dwell",
241 CONF_PAIRING_KEY_EXCHANGE_RETRIES:
"Pairing Key Exchange Retries",
242 CONF_SCAN_POWER_CLASSES:
"Pairing Scan Power Classes",
243 CONF_PAIRING_DISCOVER_CONFIRM:
"Pairing Discover Confirm",
244 CONF_PAIRING_KEY_INIT_DELAY_MS:
"Pairing Key Init Delay",
245 CONF_PAIRING_DISCOVERY_LISTEN_CHANNELS:
"Pairing Discovery Listen Channels",
246 CONF_PAIRING_KEY_TRANSFER:
"Pairing Key Transfer",
256 CONF_SX1262_RESPONSE_PREAMBLE: (8, 256, 1,
"B"),
257 CONF_SX1262_POST_TX_SETTLE_US: (0, 2000, 10,
"µs"),
258 CONF_SX1276_RESPONSE_PREAMBLE: (8, 256, 1,
"B"),
259 CONF_SX1276_DISCOVERY_HOP_SLICE_MS: (5, 200, 1,
"ms"),
265 CONF_SX1262_DISCOVERY_HOP_SLICE_MS: (0, 500, 1,
"ms"),
268 CONF_LR1121_RESPONSE_PREAMBLE: (8, 256, 1,
"B"),
269 CONF_LR1121_POST_TX_SETTLE_US: (0, 2000, 10,
"µs"),
270 CONF_LR1121_DISCOVERY_HOP_SLICE_MS: (0, 500, 1,
"ms"),
277 CONF_COLD_BROADCAST_REPLY_PREAMBLE: (8, 256, 1,
"B"),
283 CONF_NORMAL_START_PREAMBLE: (8, 256, 1,
"B"),
289 CONF_PAIRING_DISCOVERY_PREAMBLE: (8, 1024, 1,
"B"),
295 CONF_LOW_POWER_WAKE_PREAMBLE: (1024, 4096, 64,
"B"),
296 CONF_LBT_MAX_RETRIES: (0, 10, 1,
""),
297 CONF_LBT_RSSI_THRESHOLD_DBM: (-95, -70, 1,
"dBm"),
301 CONF_EXCHANGE_START_RESPONSE_WAIT_MS: (200, 4000, 50,
"ms"),
302 CONF_EXCHANGE_RESPONSE_WAIT_MS: (200, 4000, 50,
"ms"),
305 CONF_EXCHANGE_TOTAL_BUDGET_MS: (500, 12000, 100,
"ms"),
306 CONF_PAIRING_DISCOVERY_WAIT_MS: (500, 5000, 50,
"ms"),
307 CONF_PAIRING_DISCOVERY_INITIAL_DWELL_MS: (0, 500, 10,
"ms"),
308 CONF_PAIRING_KEY_EXCHANGE_RETRIES: (1, 5, 1,
""),
311 CONF_PAIRING_KEY_INIT_DELAY_MS: (0, 10000, 100,
"ms"),
324_BOOL_PARAMS = (CONF_PAIRING_DISCOVERY_LOW_POWER, CONF_PAIRING_DISCOVERY_ACK_CAPABLE, CONF_LOW_POWER_WAKE_BELIEF)
325_BOOL_SELECT_OPTIONS = [
"Off",
"On"]
330 CONF_PAIRING_DISCOVERY_COMMANDS: DISCOVERY_COMMAND_PRESETS,
331 CONF_PAIRING_DISCOVERY_DESTINATION: DISCOVERY_DESTINATION_OPTIONS,
332 CONF_PAIRING_DISCOVERY_PAYLOAD: PAIRING_DISCOVERY_PAYLOAD_OPTIONS,
336 CONF_PAIRING_DISCOVERY_LOW_POWER: _BOOL_SELECT_OPTIONS,
337 CONF_PAIRING_DISCOVERY_ACK_CAPABLE: _BOOL_SELECT_OPTIONS,
338 CONF_SX1262_RX_BANDWIDTH: list(SX1262_BANDWIDTH_OPTIONS),
339 CONF_SX1276_RX_BANDWIDTH: list(SX1276_BANDWIDTH_OPTIONS),
340 CONF_LR1121_RX_BANDWIDTH: list(LR1121_BANDWIDTH_OPTIONS),
341 CONF_LOW_POWER_WAKE_BELIEF: _BOOL_SELECT_OPTIONS,
342 CONF_SCAN_POWER_CLASSES: list(SCAN_POWER_CLASSES_OPTIONS),
343 CONF_PAIRING_DISCOVER_CONFIRM: list(DISCOVER_CONFIRM_MODE_OPTIONS),
344 CONF_PAIRING_DISCOVERY_LISTEN_CHANNELS: list(DISCOVERY_LISTEN_CHANNELS_OPTIONS),
345 CONF_PAIRING_KEY_TRANSFER: list(KEY_TRANSFER_MODE_OPTIONS),
350 """Build a validator that requires a string value present in `options`.
352 Works for both list and dict `options` (dict membership tests the keys), so a single
353 factory replaces a hand-written validator per option set while keeping a per-parameter
354 error message. With `coerce_number`, a numeric YAML value (e.g. ``117.3``) is accepted and
355 normalized to its string form, so bare-number options do not force the user to quote them.
359 if coerce_number
and isinstance(value, (int, float))
and not isinstance(value, bool):
361 value = cv.string_strict(value)
362 if value
not in options:
363 raise cv.Invalid(f
"{param_name} must be one of {list(options)}")
370 CONF_SX1262_RX_BANDWIDTH, SX1262_BANDWIDTH_OPTIONS, coerce_number=
True
373 CONF_SX1276_RX_BANDWIDTH, SX1276_BANDWIDTH_OPTIONS, coerce_number=
True
376 CONF_LR1121_RX_BANDWIDTH, LR1121_BANDWIDTH_OPTIONS, coerce_number=
True
379 f
"{CONF_PAIRING_DISCOVERY_COMMANDS} entries", DISCOVERY_COMMAND_OPTIONS
382 CONF_PAIRING_DISCOVERY_DESTINATION, DISCOVERY_DESTINATION_OPTIONS
385 CONF_PAIRING_DISCOVERY_PAYLOAD, PAIRING_DISCOVERY_PAYLOAD_OPTIONS
388 CONF_SCAN_POWER_CLASSES, SCAN_POWER_CLASSES_OPTIONS
391 CONF_PAIRING_DISCOVER_CONFIRM, DISCOVER_CONFIRM_MODE_OPTIONS
394 CONF_PAIRING_DISCOVERY_LISTEN_CHANNELS, DISCOVERY_LISTEN_CHANNELS_OPTIONS
397 CONF_PAIRING_KEY_TRANSFER, KEY_TRANSFER_MODE_OPTIONS
402 """Convert an explicit destination string like '0x00003B' to a list of 3 byte values."""
404 return [int(digits[i : i + 2], 16)
for i
in range(0, 6, 2)]
408 """Convert a payload option string like '0x00' to its numeric byte value."""
409 return int(value, 16)
418TUNING_SCHEMA = cv.Schema(
420 cv.Optional(CONF_UI_CONTROLS, default=
False): cv.boolean,
421 cv.Optional(CONF_SX1262_RX_BANDWIDTH): _validate_bandwidth,
422 cv.Optional(CONF_SX1276_RX_BANDWIDTH): _validate_sx1276_bandwidth,
423 cv.Optional(CONF_LR1121_RX_BANDWIDTH): _validate_lr1121_bandwidth,
424 cv.Optional(CONF_PAIRING_DISCOVERY_COMMANDS): cv.All(
425 cv.ensure_list(_validate_discovery_command),
428 cv.Optional(CONF_PAIRING_DISCOVERY_DESTINATION): _validate_discovery_destination,
429 cv.Optional(CONF_PAIRING_DISCOVERY_PAYLOAD): _validate_discovery_payload,
430 cv.Optional(CONF_SCAN_POWER_CLASSES): _validate_scan_power_classes,
431 cv.Optional(CONF_PAIRING_DISCOVER_CONFIRM): _validate_discover_confirm_mode,
433 CONF_PAIRING_DISCOVERY_LISTEN_CHANNELS
434 ): _validate_discovery_listen_channels,
435 cv.Optional(CONF_PAIRING_KEY_TRANSFER): _validate_key_transfer_mode,
436 **{cv.Optional(key): cv.boolean
for key
in _BOOL_PARAMS},
439 cv.Optional(key): cv.int_range(min=lo, max=hi)
440 for key, (lo, hi, _step, _unit)
in _NUMBER_PARAMS.items()
447 """Declare companion entity IDs for tuning UI controls.
449 ESPHome 2026.x sizes the runtime component vector from the number of IDs
450 declared during validation. If UI entities were created only in to_code(),
451 they could be silently dropped. This post-validator injects declared IDs
452 before the vector is sized.
454 if not config[CONF_UI_CONTROLS]:
460 base = _COMPANION_ID_BASE
462 for key
in _SELECT_OPTIONS:
463 config[
_id_key(key)] = ID(f
"{base}_{key}", is_declaration=
True, type=IOHomeTuningSelect)
464 for key
in _NUMBER_PARAMS:
465 config[
_id_key(key)] = ID(f
"{base}_{key}", is_declaration=
True, type=IOHomeTuningNumber)
470TUNING_CONFIG_SCHEMA = cv.All(TUNING_SCHEMA, _inject_tuning_companion_ids)
474 """Format a Python bool as a C++ boolean literal."""
475 return "true" if value
else "false"
479 """Emit a `struct.field = value;` C++ assignment statement at codegen time.
481 ESPHome's MockObj does not support Python attribute assignment, so struct fields
482 are populated with raw assignment statements. `value` must already be valid C++
483 (an int, or a string such as an enum value or brace-init list).
485 cg.add(cg.RawExpression(f
"{struct}.{field} = {value}"))
489 """Generate C++ code that builds a TuningConfig from the validated YAML.
491 Only keys the user actually provided are emitted; every omitted key keeps the
492 default baked into the C++ TuningConfig struct (sourced from proto_frame.h), so
493 the defaults are never restated here.
495 tuning_id = cv.declare_id(TuningConfig)(
"tuning_config")
496 tuning = cg.new_variable(tuning_id, cg.RawExpression(f
"{TuningConfig}()"))
499 _assign(tuning,
"active",
"true")
502 if CONF_SX1262_RX_BANDWIDTH
in config:
505 "sx1262_rx_bandwidth",
506 SX1262_BANDWIDTH_OPTIONS[config[CONF_SX1262_RX_BANDWIDTH]],
509 if CONF_SX1276_RX_BANDWIDTH
in config:
512 "sx1276_rx_bandwidth",
513 SX1276_BANDWIDTH_OPTIONS[config[CONF_SX1276_RX_BANDWIDTH]],
516 if CONF_LR1121_RX_BANDWIDTH
in config:
519 "lr1121_rx_bandwidth",
520 LR1121_BANDWIDTH_OPTIONS[config[CONF_LR1121_RX_BANDWIDTH]],
523 if CONF_SCAN_POWER_CLASSES
in config:
526 "scan_power_classes",
527 SCAN_POWER_CLASSES_OPTIONS[config[CONF_SCAN_POWER_CLASSES]],
530 if CONF_PAIRING_DISCOVER_CONFIRM
in config:
533 "pairing_discover_confirm",
534 DISCOVER_CONFIRM_MODE_OPTIONS[config[CONF_PAIRING_DISCOVER_CONFIRM]],
537 if CONF_PAIRING_DISCOVERY_LISTEN_CHANNELS
in config:
540 "pairing_discovery_listen_channels",
541 DISCOVERY_LISTEN_CHANNELS_OPTIONS[
542 config[CONF_PAIRING_DISCOVERY_LISTEN_CHANNELS]
546 if CONF_PAIRING_KEY_TRANSFER
in config:
549 "pairing_key_transfer",
550 KEY_TRANSFER_MODE_OPTIONS[config[CONF_PAIRING_KEY_TRANSFER]],
555 if CONF_PAIRING_DISCOVERY_COMMANDS
in config:
556 cg.add(tuning.pairing_discovery_commands.clear())
557 for cmd
in config[CONF_PAIRING_DISCOVERY_COMMANDS]:
559 tuning.pairing_discovery_commands.push_back(
560 DISCOVERY_COMMAND_OPTIONS[cmd]
564 if CONF_PAIRING_DISCOVERY_DESTINATION
in config:
565 dest = config[CONF_PAIRING_DISCOVERY_DESTINATION]
567 _assign(tuning,
"pairing_discovery_destination_auto",
"true")
569 _assign(tuning,
"pairing_discovery_destination_auto",
"false")
573 "pairing_discovery_destination",
574 f
"{{0x{b[0]:02X}, 0x{b[1]:02X}, 0x{b[2]:02X}}}",
577 if CONF_PAIRING_DISCOVERY_PAYLOAD
in config:
578 payload = config[CONF_PAIRING_DISCOVERY_PAYLOAD]
579 if payload ==
"none":
580 _assign(tuning,
"pairing_discovery_payload_enabled",
"false")
582 _assign(tuning,
"pairing_discovery_payload_enabled",
"true")
583 _assign(tuning,
"pairing_discovery_payload", f
"0x{_parse_payload(payload):02X}")
586 for key
in _BOOL_PARAMS:
591 for key
in _NUMBER_PARAMS:
593 _assign(tuning, key, config[key])
599 if CONF_NORMAL_START_PREAMBLE
in config:
600 _assign(tuning,
"normal_start_preamble_from_yaml",
"true")
602 cg.add(var.set_tuning_config(tuning))
606 """Generate number/select entities for the tuning parameters when enabled."""
607 if not config[CONF_UI_CONTROLS]:
611 for key, options
in _SELECT_OPTIONS.items():
615 for key, (min_value, max_value, step, unit)
in _NUMBER_PARAMS.items():
617 config, var, key, min_value=min_value, max_value=max_value, step=step, unit=unit
622 config, var, key, min_value, max_value, step, unit=""
624 """Create a single IOHomeTuningNumber entity.
626 The bare {id, name} dict is normalized through number_schema()+COMPONENT_SCHEMA so it
627 carries the entity/component defaults (disabled_by_default, setup_priority, ...) that
628 register_number()/register_component() require.
630 schema = number.number_schema(
632 unit_of_measurement=unit,
633 entity_category=ENTITY_CATEGORY_CONFIG,
635 entity_config = schema.extend(cv.COMPONENT_SCHEMA)(
638 CONF_NAME: UI_NAMES[key],
641 entity = await number.new_number(
649 await cg.register_component(entity, entity_config)
653 """Create a single IOHomeTuningSelect entity.
655 The bare {id, name} dict is normalized through select_schema()+COMPONENT_SCHEMA so it
656 carries the entity/component defaults that register_select()/register_component() require.
658 entity_config = select.select_schema(
659 IOHomeTuningSelect, entity_category=ENTITY_CATEGORY_CONFIG
660 ).extend(cv.COMPONENT_SCHEMA)(
663 CONF_NAME: UI_NAMES[key],
666 entity = await select.new_select(
672 await cg.register_component(entity, entity_config)
676 """Generate code for the tuning block."""
_create_tuning_entities(config, var)
_apply_tuning_config(config, var)
_parse_destination_to_bytes(value)
_inject_tuning_companion_ids(config)
_create_select(config, var, key, options)
_assign(struct, field, value)
_one_of_string(param_name, options, coerce_number=False)
_create_number(config, var, key, min_value, max_value, step, unit="")