udev rule to run Python script
Solution 1:
My working solution
Modify
a2dp.py
by replacing all instances ofpacmd
withpactl
adjustingpacmd list-sinks
topactl list sinks
(in my case saved as/usr/local/bin/a2dp_2.sh
).-
Create a wrapper script
/usr/local/bin/a2dp-wrapper.sh
#!/bin/bash MAC=$1 MACMOD=$(echo $MAC | sed 's/:/_/g') PID=$(pgrep pulseaudio) USER=$(grep -z USER= /proc/$PID/environ | sed 's/.*=//') export DISPLAY=:0 export XAUTHORITY=/home/$USER/.Xauthority if pactl list sinks short | grep "bluez_sink\.$MACMOD.*SUSPENDED" then sudo -u $USER /usr/local/bin/a2dp_2.py $MAC fi
-
Add the following line to
/etc/udev/rules.d/80-bt-headset.rules
:ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="00:22:37:3D:DA:50" RUN+="/usr/local/bin/a2dp-wrapper.sh $attr{name}"
This wrapper script accomplishes the following:
It finds out the
$USER
owning the running instance of pulseaudio, then sets the environment variablesDISPLAY=:0
andXAUTHORITY=/home/$USER/.Xauthority
necessary forpactl
to work. This should make it work for all users on a machine. (I have not tested the effects of multiple users logged in at the same time.)It checks whether the corresponding sink is suspended and only then runs
a2dp_2.py
. This is necessary to prevent an infinite loop caused bya2dp_2.py
reconnecting the device and thus triggering the rule.It runs
a2dp_2.py
as $USER. If run as root,a2dp_2.py
will leave pulseaudio, and thus any audio settings, inaccessible without root privileges.
Alternatives: dbus loop/fixed package
An alternative solution using a dbus loop can be found on the sript developer's page.
A fix for the original bug is now available here and can be easily installed by adding
ppa:ubuntu-audio-dev/pulse-testing
and updating the available packages.
Hint: Finding your device's MAC address
Not strictly part of the original problem, but this might be useful for future reference. There are numerous ways to find your device's MAC address. The following is the one I consider most helpful for udev rules:
-
Find the device path by running
udevadm monitor
and then connecting your device. Your output should look something like this:USER@MACHINE:~$ udevadm monitor monitor will print the received events for: UDEV - the event which udev sends out after rule processing KERNEL - the kernel uevent KERNEL[123043.617276] add /devices/pci0000:00/0000:00:14.0/usb3/3-7/3-7:1.0/bluetooth/hci0/hci0:256 (bluetooth) UDEV [123043.647291] add /devices/pci0000:00/0000:00:14.0/usb3/3-7/3-7:1.0/bluetooth/hci0/hci0:256 (bluetooth) KERNEL[123044.153776] add /devices/virtual/input/input68 (input) KERNEL[123044.153911] add /devices/virtual/input/input68/event17 (input) UDEV [123044.193415] add /devices/virtual/input/input68 (input) UDEV [123044.213213] add /devices/virtual/input/input68/event17 (input)
Stop the monitor with
Ctrl+C
. We have found three device paths. The one relevant for us is/devices/virtual/input/input68
. -
Plug the obtained path into
udevadm info
:USER@MACHINE:~$ udevadm info -a -p /devices/virtual/input/input68 Udevadm info starts with the device specified by the devpath and then walks up the chain of parent devices. It prints for every device found, all possible attributes in the udev rules key format. A rule to match, can be composed by the attributes of the device and the attributes from one single parent device. looking at device '/devices/virtual/input/input68': KERNEL=="input68" SUBSYSTEM=="input" DRIVER=="" ATTR{name}=="00:22:37:3D:DA:50" ATTR{phys}=="" ATTR{properties}=="0" ATTR{uniq}==""
We learn that the MAC address is
00:22:37:3D:DA:50
and also that it is stored asATTR{name}
.
Even if the output looks completely different, these two commands will be a good start in looking for the relevant conditions for a udev rule.
Experimental: Catching arbitrary bluetooth audio devices
The rule:
ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="??:??:??:??:??:??" RUN+="/usr/local/bin/a2dp-wrapper.sh $attr{name}"
will trigger for any input device that has a name attribute that looks like a MAC address and the conditional in the wrapper script should make sure no unintended actions are taken.
I do not have any other bluetooth audio device readily available to test this, but I see a number of potential issues:
This will only work for a bluetooth device that is recognised as an input device containing the MAC address in the name attribute. Not every device may be recognised as such.
This solution is not very elegant, as the rule will be triggered for any input device. However, I have not been able to find clear indicators to identify a bluetooth audio device. (As seen above, the input device has no further attributes, and the bluetooth device shows no indication of being an audio device, nor does it contain the MAC address. Maybe ACPI would be better for this.)
You may not want to treat every bluetooth audio device the same: You may want to use the HSP protocol for your headset or you may not want to automatically switch to your housemate's speakers, that you've paired with at some point, whenever they are available. In those cases it is probably preferable to have a separate rule for each device.
I will keep updating this post as I learn more.