NetPanel – The Sleigh Network Control Panel

NetPanel – Full Deployment & Configuration Guide

This document describes the final, working setup for a 3-node control panel system consisting of:

  • Tracker-Pi (192.168.196.10)
  • Playout-Pi (192.168.196.11)
  • Webserver VM (192.168.196.5)

The system provides:

  • Central dashboard (NetPanel)
  • Service control on all nodes
  • Power control (reboot/shutdown)
  • Internet & LAN connectivity checks
  • GPS fix + MQTT telemetry from Tracker-Pi
  • Ability to pause location updates without stopping the web server

Architecture Overview

  • Each node exposes a local HTTP status/control agent
  • The NetPanel aggregates these agents
  • No SSH is used for control
  • All actions are HTTP + systemd
  • Authentication uses a shared admin token

Shared Configuration (All Nodes)

Choose one long random token and use it everywhere:

ADMIN_TOKEN=CHANGE_ME_LONG_RANDOM_SECRET

This token is passed via the HTTP header:

X-Auth-Token

Node 1 – Tracker-Pi (192.168.196.10)

Purpose

  • GPS receiver
  • MQTT publisher
  • Status/control agent

Requirements

  • Tracker status agent already installed
  • Must expose:
GET /api/status
POST /api/service/<unit>/<action>
POST /api/power/<action>

systemd Environment

Add to the tracker status service:

Environment="ADMIN_TOKEN=CHANGE_ME_LONG_RANDOM_SECRET"
Environment="APP_HOST=0.0.0.0"
Environment="APP_PORT=8080"

Restart

sudo systemctl daemon-reload
sudo systemctl restart santa-status

Verify

curl http://127.0.0.1:8080/api/status | jq

Node 2 – Playout-Pi (192.168.196.11)

Purpose

  • Audio playback
  • MQTT subscriber
  • Status/control agent

systemd Environment

Add to playout status service:

Environment="ADMIN_TOKEN=CHANGE_ME_LONG_RANDOM_SECRET"

Restart

sudo systemctl daemon-reload
sudo systemctl restart audio-status

Verify

curl http://127.0.0.1/api/status | jq

Node 3 – Webserver VM (192.168.196.5)

This node runs three components:

  1. santa-server (map app)
  2. webnode-agent (local control agent)
  3. netpanel (main dashboard)

3A – Santa Server (Map App)

Purpose

  • Displays map
  • Receives GPS updates
  • Can pause updates without stopping the server

Replace App

Replace your existing app with the patched version:

cp app.py app.py.bak
cp app_patched.py app.py

systemd Configuration

Edit the service:

sudo systemctl edit santa-server

Add:

[Service]
Environment="ADMIN_TOKEN=CHANGE_ME_LONG_RANDOM_SECRET"

Restart

sudo systemctl daemon-reload
sudo systemctl restart santa-server

Required Endpoints (Implemented)

GET  /api/admin/updates
POST /api/admin/updates { "enabled": true|false }

3B – Webnode Agent

Purpose

  • Controls local services:
    • santa-server.service
    • cloudflared.service
  • Proxies update toggle to santa-server

Install

mkdir -p /opt/webnode-agent
unzip webnode-agent.zip -d /opt
cd /opt/webnode-agent
python3 -m venv venv
./venv/bin/pip install -r requirements.txt

Environment File

/etc/webnode-agent.env

ADMIN_TOKEN=CHANGE_ME_LONG_RANDOM_SECRET
SANTA_ADMIN_TOKEN=CHANGE_ME_LONG_RANDOM_SECRET

MAP_UNIT=santa-server.service
CLOUDFLARED_UNIT=cloudflared.service

SANTA_BASE=http://127.0.0.1:8000
PING_TARGET=1.1.1.1
PORT=8050

Enable

cp systemd/webnode-agent.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now webnode-agent

Verify

curl http://127.0.0.1:8050/api/status | jq

3C – NetPanel (Main Dashboard)

Purpose

  • Aggregates all nodes
  • UI for control + telemetry

Install

mkdir -p /opt/netpanel
unzip netpanel.zip -d /opt
cd /opt/netpanel
python3 -m venv venv
./venv/bin/pip install -r requirements.txt

Environment File

/etc/netpanel.env

ADMIN_TOKEN=CHANGE_ME_LONG_RANDOM_SECRET

TRACKER_BASE=http://192.168.196.10:8080
PLAYOUT_BASE=http://192.168.196.11
WEBSERVER_AGENT_BASE=http://192.168.196.5:8050

NODE_TOKEN_TRACKER=CHANGE_ME_LONG_RANDOM_SECRET
NODE_TOKEN_PLAYOUT=CHANGE_ME_LONG_RANDOM_SECRET
NODE_TOKEN_WEBSERVER=CHANGE_ME_LONG_RANDOM_SECRET

PING_TARGET=1.1.1.1
STATUS_TIMEOUT=8
CACHE_TTL=2
PORT=8060

Enable

cp systemd/netpanel.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now netpanel

Access

http://192.168.196.5:8060

NetPanel UI – Tracker Telemetry Section

Purpose

Adds a dedicated bottom section showing:

  • GPS fix information (Tracker-Pi)
  • MQTT messages (Tracker-Pi)

HTML Layout Change

Replace:

<main class="wrap" id="cards"></main>

With:

<main>
  <div class="wrap" id="cards"></div>
  <div class="wrap" id="bottom"></div>
</main>

JavaScript – Telemetry Renderer

Add above async function refreshNow():

function renderTrackerTelemetry(trackerNode) {
  if (!trackerNode || !trackerNode.agent_status?.ok) {
    return `
      <section class="card">
        <div class="title">Tracker telemetry</div>
        <div class="muted">Tracker agent unavailable</div>
      </section>`;
  }

  const data = trackerNode.agent_status.data || {};
  const gps = data.gps || {};
  const mqtt = data.mqtt_messages || data.mqtt || null;

  return `
    <section class="card">
      <div class="title">Tracker telemetry</div>

      <div class="sep"></div>
      <div class="title">GPS fix</div>
      <div class="kv">
        <div class="muted">Fix</div><div>${gps.mode ?? "-"}</div>
        <div class="muted">Lat</div><div>${gps.lat ?? "-"}</div>
        <div class="muted">Lon</div><div>${gps.lon ?? "-"}</div>
        <div class="muted">Sats</div><div>${gps.sats ?? "-"}</div>
      </div>

      <div class="sep"></div>
      <div class="title">MQTT</div>
      <pre class="mono">${mqtt ? JSON.stringify(mqtt, null, 2) : "No MQTT data"}</pre>
    </section>
  `;
}

Hook Into refreshNow()

Inside:

async function refreshNow() {

After cards are rendered:

document.getElementById("cards").innerHTML = cards;
document.getElementById("bottom").innerHTML =
  renderTrackerTelemetry(s.nodes?.tracker);

Final Result

✔ All three nodes visible
✔ Service control everywhere
✔ GPS + MQTT shown in a stable bottom section
✔ Map updates can be paused safely
✔ Cloudflared stays online
✔ No SSH, no hacks, no fragile state