# 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](https://pypi.org/project/pyserial/) - [pulsectl](https://pypi.org/project/pulsectl/) - PipeWire (with `pipewire-pulse`) or PulseAudio - `playerctl` ## Installation ### Arch Linux (AUR) ```sh yay -S turn-up-arch ``` Or manually: ```sh git clone https://aur.archlinux.org/turn-up-arch.git cd turn-up-arch makepkg -si ``` ### From source ```sh 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`](contrib/config.example.toml) for a fully annotated example. ```toml 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 (0–150 %) | | `source_volume` | Mic / input volume (0–100 %) | | `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: ```sh # After install via AUR or pip: systemctl --user enable --now turnupd.service ``` To view logs: ```sh journalctl --user -u turnupd -f ``` ## Running manually ```sh 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 FF` | `06` = press, `07` = release | | Knob | `FE 03 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. ## Credits Originally created by [Sean Doran](https://github.com/sean351) — [sean351/turn-up-arch](https://github.com/sean351/turn-up-arch). This copy tracks a personal fork with local additions (focused-window volume/mute control, app-group muting, etc.). ## License MIT — see [LICENSE](LICENSE).