screentinker/Examples/PIP-Room-Status-Calendar
ScreenTinker ab771ec595 Add PiP overlay example recipes
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:17:38 -05:00
..
.gitignore Add PiP overlay example recipes 2026-06-18 20:17:38 -05:00
config.example.json Add PiP overlay example recipes 2026-06-18 20:17:38 -05:00
fixture-room.ics Add PiP overlay example recipes 2026-06-18 20:17:38 -05:00
package.json Add PiP overlay example recipes 2026-06-18 20:17:38 -05:00
README.md Add PiP overlay example recipes 2026-06-18 20:17:38 -05:00
room-overlay.html Add PiP overlay example recipes 2026-06-18 20:17:38 -05:00
room-overlay.js Add PiP overlay example recipes 2026-06-18 20:17:38 -05:00
room.js Add PiP overlay example recipes 2026-06-18 20:17:38 -05:00
test.js Add PiP overlay example recipes 2026-06-18 20:17:38 -05:00

Room Status sign (calendar-driven Available / Busy)

Turns a ScreenTinker display into a meeting-room sign. It polls an ICS calendar feed and pushes a PiP web overlay that shows AVAILABLE (green) or BUSY (red) plus the current/next meeting time. Re-pushed every poll so the state stays fresh; cleared when you stop the script.

No dependencies — Node 18+ only.

How it works

ICS feed ──poll──> room.js ──POST /api/pip (type=web)──> player renders room-overlay.html
  • room.js fetches the calendar, parses VEVENTs, and decides busy/free at now.
  • The overlay is room-overlay.html + room-overlay.js, served by the signage server and rendered by the player in an iframe. The script reads the status from the URL query string (the server CSP forbids inline scripts, so the logic lives in the external .js).

Get an ICS URL

  • Google Calendar: Calendar settings → Integrate calendarSecret address in iCal format. (Treat it like a password.) For a room, use the room/resource calendar.
  • Outlook / Microsoft 365: Calendar → Share → Publish, then copy the ICS link.
  • Any CalDAV/ICS publisher works. The feed must be reachable by the machine running room.js.

Serve the overlay assets

Copy room-overlay.html and room-overlay.js into the signage server's web root (the same directory that serves the SPA), so they're reachable at https://<your-server>/room-overlay.html. They must be same-origin with the player (the overlay runs in an iframe under the server's CSP).

Configure

cp config.example.json config.json
# edit config.json: api_base, api_token (st_ token with the 'full' scope),
# overlay_base_url, device_id (a device OR a group id), room_name, ics_url

Run

npm start            # or: node room.js config.json

Stop with Ctrl-C — it clears the overlay on the way out.

Local quick-start (self-signed dev server)

For a local ScreenTinker instance on https://localhost:3443 with a self-signed cert:

{
  "room_name": "Aspen Room",
  "api_base": "https://localhost:3443/",
  "api_token": "st_REPLACE_WITH_A_FULL_SCOPE_TOKEN",
  "overlay_base_url": "https://localhost:3443/room-overlay.html",
  "device_id": "DEVICE_OR_GROUP_ID",
  "ics_url": "https://calendar.google.com/calendar/ical/.../basic.ics",
  "poll_interval_sec": 60
}
NODE_TLS_REJECT_UNAUTHORIZED=0 node room.js config.json

(NODE_TLS_REJECT_UNAUTHORIZED=0 only to accept the dev cert — never in production.) Remember to copy room-overlay.html + room-overlay.js into the server's web root first.

Offline demo / test

test.js runs the ICS parser and status logic against fixture-room.ics at a fixed clock — no server, no network:

npm test

You can also drive the overlay against the fixture by setting ics_file (instead of ics_url) in config.json.

Config reference

key meaning
room_name label shown on the overlay
api_base ScreenTinker server base URL
api_token st_ API token with the full scope
overlay_base_url URL where room-overlay.html is served (same-origin with the player)
device_id target device or group id
ics_url calendar feed URL (or use ics_file for a local file)
poll_interval_sec refresh cadence (default 120)
colors.available / colors.busy band colors, 6-hex no #
overlay.position center (default), top-right, top-left, bottom-right, bottom-left
overlay.width / overlay.height / overlay.border_radius overlay box geometry

Time-zone note

DTSTART/DTEND in UTC (…Z) are handled exactly. A floating time (no Z) is read as the local time of the machine running room.js, and TZID parameters are not resolved to their zone. For a single room whose host shares the room's timezone this is correct; for cross-timezone calendars, publish the feed in UTC.