Using MODE API for Devices

As explained earlier, in a MODE-based system, hardware devices communicate with apps and Smart Modules via events (from devices to the MODE cloud) and commands (from the MODE cloud to devices). This document explains the endpoints in the MODE API that your device software will most likely use.

The MODE API is an HTTP-based, RESTful API, hosted at api.tinkermode.com. You can use any HTTP library in your favorite programming language. Or you can make raw HTTP requests through a TCP connection. In this document, we will show the examples as cURL commands. The cURL program is installed by default on most Unix-like environments such as Linux and Mac OS X.

Note that MODE also provides device client libraries for building device software on some of the most common hardware platforms. See the SDKs page for details.

(It is also possible to connect your devices to MODE via MQTT. For details, see this article.)

Device Provisioning

Before a device becomes fully operational as a connected node of the system, two things need to happen first:

  1. The device is in possession of the unique device ID and API key assigned by MODE.
  2. The device has been added to the home of an end user.

MODE supports two different mechanisms for your project to provision devices. You should select the option that best suits your device’s capabilities and your system’s user experience.

Option 1: Device Pre-Provisioning

This is the default mechanism. In this mechanism, you create device instances in the MODE database (via the MODE Developer Console) ahead of time. As you build your device hardware, you copy/flash the assigned device ID and API key to it.

When an end user wants to take a device into her home, she essentially needs to “claim” the ownership of the device and link it to her home. Here is how it is done:

As a first step, the device must make a POST request to the /deviceRegistration endpoint to turn on its claim mode. In the following example, a device’s claim mode will be activated for 300 seconds. (Replace _DEVICE_ID_ and _YOUR_API_KEY_ with the actual device ID and API key.)

$ curl -i -N -H "Authorization: ModeCloud _YOUR_API_KEY_" --data "deviceId=_DEVICE_ID_&claimable=1&duration=300" https://api.tinkermode.com/deviceRegistration 

The raw HTTP request is as follows:

POST /deviceRegistration HTTP/1.1
Host: api.tinkermode.com
Authorization: ModeCloud _YOUR_API_KEY_
Content-Type: application/x-www-form-urlencoded

deviceId=_DEVICE_ID_&claimable=1&duration=300

If you succeed, you will see a response similar to the following:

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: false
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Xsrf-Guard
Content-Type: application/json; charset=utf-8
Date: Tue, 25 Aug 2015 21:39:37 GMT

{"claimExpirationTime":"2015-08-25T21:44:37.744323372Z"}

The claim mode will expire at the time specified by the claimExpirationTime property of the returned JSON object. You have to finish the device registration process before claim mode expires.

If you passed the wrong device ID, you would see:

HTTP/1.1 403 Forbidden
Access-Control-Allow-Credentials: false
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Xsrf-Guard
Content-Type: application/json; charset=utf-8
Date: Tue, 25 Aug 2015 21:40:24 GMT

{"reason":"MATCHING_DEVICE_ONLY"}

By the way, this is the same the process described here. In the Device Simulator, the Claim Mode button makes this API call.

Now that the device’s claim mode is turned on, you should make a POST request to the /devices endpoint to complete the registration process. This API request is supposed to be called by an app. For testing you can use the App Simulator for this step, as described here. Copy and paste the device claim code into the App Simulator, and the device is now added to a home and ready to send events and receive commands.

Claiming Pre-Provisioned Device

Option 2: On-demand Device Provisioning

NOTE: An important change was made to On-demand Device Provisioning on August 17, 2016. See the announcement.

In this mechanism, there is no need to create device instances in the MODE database ahead of time. Instead, a new device entry is created on demand by end users. For this provisioning method to work, the user client (usually a mobile app) must have the ability to transmit a provisioning token directly to the device hardware.

If you wish to use this mechanism to provision your devices, go to the Settings screen of your project on the Developer Console. In the Advanced Options section, enable the On-demand Device Provisioning option.

Screenshot - Project Advanced Options

To initiate device provisioning, the user client makes a POST request to the /homes/_HOME_ID_/deviceProvisioning endpoint, specifying the Device Class and an optional tag of the device to be added. You can use the App Simulator to test this step.

Screenshot - App Simulator

Screenshot - App Simulator

A provisioning token will be issued to the user client, which in turn must pass it to the actual device hardware, probably via direct WiFI, Bluetooth connection, etc. Once the device hardware gets hold of the provisioning token, it should connect to the MODE API and complete the provisioning process by making a POST request to the /devices endpoint, with a token parameter in the request body.

If the device is successfully provisioned and added to the home, the JSON returned by the request will contain the device ID and API key assigned to the device. The device should store the information in a local persistent storage and include the API key for all future communications with MODE. You may also consider programming your device to trigger an event right after the provisioning is done, so that the user client will know when to reload the list of devices in the home and notify the end user.

Provisioning Device on Demand

API Key Verification

Each HTTP request from a device to the MODE API must contain an API key in the Authorization HTTP header. You can locate the API key of your device in the MODE Developer Console. (See this tutorial for details.)

To verify that your device API key is working, run the following command (replace _YOUR_API_KEY_ with the actual API key):

$ curl -i -N -H "Authorization: ModeCloud _YOUR_API_KEY_" https://api.tinkermode.com/auth 

The raw HTTP request is as follows:

GET /auth HTTP/1.1
Host: api.tinkermode.com
Authorization: ModeCloud _YOUR_API_KEY_

If you succeed, you will see a response similar to the following:

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: false
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Xsrf-Guard
Content-Type: application/json; charset=utf-8
Date: Tue, 25 Aug 2015 21:22:49 GMT

{"deviceId":11,"projectId":11,"type":"device"}

The actual JSON object returned may vary depending on your device configuration. If the API key used was incorrect, you could see the following response:

HTTP/1.1 200 OK
Access-Control-Allow-Credentials: false
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Xsrf-Guard
Content-Type: application/json; charset=utf-8
Date: Tue, 25 Aug 2015 21:22:49 GMT

{"type":"nobody"}

Because you passed the wrong API key, the MODE cloud did not recognize the caller as a device. If you want to see the whole HTTP transaction under the hood, you can run the cURL command with the -v option.

API Keys Storage Option

While it is convenient to use MODE as the central repository for all your devices’ API keys, for added security, you may choose to store the keys separately on your own instead. To stop MODE from storing your project’s device API keys, go to Settings of your project on the Developer Console. Click Turn off Storage in the Device Keys Storage section:

Device Keys Storage

Selecting this option has two effects:

  1. All existing device keys will be purged from MODE’s database.
  2. Device keys generated from now on will not be stored in MODE’s database.

Also note that this option is irreversible. So before you selecting this option, make sure you back up all your existing device keys first. To do so, go to the console for each of your Device Classes, and click Export in the Device List section:

Export Devices

All the metadata of your device instances for that Device Class, including the API keys, will be exported as an JSON file.

Once you have opted out of storing device API keys in MODE, each time you create a new device instance, you must immediately save the generated API key in a secure place. You will not be to retrieve this key from MODE again.

Sending Events

To send an event from a device, simply make a PUT request to the /devices/_DEVICE_ID_/event endpoint. For example:

$ curl -XPUT -i -N -H "Authorization: ModeCloud _YOUR_API_KEY_" -H "Content-Type: application/json" --data "{ \"eventType\": \"foo\",  \"eventData\": {} }"  https://api.tinkermode.com/devices/_DEVICE_ID_/event

The raw HTTP request is as follows:

PUT /devices/_DEVICE_ID_/event HTTP/1.1
Host: api.tinkermode.com
Authorization: ModeCloud _YOUR_API_KEY_
Content-Type: application/json

{ "eventType": "foo",  "eventData": {} }

Please make sure that you correctly replace _YOUR_API_KEY_ and _DEVICE_ID_ according to the device settings. You can expect the following response. Make sure the HTTP status code is 204.

HTTP/1.1 204 No Content
Access-Control-Allow-Credentials: false
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: X-Xsrf-Guard
Date: Tue, 25 Aug 2015 21:50:34 GMT

You can check to see if the event has been correctly delivered by using the app simulator. On the App Simulator, open the Incoming Events window to make sure the sent event is in fact delivered to the app. You can see something like the following in the event log:

{"homeId":10,"timestamp":"2015-08-25T18:53:42.365519896Z","eventType":"foo","eventData":{},"originDeviceId":8}

The homeID, originDeviceId and timestamp properties of the returned JSON may vary depending on your configuration. If you cannot see the log, please make sure _YOUR_API_KEY_ or _DEVICE_ID_ are correct.

Receiving Commands via WebSocket

To receive commands, the device has to estable a WebSocket connection to the MODE cloud. To initiate the WebSocket connection, the device needs to make a GET request to the /devices/_DEVICE_ID_/command endpoint. Run the following command:

$ curl -i -N -H "Authorization: ModeCloud _YOUR_API_KEY_" -H "Upgrade: websocket" -H "Connection: Upgrade" -H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" -H "Sec-WebSocket-Version: 13" https://api.tinkermode.com/devices/_DEVICE_ID_/command

The raw HTTP request is as follows:

GET /devices/_DEVICE_ID_/command HTTP/1.1
Host: api.tinkermode.com
Authorization: ModeCloud _YOUR_API_KEY_
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Version: 13

You need to send the Upgrade, Connection, Sec-WebSocket-Key and Sec-WebSocket-Version headers in addition to the Authorization header to initiate a WebSocket connection. The Sec-WebSocket-Key header contains base64-encoded random bytes, and the server replies with a hash of the key in the Sec-WebSocket-Accept header. For more on WebSocket, see RFC6455.

If your request succeeds, you should see the following response. The connection is kept alive for the inbound data stream.

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

To make sure you are receiving commands, you can use the App Simulator to send some commands, as described here. If you send light as action, {"on":true} as parameters, then you will see the following output:

��+{"action":"light","parameters":{"on":true}}

You may see some strange characters before the JSON text. They are WebSocket frame headers. In the actual device software, you have to parse the headers. You can find lots of WebSocket libraries (e.g. ws, libwebsockets, etc.) in your favorite programming languages.

Some WebSocket libraries cannot accept any custom HTTP headers. In that case, you can use the authToken URL query parameter to pass the API key to the server:

$ curl -G -L -i -N -H "Upgrade: websocket" -H "Connection: Upgrade" -H "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==" -H "Sec-WebSocket-Version: 13" -d "authToken=_YOUR_API_KEY_"  "https://api.tinkermode.com/devices/__/command"

WebSocket Reconnection

Sometimes WebSocket connection could be shut down unexpectedly. For example, your Wi-Fi router could go down abruptly, the ISP and other upstream gateways could go down as well. If that happens, the device has to make the WebSocket connection again.

Normally, WebSocket libraries can detect that the connection is closed and handle errors. Whenever a device tries to reconnect, it should wait for some time before attempting the next reconnection.

The simple solution is to wait for a certain number of seconds (e.g. 10 seconds) after the first try failed. And it can keep trying to reconnect until the connection succeeds. However, this power-draining method may pose a problem to battery-powered devices.

A more sophisticated solution is to use Fibonacci numbers for the wait time. For example, after the first connection attempt failed, the device should try connecting again after 1 second. If it fails, it tries again after 2 seconds. After that, 3 seconds, 5 seconds, and so on. Obviously, the number would go to infinity, so you need to cap the number to say, 60 seconds.

WebSocket Ping Frames

The MODE cloud sends the ping control frames periodically through each WebSocket connection to prevent it from going stale. According to RFC6455 section 5.5.2, a WebSocket client has to return a pong frame back whenever a ping is received.

But even if a pong frame is not returned back to the MODE cloud, the WebSocket connection would not be shut down, because some WebSocket libraries cannot handle ping frames correctly. For example, most web browsers ignore pings and cannot send pongs back.