Version 2.5.3 — pairs with adn-server 2.0.0 (report v2 slim wire + JSON HELLO).
Releases are cut from master (merge develop → master when ready; semver from commit history since last tag).
Dashboard for ADN networks: unified FastAPI process (REST + WebSocket + report ingest), React frontend, and optional MySQL for self-service and Last Heard.
v2 highlights: PHP backend/ and standalone proxy/ were removed from this repo. Self-service, auth, and the dashboard API live in monitor/monitor.py. Hotspot UDP fan-in uses integrated PROXY in adn-server. The monitor ingests report v2 dashboard_state from new-adn-server; legacy adn-dmr-server still works when HELLO is absent (pickle/CSV fallback).
| Part | Folder | Role |
|---|---|---|
| Monitor | monitor/monitor.py |
FastAPI: /api/*, /ws, report ingest (TCP or MQTT). |
| Frontend | frontend/ |
Web UI (React). Static files after npm run build. |
- Frontend can run without the monitor API (no live dashboard data).
- In production you do not need Node running: it is only used to build the frontend; then serve
frontend/dist/with Nginx or Apache (or let FastAPI serve static assets if configured).
- Node.js 18+ (frontend build only)
- Python 3.10+ (
monitor/requirements.txt: FastAPI, Twisted for TCP ingest, PyYAML, mysqlclient, …) - MySQL (for Self Service and monitor data if using DB)
Dashboard and monitor behaviour are defined in YAML under the monitor folder:
- Typical path:
monitor/adn-monitor.yaml - Copy and adapt from
monitor/adn-monitor.yaml.example. It configures:- GLOBAL: bridges, lastheard, TG count, optional TIMEZONE (IANA, e.g.
Europe/Madrid) for logs/dashboard; last_heard / lstheard_log store naive UTC datetimes; tg_count / user_count use the UTC calendar day for the daily bucket (notCURDATE()in server TZ). The UI converts stored UTC toTIMEZONEwhen showing lastheard. IfTIMEZONEis empty, lastheard uses the server’s local zone for display. - SELF_SERVICE: MySQL credentials (monitor API; same DB as adn-server self-service when enabled).
- MONITOR_APP:
LISTEN_PORT(REST/api/*+ WebSocket/wson the same port),INGEST(tcpormqtt), optional MQTT block,FREQUENCY(background resync). - ADN_CONNECTION: IP and port of the ADN report server; optional HELLO_TIMEOUT_MS (wait for opcode
0xFFHELLO from adn-server before assuming legacy adn-dmr-server). Detected mode is legacy or v2 (JSON fieldmodeon WebSocket messages prefixed withv). With v2, the footer shows Monitor and Server versions from HELLO. - ALIASES: URLs and files for alias (peers, subscribers, talkgroups).
- LOGGER, DASHBOARD (title, language, nav/footer/news marquee links).
- GLOBAL: bridges, lastheard, TG count, optional TIMEZONE (IANA, e.g.
Paths inside the YAML (e.g. LOG_PATH, PATH for alias files) are relative to the monitor/ directory when you run the monitor from there.
A single .env in the project root is used by all components. Create it from the example:
cp .env.example .envEdit .env and set at least:
| Variable | Purpose |
|---|---|
| ADN_CONFIG_PATH | Absolute path to adn-monitor.yaml. e.g. /opt/adn-monitor/monitor/adn-monitor.yaml |
| VITE_API_BASE | Frontend build: API base URL. Empty = same origin (Nginx proxies /api to monitor.py). |
| VITE_DEFAULT_LANGUAGE | Default UI language at build time (en, es, …). |
Everything else (DB, aliases, ingest, timezone) lives in adn-monitor.yaml, not in .env.
- Monitor (
monitor.py,db_bootstrap.py): auto-load root.envon start; systemd usesEnvironmentFile. - Frontend (Vite): reads the same root
.env(VITE_*only). Do not usefrontend/.env.
Two terminals.
cd /opt/adn-monitor/monitor
pip install -r requirements.txt
./run.sh
# or: python3 monitor.py -c adn-monitor.yamlDefault listen: http://localhost:8080 (MONITOR_APP.LISTEN_PORT). Health: GET /api/health.
cd /opt/adn-monitor/frontend
npm install
npm run devVite proxies /api and /ws to port 8080 (same host as the monitor API).
Create a user and database for the monitor (credentials go in adn-monitor.yaml, SELF_SERVICE section). Then create or update tables from the monitor:
cd /opt/adn-monitor/monitor
python3 db_bootstrap.py --config adn-monitor.yaml --create # create tables
python3 db_bootstrap.py --config adn-monitor.yaml --update # migrations- Place adn-monitor.yaml in
monitor/(or your chosen path) and set ADN_CONFIG_PATH in.env. - In the project root:
cp .env.example .envand fill ADN_CONFIG_PATH, VITE_API_BASE, etc. for production.
cd /opt/adn-monitor/frontend
npm install
npm run buildOutput is in frontend/dist/. That directory is what Nginx (or Apache) will serve.
An example unit is in the repo:
sudo cp /opt/adn-monitor/examples/systemd/adn-monitor.service /etc/systemd/system/
# Adjust WorkingDirectory and ExecStart if your install is not under /opt/adn-monitor
sudo systemctl daemon-reload
sudo systemctl enable adn-monitor
sudo systemctl start adn-monitorThe example runs monitor.py. Install deps for that Python: pip install -r monitor/requirements.txt.
Unified upstream (recommended): /api and /ws → MONITOR_APP.LISTEN_PORT (default 8080):
sudo cp /opt/adn-monitor/examples/nginx-adn-monitor.conf /etc/nginx/sites-enabled/Edit the file and set:
- server_name (your domains)
- root (path to
frontend/dist) - upstream
adn_monitor_api(127.0.0.1:8080 or yourLISTEN_PORT) - If using HTTPS: certificate paths and the
listen 443 sslblock (commented in the example)
Then:
sudo nginx -t && sudo systemctl reload nginx- MySQL created and
db_bootstrap.py --create/--updaterun. adn-monitor.yamland.envconfigured; ADN_CONFIG_PATH set.npm run buildinfrontend/; Nginx servesfrontend/dist/.monitor.pyrunning (systemd); Nginx proxies/apiand/wstoLISTEN_PORT.- adn-server 2.0.0 (report v2 slim wire + JSON HELLO; upgrade/restart if the footer shows an older server version).
| File | Description |
|---|---|
| examples/systemd/adn-monitor.service | Example systemd unit (monitor.py; system Python). |
| examples/nginx-adn-monitor.conf | Nginx: static frontend + FastAPI /api + /ws. |
Copy and adapt paths, domains, and certificates to your server.
To run the whole project over IPv6 (or dual-stack):
| Component | What to do |
|---|---|
| Monitor API | Set MONITOR_APP.LISTEN_HOST to :: for dual-stack (uvicorn bind). |
| Monitor – connection to ADN | Set ADN_CONNECTION.ADN_IP to the report server’s IPv6 address (or hostname that resolves to IPv6). No code change. |
| Nginx | Listen on [::]:80 / [::]:443; proxy to the monitor API on localhost or ::1. |
| Hotspot proxy | Configure PROXY in adn-server.yaml (see adn-server docs). |
| Frontend | No change; it uses the same API/WebSocket URLs. If the server is reachable via IPv6, the browser will use it when available. |
This project is GPL v3. The monitor (Python) and frontend (React dashboard) are derivatives of the following works:
| Project | Author | Description |
|---|---|---|
| FDMR Monitor | OA4DOA | FDMR Monitor for FreeDMR Server based on HBMonv2 — github.com/yuvelq/FDMR-Monitor |
| HBMonv2 | SP2ONG | HBMonitor v2 for DMR Server based on HBlink/FreeDMR — github.com/sp2ong/HBMonv2 |
| hbmonitor3 | KC1AWV | Python 3 implementation of N0MJS HBmonitor for HBlink — github.com/kc1awv/hbmonitor3 |
| HBmonitor | Cortney T. Buffington, N0MJS | Original HBmonitor (Copyright (C) 2013-2018, n0mjs@me.com) |
This codebase: Copyright (C) 2026 Rodrigo Pérez, CE5RPY. Original works and these derivatives are free software under the GNU General Public License v3. Preserve license and attribution when distributing or modifying.
Thanks to Esteban Mackay, HP3ICC, for ideas, help, testing, and suggestions throughout development.
- Changelog: CHANGELOG.md
- YAML config: options and sections in
monitor/adn-monitor.yaml.example - adn-server: report v2 and monitoring — ADN-DMR-Peer-Server docs