I have been struggling to generate a rule that, after triggering, pauses for, lets say 12 hours in order not to send multiple commands while the triggering status is still valid.
Specifically, I want a rule that opens all blinds above a wind speed threshold but once they’re retracted the rule should pause.
rule "Vertical blinds up at wind"
when Item OWM_Wind_Speed changed
then
if (wRun === null){
if (OWM_Wind_Speed.state > 20 && gVertStoren.state > 0 ) {
gVertStoren.members.forEach[i|i.sendCommand(0)]
wRun = createTimer(now.plusHours(20), [|logWarn(ruleName1, "Timer Off")])
}
}
end
rule "Vertical blinds up at wind"
when Item OWM_Wind_Speed changed
then
if (wRun === null){
if (OWM_Wind_Speed.state > 20 && gVertStoren.state > 0 ) {
gVertStoren.members.forEach[ i|i.sendCommand(0)]
wRun = createTimer(now.plusHours(20), [ |
logWarn(ruleName1, "Timer Off")
wRun = null // reset the timer
])
}
}
end
If you are running snapshot you need to insert a space after the [
However running a timer for 20 hours is not recommended as you are creating a thread for the timer and that thread is then unavailable for the next 20 hours leaving you with only 4 threads available to run OH.
Use the expire binding instead:
Install the expire binding
Create a switch item as follow:
rule "Vertical blinds up at wind"
when Item OWM_Wind_Speed changed
then
if (BlindsSwitchTimer.state == OFF) {
if (OWM_Wind_Speed.state > 20 && gVertStoren.state > 0 ) {
gVertStoren.members.forEach[i|i.sendCommand(0)]
BlindsWindTimer.sendCommand(ON)
}
}
end
Not quite. If OP were using Thread::sleep that would be a concern. But Timer threads come from a separate thread pool, and the Timer only consumes a runtime thread when it had triggered and is running the lambda.
A long timing timer may take up a miniscule extra amount of memory but it won’t use a rules execution thread. That has why we recommend using timers instead of sleeps for anything more than half a second.
Expire binding has lots of other benefits though, including much simpler code to manage it.
There might be a little bug in the expire example. I think you need
if (BlindsSwitchTimer.state != ON) {
To cover the car where BlindsSwitchTimer is NULL. On a restart of OH, unless something else sets it to OFF the timer will never get started.
My bad, I was under the impression the the OH timer were taking up a thread and that’s why the expire binding was so powerful. I will still recommend it’s use whenever possible as it is much easier to use than timers (Syntax, check if null, cancel the timer…)
Thanks