screentinker/Examples/PIP-Announce-Broadcast
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
..
.gitignore Add PiP overlay example recipes (#132) 2026-06-18 20:20:37 -05:00
announce.js Add PiP overlay example recipes (#132) 2026-06-18 20:20:37 -05:00
config.example.json Add PiP overlay example recipes (#132) 2026-06-18 20:20:37 -05:00
message-overlay.html Add PiP overlay example recipes (#132) 2026-06-18 20:20:37 -05:00
message-overlay.js Add PiP overlay example recipes (#132) 2026-06-18 20:20:37 -05:00
package.json Add PiP overlay example recipes (#132) 2026-06-18 20:20:37 -05:00
README.md Add PiP overlay example recipes (#132) 2026-06-18 20:20:37 -05:00
test.js Add PiP overlay example recipes (#132) 2026-06-18 20:20:37 -05:00

PiP Announce / Broadcast

Flash a one-off text announcement onto a ScreenTinker screen (or a whole group) using the PiP overlay API, then clear it whenever you like. Good for fire drills, "back in 5 minutes", shift changes, a quick "Welcome, visitors!", or any manual broadcast.

It pushes a web overlay that renders a small dark card (optional coloured title band + big message + a "posted" timestamp). The overlay page reads everything from its URL query string, so there's no server-side state — the message lives entirely in the pushed URL.

How it works

announce.js ──POST /api/pip──▶ server ──WS device:pip-show──▶ player
                                                              renders <iframe
                                                              src=message-overlay.html?title&message&color>
  • announce.js builds an overlay URL from overlay_base_url + ?title&message&color and POSTs it to /api/pip (type: "web").
  • The player loads that URL in an iframe overlay. Because the player enforces a strict CSP (script-src 'self'), the overlay HTML loads its JS via <script src="message-overlay.js"> (no inline scripts) and the JS reads the query string.
  • duration controls auto-dismiss: 0 (default) keeps it up until you clear it; any positive value (seconds) auto-clears on the player at that time.

Setup

You need an st_ API token with the full scope (PiP is fleet-affecting).

cp config.example.json config.json
# edit config.json: api_base, api_token, overlay_base_url, device_id

The overlay page is served by the signage server as a same-origin static file. Copy the two overlay files into the server's frontend directory and point overlay_base_url at them:

# from the repo root, into the served frontend dir:
cp Examples/PIP-Announce-Broadcast/message-overlay.html frontend/
cp Examples/PIP-Announce-Broadcast/message-overlay.js   frontend/
# then in config.json:  "overlay_base_url": "https://<your-server>/message-overlay.html"

Same-origin matters: the player iframe and the overlay must share the server's origin so the self-signed cert / CSP are honoured.

Usage

# basic broadcast (stays until cleared)
node announce.js "Fire drill at 2:00 PM"

# with a coloured title band, auto-clear after 60s, centered
node announce.js "Back in 5 minutes" --title "AT LUNCH" --duration 60 --color "#E8730C" --position center

# target a specific device or a group (overrides config device_id)
node announce.js "All-hands in the atrium" --group <GROUP_ID>

# clear it
node announce.js --clear --device <DEVICE_ID> --pip <PIP_ID>
# (omit --pip to clear whatever overlay is showing)

Flags: --title, --device, --group, --duration (sec), --color (#RRGGBB), --position (top-right|top-left|bottom-right|bottom-left|center), --config, --clear, --pip.

Local quick-start (this dev box)

A web player is already running and paired:

  • api_base: https://localhost:3443/ (self-signed — prefix commands with NODE_TLS_REJECT_UNAUTHORIZED=0)
  • device_id: DEVICE_OR_GROUP_ID
  • token: st_REPLACE_WITH_A_FULL_SCOPE_TOKEN
cp Examples/PIP-Announce-Broadcast/message-overlay.html frontend/
cp Examples/PIP-Announce-Broadcast/message-overlay.js   frontend/
cd Examples/PIP-Announce-Broadcast
# config.json with the values above and overlay_base_url=https://localhost:3443/message-overlay.html
NODE_TLS_REJECT_UNAUTHORIZED=0 node announce.js "Hello from PiP" --title TEST --duration 20

Test

npm test   # offline; exercises the URL builder and arg parser