Compare commits
33
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42e444c0e0 | ||
|
|
2baae9ea45 | ||
|
|
789724a6a3 | ||
|
|
e53439141a | ||
|
|
d5ec39113e | ||
|
|
58117b3daf | ||
|
|
558c190512 | ||
|
|
9e124ce5cc | ||
|
|
396824d38c | ||
|
|
cc860c3d03 | ||
|
|
6a5256e9b1 | ||
|
|
00c2c2cc29 | ||
|
|
4775bda51c | ||
|
|
38fed26742 | ||
|
|
99235fbc80 | ||
|
|
feef686767 | ||
|
|
1e00f657a7 | ||
|
|
672fca6dbf | ||
|
|
bc85779272 | ||
|
|
669fb008d1 | ||
|
|
7281241edd | ||
|
|
9394964688 | ||
|
|
d8c80c24fc | ||
|
|
12f7e65d0a | ||
|
|
d31cb60a17 | ||
|
|
1b8bae3d25 | ||
|
|
598b2e4235 | ||
|
|
569249a6f4 | ||
|
|
754d441111 | ||
|
|
4ca9cc21fe | ||
|
|
2fc3e735c5 | ||
|
|
ca60d99e90 | ||
|
|
f0445683ca |
+19
-35
@@ -7,7 +7,7 @@ from blinkpy.helpers.constants import DEFAULT_URL
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
MIN_THROTTLE_TIME = 2
|
||||
MIN_THROTTLE_TIME = 5
|
||||
|
||||
|
||||
def request_login(
|
||||
@@ -47,9 +47,7 @@ def request_login(
|
||||
|
||||
def request_verify(auth, blink, verify_key):
|
||||
"""Send verification key to blink servers."""
|
||||
url = "{}/api/v4/account/{}/client/{}/pin/verify".format(
|
||||
blink.urls.base_url, blink.account_id, blink.client_id
|
||||
)
|
||||
url = f"{blink.urls.base_url}/api/v4/account/{blink.account_id}/client/{blink.client_id}/pin/verify"
|
||||
data = dumps({"pin": verify_key})
|
||||
return auth.query(
|
||||
url=url, headers=auth.header, data=data, json_resp=False, reqtype="post",
|
||||
@@ -58,7 +56,7 @@ def request_verify(auth, blink, verify_key):
|
||||
|
||||
def request_networks(blink):
|
||||
"""Request all networks information."""
|
||||
url = "{}/networks".format(blink.urls.base_url)
|
||||
url = f"{blink.urls.base_url}/networks"
|
||||
return http_get(blink, url)
|
||||
|
||||
|
||||
@@ -69,7 +67,7 @@ def request_network_status(blink, network):
|
||||
:param blink: Blink instance.
|
||||
:param network: Sync module network id.
|
||||
"""
|
||||
url = "{}/network/{}".format(blink.urls.base_url, network)
|
||||
url = f"{blink.urls.base_url}/network/{network}"
|
||||
return http_get(blink, url)
|
||||
|
||||
|
||||
@@ -80,7 +78,7 @@ def request_syncmodule(blink, network):
|
||||
:param blink: Blink instance.
|
||||
:param network: Sync module network id.
|
||||
"""
|
||||
url = "{}/network/{}/syncmodules".format(blink.urls.base_url, network)
|
||||
url = f"{blink.urls.base_url}/network/{network}/syncmodules"
|
||||
return http_get(blink, url)
|
||||
|
||||
|
||||
@@ -92,7 +90,7 @@ def request_system_arm(blink, network):
|
||||
:param blink: Blink instance.
|
||||
:param network: Sync module network id.
|
||||
"""
|
||||
url = "{}/network/{}/arm".format(blink.urls.base_url, network)
|
||||
url = f"{blink.urls.base_url}/network/{network}/arm"
|
||||
return http_post(blink, url)
|
||||
|
||||
|
||||
@@ -104,7 +102,7 @@ def request_system_disarm(blink, network):
|
||||
:param blink: Blink instance.
|
||||
:param network: Sync module network id.
|
||||
"""
|
||||
url = "{}/network/{}/disarm".format(blink.urls.base_url, network)
|
||||
url = f"{blink.urls.base_url}/network/{network}/disarm"
|
||||
return http_post(blink, url)
|
||||
|
||||
|
||||
@@ -116,16 +114,14 @@ def request_command_status(blink, network, command_id):
|
||||
:param network: Sync module network id.
|
||||
:param command_id: Command id to check.
|
||||
"""
|
||||
url = "{}/network/{}/command/{}".format(blink.urls.base_url, network, command_id)
|
||||
url = f"{blink.urls.base_url}/network/{network}/command/{command_id}"
|
||||
return http_get(blink, url)
|
||||
|
||||
|
||||
@Throttle(seconds=MIN_THROTTLE_TIME)
|
||||
def request_homescreen(blink):
|
||||
"""Request homescreen info."""
|
||||
url = "{}/api/v3/accounts/{}/homescreen".format(
|
||||
blink.urls.base_url, blink.account_id
|
||||
)
|
||||
url = f"{blink.urls.base_url}/api/v3/accounts/{blink.account_id}/homescreen"
|
||||
return http_get(blink, url)
|
||||
|
||||
|
||||
@@ -137,7 +133,7 @@ def request_sync_events(blink, network):
|
||||
:param blink: Blink instance.
|
||||
:param network: Sync module network id.
|
||||
"""
|
||||
url = "{}/events/network/{}".format(blink.urls.base_url, network)
|
||||
url = f"{blink.urls.base_url}/events/network/{network}"
|
||||
return http_get(blink, url)
|
||||
|
||||
|
||||
@@ -150,9 +146,7 @@ def request_new_image(blink, network, camera_id):
|
||||
:param network: Sync module network id.
|
||||
:param camera_id: Camera ID of camera to request new image from.
|
||||
"""
|
||||
url = "{}/network/{}/camera/{}/thumbnail".format(
|
||||
blink.urls.base_url, network, camera_id
|
||||
)
|
||||
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/thumbnail"
|
||||
return http_post(blink, url)
|
||||
|
||||
|
||||
@@ -165,14 +159,14 @@ def request_new_video(blink, network, camera_id):
|
||||
:param network: Sync module network id.
|
||||
:param camera_id: Camera ID of camera to request new video from.
|
||||
"""
|
||||
url = "{}/network/{}/camera/{}/clip".format(blink.urls.base_url, network, camera_id)
|
||||
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/clip"
|
||||
return http_post(blink, url)
|
||||
|
||||
|
||||
@Throttle(seconds=MIN_THROTTLE_TIME)
|
||||
def request_video_count(blink):
|
||||
"""Request total video count."""
|
||||
url = "{}/api/v2/videos/count".format(blink.urls.base_url)
|
||||
url = f"{blink.urls.base_url}/api/v2/videos/count"
|
||||
return http_get(blink, url)
|
||||
|
||||
|
||||
@@ -185,9 +179,7 @@ def request_videos(blink, time=None, page=0):
|
||||
:param page: Page number to get videos from.
|
||||
"""
|
||||
timestamp = get_time(time)
|
||||
url = "{}/api/v1/accounts/{}/media/changed?since={}&page={}".format(
|
||||
blink.urls.base_url, blink.account_id, timestamp, page
|
||||
)
|
||||
url = f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}/media/changed?since={timestamp}&page={page}"
|
||||
return http_get(blink, url)
|
||||
|
||||
|
||||
@@ -198,7 +190,7 @@ def request_cameras(blink, network):
|
||||
:param Blink: Blink instance.
|
||||
:param network: Sync module network id.
|
||||
"""
|
||||
url = "{}/network/{}/cameras".format(blink.urls.base_url, network)
|
||||
url = f"{blink.urls.base_url}/network/{network}/cameras"
|
||||
return http_get(blink, url)
|
||||
|
||||
|
||||
@@ -210,9 +202,7 @@ def request_camera_info(blink, network, camera_id):
|
||||
:param network: Sync module network id.
|
||||
:param camera_id: Camera ID of camera to request info from.
|
||||
"""
|
||||
url = "{}/network/{}/camera/{}/config".format(
|
||||
blink.urls.base_url, network, camera_id
|
||||
)
|
||||
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/config"
|
||||
return http_get(blink, url)
|
||||
|
||||
|
||||
@@ -224,9 +214,7 @@ def request_camera_sensors(blink, network, camera_id):
|
||||
:param network: Sync module network id.
|
||||
:param camera_id: Camera ID of camera to request sesnor info from.
|
||||
"""
|
||||
url = "{}/network/{}/camera/{}/signals".format(
|
||||
blink.urls.base_url, network, camera_id
|
||||
)
|
||||
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/signals"
|
||||
return http_get(blink, url)
|
||||
|
||||
|
||||
@@ -239,9 +227,7 @@ def request_motion_detection_enable(blink, network, camera_id):
|
||||
:param network: Sync module network id.
|
||||
:param camera_id: Camera ID of camera to enable.
|
||||
"""
|
||||
url = "{}/network/{}/camera/{}/enable".format(
|
||||
blink.urls.base_url, network, camera_id
|
||||
)
|
||||
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/enable"
|
||||
return http_post(blink, url)
|
||||
|
||||
|
||||
@@ -253,9 +239,7 @@ def request_motion_detection_disable(blink, network, camera_id):
|
||||
:param network: Sync module network id.
|
||||
:param camera_id: Camera ID of camera to disable.
|
||||
"""
|
||||
url = "{}/network/{}/camera/{}/disable".format(
|
||||
blink.urls.base_url, network, camera_id
|
||||
)
|
||||
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/disable"
|
||||
return http_post(blink, url)
|
||||
|
||||
|
||||
|
||||
+44
-12
@@ -5,7 +5,6 @@ from requests import Request, Session, exceptions
|
||||
from blinkpy import api
|
||||
from blinkpy.helpers import util
|
||||
from blinkpy.helpers.constants import BLINK_URL, LOGIN_ENDPOINT
|
||||
from blinkpy.helpers import errors as ERROR
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -33,6 +32,7 @@ class Auth:
|
||||
self.client_id = login_data.get("client_id", None)
|
||||
self.account_id = login_data.get("account_id", None)
|
||||
self.login_response = None
|
||||
self.is_errored = False
|
||||
self.no_prompt = no_prompt
|
||||
self.session = self.create_session()
|
||||
|
||||
@@ -87,6 +87,7 @@ class Auth:
|
||||
|
||||
def refresh_token(self):
|
||||
"""Refresh auth token."""
|
||||
self.is_errored = True
|
||||
try:
|
||||
_LOGGER.info("Token expired, attempting automatic refresh.")
|
||||
self.login_response = self.login()
|
||||
@@ -95,7 +96,11 @@ class Auth:
|
||||
self.token = self.login_response["authtoken"]["authtoken"]
|
||||
self.client_id = self.login_response["client"]["id"]
|
||||
self.account_id = self.login_response["account"]["id"]
|
||||
except KeyError:
|
||||
self.is_errored = False
|
||||
except LoginError:
|
||||
_LOGGER.error("Login endpoint failed. Try again later.")
|
||||
raise TokenRefreshFailed
|
||||
except (TypeError, KeyError):
|
||||
_LOGGER.error("Malformed login response: %s", self.login_response)
|
||||
raise TokenRefreshFailed
|
||||
return True
|
||||
@@ -109,15 +114,21 @@ class Auth:
|
||||
def validate_response(self, response, json_resp):
|
||||
"""Check for valid response."""
|
||||
if not json_resp:
|
||||
self.is_errored = False
|
||||
return response
|
||||
|
||||
json_data = response.json()
|
||||
self.is_errored = True
|
||||
try:
|
||||
if json_data["code"] in ERROR.BLINK_ERRORS:
|
||||
if response.status_code in [101, 401]:
|
||||
raise UnauthorizedError
|
||||
if response.status_code == 404:
|
||||
raise exceptions.ConnectionError
|
||||
json_data = response.json()
|
||||
except KeyError:
|
||||
pass
|
||||
except (AttributeError, ValueError):
|
||||
raise BlinkBadResponse
|
||||
|
||||
self.is_errored = False
|
||||
return json_data
|
||||
|
||||
def query(
|
||||
@@ -139,29 +150,42 @@ class Auth:
|
||||
:param reqtype: Can be 'get' or 'post' (default: 'get')
|
||||
:param stream: Stream response? True/FALSE
|
||||
:param json_resp: Return JSON response? TRUE/False
|
||||
:param is_retry: Is this a retry attempt? True/FALSE
|
||||
:param is_retry: Is this part of a re-auth attempt? True/FALSE
|
||||
"""
|
||||
req = self.prepare_request(url, headers, data, reqtype)
|
||||
try:
|
||||
response = self.session.send(req, stream=stream)
|
||||
return self.validate_response(response, json_resp)
|
||||
|
||||
except (exceptions.ConnectionError, exceptions.Timeout, TokenRefreshFailed):
|
||||
except (exceptions.ConnectionError, exceptions.Timeout):
|
||||
_LOGGER.error(
|
||||
"Connection error. Endpoint %s possibly down or throttled. %s: %s",
|
||||
url,
|
||||
response.status_code,
|
||||
response.reason,
|
||||
)
|
||||
except BlinkBadResponse:
|
||||
_LOGGER.error(
|
||||
"Expected json response from %s, but received: %s: %s",
|
||||
url,
|
||||
response.status_code,
|
||||
response.reason,
|
||||
)
|
||||
except UnauthorizedError:
|
||||
try:
|
||||
if not is_retry:
|
||||
self.refresh_token()
|
||||
return self.query(
|
||||
url=url,
|
||||
data=data,
|
||||
headers=headers,
|
||||
headers=self.header,
|
||||
reqtype=reqtype,
|
||||
stream=stream,
|
||||
json_resp=json_resp,
|
||||
is_retry=True,
|
||||
)
|
||||
except (TokenRefreshFailed, LoginError):
|
||||
_LOGGER.error("Endpoint %s failed. Unable to refresh login tokens", url)
|
||||
_LOGGER.error("Endpoint %s failed", url)
|
||||
_LOGGER.error("Unable to access %s after token refresh.", url)
|
||||
except TokenRefreshFailed:
|
||||
_LOGGER.error("Unable to refresh token.")
|
||||
return None
|
||||
|
||||
def send_auth_key(self, blink, key):
|
||||
@@ -192,3 +216,11 @@ class TokenRefreshFailed(Exception):
|
||||
|
||||
class LoginError(Exception):
|
||||
"""Class to throw failed login exception."""
|
||||
|
||||
|
||||
class BlinkBadResponse(Exception):
|
||||
"""Class to throw bad json response exception."""
|
||||
|
||||
|
||||
class UnauthorizedError(Exception):
|
||||
"""Class to throw an unauthorized access error."""
|
||||
|
||||
+10
-10
@@ -78,7 +78,6 @@ class Blink:
|
||||
"""
|
||||
if self.check_if_ok_to_update() or force:
|
||||
if not self.available:
|
||||
self.auth.refresh_token()
|
||||
self.setup_post_verify()
|
||||
|
||||
for sync_name, sync_module in self.sync.items():
|
||||
@@ -152,7 +151,7 @@ class Blink:
|
||||
{"name": camera["name"], "id": camera["id"]}
|
||||
)
|
||||
return all_cameras
|
||||
except KeyError:
|
||||
except (KeyError, TypeError):
|
||||
_LOGGER.error("Unable to retrieve cameras from response %s", response)
|
||||
raise BlinkSetupError
|
||||
|
||||
@@ -176,7 +175,7 @@ class Blink:
|
||||
response = api.request_networks(self)
|
||||
try:
|
||||
self.networks = response["summary"]
|
||||
except KeyError:
|
||||
except (KeyError, TypeError):
|
||||
raise BlinkSetupError
|
||||
|
||||
def setup_network_ids(self):
|
||||
@@ -250,8 +249,8 @@ class Blink:
|
||||
try:
|
||||
result = response["media"]
|
||||
if not result:
|
||||
raise IndexError
|
||||
except (KeyError, IndexError):
|
||||
raise KeyError
|
||||
except (KeyError, TypeError):
|
||||
_LOGGER.info("No videos found on page %s. Exiting.", page)
|
||||
break
|
||||
|
||||
@@ -277,9 +276,9 @@ class Blink:
|
||||
_LOGGER.debug("%s: %s is marked as deleted.", camera_name, address)
|
||||
continue
|
||||
|
||||
clip_address = "{}{}".format(self.urls.base_url, address)
|
||||
filename = "{}-{}".format(camera_name, created_at)
|
||||
filename = "{}.mp4".format(slugify(filename))
|
||||
clip_address = f"{self.urls.base_url}{address}"
|
||||
filename = f"{camera_name}-{created_at}"
|
||||
filename = f"{slugify(filename)}.mp4"
|
||||
filename = os.path.join(path, filename)
|
||||
|
||||
if not debug:
|
||||
@@ -294,8 +293,9 @@ class Blink:
|
||||
_LOGGER.info("Downloaded video to %s", filename)
|
||||
else:
|
||||
print(
|
||||
("Camera: {}, Timestamp: {}, " "Address: {}, Filename: {}").format(
|
||||
camera_name, created_at, address, filename
|
||||
(
|
||||
f"Camera: {camera_name}, Timestamp: {created_at}, "
|
||||
"Address: {address}, Filename: {filename}"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
+21
-2
@@ -77,12 +77,31 @@ class BlinkCamera:
|
||||
return self._cached_video
|
||||
return None
|
||||
|
||||
@property
|
||||
def arm(self):
|
||||
"""Return arm status of camera."""
|
||||
return self.motion_enabled
|
||||
|
||||
@arm.setter
|
||||
def arm(self, value):
|
||||
"""Set camera arm status."""
|
||||
if value:
|
||||
return api.request_motion_detection_enable(
|
||||
self.sync.blink, self.network_id, self.camera_id
|
||||
)
|
||||
return api.request_motion_detection_disable(
|
||||
self.sync.blink, self.network_id, self.camera_id
|
||||
)
|
||||
|
||||
def snap_picture(self):
|
||||
"""Take a picture with camera to create a new thumbnail."""
|
||||
return api.request_new_image(self.sync.blink, self.network_id, self.camera_id)
|
||||
|
||||
def set_motion_detect(self, enable):
|
||||
"""Set motion detection."""
|
||||
_LOGGER.warning(
|
||||
"Method is deprecated as of v0.16.0 and will be removed in a future version. Please use the BlinkCamera.arm property instead."
|
||||
)
|
||||
if enable:
|
||||
return api.request_motion_detection_enable(
|
||||
self.sync.blink, self.network_id, self.camera_id
|
||||
@@ -127,7 +146,7 @@ class BlinkCamera:
|
||||
)
|
||||
|
||||
if thumb_addr is not None:
|
||||
new_thumbnail = "{}{}.jpg".format(self.sync.urls.base_url, thumb_addr)
|
||||
new_thumbnail = f"{self.sync.urls.base_url}{thumb_addr}.jpg"
|
||||
|
||||
try:
|
||||
self.motion_detected = self.sync.motion[self.name]
|
||||
@@ -138,7 +157,7 @@ class BlinkCamera:
|
||||
if self.name in self.sync.last_record:
|
||||
clip_addr = self.sync.last_record[self.name]["clip"]
|
||||
self.last_record = self.sync.last_record[self.name]["time"]
|
||||
self.clip = "{}{}".format(self.sync.urls.base_url, clip_addr)
|
||||
self.clip = f"{self.sync.urls.base_url}{clip_addr}"
|
||||
|
||||
# If the thumbnail or clip have changed, update the cache
|
||||
update_cached_image = False
|
||||
|
||||
@@ -4,9 +4,9 @@ import os
|
||||
|
||||
MAJOR_VERSION = 0
|
||||
MINOR_VERSION = 16
|
||||
PATCH_VERSION = "0-rc0"
|
||||
PATCH_VERSION = "0-rc2"
|
||||
|
||||
__version__ = "{}.{}.{}".format(MAJOR_VERSION, MINOR_VERSION, PATCH_VERSION)
|
||||
__version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}.{PATCH_VERSION}"
|
||||
|
||||
REQUIRED_PYTHON_VER = (3, 6, 0)
|
||||
|
||||
@@ -14,7 +14,7 @@ PROJECT_NAME = "blinkpy"
|
||||
PROJECT_PACKAGE_NAME = "blinkpy"
|
||||
PROJECT_LICENSE = "MIT"
|
||||
PROJECT_AUTHOR = "Kevin Fronczak"
|
||||
PROJECT_COPYRIGHT = " 2017, {}".format(PROJECT_AUTHOR)
|
||||
PROJECT_COPYRIGHT = f" 2017, {PROJECT_AUTHOR}"
|
||||
PROJECT_URL = "https://github.com/fronzbot/blinkpy"
|
||||
PROJECT_EMAIL = "kfronczak@gmail.com"
|
||||
PROJECT_DESCRIPTION = "A Blink camera Python library " "running on Python 3."
|
||||
@@ -40,19 +40,15 @@ PROJECT_CLASSIFIERS = [
|
||||
PROJECT_GITHUB_USERNAME = "fronzbot"
|
||||
PROJECT_GITHUB_REPOSITORY = "blinkpy"
|
||||
|
||||
PYPI_URL = "https://pypi.python.org/pypi/{}".format(PROJECT_PACKAGE_NAME)
|
||||
PYPI_URL = f"https://pypi.python.org/pypi/{PROJECT_PACKAGE_NAME}"
|
||||
|
||||
"""
|
||||
URLS
|
||||
"""
|
||||
BLINK_URL = "immedia-semi.com"
|
||||
DEFAULT_URL = "{}.{}".format("rest-prod", BLINK_URL)
|
||||
BASE_URL = "https://{}".format(DEFAULT_URL)
|
||||
LOGIN_ENDPOINT = "{}/api/v4/account/login".format(BASE_URL)
|
||||
LOGIN_URLS = [
|
||||
"{}/api/v4/account/login".format(BASE_URL),
|
||||
"{}/api/v3/login".format(BASE_URL),
|
||||
]
|
||||
DEFAULT_URL = f"rest-prod.{BLINK_URL}"
|
||||
BASE_URL = f"https://{DEFAULT_URL}"
|
||||
LOGIN_ENDPOINT = f"{BASE_URL}/api/v4/account/login"
|
||||
|
||||
"""
|
||||
Dictionaries
|
||||
|
||||
@@ -12,4 +12,4 @@ AUTH_TOKEN = (
|
||||
)
|
||||
REQUEST = (4, "Cannot perform request (get/post type incorrect)")
|
||||
|
||||
BLINK_ERRORS = [101, 400, 404]
|
||||
BLINK_ERRORS = [400, 404]
|
||||
|
||||
@@ -149,14 +149,11 @@ class BlinkSyncModule:
|
||||
|
||||
def get_network_info(self):
|
||||
"""Retrieve network status."""
|
||||
is_errored = False
|
||||
self.network_info = api.request_network_status(self.blink, self.network_id)
|
||||
try:
|
||||
is_errored = self.network_info["network"]["sync_module_error"]
|
||||
except KeyError:
|
||||
is_errored = True
|
||||
|
||||
if is_errored:
|
||||
if self.network_info["network"]["sync_module_error"]:
|
||||
raise KeyError
|
||||
except (TypeError, KeyError):
|
||||
self.available = False
|
||||
return False
|
||||
return True
|
||||
|
||||
+2
-4
@@ -4,10 +4,8 @@ coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
threshold: 2%
|
||||
target: 80
|
||||
threshold: 0.02
|
||||
|
||||
comment: true
|
||||
require_ci_to_pass: yes
|
||||
range: 65..90
|
||||
round: down
|
||||
precision: 1
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
black==19.10b0
|
||||
coverage==5.1
|
||||
flake8==3.8.1
|
||||
flake8==3.8.2
|
||||
flake8-docstrings==1.5.0
|
||||
pre-commit==2.4.0
|
||||
pylint==2.5.2
|
||||
pydocstyle==5.0.2
|
||||
pytest==5.4.2
|
||||
pytest-cov==2.8.1
|
||||
pytest-cov==2.9.0
|
||||
pytest-sugar==0.9.3
|
||||
pytest-timeout==1.3.4
|
||||
restructuredtext-lint==1.3.0
|
||||
|
||||
@@ -9,6 +9,7 @@ class MockResponse:
|
||||
self.json_data = json_data
|
||||
self.status_code = status_code
|
||||
self.raw_data = raw_data
|
||||
self.reason = "foobar"
|
||||
|
||||
def json(self):
|
||||
"""Return json data from get_request."""
|
||||
|
||||
+45
-2
@@ -3,7 +3,13 @@
|
||||
import unittest
|
||||
from unittest import mock
|
||||
from requests import exceptions
|
||||
from blinkpy.auth import Auth, LoginError, TokenRefreshFailed
|
||||
from blinkpy.auth import (
|
||||
Auth,
|
||||
LoginError,
|
||||
TokenRefreshFailed,
|
||||
BlinkBadResponse,
|
||||
UnauthorizedError,
|
||||
)
|
||||
import blinkpy.helpers.constants as const
|
||||
import tests.mock_responses as mresp
|
||||
|
||||
@@ -86,19 +92,38 @@ class TestAuth(unittest.TestCase):
|
||||
|
||||
def test_bad_response_code(self):
|
||||
"""Check bad response code from server."""
|
||||
self.auth.is_errored = False
|
||||
fake_resp = mresp.MockResponse({"code": 404}, 404)
|
||||
with self.assertRaises(exceptions.ConnectionError):
|
||||
self.auth.validate_response(fake_resp, True)
|
||||
self.assertTrue(self.auth.is_errored)
|
||||
|
||||
self.auth.is_errored = False
|
||||
fake_resp = mresp.MockResponse({"code": 101}, 401)
|
||||
with self.assertRaises(UnauthorizedError):
|
||||
self.auth.validate_response(fake_resp, True)
|
||||
self.assertTrue(self.auth.is_errored)
|
||||
|
||||
def test_good_response_code(self):
|
||||
"""Check good response code from server."""
|
||||
fake_resp = mresp.MockResponse({"foo": "bar"}, 200)
|
||||
self.auth.is_errored = True
|
||||
self.assertEqual(self.auth.validate_response(fake_resp, True), {"foo": "bar"})
|
||||
self.assertFalse(self.auth.is_errored)
|
||||
|
||||
def test_response_not_json(self):
|
||||
"""Check response when not json."""
|
||||
fake_resp = "foobar"
|
||||
self.auth.is_errored = True
|
||||
self.assertEqual(self.auth.validate_response(fake_resp, False), "foobar")
|
||||
self.assertFalse(self.auth.is_errored)
|
||||
|
||||
def test_response_bad_json(self):
|
||||
"""Check response when not json but expecting json."""
|
||||
self.auth.is_errored = False
|
||||
with self.assertRaises(BlinkBadResponse):
|
||||
self.auth.validate_response(None, True)
|
||||
self.assertTrue(self.auth.is_errored)
|
||||
|
||||
def test_header(self):
|
||||
"""Test header data."""
|
||||
@@ -126,8 +151,12 @@ class TestAuth(unittest.TestCase):
|
||||
"""Test login handling when bad response."""
|
||||
fake_resp = mresp.MockResponse({"foo": "bar"}, 404)
|
||||
mock_req.return_value = fake_resp
|
||||
self.auth.is_errored = False
|
||||
with self.assertRaises(LoginError):
|
||||
self.auth.login()
|
||||
with self.assertRaises(TokenRefreshFailed):
|
||||
self.auth.refresh_token()
|
||||
self.assertTrue(self.auth.is_errored)
|
||||
|
||||
@mock.patch("blinkpy.auth.Auth.login")
|
||||
def test_refresh_token(self, mock_login):
|
||||
@@ -148,8 +177,10 @@ class TestAuth(unittest.TestCase):
|
||||
def test_refresh_token_failed(self, mock_login):
|
||||
"""Test refresh token failed."""
|
||||
mock_login.return_value = {}
|
||||
self.auth.is_errored = False
|
||||
with self.assertRaises(TokenRefreshFailed):
|
||||
self.auth.refresh_token()
|
||||
self.assertTrue(self.auth.is_errored)
|
||||
|
||||
def test_check_key_required(self):
|
||||
"""Check key required method."""
|
||||
@@ -192,10 +223,22 @@ class TestAuth(unittest.TestCase):
|
||||
def test_query_retry(self, mock_refresh, mock_validate):
|
||||
"""Check handling of request retry."""
|
||||
self.auth.session = MockSession()
|
||||
mock_validate.side_effect = [TokenRefreshFailed, "foobar"]
|
||||
mock_validate.side_effect = [UnauthorizedError, "foobar"]
|
||||
mock_refresh.return_value = True
|
||||
self.assertEqual(self.auth.query(url="http://example.com"), "foobar")
|
||||
|
||||
@mock.patch("blinkpy.auth.Auth.validate_response")
|
||||
@mock.patch("blinkpy.auth.Auth.refresh_token")
|
||||
def test_query_retry_failed(self, mock_refresh, mock_validate):
|
||||
"""Check handling of failed retry request."""
|
||||
self.auth.seession = MockSession()
|
||||
mock_validate.side_effect = [UnauthorizedError, BlinkBadResponse]
|
||||
mock_refresh.return_value = True
|
||||
self.assertEqual(self.auth.query(url="http://example.com"), None)
|
||||
|
||||
mock_validate.side_effect = [UnauthorizedError, TokenRefreshFailed]
|
||||
self.assertEqual(self.auth.query(url="http://example.com"), None)
|
||||
|
||||
|
||||
class MockSession:
|
||||
"""Object to mock a session."""
|
||||
|
||||
@@ -106,6 +106,9 @@ class TestBlinkSetup(unittest.TestCase):
|
||||
mock_home.return_value = {}
|
||||
with self.assertRaises(BlinkSetupError):
|
||||
self.blink.setup_camera_list()
|
||||
mock_home.return_value = None
|
||||
with self.assertRaises(BlinkSetupError):
|
||||
self.blink.setup_camera_list()
|
||||
|
||||
def test_setup_urls(self):
|
||||
"""Check setup of URLS."""
|
||||
@@ -132,6 +135,9 @@ class TestBlinkSetup(unittest.TestCase):
|
||||
mock_networks.return_value = {}
|
||||
with self.assertRaises(BlinkSetupError):
|
||||
self.blink.setup_networks()
|
||||
mock_networks.return_value = None
|
||||
with self.assertRaises(BlinkSetupError):
|
||||
self.blink.setup_networks()
|
||||
|
||||
@mock.patch("blinkpy.blinkpy.Auth.send_auth_key")
|
||||
def test_setup_prompt_2fa(self, mock_key):
|
||||
|
||||
@@ -144,6 +144,15 @@ class TestBlinkCameraSetup(unittest.TestCase):
|
||||
self.assertEqual(self.camera.clip, None)
|
||||
self.assertEqual(self.camera.video_from_cache, None)
|
||||
|
||||
def test_camera_arm_status(self, mock_resp):
|
||||
"""Test arming and disarming camera."""
|
||||
self.camera.motion_enabled = None
|
||||
self.assertFalse(self.camera.arm)
|
||||
self.camera.motion_enabled = False
|
||||
self.assertFalse(self.camera.arm)
|
||||
self.camera.motion_enabled = True
|
||||
self.assertTrue(self.camera.arm)
|
||||
|
||||
@mock.patch("blinkpy.camera.api.request_motion_detection_enable")
|
||||
@mock.patch("blinkpy.camera.api.request_motion_detection_disable")
|
||||
def test_motion_detection_enable_disable(self, mock_dis, mock_en, mock_rep):
|
||||
|
||||
@@ -78,7 +78,7 @@ class TestBlinkSyncModule(unittest.TestCase):
|
||||
self.assertEqual(self.blink.sync["test"].get_camera_info("1234"), "foobar")
|
||||
|
||||
def test_get_camera_info_fail(self, mock_resp):
|
||||
"""Test hadnling of failed get camera info function."""
|
||||
"""Test handling of failed get camera info function."""
|
||||
mock_resp.return_value = None
|
||||
self.assertEqual(self.blink.sync["test"].get_camera_info("1"), [])
|
||||
mock_resp.return_value = {}
|
||||
@@ -86,6 +86,24 @@ class TestBlinkSyncModule(unittest.TestCase):
|
||||
mock_resp.return_value = {"camera": None}
|
||||
self.assertEqual(self.blink.sync["test"].get_camera_info("1"), [])
|
||||
|
||||
def test_get_network_info(self, mock_resp):
|
||||
"""Test network retrieval."""
|
||||
mock_resp.return_value = {"network": {"sync_module_error": False}}
|
||||
self.assertTrue(self.blink.sync["test"].get_network_info())
|
||||
mock_resp.return_value = {"network": {"sync_module_error": True}}
|
||||
self.assertFalse(self.blink.sync["test"].get_network_info())
|
||||
|
||||
def test_get_network_info_failure(self, mock_resp):
|
||||
"""Test failed network retrieval."""
|
||||
mock_resp.return_value = {}
|
||||
self.blink.sync["test"].available = True
|
||||
self.assertFalse(self.blink.sync["test"].get_network_info())
|
||||
self.assertFalse(self.blink.sync["test"].available)
|
||||
self.blink.sync["test"].available = True
|
||||
mock_resp.return_value = None
|
||||
self.assertFalse(self.blink.sync["test"].get_network_info())
|
||||
self.assertFalse(self.blink.sync["test"].available)
|
||||
|
||||
def test_check_new_videos_startup(self, mock_resp):
|
||||
"""Test that check_new_videos does not block startup."""
|
||||
sync_module = self.blink.sync["test"]
|
||||
|
||||
@@ -67,6 +67,25 @@ class TestUtil(unittest.TestCase):
|
||||
self.assertEqual(throttled(), True)
|
||||
self.assertEqual(throttled(), None)
|
||||
|
||||
def test_throttle_multiple_objects(self):
|
||||
"""Test that function is throttled even if called by multiple objects."""
|
||||
|
||||
@Throttle(seconds=5)
|
||||
def test_throttle_method():
|
||||
return True
|
||||
|
||||
class Tester:
|
||||
"""A tester class for throttling."""
|
||||
|
||||
def test(self):
|
||||
"""Test function for throttle."""
|
||||
return test_throttle_method()
|
||||
|
||||
tester1 = Tester()
|
||||
tester2 = Tester()
|
||||
self.assertEqual(tester1.test(), True)
|
||||
self.assertEqual(tester2.test(), None)
|
||||
|
||||
def test_throttle_on_two_methods(self):
|
||||
"""Test that throttle works for multiple methods."""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user