From adec979182021027cf34cf0da572f4ae82f8dbf9 Mon Sep 17 00:00:00 2001
From: bonamin
Date: Tue, 29 Sep 2026 20:10:03 +0300
Subject: [PATCH] docs(api-reference): document the "stopping" playback state
(firmware F-065)
Co-Authored-By: Claude Opus 5.5
---
frontend/src/pages/engineering/developer/ApiReferencePage.jsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/frontend/src/pages/engineering/developer/ApiReferencePage.jsx b/frontend/src/pages/engineering/developer/ApiReferencePage.jsx
index 9a38e69..c22ca2a 100644
--- a/frontend/src/pages/engineering/developer/ApiReferencePage.jsx
+++ b/frontend/src/pages/engineering/developer/ApiReferencePage.jsx
@@ -19,7 +19,7 @@ const TRANSPORTS_V2 = [
{ transport: 'MQTT', address: 'vesper/{device_id}/control/ack', direction: 'Outbound, QoS 1', switch: 'command', notes: 'Strictly replies to commands (incl. pong) — nothing unsolicited is ever published here. Echoes req_id when the request sent one.' },
{ transport: 'MQTT', address: 'vesper/{device_id}/control/reports', direction: 'Outbound, QoS 1', switch: 'reports', notes: 'Critical, unsolicited, time-sensitive board-initiated events (e.g. bell_overload). Not retained.' },
{ transport: 'MQTT', address: 'vesper/{device_id}/status/heartbeat', direction: 'Outbound, retained, QoS 1', switch: 'heartbeat', notes: 'Every 30 s. Also carries LWT — broker publishes {"state":"offline","ok":false} on an unclean disconnect.' },
- { transport: 'MQTT', address: 'vesper/{device_id}/status/playback', direction: 'Outbound, retained, QoS 1', switch: 'playback', notes: 'Playback state transitions (playing/paused/idle) with pid, speed, duration, started_at, source, ts. Published on transition, not polled. Source of truth for what the board is doing.' },
+ { transport: 'MQTT', address: 'vesper/{device_id}/status/playback', direction: 'Outbound, retained, QoS 1', switch: 'playback', notes: 'Playback state transitions (playing/paused/stopping/idle) with pid, speed, duration, started_at, source, ts. Published on transition, not polled. Source of truth for what the board is doing.' },
{ transport: 'MQTT', address: 'vesper/{device_id}/system/alerts', direction: 'Outbound, retained until cleared, QoS 1', switch: 'alerts', notes: 'Subsystem state changes (WARNING / CRITICAL / FAILED / CLEARED). Published on transition only.' },
{ transport: 'MQTT', address: 'vesper/{device_id}/system/info', direction: 'Outbound, retained, QoS 1', switch: 'info', notes: 'Discrete system events, e.g. boot_report on every boot.' },
{ transport: 'MQTT', address: 'vesper/{device_id}/system/logs', direction: 'Outbound, QoS 0', switch: 'logs', notes: 'Full log stream, mirrors serial output. Also gated by the log.set_mqtt level, independently of this switch.' },
@@ -1858,7 +1858,7 @@ function TransportsV2() {
{[
- { field: 'action', type: 'string', notes: '"playing" | "paused" | "idle". Published on transition, not polled.' },
+ { field: 'action', type: 'string', notes: '"playing" | "paused" | "stopping" | "idle". paused = between interval segments (still active). stopping = duration reached, bells finishing the current melody loop (up to one loop long), then idle. playback.stop always goes straight to idle. Published on transition, not polled.' },
{ field: 'time_elapsed', type: 'int', notes: 'Seconds since playback started, as of ts (0 for "idle").' },
{ field: 'projected_run_time', type: 'int', notes: 'Projected total run time in ms (0 if not applicable).' },
{ field: 'pid', type: 'string', notes: 'Melody being played. "" when idle.' },