Ich glaube nicht, dass die Rule so funktioniert. Zum einen ist da ein Tippfehler (Numer statt Number in der 4. Zeile nach dem then). Allerdings verwendest Du die Variable garnicht. Dann hast Du aber auch mehrere Vergleiche des Status und verwendest dort === als Operator, das ist nicht zulässig.
Weiterhin prüfst Du teilweise den Status und schaltest dann, dabei prüfst Du aber auf OFF und schaltest dann auf ON. Ein Switch Item kann aber außer OFF und ON noch den Zustand NULL einnehmen.
Du nutzt im letzten Teil ein Item Keller_Ventilation_Trigger. der Teil der Rule wird allerdings nur abgearbeitet, (des else wegen) wenn die vorherige Bedingung if(basement_humidity < dehumitification_top) nicht erfüllt war. Diese Bedingung ist allerdings schon im ersten Teil abgehandelt und schaltet dort bereits automatisch mindestens den einen Ventilator (und je nach Zustand Timer und Taupunktdifferenz auch den anderen Ventilator) ein. Im Extremfall (Obergrenze überschritten und Taupunktdifferent groß genug) laufen anschließend zwei Timer, von denen der eine nicht mehr steuerbar ist.
Du verwendest die Action sendCommand(Itemname als String,Kommando als String), was zwar zulässig, aber nicht gut ist. Hier geht das, aber Du solltest Dir besser angewöhnen, die Methode Itemname.sendCommand(Kommando) zu verwenden. Gleiches gilt für das postUpdate.
Und die Schreibweise für createTimer ist ebenfalls nicht empfohlen, auch wenn sie zulässig ist. Besser (wegen der Lesbarkeit des Codes) ist es, das Lambda als Teil des Funktionsaufrufs zu verwenden.
Die Benennung der Variablen ventilation_interval legt nahe, dass Du einem Irrtum unterliegst

denn es erfolgt kiene Prüfung nach xx Minuten, stattdessen wird der Lüfter (bzw. die Lüfter) nach xx Minuten abgeschaltet. Eine Prüfung erfolgt ausschließlich wenn sich die Werte ändern (denn das ist der Trigger für die Rule).
Die logInfos sind hier auch eher nicht besonders hilfreich, zumal durch die langen Variablennamen ohnehin keine gute Übersichtlichkeit herrscht.
Hier mal "meine" Variante Deines Codes:
Code: Alles auswählen
var Timer tVent = null
val Number dehum_min = 65 // unterer Grenzwert Luftfeuchtigkeit
val Number dehum_max = 75 // oberer Grenzwert Luftfeuchtigkeit
val Number dewpoint_min_gap = 5 // minimale Taupunktdifferenz Keller - Außen
val Number vent_dur = 35 // Lüftungsdauer (nach xx min wird abgeschaltet)
rule "Kellerentfeuchtung"
when
Item In_THSensor_Dew changed or
Item In_THSensor_Hum changed or
Item Keller_Ventilation_Trigger changed to ON
then
val basement_hum = (In_THSensor_Hum.state as Number).floatValue
val dewpoint_gap = (In_THSensor_Dew.state as Number) - (Out_THSensor_Dew.state as Number)
var sollDecke = OFF // Default Soll
var sollWand = OFF // Default Soll
if(newState == ON) { // Keller_Ventilation_Trigger AN
sollDecke = ON // Soll ändern
sollWand = ON // Soll ändern
tVent?.cancel // laufenden Timer löschen
tVent = createTimer(now.plusMinutes(vent_dur), [| // Timer anlegen
Ventilator_Decke_switch.sendCommand(OFF)
Ventilator_Wand_switch.sendCommand(OFF)
Keller_Ventilation_Trigger.postUpdate(OFF)
tVent = null
])
} else { // Rule nicht durch Taster getriggert
if(basement_hum > dehum_max) { // Luftfeuchte Keller über Maximum
sollDecke = ON // Soll ändern
if(dewpoint_gap > dewpoint_min_gap) { // Taupunktdifferenz ok
if(tVent === null) { // Falls kein Timer läuft
sollWand = ON // Soll ändern
tVent = createTimer(now.plusMinutes(vent_dur), [| // Timer anlegen
Ventilator_Wand_switch.sendCommand(OFF)
tVent = null
])
}
}
} else if(basement_hum >= dehum_min) { // Werte innerhalb der Hysterese; kein Aktion
return;
}
}
if(Ventilator_Decke_switch.state != sollDecke) // Falls Abweichung vom Soll, schalten
Ventilator_Decke_switch.sendCommand(sollDecke.toString)
if(Ventilator_Wand_switch.state != sollWand) // Falls Abweichung vom Soll, schalten
Ventilator_Wand_switch.sendCommand(sollWand.toString)
end
Die Rule arbeitet etwas anders. Zunächst habe ich Konstanten als val definiert. Der Unterschied ist, dass diese Werte nicht mehr änderbar sind, dafür wird aber auch weniger Code erzeugt.
Dann gibt es den zusätzlichen Trigger um die Lüfter manuell zu starten. Es gibt zwei Variablen sollName, in denen der Sollzustand der beiden Ventilatoren gespeichert wird. Dadurch habe ich zwei Vorteile: Zum Einen kann ich bestimmte Prüfungen einfach auslassen (nämlich die, welche zum Zustand OFF für beide Ventilatoren führen). Zum Zweiten habe ich nur noch eine Stelle im Code, in dem ich den eigentlichen Steuerbefehl absetze (zwei Zeilen vs. sieben Zeilen). Eine Variablenzuweisung ist wesentlich effizienter als ein Funktionsaufruf, das heißt, das entstehende Kompilat ist kompakter und läuft schneller. Das macht in der einzelnen Rule nicht viel aus, in der Summe kann das aber Auswirkungen haben.
Weiter geht's im Code. Es gibt einen übergeordneten Trigger zum manuellen Starten der Ventilatoren, dieser muss also auch mit höherer Priorität verarbeitet werden, also als erstes. Ich nutze hier die Tatsache, dass die implizite Variable newState nur ON oder eine Zahl enthalten kann, wobei ON nur dann drin steht, wenn der Trigger das extra Item war. Wird also der Trigger ausgelöst, so wird der Sollzustand der Ventilatoren auf ON geändert und der Timer zum Abschalten erzeugt - wobei ein eventuell bereits laufender Timer vorher entfernt wird - tVent?.cancel ist gleichbedeutend mit if(tVent !== null) tVent.cancel.
Falls der Trigger nicht das extra Item war, wird auf Überschreiten des oberen Grenzwertes geprüft. Ist das der Fall, greift der gleiche Code wie bei Dir, nur dass ich wieder lediglich die Sollzustände setze. Ist der obere Grenzwert nicht überschritten, so kann es sein, dass der untere Grenzwert überschritten ist. In diesem Fall (wir erinnern uns - kein extra Trigger!) ist nichts zu tun und die Rule wird unverzüglich beendet.
Der letzte Fall - unterer Grenzwert unterschritten - ist bereits der Default unserer Sollzustände, muss also nicht überprüft werden.
Abschließend ist jetzt also nur noch der Sollzustand für beide Ventilatoren herzustellen, was in den letzten Codezeilen passiert.
Man könnte noch überlegen, ob man innerhalb der Timer prüft, ob der Sollzustand abweicht.
Ach so... Hysterese ist der Abstand zwischen oberem und unterem Grenzwert. Du definierst die beiden
Grenzwerte und damit
indirekt die Hysterese.