Problems with runIn

I’m trying to detect a long press (held down for more than 3 seconds) on a button. To do this, I have the following code:

def pushHandler(evt) {
	log.debug "$evt.name: $evt.value"
    
    if (evt.value == "pushed") {
    	// Do something if its still pressed in 3 seconds
        log.debug "starting runIn timer"
        state.pressed = now()
        runIn(3, stillPressed)
    }
    else if ((evt.value == "released") || (evt.value == "off")) {
    	if (!state.pressed) {
            log.debug "release after long press, doing nothing"
        }
        else {
            log.debug "unscheduling"
            state.pressed = null
            unschedule(stillPressed)
            log.debug "normal press, do something"
        }
    }
}

def stillPressed() {
    log.debug "called still pressed"
    if (state.pressed) {
    	log.debug "long press detected! time diff is ${state.pressed - now()}"
        state.pressed = null
    }
    else {
    	log.debug "its no longer pressed"
    }
}

This works sometimes (maybe 1/5 of the time) but most of the time, “stillPressed” is never called??! Anyone have any thoughts about what could be going wrong here? Thanks in advance!

Yes… runIn does not like to be scheduled for periods much less than 60 seconds. At least that has been my experience. Try replacing it with runOnce.

Thanks for the info Scott. That’s disappointing, but good to know.

Even with runOnce, I think you’ll be disappointed in the results you are trying to acheive. 3 seconds will most likely be 5 (or even 10) half the time, as all apps are run from the cloud and not locally. We need a new hub that has the capability of doing some things locally.

Yes, absolutely. There’s no reason this needs to be going to the cloud and back. I was really hoping simple stuff like this could run on the hub :frowning: Thanks again

Just realized I am making this more complicated than it needs to be and I can get rid of the unreliable timer altogether. Here is the simpler code in case anyone else is looking to detect a long press. The only disadvantage of this is that you won’t get any feedback about the long press until after you release (so you have to just make sure you hold it in long enough). The timer method would have allowed the event to happen while you’re still holding it down, then you’d know its time to release the button. Still, since the timers are unreliable, this is much better.

def pushHandler(evt) {
    // Handle the button being pushed
    log.debug "$evt.name: $evt.value"
    
    if (evt.value == "pushed") {
        state.pressed = now()
    }
    else if ((evt.value == "released") || (evt.value == "off")) {
        // Handle long press
        if (now() - state.pressed > 3000) {
            log.debug "long press detected"
            // do something here
        }
        else {
            log.debug "short press detected"
            // do something else here
        }
    }
}

Well done. Seems like 3 seconds is now overkill. Wonder if 750ms wouldn’t be more appropriate.

Good point. 3 seconds does seem a bit too long!

This is REALLY cool @obycode. Which brand of switches have you tested this with? I’m wondering if switches that don’t do instant update will work with this.

I have this working for a button, not a switch. I’m not sure that it can work with a switch. The button I’m using is based on the device type from the “Easy Button” project mentioned here: Easy Button (@bdahlem - great project idea by the way, although I didn’t use the Easy Button, I did make my own similar button based on it) using the same door/window sensor Brian mentions in that project - http://www.monoprice.com/Product?c_id=122&cp_id=12212&cs_id=1221201&p_id=10795&seq=1&format=2