Mittwoch, 18. Juli 2018

Kalender in FHEM einbinden

Die älter Bereitstellung des Link habe ich bereits hier gezeigt. In der neuen Form des Google Kalenders gibt es den grünen iCal Button nicht mehr, der Link steht jetzt unter Einstellungen/<Kalendername>/Kalendereinstellungen und dort weit unten in der Box:
Privatadresse im iCal Format
Darunter steht der wichtige Hinweis:
Mit dieser Adresse können Sie von anderen Anwendungen aus auf den Kalender zugreifen, ohne ihn öffentlich zu machen.

Das Kalender Modul in FHEM ist aktuell überarbeitet und die Funktionen sind erweitert und geändert worden. Mittlerweile weiß ich, dass es keine gute Lösung ist ein Modul einzusetzen, welches auf einem anderen Modul aufsetzt aber völlig getrennt von dem entwickelt wird. Man schafft unnötig Abhängigkeiten.
Bei der Einbindung eines Kalenders in FHEM sollte man sich über den praktischen eigenen Umgang mit dem Kalender und dem Aktualisierungsintervall (Standard 1 h) Gedanken machen. Ein Abfallkalender der sich das ganze Jahr praktisch nicht ändert muss entweder nie oder höchsten 1 mal Tag aktualisiert werden.

1. Beispiel: 

Signalisierung der Abfalltonne, Aktualisierung einmal am Tag
define AbfallKalender Calendar ical url https://... 86400
Dann brauchen wir ein Gerät wo die aktuelle Tonne drin steht, im einfachsten Fall ein Dummy
define Tonne dummy
Und ein Timer der einmal am Tag den Kalender ausliest und das Ergebnis ablegt.
define a_Tonne at *12:00:00 set Tonne {(my $evt=fhem('get AbfallKalender events format:custom="$S" limit:from=1d,to=1d');;;;$evt?$evt:"0")}
Die Funktion:
  • Am Mittag den Ganztagestermin für morgen auslesen -> limit:from=1d,to=1d
  • Nur den Textinhalt des Eintrages lesen -> format:custom="$S"
  • $evt?$evt:"0" -> Falls kein Termin gefunden wird, wird der dann leere String durch eine 0 ersetzt.
  • Den resultierenden Text in den Dummy Tonne schreiben.
Damit kann man am Vortag des Termines ein Aktion auslösen: z.B. Nachmittag den Hinweis geben: die Tonne muss morgen raus. Und am Morgen des Termines kann man nochmal den Hinweis geben: die Tonne muss heute raus. Der Zeitpunkt im Timer entscheidet über das Auftauchen und Verschwinden des Inhaltes von der "Tonne".
Hinweis:
Bei Perl Code innerhalb von einem at Kommando müssen die ; verdoppelt werden, im define müssen sie auch verdoppelt werden, deshalb ;;;; !
Bei Perl Code im set Befehl muss eine zusätzliche Klammer () stehen, sonst wird der Perlausdruck als String gesehen: set Tonne {3+5} ergibt {3+5} im Dummy, set Tonne {(3+5)} ergibt 8 im Dummy.

2. Beispiel: 

Im Kalender stehen FHEM Device Namen (z.B. Aktoren), die Terminzeiten sind die Schaltzeiten.
Das Beispiel gilt für Einzeltermine (eigene uid). Serientermine haben eine uid für alle Termine, hier muss man weiter filtern(limit:from ...).
Der Kalender mit stündlicher Aktualisierung:
define TestKalender Calendar ical url https://... 
Dann wiederum ein Dummy:
define Urlaub dummy
Und ein notify, welches exakt zu Beginn (start) und Ende (end) des Einzeltermines den Dummy auf on oder off setzt. (Code für die Raw Def)
define n_TestKalender notify TestKalender:changed:.* {\
  my $cmd ='on';;\
  $cmd = 'off' if ($EVTPART2 eq 'end');;\
  my $actor = fhem('get '.$NAME.' events format:custom="$S" filter:uid=="'.$EVTPART1.'" limit:count=1');;\
  fhem("set $actor $cmd");;\
}

Funktion
Das Calendar Modul wirft zum Zeitpunkt des Termines ein paar Events, davon spricht das notify genau auf diese Beiden an:
2018-07-16 16:25:00 Calendar TestKalender changed: 123456googlecom start
...
2018-07-16 16:30:00 Calendar TestKalender changed: 123456googlecom end
Der Event hat drei Teile:
  • $EVTPART0 ist uninteressant, 
  • $EVTPART1 enthält die ID des Eintrages und 
  • $EVTPART2 "start" bzw. "end".

Mit Hilfe der ID wird der Kalendertext gelesen und mit den umgewandelten "on" (start) und "off" (end) Befehlen das Device in FHEM gesetzt.
Warum sieht der Befehl so kompliziert aus?

  • Der uid/format String muss im String die doppelten Anführungszeichen enthalten! Damit dies funktioniert, muss der gesamte Befehl in ' ' gesetzt werden. Innerhalb werden dann zwar " akzeptiert aber keine Variablen mehr aufgelöst. Diese muss man hier mit Verkettung/concatenation einbauen.

Weiter unten habe ich eine (für den Anfänger) besser lesbare Variante eingebaut.

Komplexen Code auslagern

Für komplexeren Code sollte man alles in die 99_myUtils auslagern und im notify lediglich die wichtigen Parameter übergeben:
define n_TestKalender notify TestKalender:changed:.* {KalenderSub($EVTPART1,$EVTPART2,$NAME)} 
Das folgende Beispiel filtert das Ereignis (Devicenamen) "Urlaub" aus dem Kalender und setzt das gleichlautendes Device bei Start und Ende auf ja /nein. Alle anderen Events werden zwar getriggert aber verworfen.
Die sub kann man beliebig komplex gestaltet. Damit der get Befehl lesbar bleibt, habe ich alles in extra Variablen gepackt. (Code für die 99_myUtils.pm)
sub KalenderSub ($$$)
   {
     my ($uid,$cmd,$cname) = @_;
     #Logging der Paramter bei Bedarf;     #Log 1, "uid: $uid | Start/End: $cmd | cal: $cname";
     my $format = '"$S"';
     my $dev = 'Urlaub';
     $uid='"'.$uid.'"';
     $cmd = 'ja' if ($cmd eq 'start');
     $cmd = 'nein' if ($cmd eq 'end');
     
     my $actor = fhem("get $cname events format:custom=$format filter:uid==$uid limit:count=1");
     # Unterschiedliche Abfragen auf ist exakt oder enthält möglich;
     #fhem("set $actor $cmd") if ($actor eq $dev);
     if ($actor =~ /$dev/) {fhem("set $dev $cmd")};
}

Tipp

Der Calendar Aufruf wird mit Level 3 geloggt, wenn man das nicht möchte hängt man an den Befehlsaufruf einfach noch ",1" an.
fhem("Befehl",1)

Weitere Infos:
Forum
Wiki

1 Kommentar: