Passing params to REST API command


(Brandon Valentine) #1

[ Hope this is the right section of the forum, it’s a toss-up between this and Device Types. ]

Folks,

I’m looking for pointers on what the JSON object should look like in a PUT request to the REST API when the command being invoked takes an argument. As the simplest example, I have a virtual device type that implements the Switch Level capability. I can post to an API endpoint in my SmartApp this JSON object:

{ command: setLevel }

And I get the expected 204. However, I can’t seem to hit on the right way to specify the level. It also doesn’t seem to be well documented. Anybody done this and know how to format the JSON object with a parameter passed to the command?

Thanks,

Brandon


(Joe) #2

Here is a code sample I threw together (and didn’t test) that might help. It’s put together in a way that would be easy to extend to other devices/capabilities. I’m not using the request body to pass in parameters though, instead using URL parameters. I would be curious to see if anyone has a JSON request body example too.

In my example the URL would look something like:
/command?command=level&value=88&thingId=xxxxxxxxxxxxxxxxxxxx

code snippet:

preferences {
section(“Title”) {
input “dimmers”, “capability.switchLevel”, title: “Which Dimmers?”, multiple: true, required: false
}
}

mappings {
path("/command") {action: [GET: “command”]}
}

void command() {
def command = params.command
def value = params.value
def thingId = params.thingId

if (command == "level") 
    {
    device = dimmers?.find{it.id == thingId}
    if (device) 
    {
        device.setLevel(value as int)

    }
}

}


(Joshua Lyon (SharpTools.io Dashboard)) #3

Can you post the HTTP endpoint code you are trying to hit? You’ll want to make sure you are using the same HTTP verb specified in the endpoint mapping (eg. PUT vs. POST) and that the endpoint is grabbing the variable from the JSON body and attempting to use it for the command.

SmartApp:

path("/devices/:id") {
    action: [
            GET: "getDevice",
            PUT: "updateDevice"
    ]
}
...
def updateDevice() {
   def data = request.JSON
   def command = data.command
   def arguments = data.arguments

   log.debug "updateDevice, params: ${params}, request: ${data}"
   if (!command) {
   	httpError(400, "Command is required")
   } else {
	def device = allDevices.find { it.id == params.id }
	if (device) {
		if (arguments) {
			device."$command"(*arguments)
		} else {
			device."$command"()
		}
	} else {
		httpError(404, "Device not found")
	}
   }
}

(Brandon Valentine) #4

Thanks for responding! Just to get the hang of how the REST API for this platform works I’m trying to stick with known working SmartApps and Device Types, so as the simplest example of a Device Type, I’m using “Better Virtual Dimmer” by @wackware. The relevant command code in this device is:

def on() {
    log.info "on"
    sendEvent(name:"switch",value:"on")
}

def off() {
    log.info "off"
    sendEvent(name:"switch",value:"off")
}

def setLevel(val){
    log.info "setLevel $val"
    log.info "Step Size: ${state.stepsize}"
    
    // make sure we don't drive switches past allowed values (command will hang device waiting for it to
    // execute. Never commes back)
    if (val < 0){
    	val = 0
    }
    
    if( val > 100){
    	val = 100
    }
    
    if (val == 0){ // I liked that 0 = off
    	sendEvent(name:"level",value:val)
    	dimmerOff()
    }
    else
    {
    	dimmerOn()
    	sendEvent(name:"level",value:val)
    	sendEvent(name:"switch.setLevel",value:val) // had to add this to work if apps subscribed to
                                                    // setLevel event. "Dim With Me" was one.
    }
}

When I hit the REST API and enumerate the endpoints I’m using this URL:
https://graph.api.smarthings.com/api/smartapps/endpoints
That returns a list of endpoints for my devices, including this one for the Better Virtual Dimmer device I mention above:
https://graph.api.smartthings.com/api/smartapps/installations/00000000-0000-0000-0000-000000000000/switches/00000000-0000-0000-0000-000000000000

With the hashes changed to protect my account. :smile:

I can HTTP PUT these JSON payloads to the endpoint above and they work:

{ command: on }
{ command: off }

When I PUT the setLevel command by itself I get the same 204 success code, but without providing a value, the level is never set. I’ve tried things like:

{ command: setLevel, value: 20 }
{ command: setLevel, level: 20 }

etc. I can’t see into the part of the code that processes API events for custom Device Types AFAIK, so I don’t know how it parses the incoming JSON object before calling the setLevel() method in my custom device type. If I use the web IDE’s Live Logging feature, and send my first JSON example above, I see these log messages:

11:47:31 AM: debug update, request: [value:20, command:setLevel], params: [appId:00000000-0000-0000-0000-000000000000, param1:switches, param2:00000000-0000-0000-0000-000000000000, action:[GET:executeSmartAppGet, POST:executeSmartAppPost, PUT:executeSmartAppPut, DELETE:executeSmartAppDelete, OPTIONS:executeSmartAppOptions], controller:smartAppApi, id:00000000-0000-0000-0000-000000000000], devices: [00000000-0000-0000-0000-000000000000]
11:47:31 AM: error org.codehaus.groovy.runtime.metaclass.MethodSelectionException: Could not find which method setLevel() to invoke from this list:
public java.lang.Object script1425577651409864205761#setLevel(java.lang.Object)
public java.lang.Object script1425577651409864205761#setLevel(java.lang.Object, java.lang.Object) @ line 97

Any ideas what I’m missing?

Thanks,

Brandon


(Bobfrankston) #5

As am FYI I started a separate thread to advocate native support for a Restful API rather than trying to figure all this out within the current automation engine.


#6

Hi Joshua,

I’m trying to craft an http request that works with your updateDevice() method above but am not having much luck. Could you provide an example of one?

This is my mapping:

mappings {
       path("/switches/:command") {
         action: [
     		PUT: "updateDevice"
         ]
       }
}

…and sample command that doesn’t work:

…00000000/switches/on?command=switch&value=on&id=kitchen"

it bombs out with the 400 error.


(Joshua Lyon (SharpTools.io Dashboard)) #7

In my example above, you pass the device ID in the URL and then the command as JSON in the body of the POST.