Talk2Fhem: Unterschied zwischen den Versionen
Phill (Diskussion | Beiträge) |
Phill (Diskussion | Beiträge) |
||
Zeile 824: | Zeile 824: | ||
Bei Dingen die kein offensichtliches Feedback erzeugen oder wo getoggelt wird, macht es eventuell Sinn eine Zustandsabhängige Antwort zu geben. | Bei Dingen die kein offensichtliches Feedback erzeugen oder wo getoggelt wird, macht es eventuell Sinn eine Zustandsabhängige Antwort zu geben. | ||
schalte etwas (\S+) = | schalte etwas (\S+) = | ||
set etwas $1{ true => on, false => off}; | set <span style='color:red'>etwas</span> $1{ true => on, false => off}; | ||
defmod atdekofb at +00:00:01 IF ([etwas] eq "on") | defmod atdekofb at +00:00:01 IF ([<span style='color:red'>etwas</span>] eq "on") | ||
(setreading $NAME answers Angeschaltet.) | (setreading $NAME answers Angeschaltet.) | ||
ELSE | ELSE |
Version vom 8. März 2018, 11:09 Uhr
Talk2Fhem | |
---|---|
Zweck / Funktion | |
Das Modul stellt eine Verbindung zwischen natürlicher Sprache und FHEM Befehlen her | |
Allgemein | |
Typ | undefiniert |
Details | |
Dokumentation | ModUndef |
Support (Forum) | Unterstützende Dienste |
Modulname | 39_Talk2Fhem.pm |
Ersteller | Oliver Georgi |
Wichtig: sofern vorhanden, gilt im Zweifel immer die (englische) Beschreibung in der commandref! |
Diese Seite beschreibt die Funktionsweise und Konfiguration des Moduls 39_Talk2Fhem.pm
Voraussetzungen
Es ist sehr zu empfehlen, für die Konfiguration des Moduls, im Webfrontend von FHEM die Syntaxhervorhebung zu aktivieren. Die Aktivierung des erweiterten Editors ist hier beschrieben.
Kenntnisse im Bereich Regulärer Ausdrücke (RegExp) in Perl sind hilfreich, aber nicht zwingend erforderlich. Ein kurzer Einstieg kann hier eingesehen werden. Häufig_verwendete_RegExp
Allgemeines
Das Modul Talk2Fhem stellt eine Verbindung zwischen natürlicher Sprache und FHEM Befehlen her. Es ist ein überaus flexibles und relativ einfach zu konfigurierendes Script mit dem man sehr natürlich kommunizieren kann. Die Konfiguration erfolgt dabei über das FHEM Webfrontend.
Es werden diverse Zeit- und Datumsangaben erkannt. Außerdem ist es möglich Ereignisse zu formulieren, welche die Befehle mit einer Bedingung verknüpfen. Bei der weiteren Verarbeitung der Sprachbefehle wird die Sprache mit definierten regulären Ausdrücken verglichen.
Es lassen sich Antworten generieren, die Abhängig von den gesendeten Sprachbefehlen sind. Das vom Author verfolgte Konzept ist aber: Erzeuge eine Meldung wenn etwas nicht verstanden oder fehlgeschlagen ist, ansonsten führe den Befehl mit einer kurzen Erfolgsmeldung wie "OK" oder "Gerne" aus.
Die Text Ein- sowie Ausgabe der natürlichen Sprache übernehmen andere Anwendungen. Hierzu gibt es eine Reihe von Möglichkeiten die unter Anwendung dargestellt sind. Funktionierende Lösungsansätze werden hier gerne mit aufgenommen.
Funktionsweise
Die Zerlegung des Sprachbefehls erfolgt in mehreren Schritten.
- Aufteilen des Sprachbefehls in einzelne Kommandos anhand des Wortes UND
- Suchen nach Ereignisansagen und entfernen für die weitere Verarbeitung
- Erkennen von Zeit- und Datumsangaben und entfernen für die weitere Verarbeitung
- Entfernung überflüssiger Wörter
- Vergleich mit den definierten regulären Ausdrücken
- Konvertieren in ein FHEM Kommando
- Antwortsatz bilden
- Zeit- und Eventgebundenes auslösen des FHEM Kommandos
Installation
update 39_Talk2Fhem shutdown restart
Diskussion hier:
Forumsbeitrag.
Definition
define talk Talk2Fhem
Zum testen der Konfiguration ist es Ratsam vorerst das Attribut disable auf 1 zu setzen. Hierbei wird die Auslösung der FHEM Kommandos unterdrückt.
attr talk disable 1
attr global language DE oder attr talk T2F_language DE
Anwendung
Der Sprachbefehl wird über das Kommando set oder set ! an das Modul übermittelt.
set talk Guten Morgen liebes Zuhause
Wird kein Text angehängt, wird der letzte Sprachbefehl erneut ausgeführt.
Eingabemethoden
Die Herkunft der Sprachbefehle für das Modul sind vielfältig. Hier werden einige Methoden beschrieben wie der Sprachbefehl in das Modul gelangen kann.
Messenger Telegram
Ist ein TelegramBot telbot definiert, reicht ein einfaches "notify" um die Nachrichten an FHEM an Talk2Fhem weiterzuleiten.
define n_telbot notify telbot:msgText.* set talk $EVENT
Schon kann mit FHEM gechattet werden... ;)
Mit Telegram lässt sich auch leicht der Absender der Nachricht verarbeiten. Siehe hierzu Modul_Talk2Fhem#Personalifizierung
Google Home Geräte
Momentan ist es leider noch notwendig über einen Umweg die Sprache von einem GoogleHome in FHEM zu bringen. Hierzu ist es notwendig einen funktionierenden DNS Service am laufen zu haben. Damit FHEM per Webadresse im Internet erreichbar ist.
- Ein FHEMWEB Device anlegen
define api FHEMWEB 8087 global attr api HTTPS 1 attr api allowfrom 1 attr api csrfToken None
- Ein allowed Device anlegen
define allowed_api allowed api attr allowed_api allowedCommands set attr allowed_api allowedDevices talk attr allowed_api basicAuth {"$user:$password" eq 'user:passwort'} attr allowed_api validFor api
- Mit Googlekono bei IFTTT.com anmelden
- New Applet
- +this GoogleAssistant -> Say a phrase with a text ingredient
- Die drei Triggertexte wählen z.b.
- das Haus $
- sag dem Haus $
- frag das Haus $
- Problem bei zu kurzen Texten hat GoogleHome keine anderen Anfragen mehr angenommen.
- Einen Antworttext überlegen z.B. OK und in "What do you want the Assistant to say in response?" eintragen
- Language Deutsch
- +that Webhooks
- URL wählen
https://user:password@dnsservice:54387/fhem?cmd.talk=set talk <<<{{TextField}}>>>&XHR=1
- Fertig
Jetzt wird der gesamte Text bei dem genannten Triggerworten an FHEM weitergeleitet.
Readings / Ausgabemethoden
Über die bereit gestellten Readings lassen sich weitere Module die z.B. für Sprachausgabe zuständig sind mit einbinden.
- Im Reading set steht der letzte gesendete Sprachbefehl.
- Im Reading cmds steht das letzte ausgeführte FHEM-Kommando dessen Rückgabe, in den häufigsten Fällen ein Fehler, in das Reading response geschrieben wird.
- Die Antworten werden im Reading answers sofort ausgegeben.
- Die Modulinterne Fehler werden in dem Reading err ausgegben.
- In ifs stehen die Bedingungen mit der das letzte Kommando ausgeführt werden wird.
- notifies enthält eine Auflistung der Devices die für die aktuell wartenden bedingten Kommandos relevant sind. Auf diesen Devices liegt ein internes notify.
- Das Reading origin enthält den erkannten Text der im Attribut T2F_origin definierten RegExp.
Das Reading status wird jedes mal gesetzt und erhält Werte nach folgender Priorität.
1. | response | wenn das FHEM Kommando eine Meldung ausgegeben hat |
2. | disabled | wenn das Attribute disable auf 1 steht |
3. | err | wenn der Sprachbefehl einen Fehler zurückgegeben hat |
4. | answer | wenn der Sprachbefehl eine Antwort ausgegeben hat |
5. | done | wenn keines der oberen Fälle eingetreten ist, also alles gut verlaufen ist |
Beispiel 1
Die FHEM Befehle werden eigenständig ausgeführt, sie können aber zur Überprüfung auch weitergeleitet werden. Das Beispiel zeigt auch wie auf Fehler und Antworten von Talk2Fhem reagiert werden kann. Erstellen eines Notify
define n_talk notify talk:.* {}
Folgendes in der Definition von n_talk einfügen
talk:.* {
# Sende die Antwort per Telegram und gebe es über das GoogleHome aus
if ($EVENT =~ s/^answers: //) {
fhem("set telegram _msg \@USER $EVENT");
fhem("set d_googlespeak $EVENT");
}
# Schicke den Fehler per Telegram und sag am GoogleHome das es nicht geklappt hat.
if ($EVENT =~ s/^err: //) {
fhem("set telbot _msg \@Oliver $EVENT");
my @a = ("Das hat leider nicht geklappt", "Es gab leider einen Fehler", "Es tut mir leid. Das hat nicht funktioniert.", "Es ist leider zu einem Fehler gekommen","Könntest du das vielleicht nochmal anders sagen", "Mhhh, das kann ich so nicht verstehen");
fhem("set d_googlespeak $a[int(rand($#a))]");
}
# Schick mir alle ausgeführten Befehle als Telegram
if ($EVENT =~ s/^cmds: //) {
fhem("set telbot _msg \@USER $EVENT");
}
}
Beispiel 2 Reading: Status
Eine weitere Möglichkeit Talk2Fhem mit anderen Modulen zu verbinden, ist über das Reading status möglich.
Die folgenden Lösung sieht auch eine Standardantwort vor. !Achtung noch nicht getestet vom Author!
talk:status {
my $text = ReadingsVal("talk", $EVENT, "");
if ("$EVENT" eq "response") {
#FHEM gibt bei Erfolg eig. keine Rückmeldung, also ist vermutlich was schief gelaufen
$text = "Die Haussteuerung hat folgende Meldung gebracht. $text";
} elsif ("$EVENT" eq "err") {
# Hier kann auch die Meldung noch detailierter an einen Messenger gesendet werden.
$text = ["Das habe ich nicht verstanden!","Phuu das kann ich noch nicht!","Nein! Heute nicht.", "Wie bitte?"]->[rand(4)];
} elsif ("$EVENT" =~ /^answer/) {
$text = ReadingsVal("talk", "answers", "Ich habe nichts zu sagen.");
} elsif ("$EVENT" eq "done") {
$text = ["Ok.","Ja.","Gerne.", "Mach ich!"]->[rand(4)];
} else {
$text = "Oh, es gibt etwas neues, bitte schau mal im Forum bei Talk2Fhem vorbei.";
}
fhem('set speaker speak "$text"') if $text;
}
Modulinterne Spracherkennung
Zeitenerkennung
Die Zeit- und Datum Eingabe kann auf viele verschiedene Arten erfolgen.
Die Datumerkennung umfasst folgende Phrasen:
- morgen, übermorgen
- in ... Wochen, Monat, Jahr
- nächste Woche, Monat, Jahr
- Wochentage
- in ... Tagen
- am DATUM
Die Zeiterkennung umfasst folgende Phrasen:
- in,nach ... stunden,minuten,sekunden
- um ... (Uhr) (...)
- heute - entspricht 12:00
- früh - entspricht 9 Uhr
- abend - entspricht 18 Uhr
- nachmittag - entspricht 16 Uhr
- vormittag - entspricht 10:30 Uhr
- mittag - entspricht 12 Uhr
- gleich - entspricht 5 Minuten
- nachher - entspricht 30 Minuten
- später - entspricht 1 Stunde
- jetzt
- sofort
Datum und Zeitangaben können natürlich kombiniert werden.
Wird eine Zeit erfolgreich erkannt erfolgt die Ausführung des FHEM-Kommandos über das Modul at. Es wird ein at Ereignis angelegt welches den Namen at_<modulname>_<zeitindex> erhält.
Bei mehreren Kommandos über das Schlüsselwort und wirkt sich der Zeitpunkt des ersten Kommandos auch auf das zweite und dritte ... aus.
Schalte die Heizung um 20 Uhr aus und mache die Rollläden runter
Beide Kommandos werden um 20 Uhr ausgelöst.
Hat das zweite Kommando ebenfalls eine Zeitphrase wird diese Zeit genommen.
schalte die Heizung heute Abend ab und mache die Rollläden jetzt runter
Wie oben gesehen wird der Zeitpunkt des ersten Kommandos vor dem "und" auch für das zweite nach dem "und" gesetzt. Vorausgesetzt er wird beim 2. Kommando nicht durch eine eigenständige Zeit (hier "jetzt") ersetzt. Wenn man die Zeit des zweiten Kommandos, relativ zum ersten setzen möchte, kann dies mit den Schlüsselwörtern dann, danach oder wieder formuliert werden.
Fahre um 14 Uhr die Rollläden an der Terrasse runter und schalte dann in 2 Minuten die Bewässerung an
oder
Mach am Freitag um 5 Uhr die Heizung aus und in einer Woche wieder an
Objektverundung
Es ist möglich Objekte in einer Auflistung in den Sätzen zu übergeben. Siehe hierzu T2F_keywordlist.
Rollos im Wohnzimmer, Esszimmer und in der Küche auf 70 Schalte den Fernseher und die Soundanlage an
Ereigniswiederholung
Werden zwei Sätze über ein und miteinander verknüpft, kann im 2. Satz mit dem Wort wieder das Ereignis wiederholt werden.
Beispiel
schalte bitte die Klingel ab und in einer Stunde wieder ein mach bitte die Garage auf und gleich wieder zu
Man benötigt hier nicht erneut den kompletten ersten Satz zu wiederholen.
Um diese Funktion zu gewährleisten, sollte der anschließende Hinweis in Klammerüberführung beachtet werden.
Eventabhängige Kommandos
Es lassen sich alle definierten Befehle mit Bedingungen verknüpfen, bei der Erfüllung diese dann ausgeführt werden.
Beispiel
Sag mir bescheid wenn es klingelt Mach die garage zu wenn es draußen dunkel wird Wenn ich nach hause komme mach die Musik an
siehe hierzu Modul_Talk2Fhem#T2F_if
Konfiguration
Die Konfiguration der Sprachbefehle wird Zeile für Zeile in der Definition (DEF) des angelegten Gerätes vorgenommen. Die einzelnen Definitionen müssen folgenden Schema entsprechen.
Vor und nach dem Gleichheitszeichen muss mindestens ein Trennzeichen vorhanden sein
- Vor dem "=" mindestens ein Leer- oder Tabulatorzeichen
- Nach dem "=" können zusätzlich auch Zeilenumbrüche eingefügt werden
<regexp> = <command>
RegExp-Teil
<RegExpPart> [&& [?!]<RegExpPart_n>] =
Eine Konfiguration beginnt immer mit der Definition der gesuchten Schlüsselwörter, gefolgt von einem Gleichheitszeichen (siehe Randnotiz). Diese werden Anhand von regulären Ausdrücken (RegExp) beschrieben. Also z.B.:
garage auf =
Das bededutet, sobald die Wörter in der Reihenfolge "garage" und "auf" erkannt werden, wird der Kommandoteil der Konfiguration ausgeführt. Groß- und Kleinschreibung wird grundsätzlich ignoriert.
Kommando-Teil
Der Kommandoteil folgt dem Gleichheitszeichen (siehe Randnotiz). Und kann auf folgende Arten vorliegen.
- FHEM Kommando
- { Perl Befehl }
- ( Modul_Talk2Fhem#erweiterte_Befehlskonfiguration )
Im ganzen könnten die Konfigurationen folgendermaßen aussehen:
garage\S* auf = set dev_garage open garage\S* zu = { if (Value("obj_garage") != "present") { fhem("set dev_garage open") } else { fhem("set tts speak Es befindet sich etwas im weg!") } }
\S* Siehe hierzu Häufig verwendete RegExp.
Bei der ersten Konfiguration würde der FHEM Befehl "set garage open" bei den folgenden Sprachbefehlen ausgeführt werden.
Mach bitte die Garage auf Garagentür in 5 Minuten auf Die Garagen soll in einer Stunde aufgemacht werden Das haus soll das Garagentor wenn der Bewegungsmelder anspricht aufmachen
Durch die modulinterne Entfernung von Zeit- und Ereignisdefinitionen ist diese RegExp bei den letzten drei Beispielen ebenfalls erfolgreich.
Klammerüberführung
Es ist nicht notwendig für jeden Zustand oder jedes Gerät eine eigene Konfigurationzeile zu erzeugen. Hierfür gibt es die Möglichkeit, wie bei regulären Ausdrücken üblich, Klammern "( )" im <regex>-Teil zu erfassen. Dies erfolgt über die Standartvariablen $1, $2, ..., $n. "n" steht hier für die nte Klammer. Zusätzlich gibt es in Talk2Fhem die Möglichkeit die Klammern zu modifizieren.
Soll die Garage auf und zugemacht werden, lässt sich folgendermaßen beschreiben.
Beispiel:
garage\S* (\S+) = set dev_garage $1
Ausgabe:
Spracheingabe | FHEM Kommando |
---|---|
Mach die Garage auf | set dev_garage auf |
Mach mit der Garage irgendetwas | set dev_garage irgendetwas |
Damit die Verknüpfung zweier Kommandos mit dem Wort wieder zuverlässig funktioniert, sollte die Klammer die den gewünschten Zustand beschreibt die letzte verwendete sein.
Klammermodifikation
Da es in den meißten Fällen nicht gewünscht ist, nur das gefunde Wort in das FHEM Kommando zu überführen, lässt sich zusätzlich das gefundene Wort modifizieren.
Variante 1: nach Typ
Hier kann die Klammer auf ihren Typ hin modifiziert werden.
Definition
$n{ typ => modification[, typ2 => mod2, ..., typn => modn] }
- typ kann eines der folgenden Wörtern enthalten:
typ | Beschreibung |
---|---|
true | sind alle Wörter die eine positive Richtung enthalten. Wie z.B. auf, ein, hoch, an, usw. |
false | sind alle Wörter die eine negative Richtung enthalten. Wie z.B. ab, aus, runter, zu, usw. |
integer | Wort enthält eine Zahl |
numeral | Wort enthält eine Zahl oder ein Zahlenwort |
float | Wort enthält eine Gleitkommazahl |
/<regexp>/ | Wort entspricht der <regexp> |
word | Wort enthält gleich oder mehr als 4 Buchstaben |
empty | Wort enthält eine Leere Zeichenkette |
else | Falls keines der Fälle zutrifft |
- modification enthält das einzufügende Wort.
Wird hier ein Komma benötigt oder besteht die Möglichkeit, dass der Wert eine leere Zeichenkette beinhalten könnte, sollte das Wort in Anführungszeichen gesetzt werden. Kann ebenfalls $n oder andere Variablen beinhalten.
Beispiel
garage\S* (\S*) = set dev_garage $1{ true => open , false => close }
Ausgabe:
Spracheingabe | FHEM Kommando |
---|---|
mach die Garage auf | set dev_garage open |
bitte Garagentor schließen | set dev_garage close |
Variante 2: nach Liste
Hier kann die Klammer anhand einer oder zweier Listen selektiert werden.
Definition
$n[ wert1, wert2,,,,, wertn ]
oder
$n[ @liste ]
Innerhalb der Klammern [ ] wird eine Komma separierte Liste mit Namen erwartet die als Modifikatorliste dient. Die sogenannte Modwordlist. Die Werte sind immer optional und können leer gelassen werden. Über das Attribut T2F_modwordlist können diese Listen zur Übersicht und Wiederverwendbarkeit angelegt werden. Siehe Attribute. Auf diese Listen, lässt sich über den Namen der Liste, mit einem vorangestelltes '@' zugreifen.
Beispiel nach Position
Beim ersten Beispiel wird eine Zahl im regex-Teil erwartet (\d+). Diese Zahl entscheidet welche Position aus der Modwordlist ausgewählt werden soll.
ventilator auf (stufe )?(\d+) = set aircon $2[ off, level1, level2, level3 ] |------------------> 0 1 2 3
(Stufe )? bedeutet: Das Wort Stufe kann, muss aber nicht.
Ausgabe:
Spracheingabe | FHEM Kommando |
---|---|
Ventilator in 10 Minuten auf Stufe 0 | set aircon off |
Ventilator auf 3 | set aircon level3 |
Beispiel nach Vergleichsliste
Hier kommt eine weitere Liste ins Spiel. Die sogenannte Keywordlist ist im Eigentlichen eine RegExp "Ver-oder-ung".
( key1 | key2 | ... | keyn )
oder
( @keywordlist )
Diese Liste mit Schlüsselwörtern wird im <regex>-Teil angegeben. Hier entscheidet nicht eine Zahl über die Position, sondern die Position die in der Keywordlist einen Treffer hat wird an selber Position in der Modwordlist ausgewählt. Im Attribut T2F_keywordlist können vordefinierte Listen angelegt werden und mit @keylist ausgewählt werden
blendet && (Wohnzimmer|Esszimmer|Küche) = set $1[act_lvgroom, act_dinroom, act_kitchen] 70 |__________|_______|___________________|_____________|____________|
Ausgabe:
Spracheingabe | FHEM Kommando |
---|---|
im Esszimmer blendet es | set act_dinroom 70 |
ich sitze geblendet im Wohnzimmer | set act_lvgroom 70 |
die sonne blendet in der Küche | set act_kitchen 70 |
Auswahl des Listenelements
Soll bei der Nutzung der Variable nicht der Wert aus dem Sprachbefehl, sondern der Wert der Liste genutzt werden, so wird an die Variable ein @ angehängt. So wird bei
blendet.* (Wohnzimmer|Esszimmer|Küche) = set act_$1@ 70
Die Sätze
es blendet im Esszimmer es blendet im esszimmer
immer zu
set act_Esszimmer 70
führen, da die Liste das Esszimmer großgeschrieben enthält.
Ergänzung
Ähnlich wie in Variante 1, könne auch hier auf die Schlüsselwörte
empty für leere Zeichenkette
und
else für alle anderen Fälle
zugegriffen werden. Hierbei wird der Modwordlist einfach empty oder else gefolgt von dem gewünschten Wert als nächstes Element angehängt.
$n[ wert1, wert2,,,, empty, leererwert, else, andernfallswert ]
Generell lassen sich @ Listen mit Werten erweitern.
$n[@rooms, empty, dev_alle]
Import von Konfigurationen
Umfangreiche Konfigurationen können in Dateien ausgelagert und über $inlcude importiert werden:
$include = FHEM/t2f.cfg
Mehrere RegExps
Wenn die Reihenfolge der Wörter Unbekannt ist, kann der <regexp>-Teil in mehrere Einzelteile aufgelöst werden:
<regexp> && <regexp> && ...
Beispiel
Licht && Wohnzimmer && schalte = ...
Akzeptiert werden hier
Schalte das Licht im Wohnzimmer Im Wohnzimmer das Licht schalten Schalte im Wohnzimmer das licht
RegExp Optionen
Die einzelnen RegExp Teile können zusätzlich mit den Zeichen ? und ! beeinflusst werden:
- Negierung !<regexp>
- Optional ?<regexp>
Beipiel:
Licht && ?Wohnzimmer && schalte && !aus = ...
akzeptiert
Schalte das Licht im Wohnzimmer ein Schalte das Licht ein
aber nicht
Schalte das Licht im Wohnzimmer aus Schalte das Licht aus
zusätzliche Variablen
Ergänzend zu den Listenvariablen stehen folgende Variablen zur Verfügung:
- $& Enthält alle gefundenen Wörter
- !$& Enthält den Rest der nicht von der RegExp eingeschlossen wurde
- $0 Enthält den Text der erkannten T2F_origin RegExp
- $DATE Enthält den Zeit und Datumstext des Sprachbefehls
- $TIME Enthält die erkannte Zeit in Sekunden
- $IF Enthält den Text der erkannten T2F_if Konfiguration
- $NAME Enthält den Devicenamen
- $AGAIN Enthält das Wort wieder wenn es sich um ein wieder Kommando handelt (siehe Beispiele)
erweiterte Befehlskonfiguration
Um Talk2Fhem in den Konfigurationszeilen weitere Parameter zu übergeben, ist eine gesonderte Syntax zu verwenden.
<regexp> = ( option => 'value' , opt2 => 'value2' , ... optn => 'valuen' )
Es stehen folgende Optionen zur Verfügung:
- cmd => enthält das FHEM Kommando. Wie oben beschrieben. Wird bei bedarf Zeit- oder Ereignisgebunden ausgeführt.
- answer => Ein Perl-Befehl der direkt ausgeführt wird und dessen Rückgabe ein Text sein sollte. Das Ergebnis wird in das Reading answer geschrieben. Der Befehl muss in einem der folgenden Zeichenpaaren eingeschlossen sein. ( ), { }, " " oder ' '. Zu empfehlen sind einfache Anführungszeichen, da dieses verwendet werden müssen wenn Kommazeichen benötigt werden.
- offset => Ganzzahliger Wert in Sekunden der dem Zeitpunkt addiert wird. Siehe Modul_Talk2Fhem#Zeitenmodifikation
Zeitenmodifikation
Manchmal ist es notwendig etwas vor der angegebenen Zeit auszuführen. Hier lässt sich ein Offset zu dem ermittelten Zeitpunkt hinzufügen, um ihn zu ändern.
Beispiel
dusche\S?$ = (offset=>-3600, cmd=>'set d_log bad warm')
Ausgabe:
Spracheingabe | FHEM Kommando |
---|---|
ich will um 18:30 Uhr duschen | define at_talk_1513096200_0 at 2017-12-12T17:30:00 set d_log bad warm |
Antworten
In Talk2Fhem können Antworten, die das Modul ausgeben soll, definiert werden. Hier können Fragen verarbeitet werden oder auch der Erfolg eines Kommandos bestätigt werden. Die Antworten werden immer direkt ausgegeben ohne zeitlichen oder ereignisbehafteten Bezug.
Über die Modul_Talk2Fhem#erweiterte_Befehlskonfiguration, erhält der Parameter "answer" einen Perl Befehl. Die Rückgabe muss ein Text sein, der als Antwort dient.
Beispiel Erfolgsmeldung
tu was = ( cmd => "set tue es" , answer => ("Ja") )
Da answer immer sofort ausgeführt wird, hat das Ja keine Aussagekraft bei zeitlichen oder eventbezogenen Kommandos. Möchte man eine Rückmeldung zu diesem Zeitpunkt bekommen, müsste das dem FHEM Kommando hinzugefügt werden.
tu was = ( cmd => "set tue es;; set tts speak Erledigt!" , answer => ("Ja") )
Beispiel Zustandsabfrage
wurde es getan = ( answer => { Value("tue") eq "es" ? "Ja" : "Nein" })
Beispiel Temperaturabfragen
Eine einfache Temperaturabfrage könnte so aussehen.
wie.*(kalt|warm|grad|temperatur) =
( answer =>
' "Die Temperatur beträgt ".ReadingsVal("tempdev", "temperature", "Fehler") '
)
Für eine Raumbezogene Temperaturabfrage, siehe Modul_Talk2Fhem#Anwendungsbeispiele_und_Vorlagen
Personalifizierung
Für eine Personenbezogene Konfiguration gibt es die Hilfestellung des Attributs T2F_origin.
Der Gedanke dahinter ist, man fügt dem Sprachbefehl einen Zusatz, wie z.B. den Namen am Anfang des Satzes, hinzu.
Ein notify auf ein Telegramdevice könnte so ausehen:
telbot:msgText.* {
my $p=ReadingsVal("telbot", "msgPeer","");
fhem("set talk ".($p?$p."=":"").$EVENT=~s/^msgText: //r);
}
Jetzt wird dem Textbefehl immer der Name (msgPeer) und anschließendem "=" dem Befehl hinzugefügt und an talk gesendet. Dieser Text kann mit T2F_origin verarbeitet werden.
siehe hierzu Modul_Talk2Fhem#T2F_origin
Attribute
Folgende Attribute werden zur Zeit unterstützt
T2F_keywordlist
<Name> = <kommaseparierte Liste mit RegExp>
Beispiele
&rooms = haus|überall|wohnung , wohnzimmer , bad\\S* , toilette|wc , büro , 'eg|erdgescho\S\S?' ... names = Mama , Papa , Kevin , Jacqueline channels = ard|das erste , zdf , rtl , sat 1 , vox , rtl2 , prosieben , kabel eins , arte
Man beachte hier die möglichkeit der RegExp
Das Schlüsselwort haus löst das selbe aus wie überall und wohnung
Genauso bad oder badezimmer oder badeirgendewas
Achtung:
Damit das Modul die Fähigkeit der Objektverundung erlangt, müssen die Objekte benannt werden die dafür geeignet sind. Hierzu muss lediglich der Keywordliste mit den vorhandenen RegExp ein kaufmännisches Und '&' vorangestellt werden. Es ist nicht notwendig diese Keywordlisten in der Konfiguration zu verwenden. In der Regel handelt es sich hierbei um Räume oder Geräte. Siehe &rooms.
Dadurch lassen sich folgende beispielhaften Sätze formulieren.
mach das Licht im Wohnzimmer, in der Küche und im Esszimmer an Rollos im Schlafzimmer und im Kinderzimmer runter
Diese Eigenschaft sollte nur bei sinnhaften Listen angewendet werden. Wenn notwendig kann eine separate Liste hierfür angelegt werden. Eine Vorlage für Räume kann hier kopiert werden. #Keywordlist
T2F_modwordlist
<Name> = <kommaseparierte Liste mir RegExp>
Die Positionen der gewählten Geräte, ist eine Zuordnung zu den Listen inT2F_keywordlist
Wenn Wohnzimmer in der keywordliste <rooms> an Position 2 steht muss der Rolladenaktor in der modworliste <roll> auch an Position 2 stehen.
roll = rollos_alle , d_rollo_wz , hm_roll_bad.* , hm_roll_wc , hm_roll_buero.* , ... lights = alle_lichte , "sw_wz1,sw_wz2" , sw_bad.* , sw_wc
Sollen einem Schlüsselwort mehrere Aktoren zugeordnet werden, geht das über sogenannte "Quotes". (Anführungszeichen) Siehe <lights>.
"a,b" oder 'a,b' oder a\,b
T2F_if
Möchte man einen Befehl erst zu einem bestimmten Ereignis oder zu einer Bedingung ausführen lassen, lässt sich das über dieses Attribut konfigurieren.
Die Syntax orientiert sich hier an der der Definition. Die Syntax der Bedingung entspricht der des FHEM-Befehls IF. Sobald hier das Device dev_ring den Status ring erhält, wird das zusätzlich erkannte Kommando ausgeführt.
wenn .*?klingel = [dev_ring] eq "ring"
Beispielsätze
wenn es klingelt wenn jemand die klingel betätigt
Hier stehen genauso wie in der Definition Klammermodifikatoren zur Verfügung.
wenn die temperatur (über|unter) ([\s\.,\d]+) grad = [sens:temp] $1[>,<] $2{float=>"$2"}
Ausgabe:
Spracheingabe | FHEM Bedingung |
---|---|
wenn die temperatur über 22 grad steigt | [sens:temp] > 22 |
wenn die temperatur unter 21, 5 grad fällt | [sens:temp] < 21.5 |
Erweiterte Optionen in dieser Konfigurationszeile sind möglich aber noch nicht relevant.
T2F_origin
Das Attribut T2F_origin kann für eine herkunftsabhängige Beeinflussung der Befehle genutzt werden. Das bedeutet abhängig davon, wer den Befehl gibt oder von wo der Befehl abgegeben wurde. Es enthält eine RegExp die generell vom ganzen Sprachbefehl entfernt und in der Variable $0 zu Verfügung gestellt wird
Der Inhalt des Attributs könnte folgender sein.
^\w+=
Nun wird ein am Anfang auftretendes wort= entfernt und als Origin behandelt.
Beispiel
Spiele meine Musik = set mediaplayer play pop
Jetzt soll das Wort pop Personenbezogen umgeformt werden.
Spiele meine Musik = set mediaplayer play $0{/kevin/=>hiphop,/jaqueline/=>gothik,else=>pop}
Folgender Satz setzt dann für das Wort pop hiphop ein.
kevin= spiele meine Musik bitte
Das ganze lässt sich auch verschachteln und mit Keywordlisten kombinieren. Siehe hierzu Beispiele.
T2F_disableumlautescaping
Deaktiviert das Konvertieren der Umlaute innnerhalb von regulären Ausdrücken in \S\S?
T2F_language
Legt die verwendete Sprache fest. Alternativ wird das Globale Attribut language verwendet werden.
disable
Deaktiviert das Ausführen des FHEM Kommandos
verbose
Anwendungsbeispiele und Vorlagen
Hier folgen diverse Vorlagen und Beispiele die jeweils an die eigenen Bedürfnisse angepassst werden sollten/müssen. Hierzu sind die entsprechenden Stellen rot markiert.
Keywordlist
Eine typische Keywordlist wäre eine Auflistung der benötigten Räume
&rooms = haus|überall|wohnung,wohnzimmer,esszimmer,küche,bad\\S*,toilette|wc, büro,schlafzimmer,ankleide|garderobe,kinderzimmer,spielzimmer, flur|gang|diele,garage,garten,terrasse,balkon, 'eg|erdgescho\S*','og|obergescho\S*','\S*außen|außer haus|vor der tür'
Das '&' vor rooms ist eine Besonderheit der Objektverundung, und sollte nur bei ausgewählten Listen verwendet werden. Siehe T2F_keywordlist
Einfaches schalten
1. Beispiel
licht (\S+) = set dev_light $1{ true => on, false => off }
2. mit eigenen Wörtern
Will man neben an/aus einen weiteren Zustand selbst definieren, kann das so geschehen.
garage (\S+) = set dev_gate $1{ /kurz/ => open-for-timer 300, true => open, false => close }
3. mit Räumen
licht (\S+ ){0,2}(wohnzimmer|esszimmer|küche|terrasse) (\S+) = set $2[light_wz,light_ez,light_kitchen,light_outside] $3{ true => on, false => off }
Bei größeren Mengen an Räumen und Geräten bietet sich das anlegen von Keyword- und Modwordlisten an.
licht (\S+ ){0,2}(@rooms) (\S+) = set $2[@lights] $3{ true => on, false => off }
Rolladen fahren
Sind die Rollläden innerhalb von FHEM über das Attribut "room" den Räumen mit Klarnamen zugeordnet, lässt sich das ganze am einfachsten konfigurieren.
1. nach Devicetyp
rolll?(os?|\S\S?den) ?(\S+ ){0,2}(\S+) (auf )?(\S+) = set room~$3{/haus/=>.*,empty=>.*,else=>$3} :FILTER=a:subType=blindActuator $5{true=>on, false=>off, integer=>set_$5}
Erklärung
Wenn das Device vom subTyp "blindActuator" ist (HomeMatic Rolladenactor) und es im Raum aus der dritten Klammer ($3) ist, wird dieses Device gefahren.
2. nach Devicenamen
set room~$3{/haus/=>.*,empty=>.*,else=>$3} :FILTER=rollo.* $5{true=>on, false=>off, integer=>set_$5}
Erklärung
Gleiche wie oben, nur das alle Devices die mit rollo beginnen im Raum $3 gefahren werden. (rollo.*)
3. nach Benutzerdefinierten Räumen
Ansonsten müssen wir dem Modul sagen in welchem Raum welches Device sitzt. Das hat dafür den Vorteil das die Räume als RegExp angegeben werden können.
Wir erzeugen eine Modwordlist rollos in der wir alle Rollladen "Devices" auflisten. Die Reihenfolge ist an der Keywordlist "rooms" (Modul_Talk2Fhem#Keywordlist) anzupassen.
rollos = r_alle,r_wz,r_ez,,r_buero,...
Definition
rolll?(os?|\S\S?den) ?(\S+ ){0,2}(@rooms)? (auf )?(\S+) = set $3[@rollos, empty, rollos_alle] $5{true=>on, false=>off, integer=>"set_$5"}
Beispielsätze
Rollos auf Rolllos Überall schließen Rollo im Wohnzimmer hoch Rolladen im Esszimmer runter Rollläden in der Küche auf 50
Damit wäre eigentlich schon alles abgedeckt. Folgendes Beispiel ist aber auch noch nützlich.
Definition
(blendet|schatte\S?) ?(\S+ ){0,2}(@rooms) = set $3[@rollos] set_70
Beispielsätze
es blendet im Esszimmer ich werde geblendet in der Küche die Sonne blendet mich im Badezimmer beschatte das Haus mach schatten im Erdgeschoss
Erweiterte Personalisierung
Wie löst man das Problem mit dem Wort "ich" in Sätzen wie:
Wenn ich nach hause komme
Voraussetzung siehe Modul_Talk2Fhem#T2F_origin
T2F_keywordlist @names mit den Namen:
names = wir|alle|uns,ich|mein\S*,hans,jutta,kevin,jaqueline
T2F_modwordlist @devs mit den Geräten der Personen.
devs = d_alleda,,handyhans,handyjutta,handykevin,handyjac
Standardmäßig könnte eine Konfigurationszeile in T2F_if folgendermaßen definiert werden.
wenn (@names) (heim|nach hause) komm\S* = [$1[@devs]] =~ /ja|present/
Für das Device d_alleda wird angenommen das dieses ja und nein enthalten kann, und deswegen nicht mit present verglichen werden kann.
In @devs ist für ich kein Wert vorgesehen, da es sich ja um einen variablen Wert handelt. Deswegen erfassen wir erstmal ich über die /regexp/. Andernfalls wird die Liste @devs herangezogen.
wenn (@names) (heim|nach hause) komm\S* = [$1{/ich/=>weristich,else=>'$1[@devs]'}] =~ /ja|present/
Nun weisen wir über die origin Variabel $0 dem Platzhalter weristich die Geräte zu.
wenn (@names) (heim|nach hause) komm\S* = [$1{/ich/=>$0{/hans/=>handyhans, /jutta/=>handyjutta, /kevin/=>handykevin, /jaque/=>handyjac}, else=>'$1[@devs]'}] =~ /ja|present/
Das Beispiel zeigt wie eine verschachtelte Personenzuordnung aussehen könnte. Die Abarbeitung der $n Modifikatoren erfolgt von rechts nach links.
Folgende Sätze werden konvertiert in:
Sätze | Ergebnis |
---|---|
jutta= wenn hans nach hause kommt | [handyhans] =~ /ja|present/ |
hans= wenn wir heim kommen | [d_alleda] =~ /ja|present/ |
kevin= wenn ich nach hause komme | [handykevin] =~ /ja|present/ |
GoogleCast Befehle
Etwas bessere Lautstärkebefehle als die von Google. Die Lautstärken müssten noch dem eigenen Geschmack angepasst werden
#GoogleCast Commandos (leise\b|normale lautstärke|laut\b) = set googlecastdevice volume $1[14,20,30] (ein wenig|etwas|viel)? ?(lauter|leiser) = { fhem("set googlecastdevice volume ".(ReadingsVal("googlecastdevice","volume", 0)$2[+,-]$1[3,5,10,empty,7])) }
Beispielsätze
Mach leise laut machen Mach lauter etwas leiser viel lauter
Für eine raumbezogene Lautstärkenkonfiguration wären nur ein paar Änderungen notwendig.
?(@rooms) && (leise\b|normale lautstärke|laut\b) = set $1[@musicdevices,empty,standard] volume $2[14,20,30]
?(@rooms) && (ein wenig|etwas|viel)? ?(lauter|leiser) =
{ fhem("set $1[@musicdevices,empty,standard] volume ".(ReadingsVal("$1[@musicdevices,empty,standard]","volume", 0)$3[+,-]$2[3,5,10,empty,7])) }
Talk Timer zurücksetzen
Sollen alle von Talk2Fhem angelegten Timer gelöscht werden.
(timer|kommandos) (löschen|zurücksetzen) = set cleartimers $NAME
Beispielsätze
bitte alle timer zurücksetzen zukünftige kommandos löschen
Frage Antwort
Raumbezogene Temperaturansage
Wir erzeugen wieder unsere Modwordlist sagen wir @sens gefüllt mit den Temperaturfühler "Devices". Reihenfolge wie immer die der Keywordlist @rooms.
wie.*(kalt|warm|grad|temperatur).*(@rooms) = ( answer => '"Die Temperatur beträgt ".ReadingsVal("$2[@sens]", "temperature", "unbekannt")." grad"' )
Beispielsätze
wie warm ist es im Wohnzimmer wie ist die Temperatur in der Küche wie kalt ist es draußen
Multiple Antworten
Hallo Haus = ( answer => '["Hallo auch!","Guten Tag!","Ahoi!"]->[rand(3)]' )
Wählt zufällig eine der drei Aussagen.
Ausgabe eines oder mehrerer Zustände
Angenommen man möchte wissen ob das Haus abgeschlossen ist. Und in FHEM existiert ein Dummy der diesen Zustand wiederspiegelt und im Reading text die offenen Fenster und Türen aufgelistet sind. Kann man folgendermaßen den Status erfragen
(alles|das haus) (zu|abgeschlossen) = ( answer => '(Value("d_schliessung") eq "zu") ? "Es ist alles zu" : "Nein, offen sind: ".ReadingsVal("d_schliessung","text","")'
Einfache Standardantwort
schalte etwas (\S+) = (
cmd => "set etwas $1{ true => on, false => off }",
answer => ("OK") )
Schaltzustand als Feedback
Bei Dingen die kein offensichtliches Feedback erzeugen oder wo getoggelt wird, macht es eventuell Sinn eine Zustandsabhängige Antwort zu geben. schalte etwas (\S+) =
set etwas $1{ true => on, false => off}; defmod atdekofb at +00:00:01 IF ([etwas] eq "on") (setreading $NAME answers Angeschaltet.) ELSE (setreading $NAME answers Ausgeschaltet.)
Erfolgsmeldung
Soll die Antwort abhängig vom Erfolg des Befehls sein dann könnte es so aussehen.
schalte etwas (\S+) = set etwas $1{ true => on, false => off}; defmod atdekofb at +00:00:01 IF ([etwas] eq "$1{ true => on, false => off}") (setreading $NAME answers Kommando erfolgreich.) ELSE (setreading $NAME answers Das hat leider nicht geklappt.)
Zustandsabfrage anhand des Aliasnamen
Möchte man alle Geräte anhand seines Attribut "alias" ansprechen, um dessen Status zu erfragen, könnte das so erfolgen.
Definition
(zustand|status)( \S+)* (\S+) =
( answer => {"Der Status ist ".(Value((devspec2array('a:alias~$3'))[0]) || "unbekannt")} )
Beispielsätze
Wie ist der Zustand der Lüftung sag mir den Status von der Haustür
Es ist auch möglich ein eigenes Attribut zu kreieren, und dieses als Klarnamenattribut zu verwenden. Hierzu einfach im Device global ein userattr hinzufügen. z.B.
attr global userattr T2F_alias
Jetzt stehen in allen Devices das Attribut T2F_alias zur Verfügung. Vorteil ist das dann auch wieder RegExp verwendet werden können. Im oberen Beispiel müsste dann dass rot markierte "alias" durch "T2F_alias" ersetzt werden.
Ereignisbezogene Meldungen
Da answer immer sofort den Text ausgibt ist hier beschrieben wie man eine Antwort zu einem bestimmten Ereignisses erhalten kann.
Es genügt folgende Zeile anzulegen und seinen bedürfnissen anzupassen.
bescheid = set googlecastdevice speak "Ich sollte bescheid geben $IF"
Über die T2F_if Bedingungen bekommt man jetzt immer eine Aussage sobald eine Ereignis eintritt.
Sätze wie
gib bescheid wenn mama nach hause kommt sag mir wenn die tür öffnet bescheid wenn es draußen kalt ist sag mir bescheid
ergeben dann folgende Kommandos.
set googlecastdevice speak "Ich sollte bescheid geben wenn mama nach hause kommt" set googlecastdevice speak "Ich sollte bescheid geben wenn die tür öffnet" set googlecastdevice speak "Ich sollte bescheid geben wenn es draußen kalt ist"
Man sollte darauf achten, dass die T2F_if Bedingungnen möglichst komplett von der RegExp erfasst werden. Dadurch können die Sätze komplett wiederholt werden.
Konfiguration innerhalb der Geräte
Neben der Identifikation der Geräte über die vorhandenen FHEM-Attribute (z.B. room, alias, ...) kann eine alternative Konfiguration auch über eigens für Talk2Fhem angelegte Attribute erfolgen. Die hierfür notwendigen Grundlagen und einige Beispiele sollen im Folgenden beschrieben werden.
Grundlagen / Voraussetzungen
1. Anlegen der notwendigen userattr am global-Device:
Erweiterung des Attributes userattr des Gerätes global um folgende Einträge:
T2F_places:textField-long T2F_properties:textField-long T2F_rooms:textField-long T2F_types_color:textField-long T2F_types_heating:textField-long T2F_types_info:textField-long T2F_types_switch:textField-long
2. Optional: Hilfsfunktionen für das automatische Füllen der T2F_keywordlist im talk-Device bei Änderung eines T2F-Attributes eines Gerätes
2a. erstellen einer sub in der 99_myUtils.pm:
sub fill_T2F_keywordlist
{
my ($name, $t2f_device) = @_;
$name =~ s/T2F_//g;
if ($name eq 'userattr')
{
return;
}
Log 0, "List: ".$name;
my $currentAttr = AttrVal($t2f_device,"T2F_keywordlist","");
my @currentAttrParts = split(/$name = /, $currentAttr);
my $currentAttrBeg = @currentAttrParts[0];
$currentAttrBeg = substr($currentAttrBeg, 0, -1);
my @currentAttrEnd = split(/\n/, @currentAttrParts[1], 2);
my @array = devspec2array('a:T2F_'.$name.'=.+');
my @attributes = ();
if (@array > 0 and defined($defs{$array[0]}))
{
foreach (@array){
my @attrVals = split(/\n/,AttrVal($_, 'T2F_'.$name, ''));
foreach (@attrVals){
my $attrVal = $_;
$attrVal =~ s/ / /g;
$attrVal =~ s/ => /=>/g;
my @attrValParts = split(/=>/,$attrVal);
$attrVal = @attrValParts[0];
$attrVal =~ s/!//g;
$attrVal =~ s/, /,/g;
my @attr = split(/,/, $attrVal);
push(@attributes, @attr);
}
}
}
my %hash = map { $_ => 1 } @attributes;
my @unique = keys %hash;
my $result = $currentAttrBeg."\n".$name." = ".join(", ", @unique)."\n".@currentAttrEnd[1];
$result =~ s/\n\n/\n/g;
$result =~ s/ / /g;
fhem('attr '.$t2f_device.' T2F_keywordlist '.$result);
}
2b. Erstellen eines DOIFs zum Aufruf der sub:
defmod talk.DI.fillAttr DOIF ([global:"^ATTR.*T2F_.*"]) ({my $val = ReadingsVal("$SELF", "e_global_events", ""); $val =~ m/(\S*) (\S*) (\S*) (.*)/; if ($2 ne 'talk' && $2 ne 'global') { fill_T2F_keywordlist("$3", "talk");}}) attr talk.DI.fillAttr do always
3. Optional: Erlauben von Umlauten in den T2F-Attributen:
attr talk T2F_disableumlautescaping 1
Nun können die T2F-Attribute pro FHEM-Device definiert und dann in den Talk2Fhem-Befehlen benutzt werden. Wurde der Punkt 2 abgearbeitet, so werden mit dem Füllen der Attribute am Geräte auch die Keywordlisten am talk-Device gefüllt. Wie diese dann verwendet werden können, sollen die folgenden Beispiele zeigen.
Schalten von Geräten
Ausgangssituation: Es gibt drei Lampen Lampe1, Lampe2, Lampe3 und ein Nachtlicht mit einer Eule mit folgenden Attributen:
attr Lampe1 T2F_types_switch Lampe, !Licht attr Lampe1 T2F_rooms Haus, Obergeschoss, !Esszimmer attr Lampe1 T2F_places Decke, Tür attr Lampe1 T2F_properties hell,!
attr Lampe2 T2F_types_switch Lampe, !Licht attr Lampe2 T2F_rooms Haus, Obergeschoss, !Esszimmer attr Lampe2 T2F_places Tisch, Esstisch attr Lampe2 T2F_properties dunkel,schwach,!
attr Lampe3 T2F_types_switch Lampe, !Licht attr Lampe3 T2F_rooms Haus, Obergeschoss, !Küche attr Lampe3 T2F_places Besenschrank attr Nachtlicht T2F_types_switch Lampe, !Licht, Eule, Nachtlicht attr Nachtlicht T2F_rooms Haus, Obergeschoss, !Kinderschlafzimmer attr Nachtlicht T2F_places Steckdose
Durch die in Punkt 2 der Voraussetzungen genannten Funktionen wurden folgende T2F-keywordlisten automatisch angelegt:
attr talk T2F_keywordlist rooms = Kinderschlafzimmer, Haus, Küche, Obergeschoss, Esszimmer places = Tür, Steckdose, Tisch, Decke, Esstisch properties = hell, dunkel, schwach types_switch = Lampe, Licht, Eule
Die Definition für das T2F-Device lautet dann wie folgt:
# 1 2 3 4 5 6 7 ?(bitte) && ?(@properties) && (@types_switch) && ?(@rooms) && ?(@places) && (\S+)(schalten|machen)?$ = (cmd=>'set T2F_types_switch=.*$3@.*:FILTER=T2F_rooms=.*$4@.*:FILTER=T2F_properties=.*$2@.*:FILTER=T2F_places=.*$5@.* $6{true=>on, false=>off}', answer=>'"$AGAIN" ? "dann $DATE wieder $6{true=>ein, false=>aus}" : "$1{/bitte/=>Gern, else=>Das heißt Bitte}, ich schalte $1{/bitte/=>, else=>trotzdem} folgende Geräte $6{true=>ein, false=>aus}: ".T2F_answer("T2F_types_switch=.*$3@.*:FILTER=T2F_rooms=.*$4@.*:FILTER=T2F_properties=.*$2@.*:FILTER=T2F_places=.*$5@.*","T2F_types_switch")')
Damit sind dann die folgenden Sprachbefehle möglich:
Schalte das Licht in der Küche ein => schaltet Lampe3 ein Schalte die helle Lampe im Esszimmer an => schaltet Lampe1 ein Schalte das Licht im Esszimmer über dem Esstisch ein => schaltet Lampe2 ein Schalte die Eule ein => schaltet Nachtlicht ein Schalte das Licht im Esszimmer aus} => schaltet Lampe1 und Lampe2 aus Schalte das Licht aus => schaltet alle vier Lichter aus
Um in der Antwort die Liste der geschalteten Geräte genannt zu bekommen, wird die sub T2F_answer aus der 99_myUtils aufgerufen:
sub T2F_answer
{
my ($filter, $type) = @_;
my $answer = '';
my @devices = devspec2array($filter);
if (@devices > 0 and defined($defs{$devices[0]}))
{
foreach (@devices){
my $devAttr = AttrVal($_, $type, '');
if ($answer ne '')
{
$answer = $answer.", ";
}
if (substr($devAttr,-1) ne "!")
{
my @entries = split(/!/, $devAttr);
if (@entries < 2)
{
@entries = split(/,/, $entries[0]);
}
else
{
@entries = split(/,/, $entries[1]);
}
if (@entries[0] ne "")
{
$answer = $answer.@entries[0]." ";
}
}
$devAttr = AttrVal($_, 'T2F_properties', '');
if (substr($devAttr,-1) ne "!")
{
my @entries = split(/!/, $devAttr);
if (@entries < 2)
{
@entries = split(/,/, $entries[0]);
}
else
{
@entries = split(/,/, $entries[1]);
}
if (@entries[0] ne "")
{
$answer = $answer.@entries[0]." ";
}
}
$devAttr = AttrVal($_, 'T2F_rooms', '');
if (substr($devAttr,-1) ne "!")
{
my @entries = split(/!/, $devAttr);
if (@entries < 2)
{
@entries = split(/,/, $entries[0]);
}
else
{
@entries = split(/,/, $entries[1]);
}
if (@entries[0] ne "")
{
$answer = $answer.@entries[0]." ";
}
}
$devAttr = AttrVal($_, 'T2F_places', '');
if (substr($devAttr,-1) ne "!")
{
my @entries = split(/!/, $devAttr);
if (@entries < 2)
{
@entries = split(/,/, $entries[0]);
}
else
{
@entries = split(/,/, $entries[1]);
}
if (@entries[0] ne "")
{
$answer = $answer.@entries[0]." ";
}
}
}
}
return $answer."";
}
Die Benennung der Geräte erfolgt auf Basis derer T2F-Attribute: Typ, Eigenschaft, Raum, Ort. Ist das jeweilige Attribut leer, ist es auch in der Antwort leer. Enthält die jeweilige Attributliste ein Ausrufezeichen (!), so wird der Eintrag nach dem Ausrufezeichen für die Antwort verwendet (z.B. Esszimmer als Raum). Steht das Ausrufezeichen am Ende, so wird der Eintrag in der Antwort leer gelassen (z.B. die Eigenschaft bei Lampe1). Ist kein Ausrufezeichen vorhanden, so wird der erste Eintrag verwendet.
Einstellen der Heizung
Das gleiche Prinzp wie bei den Lampen kann auch für die Einstellung der Heizung verwendet werden.
Ausgangssituation: In der Küche, im Kinderschlafzimmer und im Esszimmer gibt es jeweils eine Heizung (Homematic HM-CC-RT-DN) mit folgenden Attributen:
attr HeizungEsszimmer T2F_places Heizung,! attr HeizungEsszimmer T2F_rooms Haus,Obergeschoss,Essbereich,!Esszimmer attr HeizungEsszimmer T2F_types_heating Heizung,Temperatur,Esszimmer,Essbereich,!
attr HeizungKüche T2F_places Heizung,! attr HeizungKüche T2F_rooms Haus,Obergeschoss,Essbereich,!Küche attr HeizungKüche T2F_types_heating Heizung,Temperatur,Küche,Essbereich,!
attr HeizungKiSchla T2F_places Heizung,! attr HeizungKiSchla T2F_rooms Haus,Obergeschoss,!Kinderschlafzimmer attr HeizungKiSchla T2F_types_heating Heizung,Temperatur,Kinderschlafzimmer,!
Mit der T2F-Definition
# 1 2 3 4 5 ?(bitte) && (@types_heating) && ?(@rooms) && (auf (\d+) grad|auto\S*)( stellen| setzen| einstellen| ein)?$ = (cmd=>'set T2F_types_heating=.*$2{empty=>.+, else=>$2@}.*:FILTER=T2F_rooms=.*$3@.* $5{integer=>desired-temp $5, else=>controlMode auto}', answer=>'"$AGAIN" ? "dann $DATE wieder auf Automatik" : "Die Durchschnittstemperatur beträgt dort zur Zeit ".averageTemp("T2F_types_heating=.*$2{empty=>.+, else=>$2@}.*:FILTER=T2F_rooms=.*$3@.*")." Grad\n$1{/bitte/=>Gern, else=>Das heißt Bitte}, ich stelle die Heizung in folgenden Räumen auf $5{integer=>$5 Grad, else=>Automatik}: ".T2F_answer("T2F_types_heating=.*$2{empty=>.+, else=>$2@}.*:FILTER=T2F_rooms=.*$3@.*","T2F_types_heating")')
funktionieren folgende Sprachbefehle:
Bitte stell die Heizung im Esszimmer auf 21 Grad => Stellt die Heizung im Esszimmer auf 21 Grad Bitte stell die Heizung im Essbereich auf 21 Grad => Stellt die Heizungen im Esszimmer und in der Küche auf 21 Grad
Bitte stell die Heizung im Obergeschoss auf Automatik => Stellt die drei Heizungen auf Automatik
Dadurch, dass im Attribut T2F_types_heating auch die Räume aufgeführt sind, sind auch Befehle in folgender Form möglich:
Bitte stell die Küche auf 21 Grad => Stellt die Heizung in der Küche auf 21 Grad
Auch kann in diesem (wie auch in den oben aufgeführten Schaltbefehlen) mit "wieder" gearbeitet werden:
Bitte stell die Heizung im Essbereich auf 21 Grad und in 2 Stunden wieder auf Automatik
Für die Generierung der Antwort wird neben der bei den Schaltbefehlen bereits gezeigten sub T2F_answer eine weiter Funktion verwende, welche die Durchschnittstemperatur im zu schaltenden Bereich ermittelt und ausgibt:
sub averageTemp($)
{
my ($filter) = @_;
my @tempDevices = devspec2array($filter);
my $count = 0;
my $measuredTemp = 0;
if (@tempDevices > 0 and defined($defs{$tempDevices[0]})){
foreach (@tempDevices){
$measuredTemp = $measuredTemp + ReadingsVal($_, "measured-temp", "");
$count = $count + 1;
}
return $measuredTemp / $count;
}
return 0;
}
Einstellen der Farbe von Farbwechsellampen (Philips Hue, Wifilight, ...)
Für das Beispiel zum Einstellen der Lichtfarbe nehmen wir eine Farbwechsellampe LampeBunt an, welche den Befehl RGB unterstützt. Diese erhält folgende Attribute:
attr LampeBunt T2F_places Decke,Couch,Sofa attr LampeBunt T2F_rooms Haus,Dachgeschoss,!Wohnzimmer attr LampeBunt T2F_types_color Lampe,Licht attr LampeBunt T2F_types_switch Lampe,Licht
Durch das Attribut T2F_types_switch lässt sich diese über die bereits beschriebene Schaltlogik ein und ausschalten. Durch die zusätzlich T2F-Definition
# 1 2 3 4 5 6 ?(bitte) && (@types_color) && ?(@rooms) && ?(@places) && auf (@colors)( schalten| stellen)?$ = (cmd=>'set T2F_types_color=.*$2@.*:FILTER=T2F_rooms=.*$3@.*:FILTER=T2F_places=.*$4@.* RGB $5[@rgb]', answer=>'"Ich schalte folgende Geräte auf $5@: ".T2F_answer("T2F_types_color=.*$2@.*:FILTER=T2F_rooms=.*$3@.*:FILTER=T2F_places=.*$4@.*","T2F_types_color")')
und die entsprechenden Attribute am T2F-Device talk (HINWEIS: eigene Listen können Problemlos ergänzt werden, diese werden durch die Hilfsfunktionen NICHT überschrieben)
attr talk T2F_keywordlist colors = Aus, Schwarz, Dunkles Schiefergrau, Schiefergrau, Helles Schiefergrau, Helles Stahlblau, Mattes Grau, Grau, Dunkelgrau, Silber, Hellgrau, Gainsboro, Rauchiges Weiß, Geisterweiß, Weiß, Schneeweiß, Elfenbein, Blütenweiß, Muschel, Altgold, Leinenfarbe, Antikes Weiß, Mandelweiß, Cremiges Papaya, Beige, Mais, Helles Goldrutengelb, Hellgelb, Chiffongelb, Blasse Goldrutenfarbe, Khaki, Gelb, Gold, Orange, Dunkles Orange, Goldrute, dunkle Goldrutenfarbe, Peru, Schokolade, Sattelbraun, Ocker, Braun, Dunkelrot, Kastanienbraun, Ziegelfarbe, Indischrot, Karmesinrot, Rot, Orangenrot, Tomatenrot, Koralle, Lachs, Helles Korallenrot, Dunkle Lachsfarbe, Helle Lachsfarbe, Sandbraun, Rosiges Braun, Gelbbraun, Grobes Braun, Weizen, Pfirsich, Navajoweiß, Tomatencreme, Rosige Lavenderfarbe, Altrosa, Rosa, Hellrosa, Leuchtendes Rosa, Fuchsie, Magentarot, Tiefrosa, Mittleres Violettrot, Blasses Violettrot, Pflaume, Distel, Lavendelfarbe, Violett, Orchidee, Dunkles Magentarot, Violett, Indigo, Blauviolett, Dunkles Violett, Dunkle Orchideenfarbe, Mittleres Violett, Mittlere Orchideenfarbe, Mittleres Schieferblau, Schieferblau, Dunkles Schieferblau, Mitternachtsblau, Marineblau, Dunkelblau, Mittelblau, Blau, Königsblau, Stahlblau, Kornblumenblau, Dodger-Blau, Tiefes Himmelblau, Helles Himmelblau, Himmelblau, Hellblau, Zyanblau, Blaugrün, Taubenblau, Helles Cyanblau, Aliceblau, Himmelblau, Cremig Pfefferminz, Honigmelone, Aquamarinblau, Türkis, Blasses Türkis, Mittleres Türkis, Dunkles Türkis, Mittleres Aquamarinblau, Helles Seegrün, Dunkles Zyanblau, Entenbraun, Kadettblau, Mittleres Seegrün, Dunkles Seegrün, Hellgrün, Blassgrün, Mittleres Frühlingsgrün, Frühlingsgrün, Zitronengrün, Gelbgrün, Seegrün, Waldgrün, Grün, Dunkelgrün, Olivfarbiges Graubraun, Dunkles Olivgrün, Olivgrün, Dunkles Khaki, Gelbgrün, Hellgrün, Grüngelb
attr talk T2F_modwordlist rgb = 000000, 000000, 8FBC8F, 708090, 778899, B0C4DE, 696969, 808080, A9A9A9, C0C0C0, D3D3D3, DCDCDC, F5F5F5, F8F8FF, FFFFFF, FFFAFA, FFFFF0, FFFAF0, FFF5EE, FDF5E6, FAF0E6, FAEBD7, FFEBCD, FFEFD5, F5F5DC, FFF8DC, FAFAD2, FFFFE0, FFFACD, EEE8AA, F0E68C, FFFF00, FFD700, FFA500, FF8C00, DAA520, B8860B, CD853F, D2691E, 8B4513, A0522D, A52A2A, 8B0000, 800000, B22222, CD5C5C, DC143C, FF0000, FF4500, FF6347, FF7F50, FA8072, F08080, E9967A, FFA07A, F4A460, BC8F8F, D2B48C, DEB887, F5DEB3, FFDAB9, FFDEAD, FFE4C4, FFF0F5, FFE4E1, FFC0CB, FFB6C1, FF69B4, FF00FF, FF00FF, FF1493, C71585, DB7093, DDA0DD, D8BFD8, E6E6FA, EE82EE, DA70D6, 8B008B, 800080, 4B0082, 8A2BE2, 9400D3, 9932CC, 9370DB, BA55D3, 7B68EE, 6A5ACD, 483D8B, 191970, 000080, 00008B, 0000CD, 0000FF, 4169E1, 4682B4, 6495ED, 1E90FF, 00BFFF, 87CEFA, 87CEEB, ADD8E6, 00FFFF, 00FFFF, B0E0E6, E0FFFF, A0CE00, F0FFFF, F5FFFA, F0FFF0, 7FFFD4, 40E0D0, AFEEEE, 48D1CC, 00CED1, 66CDAA, 20B2AA, 008B8B, 008080, 5F9EA0, 3CB371, 8FBC8F, 90EE90, 98FB98, 00FA9A, 00FF7F, 00FF00, 32CD32, 2E8B57, 228B22, 008000, 006400, 6B8E23, 556B2F, 808000, BDB76B, 9ACD32, 7FFF00, ADFF2F
kann die Farbe nun auch über folgenden Sprachbefehle eingestellt werden:
Bitte schalte die Lampe im Wohnzimmer an der Couch auf Olivfarbiges Graubraun
Dadurch, das die Liste colors den Wert "Aus" mit dem entsprechenden Wert "000000" in der Liste rgb enthält, kann die Lampe über die gleiche Logik auch ausgeschaltet werden:
Bitte schalte das Licht im Wohnzimmer auf Pflaume und in einer Stunde wieder aus
Abfragen beliebiger Geräteinformationen
Das folgende Beispiel zeigt die Möglichkeit auf, die Antworten für Statusabfragen direkt am abgefragten Gerät zu definieren. Hierfür werden zunächst eine Definition am T2F-Device
# 1 2 3 4 Wie && ?(@properties)&& (@types_info) && ?(@rooms) && ?(@places) = (answer=>'T2F_getInfo("T2F_types_info=.*$2@.*:FILTER=T2F_rooms=.*$3@.*:FILTER=T2F_properties=.*$1@.*:FILTER=T2F_places=.*$4@.*","$2@")')
und eine Funktion in der 99_myUtils
sub T2F_getInfo
{
my ($filter,$info) = @_;
my @devices = devspec2array($filter);
if (@devices > 0 and defined($defs{$devices[0]}))
{
my $answer = '';
foreach (@devices){
if ($answer ne '')
{
$answer = $answer.", ";
}
my $device = $_;
my @attrVals = split(/\n/,AttrVal($device, 'T2F_types_info', ''));
foreach (@attrVals){
my $attrVal = $_;
$attrVal =~ s/ / /g;
$attrVal =~ s/ => /=>/g;
my @attrValParts = split(/=>/,$attrVal);
if (@attrValParts[0] =~ /$info/)
{
my $cmd = "' ".@attrValParts[1]."'";
$cmd =~ s/=/\//g;
$cmd =~ s/\)\(/\/r=~s\//g;
$cmd =~ s/\)/\/r.'/g;
$cmd =~ s/#\(/', '')=~s\//g;
$cmd =~ s/ #/ '.ReadingsVal('$device', '/g;
$cmd =~ s/#/', '').'/g;
$cmd =~ s/T2F_answer/'.T2F_answer('$device', 'T2F_types_switch').'/g;
$answer = $answer.' '.eval($cmd);
}
}
}
return $answer;
}
}
Die Konfiguration und Funktionsweise sind nun wie folgt: Das Gerät, dessen Status abgefragt werden sollen erhält neben den Oben bereits beschriebenen Attributen für Eigenschaft, Raum und Ort noch das Attribut T2F_types_info, welches Zeilenweise die abzufragenen Status in der folgenden Form enthält:
abfragewert => Das ist die Antwort von Gerät T2F_answer mit den Wert #reading#(suchen1=ersetzen1)...(suchenn=ersetzenn)
Mit der Form #reading#(suchen1=ersetzen1)...(suchenn=ersetzenn) können die Werte von Readings des abgefragten Device zum Zeitpunkt der Abfrage ermittelt werden und im Ergebnis Ersetzungen vorgenommen werden (z.B. (on=an)(off=aus)). T2F_answer wird mit der T2F-Konfiguration (siehe oben) ersetzt. Für das Beispiel einer Heizung im Esszimmer wäre das zum Beispiel wie folgt möglich:
attr HeizungEsszimmer T2F_places Heizung,! attr HeizungEsszimmer T2F_rooms Haus,Obergeschoss,Essbereich,!Esszimmer attr HeizungEsszimmer T2F_types_info warm,Temperatur => Die Temperatur T2F_answer beträgt #measured-temp# Grad und soll #desired-temp# Grad erreichen\ modus,Betriebsart => Die Betriebsart T2F_answer ist #controlMode#(auto=Automatik)(manu=Hand)\ Ventilstellung => Das Ventil T2F_answer ist #ValvePosition# Prozent geöffnet
Damit sind dann folgende Abfragen möglich:
Wie warm ist die Heizung im Esszimmer? Wie ist die Temperatur der Heizung im Esszimmer? Wie ist die Ventilstellung der Heizung im Esszimmer? Wie warm sind die Heizungen?
Häufig verwendete RegExp
Perl Regular Expression, also ein regulärer Ausdruck der Programmiersprache Perl, ist eine Werkzeug um Zeichenketten zu beschreiben. Hier wird ein kurzer Einblick auf die im Artikel häufig genutzten RegExp zu geben.
Eine Auflistung der Syntax kann hier eingesehen werden. [1]
RegExp | Beschreibung |
---|---|
\S* | Beliebig viele (*) nicht Leerzeichen (\S). Für unbekannten und unwichtigen Wortendungen z.b. garage\S* ist bei Garage, Garagentor oder Garagentür erfolgreich |
\S+ | Mehr als ein Zeichen (+) welches kein Leerzeichen ist (\S) |
\S\S? | Ein oder zwei Zeichen die keine Leerzeichen sind. Das "?" wirkt hier nur auf das angrenzende "\S". Sollte für Umlaute verwendet werden, da bei manchen Eingabemethoden Probleme mit Umlauten auftreten können. |
(wort )? | Ein bestimmtes Wort oder nicht. |
(wort1|wort2) | Entweder wort1 oder Wort2 |
wort$ | Nur wenn das Wort am ende der Zeichenkette steht. |
(\S+){0,2} | Keins, eins oder zwei Wörter. Kann für Artikel wie z.B. "(in der|in dem|im|auf der|...)?" eingesetzt werden |
Ausgabemethoden
Siehe hierzu Anwendungsbeispiel