mirror of https://github.com/home-assistant/core
379 lines
11 KiB
Python
379 lines
11 KiB
Python
"""Fixtures and test data for UniFi Protect methods."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections.abc import Callable
|
|
from datetime import datetime, timedelta
|
|
from functools import partial
|
|
from ipaddress import IPv4Address
|
|
import json
|
|
from pathlib import Path
|
|
from tempfile import gettempdir
|
|
from typing import Any
|
|
from unittest.mock import AsyncMock, Mock, patch
|
|
|
|
import pytest
|
|
from uiprotect import ProtectApiClient
|
|
from uiprotect.data import (
|
|
NVR,
|
|
Bootstrap,
|
|
Camera,
|
|
Chime,
|
|
CloudAccount,
|
|
Doorlock,
|
|
Light,
|
|
Liveview,
|
|
Sensor,
|
|
SmartDetectObjectType,
|
|
VideoMode,
|
|
Viewer,
|
|
WSSubscriptionMessage,
|
|
)
|
|
from uiprotect.websocket import WebsocketState
|
|
|
|
from homeassistant.components.unifiprotect.const import DOMAIN
|
|
from homeassistant.core import HomeAssistant
|
|
import homeassistant.util.dt as dt_util
|
|
|
|
from . import _patch_discovery
|
|
from .utils import MockUFPFixture
|
|
|
|
from tests.common import MockConfigEntry, load_fixture
|
|
|
|
MAC_ADDR = "aa:bb:cc:dd:ee:ff"
|
|
|
|
|
|
@pytest.fixture(name="nvr")
|
|
def mock_nvr():
|
|
"""Mock UniFi Protect Camera device."""
|
|
|
|
data = json.loads(load_fixture("sample_nvr.json", integration=DOMAIN))
|
|
nvr = NVR.from_unifi_dict(**data)
|
|
|
|
# disable pydantic validation so mocking can happen
|
|
NVR.__config__.validate_assignment = False
|
|
|
|
yield nvr
|
|
|
|
NVR.__config__.validate_assignment = True
|
|
|
|
|
|
@pytest.fixture(name="ufp_config_entry")
|
|
def mock_ufp_config_entry():
|
|
"""Mock the unifiprotect config entry."""
|
|
|
|
return MockConfigEntry(
|
|
domain=DOMAIN,
|
|
data={
|
|
"host": "1.1.1.1",
|
|
"username": "test-username",
|
|
"password": "test-password",
|
|
"id": "UnifiProtect",
|
|
"port": 443,
|
|
"verify_ssl": False,
|
|
},
|
|
version=2,
|
|
)
|
|
|
|
|
|
@pytest.fixture(name="old_nvr")
|
|
def old_nvr():
|
|
"""Mock UniFi Protect Camera device."""
|
|
|
|
data = json.loads(load_fixture("sample_nvr.json", integration=DOMAIN))
|
|
data["version"] = "1.19.0"
|
|
return NVR.from_unifi_dict(**data)
|
|
|
|
|
|
@pytest.fixture(name="bootstrap")
|
|
def bootstrap_fixture(nvr: NVR):
|
|
"""Mock Bootstrap fixture."""
|
|
data = json.loads(load_fixture("sample_bootstrap.json", integration=DOMAIN))
|
|
data["nvr"] = nvr
|
|
data["cameras"] = []
|
|
data["lights"] = []
|
|
data["sensors"] = []
|
|
data["viewers"] = []
|
|
data["liveviews"] = []
|
|
data["events"] = []
|
|
data["doorlocks"] = []
|
|
data["chimes"] = []
|
|
|
|
return Bootstrap.from_unifi_dict(**data)
|
|
|
|
|
|
@pytest.fixture(name="ufp_client")
|
|
def mock_ufp_client(bootstrap: Bootstrap):
|
|
"""Mock ProtectApiClient for testing."""
|
|
client = Mock()
|
|
client.bootstrap = bootstrap
|
|
client._bootstrap = bootstrap
|
|
client.api_path = "/api"
|
|
client.cache_dir = Path(gettempdir()) / "ufp_cache"
|
|
# functionality from API client tests actually need
|
|
client._stream_response = partial(ProtectApiClient._stream_response, client)
|
|
client.get_camera_video = partial(ProtectApiClient.get_camera_video, client)
|
|
|
|
nvr = client.bootstrap.nvr
|
|
nvr._api = client
|
|
client.bootstrap._api = client
|
|
|
|
client.base_url = "https://127.0.0.1"
|
|
client.connection_host = IPv4Address("127.0.0.1")
|
|
client.get_nvr = AsyncMock(return_value=nvr)
|
|
client.get_bootstrap = AsyncMock(return_value=bootstrap)
|
|
client.update = AsyncMock(return_value=bootstrap)
|
|
client.async_disconnect_ws = AsyncMock()
|
|
return client
|
|
|
|
|
|
@pytest.fixture(name="ufp")
|
|
def mock_entry(
|
|
hass: HomeAssistant, ufp_config_entry: MockConfigEntry, ufp_client: ProtectApiClient
|
|
):
|
|
"""Mock ProtectApiClient for testing."""
|
|
|
|
with (
|
|
_patch_discovery(no_device=True),
|
|
patch(
|
|
"homeassistant.components.unifiprotect.utils.ProtectApiClient"
|
|
) as mock_api,
|
|
):
|
|
ufp_config_entry.add_to_hass(hass)
|
|
|
|
mock_api.return_value = ufp_client
|
|
|
|
ufp = MockUFPFixture(ufp_config_entry, ufp_client)
|
|
|
|
def subscribe(ws_callback: Callable[[WSSubscriptionMessage], None]) -> Any:
|
|
ufp.ws_subscription = ws_callback
|
|
return Mock()
|
|
|
|
def subscribe_websocket_state(
|
|
ws_state_subscription: Callable[[WebsocketState], None],
|
|
) -> Any:
|
|
ufp.ws_state_subscription = ws_state_subscription
|
|
return Mock()
|
|
|
|
ufp_client.subscribe_websocket = subscribe
|
|
ufp_client.subscribe_websocket_state = subscribe_websocket_state
|
|
yield ufp
|
|
|
|
|
|
@pytest.fixture
|
|
def liveview():
|
|
"""Mock UniFi Protect Liveview."""
|
|
|
|
data = json.loads(load_fixture("sample_liveview.json", integration=DOMAIN))
|
|
return Liveview.from_unifi_dict(**data)
|
|
|
|
|
|
@pytest.fixture(name="camera")
|
|
def camera_fixture(fixed_now: datetime):
|
|
"""Mock UniFi Protect Camera device."""
|
|
|
|
# disable pydantic validation so mocking can happen
|
|
Camera.__config__.validate_assignment = False
|
|
|
|
data = json.loads(load_fixture("sample_camera.json", integration=DOMAIN))
|
|
camera = Camera.from_unifi_dict(**data)
|
|
camera.last_motion = fixed_now - timedelta(hours=1)
|
|
|
|
yield camera
|
|
|
|
Camera.__config__.validate_assignment = True
|
|
|
|
|
|
@pytest.fixture(name="camera_all")
|
|
def camera_all_fixture(camera: Camera):
|
|
"""Mock UniFi Protect Camera device."""
|
|
|
|
all_camera = camera.copy()
|
|
all_camera.channels = [all_camera.channels[0].copy()]
|
|
|
|
medium_channel = all_camera.channels[0].copy()
|
|
medium_channel.name = "Medium"
|
|
medium_channel.id = 1
|
|
medium_channel.rtsp_alias = "test_medium_alias"
|
|
all_camera.channels.append(medium_channel)
|
|
|
|
low_channel = all_camera.channels[0].copy()
|
|
low_channel.name = "Low"
|
|
low_channel.id = 2
|
|
low_channel.rtsp_alias = "test_medium_alias"
|
|
all_camera.channels.append(low_channel)
|
|
|
|
return all_camera
|
|
|
|
|
|
@pytest.fixture(name="doorbell")
|
|
def doorbell_fixture(camera: Camera, fixed_now: datetime):
|
|
"""Mock UniFi Protect Camera device (with chime)."""
|
|
|
|
doorbell = camera.copy()
|
|
doorbell.channels = [c.copy() for c in doorbell.channels]
|
|
|
|
package_channel = doorbell.channels[0].copy()
|
|
package_channel.name = "Package Camera"
|
|
package_channel.id = 3
|
|
package_channel.fps = 2
|
|
package_channel.rtsp_alias = "test_package_alias"
|
|
|
|
doorbell.channels.append(package_channel)
|
|
doorbell.feature_flags.video_modes = [VideoMode.DEFAULT, VideoMode.HIGH_FPS]
|
|
doorbell.feature_flags.smart_detect_types = [
|
|
SmartDetectObjectType.PERSON,
|
|
SmartDetectObjectType.VEHICLE,
|
|
SmartDetectObjectType.ANIMAL,
|
|
SmartDetectObjectType.PACKAGE,
|
|
]
|
|
doorbell.has_speaker = True
|
|
doorbell.feature_flags.has_hdr = True
|
|
doorbell.feature_flags.has_lcd_screen = True
|
|
doorbell.feature_flags.has_speaker = True
|
|
doorbell.feature_flags.has_privacy_mask = True
|
|
doorbell.feature_flags.is_doorbell = True
|
|
doorbell.feature_flags.has_chime = True
|
|
doorbell.feature_flags.has_smart_detect = True
|
|
doorbell.feature_flags.has_package_camera = True
|
|
doorbell.feature_flags.has_led_status = True
|
|
doorbell.last_ring = fixed_now - timedelta(hours=1)
|
|
return doorbell
|
|
|
|
|
|
@pytest.fixture
|
|
def unadopted_camera(camera: Camera):
|
|
"""Mock UniFi Protect Camera device (unadopted)."""
|
|
|
|
no_camera = camera.copy()
|
|
no_camera.channels = [c.copy() for c in no_camera.channels]
|
|
no_camera.name = "Unadopted Camera"
|
|
no_camera.is_adopted = False
|
|
return no_camera
|
|
|
|
|
|
@pytest.fixture(name="light")
|
|
def light_fixture():
|
|
"""Mock UniFi Protect Light device."""
|
|
|
|
# disable pydantic validation so mocking can happen
|
|
Light.__config__.validate_assignment = False
|
|
|
|
data = json.loads(load_fixture("sample_light.json", integration=DOMAIN))
|
|
yield Light.from_unifi_dict(**data)
|
|
|
|
Light.__config__.validate_assignment = True
|
|
|
|
|
|
@pytest.fixture
|
|
def unadopted_light(light: Light):
|
|
"""Mock UniFi Protect Light device (unadopted)."""
|
|
|
|
no_light = light.copy()
|
|
no_light.name = "Unadopted Light"
|
|
no_light.is_adopted = False
|
|
return no_light
|
|
|
|
|
|
@pytest.fixture
|
|
def viewer():
|
|
"""Mock UniFi Protect Viewport device."""
|
|
|
|
# disable pydantic validation so mocking can happen
|
|
Viewer.__config__.validate_assignment = False
|
|
|
|
data = json.loads(load_fixture("sample_viewport.json", integration=DOMAIN))
|
|
yield Viewer.from_unifi_dict(**data)
|
|
|
|
Viewer.__config__.validate_assignment = True
|
|
|
|
|
|
@pytest.fixture(name="sensor")
|
|
def sensor_fixture(fixed_now: datetime):
|
|
"""Mock UniFi Protect Sensor device."""
|
|
|
|
# disable pydantic validation so mocking can happen
|
|
Sensor.__config__.validate_assignment = False
|
|
|
|
data = json.loads(load_fixture("sample_sensor.json", integration=DOMAIN))
|
|
sensor: Sensor = Sensor.from_unifi_dict(**data)
|
|
sensor.motion_detected_at = fixed_now - timedelta(hours=1)
|
|
sensor.open_status_changed_at = fixed_now - timedelta(hours=1)
|
|
sensor.alarm_triggered_at = fixed_now - timedelta(hours=1)
|
|
yield sensor
|
|
|
|
Sensor.__config__.validate_assignment = True
|
|
|
|
|
|
@pytest.fixture(name="sensor_all")
|
|
def csensor_all_fixture(sensor: Sensor):
|
|
"""Mock UniFi Protect Sensor device."""
|
|
|
|
all_sensor = sensor.copy()
|
|
all_sensor.light_settings.is_enabled = True
|
|
all_sensor.humidity_settings.is_enabled = True
|
|
all_sensor.temperature_settings.is_enabled = True
|
|
all_sensor.alarm_settings.is_enabled = True
|
|
all_sensor.led_settings.is_enabled = True
|
|
all_sensor.motion_settings.is_enabled = True
|
|
|
|
return all_sensor
|
|
|
|
|
|
@pytest.fixture(name="doorlock")
|
|
def doorlock_fixture():
|
|
"""Mock UniFi Protect Doorlock device."""
|
|
|
|
# disable pydantic validation so mocking can happen
|
|
Doorlock.__config__.validate_assignment = False
|
|
|
|
data = json.loads(load_fixture("sample_doorlock.json", integration=DOMAIN))
|
|
yield Doorlock.from_unifi_dict(**data)
|
|
|
|
Doorlock.__config__.validate_assignment = True
|
|
|
|
|
|
@pytest.fixture
|
|
def unadopted_doorlock(doorlock: Doorlock):
|
|
"""Mock UniFi Protect Light device (unadopted)."""
|
|
|
|
no_doorlock = doorlock.copy()
|
|
no_doorlock.name = "Unadopted Lock"
|
|
no_doorlock.is_adopted = False
|
|
return no_doorlock
|
|
|
|
|
|
@pytest.fixture
|
|
def chime():
|
|
"""Mock UniFi Protect Chime device."""
|
|
|
|
# disable pydantic validation so mocking can happen
|
|
Chime.__config__.validate_assignment = False
|
|
|
|
data = json.loads(load_fixture("sample_chime.json", integration=DOMAIN))
|
|
yield Chime.from_unifi_dict(**data)
|
|
|
|
Chime.__config__.validate_assignment = True
|
|
|
|
|
|
@pytest.fixture(name="fixed_now")
|
|
def fixed_now_fixture():
|
|
"""Return datetime object that will be consistent throughout test."""
|
|
return dt_util.utcnow()
|
|
|
|
|
|
@pytest.fixture(name="cloud_account")
|
|
def cloud_account() -> CloudAccount:
|
|
"""Return UI Cloud Account."""
|
|
|
|
return CloudAccount(
|
|
id="42",
|
|
first_name="Test",
|
|
last_name="User",
|
|
email="test@example.com",
|
|
user_id="42",
|
|
name="Test User",
|
|
location=None,
|
|
profile_img=None,
|
|
)
|