RunIn not working reliably?


(Tom Otvos) #1

I have written an app that cycles a switch. The preferences prompt for a period of time between “on” events, and a period of time, once “on”, after which the switch is turned off. The person I wrote it for has got periods of 45 minutes and 7 minutes respectively.

I find the app worked great for several days, and then mysteriously it goes silent for a chunk of time (like 8-10 hours), and then it resumes as if nothing happened. I know that “runIn” does not guarantee precise up-to-the-second timing, and I know that you can only have 4 “runIn” events scheduled at the same time in an app. I am careful to clear things out. So this leaves me stumped and, as discussed in another thread on “Live Logging” I cannot provide enough debug tracing to narrow it down.

So my question is: has anyone else experience reliability issues with “runIn”?

Here is the bulk of my code, if it helps:

preferences 
{
    section()
    {
    	paragraph "Use this app to regularly cycle a single switch."
        paragraph "If you specify an interval of zero minutes, it will disable the automatic cycling of the switch until you set the interval again."
        paragraph "If the switch is manually turned on, the cycling will be cancelled until the switch is manually turned off. The regular cycle will then resume."
	}

    section("Cycle this switch...") 
    {
		input (name:"targetSwitch", type: "capability.switch", title: "Which switch?")
	}
    
    section("Turn it on every...") 
    {
		input (name: "intervalMinutes", type: "number", title: "Minutes")
	}

    section("Turn it off after...") 
    {
		input (name: "runMinutes", type: "number", title: "Minutes")
	}
}

def installed() 
{
	log.debug "Installed with settings: ${settings}"

    if (intervalMinutes > 0 && runMinutes > 0)
		initialize()
    else
    	log.debug "Interval is zero, auto-cycling is turned off"

}

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

	unsubscribe()
    unschedule()
    
    if (intervalMinutes > 0 && runMinutes > 0)
		initialize()
    else
    	log.info "Interval is zero, auto-cycling is turned off"
}

def initialize() 
{
    state.autoOn = false

	// make sure switch is off initially
    targetSwitch.off()

	// subscribe to on/off events
	subscribe(targetSwitch, "switch.on", onHandler)
	subscribe(targetSwitch, "switch.off", offHandler)

	// prime the pump by triggering the first on event
   	runIn(intervalMinutes * 60, autoTurnSwitchOn)
}

def onHandler(evt) 
{
	log.info "Switch has turned on"
    
    // if switch has been turned on by our doing, then schedule an 'off' event, but
    // if the switch was turned on manually, do nothing until the switch is off again
    if (state.autoOn && runMinutes > 0)
		runIn(runMinutes * 60, autoTurnSwitchOff)
    else
    {
    	log.info "Switch turned on manually...will not schedule an 'off' event"
        // make sure any scheduled on/off event is cancelled
        unschedule()
    }
}

def offHandler(evt) 
{
	log.info "Switch has turned off"
    
    // unschedule pending actions in case this 'off' event came directly
   	unschedule()
    
    state.autoOn = false
    
    // regardless of source of the 'off' event, we do a regularly-scheduled 'on' event
    log.info "Switch will turn back on in ${intervalMinutes} minutes"
   	runIn(intervalMinutes * 60, autoTurnSwitchOn)
}

def autoTurnSwitchOn() 
{
	// log.debug "AutoTurnSwitchOn"
	state.autoOn = true
	targetSwitch.on()
}

def autoTurnSwitchOff() 
{
	// log.debug "AutoTurnSwitchOff"
	targetSwitch.off()
}