[Deprecated] Design Pattern: Time Of Day

Correct. But my specific astro items are - please correct me if I am wrong - irrelevant for the tod. Only the tod specific items are used.

The Items that appear in the Rule are the only ones that matter to the Time of Day.

Matthias, if I understand you right, the error occurs only after a “System started” ! Is that correct ? And when the other triggers are fired (Channel ‘astro:sun:Lohhof:civilDawn#event’, …) everything’s fine. Isn’t it ?

From my point of view it seems to be a still pending OH-Problem (or better, Linux Problem).

When OH is started/restarted it can happen that the rules are triggered even if some item-files are not loaded/initialized. In this case the Error occurs.

You can check this point easily with your “Test-Switch” (test_button) . If you switch it ON/OFF, your rule will trigger again and all items should be filled correctly and then triggered again via CRON-Triggers.

Maybe there’s a solution with createTimer and/or checking states for NULL. So maybe someone can help or give a hint in that case.

I use the "original from Rich, but I didn’t observe a problem yet. (I fairly have to say that I use it only for “information”. I have no depending rules).

Cheers,
Peter

Items begin life at system boot with a NULL state.
Item states can be reset to NULL during edits of xxx.items files, if you use them.

Astro binding will populate (assign a meaningful value to) Items via its state channels usually only once a day, just after midnight. Populating say, sunrise and sunset for the day ahead.
It should normally do that also when the binding initializes at boot time, but should hopefully complete that task before rules run. But it’s not totally guaranteed.

So you can see if an Item state gets reset to NULL it may not get updated until next midnight.

When you have doubts about an Item state inside a rule execution, log it out.

logInfo("test", "My Item state - " + someItem.state.toString)

Do that before it causes an error, probably at the top of the rule. Even if it is NULL state the log will work.

Thank you, looks like I’m getting closer :wink:

Now I have two DateTime variables, lastFeeding and nextFeeding, and the first rule to set the first one when I press a button:

rule "Set feeding time"
when
  Channel 'deconz:switch:openhabian:SW_Xia2:buttonevent' triggered 1002
then
  val lastFeeding = new DateTime(DT_Feeding.state.toString)
  logInfo(filename,lastFeeding.toString)
end

…with DT_Feeding being the last_updated channel of the button.
The loginfo ist just there for the moment and returns:

2019-11-21 15:16:48.872 [INFO ] [marthome.model.script.Schildis.rules] - 2019-11-21T15:16:47.000+01:00

Then, I have a second rule to remind me if more than two days have passed (right now it checks if one minute has passed):

rule "Food reminder"
when
    Time cron "40 18 15 1/1 * ? *"
then {
  val nextFeeding = lastFeeding.plusMinutes(1)
  if (now.isAfter(nextFeeding)) {
    sendBroadcastNotification("Feeding")
    }
  }
end

…which returns an error:

2019-11-21 15:18:40.013 [ERROR] [ntime.internal.engine.ExecuteRuleJob] - Error during the execution of rule 'Food reminder': cannot invoke method public org.joda.time.DateTime org.joda.time.DateTime.plusMinutes(int) on null

To me it looks like I have the DateTime var lastFeeding which is not null but plusMinutes seems to think otherwise. Where am I going wrong now?

Val creates a new variable within a rule.
When the rule ends, the variable disappears.

I’ve just had this conversation, see the first part of

1 Like

Use a global var instead (or test against the item)

// always define global vars at top of the file
var DateTime lastFeeding = null

rule "Set feeding time"
when
    Channel 'deconz:switch:openhabian:SW_Xia2:buttonevent' triggered 1002
then
    lastFeeding = new DateTime(DT_Feeding.state.toString)
    logInfo(filename,lastFeeding.toString)
end

rule "Food reminder"
when
    Time cron "40 18 15 1/1 * ? *" // at 15:18:40, every day
    // a more common quartz cron expression would be
    // Time cron "40 18 15 * * ?" // at 15:18:40, every day (year is optional)
then
    //  lastFeeding = new DateTime(DT_Feeding.state.toString) // update var when rule is triggered

    val nextFeeding = lastFeeding.plusMinutes(1)
    if (now.isAfter(nextFeeding)) {
        sendBroadcastNotification("Feeding")
    }
end

Dear all, thank you all very much for the support. Acually maybe something else went wrong. I could not re-create the problem. I rebooted the raspberry and the tod shows the right status even after reboot (giving it some time). Besides that you confirmed how to work and debug with openhab. Thank you all a lot!

1 Like

Quick question, can you have more than one then in a case statement?

In this case I use the day / evening / night but I want to set a second variable based on the same when criteria so rather than have to do it in a separate case statement I thought I’d be neat and do it in there same one if possible???

More than one line in a case statement? Yes of course. Just be sure to use curly brackets.

case foo: {
    line of code
    another line of code
}

Sorry, I’ll rephrase, for each case you set curr = “something” can you set it so curr = “something” and curr2 = “something else” in the same case statement.

Yes. Like I said

case foo: {
    curr = "something"
    curr2 = "something else"
}

It’s just two lines of code.

Sorry, I misread the post on my phone screen.

case conditionA : {
  // the code in the curly brackets
  // is what gets executed
  // not the condition
 }
case conditionB || conditionC : {
  // the condition is similar to that in an if()
  // here the || qualifies as OR
}
case conditionX && conditionY : {
   // here the && acts as AND
}
case "EVENING" || "NIGHT" : {
   // DON'T FORGET THE COLON
}
2 Likes

Hi,

Just got round to implementing this…but I’m msiing something here… any help please?

switch now {
  	  case now.isAfter(morning_start)   && now.isBefore(day_start):       curr = "DAWN"       curr2 = "1"
  	  case now.isAfter(day_start)       && now.isBefore(afternoon_start): curr = "DAY"        curr2 = "2"
  	  case now.isAfter(afternoon_start) && now.isBefore(evening_start):   curr = "TWILIGHT"   curr2 = "3"
  	  case now.isAfter(evening_start)   && now.isBefore(night_start):     curr = "EVENING"    curr2 = "4"
          case now.isAfter(night_start):                                      curr = "NIGHT"      curr2 = "5"
  	  case now.isAfter(bed_start)       && now.isBefore(morning_start):   curr = "BED"        curr2 = "6"
      }

What I’m trying to do is set a var curr and a second var curr2 but the error I get is

Configuration model 'timeofday.rules' has errors, therefore ignoring it: [48,100]: no viable alternative at input '='
[48,102]: no viable alternative at input '"1"'
[49,6]: mismatched input 'case' expecting '}'
[49,72]: mismatched input ':' expecting 'end'

So I guess it doesn’t like 2 thens per case statement or do I need to have a & or similar between them / put them on separate lines?

…try it this way:

switch counter {
           case 0 :  {
                      daymin = day
                      daymax = day0
            }
           case 1 :  {
                      daymin = day0
                      daymax = day1
            }
           case 2 :  {
                      daymin = day1
                      daymax = day2
            }
           case 3 :  {
                      daymin = day2
                      daymax = day3
            }
           case 4 :  {
                      daymin = day3
                      daymax = day4
            }
           case 5 :  {
                      daymin = day4
                      daymax = day5
            }
    }

… use curly braces after the colon :wink:

1 Like

Thanks, feel a bit of a wally now, I’m blaming the excess of Christmas and some time off that my brain hasn’t kicked into gear yet…

Thanks for the help

One has to ask why you need two different representations of the same thing? Why can’t you just use curr or just use curr2?

It’s a question I’ve been asking myself too.

I’m doing a few rules where I want something to happen in more than one curr state, so in my ToD rule rather than say when ToD curr = Day or Evening or Night or Bed, I just wanted to say when ToD curr2 > 4.

Lazy, probably, but it just seemed easier to me.

OK, so why not be fully lazy and just use the number? There is nothing magical about using a String to represent the time of day. If the number works better for you, standardize on the number.