pyimouapi 1.2.4__tar.gz → 1.2.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/PKG-INFO +1 -1
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/pyimouapi/__init__.py +2 -0
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/pyimouapi/device.py +28 -22
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/pyimouapi/exceptions.py +4 -2
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/pyimouapi/ha_device.py +5 -6
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/pyimouapi/openapi.py +39 -20
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/pyimouapi.egg-info/PKG-INFO +1 -1
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/pyimouapi.egg-info/SOURCES.txt +1 -0
- pyimouapi-1.2.5/pyproject.toml +3 -0
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/setup.py +1 -1
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/LICENSE +0 -0
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/README.md +0 -0
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/pyimouapi/const.py +0 -0
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/pyimouapi.egg-info/dependency_links.txt +0 -0
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/pyimouapi.egg-info/requires.txt +0 -0
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/pyimouapi.egg-info/top_level.txt +0 -0
- {pyimouapi-1.2.4 → pyimouapi-1.2.5}/setup.cfg +0 -0
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
1
5
|
from .const import (
|
|
2
6
|
API_ENDPOINT_LIST_DEVICE_DETAILS,
|
|
3
7
|
PARAM_PAGE_SIZE,
|
|
@@ -137,7 +141,7 @@ class ImouDevice:
|
|
|
137
141
|
return self._device_status
|
|
138
142
|
|
|
139
143
|
@property
|
|
140
|
-
def channels(self) -> []:
|
|
144
|
+
def channels(self) -> list[ImouChannel]:
|
|
141
145
|
return self._channels
|
|
142
146
|
|
|
143
147
|
@property
|
|
@@ -157,15 +161,15 @@ class ImouDevice:
|
|
|
157
161
|
return self._device_version
|
|
158
162
|
|
|
159
163
|
@property
|
|
160
|
-
def product_id(self) -> str:
|
|
164
|
+
def product_id(self) -> str | None:
|
|
161
165
|
return self._product_id
|
|
162
166
|
|
|
163
167
|
@property
|
|
164
|
-
def parent_product_id(self) -> str:
|
|
168
|
+
def parent_product_id(self) -> str | None:
|
|
165
169
|
return self._parent_product_id
|
|
166
170
|
|
|
167
171
|
@property
|
|
168
|
-
def parent_device_id(self) -> str:
|
|
172
|
+
def parent_device_id(self) -> str | None:
|
|
169
173
|
return self._parent_device_id
|
|
170
174
|
|
|
171
175
|
@property
|
|
@@ -185,7 +189,7 @@ class ImouDevice:
|
|
|
185
189
|
def set_product_id(self, product_id: str) -> None:
|
|
186
190
|
self._product_id = product_id
|
|
187
191
|
|
|
188
|
-
def set_channels(self, channels: []) -> None:
|
|
192
|
+
def set_channels(self, channels: list[ImouChannel]) -> None:
|
|
189
193
|
self._channels = channels
|
|
190
194
|
|
|
191
195
|
def set_channel_number(self, channel_number: int):
|
|
@@ -311,7 +315,7 @@ class ImouDeviceManager:
|
|
|
311
315
|
|
|
312
316
|
async def async_get_device_status(
|
|
313
317
|
self, device_id: str, channel_id: str, enable_type: str
|
|
314
|
-
) -> dict[
|
|
318
|
+
) -> dict[str, Any]:
|
|
315
319
|
"""obtain device capability switch status"""
|
|
316
320
|
params = {
|
|
317
321
|
PARAM_DEVICE_ID: device_id,
|
|
@@ -322,7 +326,7 @@ class ImouDeviceManager:
|
|
|
322
326
|
API_ENDPOINT_GET_DEVICE_STATUS, params
|
|
323
327
|
)
|
|
324
328
|
|
|
325
|
-
async def async_get_device_online_status(self, device_id: str) -> dict[
|
|
329
|
+
async def async_get_device_online_status(self, device_id: str) -> dict[str, Any]:
|
|
326
330
|
"""GET DEVICE ONLINE STATUS"""
|
|
327
331
|
params = {
|
|
328
332
|
PARAM_DEVICE_ID: device_id,
|
|
@@ -346,7 +350,7 @@ class ImouDeviceManager:
|
|
|
346
350
|
|
|
347
351
|
async def async_get_device_night_vision_mode(
|
|
348
352
|
self, device_id: str, channel_id: str
|
|
349
|
-
) -> dict[
|
|
353
|
+
) -> dict[str, Any]:
|
|
350
354
|
"""obtain device night vision mode"""
|
|
351
355
|
params = {
|
|
352
356
|
PARAM_DEVICE_ID: device_id,
|
|
@@ -371,7 +375,7 @@ class ImouDeviceManager:
|
|
|
371
375
|
API_ENDPOINT_SET_DEVICE_NIGHT_VISION_MODE, params
|
|
372
376
|
)
|
|
373
377
|
|
|
374
|
-
async def async_get_device_storage(self, device_id: str) -> dict[
|
|
378
|
+
async def async_get_device_storage(self, device_id: str) -> dict[str, Any]:
|
|
375
379
|
"""obtain device storage media capacity information"""
|
|
376
380
|
params = {PARAM_DEVICE_ID: device_id}
|
|
377
381
|
return await self._imou_api_client.async_request_api(
|
|
@@ -387,7 +391,7 @@ class ImouDeviceManager:
|
|
|
387
391
|
|
|
388
392
|
async def async_get_stream_url(
|
|
389
393
|
self, device_id: str, channel_id: str
|
|
390
|
-
) -> dict[
|
|
394
|
+
) -> dict[str, Any]:
|
|
391
395
|
"""obtain the hls stream address of the device"""
|
|
392
396
|
params = {PARAM_DEVICE_ID: device_id, PARAM_CHANNEL_ID: channel_id}
|
|
393
397
|
return await self._imou_api_client.async_request_api(
|
|
@@ -396,7 +400,7 @@ class ImouDeviceManager:
|
|
|
396
400
|
|
|
397
401
|
async def async_get_device_snap(
|
|
398
402
|
self, device_id: str, channel_id: str
|
|
399
|
-
) -> dict[
|
|
403
|
+
) -> dict[str, Any]:
|
|
400
404
|
params = {PARAM_DEVICE_ID: device_id, PARAM_CHANNEL_ID: channel_id}
|
|
401
405
|
return await self._imou_api_client.async_request_api(
|
|
402
406
|
API_ENDPOINT_SET_DEVICE_SNAP, params
|
|
@@ -404,7 +408,7 @@ class ImouDeviceManager:
|
|
|
404
408
|
|
|
405
409
|
async def async_create_stream_url(
|
|
406
410
|
self, device_id: str, channel_id: str, stream_id: int = 0
|
|
407
|
-
) -> dict[
|
|
411
|
+
) -> dict[str, Any]:
|
|
408
412
|
"""create device hls stream address"""
|
|
409
413
|
params = {
|
|
410
414
|
PARAM_DEVICE_ID: device_id,
|
|
@@ -425,8 +429,12 @@ class ImouDeviceManager:
|
|
|
425
429
|
)
|
|
426
430
|
|
|
427
431
|
async def async_get_iot_device_properties(
|
|
428
|
-
self,
|
|
429
|
-
|
|
432
|
+
self,
|
|
433
|
+
device_id: str,
|
|
434
|
+
channel_id: str | None,
|
|
435
|
+
product_id: str,
|
|
436
|
+
properties: list[Any],
|
|
437
|
+
) -> dict[str, Any]:
|
|
430
438
|
params = {
|
|
431
439
|
PARAM_DEVICE_LIST: [
|
|
432
440
|
{
|
|
@@ -462,15 +470,15 @@ class ImouDeviceManager:
|
|
|
462
470
|
API_ENDPOINT_SET_IOT_DEVICE_PROPERTIES, params
|
|
463
471
|
)
|
|
464
472
|
|
|
465
|
-
async def async_get_device_sd_card_status(self, device_id: str) -> dict[
|
|
473
|
+
async def async_get_device_sd_card_status(self, device_id: str) -> dict[str, Any]:
|
|
466
474
|
params = {PARAM_DEVICE_ID: device_id}
|
|
467
475
|
return await self._imou_api_client.async_request_api(
|
|
468
476
|
API_ENDPOINT_DEVICE_SD_CARD_STATUS, params
|
|
469
477
|
)
|
|
470
478
|
|
|
471
479
|
async def async_iot_device_control(
|
|
472
|
-
self, device_id: str, product_id: str, ref: str, content: dict
|
|
473
|
-
) -> dict[str,
|
|
480
|
+
self, device_id: str, product_id: str, ref: str, content: dict[str, Any]
|
|
481
|
+
) -> dict[str, Any]:
|
|
474
482
|
params = {
|
|
475
483
|
PARAM_DEVICE_ID: device_id,
|
|
476
484
|
PARAM_PRODUCT_ID: product_id,
|
|
@@ -481,7 +489,7 @@ class ImouDeviceManager:
|
|
|
481
489
|
API_ENDPOINT_IOT_DEVICE_CONTROL, params
|
|
482
490
|
)
|
|
483
491
|
|
|
484
|
-
async def async_get_device_power_info(self, device_id: str) -> dict[
|
|
492
|
+
async def async_get_device_power_info(self, device_id: str) -> dict[str, Any]:
|
|
485
493
|
params = {
|
|
486
494
|
PARAM_DEVICE_ID: device_id,
|
|
487
495
|
}
|
|
@@ -498,9 +506,7 @@ class ImouDeviceManager:
|
|
|
498
506
|
API_ENDPOINT_WAKE_UP_DEVICE, params
|
|
499
507
|
)
|
|
500
508
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
async def async_get_product_model(self, product_id: str) -> dict[any, any]:
|
|
509
|
+
async def async_get_product_model(self, product_id: str) -> dict[str, Any]:
|
|
504
510
|
params = {
|
|
505
511
|
PARAM_PRODUCT_ID: product_id,
|
|
506
512
|
}
|
|
@@ -510,7 +516,7 @@ class ImouDeviceManager:
|
|
|
510
516
|
|
|
511
517
|
async def async_get_iot_device_detail_info(
|
|
512
518
|
self, device_id: str, product_id: str
|
|
513
|
-
) -> dict[
|
|
519
|
+
) -> dict[str, Any]:
|
|
514
520
|
params = {
|
|
515
521
|
PARAM_DEVICE_ID: device_id,
|
|
516
522
|
PARAM_PRODUCT_ID: product_id,
|
|
@@ -14,8 +14,10 @@ class ImouException(Exception):
|
|
|
14
14
|
|
|
15
15
|
def traceback(self) -> str:
|
|
16
16
|
"""Return the traceback as a string."""
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
exc_info = sys.exc_info()
|
|
18
|
+
if exc_info[0] is None:
|
|
19
|
+
return ""
|
|
20
|
+
return "".join(traceback.format_exception(*exc_info))
|
|
19
21
|
|
|
20
22
|
def get_title(self) -> str:
|
|
21
23
|
"""Return the title of the exception which will be then translated."""
|
|
@@ -323,10 +323,8 @@ class ImouHaDeviceManager(object):
|
|
|
323
323
|
return await self._async_get_device_exist_stream(
|
|
324
324
|
device, live_resolution, live_protocol
|
|
325
325
|
)
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
else:
|
|
329
|
-
raise exception
|
|
326
|
+
raise ex
|
|
327
|
+
raise exception
|
|
330
328
|
|
|
331
329
|
async def _async_get_device_exist_stream(
|
|
332
330
|
self, device: ImouHaDevice, resolution: str, protocol: str
|
|
@@ -352,8 +350,9 @@ class ImouHaDeviceManager(object):
|
|
|
352
350
|
_LOGGER.debug(f"wait {wait_seconds} seconds to download a picture")
|
|
353
351
|
await asyncio.sleep(wait_seconds)
|
|
354
352
|
try:
|
|
355
|
-
|
|
356
|
-
|
|
353
|
+
timeout = aiohttp.ClientTimeout(total=120)
|
|
354
|
+
async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
355
|
+
response = await session.get(data[PARAM_URL])
|
|
357
356
|
if response.status != 200:
|
|
358
357
|
raise RequestFailedException(
|
|
359
358
|
f"request failed,status code {response.status}"
|
|
@@ -5,8 +5,10 @@ import logging
|
|
|
5
5
|
import secrets
|
|
6
6
|
import time
|
|
7
7
|
import uuid
|
|
8
|
+
from typing import Any
|
|
8
9
|
|
|
9
10
|
import aiohttp
|
|
11
|
+
from urllib.parse import urlparse
|
|
10
12
|
|
|
11
13
|
from .const import (
|
|
12
14
|
API_ENDPOINT_ACCESS_TOKEN,
|
|
@@ -40,28 +42,49 @@ _LOGGER: logging.Logger = logging.getLogger(__package__)
|
|
|
40
42
|
|
|
41
43
|
|
|
42
44
|
class ImouOpenApiClient:
|
|
45
|
+
"""Async client for Imou Open Platform HTTP API."""
|
|
46
|
+
|
|
43
47
|
def __init__(self, app_id: str, app_secret: str, api_url: str) -> None:
|
|
44
48
|
self._app_id = app_id
|
|
45
49
|
self._app_secret = app_secret
|
|
46
50
|
self._api_url = api_url
|
|
47
|
-
|
|
48
|
-
self.
|
|
51
|
+
self._access_token: str | None = None
|
|
52
|
+
self._session: aiohttp.ClientSession | None = None
|
|
53
|
+
|
|
54
|
+
async def _async_get_session(self) -> aiohttp.ClientSession:
|
|
55
|
+
if self._session is None or self._session.closed:
|
|
56
|
+
self._session = aiohttp.ClientSession(
|
|
57
|
+
headers={"Client-Type": "HomeAssistant"},
|
|
58
|
+
)
|
|
59
|
+
return self._session
|
|
60
|
+
|
|
61
|
+
async def async_close(self) -> None:
|
|
62
|
+
"""Close the HTTP session (call when done with the client)."""
|
|
63
|
+
if self._session is not None and not self._session.closed:
|
|
64
|
+
await self._session.close()
|
|
65
|
+
self._session = None
|
|
49
66
|
|
|
50
67
|
async def async_get_token(self) -> None:
|
|
51
|
-
"""
|
|
68
|
+
"""Fetch and store accessToken."""
|
|
52
69
|
response = await self.async_request_api(API_ENDPOINT_ACCESS_TOKEN, {})
|
|
53
70
|
self._access_token = response[PARAM_ACCESS_TOKEN]
|
|
54
71
|
if PARAM_CURRENT_DOMAIN in response:
|
|
55
|
-
|
|
72
|
+
raw = response[PARAM_CURRENT_DOMAIN]
|
|
73
|
+
if "://" not in raw:
|
|
74
|
+
raw = f"https://{raw}"
|
|
75
|
+
parsed = urlparse(raw)
|
|
76
|
+
if parsed.netloc:
|
|
77
|
+
self._api_url = parsed.netloc
|
|
56
78
|
|
|
57
79
|
async def async_request_api(
|
|
58
|
-
self, endpoint: str, params: dict[
|
|
59
|
-
) -> dict[
|
|
60
|
-
|
|
80
|
+
self, endpoint: str, params: dict[str, Any] | None = None
|
|
81
|
+
) -> dict[str, Any]:
|
|
82
|
+
"""POST to an API endpoint; returns the result data object."""
|
|
83
|
+
payload = dict(params) if params else {}
|
|
61
84
|
if self._access_token is None and endpoint != API_ENDPOINT_ACCESS_TOKEN:
|
|
62
85
|
await self.async_get_token()
|
|
63
86
|
if endpoint != API_ENDPOINT_ACCESS_TOKEN:
|
|
64
|
-
|
|
87
|
+
payload[PARAM_TOKEN] = self._access_token
|
|
65
88
|
timestamp = round(time.time())
|
|
66
89
|
nonce = secrets.token_urlsafe()
|
|
67
90
|
sign = hashlib.md5(
|
|
@@ -70,7 +93,7 @@ class ImouOpenApiClient:
|
|
|
70
93
|
)
|
|
71
94
|
).hexdigest()
|
|
72
95
|
request_id = str(uuid.uuid4())
|
|
73
|
-
headers = {"Content-Type": "application/json"
|
|
96
|
+
headers = {"Content-Type": "application/json"}
|
|
74
97
|
body = {
|
|
75
98
|
PARAM_SYSTEM: {
|
|
76
99
|
PARAM_VER: "1.0",
|
|
@@ -79,20 +102,16 @@ class ImouOpenApiClient:
|
|
|
79
102
|
PARAM_TIME: timestamp,
|
|
80
103
|
PARAM_NONCE: nonce,
|
|
81
104
|
},
|
|
82
|
-
PARAM_PARAMS:
|
|
105
|
+
PARAM_PARAMS: payload,
|
|
83
106
|
PARAM_ID: request_id,
|
|
84
107
|
}
|
|
85
108
|
url = f"https://{self._api_url}{endpoint}"
|
|
109
|
+
session = await self._async_get_session()
|
|
86
110
|
try:
|
|
87
|
-
async with
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
)
|
|
92
|
-
response_body = json.loads(await response.text())
|
|
93
|
-
_LOGGER.debug(
|
|
94
|
-
f"url: {url} request body: {body} response: {response_body}"
|
|
95
|
-
)
|
|
111
|
+
async with asyncio.timeout(30):
|
|
112
|
+
response = await session.request("POST", url, json=body, headers=headers)
|
|
113
|
+
response_body = json.loads(await response.text())
|
|
114
|
+
_LOGGER.debug("url: %s request body: %s response: %s", url, body, response_body)
|
|
96
115
|
except Exception as exception:
|
|
97
116
|
raise ConnectFailedException(f"connect failed,{exception}") from exception
|
|
98
117
|
if response.status != 200:
|
|
@@ -117,5 +136,5 @@ class ImouOpenApiClient:
|
|
|
117
136
|
return response_data
|
|
118
137
|
|
|
119
138
|
@property
|
|
120
|
-
def access_token(self):
|
|
139
|
+
def access_token(self) -> str | None:
|
|
121
140
|
return self._access_token
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|