From 25da5b46adbca08c25db060ad88784fb42b25647 Mon Sep 17 00:00:00 2001 From: Sean Doran Date: Fri, 27 Feb 2026 13:54:57 -0500 Subject: [PATCH] Fix: auto-enable service on install, fix service startup, add config hot-reload; bump to v0.3.3 --- .SRCINFO | 5 ++-- PKGBUILD | 3 ++- contrib/turnupd.service | 8 +++--- pyproject.toml | 2 +- src/turnup/__init__.py | 2 +- src/turnup/turnupd.py | 54 ++++++++++++++++++++++++++++------------- turnupd.install | 25 +++++++++++++++++++ 7 files changed, 72 insertions(+), 27 deletions(-) create mode 100644 turnupd.install diff --git a/.SRCINFO b/.SRCINFO index 1cae229..b01e171 100644 --- a/.SRCINFO +++ b/.SRCINFO @@ -1,6 +1,6 @@ pkgbase = turn-up-arch pkgdesc = USB serial knob/button mixer daemon for PipeWire/PulseAudio on Linux - pkgver = 0.3.2 + pkgver = 0.3.3 pkgrel = 1 url = https://github.com/sean351/turn-up-arch arch = any @@ -15,7 +15,8 @@ pkgbase = turn-up-arch depends = pipewire-pulse optdepends = playerctl: media key support via button commands optdepends = pulseaudio: alternative to pipewire-pulse - source = turn-up-arch-0.3.2.tar.gz::https://github.com/sean351/turn-up-arch/archive/refs/tags/v0.3.2.tar.gz + install = turnupd.install + source = turn-up-arch-0.3.3.tar.gz::https://github.com/sean351/turn-up-arch/archive/refs/tags/v0.3.3.tar.gz sha256sums = SKIP pkgname = turn-up-arch diff --git a/PKGBUILD b/PKGBUILD index 2a16e4d..68d29a6 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Sean Doran # AUR updates are automated via GitHub Actions on version tag push pkgname=turn-up-arch -pkgver=0.3.2 +pkgver=0.3.3 pkgrel=1 pkgdesc="USB serial knob/button mixer daemon for PipeWire/PulseAudio on Linux" arch=('any') @@ -23,6 +23,7 @@ optdepends=( 'playerctl: media key support via button commands' 'pulseaudio: alternative to pipewire-pulse' ) +install=turnupd.install source=("$pkgname-$pkgver.tar.gz::https://github.com/sean351/turn-up-arch/archive/refs/tags/v$pkgver.tar.gz") sha256sums=('SKIP') diff --git a/contrib/turnupd.service b/contrib/turnupd.service index d33aec4..b61729f 100644 --- a/contrib/turnupd.service +++ b/contrib/turnupd.service @@ -1,16 +1,14 @@ [Unit] Description=Turn Up mixer daemon Documentation=https://github.com/sean351/turn-up-arch -After=pipewire-pulse.service pulseaudio.service -Wants=pipewire-pulse.service +After=pipewire-pulse.socket pulseaudio.socket +Wants=pipewire-pulse.socket [Service] Type=simple ExecStart=/usr/bin/turnupd -Restart=on-failure +Restart=always RestartSec=5 -# Give the audio server a moment to start before connecting -ExecStartPre=/bin/sleep 2 # Logging StandardOutput=journal diff --git a/pyproject.toml b/pyproject.toml index 00cf00c..35149ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "turnup" -version = "0.3.2" +version = "0.3.3" description = "USB serial knob/button mixer daemon for PipeWire/PulseAudio on Linux" readme = "README.md" license = { text = "MIT" } diff --git a/src/turnup/__init__.py b/src/turnup/__init__.py index d733b3c..09693b7 100644 --- a/src/turnup/__init__.py +++ b/src/turnup/__init__.py @@ -1,3 +1,3 @@ """Turn Up — USB serial mixer daemon for PipeWire/PulseAudio.""" -__version__ = "0.3.2" +__version__ = "0.3.3" diff --git a/src/turnup/turnupd.py b/src/turnup/turnupd.py index 6cf12e0..44d3cce 100755 --- a/src/turnup/turnupd.py +++ b/src/turnup/turnupd.py @@ -13,6 +13,7 @@ even when no knob is being moved. """ import logging +import os import signal import subprocess import sys @@ -21,7 +22,7 @@ import time import pulsectl import serial -from turnup.config import get_knob_led_cfg, get_led_color, load_config +from turnup.config import DEFAULT_CONFIG_PATH, get_knob_led_cfg, get_led_color, load_config logging.basicConfig( level=logging.INFO, @@ -359,6 +360,13 @@ def main() -> None: knob_norms = init_knob_norms(config, pulse) buf = bytearray() + # Track config file mtime so we can restart when it changes. + try: + config_mtime: float | None = os.stat(DEFAULT_CONFIG_PATH).st_mtime + except OSError: + config_mtime = None + last_config_check = time.monotonic() + def _shutdown(sig: int, _frame: object) -> None: log.info("Received signal %d — shutting down", sig) pulse.close() @@ -376,22 +384,34 @@ def main() -> None: while True: data = ser.read(64) - if not data: - continue - buf.extend(data) - messages, buf = parse_messages(buf) - for msg in messages: - if msg["type"] == "knob": - handle_knob( - msg["id"], msg["value"], - config, pulse, ser, knob_norms, - ) - elif msg["type"] == "button": - handle_button( - msg["id"], msg["action"], config, pulse - ) - elif msg["type"] == "heartbeat": - send_leds(ser, all_led_colors(config, knob_norms)) + if data: + buf.extend(data) + messages, buf = parse_messages(buf) + for msg in messages: + if msg["type"] == "knob": + handle_knob( + msg["id"], msg["value"], + config, pulse, ser, knob_norms, + ) + elif msg["type"] == "button": + handle_button( + msg["id"], msg["action"], config, pulse + ) + elif msg["type"] == "heartbeat": + send_leds(ser, all_led_colors(config, knob_norms)) + + # Check for config changes every 2 s (serial read timeout = 0.1 s). + now = time.monotonic() + if now - last_config_check >= 2.0: + last_config_check = now + try: + new_mtime = os.stat(DEFAULT_CONFIG_PATH).st_mtime + if config_mtime is not None and new_mtime != config_mtime: + log.info("Config changed — restarting daemon") + pulse.close() + os.execv(sys.executable, [sys.executable] + sys.argv) + except OSError: + pass except serial.SerialException as exc: log.warning("Serial error: %s — retrying in 3 s", exc) diff --git a/turnupd.install b/turnupd.install new file mode 100644 index 0000000..d054017 --- /dev/null +++ b/turnupd.install @@ -0,0 +1,25 @@ +post_install() { + systemctl --global enable turnupd.service + echo "turnupd: service enabled for all users — it will start on next login." + echo "To start it now: systemctl --user start turnupd.service" +} + +post_upgrade() { + systemctl --global reenable turnupd.service + # Restart for all currently logged-in users + while read -r uid _; do + systemctl -M "${uid}@.host" --user restart turnupd.service 2>/dev/null || true + done < <(loginctl list-users --no-legend 2>/dev/null) +} + +pre_remove() { + # Stop and disable for all currently logged-in users + while read -r uid _; do + systemctl -M "${uid}@.host" --user stop turnupd.service 2>/dev/null || true + systemctl -M "${uid}@.host" --user disable turnupd.service 2>/dev/null || true + done < <(loginctl list-users --no-legend 2>/dev/null) + + # Remove the global enable symlink + # (/etc/systemd/user/default.target.wants/turnupd.service) + systemctl --global disable turnupd.service 2>/dev/null || true +}