Skip to content

Commit

Permalink
Modified caching. Added code to cache latest activity events.
Browse files Browse the repository at this point in the history
  • Loading branch information
MisterWil committed Sep 29, 2017
1 parent c21e4f5 commit 848aae7
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 89 deletions.
77 changes: 55 additions & 22 deletions skybellpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,24 +41,21 @@ def __init__(self, username=None, password=None,
self._session = None
self._cache_path = cache_path
self._disable_cache = disable_cache
self._cache = None

self._devices = None

# Create a requests session to persist the cookies
self._session = requests.session()

# Load App ID, Client ID, and Token
if not disable_cache and os.path.exists(cache_path):
_LOGGER.debug("Cookies found at: %s", cache_path)
self._cache = UTILS.load_cache(cache_path)
else:
self._cache = {
CONST.APP_ID: UTILS.gen_id(),
CONST.CLIENT_ID: UTILS.gen_id(),
CONST.TOKEN: UTILS.gen_token(),
CONST.ACCESS_TOKEN: None
}
# Create a new cache template
self._cache = {
CONST.APP_ID: UTILS.gen_id(),
CONST.CLIENT_ID: UTILS.gen_id(),
CONST.TOKEN: UTILS.gen_token(),
CONST.ACCESS_TOKEN: None,
CONST.DEVICES: {}
}

# Load and merge an existing cache
if not disable_cache:
self._load_cache()

if (self._username is not None and
self._password is not None and
Expand Down Expand Up @@ -100,10 +97,8 @@ def login(self, username=None, password=None):

response_object = json.loads(response.text)

self._cache[CONST.ACCESS_TOKEN] = response_object[CONST.ACCESS_TOKEN]

if not self._disable_cache:
UTILS.save_cache(self._cache, self._cache_path)
self.update_cache({
CONST.ACCESS_TOKEN: response_object[CONST.ACCESS_TOKEN]})

_LOGGER.info("Login successful")

Expand All @@ -117,10 +112,8 @@ def logout(self):
# we aren't currently doing.
self._session = requests.session()
self._devices = None
self._cache[CONST.ACCESS_TOKEN] = None

if not self._disable_cache:
UTILS.save_cache(self._cache, self._cache_path)
self.update_cache({CONST.ACCESS_TOKEN: None})

return True

Expand Down Expand Up @@ -198,3 +191,43 @@ def send_request(self, method, url, headers=None,
return self.send_request(method, url, headers, json_data, False)

raise SkybellException(ERROR.REQUEST, "Retry failed")

def cache(self, key):
"""Get a cached value."""
return self._cache.get(key)

def update_cache(self, data):
"""Update a cached value."""
UTILS.update(self._cache, data)
self._save_cache()

def dev_cache(self, device, key=None):
"""Get a cached value for a device."""
device_cache = self._cache.get(CONST.DEVICES, {}).get(device.device_id)

if device_cache and key:
return device_cache.get(key)

return device_cache

def update_dev_cache(self, device, data):
"""Update cached values for a device."""
self.update_cache(
{
device.device_id: data
})

def _load_cache(self):
"""Load existing cache and merge for updating if required."""
if not self._disable_cache and os.path.exists(self._cache_path):
_LOGGER.debug("Cache found at: %s", self._cache_path)
loaded_cache = UTILS.load_cache(self._cache_path)

UTILS.update(self._cache, loaded_cache)

self._save_cache()

def _save_cache(self):
"""Trigger a cache save."""
if not self._disable_cache:
UTILS.save_cache(self._cache, self._cache_path)
6 changes: 3 additions & 3 deletions skybellpy/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,9 @@ def _device_print(dev, append=''):

except SkybellException as exc:
_LOGGER.error(exc)
finally:
if skybell:
skybell.logout()
# finally:
# if skybell:
# skybell.logout()


def main():
Expand Down
130 changes: 75 additions & 55 deletions skybellpy/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from skybellpy.exceptions import SkybellException
import skybellpy.helpers.constants as CONST
import skybellpy.helpers.errors as ERROR
import skybellpy.utils as UTILS

_LOGGER = logging.getLogger(__name__)

Expand All @@ -19,84 +20,104 @@ def __init__(self, device_json, skybell):
self._type = device_json.get(CONST.TYPE)
self._skybell = skybell

self._device_info_json = self._device_info_request()
self._device_settings_json = self._device_settings_request()
self._device_activities = self._device_activities_request()
self._info_json = self._info_request()
self._settings_json = self._settings_request()

self._update_activities()

def refresh(self):
"""Refresh the devices json object data."""
# Update core device data
new_device_json = self._device_request()
_LOGGER.debug("Device Refresh Response: %s", new_device_json)

new_device_info_json = self._device_info_request()
_LOGGER.debug("Device Info Refresh Response: %s", new_device_info_json)
# Update device detail info
new_info_json = self._info_request()
_LOGGER.debug("Device Info Refresh Response: %s", new_info_json)

new_device_settings_json = self._device_settings_request()
# Update device setting details
new_settings_json = self._settings_request()
_LOGGER.debug("Device Settings Refresh Response: %s",
new_device_settings_json)
new_settings_json)

self.update(new_device_json, new_device_info_json,
new_device_settings_json)
# Update the stored data
self.update(new_device_json, new_info_json, new_settings_json)

self._device_activities = self._device_activities_request()
_LOGGER.debug("Device Activities Response: %s",
new_device_settings_json)
# Update the activities
self._update_activities()

def _device_request(self):
url = str.replace(CONST.DEVICE_URL, '$DEVID$', self.device_id)
response = self._skybell.send_request(method="get", url=url)
return json.loads(response.text)

def _device_info_request(self):
def _info_request(self):
url = str.replace(CONST.DEVICE_INFO_URL, '$DEVID$', self.device_id)
response = self._skybell.send_request(method="get", url=url)
return json.loads(response.text)

def _device_settings_request(self, method="get", json_data=None):
def _settings_request(self, method="get", json_data=None):
url = str.replace(CONST.DEVICE_SETTINGS_URL, '$DEVID$', self.device_id)
response = self._skybell.send_request(method=method,
url=url,
json_data=json_data)
return json.loads(response.text)

def _device_activities_request(self):
def _activities_request(self):
url = str.replace(CONST.DEVICE_ACTIVITIES_URL,
'$DEVID$', self.device_id)
response = self._skybell.send_request(method="get", url=url)
return json.loads(response.text)

def update(self, device_json=None, device_info_json=None,
device_settings_json=None):
"""Update the json data from a dictionary.
Only updates if it already exists in the device.
"""
def update(self, device_json=None, info_json=None, settings_json=None):
"""Update the internal device json data."""
if device_json:
self._device_json.update(
{k: device_json[k] for k in device_json
if k in self._device_json})
UTILS.update(self._device_json, device_json)

if info_json:
UTILS.update(self._info_json, info_json)

if settings_json:
UTILS.update(self._settings_json, settings_json)

def _update_activities(self):
"""Update stored activities and update caches as required."""
self._activities = self._activities_request()
_LOGGER.debug("Device Activities Response: %s", self._activities)

if not self._activities:
self._activities = []
elif not isinstance(self._activities, (list, tuple)):
self._activities = [self._activities]

self._update_events()

if device_info_json:
self._device_info_json.update(
{k: device_info_json[k] for k in device_info_json
if k in self._device_info_json})
def _update_events(self):
"""Update our cached list of latest activity events."""
events = self._skybell.dev_cache(self, CONST.EVENT) or {}

if device_settings_json:
self._device_settings_json.update(
{k: device_settings_json[k] for k in device_settings_json
if k in self._device_settings_json})
for activity in self._activities:
event = activity.get(CONST.EVENT)
created_at = activity.get(CONST.CREATED_AT)

old_event = events.get(event)

if old_event and created_at < old_event.get(CONST.CREATED_AT):
continue
else:
events[event] = activity

self._skybell.update_dev_cache(
self,
{
CONST.EVENT: events
})

def activities(self, limit=1, event=None):
"""Return device activity information."""
activities = self._device_activities

# Make sure we're working with an array
if not activities:
activities = []
elif not isinstance(activities, (list, tuple)):
activities = [activities]
activities = self._activities or []

# Filter our activity array
# Filter our activity array if requested
if event:
activities = list(
filter(
Expand All @@ -112,9 +133,9 @@ def _set_setting(self, settings):
_validate_setting(key, value)

try:
self._device_settings_request(method="patch", json_data=settings)
self._settings_request(method="patch", json_data=settings)

self.update(device_settings_json=settings)
self.update(settings_json=settings)
except SkybellException as exc:
_LOGGER.warning("Exception changing settings: %s", settings)
_LOGGER.warning(exc)
Expand Down Expand Up @@ -160,23 +181,22 @@ def image(self):
@property
def wifi_status(self):
"""Get the wifi status."""
return self._device_info_json.get(
CONST.STATUS, {}).get(CONST.WIFI_LINK)
return self._info_json.get(CONST.STATUS, {}).get(CONST.WIFI_LINK)

@property
def wifi_ssid(self):
"""Get the wifi ssid."""
return self._device_info_json.get(CONST.WIFI_SSID)
return self._info_json.get(CONST.WIFI_SSID)

@property
def last_check_in(self):
"""Get last check in timestamp."""
return self._device_info_json.get(CONST.CHECK_IN)
return self._info_json.get(CONST.CHECK_IN)

@property
def do_not_disturb(self):
"""Get if do not disturb is enabled."""
return self._device_settings_json.get(CONST.SETTINGS_DO_NOT_DISTURB)
return self._settings_json.get(CONST.SETTINGS_DO_NOT_DISTURB)

@do_not_disturb.setter
def do_not_disturb(self, enabled):
Expand All @@ -186,7 +206,7 @@ def do_not_disturb(self, enabled):
@property
def outdoor_chime_level(self):
"""Get devices outdoor chime level."""
return self._device_settings_json.get(CONST.SETTINGS_OUTDOOR_CHIME)
return self._settings_json.get(CONST.SETTINGS_OUTDOOR_CHIME)

@outdoor_chime_level.setter
def outdoor_chime_level(self, level):
Expand All @@ -202,7 +222,7 @@ def outdoor_chime(self):
def motion_sensor(self):
"""Get if the devices motion sensor is enabled."""
return (
self._device_settings_json.get(CONST.SETTINGS_MOTION_POLICY) ==
self._settings_json.get(CONST.SETTINGS_MOTION_POLICY) ==
CONST.SETTINGS_MOTION_POLICY_ON)

@motion_sensor.setter
Expand All @@ -221,7 +241,7 @@ def motion_sensor(self, enabled):
@property
def motion_threshold(self):
"""Get devices motion threshold."""
return self._device_settings_json.get(CONST.SETTINGS_MOTION_THRESHOLD)
return self._settings_json.get(CONST.SETTINGS_MOTION_THRESHOLD)

@motion_threshold.setter
def motion_threshold(self, threshold):
Expand All @@ -231,7 +251,7 @@ def motion_threshold(self, threshold):
@property
def video_profile(self):
"""Get devices video profile."""
return self._device_settings_json.get(CONST.SETTINGS_VIDEO_PROFILE)
return self._settings_json.get(CONST.SETTINGS_VIDEO_PROFILE)

@video_profile.setter
def video_profile(self, profile):
Expand All @@ -241,9 +261,9 @@ def video_profile(self, profile):
@property
def led_rgb(self):
"""Get devices LED color."""
return (self._device_settings_json.get(CONST.SETTINGS_LED_R),
self._device_settings_json.get(CONST.SETTINGS_LED_G),
self._device_settings_json.get(CONST.SETTINGS_LED_B))
return (self._settings_json.get(CONST.SETTINGS_LED_R),
self._settings_json.get(CONST.SETTINGS_LED_G),
self._settings_json.get(CONST.SETTINGS_LED_B))

@led_rgb.setter
def led_rgb(self, color):
Expand All @@ -262,7 +282,7 @@ def led_rgb(self, color):
@property
def led_intensity(self):
"""Get devices LED intensity."""
return self._device_settings_json.get(CONST.SETTINGS_LED_INTENSITY)
return self._settings_json.get(CONST.SETTINGS_LED_INTENSITY)

@led_intensity.setter
def led_intensity(self, intensity):
Expand Down
3 changes: 2 additions & 1 deletion skybellpy/helpers/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
CLIENT_ID = 'client_id'
TOKEN = 'token'
ACCESS_TOKEN = 'access_token'

DEVICES = 'devices'

# DEVICE
NAME = 'name'
Expand All @@ -89,6 +89,7 @@
EVENT_ON_DEMAND = 'application:on-demand'
EVENT_BUTTON = 'device:sensor:button'
EVENT_MOTION = 'device:sensor:motion'
CREATED_AT = 'createdAt'

STATE = 'state'
STATE_READY = 'ready'
Expand Down
Loading

0 comments on commit 848aae7

Please sign in to comment.