HOUSE OF THINGSDocs

Core concepts

Five nouns carry the entire model. Learn these and the API, the topic grammar and the UI all stop being surprising.

Entity What it is Identity
Space The tenant container — owns devices, members and billing. Also the shard key. 11-char base62
Device A physical or virtual node exposing one or more endpoints. (mac, endpoint_id)
Endpoint One independent control surface on a device. endpoint_id, 0..255
Hub ESP-NOW ⇄ MQTT bridge, and the local-autonomy tier. MAC
View A shareable surface composing endpoints across several devices. 11-char base62

Space

The namespace root for everything. Devices, members, schedules, history and billing all hang off a space, and space_id is the shard key — it is identical in the database, in MQTT topics and in the streaming layer, on purpose.

One space is the normal case

A home is one space. A restaurant chain is one space per venue. Nothing in the platform assumes a user has more than one, and nothing breaks if they have fifty.

Device and endpoint

A device is a physical node. An endpoint is one independent thing on it. Identity is the pair:

(mac, endpoint_id)

mac is 12 lowercase hex characters, no colons. endpoint_id is a uint8; endpoint 0 is the default and is what every single-function device uses implicitly.

Why the pair, and not just the MAC

One ESP8266 can legitimately be several things — a relay on D3, a blind pair on D5/D6, and a BME680 on D1/D2. There is one MAC on that chip and three unrelated control surfaces behind it. Collapsing them into one “device” with a single type means the UI can only render one category of card, and labels, schedules and automations get forced onto the whole chip.

Every mature spec resolves this the same way:

Spec “Node” “Entity”
Matter Node Endpoint (with Clusters)
Zigbee Device Endpoint
HomeKit / HAP Accessory Service
Home Assistant Device Entity
ESPHome Device Component

Two endpoints on one MAC are fully independent for every user-facing purpose. They just happen to share a radio, a firmware image and a power supply.

Endpoint IDs are local to the MAC

There is no global endpoint namespace. Endpoint 1 on two different devices are unrelated. For anything printed or shared, use the endpoint’s short_id instead.

Capabilities

A device declares what each endpoint can do in its announce payload. The UI is built from that declaration rather than from a hardcoded per-type table, which is why a new device type needs no client release:

announce (excerpt)
{
  "mac": "08d1f9fa6288",
  "endpoints": [
    {
      "endpoint": 1,
      "type": "sensor",
      "variant": "bme680",
      "channels": ["temperature", "humidity", "pressure", "gas"]
    },
    {
      "endpoint": 4,
      "type": "switch",
      "params": [
        { "name": "switch", "kind": "enum", "values": ["on", "off"] }
      ]
    }
  ]
}

A switch is a channel, not a param

This one has bitten more than once: a relay modelled as a capability param rather than as a switch channel renders on some clients and is invisible on others. Follow the channel model for anything with a state the device reports back.

Ownership tiers

What a device may do at all depends on where it is rooted:

Tier Root Capability
unowned none mute — no broker access
user-attached under the user announce, state, commands, config. No history.
space-assigned under the space the full set

Detaching a device preserves its identity and history. A change of hands wipes and re-mints it — the new owner inherits nothing.

Hub

The hub bridges ESP-NOW devices onto MQTT, and runs cross-device automation when the server is unreachable. It is space-bound.

Hub expansion is structurally mandatory, not an optimisation: an ESP-NOW device cannot receive a space-wide broadcast, so a single “everything off” has to become N frames somewhere, and the hub is the only place with the peer list.

uplink up

no uplink

uplink lost

uplink restored

Booting

Normal\nESP-NOW ⇄ MQTT\nlocal automation

Rescue mode\nlocal control only\nno history, no push

What survives in rescue mode

Local control and hub-resident automation. Not history, not push notifications, not sharing — those are server-tier by nature.

View

A view is a shareable control surface that composes several endpoints across several devices and declares its own capability set.

The motivating case: heating is a temperature sensor on one device plus a heater on another. Neither endpoint alone is “the heating”. A view is that one thing, and it can be shared — including to anonymous users via an access code — with a capability set narrower than the underlying endpoints.

Views are outside the broker-authorization problem

A view holds no broker credential. Its users go through REST and WebSocket only; the backend evaluates, approves, then publishes. So per-device credentials and broker ACLs simply do not constrain views, and the audit trail comes for free.

View permissions are a separate concern from device and endpoint permissions. A grant on a view says nothing about direct access to the endpoints inside it.

How they fit together

owns

owns

contains

grants role to

exposes

composes

bridges

SPACE

DEVICE

HUB

VIEW

MEMBER

ENDPOINT

Read VIEW }o--o{ ENDPOINT carefully — it is many-to-many. Several views may cover the same endpoints with different capabilities: one that operates, one that is read-only and safe to put on a wall.