How I Use Rule Templates

Edit: Adjusted the sensor offline implementation and added Debounce.

It’s no secret that I’m a big fan of rule templates but I think that a number of users are not familiar with them or do not really know what one can do with them. This tutorial is showing how I use rule templates in my home system to illustrate:

  • how one can use the same rule template to instantiate multiple rules that solve multiple use cases
  • how to use multiple rule template rules to solve a single use case

Note, at least for a short while I intend to make this a living document as I move more and more of my rules to templates.

What Is a Rule Template?

The tl;dr is a rule template is a rule someone else has written and published to the marketplace. All you, as the end user needs to do is install and configure it. See Rules - Basic | openHAB for more details about what a rule template is and how to install and instantiate a rule based on one.

Why Would I use a Rule Template?

The best advantage is simple, you can get complicated and well tested behaviors with minimal code or sometimes even no code.

One Template, Many Uses: Open Reminder

This is a multipurpose rule template that can be used in all sorts of situations. It implements the following behaviors:

  • triggers when a member of a Group changes state
  • if the Item changed to or from a configured state (e.g. changed from CLOSED), set a timer
  • when the timer goes off, call a user created script
  • if configured, wait a configured amount of time and repeat the call to the user defined script periodically until the Item returns to/from the configured state
  • if configured, do not call the user created script between a pair of times of day; a do not disturb (DND) period. Calls that normally would occur during the DND are delayed to the end of the DND period, unless the Item returns to/from the configured state

What can one do with such behaviors?

  • alert when a door is left open for too long and repeat the alert
  • alert when a door is left open for too long and repeat the alert, but only at night
  • turn off a light when a motion sensor stops triggering for five minutes
  • alert when a service goes offline
  • alert when a sensor doesn’t report for a long period of time

Open Doors

I’ve a small number of door sensors and I want to get an alert if they are left open for too long. My requirements are as follows:

  • if the door remains open for X minutes generate an alert
  • if the door still remains open for another Y minutes, repeat the alert and keep repeating every Y minutes
  • do not alert if it’s between 11:00pm and 8:00am

To achieve this:

  1. Install Open Reminder
  2. Add all the doors/windows to a Group: DoorStatus
  3. Create a rule that will be called when the Open Reminder rule needs to alert: door_open is the UID I’ll use here
  4. Instantiate a rule based on Open Reminder with the following configs
  • dndEnd: 08:00
  • defaultTimeout: PT15m
  • invert: false
  • groupTriggers: DoorsStatus
  • alertRuleUID: door_open
  • dndStart: 23:00
  • repeatPeriod: PT1h
  • timeoutMetadata: rem_time
  • alertState: CLOSED
  • reschedule: false

That will call open_door after the door remains open for 15 minutes and will remind every hour except between 8am and 11pm. However, one of my requirements is to have different alert times for each Item. For this add rem_time0 Item metadata to each door Item with that door’s timeout (e.g. I have rem_time="PT8h" for the back door since it often remains open most of the day.

The open_door rule is pretty simple.

configuration: {}
triggers: []
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: >-
        var {alerting} = require('rlk_personal');

        var logger = log('Open Door');


        var item = items.getItem(alertItem);

        if(currState == 'CLOSED') alerting.sendAlert(item.label + ' is now closed'); // should never be called

        else alerting.sendAlert(item.label + ' is open!');
    type: script.ScriptAction

The only code that had to be written is to send the alert. The rule template handles the rest.

Notice that the Item that generated the alert is passed to the rule as alertItem and the state the Item is in that caused the alert is currState.

Sensor Offline

I’ve some AirThings and Govee sensors whose readings are fed to OH using an MQTT bridge (I know there’s a binding but getting access to the BT inside a Docker container is a pain). Sometimes the bridge goes down or otherwise experiences problems and stops reporting without actually disconnecting from the MQTT broker so I can’t rely on the LWT message. So I want an alert when that happens.

  1. add expire to each sensor Item to set the Item to UNDEF if it doesn’t update to a value for a given amount of time. The amount of time will depend on how often the sensors report.
  2. add the sensors to a Group: Sensors
  3. create a Switch Item that is a member of the same sensor equipment with “_Status” after (e.g. BasementSensors_Status)
  4. create the rule that will be called when a sensor is determined to be offline: sensor_offline_alert
  5. instantiate a rule based on the Open Reminder template and configure
  • dndEnd: 08:00
  • defaultTimeout: PT5m
  • invert: true
  • groupTriggers: Sensors
  • alertRuleUID: sensor_offline_alert
  • dndStart: 22:00
  • repeatPeriod: “”
  • timeoutMetadata: rem_time
  • alertState: UNDEF
  • reschedule: false
  1. create the rule that will be called when a sensor is determined to be back online: ‘sensor _online_alert’
  2. instantiate a rule based on the Open Reminder template and configure
  • dndEnd: 08:00
  • reschedule: false
  • defaultTimeout: PT1m
  • invert: false
  • groupTriggers: Sensors
  • alertRuleUID: sensor-online-proc
  • dndStart: 22:00
  • timeoutMetadata: rem_time
  • repeatPeriod: “”
  • alertState: UNDEF

The sensor_offline_alert rule has a condition that checks to see if the status Item for the equipment is already OFF or not and only proceeds if the Item is ON. The action will set that Item to OFF indicating the sensors are offline.

configuration: {}
triggers: []
conditions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: >
        var { itemRegistry } = require('@runtime');

        var equipment = actions.Semantics.getEquipment(itemRegistry.getItem(alertItem)); // requires the Java Item, not JS Item

        var statusItem = items.getItem(equipment.name + '_Status');

        // Just went offline

        statusItem.state == 'ON';
    type: script.ScriptCondition
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: >-
        var { itemRegistry } = require('@runtime');

        var {alerting} = require('rlk_personal');

        var logger = log('Sensor Offline');


        var equipment = actions.Semantics.getEquipment(itemRegistry.getItem(alertItem)); // requires the Java Item, not JS Item

        var statusItem = items.getItem(equipment.name + '_Status').postUpdate('OFF');


        alerting.sendAlert(equipment.label + ' has stopped reporting and is likely offline');
    type: script.ScriptAction

The sensor_online_status rule is a mirror of the offline rule. They could probably be merged but this was simpler to implement for now.

configuration: {}
triggers: []
conditions:
  - inputs: {}
    id: "2"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: >
        var { itemRegistry } = require('@runtime');


        //console.log('Received an alert on ' + alertItem);


        var equipment = actions.Semantics.getEquipment(itemRegistry.getItem(alertItem)); // requires the Java Item, not JS Item

        var statusItem = items.getItem(equipment.name + '_Status');


        //console.log('received an online alert for equipment ' + equipment.name + ' and status Item ' + statusItem.name);


        // Just went online

        statusItem.state != 'ON'
    type: script.ScriptCondition
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: >-
        var { itemRegistry } = require('@runtime');

        var {alerting} = require('rlk_personal');

        var logger = log('Sensor Online');


        var equipment = actions.Semantics.getEquipment(itemRegistry.getItem(alertItem)); // requires the Java Item, not JS Item

        items.getItem(equipment.name + '_Status').postUpdate('ON');

        alerting.sendAlert(equipment.label + ' is now back online');
    type: script.ScriptAction

In this case, when a sensor Item remains UNDEF for five minutes send an alert except between 10pm and 8am. If an alert is scheduled between those times, reschedule it for 8am (if it’s still UNDEF at that time). The condition on sensor_offline_alert filters the alerts so only one alert per equipment is sent so even if a given sensor has a dozen Items that all go to UNDEF at the same time, only one alert will be sent.

This shows how you can add even more capabilities to a rule template using the conditions and code in the called rule without needing to implement the whole thing from scratch.

Service Offline

In this case I want an alert both when the service goes offline with a repeat once a day and again when it returns to online but only once in that case. So I instantiated two instances from the Open Reminder rule, one that matches the OFF state and the other to match the ON state. I’m able to reuse the same rule that gets called.

The offline rule configs:

  • dndEnd: 08:00
  • defaultTimeout: PT5m
  • invert: true
  • alertRuleUID: service_offline_proc
  • groupTriggers: ServiceStatuses
  • dndStart: 22:00
  • repeatPeriod: PT1h
  • timeoutMetadata: rem_time
  • alertState: OFF
  • reschedule: false

The online rule configs:

  • dndEnd: 08:00
  • defaultTimeout: PT1s
  • invert: true
  • alertRuleUID: service_offline_proc
  • groupTriggers: ServiceStatuses
  • dndStart: 22:00
  • repeatPeriod: “”
  • timeoutMetadata: rem_time
  • alertState: ON
  • reschedule: false

The custom rule that gets called.

configuration: {}
triggers: []
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: |
        var {alerting} = require('rlk_personal');
        var logger = log('Service Offline');

        var key = ruleUID+'_'alertItem;
        var service = items.getItem(alertItem).label;
        var msg = 

        if(currState == 'ON') {
          // Only alert if we previously alerted for OFFLINE
          if(cache.get(key)) {
            alerting.sendAlert(service + ' is back online');
          }
          cache.put(key, null);
        }
        else {
          alerting.sendAlert(service + ' has gone offline!');
          cache.put(key, true);
        }
    type: script.ScriptAction


The rule which gets called has a built in check to only alert when the Item changed back to ON if an alert was previously sent when it turned to OFF.

Dad Motion Alert

I’ve a motion sensor at my dad’s house. I want to get an alert if it doesn’t detect movement for a long time. This is a minimally invasive way for me to make sure he is up and moving around. This approach can also be used for standard motion sensor situations as well.

I have Tailscale set up to access the machine remotely and I use MQTT Event Bus Publication, MQTT Event Bus Subscription, and MQTT Online Status mostly because at the time I set this up, MQTT handled intermittent network interruptions better than the Remote openHAB add-on. I think it’s better now though so would probably switch to use that.

To achieve this I’m going to have to do some minor changes to the instantiated rule. But the changes are really minor.

  1. Instantiate the rule with reasonable settings.
  • dndEnd: 08:00
  • defaultTimeout: PT1m
  • invert: true
  • groupTriggers: vDad_LR_Motion
  • alertRuleUID: dad_motion_alerting
  • dndStart: 22:00
  • repeatPeriod: PT1m
  • timeoutMetadata: rem_time
  • alertState: ON
  • reschedule: true
  1. We’ve only the one motion sensor Item so we need to change the trigger on the instantiated rule to when Item vDad_LR_Motion received update ON. We only really care about ON because we don’t want the rule to trigger on other changes which will cause the timer to be cancelled.

That’s it. And the only reason step 2 is required is because we cannot use a property for the rule trigger type for some reason. But this is a great example for how it’s OK to modify the rule produced from a rule template if you need to.

Low Battery

This one uses a different rule template Threshold Alert. This will send an alert to me (except during the do not disturb period) when a battery level is too low.

The config is:

  • comparison: <
  • dnd_end: 08:00
  • limit: 8h
  • threshold: 25
  • script: battery_alert
  • dnd_start: 22:00
  • group: AllBatteries

If any member of AllBatteries is below 25%, call battery_alert which contains:

configuration: {}
triggers: []
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: >
        var {alerting} = require('rlk_personal');

        var logger = log('Low Battery');

        alerting.sendAlert('The following batteries are below 10%: ' + this.threshItemLabels, logger);
    type: script.ScriptAction

Humidity Alert

This one also uses Threshold Alert. I want an alert when the humidity gets too low.

  • comparison: <
  • dnd_end: 07:00
  • limit: 4h
  • threshold: 35
  • script: humidity_alert
  • dnd_start: 22:00
  • group: MinIndoorHumidity

Send an alert when any humidity sensor drops below 35%. The alert script is:

configuration: {}
triggers: []
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: |-
        var {alerting} = require('rlk_personal');
        var logger = log('Humidity Alert');
        var msg = 'Remember to fill the humidifiers. Minimum humidity is ' 
                  + items.getItem('MinIndoorHumidity').state 
                  + '. Low readings: ' 
                  + this.threshItemLabels;
        alerting.sendAlert(msg, logger);
    type: script.ScriptAction

I plan on using this rule template to turn on/off the humidifiers based on the humidity readings (they’ll have different higher thresholds than the alerting rule). But even I haven’t moved everything over to rule templates yet.

Using More than One Rule Template for One Use Case

Tracking Time

I have several times of day that I use to drive my home automation. Some are driven by the sun. Others are at a fixed time of the day. One time of day is driven by my alarm clock on my phone. To further complicate matters, I want the times to be different on weekends and holidays.

So I use a combination of Time Based State Machine and To Today.

The State Machine looks at sets of DateTime Items to determine when the various time of day states begin based on the day type, which is determined using Ephemerous. Therefore I’ve a set of Items for default days, a different set for weekends and yet another set for holidays. The Items that are driven by Astro are linked to the proper Channel. The Items with a static time are populated using DateTime List Item.

For those Items that are static times populated through MainUI, I use the To Today rule template to move those Items to today’s date every day.

For the alarm clock MORNING state, I have a separate rule that uses the Time is <item> rule trigger that simply posts the String “MORNING” to the TimeOfDay Item when it triggers, but only if it’s before sunrise (using a condition). This shows how a rule template can be augmented with additional rules outside what the template does. An alternative approach would have been a rule to add the alarm clock time to one or more of the DateTime Item sets for the given day types. But this way the MORNING state only occurs when I actually have to get up, regardless of the type of day.

So, to set all this up I just needed to install and configure two rule templates, instantiate them, add some Items with the proper metadata, and created a single “Alarm Clock” rule.

Time Based State Machine Config:

  • create a Group to hold the time of day DateTime Items
  • create a String Item to hold the time of day state
  • create a set of DateTimeItems for each type of day (holiday, weekend, default)
    • link those Items to Channels that are driven by bindings (e.g. Astro)
    • add them to the TimeOfDay Group

That’s the most complicated part by far.

  • install the Time of Day State Machine and To Today rule templates
  • instantiate a rule based on the state machine template
    • namespace: what ever you chose when adding the metadata to the Items
    • timesOfDayGrp: Group that holds all the time of day DateTimeItems
    • timeOfDay: Item that holds the current time of day state

To Today Config

  • instantiate a rule based on the to today template
    • itemTag: the tag you added to the static time Items
    • add the time of day Item metadata for each Item (see the instructions on the rule template)
    • set the “default list item widget” and/or the “default standalone widget” to the DateTime widgets installed from the marketplace for Items that use a static time
    • set the to today Item tag for each Item that represents the static time
      Even better.

Alarm Clock

  • create an AlarmClock Item and configure the Android app to populate it with the next upcoming alarm clock.
  • create an Alarm Clock rule
configuration: {}
triggers:
  - id: "2"
    configuration:
      itemName: AlarmClock
    type: core.ItemCommandTrigger
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: >-
        var logger = log('Alarm');


        var hour = time.ZonedDateTime.now().hour();

        var currToD = items.getItem('TimeOfDay').state;


        logger.debug('Current ToD = {} Current Hour = {}', currToD, hour);

        if(currToD == 'NIGHT' || hour < 5) {
          logger.info("The alarm went off and it's night time, turning on the lights for ten minutes");
          items.getItem('TOD_Lights_ON_MORNING').sendCommand('ON');
          actions.ScriptExecution.createTimer(time.ZonedDateTime.now().plusMinutes(5), function() {
            items.getItem('TOD_Lights_ON_MORNING').sendCommand('OFF');
          });
        }

        else if(currToD == 'BED'){
          logger.info('Good morning!');
          items.getItem('TimeOfDay').sendCommand('MORNING');
        }

        else {
          logger.warn("The alarm went off but it's not BED or NIGHT")
        }
    type: script.ScriptAction

I wrote this rule when I had a puppy that we were potty training. So if my alarm went off before 5 AM I didn’t want to transition to MORNING, I just wanted to turn on a few lights long enough to take the pup out to potty.

If the current time of day is BED, we transition to MORNING. Otherwise we ignore the alarm because it’s already after sunrise.

Now I have a pretty complicated time of day state machine that changes based on the type of day with some pretty complicated handling for the alarm clock all for a few lines of code and some Item configs.

Suites of Rule Templates

Sometimes there will be more than one rule template that are designed to work together. As of right now the Marketplace doesn’t allow the publishing of multiple templates to the same post so each have to be separately installed.

MQTT Event Bus

This consists of three rule templates:

  1. MQTT Online Status : Publishes an ONLINE message to a LWT topic to represent the online status of OH itself.
  2. MQTT Event Bus Publication : Publishes the updates and/or commands to members of a Group to a well defined MQTT topic.
  3. MQTT Event Bus Subscription : Subscribes to the well defined topics the Publish rule sends messages to, and updates/commands the Items that correspond with the topic.

I’m not going to go into the config here. See Marketplace MQTT Event Bus for details.

Thing Status

Thing Status Reporting is a rule template that lets one monitor Thing states and call a rule with a list of those Items that do not match a given state (e.g. those that are not ONLINE). The config:

  • comparison: “!=”
  • trigger: 0/30 * * * * ? *
  • script: thing_status_handler
  • status: ONLINE

And the called rule:

configuration: {}
triggers: []
conditions: []
actions:
  - inputs: {}
    id: "1"
    configuration:
      type: application/javascript;version=ECMAScript-2021
      script: >-
        var logger = log('Thing Status Processor');


        logger.debug("There are " + this.things.length + " things that are not ONLINE");
    type: script.ScriptAction

Surprising Uses

Just because a given rule template is intended to solve one problem, sometimes you can convert another problem into something that can be implemented too. For example, Debounce - #8 by boehan is intended to solve a problem where an Item may bounce between states for a short period of time before settling into a final state. The name comes from electronics when pressing a physical button may cause multiple press and release events as the spring in the button bounces. But it can be used in other cases too like a sensor that occasionally goes nuts when it gets cold or something like that.

The surprising use here is that it can be used to “debounce” presence detection. In this case, when I’m detected as home I immediately want to set the presence status to home. However, if I leave home, I want to wait a few minutes before I consider myself away, just in case I’m near the back of the property or went to get the mail or something like that.

  1. Create an Item linked to what ever you use to detect your presence (e.g. Network binding pinging you phone). Name the Item with “Raw” or some other tag to indicate this is the un-debounced Item.
  2. Create an proxy Item without the “Raw” in the name. This Item will get the debounced state.
  3. Create a Debounce Group and add the “Raw” Item to it as a member.
  4. Set the Item metadata for the raw Item per the instructions in the Rule template so it only debounces OFF for five minutes.
  5. Instantiate an rule based on the Debounce with debounce Group as the sole configuration parameter.

Now the Item created in step 2 will represent your debounced presence meaning it will be ON when you are detected present and only go to OFF if you are away for more than five minutes.

Conclusion

My hope is that this can serve as inspiration for the sorts of use cases that can be constructed using individual rule templates, multiple templates, or a combination of custom rules and rule templates. Many rule templates are not intended to be stand alone and many others are intended to be generic so they can solve many different use cases.

In nothing else, hopefully this shows the amount of coding that can be saved through the use of rule templates.

4 Likes