Time of Day Events

Edit: Note that the OH2 Astro binding now contains event functionality that eliminates the need for this workaround! What’s below can be accomplished much more simply using the native Astro binding functionality.

There was some discussion here about sunrise & sunset events in OH2.
https://community.openhab.org/t/simply-turn-on-at-sunrise-turn-off-at-sunset-in-openhab2

I’m sharing what I do in case anyone wants to use, critique, or extend.

ntp.items

DateTime CurrentTime	"Date [%1$tA, %1$tm/%1$td/%1$tY %1$tT]"	<calendar>	{ channel="ntp:ntp:local:dateTime" }

astro.items

// Times of Day
String TimePeriodOfDay              "Time of Day [%s]"

DateTime DawnStart_Time             "Dawn Start [%1$tH:%1$tM]"              { channel="astro:sun:local:civilDawn#start" }
DateTime DawnStop_Time              "Dawn Stop [%1$tH:%1$tM]"               { channel="astro:sun:local:civilDawn#end" }
DateTime DayStart_Time              "Day Start [%1$tH:%1$tM]"               { channel="astro:sun:local:daylight#start" }
DateTime DayStop_Time               "Day Stop [%1$tH:%1$tM]"                { channel="astro:sun:local:daylight#end" }
DateTime DuskStart_Time             "Dusk Start [%1$tH:%1$tM]"              { channel="astro:sun:local:civilDusk#start" }
DateTime DuskStop_Time              "Dusk Stop [%1$tH:%1$tM]"               { channel="astro:sun:local:civilDusk#end" }
DateTime NightStart_Time            "Night Start [%1$tH:%1$tM]"             { channel="astro:sun:local:night#start" }
DateTime NightStop_Time             "Night Stop[%1$tH:%1$tM]"               { channel="astro:sun:local:night#end" }
DateTime MorningNightStart_Time     "MorningNightStart [%1$tH:%1$tM]"       { channel="astro:sun:local:morningNight#start" }
DateTime MorningNightStop_Time      "MorningNightStop [%1$tH:%1$tM]"        { channel="astro:sun:local:morningNight#end" }
DateTime EveningNightStart_Time     "EveningNightStart [%1$tH:%1$tM]"       { channel="astro:sun:local:eveningNight#start" }
DateTime EveningNightStop_Time      "EveningNightStop [%1$tH:%1$tM]"        { channel="astro:sun:local:eveningNight#end" }

// Events
Switch DawnStart_Event              "Start of Dawn Event"
Switch DayStart_Event               "Start of Day Event"                         
Switch DuskStart_Event              "Start of Dusk Event"                            
Switch NightStart_Event             "Start of Night Event"                           

// Items
Switch Dawn                         "Dawn [%s]"                             // After Dawn and before Day
Switch Day                          "Day [%s]"                              // After Day and before Dusk
Switch Dusk                         "Dusk [%s]"                             // After Dusk and before Night
Switch Night                        "Night [%s]"                            // After Night and before Dawn

Number SunAzimuth                   "Azimuth [%.0f °]"                      { channel="astro:sun:local:position#azimuth" }
Number SunElevation                 "Elevation [%.0f °]"                    { channel="astro:sun:local:position#elevation" }

String MoonPhase                    "MoonPhase [%s]"                        { channel="astro:moon:local:phase#name" }

time-of-day.rules

import java.util.Date

// Time of Day Rules
// Requires time update every 60 seconds

val String RFN = "time-of-day.rules"

rule "Get time period for right now"
when
    System started
then
    val now = new Date()
    val dawn = new Date((DawnStart_Time.state as DateTimeType).calendar.timeInMillis)
    val day = new Date((DayStart_Time.state as DateTimeType).calendar.timeInMillis)
    val dusk = new Date((DuskStart_Time.state as DateTimeType).calendar.timeInMillis)
    val night = new Date((NightStart_Time.state as DateTimeType).calendar.timeInMillis)

    val String initStr = "Initializing time period. The time of day is "

    if(now.after(dawn) && now.before(day)) {
        logInfo(RFN, initStr + "Dawn: " + now)
        Dawn.sendCommand(ON)
        Day.sendCommand(OFF)
        Dusk.sendCommand(OFF)
        Night.sendCommand(OFF)
        TimePeriodOfDay.postUpdate("Dawn")
    }
    else if(now.after(day) && now.before(dusk)) {
        logInfo(RFN, initStr + "Day: " + now)
        Dawn.sendCommand(OFF)
        Day.sendCommand(ON)
        Dusk.sendCommand(OFF)
        Night.sendCommand(OFF)
        TimePeriodOfDay.postUpdate("Day")
    }
    else if(now.after(dusk) && now.before(night)) {
        logInfo(RFN, initStr + "Dusk: " + now)
        Dawn.sendCommand(OFF)
        Day.sendCommand(OFF)
        Dusk.sendCommand(ON)
        Night.sendCommand(OFF)
        TimePeriodOfDay.postUpdate("Dusk")
    }
    else {
        logInfo(RFN, initStr + "Night: " + now)
        Dawn.sendCommand(OFF)
        Day.sendCommand(OFF)
        Dusk.sendCommand(OFF)
        Night.sendCommand(ON)
        TimePeriodOfDay.postUpdate("Night")
    }
end

rule "Generate Time of Day Events"
when
    Item CurrentTime received update
then
    val now = new Date()
    val dawn = new Date((DawnStart_Time.state as DateTimeType).calendar.timeInMillis)
    val day = new Date((DayStart_Time.state as DateTimeType).calendar.timeInMillis)
    val dusk = new Date((DuskStart_Time.state as DateTimeType).calendar.timeInMillis)
    val night = new Date((NightStart_Time.state as DateTimeType).calendar.timeInMillis)
    val sunset = new Date((SunsetStart_Time.state as DateTimeType).calendar.timeInMillis)
    val sunrise = new Date((SunriseStart_Time.state as DateTimeType).calendar.timeInMillis)

    if((now.getTime-(now.getTime%60000)) == (dawn.getTime-(dawn.getTime%60000))) {
        logInfo(RFN, "Transitioning to Dawn!!!")
        DawnStart_Event.postUpdate(ON)
    }
    else if((now.getTime-(now.getTime%60000)) == (day.getTime-(day.getTime%60000))) {
        logInfo(RFN, "Transitioning to Day!!!")
        DayStart_Event.postUpdate(ON)
    }
    else if((now.getTime-(now.getTime%60000)) == (dusk.getTime-(dusk.getTime%60000))) {
        logInfo(RFN, "Transitioning to Dusk!!!")
        DuskStart_Event.postUpdate(ON)
    }
    else if((now.getTime-(now.getTime%60000)) == (night.getTime-(night.getTime%60000))) {
        logInfo(RFN, "Transitioning to Night!!!")
        NightStart_Event.postUpdate(ON)
    }

    if((now.getTime-(now.getTime%60000)) == (sunrise.getTime-(sunrise.getTime%60000))) {
        logInfo(RFN, "Start of Sunrise!")
        SunriseStart_Event.postUpdate(ON)
    }
    else if((now.getTime-(now.getTime%60000)) == (sunset.getTime-(sunset.getTime%60000))) {
        logInfo(RFN, "Start of Sunset!")
        SunsetStart_Event.postUpdate(ON)
    }
end

rule "Dawn Started"
when
    Item DawnStart_Event received update ON
then
    val now = new Date()
    val dawn = new Date((DawnStart_Time.state as DateTimeType).calendar.timeInMillis)
    val day = new Date((DayStart_Time.state as DateTimeType).calendar.timeInMillis)

    if(now.after(dawn) && now.before(day)) {
        logInfo(RFN, "Its Dawn: " + now)
        Dawn.sendCommand(ON)
        Day.sendCommand(OFF)
        Dusk.sendCommand(OFF)
        Night.sendCommand(OFF)
        TimePeriodOfDay.postUpdate("Dawn")
    }
end

rule "Day Started"
when
    Item DayStart_Event received update ON
then
    val now = new Date()
    val day = new Date((DayStart_Time.state as DateTimeType).calendar.timeInMillis)
    val dusk = new Date((DuskStart_Time.state as DateTimeType).calendar.timeInMillis)

    if(now.after(day) && now.before(dusk)) {
        logInfo(RFN, "Its Day: " + now)
        Dawn.sendCommand(OFF)
        Day.sendCommand(ON)
        Dusk.sendCommand(OFF)
        Night.sendCommand(OFF)
        TimePeriodOfDay.postUpdate("Day")
    }
end

rule "Dusk started"
when
    Item DuskStart_Event received update ON
then
    val now = new Date()
    val dusk = new Date((DuskStart_Time.state as DateTimeType).calendar.timeInMillis)
    val night = new Date((NightStart_Time.state as DateTimeType).calendar.timeInMillis)

    if(now.after(dusk) && now.before(night)) {
        logInfo(RFN, "Its Dusk: " + now)
        Dawn.sendCommand(OFF)
        Day.sendCommand(OFF)
        Dusk.sendCommand(ON)
        Night.sendCommand(OFF)
        TimePeriodOfDay.postUpdate("Dusk")
    }
end

rule "Night started"
when
    Item NightStart_Event received update ON
then
    val now = new Date()
    val morningNightStart = new Date((MorningNightStart_Time.state as DateTimeType).calendar.timeInMillis)
    val morningNightStop = new Date((MorningNightStop_Time.state as DateTimeType).calendar.timeInMillis)
    val eveningNightStart = new Date((EveningNightStart_Time.state as DateTimeType).calendar.timeInMillis)
    val eveningNightStop = new Date((EveningNightStop_Time.state as DateTimeType).calendar.timeInMillis)

    if((now.after(morningNightStart) && now.before(morningNightStop)) || 
       (now.after(eveningNightStart) && now.before(eveningNightStop))) {
        logInfo(RFN, "Its Night: " + now)
        Dawn.sendCommand(OFF)
        Day.sendCommand(OFF)
        Dusk.sendCommand(OFF)
        Night.sendCommand(ON)
        TimePeriodOfDay.postUpdate("Night")
    }
end

rule "Sunrise started"
when
    Item SunriseStart_Event received update ON
then
    val now = new Date()
    logInfo(RFN, "Its Sunrise : " + now)
end

rule "Sunset started"
when
    Item SunsetStart_Event received update ON
then
    val now = new Date()
    logInfo(RFN, "Its Sunset : " + now)
end

sitemap fragment

Frame {
    Text label="Time of Day Information" {
        Frame label="Time Period of Day" {
            Text item=TimePeriodOfDay label="Time of Day"
        }
        Frame label="Time Transition Events" {
            Text item=SunriseStart_Time label="Sunrise Start Time"
            Text item=SunsetStart_Time label="Sunset Start Time"
            Text item=DawnStart_Time label="Dawn Start Time"
            Text item=DayStart_Time label="Day Start Time"
            Text item=DuskStart_Time label="Dusk Start Time"
            Text item=NightStart_Time label="Night Start Time"
        }
        Frame label="Time of Day Status" {
            Text item=Dawn label="Is Dawn"
            Text item=Day label="Is Day"
            Text item=Dusk label="Is Dusk"
            Text item=Night label="Is Night"
        }
        Frame label="Sun" {
            Text item=SunAzimuth label="Sun Azimuth" icon="sun"
            Text item=SunElevation label="Sun Elevation" icon="sun"
        }
        Frame label="Moon" {
            Text item=MoonPhase label="Moon Phase" icon="moon"
        }
    }
}
5 Likes

Here is an alternative solution.
A smaller set of items and rules to get one Switch for Day or Night.

things/astro.thing

astro:sun:home  [ geolocation="50.12345,10.12345", interval=300]
astro:moon:home [ geolocation="50.12345,10.12345", interval=300]

items/astro.items

DateTime SunsetTime "Sunset [%1$tH:%1$tM]" <sun> (Astro) { channel="astro:sun:home:set#start" }
DateTime SunriseTime "Sunrise [%1$tH:%1$tM]" <sun> (Astro) { channel="astro:sun:home:rise#end" }
Number   SunElevation  "Elevation [%.1f °]"  <sun>  (Astro) { channel="astro:sun:home:position#elevation" }
Switch   NightState    "Night"

rules/astro.rules

rule "OpenHAB system started - astro"
when
    System started
then
    createTimer(now.plusSeconds(180)) [ |
        if (now.isAfter((SunsetTime.state as DateTimeType).calendar.timeInMillis) ||
            now.isBefore((SunriseTime.state as DateTimeType).calendar.timeInMillis)
        ) {
            postUpdate(NightState, ON)
        } else {
            postUpdate(NightState, OFF)
        }
    ]
end
rule "Update NightState"
when
    Item SunElevation changed
then
    if(SunElevation.state >  0){
        if(NightState.state != OFF) postUpdate(NightState, OFF)
    } else {
        if(NightState.state != ON) postUpdate(NightState, ON)
    }
end

With this combination you will get an item “NightState” which is ON at night and OFF by day. The first rule is needed to set NightState after openHAB was started or reloaded, the second will switch NightState at sunrise/sunset.

In yet another rule you can now react on the item NightState. You can either trigger a rule at sunrise or sunset or use NightState inside a rule as a condition.

Examples:

rule "Night has started"
when
    Item NightState changed to ON
then
    logInfo("RULE", "It's getting dark!")
end

rule "Bathroom door opened"
when
    Item BathDoor changed to OPEN
then
    if (NightState.state == ON) {
        sendCommand(BathLight, ON)
    }
end
10 Likes

I followed @mhilbush suggestion above but had to do one modification. Since sunrise / sunset gets recomputed every night at midnight GMT (and I am on Pacific time) the sunset time gets set to tomorrow’s sunset just before the sunset occurs. I had to do this hack in the sunset part:
...else if((now.getTime-(now.getTime%60000)) == (sunset.getTime-(sunset.getTime%60000)) || ((now.getTime-(now.getTime%60000)) == ((sunset.getTime-(sunset.getTime%60000)) - 86400000 ))) {

which says “if it is sunset or yesterday’s sunset time
”

This was necessary because the system timezone was set to UTC and the system location was in Los Angeles, CA USA. Since the Astro binding recalculates once a day at midnight, if the system timezone is set differently than the actual timezone, the rule might function incorrectly.

1 Like

I also followed Mark’s code but I am missing the Sunrise Start Time and the Sunset Start Time both fields in my view of my sitemap are blank, I couldn’t figure out where they were supposed to be populated. I wasn’t sure where the items that are listed //events should be put so I added them to my items file. I think I can turn my light on at dusk with it but tonight will be first test. - Thanks Mark!

Works, thanks!

Its a shame this is significantly more difficult (or rather, more work to achieve the same result) than OpenHAB 1

It’s just different, not more difficult.

http://docs.openhab.org/addons/bindings/astro/readme.html

You even don’t need any items to trigger events:

when
    Channel 'astro:sun:home:rise#event' triggered START 
then
4 Likes

Hi @sihui,
I am trying this example to switch a light at sunset, but i’l get an error in HABmin:
image

I dit this with:

Channel 'astro:sun:home:set#start' triggered START

As with:

Channel 'astro:sun:home:set#event' triggered START

Same error on the line.
Any idea what this may cause?

ESH Designer (that is a screenshot of ESH Designer, not Habmin) is kind of stuck in time. The latest working version was released before Channel triggers were implemented. You will also see it falsely marking all the third party Actions you may call and any Items defined using PaperUI as errors.

Despite its lack of updating it is the best we have until someone decides to spend the time to fix it, the vscode adds syntax highlighting, or the Experimental Rules Engine stops being experimental.

Ok Rich, yes you are right it is ESH Designer :blush: my mistake

Thanks for the explanation! I’l have to keep that in mind. I used to use only text files till i saw a tutorial where ESH D was used. Looked like a nice tool, but

It could have been a beautifut nice tool with syntax checking.

@deltabert you might want to check out openHAB VSCode extension - it recently gained Language Server Protocol support, which means it already highlights errors in your code :wink:
The prerequisite is latest openHAB SNAPSHOT and misc-lsp addon (Language Server Support) installed on your system. See here for more details.

Cheers,
Kuba

3 Likes

Thanks @kubawolanin, I just installed Visual Studio with the openhab extension, and here i do not have the error indication. But i do not use the snapshot
 Changed a rule and saved it. seems to work, althoug i have an error on REST AP on top of the screenI. Don’t know if that’s important at the moment.

Looks as i will switch to MS VS, although i prefer real open source products (you never know what they are collecting now
 :wink: )

Note it’s not Visual Studio (IDE) but Visual Studio Code - opensource general-purpose code editor :wink:

@kubawolanin. I see that you are the maker of the openHAB VS Code Extension. Great combination, and thank you for building this!
I do have a small issue though. Everything seems to work OK, i can edit mt rule files without a problem. However i do get this message in the status bar on top of the screen:
image

And when i open the Settings and Search for REST API, i see that “openhab.useRestApi”: true, but the “openhab.username”: “”, and “openhab.password”: “”, are empty.
So i tried to edit these like so:


But i don’t know how to save this, anyhow File::Save does not work.

It does not seem to interfere with the normal editing, because i can reach the files on the RPi share and they are changed as well. Still it worries me a little bit

Any idea wat this is causing?

@deltabert you need to point "openhab.host" to your openHAB IP address or hostname. See the docs for more details.

OK, think i found it. The save button for saving the user settings which overwrite the defaults.
Thanks, i changed the “openhab.host” parameter.
Have to get acquainted with this editor :blush:

1 Like

Sorry to bring this back up, but in a bid to almost complete my OH1 to OH2 migration, I’m just trying to tame the last of my rules. I have a lot of reliance on times of day, for example, my motion sensors only work when LUX is below a certain parameter AND its a specific time of day.

I’m currently testing, using some of the logic above, but not having much luck.

I’m using the theory behind @mhilbush’s original post, but cobbled together using PaperUI rather than .items. I’m using Mark’s rule set.

I’ve defined an Offset on my night#start to bring it into real time for testing, I’m just having problems with triggering the channel!

capture20171111113053873

rule “Night test”
when
Channel ‘astro:sun:casavenicio:night#start’ triggered START
then
sendMail(“myemail@addre.ss”, “NIGHT”, “Night TEST Channel Trigger!”)
logInfo(“EXTRA”, “Its Night time, dark things can happen”)
end

The logs show that the time comes and goes, and effectively nothing occurs. Any ideas?

What version of OH are you running? If you’re running 2.1 stable, you might be experiencing this.

Please be aware that the event channel trigger is in your case

Channel 'astro:sun:casavenicio:night#event' triggered START //beginning of night

or

Channel 'astro:sun:casavenicio:night#event' triggered END //end of night

This channel does not need to be linked to any item (in fact, this would be totally useless)

1 Like