[SOLVED] Group Based Timestamps and durations with triggeringItem and Member of

Hi Community,
I have purchased a couple of window Sensors.
My Syntax for the Item Names is as follows:

Contact LivingRoom_Window_Sensor "Terassentür [MAP(de.map):%s]"    (gGroundFloor,gGF_gLivingroom_gWindowSensor,fg_gStatus_gSensor_gWindow,fg_gStatus_gSensor) {channel="zwave:device:Zwave_Usb:node21:sensor_door"}
Number LivingRoom_Window_BatteryLevel "Batterie Terassentür [%d %%]"              (gGroundFloor,gGF_gLivingroom_gWindowSensor, fg_gStatus_gBattery) {channel="zwave:device:Zwave_Usb:node21:battery-level"}
DateTime LivingRoom_Window_SensorTimestamp "Terassentür gelüftet am [%1$td.%1$tm. %1$tH:%1$tM]" (gGroundFloor,gFF_gBathroom_gWindowSensor,fg_gStatus_gSensor)
String LivingRoom_Window_SensorOpen "Terassentür Gelüftet für: [JS(mins.js):%s]"                           (gGroundFloor,gFF_gBathroom_gWindowSensor,fg_gStatus_gSensor)

I have a rule, which gives returns a timestamp when the window was last opened:

rule "Window Open TimeStamp"
when 
Member of fg_gStatus_gSensor_gWindow changed from CLOSED to OPEN
then
    if(previousState == NULL) return;
    postUpdate(triggeringItem.name+"Timestamp", now.toString)
end

So far so good.

I also want do display the duration. I did that for one window as follows:

rule "duration"
when
    Item Bathroom_Window_SensorContact changed from OPEN to CLOSED 
then
    //duration as Number in minutes
val duration    = (now.millis - (Bathroom_Window_SensorTimestamp.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli) / 1000
Bathroom_Window_SensorOpen.postUpdate(duration)
end

I am now desperately trying to convert this into a Group Based approach, but my log tells me that .state is not something I can use. This is what I tried:

rule "Group duration"
when 
Member of fg_gStatus_gSensor_gWindow changed from OPEN to CLOSED
then
//duration as Number in minutes
val duration    = (now.millis - ((triggeringItem.name+"Timestamp").state as DateTimeType).zonedDateTime.toInstant.toEpochMilli) / 1000
postUpdate(triggeringItem.name+"Open", duration)

end

Probably I am terribly wrong about String/Number and DateTime. Maybe you guys can help.

Thanks :slight_smile:

That’s just a String. A String is not an Item. There is no state method on a String.

See Design Pattern: Associated Items for how to get a reference to the actual Item.

Hey Rich,
thank you (as always) for your quick reply.
I already worked through the rules documentation, also checked the Associated Items Approach. But I could not really find a solution for my problem.
I think I am stuck at that point, where I would need to convert a String to an Item.

Ultimately I want to

postUpdate(triggeringItem.name+"Open", duration)

Where duration is the a number value.

My Problem is, that I do not need the state of the triggering Item for the caluclation, but the state of.

triggeringItem.name+"Timestamp"

If I could extract that value, I could then calculate with it, I guess. . .

Try that:
Taken from:

import org.eclipse.smarthome.model.script.ScriptServiceUtil

rule "Group duration"
when 
    Member of fg_gStatus_gSensor_gWindow changed from OPEN to CLOSED
then
    //duration as Number in minutes
    val myItem = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"Timestamp"")
    val duration    = (now.millis - (myItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli) / 1000
    postUpdate(triggeringItem.name+"Open", duration)
end

Hi,
thanks. That looks plausible.
I will try tonight. My alternative thought was to try it with

members.filter

But I haven’t quite figured out the correct syntax. I’ll do some thinking tonight :slight_smile:

Yes that’s another way to do it

I tried the ScriptServiceUtil Approach the result is this code:

when 
Member of fg_gStatus_gSensor_gWindow changed from OPEN to CLOSED
then
//duration as Number in minutes
    val myItem = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"Timestamp")
logInfo("Window", "Item " + myItem)
val duration    = (now.millis - (myItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli) / 1000
logInfo("Window", "Duration " + duration)
logInfo("Window", triggeringItem.name+"Open" + duration)
postUpdate(triggeringItem.name+"Open", duration.toString)
end

So the only thing I had to add was .toString.
Otherwise I got the error that java cannot invoke method.
Later on I will try the member.filter method, because it seems like a method that might be easier to comprehend for my. But for now the rule is working. So Thank you Very much!

Which is demonstrated in two different ways in the Associated Items DP: pulling the Item from a Group by its name ( Group findFirst) and pulling the Item from the Item Registry by its name (Item Registry).

That was the basis for the Item Registry example in the Associated Items DP.

Which is the basis of the Group findFirst example in the Associated Items DP.

:see_no_evil:
Thank you for your patience mate…
I really love your Design Patterns. However for me they sometimes seem sort of abstract and I have problems deriving the pattern to my specific use case. I am an Air Traffic Controller after all - we don’t like to think about problems for too long. We want easy quick solutions :smiley:

Looking at the Associated Items DP with some Hindsight you are absolutely correct - and as practice I will try to implement my rule using findFirst as well.
Sorry Rich…:expressionless:

So here are the two working Solutions:

Option 1: Using ScriptServiceUtil

import org.eclipse.smarthome.model.script.ScriptServiceUtil

when 
Member of fg_gStatus_gSensor_gWindow changed from OPEN to CLOSED
then
  val myItem = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"Timestamp")
//logInfo("Window", "Item " + myItem)
val duration    = (now.millis - (myItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli) / 1000
//logInfo("Window", triggeringItem.name+" was Open for: " + duration + "s")
postUpdate(triggeringItem.name+"Open", duration.toString)
end

Option 2: Using Filtering of Groups:

when
Member of fg_gStatus_gSensor_gWindow changed from OPEN to CLOSED
then
val contact = triggeringItem
val timeopened= fg_gStatus_gSensor.members.findFirst[ t | t.name == contact.name+"Timestamp" ] as DateTimeItem
//logInfo("Window", "val opened at:  " + timeopened)
val opendurationitem = fg_gStatus_gSensor.members.findFirst[ dt | dt.name == contact.name+"Open" ] as StringItem
val openduration = (now.millis - (timeopened.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli) / 1000
//logInfo("Window", "val opened for:  " + openduration)
opendurationitem.sendCommand(openduration)

end

Excelnet Duration rule is working perfectly for my widow doors .
Now How Will I get cumulative time in this rule : I am noobs to programming . I am looking for Total Duration In last 24 hrs widow or Door Open .

Hi Pls help I tried with following modification but not working . Plan is to use persistence
Added Item with “______Opentotal”
Then Modified the rule

rule “DoorWindow Open Duration”

when

Member of gStatus_gDoor_gWindow changed from OPEN to CLOSED

then

val myItem = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+“Timestamp”)

//logInfo(“Window/Door”, "Item " + myItem)

val duration = (now.millis - (myItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli) / 1000

//logInfo(“Window/Door”, triggeringItem.name+" was Open for: " + duration + “s”)

postUpdate(triggeringItem.name+“Open”, duration.toString)

postUpdate(triggeringItem.name+“Opentotal”.sumSince(now.minusDays(30),“rrd4j”))


Error in Log :

019-07-24 23:02:08.642 [vent.ItemStateChangedEvent] - Bathroom_DoorOpen changed from 598 to 2283

2019-07-24 23:02:09.114 [vent.ItemStateChangedEvent] - Bedroom_DoorTimestamp changed from 2019-07-24T21:58:09.184+0530 to 2019-07-24T23:02:09.096+0530

2019-07-24 23:02:36.712 [vent.ItemStateChangedEvent] - Storage_Available changed from 18418 to 18417

==> /var/log/openhab2/openhab.log <==

2019-07-24 23:06:55.687 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model ‘duration.rules’

==> /var/log/openhab2/events.log <==

2019-07-24 23:07:04.723 [vent.ItemStateChangedEvent] - Bathroom_Door changed from CLOSED to OPEN

2019-07-24 23:07:04.736 [vent.ItemStateChangedEvent] - Bedroom_Door changed from OPEN to CLOSED

2019-07-24 23:07:04.784 [vent.ItemStateChangedEvent] - Bathroom_DoorTimestamp changed from 2019-07-24T22:24:05.360+0530 to 2019-07-24T23:07:04.766+0530

==> /var/log/openhab2/openhab.log <==

2019-07-24 23:07:09.306 [ERROR] [ntime.internal.engine.RuleEngineImpl] - Rule ‘DoorWindow Open Duration’: ‘sumSince’ is not a member of ‘java.lang.String’; line 21, column 32, length 47

==> /var/log/openhab2/events.log <==

2019-07-24 23:07:09.317 [vent.ItemStateChangedEvent] - Bedroom_DoorOpen changed from 1561 to 300

2019-07-24 23:08:42.926 [vent.ItemStateChangedEvent] - SUJITMOB changed from ON to OFF

Please use code fences.

It means what it says.
It’s about this line -

postUpdate(triggeringItem.name+"Opentotal".sumSince(now.minusDays(30),"rrd4j"))

triggeringItem.name+"Opentotal" is just a string, and strings do not have any sumSince persistence magic.

You’ll need to get hold of the Item with that name, using the getItem method already in use earlier in the rule.

Thanks rossko57

after going home I shall try following rule

rule "DoorWindow Open Duration"
when 
Member of gStatus_gDoor_gWindow changed from OPEN to CLOSED
then
  val myItem = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"Timestamp")
  val myItemopen = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"Opentotal")
      logInfo("Window/Door", "Item " + myItem)
	  logInfo("Window/Door", "Item " + myItemopen)
val duration    = (now.millis - (myItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli) / 1000

      logInfo("Window/Door", triggeringItem.name+" was Open for: " + duration + "s")
postUpdate(triggeringItem.name+"Open", duration.toString)

  
   val myItemopendur=myItem.sumSince(now.minusDays(30),"rrd4j")

postUpdate(triggeringItem.name+"Opentotal", myItemopendur.toString))

logInfo("Window/Door", triggeringItem.name+" was Open for: " + myItemopendur + "s")

end

Why do that, when you got hold of the actual Item a few lines earlier? (plus, watch the brackets)

postUpdate(myItemopen, myItemopendur.toString)

Better still, use the .method instead of the Action, it’s preferable where you have the Item.

myItemopen.postUpdate(myItemopendur.toString)

Thanks rosskp57 following rule worked

rule "DoorWindow Open Duration"
when 
Member of gStatus_gDoor_gWindow changed from OPEN to CLOSED
then
  val myItem = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"Timestamp")
  val myItemop = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"Open")
  val myItemopen = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"Opentotal")
     // logInfo("Window/Doormyitem", "Item " + myItem)
     // logInfo("Window/Doormyitem", "Item " + myItemop)
	   // logInfo("Window/DoormyitemTptalopen", "Item " + myItemopen)
  val duration = (now.millis - (myItem.state as DateTimeType).zonedDateTime.toInstant.toEpochMilli) / 1000
     // logInfo("Window/DooropenDuration", triggeringItem.name+" was Open for: " + duration + "s")
  postUpdate(triggeringItem.name+"Open", duration.toString)

  val myItemopendur=myItemop.sumSince(now.minusMonths(1),"rrd4j")
  myItemopen.postUpdate(myItemopendur.toString)
      //logInfo("Window/DooropenTotalDuration", triggeringItem.name+" was Total Open for: " + myItemopendur + "s")
end

Now setting same for all my electric Items . To find out running duration during billing cycle .