How can I use persistence history in a rule?

I am trying to look up the state of 2 Contact items from a rule.

Here’s the definition for Contact item:
Contact personA “”
Contact personB “”

Here’s the snippet of rule code:

var Contact a
var Contact b

a = personA.maximumSince(now.minusMinutes(20)).state
b = personB.maximumSince(now.minusMinutes(20)).state

if ((a == ON) || (b == ON))
home_occupied.sendCommand(ON)
else
home_occupied.sendCommand(OFF)

But all I get is an exception when OH tries to run the rule:
java.lang.RuntimeException: The name ’ == ’ cannot be resolved to an item or type.
at org.openhab.model.script.interpreter.ScriptInterpreter.internalFeatureCallDispatch(ScriptInterpreter.java:67) ~[na:na]
at org.eclipse.xtext.xbase.interpreter.impl.XbaseInterpreter._evaluateAbstractFeatureCall(XbaseInterpreter.java:658) ~[na:na]
at sun.reflect.GeneratedMethodAccessor97.invoke(Unknown Source) ~[na:na]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0]
at java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0]

How can I make the rule work?

Aha. So I tried to see the last value of some other item:

logInfo(“rules”, "prev state is " + front_lights.previousState(now))

And got an exception. Something is not set up correctly with persistence:

java.lang.IllegalStateException: Could not invoke method: org.openhab.core.persistence.extensions.PersistenceExtensions.previousState(org.openhab.core.items.Item,boolean) on instance: null

Can you guys give me a hint of what it may be? All of my items are set up with the same persistence rule: group(rrd4j:everyMinute, rrd4j:restoreOnStartup)

I am not sure this is the root cause of your problem, but I know from other posts that RRD4J is only capable of persisting numbers.

I honestly don’t know (and have not tested) if that means only items of “NumberType” or if it means any item that can consistently be represented as a number (e.g. Contact: OPEN=0, CLOSED=1).

Given that you are trying to persist Contact items using RRD4J it might be worth looking into - either by testing if your RRD4J setup is working on an item of NumberType or by switching to some other persistence service (e.g. DB4O).

Kjetil - I know from the fact that I can see historical values in HABmin. That means that they are persisting. And you are right - they are 1 and 0 for OPEN/CLOSED.

But my problem now is that I can’t access those values programmatically from a rule :unamused:

I’m not sure how what is here works. Xbase is pretty forgiving I guess. The first error and the persistence error you saw might be a bit misleading. I bet your persistence is fine but I see several problems with your code that makes me think it is having difficulty casting what you are pulling from the database to what you are telling the rules engine to store it in.

First of all, maximumSince() returns an object of type HistoricItem and HistoricItem.state returns an object of type State so assigning it to an object of type Contact should generate an error.

Second of all, in any case I think what you want is org.openhab.core.libary.items.ContactItem, not Contact for the type of “a” and “b”. But that is a little bit of a red herring since maximumSince isn’t return that type anyway.

Third of all, this is a nit pick but a Contact’s states are OPEN and CLOSE, not ON and OFF. What is here might work though.

I think to correct the code as is you would need:

var State a
var State b

a = personA.maximumSince(now.minusMinutes(20)).state
b = personB.maximumSince(now.minusMinutes(20)).state

if((a==OPEN) || (b==OPEN))
    home_occupied.sendCommand(ON)
else
    home_occupied.sendCommand(OFF)

However, I find I run into this sort of error (and greatly reduce my imports) far less frequently if I roll up a lot of this code and let the Rules engine figure the types of things out for me. At a minimum you could do:

val a = personA.maximumSince(now.minusMinutes(20)).state // since a and b are never reassigned, use val instead of var
val b = personB.maximumSince(now.minusMinutes(20)).state // Xbase is really good about figuring out the types of things most of the time so you don't have to explicitly state it

if((a == OPEN) || (b == OPEN))
    home_occupied.sendCommand(ON)
else
    home_occupited.sendCommand(OFF)

But I personally would roll it up even further and avoid the assignments entirely.

if((personA.maximumSince(now.minusMinutes(20)).state == OPEN) ||
   (personB.maximumSince(now.minusMinutes(20)).state == OPEN))
    home_occupied.sendCommand(ON)
else
    home_occupied.sendCommand(OFF)

Finally, if I ever thought I would have more than two people controlling the presence I would drop the person Items into a group and use a filter:

Items:

Group gPresence
Contact personA (gPresence)
Contact personB (gPresence)

Rule code:

if(gPresence.members.filter(person | person.maximumSince(now.minusMinutes(20) == OPEN).size > 0)
    home_occupied.sendCommand(ON)
else
    home_occupied.sendCommand(OFF)

This way to add another person you just need to add them in the Items file and make them a member of gPresence. You don’t have to touch your rules at all. I use this design pattern all over the place and as a result my rules have become much much shorter (more than a 50% reduction in lines of code) and much much easier to read.

Finally, are you using Designer? I think it would have caught some of the original errors, though maybe not. I can’t figure out which package Contact is in so I can’t tell if it would have caught the assignment error (i.e. assigning a State object to a Contact variable).

Good luck.

Rich, amazing tips! Thank you so much.

First, I tried the 1st version of the rule that you suggested - the same exception.
Then, I tried changing the persistence store from rrd4j to influxdb - didn’t help
Then, I tried the 2nd version of the script without any assignments - and the exception was gone!

But.

The “maximumSince” returns the last value without “20 minutes” range :frowning:
I am printing into the log the timestamps of the maximum value, and influxdb has a value every minute

Here is the code

logInfo("rules", "Person maximum: " + Person.maximumSince(now.minusMinutes(60), "influxdb").timestamp)
logInfo("rules", "Person minimum: " + Person.minimumSince(now.minusMinutes(60), "influxdb").timestamp)

Here is the output

2015-10-06 16:51:00.045 [INFO ] [org.openhab.model.script.rules] - Person maximum: Tue Oct 06 16:51:00 CDT 2015
2015-10-06 16:51:00.094 [INFO ] [org.openhab.model.script.rules] - Person minimum: Tue Oct 06 16:51:00 CDT 2015
2015-10-06 16:51:00.121 [INFO ] [org.openhab.model.script.rules] - HOME is not OCCUPIED
2015-10-06 16:52:00.048 [INFO ] [org.openhab.model.script.rules] - Person maximum: Tue Oct 06 16:52:00 CDT 2015
2015-10-06 16:52:00.095 [INFO ] [org.openhab.model.script.rules] - Person minimum: Tue Oct 06 16:52:00 CDT 2015
2015-10-06 16:52:00.121 [INFO ] [org.openhab.model.script.rules] - HOME is not OCCUPIED

As you can see, it takes just the last value. Any ideas?

I read mobiles macs from router registration table to detect who is at home:

if (gMobiles.members.filter(s | s.state == ON).size == 0) {
            if (gMobiles.members.filter(s | s.changedSince(now.minusMinutes(30) )).size == 0 ) {
..
            }
}

This works for me.

1 Like