2021-12-26 06:12:57 +00:00
|
|
|
"""Test the UniFi Protect camera platform."""
|
2021-12-29 04:36:18 +00:00
|
|
|
# pylint: disable=protected-access
|
2021-12-26 06:12:57 +00:00
|
|
|
from __future__ import annotations
|
|
|
|
|
2021-12-31 22:20:23 +00:00
|
|
|
from unittest.mock import AsyncMock, Mock
|
2021-12-26 06:12:57 +00:00
|
|
|
|
2022-05-20 01:34:58 +00:00
|
|
|
from pyunifiprotect.data import Camera as ProtectCamera, CameraChannel, StateType
|
2021-12-26 06:12:57 +00:00
|
|
|
from pyunifiprotect.exceptions import NvrError
|
|
|
|
|
2021-12-27 11:39:24 +00:00
|
|
|
from homeassistant.components.camera import (
|
|
|
|
SUPPORT_STREAM,
|
|
|
|
async_get_image,
|
|
|
|
async_get_stream_source,
|
|
|
|
)
|
2021-12-26 06:12:57 +00:00
|
|
|
from homeassistant.components.unifiprotect.const import (
|
|
|
|
ATTR_BITRATE,
|
|
|
|
ATTR_CHANNEL_ID,
|
|
|
|
ATTR_FPS,
|
|
|
|
ATTR_HEIGHT,
|
|
|
|
ATTR_WIDTH,
|
|
|
|
DEFAULT_ATTRIBUTION,
|
|
|
|
DEFAULT_SCAN_INTERVAL,
|
|
|
|
)
|
2021-12-27 11:39:24 +00:00
|
|
|
from homeassistant.const import (
|
|
|
|
ATTR_ATTRIBUTION,
|
|
|
|
ATTR_ENTITY_ID,
|
|
|
|
ATTR_SUPPORTED_FEATURES,
|
2021-12-29 04:36:18 +00:00
|
|
|
Platform,
|
2021-12-27 11:39:24 +00:00
|
|
|
)
|
2021-12-26 06:12:57 +00:00
|
|
|
from homeassistant.core import HomeAssistant
|
|
|
|
from homeassistant.helpers import entity_registry as er
|
|
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
from .utils import (
|
|
|
|
MockUFPFixture,
|
2022-06-27 21:03:25 +00:00
|
|
|
adopt_devices,
|
2021-12-31 22:20:23 +00:00
|
|
|
assert_entity_counts,
|
|
|
|
enable_entity,
|
2022-06-25 15:15:38 +00:00
|
|
|
init_entry,
|
2022-06-27 21:03:25 +00:00
|
|
|
remove_entities,
|
2021-12-31 22:20:23 +00:00
|
|
|
time_changed,
|
|
|
|
)
|
2021-12-26 06:12:57 +00:00
|
|
|
|
|
|
|
|
2021-12-27 11:39:24 +00:00
|
|
|
def validate_default_camera_entity(
|
2021-12-26 06:12:57 +00:00
|
|
|
hass: HomeAssistant,
|
2021-12-29 04:36:18 +00:00
|
|
|
camera_obj: ProtectCamera,
|
2021-12-26 06:12:57 +00:00
|
|
|
channel_id: int,
|
2021-12-27 11:39:24 +00:00
|
|
|
) -> str:
|
2021-12-26 06:12:57 +00:00
|
|
|
"""Validate a camera entity."""
|
|
|
|
|
2021-12-29 04:36:18 +00:00
|
|
|
channel = camera_obj.channels[channel_id]
|
2021-12-26 06:12:57 +00:00
|
|
|
|
2021-12-29 04:36:18 +00:00
|
|
|
entity_name = f"{camera_obj.name} {channel.name}"
|
2022-06-19 14:22:33 +00:00
|
|
|
unique_id = f"{camera_obj.mac}_{channel.id}"
|
2021-12-26 06:12:57 +00:00
|
|
|
entity_id = f"camera.{entity_name.replace(' ', '_').lower()}"
|
|
|
|
|
|
|
|
entity_registry = er.async_get(hass)
|
|
|
|
entity = entity_registry.async_get(entity_id)
|
|
|
|
assert entity
|
2021-12-27 11:39:24 +00:00
|
|
|
assert entity.disabled is False
|
|
|
|
assert entity.unique_id == unique_id
|
|
|
|
|
|
|
|
return entity_id
|
|
|
|
|
|
|
|
|
|
|
|
def validate_rtsps_camera_entity(
|
|
|
|
hass: HomeAssistant,
|
2021-12-29 04:36:18 +00:00
|
|
|
camera_obj: ProtectCamera,
|
2021-12-27 11:39:24 +00:00
|
|
|
channel_id: int,
|
|
|
|
) -> str:
|
|
|
|
"""Validate a disabled RTSPS camera entity."""
|
|
|
|
|
2021-12-29 04:36:18 +00:00
|
|
|
channel = camera_obj.channels[channel_id]
|
2021-12-27 11:39:24 +00:00
|
|
|
|
2021-12-29 04:36:18 +00:00
|
|
|
entity_name = f"{camera_obj.name} {channel.name}"
|
2022-06-19 14:22:33 +00:00
|
|
|
unique_id = f"{camera_obj.mac}_{channel.id}"
|
2021-12-27 11:39:24 +00:00
|
|
|
entity_id = f"camera.{entity_name.replace(' ', '_').lower()}"
|
|
|
|
|
|
|
|
entity_registry = er.async_get(hass)
|
|
|
|
entity = entity_registry.async_get(entity_id)
|
|
|
|
assert entity
|
|
|
|
assert entity.disabled is True
|
|
|
|
assert entity.unique_id == unique_id
|
|
|
|
|
|
|
|
return entity_id
|
|
|
|
|
|
|
|
|
|
|
|
def validate_rtsp_camera_entity(
|
|
|
|
hass: HomeAssistant,
|
2021-12-29 04:36:18 +00:00
|
|
|
camera_obj: ProtectCamera,
|
2021-12-27 11:39:24 +00:00
|
|
|
channel_id: int,
|
|
|
|
) -> str:
|
|
|
|
"""Validate a disabled RTSP camera entity."""
|
|
|
|
|
2021-12-29 04:36:18 +00:00
|
|
|
channel = camera_obj.channels[channel_id]
|
2021-12-27 11:39:24 +00:00
|
|
|
|
2021-12-29 04:36:18 +00:00
|
|
|
entity_name = f"{camera_obj.name} {channel.name} Insecure"
|
2022-06-19 14:22:33 +00:00
|
|
|
unique_id = f"{camera_obj.mac}_{channel.id}_insecure"
|
2021-12-27 11:39:24 +00:00
|
|
|
entity_id = f"camera.{entity_name.replace(' ', '_').lower()}"
|
|
|
|
|
|
|
|
entity_registry = er.async_get(hass)
|
|
|
|
entity = entity_registry.async_get(entity_id)
|
|
|
|
assert entity
|
|
|
|
assert entity.disabled is True
|
2021-12-26 06:12:57 +00:00
|
|
|
assert entity.unique_id == unique_id
|
|
|
|
|
2021-12-27 11:39:24 +00:00
|
|
|
return entity_id
|
|
|
|
|
2021-12-26 06:12:57 +00:00
|
|
|
|
2021-12-27 11:39:24 +00:00
|
|
|
def validate_common_camera_state(
|
|
|
|
hass: HomeAssistant,
|
|
|
|
channel: CameraChannel,
|
|
|
|
entity_id: str,
|
|
|
|
features: int = SUPPORT_STREAM,
|
|
|
|
):
|
|
|
|
"""Validate state that is common to all camera entity, regradless of type."""
|
2021-12-26 06:12:57 +00:00
|
|
|
entity_state = hass.states.get(entity_id)
|
|
|
|
assert entity_state
|
|
|
|
assert entity_state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
2021-12-27 11:39:24 +00:00
|
|
|
assert entity_state.attributes[ATTR_SUPPORTED_FEATURES] == features
|
2021-12-26 06:12:57 +00:00
|
|
|
assert entity_state.attributes[ATTR_WIDTH] == channel.width
|
|
|
|
assert entity_state.attributes[ATTR_HEIGHT] == channel.height
|
|
|
|
assert entity_state.attributes[ATTR_FPS] == channel.fps
|
|
|
|
assert entity_state.attributes[ATTR_BITRATE] == channel.bitrate
|
|
|
|
assert entity_state.attributes[ATTR_CHANNEL_ID] == channel.id
|
|
|
|
|
|
|
|
|
2021-12-27 11:39:24 +00:00
|
|
|
async def validate_rtsps_camera_state(
|
|
|
|
hass: HomeAssistant,
|
2021-12-29 04:36:18 +00:00
|
|
|
camera_obj: ProtectCamera,
|
2021-12-27 11:39:24 +00:00
|
|
|
channel_id: int,
|
|
|
|
entity_id: str,
|
|
|
|
features: int = SUPPORT_STREAM,
|
|
|
|
):
|
|
|
|
"""Validate a camera's state."""
|
2021-12-29 04:36:18 +00:00
|
|
|
channel = camera_obj.channels[channel_id]
|
2021-12-27 11:39:24 +00:00
|
|
|
|
|
|
|
assert await async_get_stream_source(hass, entity_id) == channel.rtsps_url
|
|
|
|
validate_common_camera_state(hass, channel, entity_id, features)
|
|
|
|
|
|
|
|
|
|
|
|
async def validate_rtsp_camera_state(
|
|
|
|
hass: HomeAssistant,
|
2021-12-29 04:36:18 +00:00
|
|
|
camera_obj: ProtectCamera,
|
2021-12-27 11:39:24 +00:00
|
|
|
channel_id: int,
|
|
|
|
entity_id: str,
|
|
|
|
features: int = SUPPORT_STREAM,
|
|
|
|
):
|
|
|
|
"""Validate a camera's state."""
|
2021-12-29 04:36:18 +00:00
|
|
|
channel = camera_obj.channels[channel_id]
|
2021-12-27 11:39:24 +00:00
|
|
|
|
|
|
|
assert await async_get_stream_source(hass, entity_id) == channel.rtsp_url
|
|
|
|
validate_common_camera_state(hass, channel, entity_id, features)
|
|
|
|
|
|
|
|
|
|
|
|
async def validate_no_stream_camera_state(
|
|
|
|
hass: HomeAssistant,
|
2021-12-29 04:36:18 +00:00
|
|
|
camera_obj: ProtectCamera,
|
2021-12-27 11:39:24 +00:00
|
|
|
channel_id: int,
|
|
|
|
entity_id: str,
|
|
|
|
features: int = SUPPORT_STREAM,
|
|
|
|
):
|
|
|
|
"""Validate a camera's state."""
|
2021-12-29 04:36:18 +00:00
|
|
|
channel = camera_obj.channels[channel_id]
|
2021-12-27 11:39:24 +00:00
|
|
|
|
|
|
|
assert await async_get_stream_source(hass, entity_id) is None
|
|
|
|
validate_common_camera_state(hass, channel, entity_id, features)
|
|
|
|
|
|
|
|
|
2021-12-26 06:12:57 +00:00
|
|
|
async def test_basic_setup(
|
2022-06-25 15:15:38 +00:00
|
|
|
hass: HomeAssistant,
|
|
|
|
ufp: MockUFPFixture,
|
|
|
|
camera_all: ProtectCamera,
|
|
|
|
doorbell: ProtectCamera,
|
2021-12-26 06:12:57 +00:00
|
|
|
):
|
|
|
|
"""Test working setup of unifiprotect entry."""
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
camera_high_only = camera_all.copy()
|
|
|
|
camera_high_only.channels = [c.copy() for c in camera_all.channels]
|
2021-12-26 06:12:57 +00:00
|
|
|
camera_high_only.name = "Test Camera 1"
|
|
|
|
camera_high_only.channels[0].is_rtsp_enabled = True
|
|
|
|
camera_high_only.channels[1].is_rtsp_enabled = False
|
|
|
|
camera_high_only.channels[2].is_rtsp_enabled = False
|
2022-06-25 15:15:38 +00:00
|
|
|
|
|
|
|
camera_medium_only = camera_all.copy()
|
|
|
|
camera_medium_only.channels = [c.copy() for c in camera_all.channels]
|
2021-12-27 11:39:24 +00:00
|
|
|
camera_medium_only.name = "Test Camera 2"
|
|
|
|
camera_medium_only.channels[0].is_rtsp_enabled = False
|
|
|
|
camera_medium_only.channels[1].is_rtsp_enabled = True
|
|
|
|
camera_medium_only.channels[2].is_rtsp_enabled = False
|
2022-06-25 15:15:38 +00:00
|
|
|
|
|
|
|
camera_all.name = "Test Camera 3"
|
|
|
|
|
|
|
|
camera_no_channels = camera_all.copy()
|
|
|
|
camera_no_channels.channels = [c.copy() for c in camera_all.channels]
|
2021-12-27 11:39:24 +00:00
|
|
|
camera_no_channels.name = "Test Camera 4"
|
2021-12-26 06:12:57 +00:00
|
|
|
camera_no_channels.channels[0].is_rtsp_enabled = False
|
|
|
|
camera_no_channels.channels[1].is_rtsp_enabled = False
|
|
|
|
camera_no_channels.channels[2].is_rtsp_enabled = False
|
2022-06-25 15:15:38 +00:00
|
|
|
|
|
|
|
doorbell.name = "Test Camera 5"
|
|
|
|
|
|
|
|
devices = [
|
|
|
|
camera_high_only,
|
|
|
|
camera_medium_only,
|
|
|
|
camera_all,
|
|
|
|
camera_no_channels,
|
|
|
|
doorbell,
|
|
|
|
]
|
|
|
|
await init_entry(hass, ufp, devices)
|
2021-12-26 06:12:57 +00:00
|
|
|
|
2022-01-12 21:27:41 +00:00
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 14, 6)
|
2021-12-27 11:39:24 +00:00
|
|
|
|
|
|
|
# test camera 1
|
|
|
|
entity_id = validate_default_camera_entity(hass, camera_high_only, 0)
|
|
|
|
await validate_rtsps_camera_state(hass, camera_high_only, 0, entity_id)
|
|
|
|
|
|
|
|
entity_id = validate_rtsp_camera_entity(hass, camera_high_only, 0)
|
2022-06-25 15:15:38 +00:00
|
|
|
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
2021-12-27 11:39:24 +00:00
|
|
|
await validate_rtsp_camera_state(hass, camera_high_only, 0, entity_id)
|
|
|
|
|
|
|
|
# test camera 2
|
|
|
|
entity_id = validate_default_camera_entity(hass, camera_medium_only, 1)
|
|
|
|
await validate_rtsps_camera_state(hass, camera_medium_only, 1, entity_id)
|
|
|
|
|
|
|
|
entity_id = validate_rtsp_camera_entity(hass, camera_medium_only, 1)
|
2022-06-25 15:15:38 +00:00
|
|
|
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
2021-12-27 11:39:24 +00:00
|
|
|
await validate_rtsp_camera_state(hass, camera_medium_only, 1, entity_id)
|
|
|
|
|
|
|
|
# test camera 3
|
2022-06-25 15:15:38 +00:00
|
|
|
entity_id = validate_default_camera_entity(hass, camera_all, 0)
|
|
|
|
await validate_rtsps_camera_state(hass, camera_all, 0, entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
entity_id = validate_rtsp_camera_entity(hass, camera_all, 0)
|
|
|
|
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
|
|
|
await validate_rtsp_camera_state(hass, camera_all, 0, entity_id)
|
2021-12-27 11:39:24 +00:00
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
entity_id = validate_rtsps_camera_entity(hass, camera_all, 1)
|
|
|
|
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
|
|
|
await validate_rtsps_camera_state(hass, camera_all, 1, entity_id)
|
2021-12-27 11:39:24 +00:00
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
entity_id = validate_rtsp_camera_entity(hass, camera_all, 1)
|
|
|
|
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
|
|
|
await validate_rtsp_camera_state(hass, camera_all, 1, entity_id)
|
2021-12-27 11:39:24 +00:00
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
entity_id = validate_rtsps_camera_entity(hass, camera_all, 2)
|
|
|
|
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
|
|
|
await validate_rtsps_camera_state(hass, camera_all, 2, entity_id)
|
2021-12-27 11:39:24 +00:00
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
entity_id = validate_rtsp_camera_entity(hass, camera_all, 2)
|
|
|
|
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
|
|
|
await validate_rtsp_camera_state(hass, camera_all, 2, entity_id)
|
2021-12-27 11:39:24 +00:00
|
|
|
|
|
|
|
# test camera 4
|
|
|
|
entity_id = validate_default_camera_entity(hass, camera_no_channels, 0)
|
|
|
|
await validate_no_stream_camera_state(
|
|
|
|
hass, camera_no_channels, 0, entity_id, features=0
|
2021-12-26 06:12:57 +00:00
|
|
|
)
|
|
|
|
|
2022-01-12 21:27:41 +00:00
|
|
|
# test camera 5
|
2022-06-25 15:15:38 +00:00
|
|
|
entity_id = validate_default_camera_entity(hass, doorbell, 0)
|
|
|
|
await validate_rtsps_camera_state(hass, doorbell, 0, entity_id)
|
2022-01-12 21:27:41 +00:00
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
entity_id = validate_rtsp_camera_entity(hass, doorbell, 0)
|
|
|
|
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
|
|
|
await validate_rtsp_camera_state(hass, doorbell, 0, entity_id)
|
2022-01-12 21:27:41 +00:00
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
entity_id = validate_default_camera_entity(hass, doorbell, 3)
|
|
|
|
await validate_no_stream_camera_state(hass, doorbell, 3, entity_id, features=0)
|
2022-01-12 21:27:41 +00:00
|
|
|
|
2021-12-26 06:12:57 +00:00
|
|
|
|
2022-06-27 21:03:25 +00:00
|
|
|
async def test_adopt(hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera):
|
2021-12-26 06:12:57 +00:00
|
|
|
"""Test setting up camera with no camera channels."""
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
camera1 = camera.copy()
|
|
|
|
camera1.channels = []
|
2021-12-26 06:12:57 +00:00
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
await init_entry(hass, ufp, [camera1])
|
2022-06-27 21:03:25 +00:00
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 0, 0)
|
2021-12-26 06:12:57 +00:00
|
|
|
|
2022-06-27 21:03:25 +00:00
|
|
|
await remove_entities(hass, [camera1])
|
2022-06-25 15:15:38 +00:00
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 0, 0)
|
2022-06-27 21:03:25 +00:00
|
|
|
camera1.channels = []
|
|
|
|
await adopt_devices(hass, ufp, [camera1])
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 0, 0)
|
|
|
|
|
|
|
|
camera1.channels = camera.channels
|
|
|
|
for channel in camera1.channels:
|
|
|
|
channel._api = ufp.api
|
|
|
|
|
|
|
|
mock_msg = Mock()
|
|
|
|
mock_msg.changed_data = {"channels": camera.channels}
|
|
|
|
mock_msg.new_obj = camera1
|
|
|
|
ufp.ws_msg(mock_msg)
|
|
|
|
await hass.async_block_till_done()
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
|
|
|
|
|
|
|
await remove_entities(hass, [camera1])
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 0, 0)
|
|
|
|
await adopt_devices(hass, ufp, [camera1])
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
2021-12-26 06:12:57 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_camera_image(
|
2022-06-25 15:15:38 +00:00
|
|
|
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
2021-12-26 06:12:57 +00:00
|
|
|
):
|
|
|
|
"""Test retrieving camera image."""
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
await init_entry(hass, ufp, [camera])
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
|
|
|
|
|
|
|
ufp.api.get_camera_snapshot = AsyncMock()
|
2021-12-26 06:12:57 +00:00
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
await async_get_image(hass, "camera.test_camera_high")
|
|
|
|
ufp.api.get_camera_snapshot.assert_called_once()
|
2021-12-26 06:12:57 +00:00
|
|
|
|
|
|
|
|
2022-01-14 23:38:01 +00:00
|
|
|
async def test_package_camera_image(
|
2022-06-25 15:15:38 +00:00
|
|
|
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: ProtectCamera
|
2022-01-14 23:38:01 +00:00
|
|
|
):
|
|
|
|
"""Test retrieving package camera image."""
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
await init_entry(hass, ufp, [doorbell])
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 3, 2)
|
|
|
|
|
|
|
|
ufp.api.get_package_camera_snapshot = AsyncMock()
|
2022-01-14 23:38:01 +00:00
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
await async_get_image(hass, "camera.test_camera_package_camera")
|
|
|
|
ufp.api.get_package_camera_snapshot.assert_called_once()
|
2022-01-14 23:38:01 +00:00
|
|
|
|
|
|
|
|
2021-12-26 06:12:57 +00:00
|
|
|
async def test_camera_generic_update(
|
2022-06-25 15:15:38 +00:00
|
|
|
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
2021-12-26 06:12:57 +00:00
|
|
|
):
|
|
|
|
"""Tests generic entity update service."""
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
await init_entry(hass, ufp, [camera])
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
|
|
|
entity_id = "camera.test_camera_high"
|
|
|
|
|
2021-12-26 06:12:57 +00:00
|
|
|
assert await async_setup_component(hass, "homeassistant", {})
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
state = hass.states.get(entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
assert state and state.state == "idle"
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
ufp.api.update = AsyncMock(return_value=None)
|
2021-12-26 06:12:57 +00:00
|
|
|
await hass.services.async_call(
|
|
|
|
"homeassistant",
|
|
|
|
"update_entity",
|
2022-06-25 15:15:38 +00:00
|
|
|
{ATTR_ENTITY_ID: entity_id},
|
2021-12-26 06:12:57 +00:00
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
state = hass.states.get(entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
assert state and state.state == "idle"
|
|
|
|
|
|
|
|
|
|
|
|
async def test_camera_interval_update(
|
2022-06-25 15:15:38 +00:00
|
|
|
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
2021-12-26 06:12:57 +00:00
|
|
|
):
|
|
|
|
"""Interval updates updates camera entity."""
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
await init_entry(hass, ufp, [camera])
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
|
|
|
entity_id = "camera.test_camera_high"
|
|
|
|
|
|
|
|
state = hass.states.get(entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
assert state and state.state == "idle"
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
new_camera = camera.copy()
|
2021-12-26 06:12:57 +00:00
|
|
|
new_camera.is_recording = True
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
|
|
|
ufp.api.update = AsyncMock(return_value=ufp.api.bootstrap)
|
2021-12-26 06:12:57 +00:00
|
|
|
await time_changed(hass, DEFAULT_SCAN_INTERVAL)
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
state = hass.states.get(entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
assert state and state.state == "recording"
|
|
|
|
|
|
|
|
|
|
|
|
async def test_camera_bad_interval_update(
|
2022-06-25 15:15:38 +00:00
|
|
|
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
2021-12-26 06:12:57 +00:00
|
|
|
):
|
|
|
|
"""Interval updates marks camera unavailable."""
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
await init_entry(hass, ufp, [camera])
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
|
|
|
entity_id = "camera.test_camera_high"
|
|
|
|
|
|
|
|
state = hass.states.get(entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
assert state and state.state == "idle"
|
|
|
|
|
|
|
|
# update fails
|
2022-06-25 15:15:38 +00:00
|
|
|
ufp.api.update = AsyncMock(side_effect=NvrError)
|
2021-12-26 06:12:57 +00:00
|
|
|
await time_changed(hass, DEFAULT_SCAN_INTERVAL)
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
state = hass.states.get(entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
assert state and state.state == "unavailable"
|
|
|
|
|
|
|
|
# next update succeeds
|
2022-06-25 15:15:38 +00:00
|
|
|
ufp.api.update = AsyncMock(return_value=ufp.api.bootstrap)
|
2021-12-26 06:12:57 +00:00
|
|
|
await time_changed(hass, DEFAULT_SCAN_INTERVAL)
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
state = hass.states.get(entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
assert state and state.state == "idle"
|
|
|
|
|
|
|
|
|
|
|
|
async def test_camera_ws_update(
|
2022-06-25 15:15:38 +00:00
|
|
|
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
2021-12-26 06:12:57 +00:00
|
|
|
):
|
|
|
|
"""WS update updates camera entity."""
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
await init_entry(hass, ufp, [camera])
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
|
|
|
entity_id = "camera.test_camera_high"
|
|
|
|
|
|
|
|
state = hass.states.get(entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
assert state and state.state == "idle"
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
new_camera = camera.copy()
|
2021-12-26 06:12:57 +00:00
|
|
|
new_camera.is_recording = True
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
no_camera = camera.copy()
|
2022-06-22 20:57:21 +00:00
|
|
|
no_camera.is_adopted = False
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
2021-12-26 06:12:57 +00:00
|
|
|
mock_msg = Mock()
|
2022-01-03 23:42:10 +00:00
|
|
|
mock_msg.changed_data = {}
|
2021-12-26 06:12:57 +00:00
|
|
|
mock_msg.new_obj = new_camera
|
2022-06-25 15:15:38 +00:00
|
|
|
ufp.ws_msg(mock_msg)
|
2021-12-26 06:12:57 +00:00
|
|
|
|
2022-06-22 20:57:21 +00:00
|
|
|
mock_msg = Mock()
|
|
|
|
mock_msg.changed_data = {}
|
|
|
|
mock_msg.new_obj = no_camera
|
2022-06-25 15:15:38 +00:00
|
|
|
ufp.ws_msg(mock_msg)
|
2022-06-22 20:57:21 +00:00
|
|
|
|
2021-12-26 06:12:57 +00:00
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
state = hass.states.get(entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
assert state and state.state == "recording"
|
|
|
|
|
|
|
|
|
|
|
|
async def test_camera_ws_update_offline(
|
2022-06-25 15:15:38 +00:00
|
|
|
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
2021-12-26 06:12:57 +00:00
|
|
|
):
|
|
|
|
"""WS updates marks camera unavailable."""
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
await init_entry(hass, ufp, [camera])
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
|
|
|
entity_id = "camera.test_camera_high"
|
|
|
|
|
|
|
|
state = hass.states.get(entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
assert state and state.state == "idle"
|
|
|
|
|
|
|
|
# camera goes offline
|
2022-06-25 15:15:38 +00:00
|
|
|
new_camera = camera.copy()
|
2021-12-29 22:39:55 +00:00
|
|
|
new_camera.state = StateType.DISCONNECTED
|
2021-12-26 06:12:57 +00:00
|
|
|
|
|
|
|
mock_msg = Mock()
|
2022-01-03 23:42:10 +00:00
|
|
|
mock_msg.changed_data = {}
|
2021-12-26 06:12:57 +00:00
|
|
|
mock_msg.new_obj = new_camera
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
|
|
|
ufp.ws_msg(mock_msg)
|
2021-12-26 06:12:57 +00:00
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
state = hass.states.get(entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
assert state and state.state == "unavailable"
|
|
|
|
|
|
|
|
# camera comes back online
|
2021-12-29 22:39:55 +00:00
|
|
|
new_camera.state = StateType.CONNECTED
|
2021-12-26 06:12:57 +00:00
|
|
|
|
|
|
|
mock_msg = Mock()
|
2022-01-03 23:42:10 +00:00
|
|
|
mock_msg.changed_data = {}
|
2021-12-26 06:12:57 +00:00
|
|
|
mock_msg.new_obj = new_camera
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
|
|
|
ufp.ws_msg(mock_msg)
|
2021-12-26 06:12:57 +00:00
|
|
|
await hass.async_block_till_done()
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
state = hass.states.get(entity_id)
|
2021-12-26 06:12:57 +00:00
|
|
|
assert state and state.state == "idle"
|
2022-06-08 22:29:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_camera_enable_motion(
|
2022-06-25 15:15:38 +00:00
|
|
|
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
2022-06-08 22:29:46 +00:00
|
|
|
):
|
|
|
|
"""Tests generic entity update service."""
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
await init_entry(hass, ufp, [camera])
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
|
|
|
entity_id = "camera.test_camera_high"
|
|
|
|
|
|
|
|
camera.__fields__["set_motion_detection"] = Mock()
|
|
|
|
camera.set_motion_detection = AsyncMock()
|
2022-06-08 22:29:46 +00:00
|
|
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
"camera",
|
|
|
|
"enable_motion_detection",
|
2022-06-25 15:15:38 +00:00
|
|
|
{ATTR_ENTITY_ID: entity_id},
|
2022-06-08 22:29:46 +00:00
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
camera.set_motion_detection.assert_called_once_with(True)
|
2022-06-08 22:29:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_camera_disable_motion(
|
2022-06-25 15:15:38 +00:00
|
|
|
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
2022-06-08 22:29:46 +00:00
|
|
|
):
|
|
|
|
"""Tests generic entity update service."""
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
await init_entry(hass, ufp, [camera])
|
|
|
|
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
|
|
|
entity_id = "camera.test_camera_high"
|
|
|
|
|
|
|
|
camera.__fields__["set_motion_detection"] = Mock()
|
|
|
|
camera.set_motion_detection = AsyncMock()
|
2022-06-08 22:29:46 +00:00
|
|
|
|
|
|
|
await hass.services.async_call(
|
|
|
|
"camera",
|
|
|
|
"disable_motion_detection",
|
2022-06-25 15:15:38 +00:00
|
|
|
{ATTR_ENTITY_ID: entity_id},
|
2022-06-08 22:29:46 +00:00
|
|
|
blocking=True,
|
|
|
|
)
|
|
|
|
|
2022-06-25 15:15:38 +00:00
|
|
|
camera.set_motion_detection.assert_called_once_with(False)
|