#!/usr/bin/env bash # LuxStats background launcher for Arch Linux # # * Auto-detects every installed CPython 3 and uses the newest one # * Runs WITHOUT a venv when the system python already has the deps # (pacman: python-pyqt5 python-pyserial python-psutil) # * Otherwise creates/uses a managed venv at # ${XDG_DATA_HOME:-~/.local/share}/luxstats/venv and installs deps there # * Launches the configurator detached from the terminal, logging to # ${XDG_STATE_HOME:-~/.local/state}/luxstats/luxstats.log # # Usage: ./luxstats.sh start in the background # ./luxstats.sh stop stop a running instance # ./luxstats.sh status show whether it's running set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # works both from the repo root (host/ beside this script) and from packaging/ if [[ -f "$SCRIPT_DIR/host/luxstats_configurator.py" ]]; then APP="$SCRIPT_DIR/host/luxstats_configurator.py" elif [[ -f "$SCRIPT_DIR/../host/luxstats_configurator.py" ]]; then APP="$(cd "$SCRIPT_DIR/../host" && pwd)/luxstats_configurator.py" else echo "luxstats: cannot locate host/luxstats_configurator.py" >&2 exit 1 fi DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/luxstats" STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/luxstats" VENV="$DATA_DIR/venv" LOG="$STATE_DIR/luxstats.log" PIDFILE="$STATE_DIR/luxstats.pid" DEPS_IMPORT='import PyQt5, serial, psutil' mkdir -p "$DATA_DIR" "$STATE_DIR" find_newest_python() { local best="" bestver=0 py ver base # scan PATH + common locations for pythonN.M binaries for py in $(compgen -c python3 | sort -u) /usr/bin/python3 \ /usr/local/bin/python3.*; do base="$(basename "$py" 2>/dev/null || true)" [[ "$base" =~ ^python3(\.[0-9]+)?$ ]] || continue command -v "$py" >/dev/null 2>&1 || [[ -x "$py" ]] || continue ver="$("$py" -c 'import sys;print(sys.version_info[0]*1000+sys.version_info[1])' \ 2>/dev/null)" || continue if (( ver > bestver )); then bestver=$ver; best="$py"; fi done [[ -n "$best" ]] || { echo "luxstats: no python3 found" >&2; return 1; } echo "$best" } pick_interpreter() { local sys_py; sys_py="$(find_newest_python)" echo "using system interpreter candidate: $sys_py" \ "($("$sys_py" -V 2>&1))" >&2 if "$sys_py" -c "$DEPS_IMPORT" 2>/dev/null; then echo "$sys_py" # no venv needed return fi echo "system python lacks PyQt5/pyserial/psutil - using managed venv" >&2 echo "(tip: 'sudo pacman -S python-pyqt5 python-pyserial python-psutil'" \ "avoids the venv entirely)" >&2 if [[ ! -x "$VENV/bin/python" ]] \ || ! "$VENV/bin/python" -c "$DEPS_IMPORT" 2>/dev/null; then "$sys_py" -m venv --clear "$VENV" "$VENV/bin/pip" install --quiet --upgrade pip "$VENV/bin/pip" install --quiet PyQt5 pyserial psutil fi echo "$VENV/bin/python" } is_running() { [[ -f "$PIDFILE" ]] && kill -0 "$(cat "$PIDFILE")" 2>/dev/null } case "${1:-start}" in start) if is_running; then echo "luxstats already running (pid $(cat "$PIDFILE"))" exit 0 fi PY="$(pick_interpreter)" echo "launching with $PY" setsid "$PY" "$APP" >>"$LOG" 2>&1 < /dev/null & echo $! > "$PIDFILE" disown echo "luxstats started in the background (pid $(cat "$PIDFILE"))" echo "log: $LOG" ;; stop) if is_running; then kill "$(cat "$PIDFILE")" && rm -f "$PIDFILE" echo "luxstats stopped" else echo "luxstats is not running" fi ;; status) if is_running; then echo "running (pid $(cat "$PIDFILE"))" else echo "not running" fi ;; *) echo "usage: $0 [start|stop|status]" >&2 exit 1 ;; esac