Eliminating lingering timers

Apparently, you can’t create Items in PaperUI that bind to 1.x version bindings (Expire is a 1.x binding). Daniel mentioned somewhere (this thread, another thread, I forget) that he only has Items defined through PaperUI.

Personally, I would probably still create the .items files for the Timers rather than write the Timer code in my rules but I’m not worried about keeping my configuration pure.

Well, keeping the configuration pure is one of the points, I’d rather not having to configure my Items on two different places. Also, I’m not really sure what Expire is capable of? Setting the Item to NULL after a certain time is one thing, but I’m also sending out warning notifications, is that possible with Expire?

Everyone keep telling me Expire is the way to go. I just think it feels like a step in the wrong direction to start making 1.x Items just to start using a binding that is not in phase with the rest of openHAB. Anyone knows if there are any plans on making a 2.x Expire binding?

I keep my expire timer definitions in a file called “virtual.items” along with the _PROXY variants of every switch. I never tried it, but I’d be surprised if you could define such unbound items as PROXY in PaperUI. Have you done that @rlkoshak ?

Yes. See the link above to the Expire Binding Based Timers Design Pattern above.

You can have the Expire binding set an Item to NULL which is the default behavior, update the Item to any valid state, or send a command to any valid state.

In the design pattern and how I use the binding I typically I create a Switch that receives an OFF command when the timer expires. I have a rule that triggers when the Switch received command OFF and that is where I put the code to execute (i.e. the code that would go between the [ | ] for a Timer. To cancel the timer postUpdate(OFF).

The only thing you cannot do with the Expire binding is change the Timer’s time programmatically. You still need to use Timers if the timer has to sleep for a non-predetermined amount of time.

I keep recommending the Expire binding because their use vastly simplifies and reduces the amount of code necessary to achieve the same result: no book keeping, no lambdas, nesting of code is shallower, often rules code can be entirely eliminated. Expire timers survive an update to the .rules files and if one is using restoreOnStartup it is easy to check which Timers were active when OH went down last.

I’m not sure I agree with this statement. OH 2.x was always written with backwards compatibility with 1.x bindings in mind. Using them is certainly not a step backwards. There are many absolutely critical 1.x bindings that remain and perhaps always will remain in a 1.x version (MQTT, autoupdate are two). Reimplementing a binding as a 2.x version is not always a positive experience for the users either, resulting in increased complexity (see Exec 2 compared to Exec1 and weather1 compared to weather2 and yahoo weather). I believe a 2.x version of the Expire binding would equally add in a great deal of complexity for no gains in functionality.

I might start to agree with you when OH 2.5 or so come out, or PaperUI gains the ability to configure OH 1.x bindings on an Item. I know the goal is that everything one needs will be available in OH 2.x through the UIs. But we are not there yet. For now, for many users, limiting oneself to only 2.x version bindings severely cripples OH’s utility.

You can create unbound Items in PaperUI all you want. The problem is you can only supply the stuff between { } to define the Expire binding config in a .items file. So the problem remains, the only way to use Expire (or any other 1.x version binding) is by using .items files.

I’ll continue this thread with another related question: What’s the best way to debug timers that simply don’t happen? I have a timer that should be executed after 20 minutes. I reschedule it a couple of times (always 20 minutes forward) and then suddenly I get no value from the sensor, the rescheduling doesn’t take place and when the code in the timer is supposed to execute: Just nothing. I’ve put calls to logDebug() all over the place to see exactly what happens and the only thing I’m sure of now is that the timer is scheduled to execute but doesn’t. Are timers known to be a bit buggy or is it just on my system?

(Yes I know, @rlkoshak, I should move over to using Expire instead. Leaning more towards it now, but I’d like to get my timers working anyway)

This complete rule looks like this:

rule "Utetemperatur changed"
when
    Item Utetemperatur changed
then
    sendHttpGetRequest("http://www.temperatur.nu/rapportera.php?hash=supersecret&t=" + Utetemperatur.state)
    logDebug("temprule", "Sending " + Utetemperatur.state + " to temperatur.nu")

    if(timer_u == null) {
        logDebug("temprule", "Setting timer")
        timer_u = createTimer(now.plusMinutes(19)) [|
            sendHttpGetRequest("http://www.temperatur.nu/rapportera.php?hash=supersecret&t=" + Utetemperatur.state)
            logDebug("temprule", "Resending " + Utetemperatur.state + " to temperatur.nu and rescheduling timer")
            timer_u.reschedule(now.plusMinutes(19))
        ]
    } else {
        logDebug("temprule", "Rescheduling timer")
        timer_u.reschedule(now.plusMinutes(19))
    }

end

What it does is simply send values to an external service (temperatur.nu) when the outside temperature is changed. And then if the temperature hasn’t changed for 19 minutes it repeats the sending (because temperatur.nu thinks something is wrong if it hasn’t heard from me in 20 minutes). This works 90% of all times but then suddenly I get a “Rescheduling timer” in the log and then nothing more after that. Like the timer simply vanishes…

Not that I’ve experienced or read from others on this forum. For the most part, assuming:

  • you keep a handle on the timer in a global var
  • you do not change any .rules files while the Timer is running
  • you do not explicetly cancel the Timer

Timers are pretty reliable, even really long running ones (hours). I’ve help noone with rescheduleing issues either.

It is hard to say what is going on with your system. Check the logs, are you certain that nothing happened to cause the .rules files to be reloaded when the Timer went away?

I’ve made the recommendation before but understand your reasons for not wanting to use them. I like them but that doesn’t mean I judge those who don’t use them. I’d like to get your Timers working as is as well.

I assume timer_u is a gloval val initialized to null, correct?

Under what circumstances would this Timer ever be canceled? As written right now, the Timer never gets cancels and just keeps being rescheduled forever. Is this correct?

If so I suspect the problem is your Timer is being garbage collected. This might be worth reporting as a bug on the ESH repo, though I imagine Kai’s initial reaction will be “you should not be using timers like this.”

A better way to implement something that is to run every 19 minutes forever is to use a cron triggered rule instead. Something like:

rule "get Utetemperature changed"
when
    Item Utetemperatur changed or
    Time cron "0 */19 * * * ? *"
then
    sendHttpGetRequest("http://www.temperatur.nu/rapportera.php?hash=supersecret&t=" + Utetemperatur.state)
    logDebug("temprule", "Resending " + Utetemperatur.state + " to temperatur.nu and rescheduling timer")
end

The timing won’t be exactly the same (it will post every 19 minutes AND post the changes rather than posting every 19 minutes after the last temperature change) but I think the end result will work for this service.

If you must have exactly the same timing, instead of rescheduling the Timer forever, create a new one each time. This gets tricky because it is really hard to write code inside a timer to recreate itself without hitting an infinite regression. Luckily I wrote up a way to do it here.

Honestly, I can’t say I would recommend this recursive timers approach. It is IMHO really kludgy. But it should work.

Another perhaps less kludgy way to do it could be to use an Unbound Switch to retrigger the rule when the Timer expires. That would look something like:

rule "Utetemperatur changed"
when
    Item Utetemperatur changed or
    Item Utetemperature_Timer received command
then
    sendHttpGetRequest("http://www.temperatur.nu/rapportera.php?hash=supersecret&t=" + Utetemperatur.state)
    logDebug("temprule", "Sending " + Utetemperatur.state + " to temperatur.nu")

    timer_u.?cancel // I'm not sure I have the ? and the . in the right order
    timer_u = createTimer(now.plusMinutes(19), [|
        logDebug("temprule", "Resending " + Utetemperatur.state + " to temperatur.nu and rescheduling timer")
        Utetemperature_Timer.sendCommand(ON)
        timer_u = null
    ])
end

The .?cancel is a shortcut way of doing:

if(timer_u != null) timer_u.cancel

In the above, when either Utetemperature changes or Utetemperature_Timer receives a command the Rule triggers. In either case we send the temp and log it.

If the rule triggered because of a temp change then we cancel the existing timer and create a new one. If the rule triggered because of the Timer then the Timer is already null and we create a new one.

When the Timer goes off it logs and sends a command to the Timer Item which triggers the rule.

Correct.

Yep, correct again. Either it gets rescheduled by itself every 19:th minutes or it gets rescheduled when there’s a new sensor reading.

Well, in my opinion Timers should never be garbage collected, that kinda forfeits their purpose. You should be able to trust them actually being executed. You think the info I’ve put in this thread is enough to put in a bug ticket or is there anything more I should mention?

Well spotted. Actually, the timing isn’t important at all, so I guess I’ll simply change to using this model instead. It will generate a touch of extra network traffic, but I guess it’s worth it :slight_smile:

I think the info in this thread is enough. In particularly put in your code and fully describe the behavior you are after (i.e. reschedule a Timer over and over forever) and that it disappears. You can provide a link to this thread in the issue as well.

I will say though that the issue most likely ultimately lies in a third party library that is used to schedule the Timer and not something that ESH will be able to fix unilaterally. I could be wrong, that is just a gut feel.

I do agree, the Timer should never be garbage collected and I’m not certain that is what is actually going on.

My second approach above “the slightly less kludgey” one will give you exactly the same network traffic as your Timer example with only the addition of a single new Item. And it doesn’t use Expire binding so you can do everything inside of PaperUI. It is slightly more complex but reproduces your original code’s behavior exactly, assuming you care about the log statement when the timer goes off and the reduction in network traffic.

For completeness, here is what the code would look like with the Expire binding:

Switch Utetemperature_Timer {expire="19m,command=OFF"}
rule "Utetemperatur changed"
when
    Item Utetemperatur changed or
    Item Utetemperature_Timer received command OFF
then
    sendHttpGetRequest("http://www.temperatur.nu/rapportera.php?hash=supersecret&t=" + Utetemperatur.state)
    logDebug("temprule", "Sending " + Utetemperatur.state + " to temperatur.nu")

    if(Utetemperature_Timer.state == OFF) 
        logDebug("temprule", "Resending " + Utetemperatur.state + " to temperatur.nu and rescheduling timer")

    Utetemperature_Timer.sendCommand(ON)
end

With the Expire binding, we get the exact same behavior as your original with one additional Item and a reduction from 19 lines of code to 12 (including the line for the new Item), more than a 33% reduction in lines and code with fewer branches and nesting, and no duplication.

This is why I really really like the Expire binding.

However, it is impossible to compete with the simplicity of the corn triggered rule. :slight_smile:

2 Likes

Posted an ESH ticket: https://github.com/eclipse/smarthome/issues/4208

Totally agree with @rlkoshak on the utility of the EXPIRE binding. It takes a bit of “mental conversion” at first, but, in essence it lets you “abstract out” timers as just another kind of item. Write some rules when that Item changes to OFF and you are off to the races. Much cleaner than maintaining “routine” timers in-line in your rules.

After some more testing and logging I have come to the conclusion that is simply isn’t possible rescheduling a Timer that has already been executed, they won’t ever execute again. Maybe it’s simply a fact that a Timer only executes once? I now have some test rules with one Timer that does the following:

var Timer timer_1
var Timer timer_2

rule "reschedule"
when
        Time cron "0 */5 * * * ?"
then
        if(timer_1 == null) {
                logDebug("timertest", "Creating timer 1")
                timer_1 = createTimer(now.plusMinutes(2)) [|
                        logDebug("timertest", "Timer 1 executing")
                ]
        } else {
                logDebug("timertest", "Rescheduling timer 1")
                timer_1.reschedule(now.plusMinutes(2))
        }
end

rule "recreate"
when
        Time cron "0 */5 * * * ?"
then
        if(timer_2 == null) {
                logDebug("timertest", "Creating timer 2")
                timer_2 = createTimer(now.plusMinutes(2)) [|
                        logDebug("timertest", "Timer 2 executing")
                ]
        } else {
                logDebug("timertest", "Recreating timer 2")
                timer_2.cancel
                timer_2 = createTimer(now.plusMinutes(2)) [|
                        logDebug("timertest", "Timer 2 executing")
                ]
        }
end

…and it turns out that after timer_1 has been executed once it never executes again. However timer_2 which I repeatedly cancel and recreate works exactly as intended. So obviously rescheduling a Timer is not the same thing as cancelling it and recreating it exactly the same. I guess maybe this isn’t a bug but intended behaviour…

edit: Btw, @rlkoshak, I owe you an apology for not listening to you. I finally got very tired of using Timers so now I’ve replaced my temperature Items with file based Items using Expire. So now my rules are a heck load shorter. I’ve declared them like this now:

Number Utetemp_uteplatsen "Temperatur uteplatsen [%.1f °C]" {channel="zwave:device:6843488a:node7:sensor_temperature", expire="15m,NULL"}

And in the rule I’m just doing my notification stuff if the Item changes to NULL. Works like a charm :blush:

I wish you had asked. I had already run those tests. Indeed. Once a timer triggers you must recreate it. You can only reschedule a timer that has not yet started running.

That is correct. All reschedule does is change the time that a currently scheduled timer will trigger. Once a timer triggers and runs it no longer exists as far as the Timer scheduler is concerned. A lot of people write their code to never reschedule a timer and always cancel and recreate for this very reason.

That is how the library they use to implement the Timers was implemented. I don’t know if it is designed that way or it just happened to be implemented that way.

Glad it’s working well for you now!

I’m somewhat obsessive about making my rules very short but still easy to read and understand by the human so when the Expire binding was released I immediately jumped on it as a means to further shorten my code. It does help a whole lot in in doing that, though it does take a little bit of a change in thinking since the timer body is no longer inline with the rest of the rule.

It was there in the code I posted. I had a reschedule inside the trigger’s body, which I now understand will never be a good idea. Guess this explains all the problems I’ve been having with timers :slight_smile:

Ah yes, I do remember seeing that. I meant to talk about that in one of my replies above. I guess I forgot to mention it. Sorry I made you waste a bunch of time.

Nah, you’ve been of a great help. I’m learning a lot here :blush:

Just reviving this old topic… according to the class documentation on Timer.java (see below), and the code in the reschedule method of TimerImpl.java, there should be no reason why you can’t reschedule a timer, whether or not it has already executed. If you follow the TimerImpl link you will see from the code that the intention seems to be that the timer gets reconfigured to fire again (it explicitly resets cancelled and terminated to false, as well as rescheduling it with the Scheduler service).

I’ve created Issue 6456 which contains my test rule and the logs from a couple of experiments.

One interesting thing that I found from my experiments was that I misunderstood what Timer#isRunning() is for. It returns true while the body of the timer is actually running. I initially thought it would be true while it was still scheduled to run in the future (i.e. a kind of inverse of hasTerminated()). The way it actually works is potentially a lot more useful - not quite thought it through yet but in some circumstances it could be useful to know if the payload of a timer is currently executing.

    /**
     * Reschedules a timer to a new starting time.
     * This can also be called after a timer has terminated, which will result in another
     * execution of the same code.
     * 
     * @param newTime the new time to execute the code
     * @return true, if the rescheduling was done successful
     */
    public boolean reschedule(AbstractInstant newTime);

That isn’t the problem described above. The problem was that when OP reloaded his .rules files, the variables that held the Timer objects were reset but the Timers continued to exist. So there was no longer any access to the Timers in the Rules to call the method to reschedule it.

Sure, I get that and completely agree. I was responding specifically to this:

Once a timer triggers you must recreate it. You can only reschedule a timer that has not yet started running.

and:

Once a timer triggers and runs it no longer exists as far as the Timer scheduler is concerned.

I should have quoted those sections… apologies. I am in the process of setting up a build environment so I can look into this bug.

Which demonstrates the danger of resurrecting year old threads. This has been long since fixed.

If it isn’t working for you then it is a newly introduced bug. At least as of OH 2.4 #1405 the following code works:

rule "Test string"
when
  Item Test changed
then
  logInfo("Test", "Test changed to \""+Test.state.toString+"\"")

  var count = 0
  timer = createTimer(now, [ |
      logInfo("test", "Looping timer with " + count)
      if(count < 10) {
        count = count + 1
        timer.reschedule(now.plusMillis(500))
      }
      else {
        timer = null
      }
  ])
end

Of course hasTerminated probably doesn’t get set to false until after the lambda exits.

But in the future, it would cause less confusion and receive more attention if you post a new thread and if necessary link to this thread then resurrecting a really old thread that contains information that may no longer be relevant.

Noted. Sorry.

Also, your test code shows reschedule() being called from within the “body” of the timer. This works fine, I’ve also tested it in a similar way to your test. The JavaDoc for the reschedule() action definitely says that you can call reschedule() even after a timer has terminated, which is not the case and was the basis of my bug report.