Dispatch unifiprotect websocket messages based on model (#119633)
This commit is contained in:
parent
de27f24a4c
commit
0c3a5ae5da
6 changed files with 91 additions and 36 deletions
|
@ -6,7 +6,7 @@ from collections.abc import Callable, Iterable
|
|||
from datetime import datetime, timedelta
|
||||
from functools import partial
|
||||
import logging
|
||||
from typing import Any, cast
|
||||
from typing import TYPE_CHECKING, Any, cast
|
||||
|
||||
from typing_extensions import Generator
|
||||
from uiprotect import ProtectApiClient
|
||||
|
@ -16,7 +16,6 @@ from uiprotect.data import (
|
|||
Camera,
|
||||
Event,
|
||||
EventType,
|
||||
Liveview,
|
||||
ModelType,
|
||||
ProtectAdoptableDeviceModel,
|
||||
WSSubscriptionMessage,
|
||||
|
@ -231,41 +230,49 @@ class ProtectData:
|
|||
|
||||
@callback
|
||||
def _async_process_ws_message(self, message: WSSubscriptionMessage) -> None:
|
||||
if message.new_obj is None:
|
||||
"""Process a message from the websocket."""
|
||||
if (new_obj := message.new_obj) is None:
|
||||
if isinstance(message.old_obj, ProtectAdoptableDeviceModel):
|
||||
self._async_remove_device(message.old_obj)
|
||||
return
|
||||
|
||||
obj = message.new_obj
|
||||
if isinstance(obj, (ProtectAdoptableDeviceModel, NVR)):
|
||||
if message.old_obj is None and isinstance(obj, ProtectAdoptableDeviceModel):
|
||||
self._async_add_device(obj)
|
||||
elif getattr(obj, "is_adopted_by_us", True):
|
||||
self._async_update_device(obj, message.changed_data)
|
||||
|
||||
# trigger updates for camera that the event references
|
||||
elif isinstance(obj, Event):
|
||||
model_type = new_obj.model
|
||||
if model_type is ModelType.EVENT:
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(new_obj, Event)
|
||||
if _LOGGER.isEnabledFor(logging.DEBUG):
|
||||
log_event(obj)
|
||||
if obj.type is EventType.DEVICE_ADOPTED:
|
||||
if obj.metadata is not None and obj.metadata.device_id is not None:
|
||||
device = self.api.bootstrap.get_device_from_id(
|
||||
obj.metadata.device_id
|
||||
)
|
||||
if device is not None:
|
||||
self._async_add_device(device)
|
||||
elif obj.camera is not None:
|
||||
self._async_signal_device_update(obj.camera)
|
||||
elif obj.light is not None:
|
||||
self._async_signal_device_update(obj.light)
|
||||
elif obj.sensor is not None:
|
||||
self._async_signal_device_update(obj.sensor)
|
||||
# alert user viewport needs restart so voice clients can get new options
|
||||
elif len(self.api.bootstrap.viewers) > 0 and isinstance(obj, Liveview):
|
||||
log_event(new_obj)
|
||||
if (
|
||||
(new_obj.type is EventType.DEVICE_ADOPTED)
|
||||
and (metadata := new_obj.metadata)
|
||||
and (device_id := metadata.device_id)
|
||||
and (device := self.api.bootstrap.get_device_from_id(device_id))
|
||||
):
|
||||
self._async_add_device(device)
|
||||
elif camera := new_obj.camera:
|
||||
self._async_signal_device_update(camera)
|
||||
elif light := new_obj.light:
|
||||
self._async_signal_device_update(light)
|
||||
elif sensor := new_obj.sensor:
|
||||
self._async_signal_device_update(sensor)
|
||||
return
|
||||
|
||||
if model_type is ModelType.LIVEVIEW and len(self.api.bootstrap.viewers) > 0:
|
||||
# alert user viewport needs restart so voice clients can get new options
|
||||
_LOGGER.warning(
|
||||
"Liveviews updated. Restart Home Assistant to update Viewport select"
|
||||
" options"
|
||||
)
|
||||
return
|
||||
|
||||
if message.old_obj is None and isinstance(new_obj, ProtectAdoptableDeviceModel):
|
||||
self._async_add_device(new_obj)
|
||||
return
|
||||
|
||||
if getattr(new_obj, "is_adopted_by_us", True) and hasattr(new_obj, "mac"):
|
||||
if TYPE_CHECKING:
|
||||
assert isinstance(new_obj, (ProtectAdoptableDeviceModel, NVR))
|
||||
self._async_update_device(new_obj, message.changed_data)
|
||||
|
||||
@callback
|
||||
def _async_process_updates(self, updates: Bootstrap | None) -> None:
|
||||
|
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||
from datetime import datetime, timedelta
|
||||
from unittest.mock import Mock
|
||||
|
||||
from uiprotect.data import Camera, Event, EventType, Light, MountType, Sensor
|
||||
from uiprotect.data import Camera, Event, EventType, Light, ModelType, MountType, Sensor
|
||||
from uiprotect.data.nvr import EventMetadata
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||
|
@ -281,6 +281,7 @@ async def test_binary_sensor_update_motion(
|
|||
)
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION,
|
||||
start=fixed_now - timedelta(seconds=1),
|
||||
|
@ -289,19 +290,21 @@ async def test_binary_sensor_update_motion(
|
|||
smart_detect_types=[],
|
||||
smart_detect_event_ids=[],
|
||||
camera_id=doorbell.id,
|
||||
api=ufp.api,
|
||||
)
|
||||
|
||||
new_camera = doorbell.copy()
|
||||
new_camera.is_motion_detected = True
|
||||
new_camera.last_motion_event_id = event.id
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_camera
|
||||
|
||||
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
||||
ufp.api.bootstrap.events = {event.id: event}
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = event
|
||||
ufp.ws_msg(mock_msg)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
|
@ -325,6 +328,7 @@ async def test_binary_sensor_update_light_motion(
|
|||
|
||||
event_metadata = EventMetadata(light_id=light.id)
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION_LIGHT,
|
||||
start=fixed_now - timedelta(seconds=1),
|
||||
|
|
|
@ -10,6 +10,7 @@ from uiprotect.data import (
|
|||
Camera,
|
||||
Event,
|
||||
EventType,
|
||||
ModelType,
|
||||
Permission,
|
||||
SmartDetectObjectType,
|
||||
)
|
||||
|
@ -72,6 +73,7 @@ async def test_resolve_media_thumbnail(
|
|||
await init_entry(hass, ufp, [doorbell], regenerate_ids=False)
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION,
|
||||
start=fixed_now - timedelta(seconds=20),
|
||||
|
@ -103,6 +105,7 @@ async def test_resolve_media_event(
|
|||
await init_entry(hass, ufp, [doorbell], regenerate_ids=False)
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION,
|
||||
start=fixed_now - timedelta(seconds=20),
|
||||
|
@ -172,6 +175,7 @@ async def test_browse_media_event_ongoing(
|
|||
await init_entry(hass, ufp, [doorbell], regenerate_ids=False)
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION,
|
||||
start=fixed_now - timedelta(seconds=20),
|
||||
|
@ -591,6 +595,7 @@ async def test_browse_media_recent(
|
|||
await init_entry(hass, ufp, [doorbell], regenerate_ids=False)
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION,
|
||||
start=fixed_now - timedelta(seconds=20),
|
||||
|
@ -628,6 +633,7 @@ async def test_browse_media_recent_truncated(
|
|||
await init_entry(hass, ufp, [doorbell], regenerate_ids=False)
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION,
|
||||
start=fixed_now - timedelta(seconds=20),
|
||||
|
@ -660,6 +666,7 @@ async def test_browse_media_recent_truncated(
|
|||
[
|
||||
(
|
||||
Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.RING,
|
||||
start=datetime(1000, 1, 1, 0, 0, 0),
|
||||
|
@ -673,6 +680,7 @@ async def test_browse_media_recent_truncated(
|
|||
),
|
||||
(
|
||||
Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION,
|
||||
start=datetime(1000, 1, 1, 0, 0, 0),
|
||||
|
@ -686,6 +694,7 @@ async def test_browse_media_recent_truncated(
|
|||
),
|
||||
(
|
||||
Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=datetime(1000, 1, 1, 0, 0, 0),
|
||||
|
@ -708,6 +717,7 @@ async def test_browse_media_recent_truncated(
|
|||
),
|
||||
(
|
||||
Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=datetime(1000, 1, 1, 0, 0, 0),
|
||||
|
@ -721,6 +731,7 @@ async def test_browse_media_recent_truncated(
|
|||
),
|
||||
(
|
||||
Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=datetime(1000, 1, 1, 0, 0, 0),
|
||||
|
@ -734,6 +745,7 @@ async def test_browse_media_recent_truncated(
|
|||
),
|
||||
(
|
||||
Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=datetime(1000, 1, 1, 0, 0, 0),
|
||||
|
@ -757,6 +769,7 @@ async def test_browse_media_recent_truncated(
|
|||
),
|
||||
(
|
||||
Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=datetime(1000, 1, 1, 0, 0, 0),
|
||||
|
@ -786,6 +799,7 @@ async def test_browse_media_recent_truncated(
|
|||
),
|
||||
(
|
||||
Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=datetime(1000, 1, 1, 0, 0, 0),
|
||||
|
@ -820,6 +834,7 @@ async def test_browse_media_recent_truncated(
|
|||
),
|
||||
(
|
||||
Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=datetime(1000, 1, 1, 0, 0, 0),
|
||||
|
@ -852,6 +867,7 @@ async def test_browse_media_recent_truncated(
|
|||
),
|
||||
(
|
||||
Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_AUDIO_DETECT,
|
||||
start=datetime(1000, 1, 1, 0, 0, 0),
|
||||
|
@ -906,6 +922,7 @@ async def test_browse_media_eventthumb(
|
|||
await init_entry(hass, ufp, [doorbell], regenerate_ids=False)
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=fixed_now - timedelta(seconds=20),
|
||||
|
@ -969,6 +986,7 @@ async def test_browse_media_browse_day(
|
|||
await init_entry(hass, ufp, [doorbell], regenerate_ids=False)
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION,
|
||||
start=fixed_now - timedelta(seconds=20),
|
||||
|
@ -1010,6 +1028,7 @@ async def test_browse_media_browse_whole_month(
|
|||
await init_entry(hass, ufp, [doorbell], regenerate_ids=False)
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION,
|
||||
start=fixed_now - timedelta(seconds=20),
|
||||
|
@ -1052,6 +1071,7 @@ async def test_browse_media_browse_whole_month_december(
|
|||
await init_entry(hass, ufp, [doorbell], regenerate_ids=False)
|
||||
|
||||
event1 = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=fixed_now - timedelta(seconds=3663),
|
||||
|
@ -1063,6 +1083,7 @@ async def test_browse_media_browse_whole_month_december(
|
|||
)
|
||||
event1._api = ufp.api
|
||||
event2 = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id2",
|
||||
type=EventType.MOTION,
|
||||
start=fixed_now - timedelta(seconds=20),
|
||||
|
@ -1074,6 +1095,7 @@ async def test_browse_media_browse_whole_month_december(
|
|||
)
|
||||
event2._api = ufp.api
|
||||
event3 = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id3",
|
||||
type=EventType.MOTION,
|
||||
start=fixed_now - timedelta(seconds=20),
|
||||
|
@ -1085,6 +1107,7 @@ async def test_browse_media_browse_whole_month_december(
|
|||
)
|
||||
event3._api = ufp.api
|
||||
event4 = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id4",
|
||||
type=EventType.MOTION,
|
||||
start=fixed_now - timedelta(seconds=20),
|
||||
|
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
|||
from datetime import datetime, timedelta
|
||||
from unittest.mock import Mock
|
||||
|
||||
from uiprotect.data import Camera, Event, EventType
|
||||
from uiprotect.data import Camera, Event, EventType, ModelType
|
||||
|
||||
from homeassistant.components.recorder import Recorder
|
||||
from homeassistant.components.recorder.history import get_significant_states
|
||||
|
@ -40,6 +40,7 @@ async def test_exclude_attributes(
|
|||
)
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION,
|
||||
start=fixed_now - timedelta(seconds=1),
|
||||
|
|
|
@ -6,7 +6,15 @@ from datetime import datetime, timedelta
|
|||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from uiprotect.data import NVR, Camera, Event, EventType, Sensor, SmartDetectObjectType
|
||||
from uiprotect.data import (
|
||||
NVR,
|
||||
Camera,
|
||||
Event,
|
||||
EventType,
|
||||
ModelType,
|
||||
Sensor,
|
||||
SmartDetectObjectType,
|
||||
)
|
||||
from uiprotect.data.nvr import EventMetadata, LicensePlateMetadata
|
||||
|
||||
from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION
|
||||
|
@ -438,6 +446,7 @@ async def test_sensor_update_alarm(
|
|||
|
||||
event_metadata = EventMetadata(sensor_id=sensor_all.id, alarm_type="smoke")
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.SENSOR_ALARM,
|
||||
start=fixed_now - timedelta(seconds=1),
|
||||
|
@ -521,6 +530,7 @@ async def test_camera_update_licenseplate(
|
|||
license_plate=LicensePlateMetadata(name="ABCD1234", confidence_level=95)
|
||||
)
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=fixed_now - timedelta(seconds=1),
|
||||
|
|
|
@ -6,7 +6,7 @@ from unittest.mock import AsyncMock, Mock
|
|||
|
||||
from aiohttp import ClientResponse
|
||||
import pytest
|
||||
from uiprotect.data import Camera, Event, EventType
|
||||
from uiprotect.data import Camera, Event, EventType, ModelType
|
||||
from uiprotect.exceptions import ClientError
|
||||
|
||||
from homeassistant.components.unifiprotect.views import (
|
||||
|
@ -179,6 +179,7 @@ async def test_video_bad_event(
|
|||
await init_entry(hass, ufp, [camera])
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
api=ufp.api,
|
||||
camera_id="test_id",
|
||||
start=fixed_now - timedelta(seconds=30),
|
||||
|
@ -205,6 +206,7 @@ async def test_video_bad_event_ongoing(
|
|||
await init_entry(hass, ufp, [camera])
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
api=ufp.api,
|
||||
camera_id=camera.id,
|
||||
start=fixed_now - timedelta(seconds=30),
|
||||
|
@ -232,6 +234,7 @@ async def test_video_bad_perms(
|
|||
await init_entry(hass, ufp, [camera])
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
api=ufp.api,
|
||||
camera_id=camera.id,
|
||||
start=fixed_now - timedelta(seconds=30),
|
||||
|
@ -260,6 +263,7 @@ async def test_video_bad_nvr_id(
|
|||
await init_entry(hass, ufp, [camera])
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
api=ufp.api,
|
||||
camera_id=camera.id,
|
||||
start=fixed_now - timedelta(seconds=30),
|
||||
|
@ -294,6 +298,7 @@ async def test_video_bad_camera_id(
|
|||
await init_entry(hass, ufp, [camera])
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
api=ufp.api,
|
||||
camera_id=camera.id,
|
||||
start=fixed_now - timedelta(seconds=30),
|
||||
|
@ -328,6 +333,7 @@ async def test_video_bad_camera_perms(
|
|||
await init_entry(hass, ufp, [camera])
|
||||
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
api=ufp.api,
|
||||
camera_id=camera.id,
|
||||
start=fixed_now - timedelta(seconds=30),
|
||||
|
@ -368,6 +374,7 @@ async def test_video_bad_params(
|
|||
|
||||
event_start = fixed_now - timedelta(seconds=30)
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
api=ufp.api,
|
||||
camera_id=camera.id,
|
||||
start=event_start,
|
||||
|
@ -405,6 +412,7 @@ async def test_video_bad_video(
|
|||
|
||||
event_start = fixed_now - timedelta(seconds=30)
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
api=ufp.api,
|
||||
camera_id=camera.id,
|
||||
start=event_start,
|
||||
|
@ -447,6 +455,7 @@ async def test_video(
|
|||
|
||||
event_start = fixed_now - timedelta(seconds=30)
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
api=ufp.api,
|
||||
camera_id=camera.id,
|
||||
start=event_start,
|
||||
|
@ -490,6 +499,7 @@ async def test_video_entity_id(
|
|||
|
||||
event_start = fixed_now - timedelta(seconds=30)
|
||||
event = Event(
|
||||
model=ModelType.EVENT,
|
||||
api=ufp.api,
|
||||
camera_id=camera.id,
|
||||
start=event_start,
|
||||
|
|
Loading…
Add table
Reference in a new issue