Fix multiple smart detects firing at once for UniFi Protect (#94133)
* Fix multiple smart detects firing at once * Tweak * Clean up logging. Linting * Linting
This commit is contained in:
parent
a6bb70c5d2
commit
9b6a9147c7
8 changed files with 55 additions and 40 deletions
|
@ -14,8 +14,6 @@ from pyunifiprotect.data import (
|
|||
ProtectAdoptableDeviceModel,
|
||||
ProtectModelWithId,
|
||||
Sensor,
|
||||
SmartDetectAudioType,
|
||||
SmartDetectObjectType,
|
||||
)
|
||||
from pyunifiprotect.data.nvr import UOSDisk
|
||||
|
||||
|
@ -364,8 +362,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
|
|||
ufp_value="is_smart_detected",
|
||||
ufp_required_field="can_detect_person",
|
||||
ufp_enabled="is_person_detection_on",
|
||||
ufp_event_obj="last_smart_detect_event",
|
||||
ufp_smart_type=SmartDetectObjectType.PERSON,
|
||||
ufp_event_obj="last_person_detect_event",
|
||||
),
|
||||
ProtectBinaryEventEntityDescription(
|
||||
key="smart_obj_vehicle",
|
||||
|
@ -374,8 +371,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
|
|||
ufp_value="is_smart_detected",
|
||||
ufp_required_field="can_detect_vehicle",
|
||||
ufp_enabled="is_vehicle_detection_on",
|
||||
ufp_event_obj="last_smart_detect_event",
|
||||
ufp_smart_type=SmartDetectObjectType.VEHICLE,
|
||||
ufp_event_obj="last_vehicle_detect_event",
|
||||
),
|
||||
ProtectBinaryEventEntityDescription(
|
||||
key="smart_obj_face",
|
||||
|
@ -384,8 +380,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
|
|||
ufp_value="is_smart_detected",
|
||||
ufp_required_field="can_detect_face",
|
||||
ufp_enabled="is_face_detection_on",
|
||||
ufp_event_obj="last_smart_detect_event",
|
||||
ufp_smart_type=SmartDetectObjectType.FACE,
|
||||
ufp_event_obj="last_face_detect_event",
|
||||
),
|
||||
ProtectBinaryEventEntityDescription(
|
||||
key="smart_obj_package",
|
||||
|
@ -394,8 +389,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
|
|||
ufp_value="is_smart_detected",
|
||||
ufp_required_field="can_detect_package",
|
||||
ufp_enabled="is_package_detection_on",
|
||||
ufp_event_obj="last_smart_detect_event",
|
||||
ufp_smart_type=SmartDetectObjectType.PACKAGE,
|
||||
ufp_event_obj="last_package_detect_event",
|
||||
),
|
||||
ProtectBinaryEventEntityDescription(
|
||||
key="smart_audio_any",
|
||||
|
@ -412,8 +406,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
|
|||
ufp_value="is_smart_detected",
|
||||
ufp_required_field="can_detect_smoke",
|
||||
ufp_enabled="is_smoke_detection_on",
|
||||
ufp_event_obj="last_smart_audio_detect_event",
|
||||
ufp_smart_type=SmartDetectAudioType.SMOKE,
|
||||
ufp_event_obj="last_smoke_detect_event",
|
||||
),
|
||||
ProtectBinaryEventEntityDescription(
|
||||
key="smart_audio_cmonx",
|
||||
|
@ -422,8 +415,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
|
|||
ufp_value="is_smart_detected",
|
||||
ufp_required_field="can_detect_smoke",
|
||||
ufp_enabled="is_smoke_detection_on",
|
||||
ufp_event_obj="last_smart_audio_detect_event",
|
||||
ufp_smart_type=SmartDetectAudioType.CMONX,
|
||||
ufp_event_obj="last_cmonx_detect_event",
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
@ -40,6 +40,11 @@ from .utils import async_dispatch_id as _ufpd, async_get_devices_by_type
|
|||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
ProtectDeviceType = ProtectAdoptableDeviceModel | NVR
|
||||
SMART_EVENTS = {
|
||||
EventType.SMART_DETECT,
|
||||
EventType.SMART_AUDIO_DETECT,
|
||||
EventType.SMART_DETECT_LINE,
|
||||
}
|
||||
|
||||
|
||||
@callback
|
||||
|
@ -223,6 +228,25 @@ class ProtectData:
|
|||
|
||||
# trigger updates for camera that the event references
|
||||
elif isinstance(obj, Event):
|
||||
if obj.type in SMART_EVENTS:
|
||||
if obj.camera is not None:
|
||||
if obj.end is None:
|
||||
_LOGGER.debug(
|
||||
"%s (%s): New smart detection started for %s (%s)",
|
||||
obj.camera.name,
|
||||
obj.camera.mac,
|
||||
obj.smart_detect_types,
|
||||
obj.id,
|
||||
)
|
||||
else:
|
||||
_LOGGER.debug(
|
||||
"%s (%s): Smart detection ended for %s (%s)",
|
||||
obj.camera.name,
|
||||
obj.camera.mac,
|
||||
obj.smart_detect_types,
|
||||
obj.id,
|
||||
)
|
||||
|
||||
if obj.type == 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(
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
"iot_class": "local_push",
|
||||
"loggers": ["pyunifiprotect", "unifi_discovery"],
|
||||
"quality_scale": "platinum",
|
||||
"requirements": ["pyunifiprotect==4.9.1", "unifi-discovery==1.1.7"],
|
||||
"requirements": ["pyunifiprotect==4.10.0", "unifi-discovery==1.1.7"],
|
||||
"ssdp": [
|
||||
{
|
||||
"manufacturer": "Ubiquiti Networks",
|
||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
|||
|
||||
from collections.abc import Callable, Coroutine
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from enum import Enum
|
||||
import logging
|
||||
from typing import Any, Generic, TypeVar, cast
|
||||
|
@ -10,6 +11,7 @@ from typing import Any, Generic, TypeVar, cast
|
|||
from pyunifiprotect.data import NVR, Event, ProtectAdoptableDeviceModel
|
||||
|
||||
from homeassistant.helpers.entity import EntityDescription
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from .utils import get_nested_attr
|
||||
|
||||
|
@ -67,7 +69,6 @@ class ProtectEventMixin(ProtectRequiredKeysMixin[T]):
|
|||
"""Mixin for events."""
|
||||
|
||||
ufp_event_obj: str | None = None
|
||||
ufp_smart_type: str | None = None
|
||||
|
||||
def get_event_obj(self, obj: T) -> Event | None:
|
||||
"""Return value from UniFi Protect device."""
|
||||
|
@ -79,23 +80,22 @@ class ProtectEventMixin(ProtectRequiredKeysMixin[T]):
|
|||
def get_is_on(self, obj: T) -> bool:
|
||||
"""Return value if event is active."""
|
||||
|
||||
value = bool(self.get_ufp_value(obj))
|
||||
if value:
|
||||
event = self.get_event_obj(obj)
|
||||
value = event is not None
|
||||
if not value:
|
||||
_LOGGER.debug("%s (%s): missing event", self.name, obj.mac)
|
||||
event = self.get_event_obj(obj)
|
||||
if event is None:
|
||||
return False
|
||||
|
||||
if event is not None and self.ufp_smart_type is not None:
|
||||
value = self.ufp_smart_type in event.smart_detect_types
|
||||
if not value:
|
||||
_LOGGER.debug(
|
||||
"%s (%s): %s not in %s",
|
||||
self.name,
|
||||
obj.mac,
|
||||
self.ufp_smart_type,
|
||||
event.smart_detect_types,
|
||||
)
|
||||
now = dt_util.utcnow()
|
||||
value = now > event.start
|
||||
if value and event.end is not None and now > event.end:
|
||||
value = False
|
||||
# only log if the recent ended recently
|
||||
if event.end + timedelta(seconds=10) < now:
|
||||
_LOGGER.debug(
|
||||
"%s (%s): end ended at %s",
|
||||
self.name,
|
||||
obj.mac,
|
||||
event.end.isoformat(),
|
||||
)
|
||||
|
||||
if value:
|
||||
_LOGGER.debug("%s (%s): value is on", self.name, obj.mac)
|
||||
|
|
|
@ -15,7 +15,6 @@ from pyunifiprotect.data import (
|
|||
ProtectDeviceModel,
|
||||
ProtectModelWithId,
|
||||
Sensor,
|
||||
SmartDetectObjectType,
|
||||
)
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
|
@ -528,10 +527,9 @@ EVENT_SENSORS: tuple[ProtectSensorEventEntityDescription, ...] = (
|
|||
name="License Plate Detected",
|
||||
icon="mdi:car",
|
||||
translation_key="license_plate",
|
||||
ufp_smart_type=SmartDetectObjectType.LICENSE_PLATE,
|
||||
ufp_value="is_smart_detected",
|
||||
ufp_required_field="can_detect_license_plate",
|
||||
ufp_event_obj="last_smart_detect_event",
|
||||
ufp_event_obj="last_license_plate_detect_event",
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -767,8 +765,7 @@ class ProtectEventSensor(EventEntityMixin, SensorEntity):
|
|||
EventEntityMixin._async_update_device_from_protect(self, device)
|
||||
is_on = self.entity_description.get_is_on(device)
|
||||
is_license_plate = (
|
||||
self.entity_description.ufp_smart_type
|
||||
== SmartDetectObjectType.LICENSE_PLATE
|
||||
self.entity_description.ufp_event_obj == "last_license_plate_detect_event"
|
||||
)
|
||||
if (
|
||||
not is_on
|
||||
|
|
|
@ -2184,7 +2184,7 @@ pytrafikverket==0.3.3
|
|||
pyudev==0.23.2
|
||||
|
||||
# homeassistant.components.unifiprotect
|
||||
pyunifiprotect==4.9.1
|
||||
pyunifiprotect==4.10.0
|
||||
|
||||
# homeassistant.components.uptimerobot
|
||||
pyuptimerobot==22.2.0
|
||||
|
|
|
@ -1598,7 +1598,7 @@ pytrafikverket==0.3.3
|
|||
pyudev==0.23.2
|
||||
|
||||
# homeassistant.components.unifiprotect
|
||||
pyunifiprotect==4.9.1
|
||||
pyunifiprotect==4.10.0
|
||||
|
||||
# homeassistant.components.uptimerobot
|
||||
pyuptimerobot==22.2.0
|
||||
|
|
|
@ -537,7 +537,9 @@ async def test_camera_update_licenseplate(
|
|||
|
||||
new_camera = camera.copy()
|
||||
new_camera.is_smart_detected = True
|
||||
new_camera.last_smart_detect_event_id = event.id
|
||||
new_camera.last_smart_detect_event_ids[
|
||||
SmartDetectObjectType.LICENSE_PLATE
|
||||
] = event.id
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue