Asterisk & cmus: Musik automatisch unterbrechen

(2008-02-04)

Wer gerne Musik hört und oftmals angerufen wird kennt sicherlich das Problem, dass man die Musik immer leiser macht oder ausschaltet und nach dem Gespräch wieder anschaltet. Einer der Gründe, wieso ich freie Telefonanlagen (wie zum Beispiel asterisk) so mag, ist, dass man dieses Problem nun lösen kann. Ich hab’ das mit zwei einfachen Scripts und ein paar Einstellungen für meinen MP3-Player cmus erreicht.

In der folgenden Anleitung gehe ich also davon aus, dass cmus oder ein ähnlich funktionierender MP3-Player verwendet wird sowie dass asterisk auf einem separaten Rechner läuft (wenn nicht, kann man sich den Webserver- und den Socket-Schritt sparen).

cmus’ Status speichern und abrufen

Da cmus standardmäßig keine externe „now playing”-Anzeige bietet, aber Scripts aufrufen kann, wenn sich der Status ändert, brauchen wir ein kleines Script, welches den Status behält:

~/.cmus/status.sh

#!/bin/sh
# Saves the cmus status

while test $# -ge 2
do
    eval _$1='$2'
    shift
    shift
done

echo $_status > ~/.cmus/play_status/status

Um dieses Script aufrufen zu lassen, muss die Einstellung :set status_display_program=/home/michael/.cmus/status.sh getätigt werden in cmus. Außerdem muss das Verzeichnis ~/.cmus/play_status/ existieren.

Den Status über’s Netz abrufbar machen

Mithilfe deines Lieblingswebservers oder über eine beliebige andere Art (NFS, FTP, Samba, …) kannst du nun die Datei ~/.cmus/play_status/status im Netz verfügbar machen, damit der (asterisk-)Server erkennt, ob momentan Musik läuft oder nicht (wenn diese nämlich nicht läuft und man den Pause-Befehl an cmus schickt, fängt dieser an zu spielen – genau das Gegenteil würde also erreicht).

Ich hab’ das über einen apache-Vhost gelöst, da apache ohnehin zum Testen auf meinem Rechner läuft:

<VirtualHost 192.168.1.23:2424>
    ServerAdmin [email protected]
    DocumentRoot /home/michael/.cmus/play_status/
    CustomLog /var/log/apache2/cmus.access common
    ErrorLog /var/log/apache2/cmus.error
    <Directory /home/michael/.cmus/play_status/>
        Options FollowSymlinks
        Allow from all
    </Directory>
</VirtualHost>

cmus über’s Netz steuerbar machen

Das mitgelieferte Programm cmus-remote greift standardmäßig via UNIX-Socket auf cmus zu. Damit cmus nun TCP-Sockets verwendet, muss man es mit folgender Option starten (das kann man leider (noch?) nicht fix einstellen):

cmus --listen 192.168.1.23:2525

Die IP-Adresse und den gewünschten Port natürlich gegebenenfalls ersetzen ;-).

Doch das war noch nicht alles: Bei Zugriff über TCP erhält man nun (zu recht!) die Meldung, dass das – komplett ohne Authentifizierung – unsicher sei. Mithilfe von :set passwd=foo kann man das Passwort setzen, welches die Gegenseite braucht, um cmus fernsteuern zu können.

Scripts für eingehende Anrufe und aufgelegte Anrufe

Das Script für eingehende Anrufe holt sich via wget den cmus-Status von meinem Rechner, hält (falls Musik läuft) den momentanen Song an und merkt sich, dass er eingegriffen hat, indem er /tmp/call_broke_song anlegt.

~/.pbx/cmus-incoming.sh

#!/bin/sh
# This should be called on an incoming call from asterisk

playstatus=$(wget -qO- http://192.168.1.23:2424/status)
[ $playstatus = "playing" ] && {
    touch /tmp/call_broke_song
    echo -e "foo\nplayer-pause" | nc -q0 192.168.1.23 2525
}

exit 0

Das Script für aufgelegte Anrufe ist genauso simpel: Es prüft, ob zuvor eingegriffen wurde, schaut nach, ob der Status noch immer auf Pause steht (falls man sich während des Telefongesprächs dafür entscheidet, die Musik via Stop ganz auszuschalten, könnte das nicht der Fall sein) und setzt sie dann fort.

~/.pbx/cmus-hangup.sh

#!/bin/sh
# This should be called when the call is over from asterisk

[ -f /tmp/call_broke_song ] && {
    rm /tmp/call_broke_song
    playstatus=$(wget -qO- http://192.168.1.23:2424/status)
    [ $playstatus = "paused" ] && {
        echo -e "foo\nplayer-pause" | nc -q0 192.168.1.23 2525
    }
}

exit 0

Hier sieht man übrigens auch, dass cmus-remote gar nicht verwendet wurde. Stattdessen kommt nc zum Einsatz, welches einfach den Text (Passwort und den entsprechenden Befehl in der nächsten Zeile) an den Socket sendet, welchen wir vorhin in cmus konfiguriert haben.

Vor und nach dem Anruf Scripts einbauen

Der Mechanismus basiert darauf, dass vor und nach einem Anruf, egal wie er endet, ein Script ausgeführt wird. Ich würde sagen, dass das eigentlich der schwierigste Teil der ganzen Installation war ;-).

Durch die Option g hört asterisk nicht mit der Abarbeitung des aktiven Kontexts auf, sondern führt die nachfolgenden Aktionen aus. Das gilt gleichermaßen für angenommene und nicht beantwortete Anrufe, sodass man manuell prüfen muss.

Außerdem springt asterisk (nach Angabe der j-Option bei Dial() auf jeden Fall) zu Priorität + 101 im Dialplan, falls die Leitung gerade belegt ist. Dort startet man üblicherweise die Mailbox, hier muss aber auch unser Script aufgerufen werden.

Zu guter letzt wird man feststellen, dass der Kontext direkt beendet wird, falls der Anrufer auflegt. Hier bearbeitet asterisk den Dialplan gar nicht weiter – außer man definiert den Dialplan für die Extension h (für „hangup”).

So sieht ein solcher Dialplan dann aus:

extensions.conf

[default]
exten => 23,1,System(/home/michael/.pbx/cmus-incoming.sh)
exten => 23,2,Dial(SIP/23,30,gj)
exten => 23,3,System(/home/michael/.pbx/cmus-hangup.sh)
exten => 23,4,GotoIf($[ ${DIALSTATUS} = "NOANSWER" ]?103)
exten => 23,5,Hangup()

exten => 23,103,System(/home/michael/.pbx/cmus-hangup.sh) exten => 23,104,VoiceMail(b23)

exten => h,1,System(/home/michael/.pbx/cmus-hangup.sh)