[Deprecated] Design Pattern: Time Of Day

I got the same issue few weeks ago. Try replacing && (and) by || (or). Current time cannot be between night start and next morning start. It’s only true before midnight…

3 Likes

This seems to work. Thanks a lot!

I’m a newbie in Programming, so I have a easy question. How is it possible to change the time for the switch, e.g. NIGHT starts at 22:00?

I already tried just typing

val night_start = now.withTimeAtStartOfDay.plusDays(1).

minusHours(2)

but that’s not changing the switching time away from 23:00. Same thing for DAY, wanna start it at 7:00 and not 6:00.

Can you help me?

As the OP states:

Adding a new time of day is as simple as adding a new trigger to the rule to fire when that time of day starts, calculate the DateTime for today when that time period starts, and then adding a new case to the switch statement to determine whether the current time is between when this new time period starts and the next one starts. The isBefore test for the previous time period would have to be updated as well.

So first you need to change the Rule trigger to run the Rule at 22:00 instead of 23:00.

    Time cron "0 0 22 * *? *" 

Then you need to change the line that determines night_start.

    val night_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(2)

You are not adding a new time period so you are done. Repeate for DAY.

hi i’m using this rule, but simplify for now, After some help (a alot) is runing, but the dawn trigger is not changing the items value.

DateTime    Amanecer_civil  "Astro: Amanecer Civil [%1$tH:%1$tM]" <time> { channel="astro:sun:casa:civilDawn#end" }
DateTime    Atardecer_civil "Astro: Atardecer Civil [%1$tH:%1$tM]" <time> { channel="astro:sun:casa:civilDusk#start" }
val logName = "Momento del Dia"
rule "Dia y Noche"

when
    System started or
    Channel 'astro:sun:casa:civilDawn#event' triggered START or
    Channel 'astro:sun:casa:civilDusk#event' triggered START
    //Time cron " 0 0 0/1 1/1 * ? * "

then
  Thread::sleep(1000)
  logInfo(logName, "Calculando...")
  
val amanecer = new DateTime(Amanecer_civil.state.toString)
val atardecer = new DateTime(Atardecer_civil.state.toString)
Thread::sleep(1000)
  var curr = "UNKNOWN"
switch now {
    case now.isAfter(amanecer)  && now.isBefore(atardecer):  curr = "Dia"
  	case now.isAfter(atardecer) || now.isBefore(amanecer): curr = "Noche"
}
logInfo(logName, "El momento del dia es " + curr)
dia.sendCommand(curr) 


end

I belive it must be something about the rule and the trigger happending together or something like that.
Any ideas.

You don’t seem to have accepted what I said before about that weird usage of switch-case

Sorry my mistake, i post it here becase i want to make my rule using this pattern son it can became more complex. You solution work, but i don’t want to make a lot of if and else (my professor will kill).
Also i want to understand why my rule is not working son i can learn for the next time.

Good day.
My TOD Worked and no it just does not update it any more.Did not change anything on my system.

TOD Items:

String vTimeOfDay "Current Time of Day [%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" }

String    Season             "Season"                       { channel="astro:sun:home:season#name" }

Tod Site Map:

Frame label="TOD"{
    Text label="Time of Day" icon="bedroom"{
     Default item=vTimeOfDay       label="Current Time of Day"
     Default item=vSunrise_Time    label="Day"
     Default item=vSunset_Time     label="Evening"
     Default item=vMorning_Time    label="Morning"
     Default item=vEvening_Time    label="Afternoon"
     Default item=vNight_Time      label="Night"
     Default item=vBed_Time        label="Bed"
     Default item=Season           label="Season"
     }

TOD Log :

2020-05-25 10:22:29.389 [INFO ] [e.smarthome.model.script.Time Of Day] - Calculating time of day...

==> /var/log/openhab2/events.log <==

2020-05-25 10:22:29.465 [vent.ItemStateChangedEvent] - vMorning_Time changed from NULL to 2020-05-25T06:00:00.000+0200

2020-05-25 10:22:29.474 [vent.ItemStateChangedEvent] - vNight_Time changed from NULL to 2020-05-25T23:00:00.000+0200

2020-05-25 10:22:29.477 [vent.ItemStateChangedEvent] - vBed_Time changed from NULL to 2020-05-25T00:00:00.000+0200

==> /var/log/openhab2/openhab.log <==

2020-05-25 10:22:29.556 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Error during the execution of startup rule 'Calculate time of day state': Invalid format: "UNDEF"

==> /var/log/o

TOD Rule:

val logName = "Time Of Day"

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
  Time cron "0 0 23 * * ? *"
then

  logInfo(logName, "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.toString) 
  val evening_start = new DateTime(vSunset_Time.state.toString)
  val afternoon_start = new DateTime(vEvening_Time.state.toString)

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

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

// Examples for use of vTimeOfDay
rule "Day time started"
when
  Item vTimeOfDay changed to "DAY" // does not work prior to OH 2.3 Release
then
  // do stuff when DAY starts
end

Not Sure where to look.

One or more of your Astro Items has an UNDEF state value. That’s not necessarily abnormal, depending on latitude and season. Inspect your relevant Item states to get further.

Find out.
Look in your events.log for trigger events.
Look in your openhab.log for the messages your rule produces.
Share that info with us.
Add further diagnostic logs to your rule, to see progress and to see key values, for example

val amanecer = new DateTime(Amanecer_civil.state.toString)
logInfo(logName, "datetime...{}", amanecer)
val atardecer = new DateTime(Atardecer_civil.state.toString)
logInfo(logName, "datetime...{}", atardecer)

Thank you.
Found my issue. it was the afternoon reading.

How do i get my Afternoon corected as it is the same as my evining time ?

Today morning logs are:

Events.log
2020-05-25 07:22:00.001 [vent.ChannelTriggeredEvent] - astro:sun:casa:civilDawn#event triggered START
2020-05-25 07:22:02.011 [ome.event.ItemCommandEvent] - Item 'dia' received command Noche

openhab.log
2020-05-25 07:22:01.005 [INFO ] [arthome.model.script.Momento del Dia] - Calculando...
2020-05-25 07:22:02.009 [INFO ] [arthome.model.script.Momento del Dia] - El momento del dia es Noche

My new rule is with logs is:

val logName = "Momento del Dia"
rule "Dia y Noche"

when
    System started or
    Channel 'astro:sun:casa:civilDawn#event' triggered START or
    Channel 'astro:sun:casa:civilDusk#event' triggered START

then
  Thread::sleep(1000)
  logInfo(logName, "Calculando...")
  
val amanecer = new DateTime(Amanecer_civil.state.toString)
val atardecer = new DateTime(Atardecer_civil.state.toString)
val ahora = new DateTime(now.toString)
Thread::sleep(1000)
  var curr = "UNKNOWN"
  logInfo(logName, "Amanecer: {}", amanecer)
  logInfo(logName, "atardecer: {}", atardecer)
  logInfo(logName, "ahora: {}", ahora)
switch now {
    case now.isAfter(amanecer)  && now.isBefore(atardecer):  curr = "Dia"
  	case now.isAfter(atardecer) || now.isBefore(amanecer): curr = "Noche"
}
logInfo(logName, "El momento del dia es " + curr)
dia.sendCommand(curr)  

end

Two other things, first civildusk event work perfect, only civilDawn have has problems.
Second the trigger more times than it should and when it does run sevel times. I leave the logs.

events.log
2020-05-25 17:55:00.002 [vent.ChannelTriggeredEvent] - astro:sun:casa:civilDusk#event triggered START

The first two lines match the trigger event, everithing is perfect, the others run some minutes later and sevel times. This happens few times everyday.

openhab.log
2020-05-25 17:55:01.004 [INFO ] [arthome.model.script.Momento del Dia] - Calculando...
2020-05-25 17:55:02.007 [INFO ] [arthome.model.script.Momento del Dia] - El momento del dia es Noche
2020-05-25 17:59:19.375 [INFO ] [arthome.model.script.Momento del Dia] - Calculando...
2020-05-25 17:59:20.379 [INFO ] [arthome.model.script.Momento del Dia] - El momento del dia es Noche
2020-05-25 17:59:21.360 [INFO ] [arthome.model.script.Momento del Dia] - Calculando...
2020-05-25 17:59:22.364 [INFO ] [arthome.model.script.Momento del Dia] - El momento del dia es Noche
2020-05-25 18:16:18.935 [INFO ] [arthome.model.script.Momento del Dia] - Calculando...
2020-05-25 18:16:18.935 [INFO ] [arthome.model.script.Momento del Dia] - Calculando...
2020-05-25 18:16:19.941 [INFO ] [arthome.model.script.Momento del Dia] - El momento del dia es Noche
2020-05-25 18:16:19.942 [INFO ] [arthome.model.script.Momento del Dia] - El momento del dia es Noche
2020-05-25 18:16:20.915 [INFO ] [arthome.model.script.Momento del Dia] - Calculando...
2020-05-25 18:16:21.920 [INFO ] [arthome.model.script.Momento del Dia] - El momento del dia es Noche

The most interesting thing about that. is the complete absence of three logInfo() outputs that are in the rule you show us.
The most likely explanation of that is either -
your last rules edit did not upload properly, an old version is still in use - check your openhab.log for your xxx.rules file refresh message.
or
you have more than one rule with the same “name”. Only one will work.

Looks like you get multiple System started events. That usually happens if you are editing files, and can also happen in some configuration situations. Look out for Thing updates (not Thing status updates) in your logs.

The first log are from this moorning and i edit the rules this afternoon. So new logs will be ready tomorrow morning.
Sorry.

Don’t know. What would you like it be?
I expect you want to apply an offset to your minus90 Thing?
Might help -

Hello Rich,

Having switched to openhab3, I noticed that the method for calculating time used in the Design Pattern is not supported anymore. Specifically, these lines:

val morning_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(18)
vMorning_Time.postUpdate(morning_start.toString)

The solution seems to be to use the Java Class ZonedDateTime, however, I am unable to figure out how that needs to be formatted exactly to be read by the openhab-items.

Does anyone have a hint on how to calculate the times for the “Time of Day” Design Pattern?

Thanks!

If you are on OH 3 I would recommend switching to the MainUI JavaScript implementation (link and description now in the top post). I don’t use Rules DSL any more and have not yet decided whether I’m going to go through all the DPs and update the Rules DSL versions for all of them. And the JavaScript and Python version are so much easier for you to use as is without code changes.

But the ZonedDateTime equivalent to that line of code would be

val morning_start = now.withHour(6)

That should handle the change in daylight savings without needing to jump forward than subtract back.

Rich,

thank you very much, I will have a look at the MainUI Javascript version (just as I was getting proficient in DSL rules…).

If I see this correctly, this is all driven by the different TimesOfDay items (Morning, Day, etc.), right? So I just need to figure out a way to populate those. Currently, I don’t think I have grasped the concept far enough to tell where that is done. Where or how do I define when Bedtime for a weekday is? Or for a weekend?

I’m not sure if that “How to boot strap the state of an Item” is what I need here.

I figured the ZoneDateTime variables out last night, but got stuck on importing the astro time in a correct format, kept getting all sorts of “cannot compare date” errors. So I’m definitively going to give the JavaScript a try.

Thanks!

You don’t have to abandon the Rules DSL code. The advantage of the the JavaScript and Jython versions is that you never have to even look at the code. You just import them to your OH and configure your Items. So you can import those that are useful to you and use them and write your own stuff in Rules DSL if that suits you.

You define a DateTime Item for the start time of each time of day. If you want a different set of times for different types of day (as defined in Ephemeris: https://www.openhab.org/docs/configuration/actions.html#configuration) you define a set of DateTime Items for each type of day. The example in the top post shows a set of DateTime Items for deafult, weekend, a custom day set called trash, holiday, and a custom holiday defined in the /openhab/conf/services/custom1.xml. See the Ephemeris docs (link above) for how to configure Ephemeris and define a custom dayset or custom holidays.

Each Item is a member of the TimesOfDay.

Each Item has an etod metadata. The etod metadata defines the name of the time of day that starts at that time, and what type of day type it applies to. If using a custom dayset or custom holiday there are additional parameters.

Everything is defined at the Item. The start time is the state of the Item. The rest is metadata defined on the Item.

You would need to bootstrap the state of the Item if it doesn’t have a state and doesn’t get a state from somewhere else (e.g. the Astro binding). Somehow the Item needs to get a valid date time value.

To get a ZonedDateTime from a DateTimeType just call zonedDateTime. For example MyDateTime.state.zonedDateTime. You might need to cast it to DateTimeType first. (MyDateTime.state as DateTimeType).zonedDateTime.

But you don’t have to mess with any of this with the JavaScript or Jython version. It’s already implemented for you.

Everything is defined at the Item. The start time is the state of the Item. The rest is metadata defined on the Item.

Yes, that’s how I understood it. So for those times that do not get updated automatically (like Astro) I still need a rule of some sort that updates the state of the (fixed) items, preferably once at the start of the day. I suppose that has to run daily to update the states with the correct date/time. But I think I can figure that out with ZonedDateTime, as I already did with the original rule.

I can see that the use of Ephemeris expands the usefulness of this to different types of days (weekends, holidays, even trash days).

Thanks!