pyisyox.schema package¶
Schema dataclasses for IoX 6 /rest/profiles JSON.
This package vendors the schema layer of UDI’s nucore-ai library: the type-equivalents for cmd, editor, linkdef, nodedef, and uom, plus a JSON-input loader for the unified profile blob.
The schema is intentionally separated from the wire layer so that
connection code in pyisyox.connection can be rewritten freely
while the data model remains stable. Editors carry a bidirectional codec
(see pyisyox.schema.editor) covering both controller→display
decoding and command-parameter encoding with subset/range validation.
- class Command(id, name='', parameters=<factory>, native=False, format=None)[source]¶
Bases:
objectA command a node sends or accepts.
- Variables:
id (str) – Command identifier (e.g.,
"DON","CLISPC","DISCOVER").name (str) – Human-readable label.
parameters (list[pyisyox.schema.cmd.CommandParameter]) – Positional parameters; empty for parameterless commands.
native (bool) – Whether the command is a native IoX command (
"true") or implemented at a higher layer.format (str | None) – Optional display format string used by the controller’s UI.
- Parameters:
- parameters: list[CommandParameter]¶
- classmethod from_json(raw)[source]¶
Build a
Commandfrom a JSON object as found in/rest/profilesnodedefcmds.sends[]/cmds.accepts[].Defensive against partial / null fields under PG3 dynamic profile reload — a parameter without an
editorkey is skipped rather than raisingKeyErroron the whole nodedef.
- class CommandParameter(editor_id, param_id='', init=None, optional=False)[source]¶
Bases:
objectA single positional parameter on a command.
- Variables:
editor_id (str) – Reference to the editor defining valid values for this parameter. Resolves against the parent profile’s editor table.
param_id (str) – Optional parameter identifier (often empty in IoX).
init (str | None) – Optional property whose current value seeds this parameter (e.g.,
"CLISPH"— the heat setpoint command’s parameter initialises from the currentCLISPHproperty).optional (bool) – Whether the parameter may be omitted on send.
- Parameters:
- class Editor(id, ranges=<factory>)[source]¶
Bases:
objectA profile editor — bidirectional codec for property and parameter values.
Encoding direction (
encode): user input (int or enum name) → raw int suitable to send to the controller, with subset/range validation.Decoding direction (
decode): raw int from the controller → display string (enum name if known, else formatted number with prec/uom).For multi-range editors the codec selects the range whose UOM matches a caller-supplied
uomhint, falling back to the first range. Most editors carry a single range.- Parameters:
id (str)
ranges (list[EditorRange])
- ranges: list[EditorRange]¶
- classmethod from_encoded_id(editor_id)[source]¶
Decode a self-describing encoded editor id into an
Editor.IoX lets a simple editor be referenced by an id that fully encodes its (single) range instead of pointing at a named
<editor>element — handy for the dynamically-generated Z-Wave nodedefs where most editors are spelled inline. The grammar (see https://developer.isy.io/docs/API/IoX/editors#encoded-editor-id):_<uom>_<prec>— implied bounds[-2147483647, 2147483647]optionally one of
_R_<min>_<max>(numeric range; a leadingmmakes a bound negative —_17_2_R_m5_10=> -5..10) or_S_<lowMask>[_<highMask>](subset as a 32/64-bit hex bitmask —_17_1_S_FF00FF00⇒{8..15, 24..31})optionally a trailing
_N_<nls>NLS-prefix segment
Returns
Noneifeditor_iddoesn’t parse as an encoding (so callers can fall back to a table lookup). The_N_<nls>segment is captured asEditorRange.nls_prefixbut not resolved here —Profile.find_editor()fillsnamesfrom it when the profile carries an NLS table. Range / subset validation works regardless.
- range_for(uom=None)[source]¶
Pick the range matching
uom, or the first range if no hint.- Parameters:
uom (str | None)
- Return type:
- decode(raw_value, uom=None)[source]¶
Decode a raw value to its display string.
Enum lookup first (when
namescovers the value), otherwise a precision-aware numeric string. Does not append the unit — callers format the unit separately based on the range’suom.When
uomisn’t given and the editor has multiple ranges, the enum-name lookup scans every range (so e.g. an editor whose first range is a 0-100 % scale and whose second is a tiny{1: "Previous Value"}index still decodes1to its name).UOM-101 / “degrees” with
prec=0halves the raw value (Insteon half-degree convention).
- encode(value, uom=None)[source]¶
Validate user input and return the value to put on the wire.
Two paths within a range:
Enum name (str matching ``names``) — returns the matching raw int verbatim.
min/maxdon’t apply.Numeric (int/float, or string parsed as float) — the displayed value. Validated against
[min, max]and thesubsetmask (both stored in displayed form), then returned as-is (int when integral, else float). The controller does device-side scaling from the appended UOM — the codec does not rewrite the number (no*10**precrescale, no half-degree doubling).
When
uomis given, only that range is tried. Otherwise every range is tried in order and the first that acceptsvaluewins — multi-range editors likeZW_DIM_PERCENT(range 0 is a tiny{1: "Previous Value"}index, range 1 is the 0-100 % scale) need this so a plain75lands in the percent range instead of being rejected by the index range.Raises
EditorCodecErrorif no range acceptsvalue.
- encode_param(value, uom=None)[source]¶
Like
encode(), but also returns the UOM of the range used.Command-send code appends each parameter as
/{value}/{uom}and the controller scales device-side from that UOM (proven:/cmd/DON/100/100→ 39 %,/cmd/DON/100/51→ 100 %), so the UOM has to be the one belonging to the range that actually accepted the value, not alwaysranges[0]— for a multi-range editor likeZW_DIM_PERCENTa plain75is encoded by the 0-100 % range (uom51), so/cmd/DON/75/51goes on the wire, not/cmd/DON/75/25.
- exception EditorCodecError[source]¶
Bases:
ValueErrorRaised when an editor codec cannot encode or decode a value.
- class EditorRange(uom, min=None, max=None, precision=0, subset=<factory>, names=<factory>, step=None, nls_prefix=None)[source]¶
Bases:
objectOne range entry within an editor.
An editor may carry multiple ranges (e.g., a temperature editor with Fahrenheit and Celsius variants), each tied to a distinct UOM.
- Variables:
uom (str) – Unit-of-measure identifier (string, indexes into the IoX UOM table).
min (float | None) – Lower numeric bound for raw values (inclusive).
Nonewhen the range is purely enumerative (subset only).max (float | None) – Upper numeric bound (inclusive).
precision (int) – Decimal precision applied to raw values (e.g., raw
6839withprecision=4displays as0.6839). The wire keys it as"prec"; Python attribute spells it out.subset (set[int]) – Resolved set of valid raw integers, narrower than
[min, max]. Empty when the full[min, max]range is valid.names (dict[int, str]) – Mapping of raw integer → display name for enumerated values (e.g.,
{0: "Off", 1: "Heat", 2: "Cool"}).step (float | None) – Increment hint for numeric (slider-shaped) ranges, in displayed units — e.g.
0.5on a half-degree setpoint editor.Nonewhen the editor doesn’t specify one (callers then derive a step fromprecision). Not used by the codec; it’s a UI hint, surfaced for consumers that build number entities.nls_prefix (str | None) – The NLS string-table prefix this range’s enum option names live under (the
_N_<nls>segment of an encoded editor id, or a named editor’s index nls).namesstays empty until something resolves it against an NLS table (the controller does it inline for/rest/profilesfamilies;Profile.find_editor()does it for encoded editors when the profile has an NLS table loaded).
- Parameters:
- classmethod from_json(raw)[source]¶
Build a range from a JSON object.
- Parameters:
raw (dict)
- Return type:
- is_valid(raw_value)[source]¶
True if
raw_valueis acceptable for outbound commands.Used for subset validation only (prec=0, enum-shaped editors). Numeric editors with
prec>0validate the displayed value and send it as-is (no scaling — the controller scales device-side from the UOM; seeEditor.encode()).min/maxin the IoX schema are stored in displayed form (e.g.min=5.0on a UOM-4 °C setpoint editor withprec=1means 5.0 °C, not raw 5).
- class Family(id, name, instances=<factory>)[source]¶
Bases:
objectA family of instances. Family id is a string so plugin slots (
"10","11", …) and the special"common"family can coexist.
- class Instance(id, name, editors=<factory>, linkdefs=<factory>, nodedefs=<factory>)[source]¶
Bases:
objectOne instance within a family — a self-contained set of editors, linkdefs, and nodedefs.
For built-in families there is typically one instance with id
"1". For PG3 plugin families (family"10"in the captured fixture), the instance id matches the plugin slot number and is encoded in node addresses as then0XX_prefix.- Parameters:
- class LinkDef(id, parameters=<factory>)[source]¶
Bases:
objectA link definition, identified by id and carrying parameter slots.
- Variables:
id (str) – Link definition identifier (e.g.,
"DON_OL","DON_LinkPair").parameters (list[pyisyox.schema.linkdef.LinkParameter]) – Parameter slots in declaration order.
- Parameters:
id (str)
parameters (list[LinkParameter])
- parameters: list[LinkParameter]¶
- class LinkParameter(editor_id, param_id='', init=None)[source]¶
Bases:
objectA single parameter on a link definition.
- Variables:
- Parameters:
- class NLSTable(entries=<factory>)[source]¶
Bases:
objectA parsed NLS string table — a flat
key -> valuemap plus the handful of IoX key-shape lookups callers actually need.- classmethod parse(text)[source]¶
Parse
KEY = VALUEtext. Blank lines and#comments are skipped; the value keeps everything after the first=(so format strings containing=survive).
- overlay(other)[source]¶
Return a new table with
other’s entries layered on top of this one’s (otherwins on key collisions).
- command_name(command_id, base=None)[source]¶
Label for a command id, preferring the nodedef-scoped override.
- property_name(property_id, base=None)[source]¶
Label for a property id, preferring the nodedef-scoped override.
- class NodeCommands(sends=<factory>, accepts=<factory>)[source]¶
Bases:
objectCommands a nodedef sends and accepts.
- Variables:
sends (list[pyisyox.schema.cmd.Command]) – Commands the node emits — useful as trigger sources (e.g.
OnOffControlsendsDON/DOFon physical press).accepts (list[pyisyox.schema.cmd.Command]) – Commands the node receives — drive the node’s controllable HA platform (light/switch/climate/lock/cover/button).
- Parameters:
- class NodeDef(id, family_id, instance_id, name='', properties=<factory>, cmds=<factory>, nls_key=None, links=<factory>)[source]¶
Bases:
objectThe static definition of a node class.
- Variables:
id (str) – Nodedef identifier (e.g.
"KeypadDimmer_ADV","Thermostat","flume2","controller").family_id (str) – Family id this nodedef belongs to (
"1"for Insteon,"4"for Z-Wave, plugin slot id for PG3 nodedefs).instance_id (str) – Instance id within the family (typically equal to
family_idfor built-in families and equal to the plugin slot for PG3 instances).name (str) – Default display name (the
NDN-<nls>-NAMENLS entry). Often empty — the live node carries a user-assigned name; this is just the discovery-time default./rest/profilesfamilies resolve it inline; for dynamic Z-Wave nodedefs pyisyox fills it from the family NLS table.properties (dict[str, pyisyox.schema.nodedef.NodeProperty]) – Property slots, keyed by property id.
cmds (pyisyox.schema.nodedef.NodeCommands) – Sent and accepted commands.
nls_key (str | None) – Reference key into the NLS string table (e.g.
"flume2"); pyisyox does not need to resolve this — every visible string is already inline-resolved in property/commandnamefields and in WS event frames.links (pyisyox.schema.nodedef.NodeLinks) – Control and response link references.
- Parameters:
id (str)
family_id (str)
instance_id (str)
name (str)
properties (dict[str, NodeProperty])
cmds (NodeCommands)
nls_key (str | None)
links (NodeLinks)
- properties: dict[str, NodeProperty]¶
- cmds: NodeCommands¶
- class NodeLinks(ctl=<factory>, rsp=<factory>)[source]¶
Bases:
objectControl and response link references on a nodedef.
- class NodeProperty(id, editor_id, name='', hide=False)[source]¶
Bases:
objectA property slot defined on a nodedef.
- Variables:
id (str) – Property id (e.g.
"ST","OL","CLISPC","GV1").editor_id (str) – Reference to the editor governing this property’s display and (where applicable) write-side validation.
name (str) – Human-readable label, inline-resolved by the controller (e.g.
"Current"for Flume’sGV1,"On Level"for Insteon’sOL). Authoritative source.hide (bool) – Hint that the property should not be surfaced in default UIs.
- Parameters:
- class Profile(timestamp='', families=<factory>, nodedef_lookup=<factory>, nls=<factory>)[source]¶
Bases:
objectThe decoded result of one
/rest/profilesJSON blob.The
nodedef_lookupis the load-bearing artifact callers use to resolve a node (which carriesfamily_id,instance_id, andnodeDefId) to itsNodeDef.- Parameters:
- nls: NLSTable¶
Merged NLS string table for any dynamically-loaded families (Z-Wave). Empty unless
pyisyox.clientfetched per-family NLS during load.
- merge(other)[source]¶
Merge
otherintoselfin place; return a diff summary.Designed for PG3 dynamic profile reload. Existing runtime
Nodeinstances hold references to the resolvedNodeDef, so a wholesale replace would leave them clinging to stale lookups — instead the merge mutates the existing dicts and replaces individual NodeDef/Editor/LinkDef objects. Additive only: items absent fromotherare kept.- Parameters:
other (Profile)
- Return type:
- register_nodedefs(family_id, instance_id, nodedefs)[source]¶
Add a batch of nodedefs to
family_id/instance_idin place.Used for dynamic Z-Wave nodedefs (fetched from
def/getXML — the Z-Wave families already exist in/rest/profileswith theirZW_*editors but no nodedefs). Overwrites by id.
- find_nodedef(nodedef_id, family_id, instance_id)[source]¶
Resolve a nodedef by its
(id, family, instance)join key.Returns
Nonewhen no matching nodedef exists — callers should treat that as the unknown-type case (e.g. fall back to the nodedef-driven HA platform classifier rather than the type-based one).
- find_editor(editor_id, family_id, instance_id)[source]¶
Resolve an editor by id within a family/instance scope.
Editors are scoped to their instance — the same id (e.g.
"bool") may appear in multiple instances with different ranges, so the family/instance must be supplied.An encoded editor id — one that fully describes its range, e.g.
"_51_0_R_0_101_N_IX_DIM_REP"— is decoded directly viaEditor.from_encoded_id(); this is how the dynamically- generated Z-Wave nodedefs spell most of their editors. (The check is “does it parse as an encoding”, not just “starts with_” — UDI also ships named editors that begin with_such as_sys_notify_full, which fall through to the table lookup.)Table-lookup fallback chain on miss:
family_id/instance_id(the requested scope)The
"common"family / instance"1"— UDI publishes a shared set of editors there (_sys_notify_full, etc.) that any plugin nodedef can reference
Returns
Noneif it’s neither a valid encoding nor present in either scope.
- to_dict()[source]¶
Flatten the profile to a JSON-compatible dict.
nodedef_lookupis dropped — its(nodedef_id, family_id, instance_id)tuple keys are not JSON-encodable and the same data lives underfamilies[fam].instances[inst].nodedefs.nodedef_lookup_countis surfaced as a sanity-check counter.pyisyox.schema.editor.EditorRangecarriessubset: set[int]which JSON can’t encode either; the walker below normalises every set into a sorted list so the snapshot round-trips throughjson.dumps.
- class Property(id, value, formatted='', uom='', prec=None, name='')[source]¶
Bases:
objectA live property value reported by the controller for a node.
- Variables:
id (str) – Property id (e.g.
"ST","GV1").value (str) – Raw value as reported by the controller (string form keeps controller-emitted precision).
formatted (str) – Human-readable value (e.g.
"0.6839 US gallons").uom (str) – Unit-of-measure id reported alongside the value.
prec (int | None) – Decimal precision applied to
value(None when not provided).name (str) – Optional display name override (often empty — the nodedef-level
NodeProperty.nameis the authoritative label).
- Parameters:
- class UOMEntry(id, name, description='', category_id=None)[source]¶
Bases:
objectA unit-of-measure entry.
- Variables:
- Parameters:
Submodules¶
- pyisyox.schema.cmd module
- pyisyox.schema.editor module
- pyisyox.schema.linkdef module
- pyisyox.schema.nodedef module
- pyisyox.schema.profile module
- pyisyox.schema.uom module