Topics

#298: Changing Light Colors with HomeKit 💡🌈

Topics

In Bite #297, we learned about about how to simulate a light bulb accessory using Apple's HomeKit Accessory Simulator. Today we'll continue where we left off, and learn how to change the color of our light bulb.

Let's dive in.

Everything in HomeKit is built around the HMHomeManager type. We'll use this manager to interact with homes, accessories, and more:

let manager = HMHomeManager()
manager.delegate = self

We've set ourselves as the delegate here so we can implement one function:

extension ViewController : HMHomeManagerDelegate {
  func homeManagerDidUpdateHomes(_ manager: HMHomeManager) {
    // TODO
  }
}

Like all user data, we'll need to ask for permission before we can access the user's HomeKit data. Once the user has allowed us, the homeManagerDidUpdateHomes function will be called. This function will also be called anytime signficant changes happen in a home.

We can use this as a cue to update our app's UI to match. Neat.

Ok, let's have some fun and see what it would look like (in code) to change all of the lights in our house to a different color.

First let's get all the lights in the home:

func homeManagerDidUpdateHomes(_ manager: HMHomeManager) {
  guard let home = manager.homes.first else { return }

  let lights = home.accessories.filter { $0.category.categoryType == HMAccessoryCategoryTypeLightbulb }
}

Nice. We now have an array of all lights in our home as HMAccessory objects. To make changes, we'll need to get to the services and characteristics inside of each accessory.

Let's flatMap our way to success and get an array of just the "Hue" characteristics.

Note: We're just going to try this out by tinting our light blue. In a complete app, we'd need to make sure to update all 3 Hue, Brightness, and Saturation characteristics of the light.

let hueCharacteristics = lights
  .flatMap { $0.services }
  .flatMap { $0.characteristics }
  .filter { $0.characteristicType == HMCharacteristicTypeHue }

Nice, now we need a way to grab a hue value from a UIColor:

func hsba(from color: UIColor) -> [CGFloat] {
  var HSBA = [CGFloat](repeating: 0.0, count: 4)
  color.getHue(&HSBA[0], saturation: &HSBA[1], brightness: &HSBA[2], alpha: &HSBA[3])
  return HSBA
}

Not the prettiest code, but it'll do. Now let's use it:

let color = hsba(from: UIColor.blue)
let newHue = Float(color[0])

for hc in hueCharacteristics {
  let max = hc.metadata?.maximumValue?.floatValue ?? 1.0

  hc.writeValue(NSNumber(value: newHue * max), completionHandler: { if let error = $0 { print("Failed: \(error)") } })
}

Here we grab our newHue value for our UIColor.blue color, then iterate through all the hueCharacteristics from before, ensuring we multiply our newHue value by each characteristic's maximumValue property. This makes sure we're speaking the language of the accessory, and setting a value in a scale it understands.

Finally, we wrap up our value in an NSNumber and write it to the HMCharacteristic.

When we build and run, we're prompted for permission. After allowing, we can flip back over to the HomeKit Accessory Simulator app and see the hue of our light has changed. Success!

Try adding more simulated lights, setting them to random colors, then running the app again to see a greater effect.

That's all for now. We've got plenty more to explore in HomeKit, look out for more Bites soon.

Similar Bites