Go to file
Sean db3d9e34a6
Merge pull request #1 from sean351/feat/mpris2-volume-routing
Add MPRIS2 volume routing and PA event watcher; fix Spotify song transition reset
2026-02-27 18:30:23 -05:00
.github/workflows Add TOML config, playerctl dep, and media button defaults; bump to v1.1.0 2026-02-27 14:38:36 -05:00
contrib Add TOML config, playerctl dep, and media button defaults; bump to v1.1.0 2026-02-27 14:38:36 -05:00
src/turnup Add MPRIS2 volume routing and PA event watcher; fix Spotify song transition reset 2026-02-27 18:28:56 -05:00
.SRCINFO Add TOML config, playerctl dep, and media button defaults; bump to v1.1.0 2026-02-27 14:38:36 -05:00
.gitignore Initial commit 2026-02-27 10:08:09 -05:00
LICENSE Initial commit 2026-02-27 10:08:09 -05:00
PKGBUILD Cap max volume at 100%; bump to v1.2.0 2026-02-27 15:03:50 -05:00
README.md Add TOML config, playerctl dep, and media button defaults; bump to v1.1.0 2026-02-27 14:38:36 -05:00
pyproject.toml Cap max volume at 100%; bump to v1.2.0 2026-02-27 15:03:50 -05:00
turnupd.install Fix: auto-enable service on install, fix service startup, add config hot-reload; bump to v0.3.3 2026-02-27 13:54:57 -05:00

README.md

turnup

A lightweight daemon that bridges a USB serial device (physical knobs and buttons) to PipeWire/PulseAudio on Linux. Map each knob to per-sink, per-source, or per-application volume; assign buttons to mute toggles or arbitrary shell commands.

Requirements

  • Python 3.10+
  • pyserial
  • pulsectl
  • PipeWire (with pipewire-pulse) or PulseAudio
  • playerctl

Installation

Arch Linux (AUR)

yay -S turn-up-arch

Or manually:

git clone https://aur.archlinux.org/turn-up-arch.git
cd turn-up-arch
makepkg -si

From source

git clone https://github.com/sean351/turn-up-arch.git
cd turn-up-arch
pip install .

Configuration

On first run turnupd writes a default config to ~/.config/turnup/config.toml. Edit it to match your device layout. See contrib/config.example.toml for a fully annotated example.

port = "/dev/ttyACM0"
baud = 115200

[leds]
mode       = "volume"
low_color  = [255, 0, 0]   # red   at 0 %
high_color = [0, 255, 0]   # green at 100 %

[knobs.0]
action = "sink_volume"
target = "default"

[knobs.1]
action  = "group_volume"
targets = ["vlc", "spotify"]

[knobs.2]
action = "app_volume"
target = "Brave"

[knobs.3]
action = "source_volume"
target = "default"

[buttons.0]
action = "mute_sink"
target = "default"

[buttons.1]
action = "command"
target = "playerctl previous"

[buttons.2]
action = "command"
target = "playerctl play-pause"

[buttons.3]
action = "command"
target = "playerctl next"

[buttons.4]
action = "mute_source"
target = "default"

Knob actions

Action Description
sink_volume Output device volume (0150 %)
source_volume Mic / input volume (0100 %)
app_volume Single application volume, matched by name or binary
group_volume Multiple applications at once — use "targets": [...]

Button actions

Action Description
mute_sink Toggle output mute
mute_source Toggle mic mute
command Run an arbitrary shell command

Running as a service

A systemd user service unit is included:

# After install via AUR or pip:
systemctl --user enable --now turnupd.service

To view logs:

journalctl --user -u turnupd -f

Running manually

turnupd

Pass a custom config path with the TURNUP_CONFIG environment variable (planned — currently edit ~/.config/turnup/config.json directly).

Hardware

The daemon expects a USB-serial device speaking a simple binary protocol:

Frame Bytes Description
Heartbeat FE 02 FF Keepalive
Button FE 06/07 <id> FF 06 = press, 07 = release
Knob FE 03 <id> <hi> <lo> FF 10-bit ADC value, big-endian

Tested with an RP2040-based board. Any microcontroller that enumerates as a USB-CDC serial port and speaks the protocol above will work.

License

MIT — see LICENSE.