Ankündigung

Einklappen
Keine Ankündigung bisher.

Automatische Beschattung

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

    Automatische Beschattung

    Hallo,

    hat jemand von Euch eine automatische Beschattung der Rolladen/Jalousien programmiert?
    Ich würde gerne die Rolladen anhand von Sonnenstand, Temperatur,... steuern.

    Kann ich was übernehmen oder muss ich selber tätig werden? Im Forum bin ich nicht so recht fündig geworden.

    Gruß

    Sprocky

    #2
    Hallo Sprocky!

    Ich habe mich letztes Jahr mit meiner ersten smarthome.py-Logik dem Thema Sonnenschutz gewidmet. Es war mein erster Python-Gehversuch und ist daher mit Sicherheit etwas dirty und verbos, aber es funktioniert einwandfrei. Ich kann ganz simpel im Wohnzimmer mit einem Taster die Automatik ein- oder ausschalten, was auch zu einem einigermaßen hohen WAF führte.

    Nochmal kurz zusammengefasst was es macht:
    Ich habe für meine mit Rollladen ausgerüsteten Fenster mittels iPhone-Kompass die Winkel ermittelt, bei denen Sonne in das Fenster einfällt. down -> Rollladen runter, up -> Rollladen wieder hoch. Dazu kann man noch angeben, wie weit der Rollladen runter fahren soll. (Das kann man bestimmt auch noch schön bei den eigentlichen Rollladen-items hinterlegen, aber ich wollte es erstmal langsam angehen... :-))
    Dann gehe ich einfach die entsprechenden items durch und setze die Werte, sobald die Bedingungen entsprechend erfüllt sind. Ausnahmen gibt es bei meinen Fenstern zur Terrasse: Ist ein Fenster geöffnet, wird es von der Automatik ausgenommen, damit man sich nicht aussperrt.
    Ein- und ausgeschaltet wird die Logik mit dem item "sunprotection_enabled".

    Die Logik wird folgendermaßen getriggert:

    Code:
    [sunprotection]
        filename = sunProtection.py
        crontab = init | 0,15,30,45 9,10,11,12,13,14,15,16,17,18,19,20,21 * *
        watch_item = sunprotection_enabled
    Vielleicht ist das ja ein nützlicher Startpunkt für Dich.
    Viel Spaß damit!

    Code:
    #!/usr/bin/env python
    
    import math
    
    logger.info('********** SUNPROTECTION 1.0 **********')
    
    SHUTTER_AZIMUT_MAPPING = {  
      'livingroom.shutter.bayLeft':        { 'downPos': 80, 'down': 110, 'up': 212 },
      'kitchen.shutter':                   { 'downPos': 80, 'down': 140, 'up': 264 },
      'livingroom.shutter.bayMidLeft':     { 'downPos': 50, 'down': 140, 'up': 264 },
      'livingroom.shutter.bayMidRight':    { 'downPos': 80, 'down': 140, 'up': 264 },
      'sleepingroom.shutter':              { 'downPos': 80, 'down': 140, 'up': 264 },
      'livingroom.shutter.cornerGarden':   { 'downPos': 80, 'down': 160, 'up': 264 },
      'livingroom.shutter.bayRight':       { 'downPos': 80, 'down': 215, 'up': 264 },
      'livingroom.shutter.cornerDriveway': { 'downPos': 80, 'down': 215, 'up': 264 }
    }
    
    if sh.return_item('sunprotection_enabled')():
      logger.info('Sonnenschutz ist aktiviert')
      currentAzimut = math.degrees(sh.sun.pos()[0])
      logger.info('Azimut: ' + str(currentAzimut))
      for shutterItem, values in SHUTTER_AZIMUT_MAPPING.items():
        position = None
        item = sh.return_item(shutterItem)
    
        # Azimut prüfen und Position setzen
        if currentAzimut > values['down']:
          position = values['downPos']/100*255
        if currentAzimut > values['up']:
          position = 0
    
        # Ausnahmen
        if item.id() == 'kitchen.shutter' and item.return_parent().contact():
          position = None
          logger.info('Küchentür offen, nicht herunterfahren')
        elif item.id() == 'livingroom.shutter.bayMidRight' and item.return_parent().return_parent().contact_sliding():
          position = None
          logger.info('Schiebeelement offen, nicht herunterfahren')
        elif item.id() == 'livingroom.shutter.cornerGarden' and item.return_parent().return_parent().contact_parallel():
          position = None
          logger.info('PSK offen, nicht herunterfahren')
    
        # Wert senden
        if position is not None:
          logger.info(str(item) + ': Position ' + str(position))
          item.position(position)
    
    else:
      logger.info('Sonnenschutz ist deaktiviert')

    Kommentar


      #3
      Hallo Sprocky,

      offline hat einen schönen Ansatz implementiert, siehe Forumsthread Logik für Raffstore-Steuerung oder auch SmartHome Wiki. Ich habe für mich seine Lokik erweitert in dem ich mit raumbezogene Abhängigkeiten wie Anwesendheit, Kunstlicht aktiv, Heiz/Kühlbedarf sowie Solarstrahlung die Raffstore ansteuere. Weiterhin habe ich die Berechnung der optimale Lamellenstellung für eine Beschattung in Abhängigkeit der Lamellengeometrie und Sonnenhöhe eingefügt. Die Logik ist ziemlich mit meiner Item-Struktur verwogen und außerdem noch "in Bearbeitung". Solltest Du dennoch über offlines Implementation hinaus den Bedarf haben, melde Dich bitte noch mal.

      Viele Grüße,

      Jan

      Kommentar


        #4
        Hallo Jan!
        Fände ich super, wenn du deine Arbeit hier posten könntest, zumindest auszugsweise. Damit bekommt man bestimmt gute Ideen

        Ich habe seit neuem eine Wetterstation und stehe dann auch vor dem Problem der vernünftigen Lamellensteuerung..

        Vielen Dank!

        Kommentar


          #5
          Moin,

          bei mir läuft die automatische Beschattung zwar fast komplett über die Wetterstation und wurde angangs nur durch Parameter wie Temperatur (< 18°) bzw. Handeingabe gesperrt, aber ein Hinweis zum WAF:

          umbedingt eine Zeit-Sperre berücksichtigen (z.B am Wochenende erst ab 9.30 aktiv) - Mein Küchenfeldwebel war an den ersten wärmeren Sommertagen "not amused" als Sonntags um 7.30 der Sonnenschutz die Rollläden auf 75% hochgefahren hat :-D (am Abend vorher händisch ausschalten und morgens wieder an, hat den WAF stark reduziert)

          Grüße Lars

          Kommentar


            #6
            Du meinst, sie stand nackt in der Küche und wurde enttarnt ?

            Kommentar


              #7
              Nein, Sonntags um 7.30 sind wir noch nicht in der Küche unterwegs ;-) - Aber wenn das wohlverdiente Ausschlafen schlagartig durch viel Sonne beendet wird hat sich der WAF ganz schnell erledigt

              Kommentar


                #8
                Hallo,

                nach dem ich gestern zu den Lamellensteuerungen das hoch und herunterfahren der Raffstore aktiviert habe, wurden sie im Bad hochgefahren als ich unter der Dusche stand

                Alle Bediengungen waren erfüllt: Es war nach dem Aufstehen, die Heizperiode ist noch aktiv, der Raum wurde gestern beheizt, die Solareinstrahlung war > 100 W/m^2, die Sonne kam von vorne und keine war im Raum anwesend, da der Präsenzmelder die Dusche nicht erfasst!

                Ich debugge noch ein bisschen vor ich den Quellcode hier reinsetze...

                Viele Grüße,

                Jan

                Kommentar


                  #9
                  Hallo zusammen,

                  Zitat von JanT Beitrag anzeigen
                  Hallo Sprocky,

                  offline hat einen schönen Ansatz implementiert, siehe Forumsthread Logik für Raffstore-Steuerung oder auch SmartHome Wiki. [...]
                  Jan
                  ich habe die Logik mittlerweile zu einem Plugin umgebaut, das macht programmiertechnisch ein paar Sachen einfacher. Ich bin da immer noch am Feintuning. Im Moment baue ich zusätzliche "Sperrbedingungen" ein, über die gesteuert werden kann, dass ein Zustand bis auf weiteres nicht verlassen werden kann. Wenn alles fertig ist werde ich den Wiki-Eintrag entsprechend ergänzen bzw. das Plugin anderweitig zur Verfügung stellen.

                  Grüße
                  offline

                  Kommentar


                    #10
                    Hallo offline,

                    ich nutze Deine Automatik seit gut einem halben Jahr und habe jetzt im Frühling auch noch die automatische Beschattung konfiguriert. Funktioniert tadellos und bin echt happy.
                    Ich habe kleinere Adaptionen eingebaut, die Du ggf für das Plugin berücksichtigen könntest?

                    1) Steuerung der automatischen Beschattung nur, wenn die Aussentemperatur eine gewisse Höhe hat. Im Sommer soll das natürlich beschatten, aber im Winter / in der Übergangszeit soll der Sonneneinfall das Haus mit heizen.
                    Das habe ich analog der Helligkeit implementiert.

                    item.conf
                    Code:
                                    [[[[[RaffstoreAutomatik]]]]]
                                            item_helligkeit = Aussen.wetterstation.helligkeit_sued               # Item, über das die Helligkeit ermittelt wird
                                            [B]item_temperatur = Waermepumpe.Temperatur_TA[/B]                          # Item, über das die Temperatur ermittelt wird
                    ...
                                            [[[[[TagNachfuehren]]]]]
                                                name = Tag (nachführen)
                                                min_helligkeit = 45000
                                                leave_max_helligkeit = 30000
                                                min_sun_altitude = 25
                                                min_sun_azimut = 120
                                                max_sun_azimut = 270
                                                [B]min_temperatur = 16[/B]
                                                position = auto

                    In der logik.py habe ich an allen Stellen, wo du mit der Helligkeit arbeitest, das auf das neue Attribut erweitert.
                    Code:
                    ...
                            self.item_helligkeit = self.sh.return_item(self.config["item_helligkeit"])
                            self.item_temperatur = self.sh.return_item(self.config["item_temperatur"])
                    ...
                            if self.item_helligkeit == None:
                                raise AttributeError("Das für 'item_helligkeit' angegebene Item '%s' ist unbekannt." %(self.config['item_helligkeit']))
                            if self.item_temperatur == None:
                                raise AttributeError("Das für 'item_temperatur' angegebene Item '%s' ist unbekannt." %(self.config['item_temperatur']))
                    ...
                    usw
                    2) Abschaltung der gesamten Logik mittels eines neuen Items, so dass ich nicht alle 18 Items deaktivieren muss (bei Besuch oder so)
                    item.conf
                    Code:
                    [Raffstore]
                        [[Automatik]]
                            type = bool
                            cache = on
                            visu_acl = rw
                    logik.py (im Constructor)
                    Code:
                            # Check if global Raffstore automatik an oder aus
                            if not self.sh.Raffstore.Automatik():
                                logger.info("Raffstore Automatik ist aus. Item: Raffstore.Automatik")
                                exit()
                    Was zusätzlich noch auf der Wunschliste steht ist, das bei manueller Schaltung eines Items dieses für 2-3h von der Automatik ausgenommen wird und danach wieder in den Automatik-Modus zurück fällt.
                    Use case:
                    * Übergangszeit, Raffstoren eigentlich oben, manuelles Fahren wg Fernsehen / Mittagsschlaf auf der Couch
                    * Hochsommer, Kinder gehen um 19-19:30h ins Bett, es ist noch richtig hell, Raffstoren werden manuell gefahren

                    Freu mich schon auf das Plugin. Gute Arbeit!
                    hhhc

                    ++ KNX und ETS schnell, einfach und verständlich ++
                    Der ultimative ETS Schnellkurs
                    www.ets-schnellkurs.de

                    Kommentar


                      #11
                      Zitat von JanT Beitrag anzeigen
                      und keine war im Raum anwesend, da der Präsenzmelder die Dusche nicht erfasst!
                      Deswegen hab' ich mir inzwischen nen zusätzlichen HF-Melder im Bad gegönnt. Hatte nämlich zusätzlich noch viel Spass, weil unter der Dusche immer das Licht aus ging (Winter)

                      Gruss
                      Jochen

                      Kommentar


                        #12
                        Hallo hhhc,

                        Zitat von hhhc Beitrag anzeigen
                        1) Steuerung der automatischen Beschattung nur, wenn die Aussentemperatur eine gewisse Höhe hat. Im Sommer soll das natürlich beschatten, aber im Winter / in der Übergangszeit soll der Sonneneinfall das Haus mit heizen.
                        Sowas habe ich im Plugin auch bereits eingebaut.

                        Zitat von hhhc Beitrag anzeigen
                        2) Abschaltung der gesamten Logik mittels eines neuen Items, so dass ich nicht alle 18 Items deaktivieren muss (bei Besuch oder so)
                        Das habe ich bei mir so gelöst, dass ich eine GA habe, auf die alle "Aktiv"-Items zusätzlich hören.

                        Zitat von hhhc Beitrag anzeigen
                        Was zusätzlich noch auf der Wunschliste steht ist, das bei manueller Schaltung eines Items dieses für 2-3h von der Automatik ausgenommen wird und danach wieder in den Automatik-Modus zurück fällt.
                        Use case:
                        * Übergangszeit, Raffstoren eigentlich oben, manuelles Fahren wg Fernsehen / Mittagsschlaf auf der Couch
                        * Hochsommer, Kinder gehen um 19-19:30h ins Bett, es ist noch richtig hell, Raffstoren werden manuell gefahren
                        Das ist eine gute Idee. Das lässt sich sicherlich einbauen

                        Grüße
                        offline

                        Kommentar


                          #13
                          Hallo,

                          Zitat von hhhc Beitrag anzeigen
                          Was zusätzlich noch auf der Wunschliste steht ist, das bei manueller Schaltung eines Items dieses für 2-3h von der Automatik ausgenommen wird und danach wieder in den Automatik-Modus zurück fällt.
                          Use case:
                          * Übergangszeit, Raffstoren eigentlich oben, manuelles Fahren wg Fernsehen / Mittagsschlaf auf der Couch
                          * Hochsommer, Kinder gehen um 19-19:30h ins Bett, es ist noch richtig hell, Raffstoren werden manuell gefahren
                          ich habe das gelöst in dem ich in eine separate Logik auf manuelles fahren der Behanghöhen/Lamellenwinkel lausche und das Aktiv-Item dann lösche. In der Nacht wird global alle Aktiv-Item wieder gesetzt. Neuer Tag, neue Möglichkeiten...

                          Viele Grüße,

                          Jan

                          Kommentar


                            #14
                            Ich hab die Manuell-Situation so bei mir gelöst, dass die Logik zuerst mal checkt, von wem die Jalousie als Letztes aktualisiert worden ist. Wenn das nicht durch die Logik geschehen ist, dann wohl durch die Visu oder KNX Taster. Dann soll bis zur Dunkelheit (theoretisch auch nach Ablauf von einer gewissen Zeit) die Jalousie unangetastet bleiben. Das spart letztlich, für jede einzelne Jalousie ein Aktiv-Item anzulegen und abzufragen.

                            Bei Einbruch der Dunkelheit werden alle Jalousie durch die Logik um 1% nach oben gefahren, somit also von der Logik getriggert und wieder im Automatikbetrieb. Letzteres ist vielleicht nicht ganz so nobel, da hat sicher wer ne feinere Lösung

                            Kommentar


                              #15
                              Hallo offline,

                              ich möchte meinen momentanen Stand basierend auf der Logik-Variante hier beschreiben. Es ist ein totes Pferd und dürfte spätestens wenn Du die Plugin-Version veröffentlichst keine Anwendung mehr finden. Ich spare mir deshalb das weitere Finetunen und Robustmachen der Logik gegenüber „Fehlkonfiguration“ . Ich würde mich freuen, wenn wir es schafften, dem Plugin die gleichen Fähigkeiten zu geben und würde die Zeit lieber in das Finetunen der Plugin-Variante investieren.

                              Meine Ansätze für die Beschattungssteuerung, die mit der Raffstoreautomatik zusammen mit weiteren Logiken umgesetzt sind, sind die folgenden:

                              1. Tagesablauf und Raum-Trigger
                              Die Raffstore (bzw. Lamellen) werden in Abhängigkeit von Raumparametern gefahren: Anwesenheit, Kunstlicht an, Heizungsbedarf und Raummodus (Nacht/ Aufstehzeit/ Tag/Zubettgehen). Wenn niemand im Raum ist, wird je nach Heiz- oder Kühlbedarf und aktueller Solarstrahlung beschattet oder die Sonne eingelassen. Anwesenheit hat immer Priorität und ich habe unter Verwendung weiterer Items eine - für mich zufriedenstellende- Ansteuerung implementiert, siehe Beispiele in der beigefügten beschattungsautomatik.conf. Der grobe Tagesablauf bei Anwesenheit ist wie folgt: Ab dem Weckerklingeln bis zu einer halben Stunde Schonzeit später (Raum: Betriebsmodus = 1) werden die Lamellen nur eingeschränkt geöffnet (Beschattung). Danach wird, wenn möglich, die Sicht nach außen ermöglicht (Lamellenstellung 0 = gerade). Sollte die Sonne zu stark hineinscheinen, wird beschattet. Wenn es draußen ausreichend hell ist, kann man nicht in die Fenster gucken und ein Sichtschutz wird nur bei Einbruch der Dunkelheit oder, wenn das Licht im Raum angeschaltet wird (Kunstlicht = True) aktiviert. In Betriebsmodus = 3 (Zubettgehen), falls jemand halbnackt durch die Räume läuft, wird Sichtschutz auch aktiviert.

                              Ein Sonderfall ist der globale Modus „Putzen“. Wenn aktiviert, werden - wenn der Raum betreten wird - die Lamellen in Richtung der Sonne gestellt, um möglichst viel Licht einzulassen (Lamellenwinkel = hell). Der Modus wird erst verlassen, wenn der globale Modus „Putzen“ wieder deaktiviert wird.

                              Das Hoch- und Runterfahren der Raffstore versuche ich zu vermeiden und habe mich für folgende Variante entschieden: Im Winter bzw. in der Heizperiode darf die Automatik die Raffstore in den Wohnräumen hochfahren, wenn der Raum Heizbedarf hat und die Sonnenstrahlung hoch ist. Wieder herunterfahren wird die Automatik die Raffstore nicht. Im Sommer darf die Automatik die Raffstore entsprechend bei Bedarf herunterfahren.

                              Um die Antwortzeiten klein zu halten, habe ich ein raumweises Abarbeiten der Steuerung implementiert.

                              Eine allgemeine Implementation der Bedingungen über item_*, min_, max_* usw. wäre sicherlich sinnvoll, um die Flexibilität der Logik zu erhöhen.

                              2. Mit einer Logik Raffstore und Markisen steuern
                              Wir haben ein Satteldach und Dachfenster. Entsprechend gibt es nicht nur Raffstore sondern auch Markisen, die ich mit derselben Logik ansteuere. Deshalb auch mein Vorschlag zur Namensänderung von Raffstoreautomatik zu Beschattungsautomatik

                              Um Markisen und Rollläden ansteuern zu können, habe ich den Konfigurationspunkt „Position“ der item.conf in Behanghöhe und Lamellenwinkel aufgeteilt.

                              3. Optimale Lamellenwinkel für Blendschutz
                              Durch die Angabe von Lamellenwinkel = beschatten in der item.conf werden die Lamellen so angesteuert, dass eine Linie von der Lamellenhinterkante durch die Vorderkante der darüberliegenden Lamelle in Richtung Sonne zeigt. Dadurch wird das Sonnenlicht gerade so abgeblockt und der Raum trotzdem nicht abgedunkelt, da Streulicht durchkommen kann.

                              Die Berechnung der Lamellenwinkel für Blendschutz wird global einmal pro Logik-Aufruf unter die Annahme durchgeführt, dass die Sonne direkt vor dem Fenster steht (Azimut). Das ist eine konservative Annahme und man könnte bei seitlicher Sonne die Lamellen noch weiter öffnen. Die Lamellengeometrie, Blatttiefe und Abstand zwischen zwei Lamellen werden in der Logik angegeben.

                              Die Berechnung für jedes Fenster zu wiederholen, wäre übertrieben. Es würde reichen, einmal je Fensterausrichtung die Berechnung durchzuführen.

                              4. Freigaben zurücknehmen bei manueller Bedienung
                              Wenn ein Raffstore manuell über die Visu bedient wird, hat das ja meist einen Grund und man möchte nicht, dass die Automatik es wenige Minuten später rückgängig macht. Ich habe dafür eine separate Logik implementiert, die auf Änderungen der Behanghöhe oder Lamellenwinkel über die Visu lauscht und dann die Freigabe für die betroffenen Raffstore aufhebt. Siehe beigelegter Quellcode „beschattungsautomatik_freigaben.py“. Dabei werden noch Freigaben raumübergreifend und global (alle Raffstore und Markisen) verwaltet. Die Raumfreigabe bleibt per Definition aktiv bis keine der Raffstore oder Markisen mehr eine Freigabe hat oder die Raumfreigabe selbst getriggert wird. Die globale Freigabe ist nur zum Setzen oder Aufheben der Freigabe. Dessen Status ist nicht definiert.

                              Ich habe den Namen des Items von Aktiv auf Freigabe geändert, bestehe aber nicht drauf.

                              5. Heizen/ Kühlen
                              Wir Heizen mit Erdwärme und nutzen die Möglickeit der passive Kühlung im Sommer (nur Zirkulation, der Kompressor läuft nicht). Entsprechend ist die „Heizungsanlage“ immer aktiv. Im Winter wird Energie aufgewandt für das Heizen und im Sommer für das Kühlen. Die Raffstore unterstützen, indem raumweise bedarfsgerecht beschattet oder offen gelassen wird. Die Entscheidung ob beschattet oder die Lamellen die Sonne durchlassen sollen, wird über zwei kleine Logiken ausgeführt, siehe Anhang „heizung_tagesvoraussage.py“ und „beschattung_momentanbedarf.py“. Morgens um 06:00 wird nachgeschaut, ob in den letzten 24 Stunden der thermoelektrische Stellantrieb für den Raum offen war. Wenn ja, wurde Energie für die Klimatisierung aufgewandt. Ist die Anlage im Heizbetrieb war es fürs Heizen, sonst fürs Kühlen und die Beschattungsstrategie für den Tagesanfang steht fest. War der thermoelektrische Stellantrieb nicht offen, wird weiter geschaut: Zusätzlich zu einer Solltemperatur, stellen wir einen Temperaturbereich ein. In Räumen wo das Einhalten der Temperatur wichtig ist , wird ein kleiner Temperaturbereich eingestellt, sonst halt größer. Ist die durchschnittliche Temperatur der letzten 24h höher als die obere Grenze des Temperaturbereichs, wird für heute erstmal gekühlt werden. Wenn der Temperaturbereich unterschritten ist, entsprechend geheizt. Liegt die Durchschnittstemperatur innerhalb des definierten Temperaturbereichs wird anhand des Tagesmaximum und -minmum entschieden.

                              Danach wird im Tagesverlauf zweimal pro Stunde die Temperatur überprüft und das Heizen oder Kühlen über die Raffstore ggf. neu gesetzt.

                              6. Kalibrierung der Lamellenwinkel
                              Wir nutzen zwar SMI-Antriebe für die Raffstore, wodurch die Wiederholgenauigkeit des Ansteuerns ziemlich gut ist; allerdings hat der Fensterbauer oder Raffstorehersteller im Werk „gepfuscht“, so dass die Unterschiede zwischen den Raffstoren bis zu 15 Grad beträgt. Um das auszugleichen, habe ich in einer separaten Logik eine individuelle Kalibrierung der Lamellenwinkel ermöglicht.

                              Die Ausgabe der Lamellenwinkel wie auch die Angaben in „item.conf „habe ich auf Grad (statt Aktorwert) umgestellt. Den Ausgabentyp habe ich auf „str“ gesetzt, die das Anfahren der Extremposition wie ’unten’, ’kuehlen’ oder auch ’-90’ erlaubt.

                              An dieser Logik bastele ich noch. Ich möchte eine transparente Umrechnung zwischen Aktorwert (Byte) und Winkel (Fließzahl). Wenn der eine geändert wird, soll der zweite sich auch ändern. Das Problem ist der „Triggerhagel“ schon beim Hochfahren wie auch beim Ausführen der Logik. Als einfache Logik habe ich die Funktion nicht reentrant hingekriegt und es kommt zur Vertauschung der Werte. Ich werde versuchen, die Funktionalität als Plugin zu implementieren.

                              7. Beschattungssteuerung bei Alarm
                              Das Auffahren und Sperren der Raffstore und Markisen bei Alarm oder beim Fensteröffnen ist am besten auf der unteren Ebene, sprich KNX aufgehoben.

                              Viele Grüße,

                              Jan

                              logic.conf:
                              Code:
                              # logic.conf
                              
                              [heizung_tagesvoraussage]
                                  filename = heizung_tagesvoraussage.py
                                  crontab = 0 6 * *
                               
                              [beschattung_momentanbedarf]
                                  filename = beschattung_momentanbedarf.py
                                  crontab = 30 * * *
                              
                              [beschattungsautomatik_freigaben]
                                  filename = beschattungsautomatik_freigaben.py
                                  watch_item = globals.beschattung.automatik.freigabe | *.*.beschattung.automatik.freigabe | *.*.raffstore.*.automatik.freigabe | *.*.markise.*.automatik.freigabe | *.*.raffstore.*.lamellenwinkel_aktor | *.*.raffstore.*.behangposition_aktor | *.*.markise.*.behangposition_aktor
                                  crontab = init = init
                              
                              [beschattungsautomatik]
                                  filename = beschattungsautomatik.py
                                  watch_item =  *.*.beschattung.automatik.freigabe | *.*.markise.automatik.freigabe | og.*.betriebsmodus | *.*.anwesendheit | *.*.beleuchtung.aktiv | Wetter.helligkeit.sw | globals.putzen
                                  cycle = 300
                              heizung_tagesvoraussage.py:
                              Code:
                              #!/usr/bin/env python3
                              
                              kuehlen = 0
                              heizen = 1
                              
                              # Berechnung für alle Items durchführen, die ein Subitem "heizung.bedarf_kuehlen_heizen" haben
                              items =  sh.match_items('*.heizung.bedarf_kuehlen_heizen')
                              for item in items:              
                                  heizungsitem = item.return_parent()
                                  logger.debug("item:{}".format(heizungsitem))
                                  T_soll = heizungsitem.soll()
                                  T_range = heizungsitem.temperaturbereich()
                                  max_ventilstellung = heizungsitem.stellwert.db('max', '24h')
                                  if max_ventilstellung > 0.0:
                                      heizungsitem.bedarf_kuehlen_heizen(sh.globals.heizung.kuehlen_heizen())
                                  else:
                                      T_avg = heizungsitem.ist.db('avg', '24h')
                                      if T_avg > (T_soll + T_range):
                                          heizungsitem.bedarf_kuehlen_heizen(kuehlen)
                                      elif (T_avg) < (T_soll - T_range):
                                          heizungsitem.bedarf_kuehlen_heizen(heizen)
                                      else:
                                          T_min = heizungsitem.ist.db('min', '24h')
                                          T_max = heizungsitem.ist.db('max', '24h')
                                          if T_min < (T_soll - T_range) and T_max < (T_soll + T_range):
                                              heizungsitem.bedarf_kuehlen_heizen(heizen)
                                          elif T_max > (T_soll + T_range) and T_min > (T_soll - T_range):
                                              heizungsitem.bedarf_kuehlen_heizen(kuehlen)
                                          else:
                                              pass #unverändert
                              beschattung_momentanbedarf.py:
                              Code:
                              #!/usr/bin/env python3
                              
                              # Tagesvoraussagen aus_ heizung_tagesvoraussage.py
                              
                              kuehlen = 0
                              heizen = 1
                              
                              # Berechnung für alle Items durchführen, die ein Subitem "heizung.bedarf_kuehlen_heizen" haben
                              items =  sh.match_items('*.heizung.bedarf_kuehlen_heizen')
                              for item in items:              
                                  heizungsitem = item.return_parent()
                                  T_soll = heizungsitem.soll()
                                  T_range = heizungsitem.temperaturbereich()
                                  if heizungsitem.bedarf_kuehlen_heizen() == heizen:
                                      if heizungsitem.ist() > (T_soll + T_range):
                                          heizungsitem.bedarf_kuehlen_heizen(kuehlen)
                                  else:
                                      if heizungsitem.ist() < (T_soll - T_range):
                                          heizungsitem.bedarf_kuehlen_heizen(heizen)
                              beschattungsautomatik_freigaben.py:
                              Code:
                              #!/usr/bin/env python3
                              
                              # trigger from: globals.beschattung.automatik.freigabe | *.[room].beschattung.automatik.freigabe | *.[room].raffstore/markise.*.automatik.freigabe | *.[room].raffstore.*.lamellenwinkel_aktor | *.[room].raffstore/markise.*.behangposition_aktor
                              
                              def test_room_enable(sh, room):
                                  items =  sh.match_items("*.{0}.raffstore.*.automatik.freigabe".format(room))
                                  freigabe_item = sh.match_items("*.{0}.beschattung.automatik.freigabe".format(room))[0]
                                  one_enabled = False
                                  all_enabled = True
                                  for item in items:      
                                      one_enabled = one_enabled or item()
                                      all_enabled = all_enabled and item()
                              
                                  items =  sh.match_items("*.{0}.markise.*.automatik.freigabe".format(room))
                                  for item in items:      
                                      one_enabled = one_enabled or item()
                                      all_enabled = all_enabled and item()
                                  if not one_enabled:
                                      freigabe_item = sh.match_items("*.{0}.beschattung.automatik.freigabe".format(room))[0]
                                      if freigabe_item() != False:
                                          freigabe_item(False)
                                  if all_enabled:
                                      freigabe_item = sh.match_items("*.{0}.beschattung.automatik.freigabe".format(room))[0]
                                      if freigabe_item() != True:
                                          freigabe_item(True)
                              
                              # Globale Freigabe
                              if trigger['source'] == 'globals.beschattung.automatik.freigabe':
                                  # Feigabe für alle Raffstore Items setzen
                                  #all_items_set =  set(sh.match_items('*raffstore.*.automatik.freigabe'))
                                  #items = all_items_set.difference(sh.match_items('*raffstore.alle.automatik.freigabe'))
                                  items = sh.match_items('*.*.beschattung.automatik.freigabe')
                                  for item in items:      
                                      item(trigger['value'])
                                  exit()
                              
                              # Init
                              if ('Scheduler' in trigger['by']) or ('None' in trigger['by']):
                                  for item in sh.match_items("*.*.beschattung.automatik.freigabe"):
                                      room = str(item).split('.')[1]    
                                      test_room_enable(sh, room)
                                  exit()
                              
                              # Test von CLI
                              if 'CLI' in trigger['by']:
                                  room = 'schlafen'
                              else:
                                  room = str(trigger['source']).split('.')[1]    
                              
                              
                              # Raumweise Freigabe
                              if (trigger['source'] in str(sh.match_items('*.*.beschattung.automatik.freigabe'))):
                                  # Feigabe für alle Raffstore und Markisen in 'room' setzen  
                                  items =  sh.match_items("*.{0}.raffstore.*.automatik.freigabe".format(room))
                                  for item in items:      
                                      item(trigger['value'])
                                  items =  sh.match_items("*.{0}.markise.*.automatik.freigabe".format(room))
                                  for item in items:      
                                      item(trigger['value'])
                              
                              elif (trigger['source'] in str(sh.match_items('*.*.raffstore.alle.*'))) and ('Visu' in sh.match_items(trigger['source'])[0].changed_by()):
                                  # Eine oder alle (im Raum) Raffstore wurde über die Visu bewegt: Automatik Freigabe zurücknehmen
                                  freigabe_item = sh.match_items("*.{0}.beschattung.automatik.freigabe".format(room))[0]
                                  if freigabe_item() != False:
                                      freigabe_item(False)
                              
                              elif (trigger['source'] in str(sh.match_items('*.*.markise.alle.*'))) and ('Visu' in sh.match_items(trigger['source'])[0].changed_by()):
                                  # Eine oder alle (im Raum) Raffstore wurde über die Visu bewegt: Automatik Freigabe zurücknehmen
                                  freigabe_item = sh.match_items("*.{0}.beschattung.automatik.freigabe".format(room))[0]
                                  if freigabe_item() != False:
                                      freigabe_item(False)
                                  
                              elif (trigger['source'] in str(sh.match_items('*.*.raffstore.*.behangposition_aktor')) or (trigger['source'] in str(sh.match_items('*.*.raffstore.*.lamellenwinkel_aktor')))) and ('Visu' in sh.match_items(trigger['source'])[0].changed_by()):
                                  raffstore = str(trigger['source']).split('.')[3]
                                  logger.debug("Hebe Freigabe auf, aufgrund: {0}, trigger by:{1}".format(trigger['source'],sh.match_items(trigger['source'])[0].changed_by()  ))
                                  sh.match_items("*.{0}.raffstore.{1}.automatik.freigabe".format(room, raffstore))[0](False)
                              
                              elif (trigger['source'] in str(sh.match_items('*.*.markise.*.behangposition_aktor'))) and ('Visu' in sh.match_items(trigger['source'])[0].changed_by()):
                                  markise = str(trigger['source']).split('.')[3]
                                  logger.debug("Hebe Freigabe auf, aufgrund: {0}, trigger by:{1}".format(trigger['source'],sh.match_items(trigger['source'])[0].changed_by() ))
                                  sh.match_items("*.{0}.markise.{1}.automatik.freigabe".format(room, markise))[0](False)
                              
                              elif (trigger['source'] in str(sh.match_items('*.*.raffstore.*.freigabe')) or (trigger['source'] in str(sh.match_items('*.*.markise.*.freigabe')))):
                                  # wenn für den Raum keine Freigaben mehr aktiv sind, dann setzte Freigabe für den Raum zurück
                                  test_room_enable(sh, room)
                              beschattungsautomatik.conf:
                              Code:
                              [globals]
                                      [[beschattung]]
                                              [[[automatik]]]
                                                      [[[[freigabe]]]]
                                                      type = bool
                                                      enforce_updates = true
                              
                              [og]
                                      [[bad]]  
                                              [[[beschattung]]]
                                                      [[[[automatik]]]]
                                                              [[[[[freigabe]]]]]
                                                              type = bool
                                                              enforce_updates = true
                                              [[[raffstore]]]
                                                      [[[[so_links]]]]
                                                              [[[[[automatik]]]]]
                                                              item_helligkeit = Wetter.helligkeit.so
                                                              item_anwesendheit = og.bad.anwesendheit
                                                              item_heizungsbedarf = og.bad.heizung.bedarf_kuehlen_heizen
                                                              item_kunstlicht = og.bad.beleuchtung.aktiv
                                                              item_raummodus = og.bad.betriebsmodus
                              
                                                                      [[[[[[putzen]]]]]]
                                                                      name = Putzen
                                                                      anwesendheit = True
                                                                      putzen = True
                                                                      leave_putzen = False
                                                                      lamellenwinkel = hell
                              
                                                                      ## Tageszyklus mit Anwesendheit
                                                                      [[[[[[morgen_daemmerung_anwesend]]]]]]
                                                                      name = Dämmerung am Morgen Anwesend  
                                                                      anwesendheit = True
                                                                      min_helligkeit = 5
                                                                      raummodus = 1
                                                                      lamellenwinkel = beschatten
                                                                      [[[[[[tag_anwesend]]]]]]
                                                                      name = Tag Anwesend  
                                                                      anwesendheit = True
                                                                      min_helligkeit = 20000
                                                                      kunstlicht = False
                                                                      raummodus = 2
                                                                      max_sun_altitude = 41
                                                                      min_sun_azimut = 45
                                                                      max_sun_azimut = 225
                                                                      lamellenwinkel = beschatten
                                                                      [[[[[[tag_anwesend_aussen_hell]]]]]]
                                                                      name = Tag Anwesend Hell ohne Sichtshutz
                                                                      anwesendheit = True
                                                                      min_helligheit = 500
                                                                      kunstlicht = False
                                                                      raummodus = 2
                                                                      lamellenwinkel = 0
                                                                      [[[[[[tag_anwesend_mit_sichtshutz]]]]]]
                                                                      name = Tag Anwesend Hell mit Sichtschutz  
                                                                      anwesendheit = True
                                                                      min_helligheit = 500
                                                                      kunstlicht = True
                                                                      raummodus = 2
                                                                      lamellenwinkel = 90
                                                                      [[[[[[abend_daemmerung_anwesend]]]]]]
                                                                      name = Dämmerung Abend Anwesend  
                                                                      anwesendheit = True
                                                                      min_helligkeit = 300
                                                                      raummodus = 2
                                                                      lamellenwinkel = 90
                                                                      [[[[[[abend_dunkel_dunkel_anwesend]]]]]]
                                                                      name = Abend Dunkel Anwesend
                                                                      anwesendheit = True
                                                                      max_helligkeit = 300
                                                                      raummodus = 2
                                                                      kunstlicht = False
                                                                      lamellenwinkel = 0                                      
                                                                      [[[[[[abend_dunkel_hell]]]]]]
                                                                      name = Abend Dunkel Anwesend
                                                                      max_helligkeit = 300
                                                                      raummodus = 2
                                                                      kunstlicht = True
                                                                      lamellenwinkel = 90                                     
                                                                      [[[[[[nacht_anwesend]]]]]]
                                                                      name = Nacht Anwesend
                                                                      anwesendheit = True  
                                                                      raummodus = 0
                                                                      hoehe = 100
                                                                      lamellenwinkel = 90
                                                                      [[[[[[default_anwesend]]]]]]
                                                                      name = Default Anwesend
                                                                      anwesendheit = True
                                                                      lamellenwinkel = 90
                               
                                                                      ## Tageszyklus ohne Anwesendheit
                                                                      [[[[[[nacht_abwesend]]]]]]
                                                                      name = Nacht Abwesend
                                                                      anwesendheit = False  
                                                                      max_helligkeit = 5
                                                                      #max_solarstrahlung = 5 #kuehlen/heizen nicht mehr relevant
                                                                      raummodus = 0
                                                                      lamellenwinkel = 90
                                                                      [[[[[[heizen_winter]]]]]]
                                                                      name = heizen im Winter
                                                                      anwesendheit = False
                                                                      raummodus = 2
                                                                      heizperiode = True
                                                                      heizungsbedarf = True
                                                                      min_solarstrahlung = 100
                                                                      min_sun_azimut = 45
                                                                      max_sun_azimut = 225
                                                                      #hoehe = 0
                                                                      lamellenwinkel = heizen
                                                                      [[[[[[heizen]]]]]]
                                                                      name = heizen
                                                                      anwesendheit = False
                                                                      heizungsbedarf = True
                                                                      min_solarstrahlung = 50
                                                                      min_sun_azimut = 45
                                                                      max_sun_azimut = 225
                                                                      #hoehe = auto #hoehe nie verstellen?
                                                                      lamellenwinkel = heizen
                                                                      [[[[[[kuehlen_sommer]]]]]]
                                                                      name = Kühlen im Sommer
                                                                      anwesendheit = False
                                                                      heizperiode = False
                                                                      heizungsbedarf = False
                                                                      min_solarstrahlung = 600
                                                                      min_sun_azimut = 45
                                                                      max_sun_azimut = 225
                                                                      hoehe = 100
                                                                      lamellenwinkel = kuehlen
                                                                      [[[[[[kuehlen]]]]]]
                                                                      name = Kühlen
                                                                      anwesendheit = False
                                                                      heizungsbedarf = False
                                                                      min_solarstrahlung = 50
                                                                      min_sun_azimut = 45
                                                                      max_sun_azimut = 225
                                                                      #hoehe = auto #hoehe nie verstellen?
                                                                      lamellenwinkel = kuehlen
                                                                      [[[[[[default_abwesend]]]]]]
                                                                      name = Default Abwesend
                                                                      anwesendheit = False
                                                                      lamellenwinkel = 0
                              
                              
                                                                      [[[[[[freigabe]]]]]]
                                                                      type = bool
                                                                      value = 1
                                                                      cache = on
                                                                      [[[[[[letzte_position_id]]]]]]
                                                                      type = str
                                                                      cache = on
                                                                      [[[[[[letzte_position_name]]]]]]
                                                                      type = str
                                                                      cache = on
                              
                                      [[schlafen]]  
                                              [[[beschattung]]]
                                                      [[[[automatik]]]]
                                                              [[[[[freigabe]]]]]
                                                              type = bool
                                                              enforce_updates = true
                                              [[[raffstore]]]
                                                      [[[[sw]]]]
                                                              [[[[[automatik]]]]]
                                                              item_helligkeit = Wetter.helligkeit.sw
                                                              item_anwesendheit = og.schlafen.anwesendheit
                                                              item_heizungsbedarf = og.schlafen.heizung.bedarf_kuehlen_heizen
                                                              item_kunstlicht = og.schlafen.beleuchtung.aktiv
                                                              item_raummodus = og.schlafen.betriebsmodus
                              
                                                                      [[[[[[putzen]]]]]]
                                                                      name = Putzen
                                                                      anwesendheit = True
                                                                      putzen = True
                                                                      leave_putzen = False
                                                                      lamellenwinkel = hell
                              
                                                                      ## Tageszyklus mit Anwesendheit
                                                                      [[[[[[morgen_daemmerung_anwesend]]]]]]
                                                                      name = Dämmerung am Morgen Anwesend  
                                                                      anwesendheit = True
                                                                      min_helligkeit = 5
                                                                      raummodus = 1
                                                                      lamellenwinkel = beschatten
                                                                      [[[[[[tag_anwesend]]]]]]
                                                                      name = Tag Anwesend  
                                                                      anwesendheit = True
                                                                      min_helligkeit = 20000
                                                                      kunstlicht = False
                                                                      raummodus = 2
                                                                      max_sun_altitude = 41
                                                                      min_sun_azimut = 135
                                                                      max_sun_azimut = 315
                                                                      lamellenwinkel = beschatten
                                                                      [[[[[[tag_anwesend_aussen_hell]]]]]]
                                                                      name = Tag Anwesend Hell ohne Sichtshutz
                                                                      anwesendheit = True
                                                                      min_helligheit = 500
                                                                      kunstlicht = False
                                                                      raummodus = 2
                                                                      lamellenwinkel = 0
                                                                      [[[[[[tag_anwesend_mit_sichtshutz]]]]]]
                                                                      name = Tag Anwesend Hell mit Sichtschutz  
                                                                      anwesendheit = True
                                                                      min_helligheit = 500
                                                                      kunstlicht = True
                                                                      raummodus = 2
                                                                      lamellenwinkel = 90
                                                                      [[[[[[abend_daemmerung_anwesend]]]]]]
                                                                      name = Dämmerung Abend Anwesend  
                                                                      anwesendheit = True
                                                                      min_helligkeit = 300
                                                                      raummodus = 2
                                                                      lamellenwinkel = 90
                                                                      [[[[[[abend_dunkel_dunkel_anwesend]]]]]]
                                                                      name = Abend Dunkel Anwesend
                                                                      anwesendheit = True
                                                                      max_helligkeit = 300
                                                                      raummodus = 2
                                                                      kunstlicht = False
                                                                      lamellenwinkel = 0                                      
                                                                      [[[[[[abend_dunkel_hell]]]]]]
                                                                      name = Abend Dunkel Anwesend
                                                                      max_helligkeit = 300
                                                                      raummodus = 2
                                                                      kunstlicht = True
                                                                      lamellenwinkel = 90                                     
                                                                      [[[[[[abens_anwesend_kunstlicht]]]]]]
                                                                      name = Zubettgehen (Abend) mit Kunstlicht
                                                                      anwesendheit = True
                                                                      kunstlicht = True
                                                                      raummodus = 3
                                                                      lamellenwinkel = -90
                                                                      [[[[[[abens_anwesend]]]]]]
                                                                      name = Zubettgehen (Abend) ohne Kunstlicht
                                                                      anwesendheit = True
                                                                      kunstlicht = False
                                                                      raummodus = 3
                                                                      lamellenwinkel = 0
                                                                      [[[[[[nacht_anwesend]]]]]]
                                                                      name = Nacht Anwesend
                                                                      anwesendheit = True  
                                                                      raummodus = 0
                                                                      hoehe = 100
                                                                      lamellenwinkel = -90
                                                                      [[[[[[default_anwesend]]]]]]
                                                                      name = Default Anwesend
                                                                      anwesendheit = True
                                                                      lamellenwinkel = 90
                               
                                                                      ## Tageszyklus ohne Anwesendheit
                                                                      [[[[[[nacht_abwesend]]]]]]
                                                                      name = Nacht Abwesend
                                                                      anwesendheit = False  
                                                                      max_helligkeit = 5
                                                                      raummodus = 0
                                                                      lamellenwinkel = -90
                                                                      [[[[[[heizen_winter]]]]]]
                                                                      name = heizen im Winter
                                                                      anwesendheit = False
                                                                      raummodus = 2
                                                                      heizperiode = True
                                                                      heizungsbedarf = True
                                                                      min_solarstrahlung = 100
                                                                      min_sun_azimut = 135
                                                                      max_sun_azimut = 315
                                                                      hoehe = 0
                                                                      lamellenwinkel = heizen
                                                                      [[[[[[heizen]]]]]]
                                                                      name = heizen
                                                                      anwesendheit = False
                                                                      heizungsbedarf = True
                                                                      min_solarstrahlung = 50
                                                                      min_sun_azimut = 135
                                                                      max_sun_azimut = 315
                                                                      #hoehe = auto #hoehe nie verstellen?
                                                                      lamellenwinkel = heizen
                                                                      [[[[[[kuehlen_sommer]]]]]]
                                                                      name = Kühlen im Sommer
                                                                      anwesendheit = False
                                                                      heizperiode = False
                                                                      heizungsbedarf = False
                                                                      min_solarstrahlung = 600
                                                                      min_sun_azimut = 135
                                                                      max_sun_azimut = 315
                                                                      hoehe = 100
                                                                      lamellenwinkel = kuehlen
                                                                      [[[[[[kuehlen]]]]]]
                                                                      name = Kühlen
                                                                      anwesendheit = False
                                                                      heizungsbedarf = False
                                                                      min_solarstrahlung = 50
                                                                      min_sun_azimut = 135
                                                                      max_sun_azimut = 315
                                                                      #hoehe = auto #hoehe nie verstellen?
                                                                      lamellenwinkel = kuehlen
                                                                      [[[[[[default_abwesend]]]]]]
                                                                      name = Default Abwesend
                                                                      anwesendheit = False
                                                                      lamellenwinkel = 0
                               
                              
                                                                      [[[[[[freigabe]]]]]]
                                                                      type = bool
                                                                      value = 1
                                                                      cache = on
                                                                      [[[[[[letzte_position_id]]]]]]
                                                                      type = str
                                                                      cache = on
                                                                      [[[[[[letzte_position_name]]]]]]
                                                                      type = str
                                                                      cache = on
                              
                                              [[[markise]]]
                                                      [[[[so_unten]]]]
                                                              [[[[[automatik]]]]]
                                                              item_helligkeit = Wetter.helligkeit.so
                                                              item_anwesendheit = og.schlafen.anwesendheit
                                                              item_heizungsbedarf = og.schlafen.heizung.bedarf_kuehlen_heizen
                                                              item_raummodus = og.schlafen.betriebsmodus
                                                              item_kunstlicht = og.schlafen.beleuchtung.aktiv
                              
                                                                      [[[[[[putzen]]]]]]
                                                                      name = Putzen
                                                                      putzen = True
                                                                      hoehe = 0
                              
                                                                      ## Tageszyklus mit/ohne Anwesendheit
                                                                      [[[[[[blendenschutz]]]]]]
                                                                      name = Blendenschutz
                                                                      raummodus = 2
                                                                      min_helligkeit = 20000
                                                                      hoehe = 100
                                                                      [[[[[[nacht]]]]]]
                                                                      name = Nacht
                                                                      raummodus = 0
                                                                      hoehe = 100
                                                                      [[[[[[heizen]]]]]]
                                                                      name = Heizen
                                                                      raummodus = 2
                                                                      min_solarstrahlung = 200
                                                                      heizungsbedarf = True
                                                                      min_sun_azimut = 45
                                                                      max_sun_azimut = 225
                                                                      hoehe = 0
                                                                      [[[[[[kuehlen]]]]]]
                                                                      name = Kühlen
                                                                      raummodus = 2
                                                                      min_solarstrahlung = 200
                                                                      heizungsbedarf = False
                                                                      min_sun_azimut = 45
                                                                      max_sun_azimut = 225
                                                                      hoehe = 100
                              
                              
                                                                      [[[[[[freigabe]]]]]]
                                                                      type = bool
                                                                      value = 1
                                                                      cache = on
                                                                      [[[[[[letzte_position_id]]]]]]
                                                                      type = str
                                                                      cache = on
                                                                      [[[[[[letzte_position_name]]]]]]
                                                                      type = str
                                                                      cache = on
                              
                              
                                      [[wohnen]]
                                              [[[beschattung]]]
                                                      [[[[automatik]]]]
                                                              [[[[[freigabe]]]]]
                                                              type = bool
                                                              enforce_updates = true
                                              [[[raffstore]]]   
                                                      [[[[sw_links]]]]
                                                              [[[[[automatik]]]]]
                                                              item_helligkeit = Wetter.helligkeit.sw
                                                              item_anwesendheit = eg.wohnen.anwesendheit
                                                              item_heizungsbedarf = eg.wohnen.heizung.bedarf_kuehlen_heizen
                                                              item_kunstlicht = eg.wohnen.beleuchtung.aktiv
                                                              item_raummodus = eg.wohnen.betriebsmodus
                              
                                                                      [[[[[[putzen]]]]]]
                                                                      name = Putzen
                                                                      anwesendheit = True
                                                                      putzen = True
                                                                      leave_putzen = False
                                                                      lamellenwinkel = hell
                              
                                                                      ## Tageszyklus mit Anwesendheit
                                                                      [[[[[[morgen_daemmerung_anwesend]]]]]]
                                                                      name = Dämmerung am Morgen Anwesend  
                                                                      anwesendheit = True
                                                                      min_helligkeit = 5
                                                                      raummodus = 1
                                                                      lamellenwinkel = beschatten
                                                                      [[[[[[tag_anwesend]]]]]]
                                                                      name = Tag Anwesend  
                                                                      anwesendheit = True
                                                                      min_helligkeit = 20000
                                                                      kunstlicht = False
                                                                      raummodus = 2
                                                                      max_sun_altitude = 41
                                                                      min_sun_azimut = 135
                                                                      max_sun_azimut = 315
                                                                      lamellenwinkel = beschatten
                                                                      [[[[[[tag_anwesend_aussen_hell]]]]]]
                                                                      name = Tag Anwesend Hell ohne Sichtshutz
                                                                      anwesendheit = True
                                                                      min_helligheit = 500
                                                                      kunstlicht = False
                                                                      raummodus = 2
                                                                      lamellenwinkel = 0
                                                                      [[[[[[tag_anwesend_mit_sichtshutz]]]]]]
                                                                      name = Tag Anwesend Hell mit Sichtschutz  
                                                                      anwesendheit = True
                                                                      min_helligheit = 500
                                                                      kunstlicht = True
                                                                      raummodus = 2
                                                                      lamellenwinkel = 90
                                                                      [[[[[[abend_daemmerung_anwesend]]]]]]
                                                                      name = Dämmerung Abend Anwesend  
                                                                      anwesendheit = True
                                                                      min_helligkeit = 300
                                                                      raummodus = 2
                                                                      lamellenwinkel = 90
                                                                      [[[[[[abend_dunkel_dunkel_anwesend]]]]]]
                                                                      name = Abend Dunkel Anwesend
                                                                      anwesendheit = True
                                                                      max_helligkeit = 300
                                                                      raummodus = 2
                                                                      kunstlicht = False
                                                                      lamellenwinkel = 0                                      
                                                                      [[[[[[abend_dunkel_hell]]]]]]
                                                                      name = Abend Dunkel Anwesend
                                                                      max_helligkeit = 300
                                                                      raummodus = 2
                                                                      kunstlicht = True
                                                                      lamellenwinkel = 90                                     
                                                                      [[[[[[nacht_anwesend]]]]]]
                                                                      name = Nacht Anwesend
                                                                      anwesendheit = True  
                                                                      raummodus = 0
                                                                      lamellenwinkel = -90
                                                                      [[[[[[default_anwesend]]]]]]
                                                                      name = Default Anwesend
                                                                      anwesendheit = True
                                                                      lamellenwinkel = 90
                               
                                                                      ## Tageszyklus ohne Anwesendheit
                                                                      [[[[[[nacht_abwesend]]]]]]
                                                                      name = Nacht Abwesend
                                                                      anwesendheit = False  
                                                                      max_helligkeit = 5
                                                                      raummodus = 0
                                                                      lamellenwinkel = -90
                                                                      [[[[[[heizen_winter]]]]]]
                                                                      name = heizen im Winter
                                                                      anwesendheit = False
                                                                      raummodus = 2
                                                                      heizperiode = True
                                                                      heizungsbedarf = True
                                                                      min_solarstrahlung = 100
                                                                      min_sun_azimut = 135
                                                                      max_sun_azimut = 315
                                                                      hoehe = 0
                                                                      lamellenwinkel = heizen
                                                                      [[[[[[heizen]]]]]]
                                                                      name = heizen
                                                                      anwesendheit = False
                                                                      heizungsbedarf = True
                                                                      min_solarstrahlung = 50
                                                                      min_sun_azimut = 135
                                                                      max_sun_azimut = 315
                                                                      #hoehe = auto #hoehe nie verstellen?
                                                                      lamellenwinkel = heizen
                                                                      [[[[[[kuehlen_sommer]]]]]]
                                                                      name = Kühlen im Sommer
                                                                      anwesendheit = False
                                                                      heizperiode = False
                                                                      heizungsbedarf = False
                                                                      min_solarstrahlung = 600
                                                                      min_sun_azimut = 135
                                                                      max_sun_azimut = 315
                                                                      hoehe = 100
                                                                      lamellenwinkel = kuehlen
                                                                      [[[[[[kuehlen]]]]]]
                                                                      name = Kühlen
                                                                      anwesendheit = False
                                                                      heizungsbedarf = False
                                                                      min_solarstrahlung = 50
                                                                      min_sun_azimut = 135
                                                                      max_sun_azimut = 315
                                                                      #hoehe = auto #hoehe nie verstellen?
                                                                      lamellenwinkel = kuehlen
                                                                      [[[[[[default_abwesend]]]]]]
                                                                      name = Default Abwesend
                                                                      anwesendheit = False
                                                                      lamellenwinkel = 0
                              
                               
                                                                      [[[[[[freigabe]]]]]]
                                                                      type = bool
                                                                      value = 1
                                                                      cache = on
                                                                      [[[[[[letzte_position_id]]]]]]
                                                                      type = str
                                                                      cache = on
                                                                      [[[[[[letzte_position_name]]]]]]
                                                                      type = str
                                                                      cache = on
                              beschattungsautomatik.py:
                              Code:
                              # Raffstore Automatik V2
                              #
                              # ThEr081014    Initiale fertigstellung
                              # ThEr141014    Aktivierung über Subitem "Aktiv" anstatt über Attribut "aktiv"
                              # ThEr141014    Item für letzte Position auch unterhalb von "RaffstoreAutomatik"
                              # ThEr141014    Zusätzliches Item für den Namen der letzten Position
                              # JT  240415    Bugfix in Bediengunsauswertung Azimut und Uhrzeit
                              #               Verarbeitung von weitere Items für die Bediengungsauswertung - globale und raumbezogene
                              #               Raumweise Trigger und Verarbeitung möglich
                              #               Berechnung der optimale Lamellenstellung für Beschattung: kein direkter Sonneneinlass, dabei
                              #                  Lamellen möglichst weit offen (Eine gedachte Linie von der Hinterkante eine Lamelle zum Vorderkante
                              #                  der überliegende Lamelle zeigt in richtung Sonne)
                              #               Trennung von behanghöhe und Lamellenwinkel um auch Markisen steuern zu können
                              #               Umstellung Einheiten: Lamellenwinkel in Grad, Behanghoehe in %
                              #               Ausgabeitems als String, ermöglicht angabe der Extremstellungen
                              #               Behhangposition: auto => bei Beschattung und Kühlen Behang nach unten fahren sonnst unverändert NICHT GETSETET!                   
                              #
                              # Einheiten:
                              #   item.conf: hoehe: [%], 0% oben, 100% unten
                              #   item.conf: lamellemwinkel [°], 90° nach oben, -90° nach unten
                              #                   
                              #
                              # ToDo: Lösche alte Werte wenn Freigabe zurückgenommen wird
                              #
                              
                              
                              class RaffstoreAutomatik:
                                  # Konstruktor
                                  def __init__(self, sh):
                              
                                      import math     
                                      logger.info("Initialisiere Raffstore-Automatik")
                              
                                      # Daten übernehmen
                                      self.sh = sh
                                      self.item = None
                              
                                      # Zeit ermitteln
                                      now = time.localtime()        
                                      self.akt_zeit = [now.tm_hour,now.tm_min]
                              
                                      # Position der Sonne ermitteln und in Dezimalgrad umrechnen
                                      azimut, altitude = self.sh.sun.pos()
                                      self.sun_azimut = math.degrees(float(azimut))
                                      self.sun_altitude = math.degrees(float(altitude))
                              
                                      # Solarstrahlung ermitteln
                                      self.solar_radiation = sh.Wetter.davis.solarstrahlung()
                              
                                      # Heizperiode aktiv setzen
                                      self.heizperiode = sh.globals.heizung.kuehlen_heizen()
                              
                                      # Putzmodus ermitteln
                                      self.putzen = sh.globals.putzen()
                              
                                      # Lamellen geometrie
                                      L = 8.4
                                      H = 7.2
                               
                                      # Grenzwert Sonnenhoehe für Beschattung
                                      sun_altitude_lim = math.degrees(math.atan((2*H*L+math.sqrt((2*H*L)**2-4*L**2*H**2))/(2*L**2)))
                              
                                      c = H**2-L**2
                                      a = (math.tan(math.radians(self.sun_altitude)))**2+1.0
                                      b = -2*math.tan(math.radians(self.sun_altitude))*H
                                      x = ((-b+math.sqrt(b**2-4*a*c))/(2*a))/L
                                      if(self.sun_altitude < sun_altitude_lim):
                                          self.shadow_angle = math.degrees(-math.acos(x))
                                      else:
                                          self.shadow_angle = math.degrees(math.acos(x))            
                              
                                      logger.info("Sonnenhöe: {0}, optimale Lamellenwinkel für Beschattung: {1} ".format(self.sun_altitude,self.shadow_angle))
                              
                                      if sh.trigger_source != None and (sh.trigger_source in str(sh.match_items("*.*.anwesendheit")) or sh.trigger_source in str(sh.match_items("*.*.beleuchtung.aktiv"))):
                                          # Automatik für alle Raffstore im Raum des Triggeritems durchführen, die ein Subitem "automatik" mit Subitem "freigabe = 1" haben
                                          room = sh.trigger_source.split('.')[1]
                                          logger.info("Raffstore-Automatik trigger für Raum:{0}".format(room))
                                          items =  sh.match_items("*.{0}.raffstore.*.automatik.freigabe".format(room))
                                          for item in items:              
                                              if (item() == 1):
                                                  self.__run(item.return_parent())
                                          # Automatik für alle Markisen im Raum des Triggeritems durchführen, die ein Subitem "automatik" mit Subitem "freigabe = 1" haben
                                          room = sh.trigger_source.split('.')[1]
                                          logger.info("Markisen-Automatik trigger für Raum:{0}".format(room))
                                          items =  sh.match_items("*.{0}.markise.*.automatik.freigabe".format(room))
                                          for item in items:              
                                              if (item() == 1):
                                                  self.__run(item.return_parent())
                              
                                      else:
                                          # Automatik für alle Raffstore Items durchführen, die ein Subitem "automatik" mit Subitem "freigabe = 1" haben
                                          items =  sh.match_items("*.*.raffstore.*.automatik.freigabe")
                                          logger.info("Raffstore-Automatik trigger für alle Räume")
                                          for item in items:              
                                              if (item() == 1):
                                                  self.__run(item.return_parent())
                                          # Automatik für alle markisen Items durchführen, die ein Subitem "automatik" mit Subitem "freigabe = 1" haben
                                          items =  sh.match_items("*.*.markise.*.automatik.freigabe")
                                          logger.info("Markisen-Automatik trigger für alle Räume")
                                          for item in items:              
                                              if (item() == 1):
                                                  self.__run(item.return_parent())
                              
                              
                                  # Führt die Automatik für ein Raffstore-Item durch
                                  def __run(self, item):
                                      logger.info("Starte Raffstore-Automatik mit Item {0}".format(item.id()))
                              
                                      # Daten übernehmen
                                      self.item = item.return_parent()
                                      self.config = item.conf
                              
                                      # Items holen
                                      self.item_letzte_position_id = self.__get_child_item(item,"letzte_position_id")
                                      self.item_letzte_position_name = self.__get_child_item(item,"letzte_position_name")
                              
                                      try:
                                          self.item_helligkeit = self.sh.return_item(self.config["item_helligkeit"])
                                          if self.item_helligkeit == None:
                                              raise AttributeError("Das für 'item_helligkeit' angegebene Item '%s' ist unbekannt." %(self.config['item_helligkeit']))
                                      except KeyError:
                                          self.item_helligkeit = None
                              
                              
                                      self.item_anwesendheit = self.sh.return_item(self.config["item_anwesendheit"])
                                      if self.item_anwesendheit == None:
                                          raise AttributeError("Das für 'item_anwesendheit' angegebene Item '%s' ist unbekannt." %(self.config['item_anwesendheit']))
                              
                                      self.item_heizungsbedarf = self.sh.return_item(self.config["item_heizungsbedarf"])
                                      if self.item_heizungsbedarf == None:
                                          raise AttributeError("Das für 'item_heizungsbedarf' angegebene Item '%s' ist unbekannt." %(self.config['item_heizungsbedarf']))
                              
                                      self.item_kunstlicht = self.sh.return_item(self.config["item_kunstlicht"])
                                      if self.item_kunstlicht == None:
                                          raise AttributeError("Das für 'item_kunstlicht' angegebene Item '%s' ist unbekannt." %(self.config['item_kunstlicht']))
                              
                                      self.item_raummodus = self.sh.return_item(self.config["item_raummodus"])
                                      if self.item_raummodus == None:
                                          raise AttributeError("Das für 'item_raummodus' angegebene Item '%s' ist unbekannt." %(self.config['item_raummodus']))
                              
                                      self.items_position = self.sh.find_children(self.item, "hoehe")
                                      self.items_position += self.sh.find_children(self.item, "lamellenwinkel")
                                          
                                      logger.info("items_position: {0}".format(self.items_position))
                              
                                      # Anwesendheit ermitteln        
                                      try:
                                          self.anwesendheit = self.item_anwesendheit()            
                                      except:
                                          self.anwesendheit = None
                              
                                      # Relevante Helligkeit ermitteln        
                                      try:
                                          self.helligkeit = self.item_helligkeit()            
                                      except:
                                          self.helligkeit = None
                              
                                      # Heizungsbedarf ermitteln        
                                      try:
                                          self.heizungsbedarf = self.item_heizungsbedarf()            
                                      except:
                                          self.heizungsbedarf = None
                              
                                      # Status Kunstlicht ermitteln        
                                      try:
                                          self.kunstlicht = self.item_kunstlicht()            
                                      except:
                                          self.kunstlicht = None
                              
                                      # Status Raum Betriebsmodus ermitteln        
                                      try:
                                          self.raummodus = self.item_raummodus()            
                                      except:
                                          self.raummodus = None
                              
                                      # Bisherige Position ermitteln
                                      old_pos_item_id = self.item_letzte_position_id()
                                      old_pos_item = self.sh.return_item(old_pos_item_id)
                                      if old_pos_item != None and not self.__check_leave_pos_item(old_pos_item):
                                          logger.info("Position kann nicht verlassen werden")
                                          new_pos_item = old_pos_item
                                      else:       
                                          # Passende Position heraussuchen
                                          new_pos_item = self.__find_pos_item()
                                          if new_pos_item == None:
                                              logger.info("Keine passende Position gefunden!")
                                              return
                              
                                          # Position im Item "Modus" speichern
                                          new_pos_item_id = new_pos_item.id()     
                                          new_pos_item_name = new_pos_item._name      
                                          self.item_letzte_position_id(new_pos_item_id)
                                          self.item_letzte_position_name(new_pos_item_name)
                                          logger.info("Neue Position: '{0}' ({1})".format(new_pos_item_name,new_pos_item_id))
                              
                              
                                      # Raffstoreposition aus dem Positions-Item ermitteln
                                      #position = self.__get_position_from_pos_item(new_pos_item)
                                      ret = self.__get_angle_from_pos_item(new_pos_item)
                                      angle = ret[0]
                                      anglecontrol = ret[1]
                                      height = self.__get_height_from_pos_item(new_pos_item, anglecontrol)
                              
                                      # Raffstoreposition anfahren
                                      if height == None: return
                                      try:
                                          logger.info("Fahre auf Höhe {0:.0f}%, Lamelle {1:.1f}°".format(height, angle))
                                      except ValueError:
                                          logger.info("Fahre auf Höhe {0}, Lamelle {1}".format(height, angle))
                              
                                      #Items für Raffstoresteuerung holen
                                      item_hoehe = self.__get_child_item(self.item,"behangposition_prozent")
                                      try:
                                          item_lamelle = self.__get_child_item(self.item,"lamellenwinkel_grad")
                                          logger.info("item_lamelle: {0}".format(item_lamelle))
                                      except AttributeError: # ToDo: nur wenn Markise erlauben
                                          item_lamelle = None
                                          
                              
                                      # Fahrbefehl für Höhe nur senden, wenn wir um mindestens 10% verändern //Attribut "hoehe" existiert und
                                      try:
                                          hoehe_delta = self.__byte2rel(item_hoehe()) - height            
                                          if (abs(hoehe_delta) > 10):
                                              #item_hoehe(position[0])
                                              item_hoehe(int(self.__rel2byte(height) + 0.5))
                                      except:
                                          pass
                              
                                      # Fahrbefehl für Lamelle nur, wenn "lamellenwinkel" exisitiert, der Raffstore um mindestens 10% herabgelassen ist
                                      # und die Winkeländerung > 2° ist        
                                      try:
                                          if item_lamelle != None:
                                              if len(item_lamelle()) == 0: # Lamellenitem leer?
                                                  logger.debug("item_lamelle: {0}".format(item_lamelle))
                                                  logger.debug("angle: {0}".format(angle))
                                                  try:
                                                      winkel_delta = float(item_lamelle()) - angle
                                                      if (abs(winkel_delta) >= 2 and height > 10):
                                                          item_lamelle(str(int(angle + 0.5)))
                                                  except (ValueError, TypeError):
                                                      if angle != item_lamelle():
                                                          item_lamelle(str(angle))
                                              else:
                                                  try:
                                                      winkel_delta = float(item_lamelle()) - angle
                                                      if (abs(winkel_delta) >= 2 and height > 10):
                                                          item_lamelle(str(int(angle + 0.5)))
                                                  except (ValueError, TypeError):
                                                      if angle != item_lamelle():
                                                          item_lamelle(str(angle))
                                      except ValueError:
                                          logger.info("Fehler in Lamellentem: {0}='{1}'".format(item_lamelle, item_lamelle()))
                               
                                  # Liest die Positionsinformationen aus einem Item und gibt Sie im Format "Liste [%Höhe,%Lamelle]" zurück
                                  def __get_position_from_pos_item(self, item):
                                      if not 'position' in item.conf:
                                          id = item.id()
                                          logger.error("Das Item '{0}' enthält kein Attribut 'position'".format(id))
                                          return None
                              
                                      value = item.conf['position']
                                      if value == 'auto':
                                          return self.__get_position_from_sun()
                              
                                      value_parts = value.split(",")
                                      if len(value_parts) != 2:
                                          id = item.id()
                                          logger.error("Das Konfigurations-Attribut '{0}' im Item '{1}' muss im Format '###, ###' angegeben werden.".format(attribute, id))
                                          return None
                                      else:
                                          try:
                                              hoehe = int(value_parts[0])
                                              lamelle = int(value_parts[1])
                                              return [hoehe,lamelle]
                                          except ValueError:
                                              id = item.id()
                                              logger.error("Das Konfigurations-Attribut '{0}' im Item '{1}' muss im Format '###, ###' angegeben werden.".format(attribute, id))
                                              return None
                              
                                  # Liest die Positionsinformationen aus einem Item und gibt die Lamellenwinkel zurück
                                  def __get_angle_from_pos_item(self, item):
                                      if not 'lamellenwinkel' in item.conf: # lamellenwinkel nicht ändern
                                          return [None, 'None']
                                      value = item.conf['lamellenwinkel']
                                      if value in ['down', 'min', 'unten', 'max_down', 'max_unten', 'cooling', 'kuehlen', '-90']:
                                          return ['max_unten', 'kuehlen']
                                      elif value == 'heizen':
                                          return [self.__get_angle_from_sun_heating(), 'heizen']
                                      elif value == 'hell':
                                          return [self.__get_angle_from_sun_heating(), 'hell']
                                      elif value == 'beschatten':
                                          return [self.__get_angle_from_sun_shadow(), 'beschatten']
                                      else:
                                          try:
                                              angle = int(value)
                                              return [angle, 'None']
                                          except ValueError:
                                              id = item.id()
                                              logger.error("Das Konfigurations-Attribut '{0}' im Item '{1}' ist Fehlerhaft.".format(attribute, id))
                                              return None
                              
                              
                                          
                                  # Liest die Positionsinformationen aus einem Item und gibt die Höhe zurück
                                  def __get_height_from_pos_item(self, item, anglecontrol):
                                      if not 'hoehe' in item.conf: # hohe nicht ändern verwende jetztige Wert
                                          item_hoehe = self.__get_child_item(self.item,"behangposition_prozent")
                                          return item_hoehe()
                              
                                          
                                      value = item.conf['hoehe']
                                      if value == 'auto':
                                          if value in ['kuehlen', 'beschatten']:
                                              return 100
                                      else:
                                          try:
                                              hoehe = int(value)
                                              return hoehe
                                          except ValueError:
                                              id = item.id()
                                              logger.error("Das Konfigurations-Attribut '{0}' im Item '{1}' ist Fehlerhaft.".format(attribute, id))
                                              return None
                              
                              
                                  # Liefert eine Positionsangabe für den Raffstore basierend auf dem Sonnenstand
                                  # Zur Nachführung wird der Raffstore ganz heruntergefahren und versucht,
                                  # den Lamellenwinkel senkrecht zur Sonne zu stellen.
                                  def __get_position_from_sun(self):
                                      #TODO % => byte
                                      logger.info("Sonnenposition: Azimut {0} Altitude {1}".format(self.sun_azimut,self.sun_altitude))
                              
                                      # Raffstore senkrecht zur Sonne stellen
                                      winkel = 90-self.sun_altitude
                                      logger.info("Winkel auf {0}°".format(winkel))
                              
                                      # Umrechnen auf Wert (90° = 0%, 0° = 50%, -90° = 100%)
                                      prozent = 50-winkel/90*50
                                      logger.info("Lamelle auf {0}%".format(prozent))
                              
                                      return [100,prozent]
                              
                                  # Liefert ein Lamellenwinkel in % für Heizbetrieb basierend auf dem Sonnenstand
                                  def __get_angle_from_sun_heating(self):
                                      logger.info("Sonnenposition: Azimut {0} Altitude {1}".format(self.sun_azimut,self.sun_altitude))
                              
                                      # Lamellen richtung Sonne stellen
                                      winkel = self.sun_altitude
                                      logger.info("Winkel auf {0}°".format(winkel))
                              
                                      # Umrechnen auf Wert (90° = 0%, 0° = 50%, -90° = 100%)
                                      # prozent = 50-winkel/90*50
                                      #byte = self.__deg2byte(winkel)
                                      #logger.info("Lamelle auf {0}/255".format(byte))
                              
                                      return winkel
                              
                                  # Liefert ein Lamellenwinkel in % für eine optimierte Beschattung basierend auf dem Sonnenstand
                                  def __get_angle_from_sun_shadow(self):
                                      logger.info("Sonnenposition: Azimut {0} Altitude {1}".format(self.sun_azimut,self.sun_altitude))
                              
                                      # Lamellen einstellen so, dass gearde alles abgeschattet ist
                                      winkel = self.shadow_angle
                                      logger.info("Winkel auf {0}°".format(winkel))
                                      
                                      return winkel
                              
                              
                                  # Sucht ein bestimmtes Item unterhalb eines gegebenen Items
                                  # Wenn das Item gefunden wird, wird es zurückgegeben
                                  # Wird das Item nicht gefunden, wird ein AttributeError geworfen
                                  def __get_child_item(self, item, child_id):
                                      search_id = item.id()+"."+child_id
                                      for child in item.return_children():
                                          if child.id() == search_id:
                                              return child
                                      itemId = self.item.id()
                                      raise AttributeError("Unterhalb des Items '%s' fehlt ein Item '%s'" %(itemId, child_id))
                              
                                  # Loopt durch alle Positionen und liefert die erste Position zurück, bei der alle Bedingungen erfüllt sind
                                  def __find_pos_item(self):
                                      logger.info("Suche Item für Zeit = {0}, Helligkeit = {1}, Sonnenhöhe = {2:.1f}°, Sonne Azimut: {3:.1f}°, Heizperiode: {4}, Putzmodus: {5}, Anwesendheit = {6}, Heizungsbedarf = {7}, Solarstrahlung = {8}, Kunstlicht aktiv = {9}, Raummodus = {10}".format(self.akt_zeit, self.helligkeit, self.sun_altitude, self.sun_azimut, self.heizperiode, self.putzen, self.anwesendheit, self.heizungsbedarf, self.solar_radiation, self.kunstlicht, self.raummodus))    
                                      for item in self.items_position:
                                          if self.__check_enter_pos_item(item):
                                              return item
                                      return None
                              
                                  # Prüft, ob die in einem Positions-Item erfassten Leave-Bedingungen erfüllt sind, so dass die Position wieder verlassen werden darf
                                  # position: Positions-Item mit den Bedingungen als Attribute
                                  # Rückgabe: TRUE: Position darf verlassen werden, FALSE: Position darf nicht verlassen werden
                                  def __check_leave_pos_item(self, position):
                                      id = position.id()
                                      logger.info("Prüfe ob Position '{0}' verlassen werden darf".format(id))
                              
                                      # Helligkeitsbedingung      
                                      if 'leave_min_helligkeit' in position.conf and self.helligkeit < int(position.conf['leave_min_helligkeit']):
                                          logger.info(" -> zu dunkel")
                                          return False;
                                      if 'leave_max_helligkeit' in position.conf and self.helligkeit > int(position.conf['leave_max_helligkeit']):
                                          logger.info(" -> zu hell")
                                          return False;
                              
                                      # Solarstrahlung
                                      if 'leave_min_solarstrahlung' in position.conf and self.solar_radiation < int(position.conf['leave_min_solarstrahlung']):
                                          logger.info(" -> Solarstrahlung zu gering")
                                          return False;
                                      if 'leave_max_solarstrahlung' in position.conf and self.solar_radiation > int(position.conf['leave_max_solarstrahlung']):
                                          logger.info(" -> Solarstrahlung zu hoch")
                                          return False;
                              
                              
                                      # Zeitbedingung
                                      if 'leave_min_zeit' in position.conf or 'leave_max_zeit' in position.conf:
                                          min_zeit = self.__get_time_attribute(position,"leave_min_zeit",[0,0])
                                          max_zeit = self.__get_time_attribute(position, "leave_max_zeit", [24,00])   
                                          if self.__compare_time(min_zeit, max_zeit) != 1:
                                              # min </= max: Normaler Vergleich
                                              if self.__compare_time(self.akt_zeit, min_zeit) == -1 or self.__compare_time(self.akt_zeit, max_zeit) == 1:
                                                  logger.info(" -> außerhalb der Zeit (1)")
                                                  return False
                                          else:
                                              # min > max: Invertieren
                                              #if self.__compare_time(self.akt_zeit, min_zeit) == 1 and self.__compare_time(self.akt_zeit, min_zeit) == -1:
                                              if self.__compare_time(self.akt_zeit, min_zeit) == -1 and self.__compare_time(self.akt_zeit, max_zeit) == 1:
                                                  logger.info(" -> außerhalb der Zeit (2)")
                                                  return False
                              
                                      # Putzbedingung
                                      if 'leave_putzen' in position.conf and self.putzen != self.__cast_bool(position.conf['leave_putzen']):
                                          logger.info(" -> Putzmodus nicht {0}".format(str(self.__cast_bool(position.conf['putzen']))))
                                          return False;
                              
                                      # Sonnenhöhe
                                      if 'leave_min_sun_altitude' in position.conf and self.sun_altitude < int(position.conf['leave_min_sun_altitude']):
                                          logger.info(" -> Sonne zu niedrig")
                                          return False
                                      if 'leave_max_sun_altitude' in position.conf and self.sun_altitude > int(position.conf['leave_max_sun_altitude']):
                                          logger.info(" -> Sonne zu hoch")
                                          return False
                              
                                      # Sonnenrichtung
                                      if 'leave_min_sun_azimut' in position.conf or 'leave_max_sun_azimut' in position.conf:
                                          min_azimut = 0
                                          max_azimut = 90
                                          if 'leave_min_sun_azimut' in position.conf:
                                              min_azimut = int(position.conf['leave_min_sun_azimut'])         
                                          if 'leave_max_sun_azimut' in position.conf:
                                              max_azimut = int(position.conf['leave_max_sun_azimut'])
                              
                                          if min_azimut < max_azimut:
                                              if self.sun_azimut < min_azimut or self.sun_azimut > max_azimut:
                                                  logger.info(" -> außerhalb der Sonnenrichtung (1)")
                                                  return False;               
                                          else:
                                              #if self.sun_azimut > min_azimut and self.sun_azimut < max_azimut:
                                              if self.sun_azimut < min_azimut and self.sun_azimut > max_azimut:
                                                  logger.info(" -> außerhalb der Sonnenrichtung (2)")
                                                  return False;
                              
                                      # Alle Bedingungen erfüllt
                                      logger.info(" -> passt".format(position.id()));
                                      return True
                              
                                  # Prüft, ob die in einem Positions-Item erfassten Bedingungen erfüllt sind, so dass die Position geeignet ist
                                  # position: Positions-Item mit den Bedingungen als Attribute
                                  # Rückgabe: TRUE: Position ist geeignet, FALSE: Position ist nicht geeignet
                                  def __check_enter_pos_item(self, position):
                                      id = position.id()
                                      logger.info("Prüfe ob Position '{0}' geeignet ist ".format(id))
                              
                                      # Anwesendheitsbedingung
                                      if 'anwesendheit' in position.conf and self.anwesendheit != self.__cast_bool(position.conf['anwesendheit']):
                                          logger.info(" -> Anwesendheit nicht {0}".format(str(self.__cast_bool(position.conf['anwesendheit']))))
                                          return False;
                              
                                      # Heizungsbedingung
                                      if 'heizungsbedarf' in position.conf and self.heizungsbedarf != self.__cast_bool(position.conf['heizungsbedarf']):
                                          logger.info(" -> Heizungsbedarf nicht {0}".format(str(self.__cast_bool(position.conf['heizungsbedarf']))))
                                          return False;
                              
                                      # Kunstlichtbedingung
                                      if 'kunstlicht' in position.conf and self.kunstlicht != self.__cast_bool(position.conf['kunstlicht']):
                                          logger.info(" ->Kunstlicht aktiv nicht {0}".format(str(self.__cast_bool(position.conf['kunstlicht']))))
                                          return False;
                              
                                      # Raummodusbedingung
                                      if 'raummodus' in position.conf and self.raummodus != int(position.conf['raummodus']):
                                          logger.info(" ->Raummodus nicht {0}".format(str(int(position.conf['raummodus']))))
                                          return False;
                              
                                      # Heizperiode aktiv
                                      if 'heizperiode' in position.conf and self.heizperiode != self.__cast_bool(position.conf['heizperiode']):
                                          logger.info(" ->Heizperiode nicht {0}".format(str(self.__cast_bool(position.conf['heizperiode']))))
                                          return False;
                              
                                      # Putzenbedingung
                                      if 'putzen' in position.conf and self.putzen != self.__cast_bool(position.conf['putzen']):
                                          logger.info(" ->Putzmodus nicht {0}".format(str(self.__cast_bool(position.conf['putzen']))))
                                          return False;
                              
                              
                                      # Helligkeitsbedingung
                                      if 'min_helligkeit' in position.conf and self.helligkeit < int(position.conf['min_helligkeit']):
                                          logger.info(" -> zu dunkel")
                                          return False;
                                      if 'max_helligkeit' in position.conf and self.helligkeit > int(position.conf['max_helligkeit']):
                                          logger.info(" -> zu hell")
                                          return False;
                              
                                      # Solarstrahlung
                                      if 'min_solarstrahlung' in position.conf and self.solar_radiation < int(position.conf['min_solarstrahlung']):
                                          logger.info(" -> Solarstrahlung zu gering")
                                          return False;
                                      if 'max_solarstrahlung' in position.conf and self.solar_radiation > int(position.conf['max_solarstrahlung']):
                                          logger.info(" -> Solarstrahlung zu hoch")
                                          return False;
                              
                              
                                      # Zeitbedingung
                                      if 'min_zeit' in position.conf or 'max_zeit' in position.conf:
                                          min_zeit = self.__get_time_attribute(position,"min_zeit",[0,0])
                                          max_zeit = self.__get_time_attribute(position, "max_zeit", [24,00])
                                          if self.__compare_time(min_zeit, max_zeit) != 1:
                                              # min </= max: Normaler Vergleich
                                              if self.__compare_time(self.akt_zeit, min_zeit) == -1 or self.__compare_time(self.akt_zeit, max_zeit) == 1:
                                                  logger.info(" -> außerhalb der Zeit (1)")
                                                  return False
                                          else:
                                              # min > max: Invertieren
                                              #if self.__compare_time(self.akt_zeit, min_zeit) == 1 and self.__compare_time(self.akt_zeit, min_zeit) == -1:
                                              if self.__compare_time(self.akt_zeit, min_zeit) == -1 and self.__compare_time(self.akt_zeit, max_zeit) == 1:
                                                  logger.info(" -> außerhalb der Zeit (2)")
                                                  return False
                              
                                      # Sonnenhöhe
                                      if 'min_sun_altitude' in position.conf and self.sun_altitude < int(position.conf['min_sun_altitude']):
                                          logger.info(" -> Sonne zu niedrig")
                                          return False
                                      if 'max_sun_altitude' in position.conf and self.sun_altitude > int(position.conf['max_sun_altitude']):
                                          logger.info(" -> Sonne zu hoch")
                                          return False
                              
                                      # Sonnenrichtung
                                      if 'min_sun_azimut' in position.conf or 'max_sun_azimut' in position.conf:
                                          min_azimut = 0
                                          max_azimut = 90
                                          if 'min_sun_azimut' in position.conf:
                                              min_azimut = int(position.conf['min_sun_azimut'])           
                                          if 'max_sun_azimut' in position.conf:
                                              max_azimut = int(position.conf['max_sun_azimut'])
                              
                                          if min_azimut < max_azimut:
                                              if self.sun_azimut < min_azimut or self.sun_azimut > max_azimut:
                                                  logger.info(" -> außerhalb der Sonnenrichtung (1)")
                                                  return False;               
                                          else:
                                              #if self.sun_azimut > min_azimut and self.sun_azimut < max_azimut:
                                              if self.sun_azimut < min_azimut and self.sun_azimut > max_azimut:
                                                  logger.info(" -> außerhalb der Sonnenrichtung (2)")
                                                  return False;               
                              
                                      # Alle Bedingungen erfüllt
                                      logger.info(" -> passt".format(position.id()));
                                      return True
                              
                                  # Ermittelt und prüft ein Zeit-Attribut und liefert es im Format "Liste [Stunde, Minute]" zurück
                                  def __get_time_attribute(self, item, attribute, default):
                                      if not attribute in item.conf: return default
                              
                                      value = item.conf[attribute]
                                      value_parts = value.split(",")
                                      if len(value_parts) != 2:
                                          id = item.id()
                                          logger.error("Das Konfigurations-Attribut '{0}' im Item '{1}' muss im Format '###, ###' angegeben werden.".format(attribute, id))
                                      else:
                                          try:
                                              stunde = int(value_parts[0])
                                              minute = int(value_parts[1])
                                              return [stunde,minute]
                                          except ValueError:
                                              id = item.id()
                                              logger.error("Das Konfigurations-Attribut '{0}' im Item '{1}' muss im Format '###, ###' angegeben werden.".format(attribute, id))
                                              return default
                              
                                  # Vergleicht zwei Zeitwerte (als Liste [Stunde, Minute])
                                  # -1: Zeit1 < Zeit2
                                  # 0: Zeit1 = Zeit2
                                  # 1: Zeit 1 > Zeit 2
                                  def __compare_time(self, zeit1, zeit2):
                                      if zeit1[0] < zeit2[0]:
                                          return -1
                                      elif zeit1[0] > zeit2[0]:
                                          return 1
                                      else:
                                          if zeit1[1] < zeit2[1]:
                                              return -1
                                          elif zeit1[1] > zeit2[1]:
                                              return 1
                                          else:
                                              return 0
                              
                                  def __cast_bool(self, value):
                                      if type(value) in [bool, int, float]:
                                          if value in [False, 0]:
                                              return False
                                          elif value in [True, 1]:
                                              return True
                                          else:
                                              raise ValueError
                                      elif type(value) in [str, str]:
                                          if value.lower() in ['0', 'false', 'no', 'off']:
                                              return False
                                          elif value.lower() in ['1', 'true', 'yes', 'on']:
                                              return True
                                          else:
                                              raise ValueError
                                      else:
                                          raise TypeError
                                  
                              # Raffstore-Automatik aufrufen (Klasse instanziieren, den Rest macht der Konstruktor ...)
                              sh.trigger_source = trigger['source']
                              logger.info("trigger_source: {0}".format(str(sh.trigger_source)))
                              RaffstoreAutomatik(sh)

                              Kommentar

                              Lädt...
                              X