Door Sensor Rule by Rich

Hi All & Rich

This rule has worked very well using contact item types, but recently I’ve got a gate opener which uses a Switch to determine if its open or closed, OPEN = ON, CLOSED=OFF

I translate these and display them fine, but the rule below keeps showing the gate as being closed in the notifications (using string builder) irrespective of if its open or closed.

Any suggestions on where to look? I changed the state == OPEN and added || state == ON but it didnt make a difference :frowning: Its ONLY the notifications which show incorrect. The actual item state being displayed in the sitemap appears just fine

rule "Keep track of the last time a door was opened or closed"
  Member of gDoorSensors changed
  if(previousState == NULL) return;

  val name =
  val state = triggeringItem.state

  // Update the time stamp
  postUpdate(name+"_LastUpdate", now.toString)

  // Set the timer if the door is open, cancel if it is closed
  if(state == OPEN || state == ON) sendCommand(name+"_Timer", "ON")
  else postUpdate(name+"_Timer", "OFF")

  // Set the message
  val msg = new StringBuilder
  msg.append(transform("MAP", "", name) + " was ")
  msg.append(if(state == OPEN || state == ON) "opened" else "closed")

  var alert = false
  if(vTimeOfDay.state.toString == "NIGHT" || vTimeOfDay.state.toString == "BED" || vTimeOfDay.state.toString == "EVENING") {
    msg.append(" and it is Night")
    alert = true
  if(gPresenceSensors.state == OFF) {
    msg.append(" and no one is Home")
    alert = true
  // Alert if necessary
  if(gPresenceSensors.state == ON && state == OPEN) {
  // Alert if necessary
  if(alert && gPresenceSensors.state == OFF) {
  // Log the message if we didn't alert
  else {
    logInfo("Door Sensor", msg.toString)

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.

  - id: "1"
      groupName: DoorsStatus
      state: OPEN
    type: core.GroupStateChangeTrigger
  - id: "4"
      groupName: DoorsStatus
      state: CLOSED
    type: core.GroupStateChangeTrigger
  - inputs: {}
    id: "2"
    label: The door's state didn't change to an UnDefType
      type: application/javascript
      script: |-
        event.itemState.class != UnDefType.class
        && event.oldItemState.class != UnDefType.class
    type: script.ScriptCondition
  - inputs: {}
    id: "3"
      type: application/javascript
      script: >
        var logger =
        Door Reminder");

        var OPENHAB_CONF = java.lang.System.getenv("OPENHAB_CONF");




        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") {
      "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);

        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.

Hi Rich

Thats an informative post but a bit above my experience and paygrade :stuck_out_tongue: Ill try and decipher it! thanks!

seems newState isnt available in 2.4.0

17:52:27.594 [ERROR] [untime.internal.engine.RuleEngineImpl] - Rule 'Keep track of the last time a door was opened or closed': The name 'newState' cannot be resolved to an item or type; line 7, column 30, length 8

That’s correct; I don’t think timestamp profiles are either.

You’ve been doing this long enough to know to log out key variables for problematic rules :wink:
The value of that state variable looks important here, so does gPresenceSensors.state

Not really. I’ve always found the rules like rocket science if you’re not a programmer.

That’s why there are new rule engines in OH3. For example Blocky which you can use just by drag and drop:

The tl;dr is you need to turn the Switch Item off shortly after it goes on. By shortly I mean half a second.

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.