Fix unifiprotect smart detection when end is set (#120027)

This commit is contained in:
J. Nick Koston 2024-06-20 22:03:07 -05:00 committed by GitHub
parent ecadaf314d
commit 68462b014c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 372 additions and 66 deletions

View file

@ -14,6 +14,7 @@ from uiprotect.data import (
ProtectAdoptableDeviceModel,
ProtectModelWithId,
Sensor,
SmartDetectObjectType,
)
from uiprotect.data.nvr import UOSDisk
@ -436,11 +437,13 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
ufp_enabled="is_motion_detection_on",
ufp_event_obj="last_motion_event",
),
)
SMART_EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
ProtectBinaryEventEntityDescription(
key="smart_obj_any",
name="Object detected",
icon="mdi:eye",
ufp_value="is_smart_currently_detected",
ufp_required_field="feature_flags.has_smart_detect",
ufp_event_obj="last_smart_detect_event",
),
@ -448,7 +451,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_obj_person",
name="Person detected",
icon="mdi:walk",
ufp_value="is_person_currently_detected",
ufp_obj_type=SmartDetectObjectType.PERSON,
ufp_required_field="can_detect_person",
ufp_enabled="is_person_detection_on",
ufp_event_obj="last_person_detect_event",
@ -457,7 +460,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_obj_vehicle",
name="Vehicle detected",
icon="mdi:car",
ufp_value="is_vehicle_currently_detected",
ufp_obj_type=SmartDetectObjectType.VEHICLE,
ufp_required_field="can_detect_vehicle",
ufp_enabled="is_vehicle_detection_on",
ufp_event_obj="last_vehicle_detect_event",
@ -466,7 +469,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_obj_animal",
name="Animal detected",
icon="mdi:paw",
ufp_value="is_animal_currently_detected",
ufp_obj_type=SmartDetectObjectType.ANIMAL,
ufp_required_field="can_detect_animal",
ufp_enabled="is_animal_detection_on",
ufp_event_obj="last_animal_detect_event",
@ -475,8 +478,8 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_obj_package",
name="Package detected",
icon="mdi:package-variant-closed",
ufp_value="is_package_currently_detected",
entity_registry_enabled_default=False,
ufp_obj_type=SmartDetectObjectType.PACKAGE,
ufp_required_field="can_detect_package",
ufp_enabled="is_package_detection_on",
ufp_event_obj="last_package_detect_event",
@ -485,7 +488,6 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_audio_any",
name="Audio object detected",
icon="mdi:eye",
ufp_value="is_audio_currently_detected",
ufp_required_field="feature_flags.has_smart_detect",
ufp_event_obj="last_smart_audio_detect_event",
),
@ -493,7 +495,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_audio_smoke",
name="Smoke alarm detected",
icon="mdi:fire",
ufp_value="is_smoke_currently_detected",
ufp_obj_type=SmartDetectObjectType.SMOKE,
ufp_required_field="can_detect_smoke",
ufp_enabled="is_smoke_detection_on",
ufp_event_obj="last_smoke_detect_event",
@ -502,16 +504,16 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_audio_cmonx",
name="CO alarm detected",
icon="mdi:molecule-co",
ufp_value="is_cmonx_currently_detected",
ufp_required_field="can_detect_co",
ufp_enabled="is_co_detection_on",
ufp_event_obj="last_cmonx_detect_event",
ufp_obj_type=SmartDetectObjectType.CMONX,
),
ProtectBinaryEventEntityDescription(
key="smart_audio_siren",
name="Siren detected",
icon="mdi:alarm-bell",
ufp_value="is_siren_currently_detected",
ufp_obj_type=SmartDetectObjectType.SIREN,
ufp_required_field="can_detect_siren",
ufp_enabled="is_siren_detection_on",
ufp_event_obj="last_siren_detect_event",
@ -520,7 +522,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_audio_baby_cry",
name="Baby cry detected",
icon="mdi:cradle",
ufp_value="is_baby_cry_currently_detected",
ufp_obj_type=SmartDetectObjectType.BABY_CRY,
ufp_required_field="can_detect_baby_cry",
ufp_enabled="is_baby_cry_detection_on",
ufp_event_obj="last_baby_cry_detect_event",
@ -529,7 +531,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_audio_speak",
name="Speaking detected",
icon="mdi:account-voice",
ufp_value="is_speaking_currently_detected",
ufp_obj_type=SmartDetectObjectType.SPEAK,
ufp_required_field="can_detect_speaking",
ufp_enabled="is_speaking_detection_on",
ufp_event_obj="last_speaking_detect_event",
@ -538,7 +540,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_audio_bark",
name="Barking detected",
icon="mdi:dog",
ufp_value="is_bark_currently_detected",
ufp_obj_type=SmartDetectObjectType.BARK,
ufp_required_field="can_detect_bark",
ufp_enabled="is_bark_detection_on",
ufp_event_obj="last_bark_detect_event",
@ -547,7 +549,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_audio_car_alarm",
name="Car alarm detected",
icon="mdi:car",
ufp_value="is_car_alarm_currently_detected",
ufp_obj_type=SmartDetectObjectType.BURGLAR,
ufp_required_field="can_detect_car_alarm",
ufp_enabled="is_car_alarm_detection_on",
ufp_event_obj="last_car_alarm_detect_event",
@ -556,7 +558,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_audio_car_horn",
name="Car horn detected",
icon="mdi:bugle",
ufp_value="is_car_horn_currently_detected",
ufp_obj_type=SmartDetectObjectType.CAR_HORN,
ufp_required_field="can_detect_car_horn",
ufp_enabled="is_car_horn_detection_on",
ufp_event_obj="last_car_horn_detect_event",
@ -565,7 +567,7 @@ EVENT_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
key="smart_audio_glass_break",
name="Glass break detected",
icon="mdi:glass-fragile",
ufp_value="last_glass_break_detect",
ufp_obj_type=SmartDetectObjectType.GLASS_BREAK,
ufp_required_field="can_detect_glass_break",
ufp_enabled="is_glass_break_detection_on",
ufp_event_obj="last_glass_break_detect_event",
@ -709,11 +711,50 @@ class ProtectEventBinarySensor(EventEntityMixin, BinarySensorEntity):
@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
super()._async_update_device_from_protect(device)
is_on = self.entity_description.get_is_on(self.device, self._event)
self._attr_is_on = is_on
if not is_on:
self._event = None
description = self.entity_description
event = self._event = self.entity_description.get_event_obj(device)
if is_on := bool(description.get_ufp_value(device)):
if event:
self._set_event_attrs(event)
else:
self._attr_extra_state_attributes = {}
self._attr_is_on = is_on
class ProtectSmartEventBinarySensor(EventEntityMixin, BinarySensorEntity):
"""A UniFi Protect Device Binary Sensor for smart events."""
device: Camera
entity_description: ProtectBinaryEventEntityDescription
_state_attrs = ("_attr_available", "_attr_is_on", "_attr_extra_state_attributes")
@callback
def _set_event_done(self) -> None:
self._attr_is_on = False
self._attr_extra_state_attributes = {}
@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
prev_event = self._event
super()._async_update_device_from_protect(device)
description = self.entity_description
self._event = description.get_event_obj(device)
if not (
(event := self._event)
and not self._event_already_ended(prev_event)
and description.has_matching_smart(event)
and ((is_end := event.end) or self.device.is_smart_detected)
):
self._set_event_done()
return
was_on = self._attr_is_on
self._attr_is_on = True
self._set_event_attrs(event)
if is_end and not was_on:
self._async_event_with_immediate_end()
MODEL_DESCRIPTIONS_WITH_CLASS = (
@ -727,12 +768,19 @@ def _async_event_entities(
data: ProtectData,
ufp_device: ProtectAdoptableDeviceModel | None = None,
) -> list[ProtectDeviceEntity]:
return [
ProtectEventBinarySensor(data, device, description)
for device in (data.get_cameras() if ufp_device is None else [ufp_device])
for description in EVENT_SENSORS
if description.has_required(device)
]
entities: list[ProtectDeviceEntity] = []
for device in data.get_cameras() if ufp_device is None else [ufp_device]:
entities.extend(
ProtectSmartEventBinarySensor(data, device, description)
for description in SMART_EVENT_SENSORS
if description.has_required(device)
)
entities.extend(
ProtectEventBinarySensor(data, device, description)
for description in EVENT_SENSORS
if description.has_required(device)
)
return entities
@callback