Design Pattern: Simple State Machine (e.g. Time Of Day)

I tried the bootstrapping part, too, and found it didn’t work as expected, either.
But you can easily do it from the karaf console:

The syntax is:
openhab:send <item> <command>

So if you want your Default_Morning item to be set to 5 in the morning, you’d enter:
openhab:send Default_Morning 05:00:00

It’s a DateTime item, but if you omit the date, it will be set to today’s date (and your Timezone). And then the script will move it to the next day every midnight.

You can test if it’s correct by entering:
openhab:items list | grep Default_Morning

For me, that gives back:
Default_Morning (Type=DateTimeItem, State=2020-12-26T05:15:00.000-0600, Label=MORNING, Category=time, Groups=[TimesOfDay])

Or, you can just put the TimeOfDay items in a sitemap and look at them there.

Again, this only needs to be done once (or when you want to change the times).
As you found out, this only applies to the fixed times, the variable ones that come from Astro are populated by the Astro binding.

UPDATE

Thank you for your quick answer!

After trying a couple of times and over and over … HEUREKA … it works as described. Staic times are Set and can be changed through the Widget. Why it startet to work I have no Idea.

Now to the next Part … that is the TimeOfDay item which right now is yet to show anything but NULL.

Run the script manually using that “Play” button. If you watch the logs, it should show something like this:

2020-12-26 00:01:00.863 [INFO ] [openhab.model.script.Rules.TimeOfDay] - Cancelling any existing timers
2020-12-26 00:01:00.866 [INFO ] [openhab.model.script.Rules.TimeOfDay] - Existing timers have been cancelled
2020-12-26 00:01:00.867 [INFO ] [openhab.model.script.Rules.TimeOfDay] - FUTURE: MORNING scheduleing timer for 2020-12-26T05:15-06:00
2020-12-26 00:01:00.871 [INFO ] [openhab.model.script.Rules.TimeOfDay] - FUTURE: DAY scheduleing timer for 2020-12-26T07:02-06:00
2020-12-26 00:01:00.873 [INFO ] [openhab.model.script.Rules.TimeOfDay] - FUTURE: EVENING scheduleing timer for 2020-12-26T17:05-06:00
2020-12-26 00:01:00.875 [INFO ] [openhab.model.script.Rules.TimeOfDay] - FUTURE: NIGHT scheduleing timer for 2020-12-26T21:00-06:00
2020-12-26 00:01:00.876 [INFO ] [openhab.model.script.Rules.TimeOfDay] - FUTURE: BED scheduleing timer for 2020-12-26T22:00-06:00
2020-12-26 00:01:00.878 [INFO ] [openhab.model.script.Rules.TimeOfDay] - Created 4 time of day timers
2020-12-26 00:01:00.878 [INFO ] [openhab.model.script.Rules.TimeOfDay] - The current time of day is BED

Then it should update the TimeOfDay item.
(Don’t ask me why I have five times, but it schedules only four of them. We have not figured that out, yet. But all timers fire, so it doesn’t really matter).

Finally … made it!
Problem occured because the .yml script that I pasted was somehow not pasted in the correct format so that I got loads of errors. Deleting and Pastzing the code part again didn’ work. Finally I just deleted the rule and pasted from notepad++ this did the trick. Unfortunately some static times went missing but since I implemented them with the boot strap thing it was no problem getting them back.
Will look if everything works as it should!

I have my items in .item files, so they can’t get lost in there.
For copying the script, in Github it’s usually best to get it in raw-mode. That usually eliminates errors due to improper pasting.

But I’m glad you got it working.

ok so ive created the rule in oh3 ui pasted the raw /ephemTimeOfDay.yml into the code section but when I click on the play button I get the following error in the logs.

2020-12-27 00:58:34.484 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID '96b7b0f9d3' failed: TypeError: Cannot load script from /etc/openhab/automation/lib/javascript/community/timerMgr.js in <eval> at line number 27

And I suppose you triple-checked that both of the necessary helper scripts are in the correct place, have you?

I cant see a ect/openhab share for openhabian? where is it?

ok so you have to create all the folders yourself. ive done that and now its throwing anther error.

2020-12-27 03:34:34.931 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID '96b7b0f9d3' failed: The TimeOfDay Item is not defined! in <eval> at line number 239 at column number 4

where are you meant to define a time of day thing? ive looked at line 239 and cant see anything?

First: if you are accessing the files on your openhabian installation from a windows box, I would strongly advise against it. That’s almost a guarantee to mess things up, as Windows uses a different kind of linefeed character (and most likely a different locale) than your openhabian. So don’t edit scripts via a Windows share.
Second, if you read through the instructions, you’ll see that the script needs several items to work:

  1. an item named TimeOfDay, that is the one that will be updated as the transitions happen and which is what you will want to use for your own rules. This is a string item.
  2. a DateTime item for each time you want to transition to. Morning, Day, Evening, etc. Two or more for each time if you want to use weekday/weekend/holiday etc. Those need to have the ephemeris metatags.
  3. a group named TimesOfDay of the DateTime type that all the items belong to. This has to be a DateTime group.
    Third, there’s a difference between an item and a thing in Openhab.

Best put all of these in an items file under /etc/openhab/items. Be sure to edit this file on the openhabian system itself, by using an editor. Vi, emacs, whatever suits you.

For example, my items file looks like this:

Group:DateTime TimesOfDay <time>
String TimeOfDay "Current time of day [%s]" <time>
DateTime Default_Morning "MORNING [%s]" <time> (TimesOfDay) { etod="MORNING"[type="default"] }
DateTime Default_Day "DAY [%s]" <time> (TimesOfDay) { channel="astro:sun:local:rise#start", etod="DAY"[type="default"] }
DateTime Default_Evening "EVENING [%s]"<time> (TimesOfDay) { channel="astro:sun:local:set#end", etod="EVENING"[type="default"] }
DateTime Default_Night "NIGHT [%s]" <time> (TimesOfDay) { etod="NIGHT"[type="default"] }
DateTime Default_Bed "BED [%s]" <time> (TimesOfDay) { etod="BED"[type="default"] }

The “<time>” in there is just the clock icon, that’s not important. But the rest is.

sweet thanks, got that. ive just got the following item file.

Group:DateTime TimesOfDay
String TimeOfDay "Current time of day [%s]"

// Default day, initialization for JavaScript should be done thgrough MainUI. See https://community.openhab.org/t/oh-3-examples-how-to-boot-strap-the-state-of-an-item/108234
DateTime Default_Morning (TimesOfDay) { etod="MORNING"[type="default"] }
DateTime Default_Day (TimesOfDay) { channel="astro:sun:21bf543921:rise#start", etod="DAY"[type="default"] }
DateTime Default_Evening (TimesOfDay) { channel="astro:sun:21bf543921:set#start", etod="EVENING"[type="default"] }
DateTime Default_Night (TimesOfDay) { etod="NIGHT"[type="default"] }
DateTime Default_Bed (TimesOfDay) { etod="BED"[type="default"] }

// Weekend day, notice that not all the states are listed, the unlisted states are skipped
DateTime Weekend_Day (TimesOfDay) { channel="astro:sun:set120:set#start", etod="DAY"[type="weekend"] }
DateTime Weekend_Evening (TimesOfDay) { channel="astro:sun:local:set#start", etod="EVENING"[type="weekend"] }
DateTime Default_Bed (TimesOfDay) { etod="BED"[type="weekend"] }

// Custom dayset
DateTime Trash_Morning (TimesOfDay) { etod="MORNING"[type="dayset", set="trash"] }
DateTime Trash_Trashtime (TimesOfDay) { etod="TRASH"[type="dayset", set="trash"]}
DateTime Trash_Day (TimesOfDay) { channel="astro:sun:set120:set#start", etod="DAY"[type="dayset", set="trash"] }
DateTime Trash_Evening (TimesOfDay) { channel="astro:sun:local:set#start", etod="EVENING"[type="dayset", set="trash"] }
DateTime Trash_Night (TimesOfDay) { etod="NIGHT"[type="dayset", set="trash"] }
DateTime Trash_Bed (TimesOfDay) { etod="BED"[type="dayset", set="trash"] }

// Default holiday
DateTime Weekend_Day (TimesOfDay) { channel="astro:sun:set120:set#start", etod="DAY"[type="holiday"] }
DateTime Weekend_Evening (TimesOfDay) { channel="astro:sun:local:set#start", etod="EVENING"[type="holiday"] }
DateTime Default_Bed (TimesOfDay) { etod="BED"[type="holiday"] }

// Custom holiday
DateTime Weekend_Day (TimesOfDay) { channel="astro:sun:set120:set#start", etod="DAY"[type="custom", file="/openhab/conf/services/custom1.xml"] }
DateTime Weekend_Evening (TimesOfDay) { channel="astro:sun:local:set#start", etod="EVENING"[type="custom", file="/openhab/conf/services/custom1.xml"] }
DateTime Default_Bed (TimesOfDay) { etod="BED"[type="custom", file="/openhab/conf/services/custom1.xml"] }

ive set the item times in the ui for the fixed times and used the channel for the ones that I want to change with sunrise and sunset and it looks like they should be working.

now I get this error.

2020-12-27 05:25:36.791 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID '96b7b0f9d3' failed: Default_Bed has type custom which requires a 'file' value to be defined.
Invalid metadata for Item! Expected metadata in the form of etod="STATE"[type="daytype", set="dayset", file="uri"] where set is required if type is dayset and file is required if type is custom. in <eval> at line number 255 at column number 6

Well, yeah, you defined the “Default_Bed” several times. I just see that that’s an error in Rich’s item definitions.
If you want to use weekends/holidays (trash day - really?), every time needs to have its own item. So if you want to have a Bed Time for weekends, call it Weekend_Bed.
Also, did you configure the ephemeris.cfg accordingly? That’s the main reason you get that error message, because ephemeris does not yet know which days are regular days or weekends.

And did you put your country’s settings in Settings/Ephemeris in Openhab?

My advice: stick with Default Days at first, and add the weekends/holidays later when it’s working correctly. Just put a double slash “//” in front of the items below the Default day to deactivate them.

I’ll take a bite to try and remember to fix that when I get back to a computer in a few days.

1 Like

I wouldn’t have noticed it either, as I don’t use weekends. Just stumbled on it when Rick tried to use the items file verbatim.

How did you do this?
I only get:

And this ERROR when I run the rule:

2020-12-28 00:01:00.850 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'b6c5df081b' failed: Default_Morning's state is undefined
Invalid metadata for Item! Expected metadata in the form of etod="STATE"[type="daytype", set="dayset", file="uri"] where set is required if type is dayset and file is required if type is custom. in <eval> at line number 255 at column number 6

As I said before, use the console. If you look at it in karaf, you’ll probably notice that its state is NULL:
items list | grep Default_Morning

So you just have to initially give it a value:
openhab:send Default_Morning hh:mm:ss

After that it should look like this:
Default_Morning (Type=DateTimeItem, State=2020-12-27T05:15:00.000-0600, Label=MORNING, Category=time, Groups=[TimesOfDay])

Probably with a date in 1970, but that will be updated if you manually run the script.

Thanks for spelling it out. :relaxed:
Does this approach rely on the item state being restored from persistent upon a restart?

yes

mapDB can do that for you. Which is something you’d want anyway. Most battery-operated devices only send updates when there’s a change, which means that they can be silent for most of the day. Without mapDB, those values will be NULL on a Openhab restart until that update happens. With mapDB they get populated with their former values on startup.

I opted for this solution instead, in a when System started rule:

  Default_Morning.sendCommand("06:00:00")
  Default_Night.sendCommand("23:00:00")
  Default_Bed.sendCommand("23:59:59")