Trigger alarm if one door contact of a group opens

Hello Community,
I use homeatic door contacts on my doors and windows for my alarm system. I built a group item for all the contacts

Group:Contact door_lock_alarm_sensors

My problem is that I want to react to each trigger from the door and windows contacts and not only to the aggregated group states. Example: one window is OPEN so the group item switches from CLOSED to OPEN. If I open another window no group trigger happens because the group changed it’s state with the first window. I want to react on each window which changes it’s state from CLOSED to OPEN.

Here you can see my rule:

//This rule is for alarming
// v0.2 20180217
import java.text.SimpleDateFormat
import java.util.Date

var SimpleDateFormat df = new SimpleDateFormat( "YYYY-MM-dd HH:mm:ss" )

rule "alarm"
when
  Item door_lock_alarm_sensors received update
  //Item door_lock_alarm_sensors changed from CLOSED to OPEN
then
  var String Timestamp = df.format( new Date() )
  logInfo( "FILE", "rule alarm started" )
  if (lock_frontdoor_state.state==OFF && alarm_state.state==ON) {
  // ON=unlocked OFF=locked
    if (door_lock_alarm_sensors.state==OPEN) {
      logInfo(Timestamp + " FILE", "caution alarm" )
      ibox_attic_color.sendCommand(new HSBType (new DecimalType(0),new PercentType(0),new PercentType(100)))
      stehlampe_wz_color.sendCommand(new HSBType (new DecimalType(0),new PercentType(100),new PercentType(100)))
      pushover(Timestamp + "caution alarm! " )
    }
  }
end

any ideas? thanks for helping.

you could either do as you started and query each item of a group distinct - or you can have some Group filters and stuff:

It’s a great resource in this Design Pattern and it’ll teach you how to use Groups intelligently.

Thanks for your help but I do not understand how to use the changed state of the contacts in a rule. All examples are similar like this:

when
    Item vGarageOpener1 changed or
    Item vGarageOpener2 changed or
    Item vFrontDoor changed or
    Item vBackDoor changed or
    Item vGarageDoor changed
then

but I want to use the group something like this:

when Item <groupname> one of the groupitems changed from CLOSED to OPEN
then

Sure you wan’t such a feature everyone does.

But unfortunately it is not yet in your openhab but available at the snapshot.

Either wait till it comes with the next stable release or update to snapshot.

at present you can only trigger:

when Item <groupname> received update

so this indicates, that “some item” within the group changed - but not necessarily the group itself. You can then do something like

    val door = gDoorSensors.members.filter[s|s.lastUpdate("mapdb") != null].sortBy[lastUpdate("mapdb")].last as ContactItem

which would then give you in the filter that item, which was last changed - but you’ll need persistence of all group-items for that.

I run into the same problem and made a not 100% working solution and needs more testing:

Here what I’ve done, sorry for some german comments in the rules:

Manage all with groups and group memberships.

// all active items to monitor/controll
Group:Number:SUM                gContactStatus      "gContactStatus [(%d)]"
// all items to monitor/controll, to get on overview about all item status that are open
Group:Number:SUM                gContactStatusALL   "gContactStatusAll [(%d)]"
// all proxy items to control membership of items in the group gContactStatus 
Group:Number:SUM                gContactOnOFF       "gContactOnOFF [(%d)]"

Then i configure the items, some are neded for debug.
coContactxx is the item to monitor
coContactxx_Helper is just for debug reasons and changing state of contacts
coContactxx_Proxy is used to enable disable the corresponsing coContactxx item to be in the group gContactStatus

Contact         coContact01          "Contact01 [%s]"                     (gContactStatus,gContactStatusALL)
Switch          coContact01_Helper   "Contact01 helper [%s]"
Contact         coContact02          "Contact02 [%s]"                     (gContactStatus,gContactStatusALL)
Switch          coContact02_Helper   "Contact02 helper [%s]"
Contact         coContact03          "Contact03 [%s]"                     (gContactStatus,gContactStatusALL)
Switch          coContact03_Helper   "Contact03 helper [%s]"
Contact         coContact04          "Contact04 [%s]"                     (gContactStatus,gContactStatusALL)
Switch          coContact04_Helper   "Contact04 helper [%s]"
Switch          coContact03_Proxy    "Contact03 einschalten [%s]"         (gContactOnOFF)
Switch          coContact04_Proxy    "Contact04 einschalten [%s]"         (gContactOnOFF)
Switch          coContact05          "Contact04 nochmal OPEN[%s]"
Switch          coContact06          "System Startup simulieren [%s]"
Switch          coContact10          "Loop through items and groups [%s]"

To put items into the group gContactStatus or remove them:

rule "gContactOnOFF changed"
    when
        Item gContactOnOFF changed
    then
        Thread::sleep(100)
        //logInfo("gContact","gContactOnOFF changed")
        //logInfo("gContact","######################")

        //ermitteln welcher SwitchProxy gedrückt wurde
        val gContactOnOFFChanged = gContactOnOFF.members.filter[l | l.lastUpdate !== null].sortBy[lastUpdate].last
        //logInfo("gContact","gContactOnOFFChanged.name: "+ gContactOnOFFChanged.name)

        // Ermitteln, wie der dazugehörige Switch heißt
        
        val gContactToChange=gContactStatusALL.members.filter[i | i.name==gContactOnOFFChanged.name.toString.substring(0,gContactOnOFFChanged.name.toString.indexOf('_'))].head as GenericItem
        //logInfo("gContact","gContactToChange.name:     "+ gContactToChange.name)

        // Switch muss hinzugefügt werden
        if(gContactOnOFFChanged.state == ON)
        {
            gContactStatus.addMember(gContactToChange)
            //logInfo("gContact","gContactStatus.addMember: "+gContactToChange.name)

            gContactToChange.addGroupName(gContactStatus.getName())
            //logInfo("gContact","gContactToChange.addGroupName: "+ gContactToChange.name)
           
            // Ein Item ermitteln, welches noch in der Gruppe ist
            // wird benötigt um die Anzahl richig zu berechnen
            val item = gContactStatus.members.take(1).head
            
            // Sollte der Contact schon OPEN sein, dann wird danach ein change event ausgelöst, dieses abfangen
            if(item.state==OPEN)
                ContactAddRemoveIndicator = true

            item.postUpdate(item.state.toString)

            // nachdem eventuell das event changed verarbeitet wurde den Merker wieder aufheben, sonst wird das erste richtige event nicht verarbeitet
            Thread::sleep(250)
            ContactAddRemoveIndicator = false               
        }

        // Switch muss entfernt werden        
        else
        {
            gContactStatus.removeMember(gContactToChange)
            //logInfo("gContact","gContactStatus.removeMember: " + gContactToChange.name)

            gContactToChange.removeGroupName(gContactStatus.getName())
            //logInfo("gContact","gContactToChange.removeGroupName: "+ gContactToChange.name)

            // Sollte der Switch schon ON sein, dass wird danach ein change event ausgelöst, dieses abfangen
            if(gContactToChange.state==OPEN)
            {
                ContactAddRemoveIndicator = true            
                //logInfo("gContact","gContactOnOFF ContactAddRemoveIndicator = true:")
            }
            
            // wird benötigt um die Anzahl richig zu berechnen
            val item = gContactStatus.members.take(1).head
            item.postUpdate(item.state.toString)

            // nachdem eventuell das event changed verarbeitet wurde den Merker wieder aufheben, sonst wird das erste richtige event nicht verarbeitet
            Thread::sleep(250)
            ContactAddRemoveIndicator = false               
        }
end

On every restart of OH or on every reload of item file the group membership must be restored

rule "system startup rules"
	when 
		System started
	then
		Thread::sleep(1000)
		logInfo("Logger","##################################### system startup rules started")
		swSwitch06.postUpdate(ON)
		coContact06.postUpdate(ON)
end

and this is used to do it:

var boolean ContactAddRemoveIndicator = false

// Beim Start von OH werden die Switchproxy-Schalter ausgewertet und für alle Switchews, welche nicht aktiv in der Gruppe sein sollen, diese entfernt
rule "gContact Gruppenzugehörigkeit initial setzen"
    when
        Item coContact06 changed to ON
    then
        // Gruppenmitgliedschaften wieder richig herstellen, gehen nach einem reload verloren
        gContactOnOFF.members.forEach[item |
           
            //logInfo("Logger","gContactOnOFFChanged: "+item.name + ": "+ item.state.toString)
            var gContactToChange=gContactStatusALL.members.filter[i | i.name==item.name.toString.substring(0,item.name.toString.indexOf('_'))].head as GenericItem
            if(item.state == ON)
            {
                gContactStatus.addMember(gContactToChange)
                //logInfo("gContact","OnSystemStart gContactStatus.addMember: "+gContactToChange.name)

                gContactToChange.addGroupName(gContactStatus.getName())
                //logInfo("gContact","OnSystemStart gContactToChange.addGroupName: "+ gContactToChange.name)
            }
            else
            {
                gContactStatus.removeMember(gContactToChange)
                //logInfo("gContact","OnSystemStart gContactStatus.addMember: "+gContactToChange.name)

                gContactToChange.removeGroupName(gContactStatus.getName())
                //logInfo("gContact","OnSystemStart gContactToChange.addGroupName: "+ gContactToChange.name)                
            }
        ]

        // Sollte der Contact schon OPEN sein, dann wird danach ein changed event ausgelöst, dieses abfangen
        var item = gContactStatus.members.take(1).head
        if(item.state==OPEN)
        {
            ContactAddRemoveIndicator = true
            //logInfo("gContact","OnSystemStart ContactAddRemoveIndicator = true:")
        }
        item.postUpdate(item.state.toString)

        // nachdem eventuell das event changed verarbeitet wurde den Merker wieder aufheben, sonst wird das erste richtige event nicht verarbeitet
        Thread::sleep(250)
        ContactAddRemoveIndicator = false          
        coContact06.postUpdate(OFF)
end

After all this work in done, a very simple rule can be used to get the trigger:

// ermitteln, ob ein Item der Gruppe geändert wurde
rule "gContactStatus changed"
    when
        Item gContactStatus changed
    then
        Thread::sleep(100)
        //logInfo("gContact","ContactAddRemoveIndicator: "+ ContactAddRemoveIndicator)
        if(ContactAddRemoveIndicator == false)
        {
            val item = gContactStatus.members.filter[l | l.lastUpdate !== null].sortBy[lastUpdate].last
            logInfo("gContact","########## changed: "+item.name+": "+item.state.toString)
        }
        else
            ContactAddRemoveIndicator = false
end

Some debug code, if needed:

rule "coContact10 Loop through groupitems"
when
	Item coContact10 changed to ON
then
    // may be required for persistence to catch up
    Thread::sleep(100)

	logInfo("Logger","LoopThroughGroupitems")
	logInfo("Logger","##########################################")

    //logInfo("voice", "gContactStatus.getMembers" + gContactStatus.getMembers)
    //logInfo("Logger","##########################################")
    logInfo("voice", "coContact01.getGroupNames" + coContact01.getGroupNames)
    logInfo("voice", "coContact02.getGroupNames" + coContact02.getGroupNames)
    logInfo("voice", "coContact03.getGroupNames" + coContact03.getGroupNames)
    logInfo("voice", "coContact04.getGroupNames" + coContact04.getGroupNames)

    logInfo("Logger","##########################################")
	logInfo("Logger","gContactStatus-List")
	gContactStatus.members.forEach[item |
		logInfo("Logger","gContactStatus Itemname: "+item.name + ": "+ item.state.toString)
	]

	coContact10.postUpdate(OFF)
end

rule "coContact01_Helper"
when
    Item coContact01_Helper changed to ON
then
    if(coContact01.state == OPEN)
        coContact01.postUpdate(CLOSED)
    else
        coContact01.postUpdate(OPEN)
    coContact01_Helper.postUpdate(OFF)
end

rule "coContact02_Helper"
when
    Item coContact02_Helper changed to ON
then
    if(coContact02.state == OPEN)
        coContact02.postUpdate(CLOSED)
    else
        coContact02.postUpdate(OPEN)
    coContact02_Helper.postUpdate(OFF)
end

rule "coContact03_Helper"
when
    Item coContact03_Helper changed to ON
then
    if(coContact03.state == OPEN)
        coContact03.postUpdate(CLOSED)
    else
        coContact03.postUpdate(OPEN)
    coContact03_Helper.postUpdate(OFF)
end

rule "coContact04_Helper"
when
    Item coContact04_Helper changed to ON
then
    if(coContact04.state == OPEN)
        coContact04.postUpdate(CLOSED)
    else
        coContact04.postUpdate(OPEN)
    coContact04_Helper.postUpdate(OFF)
end

And the sitemap:

	Frame label="Group notification Contact" {
		Switch item=coContact10
		Switch item=coContact06
		Text item=gContactStatus
		Text item=gContactStatusALL
		Text item=coContact01
		Text item=coContact02
		Text item=coContact03
		Text item=coContact04
		Switch item=coContact01_Helper
		Switch item=coContact02_Helper
		Switch item=coContact03_Helper
		Switch item=coContact04_Helper
		Switch item=coContact03_Proxy
		Switch item=coContact04_Proxy
		Switch item=coContact05

	}	

existing problems only can be solved if the member of group ==> triggeringItem PR is implemented:

If two or more items change to fast to each other, this connot be recognices by the rule because of to slow persistance.
This line give not on every change the right item

val item = gContactStatus.members.filter[l | l.lastUpdate !== null].sortBy[lastUpdate].last

I hope no one kills me about my code. But I want to have very flexible code and not so much similar code for a lot of same items/things.

Comments are welcome.

thanks for your answer, I will try your solution soon. I am from germany too, so your comments are welcome!

If an example for switches is needed, I have one too, is a little bit more complex.

:slight_smile: I don’t understand your example. What do you mean with the example for switches?

@un1k
This example works only for contact items not for switch items or other items. Some modifications are needed to use for switches. So if needed I can post.