Add new fields from UniFi Protect v3 (#113631)

This commit is contained in:
Christopher Bailey 2024-03-16 19:17:23 -04:00 committed by GitHub
parent 73f11064d7
commit e703baba0a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 70 additions and 25 deletions

View File

@ -120,6 +120,20 @@ CAMERA_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = (
ufp_set_method="set_chime_duration", ufp_set_method="set_chime_duration",
ufp_perm=PermRequired.WRITE, 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, ...] = ( LIGHT_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = (

View File

@ -42,6 +42,12 @@ from .utils import async_dispatch_id as _ufpd, async_get_light_motion_current
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
_KEY_LIGHT_MOTION = "light_motion" _KEY_LIGHT_MOTION = "light_motion"
HDR_MODES = [
{"id": "always", "name": "Always On"},
{"id": "off", "name": "Always Off"},
{"id": "auto", "name": "Auto"},
]
INFRARED_MODES = [ INFRARED_MODES = [
{"id": IRLEDMode.AUTO.value, "name": "Auto"}, {"id": IRLEDMode.AUTO.value, "name": "Auto"},
{"id": IRLEDMode.ON.value, "name": "Always Enable"}, {"id": IRLEDMode.ON.value, "name": "Always Enable"},
@ -228,6 +234,17 @@ CAMERA_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
ufp_set_method="set_chime_type", ufp_set_method="set_chime_type",
ufp_perm=PermRequired.WRITE, 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, ...] = ( LIGHT_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (

View File

@ -74,6 +74,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
name="HDR Mode", name="HDR Mode",
icon="mdi:brightness-7", icon="mdi:brightness-7",
entity_category=EntityCategory.CONFIG, entity_category=EntityCategory.CONFIG,
entity_registry_enabled_default=False,
ufp_required_field="feature_flags.has_hdr", ufp_required_field="feature_flags.has_hdr",
ufp_value="hdr_mode", ufp_value="hdr_mode",
ufp_set_method="set_hdr", ufp_set_method="set_hdr",

View File

@ -6,7 +6,7 @@ from datetime import timedelta
from unittest.mock import AsyncMock, Mock from unittest.mock import AsyncMock, Mock
import pytest 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.const import DEFAULT_ATTRIBUTION
from homeassistant.components.unifiprotect.number import ( 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.""" """Test removing and re-adding a camera device."""
await init_entry(hass, ufp, [camera, unadopted_camera]) 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]) await remove_entities(hass, ufp, [camera, unadopted_camera])
assert_entity_counts(hass, Platform.NUMBER, 0, 0) assert_entity_counts(hass, Platform.NUMBER, 0, 0)
await adopt_devices(hass, ufp, [camera, unadopted_camera]) 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( 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.feature_flags.has_chime = True
camera.chime_duration = timedelta(seconds=1) 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]) 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) entity_registry = er.async_get(hass)
@ -128,6 +131,7 @@ async def test_number_setup_camera_none(
camera.feature_flags.has_mic = False camera.feature_flags.has_mic = False
# has_wdr is an the inverse of has HDR # has_wdr is an the inverse of has HDR
camera.feature_flags.has_hdr = True camera.feature_flags.has_hdr = True
camera.feature_flags.has_led_ir = False
await init_entry(hass, ufp, [camera]) await init_entry(hass, ufp, [camera])
assert_entity_counts(hass, Platform.NUMBER, 0, 0) assert_entity_counts(hass, Platform.NUMBER, 0, 0)
@ -199,7 +203,7 @@ async def test_number_camera_simple(
"""Tests all simple numbers for cameras.""" """Tests all simple numbers for cameras."""
await init_entry(hass, ufp, [camera]) 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 assert description.ufp_set_method is not None

View File

@ -48,11 +48,11 @@ async def test_select_camera_remove(
ufp.api.bootstrap.nvr.system_info.ustorage = None ufp.api.bootstrap.nvr.system_info.ustorage = None
await init_entry(hass, ufp, [doorbell, unadopted_camera]) 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]) await remove_entities(hass, ufp, [doorbell, unadopted_camera])
assert_entity_counts(hass, Platform.SELECT, 0, 0) assert_entity_counts(hass, Platform.SELECT, 0, 0)
await adopt_devices(hass, ufp, [doorbell, unadopted_camera]) 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( 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).""" """Test select entity setup for camera devices (all features)."""
await init_entry(hass, ufp, [doorbell]) 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) 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): for index, description in enumerate(CAMERA_SELECTS):
unique_id, entity_id = ids_from_device_description( 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).""" """Test select entity update (new Doorbell Message)."""
await init_entry(hass, ufp, [doorbell]) 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 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).""" """Test select entity update (change doorbell message)."""
await init_entry(hass, ufp, [doorbell]) 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( _, entity_id = ids_from_device_description(
Platform.SELECT, doorbell, CAMERA_SELECTS[2] Platform.SELECT, doorbell, CAMERA_SELECTS[2]
@ -372,7 +378,7 @@ async def test_select_set_option_camera_recording(
"""Test Recording Mode select.""" """Test Recording Mode select."""
await init_entry(hass, ufp, [doorbell]) 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( _, entity_id = ids_from_device_description(
Platform.SELECT, doorbell, CAMERA_SELECTS[0] Platform.SELECT, doorbell, CAMERA_SELECTS[0]
@ -397,7 +403,7 @@ async def test_select_set_option_camera_ir(
"""Test Infrared Mode select.""" """Test Infrared Mode select."""
await init_entry(hass, ufp, [doorbell]) 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( _, entity_id = ids_from_device_description(
Platform.SELECT, doorbell, CAMERA_SELECTS[1] 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).""" """Test Doorbell Text select (user defined message)."""
await init_entry(hass, ufp, [doorbell]) 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( _, entity_id = ids_from_device_description(
Platform.SELECT, doorbell, CAMERA_SELECTS[2] 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).""" """Test Doorbell Text select (unifi message)."""
await init_entry(hass, ufp, [doorbell]) 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( _, entity_id = ids_from_device_description(
Platform.SELECT, doorbell, CAMERA_SELECTS[2] 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).""" """Test Doorbell Text select (default message)."""
await init_entry(hass, ufp, [doorbell]) 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( _, entity_id = ids_from_device_description(
Platform.SELECT, doorbell, CAMERA_SELECTS[2] Platform.SELECT, doorbell, CAMERA_SELECTS[2]

View File

@ -38,13 +38,16 @@ CAMERA_SWITCHES_BASIC = [
and d.name != "SSH Enabled" and d.name != "SSH Enabled"
and d.name != "Color Night Vision" and d.name != "Color Night Vision"
and d.name != "Tracking: Person" and d.name != "Tracking: Person"
and d.name != "HDR Mode"
) )
or d.name == "Detections: Motion" or d.name == "Detections: Motion"
or d.name == "Detections: Person" or d.name == "Detections: Person"
or d.name == "Detections: Vehicle" or d.name == "Detections: Vehicle"
] ]
CAMERA_SWITCHES_NO_EXTRA = [ 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 ufp.api.bootstrap.nvr.system_info.ustorage = None
await init_entry(hass, ufp, [doorbell, unadopted_camera]) 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]) await remove_entities(hass, ufp, [doorbell, unadopted_camera])
assert_entity_counts(hass, Platform.SWITCH, 2, 2) assert_entity_counts(hass, Platform.SWITCH, 2, 2)
await adopt_devices(hass, ufp, [doorbell, unadopted_camera]) 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( 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).""" """Test switch entity setup for camera devices (all enabled feature flags)."""
await init_entry(hass, ufp, [doorbell]) 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) entity_registry = er.async_get(hass)
@ -294,7 +297,7 @@ async def test_switch_camera_ssh(
"""Tests SSH switch for cameras.""" """Tests SSH switch for cameras."""
await init_entry(hass, ufp, [doorbell]) 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] description = CAMERA_SWITCHES[0]
@ -327,7 +330,7 @@ async def test_switch_camera_simple(
"""Tests all simple switches for cameras.""" """Tests all simple switches for cameras."""
await init_entry(hass, ufp, [doorbell]) 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 assert description.ufp_set_method is not None
@ -356,7 +359,7 @@ async def test_switch_camera_highfps(
"""Tests High FPS switch for cameras.""" """Tests High FPS switch for cameras."""
await init_entry(hass, ufp, [doorbell]) 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] description = CAMERA_SWITCHES[3]
@ -387,7 +390,7 @@ async def test_switch_camera_privacy(
previous_record = doorbell.recording_settings.mode = RecordingMode.DETECTIONS previous_record = doorbell.recording_settings.mode = RecordingMode.DETECTIONS
await init_entry(hass, ufp, [doorbell]) 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 description = PRIVACY_MODE_SWITCH
@ -439,7 +442,7 @@ async def test_switch_camera_privacy_already_on(
doorbell.add_privacy_zone() doorbell.add_privacy_zone()
await init_entry(hass, ufp, [doorbell]) 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 description = PRIVACY_MODE_SWITCH