uiprotect 5.3.0__py3-none-any.whl → 6.0.0__py3-none-any.whl
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.
Potentially problematic release.
This version of uiprotect might be problematic. Click here for more details.
- uiprotect/api.py +5 -0
- uiprotect/data/types.py +5 -0
- uiprotect/utils.py +28 -12
- uiprotect/websocket.py +4 -2
- {uiprotect-5.3.0.dist-info → uiprotect-6.0.0.dist-info}/METADATA +2 -2
- {uiprotect-5.3.0.dist-info → uiprotect-6.0.0.dist-info}/RECORD +9 -9
- {uiprotect-5.3.0.dist-info → uiprotect-6.0.0.dist-info}/LICENSE +0 -0
- {uiprotect-5.3.0.dist-info → uiprotect-6.0.0.dist-info}/WHEEL +0 -0
- {uiprotect-5.3.0.dist-info → uiprotect-6.0.0.dist-info}/entry_points.txt +0 -0
uiprotect/api.py
CHANGED
|
@@ -191,6 +191,7 @@ class BaseApiClient:
|
|
|
191
191
|
cache_dir: Path | None = None,
|
|
192
192
|
config_dir: Path | None = None,
|
|
193
193
|
store_sessions: bool = True,
|
|
194
|
+
ws_receive_timeout: int | None = None,
|
|
194
195
|
) -> None:
|
|
195
196
|
self._auth_lock = asyncio.Lock()
|
|
196
197
|
self._host = host
|
|
@@ -200,6 +201,7 @@ class BaseApiClient:
|
|
|
200
201
|
self._password = password
|
|
201
202
|
self._verify_ssl = verify_ssl
|
|
202
203
|
self._ws_timeout = ws_timeout
|
|
204
|
+
self._ws_receive_timeout = ws_receive_timeout
|
|
203
205
|
self._loaded_session = False
|
|
204
206
|
self._update_task: asyncio.Task[Bootstrap | None] | None = None
|
|
205
207
|
|
|
@@ -277,6 +279,7 @@ class BaseApiClient:
|
|
|
277
279
|
self._on_websocket_state_change,
|
|
278
280
|
verify=self._verify_ssl,
|
|
279
281
|
timeout=self._ws_timeout,
|
|
282
|
+
receive_timeout=self._ws_receive_timeout,
|
|
280
283
|
)
|
|
281
284
|
return self._websocket
|
|
282
285
|
|
|
@@ -752,6 +755,7 @@ class ProtectApiClient(BaseApiClient):
|
|
|
752
755
|
ignore_stats: bool = False,
|
|
753
756
|
ignore_unadopted: bool = True,
|
|
754
757
|
debug: bool = False,
|
|
758
|
+
ws_receive_timeout: int | None = None,
|
|
755
759
|
) -> None:
|
|
756
760
|
super().__init__(
|
|
757
761
|
host=host,
|
|
@@ -761,6 +765,7 @@ class ProtectApiClient(BaseApiClient):
|
|
|
761
765
|
verify_ssl=verify_ssl,
|
|
762
766
|
session=session,
|
|
763
767
|
ws_timeout=ws_timeout,
|
|
768
|
+
ws_receive_timeout=ws_receive_timeout,
|
|
764
769
|
cache_dir=cache_dir,
|
|
765
770
|
config_dir=config_dir,
|
|
766
771
|
store_sessions=store_sessions,
|
uiprotect/data/types.py
CHANGED
|
@@ -49,6 +49,11 @@ class ValuesEnumMixin:
|
|
|
49
49
|
_values: list[str] | None = None
|
|
50
50
|
_values_normalized: dict[str, str] | None = None
|
|
51
51
|
|
|
52
|
+
@classmethod
|
|
53
|
+
@cache
|
|
54
|
+
def from_string(cls, value: str) -> Any:
|
|
55
|
+
return cls(value) # type: ignore[call-arg]
|
|
56
|
+
|
|
52
57
|
@classmethod
|
|
53
58
|
@cache
|
|
54
59
|
def values(cls) -> list[str]:
|
uiprotect/utils.py
CHANGED
|
@@ -202,6 +202,7 @@ def to_camel_case(name: str) -> str:
|
|
|
202
202
|
|
|
203
203
|
|
|
204
204
|
_EMPTY_UUID = UUID("0" * 32)
|
|
205
|
+
_SHAPE_TYPES = {SHAPE_DICT, SHAPE_LIST, SHAPE_SET}
|
|
205
206
|
|
|
206
207
|
|
|
207
208
|
def convert_unifi_data(value: Any, field: ModelField) -> Any:
|
|
@@ -211,23 +212,20 @@ def convert_unifi_data(value: Any, field: ModelField) -> Any:
|
|
|
211
212
|
if type_ is Any:
|
|
212
213
|
return value
|
|
213
214
|
|
|
214
|
-
shape
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
215
|
+
if (shape := field.shape) in _SHAPE_TYPES:
|
|
216
|
+
if shape == SHAPE_LIST and isinstance(value, list):
|
|
217
|
+
return [convert_unifi_data(v, field) for v in value]
|
|
218
|
+
if shape == SHAPE_SET and isinstance(value, list):
|
|
219
|
+
return {convert_unifi_data(v, field) for v in value}
|
|
220
|
+
if shape == SHAPE_DICT and isinstance(value, dict):
|
|
221
|
+
return {k: convert_unifi_data(v, field) for k, v in value.items()}
|
|
221
222
|
|
|
222
223
|
if value is not None:
|
|
223
224
|
if type_ in IP_TYPES:
|
|
224
|
-
|
|
225
|
-
return ip_address(value)
|
|
226
|
-
except ValueError:
|
|
227
|
-
return value
|
|
225
|
+
return _cached_ip_address(value)
|
|
228
226
|
if type_ is datetime:
|
|
229
227
|
return from_js_time(value)
|
|
230
|
-
if type_ in _CREATE_TYPES
|
|
228
|
+
if type_ in _CREATE_TYPES:
|
|
231
229
|
# cannot do this check too soon because some types cannot be used in isinstance
|
|
232
230
|
if isinstance(value, type_):
|
|
233
231
|
return value
|
|
@@ -236,16 +234,34 @@ def convert_unifi_data(value: Any, field: ModelField) -> Any:
|
|
|
236
234
|
if type_ is UUID and value == _BAD_UUID:
|
|
237
235
|
return _EMPTY_UUID
|
|
238
236
|
return type_(value)
|
|
237
|
+
if _is_enum_type(type_):
|
|
238
|
+
if _is_from_string_enum(type_):
|
|
239
|
+
return type_.from_string(value)
|
|
240
|
+
return type_(value)
|
|
239
241
|
|
|
240
242
|
return value
|
|
241
243
|
|
|
242
244
|
|
|
245
|
+
@lru_cache
|
|
246
|
+
def _cached_ip_address(value: str) -> IPv4Address | IPv6Address | str:
|
|
247
|
+
try:
|
|
248
|
+
return ip_address(value)
|
|
249
|
+
except ValueError:
|
|
250
|
+
return value
|
|
251
|
+
|
|
252
|
+
|
|
243
253
|
@lru_cache
|
|
244
254
|
def _is_enum_type(type_: Any) -> bool:
|
|
245
255
|
"""Checks if type is an Enum."""
|
|
246
256
|
return isclass(type_) and issubclass(type_, Enum)
|
|
247
257
|
|
|
248
258
|
|
|
259
|
+
@lru_cache
|
|
260
|
+
def _is_from_string_enum(type_: Any) -> bool:
|
|
261
|
+
"""Checks if Enum has from_string method."""
|
|
262
|
+
return hasattr(type_, "from_string")
|
|
263
|
+
|
|
264
|
+
|
|
249
265
|
def serialize_unifi_obj(value: Any, levels: int = -1) -> Any:
|
|
250
266
|
"""Serializes UFP data"""
|
|
251
267
|
if unifi_dict := getattr(value, "unifi_dict", None):
|
uiprotect/websocket.py
CHANGED
|
@@ -55,10 +55,12 @@ class Websocket:
|
|
|
55
55
|
timeout: float = 30.0,
|
|
56
56
|
backoff: int = 10,
|
|
57
57
|
verify: bool = True,
|
|
58
|
+
receive_timeout: float | None = None,
|
|
58
59
|
) -> None:
|
|
59
60
|
"""Init Websocket."""
|
|
60
61
|
self.get_url = get_url
|
|
61
62
|
self.timeout = timeout
|
|
63
|
+
self.receive_timeout = receive_timeout
|
|
62
64
|
self.backoff = backoff
|
|
63
65
|
self.verify = verify
|
|
64
66
|
self._get_session = get_session
|
|
@@ -117,7 +119,7 @@ class Websocket:
|
|
|
117
119
|
async def _websocket_inner_loop(self, url: URL) -> None:
|
|
118
120
|
_LOGGER.debug("Connecting WS to %s", url)
|
|
119
121
|
await self._attempt_auth(False)
|
|
120
|
-
ssl =
|
|
122
|
+
ssl = True if self.verify else False
|
|
121
123
|
msg: WSMessage | None = None
|
|
122
124
|
self._seen_non_close_message = False
|
|
123
125
|
session = await self._get_session()
|
|
@@ -127,7 +129,7 @@ class Websocket:
|
|
|
127
129
|
url, ssl=ssl, headers=self._headers, timeout=self.timeout
|
|
128
130
|
)
|
|
129
131
|
while True:
|
|
130
|
-
msg = await self._ws_connection.receive(self.
|
|
132
|
+
msg = await self._ws_connection.receive(self.receive_timeout)
|
|
131
133
|
msg_type = msg.type
|
|
132
134
|
if msg_type is WSMsgType.ERROR:
|
|
133
135
|
_LOGGER.exception("Error from Websocket: %s", msg.data)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: uiprotect
|
|
3
|
-
Version:
|
|
3
|
+
Version: 6.0.0
|
|
4
4
|
Summary: Python API for Unifi Protect (Unofficial)
|
|
5
5
|
Home-page: https://github.com/uilibs/uiprotect
|
|
6
6
|
Author: UI Protect Maintainers
|
|
@@ -18,7 +18,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
18
18
|
Classifier: Topic :: Software Development :: Build Tools
|
|
19
19
|
Classifier: Topic :: Software Development :: Libraries
|
|
20
20
|
Requires-Dist: aiofiles (>=23)
|
|
21
|
-
Requires-Dist: aiohttp (>=3.
|
|
21
|
+
Requires-Dist: aiohttp (>=3.10.0)
|
|
22
22
|
Requires-Dist: aioshutil (>=1.3)
|
|
23
23
|
Requires-Dist: async-timeout (>=3.0.1)
|
|
24
24
|
Requires-Dist: convertertools (>=0.5.0)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
uiprotect/__init__.py,sha256=GDRM9WvWUBbOyBVgltq6Qv8i7LVdWEbG8q5EzYvOFbE,636
|
|
2
2
|
uiprotect/__main__.py,sha256=C_bHCOkv5qj6WMy-6ELoY3Y6HDhLxOa1a30CzmbZhsg,462
|
|
3
|
-
uiprotect/api.py,sha256=
|
|
3
|
+
uiprotect/api.py,sha256=8kC9v6EM67kfdtPDIW8dlqjZl_s5Lffj4o-Z_dOMmdQ,67894
|
|
4
4
|
uiprotect/cli/__init__.py,sha256=1MO8rJmjjAsfVx2x01gn5DJo8B64xdPGo6gRVJbWd18,8868
|
|
5
5
|
uiprotect/cli/backup.py,sha256=ZiS7RZnJGKI8TJKLW2cOUzkRM8nyTvE5Ov_jZZGtvSM,36708
|
|
6
6
|
uiprotect/cli/base.py,sha256=k-_qGuNT7br0iV0KE5F4wYXF75iyLLjBEckTqxC71xM,7591
|
|
@@ -19,7 +19,7 @@ uiprotect/data/bootstrap.py,sha256=OSPHu08p7Ys9KqEb8sq_LFufuECtF4lY7OnAYK27ngo,2
|
|
|
19
19
|
uiprotect/data/convert.py,sha256=8h6Il_DhMkPRDPj9F_rA2UZIlTuchS3BQD24peKpk2A,2185
|
|
20
20
|
uiprotect/data/devices.py,sha256=0l8eiTCwacdHCmOc7qNzeUMpHLxvd6FeB7DOqZKycdA,110880
|
|
21
21
|
uiprotect/data/nvr.py,sha256=8M-62AG4q1k71eX-DaFcdO52QWG4zO9X5Voj0Tj9D5A,46598
|
|
22
|
-
uiprotect/data/types.py,sha256=
|
|
22
|
+
uiprotect/data/types.py,sha256=zYQjmFDqxjVzaan-vihOzHaBirWsW_C0Q3Pd9xTH33I,18214
|
|
23
23
|
uiprotect/data/user.py,sha256=1o5gyPHafn4lHARpoSMD_NWbo5IbzGPfiSASwqqDvWs,7002
|
|
24
24
|
uiprotect/data/websocket.py,sha256=5-yM6yr8NrxKJjBPQlGVXXQUTcksF-UavligKYjJQ3k,6770
|
|
25
25
|
uiprotect/exceptions.py,sha256=kgn0cRM6lTtgLza09SDa3ZiX6ue1QqHCOogQ4qu6KTQ,965
|
|
@@ -28,10 +28,10 @@ uiprotect/release_cache.json,sha256=NamnSFy78hOWY0DPO87J9ELFCAN6NnVquv8gQO75ZG4,
|
|
|
28
28
|
uiprotect/stream.py,sha256=McV3XymKyjn-1uV5jdQHcpaDjqLS4zWyMASQ8ubcyb4,4924
|
|
29
29
|
uiprotect/test_util/__init__.py,sha256=Ky8mTL61nhp5II2mxTKBAsSGvNqK8U_CfKC5AGwToAI,18704
|
|
30
30
|
uiprotect/test_util/anonymize.py,sha256=f-8ijU-_y9r-uAbhIPn0f0I6hzJpAkvJzc8UpWihObI,8478
|
|
31
|
-
uiprotect/utils.py,sha256=
|
|
32
|
-
uiprotect/websocket.py,sha256=
|
|
33
|
-
uiprotect-
|
|
34
|
-
uiprotect-
|
|
35
|
-
uiprotect-
|
|
36
|
-
uiprotect-
|
|
37
|
-
uiprotect-
|
|
31
|
+
uiprotect/utils.py,sha256=jIWT7n_reL90oY91svBfQ4naRxo28qHzP5jNOL12mQE,20342
|
|
32
|
+
uiprotect/websocket.py,sha256=f3YdRg4iKkvyQLz9H9rQ3RdhiEudNwWO4ONaXF6OJ4Y,8130
|
|
33
|
+
uiprotect-6.0.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
|
|
34
|
+
uiprotect-6.0.0.dist-info/METADATA,sha256=6sFvO3w3TeFmN-tU8nvrw8lvdg_soSuCgtcDPFje2_E,11010
|
|
35
|
+
uiprotect-6.0.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
36
|
+
uiprotect-6.0.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
|
|
37
|
+
uiprotect-6.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|