Rule that fires when item is switched ON, OFF and ON again

Hi,

In a bedroom I have a z-wave relais switching a light either via Openhab or a regular wall switch,
In a rule I would like to catch an “ON-OFF-ON” sequence off the light and then automatically switch the light off after 1 minute.

How could this be accomplished in a rule? Does anyone have an idea or a rule example showing how to track such an ON-OFF-ON sequence?

Thanks a lot in advance.

Here is an idea to get you started. I haven’t tested anything, so debugging is up to you.

Items

Switch ToggleSwitch "Toggle Switch" 
Switch ToggledLight

Rules

var Timer toggleTimer = null  // Acts as a flag to say toggle in progress, and as a timer
var Timer offDelayTimer = null  // Delay before lights turned off


rule "Toggle Switch On"
when 
    Item ToggleSwitch changed to ON
then
    ToggledLight.sendCommand(ON)
    if (toggleTimer == null) {
        toggleTimer = createTimer(now.plusMillis(1000)) [|
              toggleTimer = null
        ]
    } else {
        toggleTimer.cancel()
        toggleTimer = null
        offDelayTimer = createTimer(now.plusSeconds(60)) [|
                ToggledLight.sendCommand(OFF)
                offDelayTimer = null
        ]
            
    }
end

rule "Toggle Switch Off"
when
    Item ToggleSwitch changed to ON
then
    if (toggleTimer == null) {
        ToggledLight.sendCommand(OFF)
    } else {
        toggleTimer.cancel()
        toggleTimer = createTimer(now.plusMillis(1000)) [|
            ToggledLight.sendCommand(OFF)
            toggleTimer = null
        ]
    }
    if (offDelayTimer != null ) {
        // THis will be true if Switch Toggled ON-OFF-ON-OFF
        offDelayTimer.cancel()
        offDelayTimer = null
    }
end
1 Like

My toggle switch and the toggled light itself are one item, I will modify your rules and test them today when I am back home.
My first try combined everything in one rule with timers which didn’t work reliably, separating this in two rules seems to be way better here.

Thanks a lot for your input, very well appreciated. Will post an update later after debugging.

I like @gpbenton’s approach a lot. But decided to post an alternative approach mainly for the challenge. :wink: I wanted to see if I could do it without Timers.

Items:

Switch LightSwitch "Light"

Rules:

import org.joda.time.*

var DateTime lastOff = now // Initialize to now so I don't have to worry about null checks later
var DateTime lastOn = now

rule "Light switched"
when
    Item LightSwitch receivedCommand
then

    if(receivedCommand == ON) {

        // Second ON received
        if(lastOn.isAfter(now.minusSeconds(2) && 
           lastOff.isAfter(now.minusSeconds(2) &&
           lastOn(isBefore(lastOff))) {
            lastOn = now // capture timestamp
            Thread::sleep(60000)
            LightSwitch.sendCommand(OFF)
        }

        // First ON of a potential ON-OFF-ON sequence
        else {
            lastOn = now
        }
    }

    // OFF received
    else {
        lastOff = now
    }
end

Theory of operation:

When an OFF is received we simply take the timestamp.

When an ON is received we look to see if we received an ON followed by an OFF within the past two seconds. This is done by checking the saved timestamps to see if they are both within the past two seconds and the ON is older than the OFF. If this is true we have detected an ON-OFF-ON sequence so we sleep for a minute and turn off the light. Otherwise we just take the timestamp.

There is a lot less code but the operation of the code is a bit more subtle so I don’t know if I would call this better. For example, I have to initialize lastOFF first at the top to make sure that lastOn(isBefore(lastOff)) evaluates to false the first time an ON is received after a restart/reload.

Same warnings apply, I just typed this in and haven’t tested it.

1 Like

Rich, this is an interesting approach as well, will definitely give it a try. I like the idea of completely avoiding timers. Thanks a lot for your input.

@gpbenton’s approach is verified in the meantime. I implemented and tested it already, works like a charm.

Once I have both rules working I will post the code here. Thanks again, really appreciate your inputs.

Rich, I tried to implement your suggestion, but the rule is throwing an exception after the first if-clause. The exception thrown is:

2016-06-11 14:36:18.469 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Light switched': The name '<XMemberFeatureCallImplCustom> && <XMemberFeatureCallImplCustom>' cannot be resolved to an item or type.

I guess this affects the two “now.minusSeconds(2)” conditions.
Do you have an idea what could be causing this exception?

I’m a rule-newbie, but it looks to me, as there are two ‘)’ missing, one after each isAfter…

You win the prize @vespaman.

I did warn that I just typed this in. Designer would have caught that.

Don’t be like Rich. Use Designer.

1 Like

Yes, true. Was looking at the rule several times but I just didn’t see it. Thanks for that, @vespaman.
Nevertheless I still have problems with this rule but I will keep trying.
I will mark this thread as solved as the first suggestion already works but I will keep trying to get the second one working as well.

Here is the currently implemented and working rule:
`import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*

var Timer toggleTimer = null  // Acts as a flag to say toggle in progress, and as a timer
var Timer offDelayTimer = null  // Delay before lights turned off


rule "Toggle Switch On"
when 
    Item Light_SchlafzimmerOg changed to ON
then
    if (toggleTimer == null) {
        toggleTimer = createTimer(now.plusMillis(1000)) [|
              toggleTimer = null
        ]
    } else {
        toggleTimer.cancel()
        toggleTimer = null
        offDelayTimer = createTimer(now.plusSeconds(60)) [|
                offDelayTimer = null
	     	    sendCommand(Light_SchlafzimmerOg, OFF)
        ]
        
    }
end

rule "Toggle Switch Off"
when
    Item Light_SchlafzimmerOg changed to OFF
then
    if (toggleTimer == null) {
    } else {
        toggleTimer.cancel()
        toggleTimer = createTimer(now.plusMillis(1000)) [|
            toggleTimer = null
        ]
    }
    if (offDelayTimer != null ) {
        offDelayTimer.cancel()
        offDelayTimer = null
    }
end

Thanks a lot everyone for your suggestions.

Hi Guys

Sorry for hijacking this topic but it is the closest to what I’m looking for just in reverse.

How would i turn this rule around so that when mu switch is press that the light stays for one minute and when on-off-on-off stays on until on-off is pressed again.

Regards