From 38ee8775cb83b974e32a86016cdc09d441541525 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Mon, 6 Mar 2017 03:39:14 -0500 Subject: [PATCH 01/12] added initial control to alert notifications --- ring_doorbell/__init__.py | 59 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/ring_doorbell/__init__.py b/ring_doorbell/__init__.py index 869139dd..527b9ba8 100644 --- a/ring_doorbell/__init__.py +++ b/ring_doorbell/__init__.py @@ -206,6 +206,17 @@ def __repr__(self): def update(self): """Refresh attributes.""" self._get_attrs() + self._update_alert() + + def _update_alert(self): + """Verify if alert received is still valid.""" + try: + if self.alert: + if datetime.now() >= self.alert_expires_at: + self.alert = None + self.alert_expires_at = None + except AttributeError: + pass def _get_attrs(self): """Return chime attributes.""" @@ -322,6 +333,9 @@ def __init__(self, ring, name): self.debug = self._ring.debug self.family = 'doorbots' self.name = name + + self.alert = None + self.alert_expires_at = None self.update() @property @@ -333,10 +347,51 @@ def battery_life(self): return value @property - def check_activity(self): + def check_alerts(self): """Return JSON when motion or ring is detected.""" url = API_URI + DINGS_ENDPOINT - return self._ring.query(url) + self.update() + try: + import time + R = [{ + 'audio_jitter_buffer_ms': 0, + 'device_kind': 'lpd_v1', + 'doorbot_description': 'Front Door', + 'doorbot_id': 1123456, + 'expires_in': 179, + 'id': 12345, + 'id_str': '123567', + 'kind': 'motion', + 'motion': True, + #'now': 1486603261.13869, + 'now': time.time(), + 'optimization_level': 1, + 'protocol': 'sip', + 'sip_ding_id': '1234567', + 'sip_endpoints': None, + 'sip_from': 'sip:12345@ring.com', + 'sip_server_ip': '1.1.1.1', + 'sip_server_port': '15063', + 'sip_server_tls': 'false', + 'sip_session_id': 'secret', + 'sip_to': 'sip:user@1.1.1.11:15063;transport=tcp', + 'sip_token': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa69b5f16', + 'snapshot_url': '', + 'state': 'ringing', + 'video_jitter_buffer_ms': 0}] + + #response = self._ring.query(url)[0] + resp = R[0] + except IndexError: + return None + + if resp: + timestamp = resp.get('now') + resp.get('expires_in') + self.alert = resp + self.alert_expires_at = datetime.fromtimestamp(timestamp) + #return self._ring.query(url) + return True + return None @property def existing_doorbell_type(self): From 42528d020150b47098f151cb124052bd4f3d05f5 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Tue, 7 Mar 2017 23:12:23 -0500 Subject: [PATCH 02/12] Removed JSON from tests --- ring_doorbell/__init__.py | 44 +++++++-------------------------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/ring_doorbell/__init__.py b/ring_doorbell/__init__.py index 527b9ba8..fa3bc9dd 100644 --- a/ring_doorbell/__init__.py +++ b/ring_doorbell/__init__.py @@ -352,46 +352,16 @@ def check_alerts(self): url = API_URI + DINGS_ENDPOINT self.update() try: - import time - R = [{ - 'audio_jitter_buffer_ms': 0, - 'device_kind': 'lpd_v1', - 'doorbot_description': 'Front Door', - 'doorbot_id': 1123456, - 'expires_in': 179, - 'id': 12345, - 'id_str': '123567', - 'kind': 'motion', - 'motion': True, - #'now': 1486603261.13869, - 'now': time.time(), - 'optimization_level': 1, - 'protocol': 'sip', - 'sip_ding_id': '1234567', - 'sip_endpoints': None, - 'sip_from': 'sip:12345@ring.com', - 'sip_server_ip': '1.1.1.1', - 'sip_server_port': '15063', - 'sip_server_tls': 'false', - 'sip_session_id': 'secret', - 'sip_to': 'sip:user@1.1.1.11:15063;transport=tcp', - 'sip_token': 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa69b5f16', - 'snapshot_url': '', - 'state': 'ringing', - 'video_jitter_buffer_ms': 0}] - - #response = self._ring.query(url)[0] - resp = R[0] + resp = self._ring.query(url)[0] + if not resp: + return None except IndexError: return None - if resp: - timestamp = resp.get('now') + resp.get('expires_in') - self.alert = resp - self.alert_expires_at = datetime.fromtimestamp(timestamp) - #return self._ring.query(url) - return True - return None + timestamp = resp.get('now') + resp.get('expires_in') + self.alert = resp + self.alert_expires_at = datetime.fromtimestamp(timestamp) + return True @property def existing_doorbell_type(self): From 4fd8dce9fc6d34f36dbb1dd0d368f5e806ec416b Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Tue, 7 Mar 2017 23:16:27 -0500 Subject: [PATCH 03/12] Make lint happy --- ring_doorbell/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ring_doorbell/__init__.py b/ring_doorbell/__init__.py index fa3bc9dd..68d602f1 100644 --- a/ring_doorbell/__init__.py +++ b/ring_doorbell/__init__.py @@ -199,6 +199,10 @@ def __init__(self): self.family = None self.name = None + # alerts notifications + self.alert = None + self.alert_expires_at = None + def __repr__(self): """Return __repr__.""" return "<{0}: {1}>".format(self.__class__.__name__, self.name) @@ -334,8 +338,6 @@ def __init__(self, ring, name): self.family = 'doorbots' self.name = name - self.alert = None - self.alert_expires_at = None self.update() @property From 26a5af88f6f541df72b6f397f9723d6982a4146b Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Tue, 7 Mar 2017 23:25:19 -0500 Subject: [PATCH 04/12] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 70316e1b..8647c18b 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ In [12]: mydoorbell. mydoorbell.account_id mydoorbell.kind mydoorbell.address mydoorbell.last_recording_id mydoorbell.battery_life mydoorbell.latitude - mydoorbell.check_activity mydoorbell.live_streaming_json + mydoorbell.check_alerts mydoorbell.live_streaming_json mydoorbell.debug mydoorbell.longitude mydoorbell.existing_doorbell_type mydoorbell.name mydoorbell.existing_doorbell_type_duration mydoorbell.recording_download From ab5ed4a52e30916f3ebdac77a1f510649e47aa68 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Tue, 7 Mar 2017 23:48:46 -0500 Subject: [PATCH 05/12] Only publish PUSH url if we have one --- ring_doorbell/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ring_doorbell/__init__.py b/ring_doorbell/__init__.py index 68d602f1..f915dc87 100644 --- a/ring_doorbell/__init__.py +++ b/ring_doorbell/__init__.py @@ -72,7 +72,7 @@ def _authenticate(self, attempts=RETRY_TOKEN): self.params = {'api_version': API_VERSION, 'auth_token': self.token} - if self._persist_token: + if self._persist_token and self._push_token_notify_url: url = API_URI + PERSIST_TOKEN_ENDPOINT PERSIST_TOKEN_DATA['auth_token'] = self.token PERSIST_TOKEN_DATA['device[push_notification_token]'] = \ From c1d85060c35ff2ac6ecb8e42232e445b788231e7 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Wed, 8 Mar 2017 00:09:18 -0500 Subject: [PATCH 06/12] Display error message only if in debug --- ring_doorbell/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ring_doorbell/__init__.py b/ring_doorbell/__init__.py index f915dc87..af2a40a5 100644 --- a/ring_doorbell/__init__.py +++ b/ring_doorbell/__init__.py @@ -140,7 +140,9 @@ def query(self, if method == 'GET': response = req.json() break - _LOGGER.error("%s", MSG_GENERIC_FAIL) + + if self.debug: + _LOGGER.debug("%s", MSG_GENERIC_FAIL) return response @property From 30f6479ee3547c8d1fb0f4455c5abeadd210d060 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Wed, 8 Mar 2017 21:43:04 -0500 Subject: [PATCH 07/12] override default URL --- ring_doorbell/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ring_doorbell/__init__.py b/ring_doorbell/__init__.py index af2a40a5..b9d883cb 100644 --- a/ring_doorbell/__init__.py +++ b/ring_doorbell/__init__.py @@ -32,7 +32,7 @@ class Ring(object): """A Python Abstraction object to Ring Door Bell.""" def __init__(self, username, password, debug=False, persist_token=False, - push_token_notify_url=""): + push_token_notify_url="http://localhost/"): """Initialize the Ring object.""" self.features = None self.is_connected = None From ca1a3baf19fe70ad095f641a6c0468f6ed26dad4 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Wed, 8 Mar 2017 22:23:32 -0500 Subject: [PATCH 08/12] Make check alerts a callable function --- ring_doorbell/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ring_doorbell/__init__.py b/ring_doorbell/__init__.py index b9d883cb..a6372efb 100644 --- a/ring_doorbell/__init__.py +++ b/ring_doorbell/__init__.py @@ -350,7 +350,6 @@ def battery_life(self): value = 100 return value - @property def check_alerts(self): """Return JSON when motion or ring is detected.""" url = API_URI + DINGS_ENDPOINT From f62fb68678c14fbf11b508e1eadfe785a45821dc Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Wed, 8 Mar 2017 23:04:43 -0500 Subject: [PATCH 09/12] Removed extra space --- ring_doorbell/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ring_doorbell/__init__.py b/ring_doorbell/__init__.py index a6372efb..2c3377c6 100644 --- a/ring_doorbell/__init__.py +++ b/ring_doorbell/__init__.py @@ -339,7 +339,6 @@ def __init__(self, ring, name): self.debug = self._ring.debug self.family = 'doorbots' self.name = name - self.update() @property From 0ac6ad0b0b275238090a3cd5207d40ee85af8999 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Wed, 8 Mar 2017 23:09:19 -0500 Subject: [PATCH 10/12] Move inside the try/except for safeness --- ring_doorbell/__init__.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ring_doorbell/__init__.py b/ring_doorbell/__init__.py index 2c3377c6..76bd4676 100644 --- a/ring_doorbell/__init__.py +++ b/ring_doorbell/__init__.py @@ -357,14 +357,13 @@ def check_alerts(self): resp = self._ring.query(url)[0] if not resp: return None + timestamp = resp.get('now') + resp.get('expires_in') + self.alert = resp + self.alert_expires_at = datetime.fromtimestamp(timestamp) + return True except IndexError: return None - timestamp = resp.get('now') + resp.get('expires_in') - self.alert = resp - self.alert_expires_at = datetime.fromtimestamp(timestamp) - return True - @property def existing_doorbell_type(self): """ From d296b20ca51a2592c98cc3569c00d9360910d6eb Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Thu, 9 Mar 2017 15:17:05 -0500 Subject: [PATCH 11/12] Introduced mecanism to save current alert state to a pickle file to allow concurrent objects to share alert notifications --- ring_doorbell/__init__.py | 49 ++++++++++++++++++++++++++++----------- ring_doorbell/utils.py | 26 +++++++++++++++++++++ 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/ring_doorbell/__init__.py b/ring_doorbell/__init__.py index 76bd4676..332930ef 100644 --- a/ring_doorbell/__init__.py +++ b/ring_doorbell/__init__.py @@ -13,7 +13,7 @@ import requests import pytz -from ring_doorbell.utils import _locator +from ring_doorbell.utils import _locator, _save_cache, _read_cache from ring_doorbell.const import ( API_VERSION, API_URI, CHIMES_ENDPOINT, CHIME_VOL_MIN, CHIME_VOL_MAX, DEVICES_ENDPOINT, DOORBELLS_ENDPOINT, DOORBELL_VOL_MIN, DOORBELL_VOL_MAX, @@ -196,6 +196,7 @@ class RingGeneric(object): def __init__(self): """Initialize Ring Generic.""" + self._alert_cache = None self._attrs = None self.debug = None self.family = None @@ -216,13 +217,24 @@ def update(self): def _update_alert(self): """Verify if alert received is still valid.""" - try: - if self.alert: - if datetime.now() >= self.alert_expires_at: - self.alert = None - self.alert_expires_at = None - except AttributeError: - pass + if self.alert and self.alert_expires_at: + if datetime.now() >= self.alert_expires_at: + self.alert = None + self.alert_expires_at = None + elif self._alert_cache: + aux = _read_cache(self._alert_cache) + if ((isinstance(aux, dict)) and + ('now' in aux) and + ('expires_in' in aux)): + aux_expires_at = datetime.fromtimestamp( + aux.get('now') + aux.get('expires_in')) + + # verify if pickle object is still valid + if datetime.now() <= aux_expires_at: + self.alert = aux + self.alert_expires_at = aux_expires_at + else: + _save_cache(None, self._alert_cache) def _get_attrs(self): """Return chime attributes.""" @@ -349,20 +361,31 @@ def battery_life(self): value = 100 return value - def check_alerts(self): + def check_alerts(self, cache=None): """Return JSON when motion or ring is detected.""" + # save alerts attributes to an external pickle file + # when multiple resources are checking for alerts + if cache: + self._alert_cache = cache + url = API_URI + DINGS_ENDPOINT self.update() + try: resp = self._ring.query(url)[0] - if not resp: - return None + except IndexError: + return None + + if resp: timestamp = resp.get('now') + resp.get('expires_in') self.alert = resp self.alert_expires_at = datetime.fromtimestamp(timestamp) + + # save to a pickle data + if self._alert_cache: + _save_cache(self.alert, self._alert_cache) return True - except IndexError: - return None + return None @property def existing_doorbell_type(self): diff --git a/ring_doorbell/utils.py b/ring_doorbell/utils.py index 18bf0495..eaa02ade 100644 --- a/ring_doorbell/utils.py +++ b/ring_doorbell/utils.py @@ -1,8 +1,14 @@ # coding: utf-8 # vim:sw=4:ts=4:et: """Python Ring Doorbell utils.""" +import os from ring_doorbell.const import NOT_FOUND +try: + import cPickle as pickle +except ImportError: + import pickle + def _locator(lst, key, value): """Return the position of a match item in list.""" @@ -11,3 +17,23 @@ def _locator(lst, key, value): if d[key] == value) except StopIteration: return NOT_FOUND + + +def _save_cache(data, filename): + """Dump data into a pickle file.""" + try: + with open(filename, 'wb') as pickle_db: + pickle.dump(data, pickle_db) + return True + except: + raise + + +def _read_cache(filename): + """Read data from a pickle file.""" + try: + if os.path.isfile(filename): + return pickle.load(open(filename, 'rb')) + except: + raise + return None From f7f5eaf7354c000e9923d12b2fbed27682619d63 Mon Sep 17 00:00:00 2001 From: Marcelo Moreira de Mello Date: Thu, 9 Mar 2017 15:39:15 -0500 Subject: [PATCH 12/12] Moved self._alert_cache position --- ring_doorbell/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ring_doorbell/__init__.py b/ring_doorbell/__init__.py index 332930ef..a23b6d26 100644 --- a/ring_doorbell/__init__.py +++ b/ring_doorbell/__init__.py @@ -196,13 +196,13 @@ class RingGeneric(object): def __init__(self): """Initialize Ring Generic.""" - self._alert_cache = None self._attrs = None self.debug = None self.family = None self.name = None # alerts notifications + self._alert_cache = None self.alert = None self.alert_expires_at = None