spoolbuddy-idle.sh 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #!/bin/bash
  2. # SpoolBuddy kiosk display idle watchdog.
  3. #
  4. # Powers the HDMI output off via wlopm after the configured inactivity
  5. # timeout, driven by swayidle inside the labwc Wayland session. The timeout
  6. # value is fetched once from the Bambuddy backend on startup so it matches
  7. # whatever the user picked in SpoolBuddy Settings → Display. Changes made
  8. # in the UI take effect on the next reboot / kiosk restart.
  9. #
  10. # Runs in labwc's autostart file as the kiosk user — needs access to
  11. # WAYLAND_DISPLAY, which it inherits from the parent labwc process.
  12. set -u
  13. LOG_FILE="${SPOOLBUDDY_IDLE_LOG:-$HOME/.cache/spoolbuddy-idle.log}"
  14. mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null || true
  15. exec >>"$LOG_FILE" 2>&1
  16. echo "=== $(date -Is) spoolbuddy-idle starting (pid=$$) ==="
  17. echo "WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-<unset>}"
  18. echo "XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-<unset>}"
  19. echo "PATH=$PATH"
  20. DEFAULT_TIMEOUT=300
  21. ENV_FILE="${SPOOLBUDDY_ENV_FILE:-/opt/bambuddy/spoolbuddy/.env}"
  22. OUTPUT="${SPOOLBUDDY_DISPLAY_OUTPUT:-HDMI-A-1}"
  23. # Wait for labwc to actually bring up its Wayland socket. Autostart fires
  24. # before labwc finishes exporting WAYLAND_DISPLAY on some systems, which
  25. # makes swayidle exit immediately.
  26. if [ -z "${WAYLAND_DISPLAY:-}" ] && [ -n "${XDG_RUNTIME_DIR:-}" ]; then
  27. for _ in $(seq 1 20); do
  28. sock=$(ls -1 "$XDG_RUNTIME_DIR"/wayland-* 2>/dev/null | grep -v '\.lock$' | head -n1 || true)
  29. if [ -n "$sock" ]; then
  30. WAYLAND_DISPLAY="$(basename "$sock")"
  31. export WAYLAND_DISPLAY
  32. echo "auto-detected WAYLAND_DISPLAY=$WAYLAND_DISPLAY"
  33. break
  34. fi
  35. sleep 0.5
  36. done
  37. fi
  38. if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
  39. XDG_RUNTIME_DIR="/run/user/$(id -u)"
  40. export XDG_RUNTIME_DIR
  41. echo "defaulted XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
  42. fi
  43. if [ -r "$ENV_FILE" ]; then
  44. set -a
  45. # shellcheck disable=SC1090
  46. . "$ENV_FILE"
  47. set +a
  48. fi
  49. BACKEND_URL="${SPOOLBUDDY_BACKEND_URL:-}"
  50. API_KEY="${SPOOLBUDDY_API_KEY:-}"
  51. DEVICE_ID="${SPOOLBUDDY_DEVICE_ID:-}"
  52. # Derive device_id from the first non-loopback NIC MAC address, the same
  53. # algorithm daemon/config.py uses so installs without an explicit
  54. # SPOOLBUDDY_DEVICE_ID still match.
  55. if [ -z "$DEVICE_ID" ]; then
  56. for iface in $(ls -1 /sys/class/net/ 2>/dev/null | sort); do
  57. [ "$iface" = "lo" ] && continue
  58. addr_file="/sys/class/net/$iface/address"
  59. [ -r "$addr_file" ] || continue
  60. mac=$(tr -d ':' < "$addr_file" 2>/dev/null)
  61. if [ -n "$mac" ] && [ "$mac" != "000000000000" ]; then
  62. DEVICE_ID="sb-$mac"
  63. break
  64. fi
  65. done
  66. fi
  67. TIMEOUT="$DEFAULT_TIMEOUT"
  68. if [ -n "$BACKEND_URL" ] && [ -n "$API_KEY" ] && [ -n "$DEVICE_ID" ]; then
  69. response=$(curl -fsS --max-time 10 \
  70. -H "Authorization: Bearer $API_KEY" \
  71. "$BACKEND_URL/api/v1/spoolbuddy/devices/$DEVICE_ID/display" 2>/dev/null || true)
  72. if [ -n "$response" ]; then
  73. fetched=$(printf '%s' "$response" | jq -r '.blank_timeout // empty' 2>/dev/null || true)
  74. if [ -n "$fetched" ] && [ "$fetched" -eq "$fetched" ] 2>/dev/null; then
  75. TIMEOUT="$fetched"
  76. fi
  77. fi
  78. fi
  79. # FIFO for the SpoolBuddy daemon to request display wake from outside the
  80. # Wayland session (NFC tag scan, scale weight change). The daemon writes
  81. # "wake\n" to this pipe; the monitor loop below calls wlopm --on.
  82. WAKE_FIFO="/tmp/spoolbuddy-wake"
  83. rm -f "$WAKE_FIFO"
  84. mkfifo -m 622 "$WAKE_FIFO"
  85. echo "wake FIFO created at $WAKE_FIFO"
  86. if [ "$TIMEOUT" -le 0 ]; then
  87. # Blanking explicitly disabled — just monitor the wake FIFO so NFC/scale
  88. # wake still works even without swayidle.
  89. echo "timeout<=0, monitoring wake FIFO only (no swayidle)"
  90. while read -r _ < "$WAKE_FIFO"; do
  91. wlopm --on "$OUTPUT" 2>/dev/null || true
  92. done
  93. exit 0
  94. fi
  95. echo "starting swayidle with timeout=$TIMEOUT output=$OUTPUT"
  96. swayidle -w \
  97. timeout "$TIMEOUT" "wlopm --off $OUTPUT" \
  98. resume "wlopm --on $OUTPUT" &
  99. SWAYIDLE_PID=$!
  100. # Monitor wake FIFO — when the daemon writes to it, turn the display on
  101. # and schedule a re-blank after TIMEOUT seconds (swayidle doesn't know about
  102. # FIFO wakes so it won't re-blank on its own).
  103. REBLANK_PID=""
  104. while read -r _ < "$WAKE_FIFO"; do
  105. wlopm --on "$OUTPUT" 2>/dev/null || true
  106. # Cancel any pending re-blank timer, then start a new one
  107. [ -n "$REBLANK_PID" ] && kill "$REBLANK_PID" 2>/dev/null || true
  108. (sleep "$TIMEOUT" && wlopm --off "$OUTPUT" 2>/dev/null) &
  109. REBLANK_PID=$!
  110. done &
  111. FIFO_PID=$!
  112. # If either process exits, clean up and exit.
  113. wait -n "$SWAYIDLE_PID" "$FIFO_PID" 2>/dev/null
  114. echo "child exited, cleaning up"
  115. kill "$SWAYIDLE_PID" "$FIFO_PID" 2>/dev/null || true
  116. rm -f "$WAKE_FIFO"