Dealing with Pipewire dynamic IDs

Written by Lyoneel on in troubleshooting
 4 mins

Dealing with Pipewire dynamic IDs

Configuring Deckboard I figure Pipewire IDs can change on reboot or suspend.

Motivation

I got an idea, and I picked an old Amazon Fire tablet 8 to use it as "StreamDeck" . I was configuring Deckboard, to use it for many things; one of them is to switch between audio devices. I’m running NixOS and Pipewire as sound server.

Changing default device

Let’s take a look at the basic usage of WirePlumber CLI:

web@lyoneel.dev : zsh — Konsole
  • $ wpctl
  • Usage:
  •   wpctl [OPTION…] COMMAND [COMMAND_OPTIONS] - WirePlumber Control CLI
  • Commands:
  •   status
  •   get-volume ID
  •   inspect ID
  •   set-default ID
  •   set-volume ID VOL[%][-/+]
  •   set-mute ID 1|0|toggle
  •   set-profile ID INDEX
  •   clear-default [ID]
  • Help Options:
  •   -h, --help       Show help options
  • Pass -h after a command to see command-specific options
 

My use case is using the command set-default to change a default device, but we need to figure out the ID.

To get the ID we have to use status, status will throw a lot of information, but we need to focus in Audio > Sinks

web@lyoneel.dev : zsh — Konsole
  • $ wpctl status
  • [...]
  • Audio
  • ├─ Devices:
  • │      46. Renoir Radeon High Definition Audio Controller [alsa]
  • │      48. Family 17h/19h HD Audio Controller  [alsa]
  • │      80. Ditoo-audio                         [bluez5]
  • │     132. GSP 370                             [alsa]
  • │  
  • ├─ Sinks:
  • │  *   58. Family 17h/19h HD Audio Controller Analog Stereo [vol: 0.50]
  • │      68. Renoir Radeon High Definition Audio Controller Digital Stereo (HDMI) [vol: 0.40]
  • │      81. Ditoo-audio                         [vol: 0.70]
  • │     103. GSP 370 Analog Stereo               [vol: 0.55]
  • [...]
 

Current default is mark with the asterisk *, using those IDs you can set the default device, for example, I want to set default device to GSP 370:

web@lyoneel.dev : zsh — Konsole
  • $ wpctl set-default 103
 

The problem: Dynamic IDs

After a reboot or suspend IDs can potentially change, to avoid it we will parse the wpctl status output to know exactly which number is needed to set the desired device as default. Because names can change for many reasons, I prefer to use status with --names, it will list sink with internal names and that remove the chance to have two equal names.

web@lyoneel.dev : zsh — Konsole
  • $ wpctl status --name
  • [...]
  • Audio
  • ├─ Devices:
  • │      46. alsa_card.pci-0000_06_00.1          [alsa]
  • │      48. alsa_card.pci-0000_06_00.6          [alsa]
  • │      80. bluez_card.XX_XX_XX_XX_XX_XX        [bluez5]
  • │     132. alsa_card.usb-Sennheiser_GSP_370-00 [alsa]
  • │  
  • ├─ Sinks:
  • │  *   58. alsa_output.pci-0000_06_00.6.analog-stereo [vol: 0.50]
  • │      68. alsa_output.pci-0000_06_00.1.hdmi-stereo [vol: 0.40]
  • │      81. bluez_output.XX_XX_XX_XX_XX_XX.1    [vol: 0.70]
  • │     103. alsa_output.usb-Sennheiser_GSP_370-00.analog-stereo [vol: 0.55]
  • [...]
 

Given this list I need the switch between 58 and 103, but we will get those numbers via regex. There is plenty of ways to do this, my preference is the command sed.

Obtaining the value

Let me show you separated commands, with a brief explanation.

1. Status

wpctl status --name

This will list the current audio sinks, similar to what I show early.

2. Grep

Using the output of the previous command, execute grep filtering with sinkname.

grep 'sinkname'

For example sinkname could be:

grep 'alsa_output.pci-0000_06_00.6.analog-stereo'

This command will return the exact line where the needed value is:

│ 58. alsa_output.pci-0000_06_00.6.analog-stereo [vol: 0.50]

3. Sed

Using the output of the previous command, I use sed to extract the needed value. This command has this basic structure s/<input>/<output>/. The command to execute is sed 's/^[^0-9]*\([0-9]\+\)\..*/\1/', let’s break it down:

  • Start searching from the beginning ^.
  • Any character [^0-9]* but numbers. Asterisk * means 0 or more occurrences. In this case ^ is negation.
  • Capture is (), everything inside parenthesis is captured y and the captured text is used in output section of the command with \1, parenthesis needs to be escaped \(\).
  • Inside capture look for numbers [0-9]+, plus sign + means one or more occurrences followed by a point \. escaped.
  • The rest of the line does not matter, already we got what we need, then at the end of the pattern add .* it means any character zero or more occurrences.
  • And the last thing is use the capture value in the output side of the command \1.

Putting command all together

wpctl set-default $(echo "$(wpctl status --name | grep 'alsa_output.pci-0000_06_00.6.analog-stereo')" | sed 's/^[^0-9]*\([0-9]\+\)\..*/\1/')

wpctl set-default $(echo "$(wpctl status --name | grep 'alsa_output.usb-Sennheiser_GSP_370-00.analog-stereo')" | sed 's/^[^0-9]*\([0-9]\+\)\..*/\1/')

You need to adapt the grep device value to fit your needs.


Thanks for reading!

Namaste.