FHEM und Rhasspy

Begonnen von drhirn, 28 Juli 2020, 14:28:50

Vorheriges Thema - Nächstes Thema

drhirn

Rhasspy ist ein Open Source offline Sprachassistent, der aus der Übernahme von Snips durch Sonos entstanden ist.

Für die Integration in FHEM gibt es ein Modul, welches auf dem Snips-Modul von Thyraz basiert.


Das Modul und die Anleitung sind hier zu finden: https://github.com/fhem/fhem-rhasspy


Bitte beachtet, dass es sich dabei um ein Modul in Entwicklung handelt. Fehler sind nicht ausgeschlossen.
Eine Mitarbeit am Modul ist gerne gesehen!

Weitere Informationen:

Derzeit (ab 10.04.2021) läuft ein Test über alle Intents. Mitarbeit ist herzlich willkommen. Die betreffenden Beiträge sind hier:

jowe

So, ich habe mittlerweile Rhasspy zusammen mit dem Snips Modul ans Laufen bekommen.
Wichtig sind zwei Dinge, damit es funktioniert:
In Rhasspy müssen die Sätze in sentences.ini und die slots identisch zu den Benennungen in der Snips FHEM App angegeben werden. Also für An/Aus z.B. in der Sentences.ini
[de.fhem:SetOnOff]
$Device{Device} $Room{Room} $Value{Value}
$Value{Value} $Device{Device} $Room{Room}
$Value{Value} $Device{Device}
$Device{Device} $Value{Value}
$Room{Room} $Device{Device} $Value{Value}
$Device{Device} und $Device{Device} $Value{Value}
$Device{Device} und $Device{Device} $Room{Room} $Value{Value}


und im entsprechenden slot "Value" z.B.
(aktiv|aktivieren|an machen|an schalten|einschalten|ein|anschalten|aktiviere|anmachen|schließe|schließen|runter|zu|ausfahren|rausfahren|an):an
(ausschalten|ab|abschalten|deaktiviere|ausmachen|öffne|öffnen|auf|einfahren|reinfahren|hoch|aus machen|ausmachen|aus schalten|aus schalte|aus):aus


Damit es in FHEM keine Dauerschleife gibt, muss man noch in der Snips.pm als quick-and-dirty Lösung die Zeile 900 abändern, so dass anstatt =~ auf !~ geprüft wird, sollte dann so aussehen:
elsif ($topic eq "hermes/nlu/intentParsed" && ($message !~ m/fhem.voiceCommand/ || $message !~ m/fhem.textCommand/)) {
So scheint es zu funktionieren.

drhirn

#2
Du hast aber einen "Snips-Satelliten", oder? Ich habe irgendwie so das Gefühl, die senden andere MQTT-Daten als ein "Rhasspy-Satellit".

Edit:
Oh, das funktioniert auch mit Rhasspy-Satelitten. Cool!

Super! Punkt 1 auf der Liste. Ich habe gestern noch an einer (unwichtigen) Lösung gebastelt, dass die "listening_"-Readings richtig geändert werden. Ist aber noch nicht marktreif. Und Text-Kommandos über das Snips-Modul gehen noch nicht.

jowe

#3
Sollte eigentlich nicht so sein. Der Satellit sendet ja nur den Ton, NLU und ASR laufen ja auf der "Basis", das heißt die an das Snips Modul übergebenen MQTT Daten sollten identisch sein, egal von welchem Satelliten der Ton kam.
Ich habe ehrlich gesagt auch bisher fast ausschließlich mit der "Recognize" Funktion auf der Rhasspy-Webpage getestet, der Satellit sollte da also garnicht mit reinspielen

Edit: Meine Antwort kam zu langsam...

Zu deinem Edit: Die spezifischen Funktionen say, inject und textCommand werden vermutlich einige Änderungen am Snips-Modul benötigen. Ich vermute, das muss als Rhasspy-Modul umgeschrieben werden. Injection funktioniert ja komplett anders, als bei Snips. Und der workaround mit der Änderung in Zeile 900 ist ja auch nicht sehr schön, da muss auf jeden Fall auch noch was besseres her. Ich habe auch noch die Zeile auskommentiert, in der die geänderte payload (mit "Standardgerät"/"Standardwert"/ usw.) gesendet wird.
Ich bin jetzt erstmal froh, dass wenigstens das Schalten mit Kommandos funktioniert. Und als nächstes schau ich mal, ob ich die bescheidene Erkennungsrate bei Spracheingabe noch in den Griff bekomme. Die Erkennung ist aktuell bei mir noch viel schlechter als bei Snips

drhirn

Zu meinem angesprochenen Thema mit den "listening_"-Readings: Rhasspy agiert da offenbar anders als Snips. Das Reading bleibt bei mir nur ganz kurz auf "1" und geht dann sofort wieder auf "0".
Wenn ich das richtig verstanden habe, soll das anzeigen, dass Snips gerade auf einen Sprachbefehl hört. Nach meiner Logik wäre das also alles zwischen hermes/dialogueManager/sessionStarted und hermes/dialogueManager/sessionEnded.

Ist im Modul leicht umgesetzt:

Die Zeilen 31 + 32

    hermes/hotword/+/detected
    hermes/hotword/toggleOn


durch

    hermes/dialogueManager/sessionStarted
    hermes/dialogueManager/sessionEnded


ersetzen.

Zeile 792
if ($topic =~ m/^hermes\/hotword/) {
durch
if ($topic =~ m/^hermes\/dialogueManager/) {

Zeile 802 bis 806

            if ($topic =~ m/detected/) {
                readingsSingleUpdate($hash, "listening_" . lc($room), 1, 1);
            } elsif ($topic =~ m/toggleOn/) {
                readingsSingleUpdate($hash, "listening_" . lc($room), 0, 1);
            }

durch

            if ($topic =~ m/sessionStarted/) {
                readingsSingleUpdate($hash, "listening_" . lc($room), 1, 1);
            } elsif ($topic =~ m/sessionEnded/) {
                readingsSingleUpdate($hash, "listening_" . lc($room), 0, 1);
            }

drhirn

Ist außer mir auch noch jemand der Meinung, dass wir die Zeilen 831 (my $info, my $sendData;) bis inkl 900 (elsif ($topic eq "hermes/nlu/intentParsed" && ($message =~ m/fhem.voiceCommand/ || $message =~ m/fhem.textCommand/)) {) eigentlich gar nicht brauchen? Weil Rhasspy ja eigentlich nichts von Standardraum, Standardgerät, etc. weiß.
Zumindest ich hatte bisher gute Erfahrungen ohne diese Zeilen. Ich kann vom Rhasspi UI und mit FHEM set Snips textcommand ... Befehle absetzen, die dann auch interpretiert werden. Mehr habe ich noch nicht getestet.

Das mit dem Slot-Namen ([de.fhem:SetOnOff]) hat übrigens den Grund, dass das Modul nur auf Intents reagiert, die einen Doppelpunkt im Namen haben. War meine größte Erkenntnis heute.

jowe

Ja, ich habe die Zeilen vorher auch komplett bei mir gelöscht. Geht immernoch... Deine Änderung zum Thema Listening scheint bei mir aber noch nicht recht zu funktionieren. Habe es aber auch nur auf die Schnelle geändert, evtl. ist dabei was schief gelaufen

drhirn

Geht bei mir auch nicht immer. Mir scheint, da werden MQTT Nachrichten durcheinander gewürfelt. Oder hin und wieder ausgelassen. Aber, theoretisch wär's richtig ;).

drcyber

Thx @ drhirn  für den neuen Thread :)

bei mir spricht jetzt Rhasspy mit FHEM, zumindest kommen in Fhem sachen an die ich Rhasspy sage.

dann weiß ich aber nichtmehr recht weiter..

also ich sage zb "schalte wohnzimmerlampe aus"

Fhem bekommt dann :


{"Device":"wohnzimmerlampe","Value":"aus","input":"wohnzimmerlampe aus","intent":"SetOnOff","probability":0.5,"requestType":"text","sessionId":"default-blueberry-8c3241d5-1414-4771-a5a6-469b38b77b64","siteId":"default"}



Die Wohnzimmerlampe in FHEM hat folgende Attribute (unter anderem)

snipsName wohnzimmerlampe
snipsRoom default
snipsMapping  ein=on,an=on,aus=off


stimmt das überhaupt so? bzw reicht das , oder stelle ich mir das zu einfach vor?

LG Markus
Raspberry Pi 3 mit 2x nanoCUL - Jeelink- LGW

jowe

Hi drcyber,

die attribute sind so nicht korrekt. Bei mir sieht das für das Device HM_420E4D_Sw (=meine Wohnzimmerlampe) so aus:

attr HM_420E4D_Sw snipsColors hell=pct 100 dunkel=pct 10
attr HM_420E4D_Sw snipsMapping SetOnOff:currentVal=state,cmdOn=pct 20,cmdOff=off\
GetOnOff:currentVal=state,valueOff=off\
SetNumeric:currentVal=pct,cmd=pct,minVal=5,maxVal=100,step=5,type=Helligkeit\
GetNumeric:currentVal=pct,type=Helligkeit\
Status:response="Die Wohnzimmerlampe ist auf [HM_420E4D_Sw:pct]%"
attr HM_420E4D_Sw snipsName Wohnzimmerlampe
attr HM_420E4D_Sw snipsRoom Wohnbereich, Wohnzimmer,Wohnzimmer2


Gruß Jonas

drhirn

#10
So, da ich die nächsten zwei Wochen keine Zeit haben werde, lasse ich meinen aktuellen Stand der 10_SNIPS.pm mal hier. Ich seid herzlich eingeladen zu testen und daran weiter zu arbeiten.

Getestete Intents: SetOnOff, GetOnOff, GetNumeric
Gibt aber keinen Grund, warum die anderen nicht auch gehen sollten.

textCommand geht auch, liefert aber gerade keine korrekte Antwort. Da habe ich beim Aufräumen des Codes was verbastelt.

say geht nicht. Weil parseParams in 99% aller Fälle nicht richtig trennt. Warum auch immer.

Shortcuts gehen natürlich auch nicht

Die Intents (sentences.ini) müssen mit Doppelpunkt benannt werden. Also am Besten so, wie sie bei Snips hießen (de.fhem:SetOnOff, de.fhem:GetOnOff, etc.)

Meine sentences.ini sieht derzeit so aus (nur so als Beispiel, ist ein ziemlicher Saustall):


[de.fhem:SetOnOff]
(Radio|Boombox){Device} [(wohnzimmer|kueche|bad|küche|klo|klö){Room}] $OnOffValue{Value}

[de.fhem:GetOnOff]
($OnOffValue|ist:an){Status} (Radio|Boombox){Device} [(wohnzimmer|schlafzimmer){Room}] [$OnOffValue{Status}]

[de.fhem:GetNumeric]
(wie laut|wie ist die lautstärke){Type:Lautstärke} (Radio|Boombox){Device} [(im|in der|auf der|draußen|auf dem)] [(wohnzimmer|kueche|bad|küche|klo|klö){Room}]
(wie ist die|wie warm ist es){Type:Temperatur} [temperatur] [(thermometer){Device}] im [(wohnzimmer|kueche|bad|küche|klo|klö){Room}]
\[(wie|wie ist die)] (hell|helligkeit){Type:Helligkeit} (Radio|Boombox){Device} [(wohnzimmer|kueche|bad|küche|klo|klö){Room}]


Hin und wieder gibt's Probleme mit Umlauten. Habe aber noch nicht herausgefunden, warum und wann.

Gruß
Stefan

drhirn

#11
Ich habe im ersten Beitrag eine weiter umgebaute Version des Snips-Moduls angehängt. Einzig große Neuerung ist, dass vom Modul aus die Slots befüllt werden können. Pseudo-Injection sozusagen. Dazu werden alle snipsNames, -Rooms, -Colors, etc. aus den Attributen der Devices im Raum "Rhasspy" gesammelt und als JSON an die HTTP-API von Rhasspy gesendet.
Vorsicht: Bestehende Slots werden überschrieben!
Die Slots haben dann Namen wie de.fhem.Device, de.fhem.Room, etc.
Ein ASR+NLU Training ist dann aus dem Modul heraus auch möglich.

Damit updateSlots und trainRhasspy funktionieren, muss das Attribut rhasspyMaster gesetzt werden. Und zwar auf die URL des Rhasspy-Masters (z.B.: http://rhasspy.example.com:12101).

Außerdem heißt's nicht mehr "SNIPS", sondern "RHASSPY". Somit heißen auch die Attribute rhasspyName, rhasspyRoom, rhasspyMapping, etc.

Ansonsten noch immer die Probleme von oben. TextCommand geht aber.

Tests + Verbesserungen sind herzlich willkommen.

bennebartsch

Habe gerade mal deine 10_RHASSPY.pm getestet. Klappt soweit ganz gut, nur irgendwie habe ich Probleme mit Geräten bei denen ich mehrere Namen vergeben habe:
attr licht rhasspyName Licht,Lampe
Es werden 2 Slots hinzugefügt, Licht und Lampe. Es funktioniert aber leider nur Lampe.

Wäre es nicht besser das rhasspyName attribut im Rhasspy Syntax zu schreiben:
attr licht rhasspyName (Lampe | Licht)
und dann die Slots so generieren:
(Lampe | Licht):licht

bennebartsch

Ich nehme alles zurück, mehrere Namen mit Komma funktionieren doch. Trotzdem fände ich es besser FHEM den Rhasspy Syntax beizubringen.

drhirn

Warum fändest du das besser?
Das Problem ist dann nämlich, dass FHEM (bzw. das Modul) auch was mit licht anfangen können muss. Und woher soll FHEM/Rhasspy dann wissen, dass es den Slot/Namen mit licht ergänzen muss?