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

5.8 KiB

Developer Guide

This document is the developer-facing companion to the root 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.

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:

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

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:

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.

TV power coordination references:

Debugging

Logs

tail -f ~/infoscreen-dev/logs/display_manager.log ~/infoscreen-dev/src/simclient.log

Runtime Files

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

mosquitto_sub -h YOUR_BROKER_IP -t 'infoscreen/#'

Screenshots

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.