Implementing Timer issue

  • Platform information:
    • Hardware: RPi4
    • OS: Raspain - current
    • openHAB version: 2.5.12-1
      I’m trying to get some basic Timer stuff working and I just cant get it to do what I want.
      The following code should create a timer is I “press button B” and should extend the Timer when I “press button A”. However the check
      GarageMovementTimer.isRunning is coming back false when the Timer is actually running and when I comment out the “isRunning” check and just do the GarageMovementTimer.reschedule I dont get any error but the timer end time is not push out by 10 seconds from the current time.
var Timer GarageMovementTimer = null

rule "RF Sensor timer" //RF Sensors triggered
when
        Item RF_Sensor changed 
then
        logInfo("Test","rule Start::RF Sensor timer:: " + triggeringItem.name + " changed from " + previousState + " to "+ newState)
    switch(RF_Sensor.state) {
        case "341AA2" : {logInfo("Test", "Button A pressed")
                if (GarageMovementTimer.isRunning){
                    logInfo("Test", "Rescheduling")
                    GarageMovementTimer.reschedule(now.plusSeconds(10))
                }
                }
        case "341AA8" : {logInfo("Test", "Button B pressed")
                GarageMovementTimer= createTimer(now.plusSeconds(15), [|
                    logInfo("Test","GarageMovementTimer ended")
                    GarageMovementTimer = null
                    ])
                    }
        case "341AA1" : {logInfo("Test", "Button C pressed")}
        case "341AA4" : {logInfo("Test", "Button D pressed")}
        default : {logInfo("Test", "Unrecognised Code ="+ RF_Sensor.state)}
        }
end

The Log looks like this

2021-06-09 00:07:16.536 [INFO ] [.eclipse.smarthome.model.script.Test] - rule Start::RF Sensor timer:: RF_Sensor changed from 341AA2 to 341AA8
2021-06-09 00:07:16.538 [INFO ] [.eclipse.smarthome.model.script.Test] - Button B pressed
2021-06-09 00:07:20.582 [INFO ] [.eclipse.smarthome.model.script.Test] - rule Start::RF Sensor timer:: RF_Sensor changed from 341AA8 to 341AA2
2021-06-09 00:07:20.584 [INFO ] [.eclipse.smarthome.model.script.Test] - Button A pressed
2021-06-09 00:07:24.885 [INFO ] [.eclipse.smarthome.model.script.Test] - rule Start::RF Sensor timer:: RF_Sensor changed from 341AA2 to 341AA1
2021-06-09 00:07:24.889 [INFO ] [.eclipse.smarthome.model.script.Test] - Button C pressed
2021-06-09 00:07:25.617 [INFO ] [.eclipse.smarthome.model.script.Test] - rule Start::RF Sensor timer:: RF_Sensor changed from 341AA1 to 341AA2
2021-06-09 00:07:25.622 [INFO ] [.eclipse.smarthome.model.script.Test] - Button A pressed
2021-06-09 00:07:31.541 [INFO ] [.eclipse.smarthome.model.script.Test] - GarageMovementTimer ended

Any pointers/help is appreciated

You misunderstand .isRunning() function. This returns true only during the few milliseconds the timer code is executing. It is not true while the timer is just awaiting its appointed time.

isActive is probably what you want… but that is a OH version 3 feature, and not available to you in OH2

You’ll have seen a great many example that approach this a different way.
We start with
var Timer GarageMovementTimer = null
so if we test the variable for null, we know the Timer is not running yet.
Conversely, if it is not-null then it is timing or executing.
Replace
if (GarageMovementTimer.isRunning) {
with
if (GarageMovementTimer !==null) {

That means we also need to set it back to null when the Timer code does execute, which you’re already doing.

Technically, you could also check to see that isRunning is not true before rescheduling, avoiding trying to do that while code is executing, but that is such a tiny time window you’ll never hit it if you tried.
I’m not sure if rescheduling a Timer during execution blows up or just fails silently.

Rossko,
Thanks for helping me with that. Unsurprisingly, its now running the getting to the “rescheduling” part of the code :slight_smile:

I think it actually successfully reschedules or else a Timer that reschedules itself wouldn’t work. For example:

    var count = 0
    var Timer timer = null
    timer = createTimer(now.plusSeconds(1), [ |
        count = count + 1
        if(count < 10){
            logInfo("Test", "Timer test: " + count)
            timer.reschedule(now.plusSeconds(1)
        }
    ]

When that call to timer.reschedule runs timer.isRunning should be returning true because the lambda is running. But the reschedule works, or at least it used to, in Rules DSL at least. There is a context/scope issue that pops up in Python and JavaScript which is independent of the timer reschedule that crops up.

1 Like

Yes, of course! By the time we are executing Timer code block, the old schedule has already passed.

I’m on a roll of hopeless ignorance :slight_smile: and have another Q if I may.
The following code is intended to set a timer to turn off the garage lights after a certain time. There’s another bit of which reschedules the timer if there is movement in the room. All seems straightforward. However sometimes all three lights com on at the same time and 2 or 3 timers appear to get initiated even though I check for “GarageMovementTimer===null” beforehand. As a consequence the “rescheduling” doesn’t really work.
I cant understand how 3 distinct timers can share the same variable/object name nor how to prevent it (other than introducing delays in the lights coming on).
What am I misunderstanding?

var Timer GarageMovementTimer = null

rule "Garage Lights On"  // Once the lights are on, set a timer to turn them off 
when 
    Item GarageInsideLight1 changed to ON or
    Item GarageInsideLight2 changed to ON or
    Item GarageInsideLight3 changed to ON 
then
        logInfo("Alarm","rule Start::Garage Lights On:: " + triggeringItem.name + " changed from " + previousState + " to "+ newState)
        if(GarageMovementTimer===null)
            {
            GarageMovementTimer= createTimer(now.plusMinutes(20), [|
                logInfo("Alarm","GarageMovementTimer ended")
                GarageInsideLight1.sendCommand("OFF")       // Turn lights on
                GarageInsideLight2.sendCommand("OFF")       // Turn lights on
                GarageInsideLight3.sendCommand("OFF")       // Turn lights on 
                GarageMovementTimer = null
                ])
            }
end
1 Like

You’ve only shown us code that creates one Timer; there doesn’t seem to be any reason to think that you’ve created three by accident.

It is possible, GarageMovementTimer contains only a pointer or handle to the real fully-independent Timer. Once you’ve set up a Timer, you can trash or overwrite the handle without affecting the Timer at all (but of course you won’t then be able to communicate with it any more).

I doubt that’s happening. In OH2 there can be multiple starts of the same rule executing in parallel, but there’s only a tiny time window in this rule between the test for handle=null and populating it.

So far as openHAB is concerned, this will always appear as three consecutive events, however minimal the spacing in time.

Please show us the logs that illustrate your problem.

You might also rework this altogether; your rule simply turns the lights OFF blindly twenty minutes after they were turned ON for any reason. “Expire” option on each Item will accomplish the same thing, with no rule.

I don’t really understand why, if you have motion sensing available to control rescheduling, you’re not using it to control the start up too.

Thanks Rossko,
I was not aware of the EXPIRE functionality and I’ll look into it this evening. However I think it will be difficult for my circumstances to be implemented using it.

My IR sensor is shared with my alarm system (primary purpose) and is in the middle of the garage a distance from the door and so will not trigger the lights on in a timely manner. So I also hook into the door switch (also primarily for alarm) for the “On” function and its also possible for someone to turn the lights on via the app or the switch on the wall.

There is definitely 2 or 3 timers getting created as I can see output from logInfo("Alarm","GarageMovementTimer ended")
multiple times in the log and with almost identical timestamps.
I think your suggestion of different routines for each light might be a way to go. The ancient programmer in me just hates code repetition though.
Maybe I could also group the 3 lights and then attached the rule to the group instead. I’ll explore that also

Thanks again

If only we could see what you see. The other logging you’ve already got provides breadcrumb trails too.

I didn’t suggest that, though of course you could apply individual light timers.

Alternative approach; put the lights in a Group with an OR(ON,OFF) function, so that any light ON gives group ON, further lights cause no further change. Trigger your rule from group state change.

Simplifies your OFF functionality too, since you need only command Group OFF to distribute an OFF command to all members.

Definitely follow rossko57’s advice about using a Group for to trigger this rule.

rossko57 did a pretty good job of explaining this but I want to describe the sequence of events.

It appears with limited information that the three lights get an ON command all within milliseconds of each other. As mentioned, in OH 2 there isn’t a lock around the rule so you’ll have that one rule running three times in parallel.

All three instances of the rule make it past the test to see if GarageMovementTimer is null. Which ever one happens to be a few milliseconds ahead of the rest creates a timer and sets it to GarageMovementTimer. As mentioned, GarageMovementTimer is just a pointer to the actual Timer. Setting GarageMovementTimer to something else does not destroy the Timer it was pointing to. Now the second instance of the rule creates a Timer and sets it to GarageMovementTimer. This replaces the pointer but does not destroy the old Timer. Now you have two Timers running but GarageMovementTimer is only pointing to one of them. Repeat for the last instance of the Rule and now there are three Timers running and GarageMovementTimer only points to the last one.

That is how you can end up with three timers at the same time.

The main reason I went through all that is to explain that this would not be a problem in OH 3. In OH3 there is a lock in place that prevents the same rule from having more than one instance running at a given time. In that case what would happen is the first instance of the rule will run to completion and the second two would be queued up. Thus, by the time the second and third instance runs GarageMovementTimer will not be null and a new Timer will not be created.

It’s an important distinction between OH 2 and OH 3 and one I’m not certain even the maintainers understand the import when it comes to creating manageable rules. IMO this new behavior is one of the most important improvements made to OH 3 right up there with MainUI.

Thanks both.
I did see that OH3 has a lock to prevent concurrent running of a rule and it also has the Timer.isActive method which would have been useful.

But I’m a bit chicken about doing the upgrade and will want a bit of time to take it on. Last time I did a major upgrade the lights, curtains and electric gate stopped working. I was not popular :slight_smile:

For now I’ll explore using the Group based solution.

I wonder if we’ve lost a little flexibility by making rules serialised in OH3

Thanks both again for the clear explanations and help

Well, given that the rules running in parallel were not really thread safe anyway I don’t see anything but that the situation is improved. For those rare use cases (if there are any) where one might want to be doing something from the same rule in parallel, Timers can be used. In the half a year that OH 3 has come out I’ve not seen anyone complain or look for such a solution. But in all the years between OH 1.6 and 2.5.12 I’ve spent hundreds of hours helping people with problems caused by the fact that the rules run in parallel. As far as I’ve seen, it’s only ever been a problem and has never been an advantage.

There are really three ways to manage upgrades:

  1. Never upgrade ever. If it ain’t broke don’t fix it. Of course at some point things outside of your setup (e.g. an API is changed or shut down) may change and an upgrade will be forced. The advantage is as long as you can get away with it, the system will be very stable. Disadvantage is you miss out on improvements, new features, security and performance fixes, etc.

  2. Upgrade rarely and set aside a weekend+ to deal with all the problems. The longer time between upgrades the more changes have accumulated and the more opportunities for breakage. And because there have been so many changes, it’s harder to identify the root cause of the breakage. The advantage is you can schedule your downtime ahead of time and your system should be pretty stable between upgrades. The disadvantage is the upgrade will likely be a lot of work and require a significant down time.

  3. Upgrade early and often. As soon as a new version is available upgrade to it. For this approach I recommend following at least the milestone releases. The advantage is the number of changes is relatively small so the chances of breakage are small. When something does break it’s easier to figure out why because there are fewer things that have changed prior to the break. And because it’s easier to figure out what broke, the down time should be smaller. At a minimum the down time will be spread out over time instead of concentrated in one weekend. The disadvantage is it’s harder to schedule for outages.

Personally, I recommend either 1 or 3. If you are afraid of upgrades, don’t upgrade anything ever. If you do want to keep up with upgrades, do so early and often. 2 is going to set you up for a world of pain and pressure every time.

NOTE: a backup and rollback procedure is a must for 2 or 3. If something does break and you can’t fix it right now, you need a fast and reliable way to roll back to the previous version until you have time to actually figure out what is wrong.

Personally I run my production system off of the SNAPSHOTS of OH and I upgrade all software on all of my machines a couple times a week at least (I’ve Ansible playbooks that let me run one command and updates all my machines). So far about once a month I’ll need to spend 15 minutes researching and fixing some problem caused by an upgrade of something. Several times I’ve discovered bugs and have been able to give back by filing issues. I wouldn’t recommend most users run the snapshots but I mention this because even on the snapshots OH is very stable.

tl;dr the longer you wait to upgrade, the more work it’s going to be.