Twilight Anwendungsbeispiel
Das Modul Twilight errechnet verschiedene Dämmerungsphasen und kalkuliert daraus einen wetter- und dämmerungsabhängigen Lichtwert, der den Grad der Außenhelligkeit angibt. Hier soll an einem Beispiel der Einsatz dieses Moduls zur Steuerung eines Ambient-Lichtes und einer Vitrinenbeleuchtung gezeigt werden.
Ziel: Eine indirekte Beleuchtung, z. B. im Wohnzimmer, soll sich, sobald es zu dämmern anfängt bzw. wetterabhängig schon früh "gefühlt" dunkler wird, eingeschaltet werden. In diesem Beispiel wird eine dimmbare indirekte Beleuchtung (ein LED-Lichtschlauch) verwendet. Die Helligkeit des Lichtschlauchs soll bei nur leichter Dämmerung maximal sein und soll sich dann bei Dunkelheit bis auf 20% reduzieren. Die Vitrinenbeleuchtung (ebenfalls LED) soll ebenfalls nur dann eingeschaltet sein, wenn es nicht taghell ist, da man die da sowieso nicht sieht. Nachts soll die Beleuchtung natürlich nicht an sein, dazu wird hier auf eine Variable "Anwesend" zurückgegriffen, die beim Autor als Indikator für Nacht und Abwesenheitsbetrieb verwendet wird. Dies kann individuell angepasst werden. Anwesend=2 bedeutet "Die Bewohner sind im Bett" und Anwesend=4 bedeutet "Die Bewohner sind abwesend".
Neben der automatischen Beleuchtung soll auch ein manuelles Eingreifen möglich sein. So kann über einen Wandtaster sowohl das AmbientLight als auch die Vitrinenbeleuchtung ein- oder ausgeschaltet werden, wobei die Automatik nach einer bestimmten Zeit automatisch wieder greifen soll. Hier werden ein Homematic-Schaltaktor und ein Homematic-Dimmer verwendet, das Beispiel lässt sich leicht auf andere Komponenten übertragen.
Zur Realisierung wurden einige Perl-Schnipsel in ein externes Modul ausgelagert, weil dies wesentlich übersichtlicher ist als die Inline-Definition in der fhem.cfg. Als Basis kann das Modul 99_Utils.pm genommen werden. Der Autor hat diese Datei kopiert, das Modul umbenannt, und den zusätzlichen Code eingetragen.
Definition des Twilight-Devices
Um überhaupt die Lichtwerte zur Verfügung zu haben, muss zunächst in der fhem.cfg ein Twilight-Device erzeugt werden. Hierzu wird folgender Aufruf verwendet:
Syntax: define <name> Twilight <Längengrad> <Breitengrad> <Indoor_Horizont> <Yahoo-Wetter-ID>
Beispiel für Helgoland:
define T twilight 54.18258 7.885938 1 709403
Hier beispielhaft eine Eingabe direkt per telnet in FHEM auf Port 7072
Code
define T twilight 54.18258 7.885938 1 709403 list T Internals: CFGFN DEF 54.18258 7.885938 1 709403 INDOOR_HORIZON 1 LATITUDE 54.18258 LONGITUDE 7.885938 NAME THelgo NR 751 STATE 6 TYPE Twilight WEATHER 709403 WEATHER_HORIZON 6 Readings: 2012-03-30 08:40:34 light 6 2012-03-30 08:40:34 nextEvent ss_weather 2012-03-30 08:40:34 nextEventTime 19:13:23 2012-03-30 08:40:34 nextUpdate 08:55:34 2012-03-30 08:40:34 sr 07:10:41 2012-03-30 08:40:34 sr_astro 04:57:56 2012-03-30 08:40:34 sr_civil 06:28:52 2012-03-30 08:40:34 sr_indoor 07:17:33 2012-03-30 08:40:34 sr_naut 05:45:13 2012-03-30 08:40:34 sr_weather 07:51:46 2012-03-30 08:40:34 ss 19:54:28 2012-03-30 08:40:34 ss_astro 22:07:13 2012-03-30 08:40:34 ss_civil 20:36:17 2012-03-30 08:40:34 ss_indoor 19:47:36 2012-03-30 08:40:34 ss_naut 21:19:56 2012-03-30 08:40:34 ss_weather 19:13:23
Zu sehen ist hier die Übersicht der entsprechenden Dämmerungszeiten des aktuellen Tages (Details siehe FHEM commandref).
Berechnung des Automatik-Wertes
In folgender Routine wird zunächst abhängig vom Lichtwert und der aktuellen Anwesenheit der Automatikwert für den Lichtschlauch und die Schrankbeleuchtung errechnet. Der Lichtschlauch ist ein Dimmer, erhält also Werte zwischen 0% und 100% und die Vitrine ist ein Schaltaktor, ist also entweder an oder aus.
sub calc_a_schlauch{ my $licht=ReadingsVal("T","light","6"); my $anwesend=ReadingsVal("anwesend","state","4"); if($licht eq 6 || $anwesend eq 2 || $anwesend eq 4){ fhem "set a_schlauch 0" ; fhem "set a_schrank off"; }elsif($licht<6 && $licht>3){ fhem "set a_schlauch 100"; fhem "set a_schrank on"; }elsif($licht>2 && $licht<5){ fhem "set a_schlauch 40"; fhem "set a_schrank on"; }elsif($licht<3){ fhem "set a_schlauch 20"; fhem "set a_schrank on"; } }
Die Ausgangsbedingungen "light" und "anwesend" werden zu Beginn abgefragt. In der darauffolgenden IF-Schleife wird dann je nach Lichtstärke ein Wert für die beiden Automatikwerte gesetzt. Zu beachten ist, dass die Devices a_schlauch und a_schrank Dummy-Devices sind (siehe fhem.cfg unten). Hier wird noch nicht direkt geschaltet sondern nur ein Wert berechnet und zwischengespeichert.
Manuelle Schaltung Vitrine
Zur manuellen Schaltung wird durch das Drücken eines Tasters folgender Schnipsel ausgeführt, der dann den neuen Manuell-Wert berechnet:
sub calc_m_schrank{ my $aktuell = ReadingsVal("m_schrank","state","auto"); my $auto = ReadingsVal("a_schrank","state","off"); if($aktuell eq "off" || ($aktuell eq "auto") && $auto eq "off"){ fhem "set m_schrank on"; }else{ fhem "set m_schrank off"; } }
Die aktuellen Werte für Manuell und Automatik werden zunächst aus den Dummy-Devices abgefragt. Jetzt gibt es zwei Möglichkeiten. Wenn die Vitrine gerade in dem Zustand ist, dass sie schon manuell ausgeschaltet wurde (also $aktuell eq "off"), dann muss sie jetzt eingeschaltet werden. Dies muss auch dann passieren, wenn man gerade im Automatikmodus ist, und eben dieser gerade auf "off" steht. Dies ist wichtig, damit für den Anwender, dem nicht klar ist, in welcher Betriebsart die Beleuchtung gerade ist, bei jedem Tasterdruck auch ein sichtbarer Schaltvorgang ausgelöst wird.
Manuelle Schaltung LED-Schlauch
Die manuelle Schaltung des LED-Schlauchs ist noch etwas aufwendiger, da mehrere Dimmerstufen angefahren werden sollen:
sub calc_m_schlauch{ my $aktuell = ReadingsVal("m_schlauch","state",0); my $auto = ReadingsVal("a_schlauch","state",0); if($aktuell==0 || ($aktuell==-1 && $auto==0)){ fhem "set m_schlauch 100"; }elsif($aktuell==100){ fhem "set m_schlauch 30"; }elsif($aktuell==30 || ($aktuell==-1 && $auto>0)){ fhem "set m_schlauch 0"; }else{ fhem "set m_schlauch 0"; } }
Das Prinzip ist wie bei der Vitrine. Da hier mit numerischen Werten gearbeitet wird, ist die Bedeutung "Automatik" nicht als "auto" definiert sondern als Zahlenwert "-1". D.h. der Wert für "m_schlauch" kann zwischen -1 und 100 schwanken, wobei im Bereich 0-100 ein manueller Wert gesetzt wird und bei "-1" die Automatik greifen soll.
In der IF-Abfrage wird zunächst der Fall behandelt, dass das Licht komplett aus ist. Dies ist entweder der Fall, wenn es manuell aus ist oder die Automatik greift und diese aber auch auf "0" ist. Der zweite Zweig schaltet den manuellen Wert auf 30 runter, wenn dieser gerade auf 100 ist. In allen anderen Fällen wird das Licht im nächsten Schritt manuell ausgeschaltet.
Schalten der Aktoren
Letztlich müssen natürlich die Aktoren tatsächlich geschaltet werden. Dazu wird folgende "Treiber"-Routine genutzt, die die manuellen und automatischen Werte logisch kombiniert:
sub drive_schrank_schlauch{ my $dimmer=dimvalue(ReadingsVal("dim_schlauch","state",0)); my $man=ReadingsVal("m_schlauch","state",0); my $auto=ReadingsVal("a_schlauch","state",0); my $newvalue; my $zeit=60; if($man==-1){ $newvalue=$auto; }else{ $newvalue=$man; $zeit=5; } if($dimmer ne $newvalue){ fhem "set dim_schlauch ".$newvalue." 84000 ".$zeit; } my $schrank=ReadingsVal("akt_schrank","state","off"); $man=ReadingsVal("m_schrank","state","auto"); $auto=ReadingsVal("a_schrank","state","off"); if($man eq "auto"){ $newvalue=$auto; }else{ $newvalue=$man; } if($schrank ne $newvalue){ fhem "set akt_schrank ".$newvalue; } }
Hier wird nun zuerst der LED-Schlauch und dann der Schrank behandelt. Zunächst wird sowohl der aktuelle Wert des tatsächlichen Aktors (hier: dim_schlauch) als auch die Manuell- und Automatik-Werte abgefragt. Die Variable $newvalue soll den neuen Schaltwert erhalten. Dies wird deswegen eingesetzt, um einen Schaltbefehl nur dann über Funk zu senden, wenn sich der Zustand geändert hat. Die Variable $zeit wird hier genutzt, um beim automatischen Schalten den Dimmer sehr langsam fahren zu lassen, so dass es zu keiner abrupten merklichen Helligkeitsänderung kommt. Dies geht nur mit Homematic-Dimmern.
In der folgenden IF-Abfrage wird geklärt, ob der Dimmer im manuellen oder automatischen Modus betrieben wird. Dementsprechend wird $newvalue gesetzt. Bei manuellem Betrieb wird die Zeit auf 5 Sekunden runtergesetzt, denn hier soll eine merkliche Helligkeitsänderung in kurzer Zeit nach dem Betätigen des Tasters erfolgen. Stellt sich dann in der kommenden Abfrage heraus, dass der aktuelle Wert des Aktors noch nicht dem von $newvalue entspricht, wird ein tatsächlicher Schaltvorgang ausgelöst. Die Zahl 84000 ist die Einschaltzeit des Dimmers (unwichtig hier, muss nur hoch genug sein, ist aus Sicherheitsgründen nicht auf 0=unendlich - so geht der Dimmer auch bei einem FHEM-Ausfall nach knapp 24 Stunden aus). Im weiteren Verlauf wird die Vitrine nach dem gleichen Prinzip geschaltet, wobei hier keine Zeit sinnvoll ist.
fhem.cfg
In der fhem.cfg müssen, damit die Schnipsel funktionieren, neben dem Twilight-Device auch die Dummy-Devices, die beiden Aktoren, und entsprechende At-/Notify-Defines eingetragen werden, die dafür sorgen, dass die Code-Schnipsel ausgeführt werden. Die Definition der beiden Aktoren ist hier nicht aufgeführt und muss individuell eingetragen werden.
define a_schlauch dummy define m_schlauch dummy define a_schrank dummy define m_schrank dummy define T twilight 54.18258 7.885938 1 709403 set m_schlauch -1 set m_schrank auto define m_a_schlauch notify m_a_schlauch {calc_a_schlauch();;} define n_abwesend_ambient notify anwesend {fhem "trigger m_a_schlauch";;} define n_lightchange_ambient notify T:light.* {fhem "trigger m_a_schlauch";;} define n_d_schlauch notify a_schlauch {drive_schrank_schlauch();;} define n_d_schlauch2 notify m_schlauch {drive_schrank_schlauch();;} define n_d_schrank notify a_schrank {drive_schrank_schlauch();;} define n_d_schrank2 notify m_schrank {drive_schrank_schlauch();;} define n_m_schlauch_reset notify m_schlauch {fhem "delete temp_at_m_schlauch_reset";;fhem "define temp_at_m_schlauch_reset at +01:00:00 set m_schlauch -1";;} define n_m_schrank_reset notify m_schrank {fhem "delete temp_at_m_schrank_reset";;fhem "define temp_at_m_schrank_reset at +01:00:00 set m_schrank auto";;} define n_m_schlauch notify taster_wohn:Btn1.off[^L]* {calc_m_schlauch();;} define n_m_schrank notify taster_wohn:Btn2.on[^L]* {calc_m_schrank();;}
m_a_schlauch
ist ein Makro, was hier überflüssig erscheint, aber sinnvoll ist, wenn man einen Perl-Aufruf von verschiedenen Stellen aus antriggern will, so ist man unabängig vom tatsächlichen Inhalt.
Die Berechnung wird also immer dann angetriggert, wenn entweder der Anwesenheitsstatus oder der Lichtwert sich verändert haben. Die folgenden Notifys reagieren auf Werteänderungen der Automatik- und Manuell-Dummys. Die nächsten beiden Notifys (n_m_schlauch_reset
und n_m_schrank_reset
) sorgen dafür, dass bei einer Änderung der Manuell-Werte durch das Drücken von Tastern, etc. der Wert nach einer Stunde wieder auf Automatik zurückfällt. Die letzten beiden Notifys reagieren auf genau diese Taster, wobei hier ein Homematic-Wandtaster eingesetzt wird, bei dem nur auf einen kurzen Tastendruck reagiert werden soll.
Der bisherige Code sieht nicht vor, mit den Tastern auch frühzeitig wieder auf Automatik zurückzuschalten. Das kann z. B. mit einem zusätzlichen Taster gemacht werden:
define n_tEingang_lichtReset notify taster_eingang:Btn1.offLong.* {lichtReset();;}
Hier wird auf den langen Tastendruck eines anderen Tasters reagiert, wobei dieser Notify zündet, sobald man den Taster länger als 0,4 Sekunden festhält, also noch bevor dieser losgelassen wird. Der Code von lichtReset kann dann mehr tun, als nur die beiden hier beschriebenen Beleuchtungen auf Automatik zu setzen. Beispiel:
sub lichtReset{ fhem "set m_schlauch -1"; fhem "set m_schrank auto"; fhem "set akt_treppe off"; fhem "set akt_eingang off"; fhem "set akt_esszimmer off"; }
yahoo - Condition Codes
Condition code Beschreibung Internes Mapping in: sub Twilight_getWeatherHorizon 0 tornado 25 1 tropical storm 25 2 hurricane 25 3 severe thunderstorms 25 4 thunderstorms 20 5 mixed rain and snow 10 6 mixed rain and sleet 10 7 mixed snow and sleet 10 8 freezing drizzle 10 9 drizzle 10 10 freezing rain 10 11 showers 7 12 showers 7 13 snow flurries 7 14 light snow showers 5 15 blowing snow 10 16 snow 10 17 hail 6 18 sleet 6 19 dust 6 20 foggy 10 21 haze 6 22 smoky 6 23 blustery 6 24 windy 6 25 cold 6 26 cloudy 6 27 mostly cloudy (night) 5 28 mostly cloudy (day) 5 29 partly cloudy (night) 3 30 partly cloudy (day) 3 31 clear (night) 0 32 sunny 0 33 fair (night) 0 34 fair (day) 0 35 mixed rain and hail 7 36 hot 0 37 isolated thunderstorms 15 38 scattered thunderstorms 15 39 scattered thunderstorms 15 40 scattered showers 9 41 heavy snow 15 42 scattered snow showers 8 43 heavy snow 5 44 partly cloudy 12 45 thundershowers 6 46 snow showers 8 47 isolated thundershowers 8 not available 1
Zusammenhang STATE und light
STATE wird beim Twilight-Modul von 0 - 11 durchgezählt.
0 -> vor astronomischen Aufgang, 1 -> vor nautischem Aufgang, 2 -> vor zivilem Aufgang, 3 -> vor Sonnenaufgang, 4 -> vor Indoor-Aufgang, 5 -> vor "Wetter-Aufgang", 6 -> vor "Wetter-Untergang" (also den meisten Tag lang)
Bis hierher ist light = STATE. Von nun an wird light wieder weniger (es wird ja dunkler) aber STATE schreitet vor, um Sonnenuntergänge von -aufgängen unterscheidbar zu machen.
7 -> vor Indoor-Untergang, 8 -> vor Sonnenuntergang, 9 -> vor zivilem Untergang, 10 -> vor nautischem Untergang, 11 -> vor astronomischem Untergang
Bitte auch bedenken, dass in der Nordhälfte Deutschlands die Sonne astronomisch im Sommer ca. 6 Wochen lang nicht untergeht, State wird also nicht alle Werte durchlaufen und light wird nie 0 sein.