- 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
922 lines
33 KiB
Markdown
922 lines
33 KiB
Markdown
# 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.0–1.0 (mapped internally to VLC's 0–100 volume scale).
|
||
- Effective playback volume is calculated as `event.video.volume * client_config.audio.video_volume_multiplier` and then mapped to VLC's 0–100 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 don’t 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 (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
|
||
|
||
```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.
|