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.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.const import (
|
||||
|
@ -68,7 +69,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
|||
nvr_info = await protect.get_nvr()
|
||||
except NotAuthorized as err:
|
||||
raise ConfigEntryAuthFailed(err) from err
|
||||
except (asyncio.TimeoutError, NvrError, ServerDisconnectedError) as err:
|
||||
except (asyncio.TimeoutError, ClientError, ServerDisconnectedError) as err:
|
||||
raise ConfigEntryNotReady from err
|
||||
|
||||
if nvr_info.version < MIN_REQUIRED_PROTECT_V:
|
||||
|
|
|
@ -6,8 +6,9 @@ import logging
|
|||
from typing import Any
|
||||
|
||||
from aiohttp import CookieJar
|
||||
from pyunifiprotect import NotAuthorized, NvrError, ProtectApiClient
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
from pyunifiprotect.data import NVR
|
||||
from pyunifiprotect.exceptions import ClientError, NotAuthorized
|
||||
from unifi_discovery import async_console_is_alive
|
||||
import voluptuous as vol
|
||||
|
||||
|
@ -253,7 +254,7 @@ class ProtectFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|||
except NotAuthorized as ex:
|
||||
_LOGGER.debug(ex)
|
||||
errors[CONF_PASSWORD] = "invalid_auth"
|
||||
except NvrError as ex:
|
||||
except ClientError as ex:
|
||||
_LOGGER.debug(ex)
|
||||
errors["base"] = "cannot_connect"
|
||||
else:
|
||||
|
|
|
@ -6,7 +6,7 @@ from datetime import timedelta
|
|||
import logging
|
||||
from typing import Any
|
||||
|
||||
from pyunifiprotect import NotAuthorized, NvrError, ProtectApiClient
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
from pyunifiprotect.data import (
|
||||
Bootstrap,
|
||||
Event,
|
||||
|
@ -15,6 +15,7 @@ from pyunifiprotect.data import (
|
|||
WSSubscriptionMessage,
|
||||
)
|
||||
from pyunifiprotect.data.base import ProtectAdoptableDeviceModel
|
||||
from pyunifiprotect.exceptions import ClientError, NotAuthorized
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback
|
||||
|
@ -100,23 +101,27 @@ class ProtectData:
|
|||
|
||||
try:
|
||||
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:
|
||||
await self.async_stop()
|
||||
_LOGGER.exception("Reauthentication required")
|
||||
self._entry.async_start_reauth(self._hass)
|
||||
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:
|
||||
self.last_update_success = True
|
||||
self._async_process_updates(updates)
|
||||
|
||||
@callback
|
||||
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:
|
||||
self.async_signal_device_id_update(message.new_obj.id)
|
||||
# trigger update for all Cameras with LCD screens when NVR Doorbell settings updates
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"name": "UniFi Protect",
|
||||
"config_flow": true,
|
||||
"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"],
|
||||
"codeowners": ["@briis", "@AngellusMortis", "@bdraco"],
|
||||
"quality_scale": "platinum",
|
||||
|
|
|
@ -8,7 +8,7 @@ from typing import Any, cast
|
|||
from pydantic import ValidationError
|
||||
from pyunifiprotect.api import ProtectApiClient
|
||||
from pyunifiprotect.data import Chime
|
||||
from pyunifiprotect.exceptions import BadRequest
|
||||
from pyunifiprotect.exceptions import ClientError
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
|
||||
|
@ -100,7 +100,7 @@ async def _async_service_call_nvr(
|
|||
await asyncio.gather(
|
||||
*(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
|
||||
|
||||
|
||||
|
|
|
@ -1990,7 +1990,7 @@ pytrafikverket==0.2.0.1
|
|||
pyudev==0.22.0
|
||||
|
||||
# homeassistant.components.unifiprotect
|
||||
pyunifiprotect==3.9.2
|
||||
pyunifiprotect==4.0.4
|
||||
|
||||
# homeassistant.components.uptimerobot
|
||||
pyuptimerobot==22.2.0
|
||||
|
|
|
@ -1325,7 +1325,7 @@ pytrafikverket==0.2.0.1
|
|||
pyudev==0.22.0
|
||||
|
||||
# homeassistant.components.unifiprotect
|
||||
pyunifiprotect==3.9.2
|
||||
pyunifiprotect==4.0.4
|
||||
|
||||
# homeassistant.components.uptimerobot
|
||||
pyuptimerobot==22.2.0
|
||||
|
|
|
@ -13,6 +13,7 @@ from unittest.mock import AsyncMock, Mock, patch
|
|||
import pytest
|
||||
from pyunifiprotect.data import (
|
||||
NVR,
|
||||
Bootstrap,
|
||||
Camera,
|
||||
Chime,
|
||||
Doorlock,
|
||||
|
@ -39,69 +40,6 @@ from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture
|
|||
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
|
||||
class MockEntityFixture:
|
||||
"""Mock for NVR."""
|
||||
|
@ -155,27 +93,42 @@ def mock_old_nvr_fixture():
|
|||
@pytest.fixture(name="mock_bootstrap")
|
||||
def mock_bootstrap_fixture(mock_nvr: NVR):
|
||||
"""Mock Bootstrap fixture."""
|
||||
return MockBootstrap(
|
||||
nvr=mock_nvr,
|
||||
cameras={},
|
||||
lights={},
|
||||
sensors={},
|
||||
viewers={},
|
||||
liveviews={},
|
||||
events={},
|
||||
doorlocks={},
|
||||
chimes={},
|
||||
)
|
||||
data = json.loads(load_fixture("sample_bootstrap.json", integration=DOMAIN))
|
||||
data["nvr"] = mock_nvr
|
||||
data["cameras"] = []
|
||||
data["lights"] = []
|
||||
data["sensors"] = []
|
||||
data["viewers"] = []
|
||||
data["liveviews"] = []
|
||||
data["events"] = []
|
||||
data["doorlocks"] = []
|
||||
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
|
||||
def mock_client(mock_bootstrap: MockBootstrap):
|
||||
def mock_client(mock_bootstrap: Bootstrap):
|
||||
"""Mock ProtectApiClient for testing."""
|
||||
client = Mock()
|
||||
client.bootstrap = mock_bootstrap
|
||||
|
||||
nvr = mock_bootstrap.nvr
|
||||
nvr = client.bootstrap.nvr
|
||||
nvr._api = client
|
||||
client.bootstrap._api = client
|
||||
|
||||
client.base_url = "https://127.0.0.1"
|
||||
client.connection_host = IPv4Address("127.0.0.1")
|
||||
|
|
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,
|
||||
assert_entity_counts,
|
||||
ids_from_device_description,
|
||||
reset_objects,
|
||||
)
|
||||
|
||||
|
||||
|
@ -51,7 +52,7 @@ async def camera_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.channels[0]._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_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.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
|
@ -87,14 +88,14 @@ async def light_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.name = "Test Light"
|
||||
light_obj.is_dark = False
|
||||
light_obj.is_pir_motion_detected = False
|
||||
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.lights = {
|
||||
light_obj.id: light_obj,
|
||||
|
@ -119,7 +120,7 @@ async def camera_none_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.channels[0]._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_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.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
|
@ -157,7 +158,7 @@ async def sensor_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.name = "Test Sensor"
|
||||
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.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.sensors = {
|
||||
sensor_obj.id: sensor_obj,
|
||||
|
@ -198,7 +199,7 @@ async def sensor_none_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.name = "Test Sensor"
|
||||
sensor_obj.mount_type = MountType.LEAK
|
||||
|
@ -206,7 +207,7 @@ async def sensor_none_fixture(
|
|||
sensor_obj.alarm_settings.is_enabled = False
|
||||
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.sensors = {
|
||||
sensor_obj.id: sensor_obj,
|
||||
|
|
|
@ -21,7 +21,7 @@ async def chime_fixture(
|
|||
):
|
||||
"""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.name = "Test Chime"
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ async def camera_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.channels[0]._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."""
|
||||
|
||||
camera_obj = mock_camera.copy(deep=True)
|
||||
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
|
||||
|
@ -95,7 +95,7 @@ async def camera_package_fixture(
|
|||
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(deep=True)
|
||||
package_channel = camera_obj.channels[0].copy()
|
||||
package_channel.is_rtsp_enabled = False
|
||||
package_channel.name = "Package Camera"
|
||||
package_channel.id = 3
|
||||
|
@ -246,8 +246,9 @@ async def test_basic_setup(
|
|||
):
|
||||
"""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.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
|
||||
|
@ -259,8 +260,9 @@ async def test_basic_setup(
|
|||
camera_high_only.channels[2].is_rtsp_enabled = False
|
||||
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.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
|
||||
|
@ -272,8 +274,9 @@ async def test_basic_setup(
|
|||
camera_medium_only.channels[2].is_rtsp_enabled = False
|
||||
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.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
|
||||
|
@ -289,8 +292,9 @@ async def test_basic_setup(
|
|||
camera_all_channels.channels[2].rtsp_alias = "test_low_alias"
|
||||
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.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
|
||||
|
@ -301,8 +305,9 @@ async def test_basic_setup(
|
|||
camera_no_channels.channels[2].is_rtsp_enabled = False
|
||||
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.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
|
||||
|
@ -313,7 +318,7 @@ async def test_basic_setup(
|
|||
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(deep=True)
|
||||
package_channel = camera_package.channels[0].copy()
|
||||
package_channel.is_rtsp_enabled = False
|
||||
package_channel.name = "Package Camera"
|
||||
package_channel.id = 3
|
||||
|
@ -398,7 +403,7 @@ async def test_missing_channels(
|
|||
):
|
||||
"""Test setting up camera with no camera channels."""
|
||||
|
||||
camera = mock_camera.copy(deep=True)
|
||||
camera = mock_camera.copy()
|
||||
camera.channels = []
|
||||
|
||||
mock_entry.api.bootstrap.cameras = {camera.id: camera}
|
||||
|
|
|
@ -7,7 +7,7 @@ from unittest.mock import AsyncMock, patch
|
|||
|
||||
import aiohttp
|
||||
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.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 . import _patch_discovery
|
||||
from .conftest import MockBootstrap, MockEntityFixture, regenerate_device_ids
|
||||
from .conftest import MockEntityFixture, regenerate_device_ids
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
@ -52,7 +52,7 @@ async def test_setup_multiple(
|
|||
hass: HomeAssistant,
|
||||
mock_entry: MockEntityFixture,
|
||||
mock_client,
|
||||
mock_bootstrap: MockBootstrap,
|
||||
mock_bootstrap: Bootstrap,
|
||||
):
|
||||
"""Test working setup of unifiprotect entry."""
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ async def light_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.name = "Test Light"
|
||||
light_obj.is_light_on = False
|
||||
|
|
|
@ -35,7 +35,7 @@ async def doorlock_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.name = "Test Lock"
|
||||
lock_obj.lock_status = LockStatusType.OPEN
|
||||
|
|
|
@ -38,7 +38,7 @@ async def camera_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.channels[0]._api = mock_entry.api
|
||||
camera_obj.channels[1]._api = mock_entry.api
|
||||
|
|
|
@ -23,6 +23,7 @@ from .conftest import (
|
|||
MockEntityFixture,
|
||||
assert_entity_counts,
|
||||
ids_from_device_description,
|
||||
reset_objects,
|
||||
)
|
||||
|
||||
|
||||
|
@ -35,13 +36,13 @@ async def light_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.name = "Test Light"
|
||||
light_obj.light_device_settings.pir_sensitivity = 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 = {
|
||||
light_obj.id: light_obj,
|
||||
}
|
||||
|
@ -65,7 +66,7 @@ async def camera_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.channels[0]._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.isp_settings.zoom_position = 0
|
||||
|
||||
mock_entry.api.bootstrap.reset_objects()
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
@ -103,12 +104,12 @@ async def doorlock_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.name = "Test Lock"
|
||||
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 = {
|
||||
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)."""
|
||||
|
||||
camera_obj = mock_camera.copy(deep=True)
|
||||
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
|
||||
|
@ -185,7 +186,7 @@ async def test_number_setup_camera_none(
|
|||
# has_wdr is an the inverse of has HDR
|
||||
camera_obj.feature_flags.has_hdr = True
|
||||
|
||||
mock_entry.api.bootstrap.reset_objects()
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
@ -204,7 +205,7 @@ async def test_number_setup_camera_missing_attr(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.channels[0]._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
|
||||
|
||||
mock_entry.api.bootstrap.reset_objects()
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ from .conftest import (
|
|||
MockEntityFixture,
|
||||
assert_entity_counts,
|
||||
ids_from_device_description,
|
||||
reset_objects,
|
||||
)
|
||||
|
||||
|
||||
|
@ -59,12 +60,12 @@ async def viewer_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.name = "Test Viewer"
|
||||
viewer_obj.liveview_id = mock_liveview.id
|
||||
|
||||
mock_entry.api.bootstrap.reset_objects()
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.viewers = {
|
||||
viewer_obj.id: viewer_obj,
|
||||
}
|
||||
|
@ -89,7 +90,7 @@ async def camera_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.channels[0]._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.chime_duration = 0
|
||||
|
||||
mock_entry.api.bootstrap.reset_objects()
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
@ -129,14 +130,14 @@ async def light_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.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
|
||||
|
||||
mock_entry.api.bootstrap.reset_objects()
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {camera.id: camera}
|
||||
mock_entry.api.bootstrap.lights = {
|
||||
light_obj.id: light_obj,
|
||||
|
@ -161,7 +162,7 @@ async def camera_none_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.channels[0]._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.isp_settings.ir_led_mode = IRLEDMode.AUTO
|
||||
|
||||
mock_entry.api.bootstrap.reset_objects()
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ from .conftest import (
|
|||
assert_entity_counts,
|
||||
enable_entity,
|
||||
ids_from_device_description,
|
||||
reset_objects,
|
||||
time_changed,
|
||||
)
|
||||
|
||||
|
@ -63,7 +64,7 @@ async def sensor_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.name = "Test Sensor"
|
||||
sensor_obj.battery_status.percentage = 10.0
|
||||
|
@ -77,7 +78,7 @@ async def sensor_fixture(
|
|||
sensor_obj.up_since = now
|
||||
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 = {
|
||||
sensor_obj.id: sensor_obj,
|
||||
}
|
||||
|
@ -102,7 +103,7 @@ async def sensor_none_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.name = "Test Sensor"
|
||||
sensor_obj.battery_status.percentage = 10.0
|
||||
|
@ -113,7 +114,7 @@ async def sensor_none_fixture(
|
|||
sensor_obj.up_since = now
|
||||
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 = {
|
||||
sensor_obj.id: sensor_obj,
|
||||
}
|
||||
|
@ -141,7 +142,7 @@ async def camera_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.channels[0]._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.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.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
|
@ -262,7 +263,7 @@ async def test_sensor_setup_nvr(
|
|||
):
|
||||
"""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.up_since = now
|
||||
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."""
|
||||
|
||||
mock_entry.api.bootstrap.reset_objects()
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
nvr: NVR = mock_entry.api.bootstrap.nvr
|
||||
nvr.system_info.memory.available = None
|
||||
nvr.system_info.memory.total = None
|
||||
|
|
|
@ -5,8 +5,8 @@ from __future__ import annotations
|
|||
from unittest.mock import AsyncMock, Mock
|
||||
|
||||
import pytest
|
||||
from pyunifiprotect.data import Camera, Light, ModelType
|
||||
from pyunifiprotect.data.devices import Chime
|
||||
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
|
||||
|
@ -163,6 +163,11 @@ async def test_set_chime_paired_doorbells(
|
|||
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.name = "Test Camera 1"
|
||||
|
@ -186,6 +191,12 @@ async def test_set_chime_paired_doorbells(
|
|||
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()
|
||||
|
|
|
@ -28,6 +28,7 @@ from .conftest import (
|
|||
assert_entity_counts,
|
||||
enable_entity,
|
||||
ids_from_device_description,
|
||||
reset_objects,
|
||||
)
|
||||
|
||||
CAMERA_SWITCHES_BASIC = [
|
||||
|
@ -51,13 +52,13 @@ async def light_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.name = "Test Light"
|
||||
light_obj.is_ssh_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 = {
|
||||
light_obj.id: light_obj,
|
||||
}
|
||||
|
@ -81,7 +82,7 @@ async def camera_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.channels[0]._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.smart_detect_settings.object_types = []
|
||||
|
||||
mock_entry.api.bootstrap.reset_objects()
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
@ -134,7 +135,7 @@ async def camera_none_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.channels[0]._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_debug_enabled = False
|
||||
|
||||
mock_entry.api.bootstrap.reset_objects()
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
@ -177,7 +178,8 @@ async def camera_privacy_fixture(
|
|||
# disable pydantic validation so mocking can happen
|
||||
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.channels[0]._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_debug_enabled = False
|
||||
|
||||
mock_entry.api.bootstrap.reset_objects()
|
||||
reset_objects(mock_entry.api.bootstrap)
|
||||
mock_entry.api.bootstrap.cameras = {
|
||||
camera_obj.id: camera_obj,
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue