Today I want you present my solution to monitor windows in a room and switch off the heating actuator if at least one window stays open for more than three minutes.
These are the assumptions on my solution:
- Working on the openHAB 4.x platform
- Using the file based rules of the JavaScript automation
- Each monitored room has one ore more window contact sensors telling if the window is opened or closes
- Each monitored room has a heating actuator that can be disabled using its lock item
- Works on a group item that goes to
ON
if a least one windows in the room is opened and goes back toOFF
if all windows in the room are closed - All rules are placed into one JavaScript file
- Uses the
openhab
library
For each room there is one rule that monitors the group of windows and acts upon them. Here is the example of one room:
"use strict";
const { rules, triggers, items, cache } = require('openhab');
rules.JSRule({
id: "Lounge Window Monitoring",
name: "Fenster in der Lounge überwachen",
description: "Überwacht die Fenster in der Lounge zum Abschalten der Heizungsaktoren",
triggers: [
triggers.ItemStateUpdateTrigger("Lounge_Sicherheit_Fensterkontakte")
],
tags: [
"Lounge",
"Fensterüberwachung"
],
execute: (event) => {
enhanceEvent(event, "Lounge_Sicherheit_Fensterkontakte")
const windowContact = items.getItem(event.itemName)
const heatingLock = items.getItem("Lounge_Klima_HVAC_Sperren")
processWindowContactForHeatingLock(event, windowContact, heatingLock)
}
})
The function processWindowContactForHeatingLock(event, windowContact, heatingLock)
processes the update on the window group. It sets a timer when at least one window is opened and saves the state of the heating lock item.
If all windows are closed before the timer expires, the time is simply cancelled. If at least one window in the room is still open, when the timer expires, the lock item is set to ON
causing the heating actor to switch of the heating of the room.
If all windows are closed and the timer has expired, the previous state of the lock item is restored.
The state of the timer and the state of the lock item are stored in the private cache. This is due to the fact that I placed all rules into one file and the context of the script file is shared between the rules.
function processWindowContactForHeatingLock(event, windowContact, heatingLock) {
if (cache.private.exists(windowContact.name) == false) {
cache.private.put(windowContact.name, new Object())
console.info("Created new context for item", windowContact.name)
}
const context = cache.private.get(windowContact.name)
const payload = event.payload
if (payload.value === "OPEN") {
// Reset old timer after window was opened
resetTimer(context, windowContact, heatingLock)
// Schedule a new timer
saveState(context, heatingLock)
scheduleTimer(context, windowContact, heatingLock)
} else {
// Reset timer after window was closed
resetTimer(context, windowContact, heatingLock)
restoreState(context, heatingLock)
}
}
function scheduleTimer(context, windowContact, heatingLock) {
var scheduledTime = time.ZonedDateTime.now().plusSeconds(180);
context.timer = actions.ScriptExecution.createTimer(scheduledTime, function() {
switchOffHeating(context, windowContact, heatingLock);
});
console.debug("Scheduled timer for", windowContact.name)
}
function resetTimer(context, windowContact, heatingLock) {
if (context.timer != null) {
context.timer.cancel()
context.timer = undefined
console.debug("Cancelled timer for", windowContact.name)
}
}
function saveState(context, heatingLock) {
context.oldState = heatingLock.state
console.debug("Saved state", context.oldState, "for lock item", heatingLock.name)
}
function restoreState(context, heatingLock) {
if (context.oldState != null) {
heatingLock.sendCommand(context.oldState)
console.debug("Restored state", context.oldState, "for lock item", heatingLock.name)
context.oldState = null
}
}
function switchOffHeating(context, windowContact, heatingLock) {
if (windowContact.state === "NULL" || windowContact.state === "OPEN") {
heatingLock.sendCommand("ON")
console.info("Switched on heating lock", heatingLock.name)
} else {
console.info("Ignoring timer for heating lock", heatingLock.name)
}
context.timer.cancel()
context.timer = null
}
Your thoughts on this?