Ja, Deine Rule hat (nach intensiver Analyse...) ein paar Fehler.
Dabei ist die Komplexität Deiner Bedingungen auch Ausgangspunkt Deiner Probleme. Beispielsweise fragst Du mehrfach auf die gleiche Bedingung ab. Es gibt aber nur eine Stelle, wo das tatsächlich notwendig ist (nämlich innerhalb des Timers, weil dieser Code losgelöst von der Rule betrachtet werden muss).
Auch die Reihenfolge der Befehle in Deiner Rule ist unglücklich gewählt. Dadurch kommt es beispielsweise zu mehreren Meldungen, falls mehrere Randbedingungen nicht erfüllt sind.
Außerdem muss der gesamte Rulecode abgearbeitet werden. Und wie gesagt, diverse Bedingungen müssen mehrfach abgefragt werden.
Dadurch hat sich auch ein grundlegender Logikfehler eingeschlichen, nämlich hier:
Code: Alles auswählen
if (Night.state == OFF && Comfort_Shutter.state == ON && Holiday.state == OFF && (tempmax < 20 || (tempmax >= 20 && (OneCallAPIweatherandforecast_Current_Conditionid.state != "800" || OneCallAPIweatherandforecast_Current_Conditionid.state != "801")))) {
Wenn wir mal die ersten drei Bedingungen weg lassen, bleibt
Code: Alles auswählen
if (tempmax < 20 || (tempmax >= 20 && (OneCallAPIweatherandforecast_Current_Conditionid.state != "800" || OneCallAPIweatherandforecast_Current_Conditionid.state != "801"))) {
Stattdessen kannst Du aber auch
schreiben.
Entweder, die Bedingung ist wahr, weil tempmax < 20 ist. Oder tempmax >= 20 und eine der beiden nachfolgenden Bedinguen ist nicht(!!!) erfüllt. Da aber immer maximal eine der beiden Bedingungen erfüllt sein kann, ist dieser Teil immer true, womit der Rest des Bool'schen Terms ebenfalls immer true ist.
Besser ist es also, solche komplexen Abfragen in kleinere Einheiten zu zerlegen.
statt
Code: Alles auswählen
if(tempmax >= 20 && (OneCallAPIweatherandforecast_Current_Conditionid.state != "800" || OneCallAPIweatherandforecast_Current_Conditionid.state != "801")))
besser
Code: Alles auswählen
var Boolean bCondition = false // default: falsch
if(strCondition == "800") bCondition = true // falls Condition 800
if(strCondition == "801") bCondition = true // oder 801 -> true
if(tempmax < 20) bCondition = false // falls Temperatur < 20 false
if(!bCondition)) { // falls false
wobei strCondition den Status des entsprechenden Items als String enthält.
Dies wäre die umgemodelte Rule:
Code: Alles auswählen
var Integer tempmax = 0
rule Rollladen_Task
when
Time cron "0 30 08 ? * MON-SAT" or
Time cron "0 00 09 ? * SUN"
then
val mailActions = getActions("mail","mail:smtp:h478579470")
tempmax = (Weather_Temp_Max_0.state as DecimalType).intValue
val String strCondition = OneCallAPIweatherandforecast_Current_Conditionid.state.toString // schon wegen der Länge des Itemnamens...
logInfo("Shutter", "Comfort_Shutter: {}", Comfort_Shutter.state)
logInfo("Shutter", "Holiday: {}", Holiday.state)
logInfo("Shutter", "Night: {}", Night.state)
logInfo("Shutter", "tempmax: {}", tempmax)
logInfo("Shutter", "OneCallAPIweatherandforecast_Current_Conditionid: {}", strCondition)
if(Comfort_Shutter.state != ON) { // Automatik ist ausgeschaltet
mailActions.sendMail("abcdefghijklmnop@api.prowlapp.com", "Komfortfunktion aus: " + OffsetDateTime.now().toString, "Rollladen bleiben unten.")
return;
}
if (Holiday.state == ON) { // Ferien
mailActions.sendMail("abcdefghijklmnop@api.prowlapp.com", "" + SpecialDay + ": " + OffsetDateTime.now().toString, "Rollladen bleiben unten.")
return;
}
if (Night.state != ON) { // Es ist Tag
// anders gestaltete Logik:
var Boolean bCondition = false // default: falsch
if(strCondition == "800") bCondition = true // falls Condition 800
if(strCondition == "801") bCondition = true // oder 801 -> true
if(tempmax < 20) bCondition = false // falls Temperatur < 20 false
if(!bCondition) { // falls false
logInfo("Shutter", "Rollladen fahren auf.")
mailActions.sendMail("abcdefghijklmnop@api.prowlapp.com", ""+ tempmax + " Grad: " + OffsetDateTime.now().toString, "Rollladen fahren auf.")
Rollladen.allMembers.filter(f|(f.state as Number).intValue != 0).forEach[ s|
s.sendCommand(UP)
logInfo("Shutter", s.name + " received UP")
]
Lampe_Treppe.sendCommand(OFF)
} else { // Temperatur >= 20 und (800 oder 801)
SunProtection.postUpdate(ON)
}
} else { // es ist Nacht
mailActions.sendMail("abcdefghijklmnop@api.prowlapp.com", "Information: " + OffsetDateTime.now().toString, "Es ist noch Nacht, Rollladen bleiben unten.")
if (t_night !== null) { // Timer läuft bereits
return;
}
t_night = createTimer(now.plusMinutes(10), [ |
if (Night.state == ON) { // es ist weiterhin Nacht
mailActions.sendMail("abcdefghijklmnop@api.prowlapp.com", "Information: " + OffsetDateTime.now().toString, "Es ist immer noch Nacht, Rollladen bleiben unten.")
t_night.reschedule(now.plusMinutes(10))
} else { // es ist nun Tag
mailActions.sendMail("abcdefghijklmnop@api.prowlapp.com", "Information: " + OffsetDateTime.now().toString, "Rollladen fahren auf")
Rollladen.allMembers.filter(f|(f.state as Number).intValue != 0).forEach[ s|
s.sendCommand(UP)
logInfo("Shutter", s.name + " received UP")
]
Lampe_Treppe.sendCommand(OFF)
t_night = null
}
])
}
end
Die Abbruchbedingungen sind nach oben gewandert. Dadurch wird nur diejenige Bedingung ausgegeben, die als erste nicht zutrifft. Wenn also Ferien sind und die Automatik ausgeschaltet ist, kommt nur die Meldung wegen der abgeschalteten Automatik, das ist aber in meinen Augen auch sinnvoller.
Noch ein Wort zu !== ... (oder doch ein paar Worte...)
a == b -> a entspricht b, bzw. a ist gleich b
a != b -> a ist ungleich b
a === b -> a ist identisch b
a !== b -> a ist nicht identisch b
Umgangssprachlich (aber korrekt im Sinne der alten Definition): a === b -> a ist dasselbe wie b (Man kann nicht dasselbe Ei zweimal essen)
Der Vergleich auf Identität ist nur an einer stelle sinnvoll, nämlich, wenn es um den Vergleich mit null geht (bitte nicht mit NULL verwechseln!) null ist ein Zeiger auf eine ganz bestimmte Speicherzelle. Wenn eine Variable null enthält, dann wird der Zeiger der Rule auf eben diese Speicherstelle verbogen, womit die Variable identisch mit null ist (sie zeigt auf dieselbe(!) Speicherzelle.
Dieser Vergleich des Zeigers ist aber ausschließlich bei null sinnvoll (jedenfalls wenn es um openHAB geht...) Schon wenn es um Zahlen geht:
Code: Alles auswählen
var Number a = 5
if(a === 5)
logInfo("test","a ist identisch mit 5!")
else if (a == 5)
logInfo("test","a ist gleich 5!")
Dieser Code wird als Ausgabe nur die zweite Logzeile ausgeben, weil a eben nicht identisch mit 5 ist.