pyisyox.classifier module

HA platform classifier for IoX nodedefs.

This module produces a classification of an IoX NodeDef into Home Assistant platform contributions. It is the fallback tier of the two-tier strategy described in the modernization plan:

  1. Type-based classification (consumer-side, e.g. hacs-isy994) — primary path. Native Insteon/Z-Wave nodes carry a real type string ("1.65.69.0" for KeypadLinc dimmer, etc.) that hacs-isy994 already maps to platforms with hardware-aware nuance. Preserve that path.

  2. Nodedef-based classification (this module) — fallback. Fires when the consumer’s type-based lookup returns no match, which in practice means PG3 plugin nodes and any future device class without a hardcoded mapping. The current HA core integration dumps these all into sensor; this classifier escapes that trap.

The classification is three-axis and produces a set of HA platform contributions, not a single platform pick:

  • controllable — at most one of light/switch/climate/lock/cover/ alarm_control_panel, derived from cmds.accepts.

  • triggers — every command in cmds.sends (the node emits these events; e.g., an Insteon OnOffControl paddle that sends DON/ DOF on press becomes a HA device_trigger source).

  • buttons — accept commands pressable with no arguments: parameterless, or every parameter optional (the controller fills in defaults — Insteon BEEP is the canonical case: one optional level param). Excludes QUERY (implicit on every node) and any command claimed by the controllable platform. Surface as HA button entities — press = send the verb with zero args.

  • parameterized_commands — accept commands with at least one required parameter (same QUERY / controllable exclusions). These can’t be plain buttons: each parameter carries an editor ref whose shape drives what input entity it needs. Consumers that don’t yet handle them can ignore this bucket.

  • readings — one entity per property, after filtering out properties already represented by the controllable platform (e.g., ST/OL/ RR on a light are the light’s state, not separate sensors).

  • aux_controls — the unified successor to the readings / parameterized_commands / buttons split: one AuxControl per logical control, with a status and its init-linked write command folded together (read/write coalesced). New consumers should prefer this; the three legacy buckets stay populated unchanged for now.

One HA device per node aggregates entities from these buckets.

class ControllablePlatform(*values)[source]

Bases: StrEnum

The single controllable HA platform a nodedef may map to.

LIGHT
SWITCH
CLIMATE
LOCK
COVER
ALARM_CONTROL_PANEL
class ReadingPlatform(*values)[source]

Bases: StrEnum

HA platform for a property reading entity.

SENSOR
BINARY_SENSOR
class Reading(property, platform, is_enum=False)[source]

Bases: object

A property surfaced as a sensor or binary_sensor entity.

Variables:
Parameters:
property: NodeProperty
platform: ReadingPlatform
is_enum: bool
class AuxPlatform(*values)[source]

Bases: StrEnum

Candidate HA platform for one coalesced aux control.

A candidate — the consumer keeps final say (type-tier-1, bespoke/service routing, HA-device grouping sit on top).

SENSOR
BINARY_SENSOR
NUMBER
SELECT
SWITCH
BUTTON
class AuxControl(id, readable, writable, candidate_platform=None, property=None, command=None, editor_id=None, is_enum=False)[source]

Bases: object

One logical aux control: a read status and/or a write command, coalesced.

The IoX nodedef expresses the read/write pairing via a command parameter’s init (the id of the <st> status it is “initialized and synchronized with” — e.g. a heat-setpoint command’s param init="CLISPH"; an Insteon i3 flags sub-node’s GV0 param init="ST" ⇄ the ST “Mode” status). This is the authoritative pairing key — not naive id matching: the cmd id and the status id can differ (i3 GV0ST).

Controllable-owned ids and QUERY are already excluded (the controllable platform represents those). ST is not special here: it is removed only when a controllable platform owns it; otherwise it is an ordinary coalescable status.

Variables:
  • id (str) – The control id (the status id when read/write paired, else the command id, else the property id).

  • readable (bool) – A backing status property exists (readback source).

  • writable (bool) – An accept command drives it.

  • candidate_platform (pyisyox.classifier.AuxPlatform | None) – Editor-shape-derived HA platform candidate. None only when no editor resolves and there is no readable backing status (a readable control falls back to its property’s read classification — SENSOR/BINARY_SENSOR); write-only with no editor stays None for the consumer.

  • property (pyisyox.schema.nodedef.NodeProperty | None) – The backing NodeProperty, if readable.

  • command (pyisyox.schema.cmd.Command | None) – The driving Command, if writable.

  • editor_id (str | None) – The editor governing the control — the write command’s parameter editor when writable (the write affordance is authoritative), else the property editor.

  • is_enum (bool) – The governing editor carries an enum names map.

Parameters:
id: str
readable: bool
writable: bool
candidate_platform: AuxPlatform | None
property: NodeProperty | None
command: Command | None
editor_id: str | None
is_enum: bool
class ClassificationResult(controllable=None, controllable_command_ids=<factory>, triggers=<factory>, buttons=<factory>, parameterized_commands=<factory>, readings=<factory>, aux_controls=<factory>)[source]

Bases: object

The set of HA platform contributions for one nodedef.

Variables:
  • controllable (pyisyox.classifier.ControllablePlatform | None) – The controllable platform, or None for a read-only / event-only node.

  • controllable_command_ids (frozenset[str]) – Command ids that belong to the controllable platform (so they aren’t double-counted as buttons). Empty when controllable is None.

  • triggers (list[pyisyox.schema.cmd.Command]) – Commands the node emits — surface as device_trigger.

  • buttons (list[pyisyox.schema.cmd.Command]) – Accept commands pressable with zero args (parameterless, or all parameters optional) — one fire-and-forget button entity each. Excludes QUERY and controllable-claimed cmds.

  • parameterized_commands (list[pyisyox.schema.cmd.Command]) – Accept commands with at least one required parameter. Not plain buttons; left for consumers that map parameter editors to input entities. Same QUERY / controllable exclusions as buttons.

  • readings (list[pyisyox.classifier.Reading]) – Per-property reading entities.

  • aux_controls (list[pyisyox.classifier.AuxControl]) – Coalesced read/write controls (see the module docstring) — the unified successor to readings / parameterized_commands / buttons.

Parameters:
controllable: ControllablePlatform | None
controllable_command_ids: frozenset[str]
triggers: list[Command]
buttons: list[Command]
parameterized_commands: list[Command]
readings: list[Reading]
aux_controls: list[AuxControl]
classify(nodedef, find_editor=None)[source]

Classify a nodedef into HA platform contributions.

Parameters:
  • nodedef (NodeDef) – The nodedef to classify. Same shape regardless of native vs PG3 plugin origin.

  • find_editor (Callable[[str], Editor | None] | None) – Optional editor resolver, scoped to nodedef’s family/instance. When provided, property readings are split into sensor vs binary_sensor by editor UOM and tagged is_enum for enum editors. When None (e.g. in unit tests), all readings default to sensor with is_enum=False — callers can still render them, just without device-class hints.

Returns:

A ClassificationResult with controllable / triggers / buttons / parameterized_commands / readings / aux_controls populated. find_editor also drives aux_controls candidate platforms; without it a readable writable control falls back to its property’s read classification, and a write-only control falls back to candidate_platform=None for the consumer to resolve.

Return type:

ClassificationResult