Sprachbefehl an python programm weitergeben / Give voice command to python program?

Hallo Leute,
sorry mein Englisch ist nicht so gut, daher versuche ich es auf deutsch. rhasspy an sich habe ich ans Laufen bekommen. Leider habe ich Probleme den Sprachbefehl weiterzuleiten. node-red “schrottet” meine Sprachausgabe, daher habe ich es wieder abgestellt. Am liebsten wäre es mir auch ich könnten den Befehl an ein Python-Programm weitergeben und dort meine Aktionen ausführen. Gibt es dafür eine einfache(!) Anleitung? Danke für Hilfe.
Viele Grüße
Horst

here my attempt to formulate it in english:
Hello folks,
sorry my english is not so good, so i try to speak german. rhasspy itself got going. Unfortunately I have problems to forward the voice command. node-red “scraps” my voice output, so I turned it off again. I would like to be able to pass the command to a Python program and execute my actions there. Is there a simple(!) manual for this in germany? Thanks for help.
Many greetings
Horst

:de: Ich habe es selbst noch nicht ausprobiert, aber du kannst einen “Intent” per JSON an ein beliebiges Programm weitergeben, siehe Intent Handling > Command. Es gibt sogar ein kurzes Python-Skript als Beispiel.

Oder du reichst das JSON per POST-Rquest an ein Skript weiter, das auf einem anderen Server liegt, wie auf der gleichen Seite beschrieben.

Hilft dir das weiter?

Viele Grüße
Matthias

Das mit dem Programm funktioniert, in 2.5 ist aber mqtt eigentlich der Weg, den man gehen will. Rhasspy sendet immer, wenn es etwas tut diese Nachricht an einen mqtt Server. Standardmäßig ist das der interne Server den es selbst aufmacht, man kann aber auch einen externen benutzen. Ich selbst habe bis jetzt nur den Externen benutzt, man kann aber auch an den internen Server kommen. Bei einer Installation mit docker muss man allerdings den Port noch nach außen hin freigeben.

Das System mit mqtt läuft so, dass man mit seinem eigenen Programm (oder node-red) eine Verbindung zum Server aufbaut und dort auf einen Intent wartet den rhasspy schickt, sobald es ihn erkennt. Dann verarbeitet man diesen und schickt eine Antwort zurück. Großer Vorteil ist, dass man seinen eigenen Code nicht irgendwie in den rhasspy docker reinbekommen muss und man auch nicht in das Problem läuft, im rhasspy docker irgendwelche Abhängigkeiten zu installieren.

Für mqtt gibt es hier eine Bibliothek die einem das vereinfacht, man kann aber problemlos auch selbst etwas schreiben oder mit node-red auf einen Intent warten.

Daenara

1 Like

Hallo Leute,
danke für die freundlichen Antworten. Leider helfen sie mir nicht weiter. Ich habe ein Python-Programm in der Intent-Einstellung hinterlegt:
Leider darf ich nur ein Bild einfügen, daher versuche ich die Konfiguration zu beschreiben. Im Intent Handling habe ich es auf Local Command gesetzt und in der Zeile Programm steht:
sudo python3 /home/pi/bin/pyintent/handle.py
Der Befehlt ansich wird auch nach einer Spracherkennung ausgeführt. Nur das json-Script wird nicht erkannt.
Das Programm sieht dann wie aus dem Beispiel wie folgt aus:


Leider wird json.loads aber gar nicht erkannt und daher die if-Abfrage auch nicht ausgeführt. Nur der Befehl “Hast Du mich verstanden” wird ausgeführt. Wo liegt der Fehler?

Ich bin mir relativ sicher, dass es daran liegt wie du das Programm aufrufst. An sich macht rhasspy das von selbst, aus seiner python Instanz raus. Wenn du es mit sudo python3 aufrufst verlierst du vermutlich die Konsolenausgabe die dein Programm versucht als json einzulesen.

Hier ist, wie ich mein Skript aufrufe:
image

Wichtig hierbei ist, dass mein Skript selbst aus dem Docker Container heraus erreichbar ist, ich habe dafür den Ordner in dem es liegt in meiner docker-compose.yml als “Laufwerk” (volume im Englischen, ich hab noch nie was deutsches zu Docker gelesen, daher bin ich mir nicht sicher, ob das der richtige Begriff ist) eingebunden.

Das hier ist der rhasspy Teil meiner docker-compose, das, was rot durchgestrichen ist hat nichts mit deiner Frage zu tun und ist daher nicht relevant. Wie ´man sieht habe ich das Verzeichnis ./rhasspy_2.5/rhasspy_weather_git von meinem Host System nach /opt/rhasspy_weather_git gelinkt, weshalb rhasspy den Pfad /opt/rhasspy_weather_git kennt und das Skript darin finden kann.

Weil ich rhasspy nur sage, wo genau das Skript liegt und es rhasspy überlasse, es auszuführen werden die Daten, die rhasspy für das Skript auf der Konsole ausgibt auch vom Skript eingelesen.

Falls du kein Docker benutzt sondern rhasspy direkt installiert hast, kannst du dir natürlich das einbinden über docker sparen und einfach den absoluten Pfad zu deinem Skript angeben.

Daenara

Hallo Daenara,
danke für deine Antwort. Aber dies war es leider nicht. Ich habe rhasspy nicht mit docker installiert, daher muß ich docker auch nicht einrichten und es sollte direkt aufgerufen werden. Die Intent-Konfiguration habe ich wie folgt geändert:
rhasspy-intent2
Aber leider kein Unterschied. json wird immer noch nicht erkannt. Was könnte es noch sein?
Viele Grüße
Horst

Dann bleibt nicht viel anderes übrig als Schritt für Schritt den Code zu testen. Ausgabe per print funktioniert in Kombination mit dem Skript nicht, daher brauchen wir ein Log.

Füg mal bitte folgendes in dein Programm ein:

import logging

logging.basicConfig(filename='/home/pi/bin/pyintent/rhasspy_debug.log')

logging.debug("lese Eingame")
o = json.loads(sys.stdin.read())
logging.debug(o)

logging.debug("Intent Name:")
intent = o["intent"]["name"]
logging.debug(intent)

if intent == "GetTime":
    logging.debug("Zeit gewünscht")
    # rest vom if hier
elif intent == "Hello":
    logging.debug("Begrüßung gewünscht")
    # rest vim elif hier

logging.debug("Ifs fertig")
speech("hast du mich verstanden")

Das sollte alles in eine Datei namens rhasspy_debug.log schreiben was im Code per logging.debug ausgegeben wird. Mit den Zeilen, die ich eingefügt habe sollte der Inhalt der Variablen (oder die Abwesenheit von Inhalt) im log stehen.

Hallo Daenara,

deine logging-befehle funktionieren leider nicht, darum habe ich mal recherchiert und das Python-Programm wie folgt geändert:

#!/usr/bin/env python3

import sys
import json
import random
import datetime
import logging
import logging.handlers
import os

def speech(text):
    global o
    o["speech"] = {"text": text}

#logging.basicConfig(filename='/home/pi/bin/pyintent/rhasspy_debug.log')
handler = logging.handlers.WatchedFileHandler(
    os.environ.get("LOGFILE", "/home/pi/bin/pyintent/rhasspy_debug.log"))
formatter = logging.Formatter(logging.BASIC_FORMAT)
handler.setFormatter(formatter)
root = logging.getLogger()
root.setLevel(os.environ.get("LOGLEVEL", "INFO"))
root.addHandler(handler)
logging.exception("gestartet")

# get json from stdin and load into python dict
o = json.loads(sys.stdin.read())
logging.exception(o)

logging.exception("Intent Name:")
intent = o["intent"]["name"]
logging.exception(intent)

if intent == "GetTime":
    logging.exception("Zeit gewünscht")
    now = datetime.datetime.now()
    speech("Es ist %s Uhr %d" % (now.strftime('%H'), now.minute))

elif intent == "Hello":
    logging.exception("Hallo gewünscht")
    replies = ['Hai!', 'Hallo!', 'Wie geht es!', 'Guten Tag!']
    speech(random.choice(replies))

elif intent == "ChangeLightState":
    logging.exception("Licht gewünscht")
    value = o["entities"][0]["value"]
    logging.exception(value)
    if value == "ein":
      logging.exception("Licht ein gewünscht")
      speech("Ich schalte jetzt das Licht ein")
      os.system("/home/pi/bin/smartplugswitch/smartplug.sh on")
    else:
      logging.exception("Licht aus gewünscht")
      speech("Ich schalte jetzt das Licht aus")
      os.system("/home/pi/bin/smartplugswitch/smartplug.sh off")

elif intent == "MeldunganRobby":
    logging.exception("Toll gewünscht")
    speech("Danke! Du bist auch toll")

else:
    speech("Hast Du mich verstanden")

logging.exception("Ist fertig")
# convert dict to json and print to stdout
print(json.dumps(o))

Woran es jetzt genau gelegen hat, kann ich nicht genau sagen. Aber man glaubt es kaum: Es funktioniert! Danke für die Hilfestellung. Damit ist mein Problem gelöst.
Viele Grüße
Horst

Meine logging Befehle wurden im rhasspy forum gecodet, es kann sein, dass sich da ein Fehler eingeschlichen hat.

Freut mich, dass es jetzt funktioniert, auch wenn ich mir nicht erklären kann, warum. Guck mal in dein generiertes log rein, vielleicht findest du da einen Eintrag den du nicht in deinem Programm geschrieben hast. Logeinträge werden per default per Konsole ausgegeben, vlt hat rhasspy mehr ausgegeben als es sollte und das hat das json verwirrt. Aber solange es funktioniert und es nicht plötzlich nicht mehr geht ist ja alles gut.

Ich denke das einfachste/flexibelste dürfte sein das folgende zu nutzen:

Es kann dann auch auf jedem beliebigen Computer in deinem Netzwerk ausgeführt werden. Wenn du in dem Thread schaust, findest du auch ein paar Beispiele (z.B. das Timer Script von mir).

Im Wesentlichen schreibst du eine Methode mit passendem Decorator (Name des Intents). Die wird dann aufgerufen und wenn du sowas wie

return EndSession(f"Es verbleiben {remaining_seconds:.0f} Sekunden")

machst, wird es dann von Rhasspy ausgesprochen.

2 Likes