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. statusLight maps strings like green/red/unknown to colors; agree on the vocabulary.
  • Log shape. logConsole accepts 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 ops updates from the same frames — no per-client work.

Add interactivity next: Recipe: Interactive Controls.