Compare commits

..
2 Commits
27 changed files with 137 additions and 339 deletions
-9
View File
@@ -1,9 +0,0 @@
version: 2
updates:
- package-ecosystem: pip
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10
reviewers:
- fronzbot
+2 -3
View File
@@ -1,5 +1,5 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 30
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
@@ -8,8 +8,7 @@ daysUntilClose: 7
exemptLabels:
- Priority
- bug
- help wanted
- feature request
- Help Wanted
# Set to true to ignore issues in a project (defaults to false)
exemptProjects: true
+3 -2
View File
@@ -33,9 +33,10 @@ jobs:
run: |
tox -r -e cov
- name: Codecov
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v1.0.6
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: unittests
file: ./coverage.xml
name: blinkpy
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.9]
python-version: [3.8]
steps:
- uses: actions/checkout@v2
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: '3.8'
python-version: '3.7'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
+2 -2
View File
@@ -10,11 +10,11 @@ jobs:
build:
runs-on: ${{ matrix.platform }}
strategy:
max-parallel: 4
max-parallel: 3
matrix:
platform:
- ubuntu-latest
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: [3.6, 3.7, 3.8]
steps:
- uses: actions/checkout@v2
-1
View File
@@ -14,4 +14,3 @@ build/*
docs/_build
*.log
venv
.session*
+25
View File
@@ -0,0 +1,25 @@
image: python
stages:
- test
before_script:
- curl -O https://bootstrap.pypa.io/get-pip.py
- python get-pip.py
- pip install tox
python35:
image: python:3.5
stage: test
script: tox -e py35
python36:
image: python:3.6
stage: test
script: tox -e py36
lint:
image: python:3.6
stage: test
script: tox -e lint
+2 -2
View File
@@ -8,12 +8,12 @@ repos:
- --quiet
files: ^((blinkpy|tests)/.+)?[^/]+\.py$
- repo: https://gitlab.com/pycqa/flake8
rev: 3.8.3
rev: 3.8.2
hooks:
- id: flake8
additional_dependencies:
- flake8-docstrings==1.5.0
- pydocstyle==5.1.1
- pydocstyle==5.0.2
files: ^(blinkpy|tests)/.+\.py$
- repo: https://github.com/Lucas-C/pre-commit-hooks-markup
rev: v1.0.0
-55
View File
@@ -4,61 +4,6 @@ Changelog
A list of changes between each release
0.17.1 (2021-02-18)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Add delay parameter to Blink.download_videos method in order to throttle API during video retrieval (`#437 <https://github.com/fronzbot/blinkpy/pull/437>`__)
- Bump pylint to 2.6.2
0.17.0 (2021-02-15)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Bugfixes:**
- Fix video downloading bug (`#424 <https://github.com/fronzbot/blinkpy/pull/424>`__)
- Fix repeated authorization email bug (`#432 <https://github.com/fronzbot/blinkpy/pull/432>`__ and `#428 <https://github.com/fronzbot/blinkpy/pull/428>`__)
**New Features:**
- Add logout method (`#429 <https://github.com/fronzbot/blinkpy/pull/429>`__)
- Add camera record method (`#430 <https://github.com/fronzbot/blinkpy/pull/430>`__)
**Other:**
- Add debug script to main repo to help with general debug
- Upgrade login endpoint from v4 to v5
- Add python 3.9 support
- Bump coverage to 5.4
- Bump pytest to 6.2.2
- Bump pytest-cov to 2.11.1
- Bump pygments to 2.8.0
- Bump pre-commit to 2.10.1
- Bump restructuredtext-lint to 1.3.2
0.16.4 (2020-11-22)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Bugfixes:**
- Updated liveview endpoint (`#389 <https://github.com/fronzbot/blinkpy/pull/389>`__)
- Fixed mini thumbnail not updating (`#388 <https://github.com/fronzbot/blinkpy/pull/388>`__)
- Add exception catch to prevent NoneType error on refresh, added test to check behavior as well (`#401 <https://github.com/fronzbot/blinkpy/pull/401>`__)
- Unrelated: had to add two force methods to refresh for testing purposes. Should not change normal usage.
- Fix malformed stream url (`#395 <https://github.com/fronzbot/blinkpy/pull/395>`__)
**All:**
- Moved testtools to requirements_test.txt (`#387 <https://github.com/fronzbot/blinkpy/pull/387>`__)
- Bumped pytest to 6.1.1
- Bumped flake8 to 3.8.4
- Fixed README spelling ((`#381 <https://github.com/fronzbot/blinkpy/pull/381>`__) via @rohitsud)
- Bumped pygments to 2.7.1
- Bumped coverage to 5.3
- Bumped pydocstyle to 5.1.1
- Bumped pre-commit to 2.7.1
- Bumped pylint to 2.6.0
- Bumped pytest-cov to 2.10.1
0.16.3 (2020-08-02)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+3 -3
View File
@@ -174,13 +174,13 @@ Similar methods exist for individual cameras:
Download videos
----------------
You can also use this library to download all videos from the server. In order to do this, you must specify a ``path``. You may also specifiy a how far back in time to go to retrieve videos via the ``since=`` variable (a simple string such as ``"2017/09/21"`` is sufficient), as well as how many pages to traverse via the ``stop=`` variable. Note that by default, the library will search the first ten pages which is sufficient in most use cases. Additionally, you can specify one or more cameras via the ``camera=`` property. This can be a single string indicating the name of the camera, or a list of camera names. By default, it is set to the string ``'all'`` to grab videos from all cameras. If you are downloading many items, setting the ``delay`` parameter is advised in order to throttle sequential calls to the API. By default this is set to ``1`` but can be any integer representing the number of seconds to delay between calls.
You can also use this library to download all videos from the server. In order to do this, you must specify a ``path``. You may also specifiy a how far back in time to go to retrieve videos via the ``since=`` variable (a simple string such as ``"2017/09/21"`` is sufficient), as well as how many pages to traverse via the ``stop=`` variable. Note that by default, the library will search the first ten pages which is sufficient in most use cases. Additionally, you can specify one or more cameras via the ``camera=`` property. This can be a single string indicating the name of the camera, or a list of camera names. By default, it is set to the string ``'all'`` to grab videos from all cameras.
Example usage, which downloads all videos recorded since July 4th, 2018 at 9:34am to the ``/home/blink`` directory with a 2s delay between calls:
Example usage, which downloads all videos recorded since July 4th, 2018 at 9:34am to the ``/home/blink`` directory:
.. code:: python
blink.download_videos('/home/blink', since='2018/07/04 09:34', delay=2)
blink.download_videos('/home/blink', since='2018/07/04 09:34')
.. |Build Status| image:: https://github.com/fronzbot/blinkpy/workflows/build/badge.svg
+6 -8
View File
@@ -23,16 +23,18 @@ def request_login(
headers = {
"Host": DEFAULT_URL,
"Content-Type": "application/json",
"Accept": "/",
"user-agent": DEFAULT_USER_AGENT,
}
data = dumps(
{
"email": login_data["username"],
"password": login_data["password"],
"notification_key": login_data["notification_key"],
"unique_id": login_data["uid"],
"device_identifier": login_data["device_id"],
"client_name": "Computer",
"reauth": True,
"reauth": "false",
}
)
@@ -55,12 +57,6 @@ def request_verify(auth, blink, verify_key):
)
def request_logout(blink):
"""Logout of blink servers."""
url = f"{blink.urls.base_url}/api/v4/account/{blink.account_id}/client/{blink.client_id}/logout"
return http_post(blink, url=url)
def request_networks(blink):
"""Request all networks information."""
url = f"{blink.urls.base_url}/networks"
@@ -248,7 +244,9 @@ def request_camera_liveview(blink, network, camera_id):
:param network: Sync module network id.
:param camera_id: Camera ID of camera to request liveview from.
"""
url = f"{blink.urls.base_url}/api/v5/accounts/{blink.account_id}/networks/{network}/cameras/{camera_id}/liveview"
url = (
f"{blink.urls.base_url}/api/v3/networks/{network}/cameras/{camera_id}/liveview"
)
return http_post(blink, url)
+14 -25
View File
@@ -58,11 +58,7 @@ class Auth:
"""Return authorization header."""
if self.token is None:
return None
return {
"TOKEN_AUTH": self.token,
"user-agent": DEFAULT_USER_AGENT,
"content-type": "application/json",
}
return {"TOKEN_AUTH": self.token, "user-agent": DEFAULT_USER_AGENT}
def create_session(self, opts=None):
"""Create a session for blink communication."""
@@ -108,12 +104,8 @@ class Auth:
if response.status_code == 200:
return response.json()
raise LoginError
except AttributeError as error:
raise LoginError from error
def logout(self, blink):
"""Log out."""
return api.request_logout(blink)
except AttributeError:
raise LoginError
def refresh_token(self):
"""Refresh auth token."""
@@ -123,21 +115,21 @@ class Auth:
self.login_response = self.login()
self.extract_login_info()
self.is_errored = False
except LoginError as error:
except LoginError:
_LOGGER.error("Login endpoint failed. Try again later.")
raise TokenRefreshFailed from error
except (TypeError, KeyError) as error:
raise TokenRefreshFailed
except (TypeError, KeyError):
_LOGGER.error("Malformed login response: %s", self.login_response)
raise TokenRefreshFailed from error
raise TokenRefreshFailed
return True
def extract_login_info(self):
"""Extract login info from login response."""
self.region_id = self.login_response["account"]["tier"]
self.region_id = self.login_response["region"]["tier"]
self.host = f"{self.region_id}.{BLINK_URL}"
self.token = self.login_response["auth"]["token"]
self.client_id = self.login_response["account"]["client_id"]
self.account_id = self.login_response["account"]["account_id"]
self.token = self.login_response["authtoken"]["authtoken"]
self.client_id = self.login_response["client"]["id"]
self.account_id = self.login_response["account"]["id"]
def startup(self):
"""Initialize tokens for communication."""
@@ -159,8 +151,8 @@ class Auth:
json_data = response.json()
except KeyError:
pass
except (AttributeError, ValueError) as error:
raise BlinkBadResponse from error
except (AttributeError, ValueError):
raise BlinkBadResponse
self.is_errored = False
return json_data
@@ -235,9 +227,6 @@ class Auth:
try:
json_resp = response.json()
blink.available = json_resp["valid"]
if not json_resp["valid"]:
_LOGGER.error("%s", json_resp["message"])
return False
except (KeyError, TypeError):
_LOGGER.error("Did not receive valid response from server.")
return False
@@ -246,7 +235,7 @@ class Auth:
def check_key_required(self):
"""Check if 2FA key is required."""
try:
if self.login_response["account"]["client_verification_required"]:
if self.login_response["client"]["verification_required"]:
return True
except (KeyError, TypeError):
pass
+8 -15
View File
@@ -76,22 +76,20 @@ class Blink:
self.no_owls = no_owls
@util.Throttle(seconds=MIN_THROTTLE_TIME)
def refresh(self, force=False, force_cache=False):
def refresh(self, force=False):
"""
Perform a system refresh.
:param force: Used to override throttle, resets refresh
:param force_cache: Used to force update without overriding throttle
:param force: Force an update of the camera data
"""
if self.check_if_ok_to_update() or force or force_cache:
if self.check_if_ok_to_update() or force:
if not self.available:
self.setup_post_verify()
self.get_homescreen()
for sync_name, sync_module in self.sync.items():
_LOGGER.debug("Attempting refresh of sync %s", sync_name)
sync_module.refresh(force_cache=(force or force_cache))
if not force_cache:
sync_module.refresh(force_cache=force)
if not force:
# Prevents rapid clearing of motion detect property
self.last_refresh = int(time.time())
return True
@@ -264,9 +262,7 @@ class Blink:
"""Save login data to file."""
util.json_save(self.auth.login_attributes, file_name)
def download_videos(
self, path, since=None, camera="all", stop=10, delay=1, debug=False
):
def download_videos(self, path, since=None, camera="all", stop=10, debug=False):
"""
Download all videos from server since specified time.
@@ -277,7 +273,6 @@ 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 delay: Number of seconds to wait in between subsequent video downloads.
:param debug: Set to TRUE to prevent downloading of items.
Instead of downloading, entries will be printed to log.
"""
@@ -304,9 +299,9 @@ class Blink:
_LOGGER.info("No videos found on page %s. Exiting.", page)
break
self._parse_downloaded_items(result, camera, path, delay, debug)
self._parse_downloaded_items(result, camera, path, debug)
def _parse_downloaded_items(self, result, camera, path, delay, debug):
def _parse_downloaded_items(self, result, camera, path, debug):
"""Parse downloaded videos."""
for item in result:
try:
@@ -354,8 +349,6 @@ class Blink:
"Address: {address}, Filename: {filename}"
)
)
if delay > 0:
time.sleep(delay)
class BlinkSetupError(Exception):
+17 -19
View File
@@ -98,19 +98,6 @@ class BlinkCamera:
self.sync.blink, self.network_id, self.camera_id
)
def record(self):
"""Initiate clip recording."""
return api.request_new_video(self.sync.blink, self.network_id, self.camera_id)
def get_media(self, media_type="image"):
"""Download media (image or video)."""
url = self.thumbnail
if media_type.lower() == "video":
url = self.clip
return api.http_get(
self.sync.blink, url=url, stream=True, json=False, timeout=TIMEOUT_MEDIA,
)
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)
@@ -193,10 +180,21 @@ class BlinkCamera:
update_cached_video = True
if new_thumbnail is not None and (update_cached_image or force_cache):
self._cached_image = self.get_media()
self._cached_image = api.http_get(
self.sync.blink,
url=self.thumbnail,
stream=True,
json=False,
timeout=TIMEOUT_MEDIA,
)
if clip_addr is not None and (update_cached_video or force_cache):
self._cached_video = self.get_media(media_type="video")
self._cached_video = api.http_get(
self.sync.blink,
url=self.clip,
stream=True,
json=False,
timeout=TIMEOUT_MEDIA,
)
def get_liveview(self):
"""Get livewview rtsps link."""
@@ -212,7 +210,7 @@ class BlinkCamera:
:param path: Path to write file
"""
_LOGGER.debug("Writing image from %s to %s", self.name, path)
response = self.get_media()
response = self._cached_image
if response.status_code == 200:
with open(path, "wb") as imgfile:
copyfileobj(response.raw, imgfile)
@@ -228,7 +226,7 @@ class BlinkCamera:
:param path: Path to write file
"""
_LOGGER.debug("Writing video from %s to %s", self.name, path)
response = self.get_media(media_type="video")
response = self._cached_video
if response is None:
_LOGGER.error("No saved video exist for %s.", self.name)
return
@@ -270,6 +268,6 @@ class BlinkCameraMini(BlinkCamera):
response = api.http_post(self.sync.blink, url)
server = response["server"]
server_split = server.split(":")
server_split[0] = "rtsps:"
server_split[0] = "rtsps"
link = "".join(server_split)
return link
+2 -3
View File
@@ -3,7 +3,7 @@
import os
MAJOR_VERSION = 0
MINOR_VERSION = 18
MINOR_VERSION = 17
PATCH_VERSION = "0.dev0"
__version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}.{PATCH_VERSION}"
@@ -34,7 +34,6 @@ PROJECT_CLASSIFIERS = [
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Topic :: Home Automation",
]
@@ -49,7 +48,7 @@ URLS
BLINK_URL = "immedia-semi.com"
DEFAULT_URL = f"rest-prod.{BLINK_URL}"
BASE_URL = f"https://{DEFAULT_URL}"
LOGIN_ENDPOINT = f"{BASE_URL}/api/v5/account/login"
LOGIN_ENDPOINT = f"{BASE_URL}/api/v4/account/login"
"""
Dictionaries
+7 -7
View File
@@ -33,13 +33,10 @@ def json_save(data, file_name):
json.dump(data, json_file, indent=4)
def gen_uid(size, uid_format=False):
def gen_uid(size):
"""Create a random sring."""
if uid_format:
token = f"BlinkCamera_{secrets.token_hex(4)}-{secrets.token_hex(2)}-{secrets.token_hex(2)}-{secrets.token_hex(2)}-{secrets.token_hex(6)}"
else:
token = secrets.token_hex(size)
return token
full_token = secrets.token_hex(size)
return full_token[0:size]
def time_to_seconds(timestamp):
@@ -82,7 +79,10 @@ def prompt_login_data(data):
def validate_login_data(data):
"""Check for missing keys."""
data["uid"] = data.get("uid", gen_uid(const.SIZE_UID, uid_format=True))
data["uid"] = data.get("uid", gen_uid(const.SIZE_UID))
data["notification_key"] = data.get(
"notification_key", gen_uid(const.SIZE_NOTIFICATION_KEY)
)
data["device_id"] = data.get("device_id", const.DEVICE_ID)
return data
+2 -2
View File
@@ -145,7 +145,7 @@ class BlinkSyncModule:
for owl in self.blink.homescreen["owls"]:
if owl["name"] == name:
return owl
except (TypeError, KeyError):
except KeyError:
pass
return None
@@ -270,7 +270,7 @@ class BlinkOwl(BlinkSyncModule):
if owl["name"] == self.name:
self.status = owl["enabled"]
return owl
except (TypeError, KeyError):
except KeyError:
pass
return None
-77
View File
@@ -1,77 +0,0 @@
"""Login testing script."""
import sys
import os
from blinkpy.blinkpy import Blink
from blinkpy.auth import Auth
from blinkpy.helpers.util import json_load, json_save
save_session = False
print("")
print("Blink Login Debug Script ...")
print(" ... Loading previous session information.")
cwd = os.getcwd()
print(f" ... Looking in {cwd}.")
session_path = os.path.join(cwd, ".session_debug")
session = json_load(session_path)
try:
auth_file = session["file"]
except (TypeError, KeyError):
print(" ... Please input location of auth file")
auth_file = input(" Must contain username and password: ")
save_session = True
data = json_load(auth_file)
if data is None:
print(f" ... Please fix file contents of {auth_file}.")
print(" ... Exiting.")
sys.exit(1)
try:
username = data["username"]
password = data["password"]
except KeyError:
print(f" ... File contents of {auth_file} incorrect.")
print(" ... Require username and password at minimum.")
print(" ... Exiting.")
sys.exit(1)
if save_session:
print(f" ... Saving session file to {session_path}.")
json_save({"file": auth_file}, session_path)
blink = Blink()
auth = Auth(data)
blink.auth = auth
print(" ... Starting Blink.")
print("")
blink.start()
print("")
print(" ... Printing login response.")
print("")
print(blink.auth.login_response)
print("")
print(" ... Printing login attributes.")
print("")
print(blink.auth.login_attributes)
print("")
input(" ... Press any key to continue: ")
print(" ... Deactivating auth token.")
blink.auth.token = "foobar"
print(f"\t - blink.auth.token = {blink.auth.token}")
print(" ... Attempting login.")
print("")
blink.start()
print("")
print(" ... Printing login response.")
print("")
print(blink.auth.login_response)
print("")
print(" ... Printing login attributes.")
print("")
print(blink.auth.login_attributes)
print("")
rint(" ... Done.")
print("")
+1
View File
@@ -1,3 +1,4 @@
python-dateutil>=2.8.1
requests>=2.24.0
python-slugify>=4.0.1
testtools>=2.4.0
+10 -11
View File
@@ -1,14 +1,13 @@
black==19.10b0
coverage==5.5
flake8==3.9.1
flake8-docstrings==1.6.0
pre-commit==2.12.1
pylint==2.8.2
pydocstyle==6.0.0
pytest==6.2.4
pytest-cov==2.11.1
coverage==5.3
flake8==3.8.3
flake8-docstrings==1.5.0
pre-commit==2.6.0
pylint==2.5.3
pydocstyle==5.0.2
pytest==6.1.1
pytest-cov==2.10.1
pytest-sugar==0.9.4
pytest-timeout==1.4.2
restructuredtext-lint==1.3.2
pygments==2.9.0
testtools>=2.4.0
restructuredtext-lint==1.3.1
pygments==2.6.1
+9 -16
View File
@@ -42,6 +42,7 @@ class TestAuth(unittest.TestCase):
"username": "foo",
"password": "bar",
"uid": 1234,
"notification_key": 1234,
"device_id": const.DEVICE_ID,
}
self.assertDictEqual(auth.data, expected_data)
@@ -61,6 +62,7 @@ class TestAuth(unittest.TestCase):
"username": "foo",
"password": "bar",
"uid": 1234,
"notification_key": 1234,
"device_id": const.DEVICE_ID,
}
self.assertDictEqual(auth.data, expected_data)
@@ -126,11 +128,7 @@ class TestAuth(unittest.TestCase):
def test_header(self):
"""Test header data."""
self.auth.token = "bar"
expected_header = {
"TOKEN_AUTH": "bar",
"user-agent": const.DEFAULT_USER_AGENT,
"content-type": "application/json",
}
expected_header = {"TOKEN_AUTH": "bar", "user-agent": const.DEFAULT_USER_AGENT}
self.assertDictEqual(self.auth.header, expected_header)
def test_header_no_token(self):
@@ -163,8 +161,10 @@ class TestAuth(unittest.TestCase):
def test_refresh_token(self, mock_login):
"""Test refresh token method."""
mock_login.return_value = {
"account": {"account_id": 5678, "client_id": 1234, "tier": "test"},
"auth": {"token": "foobar"},
"region": {"tier": "test"},
"authtoken": {"authtoken": "foobar"},
"client": {"id": 1234},
"account": {"id": 5678},
}
self.assertTrue(self.auth.refresh_token())
self.assertEqual(self.auth.region_id, "test")
@@ -186,19 +186,12 @@ class TestAuth(unittest.TestCase):
self.auth.login_response = {}
self.assertFalse(self.auth.check_key_required())
self.auth.login_response = {"account": {"client_verification_required": False}}
self.auth.login_response = {"client": {"verification_required": False}}
self.assertFalse(self.auth.check_key_required())
self.auth.login_response = {"account": {"client_verification_required": True}}
self.auth.login_response = {"client": {"verification_required": True}}
self.assertTrue(self.auth.check_key_required())
@mock.patch("blinkpy.auth.api.request_logout")
def test_logout(self, mock_req):
"""Test logout method."""
mock_blink = MockBlink(None)
mock_req.return_value = True
self.assertTrue(self.auth.logout(mock_blink))
@mock.patch("blinkpy.auth.api.request_verify")
def test_send_auth_key(self, mock_req):
"""Check sending of auth key."""
+18 -50
View File
@@ -2,27 +2,32 @@
import unittest
from unittest import mock
import logging
import time
from blinkpy import blinkpy
from blinkpy.sync_module import BlinkSyncModule
from blinkpy.camera import BlinkCamera
from blinkpy.helpers.util import get_time, BlinkURLHandler
class MockSyncModule(BlinkSyncModule):
"""Mock blink sync module object."""
"""Mock http requests from sync module."""
def get_network_info(self):
"""Mock network info method."""
return True
def __init__(self, blink, header):
"""Create mock sync module instance."""
super().__init__(blink, header, network_id=None, camera_list=None)
self.blink = blink
self.header = header
self.return_value = None
self.return_value2 = None
def http_get(self, url, stream=False, json=True):
"""Mock get request."""
if stream and self.return_value2 is not None:
return self.return_value2
return self.return_value
class MockCamera(BlinkCamera):
"""Mock blink camera object."""
def update(self, config, force_cache=False, **kwargs):
"""Mock camera update method."""
def http_post(self, url):
"""Mock post request."""
return self.return_value
class TestBlinkFunctions(unittest.TestCase):
@@ -89,33 +94,9 @@ class TestBlinkFunctions(unittest.TestCase):
"DEBUG:blinkpy.blinkpy:foo: /bar.mp4 is marked as deleted.",
]
with self.assertLogs() as dl_log:
blink.download_videos("/tmp", stop=2, delay=0)
blink.download_videos("/tmp", stop=2)
self.assertEqual(dl_log.output, expected_log)
@mock.patch("blinkpy.blinkpy.api.request_videos")
def test_parse_downloaded_throttle(self, mock_req):
"""Test ability to parse downloaded items list."""
generic_entry = {
"created_at": "1970",
"device_name": "foo",
"deleted": False,
"media": "/bar.mp4",
}
result = [generic_entry]
mock_req.return_value = {"media": result}
self.blink.last_refresh = 0
start = time.time()
self.blink.download_videos("/tmp", stop=2, delay=0, debug=True)
now = time.time()
delta = now - start
self.assertTrue(delta < 0.1)
start = time.time()
self.blink.download_videos("/tmp", stop=2, delay=0.1, debug=True)
now = time.time()
delta = now - start
self.assertTrue(delta >= 0.1)
@mock.patch("blinkpy.blinkpy.api.request_videos")
def test_parse_camera_not_in_list(self, mock_req):
"""Test ability to parse downloaded items list."""
@@ -138,18 +119,5 @@ class TestBlinkFunctions(unittest.TestCase):
"DEBUG:blinkpy.blinkpy:Skipping videos for foo.",
]
with self.assertLogs() as dl_log:
blink.download_videos("/tmp", camera="bar", stop=2, delay=0)
blink.download_videos("/tmp", camera="bar", stop=2)
self.assertEqual(dl_log.output, expected_log)
@mock.patch("blinkpy.blinkpy.api.request_network_update")
@mock.patch("blinkpy.auth.Auth.query")
def test_refresh(self, mock_req, mock_update):
"""Test ability to refresh system."""
mock_update.return_value = {"network": {"sync_module_error": False}}
mock_req.return_value = None
self.blink.last_refresh = 0
self.blink.available = True
self.blink.sync["foo"] = MockSyncModule(self.blink, "foo", 1, [])
self.blink.cameras = {"bar": MockCamera(self.blink.sync)}
self.blink.sync["foo"].cameras = self.blink.cameras
self.assertTrue(self.blink.refresh())
+2 -2
View File
@@ -68,8 +68,8 @@ class TestBlinkSetup(unittest.TestCase):
self.assertEqual(self.blink.last_refresh, None)
with mock.patch(
"blinkpy.sync_module.BlinkSyncModule.refresh", return_value=True
), mock.patch("blinkpy.blinkpy.Blink.get_homescreen", return_value=True):
self.blink.refresh(force=True)
):
self.blink.refresh()
self.assertEqual(self.blink.last_refresh, now)
self.assertEqual(self.blink.check_if_ok_to_update(), False)
-7
View File
@@ -176,10 +176,3 @@ class TestBlinkCameraSetup(unittest.TestCase):
attr = camera.attributes
for key in attr:
self.assertEqual(attr[key], None)
def test_camera_stream(self, mock_resp):
"""Test that camera stream returns correct url."""
mock_resp.return_value = {"server": "rtsps://foo.bar"}
mini_camera = BlinkCameraMini(self.blink.sync["test"])
self.assertEqual(self.camera.get_liveview(), "rtsps://foo.bar")
self.assertEqual(mini_camera.get_liveview(), "rtsps://foo.bar")
+1 -17
View File
@@ -3,7 +3,7 @@
import unittest
from unittest import mock
import time
from blinkpy.helpers.util import json_load, Throttle, time_to_seconds, gen_uid
from blinkpy.helpers.util import json_load, Throttle, time_to_seconds
class TestUtil(unittest.TestCase):
@@ -132,19 +132,3 @@ class TestUtil(unittest.TestCase):
self.assertEqual(json_load("fake.file"), None)
with mock.patch("builtins.open", mock.mock_open(read_data="")):
self.assertEqual(json_load("fake.file"), None)
def test_gen_uid(self):
"""Test gen_uid formatting."""
val1 = gen_uid(8)
val2 = gen_uid(8, uid_format=True)
self.assertEqual(len(val1), 16)
self.assertTrue(val2.startswith("BlinkCamera_"))
val2_cut = val2.split("_")
val2_split = val2_cut[1].split("-")
self.assertEqual(len(val2_split[0]), 8)
self.assertEqual(len(val2_split[1]), 4)
self.assertEqual(len(val2_split[2]), 4)
self.assertEqual(len(val2_split[3]), 4)
self.assertEqual(len(val2_split[4]), 12)
+1 -1
View File
@@ -1,5 +1,5 @@
[tox]
envlist = build, py36, py37, py38, py39, lint
envlist = build, py36, py37, py38, lint
skip_missing_interpreters = True
skipsdist = True