/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Day Lights
*
* Author:
* OldChicagoPete
*
* Documentation: https://community.hubitat.com/t/release-day-lights-an-interation-of-circadian-daylight/130157
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Forked from:
* Hubitat Circadian Daylight 0.81
* https://raw.githubusercontent.com/adamkempenich/hubitat/master/Apps/CircadianDaylight.groovy
* Which was forked from:
* SmartThings Circadian Daylight v. 2.6
* https://github.com/KristopherKubicki/smartapp-circadian-daylight/
*
*
*
* Custom fade-in/fade-out time:
* Jeff Byrom (@talz13)
* Color temperature converter:
* http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
* RGB to Hue/Saturation/Value:
* http://www.rapidtables.com/convert/color/rgb-to-hsv.htm
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Changelog:
* 0.90 (November 2, 2023)
* - Soft Release
* 0.91 (December 20, 2023)
* - General Release
* 0.92 (February 2, 2024)
* - Clarification on device selection headings
* - When using RGB mode devices with Dynamic Brightness, the calculated level will be used for the Value parameter
* - RGB mode devices will not be polled to determine if Dynamic Brightness has been overridden
* - Fix for HSV return values
* 0.93 (February 3, 2024)
* - New option in Color Temperature Options to force a device into CT mode by setting saturation to zero
* 0.94 (February 3, 2024)
* - New option to reset the color temperature when a device is dimmed manually
* - Fix for Brightness Options page
* - Minor formatting updates
* 0.95 (March 24, 2024)
* - Fix for sunrise/sunset offset
* - New option to select switches to turn on while Day Lights is active
* - Added a log entry when manual dimming turns off Dynamic Brightness
* - Don't disable Dynamic Brightness if device reports current level as null
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
import groovy.time.TimeCategory
definition(
name: "Day Lights",
namespace: "daylightsapp",
author: "OldChicagoPete",
importURL: "",
description: "Adjust device color temperature, color, and/or brightness during daylight hours",
category: "Lighting",
iconUrl: "",
iconX2Url: ""
)
preferences {
page name: "MainPage", install: true, uninstall: true
page name: "SunriseSunsetOptions"
page name: "ColorTemperatureOptions"
page name: "BrightnessOptions"
page name: "ModeOptions"
page name: "DisableOptions"
}
def MainPage(){
dynamicPage(name: "MainPage") {
section("
Day Lights
"){
paragraph "This application adjusts the color temperature, color, and/or the brightness of your lights based on the time of day. By default it runs from sunrise to sunset, adjusting the devices selected below. Many additional options are also available."
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
input "colorDevices", "capability.colorControl", title: "Color changing devices (RGB mode)", multiple:true
input "dimmableDevices", "capability.switchLevel", title: "Dimmable devices (requires Dynamic Brightness to be enabled)", multiple:true
input "otherSwitches", "capability.switch", title: "Other switches to turn on while Day Lights is active", multiple: true
}
section("Dynamic Brightness
") {
input "dynamicBrightness","bool", title: "Adjust brightness based on the time of day"
input "resetCT", "bool", title: "Reset the color temperature after manual dimming? (for bulbs (e.g. LIFX) that change color temperature when dimmed)"
}
section("Advanced Options
") {
href(name: "toSunriseSunsetOptions",
title: "Sunrise/Sunset Options",
page: "SunriseSunsetOptions",
description: "Set Advanced Sunrise/Sunset Options"
)
href(name: "toColorTemperatureOptions",
title: "Color Temperature Options",
page: "ColorTemperatureOptions",
description: "Set Advanced Color Temperature Options"
)
href(name: "toBrightnessOptions",
title: "Dynamic Brightness Options",
page: "BrightnessOptions",
description: "Set Brightness Options"
)
href(name: "toModeOptions",
title: "Mode Options",
page: "ModeOptions",
description: "Set Mode Options"
)
href(name: "toDisableOptions",
title: "Disable Options",
page: "DisableOptions",
description: "Set Disable 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 appliction values (Default: Off)",
defaultValue: false,
required: true,
displayDuringSetup: true
)
}
}
}
def SunriseSunsetOptions() {
dynamicPage(name: "SunriseSunsetOptions") {
section("Sunrise/Sunset Options
") {
input "useSunOverrides", "bool", title: "Use Sunrise/Sunset Overrides?"
input "sunriseOverride", "time", title: "Sunrise Override"
input "sunsetOverride", "time", title: "Sunset Override"
paragraph "
"
input "useSunOffsets", "bool", title: "Use Sunset/Sunrise Offsets (+/-)?"
input "sunriseOffset", "number", title: "Sunrise Offset (+/-)", range: "-240:240"
input "sunsetOffset", "number", title: "Sunset Offset (+/-)", range: "-240:240"
}
}
}
def ColorTemperatureOptions() {
dynamicPage(name: "ColorTemperatureOptions") {
section("Color Temperature Options
"){
paragraph "The color temperature on your devices will be adjusted from warm at sunrise to cold at midday then back to warm at sunset.
"
input "useCTOverrides", "bool", title: "Use Color Temperature Overrides?"
input "warmCTOverride", "number", title: "Warm White Temperature (default is 2700)"
input "coldCTOverride", "number", title: "Cold White Temperature (default is 6500)"
paragraph "
"
input "forceCT", "bool", title: "Force CT mode with Saturation=0? (for bulbs (e.g. LIFX) that do not automatically switch to CT mode when a color temperature is assigned)"
}
}
}
def BrightnessOptions() {
dynamicPage(name: "BrightnessOptions") {
section("Dynamic Brightness Options
") {
paragraph "When Dynamic Brightness is enabled the brightness on your devices will be adjusted from low at sunrise to high at midday then back to low at sunset.
"
if (dynamicBrightness) {
input name: "brightnessProfile", type: "enum", title: "Brightness Profile (default is Gradual)", required: true, defaultValue: 1, options:["Gradual":"Gradual - Brightening begins at sunrise and reaches maximum at midday","Accelerated":"Accelerated - Brightening begins at sunrise and reaches maximum prior to midday","Delayed":"Delayed - Remains at minimum until accelerated brightening will reach maximum at midday"]
paragraph "
"
}
input "useBrightnessOverrides", "bool", title: "Use Brightness Overrides?"
input "minBrightnessOverride","number", title: "Low Brightness (default is 1)"
input "maxBrightnessOverride","number", title: "High Brightness (default is 100)"
paragraph "
"
input "usePeriodOverrides", "bool", title: "Use Brightness Period Overrides?"
paragraph "Times outside of the sunrise/midday and midday/sunset periods will be ignored."
input "brightenTimeStart", "time", title: "Start Brightening At"
input "brightenTimeEnd", "time", title: "End Brightening At"
input "dimTimeStart", "time", title: "Start Dimming At"
input "dimTimeEnd", "time", title: "End Dimming At"
}
}
}
def ModeOptions() {
dynamicPage(name: "ModeOptions") {
section("Mode Options
") {
input "useModeOverrides", "bool", title: "Use Mode Overrides?"
}
section("Override 1") {
input "mode1Override", "mode", title: "Mode"
input "mode1OverrideColorTemperature","number", title: "Color temperature"
input "mode1OverrideValue","number", title: "Brightness"
}
section("Override 2") {
input "mode2Override", "mode", title: "Mode"
input "mode2OverrideColorTemperature","number", title: "Color temperature"
input "mode2OverrideValue","number", title: "Brightness"
}
section("Override 3") {
input "mode3Override", "mode", title: "Mode"
input "mode3OverrideColorTemperature","number", title: "Color temperature"
input "mode3OverrideValue","number", title: "Brightness"
}
section("Override 4") {
input "mode4Override", "mode", title: "Mode"
input "mode4OverrideColorTemperature","number", title: "Color temperature"
input "mode4OverrideValue","number", title: "Brightness"
}
section("Override 5") {
input "mode5Override", "mode", title: "Mode"
input "mode5OverrideColorTemperature","number", title: "Color temperature"
input "mode5OverrideValue","number", title: "Brightness"
}
section("Override 6") {
input "mode6Override", "mode", title: "Mode"
input "mode6OverrideColorTemperature","number", title: "Color temperature"
input "mode6OverrideValue","number", title: "Brightness"
}
section("Override 7") {
input "mode7Override", "mode", title: "Mode"
input "mode7OverrideColorTemperature","number", title: "Color temperature"
input "mode7OverrideValue","number", title: "Brightness"
}
section("Override 8") {
input "mode8Override", "mode", title: "Mode"
input "mode8OverrideColorTemperature","number", title: "Color temperature"
input "mode8OverrideValue","number", title: "Brightness"
}
section("Override 9") {
input "mode9Override", "mode", title: "Mode"
input "mode9OverrideColorTemperature","number", title: "Color temperature"
input "mode9OverrideValue","number", title: "Brightness"
}
section("Override 10") {
input "mode10Override", "mode", title: "Mode"
input "mode10OverrideColorTemperature","number", title: "Color temperature"
input "mode10OverrideValue","number", title: "Brightness"
}
}
}
def DisableOptions(){
dynamicPage(name: "DisableOptions") {
section("Disable Day Lights
") {
input "disablingSwitches","capability.switch", title: "Select switches that will disable Day Lights", multiple:true
input "disableWhenSwitchOff","bool", title: "Disable when off (normally disables when switch is on)"
}
section("Disable Dynamic Brightness
") {
input "disableWhenDimmed", "bool", title: "Disable Dynamic Brightness for the day when the brightness on a selected device is manually changed?"
paragraph "Dynamic Brightness will be re-enabled automatically at the next sunrise"
input "reenableDimmingTime", "time", title: "Add an additional time to re-enable Dynamic Brightness"
paragraph "If currently disabled, Dynamic Brightness will be re-enabled by clicking the Done button"
}
}
}
def installed() {
unsubscribe()
unschedule()
initialize()
}
def updated() {
unsubscribe()
unschedule()
initialize()
}
def uninstalled() {
unsubscribe()
unschedule()
}
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() {
state.scheduleActive = false
for (device in otherSwitches) {
device.off()
}
state.bypassManualOverrideCheck = true
state.disabledFromDimmer = false
state.lastAssignedBrightness = 0
logDebug("initialize() with settings: ${settings}")
subscribe(location, "sunriseTime", scheduleNextWakeup)
subscribe(app, eventApplication)
if (colorTemperatureDevices) {
subscribe(colorTemperatureDevices, "switch.on", eventDeviceOn)
if (resetCT) {
subscribe(colorTemperatureDevices, "level", eventLevel)
}
}
if (colorDevices) {
subscribe(colorDevices, "switch.on", eventDeviceOn)
}
if (dimmableDevices) {
subscribe(dimmableDevices, "switch.on", eventDeviceOn)
}
if (settings.useModeOverrides) {
subscribe(location, "mode", eventMode)
}
if (disablingSwitches) {
subscribe(disablingSwitches, "switch", eventSwitch)
}
scheduleNextWakeup()
eventHandler("Initialize")
}
def scheduleNextWakeup(evt) {
def sunriseTime
def events
if (settings.useSunOverrides && settings.sunriseOverride != null && settings.sunriseOverride != "") {
sunriseTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", settings.sunriseOverride)
if (sunriseTime < new Date()) {
sunriseTime = sunriseTime + 1
}
logDebug("Sunrise Override is ${sunriseTime}")
}
else {
if (evt != null) {
sunriseTime = toDateTime(evt.value)
}
else {
events = getLocationEventsSince("sunriseTime", new Date() - 2, [max: 1])
sunriseTime = toDateTime(events[0].value)
}
logDebug("Next system sunrise time is ${sunriseTime}")
}
if (settings.useSunOffsets && settings.sunriseOffset != null && settings.sunriseOffset != "") {
int offset = settings.sunriseOffset
use(TimeCategory) {
sunriseTime = sunriseTime + offset.minutes
}
logDebug("Sunrise offset to ${sunriseTime}")
}
schedule(sunriseTime, eventWakeup)
}
def eventApplication(evt) {
eventHandler("Application ${evt.name}(${evt.value})")
}
def eventDeviceOn(evt) {
state.bypassManualOverrideCheck = true
eventHandler("Device On")
}
def eventMode(evt) {
eventHandler("${location.mode} Mode")
}
def eventSwitch(evt) {
eventHandler("Disable Switch")
}
def eventLevel(evt) {
eventHandler("Reset Color Temperature")
}
def eventWakeup(evt) {
if (!state.scheduleActive) {
state.disabledFromDimmer = false
state.bypassManualOverrideCheck = true
eventHandler("Wakeup")
}
}
def disableDimmerOverride(evt) {
state.disabledFromDimmer = false
unschedule(disableDimmerOverride)
state.bypassManualOverrideCheck = true
eventHandler("Reset Brightness")
}
def eventHandler(evt) {
for (disableSwitch in disablingSwitches) {
if ((disableSwitch.currentSwitch == "on" && !settings.disableWhenSwitchOff) || (disableSwitch.currentSwitch == "off" && settings.disableWhenSwitchOff)) {
logDescriptionText("Currently disabled by a switch")
return
}
}
if (state.disabledFromDimmer) {
logDescriptionText("Processing(${evt}) (Dynamic Brightness disabled)")
}
else {
logDescriptionText("Processing(${evt})")
}
if (state.bypassManualOverrideCheck) {
state.bypassManualOverrideCheck = false
}
else if (settings.disableWhenDimmed && !state.disabledFromDimmer) {
for (device in colorTemperatureDevices) {
if (device.currentValue("switch") == "on" && device.currentValue("level") != null && device.currentValue("level") != state.lastAssignedBrightness) {
state.disabledFromDimmer = true
logDescriptionText("Dynamic Brightness disabled by ${device} level=${device.currentValue("level")} but was last assigned=${state.lastAssignedBrightness}")
}
}
//Some color devices don't precisely apply the HSV values, so they will not be considered for disabling Dynamic Brightness
//for (device in colorDevices) {
// if (device.currentValue("switch") == "on" && device.currentValue("level") != null && device.currentValue("level") != state.lastAssignedBrightness) {
// state.disabledFromDimmer = true
// }
//}
for (device in dimmableDevices) {
if (device.currentValue("switch") == "on" && device.currentValue("level") != null && device.currentValue("level") != state.lastAssignedBrightness) {
state.disabledFromDimmer = true
logDescriptionText("Dynamic Brightness disabled by ${device} level=${device.currentValue("level")} but was last assigned=${state.lastAssignedBrightness}")
}
}
if (state.disabledFromDimmer) {
if (settings.reenableDimmingTime != null && settings.reenableDimmingTime != "") {
def scheduleTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", settings.reenableDimmingTime)
if (scheduleTime > new Date()) {
schedule(scheduleTime, disableDimmerOverride)
}
}
logDescriptionText("Dynamic Brightness disabled until reset")
}
}
def nv = getNewValues()
def ct = nv.colorTemp
def bright = nv.brightness
def rgb = ctToRGB(ct)
def hex = rgbToHex(rgb).toUpperCase()
def hsv = rgbToHSV(rgb)
if (settings.dynamicBrightness && !state.disabledFromDimmer) {
hsv.v = bright
}
def color = [hex: hex, hue: hsv.h, saturation: hsv.s, level: hsv.v]
state.lastAssignedBrightness = bright
logEnhancedDescriptionText("CT=${ct}K, Level=${bright}%, Color=${hex}, Hue=${hsv.h}, Saturation=${hsv.s}, Value=${hsv.v}")
logDebug("Color Temperature: ${ct}")
logDebug("Brightness: ${bright}")
logDebug("Color: ${color}")
for (device in colorTemperatureDevices) {
if (device.currentValue("switch") == "on") {
if (forceCT && device.currentValue("colorMode") != "CT") {
logEnhancedDescriptionText("Attempting to force CT mode")
device.setSaturation(0)
}
if (device.currentValue("colorTemperature") != ct) {
device.setColorTemperature(ct)
}
if (settings.dynamicBrightness && !state.disabledFromDimmer && device.currentValue("level") != bright) {
device.setLevel(bright)
}
}
}
for (device in colorDevices) {
if (device.currentValue("switch") == "on") {
if (device.currentValue("color") != hex || (settings.dynamicBrightness && !state.disabledFromDimmer && device.currentValue("level") != bright)) {
device.setColor(color)
}
}
}
for (device in dimmableDevices) {
if (device.currentValue("switch") == "on") {
if (settings.dynamicBrightness && !state.disabledFromDimmer && device.currentValue("level") != bright) {
device.setLevel(bright)
}
}
}
}
def getNewValues() {
def sunriseTime = getSunriseTime()
def sunsetTime = getSunsetTime()
def midTime = sunriseTime.time + ((sunsetTime.time - sunriseTime.time) / 2)
def currentTime = now()
def workTime
def brightenStart = sunriseTime.time
def brightenEnd = midTime
def dimStart = midTime
def dimEnd = sunsetTime.time
if (settings.usePeriodOverrides) {
if (settings.brightenTimeStart != null && settings.brightenTimeStart != "") {
workTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", settings.brightenTimeStart)
if (workTime > sunriseTime) {
brightenStart = workTime.time
}
}
if (settings.brightenTimeEnd != null && settings.brightenTimeEnd != "") {
workTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", settings.brightenTimeEnd)
if (workTime.time < midTime) {
brightenEnd = workTime.time
}
}
if (settings.dimTimeStart != null && settings.dimTimeStart != "") {
workTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", settings.dimTimeStart)
if (workTime.time > midTime) {
dimStart = workTime.time
}
}
if (settings.dimTimeEnd != null && settings.dimTimeEnd != "") {
workTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", settings.dimTimeEnd)
if (workTime < sunsetTime) {
dimEnd = workTime.time
}
}
}
logDebug("sunriseTime: ${sunriseTime}")
logDebug("sunsetTime: ${sunsetTime}")
logDebug("midTime: ${midTime}")
logDebug("currentTime: ${currentTime}")
logDebug("brightenStart: ${brightenStart}")
logDebug("brightenEnd: ${brightenEnd}")
logDebug("dimStart: ${dimStart}")
logDebug("dimEnd: ${dimEnd}")
def int warmCT = 2700
def int coldCT = 6500
if (settings.useCTOverrides) {
if (settings.warmCTOverride != null && settings.warmCTOverride != "") {
warmCT = settings.warmCTOverride
}
if (settings.coldCTOverride != null && settings.coldCTOverride != "") {
coldCT = settings.coldCTOverride
}
}
def int rangeCT = coldCT - warmCT
def int minBrightness = 1
def int maxBrightness = 100
if (settings.useBrightnessOverrides) {
if (settings.minBrightnessOverride != null && settings.minBrightnessOverride > 1) {
minBrightness = settings.minBrightnessOverride
}
if (settings.maxBrightnessOverride != null && settings.maxBrightnessOverride < 100) {
maxBrightness = settings.maxBrightnessOverride
}
}
def int baseBrightness
def int rangeBrightness
switch (settings.brightnessProfile) {
case "Gradual":
baseBrightness = minBrightness
rangeBrightness = maxBrightness - minBrightness
break
case "Accelerated":
baseBrightness = minBrightness
rangeBrightness = 99
break
case "Delayed":
baseBrightness = 1
rangeBrightness = 99
break
default :
baseBrightness = minBrightness
rangeBrightness = maxBrightness - minBrightness
break
}
if (currentTime >= sunriseTime.time && currentTime <= sunsetTime.time) {
if (currentTime < midTime) {
colorTemp = warmCT + ((currentTime - sunriseTime.time) / (midTime - sunriseTime.time) * rangeCT)
if (currentTime < brightenStart) {
brightness = minBrightness
}
else if (currentTime < brightenEnd) {
brightness = baseBrightness + ((currentTime - brightenStart) / (brightenEnd - brightenStart) * rangeBrightness)
if (brightness < minBrightness) {
brightness = minBrightness
}
if (brightness > maxBrightness) {
brightness = maxBrightness
}
}
else {
brightness = maxBrightness
}
}
else {
colorTemp = warmCT + ((sunsetTime.time - currentTime) / (sunsetTime.time - midTime) * rangeCT)
if (currentTime < dimStart) {
brightness = maxBrightness
}
else if (currentTime < dimEnd) {
brightness = baseBrightness + ((dimEnd - currentTime) / (dimEnd - dimStart) * rangeBrightness)
if (brightness < minBrightness) {
brightness = minBrightness
}
if (brightness > maxBrightness) {
brightness = maxBrightness
}
}
else {
brightness = minBrightness
}
}
if (!state.scheduleActive) {
schedule("0 */5 * * * ?", eventHandler, [data: "Update Lights"])
state.scheduleActive = true
for (device in otherSwitches) {
device.on()
}
}
}
else {
if (state.scheduleActive) {
unschedule(eventHandler)
state.scheduleActive = false
for (device in otherSwitches) {
device.off()
}
}
colorTemp = warmCT
brightness = minBrightness
}
if (settings.useModeOverrides) {
switch (location.mode) {
case mode1Override:
if (settings.mode1OverrideValue != null && settings.mode1OverrideValue > 0 && settings.mode1OverrideValue <= 100) {
brightness = settings.mode1OverrideValue
}
if (settings.mode1OverrideColorTemperature != null && settings.mode1OverrideColorTemperature != "") {
colorTemp = settings.mode1OverrideColorTemperature
}
logDebug("Mode1 Override for ${location.mode} mode")
break
case mode2Override:
if (settings.mode2OverrideValue != null && settings.mode2OverrideValue > 0 && settings.mode2OverrideValue <= 100) {
brightness = settings.mode2OverrideValue
}
if (settings.mode2OverrideColorTemperature != null && settings.mode2OverrideColorTemperature != "") {
colorTemp = settings.mode2OverrideColorTemperature
}
logDebug("Mode2 Override for ${location.mode} mode")
break
case mode3Override:
if (settings.mode3OverrideValue != null && settings.mode3OverrideValue > 0 && settings.mode3OverrideValue <= 100) {
brightness = settings.mode3OverrideValue
}
if (settings.mode3OverrideColorTemperature != null && settings.mode3OverrideColorTemperature != "") {
colorTemp = settings.mode3OverrideColorTemperature
}
logDebug("Mode3 Override for ${location.mode} mode")
break
case mode4Override:
if (settings.mode4OverrideValue != null && settings.mode4OverrideValue > 0 && settings.mode4OverrideValue <= 100) {
brightness = settings.mode4OverrideValue
}
if (settings.mode4OverrideColorTemperature != null && settings.mode4OverrideColorTemperature != "") {
colorTemp = settings.mode4OverrideColorTemperature
}
logDebug("Mode4 Override for ${location.mode} mode")
break
case mode5Override:
if (settings.mode5OverrideValue != null && settings.mode5OverrideValue > 0 && settings.mode5OverrideValue <= 100) {
brightness = settings.mode5OverrideValue
}
if (settings.mode5OverrideColorTemperature != null && settings.mode5OverrideColorTemperature != "") {
colorTemp = settings.mode5OverrideColorTemperature
}
logDebug("Mode5 Override for ${location.mode} mode")
break
case mode6Override:
if (settings.mode6OverrideValue != null && settings.mode6OverrideValue > 0 && settings.mode6OverrideValue <= 100) {
brightness = settings.mode6OverrideValue
}
if (settings.mode6OverrideColorTemperature != null && settings.mode6OverrideColorTemperature != "") {
colorTemp = settings.mode6OverrideColorTemperature
}
logDebug("Mode6 Override for ${location.mode} mode")
break
case mode7Override:
if (settings.mode7OverrideValue != null && settings.mode7OverrideValue > 0 && settings.mode7OverrideValue <= 100) {
brightness = settings.mode7OverrideValue
}
if (settings.mode7OverrideColorTemperature != null && settings.mode7OverrideColorTemperature != "") {
colorTemp = settings.mode7OverrideColorTemperature
}
logDebug("Mode7 Override for ${location.mode} mode")
break
case mode8Override:
if (settings.mode8OverrideValue != null && settings.mode8OverrideValue > 0 && settings.mode8OverrideValue <= 100) {
brightness = settings.mode8OverrideValue
}
if (settings.mode8OverrideColorTemperature != null && settings.mode8OverrideColorTemperature != "") {
colorTemp = settings.mode8OverrideColorTemperature
}
logDebug("Mode8 Override for ${location.mode} mode")
break
case mode9Override:
if (settings.mode9OverrideValue != null && settings.mode9OverrideValue > 0 && settings.mode9OverrideValue <= 100) {
brightness = settings.mode9OverrideValue
}
if (settings.mode9OverrideColorTemperature != null && settings.mode9OverrideColorTemperature != "") {
colorTemp = settings.mode9OverrideColorTemperature
}
logDebug("Mode9 Override for ${location.mode} mode")
break
case mode10Override:
if (settings.mode10OverrideValue != null && settings.mode10OverrideValue > 0 && settings.mode10OverrideValue <= 100) {
brightness = settings.mode10OverrideValue
}
if (settings.mode10OverrideColorTemperature != null && settings.mode10OverrideColorTemperature != "") {
colorTemp = settings.mode10OverrideColorTemperature
}
logDebug("Mode10 Override for ${location.mode} mode")
break
default :
logDebug("No override for ${location.mode} mode")
break
}
}
else {
logDebug("Mode overrides are not active")
}
return [colorTemp: Math.round(colorTemp), brightness: Math.round(brightness)]
}
private def getSunriseTime() {
def sunRiseSet
def sunriseTime
if (settings.useSunOverrides && settings.sunriseOverride != null && settings.sunriseOverride != "") {
sunriseTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", settings.sunriseOverride)
logDebug("Sunrise Override is ${sunriseTime}")
}
else {
sunRiseSet = getSunriseAndSunset()
sunriseTime = sunRiseSet.sunrise
logDebug("System Sunrise time is ${sunriseTime}")
}
if (settings.useSunOffsets && settings.sunriseOffset != null && settings.sunriseOffset != "") {
int offset = settings.sunriseOffset
use(TimeCategory) {
sunriseTime = sunriseTime + offset.minutes
}
logDebug("Sunrise offset to ${sunriseTime}")
}
return sunriseTime
}
private def getSunsetTime(){
def sunRiseSet
def sunsetTime
if (settings.useSunOverrides && settings.sunsetOverride != null && settings.sunsetOverride != "") {
sunsetTime = new Date().parse("yyyy-MM-dd'T'HH:mm:ss.SSSZ", settings.sunsetOverride)
logDebug("Sunset Override is ${sunsetTime}")
}
else {
sunRiseSet = getSunriseAndSunset()
sunsetTime = sunRiseSet.sunset
logDebug("System Sunset time is ${sunsetTime}")
}
if (settings.useSunOffsets && settings.sunsetOffset != null && settings.sunsetOffset != "") {
int offset = settings.sunsetOffset
use(TimeCategory) {
sunsetTime = sunsetTime + offset.minutes
}
logDebug("Sunset offset to ${sunsetTime}")
}
return sunsetTime
}
def ctToRGB(ct) {
if(ct < 1000) { ct = 1000 }
if(ct > 40000) { ct = 40000 }
ct = ct / 100
//red
def r
if(ct <= 66) { r = 255 }
else { r = 329.698727446 * ((ct - 60) ** -0.1332047592) }
if(r < 0) { r = 0 }
if(r > 255) { r = 255 }
//green
def g
if (ct <= 66) { g = 99.4708025861 * Math.log(ct) - 161.1195681661 }
else { g = 288.1221695283 * ((ct - 60) ** -0.0755148492) }
if(g < 0) { g = 0 }
if(g > 255) { g = 255 }
//blue
def b
if(ct >= 66) { b = 255 }
else if(ct <= 19) { b = 0 }
else { b = 138.5177312231 * Math.log(ct - 10) - 305.0447927307 }
if(b < 0) { b = 0 }
if(b > 255) { b = 255 }
def rgb = [:]
rgb = [r: r as Integer, g: g as Integer, b: b as Integer]
rgb
}
def rgbToHex(rgb) {
return "#" + Integer.toHexString(rgb.r).padLeft(2,'0') + Integer.toHexString(rgb.g).padLeft(2,'0') + Integer.toHexString(rgb.b).padLeft(2,'0')
}
def rgbToHSV(rgb) {
def h, s, v
def r = rgb.r / 255
def g = rgb.g / 255
def b = rgb.b / 255
def max = [r, g, b].max()
def min = [r, g, b].min()
def delta = max - min
//hue
if(delta == 0) { h = 0}
else if(max == r) {
double dub = (g - b) / delta
h = 60 * (dub % 6)
}
else if(max == g) { h = 60 * (((b - r) / delta) + 2) }
else if(max == b) { h = 60 * (((r - g) / delta) + 4) }
//saturation
if(max == 0) { s = 0 }
else { s = (delta / max) * 100 }
//value
v = max * 100
logDebug("r=${rgb.r}, g=${rgb.g}, b=${rgb.b}, max=${max}, min=${min}, delta=${delta}, h=${Math.round(h)}, s=${Math.round(s)}, v=${Math.round(v)}")
return [h: Math.round(h / 3.6), s: Math.round(s), v: Math.round(v)]
}