screentinker/Examples/PIP-Incident-Webhook/README.md
screentinker 0b138f10c6
Add PiP overlay example recipes (#132)
Self-contained examples for the PiP overlay API (POST /api/pip), each
with a CSP-safe query-param overlay (external JS), config.example.json,
zero runtime deps, an offline test, and a README:

- PIP-Announce-Broadcast    manual one-shot message to a screen/group
- PIP-Weather-Widget        Open-Meteo current conditions (keyless)
- PIP-Air-Quality           Open-Meteo US AQI widget (keyless)
- PIP-Crypto-Ticker         CoinGecko price strip (keyless)
- PIP-News-Ticker           scrolling RSS/Atom headlines
- PIP-Room-Status-Calendar  ICS-driven Available/Busy room sign
- PIP-Event-Countdown       client-side countdown, auto-clears at zero
- PIP-Welcome-Board         rotating welcome/birthday cards from CSV
- PIP-Fundraiser-Thermometer goal-progress bar from local/URL JSON
- PIP-QR-Rotator            rotating QR codes, encoded client-side
- PIP-Incident-Webhook      event-driven: red on firing, clear on resolved

Also includes the CAP-AU (NSW RFS) and US NWS/NOAA emergency-alert
monitors that push expiry-aware PiP overlays.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 20:20:37 -05:00

3.9 KiB

PiP Incident Webhook

An event-driven PiP example: a tiny webhook receiver that turns your monitoring stack's alerts into a floating ScreenTinker overlay — perfect for an engineering wall TV or NOC screen.

  • alert firing → red overlay appears (kept until cleared)
  • alert resolved → overlay disappears

Unlike the CAP / NOAA examples (which poll a feed), nothing happens here until your alerting system pushes to POST /webhook. Zero runtime dependencies — just Node 18+ (http + global fetch).

Payload shapes

It accepts either:

Generic (great for curl, cron jobs, custom scripts):

{ "status": "firing", "key": "db-down", "title": "Primary DB unreachable", "detail": "conn refused on 5432", "severity": "critical" }

Prometheus Alertmanager (point a webhook_config straight at it):

{ "status": "firing", "alerts": [
  { "status": "firing", "fingerprint": "abc123",
    "labels": { "alertname": "HighCPU", "severity": "warning", "instance": "web-1" },
    "annotations": { "summary": "CPU > 90%", "description": "web-1 hot for 5m" } }
]}

severity drives the band colour: critical→dark red, warning→orange, info→amber, anything else→red. The key (or Alertmanager fingerprint) is what matches a later resolve back to the overlay it should clear.

Setup

  1. cp config.example.json config.json and fill in:
    • api_token — an st_ API token with the full scope.
    • api_base / overlay_base_url — your signage server.
    • device_id — a device or group id.
    • shared_secret (optional) — if set, callers must send it as the X-Webhook-Secret header or ?secret= query param.
  2. Serve the overlay assets. The overlay is a web PiP rendered in an iframe, so the player fetches overlay_base_url directly. Copy incident-overlay.html and incident-overlay.js into the directory your signage server serves at the web root (e.g. the server's frontend/ dir) so that https://<server>/incident-overlay.html resolves. They must be same-origin with the player (the server CSP only allows same-origin scripts — that's why the JS is an external incident-overlay.js, not inline).
  3. node server.js (or npm start).

Local quick-start (this repo's dev server)

cp config.example.json config.json
# edit config.json:
#   "api_base":          "https://localhost:3443/"
#   "api_token":         "st_REPLACE_WITH_A_FULL_SCOPE_TOKEN"
#   "overlay_base_url":  "https://localhost:3443/incident-overlay.html"
#   "device_id":         "DEVICE_OR_GROUP_ID"

# copy the overlay assets into the server's web root (served same-origin as the player):
cp incident-overlay.html incident-overlay.js ../../frontend/

# self-signed cert on localhost -> let Node accept it:
NODE_TLS_REJECT_UNAUTHORIZED=0 node server.js

Then drive it with curl:

# fire a critical incident -> red overlay appears on the player
curl -s localhost:8088/webhook -H 'Content-Type: application/json' -d \
  '{"status":"firing","key":"db-down","title":"Primary DB unreachable","detail":"conn refused on 5432","severity":"critical"}'

# ...later, resolve it -> overlay clears
curl -s localhost:8088/webhook -H 'Content-Type: application/json' -d \
  '{"status":"resolved","key":"db-down"}'

# health
curl -s localhost:8088/healthz

Ctrl-C clears any still-showing overlays before exiting.

Heads-up: this dev box has a shared player. If someone else is demoing on d7c88aa0-…, point device_id at your own device/group instead.

Wire up Alertmanager

# alertmanager.yml
route:
  receiver: signage
receivers:
  - name: signage
    webhook_configs:
      - url: http://YOUR_HOST:8088/webhook
        send_resolved: true        # so "resolved" clears the overlay

If you set a shared_secret, append it to the URL: ...:8088/webhook?secret=YOUR_SECRET.

Test

npm test   # offline; exercises both payload shapes + the colour map