runIn Parameters

is this the correct syntax to pass a parameter in runIn?

def speech1(msg) {
notify.speech(msg)
runIn(2,speech2, [data:msg])
}

def speech2(data) {
notify.speech(data)
}

Yes, as long as msg is a Map (the required data type for the data parameter/value itself), which is not enforced in your code but isn't a problem if you know that's what you need.

See: Common Methods | Hubitat Documentation

Note that technically the handler method name is documented as a string, so runIn(2,speech2, [data:msg]) should be runIn(2,"speech2", [data:msg]), but you'll normally find that this gets converted automatically, so it shouldn't be a problem either way.

MSG is a string. So do I need to do anything else with it?

What I am trying to do, I have HomePods that announce things. When more than one is selected they don’t talk at the same time. I want to make one particular one first, so delaying the other

Yes, you would need to put it in a Map, as these are different data types, and required type is a Map. There is an example in the docs I linked to that shows an example exactly like what you'd need, though there are many other ways you can do this (in terms of naming the keys or structuring the data -- it just has to be a Map and not a String, so the easiest thing is to make a key in the map with your desired string as the value, then retrieve that key from the map in your handler).

Im trying to pass a specific device across.
pick a device at random from a map, pass it to another method,
other method turn device on or off.
a) how do i pick the device details out of the map
b) is there any way to make the map smaller

void lightSwitching(){
    log.debug "${app.label} lightSwitching - Running = $state.running"
    if (state.running == true){
        log.info "${app.label} lightSwitching True - running the Sim"
        //random time
        int randomTime = (0 - timeRan) + (Math.random() * (timeRan*2))
        randomTime = randomTime + state.timeMin
        log.debug "random time $randomTime"
        int randomDev = Math.random() * lightControl.size()
        log.debug "random device number $randomDev, ${lightControl.get(randomDev)}"
        runIn(randomTime, switchControlOn, [data: [dev:lightControl.get(randomDev)]])
       //runIn(randomTime*2, switchControlOff, [data: [dev:lightControl.get(randomDev)]])

    }
    else {
        log.info "${app.label} lightSwitching false - no action"
    }
}

void switchControlOn(data){
    log.debug "${app.label} switchControlOn - $data.dev.currentStates.deviceId , $data.dev.label"
    it = data.dev.currentStates.deviceId
    it.on()
}

sorted it just passed the devices position in the map

int randomDev = Math.random() * lightControl.size() // pick random device from the list
runIn(randomTime, switchControlOn, [data: [dev:randomDev]])

then used that

void switchControlOn(data){
    if(state.running == true) {
        lightControl.get(data.dev).on()
        if(logEnable == true) log.info "${app.label} switchControlOn - ${lightControl.get(data.dev)} switched on"
    }
    else { 
        //log.debug "master switch must be off" 
    }
}

This isn't going to work, and the error gives you a hint as to why. The deviceId property is a string, so then you're calling on() on a string (not the device object). You don't want to call this on the property but rather the device object itself, just as you would anyee else in your code.

They being said, I don't really like to pass that kind of thing around in a handler, anyway, and it's posible it still won't work (if it does, you just want data.dev.on()). The device ID or DNI or some other unique property of the device is small and easy to pass, then you can look up the actual device (e.g., maybe find it in the input/setting where it was selected, like mySettingName.find { it.deviceId == myId}).

The position as you found looks like it works, too, but I'm not sure that List is guaranteed to be consistently sorted in any specific way, so I avoid that as well for that reason (and if it's not called quickly, this could definitely be the case if the user adds or removes a selection in the meantime).

This might help. I get my devices in a "multiple" input for all light devices in the room.

Then I make a state map of them in initialize() that I can use to get the index of the device names in the multiple input array, so I can find the device to use it.

In addition, I also use that in another a method to return the device object to send commands to it:

get you point about the is changing
struggling now with you find suggestion i cant get the find to work.

void switchControlOn(data){ //data.dev == device ID
    if(state.running == true) {
        log.debug data.dev
        lightControl.find {it.deviceId == data.dev}
        log.debug it

Maybe add a log.debug lightControl line to help figure things out..

void switchControlOn(data){ //data.dev == device ID
    if(state.running == true) {
        log.debug data.dev
        log.debug lightControl
        lightControl.find {it.deviceId == data.dev}
        log.debug it
void switchControlOn(data){ //data.dev == device ID
    if(state.running == true) {
        log.debug data.dev
        log.debug lightControl.deviceId
        lightControl.find {it.deviceId == data.dev}
        log.debug it

Try log.debug getObjectClassName(data.dev) -- wondering if one is a Number and one is a String (the property on the actual device object is a String, though there's also idAsLong).

its an int

That's it, then, as the other property is a String. You could force them both to strings or use idAsLong (I don't think comparing to an int will be a problem in Groovy...), among other options.

sorry you will have to explain where to put that

There are lots of possibilities. :slight_smile:

But here's an easy one:

lightControl.find {it.deviceId == "${data.dev}"}

still not playing ball, 'it' is still null

 log.debug data.dev
        log.debug lightControl
        log.debug lightControl.deviceId
        log.debug getObjectClassName("${data.dev}")
        log.debug getObjectClassName(lightControl.deviceId)
        lightControl.find {it.deviceId == "${data.dev}"}
        log.debug it


whats a GStringImpl?

is there a way to pass the data in the runIn command as a string instead of a map

Still strugling

String dev = data.dev.toString()
        log.debug dev
        log.debug getObjectClassName(dev)
        lightControl.find {it.deviceId == dev }
        log.debug it

image

Looks like you never set it to anything? (Also probably not a variable name I'd explicitly pick, though it should work...)