Openhab2 System Restart, Persistence Reload - Item Expire Fails to reset

I have an openhab2 setup and extensively use item “expires” on switches. I recently experienced an openhab system reload (on it’s own) while a critical switch was ON with an expire. When the reload was completed, the switch was ON but the expire never triggered.

I did some testing and confirmed this behavior. If openhab restarts and values reloaded by persistence, any expire is non-functional until a new value is set. This is troubling behavior for me. Not sure if this is a bug or feature. If anybody can shed some light on this, much appreciate hearing.

I built a workaround that seems to work. On system start and after values are reloaded, if any are ON, process another ON. This starts the expire.

rule "System Restart - Reset all Expire-able Items so Expire is working"
 when
    System started
 then
    while (test.state == NULL) {
        Thread::sleep(1000)
    }
    if (test.state == ON) test.sendCommand(ON)
    etc
end

First post - hope I did this right.

2 Likes

Per the doc’s:

‘The expiration time will be started or restarted every time an item receives an update or command other than the specified “expire” update/command.’

‘Any future expiring update or command is cancelled if the item receives an update or command that matches the “expire” update/command.’

It looks like the workaround you posted may be the solution. :wink:

Neither of these. It’s expected behavior. Expires are only stored in memory thus any restart will make OH forget about these.

That’s by design.
A restore-on-startup action setting the state of an Item is not considered to be an “update”, since as you point out it is a restore or reload and does not reflect an actual event.

I appreciate that’s not the way you’d like it to behave in this case, but that’s the way it is and it is not arbitrary.
(In practice the restore should run before bindings start anyway, so it would be difficult to have it the other way.)

I think your workaround rule will still work if you postUpdate rather than sendCommand. which could also avoid triggering any rules you might have listening for commands or change.

On another approach … why are you using restore-on-startup for a “critical” switch? You don’t know what’s happened while openHAB was off - do you need to find out in some way?

EDIT - I thought there was a thread about “saving” unexpired timing functions across a reboot, but I can’t find it now. The “simplest” way is to use a rules based timer and store the target datetime in a restore-on-startup Item. On restart, you can examine that and decide if you need to do “expired” or “resume timing” functions. It’s fiddly but may be needed for critical tasks.

You shouldn’t rely on persisted data to correctly represent the state as you cannot be sure when OH has shut down/crashed and if it has persisted the last state.
Make sure instead you initialize on system startup to a safe state.

I fully agree. I don’t know the nature of this critical task. If something was supposed to happen at some appointed time, it is possible to store and restore that appointed time, then at restart determine if it has either passed or is still pending, and decide if you need to do something about it.

Thanks for all the responses.

My use case is an item switch called peeps_seen which has a 20 minute expire. Rules look for motion or device pings to update this switch to ON. On expire, the thermostat is adjusted to Away settings. I experienced the situation of an openhab restart after leaving the home and before the 20 min expired. I was baffled as to why it failed. My solution would be late to the party but still enable the functionality.

If the expire was on a running sprinkler, I would want to use the same mechanism to prevent the sprinkler from running forever. Is there a better way?

I believe the same issue exists with using timers in rules when a restart occurs.

Depending on how Persistence is set up, you could get that timestamp from previousState instead of using a separate Item. But you have to be very careful about the configuration (e.g. everyMinute would not work).

Correct, a restart of OH deletes all the Timers (it’s more complicated than that really but that’s outside this discussion).

I do agree that often times the amount of the time of the timer isn’t that important so long as it eventually times out. In that case your Rule would work but there is a much better way to implement it.

  1. Don’t use long sleeps in a Rule. That’s what Timers are for and the long sleep can cause significant problems.

  2. Make the Rule generic.

First, create a ResetExpire Group and add all the Items that need to have their Timers reset on startup to this Group.

rule "Reset Expire Items"
when
    System started
then
  var count = 0
  val timer = createTimer(now, [ |
    val notNull = ResetExpire.members.filter[ i | i.state != NULL && i.state != UNDEF ]
    if(notNull.size != ResetExpire.members.size && count < 5) {
        count = count + 1
        timer.reschedule(now.plusSeconds(1)
    }
    else notNull.forEach[ i | i.postUpdate(i.state) ]
  ])
end

The Rule above kicks off a timer so we don’t sit around consuming an execution thread doing nothing. The Timer executes immediatly. We get a list of all the Items that are not NULL or UNDEF. If there is one or more that are NULL or UNDEF, we will wait up to five seconds (checking every second) to give restoreOnStartup a chance to finish. If it takes longer than that, either restoreOnStartup is broken or the NULL/UNDEF state is correct. Finally once all Items are initialized or we time out, we postUpdate the current state of the Item. This will trigger Expire to start it’s timer.

With the above Rule it works for any Item that may have Expire attached, doesn’t consume a runtime thread doing nothing, and has a fail safe in case where an Item is supposed to be NULL/UNDEF or restoreOnStartup failed for one or more Items for some reason.

My suggestion about persist/restore a date time Item was about critical jobs.

Example ; sprinkler is on, timer scheduled to turn it off in two hours, say 4pm.
Have the rule also put the target time into the date time Item, and persist it.
System reboot, timer lost, sprinkler still on.
But a rule can examine restored datetime.
Is it now before 4pm? Reconstruct a timer.
Is it now after 4pm? Make sure the sprinkler is off.
It’s pretty foolproof.

Right, and I was just suggesting that it might be possible to use Persistence and previousState instead of needing to create a separate Item. All the rest would be the same as you describe.

Ah okay, on the assumption that a timeout was always supposed to run and always for the same duration.

Great solution. I’ll give this a try.

Also, like the sprinkler suggestion to save off and test end point datetime on system startup.

Here’s my working code. Thanks all.

rule "Reset Expire Items"
when
    System started
then
    var count = 0
    var Timer tt = NULL
    tt = createTimer(now, [|
    val notNull = ResetExpire.members.filter[ i | i.state != NULL && i.state != UNDEF ]
    if (notNull.size != ResetExpire.members.size && count < 5) {
        count = count + 1
        tt.reschedule(now.plusSeconds(1) )
    }
    else {
        logInfo("startup","Expire Reset: notNull= " + notNull.size.toString + " count  = " + count.toString + " expiremembers = " + ResetExpire.members.size.toString)
        notNull.forEach[ i | i.postUpdate(i.state) ]
    }
  ])
end

I’m surprised that is working. NULL is not of type Timer. You shouldn’t be able to initialize tt to NULL. Are you sure you don’t see errors in VSCode or the logs? Do not confuse NULL which is the state that an Item get’s initialized with with null which is a way to say “no value”.

Furthermore, there is no need to initialize tt to null in the first place. The very next line you set it to the result of calling createTimer. So you just need var Timer tt = createTimer(now, [ |.

Finally, you never use tt and it doesn’t exist outside of this rule. It serves no purpose and in fact you will get warnings in the logs saying that tt is never used. So you don’t even need to define it. Just call createTimer.

createTimer(now, [|

The code you first suggested gave me errors.

2020-02-24 14:32:20.856 [ERROR] [org.quartz.core.JobRunShell         ] - Job DEFAULT.Timer 1 2020-02-24T14:32:20.131-08:00: Proxy for org.eclipse.xtext.xbase.lib.Procedures$Pr
ocedure0: [ | {
  val notNull
  org.eclipse.xtext.xbase.impl.XIfExpressionImpl@859631e (conditionalExpression: false)
} ] threw an unhandled Exception:
java.lang.reflect.UndeclaredThrowableException: null

No errors on my code.
I just changed mine to remove the initialize and use val tt …
Code works now. Strange. Not sure why I got previous errors.

BTW, your code was missing a “)”.
Resulting code in use now:

rule "Reset Expire Items"
when
    System started
then
    var count = 0
    val tt = createTimer(now, [|
		val notNull = ResetExpire.members.filter[ i | i.state != NULL && i.state != UNDEF ]
		if (notNull.size != ResetExpire.members.size && count < 5) {
			count = count + 1
			tt.reschedule(now.plusSeconds(1) )
		}
		else {
			notNull.forEach[ i | i.postUpdate(i.state) ]
		}
  ])
end

I am migrating to OH3. The expires do not survive a restart so I am interested in this code working. I’ve tested it on OH3 and found that it performs the update but fails to trigger the expire. I am using my OH2.5 item and rules files with OH3.

Any suggestions?

The expires never did survive a restart. This thread created a rule that runs during system started that “pings” the Items kick off the Expire.

That should still work on OH 3 and if it’s not there is something wrong with the rule and/or you’ve not added all your expire configured Items to the ResetExpire Group. Add some logging to see if the rule is triggering at all. If it’s triggering, add some logging to see if it’s ever successfully reaching that else statement. Double check that all these Items are reaching a not NULL and UNDEF state through restoreOnStartup.