Calling Web Services

SmartApps or device handlers may need to make calls to external web services. There are several APIs available to you to handle making these requests.

The various APIs are named for the underlying HTTP method they will use. httpGet() makes an HTTP GET request, for example.

Important

Requests are limited to ten seconds. If the request takes longer than that, it will timeout.

The following methods are available for making HTTP requests. You can read more about each of them in the SmartApp API documentation.

Method Description
httpDelete() Executes an HTTP DELETE request
httpGet() Executes an HTTP GET request
httpHead() Executes an HTTP HEAD request
httpPost() Executes an HTTP POST request
httpPostJson() Executes an HTTP POST request with JSON Content-Type
httpPutJson() Executes an HTTP PUT request with JSON Content-Type

Here’s a simple example of making an HTTP GET request:

def params = [
    uri: "http://httpbin.org",
    path: "/get"
]

try {
    httpGet(params) { resp ->
        resp.headers.each {
           log.debug "${it.name} : ${it.value}"
        }
        log.debug "response contentType: ${resp.contentType}"
        log.debug "response data: ${resp.data}"
    }
} catch (e) {
    log.error "something went wrong: $e"
}

Configuring The Request

The various APIs for making HTTP requests all accept a map of parameters that define various information about the request:

Parameter Description
uri Either a URI or URL of of the endpoint to make a request from.
path Request path that is merged with the URI.
query Map of URL query parameters.
headers Map of HTTP headers.
contentType Request content type and Accept header.
requestContentType Content type for the request, if it is different from the expected response content-type.
body Request body that will be encoded based on the given contentType.

Note

Specifying a reqeustContentType may override the default behavior of the various http API you are calling. For example, httpPostJson() sets the requestContentType to "application/json" by default.

Handling The Response

The HTTP APIs accept a closure that will be called with the response information from the reqeust.

The closure is passed an instance of a HttpResponseDecorator. You can inspect this object to get information about the response.

Here’s an example of getting various response information:

def params = [
    uri: "http://httpbin.org",
    path: "/get"
]

try {
    httpGet(params) { resp ->
        // iterate all the headers
        // each header has a name and a value
        resp.headers.each {
           log.debug "${it.name} : ${it.value}"
        }

        // get an array of all headers with the specified key
        def theHeaders = resp.getHeaders("Content-Length")

        // get the contentType of the response
        log.debug "response contentType: ${resp.contentType}"

        // get the status code of the response
        log.debug "response status code: ${resp.status}"

        // get the data from the response body
        log.debug "response data: ${resp.data}"
    }
} catch (e) {
    log.error "something went wrong: $e"
}

Tip

Any ‘failed’ response response will generate an exception, so you should wrap your calls in a try/catch block.

If the response returns JSON, data will be in a map-like structure that allows you to easily access the response data:

def makeJSONWeatherRequest() {
    def params = [
        uri:  'http://api.openweathermap.org/data/2.5/',
        path: 'weather',
        contentType: 'application/json',
        query: [q:'Minneapolis', mode: 'json']
    ]
    try {
        httpGet(params) {resp ->
            log.debug "resp data: ${resp.data}"
            log.debug "humidity: ${resp.data.main.humidity}"
        }
    } catch (e) {
        log.error "error: $e"
    }
}

The resp.data from the request above would look like this (indented for readability):

resp data: [id:5037649, dt:1432752405, clouds:[all:0],
    coord:[lon:-93.26, lat:44.98], wind:[speed:4.26, deg:233.507],
    cod:200, sys:[message:0.012, sunset:1432777690, sunrise:1432722741,
        country:US],
    name:Minneapolis, base:stations,
    weather:[[id:800, icon:01d, description:Sky is Clear, main:Clear]],
    main:[humidity:73, pressure:993.79, temp_max:298.696, sea_level:1026.82,
        temp_min:298.696, temp:298.696, grnd_level:993.79]]

We can easily get the humidity from this data structure as shown above:

resp.data.main.humidity

Try It Out

If you’re interested in experimenting with the various HTTP APIs, there are a few tools you can use to try out the APIs without signing up for any API keys.

You can use httpbin.org to test making simple requests. The httpGet() example above uses it.

For testing POST requests, you can use PostCatcher. You can generate a target URL and then inspect the contents of the request. Here’s an example using httpPostJson():

def params = [
    uri: "http://postcatcher.in/catchers/<yourUniquePath>",
    body: [
        param1: [subparam1: "subparam 1 value",
                 subparam2: "subparam2 value"],
        param2: "param2 value"
    ]
]

try {
    httpPostJson(params) { resp ->
        resp.headers.each {
            log.debug "${it.name} : ${it.value}"
        }
        log.debug "response contentType: ${resp.    contentType}"
    }
} catch (e) {
    log.debug "something went wrong: $e"
}

See Also

A simple example using httpGet that connects a SmartSense Temp/Humidity to your Weather Underground personal weather station can be found here.

You can browse some templates in the IDE that use the various HTTP APIs. The Ecobee Service Manager is an example that uses both httpGet() and httpPost().