uiprotect 5.2.1__tar.gz → 5.3.0__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.
Potentially problematic release.
This version of uiprotect might be problematic. Click here for more details.
- {uiprotect-5.2.1 → uiprotect-5.3.0}/PKG-INFO +1 -1
- {uiprotect-5.2.1 → uiprotect-5.3.0}/pyproject.toml +1 -1
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/api.py +21 -23
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/data/devices.py +18 -6
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/data/types.py +20 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/data/user.py +2 -2
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/utils.py +5 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/LICENSE +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/README.md +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/__init__.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/__main__.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/cli/__init__.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/cli/backup.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/cli/base.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/cli/cameras.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/cli/chimes.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/cli/doorlocks.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/cli/events.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/cli/lights.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/cli/liveviews.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/cli/nvr.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/cli/sensors.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/cli/viewers.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/data/__init__.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/data/base.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/data/bootstrap.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/data/convert.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/data/nvr.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/data/websocket.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/exceptions.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/py.typed +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/release_cache.json +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/stream.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/test_util/__init__.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/test_util/anonymize.py +0 -0
- {uiprotect-5.2.1 → uiprotect-5.3.0}/src/uiprotect/websocket.py +0 -0
|
@@ -1401,21 +1401,20 @@ class ProtectApiClient(BaseApiClient):
|
|
|
1401
1401
|
|
|
1402
1402
|
Datetime of screenshot is approximate. It may be +/- a few seconds.
|
|
1403
1403
|
"""
|
|
1404
|
-
params = {
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1404
|
+
params: dict[str, Any] = {}
|
|
1405
|
+
if dt is not None:
|
|
1406
|
+
path = "recording-snapshot"
|
|
1407
|
+
params["ts"] = to_js_time(dt)
|
|
1408
|
+
else:
|
|
1409
|
+
path = "snapshot"
|
|
1410
|
+
params["ts"] = int(time.time() * 1000)
|
|
1411
|
+
params["force"] = "true"
|
|
1408
1412
|
|
|
1409
1413
|
if width is not None:
|
|
1410
|
-
params
|
|
1414
|
+
params["w"] = width
|
|
1411
1415
|
|
|
1412
1416
|
if height is not None:
|
|
1413
|
-
params
|
|
1414
|
-
|
|
1415
|
-
path = "snapshot"
|
|
1416
|
-
if dt is not None:
|
|
1417
|
-
path = "recording-snapshot"
|
|
1418
|
-
del params["force"]
|
|
1417
|
+
params["h"] = height
|
|
1419
1418
|
|
|
1420
1419
|
return await self.api_request_raw(
|
|
1421
1420
|
f"cameras/{camera_id}/{path}",
|
|
@@ -1435,22 +1434,21 @@ class ProtectApiClient(BaseApiClient):
|
|
|
1435
1434
|
|
|
1436
1435
|
Datetime of screenshot is approximate. It may be +/- a few seconds.
|
|
1437
1436
|
"""
|
|
1438
|
-
params = {
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1437
|
+
params: dict[str, Any] = {}
|
|
1438
|
+
if dt is not None:
|
|
1439
|
+
path = "recording-snapshot"
|
|
1440
|
+
params["ts"] = to_js_time(dt)
|
|
1441
|
+
params["lens"] = 2
|
|
1442
|
+
else:
|
|
1443
|
+
path = "package-snapshot"
|
|
1444
|
+
params["ts"] = int(time.time() * 1000)
|
|
1445
|
+
params["force"] = "true"
|
|
1442
1446
|
|
|
1443
1447
|
if width is not None:
|
|
1444
|
-
params
|
|
1448
|
+
params["w"] = width
|
|
1445
1449
|
|
|
1446
1450
|
if height is not None:
|
|
1447
|
-
params
|
|
1448
|
-
|
|
1449
|
-
path = "package-snapshot"
|
|
1450
|
-
if dt is not None:
|
|
1451
|
-
path = "recording-snapshot"
|
|
1452
|
-
del params["force"]
|
|
1453
|
-
params.update({"lens": 2})
|
|
1451
|
+
params["h"] = height
|
|
1454
1452
|
|
|
1455
1453
|
return await self.api_request_raw(
|
|
1456
1454
|
f"cameras/{camera_id}/{path}",
|
|
@@ -7,7 +7,7 @@ import logging
|
|
|
7
7
|
import warnings
|
|
8
8
|
from collections.abc import Callable
|
|
9
9
|
from datetime import datetime, timedelta
|
|
10
|
-
from functools import cache
|
|
10
|
+
from functools import cache, lru_cache
|
|
11
11
|
from ipaddress import IPv4Address
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import TYPE_CHECKING, Any, Literal, cast
|
|
@@ -25,6 +25,7 @@ from ..utils import (
|
|
|
25
25
|
convert_video_modes,
|
|
26
26
|
from_js_time,
|
|
27
27
|
serialize_point,
|
|
28
|
+
timedelta_total_seconds,
|
|
28
29
|
to_js_time,
|
|
29
30
|
utc_now,
|
|
30
31
|
)
|
|
@@ -900,6 +901,15 @@ class CameraAudioSettings(ProtectBaseObject):
|
|
|
900
901
|
style: list[AudioStyle]
|
|
901
902
|
|
|
902
903
|
|
|
904
|
+
@lru_cache
|
|
905
|
+
def _chime_type_from_total_seconds(total_seconds: float) -> ChimeType:
|
|
906
|
+
if total_seconds == 0.3:
|
|
907
|
+
return ChimeType.MECHANICAL
|
|
908
|
+
if total_seconds > 0.3:
|
|
909
|
+
return ChimeType.DIGITAL
|
|
910
|
+
return ChimeType.NONE
|
|
911
|
+
|
|
912
|
+
|
|
903
913
|
class Camera(ProtectMotionDeviceModel):
|
|
904
914
|
is_deleting: bool
|
|
905
915
|
# Microphone Sensitivity
|
|
@@ -1855,11 +1865,13 @@ class Camera(ProtectMotionDeviceModel):
|
|
|
1855
1865
|
|
|
1856
1866
|
@property
|
|
1857
1867
|
def chime_type(self) -> ChimeType:
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1868
|
+
return _chime_type_from_total_seconds(
|
|
1869
|
+
timedelta_total_seconds(self.chime_duration)
|
|
1870
|
+
)
|
|
1871
|
+
|
|
1872
|
+
@property
|
|
1873
|
+
def chime_duration_seconds(self) -> float:
|
|
1874
|
+
return timedelta_total_seconds(self.chime_duration)
|
|
1863
1875
|
|
|
1864
1876
|
@property
|
|
1865
1877
|
def is_digital_chime(self) -> bool:
|
|
@@ -111,6 +111,16 @@ class ModelType(str, UnknownValuesEnumMixin, enum.Enum):
|
|
|
111
111
|
"""Return the devices key."""
|
|
112
112
|
return f"{self.value}s"
|
|
113
113
|
|
|
114
|
+
@cached_property
|
|
115
|
+
def name(self) -> str:
|
|
116
|
+
"""Return the name."""
|
|
117
|
+
return self._name_
|
|
118
|
+
|
|
119
|
+
@cached_property
|
|
120
|
+
def value(self) -> str:
|
|
121
|
+
"""Return the value."""
|
|
122
|
+
return self._value_
|
|
123
|
+
|
|
114
124
|
@classmethod
|
|
115
125
|
@cache
|
|
116
126
|
def from_string(cls, value: str) -> ModelType:
|
|
@@ -575,6 +585,16 @@ class PermissionNode(str, UnknownValuesEnumMixin, enum.Enum):
|
|
|
575
585
|
READ_LIVE = "readlive"
|
|
576
586
|
UNKNOWN = "unknown"
|
|
577
587
|
|
|
588
|
+
@cached_property
|
|
589
|
+
def name(self) -> str:
|
|
590
|
+
"""Return the name."""
|
|
591
|
+
return self._name_
|
|
592
|
+
|
|
593
|
+
@cached_property
|
|
594
|
+
def value(self) -> str:
|
|
595
|
+
"""Return the value."""
|
|
596
|
+
return self._value_
|
|
597
|
+
|
|
578
598
|
|
|
579
599
|
@enum.unique
|
|
580
600
|
class HDRMode(str, UnknownValuesEnumMixin, enum.Enum):
|
|
@@ -210,7 +210,7 @@ class User(ProtectModelWithId):
|
|
|
210
210
|
) -> bool:
|
|
211
211
|
"""Checks if a user can do a specific action"""
|
|
212
212
|
check_self = False
|
|
213
|
-
if model
|
|
213
|
+
if model is self.model and obj is not None and obj.id == self.id:
|
|
214
214
|
perm_str = f"{model.value}:{node.value}:$"
|
|
215
215
|
check_self = True
|
|
216
216
|
else:
|
|
@@ -221,7 +221,7 @@ class User(ProtectModelWithId):
|
|
|
221
221
|
return self._perm_cache[perm_str]
|
|
222
222
|
|
|
223
223
|
for perm in self.all_permissions:
|
|
224
|
-
if model
|
|
224
|
+
if model is not perm.model or node not in perm.nodes:
|
|
225
225
|
continue
|
|
226
226
|
if perm.obj_ids is None:
|
|
227
227
|
self._perm_cache[perm_str] = True
|
|
@@ -670,3 +670,8 @@ def make_required_getter(ufp_required_field: str) -> Callable[[T], bool]:
|
|
|
670
670
|
if "." not in ufp_required_field:
|
|
671
671
|
return partial(get_top_level_attr_as_bool, ufp_required_field)
|
|
672
672
|
return partial(get_nested_attr_as_bool, tuple(ufp_required_field.split(".")))
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
@lru_cache
|
|
676
|
+
def timedelta_total_seconds(td: timedelta) -> float:
|
|
677
|
+
return td.total_seconds()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|