Item Last Update


(Smhgit) #1

Hi,

I am trying to add generic way to update last item update time. I found two ways to do it:

  1. Write explicit rule for each item - working, but I really trying to avoid it

  2. Use last update group as follow (something I found in other discussion). The problem is that this is not working for me in Openhab2.

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

Is there any explanation why it doesn’t work? Should we use Calendar or GregorianCalendar?

Thx


(Rich Koshak) #2

You don’t say what currently doesn’t work.

See the [Type Conversion] (Type Conversions) article for examples of how to work with date times.


(Smhgit) #3

@rlkoshak

Thx, will take a look. Here is more detail with printouts. You can see the last time and the conversation failure.

Thx for you help!

rule "Last Update timestamp"
when
    Item Last_Update received update
then
	Last_Update.members.forEach[item |
    var lastUpdate = item.lastUpdate("influxdb");
    logInfo("LastUpdate", lastUpdate.toString)
		var Calendar cal = Calendar::getInstance()
	  cal.setTime(lastUpdate)
		//postUpdate(i.name + "_Last_Update", new DateTimeType(cal).toString)
	]
end

08:11:26.313 [INFO ] [se.smarthome.model.script.LastUpdate] - 2017-09-15T08:00:00.027+03:00
08:11:26.319 [ERROR] [.script.engine.ScriptExecutionThread] - Rule 'Last Update timestamp': Could not invoke method: java.util.Calendar.setTime(java.util.Date) on instance: java.util.GregorianCalendar[time=1505452286316,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Jerusalem",offset=7200000,dstSavings=3600000,useDaylight=true,transitions=143,lastRule=java.util.SimpleTimeZone[id=Asia/Jerusalem,offset=7200000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=3,startMonth=2,startDay=23,startDayOfWeek=6,startTime=7200000,startTimeMode=0,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=7200000,endTimeMode=0]],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=8,WEEK_OF_YEAR=37,WEEK_OF_MONTH=3,DAY_OF_MONTH=15,DAY_OF_YEAR=258,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=3,AM_PM=0,HOUR=8,HOUR_OF_DAY=8,MINUTE=11,SECOND=26,MILLISECOND=316,ZONE_OFFSET=7200000,DST_OFFSET=3600000]

(Smhgit) #4

I’ve succeed to fix the errors. WIll be happy to know if there is better / simpler way to do it.

rule "Last Update timestamp"
when
    Item Last_Update received update
then
	Last_Update.members.forEach[item |
      var DateTime lastUpdate = item.lastUpdate("influxdb").toDateTime;
	  var Calendar cal = Calendar::getInstance()
	  cal.setTimeInMillis(lastUpdate.getMillis)
	  postUpdate(item.name + "_Last_Update", new DateTimeType(cal).toString)
	]
end

(Rich Koshak) #5

Well, initially I would try:

Last_Update.members.forEach[i | postUpdate(i.name + "_Last_Update", i.lastUpdate("influxdb").toString)]

(Smhgit) #6

@rlkoshak

Thx, you suggestion is working and simpler


(vossivossi) #7

Thank you very much for all the guidance and ideas in the posts in this forum (especially @rikoshak). All the discussions and posts to design patterns helped me to find a workable solution to my requirements for “item last update”.
As i did not find a “ready to go” solution to all my requirements here so far I would like to share my thoughts also.

My requirements: Save the timestamp for each item separated by 1) last change (=timestamp of last status change of item ) and 2) last update (timestamp of last update to the item by binding without changing state). For this purpose I have defined two additional DateTime items for each item with the same name, but suffix _LCH (for last change) and _LUP (for last update).

Of course this worked with several hundred lines of repeated code that implemented inidividual rules for each item on “changed” and “received update”. But based on your other solution proposals I could now knock it down to just 12 lines of code (instead of > 1.000 lines before with more than 100 items):

rule "zwogup"
when
	Item zwogup received update
then
Thread::sleep(500) // give persistence a chance to catch up
    val recentUpdates = zwogup.members.filter[light|light.lastUpdate != null && light.lastUpdate.isAfter(now.minusSeconds(2).millis)] // all sensors that changed state in the last secon
 	recentUpdates.forEach[light|
 	postUpdate(light.name+"_LUP", light.lastUpdate.toString)
 	if (light.changedSince(now.minusSeconds(3))) 
 	{ 	postUpdate(light.name+"_LCH", light.lastUpdate.toString)	}
	]  
end

In short: the items are members of a persisted group and at every update to one group member the rule triggers. Then I update the timestamp for last update for all items updated in the last 2 seconds and after that check if the item state also changed by using the persistence method “changedSince”. If this is true, I also set the timestamp for last change.

Note: This only works if you set the persistence strategy to “everyUpdate”. Because only in this case the rule triggers on updates AND changes (if you have set “everyChange” in persistence configuration the same rule will only be triggered at status changes, not at updates without status change).

I am doing this because this allows me to check if my sensors and actors are still alive (by checking the update timestamps in another maintenance rule).

However, I am quite aware of the fact that my approach is quite “resource intensive” as I trigger the persistence DB very often and pile up a lot of data (on every update) and also query the DB very often (with the changedSince method).
As I have a rather powerful server for OH (Core-i7, 8 GB RAM) with mySQL as persistence I hope that this remains stable, but have to test in the long run yet.


(Rich Koshak) #8

:+1:

Check out the Generic Is Alive and Human Readable Names in Messages DPs. They will show you how to get a message when a sensor stops reporting.

In this case you may not need the Thread:sleep at all. If you do a sleep of 10 msec may be all you need. Even on a Pi, half a second is probably too long of a sleep for this rule.

I don’t know how many items you have but I doubt your approach is very taxing in the system. I’m sure it would run comfortably on even a resource constrained machine. You can watch top or Task Manager to see if it looks like too much is being used but I wouldn’t be worried.


(vossivossi) #9

Thank you very much for your feedback.
How does the sleep command in a rule actually influence the system behaviour? Will it only make the one rule wait or is the complete system in stillstand for that time?


(Rich Koshak) #10

It only makes the one rule’s thread stop. But there are a limited number of threads available to run rules. Adding long sleeps (according to Kai, anything longer than 100 msec is too long, I’m a little more generous and say 300 msec) ties up that thread leaving fewer threads available for other rules to run. It is not only possible but likely that using long Thread::sleeps will cause increased latency in your rules processing at a minimum and could lead to a complete failure of OH rules as everything is tied up waiting for Thread::sleeps.

Long Thread::sleeps can work just fine, but it is risky.


(Smhgit) #11

I have two type of items which I want to save the last update time for them. They are Number(s) and Switch(es). The problem is that Last_Update group can be used only for one of the types, or at least, when I add switches to the group it doesn’t work. Any suggestion how to handle this case?

Thx!

This is what I want:

Group Last_Update

Number GF_Guest_Humidity “Guest Room Humidity [%.1f %%]” (Last_Update)
Switch GF_Living_Motion “Living Room PIR [MAP(motion.map):%s]” <motion_sensor> (Last_Update)

For now I have defined two groups but I am not sure it is the right way to go

Group Last_Update_Number:Number
Group Last_Update_Switch:Switch


(Rich Koshak) #12

As above, you don’t say specifically what doesn’t work.

There was a recent change (2.1 release I think) that changed the way that Groups work. They no longer generate updates when you do not give them a Type. I’ve not yet experimented with having Items of two drastically different types in the same Group. Based on what you are seeing it appears to no longer work.

So I think using two Groups, one for each type, is probably the best way to go for now. You can reuse the same Rule assuming it looks like what you posted above and just have the rule triggered by both Groups.


(vossivossi) #13

I do have a group item which is declared as NUMBER with function AVG:

Group:Number:AVG	zwogup	"zwogup[%.1f]"

There are switches, dimmers and thermostats items included and it works fine for my update rules:

rule "zwogup"
when
	Item zwogup received update
then
if (EX_STARTUP.state == OFF)
{
	Thread::sleep(150) // give persistence a chance to catch up
    val recentUpdates = zwogup.members.filter[light|light.lastUpdate != null && light.lastUpdate.isAfter(now.minusSeconds(1).millis)] // all sensors that changed state in the last secon
 	recentUpdates.forEach[light|
 	postUpdate(light.name+"_LUP", light.lastUpdate.toString)
 	postUpdate(OH_ZWOGUP_LCH, light.lastUpdate.toString)	
 	postUpdate(OH_ZWOGUP_LCH_TXT, light.name.toString)	

 	
 	if (light.changedSince(light.lastUpdate.toDateTime.minusSeconds(2)))
 	{
 	postUpdate(light.name+"_LCH", light.lastUpdate.toString)
	}
	]  
	
}
end

I am running Snapshot 2.2.0, Build 1036.