Modified tests, added homescreen thumbnail backup Fixes #93
This commit is contained in:
+3
-3
@@ -56,7 +56,7 @@ def request_command_status(blink, network, command_id):
|
|||||||
return http_get(blink, url)
|
return http_get(blink, url)
|
||||||
|
|
||||||
|
|
||||||
def request_homescreen(blink, network):
|
def request_homescreen(blink):
|
||||||
"""Request homescreen info."""
|
"""Request homescreen info."""
|
||||||
url = "{}/homescreen".format(blink.urls.base_url)
|
url = "{}/homescreen".format(blink.urls.base_url)
|
||||||
return http_get(blink, 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:
|
if blink.auth_header is None:
|
||||||
raise BlinkException(ERROR.AUTH_TOKEN)
|
raise BlinkException(ERROR.AUTH_TOKEN)
|
||||||
return http_req(blink, url=url, headers=blink.auth_header, reqtype='get',
|
return http_req(blink, url=url, headers=blink.auth_header,
|
||||||
stream=stream, json_resp=json)
|
reqtype='get', stream=stream, json_resp=json)
|
||||||
|
|
||||||
|
|
||||||
def http_post(blink, url):
|
def http_post(blink, url):
|
||||||
|
|||||||
+31
-4
@@ -102,16 +102,29 @@ class BlinkCamera():
|
|||||||
self.battery_state = config['battery_state']
|
self.battery_state = config['battery_state']
|
||||||
self.temperature = config['temperature']
|
self.temperature = config['temperature']
|
||||||
self.wifi_strength = config['wifi_strength']
|
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()
|
self.check_for_motion()
|
||||||
if self.last_record:
|
if self.last_record:
|
||||||
clip_addr = self.sync.all_clips[self.name][self.last_record[0]]
|
clip_addr = self.sync.all_clips[self.name][self.last_record[0]]
|
||||||
self.clip = "{}{}".format(self.sync.urls.base_url,
|
self.clip = "{}{}".format(self.sync.urls.base_url,
|
||||||
clip_addr)
|
clip_addr)
|
||||||
|
|
||||||
|
# If the thumbnail or clip have changed, update the cache
|
||||||
update_cached_image = False
|
update_cached_image = False
|
||||||
if new_thumbnail != self.thumbnail or self._cached_image is None:
|
if new_thumbnail != self.thumbnail or self._cached_image is None:
|
||||||
update_cached_image = True
|
update_cached_image = True
|
||||||
@@ -121,7 +134,7 @@ class BlinkCamera():
|
|||||||
if self._cached_video is None or self.motion_detected:
|
if self._cached_video is None or self.motion_detected:
|
||||||
update_cached_video = True
|
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,
|
self._cached_image = api.http_get(self.sync.blink,
|
||||||
url=self.thumbnail,
|
url=self.thumbnail,
|
||||||
stream=True,
|
stream=True,
|
||||||
@@ -175,3 +188,17 @@ class BlinkCamera():
|
|||||||
response = self._cached_video
|
response = self._cached_video
|
||||||
with open(path, 'wb') as vidfile:
|
with open(path, 'wb') as vidfile:
|
||||||
copyfileobj(response.raw, 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
|
||||||
|
|||||||
+10
-3
@@ -1,9 +1,9 @@
|
|||||||
"""Useful functions for blinkpy."""
|
"""Useful functions for blinkpy."""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from requests import Request, Session
|
from requests import Request, Session, exceptions
|
||||||
import blinkpy.helpers.errors as ERROR
|
|
||||||
from blinkpy.helpers.constants import BLINK_URL
|
from blinkpy.helpers.constants import BLINK_URL
|
||||||
|
import blinkpy.helpers.errors as ERROR
|
||||||
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
@@ -41,13 +41,20 @@ def http_req(blink, url='http://example.com', data=None, headers=None,
|
|||||||
elif reqtype == 'get':
|
elif reqtype == 'get':
|
||||||
req = Request('GET', url, headers=headers)
|
req = Request('GET', url, headers=headers)
|
||||||
else:
|
else:
|
||||||
|
_LOGGER.error("Invalid request type: %s", reqtype)
|
||||||
raise BlinkException(ERROR.REQUEST)
|
raise BlinkException(ERROR.REQUEST)
|
||||||
|
|
||||||
prepped = req.prepare()
|
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 json_resp and 'code' in response.json():
|
||||||
if is_retry:
|
if is_retry:
|
||||||
|
_LOGGER.error("Cannot authenticate with server.")
|
||||||
raise BlinkAuthenticationException(
|
raise BlinkAuthenticationException(
|
||||||
(response.json()['code'], response.json()['message']))
|
(response.json()['code'], response.json()['message']))
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ class BlinkSyncModule():
|
|||||||
|
|
||||||
self.events = self.get_events()
|
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()
|
camera_info = self.get_camera_info()
|
||||||
for camera_config in camera_info:
|
for camera_config in camera_info:
|
||||||
@@ -113,7 +113,7 @@ class BlinkSyncModule():
|
|||||||
"""Get all blink cameras and pulls their most recent status."""
|
"""Get all blink cameras and pulls their most recent status."""
|
||||||
self.events = self.get_events()
|
self.events = self.get_events()
|
||||||
self.videos = self.get_videos()
|
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()
|
camera_info = self.get_camera_info()
|
||||||
for camera_config in camera_info:
|
for camera_config in camera_info:
|
||||||
name = camera_config['name']
|
name = camera_config['name']
|
||||||
|
|||||||
@@ -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])
|
||||||
@@ -95,7 +95,7 @@ class TestBlinkSetup(unittest.TestCase):
|
|||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
self.blink._auth_header = bad_header
|
self.blink._auth_header = bad_header
|
||||||
self.assertEqual(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)
|
self.assertEqual(self.blink.auth_header, original_header)
|
||||||
|
|
||||||
@mock.patch('blinkpy.blinkpy.time.time')
|
@mock.patch('blinkpy.blinkpy.time.time')
|
||||||
|
|||||||
@@ -117,3 +117,63 @@ class TestBlinkCameraSetup(unittest.TestCase):
|
|||||||
'https://rest.test.immedia-semi.com/test.mp4')
|
'https://rest.test.immedia-semi.com/test.mp4')
|
||||||
self.assertEqual(self.camera.image_from_cache, 'test')
|
self.assertEqual(self.camera.image_from_cache, 'test')
|
||||||
self.assertEqual(self.camera.video_from_cache, 'foobar')
|
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"]
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user