Seite auswählen
Universelles Astro/Zeit-Skript für Rolladen, Lampen …

Universelles Astro/Zeit-Skript für Rolladen, Lampen …

Im Homematic Zeitmodul fehlen eine Reihe wichtiger Zeitfunktionen wie Datumsbereiche, Monate, Wochentage und Kalenderwochen. Aber besonders die Astrofunktion ist unbefriedigend, weil man nur selten genau zum Sonnenaufgang(SA) oder Sonnenuntergang(SU) etwas schalten möchte. Meist benötigt man nämlich Schaltvorgänge vor oder nach diesem Sonnenereignis.

Um diesen Mangel „auszubügeln“ , gibt es in den einschlägigen  Foren eine Vielzahl von mehr oder weniger gelungener Hilfslösungen, die aber meist nur auf spezielle Fragestellungen abgestimmt wurden. Mit diesem universell einsetzbaren Astro/Zeitskript werden verschiedene Systemvariablen berechnet, welche die verschiedenen Zeitinformationen abbilden. Die gewählte Benamung der Systemvariablen erklären selbstredend die Funktion.

Und das geht so:

1. Folgende Systemvariablen anlegen:

Minuten_vor_SA als Zahl , Wertebereich -1000 bis +1000
Minuten_vor_SU als Zahl , Wertebereich -1000 bis +1000
Tageszeit_hh.mm als Zahl in h
Datum_MM.TT als Zahl
Monat als Werteliste Januar;Februar; …
Monatstag als Werteliste 1;2;3; … ;31
Wochennummer als Zahl
Woche_gerade logisch wahr ist gerade , unwahr ist ungerade
Wochentag als Zahl
Jahrestag als Zahl
Jahr als Zahl

2. WebUI Programm anlegen, in dem beispielsweise alle 3min das folgende Astro/Zeit-Skript aufgerufen wird:

astro_skript1
Das Skript dazu verwendet nur eine (!) Skriptvariable, was für eine gute „Skripthygiene“ wichtig ist.

 

!Skript zur Berechnung der Minuten vor SA und SU        !  
!stall.biz  04.04.2016  Dieses Skript verwendet nur eine (!) Skript-Variable     
!folgende Systemvariablen anlegen
!Minuten_vor_SA"    als Zahl , Wertebereich  -1000 bis +1000
!Minuten_vor_SU"    als Zahl , Wertebereich  -1000 bis +1000
!"Tageszeit_hh.mm"  als Zahl  in h
!"Datum_MM.TT"    als Zahl  
!"Monat"    als Werteliste Januar;Februar;  ...  
!"Monatstag"    als Zahl 
!"Wochennummer"    als Zahl  
!"Woche_gerade"    logisch   wahr ist gerade , unwahr ist ungerade
!"Wochentag"    als Zahl  
!"Jahrestag" als Zahl
!"Jahr" als Zahl 
 
real c_zeit =(system.Date("%M").ToFloat()+ (60.0*system.Date("%H").ToFloat())); 
 
if (c_zeit >720.0) { dom.GetObject("Minuten_vor_SA").State(999); 
  c_zeit = system.SunsetTime("%M").ToFloat() + (60.0*system.SunsetTime("%H").ToFloat());
  c_zeit = c_zeit -  (system.Date("%M").ToFloat()+ (60.0*system.Date("%H").ToFloat()));
  dom.GetObject("Minuten_vor_SU").State(c_zeit); 
}
else {dom.GetObject("Minuten_vor_SU").State(999); 
  c_zeit = system.SunriseTime("%M").ToFloat() + (60.0*system.SunriseTime("%H").ToFloat());
  c_zeit = c_zeit - (system.Date("%M").ToFloat()+ (60.0*system.Date("%H").ToFloat()));
  dom.GetObject("Minuten_vor_SA").State(c_zeit); 
}
 
c_zeit = (1.0*system.Date("%H").ToInteger()) +(0.01*system.Date("%M").ToInteger());                      
dom.GetObject("Tageszeit_hh.mm").State(c_zeit);
 
c_zeit  = (0.01 * system.Date("%d").ToInteger()) + system.Date("%m").ToInteger();
dom.GetObject("Datum_MM.TT").State(c_zeit );
 
c_zeit   = system.Date("%Y").ToInteger();
dom.GetObject("Jahr").State(c_zeit);
 
 
c_zeit   = system.Date("%m").ToInteger();
dom.GetObject("Monat").State(c_zeit  -1);
 
c_zeit    = system.Date("%d").ToInteger();
dom.GetObject("Monatstag").State(c_zeit );
 
c_zeit =  system.Date("%V").ToInteger();
dom.GetObject("Wochennummer").State(c_zeit);
 
if ((c_zeit - 2*(c_zeit/2.0)) == 1) {dom.GetObject("Woche_gerade").State(0);} else {dom.GetObject("Woche_gerade").State(1);}
 
c_zeit = system.Date("%u").ToInteger();
dom.GetObject("Wochentag").State( c_zeit);
 
c_zeit = system.Date("%j").ToInteger();
dom.GetObject("Jahrestag").State( c_zeit);

3. Wer CuxD installiert hat, der kann den manchmal instabilen regelmäßigen Zeitaufruf des Astro/Zeit-Skripts mit einem CuxD-Timer realisieren. Wie das geht, ist an verschiedenen Stellen im Homematic-Forum bereits beschrieben, so daß hier auf eine weitere Erklärung verzichtet wird. Das Programm zum regelmässigen Aufruf des Astro/Zeit-Skriptes sieht mit einem CuxD-Timer dann so aus:

astro_skript5
Der Vorteil bei diesem Verfahren ist, daß fast die gesamten Zeitsteuerungen der WebUI-Programme nun über die neuen Astro/Zeit-Systemvariablen erfolgen kann. Und diese Systemvariablen werden nur über einen(!) viel zuverlässigeren CuxD-Timer aktualisiert. Damit wird das gesamte Zeitmanagement deutlich stabiler und zuverlässiger.

4. So wird das Astro/Zeit-Skript eingesetzt
Zum Beispiel eine Rollade so steuern, daß sie morgens 30min vor Sonnenaufgang hoch geht, aber nicht vor 6h45 und abends 45min nach Sonnenuntergang runter fährt,aber nicht später als 22h00.

Das WebUI -Programm dazu ist mit Verwendung der soeben definierten Systemvariablen ganz einfach:

astro_skript4
…und mit der Verwendung der Systemvariablen aus der Astro/Zeitsteuerung sind die WebUI-Programm viel besser lesbar, als mit dem „verkorksten“Zeitmodul!

Einige User hatten das Problem, daß bei dem obigen Programm der Rolladenaktor jedesmal „klickt“, wenn die Systemvariable Minuten_vor_SA alle 2 Minuten aktualisiert wird. Das kann man verhindern mit folgendem geänderten Programm:

rollladen_astro

 

Hier noch einige Erläuterungen zur Astrofunktion:
Da die meisten Steuerungsaufgaben sehr oft mit dem Sonnenaufgang und -untergang verknüpft ist, hat man die sog. Astrofunktion eingeführt. Diese ist aber oft unzureichend, weil man oft lieber einige Minuten vorher oder nachher den Schaltvorgang ausführen möchte. Dazu wurden in dem Astro/Zeit-Skript die zwei Systemvariablen Minuten_vor_SA und Minuten_vor_SU eingeführt. Der Wert dieser Variablen ändert sich über den Tagesverlauf entsprechend dem folgenden Bild:


Die Systemvariable Minuten_vor_SA ist nur von 00h00 bis 12h00 aktiviert und wird im zweiten Tagesabschnitt auf einen Wert von 999 gesetzt.
Bei der Systemvariablen Minuten_vor_SU ist es genau umgekehrt. Diese Definition hat Vorteile bei Anwendung in WebUI -Programmen.

Viel Spaß mit dem neuen „Zeitgefühl“

Wunderground Wetter mit einfachem HM-Skript holen

Wunderground Wetter mit einfachem HM-Skript holen

In einem anderen Blog wurde sehr ausführlich und gut beschrieben, wie man die Wetterdaten von wunderground.com in die Homematic bekommt.

http://homematic-forum.de/forum/viewtopic.php?f=31&t=10071&hilit=wunderground
Allerdings verwendet diese Lösung ein TCL-Skript, was insbesondere für den Anfänger nur mit Aufwand implementierbar ist und dementsprechend Fehlermöglichkeiten hat.Hier wird ein „Kochrezept“ für die Implementierung eines wunderground.com-Wetterdatenabrufes beschrieben, der nur mit einem HM-Skript arbeitet:
1  API-Key von Wunderground besorgen wie unter o.a. Link beschrieben
2  Insgesamt 14 Systemvariable entsprechend der Vorgaben im nachfolgenden Skript sorgfältig ohne Schreibfehler (!) anlegen
(schon bei einem kleinsten Schreibfehler oder fehlender Systemvariable läuft das Skript nicht durch!)
3  Im nachfolgenden Skript den API-Key in die URL-Zeile eintragen und vorher die Funktion der URL ausprobieren
(im Browser aufrufen und dann erhält man den aktuellen XML-Wetterfile im Browser angezeigt!).
4  Ein einfaches WebUI-Programm erstellen, das auf Tastendruck oder alle 30min das nachfolgende Skript aufruft.Fertig!
Hinweis: Das Skript benutzt CuxD-zum Aufruf des Wunderground-Servers. Wenn ein entsprecchendes CUxD-Gerät vorher nicht installiert wurde, dann kann man auch die in der CCU standardmässig verfügbare Abfrage mit system.Exec verwenden. Dazu im Skript die entsprecheenden CUxD-Zeilen mit Rufzeichen auskommentieren und im Gegenzug bei den  system-Exec-Zeilen die Ausrufezeichen entfernen.
.
HM-Skript   
!Stand 03.04.2014  http://homematic-forum.de/forum/viewtopic.php?f=31&t=17209
        !zuerst folgende Systemvariablen anlegen
        !Achtung: keine vergessen und exakte Schreibweise mit Drag&Drop
        !W_Station                        Zeichenkette
        !W_Aktualisierung             Zeichenkette
        !W_Bedingungen               Zeichenkette
        !W_Temperatur                  Zahl                     °C
        !W_Luftfeuchte                  Zahl                      %
        !W_Windbedingungen       Zeichenkette
        !W_Windrichtung               Zeichenkette
        !W_Windrichtg                   Zahl                       °
        !W_Windgeschwindigkeit   Zahl                     km/h
        !W_Windboeen                  Zahl                     km/h
        !W_Luftdruck                      Zahl                      mb
        !W_Luftdrucktrend             Zeichenkette
        !W_Taupunkt                      Zahl                     °C
        !W_UV                                Zeichenkette
 
        var url = "http://api.wunderground.com/api/<api-key>/conditions/lang:DL/q/Germany/Neuwied.xml";
 
        !hier ist die Abfrage mit CUxD
        dom.GetObject("CUxD.CUX2801001:1.CMD_SETS").State("wget -q -O - '"#url#"'");
        dom.GetObject("CUxD.CUX2801001:1.CMD_QUERY_RET").State(1);
        string wetter_xml = dom.GetObject("CUxD.CUX2801001:1.CMD_RETS").State();
 
        !hier ist die Abfrage mit system.Exec
        !string stdout;
        !string stderr;
        !system.Exec("wget -q -O - '"#url#"'", &stdout, &stderr);
        !WriteLine(stdout);
        !string wetter_xml = stdout;
        !WriteLine(wetter_xml);
 
        !Beim XML-File den ueberfluessigen Header entfernen
        integer laenge = wetter_xml.Length();
        integer wort_position = wetter_xml.Find("display_location");
        wetter_xml = wetter_xml.Substr(wort_position, (laenge - wort_position));
        !WriteLine(wetter_xml);
 
        !Daten mit Suchworten aus XML-File ausfiltern:
 
        !string word = "full";
        string word = "city";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        dom.GetObject("W_Station").State(daten);
 
        !string word = "observation_time";
        string word = "observation_time_rfc822";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        !daten = daten.Substr(0, (word_position -2));
        daten = daten.Substr(0, (word_position -11));
        dom.GetObject("W_Aktualisierung").State(daten);
 
        string word = "weather";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        dom.GetObject("W_Bedingungen").State(daten);
 
        string word = "temp_c";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        real zahl = daten.ToFloat();
        dom.GetObject("W_Temperatur").State(zahl);
 
        string word = "relative_humidity";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        integer zahl = daten.ToFloat();
        dom.GetObject("W_Luftfeuchte").State(zahl);
 
        string word = "wind_string";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        dom.GetObject("W_Windbedingungen").State(daten);
 
        string word = "wind_dir";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        word_laenge =daten.Length();
        string anfangsbuchstabe = daten.Substr(0,1);
        ! Umlaute korrigieren
        !
        ! N # Nord ***
        if (anfangsbuchstabe == "N") {
           !
            if (daten == "Nordwest") {daten = "Nord-West" ;}
        }
 
        ! S # Süd ***
        if (anfangsbuchstabe == "S") {
           ! 4 # Süd
           if (word_laenge == 4)  {daten = "Süd";}
           ! 8 # Südwest
           if (word_laenge == 8)  {daten = "Süd-West";}
           ! 12 # Süd-Südost
           if (word_laenge == 12) {daten = "Süd-Süd-Ost" ;}
             ! 13
           if (word_laenge == 13) {daten = "Süd-Süd-West" ;}
        }
 
        ! W # Westen
        if (anfangsbuchstabe == "W") {
           ! 13 # West-Südwest
            if (word_laenge == 13) {daten = "West-Süd-West" ;}
        }
 
        ! O # Osten
        if (anfangsbuchstabe == "O") {
           ! 11 # Ost-Südost
           if (word_laenge == 11) {daten = "Ost-Süd-Ost" ;}
        }
        dom.GetObject("W_Windrichtung").State(daten);
        !WriteLine(daten);
 
 
        string word = "wind_degrees";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        integer zahl = daten.ToFloat();
        dom.GetObject("W_Windrichtg").State(zahl);
 
        string word = "wind_kph";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        integer zahl = daten.ToFloat();
        dom.GetObject("W_Windgeschwindigkeit").State(zahl);
 
        string word = "wind_gust_kph";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        integer zahl = daten.ToFloat();
        dom.GetObject("W_Windboeen").State(zahl);
 
        string word = "pressure_mb";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        integer zahl = daten.ToFloat();
        dom.GetObject("W_Luftdruck").State(zahl);
 
        string word = "pressure_trend";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        dom.GetObject("W_Luftdrucktrend").State(daten);
 
        string word = "dewpoint_c";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        real zahl = daten.ToFloat();
        dom.GetObject("W_Taupunkt").State(zahl);
 
        string word = "UV";
        integer word_laenge = word.Length();
        integer word_position = wetter_xml.Find(word);
        string daten = wetter_xml.Substr((word_position + word_laenge +1), 100);
        integer word_position = daten.Find(word);
        daten = daten.Substr(0, (word_position -2));
        real zahl = daten.ToFloat();
        dom.GetObject("W_UV").State(zahl);

Changelog: 03.04.2014 Umlaute Himmelsrichtungen korrigiert, UV-Kennwert zugefügt.

Sonnenstandsberechnung für die intelligente Betätigung von Rolladen, Markisen und  Lampen

Sonnenstandsberechnung für die intelligente Betätigung von Rolladen, Markisen und Lampen

Update 07.04.2016: Alternatives Skript mit weniger Variablennamen

Grundlagen

Die Sonnenposition wird beschrieben mit der Sonnen_Elevation (Winkel zur Horizontalen) und dem Sonnen_Azimut (Winkel in der Horizontalen, von Norden aus gemessen wie bei der Kompassrose). Beide Grössen sind unentbehrlich für eine jahreszeitabhängige Betätigung von Rolladen für die Nacht, Rolladen für die Beschattung , Jalousien mit variablem Lamellenwinkel, Markisen und letztlich auch für die dämmerungsabhängige Beleuchtung. Professionelle Systeme zur Hausautomation stellen diese Funktion oft nur optional mit Aufpreis zur Verfügung !
Zur Sonnenstandsberechnung hatte ich bereits ein Skript erstellt, das mit mehreren linearen Interpolationsverfahren die Sonnenposition mehr oder weniger genau berechnet. http://homematic-forum.de/forum/viewtopic.php?f=31&t=17300#p140282 Theorie dazu gibt´s hier: http://de.wikipedia.org/wiki/Sonnenstand
Im Hinblick auf die weitere Verbesserung der Berechnung wird hier nun ein umfangreicheres HM-Skript beschrieben, das die Sonnenposition relativ genau für die gesamten 24h eines Tages berechnet. Durch die Verfügbarkeit brauchbarer Daten der Sonnenposition auch in der Nacht (wenn die Sonne bereits unter dem Horizont ist, Elevation negativ), können auch nachtabhängige Steuerungsaufgaben stufenlos und dämmerungsabhängig einfach gelöst werden. Zeitsprünge durch Umstellung von Sommer- auf Winterzeit entfallen durch diese Steuerungsart vollständig.

Hier das Kochrezept

1.) Sicherstellen, dass in der HM-Zeit- und Positionseinstellung auch der richtige Breitengrad des eigenen Standortes (hier hilft Google Earth !) und die richtige Zeitzoneneinstellung für Deutschland „CET/CEST (UTC +1/+2)“ eingetragen sind.
2.) Zwei Systemvariablen anlegen_
sonne_elevation mit Variablentyp Zahl von -180 bis +180 grad
sonne_azimut mit Variablentyp Zahl von-360 bis +360 grad
3.) Ein einfaches WebUI-Programm anlegen, in dem man beispielsweise all 4 Minuten das folgende Skript aufruft:
HM-Skript   
!berechg sonne_elevation, sonne_azimut; stand 14.03.2014
!Verfasser: funkleuchtturm
real phi = system.Latitude(); !Breitengrad holen
phi = 0.017453292 * phi; !umwandeln in bogenmass
!####### sin_phi und cos_phi mit taylorreihe berechnen
real temp = phi * phi;
real sin_phi =phi * ((temp * temp * 0.0083334) +1.0 - (temp * 0.1666667)); !sinus-naeherung
real cos_phi = (temp *temp *0.0416667)  + 1.0 - (temp * 0.5); !cosinus-naeherung
!### berechnung sonnenzeit, alle zeiten in minuten ########
integer zeit_min = system.Date("%M").ToInteger() + 60*system.Date("%H").ToInteger();
integer tagesbeginn_min = system.SunriseTime("%M").ToInteger() + 60*system.SunriseTime("%H").ToInteger();
integer tagesende_min = system.SunsetTime("%M").ToInteger() + 60* system.SunsetTime("%H").ToInteger();
integer sonnenzeit =zeit_min + 720 - 0.5 *(tagesbeginn_min +tagesende_min);
if (sonnenzeit > 1440) {sonnenzeit = sonnenzeit -1440;}
if (sonnenzeit < 1) {sonnenzeit = 1440 + sonnenzeit;}
boolean nachmittag =false;
if (sonnenzeit > 720) {sonnenzeit =sonnenzeit - 720; nachmittag = true; }
else {sonnenzeit =720 -sonnenzeit;}
!##### berechnung sin_tau und cos_tau ############
real tau = 0.00436332313 * sonnenzeit;   ! 15/60  * pi /180 * sonnenzeit  [0 < tau < pi ]
if (tau < 1.570796327)
{temp = tau * tau;
real sin_tau =tau * ((temp * temp *  0.0083334) +1.0 - (temp *0.1666667));
tau= 1.570796327 - tau;
temp = tau * tau;
real cos_tau =tau * ((temp * temp *  0.0083334) +1.0 - (temp * 0.1666667));}
else
{real tau1  =3.141592654 - tau;
temp = tau1 * tau1;
real sin_tau =tau1 * ((temp * temp *  0.0083334) +1.0 - (temp * 0.1666667));
tau = tau  -  1.570796327;
temp = tau * tau;
real cos_tau = (tau) *(-1.0)* ((temp * temp *  0.0083334) +1.0 - (temp * 0.1666667));}
!##### berechnung delta #######################
integer tageszahl = system.Date("%j").ToInteger();
tageszahl = tageszahl +10;
if (tageszahl > 365) {tageszahl = tageszahl - 365;}
if (tageszahl < 92) {real tag = 0.0172142 *tageszahl;temp = tag * tag;
real delta = (-0.410152) *((temp *temp *0.041666)  + 1.0 - (temp * 0.5));}
if ((tageszahl >91) && (tageszahl < 184)) {tageszahl = 183 - tageszahl; real tag = 0.0172142 *tageszahl;
temp = tag * tag;  real delta = (0.410152) *((temp *temp *0.041666)  + 1.0 - (temp * 0.5));}
if ((tageszahl >183) && (tageszahl < 275)) {tageszahl = tageszahl - 183; real tag = 0.0172142 *tageszahl;
temp = tag * tag;  real delta = (0.410152) *((temp *temp *0.041666)  + 1.0 - (temp * 0.5));}
if ((tageszahl >274) && (tageszahl < 366)) {tageszahl = 365 - tageszahl; real tag = 0.0172142 *tageszahl;
temp = tag * tag;  real delta = (-0.410152) *((temp *temp *0.041666)  + 1.0 - (temp * 0.5));}
!##### berechnung sin_delta, cos_delta  #######################
temp = delta * delta;
real sin_delta =delta * ((temp * temp *  0.0083334) +1.0 - (temp * 0.1666667)); !sinus-naeherung
real cos_delta = (temp *temp *0.0416667)  + 1.0 - (temp * 0.5); !cosinus-naeherung
!##### berechnung tan_delta  mit stueckweiser linearisierung des tan  #######################
boolean vvorzeichen = true;
if (delta< 0.0) {vvorzeichen = false; delta = (-1.0) *delta;}
real tan_delta = 1.0233 * delta;
if (delta >=0.2618) {tan_delta = (1.1822*delta) - 0.0416;}
if (vvorzeichen == false) {tan_delta = (-1.0) * tan_delta;}
!##### berechnung sin_elevation und tan_azimut #######################
real sin_elevation = (sin_phi * sin_delta) +( cos_phi * cos_delta * cos_tau);
temp = sin_elevation * sin_elevation;
real sonne_elevation = sin_elevation * (1.0 + (0.1666667 * temp) + (0.075 * temp * temp));
sonne_elevation = 57.29577951 * sonne_elevation;
real nenner = (sin_phi*cos_tau) - (cos_phi * tan_delta);
if (nenner < 0.0) {boolean plus180 = true;}
real tan_azimut = sin_tau / nenner;
!##### berechnung sonne_azimut mit stueckweiser linearisierung des arctan ############
boolean vorzeichen = true;
if (tan_azimut < 0.0) {vorzeichen = false; tan_azimut = (-1.0)*tan_azimut;}
real sonne_azimut = 0.97723 * tan_azimut;
if ((tan_azimut >=0.2679)&&(tan_azimut < 0.5774)) {sonne_azimut = (0.84588* tan_azimut) + 0.035189;}
if ((tan_azimut >= 0.5774)&&(tan_azimut < 1.0)) {sonne_azimut = (0.6195* tan_azimut) + 0.1659;}
if ((tan_azimut >= 1.0)&&(tan_azimut < 1.3032)) {sonne_azimut = (0.43173* tan_azimut) + 0.3537;}
if ((tan_azimut >= 1.3032)&&(tan_azimut < 1.7321))  {sonne_azimut = (0.3052* tan_azimut) + 0.51856;}
if ((tan_azimut >= 1.7321)&&(tan_azimut < 2.4142)) {sonne_azimut = (0.1919* tan_azimut) + 0.7148;}
if ((tan_azimut >= 2.4142)&&(tan_azimut < 2.9459)) {sonne_azimut = (0.123* tan_azimut) + 0.88115;}
if ((tan_azimut >= 2.9459)&&(tan_azimut < 3.7321)) {sonne_azimut = (0.083312* tan_azimut) + 0.9981;}
if ((tan_azimut >= 3.7321)&&(tan_azimut < 5.0))  {sonne_azimut = (0.050792* tan_azimut) + 1.1194;}
if ((tan_azimut >= 5.0)&&(tan_azimut <7.0)) {sonne_azimut = (0.02775* tan_azimut) + 1.23465;}
if ((tan_azimut >= 7.0)&&(tan_azimut <12.0)) {sonne_azimut = (0.01175117* tan_azimut) + 1.346641;}
if ((tan_azimut >= 12.0)&&(tan_azimut <20.0)) {sonne_azimut = (0.004147854* tan_azimut) + 1.437881;}
if ((tan_azimut >= 20.0)&&(tan_azimut <50.0)) {sonne_azimut = (0.0009987* tan_azimut) + 1.5008639;}
if (tan_azimut >= 50.0) {sonne_azimut = (0.000099983* tan_azimut) + 1.54579974;}
if (sonne_azimut> 1.5707963278) {sonne_azimut = 1.5707963278;}
if (vorzeichen == false) {sonne_azimut = (-1.0) * sonne_azimut;}
sonne_azimut = 57.29577951 * sonne_azimut;
if (plus180 == true) {sonne_azimut = sonne_azimut + 180.0;}
!##### tageszeitliche korrektur und werte auf systemvariablen speichern ######
if (nachmittag == false)
{sonne_azimut = 180.0 - sonne_azimut; sonnenzeit = 720 - sonnenzeit;}
else
{sonne_azimut = sonne_azimut + 180.0;sonnenzeit = 720 + sonnenzeit;}
sonne_azimut = 0.1 *((sonne_azimut*10.0) .ToInteger());
sonne_elevation = 0.1 *((sonne_elevation*10.0) .ToInteger());
dom.GetObject("sonne_elevation").State(sonne_elevation);
dom.GetObject("sonne_azimut").State(sonne_azimut);

Ein sehr einfaches beispielhaftes WebUI-Programm zum Betätigen einer Rolllade ist hier:

Mit der Systemvariablen roll_AutoManuell wird die automatische Steuerung ein- und ausgeschaltet. Diese Systemvariable  kann mit einem weiteren WebUI-Programm und z.B. einer Fernbedienung ein- und ausgeschaltet werden. Die Möglichkeiten, mit Elevation und Azimut die Rollladen und Lampen eines Haus intelligent nach Licht und Schatten zu steuern, sind sehr vielfältig. Ich verweise hier auf mein Tutorial zur Rollladensteuerung.

Ach ja, zum Vergleich kann man die Daten mit einem „genauen“ Sonnenstandsrechner vergleichen: http://www.volker-quaschning.de/datserv/sunpos/index.php
Breiten und Längengrad eingeben und Berechnungsart „SUNAE“ wählen. Die Abweichungen sind bei mir meist unter einem Grad, was bei den verwendeten Näherungsverfahren für die trigonometrischen Funktionen ganz ordentlich ist. Für die Heimautomation ist das jedenfalls mehr als genau!

 

Ergänzung 14.03.2014

Wie man an den verwendeten Zahlenkolonnen im Skript erkennt, wurde bewusst weniger auf programmiertechnische „Schönheit“ ausgelegt, sondern vielmehr wurde auf möglichst wenig Rechenoperationen und damit Laufzeit optimiert. Dies ist deshalb wichtig, weil das Skript zyklisch häufig aufgerufen wird!
Geringe Unstetigkeiten im Verlauf des Elevations- und Azimutwinkels bedingt durch die verwendeten Näherungsverfahren wurden weiter optimiert, so dass jetzt für die Heimautomation mehr als ausreichende Genauigkeit der Sonnenkoordinaten erreicht wird. Insbesondere im Bereich des Nulldurchgangs der Elevation ist ein möglichst stetiger Winkelverlauf deshalb wichtig, weil die meisten Steuerungsaufgaben im Bereich der Dämmerung erfolgen und hier ein möglichst gleichmässiger stufenloser Verlauf von grossem Vorteil ist.
Das folgende Bild zeigt sehr schön den Azimut und die Elevation, aufgezeichnet mit Historian über 24h. Diese Kurve verschieben sich täglich um einen geringen Betrag, wie man an dem geringen Sprung in der Elevationskurve um ca 00:00h erkennt:

sonnenstand1

Genauigkeit

Der Vergleich mit Winkelergebnissen der Internet-Sonnenpositionsrechner ist abhängig von dem dort verwendeten Berechnungsverfahren:
Bei dem Berechnungsverfahren SUNAE war der Unterschied bei der Elevation meist weniger als ein halbes Grad. Der Azimutwinkel hatte Abweichungen bis zu etwa 2 Grad. Letztlich ist aber nicht die Genauigkeit entscheidend, sondern dass man im Bereich des Sonnenauf- und -untergangs mit dem Elevationswinkel eine gur aufgelöste stufenlose Zahl hat, von der man Schaltvorgänge reproduzierbar abhängig machen kann. Mit dem Azimut steuert man in der Regel sowieso nur Beschattungseinrichtungen, bei denen eine Winkelgenauigkeit von 2Grad mehr als genau sind.

 

Update 07.04.2016:

Alternatives Skript mit weniger verwendeter Systemvariablen

Vermutlich kann die CCU nur eine begrenzte Zahl von Namen in Skripten stabil verarbeiten. (man spricht von 200 ?!). Deshalb wurde das Sonnenstand-Skript diesbezüglich weiter optimiert, so daß beim nachfolgenden verbesserten Skript nur noch 11 Variablen verwendet werden. Allerdings ist die Lesbarkeit das Verständnis des Programmes dabei verloren gegangen. Aber man kann sich ja dafür das erste Listing ansehen, welches die gleiche Funktion allerdings mit deutlich mehr Variablennamen hat.

!berechnung sonne_elevation, sonne_azimut; stand 07.08.2016  verfasser: eugen stall,  stall.biz
!dieses skript alle 4min aufrufen 
real temp;
real sin_phi;
real cos_phi;
integer sonnenzeit;
boolean nachmittag;
real sin_tau;
real cos_tau;
real tau1;
real sin_delta;
real cos_delta;
boolean vorzeichen;
 
integer temp = system.Date("%M").ToInteger() + 60*system.Date("%H").ToInteger();
integer sonnenzeit =temp + 720 - 0.5 *((system.SunriseTime("%M").ToInteger() + 60*system.SunriseTime("%H").ToInteger()) +system.SunsetTime("%M").ToInteger() + 60* system.SunsetTime("%H").ToInteger());
if (sonnenzeit > 1440) {sonnenzeit = sonnenzeit -1440;}
if (sonnenzeit < 1) {sonnenzeit = 1440 + sonnenzeit;}
if (sonnenzeit > 720) {sonnenzeit =sonnenzeit - 720; nachmittag = true; }
   else {sonnenzeit =720 -sonnenzeit;nachmittag =false;}
temp = 0.00436332313 * sonnenzeit;   
if (temp < 1.570796327)
  {real sin_tau =temp * ((temp * temp * temp * temp *  0.0083334) +1.0 - (temp * temp *0.1666667));
   temp= 1.570796327 - temp;
   cos_tau =temp * ((temp * temp * temp * temp *  0.0083334) +1.0 - (temp * temp * 0.1666667));}                   
   else {real tau1  =3.141592654 - temp;
         sin_tau =tau1 * ((tau1 * tau1 * tau1 * tau1 *  0.0083334) +1.0 - (tau1 * tau1 * 0.1666667));
         temp = temp  -  1.570796327;
         real cos_tau = (temp) *(-1.0)* ((temp * temp * temp * temp *  0.0083334) +1.0 - (temp * temp * 0.1666667));}
temp = system.Date("%j").ToInteger() +10;
if (temp > 365) {temp = temp - 365;}
if (temp < 92) {real tag = 0.0172142 *temp;
                tau1 = (-0.410152) *((tag * tag *tag * tag *0.041666)  + 1.0 - (tag * tag * 0.5));}
if ((temp >91) && (temp < 184)) {temp = 183 - temp; real tag = 0.0172142 *temp;
                                 tau1 = (0.410152) *((tag * tag *tag * tag *0.041666)  + 1.0 - (tag * tag * 0.5));}
if ((temp >183) && (temp < 275)) {temp = temp - 183; real tag = 0.0172142 *temp;
                                  tau1 = (0.410152) *((tag * tag *tag * tag *0.041666)  + 1.0 - (tag * tag * 0.5));}
if ((temp >274) && (temp < 366)) {temp = 365 - temp; real tag = 0.0172142 *temp;
                                  tau1 = (-0.410152) *((tag * tag *tag * tag *0.041666)  + 1.0 - (tag * tag * 0.5));}
sin_delta =tau1 * ((tau1 * tau1 * tau1 * tau1 *  0.0083334) +1.0 - (tau1 * tau1 * 0.1666667)); !sinus-naeherung
cos_delta = (tau1 * tau1 * tau1 * tau1 *0.0416667)  + 1.0 - (tau1 * tau1 * 0.5); !cosinus-naeherung
if (tau1 < 0.0) {vorzeichen = false; tau1 = (-1.0) * tau1;} else {vorzeichen = true;}
temp = 0.017453292*system.Latitude(); 
sin_phi =(temp * ((temp * temp * temp * temp * 0.0083334) +1.0 - (temp * temp * 0.1666667)));
cos_phi = ((temp * temp *temp * temp *0.0416667)  + 1.0 - (temp * temp * 0.5));
temp = (sin_phi * sin_delta) +( cos_phi * cos_delta * cos_tau);
temp = temp * (1.0 + (0.1666667 * temp * temp) + (0.075 * temp * temp * temp * temp));     
temp = 57.29577951 * temp;
temp= 0.1 *((temp *10.0) .ToInteger());     
dom.GetObject("sonne_elevation").State(temp);
WriteLine(temp);
 
if (tau1 >=0.2618) {tau1 = (1.1822 * tau1) - 0.0416;}else {tau1 = 1.0233 * tau1;}
if (vorzeichen == false) {tau1 = (-1.0) * tau1;}
temp = (sin_phi*cos_tau) - (cos_phi * tau1);
if (temp < 0.0) {boolean plus180 = true;}  
temp = sin_tau / temp;
vorzeichen = true;
if (temp < 0.0) {vorzeichen = false; temp = (-1.0)*temp;}
tau1 = 0.97723 * temp;
if ((temp >= 0.2679)&&(temp < 0.5774)) {tau1 = (0.84588* temp) + 0.035189;}
if ((temp >= 0.5774)&&(temp < 1.0)) {tau1 = (0.6195* temp) + 0.1659;}
if ((temp >= 1.0)&&(temp < 1.3032)) {tau1 = (0.43173* temp) + 0.3537;}
if ((temp >= 1.3032)&&(temp < 1.7321))  {tau1 = (0.3052* temp) + 0.51856;}
if ((temp >= 1.7321)&&(temp < 2.4142)) {tau1 = (0.1919* temp) + 0.7148;}
if ((temp >= 2.4142)&&(temp < 2.9459)) {tau1 = (0.123* temp) + 0.88115;}
if ((temp >= 2.9459)&&(temp < 3.7321)) {tau1 = (0.083312* temp) + 0.9981;}
if ((temp >= 3.7321)&&(temp < 5.0))  {tau1 = (0.050792* temp) + 1.1194;}
if ((temp >= 5.0)&&(temp <7.0)) {tau1 = (0.02775* temp) + 1.23465;}
if ((temp >= 7.0)&&(temp <12.0)) {tau1 = (0.01175117*temp) + 1.346641;}
if ((temp >= 12.0)&&(temp <20.0)) {tau1 = (0.004147854* temp) + 1.437881;}
if ((temp >= 20.0)&&(temp <50.0)) {tau1 = (0.0009987*temp) + 1.5008639;}
if (temp >= 50.0) {tau1 = (0.000099983* temp) + 1.54579974;}
if (tau1> 1.5707963278) {tau1 = 1.5707963278;}
if (vorzeichen == false) {tau1 = (-1.0) * tau1;}
tau1 = 57.29577951 * tau1;
if (plus180 == true) {tau1 = tau1 + 180.0;}
if (nachmittag == false)
  {tau1 = 180.0 - tau1; sonnenzeit = 720 - sonnenzeit;}
   else {tau1 = tau1 + 180.0;sonnenzeit = 720 + sonnenzeit;}
tau1 = 0.1 *((tau1*10.0) .ToInteger());
dom.GetObject("sonne_azimut").State(tau1);
WriteLine(tau1);
Zufallsgenerator Skript für Anwesenheitssimulation, Lampen..

Zufallsgenerator Skript für Anwesenheitssimulation, Lampen..

Für die Simulation der Anwesenheit werden gerne die Lampen oder die Rolladen oder was auch immer möglichst zufällig ein und ausgeschaltet. In den meisten Programmiersprachen gibt es für die Programmierung geeignete Algorithmen sog. Randomfunktionen, die beispielsweise gleichverteilte Zahlen zwischen 0.0 und 1.0 erzeugen. Obwohl so eine Funktion für die Hausautomation eine relativ sinnvolle Funktionalität bietet, ist keinerlei Randomfunktion in der HM-Skriptsprache abgebildet. Hiermit soll diesem Mangel mit einem HM-Skript abgeholfen werden.Der gewählte Lösungsansatz ist sicher etwas ungewöhnlich, was dem doch sehr beschränkten Befehlssatz der Homemaic-Skriptsprache geschuldet ist:
In der Elektronik verwendet man gerne für die Rauscherzeugung sog. Pseudo-Rauschgeneratoren; Herzstück sind dafür mit XOR-Gattern rückgekoppelte Schieberegister. Wer mehr darüber wissen will: http://de.wikipedia.org/wiki/Linear_r%C3%BCckgekoppeltes_Schieberegister
Dieser eigentlich hardwarenahe Lösungsansatz wird im Folgenden für die softwaremässige Erzeugung von gleichverteilten Zahlen verwendet. Dahinter sind komplizierte Theorien, die aber hier nicht weiter erläutert werden. Deshalb für den reinen Anwender hier mein… Kochrezept:
1   Zwei Systemvariable „random_zahl“ und „random_tp“ vom Variablentyp Zahl anlegen, Wertebereich -2.0 bis 2.0 !
Dabei ist „random_zahl“ eine bei jedem Skriptaufruf neu berechnete Zufallszahl zwischen 0.0 und 1.0 Die Systemvariable „random_tp“ ist eine gemitteltes oder „geglättetes“ Abbild von „random_zahl“ im gleichen Wertebereich.
2   Ein WebUI-Programm anlegen, in dem zyklisch beispielsweise alle 3 Minuten folgendes HM-Skript aufgerufen wird:
HM-Skript   
!Version  2014.06.02 ;  Verfasser :Eugen Stall
!zwei Systemvariablen "random_zahl"  und "random_tp" als Zahl mit Wertebereich -2.0 bis 2.0 anlegen
!Skript speichert  Zufallszahl zwischen 0.0 und 1.0 ungefiltert auf "random_zahl und gemittelt auf "random_tp"
!##############################################
real zahl = 65536.0 * dom.GetObject("random_zahl").State();
zahl = zahl.ToInteger();
if ((zahl <1) || (zahl > 65535)) {zahl= 3757;} !um saubere Startposition sicherzustellen
integer zahl_alt = zahl;
!##############################################
string dualzahl = "";
integer xor ;
integer x;
!hier wird ein 16bit-Schieberegister mit XOR-Rückkopplungen simuliert
if (zahl > 32767) {dualzahl = dualzahl + "1"; zahl = zahl - 32768; x = 1;} else    {dualzahl = dualzahl + "0";  x = 0;}
xor = x;
if (zahl > 16383) {dualzahl = dualzahl + "1"; zahl = zahl - 16384;} else    {dualzahl = dualzahl + "0";}
if (zahl > 8191) {dualzahl = dualzahl + "1";  zahl = zahl - 8192; x = 1;} else    {dualzahl = dualzahl + "0"; x = 0;}
if ((xor + x) ==1) {xor = 1;} else {xor = 0;}
if (zahl > 4095) {dualzahl = dualzahl + "1"; zahl = zahl - 4096; x = 1;} else    {dualzahl = dualzahl + "0"; x = 0;}
if ((xor + x) ==1) {xor = 1;} else {xor = 0;}
if (zahl > 2047) {dualzahl = dualzahl + "1"; zahl = zahl - 2048 ;} else    {dualzahl = dualzahl + "0";}
if (zahl > 1023) {dualzahl = dualzahl + "1"; zahl = zahl - 1024 ; x = 1;} else    {dualzahl = dualzahl + "0"; x = 0;}
if ((xor + x) ==1) {xor = 1;} else {xor = 0;}
!niedrigstes neues bit einschieben
zahl = zahl_alt * 2;
if (zahl > 65535) {zahl = zahl - 65536;}
zahl = zahl + xor;
!auf 1 normieren und auf Systemvariable random_zahl  speichern  ############
zahl = 0.0000152590219* zahl;  !zahl / 65536
dom.GetObject("random_zahl").State(zahl);
!Mittelwertbildung : der Faktor 0.2 bestimmt die Mittelungsstärke. ###########
!Je kleiner, desto "ruhiger" das Rauschsignal  ########################
real tp_random = dom.GetObject("random_tp").State();
tp_random =  tp_random + (0.2 * (zahl - tp_random));
dom.GetObject("random_tp").State(tp_random);
tp_random = dom.GetObject("random_tp").State();
!####################################################


3  Jeweils ein WebUI-Programm für die zufällige Steuerung einer Lampe, Rollade oder was auch immer anlegen.

Hier ein Beispiel für die Zufallsteuerung einer Lampe abends zwischen 18 und 23H:

random1.jpg

Das Einschaltverhältnis (Lampe EIN zu Lampe AUS) kann man mit dem Schaltwert für „random_tp“ beliebig nach den individuellen Wünschen festlegen. Ich habe in den o.a. Beispiel einen Wert von 0.5 gewählt, was bedeutet, dass die Lampe in dem gewählten Zeitraum für etwa 50% der Zeit leuchtet.
Die Zeitdauer von Eingeschaltet und Pause hängt u.a. vom gewählten Aktualisierungsintervall des Skriptes ab. Kleine Zeiten ( 1 bis 2 Minuten) bedeutet häufiges Schalten, große Intervalle das Gegenteil.
Zur Zeit versuche ich noch die für mich geeigneten Einstellparameter zu ermitteln. Ansonsten läuft das Skript sehr schnell durch, aber trotzdem sollte man im Hinblick auf CCU-Belastung nicht zu häufig die Randomfunktion abfragen.
Viel Spass beim „zufälligen“ Steuern und Regeln

Changelog:
01.06.2014: fehlende Programmzeile im Skript ergänzt
02.06.2014: Programm verkürzt, Durchlaufzeit verbessert

Datumsbereiche oder Datumsabfragen im WebUI verwenden

Datumsbereiche oder Datumsabfragen im WebUI verwenden

Meine Teichpumpe sollte nur in einem bestimmten Datumsbereich z.B. vom 2.April bis 10.Oktober oder in einer bestimmten Jahreszeit aktiviert werden. Mit den Funktionen der Zeitsteuerung im WebUI lassen sich aber leider nur einzelne Events (einfach oder wiederholend) festlegen; eine Festlegung eines Datumsbereiches ist nach meinem Kenntnisstand damit nicht möglich.
Mit Verwendung einiger im FHZ-Forum bereits verwendeter Lösungsansätze für die Berechnung von Tageszeiten habe ich für die Aufgabe den folgenden Ansatz gewählt:
1  Definition einer Systemvariablen „Datum_MM.TT“ vom Variablentyp „Zahl“.
2  Übergabe des aktuellen Datums an die Systemvariable „Datum_MM.TT“ mit folgendem Skript, das mindestens einmal am Tag ausgelöst werden sollte:
HM-Skript   
!Skript aktuelles Datum an Systemvariable Datum_MM.TT übergeben
real datum = (0.01 * system.Date("%d").ToInteger()) + system.Date("%m").ToInteger();
dom.GetObject("Datum_MM.TT").State(datum);

3  Im WebUI den gewünschten Datumsbereich durch größer/kleiner/gleich-Abfragen realisieren, so wie im folgenden Beispiel für die Aktivierung meiner Teichpumpe:

Teichpumpe.jpg
Jede Nacht ist anders! dynamische Rolladenbetätigung

Jede Nacht ist anders! dynamische Rolladenbetätigung

Wer schon länger eine automatische Rolladenbetätigung in Abhängigkeit von Sonnenauf- / untergang betreibt, der kennt das Thema: Im Winter gehen die Rolladen viel zu früh runter und im Sommer viel zu spät. Das liegt u.a. daran, dass die Dämmerungsphase im Winter viel länger andauert und im Sommer recht kurz ist. Viele Versuche mit einer Rolladenbetätigung eine feste Zeit nach Sonnenuntergang unter Verwendung des sicher bekannten Tageszeitenskriptes zeigen, dass man zwar für eine bestimmte Jahreszeit die Rolladenschliesszeit optmieren kann, aber man muß jahreszeitabhängig immer wieder anpassen. Seit einigen Monaten fahre ich eine andere Strategie, bei der die Rolladenschliesszeit zwar von der jahreszeitlichen Sonnenuntergangszeit abgeleitet wird, diese Ableitung aber nicht additiv sondern mit einer linearen Umrechnung erfolgt. Mit dem  obigen Bild wird der Sachverhalt deutlich.

Näherungsweise übers Jahr ist die Darstellung der Sonnenuntergangszeit eine Sinuskurve mit einem Minimalwert im Dezember (SUmin) und einem Maximalwert im Juni. Ausgehend von dieser Abhängigkeit (rote Kurve) berechne ich eine gefühlt angenehmere Rolladenschliesszeit in der Weise. dass die Amplitude verkleinert und der Mittelwert vergrößert wird.(grüne Kurve). Die Berechnungsgrundlagen für die Umrechnung sind unten im Bild dargestellt. Vorgegeben aufgrund meiner individuellen Ortslage (Nähe Bonn) sind die Zeiten für SUmax und SUmin (in Winterzeit !) . Bei mir ist SUmax = 1250 (in min entsprechend 20h50) und SUmin = 990 (in min entsprechend 8h30). Die gewünschte Zielkurve wir nur duch die beiden Parameter ROLLmax und ROLLmin festgelegt. Ich habe hier für die grüne kurve ROLLmax = 1320 und ROLLmin = 1170 gewählt. Daraus ergibt sich dann eine einfache Berechnungsformel für die tägliche Rolladenschließzeit ROLL = 0.58 * SU +595 .
Wenn nun die Rolladen zu dieser Zeit geschlossen werden sollen und beispielsweise zu einer festen Zeit am morgen wieder geöffnet werden sollen, dann sieht die WebUI-Umsetzung mit integriertem Skript etwa so aus:
Zuerst definiert man zwei neue Systemvariable „rolladenzeit“ als Zahl und „rolladengeschlossen“ als log. Variable.
Dann ruft man mit einem WebUI-Programm zyklisch alle paar Minuten das folgende Skript auf, das die Systemvariable „rolladengeschlossen“ entsprechend der grünen Kurve abends auf 1 setzt und morgens um 6h45 wieder auf 0 zurücksetzt. Damit man die aktuelle Rolladenschliesszeit sich ansehen kann, wird die Systemvariable „rolladenzeit“ im Skript ebenfalls berechnet.

 

HM-Skript   
!Skript setzt die logische Systemvariable "rolladengeschlossen"  auf true, wenn Rolladen runter
 
!Rolladen-Schliesszeit ROLL berechnet sich aus der aktuellen Sonnenuntergangszeit SU
!Zeitberechnung immer im Minutenschema!aktuellen Sonnenaufgang berechnen
integer SU = system.SunsetTime("%M").ToInteger() + 60* system.SunsetTime("%H").ToInteger();
integer zeit = system.Date("%M").ToInteger() + 60*system.Date("%H").ToInteger();
 
!Systemvariable "rolladengeschlossen" abends auf 1 und morgens um 6h45 auf 0 setzen
boolean rolladennacht =0;
real ROLL = 595.0 + 0.58* SU;
if (zeit < 405)   {rolladennacht = 1;} !Zwischen 0h00 und 6h45 ist rolladennacht = 1
if (zeit > ROLL)  {rolladennacht = 1;} ! zwischen ROLL und 24h00 ist rolladennacht = 1
dom.GetObject("rolladengeschlossen").State(rolladennacht);
 
!Umformen von der Minutendarstellung von ROLL in Stundendarstellung hh,mm
!und abspeichern auf Systemvariable "rolladenzeit"
real su_zeit = 0.0166 *ROLL;
real su_zeit_h = su_zeit.ToInteger();
real su_zeit_m = (su_zeit - su_zeit_h) *0.6;
real su_zeit= su_zeit_m + su_zeit_h;
dom.GetObject("rolladenzeit").State(su_zeit);

Das eigentliche Rolladenprogramm sieht dann beispielhaft so aus:

dyn_rolladen1

Aktuell steuere ich insgesamt 14 Rolläden im Nachtbetrieb mit der beschriebenen Strategie.Mit dem Schliessverhalten bin ich jetzt sehr zufrieden, weil keine jahreszeitlichen Anpassungen mehr notwendig sind. Im Tagbetrieb wird abhängig vom Sonnenstand, der Sonneneinstrahlung individuell jedes Fenster gesteuert, aber das ist ein anderes Thema.

HM-Skript zur einfachen Sonnenstandsberechnung

HM-Skript zur einfachen Sonnenstandsberechnung

Wenn ich hier von Sonnenstand spreche, dann meine ich die Position der Sonne mit den Kennzeichen Azimut und Elevation.
Der Azimut ist der Winkel des Sonnenstandes in der Horizontalen: wie bei der Kompassrose ist 0° gleich NORD, 90°gleich OST, 180° gleich SUED und 170°gleich WEST.
Die Elevation ist der Winkel des Sonnenstandes in der Vertikalen: 0° ist der Horizont, 90° ist der „Himmel ganz oben“.
Mit diesen beiden Größen kann man sehr gut feststellen, ob und wann die Sonne bestimmte Elemente „besonnt“ und ggf. ob mit der Homematic bestimmte Aktionen auszulösen sind. Aktionen können das Herunterlassen der Rolladen sein, das Ausfahren der Markisen, das Öffnen von Lüftungsfenstern, das Steuern der Lage von Sonnenkollektoren oder einfach das Schalten von Umwälzpumpen bei thermischen Sonnenkollektoren. Schliesslich kann man sogar mit der Information des Elevationswinkels dämmerungsabhängig bestimmte Aktionen auslösen: Lampen und/oder Rolläden .Leider ist die Berechnung des Sonnenstandes eine relativ schwierige Rechnung, weil einerseits die lokalen Positionsdaten zu berücksichtigen sind, wie auch die lokale Zeit mit dem ganzen Thema Sommer/Winterzeit. siehe auch Google Sonnenstand und u.a. http://de.wikipedia.org/wiki/Sonnenstand
Zudem erfordert die Berechnung leider mathematische Winkelfunktionen, die in der HM-Skriptsprache nicht enthalten sind; also im Hinblick auf Realisierung auf der Homematic ein schwieriges Thema.Lösungsansätze:
– Eine Lösung ist die Auslagerung der komplexen Kalkulation in ein kleines TCL oder PHP-Progrämmchen, was dann per HM-Skript gestartet wird. Aber das ist auch nicht so prickelnd, weil ggf. beim Firmware-Update das Progrämmchen wieder an Ort und Stelle gespeichert werden muss.
– Eine andere Lösung ist, die Sonnenstandsdaten aus dem Internet beispielsweise alle 5 Minuten zu holen, ähnlich wie die Wetterdaten mit z.B. Wunderground. Hierfür gibt es mehrere Server, die aber eher semiprofessionell betrieben werden. Wie lange und wie oft man die Daten dort herunterladen kann ist ungewiss. Gerade im Hinblick auf die besondere Bedeutung der häuslichen Elemente, die man damit steuern will, ist das m.E. nicht so zielführend. Aber trotzdem ist das eine mögliche Lösung. http://homematic-forum.de/forum/viewtopic.php?f=31&t=4942&hilit=sonnenstandMein Lösungsansatz verwendet nur ein HM-Skript, das die Daten Azimut und Elevation für den individuellen Standort mit einem Interpolationsverfahren berechnet, welches nur mit den vier Grundrechenarten arbeitet:
Zuerst dazu hier etwas Theorie oder gleich nach unten zum Kochrezept:
Wichtig bei der Sonnenstandsberechnung ist zuerst die Ermittlung der aktuellen Sonnenzeit. Diese ergibt sich aus der Zeitzonenzeit plus Addon-Zeit , die aus dem Längengrad berechnet wird. Da wir aber auch Winter/Sommerzeit haben, muß das ebenfalls berücksichtigt werden. Ich habe hier ein anderes Verfahren verwendet, bei dem aus den in der Homematic vorhandenen Zeiten für Sonnenaufgang und Untergang einfach der Mittelwert berechnet wird und die Abweichung von 12:00h die Korrekturzeit ist, die zur aktuellen Zeit addiert wird. So erhält man die lokale Sonnenzeit, ohne dass man kompliziert rechnen muss.
In Abhängigkeit von dieser so berechneten Sonnenzeit berechnet sich der Sonnenstand in Abhängigkeit vom Breitengrad über eine komplexe trigonometrische Formel. Zur Berechnung gibt es im Internet schöne Excel-Berechnungsvorlagen. Ich habe diese hier verwendet : http://karena.de/software.htm#Solardiagramm und für meinen Standort ausgedruckt:sonnenstand0Wenn man beispielweise sich den Sonnenstand für 15:00H während des Jahres anschaut, dann verändert sich der Azimut um ca. 25°, was im Rahmen einer Beschattungssteuerung schon wichtig ist. Das zeigt, dass eine rein uhrzeitabhängige Steuerung der Beschattung oder der nachtabhängigen Rolladenbetätigung für die meisten Anwendungen zu ungenau ist.
Für die Berechnung des Sonnenstandes mit einem HM-Skript werden von dem individuellen Chart, nur die mit Kreisen gekennzeichneten Koordinaten abgelesen und in das Skript eingetragen. Das Skript macht dann eine mehrstufige lineare Interpolation und errechnet sich für den aktuellen Tag die rot gekennzeichnete Tagesline . Diese wiederum ergibt mit der Sonnenzeit die gesuchten Werte für Azimut und Elevation.Und jetzt für diejenigen ohne Interesse für die Theorie hier
das Kochrezept:

1. Anlegen der Systemvariablen:
sonne_azimut Zahl grad -360 bis +360
sonne_elevation Zahl grad -90 bis +90

2. Eintragen der lokalen Koordinatenwerte in das Skript. Für Köln sind die Werte bereits eingetragen. Wer einen anderen Breitengrad hat, kann aus folgender Tabelle sich die Werte direkt oder interpoliert herausholen:

stuetzstellen_sonnenstand

3. Folgendes Skript etwa alle 5 Minuten aufrufen, damit die Systemvariablen aktualisiert werden

Code: Alles auswählen
!############################################################################################
!Daten aus lokalem Sonnenbahndiagramm in gerundeten Werten einsetzen:
!(diese  Werte hier sind für  Breitengrad 51grad ,Köln!)
integer a12max = 180; integer a11max = 152; integer a10max = 129; integer a9max = 111;
integer e12max = 63;   integer e11max = 60;   integer e10max = 55;   integer e9max = 46;
integer  a12min = 180; integer a11min  = 166; integer a10min  = 152; integer a9min = 140;
integer e12min = 15;    integer e11min  = 14;   integer e10min = 11;    integer  e9min = 5;
!############################################################################################
!Konstantwerte für alle mitteleuropäischen Breitengerade von 48grad(Wien) bis 54grad(Kiel)
integer a5max = 63;  integer e5max = 7;integer a5min = 95;  integer e5min = -28;
!Alle Zeiten in Minuten
integer c_zeit = system.Date(„%M“).ToInteger() + 60*system.Date(„%H“).ToInteger();
integer c_tagesbeginn = system.SunriseTime(„%M“).ToInteger() + 60*system.SunriseTime(„%H“).ToInteger();
integer c_tagesende = system.SunsetTime(„%M“).ToInteger() + 60* system.SunsetTime(„%H“).ToInteger();
integer sonnenzeit =c_zeit + 720 – 0.5 *(c_tagesbeginn +c_tagesende);
boolean nachmittag = false;
if (sonnenzeit > 720) {sonnenzeit =1440 – sonnenzeit; nachmittag = true; }
integer jahrestag = system.Date(„%j“).ToInteger();
if (jahrestag > 355) {jahrestag =jahrestag -355;} else {jahrestag =jahrestag +10;}
if (jahrestag  > 182) {jahrestag =365 – jahrestag;}
real jahresfortschritt =0.005464 * jahrestag;  ! entspricht /183
!WriteLine(„jahresfortschritt“ #jahresfortschritt);   !wert läuft zwischen 0 (dec ) und 1 (jun) und zurück
!WriteLine(„sonnenzeit“ #sonnenzeit);!Stuetzwerte der heutigen Tageslinie berechnen
real a12 = 180;
real e12 =  e12min + jahresfortschritt * (e12max – e12min);real a11 = a11min + jahresfortschritt * (a11max – a11min);
real e11 =  e11min + jahresfortschritt * (e11max – e11min);real a10 = a10min + jahresfortschritt * (a10max – a10min);
real e10 =  e10min + jahresfortschritt * (e10max – e10min);real a9 = a9min + jahresfortschritt * (a9max – a9min);
real e9 =  e9min + jahresfortschritt * (e9max – e9min);real a5 = a5min + jahresfortschritt * (a5max – a5min);
real e5 =  e5min + jahresfortschritt * (e5max – e5min);

real a0 = a5; real e0 =e5; real a1= a9; real e1= e9;
real a =  a0 + (0.00417 * (sonnenzeit -300) * (a1 – a0));
real e =  e0 + (0.00417 * (sonnenzeit -300) * (e1 – e0));
!WriteLine(„a    e   “ #a #e);

if (sonnenzeit >539)
{a0 = a9; e0 =e9; a1= a10; e1= e10;
real a = a0 +  (0.0167* (sonnenzeit – 540) *(a1 – a0));
real e = e0 +  (0.0167* (sonnenzeit – 540) *(e1 – e0)); }

if (sonnenzeit >599)
{a0 = a10; e0 =e10; a1= a11; e1= e11;
real a = a0 +  (0.0167* (sonnenzeit – 600) *(a1 – a0));
real e = e0 +  (0.0167* (sonnenzeit – 600) *(e1 – e0)); }

if (sonnenzeit >659)
{a0 = a11; e0 =e11; a1= a12; e1= e12;
real a = a0 +  (0.0167* (sonnenzeit – 660) *(a1 – a0));
real e = e0 +  (0.0167* (sonnenzeit – 660) *(e1 – e0)); }

if (nachmittag == true) {a = 360 – a;  sonnenzeit = 1440 – sonnenzeit;}
dom.GetObject(„sonne_elevation“).State(e);
dom.GetObject(„sonne_azimut“).State(a);

!Wer noch was mit der lokalen Sonnenzeit anfangen will, der muss zusaetzlich die folgenden Systemvariablen anlegen:
!“sonnen_zeit“  Zahl   min    und    „sonnen_zeit_hh_mm“     Zahl
!und die Rufzeichen in den folgenden Skriptzeilen entfernen
!dom.GetObject(„sonnen_zeit“).State(sonnenzeit);
sonnenzeit =0.0166 * sonnenzeit;
integer sonnenzeit_h = sonnenzeit.ToInteger();
real sonnenzeit_min = (sonnenzeit – sonnenzeit_h) *0.6;
real sonnenzeit_hhmm = sonnenzeit_min + sonnenzeit_h;
!dom.GetObject(„sonnen_zeit_hh_mm“).State(sonnenzeit_hhmm);

Fertig!

Wie schon gesagt kann man mit den Werten sonnen_azimut und sonnen_elevation tolle Sachen veranstalten, um zu jeder Jahreszeit die richtigen Aktionen der Rolladen- und Jalousiensteuerung auszulösen. Oder man kann sogar das altbekannte Skript für die Berechnung der Tageszeiten durch ein besseres Tool ersetzen, bei dem man die verschiedenen Dämmerungswerte viel felexibler durch den Elevationswinkel ersetzt. Wenn man erst mal verstanden hat, welch wichtige Göße der absolute Sonnestand bei der Hausautomation ist, möchte man auf diese Information nicht mehr verzichten. Ich glaube sogar, dass professionelle Systeme wie KNX auch diese Grössen berechnen.
Vielleicht werde ich hierzu bei Gelegenheit mal meine aktuelle Rolladen- und Jalousiensteuerung vorstellen, die wesentlich von sonnen_azimut und sonnen_elevation gesteuert wird.

Changelog 28.02.2014: Die notwendigen Systemvariablen wurden weiter reduziert, entsprechende Änderungen im Skript eingetragen. Kennwerte für Köln korrigiert.

Translate »