Files
homelab/ansible/roles/jmri/templates/jmri-gui.j2
Hermes Agent service account 62e9f13a45 jmri: replace TigerVNC with Xpra for JMRI GUI display
VNC had window management issues and some dialogs wouldn't open correctly.
Xpra runs in rootless mode — each JMRI window appears natively on the
client without a VNC client or XQuartz required.

Changes:
- Remove tigervnc-standalone-server, jmri-vnc.service, .vnc/ directory
- Install xpra, deploy jmri-xpra.service.j2 (systemd unit)
- Update jmri-gui wrapper: DISPLAY=:100, attach instructions printed on launch
- Update defaults: drop VNC vars, add jmri_xpra_display=100
- Rename handler: Restart jmri-vnc -> Restart jmri-xpra

Connect from macOS/Linux:
  xpra attach ssh://jmri@main-street-station/100
2026-08-01 20:14:49 -05:00

111 lines
3.3 KiB
Django/Jinja

#!/bin/bash
# jmri-gui — launch JMRI GUI on Xpra virtual display
# Managed by Ansible — do not edit manually.
#
# Usage (connect as jmri user):
# jmri-gui panelpro Launch PanelPro on Xpra display
# jmri-gui decoderpro Launch DecoderPro on Xpra display
# jmri-gui status Show service status and attach command
#
# Then attach from macOS/Linux:
# xpra attach ssh://jmri@main-street-station/{{ jmri_xpra_display }}
set -euo pipefail
JMRI_DIR="{{ jmri_install_dir }}"
JMRI_SERVICE="jmri.service"
MONITOR_SERVICE="jmri-monitor.service"
XPRA_SERVICE="jmri-xpra.service"
DISPLAY=":{{ jmri_xpra_display }}"
export DISPLAY
usage() {
echo "Usage: jmri-gui <panelpro|decoderpro|status>"
exit 1
}
status() {
echo "=== JMRI daemon ==="
systemctl status "$JMRI_SERVICE" --no-pager -l 2>&1 | head -8
echo ""
echo "=== Xpra display ==="
systemctl status "$XPRA_SERVICE" --no-pager -l 2>&1 | head -5
echo ""
echo "=== Layout monitor ==="
systemctl status "$MONITOR_SERVICE" --no-pager -l 2>&1 | head -5
echo ""
echo "To attach: xpra attach ssh://jmri@$(hostname -f)/{{ jmri_xpra_display }}"
}
launch() {
local app="$1"
local binary
case "$app" in
panelpro) binary="PanelPro" ;;
decoderpro) binary="DecoderPro" ;;
*) usage ;;
esac
# Ensure Xpra display is running
if ! systemctl is-active --quiet "$XPRA_SERVICE" 2>/dev/null; then
echo "Starting Xpra display..."
sudo systemctl start "$XPRA_SERVICE"
sleep 2
fi
# Stop the JMRI daemon if running (we're taking over the hardware connections)
if systemctl is-active --quiet "$JMRI_SERVICE" 2>/dev/null; then
echo "Stopping JMRI daemon..."
sudo systemctl stop "$JMRI_SERVICE"
fi
# Force AWT out of headless mode
export JMRI_OPTIONS="-Djava.awt.headless=false"
echo "Launching $binary on Xpra display $DISPLAY..."
echo ""
echo "Attach from your workstation:"
echo " xpra attach ssh://jmri@$(hostname -f)/{{ jmri_xpra_display }}"
echo ""
"$JMRI_DIR/$binary" &
echo "$binary launched. Attach with xpra to see windows."
echo ""
# Restart daemon if layout switch is still on
if systemctl is-active --quiet "$MONITOR_SERVICE" 2>/dev/null; then
if sudo /opt/jmri-monitor/bin/python3 - <<'EOF'
from decora_wifi import DecoraWiFiSession
from decora_wifi.models.residential_account import ResidentialAccount
import sys
session = DecoraWiFiSession()
person = session.login("{{ jmri_leviton_email }}", "{{ jmri_leviton_password }}")
perms = person.get_residential_permissions()
for perm in perms:
acct_id = perm.data.get('residentialAccountId')
if not acct_id:
continue
acct = ResidentialAccount(session, acct_id)
acct.refresh()
for r in acct.get_residences():
for s in r.get_iot_switches():
if s.data.get('name') == '{{ jmri_leviton_switch_name }}':
sys.exit(0 if s.data.get('power') == 'ON' else 1)
sys.exit(1)
EOF
then
echo "Layout is ON — JMRI daemon will restart when GUI is closed."
else
echo "Layout is OFF — JMRI daemon will not restart."
fi
fi
}
case "${1:-}" in
panelpro|decoderpro) launch "$1" ;;
status) status ;;
*) usage ;;
esac