MQTT Topic Reference¶
Complete topic schema reference for cosalette MQTT topics. For conceptual background — rationale, routing internals, wildcard monitoring — see MQTT Topics (concept).
Topic Schema¶
Every cosalette application uses a flat, Home Assistant-aligned topic
hierarchy. The {prefix} is the application name, and {device} is
the device name registered with @app.device(), @app.command(), or
@app.telemetry(). When the device name is omitted, the device publishes
to root-level topics without a {device} segment.
| Topic Pattern | Direction | QoS | Retain | Description |
|---|---|---|---|---|
{prefix}/{device}/state |
Outbound | 1 | Yes | Device state (JSON) |
{prefix}/{device}/set |
Inbound | — | — | Command input (subscribed) |
{prefix}/{device}/{sub}/set |
Inbound | — | — | Sub-topic command input |
{prefix}/{device}/availability |
Outbound | 1 | Yes | Per-device online/offline (string) |
{prefix}/{device}/error |
Outbound | 1 | No | Per-device error event (JSON) |
{prefix}/error |
Outbound | 1 | No | Global error event (JSON) |
{prefix}/status |
Outbound | 1 | Yes | App heartbeat (JSON) and LWT ("offline") |
Root Device Topics¶
When name is omitted from a decorator, the device publishes at the
root level:
| Topic Pattern | Direction | QoS | Retain | Description |
|---|---|---|---|---|
{prefix}/state |
Outbound | 1 | Yes | Root device state (JSON) |
{prefix}/set |
Inbound | — | — | Root device command input |
{prefix}/availability |
Outbound | 1 | Yes | Root device online/offline |
Root device errors appear only on the global {prefix}/error topic —
there is no per-device error topic since it would be identical.
At most one root device per app. See Device Archetypes for naming rules.
QoS and retain
All framework-managed publishes use QoS 1 (at-least-once) — this is
hard-coded and not configurable. For custom channels that need QoS 0,
use ctx.publish(channel, payload, qos=0). Retain defaults match the
table above; publish_state() accepts an optional retain keyword
argument to override per-call.
Topic Prefix¶
The {prefix} placeholder resolves at runtime from two sources,
checked in order:
Settings.mqtt.topic_prefix— when set (e.g. viaMQTT__TOPIC_PREFIX=staging-velux), this value is used as{prefix}.App(name="velux2mqtt")— whentopic_prefixis empty (the default), thenameargument is used.
This lets you deploy the same binary with different topic namespaces (e.g. staging vs production) by setting an environment variable.
For example, App(name="velux2mqtt") with a device "blind" produces:
velux2mqtt/blind/state
velux2mqtt/blind/set
velux2mqtt/blind/availability
velux2mqtt/error
velux2mqtt/status
See Settings Reference for all MQTT configuration options.
State Topics¶
Published by ctx.publish_state() in device functions.
- Topic:
{prefix}/{device}/state - QoS: 1 — at-least-once delivery
- Retain: Yes — subscribers receive the last-known state immediately
- Payload: JSON dict (user-defined structure)
Command Topics¶
Inbound topics the framework subscribes to for command & control devices.
- Topic:
{prefix}/{device}/set - Direction: Inbound — the broker delivers messages to the framework
- Payload: Plain string, decoded by the user's command handler
The TopicRouter subscribes to {prefix}/{device}/set and
{prefix}/{device}/+/set for each device that has a registered command
handler (via @app.command(), @ctx.on_command, or ctx.commands() inside
an @app.device() function). The +/set wildcard captures sub-topic
commands. Telemetry-only devices are not subscribed.
Routing behaviour¶
The router extracts the device name and optional sub-topic by simple string parsing — MQTT wildcards are used for subscription, not for parsing:
- Topics that do not match
{prefix}/{device}/set,{prefix}/{device}/{sub}/set, or{prefix}/set(root devices) are silently ignored. - Messages for a device with no handler produce a WARNING log entry.
Availability Topics¶
Published automatically by the HealthReporter at device startup and
during graceful shutdown.
- Topic:
{prefix}/{device}/availability - QoS: 1
- Retain: Yes
- Payload:
"online"or"offline"(plain string, not JSON)
Compatible with Home Assistant's MQTT availability schema.
Error Topics¶
Published by the ErrorPublisher service. Every error is published to
the global topic; when a device name is known, a copy goes to the
per-device topic as well.
- Global topic:
{prefix}/error - Per-device topic:
{prefix}/{device}/error - QoS: 1
- Retain: No — errors are events, not state
- Payload: JSON (
ErrorPayloadschema)
velux2mqtt/error → {"error_type": "error", "message": "...", ...}
velux2mqtt/blind/error → {"error_type": "invalid_command", "message": "...", ...}
See Payload Schemas for the full ErrorPayload structure.
App Status and LWT¶
The {prefix}/status topic serves two roles on the same topic:
Last Will and Testament (LWT)¶
When the MQTT client connects, the framework registers a Last Will and
Testament via build_will_config():
| Property | Value |
|---|---|
| Topic | {prefix}/status |
| Payload | "offline" |
| QoS | 1 |
| Retain | Yes |
If the client disconnects unexpectedly (crash, network loss), the broker
publishes "offline" to {prefix}/status on the application's behalf.
During graceful shutdown, the HealthReporter publishes "offline"
explicitly.
Heartbeat¶
The HealthReporter publishes a structured JSON heartbeat to the
same {prefix}/status topic. An initial heartbeat is published
immediately on connect (overwriting the LWT "offline"), then
periodically at the configured heartbeat_interval (default 60 s,
set to None to disable):
{
"status": "online",
"uptime_s": 3600.0,
"version": "0.3.0",
"devices": {
"blind": {"status": "ok"},
"temp": {"status": "ok"}
}
}
- QoS: 1
- Retain: Yes
Consumers can distinguish LWT from heartbeat by attempting JSON parse:
the LWT payload is a plain string "offline", while heartbeats are
valid JSON objects.
See Payload Schemas for the full HeartbeatPayload and
DeviceStatus structures.
Wildcards¶
The framework uses explicit per-device subscriptions, not MQTT wildcards. However, external consumers (monitoring tools, Home Assistant) can use wildcards for fleet-level monitoring:
| Pattern | Use case |
|---|---|
+/status |
Monitor all apps in a fleet |
velux2mqtt/+/state |
All device states in one app |
+/error |
Global errors across all apps |
velux2mqtt/+/availability |
Per-device availability in one app |
velux2mqtt/blind/+/set |
All sub-topic commands for one device |
See Also¶
- MQTT Topics (concept) — rationale, routing internals, retained vs not-retained reasoning
- Payload Schemas — JSON payload structures
- Error Handling (concept) — error semantics
- Health & Availability (concept) — heartbeat and LWT details
- Settings Reference —
MQTT__TOPIC_PREFIXand other MQTT settings - ADR-002 — MQTT Topic Conventions
- ADR-025 — Command Channel and Sub-Topic Routing