Although there are other approaches defined, I wanted to share my take at Time of Day. With my approach, I define the time of day based on solar elevation and whether the time is before or after noontime (12:00, not the solar noon). The dawn and dusk are defined as how they are scientifically defined (6, 12 and 18 degrees below horizon). For the sunrise and sunset times, I decided to take the interval [-3°; +3°] for sun elevation.
The rule is triggered whenever sun azimut (compass direction) is updated. In practice, this means that the rule is triggered at every update of the sun info by the astro binding. This can’t be achieved with other sun channels.
The default astro binding only updates each 300 seconds, so I decided to add new astro things that update each 60 seconds in /etc/openhab2/things/astro.things
:
astro:sun:home [ geolocation="lat,lon,alt", interval=60 ]
astro:moon:home [ geolocation="lat,lon", interval=60 ]
Replace lat
, and lon
with your place’s latitude and longitude (in decimal degrees). The parameter alt
is optional but is used e.g. for the radiation
items provided by the astro binding.
Here are the relevant astro items (defined in /etc/openhab2/items/astro.items
):
Group gAstro "Astro binding" <sun_clouds> (W66a)
Group gAstroSun "Astro binding: Sun" <sun> (W66a, gAstro)
Number:Angle Astro_Sun_Azimuth "Azimut [%.2f %unit%]" <incline> (gAstroSun) { channel="astro:sun:home:position#azimuth" }
Number:Angle Astro_Sun_Elevation "Elevation [%.2f %unit%]" <incline> (gAstroSun) { channel="astro:sun:home:position#elevation" }
// Time of day (set by a rule)
String Rule_setTimeOfDay "Time of day [%s]" <tod>
For Rule_setTimeOfDay
I set as icon tod
but of course you’ll have to provide them. You can make custom dynamic icons by storing them in /etc/openhab2/icons/classic/
and by naming them as follows:
Time of day state | SVG format | PNG format |
---|---|---|
(default) | tod.svg | tod.png |
MORNING_NIGHT |
tod-morning_night.svg | tod-morning_night.png |
ASTRO_DAWN |
tod-astro_dawn.svg | tod-astro_dawn.png |
NAUTIC_DAWN |
tod-nautic_dawn.svg | tod-nautic_dawn.png |
CIVIL_DAWN |
tod-civil_dawn.svg | tod-civil_dawn.png |
SUNRISE |
tod-sunrise.svg | tod-sunrise.png |
MORNING |
tod-morning.svg | tod-morning.png |
AFTERNOON |
tod-afternoon.svg | tod-afternoon.png |
SUNSET |
tod-sunset.svg | tod-sunset.png |
CIVIL_DUSK |
tod-civil_dusk.svg | tod-civil_dusk.png |
NAUTIC_DUSK |
tod-nautic_dusk.svg | tod-nautic_dusk.png |
ASTRO_DUSK |
tod-astro_dusk.svg | tod-astro_dusk.png |
EVENING_NIGHT |
tod-evening_night.svg | tod-evening_night.png |
And here comes the rule (store it in /etc/openhab2/automation/jsr223/python/personal/
):
# Time Of Day
from core.rules import rule
from core.triggers import when
from core.actions import LogAction
from core.actions import Telegram
from org.joda.time import DateTime
# Rule initialization (used for uniquely identifying rules in case of problems):
rule_init_timestamp = DateTime.now()
logTitle = "time_of_day.py@{ts}".format(ts=rule_init_timestamp.toString("HH:mm:ss"))
ruleTimeStamp = " -- (Rule set initialised {date} at {ts})".format(
date=rule_init_timestamp.toString("E d MMM yyyy"),
ts=rule_init_timestamp.toString("HH:mm:ss (z)"),
)
rulePrefix = "Time Of Day | "
# The following items must exist in the item registry:
class my_rule_items:
TIME_OF_DAY = "Astro_Time_Of_Day"
SUN_AZIMUT = "Astro_Sun_Azimut"
SUN_ELEVATION = "Astro_Sun_Elevation"
# The following Time Of Day states are defined (triggered by sun elevation and AM/PM):
class TimeOfDayStates:
MORNING_NIGHT = "MORNING_NIGHT"
ASTRO_DAWN = "ASTRO_DAWN"
NAUTIC_DAWN = "NAUTIC_DAWN"
CIVIL_DAWN = "CIVIL_DAWN"
SUNRISE = "SUNRISE"
MORNING = "MORNING"
AFTERNOON = "AFTERNOON"
SUNSET = "SUNSET"
CIVIL_DUSK = "CIVIL_DUSK"
NAUTIC_DUSK = "NAUTIC_DUSK"
ASTRO_DUSK = "ASTRO_DUSK"
EVENING_NIGHT = "EVENING_NIGHT"
def get_sun_elevation():
global logTitle
logPrefix = "get_sun_elevation(): "
sun_elevation_item = itemRegistry.getItem(my_rule_items.SUN_ELEVATION)
if sun_elevation_item is None:
LogAction.logWarn(
logTitle,
logPrefix
+ u"⚠️ Sun Elevation Item '{}' not found in Item Registry".format(
my_rule_items.SUN_ELEVATION
),
)
return None
if not isinstance(sun_elevation_item.state, QuantityType):
LogAction.logWarn(
logTitle,
logPrefix
+ u"⚠️ Incorrect state for elevation ({}): {}".format(
sun_elevation_item.state, type(sun_elevation_item.state)
),
)
return None
elevation = sun_elevation_item.state.floatValue()
LogAction.logDebug(
logTitle, logPrefix + u"Sun elevation = {} °".format("%.2f" % elevation)
)
return elevation
@rule(
rulePrefix + "Set the time of day",
description=u"Set the time of day depending on solar elevation and time."
+ ruleTimeStamp,
tags=["TimeOfDay", "Astro", ruleTimeStamp],
)
@when("System started")
@when("Item {} changed".format(my_rule_items.SUN_AZIMUT))
def Rule_setTimeOfDay(event):
global logTitle
logPrefix = "Rule_setTimeOfDay(): "
dt_now = DateTime.now()
am = dt_now.isBefore(dt_now.withTimeAtStartOfDay().plusHours(12))
elevation = get_sun_elevation()
tod_item = itemRegistry.getItem(my_rule_items.TIME_OF_DAY)
if tod_item is None:
LogAction.logError(
logTitle,
logPrefix
+ u"⚠️ Time Of Day Item '{}' not found in Item Registry".format(
my_rule_items.TIME_OF_DAY
),
)
return
if elevation > 3.0:
tod = TimeOfDayStates.MORNING if am else TimeOfDayStates.AFTERNOON
elif elevation > -3.0:
tod = TimeOfDayStates.SUNRISE if am else TimeOfDayStates.SUNSET
elif elevation > -6.0:
tod = TimeOfDayStates.CIVIL_DAWN if am else TimeOfDayStates.CIVIL_DUSK
elif elevation > -12.0:
tod = TimeOfDayStates.NAUTIC_DAWN if am else TimeOfDayStates.NAUTIC_DUSK
elif elevation > -18.0:
tod = TimeOfDayStates.ASTRO_DAWN if am else TimeOfDayStates.ASTRO_DUSK
else:
tod = TimeOfDayStates.MORNING_NIGHT if am else TimeOfDayStates.EVENING_NIGHT
if str(tod_item.state) != tod:
events.sendCommand(tod_item, tod)
# NOTE - "when System shuts down" does not yet work. Using workaround, see:
# https://openhab-scripters.github.io/openhab-helper-libraries/Guides/Triggers.html
def scriptUnloaded():
# call rule when this file is unloaded
global logTitle
logPrefix = "scriptUnloaded(): "
LogAction.logInfo(
logTitle,
logPrefix
+ "Shutting down -- 'scriptUnloaded()' hook -- Clearing the state of '{}'".format(
my_rule_items.TIME_OF_DAY
),
)
tod_item = itemRegistry.getItem(my_rule_items.TIME_OF_DAY)
if tod_item:
events.postUpdate(tod_item, UnDefType.UNDEF)
# Initialise the time of day when the script is (re)loaded:
Rule_setTimeOfDay(None)
Now you can use Astro_Time_Of_Day
in your rules.
Just in case you want to render the time of date nicely, here’s a map transform I use (store e.g. in /etc/openhab2/transforms/
as e.g. astro.map
):
MORNING_NIGHT=Morning Night
ASTRO_DAWN=Astronomic Dawn
NAUTIC_DAWN=Nautic Dawn
CIVIL_DAWN=Civil Dawn
SUN_RISE=Sunrise
SUNRISE=Sunrise
MORNING=Morning
DAYLIGHT=Daylight
AFTERNOON=Afternoon
SUN_SET=Sunset
SUNSET=Sunset
CIVIL_DUSK=Civil Dusk
NAUTIC_DUSK=Nautic Dusk
ASTRO_DUSK=Astronomic Dusk
EVENING_NIGHT=Evening Night
NOON=Noon
NIGHT=Night
//
NULL=Unknown (NULL)
-=Unknown (-)
This way you can render it nicely in a sitemap, as in:
Default item=Astro_Time_Of_Day label="Time of day [MAP(astro.map):%s]"
Have fun!