Files
MediaandClaude Opus 5 ae70bf1dd8 Implement Phase 4 integrations and display telemetry
Add MQTT playback control, durable Home Assistant incident notifications,
and display heartbeat telemetry on top of the verified Phase 3B playback
authority. No playback logic is duplicated: every MQTT command and every
observed condition goes through the existing playback service, and all
integration work happens after the authoritative SQLite commit.

MQTT (server/src/mqtt) subscribes the exact required command topics under
a configurable prefix that defaults to announcements. Durable commands use
QoS 1; next and previous stay at QoS 0 because they are not idempotent and
carry no command ID. Retained authoritative state is published only on the
separate availability, mode/state, and playback/state topics, never onto a
command topic, and only after a mutation commits. A registered last will
reports an ungraceful disappearance, a clean shutdown retracts it, and a
broker outage degrades status while playback, HTTP, and the display
protocol continue untouched.

Notifications (server/src/notifications) use the Phase 1 incident and
outbox tables. A failing condition opens one incident and queues exactly
one notification; continued failure only updates its observation time;
recovery queues exactly one recovery; a later failure is a new episode.
Delivery retries on a documented capped schedule with a stable notification
ID, survives restart, and never blocks playback. With no webhook URL
configured, incidents are still recorded but no delivery work is queued.
Conditions come from storage health transitions, committed playback state,
display heartbeat staleness, and newly failed uploads.

Displays (server/src/displays) ingest validated, bounded display:heartbeat
telemetry keyed by persistent client ID rather than socket ID. Telemetry is
read-only and never touches playback or playlist revisions. Offline is
decided by heartbeat staleness, not by a closed socket, and a startup grace
period keeps an API restart from manufacturing offline alerts. Adds
GET /api/displays and meaningful-change display:status events.

Health and status now report redacted storage, MQTT, notification,
playback, and display state. External integrations degrade status and never
affect readiness, so no integration outage can cause a restart loop. No
MQTT password, S3 credential, webhook URL, or webhook secret is exposed.

Uses the existing Phase 1 schema unchanged; no migration was required.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-16 22:52:59 -05:00

62 lines
2.2 KiB
Bash

# A distinct project name and host port let v1 and v2 run side by side.
COMPOSE_PROJECT_NAME=announcements-v2
WEB_PORT=5175
# Docker Compose pins these internal values. They are shown here to document the
# standalone API configuration contract; do not uncomment them for normal Compose use.
# NODE_ENV=production
# HOST=0.0.0.0
# PORT=7532
# DATABASE_PATH=/data/db/announcements.sqlite
# S3_ENDPOINT=http://seaweedfs:8333
# S3_FORCE_PATH_STYLE=true
# Optional local Vite development proxy target (not used by Compose).
# VITE_API_PROXY_TARGET=http://127.0.0.1:7532
# Application configuration exposed by Compose
APP_TIMEZONE=America/Chicago
SHUTDOWN_TIMEOUT_MS=10000
DATABASE_BUSY_TIMEOUT_MS=5000
TRUST_PROXY=true
# Private SeaweedFS S3 configuration
S3_REGION=us-east-1
S3_BUCKET=announcements-v2
S3_ACCESS_KEY_ID=replace-with-a-random-access-key
S3_SECRET_ACCESS_KEY=replace-with-a-long-random-secret
S3_CONNECT_TIMEOUT_MS=2000
# Maximum socket inactivity during one S3 operation (not a total upload deadline).
S3_REQUEST_TIMEOUT_MS=5000
S3_MAX_ATTEMPTS=2
S3_HEALTH_PROBE_INTERVAL_MS=30000
# Image upload and decode limits. The default proxy body limit is 512 MiB,
# which accommodates the default twenty 25 MiB files plus multipart overhead.
UPLOAD_MAX_FILE_BYTES=26214400
UPLOAD_MAX_FILES=20
IMAGE_MAX_INPUT_PIXELS=40000000
# UPLOAD_TEMP_DIRECTORY=/tmp/announcements-uploads
# Optional integrations. Leave MQTT_URL blank to disable the MQTT integration
# and HA_WEBHOOK_URL blank to disable outgoing notifications entirely.
MQTT_URL=
MQTT_USERNAME=
MQTT_PASSWORD=
# With the default prefix the command topics are announcements/mode,
# announcements/next, announcements/previous, announcements/pause,
# announcements/resume, announcements/reload, and announcements/static.
MQTT_TOPIC_PREFIX=announcements
MQTT_RECONNECT_PERIOD_MS=5000
MQTT_CONNECT_TIMEOUT_MS=10000
# Outgoing Home Assistant webhook. The URL is a secret: it is never returned by
# any API response or status document.
HA_WEBHOOK_URL=
HA_WEBHOOK_SECRET=
HA_WEBHOOK_TIMEOUT_MS=10000
# A display is reported offline after this long without a valid heartbeat.
# The display client is expected to send one every 10-15 seconds.
DISPLAY_OFFLINE_THRESHOLD_MS=45000