Bumps version of pyunifiprotect to 4.0.4 (#73722)
This commit is contained in:
parent
109d1844b3
commit
3851c7b4b4
21 changed files with 770 additions and 155 deletions
|
@ -7,7 +7,8 @@ import logging
|
||||||
|
|
||||||
from aiohttp import CookieJar
|
from aiohttp import CookieJar
|
||||||
from aiohttp.client_exceptions import ServerDisconnectedError
|
from aiohttp.client_exceptions import ServerDisconnectedError
|
||||||
from pyunifiprotect import NotAuthorized, NvrError, ProtectApiClient
|
from pyunifiprotect import ProtectApiClient
|
||||||
|
from pyunifiprotect.exceptions import ClientError, NotAuthorized
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
|
@ -68,7 +69,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
nvr_info = await protect.get_nvr()
|
nvr_info = await protect.get_nvr()
|
||||||
except NotAuthorized as err:
|
except NotAuthorized as err:
|
||||||
raise ConfigEntryAuthFailed(err) from err
|
raise ConfigEntryAuthFailed(err) from err
|
||||||
except (asyncio.TimeoutError, NvrError, ServerDisconnectedError) as err:
|
except (asyncio.TimeoutError, ClientError, ServerDisconnectedError) as err:
|
||||||
raise ConfigEntryNotReady from err
|
raise ConfigEntryNotReady from err
|
||||||
|
|
||||||
if nvr_info.version < MIN_REQUIRED_PROTECT_V:
|
if nvr_info.version < MIN_REQUIRED_PROTECT_V:
|
||||||
|
|
|
@ -6,8 +6,9 @@ import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from aiohttp import CookieJar
|
from aiohttp import CookieJar
|
||||||
from pyunifiprotect import NotAuthorized, NvrError, ProtectApiClient
|
from pyunifiprotect import ProtectApiClient
|
||||||
from pyunifiprotect.data import NVR
|
from pyunifiprotect.data import NVR
|
||||||
|
from pyunifiprotect.exceptions import ClientError, NotAuthorized
|
||||||
from unifi_discovery import async_console_is_alive
|
from unifi_discovery import async_console_is_alive
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
|
@ -253,7 +254,7 @@ class ProtectFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
||||||
except NotAuthorized as ex:
|
except NotAuthorized as ex:
|
||||||
_LOGGER.debug(ex)
|
_LOGGER.debug(ex)
|
||||||
errors[CONF_PASSWORD] = "invalid_auth"
|
errors[CONF_PASSWORD] = "invalid_auth"
|
||||||
except NvrError as ex:
|
except ClientError as ex:
|
||||||
_LOGGER.debug(ex)
|
_LOGGER.debug(ex)
|
||||||
errors["base"] = "cannot_connect"
|
errors["base"] = "cannot_connect"
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -6,7 +6,7 @@ from datetime import timedelta
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from pyunifiprotect import NotAuthorized, NvrError, ProtectApiClient
|
from pyunifiprotect import ProtectApiClient
|
||||||
from pyunifiprotect.data import (
|
from pyunifiprotect.data import (
|
||||||
Bootstrap,
|
Bootstrap,
|
||||||
Event,
|
Event,
|
||||||
|
@ -15,6 +15,7 @@ from pyunifiprotect.data import (
|
||||||
WSSubscriptionMessage,
|
WSSubscriptionMessage,
|
||||||
)
|
)
|
||||||
from pyunifiprotect.data.base import ProtectAdoptableDeviceModel
|
from pyunifiprotect.data.base import ProtectAdoptableDeviceModel
|
||||||
|
from pyunifiprotect.exceptions import ClientError, NotAuthorized
|
||||||
|
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||||
|
@ -100,23 +101,27 @@ class ProtectData:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
updates = await self.api.update(force=force)
|
updates = await self.api.update(force=force)
|
||||||
except NvrError:
|
|
||||||
if self.last_update_success:
|
|
||||||
_LOGGER.exception("Error while updating")
|
|
||||||
self.last_update_success = False
|
|
||||||
# manually trigger update to mark entities unavailable
|
|
||||||
self._async_process_updates(self.api.bootstrap)
|
|
||||||
except NotAuthorized:
|
except NotAuthorized:
|
||||||
await self.async_stop()
|
await self.async_stop()
|
||||||
_LOGGER.exception("Reauthentication required")
|
_LOGGER.exception("Reauthentication required")
|
||||||
self._entry.async_start_reauth(self._hass)
|
self._entry.async_start_reauth(self._hass)
|
||||||
self.last_update_success = False
|
self.last_update_success = False
|
||||||
|
except ClientError:
|
||||||
|
if self.last_update_success:
|
||||||
|
_LOGGER.exception("Error while updating")
|
||||||
|
self.last_update_success = False
|
||||||
|
# manually trigger update to mark entities unavailable
|
||||||
|
self._async_process_updates(self.api.bootstrap)
|
||||||
else:
|
else:
|
||||||
self.last_update_success = True
|
self.last_update_success = True
|
||||||
self._async_process_updates(updates)
|
self._async_process_updates(updates)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_process_ws_message(self, message: WSSubscriptionMessage) -> None:
|
def _async_process_ws_message(self, message: WSSubscriptionMessage) -> None:
|
||||||
|
# removed packets are not processed yet
|
||||||
|
if message.new_obj is None: # pragma: no cover
|
||||||
|
return
|
||||||
|
|
||||||
if message.new_obj.model in DEVICES_WITH_ENTITIES:
|
if message.new_obj.model in DEVICES_WITH_ENTITIES:
|
||||||
self.async_signal_device_id_update(message.new_obj.id)
|
self.async_signal_device_id_update(message.new_obj.id)
|
||||||
# trigger update for all Cameras with LCD screens when NVR Doorbell settings updates
|
# trigger update for all Cameras with LCD screens when NVR Doorbell settings updates
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"name": "UniFi Protect",
|
"name": "UniFi Protect",
|
||||||
"config_flow": true,
|
"config_flow": true,
|
||||||
"documentation": "https://www.home-assistant.io/integrations/unifiprotect",
|
"documentation": "https://www.home-assistant.io/integrations/unifiprotect",
|
||||||
"requirements": ["pyunifiprotect==3.9.2", "unifi-discovery==1.1.4"],
|
"requirements": ["pyunifiprotect==4.0.4", "unifi-discovery==1.1.4"],
|
||||||
"dependencies": ["http"],
|
"dependencies": ["http"],
|
||||||
"codeowners": ["@briis", "@AngellusMortis", "@bdraco"],
|
"codeowners": ["@briis", "@AngellusMortis", "@bdraco"],
|
||||||
"quality_scale": "platinum",
|
"quality_scale": "platinum",
|
||||||
|
|
|
@ -8,7 +8,7 @@ from typing import Any, cast
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
from pyunifiprotect.api import ProtectApiClient
|
from pyunifiprotect.api import ProtectApiClient
|
||||||
from pyunifiprotect.data import Chime
|
from pyunifiprotect.data import Chime
|
||||||
from pyunifiprotect.exceptions import BadRequest
|
from pyunifiprotect.exceptions import ClientError
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||||
|
@ -100,7 +100,7 @@ async def _async_service_call_nvr(
|
||||||
await asyncio.gather(
|
await asyncio.gather(
|
||||||
*(getattr(i.bootstrap.nvr, method)(*args, **kwargs) for i in instances)
|
*(getattr(i.bootstrap.nvr, method)(*args, **kwargs) for i in instances)
|
||||||
)
|
)
|
||||||
except (BadRequest, ValidationError) as err:
|
except (ClientError, ValidationError) as err:
|
||||||
raise HomeAssistantError(str(err)) from err
|
raise HomeAssistantError(str(err)) from err
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1990,7 +1990,7 @@ pytrafikverket==0.2.0.1
|
||||||
pyudev==0.22.0
|
pyudev==0.22.0
|
||||||
|
|
||||||
# homeassistant.components.unifiprotect
|
# homeassistant.components.unifiprotect
|
||||||
pyunifiprotect==3.9.2
|
pyunifiprotect==4.0.4
|
||||||
|
|
||||||
# homeassistant.components.uptimerobot
|
# homeassistant.components.uptimerobot
|
||||||
pyuptimerobot==22.2.0
|
pyuptimerobot==22.2.0
|
||||||
|
|
|
@ -1325,7 +1325,7 @@ pytrafikverket==0.2.0.1
|
||||||
pyudev==0.22.0
|
pyudev==0.22.0
|
||||||
|
|
||||||
# homeassistant.components.unifiprotect
|
# homeassistant.components.unifiprotect
|
||||||
pyunifiprotect==3.9.2
|
pyunifiprotect==4.0.4
|
||||||
|
|
||||||
# homeassistant.components.uptimerobot
|
# homeassistant.components.uptimerobot
|
||||||
pyuptimerobot==22.2.0
|
pyuptimerobot==22.2.0
|
||||||
|
|
|
@ -13,6 +13,7 @@ from unittest.mock import AsyncMock, Mock, patch
|
||||||
import pytest
|
import pytest
|
||||||
from pyunifiprotect.data import (
|
from pyunifiprotect.data import (
|
||||||
NVR,
|
NVR,
|
||||||
|
Bootstrap,
|
||||||
Camera,
|
Camera,
|
||||||
Chime,
|
Chime,
|
||||||
Doorlock,
|
Doorlock,
|
||||||
|
@ -39,69 +40,6 @@ from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture
|
||||||
MAC_ADDR = "aa:bb:cc:dd:ee:ff"
|
MAC_ADDR = "aa:bb:cc:dd:ee:ff"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class MockBootstrap:
|
|
||||||
"""Mock for Bootstrap."""
|
|
||||||
|
|
||||||
nvr: NVR
|
|
||||||
cameras: dict[str, Any]
|
|
||||||
lights: dict[str, Any]
|
|
||||||
sensors: dict[str, Any]
|
|
||||||
viewers: dict[str, Any]
|
|
||||||
liveviews: dict[str, Any]
|
|
||||||
events: dict[str, Any]
|
|
||||||
doorlocks: dict[str, Any]
|
|
||||||
chimes: dict[str, Any]
|
|
||||||
|
|
||||||
def reset_objects(self) -> None:
|
|
||||||
"""Reset all devices on bootstrap for tests."""
|
|
||||||
self.cameras = {}
|
|
||||||
self.lights = {}
|
|
||||||
self.sensors = {}
|
|
||||||
self.viewers = {}
|
|
||||||
self.liveviews = {}
|
|
||||||
self.events = {}
|
|
||||||
self.doorlocks = {}
|
|
||||||
self.chimes = {}
|
|
||||||
|
|
||||||
def process_ws_packet(self, msg: WSSubscriptionMessage) -> None:
|
|
||||||
"""Fake process method for tests."""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def unifi_dict(self) -> dict[str, Any]:
|
|
||||||
"""Return UniFi formatted dict representation of the NVR."""
|
|
||||||
return {
|
|
||||||
"nvr": self.nvr.unifi_dict(),
|
|
||||||
"cameras": [c.unifi_dict() for c in self.cameras.values()],
|
|
||||||
"lights": [c.unifi_dict() for c in self.lights.values()],
|
|
||||||
"sensors": [c.unifi_dict() for c in self.sensors.values()],
|
|
||||||
"viewers": [c.unifi_dict() for c in self.viewers.values()],
|
|
||||||
"liveviews": [c.unifi_dict() for c in self.liveviews.values()],
|
|
||||||
"doorlocks": [c.unifi_dict() for c in self.doorlocks.values()],
|
|
||||||
"chimes": [c.unifi_dict() for c in self.chimes.values()],
|
|
||||||
}
|
|
||||||
|
|
||||||
def get_device_from_mac(self, mac: str) -> ProtectAdoptableDeviceModel | None:
|
|
||||||
"""Return device for MAC address."""
|
|
||||||
|
|
||||||
mac = mac.lower().replace(":", "").replace("-", "").replace("_", "")
|
|
||||||
|
|
||||||
all_devices = (
|
|
||||||
self.cameras.values(),
|
|
||||||
self.lights.values(),
|
|
||||||
self.sensors.values(),
|
|
||||||
self.viewers.values(),
|
|
||||||
self.liveviews.values(),
|
|
||||||
self.doorlocks.values(),
|
|
||||||
self.chimes.values(),
|
|
||||||
)
|
|
||||||
for devices in all_devices:
|
|
||||||
for device in devices:
|
|
||||||
if device.mac.lower() == mac:
|
|
||||||
return device
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class MockEntityFixture:
|
class MockEntityFixture:
|
||||||
"""Mock for NVR."""
|
"""Mock for NVR."""
|
||||||
|
@ -155,27 +93,42 @@ def mock_old_nvr_fixture():
|
||||||
@pytest.fixture(name="mock_bootstrap")
|
@pytest.fixture(name="mock_bootstrap")
|
||||||
def mock_bootstrap_fixture(mock_nvr: NVR):
|
def mock_bootstrap_fixture(mock_nvr: NVR):
|
||||||
"""Mock Bootstrap fixture."""
|
"""Mock Bootstrap fixture."""
|
||||||
return MockBootstrap(
|
data = json.loads(load_fixture("sample_bootstrap.json", integration=DOMAIN))
|
||||||
nvr=mock_nvr,
|
data["nvr"] = mock_nvr
|
||||||
cameras={},
|
data["cameras"] = []
|
||||||
lights={},
|
data["lights"] = []
|
||||||
sensors={},
|
data["sensors"] = []
|
||||||
viewers={},
|
data["viewers"] = []
|
||||||
liveviews={},
|
data["liveviews"] = []
|
||||||
events={},
|
data["events"] = []
|
||||||
doorlocks={},
|
data["doorlocks"] = []
|
||||||
chimes={},
|
data["chimes"] = []
|
||||||
)
|
|
||||||
|
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
|
@pytest.fixture
|
||||||
def mock_client(mock_bootstrap: MockBootstrap):
|
def mock_client(mock_bootstrap: Bootstrap):
|
||||||
"""Mock ProtectApiClient for testing."""
|
"""Mock ProtectApiClient for testing."""
|
||||||
client = Mock()
|
client = Mock()
|
||||||
client.bootstrap = mock_bootstrap
|
client.bootstrap = mock_bootstrap
|
||||||
|
|
||||||
nvr = mock_bootstrap.nvr
|
nvr = client.bootstrap.nvr
|
||||||
nvr._api = client
|
nvr._api = client
|
||||||
|
client.bootstrap._api = client
|
||||||
|
|
||||||
client.base_url = "https://127.0.0.1"
|
client.base_url = "https://127.0.0.1"
|
||||||
client.connection_host = IPv4Address("127.0.0.1")
|
client.connection_host = IPv4Address("127.0.0.1")
|
||||||
|
|
633
tests/components/unifiprotect/fixtures/sample_bootstrap.json
Normal file
633
tests/components/unifiprotect/fixtures/sample_bootstrap.json
Normal file
|
@ -0,0 +1,633 @@
|
||||||
|
{
|
||||||
|
"authUserId": "4c5f03a8c8bd48ad8e066285",
|
||||||
|
"accessKey": "8528571101220:340ff666bffb58bc404b859a:8f3f41a7b180b1ff7463fe4f7f13b528ac3d28668f25d0ecaa30c8e7888559e782b38d4335b40861030b75126eb7cea8385f3f9ab59dfa9a993e50757c277053",
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"permissions": [],
|
||||||
|
"lastLoginIp": null,
|
||||||
|
"lastLoginTime": null,
|
||||||
|
"isOwner": true,
|
||||||
|
"enableNotifications": false,
|
||||||
|
"settings": {
|
||||||
|
"flags": {}
|
||||||
|
},
|
||||||
|
"groups": ["b061186823695fb901973177"],
|
||||||
|
"alertRules": [],
|
||||||
|
"notificationsV2": {
|
||||||
|
"state": "custom",
|
||||||
|
"motionNotifications": {
|
||||||
|
"trigger": {
|
||||||
|
"when": "inherit",
|
||||||
|
"location": "away",
|
||||||
|
"schedules": []
|
||||||
|
},
|
||||||
|
"cameras": [
|
||||||
|
{
|
||||||
|
"inheritFromParent": true,
|
||||||
|
"motion": [],
|
||||||
|
"person": [],
|
||||||
|
"vehicle": [],
|
||||||
|
"camera": "61b3f5c7033ea703e7000424",
|
||||||
|
"trigger": {
|
||||||
|
"when": "always",
|
||||||
|
"location": "away",
|
||||||
|
"schedules": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doorbells": [],
|
||||||
|
"lights": [],
|
||||||
|
"doorlocks": [],
|
||||||
|
"sensors": []
|
||||||
|
},
|
||||||
|
"systemNotifications": {}
|
||||||
|
},
|
||||||
|
"featureFlags": {
|
||||||
|
"notificationsV2": true
|
||||||
|
},
|
||||||
|
"id": "fe4c12ae2c1348edb7854e2f",
|
||||||
|
"hasAcceptedInvite": true,
|
||||||
|
"allPermissions": [
|
||||||
|
"nvr:read:*",
|
||||||
|
"liveview:create",
|
||||||
|
"user:read,write,delete:$",
|
||||||
|
"nvr:write,delete:*",
|
||||||
|
"group:create,read,write,delete:*",
|
||||||
|
"user:create,read,write,delete:*",
|
||||||
|
"schedule:create,read,write,delete:*",
|
||||||
|
"legacyUFV:read,write,delete:*",
|
||||||
|
"bridge:create,read,write,delete:*",
|
||||||
|
"camera:create,read,write,delete,readmedia,deletemedia:*",
|
||||||
|
"light:create,read,write,delete:*",
|
||||||
|
"sensor:create,read,write,delete:*",
|
||||||
|
"doorlock:create,read,write,delete:*",
|
||||||
|
"viewer:create,read,write,delete:*",
|
||||||
|
"display:create,read,write,delete:*",
|
||||||
|
"chime:create,read,write,delete:*"
|
||||||
|
],
|
||||||
|
"cloudAccount": {
|
||||||
|
"firstName": "Qpvfly",
|
||||||
|
"lastName": "Ikjzilt",
|
||||||
|
"email": "QhoFvCv@example.com",
|
||||||
|
"profileImg": null,
|
||||||
|
"user": "fe4c12ae2c1348edb7854e2f",
|
||||||
|
"id": "9efc4511-4539-4402-9581-51cee8b65cf5",
|
||||||
|
"cloudId": "9efc4511-4539-4402-9581-51cee8b65cf5",
|
||||||
|
"name": "Qpvfly Ikjzilt",
|
||||||
|
"modelKey": "cloudIdentity"
|
||||||
|
},
|
||||||
|
"name": "Qpvfly Ikjzilt",
|
||||||
|
"firstName": "Qpvfly",
|
||||||
|
"lastName": "Ikjzilt",
|
||||||
|
"email": "QhoFvCv@example.com",
|
||||||
|
"localUsername": "QhoFvCv",
|
||||||
|
"modelKey": "user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions": [],
|
||||||
|
"lastLoginIp": null,
|
||||||
|
"lastLoginTime": null,
|
||||||
|
"isOwner": false,
|
||||||
|
"enableNotifications": false,
|
||||||
|
"settings": null,
|
||||||
|
"groups": ["a7f3b2eb71b4c4e56f1f45ac", "b061186823695fb901973177"],
|
||||||
|
"alertRules": [],
|
||||||
|
"notificationsV2": {
|
||||||
|
"state": "auto",
|
||||||
|
"motionNotifications": {
|
||||||
|
"trigger": {
|
||||||
|
"when": "inherit",
|
||||||
|
"location": "away",
|
||||||
|
"schedules": []
|
||||||
|
},
|
||||||
|
"cameras": [],
|
||||||
|
"doorbells": [],
|
||||||
|
"lights": [],
|
||||||
|
"doorlocks": [],
|
||||||
|
"sensors": []
|
||||||
|
},
|
||||||
|
"systemNotifications": {}
|
||||||
|
},
|
||||||
|
"featureFlags": {
|
||||||
|
"notificationsV2": true
|
||||||
|
},
|
||||||
|
"id": "dcaef9cb8aed05c7db658a46",
|
||||||
|
"hasAcceptedInvite": false,
|
||||||
|
"allPermissions": [
|
||||||
|
"nvr:read:*",
|
||||||
|
"liveview:create",
|
||||||
|
"user:read,write,delete:$",
|
||||||
|
"bridge:read:*",
|
||||||
|
"camera:read,readmedia:*",
|
||||||
|
"doorlock:read:*",
|
||||||
|
"light:read:*",
|
||||||
|
"sensor:read:*",
|
||||||
|
"viewer:read:*",
|
||||||
|
"display:read:*",
|
||||||
|
"chime:read:*",
|
||||||
|
"nvr:read:*",
|
||||||
|
"liveview:create",
|
||||||
|
"user:read,write,delete:$",
|
||||||
|
"nvr:write,delete:*",
|
||||||
|
"group:create,read,write,delete:*",
|
||||||
|
"user:create,read,write,delete:*",
|
||||||
|
"schedule:create,read,write,delete:*",
|
||||||
|
"legacyUFV:read,write,delete:*",
|
||||||
|
"bridge:create,read,write,delete:*",
|
||||||
|
"camera:create,read,write,delete,readmedia,deletemedia:*",
|
||||||
|
"light:create,read,write,delete:*",
|
||||||
|
"sensor:create,read,write,delete:*",
|
||||||
|
"doorlock:create,read,write,delete:*",
|
||||||
|
"viewer:create,read,write,delete:*",
|
||||||
|
"display:create,read,write,delete:*",
|
||||||
|
"chime:create,read,write,delete:*"
|
||||||
|
],
|
||||||
|
"cloudAccount": null,
|
||||||
|
"name": "Uxqg Wcbz",
|
||||||
|
"firstName": "Uxqg",
|
||||||
|
"lastName": "Wcbz",
|
||||||
|
"email": "epHDEhE@example.com",
|
||||||
|
"localUsername": "epHDEhE",
|
||||||
|
"modelKey": "user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions": [
|
||||||
|
"liveview:*:d65bb41c14d6aa92bfa4a6d1",
|
||||||
|
"liveview:*:49bbb5005424a0d35152671a",
|
||||||
|
"liveview:*:b28c38f1220f6b43f3930dff",
|
||||||
|
"liveview:*:b9861b533a87ea639fa4d438"
|
||||||
|
],
|
||||||
|
"lastLoginIp": null,
|
||||||
|
"lastLoginTime": null,
|
||||||
|
"isOwner": false,
|
||||||
|
"enableNotifications": false,
|
||||||
|
"settings": {
|
||||||
|
"flags": {},
|
||||||
|
"web": {
|
||||||
|
"dewarp": {
|
||||||
|
"61ddb66b018e2703e7008c19": {
|
||||||
|
"dewarp": false,
|
||||||
|
"state": {
|
||||||
|
"pan": 0,
|
||||||
|
"tilt": -1.5707963267948966,
|
||||||
|
"zoom": 1.5707963267948966,
|
||||||
|
"panning": 0,
|
||||||
|
"tilting": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"liveview.includeGlobal": true,
|
||||||
|
"elements.events_viewmode": "grid",
|
||||||
|
"elements.viewmode": "list"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"groups": ["b061186823695fb901973177"],
|
||||||
|
"location": {
|
||||||
|
"isAway": true,
|
||||||
|
"latitude": null,
|
||||||
|
"longitude": null
|
||||||
|
},
|
||||||
|
"alertRules": [],
|
||||||
|
"notificationsV2": {
|
||||||
|
"state": "custom",
|
||||||
|
"motionNotifications": {
|
||||||
|
"trigger": {
|
||||||
|
"when": "inherit",
|
||||||
|
"location": "away",
|
||||||
|
"schedules": []
|
||||||
|
},
|
||||||
|
"cameras": [
|
||||||
|
{
|
||||||
|
"inheritFromParent": true,
|
||||||
|
"motion": [],
|
||||||
|
"camera": "61b3f5c703d2a703e7000427",
|
||||||
|
"trigger": {
|
||||||
|
"when": "always",
|
||||||
|
"location": "away",
|
||||||
|
"schedules": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inheritFromParent": true,
|
||||||
|
"motion": [],
|
||||||
|
"person": [],
|
||||||
|
"vehicle": [],
|
||||||
|
"camera": "61b3f5c7033ea703e7000424",
|
||||||
|
"trigger": {
|
||||||
|
"when": "always",
|
||||||
|
"location": "away",
|
||||||
|
"schedules": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"doorbells": [],
|
||||||
|
"lights": [],
|
||||||
|
"doorlocks": [],
|
||||||
|
"sensors": []
|
||||||
|
},
|
||||||
|
"systemNotifications": {}
|
||||||
|
},
|
||||||
|
"featureFlags": {
|
||||||
|
"notificationsV2": true
|
||||||
|
},
|
||||||
|
"id": "4c5f03a8c8bd48ad8e066285",
|
||||||
|
"hasAcceptedInvite": false,
|
||||||
|
"allPermissions": [
|
||||||
|
"liveview:*:d65bb41c14d6aa92bfa4a6d1",
|
||||||
|
"liveview:*:49bbb5005424a0d35152671a",
|
||||||
|
"liveview:*:b28c38f1220f6b43f3930dff",
|
||||||
|
"liveview:*:b9861b533a87ea639fa4d438",
|
||||||
|
"nvr:read:*",
|
||||||
|
"liveview:create",
|
||||||
|
"user:read,write,delete:$",
|
||||||
|
"nvr:write,delete:*",
|
||||||
|
"group:create,read,write,delete:*",
|
||||||
|
"user:create,read,write,delete:*",
|
||||||
|
"schedule:create,read,write,delete:*",
|
||||||
|
"legacyUFV:read,write,delete:*",
|
||||||
|
"bridge:create,read,write,delete:*",
|
||||||
|
"camera:create,read,write,delete,readmedia,deletemedia:*",
|
||||||
|
"light:create,read,write,delete:*",
|
||||||
|
"sensor:create,read,write,delete:*",
|
||||||
|
"doorlock:create,read,write,delete:*",
|
||||||
|
"viewer:create,read,write,delete:*",
|
||||||
|
"display:create,read,write,delete:*",
|
||||||
|
"chime:create,read,write,delete:*"
|
||||||
|
],
|
||||||
|
"cloudAccount": null,
|
||||||
|
"name": "Ptcmsdo Tfiyoep",
|
||||||
|
"firstName": "Ptcmsdo",
|
||||||
|
"lastName": "Tfiyoep",
|
||||||
|
"email": "EQAoXL@example.com",
|
||||||
|
"localUsername": "EQAoXL",
|
||||||
|
"modelKey": "user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions": [],
|
||||||
|
"lastLoginIp": null,
|
||||||
|
"lastLoginTime": null,
|
||||||
|
"isOwner": false,
|
||||||
|
"enableNotifications": false,
|
||||||
|
"settings": {
|
||||||
|
"flags": {},
|
||||||
|
"web": {
|
||||||
|
"dewarp": {
|
||||||
|
"61c4d1db02c82a03e700429c": {
|
||||||
|
"dewarp": false,
|
||||||
|
"state": {
|
||||||
|
"pan": 0,
|
||||||
|
"tilt": 0,
|
||||||
|
"zoom": 1.5707963267948966,
|
||||||
|
"panning": 0,
|
||||||
|
"tilting": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"liveview.includeGlobal": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"groups": ["a7f3b2eb71b4c4e56f1f45ac"],
|
||||||
|
"alertRules": [],
|
||||||
|
"notificationsV2": {
|
||||||
|
"state": "auto",
|
||||||
|
"motionNotifications": {
|
||||||
|
"trigger": {
|
||||||
|
"when": "inherit",
|
||||||
|
"location": "away",
|
||||||
|
"schedules": []
|
||||||
|
},
|
||||||
|
"cameras": [],
|
||||||
|
"doorbells": [],
|
||||||
|
"lights": [],
|
||||||
|
"doorlocks": [],
|
||||||
|
"sensors": []
|
||||||
|
},
|
||||||
|
"systemNotifications": {}
|
||||||
|
},
|
||||||
|
"featureFlags": {
|
||||||
|
"notificationsV2": true
|
||||||
|
},
|
||||||
|
"id": "bc3dd633553907952a6fe20d",
|
||||||
|
"hasAcceptedInvite": false,
|
||||||
|
"allPermissions": [
|
||||||
|
"nvr:read:*",
|
||||||
|
"liveview:create",
|
||||||
|
"user:read,write,delete:$",
|
||||||
|
"bridge:read:*",
|
||||||
|
"camera:read,readmedia:*",
|
||||||
|
"doorlock:read:*",
|
||||||
|
"light:read:*",
|
||||||
|
"sensor:read:*",
|
||||||
|
"viewer:read:*",
|
||||||
|
"display:read:*",
|
||||||
|
"chime:read:*"
|
||||||
|
],
|
||||||
|
"cloudAccount": null,
|
||||||
|
"name": "Evdxou Zgyv",
|
||||||
|
"firstName": "Evdxou",
|
||||||
|
"lastName": "Zgyv",
|
||||||
|
"email": "FMZuD@example.com",
|
||||||
|
"localUsername": "FMZuD",
|
||||||
|
"modelKey": "user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions": [],
|
||||||
|
"lastLoginIp": null,
|
||||||
|
"lastLoginTime": null,
|
||||||
|
"isOwner": false,
|
||||||
|
"enableNotifications": false,
|
||||||
|
"settings": null,
|
||||||
|
"groups": ["a7f3b2eb71b4c4e56f1f45ac", "b061186823695fb901973177"],
|
||||||
|
"alertRules": [],
|
||||||
|
"notificationsV2": {
|
||||||
|
"state": "auto",
|
||||||
|
"motionNotifications": {
|
||||||
|
"trigger": {
|
||||||
|
"when": "inherit",
|
||||||
|
"location": "away",
|
||||||
|
"schedules": []
|
||||||
|
},
|
||||||
|
"cameras": [],
|
||||||
|
"doorbells": [],
|
||||||
|
"lights": [],
|
||||||
|
"doorlocks": [],
|
||||||
|
"sensors": []
|
||||||
|
},
|
||||||
|
"systemNotifications": {}
|
||||||
|
},
|
||||||
|
"featureFlags": {
|
||||||
|
"notificationsV2": true
|
||||||
|
},
|
||||||
|
"id": "adec5334b69f56f6a6c47520",
|
||||||
|
"hasAcceptedInvite": false,
|
||||||
|
"allPermissions": [
|
||||||
|
"nvr:read:*",
|
||||||
|
"liveview:create",
|
||||||
|
"user:read,write,delete:$",
|
||||||
|
"bridge:read:*",
|
||||||
|
"camera:read,readmedia:*",
|
||||||
|
"doorlock:read:*",
|
||||||
|
"light:read:*",
|
||||||
|
"sensor:read:*",
|
||||||
|
"viewer:read:*",
|
||||||
|
"display:read:*",
|
||||||
|
"chime:read:*",
|
||||||
|
"nvr:read:*",
|
||||||
|
"liveview:create",
|
||||||
|
"user:read,write,delete:$",
|
||||||
|
"nvr:write,delete:*",
|
||||||
|
"group:create,read,write,delete:*",
|
||||||
|
"user:create,read,write,delete:*",
|
||||||
|
"schedule:create,read,write,delete:*",
|
||||||
|
"legacyUFV:read,write,delete:*",
|
||||||
|
"bridge:create,read,write,delete:*",
|
||||||
|
"camera:create,read,write,delete,readmedia,deletemedia:*",
|
||||||
|
"light:create,read,write,delete:*",
|
||||||
|
"sensor:create,read,write,delete:*",
|
||||||
|
"doorlock:create,read,write,delete:*",
|
||||||
|
"viewer:create,read,write,delete:*",
|
||||||
|
"display:create,read,write,delete:*",
|
||||||
|
"chime:create,read,write,delete:*"
|
||||||
|
],
|
||||||
|
"cloudAccount": null,
|
||||||
|
"name": "Qpv Elqfgq",
|
||||||
|
"firstName": "Qpv",
|
||||||
|
"lastName": "Elqfgq",
|
||||||
|
"email": "xdr@example.com",
|
||||||
|
"localUsername": "xdr",
|
||||||
|
"modelKey": "user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions": [],
|
||||||
|
"lastLoginIp": null,
|
||||||
|
"lastLoginTime": null,
|
||||||
|
"isOwner": false,
|
||||||
|
"enableNotifications": false,
|
||||||
|
"settings": null,
|
||||||
|
"groups": ["a7f3b2eb71b4c4e56f1f45ac", "b061186823695fb901973177"],
|
||||||
|
"alertRules": [],
|
||||||
|
"notificationsV2": {
|
||||||
|
"state": "auto",
|
||||||
|
"motionNotifications": {
|
||||||
|
"trigger": {
|
||||||
|
"when": "inherit",
|
||||||
|
"location": "away",
|
||||||
|
"schedules": []
|
||||||
|
},
|
||||||
|
"cameras": [],
|
||||||
|
"doorbells": [],
|
||||||
|
"lights": [],
|
||||||
|
"doorlocks": [],
|
||||||
|
"sensors": []
|
||||||
|
},
|
||||||
|
"systemNotifications": {}
|
||||||
|
},
|
||||||
|
"featureFlags": {
|
||||||
|
"notificationsV2": true
|
||||||
|
},
|
||||||
|
"id": "8593657a25b7826a4288b6af",
|
||||||
|
"hasAcceptedInvite": false,
|
||||||
|
"allPermissions": [
|
||||||
|
"nvr:read:*",
|
||||||
|
"liveview:create",
|
||||||
|
"user:read,write,delete:$",
|
||||||
|
"bridge:read:*",
|
||||||
|
"camera:read,readmedia:*",
|
||||||
|
"doorlock:read:*",
|
||||||
|
"light:read:*",
|
||||||
|
"sensor:read:*",
|
||||||
|
"viewer:read:*",
|
||||||
|
"display:read:*",
|
||||||
|
"chime:read:*",
|
||||||
|
"nvr:read:*",
|
||||||
|
"liveview:create",
|
||||||
|
"user:read,write,delete:$",
|
||||||
|
"nvr:write,delete:*",
|
||||||
|
"group:create,read,write,delete:*",
|
||||||
|
"user:create,read,write,delete:*",
|
||||||
|
"schedule:create,read,write,delete:*",
|
||||||
|
"legacyUFV:read,write,delete:*",
|
||||||
|
"bridge:create,read,write,delete:*",
|
||||||
|
"camera:create,read,write,delete,readmedia,deletemedia:*",
|
||||||
|
"light:create,read,write,delete:*",
|
||||||
|
"sensor:create,read,write,delete:*",
|
||||||
|
"doorlock:create,read,write,delete:*",
|
||||||
|
"viewer:create,read,write,delete:*",
|
||||||
|
"display:create,read,write,delete:*",
|
||||||
|
"chime:create,read,write,delete:*"
|
||||||
|
],
|
||||||
|
"cloudAccount": null,
|
||||||
|
"name": "Sgpy Ooevsme",
|
||||||
|
"firstName": "Sgpy",
|
||||||
|
"lastName": "Ooevsme",
|
||||||
|
"email": "WQJNT@example.com",
|
||||||
|
"localUsername": "WQJNT",
|
||||||
|
"modelKey": "user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions": [],
|
||||||
|
"isOwner": false,
|
||||||
|
"enableNotifications": false,
|
||||||
|
"groups": ["a7f3b2eb71b4c4e56f1f45ac"],
|
||||||
|
"alertRules": [],
|
||||||
|
"notificationsV2": {
|
||||||
|
"state": "off",
|
||||||
|
"motionNotifications": {
|
||||||
|
"trigger": {
|
||||||
|
"when": "inherit",
|
||||||
|
"location": "away",
|
||||||
|
"schedules": []
|
||||||
|
},
|
||||||
|
"cameras": [],
|
||||||
|
"doorbells": [],
|
||||||
|
"lights": [],
|
||||||
|
"doorlocks": [],
|
||||||
|
"sensors": []
|
||||||
|
},
|
||||||
|
"systemNotifications": {}
|
||||||
|
},
|
||||||
|
"featureFlags": {
|
||||||
|
"notificationsV2": true
|
||||||
|
},
|
||||||
|
"id": "abf647aed3650a781ceba13f",
|
||||||
|
"hasAcceptedInvite": false,
|
||||||
|
"allPermissions": [
|
||||||
|
"nvr:read:*",
|
||||||
|
"liveview:create",
|
||||||
|
"user:read,write,delete:$",
|
||||||
|
"bridge:read:*",
|
||||||
|
"camera:read,readmedia:*",
|
||||||
|
"doorlock:read:*",
|
||||||
|
"light:read:*",
|
||||||
|
"sensor:read:*",
|
||||||
|
"viewer:read:*",
|
||||||
|
"display:read:*",
|
||||||
|
"chime:read:*"
|
||||||
|
],
|
||||||
|
"cloudAccount": null,
|
||||||
|
"name": "Yiiyq Glx",
|
||||||
|
"firstName": "Yiiyq",
|
||||||
|
"lastName": "Glx",
|
||||||
|
"email": "fBjmm@example.com",
|
||||||
|
"localUsername": "fBjmm",
|
||||||
|
"modelKey": "user"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"name": "Kubw Xnbb",
|
||||||
|
"permissions": [
|
||||||
|
"nvr:read:*",
|
||||||
|
"liveview:create",
|
||||||
|
"user:read,write,delete:$",
|
||||||
|
"nvr:write,delete:*",
|
||||||
|
"group:create,read,write,delete:*",
|
||||||
|
"user:create,read,write,delete:*",
|
||||||
|
"schedule:create,read,write,delete:*",
|
||||||
|
"legacyUFV:read,write,delete:*",
|
||||||
|
"bridge:create,read,write,delete:*",
|
||||||
|
"camera:create,read,write,delete,readmedia,deletemedia:*",
|
||||||
|
"light:create,read,write,delete:*",
|
||||||
|
"sensor:create,read,write,delete:*",
|
||||||
|
"doorlock:create,read,write,delete:*",
|
||||||
|
"viewer:create,read,write,delete:*",
|
||||||
|
"display:create,read,write,delete:*",
|
||||||
|
"chime:create,read,write,delete:*"
|
||||||
|
],
|
||||||
|
"type": "preset",
|
||||||
|
"isDefault": true,
|
||||||
|
"id": "b061186823695fb901973177",
|
||||||
|
"modelKey": "group"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Pmbrvp Wyzqs",
|
||||||
|
"permissions": [
|
||||||
|
"nvr:read:*",
|
||||||
|
"liveview:create",
|
||||||
|
"user:read,write,delete:$",
|
||||||
|
"bridge:read:*",
|
||||||
|
"camera:read,readmedia:*",
|
||||||
|
"doorlock:read:*",
|
||||||
|
"light:read:*",
|
||||||
|
"sensor:read:*",
|
||||||
|
"viewer:read:*",
|
||||||
|
"display:read:*",
|
||||||
|
"chime:read:*"
|
||||||
|
],
|
||||||
|
"type": "preset",
|
||||||
|
"isDefault": false,
|
||||||
|
"id": "a7f3b2eb71b4c4e56f1f45ac",
|
||||||
|
"modelKey": "group"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"schedules": [],
|
||||||
|
"legacyUFVs": [],
|
||||||
|
"lastUpdateId": "ebf25bac-d5a1-4f1d-a0ee-74c15981eb70",
|
||||||
|
"displays": [],
|
||||||
|
"bridges": [
|
||||||
|
{
|
||||||
|
"mac": "A28D0DB15AE1",
|
||||||
|
"host": "192.168.231.68",
|
||||||
|
"connectionHost": "192.168.102.63",
|
||||||
|
"type": "UFP-UAP-B",
|
||||||
|
"name": "Sffde Gxcaqe",
|
||||||
|
"upSince": 1639807977891,
|
||||||
|
"uptime": 3247782,
|
||||||
|
"lastSeen": 1643055759891,
|
||||||
|
"connectedSince": 1642374159304,
|
||||||
|
"state": "CONNECTED",
|
||||||
|
"hardwareRevision": 19,
|
||||||
|
"firmwareVersion": "0.3.1",
|
||||||
|
"latestFirmwareVersion": null,
|
||||||
|
"firmwareBuild": null,
|
||||||
|
"isUpdating": false,
|
||||||
|
"isAdopting": false,
|
||||||
|
"isAdopted": true,
|
||||||
|
"isAdoptedByOther": false,
|
||||||
|
"isProvisioned": false,
|
||||||
|
"isRebooting": false,
|
||||||
|
"isSshEnabled": false,
|
||||||
|
"canAdopt": false,
|
||||||
|
"isAttemptingToConnect": false,
|
||||||
|
"wiredConnectionState": {
|
||||||
|
"phyRate": null
|
||||||
|
},
|
||||||
|
"id": "1f5a055254fb9169d7536fb9",
|
||||||
|
"isConnected": true,
|
||||||
|
"platform": "mt7621",
|
||||||
|
"modelKey": "bridge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"mac": "C65C557CCA95",
|
||||||
|
"host": "192.168.87.68",
|
||||||
|
"connectionHost": "192.168.102.63",
|
||||||
|
"type": "UFP-UAP-B",
|
||||||
|
"name": "Axiwj Bbd",
|
||||||
|
"upSince": 1641257260772,
|
||||||
|
"uptime": null,
|
||||||
|
"lastSeen": 1643052750862,
|
||||||
|
"connectedSince": 1643052754695,
|
||||||
|
"state": "CONNECTED",
|
||||||
|
"hardwareRevision": 19,
|
||||||
|
"firmwareVersion": "0.3.1",
|
||||||
|
"latestFirmwareVersion": null,
|
||||||
|
"firmwareBuild": null,
|
||||||
|
"isUpdating": false,
|
||||||
|
"isAdopting": false,
|
||||||
|
"isAdopted": true,
|
||||||
|
"isAdoptedByOther": false,
|
||||||
|
"isProvisioned": false,
|
||||||
|
"isRebooting": false,
|
||||||
|
"isSshEnabled": false,
|
||||||
|
"canAdopt": false,
|
||||||
|
"isAttemptingToConnect": false,
|
||||||
|
"wiredConnectionState": {
|
||||||
|
"phyRate": null
|
||||||
|
},
|
||||||
|
"id": "e6901e3665a4c0eab0d9c1a5",
|
||||||
|
"isConnected": true,
|
||||||
|
"platform": "mt7621",
|
||||||
|
"modelKey": "bridge"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ from .conftest import (
|
||||||
MockEntityFixture,
|
MockEntityFixture,
|
||||||
assert_entity_counts,
|
assert_entity_counts,
|
||||||
ids_from_device_description,
|
ids_from_device_description,
|
||||||
|
reset_objects,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ async def camera_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Camera.__config__.validate_assignment = False
|
Camera.__config__.validate_assignment = False
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -62,7 +63,7 @@ async def camera_fixture(
|
||||||
camera_obj.is_dark = False
|
camera_obj.is_dark = False
|
||||||
camera_obj.is_motion_detected = False
|
camera_obj.is_motion_detected = False
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||||
mock_entry.api.bootstrap.cameras = {
|
mock_entry.api.bootstrap.cameras = {
|
||||||
camera_obj.id: camera_obj,
|
camera_obj.id: camera_obj,
|
||||||
|
@ -87,14 +88,14 @@ async def light_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Light.__config__.validate_assignment = False
|
Light.__config__.validate_assignment = False
|
||||||
|
|
||||||
light_obj = mock_light.copy(deep=True)
|
light_obj = mock_light.copy()
|
||||||
light_obj._api = mock_entry.api
|
light_obj._api = mock_entry.api
|
||||||
light_obj.name = "Test Light"
|
light_obj.name = "Test Light"
|
||||||
light_obj.is_dark = False
|
light_obj.is_dark = False
|
||||||
light_obj.is_pir_motion_detected = False
|
light_obj.is_pir_motion_detected = False
|
||||||
light_obj.last_motion = now - timedelta(hours=1)
|
light_obj.last_motion = now - timedelta(hours=1)
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||||
mock_entry.api.bootstrap.lights = {
|
mock_entry.api.bootstrap.lights = {
|
||||||
light_obj.id: light_obj,
|
light_obj.id: light_obj,
|
||||||
|
@ -119,7 +120,7 @@ async def camera_none_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Camera.__config__.validate_assignment = False
|
Camera.__config__.validate_assignment = False
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -129,7 +130,7 @@ async def camera_none_fixture(
|
||||||
camera_obj.is_dark = False
|
camera_obj.is_dark = False
|
||||||
camera_obj.is_motion_detected = False
|
camera_obj.is_motion_detected = False
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||||
mock_entry.api.bootstrap.cameras = {
|
mock_entry.api.bootstrap.cameras = {
|
||||||
camera_obj.id: camera_obj,
|
camera_obj.id: camera_obj,
|
||||||
|
@ -157,7 +158,7 @@ async def sensor_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Sensor.__config__.validate_assignment = False
|
Sensor.__config__.validate_assignment = False
|
||||||
|
|
||||||
sensor_obj = mock_sensor.copy(deep=True)
|
sensor_obj = mock_sensor.copy()
|
||||||
sensor_obj._api = mock_entry.api
|
sensor_obj._api = mock_entry.api
|
||||||
sensor_obj.name = "Test Sensor"
|
sensor_obj.name = "Test Sensor"
|
||||||
sensor_obj.mount_type = MountType.DOOR
|
sensor_obj.mount_type = MountType.DOOR
|
||||||
|
@ -170,7 +171,7 @@ async def sensor_fixture(
|
||||||
sensor_obj.alarm_triggered_at = now - timedelta(hours=1)
|
sensor_obj.alarm_triggered_at = now - timedelta(hours=1)
|
||||||
sensor_obj.tampering_detected_at = None
|
sensor_obj.tampering_detected_at = None
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||||
mock_entry.api.bootstrap.sensors = {
|
mock_entry.api.bootstrap.sensors = {
|
||||||
sensor_obj.id: sensor_obj,
|
sensor_obj.id: sensor_obj,
|
||||||
|
@ -198,7 +199,7 @@ async def sensor_none_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Sensor.__config__.validate_assignment = False
|
Sensor.__config__.validate_assignment = False
|
||||||
|
|
||||||
sensor_obj = mock_sensor.copy(deep=True)
|
sensor_obj = mock_sensor.copy()
|
||||||
sensor_obj._api = mock_entry.api
|
sensor_obj._api = mock_entry.api
|
||||||
sensor_obj.name = "Test Sensor"
|
sensor_obj.name = "Test Sensor"
|
||||||
sensor_obj.mount_type = MountType.LEAK
|
sensor_obj.mount_type = MountType.LEAK
|
||||||
|
@ -206,7 +207,7 @@ async def sensor_none_fixture(
|
||||||
sensor_obj.alarm_settings.is_enabled = False
|
sensor_obj.alarm_settings.is_enabled = False
|
||||||
sensor_obj.tampering_detected_at = None
|
sensor_obj.tampering_detected_at = None
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||||
mock_entry.api.bootstrap.sensors = {
|
mock_entry.api.bootstrap.sensors = {
|
||||||
sensor_obj.id: sensor_obj,
|
sensor_obj.id: sensor_obj,
|
||||||
|
|
|
@ -21,7 +21,7 @@ async def chime_fixture(
|
||||||
):
|
):
|
||||||
"""Fixture for a single camera for testing the button platform."""
|
"""Fixture for a single camera for testing the button platform."""
|
||||||
|
|
||||||
chime_obj = mock_chime.copy(deep=True)
|
chime_obj = mock_chime.copy()
|
||||||
chime_obj._api = mock_entry.api
|
chime_obj._api = mock_entry.api
|
||||||
chime_obj.name = "Test Chime"
|
chime_obj.name = "Test Chime"
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ async def camera_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
ProtectCamera.__config__.validate_assignment = False
|
ProtectCamera.__config__.validate_assignment = False
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -83,7 +83,7 @@ async def camera_package_fixture(
|
||||||
):
|
):
|
||||||
"""Fixture for a single camera for testing the camera platform."""
|
"""Fixture for a single camera for testing the camera platform."""
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -95,7 +95,7 @@ async def camera_package_fixture(
|
||||||
camera_obj.channels[0].rtsp_alias = "test_high_alias"
|
camera_obj.channels[0].rtsp_alias = "test_high_alias"
|
||||||
camera_obj.channels[1].is_rtsp_enabled = False
|
camera_obj.channels[1].is_rtsp_enabled = False
|
||||||
camera_obj.channels[2].is_rtsp_enabled = False
|
camera_obj.channels[2].is_rtsp_enabled = False
|
||||||
package_channel = camera_obj.channels[0].copy(deep=True)
|
package_channel = camera_obj.channels[0].copy()
|
||||||
package_channel.is_rtsp_enabled = False
|
package_channel.is_rtsp_enabled = False
|
||||||
package_channel.name = "Package Camera"
|
package_channel.name = "Package Camera"
|
||||||
package_channel.id = 3
|
package_channel.id = 3
|
||||||
|
@ -246,8 +246,9 @@ async def test_basic_setup(
|
||||||
):
|
):
|
||||||
"""Test working setup of unifiprotect entry."""
|
"""Test working setup of unifiprotect entry."""
|
||||||
|
|
||||||
camera_high_only = mock_camera.copy(deep=True)
|
camera_high_only = mock_camera.copy()
|
||||||
camera_high_only._api = mock_entry.api
|
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[0]._api = mock_entry.api
|
||||||
camera_high_only.channels[1]._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.channels[2]._api = mock_entry.api
|
||||||
|
@ -259,8 +260,9 @@ async def test_basic_setup(
|
||||||
camera_high_only.channels[2].is_rtsp_enabled = False
|
camera_high_only.channels[2].is_rtsp_enabled = False
|
||||||
regenerate_device_ids(camera_high_only)
|
regenerate_device_ids(camera_high_only)
|
||||||
|
|
||||||
camera_medium_only = mock_camera.copy(deep=True)
|
camera_medium_only = mock_camera.copy()
|
||||||
camera_medium_only._api = mock_entry.api
|
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[0]._api = mock_entry.api
|
||||||
camera_medium_only.channels[1]._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.channels[2]._api = mock_entry.api
|
||||||
|
@ -272,8 +274,9 @@ async def test_basic_setup(
|
||||||
camera_medium_only.channels[2].is_rtsp_enabled = False
|
camera_medium_only.channels[2].is_rtsp_enabled = False
|
||||||
regenerate_device_ids(camera_medium_only)
|
regenerate_device_ids(camera_medium_only)
|
||||||
|
|
||||||
camera_all_channels = mock_camera.copy(deep=True)
|
camera_all_channels = mock_camera.copy()
|
||||||
camera_all_channels._api = mock_entry.api
|
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[0]._api = mock_entry.api
|
||||||
camera_all_channels.channels[1]._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.channels[2]._api = mock_entry.api
|
||||||
|
@ -289,8 +292,9 @@ async def test_basic_setup(
|
||||||
camera_all_channels.channels[2].rtsp_alias = "test_low_alias"
|
camera_all_channels.channels[2].rtsp_alias = "test_low_alias"
|
||||||
regenerate_device_ids(camera_all_channels)
|
regenerate_device_ids(camera_all_channels)
|
||||||
|
|
||||||
camera_no_channels = mock_camera.copy(deep=True)
|
camera_no_channels = mock_camera.copy()
|
||||||
camera_no_channels._api = mock_entry.api
|
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[0]._api = mock_entry.api
|
||||||
camera_no_channels.channels[1]._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.channels[2]._api = mock_entry.api
|
||||||
|
@ -301,8 +305,9 @@ async def test_basic_setup(
|
||||||
camera_no_channels.channels[2].is_rtsp_enabled = False
|
camera_no_channels.channels[2].is_rtsp_enabled = False
|
||||||
regenerate_device_ids(camera_no_channels)
|
regenerate_device_ids(camera_no_channels)
|
||||||
|
|
||||||
camera_package = mock_camera.copy(deep=True)
|
camera_package = mock_camera.copy()
|
||||||
camera_package._api = mock_entry.api
|
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[0]._api = mock_entry.api
|
||||||
camera_package.channels[1]._api = mock_entry.api
|
camera_package.channels[1]._api = mock_entry.api
|
||||||
camera_package.channels[2]._api = mock_entry.api
|
camera_package.channels[2]._api = mock_entry.api
|
||||||
|
@ -313,7 +318,7 @@ async def test_basic_setup(
|
||||||
camera_package.channels[1].is_rtsp_enabled = False
|
camera_package.channels[1].is_rtsp_enabled = False
|
||||||
camera_package.channels[2].is_rtsp_enabled = False
|
camera_package.channels[2].is_rtsp_enabled = False
|
||||||
regenerate_device_ids(camera_package)
|
regenerate_device_ids(camera_package)
|
||||||
package_channel = camera_package.channels[0].copy(deep=True)
|
package_channel = camera_package.channels[0].copy()
|
||||||
package_channel.is_rtsp_enabled = False
|
package_channel.is_rtsp_enabled = False
|
||||||
package_channel.name = "Package Camera"
|
package_channel.name = "Package Camera"
|
||||||
package_channel.id = 3
|
package_channel.id = 3
|
||||||
|
@ -398,7 +403,7 @@ async def test_missing_channels(
|
||||||
):
|
):
|
||||||
"""Test setting up camera with no camera channels."""
|
"""Test setting up camera with no camera channels."""
|
||||||
|
|
||||||
camera = mock_camera.copy(deep=True)
|
camera = mock_camera.copy()
|
||||||
camera.channels = []
|
camera.channels = []
|
||||||
|
|
||||||
mock_entry.api.bootstrap.cameras = {camera.id: camera}
|
mock_entry.api.bootstrap.cameras = {camera.id: camera}
|
||||||
|
|
|
@ -7,7 +7,7 @@ from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from pyunifiprotect import NotAuthorized, NvrError
|
from pyunifiprotect import NotAuthorized, NvrError
|
||||||
from pyunifiprotect.data import NVR, Light
|
from pyunifiprotect.data import NVR, Bootstrap, Light
|
||||||
|
|
||||||
from homeassistant.components.unifiprotect.const import CONF_DISABLE_RTSP, DOMAIN
|
from homeassistant.components.unifiprotect.const import CONF_DISABLE_RTSP, DOMAIN
|
||||||
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
from homeassistant.config_entries import ConfigEntry, ConfigEntryState
|
||||||
|
@ -16,7 +16,7 @@ from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from . import _patch_discovery
|
from . import _patch_discovery
|
||||||
from .conftest import MockBootstrap, MockEntityFixture, regenerate_device_ids
|
from .conftest import MockEntityFixture, regenerate_device_ids
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ async def test_setup_multiple(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_entry: MockEntityFixture,
|
mock_entry: MockEntityFixture,
|
||||||
mock_client,
|
mock_client,
|
||||||
mock_bootstrap: MockBootstrap,
|
mock_bootstrap: Bootstrap,
|
||||||
):
|
):
|
||||||
"""Test working setup of unifiprotect entry."""
|
"""Test working setup of unifiprotect entry."""
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ async def light_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Light.__config__.validate_assignment = False
|
Light.__config__.validate_assignment = False
|
||||||
|
|
||||||
light_obj = mock_light.copy(deep=True)
|
light_obj = mock_light.copy()
|
||||||
light_obj._api = mock_entry.api
|
light_obj._api = mock_entry.api
|
||||||
light_obj.name = "Test Light"
|
light_obj.name = "Test Light"
|
||||||
light_obj.is_light_on = False
|
light_obj.is_light_on = False
|
||||||
|
|
|
@ -35,7 +35,7 @@ async def doorlock_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Doorlock.__config__.validate_assignment = False
|
Doorlock.__config__.validate_assignment = False
|
||||||
|
|
||||||
lock_obj = mock_doorlock.copy(deep=True)
|
lock_obj = mock_doorlock.copy()
|
||||||
lock_obj._api = mock_entry.api
|
lock_obj._api = mock_entry.api
|
||||||
lock_obj.name = "Test Lock"
|
lock_obj.name = "Test Lock"
|
||||||
lock_obj.lock_status = LockStatusType.OPEN
|
lock_obj.lock_status = LockStatusType.OPEN
|
||||||
|
|
|
@ -38,7 +38,7 @@ async def camera_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Camera.__config__.validate_assignment = False
|
Camera.__config__.validate_assignment = False
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
|
|
@ -23,6 +23,7 @@ from .conftest import (
|
||||||
MockEntityFixture,
|
MockEntityFixture,
|
||||||
assert_entity_counts,
|
assert_entity_counts,
|
||||||
ids_from_device_description,
|
ids_from_device_description,
|
||||||
|
reset_objects,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,13 +36,13 @@ async def light_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Light.__config__.validate_assignment = False
|
Light.__config__.validate_assignment = False
|
||||||
|
|
||||||
light_obj = mock_light.copy(deep=True)
|
light_obj = mock_light.copy()
|
||||||
light_obj._api = mock_entry.api
|
light_obj._api = mock_entry.api
|
||||||
light_obj.name = "Test Light"
|
light_obj.name = "Test Light"
|
||||||
light_obj.light_device_settings.pir_sensitivity = 45
|
light_obj.light_device_settings.pir_sensitivity = 45
|
||||||
light_obj.light_device_settings.pir_duration = timedelta(seconds=45)
|
light_obj.light_device_settings.pir_duration = timedelta(seconds=45)
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.lights = {
|
mock_entry.api.bootstrap.lights = {
|
||||||
light_obj.id: light_obj,
|
light_obj.id: light_obj,
|
||||||
}
|
}
|
||||||
|
@ -65,7 +66,7 @@ async def camera_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Camera.__config__.validate_assignment = False
|
Camera.__config__.validate_assignment = False
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -79,7 +80,7 @@ async def camera_fixture(
|
||||||
camera_obj.mic_volume = 0
|
camera_obj.mic_volume = 0
|
||||||
camera_obj.isp_settings.zoom_position = 0
|
camera_obj.isp_settings.zoom_position = 0
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.cameras = {
|
mock_entry.api.bootstrap.cameras = {
|
||||||
camera_obj.id: camera_obj,
|
camera_obj.id: camera_obj,
|
||||||
}
|
}
|
||||||
|
@ -103,12 +104,12 @@ async def doorlock_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Doorlock.__config__.validate_assignment = False
|
Doorlock.__config__.validate_assignment = False
|
||||||
|
|
||||||
lock_obj = mock_doorlock.copy(deep=True)
|
lock_obj = mock_doorlock.copy()
|
||||||
lock_obj._api = mock_entry.api
|
lock_obj._api = mock_entry.api
|
||||||
lock_obj.name = "Test Lock"
|
lock_obj.name = "Test Lock"
|
||||||
lock_obj.auto_close_time = timedelta(seconds=45)
|
lock_obj.auto_close_time = timedelta(seconds=45)
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.doorlocks = {
|
mock_entry.api.bootstrap.doorlocks = {
|
||||||
lock_obj.id: lock_obj,
|
lock_obj.id: lock_obj,
|
||||||
}
|
}
|
||||||
|
@ -174,7 +175,7 @@ async def test_number_setup_camera_none(
|
||||||
):
|
):
|
||||||
"""Test number entity setup for camera devices (no features)."""
|
"""Test number entity setup for camera devices (no features)."""
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -185,7 +186,7 @@ async def test_number_setup_camera_none(
|
||||||
# has_wdr is an the inverse of has HDR
|
# has_wdr is an the inverse of has HDR
|
||||||
camera_obj.feature_flags.has_hdr = True
|
camera_obj.feature_flags.has_hdr = True
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.cameras = {
|
mock_entry.api.bootstrap.cameras = {
|
||||||
camera_obj.id: camera_obj,
|
camera_obj.id: camera_obj,
|
||||||
}
|
}
|
||||||
|
@ -204,7 +205,7 @@ async def test_number_setup_camera_missing_attr(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Camera.__config__.validate_assignment = False
|
Camera.__config__.validate_assignment = False
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -214,7 +215,7 @@ async def test_number_setup_camera_missing_attr(
|
||||||
|
|
||||||
Camera.__config__.validate_assignment = True
|
Camera.__config__.validate_assignment = True
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.cameras = {
|
mock_entry.api.bootstrap.cameras = {
|
||||||
camera_obj.id: camera_obj,
|
camera_obj.id: camera_obj,
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ from .conftest import (
|
||||||
MockEntityFixture,
|
MockEntityFixture,
|
||||||
assert_entity_counts,
|
assert_entity_counts,
|
||||||
ids_from_device_description,
|
ids_from_device_description,
|
||||||
|
reset_objects,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -59,12 +60,12 @@ async def viewer_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Viewer.__config__.validate_assignment = False
|
Viewer.__config__.validate_assignment = False
|
||||||
|
|
||||||
viewer_obj = mock_viewer.copy(deep=True)
|
viewer_obj = mock_viewer.copy()
|
||||||
viewer_obj._api = mock_entry.api
|
viewer_obj._api = mock_entry.api
|
||||||
viewer_obj.name = "Test Viewer"
|
viewer_obj.name = "Test Viewer"
|
||||||
viewer_obj.liveview_id = mock_liveview.id
|
viewer_obj.liveview_id = mock_liveview.id
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.viewers = {
|
mock_entry.api.bootstrap.viewers = {
|
||||||
viewer_obj.id: viewer_obj,
|
viewer_obj.id: viewer_obj,
|
||||||
}
|
}
|
||||||
|
@ -89,7 +90,7 @@ async def camera_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Camera.__config__.validate_assignment = False
|
Camera.__config__.validate_assignment = False
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -102,7 +103,7 @@ async def camera_fixture(
|
||||||
camera_obj.lcd_message = None
|
camera_obj.lcd_message = None
|
||||||
camera_obj.chime_duration = 0
|
camera_obj.chime_duration = 0
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.cameras = {
|
mock_entry.api.bootstrap.cameras = {
|
||||||
camera_obj.id: camera_obj,
|
camera_obj.id: camera_obj,
|
||||||
}
|
}
|
||||||
|
@ -129,14 +130,14 @@ async def light_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Light.__config__.validate_assignment = False
|
Light.__config__.validate_assignment = False
|
||||||
|
|
||||||
light_obj = mock_light.copy(deep=True)
|
light_obj = mock_light.copy()
|
||||||
light_obj._api = mock_entry.api
|
light_obj._api = mock_entry.api
|
||||||
light_obj.name = "Test Light"
|
light_obj.name = "Test Light"
|
||||||
light_obj.camera_id = None
|
light_obj.camera_id = None
|
||||||
light_obj.light_mode_settings.mode = LightModeType.MOTION
|
light_obj.light_mode_settings.mode = LightModeType.MOTION
|
||||||
light_obj.light_mode_settings.enable_at = LightModeEnableType.DARK
|
light_obj.light_mode_settings.enable_at = LightModeEnableType.DARK
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.cameras = {camera.id: camera}
|
mock_entry.api.bootstrap.cameras = {camera.id: camera}
|
||||||
mock_entry.api.bootstrap.lights = {
|
mock_entry.api.bootstrap.lights = {
|
||||||
light_obj.id: light_obj,
|
light_obj.id: light_obj,
|
||||||
|
@ -161,7 +162,7 @@ async def camera_none_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Camera.__config__.validate_assignment = False
|
Camera.__config__.validate_assignment = False
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -172,7 +173,7 @@ async def camera_none_fixture(
|
||||||
camera_obj.recording_settings.mode = RecordingMode.ALWAYS
|
camera_obj.recording_settings.mode = RecordingMode.ALWAYS
|
||||||
camera_obj.isp_settings.ir_led_mode = IRLEDMode.AUTO
|
camera_obj.isp_settings.ir_led_mode = IRLEDMode.AUTO
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.cameras = {
|
mock_entry.api.bootstrap.cameras = {
|
||||||
camera_obj.id: camera_obj,
|
camera_obj.id: camera_obj,
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ from .conftest import (
|
||||||
assert_entity_counts,
|
assert_entity_counts,
|
||||||
enable_entity,
|
enable_entity,
|
||||||
ids_from_device_description,
|
ids_from_device_description,
|
||||||
|
reset_objects,
|
||||||
time_changed,
|
time_changed,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,7 +64,7 @@ async def sensor_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Sensor.__config__.validate_assignment = False
|
Sensor.__config__.validate_assignment = False
|
||||||
|
|
||||||
sensor_obj = mock_sensor.copy(deep=True)
|
sensor_obj = mock_sensor.copy()
|
||||||
sensor_obj._api = mock_entry.api
|
sensor_obj._api = mock_entry.api
|
||||||
sensor_obj.name = "Test Sensor"
|
sensor_obj.name = "Test Sensor"
|
||||||
sensor_obj.battery_status.percentage = 10.0
|
sensor_obj.battery_status.percentage = 10.0
|
||||||
|
@ -77,7 +78,7 @@ async def sensor_fixture(
|
||||||
sensor_obj.up_since = now
|
sensor_obj.up_since = now
|
||||||
sensor_obj.bluetooth_connection_state.signal_strength = -50.0
|
sensor_obj.bluetooth_connection_state.signal_strength = -50.0
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.sensors = {
|
mock_entry.api.bootstrap.sensors = {
|
||||||
sensor_obj.id: sensor_obj,
|
sensor_obj.id: sensor_obj,
|
||||||
}
|
}
|
||||||
|
@ -102,7 +103,7 @@ async def sensor_none_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Sensor.__config__.validate_assignment = False
|
Sensor.__config__.validate_assignment = False
|
||||||
|
|
||||||
sensor_obj = mock_sensor.copy(deep=True)
|
sensor_obj = mock_sensor.copy()
|
||||||
sensor_obj._api = mock_entry.api
|
sensor_obj._api = mock_entry.api
|
||||||
sensor_obj.name = "Test Sensor"
|
sensor_obj.name = "Test Sensor"
|
||||||
sensor_obj.battery_status.percentage = 10.0
|
sensor_obj.battery_status.percentage = 10.0
|
||||||
|
@ -113,7 +114,7 @@ async def sensor_none_fixture(
|
||||||
sensor_obj.up_since = now
|
sensor_obj.up_since = now
|
||||||
sensor_obj.bluetooth_connection_state.signal_strength = -50.0
|
sensor_obj.bluetooth_connection_state.signal_strength = -50.0
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.sensors = {
|
mock_entry.api.bootstrap.sensors = {
|
||||||
sensor_obj.id: sensor_obj,
|
sensor_obj.id: sensor_obj,
|
||||||
}
|
}
|
||||||
|
@ -141,7 +142,7 @@ async def camera_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Camera.__config__.validate_assignment = False
|
Camera.__config__.validate_assignment = False
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -162,7 +163,7 @@ async def camera_fixture(
|
||||||
camera_obj.stats.storage.rate = 0.1
|
camera_obj.stats.storage.rate = 0.1
|
||||||
camera_obj.voltage = 20.0
|
camera_obj.voltage = 20.0
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
mock_entry.api.bootstrap.nvr.system_info.storage.devices = []
|
||||||
mock_entry.api.bootstrap.cameras = {
|
mock_entry.api.bootstrap.cameras = {
|
||||||
camera_obj.id: camera_obj,
|
camera_obj.id: camera_obj,
|
||||||
|
@ -262,7 +263,7 @@ async def test_sensor_setup_nvr(
|
||||||
):
|
):
|
||||||
"""Test sensor entity setup for NVR device."""
|
"""Test sensor entity setup for NVR device."""
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
nvr: NVR = mock_entry.api.bootstrap.nvr
|
nvr: NVR = mock_entry.api.bootstrap.nvr
|
||||||
nvr.up_since = now
|
nvr.up_since = now
|
||||||
nvr.system_info.cpu.average_load = 50.0
|
nvr.system_info.cpu.average_load = 50.0
|
||||||
|
@ -339,7 +340,7 @@ async def test_sensor_nvr_missing_values(
|
||||||
):
|
):
|
||||||
"""Test NVR sensor sensors if no data available."""
|
"""Test NVR sensor sensors if no data available."""
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
nvr: NVR = mock_entry.api.bootstrap.nvr
|
nvr: NVR = mock_entry.api.bootstrap.nvr
|
||||||
nvr.system_info.memory.available = None
|
nvr.system_info.memory.available = None
|
||||||
nvr.system_info.memory.total = None
|
nvr.system_info.memory.total = None
|
||||||
|
|
|
@ -5,8 +5,8 @@ from __future__ import annotations
|
||||||
from unittest.mock import AsyncMock, Mock
|
from unittest.mock import AsyncMock, Mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pyunifiprotect.data import Camera, Light, ModelType
|
from pyunifiprotect.data import Camera, Chime, Light, ModelType
|
||||||
from pyunifiprotect.data.devices import Chime
|
from pyunifiprotect.data.bootstrap import ProtectDeviceRef
|
||||||
from pyunifiprotect.exceptions import BadRequest
|
from pyunifiprotect.exceptions import BadRequest
|
||||||
|
|
||||||
from homeassistant.components.unifiprotect.const import ATTR_MESSAGE, DOMAIN
|
from homeassistant.components.unifiprotect.const import ATTR_MESSAGE, DOMAIN
|
||||||
|
@ -163,6 +163,11 @@ async def test_set_chime_paired_doorbells(
|
||||||
mock_entry.api.bootstrap.chimes = {
|
mock_entry.api.bootstrap.chimes = {
|
||||||
mock_chime.id: mock_chime,
|
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 = mock_camera.copy()
|
||||||
camera1.name = "Test Camera 1"
|
camera1.name = "Test Camera 1"
|
||||||
|
@ -186,6 +191,12 @@ async def test_set_chime_paired_doorbells(
|
||||||
camera1.id: camera1,
|
camera1.id: camera1,
|
||||||
camera2.id: camera2,
|
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.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
|
@ -28,6 +28,7 @@ from .conftest import (
|
||||||
assert_entity_counts,
|
assert_entity_counts,
|
||||||
enable_entity,
|
enable_entity,
|
||||||
ids_from_device_description,
|
ids_from_device_description,
|
||||||
|
reset_objects,
|
||||||
)
|
)
|
||||||
|
|
||||||
CAMERA_SWITCHES_BASIC = [
|
CAMERA_SWITCHES_BASIC = [
|
||||||
|
@ -51,13 +52,13 @@ async def light_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Light.__config__.validate_assignment = False
|
Light.__config__.validate_assignment = False
|
||||||
|
|
||||||
light_obj = mock_light.copy(deep=True)
|
light_obj = mock_light.copy()
|
||||||
light_obj._api = mock_entry.api
|
light_obj._api = mock_entry.api
|
||||||
light_obj.name = "Test Light"
|
light_obj.name = "Test Light"
|
||||||
light_obj.is_ssh_enabled = False
|
light_obj.is_ssh_enabled = False
|
||||||
light_obj.light_device_settings.is_indicator_enabled = False
|
light_obj.light_device_settings.is_indicator_enabled = False
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.lights = {
|
mock_entry.api.bootstrap.lights = {
|
||||||
light_obj.id: light_obj,
|
light_obj.id: light_obj,
|
||||||
}
|
}
|
||||||
|
@ -81,7 +82,7 @@ async def camera_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Camera.__config__.validate_assignment = False
|
Camera.__config__.validate_assignment = False
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -110,7 +111,7 @@ async def camera_fixture(
|
||||||
camera_obj.osd_settings.is_debug_enabled = False
|
camera_obj.osd_settings.is_debug_enabled = False
|
||||||
camera_obj.smart_detect_settings.object_types = []
|
camera_obj.smart_detect_settings.object_types = []
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.cameras = {
|
mock_entry.api.bootstrap.cameras = {
|
||||||
camera_obj.id: camera_obj,
|
camera_obj.id: camera_obj,
|
||||||
}
|
}
|
||||||
|
@ -134,7 +135,7 @@ async def camera_none_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Camera.__config__.validate_assignment = False
|
Camera.__config__.validate_assignment = False
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -153,7 +154,7 @@ async def camera_none_fixture(
|
||||||
camera_obj.osd_settings.is_logo_enabled = False
|
camera_obj.osd_settings.is_logo_enabled = False
|
||||||
camera_obj.osd_settings.is_debug_enabled = False
|
camera_obj.osd_settings.is_debug_enabled = False
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.cameras = {
|
mock_entry.api.bootstrap.cameras = {
|
||||||
camera_obj.id: camera_obj,
|
camera_obj.id: camera_obj,
|
||||||
}
|
}
|
||||||
|
@ -177,7 +178,8 @@ async def camera_privacy_fixture(
|
||||||
# disable pydantic validation so mocking can happen
|
# disable pydantic validation so mocking can happen
|
||||||
Camera.__config__.validate_assignment = False
|
Camera.__config__.validate_assignment = False
|
||||||
|
|
||||||
camera_obj = mock_camera.copy(deep=True)
|
# mock_camera._update_lock = None
|
||||||
|
camera_obj = mock_camera.copy()
|
||||||
camera_obj._api = mock_entry.api
|
camera_obj._api = mock_entry.api
|
||||||
camera_obj.channels[0]._api = mock_entry.api
|
camera_obj.channels[0]._api = mock_entry.api
|
||||||
camera_obj.channels[1]._api = mock_entry.api
|
camera_obj.channels[1]._api = mock_entry.api
|
||||||
|
@ -197,7 +199,7 @@ async def camera_privacy_fixture(
|
||||||
camera_obj.osd_settings.is_logo_enabled = False
|
camera_obj.osd_settings.is_logo_enabled = False
|
||||||
camera_obj.osd_settings.is_debug_enabled = False
|
camera_obj.osd_settings.is_debug_enabled = False
|
||||||
|
|
||||||
mock_entry.api.bootstrap.reset_objects()
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
mock_entry.api.bootstrap.cameras = {
|
mock_entry.api.bootstrap.cameras = {
|
||||||
camera_obj.id: camera_obj,
|
camera_obj.id: camera_obj,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue