dispatch-console / README.md
secutorpro's picture
README: overview, quickstart, protocol, limitations
6cb1a1b verified
|
Raw History Blame Contribute Delete
3.81 kB
metadata
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)

docker compose up --build
# open http://localhost:8080 β€” log in with admin / admin (change it)

Quickstart (dev)

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:

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:

{"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).