Develop a device plugin

This section shows how to create a simple web app plugin that displays and controls the temperature of an air conditioner. By following the step-by-step guide, you can create a plugin that uses the core SmartThings API functionality. You can get a reference to the device, monitor its parameters, and control the device by using callbacks and URI communication.

Checklist

Before creating a plugin, check the following list of best practices one by one, to avoid the most common problems and ensure that your plugin is developed correctly.

  • Provide a starting page called index.html and store it in the root directory of the plugin.
  • Use a single HTML page to hold all the UI views. This is to provide the necessary navigation through views in the most efficient way.
  • Include the manifest.xml file in the root directory of the plugin.
  • Do not load content using the component as it causes page loading errors.
  • Support multi-window mode and multiple screens (multi-resolution).
  • Include any external libraries with the source code, in the plugin folder or a subfolder, instead of linking to them externally. This way, they stay accessible when the app loses its Internet connection.
  • Make all references in your plugin relative.
  • Provide any multilingual support within the plugin.
  • Debug your web pages using the Chrome browser (chrome://inspect/). However, the SmartThings API will not be accessible as this debugging is outside the SmartThings app.
  • Package your plugin as one PPK package. For details, see Packaging a plugin.
  • Optimize the performance of your plugin once it is stabilized. Profile the plugin in terms of its RAM/ROM size; the plugin will be downloaded on demand in most cases so its size is important. Also review the security of the plugin and its data.

Project Structure

In the example structure below, a plugin uses these files:

  • resources style1.css, style2.css, image1.png, image2.png, script1.js, script2.js
  • folders folder1 and folder2
  • core libraries of API functions in SCPluginApi.js, enables communication between the plugin and the SmartThings app
  • plugin base page index.html, loads into the app’s WebView after the plugin is open

Plugin Project Overview

After you create the files structure, you can use SmartThings API libraries provided for plugin development. The API libraries and functions are listed in JavaScript API Reference. Each function has list of parameters with descriptions followed by sample code.


Getting the device object

First, you need to get an OCFDevice object to control device or show device info in your plugin. The OCFDevice object contains information about the device and provides an interface for a plugin to interact with the cloud server. You can get the device object by using getOCFDevices(). The OCFDevice object list is passed by its callback function. The properties of the device object are:

Field Type Description
deviceHandle String The local unique ID for the IoT device on which the plugin is running.
deviceType String URI of the IoT device type.
deviceName String Device name.
resourceUris [String] URIs of all resources available on the IoT device in the following format: “/capability/{CAPABILITY_NAME}/{COMPONENT_NAME}/{INDEX_FOR_ENUMERATION}” (Currently supported values are “main” for COMPONENT_NAME and “0” for INDEX_FOR_ENUMERATION.)
locationId String Location ID of the device.
locationName String Location name of the device.
owner String Device permission of the user. (“me” or “others”)
firmwareVersion String Device firmware version.
metadata JSON UI Metadata of the device.

One plugin can control more than one device.

var ocfDevice;
window.onload = function () {
   scplugin.manager.getOCFDevices(onOCFDevicesCallback);
}
function onOCFDevicesCallback(devices) {
   scplugin.log.debug("SamplePlugin", "onOCFDevicesCallback", devices[0].deviceName);

   ocfDevice = devices[0]);
}

Controlling a device resource

Each device has capabilities, each of which has a state. The OCF specification uses the term resource instead of capability, and representation instead of state. So, for example:

  • device = air conditioner
  • resources = power switch, temperature
  • representation = on or off, current temperature

To get the representation of a device resource, call the API function getRemoteRepresentation(). To set a resource representation, call setRemoteRepresentation().

These API functions use GET and POST requests. For example, getRemoteRepresentation() sends a GET request to the API to obtain the state of the requested resource. The API functions take a URI string as an argument, which allows them to access a specific resource. As an example, by using the URI /capability/switch/main/0, we can either get the power switch status to check if it is on or off, or set the switch to on or off.

In this section we will show how to get an air conditioner’s current temperature. The resource URI of temperature is /temperatureMeasurement/main/0 and the representation is temperature.

// variable to save air conditioner temperature
var temperature = 0;
function onRepresentCallback(result, deviceHandle, uri, representation) {
   temperature = representation["temperature"];
}
function getTemperature() {
   ocfDevice.getRemoteRepresentation("/capability/temperatureMeasurement/main/0", onRepresentCallback);
}

This example implements a callback to read the current temperature of an air conditioner. With this resource, we can get the temperature in units of Kelvin, Celsius, or Fahrenheit.

Observing a device resource

To monitor a device’s power status changing or other parameters of a device, you can register an observer for the resource using the API call subscribe(). With this, your plugin listens for changes to the power switch.

// Assume ocfDevice object obtained using method scplugin.manager.getOCFDevices()
function onRepresentCallback(result, uri, representation, deviceHandle) {
    if (result == "OCF_OK") {
         console.log(representation ["power"]);
           // Do something...
    }
}
// All the resources URIs
ocfdevice.subscribe(onRepresentCallback);
// Given resource URIs
var uris = ["/capability/switch/main/0"];
ocfdevice.subscribe(onRepresentCallback, uris);

You can call subscribe only once. Use either of the following calls:

// observe all resources:
ocfdevice.subscribe(onRepresentCallback);                            

// observe desired resources
ocfdevice.subscribe(onRepresentCallback, ["power", "temperature"]);

Monitoring the device connection

You can monitor the state of a device’s connection to SmartThings Cloud. A plugin can manage a device only when device is connected to SmartThings Cloud.

// Assume ocfDevice object obtained using method scplugin.manager.getOCFDevices()
function onMonitoringStateCallback(result, state) {
    if (result == "OCF_OK") {
        if (state == "CONNECTED") {
            // Do something...
        } else if (state == "DISCONNECTED") {
            // Do something...
        } else if (state == "INACTIVE") {
            // Do something...
        } else if (state == "UNKNOWN") {
            // Do something...
        }
    }
}
ocfdevice.startMonitoringConnectionState(onMonitoringStateCallback);

Logging

The scplugin.log functions send log messages to a system console (for example, Android logcat).

  • scplugin.log.debug: Send a debug log message.
  • scplugin.log.error: Send an error log message.
  • scplugin.log.info: Send an info log message.
  • scplugin.log.verbose: Send a verbose log message.
  • scplugin.log.warning: Send a warning log message.

Each one of the above functions takes three parameters:

  • className: class name for log message formatting
  • functionName: function name for log message formatting
  • msg: message for log

These methods easily move the web app log to a system console (like Android logcat), which can then be used to track other defects related to the plugin. Follow the Android guidelines for logging. For example, use scplugin.log.verbose only during plugin development and not in a production release.

Reading and writing data

If you want to save non-volatile data, you can use the plugin data API, which provides simple methods to read and write data. You can set a key - value pair in a data table by calling setPluginData().

window.scplugin.manager.setPluginData(OCFDevice.deviceHandle, "key_1", "1234567890");
window.scplugin.manager.setPluginData(OCFDevice.deviceHandle, "key_2", "abcdefghighk")

To read key-value pairs from the data table, use getPluginData().

function onPluginDataCallback(key, value) {
   if (value != null) {
      console.log(" key: " + key + " value: " + value );
   } else {
      console.log(" key: " + key + " > NO VALUE FOUND");
   }
}
window.scplugin.manager.getPluginData(OCFDevice.deviceHandle, "key_1", onPluginDataCallback);

Checking the cloud connection

To check the state of a device’s connection with SmartThings Cloud:

function onCloudConnectionStateCallback(state) {
   if (state == "CONNECTED") {
   } else if (state == "DISCONNECTED") {
   }
}
window.scplugin.manager.setCloudConnectionStateListener(onCloudConnectionStateCallback);

Checking the app state

To monitor the current state of the SmartThings app:

function onApplicationStateCallback(state) {
   if (state == "ACTIVE") {
      // Application become active.
   } else if (state == "BACKGROUND") {
      // Application enter background.
   }
}
window.scplugin.manager.setApplicationStateListener(onApplicationStateCallback);

Closing a Plugin

Call close() when your to plugin should be closed. This function will close the activity with the plugin.

function closeButtonClicked() {
   window.scplugin.log.debug("SamplePlugin", "closeButtonClicked", "close plugin");
   scplugin.manager.close();
}