Service Offline Alerting
Send an alert when a home automation important service goes offline.
configuration: {}
triggers:
- id: "1"
configuration:
groupName: ServiceStatuses
state: ON
type: core.GroupStateChangeTrigger
- id: "2"
configuration:
groupName: ServiceStatuses
state: OFF
type: core.GroupStateChangeTrigger
conditions:
- inputs: {}
id: "4"
configuration:
type: application/javascript;version=ECMAScript-2021
script: |+
var logger = log('Offline Alert');
logger.debug("Offline alert for {} state {} old state {} undef {}",
event.itemName,
event.itemState,
event.oldItemState,
items.getItem(event.itemName).isUninitialized);
!items.getItem(event.itemName).isUninitialized;
type: script.ScriptCondition
actions:
- inputs: {}
id: "3"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >-
var {alerting} = require('personal');
var {timerMgr} = require('openhab_rules_tools');
var logger = log('Offline Alert');
logger.debug('Service offline alert for {} which is {}!', event.itemName, event.itemState);
var timers = cache.get(ruleUID+'_tm', () => new timerMgr.TimerMgr());
var alertGenerator = function(itemName, itemState) {
return function() {
var curr = items.getItem(itemName);
if(curr.state == itemState) {
var status = (itemState == 'ON') ? 'online' : 'offline';
var name = item.getMetadataValue('name') || item.label;
alerting.sendAlert(name + ' is now ' + status + '!', logger);
}
else {
logger.warn('Offline alert timer expired but Item ' + itemName
+ " has returned to it's previous state!")
}
}
}
timers.check(event.itemName, '5m',
alertGenerator(event.itemName, event.itemState.toString()));
type: script.ScriptAction
Service Offline Report
Send an alert with a summary of offline devices in the morning, if there are any.
configuration: {}
triggers:
- id: "1"
configuration:
time: 08:00
type: timer.TimeOfDayTrigger
conditions:
- inputs: {}
id: "2"
configuration:
itemName: ServiceStatuses
state: ON
operator: "!="
type: core.ItemStateCondition
actions:
- inputs: {}
id: "3"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >-
var {alerting} = require('personal');
var logger = log('Offline Report');
var ss = items.getItem('ServiceStatuses');
var nullItems = alerting.getNames('ServiceStatuses', s => s.isUninitialized);
if(nullItems) logger.info('Null: {}', nullItems);
else logger.debug('No null services');
var offItems = alerting.getNames('ServiceStatuses', s => s.state == 'OFF');
if(offItems) logger.info('OFF Items: {}', offItems);
else logger.debug('No OFF services');
var msg = '';
if(nullItems) {
msg = 'The following sensors are in an unknown state: ' + nullItems;
}
if(offItems) {
if(msg) msg += '\n';
msg += 'The following sensors are known to be offline: ' + offItems;
}
if(msg) alerting.sendInfo(msg);
else logger.info('There are no offline services');
type: script.ScriptAction
Thing Status Handler
I don’t know why I still have this around. I wrote it to test the Thing Status rule template, which calls this script. Thing Status Reporting
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
Time of Day Lights
The actual Time of Day rule is a Rule Template: Time Based State Machine.
Based on the time of day transitions, turn OFF and ON the lights for that period.
configuration: {}
triggers:
- id: "1"
configuration:
itemName: TimeOfDay
type: core.ItemStateChangeTrigger
conditions: []
actions:
- inputs: {}
id: "2"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >
var logger = log('Time of Day Lights');
var tod = items.getItem('TimeOfDay').state
var offGroupName = 'TOD_Lights_OFF_' + tod
var onGroupName = 'TOD_Lights_ON_' + tod
var switchLights = function(tod, grp, st) {
logger.info('Turning ' + st + ' the lights for ' + tod + ' using ' + grp);
items.getItem(grp)
.members
.filter(light => light.state != st)
.forEach(light => light.sendCommandIfDifferent(st));
}
switchLights(tod, offGroupName, "OFF");
switchLights(tod, onGroupName, "ON");
type: script.ScriptAction
Water Leak Alarm
After having to fix the ceiling in the basement under the kitchen sink, I got a couple of Zigbee water leak alarms. This rule is mainly to just back up the “beep beep beep” from the alarms.
configuration: {}
triggers:
- id: "1"
configuration:
itemName: WaterLeakAlarms
state: ON
type: core.ItemStateChangeTrigger
conditions: []
actions:
- inputs: {}
id: "2"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >
var {alerting} = require('personal');
var {loopingTimer} = require('openhab_rules_tools');
var logger = log('Water Leak Alert');
logger.warn('A water leak was detected!');
var lt = cache.get(ruleUID+'_lt', () => new loopingTimer.LoopingTimer());
var loopGenerator = function() {
return function(){
if(items.getItem('WaterLeakAlarms').state == 'OFF') {
logger.info('No more leaks!');
return null;
}
else {
logger.info('Still seeing a water leak!');
var names = alerting.getNames('WaterLeakAlarms', i => i.state == 'ON');
alerting.sendAlert("There's a leak at " + names + '!');
return '1m';
}
}
}
lt.loop(loopGenerator(), '0s');
type: script.ScriptAction
Weather Lights
During the “DAY” time, turn on or off the lights based on cloudiness level. However, if a light is overridden (based on metadata) leave it alone.
configuration: {}
triggers:
- id: "1"
configuration:
itemName: vIsCloudy
type: core.ItemStateChangeTrigger
- id: "2"
configuration:
itemName: TimeOfDay
state: DAY
type: core.ItemStateChangeTrigger
- id: "5"
configuration:
itemName: Presence
type: core.ItemStateChangeTrigger
conditions:
- inputs: {}
id: "3"
configuration:
itemName: TimeOfDay
state: DAY
operator: =
type: core.ItemStateCondition
- inputs: {}
id: "6"
label: vIsCloudy isn't undefined
configuration:
type: application/javascript;version=ECMAScript-2021
script: |
!items.getItem('vIsCloudy').isUninitialized;
type: script.ScriptCondition
actions:
- inputs: {}
id: "4"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >-
var logger = log('Weather Lights');
if(items.getItem('Presence').state == 'OFF') {
logger.info('No one is home, turing off the weather lights');
items.getItem('TOD_Lights_ON_WEATHER').sendCommand('OFF');
}
else {
// TODO find alternative to sleep
// if(event !== undefined && event.itemName == "TimeOfDay") {
// java.lang.Thread.sleep(500);
// }
logger.info('Cloudiness changed to ' + items.getItem('vIsCloudy').state + ', adjusting the lights');
items.getItem('TOD_Lights_ON_WEATHER')
.members
.filter(light => light.getMetadataValue('LightsOverride') == 'false' || !light.getMetadataValue('LightsOverride'))
.forEach(light => light.sendCommandIfDifferent(items.getItem('vIsCloudy').state));
}
type: script.ScriptAction
Weather Lights Override Reset
Sets the override metadata to false at the end of the “DAY” time of day.
configuration: {}
triggers:
- id: "1"
configuration:
itemName: TimeOfDay
previousState: DAY
type: core.ItemStateChangeTrigger
conditions: []
actions:
- inputs: {}
id: "2"
configuration:
type: application/javascript;version=ECMAScript-2021
script: "var logger = log('Reset Lights Override');
logger.info('Resetting the LightsOverride metadata value to
false');
logger.debug('Looping through the lights');
for(let light of items.getItem('TOD_Lights_ON_WEATHER').members)
{
\ logger.debug('Current override is {}',
light.getMetadataValue('LightsOverride'));
\ logger.debug('Success = {}',
light.upsertMetadataValue('LightsOverride', 'false'));
}
\ "
type: script.ScriptAction
Not sure why the formatting is weird for this one.
Weather Lights Override
If a manual change to a light is detected mark that light as overridden. This is somewhat brittle becuase it’s based on timing.
configuration: {}
triggers:
- id: "1"
configuration:
groupName: TOD_Lights_ON_WEATHER
type: core.GroupStateChangeTrigger
conditions:
- inputs: {}
id: "3"
configuration:
itemName: TimeOfDay
state: DAY
operator: =
type: core.ItemStateCondition
- inputs: {}
id: "4"
label: Manual trigger?
description: TimeOfDay changed more than 10 seconds ago and vIsCloudy more than
5 seconds ago
configuration:
type: application/javascript;version=ECMAScript-2021
script: >-
var {timeUtils} = require('openhab_rules_tools');
var logger = log('Weather Lights Override');
logger.debug('Checking to see if enough time has past since a transition');
var isBefore = function(item, secs) {
return timeUtils.toDateTime(item.history.lastUpdate('mapdb')).isBefore(time.ZonedDateTime.now().minusSeconds(secs));
}
var tod = isBefore(items.getItem('TimeOfDay'), 10);
var cloudy = isBefore(items.getItem('vIsCloudy'), 5);
tod && cloudy;
type: script.ScriptCondition
actions:
- inputs: {}
id: "2"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >
var logger = log('Weather Lights Override');
logger.info('Manual light trigger detected, overriding the light for {}', event.itemName);
items.getItem(event.itemName).upsertMetadataValue("LightsOverride", "true");
type: script.ScriptAction
Zwave Controller Offline Alert
For a time I was having my HUSZB-1 controller go offline periodically. This rule was to let me know when that happens. I think it was something caused by Docker. I’ve just never removed the rule.
configuration: {}
triggers:
- id: "1"
configuration:
thingUID: zwave:serial_zstick:zw_controller
status: OFFLINE
type: core.ThingStatusChangeTrigger
- id: "2"
configuration:
thingUID: zigbee:coordinator_ember:zg_coordinator
status: OFFLINE
type: core.ThingStatusChangeTrigger
conditions: []
actions:
- inputs: {}
id: "3"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >
var {alerting} = require('personal');
var {timerMgr, rateLimit} = require('openhab_rules_tools');
var timers = cache.get(ruleUID+'_tm', () => new timerMgr.TimerMgr());
var rl = cache.get(ruleUID+'_rl', () => new rateLimit.RateLimit());
var sendAlertGen = function() {
return function() { rl.run(() => alerting.sendAlert('The Zwave Controller or Zigbee Coordinator is OFFLINE!'), '24h'); }
};
timers.check('zigbee', '1m', sendAlertGen(), true);
type: script.ScriptAction
Stuff to look for
-
Notice how much simpler rules can become when consolidating common code into a library, even if it’s just a couple of lines function. The above would be hundreds if not a thousand lines longer if I had to rewrite the TimerMgr code for every rule that uses it. Just look at the Rule Templates which have to include these library classes inline to see what I mean.
-
These are just my JS Scripting rules. I’ve also a bunch of just pure UI rules without code as well as roughly 25% of my rules implemented by rule templates. You are not stuck with just one language.
-
All rules share the cache, take special care with the keys used to put stuff into the cache. I use the
ruleUID
as part of the key. -
The cache remains even if you save and reload the rule. Don’t forget to delete the old variable (e.g. TimerMgr), especially if you’re changing the code for what’s stored there. While writing the looping timer I couldn’t figure out why my changes to the code were not being reflected in the rule. It was because an instance of LoopingTimer was still in the cache and not being recreated with the new code.
-
Conditions can be pretty useful to simplify some rules.
-
Pay attention to the JavaScript Array filter, map, reduce type operations.
-
I pretty much only have to import my libraries. Everything else I need to work with OH is just there in openhab-js.