Navigation

    Logo
    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Unread
    • Categories
    • Unreplied
    • Popular
    • GitHub
    • Docu
    • Hilfe
    1. Home
    2. Deutsch
    3. Praktische Anwendungen (Showcase)
    4. [Javascript+Blockly] Markise automatisiert nach Sonnenstand

    NEWS

    • ioBroker@Smart Living Forum Solingen, 14.06. - Agenda added

    • ioBroker goes Matter ... Matter Adapter in Stable

    • Monatsrückblick - April 2025

    [Javascript+Blockly] Markise automatisiert nach Sonnenstand

    This topic has been deleted. Only users with topic management privileges can see it.
    • L
      localhorst last edited by

      Nachdem ich einen Motor in unsere Markise eingebaut habe, wollte ich sie dann auch automatisiert nach Sonnenstand fahren lassen. So, dass sie immer nur so weit ausfährt, dass unser Balkon komplett beschattet ist.
      So ist die (Einbau-) Situation bei uns (Sonne ist gerade herum gekommen):
      IMG_5144.jpeg
      Letztes Jahr hatte ich mir ein Blockly Script gebastelt, dass mit den aktuellen Sonnenwinkeln (Azimut + Elevation) annäherungsweise automatisiert fährt. Das funktioniert so leidlich, vor allem je weiter das Jahr voran schreitet. Auch ist die Situation - die Sonne steht fast parallel zum Balkon - sehr schwer abzubilden. Hier spielt der Azimut eine größere Rolle als die Elevation der Sonne.
      Also wollte ich rechnen. Mathematisch ist klar, es braucht die Komponenten Azimut und Elevation der Sonne, Montagehöhe der Markise, horizontaler Winkel der Markise (in welchem Winkel sie beim ausfahren nach unten abfällt), Ausrichtung / Himmelsrichtung des Balkons und die maximale Ausfahrlänge der Markise. Das Ergebnis muss in Prozent umgerechnet werden, um die Markise fahren zu lassen.
      Der Grundgedanke: es muss ein vektorieller Ansatz gefahren werden. Die Vorderkante des Balkons muss als unendliche Linie betrachtet werden, von dort muss ein Vektor aus Azimut und Elevation zur Sonne gebildet werden. Die Vorderkante der Markise muss ebenfalls als Linie betrachtet werden und es muss berechnet werden, wann diese Line beim ausfahren unter dem Ausfallwinkel den Vektor unterbricht.
      Da dachte ich mir: hui, recht komplex, aber prima, ein tolles Projekt um mal zu gucken, was KIs so hergeben.
      Ich habe also mehrere KIs tagelang gestresst und es kam viel Müll dabei raus. Die haben mir mit fester Überzeugung haufenweise Quatsch erzählt und ausgegeben. Aber es war sehr interessant damit zu arbeiten und zu lernen wie die so "ticken". Auch Simulationen konnte man sich prima erstellen lassen.
      Am Ende war Cursor die einzige KI, die mit ein paar Anpassungen vektorbasiert die Lösung brachte. Für die Simulation war Lovable ziemlich prima.

      Nun ist es im echten Leben implementiert, getestet und funktioniert hervorragend. Die Markise fährt zentimetergenau über die gesamte Dauer.
      Da ich nirgendwo eine fertige Lösung gefunden habe, möchte ich das euch hier zur Verfügung stellen:

      Am Ende besteht die Komplettlösung aus 2 JavaScript (1x Sonnenwinkel berechnen und 1x die nötige Markisenposition berechnen) und einem Blockly Script zum ansteuern der Markise.

      Benötigte Datenpunkte:

      javascript.0.Sonnenstand.Azimut
      javascript.0.Sonnenstand.Elevation
      
      0_userdata.0.Markise_Automatik.Markisenposition
      0_userdata.0.Markise_Automatik.Markise_Automatik
      

      Das Script zur Berechnung der Sonnenwinkel (die Quelle weiß ich leider nicht mehr):

      const suncalc = require('suncalc');
      const result = getObject("system.adapter.javascript.0");
      const lat = result.native.latitude;
      const long = result.native.longitude;
      const idEle = 'Sonnenstand.Elevation';
      const idAzi = 'Sonnenstand.Azimut';
       
      createState(idEle, 0, {type: 'number', unit: '°'});
      createState(idAzi, 0, {type: 'number', unit: '°'});
       
      function Sonnenstand_berechnen () {
          var now = new Date();
          var sunpos = suncalc.getPosition(now, lat, long);
          var h = sunpos.altitude * 180 / Math.PI;
          var a = sunpos.azimuth * 180 / Math.PI + 180;
          setState(idEle, Math.round(10 * h) / 10, true);
          setState(idAzi, Math.round(a), true);
      }
       
      schedule("* * * * *", Sonnenstand_berechnen); // jede Minute
      

      Das Script zur Berechnung der benötigten Markisenposition:

      // Konstanten
      const offsetKorrektur = 10;        // Korrekturwert für die reale/praktische Anpassung in %
      const maxMarkisenAusfahrt = 2.95;  // Die maximale Ausfahrlänge der Markise in Meter
      const balkonAusrichtung = 242;    // Die Ausrichtung / Himmelsrichtung des Balkons in Grad
      const neigungswinkel = 18;        // Der Neigungswinkel der Markise beim ausfahren in Grad
      const hMontage = 2.38;             // Montagehöhe der Markise über dem Balkon in Meter
      const minElevation = 12;          // Die Sonnenhöhe / Elevation, ab der die Markise irrelevant wird (z.B. hinter Bäumen verschwunden) in Grad. Steht die Sonne niedriger, fährt die Markise ein
      
      schedule("*/1 * * * *", function () {
          // Aktuelle Sonnenpositionen auslesen
          let azimut = getState("javascript.0.Sonnenstand.Azimut").val;
          let elevation = getState("javascript.0.Sonnenstand.Elevation").val;
          
          // Berechnung des minimalen Azimuts (Sonne parallel zum Balkon)
          let minAzimut = balkonAusrichtung - 90;
          
          // Wenn Elevation unter Minimum oder Azimut zu niedrig, Markise einfahren
          if (elevation < minElevation || azimut < minAzimut) {
              setState("0_userdata.0.Markise_Automatik.Markisenposition", 0);
              return;
          }
          
          // Winkel zwischen Balkonausrichtung und Sonne
          let relativeAzimut = Math.abs(azimut - balkonAusrichtung);
          
          if (relativeAzimut > 180) {
              setState("0_userdata.0.Markise_Automatik.Markisenposition", 0);
              return;
          }
      
          // Umrechnung in Radianten
          let elevationRad = elevation * Math.PI / 180;
          let relativeAzimutRad = relativeAzimut * Math.PI / 180;
          let neigungRad = neigungswinkel * Math.PI / 180;
      
          // Sonnenvektor in kartesischen Koordinaten (normalisiert)
          // x: Ost-West
          // y: Nord-Süd
          // z: Höhe
          let sonnenVektor = {
              x: Math.cos(elevationRad) * Math.sin(relativeAzimutRad),
              y: Math.cos(elevationRad) * Math.cos(relativeAzimutRad),
              z: Math.sin(elevationRad)
          };
      
          // Berechnung der benötigten Ausfahrlänge durch Schnittpunkt des Sonnenvektors
          // mit der geneigten Markisenebene
          let schnittFaktor = hMontage / (sonnenVektor.z + Math.tan(neigungRad) * sonnenVektor.y);
          let benoetigteAusfahrlaenge = Math.abs(schnittFaktor * sonnenVektor.y);
      
          // Begrenzung auf maximale Ausfahrlänge
          benoetigteAusfahrlaenge = Math.min(Math.max(0, benoetigteAusfahrlaenge), maxMarkisenAusfahrt);
      
          // Umrechnung in Prozent und Rundung
          let markisenPosition = Math.round((benoetigteAusfahrlaenge / maxMarkisenAusfahrt) * 100);
          
          // Offset-Korrektur hinzufügen und auf gültige Prozentwerte begrenzen
          markisenPosition = Math.min(Math.max(0, markisenPosition + offsetKorrektur), 100);
          
          // Setzen der Markisenposition
          setState("0_userdata.0.Markise_Automatik.Markisenposition", markisenPosition);
      });
      

      Das Blockly Script zum fahren der Markise. Hier wird im 5% Takt gefahren und die Automatik zurückgesetzt, wenn die Sonne zu tief steht.
      Bildschirmfoto 2025-04-26 um 13.04.04.png
      Hier als Code zum importieren:

      <xml xmlns="https://developers.google.com/blockly/xml">
        <block type="on_ext" id="d[M{].$K-os4mv.?MSd:" x="-7937" y="-4337">
          <mutation xmlns="http://www.w3.org/1999/xhtml" items="2"></mutation>
          <field name="CONDITION">any</field>
          <field name="ACK_CONDITION"></field>
          <value name="OID0">
            <shadow type="field_oid" id="AH]apV/o5,U4$jH3TJLi">
              <field name="oid">0_userdata.0.Markise_Automatik.Markise_Automatik</field>
            </shadow>
          </value>
          <value name="OID1">
            <shadow type="field_oid" id="HMZ-~kOaPMKu!NQO:69f">
              <field name="oid">0_userdata.0.Markise_Automatik.Markisenposition</field>
            </shadow>
          </value>
          <statement name="STATEMENT">
            <block type="controls_if" id="qD3^6mWBMyl*mSmH3R|F">
              <value name="IF0">
                <block type="logic_compare" id="*5RHvT~q1T`ryqXN]VMy">
                  <field name="OP">EQ</field>
                  <value name="A">
                    <block type="get_value" id="ciuiV0mX:C;D$s+S0/0t">
                      <field name="ATTR">val</field>
                      <field name="OID">0_userdata.0.Markise_Automatik.Markise_Automatik</field>
                    </block>
                  </value>
                  <value name="B">
                    <block type="logic_boolean" id="Z}pQy~?R%09F|s-waDMv">
                      <field name="BOOL">TRUE</field>
                    </block>
                  </value>
                </block>
              </value>
              <statement name="DO0">
                <block type="comment" id="F6^`S[I}+uS0=M+Y6wWN">
                  <field name="COMMENT">Erst wenn Balkon beschienen wird</field>
                  <next>
                    <block type="controls_if" id="8-.7D2w3CFpr7yuO:stY">
                      <value name="IF0">
                        <block type="logic_compare" id="8$97L@Oh:Jy73-et+`A3">
                          <field name="OP">GTE</field>
                          <value name="A">
                            <block type="get_value" id="^T*/,8rrY*hJ0b@h*5oK">
                              <field name="ATTR">val</field>
                              <field name="OID">javascript.0.Sonnenstand.Azimut</field>
                            </block>
                          </value>
                          <value name="B">
                            <block type="math_number" id="K27sF+@OllSm%%,5STZD">
                              <field name="NUM">152</field>
                            </block>
                          </value>
                        </block>
                      </value>
                      <statement name="DO0">
                        <block type="comment" id="]RJA7iQN:Dx,smN,:[l#">
                          <field name="COMMENT">Im 5% Takt fahren</field>
                          <next>
                            <block type="controls_if" id="gQ2/Mrz6@YW:Ym3N/+^R">
                              <value name="IF0">
                                <block type="math_number_property" id="lCMXwC6Kq^H:3T.`xg{:">
                                  <mutation divisor_input="true"></mutation>
                                  <field name="PROPERTY">DIVISIBLE_BY</field>
                                  <value name="NUMBER_TO_CHECK">
                                    <shadow type="math_number" id="OlH*#S%O5%;W;s4al5Zc">
                                      <field name="NUM">0</field>
                                    </shadow>
                                    <block type="get_value" id="HHA42-CFwe*A4m0+LEAV">
                                      <field name="ATTR">val</field>
                                      <field name="OID">0_userdata.0.Markise_Automatik.Markisenposition</field>
                                    </block>
                                  </value>
                                  <value name="DIVISOR">
                                    <block type="math_number" id="sO4;cuoVz9%m,KRYC=Xc">
                                      <field name="NUM">5</field>
                                    </block>
                                  </value>
                                </block>
                              </value>
                              <statement name="DO0">
                                <block type="control" id="uJr4$3Rqe1-RqJRe)JRg">
                                  <mutation xmlns="http://www.w3.org/1999/xhtml" delay_input="false"></mutation>
                                  <field name="OID">hm-rpc.1.00111BE9A5D588.4.LEVEL</field>
                                  <field name="WITH_DELAY">FALSE</field>
                                  <value name="VALUE">
                                    <block type="get_value" id="VQ{Czk9bgO3-N(8XTcrf">
                                      <field name="ATTR">val</field>
                                      <field name="OID">0_userdata.0.Markise_Automatik.Markisenposition</field>
                                    </block>
                                  </value>
                                </block>
                              </statement>
                              <next>
                                <block type="comment" id="-}Rn`FeJhKPU|-bt7DXZ">
                                  <field name="COMMENT">Schatten - Markise einfahren und Variable zurücksetzen</field>
                                  <next>
                                    <block type="controls_if" id="}/9L3?6@_{jm*(xDKXI-">
                                      <value name="IF0">
                                        <block type="logic_compare" id="L-c(MDVdl#!xsz$nlGjV">
                                          <field name="OP">LTE</field>
                                          <value name="A">
                                            <block type="get_value" id="JJC8+~EL0K?s/8)$Ub:r">
                                              <field name="ATTR">val</field>
                                              <field name="OID">javascript.0.Sonnenstand.Elevation</field>
                                            </block>
                                          </value>
                                          <value name="B">
                                            <block type="math_number" id="9nYCzkWBej`bL];!-n*S">
                                              <field name="NUM">12</field>
                                            </block>
                                          </value>
                                        </block>
                                      </value>
                                      <statement name="DO0">
                                        <block type="control" id="]o0JlQ^PNyWnodC##s/,">
                                          <mutation xmlns="http://www.w3.org/1999/xhtml" delay_input="false"></mutation>
                                          <field name="OID">0_userdata.0.Markise_Automatik.Markise_Automatik</field>
                                          <field name="WITH_DELAY">FALSE</field>
                                          <value name="VALUE">
                                            <block type="logic_boolean" id=";T{Q-cJ=X/zzafvV=avi">
                                              <field name="BOOL">FALSE</field>
                                            </block>
                                          </value>
                                          <next>
                                            <block type="control" id="#=eGTE0ogeWm;);D1wS}">
                                              <mutation xmlns="http://www.w3.org/1999/xhtml" delay_input="false"></mutation>
                                              <field name="OID">hm-rpc.1.00111BE9A5D588.4.LEVEL</field>
                                              <field name="WITH_DELAY">FALSE</field>
                                              <value name="VALUE">
                                                <block type="math_number" id="|bY:?e@],H5BEbXE($$U">
                                                  <field name="NUM">0</field>
                                                </block>
                                              </value>
                                            </block>
                                          </next>
                                        </block>
                                      </statement>
                                    </block>
                                  </next>
                                </block>
                              </next>
                            </block>
                          </next>
                        </block>
                      </statement>
                      <next>
                        <block type="comment" id="]%/=E7Rpfk*xc%6KpkyS">
                          <field name="COMMENT">Regen etc...</field>
                        </block>
                      </next>
                    </block>
                  </next>
                </block>
              </statement>
            </block>
          </statement>
        </block>
      </xml>
      

      Vielleicht nützt das ja dem ein oder anderen, der seine Markise auch so fahren lassen möchte.
      Viel Spaß!

      1 Reply Last reply Reply Quote 1
      • First post
        Last post

      Support us

      ioBroker
      Community Adapters
      Donate

      981
      Online

      31.7k
      Users

      79.7k
      Topics

      1.3m
      Posts

      1
      1
      106
      Loading More Posts
      • Oldest to Newest
      • Newest to Oldest
      • Most Votes
      Reply
      • Reply as topic
      Log in to reply
      Community
      Impressum | Datenschutz-Bestimmungen | Nutzungsbedingungen
      The ioBroker Community 2014-2023
      logo