[SOLVED] How to get a rule to wait before finish

whats the best way to get a rule to wait before finishing

i just want too add a wait at the end of my leaving home routine so it leaves the lights on for say 3 mins after the rule was activated i always end up pressing the leaving home button and then manually have too turn lights back off while leaving and off just before i leave

rule "Power OFF LR Routine"
when
Item HTPC2_Online_Status changed from ON to OFF
then
if (Trigger_PowerOffLR.state == ON) {
    Plug3_Switch.sendCommand("OFF")
    Plug2_Switch.sendCommand("OFF") 
    Lamp1_Brightness.sendCommand("0") //WAIT 3 mins before turning off all below
    BULB6LRMAIN1_Color.sendCommand("64,3,0")
    Bed_Trigger.sendCommand("OFF")
    Leaving_Home_Trigger.sendCommand("OFF")
    Trigger_PowerOffLR.sendCommand("OFF")
    logInfo("Rule", " Power OFF LR Routine Ran (Routines.rules)")
}
end
rule "Power OFF LR Routine"
when
Item HTPC2_Online_Status changed from ON to OFF
then
if (Trigger_PowerOffLR.state == ON) {
    If (DelayTimer !==null){
        DelayTimer = createTimer(now.plusSeconds(3))[|
           Plug3_Switch.sendCommand("OFF")
           Plug2_Switch.sendCommand("OFF") 
           Lamp1_Brightness.sendCommand("0") //WAIT 3 mins before turning off all below
           BULB6LRMAIN1_Color.sendCommand("64,3,0")
           Bed_Trigger.sendCommand("OFF")
           Leaving_Home_Trigger.sendCommand("OFF")
           Trigger_PowerOffLR.sendCommand("OFF")
           logInfo("Rule", " Power OFF LR Routine Ran (Routines.rules)")
           DelayTimer = null
       ]
   } else {
        DelayTimer.reschedule(now.plusSeconds(3))
   }
}
end

Something like the above should do it.

1 Like

For further explanation of Matthew’s approach.

Set a Timer to go off three minutes into the future and put your light turning off code inside the Timer’s body.

Also, missing from Matthew’s solution is the global variable to store the Timer. The Rule should looks something like:

var leavingTimer = null

rule "Power OFF LR Routine"
when
    Item HTPC2_Online_Status changed from ON to OFF
then
    if (Trigger_PowerOffLR.state == ON) {
        // stuff to do immediately

        if(leavingTimer == null){
            leavingTimer = createTimer(now.plusMinutes(3), [|
                // stuff to do three minutes after leaving
                leavingTimer = null
            ])
        }
        else {
            leavingTimer.reschedule(now.plusMinutes(3)) // reschedule if the Timer is still running
        }
    }
end

You will want a mirror rule to cancel the timer if you return before the timer goes off.

rule "Returned home"
when
    Item HTPC2_Online_Status changed to ON
then
    leavingTimer?.cancel
    leavingTimer = null
end

Or you can combine the rules into one with an if statement and the Item HTPC2_Online_Status changed.

1 Like

im not sure because theres quite a bit of code there i dont understand yet

i have copied what you sent and added the things to do its doesn’t seem to have errors i will test it as soon as i get chance

var leavingTimer = null

rule "Power OFF LR Routine"
when
    Item HTPC2_Online_Status changed from ON to OFF
then
    if (Trigger_PowerOffLR.state == ON) {
        // stuff to do immediately

        if(leavingTimer == null){
            leavingTimer = createTimer(now.plusMinutes(3), [|
            Plug3_Switch.sendCommand("OFF")
    Plug2_Switch.sendCommand("OFF") 
    Lamp1_Brightness.sendCommand("0")
    BULB6LRMAIN1_Color.sendCommand("64,3,0")
    Bed_Trigger.sendCommand("OFF")
    Leaving_Home_Trigger.sendCommand("OFF")
    Trigger_PowerOffLR.sendCommand("OFF")
    logInfo("Rule", " Power OFF LR Routine Ran (Routines.rules)")
                // stuff to do three minutes after leaving
                leavingTimer = null
            ])
        }
        else {
            leavingTimer.reschedule(now.plusMinutes(3)) // reschedule if the Timer is still running
        }
    }
end

Thanks for that Rich & Mathew i have had the time too test today as everybody is out it worked perfect

Great glad to help!

is there a way to get a rule to wait a short time like 5 seconds do you use thread::sleep(5)

Thread::sleep takes milliseconds so it would be Thread::sleep(5000). However, be very careful using any sleep more than a few hundred milliseconds. Anything longer should be handled with a Timer where possible. The problem is while a Rule is running it consumes an execution Thread. If you have enough Rules sleeping you can run out of execution threads and your Rules will lock up.

OK thanks for that rich i was only planning on using it for waits of a few seconds no longer how do you find the limit of these threads is it cpu related

running a ryzen 5 1600

im still not sure how you made that timer i added it too my rule and it works fine but i dont think i know enough about the code too be able too add them to rules myself soon but not yet

The number of threads is hardcoded into ESH. It is independent of the CPU. I don’t know the thread pool size for OH/ESH but think it is in the five or six range. Increasing the size has impacts far beyond just the CPU. Coming up with the right size is not always an easy problem.

And, to the CPU, a few seconds is an eternity.

Depending on what you are trying to accomplish and what your other Rules are doing you may be fine. But the more sleeps you have and the longer they are the more likely you are to encounter a deadlock or a situation where Rules take a long time to trigger while waiting for some other Rule to return an execution thread.

1 Like

ok six is not many , i agree that a few seconds is a very long time for a cpu to wait i got confused with cpu threads for a moment,

i just need to do more rules and talk too more people like you and i will soon be there a few weeks ago i couldent even make a text rule as you know but now i can make simple rules alone

i was going too add the sleep to rules that dont run on there own only when commanded by open hab once a day

It isn’t totally separate from CPU threads. But creating and destroying a new thread is expensive. So to avoid that expense, programs like OH pre-creates a pool of threads that are precreated. When a Rule needs to run, the code is passed to one of these existing threads to run so that the Rule can execute in a separate thread without the expense of creating and closing down that thread.

It provides a huge performance improvement because in Java (and therefore those languages that run on Java like the Rules DSL) one of the most expensive things one can do is create and destroy objects.

Also, in an application like OH, often a central component has far more data and is much more capable of efficiently scheduling and managing threads than an individual piece of code (e.g. a Rule) will. However, in order for that to work it means that those individual pieces of code must conform to a set of standards or else it all falls apart. Two of the big things the individual pieces of code must do are:

  1. not create their own threads
  2. execute quickly and exit

When you use Thread::sleep you run the risk of violating 2.

1 Like

the reason i wanted to use the sleep command was too slow my rule down it was running too fast it was the same rule you sent me too power off living room,

It was set too send a stop command too kodi then send a shutdown command aftee both commands were sent at pretty much the same time this was causing kodi too freeze and sometimes crash a wait after the stop seemed to solve this

In that sort of situation, a short sleep is OK, but if you need to wait seconds then

createTimer(now.plusSeconds(5), [| 
    Kodi.sendCommand(OFF) // or whatever 
])

is more appropriate.

1 Like

Thanks Rich I will keep what you have said in mind when using the sleep command and also have a better read about timers

would you like a look at one of my rules too see if it can be wrote better its a movie mode that ends and checks if the time is between a certain value and sets my light too a set value i can see this one growing

Post it as a new thread and I’ll get to it eventually (I’m about three days behind right now).

Though it can also be a good learning opportunity to see if you can come up with ways to make it better yourself. Look at the Design Pattern postings for some ways to solve common problems.

Without seeing the code itself I would expect the Associated Items, Separation of Behaviors, Lambda with Copius Notes, and Proxy Item DPs would potentially be useful.

i have posted it, its extremely simple i couldn’t write complex yet if i wanted too.

i have also been messing with the rules myself making them better and shorter they are all prob laughable this would be my best attempt atm

i will also read the topic you suggested i have found it

I have been trying to get this too work when you add a timer like in my test rule how would you cancel it

rule "rule name"
when
Item Test_Rule_Trigger changed
then
if( Test_Rule_Trigger.state ==ON ) {
Lamp1_Brightness.sendCommand ("OFF")
createTimer(now.plusSeconds(30), [| 
Lamp1_Brightness.sendCommand ("ON")
])
}
if( Test_Rule_Trigger.state ==OFF ) {
    Timer.cancel
}
end

i was trying variations of how you said to cancel a timer but i just cant get it right, i have added a few timers like the ones in my rule that i would like to be able too cancel and also the rule from this post that you sent too me i havent tried to cancel the timer you created for me but im sure the code you sent me will work on that

createTimer returns a reference to the timer. In order to cancel the timer you have to save that reference in a variable. In order to keep that reference from one execution of the rule to the next you must have the timer to a global variable.

So look at all the places leavingTimer in post 4 occurs and you will see it declared as a global var, see it being assigned the reference to a timer returned by createTimer, and the rule you quoted above which cancels it.