Tutorial: Creating a REST SmartApp Endpoint


(Ben Edwards) #1

SmartApp endpoints are a powerful way to securely control your SmartThings with familiar REST calls GET,POST,PUT or DELETE. You have the freedom to define your URL schema and can pass query values through the URL path or through JSON.

This tutorial will guide you through the process of setting up this App Endpoint Example. After you authorize the app to see specific devices, it will generate a web page of buttons with URLs to toggle your chosen lights and locks.

You can download the full code for the SmartApp Endpoint Example and PHP code to interact with the endpoint. Additional OAuth documentation can be found here.

Step 1: Enable OAuth in the SmartApp

To begin, add a New SmartApp in the IDE. Make sure to click the button to “Enable Oauth in Smart App.” Note the “OAuth Client ID” and the “OAuth Client Secret.” We will use these later to generate our interface page. After entering the other required fields, click “Create” and continue on to coding the app.

Step 2: Configuring the Endpoint Mapping

When coding, you’ll add “mappings” to your app following the preferences section.
Withing the mappings, we define the URL paths that map to our desired commands.
REST verbs further specify the SmartApp functions that specific URLs will trigger.
For example, in the mapping below, when the path “/switches” is hit with a GET request, the function listSwitches is called.

In a similar fashion, if we pass an id number and a command in the path “/switches/09f46871-dc7c-45d0-8f51-2c6d352bf8cz/on” we will trigger “updateSwitch” to execute the “on” command for the switch with id “09f46871-dc7c-45d0-8f51-2c6d352bf8cz”.

mappings {
    path("/switches") {
        action: [
            GET: "listSwitches"
        ]
    }

    path("/switches/:id/:command") {
        action: [
            GET: "updateSwitch"
        ]
    }
    ...
}

Step 3: Passing Variables

We access the variables in the path via “params.” As shown below, we retrieve the id and command as params.command and params.id.

We then call the passed command on the specified id. As easily as that - with a little bit of error checking.

void updateSwitch() {
    update(switches)
}
private void update(devices) {
    log.debug "update, request: params: ${params}, devices: $devices.id"
    def command = params.command
    def device = devices.find { it.id == params.id }

    //let's create a toggle option here
    if (command){
        if (!device) {
            httpError(404, "Device not found")
        } else {
            if(command == "toggle")
            {
                if(device.currentValue('switch') == "on")
                  device.off();
                else
                  device.on();
            }
            else
            {
                device."$command"()
            }
        }
    }
}

As an alternative to passing the variables in the URL path, we could have sent a json string and used it in this manner.

def command = request.JSON?.command

Step 4: Going through the Oauth Process to Interact with the Endpoint

Now, let’s assume you’ve installed the full demo code and hit publish in the IDE.

To interact with the endpoint, first find the Client ID and Client Secret you enabled in Step 1 (you can always go back to the “App Settings” page retrieve that info).

We’re going to create a new PHP program as an example of how to interact with the app endpoint programatically. Note that the PHP code is only used to facilitate the example - no server-size code is actually needed to call our SmartApp endpoint commands.

In the PHP code:

a) The first step is to redirect to Authorize the App with the Client ID and Redirect URL specified.

header( "Location: https://graph.api.smartthings.com/oauth/authorize?response_type=code&client_id=$client&redirect_uri=".$url."&scope=app" ) ;

b) After authorization, the page will be redirected including a code in the query string. We use this code to claim our official access token. In this case, I’m using a CURL call to request the token as shown below.

$code = $_REQUEST['code'];
$page = "https://graph.api.smartthings.com/oauth/token?grant_type=authorization_code&client_id=".$client."&client_secret=".$secret."&redirect_uri=".$url."&code=".$code."&scope=app";

	$ch = curl_init();

	curl_setopt($ch, CURLOPT_URL,            $page );
	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );
	curl_setopt($ch, CURLOPT_POST,           0 );
	curl_setopt($ch, CURLOPT_HTTPHEADER,     array('Content-Type: application/json'));

	$response =  json_decode(curl_exec($ch),true);

	curl_close($ch);
        $accessToken = $response['access_token']

//In the full example, I redirect back to the PHP page at this point with the access token in the query string so that the URL can be more easily stored for later visits.

c) We then use our access token to collect the full URLs of our endpoints.

$url = “https://graph.api.smartthings.com/api/smartapps/endpoints/$client?access_token=”.$_REQUEST[‘access_token’];
$json = implode(’’, file($url));
$theEndpoints = json_decode($json,true);

d) Finally, we generate a page building out some possible URLs to send commands to our devices.

    foreach($theEndpoints as $k => $v)
    {

        $switchUrl = "https://graph.api.smartthings.com".$v['url']."/switches";
        $access_key = $_REQUEST['access_token'];

        $ch = curl_init($switchUrl);
        curl_setopt( $ch, CURLOPT_HTTPHEADER, array( 'Authorization: Bearer ' . $access_key ) );
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1 );
        curl_setopt($ch, CURLOPT_POST,           0 );

        $resp =  curl_exec($ch);
        curl_close($ch);

        $respData = json_decode($resp,true);

        if(count($respData) > 0) print "<h2>Switches</h2>";

        //let's show each of the switches
        foreach($respData as $i => $switch)
        {
            $label = $switch['label'] != "" ? $switch['label'] : "Unlabeled Switch";

            print " <h3>$label</h3>";

            $onUrl = "https://graph.api.smartthings.com".$v['url']."/switches/".$switch['id']."/on?access_token=".$_REQUEST['access_token'];
            print "<a target='cmd' href='$onUrl'>On</a>";

            $offUrl = "https://graph.api.smartthings.com".$v['url']."/switches/".$switch['id']."/off?access_token=".$_REQUEST['access_token'];
            print "<a  target='cmd' href='$offUrl' value='Off'>Off</a>";

            $toggleUrl = "https://graph.api.smartthings.com".$v['url']."/switches/".$switch['id']."/toggle?access_token=".$_REQUEST['access_token'];
            print "<a target='cmd' href='$toggleUrl'>Toggle</a><BR>";

        }
}

Step 5: Putting it all Together

You can run the full code from here in your browser - smartApps with endpoints are installed into your account once you authorize them to access specific devices. See links to download the full source code for the example at the top of the article.

Also, check out this more advanced project that allows the Leap Motion to control light switches through this app endpoint video here:


Amazon Echo Developers Access
IFTTT and SmartThings (UK accounts)
X10 Remotes & Sensors with Smartthings using mochad
Using X10 Remote Control with SmartThings
Newbie, Execute a Routine or Device action via URL?
Presence Detection using DD-WRT Router Script
Rest Endpoint Setup
DIY Mac Desktop Shortcuts (alias) to Switch/Lights
Undocking iPhone to 'wake up house'
Creating a PHP?
Is There A Building REST Handler Tutorial?
Flic - Wireless Smart Button
Send Events to EventGhost
Send Events to EventGhost
What's the current favourite way of providing connectivity?
Tasker and SmartThings (reposting of project from build site)
Tasker and SmartThings (reposting of project from build site)
Integration with AT&T digital life
SmartThings Labs and LAN Devices
Hello Actions in REST Endpoint
Can I use an Aeons Lab Z-Stick in addition to my ST hub?
Tasker to control modes?
Home Automation Dashboard (HAD)
Presence Sensor based on pings
API and Endpoints
Controlling devices from mobile
Invisible REST SmartApp Tutorial app
Smart Alarm is here
ActiON Dashboard 4.6.3 is here! (Now SmartTiles.click)
Virtual switch to change hello, home mode
Presence Detection using DD-WRT Router Script
Need help setting up Endpoints
Playing around with Amazon Echo (technical interface discussion)
Playing around with Amazon Echo (technical interface discussion)
Playing around with Amazon Echo (technical interface discussion)
Problem implementing endpoint OAuth
ST and BlueIris with Subnets and VLans
Kodi/XBMC light control - a (relatively) simple how-to
Received Echo(Alexa) developer access
(DLee) #2

Just started to look at this. The video is still marked as private. Can you share it to public?


(Ben Edwards) #3

Updated. And also here:


DSC Alarms Server
(Dave) #4

Just wondering whether you can comment on the security of this solution? I installed it today and set up a few test devices, including my garage door. While I was away, I saw an alert that the garage door opened, and figured it was my wife coming home. When I came home an hour later, no one was here and the garage door was still open. I checked the device log for the garage door, and it says that, “Endpoint Example sent on command to Garage Door Switch,” at the time I saw the door open. I’m using the garage door endpoint URL in an app on my iPhone that lets me trigger the command from my Pebble smart watch, so this may have just been a glitch in that app. But I’m wondering whether anyone other than me can find and use the page that’s generated by Endpoint Example? Thanks.


(Sim Harbert) #5

Is it necessary to set up the PHP site? What I want are some URLs to use to control lights or devices from Tasker. Isn’t the PHP code just doing that in this example?

I have installed ActiON4 that even runs the web-pages in Groovy, and it seems to work pretty well.


Simple network commands to integrate into IR remote Android app
(Jeremy Shimko) #6

It’s not the most robust example, but this part of the docs covers everything you need to know to make an HTTP API access smart app. Definitely not intended for non-developers, but it covers exactly what you’re trying to do.

To see a working HTTP API smart app example, look at this Groovy code for this dashboard app. (See the project documentation for more details)

The PHP part that you’re referring to is just the remote side that communicates with SmartThings (Tasker is that part in your case - although you still need to sort out OAuth authentication). You could implement that with any client and/or server side code that is capable of making HTTP calls and handling OAuth authentication.


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

@noisygecko I’m pretty sure one of my friends mentioned the app SharpTools which provides Tasker plugins (as well as Android widgets) for SmartThings in another thread of yours. In that thread, you mentioned the following:

I want Tasker to be able to trigger the SmartThings app to do things that I can do through the app, like switch on a light, or change the temperature.

This is one of the main things SharpTools does - it provides straight forward and easy to use Tasker plugins that allow you to control your Things and execute Phrases. This means you can easily setup a Tasker action to turn on a light or control the temperature of your HVAC. Check out the list of features here:

As @Jeremy and others have pointed out, you can absolutely work through the OAuth, Groovy, etc. with the help of the community, but SharpTools takes care of all the heavy lifting and lets you focus on the fun of setting up the Tasker integration.


(Joe Rizzo) #8

Is there a way to implement an endpoint that can control devices at different locations owned by different accounts without the need for each location/user to publish the endpoint?


(Ron) #9

Did anyone every get a similar setup working ? I have an endpoint and a Spring Boot app which is trying to implemented this example. Steps 1 and 2 work just fine. But step three which is supposed to return a list of endpoints and a list of switches granted permission does not work. The http get calls I make which match the code in the php example just return [ ]

No way to debug since it does work it just returns no data.

Does anyone else have a working example ?

I see @Ben has never replied to anyone’s question on this thread so perhaps this thread is just old and this doesn’t work this way anymore. No way to know but the samples implemented for this thread seem to still work. But can’t see the actual code behind the links so perhaps it was modified without updating this thread ?


(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy; NOT a SmartThings Employee.) #10

There are tons of real-Apps in the wild, so finding “examples” in some language should be “easy”…

I’m not speaking from much personal experience, though, but from a position of wanting to grasp this quickly as well – perhaps to do some very basic Echo integration.

Maybe we should do some 1-1 work on this, since we’ve starting at the same level? Or would that be like blind-leading-blind?


(Ron) #11

I have figure this out. It was painful not being a php programmer I missed a lot of what was being done in the example and had to look at the code more closely at each step. But I have it all working in Java Spring Boot application now.


(Jim Anderson) #12

A very simple example of controlling a switch with an endpoint can be found here: https://github.com/SmartThingsCommunity/Code/blob/master/smartapps/workshops/02-control-devices-with-an-HTTP-request.groovy

If you install it in the IDE (make sure to edit the app settings and enable OAuth first!), you can then invoke it via simple curl commands (or Apigee, or whatever language you choose). Just grab the API token and API endpoint from the simulator and plug it in.

Get switch status:

curl -H "Authorization: Bearer <api token>" <api endpoint>/switch

Turn switch on:

curl -H "Authorization: Bearer <api token>" -H "Content-type: application/json" -X PUT -d '{"value": "on"}' <api endpoint>/switch

Turn switch off:

curl -H "Authorization: Bearer <api token>" -H "Content-type: application/json" -X PUT -d '{"value": "off"}' <api endpoint>/switch


(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy; NOT a SmartThings Employee.) #13

Congrats! I’d love to see the results.


#14

So I’ve been struggling with this, and finally figured it out…
I can now control the switches via the php page…

My question is: How can I control a dimmer switch and set the levels?

I’ve modified the groovy code by copy/pasting the relevant code and tweaking it as I believe it needs to be, but I’m lost on doing anything in the PHP code.

My goal is to set the dimmers to a pre-determined level when the URL is called by the PHP or whatever other app I am using.

I’m not a developer and have no background in coding.

Help?


#15

Hi,

I am getting “Metadata definition not found” when trying to add the smartapp. What am I doing wrong? :smile:


(Tim Slagle) #16

Make sure your meta data section has all the info it needs.

It sounds like you may not have a meta data section defined.

http://docs.smartthings.com/en/latest/ref-docs/device-handler-ref.html#metadata


(Jim Anderson) #17

I think the example you are following is pretty old and outdated. It also doesn’t contain a metadata declaration, so as you see… it won’t work.

You should check out the web services SmartApp docs here. It also includes a walkthrough tutorial of the example found here.


#18

Thanks! Got the example to work! But I’m only able to turn all switches on and off. Is there a example were you can query each device separately? Tried modifying the code with no success…


(ActionTiles.com co-founder Terry @ActionTiles; GitHub: @cosmicpuppy; NOT a SmartThings Employee.) #19

Share your attempt by linking to GitHub or at least pasting a big code chunk here (surround with three-backquotes or highlight and press the </> formatting button…?

You can private message me if you wish, but perhaps you are really close.


#20

This code is the one I use:

/**

  • Web Services Tutorial
  • Copyright 2015 SmartThings
  • Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except
  • in compliance with the License. You may obtain a copy of the License at:
  •  http://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
  • on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
  • for the specific language governing permissions and limitations under the License.

*/
definition(
name: “Web Services Tutorial”,
namespace: “smartthings”,
author: “SmartThings”,
description: “web services tutorial”,
category: “”,
iconUrl: “https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png”,
iconX2Url: “https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png”,
iconX3Url: “https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png”,
oauth: [displayName: "web services tutorial ", displayLink: “http://localhost:4567”])

preferences {
section (“Allow external service to control these things…”) {
input “switches”, “capability.switch”, multiple: true, required: true
}
}

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

// returns a list like
// [[name: “kitchen lamp”, value: “off”], [name: “bathroom”, value: “on”]]
def listSwitches() {

def resp = []
switches.each {
    resp << [name: it.displayName, value: it.currentValue("switch")]
}
return resp

}

void updateSwitches() {
// use the built-in request object to get the command parameter
def command = params.command

if (command) {
    // check that the switch supports the specified command
    // If not, return an error using httpError, providing a HTTP status code.
    switches.each {
        if (!it.hasCommand(command)) {
            httpError(501, "$command is not a valid command for all switches specified")
        } 
    }
    
    // all switches have the comand
    // execute the command on all switches
    // (note we can do this on the array - the command will be invoked on every element
    switches."$command"()
}

}
def installed() {}

def updated() {}