- 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
166 lines
5.8 KiB
Markdown
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)
|