uiprotect 0.13.0__py3-none-any.whl → 0.15.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 +4 -2
- uiprotect/data/base.py +4 -4
- uiprotect/data/bootstrap.py +3 -3
- uiprotect/data/devices.py +10 -10
- uiprotect/data/nvr.py +6 -7
- uiprotect/utils.py +4 -3
- {uiprotect-0.13.0.dist-info → uiprotect-0.15.0.dist-info}/METADATA +1 -1
- {uiprotect-0.13.0.dist-info → uiprotect-0.15.0.dist-info}/RECORD +11 -11
- {uiprotect-0.13.0.dist-info → uiprotect-0.15.0.dist-info}/LICENSE +0 -0
- {uiprotect-0.13.0.dist-info → uiprotect-0.15.0.dist-info}/WHEEL +0 -0
- {uiprotect-0.13.0.dist-info → uiprotect-0.15.0.dist-info}/entry_points.txt +0 -0
uiprotect/api.py
CHANGED
|
@@ -11,6 +11,7 @@ import sys
|
|
|
11
11
|
import time
|
|
12
12
|
from collections.abc import Callable
|
|
13
13
|
from datetime import datetime, timedelta
|
|
14
|
+
from functools import cached_property
|
|
14
15
|
from http.cookies import Morsel, SimpleCookie
|
|
15
16
|
from ipaddress import IPv4Address, IPv6Address
|
|
16
17
|
from pathlib import Path
|
|
@@ -749,10 +750,10 @@ class ProtectApiClient(BaseApiClient):
|
|
|
749
750
|
def is_ready(self) -> bool:
|
|
750
751
|
return self._bootstrap is not None
|
|
751
752
|
|
|
752
|
-
@
|
|
753
|
+
@cached_property
|
|
753
754
|
def bootstrap(self) -> Bootstrap:
|
|
754
755
|
if self._bootstrap is None:
|
|
755
|
-
raise BadRequest("Client not
|
|
756
|
+
raise BadRequest("Client not initialized, run `update` first")
|
|
756
757
|
|
|
757
758
|
return self._bootstrap
|
|
758
759
|
|
|
@@ -793,6 +794,7 @@ class ProtectApiClient(BaseApiClient):
|
|
|
793
794
|
if self._bootstrap is None or now - self._last_update > DEVICE_UPDATE_INTERVAL:
|
|
794
795
|
bootstrap_updated = True
|
|
795
796
|
self._bootstrap = await self.get_bootstrap()
|
|
797
|
+
self.__dict__.pop("bootstrap", None)
|
|
796
798
|
self._last_update = now
|
|
797
799
|
self._last_update_dt = now_dt
|
|
798
800
|
|
uiprotect/data/base.py
CHANGED
|
@@ -14,10 +14,10 @@ from uuid import UUID
|
|
|
14
14
|
from ..exceptions import BadRequest, ClientError, NotAuthorized
|
|
15
15
|
from ..utils import (
|
|
16
16
|
asyncio_timeout,
|
|
17
|
+
convert_to_datetime,
|
|
17
18
|
convert_unifi_data,
|
|
18
19
|
dict_diff,
|
|
19
20
|
is_debug,
|
|
20
|
-
process_datetime,
|
|
21
21
|
serialize_unifi_obj,
|
|
22
22
|
to_snake_case,
|
|
23
23
|
)
|
|
@@ -850,9 +850,9 @@ class ProtectDeviceModel(ProtectModelWithId):
|
|
|
850
850
|
@classmethod
|
|
851
851
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
852
852
|
if "lastSeen" in data:
|
|
853
|
-
data["lastSeen"] =
|
|
853
|
+
data["lastSeen"] = convert_to_datetime(data["lastSeen"])
|
|
854
854
|
if "upSince" in data and data["upSince"] is not None:
|
|
855
|
-
data["upSince"] =
|
|
855
|
+
data["upSince"] = convert_to_datetime(data["upSince"])
|
|
856
856
|
if (
|
|
857
857
|
"uptime" in data
|
|
858
858
|
and data["uptime"] is not None
|
|
@@ -1001,7 +1001,7 @@ class ProtectAdoptableDeviceModel(ProtectDeviceModel):
|
|
|
1001
1001
|
@classmethod
|
|
1002
1002
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
1003
1003
|
if "lastDisconnect" in data and data["lastDisconnect"] is not None:
|
|
1004
|
-
data["lastDisconnect"] =
|
|
1004
|
+
data["lastDisconnect"] = convert_to_datetime(data["lastDisconnect"])
|
|
1005
1005
|
|
|
1006
1006
|
return super().unifi_dict_to_dict(data)
|
|
1007
1007
|
|
uiprotect/data/bootstrap.py
CHANGED
|
@@ -220,7 +220,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
220
220
|
data["macLookup"] = {}
|
|
221
221
|
data["idLookup"] = {}
|
|
222
222
|
for model_type in ModelType.bootstrap_models():
|
|
223
|
-
key = model_type
|
|
223
|
+
key = f"{model_type}s"
|
|
224
224
|
items: dict[str, ProtectModel] = {}
|
|
225
225
|
for item in data[key]:
|
|
226
226
|
if (
|
|
@@ -257,7 +257,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
257
257
|
del data["idLookup"]
|
|
258
258
|
|
|
259
259
|
for model_type in ModelType.bootstrap_models():
|
|
260
|
-
attr = model_type
|
|
260
|
+
attr = f"{model_type}s"
|
|
261
261
|
if attr in data and isinstance(data[attr], dict):
|
|
262
262
|
data[attr] = list(data[attr].values())
|
|
263
263
|
|
|
@@ -387,7 +387,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
387
387
|
and obj.model is not None
|
|
388
388
|
and obj.model.value in ModelType.bootstrap_models_set()
|
|
389
389
|
):
|
|
390
|
-
key = obj.model.value
|
|
390
|
+
key = f"{obj.model.value}s"
|
|
391
391
|
if not self.api.ignore_unadopted or (
|
|
392
392
|
obj.is_adopted and not obj.is_adopted_by_other
|
|
393
393
|
):
|
uiprotect/data/devices.py
CHANGED
|
@@ -23,9 +23,9 @@ from ..utils import (
|
|
|
23
23
|
clamp_value,
|
|
24
24
|
convert_smart_audio_types,
|
|
25
25
|
convert_smart_types,
|
|
26
|
+
convert_to_datetime,
|
|
26
27
|
convert_video_modes,
|
|
27
28
|
from_js_time,
|
|
28
|
-
process_datetime,
|
|
29
29
|
serialize_point,
|
|
30
30
|
to_js_time,
|
|
31
31
|
utc_now,
|
|
@@ -494,7 +494,7 @@ class LCDMessage(ProtectBaseObject):
|
|
|
494
494
|
@classmethod
|
|
495
495
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
496
496
|
if "resetAt" in data:
|
|
497
|
-
data["resetAt"] =
|
|
497
|
+
data["resetAt"] = convert_to_datetime(data["resetAt"])
|
|
498
498
|
if "text" in data:
|
|
499
499
|
# UniFi Protect bug: some times LCD messages can get into a bad state where message = DEFAULT MESSAGE, but no type
|
|
500
500
|
if "type" not in data:
|
|
@@ -579,21 +579,21 @@ class VideoStats(ProtectBaseObject):
|
|
|
579
579
|
@classmethod
|
|
580
580
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
581
581
|
if "recordingStart" in data:
|
|
582
|
-
data["recordingStart"] =
|
|
582
|
+
data["recordingStart"] = convert_to_datetime(data["recordingStart"])
|
|
583
583
|
if "recordingEnd" in data:
|
|
584
|
-
data["recordingEnd"] =
|
|
584
|
+
data["recordingEnd"] = convert_to_datetime(data["recordingEnd"])
|
|
585
585
|
if "recordingStartLQ" in data:
|
|
586
|
-
data["recordingStartLQ"] =
|
|
586
|
+
data["recordingStartLQ"] = convert_to_datetime(data["recordingStartLQ"])
|
|
587
587
|
if "recordingEndLQ" in data:
|
|
588
|
-
data["recordingEndLQ"] =
|
|
588
|
+
data["recordingEndLQ"] = convert_to_datetime(data["recordingEndLQ"])
|
|
589
589
|
if "timelapseStart" in data:
|
|
590
|
-
data["timelapseStart"] =
|
|
590
|
+
data["timelapseStart"] = convert_to_datetime(data["timelapseStart"])
|
|
591
591
|
if "timelapseEnd" in data:
|
|
592
|
-
data["timelapseEnd"] =
|
|
592
|
+
data["timelapseEnd"] = convert_to_datetime(data["timelapseEnd"])
|
|
593
593
|
if "timelapseStartLQ" in data:
|
|
594
|
-
data["timelapseStartLQ"] =
|
|
594
|
+
data["timelapseStartLQ"] = convert_to_datetime(data["timelapseStartLQ"])
|
|
595
595
|
if "timelapseEndLQ" in data:
|
|
596
|
-
data["timelapseEndLQ"] =
|
|
596
|
+
data["timelapseEndLQ"] = convert_to_datetime(data["timelapseEndLQ"])
|
|
597
597
|
|
|
598
598
|
return super().unifi_dict_to_dict(data)
|
|
599
599
|
|
uiprotect/data/nvr.py
CHANGED
|
@@ -17,7 +17,7 @@ import orjson
|
|
|
17
17
|
from aiofiles import os as aos
|
|
18
18
|
|
|
19
19
|
from ..exceptions import BadRequest, NotAuthorized
|
|
20
|
-
from ..utils import RELEASE_CACHE,
|
|
20
|
+
from ..utils import RELEASE_CACHE, convert_to_datetime
|
|
21
21
|
from .base import (
|
|
22
22
|
ProtectBaseObject,
|
|
23
23
|
ProtectDeviceModel,
|
|
@@ -170,7 +170,7 @@ class EventDetectedThumbnail(ProtectBaseObject):
|
|
|
170
170
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
171
171
|
if "clockBestWall" in data:
|
|
172
172
|
if data["clockBestWall"]:
|
|
173
|
-
data["clockBestWall"] =
|
|
173
|
+
data["clockBestWall"] = convert_to_datetime(data["clockBestWall"])
|
|
174
174
|
else:
|
|
175
175
|
del data["clockBestWall"]
|
|
176
176
|
|
|
@@ -309,7 +309,7 @@ class Event(ProtectModelWithId):
|
|
|
309
309
|
@classmethod
|
|
310
310
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
311
311
|
for key in {"start", "end", "timestamp", "deletedAt"}.intersection(data):
|
|
312
|
-
data[key] =
|
|
312
|
+
data[key] = convert_to_datetime(data[key])
|
|
313
313
|
|
|
314
314
|
return super().unifi_dict_to_dict(data)
|
|
315
315
|
|
|
@@ -1025,11 +1025,10 @@ class NVR(ProtectDeviceModel):
|
|
|
1025
1025
|
@classmethod
|
|
1026
1026
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
1027
1027
|
if "lastUpdateAt" in data:
|
|
1028
|
-
data["lastUpdateAt"] =
|
|
1028
|
+
data["lastUpdateAt"] = convert_to_datetime(data["lastUpdateAt"])
|
|
1029
1029
|
if "lastDeviceFwUpdatesCheckedAt" in data:
|
|
1030
|
-
data["lastDeviceFwUpdatesCheckedAt"] =
|
|
1031
|
-
data
|
|
1032
|
-
"lastDeviceFwUpdatesCheckedAt",
|
|
1030
|
+
data["lastDeviceFwUpdatesCheckedAt"] = convert_to_datetime(
|
|
1031
|
+
data["lastDeviceFwUpdatesCheckedAt"]
|
|
1033
1032
|
)
|
|
1034
1033
|
if (
|
|
1035
1034
|
"recordingRetentionDurationMs" in data
|
uiprotect/utils.py
CHANGED
|
@@ -172,9 +172,10 @@ def from_js_time(num: float | str | datetime) -> datetime:
|
|
|
172
172
|
return datetime.fromtimestamp(int(num) / 1000, tz=timezone.utc)
|
|
173
173
|
|
|
174
174
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
175
|
+
@lru_cache(maxsize=1024)
|
|
176
|
+
def convert_to_datetime(source_time: float | str | datetime | None) -> datetime | None:
|
|
177
|
+
"""Converts timestamp to datetime object"""
|
|
178
|
+
return None if source_time is None else from_js_time(source_time)
|
|
178
179
|
|
|
179
180
|
|
|
180
181
|
def format_datetime(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
uiprotect/__init__.py,sha256=llnQNtiBfwQG8IkQXovvFz4LZeFjrJx7XdmmUhu3a9E,289
|
|
2
2
|
uiprotect/__main__.py,sha256=C_bHCOkv5qj6WMy-6ELoY3Y6HDhLxOa1a30CzmbZhsg,462
|
|
3
|
-
uiprotect/api.py,sha256=
|
|
3
|
+
uiprotect/api.py,sha256=83XjDcYb-Nnc72X77ujGUg4wskugPkPDIm-wFYni_-E,66253
|
|
4
4
|
uiprotect/cli/__init__.py,sha256=sSLW9keVQOkgFcMW18HTDjRrt9sJ0KWjn9DJDA6f9Pc,8658
|
|
5
5
|
uiprotect/cli/backup.py,sha256=ZiS7RZnJGKI8TJKLW2cOUzkRM8nyTvE5Ov_jZZGtvSM,36708
|
|
6
6
|
uiprotect/cli/base.py,sha256=zpTm2kyJe_GLixnv3Uadke__iRLh64AEwQzp-2hqS7g,7730
|
|
@@ -14,11 +14,11 @@ 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=TkhnYoHxYFmtg30rFOYQjmk7uw2Ky7dMSS-cV6FGLnw,37190
|
|
18
|
+
uiprotect/data/bootstrap.py,sha256=cFp8PmsRLcz_hKFn50n2BBPNZcnI_RJvEF9GYEXKPOg,21543
|
|
19
19
|
uiprotect/data/convert.py,sha256=rOQplUMIdTMD2SbAx_iI9BNPDscnhDvyRVLEMDhtADg,2047
|
|
20
|
-
uiprotect/data/devices.py,sha256=
|
|
21
|
-
uiprotect/data/nvr.py,sha256=
|
|
20
|
+
uiprotect/data/devices.py,sha256=yx30uYnAlzabVhMohdDkO6LMgEuC7kSwUJbX5LWXHnA,111735
|
|
21
|
+
uiprotect/data/nvr.py,sha256=At4N2trpfMk-wMXViBe7Lp_a5ML4JnOXOnDIVr3vPDI,47568
|
|
22
22
|
uiprotect/data/types.py,sha256=6Z5ZqWTbH4Igy0l4QJShqQZ_zvrJKD0G-hZLjoBNP-U,16193
|
|
23
23
|
uiprotect/data/user.py,sha256=yBnUQ3qpHL745hLhR41WjWv_Yx51RlmfHapgvK0KSgM,7067
|
|
24
24
|
uiprotect/data/websocket.py,sha256=WZJVA7EfYuKYMv-9jmvGgMWXKzE9ES25SKv1NQ2eHjc,6281
|
|
@@ -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=6OLY8hNiCzk418PjJJIlFW7jjPzVt1vxBKEzBSqMeTk,18418
|
|
32
32
|
uiprotect/websocket.py,sha256=iMTdchymaCgVHsmY1bRbxkcymqt6WQircIHYNxCu178,7289
|
|
33
|
-
uiprotect-0.
|
|
34
|
-
uiprotect-0.
|
|
35
|
-
uiprotect-0.
|
|
36
|
-
uiprotect-0.
|
|
37
|
-
uiprotect-0.
|
|
33
|
+
uiprotect-0.15.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
|
|
34
|
+
uiprotect-0.15.0.dist-info/METADATA,sha256=xDeX7-p5udgEtYLBKVP5E7GX0R5aotahnvZBbLxAN_w,10985
|
|
35
|
+
uiprotect-0.15.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
36
|
+
uiprotect-0.15.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
|
|
37
|
+
uiprotect-0.15.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|