Access Device Methods inside runIn()

Hi all,

I was wondering if someone could help me out here.

Here’s my code snippet :

def motionInactiveHandler(evt) {
    log.debug "motionInactiveHandler called: $evt"
    
    def device = evt.device
    
    log.debug "Device data : $device"
    def motionState = device.currentState("motion")
    log.debug "Motion State Value : ${motionState.value}"
    log.debug "Motion State Time : ${motionState.date.time}"
    
    runIn(inActivityTime, checkMotion, [data: [triggerDevice: device]])
}

def checkMotion(data) {
    log.debug "In checkMotion scheduled method"
    
    log.debug "Device data : ${data.triggerDevice}"
	def motionState = data.triggerDevice.currentState("motion")
    log.debug "Motion State Value : ${motionState.value}"
    log.debug "Motion State Time : ${motionState.date.time}"

In the above, I see the device.currentState("motion") working inside motionInactiveHandler(). But data.triggerDevice.currentState("motion") doesn’t work inside checkMotion().

I end up getting the following error :

groovy.lang.MissingMethodException: No signature of method: org.codehaus.groovy.grails.web.json.JSONObject.currentState() is applicable for argument types: (java.lang.String) values: [motion]

Any help in identifying how I can make this work would be really nice !!

Cheers,
Santhosh

I’m just tossing out something to try, but I don’t know if it will help. Keep in mind that SmartThings has a hidden layer or two between your code and what actually gets compiled an executed. That’s why what looks like perfect Groovy syntax sometimes doesn’t work. There are hidden object conversions going on.

Reference: http://docs.smartthings.com/en/latest/ref-docs/device-ref.html#current-uppercase-attribute-name

And perhaps try: triggerDevice.currentMotion

Since the runIn data is json, it looks like the device is being converted to json, resulting in your error. I think you need to pass the device id and then use find on your device list in checkMotiin to get back to the device object.

1 Like

Thanks a lot for the reply tgauchat ! But that didn’t help either.

http://docs.smartthings.com/en/latest/ref-docs/device-ref.html#current-uppercase-attribute-name

I actually did end up using the above and the following to end up with what I currently have (which doesn’t seem to work) :
http://docs.smartthings.com/en/latest/ref-docs/device-ref.html#current-uppercase-attribute-name
http://docs.smartthings.com/en/latest/smartapp-developers-guide/scheduling.html?#passing-data-to-the-handler-method

Aaaahhh !! That actually makes sense !

Couple of questions :

  1. How would I find the type of the object passed in the method ? I’m unable to use class and getClass (as mentioned in the guide) as they throw Security Exception.
  2. Could you point me to the documentation on how to do this ? I found this thread that talks about it. So I assumed this documentation page should have it. But I don’t see it there !

Again, Thanks a ton for helping out Tony !

For #1, have a go at using instanceof to check the class type. This feature is allowed on the ST platform.

if (UnknownClass instanceof ClassToCheckAgainst)
{
   ... do something
}

It probably isn’t in the documentation, but reviewing some of the example device drivers might help.

You haven’t provided enough context for me to give you the exact code you will need, but I will assume you have a list of devices in settings that includes the device you received the event for.

Given the deviceId, you can do something like:
def device = settings.deviceList.find{it.id==deviceId}

1 Like

@SteveWhite @TonyFleisher Thank you for your responses !

About #1, the instanceof helped :slight_smile:
About #2, my bad for not providing the right context here, Tony. Here are my code snippets :

preferences {
	// Motion Sensor Settings
    section("Room-1 Motion Sensor") {
        input "motionSensorRoom1", "capability.motionSensor", required: true, title: "Select sensor : "
    }

	// Motion Sensor InActivity Time
    section("Turn off when there's been no movement for selected Time") {
        input "inActivityTime", "number", required: true, title: "InActivity Time in Seconds :"
    }
}  

def motionInactiveHandler(evt) {
	def triggerDevice = evt.getDevice()
    log.debug "motionInactiveHandler called: $evt by device : ${triggerDevice.displayName}"
    
    def testDev = settings.deviceList.find{it.id == triggerDevice.getId()}
    log.debug "Testing if this works : settings : $settings"
    log.debug "Testing if this works : devList : ${settings.deviceList}"
    log.debug "Testing if this works : devices : $testDev"

    ...

Apologize if this is a noob question @TonyFleisher. But I’m unable to make that work too :confused:
I get the following error :

2:23:04 PM: debug Testing if this works : devices : null
2:23:04 PM: debug Testing if this works : devList : null
2:23:04 PM: debug Testing if this works : settings : [motionSensorRoom1:motionSensorRoom1, inActivityTime:2]

What am I missing here ?

1 Like

If in doubt, it doesn’t hurt to link to a Gist or Git of the entire SmartApp. That way some folks sometimes actually copy and paste into their own environments to play around and possibly discover a solution :wink:.

This may help to visualize what is being compared.

def motionInactiveHandler(evt) 
    	{
    	def triggerDevice = evt.getDevice()
        log.debug "motionInactiveHandler called: $evt by device : ${triggerDevice.displayName}"
    	settings.deviceList.each
    		{
    		log.debug "{it.id}  {it.displayName}"
    		if (it.id == triggerDevice.getId())
    			{
    			log.debug "Found"
    			}
    		}
    	}

It looks like you have just one motion sensor (no device list), so you don’t have a list for find, but you don’t need to pass anything to checkMotion in this case, as you can get the device direct from the settings with device = settings.motionSensorRoom1

If you had multiple devices using this handler or scheduled method, the id can be used to identify which one triggered the events.

1 Like

Thanks to all for chiming in !!

I’m gonna take up @tgauchat 's advice. Here’s the github link to what I’ve been working with so far.

I’ve taken all your inputs and come up with what I have so far. Huge thanks to you all !

Note : As you can see, I chucked the runIn() approach in favor of the sleep x; handlerMethod() approach. This was due to two reasons :

  1. With runIn(time, handlerMethod), I was observing that at times, the handler method was sometimes called a few seconds before t=time too, which was messing with my logic ! I believe the documentation also agrees with this observation.
  2. runIn(time, handlerMethod, [data : [key: value]]) is mangling the type of value.

P.S. Next steps are to get the state storing to roomOccupancies working, and then to expose that status via an API. Hopefully I can get that done :slight_smile:

1 Like