556 lines
16 KiB
Python
556 lines
16 KiB
Python
"""Implements known blink API calls."""
|
|
|
|
import logging
|
|
import string
|
|
from json import dumps
|
|
from asyncio import sleep
|
|
from blinkpy.helpers.util import (
|
|
get_time,
|
|
Throttle,
|
|
local_storage_clip_url_template,
|
|
)
|
|
from blinkpy.helpers.constants import DEFAULT_URL, TIMEOUT, DEFAULT_USER_AGENT
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
MIN_THROTTLE_TIME = 5
|
|
COMMAND_POLL_TIME = 1
|
|
MAX_RETRY = 120
|
|
|
|
|
|
async def request_login(
|
|
auth,
|
|
url,
|
|
login_data,
|
|
is_retry=False,
|
|
):
|
|
"""
|
|
Login request.
|
|
|
|
:param auth: Auth instance.
|
|
:param url: Login url.
|
|
:param login_data: Dictionary containing blink login data.
|
|
:param is_retry:
|
|
"""
|
|
headers = {
|
|
"Host": DEFAULT_URL,
|
|
"Content-Type": "application/json",
|
|
"user-agent": DEFAULT_USER_AGENT,
|
|
}
|
|
|
|
data = dumps(
|
|
{
|
|
"email": login_data["username"],
|
|
"password": login_data["password"],
|
|
"unique_id": login_data["uid"],
|
|
"device_identifier": login_data["device_id"],
|
|
"client_name": "Computer",
|
|
"reauth": True,
|
|
}
|
|
)
|
|
|
|
return await auth.query(
|
|
url=url,
|
|
headers=headers,
|
|
data=data,
|
|
json_resp=False,
|
|
reqtype="post",
|
|
is_retry=is_retry,
|
|
)
|
|
|
|
|
|
async def request_verify(auth, blink, verify_key):
|
|
"""Send verification key to blink servers."""
|
|
url = (
|
|
f"{blink.urls.base_url}/api/v5/accounts/{blink.account_id}"
|
|
f"/users/{blink.auth.user_id}"
|
|
f"/clients/{blink.client_id}/client_verification/pin/verify"
|
|
)
|
|
data = dumps({"pin": verify_key})
|
|
return await auth.query(
|
|
url=url,
|
|
headers=auth.header,
|
|
data=data,
|
|
json_resp=False,
|
|
reqtype="post",
|
|
)
|
|
|
|
|
|
async def request_logout(blink):
|
|
"""Logout of blink servers."""
|
|
url = (
|
|
f"{blink.urls.base_url}/api/v4/account/{blink.account_id}"
|
|
f"/client/{blink.client_id}/logout"
|
|
)
|
|
return await http_post(blink, url=url)
|
|
|
|
|
|
async def request_networks(blink):
|
|
"""Request all networks information."""
|
|
url = f"{blink.urls.base_url}/networks"
|
|
return await http_get(blink, url)
|
|
|
|
|
|
async def request_network_update(blink, network):
|
|
"""
|
|
Request network update.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
"""
|
|
url = f"{blink.urls.base_url}/network/{network}/update"
|
|
response = await http_post(blink, url)
|
|
await wait_for_command(blink, response)
|
|
return response
|
|
|
|
|
|
async def request_user(blink):
|
|
"""Get user information from blink servers."""
|
|
url = f"{blink.urls.base_url}/user"
|
|
return await http_get(blink, url)
|
|
|
|
|
|
async def request_network_status(blink, network):
|
|
"""
|
|
Request network information.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
"""
|
|
url = f"{blink.urls.base_url}/network/{network}"
|
|
return await http_get(blink, url)
|
|
|
|
|
|
async def request_syncmodule(blink, network):
|
|
"""
|
|
Request sync module info.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
"""
|
|
url = f"{blink.urls.base_url}/network/{network}/syncmodules"
|
|
return await http_get(blink, url)
|
|
|
|
|
|
@Throttle(seconds=MIN_THROTTLE_TIME)
|
|
async def request_system_arm(blink, network, **kwargs):
|
|
"""
|
|
Arm system.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
"""
|
|
url = (
|
|
f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}"
|
|
f"/networks/{network}/state/arm"
|
|
)
|
|
response = await http_post(blink, url)
|
|
await wait_for_command(blink, response)
|
|
return response
|
|
|
|
|
|
@Throttle(seconds=MIN_THROTTLE_TIME)
|
|
async def request_system_disarm(blink, network, **kwargs):
|
|
"""
|
|
Disarm system.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
"""
|
|
url = (
|
|
f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}"
|
|
f"/networks/{network}/state/disarm"
|
|
)
|
|
response = await http_post(blink, url)
|
|
await wait_for_command(blink, response)
|
|
return response
|
|
|
|
|
|
async def request_notification_flags(blink, **kwargs):
|
|
"""
|
|
Get system notification flags.
|
|
|
|
:param blink: Blink instance.
|
|
"""
|
|
url = (
|
|
f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}"
|
|
"/notifications/configuration"
|
|
)
|
|
response = await http_get(blink, url)
|
|
await wait_for_command(blink, response)
|
|
return response
|
|
|
|
|
|
async def request_set_notification_flag(blink, data_dict):
|
|
"""
|
|
Set a system notification flag.
|
|
|
|
:param blink: Blink instance.
|
|
:param data_dict: Dictionary of notifications to set.
|
|
"""
|
|
url = (
|
|
f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}"
|
|
"/notifications/configuration"
|
|
)
|
|
data = dumps({"notifications": data_dict})
|
|
response = await http_post(blink, url, data=data, json=False)
|
|
await wait_for_command(blink, response)
|
|
return response
|
|
|
|
|
|
async def request_command_status(blink, network, command_id):
|
|
"""
|
|
Request command status.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
:param command_id: Command id to check.
|
|
"""
|
|
url = f"{blink.urls.base_url}/network/{network}/command/{command_id}"
|
|
return await http_get(blink, url)
|
|
|
|
|
|
@Throttle(seconds=MIN_THROTTLE_TIME)
|
|
async def request_homescreen(blink, **kwargs):
|
|
"""Request homescreen info."""
|
|
url = f"{blink.urls.base_url}/api/v3/accounts/{blink.account_id}/homescreen"
|
|
return await http_get(blink, url)
|
|
|
|
|
|
@Throttle(seconds=MIN_THROTTLE_TIME)
|
|
async def request_sync_events(blink, network, **kwargs):
|
|
"""
|
|
Request events from sync module.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
"""
|
|
url = f"{blink.urls.base_url}/events/network/{network}"
|
|
return await http_get(blink, url)
|
|
|
|
|
|
@Throttle(seconds=MIN_THROTTLE_TIME)
|
|
async def request_new_image(blink, network, camera_id, **kwargs):
|
|
"""
|
|
Request to capture new thumbnail for camera.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network 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"
|
|
response = await http_post(blink, url)
|
|
await wait_for_command(blink, response)
|
|
return response
|
|
|
|
|
|
@Throttle(seconds=MIN_THROTTLE_TIME)
|
|
async def request_new_video(blink, network, camera_id, **kwargs):
|
|
"""
|
|
Request to capture new video clip.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network 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"
|
|
response = await http_post(blink, url)
|
|
await wait_for_command(blink, response)
|
|
return response
|
|
|
|
|
|
@Throttle(seconds=MIN_THROTTLE_TIME)
|
|
async def request_video_count(blink, **kwargs):
|
|
"""Request total video count."""
|
|
url = f"{blink.urls.base_url}/api/v2/videos/count"
|
|
return await http_get(blink, url)
|
|
|
|
|
|
async def request_videos(blink, time=None, page=0):
|
|
"""
|
|
Perform a request for videos.
|
|
|
|
:param blink: Blink instance.
|
|
:param time: Get videos since this time. In epoch seconds.
|
|
:param page: Page number to get videos from.
|
|
"""
|
|
timestamp = get_time(time)
|
|
url = (
|
|
f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}"
|
|
f"/media/changed?since={timestamp}&page={page}"
|
|
)
|
|
return await http_get(blink, url)
|
|
|
|
|
|
async def request_cameras(blink, network):
|
|
"""
|
|
Request all camera information.
|
|
|
|
:param Blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
"""
|
|
url = f"{blink.urls.base_url}/network/{network}/cameras"
|
|
return await http_get(blink, url)
|
|
|
|
|
|
async def request_camera_info(blink, network, camera_id):
|
|
"""
|
|
Request camera info for one camera.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
:param camera_id: Camera ID of camera to request info from.
|
|
"""
|
|
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/config"
|
|
return await http_get(blink, url)
|
|
|
|
|
|
async def request_camera_usage(blink):
|
|
"""
|
|
Request camera status.
|
|
|
|
:param blink: Blink instance.
|
|
"""
|
|
url = f"{blink.urls.base_url}/api/v1/camera/usage"
|
|
return await http_get(blink, url)
|
|
|
|
|
|
async def request_camera_liveview(blink, network, camera_id):
|
|
"""
|
|
Request camera liveview.
|
|
|
|
:param blink: Blink instance.
|
|
: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}"
|
|
f"/networks/{network}/cameras/{camera_id}/liveview"
|
|
)
|
|
response = await http_post(blink, url)
|
|
await wait_for_command(blink, response)
|
|
return response
|
|
|
|
|
|
async def request_camera_sensors(blink, network, camera_id):
|
|
"""
|
|
Request camera sensor info for one camera.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
:param camera_id: Camera ID of camera to request sensor info from.
|
|
"""
|
|
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/signals"
|
|
return await http_get(blink, url)
|
|
|
|
|
|
@Throttle(seconds=MIN_THROTTLE_TIME)
|
|
async def request_motion_detection_enable(blink, network, camera_id, **kwargs):
|
|
"""
|
|
Enable motion detection for a camera.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
:param camera_id: Camera ID of camera to enable.
|
|
"""
|
|
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/enable"
|
|
response = await http_post(blink, url)
|
|
await wait_for_command(blink, response)
|
|
return response
|
|
|
|
|
|
@Throttle(seconds=MIN_THROTTLE_TIME)
|
|
async def request_motion_detection_disable(blink, network, camera_id, **kwargs):
|
|
"""
|
|
Disable motion detection for a camera.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
:param camera_id: Camera ID of camera to disable.
|
|
"""
|
|
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/disable"
|
|
response = await http_post(blink, url)
|
|
await wait_for_command(blink, response)
|
|
return response
|
|
|
|
|
|
async def request_local_storage_manifest(blink, network, sync_id):
|
|
"""
|
|
Update local manifest.
|
|
|
|
Request creation of an updated manifest of video clips stored in
|
|
sync module local storage.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
:param sync_id: ID of sync module.
|
|
"""
|
|
url = (
|
|
f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}"
|
|
f"/networks/{network}/sync_modules/{sync_id}"
|
|
f"/local_storage/manifest/request"
|
|
)
|
|
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):
|
|
"""
|
|
Request manifest of video clips stored in sync module local storage.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
:param sync_id: ID of sync module.
|
|
:param manifest_request_id: Request ID of local storage manifest \
|
|
(requested creation of new manifest).
|
|
"""
|
|
url = (
|
|
f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}"
|
|
f"/networks/{network}/sync_modules/{sync_id}"
|
|
f"/local_storage/manifest/request/{manifest_request_id}"
|
|
)
|
|
return await http_get(blink, url)
|
|
|
|
|
|
async def request_local_storage_clip(blink, network, sync_id, manifest_id, clip_id):
|
|
"""
|
|
Prepare video clip stored in the sync module to be downloaded.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
:param sync_id: ID of sync module.
|
|
:param manifest_id: ID of local storage manifest (returned in manifest response).
|
|
:param clip_id: ID of the clip.
|
|
"""
|
|
url = blink.urls.base_url + string.Template(
|
|
local_storage_clip_url_template()
|
|
).substitute(
|
|
account_id=blink.account_id,
|
|
network_id=network,
|
|
sync_id=sync_id,
|
|
manifest_id=manifest_id,
|
|
clip_id=clip_id,
|
|
)
|
|
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"):
|
|
"""
|
|
Get camera configuration.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
:param camera_id: ID of camera
|
|
:param product_type: Camera product type "owl" or "catalina"
|
|
"""
|
|
if product_type == "owl":
|
|
url = (
|
|
f"{blink.urls.base_url}/api/v1/accounts/{blink.account_id}"
|
|
f"/networks/{network}/owls/{camera_id}/config"
|
|
)
|
|
elif product_type == "catalina":
|
|
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/config"
|
|
else:
|
|
_LOGGER.info(
|
|
"Camera %s with product type %s config get not implemented.",
|
|
camera_id,
|
|
product_type,
|
|
)
|
|
return None
|
|
return await http_get(blink, url)
|
|
|
|
|
|
async def request_update_config(
|
|
blink, network, camera_id, product_type="owl", data=None
|
|
):
|
|
"""
|
|
Update camera configuration.
|
|
|
|
:param blink: Blink instance.
|
|
:param network: Sync module network id.
|
|
:param camera_id: ID of camera
|
|
:param product_type: Camera product type "owl" or "catalina"
|
|
:param data: string w/JSON dict of parameters/values to update
|
|
"""
|
|
if product_type == "owl":
|
|
url = (
|
|
f"{blink.urls.base_url}/api/v1/accounts/"
|
|
f"{blink.account_id}/networks/{network}/owls/{camera_id}/config"
|
|
)
|
|
elif product_type == "catalina":
|
|
url = f"{blink.urls.base_url}/network/{network}/camera/{camera_id}/update"
|
|
else:
|
|
_LOGGER.info(
|
|
"Camera %s with product type %s config update not implemented.",
|
|
camera_id,
|
|
product_type,
|
|
)
|
|
return None
|
|
return await http_post(blink, url, json=False, data=data)
|
|
|
|
|
|
async def http_get(
|
|
blink, url, stream=False, json=True, is_retry=False, timeout=TIMEOUT
|
|
):
|
|
"""
|
|
Perform an http get request.
|
|
|
|
:param url: URL to perform get request.
|
|
:param stream: Stream response? True/FALSE
|
|
:param json: Return json response? TRUE/False
|
|
:param is_retry: Is this part of a re-auth attempt?
|
|
"""
|
|
_LOGGER.debug("Making GET request to %s", url)
|
|
return await blink.auth.query(
|
|
url=url,
|
|
headers=blink.auth.header,
|
|
reqtype="get",
|
|
stream=stream,
|
|
json_resp=json,
|
|
is_retry=is_retry,
|
|
)
|
|
|
|
|
|
async def http_post(blink, url, is_retry=False, data=None, json=True, timeout=TIMEOUT):
|
|
"""
|
|
Perform an http post request.
|
|
|
|
:param url: URL to perform post request.
|
|
:param is_retry: Is this part of a re-auth attempt?
|
|
:param data: str body for post request
|
|
:param json: Return json response? TRUE/False
|
|
"""
|
|
_LOGGER.debug("Making POST request to %s", url)
|
|
return await blink.auth.query(
|
|
url=url,
|
|
headers=blink.auth.header,
|
|
reqtype="post",
|
|
is_retry=is_retry,
|
|
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)
|
|
try:
|
|
network_id = json_data.get("network_id")
|
|
command_id = json_data.get("id")
|
|
except AttributeError:
|
|
return False
|
|
if command_id and network_id:
|
|
for _ in range(0, MAX_RETRY):
|
|
_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:
|
|
if status.get("status_code", 0) != 908:
|
|
return False
|
|
if status.get("complete"):
|
|
return True
|
|
await sleep(COMMAND_POLL_TIME)
|