Recipe: Telemetry Dashboard
Goal: a read-only dashboard whose gauges, sparkline, status light, and log update live from your server. One broadcast updates every connected viewer. Concepts: Data Flow — sync & actions; server setup: Building a Server in Python / Building a Server in Any Language (Raw Protocol).
The layout
All display controls listen on event: "broadcast", filtered by msg_type, and
pull their value with valuePath:
{
"name": "System Monitor",
"version": 1,
"accentColor": "#34C759",
"connection": {
"url": "ws://192.168.1.50:8765",
"identity": { "name": "Monitor", "channel": "ops", "role": "viewer" }
},
"tabs": [{
"title": "Host", "icon": "cpu",
"grid": { "columns": 4, "rows": 6 },
"children": [
{ "type": "gauge", "id": "cpu", "position": [0,0], "span": [2,2],
"label": "CPU", "min": 0, "max": 100,
"sync": [{ "method":"meshsocket","type":"listen","event":"broadcast",
"filter":{"msg_type":"metrics"}, "valuePath":"cpu" }] },
{ "type": "progressRing", "id": "mem", "position": [0,2], "span": [2,2],
"label": "Memory", "min": 0, "max": 100,
"sync": [{ "method":"meshsocket","type":"listen","event":"broadcast",
"filter":{"msg_type":"metrics"}, "valuePath":"mem" }] },
{ "type": "sparkline", "id": "cpuHist", "position": [2,0], "span": [1,4],
"label": "CPU history", "sparklinePoints": 60,
"sync": [{ "method":"meshsocket","type":"listen","event":"broadcast",
"filter":{"msg_type":"metrics"}, "valuePath":"cpu" }] },
{ "type": "statusLight", "id": "health", "position": [3,0], "span": [1,2],
"label": "Service",
"sync": [{ "method":"meshsocket","type":"listen","event":"broadcast",
"filter":{"msg_type":"health"}, "valuePath":"state" }] },
{ "type": "logConsole", "id": "log", "position": [4,0], "span": [2,4],
"label": "Events", "maxLines": 200,
"sync": [{ "method":"meshsocket","type":"listen","event":"broadcast",
"filter":{"msg_type":"logline"}, "valuePath":"line" }] }
]
}]
}
Note all four controls share one broadcast channel and tell themselves apart by
msg_type. cpu and cpuHist read the same metrics frame — the sparkline
appends each number to its 60-point history; the gauge shows the latest.
The server (Python)
import asyncio, random, sys, time
sys.path.insert(0, "/path/to/MeshSocket/Python")
from socketCore import MeshSocket
socket = MeshSocket(url="ws://192.168.1.50:8765", name="Monitor Server",
channel="ops", role="hub", can_broadcast=True)
async def main():
asyncio.create_task(socket.start())
await socket.wait_until_ready()
while True:
cpu = random.randint(5, 95)
await socket.emit("broadcast_request", {"msg_type": "metrics", "cpu": cpu, "mem": random.randint(20, 80)})
await socket.emit("broadcast_request", {"msg_type": "health", "state": "green" if cpu < 90 else "red"})
if cpu > 90:
await socket.emit("broadcast_request",
{"msg_type": "logline", "line": {"text": f"high CPU {cpu}%", "level": "warning"}})
await asyncio.sleep(1)
asyncio.run(main())
Things to get right
- Native numbers. Send
73, not"73"— gauges/rings/sparklines want numbers (Data Flow — sync & actions). - Status strings.
statusLightmaps strings likegreen/red/unknownto colors; agree on the vocabulary. - Log shape.
logConsoleaccepts a bare string,{text, level}, or an array of either.level(e.g.warning,error) tints the line. - Rate. ~1 Hz per metric is smooth; the device coalesces UI updates.
- Many viewers. Because this is broadcast, every phone on
opsupdates from the same frames — no per-client work.
Add interactivity next: Recipe: Interactive Controls.