pyimouapi 1.2.3__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyimouapi
3
- Version: 1.2.3
3
+ Version: 1.2.5
4
4
  Summary: A package for imou open api
5
5
  Home-page: https://github.com/Imou-OpenPlatform/Py-Imou-Open-Api
6
6
  Author: Imou-OpenPlatform
@@ -10,10 +10,10 @@ Classifier: Intended Audience :: Developers
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Topic :: Software Development :: Libraries
13
+ Requires-Python: >=3.11
13
14
  Description-Content-Type: text/markdown
14
15
  License-File: LICENSE
15
16
  Requires-Dist: aiohttp<4.0,>=3.11.9
16
- Requires-Dist: async-timeout>=4.0
17
17
  Requires-Dist: simpleeval>=1.0.3
18
18
  Dynamic: author
19
19
  Dynamic: author-email
@@ -24,6 +24,7 @@ Dynamic: home-page
24
24
  Dynamic: license
25
25
  Dynamic: license-file
26
26
  Dynamic: requires-dist
27
+ Dynamic: requires-python
27
28
  Dynamic: summary
28
29
 
29
30
  ## 打包
@@ -1,3 +1,5 @@
1
+ __version__ = "1.2.5"
2
+
1
3
  from .device import ImouDeviceManager, ImouDevice, ImouChannel
2
4
  from .exceptions import (
3
5
  ConnectFailedException,
@@ -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[any, any]:
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[any, any]:
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[any, any]:
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[any, any]:
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[any, any]:
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[any, any]:
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[any, any]:
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, device_id: str, channel_id: str | None, product_id: str, properties: []
429
- ) -> dict[any, any]:
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[any, any]:
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, any]:
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[any, any]:
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[any, any]:
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
- etype, value, trace = sys.exc_info()
18
- return "".join(traceback.format_exception(etype, value, trace, None))
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
- else:
327
- raise exception
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
- async with aiohttp.ClientSession() as session:
356
- response = await session.request("GET", data[PARAM_URL])
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}"
@@ -1,12 +1,14 @@
1
+ import asyncio
1
2
  import hashlib
2
3
  import json
3
4
  import logging
4
5
  import secrets
5
6
  import time
6
7
  import uuid
8
+ from typing import Any
7
9
 
8
10
  import aiohttp
9
- import async_timeout
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
- # token
48
- self._access_token = None
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
- """get accessToken"""
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
- self._api_url = response[PARAM_CURRENT_DOMAIN].split("://")[1]
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[any, any] = None
59
- ) -> dict[any, any]:
60
- # if accessToken is None , get first
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
- params[PARAM_TOKEN] = self._access_token
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", "Client-Type": "HomeAssistant"}
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: 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 aiohttp.ClientSession() as session:
88
- async with async_timeout.timeout(30):
89
- response = await session.request(
90
- "POST", url, json=body, headers=headers
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyimouapi
3
- Version: 1.2.3
3
+ Version: 1.2.5
4
4
  Summary: A package for imou open api
5
5
  Home-page: https://github.com/Imou-OpenPlatform/Py-Imou-Open-Api
6
6
  Author: Imou-OpenPlatform
@@ -10,10 +10,10 @@ Classifier: Intended Audience :: Developers
10
10
  Classifier: Operating System :: OS Independent
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Topic :: Software Development :: Libraries
13
+ Requires-Python: >=3.11
13
14
  Description-Content-Type: text/markdown
14
15
  License-File: LICENSE
15
16
  Requires-Dist: aiohttp<4.0,>=3.11.9
16
- Requires-Dist: async-timeout>=4.0
17
17
  Requires-Dist: simpleeval>=1.0.3
18
18
  Dynamic: author
19
19
  Dynamic: author-email
@@ -24,6 +24,7 @@ Dynamic: home-page
24
24
  Dynamic: license
25
25
  Dynamic: license-file
26
26
  Dynamic: requires-dist
27
+ Dynamic: requires-python
27
28
  Dynamic: summary
28
29
 
29
30
  ## 打包
@@ -1,5 +1,6 @@
1
1
  LICENSE
2
2
  README.md
3
+ pyproject.toml
3
4
  setup.py
4
5
  pyimouapi/__init__.py
5
6
  pyimouapi/const.py
@@ -1,3 +1,2 @@
1
1
  aiohttp<4.0,>=3.11.9
2
- async-timeout>=4.0
3
2
  simpleeval>=1.0.3
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61", "wheel"]
3
+ build-backend = "setuptools.build_meta"
@@ -2,11 +2,11 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name="pyimouapi",
5
- version="1.2.3",
5
+ version="1.2.5",
6
6
  packages=find_packages(),
7
+ python_requires=">=3.11",
7
8
  install_requires=[
8
9
  "aiohttp>=3.11.9,<4.0",
9
- "async-timeout>=4.0",
10
10
  "simpleeval>=1.0.3",
11
11
  ],
12
12
  description="A package for imou open api",
File without changes
File without changes
File without changes
File without changes