Add UniFi Protect chime button/camera switch (#73195)
parent
4435c641de
commit
4c45cb5c52
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue