Heizungsrule und Verbesserungsmöglichkeiten

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

Moderatoren: Cyrelian, seppy

MrCrashy
Beiträge: 113
Registriert: 2. Jan 2021 09:53
Answers: 0

Heizungsrule und Verbesserungsmöglichkeiten

Beitrag von MrCrashy »

Hallo Community,
nach langer Auszeit habe ich mich mal endlich wieder mit unserem SmartHome beschäftigt. Also habe ich mir von tado° die Heizungsthermostate geholt und diese in OH3 eingebunden. Die Rule für die Heizungssteuerung funktioniert auch ohne ausfälle. Aber ich möchte gerne dazu lernen, möchte meine Rules verbessen und schauen wie sich das System optimieren lässt.
Mich würde interessieren, ob man die Rule auch einkürzen kann und wie sowas aussehen könnte? Ich erwarte nicht, dass mir jemand eine komplett neue Rule schreibt, würde mich jedoch über Beispiele freuen.
Hier meine Rule:

Code: Alles auswählen

/*
                                Heizungssteuerung
*/
rule "Heizung"
when 
    Item TargetTemperatureClothingHome changed or 
    Item TargetTemperatureClothingAway changed or 
    Item TargetTemperatureClothingNight changed or 
    Item TargetTemperatureOGBathroomHome changed or 
    Item TargetTemperatureOGBathroomAway changed or 
    Item TargetTemperatureOGBathroomNight changed or 
    Item TargetTemperatureOfficeHome changed or 
    Item TargetTemperatureOfficeAway changed or 
    Item TargetTemperatureOfficeNight changed or 
    Item TargetTemperatureOGSleepingroomHome changed or 
    Item TargetTemperatureOGSleepingroomAway changed or 
    Item TargetTemperatureOGSleepingroomNight changed or 
    Item TargetTemperatureOGLivingroomHome changed or 
    Item TargetTemperatureOGLivingroomAway changed or 
    Item TargetTemperatureOGLivingroomNight changed or 
    Item TargetTemperatureUGFloorHome changed or 
    Item TargetTemperatureUGFloorAway changed or 
    Item TargetTemperatureUGFloorNight changed or 
    Item TargetTemperatureKitchenHome changed or 
    Item TargetTemperatureKitchenAway changed or 
    Item TargetTemperatureKitchenNight changed or 
    Item TargetTemperatureMasterbedroomHome changed or 
    Item TargetTemperatureMasterbedroomAway changed or 
    Item TargetTemperatureMasterbedroomNight changed or 
    Item TargetTemperatureLivingroomHome changed or 
    Item TargetTemperatureLivingroomAway changed or 
    Item TargetTemperatureLivingroomNight changed or 
    Item og_office_window changed or 
    Item og_livingroom_window changed or 
    Item og_sleepingroom_window changed or 
    Item gMasterbedroomWindow changed or 
    Item gLivingroomWindow changed or 
    Item gKitchenWindow changed or 
    Item HVAC_Mode changed or 
    Item Away_Long changed or 
    Item HVAC_Night_Mode changed 
then 
    val tCH = TargetTemperatureClothingHome.state as Number 
    val tCA = TargetTemperatureClothingAway.state as Number 
    val tCN = TargetTemperatureClothingNight.state as Number 
    val tOBH = TargetTemperatureOGBathroomHome.state as Number 
    val tOBA = TargetTemperatureOGBathroomAway.state as Number 
    val tOBN = TargetTemperatureOGBathroomNight.state as Number 
    val tOH = TargetTemperatureOfficeHome.state as Number 
    val tOA = TargetTemperatureOfficeAway.state as Number
    val tON = TargetTemperatureOfficeNight.state as Number 
    val tOSH = TargetTemperatureOGSleepingroomHome.state as Number 
    val tOSA = TargetTemperatureOGSleepingroomAway.state as Number 
    val tOSN = TargetTemperatureOGSleepingroomNight.state as Number 
    val tOLH = TargetTemperatureOGLivingroomHome.state as Number 
    val tOLA = TargetTemperatureOGLivingroomAway.state as Number 
    val tOLN = TargetTemperatureOGLivingroomNight.state as Number 
    val tUFH = TargetTemperatureUGFloorHome.state as Number 
    val tUFA = TargetTemperatureUGFloorAway.state as Number 
    val tUFN = TargetTemperatureUGFloorNight.state as Number 
    val tKH = TargetTemperatureKitchenHome.state as Number 
    val tKA = TargetTemperatureKitchenAway.state as Number 
    val tKN = TargetTemperatureKitchenNight.state as Number 
    val tMH = TargetTemperatureMasterbedroomHome.state as Number 
    val tMA = TargetTemperatureMasterbedroomAway.state as Number 
    val tMN = TargetTemperatureMasterbedroomNight.state as Number 
    val tLH = TargetTemperatureLivingroomHome.state as Number 
    val tLA = TargetTemperatureLivingroomAway.state as Number 
    val tLN = TargetTemperatureLivingroomNight.state as Number 

    if(HVAC_Mode.state == ON) {
        if(hvac_clothing.state == ON) {
            if(Away_Long.state == OFF){
                if(HVAC_Night_Mode.state == OFF) {
                    clothingTargetTemperature.sendCommand(tCH)
                } else {
                    clothingTargetTemperature.sendCommand(tCN)
                }
            } else {
                clothingTargetTemperature.sendCommand(tCA)
            }
            clothingHvacMode.sendCommand("HEAT")
            clothingOperationMode.sendCommand("MANUAL")
        } else {
            clothingHvacMode.sendCommand("OFF")
        }

        if(hvac_ogbathroom.state == ON) {
            if(Away_Long.state == OFF) {
                if(HVAC_Night_Mode.state == OFF){
                    ogbathroomTargetTemperature.sendCommand(tOBH)
                } else {
                    ogbathroomTargetTemperature.sendCommand(tOBN)
                }
            } else {
                ogbathroomTargetTemperature.sendCommand(tOBA)
            }
            ogbathroomHvacMode.sendCommand("HEAT")
            ogbathroomOperationMode.sendCommand("MANUAL")
        } else {
            ogbathroomHvacMode.sendCommand("OFF")
        }

        if(hvac_office.state == ON) {
            if(Away_Long.state == OFF) {
                if(og_office_window.state == CLOSED) {
                    if(HVAC_Night_Mode.state == OFF){
                        officeTargetTemperature.sendCommand(tOH)
                    } else {
                        officeTargetTemperature.sendCommand(tON)
                    }
                } else {
                    officeHvacMode.sendCommand("OFF")
                }
            } else {
                officeTargetTemperature.sendCommand(tOA)
            }
        } else {
            officeHvacMode.sendCommand("OFF")
        }

        if(hvac_ogsleepingroom.state == ON) {
            if(Away_Long.state == OFF) {
                if(og_sleepingroom_window.state == CLOSED) {
                    if(HVAC_Night_Mode.state == OFF) {
                        ogsleepingroomTargetTemperature.sendCommand(tOSH)
                    } else {
                        ogsleepingroomTargetTemperature.sendCommand(tOSN)
                    }
                } else {
                    ogsleepingroomHvacMode.sendCommand("OFF")
                }
            } else {
                ogsleepingroomTargetTemperature.sendCommand(tOSA)
            }
        } else {
            ogsleepingroomHvacMode.sendCommand("OFF")
        }

        if(hvac_oglivingroom.state == ON) {
            if(Away_Long.state == OFF){
                if(og_livingroom_window.state == CLOSED) {
                    if(HVAC_Night_Mode.state == OFF) {
                        oglivingroomTargetTemperature.sendCommand(tOLH)
                    } else {
                        oglivingroomTargetTemperature.sendCommand(tOLN)
                    }
                } else {
                    oglivingroomHvacMode.sendCommand("OFF")
                }
            } else {
                oglivingroomTargetTemperature.sendCommand(tOLA)
            }
        } else {
            oglivingroomHvacMode.sendCommand("OFF")
        }

        if(hvac_ugfloor.state == ON) {
            if(Away_Long.state == OFF) {
                if(HVAC_Night_Mode.state == OFF) {
                    ugfloorTargetTemperature.sendCommand(tUFH)
                } else {
                    ugfloorTargetTemperature.sendCommand(tUFN)
                }
            } else {
                ugfloorTargetTemperature.sendCommand(tUFA)
            }
        } else {
            ugfloorHvacMode.sendCommand("OFF")
        }

        if(hvac_kitchen.state == ON) {
            if(Away_Long.state == OFF) {
                if(gKitchenWindow.state == CLOSED) {
                    if(HVAC_Night_Mode.state == OFF) {
                        kitchenTargetTemperature.sendCommand(tKH)
                    } else {
                        kitchenTargetTemperature.sendCommand(tKN)
                    }
                } else {
                    kitchenHvacMode.sendCommand("OFF")
                }
            } else {
                kitchenTargetTemperature.sendCommand(tKA)
            }
        } else {
            kitchenHvacMode.sendCommand("OFF")
        }

        if(hvac_masterbedroom.state == ON) {
            if(Away_Long.state == OFF) {
                if(gMasterbedroomWindow.state == CLOSED) {
                    if(HVAC_Night_Mode.state == OFF) {
                        masterbedroomTargetTemperature.sendCommand(tMH)
                    } else {
                        masterbedroomTargetTemperature.sendCommand(tMN)
                    }
                } else {
                    masterbedroomHvacMode.sendCommand("OFF")
                }
            } else {
                masterbedroomTargetTemperature.sendCommand(tMA)
            }
        } else {
            masterbedroomHvacMode.sendCommand("OFF")
        }

        if(hvac_livingroom.state == ON) {
            if(Away_Long.state == OFF) {
                if(gLivingroomWindow.state == CLOSED) {
                    if(HVAC_Night_Mode.state == OFF) {
                        livingroomTargetTemperature.sendCommand(tLH)
                    } else {
                        livingroomTargetTemperature.sendCommand(tLN)
                    }
                } else {
                    livingroomHvacMode.sendCommand("OFF")
                }
            } else {
                livingroomTargetTemperature.sendCommand(tLA)
            }
        } else {
            livingroomHvacMode.sendCommand("OFF")
        }
    }
end 

rule "Ein/aus"
when 
    Item HVAC_Mode changed 
then 
    if(HVAC_Mode.state == ON) {
        hvac_clothing.postUpdate(ON)
        hvac_ogbathroom.postUpdate(ON)
        hvac_office.postUpdate(ON)
        hvac_ogsleepingroom.postUpdate(ON)
        hvac_oglivingroom.postUpdate(ON)
        hvac_ugfloor.postUpdate(ON)
        hvac_kitchen.postUpdate(ON)
        hvac_masterbedroom.postUpdate(ON)
        hvac_livingroom.postUpdate(ON)
    } else {
        hvac_clothing.postUpdate(OFF)
        hvac_ogbathroom.postUpdate(OFF)
        hvac_office.postUpdate(OFF)
        hvac_ogsleepingroom.postUpdate(OFF)
        hvac_oglivingroom.postUpdate(OFF)
        hvac_ugfloor.postUpdate(OFF)
        hvac_kitchen.postUpdate(OFF)
        hvac_masterbedroom.postUpdate(OFF)
        hvac_livingroom.postUpdate(OFF)
    }

end

end 
Über hilfreiche Tipps und Ratschläge würde ich mich sehr freuen!
MfG Elias

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

Re: Heizungsrule und Verbesserungsmöglichkeiten

Beitrag von udo1toni »

Der erste Schritt der Optimierung beginnt schon bei den Items. Die Namen der Items wurden vermutlich von openHAB vorgegeben, das heißt aber nicht, dass sie in dieser Form sinnvoll sind.
Insbesondere da Du mehrere Items gleichen Typs hast, bietet es sich an, diese in einer Gruppe zusammenzufassen. Man sollte aber, um die Vorteile der Gruppe auch voll ausnutzen zu können, unbedingt darauf achten, die Namen so zu gestalten, dass der Name gut zu splitten ist, und zwar in die einzelnen Hierarchien. Das heißt: statt TargetTemperatureClothingHome nennst Du das Item TargetTemperature_Clothing_Home. Schon kann man mit einer Methode split("_") den Namen des Items aufspalten. Nun ist es wichtig, dass die Itemnamen so gestaltet sind, dass sie zueinander in eindeutige Beziehung gebracht werden können. Es ist zum Beispiel nicht gut, einmal og_livingroom_window zu schreiben, an anderer Stelle aber TargetTemperature_OGLivingroom_Home, denn OGLivingroom und og_livingroom beziehen sich mit hoher Wahrscheinlichkeit auf den gleichen Raum.

Weiterhin sehe ich keinen Sinn darin, bei Änderungen, ein Ventil betreffend, immer alle Ventile zu steuern, egal was ist.

Durch die Verwendung einer Gruppe gHVAC_Mode, in der alle Items aus der zweiten Rule enthalten sind, schrumpft die Rule auf das hier:

Code: Alles auswählen

rule "HVAC ein/aus"
when 
    Item HVAC_Mode changed 
then 
    gHVAC_Mode.members.forEach[i|
        i.postUpdate(newState.toString)
    ]
end
Man könnte sogar einfach das Item gHVAC_Mode per sendCommand switchen und die einzelnen Items würden dem Befehl ohne Rule folgen. Das ist aber nicht sauber, denn ein postUpdate ist nun mal was anderes als ein sendCommand.

Der Befehl (ja, es ist nur einer) ist folgender: Nimm die Gruppe gHVAC_Mode, führe für alle Member dieser Gruppe, den befehl postUpdate aus, mit dem aktuellen Wert des Items, welches die Rule getriggert hat (das ist in dem Fall HVAC_Mode). Das toString sorgt dafür, dass der Status auch sicher verwendet werden kann

Du siehst aber schon an der zweiten Rule, wie viel Code Du sparen kannst.

Wenn ich die Rule quasi unverändert schreibe (es werden also immer alle Geräte gesteuert, auch wenn das gar nicht notwendig ist), sieht eine optimierte Version so aus:

Code: Alles auswählen

rule "Heizung"
when 
    Member of gTT changed or                               // Target Temperatur geändert
    Member of gWindow changed or                           // Fensterkontakt geändert
    Member of gHVAC changed                                // Betriebsart geändert
then 
    if(trigeringItem.name.contains("Current"))                                                                          // Rule hat sich selbst getriggert, also
        return;                                                                                                         // Rule abbrechen

    if(HVAC_Mode.state != ON) {                                                                                         // Steuerung aus
        return;                                                                                                         // und Rule abbrechen
    }
    var                             strMode = "Home"                                                                    // Komfort
    if(Away_Long.state == ON)       strMode = "Away"                                                                    // Standby
    else if(HVAC_Night.state == ON) strMode = "Night"                                                                   // Nacht

    gHVAC_Mode.members.forEach[h|                                                                                       // für alle Räume
        val strRoom = h.name.split("_").get(1)                                                                          // Name des aktuellen Raums
        val swHVAC  = gHVAC_Modes.members.filter[i|i.name.contains(strRoom) && i.name.contains("Hvac")].head            // zugehöriges HVAC_Mode Item
        val swOP    = gHVAC_Modes.members.filter[i|i.name.contains(strRoom) && i.name.contains("Operation")].head       // zugehörigen OP_Mode Item
        val iTT     = gTT.members.filter[i|i.name.contains(strRoom) && i.name.contains(strMode)].head.state as Number   // Zieltemperatur laut Betriebsart
        val iTTcur  = gTT.members.filter[i|i.name.contains(strRoom) && i.name.contains("Current")].head                 // Item zum Setzen der Zieltemperatur

        if(h.state != ON)                                                                                               // Falls HVAC-Status nicht ON
            swHVAC.sendCommand("OFF")                                                                                   // Ausschalten
        else {                                                                                                          // Falls HVAC-Status ON
            if(gWindow.members.filter[i|i.name.contains(strRoom)].size > 0)                                             // Falls Fensterkontakt vorhanden
                if(gWindow.members.filter[i|i.name.contains(strRoom)].head.state == OPEN) {                             // Falls Fenster offen
                    swHVAC.sendCommand("OFF")                                                                           // Ausschalten
                    return;                                                                                             // und nächstes Element "h" der Liste
                }
            iTTcur.sendCommand(iTT)                                                                                     // sende Temperatur
            if(swHVAC !== null) swHVAC.sendCommand("HEAT")                                                              // Item für HVAC vorhanden, sende "HEAT"
            if(swOP   !== null)   swOP.sendCommand("MANUAL")                                                            // falls OP_Mode vorhanden, sende "MANUAL!
        }
    ]
end
Voraussetzung dafür ist, dass die Itemanmen passend gesetzt sind, so:

Code: Alles auswählen

Group gTT                                                     // <- alle Target Temperaturen (sowohl Setup als auch aktuell
Group gHVAC_Modes                                             // <- alle Items zum Steuern von HvacMode und OperationMode
Group gWindow                                                 // <- alle Fensterkontakte (bzw. die entsprechenden Gruppen z.B. bei Livingroom)
Group gHVAC                                                   // <- Hauptschalter

Number TargetTemperature_OGLivingroom_Home    (gTT)           // <- Target Temp. zuhause
Number TargetTemperature_OGLivingroom_Away    (gTT)           // <- Target Temp. weg 
Number TargetTemperature_OGLivingroom_Night   (gTT)           // <- Target Temp. Nacht
Number TargetTemperature_OGLivingroom_Current (gTT)           // <- aktuelle Target Temp.
String Hvac_OGLivingroom_Mode                 (gHVAC_Modes)   // <- HVAC Modus
String Operation_OGLivingroom_Mode            (gHVAC_Modes)   // <- Betriebsart
Group:Contact Window_OGLivingroom_Mode        (gWindow)       // <- Fenster

Switch HVAC_Mode                              (gHVAC)         // <- 
Switch Away_Long                              (gHVAC)         // <- 
Switch HVAC_Night                             (gHVAC)         // <- 
Als Beispiel hier nur der Block für OGLivingroom (und das müsste dann auch der Namensteil im hvac-Item der zweiten Rule sein)

Wichtig ist, dass der Raumname im Namen der Items für alle zugehörigen Items identisch ist und dieser Name auch für alle Räume eindeutig ist. Da ich die Funktion .contains() verwende, dürfen auch keine Namensteile identisch sein (also z.B. Livingroom und OGLivingroom ist nicht erlaubt, da der erste Name vollständig im zweiten Namen enthalten ist.

Die Rule triggert, wenn sich Member der Gruppen ändern. Vorteil: drei Zeilen statt... 40?
Die Rule bricht ab, falls das Item den Namensteil Current enthält, denn dann hat die Rule selbst die Änderung herbeigeführt. (Man könnte auch eine separate Gruppe für die Items machen, aber so geht es halt auch)
Falls HVAC_Mode auf OFF steht (bzw. eben nicht auf ON), bricht die Rule vorzeitig ab. Das so zu machen, spart eine Ebene gegenüber Deiner Rule.

Nun wird der Modus ermittelt. Da es drei verschiedene mögliche Modi gibt, ist ein Switch dafür nicht gut geeignet. Hier mache ich mir aber zunutze, dass ich ohnehin einen namentlichen Verweis auf das jeweilige Item benötige, eben Home, Away oder Night.

Jetzt kommt der Teil, der sich bei Dir über knapp 150 Zeilen zieht, nur sind es bei mir nur 20 Zeilen :D.
Wieder kommen die Gruppen zum Einsatz, diesmal wird die Gruppe gHVAC_Mode durchlaufen, für jeden Member dieser Gruppe wird identischer Code ausgeführt.
Zunächst wird der zweite Teil des Namens ermittelt (das ist der Raum).
Anschließend werden die Items für den HVAC-Modus und den OperationMode herausgesucht, in denen der Raumname identisch ist. Beide Items sind in der Gruppe gHVAC_Modes zusammengefasst, entsprechend ist es einfach, die Items anhand der Namen aus dieser Gruppe zu extrahieren.
Dann wird noch die Solltemperatur ermittelt (anhand der Betriebsart, die wir weiter oben errechnet haben) und das zugehörige Item, welches mit der Solltemperatur gefüllt werden muss.

Nun folgt noch die Logik, welche bei Betriebsart != ON (wir erinnern uns... das Item, welches momentan Namensgeber für den Raum ist) direkt ein OFF sendet. Falls HVAC ON ist, muss noch geprüft werden, ob es Fensterkontakte zu berücksichtigen gilt (dann müsste es in der Liste der Fensterkontakte mindestens ein Item geben, welches den Raumnamen enthält; size > 0)
Ist das der Fall, muss geprüft werden, ob der Zustand OPEN ist. Ist das der Fall, so muss OFF gesendet werden und mit dem nächsten Item weitergemacht werden. Das return; springt in diesem Fall auf das nächste Element in der Liste gHVAC_Mode, also zum nächsten Raum.
War aber kein Fenster offen bzw. vielleicht kein Fensterkontakt vorhanden, wird die ermittelte Temperatur ans Item gesendet. Falls die beiden Items für Betriebsart und OperationMode existieren werden auch an sie die entsprechenden Befehle gesendet.

Allein durch die Iteration über die Räume spare ich ca. 90% Code ein. Und da die Solltemperatur nur aus einem Item gelesen werden muss, statt aus dreien, sollte der Code sogar etwas schneller sein, obwohl er, wie gesagt, nicht optimal ist.

Optimierungspotential bestünde darin, noch vor dem Einsprung in die forEach Schleife zu prüfen, ob die Rule durch ein Item getriggert wurde, welches einen Raumnamen enthält, denn dann müssen nur die Items dieses Raums angefasst werden.
Leider gibt es aber praktisch keine Möglichkeit, die dadurch entstehende Code-Doppelung zu verhindern. Man könnte höchstens vor dem jeweiligen sendCommand prüfen, ob das Item schon den passenden Zustand hat, um unnötige sendCommands zu unterdrücken.

Nun hast Du gar nicht nach einer kompletten Lösung verlangt, aber ich kann Dich beruhigen: vermutlich funktioniert der Code nicht auf Anhieb ;) es besteht also noch genug Bastelpotential. Die Optimierungen lassen sich aber am besten an einem zumindest in der Theorie lauffähigen Code erklären. :)
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

MrCrashy
Beiträge: 113
Registriert: 2. Jan 2021 09:53
Answers: 0

Re: Heizungsrule und Verbesserungsmöglichkeiten

Beitrag von MrCrashy »

Vielen Dank!
Da ich mir das schreiben von Codes bzw. die Sprache von Openhab selber beibringe und auch keine Beruflichen oder Privaten Erfahrungen mit dem Schreiben von Programmen o.ä. habe, war mir gar nicht klar, wie viele Möglichkeiten es gibt, Gruppen zu verwenden.
Das macht natürlich alles einfacher, wenn man mal durchgeblickt hat.
Ich habe die Rule fast verstanden. Natürlich nur, dank deiner Erklärung. Bei einer Sache habe ich aber noch Fragezeichen über dem Kopf:

Du verwendet ganz am Ende "swHVAC" und "swOP". Diese sind ja meine String-Items des Heizunugsmodus vom Ventil. Also müsste ich diese dann ja mit meinem Channel mit dem Ventil verlinken, sehe ich das richtig?

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

Re: Heizungsrule und Verbesserungsmöglichkeiten

Beitrag von udo1toni »

swHVAC und swOP sind zwei lokale Objekte innerhalb der Rule. Sie werden weiter oben definiert:

Code: Alles auswählen

val swHVAC  = gHVAC_Modes.members.filter[i|i.name.contains(strRoom) && i.name.contains("Hvac")].head
val swOP    = gHVAC_Modes.members.filter[i|i.name.contains(strRoom) && i.name.contains("Operation")].headItem
gHVAC_Modes ist ein Group Item. Alle Items aller Räume für die Steuerung von OperationMode und HVACMode sind in dieser Gruppe zusammengefasst.
.members ist eine Liste aller Member in dieser Gruppe.
.filter[] sucht nach bestimmten Kriterien eine Anzahl Items aus der Liste heraus. Das Ergebnis des Filters ist naturgemäß wieder eine Liste.
.head ist der erste Eintrag der Liste. Da es sich bei den einzelnen Elementen um Items handelt, ist auch .head ein Item.
Die beiden Objekte sind also Items.

Innerhalb des Filterausdrucks:
i| bedeutet, dass i als Platzhalter verwendet wird. Der Filter durchläuft also die Liste und substituiert bei jedem Durchgang i mit dem gerade aktuellen Member der Liste. Daraufhin wird der Code durchlaufen, der darüber entscheidet, ob am Ausgang das aktuelle Element noch Teil der Liste ist oder nicht.
i.name ist entsprechend der komplette Name des aktuellen Items, und zwar als String.

Ich könnte z.B. eins Deiner Items nehmen, z.B. TargetTemperatureMasterbedroomHome und dort mit .name ermitteln, dass der Name des Items TargetTemperatureMasterbedroomHome lautet. Das ist so natürlich etwas witzlos, weil ich den Namen ja nun schon hingeschrieben habe, im Zusammenhang mit Listen wird es aber interessant :)

.contains() ist eine Boolsche Funktion, die den Inhalt eines Strings mit einem zweiten String vergleicht. Kommt der zweite String im ersten vor, ist das Ergebnis true, ansonsten false.
Der erste Vergleich prüft auf den Raumnamen, der zweite Vergleich auf den Untertyp, beide Ergebnisse werden mit && (logisches UND) verknüpft. Kommt als Ergebnis true heraus, landet das Item in der Liste, ansonsten fliegt es raus.

Wenn Du nun swHVAC.sendCommand() ausführst, dann wirkt das .sendCommand() genau auf das Item, welches von dem Objekt gerade verkörpert wird.
Natürlich muss dieses Item mit einem Channel verlinkt sein, damit der Befehl beim Channel ankommt, das hat aber nichts mit der Rule zu tun, das ist bei Deiner Rule ja genauso, z.B. käme das Item ogbathroomHvacMode (anders benannt) zum Zuge, dann muss eben ogbathroomHvacMode mit einem Channel verknüpft sein, damit die Steuerung tatsächlich etwas bewirkt.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

MrCrashy
Beiträge: 113
Registriert: 2. Jan 2021 09:53
Answers: 0

Re: Heizungsrule und Verbesserungsmöglichkeiten

Beitrag von MrCrashy »

Du sagtest ja schon, dass dort auf jeden Fall gebastelt werden muss. Trotzdem habe ich dort einen Fehler, welchen ich einfach nicht weg bekomme. Ignorieren kann ich ihn aber nicht, da dieser dafür Sorgt, dass die Rule nicht ausgeführt wird.

Es handelt sich um diesen Fehler:
Cannot refer to the non-final variable strMode inside a lambda expression

In dieser Zeile

Code: Alles auswählen

gTT.members.filter[i|i.name.contains(strRoom) && i.name.contains(strMode)].head.state as Number

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

Re: Heizungsrule und Verbesserungsmöglichkeiten

Beitrag von udo1toni »

Das ist interessant, denn eigentlich sollte die Variable an dieser Stelle zugänglich sein. Solange die Rule über eine Textdatei erstellt ist, reicht es, die Variablendefinition an den Kopf der Datei zu schreiben. Innerhalb der Rule muss die Zuweisung bestehen bleiben, aber das var vorne dran muss weg, da die Variable ja nun global definiert ist (also, ein Stück weit...)

Code: Alles auswählen

// globale Variablen müssen vor der ersten Rule innerhalb der Datei definiert werden!
var strMode = "dummy"

...

rule "Heizung"
when 
    Member of gTT changed or                               // Target Temperatur geändert
    Member of gWindow changed or                           // Fensterkontakt geändert
    Member of gHVAC changed                                // Betriebsart geändert
then 
    if(trigeringItem.name.contains("Current"))                                                                          // Rule hat sich selbst getriggert, also
        return;                                                                                                         // Rule abbrechen

    if(HVAC_Mode.state != ON) {                                                                                         // Steuerung aus
        return;                                                                                                         // und Rule abbrechen
    }
    strMode = "Home"                                                                                                    // Komfort
    if(Away_Long.state == ON)       strMode = "Away"                                                                    // Standby
    else if(HVAC_Night.state == ON) strMode = "Night"                                                                   // Nacht

...

Eine Überlegung an dieser Stelle wäre aber auch, statt dieser Variablen ein String Item zu erstellen. Das String Item wird dann genauso upgedatet, am einfachsten durch die lokal erstellte Variabke:

Code: Alles auswählen

rule "Heizung"
when 
    Member of gTT changed or                               // Target Temperatur geändert
    Member of gWindow changed or                           // Fensterkontakt geändert
    Member of gHVAC changed                                // Betriebsart geändert
then 
    if(trigeringItem.name.contains("Current"))                                                                          // Rule hat sich selbst getriggert, also
        return;                                                                                                         // Rule abbrechen

    if(HVAC_Mode.state != ON) {                                                                                         // Steuerung aus
        return;                                                                                                         // und Rule abbrechen
    }
    var                             strMode = "Home"                                                                                                  // Komfort
    if(Away_Long.state == ON)       strMode = "Away"                                                                    // Standby
    else if(HVAC_Night.state == ON) strMode = "Night"                                                                   // Nacht

    HVAC_Mode_String.postUpdate(strMode)
    
    ...
    
    val iTT     = gTT.members.filter[i|i.name.contains(strRoom) && i.name.contains(HVAC_Mode_String.state.toString)].head.state as Number

...

openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

MrCrashy
Beiträge: 113
Registriert: 2. Jan 2021 09:53
Answers: 0

Re: Heizungsrule und Verbesserungsmöglichkeiten

Beitrag von MrCrashy »

Also ich muss sagen, dass es bei mir gar nicht funktioniert. Sobald ich eine Temperatur ändere, schaltet sich die Heizung ab oder es werden Fehler gesetzt. Nun ist es so, dass ich mich mit diesen Gruppenfunktionen bzw. den Filtern der Gruppe nicht gut genug auskenne, um den Fehler zu fixen.

MrCrashy
Beiträge: 113
Registriert: 2. Jan 2021 09:53
Answers: 0

Re: Heizungsrule und Verbesserungsmöglichkeiten

Beitrag von MrCrashy »

Ich vermute, dass es an dieser Stelle liegt:

Code: Alles auswählen

if(h.state != ON)                                                                                               // Falls HVAC-Status nicht ON
            swHVAC.sendCommand("OFF")  
Denn es wird ein Status abgefragt, den es bei den Modes nicht gibt. Es gibt "HEAT", "SHEDULE", "TIMER" und "OFF". Außerdem stehen die Gruppen alle auf "NULL" bzw "UNDEF" und auch das scheint die Rule abzubrechen

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

Re: Heizungsrule und Verbesserungsmöglichkeiten

Beitrag von udo1toni »

Nein, da ist was durcheinander gekommen. Das mag aber an meine Posting liegen :)

Du hast ja eine ganze Latte an Items pro HVAC (Codeansicht nur wegen Formatierung),

Code: Alles auswählen

Funktion           Gruppe      Art
Vorgabe   Zuhause  gTT         Zahl
Vorgabe   Abwesend gTT         Zahl
Vorgabe   Nacht    gTT         Zahl
Aktuelles Soll     gTT         Zahl
Modus              gHVAC_Modes Text
Betriebsart        gHVAC_Modes Text
Fenster            gWindow     Kontakt
Automatik          gHVAC_Mode  Schalter
Und h bezieht sich auf diese Zeile:

Code: Alles auswählen

    gHVAC_Mode.members.forEach[h|
es handelt sich also um einen der Schalter, die pro HVAC Modul darüber entscheiden, ob der HVAC von der Automatik gesteuert werden soll oder nicht.

Welchen Zustand die Gruppe hat, ist irrelevant, denn der Zustand der Gruppe wird zu keinem Zeitpunkt betrachtet. Es geht immer und ausschließlich um Items, die in einer Gruppe zusammengefast sind. Betrachtet wird immer das einzelne Item, niemals die Gruppe als Item.
openHAB4.1.2 stable in einem Debian-Container (bookworm) (Proxmox 8.1.5, LXC), mit openHABian eingerichtet

MrCrashy
Beiträge: 113
Registriert: 2. Jan 2021 09:53
Answers: 0

Re: Heizungsrule und Verbesserungsmöglichkeiten

Beitrag von MrCrashy »

Ok, nun ist ein Fehler schonmal raus.
Trotzdem schmeißt er mir noch diesen Fehler in den Logs:
2023-01-02 20:10:56.094 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'lighting-1' failed: cannot invoke method public abstract java.lang.String org.openhab.core.items.Item.getName() on null in heating

Kann es sein, dass das mit der Abfrage vom triggeringitem.name zusammen hängt?

Antworten