uiprotect 1.19.2__py3-none-any.whl → 1.20.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/__init__.py +16 -0
- uiprotect/cli/base.py +0 -5
- uiprotect/data/base.py +3 -11
- uiprotect/data/bootstrap.py +1 -5
- uiprotect/data/devices.py +1 -4
- uiprotect/data/nvr.py +2 -9
- uiprotect/data/types.py +3 -12
- uiprotect/data/user.py +1 -4
- uiprotect/utils.py +58 -13
- {uiprotect-1.19.2.dist-info → uiprotect-1.20.0.dist-info}/METADATA +2 -2
- {uiprotect-1.19.2.dist-info → uiprotect-1.20.0.dist-info}/RECORD +14 -14
- {uiprotect-1.19.2.dist-info → uiprotect-1.20.0.dist-info}/LICENSE +0 -0
- {uiprotect-1.19.2.dist-info → uiprotect-1.20.0.dist-info}/WHEEL +0 -0
- {uiprotect-1.19.2.dist-info → uiprotect-1.20.0.dist-info}/entry_points.txt +0 -0
uiprotect/__init__.py
CHANGED
|
@@ -4,10 +4,26 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from .api import ProtectApiClient
|
|
6
6
|
from .exceptions import Invalid, NotAuthorized, NvrError
|
|
7
|
+
from .utils import (
|
|
8
|
+
get_nested_attr,
|
|
9
|
+
get_nested_attr_as_bool,
|
|
10
|
+
get_top_level_attr,
|
|
11
|
+
get_top_level_attr_as_bool,
|
|
12
|
+
make_enabled_getter,
|
|
13
|
+
make_required_getter,
|
|
14
|
+
make_value_getter,
|
|
15
|
+
)
|
|
7
16
|
|
|
8
17
|
__all__ = [
|
|
9
18
|
"Invalid",
|
|
10
19
|
"NotAuthorized",
|
|
11
20
|
"NvrError",
|
|
12
21
|
"ProtectApiClient",
|
|
22
|
+
"get_nested_attr",
|
|
23
|
+
"get_nested_attr_as_bool",
|
|
24
|
+
"get_top_level_attr",
|
|
25
|
+
"get_top_level_attr_as_bool",
|
|
26
|
+
"make_value_getter",
|
|
27
|
+
"make_enabled_getter",
|
|
28
|
+
"make_required_getter",
|
|
13
29
|
]
|
uiprotect/cli/base.py
CHANGED
|
@@ -14,11 +14,6 @@ from ..data import NVR, ProtectAdoptableDeviceModel, ProtectBaseObject
|
|
|
14
14
|
from ..exceptions import BadRequest, NvrError, StreamError
|
|
15
15
|
from ..utils import run_async
|
|
16
16
|
|
|
17
|
-
try:
|
|
18
|
-
from pydantic.v1 import ValidationError
|
|
19
|
-
except ImportError:
|
|
20
|
-
from pydantic import ValidationError # type: ignore[assignment]
|
|
21
|
-
|
|
22
17
|
T = TypeVar("T")
|
|
23
18
|
|
|
24
19
|
OPTION_FORCE = typer.Option(False, "-f", "--force", help="Skip confirmation prompt")
|
uiprotect/data/base.py
CHANGED
|
@@ -11,6 +11,9 @@ from ipaddress import IPv4Address
|
|
|
11
11
|
from typing import TYPE_CHECKING, Any, ClassVar, TypeVar
|
|
12
12
|
from uuid import UUID
|
|
13
13
|
|
|
14
|
+
from pydantic.v1 import BaseModel
|
|
15
|
+
from pydantic.v1.fields import SHAPE_DICT, SHAPE_LIST, PrivateAttr
|
|
16
|
+
|
|
14
17
|
from ..exceptions import BadRequest, ClientError, NotAuthorized
|
|
15
18
|
from ..utils import (
|
|
16
19
|
asyncio_timeout,
|
|
@@ -34,17 +37,6 @@ from .websocket import (
|
|
|
34
37
|
WSPacketFrameHeader,
|
|
35
38
|
)
|
|
36
39
|
|
|
37
|
-
try:
|
|
38
|
-
from pydantic.v1 import BaseModel
|
|
39
|
-
from pydantic.v1.fields import SHAPE_DICT, SHAPE_LIST, PrivateAttr
|
|
40
|
-
except ImportError:
|
|
41
|
-
from pydantic import BaseModel # type: ignore[assignment, no-redef]
|
|
42
|
-
from pydantic.fields import ( # type: ignore[attr-defined, assignment, no-redef]
|
|
43
|
-
SHAPE_DICT,
|
|
44
|
-
SHAPE_LIST,
|
|
45
|
-
PrivateAttr,
|
|
46
|
-
)
|
|
47
|
-
|
|
48
40
|
if TYPE_CHECKING:
|
|
49
41
|
from asyncio.events import TimerHandle
|
|
50
42
|
|
uiprotect/data/bootstrap.py
CHANGED
|
@@ -10,11 +10,7 @@ from datetime import datetime
|
|
|
10
10
|
from typing import TYPE_CHECKING, Any
|
|
11
11
|
|
|
12
12
|
from aiohttp.client_exceptions import ServerDisconnectedError
|
|
13
|
-
|
|
14
|
-
try:
|
|
15
|
-
from pydantic.v1 import PrivateAttr, ValidationError
|
|
16
|
-
except ImportError:
|
|
17
|
-
from pydantic import PrivateAttr, ValidationError # type: ignore[assignment]
|
|
13
|
+
from pydantic.v1 import PrivateAttr, ValidationError
|
|
18
14
|
|
|
19
15
|
from ..exceptions import ClientError
|
|
20
16
|
from ..utils import normalize_mac, utc_now
|
uiprotect/data/devices.py
CHANGED
|
@@ -12,10 +12,7 @@ from ipaddress import IPv4Address
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import TYPE_CHECKING, Any, Literal, cast
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
from pydantic.v1.fields import PrivateAttr
|
|
17
|
-
except ImportError:
|
|
18
|
-
from pydantic.fields import PrivateAttr
|
|
15
|
+
from pydantic.v1.fields import PrivateAttr
|
|
19
16
|
|
|
20
17
|
from ..exceptions import BadRequest, NotAuthorized, StreamError
|
|
21
18
|
from ..stream import TalkbackStream
|
uiprotect/data/nvr.py
CHANGED
|
@@ -16,6 +16,7 @@ from uuid import UUID
|
|
|
16
16
|
import aiofiles
|
|
17
17
|
import orjson
|
|
18
18
|
from aiofiles import os as aos
|
|
19
|
+
from pydantic.v1.fields import PrivateAttr
|
|
19
20
|
|
|
20
21
|
from ..exceptions import BadRequest, NotAuthorized
|
|
21
22
|
from ..utils import RELEASE_CACHE, convert_to_datetime
|
|
@@ -58,16 +59,8 @@ from .types import (
|
|
|
58
59
|
)
|
|
59
60
|
from .user import User, UserLocation
|
|
60
61
|
|
|
61
|
-
try:
|
|
62
|
-
from pydantic.v1.fields import PrivateAttr
|
|
63
|
-
except ImportError:
|
|
64
|
-
from pydantic.fields import PrivateAttr
|
|
65
|
-
|
|
66
62
|
if TYPE_CHECKING:
|
|
67
|
-
|
|
68
|
-
from pydantic.v1.typing import SetStr
|
|
69
|
-
except ImportError:
|
|
70
|
-
from pydantic.typing import SetStr # type: ignore[assignment, no-redef]
|
|
63
|
+
from pydantic.v1.typing import SetStr
|
|
71
64
|
|
|
72
65
|
|
|
73
66
|
_LOGGER = logging.getLogger(__name__)
|
uiprotect/data/types.py
CHANGED
|
@@ -6,18 +6,9 @@ from functools import cache, cached_property
|
|
|
6
6
|
from typing import Any, Literal, Optional, TypeVar, Union
|
|
7
7
|
|
|
8
8
|
from packaging.version import Version as BaseVersion
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
from pydantic.v1.color import Color as BaseColor
|
|
13
|
-
from pydantic.v1.types import ConstrainedFloat, ConstrainedStr
|
|
14
|
-
except ImportError:
|
|
15
|
-
from pydantic import BaseModel, ConstrainedInt # type: ignore[assignment, no-redef]
|
|
16
|
-
from pydantic.color import Color as BaseColor # type: ignore[assignment, no-redef]
|
|
17
|
-
from pydantic.types import ( # type: ignore[assignment, no-redef]
|
|
18
|
-
ConstrainedFloat,
|
|
19
|
-
ConstrainedStr,
|
|
20
|
-
)
|
|
9
|
+
from pydantic.v1 import BaseModel, ConstrainedInt
|
|
10
|
+
from pydantic.v1.color import Color as BaseColor
|
|
11
|
+
from pydantic.v1.types import ConstrainedFloat, ConstrainedStr
|
|
21
12
|
|
|
22
13
|
KT = TypeVar("KT")
|
|
23
14
|
VT = TypeVar("VT")
|
uiprotect/data/user.py
CHANGED
|
@@ -6,10 +6,7 @@ from datetime import datetime
|
|
|
6
6
|
from functools import cache
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
from pydantic.v1.fields import PrivateAttr
|
|
11
|
-
except ImportError:
|
|
12
|
-
from pydantic.fields import PrivateAttr
|
|
9
|
+
from pydantic.v1.fields import PrivateAttr
|
|
13
10
|
|
|
14
11
|
from .base import ProtectBaseObject, ProtectModel, ProtectModelWithId
|
|
15
12
|
from .types import ModelType, PermissionNode
|
uiprotect/utils.py
CHANGED
|
@@ -17,17 +17,20 @@ from copy import deepcopy
|
|
|
17
17
|
from datetime import datetime, timedelta, timezone, tzinfo
|
|
18
18
|
from decimal import Decimal
|
|
19
19
|
from enum import Enum
|
|
20
|
-
from functools import cache, lru_cache
|
|
20
|
+
from functools import cache, lru_cache, partial
|
|
21
21
|
from hashlib import sha224
|
|
22
22
|
from http.cookies import Morsel
|
|
23
23
|
from inspect import isclass
|
|
24
24
|
from ipaddress import IPv4Address, IPv6Address, ip_address
|
|
25
|
+
from operator import attrgetter
|
|
25
26
|
from pathlib import Path
|
|
26
27
|
from typing import TYPE_CHECKING, Any, TypeVar, Union, overload
|
|
27
28
|
from uuid import UUID
|
|
28
29
|
|
|
29
30
|
import jwt
|
|
30
31
|
from aiohttp import ClientResponse
|
|
32
|
+
from pydantic.v1.fields import SHAPE_DICT, SHAPE_LIST, SHAPE_SET, ModelField
|
|
33
|
+
from pydantic.v1.utils import to_camel
|
|
31
34
|
|
|
32
35
|
from .data.types import (
|
|
33
36
|
Color,
|
|
@@ -38,18 +41,6 @@ from .data.types import (
|
|
|
38
41
|
)
|
|
39
42
|
from .exceptions import NvrError
|
|
40
43
|
|
|
41
|
-
try:
|
|
42
|
-
from pydantic.v1.fields import SHAPE_DICT, SHAPE_LIST, SHAPE_SET, ModelField
|
|
43
|
-
from pydantic.v1.utils import to_camel
|
|
44
|
-
except ImportError:
|
|
45
|
-
from pydantic.fields import ( # type: ignore[assignment, no-redef, attr-defined]
|
|
46
|
-
SHAPE_DICT,
|
|
47
|
-
SHAPE_LIST,
|
|
48
|
-
SHAPE_SET,
|
|
49
|
-
ModelField,
|
|
50
|
-
)
|
|
51
|
-
from pydantic.utils import to_camel # type: ignore[assignment, no-redef]
|
|
52
|
-
|
|
53
44
|
if TYPE_CHECKING:
|
|
54
45
|
from uiprotect.api import ProtectApiClient
|
|
55
46
|
from uiprotect.data import CoordType, Event
|
|
@@ -629,3 +620,57 @@ def clamp_value(value: float, step_size: float) -> float:
|
|
|
629
620
|
def normalize_mac(mac: str) -> str:
|
|
630
621
|
"""Normalize MAC address."""
|
|
631
622
|
return mac.lower().replace(":", "").replace("-", "").replace("_", "")
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
_SENTINEL = object()
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
def get_nested_attr(attrs: tuple[str, ...], obj: Any) -> Any:
|
|
629
|
+
"""Fetch a nested attribute."""
|
|
630
|
+
value = obj
|
|
631
|
+
for key in attrs:
|
|
632
|
+
if (value := getattr(value, key, _SENTINEL)) is _SENTINEL:
|
|
633
|
+
return None
|
|
634
|
+
return value.value if isinstance(value, Enum) else value
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def get_nested_attr_as_bool(attrs: tuple[str, ...], obj: Any) -> bool:
|
|
638
|
+
"""Fetch a nested attribute as a bool."""
|
|
639
|
+
value = obj
|
|
640
|
+
for key in attrs:
|
|
641
|
+
if (value := getattr(value, key, _SENTINEL)) is _SENTINEL:
|
|
642
|
+
return False
|
|
643
|
+
return bool(value.value if isinstance(value, Enum) else value)
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
def get_top_level_attr(attr: str, obj: Any) -> Any:
|
|
647
|
+
"""Fetch a top level attribute."""
|
|
648
|
+
value = getattr(obj, attr)
|
|
649
|
+
return value.value if isinstance(value, Enum) else value
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
def get_top_level_attr_as_bool(attr: str, obj: Any) -> Any:
|
|
653
|
+
"""Fetch a top level attribute as a bool."""
|
|
654
|
+
value = getattr(obj, attr)
|
|
655
|
+
return bool(value.value if isinstance(value, Enum) else value)
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
def make_value_getter(ufp_value: str) -> Callable[[T], Any]:
|
|
659
|
+
"""Return a function to get a value from a Protect device."""
|
|
660
|
+
if "." not in ufp_value:
|
|
661
|
+
return partial(get_top_level_attr, ufp_value)
|
|
662
|
+
return partial(get_nested_attr, tuple(ufp_value.split(".")))
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
def make_enabled_getter(ufp_enabled: str) -> Callable[[T], bool]:
|
|
666
|
+
"""Return a function to get a value from a Protect device."""
|
|
667
|
+
if "." not in ufp_enabled:
|
|
668
|
+
return attrgetter(ufp_enabled)
|
|
669
|
+
return partial(get_nested_attr, tuple(ufp_enabled.split(".")))
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
def make_required_getter(ufp_required_field: str) -> Callable[[T], bool]:
|
|
673
|
+
"""Return a function to get a value from a Protect device."""
|
|
674
|
+
if "." not in ufp_required_field:
|
|
675
|
+
return partial(get_top_level_attr_as_bool, ufp_required_field)
|
|
676
|
+
return partial(get_nested_attr_as_bool, tuple(ufp_required_field.split(".")))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: uiprotect
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.20.0
|
|
4
4
|
Summary: Python API for Unifi Protect (Unofficial)
|
|
5
5
|
Home-page: https://github.com/uilibs/uiprotect
|
|
6
6
|
License: MIT
|
|
@@ -27,7 +27,7 @@ Requires-Dist: orjson (>=3.9.15)
|
|
|
27
27
|
Requires-Dist: packaging (>=23)
|
|
28
28
|
Requires-Dist: pillow (>=10)
|
|
29
29
|
Requires-Dist: platformdirs (>=4)
|
|
30
|
-
Requires-Dist: pydantic (>=1.10.
|
|
30
|
+
Requires-Dist: pydantic (>=1.10.17)
|
|
31
31
|
Requires-Dist: pyjwt (>=2.6)
|
|
32
32
|
Requires-Dist: rich (>=10)
|
|
33
33
|
Requires-Dist: typer (>=0.12.3)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
uiprotect/__init__.py,sha256=
|
|
1
|
+
uiprotect/__init__.py,sha256=UdpRSSLSy7pdDfTKf0zRIfy6KRGt_Jv-fMzYWgibbG4,686
|
|
2
2
|
uiprotect/__main__.py,sha256=C_bHCOkv5qj6WMy-6ELoY3Y6HDhLxOa1a30CzmbZhsg,462
|
|
3
3
|
uiprotect/api.py,sha256=SvAni2arXGtP8HzvTljKNLclSfzpRL03RaEF8o1o6K4,65561
|
|
4
4
|
uiprotect/cli/__init__.py,sha256=sSLW9keVQOkgFcMW18HTDjRrt9sJ0KWjn9DJDA6f9Pc,8658
|
|
5
5
|
uiprotect/cli/backup.py,sha256=ZiS7RZnJGKI8TJKLW2cOUzkRM8nyTvE5Ov_jZZGtvSM,36708
|
|
6
|
-
uiprotect/cli/base.py,sha256=
|
|
6
|
+
uiprotect/cli/base.py,sha256=k-_qGuNT7br0iV0KE5F4wYXF75iyLLjBEckTqxC71xM,7591
|
|
7
7
|
uiprotect/cli/cameras.py,sha256=YvvMccQEYG3Wih0Ix8tan1R1vfaJ6cogg6YKWLzMUV8,16973
|
|
8
8
|
uiprotect/cli/chimes.py,sha256=XANn21bQVkestkKOm9HjxSM8ZGrRrqvUXLouaQ3LTqs,5326
|
|
9
9
|
uiprotect/cli/doorlocks.py,sha256=Go_Tn68bAcmrRAnUIi4kBiR7ciKQsu_R150ubPTjUAs,3523
|
|
@@ -14,13 +14,13 @@ uiprotect/cli/nvr.py,sha256=TwxEg2XT8jXAbOqv6gc7KFXELKadeItEDYweSL4_-e8,4260
|
|
|
14
14
|
uiprotect/cli/sensors.py,sha256=fQtcDJCVxs4VbAqcavgBy2ABiVxAW3GXtna6_XFBp2k,8153
|
|
15
15
|
uiprotect/cli/viewers.py,sha256=2cyrp104ffIvgT0wYGIO0G35QMkEbFe7fSVqLwDXQYQ,2171
|
|
16
16
|
uiprotect/data/__init__.py,sha256=OcfuJl2qXfHcj_mdnrHhzZ5tEIZrw8auziX5IE7dn-I,2938
|
|
17
|
-
uiprotect/data/base.py,sha256=
|
|
18
|
-
uiprotect/data/bootstrap.py,sha256=
|
|
17
|
+
uiprotect/data/base.py,sha256=5x4YJjs4gAHKSbHts_BV9e_OYwODRRxn5Zzm35y_KOk,35460
|
|
18
|
+
uiprotect/data/bootstrap.py,sha256=aF6BJ8qgE7sEDsTKPdRXsrMsL_CTmo8Xr-TUNZjfU6I,21810
|
|
19
19
|
uiprotect/data/convert.py,sha256=8h6Il_DhMkPRDPj9F_rA2UZIlTuchS3BQD24peKpk2A,2185
|
|
20
|
-
uiprotect/data/devices.py,sha256=
|
|
21
|
-
uiprotect/data/nvr.py,sha256=
|
|
22
|
-
uiprotect/data/types.py,sha256=
|
|
23
|
-
uiprotect/data/user.py,sha256=
|
|
20
|
+
uiprotect/data/devices.py,sha256=A4ambSBi36IT9Sg_88QR0t7r1TrDBjHa4yfN5cdR8-E,110412
|
|
21
|
+
uiprotect/data/nvr.py,sha256=H0QIxwzQZJYGGeGhY_itb-WYqZnaQ_KcTlWTtrc-LLM,47456
|
|
22
|
+
uiprotect/data/types.py,sha256=3CocULpkdTgF4is1nIEDYIlwf2EOkNNM7L4kJ7NkAwM,17654
|
|
23
|
+
uiprotect/data/user.py,sha256=YvgXJKV4_y-bm0eySWz9f_ie9aR5lpVn17t9H0Pix8I,6998
|
|
24
24
|
uiprotect/data/websocket.py,sha256=vnn3FUc1H3e4dvA3INGob_asbmVCaA99H1DnwDEmpdw,6709
|
|
25
25
|
uiprotect/exceptions.py,sha256=kgn0cRM6lTtgLza09SDa3ZiX6ue1QqHCOogQ4qu6KTQ,965
|
|
26
26
|
uiprotect/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -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=d2g7afa0LSdixQ0kjEDYwafDFME_UlW2LzxpamZ2BC0,18556
|
|
30
30
|
uiprotect/test_util/anonymize.py,sha256=f-8ijU-_y9r-uAbhIPn0f0I6hzJpAkvJzc8UpWihObI,8478
|
|
31
|
-
uiprotect/utils.py,sha256=
|
|
31
|
+
uiprotect/utils.py,sha256=3SJFF8qs1Jz8t3mD8qwc1hFSocolFjdXI_v4yVlC7o4,20088
|
|
32
32
|
uiprotect/websocket.py,sha256=JHI_2EZeRPqPyQopsBZS0dr3tu0HaTiqeLazfBXhW_8,7339
|
|
33
|
-
uiprotect-1.
|
|
34
|
-
uiprotect-1.
|
|
35
|
-
uiprotect-1.
|
|
36
|
-
uiprotect-1.
|
|
37
|
-
uiprotect-1.
|
|
33
|
+
uiprotect-1.20.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
|
|
34
|
+
uiprotect-1.20.0.dist-info/METADATA,sha256=2tR-i6s0-c1E1PzpEfVbFq9V9jA2RjilNdhywYM9NW0,10983
|
|
35
|
+
uiprotect-1.20.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
36
|
+
uiprotect-1.20.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
|
|
37
|
+
uiprotect-1.20.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|