J'essaie d'exécuter automatiquement le script this chaque fois que je me connecte à mon casque Bluetooth.
J'ai créé le fichier /etc/udev/rules.d/80-bt-headset.rules avec la ligne
ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="00:22:37:3D:DA:50" RUN+="/home/USER/.local/bin/a2dp.py 00:22:37:3D:DA:50"
mais ça ne fait rien. Les conditions sont bonnes, un simple test de commande est déclenché lorsque je l'entre à la place. Le script lui-même fonctionne également bien lorsqu'il est exécuté manuellement.
Qu'est-ce qui ne va pas ici?
Update : Il y a une erreur lors de l'exécution du script avec Sudo -u USER
(voir ci-dessous pour plus de détails). Est-ce que ceci pourrait être le problème? Et comment est-ce que Sudo-ing, chez le même utilisateur, casse les choses?
Update 2 : Après avoir remplacé toutes les instances de pacmd
par pactl
dans a2dp.py
(et remplacé par list-sinks
avec list sinks
pour en faire une commande pactl valide), Sudo -u USER
fonctionne, mais la règle udev ne fonctionne toujours pas. Dans /var/log/syslog
je viens de voir la ligne
systemd-udevd[32629]: Process '/home/USER/.local/bin/a2dp_2.py 00:22:37:3D:DA:50' failed with exit code 1.
Update 3 (Solution) : Le skript modifié (pacmd -> pactl, voir Update 2) avec les variables d'environnement DISPLAY=:0
et XAUTHORITY=/home/USER/.Xauthority
a fait le tour. La règle udev:
ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="00:22:37:3D:DA:50" ENV{DISPLAY}=":0" ENV{XAUTHORITY}="/home/USER/.Xauthority" RUN+="/home/USER/.local/bin/a2dp_2.py 00:22:37:3D:DA:50"
fonctionne comme prévu.
(Le seul problème qui reste est que le script lui-même déclenchera la règle, car il reconnectera le casque, ce qui produira une boucle infinie. Cependant, cette question est distincte et il ne devrait pas être trop difficile de trouver une solution de contournement. fait je m'attendais à ce comportement quand j'ai commencé ce fil.)
Les conditions sont bonnes: La ligne:
ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="00:22:37:3D:DA:50" RUN+="/bin/mkdir /tmp/testme"
créera un nouveau répertoire lorsque je me connecterai au casque.
Le script a2dp.py lui-même fonctionne bien lorsqu'il est exécuté à partir du terminal via
/home/USER/.local/bin/a2dp.py 00:22:37:3D:DA:50
Lancer un script simplement Python via udev:
ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="00:22:37:3D:DA:50" RUN+="/home/USER/.local/bin/atestscript.py"
où atestscript.py contient:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import subprocess
def main():
subprocess.Popen(['mkdir', '/tmp/atestdir'])
if __== '__main__':
main()
créera à nouveau un dossier lorsque l'appareil est connecté.
L'exécution du script à partir du terminal avec Sudo -u USER
ou même Sudo -u root
fonctionne désormais comme prévu (pour le script d'origine, cela s'est traduit par:
USER@MACHINE:~$ Sudo -u USER /usr/local/bin/a2dp.py 00:22:37:3D:DA:50
Connection MADE
Device MAC: 00:22:37:3D:DA:50
Command: pacmd list-sinks failed with status: 1
stderr: No PulseAudio daemon running, or not running as session daemon.
Exiting bluetoothctl
Exécuter le script comme ci-dessus ou avec l'une des lignes suivantes comme partie RUN+=
:
/usr/bin/Sudo -u USER /usr/bin/python3 /home/USER/.local/bin/a2dp.py 00:22:37:3D:DA:50
/usr/bin/Sudo -u USER /home/USER/.local/bin/a2dp.py 00:22:37:3D:DA:50
/usr/bin/python3.5 /usr/local/bin/a2dp.py 00:22:37:3D:DA:50
ENV{DISPLAY}=":0" RUN+="/usr/local/bin/a2dp.py 00:22:37:3D:DA:50"
Même le script modifié ne fonctionnera pas:
ENV{DISPLAY}=":0" ENV{Pulse_RUNTIME_PATH}="/run/user/1000/Pulse/" RUN+="Sudo -u USER /home/USER/.local/bin/a2dp_2.py 00:22:37:3D:DA:50"
Informations complémentaires: Sortie du moniteur udevadm lors de la connexion au casque:
KERNEL[104388.664737] add /devices/pci0000:00/0000:00:14.0/usb3/3-7/3-7:1.0/bluetooth/hci0/hci0:256 (bluetooth)
UDEV [104388.667185] add /devices/pci0000:00/0000:00:14.0/usb3/3-7/3-7:1.0/bluetooth/hci0/hci0:256 (bluetooth)
KERNEL[104390.848157] add /devices/virtual/input/input46 (input)
UDEV [104390.849150] add /devices/virtual/input/input46 (input)
KERNEL[104390.849471] add /devices/virtual/input/input46/event17 (input)
UDEV [104390.864692] add /devices/virtual/input/input46/event17 (input)
Modifiez a2dp.py
en remplaçant toutes les instances de pacmd
par pactl
en ajustant pacmd list-sinks
à pactl list sinks
(dans mon cas, enregistré sous le nom /usr/local/bin/a2dp_2.sh
).
Créer un script de wrapper /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
Ajoutez la ligne suivante à /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}"
Ce script de wrapper accomplit les tâches suivantes:
Il découvre le $USER
possédant l'instance en cours de pulseaudio, puis définit les variables d'environnement DISPLAY=:0
et XAUTHORITY=/home/$USER/.Xauthority
nécessaires au fonctionnement de pactl
. Cela devrait le faire fonctionner pour tous les utilisateurs sur une machine. (Je n'ai pas testé les effets de plusieurs utilisateurs connectés en même temps.)
Il vérifie si le récepteur correspondant est suspendu et ne lance alors que a2dp_2.py
. Cela est nécessaire pour éviter une boucle infinie causée par a2dp_2.py
reconnexion du périphérique et ainsi le déclenchement de la règle.
Il exécute a2dp_2.py
comme $ USER. S'il est exécuté en tant que root, a2dp_2.py
laissera pulseaudio, et donc tous les paramètres audio, inaccessibles sans privilèges root.
Une solution alternative utilisant une boucle dbus peut être trouvée sur le page du développeur de sript .
Un correctif pour le bogue original est maintenant disponible ici et peut être facilement installé en ajoutant ppa:ubuntu-audio-dev/Pulse-testing
et en mettant à jour les paquetages disponibles.
Ne fait pas strictement partie du problème initial, mais cela pourrait être utile pour référence future. Il existe de nombreuses façons de trouver l'adresse MAC de votre appareil. Ce qui suit est celui que j’estime le plus utile pour les règles udev:
Recherchez le chemin du périphérique en exécutant udevadm monitor
puis en connectant votre périphérique. Votre sortie devrait ressembler à ceci:
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)
Arrêtez le moniteur avec Ctrl+C
. Nous avons trouvé trois chemins de périphérique. Celui qui nous intéresse est /devices/virtual/input/input68
.
Branchez le chemin obtenu dans 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}==""
Nous apprenons que l'adresse MAC est 00:22:37:3D:DA:50
et aussi qu'elle est stockée sous le nom ATTR{name}
.
Même si le résultat est complètement différent, ces deux commandes seront un bon début pour rechercher les conditions pertinentes pour une règle udev.
La règle:
ACTION=="add", SUBSYSTEM=="input" ATTR{name}=="??:??:??:??:??:??" RUN+="/usr/local/bin/a2dp-wrapper.sh $attr{name}"
déclenchera pour tout périphérique d'entrée ayant un attribut de nom qui ressemble à une adresse MAC et la condition dans le script d'encapsidation doit s'assurer qu'aucune action involontaire n'est entreprise.
Je ne dispose d'aucun autre périphérique audio bluetooth pour le tester, mais je vois un certain nombre de problèmes potentiels:
Cela ne fonctionnera que pour un périphérique Bluetooth reconnu comme périphérique d'entrée contenant l'adresse MAC dans l'attribut name. Tous les appareils ne peuvent pas être reconnus comme tels.
Cette solution n’est pas très élégante, car la règle sera déclenchée pour tout périphérique d’entrée. Cependant, je n'ai pas été en mesure de trouver des indicateurs clairs permettant d'identifier un périphérique audio Bluetooth. (Comme indiqué ci-dessus, le périphérique d'entrée n'a pas d'attribut supplémentaire et le périphérique Bluetooth n'indique pas qu'il s'agit d'un périphérique audio et ne contient pas non plus l'adresse MAC. Peut-être qu'ACPI serait préférable pour cela.)
Vous voudrez peut-être ne pas traiter tous les périphériques audio Bluetooth de la même manière: vous voudrez peut-être utiliser le protocole HSP pour votre oreillette ou ne pas vouloir basculer automatiquement sur les haut-parleurs de votre colocataire, avec lesquels vous êtes jumelés à un moment donné, chaque fois qu'ils le sont. disponible. Dans ces cas, il est probablement préférable d’avoir une règle distincte pour chaque périphérique.
Je continuerai à mettre à jour ce post à mesure que j'apprends.