[Deprecated] Design Pattern: Time Of Day

Does anybody still has problems with Astro scheduling? Basically it works okay, until I change any part of the OH config (not related to it’s rules/items/things). After that, most scheduling works, but changing state from EVENING is not. If I re-run manually the rule it will update to the current ToD. However if I leave it there as it is, it will only change state next day… (to DAY if I’m right).

Hi,

Funnily enough I’ve noticed this too the past couple of days, I go from EVENING to UNKNOWN and it won’t change until just after midnight when it goes to BED, ie it totally skips NIGHT…

I haven’t had tine to debug it yet as I’m doing other stuff…but maybe that’s the problem, but me doing other stuff is what is causing it…?

EDIT - I wonder if it’s because I rebooted??? Despite the system rest on reboot in the rules…hummm.

Like you, I also didn’t had time to debug this, but as you look back here, there were always issues like this, which is related to Astro (and no one could find why this happens). I had running openHab for a month without a restart, it worked fine until I changed something in the config (for almost a month). Now I restarted openHab, it worked again for days, I had to add new items/rules to my setup, the recalculation stopped again (after a few days…).

Which Channels do you mean ? Item-Channels like grouprise, set, …” or groupphase” ?
Or Trigger-Channels ?
At the moment I didn’t noticed a bug, but I will have a look on it today and give you my results.

I’m working with OH2.5 M1

Cheers,
Peter

Edit:
Sorry for my fault :upside_down_face:. I didn’t read the complete post, so I thought you are discussing about problems with Astro-Binding.
Now I installed the DP of Rich from the top of the post and will see what happens :wink:

I can’t seem to get this to work. I see the entry in my log files about calculating the time of day, but it never calculates anything. I think at least part of the problem might be:

String vTimeOfDay “Current Time of Day [MAP(weather.map):%s]”

If I use that as-is, I get errors about an entry in the MAP not found. I have the ‘weather_en.map’ file from the map-files.zip download; I renamed ‘weather_en.map’ to ‘weather.map’ and put it in my ‘/etc/openhab2/transform’.

I changed the line, instead, to read:

String vTimeOfDay “Current Time of Day [%s]”

but now while the MAP error is gone, the ‘vTImeOfDay’ string never gets populated. To confirm this, I added the following to my sitemap:

Frame
{
Text item=vTimeOfDay
}

and the value never gets changed; is just remains as a “dash”. So I guess my question is: what value do I have to put in “weather.map” to make the original string work, or what do I have to change to make it work the way I have it?

Thanks! :slight_smile:

Don’t go down that path. The problem isn’t the map.

See How to Debug a Rule

I now have installed the DP of Rich and tested in several versions. As @rlkoshak said, the problem is not the map.
I found out, when I made changes(i.e the Label) in the Channel-linked items the values are lost. When waiting for the interval-time (i.e. interval=300) of the defined Astro-Thing they will be updated again. But only the “normal-ones”. The Scheduling will not work for those Things and channel-linked Items with Offset.

If the Rule starts (with Debug-Points) and if a value is not set, the Logger shows

2019-04-16 10:32:05.744 [INFO ] [e.smarthome.model.script.Time Of Day] - Calculating time of day...
2019-04-16 10:32:05.769 [INFO ] [e.smarthome.model.script.Time Of Day] - Calculating time of day...1
2019-04-16 10:32:05.788 [INFO ] [e.smarthome.model.script.Time Of Day] - Calculating time of day...2
2019-04-16 10:32:05.806 [INFO ] [e.smarthome.model.script.Time Of Day] - Calculating time of day...3
2019-04-16 10:32:05.814 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule 'Calculate time of day state': Invalid format: "NULL"

when it comes to NULL-Value.

After a Restart of OH2 or Bundle-Restart all values are set. But one can see in the Logger what happens.

2019-04-16 11:01:38.536 [INFO ] [thome.binding.astro.internal.job.Job] - Scheduled Astro event-jobs for thing astro:sun:stowing1
2019-04-16 11:01:38.640 [INFO ] [thome.binding.astro.internal.job.Job] - Scheduled Astro event-jobs for thing astro:moon:local
2019-04-16 11:01:38.654 [INFO ] [ding.astro.handler.AstroThingHandler] - Scheduled Positional job astro:moon:local every 300 seconds
2019-04-16 11:01:38.799 [INFO ] [thome.binding.astro.internal.job.Job] - Scheduled Astro event-jobs for thing astro:moon:local
2019-04-16 11:01:38.820 [INFO ] [ding.astro.handler.AstroThingHandler] - Scheduled Positional job astro:moon:local every 300 seconds
2019-04-16 11:01:38.866 [INFO ] [thome.binding.astro.internal.job.Job] - Scheduled Astro event-jobs for thing astro:sun:local
2019-04-16 11:01:38.891 [INFO ] [ding.astro.handler.AstroThingHandler] - Scheduled Positional job astro:sun:local every 300 seconds
2019-04-16 11:01:38.979 [INFO ] [thome.binding.astro.internal.job.Job] - Scheduled Astro event-jobs for thing astro:sun:local
2019-04-16 11:01:39.037 [INFO ] [ding.astro.handler.AstroThingHandler] - Scheduled Positional job astro:sun:local every 300 seconds
2019-04-16 11:01:39.059 [INFO ] [thome.binding.astro.internal.job.Job] - Scheduled Astro event-jobs for thing astro:sun:minus90
2019-04-16 11:01:39.246 [INFO ] [thome.binding.astro.internal.job.Job] - Scheduled Astro event-jobs for thing astro:sun:stowing
2019-04-16 11:01:39.500 [INFO ] [thome.binding.astro.internal.job.Job] - Scheduled Astro event-jobs for thing astro:sun:stowing2

The Re-Scheduling will only work for for the “Normal-Tings”

I’m using the following Set-Up:

RPi 3B, OH2.5M1

rich_time.items:

Group gRichTime "Rich's Daytime"
//String    vTimeOfDay       "Current Time of Day [MAP(weather.map):%s]" <time>  (gRichTime)
String    vTimeOfDay       "Current Time of Day [%s]"             <time>          (gRichTime)  // Testversion without transformation
DateTime  vMorning_Time    "Morning [%1$tH:%1$tM]"                <sunrise>       (gRichTime)
DateTime  vSunrise_Time    "Day Test1 [%1$tH:%1$tM]"              <sun>           (gRichTime)     {channel="astro:sun:local:rise#start"}
DateTime  vSunset_Time     "Evening Test1 [%1$tH:%1$tM]"          <sunset>        (gRichTime)     {channel="astro:sun:local:set#start"}
DateTime  vNight_Time      "Night [%1$tH:%1$tM]"                  <moon>          (gRichTime)    
DateTime  vBed_Time        "Bed [%1$tH:%1$tM]"                    <bedroom_blue>  (gRichTime)    
DateTime  vEvening_Time    "Late Afternoon Test1 [ %1$tH:%1$tM]"  <sunset>        (gRichTime)     {channel="astro:sun:minus90:set#start"}

rich_time.rules:

val logName = "Time Of Day"

rule "Calculate time of day state" 
when
  Item Dummy4 changed to ON or
  System started or // run at system start in case the time changed when OH was offline
  Channel 'astro:sun:local:rise#event'    triggered START or
  Channel 'astro:sun:local: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) 
  logInfo(logName, "Calculating time of day...1")

  val night_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(1)
  vNight_Time.postUpdate(night_start.toString)
  logInfo(logName, "Calculating time of day...2")

  val bed_start = now.withTimeAtStartOfDay
  vBed_Time.postUpdate(bed_start.toString)
  logInfo(logName, "Calculating time of day...3")

  // Convert the Astro Items to Joda DateTime
  val day_start = new DateTime(vSunrise_Time.state.toString) 
  logInfo(logName, "Calculating time of day...4")
  val evening_start = new DateTime(vSunset_Time.state.toString)
  logInfo(logName, "Calculating time of day...5")
  val afternoon_start = new DateTime(vEvening_Time.state.toString)
  logInfo(logName, "Calculating time of day...6")

  // 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

astro.things:

//    Astro - Binding Geo-Position  geolocation="xx.xxxxxx,y.yyyyyy,zzz"

Thing astro:sun:local     "Sonnen Daten"    [geolocation="xx.xxxxxx,y.yyyyyy,zzz", interval=300]
Thing astro:moon:local    "Mond Daten"      [geolocation="xx.xxxxxx,y.yyyyyy,zzz", interval=300]
                                        

Thing astro:sun:minus90   "Offset -90"     [geolocation="xx.xxxxxx,y.yyyyyy,zzz", interval=300]{
  Channels:
    Type rangeEvent : set#event [
      offset=-90
    ]
    Type start : set#start [
      offset=-90
    ]
    Type end : set#end [
      offset=-90
    ]
  }

Thing astro:sun:stowing   "Offset -180"     [geolocation="xx.xxxxxx,y.yyyyyy,zzz", interval=300]{
  Channels:
    Type rangeEvent : set#event [
      offset=-180,
      earliest="19:40"
    ]
  }
Thing astro:sun:stowing1   "Offset 200"     [geolocation="xx.xxxxxx,y.yyyyyy,zzz", interval=300]{
  Channels:
    Type rangeEvent : noon#event [
      offset=200,
      earliest="13:00"
    ]
    Type rangeEvent : set#event [
      offset=90,
      earliest="20:10"
    ]
    Type start : rise#start [
      offset=90,
     earliest="09:00"
    ]
    Type end : rise#end [
      offset=90
    ]
    Type start : set#start [
//      offset=90,
      earliest="23:50"
    ]
    Type end : set#end [
      offset=90
//      latest="21:50"
    ]
  }
  Thing astro:sun:stowing2   "Offset -90"     [geolocation="xx.xxxxxx,y.yyyyyy,zzz", interval=300]{
  Channels:
    Type rangeEvent : set#event [
      offset=-90
    ]
    Type start : set#start [
      offset=-90
    ]
    Type end : set#end [
      offset=-90
    ]
  }


So one can see, even if the interval is set in the Thing (minus90) it will not work. For me this looks like a bug in the binding :wink:

Cheers,
Peter

Btw: Is there an icon for <tod>. Didn’t find one.

Thanks or your help. By adding in the additional “logInfo” entries, I was able to sort things out and get a bit further, until I was getting the “NULL” value error. Based on that additional “logInfo” information, I was able to determine that it was failing to set a value for the ‘vEvening_Time’ value, which is the one with the ‘minus90’ value in the channel setting. I believe this is what fibu-freak was referring to just above in his response to me.

I am currently using PaperUI to manage my Things and the text files for everything else, but I don’t think that make a difference.

I see I can set an offset in PaperUI for the Astro Sunset, but than changes the actual sunset time, rather than allowing me to still have the proper sunset time, but apply an offset for another item (such as to the vEvening_Time item)

Hi Allen,
if I understand you right, you’re using only one Thing (“local” or “home”). Do you ? If so, you have to create another one (“minus90” or whatever name you want to have) with the offset-time. You can do this with Paper UI too, I think. Or just creating a .things File for the minus90-Thing as shown in my example above. I think this should work too.
Try it. :wink: When you have done it, you should see a log-message like the below one:

The result should look like here:

But be aware, if you make changes in your .items-file, your channel linked items values will be NULL. So for the “normal” ones (thats the items which are channel-linked to “local” or “home”) will be updated after the interval-time, but not the item linked with the “minus90”-Channel. This one is only updated after a Start of OH2 or after the midnight-rescheduling. Another way in this case could be to restart the Binding on the Console.Use

bundle:list | grep -i astro

to find out the ID of your astro-binding

and then use (but with your ID)

bundle:restart 212

to restart the Binding/Bundle

EDIT:
For testing purposes you can set a value to a specific item in the console too. Use this command

smarthome:send vEvening_Time 18:32

Yep, that was it: I was able to add another Astro binding in PaperUI and for the Sunset Start event put an offset of -90. I got the ID of the binding (like I did for my other ones) and put that into my “vEvening_Time” channel id and my “Afternoon” value is now showing 90 minutes before the real sunset start.

Thanks! I feel d-u-m-b, but unfortunately it is not the only time I will probably feel that way :frowning:

Fortunately, I believe I know enough now to be able to set some rules based on the TOD (like turning on my lights, etc.), and build form there.

Thanks again!

Hi,

I’ve made some changes to the original rule. All seems to work as expected until my last case statement for ‘BED’. Instead, I recieve [INFO ] [e.smarthome.model.script.Time Of Day] - Calculated time of day is UNKNOWN

My rule is as follows:

val todLog = "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
  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 12 * * ? *" or
  Time cron "0 0 17 * * ? *" or
  Time cron "0 0 22 * * ? *" or
  Time cron "0 0 23 * * ? *"
then
  logInfo(todLog, "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 midday_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(12)
  vMidday_Time.postUpdate(midday_start.toString) 

  val earlyEvening_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(7)
  vEarlyEvening_Time.postUpdate(earlyEvening_start.toString) 

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

  val bed_start = now.withTimeAtStartOfDay.plusDays(1).minusHours(1)
  vBed_Time.postUpdate(bed_start.toString)

  // Convert the Astro Items to Joda DateTime
  val sunrise_start = new DateTime(vSunrise_Time.state.toString) 
  val lateEvening_start = new DateTime(vSunset_Time.state.toString)

  // Calculate the current time of day
  var curr = "UNKNOWN"
  switch now {
    case now.isAfter(sunrise_start)       && now.isBefore(midday_start):        curr = "MORNING"
  	case now.isAfter(midday_start)        && now.isBefore(earlyEvening_start):  curr = "AFTERNOON"
    case now.isAfter(earlyEvening_start)  && now.isBefore(lateEvening_start):   curr = "EARLY EVENING"
  	case now.isAfter(lateEvening_start)   && now.isBefore(night_start):         curr = "LATE EVENING"
  	case now.isAfter(night_start)         && now.isBefore(bed_start):           curr = "NIGHT"
  	case now.isAfter(bed_start)           && now.isBefore(sunrise_start):       curr = "BED"
  }

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

Any help would be most appreciated. As i say, all my other time of days work correctly :frowning:

Many thanks,

Jeevs

Consider when these values get calculated.

bed_start get’s calculated for today. sunrise_start get’s calculated for today. So

bed_start      = 2019-04-18 23:00
sunrise_start  = 2019-04-18 06:20

Your last case can never be true between 2019-04-18 23:00 and 2019-04-19 00:00 (or whenever Astro generates the sunrise start time for the new day).

The switch statement works in the OP because bed_time is 2019-04-19 00:00 so there isn’t that gap between when bedtime starts and Astro recalculates the sunrise time for the new day.

This is why it is particularly important to not just copy this DP but to really understand how this DP works in order to use it successfully.

To do this successfully, you need to have a separate case statement to cover the time between bed_start and midnight. Then you need another one to cover the time between midnight and sunrise.

1 Like
rule "TimeOfDayRule"
when
    System started or
    Time cron "0 0 * ? * * *"
then
    if (now.getHourOfDay() >= 00 && now.getHourOfDay() < 06){
		myTimeOfDayMode.postUpdate("Night")
	}
	if (now.getHourOfDay() >= 06 && now.getHourOfDay() < 09){
		myTimeOfDayMode.postUpdate("Morning")
	}
	if (now.getHourOfDay() >= 09 && now.getHourOfDay() < 16){
		myTimeOfDayMode.postUpdate("Daytime")
	}
	if (now.getHourOfDay() >= 16 && now.getHourOfDay() < 23){
		myTimeOfDayMode.postUpdate("Evening")
	}
	if (now.getHourOfDay() >= 23 && now.getHourOfDay() < 24){
		myTimeOfDayMode.postUpdate("Night")
	}
end
1 Like

Thank you both very much! It’s now clear how this works. So, thank you both for the clear responses.

I’ve made changes to my rule, so fingers crossed it should work as expected.

Many thanks :wink:

Hi all,

So the more I’m getting into OH the more I find I’m using this pattern to run other rules depending on the time of day, so first off thanks for creating it!

I have relatively the same time sections, with
BED being midnight to 6am
And
DAWN being 6am to sunrise

Now this works fine for some of the year, when sunrise is after 6am, and I’ve coded it this way so that my lights slowly go off at dawn, and any light on motion doesn’t go on after sunrise etc.

However, for me and I’m guessing many others, sunrise is before 6am for at least half of the year so DAWN as a time never occurs, it goes straight to DAY, missing out some of my hard coded rules where vTimeOfDay == DAWN.

How do others work around this and is there a way to code in an if sunrise > 6am then do something else sunrise < 6am

Or, should we not really be using hard coded times in this instance?

Thoughts?

Simply use ASTRO to get the locationsspecific times for sunrise, dawn etc. This way the modes are independent of season and location and always correct

Thanks, so that’s 1 for don’t hard code any times in there, use the astro binding…

How about when dawn and sunrise are much earlier in the morning that when people get up, for example, if I turned off my cameras as dawn or sunset right now they would be off at 4:35 or 5:35, we don’t get up until nearer 7 usually.

What @rlkoshak made is a DP to learn all of us how the handling with Time Of Day could be done. It’s not a solution. One can change it as one need.

If you want to handle your specific purposes you can use the “Astro-Thing(s)” as you want, even with Offsets and/or “earliest/latest”-restriction as shown in my example above

As you see you can make as much Astro-Things as you need for your own purposes and use it in your Rules as you want and the DP can help you to realise.

Cheers
Peter

Hi,

Yes, I wasn’t for a second saying the original rule wasn’t right, far from it, I’ve used it well for many things.

I was just after a few ideas on different ways to tweak it to avoid the sunrise / 6am issue I mentioned.

For me this is the desired behavior. But if you don’t want this to occur, set the dawn start to sunrise with an offset in Astro instead of hard coding it to 6am.

Of course, a long as you can have some event that yesterday the rule at the right time. Then you just have

if(dawn_start.isAfter(day_start)) 
    // Do something else