Files
infoscreen-dev/README.md
RobbStarkAustria cfc1931975 Add client volume settings and harden screenshot publishing
- persist client config from MQTT for display manager volume control
- apply effective VLC volume from event volume and client multiplier
- support live runtime volume updates for active video playback
- make latest screenshot handoff atomic to avoid broken latest.jpg races
- ignore broken screenshot pointers when selecting fallback images
- fix screenshot size logging and document server-side volume control
2026-03-22 12:41:13 +01:00

922 lines
33 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Infoscreen Client - Display Manager
Digital signage system for Raspberry Pi that displays presentations, videos, and web content in kiosk mode. Centrally managed via MQTT with automatic client discovery, heartbeat monitoring, and screenshot-based dashboard monitoring.
## 🎯 Key Features
- **Automatic Presentation Display** - Server renders PPTX to PDF; client displays PDFs with Impressive
- **Auto-Advance Slideshows** - Configurable timing for automatic slide progression
- **Loop Mode** - Presentations can loop infinitely or quit after last slide
- **HDMI-CEC TV Control** - Automatic TV power on/off based on event scheduling
- **MQTT Integration** - Real-time event management from central server
- **Group Management** - Organize clients into groups for targeted content
- **Heartbeat Monitoring** - Regular status updates and screenshot dashboard
- **Client Process Monitoring** - Health-state bridge, crash/restart tracking, and monitoring log
- **Screenshot Dashboard** - Automatic screen capture with Wayland/X11 support, client-side compression
- **Multi-Content Support** - Presentations, videos, and web pages
- **Kiosk Mode** - Full-screen display with automatic startup
## 📋 System Requirements
### Hardware
- Raspberry Pi 4/5 (or compatible)
- HDMI display
- Network connectivity (WiFi or Ethernet)
- SSD storage recommended
### Software
- Raspberry Pi OS (Bookworm or newer)
- Python 3.x
- Impressive (PDF presenter with auto-advance)
- Chromium browser (for web content)
- VLC or MPV (for video playback)
- Screenshot tools: `scrot` or ImageMagick (X11) OR `grim` or `gnome-screenshot` (Wayland)
- CEC Utils (for HDMI-CEC TV control - optional)
## 🚀 Quick Start
### 1. Installation
```bash
# Clone repository
cd ~/
git clone <repository-url> infoscreen-dev
cd infoscreen-dev
# Install system dependencies
sudo apt-get update
sudo apt-get install -y \
python3 python3-pip python3-venv \
impressive \
chromium-browser vlc \
cec-utils \
scrot imagemagick
# For Wayland systems, install screenshot tools:
# sudo apt-get install grim gnome-screenshot
# Create Python virtual environment
python3 -m venv venv
source venv/bin/activate
# Install Python dependencies
pip install -r src/requirements.txt
```
### 2. Configuration
Create `.env` file in project root (or copy from `.env.template`):
```bash
# Screenshot capture behavior
SCREENSHOT_ALWAYS=0 # Set to 1 for testing (forces capture even without active display)
# Environment
ENV=production # development | production (CEC disabled in development)
DEBUG_MODE=0 # 1 to enable debug mode
LOG_LEVEL=INFO # DEBUG | INFO | WARNING | ERROR
# MQTT Configuration
MQTT_BROKER=192.168.1.100 # Your MQTT broker IP/hostname
MQTT_PORT=1883
# Timing (seconds)
HEARTBEAT_INTERVAL=60 # How often client sends status updates
SCREENSHOT_INTERVAL=180 # How often simclient transmits screenshots
SCREENSHOT_CAPTURE_INTERVAL=180 # How often display_manager captures screenshots
DISPLAY_CHECK_INTERVAL=15 # How often display_manager checks for new events
# File/API Server (used to download presentation files)
# Defaults to MQTT_BROKER host with port 8000 and http scheme
FILE_SERVER_HOST= # Optional; if empty, defaults to MQTT_BROKER
FILE_SERVER_PORT=8000 # Default API port
FILE_SERVER_SCHEME=http # http or https
# FILE_SERVER_BASE_URL= # Optional full override, e.g., http://192.168.1.100:8000
# HDMI-CEC TV Control (optional)
CEC_ENABLED=true # Enable automatic TV power control
CEC_DEVICE=0 # Target device (0 recommended for TV)
CEC_TURN_OFF_DELAY=30 # Seconds to wait before turning off TV
CEC_POWER_ON_WAIT=5 # Seconds to wait after power ON (for TV boot)
CEC_POWER_OFF_WAIT=5 # Seconds to wait after power OFF
```
### 3. Start Services
```bash
# Start MQTT client (handles events, heartbeat, discovery)
cd ~/infoscreen-dev/src
python3 simclient.py
# In another terminal: Start Display Manager
cd ~/infoscreen-dev/src
python3 display_manager.py
```
Or use the startup script:
```bash
./scripts/start-display-manager.sh
```
## 📊 Presentation System
### How It Works
The system uses **Impressive** as the PDF presenter with native auto-advance and loop support:
1. **Server-side rendering**: PPTX files are converted to PDF by the server using Gotenberg
2. **Client receives PDFs**: Events contain pre-rendered PDF files ready for display
3. **Direct display**: PDF files are displayed directly with Impressive (no client-side conversion needed)
4. **Auto-advance** uses Impressive's built-in `--auto` parameter
5. **Loop mode** uses Impressive's `--wrap` parameter (infinite loop)
6. **Auto-quit** uses Impressive's `--autoquit` parameter (exit after last slide)
### Event JSON Format
#### Looping Presentation (Typical for Events)
```json
{
"id": "event_123",
"start": "2025-10-01 14:00:00",
"end": "2025-10-01 16:00:00",
"presentation": {
"files": [
{
"name": "slides.pptx",
"url": "https://server/files/slides.pptx"
}
],
"auto_advance": true,
"slide_interval": 10,
"loop": true
}
}
```
**Result:** Slides advance every 10 seconds, presentation loops infinitely until event ends.
#### Single Playthrough
```json
{
"presentation": {
"files": [{"name": "welcome.pptx"}],
"auto_advance": true,
"slide_interval": 5,
"loop": false
}
}
```
**Result:** Slides advance every 5 seconds, exits after last slide.
### Presentation Parameters
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `auto_advance` | boolean | `false` | Enable automatic slide advancement |
| `slide_interval` | integer | `10` | Seconds between slides |
| `loop` | boolean | `false` | Loop presentation vs. quit after last slide |
### Scheduler-Specific Fields
The scheduler may send additional fields that are preserved in `current_event.json`:
| Field | Type | Description |
|-------|------|-------------|
| `page_progress` | boolean | Show overall progress bar in presentation (Impressive `--page-progress`). Can be provided at `presentation.page_progress` (preferred) or top-level. |
| `auto_progress` | boolean | Show per-page auto-advance countdown (Impressive `--auto-progress`). Can be provided at `presentation.auto_progress` (preferred) or top-level. |
| `occurrence_of_id` | integer | Original event ID for recurring events |
| `recurrence_rule` | string | iCal recurrence rule (RRULE format) |
| `recurrence_end` | string | End date for recurring events |
**Note:** All fields from the scheduler are automatically preserved when events are stored in `current_event.json`. The client does not filter or modify scheduler-specific metadata.
#### Progress Bar Display
When using Impressive PDF presenter:
- `page_progress: true` - Shows a progress bar at the bottom indicating position in the presentation
- `auto_progress: true` - Shows a countdown progress bar for each slide during auto-advance
- Both options can be enabled simultaneously for maximum visual feedback
## 🎥 Video Events
```json
{
"video": {
"url": "https://server/videos/intro.mp4",
"loop": true,
"autoplay": true,
"volume": 0.8
}
}
```
Notes:
- The Display Manager prefers `python-vlc` (libvlc) when available. This gives programmatic control over playback (autoplay, loop, volume) and ensures the player is cleanly stopped and released when events end.
- Supported video event fields:
- `url` (string): HTTP/HTTPS or streaming URL. URLs using the placeholder host `server` are rewritten to the configured file server (see File/API Server configuration).
- `autoplay` (boolean): start playback automatically when the event becomes active (default: true).
- `loop` (boolean): loop playback indefinitely.
- `volume` (float): 0.01.0 (mapped internally to VLC's 0100 volume scale).
- Effective playback volume is calculated as `event.video.volume * client_config.audio.video_volume_multiplier` and then mapped to VLC's 0100 scale. Example: `volume: 0.8` with `audio.video_volume_multiplier: 0.5` results in 40% VLC volume.
- If `python-vlc` is not installed, the Display Manager will fall back to launching the external `vlc` binary.
- HDMI-CEC remains the recommended mechanism for TV power control only. TV volume via CEC is not implemented because support is device-dependent and much less reliable than controlling VLC directly.
- The client-wide multiplier is intended to be sent over the existing MQTT config topic `infoscreen/{client_id}/config` and is persisted locally in `src/config/client_settings.json` for the Display Manager.
- Fullscreen behavior:
- External VLC fallback uses `--fullscreen`.
- `python-vlc` mode enforces fullscreen on startup and retries fullscreen toggling briefly because video outputs may attach asynchronously.
- For a truly panel-free fullscreen (no taskbar), run the Display Manager inside a minimal kiosk X session or a dedicated user session without a desktop panel.
- Monitoring PID behavior:
- External VLC fallback reports the external `vlc` process PID.
- `python-vlc` mode is in-process, so monitoring reports the `display_manager.py` runtime PID.
## 🌐 Web Events
```json
{
"web": {
"url": "https://dashboard.example.com"
}
}
```
Opens webpage in Chromium kiosk mode (fullscreen, no UI).
## 🗂️ Project Structure
```
infoscreen-dev/
├── .env # Environment configuration
├── README.md # This file
├── IMPRESSIVE_INTEGRATION.md # Detailed presentation system docs
├── src/
│ ├── simclient.py # MQTT client (events, heartbeat, discovery)
│ ├── display_manager.py # Display controller (manages applications)
│ ├── requirements.txt # Python dependencies
│ ├── current_event.json # Current active event (runtime)
│ ├── config/ # Persistent client data
│ │ ├── client_uuid.txt
│ │ └── last_group_id.txt
│ ├── presentation/ # Downloaded presentation files
│ └── screenshots/ # Dashboard screenshots
├── scripts/
│ ├── start-dev.sh # Start development client
│ ├── start-display-manager.sh # Start Display Manager
│ ├── test-display-manager.sh # Interactive testing
│ ├── test-impressive.sh # Test Impressive (auto-quit mode)
│ ├── test-impressive-loop.sh # Test Impressive (loop mode)
│ ├── test-mqtt.sh # Test MQTT connectivity
│ ├── test-screenshot.sh # Test screenshot capture
│ ├── test-utc-timestamps.sh # Test event timing
│ └── present-pdf-auto-advance.sh # PDF presentation wrapper
└── logs/ # Application logs
```
## 🧪 Testing
### Test Display Manager
```bash
./scripts/test-display-manager.sh
```
Interactive menu for testing:
- Check Display Manager status
- Create test events (presentation, video, webpage)
- View active processes
- Cycle through different event types
### Test Impressive Presentation
**Single playthrough (auto-quit):**
```bash
./scripts/test-impressive.sh
```
**Loop mode (infinite):**
```bash
### Test Screenshot Capture
```bash
./scripts/test-screenshot.sh
```
Captures test screenshot for dashboard monitoring.
**Manual test:**
```bash
export SCREENSHOT_ALWAYS=1
export SCREENSHOT_CAPTURE_INTERVAL=5
python3 src/display_manager.py &
sleep 15
ls -lh src/screenshots/
```
```
Verifies MQTT broker connectivity and topics.
### Test Screenshot Capture
```bash
./scripts/test-screenshot.sh
```
Captures test screenshot for dashboard monitoring.
## 🔧 Configuration Details
### Environment Variables
All configuration is done via `.env` file in the project root. Copy `.env.template` to `.env` and adjust values for your environment.
#### Environment
- `ENV` - Environment mode: `development` or `production`
- **Important:** CEC TV control is automatically disabled in `development` mode
- `DEBUG_MODE` - Enable debug output: `1` (on) or `0` (off)
- `LOG_LEVEL` - Logging verbosity: `DEBUG`, `INFO`, `WARNING`, or `ERROR`
#### MQTT Configuration
- `MQTT_BROKER` - **Required.** MQTT broker IP address or hostname
- `MQTT_PORT` - MQTT broker port (default: `1883`)
- `MQTT_USERNAME` - Optional. MQTT authentication username (if broker requires it)
- `MQTT_PASSWORD` - Optional. MQTT authentication password (if broker requires it)
#### Timing Configuration (seconds)
- `HEARTBEAT_INTERVAL` - How often client sends status updates to server (default: `60`)
- `SCREENSHOT_INTERVAL` - How often simclient transmits screenshots via MQTT (default: `180`)
- `SCREENSHOT_CAPTURE_INTERVAL` - How often display_manager captures screenshots (default: `180`)
- `DISPLAY_CHECK_INTERVAL` - How often display_manager checks for new events (default: `15`)
#### Screenshot Configuration
- `SCREENSHOT_ALWAYS` - Force screenshot capture even when no display is active
- `0` - Only capture when presentation/video/web is active (recommended for production)
- `1` - Always capture screenshots (useful for testing)
#### File/API Server Configuration
These settings control how the client downloads presentation files and other content.
- `FILE_SERVER_HOST` - Optional. File server hostname/IP. Defaults to `MQTT_BROKER` if empty
- `FILE_SERVER_PORT` - File server port (default: `8000`)
- `FILE_SERVER_SCHEME` - Protocol: `http` or `https` (default: `http`)
- `FILE_SERVER_BASE_URL` - Optional. Full base URL override (e.g., `http://192.168.1.100:8000`)
- When set, this takes precedence over HOST/PORT/SCHEME settings
#### HDMI-CEC TV Control (Optional)
Automatic TV power management based on event scheduling.
- `CEC_ENABLED` - Enable automatic TV control: `true` or `false`
- **Note:** Automatically disabled when `ENV=development` to avoid TV cycling during testing
- `CEC_DEVICE` - Target CEC device address (recommended: `0` for TV)
- `CEC_TURN_OFF_DELAY` - Seconds to wait before turning off TV after last event ends (default: `30`)
- `CEC_POWER_ON_WAIT` - Seconds to wait after power ON command for TV to boot (default: `5`)
- `CEC_POWER_OFF_WAIT` - Seconds to wait after power OFF command (default: `5`, increase for slower TVs)
### File Server URL Resolution
The MQTT client ([src/simclient.py](src/simclient.py)) downloads presentation files and videos from the configured file server.
**URL Rewriting:**
- Event URLs using placeholder host `server` (e.g., `http://server:8000/...`) are automatically rewritten to the configured file server
- By default, file server = `MQTT_BROKER` host with port `8000` and `http` scheme
- Use `FILE_SERVER_BASE_URL` for complete override, or set individual HOST/PORT/SCHEME variables
**Best practices:**
- Keep inline comments in `.env` after a space and `#` to avoid parsing issues
- Match the scheme (`http`/`https`) to your actual server configuration
- For HTTPS or non-standard ports, explicitly set `FILE_SERVER_SCHEME` and `FILE_SERVER_PORT`
### MQTT Topics
#### Client → Server
- `infoscreen/discovery` - Initial client announcement
- `infoscreen/{client_id}/heartbeat` - Regular status updates
- `infoscreen/{client_id}/dashboard` - Screenshot images (base64)
- `infoscreen/{client_id}/health` - Process health state (`event_id`, process, pid, status)
- `infoscreen/{client_id}/logs/error` - Forwarded client error logs
- `infoscreen/{client_id}/logs/warn` - Forwarded client warning logs
#### Server → Client
- `infoscreen/{client_id}/discovery_ack` - Server response with client ID
- `infoscreen/{client_id}/group_id` - Group assignment
- `infoscreen/events/{group_id}` - Event commands for group
### Client Identification
**Hardware Token:** SHA256 hash of:
- CPU serial number
- MAC addresses (all network interfaces)
**Persistent UUID:** Stored in `src/config/client_uuid.txt`
**Group Membership:** Stored in `src/config/last_group_id.txt`
## 🔍 Troubleshooting
### Display Manager doesn't start presentations
**Check Impressive installation:**
```bash
which impressive
# If not found: sudo apt-get install impressive
```
**Check logs:**
```bash
tail -f logs/display_manager.log
```
**Check disk space:**
```bash
df -h
```
**Note:** PPTX conversion happens server-side via Gotenberg. The client only receives and displays pre-rendered PDF files.
### Slides don't auto-advance
**Verify event JSON:**
- `auto_advance: true` is set
- `slide_interval` is specified (default: 10)
**Test Impressive directly:**
```bash
./scripts/test-impressive.sh
```
### Presentation doesn't loop
**Verify event JSON:**
- `loop: true` is set
**Test loop mode:**
```bash
./scripts/test-impressive-loop.sh
```
### File downloads fail
Symptoms:
- `Failed to resolve 'server'` or `NameResolutionError` when downloading files
- `Invalid URL 'http # http or https://...'` in `logs/simclient.log`
What to check:
- Look for lines like `Lade Datei herunter von:` in `logs/simclient.log` to see the effective URL used
- Ensure the URL host is the MQTT broker IP (or your configured file server), not `server`
- Verify `.env` values dont include inline comments as part of the value (e.g., keep `FILE_SERVER_SCHEME=http` on its own line)
Fixes:
- If your API is on the same host as the broker: leave `FILE_SERVER_HOST` empty (defaults to `MQTT_BROKER`), keep `FILE_SERVER_PORT=8000`, and set `FILE_SERVER_SCHEME=http` or `https`
- To override fully, set `FILE_SERVER_BASE_URL` (e.g., `http://192.168.1.100:8000`); this takes precedence over host/port/scheme
- After changing `.env`, restart the simclient process
Expected healthy log sequence:
- `Lade Datei herunter von: http://<broker-ip>:8000/...`
- Followed by `"GET /... HTTP/1.1" 200` and `Datei erfolgreich heruntergeladen:`
### VLC hardware decode / renderer issues
If you see messages like:
```
[h264_v4l2m2m @ ...] Could not find a valid device
[h264_v4l2m2m @ ...] can't configure decoder
[... ] avcodec decoder error: cannot start codec (h264_v4l2m2m)
```
that indicates libVLC / ffmpeg attempted to use the platform V4L2 M2M hardware decoder but the kernel/device isn't available. Options to resolve:
- Enable the V4L2 M2M codec driver on the system (platform-specific; on Raspberry Pi ensure correct kernel/firmware and codec modules are loaded). Check `v4l2-ctl --list-devices` and `ls /dev/video*` after installing `v4l-utils`.
- Disable hardware decoding so libVLC/ffmpeg uses software decoding (reliable but higher CPU). You can test this by launching the `vlc` binary with:
```bash
vlc --avcodec-hw=none 'http://<your-video-url>'
```
Or modify `src/display_manager.py` to create the libVLC instance with software-decoding forced:
```python
instance = vlc.Instance('--avcodec-hw=none', '--no-video-title-show', '--no-video-deco')
```
This is the fastest workaround if hardware decode is not required or not available on the device.
### MQTT connection issues
**Test broker connectivity:**
```bash
./scripts/test-mqtt.sh
```
### Monitoring and UTC timestamps
Client-side monitoring is implemented with a health-state bridge between `display_manager.py` and `simclient.py`.
- Health bridge file: `src/current_process_health.json`
- Local monitoring log: `logs/monitoring.log`
- Process states: `running`, `crashed`, `stopped`
- Restart tracking: bounded restart attempts per active event
UTC timestamp policy:
- `display_manager.log`, `simclient.log`, and `monitoring.log` are written in UTC (`...Z`)
- MQTT payload timestamps (heartbeat/dashboard/health/log messages) are UTC ISO timestamps
- Screenshot metadata timestamps are UTC ISO timestamps
This prevents daylight-saving and timezone drift issues across clients.
### VLC/PulseAudio warnings in remote sessions
Warnings such as `pulse audio output error: overflow, flushing` can appear when testing through remote desktop/audio forwarding (for example, NoMachine) or virtual/dummy display setups.
- If playback and audio are stable on a real HDMI display, this is usually non-fatal.
- If warnings appear only in remote sessions, treat them as environment-related rather than a core video playback bug.
### Screenshots not uploading
**Check which session type you're running:**
```bash
echo $WAYLAND_DISPLAY # Set if Wayland
echo $DISPLAY # Set if X11
```
**Install appropriate screenshot tool:**
```bash
# For X11:
sudo apt-get install scrot imagemagick
# For Wayland:
sudo apt-get install grim gnome-screenshot
```
**Test screenshot capture:**
```bash
export SCREENSHOT_ALWAYS=1 # Force capture even without active event
./scripts/test-screenshot.sh
ls -lh src/screenshots/
```
**Check logs for session detection:**
```bash
tail -f logs/display_manager.log | grep -i screenshot
# Should show: "Screenshot session=wayland" or "Screenshot session=x11"
```
**Verify simclient is reading screenshots:**
```bash
tail -f logs/simclient.log | grep -i screenshot
# Should show: "Dashboard heartbeat sent with screenshot: latest.jpg"
```ll topic subscriptions are restored in `on_connect` and a discovery message is re-sent on reconnect to re-register the client.
- Heartbeats are sent only when connected; if publish occurs during a brief reconnect window, Paho may return rc=4 (NO_CONN). The client performs a short retry and logs the outcome.
- Occasional `Heartbeat publish failed with code: 4` after broker restart or transient network hiccups is expected and not dangerous. It indicates "not connected at this instant"; the next heartbeat typically succeeds.
- When to investigate: repeated rc=4 with no succeeding "Heartbeat sent" entries over multiple intervals.
### Screenshots not uploading
**Test screenshot capture:**
```bash
./scripts/test-screenshot.sh
ls -l screenshots/
```
**Check DISPLAY variable:**
```bash
echo $DISPLAY # Should be :0
```
## 📚 Documentation
- **IMPRESSIVE_INTEGRATION.md** - Detailed presentation system documentation
- **src/DISPLAY_MANAGER.md** - Display Manager architecture
- **src/IMPLEMENTATION_SUMMARY.md** - Implementation overview
- **src/README.md** - MQTT client documentation
## 🔐 Security
- Hardware-based client identification (non-spoofable)
- Configurable MQTT authentication
- Local-only file storage
- No sensitive data in logs
## 🚢 Production Deployment
### Systemd Service
Create `/etc/systemd/system/infoscreen-display.service`:
```ini
[Unit]
Description=Infoscreen Display Manager
After=network.target
[Service]
Type=simple
User=olafn
WorkingDirectory=/home/olafn/infoscreen-dev/src
Environment="DISPLAY=:0"
Environment="XAUTHORITY=/home/olafn/.Xauthority"
ExecStart=/home/olafn/infoscreen-dev/venv/bin/python3 /home/olafn/infoscreen-dev/src/display_manager.py
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
```
Enable and start:
```bash
sudo systemctl daemon-reload
sudo systemctl enable infoscreen-display
sudo systemctl start infoscreen-display
sudo systemctl status infoscreen-display
```
### Auto-start on Boot
Both services (simclient.py and display_manager.py) should start automatically:
1. **simclient.py** - MQTT communication, event management
2. **display_manager.py** - Display application controller
Create similar systemd service for simclient.py.
### Docker Deployment (Alternative)
```bash
docker-compose -f src/docker-compose.production.yml up -d
```
See `src/CONTAINER_TRANSITION.md` for details.
## 📝 Development
### Development Mode
Set in `.env`:
```bash
ENV=development
DEBUG_MODE=1
LOG_LEVEL=DEBUG
HEARTBEAT_INTERVAL=10
```
### Start Development Client
```bash
./scripts/start-dev.sh
```
### View Logs
```bash
# Display Manager
tail -f logs/display_manager.log
# MQTT Client
tail -f logs/simclient.log
# Both
tail -f logs/*.log
```
## 📺 HDMI-CEC TV Control
The system includes automatic TV power control via HDMI-CEC. The TV turns on when events start and turns off (with delay) when no events are active.
### Development mode behavior
- When `ENV=development`, HDMI-CEC is automatically disabled by the Display Manager to avoid constantly switching the TV during development.
- The test script `scripts/test-hdmi-cec.sh` also respects this: menu option 5 (Display Manager CEC integration) will detect development mode and skip the integration test. Manual options (14) still work for direct cec-client testing.
To test CEC end-to-end, temporarily set `ENV=production` in `.env` and restart the Display Manager, or use the manual commands in the test script.
### Quick Setup
```bash
# Install CEC utilities
sudo apt-get install cec-utils
# Test CEC connection
echo "scan" | cec-client -s -d 1
# Configure in .env
CEC_ENABLED=true
CEC_DEVICE=0 # Use 0 for best performance
CEC_TURN_OFF_DELAY=30
CEC_POWER_ON_WAIT=5 # Adjust if TV is slow to boot
CEC_POWER_OFF_WAIT=2
```
### Features
- **Auto Power On**: TV turns on when event starts
- **Auto Power Off**: TV turns off after configurable delay when events end
- **Smart Switching**: TV stays on when switching between events
- **Configurable Delay**: Prevent rapid on/off cycles
### Testing
```bash
## 📸 Screenshot System
The system includes automatic screenshot capture for dashboard monitoring with support for both X11 and Wayland display servers.
### Architecture
**Two-process design:**
1. **display_manager.py** - Captures screenshots on host OS (has access to display)
2. **simclient.py** - Transmits screenshots via MQTT (runs in container)
3. **Shared directory** - `src/screenshots/` volume-mounted between processes
### Screenshot Capture (display_manager.py)
## Recent changes (Nov 2025)
The following notable changes were added after the previous release and are included in this branch:
### Screenshot System Implementation
- **Screenshot capture** added to `display_manager.py` with background thread
- **Session detection**: Automatic Wayland vs X11 detection with appropriate tool selection
- **Wayland support**: `grim`, `gnome-screenshot`, `spectacle` (in order)
- **X11 support**: `scrot`, `import` (ImageMagick), `xwd`+`convert` (in order)
- **File management**: Timestamped screenshots plus `latest.jpg` symlink, automatic rotation
- **Transmission**: Enhanced `simclient.py` to prefer `latest.jpg`, added detailed logging
- **Dashboard topic**: Structured JSON payload with screenshot, system info, and client status
- **Configuration**: New environment variables `SCREENSHOT_CAPTURE_INTERVAL`, `SCREENSHOT_INTERVAL`, `SCREENSHOT_ALWAYS`
- **Testing mode**: `SCREENSHOT_ALWAYS=1` forces capture even without active display
### Previous Changes (Oct 2025)
- **Wayland**: `grim``gnome-screenshot``spectacle`
- **X11**: `scrot``import` (ImageMagick)`xwd`+`convert`
**Processing pipeline:**
1. Capture full-resolution screenshot to PNG
2. Downscale and compress to JPEG (hardcoded settings in display_manager.py)
3. Save timestamped file: `screenshot_YYYYMMDD_HHMMSS.jpg`
4. Create/update `latest.jpg` symlink for easy access
5. Rotate old screenshots (automatic cleanup)
**Capture timing:**
- Only captures when a display process is active (presentation/video/web)
- Can be forced with `SCREENSHOT_ALWAYS=1` for testing
- Interval configured via `SCREENSHOT_CAPTURE_INTERVAL` (default: 180 seconds)
### Screenshot Transmission (simclient.py)
**Source selection:**
- Prefers `latest.jpg` symlink (fastest, most recent)
- Falls back to newest timestamped file if symlink missing
**MQTT topic:**
```
infoscreen/{client_id}/dashboard
```
**Payload structure:**
```json
{
"timestamp": "2025-11-30T14:23:45.123456",
"client_id": "abc123-def456-789",
"status": "alive",
"screenshot": {
"filename": "latest.jpg",
"data": "<base64-encoded-image>",
"timestamp": "2025-11-30T14:23:40.000000",
"size": 45678
},
"system_info": {
"hostname": "infoscreen-pi-01",
"ip": "192.168.1.50",
"uptime": 1732977825.123456
}
}
```
### Configuration
Configuration is done via environment variables in `.env` file. See the "Environment Variables" section above for complete documentation.
Key settings:
- `SCREENSHOT_CAPTURE_INTERVAL` - How often display_manager.py captures screenshots (default: 180 seconds)
- `SCREENSHOT_INTERVAL` - How often simclient.py transmits screenshots via MQTT (default: 180 seconds)
- `SCREENSHOT_ALWAYS` - Force capture even when no display is active (useful for testing, default: 0)
### Scalability Recommendations
**Small deployments (<10 clients):**
- Default settings work well
- `SCREENSHOT_CAPTURE_INTERVAL=30-60`, `SCREENSHOT_INTERVAL=60`
**Medium deployments (10-50 clients):**
- Reduce capture frequency: `SCREENSHOT_CAPTURE_INTERVAL=60-120`
- Reduce transmission frequency: `SCREENSHOT_INTERVAL=120-180`
- Ensure broker has adequate bandwidth
**Large deployments (50+ clients):**
- Further reduce frequency: `SCREENSHOT_CAPTURE_INTERVAL=180`, `SCREENSHOT_INTERVAL=180-300`
- Monitor MQTT broker load and consider retained message limits
- Consider staggering screenshot intervals across clients
**Very large deployments (200+ clients):**
- Consider HTTP storage + MQTT metadata pattern instead of base64-over-MQTT
- Implement screenshot upload to file server, publish only URL via MQTT
- Implement hash-based deduplication to skip identical screenshots
**Note:** Screenshot image processing (resize, compression quality) is currently hardcoded in [src/display_manager.py](src/display_manager.py). Future versions may expose these as environment variables.
### Troubleshooting
**No screenshots being captured:**
```bash
# Check session type
echo "Wayland: $WAYLAND_DISPLAY" # Set if Wayland
echo "X11: $DISPLAY" # Set if X11
# Check logs for tool detection
tail -f logs/display_manager.log | grep screenshot
# Install appropriate tools
sudo apt install scrot imagemagick # X11
sudo apt install grim # Wayland
```
**Screenshots too large:**
```bash
# Reduce quality and size
SCREENSHOT_MAX_WIDTH=640
SCREENSHOT_JPEG_QUALITY=50
```
**Not transmitting over MQTT:**
```bash
# Check simclient logs
tail -f logs/simclient.log | grep -i dashboard
# Should see:
# "Dashboard heartbeat sent with screenshot: latest.jpg (45678 bytes)"
# If NO_CONN errors, check MQTT broker connectivity
```
---
**Last Updated:** November 2025
**Status:** ✅ Production Ready
**Tested On:** Raspberry Pi 5, Raspberry Pi OS (Bookworm)
## Recent changes (Nov 2025)
echo "on 0" | cec-client -s -d 1 # Turn on
echo "standby 0" | cec-client -s -d 1 # Turn off
echo "pow 0" | cec-client -s -d 1 # Check status
```
### Documentation
See [HDMI_CEC_SETUP.md](HDMI_CEC_SETUP.md) for complete documentation including:
- Detailed setup instructions
- Troubleshooting guide
- TV compatibility information
- Advanced configuration options
## 🤝 Contributing
1. Test changes with `./scripts/test-display-manager.sh`
2. Verify MQTT communication with `./scripts/test-mqtt.sh`
3. Update documentation
4. Submit pull request
## 📄 License
[Add your license here]
## 🆘 Support
For issues or questions:
1. Check logs in `logs/` directory
2. Review troubleshooting section
3. Test individual components with test scripts
4. Check MQTT broker connectivity
---
**Last Updated:** October 2025
**Status:** ✅ Production Ready
**Tested On:** Raspberry Pi 5, Raspberry Pi OS (Bookworm)
## Recent changes (Oct 2025)
The following notable changes were added after the previous release and are included in this branch:
- Event handling: support for scheduler-provided `event_type` values (new types: `presentation`, `webuntis`, `webpage`, `website`). The display manager now prefers `event_type` when selecting which renderer to start.
- Web display: Chromium is launched in kiosk mode for web events. `website` events (scheduler) and legacy `web` keys are both supported and normalized.
- Auto-scroll feature: automatic scrolling for long websites implemented. Two mechanisms are available:
- CDP injection: The display manager attempts to inject a small auto-scroll script via Chrome DevTools Protocol (DevTools websocket) when possible (uses `websocket-client` and `requests`). Default injection duration: 60s.
- Extension fallback: When DevTools websocket handshakes are blocked (403), a tiny local Chrome extension (`src/chrome_autoscroll`) is loaded via `--load-extension` to run a content script that performs the auto-scroll reliably.
- Autoscroll enabled only for scheduler events with `event_type: "website"` (not for general `web` or `webpage`). The extension and CDP injection are only used when autoscroll is requested for that event type.
- New test utilities:
- `scripts/test_cdp.py` — quick DevTools JSON listing + Runtime.evaluate tester
- `scripts/test_cdp_origins.py` — tries several Origin headers to diagnose 403 handshakes
- Dependencies: `src/requirements.txt` updated to include `websocket-client` (used by the CDP injector).
- Small refactors and improved logging in `src/display_manager.py` to make event dispatch and browser injection more robust.
If you rely on autoscroll in production, review the security considerations around `--remote-debugging-port` (DevTools) and prefer the extension fallback if your Chromium build enforces strict websocket Origin policies.