[Deprecated] Design Pattern: Time Of Day

To cover the case where OH goes down, a time transition occurs, and OH comes back up. It would have missed the time transition so at best Mode would remain the previous state until the next transition, essentially skipping one whole time period.

Thanks for posting!

I didn’t communicate this well… I didn’t see a need to add it as a trigger, because the function will run when the file is reloaded, by putting it at the end of the file. This also makes it a little easier to spot what is going to run when I save the file.

You are very welcome, and thank you for all you do here!

OK, makes sense. A Systtem started trigger is not needed then.

ive got an error

2018-07-22 18:11:11.949 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Calculate time of day state': Could not cast NULL to org.eclipse.smarthome.core.library.types.DateTimeType; line 30, column 39, length 35

my rule is as follows

rule "Calculate time of day state" 
when
  System started or // run at system start in case the time changed when OH was offline
  Channel 'astro:sun:home:rise#event'    triggered START or
  Channel 'astro:sun:home:set#event'     triggered START or
  Channel 'astro:sun:minus90:set#event'  triggered START or
  Time cron "0 1 0 * * ? *" or // one minute after midnight so give Astro time to calculate the new day's times
  Time cron "0 0 6 * * ? *" or
  Item test7 changed from OFF to ON or
  Time cron "0 0 23 * * ? *"
then

  logInfo("Log", "Calculating time of day...")

  // Calculate the times for the static tods and populate the associated Items
  // Update when changing static times
  // Jump to tomorrow and subtract to avoid problems at the change over to/from DST
  val morning_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(18)
  vMorning_Time.postUpdate(morning_start.toString) 

  val night_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(1)
  vNight_Time.postUpdate(night_start.toString)

  val bed_start = now.withTimeAtStartOfDay
  vBed_Time.postUpdate(bed_start.toString)

  // Convert the Astro Items to Joda DateTime
  val day_start = new DateTime((vSunrise_Time.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli) 
  val evening_start = new DateTime((vSunset_Time.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli)
  val afternoon_start = new DateTime((vEvening_Time.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli)

  // Calculate the current time of day
  var curr = "UNKNOWN"
  switch now {
  	case now.isAfter(morning_start.millis)   && now.isBefore(day_start.millis):       curr = "MORNING"
  	case now.isAfter(day_start.millis)       && now.isBefore(afternoon_start.millis): curr = "DAY"
  	case now.isAfter(afternoon_start.millis) && now.isBefore(evening_start.millis):   curr = "AFTERNOON"
  	case now.isAfter(evening_start.millis)   && now.isBefore(night_start.millis):     curr = "EVENING"
  	case now.isAfter(night_start.millis):                                      curr = "NIGHT"
  	case now.isAfter(bed_start.millis)       && now.isBefore(morning_start.millis):   curr = "BED"
  }

  // Publish the current state
  logInfo(logName, "Calculated time of day is " + curr)
  vTimeOfDay.sendCommand(curr)
end

any ideas as I’m a little lost?

On of these are not initialized correctly. My gues is you didnt define a thing. vSunrise_Time,vSunset_Time, vEvening_Time
So please define the thing correctly.

this is my items file, I presume that you meant items and not things? the only thing file that I have is for the astro binding and that has been setup and working for a while now.

String vTimeOfDay "Current Time of Day [MAP(weather.map):%s]" <tod>

DateTime vMorning_Time "Morning [%1$tH:%1$tM]" <sunrise>

DateTime vSunrise_Time "Day [%1$tH:%1$tM]" <sun> { channel="astro:sun:home:rise#start" }

DateTime vSunset_Time "Evening [%1$tH:%1$tM]" <sunset> { channel="astro:sun:home:set#start" }
    
DateTime vNight_Time "Night [%1$tH:%1$tM]" <moon>
	
DateTime vBed_Time "Bed [%1$tH:%1$tM]" <bedroom_blue>

DateTime vEvening_Time "Afternoon [ %1$tH:%1$tM]" <sunset> { channel="astro:sun:minus90:set#start" }

From the log, the error was at line 30. Assuming this is the first rule in the file with nothing above it…

val afternoon_start = new DateTime((vEvening_Time.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli)

Does vEvening_Time have a value? Check it in a UI or Karaf (smarthome:status vEvening_Time).

No I meant thing!

vEvening_Time currently has no value, all of the others do have values

Please create a file in your things folder and put in (e.g. astro.things):

astro:sun:home  [ geolocation="XX.XXXX,XX", interval=60 ]
astro:moon:home [ geolocation="XX.XX,XX.XX", interval=60 ]
astro:sun:minus90 [ geolocation="XX.XX,XX.XX", interval=60 ] // +15 degrees == -60 minutes

And leave your Items as described at the top. (Especially the channels) (XX.XX are your location coordinates)
That will solve your problem.

thanks, I had the top two lines but not the third. vEvening_Time and vSunset_Time are both showing 17:17 is that correct? the sunset time is right but im a little surprised by the evening time

There’s the cause of your error.

Actually, for minus90 you either need to create an offset or subtract 22.5 from the longitude to get the event 90 minutes before actual sunset.

Hi Rich,
I have a question regarding post 140, druciak’s rule based on the string representation of current time. I’ve copied the rule but I get an “Assignment to final variable” error due to the

if (day_start < morning_start) {
        morning_start = day_start
    }

I’m not a programmer but I have spent some time trying to figure it out, only to realize that I’m gonna need to start at programming for dummies :confused:
My log shows the change from night to day but I never get the morning time. Btw log shows current time of day is now “DAY” occurring at 05:41. I should get 19 minutes of “MORNING” correct?
I’ve read many of your post and think you do a great job explaining the how’s and why’s of programming and was hoping you might could do the same for me.
Thanks

Change…

val String morning_start = "06:00"

… to…

var String morning_start = "06:00"

A val is final and can’t be changed, where a var can be.

That seems easy enough.:grinning:
Thanks Scott.
Out of curiosity, would it work if I replaced

if (day_start < morning_start) {
        morning_start = day_start
    }

with:

if (day_start < morning_start) {
        new_val = "MORNING"
    }

??

Nope… new_value would be changed to “DAY” when running through the switch/case. By changing morning_start, new_value will be set properly in the switch/case.

I just fixed my script, thank’s for spotting this out! :slight_smile:

I tried

but got the same result as before.
To see what would happen if I changed

if (day_start < morning_start) {
        morning_start = day_start
    }

with:

if (day_start < morning_start) {
        new_val = "MORNING"
    }

I didn’t get the time if day is now “MORNING” but time of day started at 06:00 (before change, time of day was starting at 05:42). I moved and changed various parts of the rule but was not able to produce a “MORNING” time. So, after some head scratching, I replaced the original rule and swapped val String morning_start with day_start. Now I have the morning time started by the Astro binding (05:42) and day start to any time I choose. I tested by changing the time, a few times, and seems to be working. I’ll watch it over the next few days, to verify, hopefully no issues.:slightly_smiling_face:

The key question is: how do define “MORNING”?

I confess I didn’t read your previous post carefully :wink:
In my script “MORNING” is when there is dark after 6:00 and this happens only when days are short. I need “MORNING” time to turn on lights when I wake up and there is still dark outside. Currently there is no such TOD, as sun is rising early.

So this is not a bug, but a feature… :wink: