Files
LEDMatrix/scripts/install/install_mqtt_bridge.sh
T
ChuckBuildsandClaude Opus 5 c7eb09db1d feat(tools): manage the MQTT bridge and Pixlet editor from the Tools tab
Both shipped in #538 with no interface at all: the bridge could only be
configured by editing integrations/mqtt_bridge/bridge_config.json over SSH, and
the Pixlet editor was a script you had to know existed. Neither appeared
anywhere in the web UI. They are now two sections in the Tools tab.

**MQTT bridge.** Service state (installed / running / stopped), install, start,
stop and restart, and a form for every setting: broker host and port, username,
TLS, command topic, client id, API base, request timeout, on-demand duration
and log level.

The password is write-only. GET returns every other field plus a password_set
flag, never the value -- this interface has no authentication, so anything it
hands back is readable by anyone who can reach the port. Submitting a blank
password keeps whatever is stored; there is an explicit Clear for removing one.
The file is written through a temp file in the same directory and chmod 0600, so
a crash mid-write cannot leave a config the bridge would refuse to load. Values
are range-checked server-side (port, timeouts, log level, http(s) API base).

**Pixlet editor.** Lists the apps on disk and starts a session per app, with a
banner showing which app is open, a link to the editor, a countdown, and Stop.
The banner survives a page reload because status is polled while a session runs,
and Edit is disabled while one is open.

Two changes to the script make that safe to drive from a browser:

- It binds 0.0.0.0 by default (PIXLET_EDITOR_HOST overrides). Loopback-only
  meant the URL shown in a browser was unreachable and needed an SSH tunnel,
  which is the confusing path. The no-auth argument for loopback does not hold
  when web_interface already serves 0.0.0.0:5000 unauthenticated and can
  reconfigure everything.
- The session ends by itself after PIXLET_EDITOR_TIMEOUT (default 30 min),
  enforced by `timeout` inside the script rather than by the caller. The display
  is stopped while editing, so a forgotten session would otherwise leave the
  panel dark and read as broken hardware -- the timeout is the whole reason a
  Start button is defensible.

Two bugs found while testing that lifecycle, both of which would have bitten the
Stop button:

- GNU timeout runs its child in a NEW process group, so signalling the script's
  group killed only bash and orphaned pixlet with the port still bound. Now
  --foreground keeps one group, and the EXIT trap kills the recorded child too.
- The liveness check treated a zombie as running: the script is a child of the
  web process and stays unreaped after exiting, and signal 0 succeeds against a
  zombie, so a finished session would have read as running forever. It now reaps
  non-blockingly and falls back to /proc state for a process that is not ours.

Covered by test/js/dom/test_tools_sections.js (26 assertions, real DOM against
the server-rendered partial): form prefill, the blank-means-unchanged password
contract, the running-session banner and countdown, and that the editor link
uses the host the page was loaded from. Also chmod +x on both scripts, which the
docs tell you to run directly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014RRtqXDCnvnY6EQwhT5CV9
2026-09-09 11:20:18 -04:00

65 lines
2.1 KiB
Bash
Executable File

#!/bin/bash
# Home Assistant MQTT bridge installation script.
#
# Optional. Installs integrations/mqtt_bridge as a service so Home Assistant
# can force display modes, toggle power and set brightness over MQTT.
# See integrations/mqtt_bridge/README.md.
set -e
PROJECT_ROOT_DIR=$(cd "$(dirname "$0")/../.." && pwd)
BRIDGE_DIR="$PROJECT_ROOT_DIR/integrations/mqtt_bridge"
SERVICE_NAME="ledmatrix-mqtt-bridge"
UNIT_SRC="$PROJECT_ROOT_DIR/systemd/$SERVICE_NAME.service"
UNIT_DEST="/etc/systemd/system/$SERVICE_NAME.service"
if [ "$EUID" -eq 0 ]; then
SYSTEMCTL_CMD="systemctl"
SUDO=""
else
SYSTEMCTL_CMD="sudo systemctl"
SUDO="sudo"
fi
echo "Installing LED Matrix MQTT bridge"
echo "Project root directory: $PROJECT_ROOT_DIR"
if [ ! -f "$BRIDGE_DIR/bridge_config.json" ]; then
cp "$BRIDGE_DIR/bridge_config.example.json" "$BRIDGE_DIR/bridge_config.json"
chmod 600 "$BRIDGE_DIR/bridge_config.json"
echo ""
echo "⚠ Created $BRIDGE_DIR/bridge_config.json from the example."
echo " Edit it with your broker details, then re-run this script."
echo " The service will refuse to start until the placeholder password is replaced."
echo ""
fi
echo "Installing Python dependencies..."
python3 -m pip install -r "$BRIDGE_DIR/requirements.txt" 2>/dev/null \
|| python3 -m pip install --break-system-packages -r "$BRIDGE_DIR/requirements.txt"
echo "Installing $UNIT_DEST..."
sed "s|__PROJECT_ROOT_DIR__|$PROJECT_ROOT_DIR|g" "$UNIT_SRC" \
| $SUDO tee "$UNIT_DEST" > /dev/null
$SYSTEMCTL_CMD daemon-reload
$SYSTEMCTL_CMD enable "$SERVICE_NAME.service"
$SYSTEMCTL_CMD restart "$SERVICE_NAME.service" || true
echo ""
if $SYSTEMCTL_CMD is-active --quiet "$SERVICE_NAME.service" 2>/dev/null; then
echo "✓ MQTT bridge is running"
echo " The matrix should appear in Home Assistant under Settings > Devices > MQTT."
else
echo "⚠ MQTT bridge is not running. Check the logs:"
echo " sudo journalctl -u $SERVICE_NAME -n 50"
fi
echo ""
echo "Useful commands:"
echo " sudo systemctl status $SERVICE_NAME"
echo " sudo journalctl -u $SERVICE_NAME -f"
echo " sudo systemctl disable --now $SERVICE_NAME # Undo"
echo ""