[Deprecated] Design Pattern: Time Of Day

The major point here would be the indentation, you are using (if I count correctly) 3 indentation styles combined in this piece of code, and something funny was going on with the spacing you were using. I would write it as below. And you were missing a closing accolade, which becomes obvious a lot quicker if you use consistent indentation

    if(morning_start.isBefore(day_start)) {
        switch now {
            case now.isAfter(morning_start)   && now.isBefore(day_start):       curr = "DAWN"
            case now.isAfter(day_start)       && now.isBefore(afternoon_start): curr = "DAY"
            case now.isAfter(afternoon_start) && now.isBefore(evening_start):   curr = "TWILIGHT"
            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"
        }
    } else {
        switch now {
            case now.isAfter(day_start)       && now.isBefore(morning_start):   curr = "DAWN"
            case now.isAfter(morning_start)   && now.isBefore(afternoon_start): curr = "DAY"
            case now.isAfter(afternoon_start) && now.isBefore(evening_start):   curr = "TWILIGHT"
            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(day_start):       curr = "BED"
        }
    }
1 Like

Thanks, the final closing accolade is on my code, must have missed it off the copy and paste but I certainly take your point with the indentation. I’m always worried if the syntax gets picky when accolades aren’t on the some line, next line, or even if extra blank lines affect it.

I’m an old school html / asp / vb script coder and love nothing more than retentively indenting codes but when I’m first learning new code I tend to copy and paste from other code, (ie this example), but not change it for fear of something going wrong…

Spaces/tabs do not have any meaning in the Rule DSL (or Xtend for that matter), so feel free to use indentation to your advantage, also from examples from others, that, possibly because of their coding background, use different indentation styles.

1 Like

Great, I’m off to indent the cr@p out of my existing code!

:+1:

I’m just doing a quick scan from my code but I think you can do this with one switch statement. The only difference is the test for “DAWN”. It feels wrong to duplicate the whole switch statement to handle that one difference.

I don’t think you need the test for isBefore. I think you just need to add the second version of the case and it will work.

    switch now {
  	  case now.isAfter(morning_start)   && now.isBefore(day_start),
             now.isAfter(day_start)       && now.isBefore(morning_start):   curr = "DAWN"
  	  case now.isAfter(day_start)       && now.isBefore(afternoon_start): curr = "DAY"
  	  case now.isAfter(afternoon_start) && now.isBefore(evening_start):   curr = "TWILIGHT"
  	  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"
  }

If morning is before day, then the first case will evaluate to true when the current time is between morning and day. If morning is after day and now it’s between the two, the first case statement can’t evaluate to true but the second case statement will evaluate to true. If either are true then it gets set to “DAWN”.

Because the switch statement stops as soon as it finds a true case you don’t need to have two versions of the third case statement. It doesn’t matter whether morning is before or after day. If the first two case statements are false, we can use day as the isAfter and the third case will run when it needs to.

@rlkoshak I have found my root of issue, described by lots of others here.
Basically cron is really unrealiable, so that’s why my Time Of Day won’t get updated every time (when it should get triggered by a cron).

Here is my original issue: Cron job failing to execute on OH 2.4

Is there a solution to drop cron triggers? Or how this ‘problem’ could be solved?

Usually when cron triggers get dropped it is because you have too many trying to run at the same time. You can only have two crons or Timers running at the same time. If you exceed that it looks like OH drops the task rather than waiting. do you have a lot of cron triggered rules or timers?

Not really, I have everything based on the Time Of Day, so basically this is the one cron trigger (and another thing which polls a device every 5 min). And it is not like it sometimes “drop” the task. It seems like after a time all of the cron jobs are missed (this might be a better word…). I had enabled debug logging for quartz and after some days I just stop receiving these quartz logs…

One thing was interesting: I have mapdb. It looked like that when a status changes which is persisted by mapdb, a new cron job starts…

It’s not just cron that operates out of that thread pool. Timers come out of that thread pool too.

All I can recommend is trying to upgrade to 2.5 M1 or the snapshot (riskier right now) and seeing if the error goes away. If it doesn’t file an issue at openhab-core.

There is anything changed in the Milestone release regarding these problems?

I don’t know but if you file an issue the developers will need you to run the latest version to verify it isn’t some thing that has already been fixed.

I’ve tackled it with separate astro-bindings. Not using the offset, but I use the earliest/lastest config (https://www.openhab.org/addons/bindings/astro/#channel-config), eg:

astro:sun:day [ geolocation="xxx,xxxx", interval=60 ] {
        Channels:
            Type rangeEvent : rise#event [
                earliest="07:00"
            ]
    }

In this example DAWN will start at 6am (as coded) till at least 7am. DAY will start at least at 7am or whenever sunrise is later than 7am.

1 Like

Hi,

I’m going to trim this DP to give me the name of the day and the week number as separate variables so that I can then run rules depending on the day, (ie I don’t want to be woken up at my normal working day time on the weekend for example), so the question is 2 fold:

  1. Where do I find the datetime formatting options to pull out just the day of a datetime item and then also the week number

  2. What option would you use to fill the variable:

just a simple one line: vNameofDay = whatever the correct channel and formatting is to give me “MONDAY / TUESDAY / etc”
or
would you go down the case route as above to hard set the name of the day? Would it make more sense to perhaps use the case method so I can call Mon “1”, Tues “2”, Wed “3” etc and then it would save me having to change type in other rules, (or perhaps give me the options to have a rule where vNameofDay >= 6 means the weekend etc)…

  1. would you tab it into my existing ToD rule so that it all runs and fills the variables in one rule or make it a separate rule - I’m a little worried at the number of cron jobs I’m going to start having and having the same / similarly related variables updated based on one rule seems the best thing to do in my mind but anyone have some alternative thoughts not to…

Any thoughts would be gratefully received!

The easiest is to just type in now. in VSCode and wait the half second for the dialog to come up that shows you all the methods on that DateTime object.

Otherwise you can look at DateTime (Joda time 2.2 API).

I think you can only get the day of the week as a number anyway. If keeping it as a number helps you in other Rules keep it as a Number and use a map transform if you need the text.

But one must ask what the benefit is of putting just the day of the week into an Item. I would expect to have a vDayOfWeek with states like “WORK”, “WEEKEND”, “HOLIDAY”, etc. If all you use is the day of the week, just use now.getDayOfWeek in your other Rules. The ToD DP is intended to centralize calculations that take places over and over in many Rules. In this case you are not doing any calculations.

I can see either approach as appropriate.

Thanks as always for the response. I do keep forgetting about VSCodes OH add in, I must get into the habit of trying that first.

But one must ask what the benefit is of putting just the day of the week into an Item. I would expect to have a vDayOfWeek with states like “WORK”, “WEEKEND”, “HOLIDAY”, etc. If all you use is the day of the week, just use now.getDayOfWeek in your other Rules. The ToD DP is intended to centralize calculations that take places over and over in many Rules. In this case you are not doing any calculations.

I want to have the day of the week as a number dummy variable for the exact reason that it centralizes it for other rules to use. I wanted to do it as simple as 1-7 as my work days might not be the same every week so setting Mon-Fri as WORK and Sat-Sun as WEEKEND isn’t always correct.

I can then combine the vToD and this vDayofWeek into other rules, ie if time of day = BED and vDayofWeek = 6, (ie Saturday night), then set the alarm for later than during the rest of the week so I get a lie in on Sunday morning.

But it’s already centralized. It’s one call to now.getDayOfWeek. You are proposing replacing that one call consisting of 16 characters with an Item definition and a rule which is far more work and complex then just using now.

Hi, yes you’ve beaten me to it :slightly_smiling_face:

I didn’t know about the getdayofweek option until after I followed your joda link and played around with VSCode, I found it last night, along with a load of other interesting datetime things I never knew was possible.

So much easier that having to write rules for them! Thanks again for the link etc, everyday is a school day!

For completion / interest…

  // get day and week numbers
  val week_number = now.getDayOfMonth
  vWeekNumber.postUpdate(week_number.toString)

  val day_of_week = now.getDayOfWeek
  vDayofWeek.postUpdate(day_of_week.toString)

Gives the week number and day of week as numbers…

nope.

a given date: 2019/05/14

now.getYear = 2019
now.getMonthOfYear = 5
now.getDayOfMonth = 14
now.getWeekyear = 20 (ISO8601 Week of Year, the first week of the year is that in which at least 4 days are in the year)

Sorry, you are correct, I must have posted an old bit of code, I changed the variable names to be more accurate after c&p here.

Given date 2019/05/14 (Tuesday)

now.getDayofMonth gives you the day of the month ie 14
now.getDayofWeek gives you a numerical version of the day of the week, ie 2, (1 is Monday, 7 is Sunday)
now.getWeekyear gives you the week number, ie 20.