Files
infoscreen-dev/README.md
RobbStarkAustria d6090a6179 fix(screenshots): harden event-triggered MQTT screenshot flow and cleanup docs
- fix race where periodic captures could overwrite pending event_start and event_stop metadata before simclient published
- keep latest.jpg and meta.json synchronized so triggered screenshots are not lost
- add stale pending trigger self-healing to recover from old or invalid metadata states
- improve non-interactive capture reliability with DISPLAY and XAUTHORITY fallbacks
- allow periodic idle captures in development mode so dashboard previews stay fresh without active events
- add deeper simclient screenshot diagnostics for trigger and metadata handling
- add regression test script for metadata preservation and trigger delivery
- add root-cause and fix documentation for the screenshot MQTT issue
- align and deduplicate README screenshot and troubleshooting sections; update release notes to March 2026
- fix scripts/start-dev.sh .env loading to ignore comments safely and remove export invalid identifier warnings
2026-03-29 10:38:29 +02:00

27 KiB
Raw Blame History

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

# 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:

  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)

{
  "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 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

{
  "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.
  • External VLC audio rendering behavior:
    • When muted: true (or effective volume resolves to 0), fallback starts VLC with --no-audio.
    • When not muted, fallback applies startup loudness with --gain=<0.00-1.00> derived from effective volume.
    • Runtime volume updates are best-effort in python-vlc mode; external VLC fallback is startup-parameter based.
  • 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

{
  "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):

./scripts/test-impressive-loop.sh

Test MQTT Connectivity

./scripts/test-mqtt.sh

Verifies MQTT broker connectivity and topic access.

Test Screenshot Capture

./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/

🔧 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 - In production: capture only when a display process is active; in development: periodic idle captures are allowed so dashboard stays fresh
    • 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) 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:

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: true is set
  • slide_interval is specified (default: 10)

Test Impressive directly:

./scripts/test-impressive.sh

Presentation doesn't loop

Verify event JSON:

  • loop: true is set

Test loop mode:

./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:
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

MQTT reconnect and heartbeat behavior

  • On reconnect, the client re-subscribes all topics in on_connect and re-sends discovery to re-register.
  • Heartbeats are sent only when connected. During brief reconnect windows, Paho may return rc=4 (NO_CONN).
  • A single rc=4 warning after broker restarts or short network stalls is expected; the next heartbeat usually succeeds.
  • Investigate only if rc=4 repeats across multiple intervals without subsequent successful heartbeat logs.

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:

echo $WAYLAND_DISPLAY  # Set if Wayland
echo $DISPLAY          # Set if X11
echo $XAUTHORITY       # Should point to ~/.Xauthority for X11 captures

If DISPLAY is empty for non-interactive starts (systemd/nohup/ssh), the display manager now falls back to :0 and tries ~/.Xauthority automatically.

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"

If you see stale dashboard images after restarts:

cat src/screenshots/meta.json
stat src/screenshots/latest.jpg
  • If send_immediately is stuck true for old metadata, restart both processes so simclient consumes and clears it.
  • If latest.jpg timestamp does not move while new screenshot_*.jpg files appear, update to latest code (fix for periodic latest.jpg update path) and restart display_manager.

Verify simclient is reading screenshots:

tail -f logs/simclient.log | grep -i screenshot
# Should show: "Dashboard heartbeat sent with screenshot: latest.jpg"

📚 Documentation

  • IMPRESSIVE_INTEGRATION.md - Detailed presentation system documentation
  • HDMI_CEC_SETUP.md - HDMI-CEC setup and troubleshooting
  • 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:

  1. simclient.py - MQTT communication, event management
  2. 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.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

# 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

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

🤝 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: March 2026
Status: Production Ready
Tested On: Raspberry Pi 5, Raspberry Pi OS (Bookworm)

Recent Changes

November 2025

  • Screenshot pipeline implemented with a two-process model (display_manager.py capture, simclient.py transmission).
  • Wayland/X11 screenshot tool fallback chains added.
  • Dashboard payload format extended with screenshot and system metadata.
  • Scheduler event type support extended (presentation, webuntis, webpage, website).
  • Website autoscroll support added (CDP injection + extension fallback).

March 2026

  • Event-trigger screenshots (event_start, event_stop) hardened against periodic overwrite races.
  • latest.jpg and meta.json synchronization improved for reliable dashboard updates.
  • Stale/invalid pending trigger metadata now self-heals instead of blocking periodic updates.
  • Display environment fallbacks (DISPLAY=:0, XAUTHORITY) improved for non-interactive starts.
  • Development mode allows periodic idle captures to keep dashboard previews fresh when no event is active.