Command Delay simulation

Appreciate the response.
Developing the command structure to replace the ST command delay or filtering out the delay: nnnn map is not the problem, the big issue for me is attempting to get dynamic device and command names to execute in the target routine, your "delayMe".

Yes there is. Sorry, tuning in late.

You can call a method using a string for the name. So, suppose you have a list of method names, and a delay parameter.

def methodList = ["abc", "def", "xyz"]

this."${methodList[index]}"(delayParam)

That is how you can parameterize the method that is called. Rule 3.0 uses this approach extensively. One thing to note, this won't work inside a closure, as this would refer to the closure, not to the app or driver as you want it to. However, you could simply put that code outside the closure and call it from the closure to get it to work.

Appreciate the response. However I need a targetMethod that handles a dynamic deviceid, versus the input setting device name as shown in my earlier example below

def delayBeep(map)
{
log.debug "delayBeep entered: ${map}"
//	thebeepers.beep()    this works but cant be used 
def dvc=map.it
dvc.beep()                       //this fails with a lazyMap error
}
```.

There is not a method available that converts device id to a device. That would represent a security hole, where an app could take over devices without the user having selected them.

Everything is running local on HE, and I somehow obtained a non owned foreign device id, would it even be possible to target that device from my local hub by issuing a command? For example if someone's device id (not real) is D0B0064:050C I could issue a command to it?

I understand the security issue and not wanting to have a user target a device id they do not own, but would it be too much overhead to verify the user owns the dynamic device prior to executing the command.

Are you trying to control a device not selected by an input? You didn't show above where you came up with the devices you want to pass to delayBeep().

As long as the devices are selected from an input somewhere, you can do this.

For example:

def genericDelay(dev, command, delayMS) {
    runInMillis(delayMS, commander, [data: [dev: dev, cmd: command]])
}

def commander(data) {
    def dev = data.dev
    def cmd = data.cmd
    dev."$cmd"()
}

This can go further if the device commands take parameters. This is how Custom Commands in RM are built and work. There are Custom Actions coming that use a similar technique to send any command and any parameters to any device.

1 Like

@bravenel this error is bring thrown:

app:2802019-12-02 03:29:20.313 pm errorgroovy.lang.MissingMethodException: No signature of method: groovy.json.internal.LazyMap.setLevel() is applicable for argument types: (java.lang.Integer, java.lang.Integer) values: [0, 3] on line 4991 (runDeviceCmd)

here is the code based on your example … indicated with <<<<< line 4991 where the error is thrown at:

private deviceCmdDelay(dev, command, delayMS, param1 = null, param2 = null)		{
	if (param2 != null)
    	runInMillis(delayMS, runDeviceCmd, [data: [dev: dev, cmd: command, param1: param1, param2: param2]])
	else if (param1 != null)
		runInMillis(delayMS, runDeviceCmd, [data: [dev: dev, cmd: command, param1: param1]])
	else
		runInMillis(delayMS, runDeviceCmd, [data: [dev: dev, cmd: command]])
}

def runDeviceCmd(data)		{
    def dev = data.dev
    def cmd = data.cmd
	def p1 = data?.param1
	def p2 = data?.param2
	if (p2 != null)
		dev."$cmd"(p1, p2)   <<<<<
	else if (p1 != null)
		dev."$cmd"(p1)
	else
		dev."$cmd"()
} 

here is the call to deviceCmdDelay:

// switches is list of devices to turn off collected from all switches specified in app settings
for (def switch : switches)
   if (switch.hasCommand("setLevel")
       deviceCmdDelay(switch, 'setLevel', delay, 0, 3)

what am i doing wrong?

thank you

I don't know. How about putting some log.debug in to see what it looks like just prior to the error?

added this:

log.debug "dev: $dev | cmd: $cmd | p1: $p1 | p2: $p2"

gets this output … looks odd …

[app:280](http://192.168.1.44/logs#app280)2019-12-02 04:01:41.655 pm [debug](http://192.168.1.44/installedapp/configure/280)dev: [currentStates:[[floatValue:6535.0, jsonValue:6535, value:6535, dataType:NUMBER, unit:null, date:2019-12-02T17:48:18+0000, stringValue:6535, id:null, numberValue:6535, doubleValue:6535.0, name:colorTemperature], [value:on, dataType:ENUM, unit:null, date:2019-12-02T07:18:08+0000, stringValue:on, id:null, name:switch], [floatValue:49.0, jsonValue:49, value:49, dataType:NUMBER, unit:null, date:2019-12-03T00:01:32+0000, stringValue:49, id:null, numberValue:49, doubleValue:49.0, name:level], [value:Polar, dataType:STRING, unit:null, date:2019-12-02T17:48:18+0000, stringValue:Polar, id:null, name:colorName]], driverId:193, isComponent:false, zigbeeId:B0CE18140301ABC0, hub:[updateTime:null, zigbeeId:000D6F000AFF715D, locationId:1, id:1, dataSorted:[hardwareID:000D], hardwareID:000D, createTime:2018-02-21T10:03:28+0000, localSrvPortTCP:39501, name:home, firmwareVersionString:2.1.6.118, type:PHYSICAL, localIP:192.168.1.44, uptime:2661, version:1, data:[hardwareID:000D, zigbeeEui:000D6F000AFF715D, zigbeeChannel:0x14 (20), zigbeePanID:B8FD, localSrvPortTCP:39501, localIP:192.168.1.44], zigbeeEui:000D6F000AFF715D, batteryInUse:false, lastActivityTime:null], id:761, supportedCommands:[[arguments:null, parameters:null, id:8, version:1, name:configure], [arguments:null, parameters:null, id:2, version:1, name:off], [arguments:null, parameters:null, id:127, version:1, name:off], [arguments:null, parameters:null, id:1, version:1, name:on], [arguments:null, parameters:null, id:126, version:1, name:on], [arguments:null, parameters:null, id:13, version:1, name:refresh], [arguments:[NUMBER], parameters:[[name:Color temperature*, description:Color temperature in degrees Kelvin, type:NUMBER, constraints:[NUMBER]]], id:83, version:1, name:setColorTemperature], [arguments:[NUMBER, NUMBER], parameters:[[name:Level*, description:Level to set (0 to 100), type:NUMBER, constraints:[NUMBER]], [name:Duration, description:Transition duration in seconds, type:NUMBER, constraints:[NUMBER]]], id:10, version:1, name:setLevel], [arguments:[ENUM], parameters:[[name:Direction*, description:Direction for level change request, type:ENUM, constraints:[up, down]]], id:1641, version:1, name:startLevelChange], [arguments:null, parameters:null, id:1642, version:1, name:stopLevelChange]], lanId:null, endpointId:01, controllerType:ZGB, displayAsChild:true, name:Generic Zigbee CT Bulb (dev), capabilities:[[commands:[[arguments:null, parameters:null, id:1, version:null, name:on], [arguments:null, parameters:null, id:2, version:null, name:off]], id:4, version:null, attributes:[[dataType:null, values:null, id:4, version:null, possibleValues:null, name:switch]], name:Switch, reference:null], [commands:[[arguments:null, parameters:null, id:8, version:null, name:configure]], id:12, version:null, attributes:[], name:Configuration, reference:null], [commands:[[arguments:null, parameters:null, id:10, version:null, name:setLevel]], id:15, version:null, attributes:[[dataType:null, values:null, id:12, version:null, possibleValues:null, name:level]], name:SwitchLevel, reference:null], [commands:[[arguments:null, parameters:null, id:13, version:null, name:refresh]], id:18, version:null, attributes:[], name:Refresh, reference:null], [commands:[], id:35, version:null, attributes:[], name:Actuator, reference:null], [commands:[[arguments:null, parameters:null, id:83, version:null, name:setColorTemperature]], id:53, version:null, attributes:[[dataType:null, values:null, id:70, version:null, possibleValues:null, name:colorTemperature], [dataType:null, values:null, id:11571, version:null, possibleValues:null, name:colorName]], name:ColorTemperature, reference:null], [commands:[[arguments:null, parameters:null, id:126, version:null, name:on], [arguments:null, parameters:null, id:127, version:null, name:off]], id:80, version:null, attributes:[[dataType:null, values:null, id:104, version:null, possibleValues:null, name:switch]], name:Light, reference:null], [commands:[[arguments:null, parameters:null, id:1641, version:null, name:startLevelChange], [arguments:null, parameters:null, id:1642, version:null, name:stopLevelChange]], id:89, version:null, attributes:[], name:ChangeLevel, reference:null]], device:[currentStates:[level:[floatValue:49.0, jsonValue:49, value:49, dataType:NUMBER, unit:null, date:2019-12-03T00:01:32+0000, stringValue:49, id:null, numberValue:49, doubleValue:49.0, name:level]], locationName:home, updateTime:2018-07-13T03:55:00+0000, hubName:home, isComponent:false, zigbeeId:B0CE18140301ABC0, deviceTypeName:Generic Zigbee CT Bulb (dev), hub:[updateTime:null, zigbeeId:000D6F000AFF715D, locationId:1, id:1, dataSorted:[hardwareID:000D], hardwareID:000D, createTime:2018-02-21T10:03:28+0000, localSrvPortTCP:39501, name:home, firmwareVersionString:2.1.6.118, type:PHYSICAL, localIP:192.168.1.44, uptime:2661, version:1, data:[hardwareID:000D, zigbeeEui:000D6F000AFF715D, zigbeeChannel:0x14 (20), zigbeePanID:B8FD, localSrvPortTCP:39501, localIP:192.168.1.44], zigbeeEui:000D6F000AFF715D, batteryInUse:false, lastActivityTime:null], locationId:1, id:761, displayAttributes:null, groupId:null, currentStatesSorted:[level:[floatValue:49.0, jsonValue:49, value:49, dataType:NUMBER, unit:null, date:2019-12-03T00:01:32+0000, stringValue:49, id:null, numberValue:49, doubleValue:49.0, name:level]], lanId:null, hubId:1, endpointId:01, createTime:2018-07-13T03:54:00+0000, controllerType:ZGB, displayAsChild:true, name:Generic Zigbee CT Bulb (dev), parentAppId:null, disabled:false, status:ACTIVE, groupName:null, version:3, driverType:sys, label:dW OF LI 3, data:[application:01, model:Z01-A19NAE26, manufacturer:sengled], deviceNetworkId:1A66, deviceTypeId:193, lastActivityTime:2019-12-03T00:01:32+0000, parentDeviceId:null, displayName:dW OF LI 3], idAsLong:761, parentAppId:null, disabled:false, status:ACTIVE, typeName:Generic Zigbee CT Bulb (dev), driverType:sys, label:dW OF LI 3, data:[application:01, model:Z01-A19NAE26, manufacturer:sengled], supportedAttributes:[[dataType:STRING, values:null, id:11571, version:1, possibleValues:null, name:colorName], [dataType:STRING, values:null, id:14109, version:1, possibleValues:null, name:colorName], [dataType:NUMBER, values:null, id:70, version:1, possibleValues:null, name:colorTemperature], [dataType:NUMBER, values:null, id:12, version:1, possibleValues:null, name:level], [dataType:ENUM, values:[on, off], id:4, version:1, possibleValues:[on, off], name:switch], [dataType:ENUM, values:[on, off], id:104, version:1, possibleValues:[on, off], name:switch]], deviceNetworkId:1A66, lastActivity:2019-12-03T00:01:32+0000, parentDeviceId:null, displayName:dW OF LI 3] | cmd: setLevel | p1: 0 | p2: 3

You must use screenshots, not copy/paste. That's some sort of map, such as a full device wrapper.

It's been 8 months, so I don't recall the post I made, or if it is right or not.

added another log.debug right before runInMillis is called:

log.debug "dev: $dev | cmd: $command | p1: $param1 | p2: $param2"
runInMillis(delayMS, runDeviceCmd, [data: [dev: dev, cmd: command, param1: param1, param2: param2]])

here is the output:

app:2802019-12-02 04:11:07.321 pm debugdev: dW OF LI 1 | cmd: setLevel | p1: 0 | p2: 3

looks like runDeviceCmd is being sent a map with all device attributes even though deviceCmdDelay specified a device wrapper in scheduling the call to runDeviceCmd via runInMillis.

You don't want to pass a device wrapper into a run in...

right. i was trying to follow the example here:

really just trying to figure out how to use device.command with delay on HE.

thanks!

I have a related question. and I apologize if I'm using the wrong terms. Remember...I'm totally self taught for anyting code related. So please be patient with me.

Do you mean only if they are selected from an input and are the only "memeber" of that input? So, the input is only a single device?

To put it another way...If i have a multi-device input and I have found the correct one from the set and assigned it to a variable, can that be passed to another method or no? Do i have to pass off the way to find the device to the method that will execute the command on said device?

For example, if I do this:

def device = myDevices.find {it.deviceId == event.deviceId}

That will pull the matching device out of myDevices. Can I use runIn to pass "device" to another method as part of the data map? Or will it be lost when I try to do that? So far I can't tell if it's the way I'm setting it up or if it's just not supposed to work. So, thought I'd ask before pulling my hair out any further.

Thanks a bunch!!!!

Yes, as long as myDevices came from an input selection.

Well, myDevices in this case would have been the input selection of multiple devices. Okay, I'll have to take a closer look and see what I was doing wrong. Maybe it's how I was setting up the mapped data. Thanks Bruce.

Okay Bruce, I must be doing something wrong. I am finding the device out of the input myDevices...that is working perfectly.

def device = myDevices.find{it.label == name}
log.debug device.label
device.on()
runIn(time, turnOff, [data:device])

So, the device turns on correctly. But then I try to do this:

def turnOff(device){
    device.off()
}

And that gives me this error:

I thought you said I could pass a device variable off to another method as long as it was part of an input. Did i miss-understand you? Or was I not stating my question clearly?

Basically, I have gone through the steps necessary to identify which device i need to use. Do i have to do that again in the method called to by runIn? To be clear, device is not an input but it is part of the multi-device input of myDevices. It probably is a lot clearer what I'm asking now that I've given you an example.

I also tried to save the device is a state variable....boy, that doesn't work And talk about a total mess!! Learned that one the hard way. :smiley:

Thank you!

As you've helped me before, I will try to return the favor. Judging from the error message, the device is stored as a json object when scheduling with runIn. A possible approach would be to pass the device id or network id instead of the whole device object, and in the turnOff function retrieve the device from myDevices using the ID.

Yes, I know that. If you read the conversation that Bruce and I were having above, I asked if that was the only method. He said I can just pass the device. When I tried it, I couldn't get it to work so I was asking him what I was doing wrong, since he said it was possible.

@bravenel, nothing huh? You said I could pass a device variable to another method with runIn but it doesn't appear to be working. Please let me know what I am doing wrong. Thanks.