--- name: digital-signage description: |- Build and maintain a multi-tenant digital signage CMS platform — Raspberry Pi kiosk players, customer upload portal, playlist management, device registration. Replaces OptiSigns ($15-30/mo). author: Sho'Nuff version: 1.0.0 tags: [raspberry-pi, signage, kiosk, multi-tenant, content-management] platforms: [linux] --- # Digital Signage CMS Multi-tenant digital signage platform. Customers upload images/video via a web portal, create playlists, and Raspberry Pi players display content in fullscreen kiosk mode. ## Project Directory All docs live at `/root/projects/digital-signage/`. Key files: | File | Contents | |------|----------| | `README.md` | Full architecture, multi-tenant hierarchy, Pi registration flow, MVP scope | | `optisigns-comparison.md` | Feature-by-feature comparison vs OptiSigns (what we build vs what we skip) | | `cms/customer-setup-guide.md` | Customer-facing 9-step quick-start guide (printable) | | `player/provisioning.md` | Zero-touch Pi provisioning — first-boot script, SD card imaging, recovery | | `player/device-identification.md` | Hidden Ctrl+I overlay for support — device name, serial, IP, playlist | | `mockups/` | Frontend HTML mockups (customer portal) | ## Architecture ``` Customer Web Portal Raspberry Pi Player ┌────────────────────────┐ ┌──────────────────────┐ │ Login (per tenant) │ │ Registration script │ │ Upload images/video │ API │ POST /api/register │ │ Create playlists │ ←───────→ │ Poll /api/playlist │ │ Manage devices │ │ Chromium kiosk mode │ │ Registration codes │ │ Fullscreen playback │ └────────────────────────┘ └──────────────────────┘ │ ▼ FastAPI + SQLite + S3 ``` ### Multi-tenant hierarchy ``` Platform ├── Tenant: Joe's Diner │ ├── Users: admin, staff@... │ ├── Device: Main Lobby Pi │ ├── Device: Menu Board Pi │ └── Content: 8 items, 2 playlists ├── Tenant: Main Street Gym │ └── Device: Front Desk Pi └── Tenant: Third Coast Dental ├── Device: Waiting Room Pi ├── Device: Exam Room Pi └── Device: Hallway Pi ``` Key rule: **one customer = one tenant = unlimited devices.** Content libraries are per-tenant. Playlists assignable to one or multiple devices. ## OptiSigns Replication | Tier | What's Included | Timeline | |------|----------------|----------| | **Phase 1 — MVP** | Upload images/video, playlists (ordered + shuffle), multi-tenant, Pi registration, fullscreen kiosk, device identification overlay | ~2 weeks | | **Phase 2 — Parity** | Content scheduling, multi-user per tenant, thumbnail previews, auto-update on content change | ~1 week | | **Phase 3 — Surpass** | Sho'Nuff text-to-broadcast integration, ops portal dark theme, no OptiSigns logo, API for remote control | ~1 week | **What we skip:** Power BI dashboards, touch interactivity, video walls, background music licensing, wireless presentation. ## Pi Provisioning — Zero Touch 1. **Image SD card** with Raspberry Pi OS lite + first-boot script (auto-login, Chromium, player service) 2. **Generate registration code** from CMS 3. **Ship Pi** to customer (HDMI + power cable included) 4. **Customer plugs in** — two cables: HDMI + power 5. **Pi boots**, auto-registers, fetches playlist, plays content **On power loss/reboot:** Auto-restarts and resumes. Already registered → skips to content. **On SD card failure:** Flash new card → 5 min fix → re-register from CMS. **On TV change:** Pi auto-detects resolution via HDMI EDID. No config needed. ## Device Registration Flow ``` 1. Customer portal → Devices → Register New Device → code: SIGN-4K7P-M9X2 2. Pi boots → runs registration script → POST /api/register with code + serial 3. Server: creates device under tenant → returns device_id + token 4. Pi stores token locally → starts polling GET /api/playlist/:device_id every 60s 5. CMS playlist change → Pi picks it up on next poll ``` ## Device Identification Overlay Hidden overlay triggered by Ctrl+I pressed 3 times in 2 seconds (or remote API call). Shows device name, serial, IP, playlist, model, last sync. Auto-hides after 30s. No customer sees it in normal operation. ### 3 trigger methods: 1. **Keyboard:** `Ctrl+I` × 3 within 2 seconds 2. **Remote API:** `POST /api/devices/:id/identify` (device polls `/status` every 10s) 3. **Boot flag:** `./kiosk.sh --identify` ## Resolution Handling **Pi auto-detects via HDMI EDID.** Upload content at highest quality (4K if you have it). Pi scales to whatever display is connected. CMS does NOT need to know or care about output resolution — that's the Pi's job. | Pi Model | Max Output | Best For | |----------|-----------|----------| | Pi 3 | 1080p @ 60fps | Stills, basic slideshows | | Pi 4 | 4K @ 60fps | Mixed content, smooth video | | Pi 5 | 4K @ 60fps HDR | Heavy video, multiple layers | ## Frontend Portal Mockup The customer portal mockup lives at `ops.itpropartner.com/signage-mockup.html` — dark theme, 6 views (login, dashboard, content, playlists, devices, register Pi modal). Demo tenant: Joe's Diner with 3 devices. ## Pitfalls - **Pi registration code expiry** — codes should auto-expire after 24h to prevent reuse/snooping - **Offline playback** — player should cache the current playlist locally so it keeps playing if network drops - **Content moderation** — uploaded files need type/size validation before storage. Allow common image formats (jpg, png, webp) and video (mp4, mov, webm). Reject executables, archives, scripts - **Multi-user per tenant** — Phase 2 feature but plan the DB schema for it now (users table with tenant_id FK) - **Scheduling complexity** — time-based playlists need a cron-like scheduler on the backend, not just the player polling interval - **Player self-healing** — if Chromium crashes (OOM on Pi 3), the service should auto-restart. Player script needs `Restart=always` in the systemd unit - **Bandwidth** — multiple Pis polling every 60s is negligible. Multiple Pis fetching 140MB video files simultaneously is not. Consider S3 signed URLs or CDN for content delivery at scale