runIn(), runOnce(), pause(msec) which one to use in this example

I have read all the examples listed here - http://docs.smartthings.com/en/latest/smartapp-developers-guide/scheduling.html but I could not get it to work for my code. I need suggestion on how to implement a while loop.

In a nut shell, Im making an api call that returns a number say “0”. Eventually (typically within 10 to 60 seconds) this api call will return me another number, say “1”, and when it does, I want to inform it was successful.

Right now I have the following code.

panelMetaData(token,locationID){
    // some HTTP call
    log.debug alarmCode
    return alarmCode
}
    
arm() {
    //some HTTP call
    def metaData = panelMetaData(token, locationId) // Get AlarmCode
            while( metaData.alarmCode != 10201 ){ 
                pause(2000) // Two Second Pause to relieve number of retried on while loop
                metaData = panelMetaData(token, locationId)
             }  
           sendPush("Home is now Armed successfully")
           
}

However, intermetently I see my code throwing error

error java.util.concurrent.TimeoutException: Execution timed out after 40 units. Start time: <some date> @ line <some line>

which I get it as, Im making my code do “stupid” recursive calls to get a confirmation. Now I have read several post talking about use case of runIn() and runOnce() in this situation, but to my non developer nature Im not able to get understanding of how I can implement this in my code. Anyone can throw some light or some suggestions?

Post seen:

How about something like this:

arm() {
    //some HTTP call
    attempt()               
}

def attempt() {
    def metaData = panelMetaData(token, locationId) // Get AlarmCode
    if ( metaData.alarmCode != 10201 ) { 
      runIn(2, attempt)
    }
    else {
      sendPush("Home is now Armed successfully")
    }
}
1 Like

What happens if you increase the delay time? Try doing like 10 seconds of delay and see if works then.

Thanks @chrisb & @obycode for your suggestions.

@obycode - Im yet to implement your work around, so I will update you later

@Chris - By increasing the pause/delay time, I think, I do reduce the number of API call it makes. Not sure if this will help me in the "Timeout Exception"situation as it seems sometimes, it requires more than 40 seconds for the api call to return correct code. Also doing so I reduce the frequency of alerting the user in amount in order of 10 seconds. I understand its a trade-off, but if it will work, Im all for it. Here are two examples of log messages (with the same code) showing how intermittent it is.

Didnt work:

  6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:33 AM: error java.util.concurrent.TimeoutException: Execution timed out after 40 units. Start time: Thu Nov 12 17:40:53 UTC 2015 @ line 75
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:31 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:29 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:27 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:25 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:22 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:24 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:20 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:16 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:18 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:13 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:14 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:11 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:09 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:07 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:03 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:02 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:05 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:41:00 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:40:58 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:40:56 AM: debug AlarmCode is 10307
    6c23a7ae-86f6-4447-bfd8-549cf612b643  11:40:54 AM: debug AlarmCode is 10307

Worked:

6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:46 AM: debug Smart Things has successfully logged out
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:46 AM: debug During logout - 507BC822-5FF6-4173-876F-63DAD4204D8D
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:46 AM: debug Home is now Disarmed
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:46 AM: debug AlarmCode is 10200
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:44 AM: debug This was given as Location - Home
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:44 AM: debug Smart Things has logged In. SessionID: 507BC822-5FF6-4173-876F-63DAD4204D8D
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:43 AM: debug Executed login
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:43 AM: debug Reset the switch
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:27 AM: debug Smart Things has successfully logged out
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:27 AM: debug During logout - A4AB15A3-1998-4FE3-BA67-BE891DFCEC59
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:27 AM: debug Home is now Armed successfully
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:27 AM: debug AlarmCode is 10201
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:26 AM: debug AlarmCode is 10307
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:26 AM: debug AlarmCode is 10307
6c23a7ae-86f6-4447-bfd8-549cf612b643  11:45:24 AM: debug This was given as Location - Home

@obycode I have been contemplating about this for a while

Can you (or anyone else) help me understand the difference between runIn() vs pause() in both the example? I would like to understand why is one better over the other in SmartThings from the execution point of view.

Also tagging @Dianoga & @pstuart to this discussion as they had in past recommended suggestions around this.

I’m not entirely sure how pause or runIn work, but I’ll guess.

Pause could be some kind of busy wait, in which case that would explain why the SmartApp hits the timeout. It could be like a coroutine yield, which would save state and release resources while paused.

The runIn is definitely releasing resources and setting a timer to call the specified function. It doesn’t require saving any additional state.

1 Like

Make sense. When I get time, I will implement that. Thank you

I’ll try and shed some light on this.

runIn() schedules a new execution of this SmartApp, calling the method specified in the number of seconds specified. With values less than 60 seconds, YMMV.

pause() simply suspends the current thread, for the number of milliseconds specified (not to exceed 15 seconds).

pause() is unsupported and should not be used. It will be removed in a future release. I considered adding it to the documentation with a big warning to not use it - but decided against documenting it at all.

Regarding the timeout you experienced - because any SmartThings code (SmartApps or Device Type Handlers) execute within the SmartThings ecosystem along with other SmartApps or Device Type Handlers, there must be limits in place to the resources they use, so they do not negatively impact any other SmartThings code. That’s why they are only allowed to execute for a certain amount of time before being limited (we have work planned to document this).

Bottom line - use runIn(), not pause(). pause() will be removed soon. If you’re wondering exactly why pause() is unsupported (aside from the discussion of the execution model above), I am working to verify the specifics around this specific case and will communicate it soon (I don’t want to communicate possibly inaccurate information). I know I’m the kind of person who needs to know the why behind things :smile:

I hope my long-winded explanation doesn’t cause more confusion, I just wanted to shed some more light on this :smile:

1 Like

This is great. I guess more reasons for me to abandon harmful pause() and use run() instead.

More ST backend system resources are expended when a runIn() or runOnce() is used for times less than 5-10 secs IMHO since a running program using runIn() would need to be added to a ST startup queue, cleanup the running programs’ resources and then terminate. A new instance of the same program would be initiated almost immediately and re-consume global backend ST resources depending on the programs logic.

A programatic pause(), less than 5-10 seconds, IMHO would only consume minimal ST cloud services before resuming the program. I can see the reason why a long pause would not be acceptable and runIn() or runOnce() would be appropriate.

When/if ST ever enables local processing on the V2 hub, a pause() would be much easier to implement than a complicated runIn() or runOnce().

Also, ST cloud has been VERY UNSTABLE in the re-scheduling jobs recently, and many programs that critically depended on runIn() have not been re-initiated where as a short pause() would have worked perfectly.

2 Likes

@Jim & @kurtsanders - Thank you for great discussion on this.

The delay that Im trying to implement is inside a while loop. Does this changes anything in terms of using pause() & runIn()

@obycode Gave a decent solution in post number [2]. I was wondering will this be still preferred and less destructive rather than my while loop.

@Jim Is there any documentation that mentions explicitly about Execution timed out after 40 units. I guess I wanted a clear understanding on whether it is bound to execution of a single function or just a single event which could result into executing of many functions. Also can you shed some light on metric “units” and what it means

I fear its the later, but as I will develop more of my code, Im afraid I this will hit me very hard.

Hi @mhatrey,

We have work identified and planned to document the runtime environment in greater detail, including the timeout limits.

In the interim, I updated the documentation here to include this.

1 Like