Files
infoscreen-dev/.github/copilot-instructions.md

393 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Copilot Instructions - Infoscreen Client
## Project Overview
This is an **Infoscreen Client** system for Raspberry Pi that creates digital signage displays. The client communicates with a server via MQTT to display presentations, videos, and web content in kiosk mode. It's designed for educational/research environments where multiple displays need to be centrally managed.
## Architecture & Technology Stack
### Core Technologies
- **Python 3.x** - Main application language
- **MQTT (paho-mqtt)** - Real-time messaging with server
- **Impressive** - PDF presenter with native auto-advance and loop support
- **LibreOffice** - PPTX to PDF conversion (headless mode)
- **Environment Variables** - Configuration management via `.env` files
- **JSON** - Data exchange format for events and configuration
- **Base64** - Screenshot transmission encoding
- **Threading** - Background services (screenshot monitoring)
### System Components
- **Main Client** (`simclient.py`) - Core MQTT client and event processor
- **Display Manager** (`display_manager.py`) - Controls display applications (presentations, videos, web)
- **Discovery System** - Automatic client registration with server
- **Heartbeat Monitoring** - Regular status updates and keepalive
- **Event Processing** - Handles presentation/content switching commands
- **Screenshot Service** - Dashboard monitoring via image capture
- **File Management** - Downloads and manages presentation files
- **Group Management** - Supports organizing clients into groups
## Key Features & Functionality
### MQTT Communication Patterns
- **Discovery**: `infoscreen/discovery``infoscreen/{client_id}/discovery_ack`
- **Heartbeat**: Regular `infoscreen/{client_id}/heartbeat` messages
- **Dashboard**: Screenshot transmission via `infoscreen/{client_id}/dashboard`
- **Group Assignment**: Server sends group via `infoscreen/{client_id}/group_id`
- **Events**: Content commands via `infoscreen/events/{group_id}`
### Event Types Supported
```json
{
"presentation": {
"files": [{"url": "https://server/file.pptx", "filename": "file.pptx"}],
"auto_advance": true,
"slide_interval": 10,
"loop": true
},
"web": {
"url": "https://example.com"
},
"video": {
"url": "https://server/video.mp4",
"loop": false,
"autoplay": true,
"volume": 0.8
}
}
```
### Presentation System (Impressive-Based)
- **PDF files** are displayed natively with Impressive PDF presenter (no conversion needed)
- **PPTX files** are automatically converted to PDF using LibreOffice headless
- **Auto-advance**: Native Impressive `--auto` parameter (no xdotool needed)
- **Loop mode**: Impressive `--wrap` parameter for infinite looping
- **Auto-quit**: Impressive `--autoquit` parameter to exit after last slide
- **Virtual Environment**: Uses venv with pygame + pillow for reliable operation
- **Reliable**: Works consistently on Raspberry Pi without window focus issues
### Client Identification
- **Hardware Token**: SHA256 hash of serial number + MAC addresses
- **Persistent UUID**: Stored in `config/client_uuid.txt`
- **Group Membership**: Persistent group assignment in `config/last_group_id.txt`
## Directory Structure
```
~/infoscreen-dev/
├── .env # Environment configuration
├── README.md # Complete project documentation
├── IMPRESSIVE_INTEGRATION.md # Presentation system details
├── QUICK_REFERENCE.md # Quick command reference
├── .github/ # GitHub configuration
│ └── copilot-instructions.md
├── src/ # Source code
│ ├── simclient.py # MQTT client (event management)
│ ├── display_manager.py # Display controller (Impressive integration)
│ ├── current_event.json # Current active event (runtime)
│ ├── config/ # Persistent client data
│ │ ├── client_uuid.txt
│ │ └── last_group_id.txt
│ ├── presentation/ # Downloaded presentation files & PDFs
│ └── screenshots/ # Screenshot captures for monitoring
├── scripts/ # Production & testing utilities
│ ├── start-dev.sh # Start development client
│ ├── start-display-manager.sh # Start Display Manager
│ ├── test-display-manager.sh # Interactive testing menu
│ ├── test-impressive.sh # Test Impressive (auto-quit)
│ ├── test-impressive-loop.sh # Test Impressive (loop mode)
│ ├── test-mqtt.sh # MQTT connectivity test
│ ├── test-screenshot.sh # Screenshot capture test
│ └── present-pdf-auto-advance.sh # PDF presentation wrapper
├── logs/ # Application logs
│ ├── simclient.log
│ └── display_manager.log
└── venv/ # Python virtual environment
```
## Configuration & Environment Variables
### Development vs Production
- **Development**: `ENV=development`, verbose logging, frequent heartbeats
- **Production**: `ENV=production`, minimal logging, longer intervals
### Key Environment Variables
```bash
# Environment
ENV=development|production
DEBUG_MODE=1|0
LOG_LEVEL=DEBUG|INFO|WARNING|ERROR
# MQTT Configuration
MQTT_BROKER=192.168.1.100 # Primary MQTT broker
MQTT_PORT=1883 # MQTT port
MQTT_BROKER_FALLBACKS=host1,host2 # Fallback brokers
# Timing (seconds)
HEARTBEAT_INTERVAL=10 # Status update frequency
SCREENSHOT_INTERVAL=30 # Dashboard screenshot frequency
# File/API Server (used to download presentation files)
# Defaults to the same host as MQTT_BROKER, port 8000, scheme http.
# If incoming event URLs use host 'server' (or are host-less), simclient rewrites them to this server.
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
```
### File Server URL Resolution
- The MQTT client (`simclient.py`) downloads presentation files listed in events.
- To avoid DNS issues when event URLs use `http://server:8000/...`, the client normalizes such URLs to the configured file server.
- By default, the file server host is the same as `MQTT_BROKER`, with port `8000` and scheme `http`.
- You can override behavior using `.env` variables above; `FILE_SERVER_BASE_URL` takes precedence over individual host/port/scheme.
- Inline comments in `.env` are supported; keep comments after a space and `#` so values stay clean.
## Development Patterns & Best Practices
### Error Handling
- Robust MQTT connection with fallbacks and retries
- Graceful degradation when services unavailable
- Comprehensive logging with rotating file handlers
- Exception handling for all external operations
### State Management
- Event state persisted in `current_event.json`
- Client configuration persisted across restarts
- Group membership maintained with server synchronization
- Clean state transitions (delete old events on group changes)
### Threading Architecture
- Main thread: MQTT communication and heartbeat
- Background thread: Screenshot monitoring service
- Thread-safe operations for shared resources
### File Operations
- Automatic directory creation for all output paths
- Safe file operations with proper exception handling
- Atomic writes for configuration files
- Automatic cleanup of temporary/outdated files
## Development Workflow
### Local Development Setup
1. Clone repository to `~/infoscreen-dev`
2. Create virtual environment: `python3 -m venv venv`
3. Install dependencies: `pip install -r src/requirements.txt` (includes pygame + pillow for PDF slideshows)
4. Configure `.env` file with MQTT broker settings
5. Use `./scripts/start-dev.sh` for MQTT client or `./scripts/start-display-manager.sh` for display manager
6. **Important**: Virtual environment must include pygame and pillow for PDF auto-advance to work
### Testing Components
- `./scripts/test-mqtt.sh` - MQTT connectivity
- `./scripts/test-screenshot.sh` - Screenshot capture
- `./scripts/test-display-manager.sh` - Interactive testing menu
- `./scripts/test-impressive.sh` - Test auto-quit presentation mode
- `./scripts/test-impressive-loop.sh` - Test loop presentation mode
- `./scripts/test-utc-timestamps.sh` - Event timing validation
- Manual event testing via mosquitto_pub or test-display-manager.sh
### Production Deployment
- Docker containerization available (`docker-compose.production.yml`)
- Systemd service integration for auto-start
- Resource limits and health checks configured
- Persistent volume mounts for data
## Code Style & Conventions
### Python Code Standards
- Environment variable parsing with fallback defaults
- Comprehensive docstrings for complex functions
- Logging at appropriate levels (DEBUG/INFO/WARNING/ERROR)
- Type hints where beneficial for clarity
- Exception handling with specific error types
### Configuration Management
- Environment-first configuration (12-factor app principles)
- Support for inline comments in environment values
- Graceful handling of missing/invalid configuration
- Multiple environment file locations for flexibility
### MQTT Message Handling
- JSON message format validation
- Robust payload parsing with fallbacks
- Topic-specific message handlers
- Retained message support where appropriate
## Hardware Considerations
### Target Platform
- **Primary**: Raspberry Pi 4/5 with desktop environment
- **Storage**: SSD recommended for performance
- **Display**: HDMI output for presentation display
- **Network**: WiFi or Ethernet connectivity required
### System Dependencies
- Python 3.x runtime
- Network connectivity for MQTT
- Display server (X11) for screenshot capture
- **Impressive** - PDF presenter with auto-advance (primary presentation tool)
- **pygame** - Required for Impressive (installed in venv)
- **Pillow/PIL** - Required for Impressive PDF rendering (installed in venv)
- **LibreOffice** - PPTX to PDF conversion (headless mode)
- Chromium/Chrome - Web content display (kiosk mode)
- VLC or MPV - Video playbook
### Video playback details (python-vlc)
- The Display Manager now prefers using python-vlc (libvlc) when available for video playback. This enables programmatic control (autoplay, loop, volume) and cleaner termination/cleanup. If python-vlc is not available, the external `vlc` binary is used as a fallback.
- Supported video event fields: `url`, `autoplay` (boolean), `loop` (boolean), `volume` (float 0.0-1.0). The manager converts `volume` to VLC's 0-100 scale.
- URLs using the placeholder host `server` (for example `http://server:8000/...`) are rewritten to the configured file server before playback. The resolution priority is: `FILE_SERVER_BASE_URL` > `FILE_SERVER_HOST` (or `MQTT_BROKER`) + `FILE_SERVER_PORT` + `FILE_SERVER_SCHEME`.
- Hardware-accelerated decoding errors (e.g., `h264_v4l2m2m`) may appear when the platform does not expose a V4L2 M2M device. To avoid these errors the Display Manager can be configured to disable hw-decoding (see README env var `VLC_HW_ACCEL`). By default the manager will attempt hw-acceleration when libvlc supports it.
- Fullscreen / kiosk: the manager will attempt to make libVLC windows fullscreen (remove decorations) when using python-vlc, and the README contains recommended system-level kiosk/session setup for a truly panel-free fullscreen experience.
## Security & Privacy
### Data Protection
- Hardware identification via cryptographic hash
- No sensitive data in plain text logs
- Local storage of minimal required data only
- Secure MQTT communication (configurable)
### Network Security
- Configurable MQTT authentication (if broker requires)
- Firewall-friendly design (outbound connections only)
- Multiple broker fallback for reliability
## Presentation System Architecture
### How It Works
1. **PDF File Received** → Event contains PDF file reference → Use directly (no conversion)
2. **PPTX File Received** → Event contains PPTX file reference
3. **Convert to PDF** → LibreOffice headless: `libreoffice --headless --convert-to pdf`
4. **Cache PDF** → Converted PDF stored in `presentation/` directory
5. **Display with Impressive** → Launch with venv environment and parameters:
- `--fullscreen` - Full screen mode
- `--nooverview` - No slide overview
- `--auto N` - Auto-advance every N seconds
- `--wrap` - Loop infinitely (if `loop: true`)
- `--autoquit` - Exit after last slide (if `loop: false`)
### Key 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 |
### Why Impressive?
-**Native auto-advance** - No xdotool or window management hacks
-**Built-in loop support** - Reliable `--wrap` parameter
-**Works on Raspberry Pi** - No focus/window issues
-**Simple integration** - Clean command-line interface
-**Maintainable** - ~50 lines of code vs. 200+ with xdotool approaches
### Implementation Location
- **File**: `src/display_manager.py`
- **Method**: `start_presentation()`
- **Key Logic**:
1. Check if PDF file (use directly) or PPTX (needs conversion)
2. Convert PPTX to PDF if needed (cached for reuse)
3. Set up virtual environment for Impressive (pygame + pillow)
4. Build Impressive command with appropriate parameters
5. Launch process and monitor
## Common Development Tasks
When working on this codebase:
1. **Adding new event types**: Extend the event processing logic in `display_manager.py``start_display_for_event()`
2. **Modifying presentation behavior**: Update `display_manager.py``start_presentation()`
3. **Configuration changes**: Update environment variable parsing and validation
4. **MQTT topics**: Follow the established `infoscreen/` namespace pattern
5. **Error handling**: Always include comprehensive logging and graceful fallbacks
6. **State persistence**: Use the established `config/` directory pattern
7. **Testing**: Use `./scripts/test-display-manager.sh` for interactive testing
8. **Presentation testing**: Use `./scripts/test-impressive*.sh` scripts
9. **File download host resolution**: If the API server differs from the MQTT broker or uses HTTPS, set `FILE_SERVER_*` in `.env` or adjust `resolve_file_url()` in `src/simclient.py`.
## Troubleshooting Guidelines
### Common Issues
- **MQTT Connection**: Check broker reachability, try fallback brokers
- **Screenshots**: Verify display environment and permissions
- **File Downloads**: Check network connectivity and disk space
- If event URLs use host `server` and DNS fails, the client rewrites to `MQTT_BROKER` by default.
- Ensure `MQTT_BROKER` points to the correct server IP; if the API differs, set `FILE_SERVER_HOST` or `FILE_SERVER_BASE_URL`.
- Match scheme/port via `FILE_SERVER_SCHEME`/`FILE_SERVER_PORT` for HTTPS or non-default ports.
- **Group Changes**: Monitor log for group assignment messages
- **Service Startup**: Check systemd logs and environment configuration
### Debugging Tools
- Log files in `logs/simclient.log` and `logs/display_manager.log` with rotation
- MQTT message monitoring with mosquitto_sub
- Interactive testing menu: `./scripts/test-display-manager.sh`
- Component test scripts: `test-impressive*.sh`, `test-mqtt.sh`, etc.
- Process monitoring: Check for `impressive`, `libreoffice`, `chromium`, `vlc` processes
### File download URL troubleshooting
- Symptoms:
- `Failed to resolve 'server'` or `NameResolutionError` when downloading files
- `Invalid URL 'http # http or https://...'` in `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
- 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:`
## Important Notes for AI Assistants
### Virtual Environment Requirements (Critical)
- **pygame and pillow MUST be installed in venv** - Required for Impressive to work
- **Display manager uses venv context** - Ensures Impressive has access to dependencies
- **Installation command**: `pip install pygame pillow` (already in requirements.txt)
- **If pygame missing**: Impressive will fail with "No module named 'pygame'" error
### Presentation System
- **ALWAYS use Impressive** for PDF presentations (primary solution)
- **DO NOT suggest xdotool approaches** - they failed on Raspberry Pi due to window focus issues
- **DO NOT suggest video conversion** - adds complexity, had black screen issues
- **PDF files are supported natively** - no conversion needed
- **PPTX conversion is automatic** - LibreOffice headless handles PPTX→PDF
- **Virtual environment is required** - pygame + pillow must be available for Impressive
- **Loop mode uses `--wrap`** - not custom scripts or workarounds
- **Auto-quit uses `--autoquit`** - native Impressive parameter
### Testing Approach
- Use `./scripts/test-display-manager.sh` for interactive testing
- Use `./scripts/test-impressive-loop.sh` to verify loop functionality
- Test individual components with specific test scripts
- Always check logs in `logs/` directory for debugging
### Code Changes
- Display logic is in `src/display_manager.py`, not `simclient.py`
- MQTT client (`simclient.py`) writes events to `current_event.json`
- Display Manager reads `current_event.json` and launches appropriate applications
- Two separate processes: simclient.py (MQTT) + display_manager.py (display control)
### Documentation
- **README.md** - Start here for comprehensive overview
- **IMPRESSIVE_INTEGRATION.md** - Deep dive into presentation system
- **QUICK_REFERENCE.md** - Quick commands and examples
- Source code has extensive comments and logging
This system is designed for reliability and ease of maintenance in educational environments with multiple deployed clients. The Impressive-based presentation solution provides native auto-advance and loop support without complex window management hacks.
## Recent changes (Oct 2025)
Summary of additions since the last Copilot instructions update:
- Added `event_type` handling for scheduler events: `presentation`, `webuntis`, `webpage`, `website`. The display manager now uses `event_type` when available to pick the correct display path.
- Implemented auto-scroll for long websites. Two strategies are available:
- CDP injection via DevTools websocket using `websocket-client` and `requests`. The injector attempts to evaluate an auto-scroll script inside the page context. Default duration: 60s.
- Local Chrome extension fallback (`src/chrome_autoscroll`) loaded with `--load-extension` when CDP handshake is blocked (403), ensuring reliable autoscroll operation.
- New helper scripts for debugging DevTools handshake: `scripts/test_cdp.py` and `scripts/test_cdp_origins.py`.
- Updated `requirements.txt` to include `websocket-client`.
Notes for Copilot:
- When adding or modifying display logic, prefer Impressive for PDF presentations and avoid xdotool approaches for slideshow control.
- For web events, ensure autoscroll is only activated for `event_type: "website"` and keep the CDP injection optional/fallback-only when feasible.