Seite 1 von 2

Heizungssteuerung mit OpenHAB 4

Verfasst: 31. Mai 2024 09:21
von Automateur0815
Hallo zusammen,
ich habe es lange vor mir hergeschoben und nun meine OpenHAB 2 Einrichtung auf OpenHAB 4 aktualisiert. Nun bin ich dabei meine ganzen Regeln und Automatismen wieder ans Laufen zu bekommen.
Bei der Heizungssteuerung tue ich mich aber schwer. Die Regeln funktionieren in diesem Fall nicht mehr. Ich habe beispielsweise folgende Regel in OpenHAB 2 gebaut gehabt:

Code: Alles auswählen

rule "Heizung Wohn- und Esszimmer"
when
	Item Temperature_GEG_Living changed 	
then
	var Number Ist_EG_Living
	var Number Soll_EG_Living
	Ist_EG_Living = Temperature_GEG_Living.state as DecimalType
	
	if(Window_GEG_Living_klein.state===CLOSED && Window_GEG_Living_Terrassentuer.state===CLOSED && 	Window_GEG_Living_Terrassentuerfluegel.state===CLOSED && Window_GEG_Living_Balkontuer.state===CLOSED && Window_GEG_Living_links.state===CLOSED && Window_GEG_Living_rechts.state===CLOSED) {
		if(Shutter_ALLG_Heizung_Sommerbetrieb.state===OFF) {
			if(Urlaubsreise.state===OFF) {
				if(Heizschalter_Living.state===ON) {	
    			Soll_EG_Living = Sollwerte_Living.state as DecimalType
					if (Ist_EG_Living < Soll_EG_Living-0.5) {sendCommand(Heating_GEG_Living, ON)	} 
					else if (Ist_EG_Living > Soll_EG_Living) {sendCommand(Heating_GEG_Living, OFF) }
   				}
				else if (Heizschalter_Living.state===OFF) { sendCommand(Heating_GEG_Living, OFF) }   				
   				}
   				else if(Urlaubsreise.state===ON){
   				Soll_EG_Living = Sollwerte_Living_Urlaub.state as DecimalType
    				if (Ist_EG_Living < Soll_EG_Living-0.5) {	sendCommand(Heating_GEG_Living, ON)	} 
				else if (Ist_EG_Living > Soll_EG_Living) { sendCommand(Heating_GEG_Living, OFF) }
   				}
   				}
   				else if(Shutter_ALLG_Heizung_Sommerbetrieb.state===ON) { sendCommand(Heating_GEG_Living, OFF) }
   				}
   	else if(Window_GEG_Living_klein.state===OPEN || Window_GEG_Living_Terrassentuer.state===OPEN || Window_GEG_Living_Terrassentuerfluegel.state===OPEN || Window_GEG_Living_Balkontuer.state===OPEN || Window_GEG_Living_links.state===OPEN || Window_GEG_Living_rechts.state===OPEN) { sendCommand(Heating_GEG_Living, OFF) }
   		
end
Kann mir jemand weiterhelfen und mir sagen wie ich die Regeln umschreiben muss, dass sie auch in OpenHAB4 funktionieren?

Gruß Jan

Re: Heizungssteuerung mit OpenHAB 4

Verfasst: 31. Mai 2024 12:57
von udo1toni
Es gibt ein paar grundsätzliche Fehler in Deiner Rule, ein paar unschöne Details und ein paar Stellen, an denen der Code eventuell inkompatibel sein kann. Der Reihe nach... ;)
Grundsätzliche Fehler: Der Vergleich === ist hier verkehrt. Eigentlich dürfte das noch nie korrekt funktioniert haben.
Bedeutung:
  • = -> Wertzuordnung. a = b -> a nimmt den Wert von b an (soweit möglich...)
  • == -> Vergleich auf Gleichheit. a == b -> wahr, falls der Wert in a dem Wert in b entspricht, z.B. a = 3 -> a == 3 liefert wahr
  • === -> Vergleich auf Identität. a === b -> wahr, wenn a mit b identisch ist.
    Dieser Vergleich ist gewöhnlich nur mit null sinnvoll (bitte nicht mit NULL verwechseln) a === null -> wahr, falls der Variablen a noch kein Wert zugewiesen wurde, denn dann zeigt der Zeiger der Variablen a auf null (ein definierter Platz im Speicher, extra für diesen Zweck)
Unschöne Details:
Du triggerst die Rule mit changed, nutzt aber anschließend Temperature_GEG_Living.state als Wert. Das mag zu über 99 % gut gehen, kann aber auch schief gehen. Besser ist es, die implizite Variable newState zu verwenden.
Action sendCommand(Itemname, Wert): kann man machen, ist aber nicht gut. Besser: Methode Itemname.sendCommand(Wert). Der Unterschied ist hier (in dieser Rule) nicht wichtig, an anderen Stellen führt die Action aber zu Problemen, welche mit der Methode nicht auftreten. Allgemein wird die Methode empfohlen.
Casting ohne vorherige Prüfung (as DecimalType): Kann man machen, kann aber schief gehen. Fehlerquelle besser abfangen.
Diverse Contact Items: Hier böte sich eine Zusammenfassung in einer Gruppe an. Mutmaßlich handelt es sich um alle Fenster eines Raumes, man könnte also ein GroupItem definieren:

Code: Alles auswählen

Group:Contact:OR(OPEN,CLOSED) gWindow_GEG_Living
und man weist jedes der Contact Items (des Wohnzimmers) dieser Gruppe zu. Anschließend muss man nur dieses eine Item auf den Zustand OPEN/CLOSED prüfen.
Anordnung der Befehle ungünstig, dadurch viel unnötiger oder gar gedoppelter Code.
Die Einrückungen sind teilweise "falsch" (siehe unten)

Mögliche Inkompatibilität:
Wie sind die Items definiert? Insbesondere interessant bei den Items, in denen die Temperaturwerte gespeichert sind. Falls Du hier inzwischen QuantityType Items verwendest (Number:Temperature), ist das schon mal der wichtigste Punkt. Wenn das Item den Status 21.5 liefert, ist das etwas anderes als der Status 21.5 °C. Da lohnt ein kurzer Blick ins log (Suche nach einem changed Ereignis der Items).

So sähe Deine Rule "besser" aus:

Code: Alles auswählen

rule "Heizung Wohn- und Esszimmer"
when
    Item Temperature_GEG_Living changed
then
    if(!(newState instanceof Number)) {                                                              // Sensorwert prüfen
        logWarn("heating","Temperature_GEG_Living liefert keine gültige Zahl!")
        return;                                                                                      // Ausführung abbrechen
    }
    var Number Ist_EG_Living = (newState as Number).floatValue                                       // Wert als Zahl ohne Einheit zuweisen
    if(!(Sollwerte_Living.state instanceof Number)) {                                                // Sollwert prüfen
        logWarn("heating","Sollwerte_Living liefert keine gültige Zahl!")
        return;                                                                                      // Ausführung abbrechen
    }
    var Number Soll_EG_Living = (Sollwerte_Living.state as Number).floatValue                        // Default Verhalten kein Urlaub

    if(Urlaubsreise.state == ON) {                                                                   // Falls Urlaub aktiv Sollwert ändern
        if(Sollwerte_Living_Urlaub.state instanceof Number)                                          // Aber nur, wen ndas auch geht...
            Soll_EG_Living = (Sollwerte_Living_Urlaub.state as Number).floatValue
        else
            logWarn("heating","Urlaub aktiv, aber Sollwert ungültig! Nutze normalen Sollwert.")
    }

    if(gWindow_GEG_Living.state == OPEN) {                                                           // mindestens einer der Kontakte meldet OPEN
        Heating_GEG_Living.sendCommand(OFF)
        return;
    }
    if(Shutter_ALLG_Heizung_Sommerbetrieb.state == ON) {                                             // Heizung ist aus
        Heating_GEG_Living.sendCommand(OFF)
        return;
    }

    if((Urlaubsreise.state == OFF && Heizschalter_Living.state == ON) || Urlaubsreise.state == ON) { // (Anwesend und Raumheizung an) oder Abwesend
        if(Ist_EG_Living < Soll_EG_Living - 0.5)                                                     // Soll um mindestens 0.5 unterschritten
            Heating_GEG_Living.sendCommand(ON) 
        else if(Ist_EG_Living > Soll_EG_Living)                                                      // Soll überschritten
            Heating_GEG_Living.sendCommand(OFF) 
    } else                                                                                           // Anwesend aber Raumheizung aus
        Heating_GEG_Living.sendCommand(OFF) 
end
Die Einrückungen sind optional, sie haben keinerlei Einfluss auf die Ausführung des Codes, verbessern aber die Lesbarkeit. Wichtig ist aber, die Einrückungen immer konsistent auszuführen, also alle Befehle einer Ebene sollten auch gleich eingerückt sein.
Die Kommentare helfen, (viel) später noch zu verstehen, was der Code machen soll.
Bei erkanntem Fehler kommt keine NullPointerException, sondern eine vernünftige Meldung.

Mit dem verfrühten Abbruch der Ausführung (return;) wird die Tiefe der Schachtelung minimiert -> bessere Lesbarkeit des Codes.

Mutmaßlich hast Du so eine oder so eine ähnliche Rule für jeden Raum. Sinnvoller wäre es, die Rule für alle Räume gemeinsam zu haben. D.h. Du hast nur eine Rule, diese kümmert sich dann um alle Räume, wobei der Code nicht viel länger werden muss, wichtig ist dann aber, dass die Items gut strukturiert sind (scheint schon der Fall zu sein) und alle "gleichartige" Items gruppiert sind. Die Rule triggert dann auf die Änderung eines Gruppenmembers der Temperaturen. Die Rule bestimmt, um welchen Raum es sich handelt (geht ja aus dem Namen des triggernden Items hervor) und sucht sich daraufhin alle Items zusammen, welche im Durchlauf verwendet werden müssen. Das sind im Zweifel nur ein paar Zeilen Code zusätzlich in dieser einen Rule, aber das ist dann auch die einzige Rule (also jedem Menge Ersparnis durch Wegfall der anderen Rules)

Re: Heizungssteuerung mit OpenHAB 4

Verfasst: 1. Jun 2024 21:09
von Automateur0815
Danke für die Infos und das Anpassen der Rule.
Ich habe die betroffenen Items entsprechend angepasst und die entsprechende Gruppe erstellt sowie die angepasste Rule eingefügt. Allerdings scheint das System immer noch ein Problem damit zu haben. Er meldet im openhab.log das die Ausführung der Regel fehlgeschlagen ist:
Script execution of rule with UID 'c213f29b31' failed: ___ rule ___ "Heizung Wohn- und Esszimmer"
when
Item Temperature_GEG_Living changed
then
if(!(newState instanceof Number)) { // Sensorwert prüfen
logWarn("heating","Temperature_GEG_Living liefert keine gültige Zahl!")
return; // Ausführung abbrechen
}
var Number Ist_EG_Living = (newState as Number).floatValue // Wert als Zahl ohne Einheit zuweisen
if(!(Sollwerte_Living.state instanceof Number)) { // Sollwert prüfen
logWarn("heating","Sollwerte_Living liefert keine gültige Zahl!")
return; // Ausführung abbrechen
}
var Number Soll_EG_Living = (Sollwerte_Living.state as Number).floatValue // Default Verhalten kein Urlaub

if(Urlaubsreise.state == ON) { // Falls Urlaub aktiv Sollwert ändern
if(Sollwerte_Living_Urlaub.state instanceof Number) // Aber nur, wen ndas auch geht...
Soll_EG_Living = (Sollwerte_Living_Urlaub.state as Number).floatValue
else
logWarn("heating","Urlaub aktiv, aber Sollwert ungültig! Nutze normalen Sollwert.")
}

if(gWindow_GEG_Living.state == OPEN) { // mindestens einer der Kontakte meldet OPEN
Heating_GEG_Living.sendCommand(OFF)
return;
}
if(Shutter_ALLG_Heizung_Sommerbetrieb.state == ON) { // Heizung ist aus
Heating_GEG_Living.sendCommand(OFF)
return;
}

if((Urlaubsreise.state == OFF && Heizschalter_Living.state == ON) || Urlaubsreise.state == ON) { // (Anwesend und Raumheizung an) oder Abwesend
if(Ist_EG_Living < Soll_EG_Living - 0.5) // Soll um mindestens 0.5 unterschritten
Heating_GEG_Living.sendCommand(ON)
else if(Ist_EG_Living > Soll_EG_Living) // Soll überschritten
Heating_GEG_Living.sendCommand(OFF)
} else // Anwesend aber Raumheizung aus
Heating_GEG_Living.sendCommand(OFF)
end
1. The method or field rule is undefined; line 1, column 0, length 4
2. The method or field when is undefined; line 2, column 35, length 4
3. The method or field changed is undefined; line 3, column 72, length 7
4. The method or field then is undefined; line 4, column 80, length 4
5. The method or field end is undefined; line 39, column 2579, length 3
6. This expression is not allowed in this context, since it doesn't cause any side effects.; line 1, column 5, length 29
7. This expression is not allowed in this context, since it doesn't cause any side effects.; line 3, column 44, length 4
8. This expression is not allowed in this context, since it doesn't cause any side effects.; line 3, column 49, length 22
Da ich mich aber eher der Kategorie Copy/Paste-Programmierer zuordne und nur halbwegs den Programmiercode verstehe aber selbst keinen frei programmieren kann, weiß ich nicht so recht wo er gerade ein Problem hat.

Zur Info: ich habe die Regel auf der Weboberfläche erstellt und die Rule als Inline Script hinzugefügt. Also When Temperature_GEG_Living was updated then execute an inline script und dort dann die komplette Rule eingefügt.

Das Item Temperature_GEG_Living ist vom type: Number:Temperature
Die Items für die Fensterkontakte sind vom type: Contact
Die Items für die Solltemperaturen sind vom type: Number:Temperature

Re: Heizungssteuerung mit OpenHAB 4

Verfasst: 2. Jun 2024 02:54
von udo1toni
Ja, aber nein. Die Rule ist eine DSL Rule und in dieser Form nur über eine Textdatei im Ordner $OPENHAB_CONF/rules/ lauffähig (der Dateiname muss auf .rules enden).
Du kannst die Rule auch über die UI eingeben, dann musst Du allerdings auch verstehen, dass die Rule aus zwei logischen Teilen besteht.
Der zweite Teil ist der auszuführende Code (zwischen den Schlüsselworten then und end) und der erste Teil der Rule setzt den Namen der Rule und gibt alle Trigger an. Im Grunde steht das sogar im Klartext da... rule "sowieso" when Item bla changed then
Wenn Du also über die UI arbeiten möchtest, musst Du den Namen der Rule setzen, den Trigger korrekt wählen (nicht was updated, sondern changed) und als Action execute Script auswählen, wobei wichtig ist, als Scriptsprache die DSL zu selektieren.
Anschließend gibst Du im Codefenster den auszuführenden Code an, also alles zwischen then und end (ohne diese beiden Schlüsselworte).

Re: Heizungssteuerung mit OpenHAB 4

Verfasst: 2. Jun 2024 10:30
von Automateur0815
:roll: Da hätte ich auch selbst drauf kommen müssen. Ist ja logisch dass man die Bedingung nicht zwei mal prüft (einmal um die Rule überhaupt zu triggern und dann nochmal um sie auszuführen).
Ok, er scheint die Rule jetzt auszuführen, bringt mir aber die Meldung das Temperature_GEG_Living keine gültige Zahl liefert.
Ich muss das mit dem Type Number:Temperature doch im Item einstellen, oder muss das irgendwie im Thing des KNX-Aktors hinterlegt werden?

Re: Heizungssteuerung mit OpenHAB 4

Verfasst: 2. Jun 2024 12:33
von udo1toni
Das war ja die Ausgangsfrage, welcher Itemtyp liegt vor. Falls Du kein QuantityType Item verwendest, wird die Rule dennoch funktionieren. Falls Du QuantityType Items verwenden willst, so muss der Channel das allerdings ebenfalls unterstützen. Im Fall von knx muss der DPT dann korrekt angegeben werden (der Sub-Teil des DPT gibt an, um welche Einheit es sich handelt, z.B. 9.001 wäre Temperatur in °C

Re: Heizungssteuerung mit OpenHAB 4

Verfasst: 4. Jun 2024 21:56
von Automateur0815
Ich verstehe es nicht. Vielleicht sehe ich den Wald auch vor lauter Bäumen nicht.
Das entsprechende Thing, oder besser gesagt der Channel um den es geht, ist mit dem passenden DPT konfiguriert:

Code: Alles auswählen

 - id: Temperature_GEG_Living
    channelTypeUID: knx:number
    label: Wohnzimmer
    description: Ein Kanal zur Verwaltung einer generischen Gruppenadressen mit
      einem DPT (Datenpunkttyp) kompatibel mit Number Items
    configuration:
      ga: 9.001:<1/2/2
Das entsprechende Item Temperature_GEG_Living ist ebenfalls konfiguriert:

Code: Alles auswählen

label: Wohnzimmer
type: Number:Temperature
category: temperature
groupNames:
  - Temperature
  - GEG_Living
tags:
  - Measurement
  - Temperature
Im Browser bekomme ich die Temperatur auch schön angezeigt. Aber egal ob es mit DPT 9.001 konfiguriere oder einfach weglasse, er bringt mir beim Ausführen der Regel immer diese Meldung:

Code: Alles auswählen

[rg.openhab.core.model.script.heating] - Temperature_GEG_Living liefert keine gültige Zahl!

Re: Heizungssteuerung mit OpenHAB 4

Verfasst: 5. Jun 2024 00:59
von udo1toni
Ergänze die Rule bitte mal (bzw. das logWarn):

Code: Alles auswählen

rule "Heizung Wohn- und Esszimmer"
when
    Item Temperature_GEG_Living changed
then
    if(!(newState instanceof Number)) {                                                              // Sensorwert prüfen
        logWarn("heating","Temperature_GEG_Living liefert keine gültige Zahl! ({})",newState)
        return;                                                                                      // Ausführung abbrechen
    }
    ...

Re: Heizungssteuerung mit OpenHAB 4

Verfasst: 7. Jun 2024 10:03
von Automateur0815
Muss ich die geschweifte Klammer noch irgendetwas hinein? Aktuell schreibt er nur das hier ins Log:

Code: Alles auswählen

2024-06-07 10:00:21.456 [WARN ] [rg.openhab.core.model.script.heating] - Temperature_GEG_Living liefert keine gültige Zahl! ({})

Re: Heizungssteuerung mit OpenHAB 4

Verfasst: 7. Jun 2024 10:47
von Automateur0815
Ich habe jetzt nochmal im events.log nachgeschaut. Hier steht folgendes:

Code: Alles auswählen

2024-06-07 10:18:55.503 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Temperature_GEG_Living' changed from 21.66 °C to 21.68 °C
 2024-06-07 10:19:14.438 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Sollwerte_Living' changed from 27 to 28
Anscheinend stört er sich demnach an °C. Ich habe mir nun das entsprechende Item nochmal angeschaut und entsprechend den anderen Temperatur-Items angepasst. nun liefert er anscheinend die richtigen Werte, da die Rule nun funktioniert.

Vielen Dank für deine Hilfe!