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

pull/73252/head
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, ...] = (
ProtectButtonEntityDescription(
key="play",
@ -69,7 +78,11 @@ async def async_setup_entry(
data: ProtectData = hass.data[DOMAIN][entry.entry_id]
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)

View File

@ -140,9 +140,9 @@ class ProtectCamera(ProtectDeviceEntity, Camera):
def _async_update_device_from_protect(self) -> None:
super()._async_update_device_from_protect()
self.channel = self.device.channels[self.channel.id]
motion_enabled = self.device.recording_settings.enable_motion_detection
self._attr_motion_detection_enabled = (
self.device.state == StateType.CONNECTED
and self.device.feature_flags.has_motion_zones
motion_enabled if motion_enabled is not None else True
)
self._attr_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:
"""Return the 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_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(
key="smart_person",
name="Detections: Person",

View File

@ -48,6 +48,9 @@ async def camera_fixture(
):
"""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._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)
return (camera_obj, "camera.test_camera_high")
yield (camera_obj, "camera.test_camera_high")
ProtectCamera.__config__.validate_assignment = True
@pytest.fixture(name="camera_package")
@ -572,3 +577,43 @@ async def test_camera_ws_update_offline(
state = hass.states.get(camera[1])
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.async_block_till_done()
assert_entity_counts(hass, Platform.SWITCH, 12, 11)
assert_entity_counts(hass, Platform.SWITCH, 13, 12)
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.async_block_till_done()
assert_entity_counts(hass, Platform.SWITCH, 5, 4)
assert_entity_counts(hass, Platform.SWITCH, 6, 5)
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.async_block_till_done()
assert_entity_counts(hass, Platform.SWITCH, 6, 5)
assert_entity_counts(hass, Platform.SWITCH, 7, 6)
yield camera_obj