uiprotect 3.7.0__tar.gz → 4.0.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-3.7.0 → uiprotect-4.0.0}/PKG-INFO +1 -1
- {uiprotect-3.7.0 → uiprotect-4.0.0}/pyproject.toml +1 -1
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/api.py +0 -1
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/data/base.py +1 -1
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/data/bootstrap.py +19 -23
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/data/devices.py +0 -11
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/utils.py +8 -5
- {uiprotect-3.7.0 → uiprotect-4.0.0}/LICENSE +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/README.md +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/__init__.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/__main__.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/cli/__init__.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/cli/backup.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/cli/base.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/cli/cameras.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/cli/chimes.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/cli/doorlocks.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/cli/events.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/cli/lights.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/cli/liveviews.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/cli/nvr.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/cli/sensors.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/cli/viewers.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/data/__init__.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/data/convert.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/data/nvr.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/data/types.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/data/user.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/data/websocket.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/exceptions.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/py.typed +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/release_cache.json +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/stream.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/test_util/__init__.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/test_util/anonymize.py +0 -0
- {uiprotect-3.7.0 → uiprotect-4.0.0}/src/uiprotect/websocket.py +0 -0
|
@@ -647,7 +647,7 @@ class ProtectModelWithId(ProtectModel):
|
|
|
647
647
|
await self._update_sync.event.wait()
|
|
648
648
|
self._update_sync.event.clear()
|
|
649
649
|
return
|
|
650
|
-
except (TimeoutError, asyncio.TimeoutError
|
|
650
|
+
except (TimeoutError, asyncio.TimeoutError):
|
|
651
651
|
async with self._update_sync.lock:
|
|
652
652
|
# Important! Now that we have the lock, we yield to the event loop so any
|
|
653
653
|
# updates from the websocket are processed before we generate the diff
|
|
@@ -464,13 +464,6 @@ class Bootstrap(ProtectBaseObject):
|
|
|
464
464
|
if TYPE_CHECKING:
|
|
465
465
|
assert isinstance(obj, Event)
|
|
466
466
|
self.process_event(obj)
|
|
467
|
-
elif model_type is ModelType.CAMERA:
|
|
468
|
-
if TYPE_CHECKING:
|
|
469
|
-
assert isinstance(obj, Camera)
|
|
470
|
-
if "last_ring" in data and (last_ring := obj.last_ring):
|
|
471
|
-
if is_recent := last_ring + RECENT_EVENT_MAX >= utc_now():
|
|
472
|
-
obj.set_ring_timeout()
|
|
473
|
-
_LOGGER.debug("last_ring for %s (%s)", obj.id, is_recent)
|
|
474
467
|
elif model_type is ModelType.SENSOR:
|
|
475
468
|
if TYPE_CHECKING:
|
|
476
469
|
assert isinstance(obj, Sensor)
|
|
@@ -560,34 +553,38 @@ class Bootstrap(ProtectBaseObject):
|
|
|
560
553
|
model_type, action, data, ignore_stats, is_ping_back
|
|
561
554
|
)
|
|
562
555
|
except (ValidationError, ValueError) as err:
|
|
563
|
-
self._handle_ws_error(action, err)
|
|
556
|
+
self._handle_ws_error(action_action, model_type, action, err)
|
|
564
557
|
|
|
565
558
|
_LOGGER.debug(
|
|
566
559
|
"Unexpected bootstrap model type deviceadoptedfor update: %s", model_key
|
|
567
560
|
)
|
|
568
561
|
return None
|
|
569
562
|
|
|
570
|
-
def _handle_ws_error(
|
|
563
|
+
def _handle_ws_error(
|
|
564
|
+
self,
|
|
565
|
+
action_action: str,
|
|
566
|
+
model_type: ModelType,
|
|
567
|
+
action: dict[str, Any],
|
|
568
|
+
err: Exception,
|
|
569
|
+
) -> None:
|
|
571
570
|
msg = ""
|
|
572
|
-
|
|
573
|
-
|
|
571
|
+
device_id: str = action["id"]
|
|
572
|
+
if model_type is ModelType.EVENT:
|
|
573
|
+
msg = f"Validation error processing event: {device_id}. Ignoring event."
|
|
574
574
|
else:
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
msg = f"{action['action']} packet caused invalid state. Unable to refresh device."
|
|
583
|
-
else:
|
|
584
|
-
msg = f"{action['action']} packet caused invalid state. Refreshing device: {model_type} {device_id}"
|
|
575
|
+
task = asyncio.create_task(self.refresh_device(model_type, device_id))
|
|
576
|
+
self._refresh_tasks.add(task)
|
|
577
|
+
task.add_done_callback(self._refresh_tasks.discard)
|
|
578
|
+
msg = (
|
|
579
|
+
f"{action_action} packet caused invalid state. "
|
|
580
|
+
f"Refreshing device: {model_type} {device_id}"
|
|
581
|
+
)
|
|
585
582
|
_LOGGER.debug("%s Error: %s", msg, err)
|
|
586
583
|
|
|
587
584
|
async def refresh_device(self, model_type: ModelType, device_id: str) -> None:
|
|
588
585
|
"""Refresh a device in the bootstrap."""
|
|
589
586
|
try:
|
|
590
|
-
if model_type
|
|
587
|
+
if model_type is ModelType.NVR:
|
|
591
588
|
device: ProtectModelWithId = await self._api.get_nvr()
|
|
592
589
|
else:
|
|
593
590
|
device = await self._api.get_device(model_type, device_id)
|
|
@@ -595,7 +592,6 @@ class Bootstrap(ProtectBaseObject):
|
|
|
595
592
|
ValidationError,
|
|
596
593
|
TimeoutError,
|
|
597
594
|
asyncio.TimeoutError,
|
|
598
|
-
asyncio.CancelledError,
|
|
599
595
|
ClientError,
|
|
600
596
|
ServerDisconnectedError,
|
|
601
597
|
):
|
|
@@ -977,7 +977,6 @@ class Camera(ProtectMotionDeviceModel):
|
|
|
977
977
|
last_smart_detect_event_ids: dict[SmartDetectObjectType, str] = {}
|
|
978
978
|
last_smart_audio_detect_event_ids: dict[SmartDetectAudioType, str] = {}
|
|
979
979
|
talkback_stream: TalkbackStream | None = None
|
|
980
|
-
_last_ring_timeout: datetime | None = PrivateAttr(None)
|
|
981
980
|
|
|
982
981
|
@classmethod
|
|
983
982
|
@cache
|
|
@@ -1845,12 +1844,6 @@ class Camera(ProtectMotionDeviceModel):
|
|
|
1845
1844
|
# endregion
|
|
1846
1845
|
# endregion
|
|
1847
1846
|
|
|
1848
|
-
@property
|
|
1849
|
-
def is_ringing(self) -> bool:
|
|
1850
|
-
if self._last_ring_timeout is None:
|
|
1851
|
-
return False
|
|
1852
|
-
return utc_now() < self._last_ring_timeout
|
|
1853
|
-
|
|
1854
1847
|
@property
|
|
1855
1848
|
def chime_type(self) -> ChimeType:
|
|
1856
1849
|
if self.chime_duration.total_seconds() == 0.3:
|
|
@@ -1935,10 +1928,6 @@ class Camera(ProtectMotionDeviceModel):
|
|
|
1935
1928
|
|
|
1936
1929
|
return False
|
|
1937
1930
|
|
|
1938
|
-
def set_ring_timeout(self) -> None:
|
|
1939
|
-
self._last_ring_timeout = utc_now() + EVENT_PING_INTERVAL
|
|
1940
|
-
self._event_callback_ping()
|
|
1941
|
-
|
|
1942
1931
|
def get_privacy_zone(self) -> tuple[int | None, CameraZone | None]:
|
|
1943
1932
|
for index, zone in enumerate(self.privacy_zones):
|
|
1944
1933
|
if zone.name == PRIVACY_ZONE_NAME:
|
|
@@ -201,11 +201,14 @@ def to_camel_case(name: str) -> str:
|
|
|
201
201
|
return name
|
|
202
202
|
|
|
203
203
|
|
|
204
|
+
_EMPTY_UUID = UUID("0" * 32)
|
|
205
|
+
|
|
206
|
+
|
|
204
207
|
def convert_unifi_data(value: Any, field: ModelField) -> Any:
|
|
205
208
|
"""Converts value from UFP data into pydantic field class"""
|
|
206
209
|
type_ = field.type_
|
|
207
210
|
|
|
208
|
-
if type_
|
|
211
|
+
if type_ is Any:
|
|
209
212
|
return value
|
|
210
213
|
|
|
211
214
|
shape = field.shape
|
|
@@ -222,7 +225,7 @@ def convert_unifi_data(value: Any, field: ModelField) -> Any:
|
|
|
222
225
|
return ip_address(value)
|
|
223
226
|
except ValueError:
|
|
224
227
|
return value
|
|
225
|
-
if type_
|
|
228
|
+
if type_ is datetime:
|
|
226
229
|
return from_js_time(value)
|
|
227
230
|
if type_ in _CREATE_TYPES or _is_enum_type(type_):
|
|
228
231
|
# cannot do this check too soon because some types cannot be used in isinstance
|
|
@@ -230,8 +233,8 @@ def convert_unifi_data(value: Any, field: ModelField) -> Any:
|
|
|
230
233
|
return value
|
|
231
234
|
# handle edge case for improperly formatted UUIDs
|
|
232
235
|
# 00000000-0000-00 0- 000-000000000000
|
|
233
|
-
if type_
|
|
234
|
-
|
|
236
|
+
if type_ is UUID and value == _BAD_UUID:
|
|
237
|
+
return _EMPTY_UUID
|
|
235
238
|
return type_(value)
|
|
236
239
|
|
|
237
240
|
return value
|
|
@@ -574,7 +577,7 @@ def log_event(event: Event) -> None:
|
|
|
574
577
|
)
|
|
575
578
|
smart_settings = camera.smart_detect_settings
|
|
576
579
|
for smart_type in event.smart_detect_types:
|
|
577
|
-
is_audio = event.type
|
|
580
|
+
is_audio = event.type is EventType.SMART_AUDIO_DETECT
|
|
578
581
|
if is_audio:
|
|
579
582
|
if smart_type.audio_type is None:
|
|
580
583
|
return
|
|
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
|