pyisyox.schema.profile module¶
Profile loader for /rest/profiles JSON.
Parses families → instances → editors/linkdefs/nodedefs and builds the
(nodedef_id, family_id, instance_id) → NodeDef lookup. Knows the
wire shape but does no HTTP — callers pass dicts to
Profile.load_from_json().
The controller inline-resolves visible strings into the name fields,
so the NLS string table isn’t needed for /rest/profiles families.
Dynamically generated Z-Wave nodedefs (from def/get XML) are the
exception: they arrive label-less, so pyisyox.client overlays
the per-family NLS tables onto Profile.nls after loading them.
- 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 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 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 ProfileMergeResult(nodedefs_added=<factory>, nodedefs_replaced=<factory>, editors_added=<factory>, editors_replaced=<factory>, linkdefs_added=<factory>, linkdefs_replaced=<factory>)[source]¶
Bases:
objectDiff produced by
Profile.merge().- Variables:
nodedefs_added (list[tuple[str, str, str]]) –
(nodedef_id, family_id, instance_id)triples for nodedefs that didn’t exist in the destination profile before the merge.nodedefs_replaced (list[tuple[str, str, str]]) – Same shape, for nodedefs whose existing entry was overwritten with a fresh
NodeDef. The old object is no longer inProfile.nodedef_lookup; consumers caching it should refresh.editors_replaced (editors_added /) –
(editor_id, family_id, instance_id)triples for editors.linkdefs_replaced (linkdefs_added /) – same for linkdefs.
- Parameters: