uiprotect 1.18.0__py3-none-any.whl → 1.19.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/data/bootstrap.py +73 -56
- uiprotect/data/devices.py +3 -1
- {uiprotect-1.18.0.dist-info → uiprotect-1.19.0.dist-info}/METADATA +1 -1
- {uiprotect-1.18.0.dist-info → uiprotect-1.19.0.dist-info}/RECORD +7 -7
- {uiprotect-1.18.0.dist-info → uiprotect-1.19.0.dist-info}/LICENSE +0 -0
- {uiprotect-1.18.0.dist-info → uiprotect-1.19.0.dist-info}/WHEEL +0 -0
- {uiprotect-1.18.0.dist-info → uiprotect-1.19.0.dist-info}/entry_points.txt +0 -0
uiprotect/data/bootstrap.py
CHANGED
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import logging
|
|
7
|
-
from collections.abc import Iterable
|
|
8
7
|
from copy import deepcopy
|
|
9
8
|
from dataclasses import dataclass
|
|
10
9
|
from datetime import datetime
|
|
@@ -66,9 +65,34 @@ STATS_KEYS = {
|
|
|
66
65
|
"lastSeen",
|
|
67
66
|
"recordingSchedules",
|
|
68
67
|
}
|
|
68
|
+
|
|
69
69
|
IGNORE_DEVICE_KEYS = {"nvrMac", "guid"}
|
|
70
70
|
STATS_AND_IGNORE_DEVICE_KEYS = STATS_KEYS | IGNORE_DEVICE_KEYS
|
|
71
71
|
|
|
72
|
+
_IGNORE_KEYS_BY_MODEL_TYPE = {
|
|
73
|
+
#
|
|
74
|
+
# `lastMotion` from cameras update every 100 milliseconds when a motion event is active
|
|
75
|
+
# this overrides the behavior to only update `lastMotion` when a new event starts
|
|
76
|
+
#
|
|
77
|
+
ModelType.CAMERA: {"lastMotion"},
|
|
78
|
+
#
|
|
79
|
+
# `cameraIds` is updated every 10s, but we don't need to process it since bootstrap
|
|
80
|
+
# is resynced every so often anyways.
|
|
81
|
+
#
|
|
82
|
+
ModelType.CHIME: {"cameraIds"},
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
IGNORE_DEVICE_KEYS_BY_MODEL_TYPE = {
|
|
87
|
+
model_type: IGNORE_DEVICE_KEYS | keys
|
|
88
|
+
for model_type, keys in _IGNORE_KEYS_BY_MODEL_TYPE.items()
|
|
89
|
+
}
|
|
90
|
+
STATS_AND_IGNORE_DEVICE_KEYS_BY_MODEL_TYPE = {
|
|
91
|
+
model_type: STATS_AND_IGNORE_DEVICE_KEYS | keys
|
|
92
|
+
for model_type, keys in _IGNORE_KEYS_BY_MODEL_TYPE.items()
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
72
96
|
CAMERA_EVENT_ATTR_MAP: dict[EventType, tuple[str, str]] = {
|
|
73
97
|
EventType.MOTION: ("last_motion", "last_motion_event_id"),
|
|
74
98
|
EventType.SMART_DETECT: ("last_smart_detect", "last_smart_detect_event_id"),
|
|
@@ -318,28 +342,9 @@ class Bootstrap(ProtectBaseObject):
|
|
|
318
342
|
|
|
319
343
|
self.events[event.id] = event
|
|
320
344
|
|
|
321
|
-
def _create_stat(
|
|
322
|
-
self,
|
|
323
|
-
packet: WSPacket,
|
|
324
|
-
keys_set: Iterable[str] | None,
|
|
325
|
-
filtered: bool,
|
|
326
|
-
) -> None:
|
|
327
|
-
if self.capture_ws_stats:
|
|
328
|
-
self._ws_stats.append(
|
|
329
|
-
WSStat(
|
|
330
|
-
model=packet.action_frame.data["modelKey"],
|
|
331
|
-
action=packet.action_frame.data["action"],
|
|
332
|
-
keys=list(packet.data_frame.data),
|
|
333
|
-
keys_set=[] if keys_set is None else list(keys_set),
|
|
334
|
-
size=len(packet.raw),
|
|
335
|
-
filtered=filtered,
|
|
336
|
-
),
|
|
337
|
-
)
|
|
338
|
-
|
|
339
345
|
def _process_add_packet(
|
|
340
346
|
self,
|
|
341
347
|
model_type: ModelType,
|
|
342
|
-
packet: WSPacket,
|
|
343
348
|
data: dict[str, Any],
|
|
344
349
|
) -> WSSubscriptionMessage | None:
|
|
345
350
|
obj = create_from_unifi_dict(data, api=self._api, model_type=model_type)
|
|
@@ -366,33 +371,26 @@ class Bootstrap(ProtectBaseObject):
|
|
|
366
371
|
_LOGGER.debug("Unexpected bootstrap model type for add: %s", model_type)
|
|
367
372
|
return None
|
|
368
373
|
|
|
369
|
-
updated = obj.dict()
|
|
370
|
-
|
|
371
|
-
self._create_stat(packet, updated, False)
|
|
372
|
-
|
|
373
374
|
return WSSubscriptionMessage(
|
|
374
375
|
action=WSAction.ADD,
|
|
375
376
|
new_update_id=self.last_update_id,
|
|
376
|
-
changed_data=
|
|
377
|
+
changed_data=obj.dict(),
|
|
377
378
|
new_obj=obj,
|
|
378
379
|
)
|
|
379
380
|
|
|
380
381
|
def _process_remove_packet(
|
|
381
|
-
self, model_type: ModelType,
|
|
382
|
+
self, model_type: ModelType, action: dict[str, Any]
|
|
382
383
|
) -> WSSubscriptionMessage | None:
|
|
383
384
|
devices_key = model_type.devices_key
|
|
384
385
|
devices: dict[str, ProtectDeviceModel] | None = getattr(self, devices_key, None)
|
|
385
|
-
|
|
386
386
|
if devices is None:
|
|
387
387
|
return None
|
|
388
388
|
|
|
389
|
-
device_id: str =
|
|
389
|
+
device_id: str = action["id"]
|
|
390
390
|
self.id_lookup.pop(device_id, None)
|
|
391
391
|
if (device := devices.pop(device_id, None)) is None:
|
|
392
392
|
return None
|
|
393
393
|
self.mac_lookup.pop(normalize_mac(device.mac), None)
|
|
394
|
-
|
|
395
|
-
self._create_stat(packet, None, False)
|
|
396
394
|
return WSSubscriptionMessage(
|
|
397
395
|
action=WSAction.REMOVE,
|
|
398
396
|
new_update_id=self.last_update_id,
|
|
@@ -402,7 +400,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
402
400
|
|
|
403
401
|
def _process_nvr_update(
|
|
404
402
|
self,
|
|
405
|
-
|
|
403
|
+
action: dict[str, Any],
|
|
406
404
|
data: dict[str, Any],
|
|
407
405
|
ignore_stats: bool,
|
|
408
406
|
) -> WSSubscriptionMessage | None:
|
|
@@ -411,24 +409,20 @@ class Bootstrap(ProtectBaseObject):
|
|
|
411
409
|
del data[key]
|
|
412
410
|
# nothing left to process
|
|
413
411
|
if not data:
|
|
414
|
-
self._create_stat(packet, None, True)
|
|
415
412
|
return None
|
|
416
413
|
|
|
417
414
|
# for another NVR in stack
|
|
418
|
-
nvr_id: str | None =
|
|
415
|
+
nvr_id: str | None = action.get("id")
|
|
419
416
|
if nvr_id and nvr_id != self.nvr.id:
|
|
420
|
-
self._create_stat(packet, None, True)
|
|
421
417
|
return None
|
|
422
418
|
|
|
423
419
|
# nothing left to process
|
|
424
420
|
if not (data := self.nvr.unifi_dict_to_dict(data)):
|
|
425
|
-
self._create_stat(packet, None, True)
|
|
426
421
|
return None
|
|
427
422
|
|
|
428
423
|
old_nvr = self.nvr.copy()
|
|
429
424
|
self.nvr = self.nvr.update_from_dict(deepcopy(data))
|
|
430
425
|
|
|
431
|
-
self._create_stat(packet, data, False)
|
|
432
426
|
return WSSubscriptionMessage(
|
|
433
427
|
action=WSAction.UPDATE,
|
|
434
428
|
new_update_id=self.last_update_id,
|
|
@@ -440,7 +434,6 @@ class Bootstrap(ProtectBaseObject):
|
|
|
440
434
|
def _process_device_update(
|
|
441
435
|
self,
|
|
442
436
|
model_type: ModelType,
|
|
443
|
-
packet: WSPacket,
|
|
444
437
|
action: dict[str, Any],
|
|
445
438
|
data: dict[str, Any],
|
|
446
439
|
ignore_stats: bool,
|
|
@@ -453,18 +446,20 @@ class Bootstrap(ProtectBaseObject):
|
|
|
453
446
|
that was generated internally as a result of an event
|
|
454
447
|
that will expire and result in a state change.
|
|
455
448
|
"""
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
449
|
+
if ignore_stats:
|
|
450
|
+
remove_keys = STATS_AND_IGNORE_DEVICE_KEYS_BY_MODEL_TYPE.get(
|
|
451
|
+
model_type, STATS_AND_IGNORE_DEVICE_KEYS
|
|
452
|
+
)
|
|
453
|
+
else:
|
|
454
|
+
remove_keys = IGNORE_DEVICE_KEYS_BY_MODEL_TYPE.get(
|
|
455
|
+
model_type, IGNORE_DEVICE_KEYS
|
|
456
|
+
)
|
|
457
|
+
|
|
459
458
|
for key in remove_keys.intersection(data):
|
|
460
459
|
del data[key]
|
|
461
|
-
|
|
462
|
-
# this overrides the behavior to only update `last_motion` when a new event starts
|
|
463
|
-
if model_type is ModelType.CAMERA and "lastMotion" in data:
|
|
464
|
-
del data["lastMotion"]
|
|
460
|
+
|
|
465
461
|
# nothing left to process
|
|
466
462
|
if not data and not is_ping_back:
|
|
467
|
-
self._create_stat(packet, None, True)
|
|
468
463
|
return None
|
|
469
464
|
|
|
470
465
|
devices: dict[str, ProtectModelWithId] = getattr(self, model_type.devices_key)
|
|
@@ -480,7 +475,6 @@ class Bootstrap(ProtectBaseObject):
|
|
|
480
475
|
|
|
481
476
|
if not data and not is_ping_back:
|
|
482
477
|
# nothing left to process
|
|
483
|
-
self._create_stat(packet, None, True)
|
|
484
478
|
return None
|
|
485
479
|
|
|
486
480
|
old_obj = obj.copy()
|
|
@@ -506,7 +500,6 @@ class Bootstrap(ProtectBaseObject):
|
|
|
506
500
|
_LOGGER.debug("alarm_triggered_at for %s (%s)", obj.id, is_recent)
|
|
507
501
|
|
|
508
502
|
devices[action_id] = obj
|
|
509
|
-
self._create_stat(packet, data, False)
|
|
510
503
|
return WSSubscriptionMessage(
|
|
511
504
|
action=WSAction.UPDATE,
|
|
512
505
|
new_update_id=self.last_update_id,
|
|
@@ -523,9 +516,10 @@ class Bootstrap(ProtectBaseObject):
|
|
|
523
516
|
is_ping_back: bool = False,
|
|
524
517
|
) -> WSSubscriptionMessage | None:
|
|
525
518
|
"""Process a WS packet."""
|
|
519
|
+
capture_ws_stats = self.capture_ws_stats
|
|
526
520
|
action = packet.action_frame.data
|
|
527
521
|
data = packet.data_frame.data
|
|
528
|
-
if
|
|
522
|
+
if capture_ws_stats:
|
|
529
523
|
action = deepcopy(action)
|
|
530
524
|
data = deepcopy(data)
|
|
531
525
|
|
|
@@ -533,33 +527,57 @@ class Bootstrap(ProtectBaseObject):
|
|
|
533
527
|
if new_update_id is not None:
|
|
534
528
|
self.last_update_id = new_update_id
|
|
535
529
|
|
|
530
|
+
message = self._make_ws_packet_message(
|
|
531
|
+
action, data, models, ignore_stats, is_ping_back
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
if capture_ws_stats:
|
|
535
|
+
self._ws_stats.append(
|
|
536
|
+
WSStat(
|
|
537
|
+
model=packet.action_frame.data["modelKey"],
|
|
538
|
+
action=packet.action_frame.data["action"],
|
|
539
|
+
keys=list(packet.data_frame.data),
|
|
540
|
+
keys_set=[] if message is None else list(message.changed_data),
|
|
541
|
+
size=len(packet.raw),
|
|
542
|
+
filtered=message is None,
|
|
543
|
+
),
|
|
544
|
+
)
|
|
545
|
+
|
|
546
|
+
return message
|
|
547
|
+
|
|
548
|
+
def _make_ws_packet_message(
|
|
549
|
+
self,
|
|
550
|
+
action: dict[str, Any],
|
|
551
|
+
data: dict[str, Any],
|
|
552
|
+
models: set[ModelType] | None,
|
|
553
|
+
ignore_stats: bool,
|
|
554
|
+
is_ping_back: bool,
|
|
555
|
+
) -> WSSubscriptionMessage | None:
|
|
556
|
+
"""Process a WS packet."""
|
|
536
557
|
model_key: str = action["modelKey"]
|
|
537
558
|
if (model_type := ModelType.from_string(model_key)) is ModelType.UNKNOWN:
|
|
538
559
|
_LOGGER.debug("Unknown model type: %s", model_key)
|
|
539
|
-
self._create_stat(packet, None, True)
|
|
540
560
|
return None
|
|
541
561
|
|
|
542
562
|
if models and model_type not in models:
|
|
543
|
-
self._create_stat(packet, None, True)
|
|
544
563
|
return None
|
|
545
564
|
|
|
546
565
|
action_action: str = action["action"]
|
|
547
566
|
if action_action == "remove":
|
|
548
|
-
return self._process_remove_packet(model_type,
|
|
567
|
+
return self._process_remove_packet(model_type, action)
|
|
549
568
|
|
|
550
569
|
if not data and not is_ping_back:
|
|
551
|
-
self._create_stat(packet, None, True)
|
|
552
570
|
return None
|
|
553
571
|
|
|
554
572
|
try:
|
|
555
573
|
if action_action == "add":
|
|
556
|
-
return self._process_add_packet(model_type,
|
|
574
|
+
return self._process_add_packet(model_type, data)
|
|
557
575
|
if action_action == "update":
|
|
558
576
|
if model_type is ModelType.NVR:
|
|
559
|
-
return self._process_nvr_update(
|
|
577
|
+
return self._process_nvr_update(action, data, ignore_stats)
|
|
560
578
|
if model_type in ModelType.bootstrap_models_types_and_event_set:
|
|
561
579
|
return self._process_device_update(
|
|
562
|
-
model_type,
|
|
580
|
+
model_type, action, data, ignore_stats, is_ping_back
|
|
563
581
|
)
|
|
564
582
|
except (ValidationError, ValueError) as err:
|
|
565
583
|
self._handle_ws_error(action, err)
|
|
@@ -567,7 +585,6 @@ class Bootstrap(ProtectBaseObject):
|
|
|
567
585
|
_LOGGER.debug(
|
|
568
586
|
"Unexpected bootstrap model type deviceadoptedfor update: %s", model_key
|
|
569
587
|
)
|
|
570
|
-
self._create_stat(packet, None, True)
|
|
571
588
|
return None
|
|
572
589
|
|
|
573
590
|
def _handle_ws_error(self, action: dict[str, Any], err: Exception) -> None:
|
uiprotect/data/devices.py
CHANGED
|
@@ -474,7 +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
|
-
|
|
477
|
+
if "audioTypes" in data:
|
|
478
|
+
data["audioTypes"] = convert_smart_audio_types(data["audioTypes"])
|
|
479
|
+
for key in ("objectTypes", "autoTrackingObjectTypes"):
|
|
478
480
|
if key in data:
|
|
479
481
|
data[key] = convert_smart_types(data[key])
|
|
480
482
|
|
|
@@ -15,9 +15,9 @@ 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=q5lpNJ4keAQ4oDjalwg9eL3kaVzEapfwtcTzZZlrN_M,35691
|
|
18
|
-
uiprotect/data/bootstrap.py,sha256=
|
|
18
|
+
uiprotect/data/bootstrap.py,sha256=ZieXqE0526F3iRbdJ1pRM6QclSatn_SmecfoOPS3nNg,21922
|
|
19
19
|
uiprotect/data/convert.py,sha256=8h6Il_DhMkPRDPj9F_rA2UZIlTuchS3BQD24peKpk2A,2185
|
|
20
|
-
uiprotect/data/devices.py,sha256=
|
|
20
|
+
uiprotect/data/devices.py,sha256=450T-8qXsFad0xNNnXwm3x53e09NT4dgqlefl7lrc2Q,110485
|
|
21
21
|
uiprotect/data/nvr.py,sha256=zCEAI-rKLEpp9P63QDvJi0hGRsuv-PWGssgHw1POYeQ,47648
|
|
22
22
|
uiprotect/data/types.py,sha256=8z8jIoMlfDre7Op1JAN45PLZdAE-4i3gDPSo2uymqu4,17996
|
|
23
23
|
uiprotect/data/user.py,sha256=Wb-ZWSwIJbyUbfVuENtUYbuW-uftHNDcoMH85dvEjkw,7071
|
|
@@ -30,8 +30,8 @@ uiprotect/test_util/__init__.py,sha256=d2g7afa0LSdixQ0kjEDYwafDFME_UlW2LzxpamZ2B
|
|
|
30
30
|
uiprotect/test_util/anonymize.py,sha256=f-8ijU-_y9r-uAbhIPn0f0I6hzJpAkvJzc8UpWihObI,8478
|
|
31
31
|
uiprotect/utils.py,sha256=kVRJwvHP683Sjhi2pnxwCwbaRl_uMQ2qFYSvt9kpfoU,18426
|
|
32
32
|
uiprotect/websocket.py,sha256=JHI_2EZeRPqPyQopsBZS0dr3tu0HaTiqeLazfBXhW_8,7339
|
|
33
|
-
uiprotect-1.
|
|
34
|
-
uiprotect-1.
|
|
35
|
-
uiprotect-1.
|
|
36
|
-
uiprotect-1.
|
|
37
|
-
uiprotect-1.
|
|
33
|
+
uiprotect-1.19.0.dist-info/LICENSE,sha256=INx18jhdbVXMEiiBANeKEbrbz57ckgzxk5uutmmcxGk,1111
|
|
34
|
+
uiprotect-1.19.0.dist-info/METADATA,sha256=L8ov7HOBW9HUayl354kr_XlTo60zmbCvG0ZsMAAm98M,10985
|
|
35
|
+
uiprotect-1.19.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
36
|
+
uiprotect-1.19.0.dist-info/entry_points.txt,sha256=J78AUTPrTTxgI3s7SVgrmGqDP7piX2wuuEORzhDdVRA,47
|
|
37
|
+
uiprotect-1.19.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|