- add process health bridge and monitoring flow between display_manager and simclient - publish health + warn/error log topics over MQTT - standardize log/payload/screenshot timestamps to UTC (Z) to avoid DST drift - improve video handling: python-vlc fullscreen enforcement and runtime PID reporting - update README and copilot instructions with monitoring architecture and troubleshooting - add Phase 3 monitoring implementation documentation - update gitignore for new runtime/log artifacts
32 KiB
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:
scrotor ImageMagick (X11) ORgrimorgnome-screenshot(Wayland) - CEC Utils (for HDMI-CEC TV control - optional)
🚀 Quick Start
1. Installation
# 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):
# 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
# 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:
./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:
- Server-side rendering: PPTX files are converted to PDF by the server using Gotenberg
- Client receives PDFs: Events contain pre-rendered PDF files ready for display
- Direct display: PDF files are displayed directly with Impressive (no client-side conversion needed)
- Auto-advance uses Impressive's built-in
--autoparameter - Loop mode uses Impressive's
--wrapparameter (infinite loop) - Auto-quit uses Impressive's
--autoquitparameter (exit after last slide)
Event JSON Format
Looping Presentation (Typical for Events)
{
"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
{
"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 presentationauto_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
{
"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 hostserverare 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.0–1.0 (mapped internally to VLC's 0–100 volume scale).
- If
python-vlcis not installed, the Display Manager will fall back to launching the externalvlcbinary. - Fullscreen behavior:
- External VLC fallback uses
--fullscreen. python-vlcmode 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.
- External VLC fallback uses
- Monitoring PID behavior:
- External VLC fallback reports the external
vlcprocess PID. python-vlcmode is in-process, so monitoring reports thedisplay_manager.pyruntime PID.
- External VLC fallback reports the external
🌐 Web Events
{
"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
./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):
./scripts/test-impressive.sh
Loop mode (infinite):
### Test Screenshot Capture
```bash
./scripts/test-screenshot.sh
Captures test screenshot for dashboard monitoring.
Manual test:
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:developmentorproduction- Important: CEC TV control is automatically disabled in
developmentmode
- Important: CEC TV control is automatically disabled in
DEBUG_MODE- Enable debug output:1(on) or0(off)LOG_LEVEL- Logging verbosity:DEBUG,INFO,WARNING, orERROR
MQTT Configuration
MQTT_BROKER- Required. MQTT broker IP address or hostnameMQTT_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 active0- 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 toMQTT_BROKERif emptyFILE_SERVER_PORT- File server port (default:8000)FILE_SERVER_SCHEME- Protocol:httporhttps(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:trueorfalse- Note: Automatically disabled when
ENV=developmentto avoid TV cycling during testing
- Note: Automatically disabled when
CEC_DEVICE- Target CEC device address (recommended:0for 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) 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_BROKERhost with port8000andhttpscheme - Use
FILE_SERVER_BASE_URLfor complete override, or set individual HOST/PORT/SCHEME variables
Best practices:
- Keep inline comments in
.envafter 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_SCHEMEandFILE_SERVER_PORT
MQTT Topics
Client → Server
infoscreen/discovery- Initial client announcementinfoscreen/{client_id}/heartbeat- Regular status updatesinfoscreen/{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 logsinfoscreen/{client_id}/logs/warn- Forwarded client warning logs
Server → Client
infoscreen/{client_id}/discovery_ack- Server response with client IDinfoscreen/{client_id}/group_id- Group assignmentinfoscreen/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:
which impressive
# If not found: sudo apt-get install impressive
Check logs:
tail -f logs/display_manager.log
Check disk space:
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: trueis setslide_intervalis specified (default: 10)
Test Impressive directly:
./scripts/test-impressive.sh
Presentation doesn't loop
Verify event JSON:
loop: trueis set
Test loop mode:
./scripts/test-impressive-loop.sh
File downloads fail
Symptoms:
Failed to resolve 'server'orNameResolutionErrorwhen downloading filesInvalid URL 'http # http or https://...'inlogs/simclient.log
What to check:
- Look for lines like
Lade Datei herunter von:inlogs/simclient.logto see the effective URL used - Ensure the URL host is the MQTT broker IP (or your configured file server), not
server - Verify
.envvalues don’t include inline comments as part of the value (e.g., keepFILE_SERVER_SCHEME=httpon its own line)
Fixes:
- If your API is on the same host as the broker: leave
FILE_SERVER_HOSTempty (defaults toMQTT_BROKER), keepFILE_SERVER_PORT=8000, and setFILE_SERVER_SCHEME=httporhttps - 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" 200andDatei 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-devicesandls /dev/video*after installingv4l-utils. - Disable hardware decoding so libVLC/ffmpeg uses software decoding (reliable but higher CPU). You can test this by launching the
vlcbinary with:
vlc --avcodec-hw=none 'http://<your-video-url>'
Or modify src/display_manager.py to create the libVLC instance with software-decoding forced:
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:
./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, andmonitoring.logare 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:
echo $WAYLAND_DISPLAY # Set if Wayland
echo $DISPLAY # Set if X11
Install appropriate screenshot tool:
# For X11:
sudo apt-get install scrot imagemagick
# For Wayland:
sudo apt-get install grim gnome-screenshot
Test screenshot capture:
export SCREENSHOT_ALWAYS=1 # Force capture even without active event
./scripts/test-screenshot.sh
ls -lh src/screenshots/
Check logs for session detection:
tail -f logs/display_manager.log | grep -i screenshot
# Should show: "Screenshot session=wayland" or "Screenshot session=x11"
Verify simclient is reading screenshots:
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:
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:
[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:
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:
- simclient.py - MQTT communication, event management
- display_manager.py - Display application controller
Create similar systemd service for simclient.py.
Docker Deployment (Alternative)
docker-compose -f src/docker-compose.production.yml up -d
See src/CONTAINER_TRANSITION.md for details.
📝 Development
Development Mode
Set in .env:
ENV=development
DEBUG_MODE=1
LOG_LEVEL=DEBUG
HEARTBEAT_INTERVAL=10
Start Development Client
./scripts/start-dev.sh
View Logs
# 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.shalso respects this: menu option 5 (Display Manager CEC integration) will detect development mode and skip the integration test. Manual options (1–4) 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
# 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
## 📸 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. Future versions may expose these as environment variables.
Troubleshooting
No screenshots being captured:
# 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:
# Reduce quality and size
SCREENSHOT_MAX_WIDTH=640
SCREENSHOT_JPEG_QUALITY=50
Not transmitting over MQTT:
# 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.