--- 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": "", "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).