"""Test the UniFi Protect number platform.""" # pylint: disable=protected-access from __future__ import annotations from datetime import timedelta from unittest.mock import AsyncMock, Mock import pytest from pyunifiprotect.data import Camera, Doorlock, Light from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION from homeassistant.components.unifiprotect.number import ( CAMERA_NUMBERS, DOORLOCK_NUMBERS, LIGHT_NUMBERS, ProtectNumberEntityDescription, ) from homeassistant.const import ATTR_ATTRIBUTION, ATTR_ENTITY_ID, Platform from homeassistant.core import HomeAssistant from homeassistant.helpers import entity_registry as er from .conftest import ( MockEntityFixture, assert_entity_counts, ids_from_device_description, ) @pytest.fixture(name="light") async def light_fixture( hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light ): """Fixture for a single light for testing the number platform.""" # disable pydantic validation so mocking can happen Light.__config__.validate_assignment = False light_obj = mock_light.copy(deep=True) light_obj._api = mock_entry.api light_obj.name = "Test Light" light_obj.light_device_settings.pir_sensitivity = 45 light_obj.light_device_settings.pir_duration = timedelta(seconds=45) mock_entry.api.bootstrap.reset_objects() mock_entry.api.bootstrap.lights = { light_obj.id: light_obj, } await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() assert_entity_counts(hass, Platform.NUMBER, 2, 2) yield light_obj Light.__config__.validate_assignment = True @pytest.fixture(name="camera") async def camera_fixture( hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera ): """Fixture for a single camera for testing the number platform.""" # disable pydantic validation so mocking can happen Camera.__config__.validate_assignment = False camera_obj = mock_camera.copy(deep=True) camera_obj._api = mock_entry.api camera_obj.channels[0]._api = mock_entry.api camera_obj.channels[1]._api = mock_entry.api camera_obj.channels[2]._api = mock_entry.api camera_obj.name = "Test Camera" camera_obj.feature_flags.can_optical_zoom = True camera_obj.feature_flags.has_mic = True # has_wdr is an the inverse of has HDR camera_obj.feature_flags.has_hdr = False camera_obj.isp_settings.wdr = 0 camera_obj.mic_volume = 0 camera_obj.isp_settings.zoom_position = 0 mock_entry.api.bootstrap.reset_objects() mock_entry.api.bootstrap.cameras = { camera_obj.id: camera_obj, } await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() assert_entity_counts(hass, Platform.NUMBER, 3, 3) yield camera_obj Camera.__config__.validate_assignment = True @pytest.fixture(name="doorlock") async def doorlock_fixture( hass: HomeAssistant, mock_entry: MockEntityFixture, mock_doorlock: Doorlock ): """Fixture for a single doorlock for testing the number platform.""" # disable pydantic validation so mocking can happen Doorlock.__config__.validate_assignment = False lock_obj = mock_doorlock.copy(deep=True) lock_obj._api = mock_entry.api lock_obj.name = "Test Lock" lock_obj.auto_close_time = timedelta(seconds=45) mock_entry.api.bootstrap.reset_objects() mock_entry.api.bootstrap.doorlocks = { lock_obj.id: lock_obj, } await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() assert_entity_counts(hass, Platform.NUMBER, 1, 1) yield lock_obj Doorlock.__config__.validate_assignment = True async def test_number_setup_light( hass: HomeAssistant, light: Light, ): """Test number entity setup for light devices.""" entity_registry = er.async_get(hass) for description in LIGHT_NUMBERS: unique_id, entity_id = ids_from_device_description( Platform.NUMBER, light, description ) entity = entity_registry.async_get(entity_id) assert entity assert entity.unique_id == unique_id state = hass.states.get(entity_id) assert state assert state.state == "45" assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION async def test_number_setup_camera_all( hass: HomeAssistant, camera: Camera, ): """Test number entity setup for camera devices (all features).""" entity_registry = er.async_get(hass) for description in CAMERA_NUMBERS: unique_id, entity_id = ids_from_device_description( Platform.NUMBER, camera, description ) entity = entity_registry.async_get(entity_id) assert entity assert entity.unique_id == unique_id state = hass.states.get(entity_id) assert state assert state.state == "0" assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION async def test_number_setup_camera_none( hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera ): """Test number entity setup for camera devices (no features).""" camera_obj = mock_camera.copy(deep=True) camera_obj._api = mock_entry.api camera_obj.channels[0]._api = mock_entry.api camera_obj.channels[1]._api = mock_entry.api camera_obj.channels[2]._api = mock_entry.api camera_obj.name = "Test Camera" camera_obj.feature_flags.can_optical_zoom = False camera_obj.feature_flags.has_mic = False # has_wdr is an the inverse of has HDR camera_obj.feature_flags.has_hdr = True mock_entry.api.bootstrap.reset_objects() mock_entry.api.bootstrap.cameras = { camera_obj.id: camera_obj, } await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() assert_entity_counts(hass, Platform.NUMBER, 0, 0) async def test_number_setup_camera_missing_attr( hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera ): """Test number entity setup for camera devices (no features, bad attrs).""" # disable pydantic validation so mocking can happen Camera.__config__.validate_assignment = False camera_obj = mock_camera.copy(deep=True) camera_obj._api = mock_entry.api camera_obj.channels[0]._api = mock_entry.api camera_obj.channels[1]._api = mock_entry.api camera_obj.channels[2]._api = mock_entry.api camera_obj.name = "Test Camera" camera_obj.feature_flags = None Camera.__config__.validate_assignment = True mock_entry.api.bootstrap.reset_objects() mock_entry.api.bootstrap.cameras = { camera_obj.id: camera_obj, } await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.async_block_till_done() assert_entity_counts(hass, Platform.NUMBER, 0, 0) async def test_number_light_sensitivity(hass: HomeAssistant, light: Light): """Test sensitivity number entity for lights.""" description = LIGHT_NUMBERS[0] assert description.ufp_set_method is not None light.__fields__["set_sensitivity"] = Mock() light.set_sensitivity = AsyncMock() _, entity_id = ids_from_device_description(Platform.NUMBER, light, description) await hass.services.async_call( "number", "set_value", {ATTR_ENTITY_ID: entity_id, "value": 15.0}, blocking=True ) light.set_sensitivity.assert_called_once_with(15.0) async def test_number_light_duration(hass: HomeAssistant, light: Light): """Test auto-shutoff duration number entity for lights.""" description = LIGHT_NUMBERS[1] light.__fields__["set_duration"] = Mock() light.set_duration = AsyncMock() _, entity_id = ids_from_device_description(Platform.NUMBER, light, description) await hass.services.async_call( "number", "set_value", {ATTR_ENTITY_ID: entity_id, "value": 15.0}, blocking=True ) light.set_duration.assert_called_once_with(timedelta(seconds=15.0)) @pytest.mark.parametrize("description", CAMERA_NUMBERS) async def test_number_camera_simple( hass: HomeAssistant, camera: Camera, description: ProtectNumberEntityDescription ): """Tests all simple numbers for cameras.""" assert description.ufp_set_method is not None camera.__fields__[description.ufp_set_method] = Mock() setattr(camera, description.ufp_set_method, AsyncMock()) set_method = getattr(camera, description.ufp_set_method) _, entity_id = ids_from_device_description(Platform.NUMBER, camera, description) await hass.services.async_call( "number", "set_value", {ATTR_ENTITY_ID: entity_id, "value": 1.0}, blocking=True ) set_method.assert_called_once_with(1.0) async def test_number_lock_auto_close(hass: HomeAssistant, doorlock: Doorlock): """Test auto-lock timeout for locks.""" description = DOORLOCK_NUMBERS[0] doorlock.__fields__["set_auto_close_time"] = Mock() doorlock.set_auto_close_time = AsyncMock() _, entity_id = ids_from_device_description(Platform.NUMBER, doorlock, description) await hass.services.async_call( "number", "set_value", {ATTR_ENTITY_ID: entity_id, "value": 15.0}, blocking=True ) doorlock.set_auto_close_time.assert_called_once_with(timedelta(seconds=15.0))