Skip to content

How the platform knows a device is alive

online, not_heard_from, and offline are not stored states in ALYT. The platform derives them from lastSeen at read time, so what a developer sees always reflects the most recent accepted message. lastSeen is the only written fact here: it means the platform heard from the device at a particular time. This matters because the house keeps working when the cloud is down, and the console can only show what the platform has heard, not what it guesses.

Liveness comes from lastSeen, not from a separate status job. That means there is no third stored state that can drift between background runs. When a device sends an accepted message, lastSeen advances. When nothing arrives, the read-time view moves through the derived states according to the platform’s grace window.

For a developer, the important rule is simple: do not treat online as a persisted flag. Treat it as a view over lastSeen.

Every update reaches lastSeen through the broker path:

PathResult
device → MQTT → mosquittomqtt-bridgeDevice.lastSeen, Device.status are updated

IngestService advances lastSeen on every accepted message. That is the only writer. If a message does not go through that accepted ingest path, it does not move the liveness clock.

alyt-hub-1 does not speak MQTT. Its firmware uses the ESPHome native API to alyt-voice-orchestrator and nothing else. Because of that, the platform cannot hear from the hub directly through MQTT, and a household view can otherwise show a working assistant as provisioning with lastSeen equal to never.

The home agent relays liveness on the hub’s behalf. It holds the property’s outbound connection and publishes the measurement so the platform can hear about the hub even though the hub has no MQTT client.

orchestrator probes the board every 15 s (device_info(), 5 s timeout)
│ connected = the ping ANSWERED, not a cached flag
voice-api, every 60 s: GET /links, map device_id -> platform_device_id (by serial)
home-agent: POST /liveness { deviceId, online } (internal, LAN-only)
MQTT `availability` on THE DEVICE'S OWN topic, over the agent's existing link
mqtt-bridge ingests it exactly like any device's own message -> lastSeen, status
  • Only probed state is relayed. The connected value comes from a ping that answered, not from a cached flag.
  • A quiet device is not reported offline. Nothing publishes offline; the relay stops, and the platform’s grace window decides the visible state.
  • The voice services never learn about tenancy. The relay body carries a device id and a boolean. The agent fills tenant and property from its own enrolment.
  • A failure is not an outage. If the uplink is down, the house keeps answering and the platform view goes stale. The relay returns {relayed:false}, not an error.
  • A local-only device is skipped. If there is no platform_device_id, the platform does not know the device and does not adopt it by guesswork.

The agent publishes on the hub’s topic, which is not something a normal device may do. That works because the agent has an explicit carve-out at the property level.

CredentialACL
Hardwarealyt/{tenantId}/+/{deviceId}/# — its own topics only
AGENTalyt/{tenantId}/{propertyId}/# — the whole property, on purpose

This carve-out is the relay. If the agent were narrowed to its own device id, the relay would stop working even if broker-mocking tests still passed. In that case the hub would drift back to never in the console after a few minutes. That is why the ACL pattern for each kind matters.

What happens when a device gains MQTT support

Section titled “What happens when a device gains MQTT support”

The relay is only for hardware that cannot speak to the broker. If a future hub has its own MQTT client, it reports for itself and the relayed path stops being needed. Nothing else changes: lastSeen still has one writer, and liveness is still derived from it at read time.

The hub relay depends on the agent’s own property-level publishing ability. A household view sees less than staff, so developers should expect liveness data to appear in customer-facing surfaces only as the derived state the platform allows, not as raw internal probe details.

A DEV key acts in a sandbox, and a LIVE key only for its own connector. That boundary applies to liveness-related reads and writes as it does elsewhere on the platform.

Last updated