[SOLVED] Openhab 2 Contact Sensor Time Rule

To make this more generic you can use something like what I use. I think I’ve posted this in a DP but I can’t remember so just reposting it here.

Group:Contact:OR(OPEN,CLOSED) gDoorSensors "The doors are [MAP(en.map):%s]" <door>
Group:Switch:OR(ON, OFF) gDoorsTimers

Contact vGarageOpener1 "Garage Door Opener 1 is [MAP(en.map):%s]" <garagedoor> (gDoorSensors)
Switch vGarageOpener1_Timer (gDoorsTimers, gResetExpire) { expire="1h,command=OFF" }

Contact vGarageOpener2 "Garage Door Opener 2 is [MAP(en.map):%s]" <garagedoor> (gDoorSensors)
Switch vGarageOpener2_Timer (gDoorsTimers, gResetExpire) { expire="1h,command=OFF" }

Contact vFrontDoor "Front Door is [MAP(en.map):%s]" <door> (gDoorSensors)
Switch vFrontDoor_Timer (gDoorsTimers, gResetExpire) { expire="1h,command=OFF" }

Contact vBackDoor "Back Door is [MAP(en.map):%s]" <door> (gDoorSensors)
Switch vBackDoor_Timer (gDoorsTimers, gResetExpire) { expire="1h,command=OFF" }

Contact vGarageDoor "Garage Door is [MAP(en.map):%s]" <door> (gDoorSensors)
Switch vGarageDoor_Timer (gDoorsTimers, gResetExpire) { expire="1h,command=OFF" }

Pay special attention to the Group memberships and the naming conventions. Not shown is gResetExpire which is used in a System started rule to retrigger the expire bindings on OH restart.

val logName = "entry"

rule "Set door timer"
when
  Member of gDoorSensors changed
then
  if(previousState == NULL) return; // we don't care about changes from NULL

  val name = triggeringItem.name
  val state = triggeringItem.state

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

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

  var alert = false
  if(vTimeOfDay.state.toString == "NIGHT" || vTimeOfDay.state.toString == "BED") {
    msg.append(" and it is night")
    alert = true
  }
  if(vPresent.state == OFF) {
    msg.append(" and no one is home")
    alert = true
  }

  // Alert if necessary
  if(alert){
    msg.append("!")
    aAlert.sendCommand(msg.toString)
  }
  // Log the message if we didn't alert
  else {
    logInfo(logName, msg.toString)
  }
end

rule "Timer expired for a door"
when
  Member of gDoorsTimers received command OFF
then
  val doorName = transform("MAP", "en.map", triggeringItem.name)

  aAlert.sendCommand(doorName + " has been open for over an hour")

  if(vTimeOfDay.state.toString == "NIGHT" || vTimeOfDay.state.toString == "BED") {
        triggeringItem.sendCommand(ON) // reschedule the timer
  }
end

Theory of Operation:
Create a Timer for each sensor named using Design Pattern: Associated Items and place them in Groups.

When any of the door sensors changes state the first Rule triggers. This Rule sets the Expire timer if the door is OPEN and cancels the timer if it is CLOSED. See Design Pattern: Expire Binding Based Timers. We construct the name of the Timer Item using Associated Items naming conventions.

Finally, we construct the message. We only alert the users of the door opening/closing at night time (see Design Pattern: Time Of Day). Alerting is centralized using Design Pattern: Separation of Behaviors.

The second rule gets triggered when any of the door Timers goes off (i.e. receives command OFF). We use Design Pattern: Human Readable Names in Messages to convert the Item name into something more human friendly and then send the alert. If it is night time we reschedule the alert to go off again. During the day we only get the one alert.

An approach like this will work with all your door Items with just these two Rules and you can add new doors just by adding them to the right Group.

1 Like