uiprotect 1.16.0__tar.gz → 1.18.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-1.16.0 → uiprotect-1.18.0}/PKG-INFO +1 -1
- {uiprotect-1.16.0 → uiprotect-1.18.0}/pyproject.toml +1 -1
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/data/base.py +10 -33
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/data/bootstrap.py +1 -7
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/data/devices.py +38 -56
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/data/nvr.py +3 -3
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/data/websocket.py +19 -11
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/utils.py +4 -6
- {uiprotect-1.16.0 → uiprotect-1.18.0}/LICENSE +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/README.md +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/__init__.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/__main__.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/api.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/cli/__init__.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/cli/backup.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/cli/base.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/cli/cameras.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/cli/chimes.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/cli/doorlocks.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/cli/events.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/cli/lights.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/cli/liveviews.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/cli/nvr.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/cli/sensors.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/cli/viewers.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/data/__init__.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/data/convert.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/data/types.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/data/user.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/exceptions.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/py.typed +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/release_cache.json +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/stream.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/test_util/__init__.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/test_util/anonymize.py +0 -0
- {uiprotect-1.16.0 → uiprotect-1.18.0}/src/uiprotect/websocket.py +0 -0
|
@@ -185,12 +185,6 @@ class ProtectBaseObject(BaseModel):
|
|
|
185
185
|
"""
|
|
186
186
|
return {}
|
|
187
187
|
|
|
188
|
-
@classmethod
|
|
189
|
-
@cache
|
|
190
|
-
def _get_unifi_remaps_set(self) -> set[str]:
|
|
191
|
-
"""Helper method to get set of all child UFP objects."""
|
|
192
|
-
return set(self._get_unifi_remaps())
|
|
193
|
-
|
|
194
188
|
@classmethod
|
|
195
189
|
@cache
|
|
196
190
|
def _get_to_unifi_remaps(cls) -> dict[str, str]:
|
|
@@ -238,9 +232,9 @@ class ProtectBaseObject(BaseModel):
|
|
|
238
232
|
|
|
239
233
|
@classmethod
|
|
240
234
|
@cache
|
|
241
|
-
def
|
|
242
|
-
"""Helper method to get all
|
|
243
|
-
return set(cls._get_protect_objs())
|
|
235
|
+
def _get_excluded_fields(cls) -> set[str]:
|
|
236
|
+
"""Helper method to get all excluded fields for the current object."""
|
|
237
|
+
return set(cls._get_protect_objs()) | set(cls._get_protect_lists())
|
|
244
238
|
|
|
245
239
|
@classmethod
|
|
246
240
|
@cache
|
|
@@ -251,12 +245,6 @@ class ProtectBaseObject(BaseModel):
|
|
|
251
245
|
assert cls._protect_lists is not None
|
|
252
246
|
return cls._protect_lists
|
|
253
247
|
|
|
254
|
-
@classmethod
|
|
255
|
-
@cache
|
|
256
|
-
def _get_protect_lists_set(cls) -> set[str]:
|
|
257
|
-
"""Helper method to get all child UFP objects"""
|
|
258
|
-
return set(cls._get_protect_lists())
|
|
259
|
-
|
|
260
248
|
@classmethod
|
|
261
249
|
@cache
|
|
262
250
|
def _get_protect_dicts(cls) -> dict[str, type[ProtectBaseObject]]:
|
|
@@ -266,12 +254,6 @@ class ProtectBaseObject(BaseModel):
|
|
|
266
254
|
assert cls._protect_dicts is not None
|
|
267
255
|
return cls._protect_dicts
|
|
268
256
|
|
|
269
|
-
@classmethod
|
|
270
|
-
@cache
|
|
271
|
-
def _get_protect_dicts_set(cls) -> set[str]:
|
|
272
|
-
"""Helper method to get all child UFP objects"""
|
|
273
|
-
return set(cls._get_protect_dicts())
|
|
274
|
-
|
|
275
257
|
@classmethod
|
|
276
258
|
def _clean_protect_obj(
|
|
277
259
|
cls,
|
|
@@ -448,9 +430,7 @@ class ProtectBaseObject(BaseModel):
|
|
|
448
430
|
"""
|
|
449
431
|
use_obj = False
|
|
450
432
|
if data is None:
|
|
451
|
-
excluded_fields = (
|
|
452
|
-
self._get_protect_objs_set() | self._get_protect_lists_set()
|
|
453
|
-
)
|
|
433
|
+
excluded_fields = self._get_excluded_fields()
|
|
454
434
|
if exclude is not None:
|
|
455
435
|
excluded_fields |= exclude
|
|
456
436
|
data = self.dict(exclude=excluded_fields)
|
|
@@ -965,16 +945,13 @@ class ProtectAdoptableDeviceModel(ProtectDeviceModel):
|
|
|
965
945
|
exclude: set[str] | None = None,
|
|
966
946
|
) -> dict[str, Any]:
|
|
967
947
|
data = super().unifi_dict(data=data, exclude=exclude)
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
del data["wifiConnectionState"]
|
|
973
|
-
if (
|
|
974
|
-
"bluetoothConnectionState" in data
|
|
975
|
-
and data["bluetoothConnectionState"] is None
|
|
948
|
+
for key in (
|
|
949
|
+
"wiredConnectionState",
|
|
950
|
+
"wifiConnectionState",
|
|
951
|
+
"bluetoothConnectionState",
|
|
976
952
|
):
|
|
977
|
-
|
|
953
|
+
if key in data and data[key] is None:
|
|
954
|
+
del data[key]
|
|
978
955
|
return data
|
|
979
956
|
|
|
980
957
|
@classmethod
|
|
@@ -8,7 +8,6 @@ from collections.abc import Iterable
|
|
|
8
8
|
from copy import deepcopy
|
|
9
9
|
from dataclasses import dataclass
|
|
10
10
|
from datetime import datetime
|
|
11
|
-
from functools import cache
|
|
12
11
|
from typing import TYPE_CHECKING, Any
|
|
13
12
|
|
|
14
13
|
from aiohttp.client_exceptions import ServerDisconnectedError
|
|
@@ -220,11 +219,6 @@ class Bootstrap(ProtectBaseObject):
|
|
|
220
219
|
|
|
221
220
|
return super().unifi_dict_to_dict(data)
|
|
222
221
|
|
|
223
|
-
@classmethod
|
|
224
|
-
@cache
|
|
225
|
-
def _unifi_dict_remove_keys(cls) -> set[str]:
|
|
226
|
-
return {"events", "captureWsStats", "macLookup", "idLookup"}
|
|
227
|
-
|
|
228
222
|
def unifi_dict(
|
|
229
223
|
self,
|
|
230
224
|
data: dict[str, Any] | None = None,
|
|
@@ -232,7 +226,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
232
226
|
) -> dict[str, Any]:
|
|
233
227
|
data = super().unifi_dict(data=data, exclude=exclude)
|
|
234
228
|
|
|
235
|
-
for key in
|
|
229
|
+
for key in ("events", "captureWsStats", "macLookup", "idLookup"):
|
|
236
230
|
if key in data:
|
|
237
231
|
del data[key]
|
|
238
232
|
for model_type in ModelType.bootstrap_models_types_set:
|
|
@@ -474,14 +474,9 @@ class SmartDetectSettings(ProtectBaseObject):
|
|
|
474
474
|
|
|
475
475
|
@classmethod
|
|
476
476
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
data["audioTypes"] = convert_smart_audio_types(data.pop("audioTypes"))
|
|
481
|
-
if "autoTrackingObjectTypes" in data:
|
|
482
|
-
data["autoTrackingObjectTypes"] = convert_smart_types(
|
|
483
|
-
data.pop("autoTrackingObjectTypes"),
|
|
484
|
-
)
|
|
477
|
+
for key in ("objectTypes", "audioTypes", "autoTrackingObjectTypes"):
|
|
478
|
+
if key in data:
|
|
479
|
+
data[key] = convert_smart_types(data[key])
|
|
485
480
|
|
|
486
481
|
return super().unifi_dict_to_dict(data)
|
|
487
482
|
|
|
@@ -578,22 +573,18 @@ class VideoStats(ProtectBaseObject):
|
|
|
578
573
|
|
|
579
574
|
@classmethod
|
|
580
575
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
if "timelapseStartLQ" in data:
|
|
594
|
-
data["timelapseStartLQ"] = convert_to_datetime(data["timelapseStartLQ"])
|
|
595
|
-
if "timelapseEndLQ" in data:
|
|
596
|
-
data["timelapseEndLQ"] = convert_to_datetime(data["timelapseEndLQ"])
|
|
576
|
+
for key in (
|
|
577
|
+
"recordingStart",
|
|
578
|
+
"recordingEnd",
|
|
579
|
+
"recordingStartLQ",
|
|
580
|
+
"recordingEndLQ",
|
|
581
|
+
"timelapseStart",
|
|
582
|
+
"timelapseEnd",
|
|
583
|
+
"timelapseStartLQ",
|
|
584
|
+
"timelapseEndLQ",
|
|
585
|
+
):
|
|
586
|
+
if key in data:
|
|
587
|
+
data[key] = convert_to_datetime(data[key])
|
|
597
588
|
|
|
598
589
|
return super().unifi_dict_to_dict(data)
|
|
599
590
|
|
|
@@ -1062,27 +1053,21 @@ class Camera(ProtectMotionDeviceModel):
|
|
|
1062
1053
|
]
|
|
1063
1054
|
|
|
1064
1055
|
data = super().unifi_dict(data=data, exclude=exclude)
|
|
1056
|
+
for key in (
|
|
1057
|
+
"lastRingEventId",
|
|
1058
|
+
"lastSmartDetect",
|
|
1059
|
+
"lastSmartAudioDetect",
|
|
1060
|
+
"lastSmartDetectEventId",
|
|
1061
|
+
"lastSmartAudioDetectEventId",
|
|
1062
|
+
"lastSmartDetects",
|
|
1063
|
+
"lastSmartAudioDetects",
|
|
1064
|
+
"lastSmartDetectEventIds",
|
|
1065
|
+
"lastSmartAudioDetectEventIds",
|
|
1066
|
+
"talkbackStream",
|
|
1067
|
+
):
|
|
1068
|
+
if key in data:
|
|
1069
|
+
del data[key]
|
|
1065
1070
|
|
|
1066
|
-
if "lastRingEventId" in data:
|
|
1067
|
-
del data["lastRingEventId"]
|
|
1068
|
-
if "lastSmartDetect" in data:
|
|
1069
|
-
del data["lastSmartDetect"]
|
|
1070
|
-
if "lastSmartAudioDetect" in data:
|
|
1071
|
-
del data["lastSmartAudioDetect"]
|
|
1072
|
-
if "lastSmartDetectEventId" in data:
|
|
1073
|
-
del data["lastSmartDetectEventId"]
|
|
1074
|
-
if "lastSmartAudioDetectEventId" in data:
|
|
1075
|
-
del data["lastSmartAudioDetectEventId"]
|
|
1076
|
-
if "lastSmartDetects" in data:
|
|
1077
|
-
del data["lastSmartDetects"]
|
|
1078
|
-
if "lastSmartAudioDetects" in data:
|
|
1079
|
-
del data["lastSmartAudioDetects"]
|
|
1080
|
-
if "lastSmartDetectEventIds" in data:
|
|
1081
|
-
del data["lastSmartDetectEventIds"]
|
|
1082
|
-
if "lastSmartAudioDetectEventIds" in data:
|
|
1083
|
-
del data["lastSmartAudioDetectEventIds"]
|
|
1084
|
-
if "talkbackStream" in data:
|
|
1085
|
-
del data["talkbackStream"]
|
|
1086
1071
|
if "lcdMessage" in data and data["lcdMessage"] is None:
|
|
1087
1072
|
data["lcdMessage"] = {}
|
|
1088
1073
|
|
|
@@ -2820,18 +2805,15 @@ class Sensor(ProtectAdoptableDeviceModel):
|
|
|
2820
2805
|
exclude: set[str] | None = None,
|
|
2821
2806
|
) -> dict[str, Any]:
|
|
2822
2807
|
data = super().unifi_dict(data=data, exclude=exclude)
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
if "extremeValueDetectedAt" in data:
|
|
2833
|
-
del data["extremeValueDetectedAt"]
|
|
2834
|
-
|
|
2808
|
+
for key in (
|
|
2809
|
+
"lastMotionEventId",
|
|
2810
|
+
"lastContactEventId",
|
|
2811
|
+
"lastValueEventId",
|
|
2812
|
+
"lastAlarmEventId",
|
|
2813
|
+
"extremeValueDetectedAt",
|
|
2814
|
+
):
|
|
2815
|
+
if key in data:
|
|
2816
|
+
del data[key]
|
|
2835
2817
|
return data
|
|
2836
2818
|
|
|
2837
2819
|
@property
|
|
@@ -309,9 +309,9 @@ class Event(ProtectModelWithId):
|
|
|
309
309
|
|
|
310
310
|
@classmethod
|
|
311
311
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
312
|
-
for key in
|
|
313
|
-
|
|
314
|
-
|
|
312
|
+
for key in ("start", "end", "timestamp", "deletedAt"):
|
|
313
|
+
if key in data:
|
|
314
|
+
data[key] = convert_to_datetime(data[key])
|
|
315
315
|
return super().unifi_dict_to_dict(data)
|
|
316
316
|
|
|
317
317
|
def unifi_dict(
|
|
@@ -47,6 +47,8 @@ class WSSubscriptionMessage:
|
|
|
47
47
|
|
|
48
48
|
|
|
49
49
|
class BaseWSPacketFrame:
|
|
50
|
+
UNPACK_FORMAT = struct.Struct("!bbbbi")
|
|
51
|
+
|
|
50
52
|
data: Any
|
|
51
53
|
position: int = 0
|
|
52
54
|
header: WSPacketFrameHeader | None = None
|
|
@@ -54,6 +56,9 @@ class BaseWSPacketFrame:
|
|
|
54
56
|
is_deflated: bool = False
|
|
55
57
|
length: int = 0
|
|
56
58
|
|
|
59
|
+
def __repr__(self) -> str:
|
|
60
|
+
return f"<{self.__class__.__name__} header={self.header} data={self.data}>"
|
|
61
|
+
|
|
57
62
|
def set_data_from_binary(self, data: bytes) -> None:
|
|
58
63
|
self.data = data
|
|
59
64
|
if self.header is not None and self.header.deflated:
|
|
@@ -89,7 +94,7 @@ class BaseWSPacketFrame:
|
|
|
89
94
|
i: payload_size
|
|
90
95
|
"""
|
|
91
96
|
header_end = position + WS_HEADER_SIZE
|
|
92
|
-
|
|
97
|
+
payload_size: int
|
|
93
98
|
try:
|
|
94
99
|
(
|
|
95
100
|
packet_type,
|
|
@@ -97,8 +102,7 @@ class BaseWSPacketFrame:
|
|
|
97
102
|
deflated,
|
|
98
103
|
unknown,
|
|
99
104
|
payload_size,
|
|
100
|
-
) =
|
|
101
|
-
"!bbbbi",
|
|
105
|
+
) = BaseWSPacketFrame.UNPACK_FORMAT.unpack(
|
|
102
106
|
data[position:header_end],
|
|
103
107
|
)
|
|
104
108
|
except struct.error as e:
|
|
@@ -117,9 +121,9 @@ class BaseWSPacketFrame:
|
|
|
117
121
|
unknown=unknown,
|
|
118
122
|
payload_size=payload_size,
|
|
119
123
|
)
|
|
120
|
-
frame.length = WS_HEADER_SIZE +
|
|
121
|
-
frame.is_deflated = bool(
|
|
122
|
-
frame_end = header_end +
|
|
124
|
+
frame.length = WS_HEADER_SIZE + payload_size
|
|
125
|
+
frame.is_deflated = bool(deflated)
|
|
126
|
+
frame_end = header_end + payload_size
|
|
123
127
|
frame.set_data_from_binary(data[header_end:frame_end])
|
|
124
128
|
|
|
125
129
|
return frame
|
|
@@ -187,12 +191,14 @@ class WSPacket:
|
|
|
187
191
|
def __init__(self, data: bytes) -> None:
|
|
188
192
|
self._raw = data
|
|
189
193
|
|
|
194
|
+
def __repr__(self) -> str:
|
|
195
|
+
return f"<{self.__class__.__name__} action_frame={self.action_frame} data_frame={self.data_frame}>"
|
|
196
|
+
|
|
190
197
|
def decode(self) -> None:
|
|
191
|
-
|
|
192
|
-
self.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
)
|
|
198
|
+
data = self._raw
|
|
199
|
+
self._action_frame = WSRawPacketFrame.from_binary(data)
|
|
200
|
+
length = self._action_frame.length
|
|
201
|
+
self._data_frame = WSRawPacketFrame.from_binary(data, length)
|
|
196
202
|
|
|
197
203
|
@cached_property
|
|
198
204
|
def action_frame(self) -> BaseWSPacketFrame:
|
|
@@ -202,6 +208,7 @@ class WSPacket:
|
|
|
202
208
|
if self._action_frame is None:
|
|
203
209
|
raise WSDecodeError("Packet unexpectedly not decoded")
|
|
204
210
|
|
|
211
|
+
self.__dict__["data_frame"] = self._data_frame
|
|
205
212
|
return self._action_frame
|
|
206
213
|
|
|
207
214
|
@cached_property
|
|
@@ -212,6 +219,7 @@ class WSPacket:
|
|
|
212
219
|
if self._data_frame is None:
|
|
213
220
|
raise WSDecodeError("Packet unexpectedly not decoded")
|
|
214
221
|
|
|
222
|
+
self.__dict__["action_frame"] = self._action_frame
|
|
215
223
|
return self._data_frame
|
|
216
224
|
|
|
217
225
|
@property
|
|
@@ -17,7 +17,7 @@ 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 lru_cache
|
|
20
|
+
from functools import cache, lru_cache
|
|
21
21
|
from hashlib import sha224
|
|
22
22
|
from http.cookies import Morsel
|
|
23
23
|
from inspect import isclass
|
|
@@ -96,22 +96,20 @@ IP_TYPES = {
|
|
|
96
96
|
Union[IPv6Address, IPv4Address, None],
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
if sys.version_info[:2] < (3, 11):
|
|
100
|
-
pass
|
|
101
|
-
else:
|
|
102
|
-
pass
|
|
103
|
-
|
|
104
99
|
|
|
105
100
|
def set_debug() -> None:
|
|
106
101
|
"""Sets ENV variable for UFP_DEBUG to on (True)"""
|
|
107
102
|
os.environ[DEBUG_ENV] = str(True)
|
|
103
|
+
is_debug.cache_clear()
|
|
108
104
|
|
|
109
105
|
|
|
110
106
|
def set_no_debug() -> None:
|
|
111
107
|
"""Sets ENV variable for UFP_DEBUG to off (False)"""
|
|
112
108
|
os.environ[DEBUG_ENV] = str(False)
|
|
109
|
+
is_debug.cache_clear()
|
|
113
110
|
|
|
114
111
|
|
|
112
|
+
@cache
|
|
115
113
|
def is_debug() -> bool:
|
|
116
114
|
"""Returns if debug ENV is on (True)"""
|
|
117
115
|
return os.environ.get(DEBUG_ENV) == str(True)
|
|
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
|