[Deprecated] Design Pattern: Time Of Day

Well, what values would you like your default Items to have? You might want your default day starting at sunrise say, and use the appropriate astro channel. Or you might want it starting at 07:00 or something, and want to populate it manually.

1 Like

Examples, by definition, are just that, examples. By their very nature it is expected that you would modify them to fit your needs. The example just shows you various ways to configure the Item. But you need to generalize that example to apply to your use case. No one expects you to just use them, particularly in a case where the example depends on external stuff, like your configuration of the Astro binding.

As a long time user of OH you should know what an Astro Thing is and what it does. You should know what a link to a Channel on a Thing looks like and what it does. And you should know how to recognize such a link and how to configure it since you are using .items files. All of this is basic OH stuff outside the scope of this tutorial.

So what are you supposed to do? Identify which Channel on which of your Astro Things should link to each of those Items and configure the channel to link to those.

The rule is actually really simple. You give it a collection of DateTime Items. The rule creates a timer for each of those DateTimes. That timer updates the TimeOfDay Item based on the value of the etod metadata.

Today is a default day so looking at your Items states:

  • BED 00:00
  • DAY 17:50
  • EVENING 17:50
  • MORNING 00:00
  • NIGHT 00:00

What do you expect to have happen with times like that? At best you only have two times of day, 17:50 and 00:00. But because you have more than one DateTime Item with each who knows which one will be chosen? The rule only makes sense if all the DateTime Items have a different time.

1 Like

Hi Rich

Have you seen these errors when rerunning the scheduled rule? Items all have values.


18:09:33.774 [WARN ] [de.jollyday.util.ClassLoadingUtil    ] - Could not load class with current threads context classloader. Using default. Reason: ClassNotFoundException: de.jollyday.impl.DefaultHolidayManager cannot be found by org.apache.aries.jax.rs.whiteboard_2.0.0
18:09:33.778 [WARN ] [de.jollyday.util.ClassLoadingUtil    ] - Could not load class with current threads context classloader. Using default. Reason: ClassNotFoundException: de.jollyday.datasource.impl.XmlFileDataSource cannot be found by org.apache.aries.jax.rs.whiteboard_2.0.0
18:09:33.789 [WARN ] [de.jollyday.util.XMLUtil             ] - Could not create JAXB context using the current threads context classloader. Falling back to ObjectFactory class classloader.
18:09:33.838 [WARN ] [de.jollyday.util.ClassLoadingUtil    ] - Could not load class with current threads context classloader. Using default. Reason: ClassNotFoundException: de.jollyday.parser.impl.ChristianHolidayParser cannot be found by org.apache.aries.jax.rs.whiteboard_2.0.0
18:09:33.842 [WARN ] [de.jollyday.util.ClassLoadingUtil    ] - Could not load class with current threads context classloader. Using default. Reason: ClassNotFoundException: de.jollyday.parser.impl.FixedParser cannot be found by org.apache.aries.jax.rs.whiteboard_2.0.0
18:09:33.844 [WARN ] [de.jollyday.util.ClassLoadingUtil    ] - Could not load class with current threads context classloader. Using default. Reason: ClassNotFoundException: de.jollyday.parser.impl.FixedWeekdayInMonthParser cannot be found by org.apache.aries.jax.rs.whiteboard_2.0.0

And the item seems to update OK

Cheers

I’ve not seen that error before but the error is coming from Ephemeris so what ever the cause it’s outside of the Time of Day Rule and is something wrong in OH core or Ephemeris config or the like. It’s just a warning so I’m not surprised that everything seems to work OK.

1 Like

Hiya … I’ve put this into my OH, and I’m assuming that it’s working, it looks like lots of stuff from that Astro thing is happening in the events.log file … but I keep seeing the below errors, and I’m wondering if that’s doing anything to it.

I don’t have anything reliant on this just yet, so it’s currently stock from your first “Rules DSL” version above.

I’m more posting this to let anyone that cares be aware of it, more than calling tech support … so there’s no entitlement here … but if anyone does feel like addressing it, that’d be amazing! :partying_face:

Errors
2021-09-30 18:00:26.944 [INFO ] [e.smarthome.model.script.Time Of Day] - Calculating time of day...
2021-09-30 18:00:26.971 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Error during the execution of startup rule 'Calculate time of day state': Invalid format: "NULL"
2021-09-30 18:00:52.817 [WARN ] [rest.core.item.EnrichedItemDTOMapper] - Failed transforming the state 'NULL' on item 'vTimeOfDay' with pattern 'MAP(weather.map):%s': An error occurred while opening file.


2021-09-30 18:26:41.549 [WARN ] [rest.core.item.EnrichedItemDTOMapper] - Failed transforming the state 'NULL' on item 'vTimeOfDay' with pattern 'MAP(weather.map):%s': An error occurred while opening file.
2021-09-30 18:26:41.585 [WARN ] [rest.core.item.EnrichedItemDTOMapper] - Failed transforming the state 'NULL' on item 'vTimeOfDay' with pattern 'MAP(weather.map):%s': An error occurred while opening file.
dateTime.rules

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
timedate.items
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" }

It’s hard to know for sure but one or more of the Astro DateTime Items is NULL.

Also, if you are running OH 3 there are lots of other things wrong with that Rule that need to be fixed. There is an OH 3 version somewhere in the bottom quarter of the replies in the thread above.

I don’t really maintain the Rules DSL version any more because it’s really limited due to limitations of the language. And soon we will have a marketplace where you’ll just be able to install a rule like this just like an add-on. When that happens I’ll probably close and deprecate this whole thread and start a new one that uses some other “problem” to use as the example for the design pattern itself. It’s been clear to me for years that the DP aspect of this post has been lost for years now.

Oh, lol, no … I am too afraid to update the system from OH 2.whateverI’mOn … so it’s just running in that.

I get that it’s a framework, mate … I’ve even used the basics to do some other stuff, but I’d like to rely on the time of day, at least.

I guess I’ll get the hammer out and start hitting it to see what falls off, and see if that resolves the errors.

FWIW … and purely thinking aloud as a very dumb human being unworthy of attention in general … but if the version of this that feels like a dumb pleb like me might be able to use it (which is what I’m assuming the Rules DSL is) is a bit broken, it might be best to remove it entirely. Also … ‘DSL’?

Domain Specific Language

It’s actually not broken for OH 2, which is why it remains. Had I removed it or updated it you wouldn’t have any option at all but to upgrade to OH 3 really.

However, which is easier for the average user?

  1. copy and paste, try to understand the code and what it does and edit said code to customize it so it works how you need it to
  2. see the rule in a list of rules, click on it and it installs

I’m going towards 2 but until 2 is as simple as installing an add-on, I’m not going to cut off everyone by removing postings and examples. And even when 2 is that simple, I’ll not remove stuff that works or makes for a good example.

There are plenty of users like you who can not or will not upgrade. Though the users here on the forum that are able to help with 2.x problems is dwindling and will eventually reach 0 (i.e. you’ll be on your own) and the longer you wait to upgrade the harder and more work the upgrade will become. Regardless, I’m not going to remove stuff that works in OH 2 just because it doesn’t work as written in OH 3.

Also, the whole point of the Design Pattern posts is not to post complete and fully realized rules that you can just take and use as is without modification. The code posted in a DP is an example, and often incomplete example at that. The illustrate the concept of the DP. They are not intended to be copy/paste and use. They all require understanding and customization.

Anyway, the tl;dr here is if you are running on OH 2.x, the Rules DSL version of the code in the original post works just fine as written. But no version of the rules will work if your DateTime Items are NULL. You have to initialize them with a valid state before the rule can run. That should happen for you already with the Astro linked Items, but in OH 2 sometimes the rules start to run before Astro updates the Items with values when OH starts up. There’s not much you can do about it (this is fixed in OH 3 BTW) except to watch the logs and run the rule manually again if it failed.

Ah … so I haven’t initialised something … my mistake … I didn’t realise I had to do that.

How do you initialise whatever it is manually?

Should I just set a basic rule to populate them with some garbage?

Hopefully I just initialise whatever it is and this will just work like you say it should. I’ll try poking some shit. :sweat_smile:

((( and cheers for the DSL thing … who knew! )))

In OH 2 you have to use a Rule or the REST API. In OH 3 you have other options.

But as I said, it’s more likely that the Astro Binding is what failed to initialize your Items, not that you failed to initialize it. When OH starts up, sometimes the rules start running before everything else has initialized. So you probably just need to manually trigger the rule to run again, wait for a day, or it already might be working just fine.

The way that version of the rule works is:

  1. At startup it attempts to figure what the current time of day is based on the values stored in the DateTime Items.
  2. The rule triggers at times that correspond with the DateTime Items and figures out what the current time of day is based on the values stored in the DateTime Items.

So if the rule ran before Astro populated the DateTime Items you’ll get a NULL error. However, after that the Astro will have done it’s job and populated the Item so the next time the rule runs it will run OK.

One other possibility is that the you’ve not linked your Items to an Astro Thing that exists. Do you have an astro:sun:home Thing and an astro:sun:minus90 Thing?

1 Like

Oh, sorry, I didn’t spot this … I mean … I’m sure that I do … I didn’t know that I had to do that … lemme go poking. :slight_smile:

I made a home thing … but I didn’t know I needed to link anything to it … … what’s a minus 90 thing, and why is that required?

Are the links the ones I detailed in the initial post?

It’s a different Astro Thing with some offsets applied so that the event occurs 90 minutes before the real event. In this case Afternoon occurs 90 minutes before sundown. There are all sorts of ways to handle that sort of thing but at the time I wrote this DP I used a separate Astro Thing (actually it was written before there were Astro Things but I updated it to OH 2 at some point).

1 Like

Question … Rather than add another binding thing … could I just run a rule that subtracts 90m from the thing that’s made in the first when the astro binding is installed?

First make sure to use the right terminology or else things get confused. The “binding” is the software that interacts with the device or API. Astro is the binding here.

A “Thing” represents a single device. The “Channels” represent the sensors and actuators, one per.

So you are creating another Thing, not a binding.

Now to the question. Rules are triggered based on events. How would you trigger a rule to subtract 90 minutes based on the sundown event from Astro? You’d have to somehow trigger a rule to run 90 minutes before sundown to do the subtraction. But once you’ve done that you don’t need to even do the subtraction.

So how do you generate an event 90 minutes before sundown? You can apply an offset to one of the sundown event Channels in the Astro Thing. But what if you also want an event that occurs at sundown too? You can either be a little flexible and use an event that’s close (e.g. civic sundown) to apply the offset to, or you have to create another Astro Thing where you can apply the offset.

I’m not going to lie, that sounded like another language, mate.

I’ll try to work out how to add a new astro that is somehow 90m before now. Cheers.

Oh, maybe I can get away with just making a manual thing, I’ve not done that, yet. Then I only need to do the one channel for sunset#start.

You should review/rereview the docs, in particular the Concepts section.

You only need to set the offset on one Channel no matter what. But once you’ve set the offset that’s when that event will occur. Once you set the offset for the sunset event to -90 minutes, that event will always occur 90 minutes before sunset and there will be no event actually at sunset.

If you want an event at exactly sunset and 90 minutes before sunset you have to have two Astro Things. And yes, you can manually create an Astro Thing.

The docs are unintelligible at times, I’m afraid … they’re really hard to understand … For example I have literally zero idea what presence is after reading them.

But thanks all the same! :slight_smile: :+1:

It comes with some nice UIs, I intend to use them as much as possible. :smiley:

OK, you lost me a bit again at the end there with the time travel, but I just made a new thing in the things folder, so hopefully after another reboot all will be well with the world. :slight_smile:

astro:sun:minus90:set [ geolocation="11.1111111111111,-0.1111111111111,0", interval=300 ] {
    Channels:
        Type rangeEvent : set#event [
            offset=-90
        ]
}

Thanks again!

1 Like

Given that the docs don’t talk about “presence” I’m not surprised. Do you mean “persistence”, a feature of OH that will save Item states to a database for charting, analysis, and restoring the state of Items on OH reboot?

Oh, yeah … that’s it … persistence.

Ohhhhhhh … that’s what it does.

Wait … it didn’t keep information over reboots before I put some SQLite thing on the other day? :sweat_smile: :sweat_smile: :sweat_smile: No wonder this thing has only started automatically doing stuff over the past few days.

That may explain it.
The usually best way to persist everything for a restore-on-startup without generating a huge database is using the MapDB. It only persists the last value, which is enough for that purpose. Otherwise you’ll end up with a lot of items in an undefined state.