Skip to content

Commit

Permalink
add check command to PUT
Browse files Browse the repository at this point in the history
  • Loading branch information
mkmer committed Sep 15, 2023
1 parent 0c7c3a1 commit 363cad2
Show file tree
Hide file tree
Showing 6 changed files with 136 additions and 46 deletions.
59 changes: 49 additions & 10 deletions blinkpy/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
import string
from json import dumps
from asyncio import sleep
from blinkpy.helpers.util import (
get_time,
Throttle,
Expand All @@ -13,6 +14,7 @@
_LOGGER = logging.getLogger(__name__)

MIN_THROTTLE_TIME = 5
COMMAND_POLL_TIME = 1


async def request_login(
Expand Down Expand Up @@ -94,7 +96,9 @@ async def request_network_update(blink, network):
:param network: Sync module network id.
"""
url = f"{blink.urls.base_url}/network/{network}/update"
return await http_post(blink, url)
response = await http_post(blink, url)
await wait_for_command(blink, response)
return response


async def request_user(blink):
Expand Down Expand Up @@ -137,7 +141,9 @@ async def request_system_arm(blink, network):
f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}"
f"/networks/{network}/state/arm"
)
return await http_post(blink, url)
response = await http_post(blink, url)
await wait_for_command(blink, response)
return response


@Throttle(seconds=MIN_THROTTLE_TIME)
Expand All @@ -152,7 +158,9 @@ async def request_system_disarm(blink, network):
f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}"
f"/networks/{network}/state/disarm"
)
return await http_post(blink, url)
response = await http_post(blink, url)
await wait_for_command(blink, response)
return response


async def request_command_status(blink, network, command_id):
Expand Down Expand Up @@ -196,7 +204,9 @@ async def request_new_image(blink, network, camera_id):
:param camera_id: Camera ID of camera to request new image from.
"""
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/thumbnail"
return await http_post(blink, url)
response = await http_post(blink, url)
await wait_for_command(blink, response)
return response


@Throttle(seconds=MIN_THROTTLE_TIME)
Expand All @@ -209,7 +219,9 @@ async def request_new_video(blink, network, camera_id):
:param camera_id: Camera ID of camera to request new video from.
"""
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/clip"
return await http_post(blink, url)
response = await http_post(blink, url)
await wait_for_command(blink, response)
return response


@Throttle(seconds=MIN_THROTTLE_TIME)
Expand Down Expand Up @@ -280,7 +292,9 @@ async def request_camera_liveview(blink, network, camera_id):
f"{blink.urls.base_url}/api/v5/accounts/{blink.account_id}"
f"/networks/{network}/cameras/{camera_id}/liveview"
)
return await http_post(blink, url)
response = await http_post(blink, url)
await wait_for_command(blink, response)
return response


async def request_camera_sensors(blink, network, camera_id):
Expand All @@ -305,7 +319,9 @@ async def request_motion_detection_enable(blink, network, camera_id):
:param camera_id: Camera ID of camera to enable.
"""
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/enable"
return await http_post(blink, url)
response = await http_post(blink, url)
await wait_for_command(blink, response)
return response


@Throttle(seconds=MIN_THROTTLE_TIME)
Expand All @@ -317,7 +333,9 @@ async def request_motion_detection_disable(blink, network, camera_id):
:param camera_id: Camera ID of camera to disable.
"""
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/disable"
return await http_post(blink, url)
response = await http_post(blink, url)
await wait_for_command(blink, response)
return response


async def request_local_storage_manifest(blink, network, sync_id):
Expand All @@ -335,7 +353,9 @@ async def request_local_storage_manifest(blink, network, sync_id):
f"/networks/{network}/sync_modules/{sync_id}"
f"/local_storage/manifest/request"
)
return await http_post(blink, url)
response = await http_post(blink, url)
await wait_for_command(blink, response)
return response


async def get_local_storage_manifest(blink, network, sync_id, manifest_request_id):
Expand Down Expand Up @@ -373,7 +393,9 @@ async def request_local_storage_clip(blink, network, sync_id, manifest_id, clip_
manifest_id=manifest_id,
clip_id=clip_id,
)
return await http_post(blink, url)
response = await http_post(blink, url)
await wait_for_command(blink, response)
return response


async def request_get_config(blink, network, camera_id, product_type="owl"):
Expand Down Expand Up @@ -467,3 +489,20 @@ async def http_post(blink, url, is_retry=False, data=None, json=True, timeout=TI
json_resp=json,
data=data,
)


async def wait_for_command(blink, json_data: dict) -> bool:
"""Wait for command to complete."""
_LOGGER.debug("Command Wait %s", json_data)
network_id = json_data.get("network_id")
command_id = json_data.get("id")
if command_id and network_id:
while True:
_LOGGER.debug("Making GET request waiting for command")
status = await request_command_status(blink, network_id, command_id)
_LOGGER.debug("command status %s", status)
if status.get("complete"):
return True
if status.get("status_code", 0) != 908:
return False
await sleep(COMMAND_POLL_TIME)
20 changes: 16 additions & 4 deletions blinkpy/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,9 @@ async def async_arm(self, value):
f"{self.network_id}/owls/{self.camera_id}/config"
)
data = dumps({"enabled": value})
return await api.http_post(self.sync.blink, url, json=False, data=data)
response = await api.http_post(self.sync.blink, url, data=data)
await api.wait_for_command(self.sync.blink, response)
return response

async def snap_picture(self):
"""Snap picture for a blink mini camera."""
Expand All @@ -480,7 +482,9 @@ async def snap_picture(self):
f"{self.sync.blink.account_id}/networks/"
f"{self.network_id}/owls/{self.camera_id}/thumbnail"
)
return await api.http_post(self.sync.blink, url)
response = await api.http_post(self.sync.blink, url)
await api.wait_for_command(self.sync.blink, response)
return response

async def get_sensor_info(self):
"""Get sensor info for blink mini camera."""
Expand All @@ -493,6 +497,7 @@ async def get_liveview(self):
f"{self.network_id}/owls/{self.camera_id}/liveview"
)
response = await api.http_post(self.sync.blink, url)
await api.wait_for_command(self.sync.blink, response)
server = response["server"]
server_split = server.split(":")
server_split[0] = "rtsps:"
Expand Down Expand Up @@ -524,7 +529,10 @@ async def async_arm(self, value):
url = f"{url}/enable"
else:
url = f"{url}/disable"
return await api.http_post(self.sync.blink, url)

response = await api.http_post(self.sync.blink, url)
await api.wait_for_command(self.sync.blink, response)
return response

async def snap_picture(self):
"""Snap picture for a blink doorbell camera."""
Expand All @@ -533,7 +541,10 @@ async def snap_picture(self):
f"{self.sync.blink.account_id}/networks/"
f"{self.sync.network_id}/doorbells/{self.camera_id}/thumbnail"
)
return await api.http_post(self.sync.blink, url)

response = await api.http_post(self.sync.blink, url)
await api.wait_for_command(self.sync.blink, response)
return response

async def get_sensor_info(self):
"""Get sensor info for blink doorbell camera."""
Expand All @@ -546,6 +557,7 @@ async def get_liveview(self):
f"{self.sync.network_id}/doorbells/{self.camera_id}/liveview"
)
response = await api.http_post(self.sync.blink, url)
await api.wait_for_command(self.sync.blink, response)
server = response["server"]
link = server.replace("immis://", "rtsps://")
return link
14 changes: 4 additions & 10 deletions blinkpy/sync_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,17 +679,11 @@ def url(self, manifest_id=None):

async def prepare_download(self, blink, max_retries=4):
"""Initiate upload of media item from the sync module to Blink cloud servers."""
if max_retries == 0:
return None
url = blink.urls.base_url + self.url()
response = None
for retry in range(max_retries):
response = await api.http_post(blink, url)
if "id" in response:
break
seconds = backoff_seconds(retry=retry, default_time=3)
_LOGGER.debug(
"[retry=%d] Retrying in %d seconds: %s", retry + 1, seconds, url
)
await asyncio.sleep(seconds)
response = await api.http_post(blink, url)
await api.wait_for_command(blink, response)
return response

async def delete_video(self, blink, max_retries=4) -> bool:
Expand Down
4 changes: 4 additions & 0 deletions tests/mock_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ def __init__(self, json_data, status_code, headers={}, raw_data=None):
async def json(self):
"""Return json data from get_request."""
return self.json_data

def get(self, name):
"""Return field for json."""
return self.json_data[name]
43 changes: 36 additions & 7 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
from blinkpy.auth import Auth
import tests.mock_responses as mresp

COMMAND_RESPONSE = {"network_id": "12345", "id": "54321"}
COMMAND_COMPLETE = {"complete": True, "status_code": 908}
COMMAND_NOT_COMPLETE = {"complete": False, "status_code": 908}


@mock.patch("blinkpy.auth.Auth.query")
class TestAPI(IsolatedAsyncioTestCase):
Expand Down Expand Up @@ -57,21 +61,27 @@ async def test_request_network_status(self, mock_resp):

async def test_request_command_status(self, mock_resp):
"""Test command_status."""
mock_resp.return_value = {"command": "done"}
mock_resp.side_effect = ({"command": "done"}, COMMAND_COMPLETE)
self.assertEqual(
await api.request_command_status(self.blink, "network", "command"),
{"command": "done"},
)

async def test_request_new_image(self, mock_resp):
"""Test api request new image."""
mock_resp.return_value = mresp.MockResponse({}, 200)
mock_resp.side_effect = (
mresp.MockResponse(COMMAND_RESPONSE, 200),
COMMAND_COMPLETE,
)
response = await api.request_new_image(self.blink, "network", "camera")
self.assertEqual(response.status, 200)

async def test_request_new_video(self, mock_resp):
"""Test api request new Video."""
mock_resp.return_value = mresp.MockResponse({}, 200)
mock_resp.side_effect = (
mresp.MockResponse(COMMAND_RESPONSE, 200),
COMMAND_COMPLETE,
)
response = await api.request_new_video(self.blink, "network", "camera")
self.assertEqual(response.status, 200)

Expand All @@ -97,23 +107,32 @@ async def test_request_camera_usage(self, mock_resp):

async def test_request_motion_detection_enable(self, mock_resp):
"""Test Motion detect enable."""
mock_resp.return_value = mresp.MockResponse({}, 200)
mock_resp.side_effect = (
mresp.MockResponse(COMMAND_RESPONSE, 200),
COMMAND_COMPLETE,
)
response = await api.request_motion_detection_enable(
self.blink, "network", "camera"
)
self.assertEqual(response.status, 200)

async def test_request_motion_detection_disable(self, mock_resp):
"""Test Motion detect enable."""
mock_resp.return_value = mresp.MockResponse({}, 200)
mock_resp.side_effect = (
mresp.MockResponse(COMMAND_RESPONSE, 200),
COMMAND_COMPLETE,
)
response = await api.request_motion_detection_disable(
self.blink, "network", "camera"
)
self.assertEqual(response.status, 200)

async def test_request_local_storage_clip(self, mock_resp):
"""Test Motion detect enable."""
mock_resp.return_value = mresp.MockResponse({}, 200)
mock_resp.side_effect = (
mresp.MockResponse(COMMAND_RESPONSE, 200),
COMMAND_COMPLETE,
)
response = await api.request_local_storage_clip(
self.blink, "network", "sync_id", "manifest_id", "clip_id"
)
Expand All @@ -135,7 +154,7 @@ async def test_request_get_config(self, mock_resp):

async def test_request_update_config(self, mock_resp):
"""Test Motion detect enable."""
mock_resp.return_value = mresp.MockResponse({}, 200)
mock_resp.return_value = mresp.MockResponse(COMMAND_RESPONSE, 200)
response = await api.request_update_config(
self.blink, "network", "camera_id", "owl"
)
Expand All @@ -149,3 +168,13 @@ async def test_request_update_config(self, mock_resp):
self.blink, "network", "camera_id", "other_camera"
)
)

async def test_wait_for_command(self, mock_resp):
"""Test Motion detect enable."""
mock_resp.side_effect = (COMMAND_NOT_COMPLETE, COMMAND_COMPLETE)
response = await api.wait_for_command(self.blink, COMMAND_RESPONSE)
assert response

mock_resp.side_effect = (COMMAND_NOT_COMPLETE, {})
response = await api.wait_for_command(self.blink, COMMAND_RESPONSE)
self.assertFalse(response)
Loading

0 comments on commit 363cad2

Please sign in to comment.