SmartApp SDK - Node.js
Version 2.0 Release
This major release is not fully backwards compatible with version 1.X, though for most SmartApps the changes required should be relatively minor. The major non-backwards compatible changes include:
- Methods that return lists now return arrays instead of objects with the properties
itemsand_links. - Axios is now used rather than request-promise-native for making HTTP calls, resulting in changes to the error objects thrown when exceptions occur.
See the Version 2.0.0 release notes for more information.
For a detailed look at the API, check out the reference documentation.
Find the SmartApp SDK - Node.js in the SmartThings GitHub repository
Getting Started
This SDK includes a set of Node.js libraries for building OAuth Service Integrations to interact with the SmartThings API.
Highlights include:
- ✅ Javascript API hides details of REST calls and authentication.
- ✅ Event handler framework dispatches lifecycle events to named event handlers.
- ✅ Configuration page API simplifies page definition.
- ✅ Integrated i18n framework provides configuration page localization.
- ✅ Winston framework manges log messages.
- ✅ Context Store plugins – easily scale access token management (and more) to support many users.
- ✅ AWS DynamoDB plugin (usage)
- ✅ Firebase Cloud Firestore plugin (usage)
Installation
npm i @smartthings/smartapp --save
Importing
- Node.js
- ES2015+
const SmartApp = require('@smartthings/smartapp')
import SmartApp from '@smartthings/smartapp'
Examples
The example SmartApp below is the equivalent of a simple Rule (if contact sensor opens/closes, turn lights on/off) which is easily achieved via our Rules API. It is given here as a brief showcase of the SDK, and is not meant to be a good candidate for a SmartApp.
Before hosting your own Automation, be sure to check out Rules. When all services and Device features involved in a Rule are local, Rules execute locally on a Hub, allowing you to benefit from greater speed, stability, and security than cloud-reliant solutions.
Running as a Web Service
To run your SmartApp with an HTTP server, such as Express.js:
const SmartApp = require('@smartthings/smartapp');
const express = require('express');
const server = express();
const PORT = 8080;
/* Define the SmartApp */
const smartapp = new SmartApp()
.enableEventLogging(2) // logs all lifecycle event requests and responses as pretty-printed JSON. Omit in production
.page('mainPage', (context, page, configData) => {
page.section('sensors', section => {
section
.deviceSetting('contactSensor')
.capabilities(['contactSensor'])
});
page.section('lights', section => {
section
.deviceSetting('lights')
.capabilities(['switch'])
.permissions('rx')
.multiple(true);
});
})
// Called for both INSTALLED and UPDATED lifecycle events if there is no separate installed() handler
.updated(async (context, updateData) => {
await context.api.subscriptions.delete() // clear any existing configuration
await context.api.subscriptions.subscribeToDevices(context.config.contactSensor, 'contactSensor', 'contact', 'myDeviceEventHandler');
})
.subscribedEventHandler('myDeviceEventHandler', async (context, event) => {
const value = event.value === 'open' ? 'on' : 'off';
await context.api.devices.sendCommands(context.config.lights, 'switch', value);
});
server.use(express.json());
/* Handle POST requests */
server.post('/', function (req, res, next) {
smartapp.handleHttpCallback(req, res);
});
/* Start listening at your defined PORT */
server.listen(PORT, () => console.log(`Server is up and running on port ${PORT}`));
Running as an AWS Lambda Function
To run your SmartApp as a Lambda function instead of an HTTP server, ensure that your main entry file exports smartapp.handleLambdaCallback(...).
This snippet is heavily truncated for brevity.
const SmartApp = require('@smartthings/smartapp');
const smartapp = new SmartApp()
.enableEventLogging() // logs all lifecycle event requests and responses. Omit in production
.page( ... )
.updated(() => { ... })
.subscribedEventHandler( ... );
exports.handler = (event, context, callback) => {
smartapp.handleLambdaCallback(event, context, callback);
};
Additional Examples
Additional examples to use as a starting point can be found in our smartthings-smartapp-example Github Topic.
Localization
Configuration page strings are specified in a separate locales/en.json file, which can be automatically created the first time you run the app. Here's a completed English localization file for the previous simple SmartApp example:
{
"pages.mainPage.name": "Let There Be Light",
"pages.mainPage.sections.sensors.name": "When this door or window opens or closes",
"pages.mainPage.settings.contactSensor.name": "Select open/close sensor",
"pages.mainPage.sections.lights.name": "Turn on and off these lights and switches",
"pages.mainPage.settings.lights.name": "Select lights and switches",
"Tap to set": "Tap to set"
}
Unhandled Promise Rejection Handling
By default, instantiation of the SmartApp object registers an unhandledReject handler that logs unhandled promise rejections. You can disable this behavior by passing an option to the SmartApp instantiation (e.g. new SmartApp({logUnhandledRejections: false})).
If desired, you can replace the handler by calling unhandledRejectionHandler(promise => {...}) on the SmartApp object.
Making API Calls Outside of an EVENT Handler
By default, the SmartApp SDK will facilitate API calls on behalf of a user within the EVENT lifecycle. These user tokens are ephemeral and last 5 minutes. These access tokens are not able to be refreshed and should not be stored.
If you are making out-of-band API calls on behalf of a user's installed app, you will need to use the 24-hour access token that is supplied after the INSTALL and UPDATE lifecycles. This token includes a refresh_token, and will automatically be refreshed by the SDK when necessary.
Note that there is no in-memory context store; you must use a context store plugin. If you'd like to add a custom context store plugin, please consider contributing.
To get started with our context store example below, we will add a compatible ContextStore plugin that will persist these tokens (among other things) to a database.
Amazon AWS DynamoDB
Available as a node package on NPM or fork on GitHub.
If you are hosting your SmartApp as an AWS Lambda, this DynamoDB context store makes perfect sense. This assumes you've already configured the aws-sdk package to interact with your Lambda, so extending your context store to DynamoDB is a drop-in solution.
If you are self-hosted and still want to use DynamoDB, you can do that, too:
- Import the package to your project:
npm i --save @smartthings/dynamodb-context-store- Note: when adding this package, you also have
aws-sdkavailable at the global scope, so you can configure the AWS SDK:AWS.config.loadFromPath(creds)
- Note: when adding this package, you also have
- Get an AWS Access Key and Secret
- Set your credentials for your app, any of the following ways are fine.
- Register your Context Store plugin as described on the project repository's readme.
For complete directions on usage, please see this project's GitHub repository. (SmartThingsCommunity/dynamodb-context-store-nodejs)
Firebase Cloud Firestore
Available as a node package on NPM or fork on GitHub.
Usage is generally the same as DynamoDB:
- Generate a Firebase service account. You will receive a JSON file with the credentials.
- Load your Google Services JSON
- Create the context store
See the full usage guide on the project's GitHub repository.