Files
infoscreen-dev/src/README.md
RobbStarkAustria 0cd0d95612 feat: remote commands, systemd units, process observability, broker auth split
- Command intake (reboot/shutdown) on infoscreen/{uuid}/commands with ack lifecycle
- MQTT_USER/MQTT_PASSWORD_BROKER split from identity vars; configure_mqtt_security() updated
- infoscreen-simclient.service: Type=notify, WatchdogSec=60, Restart=on-failure
- infoscreen-notify-failure@.service + script: retained MQTT alert when systemd gives up (Gap 3)
- _sd_notify() watchdog keepalive in simclient main loop (Gap 1)
- broker_connection block in health payload: reconnect_count, last_disconnect_at (Gap 2)
- COMMAND_MOCK_REBOOT_IMMEDIATE_COMPLETE canary flag with safety guard
- SERVER_TEAM_ACTIONS.md: server-side integration action items
- Docs: README, CHANGELOG, src/README, copilot-instructions updated
- 43 tests passing
2026-04-05 08:36:50 +02:00

166 lines
5.8 KiB
Markdown

# Developer Guide
This document is the developer-facing companion to the root [README.md](../README.md). It focuses on code structure, runtime boundaries, MQTT flow, and debugging during implementation work.
For installation, operator usage, and deployment, start at [README.md](../README.md).
## Architecture
The client is split into two cooperating processes:
- `simclient.py`: MQTT communication, discovery, group assignment, event intake, heartbeat, dashboard publishing, power-intent intake.
- `display_manager.py`: event polling, display orchestration, HDMI-CEC, screenshots, local process health state.
Primary runtime flow:
1. `simclient.py` receives group and event messages over MQTT.
2. It writes the active event into `current_event.json`.
3. `display_manager.py` polls that file and starts or stops the display process.
4. `display_manager.py` writes health, screenshot, and power telemetry files.
5. `simclient.py` publishes dashboard, health, and power-state messages.
## Key Files
- `display_manager.py`: display lifecycle, HDMI-CEC, screenshots, local fallback logic.
- `simclient.py`: MQTT callbacks, event persistence, dashboard publishing, power-intent validation, command intake.
- `current_event.json`: active event state consumed by the display manager.
- `current_process_health.json`: local health bridge for monitoring.
- `power_intent_state.json`: latest validated power intent from MQTT.
- `power_state.json`: latest applied power action telemetry.
- `screenshots/meta.json`: screenshot metadata used by the dashboard path.
- `../scripts/start-simclient.sh`: launcher for `simclient.py` (used by the systemd unit).
- `../scripts/start-display-manager.sh`: launcher for `display_manager.py`.
- `../scripts/infoscreen-simclient.service`: systemd unit for `simclient.py`.
- `../scripts/infoscreen-display.service`: systemd unit for `display_manager.py`.
- `../scripts/infoscreen-notify-failure@.service`: systemd template unit; fires on `OnFailure=`.
- `../scripts/infoscreen-notify-failure.sh`: publishes `service_failed` MQTT alert when a unit gives up.
## Developer Workflow
On deployed devices, both processes are managed by systemd:
```bash
# Start / stop / restart
sudo systemctl start infoscreen-simclient infoscreen-display
sudo systemctl restart infoscreen-simclient infoscreen-display
# Follow logs
journalctl -u infoscreen-simclient -u infoscreen-display -f
```
First-time systemd setup:
```bash
sudo cp scripts/infoscreen-simclient.service /etc/systemd/system/
sudo cp scripts/infoscreen-display.service /etc/systemd/system/
sudo cp scripts/infoscreen-notify-failure@.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable infoscreen-simclient infoscreen-display
```
Or run `src/pi-setup.sh` which includes the above as step 14.
For local development without systemd:
```bash
cd ~/infoscreen-dev
source venv/bin/activate
# Terminal 1
./scripts/start-simclient.sh
# Terminal 2
./scripts/start-display-manager.sh
```
Useful helpers:
- `./dev-workflow.sh`
- `./scripts/test-display-manager.sh`
- `./scripts/test-mqtt.sh`
- `./scripts/test-screenshot.sh`
- `./scripts/test-power-intent.sh`
- `./scripts/test-progress-bars.sh`
## MQTT Topics
### Client → Server
- `infoscreen/discovery`
- `infoscreen/{client_id}/heartbeat`
- `infoscreen/{client_id}/dashboard`
- `infoscreen/{client_id}/health` — includes `broker_connection` block with `reconnect_count`, `last_disconnect_at`
- `infoscreen/{client_id}/power/state`
- `infoscreen/{client_id}/commands/ack` — command acknowledgement (states: `accepted`, `rejected`, `execution_started`, `completed`, `failed`)
- `infoscreen/{client_id}/command/ack` — legacy ack topic (also published for compatibility)
- `infoscreen/{client_id}/service_failed` — retained alert published by `infoscreen-notify-failure.sh` when systemd gives up restarting a unit
### Server → Client
- `infoscreen/{client_id}/discovery_ack`
- `infoscreen/{client_id}/group_id`
- `infoscreen/events/{group_id}`
- `infoscreen/groups/{group_id}/power/intent`
- `infoscreen/{client_id}/commands` — remote command intake (`reboot`, `shutdown`)
## Event and Display Notes
Supported runtime content categories:
- presentation
- video
- web / webpage / website / webuntis
Presentation behavior is documented in [../IMPRESSIVE_INTEGRATION.md](../IMPRESSIVE_INTEGRATION.md).
TV power coordination references:
- [../TV_POWER_INTENT_SERVER_CONTRACT_V1.md](../TV_POWER_INTENT_SERVER_CONTRACT_V1.md)
- [../TV_POWER_RUNBOOK.md](../TV_POWER_RUNBOOK.md)
## Debugging
### Logs
```bash
tail -f ~/infoscreen-dev/logs/display_manager.log ~/infoscreen-dev/src/simclient.log
```
### Runtime Files
```bash
cat ~/infoscreen-dev/src/current_event.json
cat ~/infoscreen-dev/src/current_process_health.json
cat ~/infoscreen-dev/src/power_intent_state.json
cat ~/infoscreen-dev/src/power_state.json
```
### MQTT Inspection
```bash
mosquitto_sub -h YOUR_BROKER_IP -t 'infoscreen/#'
```
### Screenshots
```bash
ls -lh ~/infoscreen-dev/src/screenshots/
cat ~/infoscreen-dev/src/screenshots/meta.json
```
## Environment Notes
- `ENV=development` disables HDMI-CEC in the display manager.
- `POWER_CONTROL_MODE` controls local vs hybrid vs mqtt power behavior.
- `COMMAND_HELPER_PATH` points to the shell script that executes privileged commands (reboot/shutdown). Use `mock-command-helper.sh` for local testing.
- `COMMAND_MOCK_REBOOT_IMMEDIATE_COMPLETE=1` makes a mock reboot complete immediately instead of waiting for process restart. Only works when the helper basename is `mock-command-helper.sh`.
- File download host rewriting is handled in `simclient.py` using `FILE_SERVER_*` settings.
## Related Documents
- [../README.md](../README.md)
- [DISPLAY_MANAGER.md](DISPLAY_MANAGER.md)
- [IMPLEMENTATION_SUMMARY.md](IMPLEMENTATION_SUMMARY.md)
- [../CLIENT_MONITORING_SETUP.md](../CLIENT_MONITORING_SETUP.md)
- [../SCREENSHOT_MQTT_FIX.md](../SCREENSHOT_MQTT_FIX.md)