How can i schedule a method with parameters?

HI to everyone,

I look through many discussion but still cant find solution for my case.
Can i schedule something with params, for example

I want to schedule thermostat setCoolSetpoint to 70 degree on 7 pm

so i wrote

schedule($time, scheduleSetCoolSetpoint(device, temp))

def scheduleSetCoolSetpoint(device, temp){
device.setCoolSetpoint(temp)
}

but when i try it. method executes without any delay, just when i call a schedule, so im stuck, please help me if u can )

Good question. I believe this is an unresolved issue (i.e., I did a very brief scan through some code examples and the documentation, and found no syntax that permitted parameters to be passed along with the method name).

An old post of similar issue:

The scheduled method name is sometimes used with double-quotes.
It’s worth a try, but I’m not optimistic; since all the examples don’t even use empty parenthesis for no parameters.

schedule($time, "scheduleSetCoolSetpoint(device, temp)")

Possible workaround? Global Variable(s)? Probably can’t safely rely on the “state[]” global in this case, because it is not guaranteed to be updated between thread instances of the SmartApp (it is written to persistent storage only at the end of execution) – though I’m not sure the exact circumstances. You could use a Device with extra Attributes, but that seems cumbersome. So I don’t have an answer without further research.

I’m optimistic this isn’t a unique scenario though, and further posts will give us answers.

I found an official SmartThings response which seems to confirm parameters cannot be used; only a string with the name of the method. Though you are not creating a new closure, I think that the same limitation of carrying the parameter values to that new “invocation” of the SmartApp is applicable here.

But I’ve thought of another possible, but perhaps lousy, solution:

  • Move the “schedule” effect inside the body of the method by using the “delay:” option to the Devices’s setCoolSetpoint Command?

e.g., setCoolSetpoint( temp, delay: < longtime_milliseconds calculated until the desired schedule time > )

NB: This is still a guess at this time. Old posts said that delay was limited to under 60 seconds, but newer posts said that restriction was removed. Still seems too awkward; but sharing my thoughts for now.


So…

Take a look at this example for how a complex scheduling of Thermostat Temperature setting was handled:

Thank you all for answers, very informative.

My intention was to schedule some actions in future using http methods. Now i see what I should use external scheduling for that.

1 Like

The lack of global variable and being forced to use state and maps is such a pain in this environment. I won’t pretend I have the science allowing me to know what kind of constraint there, though. Is this an inner limitation of groovy?

No - not a limitation of Groovy; much more complicated than that.

SmartThings doesn’t execute our Groovy directly. It goes into what they call a “sandbox” - really just a bunch of pre-processing and other layers so it can be run in the SmartThings Cloud alongside everyone else’s SmartApps. The cloud consists of execution environments and persistent storage (Cassandra or other AWS compatible database(s), …).

Traditional global variables would only be possible if each Customer had their own Java Virtual Machine (still in the cloud and/or local) but this would take a horrendous amount of resources.

Using “Virtual Devices” sorta an appropriate way to solve this, except they could have provided a “Global Variable Device” instead of the customer having to manually spawn these themselves.

Well - You can supposed store extra data in the Location object; but that’s an entirely unofficial hack with its own issues.

For valid security reasons, not only is each SmartThings Account (and Location) supposed to be secured from all others; but every Device and SmartApp must not be able to see or affect any other (except for parent-child). Just like one App on your phone can’t talk directly to any other App.

In fact, one other smart home platform vendor designed their Hub on Android and each SmartApp was to be a distinct App.

In short? This stuff is hard. SmartThings came up with a “pretty darn good” architecture to launch the product. But it probably should have gone through a couple major evolutions since then; but then how could existing users continue to work?

Here is how I do it:
Here is how I do it. I use this trick a lot in order to allow my apps to learn from users’ new inputs (yes, this is machine learning…) but not under all occasions. Sometimes some values are meant to be temporary (like boosting your heating system for a couple minutes) and I don’t want the A.I. to learn from those inputs (since “evt.source” returns only and always “DEVICE”, which is another PIA in this environment).

I needed to be able to schedule with parameters in order to allow an exec to end before any new value is sent to my thermostats and thus have all maps updated (those maps contain data related to whether or not to record new inputs in the A.I. database). IF SOMEONE KNOWS A WAY TO DO THE SAME IN A SIMPLER MORE ELEGANT WAY, please don’t just say it, prove it and show it! :slight_smile: Thanks in advance.

QUESTION: I’ve been struggling when trying to get the String of a Key and of a value in a map for which the process is not supposed to know the characters contained in the Key string (hence the for loops). I’d love some help on that one, meaning something like getting a key using only its index value just as you’d do it in a 2D array in C.

**update: I just realized one could simply use a list of maps with 1 sole entry. Feel free to try and let me know!


def someThing(){
state.NoLearnMode = ["$thisTherm" : "true"] // make sure A.I. does not learn this temporary value related to the absence of motion 
HSP = comfort - HeatNoMotion
// State is recorded into the ST database only at the end of the execution
// so we need to delay this command as to allow NoLearnMode to be updated by then
state.setheatingsetpoint = [:] // must always be empty so as to contain only one entry
state.setheatingsetpoint."${thisTherm}" = HSP
runIn(1, setheatingsetpoint)
}

def setheatingsetpoint() {
    log.debug "state.setheatingsetpoint = $state.setheatingsetpoint"
    def map = state.setheatingsetpoint
    def thisAppliance
    def found = null
    def device = null

    def s = Thermostats.size()
    int i = 0
    for (s != 0; i < s; i++) {
        found = map.find{it.key == "${Thermostats[i]}"}
        if (found) {
            log.debug "FOUND $found"
            break
        }
    }
    if (!device) {
        i = 0
        s = ApplianceWithPwMeter.size()
        for (s != 0; i < s; i++) {
            found = map.find{it.key == "${ApplianceWithPwMeter[i]}"}
            if (found) {
                log.debug "FOUND $found"
                break
            }
        }
    }
    if (!found) {
        log.debug "ERROR ---------- NOTHING FOUND"
    }

    def value = found?.value
    def deviceString = found?.key

     device = Thermostats.find{it.toString().contains("$deviceString")} // retrieve the object
    if (!device) {
        device = ApplianceWithPwMeter?.find{it.toString().contains("$ApplianceWithPwMeter")} // retrieve the object
    }

    log.debug "setheatingsetpoint() for device: $device and value: $value"
    device.setHeatingSetpoint(value)
    state.setheatingsetpoint = [:] // must always be empty so as to contain only one entry
}

Old thread, but I was just trying to figure out how to pass parameters via runIn myself, knowing I’ve done it before in SmartThings. Finally found my own example, figured I’d share. You have to add a map named “data” and then write your method to accept the parameters in that map:

runIn(someTime, someMethod, [data: [listNumber: “$i”]])

def someMethod(data) {
def i = data.listNumber
}

1 Like