jwiseman
(Mr. Wiseman (OH 4.3.0 Snapshot on Pi4))
1
This rule isn’t working to email any devices that are 2 days old from being updated. I can’t figure out why.
Group:DateTime:EARLIEST gSensorUpdates <time> // used to track sensor updates
DateTime FurnaceSensor_Update "Update [%1$tF %1$tR]" <clock> (gSensorDateTag, gSensorUpdates)
if (gSensorUpdates.allMembers.filter[ GenericItem s | s.state == NULL || s.state === null || s.state == UNDEF || s.changedSince(now.minusDays(2)) ]) {
var String subjectemaildate = "zWave Update Date is OLD!"
var batterymessage = "zWave Update Date Field is 2 Days OLD device is " + s.name
mailActions.sendMail(JaygMail, subjectemaildate, batterymessage)
}
Those four words contain an infinity of possibilities. Not working how? Errors in the logs? Doesn’t do anything at all? Does the wrong thing?
s.state === null will always be false. An Item will never have a null state. It’s initialized to NULL and cannot be changed to null so you’ll never see s.state that’s null. But that’s not causing problems here, it’s just redundant.
.filter returns a List of all the members that meet the condition, not a boolean. So the if statement is meaningless. It’s the same as if(gSensorUpdates.allMembers) {. Obviously that doesn’t make sense.
There is no such thing as s outside of the [ ] of your filter. Yet you are trying to use s.name inside the if statement. Do you mean to loop over all the members that meet the filter and send an email for each one?
Since the members of gSensorUpdates are already DateTime Items, don’t you just want to test the state of the Item to see if it’s three days ago? Why query persistence? That’s a pretty heavy operation.
val twoDaysAgo = now.minusDays(2)
val staleItems = gSensorUpdates.allMembers.filter[ s | s.state == NULL
|| s.state == UNDEF
|| (s.state as DateTimeType).toZonedDateTime.isBefore(twoDaysAgo) ]
staleItems.forEach[s |
var subjectemaildate = "zWave Update Date is OLD!" // never force the type unless you have to in Rules DSL
var batterymessage = "zWave Update Date Field is 2 Days OLD device is " + s.name
mailActions.sendMail(JaygMail, subjectemaildate, batterymessage)
]
Or if you only want the one message:
val twoDaysAgo = now.minusDays(2)
val staleItems = gSensorUpdates.allMembers.filter[ s | s.state == NULL
|| s.state == UNDEF
|| (s.state as DateTimeType).toZonedDateTime.isBefore(twoDaysAgo) ]
val staleNames = staleItems.map[ s | s.name ].reduce[ names, curr | names names + ", " + curr ]
val subjectemaildate = "zWave Update Date is OLD!"
val batterymessage = "zWave Update Date Field is 2 Days OLD device is " + staleNames
mailActions.sendMail(JaygMail, subjectemaildate, batterymessage)
In JS it would be:
var staleItems = items.gSensorUpdates.descendents.filter( s => s.isUninitialized || time.toZDT(s).isBefore(time.toZDT("P-2D"))
.map(s => s.name)
.join(', ');
if(staleItems.length > 0) {
actions.get('mail', 'thingID').sendMail(JayMail,
"zWave Update Date is OLD!",
"zWave Update Date Field is 2 Days OLD device is " + staleItems);
}
in OH 5 lastUpdate will be a member of the Item so you won’t need to make a persistence call nor need a separate Item to store the last time an Item was updated
Expire can be useful here to expire the sensor readings when they are not updated and you can expire them to UNDEF which can trigger a rule to immediately report that the sensor went offline.
I actually use a combo of the first and last for my sensors. Expire on the Item will set the Item to UNDEF if it doesn’t update for long enough and the rule template will call a rule if it remains UNDEF for few minutes (just in case the sensor comes back) and again when the sensor does come back so I can cancel the notification.
jwiseman
(Mr. Wiseman (OH 4.3.0 Snapshot on Pi4))
3
Thank you Rich! Your explanation educated me to look at my other filter rules and tweak them.
I can always count on you!
Best, Jay
jwiseman
(Mr. Wiseman (OH 4.3.0 Snapshot on Pi4))
4
2025-03-17 18:35:01.936 [ERROR] [cript.internal.handler.AbstractScriptModuleHandler] - Script execution of rule with UID ‘default-11’ failed: ‘toZonedDateTime’ is not a member of ‘org.openhab.core.library.types.DateTimeType’; line 5136, column 110, length 41 in default
I haven’t use Rules DSL for many years now. Maybe it’s getZonedDateTime or just zonedDateTime. there might be a deprecation warning. There is a bunch of stuff going on with DateTimeType and moving to an Instant instead of a ZonedDateTime. I’m not sure what change happened when in relation to the version of OH you are running.
Perhaps .getZonedDateTime(ZoneId.systemDefault()) will work for this? I have some DSL rules that use this for date processing.
jwiseman
(Mr. Wiseman (OH 4.3.0 Snapshot on Pi4))
7
EDIT:
@laursen, can you recommend the correct syntax for this below to remove the deprecated message? I see your fixed the core around this but I’m unclear the syntax. I’m on OH 4.3.0.
(s.state as DateTimeType).getZonedDateTime.isBefore(twoDaysAgo)
Here’s what is working now except the compiler error now.
val twoDaysAgo = now.minusDays(2)
val staleItems2 = gSensorUpdates.allMembers.filter[ GenericItem s | s.state == NULL || s.state == UNDEF || (s.state as DateTimeType).getZonedDateTime.isBefore(twoDaysAgo) ]
Compiler error for OH 4.3.0
The method getZonedDateTime() from the type DateTimeType is deprecated
Found this blurb on another post about getZonedDateTime
The reason is that DateTimeType no longer contains time-zone, so this will have to be applied.
The deprecated method getZonedDateTime() uses ZoneId.systemDefault() internally. However, this can be misleading, since it is no longer the time-zone used when creating the DateTimeType object, because that time-zone is now discarded.
The minimal solution is to replace getZonedDateTime by getZonedDateTime(ZoneId.systemDefault()) .
1 Like
jwiseman
(Mr. Wiseman (OH 4.3.0 Snapshot on Pi4))
9
Final working rule below:
val twoDaysAgo = now.minusDays(2)
val staleItems2 = gSensorUpdates.allMembers.filter[ GenericItem s | s.state == NULL || s.state == UNDEF || (s.state as DateTimeType).getZonedDateTime(ZoneId.systemDefault()).isBefore(twoDaysAgo) ]
staleItems2.forEach[ GenericItem s |
subjectemailbattery = s.name + " Date is OLD!"
batterymessage = "Update Date Field is 2 Days OLD. The device is " + s.name + " and the date is " + s.state
mailActions.sendMail(JaygMail, subjectemailbattery, batterymessage)
]