SNIPS: Sprachsteuerung (mittlerweile auch per Textcommands) über snips.ai

Begonnen von Thyraz, 21 Juli 2018, 20:28:48

Vorheriges Thema - Nächstes Thema

chicco

also mein account funzt noch.

Aber ich will eigentlich was anderes erzählen.

Ich habe mal folgendes getestet:
- WAN-Kabel am Router raus, d.h. der snips-raspi ist definitiv ohne Internet
- einem bestehenden fhem-device (Funksteckdose) die Attribute snipsMapping, snipsName und snipsRoom vergeben
- einen neuen shortcut im Snips-device eingefügt
- set Snips updateModel
- snips-watch sagt mir dass injected wird
- Ergebnis: ich kann die Funksteckdose per Sprache steuern und der shortcut wird auch erkannt und ausgeführt

Snips wird also definitiv weiter benutzbar sein.
Und weil das so ist, hab ich noch etwas gebastelt, einen Shortcut-Generator.

Das Snips-device bekommt ein neues Attribut:
attr Snips userattr myShortcuts:textField-long

In das Attribut werden shortcuts in variabler Form rein geschrieben.
Dann wird eine Funktion aus der 99_myUtils.pm ausgeführt.
Die Funktion wertet das Attribut aus und generiert daraus Einträge für das Attribut shortcuts im bekannten Format trigger=cmd
EDIT: ACHTUNG: das Attribut shortcuts wird dabei komplett mit dem generierten Inhalt überschrieben!

Man kann in myShortcuts mehrere Trigger kommagetrennt angeben:
Wieviel Uhr ist es,Wieviel Uhr haben wir,Wie ist die Uhrzeit={return "es ist " . qx(date +%R);}

Ergebnis in shortcuts nach Aufruf der Funktion:
Wieviel Uhr ist es={return "es ist " . qx(date +%R);}
Wieviel Uhr haben wir={return "es ist " . qx(date +%R);}
Wie ist die Uhrzeit={return "es ist " . qx(date +%R);}


Man kann Variablen einfügen und diese in den weiteren Triggern und im cmd verwenden.
Variablen stehen in Klammern und die Werte werden per Pipe getrennt. In Trigger und cmd werden sie dann mit $0, $1, etc. verwendet
seit wann ist das Fenster im (Wohnzimmer|Schlafzimmer) geöffnet,wie lange ist das Fenster im $0 auf={getWinOpenDuration("$0")}

Ergebnis:
seit wann ist das Fenster im Wohnzimmer geöffnet={getWinOpenDuration("Wohnzimmer")}
seit wann ist das Fenster im Schlafzimmer geöffnet={getWinOpenDuration("Schlafzimmer")}
wie lange ist das Fenster im Wohnzimmer auf={getWinOpenDuration("Wohnzimmer")}
wie lange ist das Fenster im Schlafzimmer auf={getWinOpenDuration("Schlafzimmer")}


Man kann die Variablen-Werte noch per Doppelpunkt trennen, der Teil vor dem Doppelpunkt wird im Trigger verwendet, der Teil nach dem Doppelpunkt wird im cmd verwendet:
seit wann ist das Fenster im (Wohnzimmer:WZ|Schlafzimmer:SZ) geöffnet,wie lange ist das Fenster im $0 auf={getWinOpenDuration("$0")}

Ergebnis:
seit wann ist das Fenster im Wohnzimmer geöffnet={getWinOpenDuration("WZ")}
seit wann ist das Fenster im Schlafzimmer geöffnet={getWinOpenDuration("SZ")}
wie lange ist das Fenster im Wohnzimmer auf={getWinOpenDuration("WZ")}
wie lange ist das Fenster im Schlafzimmer auf={getWinOpenDuration("SZ")}


Ich habe es aktuell so geschrieben:
(seit wann|wie lange) ist das Fenster im (Wohnzimmer:WZ|Schlafzimmer:SZ) (auf|geöffnet|offen)={getWinOpenDuration("$1")}
sind im Ergebnis 12 Sätze. Die Anzahl der Werte der einzelnen Vars werden miteinander multipliziert: 2*2*3 = 12

Man kann in den Vars auch geschweifte Klammern mit perl-code einsetzen, z.B. um ein Attribut oder Reading auszulesen um so an den pipe-getrennten String zu kommen:
EDIT: siehe Hinweis in #939 zu den geschweiften Klammern und zu numerischen Variablen in eckigen Klammern.
({AttrVal('ekl','articlelist','');}) einkaufen,(setze|schreibe) $0 auf die Einkaufsliste={snipsAddToList("$0")}

Ergebnis (gekürzt):
äpfel einkaufen={snipsAddToList("Äpfel")}
Backpulver einkaufen={snipsAddToList("Backpulver")}
setze äpfel auf die Einkaufsliste={snipsAddToList("Äpfel")}
schreibe äpfel auf die Einkaufsliste={snipsAddToList("Äpfel")}
setze Backpulver auf die Einkaufsliste={snipsAddToList("Backpulver")}
schreibe Backpulver auf die Einkaufsliste={snipsAddToList("Backpulver")}


Dabei ist mir aufgefallen:
Wenn im Attribut shortcuts im Trigger ein großer Umlaut drin ist, klappt es nicht. Der gesprochene Satz wird von Snips erkannt, wird auch als shortcut erkannt, aber der Befehl wird nicht ausgeführt.
snips-watch sagt: The session timed out because its continuation or termination was not handled by any running action code
Keine Ahnung ob der Fehler in Snips oder im fhem-Snips-Modul zu suchen ist.
Ich hab in meiner Funktion jetzt einfach große Umlaute in kleine umgewandelt, dann klappt es, siehe eben die äpfel.

Evtl. muss man ein vorsichtig sein mit der Anzahl der Trigger, Variablen und Werte, denn das potenziert sich alles.
Keine Ahnung ob es Snips-seitig ein Limit gibt. Ich habe aktuell knapp 440 shortcuts (das meiste kommt von der Einkaufsliste) und kann keine Probleme feststellen.

Hier noch die Funktionen, die kommen natürlich in die 99_myUtils.pm:
setShortCuts() müsst ihr aufrufen, hier müsst ihr evtl. ganz oben den Name vom Snips-device ändern.
getShortCuts() wird von setShortCuts() aufgerufen und ruft sich selbst rekursiv auf.


sub setShortCuts() {
Log 1,("setShortCuts()");
use Text::Balanced qw(extract_codeblock);

my $device = "Snips";

my $mySC = AttrVal($device, "myShortcuts", "");
my $sc = "";

if ($mySC ne "")
{
# myShortcuts zeilenweise einlesen
my @lines = split("\n", $mySC);
foreach my $line(@lines)
{
# trigger und cmd splitten
my @parts = split("=", $line);
my $cntparts = scalar(@parts);
if ($cntparts > 1)
{
# erster Teil enthält trigger, der rest den cmd
# restliche teile zum cmd zusammenbauen
my $cmd = "";
for (my $i=1;$i<$cntparts;$i++)
{
$cmd .= ($cmd eq "" ? "" : "=") . $parts[$i];
}
$cmd =~s/;/;;/g; # Semicolons verdoppeln

# in trigger nach vars suchen
my ($start, $end, $pos, $searchvar, $var, $before, $after);
$pos = 0;
$searchvar = 1;
my @vars = ();
while ($searchvar)
{
# klammern suchen
$start = index($parts[0], "(", $pos);
$end   = index($parts[0], ")", $pos);

if ($start != -1 && $end > $start)
{
# trigger enthält klammern
# trigger zerlegen, klammerinhalt in var ablegen
($var, $after, $before) = extract_codeblock($parts[0], "(){}[]<>", '[^(]*');
# trigger wieder zusammenbauen, var als $0, $1, etc. einfügen
$parts[0] = $before . "\$" . scalar(@vars) . $after;

$var = trim(substr($var, 1, -1)); # klammern entfernen

# geschweifte klammern suchen
if (substr($var,0,1) eq "{" && substr($var,-1) eq "}")
{
$var = trim(substr($var, 1, -1)); # klammern entfernen
$var = eval($var); # perl-code auswerten
}

# var an der pipe splitten und werte merken
my @values = split(/\|/, $var);
push(@vars, \@values);

# startposition für die klammersuche im nächsten durchlauf
$pos = $start;
}
else
{
# trigger enthält keine weiteren vars
$searchvar = 0;
}
}

# alle trigger inkl. vars zusammenbauen
my @triggers = split(",", $parts[0]);
foreach my $trigger(@triggers)
{
# beim trigger $ durch § ersetzen, damit die § später als lowercase eingesetzt werden
my $trg = $trigger;
$trg =~s/\$/§/g;
# umlaute als lowercase
$trg =~s/Ä/ä/g;
$trg =~s/Ö/ö/g;
$trg =~s/Ü/ü/g;

$sc = getShortCuts(0, scalar(@vars), \@vars, $trg, $cmd, $sc);
}
}

} # foreach my $line(@lines)
}

if ($sc ne "")
{
# doppelte Einträge filtern
my @lines_result = split("\n", $sc);
my %count;
my @lines_unique = grep { ++$count{$_} < 2 } @lines_result;
$sc = join("\n", @lines_unique);

# shortcuts im Attribut speichern
fhem("attr $device shortcuts $sc");

#fhem("set $device updateModel");
}

}


sub getShortCuts($$$$$$) {
my ($curVar, $varCnt, $vars, $trigger, $cmd, $sc) = @_;

if ($curVar < $varCnt)
{
# werte der aktuellen var duchlaufen
my $ref_curVar = @$vars[$curVar];
foreach my $value(@$ref_curVar)
{
# nächste var
$sc = getShortCuts($curVar+1, $varCnt, \@$vars, $trigger, $cmd, $sc);

# wert in variablen einsetzen
my $trgval = $value;
my $cmdval = $value;
if (index($value, ":") != -1)
{
# unterschiedliche Werte für trigger und cmd
my @parts = split(":", $trgval);
$trgval = $parts[0];
$cmdval = $parts[1];
}

# umlaute in trigger-vars als lowercase einsetzen
$trgval =~s/Ä/ä/g;
$trgval =~s/Ö/ö/g;
$trgval =~s/Ü/ü/g;

# in trigger-vars (§0, §1, etc) einsetzen
$sc =~s/§$curVar/$trgval/g;
# in cmd-vars ($0, $1, etc) einsetzen
$sc =~s/\$$curVar/$cmdval/g;
}
}
else
{
# keine weitere var
# endpunkt der rekursiven schleife
# neue zeile in sc einfügen
$sc .= ($sc eq "" ? "" : "\n") . $trigger . "=" . $cmd;
}

return $sc;
}



Viel Spass damit.

Gruß
chicco

chicco

Ich habs nochmal erweitert und habe das Snips-Modul aufgebohrt und habe die Funktion ins Modul integriert, so dass man die Funktion ganz einfach mit "set Snips generateShortcuts" aufrufen kann.
Meine modifizierte Version vom Modul ist im Anhang.

@Thyraz: willst du da mal reingucken und deinen Segen dazu geben?
Die Änderungen sind überschaubar: den neuen set-Befehl bekannt gemacht, das Attribut myShortcuts bekannt gemacht, ein neuer use-Befehl und ganz am Ende 2 Funktionen eingefügt.

Es funktioniert, aber im Logfile finde ich nach einem fhem-Neustart diese Meldung:
2020.01.23 21:52:24 1: PERL WARNING: SNIPS::getShortCuts() called too early to check prototype at ./FHEM/10_SNIPS.pm line 1695, <$fh> line 610.
Die angemeckerte Zeile ist die, wo sich die Funktion getShortCuts() rekursiv selbst aufruft.
Habe die Meldung schon gegoogelt und finde Hinweise, dass die Funktions-Deklaration ohne Klammern gemacht werden soll, aber wie soll ich denn dann meine Parameter auffangen?

Im Logfile sind noch mehr warnings im Zusammenhang mit dem Snips-Modul. Die kommen alle nur einmalig nach einem fhem-Neustart wenn man bestimmte Aktionen auslöst.
Die haben nix mit meinen Änderungen zu tun und kommen auch mit der Original-Version vom Modul.

z.B. kommen diese warnings nachdem ich "set Snips updateModel" aufgerufen habe:
2020.01.23 21:53:59 1: PERL WARNING: Use of uninitialized value in split at ./FHEM/10_SNIPS.pm line 278.
2020.01.23 21:53:59 1: PERL WARNING: Use of uninitialized value in split at ./FHEM/10_SNIPS.pm line 337.

oder die hier nachdem ich das erste mal das Hotword gesagt habe:
2020.01.23 21:55:25 1: PERL WARNING: Use of uninitialized value in substitution (s///) at ./FHEM/10_SNIPS.pm line 743.
2020.01.23 21:55:25 1: PERL WARNING: Use of uninitialized value $value in concatenation (.) or string at ./FHEM/10_SNIPS.pm line 784.

Sollte man das was machen oder kann man das ignorieren weil es nur warnings sind?

Gruß
chicco

Thyraz

Ich muss zugeben, dass ich mich im Modul nicht mehr so schnell zurecht finde.
Im Diff-Tool sieht das soweit ok aus.

Wenn du vorhast Snips weiter zu nutzen und anzupassen,
kann ich dir das Github Repo auch gerne übertragen.

Ich werde da selbst nichts mehr erweitern, da ich Snips ja selbst nicht mehr nutze und neue Installationen wahrscheinlich auch gar nicht mehr möglich sein werden.
Hast dich in den Code ja scheinbar immerhin soweit reingekämpft. ;)
Fhem und MariaDB auf NUC6i5SYH in Proxmox Container (Ubuntu)
Zwave, Conbee II, Hue, Harmony, Solo4k, LaMetric, Echo, Sonos, Roborock S5, Nuki, Prusa Mini, Doorbird, ...

chicco

Ok, danke fürs reingucken.
Das repo musst du mir nicht übertragen, ich denke nicht, dass ich da noch weiter anpassen werden. Falls doch, kann ich mich ja melden.
Benutzen werde ich Snips auf jeden Fall noch ne Weile, dafür funzt es einfach zu gut. Mit dem Shortcut-Generator kann man es auch weiterhin mit Begriffen füttern und man hat sowas wie eine light-version von intents.

Gruß
chicco

Devender

Hi chicco,

Ich habe es jetzt mal ausprobiert nach deiner  Anleitung und mit veraendertem Modul.

Ergebnis: meine  vorhandenen Shortcuts wurden gelöscht  :(
Ich hatte zum Test mal den Uhrzeitstring in das neue Attribute geschrieben und dann set snips generateShortcut ausgeführt.
Dise Strings waren dann auch im Attribute Shortcut bei Snips. Leere ich dann das myShortcut und fuege ein "aepfel einkaufen" ein , dann generare sind die Uhrzeiten  gelöscht und nur das  neue drin.

Frage: muss im myShortcut immer alles drin stehen oder nur das, was ich hinzufuegen moechte?

Viele Gruesse,
Dirk
FHEM 5.8 auf RasPi mit Jessy - CUL868, JeeLink Lacrosse
Komponenten: HM, IT, ELV, FB7390, FritzPL543,Sonos Play3
Mehrere Wandtablets sowie einen Smart Mirror
https://wiki.fhem.de/wiki/Anwesenheitserkennung#PRESENCE-Modul

chicco

Hi Dirk,

in myShortcuts steht immer alles drin.
shortcuts werden immer überschrieben.
Ich hoffe du hast deine shortcuts vorher gesichert?

Gruß
chicco

Devender

Zitat von: chicco am 25 Januar 2020, 19:34:20
Hi Dirk,

in myShortcuts steht immer alles drin.
shortcuts werden immer überschrieben.
Ich hoffe du hast deine shortcuts vorher gesichert?

Gruß
chicco

Vorher gesichert :-)
FHEM 5.8 auf RasPi mit Jessy - CUL868, JeeLink Lacrosse
Komponenten: HM, IT, ELV, FB7390, FritzPL543,Sonos Play3
Mehrere Wandtablets sowie einen Smart Mirror
https://wiki.fhem.de/wiki/Anwesenheitserkennung#PRESENCE-Modul

Prof. Dr. Peter Henning


Thyraz

Ja, die Trauerphase haben wir auf den letzten 2 Seiten schon hinter uns...


Schade drum, mal sehen was Sonos auf ihren Geräten draus macht.
Eine möglichst freie SmartHome Lösung ist sicher nicht auf deren Agenda. 😉
Fhem und MariaDB auf NUC6i5SYH in Proxmox Container (Ubuntu)
Zwave, Conbee II, Hue, Harmony, Solo4k, LaMetric, Echo, Sonos, Roborock S5, Nuki, Prusa Mini, Doorbird, ...

chicco

nee, nicht trauern. Einfach weiter benutzen, z.B. mit der neuen Version vom Shortcut-Generator.

Erstmal ist mir aufgefallen, dass innerhalb der geschweiften Klammern nicht beliebiger perl-code stehen kann.
Die Klammern und Gleichheitszeichen im Code machen Probleme wenn meine Funktion die Vars ausliest.
Mann sollte sich beschränken auf AttrVal(), ReadingsVal() und eigene Funktionen aus der 99_myUtils. Eigene Funktionen müssen ein main:: vorangestellt haben, z.B. ({ main::nameDerFunktion() })

Man kann jetzt noch numerische Variablen einbauen. Im Trigger werden die Zahlen dann als Wörter ausgegeben, im cmd werden sie als Zahl ausgegeben.
Dazu in den Vars eckige Klammern einfügen und pipe-getrennte Werte/Bereiche eingeben.
Jeder Wert kann semicolon-getrennt bis zu drei Zahlen enthalten, die einen Schleifenkopf abbilden: [start;ende;step] -> for ($i=start; $i<=ende; $i+=step)
Wenn step nicht angegeben wird, ist step=1
Wenn ende nicht angegeben wird, ist ende=start

Beispiel in myShortcuts:
Teimer ([2;20|25;60;5]) Minuten={setTimer($0)}

Ergebnis in shortcuts:
Teimer zwei Minuten={setTimer(2)}
Teimer drei Minuten={setTimer(3)}
...
Teimer neunzehn Minuten={setTimer(19)}
Teimer zwanzig Minuten={setTimer(20)}
Teimer fünfundzwanzig Minuten={setTimer(25)}
Teimer dreißig Minuten={setTimer(30)}
...
Teimer fünfundfünfzig Minuten={setTimer(55)}
Teimer sechzig Minuten={setTimer(60)}


Auf die Weise hatte ich schon länger Timer mit shortcuts erstellt.
Jetzt wollte ich es variabel gestalten und wie ich es am testen bin, stelle ich fest, dass es in der fhem-App bereits einen Intent setTimer gibt. Grrr, dat steht aber nich inner Doku ;)
Also hab ich das alles wieder aus den shortcuts raus genommen und benutze den Intent.
Das Beispiel mit den Timern ist also kein sinnvolles Bsp.
Einen anderen Anwendungsfall habe ich für die numVars aktuell nicht  :(

Und man kann jetzt noch in myShortcuts die einzelnen Zeilen mit # kommentieren, die Zeilen werden dann nicht in shortcuts ausgegeben.


Gruß
chicco

bart0190

Hallo,
Da ich viele Fehlermeldungen auf meiner Snips Installation hatte und auch die Sprachausgabe nicht klappte, habe ich heute eine neu-Installation gemacht. Alles soweit geklappt, nur kann ich nun das Assistant Model nicht mehr herunterladen, weil mein Zugang gesperrt wurde (Sonos hat ja Snips gekauft). Gibt es eine Möglichkeit das Assistant Model von meiner alten Snips-Installation am Raspi auf meine neue Installation zu bringen?

lg
bart
Raspberry Pi 4 - FHEM, Homematic HM-CFG-LAN, ESP32 mit ePaper Display, Raspberry Pi 3 - BT Lokalisierung, PC - Squeezebox Server, Raspberry Pi 2 +HifiBerry - SB Player1, Raspberry Pi 2 +AVReceiver - SB Player2, nVidia Shield TV +AVReceiver - SB Player3, 2x Logitech Harmony Hub, echo dot Gen3

jowe

Zitat von: bart0190 am 30 Januar 2020, 23:28:25
Hallo,
Da ich viele Fehlermeldungen auf meiner Snips Installation hatte und auch die Sprachausgabe nicht klappte, habe ich heute eine neu-Installation gemacht. Alles soweit geklappt, nur kann ich nun das Assistant Model nicht mehr herunterladen, weil mein Zugang gesperrt wurde (Sonos hat ja Snips gekauft). Gibt es eine Möglichkeit das Assistant Model von meiner alten Snips-Installation am Raspi auf meine neue Installation zu bringen?

lg
bart

Das sollte funktionieren. Der Assistant ist in usr/share/snips/assistant gespeichert. Vermutlich kopierst du einfach am besten den kompletten usr/share/snips/ folder.
Gruß Jonas

BastianH

Hi,
ich benötige mal eure Unterstützung, ich habe snips und mehreren Satelliten erfolgreich mit  fhem verbunden. Jetzt würde ich gerne dies auch mit iobroaker testen. Da aber seit Februar die snips Console abgeschaltet wurde kann ich mir darüber nicht mehr einen eigenen Assistenten mit der iobroaker app erstellen.

Daher die Frage an euch, hat jemand noch eine assistant_proj_...zip Datei (Damals unter deploy assistant als Download) oder kann jemand von seinem funktionierenden System den Order /usr/share/snips/assistant als zip bereitstellen?

MFG BastianH
FHEM in VM Ubuntu 64Bit
Komponenten: CUL868 (HM), CUL433 (IT), JeeLink868 (LaCrosse), Tasmota, Fritzbox, BT, Zigbee, snips mit 2 Satellieten und dieverse Sensoren Temperatur / Feuchtigkeit / Strommessung
Diverse Testsysteme mit fhem, iobroker

JonasE

Hi, ob das noch Sinn macht sich in das Thema mit Snips so reinzuarbeiten weiß ich nicht. Ich habe die Dateien noch und könnte dir die schicken. Zum Hochladen im Forum sind die leider zu groß. In den nächsten Wochen soll eine komplett überarbeitet Version von rhasspy raus kommen (2.5). Diese soll dann auch das Hermes Protokoll unterstützen. Vielleicht macht es mehr Sinn direkt auf rhasspy zu setzten. Die neue Dokumentation dazu ist sogar hier schon einsehbar: https://rhasspy.github.io/rhasspy-voltron/

Beste Grüße

BastianH

Danke JonasE,
ich habe dir eine PN gesendet.
In der Vergangenheit habe ich schon mehrere Sprach Assistenten getestet. Bisher hatte ich immer erhebliche Probleme mit installieren von Abhängigkeiten bzw. die richtige Linux Version zu nehmen.
Leider unterstützen auch viele kein Deutsch. Daher bin ich mit meiner momentanen Konstellation mit snips und mehreren Satelliten (mehreren eigenen Hotwörtern, LED anzeigen auf Rearspeker2 mit piz-Wlan) und Integration in fhem sehr zu frieden. Wenn der ,,Rhasspy Voice Assistant" relativ ähnlich ist und zukünftig weiterentwickelt würde ist dies auf jeden Fall ein Test bzw. gegebenenfalls späterer Wechsel in Betracht zu ziehen.
FHEM in VM Ubuntu 64Bit
Komponenten: CUL868 (HM), CUL433 (IT), JeeLink868 (LaCrosse), Tasmota, Fritzbox, BT, Zigbee, snips mit 2 Satellieten und dieverse Sensoren Temperatur / Feuchtigkeit / Strommessung
Diverse Testsysteme mit fhem, iobroker