uiprotect 0.8.0__tar.gz → 0.10.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-0.8.0 → uiprotect-0.10.0}/PKG-INFO +1 -1
- {uiprotect-0.8.0 → uiprotect-0.10.0}/pyproject.toml +1 -1
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/api.py +5 -2
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/data/bootstrap.py +24 -17
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/data/types.py +20 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/LICENSE +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/README.md +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/__init__.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/__main__.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/cli/__init__.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/cli/backup.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/cli/base.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/cli/cameras.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/cli/chimes.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/cli/doorlocks.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/cli/events.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/cli/lights.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/cli/liveviews.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/cli/nvr.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/cli/sensors.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/cli/viewers.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/data/__init__.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/data/base.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/data/convert.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/data/devices.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/data/nvr.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/data/user.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/data/websocket.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/exceptions.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/py.typed +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/release_cache.json +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/stream.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/test_util/__init__.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/test_util/anonymize.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/utils.py +0 -0
- {uiprotect-0.8.0 → uiprotect-0.10.0}/src/uiprotect/websocket.py +0 -0
|
@@ -1075,7 +1075,10 @@ class ProtectApiClient(BaseApiClient):
|
|
|
1075
1075
|
|
|
1076
1076
|
for event_dict in response:
|
|
1077
1077
|
# ignore unknown events
|
|
1078
|
-
if
|
|
1078
|
+
if (
|
|
1079
|
+
"type" not in event_dict
|
|
1080
|
+
or event_dict["type"] not in EventType.values_set()
|
|
1081
|
+
):
|
|
1079
1082
|
_LOGGER.debug("Unknown event type: %s", event_dict)
|
|
1080
1083
|
continue
|
|
1081
1084
|
|
|
@@ -1086,7 +1089,7 @@ class ProtectApiClient(BaseApiClient):
|
|
|
1086
1089
|
continue
|
|
1087
1090
|
|
|
1088
1091
|
if (
|
|
1089
|
-
event.type.value in EventType.
|
|
1092
|
+
event.type.value in EventType.device_events_set()
|
|
1090
1093
|
and event.score >= self._minimum_score
|
|
1091
1094
|
):
|
|
1092
1095
|
events.append(event)
|
|
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
6
|
import logging
|
|
7
|
+
from collections.abc import Iterable
|
|
7
8
|
from copy import deepcopy
|
|
8
9
|
from dataclasses import dataclass
|
|
9
10
|
from datetime import datetime
|
|
@@ -337,7 +338,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
337
338
|
def _create_stat(
|
|
338
339
|
self,
|
|
339
340
|
packet: WSPacket,
|
|
340
|
-
keys_set:
|
|
341
|
+
keys_set: Iterable[str] | None,
|
|
341
342
|
filtered: bool,
|
|
342
343
|
) -> None:
|
|
343
344
|
if self.capture_ws_stats:
|
|
@@ -346,7 +347,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
346
347
|
model=packet.action_frame.data["modelKey"],
|
|
347
348
|
action=packet.action_frame.data["action"],
|
|
348
349
|
keys=list(packet.data_frame.data),
|
|
349
|
-
keys_set=keys_set,
|
|
350
|
+
keys_set=[] if keys_set is None else list(keys_set),
|
|
350
351
|
size=len(packet.raw),
|
|
351
352
|
filtered=filtered,
|
|
352
353
|
),
|
|
@@ -389,7 +390,9 @@ class Bootstrap(ProtectBaseObject):
|
|
|
389
390
|
return None
|
|
390
391
|
|
|
391
392
|
updated = obj.dict()
|
|
392
|
-
|
|
393
|
+
|
|
394
|
+
self._create_stat(packet, updated, False)
|
|
395
|
+
|
|
393
396
|
return WSSubscriptionMessage(
|
|
394
397
|
action=WSAction.ADD,
|
|
395
398
|
new_update_id=self.last_update_id,
|
|
@@ -415,7 +418,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
415
418
|
return None
|
|
416
419
|
self.mac_lookup.pop(device.mac.lower().replace(":", ""), None)
|
|
417
420
|
|
|
418
|
-
self._create_stat(packet,
|
|
421
|
+
self._create_stat(packet, None, False)
|
|
419
422
|
return WSSubscriptionMessage(
|
|
420
423
|
action=WSAction.REMOVE,
|
|
421
424
|
new_update_id=self.last_update_id,
|
|
@@ -433,25 +436,25 @@ class Bootstrap(ProtectBaseObject):
|
|
|
433
436
|
_remove_stats_keys(data)
|
|
434
437
|
# nothing left to process
|
|
435
438
|
if not data:
|
|
436
|
-
self._create_stat(packet,
|
|
439
|
+
self._create_stat(packet, None, True)
|
|
437
440
|
return None
|
|
438
441
|
|
|
439
442
|
# for another NVR in stack
|
|
440
443
|
nvr_id = packet.action_frame.data.get("id")
|
|
441
444
|
if nvr_id and nvr_id != self.nvr.id:
|
|
442
|
-
self._create_stat(packet,
|
|
445
|
+
self._create_stat(packet, None, True)
|
|
443
446
|
return None
|
|
444
447
|
|
|
445
448
|
data = self.nvr.unifi_dict_to_dict(data)
|
|
446
449
|
# nothing left to process
|
|
447
450
|
if not data:
|
|
448
|
-
self._create_stat(packet,
|
|
451
|
+
self._create_stat(packet, None, True)
|
|
449
452
|
return None
|
|
450
453
|
|
|
451
454
|
old_nvr = self.nvr.copy()
|
|
452
455
|
self.nvr = self.nvr.update_from_dict(deepcopy(data))
|
|
453
456
|
|
|
454
|
-
self._create_stat(packet,
|
|
457
|
+
self._create_stat(packet, data, False)
|
|
455
458
|
return WSSubscriptionMessage(
|
|
456
459
|
action=WSAction.UPDATE,
|
|
457
460
|
new_update_id=self.last_update_id,
|
|
@@ -478,7 +481,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
478
481
|
del data["lastMotion"]
|
|
479
482
|
# nothing left to process
|
|
480
483
|
if not data:
|
|
481
|
-
self._create_stat(packet,
|
|
484
|
+
self._create_stat(packet, None, True)
|
|
482
485
|
return None
|
|
483
486
|
|
|
484
487
|
key = f"{model_type}s"
|
|
@@ -492,12 +495,14 @@ class Bootstrap(ProtectBaseObject):
|
|
|
492
495
|
data = obj.unifi_dict_to_dict(data)
|
|
493
496
|
old_obj = obj.copy()
|
|
494
497
|
obj = obj.update_from_dict(deepcopy(data))
|
|
495
|
-
now =
|
|
498
|
+
now: datetime | None = None
|
|
496
499
|
|
|
497
500
|
if isinstance(obj, Event):
|
|
498
501
|
self.process_event(obj)
|
|
499
502
|
elif isinstance(obj, Camera):
|
|
500
503
|
if "last_ring" in data and obj.last_ring:
|
|
504
|
+
if now is None:
|
|
505
|
+
now = utc_now()
|
|
501
506
|
is_recent = obj.last_ring + RECENT_EVENT_MAX >= now
|
|
502
507
|
_LOGGER.debug("last_ring for %s (%s)", obj.id, is_recent)
|
|
503
508
|
if is_recent:
|
|
@@ -507,6 +512,8 @@ class Bootstrap(ProtectBaseObject):
|
|
|
507
512
|
and "alarm_triggered_at" in data
|
|
508
513
|
and obj.alarm_triggered_at
|
|
509
514
|
):
|
|
515
|
+
if now is None:
|
|
516
|
+
now = utc_now()
|
|
510
517
|
is_recent = obj.alarm_triggered_at + RECENT_EVENT_MAX >= now
|
|
511
518
|
_LOGGER.debug("alarm_triggered_at for %s (%s)", obj.id, is_recent)
|
|
512
519
|
if is_recent:
|
|
@@ -514,7 +521,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
514
521
|
|
|
515
522
|
devices[action["id"]] = obj
|
|
516
523
|
|
|
517
|
-
self._create_stat(packet,
|
|
524
|
+
self._create_stat(packet, data, False)
|
|
518
525
|
return WSSubscriptionMessage(
|
|
519
526
|
action=WSAction.UPDATE,
|
|
520
527
|
new_update_id=self.last_update_id,
|
|
@@ -553,20 +560,20 @@ class Bootstrap(ProtectBaseObject):
|
|
|
553
560
|
if action["newUpdateId"] is not None:
|
|
554
561
|
self.last_update_id = action["newUpdateId"]
|
|
555
562
|
|
|
556
|
-
if action["modelKey"] not in ModelType.
|
|
563
|
+
if action["modelKey"] not in ModelType.values_set():
|
|
557
564
|
_LOGGER.debug("Unknown model type: %s", action["modelKey"])
|
|
558
|
-
self._create_stat(packet,
|
|
565
|
+
self._create_stat(packet, None, True)
|
|
559
566
|
return None
|
|
560
567
|
|
|
561
568
|
if len(models) > 0 and ModelType(action["modelKey"]) not in models:
|
|
562
|
-
self._create_stat(packet,
|
|
569
|
+
self._create_stat(packet, None, True)
|
|
563
570
|
return None
|
|
564
571
|
|
|
565
572
|
if action["action"] == "remove":
|
|
566
573
|
return self._process_remove_packet(packet, data)
|
|
567
574
|
|
|
568
575
|
if data is None or len(data) == 0:
|
|
569
|
-
self._create_stat(packet,
|
|
576
|
+
self._create_stat(packet, None, True)
|
|
570
577
|
return None
|
|
571
578
|
|
|
572
579
|
try:
|
|
@@ -577,7 +584,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
577
584
|
if action["modelKey"] == ModelType.NVR.value:
|
|
578
585
|
return self._process_nvr_update(packet, data, ignore_stats)
|
|
579
586
|
if (
|
|
580
|
-
action["modelKey"] in ModelType.
|
|
587
|
+
action["modelKey"] in ModelType.bootstrap_models_set()
|
|
581
588
|
or action["modelKey"] == ModelType.EVENT.value
|
|
582
589
|
):
|
|
583
590
|
return self._process_device_update(
|
|
@@ -593,7 +600,7 @@ class Bootstrap(ProtectBaseObject):
|
|
|
593
600
|
except (ValidationError, ValueError) as err:
|
|
594
601
|
self._handle_ws_error(action, err)
|
|
595
602
|
|
|
596
|
-
self._create_stat(packet,
|
|
603
|
+
self._create_stat(packet, None, True)
|
|
597
604
|
return None
|
|
598
605
|
|
|
599
606
|
def _handle_ws_error(self, action: dict[str, Any], err: Exception) -> None:
|
|
@@ -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
|
|
@@ -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
|
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|