dispatch-console / README.md
secutorpro's picture
README: overview, quickstart, protocol, limitations
6cb1a1b verified
|
Raw History Blame Contribute Delete
3.81 kB
---
title: Dispatch Console
emoji: πŸ—ΊοΈ
colorFrom: blue
colorTo: green
sdk: docker
app_port: 8080
pinned: false
---
# Dispatch Console
A self-hostable PoC-style fleet dispatch console β€” in the spirit of platforms like Global-PTT's dispatch: a live map of members, SOS alert handling, group push-to-talk, and member/talkgroup management. One process, one SQLite file, no external services.
## Features
- **Live map + GPS** β€” Leaflet/OpenStreetMap map, member markers colored by talkgroup, online/offline styling, SOS pulse, popup with battery and coordinates; devices push positions over WebSocket, every ping is stored for trails (`GET /api/members/:id/trail`)
- **SOS alerts** β€” devices raise SOS, console shows a queue with Ack β†’ Resolve flow and an audio alarm; state is broadcast to all consoles instantly
- **Intercom / PTT** β€” talkgroups with floor control (one speaker per group); dispatcher holds a button to talk, mic is captured at 16 kHz via AudioWorklet and relayed as PCM over the same WebSocket; floor indicator shows who is talking; call log recorded
- **Member management** β€” add/edit/remove members, assign talkgroups, see status/battery/last-seen; talkgroup list with colors
## Architecture
```
browser / devices
β”‚ HTTPS (REST: members, groups, SOS, trails)
β”‚ WSS /ws (GPS, SOS, PTT control, PCM audio frames)
β–Ό
Node + Express ── better-sqlite3 (dispatch.db)
└─ ws Hub: auth, floor control per talkgroup, audio relay
React + Vite + Leaflet frontend (served statically by the same process)
```
Binary audio frame on the WebSocket:
`[1B type=0x01][4B groupId BE][4B memberId BE][Int16LE PCM @ 16 kHz]`
Only the current floor holder of a talkgroup may transmit; the server drops audio from anyone else.
## Quickstart (Docker)
```bash
docker compose up --build
# open http://localhost:8080 β€” log in with admin / admin (change it)
```
## Quickstart (dev)
```bash
cd server && npm install && npm run dev # API + WS on :8080
cd web && npm install && npm run dev # Vite on :5173, proxied to :8080
```
## Simulated fleet
With the server running, drive a fake fleet so you can see the console come alive:
```bash
cd server && npm run sim
# 5 units driving around Paris, one SOS after ~25 s, rotating PTT floor requests
```
## Device integration
A device (or backend integration) authenticates with a dispatcher JWT and identifies as a member:
```json
{"type": "auth", "token": "<jwt>", "memberId": 12}
```
Then, per message: `{"type":"gps","lat":48.85,"lng":2.35,"speed":34,"battery":80}`,
`{"type":"sos"}`, `{"type":"ptt_start"}`, `{"type":"ptt_stop"}`,
and raw binary frames for audio (format above).
## Configuration
| Env | Meaning |
| --- | --- |
| `PORT` | HTTP/WS port (default 8080) |
| `DATA_DIR` | where `dispatch.db` and the JWT secret live (default `server/data`) |
| `JWT_SECRET` | signing secret; generated and persisted on first run if unset |
| `ADMIN_PASSWORD` | password for the first-run `admin` dispatcher |
## Limitations & next steps
- **Device auth is trusted-by-JWT**: any valid token can claim any `memberId`. Production needs per-device credentials and revocation.
- **Audio is uncompressed 16 kHz PCM relayed over WebSocket** β€” fine for small fleets on good links; swap in Opus + an SFU (LiveKit, mediasoup) for larger ones and lossy networks.
- Single-node SQLite; horizontal scale would need Postgres and a Redis pub/sub fan-out.
- Map tiles come from public OpenStreetMap servers β€” self-host tiles for production privacy.
- No TLS in the container β€” terminate TLS at your reverse proxy (devices require WSS/HTTPS for mic access anyway).
- On HF Spaces, storage is ephemeral: the SQLite file resets on rebuild (fine for demos; use a mounted volume or external DB for real data).