Go to file
Sean Doran 873e7fa7bb
Fix: use printf for SSH config to avoid heredoc indentation issues
2026-02-27 11:46:29 -05:00
.github/workflows Fix: use printf for SSH config to avoid heredoc indentation issues 2026-02-27 11:46:29 -05:00
contrib Add LED support and remove legacy root-level scripts 2026-02-27 11:35:54 -05:00
src/turnup Bump version to v0.2.2 2026-02-27 11:45:09 -05:00
.SRCINFO Update PKGBUILD and .SRCINFO for v0.2.0 2026-02-27 11:37:00 -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 Bump version to v0.2.1 2026-02-27 11:42:35 -05:00
README.md Fix pkgname, maintainer email, and AUR package name to match repo 2026-02-27 10:10:37 -05:00
pyproject.toml Bump version to v0.2.2 2026-02-27 11:45:09 -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 (optional — for media key bindings)

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.json. Edit it to match your device layout. See contrib/config.example.json for a fully commented example.

{
  "port": "/dev/ttyACM0",
  "baud": 115200,

  "knobs": {
    "0": { "action": "sink_volume",  "target": "default" },
    "1": { "action": "group_volume", "targets": ["vlc", "spotify"] },
    "2": { "action": "app_volume",   "target": "Brave" },
    "3": { "action": "source_volume","target": "default" }
  },

  "buttons": {
    "0": { "action": "mute_sink",  "target": "default" },
    "1": { "action": "command",    "target": "playerctl previous" },
    "2": { "action": "command",    "target": "playerctl play-pause" },
    "3": { "action": "command",    "target": "playerctl next" },
    "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.