| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- #!/bin/bash
- # SpoolBuddy kiosk display idle watchdog.
- #
- # Powers the HDMI output off via wlopm after the configured inactivity
- # timeout, driven by swayidle inside the labwc Wayland session. The timeout
- # value is fetched once from the Bambuddy backend on startup so it matches
- # whatever the user picked in SpoolBuddy Settings → Display.
- #
- # Changes made in the UI are applied live: the daemon writes a
- # "reload-timeout N" line to /tmp/spoolbuddy-wake whenever it sees a new
- # value over the heartbeat, and the FIFO loop below kills the current
- # swayidle and starts a fresh one with the new timeout. No kiosk restart
- # is required.
- #
- # Runs in labwc's autostart file as the kiosk user — needs access to
- # WAYLAND_DISPLAY, which it inherits from the parent labwc process.
- set -u
- LOG_FILE="${SPOOLBUDDY_IDLE_LOG:-$HOME/.cache/spoolbuddy-idle.log}"
- mkdir -p "$(dirname "$LOG_FILE")" 2>/dev/null || true
- exec >>"$LOG_FILE" 2>&1
- echo "=== $(date -Is) spoolbuddy-idle starting (pid=$$) ==="
- echo "WAYLAND_DISPLAY=${WAYLAND_DISPLAY:-<unset>}"
- echo "XDG_RUNTIME_DIR=${XDG_RUNTIME_DIR:-<unset>}"
- echo "PATH=$PATH"
- DEFAULT_TIMEOUT=300
- ENV_FILE="${SPOOLBUDDY_ENV_FILE:-/opt/bambuddy/spoolbuddy/.env}"
- OUTPUT="${SPOOLBUDDY_DISPLAY_OUTPUT:-HDMI-A-1}"
- # Wait for labwc to actually bring up its Wayland socket. Autostart fires
- # before labwc finishes exporting WAYLAND_DISPLAY on some systems, which
- # makes swayidle exit immediately.
- if [ -z "${WAYLAND_DISPLAY:-}" ] && [ -n "${XDG_RUNTIME_DIR:-}" ]; then
- for _ in $(seq 1 20); do
- sock=$(ls -1 "$XDG_RUNTIME_DIR"/wayland-* 2>/dev/null | grep -v '\.lock$' | head -n1 || true)
- if [ -n "$sock" ]; then
- WAYLAND_DISPLAY="$(basename "$sock")"
- export WAYLAND_DISPLAY
- echo "auto-detected WAYLAND_DISPLAY=$WAYLAND_DISPLAY"
- break
- fi
- sleep 0.5
- done
- fi
- if [ -z "${XDG_RUNTIME_DIR:-}" ]; then
- XDG_RUNTIME_DIR="/run/user/$(id -u)"
- export XDG_RUNTIME_DIR
- echo "defaulted XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR"
- fi
- if [ -r "$ENV_FILE" ]; then
- set -a
- # shellcheck disable=SC1090
- . "$ENV_FILE"
- set +a
- fi
- BACKEND_URL="${SPOOLBUDDY_BACKEND_URL:-}"
- API_KEY="${SPOOLBUDDY_API_KEY:-}"
- DEVICE_ID="${SPOOLBUDDY_DEVICE_ID:-}"
- # Derive device_id from the first non-loopback NIC MAC address, the same
- # algorithm daemon/config.py uses so installs without an explicit
- # SPOOLBUDDY_DEVICE_ID still match.
- if [ -z "$DEVICE_ID" ]; then
- for iface in $(ls -1 /sys/class/net/ 2>/dev/null | sort); do
- [ "$iface" = "lo" ] && continue
- addr_file="/sys/class/net/$iface/address"
- [ -r "$addr_file" ] || continue
- mac=$(tr -d ':' < "$addr_file" 2>/dev/null)
- if [ -n "$mac" ] && [ "$mac" != "000000000000" ]; then
- DEVICE_ID="sb-$mac"
- break
- fi
- done
- fi
- TIMEOUT="$DEFAULT_TIMEOUT"
- if [ -n "$BACKEND_URL" ] && [ -n "$API_KEY" ] && [ -n "$DEVICE_ID" ]; then
- response=$(curl -fsS --max-time 10 \
- -H "Authorization: Bearer $API_KEY" \
- "$BACKEND_URL/api/v1/spoolbuddy/devices/$DEVICE_ID/display" 2>/dev/null || true)
- if [ -n "$response" ]; then
- fetched=$(printf '%s' "$response" | jq -r '.blank_timeout // empty' 2>/dev/null || true)
- if [ -n "$fetched" ] && [ "$fetched" -eq "$fetched" ] 2>/dev/null; then
- TIMEOUT="$fetched"
- fi
- fi
- fi
- # FIFO for the SpoolBuddy daemon to talk to this watchdog from outside the
- # Wayland session. Two messages are understood:
- # wake — turn the display on (NFC tag scan, scale weight change)
- # reload-timeout N — kill swayidle and restart it with timeout=N
- WAKE_FIFO="/tmp/spoolbuddy-wake"
- rm -f "$WAKE_FIFO"
- mkfifo -m 622 "$WAKE_FIFO"
- echo "wake FIFO created at $WAKE_FIFO"
- SWAYIDLE_PID=""
- REBLANK_PID=""
- start_swayidle() {
- [ "$TIMEOUT" -gt 0 ] || return 0
- swayidle -w \
- timeout "$TIMEOUT" "wlopm --off $OUTPUT" \
- resume "wlopm --on $OUTPUT" &
- SWAYIDLE_PID=$!
- echo "swayidle started (pid=$SWAYIDLE_PID, timeout=$TIMEOUT, output=$OUTPUT)"
- }
- stop_swayidle() {
- if [ -n "$SWAYIDLE_PID" ]; then
- kill "$SWAYIDLE_PID" 2>/dev/null || true
- wait "$SWAYIDLE_PID" 2>/dev/null || true
- SWAYIDLE_PID=""
- fi
- if [ -n "$REBLANK_PID" ]; then
- kill "$REBLANK_PID" 2>/dev/null || true
- REBLANK_PID=""
- fi
- }
- cleanup() {
- stop_swayidle
- rm -f "$WAKE_FIFO"
- exit 0
- }
- trap cleanup TERM INT HUP
- start_swayidle
- # Open the FIFO read+write so EOF never arrives even when the daemon
- # (the writer) momentarily disconnects between messages — without this,
- # `read` would return immediately the first time the daemon closes its
- # write end and the loop would spin.
- exec 3<>"$WAKE_FIFO"
- while IFS= read -r line <&3; do
- case "$line" in
- wake)
- wlopm --on "$OUTPUT" 2>/dev/null || true
- # Cancel any pending re-blank timer, then start a new one
- # at the *current* timeout (swayidle doesn't know about
- # FIFO wakes so it won't re-blank on its own).
- [ -n "$REBLANK_PID" ] && kill "$REBLANK_PID" 2>/dev/null || true
- REBLANK_PID=""
- if [ "$TIMEOUT" -gt 0 ]; then
- (sleep "$TIMEOUT" && wlopm --off "$OUTPUT" 2>/dev/null) &
- REBLANK_PID=$!
- fi
- ;;
- reload-timeout\ *)
- new_timeout="${line#reload-timeout }"
- # Validate: must be a non-negative integer.
- if [ "$new_timeout" -eq "$new_timeout" ] 2>/dev/null && [ "$new_timeout" -ge 0 ]; then
- if [ "$new_timeout" != "$TIMEOUT" ]; then
- echo "reload-timeout: $TIMEOUT -> $new_timeout"
- stop_swayidle
- TIMEOUT="$new_timeout"
- start_swayidle
- # Bring the display back on so the user sees the
- # change took effect (a setting saved while the
- # screen was already blanked would otherwise look
- # ignored until the next touch).
- wlopm --on "$OUTPUT" 2>/dev/null || true
- fi
- else
- echo "ignoring invalid reload-timeout payload: $new_timeout"
- fi
- ;;
- '')
- : # ignore empty lines (e.g. opening the FIFO with no payload)
- ;;
- *)
- echo "unknown FIFO message: $line"
- ;;
- esac
- done
- cleanup
|