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!

