|
Home IO Control
ESPHome add-on for IO-Homecontrol devices
|
Functions | |
| _no_duplicate_enrollment_classes (value) | |
| derive_oneway_node_id (hub_node_id, identity_id) | |
| _hex_byte_array (hex_string) | |
| _enrollment_classes_initialiser (identity) | |
| _power_class_override_expression (identity) | |
| oneway_controller_expression (identity, hub_node_id) | |
| _reject_node_id_collision (identity_id, node_id, seen_node_ids, derived) | |
| validate_oneway_controllers (config) | |
| _collect_declared_device_addresses (full_config) | |
| final_validate_oneway_controller_addresses (config) | |
| create_oneway_controller_entities (identity, var) | |
Variables | |
| _LOGGER = logging.getLogger(__name__) | |
| dict | ONEWAY_COMMANDS |
| ONEWAY_CONTROLLER_SCHEMA | |
| tuple | _DEVICE_BOUND_DOMAINS = ("cover", "light", "lock", "switch") |
|
protected |
Map every node ID declared as an `io_device_id:` or a bare `linked_remotes:` entry, across every `home_io_control` entity in `full_config`, to a human-readable owner string. Only entries with `platform: home_io_control` are considered — the domains in _DEVICE_BOUND_DOMAINS are shared with every other component that provides a cover/light/ lock/switch platform, and those have nothing to do with this component's address space. `class:` linked_remotes entries name a device *type*, not a node, so they carry no address to collide with and are skipped. CONF_IO_DEVICE_ID/CONF_LINKED_REMOTES are imported locally from platform_common rather than at module level: platform_common imports from the package (`from . import ...`), whose __init__.py imports this module, so a module-level import here would be circular. By the time this function actually runs (final validation, after every used platform module has already been imported), the cycle has already resolved and the import is a plain cache hit.
Definition at line 482 of file oneway_controllers.py.
|
protected |
Render `enrollment_classes:` as a 3-element std::array<DeviceType,3> brace-initialiser. Unset, or fewer than 3, is padded with UNKNOWN (0x00) -- the sentinel effective_enrollment_classes() (oneway_controller.h) reads as "not overridden here" / "skip this slot".
Definition at line 212 of file oneway_controllers.py.
|
protected |
Render a hex string as a C++ brace-initialiser list of bytes.
Definition at line 204 of file oneway_controllers.py.
|
protected |
Reject a repeated class in `enrollment_classes:` -- each just retransmits the same 0x30.
Definition at line 87 of file oneway_controllers.py.
|
protected |
Map `low_power:` to the C++ `std::optional<OneWayPowerClass>` the identity stores.
Tri-state, and the three states are emitted as three distinct values rather than collapsed
here: unset -> `{}` (empty optional), `false` -> ALWAYS_ALIVE, `true` -> LOW_POWER.
An unset key deliberately does **not** pick a shape at codegen time. Resolving it needs the
manufacturer profile, which lives in C++ (`resolve_oneway_wire_profile()`), and splitting that
lookup across two languages is how the ACEI and enrollment-class defaults would drift from this
one. `effective_power_class()` is the single resolver. See ADR 0041, and ADR 0038 for the
shapes themselves.
Definition at line 226 of file oneway_controllers.py.
|
protected |
Raise if `node_id` is already claimed in `seen_node_ids`; no-op otherwise. Shared between validate_oneway_controllers() (collisions against the hub's own node_id and other oneway_controllers entries) and final_validate_oneway_controller_addresses() (collisions against `linked_remotes:`/`io_device_id:` declared elsewhere in the same YAML), so both raise identically-worded errors regardless of which side of the config the other claimant lives on.
Definition at line 281 of file oneway_controllers.py.
| create_oneway_controller_entities | ( | identity, | |
| var ) |
Create one identity's command buttons and its "Last 1W Command" diagnostic sensor.
Same normalization as create_hub_arming_switch() (hub_entities.py): run a bare {id, name}
dict through the platform's own schema so it carries the entity/component defaults register_*() require.
Entity names derive from the identity handle and the command ("awning_remote" + "open" ->
"Awning Remote Open"), mirroring how the cover's favourite/vent companions derive theirs. The
*IDs* follow the documented `<identity_id>_<command>` rule instead, because those are what a
`time_based` cover composes against.
Definition at line 540 of file oneway_controllers.py.
| derive_oneway_node_id | ( | hub_node_id, | |
| identity_id ) |
Derive a stable 3-byte 1W source address from the hub's node_id and an identity handle. `node_id` is optional on a `oneway_controllers:` entry because asking a user to invent a 3-byte radio address is an unanswerable question — nothing tells them which addresses are safe, and colliding with a real remote in range silently desyncs both transmitters' rolling sequence counters. Deriving one removes the decision while leaving an explicit value available to anyone who needs it. The derivation is done here, at schema time, rather than at runtime on the device, so that a derived address participates in the same collision checks as a configured one and a clash fails the build instead of surfacing as a device that silently ignores commands. It is a pure function of (hub node_id, identity id), so it is stable across builds and reproducible from the YAML alone. Uses BLAKE2b rather than Python's hash(), which is salted per-process and would produce a different address on every compile.
Definition at line 180 of file oneway_controllers.py.
| final_validate_oneway_controller_addresses | ( | config | ) |
Extend the oneway_controllers address-collision check to addresses declared outside `home_io_control:` — a `linked_remotes:` entry or an `io_device_id:` on some other entity in this same YAML. Runs as FINAL_VALIDATE_SCHEMA rather than inside validate_oneway_controllers() because only final validation has access to the full cross-component config (`fv.full_config`) — `cover:`/`light:`/`lock:`/`switch:` entries are validated independently of `home_io_control:`'s own CONFIG_SCHEMA and are not visible to it. By this point validate_oneway_controllers() has already run, so every identity's `node_id` (derived or explicit) is resolved.
Definition at line 516 of file oneway_controllers.py.
| oneway_controller_expression | ( | identity, | |
| hub_node_id ) |
Generate the C++ OneWayControllerIdentity initialiser for one configured identity. Emitted as a designated initialiser so the generated code reads like the YAML that produced it, and so adding a field to the struct cannot silently shift an existing value.
Definition at line 244 of file oneway_controllers.py.
| validate_oneway_controllers | ( | config | ) |
Resolve per-identity defaults and reject address/handle collisions at compile time. Runs as a post-validator on the whole hub config because every rule here needs the hub's own `node_id`/`system_key`, which a per-entry validator cannot see. Only checks addresses visible within `home_io_control:` itself (its own `node_id` and every configured `oneway_controllers` entry) — see final_validate_oneway_controller_addresses() below for the matching check against `linked_remotes:`/`io_device_id:` declared elsewhere in the same YAML, which needs the full cross-component config and so cannot run here. Even together the two cannot see a real remote the user has never mentioned to this config at all; see derive_oneway_node_id()'s own docstring for why that residual risk cannot be validated away.
Definition at line 305 of file oneway_controllers.py.
|
protected |
Definition at line 479 of file oneway_controllers.py.
|
protected |
Definition at line 62 of file oneway_controllers.py.
| dict home_io_control.oneway_controllers.ONEWAY_COMMANDS |
Definition at line 68 of file oneway_controllers.py.
| home_io_control.oneway_controllers.ONEWAY_CONTROLLER_SCHEMA |
Definition at line 103 of file oneway_controllers.py.