uiprotect 6.7.0__py3-none-any.whl → 6.8.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 +16 -10
- uiprotect/data/bootstrap.py +10 -9
- uiprotect/data/convert.py +6 -7
- uiprotect/data/user.py +99 -1
- {uiprotect-6.7.0.dist-info → uiprotect-6.8.0.dist-info}/METADATA +1 -1
- {uiprotect-6.7.0.dist-info → uiprotect-6.8.0.dist-info}/RECORD +9 -9
- {uiprotect-6.7.0.dist-info → uiprotect-6.8.0.dist-info}/LICENSE +0 -0
- {uiprotect-6.7.0.dist-info → uiprotect-6.8.0.dist-info}/WHEEL +0 -0
- {uiprotect-6.7.0.dist-info → uiprotect-6.8.0.dist-info}/entry_points.txt +0 -0
uiprotect/api.py
CHANGED
|
@@ -27,8 +27,8 @@ from aiohttp import CookieJar, client_exceptions
|
|
|
27
27
|
from platformdirs import user_cache_dir, user_config_dir
|
|
28
28
|
from yarl import URL
|
|
29
29
|
|
|
30
|
-
from uiprotect.data.convert import
|
|
31
|
-
from uiprotect.data.user import Keyring, UlpUser
|
|
30
|
+
from uiprotect.data.convert import list_from_unifi_list
|
|
31
|
+
from uiprotect.data.user import Keyring, Keyrings, UlpUser, UlpUsers
|
|
32
32
|
|
|
33
33
|
from ._compat import cached_property
|
|
34
34
|
from .data import (
|
|
@@ -829,15 +829,21 @@ class ProtectApiClient(BaseApiClient):
|
|
|
829
829
|
async with self._update_lock:
|
|
830
830
|
bootstrap = await self.get_bootstrap()
|
|
831
831
|
if bootstrap.nvr.version >= NFC_FINGERPRINT_SUPPORT_VERSION:
|
|
832
|
-
bootstrap.keyrings =
|
|
833
|
-
|
|
834
|
-
|
|
832
|
+
bootstrap.keyrings = Keyrings.from_list(
|
|
833
|
+
cast(
|
|
834
|
+
list[Keyring],
|
|
835
|
+
list_from_unifi_list(
|
|
836
|
+
self, await self.api_request_list("keyrings")
|
|
837
|
+
),
|
|
838
|
+
)
|
|
835
839
|
)
|
|
836
|
-
bootstrap.ulp_users =
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
840
|
+
bootstrap.ulp_users = UlpUsers.from_list(
|
|
841
|
+
cast(
|
|
842
|
+
list[UlpUser],
|
|
843
|
+
list_from_unifi_list(
|
|
844
|
+
self, await self.api_request_list("ulp-users")
|
|
845
|
+
),
|
|
846
|
+
)
|
|
841
847
|
)
|
|
842
848
|
self.__dict__.pop("bootstrap", None)
|
|
843
849
|
self._bootstrap = bootstrap
|
uiprotect/data/bootstrap.py
CHANGED
|
@@ -34,7 +34,7 @@ from .devices import (
|
|
|
34
34
|
)
|
|
35
35
|
from .nvr import NVR, Event, Liveview
|
|
36
36
|
from .types import EventType, FixSizeOrderedDict, ModelType
|
|
37
|
-
from .user import Group,
|
|
37
|
+
from .user import Group, Keyrings, UlpUserKeyringBase, UlpUsers, User
|
|
38
38
|
from .websocket import (
|
|
39
39
|
WSAction,
|
|
40
40
|
WSPacket,
|
|
@@ -188,8 +188,8 @@ class Bootstrap(ProtectBaseObject):
|
|
|
188
188
|
# agreements
|
|
189
189
|
|
|
190
190
|
# not directly from UniFi
|
|
191
|
-
keyrings:
|
|
192
|
-
ulp_users:
|
|
191
|
+
keyrings: Keyrings = Keyrings()
|
|
192
|
+
ulp_users: UlpUsers = UlpUsers()
|
|
193
193
|
events: dict[str, Event] = FixSizeOrderedDict()
|
|
194
194
|
capture_ws_stats: bool = False
|
|
195
195
|
mac_lookup: dict[str, ProtectDeviceRef] = {}
|
|
@@ -393,7 +393,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
393
393
|
model_type: ModelType,
|
|
394
394
|
) -> WSSubscriptionMessage | None:
|
|
395
395
|
action_id = action["id"]
|
|
396
|
-
|
|
396
|
+
obj_from_bootstrap: UlpUserKeyringBase[ProtectModelWithId] = getattr(
|
|
397
397
|
self, to_snake_case(model_type.devices_key)
|
|
398
398
|
)
|
|
399
399
|
action_type = action["action"]
|
|
@@ -403,7 +403,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
403
403
|
model_class = MODEL_TO_CLASS.get(model_type)
|
|
404
404
|
assert model_class is not None and isinstance(add_obj, model_class)
|
|
405
405
|
add_obj = cast(ProtectModelWithId, add_obj)
|
|
406
|
-
|
|
406
|
+
obj_from_bootstrap.add(add_obj)
|
|
407
407
|
return WSSubscriptionMessage(
|
|
408
408
|
action=WSAction.ADD,
|
|
409
409
|
new_update_id=self.last_update_id,
|
|
@@ -411,17 +411,18 @@ class Bootstrap(ProtectBaseObject):
|
|
|
411
411
|
new_obj=add_obj,
|
|
412
412
|
)
|
|
413
413
|
elif action_type == "remove":
|
|
414
|
-
|
|
415
|
-
if
|
|
414
|
+
to_remove = obj_from_bootstrap.by_id(action_id)
|
|
415
|
+
if to_remove is None:
|
|
416
416
|
return None
|
|
417
|
+
obj_from_bootstrap.remove(to_remove)
|
|
417
418
|
return WSSubscriptionMessage(
|
|
418
419
|
action=WSAction.REMOVE,
|
|
419
420
|
new_update_id=self.last_update_id,
|
|
420
421
|
changed_data={},
|
|
421
|
-
old_obj=
|
|
422
|
+
old_obj=to_remove,
|
|
422
423
|
)
|
|
423
424
|
elif action_type == "update":
|
|
424
|
-
updated_obj =
|
|
425
|
+
updated_obj = obj_from_bootstrap.by_id(action_id)
|
|
425
426
|
if updated_obj is None:
|
|
426
427
|
return None
|
|
427
428
|
|
uiprotect/data/convert.py
CHANGED
|
@@ -85,11 +85,10 @@ def create_from_unifi_dict(
|
|
|
85
85
|
return klass.from_unifi_dict(**data, api=api)
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
def
|
|
88
|
+
def list_from_unifi_list(
|
|
89
89
|
api: ProtectApiClient, unifi_list: list[dict[str, ProtectModelWithId]]
|
|
90
|
-
) ->
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return return_dict
|
|
90
|
+
) -> list[ProtectModelWithId]:
|
|
91
|
+
return [
|
|
92
|
+
cast(ProtectModelWithId, create_from_unifi_dict(obj_dict, api))
|
|
93
|
+
for obj_dict in unifi_list
|
|
94
|
+
]
|
uiprotect/data/user.py
CHANGED
|
@@ -2,15 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import sys
|
|
6
|
+
from abc import abstractmethod
|
|
5
7
|
from datetime import datetime
|
|
6
8
|
from functools import cache
|
|
7
|
-
from typing import Any
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
|
8
10
|
|
|
9
11
|
from pydantic.v1.fields import PrivateAttr
|
|
10
12
|
|
|
11
13
|
from .base import ProtectBaseObject, ProtectModel, ProtectModelWithId
|
|
12
14
|
from .types import ModelType, PermissionNode
|
|
13
15
|
|
|
16
|
+
if sys.version_info >= (3, 11):
|
|
17
|
+
from typing import Self
|
|
18
|
+
else:
|
|
19
|
+
from typing_extensions import Self
|
|
20
|
+
|
|
14
21
|
|
|
15
22
|
class Permission(ProtectBaseObject):
|
|
16
23
|
raw_permission: str
|
|
@@ -236,6 +243,47 @@ class User(ProtectModelWithId):
|
|
|
236
243
|
return False
|
|
237
244
|
|
|
238
245
|
|
|
246
|
+
T = TypeVar("T", bound="ProtectModelWithId")
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class UlpUserKeyringBase(Generic[T]):
|
|
250
|
+
"""Base class for collections of ULP users and keyrings."""
|
|
251
|
+
|
|
252
|
+
def __init__(self) -> None:
|
|
253
|
+
self._id_to_item: dict[str, T] = {}
|
|
254
|
+
|
|
255
|
+
@classmethod
|
|
256
|
+
def from_list(cls, items: list[T]) -> Self:
|
|
257
|
+
instance = cls()
|
|
258
|
+
for item in items:
|
|
259
|
+
instance.add(item)
|
|
260
|
+
return instance
|
|
261
|
+
|
|
262
|
+
def add(self, item: T) -> None:
|
|
263
|
+
"""Add an item to the collection."""
|
|
264
|
+
self._id_to_item[item.id] = item
|
|
265
|
+
|
|
266
|
+
def remove(self, item: T) -> None:
|
|
267
|
+
"""Remove an item from the collection."""
|
|
268
|
+
self._id_to_item.pop(item.id, None)
|
|
269
|
+
|
|
270
|
+
def by_id(self, item_id: str) -> T | None:
|
|
271
|
+
"""Retrieve an item by its ID."""
|
|
272
|
+
return self._id_to_item.get(item_id)
|
|
273
|
+
|
|
274
|
+
@abstractmethod
|
|
275
|
+
def by_ulp_id(self, item_id: str) -> T | None:
|
|
276
|
+
"""Retrieve an item by its ULP ID."""
|
|
277
|
+
|
|
278
|
+
def as_list(self) -> list[T]:
|
|
279
|
+
return list(self._id_to_item.values())
|
|
280
|
+
|
|
281
|
+
def __eq__(self, other: Any) -> bool:
|
|
282
|
+
if TYPE_CHECKING:
|
|
283
|
+
assert isinstance(other, UlpUserKeyringBase)
|
|
284
|
+
return self._id_to_item == other._id_to_item
|
|
285
|
+
|
|
286
|
+
|
|
239
287
|
class Keyring(ProtectModelWithId):
|
|
240
288
|
device_type: str
|
|
241
289
|
device_id: str
|
|
@@ -245,6 +293,34 @@ class Keyring(ProtectModelWithId):
|
|
|
245
293
|
ulp_user: str
|
|
246
294
|
|
|
247
295
|
|
|
296
|
+
class Keyrings(UlpUserKeyringBase[Keyring]):
|
|
297
|
+
def __init__(self) -> None:
|
|
298
|
+
super().__init__()
|
|
299
|
+
self._keyrings_by_registry_id: dict[str, Keyring] = {}
|
|
300
|
+
self._keyrings_by_ulp_user: dict[str, Keyring] = {}
|
|
301
|
+
|
|
302
|
+
def add(self, keyring: Keyring) -> None:
|
|
303
|
+
super().add(keyring)
|
|
304
|
+
self._keyrings_by_registry_id[keyring.registry_id] = keyring
|
|
305
|
+
self._keyrings_by_ulp_user[keyring.ulp_user] = keyring
|
|
306
|
+
|
|
307
|
+
def remove(self, keyring: Keyring) -> None:
|
|
308
|
+
super().remove(keyring)
|
|
309
|
+
self._keyrings_by_registry_id.pop(keyring.registry_id, None)
|
|
310
|
+
self._keyrings_by_ulp_user.pop(keyring.ulp_user, None)
|
|
311
|
+
|
|
312
|
+
def by_ulp_id(self, ulp_id: str) -> Keyring | None:
|
|
313
|
+
return self._keyrings_by_ulp_user.get(ulp_id)
|
|
314
|
+
|
|
315
|
+
def by_registry_id(self, registry_id: str) -> Keyring | None:
|
|
316
|
+
return self._keyrings_by_registry_id.get(registry_id)
|
|
317
|
+
|
|
318
|
+
def __eq__(self, other: Any) -> bool:
|
|
319
|
+
if not isinstance(other, Keyrings):
|
|
320
|
+
return NotImplemented
|
|
321
|
+
return super().__eq__(other)
|
|
322
|
+
|
|
323
|
+
|
|
248
324
|
class UlpUser(ProtectModelWithId):
|
|
249
325
|
ulp_id: str
|
|
250
326
|
first_name: str
|
|
@@ -252,3 +328,25 @@ class UlpUser(ProtectModelWithId):
|
|
|
252
328
|
full_name: str
|
|
253
329
|
avatar: str
|
|
254
330
|
status: str
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
class UlpUsers(UlpUserKeyringBase[UlpUser]):
|
|
334
|
+
def __init__(self) -> None:
|
|
335
|
+
super().__init__()
|
|
336
|
+
self._users_by_ulp_id: dict[str, UlpUser] = {}
|
|
337
|
+
|
|
338
|
+
def add(self, user: UlpUser) -> None:
|
|
339
|
+
super().add(user)
|
|
340
|
+
self._users_by_ulp_id[user.ulp_id] = user
|
|
341
|
+
|
|
342
|
+
def remove(self, user: UlpUser) -> None:
|
|
343
|
+
super().remove(user)
|
|
344
|
+
self._users_by_ulp_id.pop(user.ulp_id, None)
|
|
345
|
+
|
|
346
|
+
def by_ulp_id(self, ulp_id: str) -> UlpUser | None:
|
|
347
|
+
return self._users_by_ulp_id.get(ulp_id)
|
|
348
|
+
|
|
349
|
+
def __eq__(self, other: Any) -> bool:
|
|
350
|
+
if not isinstance(other, UlpUsers):
|
|
351
|
+
return NotImplemented
|
|
352
|
+
return super().__eq__(other)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
uiprotect/__init__.py,sha256=Oz6i1tonIz4QWVnEPkbielJDJ3WQdwZVgYtjY4IwGAQ,636
|
|
2
2
|
uiprotect/__main__.py,sha256=C_bHCOkv5qj6WMy-6ELoY3Y6HDhLxOa1a30CzmbZhsg,462
|
|
3
3
|
uiprotect/_compat.py,sha256=HThmb1zQZCEssCxYYbQzFhJq8zYYlVaSnIEZabKc-6U,302
|
|
4
|
-
uiprotect/api.py,sha256=
|
|
4
|
+
uiprotect/api.py,sha256=pSQ_C2cjtttnFnrVzjmHlqS7zOfMH-bwk4NxbraUCKI,68850
|
|
5
5
|
uiprotect/cli/__init__.py,sha256=1MO8rJmjjAsfVx2x01gn5DJo8B64xdPGo6gRVJbWd18,8868
|
|
6
6
|
uiprotect/cli/backup.py,sha256=ZiS7RZnJGKI8TJKLW2cOUzkRM8nyTvE5Ov_jZZGtvSM,36708
|
|
7
7
|
uiprotect/cli/base.py,sha256=k-_qGuNT7br0iV0KE5F4wYXF75iyLLjBEckTqxC71xM,7591
|
|
@@ -16,12 +16,12 @@ uiprotect/cli/sensors.py,sha256=fQtcDJCVxs4VbAqcavgBy2ABiVxAW3GXtna6_XFBp2k,8153
|
|
|
16
16
|
uiprotect/cli/viewers.py,sha256=2cyrp104ffIvgT0wYGIO0G35QMkEbFe7fSVqLwDXQYQ,2171
|
|
17
17
|
uiprotect/data/__init__.py,sha256=OcfuJl2qXfHcj_mdnrHhzZ5tEIZrw8auziX5IE7dn-I,2938
|
|
18
18
|
uiprotect/data/base.py,sha256=sn7IHKQN96uiZL6ImN1gdCHV97EpUmy-X7xWTUAtWsg,35054
|
|
19
|
-
uiprotect/data/bootstrap.py,sha256=
|
|
20
|
-
uiprotect/data/convert.py,sha256=
|
|
19
|
+
uiprotect/data/bootstrap.py,sha256=j7bVRWwc3zI6gjn30LA_N5rqQbUYu6yxdy8RmrmCuSc,23224
|
|
20
|
+
uiprotect/data/convert.py,sha256=CDPkSMxSEhvDigmzmLFKpjrz0oa5FOvOdkNIHZrOZ4Q,2586
|
|
21
21
|
uiprotect/data/devices.py,sha256=P5U47i_YMpvD0jaWiDYbFz_mZt_Wiop_ssWGWnEMySU,113489
|
|
22
22
|
uiprotect/data/nvr.py,sha256=FGI0eIAyy3Zy9kaxcr67HxwaVCUU8wq3oZyWvoDq7Sg,47251
|
|
23
23
|
uiprotect/data/types.py,sha256=8fEf-fdm_Fqrfb3zwiJJle0ten-Rv3_0Vwk5uimf_vk,18571
|
|
24
|
-
uiprotect/data/user.py,sha256
|
|
24
|
+
uiprotect/data/user.py,sha256=4rDMUPo02LhoVGfDjwXB9NASL4RnZjP3pvqtwjudZeE,10398
|
|
25
25
|
uiprotect/data/websocket.py,sha256=m4EV1Qfh08eKOihy70ycViYgEQpeNSGZQJWdtGIYJDA,6791
|
|
26
26
|
uiprotect/exceptions.py,sha256=kgn0cRM6lTtgLza09SDa3ZiX6ue1QqHCOogQ4qu6KTQ,965
|
|
27
27
|
uiprotect/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -31,8 +31,8 @@ uiprotect/test_util/__init__.py,sha256=Ky8mTL61nhp5II2mxTKBAsSGvNqK8U_CfKC5AGwTo
|
|
|
31
31
|
uiprotect/test_util/anonymize.py,sha256=f-8ijU-_y9r-uAbhIPn0f0I6hzJpAkvJzc8UpWihObI,8478
|
|
32
32
|
uiprotect/utils.py,sha256=jIWT7n_reL90oY91svBfQ4naRxo28qHzP5jNOL12mQE,20342
|
|
33
33
|
uiprotect/websocket.py,sha256=tEyenqblNXHcjWYuf4oRP1E7buNwx6zoECMwpBr-jig,8191
|
|
34
|
-
uiprotect-6.
|
|
35
|
-
uiprotect-6.
|
|
36
|
-
uiprotect-6.
|
|
37
|
-
uiprotect-6.
|
|
38
|
-
uiprotect-6.
|
|
34
|
+
uiprotect-6.8.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
|
|
35
|
+
uiprotect-6.8.0.dist-info/METADATA,sha256=8FkmORQLIos0mSeuDePD0xTl_CfGdDiE68a1VNTpb9o,11096
|
|
36
|
+
uiprotect-6.8.0.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
|
37
|
+
uiprotect-6.8.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
|
|
38
|
+
uiprotect-6.8.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|