Last active
February 26, 2020 14:07
-
-
Save zkniebel/1258e7779141042e9e43e481438f6347 to your computer and use it in GitHub Desktop.
Node.js helper functions meant to support the creation of automated circadian lighting scripts. These scripts are based on a port of the Python code from claytonjn's hass-circadian_lighting repository (https://github.com/claytonjn/hass-circadian_lighting)
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
/** | |
* CREDIT: The below is a modified JS port of the Python code from claytonjn's hass-circadian_lighting repository | |
* URL : https://github.com/claytonjn/hass-circadian_lighting | |
*/ | |
// weather is expected to be a darksky weather response JSON object with at least today and tomorrow as daily forcast objects | |
// NOTE: this implementation could be made more accurate by making a Time Machine request to darksky, but this would be an | |
// additional API call and could possibly result in hitting the daily API call limit. As such, yesterday's weather is | |
// optional and can be omitted in favor of an estimation based on today's values if desired | |
function getPercent(weather, yesterdayWeather) { | |
var sunTimes = { | |
today: { | |
sunrise: weather.daily.data[0].sunriseTime, | |
sunset: weather.daily.data[0].sunsetTime | |
}, | |
tomorrow: { | |
sunrise: weather.daily.data[1].sunriseTime, | |
sunset: weather.daily.data[1].sunsetTime | |
}, | |
dayAfterTomorrow: { | |
sunrise: weather.daily.data[2].sunriseTime | |
} | |
}; | |
// if historical data was provided | |
if (yesterdayWeather) { | |
sunTimes.yesterday = { | |
sunset: yesterdayWeather.daily.data[0].sunsetTime | |
}; | |
} else { | |
// estimated by using today's sunset time - seconds/day | |
sunTimes.yesterday = { | |
sunset: sunTimes.today.sunset - 86400 | |
}; | |
} | |
// set relevant solar noon and solar midnight properties | |
// yesterday (only need solar midnight) | |
sunTimes.yesterday.solarMidnight = Math.round( | |
sunTimes.yesterday.sunset + (sunTimes.today.sunrise - sunTimes.yesterday.sunset) / 2); | |
// today | |
sunTimes.today.solarNoon = Math.round( | |
sunTimes.today.sunrise + (sunTimes.today.sunset - sunTimes.today.sunrise) / 2); | |
sunTimes.today.solarMidnight = Math.round( | |
sunTimes.today.sunset + (sunTimes.tomorrow.sunrise - sunTimes.today.sunset) / 2); | |
// tomorrow | |
sunTimes.tomorrow.solarNoon = Math.round( | |
sunTimes.tomorrow.sunrise + (sunTimes.tomorrow.sunset - sunTimes.tomorrow.sunrise) / 2); | |
sunTimes.tomorrow.solarMidnight = Math.round( | |
sunTimes.tomorrow.sunset + (sunTimes.dayAfterTomorrow.sunrise - sunTimes.tomorrow.sunset) / 2); | |
// set chosen time defaults | |
var now = Math.round(new Date().getTime() / 1000); | |
var sunrise = sunTimes.today.sunrise; | |
var sunset = sunTimes.today.sunset; | |
var solarNoon = sunTimes.today.solarNoon; | |
var solarMidnight = sunTimes.today.solarMidnight; | |
if (now < sunrise) { // if before sunrise/after midnight | |
// it's before sunrise/after midnight -> sunset must have happend yesterday | |
sunset = sunTimes.yesterday.sunset; | |
if (sunTimes.today.solarMidnight > sunTimes.today.sunset | |
&& sunTimes.yesterday.solarMidnight > sunTimes.yesterday.sunset) { | |
// solar midnight is after sunset -> use yesterdays's time | |
solarMidnight = sunTimes.yesterday.solarMidnight; | |
} | |
} else if (now > sunset) { // if after sunset/before midnight | |
// it's after sunset/before midnight -> sunrise must happen tomorrow | |
sunrise = sunTimes.tomorrow.sunrise; | |
if (sunTimes.today.solarMidnight < sunTimes.today.sunrise | |
&& sunTimes.tomorrow.solarMidnight < sunTimes.tomorrow.sunrise) { | |
// solar midnight is before sunrise -> use tomorrow's time | |
solarMidnight = sunTimes.tomorrow.solarMidnight; | |
} | |
} | |
// generate the parabola based on current time and whether it's before/after sunset/sunrise and solar noon/midnight | |
var h, | |
k, | |
x, | |
y; | |
// sunrise-sunset parabola | |
if (now > sunrise && now < sunset) { | |
h = solarNoon; | |
k = 100; | |
y = 0; | |
// if before solar noon | |
if (now < solarNoon) { | |
x = sunrise; | |
} else { | |
x = sunset; | |
} | |
// sunset-sunrise parabola | |
} else if (now > sunset && now < sunrise) { | |
h = solarMidnight; | |
k = -100; | |
y = 0; | |
// if before solar midnight | |
if (now < solarMidnight) { | |
x = sunset; | |
} else { | |
x = sunrise; | |
} | |
} | |
return ((y - k) / Math.pow(h - x, 2)) * Math.pow(now - h, 2) + k; | |
} | |
function getColorTemp(percent, minColorTemp, maxColorTemp) { | |
if (percent > 0) { | |
return ((maxColorTemp - minColorTemp) * (percent / 100)) + minColorTemp; | |
} | |
return minColorTemp; | |
} | |
function getBrightness(percent, minBrightness, maxBrightness) { | |
const hueMaxBrightness = 255; | |
if (percent <= 0) { | |
return minBrightness / hueMaxBrightness * 100; | |
} else { | |
var britValue = Math.round(((maxBrightness - minBrightness) * percent / 100) + minBrightness); | |
var britPercentage = Math.round(britValue / hueMaxBrightness * 100); | |
return britPercentage; | |
} | |
} |
Added in missing function for getting brightness (was accidentally excluded from original)
Updated brightness function to more clearly produce two possible values: a brightness value (within the range of [minBrightness,maxBrightness]
) and percentage. I use the percentage and previously included that logic outside of the functions in this gist, but in retrospect I didn't think this was as clear or legible as including both as variables within the function. Feel free to modify at your discretion.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For those interested in using this gist, note that
getColorTemp
returns the color temperature in Kelvin, and also expects theminColorTemp
andmaxColorTemp
parameters to be in Kelvin. For my use case, I pass2500
as theminColorTemp
and5500
as themaxColorTemp
. Feel free to pass your own values as desired.