Ben Dodson

Freelance iOS, Apple Watch, and Apple TV Developer

Using a physical button (Flic) with HomeKit scenes and triggers

When I started out as a web developer I was fascinated by APIs; the ability to hook into other data sources or update other applications from my own had huge potential that is finally being realised with the Internet of Things1. It is now possible to write apps that interact with physical objects via such things as IFTTT or dedicated APIs for devices like the Philips Hue. I’ve personally been very interested in the HomeKit initiative from Apple although it hasn’t really taken off yet.

Whilst I have a lot of internet connected devices, I currently own just a single HomeKit-compatible device; an Elgato eve door & window that lets me ask Siri if my back door is currently open2. Luckily there is the Homebridge project (which I’ve written about previously) that allows me to connect all of those together and use them as if they were native HomeKit devices.

Over the past few months, I’ve been dabbling with HomeKit scenes so that I can control groups of devices all at once like so:

Good Morning

  • Turns on downstairs lights
  • Starts playing my “morning” playlist in kitchen via AirPlay
  • Disables security cameras

Good Night

  • Turns off all lights (including bedside table lamp)
  • Stops any playing music
  • Enables security cameras

This has been working well but I frequently forget to use scenes as many of these things can be controlled in other ways. For example, I might turn off the downstairs lights with my Hue Dimmer Switch and then turn off the bedside table lamp via the Hue today widget which means I forget to activate the security cameras. The eventual solution I’ve come up with is a simple push button (from Flic) stuck under my bedside table that toggles between my Good Morning and Good Night scenes. In this article, I’ll tell you how I did it!

First of all, you are going to need a Flic. I did a lot of research on “smart buttons” and this one was far and away the best3. It’s small, has a changeable battery (that lasts 18 months), looks good, and it has an iOS SDK complete with background Bluetooth LE support so you can wake your app up even if it is inactive. The amount of code necessary to support Flic is ridiculously small; you can take a look at their iOS tutorial to see it in more detail but essentially you will be kicking your user to the Flic app in order to assign a button to the app. Once you’re back, it’s a simple case of activating a singleton and listening for a manager:didReceiveButtonDown request.

With the button tested and working, I set to work on adding HomeKit support to my app. This is relatively straightforward although you do need to create a dedicated provisioning profile in order to add the necessary entitlements to your project. Within a few minutes, I had a toggle in my app that would toggle between my Good Morning and Good Night scenes. That was when disaster struck; the HomeKit APIs do not work when your app is not in the foreground4.

This threw me for a little while until I decided that my app would simply have to connect directly to my mac and interface with the Homebridge software that was basically powering everything5. After a quick search, I found Homebridge-websocket, a plugin that basically adds a custom websocket server as a platform to HomeKit. You can create accessories (like a switch) within the websocket server and then you’ll get callbacks when they are turned on and off.

I planned to make use of this via a HomeKit property known as “triggers”. A trigger is basically a way for HomeKit to monitor a specific device for a specific value (i.e. when a switch is “on”). As soon as it sees that value, it will activate a chosen scene. I set this up by creating two new switches within Homebridge-websocket; ws_morning and ws_evening. I then set up two triggers within HomeKit; WebSocket Morning (which triggers the Good Morning scene when the ws_morning switch turns on) and WebSocket Evening (which triggers the Good Night scene when the ws_evening switch turns on). The only change I needed to make to my scenes was to ensure that both ws_morning and ws_evening are set to “off” when either is activated; this ensures that I can always turn them from “off” to “on” and thus cause the trigger.

The final step was to update my iOS app to talk to the websocket server instead of to Homebridge. I found a simple drop-in framework named Starscream which allowed me to connect to the server and send commands which led me to this code for the entire project:

import UIKit
import Starscream

let ipAddress = "192.168.1.100:4050"
let flicAppKey = "Your-Flic-App-Key"
let flicAppSecret = "Your-Flic-App-Secret"

class ViewController: UIViewController, SCLFlicButtonDelegate, SCLFlicManagerDelegate, WebSocketDelegate {

    let socket = WebSocket(url: NSURL(string: "ws://\(ipAddress)/")!)
    
    override func viewDidLoad() {
        super.viewDidLoad()
        socket.delegate = self
        SCLFlicManager.configureWithDelegate(self, defaultButtonDelegate: self, appID: flicAppKey, appSecret: flicAppSecret, backgroundExecution: true)
    }

    @IBAction func setupFlic(sender: AnyObject) {
        SCLFlicManager.sharedManager()?.grabFlicFromFlicAppWithCallbackUrlScheme("sceneControl")
    }
    
    @IBAction func toggle() {
        socket.connect()
    }
    

    // MARK: Flic Manager
    
    func flicManager(manager: SCLFlicManager, didGrabFlicButton button: SCLFlicButton?, withError error: NSError?) {
        if let error = error {
            NSLog("ERROR: \(error)")
        }
    }
    
    func flicButton(button: SCLFlicButton, didReceiveButtonDown queued: Bool, age: Int) {
        toggle()
    }
    
    
    // MARK: Websocket Delegate
    
    func websocketDidConnect(socket: WebSocket) {
        let defaults = NSUserDefaults.standardUserDefaults()
        let last = defaults.stringForKey("last_status")
        let name = last == "ws_morning" ? "ws_evening" : "ws_morning"
        let params = ["topic": "setValue", "payload": ["name": name, "characteristic": "On", "value": NSNumber(bool: true)]]
        let data = try! NSJSONSerialization.dataWithJSONObject(params, options: .PrettyPrinted)
        if let string = String(data: data, encoding: NSUTF8StringEncoding) {
            socket.writeString(string)
            socket.disconnect()
            defaults.setObject(name, forKey: "last_status")
            defaults.synchronize()
        }
    }
    
    func websocketDidDisconnect(socket: WebSocket, error: NSError?) {
    }
    
    func websocketDidReceiveData(socket: WebSocket, data: NSData) {
    }
    
    func websocketDidReceiveMessage(socket: WebSocket, text: String) {
    }
    
}

Note: this is just a personal project that I built as quickly as possible. By nature, it is a fairly dirty hack. You shouldn’t write professional apps like this.

With this code in place, I can now press my Flic button (stuck to the underside of my bedside table) and it will toggle between my Good Morning and Good Night scenes. This is the full process of what is going on:

  • Press the Flic button
  • Wakes up the iOS app (if terminated or backgrounded) and hits the toggle function
  • App sends a websocket command to the Homebridge-websocket server
  • Server turns on a fake switch
  • HomeKit notices that the fake switch has been turned on so fires trigger to run appropriate scene
  • Scene activates or deactivates various accessories and then switches the fake switch back off ensuring it is ready for next button press

This is a long way away from the process I’d envisioned (press button, wake up app, toggle scene with HomeKit API) but it has been a fun challenge finding a way to get it work. I now have a physical button that controls multiple aspects of my home with a single press - it’s pretty cool!

  1. Ugh what a horrible expression. I renamed it the Internet of Stuff on episode 7 of The Divide↩︎

  2. Phrasing ↩︎

  3. “Why not just build your own with a Raspberry Pi or similar?”. Because I can’t be bothered; the cost of my time to do that is more than the cost of buying an incredibly well put together button that already does exactly what I want. ↩︎

  4. Probably for a very good reason, you likely don’t want an app reading information about or controlling your HomeKit devices when you aren’t literally using the app. It’s a bit of a pain though for a simple app like this. I did manage to find a com.apple.developer.homekit.background-mode entitlement but there is no way to generate a provisioning profile that uses it. Maybe Apple will add this in a future version of iOS but I wouldn’t count on it. ↩︎

  5. Technically, as all of my devices have their own APIs I could have just built my app to directly talk to Hue, Canary, iTunes, etc and rebuild my scenes that way. That idea didn’t sit well with me though as it’d be a lot more work and effectively duplicating what I already have. Similarly, I could have just used the native IFTTT support with the Flic to turn on or off some of my devices but that would add more latency and I don’t want to trust my connected home to something that has to be connected online; the beauty of HomeKit is that it works locally. ↩︎

Great British Bee Count 2016 » « Fetching RSS Feeds for Steam game updates