Threshold Alert and Open Reminder [4.0.0.0;4.1.0.0)

Note: This is a complete rewrite of the previous Threshold Alert and Open Reminder rules. In addition to merging the behaviors, the behaviors are slight different from both of these predecessors. Please read carefully.

See Threshold Alert and Open Reminder [4.0.0.0;4.1.0.0) - #2 by rlkoshak for configuration examples. This is template can do a lot of different things depending on the parameters configured.

Problem Statement
Frequently one wants to do something when one or more Items exceed or fall below a certain threshold, matches a given state for a given amount of time, or fails to change state (or update) for a certain amount of time.

What the Rule Does
This rule is triggered by changes to the members of a Group. The Group should have members that are all of the same type of Item. At this time it does not support a mix of compatible Item types (e.g. Switches, Dimmers and Color Items) except in the case where it’s looking for NULL or UNDEF as the alerting state.

When the rule triggers, it compares the state of the Item with a threshold state using a user selected comparison.

  • If the comparison evaluates to true (or false if invert is enabled), an optional user defined rule is called.
  • After a user defined delay an optional alert rule is called.
  • When the Item leaves this alerting state, the rule will optionally call an end alert rule, but only if the threshold alert rule was called.
  • Any call to any rule that occurs during a user defined Do Not Disturb (DND) period are suppressed to the end of that period.
  • Finally, an optional reminder period can be defined to call the alert rule repeatedly while the Item continues to meet the threshold comparison. These reminders also follow the do not disturb period.

The alert rule and end alert rule will receive these parameters passed to them when called:

Variable Purpose
alertItem name of the Item that generated the alert
alertState current state of the Item
isAlerting boolean, when true the Item meets the threshold comparison, when false the Item exited the threshold state
isInitialAlert boolean, when true the Item just now met the threshold comparison, when `false, the Item has been in this state for awhile.
threshItems JavaScript Array of JavaScript Item Objects whose state meet the alerting criteria
threshItemLabels JavaScript Array of the Item labels whose state meet the alerting criteria
nullItems JavaScript Array of JavaScript Item Objects whose state is NULL or UNDEF
nullItemLabels JavaScript Array of the Item labels whose state is NULL or UNDEF

To access these attributes in your called rule varies from language to language. Blockly has a context block. JS Scripting ECMAScript 2021 injects them as variables (e.g. alertItem). Nashorn JS Scripting (and other JSR223 rules engines) makes them available via context.getAttribute('alertItem');.

Rule Properties

Property Format Required Default Purpose
Triggering Group Group Item X Changes to it’s members cause the rule to run.
Threshold State String X The state to compare the members of Triggering Group to using the Comparison Operator. If it’s parsable as a number, a number comparison will be made. If it’s a number with units, a Quantity comparison will be made. UnDefType, NULL and UNDEF are all normalized to UnDefType and treated as the same state. In all other cases a string comparison will be made.
Comparison Operator X The comparison operator to use (e.g. ==)
Invert Comparison Boolean false Invert the result of the comparison.
Reschedule Boolean false When true, if an alert event occurs when an alerting timer already exists, the timer will be rescheduled instead of ignoring the new event. The different is whether the alert occurs based on the first alerting event or the most recent alerting event.
Hysteresis String '' Optional parameter which, when populated, will wait until the Item’s state changes at least that amount from the threshold before disabling the alert. Only applied for states which are Numbers or QuantityTypes. If the comparison is >, the Item will need to drop at least this amount below the threshold before alerting ends, and vice versa for <. If == the value needs to move above or below the threshold by this amount. It doesn’t make sense to use with !=.
Alert Delay ISO8601 duration string (e.g. PT15M) '' How long to wait after the Item meets the threshold comparison before calling the Alert Rule. Leave blank to call the rule immediately.
Reminder Period ISO8601 duration string '' How long to wait after the first call to the alert rule before calling it again as a reminder. The rule will continue to be recalled by this period until the Item no longer meets the threshold comparison.
Metadata Namesapce String thresholdAlert An optional metadata namespace where a per Item override for Alert Delan and Reminder Period can be defined (see below).
Alert Rule Rule UID X Rule called when the Item meets the threshold comparison and repeatedly thereafter if Reminder Period is defined.
End Alert Rule Rule UID '' Rule called when the Item first exits the threshold comparison. This can be the same as Alert Rule or Initial Alert Rule.
Initial Alert Rule Rule UID '' Rule called when the Item first meets the threshold comparison. This can be the same as Alert Rule or the End Alert Rule.
Do Not Disturb Start Time HH:MM '' The start time for the optional DND. If this is after the End DND time, the period will span midnight.
Do Not Disturb End Time HH:MM '' The end time for the optional DND. The most recent call to either Alert Rule or End Alert Rule will be delated until this time.
Gatekeeper Delay Number 0 The minimum amount of time between calls to the alert and end alert rules before the next call will be allowed to occur. If you experience ā€œMulti-threadedā€ exceptions, increase this time until they stop. The time is in milliseconds.
Rate Limit ISO8601 Duration String '' The minimum amount of time between calls to the alert rule before the next call will be allowed to occur. Unlike Gatekeeper Delay, the rate limit will simply drop any events that occur rather than queueing them up and working them off in sequence. If used with Gatekeeper Delay, Rate Limit should be significantly longer.

Any or all of the above properties except for Triggering Group and Metadata Namespace can be defined at the Item level using Item metadata with the addition of one extra parameter. Instead of threshold, a thresholdItem can be defined which will cause the state of that Item to be used as the threshold instead of the statically defined threshold. In the UI a full set of metadata should look like the following

value: " "
config:
  thresholdItem: OfficeHumidity_Setpoint
  operator: <
  invert: false
  reschedule: false
  hysteresis: 4 %
  alertDelay: PT1M
  remPeriod: PT15M
  alertRuleID: new_humidity_proc
  endRuleID: new_humidity_proc
  initAlertRuleID: new_humidity_proc
  dndStart: 22:01
  dndEnd: 08:02
  gatekeeperDelay: 10
  rateLimit: PT30M

All of these metadata are optional. If missing, the rule’s setting will be used for that Item. dndStart and dndEnd must both be present or both be absent and represent a time using either 24 hour (e.g. 22:00) or 12 hour (e.g. 10:00 PM) format. gatekeeperDelay must be a positive integer.

Running the rule manually will cause the rule to verify the configuration of the rule parameters and the Item metadata configurations. Watch the logs for warnings and errors.

Use Cases

  • Generate an alert and reminders when a door is left open for too long.
  • Turn on a humidifier when the humidity gets too low, turn it back off when rises back to the threshold.
  • Take remedial action (e.g. restart it) when a service goes offline.
  • Alert when a sensor’s state remains UNDEF or NULL for too long.
  • Control a light based on a motion sensor.

Requirements

Language: JS Scripting ECMAScript 2021

Dependencies:

  • A script to call when an Item meets the threshold
  • A Group containing all the Items to trigger this rule. All members of the Group must be the same type.
  • openhab-js 4.1+
  • openhab_rules_tools 2.0.2+

TODO:

  • DateTime comparisons (i.e. a DateTime Item’s state is too long ago)
  • Restart alerting on OH startup
  • Make initAlertRule follow DND
  • Support using separate DateTime Items for DND periods (so we can use Astro based times)
  • Option to operate at the semantic model equipment level (alert is called on Equipment, not point Items)
  • Ignore events during DND instead of moving them to the end of DND
  • Figure out why I’m still getting Multi-threaded exceptions even with a half second gatekeeper (mostly during startup)
  • Figure out Failed to execute rule 'sensor-status-detection': Fail to execute action: 1 even though the rule appears to run
  • Support standard motion sensor timer use cases (e.g. turning on/off lights).
  • Normalize UnDefType so it matches both NULL and UNDEF
  • Option to call a rule when alert timer is created, when it expires, reminder, and when Item is not alerting any more
  • bring back the summary list of Items that meet the threshold
  • bring back the rate limit
  • hysteresis
  • Support for Thing status events See Thing Status Reporting [4.0.0.0;4.1.0.0) , It’s going to be too much to add support for that here.
  • Generate an alert if an Item doesn’t change for too long
  • Allow most of the properties to be overridden through Item metadata
  • Provide override to threshold in another Item (identified via metadata) for use cases like controlling a device that is controlled by a setpoint Item
  • Address multithreaded exception that occurs during startup if the rule gets pounded. It’s less than it used to be but still occurs occasionally for me. See Delay Start [4.0.0.0;4.1.0.0), note that at least on OH 4.0 SNAPSHOT #3368 the error does not occur for me.
  • Catch exception in cases where called alert or end alert rule is disabled.

Changelog

Version 0.9

  • give each Item it’s own logger to make tracing logs easier
  • schedule initAlertTimer to the end of DND like the alertTimer and endAlertTimer

Version 0.8

  • normalize UNDEF and NULL to UnDefType so both are treated as the same
  • added an initial alert rule called when the Item first enters the alerting state to make motion sensor light type use cases easier to implement

Version 0.7

  • refresh the Item’s settings inside the looping timer so reminders always run with the latest Item metadata settings
  • cancel any running timers in int()

Version 0.6

  • added a reschedule parameter which causes the rule to call the alert rule alert duration after the most recent alerting event instead of after the first alerting event. This implements alerting when an Item doesn’t change for too long a time, mostion sensor timers, etc…

Version 0.5

  • added ability to define another Item whose state will be used as the threshold (thresholdItem)
  • any and all rule properties can be overridden through Item metadata
  • todo: update the docs above to cover the metadata and throughly test the config checking

Version 0.4

  • added rate limiting option

Version 0.3

  • catch and log exception if the alerting rule no longer exists or is disabled
  • pass a list of all the Items and their labels that are null/undef and those that are alerting to have parity with the old Threshold Alert rule to ease migration.

Version 0.2

  • added support for hysteresis

Version 0.1

  • initial release

Resources

https://raw.githubusercontent.com/rkoshak/openhab-rules-tools/main/rule-templates/thresholdAlert/newThresholdAlert.yaml

2 Likes

Humidity Alerts and Control

Where I live it’s dry and I’ve a number of humidifiers scattered throughout the house. When the tank runs out the humidity drops pretty fast and I want to get a notification.

Create the rule that will generate the notification. This can be a script. Mine looks like this:

var {alerting} = require('rlk_personal');
var logger = log('Humidity Alert');
var location = items[actions.Semantics.getLocation(items[alertItem]).name];

var msg = 'The humidity in ' + location.label + ' is low (' + alertState + ')! Remember to refill the humidifier.'
alerting.sendAlert(msg, logger);

logger.info('There are ' + nullItemLabels.length + ' null Items and ' + threshItemLabels.length + ' Items total in the alerting state');

alerting is a personal library that change how I’m alerted based on the time of day and other factors.

I instantiate an instance of this rule template with the following properties:

Property Value What it does
Triggering Group MinIndoorHumidity Group that holds the Items with the humidity sensor readings
Threshold State 35 % Setpoint for the rule to alert
Comparison Operator < Comparison the Item’s current state is made against the Threshold State. If the comparison evaluates to true, the Item is considered to be in the alerting state
Invert Comparison false If true it would negate the Comparison Operator (e.g. the Item would be considered alerting if it’s >= the Threshold State instead of <).
Reschedule false The alert delay applies from the time where the Item first enters an alerting state.
Hysteresis 3 % After calling the alert Item, wait for the humidity to return to 38 % (35 % + 3%) before considering the Item no longer alerting. In this case, this helps keep the Item from bouncing between the threshold and sending lots of alerts.
Alert Delay '' Don’t wait and alert immediately.
Reminder Period PT8H If the humidity remains below 35 %, repeat the alert every 8 hours.
Metadata Namespace humidityAlert
Alert Rule new_humidity_proc The ID of the rule/script shown above.
End Alert Rule '' Do nothing when the humidity is no longer alerting.
Initial Alert Rule '' Do nothing when first entering the alerting state.
Do Not Disturb Start Time 22:00 Don’t call the alert or end alert rule after 10PM.
Do Not Disturb End Time 08:00 Allow the alert or end alert rule to be called again. If a call to one of the rules was scheduled between the start and end time and the Item is still in that alerting/end alerting state, the rule will be called at this time. That way, even if the humidifiers run out at night, I’ll still get notified.
Gatekeeper Delay 0 Calls to the alerting/end alerting rules will happen no faster than this delay (in milliseconds). This can help if the rule is being called rapidly or it interacts with devices that cannot process commands to fast. In this case we have no delay.
Rate Limit '' If this had a value, this defines a period during which new alerts are ignored after calling the alerting rule. Unlike gatekeeper, it drops the events instead of queueing them to deliver later.

Note that most of the above are the defaults. So what does this rule do? It calls new_humidity_proc immediately when a humidity Item drops below 35%. If it is between 10PM and 8AM the call to the rule is delayed until 8AM. If the Item remains below 35% for an additional 8 hours, the rule is called again. The Item will continue to be considered alerting until it reaches 38% and then won’t become alerting again until it falls below 35% again.

However, there is one sensor where I want the threshold to be 40%. So I’ve added the following humidityAlert metadata for that Item to:

value: " "
config:
  threshold: 40 %

Open Door Reminder

I want to get an alert when a door is left open for too long. However, each door has a different amount of time before I want to get the alert.

I created a rule to process the alert.

var {alerting} = require('rlk_personal');
var logger = log('Open Door');

var item = items.getItem[alertItem];
if(isAlerting) alerting.sendAlert(item.label + ' is now closed', logger);
else alerting.sendAlert(item.label +' has been open for a long time!');

I instantiate an instance of this rule template with the following properties:

Property Value What it does
Triggering Group DoorsStatus Group that holds the Items with the door Contact Items
Threshold State OPEN We want to alert on OPEN
Comparison Operator == Any state other than OPEN is considered not alerting
Invert Comparison false If true it would negate the Comparison Operator (e.g. !=).
Reschedule false The alert period starts when the door first opens.
Hysteresis '' Hysteresis can only be used with numeric states.
Alert Delay PT15M If not overridden in the Item metadata, alert when the door remains OPEN for 15 minutes.
Reminder Period PT1H If not overridden in the Item metadata, repeat the alert every hour.
Metadata Namespace dayDoorAlert I’m not going to show it here, but I’ve another instance of this rule to generate alerts for open doors at night time.
Alert Rule door_reminder_detection The ID of the rule/script shown above.
End Alert Rule door_reminder_detection The ID of the rule/script shown above.
Initial Alert Rule '' Do nothing on first entering the alert state.
Do Not Disturb Start Time 22:00 Don’t call the alert or end alert rule after 10PM.
Do Not Disturb End Time 08:00 Allow the alert or end alert rule to be called again. If a call to one of the rules was scheduled between the start and end time and the Item is still in that alerting/end alerting state, the rule will be called at this time. That way, there is a handoff between the daytime rule and the nighttime rule.
Gatekeeper Delay 0 Calls to the alerting/end alerting rules will happen no faster than this delay (in milliseconds). This can help if the rule is being called rapidly or it interacts with devices that cannot process commands to fast. In this case we have no delay.
Rate Limit '' If this had a value, this defines a period during which new alerts are ignored after calling the alerting rule. Unlike gatekeeper, it drops the events instead of queueing them to deliver later.

Again, most of the above are the default values. This rule will send me an alert when a door remains open for too long and repeats the alert periodically between the hours of 08:00 and 22:00.

Each door where I want to customize the Alert Delay and Reminder have dayDoorAlert metadata. For example, I want to be alerted when my garage door is left open for only five minutes, but don’t remind me again for four hours.

value: " "
config:
  alertDelay: PT5M
  remPeriod: PT4H

On-the-other-hand I want to get an alert only if the back door is left open for more than eight hours and I want no reminders.

value: " "
config:
  alertDelay: PT8H
  remPeriod: PT0S

Motion Sensor Light

This is an example of controlling a light based on the state of a motion sensor Item. The light should turn on upon the first motion detection and turn off five minutes after the last motion is detected. The assumption is that the Switch Item representing the motion sensor does not return to OFF on it’s own, it just gets repeated ON updates on each motion detection.

First we define a rule that gets called on the first motion and five minutes after the last motion to control the light.

var light = items[alertItem.replace('Sensor', 'Light')];
if(isInitialAlert) {
  light.sendCommand('ON');
} else {
  light.sendCommand('OFF');
  items[alertItem].postUpdate('OFF'); // reset the motion sensor
}

This script assumes that the light Item is named the same as the motion sensor only with ā€œLightā€ instead of ā€œSensorā€ (e.g. the motion sensor could be ā€œFrontdoorSensorā€ and the light could be ā€œFrontdoorLightā€).

I instantiate an instance of this rule template with the following properties:

Property Value What it does
Triggering Group MotionSensors Group that holds the motion sensor Items
Threshold State ON We want to alert on ON
Comparison Operator == Any state other than ON is considered not alerting
Invert Comparison false If true it would negate the Comparison Operator (e.g. !=).
Reschedule false The alert period starts when the door first opens.
Hysteresis '' Hysteresis can only be used with numeric states.
Alert Delay PT5M If not overridden in the Item metadata, call the alert rule when the motion sensor Item remains on for 5 minutes without being updated.
Reminder Period '' No reminders
Metadata Namespace motionSensorTimer Metadata namespace, maybe you want a different time per motion sensor.
Alert Rule motion_sensor_light The ID of the rule/script shown above. When called, isAlerting will be true and isInitialAlert will be false.
End Alert Rule '' Do nothing at the end of the alert.
Initial Alert Rule motion_sensor_light The ID of the rule/script shown above. When called, isAlerting will be false (isAlerting remains false until the alert rule is called) and isInitialAlert will be true.
Do Not Disturb Start Time 06:00 Don’t call the rules during the day time.
Do Not Disturb End Time 20:00 Don’t call the rules during the day time.
Gatekeeper Delay 0 Calls to the alerting/end alerting rules will happen no faster than this delay (in milliseconds). This can help if the rule is being called rapidly or it interacts with devices that cannot process commands to fast. In this case we have no delay.
Rate Limit '' If this had a value, this defines a period during which new alerts are ignored after calling the alerting rule. Unlike gatekeeper, it drops the events instead of queueing them to deliver later.

One additional change is to modify the rule trigger to be ā€œMember of Group receives updateā€.

No Motion Timer (i.e. Item remains in same state for too long)

This is an example of a motion sensor timer where you want something to occur after a given amount of time after the most recent event. This could be to turn off a light, alert that a sensor has been offline for too long, etc.

In my particular case I want an alert if no motion is detected at my dad’s house for eight hours or more with a reminder every eight hours until motion is detected again.

My rule to process the alerts is pretty simple for now. We haven’t found a good place to put it where it detects him and not the dogs so I don’t publish notifications right now.

if(isAlerting) console.log('It has been a long time since dad moved.');
else console.log("Motion detected at dad's house after alerting.');
Property Value What it does
Triggering Group MotionSensors Group that holds the Items with the motion sensors. I’ve just the one but it’s easier to put it into a Group than to modify the trigger and code to handle an individual Item.
Threshold State ON Motion results in an ON update to the Item
Comparison Operator == Any state other than OFF is ignored
Invert Comparison false If true it would negate the Comparison Operator (e.g. !=).
Reschedule true The alert delay starts after the most recent event with the Item still in the alerting state.
Hysteresis '' Hysteresis can only be used with numeric states.
Alert Delay PT8H Alert when the motion sensor doesn’t change for over eight hours.
Reminder Period PT8H Repeat the alert every eight hours.
Metadata Namespace dadMotion I may decide to override things in metadata but for not I’m not using this.
Alert Rule dad_motion_alert The ID of the rule/script shown above.
End Alert Rule ā€˜dad_motion_alert’ The ID of the rule/script shown above.
Initial Alert Rule '' Do nothing on the initial alert.
Do Not Disturb Start Time 22:00 Don’t call the alert or end alert rule after 10PM.
Do Not Disturb End Time 08:00 Allow the alert or end alert rule to be called again. If a call to one of the rules was scheduled between the start and end time and the Item is still in that alerting/end alerting state, the rule will be called at this time. That way, there is a handoff between the daytime rule and the nighttime rule.
Gatekeeper Delay 0 Calls to the alerting/end alerting rules will happen no faster than this delay (in milliseconds). This can help if the rule is being called rapidly or it interacts with devices that cannot process commands to fast. In this case we have no delay.
Rate Limit '' If this had a value, this defines a period during which new alerts are ignored after calling the alerting rule. Unlike gatekeeper, it drops the events instead of queueing them to deliver later.

I don’t define any overridden parameters at the Item level for this one.

Sensor Online/Offline Alerting

Generate an alert when an Item stops updating for a given amount of time. This can be useful to monitor periodically updating sensor Items and report when they stop reporting. An alternative approach is to use Expire to set the Item to UNDEF or NULL but this could be less work overall.

First define a rule/script to handle the calls.

console.debug('Sensor status proc called with Item ' + alertItem + ', state ' + alertState + ' initialAlert ' + isInitialAlert + ' and alerting ' + isAlerting);
if(isInitialAlert) {
  // you might want to add some book keeping to only call if the alert was sent
  console.info('Sensor ' + alertItem + ' is back online!')
}
else {
  console.info('Sensor ' + alertItem + ' has stopped reporting, updating the Item to UNDEF');
  // send alert here
  const update = (alertItem) => {
    return () => {
      items[alertItem].postUpdate('UNDEF');
    }
  }
  setTimeout(update(alertItem), 250); // helps avoid multi-threaded exceptions.
}

I instantiate an instance of this rule template with the following properties:

Property Value What it does
Triggering Group AllSensirs Group that holds the sensor Items
Threshold State UnDefType We want to start tracking the time on valid states.
Comparison Operator == Any state other than ON is considered not alerting
Invert Comparison invert Negates the Comparison Operator (e.g. !=) so the timer starts on any non-UnDefType.
Reschedule true Reschedule the alert timer to happen after the last event received.
Hysteresis '' Hysteresis can only be used with numeric states.
Alert Delay PT15M If not overridden in the Item metadata, call the alert rule when the sensor remains unchanged for 15 minutes without being updated.
Reminder Period '' No reminders
Metadata Namespace sensorStatus Metadata namespace, sensors have varying reporting periods.
Alert Rule sensor_status_proc The ID of the rule/script shown above. When called, isAlerting will be true and isInitialAlert will be false.
End Alert Rule '' Do nothing at the end of the alert.
Initial Alert Rule sensor_status_light The ID of the rule/script shown above. When called, isAlerting will be false (isAlerting remains false until the alert rule is called) and isInitialAlert will be true.
Do Not Disturb Start Time 22:00 Don’t call the rules during the day time.
Do Not Disturb End Time 08:00 Don’t call the rules during the day time.
Gatekeeper Delay 250 There are lots of sensors so the chances of multi-threaded exceptions is high.
Rate Limit '' If this had a value, this defines a period during which new alerts are ignored after calling the alerting rule. Unlike gatekeeper, it drops the events instead of queueing them to deliver later.
1 Like