pyisyox package¶
PyISYoX — async Python client for Universal Devices’ eisy / Polisy controllers running IoX 6.0.0+.
The public surface is intentionally small. Most consumers want:
pyisyox.Controller— the top-level handle (connect, query nodes, subscribe to events).pyisyox.PortalAuth/pyisyox.LocalAuth— auth strategies. Portal mode (JWT) is the recommended default; Local mode (HTTP basic) exists as a feature-degraded fallback.pyisyox.Node— runtime device handle with editor-validatedNode.send_command().
Example:
from pyisyox import Controller, PortalAuth
async def main():
controller = Controller("https://eisy.local:443", PortalAuth("me@x.com", "pw"))
await controller.connect()
try:
await controller.nodes["3D 7D 87 1"].send_command("DON", 75)
finally:
await controller.stop()
The library targets eisy/Polisy on IoX 6+. Original ISY-994 hardware is
out of scope; consumers needing that should use the upstream pyisy
(v3.x) library.
- class Auth(*args, **kwargs)[source]¶
Bases:
ProtocolAuth strategy protocol shared by
LocalAuthandPortalAuth.Not
@runtime_checkable—isinstance(x, Auth)against an unrelated class that happens to share these attribute names would pass without verifying coroutine signatures, which masks bugs. Tests construct the concrete classes directly.The HTTP client calls
authenticate()once during connect, thenrequest_kwargs()before every request to obtain the kwargs to splat intosession.get(...)/session.post(...). On a 401 response, the client callshandle_unauthorized(); if it returns True, the client retries the original request once.- async authenticate(session, base_url)[source]¶
Perform any one-time authentication setup (e.g., login POST).
- Parameters:
session (aiohttp.ClientSession)
base_url (str)
- Return type:
None
- async request_kwargs(session, base_url)[source]¶
Return kwargs for
session.request()(auth, headers, etc.).- Parameters:
session (aiohttp.ClientSession)
base_url (str)
- Return type:
- async handle_unauthorized(session, base_url)[source]¶
Handle a 401 response. Return True if re-auth succeeded and the original request should be retried; False if the auth state cannot recover (caller should propagate the 401 as a permanent error).
- Parameters:
session (aiohttp.ClientSession)
base_url (str)
- Return type:
- async close(session, base_url)[source]¶
Release any auth-held resources (e.g., explicit logout).
sessionandbase_urlare passed so implementations can make a final logout call to invalidate server-side state (PortalAuth posts/api/logout); LocalAuth ignores them since basic-auth has no server-side session.- Parameters:
session (aiohttp.ClientSession)
base_url (str)
- Return type:
None
- exception AuthError[source]¶
Bases:
ExceptionAuthentication failure (login rejected, refresh failed, etc.).
- class AuxControl(id, readable, writable, candidate_platform=None, property=None, command=None, editor_id=None, is_enum=False)[source]¶
Bases:
objectOne 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 paraminit="CLISPH"; an Insteon i3 flags sub-node’sGV0paraminit="ST"⇄ theST“Mode” status). This is the authoritative pairing key — not naive id matching: the cmd id and the status id can differ (i3GV0⇄ST).Controllable-owned ids and
QUERYare already excluded (the controllable platform represents those).STis 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.
Noneonly 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 staysNonefor 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
namesmap.
- Parameters:
id (str)
readable (bool)
writable (bool)
candidate_platform (AuxPlatform | None)
property (NodeProperty | None)
command (Command | None)
editor_id (str | None)
is_enum (bool)
- candidate_platform: AuxPlatform | None¶
- property: NodeProperty | None¶
- class AuxPlatform(*values)[source]¶
Bases:
StrEnumCandidate 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 ClassificationResult(controllable=None, controllable_command_ids=<factory>, triggers=<factory>, buttons=<factory>, parameterized_commands=<factory>, readings=<factory>, aux_controls=<factory>)[source]¶
Bases:
objectThe set of HA platform contributions for one nodedef.
- Variables:
controllable (pyisyox.classifier.ControllablePlatform | None) – The controllable platform, or
Nonefor 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
controllableisNone.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-forgetbuttonentity each. ExcludesQUERYand 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¶
- aux_controls: list[AuxControl]¶
- exception ClientError[source]¶
Bases:
ExceptionBase error for client-level failures (HTTP non-2xx, parse errors).
- class ControllablePlatform(*values)[source]¶
Bases:
StrEnumThe single controllable HA platform a nodedef may map to.
- LIGHT¶
- SWITCH¶
- CLIMATE¶
- LOCK¶
- COVER¶
- ALARM_CONTROL_PANEL¶
- class Controller(base_url, auth, session=None, ws_path='/rest/subscribe', tls_version=None, verify_ssl=False)[source]¶
Bases:
objectTop-level handle for one IoX 6+ controller (eisy / Polisy).
Construction is cheap and synchronous; the network round-trips happen in
connect(). Disconnect is symmetric:stop()closes the WebSocket and (if the controller owns the aiohttp session) closes that too.Threading: this class is async-only. The WS reader runs as a background
asyncio.Task; do not block the event loop in event or status callbacks — schedule heavier work on a separate task.- Parameters:
- async connect(*, start_websocket=True)[source]¶
Authenticate, run the initial load, and (optionally) open the WS.
Builds the IoXClient, calls
IoXClient.connect()to fetch/api/config,/rest/profiles,/api/nodes,/rest/status, programs/triggers/variables in parallel, and merges the status overlay. Then constructs theEventDispatcherover the same node registry the runtimeNodeinstances will read from, so WebSocket frames mutate properties in place.- Parameters:
start_websocket (bool) – When
True(default), the WS reader starts in the background after the initial load completes. PassFalsefor one-shot reads (CLI tools, tests) where the consumer doesn’t need live updates.- Return type:
None
:raises Any error from
IoXClient.connect()(auth failure,: :raises HTTP failure, malformed payload) propagates unchanged.:
- async stop()[source]¶
Stop the WebSocket, log out, and (if we own it) close the session.
Idempotent — safe to call from cleanup paths even if
connect()partially failed.- Return type:
None
- property websocket: WebSocketEventStream | None¶
The active WebSocket stream, or
None.Returns the live
WebSocketEventStreamwhenconnect()was called withstart_websocket=Trueandstop()hasn’t run yet.Nonefor one-shot reads (CLI tools, snapshot tests) that opted out of the WS upgrade. Consumers polling stream health (HA system_health, diagnostics) readwebsocket.status/websocket.last_event_atdirectly.
- property config: ControllerConfig¶
Decoded
/api/configslice (uuid, version, portalHost).
- property name: str¶
User-assigned controller name (e.g.
"Main eisy").Sourced from the
<name>of the root group in/rest/nodes(the same value the eisy admin UI shows and that the legacy/rest/config<configuration><root><name>path carried). Empty string when the controller hasn’t been named or the legacy endpoint isn’t available.Consumers driving HA device names should prefer this over the hostname so users see the friendly label they set on the controller, with the hostname as a fallback.
- property nodes: dict[str, Node]¶
Map of node address → runtime
Node.Built lazily on first access from the loaded
NodeRecordregistry; subsequent accesses return the cached dict so identity is stable across calls (consumers can hold references to specific nodes safely).
- property groups: dict[str, Group]¶
Map of group address → runtime
Group(IoX scenes).Sourced from
/rest/nodesXML at connect time. The controller-self group (flag="12") is filtered out.
- property programs: dict[str, Program]¶
Map of program id → runtime
Program.Folders share the same id space but live under
program_folders; this map only contains executable programs (is_folder=False).
- property program_folders: dict[str, ProgramFolder]¶
Map of folder id → runtime
ProgramFolder.The synthetic root folder (
"My Programs"on stock eisy firmware) is included — consumers walking the tree from the controller can use it as the root anchor.
- property variables: dict[str, dict[str, Variable]]¶
Map of variable type → id → typed
Variablewrapper.Outer key is
"1"(integer) or"2"(state); inner key is the variable id within that type. EachVariableshares its underlyingVariableRecordwith the controller’s loaded state — writes via the wrapper’s mutation coroutines update the record in place so subsequent reads reflect the new value without waiting for a WS frame.Returns an empty inner dict for a type the controller has no variables in.
- property network_resources: dict[str, NetworkResource]¶
Map of resource id → runtime
NetworkResource.Empty when the controller has no networking module enabled — the optional endpoint either 404s or returns an empty
<NetConfig/>, both flattened to{}here.
- to_dict()[source]¶
Flatten the full controller state to a JSON-compatible dict.
Aggregates every loaded collection (nodes / groups / folders / programs / program_folders / variables / network_resources) plus the controller’s own config + WebSocket health. Each nested object’s structural fields come from its own
to_dict()so the same code path drives thepyisyox -m … --dumpCLI flag and consumer diagnostics. RaisesControllerNotConnectedErrorwhen called beforeconnect()(no loaded state to snapshot).
- async refresh_profile()[source]¶
Re-fetch
/rest/profilesand merge updates into the live profile.Designed for PG3 dynamic profile reload — when a plugin updates its nodedefs at runtime, consumers detect the controller-side signal (the WS event control code is plugin- + version-specific; capture it from a real reload to wire up an automatic listener) and call this method to absorb the change.
The live
Profileis mutated in place: existingpyisyox.runtime.Nodeinstances that resolved against a NodeDef before the reload now see the new NodeDef on their next attribute access. The returnedProfileMergeResultlists the lookup-key triples that were added vs replaced so consumers can re-classify or invalidate any caches keyed on nodedef.- Returns:
A
ProfileMergeResultsummarising the diff. Empty (result.changed is False) when the controller’s response was identical to what we had.- Raises:
ControllerNotConnectedError – When called before
connect().ClientError / HTTPError / AuthError – As with any HTTP round-trip.
- Return type:
- add_event_listener(callback)[source]¶
Subscribe to every parsed WebSocket event.
The dispatcher applies the property update before calling listeners, so callbacks observing a property event can read the new value via
controller.nodes[address].properties[id]synchronously.- Returns:
An unsubscribe function. Calling it removes
callback.- Raises:
ControllerNotConnectedError – When called before
connect()or afterstop().- Parameters:
callback (EventListener)
- Return type:
Callable[[], None]
- add_status_listener(callback)[source]¶
Subscribe to WebSocket lifecycle status changes.
- Returns:
An unsubscribe function.
- Raises:
ControllerNotConnectedError – When called before
connect()(or afterstop()), or whenconnect()was called withstart_websocket=False.- Parameters:
callback (StatusListener)
- Return type:
Callable[[], None]
- add_node_lifecycle_listener(callback)[source]¶
Subscribe to node-tree lifecycle changes (add / remove / rename).
The eisy emits
<control>_3</control>frames when nodes appear or disappear (typically driven by PG3 plugin reloads). The dispatcher does not auto-update the live registry — consumers decide whether to callrefresh()or live with a stale view until the user manually reloads the integration.HA Core’s intended UX is to register a Repair issue on the first lifecycle event with
ev.requires_reload is Trueand clear it once the user-initiated reload completes.- Returns:
An unsubscribe function.
- Raises:
ControllerNotConnectedError – When called before
connect().- Parameters:
callback (NodeLifecycleListener)
- Return type:
Callable[[], None]
- add_program_status_listener(callback)[source]¶
Subscribe to program-status changes (the
<control>_1</control>action"0"frames).The dispatcher mutates the matching
ProgramRecord.status/runningin place before firing, so consumers readingcontroller.programs[id].statusfrom the callback see the new value.- Returns:
An unsubscribe function.
- Raises:
ControllerNotConnectedError – When called before
connect().- Parameters:
callback (ProgramStatusListener)
- Return type:
Callable[[], None]
- add_variable_table_change_listener(callback)[source]¶
Subscribe to
_1/9VARIABLE_TABLE_CHANGEDframes.These fire on variable create / delete / rename / precision change — not on per-value writes (those use
_1/6and_1/7). TheControlleralready wires its own listener that auto-refreshesself.variables[type_id]; consumers add their own listener on top to drive UI invalidation, telemetry, etc.- Returns:
An unsubscribe function.
- Raises:
ControllerNotConnectedError – When called before
connect().- Parameters:
callback (VariableTableChangeListener)
- Return type:
Callable[[], None]
- async refresh()[source]¶
Re-run the parallel load fan-out and merge results into the live
LoadResult.Use after a
NodeLifecycleEventwithrequires_reload=Trueto absorb the new node tree without re-authenticating. The liveProfileis mutated in place (seeProfile.merge()); thenodes/groups/folders/programs/triggers/variablesregistries on the LoadResult are updated to match the fresh snapshot. The dispatcher’s binding toLoadResult.nodessurvives because we mutate the dict in place.- Returns:
The
ProfileMergeResultfrom the schema merge — useful for tracking which nodedefs changed.- Raises:
ControllerNotConnectedError – When called before
connect().- Return type:
- async send_program_command(program_id, command)[source]¶
Send a program / folder command via the legacy REST endpoint.
Wire shape:
GET /rest/programs/{id}/{command}. Seepyisyox.runtime.ProgramCommandfor the typed command set; bare strings are accepted too (the StrEnum members are themselves strings, soProgramCommand.RUN_THEN == "runThen"— pass either form).Lower-level than
Program.run()etc.; useful for consumers that hold ids without a Program wrapper (e.g. an HA service receiving raw ids).- Parameters:
program_id (str)
command (ProgramCommand | str)
- Return type:
None
- async run_network_resource(resource_id)[source]¶
Fire a network resource by id.
Wire shape:
GET /rest/networking/resources/{id}. Treat as fire-and-forget — the controller acknowledges receipt only, not the result of the underlying HTTP / TCP / UDP fire.
- async set_variable_value(var_type, var_id, value)[source]¶
Set the current value of a controller variable.
Wire shape:
POST /api/variables/{type}/{id}with body{"value": <int>}.
- async set_variable_init(var_type, var_id, init)[source]¶
Set the initial / restore-on-startup value of a variable.
Wire shape:
POST /api/variables/{type}/{id}with{"init": <int>}.
- async rename_variable(var_type, var_id, name)[source]¶
Rename a variable.
Wire shape:
POST /api/variables/{type}/{id}with{"name": "<str>"}.
- async create_variable(var_type, name, *, prec=0)[source]¶
Create a new variable on the controller.
Wire shape:
PUT /api/variables/{type}with body{"name": "<str>", "prec": <int>}. The controller assigns the id and returns the new record.Inserts a
VariableRecordinto the loaded registry in place (so the dispatcher’s binding survives) and returns aVariablewrapper bound to it. Per issue #125, the controller silently dropsinit/valuekeys on PUT — callVariable.set_value()/Variable.set_init()on the returned wrapper to populate them.- Raises:
ControllerNotConnectedError – When called before
connect().ClientError – When the response payload is missing the new id.
- Parameters:
- Return type:
- async refresh_variables(var_type)[source]¶
Re-fetch one variable type and mutate the registry in place.
Wire shape:
GET /api/variables/{type}. Mutatesself._loaded.variables[type]in place (clear + update) so the dispatcher’s binding to the same dict survives — a fullrefresh()would replace the dict and break per-record WS overlay routing.Used internally by the auto-wired
VARIABLE_TABLE_CHANGEDlistener; also callable directly when a consumer wants to force a re-sync.
- async rename_node(address, name)[source]¶
Rename a node.
Wire shape:
POST /api/nodes/{address}with{"name": "<str>", "nodeType": "node"}.The
nodeTypefield is required by the server. Userename_group()for scenes.
- async rename_group(address, name)[source]¶
Rename a group / scene.
Same endpoint as
rename_node()but withnodeType: "group"so the server applies the change through the scene registry.
- async rename_folder(address, name)[source]¶
Rename a folder (organisational container).
Same endpoint as
rename_node()/rename_group()but withnodeType: "folder". Folders are address-keyed like nodes/groups; their addresses are typically 5-digit integers (family"13").
- feed_event_frame(raw_frame)[source]¶
Inject a raw frame into the dispatcher.
Useful in tests and CLIs replaying captured WebSocket data. Production code paths drive the dispatcher through the
WebSocketEventStreamreader.
- class ControllerConfig(uuid, version, portal_host=None)[source]¶
Bases:
objectSubset of
/api/configthat the rest of the load flow needs.
- exception ControllerNotConnectedError[source]¶
Bases:
RuntimeErrorRaised when accessing live data before
Controller.connect()has populated it.
- class DeviceLinkerAction(*values)[source]¶
Bases:
StrEnumAction codes on
SystemEventControl.DEVICE_LINKER(_20) frames — Cookbook §8.5.22 (udievnts.xsd).- STATUS¶
Linking status update —
<eventInfo>carries device-linker info.
- CLEARED¶
The device-linking list was cleared. No payload.
- class DeviceWriteAction(*values)[source]¶
Bases:
StrEnumDevice-write sub-codes that ride through on
_7(SystemEventControl.PROGRESS) frames — PyISY 3.x surfaced these asNodeChangeAction.DEVICE_WRITING/DEVICE_MEMORY.Unlike the other action enums, these are control-value sub-codes (they have the
_prefix and arrive in the<control>slot), not<action>values — the dispatcher doesn’t route them; they pass through as plain control events.<eventInfo>child tags per code are inDEVICE_WRITE_PROGRESS_EVENT_INFO_TAGS.- PROGRESS¶
Device-writing progress message —
<eventInfo>carries<message>.
- MEMORY¶
Raw Insteon memory write —
<eventInfo>carries<memory>/<cmd1>/<cmd2>/<value>.hacs-udi-iox’s backlight entities subscribe to this to catch memory-write echoes.
- class Event(seqnum, timestamp, control, action, node_address, formatted_action='', formatted_name='', uom='', precision=None, event_info='')[source]¶
Bases:
objectOne parsed event frame.
- Variables:
seqnum (int) – Event sequence number from the eisy. Monotonic per connection; resets on reconnect.
timestamp (str) – ISO 8601 timestamp string from the frame (preserved verbatim — consumer parses if needed).
control (str) – Property id (
"ST","GV1", …) or system code ("_5","_28", …).action (str) – Raw value as reported (string form preserves the controller’s precision representation).
node_address (str) – Wire address of the affected node, or empty string for system events.
formatted_action (str) – Human-readable display value (e.g.
"0.6839 US gallons"). Empty when the controller didn’t supply one (system events typically don’t).formatted_name (str) – Display name of the property (e.g.
"Current"). Empty when not provided.uom (str) – Unit-of-measure id from
<action uom="...">.precision (int | None) – Decimal precision from
<action prec="...">, orNoneif absent. (Wire keys it as"prec"; Python attribute spells it out.)event_info (str) – Inner
<eventInfo>XML preserved verbatim. Empty string when the frame had no<eventInfo>element or when its content was empty. Consumers that need the structured payload (e.g. variable change frames carrying<var type="..." id="...">, or controller logs in CDATA) parse this themselves — the IoX wire schema differs across system control codes and pyisyox stays neutral.
- Parameters:
- class EventDispatcher(nodes, programs=None, variables=None, groups=None)[source]¶
Bases:
objectRoutes parsed
Eventinstances into a node registry + listener callbacks.The dispatcher is intentionally not coupled to the WebSocket transport —
feed()accepts a raw frame and does the parse + route + emit dance. The actual WS read loop lives inpyisyox.runtime.ws; tests can drive the dispatcher directly with synthetic frames.- Parameters:
nodes (dict[str, NodeRecord])
programs (dict[str, ProgramRecord] | None)
variables (dict[str, dict[str, VariableRecord]] | None)
groups (dict[str, GroupRecord] | None)
- update_groups(groups)[source]¶
(Re)build the member→groups reverse index from a group registry.
Called from
__init__and again bypyisyox.controller.Controller.refresh()—refresh()replacesLoadResult.groupswith a fresh dict (unlikenodes, which is mutated in place), so the index has to be rebuilt or scene-membership changes from a reload lifecycle event would be missed (new members never re-emit; removed members still would).- Parameters:
groups (dict[str, GroupRecord])
- Return type:
None
- add_program_status_listener(callback)[source]¶
Register
callbackto fire on every program-status frame (<control>_1</control>action"0").The dispatcher updates the matching
pyisyox.client.ProgramRecordin place before firing, so consumers readingprogram.statusfrom the callback see the new value.- Returns:
An unsubscribe function.
- Parameters:
callback (Callable[[ProgramStatusEvent], None])
- Return type:
Callable[[], None]
- add_variable_table_change_listener(callback)[source]¶
Register
callbackto fire on every variable-table-change frame (<control>_1</control>action"9").Fired when a variable is added, removed, or has its precision changed on the controller. The dispatcher itself does not re-fetch the variable table — the listener is the seam where consumers wire in a focused re-fetch (e.g. by calling
Controller.refresh()) so the registry mirrors the new metadata. SeeVariableTableChangeEventfor the payload.- Returns:
An unsubscribe function.
- Parameters:
callback (Callable[[VariableTableChangeEvent], None])
- Return type:
Callable[[], None]
- add_lifecycle_listener(callback)[source]¶
Register
callbackto fire on every parsedNodeLifecycleEvent(<control>_3</control>frames).Use this to drive reload UX: HA Core typically registers a Repair issue when it sees a lifecycle event with
requires_reload=True, prompting the user to reload the integration when convenient. The dispatcher does not update the node registry on lifecycle events — consumers decide whether to callpyisyox.controller.Controller.refresh()or live with a stale view until manual reload.- Returns:
An unsubscribe function.
- Parameters:
callback (Callable[[NodeLifecycleEvent], None])
- Return type:
Callable[[], None]
- feed(raw_frame)[source]¶
Parse one frame, apply the property update, fan out to listeners.
Returns the parsed
Eventfor callers that want to peek (e.g. for sequence-number tracking), orNonewhen the frame couldn’t be parsed (malformed XML, non-event envelope, keep-alive null). Never raises on bad input — a single bad frame must not crash the read loop.
- class Folder(record)[source]¶
Bases:
objectUser-facing handle for one folder in the node tree.
- Parameters:
record (FolderRecord)
- class FolderRecord(address, name, family_id='13', parent_address=None)[source]¶
Bases:
objectOne folder (organisational, no command surface). Family
"13".
- class Group(record, profile, client, nodes=None)[source]¶
Bases:
objectUser-facing handle for one group / scene in the controller.
- Parameters:
record (GroupRecord)
profile (Profile)
client (IoXClient)
nodes (dict[str, NodeRecord] | None)
- classmethod from_record(record, profile, client, nodes=None)[source]¶
Construct a Group from a parsed record.
Pass
nodes(the controller’sloaded.nodesdict) to enable thegroup_all_onderived property. Without it the group is purely command-issuing.- Parameters:
record (GroupRecord)
profile (Profile)
client (IoXClient)
nodes (dict[str, NodeRecord] | None)
- Return type:
- property address: str¶
Group address — usually a 5-digit integer string or
"ADR####"for special groups like~zAuto DR.
- property nodedef_id: str¶
Scene-class label (
"InsteonDimmer"etc.). Not a real profile nodedef — see module docstring.
- property member_addresses: tuple[str, ...]¶
Addresses of the nodes that belong to this group.
Sourced from the
<members>element in/rest/nodesXML. Order matches the controller’s declaration order. Includes both controllers and responders; usecontroller_addressesfor the controller subset.
- property controller_addresses: tuple[str, ...]¶
Subset of
member_addressesthat the controller flags as scene controllers (<link type="16">).Empty when the group has no explicit controller (e.g. virtual scenes / SmartLinc-style automation groups).
- property has_state_target: bool¶
Whether the scene maintains any member on/off state.
Truewhen link targets resolved and at least one member has anon/offintent. A resolved scene with only fire-only / config links (cmdBL/BEEP/…, or empty) →False: it has no steady state, so a consumer should model it as a momentary button, not a switch. When targets are unresolved we can’t tell, so assumeTrue(the safe default — keep it a stateful scene).
- property has_dimmable_members: bool¶
True iff any member node is a dimmable load.
Nodedef-derived via
pyisyox.runtime.Node.is_dimmable, so it’s robust and — unlikehas_state_target— does not depend on/api/groupslink resolution (works on older firmware too). Consumers pair it withhas_state_targetto pick the scene’s HA platform: no state target → button; else dimmable members → on/off light (preserving light semantics + the group/more-info framework, noswitch_as_x); else → switch. Scenes have no settable brightness — fade/brt/dim are separate manual commands — so “light” here is on/off only.Returns
Falsewithout a node-registry reference. Members missing from the registry are skipped (defensive). Memoised on first access (oneNode+find_nodedefper member) — the result is static for this record, so repeated reads (e.g.to_dict) don’t rebuild it.
- property group_all_on: bool¶
True iff every on-target member currently reports an “on” state.
Computed on access from the controller’s node registry. Stateless members — motion sensors, RemoteLincs, binary-alarm devices, see
_STATELESS_NODEDEF_IDS— are excluded; theirSTisn’t a persistent state.When
/api/groupslink targets resolved, the aggregate is over the scene’s on-target members only (see_on_set()) — so a radio-style keypad scene (one button on-target, the rest driven off) tracks correctly instead of being structurally never-all-on. Otherwise it falls back to the legacy all-member behaviour. ReturnsFalsewhen the group has no node-registry reference, the (on-target / member) set is empty, a member is missing from the registry, or any counted member’sSTis missing or zero.Cheap:
O(N), computed on read — the underlyingSTvalues mutate in place via the WS dispatcher, so each access reflects the latest state.
- property group_any_on: bool¶
True iff at least one on-target member currently reports “on”.
Companion to
group_all_on; this is the aggregation HA scene-switch consumers want for theiris_on. When/api/groupslink targets resolved it considers only the scene’s on-target members (see_on_set()), so a scene reads on iff a member it actually drives on is on — not merely because some always-lit keypad button is non-zero. Otherwise it falls back to the legacy “any stateful member non-zero” behaviour (whatpyisy.Group.statusdid). Stateless members and members not in the registry are skipped.Returns
Falsewith no node-registry reference, an empty (on-target / member) set, or when every counted member’sSTis missing or zero. Cheap:O(N), computed on read.
- async send_command(command_id, *params)[source]¶
Send a command to every member of this group.
Wire shape:
GET /rest/nodes/{group_addr}/cmd/{command_id}[/{p1}...]. The controller broadcasts to each member; results aren’t returned per-member.Unlike
Node.send_command(), parameters are not validated through the editor codec — group nodedefs aren’t profile-resolvable. Pass already-encoded integers; consumers are responsible for sanity checks (e.g. clamp on-level to 0-100). Common usage:await group.send_command("DON")— turn the scene on to its programmed levelawait group.send_command("DON", 75)— explicit on-levelawait group.send_command("DOF")await group.send_command("DFON")/"DFOF"— fastawait group.send_command("BRT")/"DIM"— manual brighten/dim step
- async rename(name)[source]¶
Rename this group / scene.
Wire shape:
POST /api/nodes/{address}with{"name": "<str>", "nodeType": "group"}. ThenodeTypefield is required by the server even though the address already disambiguates — without it the call is rejected.- Parameters:
name (str)
- Return type:
None
- class GroupRecord(address, name, nodedef_id, family_id, instance_id='1', parent_address=None, pnode=None, member_addresses=(), controller_addresses=(), member_intents=<factory>, targets_resolved=False)[source]¶
Bases:
objectOne scene/group. Commands to
addressbroadcast to every member.Sourced from
<group flag="132">elements; the specialflag="12"controller-self group is filtered out at parse time.- Parameters:
- member_addresses: tuple[str, ...]¶
All member node addresses, in declaration order (controllers + responders).
- controller_addresses: tuple[str, ...]¶
Subset of
member_addresseswhose<link type="16">flag marks them as scene controllers (rather than responders). Empty when the group has no explicit controller (e.g. SmartLinc-style virtual scenes).
- member_intents: dict[str, str]¶
Per-member scene intent resolved from
/api/groupslink targets: address →"on"|"off"|"discard". Empty when/api/groupswas unavailable or the group wasn’t present there.
- targets_resolved: bool¶
Trueiff the group’s link targets were found and every link resolved to a known intent (no unknown linktype, nonativelink missing itsOLparam). WhenFalse, consumers fall back to the legacy all-memberSTaggregate. A resolved group with no"on"members (fire-only / config-only scene) keeps thisTruewith an empty or all-off/discardmember_intents.
- exception HTTPError(status, url)[source]¶
Bases:
ClientErrorNon-2xx response after auth retries are exhausted.
- exception ISYMaxConnections[source]¶
Bases:
ISYStreamDisconnectedThe isy has disconnected because it reached maximum connections.
- exception ISYResponseParseError[source]¶
Bases:
ExceptionError parsing a response provided by the ISY.
- exception ISYStreamDisconnected[source]¶
Bases:
ISYStreamDataErrorThe isy has disconnected.
- class InternetAccessStatus(*values)[source]¶
Bases:
StrEnumAction codes on
SystemEventControl.INTERNET_ACCESS(_6) frames — Cookbook §8.5.8.- DISABLED¶
- ENABLED¶
Enabled —
<eventInfo>is the external URL.
- FAILED¶
- class IoXClient(base_url, auth, session)[source]¶
Bases:
objectAuth-aware async HTTP client for IoX 6+ controllers.
- Parameters:
base_url (str)
auth (Auth)
session (aiohttp.ClientSession)
- async connect()[source]¶
Authenticate (if needed) and run the parallel initial load.
- Order:
GET /api/config— synchronous, must succeed before the rest of the calls fire.Authenticate via the auth strategy (no-op for LocalAuth).
Parallel: profiles, nodes, status, programs, triggers, variables/1, variables/2.
Merge
/rest/statusproperties into the node records.
- Returns:
A populated
LoadResult.- Return type:
- async load(config=None)[source]¶
Run the parallel load fan-out and return a fresh
LoadResult.Used both by
connect()(which prepends config + auth) and bypyisyox.controller.Controller.refresh()(which re-runs the fan-out without re-authenticating).- Parameters:
config (ControllerConfig | None) – Pre-fetched
ControllerConfigto attach to the returned LoadResult. WhenNone, the existing config is re-fetched (cheap — small JSON, no auth).- Returns:
A populated
LoadResult.- Return type:
- async send_node_command(address, command_id, *params)[source]¶
Issue
GET /rest/nodes/{addr}/cmd/{cmd}[/{p1}[/{p2}...]].Params are stringified and joined as-is — the editor codec runs in
Node.send_command().addressis URL-quoted.
- async get_zwave_parameter(address, number, *, zmatter=False)[source]¶
Issue
GET /rest/(zmatter/)?zwave/node/{addr}/config/query/{n}.Body on success:
<config paramNum="N" size="SZ" value="V"/>. Controller failure surfaces as a<RestResponse succeeded="false">envelope (caller must inspect — HTTPError covers transport only).zmatter=Trueswitches to the family-12 path prefix.
- async set_zwave_parameter(address, number, value, size, *, zmatter=False)[source]¶
Issue
GET /rest/(zmatter/)?zwave/node/{addr}/config/set/{n}/{v}/{sz}.size(1/2/4 bytes) is carried explicitly; the Insteon-styleCONFIGcommand editor doesn’t model byte size, so this path takes precedence oversend_command("CONFIG", ...)for Z-Wave.
- async set_zwave_lock_code(address, user_num, code, *, zmatter=False)[source]¶
Issue
GET /rest/(zmatter/)?zwave/node/{addr}/security/user/{n}/set/code/{c}.Programs one user-code slot. Returns a
<RestResponse>envelope — callers should pass it throughNode.set_zwave_lock_code()’s parser, which raises onsucceeded="false".
- async delete_zwave_lock_code(address, user_num, *, zmatter=False)[source]¶
Issue
GET /rest/(zmatter/)?zwave/node/{addr}/security/user/{n}/delete.Clears one user-code slot.
- async set_node_enabled(address, enabled)[source]¶
Issue
GET /rest/nodes/{addr}/{enable|disable}.A disabled node stays in the table; the controller stops polling and commanding it.
- async post_variable_update(var_type, var_id, body)[source]¶
Issue
POST /api/variables/{type}/{id}with the supplied body.Four documented body shapes (one key per call; eisy-ui doesn’t mix them):
{"value": <int>}— set the current value{"init": <int>}— set the initial/restore value{"name": "<str>"}— rename{"prec": <int>}— set decimal precision (fires_1/9VARIABLE_TABLE_CHANGEDinstead of the per-value6/7frames; without an auto-refresh listener wired to that event, downstream consumers won’t notice the precision change until the nextrefresh()).
- async create_variable(var_type, name, *, prec=0)[source]¶
Create a new variable on the controller.
Wire shape:
PUT /api/variables/{type}with body{"name": "<str>", "prec": <int>}. The controller assigns theidand echoes the new record back asdata.Note: the eisy controller accepts
init/valuekeys in the PUT body and even echoes them in the response, but silently drops them at storage time (issue #125 captures confirm a freshly created variable is alwaysval=0/init=0regardless of what was sent). Passprechere and follow up withpost_variable_update()for value / init.prec=0(the controller default) is omitted from the request body — there’s no “reset to 0” path, only creation, so sending the default would just bloat the wire.
- async delete_variable(var_type, var_id)[source]¶
Delete a variable.
Wire shape:
DELETE /api/variables/{type}/{id}. Response is{"successful": true, "data": null}(no record echo); a_1/9VARIABLE_TABLE_CHANGEDframe fires alongside so an auto-refresh listener can drop the entry from the registry.
- async get_variables_type(var_type)[source]¶
Fetch + parse one variable type as
{id: VariableRecord}.Wire shape:
GET /api/variables/{type}. Wrapper over the connect-time fan-out so consumers (andController.refresh_variables) don’t have to import the private_unwrap_data/parse_api_variables_typehelpers themselves.- Parameters:
- Return type:
- async run_program_command(program_id, command)[source]¶
Send a program / folder command via the legacy REST endpoint.
Wire shape:
GET /rest/programs/{id}/{command}. Seepyisyox.runtime.ProgramCommandfor the typed command set; bare strings (the camelCase wire values) are accepted too.IoX 6 keeps this legacy path; no
/api/programs/{id}/...equivalent has been observed. The controller acknowledges receipt only — status changes flow back over the WebSocket.
- async run_network_resource(resource_id)[source]¶
Fire a network resource by id.
Wire shape:
GET /rest/networking/resources/{id}. Response is a small<RestResponse status="200">envelope on success. The controller acknowledges receipt only — it doesn’t return the result of the underlying HTTP / TCP / UDP fire.
- async post_node_update(address, body)[source]¶
Issue
POST /api/nodes/{address}with the supplied body.Documented body shape (verified against eisy-ui capture):
{"name": "<str>", "nodeType": "node" | "group"}— rename the node or group.nodeTypeis required by the server even though the address already disambiguates.
Returns the parsed response body (a
{successful, data}envelope).
- class LoadResult(config, profile, nodes, groups, folders, programs, triggers, variables, network_resources, root_name='')[source]¶
Bases:
objectOutput of
IoXClient.connect(). See attributes for shape.- Parameters:
config (ControllerConfig)
profile (Profile)
nodes (dict[str, NodeRecord])
groups (dict[str, GroupRecord])
folders (dict[str, FolderRecord])
programs (dict[str, ProgramRecord])
variables (dict[str, dict[str, VariableRecord]])
network_resources (dict[str, NetworkResourceRecord])
root_name (str)
- config: ControllerConfig¶
- nodes: dict[str, NodeRecord]¶
- groups: dict[str, GroupRecord]¶
- folders: dict[str, FolderRecord]¶
- programs: dict[str, ProgramRecord]¶
- network_resources: dict[str, NetworkResourceRecord]¶
- class LocalAuth(username, password)[source]¶
Bases:
objectHTTP basic auth against
:8443/rest/*with the local admin account.No login round-trip is needed — credentials are passed on every request. A 401 on this path means the credentials are wrong, so re-auth cannot recover.
- async authenticate(session, base_url)[source]¶
No-op — basic auth attaches per request.
- Parameters:
session (aiohttp.ClientSession)
base_url (str)
- Return type:
None
- async request_kwargs(session, base_url)[source]¶
Return kwargs that attach HTTP basic auth.
- Parameters:
session (aiohttp.ClientSession)
base_url (str)
- Return type:
- async handle_unauthorized(session, base_url)[source]¶
Cannot recover from 401 with basic auth — credentials are wrong.
- Parameters:
session (aiohttp.ClientSession)
base_url (str)
- Return type:
- async close(session, base_url)[source]¶
No-op — basic auth has no server-side session to tear down.
- Parameters:
session (aiohttp.ClientSession)
base_url (str)
- Return type:
None
- class NetworkResource(record, client)[source]¶
Bases:
objectUser-facing handle for one networking module resource.
- Parameters:
record (NetworkResourceRecord)
client (IoXClient)
- async run()[source]¶
Fire this network resource.
Wire shape:
GET /rest/networking/resources/{id}. The controller acknowledges receipt only — the response doesn’t carry the result of the underlying HTTP / TCP / UDP fire, and there’s no progress event on the WebSocket. Treat this as fire-and-forget.- Return type:
None
- class NetworkResourceRecord(address, name)[source]¶
Bases:
objectOne user-defined HTTP/TCP/UDP fire-trigger from
/rest/networking/resources.addressis the integer id as a string for URL-path symmetry.
- class Node(record, nodedef, profile, client)[source]¶
Bases:
objectUser-facing handle around one node from a
LoadResult.Construct via
Node.from_record()rather than the bare constructor so the editor resolver and nodedef are wired automatically from the parsedProfile.- Parameters:
record (NodeRecord)
nodedef (NodeDef | None)
profile (Profile)
client (IoXClient)
- classmethod from_record(record, profile, client)[source]¶
Resolve the nodedef for
recordand construct a Node.- Parameters:
record (NodeRecord)
profile (Profile)
client (IoXClient)
- Return type:
- property type: str¶
IoX type triple, e.g.
"1.65.69.0"for KeypadLinc dimmer.Plugin nodes carry a placeholder (Flume reports
"1.2.3.4"); consumers should not rely on it for plugin classification — usenodedefinstead.
- property parent_address: str | None¶
Tree-hierarchy parent (containing folder).
Noneat root.Distinct from
primary_address:<parent>is the folder,<pnode>is the device primary for multi-button hardware.
- property primary_address: str | None¶
Device primary for sub-button nodes (from
<pnode>).Sub-buttons of multi-button devices (KeypadLinc, RemoteLinc, FanLinc) carry the primary’s address.
Nonefor primaries — soprimary_address is not Nonereads as “sub-node” andprimary_address or addressas the device-grouping address.
- property properties: dict[str, NodePropertyValue]¶
Live property values, keyed by property id (e.g.
"ST").Each value is UOM-normalised to its nodedef editor’s canonical unit — e.g. an Insteon dimmer reporting
OLas a UOM-100 0-255 byte is surfaced as the UOM-51 0-100% theI_OLeditor (and the/cmdwrite surface) uses. Values already matching the editor pass through unchanged.
- property status: NodePropertyValue | None¶
Shortcut for
properties[PROP_STATUS]— the node’s primary status reading ("ST"), UOM-normalised the same waypropertiesis.Returns
Nonewhen the node hasn’t reported ST yet (common for write-only Insteon controllers and plugin nodes that don’t advertise ST). Consumers that want a scalar should readnode.status.value(a string) and parse it themselves; the property keeps the structured shape so callers can also reach.uom,.formatted, etc.
- property flag: int¶
Raw node-flag bitfield from the controller’s node table.
Bit meanings live in
pyisyox.constants.NodeFlag(NEW,IN_ERR,DEVICE_ROOT, …). Usehas_flag()for individual bit checks rather than reading this directly. Returns0when the controller didn’t carry a value for this node — treat0as “no bits set” rather than “unknown”.
- has_flag(flag)[source]¶
Return
Trueif every bit inflagis set on this node.flagmay be OR’d; combined values must have every bit set.
- property protocol: Protocol¶
Transport-protocol classification from
family_id.Returns
NODE_SERVERfor any non-core family id (PG3 plugin nodes report a slot id here),UNKNOWNfor recognised but unmapped core families. Classifies transport, not device class — useis_thermostatetc. for capability.
- property is_lock: bool¶
True for door/deadbolt locks.
Two tells: nodedef accepts
SECMD(Z-Wave / Insteon I2CS), or nodedef id contains"Lock"(IoX 6+DoorLockvariants that drive viaDON/DOF).
- property is_fan: bool¶
True for multi-speed fan controllers (nodedef id contains
"Fan").Fan nodes are a subset of dimmable (
FanLincMotoracceptsDONwith a{0, 25, 75, 100}subset), so platform classification should checkis_fanbeforeis_dimmable.
- property is_dimmable: bool¶
True if the node has a multilevel
STstate and accepts a parameterizedDON.Three conditions must all hold for a real dimmer:
STeditor reports a multilevel range (not a binary{0, 100}subset). Relay nodedefs acceptDONwith an ignored level param, soDON’s editor alone is unreliable —STis the source of truth for “can the node hold a non-binary level”.The nodedef accepts
DON. Some Insteon nodedefs (RemoteLinc2_ADV scene buttons, IMETER_SOLO meters) carry a multilevelSTeditor — meaningful for the device’s own bookkeeping — but only acceptWDU/QUERY, so they can’t actually be commanded on. Without this checkis_dimmablereturns True for them and consumers route them onto the LIGHT platform where DON-based turn_on silently fails.The accepted
DONdeclares at least one parameter (the on-level). HA’s light platform sets brightness withDON <level>; a parameterlessDONcannot take one, so the node is on/off-only even with a multilevelST(some node-server nodedefs set level via a separateSETST/SETOLcommand instead — issue #64 / Virtual#11). Real Insteon/Z-Wave dimmers declareDONwith an optional on-level param, so they still qualify.
- property is_battery_node: bool¶
True if the node reports
BATLVLbut noST.Battery-powered Insteon sensors (motion, leak, open/close) match this — they have no on/off primary state.
- property zwave_props: ZWaveProperties | None¶
Parsed
ZWavePropertiesfor Z-Wave / Z-Matter nodes;Nonefor Insteon and other families.
- async send_command(command_id, *params)[source]¶
Send a command, with editor-codec parameter validation.
Each parameter is sent as
/{value}/{uom}using the UOM its editor declares (the eisy web-UI convention —/cmd/DON/75/51). Parameters whose editor carries no real unit (UOM"0"or unset) are sent bare.When the node has no resolved nodedef (dynamically provisioned Z-Wave/Z-Matter nodes whose
UZW*defs aren’t in/rest/profiles), params pass through verbatim (numeric → int, no UOM) so the node stays controllable without validation.
- async set_climate_mode(mode)[source]¶
Set HVAC mode. Accepts enum names (
"Heat","Cool","Auto","Program Auto", …) or raw ints. The editor forCLIMDenforces subset membership (e.g. excludes"Fan Only"on devices that don’t support it).
- async set_climate_setpoint_heat(val)[source]¶
Set the heat setpoint. The codec scales by
prec(or doubles for legacy UOM-101 half-degree editors).- Parameters:
val (float)
- Return type:
None
- async set_climate_setpoint_cool(val)[source]¶
Set the cool setpoint.
- Parameters:
val (float)
- Return type:
None
- async set_fan_mode(mode)[source]¶
Set fan mode. Accepts enum names (
"Auto","On","Auto High", …) or raw ints.
- async set_on_level(val)[source]¶
Set the remembered on-level via
OL(0-100 percent).- Parameters:
val (int)
- Return type:
None
- async set_ramp_rate(val)[source]¶
Set the device’s ramp rate.
Insteon: 0-31 index into the IoX ramp-rate table. Z-Wave: seconds. The editor enforces the per-device range.
- Parameters:
val (int)
- Return type:
None
- async set_backlight(val)[source]¶
Set keypad/switch backlight intensity.
Two encodings driven by the BL editor’s UOM: UOM 100 → 0-100%, UOM 25 → integer index (or enum-name string the codec resolves).
- async start_manual_dimming()[source]¶
Begin manual dimming (legacy Insteon
BMAN).The IoX docs prefer the
FADE_*family for new code.- Return type:
None
- async rename(name)[source]¶
Rename this node. The controller emits a
_3lifecycle frame withaction="NN"on success.- Parameters:
name (str)
- Return type:
None
- async get_zwave_parameter(number)[source]¶
Request parameter
number; return{parameter, size, value}.Family id picks the wire prefix (
"4"→/rest/zwave/...,"12"→/rest/zmatter/zwave/...). RaisesNodeCommandErroron non-Z-Wave nodes or controller failure;ISYResponseParseErroron malformed bodies.
- async set_zwave_parameter(number, value, size)[source]¶
Write parameter
number(size 1/2/4 bytes) on this Z-Wave node.The post-write report arrives asynchronously on the WS stream. Raises
NodeCommandErroron rejection so failures aren’t silent.
- async set_zwave_lock_code(user_num, code)[source]¶
Program a Z-Wave lock’s user-code slot. Raises
NodeCommandErroron a failed envelope.
- exception NodeCommandError[source]¶
Bases:
ExceptionRaised when a command can’t be sent — unknown command id, missing parameter, validation failure, or no nodedef resolved for this node.
Defined here (not in
node.py) to keep the module dependency one-way:node.pyimports from_commands.py, never the reverse.
- class NodeLifecycleAction(*values)[source]¶
Bases:
StrEnumVerbs the eisy emits via
<control>_3</control>events — ISY994 Developer Cookbook §8.5.5 (“Node Changed/Updated”). PyISY 3.x keeps the same mapping.<eventInfo>child tags per verb are inNODE_LIFECYCLE_EVENT_INFO_TAGS.ENcarries anenabledboolean in<eventInfo>— there’s no separate “disabled” verb; the same code handles both transitions.- NODE_ADDED¶
Node added.
<eventInfo>carries<nodeName>plus a<nodeType>that is itself the full<node>element — seeNodeLifecycleEvent.node_xml.
- NODE_REMOVED¶
Node removed (device deleted from the controller).
- NODE_RENAMED¶
Node renamed (display name changed).
- NODE_MOVED¶
Node moved into a Scene.
- LINK_CHANGED¶
Link changed (within a scene). Not supported by the controller — kept for documentation; never observed.
- NODE_REMOVED_FROM_GROUP¶
Node removed from a Scene.
- PARENT_CHANGED¶
Parent (primary node) changed.
- NODE_ENABLED¶
Node enabled/disabled — direction is in
eventInfo.enabled.
- POWER_INFO_CHANGED¶
Power-info changed —
<eventInfo>carries<deviceClass>/<wattage>/<dcPeriod>.
- DEVICE_ID_CHANGED¶
Device ID changed. Not implemented by the controller — kept for documentation.
- DEVICE_PROPERTY_CHANGED¶
Device property changed — UPB only.
- PENDING_DEVICE_OP¶
Pending device operation queued, awaiting commit. On Insteon a write (e.g. changing backlight level) surfaces
WHfirst, thenPROGRAMMING_DEVICE(WD) while the value is written; a property-update event arrives separately once it lands.
- PROGRAMMING_DEVICE¶
The controller is carrying out a programming/write operation on this node (follows
PENDING_DEVICE_OP). Cookbook name: “Programming Device”. Not a completion signal — watch the subsequent property-update event for the new value.
- NODE_REVISED¶
Node revised — drastically changed (UPB-style); the consumer should discard cached info for the node and rebuild it.
<eventInfo>carries the full<node>structure.
- NODE_TYPE_INFO_CHANGED¶
Supported-type info changed — the node’s nodedef assignment was reassigned (e.g. a node server’s
changeNode, or a device driver detecting new capabilities). The primary signal that a cached nodedef → entity mapping is stale. Not fired for/rest/profilesdefinition updates or moves, or at startup migration — those rewrite the profile DB without notifying.
- ALL_NODES_ADDED¶
All nodes for a single device have been added (bulk). Fired after an include / re-pair so consumers can coalesce a single refresh per device instead of per child node.
- LINK_UPDATED¶
Scene link updated — a link’s properties (on-level / ramp rate) changed for an existing scene member.
- DISCOVERING_NODES¶
Discovering nodes (linking in progress). No node.
- NODE_DISCOVERY_COMPLETE¶
Node discovery complete. No node.
- NODE_ERROR¶
Node communication error (device unreachable).
- NODE_ERROR_CLEARED¶
A previously-reported node communication error was cleared (cookbook: “Clear Node Error / Comm. Errors Cleared”) — the companion to
NODE_ERROR.
- FOLDER_ADDED¶
Folder added.
- FOLDER_REMOVED¶
Folder removed.
- FOLDER_RENAMED¶
Folder renamed —
<eventInfo>carries<newName>.
- GROUP_ADDED¶
Scene (group) added —
<eventInfo>carries<groupName>/<groupType>.
- GROUP_REMOVED¶
Scene (group) removed.
- GROUP_RENAMED¶
Scene (group) renamed —
<eventInfo>carries<newName>.
- NET_RENAMED¶
A networking-module resource was renamed (
node= the new name). Doesn’t affect the node registry.
- class NodeLifecycleEvent(action, node_address, raw_action, seqnum, node_xml=None, enabled=None)[source]¶
Bases:
objectA high-level summary of a
<control>_3</control>lifecycle frame.Emitted alongside the raw
Eventwhenever the dispatcher sees one of the actions inNodeLifecycleAction. Consumers subscribe viapyisyox.controller.Controller.add_node_lifecycle_listener()to drive their own reload UX (HA Core’s Repair issue, etc.).- Variables:
action (pyisyox.runtime.events.NodeLifecycleAction | str) – The lifecycle verb (typed enum). Unknown verbs come through as a plain string via
raw_action.node_address (str) – Wire address of the affected node. Empty string only for system-wide signals (none observed yet).
raw_action (str) – The string action value verbatim, in case a new verb appears that isn’t yet in
NodeLifecycleAction.node_xml (str | None) – For
NDactions, the inner<node>element text from<eventInfo>.Nonefor verbs that don’t include the full element. Consumers wanting the parsed shape can pass this toparse_lifecycle_node_xml().enabled (bool | None) – For
EN(NODE_ENABLED) actions, the new enabled/disabled state from<eventInfo><enabled>— the same value already written back toNode.enabled.Nonefor every other verb (and forENframes that omit the flag).
- Parameters:
- action: NodeLifecycleAction | str¶
- property requires_reload: bool¶
True for verbs that invalidate the cached node/group/folder registry.
Reload-worthy:
ND/NR/NN(node added/removed/renamed — the registry’s set or display names are stale),EN(enabled/disabled — the entity’s property shape may change),RV(revised — discard and rebuild this node),NI(supported-type info changed — the node’s nodedef assignment was reassigned, so the cached nodedef→entity mapping is stale; per UDI’s notification taxonomy this is the primary signal for profile-related node changes),AA(all-nodes-added bulk signal after a device include),RG(removed from scene — membership changed),SC(node-discovery complete — new nodes may have appeared), and the folder/scene tree verbsFD/FR/FN/GD/GR/GN(thegroups/foldersregistries are stale).Softer signals — informational, don’t trigger reload UX:
MV(added to scene),CL(link changed — not supported),LU(scene link’s on-level/ramp updated — property change, not shape change),PC(parent changed),PI(power info),DI(device id — not implemented),DP(UPB property),WH(pending op),WD(programming device — a property-update event follows),SN(discovering nodes — wait forSC),CE/NE(comm error/cleared — no shape change),WR(a networking resource was renamed — doesn’t touch nodes).
- class NodePropertyValue(id, value, formatted='', uom='', name='', precision=0)[source]¶
Bases:
objectOne live property value (JSON
/api/nodesor XML/rest/status).precision: decimal precision (raw / 10**precision). Wire field isprec; defaults to0when omitted.
- class NodeRecord(address, name, nodedef_id, family_id, instance_id, type='', parent_address=None, pnode=None, enabled=True, flag=0, properties=<factory>, zwave_props=None)[source]¶
Bases:
objectOne node from
/api/nodes, with property values merged in from/rest/status. The structural fields come from JSON; thepropertiesdict is the merged-in canonical state.- Parameters:
- flag: int¶
Bitfield from the controller’s node table — see
pyisyox.constants.NodeFlagfor the bit meanings (NEW, IN_ERR, DEVICE_ROOT, …). Sourced from theflagfield on/api/nodesJSON (which the controller stringifies — e.g."128");0when the controller didn’t supply one for this node.
- properties: dict[str, NodePropertyValue]¶
- zwave_props: ZWaveProperties | None¶
Parsed Z-Wave
devtypeblock;Nonefor non-Z-Wave nodes.
- class NodeType(*values)[source]¶
Bases:
StrEnumRequired
nodeTypebody field onPOST /api/nodes/{address}; also the lifecycle-event vocabulary. Legacy XML surface uses numeric codes — seepyisyox.constants.UDHierarchyNodeType.- NODE¶
- GROUP¶
- FOLDER¶
- class PortalAuth(email, password)[source]¶
Bases:
objectJWT bearer auth from
POST :443/api/login.Maintains an in-memory
TokenPairwith proactive refresh. On 401, attempts one refresh; if refresh fails (or has expired), falls back to a fresh login.Login URL:
{base_url}/api/login. Refresh URL:{base_url}/api/jwt/refresh. Logout URL (optional, onclose()):{base_url}/api/jwt/logout. Verified against eisy 1.0.3 —POST /api/jwt/logoutreturns200with{"successful": true, "data": null}. (Pre-2026-05-12 versions of this module used/api/logout, which 404s.)- LOGIN_PATH¶
- REFRESH_PATH¶
- LOGOUT_PATH¶
- PROACTIVE_REFRESH_LEEWAY¶
Number of seconds before access-token expiry at which we proactively refresh.
- property tokens: TokenPair | None¶
Currently held tokens, or
Noneif not yet authenticated.Tests use this to assert state without forcing a real network round-trip.
- async authenticate(session, base_url)[source]¶
Perform
POST /api/loginand store the returned token pair.Concurrent calls collapse onto a single login round-trip via the instance lock; the second caller observes the tokens already set and returns without making a network request.
- Raises:
AuthError – When the login response is not
successful: trueor lacks tokens.- Parameters:
session (aiohttp.ClientSession)
base_url (str)
- Return type:
None
- async request_kwargs(session, base_url)[source]¶
Return
Authorization: Bearer <accessToken>headers.Refreshes the token proactively if it expires within
PROACTIVE_REFRESH_LEEWAYseconds, avoiding the cost of an in-flight 401 + refresh + retry round. Concurrent callers that observe an expiring token both queue on the auth lock; the winner refreshes once, the runners-up re-check and skip.- Parameters:
session (aiohttp.ClientSession)
base_url (str)
- Return type:
- async handle_unauthorized(session, base_url)[source]¶
Handle 401: try refresh, then re-login.
Concurrent 401s from in-flight requests all enter
_refresh_or_relogin; the first runs the refresh, subsequent callers re-check the cached token (which has just been updated) and skip the network round-trip. Returns True if re-auth succeeded and the caller should retry the original request; False if both refresh and login failed.- Parameters:
session (aiohttp.ClientSession)
base_url (str)
- Return type:
- async close(session, base_url)[source]¶
Best-effort logout against
POST /api/jwt/logout, then clear the in-memory tokens.If we don’t tell the eisy we’re done, the refresh token stays live for its full 30-day TTL — useful only to attackers who somehow obtain it. The logout call is best-effort: any error (network down, controller already gone, stale token) is logged at debug level and swallowed. The local token state is cleared regardless so the consumer can construct a fresh
PortalAuthand re-authenticate.- Parameters:
session (aiohttp.ClientSession)
base_url (str)
- Return type:
None
- 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:
- class Program(record, client)[source]¶
Bases:
_ProgramBaseUser-facing handle for one program.
- Parameters:
record (ProgramRecord)
client (IoXClient)
- property enabled: bool | None¶
Falsewhen the program is disabled.Noneif the wire payload omitted the field (defensive — every captured program carries it).
- property running: str | None¶
Raw runtime-state field as the controller reported it.
Two wire shapes: REST
/api/programsemits a human label ("idle"/"running then"/"running else"); the WS event stream emits the cookbook<s>byte (two ASCII hex digits). Userun_state/eval_statefor a firmware-agnostic typed view.
- property run_state: ProgramRunState | None¶
Typed run-clause state — one of
IDLE/THEN/ELSE.Nonewhen the program errored (ProgramEvalState.NOT_LOADED) or the controller hasn’t reported a running field yet.
- property eval_state: ProgramEvalState | None¶
Typed if-clause evaluation state — disambiguates the three “not really True/False” cases that
statuscollapses.Nonefrom REST loads (which only carry the run label) and when the controller hasn’t reported a running field yet.
- property last_run_time: datetime | None¶
Tz-aware
datetimeof the program’s last run start, orNoneif it has never run.REST
/api/programsemits the timestamp as ISO 8601 UTC ("2026-05-10T14:49:53.000Z"); we parse on read so the wrapper hands consumers a realdatetimerather than the wire string. The raw form remains accessible viaself._record.last_run_timefor diagnostics / round-trip.
- property last_finish_time: datetime | None¶
Tz-aware
datetimeof the program’s last run completion, orNone.
- property next_scheduled_run_time: datetime | None¶
Tz-aware
datetimeof the next scheduled run, orNonefor manual-only programs.
- async run_then()[source]¶
Run the program’s
thenclause.Wire:
GET /rest/programs/{id}/runThen.- Return type:
None
- async run_if()[source]¶
Re-evaluate the program’s
ifcondition (without running the matching clause’s actions).- Return type:
None
- class ProgramCommand(*values)[source]¶
Bases:
StrEnumVerbs accepted by
GET /rest/programs/{id}/{command}.Members are the camelCase wire strings the eisy expects; consumers building HA-style snake-case service schemas can use the member names (
ProgramCommand.RUN_THEN.name == "RUN_THEN") or pull the wire string via.value/ direct comparison (StrEnummembers compare equal to their underlying string).Folders only support
RUN,STOP,ENABLE, andDISABLE—Program-only verbs raise server-side on a folder target.- RUN¶
Run the program (or every program under a folder). For programs, evaluates the if-clause and runs the matching branch.
- RUN_THEN¶
Run the program’s
thenclause directly.
- RUN_ELSE¶
Run the program’s
elseclause directly.
- RUN_IF¶
Re-evaluate the program’s
ifcondition without running the matching clause’s actions.
- STOP¶
Abort an executing program / folder.
- ENABLE¶
Enable the program / folder for evaluation.
- DISABLE¶
Disable the program / folder (status freezes).
- ENABLE_RUN_AT_STARTUP¶
Mark the program as auto-run on controller boot.
- DISABLE_RUN_AT_STARTUP¶
Clear the auto-run-on-boot flag.
- class ProgramEvalState(*values)[source]¶
Bases:
IntEnumHigh-nibble of the
<s>byte on a program-status frame.Cookbook §8.5.3: the program’s last if-clause evaluation result. The
status: boolfield onProgramStatusEventderives from the same source (the<on/>/<off/>element) but this enum disambiguates the three “not really True/False” cases that the bool collapses.Note
NOT_LOADEDis the cookbook’s literal label, but in practice the controller emits0xF0when the program failed to compile or hit a runtime error — not (only) when it hasn’t been loaded yet. Treat this as the program-error sentinel; seeProgramRunStatefor whyrun_stateisNonein this case.- UNKNOWN¶
- TRUE¶
- FALSE¶
- NOT_LOADED¶
- class ProgramFolder(record, client)[source]¶
Bases:
_ProgramBaseOrganisational container for programs.
Folders share the program command surface but only
run/stop/enable/disableare documented to apply. The eisy aggregates child status intostatusserver-side.- Parameters:
record (ProgramRecord)
client (IoXClient)
- class ProgramRecord(address, name, path, parent_address, is_folder, status, enabled=None, run_at_startup=None, running=None, last_run_time=None, last_finish_time=None, next_scheduled_run_time=None)[source]¶
Bases:
objectOne program or program-folder from
/api/programs.Programs and folders share the flat list, discriminated by
is_folder. Status strings"true"/"false"are decoded to bool; empty time strings becomeNone.pathis the slash-joined ancestry (excluding the"My Programs"root) to match the pyisy 3.x convention. Timestamps stay as ISO 8601 strings on the record (wire shape preserved);pyisyox.runtime.Programexposes them as parsed tz-awaredatetimeinstances.runningis free-form ("idle"/"running then"/ the cookbook<s>byte) — the typedProgram.run_state/Program.eval_stateaccessors decode it.- Parameters:
- class ProgramRunState(*values)[source]¶
Bases:
IntEnumLow-nibble of the
<s>byte on a program-status frame.Cookbook §8.5.3: exactly one of three run-clause states per frame, ORed with a
ProgramEvalState(high nibble) in the byte. Absent (Noneon the event) when the high nibble isProgramEvalState.NOT_LOADED— the program errored so there’s no clause currently running.- IDLE¶
- THEN¶
- ELSE¶
- class ProgramStatusEvent(address, status, running, seqnum, run_state=None, eval_state=None, enabled=None, run_at_startup=None)[source]¶
Bases:
objectA program toggled true/false on the controller.
Emitted by
EventDispatcherwhenever a<control>_1</control>frame with<action>0</action>arrives carrying a program id in its<eventInfo>. The matchingpyisyox.client.ProgramRecordis mutated in place before listeners fire, so consumers readingprogram.statusfrom a callback see the updated value.- Variables:
address (str) – Program id (4-character hex, zero-padded to match
/api/programs).status (bool) –
Truewhen the cookbook<s>byte’s eval state isProgramEvalState.TRUE(the if-clause matched on the most recent evaluation);FalseforProgramEvalState.FALSE. ForProgramEvalState.UNKNOWN/ProgramEvalState.NOT_LOADED(and frames with no<s>byte) the dispatcher carries forward the priorrecord.statusso a transient unknown doesn’t flip the entity. Wire-shape note: the<on/>/<off/>elements that ride along on the same frame are the enabled flag, not the status — seeenabled.running (int | None) – Raw
<s>byte the eisy sent, orNoneif absent. Cookbook §8.5.3: the byte is a bitwise OR of aProgramRunState(low nibble) and aProgramEvalState(high nibble); userun_state/eval_statefor the typed view.run_state (pyisyox.runtime.events.ProgramRunState | None) – Decoded low nibble —
IDLE/THEN/ELSE, orNonewhen the program isn’t loaded (eval_state == NOT_LOADED) or the wire byte was absent / unrecognised.eval_state (pyisyox.runtime.events.ProgramEvalState | None) – Decoded high nibble —
UNKNOWN/TRUE/FALSE/NOT_LOADED, orNonewhen the wire byte was absent / unrecognised. Disambiguates the three “not really True/False” cases thatstatus: boolcollapses.NOT_LOADEDis the cookbook label for what is in practice the program-errored sentinel — seeProgramEvalState.enabled (bool | None) – New
enabledstate when the frame carried an<on/>/<off/>element —Truefor<on/>,Falsefor<off/>.Nonewhen the frame omitted both (some “ran”-only frames carry only<r>/<f>/<s>— see cookbook §8.5.3). The matching record’spyisyox.client.ProgramRecord.enabledis updated in-place before listeners fire when this is non-None.run_at_startup (bool | None) – New
run_at_startupstate when the frame carried an<rr/>(True) or<nr/>(False) element.Nonewhen the frame omitted both. Mirror of the enabled-flag pattern; the record’spyisyox.client.ProgramRecord.run_at_startupis updated in-place before listeners fire.
- Parameters:
address (str)
status (bool)
running (int | None)
seqnum (int)
run_state (ProgramRunState | None)
eval_state (ProgramEvalState | None)
enabled (bool | None)
run_at_startup (bool | None)
- run_state: ProgramRunState | None¶
- eval_state: ProgramEvalState | None¶
- class ProgressAction(*values)[source]¶
Bases:
StrEnumAction codes on
SystemEventControl.PROGRESS(_7) frames — Cookbook §8.5.9.<eventInfo>is free-text progress detail.- UPDATE¶
Generic progress update.
- DEVICE_ADDER_INFO¶
Device-adder info (UPB only).
- DEVICE_ADDER_WARN¶
Device-adder warning (UPB only).
- DEVICE_ADDER_ERROR¶
Device-adder error (UPB only).
- class Reading(property, platform, is_enum=False)[source]¶
Bases:
objectA property surfaced as a sensor or binary_sensor entity.
- Variables:
property (pyisyox.schema.nodedef.NodeProperty) – The
NodePropertydefinition.platform (pyisyox.classifier.ReadingPlatform) – Which HA platform hosts this entity.
is_enum (bool) – True when the property’s editor carries an enum
namesmap — caller should set HAdevice_class="enum"and supplyoptions=[...]from the editor.
- Parameters:
property (NodeProperty)
platform (ReadingPlatform)
is_enum (bool)
- property: NodeProperty¶
- platform: ReadingPlatform¶
- class ReadingPlatform(*values)[source]¶
Bases:
StrEnumHA platform for a property reading entity.
- SENSOR¶
- BINARY_SENSOR¶
- class SecuritySystemAction(*values)[source]¶
Bases:
StrEnumAction codes on
SystemEventControl.SECURITY_SYSTEM(_8) frames — Cookbook §8.5.10.nodeand<eventInfo>are null.- DISCONNECTED¶
- CONNECTED¶
- DISARMED¶
- ARMED_AWAY¶
- ARMED_STAY¶
- ARMED_STAY_INSTANT¶
- ARMED_NIGHT¶
- ARMED_NIGHT_INSTANT¶
- ARMED_VACATION¶
- class SystemConfigAction(*values)[source]¶
Bases:
StrEnumAction codes on
SystemEventControl.SYSTEM_CONFIG(_4) frames — Cookbook §8.5.6.- TIME_CHANGED¶
- TIME_CONFIG_CHANGED¶
- NTP_SETTINGS_UPDATED¶
- NOTIFICATIONS_SETTINGS_UPDATED¶
- NTP_COMM_ERROR¶
- BATCH_MODE_UPDATED¶
Batch mode toggled —
<eventInfo><status>is"1"/"0".
- BATTERY_WRITE_MODE_UPDATED¶
Battery-powered-write mode toggled —
<eventInfo><status>is"1"/"0".
- class SystemEditorAction(*values)[source]¶
Bases:
StrEnumAction codes on
SystemEventControl.SYSTEM_EDITOR(_24) frames. The<node>slot carries the editor name (e.g._sys_notify_short).- EDITOR_CHANGED¶
A system editor’s contents changed.
- class SystemEventControl(*values)[source]¶
Bases:
StrEnumIoX WebSocket “system” control codes (underscore-prefixed).
Property updates use the property id (
"ST","GV1", …) with a populatednode_address. System events use one of these underscore-prefixed codes with an emptynode_address.Codes
_0-_23are the full ISY-994 set from the ISY994 Developer Cookbook §8.5;_24-_28are IoX-6 additions (system editors, the modern Z-Wave / ZigBee / Matter drivers, system upgrade) not in that document — tracked from UDI’s internalUDEvents.htaxonomy. Newer IoX firmware may emit further codes; those aren’t enumerated, andlabel()passes them through verbatim so logs still identify them.- HEARTBEAT¶
Periodic heartbeat.
<action>is the duration in seconds until the next expected heartbeat (use it to detect a stalled stream). No<eventInfo>.
- TRIGGER¶
Trigger events — program status, variable change/init, schedule change, key/info-string pushes, “get status” refresh signal.
<action>discriminates; seeTriggerAction.
- DRIVER_SPECIFIC¶
Driver-specific events — payload depends on the underlying protocol driver. Not modelled.
- NODE_LIFECYCLE¶
Node / scene / folder lifecycle — add / remove / rename / enable / revise / comm-error / etc.
<action>carries the verb; seeNodeLifecycleActionandNODE_LIFECYCLE_EVENT_INFO_TAGS.
- SYSTEM_CONFIG¶
System configuration updated — time / NTP / notifications / batch-mode / battery-write-mode.
<action>0-6; seeSystemConfigAction.
- SYSTEM_STATUS¶
Controller-side busy/idle/safe-mode status.
<action>0-3; seepyisyox.constants.SystemStatus.
- INTERNET_ACCESS¶
Internet-access status — disabled / enabled (
<eventInfo>= external URL) / failed. SeeInternetAccessStatus.
- PROGRESS¶
Progress report during long-running operations (device programming, restore, device-adder).
<action>1 / 2.1 / 2.2 / 2.3; seeProgressAction. The_7A/_7Mdevice-write sub-codes also ride through on this control — seeDeviceWriteAction.
- SECURITY_SYSTEM¶
Security-system event — connected / disconnected / armed-* / disarmed. See
SecuritySystemAction.
- SYSTEM_ALERT¶
System alert event — “not implemented and should be ignored” per the cookbook.
- OPENADR¶
OpenADR / Flex-Your-Power events — ISY994 Z-Series demand-response.
- CLIMATE¶
Climate / weather events — required the ISY994 WeatherBug module; not present on eisy.
- AMI_SEP¶
AMI/SEP energy events — ISY994 only (see the Energy Management Developer’s Manual).
- ENERGY_MONITORING¶
External energy-monitoring (Brultech) — ISY994 only; on later firmware these are folded into node events instead.
- UPB_LINKER¶
UPB linker events — UPB-enabled units only.
- UPB_DEVICE_ADDER¶
UPB device-adder state — UPB-enabled units only.
- UPB_DEVICE_STATUS¶
UPB device-status events — UPB-enabled units only.
- GAS_METER¶
Gas-meter events — ISY994 only.
- ZIGBEE¶
Legacy ZigBee events — ISY994-era driver. See
ZIGBEE_UYB(_27) for the IoX-6+ ZigBee driver used on eisy.
- ELK¶
ELK alarm-panel events — requires the ELK module (see the ELK Integration Developer’s Manual).
- DEVICE_LINKER¶
Device-linker events —
<action>1 (status) / 2 (cleared). SeeDeviceLinkerAction.
- ZWAVE¶
Legacy Z-Wave integration events — ISY994-era driver. See
ZMATTER_ZWAVE(_25) for the IoX-6+ ZMatter Z-Wave driver used on eisy.
- BILLING¶
Billing events — ISY994 ZS-series only.
- PORTAL¶
Portal events — portal socket-connection / account-registration status when a portal module is installed.
- SYSTEM_EDITOR¶
System editor changed — fired when a “system editor” (e.g.
_sys_notify_short) is updated.<node>carries the editor name.<action>isSystemEditorAction. IoX-6 addition.
- ZMATTER_ZWAVE¶
ZMatter Z-Wave events — IoX-6+ Z-Wave driver on eisy hardware.
<action>is dotted ("{category}.{type}"); category numbers are system-status (1), discovery (2), general-status (3), general-error (4), S2 (5), OTA (6), backup/restore (7), device- interview (8), button-detect (9), logger (10). Sub-action details aren’t modelled — the dotted string passes through verbatim. Distinct fromZWAVE(_21, ISY994-era driver).
- SYSTEM_UPGRADE¶
System-upgrade lifecycle —
<action>isSystemUpgradeAction(active / inactive / available / reboot-required). IoX-6 addition.
- ZIGBEE_UYB¶
ZigBee events — IoX-6+ ZigBee driver on eisy hardware. Same dotted
"{category}.{type}"action shape asZMATTER_ZWAVE(minus the logger sub-category). Distinct fromZIGBEE(_18, ISY994-era driver).
- MATTER_STATUS¶
Matter network status — IoX-6+ Matter driver.
<action>is dotted; active sub-categories are 1 (system status), 2 (discovery), 3 (RX/TX), 8 (device interview). Not in the ISY994 cookbook.Note:
_28is also reserved in UDI’s source for Profile change events (actions 1-8 — profile/editor/nodedef/linkdef updated/deleted) — but no firmware path fires those today (placeholders since Dec 2024 per UDI). Don’t subscribe expecting them.
- class SystemUpgradeAction(*values)[source]¶
Bases:
StrEnumAction codes on
SystemEventControl.SYSTEM_UPGRADE(_26) frames — IoX-6 firmware-upgrade lifecycle.- ACTIVE¶
Upgrade in progress.
- INACTIVE¶
Upgrade not active (post-completion or idle).
- AVAILABLE¶
A new upgrade is available to install.
- REBOOT_REQUIRED¶
Upgrade applied; reboot required to take effect.
- exception TLSVersionError[source]¶
Bases:
ValueErrorRaised when the requested TLS version isn’t usable on this build.
- class TriggerAction(*values)[source]¶
Bases:
StrEnumAction codes carried in
SystemEventControl.TRIGGER(_1) frames — ISY994 Developer Cookbook §8.5.3.<action>discriminates what the frame is; pyisyox only routes onPROGRAM_STATUS/VARIABLE_VALUE/VARIABLE_INIT.- PROGRAM_STATUS¶
Program status changed — handled by
_apply_program_status.<eventInfo>carries the program<id>, enabled/run-at-reboot flags, last run/finish times, and a bitwise<s>status.
- GET_STATUS¶
“Get status” — the controller is telling subscribers to re-poll everything (e.g. after a config change). No payload.
- KEY_CHANGED¶
A key changed.
nodecarries the key.
- INFO_STRING¶
An info string.
nodecarries the key;<eventInfo>is the text.
- IR_LEARN_MODE¶
IR learn mode toggled. No payload.
- SCHEDULE¶
A schedule’s status changed.
nodecarries the key.
- VARIABLE_VALUE¶
Variable value changed — handled by
_apply_variable_change.<eventInfo>carries<var type id><val><ts>.
- VARIABLE_INIT¶
Variable init (restore-on-startup) value changed — same handler / payload shape as
VARIABLE_VALUE, applied toinit.
- KEY¶
The current subscription key, sent once right after a new subscription is established.
<eventInfo>is the key.
- VARIABLE_TABLE_CHANGED¶
Variable table structurally changed — fires when a variable is added or removed, or when its precision is changed (a metadata change that the per-value
VARIABLE_VALUE/VARIABLE_INITframes don’t cover).<eventInfo>carries<var><type>N</type><id>0</id></var>(id=0 is the wildcard sentinel — “this whole type’s table changed”). The right response is to re-fetch/api/variables/{type}for the affected type so the registry mirrors the controller’s metadata (precision in particular — the wirevalfrom the per-value frames is raw, and a stale precision will mis-render the value). The dispatcher recognises it and firesEventDispatcher.add_variable_table_change_listener()callbacks; the dispatcher itself does not re-fetch.
- class Variable(record, client)[source]¶
Bases:
objectUser-facing handle for one controller variable.
- Parameters:
record (VariableRecord)
client (IoXClient)
- classmethod from_record(record, client)[source]¶
Construct a
Variablefrom a parsed record.- Parameters:
record (VariableRecord)
client (IoXClient)
- Return type:
- property value: int | float¶
Current value (wire field
val).Reads reflect the latest write — mutations via
set_value()update the underlying record in place after a successful POST.Type is
int | float: most variables read back asintfrom the wire (/api/variables/{type}parses"val"as int), but a controller may surfacefloaton a fresh write that posted a non-integer (the modernPOST /api/variables/{type}/{id}endpoint accepts floats and the wrapper stores whatever was sent on success).
- property ts: str¶
Last-change timestamp as the controller emits it.
ISO 8601 UTC string when present,
""when the controller doesn’t stamp the entry (e.g. freshly created variables before the first change).
- async set_value(value)[source]¶
Set the current value of this variable.
Wire shape:
POST /api/variables/{type}/{id}with body{"value": <number>}. The modern endpoint accepts bothintandfloat— for aprecision > 0variable, send the displayed float (e.g.51.5) and the controller applies the* 10**precisionscale on store. Sending aninton the same variable means the controller stores it verbatim (no scale applied), which produces a mismatch between consumer-displayed and controller-internal values — so callers driving displayed-unit UIs should send floats.Strings are tolerated for legacy callers (parsed as float if they contain a decimal point, else int).
Updates the underlying record on success so subsequent reads of
valuereflect the new state without waiting for a WS frame.- Parameters:
value (float)
- Return type:
None
- async set_init(init)[source]¶
Set the init / restore-on-startup value.
Wire shape:
POST /api/variables/{type}/{id}with{"init": <number>}. Same int-or-float semantics asset_value().- Parameters:
init (float)
- Return type:
None
- async rename(name)[source]¶
Rename this variable on the controller.
Wire shape:
POST /api/variables/{type}/{id}with{"name": "<str>"}.- Parameters:
name (str)
- Return type:
None
- async set_precision(prec)[source]¶
Set decimal precision (
displayed = raw / 10**precision).Wire shape:
POST /api/variables/{type}/{id}with{"prec": <int>}.A precision change fires
_1/9(VARIABLE_TABLE_CHANGED) on the WebSocket — not the per-value6/7frames — so consumers that only listen on value/init updates won’t see the new precision until they refresh. Thepyisyox.Controllerauto-refreshes the affected type on this event when its dispatcher is wired.- Parameters:
prec (int)
- Return type:
None
- async delete()[source]¶
Delete this variable on the controller.
Wire shape:
DELETE /api/variables/{type}/{id}. Fires aVARIABLE_TABLE_CHANGEDframe so an auto-refresh listener can drop the entry from the registry; the wrapper itself becomes inert (subsequent mutations would 404 — the wrapper carries no flag for this; consumers should drop their reference).- Return type:
None
- class VariableField(*values)[source]¶
Bases:
StrEnumBody keys accepted by
POST /api/variables/{type}/{id}; one key per request.- VALUE¶
- INIT¶
- NAME¶
- PREC¶
- class VariableRecord(type_id, id, name, value=0, init=0, precision=0, ts='')[source]¶
Bases:
objectOne entry from
/api/variables/{type}.type_idis"1"(integer) or"2"(state). Wire fieldvalis exposed asvalue;precis exposed asprecision.- Parameters:
- class VariableTableChangeEvent(type_id, seqnum)[source]¶
Bases:
objectA
_1/ action"9"system event — variable table changed.The eisy fires this when a variable is added, removed, or has its precision changed (a structural / metadata change that the per-value
TriggerAction.VARIABLE_VALUE/VARIABLE_INITframes don’t carry).<eventInfo>payload:<var><type>N</type><id>0</id></var>
id=0is the wildcard sentinel — “this whole type’s table changed”, not a specific variable. Consumers should re-fetch/api/variables/{type_id}to pick up the new metadata (precision in particular — the per-value frames carry rawvaland a stale precision will mis-render every variable of this type until the registry is refreshed).
- class WebSocketEventStream(client, dispatcher, path='/rest/subscribe')[source]¶
Bases:
objectBackground reader that feeds frames into an
EventDispatcher.Lifecycle:
start()schedules the read task and returns immediately.The task connects, dispatches frames, reconnects on transport errors, and pumps
EventStreamStatusnotifications to any registered status listener. On each connect it holdsSYNCING(notCONNECTED) until the controller’s initial status replay drains, so consumers don’t treat the replay as live events.stop()cancels the task and closes any active WS.
The class deliberately keeps its surface narrow — the consumer is expected to be the top-level
ISYglue object that owns both theIoXClientand the dispatcher.- Parameters:
client (IoXClient)
dispatcher (EventDispatcher)
path (str)
- property status: EventStreamStatus¶
Most-recent stream status.
Updated on every transition (initialise / connect / reconnect / disconnect / lost). Defaults to
EventStreamStatus.NOT_STARTEDbeforestart(). Useful for system-health pages that want a single readable status string without subscribing to every notification.
- property connected: bool¶
Truewhile the stream is in theCONNECTEDstate.Convenience over comparing
statusdirectly. Note thatconnectedflippingFalsedoesn’t mean the reader has given up — it may be reconnecting, or inEventStreamStatus.SYNCING(socket open but the controller’s initial status replay hasn’t drained yet — intentionally not “connected” so event consumers don’t treat the replay as live changes).
- property last_event_at: datetime | None¶
UTC timestamp of the most recent text frame, or
Noneif no frame has been received this lifetime.The eisy emits a heartbeat
<control>_0</control>frame every 30 seconds even when nothing else changes, so a stalelast_event_at(more than ~60 s ago) is a reasonable signal that the connection is broken even when the WS handshake hasn’t returned an error yet.
- add_status_listener(callback)[source]¶
Register a callback for stream-status changes.
- Returns:
An unsubscribe function.
- Parameters:
callback (Callable[[EventStreamStatus], None])
- Return type:
Callable[[], None]
- class ZWaveProperties(category='0', devtype_mfg='0.0.0', devtype_gen='0.0.0', basic_type='0', generic_type='0', specific_type='0', mfr_id='0', prod_type_id='0', product_id='0')[source]¶
Bases:
objectZ-Wave product details from the controller’s
devtypeblock.Surfaces only on Z-Wave / Z-Matter nodes (family ids
"4"/"12"). Sourced from thedevtypeJSON object on/api/nodes:catis the Z-Wave generic-class id (e.g."121"for a multi-channel composite,"155"for a notification sensor);mfgis"<mfr_id>.<prod_type_id>.<product_id>";genis"<basic>.<generic>.<specific>". The split-outbasic_type/generic_type/specific_typeandmfr_id/prod_type_id/product_idare convenience accessors over the same values.Adapted from the legacy
PyISY.helpers.ZWavePropertiesso consumers (notably the hacs-udi-iox device-class lookup) can keep usingnode.zwave_props.categoryinstead of re-parsing the triple.- Parameters:
- classmethod from_devtype(devtype)[source]¶
Parse a
devtypeJSON object — or returnNonefor any non-mapping input (Insteon nodes don’t carry one).- Parameters:
devtype (Any)
- Return type:
ZWaveProperties | None
- build_sslcontext(*, use_https, tls_version=None, verify_ssl=False)[source]¶
Build an
ssl.SSLContextfor the connection, orNonewhen the controller is reached over HTTP.- Parameters:
use_https (bool) –
Falseshort-circuits toNone.tls_version (float | None) –
None(default) auto-negotiates the highest mutually-supported version.1.2or1.3pin a specific minimum + maximum. Anything else raises.verify_ssl (bool) –
False(default) accepts the controller’s self-signed certificate.Trueenables strict verification — requires consumers to deploy their own CA.
- Raises:
TLSVersionError – When
tls_versionisn’tNone/1.2/1.3.- Return type:
SSLContext | None
- 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 taggedis_enumfor enum editors. WhenNone(e.g. in unit tests), all readings default tosensorwithis_enum=False— callers can still render them, just without device-class hints.
- Returns:
A
ClassificationResultwith controllable / triggers / buttons / parameterized_commands / readings / aux_controls populated.find_editoralso drivesaux_controlscandidate platforms; without it a readable writable control falls back to its property’s read classification, and a write-only control falls back tocandidate_platform=Nonefor the consumer to resolve.- Return type:
- describe_system_event(control, action)[source]¶
Render a
<control>/<action>pair from a system event frame as a friendly"<control_label> = <action_label>"string.Resolves both halves to their enum names where one applies:
"_5"/"0"→"system_status = not_busy""_1"/"0"→"trigger = program_status""_3"/"WH"→"node_lifecycle = pending_device_op""_4"/"5"→"system_config = batch_mode_updated""_8"/"AW"→"security_system = armed_away""_20"/"2"→"device_linker = cleared""_0"/"90"→"heartbeat = 90"(action = seconds to the next heartbeat; not enumerated)"_28"/"1.3"→"matter_status = 1.3"(no enum)"_26"/"2"→"system_upgrade = inactive""_24"/"1"→"system_editor = editor_changed""_99"/"x"→"_99 = x"(control we don’t recognise — both halves pass through verbatim)
Intended for the debug logging consumers do over raw event frames (so a line reads
system_status = busyinstead ofsystem_status = 1); not part of any dispatch path. Property- update frames (non-underscore control) aren’t system events — this just echoes them back unchanged if you pass one.
Subpackages¶
- pyisyox.helpers package
- pyisyox.runtime package
DeviceLinkerActionDeviceWriteActionEventEventDispatcherFolderGroupGroup.from_record()Group.addressGroup.nameGroup.nodedef_idGroup.family_idGroup.instance_idGroup.parent_addressGroup.member_addressesGroup.controller_addressesGroup.has_state_targetGroup.has_dimmable_membersGroup.group_all_onGroup.group_any_onGroup.send_command()Group.rename()Group.to_dict()
InternetAccessStatusNetworkResourceNodeNode.from_record()Node.addressNode.nameNode.nodedef_idNode.family_idNode.instance_idNode.typeNode.parent_addressNode.primary_addressNode.enabledNode.propertiesNode.statusNode.nodedefNode.flagNode.has_flag()Node.protocolNode.is_thermostatNode.is_lockNode.is_fanNode.is_dimmableNode.is_battery_nodeNode.zwave_propsNode.send_command()Node.set_climate_mode()Node.set_climate_setpoint_heat()Node.set_climate_setpoint_cool()Node.set_fan_mode()Node.secure_lock()Node.secure_unlock()Node.set_on_level()Node.set_ramp_rate()Node.set_backlight()Node.start_manual_dimming()Node.stop_manual_dimming()Node.rename()Node.to_dict()Node.get_zwave_parameter()Node.set_zwave_parameter()Node.set_zwave_lock_code()Node.delete_zwave_lock_code()Node.set_enabled()
NodeCommandErrorNodeLifecycleActionNodeLifecycleAction.NODE_ADDEDNodeLifecycleAction.NODE_REMOVEDNodeLifecycleAction.NODE_RENAMEDNodeLifecycleAction.NODE_MOVEDNodeLifecycleAction.LINK_CHANGEDNodeLifecycleAction.NODE_REMOVED_FROM_GROUPNodeLifecycleAction.PARENT_CHANGEDNodeLifecycleAction.NODE_ENABLEDNodeLifecycleAction.POWER_INFO_CHANGEDNodeLifecycleAction.DEVICE_ID_CHANGEDNodeLifecycleAction.DEVICE_PROPERTY_CHANGEDNodeLifecycleAction.PENDING_DEVICE_OPNodeLifecycleAction.PROGRAMMING_DEVICENodeLifecycleAction.NODE_REVISEDNodeLifecycleAction.NODE_TYPE_INFO_CHANGEDNodeLifecycleAction.ALL_NODES_ADDEDNodeLifecycleAction.LINK_UPDATEDNodeLifecycleAction.DISCOVERING_NODESNodeLifecycleAction.NODE_DISCOVERY_COMPLETENodeLifecycleAction.NODE_ERRORNodeLifecycleAction.NODE_ERROR_CLEAREDNodeLifecycleAction.FOLDER_ADDEDNodeLifecycleAction.FOLDER_REMOVEDNodeLifecycleAction.FOLDER_RENAMEDNodeLifecycleAction.GROUP_ADDEDNodeLifecycleAction.GROUP_REMOVEDNodeLifecycleAction.GROUP_RENAMEDNodeLifecycleAction.NET_RENAMEDNodeLifecycleAction.label()
NodeLifecycleEventProgramProgramCommandProgramEvalStateProgramFolderProgramRunStateProgramStatusEventProgressActionSecuritySystemActionSecuritySystemAction.DISCONNECTEDSecuritySystemAction.CONNECTEDSecuritySystemAction.DISARMEDSecuritySystemAction.ARMED_AWAYSecuritySystemAction.ARMED_STAYSecuritySystemAction.ARMED_STAY_INSTANTSecuritySystemAction.ARMED_NIGHTSecuritySystemAction.ARMED_NIGHT_INSTANTSecuritySystemAction.ARMED_VACATIONSecuritySystemAction.label()
SystemConfigActionSystemConfigAction.TIME_CHANGEDSystemConfigAction.TIME_CONFIG_CHANGEDSystemConfigAction.NTP_SETTINGS_UPDATEDSystemConfigAction.NOTIFICATIONS_SETTINGS_UPDATEDSystemConfigAction.NTP_COMM_ERRORSystemConfigAction.BATCH_MODE_UPDATEDSystemConfigAction.BATTERY_WRITE_MODE_UPDATEDSystemConfigAction.label()
SystemEditorActionSystemEventControlSystemEventControl.HEARTBEATSystemEventControl.TRIGGERSystemEventControl.DRIVER_SPECIFICSystemEventControl.NODE_LIFECYCLESystemEventControl.SYSTEM_CONFIGSystemEventControl.SYSTEM_STATUSSystemEventControl.INTERNET_ACCESSSystemEventControl.PROGRESSSystemEventControl.SECURITY_SYSTEMSystemEventControl.SYSTEM_ALERTSystemEventControl.OPENADRSystemEventControl.CLIMATESystemEventControl.AMI_SEPSystemEventControl.ENERGY_MONITORINGSystemEventControl.UPB_LINKERSystemEventControl.UPB_DEVICE_ADDERSystemEventControl.UPB_DEVICE_STATUSSystemEventControl.GAS_METERSystemEventControl.ZIGBEESystemEventControl.ELKSystemEventControl.DEVICE_LINKERSystemEventControl.ZWAVESystemEventControl.BILLINGSystemEventControl.PORTALSystemEventControl.SYSTEM_EDITORSystemEventControl.ZMATTER_ZWAVESystemEventControl.SYSTEM_UPGRADESystemEventControl.ZIGBEE_UYBSystemEventControl.MATTER_STATUSSystemEventControl.label()
SystemUpgradeActionTriggerActionVariableVariableTableChangeEventWebSocketEventStreamdescribe_system_event()parse_event_frame()- Submodules
- pyisyox.runtime.events module
SystemEventControlTriggerActionProgressActionSystemConfigActionInternetAccessStatusSecuritySystemActionDeviceLinkerActionSystemUpgradeActionSystemEditorActionDeviceWriteActionNodeLifecycleActionNODE_LIFECYCLE_EVENT_INFO_TAGSDEVICE_WRITE_PROGRESS_EVENT_INFO_TAGSdescribe_system_event()NodeLifecycleEventEventparse_event_frame()ProgramRunStateProgramEvalStateProgramStatusEventVariableTableChangeEventEventDispatcher
- pyisyox.runtime.folder module
- pyisyox.runtime.group module
- pyisyox.runtime.network_resource module
- pyisyox.runtime.node module
- pyisyox.runtime.program module
- pyisyox.runtime.variable module
- pyisyox.runtime.ws module
- pyisyox.runtime.events module
- pyisyox.schema package
CommandCommandParameterEditorEditorCodecErrorEditorRangeFamilyInstanceLinkDefLinkParameterNLSTableNodeCommandsNodeDefNodeLinksNodePropertyProfilePropertyUOMEntryget_uom()- Submodules
Submodules¶
- pyisyox.auth module
- pyisyox.classifier module
- pyisyox.client module
ClientErrorHTTPErrorNodeTypeVariableFieldControllerConfigNodePropertyValueZWavePropertiesNodeRecordGroupRecordFolderRecordProgramRecordProgramRecord.addressProgramRecord.nameProgramRecord.pathProgramRecord.parent_addressProgramRecord.is_folderProgramRecord.statusProgramRecord.enabledProgramRecord.run_at_startupProgramRecord.runningProgramRecord.last_run_timeProgramRecord.last_finish_timeProgramRecord.next_scheduled_run_time
VariableRecordNetworkResourceRecordLoadResultIoXClientIoXClient.connect()IoXClient.load()IoXClient.send_node_command()IoXClient.get_zwave_parameter()IoXClient.set_zwave_parameter()IoXClient.set_zwave_lock_code()IoXClient.delete_zwave_lock_code()IoXClient.set_node_enabled()IoXClient.post_variable_update()IoXClient.create_variable()IoXClient.delete_variable()IoXClient.get_variables_type()IoXClient.run_program_command()IoXClient.run_network_resource()IoXClient.post_node_update()
parse_api_nodes_groups_folders()apply_group_link_targets()parse_api_nodes()parse_rest_status()merge_status_into_nodes()parse_rest_nodes_groups_folders()parse_zwave_nodedefs()parse_rest_networking_resources()parse_api_variables_type()parse_api_programs()
- pyisyox.constants module
EventStreamStatusEventStreamStatus.LOST_CONNECTIONEventStreamStatus.CONNECTEDEventStreamStatus.SYNCINGEventStreamStatus.DISCONNECTEDEventStreamStatus.START_UPDATESEventStreamStatus.STOP_UPDATESEventStreamStatus.INITIALIZINGEventStreamStatus.LOADEDEventStreamStatus.RECONNECT_FAILEDEventStreamStatus.RECONNECTINGEventStreamStatus.DISCONNECTINGEventStreamStatus.NOT_STARTED
TAG_ENABLEDProtocolNodeFamilyEVENT_PROPS_IGNOREDSystemStatusNodeFlagUDHierarchyNodeType
- pyisyox.controller module
ControllerNotConnectedErrorControllerController.connect()Controller.stop()Controller.connectedController.websocketController.base_urlController.configController.nameController.profileController.nodesController.groupsController.foldersController.programsController.program_foldersController.triggersController.variablesController.network_resourcesController.to_dict()Controller.refresh_profile()Controller.add_event_listener()Controller.add_status_listener()Controller.add_node_lifecycle_listener()Controller.add_program_status_listener()Controller.add_variable_table_change_listener()Controller.refresh()Controller.send_program_command()Controller.run_network_resource()Controller.set_variable_value()Controller.set_variable_init()Controller.rename_variable()Controller.create_variable()Controller.refresh_variables()Controller.rename_node()Controller.rename_group()Controller.rename_folder()Controller.feed_event_frame()
- pyisyox.exceptions module
- pyisyox.logging module
- pyisyox.paths module
CONFIG_PATHNODES_PATHGROUPS_PATHPROGRAMS_PATHTRIGGERS_PATHPROFILES_PATHREST_NODES_PATHZWAVE_NODEDEFS_PATHZMATTER_ZWAVE_NODEDEFS_PATHZWAVE_PARAMETER_GET_PATHZWAVE_PARAMETER_SET_PATHZMATTER_ZWAVE_PARAMETER_GET_PATHZMATTER_ZWAVE_PARAMETER_SET_PATHZWAVE_LOCK_CODE_SET_PATHZWAVE_LOCK_CODE_DELETE_PATHZMATTER_ZWAVE_LOCK_CODE_SET_PATHZMATTER_ZWAVE_LOCK_CODE_DELETE_PATHREST_STATUS_PATHNLS_PATHNETWORKING_RESOURCES_PATHSUBSCRIBE_PATHSUBSCRIBE_JSON_PATHVARIABLES_TYPE_PATHVARIABLE_ITEM_PATHNODE_COMMAND_PATHNODE_ITEM_PATHNODE_ENABLE_PATHNODE_DISABLE_PATHPROGRAM_COMMAND_PATHNETWORK_RESOURCE_ITEM_PATH
- pyisyox.redactor module