fhem / AutoShuttersControl

Module for controlling shutters depending on various conditions
GNU General Public License v2.0
8 stars 6 forks source link

Shading erweitern für Dachfenster #42

Open der-oBi opened 5 years ago

der-oBi commented 5 years ago

Hey, wie schon im Forum kurz andiskutiert, würde ich mich sehr freuen, wenn die Beschattungssteuerung so geändert würde, dass auch Dachfenster ideal beschattet werden können.

Als kurze Erklärung: Nur mit festen Grenzen für Azimuth/Elevation kommt man bei der Beschattung Dachfenstern leider nicht weit.

Beispiel: Dachfenster zeigt nach Norden (ASC_Shading_Pos = 0). Sonne steht voll im Süden (azimuth=180). Sonne steht bei Elevation 60°, Dachneigung wäre 30°. Obwohl die Sonne auf der gegenüberliegenden Seite steht, würde sie dennoch ins Fenster scheinen (mit einem Einfallswinkel von 30°). Das wird leider derzeit nicht erfasst.

Meine Idee, wie ich sie bisher (wohl gemerkt schlecht programmiert) in meinen myUtils umgesetzt habe: Zusätzlich zum bisherigen Vorgehen müsste für jedes Fenster neben ASC_Shading_Pos die Neigung des Fensters zur Horizontalen angegeben werden (bei einem Wandfenster also per default 90°). Danach wird das Fenster und der Sonnenstand als 3 dimensionaler Vektor dargestellt und der tatsächliche Einfallwinkel berechnet: ("sonnenstand" ein einfaches twilight Device)

my $sun_az_grad = ReadingsVal("sonnenstand","azimuth",0) ; my $sun_el_grad = ReadingsVal("sonnenstand","elevation",0) ; my $sun_az_rad = $sun_az_grad 3.14 / 180 ; my $sun_el_rad = $sun_el_grad 3.14 / 180 ; my $x_sun = sin($sun_az_rad) cos($sun_el_rad) ; my $y_sun = cos($sun_az_rad) cos($sun_el_rad) ; my $z_sun = sin($sun_el_rad) ;

$ausrichtung_az = $grenzen[0] / 180 3.14 ; #### entspricht ASC_Shading_Pos $ausrichtung_el = $grenzen[1] / 180 3.14 ; #### entspricht der Dachneigung $x_fenster = sin($ausrichtung_az) cos($ausrichtung_el) ; $y_fenster = cos($ausrichtung_az) cos($ausrichtung_el) ; $z_fenster = sin($ausrichtung_el) ;

$schnittwinkel = max ( 0, ( asin ( $x_fenster $x_sun + $y_fenster $y_sun + $z_fenster $z_sun ) ) 180 / 3.14 ) ;

Der tatsächlich berechnete Schnittwinkel könnte dann mit einem Maximal-Schnittwinkel verglichen werden (bei mir z.B. 15°) und wenn der überschritten wird, soll beschattet werden.

Funktioniert bei mir seit 2 Jahren tadellos (auch für Wandfenster). Aber die vielen DOIFs und myUtils sind nicht halb so schick wie dein ASC-Modul :-)

PS: Die festen Grenzen (Shading_Angle, MinMaxElevation) sollten dennoch bleiben und weiterhin abgefragt werden um beispielsweise Beschattung durch nahe stehende Bäume/Häuser mitzunehmen.

CoolTuxNet commented 5 years ago

Ich danke Dir. Ich werde es mir in den kommenden Wochen einmal genauer anschauen.

der-oBi commented 5 years ago

Freut mich. Wenn du Fragen hast, sag einfach Bescheid.

Ich muss allerdings einen kleinen Schönheitsfehler in meinen Ausführungen korrigieren: So wie ich es derzeit umgesetzt habe, gibt $ausrichtung_el nicht die Dachneigung in Relation zur Horizontalen wieder, sondern in Relation zur Vertikalen. Somit ist in meiner Umsetzung ein Wandfenster also nicht per default mit $ausrichtung_el=90 sondern mit $ausrichtung_el=0 (also parallel zur Vertikalen) umzusetzen. Ist aber eine reine Verständnissache. Könnte man auch einfach ändern, indem man 2mal cos gegen sin tauscht ;-)

linuzer commented 5 years ago

Hallo, sorry, ich hatte Deine Antwort im Forum übersehen... Hier also wie gewünscht mein Script, das glaube ich einen ganz ähnlichen Ansatz verfolgt wie das oben von der-oBi. Die Grundangaben pro Fenster könnten als Readings bei den Rollläden abgelegt werden. Die Logik funktioniert gleichermaßen für vertikale Wandfenster, als auch für Dachfenster. Funktioniert bei mir hervorragend...

Die Idee ist, es wird sowohl der Sonneneinfall als Vektor beschrieben, als auch der Rollo-Fahrweg. Damit kann man dann den Winkel der beiden Vektoren berechnen. Damit und mit den Infos wie groß der Fensterausschnitt, sowie eventuelle Mauervorsprünge oder Aufbauten sind, läßt sich relativ exakt ausrechnen, wie weit der Rollo fahren muß, um das Fenster in der gewünschten Weise zu beschatten.

Würde mich sehr freuen, wenn Du das irgendwie in das Modul integrieren könntest! Wenn Du irgendwelche Fragen hast, sag Bescheid!

######### Rolladensteuerung
sub RolladenHitze 
{
    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
    my $Elev = ReadingsVal('MyTwilight', 'elevation', '0');
    my $Azim = ReadingsVal('MyTwilight', 'azimuth', '0');

    Log 1, "Elev: $Elev";
    Log 1, "Azim: $Azim";

    # Fensterdefinitionen (DevName, Breite, Höhe, Tiefe, Dachwinkel, Aufbauhöhe, Fensterausrichtung, Aktiv)
        # 0, DeviceName des Rollos
        # 1, 2: Breit, Höhe (cm) = sichtbare Breite, Höhe des Fensters (Glas innerhalb des Rahmens)
        # 3: Tiefe (cm) = Wie weit darf die Sonne "ins Fenster" scheinen (vom Fensteraussenscheibe messen!)
        # 4: Dachwinkel (Grad) = native Neigung der Fensterscheibe (=Dach), bzw. 90° bei "normalen" Wandfenstern
        # 5: Aufbauhöhe (cm) = Wieviel cm tragen Rollläden, Wandvorsprünge, etc. zur Fensterscheibe auf?
        # 6: Fensterausrichtung (Grad) = In welche Himmelsrichtung "schaut" das Fenster? -> Senkrechte auf die Scheibe, Nord = 0°, Süd = 180°
        # 7: Min Azim. (Grad) = ab welchem Azimut wird beschattet?
        # 8: Max Azim. (Grad) = bis welchem Azimut wird beschattet?
        # 9: Min Elev. (Grad) = ab welcher Elevation wird beschattet?
        # 10: Max Elev. (Grad) = bis welcher Elevation wird beschattet?
        # 11: Min Schatten (%) = minimale schattenrelevante Rollo-Position (z.B. 12%)
        # 12: Max Schatten (%) = maximale schattenrelevante Rollo-Position (z.B. 98%)
        # 13: Aktiv = 0 -> Deaktiviert, 1 -> Aktiv
    my @BU_S = ("BU_RolloS", 77, 98, 15, 39, 13, 276, 90, 360, 25, 90, 16, 100, 1);
    my @BU_N = ("BU_RolloN", 77, 98, 15, 39, 13, 276, 90, 360, 22, 90, 16, 100, 1);
    my @KU = ("KU_Rollo", 77, 100, 15, 39, 13, 276, 90, 360, 21, 90, 16, 100, 1);
    my @WC = ("WC_Rollo", 60, 90, 15, 39, 13, 276, 90, 360, 20, 90, 16, 100, 1);
    my @SZ = ("SZ_Rollo", 300, 200, 25, 90, 15, 96, 0, 186, -10, 90, 14, 100, 1);
    my @WZ = ("WZ_Rollo", 300, 200, 25, 90, 15, 96, 0, 186, 0, 90, 14, 100, 1);
    my @GA = ("GA_Rollo", 105, 130, 15, 39, 13, 96, 0, 270, -10, 90, 13, 100, 1);
    my @GZ_O = ("GZ_RolloO", 105, 130, 15, 39, 13, 96, 0, 270, 0, 90, 13, 100, 1);
    my @GZ_W = ("GZ_RolloW", 125, 130, 15, 39, 13, 276, 90, 360, 15, 90, 14, 100, 1);

    my @ArrFenster = (\@BU_S, \@BU_N, \@KU, \@WC, \@SZ, \@WZ, \@GA, \@GZ_O, \@GZ_W);

    # Array-Zugriffskonstanten
    my $C_DEVNAME = 0;
    my $C_BREITE = 1;
    my $C_HOEHE = 2;
    my $C_TIEFE = 3;
    my $C_DACHWINKEL = 4;
    my $C_AUFBAU = 5;
    my $C_F_AUSRICHTUNG = 6;
    my $C_MINAZIM = 7;
    my $C_MAXAZIM = 8;
    my $C_MINELEV = 9;
    my $C_MAXELEV = 10;
    my $C_MINPOS = 11;
    my $C_MAXPOS = 12;
    my $C_AKTIV = 13;

    # allgemeine Variablen/Konstanten
    my $pi = 3.1415926535897932;
    my @V_Rollo = (0, 0, 0);    # Vektor Rollo
    my @V_Sonne = (0, 0, 0);    # Vektor Sonne
    my $Aufbauwinkel = 0;
    my $Gesamtneigung = 0;
    my $Theta = 0;
    my $Phi = 0;
    my $Winkel = 0;
    my $RolloPos = 0;

    # rad = grad * pi / 180;        grad = rad * 180 / pi

    # Sonnenvektor berechnen
    $Theta = (90 + $Elev) * ($pi / 180);
    $Phi = $Azim * ($pi / 180);

    $V_Sonne[0] = sin($Theta) * sin($Phi);
    $V_Sonne[1] = cos($Theta);
    $V_Sonne[2] = sin($Theta) * cos($Phi) * -1;

    foreach (@ArrFenster) {
        my @Fenster = @$_;

        # Gesamtneigung berechnen
        $Aufbauwinkel = atan2($Fenster[$C_AUFBAU], $Fenster[$C_HOEHE]) * (180 / $pi);
        $Gesamtneigung = $Fenster[$C_DACHWINKEL] + $Aufbauwinkel;

        # Rollo-Vektor berechnen
        $Theta = (90 + $Gesamtneigung) * ($pi / 180);
        $Phi = $Fenster[$C_F_AUSRICHTUNG] * ($pi / 180);

        $V_Rollo[0] = sin($Theta) * sin($Phi) * -1;
        $V_Rollo[1] = cos($Theta);
        $V_Rollo[2] = sin($Theta) * cos($Phi) * -1;

        # Sonnenwinkel auf Fensterfläche berechnen (=Winkel der beiden Vektoren)
        $Winkel = acos(($V_Rollo[0] * $V_Sonne[0] + $V_Rollo[1] * $V_Sonne[1] + $V_Rollo[2] * $V_Sonne[2]) /
                  (sqrt($V_Rollo[0] ** 2 + $V_Rollo[1] ** 2 + $V_Rollo[2] ** 2) * 
                  sqrt($V_Sonne[0] ** 2 + $V_Sonne[1] ** 2 + $V_Sonne[2] ** 2)));

        # Rollo-Position bestimmen
        if ($Fenster[$C_MINAZIM] < $Azim && $Azim < $Fenster[$C_MAXAZIM] && 
            $Fenster[$C_MINELEV] < $Elev && $Elev < $Fenster[$C_MAXELEV]) {
            $RolloPos = ((($Fenster[$C_TIEFE] + $Fenster[$C_AUFBAU]) / tan($Winkel)) / $Fenster[$C_HOEHE]) * 100;
        } else {
            $RolloPos = 100;
        }

        if ($RolloPos < 0) {$RolloPos = 0};

        # Rollo-Pos auf schattenrelevanten Bereich skalieren
        $RolloPos = ($Fenster[$C_MAXPOS] - $Fenster[$C_MINPOS]) * ($RolloPos / 100) + $Fenster[$C_MINPOS];

        # Rollo auf Position fahren
        if ($Fenster[$C_AKTIV] == 1) {
            fhem("sleep 1; set ".@Fenster[$C_DEVNAME]." pct ".sprintf("%.0f", $RolloPos));
            Log 1, "Rollo ".$Fenster[$C_DEVNAME]." auf ".sprintf("%.0f", $RolloPos);
        }
    }
}
DrS332 commented 4 years ago

Hallo, ich versuche schön länger meine Dachfenster automatisch zu beschatten. Dabei möchte ich das Fenster nur soweit schließen wie notwendig. Der Code von Linuzer würde diese Aufgabe genau erfüllen.

Jetzt habe ich den Code in eine Excel Tabelle übertragen um herauszufinden ob das bei meiner Loxone Steuerung auch funktionieren würde.

Es schaut schon ziemlich gut aus. Nur mein Fenster im Westen (245•) will nie ganz zufahren obwohl die Sonne dort NM direkt rein scheint. Vielleicht hab ich die Parameter nicht richtig verstanden. Wie ist das mit AUFBAU und TIEFE gemeint?

Beste Grüße

linuzer commented 4 years ago

Also vorab, die Logik ist nicht 100% perfekt, denn es gibt wahrscheinlich immer Sonder- und Grenzfälle, in denen es nicht (mehr) perfekt funktioniert. Aber ich habe versucht einige Besonderheiten zu berücksichtigen. Dafür sind u.a. die beiden Parameter Aufbau und Tiefe gedacht:

Aufbau = Alles, was "über" (= im 90°-Winkel zur Fensterfläche) "hoch" steht. Bei Dachfenstern ist das z.B. der Rollladenkasten. Wenn die Sonne fast parallel zur Dachfläche scheint, bewirkt der Rollokoasten einen Schatten, der effektiv die Dachneigung erhöht. D.h. die Sonne muss höher stehen als ohne, um ins Fenster zu scheinen. Bei senkrechten Fenstern kann das aber auch ein Mauervorsprung über dem Fenster sein, im Extremfall z.B. ein Balkon über dem Fenster. All das bewirkt, dass der Rollladen bei einem anderen Sonnenwinkel reagieren muss, als bei einem "nackten" Fenster.

Tiefe = Bei mir sind die Rollläden 100% Lichtundurchlässig. D.h. wenn sie zu sind, muss ich das Licht einschalten. Das fühlt sich natürlich im Hochsommer am helligen Tag ziemlich blöd an, weshalb ich möchte, dass der Rolladen die Sonne nicht zu 100% abschattet, sondern zuläßt, dass die Sonne etwas in die Fensterlaibung nach innen scheint und damit das Zimmer noch etwas beleuchtet. Dabei muss man natürlich abwägen zwischen Hitze versus Licht. Der Parameter "Tiefe" steht dabei für das Maß in "cm", bis zu der die Sonne von der Fensterfläche nach innen kommen darf. Bei 20 cm z.B. sollte die Sonne noch 20 cm "rein" scheinen.

Hoffe, das hilft... LG linuzer

Edit: Ach so, ich seh grad ich hab das nirgends explizit erwähnt: Das ganze läuft bei mir als notify auf das Twilight-Device, d.h. alle paar Minuten, wenn sich der Sonnenstand ändert, werden alle Rollos in ihrer Position entsprechend angepaßt...

zivillian commented 3 years ago

@linuzer Ich habe ein Problem in dem Skript identifiziert, was bei flachen Dachfenstern zu extremen Abweichungen führt. Zwar wird die Ausrichtung des Fensters für die Berechnung des Winkels berücksichtigt, aber nicht für Berechnung der Öffnung. Ich habe hier ein Dachfenster mit Nordausrichtung, wo aber durch den flachen Winkel von ca. 30° in den Abendstunden im Sommer trotzdem die Sonne drauf steht. Durch den extremen Winkel zwischen Fensterausrichtung und Sonnenstand entstehen hier Abweichungen zwischen wenigen Zentimetern zulässiger Öffnung und fast einem Meter berechneter Öffnung.

Zur Veranschaulichung habe ich hier eine Skizze des Fehlers: Fenster