Source code for pyisyox.schema.cmd

"""Command and command-parameter dataclasses for IoX nodedefs.

A ``Command`` is an action a node can either *send* (emit as an event) or
*accept* (receive as an instruction). ``CommandParameter`` describes one
positional argument the command takes; the parameter's ``editor`` reference
resolves to an :class:`~pyisyox.schema.editor.Editor` and provides
write-side validation.

Source schema: ``/rest/profiles?include=nodedefs`` ``cmds.{sends,accepts}[]``.
"""

from __future__ import annotations

from dataclasses import dataclass, field


[docs] @dataclass(slots=True) class CommandParameter: """A single positional parameter on a command. Attributes: editor_id: Reference to the editor defining valid values for this parameter. Resolves against the parent profile's editor table. param_id: Optional parameter identifier (often empty in IoX). init: Optional property whose current value seeds this parameter (e.g., ``"CLISPH"`` — the heat setpoint command's parameter initialises from the current ``CLISPH`` property). optional: Whether the parameter may be omitted on send. """ editor_id: str param_id: str = "" init: str | None = None optional: bool = False
[docs] @dataclass(slots=True) class Command: """A command a node sends or accepts. Attributes: id: Command identifier (e.g., ``"DON"``, ``"CLISPC"``, ``"DISCOVER"``). name: Human-readable label. parameters: Positional parameters; empty for parameterless commands. native: Whether the command is a native IoX command (``"true"``) or implemented at a higher layer. format: Optional display format string used by the controller's UI. """ id: str name: str = "" parameters: list[CommandParameter] = field(default_factory=list) native: bool = False format: str | None = None
[docs] @classmethod def from_json(cls, raw: dict) -> Command: """Build a :class:`Command` from a JSON object as found in ``/rest/profiles`` nodedef ``cmds.sends[]`` / ``cmds.accepts[]``. Defensive against partial / null fields under PG3 dynamic profile reload — a parameter without an ``editor`` key is skipped rather than raising ``KeyError`` on the whole nodedef. """ params: list[CommandParameter] = [] for p in raw.get("parameters") or []: if not isinstance(p, dict): continue editor_id = p.get("editor") if not editor_id: continue params.append( CommandParameter( editor_id=editor_id, param_id=p.get("id", ""), init=p.get("init"), optional=bool(p.get("optional", False)), ) ) return cls( id=raw["id"], name=raw.get("name", ""), parameters=params, native=str(raw.get("native", "false")).lower() == "true", format=raw.get("format"), )