In case you missed it, SwitchBot is pretty cool. Call me strange, but there is something awesome in little, simple robots doing mundane tasks for you. I transformed one of SwitchBots (review) into a KettleBot. SwitchBot Curtain (review), or CurtainBot is, on the other hand, the best way to drag your curtains on the cheap without modifications. Everything in SwitchBot just got better, thanks to the SwitchBot API published on GitHub. I’m here to give you a headstart so you don’t feel lost.
Now updated to SwitchBot API v1.1
New in SwitchBot
The team behind SwitchBot has been busy with app updates and the release of the SwitchBot API. The SwitchBot app has a new, more modern look, and thanks to new tabs, it’s easier to navigate. The functionality is still the same.
The SwitchBot API is a much more interesting update for me. Available on GitHub, libraries are split between direct Bluetooth control and the SwitchBot account API. There is also a Home Bridge integration if you are into HomeKit. Playing with the Bluetooth stack is over my head if I’m honest, but I really appreciate the option. This way, you can interact with SwitchBot gadgets directly (without the need for a SwitchBot Hub).
REST API enables access to SwitchBot serves in a very easy way. To take advantage of this, you will need the SwitchBot hub. All devices registered with the SwitchBot account will be available via API.
How to use SwitchBot API
While HomeKit and Bluetooth integrations you have to figure out yourself, I will show you how to interact with SwitchBot API via REST and NodeRED. You will be able to download the flow as well so you don’t have to create anything from scratch!
Thanks to SwitchBot API you can control your bots, curtains, IR devices and trigger scenes. Unfortunately, you won’t be able to capture SwitchBot Remote using this method (you can do so via Bluetooth API).
To get started, you will need an authentication token and secret. Go to SwitchBot app Profile – Preferences-About and tap 10 times on App Version. It will generate the authentication data for you. You will need this info to authenticate your requests. To make it easier, save the token as a global variable (and turn on persistent context) as this information will be used each time a REST call is made. In my sample flow I created a settings node to take care of that for you:
const SwitchBot_token = ""; const SwitchBot_secret = ""; const SwitchBot_url = "https://api.switch-bot.com/v1.1/"; var SwitchBot = {"SwitchBot":{ "token" : SwitchBot_token, "secret": SwitchBot_secret, "url" : SwitchBot_url} } global.set("API", SwitchBot);
Populate the fields in the settings node and you can start using the sample flow to your heart’s content.
The new SwitchBot API v1.1 changes the way requests are authenticated and to transition from the old API you have to enable a couple of things.
By setting functionExternalModules
to true
in your settings.js file, the Function node’s edit dialog will provide a list where you can add additional modules that should be available to the node. You also specify the variable that will be used to refer to the module in the node’s code.
Each function node in my flow requires crypto module to be added – so the SHA256 can be generated inside the function node. Imported samples should have all of this covered, but if you are re-writing your flows, these changes must be added manually to each node.
Now that the basic conffiguration is set, let’s take a look at the NodeRED and control SwitchBot devices from there.
Authentication changes in SwitchBot API v1.1
The new version of the API changed the way each call is authenticated, rendering my original project unusable. To fix that, I needed to add new authentication information to each call.
Currently, the API requires you to:
- Print the 13 digit timestamp and concatenate it with your
token
- Create a signature using your
secret
and the string produced in the previous step - Convert the signature to upper case
To streamline the process, I created small function that signs the message and embeds the information with each REST call without the need to modify my flows further. You will notice that each Format URL node contains the following:
var nonce = "requestID";
var token = global.get("API.SwitchBot.token");
var secret = global.get("API.SwitchBot.secret");
var url = global.get("API.SwitchBot.url");
var time = Date.now();
// function to encrypt the signature
function encrypt(time, token, secret, nonce){
const hmac = crypto
.createHmac("sha256", secret)
.update(token+time+nonce)
.digest();
let z= hmac.toString("base64");
return z.toUpperCase();
}
// header for each REST call
msg.headers = {"Authorization": token,
"sign": encrypt(time, token, secret, nonce),
"nonce": nonce,
"t": time,
'Content-Type': 'application/json'
};
As this is present in each call, I’m going to skip these in my explanation below.
Getting devices and scenes
SwitchBot API uses device ID in requests, so it makes sense to grab all of these for each device and scene, and then save them as flow variables. This way I don’t have to refer back and forth and my functions will retrieve the correct ID based on submitted names. The same principle applies to scenes created in the app.
The list is iterated and saved to flow variables for devices and scenes for your convenience. If you add new devices, simply re-run the flow and you are up to date. The same goes if your devices are renamed.
//devices var devices = msg.payload.body.deviceList var deviceList = []; for( var y in devices) { let device = {"type": devices[y].deviceType, "name": devices[y].deviceName, "deviceID": devices[y].deviceId }; deviceList.push(device); } flow.set("devices", deviceList); return msg; //scenes var scenes = msg.payload.body var sceneList = []; for( var y in scenes) { let scene = { "name": scenes[y].sceneName, "sceneID": scenes[y].sceneId }; sceneList.push(scene); } flow.set("scenes", sceneList); return msg;
Now that we have a list of devices to play with, let’s get started. Before I manipulate any of them, I will show you how to get statuses.
Status
As before, the request has to be authenticated, but this time, the URL has to contain the deviceID.
https://api.switch-bot.com/v1.1/devices/"+ FindDevice(device) +"/status
Flow is designed to work with names. Submit the name, and the correct ID will be added to the final URL and status retrieved as a JSON message.
var devices = flow.get("devices");
var device = msg.payload; //enter name of the device or use msg.payload
function FindDevice(name){
let index = devices.find(o => o.name === name);
let deviceID = index.deviceID;
return deviceID
}
Just make sure to ignore the incoming payloads in the HTTP node and keep it set as GET request.
Depending on the queried device, the response will contain different data. The temperature sensor will respond with values for temperature and humidity, SwitchBots will share the state and CurtainBots will let you know about their position.
// SwitchBot "body": { "deviceId": "XXXXXXXXXX", "deviceType": "Bot", "hubDeviceId": "XXXXXXXXXX", "power": "off" } //CurtainBot "body": { "deviceId": "XXXXXXXXXX", "deviceType": "Curtain", "hubDeviceId": "XXXXXXXXXX", "calibrate": true, "group": false, "moving": false, "slidePosition": 25 } //SwitchBot Sensor "body": { "deviceId": "XXXXXXXXXX", "deviceType": "Meter", "hubDeviceId": "XXXXXXXXXX", "humidity": 43, "temperature": 19.9 }
Unfortunately, the data from the luminosity sensor from CurtainBots isn’t available at the time of the writing. Perhaps this will change in the future.
Control
It’s time for the exciting part: the control. You already had a peek at how I’m going to tackle the control scheme. I already have a script that identifies the deviceID given the name of the device. This time around, we have to use POST request and add the content type to the header. To complete the control scheme, we have to submit the control JSON in the body of the request: {"command":"command","parameter":"default","commandType":"command"};
.
All control schemes will have a similar pattern. Take a closer look at the GitHub page to look up control commands for specific devices.
SwitchBot control
SwitchBot require “command
” set to turnOn/turnOff/toggle
– so the command JSON would look like this: {"command":turnOn/turnOff/toggle,"parameter":"default","commandType":"command"}
– to. To streamline the use, I will pass the command as the msg.payload
. I assume that for the control scenario you want to hardcode the name in the script.
var devices = flow.get("devices");
var device = "White B"; //enter name of the device
function FindDevice(name){
let index = devices.find(o => o.name === name);
let deviceID = index.deviceID;
return deviceID
}
var command = msg.payload;
This is then composed into the header and a new REST call is made to the following URL:
https://api.switch-bot.com/v1.1/devices/"+ FindDevice(device) +"/commands"
CurtainBot control
CurtainBot also uses turnOn/turnOff
commands as well as setPosition
. The last one needs specific “parameter
” to be set (index,mode,position
). To be fair, I’m not sure what the index refers to, but “0” works just fine.
Modes are 0 (Performance Mode), 1 (Silent Mode), ff (default mode) and position is expressed in % 0-100 where 100 is closed.
In my flow, if the topic is set to setPosition
, then the script will execute the params given in the payload, otherwise, you can use turnOn/turnOff
as with SwitchBot.
var devices = flow.get("devices");
var param = msg.topic;
if(param === undefined){
param = "default";
}
var device = "Curtain"; //enter name of the device or use msg.payload
function FindDevice(name){
let index = devices.find(o => o.name === name);
let deviceID = index.deviceID;
return deviceID
}
var command = msg.payload;
IR control
The most complex commands are with IR devices. I’d strongly recommend using scenes instead for this, but if you are fixed on making the commands from scratch, you can compose it based on GitHub reference.
In this case, all params have to be set according to device specifications. The script allows this if msg.topic
is set to custom.
var devices = flow.get("devices");
var device = "Curtain"; //enter name of the device or use msg.payload
msg.payload = {"command":command,"parameter":"default","commandType":"customize"};
if(msg.topic !== "custom"){
msg.payload = {"command":command,"parameter":"default","commandType":"customize"};
}
function FindDevice(name){
let index = devices.find(o => o.name === name);
let deviceID = index.deviceID;
return deviceID
}
var command = msg.payload;
Executing scenes
As I already have a list of scenes available in NodeRED, executing these is very simple. SwitchBot API uses SceneID to trigger it and I already have a way of matching the name of the scene to the ID.
var scenes = flow.get("scenes");
var scene = msg.payload;
function FindScene(name){
let index = scenes.find(o => o.name === name);
node.warn(index);
let sceneID = index.sceneID;
node.warn(sceneID);
return sceneID
}
Once the scene is found, the REST call is composed and sent to a custom URL:
https://api.switch-bot.com/v1.1/scenes/"+ FindScene(scene) +"/execute"
My NodeRED flow contains examples of executing commands for SwitchBot, CurtainBot and Scenes. So feel free to download it and modify that to your needs.
Final thoughts
I’m pleased to see SwitchBot opening API and offering local ways of controlling SwitchBot devices. It’s a move in a good direction and welcomed by any DIY enthusiast out there. For now, I’ll stick with SwitchBot API, but perhaps with time, I will give the local Bluetooth controls a go as well. I will keep you all posted. If you have any comments or questions leave it in this Reddit thread.