Modul Talk2Fhem

Aus FHEMWiki
Wechseln zu: Navigation, Suche
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

Inhaltsverzeichnis

Voraussetzungen

ModulTalk2FhemScreenshot.png

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.

  1. Aufteilen des Sprachbefehls in einzelne Kommandos anhand des Wortes UND
  2. Suchen nach Ereignisansagen und entfernen für die weitere Verarbeitung
  3. Erkennen von Zeit- und Datumsangaben und entfernen für die weitere Verarbeitung
  4. Entfernung überflüssiger Wörter
  5. Vergleich mit den definierten regulären Ausdrücken
  6. Konvertieren in ein FHEM Kommando
  7. Antwortsatz bilden
  8. 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 verschiedene Arten erfolgen.
Die Datumerkennung umfasst folgende Phrasen:

  • morgen, übermorgen
  • in ... Wochen, Monat, Jahr
  • in einem drei-,viertel,halben Jahr
  • nächste Woche, Monat, Jahr
  • Wochentage
  • in ... Tagen
  • am DATUM

Die Zeiterkennung umfasst folgende Phrasen:

  • in,nach ... stunden,minuten,sekunden
  • in einer drei-,viertel,halben Stunde
  • um ... (Uhr) (...)
  • um drei-,viertel,halb,viertel vor/nach, 1-12
  • 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 kombiniert werden. Morgen abend um 8 Uhr
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.

Info green.png Wichtig

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.

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

Info green.png Info

$0 stellt in diesem Zusammenhang eine Besonderheit dar, indem sie den Text der erkannten T2F_origin RegExp enthält.

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
Info green.png Info

Die genaue Definition der Typen lässt sich mit dem Befehl get modificationtypes anzeigen


$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#Raumbezogene_Temperaturansage

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
Info green.png Wichtig

Backslashes "\" und Kommas "," müssen geschützt werden. Das erfolgt über das Quoting. Mit einfachen Anführungszeichen oder doppelten Backslash. Siehe rooms.

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

Da answer immer sofort ausgeführt wird, wird OK direkt gesetzt unabhängig davon ob beim Kommando eine Zeit- oder Eventbedingung mitgegeben wurde. Möchte man eine Antwort zum Zeitpunkt des Ausführens bekommen, oder bei Dingen die kein offensichtliches Feedback erzeugen, ist folgendes eine Möglichkeit.

schalte etwas (\S+) = (
 cmd  => 
   "set etwas $1{ true => on, false => off };
    setreading $NAME answers Etwas wurde $1{true=>eingeschaltet,false=>ausgeschaltet};
    setreading $NAME status answer",
 answer =>  ("OK") )

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 Der Befehl war nicht erfolgreich.);;
  setreading $NAME status answer

Je nach der individuellen Auswertung des Reading "answers" ist die letzte Zeile (Reading status) notwendig oder auch nicht.

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