hi, I used the window open timer from the tutorial and applied it to my environment.
unfortunately I still get open door messages altough the doors are closed. I think the cancel timer doesn`t shoot correctly. can someone please double check?
the logs:
2018-03-29 20:32:50.771 [GroupItemStateChangedEvent] - Door changed from OPEN to CLOSED through Garagentor_links
2018-03-29 20:32:50.943 [INFO ] [eclipse.smarthome.model.script.Rules] - canceltimer {Garagentor_links=org.eclipse.smarthome.model.script.internal.actions.TimerImpl@1453fb}
2018-03-29 20:34:18.622 [INFO ] [eclipse.smarthome.model.script.Rules] - Fenster Garagentor_links 10 Min. offen
the rule:
import java.util.Map
val Map<String, Timer> OpenWindowTimers = newHashMap
val Functions$Function2<ContactItem, Map<String, Timer>, Boolean> checkOpenWindow = [
Door,
timerMap |
val String myTimerKey = Door.name.toString
//val Number temp = Temperature_outside.toString
if (Door.state == CLOSED) {
//if (timerMap.get(myTimerKey) !== null) timerMap.get(myTimerKey).cancel() //this line should do the same as the next one
timerMap.get(myTimerKey)?.cancel
logInfo("Rules", "canceltimer " + timerMap)
} else if (Door.state == OPEN && Temperature_outside.state <= 5) {
timerMap.put(myTimerKey, createTimer(now.plusMinutes(2)) [|
timerMap.put(myTimerKey, null)
logInfo("Rules", "Fenster " + Door.name.toString + " 10 Min. offen")
Thread::sleep(100)
])
}
else if (Door.state == OPEN && Temperature_outside.state > 5 && Temperature_outside.state < 15) {
timerMap.put(myTimerKey, createTimer(now.plusMinutes(2)) [|
timerMap.put(myTimerKey, null)
logInfo("Rules", "Fenster " + Door.name.toString + " 30 Min. offen")
Thread::sleep(100)
])
}
true
]
rule "Fenster check"
when
Item Door received update
then
Thread::sleep(250) // this gives the persistence service time to store the last update
val lastUpdatedDoor = Door.members.filter[s|s.lastUpdate("mapdb") !== null].sortBy[lastUpdate("mapdb")].last as ContactItem
checkOpenWindow.apply(lastUpdatedDoor, OpenWindowTimers)
end
rule "Fenster check"
when
Item Door1 changed or
Item Door2 changed or
...
then
val timerItem = DoorTimers.members.findFirst[ door | door.name == triggeringItem.name +"_Timer" ]
if(triggeringItem.state == CLOSED) timerItem.postUpdate(OFF) // cancel timer
else timerItem.sendCommand(ON) // set the timer
end
rule "Fenster timer"
when
Item Door1_Timer received command OFF or
Item Door2_Timer received command OFF or
...
then
if(Temperature_outside.state <= 5) {
logInfo("Rules", "Fenster " + triggeringItem.name.replace("_Timer", "") + " 10 Min. offen")
}
else if(Temperature_outside.state > 5 && Temperature_outside.state < 15) {
logInfo("Rules", "Fenster " + triggeringItem.name.replace("_Timer", "") + " 30 Min. offen")
}
end
No more maps, no more lambdas, no more sleeps, no more timers, half as many lines of code, and fewer levels of indentation. I think it is a fair trade for n more Items to implement the timers.
hi Rich,
I was dealing already with your design patterns but I didn´t achieve what I wanted.
my idea was to have two different timers depending on the temperature, because I use that rule mainly for beeing remembered if someone forgot to close the window after ventilating the room. If it´s below 5 deg Celcius I allow the window to be open for 5 minutes, if it´s 5 to 15 deg I allow 30 minutes, above 15 deg my heating is switched off anyway and I don`t care about open windows.
I gave your rule a try and split the rule “Fenster timer” in two rules:
rule "Fenster timer1"
when
Item Door1_Timer1 received command OFF
then
if(Temperature_outside.state <= 5) {
logInfo("Rules", "Fenster " + triggeringItem.name.replace("_Timer", "") + " 10 Min. offen")
}
end
rule "Fenster timer2"
when
Item Door1_Timer2 received command OFF
then
if(Temperature_outside.state > 5 && Temperature_outside.state < 15) {
logInfo("Rules", "Fenster " + triggeringItem.name.replace("_Timer", "") + " 30 Min. offen")
}
end
the rules fail because the val timerItem requires a special namingconvention “_Timer” and ignores “_Timer1”
my Contact Items have different names, do I also have to rename them to Door1 to x?
the rule indeed simplifies things but on the other hand the item list gets thicker and thicker.
any idea how to implement two different timers for one item?
If that was your intent, that isn’t want l what you implemented. In both cases you set a timer for 2 minutes in the original code which is what I used as a guide.
You can do the same with Expire based timers. You would have a second timer Item and then use the name of the timer Item to send the right alert in the timer rule. Pretty much as you have.
The Associated Item DP requires the items to bed named do you can easily created the name of an associated Item for there name of another one. You don’t have to change the name of your other contact items but you need to be consistent in how you name the timer items. Above I appended “_Timer”.
Like I said on the outset, you are trading additional items in exchange for reduced complexity in Rules. But because items are relatively simple it is more than a fair trade. If it is a code between a two new items per window versus two timers, a lambda, and a Map, the choice is simple. If add 30 or 40 new items to avoid that complexity in rules.
But to answer your question, you can only use one timer Item if the amount if time remains the same for both uses.
If that was your intent, that isn’t want l what you implemented. In both cases you set a timer for 2 minutes in the original code which is what I used as a guide.
my fault, next time I try to explain my use-case in more details. I´ve set both timers to 2 minutes for testing purposes only, didn´t want to wait 15 or 30 minutes each time I test the rules.
I implemented your recommendations but there still seems to be a bug. in the rule “Fenster check” you cancel the timer when a window closes
timerItem.postUpdate(OFF)
but this triggers rule “Fenster timer” (because Timer received command OFF) and reports a message which it shouldn´t do?
Using postUpdate will not trigger a rule that is using received command as the trigger. That is why we have both a received command an received update rule trigger. The “Fenster timer1” rule will only trigger when the Door1_Timer1 Item received an OFF command and since the only thing that is sending an OFF command to that Item is the expire binding that Rule should only trigger when the expire timer times out.
The postUpdate is used to cancel the timer.
So if that Rule is triggering then it means you are using Door1_Timer1.sendCommand(OFF) somewhere else in your rules or failing to cancel the timer with a postUpdate(OFF) when the door closes.
Add some logging to “Fenster check” to make sure that the postUpdate(OFF) is being called when the door closes.
Also look in events.log to make sure that all the Items are changing state as you expect.
rule "Fenster check"
when
Item Wohnzimmer_Terrassentuer_Sensor changed or
Item Kueche_Terrassentuer_Sensor changed or
Item Waschkueche_Fenster_Sensor changed or
Item Gaestezimmer_Fenster_Sensor changed or
Item Schlafzimmer_Balkon changed or
Item Keller_Katzenzimmer_Sensor changed or
Item Keller_Kelleraufgang_Sensor changed or
Item Garagentor_links changed or
Item Garagentor_rechts changed
then
val timerItem1 = DoorTimers.members.findFirst[ Door | Door.name == triggeringItem.name +"_Timer1" ]
val timerItem2 = DoorTimers.members.findFirst[ Door | Door.name == triggeringItem.name +"_Timer2" ]
Thread::sleep(500)
if(triggeringItem.state == CLOSED) {
Thread::sleep(500)
timerItem1.postUpdate(OFF) // cancel timer1
timerItem2.postUpdate(OFF) // cancel timer2
logInfo("Rules", "Fenster check " + triggeringItem.name + " cancel timer")
}
else
Thread::sleep(500)
timerItem1.sendCommand(ON) // set the timer1
timerItem2.sendCommand(ON) // set the timer2
logInfo("Rules", "Fenster check " + triggeringItem.name + " set timer")
end
rule "Fenster timer1"
when
Item Garagentor_links_Timer1 received command OFF or
Item Garagentor_rechts_Timer1 received command OFF or
Item Kueche_Terrassentuer_Sensor_Timer1 received command OFF or
Item Waschkueche_Fenster_Sensor_Timer1 received command OFF or
Item Gaestezimmer_Fenster_Sensor_Timer1 received command OFF or
Item Schlafzimmer_Balkon_Timer1 received command OFF or
Item Keller_Katzenzimmer_Sensor_Timer1 received command OFF or
Item Keller_Kelleraufgang_Sensor_Timer1 received command OFF or
Item Wohnzimmer_Terrassentuer_Sensor_Timer1 received command OFF
then
if(Temperature_outside.state <= 5) {
logInfo("Rules", "Fenster " + triggeringItem.name.replace("_Timer1", "") + " 10 Min. offen")
}
end
rule "Fenster timer2"
when
Item Garagentor_links_Timer2 received command OFF or
Item Garagentor_rechts_Timer2 received command OFF or
Item Kueche_Terrassentuer_Sensor_Timer2 received command OFF or
Item Waschkueche_Fenster_Sensor_Timer2 received command OFF or
Item Gaestezimmer_Fenster_Sensor_Timer2 received command OFF or
Item Schlafzimmer_Balkon_Timer2 received command OFF or
Item Keller_Katzenzimmer_Sensor_Timer2 received command OFF or
Item Keller_Kelleraufgang_Sensor_Timer2 received command OFF or
Item Wohnzimmer_Terrassentuer_Sensor_Timer2 received command OFF
then
if(Temperature_outside.state > 5 && Temperature_outside.state < 30) {
logInfo("Rules", "Fenster " + triggeringItem.name.replace("_Timer2", "") + " 30 Min. offen")
}
end
Don’t mix the event.log and openhab.log. It is easier for me to look at them in parallel then all mixed up like that.
If the timer is immediately turning back on it means “Fenster check” is triggering again and setting the timer to ON again. Add logging to verify that is indeed what is happening and to try and figure out why.