Skip to content

Instantly share code, notes, and snippets.

@brindy
Created January 10, 2023 13:53
Show Gist options
  • Save brindy/7b16f210e40cc8ec9ea9f7eefc5fc714 to your computer and use it in GitHub Desktop.
Save brindy/7b16f210e40cc8ec9ea9f7eefc5fc714 to your computer and use it in GitHub Desktop.
An example of using HomeKit to periodically change the colour of a light to something random. (Targets iOS 15.6)
//
// ContentView.swift
// DragonLight
//
// Created by Chris Brind on 09/01/2023.
//
import SwiftUI
import HomeKit
import Combine
class DragonLightModel: ObservableObject {
struct HSL {
let hue: Double
let saturation: Double
let brightness: Double
var color: Color {
Color(hue: hue / 360.0, saturation: saturation / 100.0, brightness: brightness / 100.0)
}
}
let homeManager = HMHomeManager()
@Published var active = false
@Published var current = HSL(hue: 0.0, saturation: 0.0, brightness: 0.0)
init() {
}
@MainActor
func start() {
print("***", #function, active)
if !active {
active = true
}
let hue = Double.random(in: 0 ..< 45)
let saturation = Double.random(in: 80 ..< 100)
let brightness = Double.random(in: 50 ..< 100)
let color = HSL(hue: hue, saturation: saturation, brightness: brightness)
current = color
Task {
do {
try await self.updateLight(color: color)
} catch {
print(error.localizedDescription)
}
let interval = Double.random(in: 5.0 ..< 30.0)
// If targetting iOS 16 use Task.sleep(duration: .seconds(interval)) instead
try await Task.sleep(nanoseconds: UInt64(1_000_000_000 * interval))
if active {
self.start()
}
}
}
func stop() {
active = false
}
func updateLight(color: HSL) async throws {
print("***", #function, color.hue, color.saturation, color.brightness)
guard let light = findLight() else {
print("*** failed to find light")
return
}
guard let hue = light.find(serviceType: HMServiceTypeLightbulb,
characteristicType: HMCharacteristicTypeHue) else {
print("*** failed to find", HMServiceTypeLightbulb, HMCharacteristicTypeHue)
return
}
guard let saturation = light.find(serviceType: HMServiceTypeLightbulb, characteristicType: HMCharacteristicTypeSaturation) else {
print("*** failed to find", HMServiceTypeLightbulb, HMCharacteristicTypeSaturation)
return
}
guard let brightness = light.find(serviceType: HMServiceTypeLightbulb, characteristicType: HMCharacteristicTypeBrightness) else {
print("*** failed to find", HMServiceTypeLightbulb, HMCharacteristicTypeBrightness)
return
}
try await hue.writeValue(color.hue)
try await saturation.writeValue(color.saturation)
try await brightness.writeValue(color.brightness)
}
func findLight() -> HMAccessory? {
guard let home = findHome() else {
print("*** failed to find home")
return nil
}
return home.accessories.first(where: { $0.name == "Dragon" })
}
func findHome() -> HMHome? {
return homeManager.homes.first
}
@MainActor
func activate() {
active = true
}
}
struct ContentView: View {
@ObservedObject var model = DragonLightModel()
@State var hue = 0.0
@State var saturation = 0.0
@State var brightness = 0.0
var body: some View {
VStack {
RoundedRectangle(cornerRadius: 8)
.foregroundColor(model.current.color)
.frame(width: 200, height: 200)
if model.active {
Button {
model.stop()
} label: {
Text("Stop")
}
} else {
Button {
model.start()
} label: {
Text("Start")
}
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
extension HMAccessory {
func find(serviceType: String, characteristicType: String) -> HMCharacteristic? {
return services
.first(where: { $0.serviceType == serviceType })?
.characteristics.first(where: { $0.characteristicType == characteristicType })
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment