p-2000/CLAUDE.md
edo 2c37d5934c Add P-2000 real-time monitor with WebSocket, map, and filter panel
- Koa server on port 10000 with WebSocket live feed from 112-nu.nl RSS
- PM2 watch mode for auto-restart on file changes
- Dark navy UI with per-type color accents (fire/ambulance/police/rescue)
- Slide-in filter panel with service type + 12 Dutch province filters
- Card click opens detail modal: parsed priority (A1/A2/MGS), vehicle
  number, rit/bon number, alarm type, meldkamer, and eenheden
- Server-side Nominatim geocoder (cached, rate-limited) powering an
  interactive Leaflet/OpenStreetMap map in the modal (CartoDB Voyager tiles)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-01 10:16:38 +00:00

2.5 KiB

P-2000 Monitor

Real-time Dutch emergency services (P-2000) channel monitor. Koa server + WebSocket + RSS polling, dark shadcn-style UI.

Stack

Layer Tech
Server Node.js 20, Koa 2, koa-static, koa-router
WebSocket ws library — attached to same HTTP server
P-2000 data RSS polling via axios + xml2js (30s interval)
Process mgr PM2 with watch mode (pm2.config.js)
Frontend Vanilla JS, TailwindCSS Play CDN, shadcn-style design

Port

10000 (override via PORT env var)

Running

# Install
npm install

# Dev (PM2 watch mode — auto-restart on src/ or server.js changes)
npm run dev

# Logs
npm run logs

# Stop
npm run stop

# Plain node (no PM2)
npm start

Project layout

server.js          # Koa HTTP + WebSocket server, P-2000 event bridge
src/p2000.js       # EventEmitter: RSS fetcher, XML parser, type detector
public/index.html  # Single-page frontend (TailwindCSS + vanilla JS)
pm2.config.js      # PM2 watch config
logs/              # PM2 out/error logs (git-ignored)

P-2000 Feed source

Primary: https://112-nu.nl/hulpdiensten/rss (all services combined, polled every 30s)

Per-service feeds also available but not used (combined is sufficient):

  • https://112-nu.nl/brandweer/rss
  • https://112-nu.nl/ambulance/rss
  • https://112-nu.nl/politie/rss

Change the feed URL via COMBINED_FEED constant in src/p2000.js.

Message types

Detected from <category> RSS tag first, then keyword fallback:

Category code Type
BRA, BRW fire
AMB, MKA ambulance
POL police
KNRM, HELI rescue

Extend CATEGORY_MAP or KEYWORD_MAP in src/p2000.js to tune detection.

WebSocket protocol

Client connects to ws://localhost:10000/ws

Server → Client messages:

{ "type": "history",  "messages": [ ...MessageObject ] }
{ "type": "message",  "message":  MessageObject }

MessageObject:

{
  "id":          "unique-guid",
  "title":       "P1 Melding Brand - Rotterdam",
  "description": "Extended alert text",
  "pubDate":     "2024-01-15T12:34:56.000Z",
  "link":        "https://...",
  "type":        "fire",
  "region":      "Zuid-Holland"
}

Health check

GET /health{ status, uptime, messages }