Ankündigung

Einklappen
Keine Ankündigung bisher.

Rolladensteuerung mit Verschattung

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    [WireGate-Plugin] Rolladensteuerung mit Verschattung

    Anbei meine aktuelle Lösung zur Rolladensteuerung inklusive Verschattung.

    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;
    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!
Lädt...
X