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 diff --git a/ring_doorbell/__init__.py b/ring_doorbell/__init__.py index 869139dd..a23b6d26 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, @@ -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 @@ -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]'] = \ @@ -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 @@ -199,6 +201,11 @@ def __init__(self): self.family = None self.name = None + # alerts notifications + self._alert_cache = None + self.alert = None + self.alert_expires_at = None + def __repr__(self): """Return __repr__.""" return "<{0}: {1}>".format(self.__class__.__name__, self.name) @@ -206,6 +213,28 @@ 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.""" + 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.""" @@ -332,11 +361,31 @@ def battery_life(self): value = 100 return value - @property - def check_activity(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 - return self._ring.query(url) + self.update() + + try: + resp = self._ring.query(url)[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) + + # save to a pickle data + if self._alert_cache: + _save_cache(self.alert, self._alert_cache) + return True + 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