Spaces:
Paused
Paused
File size: 3,809 Bytes
f17d936 c543568 6cb1a1b f17d936 c543568 f17d936 6cb1a1b c543568 6cb1a1b c543568 6cb1a1b c543568 6cb1a1b c543568 6cb1a1b c543568 6cb1a1b c543568 6cb1a1b c543568 6cb1a1b c543568 6cb1a1b c543568 6cb1a1b c543568 6cb1a1b c543568 6cb1a1b c543568 6cb1a1b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | ---
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). |