mirror of
https://github.com/home-assistant/core.git
synced 2026-08-24 02:24:51 -05:00
Migrate UniFi Protect camera config switches to the public API (#174963)
This commit is contained in:
@@ -77,7 +77,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="status_light",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="feature_flags.has_led_status",
|
||||
ufp_value="led_settings.is_enabled",
|
||||
ufp_public_value="led_settings.is_enabled",
|
||||
ufp_set_method="set_status_light_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -96,7 +96,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="high_fps",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="feature_flags.has_highfps",
|
||||
ufp_value="is_high_fps_enabled",
|
||||
ufp_public_value="is_high_fps_enabled",
|
||||
ufp_set_method_fn=_set_highfps,
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -114,7 +114,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
key="osd_name",
|
||||
translation_key="overlay_show_name",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_value="osd_settings.is_name_enabled",
|
||||
ufp_public_value="osd_settings.is_name_enabled",
|
||||
ufp_set_method="set_osd_name_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -122,7 +122,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
key="osd_date",
|
||||
translation_key="overlay_show_date",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_value="osd_settings.is_date_enabled",
|
||||
ufp_public_value="osd_settings.is_date_enabled",
|
||||
ufp_set_method="set_osd_date_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -130,7 +130,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
key="osd_logo",
|
||||
translation_key="overlay_show_logo",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_value="osd_settings.is_logo_enabled",
|
||||
ufp_public_value="osd_settings.is_logo_enabled",
|
||||
ufp_set_method="set_osd_logo_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -138,7 +138,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
key="osd_bitrate",
|
||||
translation_key="overlay_show_nerd_mode",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_value="osd_settings.is_debug_enabled",
|
||||
ufp_public_value="osd_settings.is_debug_enabled",
|
||||
ufp_set_method="set_osd_nerd_mode_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -165,8 +165,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_person",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_person",
|
||||
ufp_value="is_person_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_person_detection_on",
|
||||
ufp_set_method="set_person_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -175,8 +174,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_vehicle",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_vehicle",
|
||||
ufp_value="is_vehicle_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_vehicle_detection_on",
|
||||
ufp_set_method="set_vehicle_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -185,8 +183,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_animal",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_animal",
|
||||
ufp_value="is_animal_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_animal_detection_on",
|
||||
ufp_set_method="set_animal_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -195,8 +192,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_package",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_package",
|
||||
ufp_value="is_package_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_package_detection_on",
|
||||
ufp_set_method="set_package_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -205,8 +201,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_license_plate",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_license_plate",
|
||||
ufp_value="is_license_plate_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_license_plate_detection_on",
|
||||
ufp_set_method="set_license_plate_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -215,8 +210,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_smoke",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_smoke",
|
||||
ufp_value="is_smoke_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_smoke_detection_on",
|
||||
ufp_set_method="set_smoke_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -225,8 +219,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_co_alarm",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_co",
|
||||
ufp_value="is_co_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_co_detection_on",
|
||||
ufp_set_method="set_co_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -235,8 +228,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_siren",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_siren",
|
||||
ufp_value="is_siren_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_siren_detection_on",
|
||||
ufp_set_method="set_siren_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -245,8 +237,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_baby_cry",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_baby_cry",
|
||||
ufp_value="is_baby_cry_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_baby_cry_detection_on",
|
||||
ufp_set_method="set_baby_cry_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -255,8 +246,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_speak",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_speaking",
|
||||
ufp_value="is_speaking_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_speaking_detection_on",
|
||||
ufp_set_method="set_speaking_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -265,8 +255,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_bark",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_bark",
|
||||
ufp_value="is_bark_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_bark_detection_on",
|
||||
ufp_set_method="set_bark_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -275,9 +264,8 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_car_alarm",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_car_alarm",
|
||||
ufp_value="is_car_alarm_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
# Public API renamed "car alarm" to "burglar"; internal model keeps the legacy name.
|
||||
ufp_public_value="is_car_alarm_detection_on",
|
||||
ufp_set_method="set_burglar_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -286,8 +274,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_car_horn",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_car_horn",
|
||||
ufp_value="is_car_horn_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_car_horn_detection_on",
|
||||
ufp_set_method="set_car_horn_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
@@ -296,8 +283,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||
translation_key="detections_glass_break",
|
||||
entity_category=EntityCategory.CONFIG,
|
||||
ufp_required_field="can_detect_glass_break",
|
||||
ufp_value="is_glass_break_detection_on",
|
||||
ufp_enabled="is_recording_enabled",
|
||||
ufp_public_value="is_glass_break_detection_on",
|
||||
ufp_set_method="set_glass_break_detection_public",
|
||||
ufp_perm=PermRequired.WRITE,
|
||||
),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
"""Test the UniFi Protect switch platform."""
|
||||
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, Mock, call
|
||||
|
||||
import pytest
|
||||
@@ -44,9 +45,11 @@ from .utils import (
|
||||
enable_entity,
|
||||
ids_from_device_description,
|
||||
init_entry,
|
||||
make_public_camera,
|
||||
make_public_light,
|
||||
public_device_ws_message,
|
||||
remove_entities,
|
||||
setup_public_camera,
|
||||
setup_public_light,
|
||||
)
|
||||
|
||||
@@ -194,6 +197,7 @@ async def test_switch_setup_camera_all(
|
||||
) -> None:
|
||||
"""Test switch entity setup for camera devices (all enabled feature flags)."""
|
||||
|
||||
setup_public_camera(ufp)
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 17, 15)
|
||||
|
||||
@@ -237,6 +241,7 @@ async def test_switch_setup_camera_none(
|
||||
) -> None:
|
||||
"""Test switch entity setup for camera devices (no enabled feature flags)."""
|
||||
|
||||
setup_public_camera(ufp)
|
||||
await init_entry(hass, ufp, [camera])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 8, 7)
|
||||
|
||||
@@ -378,6 +383,7 @@ async def test_switch_camera_simple(
|
||||
) -> None:
|
||||
"""Tests all simple switches for cameras."""
|
||||
|
||||
setup_public_camera(ufp)
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 17, 15)
|
||||
|
||||
@@ -408,6 +414,7 @@ async def test_switch_camera_highfps(
|
||||
) -> None:
|
||||
"""Tests High FPS switch for cameras."""
|
||||
|
||||
setup_public_camera(ufp)
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 17, 15)
|
||||
|
||||
@@ -506,6 +513,7 @@ async def test_switch_camera_detections_public_api(
|
||||
SmartDetectAudioType.GLASS_BREAK,
|
||||
]
|
||||
|
||||
setup_public_camera(ufp)
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
|
||||
assert description.ufp_set_method is not None
|
||||
@@ -529,6 +537,205 @@ async def test_switch_camera_detections_public_api(
|
||||
assert mock_method.call_count == 2
|
||||
|
||||
|
||||
async def test_switch_camera_status_light_public_value(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
) -> None:
|
||||
"""Status light reads from the public object and refreshes on a public WS update."""
|
||||
|
||||
setup_public_camera(ufp)
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
|
||||
description = next(d for d in CAMERA_SWITCHES if d.key == "status_light")
|
||||
_, entity_id = await ids_from_device_description(
|
||||
hass, Platform.SWITCH, doorbell, description
|
||||
)
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
|
||||
public = make_public_camera(doorbell, status_light=True)
|
||||
ufp.devices_ws_subscription(public_device_ws_message(public))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_ON
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("switch_key", "camera_kwarg"),
|
||||
[
|
||||
("osd_name", "osd_name"),
|
||||
("osd_date", "osd_date"),
|
||||
("osd_logo", "osd_logo"),
|
||||
("osd_bitrate", "osd_debug"),
|
||||
],
|
||||
)
|
||||
async def test_switch_camera_osd_public_value(
|
||||
hass: HomeAssistant,
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
switch_key: str,
|
||||
camera_kwarg: str,
|
||||
) -> None:
|
||||
"""Each OSD switch reads its own public osd_settings flag independently."""
|
||||
|
||||
setup_public_camera(ufp)
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
|
||||
description = next(d for d in CAMERA_SWITCHES if d.key == switch_key)
|
||||
_, entity_id = await ids_from_device_description(
|
||||
hass, Platform.SWITCH, doorbell, description
|
||||
)
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
|
||||
public = make_public_camera(doorbell, **{camera_kwarg: True})
|
||||
ufp.devices_ws_subscription(public_device_ws_message(public))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_ON
|
||||
|
||||
|
||||
# Only the switch under test is enabled; ``make_public_camera`` defaults both
|
||||
# lists to every type, so each case pins both to keep the others off.
|
||||
CAMERA_SWITCHES_DETECTION_READ = [
|
||||
("smart_person", [SmartDetectObjectType.PERSON], []),
|
||||
("smart_vehicle", [SmartDetectObjectType.VEHICLE], []),
|
||||
("smart_animal", [SmartDetectObjectType.ANIMAL], []),
|
||||
("smart_package", [SmartDetectObjectType.PACKAGE], []),
|
||||
("smart_licenseplate", [SmartDetectObjectType.LICENSE_PLATE], []),
|
||||
("smart_smoke", [], [SmartDetectAudioType.SMOKE]),
|
||||
("smart_cmonx", [], [SmartDetectAudioType.CMONX]),
|
||||
("smart_siren", [], [SmartDetectAudioType.SIREN]),
|
||||
("smart_baby_cry", [], [SmartDetectAudioType.BABY_CRY]),
|
||||
("smart_speak", [], [SmartDetectAudioType.SPEAK]),
|
||||
("smart_bark", [], [SmartDetectAudioType.BARK]),
|
||||
("smart_car_alarm", [], [SmartDetectAudioType.BURGLAR]),
|
||||
("smart_car_horn", [], [SmartDetectAudioType.CAR_HORN]),
|
||||
("smart_glass_break", [], [SmartDetectAudioType.GLASS_BREAK]),
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("key", "object_types", "audio_types"), CAMERA_SWITCHES_DETECTION_READ
|
||||
)
|
||||
async def test_switch_camera_detection_public_value(
|
||||
hass: HomeAssistant,
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
key: str,
|
||||
object_types: list[SmartDetectObjectType],
|
||||
audio_types: list[SmartDetectAudioType],
|
||||
) -> None:
|
||||
"""Each detection toggle reads its on/off state from its own public flag."""
|
||||
|
||||
doorbell.feature_flags.smart_detect_types = [
|
||||
SmartDetectObjectType.PERSON,
|
||||
SmartDetectObjectType.VEHICLE,
|
||||
SmartDetectObjectType.ANIMAL,
|
||||
SmartDetectObjectType.PACKAGE,
|
||||
SmartDetectObjectType.LICENSE_PLATE,
|
||||
]
|
||||
doorbell.feature_flags.smart_detect_audio_types = [
|
||||
SmartDetectAudioType.SMOKE,
|
||||
SmartDetectAudioType.CMONX,
|
||||
SmartDetectAudioType.SIREN,
|
||||
SmartDetectAudioType.BABY_CRY,
|
||||
SmartDetectAudioType.SPEAK,
|
||||
SmartDetectAudioType.BARK,
|
||||
SmartDetectAudioType.BURGLAR,
|
||||
SmartDetectAudioType.CAR_HORN,
|
||||
SmartDetectAudioType.GLASS_BREAK,
|
||||
]
|
||||
|
||||
setup_public_camera(ufp)
|
||||
|
||||
async def _prime_without_camera() -> Any:
|
||||
pb = ufp.api.public_bootstrap
|
||||
pb.cameras = {}
|
||||
return pb
|
||||
|
||||
ufp.api.update_public = AsyncMock(side_effect=_prime_without_camera)
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
|
||||
description = next(d for d in CAMERA_SWITCHES if d.key == key)
|
||||
_, entity_id = await ids_from_device_description(
|
||||
hass, Platform.SWITCH, doorbell, description
|
||||
)
|
||||
|
||||
all_off = make_public_camera(doorbell, object_types=[], audio_types=[])
|
||||
ufp.devices_ws_subscription(public_device_ws_message(all_off))
|
||||
await hass.async_block_till_done()
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
|
||||
public = make_public_camera(
|
||||
doorbell, object_types=object_types, audio_types=audio_types
|
||||
)
|
||||
ufp.devices_ws_subscription(public_device_ws_message(public))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_ON
|
||||
|
||||
|
||||
async def test_switch_camera_highfps_public_value(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
) -> None:
|
||||
"""The high FPS switch reads video_mode from the public object."""
|
||||
|
||||
setup_public_camera(ufp)
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
|
||||
description = next(d for d in CAMERA_SWITCHES if d.key == "high_fps")
|
||||
_, entity_id = await ids_from_device_description(
|
||||
hass, Platform.SWITCH, doorbell, description
|
||||
)
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
|
||||
public = make_public_camera(doorbell, video_mode=VideoMode.HIGH_FPS)
|
||||
ufp.devices_ws_subscription(public_device_ws_message(public))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_ON
|
||||
|
||||
|
||||
async def test_switch_camera_detection_unavailable_without_public(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
) -> None:
|
||||
"""A migrated detection toggle is unavailable without a public object."""
|
||||
|
||||
async def _prime_without_camera() -> Any:
|
||||
pb = ufp.api.public_bootstrap
|
||||
pb.cameras = {}
|
||||
return pb
|
||||
|
||||
ufp.api.update_public = AsyncMock(side_effect=_prime_without_camera)
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
|
||||
description = next(d for d in CAMERA_SWITCHES if d.key == "smart_person")
|
||||
_, entity_id = await ids_from_device_description(
|
||||
hass, Platform.SWITCH, doorbell, description
|
||||
)
|
||||
assert hass.states.get(entity_id).state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_switch_camera_detection_available_with_recording_disabled(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
) -> None:
|
||||
"""A migrated detection toggle stays available with recording disabled.
|
||||
|
||||
Unlike the legacy private-only switch, the public detection toggles no
|
||||
longer gate their availability on ``is_recording_enabled`` (breaking change).
|
||||
"""
|
||||
|
||||
doorbell.recording_settings.mode = RecordingMode.NEVER
|
||||
setup_public_camera(ufp)
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
|
||||
description = next(d for d in CAMERA_SWITCHES if d.key == "smart_person")
|
||||
_, entity_id = await ids_from_device_description(
|
||||
hass, Platform.SWITCH, doorbell, description
|
||||
)
|
||||
assert hass.states.get(entity_id).state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_switch_camera_privacy(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
) -> None:
|
||||
|
||||
@@ -25,15 +25,18 @@ from uiprotect.data import (
|
||||
Sensor,
|
||||
SmartDetectAudioType,
|
||||
SmartDetectObjectType,
|
||||
VideoMode,
|
||||
WSSubscriptionMessage,
|
||||
)
|
||||
from uiprotect.data.bootstrap import ProtectDeviceRef
|
||||
from uiprotect.data.public_devices import (
|
||||
PublicCamera,
|
||||
PublicCameraLedSettings,
|
||||
PublicHdrMode,
|
||||
PublicLight,
|
||||
PublicLightDeviceSettings,
|
||||
PublicLightModeSettings,
|
||||
PublicOsdSettings,
|
||||
PublicSensor,
|
||||
PublicSensorLeakSettings,
|
||||
PublicSensorMotionSettingsRead,
|
||||
@@ -425,6 +428,12 @@ def make_public_camera(
|
||||
camera: Camera,
|
||||
*,
|
||||
state: DeviceState | None = None,
|
||||
status_light: bool = False,
|
||||
osd_name: bool = False,
|
||||
osd_date: bool = False,
|
||||
osd_logo: bool = False,
|
||||
osd_debug: bool = False,
|
||||
video_mode: VideoMode | None = None,
|
||||
is_motion_detected: bool = False,
|
||||
is_smart_currently_detected: bool = False,
|
||||
is_person_currently_detected: bool = False,
|
||||
@@ -445,13 +454,19 @@ def make_public_camera(
|
||||
mic_volume: int | None = None,
|
||||
hdr_type: PublicHdrMode | None = None,
|
||||
) -> Mock:
|
||||
"""Build a public-API camera mirroring a private camera's migrated fields.
|
||||
"""Build a public-API camera for a private camera's migrated fields.
|
||||
|
||||
The stream tiers/mic/HDR back the migrated stream and select entities; the
|
||||
``is_*`` flags back the migrated ``ufp_public_value`` detection paths and the
|
||||
``smart_detect_settings`` types back the per-type ``ufp_public_enabled_fn``
|
||||
gates (default: all types enabled). ``mic_volume`` and ``hdr_type`` default to
|
||||
values derived from the private fixture so the public mirror matches it.
|
||||
gates (default: all types enabled). ``state``, ``video_mode``, ``mic_volume``
|
||||
and ``hdr_type`` (derived from the private ``hdr_mode_display``) mirror the
|
||||
private camera when not overridden.
|
||||
|
||||
``status_light`` and the ``osd_*`` flags deliberately default to off instead
|
||||
of mirroring, so a test overriding one sets a value the private object would
|
||||
not produce and a wrong ``ufp_public_value``/``ufp_public_value_fn`` fails
|
||||
the test.
|
||||
"""
|
||||
public = Mock(spec=PublicCamera)
|
||||
public.id = camera.id
|
||||
@@ -461,6 +476,14 @@ def make_public_camera(
|
||||
public.type = camera.type
|
||||
public.model = ModelType.CAMERA
|
||||
public.state = DeviceState[camera.state.name] if state is None else state
|
||||
public.led_settings = PublicCameraLedSettings(is_enabled=status_light)
|
||||
public.osd_settings = PublicOsdSettings(
|
||||
is_name_enabled=osd_name,
|
||||
is_date_enabled=osd_date,
|
||||
is_logo_enabled=osd_logo,
|
||||
is_debug_enabled=osd_debug,
|
||||
)
|
||||
public.video_mode = camera.video_mode if video_mode is None else video_mode
|
||||
public.mic_volume = camera.mic_volume if mic_volume is None else mic_volume
|
||||
public.is_motion_detected = is_motion_detected
|
||||
public.is_smart_currently_detected = is_smart_currently_detected
|
||||
@@ -482,12 +505,15 @@ def make_public_camera(
|
||||
audio_types=_ALL_AUDIO_TYPES if audio_types is None else audio_types,
|
||||
)
|
||||
# A Mock(spec) does not evaluate properties, so mirror the PublicCamera
|
||||
# parity properties the migrated detection sensors gate on using the
|
||||
# library's own logic.
|
||||
# parity properties the migrated switches read and the detection sensors
|
||||
# gate on, using the library's own logic.
|
||||
for name in (
|
||||
"is_high_fps_enabled",
|
||||
"is_person_detection_on",
|
||||
"is_vehicle_detection_on",
|
||||
"is_animal_detection_on",
|
||||
"is_package_detection_on",
|
||||
"is_license_plate_detection_on",
|
||||
"is_smoke_detection_on",
|
||||
"is_co_detection_on",
|
||||
"is_siren_detection_on",
|
||||
|
||||
Reference in New Issue
Block a user