uiprotect 0.11.0__py3-none-any.whl → 0.13.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.

@@ -18,7 +18,7 @@ except ImportError:
18
18
  from pydantic import PrivateAttr, ValidationError # type: ignore[assignment]
19
19
 
20
20
  from ..exceptions import ClientError
21
- from ..utils import utc_now
21
+ from ..utils import normalize_mac, utc_now
22
22
  from .base import (
23
23
  RECENT_EVENT_MAX,
24
24
  ProtectBaseObject,
@@ -131,26 +131,34 @@ def _process_sensor_event(event: Event) -> None:
131
131
  event.sensor.last_value_event_id = event.id
132
132
 
133
133
 
134
+ _CAMERA_SMART_AND_LINE_EVENTS = {
135
+ EventType.SMART_DETECT,
136
+ EventType.SMART_DETECT_LINE,
137
+ }
138
+ _CAMERA_SMART_AUDIO_EVENT = EventType.SMART_AUDIO_DETECT
139
+
140
+
134
141
  def _process_camera_event(event: Event) -> None:
135
- if event.camera is None:
142
+ if (camera := event.camera) is None:
136
143
  return
137
144
 
138
- dt_attr, event_attr = CAMERA_EVENT_ATTR_MAP[event.type]
139
- dt = getattr(event.camera, dt_attr)
145
+ event_type = event.type
146
+ dt_attr, event_attr = CAMERA_EVENT_ATTR_MAP[event_type]
147
+ dt = getattr(camera, dt_attr)
140
148
  if dt is None or event.start >= dt or (event.end is not None and event.end >= dt):
141
- setattr(event.camera, event_attr, event.id)
142
- setattr(event.camera, dt_attr, event.start)
143
- if event.type in {EventType.SMART_DETECT, EventType.SMART_DETECT_LINE}:
149
+ setattr(camera, event_attr, event.id)
150
+ setattr(camera, dt_attr, event.start)
151
+ if event_type in _CAMERA_SMART_AND_LINE_EVENTS:
144
152
  for smart_type in event.smart_detect_types:
145
- event.camera.last_smart_detect_event_ids[smart_type] = event.id
146
- event.camera.last_smart_detects[smart_type] = event.start
147
- elif event.type == EventType.SMART_AUDIO_DETECT:
153
+ camera.last_smart_detect_event_ids[smart_type] = event.id
154
+ camera.last_smart_detects[smart_type] = event.start
155
+ elif event_type is _CAMERA_SMART_AUDIO_EVENT:
148
156
  for smart_type in event.smart_detect_types:
149
157
  audio_type = smart_type.audio_type
150
158
  if audio_type is None:
151
159
  continue
152
- event.camera.last_smart_audio_detect_event_ids[audio_type] = event.id
153
- event.camera.last_smart_audio_detects[audio_type] = event.start
160
+ camera.last_smart_audio_detect_event_ids[audio_type] = event.id
161
+ camera.last_smart_audio_detects[audio_type] = event.start
154
162
 
155
163
 
156
164
  @dataclass
@@ -226,7 +234,7 @@ class Bootstrap(ProtectBaseObject):
226
234
  items[item["id"]] = item
227
235
  data["idLookup"][item["id"]] = ref
228
236
  if "mac" in item:
229
- cleaned_mac = item["mac"].lower().replace(":", "")
237
+ cleaned_mac = normalize_mac(item["mac"])
230
238
  data["macLookup"][cleaned_mac] = ref
231
239
  data[key] = items
232
240
 
@@ -312,8 +320,7 @@ class Bootstrap(ProtectBaseObject):
312
320
 
313
321
  def get_device_from_mac(self, mac: str) -> ProtectAdoptableDeviceModel | None:
314
322
  """Retrieve a device from MAC address."""
315
- mac = mac.lower().replace(":", "").replace("-", "").replace("_", "")
316
- ref = self.mac_lookup.get(mac)
323
+ ref = self.mac_lookup.get(normalize_mac(mac))
317
324
  if ref is None:
318
325
  return None
319
326
 
@@ -378,7 +385,7 @@ class Bootstrap(ProtectBaseObject):
378
385
  elif (
379
386
  isinstance(obj, ProtectAdoptableDeviceModel)
380
387
  and obj.model is not None
381
- and obj.model.value in ModelType.bootstrap_models()
388
+ and obj.model.value in ModelType.bootstrap_models_set()
382
389
  ):
383
390
  key = obj.model.value + "s"
384
391
  if not self.api.ignore_unadopted or (
@@ -387,7 +394,7 @@ class Bootstrap(ProtectBaseObject):
387
394
  getattr(self, key)[obj.id] = obj
388
395
  ref = ProtectDeviceRef(model=obj.model, id=obj.id)
389
396
  self.id_lookup[obj.id] = ref
390
- self.mac_lookup[obj.mac.lower().replace(":", "")] = ref
397
+ self.mac_lookup[normalize_mac(obj.mac)] = ref
391
398
  else:
392
399
  _LOGGER.debug("Unexpected bootstrap model type for add: %s", obj.model)
393
400
  return None
@@ -403,23 +410,19 @@ class Bootstrap(ProtectBaseObject):
403
410
  new_obj=obj,
404
411
  )
405
412
 
406
- def _process_remove_packet(
407
- self,
408
- packet: WSPacket,
409
- data: dict[str, Any] | None,
410
- ) -> WSSubscriptionMessage | None:
411
- model = packet.action_frame.data.get("modelKey")
412
- device_id = packet.action_frame.data.get("id")
413
+ def _process_remove_packet(self, packet: WSPacket) -> WSSubscriptionMessage | None:
414
+ model: str | None = packet.action_frame.data.get("modelKey")
413
415
  devices = getattr(self, f"{model}s", None)
414
416
 
415
417
  if devices is None:
416
418
  return None
417
419
 
420
+ device_id: str = packet.action_frame.data["id"]
418
421
  self.id_lookup.pop(device_id, None)
419
422
  device = devices.pop(device_id, None)
420
423
  if device is None:
421
424
  return None
422
- self.mac_lookup.pop(device.mac.lower().replace(":", ""), None)
425
+ self.mac_lookup.pop(normalize_mac(device.mac), None)
423
426
 
424
427
  self._create_stat(packet, None, False)
425
428
  return WSSubscriptionMessage(
@@ -489,12 +492,11 @@ class Bootstrap(ProtectBaseObject):
489
492
 
490
493
  key = f"{model_type}s"
491
494
  devices = getattr(self, key)
492
- if action["id"] in devices:
493
- if action["id"] not in devices:
494
- raise ValueError(
495
- f"Unknown device update for {model_type}: { action['id']}",
496
- )
497
- obj: ProtectModelWithId = devices[action["id"]]
495
+ action_id: str = action["id"]
496
+ if action_id in devices:
497
+ if action_id not in devices:
498
+ raise ValueError(f"Unknown device update for {model_type}: {action_id}")
499
+ obj: ProtectModelWithId = devices[action_id]
498
500
  data = obj.unifi_dict_to_dict(data)
499
501
  old_obj = obj.copy()
500
502
  obj = obj.update_from_dict(deepcopy(data))
@@ -517,7 +519,7 @@ class Bootstrap(ProtectBaseObject):
517
519
  if is_recent:
518
520
  obj.set_alarm_timeout()
519
521
 
520
- devices[action["id"]] = obj
522
+ devices[action_id] = obj
521
523
 
522
524
  self._create_stat(packet, data, False)
523
525
  return WSSubscriptionMessage(
@@ -529,8 +531,8 @@ class Bootstrap(ProtectBaseObject):
529
531
  )
530
532
 
531
533
  # ignore updates to events that phase out
532
- if model_type != ModelType.EVENT.value:
533
- _LOGGER.debug("Unexpected %s: %s", key, action["id"])
534
+ if model_type != _ModelType_Event_value:
535
+ _LOGGER.debug("Unexpected %s: %s", key, action_id)
534
536
  return None
535
537
 
536
538
  def process_ws_packet(
@@ -557,7 +559,7 @@ class Bootstrap(ProtectBaseObject):
557
559
 
558
560
  action_action: str = action["action"]
559
561
  if action_action == "remove":
560
- return self._process_remove_packet(packet, data)
562
+ return self._process_remove_packet(packet)
561
563
 
562
564
  if not data:
563
565
  self._create_stat(packet, None, True)
@@ -596,7 +598,7 @@ class Bootstrap(ProtectBaseObject):
596
598
  else:
597
599
  try:
598
600
  model_type = ModelType(action["modelKey"])
599
- device_id = action["id"]
601
+ device_id: str = action["id"]
600
602
  task = asyncio.create_task(self.refresh_device(model_type, device_id))
601
603
  self._refresh_tasks.add(task)
602
604
  task.add_done_callback(self._refresh_tasks.discard)
uiprotect/utils.py CHANGED
@@ -624,3 +624,9 @@ def clamp_value(value: float, step_size: float) -> float:
624
624
  """Clamps value to multiples of step size."""
625
625
  ratio = 1 / step_size
626
626
  return int(value * ratio) / ratio
627
+
628
+
629
+ @lru_cache(maxsize=1024)
630
+ def normalize_mac(mac: str) -> str:
631
+ """Normalize MAC address."""
632
+ return mac.lower().replace(":", "").replace("-", "").replace("_", "")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: uiprotect
3
- Version: 0.11.0
3
+ Version: 0.13.0
4
4
  Summary: Python API for Unifi Protect (Unofficial)
5
5
  Home-page: https://github.com/uilibs/uiprotect
6
6
  License: MIT
@@ -15,7 +15,7 @@ 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
17
  uiprotect/data/base.py,sha256=ex-UC9CJUtzxMFqtYokSiXM8pNHVBqCzq7r8WrEf1Mw,37178
18
- uiprotect/data/bootstrap.py,sha256=e84W72zg6MZCgaYEunr6CCvd5b7Hb-GDSDEvfEGEt0Y,21556
18
+ uiprotect/data/bootstrap.py,sha256=bev4mrf8LHhh4ZfTP-g_3ZrB1ZPf_dtLl82QvyqOkE0,21543
19
19
  uiprotect/data/convert.py,sha256=rOQplUMIdTMD2SbAx_iI9BNPDscnhDvyRVLEMDhtADg,2047
20
20
  uiprotect/data/devices.py,sha256=LHVBT8ihMAZen7gIlQNbiYxukRrBpi_TNKNmV_5R6Xc,111705
21
21
  uiprotect/data/nvr.py,sha256=OJso6oewA_jY7ovKbD2U2Onp1GqheT5bCW0F6dC53DQ,47570
@@ -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=kXEr1xEoPAwUYuVPd6QoVnTf8MQwzQXQQ4JLyJsiRfY,18219
31
+ uiprotect/utils.py,sha256=ItYUrIhCbvdvIO3Q_MI9jQFnFs5OouqfsO_SXWeQZPM,18389
32
32
  uiprotect/websocket.py,sha256=iMTdchymaCgVHsmY1bRbxkcymqt6WQircIHYNxCu178,7289
33
- uiprotect-0.11.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
34
- uiprotect-0.11.0.dist-info/METADATA,sha256=xyc8Jqihak81Xrqx9GhCDmIdJLGT-1CMsTfUDXVWskw,10985
35
- uiprotect-0.11.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
36
- uiprotect-0.11.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
37
- uiprotect-0.11.0.dist-info/RECORD,,
33
+ uiprotect-0.13.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
34
+ uiprotect-0.13.0.dist-info/METADATA,sha256=iHv0Q0WVlQshNOYhj9owHz_07fWNB4gWhQzt7Lw3Xpk,10985
35
+ uiprotect-0.13.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
36
+ uiprotect-0.13.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
37
+ uiprotect-0.13.0.dist-info/RECORD,,