Von der Spracherkennung zur Sprachsteuerung: Snips JSON-Objekte zur Steuerung von Geräten verwenden
Wie im Teil 2 dieser Artikelserie gezeigt, generiert Snips aus den aufgezeichneten und "verstandenen" Worten einen JSON-Ausdruck. In diesem Teil werde ich beispielhaft darstellen, wie dieser JSON-Ausdruck mit einem kleinen Python-Skript ausgewertet wird und Schaltvorgänge auf mit Tasmota geflashten Geräten ausgelöst werden bzw. MQTT benutzt wird, um die Hausautomation anzusteuern.
Dabei gehe ich von folgender Konfiguration aus:
- Snips (bzw. die Snips-Basis) ist auf einem Raspberry Pi installiert
- Auf demselben Board (localhost) läuft bereits ein MQTT-Broker (mosquitto, IOBroker usw.)
- Auf den Tasmota-Geräten ist MQTT aktiviert
- Alle Tasmota-Geräte haben einen eindeutigen MQTT-Namen (= topic), z. B. Fernseher, Werkstattleuchte usw.
- Das fulltopic auf den Geräten ist folgendermaßen konfiguriert: %topic%/%prefix%/
Anmerkung: Es scheint, dass bei Tasmota-Versionen > 6.2.1 die fulltopic-Struktur %topic%/%prefix%/ voreingestellt ist. In älteren Versionen ist es %prefix%/%topic%/. Ich weise an dieser Stelle deshalb daraufhin, weil das Skriptbeispiel mit der neueren Struktur arbeitet. Bei einer anderen fulltopic-Struktur müssen die Funktionen im Skript entsprechend angepasst werden. - Das Python-Skript läuft ebenfalls auf demselben Gerät
Anmerkung: Das muss nicht sein, es kann auch auf einem anderen Gerät laufen, denn es wird lediglich der Zugriff auf den MQTT-Broker benötigt. Da aber das Skript im 24/7-Modus läuft, macht es Sinn, es auf demselben Gerät zu starten.
Schritt 1 Python-Bibliothek paho-mqtt installieren
sudo pip install paho-mqtt
Diese Bibliothek und ihre Funktionen werden gebraucht, um mit dem MQTT-Broker zu kommunizieren. Zudem wird die Bibliothek json benötigt. Diese wird nicht installiert, weil sie bereits vorhanden ist. Sie muss durch das Skript lediglich importiert werden.
Hier nun ein Skript, das mit den Slots aus Teil 2 arbeitet (Dateiname sprachsteuerung.py):
#!/usr/bin/python
#encoding: utf-8
import paho.mqtt.client as mqtt
import paho.mqtt.publish as publish
import json
#Funktionen
def on_connect(client, userdata, flags, rc):
#print('Connected')
mqtt.subscribe('hermes/intent/#')
def on_message(client, userdata, msg):
# Parse the json response
messages = {}
intent_json = json.loads(msg.payload)
site_id = intent_json['siteId']
intentName = intent_json['intent']['intentName']
slots = intent_json['slots']
#print('Intent {}'.format(intentName))
for slot in slots:
slot_name = slot['slotName']
raw_value = slot['rawValue']
value = slot['value']['value']
geraet = slots[0]['value']['value']
#print('Slot {} -> \n\tRaw: {} \tValue: {}'.format(slot_name, raw_value, value))
messages.update({slot_name : value})
topic = (messages["geraete"])+"/cmnd/Power"
if messages["schalten"] == "1":
publish.single(topic, messages["schalten"], hostname = "192.168.178.20")
if messages["schalten"]== "0":
publish.single(topic, messages["schalten"], hostname = "192.168.178.20")
#Hier könnten noch jede Menge weiterer Auswertungen stehen....
#Hauptprogramm
mqtt = mqtt.Client()
mqtt.on_connect = on_connect
mqtt.on_message = on_message
mqtt.connect("192.168.178.20", 1883)
snips_base = "192.168.178.20"
mqtt.loop_forever()
Alles anzeigen
Dieses Skript muss noch mit dem Befehl chmod +x sprachsteuerung.py ausführbar gemacht werden und kann nun mit der Eingabe ./sprachsteuerung.py gestartet werden. Nun sollte, wenn z. B. der Satz "Hey Snips, schalte den Fernseher ein" gesprochen wird, das MQTT-Topic "Fernseher/cmnd/Power" mit der payload "1" ausgesendet werden.
Überprüfen kann man das z. B. praktisch, indem man einem Testgerät den MQTT-Namen (topic) "Fernseher" gibt, z. B. auf der Tasmota-Console durch topic Fernseher oder eher "virtuell" indem wir eine weiteres Terminal öffnen und dort mit mosquitto_sub -h <IP MQTT-Broker> -t Fernseher/stat/# das Status-Topic abonnieren (subskribieren) . Damit können wir kontrollieren, ob das Topic (richtig) übermittelt wird.
Sicherlich kann man die Auswertung des JSON-Strings auch anders vornehmen. Dies ist eine quick-and-dirty-Version um die Weiterverarbeitung der von Snips generierten MQTT Botschaften zu demonstrieren.
Zum Schluss dieses Artikels möchte ich noch vorstellen, wie man einen systemd-Starter anlegt, damit das Skript bei einem (Neu-) Start des Rechners gleich geladen wird und von Anfang an läuft um die Sprachbotschaften umzusetzen:
Auf dem Raspberry Pi wird mit dem Befehl
sudo nano etc/systemd/system/multi-user.target.wants/sprachsteuerung.service
eine neue Datei anlegen. Der Inhalt dieser Datei soll so aussehen:
[Unit]
Description=Starte das Auswertungsskript für Snips
After=network.target
[Service]
User=pi
Group=pi
Type=simple
WorkingDirectory=/home/pi
ExecStart=/home/pi/sprachsteuerung.py
#RestartSec=15
#Restart=always
[Install]
WantedBy=multi-user.target
Alles anzeigen
Mit STRG-x und y die Datei speichern und verlassen. Danach noch folgende Eingaben:
sudo systemctl enable sprachsteuerung.service
sudo systemctl start sprachsteuerung.service
Nun läuft die Sprachsteuerung und würde nach jedem Neustart automatisch gestartet. Wenn die Kommentarzeichen # in den beiden Restart-Zeilen entfernt werden startet das Skript auch im Falle von Fehlern (z. B. Unerreichbarkeit des MQTT-Brokers) immer wieder neu. Sonst würde es bei einem solchen Zustand/Fehler einfach stoppen und die Sprachbefehlsausführung nicht mehr umsetzen.
Grundsätzlich ist es möglich, dass die Ausführung von Skripten im Prinzip in Snips-Assistenten mit angelegt werden kann. Die Bezeichnung von Snips dafür ist es, eine action zu definieren. Diese wird in der Snips-Console an der Stelle angelegt, wo auch ein Intent definiert wird. Rechts neben dem Tab Intent befindet sich der Tab Action. Dort kann man unter Nutzung diverser Hermes-*-Python-Bibliotheken mehr oder weniger komplexe Skripte programmieren, die fest mit Snips verbunden werden. Doch die Aktivierung dieser Skripte ist nicht gerade ein Selbstläufer. Deshalb möchte ich hier nicht darauf eingehen.