Compare commits

...
41 Commits
Author SHA1 Message Date
Kevin FronczakandGitHub f8db8eb3b6 Merge pull request #318 from fronzbot/login-switch
Add option to use v3 login endpoint
2020-06-29 12:49:56 -04:00
Kevin FronczakandGitHub a34b6dec67 Merge pull request #315 from fronzbot/dependabot/pip/requests-approx-eq-2.24.0
Update requests requirement from ~=2.23.0 to ~=2.24.0
2020-06-29 11:54:10 -04:00
Kevin Fronczak bded3eb156 Add option to use v3 login endpoint 2020-06-29 15:46:43 +00:00
dependabot-preview[bot]andGitHub 9ca73934ce Update requests requirement from ~=2.23.0 to ~=2.24.0
Updates the requirements on [requests](https://github.com/psf/requests) to permit the latest version.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/master/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.23.0...v2.24.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-18 08:04:10 +00:00
Kevin FronczakandGitHub e02b429523 Merge pull request #314 from fronzbot/fix-return-error
Fix error with updating owl information during refresh
2020-06-16 14:46:40 -04:00
Kevin Fronczak 22e4b7dabe Fix error with updating owl information during refresh 2020-06-16 18:42:59 +00:00
Kevin FronczakandGitHub 130b5acf84 Merge pull request #312 from fronzbot/dependabot/pip/pytest-timeout-1.4.1
Bump pytest-timeout from 1.4.0 to 1.4.1
2020-06-16 13:54:52 -04:00
Kevin FronczakandGitHub d0fa303c88 Merge pull request #313 from fronzbot/improve-mini-extraction
Broke out homescreen call to prevent back-to-back api calls
2020-06-16 13:54:37 -04:00
Kevin Fronczak dff0746450 Broke out homescreen call to prevent back-to-back api calls 2020-06-16 17:43:22 +00:00
dependabot-preview[bot]andGitHub 815dfd9a8e Bump pytest-timeout from 1.4.0 to 1.4.1
Bumps [pytest-timeout](https://github.com/pytest-dev/pytest-timeout) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/pytest-dev/pytest-timeout/releases)
- [Commits](https://github.com/pytest-dev/pytest-timeout/compare/1.4.0...1.4.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-16 07:58:09 +00:00
Kevin FronczakandGitHub b0ea0566a5 Merge pull request #310 from fronzbot/dependabot/pip/pytest-cov-2.10.0
Bump pytest-cov from 2.9.0 to 2.10.0
2020-06-15 10:57:19 -04:00
dependabot-preview[bot]andGitHub 0167900415 Bump pytest-cov from 2.9.0 to 2.10.0
Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.9.0 to 2.10.0.
- [Release notes](https://github.com/pytest-dev/pytest-cov/releases)
- [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.9.0...v2.10.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-15 14:34:40 +00:00
Kevin FronczakandGitHub 9279ba2aa9 Merge pull request #311 from fronzbot/dependabot/pip/pytest-timeout-1.4.0
Bump pytest-timeout from 1.3.4 to 1.4.0
2020-06-15 10:33:14 -04:00
dependabot-preview[bot]andGitHub 2abfc3e843 Bump pytest-timeout from 1.3.4 to 1.4.0
Bumps [pytest-timeout](https://github.com/pytest-dev/pytest-timeout) from 1.3.4 to 1.4.0.
- [Release notes](https://github.com/pytest-dev/pytest-timeout/releases)
- [Commits](https://github.com/pytest-dev/pytest-timeout/compare/1.3.4...1.4.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-15 08:34:05 +00:00
Kevin FronczakandGitHub 4ff83585b4 Merge pull request #309 from fronzbot/dev-bump
dev bump
2020-06-14 10:51:47 -04:00
Kevin FronczakandGitHub 3cf2b5e093 dev bump 2020-06-14 10:49:17 -04:00
Kevin FronczakandGitHub 6a7eff6501 Merge pull request #308 from fronzbot/fix-extra-reauth
Removed 'reauth' key from login payload
2020-06-14 10:45:58 -04:00
Kevin Fronczak 3805a41d9f Removed 'reauth' key from login payload 2020-06-14 13:43:58 +00:00
Kevin FronczakandGitHub f8c78fb499 Merge pull request #307 from fronzbot/update-docker-app
Updated docker app to be compatible with 0.16.0 changes
2020-06-13 22:40:35 -04:00
Kevin Fronczak 3314ff85ed Updated docke app to be compatible with 0.16.0 changes 2020-06-14 02:36:57 +00:00
Kevin FronczakandGitHub 67b24eb89a Merge pull request #306 from fronzbot/dev-bump
Dev version bump
2020-06-13 22:18:31 -04:00
Kevin Fronczak d416e42925 Dev version bump 2020-06-14 02:12:43 +00:00
Kevin FronczakandGitHub 17655849ae Merge pull request #305 from fronzbot/disable-owl-option
Add option to skip homescreen check
2020-06-13 22:08:08 -04:00
Kevin Fronczak d9e3761c7b Add option to skip homescreen check 2020-06-14 02:03:22 +00:00
Kevin FronczakandGitHub 382db80584 Merge pull request #304 from fronzbot/codecov-update
Update codecov.yml
2020-06-11 22:57:13 -04:00
Kevin FronczakandGitHub 695dc96340 Update codecov.yml 2020-06-11 22:54:46 -04:00
Kevin FronczakandGitHub 8b1851b647 Merge pull request #303 from fronzbot/support-mini-attached-to-sync
Prevent sync module creation for minis already attached to one
2020-06-11 22:51:24 -04:00
Kevin Fronczak eeaa9cdf6f Prevent sync module creation for minis already attached to one 2020-06-11 23:05:37 +00:00
Kevin FronczakandGitHub 5cd59c049e Merge pull request #302 from fronzbot/dev-bump
Dev version bump
2020-06-10 11:14:03 -04:00
Kevin Fronczak 5d86e0ac9a Dev version bump 2020-06-10 15:10:17 +00:00
Kevin FronczakandGitHub d9136e611f Merge pull request #297 from fronzbot/dependabot/pip/pre-commit-2.5.1
Bump pre-commit from 2.5.0 to 2.5.1
2020-06-10 11:03:38 -04:00
Kevin FronczakandGitHub 5ca1aca3c0 Merge pull request #301 from fronzbot/fix-mini-attributes
Add type check on temperature conversion
2020-06-10 11:03:14 -04:00
Kevin Fronczak 08632ec9ce Add type check on temperature conversion 2020-06-10 14:58:28 +00:00
Kevin FronczakandGitHub 45ba46e637 Merge pull request #300 from fronzbot/blink-mini-arm-support
Add blink mini arm/disarm support
2020-06-10 10:20:42 -04:00
Kevin Fronczak e51740ca89 Removed blink mini arm overload 2020-06-10 14:16:16 +00:00
Kevin Fronczak eb7d7d9601 Add blink mini arm/disarm support
- Updates endpoint for arm/disarm so it's consistent across device types
2020-06-10 14:13:42 +00:00
Kevin FronczakandGitHub c4b70f963c Merge pull request #299 from fronzbot/fix-sync-arm
Fixed missing sync module arm method
2020-06-10 10:13:00 -04:00
Kevin Fronczak b73d122b15 Fixed missing sync module arm method 2020-06-10 14:06:42 +00:00
dependabot-preview[bot]andGitHub db072b9dea Bump pre-commit from 2.5.0 to 2.5.1
Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 2.5.0 to 2.5.1.
- [Release notes](https://github.com/pre-commit/pre-commit/releases)
- [Changelog](https://github.com/pre-commit/pre-commit/blob/master/CHANGELOG.md)
- [Commits](https://github.com/pre-commit/pre-commit/compare/v2.5.0...v2.5.1)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2020-06-10 08:27:36 +00:00
Kevin FronczakandGitHub 5c56c903e8 Merge pull request #294 from fronzbot/dev-bump
Dev version bump
2020-06-09 13:54:34 -04:00
Kevin Fronczak 0758fa66d3 Dev version bump 2020-06-09 17:50:31 +00:00
14 changed files with 207 additions and 65 deletions
+14 -6
View File
@@ -1,11 +1,12 @@
"""Script to run blinkpy as an app."""
from os import environ
from datetime import datetime, timedelta
from blinkpy import blinkpy
from blinkpy.blinkpy import Blink
from blinkpy.auth import Auth
from blinkpy.helpers.util import json_load
USERNAME = environ.get("USERNAME")
PASSWORD = environ.get("PASSWORD")
CREDFILE = environ.get("CREDFILE")
TIMEDELTA = timedelta(environ.get("TIMEDELTA", 1))
@@ -21,11 +22,18 @@ def download_videos(blink, save_dir="/media"):
def start():
"""Startup blink app."""
blink = blinkpy.Blink(username=USERNAME, password=PASSWORD)
blink = Blink()
blink.auth = Auth(json_load(CREDFILE))
blink.start()
return blink
def main():
"""Run the app."""
blink = start()
download_videos(blink)
blink.save(CREDFILE)
if __name__ == "__main__":
BLINK = start()
download_videos(BLINK)
main()
+3 -3
View File
@@ -32,7 +32,7 @@ def request_login(
"client_name": "Computer",
"client_type": "android",
"os_version": "5.1.1",
"reauth": "true",
"reauth": "false",
}
)
return auth.query(
@@ -107,7 +107,7 @@ def request_system_arm(blink, network):
:param blink: Blink instance.
:param network: Sync module network id.
"""
url = f"{blink.urls.base_url}/network/{network}/arm"
url = f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}/networks/{network}/state/arm"
return http_post(blink, url)
@@ -119,7 +119,7 @@ def request_system_disarm(blink, network):
:param blink: Blink instance.
:param network: Sync module network id.
"""
url = f"{blink.urls.base_url}/network/{network}/disarm"
url = f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}/networks/{network}/state/disarm"
return http_post(blink, url)
+6 -4
View File
@@ -12,7 +12,7 @@ _LOGGER = logging.getLogger(__name__)
class Auth:
"""Class to handle login communication."""
def __init__(self, login_data=None, no_prompt=False):
def __init__(self, login_data=None, no_prompt=False, login_method="v4"):
"""
Initialize auth handler.
@@ -22,6 +22,7 @@ class Auth:
- password
:param no_prompt: Should any user input prompts
be supressed? True/FALSE
:param login_method: Choose the login endpoint to use. Default: v4. v3 uses email verification rather than a 2FA code.
"""
if login_data is None:
login_data = {}
@@ -31,6 +32,7 @@ class Auth:
self.region_id = login_data.get("region_id", None)
self.client_id = login_data.get("client_id", None)
self.account_id = login_data.get("account_id", None)
self.login_url = LOGIN_ENDPOINT[login_method]
self.login_response = None
self.is_errored = False
self.no_prompt = no_prompt
@@ -73,11 +75,11 @@ class Auth:
self.data = util.validate_login_data(self.data)
def login(self, login_url=LOGIN_ENDPOINT):
def login(self):
"""Attempt login to blink servers."""
self.validate_login()
_LOGGER.info("Attempting login with %s", login_url)
response = api.request_login(self, login_url, self.data, is_retry=False,)
_LOGGER.info("Attempting login with %s", self.login_url)
response = api.request_login(self, self.login_url, self.data, is_retry=False,)
try:
if response.status_code == 200:
return response.json()
+28 -6
View File
@@ -40,7 +40,10 @@ class Blink:
"""Class to initialize communication."""
def __init__(
self, refresh_rate=DEFAULT_REFRESH, motion_interval=DEFAULT_MOTION_INTERVAL,
self,
refresh_rate=DEFAULT_REFRESH,
motion_interval=DEFAULT_MOTION_INTERVAL,
no_owls=False,
):
"""
Initialize Blink system.
@@ -51,6 +54,7 @@ class Blink:
Defaults to last refresh time.
Useful for preventing motion_detected property
from de-asserting too quickly.
:param no_owls: Disable searching for owl entries (blink mini cameras only known entity). Prevents an uneccessary API call if you don't have these in your network.
"""
self.auth = Auth()
self.account_id = None
@@ -68,6 +72,7 @@ class Blink:
self.available = False
self.key_required = False
self.homescreen = {}
self.no_owls = no_owls
@util.Throttle(seconds=MIN_THROTTLE_TIME)
def refresh(self, force=False):
@@ -95,6 +100,7 @@ class Blink:
self.auth.startup()
self.setup_login_ids()
self.setup_urls()
self.get_homescreen()
except (LoginError, TokenRefreshFailed, BlinkSetupError):
_LOGGER.error("Cannot setup Blink platform.")
self.available = False
@@ -128,7 +134,6 @@ class Blink:
sync_cameras = cameras.get(network_id, {})
self.setup_sync_module(name, network_id, sync_cameras)
self.setup_owls()
self.cameras = self.merge_cameras()
self.available = True
@@ -140,15 +145,27 @@ class Blink:
self.sync[name] = BlinkSyncModule(self, name, network_id, cameras)
self.sync[name].start()
def get_homescreen(self):
"""Get homecreen information."""
if self.no_owls:
_LOGGER.debug("Skipping owl extraction.")
self.homescreen = {}
return
self.homescreen = api.request_homescreen(self)
def setup_owls(self):
"""Check for mini cameras."""
response = api.request_homescreen(self)
self.homescreen = response
network_list = []
camera_list = []
try:
for owl in response["owls"]:
for owl in self.homescreen["owls"]:
name = owl["name"]
network_id = owl["network_id"]
network_id = str(owl["network_id"])
if network_id in self.network_ids:
camera_list.append(
{network_id: {"name": name, "id": network_id, "type": "mini"}}
)
continue
if owl["onboarded"]:
network_list.append(str(network_id))
self.sync[name] = BlinkOwl(self, name, network_id, owl)
@@ -158,6 +175,7 @@ class Blink:
pass
self.network_ids.extend(network_list)
return camera_list
def setup_camera_list(self):
"""Create camera list for onboarded networks."""
@@ -172,6 +190,10 @@ class Blink:
all_cameras[camera_network].append(
{"name": camera["name"], "id": camera["id"]}
)
mini_cameras = self.setup_owls()
for camera in mini_cameras:
for network, camera_info in camera.items():
all_cameras[network].append(camera_info)
return all_cameras
except (KeyError, TypeError):
_LOGGER.error("Unable to retrieve cameras from response %s", response)
+13 -2
View File
@@ -29,6 +29,7 @@ class BlinkCamera:
self.last_record = None
self._cached_image = None
self._cached_video = None
self.camera_type = ""
@property
def attributes(self):
@@ -61,7 +62,10 @@ class BlinkCamera:
@property
def temperature_c(self):
"""Return temperature in celcius."""
return round((self.temperature - 32) / 9.0 * 5.0, 1)
try:
return round((self.temperature - 32) / 9.0 * 5.0, 1)
except TypeError:
return None
@property
def image_from_cache(self):
@@ -226,6 +230,11 @@ class BlinkCamera:
class BlinkCameraMini(BlinkCamera):
"""Define a class for a Blink Mini camera."""
def __init__(self, sync):
"""Initialize a Blink Mini cameras."""
super().__init__(sync)
self.camera_type = "mini"
@property
def arm(self):
"""Return camera arm status."""
@@ -234,7 +243,9 @@ class BlinkCameraMini(BlinkCamera):
@arm.setter
def arm(self, value):
"""Set camera arm status."""
self.sync.arm = value
_LOGGER.warning(
"Individual camera motion detection enable/disable for Blink Mini cameras is unsupported at this time."
)
def snap_picture(self):
"""Snap picture for a blink mini camera."""
+5 -2
View File
@@ -4,7 +4,7 @@ import os
MAJOR_VERSION = 0
MINOR_VERSION = 16
PATCH_VERSION = "0-rc3"
PATCH_VERSION = "0-rc9"
__version__ = f"{MAJOR_VERSION}.{MINOR_VERSION}.{PATCH_VERSION}"
@@ -48,7 +48,10 @@ 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/v4/account/login"
LOGIN_ENDPOINT = {
"v4": f"{BASE_URL}/api/v4/account/login",
"v3": f"{BASE_URL}/api/v3/login",
}
"""
Dictionaries
+32 -28
View File
@@ -77,8 +77,10 @@ class BlinkSyncModule:
@arm.setter
def arm(self, value):
"""Arm or disarm system."""
_LOGGER.warning("Arm/Disarm API for %s not currently implemented.", self.name)
"""Arm or disarm camera."""
if value:
return api.request_system_arm(self.blink, self.network_id)
return api.request_system_disarm(self.blink, self.network_id)
def start(self):
"""Initialize the system."""
@@ -122,16 +124,33 @@ class BlinkSyncModule:
for camera_config in self.camera_list:
if "name" not in camera_config:
break
blink_camera_type = camera_config.get("type", "")
name = camera_config["name"]
self.cameras[name] = camera_type(self)
self.motion[name] = False
camera_info = self.get_camera_info(camera_config["id"])
owl_info = self.get_owl_info(name)
if blink_camera_type == "mini":
camera_type = BlinkCameraMini
self.cameras[name] = camera_type(self)
camera_info = self.get_camera_info(
camera_config["id"], owl_info=owl_info
)
self.cameras[name].update(camera_info, force_cache=True, force=True)
except KeyError:
_LOGGER.error("Could not create camera instances for %s", self.name)
return False
return True
def get_owl_info(self, name):
"""Extract owl information."""
try:
for owl in self.blink.homescreen["owls"]:
if owl["name"] == name:
return owl
except KeyError:
pass
return None
def get_events(self, **kwargs):
"""Retrieve events from server."""
force = kwargs.pop("force", False)
@@ -142,14 +161,17 @@ class BlinkSyncModule:
_LOGGER.error("Could not extract events: %s", response, exc_info=True)
return False
def get_camera_info(self, camera_id):
def get_camera_info(self, camera_id, **kwargs):
"""Retrieve camera information."""
owl = kwargs.get("owl_info", None)
if owl is not None:
return owl
response = api.request_camera_info(self.blink, self.network_id, camera_id)
try:
return response["camera"][0]
except (TypeError, KeyError):
_LOGGER.error("Could not extract camera info: %s", response, exc_info=True)
return []
return {}
def get_network_info(self):
"""Retrieve network status."""
@@ -169,7 +191,9 @@ class BlinkSyncModule:
self.check_new_videos()
for camera_name in self.cameras.keys():
camera_id = self.cameras[camera_name].camera_id
camera_info = self.get_camera_info(camera_id)
camera_info = self.get_camera_info(
camera_id, owl_info=self.get_owl_info(camera_name)
)
self.cameras[camera_name].update(camera_info, force_cache=force_cache)
self.available = True
@@ -241,7 +265,7 @@ class BlinkOwl(BlinkSyncModule):
"""Update sync-less cameras."""
return super().update_cameras(camera_type=BlinkCameraMini)
def get_camera_info(self, camera_id):
def get_camera_info(self, camera_id, **kwargs):
"""Retrieve camera information."""
try:
for owl in self.blink.homescreen["owls"]:
@@ -272,23 +296,3 @@ class BlinkOwl(BlinkSyncModule):
@network_info.setter
def network_info(self, value):
"""Set network_info property."""
@property
def arm(self):
"""Return arm status."""
try:
return self.network_info["network"]["armed"]
except (KeyError, TypeError):
self.available = False
return None
@arm.setter
def arm(self, value):
"""Arm or disarm camera."""
if value:
return api.request_motion_detection_enable(
self.blink, self.network_id, self.sync_id
)
return api.request_motion_detection_disable(
self.blink, self.network_id, self.sync_id
)
+6 -2
View File
@@ -4,8 +4,12 @@ coverage:
status:
project:
default:
target: 80
threshold: 0.02
target: 80%
threshold: 2%
patch:
default:
target: 60%
threshold: 5%
comment: true
require_ci_to_pass: yes
+1 -1
View File
@@ -1,4 +1,4 @@
python-dateutil~=2.8.1
requests~=2.23.0
requests~=2.24.0
python-slugify~=4.0.0
testtools==2.4.0
+3 -3
View File
@@ -2,12 +2,12 @@ black==19.10b0
coverage==5.1
flake8==3.8.3
flake8-docstrings==1.5.0
pre-commit==2.5.0
pre-commit==2.5.1
pylint==2.5.3
pydocstyle==5.0.2
pytest==5.4.3
pytest-cov==2.9.0
pytest-cov==2.10.0
pytest-sugar==0.9.3
pytest-timeout==1.3.4
pytest-timeout==1.4.1
restructuredtext-lint==1.3.1
pygments==2.6.1
+15
View File
@@ -144,6 +144,21 @@ class TestAuth(unittest.TestCase):
fake_resp = mresp.MockResponse({"foo": "bar"}, 200)
mock_req.return_value = fake_resp
self.assertEqual(self.auth.login(), {"foo": "bar"})
mock_req.assert_called_with(
self.auth, const.LOGIN_ENDPOINT["v4"], {}, is_retry=False
)
@mock.patch("blinkpy.auth.Auth.validate_login", return_value=None)
@mock.patch("blinkpy.auth.api.request_login")
def test_login_v3(self, mock_req, mock_validate):
"""Test login handling."""
auth_v3 = Auth(login_method="v3")
fake_resp = mresp.MockResponse({"foo": "bar"}, 200)
mock_req.return_value = fake_resp
self.assertEqual(auth_v3.login(), {"foo": "bar"})
mock_req.assert_called_with(
auth_v3, const.LOGIN_ENDPOINT["v3"], {}, is_retry=False
)
@mock.patch("blinkpy.auth.Auth.validate_login", return_value=None)
@mock.patch("blinkpy.auth.api.request_login")
+59 -4
View File
@@ -83,8 +83,10 @@ class TestBlinkSetup(unittest.TestCase):
self.assertEqual(self.blink.sync["tEsT"], 1234)
@mock.patch("blinkpy.api.request_camera_usage")
def test_setup_cameras(self, mock_req):
@mock.patch("blinkpy.api.request_homescreen")
def test_setup_cameras(self, mock_home, mock_req):
"""Check retrieval of camera information."""
mock_home.return_value = {}
mock_req.return_value = {
"networks": [
{
@@ -195,12 +197,11 @@ class TestBlinkSetup(unittest.TestCase):
self.assertEqual(combined["fizz"], "buzz")
self.assertEqual(combined["bar"], "foo")
@mock.patch("blinkpy.api.request_homescreen")
@mock.patch("blinkpy.blinkpy.BlinkOwl.start")
def test_initialize_blink_minis(self, mock_start, mock_home):
def test_initialize_blink_minis(self, mock_start):
"""Test blink mini initialization."""
mock_start.return_value = True
mock_home.return_value = {
self.blink.homescreen = {
"owls": [
{
"enabled": False,
@@ -233,6 +234,60 @@ class TestBlinkSetup(unittest.TestCase):
self.assertEqual(self.blink.sync["foo"].name, "foo")
self.assertEqual(self.blink.sync["bar"].name, "bar")
def test_blink_mini_cameras_returned(self):
"""Test that blink mini cameras are found if attached to sync module."""
self.blink.network_ids = ["1234"]
self.blink.homescreen = {
"owls": [
{
"id": 1,
"name": "foo",
"network_id": 1234,
"onboarded": True,
"enabled": True,
"status": "online",
"thumbnail": "/foo/bar",
"serial": "abc123",
}
]
}
result = self.blink.setup_owls()
self.assertEqual(self.blink.network_ids, ["1234"])
self.assertEqual(
result, [{"1234": {"name": "foo", "id": "1234", "type": "mini"}}]
)
self.blink.no_owls = True
self.blink.network_ids = []
self.blink.get_homescreen()
result = self.blink.setup_owls()
self.assertEqual(self.blink.network_ids, [])
self.assertEqual(result, [])
@mock.patch("blinkpy.api.request_camera_usage")
def test_blink_mini_attached_to_sync(self, mock_usage):
"""Test that blink mini cameras are properly attached to sync module."""
self.blink.network_ids = ["1234"]
self.blink.homescreen = {
"owls": [
{
"id": 1,
"name": "foo",
"network_id": 1234,
"onboarded": True,
"enabled": True,
"status": "online",
"thumbnail": "/foo/bar",
"serial": "abc123",
}
]
}
mock_usage.return_value = {"networks": [{"cameras": [], "network_id": 1234}]}
result = self.blink.setup_camera_list()
self.assertEqual(
result, {"1234": [{"name": "foo", "id": "1234", "type": "mini"}]}
)
class MockSync:
"""Mock sync module class."""
+19 -1
View File
@@ -11,7 +11,7 @@ from unittest import mock
from blinkpy.blinkpy import Blink
from blinkpy.helpers.util import BlinkURLHandler
from blinkpy.sync_module import BlinkSyncModule
from blinkpy.camera import BlinkCamera
from blinkpy.camera import BlinkCamera, BlinkCameraMini
CAMERA_CFG = {
@@ -161,3 +161,21 @@ class TestBlinkCameraSetup(unittest.TestCase):
mock_en.return_value = "enable"
self.assertEqual(self.camera.set_motion_detect(True), "enable")
self.assertEqual(self.camera.set_motion_detect(False), "disable")
def test_missing_attributes(self, mock_resp):
"""Test that attributes return None if missing."""
self.camera.temperature = None
self.camera.serial = None
attr = self.camera.attributes
self.assertEqual(attr["serial"], None)
self.assertEqual(attr["temperature"], None)
self.assertEqual(attr["temperature_c"], None)
def test_mini_missing_attributes(self, mock_resp):
"""Test that attributes return None if missing."""
camera = BlinkCameraMini(self.blink.sync)
self.blink.sync.network_id = None
self.blink.sync.name = None
attr = camera.attributes
for key in attr:
self.assertEqual(attr[key], None)
+3 -3
View File
@@ -80,11 +80,11 @@ class TestBlinkSyncModule(unittest.TestCase):
def test_get_camera_info_fail(self, mock_resp):
"""Test handling of failed get camera info function."""
mock_resp.return_value = None
self.assertEqual(self.blink.sync["test"].get_camera_info("1"), [])
self.assertEqual(self.blink.sync["test"].get_camera_info("1"), {})
mock_resp.return_value = {}
self.assertEqual(self.blink.sync["test"].get_camera_info("1"), [])
self.assertEqual(self.blink.sync["test"].get_camera_info("1"), {})
mock_resp.return_value = {"camera": None}
self.assertEqual(self.blink.sync["test"].get_camera_info("1"), [])
self.assertEqual(self.blink.sync["test"].get_camera_info("1"), {})
def test_get_network_info(self, mock_resp):
"""Test network retrieval."""