Pulseaudio pour enceinte Bluetooth

Activation de la sortie du son sur l’enceinte automatiquement

Si tu as redémarré le raspberry, tu constateras que de nouveau, il faudra redéfinir le profil a2dp par défaut et la sortie par défaut.
Pas très user-friendly tout ça comme on dit dans le milieu. Nous allons donc voir comment arranger ça.

Le problème avec pulseaudio c’est qu’il est lancé en tant que daemon par l’utilisateur qui ouvre la session graphique. Du coup, il faut au préalable savoir à qui appartient le processus pulseaudio. Tu vas me dire que sur raspberry il y a de fortes probabilités pour que ce soit pi. Je te répondrai : certes, mais voyons plus loin, voyons plus générique.

Voilà grosso modo ce qu’on va faire. On va créer une règle udev qui va détecter la connexion à l’enceinte et démarrer un service que nous aurons au préalable créé. Ce service lancera un script A qui va se charger de récupérer les infos sur l’utilisateur qui a lancé pulseaudio. Puis il lancera un script B de configuration mais qui sera exécuté en tant que l’utilisateur possesseur de pulseaudio. C’est simple non ? 😀

C’est parti.

Règle udev

Alors qu’est ce que udev. Pour faire simple c’est un logiciel qui existe sous linux et qui permet entre autre d’exécuter des actions à la connexion/déconnexion de périphérique. Dans notre cas, ce qui nous intéresse c’est d’utiliser udev pour qu’il lance un script à la connexion de notre enceinte bluetooth

sudo nano /etc/udev/rules.d/99-com.rules

Normalement, il devrait y avoir déjà des choses dedans si tu es sur ton raspberry. Dans le cas de Linux, c’est probable que ce soit vide. tu peux aussi utiliser un autre nom.

tout à la fin de notre fichier de règle, nous allons ajouter la ligne ci dessous

KERNEL=="input[0-9]*", ATTR{name}=="xx:xx:xx:xx:xx:xx", RUN+="/bin/systemctl start connect-speaker-bt"

Que dit cette règle ?
Elle dit que lorsqu’on a un périphérique de type « input » suivi d’un ou plusieurs numéros et dont l’attribut « name » correspond à l’adresse MAC de notre enceinte qui se connecte, alors le service connect-speaker-bt doit être démarré.

CTRL+O puis CTRL+X pour sauvegarder et quitter (sans oublier de changer les xx par l’adresse MAC de ton enceinte)

Si tu veux t’assurer que udev voit bien ton enceinte, voici une petite astuce. Temporairement, tu vas remplacer la ligne ci dessus par celle-ci :

KERNEL=="input[0-9]*", ATTR{name}=="xx:xx:xx:xx:xx:xx", RUN+="/usr/bin/logger 'ton enceinte vient de se connecter'"

Ensuite tu connectes ton enceinte (si celle-ci ne s’est pas connectée automatiquement), ou bien tu l’éteins et redémarre, ça marche aussi. Regarde dans le fichier syslog

cat /var/log/syslog

Tu devrais avoir cette magnifique ligne qui arrive :

Jan  2 01:08:11 raspberrypi root: ton enceinte vient de se connecter

Ça veut dire que udev a bien reconnu l’enceinte et que la règle a bien lancé la commande logger.

Si tu ne vois pas cette ligne alors ne va pas plus loin dans l’article et corrige le problème.

Le service connect-speaker-bt

Je ne rentrerai pas dans les détails du choix de l’utilisation d’un service mais il y avait comme un bug avec l’utilisation de udev et de la commande pacmd/pactl. Et après quelques recherches, il était conseillé de passer par un service.

Nous allons donc créer notre service avec ceci :

sudo nano /lib/systemd/system/connect-speaker-bt.service

Et tu vas coller ça à l’intérieur

[Unit]
Description=Connect bluetooth speaker

[Service]
ExecStart=/usr/local/bin/connect-speaker-bt "xx:xx:xx:xx:xx:xx" "add"

[Install]
WantedBy=multi-user.target

Pour faire simple, on lui donne une description, la ligne de commande lorsqu’on démarre le service et dans quelle cible il doit se lancer lorsqu’on active le service.
N’oublie pas de remplacer les xx par l’adresse mac de ton enceinte
Dans notre cas, le service ne sera pas activé car lancé par udev, mais ça peut être utile dans d’autres cas

sudo systemctl daemon-reload

On demande à notre gestionnaire de service de recharger tous les services mais surtout d’ajouter le notre 🙂

Script récupérateur d’info

C’est notre script qui va se charger de savoir avec quel utilisateur a été lancé pulseaudio. Une fois les infos obtenues, il lancera un script de config mais en tant que l’utilisateur possesseur de pulseaudio. En effet, seul le propriétaire du daemon pulseaudio peut le configurer.

sudo nano /usr/local/bin/connect-speaker-bt

Et on colle ceci à l’intérieur :

#!/bin/bash
# Get argument
name=$1
action=$2

# Log all infos in /var/log/syslog
logger "connect-speaker-bt : action = $action"
logger "connect-speaker-bt : name = $name"

# Get UID of user running pulseaudio 
PUID=`ps -C pulseaudio -o ruid= | awk '{print $1}'`
logger "connect-speaker-bt : pulseaudio user uid = $PUID"

if [ ! -z "$PUID" ]; then
  # environment variables to export
  export PULSE_RUNTIME_PATH="/var/run/user/$PUID/pulse"
  export HOME=`getent passwd $PUID | cut -d: -f6`
  logger "connect-speaker-bt : home of user = $HOME"

  if [ -x "$HOME/.pulse_events" ]; then
    # start script as user
    logger "connect-speaker-bt : start sudo user script"
    sudo -u "#$PUID" -E $HOME/.pulse_events $name $action 
  fi
fi

On n’oublie pas le CTRL+O et le CTRL+X pour sauvegarder et quitter.

sudo chmod +x /usr/local/bin/connect-speaker-bt

Et oui, il faut bien rendre notre script exécutable, sinon ça ne marchera pas.

Script de configuration

C’est le script qui sera exécuté en tant que propriétaire du daemon pulseaudio

cd
nano .pulse_events

Hop ! On ouvre notre script qui va s’appeler .pulse_event (oui oui, il y a bien un point devant le nom, c’est fait exprès, c’est pour qu’il soit un peu caché)

Comme je suis d’humeur joyeuse, on va découper en différente partie le script pour donner un peu plus d’explication. Si ça te saoule, tu peux aller à la fin, j’ai mis le script complet pour le copier/coller

Partie 1

#!/bin/bash
i=0
name=$1
action=$2
undersc_name=${name//:/\_}
logger "pulse_events : start user script with action $action and device $name ( $undersc_name )"

Bon là, simple, c’est un script qui s’exécute avec bash. La variable « i » va nous servir dans une boucle un peu plus bas.
$1 et $2 sont en fait les paramètres qui sont ajoutés au lancement du script. On le récupère donc et leur affecte un nom un peu plus parlant.
le nom de l’enceinte comme nous l’avons vu est de format xx:xx:xx:xx:xx:xx. Or dans notre script, nous allons aussi avoir besoin du format xx_xx_xx_xx_xx_xx. Pour cela, j’utilise du regex et affecte le résultat à la variable undersc_name qui te fera irrémédiablement penser à « nom avec underscore ».
Quant à la commande logger, elle est surtout là pour debugger puisqu’elle permet d’écrire des infos dans /var/log/syslog.

Partie 2

if [ $action = "add" ]
then

      while ! pactl list cards | grep 'device.icon_name = "audio-speakers-bluetooth"'
        do let "i += 1"
        sleep 1
        if [ "$i" -eq 10 ]; then
          logger "pulse_events : unable to find Bluetooth speaker"
          break
        fi
      done

Il devrait techniquement y avoir deux actions possibles : « add » et « remove » qui correspondent aux actions de udev. Mais dans cet article, on se limite à l’action « add ».
Donc une fois que l’on s’est assuré que c’est une action de type « add », nous entamons une petite boucle qui s’assure vite fait mal fait qu’on a bien un périphérique audio bluetooth de connecté.
Par contre au bout de 10 boucles (grâce à notre variable i), si il n y a toujours pas de device audio bluetooth, et bien on arrête le script, c’est la fin du monde.

Partie 3

     logger "pulse_events : Set the default profile to bluez_sink.$undersc_name to a2dp_sink "
      pactl set-card-profile "bluez_card.$undersc_name" "a2dp_sink" 
      logger "pulse_events : set the default sink to bluez_sink.$undersc_name.a2dp_sink"
      pactl set-default-sink "bluez_sink.$undersc_name.a2dp_sink"
 
else
      logger "pulse_events : action - $action - not known"   
fi

On définit notre profil à a2dp_sink et on définit notre sortie audio vers notre enceinte.

Le script complet :

#!/bin/bash
n=0
name=$1
action=$2
undersc_name=${name//:/\_}
logger "pulse_events : start user script witch action $action and device $name ( $undersc_name )"

if [ $action = "add" ]
then

      while ! pactl list cards | grep 'device.icon_name = "audio-speakers-bluetooth"'
        do let "n += 1"
        sleep 1
        if [ "$n" -eq 10 ]; then
          logger "pulse_events : unable to find Bluetooth speaker"
          break
        fi
      done

      logger "pulse_events : Set the default profile to bluez_sink.$undersc_name to a2dp_sink "
      pactl set-card-profile "bluez_card.$undersc_name" "a2dp_sink" 
      logger "pulse_events : set the default sink to bluez_sink.$undersc_name.a2dp_sink"
      pactl set-default-sink "bluez_sink.$undersc_name.a2dp_sink"
 
else
      logger "pulse_events : action - $action - not known"   
fi


7 Comments

  1. Bjr,

    merci pour toutes ces informations !

    L’idée (la mienne…) était d’installer une enceinte BT sur un pi zero avec Snips …

    J’ai fait un mix entre les 2 séries (Snips & BT), tout va à peu près bien (encore merci). mais je bute toujours sur le non-lancement de ‘pulseaudio –start’ lors du démarrage.
    Et j’avoue ne pas être bon sur ce genre de sujet …

    • Précision : j’arrive à lancer ‘pulseaudio –start’ … mais le système me demande le mot de passe. Je cherche un moyen (avec les fameuses explications) pour contourner le problème.

    • Bonjour,

      Oui tout à fait. L’utilisation du socket est faite pour que des utilisateurs distincts utilisent le même serveur pulseaudio. Donc, après, qu’importe le périphérique de sortie 🙂

      Ced

  2. Salut,

    Merci pour ton blog et les articles. J ai suivi ceux de l installation de snips.
    En voyant celui-ci, je me posais la question de savoir si c était compatible avec snips.
    Merci d avance

    Zak

    • A partir du moment que c’est à travers de pulseaudio que fonctionne snips, oui sans soucis. I faut juste eviter de refaire un « sam setup audio » derrière viendrait écraser le asound.conf

Poster un Commentaire

Votre adresse de messagerie ne sera pas publiée.


*


Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.