Snips, openHAB und Respeaker-Mic-Array

Posted by on Feb 23, 2018 in Smart Home | No Comments
Snips, openHAB und Respeaker-Mic-Array

Eigentlich finde ich die Idee, einen sprachgesteuerten Assistenten für mein Smarthome zu haben, nicht schlecht. Allerdings habe ich auch keine Lust, mir eine „Wanze“ ala Alexa, Echo oder Siri ins Haus zu stellen. Die Konzerne beteuern hier zwar immer, den Datenschutz zu wahren, aber erstens vertraue ich diesen keinen Meter weit und zweitens bin ich als Informatiker davon überzeugt, dass sich 99% aller Features eines Smarthome auch ohne jegliches Internet realisieren lassen. Der Marketing-Bullshit einschlägiger Unternehmen, dass man ja nur den Komfort der Nutzer erhöhen würde, ist ein ganz schmutziges Feigenblatt.

Leider gab es bisher keine großartige Alternative zu den oben genannten Anbietern, bis mich ein Freund auf Snips aufmerksam gemacht hat. Dabei handelt es sich um einen Sprachassistenten, der ganz ohne Internet, lokal auf einem Raspberry Pi 3 läuft. Natürlich hat dieser dann nicht die Fähigkeit ihn nach der Einwohnerzahl von Lossa fragen zu können. Aber das ist es ja auch nicht, was ich erwarte. Ich möchte in einem ersten Schritt, lediglich meine Steckdosen und Smartlampen steuern können. Und das funktioniert schon erstaunlich gut. In diesem kleinen Abriss soll es um die Installation von Snips auf einem Raspi gehen. Außerdem hatte ich mir extra für dieses kleine Projekt ein Mikrofon-Array von Respeaker gekauft. Leider scheint es das nicht mehr zu geben, aber in einem Blogbeitrag von Snips werden noch weitere Empfehlungen gegeben. Zu guter Letzt, muss das ganze dann noch an openHAB angebunden werden, damit ich meine Lampe auch an- und ausschalten kann.

Wie man Raspbian-Lite auf dem Pi installiert, werde ich jetzt nicht extra aufführen. Das wird überall im Internet ausführlich beschrieben. Nachdem das geschafft ist, wird Snips installiert.

sudo apt-get update
sudo apt-get install -y dirmngr
sudo bash -c  'echo "deb https://raspbian.snips.ai/$(lsb_release -cs) stable main" > /etc/apt/sources.list.d/snips.list'
sudo apt-key adv --keyserver pgp.mit.edu --recv-keys D4F50CDCA10A2849

sudo apt-get update
sudo apt-get install -y snips-platform-voice

Das wird auch in der Snips-Dokumentation gut beschrieben. Als nächstes muss das Respeaker Mic-Array installiert werden. Dazu muss man erst einmal die Firmware von Respeaker auf den neusten Stand bringen. Auch das wird auf der Github-Seite des Projektes gut beschrieben, und muss hier nicht noch einmal kopiert werden. Da es das Mic-Array sowieso nicht mehr zu kaufen gibt, ist das an dieser Stelle vielleicht eh nicht mehr so interessant.
Die Einrichtung des Mikrofons auf dem Raspi gestaltet sich wieder recht simpel.

aplay -l

Liefert Informationen über die am Raspi angeschlossenen Audiogeräte.

**** Liste der Hardware-Geräte (PLAYBACK) ****
Karte 0: ALSA [bcm2835 ALSA], Gerät 0: bcm2835 ALSA [bcm2835 ALSA]
  Sub-Geräte: 8/8
  Sub-Gerät #0: subdevice #0
  Sub-Gerät #1: subdevice #1
  Sub-Gerät #2: subdevice #2
  Sub-Gerät #3: subdevice #3
  Sub-Gerät #4: subdevice #4
  Sub-Gerät #5: subdevice #5
  Sub-Gerät #6: subdevice #6
  Sub-Gerät #7: subdevice #7
Karte 0: ALSA [bcm2835 ALSA], Gerät 1: bcm2835 ALSA [bcm2835 IEC958/HDMI]
  Sub-Geräte: 1/1
  Sub-Gerät #0: subdevice #0
Karte 1: UAC20 [ReSpeaker MicArray UAC2.0], Gerät 0: USB Audio [USB Audio]
  Sub-Geräte: 1/1
  Sub-Gerät #0: subdevice #0

Um das Mikrofon zu konfigurieren, muss die Datei /etc/asound.conf bearbeitet werden.

sudo nano /etc/asound.conf

Folgendes sollte hinzugefügt werden:

pcm.!default {
  type asym
   playback.pcm {
     type plug
     slave.pcm "hw:0,0"
   }
   capture.pcm {
     type plug
     slave.pcm "hw:1,0"
   }
}

Einen Hinweis an dieser Stelle: Ich hatte bei der Einrichtung des Mikrofons Probleme, da ich zur globalen Konfiguration in der /etc/asound.conf noch eine in meinem Heimverzeichnis liegende ~/.asoundrc hatte. Diese haben sich irgendwie gestört. Also besser nur eine der beiden Konfigurationsmöglichkeiten nutzen.

Der nächste Schritt war die Installation des Modells. Wie man das Modell erstellt, ist bei Snips sehr gut beschrieben. Für meine Lampe habe ich das Bundle „Haus-Automation“ von mkloeckner benutzt. Das ist schon sehr gut. Ich habe lediglich noch ein paar kurze Trainingssätze wie „Licht aus“ hinzufügen müssen, da ich manchmal keine Lust habe, in freundlichen langen Sätzen mit meinem Sprachassistenten zu reden. Manchmal muss es auch der schnöde Befehlston tun.
Nachdem das Modell auf meiner Festplatte lag, habe ich es in den Ordner c:/pscp geschoben, zusammen mit der pscp.exe, welche man hier herunterladen kann.
Mit den nachfolgenden Befehlen kopiert man das Modell in das Heimverzeichnis des Raspi, und danach verschiebt man es in den snips-Ordner.

pscp -r c:\pscp\assistant pi@192.168.xxx.yyy:/home/pi
sudo mv /home/pi/assistant /usr/share/snips/

Als Letztes habe ich dann noch snips-watch installiert, um mir die Ereignisse auf dem Bus anschauen zu können.

sudo apt-get install snips-watch

Mit snips-watch -v (-vv, -vvv) kann man dann den Bus überwachen und sehen, was Snips erkennt hat.

Snips bringt von Haus aus schon einen MQTT-Broker mit, der bei der Installation aktiviert wird. Hier ist also keinerlei Arbeit nötig. Um das zu überprüfen habe ich mir das Programm MQTT.fx heruntergeladen und auf der IP (192.168.xxx.yyy:1883) meines Snips-Brokers gelauscht. Mit dem Programm kann man sich nicht nur für verschiedene „topics“ eines Brokers anmelden, sondern auch den ganzen Broker scannen. Man sieht also was Snips so alles rauspustet.

Also nächstes muss noch openHAB mit dem Broker verbunden werden. Dazu muss man das MQTT-Binding in openHAB installieren. Das findet man im PaperUI unter Addons>Bindings. Die Konfiguration des Service erfolgt dann in der mqtt.cfg.

snips.url=tcp://192.168.xxx.yyy:1883
snips.clientId=openHab2

Folgende Items habe ich dann noch in einer snips.items Datei hinzugefügt:

String Snips_Intent_Active "Snips Intent Active" { mqtt="<[snips:hermes/intent/#:state:default]" }
Switch Snips_Listening "Snips Listening" { mqtt="<[snips:hermes/asr/toggleOn:state:ON],<[snips:hermes/asr/toggleOff:state:OFF]" }

Auch hier gibt es wieder einen kleinen Fallstrick. Eigentlich wäre es schön, wenn man sich mit openHAB nicht bei allen „topics“ mit hermes/intent/# anmelden müsste, sondern für jedes „topic“ ein extra Item haben könnte. Zum Beispiel so etwas:

String Snips_Intent_Light_On "Snips Intent Light On" { mqtt="<[snips:hermes/intent/lightOn:state:default]" }
String Snips_Intent_Light_Off "Snips Intent Light Off" { mqtt="<[snips:hermes/intent/lightOff:state:default]" }

Leider ist das seit der Einführung der deutschen Sprache in Snips nicht mehr möglich, da Snips den vergebenen Intent-Namen mit dem eigenen Username verbindet. Also Intent „lightOn“ wird zu „Username:lightOn“. Damit kommt openHAB nicht klar, da der Doppelpunkt als Trenner bei der Itemkonfiguration genutzt wird und das Parsen dann fehlschlägt.
Der Workaround sieht dann so aus, dass man sich auf alle „topics“ anmeldet und dann in der Rule die entsprechende String-Verarbeitung machen muss. Das ist sicher nicht schön, aber funktioniert zumindest für kleine Setups. Für größere Umgebungen mit mehr zu steuernden Items ended das in einer if-Hell.

rule "Turn on light by snips"
when
	Item Snips_Intent_Active changed 
then
    logInfo("SnipsMessage: ", "Snips_Intent_Active changed")
    var String json = Snips_Intent_Active.state.toString
    var String intentName = transform("JSONPATH", "$.intent.intentName", json)
    var String splittedIntentName = intentName.split(":").get(1);
    logInfo("SnipsMessageIntentName: ", splittedIntentName)
    var Integer slotsLength = Integer::parseInt(transform("JSONPATH", "$.slots.length()", json))
    if(slotsLength > 0)
    {
        if(splittedIntentName.equals("ActivateObject"))
        {
            Leselampe_Brightness.sendCommand(ON)
        }
        if(splittedIntentName.equals("DeactivateObject"))
        {
            Leselampe_Brightness.sendCommand(OFF)
        }
    }
end

Das ist natürlich nur ein ganz simples Beispiel, welches mir meine Leselampe an- bzw. ausschaltet. Es beachtet natürlich auch nicht die Slots der Intents usw.

Für die Leselampe reicht es aber. Die Spracherkennungsrate bei Snips empfinde ich als sehr gut. Ich habe natürlich keinen Vergleich zu den Wanzen der großen Hersteller, aber sogar mein Vierjähriger schafft es in den meisten Fällen die Lampe an- und wieder auszuschalten. Was natürlich ein tolles Spielzeug ist. Er fragte mich dann auch neugierig:“Papa, warum musst Du immer so komisches Zeug erfinden?“
Im Vergleich zur englischen Spracherkennung, empfinde ich die Erkennungsrate sogar als noch höher. Das kann natürlich auch mit meinem Akzent zusammenhängen. Jetzt fehlt eigentlich nur noch ein schickes Gehäuse.

 

Leave a Reply

*