Peer Review Requested - Problems with Schedule()

(Mike) #1

After a period of time the program stops executing the target proc of the Schedule function. The only way I can get my programs to start working again is to go into the SmartApp configuration screen and hit Done to trigger an update.

SmartThings support mentioned that they have a potential bug that they are tracking that could be related but wanted me to have a peer review before associating my issue.

Any help is appreciated!

definition( name: "Turn Off After Motion Stops for X Mins", namespace: "", author: "Michael Kock", description: "Turns off switches x minutes after motion stops from an associated motion sensor", category: "Green Living", iconUrl: "", iconX2Url: "")

preferences {
section(“Monitor for motion here…”){
input “motionSensor”, “capability.motionSensor”, title: “Where?”
section(“And after this many minues of no motion…”){
input “minutes1”, “number”, title: “Minutes?”
section(“Turn off these light(s)…”){
input “switches”, “capability.switch”, multiple: true
section(“Optionally, only during this timeframe”){
input “starting”, “time”, title: “Start time”, required: false
input “ending”, “time”, title: “End time”, required: false

def installed()
/Prime the timestamp in case no motion is seen subsequently/
state.inactiveAt = now()


def updated()

def createSubscriptions() {
subscribe(motionSensor, “”, motionActiveHandler)
subscribe(motionSensor, “motion.inactive”, motionInactiveHandler)
schedule(“0 * * * * ?”, “scheduleCheck”)

def motionActiveHandler(evt) {
log.debug "$ $evt.value"
state.inactiveAt = null

def motionInactiveHandler(evt) {
log.debug "$ $evt.value"
if (!state.inactiveAt) {
state.inactiveAt = now()

def scheduleCheck() {
log.debug "schedule check, ts = ${state.inactiveAt}"
if (state.inactiveAt) {
def elapsed = now() - state.inactiveAt
def threshold = 1000 * 60 * minutes1
log.debug "Elapsed = ${elapsed} and Threshold = ${threshold} and ${getTimeOk()}"
if (elapsed >= threshold && getTimeOk()) {
log.debug "turning off lights"
state.inactiveAt = null
else {
log.debug “${elapsed / 1000} sec since motion stopped”

private getTimeOk() {
def result = true
if (starting && ending) {
def currTime = now()
def start = timeToday(starting).time
def stop = timeToday(ending).time
result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
log.trace "timeOk = $result"

(Aaron) #2

If you don’t mind me asking, what is this doing that the built-in ‘Light & Switches’ app doesn’t already do?

(Mike) #3

This is a very simplified example of one of the programs that employ the Schedule() method and I thought would be simpler to review. There certainly is functionality in the Lights and Switches to turn on and off based on motion. There are other versions using this same Schedule() technique that have the same issue symptoms that are not taken care of by the built in programs such as performing different operations based on the amount of Lux registering on an outside sensor.

(Kristopher Kubicki) #4

I’ve read that polling() excessively can result in throttling, and perhaps scheduling is similar.

I recommend looking at the runEveryXMinutes() method as I have seen more reliability from this.

(Aaron) #5

1000 appologies, @forestall. I forgot set this thread to ‘watching’ and you didn’t tag me in your response so I didn’t realize you had responded to my question until I just now stumbled back onto this thread.

I don’t fully understand what you are trying to do in your code. It looks like you are frequently calculating an elapsed time and comparing to to some threshold in order to decide if its time to turn the lights off. If this is the case, a better method would be to use a runIn() method or runOnce() to schedule the turning off. The documentation of these methods has been drastically improved recently and is worth taking a look at if you haven’t done so recently.

This is what I have in my dusk-to-dawn app:

def handleEndMotionEvent(evt) {
	log.debug "handleEndMotionEvent() Motion stopped . . . ."
    if (state.ambient == "dark") {
    	switches?.setLevel(state.BrightLevel)  //does nothing unless sun went down during active motion
        state.Level = state.BrightLevel
    	log.debug ". . . set the dimmers to level $state.BrightLevel if not already there"
        runIn((state.DelayMin*60), modeDim)  //delay is number of minutes entered in preferences x 60 to get seconds
                                             //will be unscheduled if it gets light in the meantime
    	log.debug ". . . but it light, so do nothing"

DelayMin is a value entered in the preferences in minutes.

If this doesn’t help, ask some more questions . . . .

(Ron S) #6

I think there’s a max. 4 scheduling from a SmartApp. Veterans can confirm. There is something like you can check canSchedule or something…

(Aaron) #7

4 pending scheduled jobs is the limit, yes. The code presented by the OP uses cron notation . . .

. . . . which I am not that familiar with. I think this notation translates to once every minute but would only count as one of the allowable 4 pending scheduled jobs at any given time. It may be excessive and/or unnecessary, certainly . .

(Mike) #8

Thank you all for your assistance! @AaronZON, would you be willing to PM me you entire Dusk to Dawn code? I am guessing there may be several other better techniques I can glean from it!

Again guys, thanks!

(Aaron) #9

Happy to help, @forestall.

The link to where I am keeping the updated version is in the first post of this thread. I’m tracking any remaining issues with the app there as well.