/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Homekit CT Corrector * * Author: * mrmikeface * * Documentation: TBD * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ import com.hubitat.app.DeviceWrapper import com.hubitat.hub.domain.Event definition( name: "Homekit CT Corrector", namespace: "homekitcorrect", author: "mrmikeface", importURL: "", description: "Adjust device color temperature when controlled by homekit via hubitat", category: "Lighting", iconUrl: "", iconX2Url: "" ) preferences { page name: "MainPage", install: true, uninstall: true page name: "ColorTemperatureOptions" } def MainPage(){ dynamicPage(name: "MainPage") { section("

Hubitat Homekit Corrector

"){ paragraph "Adjust device color temperature when controlled by homekit via hubitat." label title: "Enter a name for this instance of the application (optional)" } section("

Select Devices

") { paragraph "Individual devices should only be selected for one group" input "colorTemperatureDevices", "capability.colorTemperature", title: "Color temperature devices (CT mode)", multiple:true } section("

Advanced Options

") { input(name:"waitTime", type:"number", title: "Amount of time in milliseconds to search for events", description: "Interval used to find a corresponding homekit command event for a colour change", defaultValue: 200L, required: true, displayDuringSetup: true ) href(name: "toColorTemperatureOptions", title: "Color Temperature Options", page: "ColorTemperatureOptions", description: "Set Advanced Color Temperature Options" ) } section("

Logging

") { input(name:"logDescriptionText", type:"bool", title: "Enable descriptionText logging", description: "Logs information regarding application activity (Default: On)", defaultValue: true, required: true, displayDuringSetup: true ) input(name:"logEnhancedDescriptionText", type:"bool", title: "Enable enhanced descriptionText logging", description: "Logs additional information regarding application activity (Default: On)", defaultValue: false, required: true, displayDuringSetup: true ) input(name:"logDebug", type:"bool", title: "Enable debug logging", description: "Logs detailed data regarding application values (Default: Off)", defaultValue: false, required: true, displayDuringSetup: true ) } } } def ColorTemperatureOptions() { dynamicPage(name: "ColorTemperatureOptions") { section("

Color Temperature Options

"){ paragraph "The color temperature range available from homekit.

" input "useCTOverrides", "bool", title: "Use Color Temperature Overrides?" input "warmCTOverride", "number", title: "Warm White Temperature (default is 2500)" input "coldCTOverride", "number", title: "Cold White Temperature (default is max of an individual light)" } section("

CT Options

") { input "useIndividualCTOverrides", "bool", title: "Use Individual Color Temperature Overrides?" } section("Override 1") { paragraph "The color temperature range available from homekit.

" input "colorTemperatureDevicesOverride1", "capability.colorTemperature", title: "Color temperature devices (CT mode)", multiple:true input "warmCTOverride1", "number", title: "Warm White Temperature (default is 2500)" input "coldCTOverride1", "number", title: "Cold White Temperature (default is max of an individual light)" } section("Override 2") { paragraph "The color temperature range available from homekit.

" input "colorTemperatureDevicesOverride2", "capability.colorTemperature", title: "Color temperature devices (CT mode)", multiple:true input "warmCTOverride2", "number", title: "Warm White Temperature (default is 2500)" input "coldCTOverride2", "number", title: "Cold White Temperature (default is max of an individual light)" } section("Override 3") { paragraph "The color temperature range available from homekit.

" input "colorTemperatureDevicesOverride3", "capability.colorTemperature", title: "Color temperature devices (CT mode)", multiple:true input "warmCTOverride3", "number", title: "Warm White Temperature (default is 2500)" input "coldCTOverride3", "number", title: "Cold White Temperature (default is max of an individual light)" } section("Override 4") { paragraph "The color temperature range available from homekit.

" input "colorTemperatureDevicesOverride4", "capability.colorTemperature", title: "Color temperature devices (CT mode)", multiple:true input "warmCTOverride4", "number", title: "Warm White Temperature (default is 2500)" input "coldCTOverride4", "number", title: "Cold White Temperature (default is max of an individual light)" } section("Override 5") { paragraph "The color temperature range available from homekit.

" input "colorTemperatureDevicesOverride5", "capability.colorTemperature", title: "Color temperature devices (CT mode)", multiple:true input "warmCTOverride5", "number", title: "Warm White Temperature (default is 2500)" input "coldCTOverride5", "number", title: "Cold White Temperature (default is max of an individual light)" } section("Override 6") { paragraph "The color temperature range available from homekit.

" input "colorTemperatureDevicesOverride6", "capability.colorTemperature", title: "Color temperature devices (CT mode)", multiple:true input "warmCTOverride6", "number", title: "Warm White Temperature (default is 2500)" input "coldCTOverride6", "number", title: "Cold White Temperature (default is max of an individual light)" } section("Override 7") { paragraph "The color temperature range available from homekit.

" input "colorTemperatureDevicesOverride7", "capability.colorTemperature", title: "Color temperature devices (CT mode)", multiple:true input "warmCTOverride7", "number", title: "Warm White Temperature (default is 2500)" input "coldCTOverride7", "number", title: "Cold White Temperature (default is max of an individual light)" } section("Override 8") { paragraph "The color temperature range available from homekit.

" input "colorTemperatureDevicesOverride8", "capability.colorTemperature", title: "Color temperature devices (CT mode)", multiple:true input "warmCTOverride8", "number", title: "Warm White Temperature (default is 2500)" input "coldCTOverride8", "number", title: "Cold White Temperature (default is max of an individual light)" } section("Override 9") { paragraph "The color temperature range available from homekit.

" input "colorTemperatureDevicesOverride9", "capability.colorTemperature", title: "Color temperature devices (CT mode)", multiple:true input "warmCTOverride9", "number", title: "Warm White Temperature (default is 2500)" input "coldCTOverride9", "number", title: "Cold White Temperature (default is max of an individual light)" } section("Override 10") { paragraph "The color temperature range available from homekit.

" input "colorTemperatureDevicesOverride10", "capability.colorTemperature", title: "Color temperature devices (CT mode)", multiple:true input "warmCTOverride10", "number", title: "Warm White Temperature (default is 2500)" input "coldCTOverride10", "number", title: "Cold White Temperature (default is max of an individual light)" } } } def installed() { unsubscribe() unschedule() removeAllInUseGlobalVar() initialize() } def updated() { unsubscribe() unschedule() removeAllInUseGlobalVar() initialize() } def uninstalled() { unsubscribe() unschedule() removeAllInUseGlobalVar() } private logDescriptionText(debugText) { if (settings.logDescriptionText) { log.info "${app.name} (${app.getLabel()}): ${debugText}" } } private logEnhancedDescriptionText(debugText) { if (settings.logEnhancedDescriptionText) { log.info "${app.name} (${app.getLabel()}): ${debugText}" } } private logDebug(debugText) { if (settings.logDebug) { log.debug "${app.name} (${app.getLabel()}): ${debugText}" } } private def initialize() { logDebug("initialize() with settings: ${settings}") state.homekit = appList.find { it.name == "HomeKit Integration" && !it.user } subscribe(colorTemperatureDevices, "colorTemperature", eventHandler) } def eventHandler(Event evt) { logDebug("Processing event (${evt.name}, ${evt.value}) for ${evt.device.label}") def startDate = new Date(evt.date.getTime() - settings.waitTime) List eventsBetween = evt.device.eventsBetween(startDate, evt.date, [:]) List eventList = eventsBetween.findAll() { it.name = 'command-setColorTemperature' && "APP-${state.homekit.id}" == it.producedBy } if (eventList.size() >= 1) { logDebug("Found event for homekit(${eventList.get(0).name}) for ${evt.device.label}") int oldCt = Integer.parseInt(evt.value) def newValues = getNewValues(oldCt, evt.device) if (newValues.colorTemp != oldCt) { pause(settings.waitTime) logDebug("Setting ${evt.device.label} Color Temperature: ${newValues.colorTemp} (old value ${evt.value})") evt.device.setColorTemperature(newValues.colorTemp) } } else { logDescriptionText("Skipping event (${evt.name}, ${evt.value}) for ${evt.device.label}:${eventsBetween.collect {it.getProperties()}}") } } def getOverrides(DeviceWrapper device, Map values) { if (settings.useCTOverrides) { if (settings.warmCTOverride != null && settings.warmCTOverride != "") { values.warmCT = settings.warmCTOverride } if (settings.coldCTOverride != null && settings.coldCTOverride != "") { values.coldCT = settings.coldCTOverride } } if (settings.useIndividualCTOverrides) { switch (device) { case colorTemperatureDevicesOverride1: if (settings.warmCTOverride1 != null && settings.warmCTOverride1 != "") { values.warmCT = settings.warmCTOverride1 } if (settings.coldCTOverride1 != null && settings.coldCTOverride1 != "") { values.coldCT = settings.coldCTOverride1 } break case colorTemperatureDevicesOverride2: if (settings.warmCTOverride2 != null && settings.warmCTOverride2 != "") { values.warmCT = settings.warmCTOverride2 } if (settings.coldCTOverride2 != null && settings.coldCTOverride2 != "") { values.coldCT = settings.coldCTOverride2 } break case colorTemperatureDevicesOverride3: if (settings.warmCTOverride3 != null && settings.warmCTOverride3 != "") { values.warmCT = settings.warmCTOverride3 } if (settings.coldCTOverride3 != null && settings.coldCTOverride3 != "") { values.coldCT = settings.coldCTOverride3 } break case colorTemperatureDevicesOverride4: if (settings.warmCTOverride4 != null && settings.warmCTOverride4 != "") { values.warmCT = settings.warmCTOverride4 } if (settings.coldCTOverride4 != null && settings.coldCTOverride4 != "") { values.coldCT = settings.coldCTOverride4 } break case colorTemperatureDevicesOverride5: if (settings.warmCTOverride5 != null && settings.warmCTOverride5 != "") { values.warmCT = settings.warmCTOverride5 } if (settings.coldCTOverride5 != null && settings.coldCTOverride5 != "") { values.coldCT = settings.coldCTOverride5 } break case colorTemperatureDevicesOverride6: if (settings.warmCTOverride6 != null && settings.warmCTOverride6 != "") { values.warmCT = settings.warmCTOverride6 } if (settings.coldCTOverride6 != null && settings.coldCTOverride6 != "") { values.coldCT = settings.coldCTOverride6 } break case colorTemperatureDevicesOverride7: if (settings.warmCTOverride7 != null && settings.warmCTOverride7 != "") { values.warmCT = settings.warmCTOverride7 } if (settings.coldCTOverride7 != null && settings.coldCTOverride7 != "") { values.coldCT = settings.coldCTOverride7 } break case colorTemperatureDevicesOverride8: if (settings.warmCTOverride8 != null && settings.warmCTOverride8 != "") { values.warmCT = settings.warmCTOverride8 } if (settings.coldCTOverride8 != null && settings.coldCTOverride8 != "") { values.coldCT = settings.coldCTOverride8 } break case colorTemperatureDevicesOverride9: if (settings.warmCTOverride9 != null && settings.warmCTOverride9 != "") { values.warmCT = settings.warmCTOverride9 } if (settings.coldCTOverride9 != null && settings.coldCTOverride9 != "") { values.coldCT = settings.coldCTOverride9 } break case colorTemperatureDevicesOverride10: if (settings.warmCTOverride10 != null && settings.warmCTOverride10 != "") { values.warmCT = settings.warmCTOverride10 } if (settings.coldCTOverride10 != null && settings.coldCTOverride10 != "") { values.coldCT = settings.coldCTOverride10 } break default: logDebug("No override for ${device.label}") break } } return values } def getNewValues(Integer oldCT, DeviceWrapper device) { def overrides = getOverrides(device, [warmCT: 2500, coldCT: 6535]) int warmCT = overrides.warmCT as int int coldCT = overrides.coldCT as int def deviceCT = getDeviceJson(device.id).deviceState.ct int rangeCT = coldCT - warmCT int deviceCTRange = deviceCT.max - deviceCT.min int colorTemp = oldCT-((deviceCT.max-oldCT)*((deviceCTRange/rangeCT)-1)) int upperOffset = Math.abs(deviceCT.max - coldCT) if (upperOffset > 0) { logDebug("Adding ${upperOffset} to final CT") colorTemp += upperOffset } return [colorTemp: Math.round(colorTemp)] } // No easy way to find device state variables other than this. // Could be cached at set up so that we can use auth // but would need updating if device changes def getDeviceJson(String deviceId) { def result = [:] def params = [ uri: getBaseUrl(), path: "/device/fullJson/${deviceId}", headers: [ Cookie: state.cookie ], ignoreSSLIssues: true ] try { httpGet(params) { result = it.data } } catch (e) { log.error "Error retrieving device info: ${e}" } return result } def getAppList() { def result = [] def params = [ uri: getBaseUrl(), path: "/hub2/appsList", headers: [ Cookie: state.cookie ], ignoreSSLIssues: true ] try { httpGet(params) { resp -> resp.data.apps.each { result += it.data } } } catch (e) { log.error "Error retrieving installed apps: ${e}" } return result } def getBaseUrl() { def scheme = sslEnabled ? "https" : "http" def port = sslEnabled ? "8443" : "8080" return "$scheme://127.0.0.1:$port" }