It might be possible to use the Timestamp Profile, Items | openHAB, and eliminate the part of the rule that keeps track of the time when the door was opened or closed. Just link the same Channel to a DateTime Item as is linked to the Switch and when the Channel triggers the DateTime will be updated with the current time.
Log out the value of state
immediately before appending “opened” or “closed”. Maybe it’s not what is expected. To avoid the possibility that the Item changed back between the time when the rule triggered and the state is retrieved from the Item, use newState
instead of triggeringItem.state
. That will give you the state the Item changed to regardless of what state it’s in now.
Does the timer always get created or always cancelled? I would assume always cancelled but add logging to confirm.
You can also make things simpler. Instead of testing for == OPEN || == ON in every if statement, normalize the state when you first retrieve it.
if(previousState == NULL || newState == NULL || newState == UNDEF) return;
...
val state = if(newState == OPEN || newState == ON) OPEN else CLOSED
Then you can just test for if(state == OPEN)
in the rest of the rule.
A clever trick I’ve seen is to use a number in front of the Item state names so you can simplify the time of day if statement to if(vTimeOfDay.state.toString >= "4")
and for example the time of day states are “4.EVENING”, “5.NIGHT”, “6.BED”. I don’t use this myself but if you have more than one condition like this it can be handy, particularly if using one of the new Time of Day implementations which allow for a different set of times of day based on the type of day.
Over all I don’t see anything wrong with this rule as written. More information is necessary through logging and experimentation.
And in fact, now that I look at it, why mess with storing OPEN and CLOSED in state
at all. Just put the string “opened” and “closed” in state
and you can eliminate one ternary operation. You’ll have to change the if statements to test for “opened” and “closed” though.
I wrote this rule a long time ago so I suppose it’s worth posting what I’m using now in OH 3. It’s a UI created rule using JavaScript as the Script Action.
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
script: |-
event.itemState.class != UnDefType.class
&& event.oldItemState.class != UnDefType.class
type: script.ScriptCondition
actions:
- inputs: {}
id: "3"
configuration:
type: application/javascript
script: >
var logger =
Java.type("org.slf4j.LoggerFactory").getLogger("org.openhab.model.script.Rules.Open
Door Reminder");
var OPENHAB_CONF = java.lang.System.getenv("OPENHAB_CONF");
load(OPENHAB_CONF+'/automation/lib/javascript/community/timerMgr.js');
load(OPENHAB_CONF+'/automation/lib/javascript/personal/alerting.js');
load(OPENHAB_CONF+'/automation/lib/javascript/personal/metadata.js')
this.timers = (this.timers === undefined) ? new TimerMgr() : this.timers;
var reminderGenerator = function(itemName, name, when, sendAlert, timers){
return function() {
if(items[itemName] != OPEN) {
logger.warn(itemName + " open timer expired but the door is " + items[itemName] + ". Timer should have been cancelled.");
}
else {
sendAlert(name + " has been open for " + when);
// Reschedule if it's night
var tod = items["TimeOfDay"].toString();
if(tod == "NIGHT" || tod == "BED") {
logger.info("Rescheduling timer for " + name + " because it's night");
timers.check(itemName, when, reminderGenerator(itemName, name, when));
}
}
}
}
logger.debug("Handling new door state for reminder: " + event.itemName + " = " + event.itemState);
if(event.itemState == CLOSED && this.timers.hasTimer(event.itemName)) {
logger.debug("Cancelling the timer for " + event.itemName);
this.timers.cancel(event.itemName);
}
else {
var name = getName(event.itemName);
var remTime = getMetadataValue(event.itemName, "rem_time");
if(remTime === null) {
logger.warn("rem_time metadata is not defined for " + event.itemName + ", defaulting to 60m");
remTime = "60m";
}
logger.debug("Creating a reminder timer for " + event.itemName + " for " + remTime);
timers.check(event.itemName, remTime, reminderGenerator(event.itemName, name, remTime, sendAlert, timers));
}
type: script.ScriptAction
It triggers when one of the doors opens or closes. There is a Script Condition that keeps the rule from running if the door changed from an UnDefType.
You can find timerMgr at GitHub - rkoshak/openhab-rules-tools: Library functions, classes, and examples to reuse in the development of new Rules. and it’s a library that essentially handles all the book keeping involved when one has one rule that generates a whole bunch of timers, in this case one timer per door.
alerting.js is a personal library that implements sendAlert
and sendInfo
functions which centralizes all my alerting and push notification stuff.
metadata.js is a personal library that I could post if desired that lets me set and read metadata from Items. I use this to set a different reminder time for each door so the front door reminds after 30 minutes, the garage door after 10 but the back door after 5 hours.
The rest should be relatively straight forward. When the door closes, any running timers are cancelled. When the door opens, the we get the human friendly name from Item metadata (instead of using the Map transformation), get the reminder time from metadata and set the timer. When the timer goes off generate the alert. If it’s night time reschedule the timer.
Keeping track of the time the door opened or closed is handled by the Timestamp profile.