Anbei meine aktuelle Lösung zur Rolladensteuerung inklusive Verschattung.
Das Script ist weitgehend getestet, aber nach meinen individuellen Bedingungen und Vorstellungen ausgerichtet.
Funktionen:
- Rollade hoch/runter nach Sonnenauf/-untergang
- helligkeitsabh. Verschiebung um gewisse Zeit
- Begrenzung jeweils min/max
- Offset je Rollade
- hoch runter für DPT=3, zusaetzlich Verschattung falls DPT=5
- Verschattung nach Über-/unterschreiten Min/max-Helligkeitswert
- Mittelwertbildung Helligkeit für Verschattung, opt Korrekturfaktor abh. von Azimuth
- Sperre Rollade hoch/runter, Verschattung, Kontakt
- Sperre Verschattung global
- Abwesenheitsmodus deaktiviert Fahr-/Verschattungssperre
- Drei Stufen der Verschattung in Abhaengigkeit der Jahreszeit
- todo: Wenn Rollade von Hand bewegt wurde, schalte Verschattungssperre ein?
Zur Übernahme sind natürlich einige Anpassungen (GAs, Zeiten, Winkel, Schwellen) nötig, wofür Debugausgaben und rrd-Diagramme hilfreich sind. Ich empfehle, die knx_writes rauszunehmen und das Fahren eine zeitlang zu beobachten und dabei die Parameter zu justieren. Auf eine Auslagerung der Parameter in eine Config-Datei habe ich verzichtet.
Im Moment wird der tatsächliche Stand der Rollade nicht berücksichtigt, d.h. Variable _xxxstate und Aktorrückmeldung werden sich unterscheiden, wenn man die Rollade von Hand verfährt. Das hat gewisse Vor- und Nachteile.
Sorry für das verwendete Denglish, es macht die Variablen etc. prägnanter und kürzer
.
Falls Interesse, viel Spass!
Code:
# Plugin fuer Rolladen # Version 3.1 03.09.2013 # Copyright: nipponichi # License: GPL (v2) # Variablen (read/write) # r/w _<name>state Zustand Rollade <name> (0/100 bzw. 0..100) # r/w _HellAvg Durchschnittshelligkeit # r/w _HellState Zustand Verschattung # r/w _lastCall Minutenwert des letzten Aufrufs # w _lastAzimuth Azimuth in Grad (debugging) # w _lastElevation Elevation in Grad (debugging) # r SunRiseSet_sunrise Uhrzeit Sonnenaufgang hh:mm (externes Plugin benoetigt) # r SunRiseSet_sunset Uhrzeit Sonnenuntergang hh:mm (externes Plugin benoetigt) ###################### ### Einstellungen: ### ###################### # Die Koordinaten des Hauses, ueber http://www.getlatlon.com/ zu ermitteln. my ($lat, $lon, $elev) = ( 51.1234567890, # Breitengrad in Grad 6.1234567890, # Laengengrad in Grad 50 # Hoehe ueber NN in Meter ); my $cycletime = 50; # Aufruf alle x Sekunden my $verschattung_ga = '0/1/10'; # GA global Verschattung ein/aus my $abwesend_ga = '0/0/13'; # GA Verschattung on/off, immer Verschattung bei Abwesendheit my $helligkeit_ga = '0/0/241'; # GA Helligkeit read my $helligkeit_gaw = '0/0/242'; # GA Helligkeit write (gemittelt und gedeckelt) my $loops = 20; # Anzahl Zyklen (cycletime) fuer Durchschittshelligkeit # Shift Rollade my $rlimit_lo = 20; # laenger unten lassen/frueher runterfahren fuer max hshift Minuten falls < rlimit_lo my $rlimit_hi = 60; # frueher auffahren/laenger oben lassen fuer max hshift Minuten falls > rlimit_hi # Verschattung my $vlimit_hi = 3500; # Hysterese oberes Limit durchschn Helligkeit my $vlimit_lo = 3100; # Hysterese unteres Limit durchschn Helligkeit my $verschattung1 = 65; # prozentuale verschattung Jun-Aug my $verschattung2 = 50; # prozentuale verschattung Mar-Mai, Sep-Oct my $verschattung3 = 40; # prozentuale verschattung Nov-Feb # Korrekturfaktor Helligkeit ab Azimuth in grad (z.B. 222-237 grad Faktor 1.1) my %hcorr = ( 222, 1.1, 237, 1.0 ); # RollCfg: Einstellungen Rolladen # name - Name # Up_min, Up_max - Uhrzeiten min/max fuer hoch std:min # Dn_min, Dn_max - Uhrzeiten min/max fuer runter std:min # Up_val, Dn_val - zu sendene Werte, abhaengig von dpt 3/5 # Up_ofs, Dn_ofs - (opt) Offset hoch/runter in Minuten (0..59) # hshift - (opt) Verschiebung hoch/runter abh von Helligkeit um max +/- n Minuten # ga - GA Rolladenaktor # dpt - DPT 3 oder 5, Verschattung nur fuer 5 # gar - GA Rolladenaktor Rueck (unbenutzt) # ga_lockR - (opt) GA Sperr Rollade allgemein # ga_lockV - (opt) GA Sperr Verschattung # ga_lockC - (opt) GA Sperr Kontakt # az1, az2 - (opt) Azimuth Sonnenwinkel (Grad, horiz.) start/stop fuer Verschattung # el - (opt) Elevation Sonnenwinkel (Grad, vert.) start fuer Verschattung my @RollCfg; push @RollCfg, { name => "Rolle_SO", Up_min => '7:30', Up_max => '8:30', Up_val => 0, Dn_min => '17:30', Dn_max => '20:30', Dn_val => 100, az1 => 90, az2 => 210, el => 10, ga => '1/2/32', dpt => 5, gar => '1/2/33', ga_lockR => '1/2/200' }; push @RollCfg, { name => "Rolle_SW", Up_min => '9:30', Up_max => '9:30', Up_val => 0, Dn_min => '17:30', Dn_max => '20:45', Dn_val => 100, az1 => 165, az2 => 285, el => 10, ga => '1/2/42', dpt => 5, gar => '1/2/43', ga_lockR => '1/2/200' }; push @RollCfg, { name => "olleRolle", Up_min => '7:30', Up_max => '8:00', Up_val => 0, Dn_min => '17:00', Dn_max => '21:00', Dn_val => 1, Up_ofs => 1, Dn_ofs => -1, ga => '1/1/81', dpt => 3, gar => '', ga_lockR => '' }; ######################## ## ENDE Einstellungen ## ######################## # Konstanten fuer Aufrufart use constant EVENT_RESTART => 'restart'; use constant EVENT_MODIFIED => 'modified'; use constant EVENT_BUS => 'bus'; use constant EVENT_SOCKET => 'socket'; use constant EVENT_CYCLE => 'cycle'; use POSIX; use Time::Local; # Aus welchem Grund laeuft das Plugin gerade my $gv_event=undef; if (!$plugin_initflag) { $gv_event = EVENT_RESTART; # Restart des daemons / Reboot } elsif ($plugin_info{$plugname.'_lastsaved'} > $plugin_info{$plugname.'_last'}) { $gv_event = EVENT_MODIFIED; # Plugin modifiziert } elsif (%msg) { $gv_event = EVENT_BUS; # Bustraffic } elsif ($fh) { $gv_event = EVENT_SOCKET; # Netzwerktraffic } else { $gv_event = EVENT_CYCLE; # Zyklus } if ($gv_event eq EVENT_RESTART) { } elsif ($gv_event eq EVENT_MODIFIED) { $plugin_info{$plugname.'_cycle'} = $cycletime; # Aufruf-Zyklus setzen if ($abwesend_ga ne '') { $plugin_subscribe{$abwesend_ga}{$plugname} = 1; } # Variableninitialisierung falls bisher nicht vorhanden if (!(exists $plugin_info{$plugname.'_HellState'})) { $plugin_info{$plugname.'_HellState'} = 0; } foreach my $element (@RollCfg) { if (!(exists $plugin_info{$plugname.'_'.$element->{name}.'state'})) { $plugin_info{$plugname.'_'.$element->{name}.'state'} = -1; # erzeugen, falls nicht vorhanden } } } elsif ($gv_event eq EVENT_BUS) { if ($msg{'apci'} eq "A_GroupValue_Write" and $msg{'dst'} eq $abwesend_ga and ($msg{'data'} == 1)) { # Abwesendheit ist ein foreach my $element (@RollCfg) { if (exists $element->{ga_lockV} && ($element->{ga_lockV} ne '')) { # loesche alle Verschattungs Sperrungen if (knx_read($element->{ga_lockV},0,1) ) { knx_write($element->{ga_lockV},0,1); } } if ($element->{ga_lockR} ne '') { # loesche alle Sperren hoch/runter if (knx_read($element->{ga_lockR},0,1) ) { knx_write($element->{ga_lockR},0,1); } } } } } elsif ($gv_event eq EVENT_SOCKET) { } elsif ($gv_event eq EVENT_CYCLE) { my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); if ($plugin_info{$plugname.'_lastCall'} == $min) { return 0; # in dieser Minute bereits gelaufen } # Module laden use Astro::Coord::ECI; use Astro::Coord::ECI::Sun; use Astro::Coord::ECI::TLE; use Astro::Coord::ECI::Utils qw{rad2deg deg2rad}; my @sunrise=split ':', $plugin_info{'SunRiseSet'.'_sunrise'}; # Zeit Sonnenaufgang als xx:yy my @sunset=split ':', $plugin_info{'SunRiseSet'.'_sunset'}; # Zeit Sonnenuntergang als xx:yy my $verschattung; # Verschattung jahreszeitabh if (($mon <=1) || ($mon >=10)) { # Nov-Feb $verschattung = $verschattung3; } elsif (($mon <=4) || ($mon >=8)) { # Feb-May, Sep-Oct $verschattung = $verschattung2; } else { # Jun-Aug $verschattung = $verschattung1; } # Aktuelle Zeit my $time = time (); # aktueller Sonnenstand abh von eigenen Koordinaten my $loc = Astro::Coord::ECI->geodetic(deg2rad($lat), deg2rad($lon), $elev / 1000); my $sun = Astro::Coord::ECI::Sun->universal($time); my ($azimuth, $elevation, $range) = $loc->azel($sun); my $active=(knx_read($verschattung_ga,0,1) == 1); # Verschattung global ein/aus #my $abwesend=knx_read($abwesend_ga,0,1); my $hellJetzt = knx_read($helligkeit_ga,0,9); # aktuelle Helligkeit my $f=1.0; # Korrekturfaktor abh von Azimuth my $a; my $v; while (($a, $v) = each(%hcorr)){ if ($a > $azimuth) { last; } $f = $v; } $hellJetzt = $hellJetzt * $f; if ($hellJetzt > 8192) { $hellJetzt = 8192; # Begrenzung direkte Sonne } #plugin_log($plugname,"Helligkeit: " . $hellJetzt . " Avg " . $hellavg); my $hellavg = $plugin_info{$plugname.'_HellAvg'}; $hellavg = ($hellavg * ($loops -1) + $hellJetzt) / $loops; knx_write($helligkeit_gaw,$hellavg ,9); my $hellState = $plugin_info{$plugname.'_HellState'}; if ($hellState == 1) { if ($hellavg < $vlimit_lo ) { $hellState = 0; # Verschattung aus } } else { if ($hellavg > $vlimit_hi ) { $hellState = 1; # Verschattung ein } } $plugin_info{$plugname.'_HellState'} = $hellState; foreach my $element (@RollCfg) { if ($element->{ga_lockR} ne '') { # Rollade rauf/runter gesperrt if (knx_read($element->{ga_lockR},0,1) ) { next; } } if (exists $element->{ga_lockK} && ($element->{ga_lockK} ne '')) { # Kontakt sperrt if (knx_read($element->{ga_lockK},0,1) ) { next; } } my $tcalc; # berechnete Zeit in Minuten my @fmin; my @fmax; if ($hour <= 12) { # Sonnenaufgang $tcalc=$sunrise[0]*60 + $sunrise[1]; if (exists $element->{hshift}) { if ($hellJetzt < $rlimit_lo) { # laenger dunkel $tcalc = $tcalc + $element->{hshift}; } if ($hellJetzt > $rlimit_hi) { # frueher hell $tcalc = $tcalc - $element->{hshift}; } } @fmin=split ':', $element->{Up_min}; @fmax=split ':', $element->{Up_max}; } else { # Sonnenuntergang $tcalc=$sunset[0]*60 + $sunset[1]; if (exists $element->{hshift}) { if ($hellJetzt < $rlimit_lo) { # frueher dunkel $tcalc = $tcalc - $element->{hshift}; } if ($hellJetzt > $rlimit_hi) { # laenger hell $tcalc = $tcalc + $element->{hshift}; } } @fmin=split ':', $element->{Dn_min}; @fmax=split ':', $element->{Dn_max}; } my $tmax = $fmax[0]*60 + $fmax[1]; if ($tcalc > $tmax ) { # fahren zeitlich nach frueher begrenzen $tcalc = $tmax; } my $tmin = $fmin[0]*60 + $fmin[1]; if ($tcalc < $tmin ) { # fahren zeitlich nach spaeter begrenzen $tcalc = $tmin; } #plugin_log($plugname,"Name: " . $element->{name} . $hellavg . int($tcalc/60) . ":" . ($tcalc % 60) ); if ($hour <= 12) { # Sonnenaufgang if (exists $element->{Up_ofs}) { $tcalc = $tcalc + $element->{Up_ofs}; # offset } if ( ($plugin_info{$plugname.'_'.$element->{name}.'state'} >= 90) && ( ($hour*60 + $min) > $tcalc ) ) { knx_write($element->{ga},$element->{Up_val},$element->{dpt}); usleep(20000); plugin_log($plugname,"Name: " . $element->{name} . " auffahren " . $hellJetzt ); $plugin_info{$plugname.'_'.$element->{name}.'state'} = 0; } } else { # Sonnenuntergang if (exists $element->{Dn_ofs}) { $tcalc = $tcalc + $element->{Dn_ofs}; # offset } if ( ($plugin_info{$plugname.'_'.$element->{name}.'state'} < 90) && ( ($hour*60 + $min) > $tcalc ) ) { knx_write($element->{ga},$element->{Dn_val},$element->{dpt}); usleep(20000); plugin_log($plugname,"Name: " . $element->{name} . " zufahren " . $hellJetzt ); $plugin_info{$plugname.'_'.$element->{name}.'state'} = 100; } } # nur falls aktiv, prozentuales Fahren moeglich und nicht (fast) komplett runter if ( $active && ($element->{dpt} == 5) && ($plugin_info{$plugname.'_'.$element->{name}.'state'} < 90) ) { my $fahre = 0; my $skip = 0; # Die Einfallwinkel in Radians umrechnen my $azimuth1 = deg2rad($element->{az1}); my $azimuth2 = deg2rad($element->{az2}); my $elev = deg2rad($element->{el}); # Teste ob das Fenster beschienen wird my $testJetztBeschienen = ($azimuth > $azimuth1 && $azimuth < $azimuth2 && $elevation > $elev) || 0; my $testJetztSonne = $hellState; if ($testJetztBeschienen && $testJetztSonne ) { $fahre = $verschattung; } my $state = $plugin_info{$plugname.'_'.$element->{name}.'state'}; #plugin_log($plugname,"Name: " . $element->{name} . "_" . $fahre . "_" . $state . "-" . $hellavg ); if (exists $element->{ga_lockV} && ($element->{ga_lockV} ne '')) { if (knx_read($element->{ga_lockV},0,1) ) { $skip = 1; } } if (($state != $fahre) && !$skip) { knx_write($element->{ga},$fahre,$element->{dpt}); plugin_log($plugname,"Name: " . $element->{name} . "Fahre " . $fahre . " Az " .$azimuth . " El ". $elevation); $plugin_info{$plugname.'_'.$element->{name}.'state'} = $fahre; } } next; } $plugin_info{$plugname.'_HellAvg'} = $hellavg; # neu berechnete Durchschnittshelligkeit seit letzten Start $plugin_info{$plugname.'_lastCall'} = $min; # merke Minute des letzten Aufrufs $plugin_info{$plugname.'_lastAzimuth'} = rad2deg($azimuth); # only for debugging $plugin_info{$plugname.'_lastElevation'} = rad2deg($elevation); # only for debugging } return 0;
Funktionen:
- Rollade hoch/runter nach Sonnenauf/-untergang
- helligkeitsabh. Verschiebung um gewisse Zeit
- Begrenzung jeweils min/max
- Offset je Rollade
- hoch runter für DPT=3, zusaetzlich Verschattung falls DPT=5
- Verschattung nach Über-/unterschreiten Min/max-Helligkeitswert
- Mittelwertbildung Helligkeit für Verschattung, opt Korrekturfaktor abh. von Azimuth
- Sperre Rollade hoch/runter, Verschattung, Kontakt
- Sperre Verschattung global
- Abwesenheitsmodus deaktiviert Fahr-/Verschattungssperre
- Drei Stufen der Verschattung in Abhaengigkeit der Jahreszeit
- todo: Wenn Rollade von Hand bewegt wurde, schalte Verschattungssperre ein?
Zur Übernahme sind natürlich einige Anpassungen (GAs, Zeiten, Winkel, Schwellen) nötig, wofür Debugausgaben und rrd-Diagramme hilfreich sind. Ich empfehle, die knx_writes rauszunehmen und das Fahren eine zeitlang zu beobachten und dabei die Parameter zu justieren. Auf eine Auslagerung der Parameter in eine Config-Datei habe ich verzichtet.
Im Moment wird der tatsächliche Stand der Rollade nicht berücksichtigt, d.h. Variable _xxxstate und Aktorrückmeldung werden sich unterscheiden, wenn man die Rollade von Hand verfährt. Das hat gewisse Vor- und Nachteile.
Sorry für das verwendete Denglish, es macht die Variablen etc. prägnanter und kürzer

Falls Interesse, viel Spass!