Design Pattern: Expire Binding Based Timers

Problem Statement

One often needs to schedule some code to execute at some time in the future. The “usual” way to achieve this is by using a Timer which lets you create and schedule a lambda to execute at a given date and time. However, Timers have their drawbacks including:

  • the need to keep track of open Timers so they can be canceled at a later date, often requiring the use of HashMaps or some other data structure
  • rescheduling or creating a cascade of Timers is challenging (i.e. when a timer expires it kicks off another copy of itself or another Timer to do something else
  • a lot of boiler plate code is required to check for whether a timer is running, canceling it, rescheduling it, etc.; in some cases this boiler plate code will dominate the other “functional” code in a Rule


Create a special Switch Item bound to the Expire binding to drive your Timer. Configure the Expire binding with command=OFF so when the timer expires it generates an OFF command.

To start the Timer sendCommand(ON) or postUpdate(ON) to the Timer Item.

To cancel the Timer postUpdate(OFF) to the Timer Item.

Place the body of the code that you want to execute when the Timer expires in a rule triggered by received command OFF.

To check to see if the Timer is still running check to see if the Timer’s state is ON.

Simple Example


Switch MyTimer { expire="5m,command=OFF" }

JSR223 Python

from core.rules import rule
from core.triggers import when

@rule("Some Rule that starts MyTimer")
@when("<some tigger>")
def some_rule(event):
    # do some work

    # Timer running?
    if items["MyTimer"] == ON:
        # do stuff if Timer is actively running

    # cancel Timer
    events.postUpdate("MyTimer", OFF)

    # start Timer
    events.sendCommand("MyTimer", ON)

    # do some stuff

@rule("MyTimer expired")
@when("Item MyTimer received command OFF")
def mytimer_expired(event):
    # Timer body

Rules DSL

rule "Some Rule that Starts MyTimer"
    // some trigger
    // do some work

    // Timer running?
    if(MyTimer.state == ON) {
        // do stuff if Timer is actively running

    // cancel Timer

    // start Timer

    // do some stuff

rule "MyTimer expired"
    Item MyTimer received command OFF
    // Timer body

Comprehensive Example

See the Cascading Timers Design Pattern

Bonus, reschedule at startup

Unlike normal Timers, it is very easy to restart these Timers on an OH restart or Rules reload which is significantly more difficult with traditional Timers.


Create a Group and add all your Expire Binding Timer Items to this Group.

Group:Switch gResetExpire

JSR223 Python

from core.rules import rule
from core.triggers import when

@rule("Reset Expire Binding Timers", description="Sends an ON command to all members of gResetExpire if they were restoreOnStartup to ON", tags=["admin"])
@when("System started")
def reset_expire(event):"Restarting Expire binding Timers")
    for timer in ir.getItem("gResetExpire").members:
        events.sendCommand(timer, ON)

Rules DSL

rule "Reset Expire Binding Timers"
    System started
    gResetExpire.members.forEach[ timer | timer.sendCommand(timer.state) ]

Advantages and Limitations


  • The code involved when using the Expire binding is much simpler and easier to follow than Timers.
  • It is easy to determine whether a Timer was active when OH comes back through restoreOnStartup (I don’t think restoreOnStartup actually restarts the Expire binding though).
  • If all you are using the Timer for is to reset the state of an Item, you can just use the Expire binding on that Item and avoid rules entirely.


  • The amount of time applied to the Timer is hard coded. You cannot calculate the length of time in the future that the Timer needs to execute programmatically in a Rule
  • It requires installation of the Expire binding.
  • It requires the creation of more Items

Related Design Patterns

Trrying to turn a device off after a certain amount of time
Timer's and OH2 Restarts
Increasing light over a given time period
Rules stop firing after a short while, stack trace in logs
Action on DateTime item age
Grafana Image Charts
Switch/Group changed Rule not working as expected
[SOLVED] Timer not expiring
[SOLVED] Rule with a delay
Journey to JSR223 Python 5 of 9
Journey to JSR223 Python 2 of 9
Dresden Elektronik deCONZ Binding RaspBee
My Garadget + MQTT + OpenHAB Setup
[SOLVED] How do I debounce or antiflap notifications for many devices in a group?
triggeringItem problem (concurrency?)
Repeat some actions (and waiting) in rule
Programmatically check OH config files have loaded
Arguments to createTimer
[SOLVED] Strange rules behavior (including timers)
[SOLVED] Simple timer
Connection rule: disconnected for more than 15 seconds
Openhab filling up Memory and Swap
Timer sometimes does not expire
Presence rule with reed switch state & time
Finally not called -> deadlock
Roadmap to Happiness - What is missing in the core framework
Delay Alarm
[SOLVED] Problem restoring Switch item from influxdb
Advanced motion sensor rule
Displaying time since last update of an item
Item change state and wait time
[SOLVED] MQTT populating temperature values - no it is not!
Design Pattern : Expire Binding based Countdown timer
Only do action only if item state is OPEN for 10 seconds
[SOLVED] Trying to avoid "debounce" in a rule
[SOLVED] Help with Motion sensor goes too off too quick / canceling a timer
Rules to only fire when its :00 or :30
[SOLVED] Openhab 2 Contact Sensor Time Rule
Design Pattern: Sensor Aggregation
Design Pattern: Manual Trigger Detection
Design Pattern: Working with Groups in Rules
Cron expression in if statement
Design Pattern: Motion Sensor Timer
[SOLVED] Timer off delay for PowerView blinds
Lambda in rule fails with 'Error during the execution of rule '{RuleName}': null'
Help With Hue Rules
[SOLVED] Help to create a rule "turn on a lamp when motion is detected but only after sunset until sunrise"
Stop timer and reset to null. Structure of rule okay?
Is it possible to force a LUX sensor to update?
Cancel timer doesn`t work
Cannot get rule to Cancel Timer
Yet another WakeUp Light Rule
Rule, that check if last update is older than 5 Minutes
[SOLVED] Group rule stopped triggering. (Snapshot #1220 and back a week or so)
Why is everything in JSON format in my OpenHAB installation
How to wait in a rule?
Rule optimization: Window OPEN reminder
Simplifying Rule
Resetting timers
Timers and cancel 2 doorlocks
Making Decisions Based on Time of Day
Using Timer Class in Rule Making
ReentrantLock with Timer
Timed lights, Alerts to push notification?
Very simple rule executes slow
Send command with .map with MQTT
Taming a flapping sensor
Yet another Timer question
Eliminating lingering timers
Design Pattern: Proxy Item
Error when resetting timer
How to create a complex light status check rule?
Declaring Lambdas as Parameters in a Lambda (newest syntax)
Simple UI based Fridge/Freezer open alarm
Lost on rule using switch/case statements
Using 'expire' as a timer
Expire-bindings never expire
Control a dimmer with a switch
Humidity Rule vs Manual Switch
Bedroom motion sensor rule

This is great - thank you. Looks much more elegant than timers.


N.B. The actual syntax for an item using this binding is:

Switch MyTimer { expire="5m,command=OFF" }   //the 'expire=' is OUTSIDE the quotes
1 Like

Thanks. It’s so ready to miss stops like that when typing coffee on the phone.

1 Like

is there a reason why this one won’t work?

Switch MyTimer { expire="5m,command=ON" }

…and by the way. if it is a Proxy Item (like here), what’s the difference between expire="5m,command=ON" and expire="5m,state=ON"

Very little in this case, but it might or might not trigger other rules depending upon what they trigger on.

If you also had autoupdate=false, it would behave differently.

If you had a ‘real’ binding linkage(s), command would trigger that/them but state would not.

This does allow you to set up subtle controls

1 Like

I don’t see any reason why that won’t work but would need a lot more context to say so definitively. I’ve nothing to add to rossko57’s reply. He hit the nail on the head. command=ON is the equivalent to MyItem.sendCommand(ON) and state=ON is the equivalent to MyItem.postUpdate(ON) which implies all the same behaviors and caveats one has with updates and commands from any other binding.

One thing that is hard to get across in a DP like this is that when it is appropriate to use state versus command versus nothing because it is very context dependent.

In the context of the examples above, we use an OFF command to indicate that the Timer has expired. So to set the Timer we sendCommand(ON) which starts the expire binding running. A postUpdate(ON) would work just as well. To cancel the timer we purposefully use postUpdate(OFF) because we don’t want to trigger the “MyTimer expired” Rule. When the expire binding triggers it sendCommand(OFF) which does trigger the “MyTimer expired” Rule and the code that needs to run when the timer expires runs.

If you change the command to ON from the expire binding like you propose, you will need to also swap everything in the rule. So where you see an ON you would need to change it to OFF and the same for OFF or else the code won’t work like a Timer.

1 Like

Thanks, I thought so. I had to restart and now it works…

Thanks for this informative explanation, I have few questions please:

  • To run code when timer expires, we can use (MyTimer received command OFF) or (MyTimer changed), so what is the difference ?

  • Also when using (MyItem changed from OFF to ON), initially it doesn’t work because the item changed from null to on, so how can resolve this ?

  • Which is better: to trigger code using (when clause of rule), or (if statement within the rule) ?

Depends on the context and what the Expire timer is configured to do.

In the above, the Expire Binding is configure to send an OFF command to the Item when it expires. So to trigger a Rule only when the timer expires you would use Item MyTimer received command OFF.

Item MyTimer changed will trigger anytime MyTimer changes for any reason. So the Rule will trigger when the timer is started and MyTimer changes to ON and it will trigger when MyTimer changes to OFF. Notice this will capture the change caused for any reason. So if you use call MyTimer.postUpdate(OFF) and MyTimer isn’t already OFF then the Rule will trigger if you use changed but it will not trigger if you use received command OFF.

This was a deliberate design choice. In this case we don’t want to trigger the Rule when the Item changes from NULL to ON.

If you do want to trigger the rule then you can use Item MyItem changed to ON or Item MyItem received command ON.

All of the different rule triggers exist for a reason and they all have their place. You need to make sure you understand exactly what events that your system generates and choose the appropriate rule trigger for that case.

The question doesn’t make sense. Your two options have two completely different purposes.

Rules are triggered by events. If any one of the events in the Trigger occurs, the Rule will be run. This is why there is no such thing as an and in rule triggers. No two events will ever take place at exactly the same time so it makes no sense to have an and in a Rule trigger.

Once you are in the rule you know one of the events took place where you might need to take some action. So here is where you have your if statements to check the states of one or more Items or variables to determine if you need to take an action and what action you need to take.

It’s not an either or. You need both.

Is it possible to pass a variable to the expire function?

Number ExpireMinutesOffice
Switch Light_FF_Office_Ceiling      "Office"           <light>    (FF_Office, Lights, G_PresenceSimulation)      {mqtt=">[mqttbroker:/bo19/sw005:command:ON:1],>[mqttbroker:/bo19/sw005:command:OFF:0],<[mqttbroker:/bo19/sw005_status:state:ON:1],<[mqttbroker:/bo19/sw005_status:state:OFF:0]", expire= ExpireMinutesOffice.state.toString() + "m,command=OFF"}
Setpoint item=ExpireMinutesOffice label="Light switch off [%.1f]" icon="time" minValue=0 maxValue=100 step=1

I am getting the following error message:
mismatched input 'ExpireMinutesOffice' expecting RULE_STRING

Any idea?

No. If you need variable times you must use a Timer in a Rule.

1 Like

Great Stuff!

Easily applied and understood thanks to your design pattern!

can I add this binding in the JSON file?
Whats the syntex, if yes?

“Stue_lys”: {
“class”: “org.eclipse.smarthome.core.items.ManagedItemProvider$PersistedItem”,
“value”: {
“groupNames”: [],
“itemType”: “Switch”,
“tags”: [],
“label”: “Lys i stuen”

No, you cannot. It’s a 1.x version binding so .items files must be used. In OH 3 some other mechanism will be created that will implement the Expire Binding feature.

Hello Rich,

Your post is very helpful. Thank you very much.

I am very new to Openhab, and was wondering if you could clarify what you meant by

So to trigger a Rule only when the timer expires you would use Item MyTimer received command OFF .

I have used your code snippet and would like to use that to create an alert when a door is left open. The first part of my code creates a ding-dong song (to keep my 3 year old from walking out by himself).

I have seen your other post on using groups to reduce the lines of code, but to be honest it’s over my head. And honestly, I’m still trying to figure out this whole OpenHab thing.

so in items, I have created the following switch
Switch FrontDoorOpen_Timer { expire="5m,command=OFF" }

And in my Rules folder, I have:

rule “front_door_open”
Item zwave_device_08f074d1_node6_sensor_door changed from CLOSED to OPEN
say(“front door”)

if(FrontDoor_Timer.state == ON) {
    // do stuff if Timer is actively running

// cancel Timer

// start Timer

// do some stuff


rule “Door Open timer expired”
Item FrontDoor_Timer received command OFF
// Timer body
say(“the front door has been left open for 5 minutes”)

The ding-dong works, but for the life of my I can’t figure out how to change this to only give the alert when the door remains open. Any help would be super appreciated!

You’d want to “cancel” the expire timer by setting the timing Item to OFF without using a command.
Make another rule, triggered by the door changing to closed, that issues
Don’t be afraid to have many small rules for each task.

Setting the timer Item to the same state as the “target action” cancels the expire effect; using postUpdate instead of sending a command avoid triggering your alerting rule.

I am on latest openhab snapshot and I get following error every few seconds in the logs:
[ERROR] [core.karaf.internal.FeatureInstaller] - Failed installing ‘openhab-binding-expire’

Within paperui the expire binding shows as installed but is not functioning (switch never expires)

If I remember correctly the expire binding was within the action addon section of the paperui and is now in the binding section. Could this error be related to this move?


I’m pretty positive that is not the case. Expire has always been a binding. It doesn’t work like an Action (i.e. you call it from a Rule) so it doesn’t belong as an Action.

First thing to try, assuming you already tried restarting OH is to Clear the Cache and try again.

OK then I have bad memories … old man you know :wink:

I just restarted OH and cleared the cache but I get the same error in the logfiles after installing Expire via paperui after the restart.