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:
santa-server(map app)webnode-agent(local control agent)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.servicecloudflared.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