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 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
- if msg.new_obj is not None:
822
- _LOGGER.debug(
823
- "emitting message: %s:%s:%s:%s",
824
- msg.action,
825
- msg.new_obj.model,
826
- msg.new_obj.id,
827
- list(msg.changed_data.keys()),
828
- )
829
- elif msg.old_obj is not None:
830
- _LOGGER.debug(
831
- "emitting message: %s:%s:%s",
832
- msg.action,
833
- msg.old_obj.model,
834
- msg.old_obj.id,
835
- )
836
- else:
837
- _LOGGER.debug("emitting message: %s", msg.action)
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 "type" not in event_dict or event_dict["type"] not in EventType.values():
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.device_events()
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.keys())):
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.keys()):
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.keys())
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(
@@ -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.values():
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.bootstrap_models()
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"].keys()) == 0:
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.keys()):
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.keys()):
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.keys()):
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.keys()):
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.keys()):
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.keys()))]
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.keys()):
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: uiprotect
3
- Version: 0.7.0
3
+ Version: 0.9.0
4
4
  Summary: Python API for Unifi Protect (Unofficial)
5
5
  Home-page: https://github.com/uilibs/uiprotect
6
6
  License: MIT
@@ -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=1eIAuA4GC32-mpibk-Avd4vU59fT_UgxqjKVP9ISTkY,65943
3
+ uiprotect/api.py,sha256=9NQ8H4AxrGmneTwWo2K2EgFgsm1L5o-2vm7cDJdjWIg,66158
4
4
  uiprotect/cli/__init__.py,sha256=sSLW9keVQOkgFcMW18HTDjRrt9sJ0KWjn9DJDA6f9Pc,8658
5
- uiprotect/cli/backup.py,sha256=SxUyFFwLHcU60qsI8qBS_Xw7RLJeqUEdMAyjwk30MKw,36715
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=f032w_AnTT3Hd85eBVs2XdnyzHpckdCwiJrsMQyLsR8,37192
18
- uiprotect/data/bootstrap.py,sha256=ibfCHqNhH44iw-JsuQs41zBCjb9ksSXz_QQq7qDbLsQ,21876
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=AsCQCoOpswdIU7X5ty1XhhOl2v6CkQCXu2Y-lIS_6_k,111712
21
- uiprotect/data/nvr.py,sha256=c8WxXpBcMaZ5REzCUb8aqmlLkttFYO1O5jLMhus5rkw,47605
22
- uiprotect/data/types.py,sha256=MTi9dVzKTp_8XtAmWKtbZujwE5plKGHdjglFGcJ4Yxs,15789
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=gCLoZBQ94Yi9PqefiqTZK7WrvT3Byue79a4jvDc0k44,18226
31
+ uiprotect/utils.py,sha256=kXEr1xEoPAwUYuVPd6QoVnTf8MQwzQXQQ4JLyJsiRfY,18219
32
32
  uiprotect/websocket.py,sha256=iMTdchymaCgVHsmY1bRbxkcymqt6WQircIHYNxCu178,7289
33
- uiprotect-0.7.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
34
- uiprotect-0.7.0.dist-info/METADATA,sha256=RDKJOvu9_P8mVZTKOBwzBBfZhxXdU_KdMHbUrMQEnZ4,10984
35
- uiprotect-0.7.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
36
- uiprotect-0.7.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
37
- uiprotect-0.7.0.dist-info/RECORD,,
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,,