pyisyox.runtime.group module

Runtime Group — IoX scenes (a.k.a. controller-side groups).

A group represents a controller-managed collection of nodes — the IoX “scene” concept. Sending a command to the group’s address is the same wire shape as sending to a node (GET /rest/nodes/{addr}/cmd/{cmd}); the controller broadcasts to every member.

Group commands are sent raw without editor-codec validation: the nodeDefId attribute on a <group> element (typically "InsteonDimmer") is a scene-class label, not a profile-level nodedef, so there’s no editor table to validate parameters against. The well-known group commands are documented in the IoX REST guide: DON (with optional level), DOF, DFON / DFOF (fast on/ off), BRT / DIM (manual brighten/dim). Pre-encode any parameters as integers — the same shape pyisyox.runtime.Node produces internally.

Groups don’t carry live property state of their own — there’s no properties dict — because the wire-level group has no observable attribute beyond its membership. State events flow through individual member nodes.

Sourced from <group flag="132"> elements in the legacy /rest/nodes XML. flag="12" (the special “ISY” group representing the controller itself) is filtered out at parse time.

class Group(record, profile, client, nodes=None)[source]

Bases: object

User-facing handle for one group / scene in the controller.

Parameters:
classmethod from_record(record, profile, client, nodes=None)[source]

Construct a Group from a parsed record.

Pass nodes (the controller’s loaded.nodes dict) to enable the group_all_on derived property. Without it the group is purely command-issuing.

Parameters:
Return type:

Group

property address: str

Group address — usually a 5-digit integer string or "ADR####" for special groups like ~zAuto DR.

property name: str

User-assigned scene name.

property nodedef_id: str

Scene-class label ("InsteonDimmer" etc.). Not a real profile nodedef — see module docstring.

property family_id: str

Family id — usually "6" (Insteon group family).

property instance_id: str

Instance id within the family.

property parent_address: str | None

Address of the parent folder, or None if at the top level.

property member_addresses: tuple[str, ...]

Addresses of the nodes that belong to this group.

Sourced from the <members> element in /rest/nodes XML. Order matches the controller’s declaration order. Includes both controllers and responders; use controller_addresses for the controller subset.

property controller_addresses: tuple[str, ...]

Subset of member_addresses that 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.

True when link targets resolved and at least one member has an on/off intent. A resolved scene with only fire-only / config links (cmd BL/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 assume True (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 — unlike has_state_target — does not depend on /api/groups link resolution (works on older firmware too). Consumers pair it with has_state_target to pick the scene’s HA platform: no state target → button; else dimmable members → on/off light (preserving light semantics + the group/more-info framework, no switch_as_x); else → switch. Scenes have no settable brightness — fade/brt/dim are separate manual commands — so “light” here is on/off only.

Returns False without a node-registry reference. Members missing from the registry are skipped (defensive). Memoised on first access (one Node + find_nodedef per 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; their ST isn’t a persistent state.

When /api/groups link 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. Returns False when 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’s ST is missing or zero.

Cheap: O(N), computed on read — the underlying ST values 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 their is_on. When /api/groups link 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 (what pyisy.Group.status did). Stateless members and members not in the registry are skipped.

Returns False with no node-registry reference, an empty (on-target / member) set, or when every counted member’s ST is 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 level

  • await group.send_command("DON", 75) — explicit on-level

  • await group.send_command("DOF")

  • await group.send_command("DFON") / "DFOF" — fast

  • await group.send_command("BRT") / "DIM" — manual brighten/dim step

Parameters:
  • command_id (str)

  • params (int)

Return type:

None

async rename(name)[source]

Rename this group / scene.

Wire shape: POST /api/nodes/{address} with {"name": "<str>", "nodeType": "group"}. The nodeType field is required by the server even though the address already disambiguates — without it the call is rejected.

Parameters:

name (str)

Return type:

None

to_dict()[source]

Flatten this scene to a JSON-compatible dict.

Adds the live aggregate flags (group_all_on / group_any_on) on top of the structural record so a snapshot reflects whether the scene is currently active.

Return type:

dict[str, Any]