Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added new request url for camera config #65

Merged
merged 2 commits into from
May 21, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ A list of changes between each release

0.8.0.dev (Development version)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Added support for battery voltage level
- Added motion detection per camera
- Added fully accessible camera configuration dict

0.7.0 (2018-02-08)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
49 changes: 43 additions & 6 deletions blinkpy/blinkpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,17 @@ def __init__(self, config, blink):
config['thumbnail'])
self.clip = "{}{}".format(self.urls.base_url, config['video'])
self.temperature = config['temp']
self.battery = config['battery']
self._battery_string = config['battery']
self.notifications = config['notifications']
self.motion = {}
self.motion = dict()
self.header = None
self.image_link = None
self.arm_link = None
self.region_id = config['region_id']
self.battery_voltage = -180
self.motion_detected = None
self.wifi_strength = None
self.camera_config = dict()

@property
def attributes(self):
Expand All @@ -128,6 +132,8 @@ def attributes(self):
'thumbnail': self.thumbnail,
'video': self.clip,
'notifications': self.notifications,
'motion_detected': self.motion_detected,
'wifi_strength': self.wifi_strength,
'network_id': self.blink.network_id
}
return attributes
Expand All @@ -142,13 +148,18 @@ def armed(self):
"""Return camera arm status."""
return True if self._status == 'armed' else False

@property
def battery(self):
"""Return battery level as percentage."""
return round(self.battery_voltage / 180 * 100)

@property
def battery_string(self):
"""Return string indicating battery status."""
status = "Unknown"
if self.battery > 1 and self.battery <= 3:
if self._battery_string > 1 and self._battery_string <= 3:
status = "OK"
elif self.battery >= 0:
elif self._battery_string >= 0:
status = "Low"
return status

Expand All @@ -175,10 +186,26 @@ def update(self, values):
self.urls.base_url, values['thumbnail'])
self.clip = "{}{}".format(
self.urls.base_url, values['video'])
self.temperature = values['temp']
self.battery = values['battery']
self._battery_string = values['battery']
self.notifications = values['notifications']

try:
cfg = self.blink.camera_config_request(self.id)
self.camera_config = cfg
except requests.exceptions.RequestException as err:
_LOGGER.warning("Could not get config for %s with id %s",
self.name, self.id)
_LOGGER.warning("Exception raised: %s", err)

try:
self.battery_voltage = cfg['camera'][0]['battery_voltage']
self.motion_detected = cfg['camera'][0]['motion_alert']
self.wifi_strength = cfg['camera'][0]['wifi_strength']
self.temperature = cfg['camera'][0]['temperature']
except KeyError:
_LOGGER.warning("Problem extracting config for camera %s",
self.name)

def image_refresh(self):
"""Refresh current thumbnail."""
url = self.urls.home_url
Expand Down Expand Up @@ -327,6 +354,7 @@ def get_videos(self, start_page=0, end_page=1):
videos.append(this_page)

for page in videos:
_LOGGER.debug("Retrieved video page %s", page)
for entry in page:
camera_name = entry['camera_name']
clip_addr = entry['address']
Expand Down Expand Up @@ -365,6 +393,7 @@ def get_cameras(self):
device = BlinkCamera(element, self)
self.cameras[device.name] = device
self._idlookup[device.id] = device.name
self.refresh()

def set_links(self):
"""Set access links and required headers for each camera in system."""
Expand Down Expand Up @@ -497,3 +526,11 @@ def _status_request(self):
self.network_id)
headers = self._auth_header
return _request(self, url=url, headers=headers, reqtype='get')

def camera_config_request(self, camera_id):
"""Retrieve more info about Blink config."""
url = "{}/network/{}/camera/{}/config".format(self.urls.base_url,
self.network_id,
str(camera_id))
headers = self._auth_header
return _request(self, url=url, headers=headers, reqtype='get')
41 changes: 31 additions & 10 deletions tests/test_blink_cameras.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@
USERNAME = 'foobar'
PASSWORD = 'deadbeef'

CAMERA_CFG = {
'camera': [
{
'battery_voltage': 90,
'motion_alert': True,
'wifi_strength': -30,
'temperature': 68
}
]
}


class TestBlinkCameraSetup(unittest.TestCase):
"""Test the Blink class in blinkpy."""
Expand All @@ -35,18 +46,21 @@ def setUp(self):
'notifications': 2,
'region_id': 'test'
}

self.blink.urls = blinkpy.BlinkURLHandler('test')
self.blink.network_id = '0000'

def tearDown(self):
"""Clean up after test."""
self.blink = None

@mock.patch('blinkpy.blinkpy.Blink.camera_config_request',
return_value=CAMERA_CFG)
@mock.patch('blinkpy.blinkpy.requests.post',
side_effect=mresp.mocked_requests_post)
@mock.patch('blinkpy.blinkpy.requests.get',
side_effect=mresp.mocked_requests_get)
def test_camera_properties(self, mock_get, mock_post):
def test_camera_properties(self, mock_get, mock_post, mock_cfg):
"""Tests all property set/recall."""
self.blink.urls = blinkpy.BlinkURLHandler('test')

Expand All @@ -56,7 +70,7 @@ def test_camera_properties(self, mock_get, mock_post):

for name in self.blink.cameras:
camera = self.blink.cameras[name]

camera.update(self.camera_config)
self.assertEqual(camera.id, '1111')
self.assertEqual(camera.name, 'foobar')
self.assertEqual(camera.armed, False)
Expand All @@ -68,19 +82,21 @@ def test_camera_properties(self, mock_get, mock_post):
camera.clip,
"https://rest.test.{}/test/clip/clip.mp4".format(BLINK_URL)
)
self.assertEqual(camera.temperature, 70)
self.assertEqual(camera.battery, 3)
self.assertEqual(camera.temperature, 68)
self.assertEqual(camera.battery, 50)
self.assertEqual(camera.battery_string, "OK")
self.assertEqual(camera.notifications, 2)
self.assertEqual(camera.region_id, 'test')
self.assertEqual(camera.motion_detected, True)
self.assertEqual(camera.wifi_strength, -30)

camera_config = self.camera_config
camera_config['active'] = 'armed'
camera_config['thumbnail'] = '/test2/image'
camera_config['video'] = '/test2/clip.mp4'
camera_config['temp'] = 60
camera_config['battery'] = 0
camera_config['notifications'] = 4

for name in self.blink.cameras:
camera = self.blink.cameras[name]
camera.update(camera_config)
Expand All @@ -93,8 +109,8 @@ def test_camera_properties(self, mock_get, mock_post):
camera.clip,
"https://rest.test.{}/test2/clip.mp4".format(BLINK_URL)
)
self.assertEqual(camera.temperature, 60)
self.assertEqual(camera.battery, 0)
self.assertEqual(camera.temperature, 68)
self.assertEqual(camera.battery, 50)
self.assertEqual(camera.battery_string, "Low")
self.assertEqual(camera.notifications, 4)
camera_config['battery'] = -10
Expand All @@ -107,7 +123,9 @@ def test_camera_case(self):
self.blink.cameras['foobar'] = camera_object
self.assertEqual(camera_object, self.blink.cameras['fOoBaR'])

def test_camera_attributes(self):
@mock.patch('blinkpy.blinkpy.Blink.camera_config_request',
return_value=CAMERA_CFG)
def test_camera_attributes(self, mock_cfg):
"""Tests camera attributes."""
self.blink.urls = blinkpy.BlinkURLHandler('test')

Expand All @@ -117,6 +135,7 @@ def test_camera_attributes(self):

for name in self.blink.cameras:
camera = self.blink.cameras[name]
camera.update(self.camera_config)
camera_attr = camera.attributes
self.assertEqual(camera_attr['device_id'], '1111')
self.assertEqual(camera_attr['name'], 'foobar')
Expand All @@ -129,7 +148,9 @@ def test_camera_attributes(self):
camera_attr['video'],
"https://rest.test.{}/test/clip/clip.mp4".format(BLINK_URL)
)
self.assertEqual(camera_attr['temperature'], 70)
self.assertEqual(camera_attr['battery'], 3)
self.assertEqual(camera_attr['temperature'], 68)
self.assertEqual(camera_attr['battery'], 50)
self.assertEqual(camera_attr['notifications'], 2)
self.assertEqual(camera_attr['network_id'], '0000')
self.assertEqual(camera_attr['motion_detected'], True)
self.assertEqual(camera_attr['wifi_strength'], -30)
10 changes: 9 additions & 1 deletion tests/test_blink_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,17 @@ def test_get_videos(self, req):
'/test/thumb')

@mock.patch('blinkpy.blinkpy._request')
def test_get_cameras(self, req):
@mock.patch('blinkpy.blinkpy.Blink._video_request')
def test_get_cameras(self, vid_req, req):
"""Test camera extraction."""
req.return_value = {'devices': [self.config]}
vid_req.return_value = [
{
'camera_name': 'foobar',
'address': '/new.mp4',
'thumbnail': '/new'
}
]
self.blink.get_cameras()
self.assertTrue('foobar' in self.blink.cameras)

Expand Down