Homebridge, SmartThings, Nest and Insteon
Joe Angell
With the forthcoming release of iOS 10 and better HomeKit functionality, I decided to get my SmartThings and Insteon hubs into the HomeKit universe. SmartThings and Nest doesn't support HomeKit at all, and Insteon requires a new $150 hub to do it in place of the old hub I already have.
I did a bit of googling and came across Homebridge, an open source project to bridge existing hardware to HomeKit via an always-on computer somewhere in your home. I was originally going to use my Magic Mirror's Raspberry Pi, but after hitting a configuration issue I used my Mac mini server instead.
Installing Homebridge
Homebridge is implemented through node.js, and is installed as a series of packages through the command line. On the Mac, this wasn't overly complex, especially with their instructions. There also provide detailed installation instructions.
Xcode
The first step is to download and install Xcode from the Mac App Store so the you can set up everything else. Note that you have to launch Xcode once (or enter the appropriate command line string) and agree to the license before you can continue.
node.js and Homebridge
Next, download and install node.js from their site. Once that's set up, you can get Homebridge itself installed from the Terminal.
sudo npm install -g homebridge --unsafe-perm
I found that I needed the --unsafe-perm flag to get it to install properly (this flag is mentioned in the HomeBridge readme). The OS X instructions also include information about how to update Homebridge and its plugins with npm.
Now Homebridge is installed, but it doesn't do a whole lot yet.
SmartThings and Homebridge
I found SmartThings support from the associated homebridge-smartthings project. Installing that package is also quite simple:
sudo npm install -g homebridge-smartthings --unsafe-perm
You then have to configure the SmartThings SmartApp. This requires adding its Github repository to your SmartThings developer page, adding it and turning on OAuth. Then in the smartphone app you open the SmartApp and check off all the devices you want. Finally, you copy the generated JSON snippet out of the SmartApp so that you can add it to your config.
This snippet doesn't contain any information about your SmartThing modules, so if you add new devices later you don't have to do this again. The code looks like this:
{ "platform": "homebridge-smartthings.SmartThings", "name": "SmartThings", "app_url": "https://graph.api.smartthings.com:443/api/smartapps/installations/", "app_id": "THIS-SHOULD-BE-YOUR-APPID", "access_token": "THIS-SHOULD-BE-YOUR-TOKEN", "polling_seconds": 600, "update_seconds": 1 }
Detailed steps for all of this information is described on the homebridge-smartthings page.
Editing Your Homebridge Config
I copied the JSON snippet from the SmartApp into Notes, and the copied it back out into a new file created with TextWrangler. The Homebridge devs say not to use TextEdit, but of course I tried it anyway and had odd problems that were fixed only by retyping entire lines by hand. I later had this issue with TextWrangler as well, so I think something just went weird with copy/paste. jsonlint can help you find errors in the JSON, although if you're paranoid you may want to remove any keys, usernames or passwords first.
Anyway, you need more in your config than just the SmartThings JSON. Here's a complete example config.
{ "bridge": { "name": "Homebridge", "username": "CC:22:3D:E3:CE:30", "port": 51826, "pin": "031-45-154" }, "description": "JSON API", "platforms": [ { "platform": "SmartThings", "name": "SmartThings", "app_url": "https://graph.api.smartthings.com:443/api/smartapps/installations/", "app_id": "YOUR_API_KEY_HERE", "access_token": "OUR_ACCESS_TOKEN_HERE" }, ] }
Finally, save the file in ~/.homebridge/config.json , and make sure to save it as plain ASCII text, not rich text or anything like that. I think I saved it to my desktop first, opened my user folder in Finder, created the .homebridge folder in the root of the drive, and since the leading period makes the folder invisible, I used the Finder's Go menu, Go To Folder to go to ~/.homebridge. Then I just dragged config.json file in from the desktop.
Launching Homebridge
Now you can manually launch Homebridge and try it out.
homebridge
With luck, you should see Homebridge initialize, load up all the SmartThings devices you flagged, and show you a code to enter into your HomeKit app. I found Eve and Insteon+ to be pretty decent free apps for setting up HomeKit.
To stop Homebridge, just hit ctrl-c in the terminal window. We'll be doing this a lot.
Automatically Launching Homebridge with launchd
During setup t's more useful to run Homebridge manually but once everything is compete you'll probably want to have it launch automatically.
launchd is an Apple daemon that launches background applications and relaunches them if they crash or are killed. The Homebridge macOS installation docs this. It's pretty easy: simply create an file named com.homebridge.server.plist in ~/Library/LaunchAgents , and put this in the file:
<plist version="1.0"> <dict> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>Label</key> <string>com.homebridge.server</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/homebridge</string> <string>-I</string> </array> <key>EnvironmentVariables</key> <dict> <key>PATH</key> <string>/usr/local/bin/:$PATH</string> </dict> </dict> </plist>
You can manually load and unload Homebridge with launchctl.
launchctl load ~/Library/LaunchAgents/com.homebridge.server.plist
launchctl unload ~/Library/LaunchAgents/com.homebridge.server.plist
Once loaded, Homebridge will immediately begin running in the background. If the app crashes or is killed, it will automatically restart. If you'd on't want it to restart, you have to unload it, which will immediately kill it. While you're doing setup you'll likely wind up needing to stop and start the server and look up the output a lot, so you'll want to just keep it unload it and manually launch it form the terminal with homebridge.
For the time being, keep it unloaded; we need to see the terminal output.
Testing SmartThings through Homebridge
Open up Insteon+, Eve, or your HomeKit app of choice and add your Homebridge as a new device. The pin code in the config.json file should be displayed in the Terminal after running homebridge. You'll need to enter that code into your app; theoretically, you can use your phone's camera to read the number, but I couldn't get a good image off my screen. Once added, all of the devices you've flagged in the SmartApp should show up in your HomeKit app. You should now be able to turn devices on and off both from within the app as well as with Siri.
If you add new SmartThings devices or flag new devices in the SmartApp, you'll need to restart the Homebridge server, either by ctrl-c in the terminal, or ill pgrep homebridge if you have it running through launchd, or by unloading it and loading it again with launchctl.
Adding Nest to Homebridge
Nest support through Homebridge is also available. Installing the plug-in is easy, but you have to jump through a number of hoops to get it set up. Installing is just another call to npm.
sudo npm install -g homebridge-nest
You then have to update your config.json with a new platform and get a key from Nest's developer site. This takes some back and forth; after setting up the Nest developer account, you have to put some data in your config.json, then run the Homebridge, then copy data out of the terminal output and paste that back into the Nest website so that you can get the final key you need.
I'm not going to try to cover the steps here just go to the Nest Homebridge page and follow the steps. The only thing I had to do differently was change the name of my "Product ID" on the Nest site to be unique, but everything else went smoothly.
Once that's all set up and you've relaunched Homebridge, you should just be able to open your favorite HomeKit app and see your newly-added Nest devices.
Adding Insteon Support to Homebridge
For reason I don't really understand, there isn't a Homebridge package for Insteon. You'll need an Insteon hub for this; I had an older 2242-222, which works fine through the Insteon app (not the Instead for Hub or Insteon+ app, mind you). After a bit of hunting, I found someone who had created their own based the Homebridge legacy plug-ins. So the first step is to install those:
sudo npm install -g homebridge-legacy-plugins
Next I added Insteon.js (from the aforementioned thread). This goes in /usr/local/lib/node_modules/homebridge/homebridge-legacy-plugins/accessories . This path requires admin rights, so expect to enter your password. I didn't need any of the other platforms in that folder, so I created a subdir called Disabled and moved them all into there.
This file relies on a the sync-require module, so you'll have to install that as well.
npm install -g sync-request
This also relies on HAP-NodesJS existing in homebridge-legacy-plug-ins/node_modules. The easiest fix here is to either copy it out of you homebridge/node_modules directory, or edit Insteon.js to point to it by changing the first line to:
var types ="require( "../../homebridge/node_modules/HAP-NodeJS/accessories/types.js" );
After that, you need to add each of your Insteon devices, one at a time, to the accessories part of your config.json. Here's a simple example:
"accessories": [ { "accessory": "Insteon", "name": "Entry Lamp", "host": "192.168.3.39", "port": "25105", "username": "username", "password": "password", "device_id": "AABBCC", "can_dim": true, "deviceType": "lightBulb" }, { "accessory": "Insteon", "name": "Wood Lamp", "host": "192.168.3.39", "port": "25105", "username": "username", "password": "password", "device_id": "C10", "can_dim": true, "deviceType": "lightBulb" } ]
Important notes:
- You can get the hub IP and port from the Insteon app, and change it form there. You'll likely want to set it up as a static IP in a non-DHCP part of your router's IP table.
- "device_id" is usually six character hexadecimal string. You can get this from your Insteon application by looking at the device's properties. My Insteon app shows periods between each hexadecimal byte, like "AA.BB.CC". You should remove the periods when entering it here, like "AABBCC".
- "can_dim" is just set to true for things like dimmable lights and variable speed fans.
- "deviceType" can be "lightBulb", "switch", "fan" or "garage". The "garage" case is specially handled; the others are just there so that HomeKit can handle "turn on all the lights" in such a way that it doesn't affect fans or non-light switches.
- "username" and "password" are not for your Insteon account, but for the hub itself. You can find this in the hub's settings in the Insteon app.
You'll need to create an accessory block for each of the the devices you want to add. You'll probably wind up running this through jsonlint to find all the extra or missing commas and quotes before you get it right.
Now you should be able to restart the Homebridge, open your HomeKit app, and turn on and off your Insteon devices.
That's great, but what about FanLinc?
I have three FanLinc modules in my house, and I really want to control the fan speed with HomeKit. However, the above Insteon home bridge solution doesn't handle them. The problem lies in the fact that FanLinc is really two devices in one, each addressed through different group codes, and Insteon.js only supports one group code.
After failing to find anything about how to set the group over the HTTP-based protocol used by the hub, I resorted to setting up a packet sniffer to monitor traffic from my iPhone. This article in particular was invaluable in getting that working. You can use WireShark or just the command line tcpdump; I used both as I learned how to find the information I needed.
The URL used to provide a group code is much longer than the one used for the lights, and not publicly documented unless you buy an SDK from Insteon. Luckily, a resilient developer has figured out much of the protocol on his own, including how to compute the checksum required for I2CS devices like FanLinc.
I, however, am lazy, so I used WireShark to capture the low, medium, high and off URLs sent to the fans from the Insteon app on my iPhone. The checksum only includes the command and data bytes, note the device ID bytes, so I was able to just add the four cases directly to Insteon.js.
Controlling Fan Speed
After integrating the extended codes, turning the fan on and off the worked flawlessly. Controlling the speed was another story. Dragging the "brightness" slider in Eve (the fan shows up as a "dimmable light") actually changed the light brightness, not the fan speed. After screwing around with this for a couple of hours I finally realized that it has to do with how fast you send the commands for the fan. Quickly sending a series of fan commands causes the light to change instead.
To work around this, I have Insteon.js only send fan speed change events up to once per second, using a timer to ensure that the most recent change is sent. This works great, and now I can control fan speed with HomeKit.
Controlling Fan Speed through Siri
The only problem now was that Siri doesn't seem to have fan speed commands. Also, since the device is identified as a fan, telling it to change the "brightness" or to "turn it up to 70%" (i.e.: "medium speed", since there are only four speeds) didn't do anything. Siri just said it couldn't find the device, but it had no problem turning it on and off.
To work around this problem, I added "onValue" and "offValue" entries to the config. This let me create multiple instances of the fans, each with different on speeds. For example, I had "Bedroom Fan Medium" with the "onValue" set to "medium" and the "offValue" set to "off". Now I could tell Siri "Turn on Bedroom Fan Medium" and it would jump to the medium speed.
But this was really not how things should work. I did some more investigation and found that there is fan speed support, and that I just needed to add the "rotation speed" characteristic to the device, handling it specially form the light brightness. Once I did that, i was able to tell Siri to "Set the Bedroom Fan to medium", and it did just what I requested.
Control vs Reporting
Both the original and my modified Insteon.js only support setting state, not reading state. Since the main goal here is to control devices through Siri, I consider that acceptable for the time being.
Sharing the Code
This should probably some homebridge-insteaon node.js package on Github, but I really don't know how to set that up and it's not a huge priority for me, so for now I'm just posting a link on my site here: Insteon.js.
This is not the prettiest code; I'm more of a C programmer than a Javascript programmer, but it gets the job done.
To install:
- Install Xcode tools as above
- Install node.js as above
- Install Homebridge as above
- Install the Homebridge legacy plug-ins as above
- Install sync-request as above
- Copy Insteon.js into /usr/local/lub/node_modules/homebridge/homebridge-legacy-plugins/accessories . You'll need to enter your admin password to copy to this location.
Now you just need to add your devices to your config.json. For example, this adds a single FanLinc device as two separate accessories, one as "lightBulb" for the light, and one as "fanlinc" for the fan.
"accessories": [ { "accessory": "Insteon", "name": "Bedroom Ceiling LIght", "host": "192.168.3.39", "port": "25105", "username": "username", "password": "password", "device_id": "A1B2C3", "can_dim": true, "onValue": "100", "offValue": "0", "deviceType": "lightBulb" }, { "accessory": "Insteon", "name": "Bedroom Fan", "host": "192.168.3.39", "port": "25105", "username": "username", "password": "password", "device_id": "A1B2C3", "can_dim": false, "onValue": "low", "offValue": "off", "deviceType": "fanlinc" } ]
Note that the same device_id is used for both "Bedroom Ceiling Light" and "Bedroom Fan". You will see two separate devices in your HomeKit app, and you can control the light and fan separately by using their individual names.
onValue and offValue are new as well, and do what you'd expect. For most devices, this is a value between 0 and 100. For FanLinc devices, it's one of "low", "medium" or "med", "high", or "off".
The Siri commands for fan speed seem to be:
- "Turn on the Bedroom Fan"
- "Set the Bedroom Fan to low"
- "Set the Bedroom Fan to medium"
- "Set the Bedroom Fan to maximum"
- "Set the Bedroom Fan to 50%"
- "Turn off the Bedroom Fan"
Note that "high" is not recognized as a fan speed, so you have to use "maximum" or "100%". There might be other commands that work as well.
That's it. It works well for me, and while I can't guarantee it'll work for anyone else, I don't see why it wouldn't.