Design Pattern: Time Of Day

jodatime
designpattern
time
Tags: #<Tag:0x00007f014743f630> #<Tag:0x00007f014743f478> #<Tag:0x00007f014743f338>

(Udo Hartmann) #102

One cron trigger per rule:
This issue should be corrected at least in openHAB snapshot. Try it out (when this issue emerged, for me I could guarantee that cron triggers only worked as the first trigger of a rule. Even

rule "test"
when
    Item TestItem received command or
    Time cron "1 0 0 * * ?"
then
    logInfo("trigger","Time is {}",now().toString)
end

would not trigger at 12:00:01 am.

Thread::sleep() is intended to give openHAB some time. This is due to the fact, that openHAB does all asyncronous. So, when an item gets updated, the rule with this trigger will be executed immediately. While the rule is executed, openHAB will also persist the item state (if there is persistence set up). So, if you want to use persistence in the rule, you have to ensure, that the item is persisted. Though a Thread::sleep(100) does not guarantee that persistence took place, there is a good chance, that it worked, while there is a good chance, that without this Thread::sleep(100) the persistence didn’t have enough time to do it’s job.

However, your rule should always set the val day to the new day of week. But keep in mind, that this val does only exist within this rule, as soon as the rule exits, the val will vanish. And as a val is not to be changed after it was defined, you can’t define it outside a rule, you would have to use var instead.


(Rich Koshak) #103

That has long since been fixed.

In this case the Thread::sleep was only necessary because I needed the 00:00 trigger to run a little after midnight but the rest of the Cron triggers needed to happen at exactly the time indicated and since I couldn’t have more than one Cron trigger I temporarily dealt with that using the sleep.

Now I just schedule the midnight rule to run a little after midnight and no longer have the sleep.

The whole purpose of the sleep is that Astro doesn’t finish calculating the times for the new sunset and sunrise until a little after midnight. I generalized this to take into account any other event that others might use to trigger the Time of Day event. You may or may not require that in your setup.

The Rule depends on a whole lot of other activities taking place in other threads potentially. You cannot guarantee that all of those other activities, in this case the Astro binding recalculating the new times for the new day, will finish before this Rule runs unless you trigger the rule based on some other event, perhaps when the Sunset Time changed (though that might cause the rule to trigger at unexpected times.


(Jack Black) #104

Sorry if this is a repeat question but coding isn’t my strong suit. Sometimes as I’m teaching myself all this stuff my brain gets a little fried and I look right past answers that were right in front of me.

I have everything up and running as far as the vTimeOfDay string item being updated at the correct times. Coding wise what would be the most efficient way to use the state of vTimeOfDay to change what rules execute throughout the day?

For example. A current rule for when the front door opens

rule "Front Door"
when
    Item FrontDoorSensor received update OPEN
then
    FrontDoorMicroSwitch.sendCommand(ON)
    playSound("frontdoor.mp3")
end

Is there a way to determine what the then action will be based on the current state of the vTimeOfDay iten?

For example if

when
    Item FrontDoorSensor received update OPEN

if vTimeOfDay is MORNING, DAY, or AFTERNOON then have openhab

then
    playSound("FrontDoorDay.mp3")

But if vTimeOfDay is EVENING, NIGHT, or BED then

then
    FrontDoorMicroSwitch.sendCommand(ON)
    playSound("FrontDoorNight.mp3")

I know this is probably just really basic syntax stuff so again I apologize.


(Jerome Luckenbach) #105

This is pretty straight forward:

rule "Front Door"
when
    Item FrontDoorSensor received update OPEN
then

    if(vTimeOfDay == "MORNING" || vTimeOfDay == "DAY" || vTimeOfDay == "AFTERNOON" ){
       playSound("FrontDoorDay.mp3")
    }
    else if(vTimeOfDay == "EVENING" || vTimeOfDay == "NIGHT" || vTimeOfDay == "BED" ){
        FrontDoorMicroSwitch.sendCommand(ON)
        playSound("frontdoor.mp3")
    }

    // Probably you need an else condition here, if there are additional time of day states 
    // or you should at least keep in mind to handle them too
end

Another possible way could be to add a front door virtual item which switches depending on the time of day item when it changes.


(Jack Black) #106

Awesome, that is what I was looking for. In regards to your comment towards the end about possibly needing an else condition. If there were a 3rd option for an additional time of day would I just add another “else if” like you did here? Or is there a command that is simply “else”. And if so, what is the difference?

Sorry again. With several years of Linux, Arduino, and Pi experience under my belt I’m typically much better at tracking down answers like this on my own without having to bother people. With this XTend language being comparatively more obscure it seems like a lot of the documentation written on it is aimed towards people who are coders by trade and are picking up on XTend as an additional language. I’m sure I’ll get better at it, it’s just easy to get lost in all that stuff when you’re sophomoric like me.


(Jerome Luckenbach) #107

You could add another else if when you have additional conditions that should match a “specific” time of day.

If you simply want to handle “all other” time of day conditions, an else will do the trick.


(Chip Rosenthal) #108

With OpenHAB 2.2.0, the statement:

  val long day_start = (vSunrise_Time.state as DateTimeType).calendar.timeInMillis

now gives a warning:

The method getCalendar() from the type DateTimeType is deprecated

Use this instead:

  val long day_start = (vSunrise_Time.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli

(MikeH) #109

In a post above for setting up “Time of Day” rule with OpenHAB 2.2 there is the following message.

 // TODO figure out how to do this with offsets instead of a new Thing
     DateTime vEvening_Time "Evening [%1$tH:%1$tM]"
         { channel="astro:sun:minus90:set#start" }

My question is do you still have to create a new thing for this? If so could someone give me a quick walk through on how to create it? Thanks!!!


(Rich Koshak) #110

There is a limitation in Astro. You can create a Thing with an offset, but that offset will apply to all of the events for that thing. If you want an event to occur 90 minutes before sunset and another to occur at actual sunset, you must create two Things. As far as I know you cannot get both events from the same Thing.

See the openHAB 2 Astro 2.0 Example section for examples of two Things, one with an offset of 90 minutes. This example does the offset by modifying the longitude. The Astro binding added after the writing of this DP, the ability to define the offset directly. See the Astro binding docs for details.


(MikeH) #111

If I changed one of the channels within the Astro binding would that effect all the channels? I changed the offset on “Astro Dusk” Start time channel to make it come on 90 minutes prior to sunset. I then changed my twilight DateTime item to that channel.


(Rich Koshak) #112

I don’t know. I use lat/long rather than the offset so I don’t know how it works. I think it only affects that one channel.


(MikeH) #113

Need help with this openhab 2.2 version of the TIme of Day rule. Last night the currPeriod changed to UNKNOWN when it should have been Bedtime. Any help would be appreciated.

rule "Calculate time of day state" 
when
  System started or
  Channel 'astro:sun:local:rise#event'       triggered START or
  Channel 'astro:sun:local:set#event'        triggered START or
  Channel 'astro:sun:local:astroDusk#event' triggered START or
  Time cron "0 1 0 * * ? *" or
  Time cron "0 0 5 * * ? *" or
  Time cron "0 0 22 * * ? *"
then

  logInfo("Sun", "Calculating time of day...")

  val long morning   = now.withTimeAtStartOfDay.plusHours(5).millis
  val long sunrise   = (Sunrise_Time.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli
  val long twilight  = (Twilight_Time.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli
  val long evening   = (Sunset_Time.state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli
  val long night     = now.withTimeAtStartOfDay.plusHours(22).millis

 // Update when changing the cron triggers above change
 //morning.postUpdate(now.withTimeAtStartOfDay.plusHours(5).toString)
 //night.postUpdate(now.withTimeAtStartOfDay.plusHours(22).toString)  

  var currPeriod = "UNKNOWN"

  switch now {
    case now.isAfter(morning)   && now.isBefore(sunrise):   currPeriod = "Morning"
    case now.isAfter(sunrise)   && now.isBefore(twilight):  currPeriod = "Day"
    case now.isAfter(twilight)  && now.isBefore(evening):   currPeriod = "Twilight"
    case now.isAfter(evening)   && now.isBefore(night):     currPeriod = "Night"
    case now.isAfter(night)     && now.isBefore(morning):   currPeriod = "Bedtime"
  }
    if(TimeOfDay.state.toString != currPeriod) {
  	    logInfo("Sun", "Updating Time of Day {}, Previous Time of Day {}", currPeriod, TimeOfDay.state.toString)
  	    PreviousTimeOfDay.sendCommand(TimeOfDay.state.toString)
	      TimeOfDay.sendCommand(currPeriod)
    }
        logInfo("Sun",  "Calculated time of day is " + currPeriod)
end

(Rich Koshak) #114

Make sure there are no gaps between the start and stop times.

You should see a log statement from Astro saying that it has recalculated the times for the new day BEFORE this rule runs. If the new times are calculated after this Rule runs the calculations will fail.

Add lots and lots of logging to log out all the times when the Rule runs.

There is nothing wrong with the code. The problems lies in what all the times are set to (i.e. there are gaps between time periods) or the rule is running before Astro calculates the times for the new day.


(Udo Hartmann) #115
case now.isAfter(night)     && now.isBefore(morning):   currPeriod = "Bedtime"

I think this will never get true.
You have to use two lines instead:

case now.isAfter(night)     :   currPeriod = "Bedtime"
case now.isBefore(morning):   currPeriod = "Bedtime"

or is it possible to use an or ?

case now.isAfter(night)     || now.isBefore(morning):   currPeriod = "Bedtime"

but the simple way would be

case default:   currPeriod = "Bedtime"

which will be used if none other case is true :wink:


(Rich Koshak) #116

It is possible to use an or.


(MikeH) #117

Thanks Rich and Udo the “or” method worked. OpenHab went through all the periods of time correctly yesterday,


(Guido Van Haasteren) #118

Dear Rich, first I want to say THANK YOU for so many threads with your ideas and knowledge. Unfortunately there is a large gap between where my knowledge ends and what I need to get what you are talking about (specially lambda’s I will put on my study list over a few months). I do get roughly what is going on, but not 100%. I hope I do not offend you with my questions! I highly respect what you do, I just don’t get most of it.

First off: the vals and vars are not on-top. I cut-and-paste them to the top of my rules-file, since I understood this is where they should be. But I get errors like: method not known’ etc. When I put them back, these errors are gone.

I get confused also because there are some updates and changes down the thread, and I am affraid not all of them are updated in the top how-to, or am I wrong? I ended up with items with the ‘v’ in front and rules with items without the ‘v’. Ok, I corrected that. I though I had everything, Also channels linked from Astro-binding (something I didn’t have to do before with my z-wave stuff)
You mention also sitemap-content, maybe I oversee it but I cannot find it.
Can you link an Astro-channel to an item and give extra configuration settings in the item?
I am on OH2.2, so I concentrated on the lower parts. But still I get this error:

2018-01-26 20:58:08.912 [INFO ] [el.core.internal.ModelRepositoryImpl] - Validation issues found in configuration model 'default.rules', using it anyway: 
The use of wildcard imports is deprecated. 
The method getCalendar() from the type DateTimeType is deprecated   

First I did without the include above, this saves the wildcard-error, but without it I still get the 'getCalendar) error.
Could you please make a resume for me with OH2.2 in mind, starting from the top and maybe including the linking of the channels?
Thanks for your time!


(Rich Koshak) #119

So this DP was written a year and a half ago but if you look at the bottom of the first posting you will see a section labeled “openHAB 2.2 Updates”.

The Rules as written in the original post are functional as written. You should not copy and paste code around willy-nilly wihtout understanding what you are doing.

The vals and vars in this DP should not be moved to the top of the .rule file.

I can’t begin to explain the errors without listing the actual errors.

See the “openHAB 2.2 Updates” section. This section shows updates to the Rules posted in the openHAB 2.0 section.

You will see that all the Items in the 2.0 section above start with a “v” and the Rules in that section as well as the updated 2.2 section all use the “v”.

Are you using the zwave 1 binding? If you are using the zwave 2 binding and you do not have simple mode turned on you most certainly do have to link the channels to your Items.

That mention is in error. I did not post any sitemap and usually I treat the sitemap stuff as being beyond the scope of a Design Pattern posting.

See https://docs.openhab.org/configuration/sitemaps.html

No. See https://docs.openhab.org/addons/bindings/astro/readme.html

There are no imports anywhere in the 2.x sections of the posting. Your rule should match what is posted.

Again, the 2.2 Rule does not have a getCalendar call in it anywhere. Your Rule should match what is posted.


(Guido Van Haasteren) #120

Thanks for your quick answer. I understand these kind of question can be frustrating for you, but you have to understand that everything is a strugle when you’re not a programmer from professhion. I will start over and see what happens. I guess I messed up scroling up and down.

I use the zwave 2-binding. I make a thing automatically in PaperUI, after which I can make an item myself, using of coarse the zwave-name that is shown underneath the channels.
But here with Astro I found one has to make an item first, without any code linking to the channel. Then go back to the thing in PaperUI and choose the item from a list. It works the other way around I guess. But I will read up on Astro too, sorry to bother you with that.
UPDATE: I had a quick look at the Astro-doc you referenced, and I now see it can be like I used to do with z-wave. I will do it that way and leave the dropdown in the thing/channel-menu from PaperUI as-is.

Have a nice weekend!


(Rich Koshak) #121

It should work exactly the same way.

You can scan for Astro Things from the Inbox and accept the Thing(s) that get found and you have a new Astro Thing just like the Zwave Things.

https://docs.openhab.org/addons/bindings/astro/readme.html

If a system location is set, “Local Sun” and a “Local Moon” will be automatically discovered for this location.

You can also make the Astro Things manually in PaperUI. Just go to Things in PaperUI, click the + icon, choose Astro, and fill out the asked for information.

Once you have the Thing it should work identically the same as the Zwave Things and Items.

Above shows a third approach which has the Things being defined in .things files largely because it is far easier to write a tutorial that can just list the config in one line and because when this was written PaperUI did not have as good support for manually defining Things for Astro.

If I responded it wasn’t a bother. I ignore the bothersome postings. When I’m on my phone, I tend to keep my responses short.