From e703baba0a644fcfd89391c8d1a6b44b0f7ad2eb Mon Sep 17 00:00:00 2001 From: Christopher Bailey Date: Sat, 16 Mar 2024 19:17:23 -0400 Subject: [PATCH] Add new fields from UniFi Protect v3 (#113631) --- .../components/unifiprotect/number.py | 14 ++++++++++ .../components/unifiprotect/select.py | 17 +++++++++++ .../components/unifiprotect/switch.py | 1 + tests/components/unifiprotect/test_number.py | 14 ++++++---- tests/components/unifiprotect/test_select.py | 28 +++++++++++-------- tests/components/unifiprotect/test_switch.py | 21 ++++++++------ 6 files changed, 70 insertions(+), 25 deletions(-) diff --git a/homeassistant/components/unifiprotect/number.py b/homeassistant/components/unifiprotect/number.py index 89d8552f3ab..49c629ac42f 100644 --- a/homeassistant/components/unifiprotect/number.py +++ b/homeassistant/components/unifiprotect/number.py @@ -120,6 +120,20 @@ CAMERA_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = ( ufp_set_method="set_chime_duration", ufp_perm=PermRequired.WRITE, ), + ProtectNumberEntityDescription( + key="icr_lux", + name="Infrared Custom Lux Trigger", + icon="mdi:white-balance-sunny", + entity_category=EntityCategory.CONFIG, + ufp_min=1, + ufp_max=30, + ufp_step=1, + ufp_required_field="feature_flags.has_led_ir", + ufp_value="icr_lux_display", + ufp_set_method="set_icr_custom_lux", + ufp_enabled="is_ir_led_slider_enabled", + ufp_perm=PermRequired.WRITE, + ), ) LIGHT_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = ( diff --git a/homeassistant/components/unifiprotect/select.py b/homeassistant/components/unifiprotect/select.py index e6e0762f478..6ba90948fca 100644 --- a/homeassistant/components/unifiprotect/select.py +++ b/homeassistant/components/unifiprotect/select.py @@ -42,6 +42,12 @@ from .utils import async_dispatch_id as _ufpd, async_get_light_motion_current _LOGGER = logging.getLogger(__name__) _KEY_LIGHT_MOTION = "light_motion" +HDR_MODES = [ + {"id": "always", "name": "Always On"}, + {"id": "off", "name": "Always Off"}, + {"id": "auto", "name": "Auto"}, +] + INFRARED_MODES = [ {"id": IRLEDMode.AUTO.value, "name": "Auto"}, {"id": IRLEDMode.ON.value, "name": "Always Enable"}, @@ -228,6 +234,17 @@ CAMERA_SELECTS: tuple[ProtectSelectEntityDescription, ...] = ( ufp_set_method="set_chime_type", ufp_perm=PermRequired.WRITE, ), + ProtectSelectEntityDescription( + key="hdr_mode", + name="HDR Mode", + icon="mdi:brightness-7", + entity_category=EntityCategory.CONFIG, + ufp_required_field="feature_flags.has_hdr", + ufp_options=HDR_MODES, + ufp_value="hdr_mode_display", + ufp_set_method="set_hdr_mode", + ufp_perm=PermRequired.WRITE, + ), ) LIGHT_SELECTS: tuple[ProtectSelectEntityDescription, ...] = ( diff --git a/homeassistant/components/unifiprotect/switch.py b/homeassistant/components/unifiprotect/switch.py index 148170d00c0..bd7cfa4d2a2 100644 --- a/homeassistant/components/unifiprotect/switch.py +++ b/homeassistant/components/unifiprotect/switch.py @@ -74,6 +74,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( name="HDR Mode", icon="mdi:brightness-7", entity_category=EntityCategory.CONFIG, + entity_registry_enabled_default=False, ufp_required_field="feature_flags.has_hdr", ufp_value="hdr_mode", ufp_set_method="set_hdr", diff --git a/tests/components/unifiprotect/test_number.py b/tests/components/unifiprotect/test_number.py index 15dec4b0c74..5eeb5308d62 100644 --- a/tests/components/unifiprotect/test_number.py +++ b/tests/components/unifiprotect/test_number.py @@ -6,7 +6,7 @@ from datetime import timedelta from unittest.mock import AsyncMock, Mock import pytest -from pyunifiprotect.data import Camera, Doorlock, Light +from pyunifiprotect.data import Camera, Doorlock, IRLEDMode, Light from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION from homeassistant.components.unifiprotect.number import ( @@ -35,11 +35,11 @@ async def test_number_sensor_camera_remove( """Test removing and re-adding a camera device.""" await init_entry(hass, ufp, [camera, unadopted_camera]) - assert_entity_counts(hass, Platform.NUMBER, 3, 3) + assert_entity_counts(hass, Platform.NUMBER, 4, 4) await remove_entities(hass, ufp, [camera, unadopted_camera]) assert_entity_counts(hass, Platform.NUMBER, 0, 0) await adopt_devices(hass, ufp, [camera, unadopted_camera]) - assert_entity_counts(hass, Platform.NUMBER, 3, 3) + assert_entity_counts(hass, Platform.NUMBER, 4, 4) async def test_number_sensor_light_remove( @@ -99,8 +99,11 @@ async def test_number_setup_camera_all( camera.feature_flags.has_chime = True camera.chime_duration = timedelta(seconds=1) + camera.feature_flags.has_led_ir = True + camera.isp_settings.icr_custom_value = 1 + camera.isp_settings.ir_led_mode = IRLEDMode.CUSTOM await init_entry(hass, ufp, [camera]) - assert_entity_counts(hass, Platform.NUMBER, 4, 4) + assert_entity_counts(hass, Platform.NUMBER, 5, 5) entity_registry = er.async_get(hass) @@ -128,6 +131,7 @@ async def test_number_setup_camera_none( camera.feature_flags.has_mic = False # has_wdr is an the inverse of has HDR camera.feature_flags.has_hdr = True + camera.feature_flags.has_led_ir = False await init_entry(hass, ufp, [camera]) assert_entity_counts(hass, Platform.NUMBER, 0, 0) @@ -199,7 +203,7 @@ async def test_number_camera_simple( """Tests all simple numbers for cameras.""" await init_entry(hass, ufp, [camera]) - assert_entity_counts(hass, Platform.NUMBER, 3, 3) + assert_entity_counts(hass, Platform.NUMBER, 4, 4) assert description.ufp_set_method is not None diff --git a/tests/components/unifiprotect/test_select.py b/tests/components/unifiprotect/test_select.py index 6987e526e34..7c6e449be5e 100644 --- a/tests/components/unifiprotect/test_select.py +++ b/tests/components/unifiprotect/test_select.py @@ -48,11 +48,11 @@ async def test_select_camera_remove( ufp.api.bootstrap.nvr.system_info.ustorage = None await init_entry(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.SELECT, 4, 4) + assert_entity_counts(hass, Platform.SELECT, 5, 5) await remove_entities(hass, ufp, [doorbell, unadopted_camera]) assert_entity_counts(hass, Platform.SELECT, 0, 0) await adopt_devices(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.SELECT, 4, 4) + assert_entity_counts(hass, Platform.SELECT, 5, 5) async def test_select_light_remove( @@ -142,10 +142,16 @@ async def test_select_setup_camera_all( """Test select entity setup for camera devices (all features).""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SELECT, 4, 4) + assert_entity_counts(hass, Platform.SELECT, 5, 5) entity_registry = er.async_get(hass) - expected_values = ("Always", "Auto", "Default Message (Welcome)", "None") + expected_values = ( + "Always", + "Auto", + "Default Message (Welcome)", + "None", + "Always Off", + ) for index, description in enumerate(CAMERA_SELECTS): unique_id, entity_id = ids_from_device_description( @@ -233,7 +239,7 @@ async def test_select_update_doorbell_settings( """Test select entity update (new Doorbell Message).""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SELECT, 4, 4) + assert_entity_counts(hass, Platform.SELECT, 5, 5) expected_length = len(ufp.api.bootstrap.nvr.doorbell_settings.all_messages) + 1 @@ -279,7 +285,7 @@ async def test_select_update_doorbell_message( """Test select entity update (change doorbell message).""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SELECT, 4, 4) + assert_entity_counts(hass, Platform.SELECT, 5, 5) _, entity_id = ids_from_device_description( Platform.SELECT, doorbell, CAMERA_SELECTS[2] @@ -372,7 +378,7 @@ async def test_select_set_option_camera_recording( """Test Recording Mode select.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SELECT, 4, 4) + assert_entity_counts(hass, Platform.SELECT, 5, 5) _, entity_id = ids_from_device_description( Platform.SELECT, doorbell, CAMERA_SELECTS[0] @@ -397,7 +403,7 @@ async def test_select_set_option_camera_ir( """Test Infrared Mode select.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SELECT, 4, 4) + assert_entity_counts(hass, Platform.SELECT, 5, 5) _, entity_id = ids_from_device_description( Platform.SELECT, doorbell, CAMERA_SELECTS[1] @@ -422,7 +428,7 @@ async def test_select_set_option_camera_doorbell_custom( """Test Doorbell Text select (user defined message).""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SELECT, 4, 4) + assert_entity_counts(hass, Platform.SELECT, 5, 5) _, entity_id = ids_from_device_description( Platform.SELECT, doorbell, CAMERA_SELECTS[2] @@ -449,7 +455,7 @@ async def test_select_set_option_camera_doorbell_unifi( """Test Doorbell Text select (unifi message).""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SELECT, 4, 4) + assert_entity_counts(hass, Platform.SELECT, 5, 5) _, entity_id = ids_from_device_description( Platform.SELECT, doorbell, CAMERA_SELECTS[2] @@ -491,7 +497,7 @@ async def test_select_set_option_camera_doorbell_default( """Test Doorbell Text select (default message).""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SELECT, 4, 4) + assert_entity_counts(hass, Platform.SELECT, 5, 5) _, entity_id = ids_from_device_description( Platform.SELECT, doorbell, CAMERA_SELECTS[2] diff --git a/tests/components/unifiprotect/test_switch.py b/tests/components/unifiprotect/test_switch.py index 1ad3baf5db1..562eec8c5d0 100644 --- a/tests/components/unifiprotect/test_switch.py +++ b/tests/components/unifiprotect/test_switch.py @@ -38,13 +38,16 @@ CAMERA_SWITCHES_BASIC = [ and d.name != "SSH Enabled" and d.name != "Color Night Vision" and d.name != "Tracking: Person" + and d.name != "HDR Mode" ) or d.name == "Detections: Motion" or d.name == "Detections: Person" or d.name == "Detections: Vehicle" ] CAMERA_SWITCHES_NO_EXTRA = [ - d for d in CAMERA_SWITCHES_BASIC if d.name not in ("High FPS", "Privacy Mode") + d + for d in CAMERA_SWITCHES_BASIC + if d.name not in ("High FPS", "Privacy Mode", "HDR Mode") ] @@ -55,11 +58,11 @@ async def test_switch_camera_remove( ufp.api.bootstrap.nvr.system_info.ustorage = None await init_entry(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.SWITCH, 15, 14) + assert_entity_counts(hass, Platform.SWITCH, 15, 13) await remove_entities(hass, ufp, [doorbell, unadopted_camera]) assert_entity_counts(hass, Platform.SWITCH, 2, 2) await adopt_devices(hass, ufp, [doorbell, unadopted_camera]) - assert_entity_counts(hass, Platform.SWITCH, 15, 14) + assert_entity_counts(hass, Platform.SWITCH, 15, 13) async def test_switch_light_remove( @@ -171,7 +174,7 @@ async def test_switch_setup_camera_all( """Test switch entity setup for camera devices (all enabled feature flags).""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SWITCH, 15, 14) + assert_entity_counts(hass, Platform.SWITCH, 15, 13) entity_registry = er.async_get(hass) @@ -294,7 +297,7 @@ async def test_switch_camera_ssh( """Tests SSH switch for cameras.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SWITCH, 15, 14) + assert_entity_counts(hass, Platform.SWITCH, 15, 13) description = CAMERA_SWITCHES[0] @@ -327,7 +330,7 @@ async def test_switch_camera_simple( """Tests all simple switches for cameras.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SWITCH, 15, 14) + assert_entity_counts(hass, Platform.SWITCH, 15, 13) assert description.ufp_set_method is not None @@ -356,7 +359,7 @@ async def test_switch_camera_highfps( """Tests High FPS switch for cameras.""" await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SWITCH, 15, 14) + assert_entity_counts(hass, Platform.SWITCH, 15, 13) description = CAMERA_SWITCHES[3] @@ -387,7 +390,7 @@ async def test_switch_camera_privacy( previous_record = doorbell.recording_settings.mode = RecordingMode.DETECTIONS await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SWITCH, 15, 14) + assert_entity_counts(hass, Platform.SWITCH, 15, 13) description = PRIVACY_MODE_SWITCH @@ -439,7 +442,7 @@ async def test_switch_camera_privacy_already_on( doorbell.add_privacy_zone() await init_entry(hass, ufp, [doorbell]) - assert_entity_counts(hass, Platform.SWITCH, 15, 14) + assert_entity_counts(hass, Platform.SWITCH, 15, 13) description = PRIVACY_MODE_SWITCH