diff --git a/blinkpy/api.py b/blinkpy/api.py index 665abcd..df0772c 100644 --- a/blinkpy/api.py +++ b/blinkpy/api.py @@ -56,7 +56,7 @@ def request_command_status(blink, network, command_id): return http_get(blink, url) -def request_homescreen(blink, network): +def request_homescreen(blink): """Request homescreen info.""" url = "{}/homescreen".format(blink.urls.base_url) return http_get(blink, url) @@ -144,8 +144,8 @@ def http_get(blink, url, stream=False, json=True): """ if blink.auth_header is None: raise BlinkException(ERROR.AUTH_TOKEN) - return http_req(blink, url=url, headers=blink.auth_header, reqtype='get', - stream=stream, json_resp=json) + return http_req(blink, url=url, headers=blink.auth_header, + reqtype='get', stream=stream, json_resp=json) def http_post(blink, url): diff --git a/blinkpy/camera.py b/blinkpy/camera.py index c02a220..0f18aa5 100644 --- a/blinkpy/camera.py +++ b/blinkpy/camera.py @@ -102,16 +102,29 @@ class BlinkCamera(): self.battery_state = config['battery_state'] self.temperature = config['temperature'] self.wifi_strength = config['wifi_strength'] - if config['thumbnail']: - new_thumbnail = "{}{}.jpg".format(self.sync.urls.base_url, - config['thumbnail']) + # Check if thumbnail exists in config, if not try to + # get it from the homescreen info in teh sync module + # otherwise set it to None and log an error + new_thumbnail = None + if config['thumbnail']: + thumb_addr = config['thumbnail'] + else: + thumb_addr = self.get_thumb_from_homescreen() + + if thumb_addr is not None: + new_thumbnail = "{}{}.jpg".format(self.sync.urls.base_url, + thumb_addr) + + # Check if a new motion clip has been recorded + # check_for_motion_method sets motion_detected variable self.check_for_motion() if self.last_record: clip_addr = self.sync.all_clips[self.name][self.last_record[0]] self.clip = "{}{}".format(self.sync.urls.base_url, clip_addr) + # If the thumbnail or clip have changed, update the cache update_cached_image = False if new_thumbnail != self.thumbnail or self._cached_image is None: update_cached_image = True @@ -121,7 +134,7 @@ class BlinkCamera(): if self._cached_video is None or self.motion_detected: update_cached_video = True - if update_cached_image or force_cache: + if new_thumbnail is not None and (update_cached_image or force_cache): self._cached_image = api.http_get(self.sync.blink, url=self.thumbnail, stream=True, @@ -175,3 +188,17 @@ class BlinkCamera(): response = self._cached_video with open(path, 'wb') as vidfile: copyfileobj(response.raw, vidfile) + + def get_thumb_from_homescreen(self): + """Retrieve thumbnail from homescreen.""" + for device in self.sync.homescreen['devices']: + try: + device_type = device['device_type'] + device_name = device['name'] + device_thumb = device['thumbnail'] + if device_type == 'camera' and device_name == self.name: + return device_thumb + except KeyError: + pass + _LOGGER.error("Could not find thumbnail for camera %s", self.name) + return None diff --git a/blinkpy/helpers/util.py b/blinkpy/helpers/util.py index f6c16a9..23fb58d 100644 --- a/blinkpy/helpers/util.py +++ b/blinkpy/helpers/util.py @@ -1,9 +1,9 @@ """Useful functions for blinkpy.""" import logging -from requests import Request, Session -import blinkpy.helpers.errors as ERROR +from requests import Request, Session, exceptions from blinkpy.helpers.constants import BLINK_URL +import blinkpy.helpers.errors as ERROR _LOGGER = logging.getLogger(__name__) @@ -41,13 +41,20 @@ def http_req(blink, url='http://example.com', data=None, headers=None, elif reqtype == 'get': req = Request('GET', url, headers=headers) else: + _LOGGER.error("Invalid request type: %s", reqtype) raise BlinkException(ERROR.REQUEST) prepped = req.prepare() - response = blink.session.send(prepped, stream=stream) + + try: + response = blink.session.send(prepped, stream=stream) + except (exceptions.ConnectionError, exceptions.Timeout): + _LOGGER.error("Cannot connect to server. Possible outage.") + return None if json_resp and 'code' in response.json(): if is_retry: + _LOGGER.error("Cannot authenticate with server.") raise BlinkAuthenticationException( (response.json()['code'], response.json()['message'])) else: diff --git a/blinkpy/sync_module.py b/blinkpy/sync_module.py index f909b8f..d1fa668 100644 --- a/blinkpy/sync_module.py +++ b/blinkpy/sync_module.py @@ -86,7 +86,7 @@ class BlinkSyncModule(): self.events = self.get_events() - self.homescreen = api.request_homescreen(self.blink, self.network_id) + self.homescreen = api.request_homescreen(self.blink) camera_info = self.get_camera_info() for camera_config in camera_info: @@ -113,7 +113,7 @@ class BlinkSyncModule(): """Get all blink cameras and pulls their most recent status.""" self.events = self.get_events() self.videos = self.get_videos() - self.homescreen = api.request_homescreen(self.blink, self.network_id) + self.homescreen = api.request_homescreen(self.blink) camera_info = self.get_camera_info() for camera_config in camera_info: name = camera_config['name'] diff --git a/tests/test_api.py b/tests/test_api.py new file mode 100644 index 0000000..d50a64e --- /dev/null +++ b/tests/test_api.py @@ -0,0 +1,32 @@ +"""Test various api functions.""" + +import unittest +from blinkpy import api +from blinkpy.blinkpy import Blink +from blinkpy.helpers.util import create_session + + +class TestBlinkAPI(unittest.TestCase): + """Test the blink api module.""" + + def setUp(self): + """Initialize the blink module.""" + self.blink = Blink() + self.blink.session = create_session() + # pylint: disable=protected-access + self.blink._auth_header = {} + + def tearDown(self): + """Tear down blink module.""" + self.blink = None + + def test_http_req_connect_error(self): + """Test http_get error condition.""" + expected = ("ERROR:blinkpy.helpers.util:" + "Cannot connect to server. Possible outage.") + with self.assertLogs() as getlog: + api.http_get(self.blink, 'http://notreal.fake') + with self.assertLogs() as postlog: + api.http_post(self.blink, 'http://notreal.fake') + self.assertEqual(getlog.output, [expected]) + self.assertEqual(postlog.output, [expected]) diff --git a/tests/test_blinkpy.py b/tests/test_blinkpy.py index 62c9a6a..063c724 100644 --- a/tests/test_blinkpy.py +++ b/tests/test_blinkpy.py @@ -95,7 +95,7 @@ class TestBlinkSetup(unittest.TestCase): # pylint: disable=protected-access self.blink._auth_header = bad_header self.assertEqual(self.blink.auth_header, bad_header) - api.request_homescreen(self.blink, '1234') + api.request_homescreen(self.blink) self.assertEqual(self.blink.auth_header, original_header) @mock.patch('blinkpy.blinkpy.time.time') diff --git a/tests/test_cameras.py b/tests/test_cameras.py index 9d6ff90..d063d50 100644 --- a/tests/test_cameras.py +++ b/tests/test_cameras.py @@ -117,3 +117,63 @@ class TestBlinkCameraSetup(unittest.TestCase): 'https://rest.test.immedia-semi.com/test.mp4') self.assertEqual(self.camera.image_from_cache, 'test') self.assertEqual(self.camera.video_from_cache, 'foobar') + + def test_thumbnail_not_in_info(self, mock_sess): + """Test that we grab thumbanil if not in camera_info.""" + mock_sess.side_effect = ['foobar', 'barfoo'] + self.camera.last_record = ['1'] + self.camera.sync.record_dates['new'] = ['1'] + self.camera.sync.all_clips = {'new': {'1': '/test.mp4'}} + config = { + 'name': 'new', + 'camera_id': 1234, + 'network_id': 5678, + 'serial': '12345678', + 'enabled': False, + 'battery_voltage': 90, + 'battery_state': 'ok', + 'temperature': 68, + 'wifi_strength': 4, + 'thumbnail': '', + } + self.camera.sync.homescreen = { + 'devices': [ + {'foo': 'bar'}, + {'device_type': 'foobar'}, + {'device_type': 'camera', + 'name': 'new', + 'thumbnail': '/new/thumb'} + ] + } + self.camera.update(config) + self.assertEqual(self.camera.thumbnail, + 'https://rest.test.immedia-semi.com/new/thumb.jpg') + + def test_no_thumbnails(self, mock_sess): + """Tests that thumbnail is 'None' if none found.""" + mock_sess.return_value = 'foobar' + self.camera.last_record = ['1'] + self.camera.sync.record_dates['new'] = ['1'] + self.camera.sync.all_clips = {'new': {'1': '/test.mp4'}} + config = { + 'name': 'new', + 'camera_id': 1234, + 'network_id': 5678, + 'serial': '12345678', + 'enabled': False, + 'battery_voltage': 90, + 'battery_state': 'ok', + 'temperature': 68, + 'wifi_strength': 4, + 'thumbnail': '', + } + self.camera.sync.homescreen = { + 'devices': [] + } + with self.assertLogs() as logrecord: + self.camera.update(config) + self.assertEqual(self.camera.thumbnail, None) + self.assertEqual( + logrecord.output, + ["ERROR:blinkpy.camera:Could not find thumbnail for camera new"] + )