Skip to content

Devices, units and capabilities

A device in ALYT is the logical thing the household interacts with, not the physical board that happens to be serving it today. That split lets a device keep its room, settings, and history even when hardware changes, and it also lets one physical product grow from a voice satellite into a hub without forcing a new device type. For developers, the important rule is that device behavior is described by capabilities, settings are grouped by capability, and child devices acquired through a hub are represented as devices in their own right.

Device is the logical device. DeviceUnit is the physical board currently serving as that device. The burned-in serial lives on DeviceUnit.serial, not on Device. That means a warranty replacement can become a new unit for the same logical device instead of becoming a new household object.

Identity for a child device acquired over Zigbee, Matter, or Thread does not use an ALYT serial. It uses Device.protocol and Device.protocolId, together with the hub that owns it. This is the same stable-id rule used for other provider-backed endpoints.

A device’s role is a set of capabilities, not a single kind. The current capability set is Device.capabilities: DeviceCapability[], with values such as VOICE, HUB, and SENSOR. A device can carry more than one capability at the same time, so a satellite that also becomes a hub is represented as both.

The device page is driven by the capability list. A client renders one section per capability the device reports rather than branching on a fixed device type. This leaves room for new capability sections later without changing the page structure.

DeviceSettings.byCapability stores settings as one JSON document per capability. A capability-specific patch must name a capability the device reports. If it names a capability the device does not report, the API returns a 400 rather than ignoring the change.

The same grouping applies to the page UI. Audio settings belong under voice, while radio and join-related settings belong under hub. The page should keep this seam even when only one capability is present today.

A hub owns child devices. A Zigbee or Matter child is a Device whose parentDeviceId points to the hub, in the same Room model, under the same tenancy. The hub relationship is not a separate table or a special one-off case in the room model. Room moves, co-membership pairing, and tenant scoping must work the same way for a hub-owned bulb as for any other device.

The model enforces that a child device under a hub is unique by @@unique([parentDeviceId, protocolId]), and hub deletion cascades to its child devices.

Provisioning and acquisition are different flows

Section titled “Provisioning and acquisition are different flows”

Provisioning an ALYT device is the normal onboarding flow: burned-in serial, pairing token minted by a user, credentials handed over BLE or Improv, then POST /devices/register.

Acquiring a child device through Zigbee or Matter is different. The hub is already trusted, so the household puts the hub into permit-join and the child arrives through that hub. The UI must keep those flows separate. One action is to add an ALYT device, and the other is to add something to an existing hub.

When you build device UIs or write code against DeviceSettings, keep the capability boundary visible. Do not hardcode the page around a single device kind such as VOICE_SATELLITE. Render sections from the capability list, and keep settings keyed by capability.

For child devices, treat the owning hub as part of the identity. For logical devices with no current unit, keep the row meaningful: a Device without a DeviceUnit is valid.

LAN devices such as a WiZ bulb, a Tuya switch, a Cync dimmer, or an ecobee are not modeled as Device rows in this design. They are Endpoint rows, keyed by a provider’s stable id and described by Endpoint.capabilitySet. Their secrets live in Integration.config. A child acquired by a hub over a radio still uses Device.parentDeviceId; that keeps the two meanings separate.

Switches can be presented differently by the household

Section titled “Switches can be presented differently by the household”

A wall switch may drive lights in one household and a fan in another, so the household chooses how it is presented. Endpoint.presentedAs stores light, switch, or null for “as described”, and PATCH /endpoints/:id sets it. That choice changes the category, icon, and which automation targets include the thing. A thing can also be chosen when its only power comes from a switch, or when it is a light with neither dimming nor colour through Thing.presentable.

  • Device.capabilities is a set, not a single kind.
  • DeviceSettings.byCapability must reject a patch for a capability the device does not report.
  • A hub-owned child uses Device.parentDeviceId, Device.protocol, and Device.protocolId for identity.
  • DeviceUnit.serial is the burned-in serial; Device is the logical device.
  • POST /devices/register is the provisioning path for ALYT devices, not for hub-acquired children.
  • LAN devices live as Endpoint rows, not as hub children.
Last updated