CustomIntent mit Dialog

Begonnen von gregorv, 03 Oktober 2024, 10:54:24

Vorheriges Thema - Nächstes Thema

gregorv

Version zwischenzeitlich eingechecktIst OK, Stand jetzt ist nichts aufgefallen

ZitatTestsuite für Sprachsteuerung und [RHASSPY] - Einstellungen optimieren
Stimmt, irgendwo mal gelesen - danke für die Links. Ich sagte ja, dass ich mit Rhasspy noch Anfänger bin - die Informationen und Threads, die es da gibt, habe ich noch lang nicht durch.
Mit FHEM bin ich aber schon lange unterwegs - so lange, das zwischendrin schon mein Forum-Zugang verschwunden war. Die alten Beiträge sind noch da - im Grund bin ich also Doppel Junior.

Zitatvermutlich macht RHASSPY auch das Mikro wieder auf. Was fehlt ist das "saubere Ende"
Teste ich nachher auch mal - nach dem Code sollte neue session und timeout funktionieren

Zitatfehlt in handleCustomIntent noch eine Verzweigung für den neuen "reopenVoiceInput"-Key
Ist wohl nicht nötig, es reicht, den Key noResponse zu entfernen, was ich in meiner Blind() z.B. beim Kommando stop machen könnte werde (dann kann ich das gleich da testen). Das stop ist das einzige Kommando, was keine Verlängerung des Dialoges braucht. Da habe ich ohnehin schon die Audio-Ausgabe gelöscht, weil man ja kaum Stop sagen wird, wenn man das Resultat nicht sieht. Die entferne ich bei mehr, weinger... und so auch noch.

Ein Info noch...
Derzeit habe ich die Möglichkeit den value ohne Mapping durchzuschleifen (temporär und nur für mich) in die handleIntentSetOnOffGroup doch wieder eingebaut, damit ich testen kann. Wenn ich mit Aufräumen fertig bin, schau ich mal wie es mit der handleSetNumeric()/(Group()) gehen könnte.

Uff... gerade ein Fehler - die Keys:
    "reopenVoiceInput"=>"true",
     "noResponse"=>"true",
       "SilentClosure"=>$newTimeout, <---- Wert 10 (auch 10.0 getestet)
sind gesetzt und der Dialog wird gestartet.

Dann den sessionTimeout ablaufen lassen, oder Stop sagen).
Nach etwa 20 Sekunden (das könnte mein ursprünglicher sessionTimeout (21) sein) wird eine neue session gestartet und exakt 10 Sekunden später ($newTime oben ??) kommt folgende Meldung:
Can't use an undefined value as an ARRAY reference at /opt/fhem/FHEM/10_RHASSPY.pm line 3117.
2024.10.28 14:43:18 1: PERL WARNING: Prototype mismatch: sub main::ctime: none vs (;$) at /usr/local/share/perl/5.32.1/Exporter.pm line 66.
gefolgt von einem FHEM Restart.

Die Zeile 3117 ist in der testmode_end():
$result .= join q{ => }, @{$hash->{helper}->{test}->{result}->{0}};
Log dazu angehängt

gregorv

#106
Nachtrag:
das Problem tritt auch auf, wenn ich bei einem sentence im SetOnOff Intent noch {reopenVoiceInput:true} anhänge.

Wieder was gelernt: mit []{reopenVoiceInput:true} []{SilentClosure:20}im sentence kann man auch mehrere Parameter aus dem Sentence in $data übernehmen

Beta-User

Zitat von: gregorv am 28 Oktober 2024, 15:35:10Die Zeile 3117 ist in der testmode_end():
Ups, dann wird es wohl dringend, dass eine neue timeout-Routine dazukommt O:-) ...

(es sollte eigentlich für's erste reichen, über den timer eine dummy-Funktion aufzurufen, die dann schlicht ein "return" ausführt. Falls du verstehst, wie ich das meine)...

Sorry!
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

gregorv

Falls du verstehst, wie ich das meine)..noch nicht so ganz... help please

meinst Du, in RHASSPY_DialogTimeout()

Beta-User

Nein, eine neue Funktion.

testmode_end() wird (indirekt?) aufgerufen, Quelle ist an der Stelle, wo der Key geprüft wird. Da steht auch ein Kommentar von mir dazu.
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

gregorv

DONE, funktioniert:

  • roberta ->ja?
  • mach das licht an -> licht geht an -> ok
  • mach das lich aus -> licht geht aus -> zu diensten
  • nach 30 Sekunden -> Dialog Ende
SUPER!

Beta-User

#111
Zitat von: gregorv am 28 Oktober 2024, 16:27:17SUPER
8)

Anbei eine Fassung, die (hoffentlich) etwas mehr macht wie nur return (aufräumen...) und den timeout uU. auch aus dem reopenVoiceInput-Key holt, falls nicht SilentClosure explizit gesetzt ist. Werde das vermutlich spätestens morgen früh einchecken, falls nicht noch (größere oder andere) Probleme auftauchen...

Was mir gedanklich mindestens noch fehlt, ist das "Abräumen" für den Fall, dass tatsächlich eine Ansage gemacht wird. Da im Moment aber sowieso bei timer-Ende nichts weiter passiert wie Aufräumen, ist das nicht weiter tragisch, aber sobald da eine Ansage stattfinden soll, irgendwelche Sitzungsdaten gespeichert sind etc. pp. könnte es "komisch" werden. War vermutlich einer der Gründe, warum ich gestern dann nicht weiter an dem Code gebastelt hatte.

Wie du vermutlich im Code gesehen hast, läßt sich das feature auch über das RHASSPY-define (global, also für alle Intents und alle Satelliten) aktivieren, so dass man nicht zwingend über einen CustomIntent kommen muss.
Zitat von: gregorv am 28 Oktober 2024, 15:44:47Wieder was gelernt: mit []{reopenVoiceInput:true} []{SilentClosure:20}im sentence kann man auch mehrere Parameter aus dem Sentence in $data übernehmen
Das ist ein cooles finding!
Es erleichtert es ggf. sehr, das feature sehr gezielt einzusetzen.

"Eigentlich" sollten wir damit dein eigentliches Anliegen weitestgehend gelöst haben, oder? Es gibt dann zwar keinen Rollladen-spezifischen Timer für den onOff-Intent, und du kannst auch "alles" sagen, solange das Mikro offen ist, aber setNumeric (?) mit "stop" sollte klappen. Hier meine sentences dazu:
( stoppe | stop ){Change:cmdStop} [<den>] $de.fhem.Device-blind{Device} [<rooms>]
( halte | halt ){Change:cmdStop} [<den>] $de.fhem.Device-blind{Device} [<rooms>] an
Schadet ja nicht, wenn das auch funktioniert, wenn man mal eine Taste gedrückt hat...

Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

gregorv

Zitat...irgendwelche Sitzungsdaten gespeichert...
ich sehe zwar im Code beim Beenden der Sitzung:
delete $hash->{helper}{'.delayed'}{$identity};ABER könnnte das überhaupt passieren, dass wenn eine neue sessionId vergeben wurde der Inhalt von der alten sessionId unter der neuen auch erscheint ? Das müsste dann doch irgendwo explizit kopiert worden sein - oder ?
Ich baue ja gerade meine Blinds() um, da benutze ich:
my $data_old = $defs{'RHASSPY.IF'}->{helper}->{".delayed"}->{$data->{sessionId}} // undef ; # get old data if exist - means that a dialog is open
und
if (defined $data_old) {
um zu prüfen, ob die session neu ist, oder schon existiert. Wenn da irgendwie Daten von einer vorherigen Session drin sein könnten, ist die Prüfung nicht zuverlässig.
Bereits getestet hatte ich: Dialog starten und während der läuft, auf dem Rhasspy WEB Interface einen anderen Intent starten. Funktioniert, einwandfrei, im Log taucht temporär eine neue sessionId auf und ein anschließendes stop geht auch noch.
Ich habe mal das Log genauer geprüft und sehe unter der neuen sessionId tatsächlich einen Wert (aber nur einen) von meinem customData hash. Der betreffende Wert ist da drin der letzte. Ich mach mal einen Log Dump vom hash bevor der neue Intent bearbeitet wird würde ich erwarten, dass kein old_data da ist. Sind auch sonst keine alten keys drin.
 

ZitatDas ist ein cooles finding!
Ist einer der Vorteile von try&error :) Das steht vermutlich nicht einmal in einer Rhasspy Best Practice
Zitat"Eigentlich" sollten wir damit dein eigentliches Anliegen weitestgehend gelöst haben
Ja, absolut - sogar mehr als das.
Rundum erfolreicher Sprint, sogar ohne dauerde Telko's und umfangreichen RequestForChange.

Hole mir jetzt mal die neue, aber heute werde ich wohl nicht mehr so viel machen.
Aber sicher werde ich noch Prüfen,ob der CancelIntent jetzt wirklich noch alles beendet.
Meine Frau kam gerade ins Zimmer, als ein Dialog offen war und sich dann empört, dass Roberta dauernd dazwischen geredet hat.
Dabei ist mir aufgefallen, dass reopenVoiceInput den sessionTimer immer neu setzt - da kommt dann echt wieder der Zähler ins Spiel, ggf. nur bei IntentNotRecognized. Das erhöht den WAF-Faktor.


gregorv

#113
Info:
mit reopenVoiceInput ist offenbar der Intent CancelAction NICHT aktiv.
Es kommt IntentNotRecognized aber immerhin ist dann der Dialog beendet (ich meine, das war mit der vorherigen Version nicht so).

Jetzt ist die sessionTimeout Einstellung für reopenVoiceInput OK (auch wenn der Wert dafür mit reopenVoiceInput übergeben wird .

gregorv

habe noch einen Fehler gefunden. Der IntentFilter wurde beim publish nicht gesetzt.
Der erscheint zwar im hash, aber muss mit dem prefix "de.fhem:" auch an die respond() weitergegeben werden.
In der setDialogTimeout() bei:
    my @ca_strings;
    $toEnable = split m{,}xms, $toEnable if ref $toEnable ne 'ARRAY';
    if (ref $toEnable eq 'ARRAY') {
        for (@{$toEnable}) {
            my $id = qq{$hash->{LANGUAGE}.$hash->{fhemId}:$_};
            push @ca_strings, $id;
        }
    $response->{intentFilter} = [@ca_strings];
    }
das ist jetzt noch die Zeile 1741 $response->{intentFilter} = [@ca_strings]; dazugekommen.

Keine Ahnung, wann das untergegangen ist, vermutlich bei meiner Verschiebung der Code Teile von handleCustomIntent() zu setDialogTimeout().
Für reopenVoiceInput wird CancelAction dadurch leider nicht aktiv.


Sorry, Gregor

Beta-User

Sorry für die Stille; im Moment komme ich nicht dazu, mir das näher anzusehen, es wird mir allerdings immer deutlicher, dass wir ein gewisses session-Management auch für den Teil brauchen.

Was das mit dem intentFilter angeht, bin ich mehrfach "vorirritiert"; irgendwie müßte man das insgesamt anders lösen (und insbesondere die Möglichkeit lassen, den Filter nicht zu ändern), aber im Moment fürchte ich Nebenwirkungen, weil das intern auf eine bestimmte Weise genutzt wird.... Und das Bilden des/der Array-Strings könnte man auch in eine eigene (separate) Funktion auslagern (ist aber gefahrgeneigt). Unrund...
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

gregorv

#116
@Beta-User
Ich denke, dass noch eine Funktion ganz hilfreich wäre. Sowohl bei reopenVoiceInput wie auch bei wieBitte wäre es sinnvoll, die die session mit einem Sprachkommando stoppen zu können. CancelAction wäre geeignet, wenn man das
  • beim Start einer sesssion aktivieren könnte (habe ich noch nicht herausgefunden)
  • die Standard Audio Ausgabe 'habe abgebrochen' dynamisch ändern könnte
Situation: eine session wartet gerade auf Eingaben, aber das Telefon klingelt.
Damit Rhasspy nicht dauernd dazwischen reded, sollte ein sei still möglich sein.

ich habe derzeit einen CustomIntent CancelSession() wie folgt, eingesetzt.
[de.fhem:CancelSession]
\[bitte][ich] ((möchte | will) nichts [mehr] | nicht mehr zuhören | fertig | sei still | ruhe jetzt) {text:okeey}


# terminate listen mode, terminate session
sub CancelSession($) {
  my ($rawd)     = @_;
  my $data;
  if (defined $rawd && !eval { $data  = JSON->new->decode($rawd) ; 1 } ) {                    # convert JSON data
    Log 1, "JSON decoding error, $rawd seems not to be valid JSON data:  $@";                 # error handling
    return "Error! $rawd seems not to be valid JSON data!";
  }
  return $data->{text};
}

gregorv

#117
ZitatSorry für die Stille
Kein Problem ich hab noch ne Menge zu testen bei meinem Umbau.

ZitatWas das mit dem intentFilter angeht, bin ich mehrfach "vorirritiert";
ja, ich auch - gerade kam ein Fehler, genau an dieser Stelle.
Can't use string ("Du hast widersprüchliche Anga"...) as a HASH ref while "strict refs" in use at /opt/fhem/FHEM/10_RHASSPY.pm line 1741.

Problem: $response kann auch ein String sein, daher Umbau:
    $response->{intentFilter} = [@ca_strings] if ref $response eq 'HASH' ;
Außerdem habe ich die Zeile als 1742 eingefügt, unmittelbar vor my $reaction = ref ...
Jetzt geht es - irritation Ende (meine).

Das Problem mit dem fehlenden IntentFilter scheint mir aber sauber gelöst, der darunter stehende Teil, wo $reaction hash zusammengebaut wird, wird ja nur durchlaufen, wenn $response kein hash ist, daher fehlte der IntentFilter im hash Fall.

Die Zeile 1719:
delete $response->{intentFilter};kann damit raus, weil intentFilter weiter unten (mit Präfix) überschrieben wird.

Beta-User

Zitat von: gregorv am 30 Oktober 2024, 14:48:19Das Problem mit dem fehlenden IntentFilter
Weiß nicht recht, ob wir über dasselbe reden.
Mein Gedanke, ausgehend von
    my $toEnable = shift // [qw(ConfirmAction CancelAction)];Ist es richtig, dass es immer und zwangsweise einen (geänderten) intentFilter gibt?
Und (im HASH-Fall): Ist es richtig, dass diese Defaults ggf. das überschreiben, was der (CustomIntent-) Code explizit geliefert hat?

Anders gesagt: die Logik muss vom Ergebnis her gedacht so bleiben, dass im "es steht nirgendwo was"-Fall genau das rauskommt, was jetzt rauskommt (die defaults), aber es muss anders geprüft werden, ob der Code an anderer Stelle bereits mit anderen Vorgaben aufgerufen wurde. Wurde was in $toEnable übergeben, hat das Vorrang, stand was im HASH, dann das...

Und es muss (?) eine Möglichkeit geben, den bereits (in einer vorherigen Runde) gesetzten Filter nicht zu ändern.
Server: HP-elitedesk@Debian 12, aktuelles FHEM@ConfigDB | CUL_HM (VCCU) | MQTT2: MiLight@ESP-GW, BT@OpenMQTTGw | MySensors: seriell, v.a. 2.3.1@RS485 | ZWave | ZigBee@deCONZ | SIGNALduino | MapleCUN | RHASSPY
svn: u.a MySensors, Weekday-&RandomTimer, Twilight,  div. attrTemplate-files

gregorv

#119
ZitatIst es richtig, dass es immer und zwangsweise einen (geänderten) intentFilter gibt?
Nein, der kann auch bleiben. Prinzipiell ist es aber möglich bei jedem Aufruf eines CustomIntent den IntenFilter zu ändern.

ZitatUnd (im HASH-Fall): Ist es richtig, dass diese Defaults ggf. das überschreiben, was der (CustomIntent-) Code explizit geliefert hat?
Kommt drauf an, was Du meinst:
Nein, wenn der CustomIntent im hash (oder ToEnable) was liefert wird, kann das nicht durch die Defaults überschrieben werden.
ABER
Ja, alle IntentFilter müssen vor dem publish verändert werden weil Rhasspy die Intents nur mit einem Präfix (de.fhem:) erkennt und FHEM die nur ohne Präfix bearbeitet. Das ist sicher so gemacht, weil das de.fhem: ja bei jedem anders sein könnte. Also de.fhem:ConfirmAction statt ConfirmAction.

Deshalb wird auch die Zeile
$response->{intentFilter} = [@ca_strings] if ref $response eq 'HASH' ; benötigt, da stehen in @ca_strings die Intents mit Präfix.

Zitatwas jetzt rauskommt (die defaults)
Die default kommen nur, wenn im Argument ToEnable nichts drin steht UND im return hash der CustomIntent() (z.b. Blinds()) keinen Key intentFilter (mit gültigen Intents) hat.
Vermutlich kommst Du da drauf, weil das in der Fehlenmeldung stand - das war ein beliebiger Intent der mehrdeutig war. Rhasspy hatte verstanden 'mach das licht im Keller an' da gibt es aber derzeit nur die Heizung -> also Rückfrage und dabei setDialogTimeout() mit response als string, was natürlich schief geht (mächtig sogar - restart FHEM), wenn ich $response->{xxx} irgendwas zuweise.

Im Falle von Blinds() wird im hash der Key intentFilter mit ["Blinds", "CancelSession"] übergeben. Die setDialogTimeout() überschreibt damit die default Werte ODER auch Werte die ggf. mit Argument ToEnable übergeben werden. Das hatte ich mal so gebaut, dass Argument ToEnable vorrangig wäre, hast Du aber entfernt. Ich nehme daher an, das kommt ohnehin nicht vor, dass ToEnable UND ein hash mit key intentFilter als Argument response übergeben werden. Genau so übrigens auch mit sessionTimeout, das steckt auch im hash - könnte natürlich auch im timeout Argument übergeben werden. Wollte man ToEnable und timeout explizit mit den Argumenten übergeben, müsste man die Werte in der handleCustomIntent() erst aus $error holen. Das hast Du sogar mit dem timeout so gemacht, wird aber nicht benötigt - das macht die setDialogTimeout(). Ich hatte als timeout Argument deshalb undef übergeben, aber es schadet ja nicht.

Und es muss (?) eine Möglichkeit geben, den bereits (in einer vorherigen Runde) Nein, das muss nicht, so, wie es ist, geht es aber.
In Falle der Blinds() ist es so, dass die so beleiben können. Da ich mittlerweile die BlindStop() fast vollständig in die Blinds() mit eingebaut habe, wird die nur noch alleine, aber oft mehrfach aufgerufen. Beispiel die Sonne blendet -> stell den Rolladen auf halb -> Sonne blendet immer noch -> weiter -> weiter -> stop...
Für alle Kommandos wird die Blinds() aufgerufen und immer mit dem gleichen hash beendet. Also werden die IntentFilter auch immer neu gesetzt. Die Blinds geht ja direkt zur
b]setDialogTimeout()[/b].