Best Practice LastUpdate rule

Hi!
What is best practice for generating Last Update value?

Currently I use:

rule “Last Update: Outdoor Temperature”
when Item Temperature_OU_Outdoor received update then postUpdate(Update_OU_Outdoor_Temperature, new DateTimeType()) end

One for every item. It is a lot of work, and I think there is a better way.
Someone want to share?

1 Like

Set up persistence.

To get at your last update value in a rule you just:

Temperature_OU_Outdoor.lastUpdate

However, if you want to put the last update on your sitemap you need to do it like you are doing. However, you can save a lot of work if you set up persistence, use some groups, and a foreach.

Items:

Group LastUpdateGroup
// Put all the Items you want to have its lastUpdate stored into the LastUpdateGroup
// Make your Update Items match the name of your Items with Update either at the end or at the beginning so we can get at the Update Item by name
Number Temperature_OU_Outdoor "label" <icon> (LastUpdateGroup) ...
DateTime Temperature_OU_Outdoor_Update "label" <icon>

Rule:

rule "Update timestamp"
when
    Item LastUpdateGroup received update
then
    LastUpdateGroup.members.forEach[i | 
        postUpdate(i.name+"_Update", new DateTimeType(i.lastUpdate))
    ]
end

NOTES:

  • I just typed in the above without the help of Designer. There are almost certainly typos and/or syntax errors
  • I’m not positive that you can do the `new DateTimeType(i.lastUpdate). You may have to experiment to find what works. For example, you can try just posting ‘i.lastUpdate’, or ‘i.lastUpdate.toString’.
  • You still need to create an Item for each DateTime you want to post an update to.
  • You only need to do the above for Items where you want to put the lastUpdate on your sitemap. If you just need access to them in your rules you can just use MyItem.lastUpdate
  • Again, make sure you have persistence properly set up for all the Items you want to save their lastUpdate time. Use an everyChange strategy and, if using rrd4j, also use an every minute strategy.
3 Likes

Okey, I’ve tried around a bit. But have not got it to work, yet…
When I tried your code. The Desinger gave me a lot of error:

rule “Update timestamp”
when
Item gUpdate received update
then
gUpdate.members.forEach[i | postUpdate(“Update_”+i.name, new DateTimeType(i.lastUpdate)) ]
end

Multiple markers at this line
- Missig error message for org.eclipse.xtext.xbase.validation.IssueCodes.invalid_generic_argument_types
- Incompatible types. Expected java.lang.String but was org.openhab.core.library.types.DateTimeType
- Incompatible types. Expected org.openhab.core.items.Item but was java.lang.String
- Incompatible types. Expected java.util.Calendar but was java.util.Date

My Log files shows this:
Error during the execution of rule 'Update timestamp': Could not invoke constructor: org.openhab.core.library.types.DateTimeType.DateTimeType(java.util.Calendar)

You got any idea what’s wrong? I’ve been searching around, but to me it seems like I have two problems:

  1. new DateTimeType(i.lastUpdate) expects a Calendar, but it is a Date. I’ve have not found a solution, yet…
  2. The Item name is not recognized. I have not done this before, so I’m pretty stuck!

Any suggestions?

Ok, I’ve narrowed it down to 2 errors:

  1. It does not recognize my String as an Item name. I’ve been searching around, and trying many different approaches, but no luck.
  2. The new DateTimeType expects java.util.Calendar, but the lastUpdate is java.util.Date

For me, the first one is the biggest problem. I can’t figure out whats I do wrong!

G

Assuming the rule DSL uses the org.openhab.model.script.actions.BusEvent class for the postUpdate implementation, there are only specific combinations of types that are supported. They are:

  • Item, Number
  • Item, State
  • Item, String
  • String, String

A String,DateTimeType combination doesn’t match any of those combinations. You may need to convert the DateTimeType to a String if you are using a String for the first argument.

I’ve run into this issue frequently with JSR223 Jython rules, but it may apply to the rule DSL as well. It seems to be consistent with the error messages you are seeing.

Thanks,
I got it to work. Here are my working rule, for further reference.

rule "Update timestamp"
when
    Item gUpdate received update
then
	gUpdate.members.forEach[i |
		var Calendar cal = Calendar::getInstance()
		cal.setTime(i.lastUpdate("rrd4j"))
		postUpdate("Update_" + i.name, new DateTimeType(cal).toString)
	]
end
2 Likes

Which persistence is best to be used for Last Update data?

Can anyone think if a loop like this could be used to identify any item that has NOT been updated in the last hour and notify me?

I have a bunch of sensors that should be reporting to openhab at least every 60 minutes and so I want to know if one or more have not been updated…

I’m using the expire binding for that, add a expire="60m" to your channel definition, then the item state gets UNDEF if there has not been an update, then trigger a rule on UNDEF to send a notification.

Thanks I will take a look at that approach…

Here you can see my roof temp sensor has stopped working a few days ago and so it’s this situation that I am hoping to get notified about.

image

That is exactly how am I using that.

I’ve installed the Expire binding and am adding it to some sensors now… However this still feels like I have to write a rule per item I want to check? Is there any way to check all items in a group and report any that have not communicated for an hour?

Yes, but as far as I know the items have to be all of the same itemtype.

rule "check sensor uptime state"
when
	Item gCheckSensorStates changed
then
	if (gCheckSensorStates.state==UNDEF) {
	val triggerItem = gCheckSensorStates.members.filter[ i|i.state==UNDEF ]
	logInfo("EXTRA","LATEUPDATE: Ein Sensor ist ausgefallen: "+triggerItem)
	}
end

@sihui Thanks am trying to set this up today…
Another thought is that your example rule will only fire when an item in the group is updated… So if something has gone wrong and none if the items in the group are being updated then the rule will never be run?

Maybe it needs to be a time based check every 10 minutes or something?

You are allowed to have Groups of mixed Item types. Don’t expect the group to take on a sensible .state of it’s own, but here you don’t care about that.

You can have more selective rule triggers

when
	Item gCheckSensorStates changed to UNDEF

nb. the expire binding won’t be ‘started’ timing at system boot time, so this method will miss sensors broken before you start.

Depending on technology, some bindings will set UNDEF by themselves if communications fails.

Thanks @rossko57 I’ve updated my setup to be this… At the moment I’m testing with a 5 minute expiry…

ITEM

String UPSInterface_Uptime "UPS Interface Uptime [%s]" <wallclock> (APCUPS, LogRRD4J, StringCheck5m) {snmp="<[192.168.1.220:xxxxxxxx:.1.3.6.1.2.1.1.3.0:60000]", expire="5m"}

RULE

rule "check StringCheck5m states"
when
	Item StringCheck5m changed to UNDEF
then
	if (StringCheck5m.state==UNDEF) {
	val triggerItem = StringCheck5m.members.filter[ i|i.state==UNDEF ]
	logInfo("EXTRA","LATEUPDATE: Check Communication: " + triggerItem)
	}
end

I can see the expire binding changing the state of UPSInterface_Uptime to UNDEF but as far as I can tell the rule is not firing…

2018-10-31 11:52:31.485 [vent.ItemStateChangedEvent] - UPSInterface_Uptime changed from 0:05:34.80 to 0:06:34.80
2018-10-31 11:57:32.034 [vent.ItemStateChangedEvent] - UPSInterface_Uptime changed from 0:06:34.80 to UNDEF

Maybe my definition of my group is not correct?

Group StringCheck5m

I see that @rlkoshak has something like this in one of his examples…

Group:Contact:OR(OPEN, CLOSED) gDoorSensors "The doors are [%s]"

His write up is a couple of years old so it might be out of date but I’ll keep reading to see if I can figure it out.
His rule triggers do seem to be different in that the trigger if “Member of” and not “Item Changed”…

rule "Rule Name"
when
    Member of gDoorSensors changed
then

Alright then… It looks like this version of the rule works better!

rule "check StringCheck5m states 2"
when
    Member of StringCheck5m changed to UNDEF
then
    val problemitem = triggeringItem
	logInfo("EXTRA","LATEUPDATE: Check Communication: " + problemitem)
end
1 Like

Yup, your original version looked at the Group’s .state which was indeterminate, as no aggregation function had been defined.