Raspberry Pi Kiosk Display System
A comprehensive web-based image and video display system designed for Raspberry Pi 5 with portrait displays. Features theme organization, atmosphere management, day scheduling, remote control, and systemd auto-start.
System Overview
This skill helps you develop, deploy, and maintain a Flask-based kiosk system with:
WebSocket real-time updatesThree-tier organization (Images → Themes → Atmospheres)12-period day scheduling for automatic atmosphere switchingVideo playback with YouTube integration (yt-dlp + ffplay)Image cropping and managementBackup/restore functionalitySystemd services for auto-start on bootArchitecture
**Backend (Flask):**
Port 80 server with Flask-SocketIOJSON file storage (settings.json) with atomic writesWebSocket for real-time client updatesVideo playback via ffplay subprocessTest mode API for automated testing**Frontend (9 HTML interfaces):**
Kiosk display with smart reload (2s check interval)Management dashboard with theme/atmosphere controlsUpload interface with drag-and-drop and croppingBackup/restore interfaceRemote control, debug console, art search, and more**Organization Hierarchy:**
```
Images/Videos → Themes → Atmospheres → Day Scheduling (12 periods)
```
Development Workflow
1. Local Development Setup
```bash
Create virtual environment
python3 -m venv venv
source venv/bin/activate
Install dependencies
pip install -r requirements.txt
Grant port 80 binding capability
PYTHON_BIN=$(readlink -f venv/bin/python3)
sudo setcap 'cap_net_bind_service=+ep' "$PYTHON_BIN"
Run server
sudo ./venv/bin/python app.py
Or use start script
./start-kiosk.sh
```
2. Running Tests
```bash
cd kiosk-tests
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
playwright install chromium
Run all tests
pytest
Run by category
pytest -m unit # Unit tests
pytest -m integration # API tests
pytest -m e2e # Browser tests
```
3. Deploying to Raspberry Pi
**CRITICAL 4-STEP PROCESS:**
Before deploying, ALWAYS read device credentials from `device.txt` (gitignored file containing hostname, username, password).
```bash
STEP 1: Stop all kiosk services
sshpass -p '<password>' ssh -o StrictHostKeyChecking=no <username>@<hostname> \
"sudo systemctl stop kiosk-display.service"
STEP 2: Sync code files (excludes venv, images, settings.json, .git)
sshpass -p '<password>' rsync -avz \
--exclude 'venv/' --exclude 'images/' --exclude '*.pyc' \
--exclude '__pycache__/' --exclude '.git/' --exclude 'settings.json' \
-e "ssh -o StrictHostKeyChecking=no" \
./ <username>@<hostname>:~/kiosk_images/
STEP 3: Start kiosk.target (starts both display and firefox services)
sshpass -p '<password>' ssh -o StrictHostKeyChecking=no <username>@<hostname> \
"sudo systemctl start kiosk.target"
STEP 4: VALIDATE services (REQUIRED - do not skip!)
Check display service
sshpass -p '<password>' ssh -o StrictHostKeyChecking=no <username>@<hostname> \
"sudo systemctl status kiosk-display.service --no-pager"
Check firefox service - restart if failed
sshpass -p '<password>' ssh -o StrictHostKeyChecking=no <username>@<hostname> \
"sudo systemctl status kiosk-firefox.service --no-pager"
If Firefox failed, restart it
sshpass -p '<password>' ssh -o StrictHostKeyChecking=no <username>@<hostname> \
"sudo systemctl restart kiosk-firefox.service && sleep 2 && \
sudo systemctl status kiosk-firefox.service --no-pager"
```
**Why this order matters:**
Stopping kiosk-display auto-stops kiosk-firefox (via BindsTo)Kills mpv/ffplay processes cleanly (via ExecStopPost)Prevents file corruption and race conditionsFirefox service may fail to restart automatically - MUST validateNEVER skip stopping before copying filesNEVER skip validation step4. Installing System Services
```bash
Install and enable autostart
sudo ./install-autostart.sh
Check service status
sudo systemctl status kiosk-display.service
sudo systemctl status kiosk-firefox.service
View logs
sudo journalctl -u kiosk-display -f
sudo journalctl -u kiosk-firefox -f
Restart services
sudo systemctl restart kiosk-display.service
sudo systemctl restart kiosk-firefox.service
```
Key Implementation Details
Smart Reload Algorithm (kiosk.html)
Checks every 2 seconds for changes via vector comparisonOnly reloads if image list or interval changedPrevents unnecessary disruption during playbackTheme/Atmosphere Filtering
"All Images" theme is permanent default (shows all enabled images)Specific theme shows only assigned imagesAtmosphere active: shows all images from themes in that atmosphereDay scheduling overrides atmosphere based on time periodDay Scheduling
12 time periods of 2 hours each (24-hour coverage)Times 7-12 mirror times 1-6 (PM mirrors AM)Each period can have multiple atmospheresEmpty period defaults to "All Images" atmospherePort 80 Binding
Uses Linux capability CAP_NET_BIND_SERVICE (not root)Capability set on actual Python binary (not symlink)Install script uses `readlink -f` to resolve venv symlinksVideo Playback
Uses ffplay (from ffmpeg) for fullscreen displayyt-dlp fetches best 30fps format from YouTubeAuto-crops/scales to fit 2560x2880 portrait displayThumbnail generated by capturing frame at 20 secondsAtomic Settings Writes
Uses tempfile + os.replace() to prevent corruptionAll settings changes synchronized via WebSocketBackup check via 2-second polling for redundancyImportant API Endpoints
**Pages:**
`GET /` - Management interface`GET /view` - Kiosk display`GET /upload` - Image upload`GET /backup` - Backup management`GET /remote` - Remote control**Images API:**
`POST /api/images` - Upload image`DELETE /api/images/<filename>` - Delete image`POST /api/images/<filename>/toggle` - Toggle enabled`POST /api/images/<filename>/themes` - Update themes**Themes API:**
`GET /api/themes` - List themes`POST /api/themes` - Create theme`POST /api/themes/active` - Set active theme**Atmospheres API:**
`POST /api/atmospheres` - Create atmosphere`POST /api/atmospheres/active` - Set active atmosphere`POST /api/atmospheres/<name>/themes` - Update themes**Day Scheduling API:**
`POST /api/day/toggle` - Toggle scheduling`POST /api/day/times/<id>/atmospheres` - Set time period**Video API:**
`POST /api/videos` - Add video URL`POST /api/videos/<id>/play` - Play video**Control API:**
`POST /api/control/send` - Send command (next/prev/pause/play/reload)Git & GitHub Policy
**IMPORTANT:** Never push to GitHub without explicit user permission.
Always commit changes locally when making updatesWait for explicit "push" or "push to github" instruction before running `git push`This applies to all sessionsConstraints
Designed for Raspberry Pi 5 with 2560x2880 portrait displayRequires systemd for auto-start functionalityPort 80 requires CAP_NET_BIND_SERVICE capabilityVideo playback requires ffmpeg/ffplay and yt-dlpTesting requires Playwright (Chromium)SSH deployment requires sshpass utilityDevice credentials must be in gitignored `device.txt` file