Automation #7: Detect a particular sequence of events

Tags: #<Tag:0x00007f745a5e7240> #<Tag:0x00007f745a5e7010> #<Tag:0x00007f745a5e6f20>

Hello guys!

In this example, I will show a way of how to detect a particular sequence of events (other solutions can be possible). I created two simple rules to detect when I arrive or leave home. More complex rules can be created, if more than one person lives in the house and if you consider other conditions.

Automation #7: Detect a particular sequence of events
In summary, the pattern to detect when I arrive home is represented by the following sequence of events:
(1) Front_Door_Motion --> (2) Front_Door_Contact --> (3) Entrance_Hall_Motion

and the pattern to detect that I’m leaving home is:
(1) Entrance_Hall_Motion --> (2) Front_Door_Contact --> (3) Front_Door_Motion

Note: I’m only considering that all three events occur within a time window of 60 seconds.

Items

Contact Front_Door_Contact     ...
Switch  Entrance_Hall_Motion   ...
Switch  Front_Door_Motion       ...

Rules DSL Implementation

// Initialize variables to avoid null checks
// These variable will save the last time in which they changed to the desired state
var lastDoorOpen = now().minusHours(24)
var lastEHallMotion = now().minusHours(24)
var lastFDoorMotion = now().minusHours(24)

rule "(DSL) Front Door Opened"
when
	Item Front_Door_Contact changed to OPEN
then
	lastDoorOpen = now()
end

rule "(DSL) Motion Detected - Entrance Hall"
when
	Item Entrance_Hall_Motion changed to ON
then
    lastEHallMotion = now()

    // discard events older than 60 secs
    if( lastFDoorMotion.isBefore(lastEHallMotion.minusSeconds(60)))   return;

    if(lastEHallMotion.isAfter(lastDoorOpen) && lastDoorOpen.isAfter(lastFDoorMotion)) {
         // code logic for arriving home
    }
end

rule "(DSL) Motion Detected - Front Door "
when
	Item Front_Door_Motion changed to ON
then
    lastFDoorMotion = now()

    // discard events older than 60 secs
    if( lastEHallMotion.isBefore(lastFDoorMotion.minusSeconds(60)))  return;

    if(lastFDoorMotion.isAfter(lastDoorOpen) && lastDoorOpen.isAfter(lastEHallMotion)) {
       // code logic for leaving home
    }
end

Jython Implementation

from core.rules import rule
from core.triggers import when
from java.time import ZonedDateTime as ZDateTime

lastDoorOpen = ZDateTime.now().minusHours(24)
lastEHallMotion = ZDateTime.now().minusHours(24)
lastFDoorMotion = ZDateTime.now().minusHours(24)

@rule("(Py) Front Door Opened")
@when("Item Front_Door_Contact changed to OPEN")
def front_door_opened(event):
    global lastDoorOpen
    lastDoorOpen = ZDateTime.now()

@rule("(Py) Motion Detected - Entrance Hall")
@when("Item Entrance_Hall_Motion changed to ON")
def entrance_hall_motion(event):
    global lastEHallMotion, lastFDoorMotion
    lastEHallMotion = ZDateTime.now()

    if lastFDoorMotion.isBefore(lastEHallMotion.minusSeconds(60)):
        return
    
    if lastEHallMotion.isAfter(lastDoorOpen) and lastDoorOpen.isAfter(lastFDoorMotion):
        # code logic for arriving home


@rule("(Py) Motion Detected - Front Door")
@when("Item Front_Door_Motion changed to ON")
def front_door_motion(event):
    global lastEHallMotion, lastFDoorMotion
    lastFDoorMotion = ZDateTime.now()

    if lastEHallMotion.isBefore(lastFDoorMotion.minusSeconds(60)):
        return
    
    if lastFDoorMotion.isAfter(lastDoorOpen) and lastDoorOpen.isAfter(lastEHallMotion):
        # code logic for leaving Home

Happy coding!
Humberto

Note :

  • Suggestions or recommendations to improve the implementation are welcome!
  • Do you have more complex automations that shares the same logic of this example? Please share it :slightly_smiling_face:

Other automation examples

9 Likes

Hi Humberto,
thanks for sharing this solution!

I´ve rebuild my welcome rule with your example.
It´s working pretty good but there´s still something that´s a little bit unclear.

I´m using two Homematic sensors for the door and the lock and one Fibaro motion sensor in the hallway.
I would like to check for the sequence: Lock opened -> door opened -> motion detected
That´s already working fine, but the rule fires again when there´s another motion detected 60 seconds after the door was opened.
For my understanding, i just need to reduce the for // discard events older than 60 secs

Or is there any other way?

var lastDoorOpen = now().minusHours(24)
var lastLockOpen = now().minusHours(24)
var lastMotionHall = now().minusHours(24)
val String ruleIdentifier = "WelcomeHome"

rule "Schloss geöffnet"

when
    Item itmTuerschloss changed from CLOSED to OPEN
then
    lastLockOpen = now()
    logInfo(ruleIdentifier, "Schloss geöffnet " + lastLockOpen)
end

rule "Tür geöffnet"

when
    Item itmTuer_Flur changed from CLOSED to OPEN
then
    lastDoorOpen = now()
    logInfo(ruleIdentifier, "Tür geöffnet " + lastDoorOpen)
end

rule "Begrüßung"

when
    Item itmFIBmotion2 changed to 1
then
    lastMotionHall = now()
    logInfo(ruleIdentifier, "Bewegung erkannt " + lastMotionHall)

    if(lastMotionHall.isBefore(lastDoorOpen.minusSeconds(60)))
    {
        return;
    }

    val CurrentHour = now.getHourOfDay
    val Night = (CurrentHour >22 || CurrentHour <8)

    if(lastMotionHall.isAfter(lastDoorOpen) && lastDoorOpen.isAfter(lastLockOpen))
    {
        // complex rule to check if someones home and let an Echo Dot say something
    }
end

Hi @Bredmich

Happy to hear that my post helped you :slightly_smiling_face:

If your motion sensor checks for movement more frequently (< 60 secs), indeed you should consider reducing the time window. In my case after a movement is detected the motion sensor goes to sleep for 60 secs.

Maybe there is a better solution than mine. I’m not an experienced OpenHab user :wink:

Yeah my Fibaro has an Motion Sensor Blind Time of 15 seconds.

So i would need to change the check at the start to

if(lastMotionHall.isBefore(lastDoorOpen.minusSeconds(15)))

correct?

Thanks for your help and kind regards

Yes, if your door contact was opened more than 15 secs than your Fibaro Motion Event, then it should not be considered for that rule

Let me know if that worked for you

Have a nice day
:belgium:

It´s not working :frowning:
The rule still fires.

2019-09-12 17:06:08.739 [INFO ] [e.smarthome.model.script.WelcomeHome] - Unlocked: 2019-09-12T17:06:07.000+02:00
2019-09-12 17:06:17.379 [INFO ] [e.smarthome.model.script.WelcomeHome] - Door opened: 2019-09-12T17:06:17.373+02:00
2019-09-12 17:06:19.361 [INFO ] [e.smarthome.model.script.WelcomeHome] - Motion detected: 2019-09-12T17:06:19.350+02:00
2019-09-12 17:06:19.502 [INFO ] [e.smarthome.model.script.WelcomeHome] - Welcome
2019-09-12 17:10:25.547 [INFO ] [e.smarthome.model.script.WelcomeHome] - Motion detected: 2019-09-12T17:10:25.509+02:00
2019-09-12 17:10:25.725 [INFO ] [e.smarthome.model.script.WelcomeHome] - Welcome
2019-09-12 17:12:10.964 [INFO ] [e.smarthome.model.script.WelcomeHome] - Motion detected: 2019-09-12T17:12:10.949+02:00
2019-09-12 17:12:11.137 [INFO ] [e.smarthome.model.script.WelcomeHome] - Welcome
2019-09-12 17:12:59.234 [INFO ] [e.smarthome.model.script.WelcomeHome] - Door opened: 2019-09-12T17:12:59.225+02:00
2019-09-12 17:12:59.839 [INFO ] [e.smarthome.model.script.WelcomeHome] - Motion detected: 2019-09-12T17:12:59.835+02:00
2019-09-12 17:12:59.963 [INFO ] [e.smarthome.model.script.WelcomeHome] - Welcome

So the first check doesn´t stop the execution

    if(lastMotionHall.isBefore(lastDoorOpen.minusSeconds(15)))
    {
        return;
    }

The second still fires because it´s still correct that motion was detected after the door was opened and after the lock was unlocked.

    if(lastMotionHall.isAfter(lastDoorOpen) && lastDoorOpen.isAfter(lastLockOpen))
    {
        // Welcome
    }

So i added another check to only do the welcome routine once.

var lastWelcome = now().minusHours(24)

    if(lastMotionHall.isBefore(lastDoorOpen.minusSeconds(15)) || lastMotionHall.isAfter(lastWelcome))
    {
        return;
    }

I’m busy now, but I will try simulate your scenario later today or tomorrow morning

1 Like

@Bredmich please I accept my apologies, I didn’t read well your initial question and I gave you a wrong answer.

In your case, I think you have to add an extra condition to discard Motion events occurred within the time window of the 60 secs.

So it should be something like this

if(lastMotionHall.isBefore(now().minusSeconds(60)))
{
lastMotionHall = now()
logInfo(ruleIdentifier, "Bewegung erkannt " + lastMotionHall)

}

I haven’t tested this solution but should be something like that… I used the same approach to avoid multiple notifications from the doorbell Automation #1: Doorbell notification

If that doesn’t work let me know and I will look it later

@Bredmich did that :point_up_2: work?

Can´t tell you until tomorrow.

No problem, I hope that can fix your rule

Had some other issues today and couldn´t test the rule.
My motion sensor wasn´t correctly connected to the migrated oH instance…

No problem :+1:

Just for my understanding.
When does oH use the initial var = parameters?
Everytime the rule fires or just when the rule is loaded into the runtime?
2019-09-15 02:05:37.613 [INFO ] [el.core.internal.ModelRepositoryImpl] - Refreshing model 'FlurWelcome.rules'

Because my check to ensure the rule only fires once made the rule stop before even firing.
I changed the initial parameter for my lastWelcome to var lastWelcome = now().plusHours(24) to ensure the check lastMotionHall.isAfter(lastWelcome) fires correctly.

Only when the rule is loaded