Refactor UniFi Protect tests (#73971)
Co-authored-by: J. Nick Koston <nick@koston.org>pull/73984/head
parent
85fdc56240
commit
e67f8720e8
|
@ -3,14 +3,14 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from datetime import datetime, timedelta
|
||||
from ipaddress import IPv4Address
|
||||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
from pyunifiprotect.data import (
|
||||
NVR,
|
||||
Bootstrap,
|
||||
|
@ -19,37 +19,27 @@ from pyunifiprotect.data import (
|
|||
Doorlock,
|
||||
Light,
|
||||
Liveview,
|
||||
ProtectAdoptableDeviceModel,
|
||||
Sensor,
|
||||
SmartDetectObjectType,
|
||||
VideoMode,
|
||||
Viewer,
|
||||
WSSubscriptionMessage,
|
||||
)
|
||||
from pyunifiprotect.test_util.anonymize import random_hex
|
||||
|
||||
from homeassistant.components.unifiprotect.const import DOMAIN
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant, split_entity_id
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity import EntityDescription
|
||||
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, async_fire_time_changed, load_fixture
|
||||
from tests.common import MockConfigEntry, load_fixture
|
||||
|
||||
MAC_ADDR = "aa:bb:cc:dd:ee:ff"
|
||||
|
||||
|
||||
@dataclass
|
||||
class MockEntityFixture:
|
||||
"""Mock for NVR."""
|
||||
|
||||
entry: MockConfigEntry
|
||||
api: Mock
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_nvr")
|
||||
def mock_nvr_fixture():
|
||||
@pytest.fixture(name="nvr")
|
||||
def mock_nvr():
|
||||
"""Mock UniFi Protect Camera device."""
|
||||
|
||||
data = json.loads(load_fixture("sample_nvr.json", integration=DOMAIN))
|
||||
|
@ -63,7 +53,7 @@ def mock_nvr_fixture():
|
|||
NVR.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_ufp_config_entry")
|
||||
@pytest.fixture(name="ufp_config_entry")
|
||||
def mock_ufp_config_entry():
|
||||
"""Mock the unifiprotect config entry."""
|
||||
|
||||
|
@ -81,8 +71,8 @@ def mock_ufp_config_entry():
|
|||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_old_nvr")
|
||||
def mock_old_nvr_fixture():
|
||||
@pytest.fixture(name="old_nvr")
|
||||
def old_nvr():
|
||||
"""Mock UniFi Protect Camera device."""
|
||||
|
||||
data = json.loads(load_fixture("sample_nvr.json", integration=DOMAIN))
|
||||
|
@ -90,11 +80,11 @@ def mock_old_nvr_fixture():
|
|||
return NVR.from_unifi_dict(**data)
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_bootstrap")
|
||||
def mock_bootstrap_fixture(mock_nvr: NVR):
|
||||
@pytest.fixture(name="bootstrap")
|
||||
def bootstrap_fixture(nvr: NVR):
|
||||
"""Mock Bootstrap fixture."""
|
||||
data = json.loads(load_fixture("sample_bootstrap.json", integration=DOMAIN))
|
||||
data["nvr"] = mock_nvr
|
||||
data["nvr"] = nvr
|
||||
data["cameras"] = []
|
||||
data["lights"] = []
|
||||
data["sensors"] = []
|
||||
|
@ -107,24 +97,11 @@ def mock_bootstrap_fixture(mock_nvr: NVR):
|
|||
return Bootstrap.from_unifi_dict(**data)
|
||||
|
||||
|
||||
def reset_objects(bootstrap: Bootstrap):
|
||||
"""Reset bootstrap objects."""
|
||||
|
||||
bootstrap.cameras = {}
|
||||
bootstrap.lights = {}
|
||||
bootstrap.sensors = {}
|
||||
bootstrap.viewers = {}
|
||||
bootstrap.liveviews = {}
|
||||
bootstrap.events = {}
|
||||
bootstrap.doorlocks = {}
|
||||
bootstrap.chimes = {}
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_client(mock_bootstrap: Bootstrap):
|
||||
@pytest.fixture(name="ufp_client")
|
||||
def mock_ufp_client(bootstrap: Bootstrap):
|
||||
"""Mock ProtectApiClient for testing."""
|
||||
client = Mock()
|
||||
client.bootstrap = mock_bootstrap
|
||||
client.bootstrap = bootstrap
|
||||
|
||||
nvr = client.bootstrap.nvr
|
||||
nvr._api = client
|
||||
|
@ -133,161 +110,227 @@ def mock_client(mock_bootstrap: Bootstrap):
|
|||
client.base_url = "https://127.0.0.1"
|
||||
client.connection_host = IPv4Address("127.0.0.1")
|
||||
client.get_nvr = AsyncMock(return_value=nvr)
|
||||
client.update = AsyncMock(return_value=mock_bootstrap)
|
||||
client.update = AsyncMock(return_value=bootstrap)
|
||||
client.async_disconnect_ws = AsyncMock()
|
||||
|
||||
def subscribe(ws_callback: Callable[[WSSubscriptionMessage], None]) -> Any:
|
||||
client.ws_subscription = ws_callback
|
||||
|
||||
return Mock()
|
||||
|
||||
client.subscribe_websocket = subscribe
|
||||
return client
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@pytest.fixture(name="ufp")
|
||||
def mock_entry(
|
||||
hass: HomeAssistant,
|
||||
mock_ufp_config_entry: MockConfigEntry,
|
||||
mock_client, # pylint: disable=redefined-outer-name
|
||||
hass: HomeAssistant, ufp_config_entry: MockConfigEntry, ufp_client: ProtectApiClient
|
||||
):
|
||||
"""Mock ProtectApiClient for testing."""
|
||||
|
||||
with _patch_discovery(no_device=True), patch(
|
||||
"homeassistant.components.unifiprotect.ProtectApiClient"
|
||||
) as mock_api:
|
||||
mock_ufp_config_entry.add_to_hass(hass)
|
||||
ufp_config_entry.add_to_hass(hass)
|
||||
|
||||
mock_api.return_value = mock_client
|
||||
mock_api.return_value = ufp_client
|
||||
|
||||
yield MockEntityFixture(mock_ufp_config_entry, mock_client)
|
||||
ufp = MockUFPFixture(ufp_config_entry, ufp_client)
|
||||
|
||||
def subscribe(ws_callback: Callable[[WSSubscriptionMessage], None]) -> Any:
|
||||
ufp.ws_subscription = ws_callback
|
||||
return Mock()
|
||||
|
||||
ufp_client.subscribe_websocket = subscribe
|
||||
yield ufp
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_liveview():
|
||||
def liveview():
|
||||
"""Mock UniFi Protect Liveview."""
|
||||
|
||||
data = json.loads(load_fixture("sample_liveview.json", integration=DOMAIN))
|
||||
return Liveview.from_unifi_dict(**data)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_camera():
|
||||
@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))
|
||||
return Camera.from_unifi_dict(**data)
|
||||
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,
|
||||
]
|
||||
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.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 mock_light():
|
||||
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))
|
||||
return Light.from_unifi_dict(**data)
|
||||
yield Light.from_unifi_dict(**data)
|
||||
|
||||
Light.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_viewer():
|
||||
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))
|
||||
return Viewer.from_unifi_dict(**data)
|
||||
yield Viewer.from_unifi_dict(**data)
|
||||
|
||||
Viewer.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_sensor():
|
||||
@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))
|
||||
return Sensor.from_unifi_dict(**data)
|
||||
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
|
||||
def mock_doorlock():
|
||||
@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))
|
||||
return Doorlock.from_unifi_dict(**data)
|
||||
yield Doorlock.from_unifi_dict(**data)
|
||||
|
||||
Doorlock.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_chime():
|
||||
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))
|
||||
return Chime.from_unifi_dict(**data)
|
||||
yield Chime.from_unifi_dict(**data)
|
||||
|
||||
Chime.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def now():
|
||||
@pytest.fixture(name="fixed_now")
|
||||
def fixed_now_fixture():
|
||||
"""Return datetime object that will be consistent throughout test."""
|
||||
return dt_util.utcnow()
|
||||
|
||||
|
||||
async def time_changed(hass: HomeAssistant, seconds: int) -> None:
|
||||
"""Trigger time changed."""
|
||||
next_update = dt_util.utcnow() + timedelta(seconds)
|
||||
async_fire_time_changed(hass, next_update)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def enable_entity(
|
||||
hass: HomeAssistant, entry_id: str, entity_id: str
|
||||
) -> er.RegistryEntry:
|
||||
"""Enable a disabled entity."""
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
updated_entity = entity_registry.async_update_entity(entity_id, disabled_by=None)
|
||||
assert not updated_entity.disabled
|
||||
await hass.config_entries.async_reload(entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return updated_entity
|
||||
|
||||
|
||||
def assert_entity_counts(
|
||||
hass: HomeAssistant, platform: Platform, total: int, enabled: int
|
||||
) -> None:
|
||||
"""Assert entity counts for a given platform."""
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
entities = [
|
||||
e for e in entity_registry.entities if split_entity_id(e)[0] == platform.value
|
||||
]
|
||||
|
||||
assert len(entities) == total
|
||||
assert len(hass.states.async_all(platform.value)) == enabled
|
||||
|
||||
|
||||
def ids_from_device_description(
|
||||
platform: Platform,
|
||||
device: ProtectAdoptableDeviceModel,
|
||||
description: EntityDescription,
|
||||
) -> tuple[str, str]:
|
||||
"""Return expected unique_id and entity_id for a give platform/device/description combination."""
|
||||
|
||||
entity_name = (
|
||||
device.name.lower().replace(":", "").replace(" ", "_").replace("-", "_")
|
||||
)
|
||||
description_entity_name = (
|
||||
description.name.lower().replace(":", "").replace(" ", "_").replace("-", "_")
|
||||
)
|
||||
|
||||
unique_id = f"{device.mac}_{description.key}"
|
||||
entity_id = f"{platform.value}.{entity_name}_{description_entity_name}"
|
||||
|
||||
return unique_id, entity_id
|
||||
|
||||
|
||||
def generate_random_ids() -> tuple[str, str]:
|
||||
"""Generate random IDs for device."""
|
||||
|
||||
return random_hex(24).lower(), random_hex(12).upper()
|
||||
|
||||
|
||||
def regenerate_device_ids(device: ProtectAdoptableDeviceModel) -> None:
|
||||
"""Regenerate the IDs on UFP device."""
|
||||
|
||||
device.id, device.mac = generate_random_ids()
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"host": "192.168.6.90",
|
||||
"connectionHost": "192.168.178.217",
|
||||
"type": "UVC G4 Instant",
|
||||
"name": "Fufail Qqjx",
|
||||
"name": "Test Camera",
|
||||
"upSince": 1640020678036,
|
||||
"uptime": 3203,
|
||||
"lastSeen": 1640023881036,
|
||||
|
@ -20,18 +20,18 @@
|
|||
"isAdoptedByOther": false,
|
||||
"isProvisioned": true,
|
||||
"isRebooting": false,
|
||||
"isSshEnabled": true,
|
||||
"isSshEnabled": false,
|
||||
"canAdopt": false,
|
||||
"isAttemptingToConnect": false,
|
||||
"lastMotion": 1640021213927,
|
||||
"micVolume": 100,
|
||||
"micVolume": 0,
|
||||
"isMicEnabled": true,
|
||||
"isRecording": false,
|
||||
"isWirelessUplinkEnabled": true,
|
||||
"isMotionDetected": false,
|
||||
"isSmartDetected": false,
|
||||
"phyRate": 72,
|
||||
"hdrMode": true,
|
||||
"hdrMode": false,
|
||||
"videoMode": "default",
|
||||
"isProbingForWifi": false,
|
||||
"apMac": null,
|
||||
|
@ -57,18 +57,18 @@
|
|||
}
|
||||
},
|
||||
"videoReconfigurationInProgress": false,
|
||||
"voltage": null,
|
||||
"voltage": 20.0,
|
||||
"wiredConnectionState": {
|
||||
"phyRate": null
|
||||
"phyRate": 1000
|
||||
},
|
||||
"channels": [
|
||||
{
|
||||
"id": 0,
|
||||
"videoId": "video1",
|
||||
"name": "Jzi Bftu",
|
||||
"name": "High",
|
||||
"enabled": true,
|
||||
"isRtspEnabled": true,
|
||||
"rtspAlias": "ANOAPfoKMW7VixG1",
|
||||
"rtspAlias": "test_high_alias",
|
||||
"width": 2688,
|
||||
"height": 1512,
|
||||
"fps": 30,
|
||||
|
@ -83,10 +83,10 @@
|
|||
{
|
||||
"id": 1,
|
||||
"videoId": "video2",
|
||||
"name": "Rgcpxsf Xfwt",
|
||||
"name": "Medium",
|
||||
"enabled": true,
|
||||
"isRtspEnabled": true,
|
||||
"rtspAlias": "XHXAdHVKGVEzMNTP",
|
||||
"isRtspEnabled": false,
|
||||
"rtspAlias": null,
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"fps": 30,
|
||||
|
@ -101,7 +101,7 @@
|
|||
{
|
||||
"id": 2,
|
||||
"videoId": "video3",
|
||||
"name": "Umefvk Fug",
|
||||
"name": "Low",
|
||||
"enabled": true,
|
||||
"isRtspEnabled": false,
|
||||
"rtspAlias": null,
|
||||
|
@ -121,7 +121,7 @@
|
|||
"aeMode": "auto",
|
||||
"irLedMode": "auto",
|
||||
"irLedLevel": 255,
|
||||
"wdr": 1,
|
||||
"wdr": 0,
|
||||
"icrSensitivity": 0,
|
||||
"brightness": 50,
|
||||
"contrast": 50,
|
||||
|
@ -161,8 +161,8 @@
|
|||
"quality": 100
|
||||
},
|
||||
"osdSettings": {
|
||||
"isNameEnabled": true,
|
||||
"isDateEnabled": true,
|
||||
"isNameEnabled": false,
|
||||
"isDateEnabled": false,
|
||||
"isLogoEnabled": false,
|
||||
"isDebugEnabled": false
|
||||
},
|
||||
|
@ -181,7 +181,7 @@
|
|||
"minMotionEventTrigger": 1000,
|
||||
"endMotionEventDelay": 3000,
|
||||
"suppressIlluminationSurge": false,
|
||||
"mode": "detections",
|
||||
"mode": "always",
|
||||
"geofencing": "off",
|
||||
"motionAlgorithm": "enhanced",
|
||||
"enablePirTimelapse": false,
|
||||
|
@ -223,8 +223,8 @@
|
|||
],
|
||||
"smartDetectLines": [],
|
||||
"stats": {
|
||||
"rxBytes": 33684237,
|
||||
"txBytes": 1208318620,
|
||||
"rxBytes": 100,
|
||||
"txBytes": 100,
|
||||
"wifi": {
|
||||
"channel": 6,
|
||||
"frequency": 2437,
|
||||
|
@ -248,8 +248,8 @@
|
|||
"timelapseEndLQ": 1640021765237
|
||||
},
|
||||
"storage": {
|
||||
"used": 20401094656,
|
||||
"rate": 693.424269097809
|
||||
"used": 100,
|
||||
"rate": 0.1
|
||||
},
|
||||
"wifiQuality": 100,
|
||||
"wifiStrength": -35
|
||||
|
@ -257,7 +257,7 @@
|
|||
"featureFlags": {
|
||||
"canAdjustIrLedLevel": false,
|
||||
"canMagicZoom": false,
|
||||
"canOpticalZoom": false,
|
||||
"canOpticalZoom": true,
|
||||
"canTouchFocus": false,
|
||||
"hasAccelerometer": true,
|
||||
"hasAec": true,
|
||||
|
@ -268,15 +268,15 @@
|
|||
"hasIcrSensitivity": true,
|
||||
"hasLdc": false,
|
||||
"hasLedIr": true,
|
||||
"hasLedStatus": true,
|
||||
"hasLedStatus": false,
|
||||
"hasLineIn": false,
|
||||
"hasMic": true,
|
||||
"hasPrivacyMask": true,
|
||||
"hasPrivacyMask": false,
|
||||
"hasRtc": false,
|
||||
"hasSdCard": false,
|
||||
"hasSpeaker": true,
|
||||
"hasSpeaker": false,
|
||||
"hasWifi": true,
|
||||
"hasHdr": true,
|
||||
"hasHdr": false,
|
||||
"hasAutoICROnly": true,
|
||||
"videoModes": ["default"],
|
||||
"videoModeMaxFps": [],
|
||||
|
@ -353,14 +353,14 @@
|
|||
"frequency": 2437,
|
||||
"phyRate": 72,
|
||||
"signalQuality": 100,
|
||||
"signalStrength": -35,
|
||||
"signalStrength": -50,
|
||||
"ssid": "Mortis Camera"
|
||||
},
|
||||
"lenses": [],
|
||||
"id": "0de062b4f6922d489d3b312d",
|
||||
"isConnected": true,
|
||||
"platform": "sav530q",
|
||||
"hasSpeaker": true,
|
||||
"hasSpeaker": false,
|
||||
"hasWifi": true,
|
||||
"audioBitrate": 64000,
|
||||
"canManage": false,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"host": "192.168.144.146",
|
||||
"connectionHost": "192.168.234.27",
|
||||
"type": "UP Chime",
|
||||
"name": "Xaorvu Tvsv",
|
||||
"name": "Test Chime",
|
||||
"upSince": 1651882870009,
|
||||
"uptime": 567870,
|
||||
"lastSeen": 1652450740009,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"host": null,
|
||||
"connectionHost": "192.168.102.63",
|
||||
"type": "UFP-LOCK-R",
|
||||
"name": "Wkltg Qcjxv",
|
||||
"name": "Test Lock",
|
||||
"upSince": 1643050461849,
|
||||
"uptime": null,
|
||||
"lastSeen": 1643052750858,
|
||||
|
@ -23,9 +23,9 @@
|
|||
"canAdopt": false,
|
||||
"isAttemptingToConnect": false,
|
||||
"credentials": "955756200c7f43936df9d5f7865f058e1528945aac0f0cb27cef960eb58f17db",
|
||||
"lockStatus": "CLOSING",
|
||||
"lockStatus": "OPEN",
|
||||
"enableHomekit": false,
|
||||
"autoCloseTimeMs": 15000,
|
||||
"autoCloseTimeMs": 45000,
|
||||
"wiredConnectionState": {
|
||||
"phyRate": null
|
||||
},
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"host": "192.168.10.86",
|
||||
"connectionHost": "192.168.178.217",
|
||||
"type": "UP FloodLight",
|
||||
"name": "Byyfbpe Ufoka",
|
||||
"name": "Test Light",
|
||||
"upSince": 1638128991022,
|
||||
"uptime": 1894890,
|
||||
"lastSeen": 1640023881022,
|
||||
|
@ -19,7 +19,7 @@
|
|||
"isAdoptedByOther": false,
|
||||
"isProvisioned": false,
|
||||
"isRebooting": false,
|
||||
"isSshEnabled": true,
|
||||
"isSshEnabled": false,
|
||||
"canAdopt": false,
|
||||
"isAttemptingToConnect": false,
|
||||
"isPirMotionDetected": false,
|
||||
|
@ -31,20 +31,20 @@
|
|||
"phyRate": 100
|
||||
},
|
||||
"lightDeviceSettings": {
|
||||
"isIndicatorEnabled": true,
|
||||
"isIndicatorEnabled": false,
|
||||
"ledLevel": 6,
|
||||
"luxSensitivity": "medium",
|
||||
"pirDuration": 120000,
|
||||
"pirSensitivity": 46
|
||||
"pirDuration": 45000,
|
||||
"pirSensitivity": 45
|
||||
},
|
||||
"lightOnSettings": {
|
||||
"isLedForceOn": false
|
||||
},
|
||||
"lightModeSettings": {
|
||||
"mode": "off",
|
||||
"mode": "motion",
|
||||
"enableAt": "fulltime"
|
||||
},
|
||||
"camera": "193be66559c03ec5629f54cd",
|
||||
"camera": null,
|
||||
"id": "37dd610720816cfb5c547967",
|
||||
"isConnected": true,
|
||||
"isCameraPaired": true,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"mac": "26DBAFF133A4",
|
||||
"connectionHost": "192.168.216.198",
|
||||
"type": "UFP-SENSE",
|
||||
"name": "Egdczv Urg",
|
||||
"name": "Test Sensor",
|
||||
"upSince": 1641256963255,
|
||||
"uptime": null,
|
||||
"lastSeen": 1641259127934,
|
||||
|
@ -25,7 +25,7 @@
|
|||
"mountType": "door",
|
||||
"leakDetectedAt": null,
|
||||
"tamperingDetectedAt": null,
|
||||
"isOpened": true,
|
||||
"isOpened": false,
|
||||
"openStatusChangedAt": 1641269036582,
|
||||
"alarmTriggeredAt": null,
|
||||
"motionDetectedAt": 1641269044824,
|
||||
|
@ -34,53 +34,53 @@
|
|||
},
|
||||
"stats": {
|
||||
"light": {
|
||||
"value": 0,
|
||||
"value": 10.0,
|
||||
"status": "neutral"
|
||||
},
|
||||
"humidity": {
|
||||
"value": 35,
|
||||
"value": 10.0,
|
||||
"status": "neutral"
|
||||
},
|
||||
"temperature": {
|
||||
"value": 17.23,
|
||||
"value": 10.0,
|
||||
"status": "neutral"
|
||||
}
|
||||
},
|
||||
"bluetoothConnectionState": {
|
||||
"signalQuality": 15,
|
||||
"signalStrength": -84
|
||||
"signalStrength": -50
|
||||
},
|
||||
"batteryStatus": {
|
||||
"percentage": 100,
|
||||
"percentage": 10,
|
||||
"isLow": false
|
||||
},
|
||||
"alarmSettings": {
|
||||
"isEnabled": false
|
||||
},
|
||||
"lightSettings": {
|
||||
"isEnabled": true,
|
||||
"isEnabled": false,
|
||||
"lowThreshold": null,
|
||||
"highThreshold": null,
|
||||
"margin": 10
|
||||
},
|
||||
"motionSettings": {
|
||||
"isEnabled": true,
|
||||
"isEnabled": false,
|
||||
"sensitivity": 100
|
||||
},
|
||||
"temperatureSettings": {
|
||||
"isEnabled": true,
|
||||
"isEnabled": false,
|
||||
"lowThreshold": null,
|
||||
"highThreshold": null,
|
||||
"margin": 0.1
|
||||
},
|
||||
"humiditySettings": {
|
||||
"isEnabled": true,
|
||||
"isEnabled": false,
|
||||
"lowThreshold": null,
|
||||
"highThreshold": null,
|
||||
"margin": 1
|
||||
},
|
||||
"ledSettings": {
|
||||
"isEnabled": true
|
||||
"isEnabled": false
|
||||
},
|
||||
"bridge": "61b3f5c90050a703e700042a",
|
||||
"camera": "2f9beb2e6f79af3c32c22d49",
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
# pylint: disable=protected-access
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import copy
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from pyunifiprotect.data import Camera, Event, EventType, Light, MountType, Sensor
|
||||
from pyunifiprotect.data.nvr import EventMetadata
|
||||
|
||||
|
@ -32,218 +30,25 @@ from homeassistant.const import (
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import (
|
||||
MockEntityFixture,
|
||||
from .utils import (
|
||||
MockUFPFixture,
|
||||
assert_entity_counts,
|
||||
ids_from_device_description,
|
||||
regenerate_device_ids,
|
||||
reset_objects,
|
||||
init_entry,
|
||||
)
|
||||
|
||||
LIGHT_SENSOR_WRITE = LIGHT_SENSORS[:2]
|
||||
SENSE_SENSORS_WRITE = SENSE_SENSORS[:4]
|
||||
|
||||
|
||||
@pytest.fixture(name="camera")
|
||||
async def camera_fixture(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_camera: Camera,
|
||||
now: datetime,
|
||||
):
|
||||
"""Fixture for a single camera for testing the binary_sensor platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Camera.__config__.validate_assignment = False
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.feature_flags.has_chime = True
|
||||
camera_obj.last_ring = now - timedelta(hours=1)
|
||||
camera_obj.is_dark = False
|
||||
camera_obj.is_motion_detected = False
|
||||
regenerate_device_ids(camera_obj)
|
||||
|
||||
no_camera_obj = mock_camera.copy()
|
||||
no_camera_obj._api = mock_entry.api
|
||||
no_camera_obj.channels[0]._api = mock_entry.api
|
||||
no_camera_obj.channels[1]._api = mock_entry.api
|
||||
no_camera_obj.channels[2]._api = mock_entry.api
|
||||
no_camera_obj.name = "Unadopted Camera"
|
||||
no_camera_obj.is_adopted = False
|
||||
regenerate_device_ids(no_camera_obj)
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
no_camera_obj.id: no_camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 9, 9)
|
||||
|
||||
yield camera_obj
|
||||
|
||||
Camera.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="light")
|
||||
async def light_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light, now: datetime
|
||||
):
|
||||
"""Fixture for a single light for testing the binary_sensor platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Light.__config__.validate_assignment = False
|
||||
|
||||
light_obj = mock_light.copy()
|
||||
light_obj._api = mock_entry.api
|
||||
light_obj.name = "Test Light"
|
||||
light_obj.is_dark = False
|
||||
light_obj.is_pir_motion_detected = False
|
||||
light_obj.last_motion = now - timedelta(hours=1)
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light_obj.id: light_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 8, 8)
|
||||
|
||||
yield light_obj
|
||||
|
||||
Light.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="camera_none")
|
||||
async def camera_none_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera
|
||||
):
|
||||
"""Fixture for a single camera for testing the binary_sensor platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Camera.__config__.validate_assignment = False
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.feature_flags.has_chime = False
|
||||
camera_obj.is_dark = False
|
||||
camera_obj.is_motion_detected = False
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||
mock_entry.api.bootstrap.nvr.system_info.ustorage = None
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 2, 2)
|
||||
|
||||
yield camera_obj
|
||||
|
||||
Camera.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="sensor")
|
||||
async def sensor_fixture(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_sensor: Sensor,
|
||||
now: datetime,
|
||||
):
|
||||
"""Fixture for a single sensor for testing the binary_sensor platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Sensor.__config__.validate_assignment = False
|
||||
|
||||
sensor_obj = mock_sensor.copy()
|
||||
sensor_obj._api = mock_entry.api
|
||||
sensor_obj.name = "Test Sensor"
|
||||
sensor_obj.mount_type = MountType.DOOR
|
||||
sensor_obj.is_opened = False
|
||||
sensor_obj.battery_status.is_low = False
|
||||
sensor_obj.is_motion_detected = False
|
||||
sensor_obj.alarm_settings.is_enabled = True
|
||||
sensor_obj.motion_detected_at = now - timedelta(hours=1)
|
||||
sensor_obj.open_status_changed_at = now - timedelta(hours=1)
|
||||
sensor_obj.alarm_triggered_at = now - timedelta(hours=1)
|
||||
sensor_obj.tampering_detected_at = None
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||
mock_entry.api.bootstrap.sensors = {
|
||||
sensor_obj.id: sensor_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 10, 10)
|
||||
|
||||
yield sensor_obj
|
||||
|
||||
Sensor.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="sensor_none")
|
||||
async def sensor_none_fixture(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_sensor: Sensor,
|
||||
now: datetime,
|
||||
):
|
||||
"""Fixture for a single sensor for testing the binary_sensor platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Sensor.__config__.validate_assignment = False
|
||||
|
||||
sensor_obj = mock_sensor.copy()
|
||||
sensor_obj._api = mock_entry.api
|
||||
sensor_obj.name = "Test Sensor"
|
||||
sensor_obj.mount_type = MountType.LEAK
|
||||
sensor_obj.battery_status.is_low = False
|
||||
sensor_obj.alarm_settings.is_enabled = False
|
||||
sensor_obj.tampering_detected_at = None
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||
mock_entry.api.bootstrap.sensors = {
|
||||
sensor_obj.id: sensor_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 10, 10)
|
||||
|
||||
yield sensor_obj
|
||||
|
||||
Sensor.__config__.validate_assignment = True
|
||||
|
||||
|
||||
async def test_binary_sensor_setup_light(
|
||||
hass: HomeAssistant, light: Light, now: datetime
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
|
||||
):
|
||||
"""Test binary_sensor entity setup for light devices."""
|
||||
|
||||
await init_entry(hass, ufp, [light])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 8, 8)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
for description in LIGHT_SENSOR_WRITE:
|
||||
|
@ -262,15 +67,22 @@ async def test_binary_sensor_setup_light(
|
|||
|
||||
|
||||
async def test_binary_sensor_setup_camera_all(
|
||||
hass: HomeAssistant, camera: Camera, now: datetime
|
||||
hass: HomeAssistant,
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
unadopted_camera: Camera,
|
||||
):
|
||||
"""Test binary_sensor entity setup for camera devices (all features)."""
|
||||
|
||||
ufp.api.bootstrap.nvr.system_info.ustorage = None
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 3, 3)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
description = CAMERA_SENSORS[0]
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.BINARY_SENSOR, camera, description
|
||||
Platform.BINARY_SENSOR, doorbell, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -285,7 +97,7 @@ async def test_binary_sensor_setup_camera_all(
|
|||
# Is Dark
|
||||
description = CAMERA_SENSORS[1]
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.BINARY_SENSOR, camera, description
|
||||
Platform.BINARY_SENSOR, doorbell, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -300,7 +112,7 @@ async def test_binary_sensor_setup_camera_all(
|
|||
# Motion
|
||||
description = MOTION_SENSORS[0]
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.BINARY_SENSOR, camera, description
|
||||
Platform.BINARY_SENSOR, doorbell, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -315,16 +127,19 @@ async def test_binary_sensor_setup_camera_all(
|
|||
|
||||
|
||||
async def test_binary_sensor_setup_camera_none(
|
||||
hass: HomeAssistant,
|
||||
camera_none: Camera,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: Camera
|
||||
):
|
||||
"""Test binary_sensor entity setup for camera devices (no features)."""
|
||||
|
||||
ufp.api.bootstrap.nvr.system_info.ustorage = None
|
||||
await init_entry(hass, ufp, [camera])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 2, 2)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
description = CAMERA_SENSORS[1]
|
||||
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.BINARY_SENSOR, camera_none, description
|
||||
Platform.BINARY_SENSOR, camera, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -338,15 +153,18 @@ async def test_binary_sensor_setup_camera_none(
|
|||
|
||||
|
||||
async def test_binary_sensor_setup_sensor(
|
||||
hass: HomeAssistant, sensor: Sensor, now: datetime
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, sensor_all: Sensor
|
||||
):
|
||||
"""Test binary_sensor entity setup for sensor devices."""
|
||||
|
||||
await init_entry(hass, ufp, [sensor_all])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 10, 10)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
for description in SENSE_SENSORS_WRITE:
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.BINARY_SENSOR, sensor, description
|
||||
Platform.BINARY_SENSOR, sensor_all, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -360,10 +178,14 @@ async def test_binary_sensor_setup_sensor(
|
|||
|
||||
|
||||
async def test_binary_sensor_setup_sensor_none(
|
||||
hass: HomeAssistant, sensor_none: Sensor
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, sensor: Sensor
|
||||
):
|
||||
"""Test binary_sensor entity setup for sensor with most sensors disabled."""
|
||||
|
||||
sensor.mount_type = MountType.LEAK
|
||||
await init_entry(hass, ufp, [sensor])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 10, 10)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
expected = [
|
||||
|
@ -374,7 +196,7 @@ async def test_binary_sensor_setup_sensor_none(
|
|||
]
|
||||
for index, description in enumerate(SENSE_SENSORS_WRITE):
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.BINARY_SENSOR, sensor_none, description
|
||||
Platform.BINARY_SENSOR, sensor, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -388,27 +210,33 @@ async def test_binary_sensor_setup_sensor_none(
|
|||
|
||||
|
||||
async def test_binary_sensor_update_motion(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, camera: Camera, now: datetime
|
||||
hass: HomeAssistant,
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
unadopted_camera: Camera,
|
||||
fixed_now: datetime,
|
||||
):
|
||||
"""Test binary_sensor motion entity."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 9, 9)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.BINARY_SENSOR, camera, MOTION_SENSORS[0]
|
||||
Platform.BINARY_SENSOR, doorbell, MOTION_SENSORS[0]
|
||||
)
|
||||
|
||||
event = Event(
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION,
|
||||
start=now - timedelta(seconds=1),
|
||||
start=fixed_now - timedelta(seconds=1),
|
||||
end=None,
|
||||
score=100,
|
||||
smart_detect_types=[],
|
||||
smart_detect_event_ids=[],
|
||||
camera_id=camera.id,
|
||||
camera_id=doorbell.id,
|
||||
)
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_camera = camera.copy()
|
||||
new_camera = doorbell.copy()
|
||||
new_camera.is_motion_detected = True
|
||||
new_camera.last_motion_event_id = event.id
|
||||
|
||||
|
@ -416,10 +244,9 @@ async def test_binary_sensor_update_motion(
|
|||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_camera
|
||||
|
||||
new_bootstrap.cameras = {new_camera.id: new_camera}
|
||||
new_bootstrap.events = {event.id: event}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
||||
ufp.api.bootstrap.events = {event.id: event}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
|
@ -430,10 +257,13 @@ async def test_binary_sensor_update_motion(
|
|||
|
||||
|
||||
async def test_binary_sensor_update_light_motion(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, light: Light, now: datetime
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light, fixed_now: datetime
|
||||
):
|
||||
"""Test binary_sensor motion entity."""
|
||||
|
||||
await init_entry(hass, ufp, [light])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 8, 8)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.BINARY_SENSOR, light, LIGHT_SENSOR_WRITE[1]
|
||||
)
|
||||
|
@ -442,16 +272,15 @@ async def test_binary_sensor_update_light_motion(
|
|||
event = Event(
|
||||
id="test_event_id",
|
||||
type=EventType.MOTION_LIGHT,
|
||||
start=now - timedelta(seconds=1),
|
||||
start=fixed_now - timedelta(seconds=1),
|
||||
end=None,
|
||||
score=100,
|
||||
smart_detect_types=[],
|
||||
smart_detect_event_ids=[],
|
||||
metadata=event_metadata,
|
||||
api=mock_entry.api,
|
||||
api=ufp.api,
|
||||
)
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_light = light.copy()
|
||||
new_light.is_pir_motion_detected = True
|
||||
new_light.last_motion_event_id = event.id
|
||||
|
@ -460,10 +289,9 @@ async def test_binary_sensor_update_light_motion(
|
|||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = event
|
||||
|
||||
new_bootstrap.lights = {new_light.id: new_light}
|
||||
new_bootstrap.events = {event.id: event}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.lights = {new_light.id: new_light}
|
||||
ufp.api.bootstrap.events = {event.id: event}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
|
@ -472,29 +300,30 @@ async def test_binary_sensor_update_light_motion(
|
|||
|
||||
|
||||
async def test_binary_sensor_update_mount_type_window(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, sensor: Sensor
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, sensor_all: Sensor
|
||||
):
|
||||
"""Test binary_sensor motion entity."""
|
||||
|
||||
await init_entry(hass, ufp, [sensor_all])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 10, 10)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.BINARY_SENSOR, sensor, SENSE_SENSORS_WRITE[0]
|
||||
Platform.BINARY_SENSOR, sensor_all, SENSE_SENSORS_WRITE[0]
|
||||
)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.attributes[ATTR_DEVICE_CLASS] == BinarySensorDeviceClass.DOOR.value
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_sensor = sensor.copy()
|
||||
new_sensor = sensor_all.copy()
|
||||
new_sensor.mount_type = MountType.WINDOW
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_sensor
|
||||
|
||||
new_bootstrap.sensors = {new_sensor.id: new_sensor}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.sensors = {new_sensor.id: new_sensor}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
|
@ -503,29 +332,30 @@ async def test_binary_sensor_update_mount_type_window(
|
|||
|
||||
|
||||
async def test_binary_sensor_update_mount_type_garage(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, sensor: Sensor
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, sensor_all: Sensor
|
||||
):
|
||||
"""Test binary_sensor motion entity."""
|
||||
|
||||
await init_entry(hass, ufp, [sensor_all])
|
||||
assert_entity_counts(hass, Platform.BINARY_SENSOR, 10, 10)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.BINARY_SENSOR, sensor, SENSE_SENSORS_WRITE[0]
|
||||
Platform.BINARY_SENSOR, sensor_all, SENSE_SENSORS_WRITE[0]
|
||||
)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.attributes[ATTR_DEVICE_CLASS] == BinarySensorDeviceClass.DOOR.value
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_sensor = sensor.copy()
|
||||
new_sensor = sensor_all.copy()
|
||||
new_sensor.mount_type = MountType.GARAGE
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_sensor
|
||||
|
||||
new_bootstrap.sensors = {new_sensor.id: new_sensor}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.sensors = {new_sensor.id: new_sensor}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
|
|
|
@ -4,7 +4,6 @@ from __future__ import annotations
|
|||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from pyunifiprotect.data.devices import Chime
|
||||
|
||||
from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION
|
||||
|
@ -12,39 +11,20 @@ from homeassistant.const import ATTR_ATTRIBUTION, ATTR_ENTITY_ID, Platform
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MockEntityFixture, assert_entity_counts, enable_entity
|
||||
|
||||
|
||||
@pytest.fixture(name="chime")
|
||||
async def chime_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_chime: Chime
|
||||
):
|
||||
"""Fixture for a single camera for testing the button platform."""
|
||||
|
||||
chime_obj = mock_chime.copy()
|
||||
chime_obj._api = mock_entry.api
|
||||
chime_obj.name = "Test Chime"
|
||||
|
||||
mock_entry.api.bootstrap.chimes = {
|
||||
chime_obj.id: chime_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.BUTTON, 3, 2)
|
||||
|
||||
return chime_obj
|
||||
from .utils import MockUFPFixture, assert_entity_counts, enable_entity, init_entry
|
||||
|
||||
|
||||
async def test_reboot_button(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
ufp: MockUFPFixture,
|
||||
chime: Chime,
|
||||
):
|
||||
"""Test button entity."""
|
||||
|
||||
mock_entry.api.reboot_device = AsyncMock()
|
||||
await init_entry(hass, ufp, [chime])
|
||||
assert_entity_counts(hass, Platform.BUTTON, 3, 2)
|
||||
|
||||
ufp.api.reboot_device = AsyncMock()
|
||||
|
||||
unique_id = f"{chime.mac}_reboot"
|
||||
entity_id = "button.test_chime_reboot_device"
|
||||
|
@ -55,7 +35,7 @@ async def test_reboot_button(
|
|||
assert entity.disabled
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
@ -63,17 +43,20 @@ async def test_reboot_button(
|
|||
await hass.services.async_call(
|
||||
"button", "press", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||
)
|
||||
mock_entry.api.reboot_device.assert_called_once()
|
||||
ufp.api.reboot_device.assert_called_once()
|
||||
|
||||
|
||||
async def test_chime_button(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
ufp: MockUFPFixture,
|
||||
chime: Chime,
|
||||
):
|
||||
"""Test button entity."""
|
||||
|
||||
mock_entry.api.play_speaker = AsyncMock()
|
||||
await init_entry(hass, ufp, [chime])
|
||||
assert_entity_counts(hass, Platform.BUTTON, 3, 2)
|
||||
|
||||
ufp.api.play_speaker = AsyncMock()
|
||||
|
||||
unique_id = f"{chime.mac}_play"
|
||||
entity_id = "button.test_chime_play_chime"
|
||||
|
@ -91,4 +74,4 @@ async def test_chime_button(
|
|||
await hass.services.async_call(
|
||||
"button", "press", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||
)
|
||||
mock_entry.api.play_speaker.assert_called_once()
|
||||
ufp.api.play_speaker.assert_called_once()
|
||||
|
|
|
@ -2,16 +2,13 @@
|
|||
# pylint: disable=protected-access
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import copy
|
||||
from unittest.mock import AsyncMock, Mock
|
||||
|
||||
import pytest
|
||||
from pyunifiprotect.data import Camera as ProtectCamera, CameraChannel, StateType
|
||||
from pyunifiprotect.exceptions import NvrError
|
||||
|
||||
from homeassistant.components.camera import (
|
||||
SUPPORT_STREAM,
|
||||
Camera,
|
||||
async_get_image,
|
||||
async_get_stream_source,
|
||||
)
|
||||
|
@ -34,87 +31,15 @@ from homeassistant.core import HomeAssistant
|
|||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from .conftest import (
|
||||
MockEntityFixture,
|
||||
from .utils import (
|
||||
MockUFPFixture,
|
||||
assert_entity_counts,
|
||||
enable_entity,
|
||||
regenerate_device_ids,
|
||||
init_entry,
|
||||
time_changed,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="camera")
|
||||
async def camera_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera
|
||||
):
|
||||
"""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()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.channels[0].is_rtsp_enabled = True
|
||||
camera_obj.channels[0].name = "High"
|
||||
camera_obj.channels[1].is_rtsp_enabled = False
|
||||
camera_obj.channels[2].is_rtsp_enabled = False
|
||||
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
||||
|
||||
yield (camera_obj, "camera.test_camera_high")
|
||||
|
||||
ProtectCamera.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="camera_package")
|
||||
async def camera_package_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera
|
||||
):
|
||||
"""Fixture for a single camera for testing the camera platform."""
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.feature_flags.has_package_camera = True
|
||||
camera_obj.channels[0].is_rtsp_enabled = True
|
||||
camera_obj.channels[0].name = "High"
|
||||
camera_obj.channels[0].rtsp_alias = "test_high_alias"
|
||||
camera_obj.channels[1].is_rtsp_enabled = False
|
||||
camera_obj.channels[2].is_rtsp_enabled = False
|
||||
package_channel = camera_obj.channels[0].copy()
|
||||
package_channel.is_rtsp_enabled = False
|
||||
package_channel.name = "Package Camera"
|
||||
package_channel.id = 3
|
||||
package_channel.fps = 2
|
||||
package_channel.rtsp_alias = "test_package_alias"
|
||||
camera_obj.channels.append(package_channel)
|
||||
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.CAMERA, 3, 2)
|
||||
|
||||
return (camera_obj, "camera.test_camera_package_camera")
|
||||
|
||||
|
||||
def validate_default_camera_entity(
|
||||
hass: HomeAssistant,
|
||||
camera_obj: ProtectCamera,
|
||||
|
@ -242,99 +167,46 @@ async def validate_no_stream_camera_state(
|
|||
|
||||
|
||||
async def test_basic_setup(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: ProtectCamera
|
||||
hass: HomeAssistant,
|
||||
ufp: MockUFPFixture,
|
||||
camera_all: ProtectCamera,
|
||||
doorbell: ProtectCamera,
|
||||
):
|
||||
"""Test working setup of unifiprotect entry."""
|
||||
|
||||
camera_high_only = mock_camera.copy()
|
||||
camera_high_only._api = mock_entry.api
|
||||
camera_high_only.channels = [c.copy() for c in mock_camera.channels]
|
||||
camera_high_only.channels[0]._api = mock_entry.api
|
||||
camera_high_only.channels[1]._api = mock_entry.api
|
||||
camera_high_only.channels[2]._api = mock_entry.api
|
||||
camera_high_only = camera_all.copy()
|
||||
camera_high_only.channels = [c.copy() for c in camera_all.channels]
|
||||
camera_high_only.name = "Test Camera 1"
|
||||
camera_high_only.channels[0].is_rtsp_enabled = True
|
||||
camera_high_only.channels[0].name = "High"
|
||||
camera_high_only.channels[0].rtsp_alias = "test_high_alias"
|
||||
camera_high_only.channels[1].is_rtsp_enabled = False
|
||||
camera_high_only.channels[2].is_rtsp_enabled = False
|
||||
regenerate_device_ids(camera_high_only)
|
||||
|
||||
camera_medium_only = mock_camera.copy()
|
||||
camera_medium_only._api = mock_entry.api
|
||||
camera_medium_only.channels = [c.copy() for c in mock_camera.channels]
|
||||
camera_medium_only.channels[0]._api = mock_entry.api
|
||||
camera_medium_only.channels[1]._api = mock_entry.api
|
||||
camera_medium_only.channels[2]._api = mock_entry.api
|
||||
camera_medium_only = camera_all.copy()
|
||||
camera_medium_only.channels = [c.copy() for c in camera_all.channels]
|
||||
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[1].name = "Medium"
|
||||
camera_medium_only.channels[1].rtsp_alias = "test_medium_alias"
|
||||
camera_medium_only.channels[2].is_rtsp_enabled = False
|
||||
regenerate_device_ids(camera_medium_only)
|
||||
|
||||
camera_all_channels = mock_camera.copy()
|
||||
camera_all_channels._api = mock_entry.api
|
||||
camera_all_channels.channels = [c.copy() for c in mock_camera.channels]
|
||||
camera_all_channels.channels[0]._api = mock_entry.api
|
||||
camera_all_channels.channels[1]._api = mock_entry.api
|
||||
camera_all_channels.channels[2]._api = mock_entry.api
|
||||
camera_all_channels.name = "Test Camera 3"
|
||||
camera_all_channels.channels[0].is_rtsp_enabled = True
|
||||
camera_all_channels.channels[0].name = "High"
|
||||
camera_all_channels.channels[0].rtsp_alias = "test_high_alias"
|
||||
camera_all_channels.channels[1].is_rtsp_enabled = True
|
||||
camera_all_channels.channels[1].name = "Medium"
|
||||
camera_all_channels.channels[1].rtsp_alias = "test_medium_alias"
|
||||
camera_all_channels.channels[2].is_rtsp_enabled = True
|
||||
camera_all_channels.channels[2].name = "Low"
|
||||
camera_all_channels.channels[2].rtsp_alias = "test_low_alias"
|
||||
regenerate_device_ids(camera_all_channels)
|
||||
camera_all.name = "Test Camera 3"
|
||||
|
||||
camera_no_channels = mock_camera.copy()
|
||||
camera_no_channels._api = mock_entry.api
|
||||
camera_no_channels.channels = [c.copy() for c in camera_no_channels.channels]
|
||||
camera_no_channels.channels[0]._api = mock_entry.api
|
||||
camera_no_channels.channels[1]._api = mock_entry.api
|
||||
camera_no_channels.channels[2]._api = mock_entry.api
|
||||
camera_no_channels = camera_all.copy()
|
||||
camera_no_channels.channels = [c.copy() for c in camera_all.channels]
|
||||
camera_no_channels.name = "Test Camera 4"
|
||||
camera_no_channels.channels[0].is_rtsp_enabled = False
|
||||
camera_no_channels.channels[0].name = "High"
|
||||
camera_no_channels.channels[1].is_rtsp_enabled = False
|
||||
camera_no_channels.channels[2].is_rtsp_enabled = False
|
||||
regenerate_device_ids(camera_no_channels)
|
||||
|
||||
camera_package = mock_camera.copy()
|
||||
camera_package._api = mock_entry.api
|
||||
camera_package.channels = [c.copy() for c in mock_camera.channels]
|
||||
camera_package.channels[0]._api = mock_entry.api
|
||||
camera_package.channels[1]._api = mock_entry.api
|
||||
camera_package.channels[2]._api = mock_entry.api
|
||||
camera_package.name = "Test Camera 5"
|
||||
camera_package.channels[0].is_rtsp_enabled = True
|
||||
camera_package.channels[0].name = "High"
|
||||
camera_package.channels[0].rtsp_alias = "test_high_alias"
|
||||
camera_package.channels[1].is_rtsp_enabled = False
|
||||
camera_package.channels[2].is_rtsp_enabled = False
|
||||
regenerate_device_ids(camera_package)
|
||||
package_channel = camera_package.channels[0].copy()
|
||||
package_channel.is_rtsp_enabled = False
|
||||
package_channel.name = "Package Camera"
|
||||
package_channel.id = 3
|
||||
package_channel.fps = 2
|
||||
package_channel.rtsp_alias = "test_package_alias"
|
||||
camera_package.channels.append(package_channel)
|
||||
doorbell.name = "Test Camera 5"
|
||||
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_high_only.id: camera_high_only,
|
||||
camera_medium_only.id: camera_medium_only,
|
||||
camera_all_channels.id: camera_all_channels,
|
||||
camera_no_channels.id: camera_no_channels,
|
||||
camera_package.id: camera_package,
|
||||
}
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
devices = [
|
||||
camera_high_only,
|
||||
camera_medium_only,
|
||||
camera_all,
|
||||
camera_no_channels,
|
||||
doorbell,
|
||||
]
|
||||
await init_entry(hass, ufp, devices)
|
||||
|
||||
assert_entity_counts(hass, Platform.CAMERA, 14, 6)
|
||||
|
||||
|
@ -343,7 +215,7 @@ async def test_basic_setup(
|
|||
await validate_rtsps_camera_state(hass, camera_high_only, 0, entity_id)
|
||||
|
||||
entity_id = validate_rtsp_camera_entity(hass, camera_high_only, 0)
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
await validate_rtsp_camera_state(hass, camera_high_only, 0, entity_id)
|
||||
|
||||
# test camera 2
|
||||
|
@ -351,32 +223,32 @@ async def test_basic_setup(
|
|||
await validate_rtsps_camera_state(hass, camera_medium_only, 1, entity_id)
|
||||
|
||||
entity_id = validate_rtsp_camera_entity(hass, camera_medium_only, 1)
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
await validate_rtsp_camera_state(hass, camera_medium_only, 1, entity_id)
|
||||
|
||||
# test camera 3
|
||||
entity_id = validate_default_camera_entity(hass, camera_all_channels, 0)
|
||||
await validate_rtsps_camera_state(hass, camera_all_channels, 0, entity_id)
|
||||
entity_id = validate_default_camera_entity(hass, camera_all, 0)
|
||||
await validate_rtsps_camera_state(hass, camera_all, 0, entity_id)
|
||||
|
||||
entity_id = validate_rtsp_camera_entity(hass, camera_all_channels, 0)
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await validate_rtsp_camera_state(hass, camera_all_channels, 0, entity_id)
|
||||
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)
|
||||
|
||||
entity_id = validate_rtsps_camera_entity(hass, camera_all_channels, 1)
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await validate_rtsps_camera_state(hass, camera_all_channels, 1, entity_id)
|
||||
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)
|
||||
|
||||
entity_id = validate_rtsp_camera_entity(hass, camera_all_channels, 1)
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await validate_rtsp_camera_state(hass, camera_all_channels, 1, entity_id)
|
||||
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)
|
||||
|
||||
entity_id = validate_rtsps_camera_entity(hass, camera_all_channels, 2)
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await validate_rtsps_camera_state(hass, camera_all_channels, 2, entity_id)
|
||||
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)
|
||||
|
||||
entity_id = validate_rtsp_camera_entity(hass, camera_all_channels, 2)
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await validate_rtsp_camera_state(hass, camera_all_channels, 2, entity_id)
|
||||
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)
|
||||
|
||||
# test camera 4
|
||||
entity_id = validate_default_camera_entity(hass, camera_no_channels, 0)
|
||||
|
@ -385,197 +257,194 @@ async def test_basic_setup(
|
|||
)
|
||||
|
||||
# test camera 5
|
||||
entity_id = validate_default_camera_entity(hass, camera_package, 0)
|
||||
await validate_rtsps_camera_state(hass, camera_package, 0, entity_id)
|
||||
entity_id = validate_default_camera_entity(hass, doorbell, 0)
|
||||
await validate_rtsps_camera_state(hass, doorbell, 0, entity_id)
|
||||
|
||||
entity_id = validate_rtsp_camera_entity(hass, camera_package, 0)
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await validate_rtsp_camera_state(hass, camera_package, 0, entity_id)
|
||||
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)
|
||||
|
||||
entity_id = validate_default_camera_entity(hass, camera_package, 3)
|
||||
await validate_no_stream_camera_state(
|
||||
hass, camera_package, 3, entity_id, features=0
|
||||
)
|
||||
entity_id = validate_default_camera_entity(hass, doorbell, 3)
|
||||
await validate_no_stream_camera_state(hass, doorbell, 3, entity_id, features=0)
|
||||
|
||||
|
||||
async def test_missing_channels(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: ProtectCamera
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
||||
):
|
||||
"""Test setting up camera with no camera channels."""
|
||||
|
||||
camera = mock_camera.copy()
|
||||
camera.channels = []
|
||||
camera1 = camera.copy()
|
||||
camera1.channels = []
|
||||
|
||||
mock_entry.api.bootstrap.cameras = {camera.id: camera}
|
||||
await init_entry(hass, ufp, [camera1])
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
assert len(hass.states.async_all()) == 0
|
||||
assert len(entity_registry.entities) == 0
|
||||
assert_entity_counts(hass, Platform.CAMERA, 0, 0)
|
||||
|
||||
|
||||
async def test_camera_image(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: tuple[Camera, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
||||
):
|
||||
"""Test retrieving camera image."""
|
||||
|
||||
mock_entry.api.get_camera_snapshot = AsyncMock()
|
||||
await init_entry(hass, ufp, [camera])
|
||||
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
||||
|
||||
await async_get_image(hass, camera[1])
|
||||
mock_entry.api.get_camera_snapshot.assert_called_once()
|
||||
ufp.api.get_camera_snapshot = AsyncMock()
|
||||
|
||||
await async_get_image(hass, "camera.test_camera_high")
|
||||
ufp.api.get_camera_snapshot.assert_called_once()
|
||||
|
||||
|
||||
async def test_package_camera_image(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera_package: tuple[Camera, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: ProtectCamera
|
||||
):
|
||||
"""Test retrieving package camera image."""
|
||||
|
||||
mock_entry.api.get_package_camera_snapshot = AsyncMock()
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.CAMERA, 3, 2)
|
||||
|
||||
await async_get_image(hass, camera_package[1])
|
||||
mock_entry.api.get_package_camera_snapshot.assert_called_once()
|
||||
ufp.api.get_package_camera_snapshot = AsyncMock()
|
||||
|
||||
await async_get_image(hass, "camera.test_camera_package_camera")
|
||||
ufp.api.get_package_camera_snapshot.assert_called_once()
|
||||
|
||||
|
||||
async def test_camera_generic_update(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: tuple[ProtectCamera, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
||||
):
|
||||
"""Tests generic entity update service."""
|
||||
|
||||
await init_entry(hass, ufp, [camera])
|
||||
assert_entity_counts(hass, Platform.CAMERA, 2, 1)
|
||||
entity_id = "camera.test_camera_high"
|
||||
|
||||
assert await async_setup_component(hass, "homeassistant", {})
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
state = hass.states.get(entity_id)
|
||||
assert state and state.state == "idle"
|
||||
|
||||
mock_entry.api.update = AsyncMock(return_value=None)
|
||||
ufp.api.update = AsyncMock(return_value=None)
|
||||
await hass.services.async_call(
|
||||
"homeassistant",
|
||||
"update_entity",
|
||||
{ATTR_ENTITY_ID: camera[1]},
|
||||
{ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
state = hass.states.get(entity_id)
|
||||
assert state and state.state == "idle"
|
||||
|
||||
|
||||
async def test_camera_interval_update(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: tuple[ProtectCamera, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
||||
):
|
||||
"""Interval updates updates camera entity."""
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
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)
|
||||
assert state and state.state == "idle"
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_camera = camera[0].copy()
|
||||
new_camera = camera.copy()
|
||||
new_camera.is_recording = True
|
||||
|
||||
new_bootstrap.cameras = {new_camera.id: new_camera}
|
||||
mock_entry.api.update = AsyncMock(return_value=new_bootstrap)
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
||||
ufp.api.update = AsyncMock(return_value=ufp.api.bootstrap)
|
||||
await time_changed(hass, DEFAULT_SCAN_INTERVAL)
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
state = hass.states.get(entity_id)
|
||||
assert state and state.state == "recording"
|
||||
|
||||
|
||||
async def test_camera_bad_interval_update(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: tuple[Camera, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
||||
):
|
||||
"""Interval updates marks camera unavailable."""
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
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)
|
||||
assert state and state.state == "idle"
|
||||
|
||||
# update fails
|
||||
mock_entry.api.update = AsyncMock(side_effect=NvrError)
|
||||
ufp.api.update = AsyncMock(side_effect=NvrError)
|
||||
await time_changed(hass, DEFAULT_SCAN_INTERVAL)
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
state = hass.states.get(entity_id)
|
||||
assert state and state.state == "unavailable"
|
||||
|
||||
# next update succeeds
|
||||
mock_entry.api.update = AsyncMock(return_value=mock_entry.api.bootstrap)
|
||||
ufp.api.update = AsyncMock(return_value=ufp.api.bootstrap)
|
||||
await time_changed(hass, DEFAULT_SCAN_INTERVAL)
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
state = hass.states.get(entity_id)
|
||||
assert state and state.state == "idle"
|
||||
|
||||
|
||||
async def test_camera_ws_update(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: tuple[ProtectCamera, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
||||
):
|
||||
"""WS update updates camera entity."""
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
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)
|
||||
assert state and state.state == "idle"
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_camera = camera[0].copy()
|
||||
new_camera = camera.copy()
|
||||
new_camera.is_recording = True
|
||||
|
||||
no_camera = camera[0].copy()
|
||||
no_camera = camera.copy()
|
||||
no_camera.is_adopted = False
|
||||
|
||||
new_bootstrap.cameras = {new_camera.id: new_camera}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
|
||||
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_camera
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.ws_msg(mock_msg)
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = no_camera
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.ws_msg(mock_msg)
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
state = hass.states.get(entity_id)
|
||||
assert state and state.state == "recording"
|
||||
|
||||
|
||||
async def test_camera_ws_update_offline(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: tuple[ProtectCamera, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
||||
):
|
||||
"""WS updates marks camera unavailable."""
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
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)
|
||||
assert state and state.state == "idle"
|
||||
|
||||
# camera goes offline
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_camera = camera[0].copy()
|
||||
new_camera = camera.copy()
|
||||
new_camera.state = StateType.DISCONNECTED
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_camera
|
||||
|
||||
new_bootstrap.cameras = {new_camera.id: new_camera}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
state = hass.states.get(entity_id)
|
||||
assert state and state.state == "unavailable"
|
||||
|
||||
# camera comes back online
|
||||
|
@ -585,50 +454,53 @@ async def test_camera_ws_update_offline(
|
|||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_camera
|
||||
|
||||
new_bootstrap.cameras = {new_camera.id: new_camera}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
state = hass.states.get(entity_id)
|
||||
assert state and state.state == "idle"
|
||||
|
||||
|
||||
async def test_camera_enable_motion(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: tuple[ProtectCamera, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
||||
):
|
||||
"""Tests generic entity update service."""
|
||||
|
||||
camera[0].__fields__["set_motion_detection"] = Mock()
|
||||
camera[0].set_motion_detection = AsyncMock()
|
||||
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()
|
||||
|
||||
await hass.services.async_call(
|
||||
"camera",
|
||||
"enable_motion_detection",
|
||||
{ATTR_ENTITY_ID: camera[1]},
|
||||
{ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
camera[0].set_motion_detection.assert_called_once_with(True)
|
||||
camera.set_motion_detection.assert_called_once_with(True)
|
||||
|
||||
|
||||
async def test_camera_disable_motion(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: tuple[ProtectCamera, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: ProtectCamera
|
||||
):
|
||||
"""Tests generic entity update service."""
|
||||
|
||||
camera[0].__fields__["set_motion_detection"] = Mock()
|
||||
camera[0].set_motion_detection = AsyncMock()
|
||||
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()
|
||||
|
||||
await hass.services.async_call(
|
||||
"camera",
|
||||
"disable_motion_detection",
|
||||
{ATTR_ENTITY_ID: camera[1]},
|
||||
{ATTR_ENTITY_ID: entity_id},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
camera[0].set_motion_detection.assert_called_once_with(False)
|
||||
camera.set_motion_detection.assert_called_once_with(False)
|
||||
|
|
|
@ -6,7 +6,7 @@ import socket
|
|||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from pyunifiprotect import NotAuthorized, NvrError
|
||||
from pyunifiprotect import NotAuthorized, NvrError, ProtectApiClient
|
||||
from pyunifiprotect.data import NVR
|
||||
|
||||
from homeassistant import config_entries
|
||||
|
@ -61,7 +61,7 @@ UNIFI_DISCOVERY_DICT = asdict(UNIFI_DISCOVERY)
|
|||
UNIFI_DISCOVERY_DICT_PARTIAL = asdict(UNIFI_DISCOVERY_PARTIAL)
|
||||
|
||||
|
||||
async def test_form(hass: HomeAssistant, mock_nvr: NVR) -> None:
|
||||
async def test_form(hass: HomeAssistant, nvr: NVR) -> None:
|
||||
"""Test we get the form."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
|
@ -71,7 +71,7 @@ async def test_form(hass: HomeAssistant, mock_nvr: NVR) -> None:
|
|||
|
||||
with patch(
|
||||
"homeassistant.components.unifiprotect.config_flow.ProtectApiClient.get_nvr",
|
||||
return_value=mock_nvr,
|
||||
return_value=nvr,
|
||||
), patch(
|
||||
"homeassistant.components.unifiprotect.async_setup_entry",
|
||||
return_value=True,
|
||||
|
@ -99,7 +99,7 @@ async def test_form(hass: HomeAssistant, mock_nvr: NVR) -> None:
|
|||
assert len(mock_setup_entry.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_form_version_too_old(hass: HomeAssistant, mock_old_nvr: NVR) -> None:
|
||||
async def test_form_version_too_old(hass: HomeAssistant, old_nvr: NVR) -> None:
|
||||
"""Test we handle the version being too old."""
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
|
@ -107,7 +107,7 @@ async def test_form_version_too_old(hass: HomeAssistant, mock_old_nvr: NVR) -> N
|
|||
|
||||
with patch(
|
||||
"homeassistant.components.unifiprotect.config_flow.ProtectApiClient.get_nvr",
|
||||
return_value=mock_old_nvr,
|
||||
return_value=old_nvr,
|
||||
):
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
|
@ -168,7 +168,7 @@ async def test_form_cannot_connect(hass: HomeAssistant) -> None:
|
|||
assert result2["errors"] == {"base": "cannot_connect"}
|
||||
|
||||
|
||||
async def test_form_reauth_auth(hass: HomeAssistant, mock_nvr: NVR) -> None:
|
||||
async def test_form_reauth_auth(hass: HomeAssistant, nvr: NVR) -> None:
|
||||
"""Test we handle reauth auth."""
|
||||
mock_config = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
|
@ -217,7 +217,7 @@ async def test_form_reauth_auth(hass: HomeAssistant, mock_nvr: NVR) -> None:
|
|||
|
||||
with patch(
|
||||
"homeassistant.components.unifiprotect.config_flow.ProtectApiClient.get_nvr",
|
||||
return_value=mock_nvr,
|
||||
return_value=nvr,
|
||||
):
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
|
@ -231,7 +231,7 @@ async def test_form_reauth_auth(hass: HomeAssistant, mock_nvr: NVR) -> None:
|
|||
assert result3["reason"] == "reauth_successful"
|
||||
|
||||
|
||||
async def test_form_options(hass: HomeAssistant, mock_client) -> None:
|
||||
async def test_form_options(hass: HomeAssistant, ufp_client: ProtectApiClient) -> None:
|
||||
"""Test we handle options flows."""
|
||||
mock_config = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
|
@ -251,7 +251,7 @@ async def test_form_options(hass: HomeAssistant, mock_client) -> None:
|
|||
with _patch_discovery(), patch(
|
||||
"homeassistant.components.unifiprotect.ProtectApiClient"
|
||||
) as mock_api:
|
||||
mock_api.return_value = mock_client
|
||||
mock_api.return_value = ufp_client
|
||||
|
||||
await hass.config_entries.async_setup(mock_config.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
@ -300,7 +300,7 @@ async def test_discovered_by_ssdp_or_dhcp(
|
|||
|
||||
|
||||
async def test_discovered_by_unifi_discovery_direct_connect(
|
||||
hass: HomeAssistant, mock_nvr: NVR
|
||||
hass: HomeAssistant, nvr: NVR
|
||||
) -> None:
|
||||
"""Test a discovery from unifi-discovery."""
|
||||
|
||||
|
@ -324,7 +324,7 @@ async def test_discovered_by_unifi_discovery_direct_connect(
|
|||
|
||||
with patch(
|
||||
"homeassistant.components.unifiprotect.config_flow.ProtectApiClient.get_nvr",
|
||||
return_value=mock_nvr,
|
||||
return_value=nvr,
|
||||
), patch(
|
||||
"homeassistant.components.unifiprotect.async_setup_entry",
|
||||
return_value=True,
|
||||
|
@ -352,7 +352,7 @@ async def test_discovered_by_unifi_discovery_direct_connect(
|
|||
|
||||
|
||||
async def test_discovered_by_unifi_discovery_direct_connect_updated(
|
||||
hass: HomeAssistant, mock_nvr: NVR
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test a discovery from unifi-discovery updates the direct connect host."""
|
||||
mock_config = MockConfigEntry(
|
||||
|
@ -384,7 +384,7 @@ async def test_discovered_by_unifi_discovery_direct_connect_updated(
|
|||
|
||||
|
||||
async def test_discovered_by_unifi_discovery_direct_connect_updated_but_not_using_direct_connect(
|
||||
hass: HomeAssistant, mock_nvr: NVR
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test a discovery from unifi-discovery updates the host but not direct connect if its not in use."""
|
||||
mock_config = MockConfigEntry(
|
||||
|
@ -419,7 +419,7 @@ async def test_discovered_by_unifi_discovery_direct_connect_updated_but_not_usin
|
|||
|
||||
|
||||
async def test_discovered_by_unifi_discovery_does_not_update_ip_when_console_is_still_online(
|
||||
hass: HomeAssistant, mock_nvr: NVR
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test a discovery from unifi-discovery does not update the ip unless the console at the old ip is offline."""
|
||||
mock_config = MockConfigEntry(
|
||||
|
@ -454,7 +454,7 @@ async def test_discovered_by_unifi_discovery_does_not_update_ip_when_console_is_
|
|||
|
||||
|
||||
async def test_discovered_host_not_updated_if_existing_is_a_hostname(
|
||||
hass: HomeAssistant, mock_nvr: NVR
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test we only update the host if its an ip address from discovery."""
|
||||
mock_config = MockConfigEntry(
|
||||
|
@ -484,9 +484,7 @@ async def test_discovered_host_not_updated_if_existing_is_a_hostname(
|
|||
assert mock_config.data[CONF_HOST] == "a.hostname"
|
||||
|
||||
|
||||
async def test_discovered_by_unifi_discovery(
|
||||
hass: HomeAssistant, mock_nvr: NVR
|
||||
) -> None:
|
||||
async def test_discovered_by_unifi_discovery(hass: HomeAssistant, nvr: NVR) -> None:
|
||||
"""Test a discovery from unifi-discovery."""
|
||||
|
||||
with _patch_discovery():
|
||||
|
@ -509,7 +507,7 @@ async def test_discovered_by_unifi_discovery(
|
|||
|
||||
with patch(
|
||||
"homeassistant.components.unifiprotect.config_flow.ProtectApiClient.get_nvr",
|
||||
side_effect=[NotAuthorized, mock_nvr],
|
||||
side_effect=[NotAuthorized, nvr],
|
||||
), patch(
|
||||
"homeassistant.components.unifiprotect.async_setup_entry",
|
||||
return_value=True,
|
||||
|
@ -537,7 +535,7 @@ async def test_discovered_by_unifi_discovery(
|
|||
|
||||
|
||||
async def test_discovered_by_unifi_discovery_partial(
|
||||
hass: HomeAssistant, mock_nvr: NVR
|
||||
hass: HomeAssistant, nvr: NVR
|
||||
) -> None:
|
||||
"""Test a discovery from unifi-discovery partial."""
|
||||
|
||||
|
@ -561,7 +559,7 @@ async def test_discovered_by_unifi_discovery_partial(
|
|||
|
||||
with patch(
|
||||
"homeassistant.components.unifiprotect.config_flow.ProtectApiClient.get_nvr",
|
||||
return_value=mock_nvr,
|
||||
return_value=nvr,
|
||||
), patch(
|
||||
"homeassistant.components.unifiprotect.async_setup_entry",
|
||||
return_value=True,
|
||||
|
@ -589,7 +587,7 @@ async def test_discovered_by_unifi_discovery_partial(
|
|||
|
||||
|
||||
async def test_discovered_by_unifi_discovery_direct_connect_on_different_interface(
|
||||
hass: HomeAssistant, mock_nvr: NVR
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test a discovery from unifi-discovery from an alternate interface."""
|
||||
mock_config = MockConfigEntry(
|
||||
|
@ -619,7 +617,7 @@ async def test_discovered_by_unifi_discovery_direct_connect_on_different_interfa
|
|||
|
||||
|
||||
async def test_discovered_by_unifi_discovery_direct_connect_on_different_interface_ip_matches(
|
||||
hass: HomeAssistant, mock_nvr: NVR
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test a discovery from unifi-discovery from an alternate interface when the ip matches."""
|
||||
mock_config = MockConfigEntry(
|
||||
|
@ -649,7 +647,7 @@ async def test_discovered_by_unifi_discovery_direct_connect_on_different_interfa
|
|||
|
||||
|
||||
async def test_discovered_by_unifi_discovery_direct_connect_on_different_interface_resolver(
|
||||
hass: HomeAssistant, mock_nvr: NVR
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test a discovery from unifi-discovery from an alternate interface when direct connect domain resolves to host ip."""
|
||||
mock_config = MockConfigEntry(
|
||||
|
@ -687,7 +685,7 @@ async def test_discovered_by_unifi_discovery_direct_connect_on_different_interfa
|
|||
|
||||
|
||||
async def test_discovered_by_unifi_discovery_direct_connect_on_different_interface_resolver_fails(
|
||||
hass: HomeAssistant, mock_nvr: NVR
|
||||
hass: HomeAssistant, nvr: NVR
|
||||
) -> None:
|
||||
"""Test we can still configure if the resolver fails."""
|
||||
mock_config = MockConfigEntry(
|
||||
|
@ -730,7 +728,7 @@ async def test_discovered_by_unifi_discovery_direct_connect_on_different_interfa
|
|||
|
||||
with patch(
|
||||
"homeassistant.components.unifiprotect.config_flow.ProtectApiClient.get_nvr",
|
||||
return_value=mock_nvr,
|
||||
return_value=nvr,
|
||||
), patch(
|
||||
"homeassistant.components.unifiprotect.async_setup_entry",
|
||||
return_value=True,
|
||||
|
@ -758,7 +756,7 @@ async def test_discovered_by_unifi_discovery_direct_connect_on_different_interfa
|
|||
|
||||
|
||||
async def test_discovered_by_unifi_discovery_direct_connect_on_different_interface_resolver_no_result(
|
||||
hass: HomeAssistant, mock_nvr: NVR
|
||||
hass: HomeAssistant,
|
||||
) -> None:
|
||||
"""Test a discovery from unifi-discovery from an alternate interface when direct connect domain resolve has no result."""
|
||||
mock_config = MockConfigEntry(
|
||||
|
@ -791,7 +789,7 @@ async def test_discovered_by_unifi_discovery_direct_connect_on_different_interfa
|
|||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_discovery_can_be_ignored(hass: HomeAssistant, mock_nvr: NVR) -> None:
|
||||
async def test_discovery_can_be_ignored(hass: HomeAssistant) -> None:
|
||||
"""Test a discovery can be ignored."""
|
||||
mock_config = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
|
|
|
@ -4,53 +4,44 @@ from pyunifiprotect.data import NVR, Light
|
|||
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .conftest import MockEntityFixture, regenerate_device_ids
|
||||
from .utils import MockUFPFixture, init_entry
|
||||
|
||||
from tests.components.diagnostics import get_diagnostics_for_config_entry
|
||||
|
||||
|
||||
async def test_diagnostics(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light, hass_client
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light, hass_client
|
||||
):
|
||||
"""Test generating diagnostics for a config entry."""
|
||||
|
||||
light1 = mock_light.copy()
|
||||
light1._api = mock_entry.api
|
||||
light1.name = "Test Light 1"
|
||||
regenerate_device_ids(light1)
|
||||
await init_entry(hass, ufp, [light])
|
||||
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light1.id: light1,
|
||||
}
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
diag = await get_diagnostics_for_config_entry(hass, hass_client, ufp.entry)
|
||||
|
||||
diag = await get_diagnostics_for_config_entry(hass, hass_client, mock_entry.entry)
|
||||
|
||||
nvr_obj: NVR = mock_entry.api.bootstrap.nvr
|
||||
nvr: NVR = ufp.api.bootstrap.nvr
|
||||
# validate some of the data
|
||||
assert "nvr" in diag and isinstance(diag["nvr"], dict)
|
||||
nvr = diag["nvr"]
|
||||
nvr_dict = diag["nvr"]
|
||||
# should have been anonymized
|
||||
assert nvr["id"] != nvr_obj.id
|
||||
assert nvr["mac"] != nvr_obj.mac
|
||||
assert nvr["host"] != str(nvr_obj.host)
|
||||
assert nvr_dict["id"] != nvr.id
|
||||
assert nvr_dict["mac"] != nvr.mac
|
||||
assert nvr_dict["host"] != str(nvr.host)
|
||||
# should have been kept
|
||||
assert nvr["firmwareVersion"] == nvr_obj.firmware_version
|
||||
assert nvr["version"] == str(nvr_obj.version)
|
||||
assert nvr["type"] == nvr_obj.type
|
||||
assert nvr_dict["firmwareVersion"] == nvr.firmware_version
|
||||
assert nvr_dict["version"] == str(nvr.version)
|
||||
assert nvr_dict["type"] == nvr.type
|
||||
|
||||
assert (
|
||||
"lights" in diag
|
||||
and isinstance(diag["lights"], list)
|
||||
and len(diag["lights"]) == 1
|
||||
)
|
||||
light = diag["lights"][0]
|
||||
light_dict = diag["lights"][0]
|
||||
# should have been anonymized
|
||||
assert light["id"] != light1.id
|
||||
assert light["name"] != light1.mac
|
||||
assert light["mac"] != light1.mac
|
||||
assert light["host"] != str(light1.host)
|
||||
assert light_dict["id"] != light.id
|
||||
assert light_dict["name"] != light.mac
|
||||
assert light_dict["mac"] != light.mac
|
||||
assert light_dict["host"] != str(light.host)
|
||||
# should have been kept
|
||||
assert light["firmwareVersion"] == light1.firmware_version
|
||||
assert light["type"] == light1.type
|
||||
assert light_dict["firmwareVersion"] == light.firmware_version
|
||||
assert light_dict["type"] == light.type
|
||||
|
|
|
@ -6,7 +6,7 @@ from collections.abc import Awaitable, Callable
|
|||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import aiohttp
|
||||
from pyunifiprotect import NotAuthorized, NvrError
|
||||
from pyunifiprotect import NotAuthorized, NvrError, ProtectApiClient
|
||||
from pyunifiprotect.data import NVR, Bootstrap, Light
|
||||
|
||||
from homeassistant.components.unifiprotect.const import CONF_DISABLE_RTSP, DOMAIN
|
||||
|
@ -16,7 +16,7 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er
|
|||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from . import _patch_discovery
|
||||
from .conftest import MockEntityFixture, regenerate_device_ids
|
||||
from .utils import MockUFPFixture, init_entry
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
@ -37,37 +37,36 @@ async def remove_device(
|
|||
return response["success"]
|
||||
|
||||
|
||||
async def test_setup(hass: HomeAssistant, mock_entry: MockEntityFixture):
|
||||
async def test_setup(hass: HomeAssistant, ufp: MockUFPFixture):
|
||||
"""Test working setup of unifiprotect entry."""
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_entry.entry.state == ConfigEntryState.LOADED
|
||||
assert mock_entry.api.update.called
|
||||
assert mock_entry.entry.unique_id == mock_entry.api.bootstrap.nvr.mac
|
||||
assert ufp.entry.state == ConfigEntryState.LOADED
|
||||
assert ufp.api.update.called
|
||||
assert ufp.entry.unique_id == ufp.api.bootstrap.nvr.mac
|
||||
|
||||
|
||||
async def test_setup_multiple(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_client,
|
||||
mock_bootstrap: Bootstrap,
|
||||
ufp: MockUFPFixture,
|
||||
bootstrap: Bootstrap,
|
||||
):
|
||||
"""Test working setup of unifiprotect entry."""
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_entry.entry.state == ConfigEntryState.LOADED
|
||||
assert mock_entry.api.update.called
|
||||
assert mock_entry.entry.unique_id == mock_entry.api.bootstrap.nvr.mac
|
||||
assert ufp.entry.state == ConfigEntryState.LOADED
|
||||
assert ufp.api.update.called
|
||||
assert ufp.entry.unique_id == ufp.api.bootstrap.nvr.mac
|
||||
|
||||
nvr = mock_bootstrap.nvr
|
||||
nvr._api = mock_client
|
||||
nvr = bootstrap.nvr
|
||||
nvr._api = ufp.api
|
||||
nvr.mac = "A1E00C826983"
|
||||
nvr.id
|
||||
mock_client.get_nvr = AsyncMock(return_value=nvr)
|
||||
ufp.api.get_nvr = AsyncMock(return_value=nvr)
|
||||
|
||||
with patch("homeassistant.components.unifiprotect.ProtectApiClient") as mock_api:
|
||||
mock_config = MockConfigEntry(
|
||||
|
@ -84,148 +83,134 @@ async def test_setup_multiple(
|
|||
)
|
||||
mock_config.add_to_hass(hass)
|
||||
|
||||
mock_api.return_value = mock_client
|
||||
mock_api.return_value = ufp.api
|
||||
|
||||
await hass.config_entries.async_setup(mock_config.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_config.state == ConfigEntryState.LOADED
|
||||
assert mock_client.update.called
|
||||
assert mock_config.unique_id == mock_client.bootstrap.nvr.mac
|
||||
assert ufp.api.update.called
|
||||
assert mock_config.unique_id == ufp.api.bootstrap.nvr.mac
|
||||
|
||||
|
||||
async def test_reload(hass: HomeAssistant, mock_entry: MockEntityFixture):
|
||||
async def test_reload(hass: HomeAssistant, ufp: MockUFPFixture):
|
||||
"""Test updating entry reload entry."""
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_entry.entry.state == ConfigEntryState.LOADED
|
||||
assert ufp.entry.state == ConfigEntryState.LOADED
|
||||
|
||||
options = dict(mock_entry.entry.options)
|
||||
options = dict(ufp.entry.options)
|
||||
options[CONF_DISABLE_RTSP] = True
|
||||
hass.config_entries.async_update_entry(mock_entry.entry, options=options)
|
||||
hass.config_entries.async_update_entry(ufp.entry, options=options)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert mock_entry.entry.state == ConfigEntryState.LOADED
|
||||
assert mock_entry.api.async_disconnect_ws.called
|
||||
assert ufp.entry.state == ConfigEntryState.LOADED
|
||||
assert ufp.api.async_disconnect_ws.called
|
||||
|
||||
|
||||
async def test_unload(hass: HomeAssistant, mock_entry: MockEntityFixture):
|
||||
async def test_unload(hass: HomeAssistant, ufp: MockUFPFixture):
|
||||
"""Test unloading of unifiprotect entry."""
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_entry.entry.state == ConfigEntryState.LOADED
|
||||
assert ufp.entry.state == ConfigEntryState.LOADED
|
||||
|
||||
await hass.config_entries.async_unload(mock_entry.entry.entry_id)
|
||||
assert mock_entry.entry.state == ConfigEntryState.NOT_LOADED
|
||||
assert mock_entry.api.async_disconnect_ws.called
|
||||
await hass.config_entries.async_unload(ufp.entry.entry_id)
|
||||
assert ufp.entry.state == ConfigEntryState.NOT_LOADED
|
||||
assert ufp.api.async_disconnect_ws.called
|
||||
|
||||
|
||||
async def test_setup_too_old(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_old_nvr: NVR
|
||||
):
|
||||
async def test_setup_too_old(hass: HomeAssistant, ufp: MockUFPFixture, old_nvr: NVR):
|
||||
"""Test setup of unifiprotect entry with too old of version of UniFi Protect."""
|
||||
|
||||
mock_entry.api.get_nvr.return_value = mock_old_nvr
|
||||
ufp.api.get_nvr.return_value = old_nvr
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_entry.entry.state == ConfigEntryState.SETUP_ERROR
|
||||
assert not mock_entry.api.update.called
|
||||
assert ufp.entry.state == ConfigEntryState.SETUP_ERROR
|
||||
assert not ufp.api.update.called
|
||||
|
||||
|
||||
async def test_setup_failed_update(hass: HomeAssistant, mock_entry: MockEntityFixture):
|
||||
async def test_setup_failed_update(hass: HomeAssistant, ufp: MockUFPFixture):
|
||||
"""Test setup of unifiprotect entry with failed update."""
|
||||
|
||||
mock_entry.api.update = AsyncMock(side_effect=NvrError)
|
||||
ufp.api.update = AsyncMock(side_effect=NvrError)
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_entry.entry.state == ConfigEntryState.SETUP_RETRY
|
||||
assert mock_entry.api.update.called
|
||||
assert ufp.entry.state == ConfigEntryState.SETUP_RETRY
|
||||
assert ufp.api.update.called
|
||||
|
||||
|
||||
async def test_setup_failed_update_reauth(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture
|
||||
):
|
||||
async def test_setup_failed_update_reauth(hass: HomeAssistant, ufp: MockUFPFixture):
|
||||
"""Test setup of unifiprotect entry with update that gives unauthroized error."""
|
||||
|
||||
mock_entry.api.update = AsyncMock(side_effect=NotAuthorized)
|
||||
ufp.api.update = AsyncMock(side_effect=NotAuthorized)
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_entry.entry.state == ConfigEntryState.SETUP_RETRY
|
||||
assert mock_entry.api.update.called
|
||||
assert ufp.entry.state == ConfigEntryState.SETUP_RETRY
|
||||
assert ufp.api.update.called
|
||||
|
||||
|
||||
async def test_setup_failed_error(hass: HomeAssistant, mock_entry: MockEntityFixture):
|
||||
async def test_setup_failed_error(hass: HomeAssistant, ufp: MockUFPFixture):
|
||||
"""Test setup of unifiprotect entry with generic error."""
|
||||
|
||||
mock_entry.api.get_nvr = AsyncMock(side_effect=NvrError)
|
||||
ufp.api.get_nvr = AsyncMock(side_effect=NvrError)
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_entry.entry.state == ConfigEntryState.SETUP_RETRY
|
||||
assert not mock_entry.api.update.called
|
||||
assert ufp.entry.state == ConfigEntryState.SETUP_RETRY
|
||||
assert not ufp.api.update.called
|
||||
|
||||
|
||||
async def test_setup_failed_auth(hass: HomeAssistant, mock_entry: MockEntityFixture):
|
||||
async def test_setup_failed_auth(hass: HomeAssistant, ufp: MockUFPFixture):
|
||||
"""Test setup of unifiprotect entry with unauthorized error."""
|
||||
|
||||
mock_entry.api.get_nvr = AsyncMock(side_effect=NotAuthorized)
|
||||
ufp.api.get_nvr = AsyncMock(side_effect=NotAuthorized)
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
assert mock_entry.entry.state == ConfigEntryState.SETUP_ERROR
|
||||
assert not mock_entry.api.update.called
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
assert ufp.entry.state == ConfigEntryState.SETUP_ERROR
|
||||
assert not ufp.api.update.called
|
||||
|
||||
|
||||
async def test_setup_starts_discovery(
|
||||
hass: HomeAssistant, mock_ufp_config_entry: ConfigEntry, mock_client
|
||||
hass: HomeAssistant, ufp_config_entry: ConfigEntry, ufp_client: ProtectApiClient
|
||||
):
|
||||
"""Test setting up will start discovery."""
|
||||
with _patch_discovery(), patch(
|
||||
"homeassistant.components.unifiprotect.ProtectApiClient"
|
||||
) as mock_api:
|
||||
mock_ufp_config_entry.add_to_hass(hass)
|
||||
mock_api.return_value = mock_client
|
||||
mock_entry = MockEntityFixture(mock_ufp_config_entry, mock_client)
|
||||
ufp_config_entry.add_to_hass(hass)
|
||||
mock_api.return_value = ufp_client
|
||||
ufp = MockUFPFixture(ufp_config_entry, ufp_client)
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert mock_entry.entry.state == ConfigEntryState.LOADED
|
||||
assert ufp.entry.state == ConfigEntryState.LOADED
|
||||
await hass.async_block_till_done()
|
||||
assert len(hass.config_entries.flow.async_progress_by_handler(DOMAIN)) == 1
|
||||
|
||||
|
||||
async def test_device_remove_devices(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_light: Light,
|
||||
ufp: MockUFPFixture,
|
||||
light: Light,
|
||||
hass_ws_client: Callable[
|
||||
[HomeAssistant], Awaitable[aiohttp.ClientWebSocketResponse]
|
||||
],
|
||||
) -> None:
|
||||
"""Test we can only remove a device that no longer exists."""
|
||||
|
||||
await init_entry(hass, ufp, [light])
|
||||
assert await async_setup_component(hass, "config", {})
|
||||
|
||||
light1 = mock_light.copy()
|
||||
light1._api = mock_entry.api
|
||||
light1.name = "Test Light 1"
|
||||
regenerate_device_ids(light1)
|
||||
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light1.id: light1,
|
||||
}
|
||||
|
||||
mock_entry.api.get_bootstrap = AsyncMock(return_value=mock_entry.api.bootstrap)
|
||||
light_entity_id = "light.test_light_1"
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
entry_id = mock_entry.entry.entry_id
|
||||
entity_id = "light.test_light"
|
||||
entry_id = ufp.entry.entry_id
|
||||
|
||||
registry: er.EntityRegistry = er.async_get(hass)
|
||||
entity = registry.entities[light_entity_id]
|
||||
entity = registry.async_get(entity_id)
|
||||
assert entity is not None
|
||||
device_registry = dr.async_get(hass)
|
||||
|
||||
live_device_entry = device_registry.async_get(entity.device_id)
|
||||
|
@ -246,7 +231,7 @@ async def test_device_remove_devices(
|
|||
|
||||
async def test_device_remove_devices_nvr(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
ufp: MockUFPFixture,
|
||||
hass_ws_client: Callable[
|
||||
[HomeAssistant], Awaitable[aiohttp.ClientWebSocketResponse]
|
||||
],
|
||||
|
@ -254,10 +239,10 @@ async def test_device_remove_devices_nvr(
|
|||
"""Test we can only remove a NVR device that no longer exists."""
|
||||
assert await async_setup_component(hass, "config", {})
|
||||
|
||||
mock_entry.api.get_bootstrap = AsyncMock(return_value=mock_entry.api.bootstrap)
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
ufp.api.get_bootstrap = AsyncMock(return_value=ufp.api.bootstrap)
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
entry_id = mock_entry.entry.entry_id
|
||||
entry_id = ufp.entry.entry_id
|
||||
|
||||
device_registry = dr.async_get(hass)
|
||||
|
||||
|
|
|
@ -2,11 +2,10 @@
|
|||
# pylint: disable=protected-access
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import copy
|
||||
from unittest.mock import AsyncMock, Mock
|
||||
|
||||
import pytest
|
||||
from pyunifiprotect.data import Light
|
||||
from pyunifiprotect.data.types import LEDLevel
|
||||
|
||||
from homeassistant.components.light import ATTR_BRIGHTNESS
|
||||
from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION
|
||||
|
@ -20,53 +19,19 @@ from homeassistant.const import (
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MockEntityFixture, assert_entity_counts, regenerate_device_ids
|
||||
|
||||
|
||||
@pytest.fixture(name="light")
|
||||
async def light_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light
|
||||
):
|
||||
"""Fixture for a single light for testing the light platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Light.__config__.validate_assignment = False
|
||||
|
||||
light_obj = mock_light.copy()
|
||||
light_obj._api = mock_entry.api
|
||||
light_obj.name = "Test Light"
|
||||
light_obj.is_light_on = False
|
||||
regenerate_device_ids(light_obj)
|
||||
|
||||
no_light_obj = mock_light.copy()
|
||||
no_light_obj._api = mock_entry.api
|
||||
no_light_obj.name = "Unadopted Light"
|
||||
no_light_obj.is_adopted = False
|
||||
regenerate_device_ids(no_light_obj)
|
||||
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light_obj.id: light_obj,
|
||||
no_light_obj.id: no_light_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.LIGHT, 1, 1)
|
||||
|
||||
yield (light_obj, "light.test_light")
|
||||
|
||||
Light.__config__.validate_assignment = True
|
||||
from .utils import MockUFPFixture, assert_entity_counts, init_entry
|
||||
|
||||
|
||||
async def test_light_setup(
|
||||
hass: HomeAssistant,
|
||||
light: tuple[Light, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light, unadopted_light: Light
|
||||
):
|
||||
"""Test light entity setup."""
|
||||
|
||||
unique_id = light[0].mac
|
||||
entity_id = light[1]
|
||||
await init_entry(hass, ufp, [light, unadopted_light])
|
||||
assert_entity_counts(hass, Platform.LIGHT, 1, 1)
|
||||
|
||||
unique_id = light.mac
|
||||
entity_id = "light.test_light"
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -80,41 +45,42 @@ async def test_light_setup(
|
|||
|
||||
|
||||
async def test_light_update(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
light: tuple[Light, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light, unadopted_light: Light
|
||||
):
|
||||
"""Test light entity update."""
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_light = light[0].copy()
|
||||
await init_entry(hass, ufp, [light, unadopted_light])
|
||||
assert_entity_counts(hass, Platform.LIGHT, 1, 1)
|
||||
|
||||
new_light = light.copy()
|
||||
new_light.is_light_on = True
|
||||
new_light.light_device_settings.led_level = 3
|
||||
new_light.light_device_settings.led_level = LEDLevel(3)
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_light
|
||||
|
||||
new_bootstrap.lights = {new_light.id: new_light}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.lights = {new_light.id: new_light}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(light[1])
|
||||
state = hass.states.get("light.test_light")
|
||||
assert state
|
||||
assert state.state == STATE_ON
|
||||
assert state.attributes[ATTR_BRIGHTNESS] == 128
|
||||
|
||||
|
||||
async def test_light_turn_on(
|
||||
hass: HomeAssistant,
|
||||
light: tuple[Light, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light, unadopted_light: Light
|
||||
):
|
||||
"""Test light entity turn off."""
|
||||
|
||||
entity_id = light[1]
|
||||
light[0].__fields__["set_light"] = Mock()
|
||||
light[0].set_light = AsyncMock()
|
||||
await init_entry(hass, ufp, [light, unadopted_light])
|
||||
assert_entity_counts(hass, Platform.LIGHT, 1, 1)
|
||||
|
||||
entity_id = "light.test_light"
|
||||
light.__fields__["set_light"] = Mock()
|
||||
light.set_light = AsyncMock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
|
@ -123,18 +89,20 @@ async def test_light_turn_on(
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
light[0].set_light.assert_called_once_with(True, 3)
|
||||
light.set_light.assert_called_once_with(True, 3)
|
||||
|
||||
|
||||
async def test_light_turn_off(
|
||||
hass: HomeAssistant,
|
||||
light: tuple[Light, str],
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light, unadopted_light: Light
|
||||
):
|
||||
"""Test light entity turn on."""
|
||||
|
||||
entity_id = light[1]
|
||||
light[0].__fields__["set_light"] = Mock()
|
||||
light[0].set_light = AsyncMock()
|
||||
await init_entry(hass, ufp, [light, unadopted_light])
|
||||
assert_entity_counts(hass, Platform.LIGHT, 1, 1)
|
||||
|
||||
entity_id = "light.test_light"
|
||||
light.__fields__["set_light"] = Mock()
|
||||
light.set_light = AsyncMock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"light",
|
||||
|
@ -143,4 +111,4 @@ async def test_light_turn_off(
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
light[0].set_light.assert_called_once_with(False)
|
||||
light.set_light.assert_called_once_with(False)
|
||||
|
|
|
@ -2,10 +2,8 @@
|
|||
# pylint: disable=protected-access
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import copy
|
||||
from unittest.mock import AsyncMock, Mock
|
||||
|
||||
import pytest
|
||||
from pyunifiprotect.data import Doorlock, LockStatusType
|
||||
|
||||
from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION
|
||||
|
@ -23,53 +21,22 @@ from homeassistant.const import (
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MockEntityFixture, assert_entity_counts, regenerate_device_ids
|
||||
|
||||
|
||||
@pytest.fixture(name="doorlock")
|
||||
async def doorlock_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_doorlock: Doorlock
|
||||
):
|
||||
"""Fixture for a single doorlock for testing the lock platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Doorlock.__config__.validate_assignment = False
|
||||
|
||||
lock_obj = mock_doorlock.copy()
|
||||
lock_obj._api = mock_entry.api
|
||||
lock_obj.name = "Test Lock"
|
||||
lock_obj.lock_status = LockStatusType.OPEN
|
||||
regenerate_device_ids(lock_obj)
|
||||
|
||||
no_lock_obj = mock_doorlock.copy()
|
||||
no_lock_obj._api = mock_entry.api
|
||||
no_lock_obj.name = "Unadopted Lock"
|
||||
no_lock_obj.is_adopted = False
|
||||
regenerate_device_ids(no_lock_obj)
|
||||
|
||||
mock_entry.api.bootstrap.doorlocks = {
|
||||
lock_obj.id: lock_obj,
|
||||
no_lock_obj.id: no_lock_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.LOCK, 1, 1)
|
||||
|
||||
yield (lock_obj, "lock.test_lock_lock")
|
||||
|
||||
Doorlock.__config__.validate_assignment = True
|
||||
from .utils import MockUFPFixture, assert_entity_counts, init_entry
|
||||
|
||||
|
||||
async def test_lock_setup(
|
||||
hass: HomeAssistant,
|
||||
doorlock: tuple[Doorlock, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorlock: Doorlock,
|
||||
unadopted_doorlock: Doorlock,
|
||||
):
|
||||
"""Test lock entity setup."""
|
||||
|
||||
unique_id = f"{doorlock[0].mac}_lock"
|
||||
entity_id = doorlock[1]
|
||||
await init_entry(hass, ufp, [doorlock, unadopted_doorlock])
|
||||
assert_entity_counts(hass, Platform.LOCK, 1, 1)
|
||||
|
||||
unique_id = f"{doorlock.mac}_lock"
|
||||
entity_id = "lock.test_lock_lock"
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -84,166 +51,183 @@ async def test_lock_setup(
|
|||
|
||||
async def test_lock_locked(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
doorlock: tuple[Doorlock, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorlock: Doorlock,
|
||||
unadopted_doorlock: Doorlock,
|
||||
):
|
||||
"""Test lock entity locked."""
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_lock = doorlock[0].copy()
|
||||
await init_entry(hass, ufp, [doorlock, unadopted_doorlock])
|
||||
assert_entity_counts(hass, Platform.LOCK, 1, 1)
|
||||
|
||||
new_lock = doorlock.copy()
|
||||
new_lock.lock_status = LockStatusType.CLOSED
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_lock
|
||||
|
||||
new_bootstrap.doorlocks = {new_lock.id: new_lock}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.doorlocks = {new_lock.id: new_lock}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(doorlock[1])
|
||||
state = hass.states.get("lock.test_lock_lock")
|
||||
assert state
|
||||
assert state.state == STATE_LOCKED
|
||||
|
||||
|
||||
async def test_lock_unlocking(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
doorlock: tuple[Doorlock, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorlock: Doorlock,
|
||||
unadopted_doorlock: Doorlock,
|
||||
):
|
||||
"""Test lock entity unlocking."""
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_lock = doorlock[0].copy()
|
||||
await init_entry(hass, ufp, [doorlock, unadopted_doorlock])
|
||||
assert_entity_counts(hass, Platform.LOCK, 1, 1)
|
||||
|
||||
new_lock = doorlock.copy()
|
||||
new_lock.lock_status = LockStatusType.OPENING
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_lock
|
||||
|
||||
new_bootstrap.doorlocks = {new_lock.id: new_lock}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.doorlocks = {new_lock.id: new_lock}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(doorlock[1])
|
||||
state = hass.states.get("lock.test_lock_lock")
|
||||
assert state
|
||||
assert state.state == STATE_UNLOCKING
|
||||
|
||||
|
||||
async def test_lock_locking(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
doorlock: tuple[Doorlock, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorlock: Doorlock,
|
||||
unadopted_doorlock: Doorlock,
|
||||
):
|
||||
"""Test lock entity locking."""
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_lock = doorlock[0].copy()
|
||||
await init_entry(hass, ufp, [doorlock, unadopted_doorlock])
|
||||
assert_entity_counts(hass, Platform.LOCK, 1, 1)
|
||||
|
||||
new_lock = doorlock.copy()
|
||||
new_lock.lock_status = LockStatusType.CLOSING
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_lock
|
||||
|
||||
new_bootstrap.doorlocks = {new_lock.id: new_lock}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.doorlocks = {new_lock.id: new_lock}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(doorlock[1])
|
||||
state = hass.states.get("lock.test_lock_lock")
|
||||
assert state
|
||||
assert state.state == STATE_LOCKING
|
||||
|
||||
|
||||
async def test_lock_jammed(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
doorlock: tuple[Doorlock, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorlock: Doorlock,
|
||||
unadopted_doorlock: Doorlock,
|
||||
):
|
||||
"""Test lock entity jammed."""
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_lock = doorlock[0].copy()
|
||||
await init_entry(hass, ufp, [doorlock, unadopted_doorlock])
|
||||
assert_entity_counts(hass, Platform.LOCK, 1, 1)
|
||||
|
||||
new_lock = doorlock.copy()
|
||||
new_lock.lock_status = LockStatusType.JAMMED_WHILE_CLOSING
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_lock
|
||||
|
||||
new_bootstrap.doorlocks = {new_lock.id: new_lock}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.doorlocks = {new_lock.id: new_lock}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(doorlock[1])
|
||||
state = hass.states.get("lock.test_lock_lock")
|
||||
assert state
|
||||
assert state.state == STATE_JAMMED
|
||||
|
||||
|
||||
async def test_lock_unavailable(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
doorlock: tuple[Doorlock, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorlock: Doorlock,
|
||||
unadopted_doorlock: Doorlock,
|
||||
):
|
||||
"""Test lock entity unavailable."""
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_lock = doorlock[0].copy()
|
||||
await init_entry(hass, ufp, [doorlock, unadopted_doorlock])
|
||||
assert_entity_counts(hass, Platform.LOCK, 1, 1)
|
||||
|
||||
new_lock = doorlock.copy()
|
||||
new_lock.lock_status = LockStatusType.NOT_CALIBRATED
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_lock
|
||||
|
||||
new_bootstrap.doorlocks = {new_lock.id: new_lock}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.doorlocks = {new_lock.id: new_lock}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(doorlock[1])
|
||||
state = hass.states.get("lock.test_lock_lock")
|
||||
assert state
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_lock_do_lock(
|
||||
hass: HomeAssistant,
|
||||
doorlock: tuple[Doorlock, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorlock: Doorlock,
|
||||
unadopted_doorlock: Doorlock,
|
||||
):
|
||||
"""Test lock entity lock service."""
|
||||
|
||||
doorlock[0].__fields__["close_lock"] = Mock()
|
||||
doorlock[0].close_lock = AsyncMock()
|
||||
await init_entry(hass, ufp, [doorlock, unadopted_doorlock])
|
||||
assert_entity_counts(hass, Platform.LOCK, 1, 1)
|
||||
|
||||
doorlock.__fields__["close_lock"] = Mock()
|
||||
doorlock.close_lock = AsyncMock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"lock",
|
||||
"lock",
|
||||
{ATTR_ENTITY_ID: doorlock[1]},
|
||||
{ATTR_ENTITY_ID: "lock.test_lock_lock"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
doorlock[0].close_lock.assert_called_once()
|
||||
doorlock.close_lock.assert_called_once()
|
||||
|
||||
|
||||
async def test_lock_do_unlock(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
doorlock: tuple[Doorlock, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorlock: Doorlock,
|
||||
unadopted_doorlock: Doorlock,
|
||||
):
|
||||
"""Test lock entity unlock service."""
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_lock = doorlock[0].copy()
|
||||
await init_entry(hass, ufp, [doorlock, unadopted_doorlock])
|
||||
assert_entity_counts(hass, Platform.LOCK, 1, 1)
|
||||
|
||||
new_lock = doorlock.copy()
|
||||
new_lock.lock_status = LockStatusType.CLOSED
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_lock
|
||||
|
||||
new_bootstrap.doorlocks = {new_lock.id: new_lock}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.doorlocks = {new_lock.id: new_lock}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
new_lock.__fields__["open_lock"] = Mock()
|
||||
|
@ -252,7 +236,7 @@ async def test_lock_do_unlock(
|
|||
await hass.services.async_call(
|
||||
"lock",
|
||||
"unlock",
|
||||
{ATTR_ENTITY_ID: doorlock[1]},
|
||||
{ATTR_ENTITY_ID: "lock.test_lock_lock"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
# pylint: disable=protected-access
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import copy
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
|
@ -26,66 +25,29 @@ from homeassistant.core import HomeAssistant
|
|||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MockEntityFixture, assert_entity_counts, regenerate_device_ids
|
||||
|
||||
|
||||
@pytest.fixture(name="camera")
|
||||
async def camera_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera
|
||||
):
|
||||
"""Fixture for a single camera for testing the media_player platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Camera.__config__.validate_assignment = False
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.feature_flags.has_speaker = True
|
||||
regenerate_device_ids(camera_obj)
|
||||
|
||||
no_camera_obj = mock_camera.copy()
|
||||
no_camera_obj._api = mock_entry.api
|
||||
no_camera_obj.channels[0]._api = mock_entry.api
|
||||
no_camera_obj.channels[1]._api = mock_entry.api
|
||||
no_camera_obj.channels[2]._api = mock_entry.api
|
||||
no_camera_obj.name = "Unadopted Camera"
|
||||
no_camera_obj.is_adopted = False
|
||||
regenerate_device_ids(no_camera_obj)
|
||||
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
no_camera_obj.id: no_camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
|
||||
|
||||
yield (camera_obj, "media_player.test_camera_speaker")
|
||||
|
||||
Camera.__config__.validate_assignment = True
|
||||
from .utils import MockUFPFixture, assert_entity_counts, init_entry
|
||||
|
||||
|
||||
async def test_media_player_setup(
|
||||
hass: HomeAssistant,
|
||||
camera: tuple[Camera, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
unadopted_camera: Camera,
|
||||
):
|
||||
"""Test media_player entity setup."""
|
||||
|
||||
unique_id = f"{camera[0].mac}_speaker"
|
||||
entity_id = camera[1]
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
|
||||
|
||||
unique_id = f"{doorbell.mac}_speaker"
|
||||
entity_id = "media_player.test_camera_speaker"
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
assert entity
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
expected_volume = float(camera[0].speaker_settings.volume / 100)
|
||||
expected_volume = float(doorbell.speaker_settings.volume / 100)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
@ -98,13 +60,16 @@ async def test_media_player_setup(
|
|||
|
||||
async def test_media_player_update(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: tuple[Camera, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
unadopted_camera: Camera,
|
||||
):
|
||||
"""Test media_player entity update."""
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_camera = camera[0].copy()
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
|
||||
|
||||
new_camera = doorbell.copy()
|
||||
new_camera.talkback_stream = Mock()
|
||||
new_camera.talkback_stream.is_running = True
|
||||
|
||||
|
@ -112,44 +77,51 @@ async def test_media_player_update(
|
|||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_camera
|
||||
|
||||
new_bootstrap.cameras = {new_camera.id: new_camera}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(camera[1])
|
||||
state = hass.states.get("media_player.test_camera_speaker")
|
||||
assert state
|
||||
assert state.state == STATE_PLAYING
|
||||
|
||||
|
||||
async def test_media_player_set_volume(
|
||||
hass: HomeAssistant,
|
||||
camera: tuple[Camera, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
unadopted_camera: Camera,
|
||||
):
|
||||
"""Test media_player entity test set_volume_level."""
|
||||
|
||||
camera[0].__fields__["set_speaker_volume"] = Mock()
|
||||
camera[0].set_speaker_volume = AsyncMock()
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
|
||||
|
||||
doorbell.__fields__["set_speaker_volume"] = Mock()
|
||||
doorbell.set_speaker_volume = AsyncMock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"media_player",
|
||||
"volume_set",
|
||||
{ATTR_ENTITY_ID: camera[1], "volume_level": 0.5},
|
||||
{ATTR_ENTITY_ID: "media_player.test_camera_speaker", "volume_level": 0.5},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
camera[0].set_speaker_volume.assert_called_once_with(50)
|
||||
doorbell.set_speaker_volume.assert_called_once_with(50)
|
||||
|
||||
|
||||
async def test_media_player_stop(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: tuple[Camera, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
unadopted_camera: Camera,
|
||||
):
|
||||
"""Test media_player entity test media_stop."""
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_camera = camera[0].copy()
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
|
||||
|
||||
new_camera = doorbell.copy()
|
||||
new_camera.talkback_stream = AsyncMock()
|
||||
new_camera.talkback_stream.is_running = True
|
||||
|
||||
|
@ -157,15 +129,14 @@ async def test_media_player_stop(
|
|||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_camera
|
||||
|
||||
new_bootstrap.cameras = {new_camera.id: new_camera}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
await hass.services.async_call(
|
||||
"media_player",
|
||||
"media_stop",
|
||||
{ATTR_ENTITY_ID: camera[1]},
|
||||
{ATTR_ENTITY_ID: "media_player.test_camera_speaker"},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
|
@ -174,44 +145,56 @@ async def test_media_player_stop(
|
|||
|
||||
async def test_media_player_play(
|
||||
hass: HomeAssistant,
|
||||
camera: tuple[Camera, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
unadopted_camera: Camera,
|
||||
):
|
||||
"""Test media_player entity test play_media."""
|
||||
camera[0].__fields__["stop_audio"] = Mock()
|
||||
camera[0].__fields__["play_audio"] = Mock()
|
||||
camera[0].__fields__["wait_until_audio_completes"] = Mock()
|
||||
camera[0].stop_audio = AsyncMock()
|
||||
camera[0].play_audio = AsyncMock()
|
||||
camera[0].wait_until_audio_completes = AsyncMock()
|
||||
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
|
||||
|
||||
doorbell.__fields__["stop_audio"] = Mock()
|
||||
doorbell.__fields__["play_audio"] = Mock()
|
||||
doorbell.__fields__["wait_until_audio_completes"] = Mock()
|
||||
doorbell.stop_audio = AsyncMock()
|
||||
doorbell.play_audio = AsyncMock()
|
||||
doorbell.wait_until_audio_completes = AsyncMock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"media_player",
|
||||
"play_media",
|
||||
{
|
||||
ATTR_ENTITY_ID: camera[1],
|
||||
ATTR_ENTITY_ID: "media_player.test_camera_speaker",
|
||||
"media_content_id": "http://example.com/test.mp3",
|
||||
"media_content_type": "music",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
camera[0].play_audio.assert_called_once_with(
|
||||
doorbell.play_audio.assert_called_once_with(
|
||||
"http://example.com/test.mp3", blocking=False
|
||||
)
|
||||
camera[0].wait_until_audio_completes.assert_called_once()
|
||||
doorbell.wait_until_audio_completes.assert_called_once()
|
||||
|
||||
|
||||
async def test_media_player_play_media_source(
|
||||
hass: HomeAssistant,
|
||||
camera: tuple[Camera, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
unadopted_camera: Camera,
|
||||
):
|
||||
"""Test media_player entity test play_media."""
|
||||
camera[0].__fields__["stop_audio"] = Mock()
|
||||
camera[0].__fields__["play_audio"] = Mock()
|
||||
camera[0].__fields__["wait_until_audio_completes"] = Mock()
|
||||
camera[0].stop_audio = AsyncMock()
|
||||
camera[0].play_audio = AsyncMock()
|
||||
camera[0].wait_until_audio_completes = AsyncMock()
|
||||
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
|
||||
|
||||
doorbell.__fields__["stop_audio"] = Mock()
|
||||
doorbell.__fields__["play_audio"] = Mock()
|
||||
doorbell.__fields__["wait_until_audio_completes"] = Mock()
|
||||
doorbell.stop_audio = AsyncMock()
|
||||
doorbell.play_audio = AsyncMock()
|
||||
doorbell.wait_until_audio_completes = AsyncMock()
|
||||
|
||||
with patch(
|
||||
"homeassistant.components.media_source.async_resolve_media",
|
||||
|
@ -221,65 +204,75 @@ async def test_media_player_play_media_source(
|
|||
"media_player",
|
||||
"play_media",
|
||||
{
|
||||
ATTR_ENTITY_ID: camera[1],
|
||||
ATTR_ENTITY_ID: "media_player.test_camera_speaker",
|
||||
"media_content_id": "media-source://some_source/some_id",
|
||||
"media_content_type": "audio/mpeg",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
camera[0].play_audio.assert_called_once_with(
|
||||
doorbell.play_audio.assert_called_once_with(
|
||||
"http://example.com/test.mp3", blocking=False
|
||||
)
|
||||
camera[0].wait_until_audio_completes.assert_called_once()
|
||||
doorbell.wait_until_audio_completes.assert_called_once()
|
||||
|
||||
|
||||
async def test_media_player_play_invalid(
|
||||
hass: HomeAssistant,
|
||||
camera: tuple[Camera, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
unadopted_camera: Camera,
|
||||
):
|
||||
"""Test media_player entity test play_media, not music."""
|
||||
|
||||
camera[0].__fields__["play_audio"] = Mock()
|
||||
camera[0].play_audio = AsyncMock()
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
|
||||
|
||||
doorbell.__fields__["play_audio"] = Mock()
|
||||
doorbell.play_audio = AsyncMock()
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
"media_player",
|
||||
"play_media",
|
||||
{
|
||||
ATTR_ENTITY_ID: camera[1],
|
||||
ATTR_ENTITY_ID: "media_player.test_camera_speaker",
|
||||
"media_content_id": "/test.png",
|
||||
"media_content_type": "image",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert not camera[0].play_audio.called
|
||||
assert not doorbell.play_audio.called
|
||||
|
||||
|
||||
async def test_media_player_play_error(
|
||||
hass: HomeAssistant,
|
||||
camera: tuple[Camera, str],
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
unadopted_camera: Camera,
|
||||
):
|
||||
"""Test media_player entity test play_media, not music."""
|
||||
|
||||
camera[0].__fields__["play_audio"] = Mock()
|
||||
camera[0].__fields__["wait_until_audio_completes"] = Mock()
|
||||
camera[0].play_audio = AsyncMock(side_effect=StreamError)
|
||||
camera[0].wait_until_audio_completes = AsyncMock()
|
||||
await init_entry(hass, ufp, [doorbell, unadopted_camera])
|
||||
assert_entity_counts(hass, Platform.MEDIA_PLAYER, 1, 1)
|
||||
|
||||
doorbell.__fields__["play_audio"] = Mock()
|
||||
doorbell.__fields__["wait_until_audio_completes"] = Mock()
|
||||
doorbell.play_audio = AsyncMock(side_effect=StreamError)
|
||||
doorbell.wait_until_audio_completes = AsyncMock()
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
"media_player",
|
||||
"play_media",
|
||||
{
|
||||
ATTR_ENTITY_ID: camera[1],
|
||||
ATTR_ENTITY_ID: "media_player.test_camera_speaker",
|
||||
"media_content_id": "/test.mp3",
|
||||
"media_content_type": "music",
|
||||
},
|
||||
blocking=True,
|
||||
)
|
||||
|
||||
assert camera[0].play_audio.called
|
||||
assert not camera[0].wait_until_audio_completes.called
|
||||
assert doorbell.play_audio.called
|
||||
assert not doorbell.wait_until_audio_completes.called
|
||||
|
|
|
@ -5,7 +5,6 @@ from __future__ import annotations
|
|||
from unittest.mock import AsyncMock
|
||||
|
||||
from pyunifiprotect.data import Light
|
||||
from pyunifiprotect.data.bootstrap import ProtectDeviceRef
|
||||
from pyunifiprotect.exceptions import NvrError
|
||||
|
||||
from homeassistant.components.unifiprotect.const import DOMAIN
|
||||
|
@ -14,56 +13,47 @@ from homeassistant.const import Platform
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import MockEntityFixture, generate_random_ids, regenerate_device_ids
|
||||
from .utils import (
|
||||
MockUFPFixture,
|
||||
generate_random_ids,
|
||||
init_entry,
|
||||
regenerate_device_ids,
|
||||
)
|
||||
|
||||
|
||||
async def test_migrate_reboot_button(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
|
||||
):
|
||||
"""Test migrating unique ID of reboot button."""
|
||||
|
||||
light1 = mock_light.copy()
|
||||
light1._api = mock_entry.api
|
||||
light1 = light.copy()
|
||||
light1.name = "Test Light 1"
|
||||
regenerate_device_ids(light1)
|
||||
|
||||
light2 = mock_light.copy()
|
||||
light2._api = mock_entry.api
|
||||
light2 = light.copy()
|
||||
light2.name = "Test Light 2"
|
||||
regenerate_device_ids(light2)
|
||||
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light1.id: light1,
|
||||
light2.id: light2,
|
||||
}
|
||||
mock_entry.api.bootstrap.id_lookup = {
|
||||
light1.id: ProtectDeviceRef(id=light1.id, model=light1.model),
|
||||
light2.id: ProtectDeviceRef(id=light2.id, model=light2.model),
|
||||
}
|
||||
mock_entry.api.get_bootstrap = AsyncMock(return_value=mock_entry.api.bootstrap)
|
||||
|
||||
registry = er.async_get(hass)
|
||||
registry.async_get_or_create(
|
||||
Platform.BUTTON, DOMAIN, light1.id, config_entry=mock_entry.entry
|
||||
Platform.BUTTON, DOMAIN, light1.id, config_entry=ufp.entry
|
||||
)
|
||||
registry.async_get_or_create(
|
||||
Platform.BUTTON,
|
||||
DOMAIN,
|
||||
f"{light2.mac}_reboot",
|
||||
config_entry=mock_entry.entry,
|
||||
config_entry=ufp.entry,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
ufp.api.get_bootstrap = AsyncMock(return_value=ufp.api.bootstrap)
|
||||
await init_entry(hass, ufp, [light1, light2], regenerate_ids=False)
|
||||
|
||||
assert mock_entry.entry.state == ConfigEntryState.LOADED
|
||||
assert mock_entry.api.update.called
|
||||
assert mock_entry.entry.unique_id == mock_entry.api.bootstrap.nvr.mac
|
||||
assert ufp.entry.state == ConfigEntryState.LOADED
|
||||
assert ufp.api.update.called
|
||||
assert ufp.entry.unique_id == ufp.api.bootstrap.nvr.mac
|
||||
|
||||
buttons = []
|
||||
for entity in er.async_entries_for_config_entry(
|
||||
registry, mock_entry.entry.entry_id
|
||||
):
|
||||
for entity in er.async_entries_for_config_entry(registry, ufp.entry.entry_id):
|
||||
if entity.domain == Platform.BUTTON.value:
|
||||
buttons.append(entity)
|
||||
assert len(buttons) == 2
|
||||
|
@ -83,29 +73,33 @@ async def test_migrate_reboot_button(
|
|||
assert light.unique_id == f"{light2.mac}_reboot"
|
||||
|
||||
|
||||
async def test_migrate_nvr_mac(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light
|
||||
):
|
||||
async def test_migrate_nvr_mac(hass: HomeAssistant, ufp: MockUFPFixture, light: Light):
|
||||
"""Test migrating unique ID of NVR to use MAC address."""
|
||||
|
||||
mock_entry.api.get_bootstrap = AsyncMock(return_value=mock_entry.api.bootstrap)
|
||||
nvr = mock_entry.api.bootstrap.nvr
|
||||
regenerate_device_ids(nvr)
|
||||
light1 = light.copy()
|
||||
light1.name = "Test Light 1"
|
||||
regenerate_device_ids(light1)
|
||||
|
||||
light2 = light.copy()
|
||||
light2.name = "Test Light 2"
|
||||
regenerate_device_ids(light2)
|
||||
|
||||
nvr = ufp.api.bootstrap.nvr
|
||||
regenerate_device_ids(nvr)
|
||||
registry = er.async_get(hass)
|
||||
registry.async_get_or_create(
|
||||
Platform.SENSOR,
|
||||
DOMAIN,
|
||||
f"{nvr.id}_storage_utilization",
|
||||
config_entry=mock_entry.entry,
|
||||
config_entry=ufp.entry,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
ufp.api.get_bootstrap = AsyncMock(return_value=ufp.api.bootstrap)
|
||||
await init_entry(hass, ufp, [light1, light2], regenerate_ids=False)
|
||||
|
||||
assert mock_entry.entry.state == ConfigEntryState.LOADED
|
||||
assert mock_entry.api.update.called
|
||||
assert mock_entry.entry.unique_id == mock_entry.api.bootstrap.nvr.mac
|
||||
assert ufp.entry.state == ConfigEntryState.LOADED
|
||||
assert ufp.api.update.called
|
||||
assert ufp.entry.unique_id == ufp.api.bootstrap.nvr.mac
|
||||
|
||||
assert registry.async_get(f"{Platform.SENSOR}.{DOMAIN}_storage_utilization") is None
|
||||
assert (
|
||||
|
@ -119,171 +113,123 @@ async def test_migrate_nvr_mac(
|
|||
|
||||
|
||||
async def test_migrate_reboot_button_no_device(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
|
||||
):
|
||||
"""Test migrating unique ID of reboot button if UniFi Protect device ID changed."""
|
||||
|
||||
light1 = mock_light.copy()
|
||||
light1._api = mock_entry.api
|
||||
light1.name = "Test Light 1"
|
||||
regenerate_device_ids(light1)
|
||||
|
||||
light2_id, _ = generate_random_ids()
|
||||
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light1.id: light1,
|
||||
}
|
||||
mock_entry.api.get_bootstrap = AsyncMock(return_value=mock_entry.api.bootstrap)
|
||||
|
||||
registry = er.async_get(hass)
|
||||
registry.async_get_or_create(
|
||||
Platform.BUTTON, DOMAIN, light2_id, config_entry=mock_entry.entry
|
||||
Platform.BUTTON, DOMAIN, light2_id, config_entry=ufp.entry
|
||||
)
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
ufp.api.get_bootstrap = AsyncMock(return_value=ufp.api.bootstrap)
|
||||
await init_entry(hass, ufp, [light], regenerate_ids=False)
|
||||
|
||||
assert mock_entry.entry.state == ConfigEntryState.LOADED
|
||||
assert mock_entry.api.update.called
|
||||
assert mock_entry.entry.unique_id == mock_entry.api.bootstrap.nvr.mac
|
||||
assert ufp.entry.state == ConfigEntryState.LOADED
|
||||
assert ufp.api.update.called
|
||||
assert ufp.entry.unique_id == ufp.api.bootstrap.nvr.mac
|
||||
|
||||
buttons = []
|
||||
for entity in er.async_entries_for_config_entry(
|
||||
registry, mock_entry.entry.entry_id
|
||||
):
|
||||
for entity in er.async_entries_for_config_entry(registry, ufp.entry.entry_id):
|
||||
if entity.domain == Platform.BUTTON.value:
|
||||
buttons.append(entity)
|
||||
assert len(buttons) == 2
|
||||
|
||||
light = registry.async_get(f"{Platform.BUTTON}.unifiprotect_{light2_id.lower()}")
|
||||
assert light is not None
|
||||
assert light.unique_id == light2_id
|
||||
entity = registry.async_get(f"{Platform.BUTTON}.unifiprotect_{light2_id.lower()}")
|
||||
assert entity is not None
|
||||
assert entity.unique_id == light2_id
|
||||
|
||||
|
||||
async def test_migrate_reboot_button_fail(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
|
||||
):
|
||||
"""Test migrating unique ID of reboot button."""
|
||||
|
||||
light1 = mock_light.copy()
|
||||
light1._api = mock_entry.api
|
||||
light1.name = "Test Light 1"
|
||||
regenerate_device_ids(light1)
|
||||
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light1.id: light1,
|
||||
}
|
||||
mock_entry.api.bootstrap.id_lookup = {
|
||||
light1.id: ProtectDeviceRef(id=light1.id, model=light1.model),
|
||||
}
|
||||
mock_entry.api.get_bootstrap = AsyncMock(return_value=mock_entry.api.bootstrap)
|
||||
|
||||
registry = er.async_get(hass)
|
||||
registry.async_get_or_create(
|
||||
Platform.BUTTON,
|
||||
DOMAIN,
|
||||
light1.id,
|
||||
config_entry=mock_entry.entry,
|
||||
suggested_object_id=light1.name,
|
||||
light.id,
|
||||
config_entry=ufp.entry,
|
||||
suggested_object_id=light.display_name,
|
||||
)
|
||||
registry.async_get_or_create(
|
||||
Platform.BUTTON,
|
||||
DOMAIN,
|
||||
f"{light1.id}_reboot",
|
||||
config_entry=mock_entry.entry,
|
||||
suggested_object_id=light1.name,
|
||||
f"{light.id}_reboot",
|
||||
config_entry=ufp.entry,
|
||||
suggested_object_id=light.display_name,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
ufp.api.get_bootstrap = AsyncMock(return_value=ufp.api.bootstrap)
|
||||
await init_entry(hass, ufp, [light], regenerate_ids=False)
|
||||
|
||||
assert mock_entry.entry.state == ConfigEntryState.LOADED
|
||||
assert mock_entry.api.update.called
|
||||
assert mock_entry.entry.unique_id == mock_entry.api.bootstrap.nvr.mac
|
||||
assert ufp.entry.state == ConfigEntryState.LOADED
|
||||
assert ufp.api.update.called
|
||||
assert ufp.entry.unique_id == ufp.api.bootstrap.nvr.mac
|
||||
|
||||
light = registry.async_get(f"{Platform.BUTTON}.test_light_1")
|
||||
assert light is not None
|
||||
assert light.unique_id == f"{light1.mac}"
|
||||
entity = registry.async_get(f"{Platform.BUTTON}.test_light")
|
||||
assert entity is not None
|
||||
assert entity.unique_id == f"{light.mac}"
|
||||
|
||||
|
||||
async def test_migrate_device_mac_button_fail(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
|
||||
):
|
||||
"""Test migrating unique ID to MAC format."""
|
||||
|
||||
light1 = mock_light.copy()
|
||||
light1._api = mock_entry.api
|
||||
light1.name = "Test Light 1"
|
||||
regenerate_device_ids(light1)
|
||||
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light1.id: light1,
|
||||
}
|
||||
mock_entry.api.bootstrap.id_lookup = {
|
||||
light1.id: ProtectDeviceRef(id=light1.id, model=light1.model)
|
||||
}
|
||||
mock_entry.api.get_bootstrap = AsyncMock(return_value=mock_entry.api.bootstrap)
|
||||
|
||||
registry = er.async_get(hass)
|
||||
registry.async_get_or_create(
|
||||
Platform.BUTTON,
|
||||
DOMAIN,
|
||||
f"{light1.id}_reboot",
|
||||
config_entry=mock_entry.entry,
|
||||
suggested_object_id=light1.name,
|
||||
f"{light.id}_reboot",
|
||||
config_entry=ufp.entry,
|
||||
suggested_object_id=light.display_name,
|
||||
)
|
||||
registry.async_get_or_create(
|
||||
Platform.BUTTON,
|
||||
DOMAIN,
|
||||
f"{light1.mac}_reboot",
|
||||
config_entry=mock_entry.entry,
|
||||
suggested_object_id=light1.name,
|
||||
f"{light.mac}_reboot",
|
||||
config_entry=ufp.entry,
|
||||
suggested_object_id=light.display_name,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
ufp.api.get_bootstrap = AsyncMock(return_value=ufp.api.bootstrap)
|
||||
await init_entry(hass, ufp, [light], regenerate_ids=False)
|
||||
|
||||
assert mock_entry.entry.state == ConfigEntryState.LOADED
|
||||
assert mock_entry.api.update.called
|
||||
assert mock_entry.entry.unique_id == mock_entry.api.bootstrap.nvr.mac
|
||||
assert ufp.entry.state == ConfigEntryState.LOADED
|
||||
assert ufp.api.update.called
|
||||
assert ufp.entry.unique_id == ufp.api.bootstrap.nvr.mac
|
||||
|
||||
light = registry.async_get(f"{Platform.BUTTON}.test_light_1")
|
||||
assert light is not None
|
||||
assert light.unique_id == f"{light1.id}_reboot"
|
||||
entity = registry.async_get(f"{Platform.BUTTON}.test_light")
|
||||
assert entity is not None
|
||||
assert entity.unique_id == f"{light.id}_reboot"
|
||||
|
||||
|
||||
async def test_migrate_device_mac_bootstrap_fail(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
|
||||
):
|
||||
"""Test migrating with a network error."""
|
||||
|
||||
light1 = mock_light.copy()
|
||||
light1._api = mock_entry.api
|
||||
light1.name = "Test Light 1"
|
||||
regenerate_device_ids(light1)
|
||||
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light1.id: light1,
|
||||
}
|
||||
mock_entry.api.get_bootstrap = AsyncMock(side_effect=NvrError)
|
||||
|
||||
registry = er.async_get(hass)
|
||||
registry.async_get_or_create(
|
||||
Platform.BUTTON,
|
||||
DOMAIN,
|
||||
f"{light1.id}_reboot",
|
||||
config_entry=mock_entry.entry,
|
||||
suggested_object_id=light1.name,
|
||||
f"{light.id}_reboot",
|
||||
config_entry=ufp.entry,
|
||||
suggested_object_id=light.name,
|
||||
)
|
||||
registry.async_get_or_create(
|
||||
Platform.BUTTON,
|
||||
DOMAIN,
|
||||
f"{light1.mac}_reboot",
|
||||
config_entry=mock_entry.entry,
|
||||
suggested_object_id=light1.name,
|
||||
f"{light.mac}_reboot",
|
||||
config_entry=ufp.entry,
|
||||
suggested_object_id=light.name,
|
||||
)
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
ufp.api.get_bootstrap = AsyncMock(side_effect=NvrError)
|
||||
await init_entry(hass, ufp, [light], regenerate_ids=False)
|
||||
|
||||
assert mock_entry.entry.state == ConfigEntryState.SETUP_RETRY
|
||||
assert ufp.entry.state == ConfigEntryState.SETUP_RETRY
|
||||
|
|
|
@ -19,119 +19,23 @@ from homeassistant.const import ATTR_ATTRIBUTION, ATTR_ENTITY_ID, Platform
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import (
|
||||
MockEntityFixture,
|
||||
from .utils import (
|
||||
MockUFPFixture,
|
||||
assert_entity_counts,
|
||||
ids_from_device_description,
|
||||
reset_objects,
|
||||
init_entry,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="light")
|
||||
async def light_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light
|
||||
):
|
||||
"""Fixture for a single light for testing the number platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Light.__config__.validate_assignment = False
|
||||
|
||||
light_obj = mock_light.copy()
|
||||
light_obj._api = mock_entry.api
|
||||
light_obj.name = "Test Light"
|
||||
light_obj.light_device_settings.pir_sensitivity = 45
|
||||
light_obj.light_device_settings.pir_duration = timedelta(seconds=45)
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light_obj.id: light_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.NUMBER, 2, 2)
|
||||
|
||||
yield light_obj
|
||||
|
||||
Light.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="camera")
|
||||
async def camera_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera
|
||||
):
|
||||
"""Fixture for a single camera for testing the number platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Camera.__config__.validate_assignment = False
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.feature_flags.can_optical_zoom = True
|
||||
camera_obj.feature_flags.has_mic = True
|
||||
# has_wdr is an the inverse of has HDR
|
||||
camera_obj.feature_flags.has_hdr = False
|
||||
camera_obj.isp_settings.wdr = 0
|
||||
camera_obj.mic_volume = 0
|
||||
camera_obj.isp_settings.zoom_position = 0
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.NUMBER, 3, 3)
|
||||
|
||||
yield camera_obj
|
||||
|
||||
Camera.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="doorlock")
|
||||
async def doorlock_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_doorlock: Doorlock
|
||||
):
|
||||
"""Fixture for a single doorlock for testing the number platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Doorlock.__config__.validate_assignment = False
|
||||
|
||||
lock_obj = mock_doorlock.copy()
|
||||
lock_obj._api = mock_entry.api
|
||||
lock_obj.name = "Test Lock"
|
||||
lock_obj.auto_close_time = timedelta(seconds=45)
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.doorlocks = {
|
||||
lock_obj.id: lock_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.NUMBER, 1, 1)
|
||||
|
||||
yield lock_obj
|
||||
|
||||
Doorlock.__config__.validate_assignment = True
|
||||
|
||||
|
||||
async def test_number_setup_light(
|
||||
hass: HomeAssistant,
|
||||
light: Light,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
|
||||
):
|
||||
"""Test number entity setup for light devices."""
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
await init_entry(hass, ufp, [light])
|
||||
assert_entity_counts(hass, Platform.NUMBER, 2, 2)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
for description in LIGHT_NUMBERS:
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.NUMBER, light, description
|
||||
|
@ -148,11 +52,13 @@ async def test_number_setup_light(
|
|||
|
||||
|
||||
async def test_number_setup_camera_all(
|
||||
hass: HomeAssistant,
|
||||
camera: Camera,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: Camera
|
||||
):
|
||||
"""Test number entity setup for camera devices (all features)."""
|
||||
|
||||
await init_entry(hass, ufp, [camera])
|
||||
assert_entity_counts(hass, Platform.NUMBER, 3, 3)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
for description in CAMERA_NUMBERS:
|
||||
|
@ -171,64 +77,38 @@ async def test_number_setup_camera_all(
|
|||
|
||||
|
||||
async def test_number_setup_camera_none(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: Camera
|
||||
):
|
||||
"""Test number entity setup for camera devices (no features)."""
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.feature_flags.can_optical_zoom = False
|
||||
camera_obj.feature_flags.has_mic = False
|
||||
camera.feature_flags.can_optical_zoom = False
|
||||
camera.feature_flags.has_mic = False
|
||||
# has_wdr is an the inverse of has HDR
|
||||
camera_obj.feature_flags.has_hdr = True
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
camera.feature_flags.has_hdr = True
|
||||
|
||||
await init_entry(hass, ufp, [camera])
|
||||
assert_entity_counts(hass, Platform.NUMBER, 0, 0)
|
||||
|
||||
|
||||
async def test_number_setup_camera_missing_attr(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: Camera
|
||||
):
|
||||
"""Test number entity setup for camera devices (no features, bad attrs)."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Camera.__config__.validate_assignment = False
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.feature_flags = None
|
||||
|
||||
Camera.__config__.validate_assignment = True
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
camera.feature_flags = None
|
||||
|
||||
await init_entry(hass, ufp, [camera])
|
||||
assert_entity_counts(hass, Platform.NUMBER, 0, 0)
|
||||
|
||||
|
||||
async def test_number_light_sensitivity(hass: HomeAssistant, light: Light):
|
||||
async def test_number_light_sensitivity(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
|
||||
):
|
||||
"""Test sensitivity number entity for lights."""
|
||||
|
||||
await init_entry(hass, ufp, [light])
|
||||
assert_entity_counts(hass, Platform.NUMBER, 2, 2)
|
||||
|
||||
description = LIGHT_NUMBERS[0]
|
||||
assert description.ufp_set_method is not None
|
||||
|
||||
|
@ -244,9 +124,14 @@ async def test_number_light_sensitivity(hass: HomeAssistant, light: Light):
|
|||
light.set_sensitivity.assert_called_once_with(15.0)
|
||||
|
||||
|
||||
async def test_number_light_duration(hass: HomeAssistant, light: Light):
|
||||
async def test_number_light_duration(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
|
||||
):
|
||||
"""Test auto-shutoff duration number entity for lights."""
|
||||
|
||||
await init_entry(hass, ufp, [light])
|
||||
assert_entity_counts(hass, Platform.NUMBER, 2, 2)
|
||||
|
||||
description = LIGHT_NUMBERS[1]
|
||||
|
||||
light.__fields__["set_duration"] = Mock()
|
||||
|
@ -263,10 +148,16 @@ async def test_number_light_duration(hass: HomeAssistant, light: Light):
|
|||
|
||||
@pytest.mark.parametrize("description", CAMERA_NUMBERS)
|
||||
async def test_number_camera_simple(
|
||||
hass: HomeAssistant, camera: Camera, description: ProtectNumberEntityDescription
|
||||
hass: HomeAssistant,
|
||||
ufp: MockUFPFixture,
|
||||
camera: Camera,
|
||||
description: ProtectNumberEntityDescription,
|
||||
):
|
||||
"""Tests all simple numbers for cameras."""
|
||||
|
||||
await init_entry(hass, ufp, [camera])
|
||||
assert_entity_counts(hass, Platform.NUMBER, 3, 3)
|
||||
|
||||
assert description.ufp_set_method is not None
|
||||
|
||||
camera.__fields__[description.ufp_set_method] = Mock()
|
||||
|
@ -282,9 +173,14 @@ async def test_number_camera_simple(
|
|||
set_method.assert_called_once_with(1.0)
|
||||
|
||||
|
||||
async def test_number_lock_auto_close(hass: HomeAssistant, doorlock: Doorlock):
|
||||
async def test_number_lock_auto_close(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorlock: Doorlock
|
||||
):
|
||||
"""Test auto-lock timeout for locks."""
|
||||
|
||||
await init_entry(hass, ufp, [doorlock])
|
||||
assert_entity_counts(hass, Platform.NUMBER, 1, 1)
|
||||
|
||||
description = DOORLOCK_NUMBERS[0]
|
||||
|
||||
doorlock.__fields__["set_auto_close_time"] = Mock()
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from copy import copy
|
||||
from datetime import timedelta
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import AsyncMock, Mock, patch
|
||||
|
||||
import pytest
|
||||
|
@ -38,162 +38,24 @@ from homeassistant.const import ATTR_ATTRIBUTION, ATTR_ENTITY_ID, ATTR_OPTION, P
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from .conftest import (
|
||||
MockEntityFixture,
|
||||
from .utils import (
|
||||
MockUFPFixture,
|
||||
assert_entity_counts,
|
||||
ids_from_device_description,
|
||||
reset_objects,
|
||||
init_entry,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(name="viewer")
|
||||
async def viewer_fixture(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_viewer: Viewer,
|
||||
mock_liveview: Liveview,
|
||||
):
|
||||
"""Fixture for a single viewport for testing the select platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Viewer.__config__.validate_assignment = False
|
||||
|
||||
viewer_obj = mock_viewer.copy()
|
||||
viewer_obj._api = mock_entry.api
|
||||
viewer_obj.name = "Test Viewer"
|
||||
viewer_obj.liveview_id = mock_liveview.id
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.viewers = {
|
||||
viewer_obj.id: viewer_obj,
|
||||
}
|
||||
mock_entry.api.bootstrap.liveviews = {mock_liveview.id: mock_liveview}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.SELECT, 1, 1)
|
||||
|
||||
yield viewer_obj
|
||||
|
||||
Viewer.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="camera")
|
||||
async def camera_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera
|
||||
):
|
||||
"""Fixture for a single camera for testing the select platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Camera.__config__.validate_assignment = False
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.feature_flags.has_lcd_screen = True
|
||||
camera_obj.feature_flags.has_chime = True
|
||||
camera_obj.recording_settings.mode = RecordingMode.ALWAYS
|
||||
camera_obj.isp_settings.ir_led_mode = IRLEDMode.AUTO
|
||||
camera_obj.lcd_message = None
|
||||
camera_obj.chime_duration = 0
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
yield camera_obj
|
||||
|
||||
Camera.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="light")
|
||||
async def light_fixture(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_light: Light,
|
||||
camera: Camera,
|
||||
):
|
||||
"""Fixture for a single light for testing the select platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Light.__config__.validate_assignment = False
|
||||
|
||||
light_obj = mock_light.copy()
|
||||
light_obj._api = mock_entry.api
|
||||
light_obj.name = "Test Light"
|
||||
light_obj.camera_id = None
|
||||
light_obj.light_mode_settings.mode = LightModeType.MOTION
|
||||
light_obj.light_mode_settings.enable_at = LightModeEnableType.DARK
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {camera.id: camera}
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light_obj.id: light_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_reload(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.SELECT, 6, 6)
|
||||
|
||||
yield light_obj
|
||||
|
||||
Light.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="camera_none")
|
||||
async def camera_none_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera
|
||||
):
|
||||
"""Fixture for a single camera for testing the select platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Camera.__config__.validate_assignment = False
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.feature_flags.has_lcd_screen = False
|
||||
camera_obj.feature_flags.has_chime = False
|
||||
camera_obj.recording_settings.mode = RecordingMode.ALWAYS
|
||||
camera_obj.isp_settings.ir_led_mode = IRLEDMode.AUTO
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.SELECT, 2, 2)
|
||||
|
||||
yield camera_obj
|
||||
|
||||
Camera.__config__.validate_assignment = True
|
||||
|
||||
|
||||
async def test_select_setup_light(
|
||||
hass: HomeAssistant,
|
||||
light: Light,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
|
||||
):
|
||||
"""Test select entity setup for light devices."""
|
||||
|
||||
light.light_mode_settings.enable_at = LightModeEnableType.DARK
|
||||
await init_entry(hass, ufp, [light])
|
||||
assert_entity_counts(hass, Platform.SELECT, 2, 2)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
expected_values = ("On Motion - When Dark", "Not Paired")
|
||||
|
||||
|
@ -213,11 +75,14 @@ async def test_select_setup_light(
|
|||
|
||||
|
||||
async def test_select_setup_viewer(
|
||||
hass: HomeAssistant,
|
||||
viewer: Viewer,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, viewer: Viewer, liveview: Liveview
|
||||
):
|
||||
"""Test select entity setup for light devices."""
|
||||
|
||||
ufp.api.bootstrap.liveviews = {liveview.id: liveview}
|
||||
await init_entry(hass, ufp, [viewer])
|
||||
assert_entity_counts(hass, Platform.SELECT, 1, 1)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
description = VIEWER_SELECTS[0]
|
||||
|
||||
|
@ -236,15 +101,46 @@ async def test_select_setup_viewer(
|
|||
|
||||
|
||||
async def test_select_setup_camera_all(
|
||||
hass: HomeAssistant,
|
||||
camera: Camera,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Test select entity setup for camera devices (all features)."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
expected_values = ("Always", "Auto", "Default Message (Welcome)", "None")
|
||||
|
||||
for index, description in enumerate(CAMERA_SELECTS):
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, doorbell, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
assert entity
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == expected_values[index]
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_select_setup_camera_none(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, camera: Camera
|
||||
):
|
||||
"""Test select entity setup for camera devices (no features)."""
|
||||
|
||||
await init_entry(hass, ufp, [camera])
|
||||
assert_entity_counts(hass, Platform.SELECT, 2, 2)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
expected_values = ("Always", "Auto", "Default Message (Welcome)")
|
||||
|
||||
for index, description in enumerate(CAMERA_SELECTS):
|
||||
if index == 2:
|
||||
return
|
||||
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, camera, description
|
||||
)
|
||||
|
@ -259,41 +155,15 @@ async def test_select_setup_camera_all(
|
|||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_select_setup_camera_none(
|
||||
hass: HomeAssistant,
|
||||
camera_none: Camera,
|
||||
):
|
||||
"""Test select entity setup for camera devices (no features)."""
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
expected_values = ("Always", "Auto", "Default Message (Welcome)")
|
||||
|
||||
for index, description in enumerate(CAMERA_SELECTS):
|
||||
if index == 2:
|
||||
return
|
||||
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, camera_none, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
assert entity
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == expected_values[index]
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_select_update_liveview(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
viewer: Viewer,
|
||||
mock_liveview: Liveview,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, viewer: Viewer, liveview: Liveview
|
||||
):
|
||||
"""Test select entity update (new Liveview)."""
|
||||
|
||||
ufp.api.bootstrap.liveviews = {liveview.id: liveview}
|
||||
await init_entry(hass, ufp, [viewer])
|
||||
assert_entity_counts(hass, Platform.SELECT, 1, 1)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, viewer, VIEWER_SELECTS[0]
|
||||
)
|
||||
|
@ -302,17 +172,18 @@ async def test_select_update_liveview(
|
|||
assert state
|
||||
expected_options = state.attributes[ATTR_OPTIONS]
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_liveview = copy(mock_liveview)
|
||||
new_liveview = copy(liveview)
|
||||
new_liveview.id = "test_id"
|
||||
|
||||
mock_msg = Mock()
|
||||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_liveview
|
||||
|
||||
new_bootstrap.liveviews = {**new_bootstrap.liveviews, new_liveview.id: new_liveview}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.liveviews = {
|
||||
**ufp.api.bootstrap.liveviews,
|
||||
new_liveview.id: new_liveview,
|
||||
}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
|
@ -321,16 +192,17 @@ async def test_select_update_liveview(
|
|||
|
||||
|
||||
async def test_select_update_doorbell_settings(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, camera: Camera
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Test select entity update (new Doorbell Message)."""
|
||||
|
||||
expected_length = (
|
||||
len(mock_entry.api.bootstrap.nvr.doorbell_settings.all_messages) + 1
|
||||
)
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
expected_length = len(ufp.api.bootstrap.nvr.doorbell_settings.all_messages) + 1
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, camera, CAMERA_SELECTS[2]
|
||||
Platform.SELECT, doorbell, CAMERA_SELECTS[2]
|
||||
)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
|
@ -338,7 +210,7 @@ async def test_select_update_doorbell_settings(
|
|||
assert len(state.attributes[ATTR_OPTIONS]) == expected_length
|
||||
|
||||
expected_length += 1
|
||||
new_nvr = copy(mock_entry.api.bootstrap.nvr)
|
||||
new_nvr = copy(ufp.api.bootstrap.nvr)
|
||||
new_nvr.__fields__["update_all_messages"] = Mock()
|
||||
new_nvr.update_all_messages = Mock()
|
||||
|
||||
|
@ -354,8 +226,8 @@ async def test_select_update_doorbell_settings(
|
|||
mock_msg.changed_data = {"doorbell_settings": {}}
|
||||
mock_msg.new_obj = new_nvr
|
||||
|
||||
mock_entry.api.bootstrap.nvr = new_nvr
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.nvr = new_nvr
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
new_nvr.update_all_messages.assert_called_once()
|
||||
|
@ -366,22 +238,22 @@ async def test_select_update_doorbell_settings(
|
|||
|
||||
|
||||
async def test_select_update_doorbell_message(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: Camera,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Test select entity update (change doorbell message)."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, camera, CAMERA_SELECTS[2]
|
||||
Platform.SELECT, doorbell, CAMERA_SELECTS[2]
|
||||
)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == "Default Message (Welcome)"
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_camera = camera.copy()
|
||||
new_camera = doorbell.copy()
|
||||
new_camera.lcd_message = LCDMessage(
|
||||
type=DoorbellMessageType.CUSTOM_MESSAGE, text="Test"
|
||||
)
|
||||
|
@ -390,9 +262,8 @@ async def test_select_update_doorbell_message(
|
|||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = new_camera
|
||||
|
||||
new_bootstrap.cameras = {new_camera.id: new_camera}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
|
@ -401,10 +272,13 @@ async def test_select_update_doorbell_message(
|
|||
|
||||
|
||||
async def test_select_set_option_light_motion(
|
||||
hass: HomeAssistant,
|
||||
light: Light,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
|
||||
):
|
||||
"""Test Light Mode select."""
|
||||
|
||||
await init_entry(hass, ufp, [light])
|
||||
assert_entity_counts(hass, Platform.SELECT, 2, 2)
|
||||
|
||||
_, entity_id = ids_from_device_description(Platform.SELECT, light, LIGHT_SELECTS[0])
|
||||
|
||||
light.__fields__["set_light_settings"] = Mock()
|
||||
|
@ -423,10 +297,13 @@ async def test_select_set_option_light_motion(
|
|||
|
||||
|
||||
async def test_select_set_option_light_camera(
|
||||
hass: HomeAssistant,
|
||||
light: Light,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light, camera: Camera
|
||||
):
|
||||
"""Test Paired Camera select."""
|
||||
|
||||
await init_entry(hass, ufp, [light, camera])
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
_, entity_id = ids_from_device_description(Platform.SELECT, light, LIGHT_SELECTS[1])
|
||||
|
||||
light.__fields__["set_paired_camera"] = Mock()
|
||||
|
@ -454,16 +331,19 @@ async def test_select_set_option_light_camera(
|
|||
|
||||
|
||||
async def test_select_set_option_camera_recording(
|
||||
hass: HomeAssistant,
|
||||
camera: Camera,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Test Recording Mode select."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, camera, CAMERA_SELECTS[0]
|
||||
Platform.SELECT, doorbell, CAMERA_SELECTS[0]
|
||||
)
|
||||
|
||||
camera.__fields__["set_recording_mode"] = Mock()
|
||||
camera.set_recording_mode = AsyncMock()
|
||||
doorbell.__fields__["set_recording_mode"] = Mock()
|
||||
doorbell.set_recording_mode = AsyncMock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"select",
|
||||
|
@ -472,20 +352,23 @@ async def test_select_set_option_camera_recording(
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
camera.set_recording_mode.assert_called_once_with(RecordingMode.NEVER)
|
||||
doorbell.set_recording_mode.assert_called_once_with(RecordingMode.NEVER)
|
||||
|
||||
|
||||
async def test_select_set_option_camera_ir(
|
||||
hass: HomeAssistant,
|
||||
camera: Camera,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Test Infrared Mode select."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, camera, CAMERA_SELECTS[1]
|
||||
Platform.SELECT, doorbell, CAMERA_SELECTS[1]
|
||||
)
|
||||
|
||||
camera.__fields__["set_ir_led_model"] = Mock()
|
||||
camera.set_ir_led_model = AsyncMock()
|
||||
doorbell.__fields__["set_ir_led_model"] = Mock()
|
||||
doorbell.set_ir_led_model = AsyncMock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"select",
|
||||
|
@ -494,20 +377,23 @@ async def test_select_set_option_camera_ir(
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
camera.set_ir_led_model.assert_called_once_with(IRLEDMode.ON)
|
||||
doorbell.set_ir_led_model.assert_called_once_with(IRLEDMode.ON)
|
||||
|
||||
|
||||
async def test_select_set_option_camera_doorbell_custom(
|
||||
hass: HomeAssistant,
|
||||
camera: Camera,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Test Doorbell Text select (user defined message)."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, camera, CAMERA_SELECTS[2]
|
||||
Platform.SELECT, doorbell, CAMERA_SELECTS[2]
|
||||
)
|
||||
|
||||
camera.__fields__["set_lcd_text"] = Mock()
|
||||
camera.set_lcd_text = AsyncMock()
|
||||
doorbell.__fields__["set_lcd_text"] = Mock()
|
||||
doorbell.set_lcd_text = AsyncMock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"select",
|
||||
|
@ -516,22 +402,25 @@ async def test_select_set_option_camera_doorbell_custom(
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
camera.set_lcd_text.assert_called_once_with(
|
||||
doorbell.set_lcd_text.assert_called_once_with(
|
||||
DoorbellMessageType.CUSTOM_MESSAGE, text="Test"
|
||||
)
|
||||
|
||||
|
||||
async def test_select_set_option_camera_doorbell_unifi(
|
||||
hass: HomeAssistant,
|
||||
camera: Camera,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Test Doorbell Text select (unifi message)."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, camera, CAMERA_SELECTS[2]
|
||||
Platform.SELECT, doorbell, CAMERA_SELECTS[2]
|
||||
)
|
||||
|
||||
camera.__fields__["set_lcd_text"] = Mock()
|
||||
camera.set_lcd_text = AsyncMock()
|
||||
doorbell.__fields__["set_lcd_text"] = Mock()
|
||||
doorbell.set_lcd_text = AsyncMock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"select",
|
||||
|
@ -543,7 +432,7 @@ async def test_select_set_option_camera_doorbell_unifi(
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
camera.set_lcd_text.assert_called_once_with(
|
||||
doorbell.set_lcd_text.assert_called_once_with(
|
||||
DoorbellMessageType.LEAVE_PACKAGE_AT_DOOR
|
||||
)
|
||||
|
||||
|
@ -557,20 +446,23 @@ async def test_select_set_option_camera_doorbell_unifi(
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
camera.set_lcd_text.assert_called_with(None)
|
||||
doorbell.set_lcd_text.assert_called_with(None)
|
||||
|
||||
|
||||
async def test_select_set_option_camera_doorbell_default(
|
||||
hass: HomeAssistant,
|
||||
camera: Camera,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Test Doorbell Text select (default message)."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, camera, CAMERA_SELECTS[2]
|
||||
Platform.SELECT, doorbell, CAMERA_SELECTS[2]
|
||||
)
|
||||
|
||||
camera.__fields__["set_lcd_text"] = Mock()
|
||||
camera.set_lcd_text = AsyncMock()
|
||||
doorbell.__fields__["set_lcd_text"] = Mock()
|
||||
doorbell.set_lcd_text = AsyncMock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"select",
|
||||
|
@ -582,14 +474,18 @@ async def test_select_set_option_camera_doorbell_default(
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
camera.set_lcd_text.assert_called_once_with(None)
|
||||
doorbell.set_lcd_text.assert_called_once_with(None)
|
||||
|
||||
|
||||
async def test_select_set_option_viewer(
|
||||
hass: HomeAssistant,
|
||||
viewer: Viewer,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, viewer: Viewer, liveview: Liveview
|
||||
):
|
||||
"""Test Liveview select."""
|
||||
|
||||
ufp.api.bootstrap.liveviews = {liveview.id: liveview}
|
||||
await init_entry(hass, ufp, [viewer])
|
||||
assert_entity_counts(hass, Platform.SELECT, 1, 1)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, viewer, VIEWER_SELECTS[0]
|
||||
)
|
||||
|
@ -610,16 +506,19 @@ async def test_select_set_option_viewer(
|
|||
|
||||
|
||||
async def test_select_service_doorbell_invalid(
|
||||
hass: HomeAssistant,
|
||||
camera: Camera,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Test Doorbell Text service (invalid)."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, camera, CAMERA_SELECTS[1]
|
||||
Platform.SELECT, doorbell, CAMERA_SELECTS[1]
|
||||
)
|
||||
|
||||
camera.__fields__["set_lcd_text"] = Mock()
|
||||
camera.set_lcd_text = AsyncMock()
|
||||
doorbell.__fields__["set_lcd_text"] = Mock()
|
||||
doorbell.set_lcd_text = AsyncMock()
|
||||
|
||||
with pytest.raises(HomeAssistantError):
|
||||
await hass.services.async_call(
|
||||
|
@ -629,20 +528,23 @@ async def test_select_service_doorbell_invalid(
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
assert not camera.set_lcd_text.called
|
||||
assert not doorbell.set_lcd_text.called
|
||||
|
||||
|
||||
async def test_select_service_doorbell_success(
|
||||
hass: HomeAssistant,
|
||||
camera: Camera,
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Test Doorbell Text service (success)."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, camera, CAMERA_SELECTS[2]
|
||||
Platform.SELECT, doorbell, CAMERA_SELECTS[2]
|
||||
)
|
||||
|
||||
camera.__fields__["set_lcd_text"] = Mock()
|
||||
camera.set_lcd_text = AsyncMock()
|
||||
doorbell.__fields__["set_lcd_text"] = Mock()
|
||||
doorbell.set_lcd_text = AsyncMock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"unifiprotect",
|
||||
|
@ -654,7 +556,7 @@ async def test_select_service_doorbell_success(
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
camera.set_lcd_text.assert_called_once_with(
|
||||
doorbell.set_lcd_text.assert_called_once_with(
|
||||
DoorbellMessageType.CUSTOM_MESSAGE, "Test", reset_at=None
|
||||
)
|
||||
|
||||
|
@ -663,18 +565,23 @@ async def test_select_service_doorbell_success(
|
|||
async def test_select_service_doorbell_with_reset(
|
||||
mock_now,
|
||||
hass: HomeAssistant,
|
||||
camera: Camera,
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
fixed_now: datetime,
|
||||
):
|
||||
"""Test Doorbell Text service (success with reset time)."""
|
||||
now = utcnow()
|
||||
mock_now.return_value = now
|
||||
|
||||
mock_now.return_value = fixed_now
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SELECT, camera, CAMERA_SELECTS[2]
|
||||
Platform.SELECT, doorbell, CAMERA_SELECTS[2]
|
||||
)
|
||||
|
||||
camera.__fields__["set_lcd_text"] = Mock()
|
||||
camera.set_lcd_text = AsyncMock()
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SELECT, 4, 4)
|
||||
|
||||
doorbell.__fields__["set_lcd_text"] = Mock()
|
||||
doorbell.set_lcd_text = AsyncMock()
|
||||
|
||||
await hass.services.async_call(
|
||||
"unifiprotect",
|
||||
|
@ -687,8 +594,8 @@ async def test_select_service_doorbell_with_reset(
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
camera.set_lcd_text.assert_called_once_with(
|
||||
doorbell.set_lcd_text.assert_called_once_with(
|
||||
DoorbellMessageType.CUSTOM_MESSAGE,
|
||||
"Test",
|
||||
reset_at=now + timedelta(minutes=60),
|
||||
reset_at=fixed_now + timedelta(minutes=60),
|
||||
)
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
# pylint: disable=protected-access
|
||||
from __future__ import annotations
|
||||
|
||||
from copy import copy
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import AsyncMock, Mock
|
||||
|
||||
import pytest
|
||||
from pyunifiprotect.data import (
|
||||
NVR,
|
||||
Camera,
|
||||
|
@ -15,7 +13,6 @@ from pyunifiprotect.data import (
|
|||
Sensor,
|
||||
SmartDetectObjectType,
|
||||
)
|
||||
from pyunifiprotect.data.base import WifiConnectionState, WiredConnectionState
|
||||
from pyunifiprotect.data.nvr import EventMetadata
|
||||
|
||||
from homeassistant.components.unifiprotect.const import (
|
||||
|
@ -42,11 +39,12 @@ from homeassistant.const import (
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import (
|
||||
MockEntityFixture,
|
||||
from .utils import (
|
||||
MockUFPFixture,
|
||||
assert_entity_counts,
|
||||
enable_entity,
|
||||
ids_from_device_description,
|
||||
init_entry,
|
||||
reset_objects,
|
||||
time_changed,
|
||||
)
|
||||
|
@ -55,136 +53,12 @@ CAMERA_SENSORS_WRITE = CAMERA_SENSORS[:5]
|
|||
SENSE_SENSORS_WRITE = SENSE_SENSORS[:8]
|
||||
|
||||
|
||||
@pytest.fixture(name="sensor")
|
||||
async def sensor_fixture(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_sensor: Sensor,
|
||||
now: datetime,
|
||||
):
|
||||
"""Fixture for a single sensor for testing the sensor platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Sensor.__config__.validate_assignment = False
|
||||
|
||||
sensor_obj = mock_sensor.copy()
|
||||
sensor_obj._api = mock_entry.api
|
||||
sensor_obj.name = "Test Sensor"
|
||||
sensor_obj.battery_status.percentage = 10.0
|
||||
sensor_obj.light_settings.is_enabled = True
|
||||
sensor_obj.humidity_settings.is_enabled = True
|
||||
sensor_obj.temperature_settings.is_enabled = True
|
||||
sensor_obj.alarm_settings.is_enabled = True
|
||||
sensor_obj.stats.light.value = 10.0
|
||||
sensor_obj.stats.humidity.value = 10.0
|
||||
sensor_obj.stats.temperature.value = 10.0
|
||||
sensor_obj.up_since = now
|
||||
sensor_obj.bluetooth_connection_state.signal_strength = -50.0
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.sensors = {
|
||||
sensor_obj.id: sensor_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
yield sensor_obj
|
||||
|
||||
Sensor.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="sensor_none")
|
||||
async def sensor_none_fixture(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_sensor: Sensor,
|
||||
now: datetime,
|
||||
):
|
||||
"""Fixture for a single sensor for testing the sensor platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Sensor.__config__.validate_assignment = False
|
||||
|
||||
sensor_obj = mock_sensor.copy()
|
||||
sensor_obj._api = mock_entry.api
|
||||
sensor_obj.name = "Test Sensor"
|
||||
sensor_obj.battery_status.percentage = 10.0
|
||||
sensor_obj.light_settings.is_enabled = False
|
||||
sensor_obj.humidity_settings.is_enabled = False
|
||||
sensor_obj.temperature_settings.is_enabled = False
|
||||
sensor_obj.alarm_settings.is_enabled = False
|
||||
sensor_obj.up_since = now
|
||||
sensor_obj.bluetooth_connection_state.signal_strength = -50.0
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.sensors = {
|
||||
sensor_obj.id: sensor_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# 4 from all, 5 from sense, 12 NVR
|
||||
assert_entity_counts(hass, Platform.SENSOR, 22, 14)
|
||||
|
||||
yield sensor_obj
|
||||
|
||||
Sensor.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="camera")
|
||||
async def camera_fixture(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_camera: Camera,
|
||||
now: datetime,
|
||||
):
|
||||
"""Fixture for a single camera for testing the sensor platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Camera.__config__.validate_assignment = False
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.feature_flags.has_smart_detect = True
|
||||
camera_obj.feature_flags.has_chime = True
|
||||
camera_obj.is_smart_detected = False
|
||||
camera_obj.wired_connection_state = WiredConnectionState(phy_rate=1000)
|
||||
camera_obj.wifi_connection_state = WifiConnectionState(
|
||||
signal_quality=100, signal_strength=-50
|
||||
)
|
||||
camera_obj.stats.rx_bytes = 100.0
|
||||
camera_obj.stats.tx_bytes = 100.0
|
||||
camera_obj.stats.video.recording_start = now
|
||||
camera_obj.stats.storage.used = 100.0
|
||||
camera_obj.stats.storage.used = 100.0
|
||||
camera_obj.stats.storage.rate = 0.1
|
||||
camera_obj.voltage = 20.0
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
yield camera_obj
|
||||
|
||||
Camera.__config__.validate_assignment = True
|
||||
|
||||
|
||||
async def test_sensor_setup_sensor(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, sensor: Sensor
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, sensor_all: Sensor
|
||||
):
|
||||
"""Test sensor entity setup for sensor devices."""
|
||||
# 5 from all, 5 from sense, 12 NVR
|
||||
|
||||
await init_entry(hass, ufp, [sensor_all])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 22, 14)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
@ -196,6 +70,57 @@ async def test_sensor_setup_sensor(
|
|||
"10.0",
|
||||
"none",
|
||||
)
|
||||
for index, description in enumerate(SENSE_SENSORS_WRITE):
|
||||
if not description.entity_registry_enabled_default:
|
||||
continue
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, sensor_all, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
assert entity
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == expected_values[index]
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
# BLE signal
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, sensor_all, ALL_DEVICES_SENSORS[1]
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
assert entity
|
||||
assert entity.disabled is True
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == "-50"
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_sensor_setup_sensor_none(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, sensor: Sensor
|
||||
):
|
||||
"""Test sensor entity setup for sensor devices with no sensors enabled."""
|
||||
|
||||
await init_entry(hass, ufp, [sensor])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 22, 14)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
expected_values = (
|
||||
"10",
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNAVAILABLE,
|
||||
)
|
||||
for index, description in enumerate(SENSE_SENSORS_WRITE):
|
||||
if not description.entity_registry_enabled_default:
|
||||
continue
|
||||
|
@ -212,63 +137,15 @@ async def test_sensor_setup_sensor(
|
|||
assert state.state == expected_values[index]
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
# BLE signal
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, sensor, ALL_DEVICES_SENSORS[1]
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
assert entity
|
||||
assert entity.disabled is True
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == "-50"
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_sensor_setup_sensor_none(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, sensor_none: Sensor
|
||||
):
|
||||
"""Test sensor entity setup for sensor devices with no sensors enabled."""
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
expected_values = (
|
||||
"10",
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNAVAILABLE,
|
||||
)
|
||||
for index, description in enumerate(SENSE_SENSORS_WRITE):
|
||||
if not description.entity_registry_enabled_default:
|
||||
continue
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, sensor_none, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
assert entity
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == expected_values[index]
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_sensor_setup_nvr(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, now: datetime
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, fixed_now: datetime
|
||||
):
|
||||
"""Test sensor entity setup for NVR device."""
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
nvr: NVR = mock_entry.api.bootstrap.nvr
|
||||
nvr.up_since = now
|
||||
reset_objects(ufp.api.bootstrap)
|
||||
nvr: NVR = ufp.api.bootstrap.nvr
|
||||
nvr.up_since = fixed_now
|
||||
nvr.system_info.cpu.average_load = 50.0
|
||||
nvr.system_info.cpu.temperature = 50.0
|
||||
nvr.storage_stats.utilization = 50.0
|
||||
|
@ -282,16 +159,15 @@ async def test_sensor_setup_nvr(
|
|||
nvr.storage_stats.storage_distribution.free.percentage = 50.0
|
||||
nvr.storage_stats.capacity = 50.0
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# 2 from all, 4 from sense, 12 NVR
|
||||
assert_entity_counts(hass, Platform.SENSOR, 12, 9)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
expected_values = (
|
||||
now.replace(second=0, microsecond=0).isoformat(),
|
||||
fixed_now.replace(second=0, microsecond=0).isoformat(),
|
||||
"50.0",
|
||||
"50.0",
|
||||
"50.0",
|
||||
|
@ -312,7 +188,7 @@ async def test_sensor_setup_nvr(
|
|||
assert entity.unique_id == unique_id
|
||||
|
||||
if not description.entity_registry_enabled_default:
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
@ -330,7 +206,7 @@ async def test_sensor_setup_nvr(
|
|||
assert entity.disabled is not description.entity_registry_enabled_default
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
@ -338,22 +214,19 @@ async def test_sensor_setup_nvr(
|
|||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_sensor_nvr_missing_values(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, now: datetime
|
||||
):
|
||||
async def test_sensor_nvr_missing_values(hass: HomeAssistant, ufp: MockUFPFixture):
|
||||
"""Test NVR sensor sensors if no data available."""
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
nvr: NVR = mock_entry.api.bootstrap.nvr
|
||||
reset_objects(ufp.api.bootstrap)
|
||||
nvr: NVR = ufp.api.bootstrap.nvr
|
||||
nvr.system_info.memory.available = None
|
||||
nvr.system_info.memory.total = None
|
||||
nvr.up_since = None
|
||||
nvr.storage_stats.capacity = None
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# 2 from all, 4 from sense, 12 NVR
|
||||
assert_entity_counts(hass, Platform.SENSOR, 12, 9)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
@ -368,7 +241,7 @@ async def test_sensor_nvr_missing_values(
|
|||
assert entity
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
@ -401,7 +274,7 @@ async def test_sensor_nvr_missing_values(
|
|||
assert entity.disabled is True
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
@ -410,16 +283,17 @@ async def test_sensor_nvr_missing_values(
|
|||
|
||||
|
||||
async def test_sensor_setup_camera(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, camera: Camera, now: datetime
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera, fixed_now: datetime
|
||||
):
|
||||
"""Test sensor entity setup for camera devices."""
|
||||
# 3 from all, 7 from camera, 12 NVR
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 13)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
expected_values = (
|
||||
now.replace(microsecond=0).isoformat(),
|
||||
fixed_now.replace(microsecond=0).isoformat(),
|
||||
"100",
|
||||
"100.0",
|
||||
"20.0",
|
||||
|
@ -428,7 +302,7 @@ async def test_sensor_setup_camera(
|
|||
if not description.entity_registry_enabled_default:
|
||||
continue
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, camera, description
|
||||
Platform.SENSOR, doorbell, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -444,7 +318,7 @@ async def test_sensor_setup_camera(
|
|||
expected_values = ("100", "100")
|
||||
for index, description in enumerate(CAMERA_DISABLED_SENSORS):
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, camera, description
|
||||
Platform.SENSOR, doorbell, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -452,7 +326,7 @@ async def test_sensor_setup_camera(
|
|||
assert entity.disabled is not description.entity_registry_enabled_default
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
@ -461,7 +335,7 @@ async def test_sensor_setup_camera(
|
|||
|
||||
# Wired signal
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, camera, ALL_DEVICES_SENSORS[2]
|
||||
Platform.SENSOR, doorbell, ALL_DEVICES_SENSORS[2]
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -469,7 +343,7 @@ async def test_sensor_setup_camera(
|
|||
assert entity.disabled is True
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
@ -478,7 +352,7 @@ async def test_sensor_setup_camera(
|
|||
|
||||
# WiFi signal
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, camera, ALL_DEVICES_SENSORS[3]
|
||||
Platform.SENSOR, doorbell, ALL_DEVICES_SENSORS[3]
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -486,7 +360,7 @@ async def test_sensor_setup_camera(
|
|||
assert entity.disabled is True
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
@ -495,7 +369,7 @@ async def test_sensor_setup_camera(
|
|||
|
||||
# Detected Object
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, camera, MOTION_SENSORS[0]
|
||||
Platform.SENSOR, doorbell, MOTION_SENSORS[0]
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -512,16 +386,20 @@ async def test_sensor_setup_camera(
|
|||
async def test_sensor_setup_camera_with_last_trip_time(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: AsyncMock,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: Camera,
|
||||
now: datetime,
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
fixed_now: datetime,
|
||||
):
|
||||
"""Test sensor entity setup for camera devices with last trip time."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 25)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
# Last Trip Time
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, camera, MOTION_TRIP_SENSORS[0]
|
||||
Platform.SENSOR, doorbell, MOTION_TRIP_SENSORS[0]
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
|
@ -530,35 +408,38 @@ async def test_sensor_setup_camera_with_last_trip_time(
|
|||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == "2021-12-20T17:26:53+00:00"
|
||||
assert (
|
||||
state.state
|
||||
== (fixed_now - timedelta(hours=1)).replace(microsecond=0).isoformat()
|
||||
)
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_sensor_update_motion(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, camera: Camera, now: datetime
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera, fixed_now: datetime
|
||||
):
|
||||
"""Test sensor motion entity."""
|
||||
# 3 from all, 7 from camera, 12 NVR
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 25, 13)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, camera, MOTION_SENSORS[0]
|
||||
Platform.SENSOR, doorbell, MOTION_SENSORS[0]
|
||||
)
|
||||
|
||||
event = Event(
|
||||
id="test_event_id",
|
||||
type=EventType.SMART_DETECT,
|
||||
start=now - timedelta(seconds=1),
|
||||
start=fixed_now - timedelta(seconds=1),
|
||||
end=None,
|
||||
score=100,
|
||||
smart_detect_types=[SmartDetectObjectType.PERSON],
|
||||
smart_detect_event_ids=[],
|
||||
camera_id=camera.id,
|
||||
api=mock_entry.api,
|
||||
camera_id=doorbell.id,
|
||||
api=ufp.api,
|
||||
)
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_camera = camera.copy()
|
||||
new_camera = doorbell.copy()
|
||||
new_camera.is_smart_detected = True
|
||||
new_camera.last_smart_detect_event_id = event.id
|
||||
|
||||
|
@ -566,10 +447,9 @@ async def test_sensor_update_motion(
|
|||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = event
|
||||
|
||||
new_bootstrap.cameras = {new_camera.id: new_camera}
|
||||
new_bootstrap.events = {event.id: event}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.cameras = {new_camera.id: new_camera}
|
||||
ufp.api.bootstrap.events = {event.id: event}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
|
@ -580,31 +460,31 @@ async def test_sensor_update_motion(
|
|||
|
||||
|
||||
async def test_sensor_update_alarm(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, sensor: Sensor, now: datetime
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, sensor_all: Sensor, fixed_now: datetime
|
||||
):
|
||||
"""Test sensor motion entity."""
|
||||
# 5 from all, 5 from sense, 12 NVR
|
||||
|
||||
await init_entry(hass, ufp, [sensor_all])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 22, 14)
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, sensor, SENSE_SENSORS_WRITE[4]
|
||||
Platform.SENSOR, sensor_all, SENSE_SENSORS_WRITE[4]
|
||||
)
|
||||
|
||||
event_metadata = EventMetadata(sensor_id=sensor.id, alarm_type="smoke")
|
||||
event_metadata = EventMetadata(sensor_id=sensor_all.id, alarm_type="smoke")
|
||||
event = Event(
|
||||
id="test_event_id",
|
||||
type=EventType.SENSOR_ALARM,
|
||||
start=now - timedelta(seconds=1),
|
||||
start=fixed_now - timedelta(seconds=1),
|
||||
end=None,
|
||||
score=100,
|
||||
smart_detect_types=[],
|
||||
smart_detect_event_ids=[],
|
||||
metadata=event_metadata,
|
||||
api=mock_entry.api,
|
||||
api=ufp.api,
|
||||
)
|
||||
|
||||
new_bootstrap = copy(mock_entry.api.bootstrap)
|
||||
new_sensor = sensor.copy()
|
||||
new_sensor = sensor_all.copy()
|
||||
new_sensor.set_alarm_timeout()
|
||||
new_sensor.last_alarm_event_id = event.id
|
||||
|
||||
|
@ -612,10 +492,9 @@ async def test_sensor_update_alarm(
|
|||
mock_msg.changed_data = {}
|
||||
mock_msg.new_obj = event
|
||||
|
||||
new_bootstrap.sensors = {new_sensor.id: new_sensor}
|
||||
new_bootstrap.events = {event.id: event}
|
||||
mock_entry.api.bootstrap = new_bootstrap
|
||||
mock_entry.api.ws_subscription(mock_msg)
|
||||
ufp.api.bootstrap.sensors = {new_sensor.id: new_sensor}
|
||||
ufp.api.bootstrap.events = {event.id: event}
|
||||
ufp.ws_msg(mock_msg)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
|
@ -627,15 +506,18 @@ async def test_sensor_update_alarm(
|
|||
async def test_sensor_update_alarm_with_last_trip_time(
|
||||
hass: HomeAssistant,
|
||||
entity_registry_enabled_by_default: AsyncMock,
|
||||
mock_entry: MockEntityFixture,
|
||||
sensor: Sensor,
|
||||
now: datetime,
|
||||
ufp: MockUFPFixture,
|
||||
sensor_all: Sensor,
|
||||
fixed_now: datetime,
|
||||
):
|
||||
"""Test sensor motion entity with last trip time."""
|
||||
|
||||
await init_entry(hass, ufp, [sensor_all])
|
||||
assert_entity_counts(hass, Platform.SENSOR, 22, 22)
|
||||
|
||||
# Last Trip Time
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SENSOR, sensor, SENSE_SENSORS_WRITE[-3]
|
||||
Platform.SENSOR, sensor_all, SENSE_SENSORS_WRITE[-3]
|
||||
)
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
|
@ -645,5 +527,8 @@ async def test_sensor_update_alarm_with_last_trip_time(
|
|||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == "2022-01-04T04:03:56+00:00"
|
||||
assert (
|
||||
state.state
|
||||
== (fixed_now - timedelta(hours=1)).replace(microsecond=0).isoformat()
|
||||
)
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
|
|
@ -6,7 +6,6 @@ from unittest.mock import AsyncMock, Mock
|
|||
|
||||
import pytest
|
||||
from pyunifiprotect.data import Camera, Chime, Light, ModelType
|
||||
from pyunifiprotect.data.bootstrap import ProtectDeviceRef
|
||||
from pyunifiprotect.exceptions import BadRequest
|
||||
|
||||
from homeassistant.components.unifiprotect.const import ATTR_MESSAGE, DOMAIN
|
||||
|
@ -21,15 +20,14 @@ from homeassistant.core import HomeAssistant
|
|||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
|
||||
from .conftest import MockEntityFixture, regenerate_device_ids
|
||||
from .utils import MockUFPFixture, init_entry
|
||||
|
||||
|
||||
@pytest.fixture(name="device")
|
||||
async def device_fixture(hass: HomeAssistant, mock_entry: MockEntityFixture):
|
||||
async def device_fixture(hass: HomeAssistant, ufp: MockUFPFixture):
|
||||
"""Fixture with entry setup to call services with."""
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
await init_entry(hass, ufp, [])
|
||||
|
||||
device_registry = dr.async_get(hass)
|
||||
|
||||
|
@ -37,30 +35,20 @@ async def device_fixture(hass: HomeAssistant, mock_entry: MockEntityFixture):
|
|||
|
||||
|
||||
@pytest.fixture(name="subdevice")
|
||||
async def subdevice_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light
|
||||
):
|
||||
async def subdevice_fixture(hass: HomeAssistant, ufp: MockUFPFixture, light: Light):
|
||||
"""Fixture with entry setup to call services with."""
|
||||
|
||||
mock_light._api = mock_entry.api
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
mock_light.id: mock_light,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
await init_entry(hass, ufp, [light])
|
||||
|
||||
device_registry = dr.async_get(hass)
|
||||
|
||||
return [d for d in device_registry.devices.values() if d.name != "UnifiProtect"][0]
|
||||
|
||||
|
||||
async def test_global_service_bad_device(
|
||||
hass: HomeAssistant, device: dr.DeviceEntry, mock_entry: MockEntityFixture
|
||||
):
|
||||
async def test_global_service_bad_device(hass: HomeAssistant, ufp: MockUFPFixture):
|
||||
"""Test global service, invalid device ID."""
|
||||
|
||||
nvr = mock_entry.api.bootstrap.nvr
|
||||
nvr = ufp.api.bootstrap.nvr
|
||||
nvr.__fields__["add_custom_doorbell_message"] = Mock()
|
||||
nvr.add_custom_doorbell_message = AsyncMock()
|
||||
|
||||
|
@ -75,11 +63,11 @@ async def test_global_service_bad_device(
|
|||
|
||||
|
||||
async def test_global_service_exception(
|
||||
hass: HomeAssistant, device: dr.DeviceEntry, mock_entry: MockEntityFixture
|
||||
hass: HomeAssistant, device: dr.DeviceEntry, ufp: MockUFPFixture
|
||||
):
|
||||
"""Test global service, unexpected error."""
|
||||
|
||||
nvr = mock_entry.api.bootstrap.nvr
|
||||
nvr = ufp.api.bootstrap.nvr
|
||||
nvr.__fields__["add_custom_doorbell_message"] = Mock()
|
||||
nvr.add_custom_doorbell_message = AsyncMock(side_effect=BadRequest)
|
||||
|
||||
|
@ -94,11 +82,11 @@ async def test_global_service_exception(
|
|||
|
||||
|
||||
async def test_add_doorbell_text(
|
||||
hass: HomeAssistant, device: dr.DeviceEntry, mock_entry: MockEntityFixture
|
||||
hass: HomeAssistant, device: dr.DeviceEntry, ufp: MockUFPFixture
|
||||
):
|
||||
"""Test add_doorbell_text service."""
|
||||
|
||||
nvr = mock_entry.api.bootstrap.nvr
|
||||
nvr = ufp.api.bootstrap.nvr
|
||||
nvr.__fields__["add_custom_doorbell_message"] = Mock()
|
||||
nvr.add_custom_doorbell_message = AsyncMock()
|
||||
|
||||
|
@ -112,11 +100,11 @@ async def test_add_doorbell_text(
|
|||
|
||||
|
||||
async def test_remove_doorbell_text(
|
||||
hass: HomeAssistant, subdevice: dr.DeviceEntry, mock_entry: MockEntityFixture
|
||||
hass: HomeAssistant, subdevice: dr.DeviceEntry, ufp: MockUFPFixture
|
||||
):
|
||||
"""Test remove_doorbell_text service."""
|
||||
|
||||
nvr = mock_entry.api.bootstrap.nvr
|
||||
nvr = ufp.api.bootstrap.nvr
|
||||
nvr.__fields__["remove_custom_doorbell_message"] = Mock()
|
||||
nvr.remove_custom_doorbell_message = AsyncMock()
|
||||
|
||||
|
@ -130,11 +118,11 @@ async def test_remove_doorbell_text(
|
|||
|
||||
|
||||
async def test_set_default_doorbell_text(
|
||||
hass: HomeAssistant, device: dr.DeviceEntry, mock_entry: MockEntityFixture
|
||||
hass: HomeAssistant, device: dr.DeviceEntry, ufp: MockUFPFixture
|
||||
):
|
||||
"""Test set_default_doorbell_text service."""
|
||||
|
||||
nvr = mock_entry.api.bootstrap.nvr
|
||||
nvr = ufp.api.bootstrap.nvr
|
||||
nvr.__fields__["set_default_doorbell_message"] = Mock()
|
||||
nvr.set_default_doorbell_message = AsyncMock()
|
||||
|
||||
|
@ -149,57 +137,21 @@ async def test_set_default_doorbell_text(
|
|||
|
||||
async def test_set_chime_paired_doorbells(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_chime: Chime,
|
||||
mock_camera: Camera,
|
||||
ufp: MockUFPFixture,
|
||||
chime: Chime,
|
||||
doorbell: Camera,
|
||||
):
|
||||
"""Test set_chime_paired_doorbells."""
|
||||
|
||||
mock_entry.api.update_device = AsyncMock()
|
||||
ufp.api.update_device = AsyncMock()
|
||||
|
||||
mock_chime._api = mock_entry.api
|
||||
mock_chime.name = "Test Chime"
|
||||
mock_chime._initial_data = mock_chime.dict()
|
||||
mock_entry.api.bootstrap.chimes = {
|
||||
mock_chime.id: mock_chime,
|
||||
}
|
||||
mock_entry.api.bootstrap.mac_lookup = {
|
||||
mock_chime.mac.lower(): ProtectDeviceRef(
|
||||
model=mock_chime.model, id=mock_chime.id
|
||||
)
|
||||
}
|
||||
|
||||
camera1 = mock_camera.copy()
|
||||
camera1 = doorbell.copy()
|
||||
camera1.name = "Test Camera 1"
|
||||
camera1._api = mock_entry.api
|
||||
camera1.channels[0]._api = mock_entry.api
|
||||
camera1.channels[1]._api = mock_entry.api
|
||||
camera1.channels[2]._api = mock_entry.api
|
||||
camera1.feature_flags.has_chime = True
|
||||
regenerate_device_ids(camera1)
|
||||
|
||||
camera2 = mock_camera.copy()
|
||||
camera2 = doorbell.copy()
|
||||
camera2.name = "Test Camera 2"
|
||||
camera2._api = mock_entry.api
|
||||
camera2.channels[0]._api = mock_entry.api
|
||||
camera2.channels[1]._api = mock_entry.api
|
||||
camera2.channels[2]._api = mock_entry.api
|
||||
camera2.feature_flags.has_chime = True
|
||||
regenerate_device_ids(camera2)
|
||||
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera1.id: camera1,
|
||||
camera2.id: camera2,
|
||||
}
|
||||
mock_entry.api.bootstrap.mac_lookup[camera1.mac.lower()] = ProtectDeviceRef(
|
||||
model=camera1.model, id=camera1.id
|
||||
)
|
||||
mock_entry.api.bootstrap.mac_lookup[camera2.mac.lower()] = ProtectDeviceRef(
|
||||
model=camera2.model, id=camera2.id
|
||||
)
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
await init_entry(hass, ufp, [camera1, camera2, chime])
|
||||
|
||||
registry = er.async_get(hass)
|
||||
chime_entry = registry.async_get("button.test_chime_play_chime")
|
||||
|
@ -220,6 +172,6 @@ async def test_set_chime_paired_doorbells(
|
|||
blocking=True,
|
||||
)
|
||||
|
||||
mock_entry.api.update_device.assert_called_once_with(
|
||||
ModelType.CHIME, mock_chime.id, {"cameraIds": sorted([camera1.id, camera2.id])}
|
||||
ufp.api.update_device.assert_called_once_with(
|
||||
ModelType.CHIME, chime.id, {"cameraIds": sorted([camera1.id, camera2.id])}
|
||||
)
|
||||
|
|
|
@ -5,14 +5,7 @@ from __future__ import annotations
|
|||
from unittest.mock import AsyncMock, Mock
|
||||
|
||||
import pytest
|
||||
from pyunifiprotect.data import (
|
||||
Camera,
|
||||
Light,
|
||||
Permission,
|
||||
RecordingMode,
|
||||
SmartDetectObjectType,
|
||||
VideoMode,
|
||||
)
|
||||
from pyunifiprotect.data import Camera, Light, Permission, RecordingMode, VideoMode
|
||||
|
||||
from homeassistant.components.unifiprotect.const import DEFAULT_ATTRIBUTION
|
||||
from homeassistant.components.unifiprotect.switch import (
|
||||
|
@ -24,12 +17,12 @@ from homeassistant.const import ATTR_ATTRIBUTION, ATTR_ENTITY_ID, STATE_OFF, Pla
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import (
|
||||
MockEntityFixture,
|
||||
from .utils import (
|
||||
MockUFPFixture,
|
||||
assert_entity_counts,
|
||||
enable_entity,
|
||||
ids_from_device_description,
|
||||
reset_objects,
|
||||
init_entry,
|
||||
)
|
||||
|
||||
CAMERA_SWITCHES_BASIC = [
|
||||
|
@ -44,218 +37,33 @@ CAMERA_SWITCHES_NO_EXTRA = [
|
|||
]
|
||||
|
||||
|
||||
@pytest.fixture(name="light")
|
||||
async def light_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_light: Light
|
||||
):
|
||||
"""Fixture for a single light for testing the switch platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Light.__config__.validate_assignment = False
|
||||
|
||||
light_obj = mock_light.copy()
|
||||
light_obj._api = mock_entry.api
|
||||
light_obj.name = "Test Light"
|
||||
light_obj.is_ssh_enabled = False
|
||||
light_obj.light_device_settings.is_indicator_enabled = False
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light_obj.id: light_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.SWITCH, 2, 1)
|
||||
|
||||
yield light_obj
|
||||
|
||||
Light.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="camera")
|
||||
async def camera_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera
|
||||
):
|
||||
"""Fixture for a single camera for testing the switch platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Camera.__config__.validate_assignment = False
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.recording_settings.mode = RecordingMode.DETECTIONS
|
||||
camera_obj.feature_flags.has_led_status = True
|
||||
camera_obj.feature_flags.has_hdr = True
|
||||
camera_obj.feature_flags.video_modes = [VideoMode.DEFAULT, VideoMode.HIGH_FPS]
|
||||
camera_obj.feature_flags.has_privacy_mask = True
|
||||
camera_obj.feature_flags.has_speaker = True
|
||||
camera_obj.feature_flags.has_smart_detect = True
|
||||
camera_obj.feature_flags.smart_detect_types = [
|
||||
SmartDetectObjectType.PERSON,
|
||||
SmartDetectObjectType.VEHICLE,
|
||||
]
|
||||
camera_obj.is_ssh_enabled = False
|
||||
camera_obj.led_settings.is_enabled = False
|
||||
camera_obj.hdr_mode = False
|
||||
camera_obj.video_mode = VideoMode.DEFAULT
|
||||
camera_obj.remove_privacy_zone()
|
||||
camera_obj.speaker_settings.are_system_sounds_enabled = False
|
||||
camera_obj.osd_settings.is_name_enabled = False
|
||||
camera_obj.osd_settings.is_date_enabled = False
|
||||
camera_obj.osd_settings.is_logo_enabled = False
|
||||
camera_obj.osd_settings.is_debug_enabled = False
|
||||
camera_obj.smart_detect_settings.object_types = []
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.SWITCH, 13, 12)
|
||||
|
||||
yield camera_obj
|
||||
|
||||
Camera.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="camera_none")
|
||||
async def camera_none_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera
|
||||
):
|
||||
"""Fixture for a single camera for testing the switch platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Camera.__config__.validate_assignment = False
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.recording_settings.mode = RecordingMode.DETECTIONS
|
||||
camera_obj.feature_flags.has_led_status = False
|
||||
camera_obj.feature_flags.has_hdr = False
|
||||
camera_obj.feature_flags.video_modes = [VideoMode.DEFAULT]
|
||||
camera_obj.feature_flags.has_privacy_mask = False
|
||||
camera_obj.feature_flags.has_speaker = False
|
||||
camera_obj.feature_flags.has_smart_detect = False
|
||||
camera_obj.is_ssh_enabled = False
|
||||
camera_obj.osd_settings.is_name_enabled = False
|
||||
camera_obj.osd_settings.is_date_enabled = False
|
||||
camera_obj.osd_settings.is_logo_enabled = False
|
||||
camera_obj.osd_settings.is_debug_enabled = False
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
yield camera_obj
|
||||
|
||||
Camera.__config__.validate_assignment = True
|
||||
|
||||
|
||||
@pytest.fixture(name="camera_privacy")
|
||||
async def camera_privacy_fixture(
|
||||
hass: HomeAssistant, mock_entry: MockEntityFixture, mock_camera: Camera
|
||||
):
|
||||
"""Fixture for a single camera for testing the switch platform."""
|
||||
|
||||
# disable pydantic validation so mocking can happen
|
||||
Camera.__config__.validate_assignment = False
|
||||
|
||||
# mock_camera._update_lock = None
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
camera_obj.name = "Test Camera"
|
||||
camera_obj.recording_settings.mode = RecordingMode.NEVER
|
||||
camera_obj.feature_flags.has_led_status = False
|
||||
camera_obj.feature_flags.has_hdr = False
|
||||
camera_obj.feature_flags.video_modes = [VideoMode.DEFAULT]
|
||||
camera_obj.feature_flags.has_privacy_mask = True
|
||||
camera_obj.feature_flags.has_speaker = False
|
||||
camera_obj.feature_flags.has_smart_detect = False
|
||||
camera_obj.add_privacy_zone()
|
||||
camera_obj.is_ssh_enabled = False
|
||||
camera_obj.osd_settings.is_name_enabled = False
|
||||
camera_obj.osd_settings.is_date_enabled = False
|
||||
camera_obj.osd_settings.is_logo_enabled = False
|
||||
camera_obj.osd_settings.is_debug_enabled = False
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert_entity_counts(hass, Platform.SWITCH, 7, 6)
|
||||
|
||||
yield camera_obj
|
||||
|
||||
Camera.__config__.validate_assignment = True
|
||||
|
||||
|
||||
async def test_switch_setup_no_perm(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_light: Light,
|
||||
mock_camera: Camera,
|
||||
ufp: MockUFPFixture,
|
||||
light: Light,
|
||||
doorbell: Camera,
|
||||
):
|
||||
"""Test switch entity setup for light devices."""
|
||||
|
||||
light_obj = mock_light.copy()
|
||||
light_obj._api = mock_entry.api
|
||||
|
||||
camera_obj = mock_camera.copy()
|
||||
camera_obj._api = mock_entry.api
|
||||
camera_obj.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
camera_obj.channels[2]._api = mock_entry.api
|
||||
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light_obj.id: light_obj,
|
||||
}
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
mock_entry.api.bootstrap.auth_user.all_permissions = [
|
||||
ufp.api.bootstrap.auth_user.all_permissions = [
|
||||
Permission.unifi_dict_to_dict({"rawPermission": "light:read:*"})
|
||||
]
|
||||
|
||||
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
await init_entry(hass, ufp, [light, doorbell])
|
||||
|
||||
assert_entity_counts(hass, Platform.SWITCH, 0, 0)
|
||||
|
||||
|
||||
async def test_switch_setup_light(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
ufp: MockUFPFixture,
|
||||
light: Light,
|
||||
):
|
||||
"""Test switch entity setup for light devices."""
|
||||
|
||||
await init_entry(hass, ufp, [light])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 2, 1)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
description = LIGHT_SWITCHES[1]
|
||||
|
@ -283,7 +91,7 @@ async def test_switch_setup_light(
|
|||
assert entity.disabled is True
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
@ -293,14 +101,67 @@ async def test_switch_setup_light(
|
|||
|
||||
async def test_switch_setup_camera_all(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera: Camera,
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
):
|
||||
"""Test switch entity setup for camera devices (all enabled feature flags)."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 13, 12)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
for description in CAMERA_SWITCHES_BASIC:
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SWITCH, doorbell, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
assert entity
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_OFF
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
description = CAMERA_SWITCHES[0]
|
||||
|
||||
description_entity_name = (
|
||||
description.name.lower().replace(":", "").replace(" ", "_")
|
||||
)
|
||||
unique_id = f"{doorbell.mac}_{description.key}"
|
||||
entity_id = f"switch.test_camera_{description_entity_name}"
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
assert entity
|
||||
assert entity.disabled is True
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_OFF
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_switch_setup_camera_none(
|
||||
hass: HomeAssistant,
|
||||
ufp: MockUFPFixture,
|
||||
camera: Camera,
|
||||
):
|
||||
"""Test switch entity setup for camera devices (no enabled feature flags)."""
|
||||
|
||||
await init_entry(hass, ufp, [camera])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 6, 5)
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
for description in CAMERA_SWITCHES_BASIC:
|
||||
if description.ufp_required_field is not None:
|
||||
continue
|
||||
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SWITCH, camera, description
|
||||
)
|
||||
|
@ -327,7 +188,7 @@ async def test_switch_setup_camera_all(
|
|||
assert entity.disabled is True
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
|
@ -335,56 +196,14 @@ async def test_switch_setup_camera_all(
|
|||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_switch_setup_camera_none(
|
||||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
camera_none: Camera,
|
||||
async def test_switch_light_status(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, light: Light
|
||||
):
|
||||
"""Test switch entity setup for camera devices (no enabled feature flags)."""
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
for description in CAMERA_SWITCHES_BASIC:
|
||||
if description.ufp_required_field is not None:
|
||||
continue
|
||||
|
||||
unique_id, entity_id = ids_from_device_description(
|
||||
Platform.SWITCH, camera_none, description
|
||||
)
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
assert entity
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_OFF
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
description = CAMERA_SWITCHES[0]
|
||||
|
||||
description_entity_name = (
|
||||
description.name.lower().replace(":", "").replace(" ", "_")
|
||||
)
|
||||
unique_id = f"{camera_none.mac}_{description.key}"
|
||||
entity_id = f"switch.test_camera_{description_entity_name}"
|
||||
|
||||
entity = entity_registry.async_get(entity_id)
|
||||
assert entity
|
||||
assert entity.disabled is True
|
||||
assert entity.unique_id == unique_id
|
||||
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
|
||||
state = hass.states.get(entity_id)
|
||||
assert state
|
||||
assert state.state == STATE_OFF
|
||||
assert state.attributes[ATTR_ATTRIBUTION] == DEFAULT_ATTRIBUTION
|
||||
|
||||
|
||||
async def test_switch_light_status(hass: HomeAssistant, light: Light):
|
||||
"""Tests status light switch for lights."""
|
||||
|
||||
await init_entry(hass, ufp, [light])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 2, 1)
|
||||
|
||||
description = LIGHT_SWITCHES[1]
|
||||
|
||||
light.__fields__["set_status_light"] = Mock()
|
||||
|
@ -406,44 +225,53 @@ async def test_switch_light_status(hass: HomeAssistant, light: Light):
|
|||
|
||||
|
||||
async def test_switch_camera_ssh(
|
||||
hass: HomeAssistant, camera: Camera, mock_entry: MockEntityFixture
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Tests SSH switch for cameras."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 13, 12)
|
||||
|
||||
description = CAMERA_SWITCHES[0]
|
||||
|
||||
camera.__fields__["set_ssh"] = Mock()
|
||||
camera.set_ssh = AsyncMock()
|
||||
doorbell.__fields__["set_ssh"] = Mock()
|
||||
doorbell.set_ssh = AsyncMock()
|
||||
|
||||
_, entity_id = ids_from_device_description(Platform.SWITCH, camera, description)
|
||||
await enable_entity(hass, mock_entry.entry.entry_id, entity_id)
|
||||
_, entity_id = ids_from_device_description(Platform.SWITCH, doorbell, description)
|
||||
await enable_entity(hass, ufp.entry.entry_id, entity_id)
|
||||
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||
)
|
||||
|
||||
camera.set_ssh.assert_called_once_with(True)
|
||||
doorbell.set_ssh.assert_called_once_with(True)
|
||||
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||
)
|
||||
|
||||
camera.set_ssh.assert_called_with(False)
|
||||
doorbell.set_ssh.assert_called_with(False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("description", CAMERA_SWITCHES_NO_EXTRA)
|
||||
async def test_switch_camera_simple(
|
||||
hass: HomeAssistant, camera: Camera, description: ProtectSwitchEntityDescription
|
||||
hass: HomeAssistant,
|
||||
ufp: MockUFPFixture,
|
||||
doorbell: Camera,
|
||||
description: ProtectSwitchEntityDescription,
|
||||
):
|
||||
"""Tests all simple switches for cameras."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 13, 12)
|
||||
|
||||
assert description.ufp_set_method is not None
|
||||
|
||||
camera.__fields__[description.ufp_set_method] = Mock()
|
||||
setattr(camera, description.ufp_set_method, AsyncMock())
|
||||
set_method = getattr(camera, description.ufp_set_method)
|
||||
doorbell.__fields__[description.ufp_set_method] = Mock()
|
||||
setattr(doorbell, description.ufp_set_method, AsyncMock())
|
||||
set_method = getattr(doorbell, description.ufp_set_method)
|
||||
|
||||
_, entity_id = ids_from_device_description(Platform.SWITCH, camera, description)
|
||||
_, entity_id = ids_from_device_description(Platform.SWITCH, doorbell, description)
|
||||
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||
|
@ -458,70 +286,82 @@ async def test_switch_camera_simple(
|
|||
set_method.assert_called_with(False)
|
||||
|
||||
|
||||
async def test_switch_camera_highfps(hass: HomeAssistant, camera: Camera):
|
||||
async def test_switch_camera_highfps(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Tests High FPS switch for cameras."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 13, 12)
|
||||
|
||||
description = CAMERA_SWITCHES[3]
|
||||
|
||||
camera.__fields__["set_video_mode"] = Mock()
|
||||
camera.set_video_mode = AsyncMock()
|
||||
doorbell.__fields__["set_video_mode"] = Mock()
|
||||
doorbell.set_video_mode = AsyncMock()
|
||||
|
||||
_, entity_id = ids_from_device_description(Platform.SWITCH, camera, description)
|
||||
_, entity_id = ids_from_device_description(Platform.SWITCH, doorbell, description)
|
||||
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||
)
|
||||
|
||||
camera.set_video_mode.assert_called_once_with(VideoMode.HIGH_FPS)
|
||||
doorbell.set_video_mode.assert_called_once_with(VideoMode.HIGH_FPS)
|
||||
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||
)
|
||||
|
||||
camera.set_video_mode.assert_called_with(VideoMode.DEFAULT)
|
||||
doorbell.set_video_mode.assert_called_with(VideoMode.DEFAULT)
|
||||
|
||||
|
||||
async def test_switch_camera_privacy(hass: HomeAssistant, camera: Camera):
|
||||
async def test_switch_camera_privacy(
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Tests Privacy Mode switch for cameras."""
|
||||
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 13, 12)
|
||||
|
||||
description = CAMERA_SWITCHES[4]
|
||||
|
||||
camera.__fields__["set_privacy"] = Mock()
|
||||
camera.set_privacy = AsyncMock()
|
||||
doorbell.__fields__["set_privacy"] = Mock()
|
||||
doorbell.set_privacy = AsyncMock()
|
||||
|
||||
_, entity_id = ids_from_device_description(Platform.SWITCH, camera, description)
|
||||
_, entity_id = ids_from_device_description(Platform.SWITCH, doorbell, description)
|
||||
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_on", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||
)
|
||||
|
||||
camera.set_privacy.assert_called_once_with(True, 0, RecordingMode.NEVER)
|
||||
doorbell.set_privacy.assert_called_once_with(True, 0, RecordingMode.NEVER)
|
||||
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||
)
|
||||
|
||||
camera.set_privacy.assert_called_with(
|
||||
False, camera.mic_volume, camera.recording_settings.mode
|
||||
doorbell.set_privacy.assert_called_with(
|
||||
False, doorbell.mic_volume, doorbell.recording_settings.mode
|
||||
)
|
||||
|
||||
|
||||
async def test_switch_camera_privacy_already_on(
|
||||
hass: HomeAssistant, camera_privacy: Camera
|
||||
hass: HomeAssistant, ufp: MockUFPFixture, doorbell: Camera
|
||||
):
|
||||
"""Tests Privacy Mode switch for cameras with privacy mode defaulted on."""
|
||||
|
||||
doorbell.add_privacy_zone()
|
||||
await init_entry(hass, ufp, [doorbell])
|
||||
assert_entity_counts(hass, Platform.SWITCH, 13, 12)
|
||||
|
||||
description = CAMERA_SWITCHES[4]
|
||||
|
||||
camera_privacy.__fields__["set_privacy"] = Mock()
|
||||
camera_privacy.set_privacy = AsyncMock()
|
||||
doorbell.__fields__["set_privacy"] = Mock()
|
||||
doorbell.set_privacy = AsyncMock()
|
||||
|
||||
_, entity_id = ids_from_device_description(
|
||||
Platform.SWITCH, camera_privacy, description
|
||||
)
|
||||
_, entity_id = ids_from_device_description(Platform.SWITCH, doorbell, description)
|
||||
|
||||
await hass.services.async_call(
|
||||
"switch", "turn_off", {ATTR_ENTITY_ID: entity_id}, blocking=True
|
||||
)
|
||||
|
||||
camera_privacy.set_privacy.assert_called_once_with(False, 100, RecordingMode.ALWAYS)
|
||||
doorbell.set_privacy.assert_called_once_with(False, 100, RecordingMode.ALWAYS)
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
"""Test helpers for UniFi Protect."""
|
||||
# pylint: disable=protected-access
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import timedelta
|
||||
from typing import Any, Callable, Sequence
|
||||
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
from pyunifiprotect.data import (
|
||||
Bootstrap,
|
||||
Camera,
|
||||
ProtectAdoptableDeviceModel,
|
||||
WSSubscriptionMessage,
|
||||
)
|
||||
from pyunifiprotect.data.bootstrap import ProtectDeviceRef
|
||||
from pyunifiprotect.test_util.anonymize import random_hex
|
||||
|
||||
from homeassistant.const import Platform
|
||||
from homeassistant.core import HomeAssistant, split_entity_id
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.entity import EntityDescription
|
||||
import homeassistant.util.dt as dt_util
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
@dataclass
|
||||
class MockUFPFixture:
|
||||
"""Mock for NVR."""
|
||||
|
||||
entry: MockConfigEntry
|
||||
api: ProtectApiClient
|
||||
ws_subscription: Callable[[WSSubscriptionMessage], None] | None = None
|
||||
|
||||
def ws_msg(self, msg: WSSubscriptionMessage) -> Any:
|
||||
"""Emit WS message for testing."""
|
||||
|
||||
if self.ws_subscription is not None:
|
||||
return self.ws_subscription(msg)
|
||||
|
||||
|
||||
def reset_objects(bootstrap: Bootstrap):
|
||||
"""Reset bootstrap objects."""
|
||||
|
||||
bootstrap.cameras = {}
|
||||
bootstrap.lights = {}
|
||||
bootstrap.sensors = {}
|
||||
bootstrap.viewers = {}
|
||||
bootstrap.events = {}
|
||||
bootstrap.doorlocks = {}
|
||||
bootstrap.chimes = {}
|
||||
|
||||
|
||||
async def time_changed(hass: HomeAssistant, seconds: int) -> None:
|
||||
"""Trigger time changed."""
|
||||
next_update = dt_util.utcnow() + timedelta(seconds)
|
||||
async_fire_time_changed(hass, next_update)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def enable_entity(
|
||||
hass: HomeAssistant, entry_id: str, entity_id: str
|
||||
) -> er.RegistryEntry:
|
||||
"""Enable a disabled entity."""
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
updated_entity = entity_registry.async_update_entity(entity_id, disabled_by=None)
|
||||
assert not updated_entity.disabled
|
||||
await hass.config_entries.async_reload(entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return updated_entity
|
||||
|
||||
|
||||
def assert_entity_counts(
|
||||
hass: HomeAssistant, platform: Platform, total: int, enabled: int
|
||||
) -> None:
|
||||
"""Assert entity counts for a given platform."""
|
||||
|
||||
entity_registry = er.async_get(hass)
|
||||
|
||||
entities = [
|
||||
e for e in entity_registry.entities if split_entity_id(e)[0] == platform.value
|
||||
]
|
||||
|
||||
assert len(entities) == total
|
||||
assert len(hass.states.async_all(platform.value)) == enabled
|
||||
|
||||
|
||||
def normalize_name(name: str) -> str:
|
||||
"""Normalize name."""
|
||||
|
||||
return name.lower().replace(":", "").replace(" ", "_").replace("-", "_")
|
||||
|
||||
|
||||
def ids_from_device_description(
|
||||
platform: Platform,
|
||||
device: ProtectAdoptableDeviceModel,
|
||||
description: EntityDescription,
|
||||
) -> tuple[str, str]:
|
||||
"""Return expected unique_id and entity_id for a give platform/device/description combination."""
|
||||
|
||||
entity_name = normalize_name(device.display_name)
|
||||
description_entity_name = normalize_name(str(description.name))
|
||||
|
||||
unique_id = f"{device.mac}_{description.key}"
|
||||
entity_id = f"{platform.value}.{entity_name}_{description_entity_name}"
|
||||
|
||||
return unique_id, entity_id
|
||||
|
||||
|
||||
def generate_random_ids() -> tuple[str, str]:
|
||||
"""Generate random IDs for device."""
|
||||
|
||||
return random_hex(24).lower(), random_hex(12).upper()
|
||||
|
||||
|
||||
def regenerate_device_ids(device: ProtectAdoptableDeviceModel) -> None:
|
||||
"""Regenerate the IDs on UFP device."""
|
||||
|
||||
device.id, device.mac = generate_random_ids()
|
||||
|
||||
|
||||
def add_device_ref(bootstrap: Bootstrap, device: ProtectAdoptableDeviceModel) -> None:
|
||||
"""Manually add device ref to bootstrap for lookup."""
|
||||
|
||||
ref = ProtectDeviceRef(id=device.id, model=device.model)
|
||||
bootstrap.id_lookup[device.id] = ref
|
||||
bootstrap.mac_lookup[device.mac.lower()] = ref
|
||||
|
||||
|
||||
def add_device(
|
||||
bootstrap: Bootstrap, device: ProtectAdoptableDeviceModel, regenerate_ids: bool
|
||||
) -> None:
|
||||
"""Add test device to bootstrap."""
|
||||
|
||||
if device.model is None:
|
||||
return
|
||||
|
||||
device._api = bootstrap.api
|
||||
if isinstance(device, Camera):
|
||||
for channel in device.channels:
|
||||
channel._api = bootstrap.api
|
||||
|
||||
if regenerate_ids:
|
||||
regenerate_device_ids(device)
|
||||
device._initial_data = device.dict()
|
||||
|
||||
devices = getattr(bootstrap, f"{device.model.value}s")
|
||||
devices[device.id] = device
|
||||
add_device_ref(bootstrap, device)
|
||||
|
||||
|
||||
async def init_entry(
|
||||
hass: HomeAssistant,
|
||||
ufp: MockUFPFixture,
|
||||
devices: Sequence[ProtectAdoptableDeviceModel],
|
||||
regenerate_ids: bool = True,
|
||||
) -> None:
|
||||
"""Initialize Protect entry with given devices."""
|
||||
|
||||
reset_objects(ufp.api.bootstrap)
|
||||
for device in devices:
|
||||
add_device(ufp.api.bootstrap, device, regenerate_ids)
|
||||
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
Loading…
Reference in New Issue