Add new features from new UniFi Protect (#82892)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
a5890b2374
commit
596016c2ac
7 changed files with 166 additions and 17 deletions
|
@ -14,6 +14,7 @@ from pyunifiprotect.data import (
|
|||
ProtectAdoptableDeviceModel,
|
||||
ProtectModelWithId,
|
||||
Sensor,
|
||||
SmartDetectAudioType,
|
||||
SmartDetectObjectType,
|
||||
)
|
||||
from pyunifiprotect.data.nvr import UOSDisk
|
||||
|
@ -201,6 +202,24 @@ CAMERA_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = (
|
|||
ufp_value="is_package_detection_on",
|
||||
ufp_perm=PermRequired.NO_WRITE,
|
||||
),
|
||||
ProtectBinaryEntityDescription(
|
||||
key="smart_licenseplate",
|
||||
name="Detections: License Plate",
|
||||
icon="mdi:car",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ufp_required_field="can_detect_license_plate",
|
||||
ufp_value="is_license_plate_detection_on",
|
||||
ufp_perm=PermRequired.NO_WRITE,
|
||||
),
|
||||
ProtectBinaryEntityDescription(
|
||||
key="smart_smoke",
|
||||
name="Detections: Smoke/CO",
|
||||
icon="mdi:fire",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
ufp_required_field="can_detect_smoke",
|
||||
ufp_value="is_smoke_detection_on",
|
||||
ufp_perm=PermRequired.NO_WRITE,
|
||||
),
|
||||
)
|
||||
|
||||
LIGHT_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = (
|
||||
|
@ -381,6 +400,37 @@ MOTION_SENSORS: tuple[ProtectBinaryEventEntityDescription, ...] = (
|
|||
ufp_event_obj="last_smart_detect_event",
|
||||
ufp_smart_type=SmartDetectObjectType.PACKAGE,
|
||||
),
|
||||
ProtectBinaryEventEntityDescription(
|
||||
key="smart_audio_any",
|
||||
name="Audio Detected",
|
||||
icon="mdi:eye",
|
||||
device_class=DEVICE_CLASS_DETECTION,
|
||||
ufp_value="is_smart_detected",
|
||||
ufp_required_field="feature_flags.has_smart_detect",
|
||||
ufp_event_obj="last_smart_audio_detect_event",
|
||||
),
|
||||
ProtectBinaryEventEntityDescription(
|
||||
key="smart_audio_smoke",
|
||||
name="Smoke Alarm Detected",
|
||||
device_class=DEVICE_CLASS_DETECTION,
|
||||
icon="mdi:fire",
|
||||
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,
|
||||
),
|
||||
ProtectBinaryEventEntityDescription(
|
||||
key="smart_audio_cmonx",
|
||||
name="CO Alarm Detected",
|
||||
device_class=DEVICE_CLASS_DETECTION,
|
||||
icon="mdi:fire",
|
||||
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,
|
||||
),
|
||||
)
|
||||
|
||||
DOORLOCK_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = (
|
||||
|
|
|
@ -406,10 +406,13 @@ class ProtectMediaSource(MediaSource):
|
|||
event_text = "Motion Event"
|
||||
elif event_type == EventType.SMART_DETECT.value:
|
||||
if isinstance(event, Event):
|
||||
smart_type = event.smart_detect_types[0]
|
||||
smart_types = event.smart_detect_types
|
||||
else:
|
||||
smart_type = SmartDetectObjectType(event["smartDetectTypes"][0])
|
||||
event_text = f"Smart Detection - {smart_type.name.title()}"
|
||||
smart_types = [
|
||||
SmartDetectObjectType(e) for e in event["smartDetectTypes"]
|
||||
]
|
||||
smart_type_names = [s.name.title().replace("_", " ") for s in smart_types]
|
||||
event_text = f"Smart Detection - {','.join(smart_type_names)}"
|
||||
title += f" {event_text}"
|
||||
|
||||
nvr = data.api.bootstrap.nvr
|
||||
|
|
|
@ -15,6 +15,7 @@ from pyunifiprotect.data import (
|
|||
ProtectDeviceModel,
|
||||
ProtectModelWithId,
|
||||
Sensor,
|
||||
SmartDetectObjectType,
|
||||
)
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
|
@ -527,6 +528,15 @@ MOTION_SENSORS: tuple[ProtectSensorEventEntityDescription, ...] = (
|
|||
ufp_value="is_smart_detected",
|
||||
ufp_event_obj="last_smart_detect_event",
|
||||
),
|
||||
ProtectSensorEventEntityDescription(
|
||||
key="smart_obj_licenseplate",
|
||||
name="License Plate Detected",
|
||||
icon="mdi:car",
|
||||
device_class=DEVICE_CLASS_DETECTION,
|
||||
ufp_value="is_smart_detected",
|
||||
ufp_event_obj="last_smart_detect_event",
|
||||
ufp_smart_type=SmartDetectObjectType.LICENSE_PLATE,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
@ -756,7 +766,20 @@ class ProtectEventSensor(EventEntityMixin, SensorEntity):
|
|||
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
|
||||
# do not call ProtectDeviceSensor method since we want event to get value here
|
||||
EventEntityMixin._async_update_device_from_protect(self, device)
|
||||
if self._event is None:
|
||||
self._attr_native_value = OBJECT_TYPE_NONE
|
||||
if (
|
||||
self.entity_description.ufp_smart_type
|
||||
== SmartDetectObjectType.LICENSE_PLATE
|
||||
):
|
||||
if (
|
||||
self._event is None
|
||||
or self._event.metadata is None
|
||||
or self._event.metadata.license_plate is None
|
||||
):
|
||||
self._attr_native_value = OBJECT_TYPE_NONE
|
||||
else:
|
||||
self._attr_native_value = self._event.metadata.license_plate.name
|
||||
else:
|
||||
self._attr_native_value = self._event.smart_detect_types[0].value
|
||||
if self._event is None:
|
||||
self._attr_native_value = OBJECT_TYPE_NONE
|
||||
else:
|
||||
self._attr_native_value = self._event.smart_detect_types[0].value
|
||||
|
|
|
@ -126,7 +126,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
|||
),
|
||||
ProtectSwitchEntityDescription(
|
||||
key="osd_bitrate",
|
||||
name="Overlay: Show Bitrate",
|
||||
name="Overlay: Show Nerd Mode",
|
||||
icon="mdi:fullscreen",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_value="osd_settings.is_debug_enabled",
|
||||
|
@ -182,6 +182,26 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
|||
ufp_set_method="set_package_detection",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
ProtectSwitchEntityDescription(
|
||||
key="smart_licenseplate",
|
||||
name="Detections: License Plate",
|
||||
icon="mdi:car",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_license_plate",
|
||||
ufp_value="is_license_plate_detection_on",
|
||||
ufp_set_method="set_license_plate_detection",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
ProtectSwitchEntityDescription(
|
||||
key="smart_smoke",
|
||||
name="Detections: Smoke/CO",
|
||||
icon="mdi:fire",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_smoke",
|
||||
ufp_value="is_smoke_detection_on",
|
||||
ufp_set_method="set_smoke_detection",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
)
|
||||
|
||||
PRIVACY_MODE_SWITCH = ProtectSwitchEntityDescription[Camera](
|
||||
|
|
|
@ -50,11 +50,11 @@ async def test_binary_sensor_camera_remove(
|
|||
|
||||
ufp.api.bootstrap.nvr.system_info.ustorage = None
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 6, 6)
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 7, 7)
|
||||
await remove_entities(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 0, 0)
|
||||
await adopt_devices(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 6, 6)
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 7, 7)
|
||||
|
||||
|
||||
async def test_binary_sensor_light_remove(
|
||||
|
@ -120,7 +120,7 @@ async def test_binary_sensor_setup_camera_all(
|
|||
|
||||
ufp.api.bootstrap.nvr.system_info.ustorage = None
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 6, 6)
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 7, 7)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
|
@ -262,7 +262,7 @@ async def test_binary_sensor_update_motion(
|
|||
"""Test binary_sensor motion entity."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 12, 12)
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 13, 13)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.BINARY_SENSOR, doorbell, MOTION_SENSORS[0]
|
||||
|
|
|
@ -13,7 +13,7 @@ from pyunifiprotect.data import (
|
|||
Sensor,
|
||||
SmartDetectObjectType,
|
||||
)
|
||||
from pyunifiprotect.data.nvr import EventMetadata
|
||||
from pyunifiprotect.data.nvr import EventMetadata, LicensePlateMetadata
|
||||
|
||||
from homeassistant.components.unifiprotect.const import (
|
||||
ATTR_EVENT_SCORE,
|
||||
|
@ -62,11 +62,11 @@ async def test_sensor_camera_remove(
|
|||
|
||||
ufp.api.bootstrap.nvr.system_info.ustorage = None
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 12)
|
||||
assert_entity_counts(hass, Platform.SENSOR, 26, 13)
|
||||
await remove_entities(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 12, 9)
|
||||
await adopt_devices(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 12)
|
||||
assert_entity_counts(hass, Platform.SENSOR, 26, 13)
|
||||
|
||||
|
||||
async def test_sensor_sensor_remove(
|
||||
|
@ -318,7 +318,7 @@ async def test_sensor_setup_camera(
|
|||
"""Test sensor entity setup for camera devices."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 12)
|
||||
assert_entity_counts(hass, Platform.SENSOR, 26, 13)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
|
@ -424,7 +424,7 @@ async def test_sensor_setup_camera_with_last_trip_time(
|
|||
"""Test sensor entity setup for camera devices with last trip time."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 25)
|
||||
assert_entity_counts(hass, Platform.SENSOR, 26, 26)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
|
@ -452,7 +452,7 @@ async def test_sensor_update_motion(
|
|||
"""Test sensor motion entity."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 12)
|
||||
assert_entity_counts(hass, Platform.SENSOR, 26, 13)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, doorbell, MOTION_SENSORS[0]
|
||||
|
@ -565,3 +565,54 @@ async def test_sensor_update_alarm_with_last_trip_time(
|
|||
== (fixed_now - timedelta(hours=1)).replace(microsecond=0).isoformat()
|
||||
)
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_camera_update_licenseplate(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: Camera, fixed_now: datetime
|
||||
):
|
||||
"""Test sensor motion entity."""
|
||||
|
||||
camera.feature_flags.smart_detect_types.append(SmartDetectObjectType.LICENSE_PLATE)
|
||||
camera.feature_flags.has_smart_detect = True
|
||||
camera.smart_detect_settings.object_types.append(
|
||||
SmartDetectObjectType.LICENSE_PLATE
|
||||
)
|
||||
|
||||
await init_entry(hass, ufp, [camera])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 24, 13)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, camera, MOTION_SENSORS[1]
|
||||
)
|
||||
|
||||
event_metadata = EventMetadata(
|
||||
license_plate=LicensePlateMetadata(name="ABCD1234", confidence_level=95)
|
||||
)
|
||||
event = Event(
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=fixed_now - timedelta(seconds=1),
|
||||
end=None,
|
||||
score=100,
|
||||
smart_detect_types=[SmartDetectObjectType.LICENSE_PLATE],
|
||||
smart_detect_event_ids=[],
|
||||
metadata=event_metadata,
|
||||
api=ufp.api,
|
||||
)
|
||||
|
||||
new_camera = camera.copy()
|
||||
new_camera.is_smart_detected = True
|
||||
new_camera.last_smart_detect_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}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == "ABCD1234"
|
||||
|
|
|
@ -35,6 +35,8 @@ CAMERA_SWITCHES_BASIC = [
|
|||
for d in CAMERA_SWITCHES
|
||||
if d.name != "Detections: Face"
|
||||
and d.name != "Detections: Package"
|
||||
and d.name != "Detections: License Plate"
|
||||
and d.name != "Detections: Smoke/CO"
|
||||
and d.name != "SSH Enabled"
|
||||
]
|
||||
CAMERA_SWITCHES_NO_EXTRA = [
|
||||
|
|
Loading…
Add table
Reference in a new issue