Timer Binding discussion

Hey all!
While discussing a better interaction with timers in this thread I got the feeling that timers should be more integrated and powerful in OpenHAB as a lot of people use them in their automation setup, be it rules or sitemap elements:

  1. activate or cancel a timer
  • set or reset the timer duration/deadline
  • execute some logic upon “timer ended”, “every minute if active” and so on
  • get information about the timer element, like active, duration, deadline,…

Currently the whole thing is typically done in script but with the “OpenHAB approach” in mind, I think a timer should (also) be available as a a Binding. Timers in rules simulate events with actions and properties and therefore are like nested rules in rules, which I guess is not desired by design. A timer binding would provide items to interact with the timer in a “thing” way.

A rule would activate the timer through a Switch item (1), set duration or end time through a Number or DateTime item (2) and new rules would trigger on certain events (3) and contain the execution logic for these. A set of additional item posibilities would then be able to provide all kind of useful information for both rules and sitemaps (4).

Example:

DateTime   Timer_End_Time      {timer="id=timer1, property=end"}
Number     Timer_Remainder     {timer="id=timer1, property=end"}
Switch     Timer_Active        {timer="id=timer1, property=activated, state=continuous"}
Switch     Timer_Minute_Events {timer="id=timer1, property=activated, state=minute"}

What are your thoughts on this topic? Is the idea worth to pursue further?

The only thing that the current implementation of Timers do not support of your list is #4 and the “every minute if active” part of #3 though that is easy enough to implement in existing timers or using a Time cron trigger on a rule. So I really see this as being a way to get and display the current state of a Timer on the sitemap (e.g. a countdown). And I’ll be honest (and you probably won’t be surprised to learn given my other postings) I do not find #4 to be all that compelling of a use case.

I can see several reasons why I would not want the Timers to be a binding:

  • When you create a Timer now the code that gets executed inherits the context of the rule in which it was created. What that means is the Timer will have access to all the variables, Items, and any other state that exists at the time the Timer was created. If you move Timers to a an Item you lose all that context. So a mechanism needs to be created to pass an arbitrary number of vars and vals to the Timer.
  • When you create a Timer now, the logic (i.e. lines of codes) are located alongside the logic in the Rule that created it. If one moves Timers to an Item the logic will need to move to logic to another Rule and therefore isolated from the logic that depends on it.
  • A Timer seems to be a Rules thing, not an Item or Binding thing. A Timer is created in response to a Rule, not some external entity. Given that, a Timer Binding would be fairly unique in that it does not interact with any external entity which is probably why it is currently implemented as an Action.
  • I do not see it as impossible to add #4 to the current implementation of Timers and for all I know it is already implemented or on the roadmap for OH 2. This is certainly worth bringing up on the Eclipse SmartHome project which would be where these sort of change would be implemented.
  • There is some ambiguity as to what the expected behavior of a Timer Item should be when using restoreOnStartup. Does it continue to count down? Does it check to see if the time has passed and if it has does it trigger or does it just go away?
  • In general if there is a way to do something in Rules or using an existing Binding I find that far preferable to implementing a new Binding. We already have so many Bindings and it is already a problem to maintain them all that adding new ones can, in my opinion, cause as many problems as it solves.

I can also see some benefits:

  • I prefer to store state in Items as much as possible and currently there is no way to store a Timer in Items.
  • Ignoring the issues I discussed above, I like the idea of a Timer being restored on startup and the ability to get the Timer out of a global var.
  • Some people want to be able to see how much time is left on a Timer. While I put that in the category of putting stuff on the UI because you can rather than because it is truly useful which goes against my HA philosophy, that HA should just work and if you have to resort to a UI it is an HA failure. But I’m willing to posit this is useful to those who do not share my philosophy. However, I believe there are other ways to get to this without resorting to a new binding.

Alternative approaches:

The missing piece is #4 and I can see some alternative ways to get to #4 without a new binding.

  • Add the ability to get when a Timer is scheduled to trigger to the Timer class. You can then do the math and populate a countdown Item based on that. This would probably be the least amount of work and the least disruptive of the solutions that involve actual changes to OH.
  • Create a countdown Item which is not necessarily connected to a timer. This Item will simply decrement itself every second when it is set. This can be simply implemented in a Rule (see below).
  • Perhaps when OH 2’s new Rules DSL is released which supports reusable code someone can implement this in reusable Rules logic.
  • Use one of the approaches already posted in the forum that implements this in the timer itself.

Here is how I would do it using a countdown Item:

Item:

Number MinutesRemainingOnTimer

Rules:

var myTimer = null

rule "Set timer"
when
    // whatever
then
    myTimer = createTimer(now.plusMinutes(5), [| //stuff ])
    MinutesRemainingOnTimer.postUpdate(5)
end

rule "Countdown"
when
    Time cron "0 0 * * * ?"
then
    var Number remain = MinutesRemainingOnTimer.state as DecimalType
    if((myTimer == null || myTimer.hasTerminated) && remain != 0) {
        MinutesRemainingOnTimer.postUpdate(0)
    }
    MinutesRemainingOnTimer.postUpdate(remain - 1)
end

Hey Rich, thanks for the extensive answer! I was really interested in the opinion of the more experienced users/developers.

My proposal was not just aimed at #4. While this was (as you know) my original goal, one can of course solve it like in your example. Actually I build something similar and more complete on Saturday and wanted to post it as a conclusion for others to find in the referenced thread in the next few days. My true intend was aimed at the design break with timers, just being an object in the script world/part of OH.

While I can follow your reasoning, the warning of unnecessary bindings, and the possibilities to enhance the Timer with OH2, there is one thing I can not really agree with - “A Timer seems to be a Rules thing, not an Item or Binding thing”.

A Timer is an (object/thing/whatever) with properties, logic, interaction possibility and triggers. With that it seems to be more than just a simple variable, method, condition or loop, interacting with items as we normally see in rule/script logic. A Timer seems to be above one rule, let’s take your mwe as an example:

rule "Set timer"
when
    // whatever event trigger
then
    // some logic
    // some more logic
    myTimer = createTimer(now.plusMinutes(5) // when Time cron "s m+5 h..." 
    [ | //then
        //    some logic inside timer
        //    some more logic inside timer
        if (...) myTimer.rescheudule(..)
    ])
    // even more logic
    MinutesRemainingOnTimer.postUpdate(5)
    TimerEndTime = (now)
    if (...) myTimer.rescheudule(..)
    if (...) myTimer.cancel()
end

This is a construct I have a few times in my still growing OH setup. What’s happening is, inside of one rule, another rule get’s hacked into existence. There is a trigger for the rule and a rule logic thrown in between the rest of the code, there are even some further timer interactions. These two set’s of logic are not really depending on each other. One could for example switch a light on, the other one would turn it off again at some point in the future. From a systematic point of view, that’s not really different to the remote control on my table.
With the Timer/Timer properties and methods bound to Items and Rules acting on them, this example could get untangled easily.

In your first point you are talking about the benefit of being in the same context. I can see the convenience of that but would that not be valid for all rules. If we need to act on some variable data, OH provides Items to store it, e.g. MinutesRemainingOnTimer.

Having a Timer stored directly in an Item instead of being addressed through a binding is an interesting alternative solution. I am however not sure what information it would posses, what the element would show on the sitemap (you can show Numbers and Switches, how would a “Timer” look like?) or which events it would create. A Timer has more than one property and is therefore not really suited as Item, what do you think?

To conclude. In my humble opinion (!) integration of timed logic should be backed by information in items and executed by rules linked to the timed event. Just like with the Astro binding, this can be done manually and tediously by the end user or provided in a condensed way - be it a binding, OH2 reusable code or whatever :wink:

The big difference is that all other bindings interact with something external to OH. A Timer Binding would not. As such it seems to fit the model of an Action, which is its current implementation. That is my point.

You can use the same logic for

gMyGroup.members.forEach[i | //do stuff with i ]

It is the exact same construct. The stuff in “” is a lambda, a function that you can pass around as if it were an object. That is all you are doing with createTimer, passing it a lambda and telling it when to execute the lambda. Would you argue that forEach needs a binding? Passing lambdas around like this is a core part of the language so I don’t really see it as hacking a rule into existence.

Oh but they can and in all my cases they do. For example:

val GroupItem gr = // some assignment dynamically calculated
myTimer = createTimer(now.plusMinutes(1), [|
    gr.members.forEach[s | //do stuff with s
]

In the above I’m allowed to reference gr from inside my Timer because the Timer inherits the context from the rule. But because I don’t know a priori which Group gr actually is I have to have a way to pass it to the Timer body. I can see no way to do that through the existing Item based API. How would I get gr into the body of a Rule when the Timer Item triggers? What if I need more than one of the same Timer each of which needs a different gr?

If you are concerned about having the timer logic inline with the rule that created it, you can define the timer’s body separately. For example:

And if you don’t like defining the lambda inline, you can define it as a global var:

val Functions$Function0 timerBody = [ | 
    // timer body
]
...

    // In the rule
    myTimer = createTimer(now.plusMinutes(1), timerBody)

You can even solve the problem of passing gr into it:

val Functions$Function1 timerBody = [ GroupItem gr | 
    // timer body
]
...

    // In the rule
    myTimer = createTimer(now.plusMinutes(1), [ | timerBody.apply(gr) ])

Alternatively you can create a TimerEvent Item and create a very simple Timer that just sends an event to TimerEvent and put your code into a Rule:

rule "Test"
when
    // test trigger
then
    // do stuff
    myTimer = createTimer(now.plusMinutes(1), [| TimerEvent.sendCommand(ON) ]
    // do stuff
end

rule "TimerEvent"
when
    Item TimerEvent received command
then
    // timer body
end

So ultimately this part of your argument is one of coding style. You don’t like putting the Timer’s body logic inline with the rest of the code. That is fine and I presented two alternatives which lets you code in the style you prefer. But I will push back against changes that would prevent me from coding in my prefered style, gives me less functionality (see below), and no additional functionality simply because a coding style preference.

Here is an example of what I’m talking about.

rule "TimeOfDay changed"
when
        Item TimeOfDay received command
then
        // Disable overrides
        V_WhoCalled.sendCommand("TIMER")

        Thread::sleep(100) // give lastUpdate time to catch up

        // Turn off previous time of day lights
        val lastTod = PreviousTimeOfDay.state.toString
        val offGroupName = "g"+lastTod+"LightsOFF"
        logInfo("Lights", "Timer turning off " + offGroupName)
        val GroupItem offGroup = gTimerLights.members.filter[g|g.name == offGroupName].head as GroupItem
        offGroup.members.forEach[light |
                logInfo("Lights", "Timer turning OFF " + light.name)
                light.sendCommand(OFF)
        ]

        // Turn on current time of day lights
        val onGroupName = "g"+receivedCommand+"LightsON"
        logInfo("Lights", "Timer turning on " + onGroupName)
        val GroupItem onGroup = gTimerLights.members.filter[g|g.name == onGroupName].head as GroupItem
        onGroup.members.forEach[light |
                logInfo("Lights", "Timer turning ON " + light.name)
                light.sendCommand(ON)
        ]

        Thread::sleep(1000) // give all Override rules to finish running after all the switching above
        V_WhoCalled.sendCommand("MANUAL")
        gLightsOverride.members.forEach[o | o.sendCommand(OFF)]
end

In the rule above I do not know a priori which groups the rule needs to operate on. I construct the name of the group based on the TimeOfDay and then pull a reference to the Group by name out of another Group. If I had to create a timer here that is OK because the Timer will inherit my vars and vals so I don’t have to figure out how to communicate to another Rule somewhere which Groups to operate on when the Timer triggers. And this works flawlessly in the case where multiple instances of a Rule is executing at the same time because each rule has its own context.

For a real example (which I didn’t list above because its complicated), here is a Timer which I create inside a lambda and the lambda is called from multiple rules and multiple copies of this lambda can be executing at the same time:

val Functions$Function5 procChange = [ GroupItem gr, String name, Map<String, Timer> timers, SwitchItem reset, Map<String, Boolean> notified|
        logInfo("Network", "Detected the " + name + " controller state changed to " + if(gr.state==ON) "ON" else "OFF")

        // New state is OFF
        if(gr.state == OFF) {
                if(timers.get(gr.name) == null || timers.get(gr.name).hasTerminated){
                        logDebug("Network", "Creating controller offline timer for " + name)

                        timers.put(gr.name, createTimer(now.plusMinutes(1), [|

                // Still off
                                if(gr.state == OFF) {
                                        logInfo("Network", name + " is still offline after one minute")
                                        Notification_Proxy.postUpdate("The " + name + " controller is still offline after one minute, resetting")
                                        notified.put(gr.name, true)

                                        // reset the power
                                        try {
                                                reset.sendCommand(OFF)
                                                Thread::sleep(5000)
                                        } catch(InterruptedException e) {
                                                logWarn("Network", "Interrupted while sleeping to give switch time to power off")
                                        } finally {
                                                reset.sendCommand(ON)
                                        }

                                        // if it is the garage, wait five minutes then reset mqttReporter
                                        if(name == "Garage") {
                                                logWarn("Network", "Resetting mqttReporter in five minutes")
                                                createTimer(now.plusMinutes(5), [|
                                                        logWarn("Network", "Resetting mqttReporter on Garage")
                                                        S_N_GarageMqttReporter.sendCommand(ON)

                                                ])
                                        }
                                }

                                // came back while timer was asleep
                                else {
                                        logInfo("Network", name + " came back online before timer expired")
                                }

                                timers.put(gr.name, null)
                        ]))

                } else {
                        logInfo("Network", name + " was detected offline again but a timer is already set")
                }
        }

    // New state is ON
        else {
                if(timers.get(gr.name) != null) {
                        logInfo("Network", name + " is now ON, canceling timer")
                        timers.get(gr.name).cancel
                        timers.put(gr.name, null)
                }

                // If we sent a notification that it went offline, send one so we know it is back
                if(notified.get(gr.name)) {
                        Notification_Proxy.postUpdate(name + " is back online")
                        notified.put(gr.name, false)
                }
                else {
                        logInfo("Network", name + " is back online and there was no timer and no notification was sent: " + notified.get(gr.name))
                }
        }

]

I just do not see a way to support that use case using a Binding based Timer without out a whole lot of massive pain, even more massive than the current amount of pain to get a countdown on the sitemap. And I’m not even certain it is even possible which would break pretty much every Timer I use in my rules.

Now I’m really confused. The only way to interact with a Binding is through an Item. You do not interact directly with Bindings from within Rules (by design). You can interact with Actions, which is the current implementation. So perhaps you need to explain a little more about what you mean by implementing a Timer Binding. Because if you don’t put the Timer into an Item you are not implementing a Binding. If you really mean you want to implement an Action, the current implementation already is an Action and what we are really arguing is the functionality of the existing Action.

Given the use case I presented above, in my humble opinion :wink: the current approach:

  • follows the existing design and coding patterns of the Rules DSL (see forEach, filter, sortBy, etc)
  • is more in keeping with the current approach to separation of concerns (i.e. Items and external stuff is in Bindings, internal stuff and external stuff called directly from a rule is an Action)
  • supports the ability to write more dynamic rules code (see my example above) because the timer can inherit context so multiple instances of the same Timer can be executing at the same time with different contexts.

Whereas the proposed approach

  • Makes Timers a special case when it comes to how the Rules DSL normally handles this sort of thing.
  • Breaks several Timer use cases
  • Causes certain things which are simple to become very complicated because of the lack of inherited context (e.g. dealing with multiple copies of the timer at the same time with different contexts)
  • Largely addresses concerns with coding style for which there are alternative approaches that do not require changes to core functionality

I don’t see these as being equivalent. The Astro binding does a whole lot of calculations and such for you and (for all I know) reaches out to the internet for some information (I’ve not looked at the code) but ultimately it has a very limited and Item friendly set of interactions (set a DateTime Item, toggle a switch at a certain time). Timers on the other hand have a number of interactions (create, cancel, reschedule, query status) that are not Item friendly. There is not an interaction with a Timer that can be further abstracted without eliminating capability. So moving Timers to a Binding would either have to reproduce all the same interactions with Timers that exist today (i.e. just migrating the complexity to somewhere else) or eliminate functionality.

I’m sorry I didn’t make that clear. I know my way around coding. Lambda functions are not limited to Xtend, I am using a few in my setup and I saw your “Design Patterns” thread. A powerful scripting language sure gives you the possibilities to do everything. Still, with simple intentions in mind, every developer is thankful to have existing higher level core elements or libs to solve a problem for you. I don’t have to tell you that, OpenHAB’s pure existence is based on that idea.

You got the paragraph about the “Timer Item” wrong (I thought you were talking about a TimerType item data type) but that doesn’t really make a difference now. I wanted to discuss an alternative additional way to use Timers in a maybe less powerful but more user friendly way. Some may see it as a privilege to be able to handle OpenHAB but please take into consideration that not everyone is able to understand or does have the time to debug a complex algorithm. I built what I was talking about in the thread on Saturday and it’s doing its job just fine. However with all testing, fine tuning, makeup and debugging, it came up to around 2h of work. That’s too much for a rather simple task like that. The other day I saw a thread in a German forum from a firefighter with basic computer skills. He was asking if OH is something he should look into and one of the first responses was something along the line “yes it’s great but needs a lot of time and trial&error”. I don’t think that’s the vision here and Kai Kreuzer said something regarding this in one of his last talks.

It’s clear that you are not a big fan of the concept and that’s fine, I totally respect and understand that. Your arguments against an implementation as binding make sense. I still think my argument “A Timer should be referenced by OH Items rather than script-side variables/objects” is legit. Maybe the whole problem can be tackled much nicer with OH2 reusable code which I did not look into yet.

I think we can end this discussion here as you made your point. Thanks for your input.