Elektrischen Rolladen abhängig von Sonnenstand und Wetter steuern lassen

Für welche Projekte verwendet Ihr OpenHAB? Was habt Ihr automatisiert? Stellt eure Projekte hier vor.

Moderatoren: Cyrelian, seppy

Antworten
Benutzeravatar
udo1toni
Beiträge: 15291
Registriert: 11. Apr 2018 18:05
Answers: 246
Wohnort: Darmstadt

Re: Elektrischen Rolladen abhängig von Sonnenstand und Wetter steuern lassen

Beitrag von udo1toni »

Mein Code war quasi der vollständige Codeschnipsel, um die Läden zu fahren.

Code: Alles auswählen

val telegramAction = getActions("telegram","telegram:telegramBot:telegram")
blinds.members.forEach[blind|
    telegramAction.sendTelegram(5128384828L, "Verschattung für " + blind.label + " aktiviert, schließen auf " + targetHights.get(blind.name) + " %%")
    blind.sendCommand(targetHights.get(blind.name))
]
sendet pro Member von blinds ein Telegram für die Verschattung und fährt anschließend diesen Member auf die Sollhöhe.
Das hast Du in Deinem Code schon drin, nur hast Du diesen Teil ursprünglich nicht gepostet... Bei Dir ist es die Schleife shutters.allMembers.forEach[]

Zu Deinem Code (in der Reihenfolge des Auftretens...):
Erst mal wäre es sinnvoll, alle nicht (mehr) benötigten Variablen zu entfernen. Das betrifft z.B. message, filename, blind* und die *Temperature Variablen bzw. Konstanten.
Weiterhin hast Du die Variablen DWD1 und DWD2 global definiert, definierst sie jedoch innerhalb der Rule erneut. Entweder global, oder lokal definieren, aber bitte nicht beides gleichzeitig.

Die Sache mit den Funktionen... kann man machen, trägt allerdings nicht unbedingt zur Lesbarkeit des Codes bei ;) aber sei's drum.
Die Map... Du verwendest eine HashMap, entsprechend solltest Du auch eine HashMap als Typ verwenden, nicht einfach Map (gilt auch für den import).

log-Befehle... ganz grundsätzlich: der erste String ergänzt den Loggernamen, der zweite String ist die Meldung.
Über den Loggernamen kann man das LogLevel zur Ausführungszeit steuern, also die Meldungen "ein- bzw. ausschalten", und zwar in Abhängigkeit des Meldungstyps (Debug, Info, Warn, Error). Es erscheint sinnvoll, für unterschiedliche Bereiche auch unterschiedliche Loggernamen zu verwenden."rules" ist kein sinnvoller Loggername, denn alle aus Rules heraus generierten Logeinträge nutzen als Namespace org.openhab.core.model.script, wobei im log die letzen 36 Zeichen angezeigt werden. sinnvoll könnte z.B. "shutter" sein, damit kannst Du dann alle Logmeldungen im Zusammenhang mit der Verschattung gemeinsam ein- oder ausschalten, ohne aber Logmeldungen anderer Rules zu beeinflussen.
logXxxx() beherrscht für den zweiten String Substitution, das ist sehr hilfreich wenn man variable Inhalte ausgeben möchte. Natürlich kann man Strings verketten, das ist aber schlecht lesbar:

Code: Alles auswählen

logInfo("rules", logPrefix + "Fahre Rollladen (" + blind.name + ") auf (" + targetHights.get(blind.name).intValue + ") %" )
Besser:

Code: Alles auswählen

logInfo("shutter", "Fahre Rollladen {} auf {} %",blind.name, targetHights.get(blind.name))
Und hier könnte man natürlich blind.name durch blind.label ersetzen.
Der Witz: Du nutzt das an anderer Stelle im Code schon...

Weiter im Text...

Wie erwähnt, liefert .name genau wie .label ein String Objekt. .toString() als Methode aufzurufen ist hier unsinnig. Die HashMap liefert Number zurück, das könnte man bequem auf Integer ändern, dann kann man sich das .intValue ersparen.

Als Beispiel sollte das Lambda shadingStart also eher so aussehen:

Code: Alles auswählen

val shadingStart = [GroupItem shutter |
    val HashMap<String, Integer> targetHights = newHashMap(
        "Shellyswitch25LesezimmerRollerControl"       ->  40,
        "Shellyswitch25BueroRollerControl"            -> 100,
        "Shellyswitch25SZRechtsRollerControl"         ->  90,
        "Shellyswitch25SZLinksRollerControl"          ->  85,
        "Shellyswitch25BadRollerControl"              ->  80,
        "Shellyswitch25RollladenRaphaelRollerControl" ->  80,
        "Shellyswitch25KucheRollerControl"            ->   0
    )
    // Rollladen werden geschlossen
    logInfo("shutter", "Grenzwerte wurden erreicht, Rollladen werden geschlossen")
    val telegramAction = getActions("telegram","telegram:telegramBot:telegram")
    shutter.allMembers.forEach[ blind |
        val iTarget = targetHights.get(blind.name)
        if((blind.state as Number).intValue < iTarget) {
            logInfo("shutter", "Fahre Rollladen {} auf {} %", blind.label, iTarget)
            blind.sendCommand(iTarget)
            // Telegram versenden
            telegramAction.sendTelegram(5128384828L, "Verschattung für " + blind.label + " aktiviert, schließen auf " + targetHights.get(blind.name) + " %%")
        } else {
            logInfo("shutter", "Rollladen {} ist bereits weiter geschlossen ({} %) als er geschlossen werden sollte und wird daher ignoriert", blind.label, blind.state)
        }
    ]
    AutoShading_Start_last.postUpdate(new DateTimeType())
    return;
]
Ich nutze selbst keine Lambda Funktionen, weil es gerne Probleme insbesondere mit lokalen Variablen gibt. Hier wäre telegramAction so ein Kandidat. Es wäre aber ohnehin sinnvoller, die verfahrenen Rollläden zu sammeln und in einer Nachricht zu versenden (siehe zweiter Teil meines vorigen Posts), z.B. so:

Code: Alles auswählen

val shadingStart = [GroupItem shutter |
    val HashMap<String, Integer> targetHights = newHashMap(
        "Shellyswitch25LesezimmerRollerControl"       ->  40,
        "Shellyswitch25BueroRollerControl"            -> 100,
        "Shellyswitch25SZRechtsRollerControl"         ->  90,
        "Shellyswitch25SZLinksRollerControl"          ->  85,
        "Shellyswitch25BadRollerControl"              ->  80,
        "Shellyswitch25RollladenRaphaelRollerControl" ->  80,
        "Shellyswitch25KucheRollerControl"            ->   0
    )
    // Rollladen werden geschlossen
    logInfo("shutter", "Grenzwerte wurden erreicht, Rollladen werden geschlossen")
    var String strNewPos = "Folgende Rollläden wurden geschlossen: " //39 Zeichen
    var String strOldPos = "Folgende Rollläden bleiben auf Position: " // 41 Zeichen
    shutter.allMembers.forEach[ blind |
        val iTarget = targetHights.get(blind.name)
        if((blind.state as Number).intValue < iTarget) {
            logInfo("shutter", "Fahre Rollladen {} auf {} %", blind.label, iTarget)
            if(strNewPos.length > 39) strNewPos = strNewPos + ", "
            strNewPos = strNewPos + blind.label + " (" + iTarget.toString + " %)"
            blind.sendCommand(iTarget)
        } else {
            if(strOldPos.length > 41) strOldPos = strOldPos + ", "
            strOldPos = strOldPos + blind.label + " (" + blind.state.toString + " %)"
            logInfo("shutter", "Rollladen {} ist bereits weiter geschlossen ({} %) als er geschlossen werden sollte und wird daher ignoriert", blind.label, blind.state)
        }
    ]
    val telegramAction = getActions("telegram","telegram:telegramBot:telegram")
    telegramAction.sendTelegram(5128384828L, strNewPos + ". " + strOldPos + ".")
    AutoShading_Start_last.postUpdate(new DateTimeType())
    return;
]
Falls es ein Problem mit den lokalen Variablen strNewPos und strOldPos gibt, können die auch einfach global definiert werden. Innerhalb des Lambdas lässt Du dann das var davor weg, dann wird die Variable beim Aufruf des Lambdas "initialisiert" und ansonsten normal mit der entsprechenden Information befüllt. Natürlich kann man den zweiten String auch komplett weg lassen. Da die beiden Teilstrings strNewPos und strOldPos (die Items, welche einen Befehl erhalten und die, welche keinen Befehl erhalten) voneinander unabhängig sind, muss das Komma dynamisch ergänzt werden, wenn die alte Zeichenkette schon mindestens ein Label enthält.
Man könnte es noch auf die Spitze treiben und das letzte Komma durch ein " und" ersetzen...
openHAB5.0.0 stable in einem Debian-Container (bookworm) (Proxmox 8.4.5, LXC)

Benutzeravatar
udo1toni
Beiträge: 15291
Registriert: 11. Apr 2018 18:05
Answers: 246
Wohnort: Darmstadt

Re: Elektrischen Rolladen abhängig von Sonnenstand und Wetter steuern lassen

Beitrag von udo1toni »

Nachtrag:

Betreffs der letzten Rule:

Code: Alles auswählen

rule "Tag und Nacht"
when
    Item Elevation changed or System started
then
    if(Elevation.state > 0|°) {
        if (IsDay.state!=ON) {
            logInfo("AstroRules:Tag und Nacht", "Sonnenstand > 0 Grad - Es ist Tag")
              IsTwilight.postUpdate(OFF)
              IsDay.postUpdate(ON)
              IsNight.postUpdate(OFF)
        }
      }
      if(Elevation.state <= 0|° && Elevation.state >= -6|°) {
          if (IsDay.state!=OFF) {
              logInfo("AstroRules:Tag und Nacht", "Sonnenstand ist zwischen 0 und -6 Grad - Es ist Abenddämmerung")
              IsTwilight.postUpdate(ON)
              IsDay.postUpdate(OFF)
              IsNight.postUpdate(ON)
         }            
      }
      if(Elevation.state < -6|°) {
        if(IsTwilight.state!=OFF) {
            logInfo("AstroRules:Tag und Nacht", "Sonnenstand < -6 Grad - Es ist Nacht")
              IsTwilight.postUpdate(OFF)
              IsDay.postUpdate(OFF)
              IsNight.postUpdate(ON)
          }
      }
end
Mein Tipp an dieser Stelle wäre eine (völlig) andere Herangehensweise. Definiere ein String Item (ich nenne es hier mal daylight).
Dieses Item verlinkst Du mit dem elevation Channel. Dabei verwendest Du die SCALE Transformation und lässt für den Wertebereich < -6 den Text NIGHT, für den Wertebereich >= -6 < 0 den Text TWILIGHT und für den Wertebereich > 0 den Text DAY setzen.
Innerhalb der Rules kannst Du dann dieses Item auf den enthaltenen Text prüfen.
/etc/openhab/transform/daytime.scale (oder über die UI erzeugen):

Code: Alles auswählen

[..-6[=NIGHT
[-6..0]=TWILIGHT
]0..]=DAY
Itemdefinition (als XText Definition, geht natürlich auch per UI)

Code: Alles auswählen

String daylight "Tageslicht" [Status] { channel="astro:sun:local:position#elevation" [profile="transform:SCALE", function="daytime.scale"] }
Statt der Logs gibt es dann nur noch State Changes für das Item, das sollte aber zu verschmerzen sein.
Nun statt

Code: Alles auswählen

if (IsDay.state != ON)
einfach

Code: Alles auswählen

if (daylight.state.toString != "DAY")
schreiben (und an anderen Stellen entsprechend)
openHAB5.0.0 stable in einem Debian-Container (bookworm) (Proxmox 8.4.5, LXC)

Backbe01
Beiträge: 129
Registriert: 19. Jul 2019 21:04
Answers: 0

Re: Elektrischen Rolladen abhängig von Sonnenstand und Wetter steuern lassen

Beitrag von Backbe01 »

Hallo Udo, vielen Dank für Deine ausführliche Antwort! Ich bin ehrlich, ich habe Deinen Post jetzt dreimal gelesen, aber leider nur die Hälfte (wahrscheinlich sogar weniger) verstanden. Ich könnte mir gut vorstellen, wenn Du meine anderen, selbstgebauten rules sehen würdest, würden Dir die Haare zu Berge stehen. :lol:

Die Logs werde ich anpassen, klingt für mich logisch und nachvollziehbar. Die Variablen für DWD klingt auch logisch. Zu Hashmap habe ich null Ahnung. Genauso wie von Funktionen und Lambdas oder welche Variablen ich entfernen kann. Die Rule ist ja von Cyrelian. Gerade die ersten Zeilen sind für mich sowieso böhmische Dörfer…

Sitze gerade „nur“ am iPad. Werde mir die nächsten Tage mal Deine Vorschläge anschauen. Bisher hat die rule für mich immer gut funktioniert, aber ich lerne ja auch gerne dazu.

Die Day/Night Geschichte werde ich auch so belassen, oder ich überlegs mir… :D Die brauche ich auch nur für die rule.

Problem ist momentan auch, dass ich derzeit wegen dem Wetter, nicht so gut testen kann.

Ich habe die rule nur für eventuell Interessierte eingestellt. Und ja, da hast Du Recht, dann sollte sie auch hübsch sein… Ich setz mich dran!!! 8-)
OH 4.3.0 M1 auf nuc in Docker

Antworten