Time Based State Machine [3.2.0;3.4.9]

This. It’s not easy to support. All the rule sees and knows is that you have a bunch of Items and each Item has a DateTime. It puts those in order and schedules the timers. It has no concept that MORNING should normally happen before DAY.

However, all is not lost. You have some options depending on where the DateTime in Morning and Day comes from.

  1. Set a no earlier than in the Sunrise channel to something later than the static start for MORNING. So if you set that, no matter what the sun is doing, sunrise will never be earlier than 6:46 AM (for example).

  2. My MORNING is actually driven by the alarm clock on my phone. When it’s set it triggers a rule (using the Alarm Clock rule template for now, but you should use the new Time is <item> trigger). The rule checks to see if it’s NIGHT or BED and only if so does it run and set the TimeOfDay Item to MORNING. On weekends and days where I don’t need to get up, no alarm is set and those days do not have MORNING state.

  3. You can modify the rule and make it smarter. Add some if statements that look to see if DAY actually occurs before MORNING and adjust the times as appropriate. But that’s something that will be unique to you and can’t be something I can make generic. You could even put this into a separate rule that checks the DateTimes sometime after the rule runs at midnight and adjusts the times as necessary. Changing the DateTimes will cause the rule to run again and reschedule the Timers.

Tl;dr, it’s up to you to make sure the DateTimes are in the order you require, but there are options to make sure that’s the case that are not too onerous.

1 Like

Awesome, thanks for the comprehensive answer and providing even 3 different approaches. I wasn’t aware that I can set a earliest/latest for the astro binding, whichdoes the trick for me and is so simple to implement!

Thanks again!

I hate to pollute this thread with a support request (please lmk if there is a more appropriate venue and I’ll happily redirect!), but I was hoping someone may be able to offer some insight. My TOD setup has been working rather well, until I noticed the following (that I had not seen in the couple of weeks I have been using my current TOD config) happen tonight:

2022-11-02 21:00:00.005 [INFO ] [del.script.Rules.rules_tools.Time_SM] - Transitioning Time of Day from EVENING to NIGHT
2022-11-03 00:00:00.582 [INFO ] [del.script.Rules.rules_tools.Time_SM] - Today is a default day.
2022-11-03 00:01:00.695 [INFO ] [del.script.Rules.rules_tools.Time_SM] - The current time of day is DAY

(at which point TimeOfDay became DAY, and triggered my script that turned lights on)

This is incredibly odd to me (and somewhat unfortunate, because it resulted in the bedroom lights turning on while my sick partner was asleep! :/), because normally 00:01:00.695 would not correspond to DAY, and there was no “Transitioning” message in the log, just a state change. Looking back through my event logs, this seems to be the first time this has happened out of ~two weeks of logs, so I am somewhat doubtful that it is mis-configuration on my part (though I would always be happy to learn it is!). I’m not aware of anything in my config changing recently related to TOD.

openhab> items list Default*
Default_Bed (Type=DateTimeItem, State=2022-11-02T00:05:00.000-0700, Label=null, Category=null, Tags=[totd], Groups=[TimesOfDay])
Default_Day (Type=DateTimeItem, State=2022-11-02T09:30:00.000-0700, Label=null, Category=null, Tags=[totd], Groups=[TimesOfDay])
Default_Morning (Type=DateTimeItem, State=2022-11-03T08:10:00.000-0700, Label=null, Category=null, Groups=[TimesOfDay])
Default_Night (Type=DateTimeItem, State=2022-11-03T21:00:00.000-0700, Label=null, Category=null, Tags=[totd], Groups=[TimesOfDay])
Default_Evening (Type=DateTimeItem, State=2022-11-03T18:10:00.000-0700, Label=null, Category=null, Groups=[TimesOfDay])

Where would you look to debug this infrequent problem? I would not expect that createTimersGenerator() would events.sendCommand(), but it does. I’ll enable debug on that module, but if you have any guesses on where to look I’ll happily poke at it. :slight_smile: Upon entry, mostRecentState would have been NIGHT according to my event log, which means that it hit if(dt.isBefore(now) && dt.isAfter(mostRecentTime)) {}.

Debug context: openhab 3.3.0 in a docker container on ARM, javascript is marketplace:128245 (I’m unsure if this auto-updates, but I set this up on 10/20 and haven’t touched it since).

That’s what this thread is for.

I probably won’t be able to help much without more information. Either modify your log4j2.xml file or the karaf console to add a logger for org.openhab.automation.script.Rules.rules_tools set to DEBUG level logging or modify the rule itself and change all the log debug statements to log infos. Then post the debug logs from the rule when this behavior occurs.

For some reason, the part of the code that schedules the timers for the next day scheduled the DAY timer to one minute after midnight. Without knowing what the state of that DateTime Item was at the time the rule ran I couldn’t guess as to what went wrong.

It might help a bit to see the events.log showing which of your DateTime Items changed and when they changed in relation to when the state changed to DAY. It might be a timing issue where the rule ran in response to an event from Astro (for example) before To Today was able to update the DAY Item, meaning it was still scheduled for yesterday and ran immediately. I’ve never seen that before but it makes sense logically.

If that is the case I can add some logic to prevent that. But I need more details to know if that’s the case.

It doesn’t. But I’ve not modified the template since February of this year so you should have the latest.

To upgrade you need to remove the template, add it again, and then delete and recreate the rule(s) instantiated from the template.

Thanks, Rich. I always appreciate the support I see you give. I enabled debug on org.openhab.automation.script.Rules.rules_tools.Time_SM, so hopefully I’ll catch a hint later.

At 00:00, I see my hard-coded times rolled forward to the next day via marketplace:127921.
I also see my *_Morning and *_Evening times updated (Astro; the others are manually set).

I give the earliest event of the day (00:05) a several-minute buffer after time rolls forward to hopefully avoid any race condition.

I left one non-TOD entry in my event log here for fun: I came home right around midnight and triggered my motion sensor, the clear event happening at the very same second that the DAY command was issued, but it was SURELY a complete coincidence. :slight_smile: (There are no rules tied to it currently - just a note in the event log.) I don’t see any more hints here.

2022-11-03 00:00:00.009 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Holiday_Bed' changed from 2022-11-02T02:00:00.000-0700 to 2022-11-03T02:00:00.000-0700
2022-11-03 00:00:00.025 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Night' changed from 2022-11-02T21:00:00.000-0700 to 2022-11-03T21:00:00.000-0700
2022-11-03 00:00:00.027 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Weekend_Bed' changed from 2022-11-02T02:00:00.000-0700 to 2022-11-03T02:00:00.000-0700
2022-11-03 00:00:00.028 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Holiday_Night' changed from 2022-11-02T22:00:00.000-0700 to 2022-11-03T22:00:00.000-0700
2022-11-03 00:00:00.052 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Weekend_Day' changed from 2022-11-02T11:00:00.000-0700 to 2022-11-03T11:00:00.000-0700
2022-11-03 00:00:00.060 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Holiday_Day' changed from 2022-11-02T11:00:00.000-0700 to 2022-11-03T11:00:00.000-0700
2022-11-03 00:00:00.062 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Weekend_Night' changed from 2022-11-02T22:00:00.000-0700 to 2022-11-03T22:00:00.000-0700
2022-11-03 00:00:30.125 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Holiday_Morning' changed from 2022-11-02T08:09:00.000-0700 to 2022-11-03T08:10:00.000-0700
2022-11-03 00:00:30.136 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Weekend_Morning' changed from 2022-11-02T08:09:00.000-0700 to 2022-11-03T08:10:00.000-0700
2022-11-03 00:00:30.137 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Morning' changed from 2022-11-02T08:09:00.000-0700 to 2022-11-03T08:10:00.000-0700
2022-11-03 00:00:30.139 [INFO ] [openhab.event.ChannelTriggeredEvent ] - astro:sun:local:morningNight#event triggered START
2022-11-03 00:00:30.140 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Holiday_Evening' changed from 2022-11-02T18:11:00.000-0700 to 2022-11-03T18:10:00.000-0700
2022-11-03 00:00:30.141 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Evening' changed from 2022-11-02T18:11:00.000-0700 to 2022-11-03T18:10:00.000-0700
2022-11-03 00:00:30.143 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Weekend_Evening' changed from 2022-11-02T18:11:00.000-0700 to 2022-11-03T18:10:00.000-0700
2022-11-03 00:01:00.213 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'F1_LivingRoom_multisensor_Motion' changed from ON to OFF
2022-11-03 00:01:00.756 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'TimeOfDay' received command DAY

Ok, that’s weird.

First, please use code fences for logs.

```
code/logs/configs go here
```

Here is what I see.

  • 9 msec after midnight the To Today rule starts updating the time of Day Items. This will trigger the Time Of Day rule
  • It takes about half a second for that first trigger to get to the point where it’s calculated what type of day it is. During all this time the many triggers caused by the To Today rule are queueing up. A 30 second timer is created to wait for all these updates to finish happening before calculating the time of day.
  • The Time of Day rule is triggered 6 more times from the To Today but because the timer exists, it’s just rescheduled. The Items updates are:
    • Holiday_Bed
    • Default_Night
    • Weekend_Bed
    • Holiday_Night
    • Weekend_Day
    • Holiday_Day
    • Weekend_Night
  • At 30 seconds after midnight Astro kicks in and calculates the next day’s date times. The timer still exists in the Time of Day Rule so the timer gets rescheduled. The Items updated by Astro are:
    • Holiday_Morning
    • Weekend_Morning
    • Default_Morning
    • Holiday_Evening
    • Default_Evening
    • Weekend_Evening
  • About one minute after midnight the timer finally expires and calculates the time of day as DAY.

Notice anything missing? Default_Day was never updated. So it still had the previous date which was in the past and therefore it was the earliest date time found by the rule.

So the problem isn’t the Time of Day rule, it’s the To Today rule (I assume). Why wasn’t Default_Day updated? Does it still have the “tod” tag (given its state I’m assuming it’s not driven by Astro) or is it driven by something else that To Today?

Looking at the states you posted above, indeed, both Default_Bed and Default_Day have yesterday’s date. So what ever is supposed to update those two Items (perhaps others) is not working.

Put the To Today rule into DEBUG logging too (assuming these rules are usually driven by To Today). That’s where the problem lies.

I might be able to put a check for date times that are the previous day. But I don’t know what’s appropriate to do in that case, ignore it? I’m not sure that’s right.

re code fences: thanks - that’s what I was looking for! That’s what I get for posting after midnight! Fixed.

Wow, good eye! I didn’t notice the absence of Default_Day. I just confirmed that Default_Day has the same (sole) tag as Default_Night (which, as you saw, did get moved). I’ll definitely look at the rollover script more closely now. Funny that this has worked so well every other day:

cat /usr/local/openhab/userdata/logs/events.log | grep Default_Day
2022-10-20 16:24:18.351 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from NULL to 2022-10-20T09:30:00.000-0700
2022-10-21 00:00:03.729 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-10-20T09:30:00.000-0700 to 2022-10-21T09:30:00.000-0700
2022-10-22 00:00:00.596 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-10-21T09:30:00.000-0700 to 2022-10-22T09:30:00.000-0700
2022-10-23 00:00:00.488 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-10-22T09:30:00.000-0700 to 2022-10-23T09:30:00.000-0700
2022-10-24 00:00:00.485 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-10-23T09:30:00.000-0700 to 2022-10-24T09:30:00.000-0700
2022-10-25 00:00:00.480 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-10-24T09:30:00.000-0700 to 2022-10-25T09:30:00.000-0700
2022-10-26 00:00:00.473 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-10-25T09:30:00.000-0700 to 2022-10-26T09:30:00.000-0700
2022-10-27 00:00:00.548 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-10-26T09:30:00.000-0700 to 2022-10-27T09:30:00.000-0700
2022-10-28 00:00:00.524 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-10-27T09:30:00.000-0700 to 2022-10-28T09:30:00.000-0700
2022-10-29 00:00:00.475 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-10-28T09:30:00.000-0700 to 2022-10-29T09:30:00.000-0700
2022-10-30 00:00:00.613 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-10-29T09:30:00.000-0700 to 2022-10-30T09:30:00.000-0700
2022-10-31 00:00:00.572 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-10-30T09:30:00.000-0700 to 2022-10-31T09:30:00.000-0700
2022-11-01 00:00:00.504 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-10-31T09:30:00.000-0700 to 2022-11-01T09:30:00.000-0700
2022-11-02 00:00:00.471 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'Default_Day' changed from 2022-11-01T09:30:00.000-0700 to 2022-11-02T09:30:00.000-0700

Somewhat unrelated, is it normal to have spaces embedded in the namespace like this?
.getLogger(“org.openhab.model.script.rules_tools.To Today”)

It doesn’t matter one way or the other. I don’t know if it’s normal but it doesn’t cause any problems.

I’m late to the party here, I know.
I’ve been doing a lot of digging around - both on this thread and the one for OH4+, in addition to looking around at the ToToday automation.
I am having a difficult time understanding how the Time and Date Machine processes the objects.

My items file is:

String TimeOfDay "Current Time of Day [MAP(weather.map):%s]" <tod>   // weather.map has mapping from caps to lowercase for times of Day

Group TimesOfDay

DateTime DefaultMorning "Default Morning"  <time>  (TimesOfDay) ["DTUpdate"] { tod_sm="MORNING" [type="default"] }
DateTime DefaultDay "Default Day"  <sunrise>  (TimesOfDay) { channel="astro:sun:local:rise#end", tod_sm="DAY" [type="default"] }
DateTime DefaultTwilight "Default Twilight"  <sunset>  (TimesOfDay) { channel="astro:sun:local:set#start", tod_sm="TWILIGHT" [type="default"] }
DateTime DefaultEvening "Default Evening"  <time>  (TimesOfDay) ["DTUpdate"] { tod_sm="EVENING"[type="default"] }
DateTime DefaultNight "Default Night"  <moon>  (TimesOfDay) ["DTUpdate"] { tod_sm="NIGHT"[type="default"] }

DateTime WeekdayMorning "Weekday Morning"  <time>  (TimesOfDay) ["DTUpdate"] { tod_sm="MORNING" [type="weekday"] }
DateTime WeekdayDay "Weekday Day"  <sunrise>  (TimesOfDay) { channel="astro:sun:local:rise#end", tod_sm="DAY" [type="weekday"] }
DateTime WeekdayTwilight "Weekday Twilight"  <sunset>  (TimesOfDay) { channel="astro:sun:local:set#start", tod_sm="TWILIGHT" [type="weekday"] }
DateTime WeekdayEvening "Weekday Evening"  <time>  (TimesOfDay) ["DTUpdate"] { tod_sm="EVENING"[type="weekday"] }
DateTime WeekdayNight "Weekday Night"  <moon>  (TimesOfDay) ["DTUpdate"] { tod_sm="NIGHT"[type="weekday"] }

DateTime WeekendMorning "Weekend Morning"  <time>  (TimesOfDay) ["DTUpdate"] { tod_sm="MORNING" [type="weekend"] }
DateTime WeekendDay "Weekend Day"  <sunrise>  (TimesOfDay) { channel="astro:sun:local:rise#end", tod_sm="DAY" [type="weekend"] }
DateTime WeekendTwilight "Weekend Twilight"  <sunset>  (TimesOfDay) { channel="astro:sun:local:set#start", tod_sm="TWILIGHT" [type="weekend"] }
DateTime WeekendEvening "Weekend Evening"  <time>  (TimesOfDay) ["DTUpdate"] { tod_sm="EVENING"[type="weekend"] }
DateTime WeekendNight "Weekend Night"  <moon>  (TimesOfDay) ["DTUpdate"] { tod_sm="NIGHT"[type="weekend"] }

DateTime HolidayMorning "Holiday Morning"  <time>  (TimesOfDay) ["DTUpdate"] { tod_sm="MORNING" [type="holiday"] }
DateTime HolidayDay "Holiday Day"  <sunrise>  (TimesOfDay) { channel="astro:sun:local:rise#end", tod_sm="DAY" [type="holiday"] }
DateTime HolidayTwilight "Holiday Twilight"  <sunset>  (TimesOfDay) { channel="astro:sun:local:set#start", tod_sm="TWILIGHT" [type="holiday"] }
DateTime HolidayEvening "Holiday Evening"  <time>  (TimesOfDay) ["DTUpdate"] { tod_sm="EVENING"[type="holiday"] }
DateTime HolidayNight "Holiday Night"  <moon>  (TimesOfDay) ["DTUpdate"] { tod_sm="NIGHT"[type="holiday"] }

(the weather.map reference is one that changes lowercase to upper case for the times of day)

I no longer am using the TOD.rules I had in older versions, and have installed the Time Based State Machine for my OH 3.4.2 installation.
I have installed ToToday, for the item tag DTUpdate.
I installed standalone Date and Time Widgets, and have set all the values of the non-astro DateTIme items.

When the day switches over to a weekend, what happens? Does the default item override everything? What happens to the default/weekday, default/weekend, default/holiday, or weekday/holiday conflicts that can occur? (I have my regionalization set up in Ephemeris). Does ToToday blindly move all my holiday start times over to the next non-holiday?
Can I, without further modification, use rules triggered by receiving ‘MORNING’ to do my usual morning things, with the actual time varying by weekday/weekend/holiday parameters?

I haven’t found the answer to this in my reading, although I may have gone cross-eyed a couple times.
Without an exposed dsl rule file that I can tweak, it’s hard to know what happens under the surface.

Thanks
Ben
OH 3.4.2 on a RPi4 running Openhabian

At a high level the rule gets triggered a little after midnight and waits a little because it gets triggered multiple times as To Today and Astro and others change the states of the Items.

When the changes stop the rule runs.

First it looks to see what types of day it is using Ephemeris. I say “types” because it could be more than one (e.g. a holiday and a weekend). Then it looks to see if there is a set of items for that day type and use the first one it finds.

It looks in this order: custom, holiday, custom day type, weekend, weekday, default.

No, it’s what is used if none of the others apply.

If you have a set of items for holiday, weekend and default and not one for weekdays (for example), if today is a holiday no matter what other type of day it is the holiday set will be used. Only if there is no set of items for the type of day is the default set used.

And again, the type of day is determined by Ephemeris.

To Today blindly moves those date time items tagged to today’s date (with some extra stuff to properly handle DST changes). It knows nothing about holidays, weekdays or anything to do with Ephemeris and it doesn’t need to. They are completely separate and irrelevant to To Today. Only those Items that are not already updated daily should be tagged for To Today to process.

Ephemeris is used only by this rule to pick which set of date time items to use for today’s state machine.

That’s the whole point of this rule template. Set a different set of Items for default, weekend, and holiday. If it’s a holiday that set of Items are used, even if it’s also a weekend. If it’s a weekend and not a holiday, the weekend set of Items are used. If it’s neither a holiday nor a weekend the default Items are used.

It’s not hidden. It’s not Rules DSL but all the code is there (in this case it’s Nashorn JS). Click on the Link to the source code at the bottom of the first link and you’ll see the raw voice. Or once you instantiate the rule from the template you can inspect and modify the code like any other rule.

Note that the 4.0 version of the template is rewritten in the newer JS and only looks at the time part of the Items so there is no more need for To Today.

Thank-you for your many detailed clarifications.

The one missing piece for me that I had been oblivious to was that after installing the Time Based State Machine, I had to add the rule template from Rules (First time doing this for me). Once I saw it was an option there, I added it, and it started to update all the DateTIme items as I expected it.
I am not sure how common this sort of oversight is, but it may be of benefit to add the step ‘and then add the rule’ to the instructions. Now that I’ve started to use these pre-made rule templates, I’ll certainly know to do it in the future.

Thanks again.

This is covered in Getting Started: https://www.openhab.org/docs/tutorial/rules_basic.html#rule-templates and somewhat in the Ruels Concetps page: https://www.openhab.org/docs/concepts/rules.html#rule-templates. It doesn’t make sense to duplicate this generic “how to use OH” in each and every rule template posting. That’s a whole lot of duplicated documentation that could be a real problem if the way to install and instantiate a rule from a template ever changes.

Sorry - another question.
I have many of the day state transitions working fine.
Twilight came at sunset (4:18PM), my outdoor lights came on. Then evening (8PM) happened a few hours later and many lights turned on, and night (11PM) happened and the remaining outdoor lights turned off.

  • this was all exactly as intended.

Then:
Just after midnight, it appears my state was changed to twilight (and as such my outdoor lights all came on). This was not intended. After

From event.log, searching for midnight (grep “2023-12-03 00:00:” events.log):

2023-12-03 00:00:00.136 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - MoveToToday updated: RUNNING
2023-12-03 00:00:00.221 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'DefaultNight' changed from 2023-12-02T23:00:00.000-0800 to 2023-12-03T23:00:00.000-0800
2023-12-03 00:00:00.223 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:00.228 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'HolidayNight' changed from 2023-12-02T23:00:00.000-0800 to 2023-12-03T23:00:00.000-0800
2023-12-03 00:00:00.230 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:00.233 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'WeekdayNight' changed from 2023-12-02T23:00:00.000-0800 to 2023-12-03T23:00:00.000-0800
2023-12-03 00:00:00.235 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'WeekdayEvening' changed from 2023-12-02T20:00:00.000-0800 to 2023-12-03T20:00:00.000-0800
2023-12-03 00:00:00.239 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:00.240 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'WeekendMorning' changed from 2023-12-02T09:00:00.000-0800 to 2023-12-03T09:00:00.000-0800
2023-12-03 00:00:00.242 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'WeekendEvening' changed from 2023-12-02T20:00:00.000-0800 to 2023-12-03T20:00:00.000-0800
2023-12-03 00:00:00.244 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:00.245 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:00.248 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:00.249 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:00.250 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:00.252 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:00.253 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'WeekdayMorning' changed from 2023-12-02T06:55:00.000-0800 to 2023-12-03T06:55:00.000-0800
2023-12-03 00:00:00.255 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'HolidayEvening' changed from 2023-12-02T20:00:00.000-0800 to 2023-12-03T20:00:00.000-0800
2023-12-03 00:00:00.256 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'DefaultEvening' changed from 2023-12-02T20:00:00.000-0800 to 2023-12-03T20:00:00.000-0800
2023-12-03 00:00:00.258 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - MoveToToday updated: IDLE
2023-12-03 00:00:00.260 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'WeekendNight' changed from 2023-12-02T23:00:00.000-0800 to 2023-12-03T23:00:00.000-0800
2023-12-03 00:00:00.262 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'DefaultMorning' changed from 2023-12-02T07:00:00.000-0800 to 2023-12-03T07:00:00.000-0800
2023-12-03 00:00:00.264 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'HolidayMorning' changed from 2023-12-02T09:00:00.000-0800 to 2023-12-03T09:00:00.000-0800
2023-12-03 00:00:00.265 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:00.267 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:00.268 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:00.269 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:00.271 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:00.272 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:00.273 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:00.275 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:00.276 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:00.278 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:00.279 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:00.280 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:00.281 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:00.282 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:00.283 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE

2023-12-03 00:00:28.201 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'TimeOfDay' received command TWILIGHT
2023-12-03 00:00:28.203 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'TimeOfDay' changed from NIGHT to TWILIGHT
2023-12-03 00:00:28.206 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - lights-15 updated: RUNNING
2023-12-03 00:00:28.218 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'FF_FrontPorch_Light' received command ON
2023-12-03 00:00:28.227 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'CH_Outdoor_Yard_Light' received command ON
2023-12-03 00:00:28.230 [INFO ] [penhab.event.ItemStatePredictedEvent] - Item 'FF_FrontPorch_Light' predicted to become ON
2023-12-03 00:00:28.246 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'FF_Front_Porch_Accessory_Outlet' received command ON
2023-12-03 00:00:28.248 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - lights-15 updated: IDLE
2023-12-03 00:00:28.249 [INFO ] [penhab.event.ItemStatePredictedEvent] - Item 'CH_Outdoor_Yard_Light' predicted to become ON
2023-12-03 00:00:28.256 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'FF_FrontPorch_Light' changed from OFF to ON
2023-12-03 00:00:28.257 [INFO ] [hab.event.GroupItemStateChangedEvent] - Item 'Outdoor_Lights' changed from OFF to ON through FF_FrontPorch_Light
2023-12-03 00:00:28.258 [INFO ] [penhab.event.ItemStatePredictedEvent] - Item 'FF_Front_Porch_Accessory_Outlet' predicted to become ON
2023-12-03 00:00:28.264 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'CH_Outdoor_Yard_Light' changed from OFF to ON
2023-12-03 00:00:28.265 [INFO ] [hab.event.GroupItemStateChangedEvent] - Item 'CH_Outdoor_Lights' changed from OFF to ON through CH_Outdoor_Yard_Light
2023-12-03 00:00:28.266 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'FF_Front_Porch_Accessory_Outlet' changed from OFF to ON
2023-12-03 00:00:30.485 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'DefaultDay' changed from 2023-12-02T07:58:00.000-0800 to 2023-12-03T07:55:00.000-0800
2023-12-03 00:00:30.490 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'WeekdayDay' changed from 2023-12-02T07:58:00.000-0800 to 2023-12-03T07:55:00.000-0800
2023-12-03 00:00:30.498 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:30.499 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'WeekendDay' changed from 2023-12-02T07:58:00.000-0800 to 2023-12-03T07:55:00.000-0800
2023-12-03 00:00:30.502 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'HolidayDay' changed from 2023-12-02T07:58:00.000-0800 to 2023-12-03T07:55:00.000-0800
2023-12-03 00:00:30.506 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'DefaultDay' changed from 2023-12-03T07:55:00.000-0800 to 2023-12-03T07:59:00.000-0800
2023-12-03 00:00:30.507 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'WeekdayDay' changed from 2023-12-03T07:55:00.000-0800 to 2023-12-03T07:59:00.000-0800
2023-12-03 00:00:30.509 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'WeekendDay' changed from 2023-12-03T07:55:00.000-0800 to 2023-12-03T07:59:00.000-0800
2023-12-03 00:00:30.511 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'HolidayDay' changed from 2023-12-03T07:55:00.000-0800 to 2023-12-03T07:59:00.000-0800
2023-12-03 00:00:30.514 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'DefaultTwilight' changed from 2023-12-02T16:19:00.000-0800 to 2023-12-03T16:18:00.000-0800
2023-12-03 00:00:30.515 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'WeekdayTwilight' changed from 2023-12-02T16:19:00.000-0800 to 2023-12-03T16:18:00.000-0800
2023-12-03 00:00:30.518 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'WeekendTwilight' changed from 2023-12-02T16:19:00.000-0800 to 2023-12-03T16:18:00.000-0800
2023-12-03 00:00:30.519 [INFO ] [openhab.event.ItemStateChangedEvent ] - Item 'HolidayTwilight' changed from 2023-12-02T16:19:00.000-0800 to 2023-12-03T16:18:00.000-0800
2023-12-03 00:00:30.537 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:30.539 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:30.543 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:30.545 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:30.549 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:30.550 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:30.553 [INFO ] [openhab.event.ChannelTriggeredEvent ] - astro:sun:local:morningNight#event triggered START
2023-12-03 00:00:30.556 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:30.558 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:30.562 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:30.564 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:30.567 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:30.568 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:30.569 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:30.571 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:30.572 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:30.574 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:30.575 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:30.577 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:30.578 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:30.580 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:30.581 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE
2023-12-03 00:00:30.582 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: RUNNING
2023-12-03 00:00:30.584 [INFO ] [openhab.event.RuleStatusInfoEvent   ] - TODStateMachine updated: IDLE

rule in question (light-15):

rule "Porch light on at dusk"
when
  Item TimeOfDay changed to "TWILIGHT"
then
if (FrontPorchSuppressLights.state == OFF) {
  FF_FrontPorch_Light.sendCommand(ON)
  CH_Outdoor_Yard_Light.sendCommand(ON) // CH parking lights
  logInfo("lights.rules", "TWILIGHT started, entry lights turned on")
}
else {
  logInfo("lights.rules", "Was going to turn porch lights on due to dusk, but already too late or was suppressed")
}
  FF_Front_Porch_Accessory_Outlet.sendCommand(ON) 

end

relevant TOD items:

DateTime WeekendMorning "Weekend Morning"  <time>  (TimesOfDay) ["DTUpdate"] { tod_sm="MORNING" [type="weekend"] }
DateTime WeekendDay "Weekend Day"  <sunrise>  (TimesOfDay) { channel="astro:sun:local:rise#end", tod_sm="DAY" [type="weekend"] }
DateTime WeekendTwilight "Weekend Twilight"  <sunset>  (TimesOfDay) { channel="astro:sun:local:set#start", tod_sm="TWILIGHT" [type="weekend"] }
DateTime WeekendEvening "Weekend Evening"  <time>  (TimesOfDay) ["DTUpdate"] { tod_sm="EVENING"[type="weekend"] }
DateTime WeekendNight "Weekend Night"  <moon>  (TimesOfDay) ["DTUpdate"] { tod_sm="NIGHT"[type="weekend"] }

My (unmodified/noncustomized from template implementation) TOD State Machine rule code:
configuration:

  namespace: tod_sm
  timesOfDayGrp: TimesOfDay
  timeOfDay: TimeOfDay
triggers:
  - id: "1"
    configuration:
      groupName: TimesOfDay
    type: core.GroupStateChangeTrigger
  - id: "2"
    configuration:
      startlevel: 20
    type: core.SystemStartlevelTrigger
conditions: []
actions:
  - inputs: {}
    id: "3"
    label: Determine current time of day
    configuration:
      type: application/javascript
      script: >
        // Imports 

        if(typeof(require) === "function") Object.assign(this, require('@runtime'));

        var logger = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.rules_tools.Time_SM"); 

        this.Ephemeris = (this.Ephemeris === undefined) ? Java.type("org.openhab.core.model.script.actions.Ephemeris") : this.Ephemeris; 

        this.ZonedDateTime = (this.ZonedDateTime === undefined) ? Java.type("java.time.ZonedDateTime") : this.ZonedDateTime; 


        //   Get Metadata query stuff 

        this.FrameworkUtil = (this.FrameworkUtil === undefined) ? Java.type("org.osgi.framework.FrameworkUtil") : this.FrameworkUtil; 

        this.ScriptHandler = Java.type("org.openhab.core.automation.module.script.rulesupport.shared.ScriptedHandler");

        this._bundle = (this._bundle === undefined) ? FrameworkUtil.getBundle(ScriptHandler.class) : this._bundle;

        this.bundle_context = (this.bundle_context === undefined) ? this._bundle.getBundleContext() : this.bundle_context; 

        this.MetadataRegistry_Ref = (this.MetadataRegistry_Ref === undefined) ? bundle_context.getServiceReference("org.openhab.core.items.MetadataRegistry") : this.MetadataRegistry_Ref; 

        this.MetadataRegistry = (this.MetadataRegistry === undefined) ? bundle_context.getService(MetadataRegistry_Ref) : this.MetadataRegistry; 

        this.Metadata = (this.Metadata === undefined) ? Java.type("org.openhab.core.items.Metadata") : this.Metadata; 

        this.MetadataKey = (this.MetadataKey === undefined) ? Java.type("org.openhab.core.items.MetadataKey") : this.MetadataKey; 


        // Constants 

        var ETOD_ITEM = "TimeOfDay"; 

        var ETOD_GROUP = "TimesOfDay"; 

        var DAY_TYPES = ["default", "weekday", "weekend", "dayset", "holiday", "custom"]; 

        var EXPECTED = "Invalid metadata for Item! "
                     + "Expected metadata in the form of tod_sm=\"STATE\"[type=\"daytype\", set=\"dayset\", file=\"uri\"] "
                     + "where set is required if type is dayset and file is required if type is custom.";
        var ETOD_NAMESPACE = "tod_sm"; 


        // TODO Load timer manager from a library

        // Load TimerMgr 

        //this.OPENHAB_CONF = (this.OPENHAB_CONF === undefined) ? java.lang.System.getenv("OPENHAB_CONF") : this.OPENHAB_CONF; 

        //load(OPENHAB_CONF+'/automation/lib/javascript/community/timerMgr.js'); 

        //load(OPENHAB_CONF+'/automation/lib/javascript/community/timeUtils.js'); 

        var TimerMgr = function() {
          var OPENHAB_CONF = java.lang.System.getenv("OPENHAB_CONF");
          this.log = Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.rules_tools.rules_tools.TimerMgr");
          this.log.debug("Building timerMgr instance.");
          this.timers = {};
        //  this.log.debug("Loading timeUtils");

        //  load(OPENHAB_CONF+'/automation/lib/javascript/community/timeUtils.js');
          this.ScriptExecution = Java.type("org.openhab.core.model.script.actions.ScriptExecution");
          this.log.debug("Timer Mgr is ready to operate");
        }


        TimerMgr.prototype._notFlapping = function(key) {
          this.log.debug("Timer expired for " + key);
          if (key in this.timers && "notFlapping" in this.timers[key]) {
            this.log.debug("Calling expired function " + this.timers[key]["notFlapping"]);
            this.timers[key]["notFlapping"]();
          }
          if (key in this.timers){
            this.log.debug("Deleting the expired timer");
            delete this.timers[key];
          }
        },


        TimerMgr.prototype._noop = function() { },


        TimerMgr.prototype.check = function(key, when, func, reschedule, flappingFunc) {
          this.log.debug("Timer manager check called");
          if (reschedule === undefined) reschedule = false;

          // var timeout = this.toDateTime(when); We will assume it's already a ZonedDateTime
          var timeout = when;
          this.log.debug("Timer to be set for " + timeout.toString());

          // Timer exists
          if (key in this.timers){
            if (reschedule){
              this.log.debug("Rescheduling timer " + key + " for  " + timeout.toString());
              this.timers[key]["timer"].reschedule(timeout);
            }
            else {
              this.log.debug("Cancelling timer " + key);
              this.cancel(key);
            }
            if (flappingFunc !== undefined){
              this.log.debug("Running flapping function for " + key);
              flappingFunc();
            }
          }
          
          // Timer doesn't already exist, create one
          else {
            this.log.debug("Creating timer for " + key);
            var timer = this.ScriptExecution.createTimerWithArgument(timeout, this, function(context) { context._notFlapping(key); });
            this.timers[key] = { "timer": timer,
                                 "flapping": flappingFunc,
                                 "notFlapping": (func !== undefined) ? func : this._noop }
            this.log.debug("Timer created for " + key);
          }
        },


        TimerMgr.prototype.hasTimer = function(key) {
          return key in this.timers;
        },


        TimerMgr.prototype.cancel = function(key) {
          if (key in this.timers) {
            this.timers[key]["timer"].cancel();
            delete this.timers[key];
          }
        },


        TimerMgr.prototype.cancelAll = function() {
          for (var key in this.timers) {
            if (!this.timers[key]["timer"].hasTerminated() && !this.timers[key]["timer"].isRunning()) {
              this.log.debug("Timer has not terminated, cancelling timer " + key);
              this.cancel(key);
            }
            delete this.timers[key];
            this.log.debug("Timer entry has been deleted for " + key);
          }
        }

        // END TODO


        /**
         * Return the value or a key value from the Item's metadata
         * @param {string} item name of the item
         * @param {string} namespace metadata namespace to pull
         * @param {string} key index into the configuration dict for the value
         * @return {string} value assocaited with key or null if it doesn't exist.
         */
        var getValue = function(item, namespace, key) {
          var md = MetadataRegistry.get(new MetadataKey(namespace, item));
          if(md === null || md === undefined) {
            return null;
          }
          else if(key === undefined) {
            return md.value;
          }
          else {
            return md.configuration[key];
          }
        } 


        /**
         * Verify Item and Item metadata
         * @param {string} item name of the Item
         * return {string} error string or null if the metadata checks out
         */
        var verifyMetadata = function(item) {

        //  if(items[item].class == UnDefType.class) {

        //    return item +"'s state is " + items[items];

        //  }
          if(getValue(item, ETOD_NAMESPACE) === null) {
            return item + " lacks metadata or metadata value.";
          }
          var type = getValue(item, ETOD_NAMESPACE, "type");
          if(type === null) {
            return item + " lacks a type key."
          }
          
          if(DAY_TYPES.indexOf(type) < 0) {
            return item + " has " + type + " which is not a valid day type, expected one of " + DAY_TYPES + ".";
          }
          
          if(type == "dayset" && getValue(item, ETOD_NAMESPACE, "set") === null) {
            return item + " has type " + type + " which requires a 'set' value to be defined.";
          }
          
          if(type == "custom" && getValue(item, ETOD_NAMESPACE, "file") === null ) {
            return item + " has type " + type + " which requires a 'file' value to be defined.";
          }
          
          return null;
        } 


        /**
         * Get a list of all the Items that have ephem metadata with type
         * @param {java.util.List} etodItems collection of all the ETOD Items
         * @param {string} type the type of day 
         * @return {java.util.List} those Items with a type metadata matching type
         */
        var getType = function(etodItems, type){
          return etodItems.stream()
                          .filter(function(item){ 
                              return getValue(item.name, ETOD_NAMESPACE, "type") == type;
                           })
                          .toArray();
        } 


        /**
         * Pull the set of Items for today based on Ephemeris
         * @param {java.util.List} etodItems collection of all ETOD Items
         * @return {java.util.List} only those Items defined for today's daytype
         */
        var getTodayItems = function(etodItems) {
          /** 
          Get the Items for today. Hierarchy is:
            - custom
            - holiday
            - dayset
            - weekend
            - weekday
            - default
          */
          var startTimes = {"default": getType(etodItems, "default"),
                            "weekday": (!Ephemeris.isWeekend()) ? getType(etodItems, "weekday") : [],
                            "weekend": (Ephemeris.isWeekend()) ? getType(etodItems, "weekend") : [],
                            "dayset": etodItems.stream()
                                               .filter(function(item) {
                                                  return getValue(item.name, ETOD_NAMESPACE, "type") == "dayset"
                                                         && Ephemeris.isInDayset(getValue(item.name, ETOD_NAMESPACE, "set"));
                                               })
                                               .toArray(),
                            "holiday": (Ephemeris.isBankHoliday()) ? getType(etodItems, "holiday") : [],
                            "custom": etodItems.stream()
                                               .filter(function(item) {
                                                  return getValue(item.name, ETOD_NAMESPACE, "type") == "custom"
                                                         && Ephemeris.isBankHoliday(0, getValue(item.name, ETOD_NAMESPACE, "file"));
                                               })
                                               .toArray()
                           };
          var dayType = null;
          if(startTimes["custom"].length > 0) {
            dayType = "custom";
          }
          else if(startTimes["holiday"].length > 0) {
            dayType = "holiday";
          }
          else if(startTimes["dayset"].length > 0) {
            dayType = "dayset";
          }
          else if(startTimes["weekend"].length > 0) {
            dayType = "weekend";
          }
          else if(startTimes["weekday"].length > 0) {
            dayType = "weekday";
          }
          else if(startTimes["default"].length > 0) {
            dayType = "default";
          }
          logger.info("Today is a " + dayType + " day.");
          return (dayType === null) ? null : startTimes[dayType];

        } 


        /**
         * Update Items to today
         * @param {java.util.List} times list of all the ETOD Items for today
         */
        /** Use To Today rule template

        var moveTimes = function(times) {
          var ZonedDateTime = Java.type("java.time.ZonedDateTime"); 
          var now = ZonedDateTime.now();
          for each(var time in times) {
            if(time.state.zonedDateTime.isBefore(now.withHour(0).withMinute(0).withSecond(0))) {
              events.postUpdate(time.name, toToday(items[time.name]).toString());
              logger.info("Moved " + time.name + " to today.");
            }
          }
        } */


        /**
         * Create timers for all Items with a time in the future
         * @param {java.util.List} times list of all the ETOD Items for todayu
         */
        var createTimersGenerator = function(times, timers) {
          return function() {
            var ZonedDateTime = Java.type("java.time.ZonedDateTime"); 
            var now = ZonedDateTime.now();
            var mostRecentTime = now.minusDays(1);
            var mostRecentState = items[ETOD_ITEM];
            logger.debug("Cancelling any existing timers");
            timers.cancelAll();
            logger.debug("Existing timers have been cancelled");
            for each (var time in times) {
              var name = time.name;
              if(time.state.class === UnDefType.class) {
                logger.warn("Time State Machine Item " + name + " is NULL or UNDEF, ignoring");
              }
              else {
                var dt = time.state.zonedDateTime
                var state = getValue(name, ETOD_NAMESPACE);
                if(dt.isBefore(now) && dt.isAfter(mostRecentTime)) {
                  logger.debug("NOW:    " + state + " start time " + dt + " is in the past " 
                               + " after " + mostRecentTime);
                  mostRecentTime = dt;
                  mostRecentState = state;
                }
                else if(dt.isAfter(now)) {
                  logger.debug("FUTURE: " + state + " scheduleing timer for " + dt);
                  timers.check(state, dt, etodTransitionGenerator(state));
                }
                else {
                  logger.debug("PAST  : " + state + " start time of " + dt + " is before " 
                               + now + " and before " + mostRecentState + " " + mostRecentTime);
                }
              }
            }
            logger.debug("Created " + (Object.keys(timers.timers).length - 1) + " time of day timers");
            logger.info("The current time of day is " + mostRecentState);
            if(items[ETOD_ITEM] != mostRecentState) {
              events.sendCommand(ETOD_ITEM, mostRecentState);
            }
          }
        } 


        /**
         * Transition to a new Time of Day
         * @TODO look into moving this to another rule we can call so it shows up in schedule
         * @param {string} state the new time of day state
         */
        var etodTransitionGenerator = function(state) {
          return function() {
            logger.info("Transitioning Time of Day from " + items[ETOD_ITEM] + " to " + state);
            events.sendCommand(ETOD_ITEM, state);
          }
        } 


        //-------------------------------------------- 

        // Main body of rule 

        if(this.timers === undefined){
          logger.debug("Creating timer manager");
        } 

        this.timers = (this.timers === undefined) ? new TimerMgr() : this.timers; 


        // Skip if we have a flapping timer set 

        if(!this.timers.hasTimer("ephem_tod_rule")) {

          // Check that all the required Items and Groups exist
          if(items[ETOD_ITEM] === undefined) {
            throw "The " + ETOD_ITEM + " Item is not defined!";
          }
          if(items[ETOD_GROUP] === undefined) {
            throw "The " + ETOD_GROUP + " Group is not defined!";
          }
          var etodItems = ir.getItem(ETOD_GROUP).getMembers();
          
          if(etodItems.size() == 0) {
            throw ETOD_GROUP + " has no members!";
          }

          // Check the metadata for all the relevant Items
          for each (var item in etodItems) {
            var verify = verifyMetadata(item.name);
            if(verify !== null) {
              throw verify + "\n" + EXPECTED;
            }
          }

          // Get the time Items for today
          var times = getTodayItems(etodItems);
          if(times === null){
            throw "No set of date times were found for today! Do you have a default set of date times?";
          }
          
          // Update the Items to today
          //moveTimes(times);

          // The times will often be updated all at once, schedule a timer to wait for all the Items to update before creating the Timers.
          var ZonedDateTime = Java.type("java.time.ZonedDateTime"); 
          this.timers.check("ephem_tod_rule", 
                            ZonedDateTime.now().plusSeconds(60), 
                            createTimersGenerator(times, this.timers), 
                            true, 
                            function() { logger.info("Flapping timer, waiting before creating timers for time of day"); });
        }
    type: script.ScriptAction

Searching my log for all the TimeofDay updates:
grep “Item ‘TimeOfDay’ received command” events.log

2023-12-02 16:50:31.361 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'TimeOfDay' received command TWILIGHT
2023-12-02 20:00:00.043 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'TimeOfDay' received command EVENING
2023-12-02 23:00:00.018 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'TimeOfDay' received command NIGHT
2023-12-03 00:00:28.201 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'TimeOfDay' received command TWILIGHT
2023-12-03 07:59:00.007 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'TimeOfDay' received command DAY
2023-12-03 09:00:00.009 [INFO ] [openhab.event.ItemCommandEvent      ] - Item 'TimeOfDay' received command MORNING

It looks like it triggers Twilight, Evening, and Night as expected.
It then triggered twilight for some reason at midnight…
and then went on to trigger day (sunrise) and morning (late start as weekend) as expected.

A quick few searches of the forum do not reveal similar problems with others.
Is there something I need to do to look into this further?

Thanks again!

Most likely whatever updates the Twilight date time Item for today’s day type happened after the time of day rule ran. Because of that, Twilight still had the previous date and therefore it was before all the other DateTimes for today when the rule ran.

Astro doesn’t update Items until 30 seconds after midnight but you’ve configured To Today to run exactly at midnight. So Time of Day runs once at midnight and Twilight is still yesterday, therefore is the earliest DateTime and the rule sends TWILIGHT to the TimeOfDay Item. Then 30 seconds later it runs when Astro updates it’s Items,

Change the To Today rule to be at 30 seconds after midnight so there isn’t that much time between them.

Timing issues like these are one reason why the 4.x version of the rule ignores the date and only looks at the time.

Perfect, thanks!
I hadn’t recognized the rule template had filled in the time at midnight (still getting used to using templates). I’ve adjusted it now.
Ben

Sorry, another question here about ZonedDateTime comparison.
I have a morning rule that turns on the outdoor lights when ‘Morning’ arrives (currently 6:55) AM, but I want it to only turn on if it is still before sunrise (defined as Day, set from astro)

My rule doesn’t seem to run past my if-statement, and I have tried adding .state to it as well, resulting in an error that state is not a member of java.time.ZonedDateTime. I again have the feeling like I’m missing something basic. Is a DSL Rule using a java time implementation of now, that I am trying to compare to a DateTime variable? Is there a better way of going about this? It seems that most of the examples I can find online are either comparing/parsing strings and comparing to OH3x, using non DSL rules, or using older (still) versions of openhab.

I am using Openhab 3.4 (plans to upgrade when I get a chunk of free time to fix the problems I will inevitably create).

My relevant items:

DateTime DefaultMorning "Default Morning"  <time>  (TimesOfDay) ["DTUpdate"] { tod_sm="MORNING" [type="default"] }
DateTime DefaultDay "Default Day"  <sunrise>  (TimesOfDay) { channel="astro:sun:local:rise#end", tod_sm="DAY" [type="default"] }
DateTime DefaultTwilight "Default Twilight"  <sunset>  (TimesOfDay) { channel="astro:sun:local:set#start", tod_sm="TWILIGHT" [type="default"] }
DateTime DefaultEvening "Default Evening"  <time>  (TimesOfDay) ["DTUpdate"] { tod_sm="EVENING"[type="default"] }
DateTime DefaultNight "Default Night"  <moon>  (TimesOfDay) ["DTUpdate"] { tod_sm="NIGHT"[type="default"] }

My relevant rule:

rule "Front entry lights on at morning"
when
  Item TimeOfDay changed to "MORNING"
then
// if it before sunrise, then turn on the porch lights, etc
logInfo("lights.rules", "Morning porch lights rule now starting")
  var CurrentDT = now
    logInfo("lights.rules", "Morning rule data: Now is " + CurrentDT.state + ", and DefaultDay is " + DefaultDay.state)
  
  if (CurrentDT.isBefore(DefaultDay)) {  
    logInfo("lights.rules", "Current time is before sunrise")
    FF_FrontPorch_Light.sendCommand(ON)  
	FF_Front_Porch_Accessory_Outlet.sendCommand(ON)
	twinklyTree.sendCommand(ON)
  }
  else {
    logInfo("lights.rules", "Current time is after sunrise")
  }
  
end

The error in openhab.log: (using the current x.state log line 206 )

openhabian@openhab:/var/log/openhab $ grep "2023-12-15 06:55:00" openhab.log
2023-12-15 06:55:00.002 [INFO ] [del.script.Rules.rules_tools.Time_SM] - Transitioning Time of Day from NIGHT to MORNING
2023-12-15 06:55:00.014 [INFO ] [enhab.core.model.script.lights.rules] - Morning porch lights rule now starting
2023-12-15 06:55:00.020 [ERROR] [internal.handler.ScriptActionHandler] - Script execution of rule with UID 'lights-13' failed: 'state' is not a member of 'java.time.ZonedDateTime'; line 206, column 58, length 15 in lights
2023-12-15 06:55:00.177 [INFO ] [enhab.core.model.script.lights.rules] - Weekday morning, boys bedroom lights and downstairs centgral lights turned on
2023-12-15 06:55:00.880 [INFO ] [enhab.core.model.script.lights.rules] - 1

Of note, the line 206 referenced is the logInfo line attempting to log the date and time. This rule wasn’t running past the if statement long prior to me entering all the logInfo lines to try and chase it down.

I saw javascript examples for now.toLocalDateTime(), but can’t see any DSL examples of this - would this work? can I set it as
CurrentDT = now.toLocalDateTIme() rather than CurrentDT = now?
Is there a better implementation for my circumstance? Is there something else I am missing?
It has been a slow debugging process, as this rule only runs once per day (and I only get free time every other day or so)

Thanks for your directions/corrections/suggestions!

Ben

Openhab 3.4.2, on Openhabian RPi4

.state before what?

CurrentDT is indeed a ZonedDateTime and it has no .state.

DefaultDay is a DateTimeItem and that is not a ZonedDateTime and cannot be used in the call to isBefore(). DefaultDay does have a .state but that’s a DateTimeType, not a ZonedDateTime so you still cannot use that in the call to isBefore(). You need to get the ZonedDateTime from that.

(DefaultDay.state as DateTimeType).zonedDateTime

See DateTime Conversion (openHAB 3.x) (
note that article remains unchanged for OH 4).

All of this works very differently in JS. There is a utility called time.toZDT() which handles converting just about anything that makes sense into a ZonedDateTime.

But no, using now.toLocalDateTime() isn’t going to change anything because a LocalDateTime doesn’t have a .state either.

Yes, see above and the link above.

@rlkoshak
is there somewhere documented, what are mandatory prerequisists? I don’t mean the creation of items and groups, more the installation of other automation templates and so on. Because I’m starting from a completly blank installation, without any running rule and no JS installation or else.

If you are in fact running OH 3.x then there are no requisites for this rule template.

If you are running OH 4.x+, you are on the wrong posting. See Time Based State Machine [4.0.0.0;4.9.9.9] which has a section that discusses the prequisits. At a high level, the JS Scripting add-on, a certain version of the helper library (which comes with the add-on so you don’t have to worry about it unless you’ve installed the openhab-js manually), and a certain version or above for openhab-rules-tools which can be installed through openhabian-config or by running npm manually.

Ok, I really did not recognize the version in the title. Thank you, now it works for me