Jython Group Filters

So I’m migrating the last few DSL rules to Jython and I’m trying to figure out the equivalent to the following that is used as part of presence detection:

if(gPresence.members.filter(s | s.state == ON).size == 0) {
    if(gPresence.members.filter(s | s.changedSince(now.minusMinutes(5))).size == 0) {
        IsAnyoneHome.sendCommand(OFF)
    }
}

The first if statement can be represented as:

list_of_members = filter(lambda item: item.state == ON, ir.getItem("gPresence").members)

but I’m having trouble getting the second filter statement to work; specifically the changedSince aspect.

What is the correct format for this filter?

filter(lambda item: item.changedSince(Dateformat.now.minusMinutes(5)) == 0, ir.getItem("gPresence").members)

throws errors about the changedSince.

TIA

Nick

The Persistence method calls on Items do not exist in Jython. You need to use the Persistence Extensions: https://openhab-scripters.github.io/openhab-helper-libraries/Guides/Actions.html#id1

Another way to do the filter is to use List Comprehension.

list_of_members = [item for item in ir.getItem("gPresence").members if item.state == ON]
if size(list_of_members) == 0:

Though if you define your Group as

Group:Switch:OR(ON, OFF) gPresence

than you don’t need the filter at all.

if items["gPresence"] == OFF:

gPresence will only be OFF if all it’s members are OFF. It will be ON if one or more members are ON.

I think it would be good to take a step back and look at your whole rule and what you are trying to do with it. What is triggering this to be run? I’m guessing without seeing the rest, but it it seems like this could be better implemented by triggering when a member of gPresence changes to OFF, or if gPresence was setup with an aggregation function, trigger the rule when it changes. The latter implements…

I get that you are trying to migrate though and I’ve been through that grind. It just might be better to tweak some things that could be better implemented rather than recreating the bad habits of the DSL. :slightly_smiling_face:

The point of the rule is to check that the current IsAnyoneHome == ON state is correct and adjust if necessary. It will check devices (first filter) and to prevent flapping by seeing if the device state changed more than 5 minutes in the past (second filter). It runs via cron every 2 minutes OR on a change to gPresence. The full rule is :

//Check to see if anyone home
rule "Periodically check presence"
when
    Time cron "0 0/2 * * * ? *" //every 2 minutes
    or Item gPresence changed
then
	if (IsAnyoneHome.state == ON) {
		//Is ON check it should be
		if(gPresence.members.filter(s | s.state == ON).size == 0) {
			//logInfo("PresenceCheck", "Nobody detected at home, checking for flapping")
			if(gPresence.members.filter(s | s.changedSince(now.minusMinutes(5))).size == 0) {
				//logInfo("PresenceCheck", "Not flapping, nothing has changed for 5 minutes so nobody is at home")
				IsAnyoneHome.sendCommand(OFF)
			}
		}
	}
	else {
		//Is OFF check it should be
		if(gPresence.members.filter(s | s.state == ON).size > 0) {
			//logInfo("PresenceCheck", "State isn't ON but should be")
			IsAnyoneHome.sendCommand(ON)
		}
		else if (IsAnyoneHome.state == UNDEF || IsAnyoneHome.state == NULL) {
			//For initialisation. If IsAnyoneHome is undefined set it to OFF
			logInfo("PresenceCheck", "Initial state set to nobody is at home")
			IsAnyoneHome.sendCommand(OFF)
		}
	}
end

As Rich and Scott suggested, it makes sense to replace the first filter with a group switch. That is a lot simpler.

I could replace the second filter with a timer, but that is forward looking - i.e. I’ll then have to wait a further 5 minutes before I check to see if the device(s) are still OFF. By using the original changedSince I was able to say that the device is currently OFF and has been off for more than 5 minutes.

I’ll look at swapping out the first filter with a group switch and then experiment with the Persistence Extensions to use the changedSince.

If that becomes “inelegant” I’ll look at increasing the time between runs and add a delay timer for the flapping.

I’m not sure how a Timer doesn’t give you exactly the same information. When the Switch goes to OFF a five minute Timer is created. If it goes ON before the five minutes cancel the timer. Five minutes later the Timer runs if it’s not been cancelled, check to see if the Switch is still OFF, if it is you know the Switch has been OFF for five minutes. If the timer exists, you know that the Switch is OFF and has been off for less than five minutes.

Not only does this provide the exact same information, it’s more timely because with an every two minutes polling period it can be up to seven minutes in the worst case scenario after the Switch goes OFF before you know it’s been OFF for five minutes. With the Timer you know within a couple of hundred milliseconds at most.

You can see a Python version of this which has been submitted to the community libraries which you should be able to just download and use at https://github.com/openhab-scripters/openhab-helper-libraries/pull/234/files. It has the added feature where you can set a separate timeout for each device individually.

Thanks Rich,

I’ll look at the persistence.py file and see if I can implement that. TBH, my reluctance with timers was due to the fact that I expected to end up with either multiple overlapping timers or a single timer getting restarted. Either way I knew that would end up being a minefield to debug :frowning:

I’ve been putting off re-vamping my groups (too much organic growth), but I guess I’ve no excuse now!

N

This has all been put together for you in Area Triggers and Actions. When your gPresence group turns OFF, you can have a 5 minute timer start that turns the IsAnyoneHome Item OFF. All you have to do is setup the groups and add some metadata.