Created
January 10, 2023 13:53
-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 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