From 7305d719ee671aba392982944f7d2e6a2cc4bb0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Lov=C3=A9n?= Date: Mon, 5 Aug 2019 13:39:03 +0200 Subject: [PATCH] Initial work on a presence tracker. Inspired by @helto4real --- appdaemon/apps/presence.py | 79 ++++++++++++++++++++++++++++++ appdaemon/apps/presence.yaml | 37 ++++++++++++++ lovelace/views/dashboard_view.yaml | 57 +++++++++++++++++++-- packages/presence.yaml | 37 ++++++++++++++ 4 files changed, 206 insertions(+), 4 deletions(-) create mode 100644 appdaemon/apps/presence.py create mode 100644 appdaemon/apps/presence.yaml create mode 100644 packages/presence.yaml diff --git a/appdaemon/apps/presence.py b/appdaemon/apps/presence.py new file mode 100644 index 0000000..d62c471 --- /dev/null +++ b/appdaemon/apps/presence.py @@ -0,0 +1,79 @@ +from entities import Entities +from timers import Timers + +class Presence(Timers, Entities): + + HOME = 'home' + AWAY = 'not_home' + ARRIVED = 'just_arrived' + LEFT = 'just_left' + TIMEOUT = 300 + + def initialize(self): + super().initialize() + + attr = self.args.get('attributes', {}) + + self.register_entity('tracker', self.args['name'], managed=True, default="not_home", attributes=self.args.get('attributes',{})) + self.tracker = self.e['tracker'] + if(self.tracker.state in [self.ARRIVED, self.LEFT]): + self.tracker.state = self.AWAY + + self.devices = [] + for i,d in enumerate(self.args['devices']): + name = f"device{i}" + self.register_entity(name, d); + self.e[name].listen(self.update, {"name": name}) + self.devices.append(self.e[name]) + + self.update() + + def update(self, old=None, new=None, kwarg=None): + if not old or isinstance(old, str): + timeout = False + elif old is None: + timeout = False + else: + timeout = old.get('trigger', False) + new_state = self.is_home() + old_state = self.tracker.state + if new_state: + if old_state == self.AWAY: + self.run_in('timeout', self.update, self.TIMEOUT, trigger = "timeout") + state = self.ARRIVED + elif old_state == self.LEFT: + self.cancel_timer('timeout') + state = self.HOME + elif old_state == self.ARRIVED and timeout: + state = self.HOME + else: + state = old_state + else: + if old_state == self.HOME: + self.run_in('timeout', self.update, self.TIMEOUT, trigger = "timeout") + state = self.LEFT + elif old_state == self.ARRIVED: + self.cancel_timer('timeout') + state = self.AWAY + elif old_state == self.LEFT and timeout: + state = self.AWAY + else: + state = old_state + + self.tracker.state = state + self.tracker.push() + + def is_home(self): + for d in self.devices: + if d.domain == 'device_tracker': + if d.attr['source_type'] in ['router']: + if d.state == 'home': + return True + if d.domain == 'sensor': + if d.state != 'unknown' and int(d.state) > 0: + return True + + return False + + + diff --git a/appdaemon/apps/presence.yaml b/appdaemon/apps/presence.yaml new file mode 100644 index 0000000..c0c0c26 --- /dev/null +++ b/appdaemon/apps/presence.yaml @@ -0,0 +1,37 @@ +presence_thomas: + module: presence + class: Presence + global_dependencies: + - entities + - timers + dependencies: + - entity_manager + + name: device_tracker.thomas_presence + attributes: {} + + devices: + - device_tracker.thomas_iphone_beta + - device_tracker.thomas_iphone_2 + - sensor.thomas_iphone_bt + - sensor.thomas_iphone_bt2 + +presence_anneli: + module: presence + class: Presence + global_depencencies: + - entities + - timers + dependencies: + - entity_manager + + name: device_tracker.anneli_presence + attributes: {} + + devices: + - device_tracker.anneli_lovn + - device_tracker.anneli_loven + - sensor.anneli_iphone_bt + - sensor.anneli_iphone_bt2 + # - sensor.anneli_klocka_bt + # - sensor.anneli_klocka_bt2 diff --git a/lovelace/views/dashboard_view.yaml b/lovelace/views/dashboard_view.yaml index 06c698c..d520ec0 100644 --- a/lovelace/views/dashboard_view.yaml +++ b/lovelace/views/dashboard_view.yaml @@ -21,6 +21,43 @@ popup_cards: - type: history-graph entities: *door_entities + device_tracker.thomas_presence: + title: Thomas + large: true + card: + type: vertical-stack + cards: + - type: entities + entities: &thomas_entities + - device_tracker.thomas_iphone_beta + - device_tracker.thomas_iphone_2 + - sensor.thomas_iphone_bt + - sensor.thomas_iphone_bt2 + - type: history-graph + entities: + - device_tracker.thomas_presence + - type: history-graph + entities: *thomas_entities + + device_tracker.anneli_presence: + title: Anneli + large: true + card: + type: vertical-stack + cards: + - type: entities + entities: &anneli_entities + - device_tracker.anneli_lovn + - device_tracker.anneli_loven + - sensor.anneli_iphone_bt + - sensor.anneli_iphone_bt2 + - sensor.anneli_klocka_bt + - sensor.anneli_klocka_bt2 + - type: history-graph + entities: + - device_tracker.anneli_presence + - type: history-graph + entities: *anneli_entities cards: - type: custom:layout-card layout: vertical @@ -56,10 +93,22 @@ cards: - break - type: horizontal-stack cards: - - type: picture - image: /local/images/thomas_clr.jpg - - type: picture - image: /local/images/anneli_clr.png + - type: picture-entity + entity: device_tracker.thomas_presence + image: /local/images/thomas_bw.jpg + show_name: false + show_state: false + state_image: + "home": /local/images/thomas_clr.jpg + "just_arrived": /local/images/thomas_clr.jpg + - type: picture-entity + entity: device_tracker.anneli_presence + image: /local/images/anneli_bw.png + show_name: false + show_state: false + state_image: + "home": /local/images/anneli_clr.png + "just_arrived": /local/images/anneli_clr.png - type: horizontal-stack cards: - type: entity-button diff --git a/packages/presence.yaml b/packages/presence.yaml new file mode 100644 index 0000000..94e1771 --- /dev/null +++ b/packages/presence.yaml @@ -0,0 +1,37 @@ +homeassistant: + customize: + package.node_anchors: + common: &common + package: 'security' + + binary_sensor.home_occupied: + <<: *common + automation.adm_rescan_monitor_on_restart: + <<: *common + +binary_sensor: + - platform: template + sensors: + home_occupied: + friendly_name: Anyone home + value_template: >- + {{ (is_state('device_tracker.thomas_presence', 'home') or + is_state('device_tracker.anneli_presence', 'home')) }} + +device_tracker: + - platform: unifi + host: unifi + username: !secret unifi_username + password: !secret unifi_password + verify_ssl: false + ssid_filter: !secret unifi_ssids + +automation: + - alias: ADM - Rescan monitor on restart + trigger: + platform: homeassistant + event: start + action: + - service: mqtt.publish + data: + topic: "monitor/scan/restart"