Motion detection with several sensors and door

Hello, I am trying to setup a system for motion detection. I am starting small, and trying the following logic:

If sensor A is triggered OR (Sensor B is triggered AND door is locked), change input boleean to on. Wait 60 seconds, if neither is triggered again within the 60 seconds, turn the boleean off. If they are triggered, restart the 60 second countdown.

I have based it on this example: https://github.com/home-assistant/appdaemon/blob/dev/conf/example_apps/motion_lights.py

For testing at the moment i have implmented the closed door sensor as a input boleean i can turn on or off.

The code as it is seems to activate the input boleean when one of the sensors go on, but it is never turned off, and also the “state of the door” input boleean doesn’t seem to make a difference.


    def initialize(self):
        self.handle = None
        self.listen_state(self.motion, "binary_sensor.motion_a", new = "on")
        self.listen_state(self.motion, "binary_sensor.motion_b", new = "on")
    
    def motion(self, entity, attribute, old, new, kwargs):
        if entity == "motion_b":
            if self.get_state("input_boolean.door_closed") == "on":
                self.turn_on("input_boolean.bev_mot_kontor")
                self.cancel_timer(self.handle)
                self.handle = self.run_in(self.motionoff, 10)
        else:
            self.turn_on("input_boolean.bev_mot_kontor")
            self.cancel_timer(self.handle)
            self.handle = self.run_in(self.motionoff, 10)

    def motionoff(self, kwargs):
        self.turn_off("input_boolean.bev_mot_kontor")
    def cancel(self):
        self.cancel_timer(self.handle)

Take a look at the custom component entity controller, it can do what you want (and more).

Thanks, that looks like a quite useful component. But it doesn’t seem to be able to solve my particular problem, as it can’t have conditions for just one of the two sensors?

Yes you can, you just need to create a template sensor with the conditions.

Thanks, you are right, forgot about that, not the cleanest solution, but it works, thanks a lot for your help.

Your “if” statement in the “motion” function is never triggered, because you only wrote “motion_b” instead of the full entity name “binary_sensor.motion_b”.

It should be:

def motion(self, entity, attribute, old, new, kwargs):
        if entity == "binary_sensor.motion_b":

Currently your “motion” function will always go the “else” route.

And what is the purpose of the “cancel” function?

Thanks :slight_smile:

I don’t know what purpose it actually serves, but this is what i am trying to achieve with it:

If motion A activates, and then Motion B 5 seconds later, the motion A command will run “motionoff” 5 seconds after B has triggered (10 seconds after A), I want the countdown to restart every time the motion is re-triggered. That is why every retrigger first cancels the old countdown, and starts a new one.

Did you get any errors in the logs? Because I think that you first need to check if a timer exists before you try to cancel it, otherwise the “self.cancel_timer” function will probably trigger an error, because there is no timer to cancel.

e.g.

> if self.handle:
>     self.cancel_timer(self.handle)
>     .....

You can delete the “cancel” function, you don’t need it.

And could you please elaborate on what you want to achieve? I’m a bit confused with the scenario you are describing here.

Lets say I didn’t have the cancel, and motion A triggered, and 8 seconds later motion B triggered. The “motionoff”-function would then run 2 seconds after that. That is not wanted. I want the motionoff to only run if neither motionA nor motion B has triggered for 10 seconds.

You are misunderstanding me :slight_smile: you don’t need the cancel function which you defined at the end.

def cancel(self):

See the full example below. I didn’t test it.

def initialize(self):
    self.handle = None
    self.listen_state(self.motion, "binary_sensor.motion_a", new="on")
    self.listen_state(self.motion, "binary_sensor.motion_b", new="on")


def motion(self, entity, attribute, old, new, kwargs):
    if entity == "binary_sensor.motion_b":
        if self.get_state("input_boolean.door_closed") == "on":
            self.turn_on("input_boolean.bev_mot_kontor")
            if self.handle:
                self.cancel_timer(self.handle)
            self.handle = self.run_in(self.motionoff, 10)
    else:
        self.turn_on("input_boolean.bev_mot_kontor")
        if self.handle:
            self.cancel_timer(self.handle)
        self.handle = self.run_in(self.motionoff, 10)


def motionoff(self, kwargs):
    self.turn_off("input_boolean.bev_mot_kontor")

Thanks, can i ask what the “if self handle” is doing? Just checking if the timer exists?

It checks if a timer already exists. Let’s say you trigger motion sensor A for the first time, without the “if self.handle”, your “self.cancel_timer” function will result in an error because there is no timer (self.handle) to cancel.