pyimouapi 1.3.1__tar.gz → 1.3.3__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.3.1 → pyimouapi-1.3.3}/PKG-INFO +4 -3
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/README.md +3 -2
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyimouapi/__init__.py +1 -1
- pyimouapi-1.3.3/pyimouapi/collection_point.py +89 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyimouapi/const.py +42 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyimouapi/device.py +65 -8
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyimouapi/ha_device.py +175 -28
- pyimouapi-1.3.3/pyimouapi/siren.py +15 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyimouapi.egg-info/PKG-INFO +4 -3
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyimouapi.egg-info/SOURCES.txt +7 -1
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyproject.toml +1 -1
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/setup.py +1 -1
- pyimouapi-1.3.3/tests/test_channel_id_and_api_guards.py +97 -0
- pyimouapi-1.3.3/tests/test_collection_point.py +205 -0
- pyimouapi-1.3.3/tests/test_siren.py +272 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/tests/test_update_from_detail.py +3 -5
- pyimouapi-1.3.3/tests/test_write_optimistic_state.py +60 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/LICENSE +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyimouapi/exceptions.py +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyimouapi/openapi.py +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyimouapi/sensor.py +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyimouapi.egg-info/dependency_links.txt +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyimouapi.egg-info/requires.txt +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/pyimouapi.egg-info/top_level.txt +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/setup.cfg +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/tests/test_collect_property_entities.py +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/tests/test_device_summaries.py +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/tests/test_event_identifier_map.py +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/tests/test_lookup_property.py +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/tests/test_message_callback.py +0 -0
- {pyimouapi-1.3.1 → pyimouapi-1.3.3}/tests/test_sensor_normalize.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyimouapi
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
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
|
|
@@ -30,8 +30,9 @@ Async Python client for the **Imou Open Platform** cloud APIs. Built on **aiohtt
|
|
|
30
30
|
| Module | Role |
|
|
31
31
|
|--------|------|
|
|
32
32
|
| `pyimouapi.openapi` | `ImouOpenApiClient` — auth, signing, token lifecycle, HTTP calls |
|
|
33
|
-
| `pyimouapi.device` | `ImouDeviceManager`, `ImouDevice`, `ImouChannel` — listing, PTZ, alarms, storage, and related endpoints |
|
|
34
|
-
| `pyimouapi.
|
|
33
|
+
| `pyimouapi.device` | `ImouDeviceManager`, `ImouDevice`, `ImouChannel` — listing, PTZ, alarms, storage, collection points (`getCollection` / `turnCollection`), and related endpoints |
|
|
34
|
+
| `pyimouapi.collection_point` | Parsing helpers for PaaS / IoT collection point (preset) name lists |
|
|
35
|
+
| `pyimouapi.ha_device` | `ImouHaDeviceManager`, `ImouHaDevice`, … — aggregated “device model” helpers for automation stacks (including `select.collection_point` when the device exposes `CollectionPoint` or IoT refs `21500`/`22000`) |
|
|
35
36
|
| `pyimouapi.exceptions` | `ImouException` and typed errors (connect, request, invalid credentials, …) |
|
|
36
37
|
|
|
37
38
|
The top-level `pyimouapi` package re-exports common symbols. Import submodules directly when needed, for example `from pyimouapi.ha_device import ImouHaDeviceManager`.
|
|
@@ -11,8 +11,9 @@ Async Python client for the **Imou Open Platform** cloud APIs. Built on **aiohtt
|
|
|
11
11
|
| Module | Role |
|
|
12
12
|
|--------|------|
|
|
13
13
|
| `pyimouapi.openapi` | `ImouOpenApiClient` — auth, signing, token lifecycle, HTTP calls |
|
|
14
|
-
| `pyimouapi.device` | `ImouDeviceManager`, `ImouDevice`, `ImouChannel` — listing, PTZ, alarms, storage, and related endpoints |
|
|
15
|
-
| `pyimouapi.
|
|
14
|
+
| `pyimouapi.device` | `ImouDeviceManager`, `ImouDevice`, `ImouChannel` — listing, PTZ, alarms, storage, collection points (`getCollection` / `turnCollection`), and related endpoints |
|
|
15
|
+
| `pyimouapi.collection_point` | Parsing helpers for PaaS / IoT collection point (preset) name lists |
|
|
16
|
+
| `pyimouapi.ha_device` | `ImouHaDeviceManager`, `ImouHaDevice`, … — aggregated “device model” helpers for automation stacks (including `select.collection_point` when the device exposes `CollectionPoint` or IoT refs `21500`/`22000`) |
|
|
16
17
|
| `pyimouapi.exceptions` | `ImouException` and typed errors (connect, request, invalid credentials, …) |
|
|
17
18
|
|
|
18
19
|
The top-level `pyimouapi` package re-exports common symbols. Import submodules directly when needed, for example `from pyimouapi.ha_device import ImouHaDeviceManager`.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""Collection point (PTZ preset) parsing helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from .const import IOT_COLLECTION_NAME_REF, PARAM_COLLECTION_POINT_PROMPT
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def parse_paas_collection_names(data: dict[str, Any]) -> list[str]:
|
|
11
|
+
"""Extract preset names from getCollection PaaS response data."""
|
|
12
|
+
names: list[str] = []
|
|
13
|
+
for item in data.get("collections") or []:
|
|
14
|
+
if not isinstance(item, dict):
|
|
15
|
+
continue
|
|
16
|
+
name = item.get("name")
|
|
17
|
+
if name and str(name) != PARAM_COLLECTION_POINT_PROMPT:
|
|
18
|
+
names.append(str(name))
|
|
19
|
+
return unique_preserve_order(names)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _name_from_collection_item(item: dict[str, Any]) -> str | None:
|
|
23
|
+
for key in ("Name", "name", IOT_COLLECTION_NAME_REF):
|
|
24
|
+
value = item.get(key)
|
|
25
|
+
if value:
|
|
26
|
+
return str(value)
|
|
27
|
+
return None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def parse_iot_collection_names(output_data: Any) -> list[str]:
|
|
31
|
+
"""Extract preset names from GetCollection IoT outputData."""
|
|
32
|
+
names: list[str] = []
|
|
33
|
+
if output_data is None:
|
|
34
|
+
return names
|
|
35
|
+
|
|
36
|
+
if isinstance(output_data, dict):
|
|
37
|
+
for value in output_data.values():
|
|
38
|
+
if isinstance(value, list):
|
|
39
|
+
names.extend(_names_from_collection_items(value))
|
|
40
|
+
elif isinstance(value, str) and value != PARAM_COLLECTION_POINT_PROMPT:
|
|
41
|
+
names.append(value)
|
|
42
|
+
return unique_preserve_order(names)
|
|
43
|
+
|
|
44
|
+
if isinstance(output_data, list):
|
|
45
|
+
if output_data and all(isinstance(item, dict) for item in output_data):
|
|
46
|
+
if any(
|
|
47
|
+
item.get("identifier") == "collection"
|
|
48
|
+
or IOT_COLLECTION_NAME_REF in item
|
|
49
|
+
or "Name" in item
|
|
50
|
+
for item in output_data
|
|
51
|
+
):
|
|
52
|
+
names.extend(_names_from_collection_items(output_data))
|
|
53
|
+
else:
|
|
54
|
+
for item in output_data:
|
|
55
|
+
if not isinstance(item, dict):
|
|
56
|
+
continue
|
|
57
|
+
nested = item.get("value")
|
|
58
|
+
if isinstance(nested, list):
|
|
59
|
+
names.extend(_names_from_collection_items(nested))
|
|
60
|
+
return unique_preserve_order(names)
|
|
61
|
+
|
|
62
|
+
return names
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _names_from_collection_items(items: list[Any]) -> list[str]:
|
|
66
|
+
names: list[str] = []
|
|
67
|
+
for item in items:
|
|
68
|
+
if not isinstance(item, dict):
|
|
69
|
+
continue
|
|
70
|
+
name = _name_from_collection_item(item)
|
|
71
|
+
if name and name != PARAM_COLLECTION_POINT_PROMPT:
|
|
72
|
+
names.append(name)
|
|
73
|
+
return names
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def unique_preserve_order(names: list[str]) -> list[str]:
|
|
77
|
+
seen: set[str] = set()
|
|
78
|
+
ordered: list[str] = []
|
|
79
|
+
for name in names:
|
|
80
|
+
if name in seen:
|
|
81
|
+
continue
|
|
82
|
+
seen.add(name)
|
|
83
|
+
ordered.append(name)
|
|
84
|
+
return ordered
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def build_collection_point_options(names: list[str]) -> list[str]:
|
|
88
|
+
"""Build select options with placeholder first, preserving API order."""
|
|
89
|
+
return [PARAM_COLLECTION_POINT_PROMPT, *unique_preserve_order(names)]
|
|
@@ -23,6 +23,10 @@ API_ENDPOINT_GET_PRODUCT_MODEL = "/openapi/getProductModel"
|
|
|
23
23
|
API_ENDPOINT_GET_IOT_DEVICE_DETAIL_INFO = "/openapi/getIotDeviceDetailInfo"
|
|
24
24
|
API_ENDPOINT_WAKE_UP_DEVICE = "/openapi/wakeUpDevice"
|
|
25
25
|
API_ENDPOINT_SET_MESSAGE_CALLBACK = "/openapi/setMessageCallback"
|
|
26
|
+
API_ENDPOINT_GET_COLLECTION = "/openapi/getCollection"
|
|
27
|
+
API_ENDPOINT_TURN_COLLECTION = "/openapi/turnCollection"
|
|
28
|
+
API_ENDPOINT_SIREN_START = "/openapi/sirenStart"
|
|
29
|
+
API_ENDPOINT_SIREN_STOP = "/openapi/sirenStop"
|
|
26
30
|
|
|
27
31
|
# error_codes
|
|
28
32
|
ERROR_CODE_SUCCESS = "0"
|
|
@@ -109,6 +113,10 @@ PARAM_MOTION_DETECT = "motion_detect"
|
|
|
109
113
|
PARAM_STORAGE_USED = "storage_used"
|
|
110
114
|
PARAM_RESTART_DEVICE = "restart_device"
|
|
111
115
|
PARAM_NIGHT_VISION_MODE = "night_vision_mode"
|
|
116
|
+
PARAM_COLLECTION_POINT = "collection_point"
|
|
117
|
+
PARAM_COLLECTION_POINT_PROMPT = "select_collection_point"
|
|
118
|
+
PARAM_SIREN_START = "siren_start"
|
|
119
|
+
PARAM_SIREN_STOP = "siren_stop"
|
|
112
120
|
PARAM_PTZ = "ptz"
|
|
113
121
|
PARAM_TEMPERATURE_CURRENT = "temperature_current"
|
|
114
122
|
PARAM_HUMIDITY_CURRENT = "humidity_current"
|
|
@@ -128,6 +136,18 @@ PARAM_ABILITY_REFS = "abilityRefs"
|
|
|
128
136
|
PARAM_REF_TYPE = "ref_type"
|
|
129
137
|
PARAM_EXPRESSION = "expression"
|
|
130
138
|
PARAM_OUTPUT_DATA = "outputData"
|
|
139
|
+
PARAM_TURN_REF = "turn_ref"
|
|
140
|
+
PARAM_TURN_INPUT_REF = "turn_input_ref"
|
|
141
|
+
PARAM_INPUT_REF = "input_ref"
|
|
142
|
+
PARAM_COLLECTION_NAME = "name"
|
|
143
|
+
|
|
144
|
+
IOT_GET_COLLECTION_REF = "21500"
|
|
145
|
+
IOT_TURN_COLLECTION_REF = "22000"
|
|
146
|
+
IOT_TURN_COLLECTION_NAME_REF = "22001"
|
|
147
|
+
IOT_COLLECTION_NAME_REF = "21551"
|
|
148
|
+
IOT_SIREN_START_REF = "25500"
|
|
149
|
+
IOT_SIREN_START_INPUT_REF = "25501"
|
|
150
|
+
IOT_SIREN_STOP_REF = "22200"
|
|
131
151
|
PARAM_VALUE_TYPE = "value_type"
|
|
132
152
|
PARAM_ACCESS_TYPE = "accessType"
|
|
133
153
|
PARAM_ABILITY = "ability"
|
|
@@ -294,6 +314,8 @@ SWITCH_TYPE_REF = {
|
|
|
294
314
|
# Required capacity for various button types
|
|
295
315
|
BUTTON_TYPE_ABILITY = {
|
|
296
316
|
"restart_device": ["Reboot"],
|
|
317
|
+
"siren_start": ["Siren"],
|
|
318
|
+
"siren_stop": ["Siren"],
|
|
297
319
|
"ptz_up": ["PT", "PTZ"],
|
|
298
320
|
"ptz_down": ["PT", "PTZ"],
|
|
299
321
|
"ptz_left": ["PT", "PTZ"],
|
|
@@ -331,6 +353,15 @@ BUTTON_TYPE_REF = {
|
|
|
331
353
|
],
|
|
332
354
|
},
|
|
333
355
|
],
|
|
356
|
+
"siren_start": [
|
|
357
|
+
{
|
|
358
|
+
"ref": IOT_SIREN_START_REF,
|
|
359
|
+
"input_ref": IOT_SIREN_START_INPUT_REF,
|
|
360
|
+
},
|
|
361
|
+
],
|
|
362
|
+
"siren_stop": [
|
|
363
|
+
{"ref": IOT_SIREN_STOP_REF},
|
|
364
|
+
],
|
|
334
365
|
"ptz_up": [
|
|
335
366
|
{"ref": "22100"},
|
|
336
367
|
{"ref": "88700"},
|
|
@@ -371,6 +402,7 @@ BUTTON_TYPE_REF = {
|
|
|
371
402
|
# Required capacity for various select types
|
|
372
403
|
SELECT_TYPE_ABILITY = {
|
|
373
404
|
"night_vision_mode": ["NVM"],
|
|
405
|
+
"collection_point": ["CollectionPoint"],
|
|
374
406
|
}
|
|
375
407
|
SELECT_TYPE_REF = {
|
|
376
408
|
"night_vision_mode": [
|
|
@@ -404,6 +436,16 @@ SELECT_TYPE_REF = {
|
|
|
404
436
|
"value_type": "int",
|
|
405
437
|
}
|
|
406
438
|
],
|
|
439
|
+
"collection_point": [
|
|
440
|
+
{
|
|
441
|
+
"ref": IOT_GET_COLLECTION_REF,
|
|
442
|
+
"turn_ref": IOT_TURN_COLLECTION_REF,
|
|
443
|
+
"turn_input_ref": IOT_TURN_COLLECTION_NAME_REF,
|
|
444
|
+
"ref_type": "services",
|
|
445
|
+
"default": PARAM_COLLECTION_POINT_PROMPT,
|
|
446
|
+
"options": [PARAM_COLLECTION_POINT_PROMPT],
|
|
447
|
+
}
|
|
448
|
+
],
|
|
407
449
|
}
|
|
408
450
|
# Required capacity for various sensor types
|
|
409
451
|
SENSOR_TYPE_ABILITY = {
|
|
@@ -10,6 +10,7 @@ from .const import (
|
|
|
10
10
|
API_ENDPOINT_CONTROL_DEVICE_PTZ,
|
|
11
11
|
API_ENDPOINT_DEVICE_SD_CARD_STATUS,
|
|
12
12
|
API_ENDPOINT_DEVICE_STORAGE,
|
|
13
|
+
API_ENDPOINT_GET_COLLECTION,
|
|
13
14
|
API_ENDPOINT_GET_DEVICE_ALARM_PARAM,
|
|
14
15
|
API_ENDPOINT_GET_DEVICE_LIVE_INFO,
|
|
15
16
|
API_ENDPOINT_GET_DEVICE_NIGHT_VISION_MODE,
|
|
@@ -27,6 +28,9 @@ from .const import (
|
|
|
27
28
|
API_ENDPOINT_SET_DEVICE_SNAP,
|
|
28
29
|
API_ENDPOINT_SET_DEVICE_STATUS,
|
|
29
30
|
API_ENDPOINT_SET_IOT_DEVICE_PROPERTIES,
|
|
31
|
+
API_ENDPOINT_SIREN_START,
|
|
32
|
+
API_ENDPOINT_SIREN_STOP,
|
|
33
|
+
API_ENDPOINT_TURN_COLLECTION,
|
|
30
34
|
API_ENDPOINT_WAKE_UP_DEVICE,
|
|
31
35
|
NIGHT_VISION_MODE_MAP,
|
|
32
36
|
PARAM_ABILITY_REFS,
|
|
@@ -39,6 +43,7 @@ from .const import (
|
|
|
39
43
|
PARAM_CHANNEL_NUM,
|
|
40
44
|
PARAM_CHANNEL_STATUS,
|
|
41
45
|
PARAM_CHANNELS,
|
|
46
|
+
PARAM_COLLECTION_NAME,
|
|
42
47
|
PARAM_CONTENT,
|
|
43
48
|
PARAM_COUNT,
|
|
44
49
|
PARAM_DEVICE_ABILITY,
|
|
@@ -206,6 +211,10 @@ class ImouDevice:
|
|
|
206
211
|
def parent_device_id(self) -> str | None:
|
|
207
212
|
return self._parent_device_id
|
|
208
213
|
|
|
214
|
+
@property
|
|
215
|
+
def channel_number(self) -> int:
|
|
216
|
+
return self._channel_number
|
|
217
|
+
|
|
209
218
|
@property
|
|
210
219
|
def is_ipc(self) -> bool:
|
|
211
220
|
return (
|
|
@@ -258,6 +267,10 @@ class ImouDeviceManager:
|
|
|
258
267
|
self._event_map_locks: dict[str, asyncio.Lock] = {}
|
|
259
268
|
self._event_map_locks_guard = asyncio.Lock()
|
|
260
269
|
|
|
270
|
+
async def async_close(self) -> None:
|
|
271
|
+
"""Close the underlying Open API HTTP session."""
|
|
272
|
+
await self._imou_api_client.async_close()
|
|
273
|
+
|
|
261
274
|
async def _async_lock_for_product(self, product_id: str) -> asyncio.Lock:
|
|
262
275
|
async with self._event_map_locks_guard:
|
|
263
276
|
lock = self._event_map_locks.get(product_id)
|
|
@@ -555,11 +568,13 @@ class ImouDeviceManager:
|
|
|
555
568
|
}
|
|
556
569
|
]
|
|
557
570
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
571
|
+
data = await self._imou_api_client.async_request_api(
|
|
572
|
+
API_ENDPOINT_GET_IOT_DEVICE_PROPERTIES, params
|
|
573
|
+
)
|
|
574
|
+
device_list = data.get(PARAM_DEVICE_LIST) if isinstance(data, dict) else None
|
|
575
|
+
if not device_list:
|
|
576
|
+
return {}
|
|
577
|
+
return device_list[0]
|
|
563
578
|
|
|
564
579
|
async def async_set_iot_device_properties(
|
|
565
580
|
self, device_id: str, channel_id: str | None, product_id: str, properties: dict
|
|
@@ -635,6 +650,45 @@ class ImouDeviceManager:
|
|
|
635
650
|
API_ENDPOINT_GET_IOT_DEVICE_DETAIL_INFO, params
|
|
636
651
|
)
|
|
637
652
|
|
|
653
|
+
async def async_get_device_collection(
|
|
654
|
+
self, device_id: str, channel_id: str
|
|
655
|
+
) -> dict[str, Any]:
|
|
656
|
+
params = {
|
|
657
|
+
PARAM_DEVICE_ID: device_id,
|
|
658
|
+
PARAM_CHANNEL_ID: channel_id,
|
|
659
|
+
}
|
|
660
|
+
return await self._imou_api_client.async_request_api(
|
|
661
|
+
API_ENDPOINT_GET_COLLECTION, params
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
async def async_turn_device_collection(
|
|
665
|
+
self, device_id: str, channel_id: str, name: str
|
|
666
|
+
) -> None:
|
|
667
|
+
params = {
|
|
668
|
+
PARAM_DEVICE_ID: device_id,
|
|
669
|
+
PARAM_CHANNEL_ID: channel_id,
|
|
670
|
+
PARAM_COLLECTION_NAME: name,
|
|
671
|
+
}
|
|
672
|
+
await self._imou_api_client.async_request_api(
|
|
673
|
+
API_ENDPOINT_TURN_COLLECTION, params
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
async def async_siren_start(
|
|
677
|
+
self, device_id: str, channels: list[int] | None = None
|
|
678
|
+
) -> None:
|
|
679
|
+
params: dict[str, Any] = {PARAM_DEVICE_ID: device_id}
|
|
680
|
+
if channels is not None:
|
|
681
|
+
params[PARAM_CHANNELS] = channels
|
|
682
|
+
await self._imou_api_client.async_request_api(API_ENDPOINT_SIREN_START, params)
|
|
683
|
+
|
|
684
|
+
async def async_siren_stop(
|
|
685
|
+
self, device_id: str, channels: list[int] | None = None
|
|
686
|
+
) -> None:
|
|
687
|
+
params: dict[str, Any] = {PARAM_DEVICE_ID: device_id}
|
|
688
|
+
if channels is not None:
|
|
689
|
+
params[PARAM_CHANNELS] = channels
|
|
690
|
+
await self._imou_api_client.async_request_api(API_ENDPOINT_SIREN_STOP, params)
|
|
691
|
+
|
|
638
692
|
async def _async_update_device_ability_refs(self, imou_device: ImouDevice) -> None:
|
|
639
693
|
device_id = imou_device.device_id
|
|
640
694
|
if imou_device.parent_product_id is not None:
|
|
@@ -655,12 +709,15 @@ class ImouDeviceManager:
|
|
|
655
709
|
if device_detail.get(PARAM_CHANNELS) and imou_device.channels:
|
|
656
710
|
channels_detail = device_detail.get(PARAM_CHANNELS)
|
|
657
711
|
channel_detail_map = {
|
|
658
|
-
channel_detail.get(PARAM_CHANNEL_ID): channel_detail.get(
|
|
712
|
+
str(channel_detail.get(PARAM_CHANNEL_ID)): channel_detail.get(
|
|
659
713
|
PARAM_ABILITY_REFS, "unknown"
|
|
660
714
|
)
|
|
661
715
|
for channel_detail in channels_detail
|
|
716
|
+
if channel_detail.get(PARAM_CHANNEL_ID) is not None
|
|
662
717
|
}
|
|
663
718
|
for channel in imou_device.channels:
|
|
664
|
-
channel_id =
|
|
665
|
-
|
|
719
|
+
channel_id = (
|
|
720
|
+
str(channel.channel_id) if channel.channel_id is not None else None
|
|
721
|
+
)
|
|
722
|
+
if channel_id is not None and channel_id in channel_detail_map:
|
|
666
723
|
channel.set_channel_ability_refs(channel_detail_map[channel_id])
|