Skip to content
This repository has been archived by the owner on Jul 30, 2021. It is now read-only.

Commit

Permalink
Merge pull request #57 from tchellomello/008
Browse files Browse the repository at this point in the history
Published version 0.0.8
  • Loading branch information
tchellomello authored Oct 15, 2017
2 parents a785004 + 0fa903a commit d05143c
Show file tree
Hide file tree
Showing 9 changed files with 303 additions and 126 deletions.
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Installation
$ pip install pyarlo
# Install latest development
$ pip install git+https://github.com/tchellomello/python-arlo@dev
$ pip install git+https://github.com/tchellomello/python-arlo
Usage
-----
Expand Down
24 changes: 15 additions & 9 deletions pyarlo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,12 @@ def __init__(self, username=None, password=None,
self.__headers = None
self.__params = None

self._all_devices = {}

# set username and password
self.__password = password
self.__username = username
self.session = requests.Session()
self.__base_stations = []

# login user
self.login()
Expand Down Expand Up @@ -131,14 +132,16 @@ def query(self,
loop += 1

# define connection method
req = None

if method == 'GET':
req = self.session.get(url, headers=headers, stream=stream)
elif method == 'PUT':
req = self.session.put(url, json=params, headers=headers)
elif method == 'POST':
req = self.session.post(url, json=params, headers=headers)

if req.status_code == 200:
if req and (req.status_code == 200):
if raw:
_LOGGER.debug("Required raw object.")
response = req
Expand All @@ -163,9 +166,12 @@ def base_stations(self):
@property
def devices(self):
"""Return all devices on Arlo account."""
devices = {}
devices['cameras'] = []
devices['base_station'] = []
if self._all_devices:
return self._all_devices

self._all_devices = {}
self._all_devices['cameras'] = []
self._all_devices['base_station'] = []

url = DEVICES_ENDPOINT
data = self.query(url)
Expand All @@ -176,15 +182,15 @@ def devices(self):
device.get('deviceType') == 'arloq' or
device.get('deviceType') == 'arloqs') and
device.get('state') == 'provisioned'):
devices['cameras'].append(ArloCamera(name, device, self))
camera = ArloCamera(name, device, self)
self._all_devices['cameras'].append(camera)

if device.get('deviceType') == 'basestation' and \
device.get('state') == 'provisioned':
base = ArloBaseStation(name, device, self.__token, self)
devices['base_station'].append(base)
self.__base_stations.append(base)
self._all_devices['base_station'].append(base)

return devices
return self._all_devices

def lookup_camera_by_id(self, device_id):
"""Return camera object by device_id."""
Expand Down
111 changes: 79 additions & 32 deletions pyarlo/base_station.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
import json
import threading
import logging
import time
import sseclient
from pyarlo.const import (
ACTION_BODY, SUBSCRIBE_ENDPOINT, UNSUBSCRIBE_ENDPOINT,
FIXED_MODES, NOTIFY_ENDPOINT, RESOURCES)
_LOGGER = logging.getLogger(__name__)

REFRESH_RATE = 15


class ArloBaseStation(object):
"""Arlo Base Station module implementation."""
Expand All @@ -25,6 +28,11 @@ def __init__(self, name, attrs, session_token, arlo_session):
self._attrs = attrs
self._session = arlo_session
self._session_token = session_token
self._available_modes = None
self._available_mode_ids = None
self._camera_properties = None
self._last_refresh = None
self._refresh_rate = REFRESH_RATE
self.__sseclient = None
self.__subscribed = False
self.__events = []
Expand All @@ -42,6 +50,7 @@ def thread_function(self):

data = self._session.query(url, method='GET', raw=True, stream=True)
self.__sseclient = sseclient.SSEClient(data)

for event in (self.__sseclient).events():
if not self.__subscribed:
break
Expand Down Expand Up @@ -99,6 +108,7 @@ def publish_and_get_event(self, resource):
resource=resource,
mode=None,
publish_response=False)

if status == 'success':
i = 0
while not this_event and i < 2:
Expand All @@ -123,12 +133,14 @@ def __run_action(
self,
method='GET',
resource=None,
camera_id=None,
mode=None,
publish_response=None):
"""Run action.
:param method: Specify the method GET, POST or PUT. Default is GET.
:param resource: Specify one of the resources to fetch from arlo.
:param camera_id: Specify the camera ID involved with this action
:param mode: Specify the mode to set, else None for GET operations
:param publish_response: Set to True for SETs. Default False
"""
Expand All @@ -155,7 +167,9 @@ def __run_action(
elif resource == 'modes':
available_modes = self.available_modes_with_ids
body['properties'] = {'active': available_modes.get(mode)}

elif resource == 'privacy':
body['properties'] = {'privacyActive': not mode}
body['resource'] = "cameras/{0}".format(camera_id)
else:
_LOGGER.info("Invalid method requested")
return None
Expand All @@ -167,15 +181,15 @@ def __run_action(

body['from'] = "{0}_web".format(self.user_id)
body['to'] = self.device_id
body['transId'] = "web!e6d1b969.8aa4b!1498165992111"
body['transId'] = "web!{0}".format(self.xcloud_id)

_LOGGER.debug("Action body: %s", body)

ret = \
self._session.query(url, method='POST', extra_params=body,
extra_headers={"xCloudId": self.xcloud_id})

if ret.get('success'):
if ret and ret.get('success'):
return 'success'

return None
Expand Down Expand Up @@ -234,18 +248,28 @@ def xcloud_id(self):
@property
def available_modes(self):
"""Return list of available mode names."""
return list(self.available_modes_with_ids.keys())
if not self._available_modes:
modes = self.available_modes_with_ids
if not modes:
return None
self._available_modes = list(modes.keys())
return self._available_modes

@property
def available_modes_with_ids(self):
"""Return list of objects containing available mode name and id."""
modes = self.get_available_modes()
simple_modes = dict(
[(m.get("type", m.get("name")), m.get("id")) for m in modes]
)
all_modes = FIXED_MODES.copy()
all_modes.update(simple_modes)
return all_modes
if not self._available_mode_ids:
all_modes = FIXED_MODES.copy()
self._available_mode_ids = all_modes
modes = self.get_available_modes()
if modes:
simple_modes = dict(
[(m.get("type", m.get("name")), m.get("id"))
for m in modes]
)
all_modes.update(simple_modes)
self._available_mode_ids = all_modes
return self._available_mode_ids

@property
def available_resources(self):
Expand Down Expand Up @@ -281,45 +305,47 @@ def get_available_modes(self):
return None

@property
def get_camera_properties(self):
def camera_properties(self):
"""Return _camera_properties"""
if self._camera_properties is None:
self.get_cameras_properties()
return self._camera_properties

def get_cameras_properties(self):
"""Return camera properties."""
resource = "cameras"
resource_event = self.publish_and_get_event(resource)
if resource_event:
return resource_event.get('properties')
self._last_refresh = int(time.time())
self._camera_properties = resource_event.get('properties')
return

return None

@property
def get_camera_battery_level(self):
def get_cameras_battery_level(self):
"""Return a list of battery levels of all cameras."""
battery_levels = {}
camera_properties = self.get_camera_properties
if not camera_properties:
if not self.camera_properties:
return None

for camera in camera_properties:
for camera in self.camera_properties:
serialnum = camera.get('serialNumber')
cam_battery = camera.get('batteryLevel')
battery_levels[serialnum] = cam_battery
return battery_levels

@property
def get_camera_signal_strength(self):
def get_cameras_signal_strength(self):
"""Return a list of signal strength of all cameras."""
signal_strength = {}
camera_properties = self.get_camera_properties
if not camera_properties:
if not self.camera_properties:
return None

for camera in camera_properties:
for camera in self.camera_properties:
serialnum = camera.get('serialNumber')
cam_strength = camera.get('signalStrength')
signal_strength[serialnum] = cam_strength
return signal_strength

@property
def get_basestation_properties(self):
def properties(self):
"""Return the base station info."""
resource = "basestation"
basestn_event = self.publish_and_get_event(resource)
Expand All @@ -328,8 +354,7 @@ def get_basestation_properties(self):

return None

@property
def get_camera_rules(self):
def get_cameras_rules(self):
"""Return the camera rules."""
resource = "rules"
rules_event = self.publish_and_get_event(resource)
Expand All @@ -338,8 +363,7 @@ def get_camera_rules(self):

return None

@property
def get_camera_schedule(self):
def get_cameras_schedule(self):
"""Return the schedule set for cameras."""
resource = "schedule"
schedule_event = self.publish_and_get_event(resource)
Expand Down Expand Up @@ -371,7 +395,8 @@ def mode(self, mode):
:param mode: arm, disarm
"""
if mode not in self.available_modes:
modes = self.available_modes
if (not modes) or (mode not in modes):
return
self.__run_action(
method='SET',
Expand All @@ -380,8 +405,30 @@ def mode(self, mode):
publish_response=True)
self.update()

def set_camera_enabled(self, camera_id, is_enabled):
"""Turn Arlo camera On/Off.
:param mode: True, False
"""
self.__run_action(
method='SET',
resource='privacy',
camera_id=camera_id,
mode=is_enabled,
publish_response=True)
self.update()

def update(self):
"""Update object properties."""
self._attrs = self._session.refresh_attributes(self.name)
current_time = int(time.time())
last_refresh = 0 if self._last_refresh is None else self._last_refresh

if current_time >= (last_refresh + self._refresh_rate):
self.get_cameras_properties()
self._attrs = self._session.refresh_attributes(self.name)
_LOGGER.debug("Called base station update of camera properties: "
"Scan Interval: %s, New Properties: %s",
self._refresh_rate, self.camera_properties)
return

# vim:sw=4:ts=4:et:
Loading

0 comments on commit d05143c

Please sign in to comment.