Switch set on time

I can not write this rule.
There are 2 switches sw1 and sw2. Sw1 is changing its state ocasionally. Sw2 changes state on the base of sw1:

  1. when sw1 become ON -> sw2 become ON but this state of sw2 lasts at least 10min regardles what happens with sw1 state during this time. After 10 min when sw1 is still ON sw2 remains ON as long as sw1 remains ON.
  2. then when sw1 changes to OFF, sw2 changes to OFF as well, but this state of sw2 lasts at least 5min regardles what happens with sw1 state during this time. After 5min when sw1 is still OFF sw2 remains OFF as long as sw1 remains OFF.

Summarizing sw2 min. ON time is 10min and min OFF time is 5min.

This sounds a whole lot like you are trying to control a light based on a motion sensor. Have you looked at any of the motion sensor examples, such as Design Pattern: Motion Sensor Timer?

Ultimately you need to use a Timer.

var Timer timer = null

rule "Sw1 changed to ON"
when
    Item Sw1 changed
then
    // if the timer is not null we ignore the change in Sw1
    if(timer == null) {

        // The two switches have different states and since there isn't a Timer running we know that Sw1 
        // changed states which will cause Sw2 to change as well
        if(Sw1.state != Sw2.state) {

            // how long to set the timer for
            val time = if(Sw1.state == ON) 10 else 5

            // change Sw2's state to match Sw1's
            Sw2.sendCommand(Sw1.state)

            // create a timer to update Sw2 after the timeout if necessary
            timer = createTimer(now.plusMinutes(time), [|
                if(Sw1.state != Sw2.state) Sw2.sendCommand(Sw1.state)
                timer = null
            ])
        }
    }
end

I just typed in the above, something may be wrong.

Thank you for reference link and code example, especially for comments which pointed me out what is happening here! Your code is working perfectly but not exactly how I want, please correct me.
In your code:

  1. If Sw1.state changes after timer is set, after the timeout Sw1.state -> Sw2.State and timer -> null.
  2. Because timer is null after timeout we can immediately change state of Sw2 … but I want to wait.
  3. It seems that to fulfill my assumptions after timeout of 1st timer I have to set another timer, and over and over for next cycles.

I spent last few hours trying to solve this problem, but without a success. Per analogia coding does not work :slight_smile:

What I wrote matches your original set of requirements. I don’t understand what you are describing now.

Are you saying that you want there to be a five or 10 minute block in setting Sw2 every time it changes? In other words that after 10 minutes and Sw1 doesn’t match Sw2 we send the command to Sw2 to make them match but then it needs to be another 5 or ten minutes (depending on the state) before Sw2 can be changed again?

That is a completely different problem from what you originally stated and in a lot of ways a simpler problem. However, it has a tricky nested timers problem because you would need to create the Timer from inside the Timer (turtles all the way down).

This could be implemented with a lambda but I’m going to use Expire Based Timers.

Group:Switch:OR(ON,OFF) Sw2_Timers
Switch Sw2_Five_Timer (Sw2_Timers) {expire="5m,command=OFF"}
Switch Sw2_Ten_Timer (Sw2_Timers) {expire="10m,command=OFF"}
rule "Sw1 changed"
then
    Item Sw1 changed
then
    // If either Timer is on, the Group Sw2_Timers state will be ON
    if(Sw2_Timers.state == ON){
    
        // The two switches are in different states
        if(Sw1.state != Sw2.state) {
            // Send the command to synchronize the switches
            Sw2.sendCommand(Sw1)

            // Set the timer
            if(Sw1.state == ON) Sw2_Ten_Timer.sendCommand(ON)
            else Sw2_Five_Timer.sendCommand(ON)
        }
    }
end

rule "Sw2 Timer went off"
when
    Item Sw2_Five_Timer received command OFF or
    Item Sw2_Ten_Timer received command OFF
then
    // If Sw1 doesn't match Sw2, make them match and reset the timer
    if(Sw1.state != Sw2.state){

        // Send command to sync Sw2 with Sw1
        Sw2.sendCommand(Sw1.state)

        // Reset the Timer
        if(Sw1.state == ON) Sw2_Ten_Timer.sendCommand(ON)
        else Sw2_Five_Timer.sendCommand(ON)
    }
end

Rich, thank you for your time, example code and comments! Now it works as expected.
Sorry about poor explanation of my problems. This is because I am beginner, my Engish isn’t fluent as well.

Just for others: there are few typos in the “Sw1 changed” rule. Below find rule with my corrections:

rule "Sw1 changed"
when   //was then
    Item Sw1 changed
then
	if(Sw2_Timers.state == OFF){   //state was ON, should be OFF I think
        if(Sw1.state != Sw2.state) {
            Sw2.sendCommand(Sw1.state)    //was Sw1 instead of Sw1.state
            if(Sw1.state == ON) Sw2_Ten_Timer.sendCommand(ON)
            else Sw2_Five_Timer.sendCommand(ON)
        }
    }
end

Once more thank you for sharing your knowledge and it would be interesting to see solution with a lambda.

Your corrections are correct. The forum has been unresponsive all day and it is easy to miss things on the phone. Search the forum for Cascading Timers for a lambda example. I don’t recommend its approach. At a high level it creates a lambda that creates the Timer for you. Then from your Rule and from inside the Timer you call this lambda to recreate the Timer.

It’s clever but overly complicated when compared to the Expire Binding Example. It is much cleaner to just sendCommand(ON) to set a timer than have a lambda that creates a lambda that can call itself.