Introduction to a SmartThings SmartApp, Part 2


Intro

In this part 2 of my two-part series, we will dig into writing a SmartApp. I will cover the basic structure of a SmartApp using the Node.js SDK (software development kit). If you haven’t checked out part 1, I’d recommend starting there first.

Prerequisites:

  • A SmartThings compatible color light.
  • Familiarity with APIs (Application Programming Interfaces).
  • Some understanding of Node.js or JavaScript.
  • Samsung account.
  • SmartThings Developer Workspace account. You can use your Samsung account login for the Developer Workspace.
  • SmartThings mobile application, Android or iOS.
  • Node.js installed.
  • npm installed.
  • ngrok installed. Ngrok will create a secure tunnel to the local node server.

Working with the SmartThings API

SmartThings has many SDKs for developers. The code examples in this blog post will use the Node.js SDK.

This library wraps the SmartThings API. This makes it easier to interact with the SmartThings API and use less code to do so.

Initial requirements

When writing the code to connect to the SmartThings API, there are a few small requirements.

Include the SmartThings library.

const SmartApp = require('@smartthings/smartapp');

Construct the general outline of the SmartApp.

// Define the SmartApp.
const smartapp = new SmartApp()
  // Enable translations.
  // Update translation file on changes.
  .configureI18n({updateFiles: true})

  // Logging for testing.
  .enableEventLogging(2)

  .page('mainPage', (context, page, configData) => {
    // Define SmartApp page sections.
    // These are the sections for user input.
  })

  .updated(async (context, updateData) => {
    // Use this section during the install and update lifecycles.
  })

  .subscribedEventHandler('myDeviceEventHandler', async (context, event) => {
    // Subscribe to a device event such as a switch turning on or off.
  });

  .scheduledEventHandler('myScheduledEventHandler', async (context, event) => {
    // Subscribe to a Scheduled event such as change the light color every hour.
  });

Add a webserver to handle requests.

const express = require('express');
const server = express();
const PORT = 8080;

server.use(express.json());

// Handle POST requests.
server.post('/', function (req, res, next) {
  smartapp.handleHttpCallback(req, res);
  // handleHttpCallback is a function in the SmartThings SDK.
});

When these pieces get added together, a simple SmartApp will look like the following. Note: I’ve filled in more information for a complete SmartApp. Including a function to determine how to send commands to the API.

// Import SmartApp sdk.
const SmartApp = require('@smartthings/smartapp');

// Use express for the Node.js webserver.
const express = require('express');
const server = express();
const PORT = 8080;

// A function to send random on and off commands to the light.
async function randomSwitch(context) {
  // Get a random 1 or 0 and assign on or off based on the result.
  const randomSwitch = Math.round(Math.random()) == 1 ? 'on' : 'off';

  // Send command to the SmartThings API.
  await context.api.devices.sendCommands(context.config.lightSwitch, [
    {
      capability: 'switch',
      command: randomSwitch
    }
  ]);
}

// Define the SmartApp.
const smartapp = new SmartApp()
  // Enable translations.
  // Update translation file on changes.
  .configureI18n({updateFiles: true})

  // Logging for testing.
  .enableEventLogging(2)

  .page('mainPage', (context, page, configData) => {
    // Define SmartApp page sections.
    // These are the sections for user input.
    page.section('checkDuration', section => {
      section
        .enumSetting('lightCheckDuration')
        .options([
          { id: '1', name: '1 Minute' },
          { id: '2', name: '2 Minutes' },
          { id: '5', name: '5 Minutes' }
        ])
        .defaultValue('1');
    });
    page.section('lightSwitch', section => {
      section
        .deviceSetting('lightSwitch')
        .capabilities(['switch'])
        .permissions('rx')
        .required(true);
    });
  })

  .updated(async (context, updateData) => {
    // Use this section during the install and update lifecycles.

    // Clear any existing configuration.
    await context.api.schedules.delete()

    // Set initial switch toggle.
    await randomSwitch(context);

    // Schedule future toggle checks.
    await context.api.schedules.schedule(
      'lightScheduleHandler', 
      `0/${context.configStringValue('lightCheckDuration')} * * * ? *`, 
      'UTC'
    );
  })

  .subscribedEventHandler('myDeviceEventHandler', async (context, event) => {
    // Subscribe to a device event such as a switch turning on or off.
    // Not using for this implementation.
  })

  .scheduledEventHandler('lightScheduleHandler', async (context, event) => {
    // Every duration chosen by user, toggle the light switch.
    randomSwitch(context);
  });

// Define webserver for receiving communication.
server.use(express.json());

// Handle POST requests.
server.post('/', function (req, res, next) {
  smartapp.handleHttpCallback(req, res);
  // handleHttpCallback is a function in the SmartThings SDK.
});

The result is a SmartApp that will turn the light on and off.

When the application calls the function randomSwitch(). The function determines if we send on or off to the SmartThings API with this command:

await context.api.devices.sendCommands(context.config.lightSwitch, [
  {
    capability: 'switch',
    command: randomSwitch
  }
]);

Capability refers to the SmartThings capability architecture. There is a list of the capabilities defined in the API documentation.

Each capability has a corresponding command. These commands are what the SDK works with to send changes to devices. Each smart device will have different capabilities. For example, a light most likely won’t have an audioMute capability.

Conclusion/Key takeaways

The example is not very innovative, but there are four takeaways from this test.

Four takeaways:

  1. The SmartThings SDK sends commands to a smart device.
  2. Custom functions determine smart device changes.
  3. Schedules determine how often to run a custom function.
  4. Device events determine when to run a custom function.

SmartApps create a wide variety of use cases and opportunities for unique implementations. SmartApps also allows the developer to use preexisting smart devices for IoT implementations.

Now we have covered both the setup of a SmartApp and how to start writing a SmartApp. I hope you can take this information and start creating!

References

Example repositories

API

SDK


About the author

Written by Nicholas Diesslin Pizza acrobat 🍕, typographer, gardener, bicyclist, juggler, senior developer, web designer, all around whittler of the web.
Follow Nick on Twitter!