Rule logic failing

Platform: openhabian snapshot, openhab 2.4.0

I’ve been staring at this and reviewing as much as I can find in the forums and the docs.

It may be I can’t see the forest for the trees or I’m just lost… :crazy_face: (It wouldn’t the first time!)

I have the following rule:

val night_beg = new DateTime(Night_Start.state.toString)
val night_end = new DateTime(Night_End.state.toString)
val day_beg = new DateTime(Day_Start.state.toString)
val day_end = new DateTime(Day_End.state.toString)

rule "Driveway Motion"
when
	Item mqtt_test changed from "NULL" to "Motion" or
	Item mqtt_test changed from "No Motion" to "Motion"	
then
	
	//Handle amber light if after 10PM
	if(	now.isAfter(day_end)  &&
        now.isBefore(day_beg.plusDays(1))  &&
		//zwave_device_16500637f6a_node11_switch_binary.state == OFF) {
		Plug_11.state == OFF) {
			logInfo("Driveway Motion Rule", "Send Amber lamp on")
			//zwave_device_16500637f6a_node11_switch_binary.sendCommand(ON)
			Plug_11.sendCommand(ON)
			createTimer(now.plusMinutes(30), [ | Plug_11.sendCommand(OFF) ])
			}
	//handle driveway lights
	if(	now.isAfter(day_end)  &&
        now.isBefore(day_beg.plusDays(1))  && 
		Wall_14_Swt.state == OFF) {
			logInfo("Driveway Motion Rule", "Send Driveway lights on")
			Wall_14_Swt.sendCommand(ON)
			createTimer(now.plusMinutes(10), [ | Wall_14_Swt.sendCommand(OFF) ])
			}
	val tc = Motion_Count.state as Number + 1
	Motion_Count.postUpdate(tc)
	val String mailTo = "<valid email address>"
	val String message = "New motion detected. New Total: " + tc
	sendMail(mailTo, "Motion Detected", message)

end

When the mqtt_test changes the rule fires as expected. (I believe this is true because the email at the bottom is received and the motion count is updated.)

But, Wall_14_Swt and Plug_11 never come on.

I don’t show it above, but I’ve added a few log statements and the day_end, day_beg, and .plusDays all seem to have the values I expect. And the devices are OFF.

What am I missing?

Are you seeing the other log entries (the ones before the sendComands)?

No.

I’m assuming it is something to do with the booleans in the if clause, but, not sure what.

I added other loginfo to confirm that Wall_14_Swt.state == OFF returns true and it does.

Does the rule work the first day after saving the rule file? You’re using some global variables which are not going to change the values they get after the file is saved, even if the items change. If you don’t touch the file until tomorrow, the logs you have for day_end, day_beg, etc. will have the same values as they did today. Put them inside the rule.

I think you rule will run as it is, and this is kind of a personal preference thing, but the formatting is a bit rough on the edges. Try this on…

rule "Driveway Motion"
when
    Item mqtt_test changed from "NULL" to "Motion" or
    Item mqtt_test changed from "No Motion" to "Motion"    
then
    val night_beg = new DateTime(Night_Start.state.toString)
    val night_end = new DateTime(Night_End.state.toString)
    val day_beg = new DateTime(Day_Start.state.toString)
    val day_end = new DateTime(Day_End.state.toString)

    if (now.isAfter(day_end) && now.isBefore(day_beg.plusDays(1)) && Plug_11.state == OFF) {
        logInfo("Driveway Motion Rule", "Send Amber lamp on")
        //zwave_device_16500637f6a_node11_switch_binary.sendCommand(ON)
        Plug_11.sendCommand(ON)
        createTimer(now.plusMinutes(30), [ | Plug_11.sendCommand(OFF) ])
    }
    //handle driveway lights
    if (now.isAfter(day_end) && now.isBefore(day_beg.plusDays(1)) && Wall_14_Swt.state == OFF) {
        logInfo("Driveway Motion Rule", "Send Driveway lights on")
        Wall_14_Swt.sendCommand(ON)
        createTimer(now.plusMinutes(10), [ | Wall_14_Swt.sendCommand(OFF) ])
    }
    val tc = Motion_Count.state as Number + 1
    Motion_Count.postUpdate(tc)
    val String mailTo = "<valid email address>"
    val String message = "New motion detected. New Total: " + tc
    sendMail(mailTo, "Motion Detected", message)
end

It may well only work the first day after saving the rule file. To be honest, I only remember it working once and that was before I clobbered all my things, including the controller and had to re-do the lot.

I thought I’d researched or asked about the update of the globals. (I’ve been experimenting a lot in the past month…) Thought I’d determined they updated each day when the astro thing is triggered and updates the item…but, I was thinking that I was wrong. And, thinking about it doesn’t make sense that vars outside a rule would be dynamic.

Let me ask about a couple things. The “global” scope of these variables is only global to this file, correct? Thus items from all .items files are in scope in all .rules files. But, vars are not available outside the file they are in.

Obviously, if I move them into the rule their scope is only within the rule, correct? And the execution of the rule sets them to the current values of the item linked to the astro thing.

Oh, I agree my formatting is “loose” :blush: , but, I tend to be able to visualize the multiple boolean clauses better on separate lines. And I think I can simplify things as well, for example:

when
    Item mqtt_test changed from "NULL" to "Motion" or
    Item mqtt_test changed from "No Motion" to "Motion"    
then

could be:

when
    Item mqtt_test changed to "Motion" or
then

I’ll change the vars and test, thanks!

For Rules DSL scripts, correct. JSR223 allows for globals to be shared across scripts and modules.

For Rules DSL scripts, correct. If you want to leave them as globals for use in other rules, you could create another rule that triggers on Astro events and updates them, but make them vars and not vals.

So, thinking this through, what I’d really need is at midnight to update these to the new days values of the new astro daylight start/end.

Can I do:

var DateTime night_beg
rule "Update Day Beg End"
when
	Time is midnight
then
	day_beg = Day_Start.state.toString
end

All of this is correct. “global” variables and values are only global to the .rules files they are defined in. Vals and vars defined in a Rule are only available in that one Rule. Vals and vars defined in a lambda only exist in that lambda. And vals and vars only exist from the point they are defined and below for the context they are created.

You might consider employing Design Pattern: Time Of Day to centralize all your time of day calculations which will simplify the rest of your Rules that care about what time of day it is. So the if(now.isAfter(day_end) ... can be replaced with if(vTimeOfDay.state == "NIGHT")

I will definitely do something like that once I’m more comfortable with the basics (that said… I might do it sooner than later…)

Reading through that Design Pattern I think I get most of what it’s doing.

After doing some searching though, I can’t find docs about the methods of the now function. Particularly:

now.withTimeAtStartOfDay

Knowing what that does might clarify how:

now.withTimeAtStartOfDay.plusDays(1).minusHours(18)

Fixes dealing with DST…

withTimeAtStartOfDay takes the date time to midnight today, the start of the day.

The day where DST changes there is an hour lost or gained between midnight and morning so if you use now.withTimeAtStartOfDay.plusHours(6) it will produce:

  • 6 am today on most days
  • 5 am today during the changeover in the spring
  • 7 am today during the changeover in the fall

So if we instead jump to midnight tomorrow and subtract hours using now.withTimeAtStartOfDay.plusDays(1).minusHours(18) we will always get 6 am today no matter whether there is an extra hour or one hour less between midnight and 6 am today.

http://joda-time.sourceforge.net/apidocs/org/joda/time/DateTime.html

Reading the design pattern I’m imagining the astro things are defined something like:

astro:sun:home [ geolocation="<x>, <y>", interval=60 ] {
}
astro:sun:minus90 [ geolocation="<x>, <y>", interval=60 ] {
    Channels:
        Type rangeEvent : set#event [
            offset=-90
        ]
}

It was my understanding that the “Type rangeEvent : set#event [offset=-90]” only effected the set channel of sun:home. But, the design pattern discussion of things says the -90 effects all of sun:minus90 channels. (The example above is how I have a astro:sun defined currently. Am I defining -90 on only the set channel or all the astro:sun:myastrothing channels?

I’m no expert in Astro but what I’m referring to above is if you need to have an event for both sunset and sunset-90 then you need two Things because each Thing only has one set#event.

But, if you use the latitude offset to get the -90 minutes offset instead of the Thing Channel offset, then the -90 applies to ALL of that Thing’s events since you’ve effectively moved the location east or west by enough degrees to equate to -90 minute offset.

Ok, got it.

Thanks!