Update smartcamera to support single get/set/do requests (#1187)

Not supported by H200 hub
This commit is contained in:
Steven B.
2024-10-24 13:11:28 +01:00
committed by GitHub
parent c839aaa1dd
commit 8ee8c17bdc
7 changed files with 505 additions and 123 deletions

View File

@@ -3,6 +3,7 @@
from __future__ import annotations
import logging
from dataclasses import dataclass
from pprint import pformat as pf
from typing import Any
@@ -22,6 +23,28 @@ from .sslaestransport import (
_LOGGER = logging.getLogger(__name__)
# List of getMethodNames that should be sent as {"method":"do"}
# https://md.depau.eu/s/r1Ys_oWoP#Modules
GET_METHODS_AS_DO = {
"getSdCardFormatStatus",
"getConnectionType",
"getUserID",
"getP2PSharePassword",
"getAESEncryptKey",
"getFirmwareAFResult",
"getWhitelampStatus",
}
@dataclass
class SingleRequest:
"""Class for returning single request details from helper functions."""
method_type: str
method_name: str
param_name: str
request: dict[str, Any]
class SmartCameraProtocol(SmartProtocol):
"""Class for SmartCamera Protocol."""
@@ -63,37 +86,70 @@ class SmartCameraProtocol(SmartProtocol):
"""Close the underlying transport."""
await self._transport.close()
@staticmethod
def _get_smart_camera_single_request(
request: dict[str, dict[str, Any]],
) -> SingleRequest:
method = next(iter(request))
if method == "multipleRequest":
method_type = "multi"
params = request["multipleRequest"]
req = {"method": "multipleRequest", "params": params}
return SingleRequest("multi", "multipleRequest", "", req)
param = next(iter(request[method]))
method_type = method
req = {
"method": method,
param: request[method][param],
}
return SingleRequest(method_type, method, param, req)
@staticmethod
def _make_snake_name(name: str) -> str:
"""Convert camel or pascal case to snake name."""
sn = "".join(["_" + i.lower() if i.isupper() else i for i in name]).lstrip("_")
return sn
@staticmethod
def _make_smart_camera_single_request(
request: str,
) -> SingleRequest:
"""Make a single request given a method name and no params.
If method like getSomeThing then module will be some_thing.
"""
method = request
method_type = request[:3]
snake_name = SmartCameraProtocol._make_snake_name(request)
param = snake_name[4:]
if (
(short_method := method[:3])
and short_method in {"get", "set"}
and method not in GET_METHODS_AS_DO
):
method_type = short_method
param = snake_name[4:]
else:
method_type = "do"
param = snake_name
req = {"method": method_type, param: {}}
return SingleRequest(method_type, method, param, req)
async def _execute_query(
self, request: str | dict, *, retry_count: int, iterate_list_pages: bool = True
) -> dict:
debug_enabled = _LOGGER.isEnabledFor(logging.DEBUG)
if isinstance(request, dict):
if len(request) == 1:
method = next(iter(request))
if method == "multipleRequest":
params = request["multipleRequest"]
req = {"method": "multipleRequest", "params": params}
elif method[:3] == "set":
params = next(iter(request[method]))
req = {
"method": method[:3],
params: request[method][params],
}
else:
return await self._execute_multiple_query(request, retry_count)
method = next(iter(request))
if len(request) == 1 and method in {"get", "set", "do", "multipleRequest"}:
single_request = self._get_smart_camera_single_request(request)
else:
return await self._execute_multiple_query(request, retry_count)
else:
# If method like getSomeThing then module will be some_thing
method = request
snake_name = "".join(
["_" + i.lower() if i.isupper() else i for i in method]
).lstrip("_")
params = snake_name[4:]
req = {"method": snake_name[:3], params: {}}
single_request = self._make_smart_camera_single_request(request)
smart_request = json_dumps(req)
smart_request = json_dumps(single_request.request)
if debug_enabled:
_LOGGER.debug(
"%s >> %s",
@@ -111,15 +167,29 @@ class SmartCameraProtocol(SmartProtocol):
if "error_code" in response_data:
# H200 does not return an error code
self._handle_response_error_code(response_data, method)
self._handle_response_error_code(response_data, single_request.method_name)
# Requests that are invalid and raise PROTOCOL_FORMAT_ERROR when sent
# as a multipleRequest will return {} when sent as a single request.
if single_request.method_type == "get" and (
not (section := next(iter(response_data))) or response_data[section] == {}
):
raise DeviceError(
f"No results for get request {single_request.method_name}"
)
# TODO need to update handle response lists
if method[:3] == "set":
if single_request.method_type == "do":
return {single_request.method_name: response_data}
if single_request.method_type == "set":
return {}
if method == "multipleRequest":
return {method: response_data["result"]}
return {method: {params: response_data[params]}}
if single_request.method_type == "multi":
return {single_request.method_name: response_data["result"]}
return {
single_request.method_name: {
single_request.param_name: response_data[single_request.param_name]
}
}
class _ChildCameraProtocolWrapper(SmartProtocol):