mirror of
https://github.com/basnijholt/adaptive-lighting.git
synced 2026-09-11 22:34:04 +02:00
Re-write of values calculation
The basis of Circadian Lighting is to figure out how to adjust lights to mimic the sun, which is roughly parabolic above and below the horizon. Originally I used three points to calculate the parabolas - sunrise, sunset, and solar noon/midnight. I did a bunch of generic arithmetic to generate the parabola from those three points. The problem is that this arithmetic generates a parabola from _any_ three points - in reality the points I have are the intercepts and vertex. The problem with the old method is that if solar noon/midnight did not fall exactly between sunrise and sunset then the calculated vertex could be more or less than 100%, which would cause the integration to set color/brightness values above or below the set min/max. Generating a parabola from the intercepts and vertex is much simpler and also should guarantee the min/max are what's expected. In addition to this change, I'm also now calculating 4 "half" parabolas instead of just one for above horizon and one for below horizon. The "half" parabolas are: sunrise->solar-noon, solar-noon->sunset, sunset->solar-midnight, solar-midnight->sunrise. This is necessary for the case where solar noon/midnight don't fall exactly half way between sunrise and sunset, which is especially prevalent for users who specify a specific time for sunrise or sunset but not both. I'm embarrassed at how it's been before it dawned on me that I could calculate the parabolas from intercepts/vertex rather than the three random points method, but hopefully this fixes inconsistencies for users - it should definitely fix issue #25!
This commit is contained in:
parent
34d5529a05
commit
4f332a514c
2 changed files with 65 additions and 64 deletions
|
|
@ -48,7 +48,7 @@ from homeassistant.util.dt import utcnow as dt_utcnow, as_local
|
|||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
VERSION = '1.0.4'
|
||||
VERSION = '1.0.5'
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -199,77 +199,78 @@ class CircadianLighting(object):
|
|||
def calc_percent(self):
|
||||
utcnow = dt_utcnow()
|
||||
now = as_local(utcnow)
|
||||
_LOGGER.debug("now: " + str(now))
|
||||
|
||||
today_sun_times = self.get_sunrise_sunset(now)
|
||||
_LOGGER.debug("today_sun_times: " + str(today_sun_times))
|
||||
|
||||
# Convert everything to epoch timestamps for easy calculation
|
||||
now_seconds = now.timestamp()
|
||||
today_sunrise_seconds = today_sun_times[SUN_EVENT_SUNRISE].timestamp()
|
||||
today_sunset_seconds = today_sun_times[SUN_EVENT_SUNSET].timestamp()
|
||||
today_solar_noon_seconds = today_sun_times['solar_noon'].timestamp()
|
||||
today_solar_midnight_seconds = today_sun_times['solar_midnight'].timestamp()
|
||||
sunrise_seconds = today_sun_times[SUN_EVENT_SUNRISE].timestamp()
|
||||
sunset_seconds = today_sun_times[SUN_EVENT_SUNSET].timestamp()
|
||||
solar_noon_seconds = today_sun_times['solar_noon'].timestamp()
|
||||
solar_midnight_seconds = today_sun_times['solar_midnight'].timestamp()
|
||||
|
||||
_LOGGER.debug("now: " + str(now) + "\n\n today_sun_times: " + str(today_sun_times))
|
||||
|
||||
if now < today_sun_times[SUN_EVENT_SUNRISE]:
|
||||
if now < today_sun_times[SUN_EVENT_SUNRISE]: # It's before sunrise (after midnight)
|
||||
# Because it's before sunrise (and after midnight) sunset must have happend yesterday
|
||||
yesterday_sun_times = self.get_sunrise_sunset(now - timedelta(days=1))
|
||||
yesterday_sunrise_seconds = yesterday_sun_times[SUN_EVENT_SUNRISE].timestamp()
|
||||
yesterday_sunset_seconds = yesterday_sun_times[SUN_EVENT_SUNSET].timestamp()
|
||||
yesterday_solar_midnight_seconds = yesterday_sun_times['solar_midnight'].timestamp()
|
||||
|
||||
_LOGGER.debug("yesterday_sun_times: " + str(yesterday_sun_times))
|
||||
|
||||
x1 = yesterday_sunset_seconds
|
||||
y1 = 0
|
||||
|
||||
if today_sun_times['solar_midnight'] > yesterday_sun_times[SUN_EVENT_SUNSET] and today_sun_times['solar_midnight'] < today_sun_times[SUN_EVENT_SUNRISE]:
|
||||
x2 = today_solar_midnight_seconds
|
||||
else:
|
||||
x2 = yesterday_solar_midnight_seconds
|
||||
y2 = -100
|
||||
|
||||
x3 = today_sunrise_seconds
|
||||
y3 = 0
|
||||
elif now > today_sun_times[SUN_EVENT_SUNSET]:
|
||||
sunset_seconds = yesterday_sun_times[SUN_EVENT_SUNSET].timestamp()
|
||||
if today_sun_times['solar_midnight'] > today_sun_times[SUN_EVENT_SUNSET] and yesterday_sun_times['solar_midnight'] > yesterday_sun_times[SUN_EVENT_SUNSET]:
|
||||
# Solar midnight is after sunset so use yesterdays's time
|
||||
solar_midnight_seconds = yesterday_sun_times['solar_midnight'].timestamp()
|
||||
elif now > today_sun_times[SUN_EVENT_SUNSET]: # It's after sunset (before midnight)
|
||||
# Because it's after sunset (and before midnight) sunrise should happen tomorrow
|
||||
tomorrow_sun_times = self.get_sunrise_sunset(now + timedelta(days=1))
|
||||
tomorrow_sunrise_seconds = tomorrow_sun_times[SUN_EVENT_SUNRISE].timestamp()
|
||||
tomorrow_sunset_seconds = tomorrow_sun_times[SUN_EVENT_SUNSET].timestamp()
|
||||
tomorrow_solar_midnight_seconds = tomorrow_sun_times['solar_midnight'].timestamp()
|
||||
_LOGGER.debug("tomorrow_sun_times: " + str(tomorrow_sun_times))
|
||||
sunrise_seconds = tomorrow_sun_times[SUN_EVENT_SUNRISE].timestamp()
|
||||
if today_sun_times['solar_midnight'] < today_sun_times[SUN_EVENT_SUNRISE] and tomorrow_sun_times['solar_midnight'] < tomorrow_sun_times[SUN_EVENT_SUNRISE]:
|
||||
# Solar midnight is before sunrise so use tomorrow's time
|
||||
solar_midnight_seconds = tomorrow_sun_times['solar_midnight'].timestamp()
|
||||
|
||||
x1 = today_sunset_seconds
|
||||
y1 = 0
|
||||
_LOGGER.debug("now_seconds: " + str(now_seconds))
|
||||
_LOGGER.debug("sunrise_seconds: " + str(sunrise_seconds))
|
||||
_LOGGER.debug("sunset_seconds: " + str(sunset_seconds))
|
||||
_LOGGER.debug("solar_midnight_seconds: " + str(solar_midnight_seconds))
|
||||
_LOGGER.debug("solar_noon_seconds: " + str(solar_noon_seconds))
|
||||
|
||||
if today_sun_times['solar_midnight'] > today_sun_times[SUN_EVENT_SUNSET] and today_sun_times['solar_midnight'] < tomorrow_sun_times[SUN_EVENT_SUNRISE]:
|
||||
x2 = today_solar_midnight_seconds
|
||||
# Figure out where we are in time so we know which half of the parabola to calculate
|
||||
# We're generating a different sunset-sunrise parabola for before and after solar midnight
|
||||
# because it might not be half way between sunrise and sunset
|
||||
# We're also (obviously) generating a different parabola for sunrise-sunset
|
||||
|
||||
# sunrise-sunset parabola
|
||||
if now_seconds > sunrise_seconds and now_seconds < sunset_seconds:
|
||||
h = solar_noon_seconds
|
||||
k = 100
|
||||
# parabola before solar_noon
|
||||
if now_seconds < solar_noon_seconds:
|
||||
x = sunrise_seconds
|
||||
# parabola after solar_noon
|
||||
else:
|
||||
x2 = tomorrow_solar_midnight_seconds
|
||||
y2 = -100
|
||||
x = sunset_seconds
|
||||
y = 0
|
||||
|
||||
x3 = tomorrow_sunrise_seconds
|
||||
y3 = 0
|
||||
else:
|
||||
x1 = today_sunrise_seconds
|
||||
y1 = 0
|
||||
x2 = today_solar_noon_seconds
|
||||
y2 = 100
|
||||
x3 = today_sunset_seconds
|
||||
y3 = 0
|
||||
# sunset_sunrise parabola
|
||||
elif now_seconds > sunset_seconds and now_seconds < sunrise_seconds:
|
||||
h = solar_midnight_seconds
|
||||
k = -100
|
||||
# parabola before solar_midnight
|
||||
if now_seconds < solar_midnight_seconds:
|
||||
x = sunset_seconds
|
||||
# parabola after solar_midnight
|
||||
else:
|
||||
x = sunrise_seconds
|
||||
y = 0
|
||||
|
||||
_LOGGER.debug("x1: " + str(x1) + "\n\n y1: " + str(y1) + "\n\n x2: " + str(x2) + "\n\n y2: " + str(y2))
|
||||
|
||||
# Generate color temperature parabola from points
|
||||
a1 = -x1**2+x2**2
|
||||
b1 = -x1+x2
|
||||
d1 = -y1+y2
|
||||
a2 = -x2**2+x3**2
|
||||
b2 = -x2+x3
|
||||
d2 = -y2+y3
|
||||
bm = -(b2/b1)
|
||||
a3 = bm*a1+a2
|
||||
d3 = bm*d1+d2
|
||||
a = d3/a3
|
||||
b = (d1-a1*a)/b1
|
||||
c = y1-a*x1**2-b*x1
|
||||
percentage = a*now_seconds**2+b*now_seconds+c
|
||||
a = (y-k)/(h-x)**2
|
||||
percentage = a*(now_seconds-h)**2+k
|
||||
|
||||
_LOGGER.debug("h: " + str(h))
|
||||
_LOGGER.debug("k: " + str(k))
|
||||
_LOGGER.debug("x: " + str(x))
|
||||
_LOGGER.debug("y: " + str(y))
|
||||
_LOGGER.debug("a: " + str(a))
|
||||
_LOGGER.debug("percentage: " + str(percentage))
|
||||
|
||||
return percentage
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"circadian_lighting": {
|
||||
"updated_at": "2019-05-09",
|
||||
"version": "1.0.4",
|
||||
"updated_at": "2019-06-01",
|
||||
"version": "1.0.5",
|
||||
"local_location": "/custom_components/circadian_lighting/__init__.py",
|
||||
"remote_location": "https://raw.githubusercontent.com/claytonjn/hass-circadian_lighting/master/custom_components/circadian_lighting/__init__.py",
|
||||
"visit_repo": "https://github.com/claytonjn/hass-circadian_lighting",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue