Hello
One more example to end the week
This automation is not final or complete, and probably there is a way to make more generic. My aim is to share examples that maybe can help new members starting with HA+AppDaemon.
Suggestions or recommendations to improve the implementation are welcome!
Sometimes we need to fire a notification or activate some scene when a particular sequence of events occur.
This example showcases a way of how to detect a particular sequence of events (other solutions can be possible). I created a simple App that detects when I arrive or leave home. More complex automation can be created, if more than one person lives in the house and if you consider other conditions.
App #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:
Front Door Motion = ON —> Front Door Contact = ON —> Entrance Hall Motion = ON
and the pattern to detect that I’m leaving home is:
Entrance Hall Motion = ON —> Front Door Contact = ON —> Front Door Motion = ON
Note:
After the feedback, received by @clyra, @ReneTode, and @swiftlyfalling I updated my code to be more generic. Now, instead of detecting the sequence of events from 3 sensors (original code) this version can detect an N sequence of events. Like the original code, this version considers 2 action triggers (i.e., Arriving and Leaving) based on the particular order of the events. The App is expecting that the TRIGGER#1 will be the first element of the sensors
list defined in the home_presence.yaml
, and the TRIGGER#2 will be the last one.
Guys (@clyra, @ReneTode, @swiftlyfalling)! thanks again for your feedback
Here is the final version of my app:
Entities
binary_sensor.entrance_hall_motion
binary_sensor.front_door_contact
binary_sensor.front_door_motion
home_presence.yaml
home_presence:
module: home_presence
class: HomePresence
sensors: # order matters (min 2 sensors)
- name: binary_sensor.front_door_motion #1
state: "on"
- name: binary_sensor.front_door_contact #2
state: "on"
- name: binary_sensor.entrance_hall_motion #3
state: "on"
time_delta: 60 # seconds
home_presence.py
import appdaemon.plugins.hass.hassapi as hass
from datetime import datetime, timedelta
class HomePresence(hass.Hass):
def initialize(self):
self.log('initializing ...')
sensors = list(self.args["sensors"])
trigger1 = sensors[0]
trigger2 = sensors[-1]
default_time = datetime.now() - timedelta(hours= 2)
# init times dictionary
self.times = {trigger1["name"]: default_time, trigger2["name"]: default_time }
# subscribe and register callbacks for both triggers
self.listen_state(self.on_trigger1, trigger1["name"], new= trigger1["state"])
self.listen_state(self.on_trigger2, trigger2["name"], new= trigger2["state"])
self.log(f'subscribed to trigger1: {trigger1["name"]} with state: {trigger1["state"]}')
self.log(f'subscribed to trigger2: {trigger2["name"]} with state: {trigger2["state"]}')
# remove trigger sensors
del sensors[0]
del sensors[-1]
# subscribe and register callbacks for the remaining sensors
for sensor in sensors:
sensor_name = sensor["name"]
self.times[sensor_name] = default_time
self.listen_state(self.on_sensor_update, sensor_name, new= sensor["state"])
self.log(f'subscribed to: {sensor_name} with state: {sensor["state"]}')
def on_trigger1(self, entity, attribute, old, new, kwargs):
self.log('on_trigger1 ...')
last_update_trigger1 = datetime.now()
last_update_trigger2 = self.times[self.args["sensors"][-1]["name"]]
self.times[entity] = last_update_trigger1
# stop if the the last motion detected by the trigger2 is older than TIME_DELTA sec
if last_update_trigger2 < (last_update_trigger1 - timedelta(seconds= int(self.args["time_delta"]))):
return
sensors = self.args["sensors"]
if all((lambda i: self.times[sensors[i]["name"]] > self.times[sensors[i+1]["name"]]) for i in sensors):
self.log('leaving home ....')
# code logic for leaving home
def on_trigger2(self, entity, attribute, old, new, kwargs):
self.log('on_trigger2 ...')
last_update_trigger1 = self.times[self.args["sensors"][0]["name"]]
last_update_trigger2 = datetime.now()
self.times[entity] = last_update_trigger2
# stop if the the last motion detected by the trigger1 is older than TIME_DELTA sec
if last_update_trigger1 < (last_update_trigger2 - timedelta(seconds= int(self.args["time_delta"]))):
return
sensors = self.args["sensors"]
if all((lambda i: self.times[sensors[i]["name"]] < self.times[sensors[i+1]["name"]]) for i in sensors):
self.log('arriving home ...')
# code logic for arriving home
def on_sensor_update(self, entity, attribute, old, new, kwargs):
self.log('on_sensor_update ...')
self.times[entity] = datetime.now()
Happy coding!
Humberto
Previous post: App #7: Boiler Temperature Alert
Next post: ?