Edit1: see Some JS Scripting UI Rules Examples - #14 by rlkoshak for some more point examples.
Edit 2: Removed direct quoting of openhab_rules_tools, changed to installation instructions. The actual libraries are constantly being expanded and improved and I don’t want to keep them up to date here too.
I’ve managed to polish off my primary rules so they use JS Scripting and the openhab-js library it comes with. I’m posting them here just so people can have some working examples to look at and ask questions about. Be sure to refer to the JS Scripting addon docs for full documentation on the library: JavaScript Scripting - Automation | openHAB
Libraries
I’m trying to use the Node.js npm way of writing and using libraries. I’m mainly going from examples so I’m certain there’s lots I’m doing wrong. Anyway:
- create a folder under
$OH_CONF/automation/js/node_modules
- run
npm init
and answer the questions - create
index.js
where you will list your exports - create your file(s) with your library functions, classes, etc.
But definitely go out and find a good tutorial.
An example of my personal index.js
module.exports = {
get alerting() { return require('./alerting.js') },
get utils() { return require('./utils.js') }
}
I’ve two files that are exported, one as alerting
and the other as utils
.
When adding new files, remember to update the index.js file.
I don’t explicitly require openhab in these and I probably should. They seem to work without doing so though.
**I’m not posting these libraries for your to just copy and use. They are intended to be examples to learn from. If you do want to use these libraries, wait a bit until I have them available in npm where they can just be installed.
personal
These are pretty basic and simple functions that get used in lots of my rules. Mail is not working right now so I’ve commented that out and am using the Notification actions instead for alerting.
alerts.js
exports.sendAlert = function(message, logger) {
var logger = (logger) ? logger : log('sendAlert');
logger.warn('ALERT: ' + message);
actions.NotificationAction.sendBroadcastNotification(message, 'alarm', 'alert');
// if(!actions.Things.getActions("mail", "mail:smtp:gmail").sendMail("email@server.com", "openHAB Alert", message)) {
// logger.error("Failed to send email alert alert");
// }
}
exports.sendInfo = function(message, logger) {
var logger = (logger) ? logger : log('sendInfo');
logger.info('INFO: ' + message);
// if(!actions.Things.getActions("mail", "mail:smtp:gmail")
// .sendMail("email@server.com", "openHAB Info", message)) {
// logger.error("Failed to send email info alert");
// }
}
exports.isNight = function() {
const currToD = items.getItem('TimeOfDay').state;
return currToD == 'NIGHT' || currToD == 'BED';
}
exports.isAway = function() {
return exports.isNight() || items.getItem('Presence').state != 'ON';
}
exports.getNames = function(group, filterFunc) {
return items.getItem(group.name || group).members
.filter(filterFunc)
.map(s => s.getMetadataValue('name') || s.label)
.join(', ');
}
Note that these are defined on exports
so they get exposed when requires
in the rule. You can also/alternatively list them (which you’ll see later).
utils.js
exports.hysteresis = function(curr, threshold, hyst) {
var min = max - hyst;
if(curr < min) return 'ON';
else if(curr > max) return 'OFF';
else return 'STAY';
}
I expect over time this file will grow.
openhab_rules_tools
I’ve completed an initial release of my openhab_rules_tools libraries ported to JS Scripting and as an npm module. See Announcing the initial release of openhab_rules_tools.
Basically all of my rules depend upon this library which includes a number of useful capabilities mostly having to deal with managing timers and controlling the scheduling of events.
For full docs, installation and usage examples see https://github.com/rkoshak/openhab-rules-tools.
tl;dr:
cd $OH_CONF/automation/js
sudo -u openhab npm i openhab_rules_tools
For now, use the comments in the code, the examples here, and the tests for usage. Eventually I’ll generate some reference docs.
Rule Templates
All of my rule templates, which are how I implement a good percentage of my rules, are published to the Marketplace. I will not reproduce them here. Besides, those are essentially Nashorn rules. They all need to be rewritten. These templates actually generate almost half of portion of my rules.
Rules
In no particular order:
Alarm Script
This rule gets called from my Alarm Clock Rule Template. When my alarm goes off in the middle of the night, turn on some lights briefly (I’ve a puppy that can’t make it through the night yet). When it goes off after 05:00, transition time of day to MORNING.
configuration: {}
triggers: []
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.info('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
All Away
I originally wrote this to debug some presence detection problem I was having and I’ve kept it in place and kept it up to date over time.
configuration: {}
triggers:
- id: "1"
configuration:
itemName: Presence
type: core.ItemStateChangeTrigger
- id: "3"
configuration:
itemName: vRichPresence
type: core.ItemStateChangeTrigger
- id: "4"
configuration:
itemName: vJennPresence
type: core.ItemStateChangeTrigger
conditions:
- inputs: {}
id: "5"
label: Triggering Item didn't change to uninitialized
configuration:
type: application/javascript;version=ECMAScript-2021
script: "!items.getItem(event.itemName).isUninitialized"
type: script.ScriptCondition
actions:
- inputs: {}
id: "2"
label: Log out who left or returned home
configuration:
type: application/javascript;version=ECMAScript-2021
script: |-
var {alerting} = require('personal');
var logger = log('All Away');
var item = items.getItem(event.itemName);
var state = item.state;
var name = item.getMetadataValue('name') || item.label.split("'")[0];
logger.debug('{} has changed state', name);
if(name == 'Everyone' && state == 'ON') {
name = 'Someone';
}
var status = ' is unknown: ' + state;
if(state == 'ON') {
status = ' has arrived';
}
else if(state == 'OFF') {
status = ' is away';
}
logger.info(name + status);
Calculate Electricity Bill
Estimate the month’s power bill based on the whole house energy meter.
configuration: {}
triggers:
- id: "1"
configuration:
itemName: HomeEnergyMeter_ElectricmeterkWh
type: core.ItemStateChangeTrigger
- id: "2"
configuration:
itemName: HomeEnergyMeter_Access
type: core.ItemStateChangeTrigger
- id: "3"
configuration:
itemName: HomeEnergyMeter_Rate
type: core.ItemStateChangeTrigger
conditions: []
actions:
- inputs: {}
id: "4"
label: Calculate the power bill and update the Item
configuration:
type: application/javascript;version=ECMAScript-2021
script: >
var logger = log('Power Bill Estimate');
var curr = items.getItem('HomeEnergyMeter_ElectricmeterkWh').rawState;
var rate = items.getItem('HomeEnergyMeter_Rate').rawState;
var access = items.getItem('HomeEnergyMeter_Access').rawState;
var estimate = (curr * rate) + access;
items.getItem('HomeEnergyMeter_CurrentBillEstimate').postUpdate(estimate.toString());
logger.debug('Calculated bill = ${}', estimate);
type: script.ScriptAction
Christmas Lights
This rule becomes disabled at the end of the season. Turn on the Christmas lights a little before sundown (based on Time of Day). On Christmas Eve and Christmas Day turn them on first thing in the morning.
The condition is probably redundant given I’ve a rule that disables this rule when TisTheSeason becomes OFF anyway.
configuration: {}
triggers:
- id: "1"
configuration:
itemName: TimeOfDay
type: core.ItemStateChangeTrigger
conditions:
- inputs: {}
id: "2"
configuration:
itemName: TisTheSeason
state: ON
operator: =
type: core.ItemStateCondition
actions:
- inputs: {}
id: "3"
configuration:
type: application/javascript;version=ECMAScript-2021
script: |
var logger = log('Christmas Lights');
logger.info('Setting the christmas lights!');
var newState = 'OFF';
var currToD = items.getItem('TimeOfDay').state;
var tods = 'AFTERNOON,EVENING';
var daysto = actions.Ephemeris.getDaysUntil('Christmas');
if(daysto <= 1) tods = tods + ',DAY';
if(tods.contains(currToD)){
newState = 'ON';
}
logger.info('Current tod = {} on tods = {}', currToD, tods);
for each (var light in items.getItem('AllChristmasLights').members) {
light.sendCommandIfDifferent(newState);
}
type: script.ScriptAction
Chromecast Alert
My eight-year-old like to sneak and mess around with the Chromecasts. This lets me know what is playing on what device.
configuration: {}
triggers:
- id: "1"
configuration:
groupName: Chromecast_Idle
state: OFF
previousState: ON
type: core.GroupStateChangeTrigger
conditions: []
actions:
- inputs: {}
id: "2"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >-
var {alerting} = require('personal');
var {timerMgr} = require('openhab_rules_tools');
var logger = log('Rules CC Alert');
timers = cache.get(ruleUID+'_tm', () => new timerMgr.TimerMgr());
var alertFunctionGenerator = function() {
return function() {
// generate and send the alert if it's still on
var idleItem = items.getItem('Chromecast_Idle');
if(idleItem.state == 'ON') {
var activeDevices = idleItem
.members
.filter(cc => cc.state == 'OFF')
.map(function(cc) {
var devName = cc.name.split(/_/)[0];
return ' ' + cc.label.replace(' Idling', '') + ' | '
+ items.getItem(devName+'_App').state + ' | '
+ items.getItem(devName+'_MediaType').state + ' | '
+ items.getItem(devName+'_MediaArtist').state + ' | '
+ items.getItem(devName+'_MediaTitle').state;
})
.join('\n');
alerting.sendInfo('The following Chromecast devices are now in use:\n' + activeDevices);
}
else {
logger.debug('The Chromecast is no longer in use.');
}
}
}
logger.info('A Chromecast is now in use, waiting a bit for artist and track title info before sending the alert');
timers.check('cc_alert', '60s', alertFunctionGenerator(), true);
type: script.ScriptAction
Dad Motion
I’ve an instance of OH at my dad’s house with a motion sensor. If there is a period of time where he doesn’t move, I get an alert and can call and check in on him. I’m probably going to try to beef up the Threshold Alert template to handle this rule.
configuration: {}
triggers:
- id: "2"
configuration:
itemName: Dads_Motion_Timeout
type: core.ItemStateChangeTrigger
- id: "4"
configuration:
startlevel: 100
type: core.SystemStartlevelTrigger
- id: "1"
configuration:
itemName: MotionSensor_LastMotion
type: core.ItemStateChangeTrigger
conditions: []
actions:
- inputs: {}
id: "3"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >
var {alerting} = require('personal');
var {timeUtils, timerMgr} = require('openhab_rules_tools');
var logger = log('Dad Motion');
var formatDT = function(dt) {
// TODO revist after this gets fixed in the library
//var DateTimeFormatter = Java.type("java.time.format.DateTimeFormatter");
//var formatter = time.DateTimeFormatter.ofPattern("h:mm a, ccc MMM dd");
//return dt.format(formatter);
return dt.toString();
}
var dadTimerExpiredGenerator = function(formatDT){
return function() {
if(items.getItem('Dads_Motion_Alert').state == 'ON') {
alerting.sendInfo('It has been awhile since dad has moved. Last movement detected at '
+ formatDT(items.getItem('MotionSensor_LastMotion').rawState.getZonedDateTime()));
}
else {
logger.info("Dad's motion timer went off but alerting is OFF");
}
}
}
// Initialize variables
var timers = cache.get(ruleUID+'_tm', () => new timerMgr.TimerMgr());
var now = time.ZonedDateTime.now();
var lastMotionItem = items.getItem('MotionSensor_LastMotion');
var timeout = items.getItem('Dads_Motion_Timeout');
var motionTime = (lastMotionItem.isUninitialized) ? now : timeUtils.toDateTime(lastMotionItem.rawState.getZonedDateTime());
var motionTime_Str = formatDT(motionTime);
var timerHours = 12;
// If it's not set move the timout to 12 hours
logger.debug('Timeout is {}', timeout.state);
if(timeout.isUninitialized) {
timeout.postUpdate('12');
}
else {
timerHours = timeout.state;
}
// Calculate the alert time, move it to tomorrow if it'll go off at night
var timerTime = motionTime.plusHours(timerHours);
var nightStart = time.ZonedDateTime.now().withHour(22).withMinute(0).withSecond(0);
var nightEnd = time.ZonedDateTime.now().withHour(9).withMinute(0).withSecond(0).plusDays(1);
if(timerTime.isAfter(nightStart) && timerTime.isBefore(nightEnd)) {
timerTime = nightEnd;
}
var timerTime_Str = formatDT(timerTime);
// If it's already been too much time, alert now
if(timerTime.isBefore(now)) {
logger.warn('timerTime is in the past! {}', timerTime_Str);
dadTimerExpiredGenerator(formatDT)();
}
// Set or reschedule a timer to alert if there is no motion for too much time
else {
logger.debug("Motion detected at Dad's at {} or reminder time has changed, setting reminder to expire at {}",
motionTime_Str, timerTime_Str);
timers.check('alertTimer', timerTime, dadTimerExpiredGenerator(formatDT), true);
}
type: script.ScriptAction
Garage Alert
It’s really annoying to try and trigger the garage door openers when the controller is offline. This gives some feedback when that occurs.
configuration: {}
triggers:
- id: "1"
configuration:
itemName: Large_Garagedoor_Opener
type: core.ItemCommandTrigger
- id: "2"
configuration:
itemName: Small_Garagedoor_Opener
type: core.ItemCommandTrigger
conditions:
- inputs: {}
id: "3"
configuration:
type: application/javascript;version=ECMAScript-2021
script: items.getItem('vCerberos_Status').state != 'ON' ||
items.getItem('Cerberossensorreporter_Onlinestatus').state != 'ON';
type: script.ScriptCondition
actions:
- inputs: {}
id: "4"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >-
var {alerting} = require('personal');
alerting.sendAlert('Attempting to trigger a garage door but cerberos is not online!');
type: script.ScriptAction
Humidifier Control
I’ve some dumb humidifiers connected to smart outlets. This controls them based on humidity readings and setpoints.
configuration: {}
triggers:
- id: "1"
configuration:
itemName: Mainfloorsensors_Humidity
type: core.ItemStateChangeTrigger
- id: "2"
configuration:
itemName: Mainfloorsensors_Humidity_Setpoint
type: core.ItemStateChangeTrigger
- id: "3"
configuration:
itemName: MasterBedroomSensors_Humidity
type: core.ItemStateChangeTrigger
- id: "4"
configuration:
itemName: MasterBedroomSensors_Humidity_Setpoint
type: core.ItemStateChangeTrigger
conditions: []
actions:
- inputs: {}
id: "7"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >-
var {utils} = require('personal');
var logger = log('Humidifier Control');
var HYST = 10;
var sensorName = event.itemName.replace('_Setpoint', '');
var setpointName = sensorName + '_Setpoint';
var controlItem = items.getItem(sensorName + '_Switch');
var curr = (event.itemName == sensorName)? event.itemState : items.getItem(sensorName).state;
var max = (event.itemName == setpointName) ? event.itemState : items.getItem(setpointName).state;
var min = max - HYST;
logger.debug(sensorName + ' is currently ' + curr + ' with min ' + min + ' and max ' + max);
var newState = utils.hysteresis(curr, max, HYST);
logger.debug('New humidifier state is ' + newState + ' and Item state is ' + controlItem.state);
if(newState != 'STAY' && controlItem.state != newState) {
logger.debug('Command the humidifier '
+ sensorName + ' to ' + newState
+ ' because min='+ min + ' max='+ max
+ ' and curr=' + curr);
controlItem.sendCommand(newState);
}
else {
logger.debug('Leaving the humidifier ' + controlItem.state);
}
type: script.ScriptAction
Humidity Alert
When the humidity drops too low it’s probably because a humidifier needs to be filled. This generates an alert. This is called by the Threshold Alert rule template.
configuration: {}
triggers: []
conditions: []
actions:
- inputs: {}
id: "1"
configuration:
type: application/javascript;version=ECMAScript-2021
script: |-
var {alerting} = require('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
Low Battery Alert
Let me know when a battery gets too low. This also is called by the Threshold Alert template.
configuration: {}
triggers: []
conditions: []
actions:
- inputs: {}
id: "1"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >
var {alerting} = require('personal');
var logger = log('Low Battery');
alerting.sendAlert('The following batteries are below 25%: ' + this.threshItemLabels, logger);
type: script.ScriptAction
MBR TV Stop at Night
My ISP has a bandwidth cap. My wife likes to fall to sleep to the sound of the TV. So this rule will stop the TV well after we’ve all fallen to sleep.
configuration: {}
triggers:
- id: "2"
configuration:
time: 01:00
type: timer.TimeOfDayTrigger
conditions: []
actions:
- inputs: {}
id: "3"
label: Stop playback on MBR Chromecast
configuration:
type: application/javascript;version=ECMAScript-2021
script: |
var logger = log('TV Home');
logger.info('Stopping the Master Bedroom TV');
items.getItem('MasterBedroomTV_Stop').sendCommand('ON');
type: script.ScriptAction
MBR Lights ON when Returning
If it’s dark turn on the master bedroom lights when we get home.
configuration: {}
triggers:
- id: "1"
configuration:
itemName: Presence
state: ON
previousState: OFF
type: core.ItemStateChangeTrigger
conditions:
- inputs: {}
id: "2"
configuration:
itemName: MasterBedroomSensors_LightLevel
state: "100"
operator: <
type: core.ItemStateCondition
- inputs: {}
id: "4"
configuration:
type: application/javascript;version=ECMAScript-2021
script: |
var {alerting} = require('personal');
!alerting.isNight()
type: script.ScriptCondition
actions:
- inputs: {}
id: "3"
configuration:
type: application/javascript;version=ECMAScript-2021
script: |
var logger = log('MBR Lights');
logger.info('Welcome home! Turning on the MBR Lights!');
items.getItem('MasterBedroomLights_Power').sendCommand('ON');
type: script.ScriptAction
Open Door Night Time
If it’s nearing bed time or no one is home, alert if a door is OPEN. Repeat the warning until the doors are closed.
configuration: {}
triggers:
- id: "1"
configuration:
itemName: TimeOfDay
state: NIGHT
type: core.ItemStateChangeTrigger
- id: "2"
configuration:
itemName: Presence
state: OFF
previousState: ON
type: core.ItemStateChangeTrigger
- id: "4"
configuration:
itemName: DoorsStatus
state: OPEN
type: core.ItemStateChangeTrigger
- id: "6"
configuration:
itemName: TimeOfDay
state: BED
type: core.ItemStateChangeTrigger
conditions:
- inputs: {}
id: "5"
configuration:
type: application/javascript;version=ECMAScript-2021
script: |-
var {alerting} = require('personal');
var logger = log('Open Door Night')
logger.debug('State = {} not closed = {} away = {}',
event.itemState,
(event.itemState != 'CLOSED'),
alerting.isAway());
event.itemState != 'CLOSED' && alerting.isAway();
type: script.ScriptCondition
actions:
- inputs: {}
id: "3"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >
var {alerting} = require('personal');
var {loopingTimer} = require('openhab_rules_tools');
var logger = log('Open Door Night Reminder');
var lt = cache.get(ruleUID+'_lt', () => new loopingTimer.LoopingTimer());
var reminder = function() {
var doorsStatus = items.getItem('DoorsStatus');
var state = doorsStatus.state;
logger.info("It's night or all are away, sending alerts until all doors are closed, doors status "
+ state);
if(state == 'OPEN') {
logger.info('Sending open door alert');
var openDoors = alerting.getNames(doorsStatus, door => door.state == 'OPEN');
var tod = items.getItem('TimeOfDay').state;
var pres = items.getItem('Presence').state;
var msg = 'The following doors are open';
msg += (alerting.isNight()) ? " and it's night time" : '';
msg += (pres == 'OFF') ? ' and no one is home: ' : ': ';
msg += openDoors;
alerting.sendAlert(msg);
return '15m';
}
else {
logger.info('All doors are now closed');
return null;
}
}
// Kick off the looping timer
lt.loop(reminder, '0s');
type: script.ScriptAction
Open Door Reminder
Each door sensor Item has some metadata with a time. If the door remains open for longer than that time send an alert.
configuration: {}
triggers:
- id: "1"
configuration:
groupName: DoorsStatus
state: OPEN
type: core.GroupStateChangeTrigger
- id: "4"
configuration:
groupName: DoorsStatus
state: CLOSED
type: core.GroupStateChangeTrigger
conditions:
- inputs: {}
id: "2"
label: The door's state didn't change to an UnDefType
configuration:
type: application/javascript;version=ECMAScript-2021
script: |
!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('Open Door Reminder');
var timers = cache.get(ruleUID+'_tm', () => new timerMgr.TimerMgr());
var reminderGenerator = function(itemName, name, when, timers){
return function() {
var item = items.getItem(itemName)
if(item.state != 'OPEN') {
logger.warn(itemName + ' open timer expired but the door is '
+ itemState + '. Timer should have been cancelled.');
}
else {
alerting.sendAlert(name + ' has been open for ' + when);
// Reschedule if it's night
var tod = items.getItem('TimeOfDay').state;
if(alerting.isNight()) {
logger.info('Rescheduling timer for ' + name + " because it's night");
timers.check(itemName, when, reminderGenerator(itemName, name, when));
}
}
}
}
var item = items.getItem(event.itemName);
logger.debug('Handling new door state for reminder: ' + item.name + ' = ' + item.state);
if(item.state == 'CLOSED' && this.timers.hasTimer(item.name)) {
logger.info('Cancelling the timer for ' + item.name);
this.timers.cancel(item.name);
}
else {
var name = item.getMetadataValue('name') || item.label.replace(' Sensor', '').replace(' Status', '');
var remTime = item.getMetadataValue('rem_time') || '60m';
logger.debug('Creating a reminder timer for ' + item.name + ' for ' + remTime);
timers.check(item.name, remTime, reminderGenerator(event.itemName, name, remTime, timers));
}
type: script.ScriptAction
SSID Presence
My presence detection has evolved over time. The Android app’s ability to report the connected SSID is the simplest and more reliable I’ve implemented so far. This looks to see if it’s connected to my home SSID.
configuration: {}
triggers:
- id: "1"
configuration:
itemName: RichSSID
state: ""
type: core.ItemStateChangeTrigger
- id: "2"
configuration:
itemName: JennSSID
type: core.ItemStateChangeTrigger
conditions: []
actions:
- inputs: {}
id: "4"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >-
var logger = log('SSID Presence');
logger.info('{} changed to {}', event.itemName, event.itemState);
var ssids = 'myssid';
var newState = (ssids.contains(event.itemState.toString())) ? 'ON' : 'OFF';
var name = event.itemName.replace('SSID', '');
logger.info('{} presence is {}', name, newState);
var item = items.getItem('v'+name+'Phone');
if(item.state != newState) {
logger.info('Commanding {} to {}', item.name, newState);
item.sendCommand(newState);
}
else {
logger.info('{} is already {}', item.name, newState);
}
type: script.ScriptAction
Reset Powerbill
At the end of the billing month record the estimated power bill and start calculating the next month’s.
configuration: {}
triggers:
- id: "1"
configuration:
cronExpression: 0 0 0 9 * ? *
type: timer.GenericCronTrigger
conditions: []
actions:
- inputs: {}
id: "3"
configuration:
type: application/javascript;version=ECMAScript-2021
script: >
var logger = log('Reset Power Estimate');
logger.info("It's the first day of the electricity billing period, time to reset the running total.");
items.getItem('HomeEnergyMeter_LastMonthBillEstimate').postUpdate(items.getItem('HomeEnergyMeter_CurrentBillEstimate').state);
items.getItem('HomeEnergyMeter_ResetMeter').sendCommand('ON');
items.getItem('HomeEnergyMeter_CurrentBillEstimate').postUpdate(items.getItem('HomeEnergyMeter_Access').state);
type: script.ScriptAction