Generic rule to display last sensor update time

Hi,
I want to display on my sitemap the time of the last received update from my sensors. I used the rule that was discussed on the forum:

rule "Update timestamp"
when
    Item gLastUpdate received update
then
    gLastUpdate.allMembers.forEach[i |
        postUpdate(i.name + "_lastUpdate", new DateTimeType(i.lastUpdate.toString).toString)
    ]
end

but I’m feeling that this is not what I need. What I get is all my sensors have the same time of the last update, and this may be due to the fact that lastUpdate refers to the last update time of the persistance db (which I set to “everyChange, everyMinute, restoreOnStartup”). And what I need is to capture the time of the last received message from my sensors. I guess I could set my persistance to everyUpdate but than it would brake the requirement for the charts to have persistance set to everyMinute … If there was a way to know which items updated the group item I could make a rule to send current time to those items ?

As I thought about this I realized that I need single DateTime item for single sensor which can send multiple types of sensor data. So for example I have sensor which sends humidity, temperature and battery level, and I want a single item for that sensor to tell the last update time of any of those values. So I created for each sensor device a single group item, a DateTime item and a rule like this:
rule “Fridge update timestamp”
when
Item gFridgeSensor received update
then
fridge_sensor_lastUpdate.postUpdate(new DateTimeType())
end
I need as many rules defined as there is sensor devices, but at least it is working exactly as I desire :slight_smile:

In addition to Associated Items sihui linked to, use the new implicit variable triggerngItem and you can put it all in one Rule.

rule "last update"
when
    Item gFridgeSensor received update or
    Item gOtherSensor received update or
    ...
then
    val lastUpdateItem = gLastUpdates.members.findFirst[ lu | lu.name == triggeringItem.name + "_LastUpdate" ]
    lastUpdateItem.postUpdate(new DateTimeType())
end
2 Likes

Thanks, that might make things easier :slight_smile:

I have a quick question. When I try to use the above rule, thetriggeringItem.name resolves to
" (Type=ContactItem, State=CLOSED, Label=Door, Category=door, Tags=[Door], Groups=[GF_Entryway, gDoor])"

Can someone please help me understand how I can limit the triggeringItem.name to ONLY return the item name and NOT the additional attributes related to the name.
Thank you VERY much for your help and the great example it is going to make my rules a lot easier.

Can you show us why you think that? triggeringItem.name gives the expected result.

The text in quotations is a copy and paste from the log file after I simply dumped the value of the lastUpdateItem to the log. Note, I removed the “+ “_LastUpdate”” part from above to make sure I only capture the value of the thetriggeringItem.name. I hope this makes sense. Thank you

That makes sense, it’s a dump of the Item. It’s not triggeringItem.name
Perhaps you could logInfo("test", triggeringItem.name) if you have doubts about it.

I assume that means you did this

val lastUpdateItem = gLastUpdates.members.findFirst[ lu | lu.name == triggeringItem.name ]

which will search through the group gLastUpdates for an Item named triggeringItem.name … and return that Item to you in lastUpdateItem. It will not return the name, it returns the whole Item.
If it doesn’t find it - say the target is not a member of gLastUpdates - it returns null

You could if you wanted use lastUpdateItem.name (since it is an Item) but that’s a bit redundant where you already know the name you searched for.

I use the new OH 2.4 feature to solve this problem:

first:
For every item I want to monitor I define a corresponding _LastUpdate-Item.
I put all the items I monitor into a group “gMonitorLastUpdateNU”

Number WallSwitch02_Battery  			"Wandschalter 02 [%d %%]"  										    <battery> (gMonitorLastUpdateNU) 
DateTime WallSwitch02_LastUpdate		"Wandschalter 02 Letztes Update [%1$tY.%1$tm.%1$td %1$tH:%1$tM]" (gMonitorLastUpdate)

Then I use a rule to update the _LastUpdate-item:

rule "gMonitorLastUpdateNU setzen"
	when
		Member of gMonitorLastUpdateNU received update or
	then
		postUpdate(triggeringItem.name.toString.substring(0,triggeringItem.name.indexOf('_')) + "_LastUpdate", new DateTimeType().toString)
end

To check if a update was to long ago I use:

        val SimpleDateFormat sdf = new SimpleDateFormat("EEEE, d MMM yyyy HH:mm:ss")
        if(!gMonitorLastUpdate.members.filter[ (state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli <= (now.minusHours((nuLastUpdateInterval.state as Number).intValue).millis) ].empty)
        {
            val report = gMonitorLastUpdate.members.filter[ (state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli <= (now.minusHours((nuLastUpdateInterval.state as Number).intValue).millis) ].sortBy[ (state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli ].map[
                    name.toString.substring(0,name.indexOf('_')) + " - " + label + ": " + sdf.format(new Date((state as DateTimeType).getZonedDateTime.toInstant.toEpochMilli)) + "\n"
            ].join
            
            val message = "LastUpdateOfItem reaches Error-Level:\n" + report
            logInfo("`Logger", message)
        }
``
2 Likes

Thank you. As always, the problem is sitting in front of the computer and your explanation helped me figure it out.

There is an even easier way now (since 2.5) by using thing profiles:

String Pumprum_BatteryLevel
  "Pumprum [%s]"
  <battery> (gBattery)
  {channel="mqtt:topic:mybroker:pumprum:battery"}
DateTime Pumprum_BatteryLevel_LastUpdate
  "Pumprum [%1$tF %1$tR]"
  <battery> (gTimestamps)
  {channel="mqtt:topic:mybroker:pumprum:battery" [profile="timestamp-update"]}

No rules needed. The timestamp can be either on update or on change.

2 Likes

Thats a nice alternative. But I dont see how it would work with groups…
The advantage with the rule is, that you can group a bunch of sensors (ie door or motion sensor) and from a single very short rule having a datetime update of each.
Your suggestion is great, if this is only a single or few items.

With this approach you don’t need the Groups nor the Rules any more. You just need the two Items you already have to have anyway and the profile and that’s it.

@martin1, would you be willing to write this up as a Design Pattern? It’s a super elegant way to achieve this. This tells me I need to look into profiles more.

One caveat here is it will only work with OH 2.x bindings. If you are using OH 1.x bindings, the old “put them in a Group and update an associated Item using a Rule” is the only way.

1 Like

Good idea!

I created this design pattern: Design Pattern: Time of Last Update - feedback welcome!