Compare commits
41
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8db8eb3b6 | ||
|
|
a34b6dec67 | ||
|
|
bded3eb156 | ||
|
|
9ca73934ce | ||
|
|
e02b429523 | ||
|
|
22e4b7dabe | ||
|
|
130b5acf84 | ||
|
|
d0fa303c88 | ||
|
|
dff0746450 | ||
|
|
815dfd9a8e | ||
|
|
b0ea0566a5 | ||
|
|
0167900415 | ||
|
|
9279ba2aa9 | ||
|
|
2abfc3e843 | ||
|
|
4ff83585b4 | ||
|
|
3cf2b5e093 | ||
|
|
6a7eff6501 | ||
|
|
3805a41d9f | ||
|
|
f8c78fb499 | ||
|
|
3314ff85ed | ||
|
|
67b24eb89a | ||
|
|
d416e42925 | ||
|
|
17655849ae | ||
|
|
d9e3761c7b | ||
|
|
382db80584 | ||
|
|
695dc96340 | ||
|
|
8b1851b647 | ||
|
|
eeaa9cdf6f | ||
|
|
5cd59c049e | ||
|
|
5d86e0ac9a | ||
|
|
d9136e611f | ||
|
|
5ca1aca3c0 | ||
|
|
08632ec9ce | ||
|
|
45ba46e637 | ||
|
|
e51740ca89 | ||
|
|
eb7d7d9601 | ||
|
|
c4b70f963c | ||
|
|
b73d122b15 | ||
|
|
db072b9dea | ||
|
|
5c56c903e8 | ||
|
|
0758fa66d3 |
+14
-6
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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."""
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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."""
|
||||
|
||||
Reference in New Issue
Block a user