pyimouapi 1.0.2__tar.gz → 1.0.4__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.1
2
2
  Name: pyimouapi
3
- Version: 1.0.2
3
+ Version: 1.0.4
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
@@ -12,11 +12,9 @@ Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Topic :: Software Development :: Libraries
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: aiohttp==3.11.0
16
- Requires-Dist: async-timeout==5.0.1
17
-
18
- # Py-Imou-Open-Api
19
-
20
- imou open platform api SDK for python
21
15
 
16
+ ## 打包
17
+ python setup.py sdist bdist_wheel
22
18
 
19
+ ## 上传
20
+ twine upload dist/*
@@ -0,0 +1,5 @@
1
+ ## 打包
2
+ python setup.py sdist bdist_wheel
3
+
4
+ ## 上传
5
+ twine upload dist/*
@@ -67,3 +67,65 @@ PARAM_STREAM_ID = "streamId"
67
67
  PARAM_OPERATION = "operation"
68
68
  PARAM_DURATION = "duration"
69
69
  PARAM_PROPERTIES = "properties"
70
+ PARAM_API_URL = "api_url"
71
+ PARAM_MOTION_DETECT = "motion_detect"
72
+ PARAM_MOBILE_DETECT = "mobile_detect"
73
+ PARAM_STATUS = "status"
74
+ PARAM_STORAGE_USED = "storage_used"
75
+ PARAM_NIGHT_VISION_MODE = "night_vision_mode"
76
+ PARAM_CURRENT_OPTION = "current_option"
77
+ PARAM_MODES = "modes"
78
+ PARAM_OPTIONS = "options"
79
+ PARAM_CHANNELS = "channels"
80
+ PARAM_USED_BYTES = "usedBytes"
81
+ PARAM_TOTAL_BYTES = "totalBytes"
82
+ PARAM_STREAMS = "streams"
83
+ PARAM_HLS = "hls"
84
+ PARAM_RESTART_DEVICE = "restart_device"
85
+ PARAM_URL = "url"
86
+ PARAM_CLOSE_CAMERA = "close_camera"
87
+ PARAM_WHITE_LIGHT = "white_light"
88
+ PARAM_AB_ALARM_SOUND = "ab_alarm_sound"
89
+ PARAM_AUDIO_ENCODE_CONTROL = "audio_encode_control"
90
+
91
+ # Configuration definitions
92
+ CONF_API_URL_SG = "openapi-sg.easy4ip.com"
93
+ CONF_API_URL_OR = "openapi-or.easy4ip.com"
94
+ CONF_API_URL_FK = "openapi-fk.easy4ip.com"
95
+ CONF_CLOSE_CAMERA = "CloseCamera"
96
+ CONF_WHITE_LIGHT = "WhiteLight"
97
+ CONF_AB_ALARM_SOUND = "AbAlarmSound"
98
+ CONF_AUDIO_ENCODE_CONTROL = "AudioEncodeControl"
99
+ CONF_NVM = "NVM"
100
+ CONF_PT = "PT"
101
+
102
+ PLATFORMS = [
103
+ "select",
104
+ "sensor",
105
+ "switch",
106
+ "camera",
107
+ "button"
108
+ ]
109
+
110
+ SWITCH_TYPE_ABILITY = {
111
+ "close_camera": "CloseCamera",
112
+ "white_light": "WhiteLight",
113
+ "audio_encode_control": "AudioEncodeControl",
114
+ "ab_alarm_sound": "AbAlarmSound"
115
+ }
116
+
117
+ SWITCH_TYPE_ENABLE = {
118
+ "motion_detect": ["motionDetect", "mobileDetect"],
119
+ "close_camera": ["closeCamera"],
120
+ "white_light": ["whiteLight"],
121
+ "audio_encode_control": ["audioEncodeControl"],
122
+ "ab_alarm_sound": ["abAlarmSound"]
123
+ }
124
+
125
+ BUTTON_TYPE_PARAM_VALUE = {
126
+ "ptz_up": 0,
127
+ "ptz_down": 1,
128
+ "ptz_left": 2,
129
+ "ptz_right": 3
130
+ }
131
+
@@ -0,0 +1,313 @@
1
+ import asyncio
2
+ import logging
3
+ import re
4
+ from enum import Enum
5
+
6
+ import aiohttp
7
+
8
+ from .const import BUTTON_TYPE_PARAM_VALUE, SWITCH_TYPE_ENABLE, PARAM_MOTION_DETECT, PARAM_STATUS, PARAM_STORAGE_USED, \
9
+ PARAM_NIGHT_VISION_MODE, PARAM_MODE, PARAM_CURRENT_OPTION, PARAM_MODES, PARAM_OPTIONS, PARAM_CHANNELS, \
10
+ PARAM_CHANNEL_ID, PARAM_USED_BYTES, PARAM_TOTAL_BYTES, PARAM_STREAMS, PARAM_HLS, PARAM_RESTART_DEVICE, PARAM_URL, \
11
+ PARAM_CLOSE_CAMERA, PARAM_WHITE_LIGHT, PARAM_AB_ALARM_SOUND, PARAM_AUDIO_ENCODE_CONTROL, CONF_CLOSE_CAMERA, \
12
+ CONF_WHITE_LIGHT, CONF_AB_ALARM_SOUND, CONF_AUDIO_ENCODE_CONTROL, CONF_NVM, PARAM_STREAM_ID, CONF_PT
13
+ from .exceptions import RequestFailedException
14
+ from .device import ImouDeviceManager
15
+
16
+ _LOGGER: logging.Logger = logging.getLogger(__package__)
17
+
18
+ PATTERN = re.compile(r'^\d+$')
19
+
20
+
21
+ class ImouHaDevice(object):
22
+ def __init__(self, device_id: str, channel_id: str, channel_name: str,
23
+ manufacturer: str, model: str, swversion: str, product_id: str):
24
+ self._device_id = device_id
25
+ self._channel_id = str(channel_id) if isinstance(channel_id, int) else channel_id
26
+ self._channel_name = channel_name
27
+ self._manufacturer = manufacturer
28
+ self._model = model
29
+ self._swversion = swversion
30
+ self._switches = {
31
+ PARAM_MOTION_DETECT: False
32
+ }
33
+ self._sensors = {
34
+ PARAM_STATUS: DeviceStatus.OFFLINE.status,
35
+ PARAM_STORAGE_USED: "Abnormal"
36
+ }
37
+
38
+ self._selects = {
39
+
40
+ }
41
+ self._buttons = ["restart_device"]
42
+ self._product_id = product_id
43
+
44
+ @property
45
+ def device_id(self):
46
+ return self._device_id
47
+
48
+ @property
49
+ def channel_id(self):
50
+ return self._channel_id
51
+
52
+ @property
53
+ def channel_name(self):
54
+ return self._channel_name
55
+
56
+ @property
57
+ def manufacturer(self):
58
+ return self._manufacturer
59
+
60
+ @property
61
+ def model(self):
62
+ return self._model
63
+
64
+ @property
65
+ def swversion(self):
66
+ return self._swversion
67
+
68
+ @property
69
+ def switches(self):
70
+ return self._switches
71
+
72
+ @property
73
+ def sensors(self):
74
+ return self._sensors
75
+
76
+ @property
77
+ def selects(self):
78
+ return self._selects
79
+
80
+ @property
81
+ def buttons(self):
82
+ return self._buttons
83
+
84
+ @property
85
+ def product_id(self) -> str:
86
+ return self._product_id
87
+
88
+ def set_product_id(self, product_id: str) -> None:
89
+ self._product_id = product_id
90
+
91
+ def __str__(self):
92
+ return f"{self.device_id} {self.channel_id} {self.channel_name} {self.manufacturer} {self.model} {self.product_id} {self.swversion} {self.switches} {self.sensors} {self._selects}"
93
+
94
+
95
+ def get_device_status(origin_value: str):
96
+ try:
97
+ for status in DeviceStatus:
98
+ if status.origin_value == origin_value:
99
+ return status.status
100
+ return DeviceStatus.OFFLINE.status
101
+ except Exception as e:
102
+ _LOGGER.info(f"An error occurred: {e}")
103
+ return DeviceStatus.OFFLINE.status
104
+
105
+
106
+ class ImouHaDeviceManager(object):
107
+ def __init__(self, device_manager: ImouDeviceManager):
108
+ self._device_manager = device_manager
109
+
110
+ @property
111
+ def device_manager(self):
112
+ return self._device_manager
113
+
114
+ async def async_update_device_status(self, device: ImouHaDevice):
115
+ """Update device status, with the updater calling every time the coordinator is updated"""
116
+ await asyncio.gather(
117
+ self.async_update_device_switch_status(device),
118
+ self.async_update_device_select_status(device),
119
+ self.async_update_device_sensor_status(device),
120
+ return_exceptions=True
121
+ )
122
+ _LOGGER.info(f"update_device_status finish: {device.__str__()}")
123
+
124
+ async def async_update_device_switch_status(self, device):
125
+ """UPDATE SWITCH STATUS"""
126
+ for switch_type in device.switches.keys():
127
+ device.switches[switch_type] = any(await asyncio.gather(
128
+ *[self._async_update_device_switch_status_by_ability(device, ability_type) for ability_type in
129
+ SWITCH_TYPE_ENABLE[switch_type]], return_exceptions=True))
130
+
131
+ async def async_update_device_select_status(self, device):
132
+ """UPDATE SELECT STATUS"""
133
+ for select_type in device.selects.keys():
134
+ await self.async_update_device_select_status_by_type(device, select_type)
135
+
136
+ async def async_update_device_sensor_status(self, device):
137
+ """UPDATE SENSOR STATUS"""
138
+ for sensor_type in device.sensors.keys():
139
+ if sensor_type == PARAM_STATUS:
140
+ try:
141
+ data = await self.device_manager.async_get_device_online_status(device.device_id)
142
+ for channel in data[PARAM_CHANNELS]:
143
+ if channel[PARAM_CHANNEL_ID] == device.channel_id:
144
+ device.sensors[PARAM_STATUS] = get_device_status(channel["onLine"])
145
+ break
146
+ except RequestFailedException:
147
+ device.sensors[PARAM_STATUS] = DeviceStatus.OFFLINE.status
148
+ elif sensor_type == PARAM_STORAGE_USED:
149
+ await self._get_device_storage(device)
150
+
151
+ async def _get_device_storage(self, device):
152
+ try:
153
+ data = await self.device_manager.async_get_device_storage(device.device_id)
154
+ if data[PARAM_TOTAL_BYTES] != 0:
155
+ percentage_used = int(data[PARAM_USED_BYTES] * 100 / data[PARAM_TOTAL_BYTES])
156
+ device.sensors[PARAM_STORAGE_USED] = f"{percentage_used}%"
157
+ else:
158
+ device.sensors[PARAM_STORAGE_USED] = "No Storage Medium"
159
+ except RequestFailedException as exception:
160
+ if "DV1049" in exception.message:
161
+ device.sensors[PARAM_STORAGE_USED] = "No Storage Medium"
162
+ else:
163
+ device.sensors[PARAM_STORAGE_USED] = "Abnormal"
164
+
165
+ async def async_get_device_stream(self, device):
166
+ try:
167
+ return await self.async_get_device_exist_stream(device)
168
+ except RequestFailedException as exception:
169
+ if "LV1002" in exception.message:
170
+ try:
171
+ return await self.async_create_device_stream(device)
172
+ except RequestFailedException as ex:
173
+ if "LV1001" in ex.message:
174
+ return await self.async_get_device_exist_stream(device)
175
+ raise RequestFailedException("get_stream_url failed")
176
+
177
+ async def async_get_device_exist_stream(self, device):
178
+ data = await self.device_manager.async_get_stream_url(device.device_id, device.channel_id)
179
+ if PARAM_STREAMS in data and len(data[PARAM_STREAMS]) > 0:
180
+ # Prioritize obtaining high-definition live streaming addresses for HTTPS
181
+ for stream in data[PARAM_STREAMS]:
182
+ if "https" in stream[PARAM_HLS] and stream[PARAM_STREAM_ID] == 0:
183
+ _LOGGER.info(f"create_device_stream {stream[PARAM_HLS]}")
184
+ return stream[PARAM_HLS]
185
+ return data[PARAM_STREAMS][0][PARAM_HLS]
186
+
187
+ async def async_create_device_stream(self, device):
188
+ data = await self.device_manager.async_create_stream_url(device.device_id, device.channel_id)
189
+ if PARAM_STREAMS in data and len(data[PARAM_STREAMS]) > 0:
190
+ # Prioritize obtaining high-definition live streaming addresses for HTTPS
191
+ for stream in data[PARAM_STREAMS]:
192
+ if "https" in stream[PARAM_HLS] and stream[PARAM_STREAM_ID] == 0:
193
+ _LOGGER.info(f"create_device_stream {stream[PARAM_HLS]}")
194
+ return stream[PARAM_HLS]
195
+ return data[PARAM_STREAMS][0][PARAM_HLS]
196
+
197
+ async def async_get_device_image(self, device):
198
+ data = await self.device_manager.async_get_device_snap(device.device_id, device.channel_id)
199
+ if PARAM_URL in data:
200
+ await asyncio.sleep(3)
201
+ try:
202
+ async with aiohttp.ClientSession() as session:
203
+ response = await session.request("GET", data[PARAM_URL])
204
+ if response.status != 200:
205
+ raise RequestFailedException(f"request failed,status code {response.status}")
206
+ return await response.read()
207
+ except Exception as exception:
208
+ _LOGGER.info("error get_device_image %s", exception)
209
+ return None
210
+
211
+ async def async_get_devices(self) -> list[ImouHaDevice]:
212
+ """
213
+ GET A LIST OF ALL DEVICES。
214
+ """
215
+ devices = []
216
+ for device in await self.device_manager.async_get_devices():
217
+ if device.channel_number > 0 and len(device.channels) > 0:
218
+ for channel in device.channels:
219
+ imou_ha_device = ImouHaDevice(device.device_id, channel.channel_id,
220
+ channel.channel_name, device.brand, device.device_model,
221
+ device.device_version, device.product_id)
222
+ # Determine which switches are needed based on the ability
223
+ abilities = device.device_ability if len(device.channels) == 1 else channel.channel_ability
224
+ if CONF_CLOSE_CAMERA in abilities:
225
+ imou_ha_device.switches[PARAM_CLOSE_CAMERA] = False
226
+ if CONF_WHITE_LIGHT in abilities:
227
+ imou_ha_device.switches[PARAM_WHITE_LIGHT] = False
228
+ if CONF_AB_ALARM_SOUND in abilities:
229
+ imou_ha_device.switches[PARAM_AB_ALARM_SOUND] = False
230
+ if CONF_AUDIO_ENCODE_CONTROL in abilities:
231
+ imou_ha_device.switches[PARAM_AUDIO_ENCODE_CONTROL] = False
232
+ if CONF_NVM in abilities:
233
+ imou_ha_device.selects[PARAM_NIGHT_VISION_MODE] = {
234
+ PARAM_CURRENT_OPTION: "",
235
+ PARAM_OPTIONS: []
236
+ }
237
+ if CONF_PT in abilities:
238
+ imou_ha_device.buttons.extend(["ptz_up", "ptz_down", "ptz_left", "ptz_right"])
239
+
240
+ devices.append(imou_ha_device)
241
+ return devices
242
+
243
+ async def async_press_button(self, device_id: str, channel_id: str, button_type: str):
244
+ if "ptz" in button_type:
245
+ await self.device_manager.async_control_device_ptz(device_id, channel_id,
246
+ BUTTON_TYPE_PARAM_VALUE[button_type])
247
+ elif PARAM_RESTART_DEVICE == button_type:
248
+ await self.device_manager.async_restart_device(device_id)
249
+
250
+ async def async_switch_operation(self, device, switch_type: str, enable: bool):
251
+ if PARAM_MOTION_DETECT == switch_type:
252
+ await self.device_manager.async_modify_device_alarm_status(device.device_id, device.channel_id, enable)
253
+ else:
254
+ result = await asyncio.gather(
255
+ *[self._async_set_device_switch_status_by_ability(device, ability_type, enable)
256
+ for ability_type in SWITCH_TYPE_ENABLE[switch_type]],
257
+ return_exceptions=True)
258
+ # Request all failed, consider this operation a failure
259
+ if all(isinstance(result_item, Exception) for result_item in result):
260
+ raise result[0]
261
+ await asyncio.sleep(3)
262
+ device.switches[switch_type] = any(await asyncio.gather(
263
+ *[self._async_update_device_switch_status_by_ability(device, ability_type) for ability_type in
264
+ SWITCH_TYPE_ENABLE[switch_type]], return_exceptions=True))
265
+
266
+ async def async_select_option(self, device, select_type: str, option: str):
267
+ if PARAM_NIGHT_VISION_MODE == select_type:
268
+ await self.device_manager.async_set_device_night_vision_mode(device.device_id, device.channel_id, option)
269
+
270
+ async def _async_update_device_switch_status_by_ability(self, device, ability_type) -> bool:
271
+ # Updating the interface requires capturing exceptions for two main purposes:
272
+ # 1. To prevent the updater from failing to load due to exceptions;
273
+ # 2. To set default values
274
+ try:
275
+ data = await self.device_manager.async_get_device_status(device.device_id, device.channel_id,
276
+ ability_type)
277
+ return data[PARAM_STATUS] == "on"
278
+ except RequestFailedException:
279
+ return False
280
+
281
+ async def _async_set_device_switch_status_by_ability(self, device, ability_type, enable: bool) -> None:
282
+ await self.device_manager.async_set_device_status(device.device_id, device.channel_id, ability_type, enable)
283
+
284
+ async def async_update_device_select_status_by_type(self, device, select_type):
285
+ if select_type == PARAM_NIGHT_VISION_MODE:
286
+ try:
287
+ await self._async_update_device_night_vision_mode(device)
288
+ except RequestFailedException:
289
+ device.selects[PARAM_NIGHT_VISION_MODE] = {
290
+ PARAM_CURRENT_OPTION: "",
291
+ PARAM_OPTIONS: []
292
+ }
293
+
294
+ async def _async_update_device_night_vision_mode(self, device):
295
+ data = await self.device_manager.async_get_device_night_vision_mode(device.device_id,
296
+ device.channel_id)
297
+ if PARAM_MODE not in data or PARAM_MODES not in data:
298
+ raise RequestFailedException("get_device_night_vision fail")
299
+ if data[PARAM_MODE] is not None:
300
+ device.selects[PARAM_NIGHT_VISION_MODE][PARAM_CURRENT_OPTION] = data[PARAM_MODE]
301
+ if data[PARAM_MODES] is not None:
302
+ device.selects[PARAM_NIGHT_VISION_MODE][PARAM_OPTIONS] = data[PARAM_MODES]
303
+
304
+
305
+ class DeviceStatus(Enum):
306
+ ONLINE = ("1", "online")
307
+ OFFLINE = ("0", "offline")
308
+ SLEEP = ("4", "sleep")
309
+ UPGRADING = ("3", "upgrading")
310
+
311
+ def __init__(self, origin_value, status):
312
+ self.origin_value = origin_value
313
+ self.status = status
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyimouapi
3
- Version: 1.0.2
3
+ Version: 1.0.4
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
@@ -12,11 +12,9 @@ Classifier: Programming Language :: Python :: 3
12
12
  Classifier: Topic :: Software Development :: Libraries
13
13
  Description-Content-Type: text/markdown
14
14
  License-File: LICENSE
15
- Requires-Dist: aiohttp==3.11.0
16
- Requires-Dist: async-timeout==5.0.1
17
-
18
- # Py-Imou-Open-Api
19
-
20
- imou open platform api SDK for python
21
15
 
16
+ ## 打包
17
+ python setup.py sdist bdist_wheel
22
18
 
19
+ ## 上传
20
+ twine upload dist/*
@@ -5,9 +5,9 @@ pyimouapi/__init__.py
5
5
  pyimouapi/const.py
6
6
  pyimouapi/device.py
7
7
  pyimouapi/exceptions.py
8
+ pyimouapi/ha_device.py
8
9
  pyimouapi/openapi.py
9
10
  pyimouapi.egg-info/PKG-INFO
10
11
  pyimouapi.egg-info/SOURCES.txt
11
12
  pyimouapi.egg-info/dependency_links.txt
12
- pyimouapi.egg-info/requires.txt
13
13
  pyimouapi.egg-info/top_level.txt
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='pyimouapi',
5
- version='1.0.2',
5
+ version='1.0.4',
6
6
  packages=find_packages(),
7
7
  description='A package for imou open api',
8
8
  # long_description=open('README.md').read(),
@@ -13,10 +13,6 @@ setup(
13
13
  author='Imou-OpenPlatform',
14
14
  author_email='cloud_openteam_service@imou.com',
15
15
  license='MIT',
16
- install_requires=[
17
- 'aiohttp==3.11.0',
18
- 'async-timeout==5.0.1'
19
- ],
20
16
  classifiers=[
21
17
  'Intended Audience :: Developers',
22
18
  'Operating System :: OS Independent',
pyimouapi-1.0.2/README.md DELETED
@@ -1,5 +0,0 @@
1
- # Py-Imou-Open-Api
2
-
3
- imou open platform api SDK for python
4
-
5
-
@@ -1,2 +0,0 @@
1
- aiohttp==3.11.0
2
- async-timeout==5.0.1
File without changes
File without changes
File without changes