uiprotect 0.7.0__py3-none-any.whl → 0.9.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 +25 -19
- uiprotect/cli/backup.py +1 -1
- uiprotect/data/base.py +2 -2
- uiprotect/data/bootstrap.py +2 -2
- uiprotect/data/devices.py +1 -1
- uiprotect/data/nvr.py +5 -5
- uiprotect/data/types.py +21 -1
- uiprotect/utils.py +1 -1
- {uiprotect-0.7.0.dist-info → uiprotect-0.9.0.dist-info}/METADATA +1 -1
- {uiprotect-0.7.0.dist-info → uiprotect-0.9.0.dist-info}/RECORD +13 -13
- {uiprotect-0.7.0.dist-info → uiprotect-0.9.0.dist-info}/LICENSE +0 -0
- {uiprotect-0.7.0.dist-info → uiprotect-0.9.0.dist-info}/WHEEL +0 -0
- {uiprotect-0.7.0.dist-info → uiprotect-0.9.0.dist-info}/entry_points.txt +0 -0
uiprotect/api.py
CHANGED
|
@@ -818,23 +818,26 @@ class ProtectApiClient(BaseApiClient):
|
|
|
818
818
|
return self._bootstrap
|
|
819
819
|
|
|
820
820
|
def emit_message(self, msg: WSSubscriptionMessage) -> None:
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
821
|
+
"""Emit message to all subscriptions."""
|
|
822
|
+
if _LOGGER.isEnabledFor(logging.DEBUG):
|
|
823
|
+
if msg.new_obj is not None:
|
|
824
|
+
_LOGGER.debug(
|
|
825
|
+
"emitting message: %s:%s:%s:%s",
|
|
826
|
+
msg.action,
|
|
827
|
+
msg.new_obj.model,
|
|
828
|
+
msg.new_obj.id,
|
|
829
|
+
list(msg.changed_data),
|
|
830
|
+
)
|
|
831
|
+
elif msg.old_obj is not None:
|
|
832
|
+
_LOGGER.debug(
|
|
833
|
+
"emitting message: %s:%s:%s",
|
|
834
|
+
msg.action,
|
|
835
|
+
msg.old_obj.model,
|
|
836
|
+
msg.old_obj.id,
|
|
837
|
+
)
|
|
838
|
+
else:
|
|
839
|
+
_LOGGER.debug("emitting message: %s", msg.action)
|
|
840
|
+
|
|
838
841
|
for sub in self._ws_subscriptions:
|
|
839
842
|
try:
|
|
840
843
|
sub(msg)
|
|
@@ -1072,7 +1075,10 @@ class ProtectApiClient(BaseApiClient):
|
|
|
1072
1075
|
|
|
1073
1076
|
for event_dict in response:
|
|
1074
1077
|
# ignore unknown events
|
|
1075
|
-
if
|
|
1078
|
+
if (
|
|
1079
|
+
"type" not in event_dict
|
|
1080
|
+
or event_dict["type"] not in EventType.values_set()
|
|
1081
|
+
):
|
|
1076
1082
|
_LOGGER.debug("Unknown event type: %s", event_dict)
|
|
1077
1083
|
continue
|
|
1078
1084
|
|
|
@@ -1083,7 +1089,7 @@ class ProtectApiClient(BaseApiClient):
|
|
|
1083
1089
|
continue
|
|
1084
1090
|
|
|
1085
1091
|
if (
|
|
1086
|
-
event.type.value in EventType.
|
|
1092
|
+
event.type.value in EventType.device_events_set()
|
|
1087
1093
|
and event.score >= self._minimum_score
|
|
1088
1094
|
):
|
|
1089
1095
|
events.append(event)
|
uiprotect/cli/backup.py
CHANGED
|
@@ -814,7 +814,7 @@ def _add_metadata(path: Path, creation: datetime, title: str) -> bool:
|
|
|
814
814
|
in_to_out[stream] = output_file.add_stream(template=stream) # type: ignore[index]
|
|
815
815
|
in_to_out[stream].metadata["creation_time"] = creation.isoformat() # type: ignore[index]
|
|
816
816
|
|
|
817
|
-
for packet in input_file.demux(list(in_to_out
|
|
817
|
+
for packet in input_file.demux(list(in_to_out)):
|
|
818
818
|
if packet.dts is None:
|
|
819
819
|
continue
|
|
820
820
|
|
uiprotect/data/base.py
CHANGED
|
@@ -338,7 +338,7 @@ class ProtectBaseObject(BaseModel):
|
|
|
338
338
|
data[remaps[from_key]] = data.pop(from_key)
|
|
339
339
|
|
|
340
340
|
# convert to snake_case and remove extra fields
|
|
341
|
-
for key in list(data
|
|
341
|
+
for key in list(data):
|
|
342
342
|
new_key = to_snake_case(key)
|
|
343
343
|
data[new_key] = data.pop(key)
|
|
344
344
|
key = new_key
|
|
@@ -765,7 +765,7 @@ class ProtectModelWithId(ProtectModel):
|
|
|
765
765
|
if updated == {}:
|
|
766
766
|
return
|
|
767
767
|
|
|
768
|
-
read_only_keys = read_only_fields.intersection(updated
|
|
768
|
+
read_only_keys = read_only_fields.intersection(updated)
|
|
769
769
|
if len(read_only_keys) > 0:
|
|
770
770
|
self.revert_changes(data_before_changes)
|
|
771
771
|
raise BadRequest(
|
uiprotect/data/bootstrap.py
CHANGED
|
@@ -553,7 +553,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
553
553
|
if action["newUpdateId"] is not None:
|
|
554
554
|
self.last_update_id = action["newUpdateId"]
|
|
555
555
|
|
|
556
|
-
if action["modelKey"] not in ModelType.
|
|
556
|
+
if action["modelKey"] not in ModelType.values_set():
|
|
557
557
|
_LOGGER.debug("Unknown model type: %s", action["modelKey"])
|
|
558
558
|
self._create_stat(packet, [], True)
|
|
559
559
|
return None
|
|
@@ -577,7 +577,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
577
577
|
if action["modelKey"] == ModelType.NVR.value:
|
|
578
578
|
return self._process_nvr_update(packet, data, ignore_stats)
|
|
579
579
|
if (
|
|
580
|
-
action["modelKey"] in ModelType.
|
|
580
|
+
action["modelKey"] in ModelType.bootstrap_models_set()
|
|
581
581
|
or action["modelKey"] == ModelType.EVENT.value
|
|
582
582
|
):
|
|
583
583
|
return self._process_device_update(
|
uiprotect/data/devices.py
CHANGED
|
@@ -1034,7 +1034,7 @@ class Camera(ProtectMotionDeviceModel):
|
|
|
1034
1034
|
@classmethod
|
|
1035
1035
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
1036
1036
|
# LCD messages comes back as empty dict {}
|
|
1037
|
-
if "lcdMessage" in data and len(data["lcdMessage"]
|
|
1037
|
+
if "lcdMessage" in data and len(data["lcdMessage"]) == 0:
|
|
1038
1038
|
del data["lcdMessage"]
|
|
1039
1039
|
if "chimeDuration" in data and not isinstance(data["chimeDuration"], timedelta):
|
|
1040
1040
|
data["chimeDuration"] = timedelta(milliseconds=data["chimeDuration"])
|
uiprotect/data/nvr.py
CHANGED
|
@@ -152,7 +152,7 @@ class EventThumbnailAttributes(ProtectBaseObject):
|
|
|
152
152
|
) -> dict[str, Any]:
|
|
153
153
|
data = super().unifi_dict(data=data, exclude=exclude)
|
|
154
154
|
|
|
155
|
-
for key in DELETE_KEYS_THUMB.intersection(data
|
|
155
|
+
for key in DELETE_KEYS_THUMB.intersection(data):
|
|
156
156
|
if data[key] is None:
|
|
157
157
|
del data[key]
|
|
158
158
|
|
|
@@ -240,7 +240,7 @@ class EventMetadata(ProtectBaseObject):
|
|
|
240
240
|
|
|
241
241
|
@classmethod
|
|
242
242
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
243
|
-
for key in cls._collapse_keys.intersection(data
|
|
243
|
+
for key in cls._collapse_keys.intersection(data):
|
|
244
244
|
if isinstance(data[key], dict):
|
|
245
245
|
data[key] = data[key]["text"]
|
|
246
246
|
|
|
@@ -258,7 +258,7 @@ class EventMetadata(ProtectBaseObject):
|
|
|
258
258
|
if value is None:
|
|
259
259
|
del data[key]
|
|
260
260
|
|
|
261
|
-
for key in self._collapse_keys.intersection(data
|
|
261
|
+
for key in self._collapse_keys.intersection(data):
|
|
262
262
|
# AI Theta/Hotplug exception
|
|
263
263
|
if key != "type" or data[key] not in {"audio", "video", "extender"}:
|
|
264
264
|
data[key] = {"text": data[key]}
|
|
@@ -308,7 +308,7 @@ class Event(ProtectModelWithId):
|
|
|
308
308
|
|
|
309
309
|
@classmethod
|
|
310
310
|
def unifi_dict_to_dict(cls, data: dict[str, Any]) -> dict[str, Any]:
|
|
311
|
-
for key in {"start", "end", "timestamp", "deletedAt"}.intersection(data
|
|
311
|
+
for key in {"start", "end", "timestamp", "deletedAt"}.intersection(data):
|
|
312
312
|
data[key] = process_datetime(data, key)
|
|
313
313
|
|
|
314
314
|
return super().unifi_dict_to_dict(data)
|
|
@@ -320,7 +320,7 @@ class Event(ProtectModelWithId):
|
|
|
320
320
|
) -> dict[str, Any]:
|
|
321
321
|
data = super().unifi_dict(data=data, exclude=exclude)
|
|
322
322
|
|
|
323
|
-
for key in DELETE_KEYS_EVENT.intersection(data
|
|
323
|
+
for key in DELETE_KEYS_EVENT.intersection(data):
|
|
324
324
|
if data[key] is None:
|
|
325
325
|
del data[key]
|
|
326
326
|
|
uiprotect/data/types.py
CHANGED
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import enum
|
|
4
4
|
from collections.abc import Callable, Coroutine
|
|
5
|
+
from functools import cache
|
|
5
6
|
from typing import Any, Literal, Optional, TypeVar, Union
|
|
6
7
|
|
|
7
8
|
from packaging.version import Version as BaseVersion
|
|
@@ -50,7 +51,7 @@ class FixSizeOrderedDict(dict[KT, VT]):
|
|
|
50
51
|
"""Set an update up to the max size."""
|
|
51
52
|
dict.__setitem__(self, key, value)
|
|
52
53
|
if self._max_size > 0 and len(self) > 0 and len(self) > self._max_size:
|
|
53
|
-
del self[next(iter(self
|
|
54
|
+
del self[next(iter(self))]
|
|
54
55
|
|
|
55
56
|
|
|
56
57
|
class ValuesEnumMixin:
|
|
@@ -58,11 +59,17 @@ class ValuesEnumMixin:
|
|
|
58
59
|
_values_normalized: dict[str, str] | None = None
|
|
59
60
|
|
|
60
61
|
@classmethod
|
|
62
|
+
@cache
|
|
61
63
|
def values(cls) -> list[str]:
|
|
62
64
|
if cls._values is None:
|
|
63
65
|
cls._values = [e.value for e in cls] # type: ignore[attr-defined]
|
|
64
66
|
return cls._values
|
|
65
67
|
|
|
68
|
+
@classmethod
|
|
69
|
+
@cache
|
|
70
|
+
def values_set(cls) -> set[str]:
|
|
71
|
+
return set(cls.values())
|
|
72
|
+
|
|
66
73
|
@classmethod
|
|
67
74
|
def _missing_(cls, value: Any) -> Any | None:
|
|
68
75
|
if cls._values_normalized is None:
|
|
@@ -103,6 +110,7 @@ class ModelType(str, UnknownValuesEnumMixin, enum.Enum):
|
|
|
103
110
|
UNKNOWN = "unknown"
|
|
104
111
|
|
|
105
112
|
@staticmethod
|
|
113
|
+
@cache
|
|
106
114
|
def bootstrap_models() -> tuple[str, ...]:
|
|
107
115
|
# TODO:
|
|
108
116
|
# legacyUFV
|
|
@@ -121,6 +129,11 @@ class ModelType(str, UnknownValuesEnumMixin, enum.Enum):
|
|
|
121
129
|
ModelType.CHIME.value,
|
|
122
130
|
)
|
|
123
131
|
|
|
132
|
+
@staticmethod
|
|
133
|
+
@cache
|
|
134
|
+
def bootstrap_models_set() -> set[str]:
|
|
135
|
+
return set(ModelType.bootstrap_models())
|
|
136
|
+
|
|
124
137
|
|
|
125
138
|
@enum.unique
|
|
126
139
|
class EventType(str, ValuesEnumMixin, enum.Enum):
|
|
@@ -204,6 +217,7 @@ class EventType(str, ValuesEnumMixin, enum.Enum):
|
|
|
204
217
|
RECORDING_OFF = "recordingOff"
|
|
205
218
|
|
|
206
219
|
@staticmethod
|
|
220
|
+
@cache
|
|
207
221
|
def device_events() -> list[str]:
|
|
208
222
|
return [
|
|
209
223
|
EventType.MOTION.value,
|
|
@@ -212,6 +226,12 @@ class EventType(str, ValuesEnumMixin, enum.Enum):
|
|
|
212
226
|
]
|
|
213
227
|
|
|
214
228
|
@staticmethod
|
|
229
|
+
@cache
|
|
230
|
+
def device_events_set() -> set[str]:
|
|
231
|
+
return set(EventType.device_events())
|
|
232
|
+
|
|
233
|
+
@staticmethod
|
|
234
|
+
@cache
|
|
215
235
|
def motion_events() -> list[str]:
|
|
216
236
|
return [EventType.MOTION.value, EventType.SMART_DETECT.value]
|
|
217
237
|
|
uiprotect/utils.py
CHANGED
|
@@ -278,7 +278,7 @@ def serialize_unifi_obj(value: Any, levels: int = -1) -> Any:
|
|
|
278
278
|
|
|
279
279
|
def serialize_dict(data: dict[str, Any], levels: int = -1) -> dict[str, Any]:
|
|
280
280
|
"""Serializes UFP data dict"""
|
|
281
|
-
for key in list(data
|
|
281
|
+
for key in list(data):
|
|
282
282
|
set_key = key
|
|
283
283
|
if set_key not in SNAKE_CASE_KEYS:
|
|
284
284
|
set_key = to_camel_case(set_key)
|
|
@@ -1,8 +1,8 @@
|
|
|
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=9NQ8H4AxrGmneTwWo2K2EgFgsm1L5o-2vm7cDJdjWIg,66158
|
|
4
4
|
uiprotect/cli/__init__.py,sha256=sSLW9keVQOkgFcMW18HTDjRrt9sJ0KWjn9DJDA6f9Pc,8658
|
|
5
|
-
uiprotect/cli/backup.py,sha256=
|
|
5
|
+
uiprotect/cli/backup.py,sha256=ZiS7RZnJGKI8TJKLW2cOUzkRM8nyTvE5Ov_jZZGtvSM,36708
|
|
6
6
|
uiprotect/cli/base.py,sha256=zpTm2kyJe_GLixnv3Uadke__iRLh64AEwQzp-2hqS7g,7730
|
|
7
7
|
uiprotect/cli/cameras.py,sha256=YvvMccQEYG3Wih0Ix8tan1R1vfaJ6cogg6YKWLzMUV8,16973
|
|
8
8
|
uiprotect/cli/chimes.py,sha256=XANn21bQVkestkKOm9HjxSM8ZGrRrqvUXLouaQ3LTqs,5326
|
|
@@ -14,12 +14,12 @@ 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=ex-UC9CJUtzxMFqtYokSiXM8pNHVBqCzq7r8WrEf1Mw,37178
|
|
18
|
+
uiprotect/data/bootstrap.py,sha256=atqHRlZVtRw24obwot7jkXxf_rDzgooPXKW7dMr_3Zo,21884
|
|
19
19
|
uiprotect/data/convert.py,sha256=rOQplUMIdTMD2SbAx_iI9BNPDscnhDvyRVLEMDhtADg,2047
|
|
20
|
-
uiprotect/data/devices.py,sha256=
|
|
21
|
-
uiprotect/data/nvr.py,sha256=
|
|
22
|
-
uiprotect/data/types.py,sha256=
|
|
20
|
+
uiprotect/data/devices.py,sha256=LHVBT8ihMAZen7gIlQNbiYxukRrBpi_TNKNmV_5R6Xc,111705
|
|
21
|
+
uiprotect/data/nvr.py,sha256=OJso6oewA_jY7ovKbD2U2Onp1GqheT5bCW0F6dC53DQ,47570
|
|
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=lkdobRh5SPu7YzLHyhZVe7qlh5W3L8LKzS63Md-4DOk,6048
|
|
25
25
|
uiprotect/exceptions.py,sha256=kgn0cRM6lTtgLza09SDa3ZiX6ue1QqHCOogQ4qu6KTQ,965
|
|
@@ -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=kXEr1xEoPAwUYuVPd6QoVnTf8MQwzQXQQ4JLyJsiRfY,18219
|
|
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.9.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
|
|
34
|
+
uiprotect-0.9.0.dist-info/METADATA,sha256=vkY-rMmqWGZn2k1aMLMjwOykXB-GyPuW1VoOWr3JUeA,10984
|
|
35
|
+
uiprotect-0.9.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
36
|
+
uiprotect-0.9.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
|
|
37
|
+
uiprotect-0.9.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|