Compare commits

...
46 Commits
Author SHA1 Message Date
Kevin FronczakandGitHub 197695ee40 Merge pull request #190 from fronzbot/dev-version-bump
Dev version bump
2019-06-20 13:24:24 -04:00
Kevin Fronczak 490c42550d Dev version bump
- Added Py3.8 to supported versions
2019-06-20 12:42:01 -04:00
Kevin FronczakandGitHub 7367eb6e87 Merge pull request #187 from fronzbot/upgrade-login
Upgraded login urls to use rest-region subdomain
2019-06-18 10:55:32 -04:00
Kevin FronczakandGitHub 414b647dc8 Merge pull request #189 from fronzbot/timeout
Improved timeout, prevent blocking on startup when unable to login
2019-06-17 11:54:17 -04:00
Kevin FronczakandGitHub 57882bb811 Merge pull request #188 from fronzbot/remove-thumbnail-method
Remove thumbnail from homescreen method
2019-06-17 11:17:15 -04:00
Kevin Fronczak bb76f09f31 Fix dev version (will add to different PR) 2019-06-17 11:15:46 -04:00
Kevin Fronczak 741cf489c8 Fix tests for improved login routine 2019-06-17 11:11:07 -04:00
Kevin Fronczak 7ce332b3ca Improved timeout, prevent blocking on startup when unable to login 2019-06-17 11:07:16 -04:00
Kevin Fronczak 8b7a491c89 Remove thumbnail from homescreen method 2019-06-17 10:20:23 -04:00
Kevin Fronczak 604eebd1b7 Upgraded login urls to use rest-region subdomain 2019-06-17 10:04:34 -04:00
Kevin FronczakandGitHub c2f44b3dfd Merge pull request #183 from fronzbot/dev-version-bump
Dev version bump
2019-05-24 09:38:56 -04:00
Kevin Fronczak ccad3dcb0c Dev version bump 2019-05-23 16:53:02 -04:00
Kevin FronczakandGitHub 7767d3af65 Merge pull request #182 from fronzbot/update-changes
Update CHANGES.rst
2019-05-23 16:51:54 -04:00
Kevin FronczakandGitHub 79e699ba78 Update CHANGES.rst 2019-05-23 16:42:12 -04:00
Kevin FronczakandGitHub b28d6f4eb4 Merge pull request #181 from fronzbot/version-bump
Version bump
2019-05-23 16:40:14 -04:00
Kevin FronczakandGitHub d021f48c67 Update CHANGES.rst 2019-05-23 16:15:07 -04:00
Kevin Fronczak 1e71af28dd Version bump 2019-05-23 16:06:56 -04:00
Kevin FronczakandGitHub cb5867d430 Merge pull request #179 from fronzbot/bugfix
Blink instantiation bugfix
2019-05-23 14:45:26 -04:00
Kevin Fronczak bb887936b6 Blink instantiation bugfix 2019-05-23 14:35:27 -04:00
Kevin FronczakandGitHub 720d520398 Merge pull request #175 from fronzbot/update-subdomains
Change subdomain from rest.region to rest-region
2019-05-21 23:56:25 -04:00
Kevin FronczakandGitHub 8fb1116641 Merge branch 'dev' into update-subdomains 2019-05-21 23:37:00 -04:00
Kevin FronczakandGitHub c319762152 Merge pull request #178 from fronzbot/dev-bump
dev version bump
2019-05-21 23:35:25 -04:00
Kevin Fronczak 726d8b8c65 dev version bump 2019-05-21 23:14:26 -04:00
Kevin FronczakandGitHub 683650e2c2 Merge pull request #177 from fronzbot/fix-motion-detect
Fix motion detect
2019-05-21 23:13:15 -04:00
Kevin Fronczak 50b1a35168 Update tests 2019-05-21 22:46:11 -04:00
Kevin Fronczak b88a5feddf Change default interval to one minute 2019-05-21 22:38:49 -04:00
Kevin Fronczak 93472e38de Added in motion interval to allow for looking at historic motion events.
- Good debug utility
- Helps iron out rapid motion events, or missed events due to quick
calls to refresh method
2019-05-21 22:36:46 -04:00
Kevin Fronczak 85c14ede8d Move constants into helpers/constants.py 2019-05-21 22:23:31 -04:00
Kevin Fronczak 58ce109518 Use UTC for time conversions 2019-05-21 22:09:36 -04:00
Kevin Fronczak cf65648293 Add legacy flag to be able to use old 'rest.region' subdomain 2019-05-20 21:15:08 -04:00
Kevin FronczakandGitHub d46e7ed96d Merge pull request #176 from fronzbot/slug
Slugify filenames in video download to ensure OS interoperability
2019-05-20 20:43:28 -04:00
Kevin Fronczak 00395b3825 Slugify filenames in video download to ensure OS interoperability 2019-05-20 11:04:58 -04:00
Kevin FronczakandGitHub f67f0dd6a2 Dev version bump 2019-05-20 00:00:57 -04:00
Kevin FronczakandGitHub d90cd3309f Update README.rst 2019-05-19 18:00:42 -04:00
Kevin Fronczak 130aa8382a Change subdomain from rest.region to rest-region 2019-05-18 18:23:46 -04:00
Kevin FronczakandGitHub e836a6fa58 Merge pull request #174 from fronzbot/fix-battery-voltage
Change battery percentage to state
2019-05-18 18:17:38 -04:00
Kevin Fronczak 92d89b9fe5 Change battery percentage to state 2019-05-18 12:42:01 -04:00
Kevin FronczakandGitHub c10fb432f7 Merge pull request #173 from fronzbot/reduce-throttling
Remove throttling from critical api methods
2019-05-18 12:23:25 -04:00
Kevin Fronczak 37b729b597 Remove throttling from critical api methods 2019-05-18 12:15:57 -04:00
Kevin FronczakandGitHub 749c68bb8a Merge pull request #172 from fronzbot/fix-video-api
Changed log to print
2019-05-18 11:57:46 -04:00
Kevin Fronczak dcf0ce6394 Fix lint issue 2019-05-18 11:51:58 -04:00
Kevin Fronczak 102190e61b Changed log to print 2019-05-18 11:41:17 -04:00
Kevin FronczakandGitHub a7340c97ca Merge pull request #171 from fronzbot/fix-video-api
Added changed video download endpoint
2019-05-18 11:40:23 -04:00
Kevin Fronczak 43c1162634 Added vieo endpoint key changes to motion detect logic 2019-05-18 10:54:24 -04:00
Kevin Fronczak 4db7a33ef3 Added changed video download endpoint 2019-05-18 10:48:58 -04:00
Kevin FronczakandGitHub bfdc1e47bd Dev version bump 2019-03-01 22:00:37 -05:00
13 changed files with 165 additions and 134 deletions
+27 -5
View File
@@ -3,6 +3,28 @@ Changelog
A list of changes between each release
0.14.0 (2019-05-23)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Breaking Changes:**
- ``BlinkCamera.battery`` no longer reports a percentage, instead it returns a string representing the state of the battery.
- Previous logic for calculating percentage was incorrect
- raw battery voltage can be accessed via ``BlinkCamera.battery_voltage``
**Bug Fixes:**
- Updated video endpoint (fixes broken motion detection)
- Removed throttling from critical api methods which prevented proper operation of multi-sync unit setups
- Slugify downloaded video names to allow for OS interoperability
- Added one minute offset (``Blink.motion_interval``) when checking for recent motion to allow time for events to propagate to server prior to refresh call.
**Everything else:**
- Changed all urls to use ``rest-region`` rather than ``rest.region``. Ability to revert to old method is enabled by instantiating ``Blink()`` with the ``legacy_subdomain`` variable set to ``True``.
- Added debug mode to ``blinkpy.download_videos`` routine to simply print the videos prepped for download, rather than actually saving them.
- Use UTC for time conversions, rather than local timezone
0.13.1 (2019-03-01)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Remove throttle decorator from network status request
@@ -10,10 +32,10 @@ A list of changes between each release
0.13.0 (2019-03-01)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
**Breaking change:**
Wifi status reported in dBm again, instead of bars (which is great). Also, the old `get_camera_info` method has changed and requires a `camera_id` parameter.
Wifi status reported in dBm again, instead of bars (which is great). Also, the old ``get_camera_info`` method has changed and requires a ``camera_id`` parameter.
- Adds throttle decorator
- Decorate following functions with 4s throttle (call method with `force=True` to override):
- Decorate following functions with 4s throttle (call method with ``force=True`` to override):
- request_network_status
- request_syncmodule
- request_system_arm
@@ -28,7 +50,7 @@ Wifi status reported in dBm again, instead of bars (which is great). Also, the
- request_motion_detection_enable
- request_motion_detection_disable
- Use the updated homescreen api endpoint to retrieve camera information. The old method to retrieve all cameras at once seems to not exist, and this was the only solution I could figure out and confirm to work.
- Adds throttle decorator to refresh function to prevent too many frequent calls with `force_cache` flag set to `True`. This additional throttle can be overridden with the `force=True` argument passed to the refresh function.
- Adds throttle decorator to refresh function to prevent too many frequent calls with ``force_cache`` flag set to ``True``. This additional throttle can be overridden with the ``force=True`` argument passed to the refresh function.
- Add ability to cycle through login api endpoints to anticipate future endpoint deprecation
@@ -70,7 +92,7 @@ Wifi status reported in dBm again, instead of bars (which is great). Also, the
0.10.2 (2018-10-30)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Set minimum required version of the requests library to 2.20.0 due to vulnerability in earlier releases.
- When multiple networks detected, changed log level to 'warning' from 'error'
- When multiple networks detected, changed log level to ``warning`` from ``error``
0.10.1 (2018-10-18)
@@ -98,7 +120,7 @@ Wifi status reported in dBm again, instead of bars (which is great). Also, the
- Use session for http requests
**Breaking change:**
- Cameras now accessed through sync module Blink.sync.cameras
- Cameras now accessed through sync module ``Blink.sync.cameras``
0.8.1 (2018-09-24)
+7
View File
@@ -2,6 +2,10 @@ blinkpy |Build Status| |Coverage Status| |Docs| |PyPi Version| |Python Version|
================================================================================
A Python library for the Blink Camera system
Like the library? Consider buying me a cup of coffee!
|Donate|
Disclaimer:
~~~~~~~~~~~~~~~
Published under the MIT license - See LICENSE file for more details.
@@ -106,3 +110,6 @@ Example usage, which downloads all videos recorded since July 4th, 2018 at 9:34a
:target: http://blinkpy.readthedocs.io/en/latest/?badge=latest
.. |Python Version| image:: https://img.shields.io/pypi/pyversions/blinkpy.svg
:target: https://img.shields.io/pypi/pyversions/blinkpy.svg
.. |Donate| image:: https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif
:target: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=UR6Z2B8GXYUCC
+2 -5
View File
@@ -40,7 +40,6 @@ def request_networks(blink):
return http_get(blink, url)
@Throttle(seconds=MIN_THROTTLE_TIME)
def request_network_status(blink, network):
"""
Request network information.
@@ -52,7 +51,6 @@ def request_network_status(blink, network):
return http_get(blink, url)
@Throttle(seconds=MIN_THROTTLE_TIME)
def request_syncmodule(blink, network):
"""
Request sync module info.
@@ -168,12 +166,11 @@ def request_videos(blink, time=None, page=0):
:param page: Page number to get videos from.
"""
timestamp = get_time(time)
url = "{}/api/v2/videos/changed?since={}&page={}".format(
blink.urls.base_url, timestamp, page)
url = "{}/api/v1/accounts/{}/media/changed?since={}&page={}".format(
blink.urls.base_url, blink.account_id, timestamp, page)
return http_get(blink, url)
@Throttle(seconds=MIN_THROTTLE_TIME)
def request_cameras(blink, network):
"""
Request all camera information.
+47 -30
View File
@@ -21,6 +21,7 @@ from shutil import copyfileobj
from requests.structures import CaseInsensitiveDict
from dateutil.parser import parse
from slugify import slugify
from blinkpy import api
from blinkpy.sync_module import BlinkSyncModule
@@ -29,14 +30,10 @@ from blinkpy.helpers.util import (
create_session, merge_dicts, get_time, BlinkURLHandler,
BlinkAuthenticationException, Throttle)
from blinkpy.helpers.constants import (
BLINK_URL, LOGIN_URL, OLD_LOGIN_URL, LOGIN_BACKUP_URL)
BLINK_URL, LOGIN_URL, OLD_LOGIN_URL, LOGIN_BACKUP_URL,
DEFAULT_MOTION_INTERVAL, DEFAULT_REFRESH, MIN_THROTTLE_TIME)
from blinkpy.helpers.constants import __version__
REFRESH_RATE = 30
# Prevents rapid calls to blink.refresh()
# with the force_cache flag set to True
MIN_THROTTLE_TIME = 2
_LOGGER = logging.getLogger(__name__)
@@ -45,7 +42,9 @@ class Blink():
"""Class to initialize communication."""
def __init__(self, username=None, password=None,
refresh_rate=REFRESH_RATE):
refresh_rate=DEFAULT_REFRESH,
motion_interval=DEFAULT_MOTION_INTERVAL,
legacy_subdomain=False):
"""
Initialize Blink system.
@@ -53,6 +52,13 @@ class Blink():
:param password: Blink password
:param refresh_rate: Refresh rate of blink information.
Defaults to 15 (seconds)
:param motion_interval: How far back to register motion in minutes.
Defaults to last refresh time.
Useful for preventing motion_detected property
from de-asserting too quickly.
:param legacy_subdomain: Set to TRUE to use old 'rest.region'
endpoints (only use if you are having
api issues).
"""
self._username = username
self._password = password
@@ -72,7 +78,10 @@ class Blink():
self.cameras = CaseInsensitiveDict({})
self.video_list = CaseInsensitiveDict({})
self._login_url = LOGIN_URL
self.login_urls = []
self.motion_interval = motion_interval
self.version = __version__
self.legacy = legacy_subdomain
@property
def auth_header(self):
@@ -123,9 +132,9 @@ class Blink():
if not isinstance(self._password, str):
raise BlinkAuthenticationException(ERROR.PASSWORD)
login_urls = [LOGIN_URL, OLD_LOGIN_URL, LOGIN_BACKUP_URL]
self.login_urls = [LOGIN_URL, OLD_LOGIN_URL, LOGIN_BACKUP_URL]
response = self.login_request(login_urls, is_retry=is_retry)
response = self.login_request(is_retry=is_retry)
if not response:
return False
@@ -136,14 +145,14 @@ class Blink():
self._auth_header = {'Host': self._host,
'TOKEN_AUTH': self._token}
self.urls = BlinkURLHandler(self.region_id)
self.urls = BlinkURLHandler(self.region_id, legacy=self.legacy)
return self._auth_header
def login_request(self, login_urls, is_retry=False):
def login_request(self, is_retry=False):
"""Make a login request."""
try:
login_url = login_urls.pop(0)
login_url = self.login_urls.pop(0)
except IndexError:
_LOGGER.error("Could not login to blink servers.")
return False
@@ -157,14 +166,13 @@ class Blink():
is_retry=is_retry)
try:
if response.status_code != 200:
response = self.login_request(login_urls)
response = self.login_request(is_retry=True)
response = response.json()
(self.region_id, self.region), = response['region'].items()
except AttributeError:
_LOGGER.error("Login API endpoint failed with response %s",
response,
exc_info=True)
response)
return False
except KeyError:
@@ -247,7 +255,8 @@ class Blink():
combined = merge_dicts(combined, self.sync[sync].cameras)
return combined
def download_videos(self, path, since=None, camera='all', stop=10):
def download_videos(self, path, since=None,
camera='all', stop=10, debug=False):
"""
Download all videos from server since specified time.
@@ -258,6 +267,8 @@ class Blink():
:param camera: Camera name to retrieve. Defaults to "all".
Use a list for multiple cameras.
:param stop: Page to stop on (~25 items per page. Default page 10).
:param debug: Set to TRUE to prevent downloading of items.
Instead of downloading, entries will be printed to log.
"""
if since is None:
since_epochs = self.last_refresh
@@ -275,23 +286,23 @@ class Blink():
response = api.request_videos(self, time=since_epochs, page=page)
_LOGGER.debug("Processing page %s", page)
try:
result = response['videos']
result = response['media']
if not result:
raise IndexError
except (KeyError, IndexError):
_LOGGER.info("No videos found on page %s. Exiting.", page)
break
self._parse_downloaded_items(result, camera, path)
self._parse_downloaded_items(result, camera, path, debug)
def _parse_downloaded_items(self, result, camera, path):
def _parse_downloaded_items(self, result, camera, path, debug):
"""Parse downloaded videos."""
for item in result:
try:
created_at = item['created_at']
camera_name = item['camera_name']
camera_name = item['device_name']
is_deleted = item['deleted']
address = item['address']
address = item['media']
except KeyError:
_LOGGER.info("Missing clip information, skipping...")
continue
@@ -307,16 +318,22 @@ class Blink():
continue
clip_address = "{}{}".format(self.urls.base_url, address)
filename = "{}_{}.mp4".format(camera_name, created_at)
filename = "{}-{}".format(camera_name, created_at)
filename = "{}.mp4".format(slugify(filename))
filename = os.path.join(path, filename)
if os.path.isfile(filename):
_LOGGER.info("%s already exists, skipping...", filename)
continue
if not debug:
if os.path.isfile(filename):
_LOGGER.info("%s already exists, skipping...", filename)
continue
response = api.http_get(self, url=clip_address,
stream=True, json=False)
with open(filename, 'wb') as vidfile:
copyfileobj(response.raw, vidfile)
response = api.http_get(self, url=clip_address,
stream=True, json=False)
with open(filename, 'wb') as vidfile:
copyfileobj(response.raw, vidfile)
_LOGGER.info("Downloaded video to %s", filename)
_LOGGER.info("Downloaded video to %s", filename)
else:
print(("Camera: {}, Timestamp: {}, "
"Address: {}, Filename: {}").format(
camera_name, created_at, address, filename))
+8 -20
View File
@@ -41,6 +41,7 @@ class BlinkCamera():
'temperature_c': self.temperature_c,
'temperature_calibrated': self.temperature_calibrated,
'battery': self.battery,
'battery_voltage': self.battery_voltage,
'thumbnail': self.thumbnail,
'video': self.clip,
'motion_enabled': self.motion_enabled,
@@ -54,8 +55,8 @@ class BlinkCamera():
@property
def battery(self):
"""Return battery level as percentage."""
return round(self.battery_voltage / 180 * 100)
"""Return battery as string."""
return self.battery_state
@property
def temperature_c(self):
@@ -116,13 +117,16 @@ class BlinkCamera():
_LOGGER.warning("Could not retrieve calibrated temperature.")
# Check if thumbnail exists in config, if not try to
# get it from the homescreen info in teh sync module
# get it from the homescreen info in the sync module
# otherwise set it to None and log an error
new_thumbnail = None
thumb_addr = None
if config['thumbnail']:
thumb_addr = config['thumbnail']
else:
thumb_addr = self.get_thumb_from_homescreen()
_LOGGER.warning("Could not find thumbnail for camera %s",
self.name,
exc_info=True)
if thumb_addr is not None:
new_thumbnail = "{}{}.jpg".format(self.sync.urls.base_url,
@@ -191,19 +195,3 @@ class BlinkCamera():
return
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,
exc_info=True)
return None
+9 -4
View File
@@ -3,8 +3,8 @@
import os
MAJOR_VERSION = 0
MINOR_VERSION = 13
PATCH_VERSION = 1
MINOR_VERSION = 14
PATCH_VERSION = '1.dev0'
__version__ = '{}.{}.{}'.format(MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION)
@@ -33,6 +33,7 @@ PROJECT_CLASSIFIERS = [
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Topic :: Home Automation'
]
@@ -45,11 +46,11 @@ PYPI_URL = 'https://pypi.python.org/pypi/{}'.format(PROJECT_PACKAGE_NAME)
URLS
'''
BLINK_URL = 'immedia-semi.com'
DEFAULT_URL = "{}.{}".format('prod', BLINK_URL)
DEFAULT_URL = "{}.{}".format('rest-prod', BLINK_URL)
BASE_URL = "https://{}".format(DEFAULT_URL)
LOGIN_URL = "{}/api/v2/login".format(BASE_URL)
OLD_LOGIN_URL = "{}/login".format(BASE_URL)
LOGIN_BACKUP_URL = "https://{}.{}/login".format('rest.piri', BLINK_URL)
LOGIN_BACKUP_URL = "https://{}.{}/login".format('rest-piri', BLINK_URL)
'''
Dictionaries
@@ -60,3 +61,7 @@ ONLINE = {'online': True, 'offline': False}
OTHER
'''
TIMESTAMP_FORMAT = '%Y-%m-%dT%H:%M:%S%Z'
DEFAULT_MOTION_INTERVAL = 1
DEFAULT_REFRESH = 30
MIN_THROTTLE_TIME = 2
+15 -6
View File
@@ -2,7 +2,7 @@
import logging
import time
from functools import wraps
from functools import partial, wraps
from requests import Request, Session, exceptions
from blinkpy.helpers.constants import BLINK_URL, TIMESTAMP_FORMAT
import blinkpy.helpers.errors as ERROR
@@ -15,7 +15,7 @@ def get_time(time_to_convert=None):
"""Create blink-compatible timestamp."""
if time_to_convert is None:
time_to_convert = time.time()
return time.strftime(TIMESTAMP_FORMAT, time.localtime(time_to_convert))
return time.strftime(TIMESTAMP_FORMAT, time.gmtime(time_to_convert))
def merge_dicts(dict_a, dict_b):
@@ -28,8 +28,14 @@ def merge_dicts(dict_a, dict_b):
def create_session():
"""Create a session for blink communication."""
"""
Create a session for blink communication.
From @ericfrederich via
https://github.com/kennethreitz/requests/issues/2011
"""
sess = Session()
sess.get = partial(sess.get, timeout=5)
return sess
@@ -65,7 +71,7 @@ def http_req(blink, url='http://example.com', data=None, headers=None,
prepped = req.prepare()
try:
response = blink.session.send(prepped, stream=stream, timeout=10)
response = blink.session.send(prepped, stream=stream)
if json_resp and 'code' in response.json():
if is_retry:
_LOGGER.error("Cannot obtain new token for server auth.")
@@ -111,9 +117,12 @@ class BlinkAuthenticationException(BlinkException):
class BlinkURLHandler():
"""Class that handles Blink URLS."""
def __init__(self, region_id):
def __init__(self, region_id, legacy=False):
"""Initialize the urls."""
self.base_url = "https://rest.{}.{}".format(region_id, BLINK_URL)
self.subdomain = 'rest-{}'.format(region_id)
if legacy:
self.subdomain = 'rest.{}'.format(region_id)
self.base_url = "https://{}.{}".format(self.subdomain, BLINK_URL)
self.home_url = "{}/homescreen".format(self.base_url)
self.event_url = "{}/events/network".format(self.base_url)
self.network_url = "{}/network".format(self.base_url)
+14 -7
View File
@@ -33,6 +33,7 @@ class BlinkSyncModule():
self.network_info = None
self.events = []
self.cameras = CaseInsensitiveDict({})
self.motion_interval = blink.motion_interval
self.motion = {}
self.last_record = {}
self.camera_list = camera_list
@@ -80,8 +81,7 @@ class BlinkSyncModule():
def start(self):
"""Initialize the system."""
response = api.request_syncmodule(self.blink,
self.network_id,
force=True)
self.network_id)
try:
self.summary = response['syncmodule']
self.network_id = self.summary['network_id']
@@ -162,23 +162,30 @@ class BlinkSyncModule():
def check_new_videos(self):
"""Check if new videos since last refresh."""
try:
interval = self.blink.last_refresh - self.motion_interval*60
except TypeError:
# This is the first start, so refresh hasn't happened yet.
# No need to check for motion.
return False
resp = api.request_videos(self.blink,
time=self.blink.last_refresh,
page=0)
time=interval,
page=1)
for camera in self.cameras.keys():
self.motion[camera] = False
try:
info = resp['videos']
info = resp['media']
except (KeyError, TypeError):
_LOGGER.warning("Could not check for motion. Response: %s", resp)
return False
for entry in info:
try:
name = entry['camera_name']
clip = entry['address']
name = entry['device_name']
clip = entry['media']
timestamp = entry['created_at']
self.motion[name] = True
self.last_record[name] = {'clip': clip, 'time': timestamp}
+1
View File
@@ -1,3 +1,4 @@
python-dateutil==2.7.5
requests>=2.20.0
python-slugify==3.0.2
testtools==2.3.0
+10 -8
View File
@@ -69,7 +69,8 @@ class TestBlinkFunctions(unittest.TestCase):
bad_req,
new_req
]
self.blink.login_request(['test1', 'test2', 'test3'])
self.blink.login_urls = ['test1', 'test2', 'test3']
self.blink.login_request()
# pylint: disable=protected-access
self.assertEqual(self.blink._login_url, 'test3')
@@ -78,7 +79,8 @@ class TestBlinkFunctions(unittest.TestCase):
new_req,
bad_req
]
self.blink.login_request(['test1', 'test2', 'test3'])
self.blink.login_urls = ['test1', 'test2', 'test3']
self.blink.login_request()
# pylint: disable=protected-access
self.assertEqual(self.blink._login_url, 'test2')
@@ -121,12 +123,12 @@ class TestBlinkFunctions(unittest.TestCase):
blinkpy._LOGGER.setLevel(logging.DEBUG)
generic_entry = {
'created_at': '1970',
'camera_name': 'foo',
'device_name': 'foo',
'deleted': True,
'address': '/bar.mp4'
'media': '/bar.mp4'
}
result = [generic_entry]
mock_req.return_value = {'videos': result}
mock_req.return_value = {'media': result}
blink.last_refresh = 0
formatted_date = get_time(blink.last_refresh)
expected_log = [
@@ -147,12 +149,12 @@ class TestBlinkFunctions(unittest.TestCase):
blinkpy._LOGGER.setLevel(logging.DEBUG)
generic_entry = {
'created_at': '1970',
'camera_name': 'foo',
'device_name': 'foo',
'deleted': True,
'address': '/bar.mp4'
'media': '/bar.mp4'
}
result = [generic_entry]
mock_req.return_value = {'videos': result}
mock_req.return_value = {'media': result}
blink.last_refresh = 0
formatted_date = get_time(blink.last_refresh)
expected_log = [
+4 -43
View File
@@ -87,57 +87,18 @@ class TestBlinkCameraSetup(unittest.TestCase):
self.assertEqual(self.camera.network_id, '5678')
self.assertEqual(self.camera.serial, '12345678')
self.assertEqual(self.camera.motion_enabled, False)
self.assertEqual(self.camera.battery, 50)
self.assertEqual(self.camera.battery, 'ok')
self.assertEqual(self.camera.temperature, 68)
self.assertEqual(self.camera.temperature_c, 20)
self.assertEqual(self.camera.temperature_calibrated, 71)
self.assertEqual(self.camera.wifi_strength, 4)
self.assertEqual(self.camera.thumbnail,
'https://rest.test.immedia-semi.com/thumb.jpg')
'https://rest-test.immedia-semi.com/thumb.jpg')
self.assertEqual(self.camera.clip,
'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.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 = [
mresp.MockResponse({'temp': 71}, 200),
'foobar',
'barfoo'
]
self.camera.last_record = ['1']
self.camera.sync.last_record = {
'new': {
'clip': '/test.mp4',
'time': '1970-01-01T00:00:00'
}
}
config = {
'name': 'new',
'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'
@@ -167,7 +128,7 @@ class TestBlinkCameraSetup(unittest.TestCase):
logrecord.output,
[("WARNING:blinkpy.camera:Could not retrieve calibrated "
"temperature."),
("ERROR:blinkpy.camera:Could not find thumbnail for camera new"
("WARNING:blinkpy.camera:Could not find thumbnail for camera new"
"\nNoneType: None")]
)
+13 -5
View File
@@ -17,12 +17,14 @@ class TestBlinkSyncModule(unittest.TestCase):
def setUp(self):
"""Set up Blink module."""
self.blink = blinkpy.Blink(username=USERNAME,
password=PASSWORD)
password=PASSWORD,
motion_interval=0)
# pylint: disable=protected-access
self.blink._auth_header = {
'Host': 'test.url.tld',
'TOKEN_AUTH': 'foobar123'
}
self.blink.last_refresh = 0
self.blink.urls = blinkpy.BlinkURLHandler('test')
self.blink.sync['test'] = BlinkSyncModule(self.blink,
'test',
@@ -59,12 +61,18 @@ class TestBlinkSyncModule(unittest.TestCase):
self.assertEqual(self.blink.sync['test'].get_camera_info('1234'),
'foobar')
def test_check_new_videos_startup(self, mock_resp):
"""Test that check_new_videos does not block startup."""
sync_module = self.blink.sync['test']
self.blink.last_refresh = None
self.assertFalse(sync_module.check_new_videos())
def test_check_new_videos(self, mock_resp):
"""Test recent video response."""
mock_resp.return_value = {
'videos': [{
'camera_name': 'foo',
'address': '/foo/bar.mp4',
'media': [{
'device_name': 'foo',
'media': '/foo/bar.mp4',
'created_at': '1970-01-01T00:00:00+0:00'
}]
}
@@ -76,7 +84,7 @@ class TestBlinkSyncModule(unittest.TestCase):
{'clip': '/foo/bar.mp4',
'time': '1970-01-01T00:00:00+0:00'})
self.assertEqual(sync_module.motion, {'foo': True})
mock_resp.return_value = {'videos': []}
mock_resp.return_value = {'media': []}
self.assertTrue(sync_module.check_new_videos())
self.assertEqual(sync_module.motion, {'foo': False})
self.assertEqual(sync_module.last_record['foo'],
+8 -1
View File
@@ -3,7 +3,7 @@
import unittest
from unittest import mock
import time
from blinkpy.helpers.util import Throttle
from blinkpy.helpers.util import Throttle, BlinkURLHandler
class TestUtil(unittest.TestCase):
@@ -98,3 +98,10 @@ class TestUtil(unittest.TestCase):
with mock.patch('time.time', return_value=now_plus_6):
self.assertEqual(tester.test1(), None)
self.assertEqual(tester.test2(), True)
def test_legacy_subdomains(self):
"""Test that subdomain can be set to legacy mode."""
urls = BlinkURLHandler('test')
self.assertEqual(urls.subdomain, 'rest-test')
urls = BlinkURLHandler('test', legacy=True)
self.assertEqual(urls.subdomain, 'rest.test')