docs(ApiReferencePage): full v2 API reference update

Fixes and additions across all namespaces to accurately reflect firmware v2.1.0:

Fixes:
- system.get_settings → system.get_config (backward compat alias noted)
- network.status returns now includes ssid, mac, hostname
- clock.set_alerts: correct alert_type values (OFF|SINGLE|HOURS, not none|hour|quarter)
- ota: removed non-existent ota.status and ota.check commands
- telemetry.get_loads returns now includes max_loads and guard_enabled

Additions:
- relay.test_bell and relay.test_batch (F-038)
- clock.test_c1 and clock.test_c2 (F-039)
- system.get_config, system.get_all, system.get_telemetry (F-035)
- telemetry.set_max_loads, telemetry.set_guard_enabled, telemetry.get_metrics, telemetry.reset_metrics (F-030, F-031, F-034)
- log.get_config and mqtt.get_config (F-036)
- ota.set_channel
- bells namespace: bells.enable, bells.disable, bells.get_config (F-029, F-036)
- UART whitelist updated for new commands
- Legacy map updated: get_full_settings → system.get_config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 19:55:08 +03:00
co-authored by Claude Sonnet 4.6
parent abf3c3825b
commit 3394716f8f
@@ -23,7 +23,14 @@ const TRANSPORTS = [
{ transport: 'UDP', address: 'Port 32101', direction: 'Discovery', notes: 'Passive — device announces itself' }, { transport: 'UDP', address: 'Port 32101', direction: 'Discovery', notes: 'Passive — device announces itself' },
] ]
const UART_WHITELIST = ['ping', 'playback.play', 'playback.stop', 'clock.pause', 'clock.resume', 'system.get_time'] const UART_WHITELIST = [
'ping', 'identify',
'playback.play', 'playback.stop',
'relay.test_bell', 'relay.test_batch', 'relay.test_output',
'clock.pause', 'clock.resume', 'clock.test_c1', 'clock.test_c2',
'system.get_time', 'system.status', 'system.get_config', 'system.get_telemetry',
'bells.enable', 'bells.disable',
]
const NAMESPACES = [ const NAMESPACES = [
{ {
@@ -81,7 +88,43 @@ const NAMESPACES = [
{ cmd: 'relay.set_durations', handler: 'RelayHandler', transports: ['All'], description: 'Set strike duration (ms) for one or more bell channels. Partial updates accepted — omitted channels are unchanged.', contents: [{ field: 'bells', type: 'object', required: true, notes: 'Map of channel index → duration ms. e.g. { "0": 95, "2": 110 }. At least one entry required.' }], returns: null, example: '{\n "v": 2,\n "cmd": "relay.set_durations",\n "contents": { "bells": { "0": 95, "1": 100, "2": 110 } }\n}', warning: null }, { cmd: 'relay.set_durations', handler: 'RelayHandler', transports: ['All'], description: 'Set strike duration (ms) for one or more bell channels. Partial updates accepted — omitted channels are unchanged.', contents: [{ field: 'bells', type: 'object', required: true, notes: 'Map of channel index → duration ms. e.g. { "0": 95, "2": 110 }. At least one entry required.' }], returns: null, example: '{\n "v": 2,\n "cmd": "relay.set_durations",\n "contents": { "bells": { "0": 95, "1": 100, "2": 110 } }\n}', warning: null },
{ cmd: 'relay.set_outputs', handler: 'RelayHandler', transports: ['All'], description: 'Map bell channels to physical relay outputs. Partial updates accepted.', contents: [{ field: 'bells', type: 'object', required: true, notes: 'Map of channel index → relay output index. e.g. { "0": 2, "1": 3 }' }], returns: null, example: '{\n "v": 2,\n "cmd": "relay.set_outputs",\n "contents": { "bells": { "0": 2, "1": 3, "2": 4 } }\n}', warning: null }, { cmd: 'relay.set_outputs', handler: 'RelayHandler', transports: ['All'], description: 'Map bell channels to physical relay outputs. Partial updates accepted.', contents: [{ field: 'bells', type: 'object', required: true, notes: 'Map of channel index → relay output index. e.g. { "0": 2, "1": 3 }' }], returns: null, example: '{\n "v": 2,\n "cmd": "relay.set_outputs",\n "contents": { "bells": { "0": 2, "1": 3, "2": 4 } }\n}', warning: null },
{ cmd: 'relay.get_config', handler: 'RelayHandler', transports: ['All'], description: 'Read current bell durations and relay output assignments.', contents: null, returns: '{ "durations": { "0": 95, ... }, "outputs": { "0": 2, ... } }', example: '{ "v": 2, "cmd": "relay.get_config" }', warning: null }, { cmd: 'relay.get_config', handler: 'RelayHandler', transports: ['All'], description: 'Read current bell durations and relay output assignments.', contents: null, returns: '{ "durations": { "0": 95, ... }, "outputs": { "0": 2, ... } }', example: '{ "v": 2, "cmd": "relay.get_config" }', warning: null },
{ cmd: 'relay.test_output', handler: 'RelayHandler', transports: ['All'], description: 'Fire a specific relay for a caller-specified duration. Safety-gated: Player must be stopped.', contents: [{ field: 'output', type: 'int', required: true, notes: 'Physical relay output index' }, { field: 'duration_ms', type: 'int', required: true, notes: 'Duration in ms. No defaults. Typical range: 85–140 ms' }], returns: null, example: '{\n "v": 2,\n "cmd": "relay.test_output",\n "contents": { "output": 2, "duration_ms": 95 }\n}', warning: null }, {
cmd: 'relay.test_output',
handler: 'RelayHandler',
transports: ['All'],
description: 'Fire a raw relay output for a caller-specified duration. Bypasses bell assignments — fires the physical relay directly. Player must be stopped.',
contents: [
{ field: 'output', type: 'int', required: true, notes: 'Physical relay output index (0–31)' },
{ field: 'duration_ms', type: 'int', required: true, notes: 'Duration in ms (1–5000). No default.' },
],
returns: null,
example: '{\n "v": 2,\n "cmd": "relay.test_output",\n "contents": { "output": 2, "duration_ms": 95 }\n}',
warning: null,
},
{
cmd: 'relay.test_bell',
handler: 'RelayHandler',
transports: ['All'],
description: 'Fire bell channel N using its configured output and duration. Non-blocking. Can be sent repeatedly in quick succession like a piano key. Bypasses bellsEnabled guard — test commands always fire.',
contents: [
{ field: 'bell', type: 'int', required: true, notes: 'Bell channel index (0–15, 0-based). Firmware looks up the configured output and duration automatically.' },
],
returns: '{ "bell": 0, "output": 2, "duration_ms": 95 }',
example: '// Fire bell 0\n{ "v": 2, "cmd": "relay.test_bell", "contents": { "bell": 0 } }\n\n// Rapid succession — fire bell 0, then bell 1 immediately\n{ "v": 2, "cmd": "relay.test_bell", "contents": { "bell": 0 } }\n{ "v": 2, "cmd": "relay.test_bell", "contents": { "bell": 1 } }',
warning: null,
},
{
cmd: 'relay.test_batch',
handler: 'RelayHandler',
transports: ['All'],
description: 'Fire a list of bell channels simultaneously, like a single melody step. Each bell fires for its own configured duration. Non-blocking. Bypasses bellsEnabled guard.',
contents: [
{ field: 'bells', type: 'int[]', required: true, notes: 'Array of bell channel indices (0–15, 0-based). All fire simultaneously.' },
],
returns: '{ "fired": 3 }',
example: '// Fire bells 0, 2, and 4 at the same time\n{\n "v": 2,\n "cmd": "relay.test_batch",\n "contents": { "bells": [0, 2, 4] }\n}',
warning: null,
},
], ],
}, },
{ {
@@ -91,7 +134,7 @@ const NAMESPACES = [
commands: [ commands: [
{ cmd: 'clock.set_outputs', handler: 'ClockHandler', transports: ['All'], description: 'Set which relay outputs the clock strikes use. Partial updates accepted.', contents: [{ field: 'c1', type: 'int', required: false, notes: 'Relay output for the first clock channel' }, { field: 'c2', type: 'int', required: false, notes: 'Relay output for the second clock channel. At least one of c1/c2 required.' }], returns: null, example: '{ "v": 2, "cmd": "clock.set_outputs", "contents": { "c1": 4, "c2": 5 } }', warning: null }, { cmd: 'clock.set_outputs', handler: 'ClockHandler', transports: ['All'], description: 'Set which relay outputs the clock strikes use. Partial updates accepted.', contents: [{ field: 'c1', type: 'int', required: false, notes: 'Relay output for the first clock channel' }, { field: 'c2', type: 'int', required: false, notes: 'Relay output for the second clock channel. At least one of c1/c2 required.' }], returns: null, example: '{ "v": 2, "cmd": "clock.set_outputs", "contents": { "c1": 4, "c2": 5 } }', warning: null },
{ cmd: 'clock.set_timings', handler: 'ClockHandler', transports: ['All'], description: 'Set clock strike timing configuration. At least one field required.', contents: [{ field: 'pulse_duration', type: 'int', required: false, notes: 'Duration of each clock output pulse in ms. Typical: 80–150 ms.' }, { field: 'pause_duration', type: 'int', required: false, notes: 'Pause between consecutive pulses (e.g. hour chimes) in ms.' }], returns: null, example: '{ "v": 2, "cmd": "clock.set_timings", "contents": { "pulse_duration": 100, "pause_duration": 500 } }', warning: null }, { cmd: 'clock.set_timings', handler: 'ClockHandler', transports: ['All'], description: 'Set clock strike timing configuration. At least one field required.', contents: [{ field: 'pulse_duration', type: 'int', required: false, notes: 'Duration of each clock output pulse in ms. Typical: 80–150 ms.' }, { field: 'pause_duration', type: 'int', required: false, notes: 'Pause between consecutive pulses (e.g. hour chimes) in ms.' }], returns: null, example: '{ "v": 2, "cmd": "clock.set_timings", "contents": { "pulse_duration": 100, "pause_duration": 500 } }', warning: null },
{ cmd: 'clock.set_alerts', handler: 'ClockHandler', transports: ['All'], description: 'Configure hourly/quarter alert behavior. Partial updates accepted — omitted fields are unchanged.', contents: [{ field: 'alert_type', type: 'string', required: false, notes: '"none" | "hour" | "quarter" | "half" — which strikes trigger the alert.' }, { field: 'alert_interval', type: 'int', required: false, notes: 'Number of times the alert bell rings per trigger.' }, { field: 'hour_bell', type: 'int', required: false, notes: 'Output index for the hour bell. 255 = disabled.' }, { field: 'half_bell', type: 'int', required: false, notes: 'Output index for the half-hour bell. 255 = disabled.' }, { field: 'quarter_bell', type: 'int', required: false, notes: 'Output index for the quarter-hour bell. 255 = disabled.' }], returns: null, example: '{ "v": 2, "cmd": "clock.set_alerts", "contents": { "alert_type": "hour", "alert_interval": 1, "hour_bell": 5 } }', warning: null }, { cmd: 'clock.set_alerts', handler: 'ClockHandler', transports: ['All'], description: 'Configure hourly/quarter alert behavior. Partial updates accepted — omitted fields are unchanged.', contents: [{ field: 'alert_type', type: 'string', required: false, notes: '"OFF" | "SINGLE" | "HOURS" — OFF disables alerts; SINGLE fires once per trigger; HOURS fires N times equal to the current hour count.' }, { field: 'alert_interval', type: 'int', required: false, notes: 'Number of times the alert bell rings per trigger (used with SINGLE mode).' }, { field: 'hour_bell', type: 'int', required: false, notes: 'Output index for the hour bell. 255 = disabled.' }, { field: 'half_bell', type: 'int', required: false, notes: 'Output index for the half-hour bell. 255 = disabled.' }, { field: 'quarter_bell', type: 'int', required: false, notes: 'Output index for the quarter-hour bell. 255 = disabled.' }], returns: null, example: '{ "v": 2, "cmd": "clock.set_alerts", "contents": { "alert_type": "HOURS", "hour_bell": 5 } }', warning: null },
{ cmd: 'clock.set_backlight', handler: 'ClockHandler', transports: ['All'], description: 'Configure LCD backlight behavior. Partial updates accepted.', contents: [{ field: 'backlight', type: 'bool', required: false, notes: 'Enable or disable automatic backlight control.' }, { field: 'backlight_output', type: 'int', required: false, notes: 'Relay output index used for the backlight.' }, { field: 'backlight_on', type: 'string', required: false, notes: 'Time to turn backlight on, format "HH:MM".' }, { field: 'backlight_off', type: 'string', required: false, notes: 'Time to turn backlight off, format "HH:MM".' }], returns: null, example: '{ "v": 2, "cmd": "clock.set_backlight", "contents": { "backlight": true, "backlight_output": 6, "backlight_on": "07:00", "backlight_off": "22:00" } }', warning: null }, { cmd: 'clock.set_backlight', handler: 'ClockHandler', transports: ['All'], description: 'Configure LCD backlight behavior. Partial updates accepted.', contents: [{ field: 'backlight', type: 'bool', required: false, notes: 'Enable or disable automatic backlight control.' }, { field: 'backlight_output', type: 'int', required: false, notes: 'Relay output index used for the backlight.' }, { field: 'backlight_on', type: 'string', required: false, notes: 'Time to turn backlight on, format "HH:MM".' }, { field: 'backlight_off', type: 'string', required: false, notes: 'Time to turn backlight off, format "HH:MM".' }], returns: null, example: '{ "v": 2, "cmd": "clock.set_backlight", "contents": { "backlight": true, "backlight_output": 6, "backlight_on": "07:00", "backlight_off": "22:00" } }', warning: null },
{ cmd: 'clock.set_silence', handler: 'ClockHandler', transports: ['All'], description: 'Configure silence periods (quiet hours). Daytime and nighttime windows are independent. Partial updates accepted.', contents: [{ field: 'daytime_silence', type: 'bool', required: false, notes: 'Enable daytime silence window.' }, { field: 'daytime_on', type: 'string', required: false, notes: 'Start of daytime silence, format "HH:MM".' }, { field: 'daytime_off', type: 'string', required: false, notes: 'End of daytime silence, format "HH:MM".' }, { field: 'night_silence', type: 'bool', required: false, notes: 'Enable nighttime silence window.' }, { field: 'night_on', type: 'string', required: false, notes: 'Start of nighttime silence, format "HH:MM".' }, { field: 'night_off', type: 'string', required: false, notes: 'End of nighttime silence, format "HH:MM".' }], returns: null, example: '{ "v": 2, "cmd": "clock.set_silence", "contents": { "night_silence": true, "night_on": "22:00", "night_off": "07:00" } }', warning: null }, { cmd: 'clock.set_silence', handler: 'ClockHandler', transports: ['All'], description: 'Configure silence periods (quiet hours). Daytime and nighttime windows are independent. Partial updates accepted.', contents: [{ field: 'daytime_silence', type: 'bool', required: false, notes: 'Enable daytime silence window.' }, { field: 'daytime_on', type: 'string', required: false, notes: 'Start of daytime silence, format "HH:MM".' }, { field: 'daytime_off', type: 'string', required: false, notes: 'End of daytime silence, format "HH:MM".' }, { field: 'night_silence', type: 'bool', required: false, notes: 'Enable nighttime silence window.' }, { field: 'night_on', type: 'string', required: false, notes: 'Start of nighttime silence, format "HH:MM".' }, { field: 'night_off', type: 'string', required: false, notes: 'End of nighttime silence, format "HH:MM".' }], returns: null, example: '{ "v": 2, "cmd": "clock.set_silence", "contents": { "night_silence": true, "night_on": "22:00", "night_off": "07:00" } }', warning: null },
{ cmd: 'clock.set_time', handler: 'ClockHandler', transports: ['All'], description: 'Set the RTC time.', contents: [{ field: 'timestamp', type: 'int', required: true, notes: 'Unix epoch timestamp' }, { field: 'timezone_offset', type: 'int', required: false, notes: 'Offset in seconds (e.g. 7200 for UTC+2)' }, { field: 'dst_offset', type: 'int', required: false, notes: 'DST offset in seconds' }], returns: null, example: '{\n "v": 2,\n "cmd": "clock.set_time",\n "contents": { "timestamp": 1740000000, "timezone_offset": 7200 }\n}', warning: null }, { cmd: 'clock.set_time', handler: 'ClockHandler', transports: ['All'], description: 'Set the RTC time.', contents: [{ field: 'timestamp', type: 'int', required: true, notes: 'Unix epoch timestamp' }, { field: 'timezone_offset', type: 'int', required: false, notes: 'Offset in seconds (e.g. 7200 for UTC+2)' }, { field: 'dst_offset', type: 'int', required: false, notes: 'DST offset in seconds' }], returns: null, example: '{\n "v": 2,\n "cmd": "clock.set_time",\n "contents": { "timestamp": 1740000000, "timezone_offset": 7200 }\n}', warning: null },
@@ -104,7 +147,27 @@ const NAMESPACES = [
{ cmd: 'clock.pause', handler: 'ClockHandler', transports: ['All'], description: 'Pause clock updates.', contents: null, returns: null, example: '{ "v": 2, "cmd": "clock.pause" }', warning: null }, { cmd: 'clock.pause', handler: 'ClockHandler', transports: ['All'], description: 'Pause clock updates.', contents: null, returns: null, example: '{ "v": 2, "cmd": "clock.pause" }', warning: null },
{ cmd: 'clock.resume', handler: 'ClockHandler', transports: ['All'], description: 'Resume clock updates.', contents: null, returns: null, example: '{ "v": 2, "cmd": "clock.resume" }', warning: null }, { cmd: 'clock.resume', handler: 'ClockHandler', transports: ['All'], description: 'Resume clock updates.', contents: null, returns: null, example: '{ "v": 2, "cmd": "clock.resume" }', warning: null },
{ cmd: 'clock.get_face', handler: 'ClockHandler', transports: ['All'], description: 'Read current analog clock face position.', contents: null, returns: '{ "clock_hour": 10, "clock_minute": 30, "last_sync_time": 1740000000, "next_output_is_c1": true }', example: '{ "v": 2, "cmd": "clock.get_face" }', warning: null }, { cmd: 'clock.get_face', handler: 'ClockHandler', transports: ['All'], description: 'Read current analog clock face position.', contents: null, returns: '{ "clock_hour": 10, "clock_minute": 30, "last_sync_time": 1740000000, "next_output_is_c1": true }', example: '{ "v": 2, "cmd": "clock.get_face" }', warning: null },
{ cmd: 'clock.get_config', handler: 'ClockHandler', transports: ['All'], description: 'Read full clock configuration — outputs, timings, alerts, backlight, silence, enabled state.', contents: null, returns: '{ "enabled", "c1output", "c2output", "pulse_duration", "pause_duration", "physical_hour", "physical_minute", "next_is_c1", "last_sync_time", "alert_type", "alert_interval", "hour_bell", "half_bell", "quarter_bell", "backlight", "backlight_output", "backlight_on", "backlight_off", "daytime_silence", "daytime_on", "daytime_off", "night_silence", "night_on", "night_off" }', example: '{ "v": 2, "cmd": "clock.get_config" }', warning: null }, { cmd: 'clock.get_config', handler: 'ClockHandler', transports: ['All'], description: 'Read full clock configuration — outputs, timings, alerts, backlight, silence, enabled state.', contents: null, returns: '{\n "enabled": true,\n "c1output": 4, "c2output": 5,\n "pulse_duration": 100, "pause_duration": 500,\n "physical_hour": 10, "physical_minute": 30,\n "next_is_c1": true, "last_sync_time": 1740000000,\n "alert_type": "HOURS", "alert_interval": 1,\n "hour_bell": 5, "half_bell": 255, "quarter_bell": 255,\n "backlight": true, "backlight_output": 6,\n "backlight_on": "07:00", "backlight_off": "22:00",\n "daytime_silence": false, "daytime_on": "00:00", "daytime_off": "00:00",\n "night_silence": true, "night_on": "22:00", "night_off": "07:00"\n}', example: '{ "v": 2, "cmd": "clock.get_config" }', warning: null },
{
cmd: 'clock.test_c1',
handler: 'ClockHandler',
transports: ['All'],
description: 'Fire the C1 (ODD) clock output immediately using the configured pulse duration. Non-blocking. Useful for verifying wiring and output assignment.',
contents: null,
returns: '{ "output": "C1", "duration_ms": 100 }',
example: '{ "v": 2, "cmd": "clock.test_c1" }',
warning: null,
},
{
cmd: 'clock.test_c2',
handler: 'ClockHandler',
transports: ['All'],
description: 'Fire the C2 (EVEN) clock output immediately using the configured pulse duration. Non-blocking. Useful for verifying wiring and output assignment.',
contents: null,
returns: '{ "output": "C2", "duration_ms": 100 }',
example: '{ "v": 2, "cmd": "clock.test_c2" }',
warning: null,
},
], ],
}, },
{ {
@@ -112,12 +175,41 @@ const NAMESPACES = [
label: 'system', label: 'system',
description: 'Device status, settings, health, and control. Handler: SystemHandler.', description: 'Device status, settings, health, and control. Handler: SystemHandler.',
commands: [ commands: [
{ cmd: 'system.status', handler: 'SystemHandler', transports: ['All'], description: 'Get current device status: player state, strike counters, projected run time.', contents: null, returns: '{ "player_status": "STOPPED|PLAYING|PAUSED|STOPPING", "time_elapsed_ms": 0, "projected_run_time": 0, "strike_counters": { "0": 1234, ... } }', example: '{ "v": 2, "cmd": "system.status" }', warning: null }, { cmd: 'system.status', handler: 'SystemHandler', transports: ['All'], description: 'Get current device status: player state, strike counters, projected run time.', contents: null, returns: '{\n "player_status": "STOPPED|PLAYING|PAUSED|STOPPING",\n "time_elapsed_ms": 0,\n "projected_run_time": 0,\n "strike_counters": { "0": 1234, "1": 567, ... }\n}', example: '{ "v": 2, "cmd": "system.status" }', warning: null },
{ cmd: 'system.get_time', handler: 'SystemHandler', transports: ['All'], description: 'Get current RTC time.', contents: null, returns: '{ "local_timestamp", "utc_timestamp", "year", "month", "day", "hour", "minute", "second", "rtc_available" }', example: '{ "v": 2, "cmd": "system.get_time" }', warning: null }, { cmd: 'system.get_time', handler: 'SystemHandler', transports: ['All'], description: 'Get current RTC time.', contents: null, returns: '{ "local_timestamp": 1740000000, "utc_timestamp": 1739993000, "year": 2025, "month": 2, "day": 20, "hour": 10, "minute": 30, "second": 0, "rtc_available": true }', example: '{ "v": 2, "cmd": "system.get_time" }', warning: null },
{ cmd: 'system.get_settings', handler: 'SystemHandler', transports: ['All'], description: 'Get full ConfigManager JSON dump (all saved settings).', contents: null, returns: null, example: '{ "v": 2, "cmd": "system.get_settings" }', warning: null }, {
{ cmd: 'system.get_device_info', handler: 'SystemHandler', transports: ['All'], description: 'Get device identity and runtime stats.', contents: null, returns: '{ "uid", "hw_type", "hw_version", "fw_version", "uptime_ms", "free_heap", "min_free_heap" }', example: '{ "v": 2, "cmd": "system.get_device_info" }', warning: null }, cmd: 'system.get_config',
{ cmd: 'system.health', handler: 'SystemHandler', transports: ['All'], description: 'Get full HealthMonitor report — all subsystem states, warnings, and critical failures.', contents: null, returns: '{ "critical_count": 0, "warning_count": 0, "firmware_stable": true, "subsystems": [{ "name": "BellEngine", "status": "HEALTHY|WARNING|CRITICAL|FAILED", "error": "..." }] }', example: '{ "v": 2, "cmd": "system.health" }', warning: null }, handler: 'SystemHandler',
{ cmd: 'system.factory_reset', handler: 'SystemHandler', transports: ['All'], description: 'Reset all settings to factory defaults.', contents: null, returns: null, example: '{ "v": 2, "cmd": "system.factory_reset" }', warning: 'Non-reversible. All saved configuration is permanently wiped.' }, transports: ['All'],
description: 'Full ConfigManager dump — all saved settings grouped by subsystem. Alias: system.get_settings (backward compatible).',
contents: null,
returns: '{\n "general": { "bells_enabled": true, "mqtt_enabled": true, "ota_channel": "stable", "serial_log": 3, "sd_log": 2, "mqtt_log": 1 },\n "bell": { "durations": {...}, "outputs": {...} },\n "clock": { ... },\n "time": { ... },\n "network": { ... },\n "mqtt": { ... }\n}',
example: '{ "v": 2, "cmd": "system.get_config" }',
warning: null,
},
{
cmd: 'system.get_all',
handler: 'SystemHandler',
transports: ['All'],
description: 'Single-shot fetch of everything: device identity + full config + live telemetry snapshot + player status. Use when loading the console for the first time — avoids multiple round trips.',
contents: null,
returns: '{\n "device": { "serial": "...", "hw_family": "VS", "hw_revision": "01", "fw_version": "143", "uptime_ms": 123456, "free_heap": 180000 },\n "config": { ... },\n "telemetry": { "strikes": {...}, "loads": {...}, "max_loads": {...}, "cooling_active": false, "guard_enabled": true, "lifetime_runtime_seconds": 1234567, "session_runtime_seconds": 3600, "total_playbacks": 4200 },\n "player": { "status": "STOPPED" }\n}',
example: '{ "v": 2, "cmd": "system.get_all" }',
warning: null,
},
{
cmd: 'system.get_telemetry',
handler: 'SystemHandler',
transports: ['All'],
description: 'Live telemetry snapshot: strike counters, bell loads, guard state, runtime metrics, device uptime, and free heap. Read-only data — does not include config.',
contents: null,
returns: '{\n "strikes": { "0": 1234, ... },\n "loads": { "0": 42, ... },\n "max_loads": { "0": 500, ... },\n "cooling_active": false,\n "guard_enabled": true,\n "lifetime_runtime_seconds": 1234567,\n "session_runtime_seconds": 3600,\n "total_playbacks": 4200,\n "uptime_ms": 12345678,\n "free_heap": 178000\n}',
example: '{ "v": 2, "cmd": "system.get_telemetry" }',
warning: null,
},
{ cmd: 'system.get_device_info', handler: 'SystemHandler', transports: ['All'], description: 'Get device identity and runtime stats.', contents: null, returns: '{ "uid": "PV000000000000", "hw_type": "VS", "hw_version": "01", "fw_version": "143", "uptime_ms": 12345678, "free_heap": 178000, "min_free_heap": 150000 }', example: '{ "v": 2, "cmd": "system.get_device_info" }', warning: null },
{ cmd: 'system.health', handler: 'SystemHandler', transports: ['All'], description: 'Full HealthMonitor report — all subsystem states, warnings, and critical failures.', contents: null, returns: '{ "critical_count": 0, "warning_count": 1, "firmware_stable": true, "subsystems": [{ "name": "BellEngine", "status": "HEALTHY|WARNING|CRITICAL|FAILED", "error": "..." }, ...] }', example: '{ "v": 2, "cmd": "system.health" }', warning: null },
{ cmd: 'system.factory_reset', handler: 'SystemHandler', transports: ['All'], description: 'Reset all settings to factory defaults. Device must be restarted to apply.', contents: null, returns: null, example: '{ "v": 2, "cmd": "system.factory_reset" }', warning: 'Non-reversible. All saved configuration is permanently wiped.' },
{ cmd: 'system.restart', handler: 'SystemHandler', transports: ['All'], description: 'Reboot the device. Response is sent before reboot (2 s delay).', contents: null, returns: null, example: '{ "v": 2, "cmd": "system.restart" }', warning: null }, { cmd: 'system.restart', handler: 'SystemHandler', transports: ['All'], description: 'Reboot the device. Response is sent before reboot (2 s delay).', contents: null, returns: null, example: '{ "v": 2, "cmd": "system.restart" }', warning: null },
], ],
}, },
@@ -136,10 +228,45 @@ const NAMESPACES = [
label: 'ota', label: 'ota',
description: 'Over-the-air update management. Handler: FirmwareHandler.', description: 'Over-the-air update management. Handler: FirmwareHandler.',
commands: [ commands: [
{ cmd: 'ota.status', handler: 'FirmwareHandler', transports: ['All'], description: 'Get OTA update status.', contents: null, returns: '{ "current_version", "available_version", "channel", "update_available", "last_check", "progress" }', example: '{ "v": 2, "cmd": "ota.status" }', warning: null }, {
{ cmd: 'ota.check', handler: 'FirmwareHandler', transports: ['All'], description: 'Check for available updates.', contents: [{ field: 'channel', type: 'string', required: false, notes: 'Update channel. Default: "stable"' }], returns: null, example: '{ "v": 2, "cmd": "ota.check", "contents": { "channel": "beta" } }', warning: null }, cmd: 'ota.update',
{ cmd: 'ota.update', handler: 'FirmwareHandler', transports: ['All'], description: 'Trigger OTA update from VPS. Device may reboot after flashing.', contents: [{ field: 'channel', type: 'string', required: false, notes: 'Update channel. Default: "stable"' }], returns: null, example: '{ "v": 2, "cmd": "ota.update", "contents": { "channel": "stable" } }', warning: 'Device may reboot after the update is applied.' }, handler: 'FirmwareHandler',
{ cmd: 'ota.custom', handler: 'FirmwareHandler', transports: ['All'], description: 'Flash firmware from a custom URL.', contents: [{ field: 'firmware_url', type: 'string', required: true, notes: 'Direct download URL for the firmware binary' }, { field: 'checksum', type: 'string', required: false, notes: 'Optional integrity checksum' }, { field: 'file_size', type: 'int', required: false, notes: 'Optional file size in bytes' }, { field: 'version', type: 'string', required: false, notes: 'Version label for display purposes' }], returns: null, example: '{\n "v": 2,\n "cmd": "ota.custom",\n "contents": {\n "firmware_url": "http://example.com/firmware.bin",\n "checksum": "abc123",\n "version": "142"\n }\n}', warning: 'Device may reboot after the update is applied.' }, transports: ['All'],
description: 'Trigger OTA update from the VPS update server using the configured channel. Device reboots after flashing.',
contents: [
{ field: 'channel', type: 'string', required: false, notes: 'Update channel: "stable" or "beta". If omitted, uses the channel saved in config.' },
],
returns: null,
example: '{ "v": 2, "cmd": "ota.update" }\n{ "v": 2, "cmd": "ota.update", "contents": { "channel": "stable" } }',
warning: 'Device will reboot after flashing. Ensure playback is stopped first.',
},
{
cmd: 'ota.custom',
handler: 'FirmwareHandler',
transports: ['All'],
description: 'Flash firmware from a custom HTTP URL. Use for staging builds or manual pushes.',
contents: [
{ field: 'firmware_url', type: 'string', required: true, notes: 'Direct HTTP download URL for the .bin firmware file. HTTPS not supported.' },
{ field: 'checksum', type: 'string', required: false, notes: 'Optional integrity checksum.' },
{ field: 'file_size', type: 'int', required: false, notes: 'Optional file size in bytes.' },
{ field: 'version', type: 'string', required: false, notes: 'Version label for display/logging.' },
],
returns: null,
example: '{\n "v": 2,\n "cmd": "ota.custom",\n "contents": {\n "firmware_url": "http://example.com/firmware.bin",\n "version": "143"\n }\n}',
warning: 'Device will reboot after flashing.',
},
{
cmd: 'ota.set_channel',
handler: 'FirmwareHandler',
transports: ['All'],
description: 'Persist the OTA update channel. Subsequent ota.update calls without a channel argument will use this value.',
contents: [
{ field: 'channel', type: 'string', required: true, notes: '"stable" or "beta"' },
],
returns: null,
example: '{ "v": 2, "cmd": "ota.set_channel", "contents": { "channel": "beta" } }',
warning: null,
},
], ],
}, },
{ {
@@ -148,7 +275,7 @@ const NAMESPACES = [
description: 'Network configuration and connection status. Handler: NetworkHandler.', description: 'Network configuration and connection status. Handler: NetworkHandler.',
commands: [ commands: [
{ cmd: 'network.info', handler: 'NetworkHandler', transports: ['All'], description: 'Get IP address, gateway, and DNS.', contents: null, returns: '{ "ip", "gateway", "dns" }', example: '{ "v": 2, "cmd": "network.info" }', warning: null }, { cmd: 'network.info', handler: 'NetworkHandler', transports: ['All'], description: 'Get IP address, gateway, and DNS.', contents: null, returns: '{ "ip", "gateway", "dns" }', example: '{ "v": 2, "cmd": "network.info" }', warning: null },
{ cmd: 'network.status', handler: 'NetworkHandler', transports: ['All'], description: 'Get full connection state.', contents: null, returns: '{ "connected", "state", "type", "ip", "rssi", "ap_mode" }', example: '{ "v": 2, "cmd": "network.status" }', warning: null }, { cmd: 'network.status', handler: 'NetworkHandler', transports: ['All'], description: 'Get full connection state including SSID, MAC, and hostname.', contents: null, returns: '{ "connected": true, "state": "CONNECTED", "type": "WiFi", "ip": "10.0.0.3", "rssi": -62, "ap_mode": false, "ssid": "MyNetwork", "mac": "AA:BB:CC:DD:EE:FF", "hostname": "vesper-lab" }', example: '{ "v": 2, "cmd": "network.status" }', warning: null },
{ cmd: 'network.set_config', handler: 'NetworkHandler', transports: ['All'], description: 'Update network configuration. Partial updates accepted. Returns restart_required flag.', contents: [{ field: 'hostname', type: 'string', required: false, notes: 'Device hostname on the network' }, { field: 'useStaticIP', type: 'bool', required: false, notes: 'Enable static IP mode' }, { field: 'ip', type: 'string', required: false, notes: 'Static IP address. Required together with gateway + subnet when using static IP.' }, { field: 'gateway', type: 'string', required: false, notes: 'Gateway address' }, { field: 'subnet', type: 'string', required: false, notes: 'Subnet mask' }, { field: 'dns1', type: 'string', required: false, notes: 'Primary DNS' }, { field: 'dns2', type: 'string', required: false, notes: 'Secondary DNS' }], returns: '{ "restart_required": true/false }', example: '{\n "v": 2,\n "cmd": "network.set_config",\n "contents": { "hostname": "vesper-lab", "useStaticIP": false }\n}', warning: null }, { cmd: 'network.set_config', handler: 'NetworkHandler', transports: ['All'], description: 'Update network configuration. Partial updates accepted. Returns restart_required flag.', contents: [{ field: 'hostname', type: 'string', required: false, notes: 'Device hostname on the network' }, { field: 'useStaticIP', type: 'bool', required: false, notes: 'Enable static IP mode' }, { field: 'ip', type: 'string', required: false, notes: 'Static IP address. Required together with gateway + subnet when using static IP.' }, { field: 'gateway', type: 'string', required: false, notes: 'Gateway address' }, { field: 'subnet', type: 'string', required: false, notes: 'Subnet mask' }, { field: 'dns1', type: 'string', required: false, notes: 'Primary DNS' }, { field: 'dns2', type: 'string', required: false, notes: 'Secondary DNS' }], returns: '{ "restart_required": true/false }', example: '{\n "v": 2,\n "cmd": "network.set_config",\n "contents": { "hostname": "vesper-lab", "useStaticIP": false }\n}', warning: null },
], ],
}, },
@@ -171,6 +298,7 @@ const NAMESPACES = [
{ cmd: 'log.set_serial', handler: 'LoggingHandler', transports: ['All'], description: 'Set log verbosity for the UART serial output.', contents: [{ field: 'level', type: 'int', required: true, notes: '0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Verbose' }], returns: null, example: '{ "v": 2, "cmd": "log.set_serial", "contents": { "level": 3 } }', warning: null }, { cmd: 'log.set_serial', handler: 'LoggingHandler', transports: ['All'], description: 'Set log verbosity for the UART serial output.', contents: [{ field: 'level', type: 'int', required: true, notes: '0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Verbose' }], returns: null, example: '{ "v": 2, "cmd": "log.set_serial", "contents": { "level": 3 } }', warning: null },
{ cmd: 'log.set_sd', handler: 'LoggingHandler', transports: ['All'], description: 'Set log verbosity for SD card logging.', contents: [{ field: 'level', type: 'int', required: true, notes: '0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Verbose' }], returns: null, example: '{ "v": 2, "cmd": "log.set_sd", "contents": { "level": 2 } }', warning: null }, { cmd: 'log.set_sd', handler: 'LoggingHandler', transports: ['All'], description: 'Set log verbosity for SD card logging.', contents: [{ field: 'level', type: 'int', required: true, notes: '0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Verbose' }], returns: null, example: '{ "v": 2, "cmd": "log.set_sd", "contents": { "level": 2 } }', warning: null },
{ cmd: 'log.set_mqtt', handler: 'LoggingHandler', transports: ['All'], description: 'Set log verbosity for MQTT log publishing.', contents: [{ field: 'level', type: 'int', required: true, notes: '0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Verbose' }], returns: null, example: '{ "v": 2, "cmd": "log.set_mqtt", "contents": { "level": 1 } }', warning: null }, { cmd: 'log.set_mqtt', handler: 'LoggingHandler', transports: ['All'], description: 'Set log verbosity for MQTT log publishing.', contents: [{ field: 'level', type: 'int', required: true, notes: '0 = None, 1 = Error, 2 = Warning, 3 = Info, 4 = Debug, 5 = Verbose' }], returns: null, example: '{ "v": 2, "cmd": "log.set_mqtt", "contents": { "level": 1 } }', warning: null },
{ cmd: 'log.get_config', handler: 'LoggingHandler', transports: ['All'], description: 'Read current log levels for all three output channels.', contents: null, returns: '{ "serial_level": 3, "sd_level": 2, "mqtt_level": 1 }', example: '{ "v": 2, "cmd": "log.get_config" }', warning: null },
], ],
}, },
{ {
@@ -180,16 +308,126 @@ const NAMESPACES = [
commands: [ commands: [
{ cmd: 'mqtt.enable', handler: 'MQTTHandler', transports: ['All'], description: 'Enable MQTT connectivity.', contents: null, returns: null, example: '{ "v": 2, "cmd": "mqtt.enable" }', warning: null }, { cmd: 'mqtt.enable', handler: 'MQTTHandler', transports: ['All'], description: 'Enable MQTT connectivity.', contents: null, returns: null, example: '{ "v": 2, "cmd": "mqtt.enable" }', warning: null },
{ cmd: 'mqtt.disable', handler: 'MQTTHandler', transports: ['All'], description: 'Disable MQTT connectivity.', contents: null, returns: null, example: '{ "v": 2, "cmd": "mqtt.disable" }', warning: null }, { cmd: 'mqtt.disable', handler: 'MQTTHandler', transports: ['All'], description: 'Disable MQTT connectivity.', contents: null, returns: null, example: '{ "v": 2, "cmd": "mqtt.disable" }', warning: null },
{ cmd: 'mqtt.get_config', handler: 'MQTTHandler', transports: ['All'], description: 'Read MQTT connection configuration from firmware (does not expose password).', contents: null, returns: '{ "enabled": true, "host": "72.61.191.197", "port": 1883, "user": "PV000000000000", "use_ssl": false }', example: '{ "v": 2, "cmd": "mqtt.get_config" }', warning: null },
], ],
}, },
{ {
id: 'telemetry', id: 'telemetry',
label: 'telemetry', label: 'telemetry',
description: 'Bell strike counters and thermal load monitoring. Handler: TelemetryHandler.', description: 'Bell strike counters, thermal load monitoring, and runtime metrics. Handler: TelemetryHandler.',
commands: [ commands: [
{ cmd: 'telemetry.get_strikes', handler: 'TelemetryHandler', transports: ['All'], description: 'Get strike counts per bell channel.', contents: null, returns: '{ "strikes": { "0": 1234, "1": 567, ... } }', example: '{ "v": 2, "cmd": "telemetry.get_strikes" }', warning: null }, {
{ cmd: 'telemetry.reset_strikes', handler: 'TelemetryHandler', transports: ['All'], description: 'Reset strike counters. Omit bell to reset all channels; include it to reset a single channel.', contents: [{ field: 'bell', type: 'int', required: false, notes: 'Channel index. If omitted, all channels are reset.' }], returns: null, example: '{ "v": 2, "cmd": "telemetry.reset_strikes" }\n{ "v": 2, "cmd": "telemetry.reset_strikes", "contents": { "bell": 2 } }', warning: null }, cmd: 'telemetry.get_strikes',
{ cmd: 'telemetry.get_loads', handler: 'TelemetryHandler', transports: ['All'], description: 'Get heat load per bell channel, cooling status, and overload flags.', contents: null, returns: '{ "loads": { "0": 42.5, ... }, "cooling_active": false, "overloaded": [2, 5] }', example: '{ "v": 2, "cmd": "telemetry.get_loads" }', warning: null }, handler: 'TelemetryHandler',
transports: ['All'],
description: 'Get lifetime strike counts per bell channel (persisted to SD).',
contents: null,
returns: '{ "strikes": { "0": 1234, "1": 567, "2": 890, ... } }',
example: '{ "v": 2, "cmd": "telemetry.get_strikes" }',
warning: null,
},
{
cmd: 'telemetry.reset_strikes',
handler: 'TelemetryHandler',
transports: ['All'],
description: 'Reset strike counters. Omit bell to reset all channels; include it to reset a single channel. Changes are saved to SD immediately.',
contents: [{ field: 'bell', type: 'int', required: false, notes: 'Channel index (0–15). If omitted, all channels are reset.' }],
returns: null,
example: '// Reset all\n{ "v": 2, "cmd": "telemetry.reset_strikes" }\n\n// Reset one channel\n{ "v": 2, "cmd": "telemetry.reset_strikes", "contents": { "bell": 2 } }',
warning: null,
},
{
cmd: 'telemetry.get_loads',
handler: 'TelemetryHandler',
transports: ['All'],
description: 'Get current heat load per bell channel, configured max loads, cooling/guard status, and overload flags.',
contents: null,
returns: '{\n "loads": { "0": 42, "1": 0, ... },\n "max_loads": { "0": 500, "1": 500, ... },\n "cooling_active": false,\n "guard_enabled": true,\n "overloaded": []\n}',
example: '{ "v": 2, "cmd": "telemetry.get_loads" }',
warning: null,
},
{
cmd: 'telemetry.set_max_loads',
handler: 'TelemetryHandler',
transports: ['All'],
description: 'Set the overload threshold for one or more bell channels. Use "all" to apply a single value across all channels, or "loads" to set per-channel values.',
contents: [
{ field: 'all', type: 'int', required: false, notes: 'Set all channels to this max load. e.g. 500' },
{ field: 'loads', type: 'object', required: false, notes: 'Per-channel map: { "0": 300, "3": 700, ... }. At least one of all/loads required.' },
],
returns: null,
example: '// Set all channels to 500\n{ "v": 2, "cmd": "telemetry.set_max_loads", "contents": { "all": 500 } }\n\n// Set specific channels\n{ "v": 2, "cmd": "telemetry.set_max_loads", "contents": { "loads": { "0": 300, "3": 700 } } }',
warning: null,
},
{
cmd: 'telemetry.set_guard_enabled',
handler: 'TelemetryHandler',
transports: ['All'],
description: 'Enable or disable the bell load guard. When disabled, the guard never triggers an emergency stop — load still accumulates and decays but no action is taken.',
contents: [
{ field: 'enabled', type: 'bool', required: true, notes: 'true = guard active, false = guard bypassed' },
],
returns: null,
example: '{ "v": 2, "cmd": "telemetry.set_guard_enabled", "contents": { "enabled": false } }',
warning: null,
},
{
cmd: 'telemetry.get_metrics',
handler: 'TelemetryHandler',
transports: ['All'],
description: 'Read runtime metrics: total lifetime seconds, current session seconds, total melody playback count, and boot log (up to 200 entries).',
contents: null,
returns: '{\n "lifetime_runtime_seconds": 1234567,\n "session_runtime_seconds": 3600,\n "total_playbacks": 4200,\n "boot_log": [\n { "timestamp": 1740000000 },\n ...\n ]\n}',
example: '{ "v": 2, "cmd": "telemetry.get_metrics" }',
warning: null,
},
{
cmd: 'telemetry.reset_metrics',
handler: 'TelemetryHandler',
transports: ['All'],
description: 'Reset all lifetime runtime metrics (lifetime seconds, total playbacks, boot log). Strike counters are NOT reset — use telemetry.reset_strikes for that.',
contents: null,
returns: null,
example: '{ "v": 2, "cmd": "telemetry.reset_metrics" }',
warning: 'Clears all historical runtime data. Cannot be undone.',
},
],
},
{
id: 'bells',
label: 'bells',
description: 'Bell mechanism master enable/disable and config read. Handler: BellsHandler.',
commands: [
{
cmd: 'bells.enable',
handler: 'BellsHandler',
transports: ['All'],
description: 'Enable the bell mechanism. Allows BellEngine playback and clock alerts to fire. Manual relay tests (relay.test_*) bypass this flag regardless.',
contents: null,
returns: null,
example: '{ "v": 2, "cmd": "bells.enable" }',
warning: null,
},
{
cmd: 'bells.disable',
handler: 'BellsHandler',
transports: ['All'],
description: 'Disable the bell mechanism. Blocks BellEngine playback and clock alerts. Manual relay tests still work. Setting is persisted to SD.',
contents: null,
returns: null,
example: '{ "v": 2, "cmd": "bells.disable" }',
warning: null,
},
{
cmd: 'bells.get_config',
handler: 'BellsHandler',
transports: ['All'],
description: 'Read bell mechanism enabled state plus all 16 channel durations and output assignments.',
contents: null,
returns: '{\n "bells_enabled": true,\n "durations": { "0": 95, "1": 100, ... },\n "outputs": { "0": 2, "1": 3, ... }\n}',
example: '{ "v": 2, "cmd": "bells.get_config" }',
warning: null,
},
], ],
}, },
] ]
@@ -220,7 +458,7 @@ const LEGACY_MAP = [
{ v1_cmd: 'system_info', v1_action: 'get_clock_time', v2: 'clock.get_face' }, { v1_cmd: 'system_info', v1_action: 'get_clock_time', v2: 'clock.get_face' },
{ v1_cmd: 'system_info', v1_action: 'get_firmware_status', v2: 'firmware.status' }, { v1_cmd: 'system_info', v1_action: 'get_firmware_status', v2: 'firmware.status' },
{ v1_cmd: 'system_info', v1_action: 'network_info', v2: 'network.info' }, { v1_cmd: 'system_info', v1_action: 'network_info', v2: 'network.info' },
{ v1_cmd: 'system_info', v1_action: 'get_full_settings', v2: 'system.get_settings' }, { v1_cmd: 'system_info', v1_action: 'get_full_settings', v2: 'system.get_config' },
{ v1_cmd: 'system', v1_action: 'status', v2: 'system.status' }, { v1_cmd: 'system', v1_action: 'status', v2: 'system.status' },
{ v1_cmd: 'system', v1_action: 'reset_defaults', v2: 'system.factory_reset' }, { v1_cmd: 'system', v1_action: 'reset_defaults', v2: 'system.factory_reset' },
{ v1_cmd: 'system', v1_action: 'commit_firmware', v2: 'firmware.commit' }, { v1_cmd: 'system', v1_action: 'commit_firmware', v2: 'firmware.commit' },
@@ -236,6 +474,13 @@ const LEGACY_MAP = [
{ v1_cmd: 'system', v1_action: 'reboot', v2: 'system.restart' }, { v1_cmd: 'system', v1_action: 'reboot', v2: 'system.restart' },
{ v1_cmd: 'system', v1_action: 'force_update', v2: 'ota.update' }, { v1_cmd: 'system', v1_action: 'force_update', v2: 'ota.update' },
{ v1_cmd: 'system', v1_action: 'custom_update', v2: 'ota.custom' }, { v1_cmd: 'system', v1_action: 'custom_update', v2: 'ota.custom' },
{ v1_cmd: 'system', v1_action: 'get_all_settings', v2: 'system.get_all' },
{ v1_cmd: 'system', v1_action: 'get_telemetry', v2: 'system.get_telemetry' },
{ v1_cmd: 'system', v1_action: 'get_full_settings', v2: 'system.get_config' },
{ v1_cmd: 'telemetry', v1_action: 'get_bell_loads', v2: 'telemetry.get_loads' },
{ v1_cmd: 'telemetry', v1_action: 'get_strike_counters', v2: 'telemetry.get_strikes' },
{ v1_cmd: 'bells', v1_action: 'set_enabled (enabled=true)', v2: 'bells.enable' },
{ v1_cmd: 'bells', v1_action: 'set_enabled (enabled=false)', v2: 'bells.disable' },
] ]
// Transport badge styles // Transport badge styles