If I find some time this week, I might see if I can figure out how to add an override option.
I'm not sure why the overrides aren't functioning as expected. I had to take a few years off from this hobby.
That being said, there was a new version of this app in the works. I'm hoping to finish that at some point this year...
I'll do a write-up at some point in the next couple weeks about what's been happening in my life.
Thanks Adam, hope you are ok?
So if I understand you correctly, CD shouldn’t touch the brightness, unless you turn on dynamic brightness?
I'm okay, now! Rough few years though.
Dynamic brightness, or brightness overrides, will modify brightness.
If you just want to disable brightness modifications entirely, comment out line 459. It'll look like this.
459 // colorTemperatureDevice.setLevel(bright)
Cheers, I’ll take a look tomorrow, I need to get some sleep (1am here on Melbourne).
One thing I have found that works is using a virtual bulb with Circadian Daylight as an in-between the app and your real bulbs. Basically, I've run it in two different ways that gets around the brightness issue. In both instances, CD sets the color temperature of a virtual bulb and I do one of the following:
1.) Set up a Hub Variable for color temperature that changes as the virtual bulb changes. Then I use the variable in Room Lighting and other places as necessary.
2.) Use [PRERELEASE] Virtual Prestaging as an in-between the virtual bulb and my real bulbs. I can set Virtual Prestaging to just sync color temperature and ignore brightness.
As I do not know the first thing about coding, I've had to get creative with the brightness bug in CD; otherwise, I love the app!
I actually do both. By having the value in the variable so Room Lights et al. send the CT to the bulb when it turns on, it reduces the flash of the old color temperature. Then Virtual Prestaging takes over updating the color temperature once the light is on.
I just have Rule Machine handle the change of CT over time:
I had a quick play this morning and just commenting out 459 didn’t help. But I’ll investigate further today.
Long time lurker, first time poster...
I installed Adam's app a month or two ago just to see how I liked having the bulb in my workspace change CT as the day went on. I then jumped into the code to try to change one little thing. I got hooked on the development aspect and I've ended up spending the last month tinkering with it. Well, to be honest I've basically re-written the whole thing. I've worked through the to-do list in Adam's code and added a few things for myself. At this point I think it's in pretty stable shape, at least for the functionality that I use.
To address a couple of points in this topic, I have completely separated the CT updates from the brightness updates. If you turn off Dynamic Brightness it will continue to change the CT. Also, I tamed that jump to 100% at the end of the day (this was my original reason for taking a look at the code). Finally, it will not attempt to change a bulb that is off, and will apply the current values when it is turned on without thinking that the brightness was manually overridden, thus disabling the updates.
I'd be happy to share my code with Adam or anyone else that's interested. I renamed the app so it won't stomp on any current instances of CD, but I think the look and feel are pretty similar. I don't have a github account so I don't know which distribution would work best. And again, it's working well for my particular settings, but no comprehensive testing has been done.
@OldChicagoPete I'd be willing to take your app for a spin if you wanted a tester in a different environment. I've got a vacant guest room with hue color bulb that would work nicely since it is not currently in use (i.e. about 6 weeks of testing without bothering the family in more common spaces). Feel free to shoot me a message if you are interested.
After some tinkering, I found that the culprit for colour bulbs is line 472. Commenting this out prevents CD from messing with the brightness.
Edit; actually that didn’t help.
I sent you a message, but it looks like the code didn't like the message format and may be useless. I'll see if I can get it in a more stable format.
For those interested, I'm attaching the code for my take on Adam's app. Thanks to @JB10 for giving it a pretty good shakedown.
As I mentioned before, the CT updates happen independently of the brightness functionality. So if you have Dynamic Brightness turned off, or a manual brightness update has occurred to suspend automatic updates, the CT will continue to change throughout the day.
I don't know if I can provide the level of timely support that I've seen from a lot of the great developers here, but I will certainly do my best to answer any questions and am open to suggestions.
/**
* Day Lights
*
* Author:
* OldChicagoPete
*
* Documentation: Future Hubitat Community Post
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* 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/ *
* *
* Other credits from the old source code *
* *
* 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
*/
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("<h2>Day Lights</h2>"){
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("<h2>Select Devices</h2>") {
paragraph "Individual devices should only be selected for one group"
input "colorTemperatureDevices", "capability.colorTemperature", title: "<b>Color temperature devices</b>", multiple:true
input "colorDevices", "capability.colorControl", title: "<b>Color changing devices</b>", multiple:true
input "dimmableDevices", "capability.switchLevel", title: "<b>Dimmable devices (requires Dynamic Brightness to be enabled)</b>", multiple:true
}
section("<h2>Dynamic Brightness</h2>") {
input "dynamicBrightness","bool", title: "Adjust brightness based on the time of day"
}
section("<h2>Advanced Options</h2>") {
href(name: "toSunriseSunsetOptions",
title: "<b>Sunrise/Sunset Options</b>",
page: "SunriseSunsetOptions",
description: "Set Advanced Sunrise/Sunset Options"
)
href(name: "toColorTemperatureOptions",
title: "<b>Color Temperature Options</b>",
page: "ColorTemperatureOptions",
description: "Set Advanced Color Temperature Options"
)
href(name: "toBrightnessOptions",
title: "<b>Dynamic Brightness Options</b>",
page: "BrightnessOptions",
description: "Set Brightness Options"
)
href(name: "toModeOptions",
title: "<b>Mode Options</b>",
page: "ModeOptions",
description: "Set Mode Options"
)
href(name: "toDisableOptions",
title: "<b>Disable Options</b>",
page: "DisableOptions",
description: "Set Disable Options"
)
}
section("<h2>Logging</h2>") {
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("<h2>Sunrise/Sunset Options</h2>") {
input "useSunOverrides", "bool", title: "<b>Use Sunrise/Sunset Overrides?</b>"
input "sunriseOverride", "time", title: "Sunrise Override"
input "sunsetOverride", "time", title: "Sunset Override"
paragraph "<br>"
input "useSunOffsets", "bool", title: "<b>Use Sunset/Sunrise Offsets (+/-)?</b>"
input "sunriseOffset", "decimal", title: "Sunrise Offset (+/-)"
input "sunsetOffset", "decimal", title: "Sunset Offset (+/-)"
}
}
}
def ColorTemperatureOptions() {
dynamicPage(name: "ColorTemperatureOptions") {
section("<h2>Color Temperature Options</h2>"){
paragraph "The color temperature on your devices will be adjusted from warm at sunrise to cold at midday then back to warm at sunset.<br><br>"
input "useCTOverrides", "bool", title: "<b>Use Color Temperature Overrides?</b>"
input "warmCTOverride", "number", title: "Warm White Temperature (default is 2700)"
input "coldCTOverride", "number", title: "Cold White Temperature (default is 6500)"
}
}
}
def BrightnessOptions() {
dynamicPage(name: "BrightnessOptions") {
section("<h2>Dynamic Brightness Options</h2>") {
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.<br><br>"
input name: "brightnessProfile", type: "enum", title: "<b>Brightness Profile</b> (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 "<br>"
input "useBrightnessOverrides", "bool", title: "<b>Use Brightness Overrides?</b>"
input "minBrightnessOverride","number", title: "Low Brightness (default is 1)"
input "maxBrightnessOverride","number", title: "High Brightness (default is 100)"
paragraph "<br>"
input "usePeriodOverrides", "bool", title: "<b>Use Brightness Period Overrides?</b>"
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("<h2>Mode Options</h2>") {
input "useModeOverrides", "bool", title: "<b>Use Mode Overrides?</b>"
}
section("<b>Override 1</b>") {
input "mode1Override", "mode", title: "Mode"
input "mode1OverrideColorTemperature","number", title: "Color temperature"
input "mode1OverrideValue","number", title: "Brightness"
}
section("<b>Override 2</b>") {
input "mode2Override", "mode", title: "Mode"
input "mode2OverrideColorTemperature","number", title: "Color temperature"
input "mode2OverrideValue","number", title: "Brightness"
}
section("<b>Override 3</b>") {
input "mode3Override", "mode", title: "Mode"
input "mode3OverrideColorTemperature","number", title: "Color temperature"
input "mode3OverrideValue","number", title: "Brightness"
}
section("<b>Override 4</b>") {
input "mode4Override", "mode", title: "Mode"
input "mode4OverrideColorTemperature","number", title: "Color temperature"
input "mode4OverrideValue","number", title: "Brightness"
}
section("<b>Override 5</b>") {
input "mode5Override", "mode", title: "Mode"
input "mode5OverrideColorTemperature","number", title: "Color temperature"
input "mode5OverrideValue","number", title: "Brightness"
}
section("<b>Override 6</b>") {
input "mode6Override", "mode", title: "Mode"
input "mode6OverrideColorTemperature","number", title: "Color temperature"
input "mode6OverrideValue","number", title: "Brightness"
}
section("<b>Override 7</b>") {
input "mode7Override", "mode", title: "Mode"
input "mode7OverrideColorTemperature","number", title: "Color temperature"
input "mode7OverrideValue","number", title: "Brightness"
}
section("<b>Override 8</b>") {
input "mode8Override", "mode", title: "Mode"
input "mode8OverrideColorTemperature","number", title: "Color temperature"
input "mode8OverrideValue","number", title: "Brightness"
}
section("<b>Override 9</b>") {
input "mode9Override", "mode", title: "Mode"
input "mode9OverrideColorTemperature","number", title: "Color temperature"
input "mode9OverrideValue","number", title: "Brightness"
}
section("<b>Override 10</b>") {
input "mode10Override", "mode", title: "Mode"
input "mode10OverrideColorTemperature","number", title: "Color temperature"
input "mode10OverrideValue","number", title: "Brightness"
}
}
}
def DisableOptions(){
dynamicPage(name: "DisableOptions") {
section("<h2>Disable Day Lights</h2>") {
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("<h2>Disable Dynamic Brightness</h2>") {
input "disableWhenDimmed", "bool", title: "<b>Disable Dynamic Brightness for the day when the brightness on a selected device is manually changed?</b>"
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
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 (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 != "") {
sunriseTime = sunriseTime.plusMinutes(settings.sunriseOffset)
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 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 Brighness")
}
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") != state.lastAssignedBrightness) {
state.disabledFromDimmer = true
}
}
for (device in colorDevices) {
if (device.currentValue("switch") == "on" && device.currentValue("level") != state.lastAssignedBrightness) {
state.disabledFromDimmer = true
}
}
for (device in dimmableDevices) {
if (device.currentValue("switch") == "on" && device.currentValue("level") != state.lastAssignedBrightness) {
state.disabledFromDimmer = true
}
}
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)
def color = [hex: hex, hue: hsv.h, saturation: hsv.s, level: bright]
state.lastAssignedBrightness = bright
logEnhancedDescriptionText("CT=${ct}K, Level=${bright}%, Color=${hex}, Hue=${hsv.h}, Saturation=${hsv.s}")
logDebug("Color Temperature: ${ct}")
logDebug("Brightness: ${bright}")
logDebug("Color: ${color}")
for (device in colorTemperatureDevices) {
if (device.currentValue("switch") == "on") {
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)) {
if (!settings.dynamicBrightness || state.disabledFromDimmer) {
color.level = device.currentValue("level")
}
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
}
}
else {
if (state.scheduleActive) {
unschedule(eventHandler)
state.scheduleActive = false
}
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 != "") {
sunriseTime = sunriseTime.plusMinutes(settings.sunriseOffset)
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 != "") {
sunsetTime = sunsetTime.plusMinutes(settings.sunsetOffset)
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
def degreesRange = (360 - 0)
def percentRange = (100 - 0)
return [h: ((h * percentRange) / degreesRange) as Integer, s: ((s * percentRange) / degreesRange) as Integer, v: v as Integer]
}
I just had a quick play with Daylights on my Study Lamp (Colour LiFX Bulb). If I set the brightness to 70%, Daylights overrides it. eg below it went to 77%.
Am I doing something wrong?
app:44242023-11-03 02:36:50.454 PMdebugDay Lights (Day Lights): Color: [hex:#FFEFE2, hue:7, saturation:3, level:77]
app:44242023-11-03 02:36:50.452 PMdebugDay Lights (Day Lights): Brightness: 77
app:44242023-11-03 02:36:50.451 PMdebugDay Lights (Day Lights): Color Temperature: 5631
app:44242023-11-03 02:36:50.449 PMinfoDay Lights (Day Lights): CT=5631K, Level=77%, Color=#FFEFE2, Hue=7, Saturation=3
app:44242023-11-03 02:36:50.444 PMdebugDay Lights (Day Lights): Mode overrides are not active
app:44242023-11-03 02:36:50.407 PMdebugDay Lights (Day Lights): dimEnd: 1699001700000
app:44242023-11-03 02:36:50.406 PMdebugDay Lights (Day Lights): dimStart: 1698976950000
app:44242023-11-03 02:36:50.404 PMdebugDay Lights (Day Lights): brightenEnd: 1698976950000
app:44242023-11-03 02:36:50.403 PMdebugDay Lights (Day Lights): brightenStart: 1698952200000
app:44242023-11-03 02:36:50.402 PMdebugDay Lights (Day Lights): currentTime: 1698982610397
app:44242023-11-03 02:36:50.401 PMdebugDay Lights (Day Lights): midTime: 1698976950000
app:44242023-11-03 02:36:50.399 PMdebugDay Lights (Day Lights): sunsetTime: Fri Nov 03 19:55:00 AEDT 2023
app:44242023-11-03 02:36:50.398 PMdebugDay Lights (Day Lights): sunriseTime: Fri Nov 03 06:10:00 AEDT 2023
app:44242023-11-03 02:36:50.395 PMdebugDay Lights (Day Lights): System Sunset time is Fri Nov 03 19:55:00 AEDT 2023
app:44242023-11-03 02:36:50.387 PMdebugDay Lights (Day Lights): System Sunrise time is Fri Nov 03 06:10:00 AEDT 2023
app:44242023-11-03 02:36:50.380 PMinfoDay Lights (Day Lights): Processing(Initialize)
If I understand correctly, take a look at the Disable Options. There's a switch to disable Dynamic Brightness when one of your devices has been manually set to a different brightness.
Oh ok. I'd have thought If Dynamic Brightness was off, then Daylights wouldn't touch it? Seems unintuitive that I then need to turn on additional disable options?
I didn't catch that this was a color type situation and that you had DB off. You are correct, it shouldn't change the brightness in that case.
I'm at the end of my day here, but I will run this test tomorrow and get you an update.
Cheers, I did try the additional disable option and it seems to work well.
Your app would suit my needs perfectly if "Dynamic Brightness = Off ", didn't change the Brightness at all. At least until the bulb was power cycled.
Nice work.
Ah, fresh eyes!
I haven't tested it yet, but it looks like I have a typo on line 478...
color.value = device.currentValue("level")
should be
color.level = device.currentValue("level")
If that does the trick for you I'll edit my post to correct the code. I'll also set up a test myself once I get settled in.
I ran a test with a physical color bulb and a virtual color bulb. I was able to re-create your issue with both.
After the change on line 478 the virtual bulb is working perfectly. My physical bulb is a cheap Tuya-type that I have set up with the Tuya Cloud Driver from the community. When I specify a brightness level manually the bulb ends up with a value of one less. I set it to 40 and the bulb reports back that it's at 39. Not a big deal for a one-time event but as the app processes every five minutes setting the bulb to its current brightness level that causes it to actually slowly creep lower.
Hopefully your LiFX bulb doesn't have this same problem. I'm going to continue to think about a way to work around my Tuya issue. But in the meantime I will edit my code above with the change to line 478.