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?
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
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. 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.
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?
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
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.