Momentary Button to turn on light and off in x mins BUT reset timer

Hey Guys,

Pulling my hair out waiting for my ST Hub. I’m trying to use IFTTT to send my Wemo Motion to ST which will then tell a light to turn on and then for the light to turn off after 5 minutes. I also want it in such a way that if there was motion in that 5 minutes to leave the light on for another 5 minutes, so to over write the delay 5 mins for another 5 mins.

I have gotten the IFTTT Wemo to send data to the virtual switch (Momentary Button Tile) to turn on and all is good, problem is will turn off after the set duration rather than resetting the existing timer.

Does anyone have any example code that would help?
Here is what I have so far, the dimmer isnt working either, cant work out why not

/** * switch test * * Copyright 2015 Barry Murphy * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * for the specific language governing permissions and limitations under the License. * */ definition( name: "Turn on lights for duration", namespace: "Barry", author: "Barry Murphy", description: "Turn lights on for x minutes", category: "", iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png")

/**

  • Turn me off quick

  • Author: seateabee@gmail.com

  • Heavily based on Power Allowance by SmartThings

  • Date: 2013-06-28
    */
    preferences {
    section(“When this switch is turned on or off”) {
    input “theSwitch”, “capability.switch”, title: “Where?”
    }
    section(“Turn this light on”) {
    input “switch1”, “capability.switch”, title: “Turn on…”, required: true, multiple: true
    }
    section (“Set Dim Levels”) {
    input “DimLevelStr”, “enum”, title: “Dimmed Level %”, required: true,
    options: [“10”,“15”,“20”,“30”,“50”,“75”], defaultValue: “20”
    }

    section(“Turn it off how many Minutes later?”) {
    input “secondsLater”, “number”, title: “When?”
    }
    }

def installed() {
// log.debug “Installed with settings: ${settings}” subscribe(theSwitch, “switch.off”, switchOnHandler)

}

def updated() {
log.debug “Updated with settings: ${settings}”

unsubscribe()
subscribe(theSwitch, "switch.off", switchOnHandler)

}

def switchOnHandler(evt) {
log.debug "Switch ${theSwitch} turned: ${evt.value}"
state.DimLevel = DimLevelStr as Integer
switch1.on()
def delay = (secondsLater * 1000) * 60 /*Because delay is counted in mill-seconds, multiple seconds by 1000 to get the proper delay. */
log.debug “Turning off in ${secondsLater} minutes (${delay}ms)”
// Working part
// switch1.off(delay: delay)
switch1?.setLevel(state.DimLevel)
}

Many thanks
Barry

In short I want to cancel my existing delay and over write it with the new delay; is this possible?

Instead of using the delay in the off command, use a runIn() (documentation here). The optional argument overwrite will do what you want.

Thanks, towards the last few hours I managed to find some example code on that, however I dont know what to use in my IF statement to see if this is a new request or old.
Can I do IF runIn( delay exists then remove it and add a new one?

I.e.

def switchOnHandler(evt) {
    log.debug "Switch ${theSwitch} turned: ${evt.value}"
    log.debug "${evt.value} is ${evt.value}."
    
        if (evt.value == "active") {                // If there is movement then...
        log.debug "Cancelling previous turn off task..."
        unschedule( turnOff )                   // ...we don't need to turn it off.
    }
    else {                                      // If there is no movement then...
def delay = (secondsLater * 1000) * 60           // runIn uses seconds
        switch1.on()
        log.debug "Turning off switches in ${minutesLater} minutes (${delay}s)."
        runIn( delay, turnOff )                 // ...schedule to turn off in x minutes.
    }
}

The overwrite option will do this for you. If it is already scheduled, the new one will overwrite the old one, no need to separately remove the old one.

runIn(delay, turnOff, [overwrite:true])

Actually, after double-checking the documentation, this is the default, so you don’t even need the overwrite argument.

If what you’re looking for is for the light to turn on when motion is detected, and off 5 minutes after motion is no longer detected, you may not actually need that feature anyway. How about something like this (assuming switchOnHandler runs when motion is detected and switchOffHandler runs when motion is no longer detected):

def switchOnHandler(evt) {
    log.debug "Switch ${theSwitch} turned: ${evt.value}"
    
    switch1.on()                            // turn on the light (if its already on, no harm done)
    log.debug "Cancelling previous turn off task..."
    unschedule( turnOff )                   // (if nothing was scheduled, no harm done)
}

def switchOffHandler(evt) {
    log.debug "Switch ${theSwitch} turned: ${evt.value}"

    def delay = minutesLater * 60           // runIn uses seconds
    log.debug "Turning off switches in ${minutesLater} minutes (${delay}s)."
    runIn(delay, turnOff)                 // ...schedule to turn off in x minutes.
}

Thanks mate…

I’m not quite using motion in the normal sense, I call the virtual switch from IFTTT.

If I replace switch1.off(delay: delay) with
runIn( 60, turnOff() )
def turnOff()
{
log.debug “Turning switches off. I’ll miss you.”
// switch1?.setLevel(state.DimLevel)
switch1.off()
}

I get errors every time i try use runin, any ideas?

	groovy.lang.MissingMethodException: No signature of 
method: 
physicalgraph.scheduling.CassandraInstalledSmartAppSchedulerService.runIn()
 is applicable for argument types: 
(physicalgraph.device.cache.InstalledSmartAppDTO, java.util.ArrayList, 
java.lang.Integer, java.util.LinkedHashMap) values: [Turn on lights for 
duration, [null], 60, ...]
Possible solutions: 
runIn(java.lang.Object, java.lang.String, int, java.util.Map), dump(), 
any(), println(), find(), wait() @ line 85

it should be runIn(60, turnOff).

In your code above, you added the () to the method, which has the result of calling that method and using its return value as the second argument to runIn. Instead, you should just pass the reference to the method (without the ()).

Life saver! thanks for that, its all working as expected now :smile:
One last thing I’m trying to work out is whether i can set the level of the lights back to a higher value during switch off.

I tried the following, but I dont really want to flash all lights from 20 to 80% then turn them off.
Is there any variable you can set on swtch.off to set the level back to 80, so if someone physically turns them back on it will be at the standard light levels rather than the 20 dim?

def turnOff()
{
    log.debug "Turning switches off.  I'll miss you."
//    switch1?.setLevel(80)
    switch1.off()
}