Rooms and devices

HomeCmdr maintains an in-memory registry of devices and rooms. Devices come from adapters; rooms are user-defined.


Devices

A device is the atomic unit of state in HomeCmdr. Each device has:

FieldDescription
idStable string like elgato_lights:light:0 or roku_tv:tv
kindDevice category (e.g. Light, Switch, Sensor)
attributesCurrent state, keyed by capability name
room_idAssigned room, or null if unassigned
metadataAdapter-specific descriptive data
updated_atWhen meaningful state last changed
last_seenWhen the adapter last observed the device

Device IDs are namespaced by adapter — {adapter_name}:{vendor_id}. They are stable across restarts.

List all devices

curl -H "Authorization: Bearer $TOKEN" http://localhost:3001/devices

Get one device

curl -H "Authorization: Bearer $TOKEN" http://localhost:3001/devices/roku_tv:tv

Get specific devices by ID

curl -H "Authorization: Bearer $TOKEN" \
  "http://localhost:3001/devices?ids=open_meteo:temperature_outdoor&ids=open_meteo:wind_speed"

Sending commands

Commands use a canonical shape across all adapters:

{
  "capability": "power",
  "action": "on"
}

For capabilities with a value:

{
  "capability": "brightness",
  "action": "set",
  "value": 75
}

Send a command to one device

curl -X POST http://localhost:3001/devices/elgato_lights:light:0/command \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"capability":"power","action":"toggle"}'

Set brightness

curl -X POST http://localhost:3001/devices/elgato_lights:light:0/command \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"capability":"brightness","action":"set","value":80}'

Set colour temperature

curl -X POST http://localhost:3001/devices/elgato_lights:light:0/command \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"capability":"color_temperature","action":"set","value":{"value":4000,"unit":"kelvin"}}'

See the full API reference for all command shapes and response codes.


Rooms

Rooms are user-defined. Devices start with no room assignment.

Create a room

curl -X POST http://localhost:3001/rooms \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"id":"living_room","name":"Living Room"}'

List rooms

curl -H "Authorization: Bearer $TOKEN" http://localhost:3001/rooms

Get one room

curl -H "Authorization: Bearer $TOKEN" http://localhost:3001/rooms/living_room

Get devices in a room

curl -H "Authorization: Bearer $TOKEN" http://localhost:3001/rooms/living_room/devices

Assigning devices to rooms

Room assignment is independent of adapter refreshes — polling a device does not overwrite its room.

Assign a device

curl -X POST http://localhost:3001/devices/roku_tv:tv/room \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"room_id":"living_room"}'

Clear room assignment

curl -X POST http://localhost:3001/devices/roku_tv:tv/room \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"room_id":null}'

Sending commands to a whole room

Fan out one command to every device assigned to a room:

curl -X POST http://localhost:3001/rooms/living_room/command \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"capability":"power","action":"off"}'

Response includes a per-device result for each device:

[
  { "device_id": "roku_tv:tv", "status": "ok", "message": null },
  { "device_id": "open_meteo:wind_speed", "status": "unsupported", "message": "device commands are not implemented" }
]

Devices that do not support the capability return "unsupported" — the command still runs on any that do.


Groups

Groups are explicit user-defined device collections. Unlike rooms (which organise by location), groups are useful for functional groupings — all the lights in a lighting rig, all the speakers in a multi-room audio setup, etc.

Create a group

curl -X POST http://localhost:3001/groups \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"id":"bedroom_lamps","name":"Bedroom Lamps","members":["zigbee2mqtt:bedside_left","zigbee2mqtt:bedside_right"]}'

Command a whole group

curl -X POST http://localhost:3001/groups/bedroom_lamps/command \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"capability":"power","action":"off"}'

Groups can also be targeted from Lua scenes and automations using ctx:command_group(group_id, command_table).


Live event stream

Connect to the WebSocket endpoint to receive device state changes in real time:

wscat -c "ws://localhost:3001/events?token=$TOKEN"

Example events:

{ "type": "device.state_changed", "id": "roku_tv:tv", "state": { "power": "off" } }
{ "type": "device.room_changed", "id": "elgato_lights:light:0", "room_id": "office" }
{ "type": "adapter.started", "adapter": "zigbee2mqtt" }

Persistence

Device and room state is persisted to SQLite (or PostgreSQL) automatically. When the server restarts, devices and their room assignments are restored from storage before adapters begin polling.

History is stored too — use /devices/{id} to see updated_at and last_seen timestamps, or explore the database directly for full attribute history and command audit logs.