As most people probably know, lights has a very important impact on sleep (https://www.sleepfoundation.org/bedroom ... -and-sleep), or as the page linked in this sentence mentions
At the time of writing, which is during the covid-19 pandemic, some of us are working at home and may therefore be unable to go outside during the day. Or maybe you just don't feel like going outside (I'm not judging). Whatever your reason, you are staying inside but also like to reduce the negative impact that artificial lighting has on your sleep. Wouldn't it be nice if there were some way to mimic the sun indoors? That is where this script comes in. It uses weather data and RGBW(W) lights to mimic the colour temperature of the sunlight as best as possible.When exposed to only natural light, a person’s circadian rhythm becomes closely synchronized with sunrise and sunset3, staying awake during the day and sleeping when it’s dark. In modern society, though, electricity creates an abundance of light sources that affect the brain’s circadian pacemaker.
Note 1) I am no medical profession and have not done any in depth research on the health related effects. I'm operating under the assumption that mimicking sunlight as best as possible will reduce negative effects on sleep, but it is an assumption. If you are a medical professional or an expert in fields related, please contact me by PM, rather than starting a discussion in the thread.
Note 2) The code only tries to mimic the colour temperature of the sunlight. It does not adjust the brightness to mimic outdoor conditions.
Acknowledgments
I would like to thank the maker (Alex Mellnik) of the EdiSun project on Github (https://github.com/amellnik/EdiSun). Without his code, this entire project would not have been possible. All I did was take his code, written in Julia, and adapt it so it could be used in dzVents. That's it. All the rest is his work. All credit goes to him.
License
Since the work is based on GPL v2 licensed code, everything here is also using the GPL v2 license (https://www.gnu.org/licenses/old-licenses/gpl-2.0.html).
How to use
- Sign-up for an account at openweathermap.org (https://home.openweathermap.org/users/sign_up
- Verify your email address
- Replace the <CITY> in the code with your city and the <API_KEY> with the key you receive by e-mail.
- Replace the maxTemp and minTemp variable values by the minimun and maximum colour temperature values your lights are specced at.
- Replace the devId by the id of your light.
- Optionally: calibrate the colour temperature output (see calibration section)
Code: Select all
return {
on = {
timer = { 'every 10 minutes' },
httpResponses = { 'gotWeather' }
},
execute = function(domoticz, item)
----------------------
-- Helper functions --
----------------------
local function getTemp(mult)
-- Calculates the color temperature of the sunlight outside
local currentTime = domoticz.time.minutesSinceMidnight
local sunriseTime = domoticz.time.sunriseInMinutes
local sunsetTime = domoticz.time.sunsetInMinutes
local percentDay = (currentTime - sunriseTime)/(sunsetTime - sunriseTime)
if (percentDay >= 0 and percentDay <= 1) then
return mult*(6990.5 +
percentDay*74508 +
percentDay*percentDay*-4.5021e5 +
percentDay*percentDay*percentDay*9.5966e5 +
percentDay*percentDay*percentDay*percentDay*-8.6916e5 +
percentDay*percentDay*percentDay*percentDay*percentDay*2.8073e5)
else
return 2519
end
end
local function getMultFromCode(code)
-- Calculates a multiplier based on the type of weather
if (math.floor(code/100) == 2) then
return 1.8
elseif (math.floor(code/100) == 3) then
return 1.3
elseif (math.floor(code/100) == 5) then
return 1.5
elseif (math.floor(code/100) == 7) then
return 1.74
else
return -1
end
end
local function getKelvinFromTemp(temp)
local maxTemp = 6500
local minTemp = 2000
if (temp > maxTemp) then
return 0
elseif (temp < minTemp) then
return 100
else
local kelvin = (temp - minTemp)/(maxTemp - minTemp)
return math.floor(kelvin + 0.5)
end
end
-------------------
-- Main function --
-------------------
if (item.isTimer) then
domoticz.openURL({
url = 'https://api.openweathermap.org/data/2.5/weather?q=<CITY>&appid=<API_KEY>',
method = 'GET',
callback = 'gotWeather'
})
elseif (item.isHTTPResponse) then
if (item.ok) then
local code = item.json["weather"][1]["id"]
if (code ~= nil) then
mult = getMultFromCode(code)
else
-- Don't have a code, estimate from cloud cover
local coverage = item.json.clouds.all
mult = 1 + 0.4*coverage/100
end
colorTemp = getTemp(mult)
local k = getKelvinFromTemp(colorTemp)
local devId = 1
if (domoticz.devices(devId).active) then
domoticz.devices(devId).setWhiteMode()
domoticz.devices(devId).setKelvin(k)
domoticz.devices(devId).dimTo(100)
end
end
end
end
}
The code above assumes that the colour temperature output of the light follows a linear distribution. As an example, if you have a light that has a minimum colour temperature of 1800K and a maximum of 6500K, it assumes that setKelvin(0) = 1800K, setKelvin(10) = 2270K, setKelvin(20) = 2740K, setKelvin(30) = 3210K, ..., setKelvin(90) = 6030K, setKelvin(100) = 6500K. In other words, increasing setKelvin by 1% should increase the colour temperature by 1%.
This is however rarely the case. For example, my light bulbs, the Innr rb 285 c-2, follows a graph that looks more like this:

So the change in colour temperature is much larger in the beginning and smaller towards the end. The line shows a third order polynomial fit and the black dots show the actual measured data points.
You can calibrate your own lights by using a camera, pointing it at a white wall and reading out the automatically chosen colour temperatures, while using the setKelvin function to change the output of the light bulb. This is not super accurate, but better than having no calibration at all. If somebody knows of a better way to measure the colour temperature cheaply I'd love to hear it by the way!
Using the measured points, you can fit a curve through them and use that to adjust your lights. If you don't know how to do curve fitting, please send me a PM with the measuring points you have and I will do it for you if/when I have time.
This is the code I used for calibrating my light bulbs:
Code: Select all
local function getKelvinFromTemp(temp)
if (temp > 5150) then
return 0
elseif (temp < 2050) then
return 100
else
-- Normalized color temperature
local tn = (temp - 3183)/955.3
local kelvin = -3.85*tn*tn*tn + 12.43*tn*tn - 29.86*tn + 40.41
return math.floor(kelvin + 0.5)
end
end
If you have improvements or suggestions you can let me know here in the thread or by PM. I probably won't have time to program these myself however, so if you know how to code please code it yourself and then share it here so everybody can benefit. Be aware that I may incorporate anything shared by thread or PM in the main post - unless you specifically request I do not - so the code will end up under the GPL v2 license.
That is all folks! I hope you found this post useful.