Add UniFi Protect chime button/camera switch (#73195)

This commit is contained in:
Christopher Bailey 2022-06-08 18:29:46 -04:00 committed by GitHub
parent 4435c641de
commit 4c45cb5c52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 81 additions and 7 deletions

View File

@ -43,6 +43,15 @@ ALL_DEVICE_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = (
), ),
) )
SENSOR_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = (
ProtectButtonEntityDescription(
key="clear_tamper",
name="Clear Tamper",
icon="mdi:notification-clear-all",
ufp_press="clear_tamper",
),
)
CHIME_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = ( CHIME_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = (
ProtectButtonEntityDescription( ProtectButtonEntityDescription(
key="play", key="play",
@ -69,7 +78,11 @@ async def async_setup_entry(
data: ProtectData = hass.data[DOMAIN][entry.entry_id] data: ProtectData = hass.data[DOMAIN][entry.entry_id]
entities: list[ProtectDeviceEntity] = async_all_device_entities( entities: list[ProtectDeviceEntity] = async_all_device_entities(
data, ProtectButton, all_descs=ALL_DEVICE_BUTTONS, chime_descs=CHIME_BUTTONS data,
ProtectButton,
all_descs=ALL_DEVICE_BUTTONS,
chime_descs=CHIME_BUTTONS,
sense_descs=SENSOR_BUTTONS,
) )
async_add_entities(entities) async_add_entities(entities)

View File

@ -140,9 +140,9 @@ class ProtectCamera(ProtectDeviceEntity, Camera):
def _async_update_device_from_protect(self) -> None: def _async_update_device_from_protect(self) -> None:
super()._async_update_device_from_protect() super()._async_update_device_from_protect()
self.channel = self.device.channels[self.channel.id] self.channel = self.device.channels[self.channel.id]
motion_enabled = self.device.recording_settings.enable_motion_detection
self._attr_motion_detection_enabled = ( self._attr_motion_detection_enabled = (
self.device.state == StateType.CONNECTED motion_enabled if motion_enabled is not None else True
and self.device.feature_flags.has_motion_zones
) )
self._attr_is_recording = ( self._attr_is_recording = (
self.device.state == StateType.CONNECTED and self.device.is_recording self.device.state == StateType.CONNECTED and self.device.is_recording
@ -171,3 +171,11 @@ class ProtectCamera(ProtectDeviceEntity, Camera):
async def stream_source(self) -> str | None: async def stream_source(self) -> str | None:
"""Return the Stream Source.""" """Return the Stream Source."""
return self._stream_source return self._stream_source
async def async_enable_motion_detection(self) -> None:
"""Call the job and enable motion detection."""
await self.device.set_motion_detection(True)
async def async_disable_motion_detection(self) -> None:
"""Call the job and disable motion detection."""
await self.device.set_motion_detection(False)

View File

@ -133,6 +133,14 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
ufp_value="osd_settings.is_debug_enabled", ufp_value="osd_settings.is_debug_enabled",
ufp_set_method="set_osd_bitrate", ufp_set_method="set_osd_bitrate",
), ),
ProtectSwitchEntityDescription(
key="motion",
name="Detections: Motion",
icon="mdi:run-fast",
entity_category=EntityCategory.CONFIG,
ufp_value="recording_settings.enable_motion_detection",
ufp_set_method="set_motion_detection",
),
ProtectSwitchEntityDescription( ProtectSwitchEntityDescription(
key="smart_person", key="smart_person",
name="Detections: Person", name="Detections: Person",

View File

@ -48,6 +48,9 @@ async def camera_fixture(
): ):
"""Fixture for a single camera for testing the camera platform.""" """Fixture for a single camera for testing the camera platform."""
# disable pydantic validation so mocking can happen
ProtectCamera.__config__.validate_assignment = False
camera_obj = mock_camera.copy(deep=True) camera_obj = mock_camera.copy(deep=True)
camera_obj._api = mock_entry.api camera_obj._api = mock_entry.api
camera_obj.channels[0]._api = mock_entry.api camera_obj.channels[0]._api = mock_entry.api
@ -68,7 +71,9 @@ async def camera_fixture(
assert_entity_counts(hass, Platform.CAMERA, 2, 1) assert_entity_counts(hass, Platform.CAMERA, 2, 1)
return (camera_obj, "camera.test_camera_high") yield (camera_obj, "camera.test_camera_high")
ProtectCamera.__config__.validate_assignment = True
@pytest.fixture(name="camera_package") @pytest.fixture(name="camera_package")
@ -572,3 +577,43 @@ async def test_camera_ws_update_offline(
state = hass.states.get(camera[1]) state = hass.states.get(camera[1])
assert state and state.state == "idle" assert state and state.state == "idle"
async def test_camera_enable_motion(
hass: HomeAssistant,
mock_entry: MockEntityFixture,
camera: tuple[ProtectCamera, str],
):
"""Tests generic entity update service."""
camera[0].__fields__["set_motion_detection"] = Mock()
camera[0].set_motion_detection = AsyncMock()
await hass.services.async_call(
"camera",
"enable_motion_detection",
{ATTR_ENTITY_ID: camera[1]},
blocking=True,
)
camera[0].set_motion_detection.assert_called_once_with(True)
async def test_camera_disable_motion(
hass: HomeAssistant,
mock_entry: MockEntityFixture,
camera: tuple[ProtectCamera, str],
):
"""Tests generic entity update service."""
camera[0].__fields__["set_motion_detection"] = Mock()
camera[0].set_motion_detection = AsyncMock()
await hass.services.async_call(
"camera",
"disable_motion_detection",
{ATTR_ENTITY_ID: camera[1]},
blocking=True,
)
camera[0].set_motion_detection.assert_called_once_with(False)

View File

@ -118,7 +118,7 @@ async def camera_fixture(
await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
assert_entity_counts(hass, Platform.SWITCH, 12, 11) assert_entity_counts(hass, Platform.SWITCH, 13, 12)
yield camera_obj yield camera_obj
@ -161,7 +161,7 @@ async def camera_none_fixture(
await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
assert_entity_counts(hass, Platform.SWITCH, 5, 4) assert_entity_counts(hass, Platform.SWITCH, 6, 5)
yield camera_obj yield camera_obj
@ -205,7 +205,7 @@ async def camera_privacy_fixture(
await hass.config_entries.async_setup(mock_entry.entry.entry_id) await hass.config_entries.async_setup(mock_entry.entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
assert_entity_counts(hass, Platform.SWITCH, 6, 5) assert_entity_counts(hass, Platform.SWITCH, 7, 6)
yield camera_obj yield camera_obj