uiprotect 3.3.0__tar.gz → 3.4.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-3.3.0 → uiprotect-3.4.0}/PKG-INFO +1 -2
- {uiprotect-3.3.0 → uiprotect-3.4.0}/pyproject.toml +3 -3
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/data/base.py +33 -22
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/data/devices.py +74 -74
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/data/nvr.py +45 -62
- {uiprotect-3.3.0 → uiprotect-3.4.0}/LICENSE +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/README.md +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/__init__.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/__main__.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/api.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/cli/__init__.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/cli/backup.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/cli/base.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/cli/cameras.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/cli/chimes.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/cli/doorlocks.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/cli/events.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/cli/lights.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/cli/liveviews.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/cli/nvr.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/cli/sensors.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/cli/viewers.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/data/__init__.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/data/bootstrap.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/data/convert.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/data/types.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/data/user.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/data/websocket.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/exceptions.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/py.typed +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/release_cache.json +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/stream.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/test_util/__init__.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/test_util/anonymize.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/utils.py +0 -0
- {uiprotect-3.3.0 → uiprotect-3.4.0}/src/uiprotect/websocket.py +0 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: uiprotect
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.4.0
|
|
4
4
|
Summary: Python API for Unifi Protect (Unofficial)
|
|
5
5
|
Home-page: https://github.com/uilibs/uiprotect
|
|
6
|
-
License: MIT
|
|
7
6
|
Author: UI Protect Maintainers
|
|
8
7
|
Author-email: ui@koston.org
|
|
9
8
|
Requires-Python: >=3.10
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "uiprotect"
|
|
3
|
-
version = "3.
|
|
3
|
+
version = "3.4.0"
|
|
4
4
|
description = "Python API for Unifi Protect (Unofficial)"
|
|
5
5
|
authors = ["UI Protect Maintainers <ui@koston.org>"]
|
|
6
|
-
license = "MIT"
|
|
7
6
|
readme = "README.md"
|
|
8
7
|
repository = "https://github.com/uilibs/uiprotect"
|
|
9
8
|
documentation = "https://uiprotect.readthedocs.io"
|
|
@@ -16,7 +15,8 @@ classifiers = [
|
|
|
16
15
|
"Development Status :: 5 - Production/Stable",
|
|
17
16
|
"Programming Language :: Python :: 3.10",
|
|
18
17
|
"Programming Language :: Python :: 3.11",
|
|
19
|
-
"Programming Language :: Python :: 3.12"
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"License :: OSI Approved :: MIT License"
|
|
20
20
|
]
|
|
21
21
|
packages = [
|
|
22
22
|
{ include = "uiprotect", from = "src" },
|
|
@@ -275,6 +275,19 @@ class ProtectBaseObject(BaseModel):
|
|
|
275
275
|
items[key] = cls._clean_protect_obj(value, klass, api)
|
|
276
276
|
return items
|
|
277
277
|
|
|
278
|
+
@classmethod
|
|
279
|
+
@cache
|
|
280
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
281
|
+
"""
|
|
282
|
+
Helper method for overriding in child classes for converting UFP JSON data to Python data types.
|
|
283
|
+
|
|
284
|
+
Return format is
|
|
285
|
+
{
|
|
286
|
+
"ufpJsonName": Callable[[Any], Any]
|
|
287
|
+
}
|
|
288
|
+
"""
|
|
289
|
+
return {}
|
|
290
|
+
|
|
278
291
|
@classmethod
|
|
279
292
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
280
293
|
"""
|
|
@@ -295,6 +308,11 @@ class ProtectBaseObject(BaseModel):
|
|
|
295
308
|
cls._api if isinstance(cls, ProtectBaseObject) else None
|
|
296
309
|
)
|
|
297
310
|
|
|
311
|
+
conversions = cls.unifi_dict_conversions()
|
|
312
|
+
for key, convert in conversions.items():
|
|
313
|
+
if (val := data.get(key)) is not None:
|
|
314
|
+
data[key] = convert(val) # type: ignore[operator]
|
|
315
|
+
|
|
298
316
|
remaps = cls._get_unifi_remaps()
|
|
299
317
|
# convert to snake_case and remove extra fields
|
|
300
318
|
_fields = cls.__fields__
|
|
@@ -810,23 +828,16 @@ class ProtectDeviceModel(ProtectModelWithId):
|
|
|
810
828
|
}
|
|
811
829
|
|
|
812
830
|
@classmethod
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
)
|
|
823
|
-
data["uptime"] = timedelta(milliseconds=int(data["uptime"]))
|
|
824
|
-
# hardware revisions for all devices are not simple numbers
|
|
825
|
-
# so cast them all to str to be consistent
|
|
826
|
-
if "hardwareRevision" in data and data["hardwareRevision"] is not None:
|
|
827
|
-
data["hardwareRevision"] = str(data["hardwareRevision"])
|
|
828
|
-
|
|
829
|
-
return super().unifi_dict_to_dict(data)
|
|
831
|
+
@cache
|
|
832
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
833
|
+
return {
|
|
834
|
+
"upSince": convert_to_datetime,
|
|
835
|
+
"uptime": lambda x: timedelta(milliseconds=int(x)),
|
|
836
|
+
"lastSeen": convert_to_datetime,
|
|
837
|
+
# hardware revisions for all devices are not simple numbers
|
|
838
|
+
# so cast them all to str to be consistent
|
|
839
|
+
"hardwareRevision": str,
|
|
840
|
+
} | super().unifi_dict_conversions()
|
|
830
841
|
|
|
831
842
|
def _event_callback_ping(self) -> None:
|
|
832
843
|
_LOGGER.debug("Event ping timer started for %s", self.id)
|
|
@@ -958,11 +969,11 @@ class ProtectAdoptableDeviceModel(ProtectDeviceModel):
|
|
|
958
969
|
return data
|
|
959
970
|
|
|
960
971
|
@classmethod
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
972
|
+
@cache
|
|
973
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
974
|
+
return {
|
|
975
|
+
"lastDisconnect": convert_to_datetime,
|
|
976
|
+
} | super().unifi_dict_conversions()
|
|
966
977
|
|
|
967
978
|
@property
|
|
968
979
|
def display_name(self) -> str:
|
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import logging
|
|
7
7
|
import warnings
|
|
8
|
-
from collections.abc import
|
|
8
|
+
from collections.abc import Callable
|
|
9
9
|
from datetime import datetime, timedelta
|
|
10
10
|
from functools import cache
|
|
11
11
|
from ipaddress import IPv4Address
|
|
@@ -107,11 +107,11 @@ class LightDeviceSettings(ProtectBaseObject):
|
|
|
107
107
|
pir_sensitivity: PercentInt
|
|
108
108
|
|
|
109
109
|
@classmethod
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
110
|
+
@cache
|
|
111
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
112
|
+
return {
|
|
113
|
+
"pirDuration": lambda x: timedelta(milliseconds=x)
|
|
114
|
+
} | super().unifi_dict_conversions()
|
|
115
115
|
|
|
116
116
|
|
|
117
117
|
class LightOnSettings(ProtectBaseObject):
|
|
@@ -400,6 +400,14 @@ class RecordingSettings(ProtectBaseObject):
|
|
|
400
400
|
"retentionDurationMs": "retentionDuration",
|
|
401
401
|
}
|
|
402
402
|
|
|
403
|
+
@classmethod
|
|
404
|
+
@cache
|
|
405
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
406
|
+
return {
|
|
407
|
+
"minMotionEventTrigger": lambda x: timedelta(seconds=x),
|
|
408
|
+
"endMotionEventDelay": lambda x: timedelta(seconds=x),
|
|
409
|
+
} | super().unifi_dict_conversions()
|
|
410
|
+
|
|
403
411
|
@classmethod
|
|
404
412
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
405
413
|
if "prePaddingSecs" in data:
|
|
@@ -414,18 +422,6 @@ class RecordingSettings(ProtectBaseObject):
|
|
|
414
422
|
data["smartDetectPostPadding"] = timedelta(
|
|
415
423
|
seconds=data.pop("smartDetectPostPaddingSecs"),
|
|
416
424
|
)
|
|
417
|
-
if "minMotionEventTrigger" in data and not isinstance(
|
|
418
|
-
data["minMotionEventTrigger"],
|
|
419
|
-
timedelta,
|
|
420
|
-
):
|
|
421
|
-
data["minMotionEventTrigger"] = timedelta(
|
|
422
|
-
seconds=data["minMotionEventTrigger"],
|
|
423
|
-
)
|
|
424
|
-
if "endMotionEventDelay" in data and not isinstance(
|
|
425
|
-
data["endMotionEventDelay"],
|
|
426
|
-
timedelta,
|
|
427
|
-
):
|
|
428
|
-
data["endMotionEventDelay"] = timedelta(seconds=data["endMotionEventDelay"])
|
|
429
425
|
|
|
430
426
|
return super().unifi_dict_to_dict(data)
|
|
431
427
|
|
|
@@ -469,14 +465,13 @@ class SmartDetectSettings(ProtectBaseObject):
|
|
|
469
465
|
auto_tracking_object_types: list[SmartDetectObjectType] | None = None
|
|
470
466
|
|
|
471
467
|
@classmethod
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
return super().unifi_dict_to_dict(data)
|
|
468
|
+
@cache
|
|
469
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
470
|
+
return {
|
|
471
|
+
"audioTypes": convert_smart_audio_types,
|
|
472
|
+
"objectTypes": convert_smart_types,
|
|
473
|
+
"autoTrackingObjectTypes": convert_smart_types,
|
|
474
|
+
} | super().unifi_dict_conversions()
|
|
480
475
|
|
|
481
476
|
|
|
482
477
|
class LCDMessage(ProtectBaseObject):
|
|
@@ -484,10 +479,15 @@ class LCDMessage(ProtectBaseObject):
|
|
|
484
479
|
text: str
|
|
485
480
|
reset_at: datetime | None = None
|
|
486
481
|
|
|
482
|
+
@classmethod
|
|
483
|
+
@cache
|
|
484
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
485
|
+
return {
|
|
486
|
+
"resetAt": convert_to_datetime,
|
|
487
|
+
} | super().unifi_dict_conversions()
|
|
488
|
+
|
|
487
489
|
@classmethod
|
|
488
490
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
489
|
-
if "resetAt" in data:
|
|
490
|
-
data["resetAt"] = convert_to_datetime(data["resetAt"])
|
|
491
491
|
if "text" in data:
|
|
492
492
|
# UniFi Protect bug: some times LCD messages can get into a bad state where message = DEFAULT MESSAGE, but no type
|
|
493
493
|
if "type" not in data:
|
|
@@ -570,21 +570,21 @@ class VideoStats(ProtectBaseObject):
|
|
|
570
570
|
}
|
|
571
571
|
|
|
572
572
|
@classmethod
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
573
|
+
@cache
|
|
574
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
575
|
+
return {
|
|
576
|
+
key: convert_to_datetime
|
|
577
|
+
for key in (
|
|
578
|
+
"recordingStart",
|
|
579
|
+
"recordingEnd",
|
|
580
|
+
"recordingStartLQ",
|
|
581
|
+
"recordingEndLQ",
|
|
582
|
+
"timelapseStart",
|
|
583
|
+
"timelapseEnd",
|
|
584
|
+
"timelapseStartLQ",
|
|
585
|
+
"timelapseEndLQ",
|
|
586
|
+
)
|
|
587
|
+
} | super().unifi_dict_conversions()
|
|
588
588
|
|
|
589
589
|
|
|
590
590
|
class StorageStats(ProtectBaseObject):
|
|
@@ -654,12 +654,11 @@ class CameraZone(ProtectBaseObject):
|
|
|
654
654
|
points: list[tuple[Percent, Percent]]
|
|
655
655
|
|
|
656
656
|
@classmethod
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
return data
|
|
657
|
+
@cache
|
|
658
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
659
|
+
return {
|
|
660
|
+
"points": lambda x: [(p[0], p[1]) for p in x],
|
|
661
|
+
} | super().unifi_dict_conversions()
|
|
663
662
|
|
|
664
663
|
def unifi_dict(
|
|
665
664
|
self,
|
|
@@ -691,11 +690,11 @@ class SmartMotionZone(MotionZone):
|
|
|
691
690
|
object_types: list[SmartDetectObjectType]
|
|
692
691
|
|
|
693
692
|
@classmethod
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
693
|
+
@cache
|
|
694
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
695
|
+
return {
|
|
696
|
+
"objectTypes": convert_smart_types,
|
|
697
|
+
} | super().unifi_dict_conversions()
|
|
699
698
|
|
|
700
699
|
|
|
701
700
|
class PrivacyMaskCapability(ProtectBaseObject):
|
|
@@ -846,16 +845,16 @@ class CameraFeatureFlags(ProtectBaseObject):
|
|
|
846
845
|
zoom: PTZZoomRange
|
|
847
846
|
|
|
848
847
|
@classmethod
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
if "videoModes" in data:
|
|
857
|
-
data["videoModes"] = convert_video_modes(data.pop("videoModes"))
|
|
848
|
+
@cache
|
|
849
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
850
|
+
return {
|
|
851
|
+
"smartDetectTypes": convert_smart_types,
|
|
852
|
+
"smartDetectAudioTypes": convert_smart_audio_types,
|
|
853
|
+
"videoModes": convert_video_modes,
|
|
854
|
+
} | super().unifi_dict_conversions()
|
|
858
855
|
|
|
856
|
+
@classmethod
|
|
857
|
+
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
859
858
|
# backport support for `is_doorbell` to older versions of Protect
|
|
860
859
|
if "hasChime" in data and "isDoorbell" not in data:
|
|
861
860
|
data["isDoorbell"] = data["hasChime"]
|
|
@@ -1020,14 +1019,18 @@ class Camera(ProtectMotionDeviceModel):
|
|
|
1020
1019
|
"featureFlags",
|
|
1021
1020
|
}
|
|
1022
1021
|
|
|
1022
|
+
@classmethod
|
|
1023
|
+
@cache
|
|
1024
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
1025
|
+
return {
|
|
1026
|
+
"chimeDuration": lambda x: timedelta(milliseconds=x),
|
|
1027
|
+
} | super().unifi_dict_conversions()
|
|
1028
|
+
|
|
1023
1029
|
@classmethod
|
|
1024
1030
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
1025
1031
|
# LCD messages comes back as empty dict {}
|
|
1026
1032
|
if "lcdMessage" in data and len(data["lcdMessage"]) == 0:
|
|
1027
1033
|
del data["lcdMessage"]
|
|
1028
|
-
if "chimeDuration" in data and not isinstance(data["chimeDuration"], timedelta):
|
|
1029
|
-
data["chimeDuration"] = timedelta(milliseconds=data["chimeDuration"])
|
|
1030
|
-
|
|
1031
1034
|
return super().unifi_dict_to_dict(data)
|
|
1032
1035
|
|
|
1033
1036
|
def unifi_dict(
|
|
@@ -3086,14 +3089,11 @@ class Doorlock(ProtectAdoptableDeviceModel):
|
|
|
3086
3089
|
}
|
|
3087
3090
|
|
|
3088
3091
|
@classmethod
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
timedelta
|
|
3093
|
-
)
|
|
3094
|
-
data["autoCloseTimeMs"] = timedelta(milliseconds=data["autoCloseTimeMs"])
|
|
3095
|
-
|
|
3096
|
-
return super().unifi_dict_to_dict(data)
|
|
3092
|
+
@cache
|
|
3093
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
3094
|
+
return {
|
|
3095
|
+
"autoCloseTimeMs": lambda x: timedelta(milliseconds=x)
|
|
3096
|
+
} | super().unifi_dict_conversions()
|
|
3097
3097
|
|
|
3098
3098
|
@property
|
|
3099
3099
|
def camera(self) -> Camera | None:
|
|
@@ -94,11 +94,11 @@ class SmartDetectItem(ProtectBaseObject):
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
@classmethod
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
@cache
|
|
98
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
99
|
+
return {
|
|
100
|
+
"duration": lambda x: timedelta(milliseconds=x),
|
|
101
|
+
} | super().unifi_dict_conversions()
|
|
102
102
|
|
|
103
103
|
|
|
104
104
|
class SmartDetectTrack(ProtectBaseObject):
|
|
@@ -161,14 +161,9 @@ class EventDetectedThumbnail(ProtectBaseObject):
|
|
|
161
161
|
name: str | None
|
|
162
162
|
|
|
163
163
|
@classmethod
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
data["clockBestWall"] = convert_to_datetime(data["clockBestWall"])
|
|
168
|
-
else:
|
|
169
|
-
del data["clockBestWall"]
|
|
170
|
-
|
|
171
|
-
return super().unifi_dict_to_dict(data)
|
|
164
|
+
@cache
|
|
165
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
166
|
+
return {"clockBestWall": convert_to_datetime} | super().unifi_dict_conversions()
|
|
172
167
|
|
|
173
168
|
def unifi_dict(
|
|
174
169
|
self,
|
|
@@ -301,11 +296,12 @@ class Event(ProtectModelWithId):
|
|
|
301
296
|
}
|
|
302
297
|
|
|
303
298
|
@classmethod
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
299
|
+
@cache
|
|
300
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
301
|
+
return {
|
|
302
|
+
key: convert_to_datetime
|
|
303
|
+
for key in ("start", "end", "timestamp", "deletedAt")
|
|
304
|
+
} | super().unifi_dict_conversions()
|
|
309
305
|
|
|
310
306
|
def unifi_dict(
|
|
311
307
|
self,
|
|
@@ -618,11 +614,11 @@ class UOSDisk(ProtectBaseObject):
|
|
|
618
614
|
}
|
|
619
615
|
|
|
620
616
|
@classmethod
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
617
|
+
@cache
|
|
618
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
619
|
+
return {
|
|
620
|
+
"estimate": lambda x: timedelta(seconds=x)
|
|
621
|
+
} | super().unifi_dict_conversions()
|
|
626
622
|
|
|
627
623
|
def unifi_dict(
|
|
628
624
|
self,
|
|
@@ -702,11 +698,11 @@ class UOSSpace(ProtectBaseObject):
|
|
|
702
698
|
}
|
|
703
699
|
|
|
704
700
|
@classmethod
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
701
|
+
@cache
|
|
702
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
703
|
+
return {
|
|
704
|
+
"estimate": lambda x: timedelta(seconds=x)
|
|
705
|
+
} | super().unifi_dict_conversions()
|
|
710
706
|
|
|
711
707
|
def unifi_dict(
|
|
712
708
|
self,
|
|
@@ -770,13 +766,12 @@ class DoorbellSettings(ProtectBaseObject):
|
|
|
770
766
|
}
|
|
771
767
|
|
|
772
768
|
@classmethod
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
)
|
|
778
|
-
|
|
779
|
-
return super().unifi_dict_to_dict(data)
|
|
769
|
+
@cache
|
|
770
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
771
|
+
return {
|
|
772
|
+
# defaultMessageResetTimeoutMs is remapped to defaultMessageResetTimeout
|
|
773
|
+
"defaultMessageResetTimeoutMs": lambda x: timedelta(milliseconds=x),
|
|
774
|
+
} | super().unifi_dict_conversions()
|
|
780
775
|
|
|
781
776
|
|
|
782
777
|
class RecordingTypeDistribution(ProtectBaseObject):
|
|
@@ -864,15 +859,12 @@ class StorageStats(ProtectBaseObject):
|
|
|
864
859
|
storage_distribution: StorageDistribution
|
|
865
860
|
|
|
866
861
|
@classmethod
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
)
|
|
874
|
-
|
|
875
|
-
return super().unifi_dict_to_dict(data)
|
|
862
|
+
@cache
|
|
863
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
864
|
+
return {
|
|
865
|
+
"capacity": lambda x: timedelta(milliseconds=x),
|
|
866
|
+
"remainingCapacity": lambda x: timedelta(milliseconds=x),
|
|
867
|
+
} | super().unifi_dict_conversions()
|
|
876
868
|
|
|
877
869
|
|
|
878
870
|
class NVRFeatureFlags(ProtectBaseObject):
|
|
@@ -1017,24 +1009,15 @@ class NVR(ProtectDeviceModel):
|
|
|
1017
1009
|
}
|
|
1018
1010
|
|
|
1019
1011
|
@classmethod
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
and data["recordingRetentionDurationMs"] is not None
|
|
1030
|
-
):
|
|
1031
|
-
data["recordingRetentionDuration"] = timedelta(
|
|
1032
|
-
milliseconds=data.pop("recordingRetentionDurationMs"),
|
|
1033
|
-
)
|
|
1034
|
-
if "timezone" in data and not isinstance(data["timezone"], tzinfo):
|
|
1035
|
-
data["timezone"] = zoneinfo.ZoneInfo(data["timezone"])
|
|
1036
|
-
|
|
1037
|
-
return super().unifi_dict_to_dict(data)
|
|
1012
|
+
@cache
|
|
1013
|
+
def unifi_dict_conversions(cls) -> dict[str, object | Callable[[Any], Any]]:
|
|
1014
|
+
return {
|
|
1015
|
+
"lastUpdateAt": convert_to_datetime,
|
|
1016
|
+
"lastDeviceFwUpdatesCheckedAt": convert_to_datetime,
|
|
1017
|
+
"timezone": zoneinfo.ZoneInfo,
|
|
1018
|
+
# recordingRetentionDurationMs is remapped to recordingRetentionDuration
|
|
1019
|
+
"recordingRetentionDurationMs": lambda x: timedelta(milliseconds=x),
|
|
1020
|
+
} | super().unifi_dict_conversions()
|
|
1038
1021
|
|
|
1039
1022
|
async def _api_update(self, data: dict[str, Any]) -> None:
|
|
1040
1023
|
return await self._api.update_nvr(data)
|
|
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
|
|
File without changes
|
|
File without changes
|