Making Decisions Based on Time of Day

Hello,

The rules documentation seems lacking in this area - in fact, it seems lacking in general about what capabilities are available to me via the rules language.

That said, I want to be able to make decisions based on an event. Let’s say one of my items detects I am home - I want my rule to be able to say things like “if before 6PM and lightlevel > 200, do this, if after 6PM, do this…”

It seems unclear to me how to compare the current time against a static time. Does anyone have any good ideas?

An expansion of this would be to compare against a string value I have for the current sunset time. How would I convert that string to a datetime for comparisons?

I use the following -:

if((new LocalTime().getLocalMillis()) >= (new LocalTime(23, 0, 0, 0).getLocalMillis()))

The time in LocalTime is in hours, minutes, seconds, so it’s pretty easy to set…

Chris

2 Likes

OpenHAB’s rules engine is Turing Complete. If you can think of it, if it can be calculated, you can do it. That being said it means you need to learn a new programming language. The rules are a Domain Specific Langauge based on the Xtend language, more documentation for which can be found here. There are some limitations though, for example you cannot create your own classes. To further complicate/enhance what you can do, Xtend is built upon and has access to Java so anything built into Java (e.g the DateTime class) you can use as well. However, the best place I’ve gone to learn this sort of thing are the examples on the wiki and in this forum.

My general approach to what you want to do is to create a rule that triggers on the main trigger event (in your example your coming home switch) and then use if/else logic in your rule to implement the rest of the conditions. So I would probably implement your rule like this, using “now” which is based on the Joda library.

import org.joda.time.*

rule "Came home"
when
    Item Home changed to ON
then
    if(now.getHourOfDay < 18 && LightLevel.state as Number > 200) {
        // do stuff
    }
    else {
        // do other stuff
    }
end

Chris also has a good approach. As with many things in programming, there are dozens of ways to do this, each slightly different and each more or less suited to particular problem types.

To convert from a String to a DateTime, in Joda you can do the following:

DateTimeFormat formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss"); // or whatever your pattern is
DateTime date = formatter.parseDateTime(dateString)

EDIT: Added how to convert String to DateTime.

2 Likes

Absolutely - there’s always more than one way to do things :smile:

I selected the method I showed above as it can easily be used in any situation (ie if you want hours, minutes, seconds, then it’s pretty messy to use the individual hour/minute/seconds methods) so it really depends on the situation… As I was writing the block rule editor in HABmin, I wanted a one option fits all way of doing it…

1 Like

I’m getting the message “Feature getLocalMillis is not visible” when I try to use your if logic.

I do have these includes:
import org.openhab.core.library.types.*
import org.openhab.core.persistence.*
import org.openhab.model.script.actions.*
import org.joda.time.*
import org.openhab.core.library.items.*

Strange - this works fine for me - I just import org.joda.time.*

Hello,

I feel resurrecting this thread is more suitable than opening a new one as it’s in a similar direction. I am trying to figure out whether now is within a particular time window to turn on heating if its cold (so, e.g., if temperature is below 22 Celsius and it is between 5.30pm and 8.45pm). Is there an easy way to figure out whether now is within a given time or do I have to check whether now.getHour is greater than 5 and lower than 8 and then check whether if it’s 5 whether now.getMinute is bigger than 30, etc… this feels very inefficient (though I cannot tell what it means for computation load) and is there some other way which would figure this out?

A neat way would be if I could do something like time=5:30pm and check whether now is within time.plusMinutes(195) (which would be 5.30pm to 8.45pm).

Did I confuse now? :wink: Thanks in advance for suggestions.

First, see the Design Pattern: Time Of Day for some examples of just this as well as using DateTime Items to define the start and end times rather than hardcoded start and end times. Overall it lets you centralize the calculations of these times of day so that you can modify them in one place and/or use events like sunset to define some of your times without needing to change it all over the place in your rules.

if(now.isAfter(now.withTimeAtStartOfDay.plusHours(5).plusMinutes(30)) && 
   now.isBefore(now.withTimeAtStartOfDay.plusHours(20).plusMinutes(45))

You could do plusMintes(330) instead for 05:30 but I think the above is more legible.

1 Like

Hello @rlkoshak

thanks a lot for that hint. I was lost a bit in searching the forum and documentation but didn’t get this to work. I will try it out but sounds exactly as what I’m looking for.

Cheers!

I see two approaches:

  • Event-based driven by when the sensor is updated.
    If the sensor is read regularly, this may waste CPU performing time comparisons.
  • Time-based driven by a cron statement.
    Uses less CPU, but at the cost of being less flexible on start/stop and comparison times meaning you can’t check 17:30 - 20:45, but need to round to 17:30 - 20:30.

A combination is also possible, where a cron statement triggers a rule, say every 15 minutes, and then nested if statements check the time and temp.

My own rules use a combination of both - some use cases make sense to trigger by time (e.g. regularly check if a door has been left open, and how long), others by events (e.g. a motion sensor or an Astro binding item changing state on a variable sunset event).

I certainly feel your pain with date / time calculations.
Many OH1 examples use the joda.time libraries (http://www.joda.org/joda-time/) with explicit import statements, but in Java 8 and OH2, the functionality was incorporated (JSR-310) under slightly different java.time names.
After deciding to use the Java native versions in OH2, I have spent many happy hours reading Java class library documentation and wondering what the latest interpreter error message means!

So, at the very real risk of showing my ignorance of eXtend and the Java API, here’s two attempts at some pseudo code.

Event based

rule    "Event Rule"
when
    Item TempSensor changed
then
    // borrow comparison code from Rich
    // didn't realise it was possible to stack .plus - neat
    if (now.isAfter(now.withTimeAtStartOfDay.plusHours(17).plusMinutes(30)) && now.isBefore(now.withTimeAtStartOfDay.plusHours(20).plusMinutes(45)) {
        if ((TempSensor.state as DecimalType) < 22) {
           logInfo("Time Rule", "Cold!")
        }
    }
end

Cron time based

rule    "Time Rule"
when
    //          sec min hr  dom mon dow yr
     Time cron   "0  30 17-20  ?   *   *"
then
    // Rules fires at 17:30, 18:30, 19:30, 20:30 so efficient, but not to specification!
    if ((TempSensor.state as DecimalType) < 22) {
       logInfo("Time Rule", "Cold!")
    {
end

Note that although temperature control looks as simple as a comparison statement, ‘real’ thermostats accommodate the characteristics of the controlled device with extras such as hysteresis (prevents lots of switching in response to small temperature changes), modulation / proportional control (some loads are not just ON or OFF - they can be 30% on), and optimum start (model how long it takes to achieve a set temperature, and turn on the device early to achieve the target set point at the start of the time period).

I’d also recommend including a hardware fall back (such as a physical frost thermostat) just in case your software controller decides to lock up in the midst of winter!

1 Like

Joda is still available so all the old 1.x Joda code examples should work just fine. Only OH does the imports for you so you no longer need to import from org.joda to have access to the Joda classes. But that doesn’t make them unavailable.

That is actually illustrated in the Time of Day DP I linked to above. It has some times defined by cron triggers and others based on events, Astro events in this case. One could easily add additional events and criteria if needed.

Timers and/or Design Pattern: Expire Binding Based Timers is a better approach to this over a cron triggered polling rule.

A couple of examples:

One reason why I came up with the Time of Day Design Pattern is because it lets me centralize all of those Time cron triggers and if(now.isBefore tests and make them into events to drive the rest of the automation.

Thanks for the links - I can see lots of reading, and proto-typing ahead of me as a few of the patterns (especially occupancy with a Fibaro PIR sensor only sending a usable ON event) do indeed look better than my current production rules.

@rlkoshak

Could you please tell us what is the best example for such a rule now?

Thanks

It depends on your context and your requirements. There is no best example in all circumstances and for all purposes.

@rlkoshak Thanks,

What I am looking for:

I want to be able to make decisions based on an event. Let’s say one of my items detects “I am not at home” - I want my rule to be able to say like “if time is between 6am and 10pm do this” (daytime)
Or another " if time is between 10pm and 6am (next Day) do this" ( overnight) ?

Do you have for both situation an example?

Time of Day to define the “between 6am and 10pm” time periods.

Then in your rule

if(vTimeOfDay.state == "DAY" && vPresent.state != ON) // send your alert for daytime
else if(vTimeOfDay.state == "NIGHT" && vPresent.state != ON) // send your alert for night time

See the Design Pattern: Separation of Behaviors for how to centralize your alerting logic so you only have to put the above if else in one place to, for example, use a different alerting based on the time of day. Then in your rule you just vAlert.sendCommand("My alert message!").

@rlkoshak sorry for my misunderstanding.

I am looking for an example in a rule, were I can define “Start and End” Time like this:
" if a item state is changed then if time is between 6am and 10pm do this…"

rule "Rulename, ON" 
when
                Item xy changed from OFF to ON
then
                val LocalTime morning = new LocalTime(6, 0)  // 6:00 
                val LocalTime evening = new LocalTime(22, 0)  // 22:00 
                if (now.toLocalTime().isAfter(morning) && now.toLocalTime().isBefore(evening)) {
                               sendNotification .........
                }
else
                if (item2.state !=OFF) {
                               sendNotification ........              
                }
end

Or

“If a item state is changed then if time is between 9pm and next day 6am do this…”

Do you have an example?

I hope you know now what I looking for?

You asked for the best solution. The best solution, IMHO, is to do what I suggested.

Put the calculation for 9pm to 6am into a separate rule using the Time of Day Design Pattern. In your rule you use:

rule "Rulename, ON" 
when
                Item xy changed from OFF to ON
then

                if (vTimeOfDay.state == "DAY") {
                               sendNotification .........
                }
                else if (item2.state !=OFF) {
                               sendNotification ........              
                }
end

You will have to actually click on the Time of Day Design Pattern link provided above. Since I’ve already written a thorough article on how to do it I’m not going to reproduce it here.