mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
UniFi Protect bugfixes (#74156)
This commit is contained in:
parent
309cf030b0
commit
54320ff134
@ -271,7 +271,7 @@ SENSE_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = (
|
||||
ufp_perm=PermRequired.NO_WRITE,
|
||||
),
|
||||
ProtectBinaryEntityDescription(
|
||||
key="motion",
|
||||
key="motion_enabled",
|
||||
name="Motion Detection",
|
||||
icon="mdi:walk",
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
@ -383,7 +383,9 @@ async def async_setup_entry(
|
||||
entities += _async_motion_entities(data, ufp_device=device)
|
||||
async_add_entities(entities)
|
||||
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
entry.async_on_unload(
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
)
|
||||
|
||||
entities: list[ProtectDeviceEntity] = async_all_device_entities(
|
||||
data,
|
||||
|
@ -92,7 +92,9 @@ async def async_setup_entry(
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
entry.async_on_unload(
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
)
|
||||
|
||||
entities: list[ProtectDeviceEntity] = async_all_device_entities(
|
||||
data,
|
||||
|
@ -120,8 +120,12 @@ async def async_setup_entry(
|
||||
entities = _async_camera_entities(data, ufp_device=device)
|
||||
async_add_entities(entities)
|
||||
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_CHANNELS), _add_new_device)
|
||||
entry.async_on_unload(
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
)
|
||||
entry.async_on_unload(
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_CHANNELS), _add_new_device)
|
||||
)
|
||||
|
||||
entities = _async_camera_entities(data)
|
||||
async_add_entities(entities)
|
||||
|
@ -4,17 +4,17 @@ from __future__ import annotations
|
||||
from collections.abc import Callable, Generator, Iterable
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, Union
|
||||
|
||||
from pyunifiprotect import ProtectApiClient
|
||||
from pyunifiprotect.data import (
|
||||
NVR,
|
||||
Bootstrap,
|
||||
Event,
|
||||
EventType,
|
||||
Liveview,
|
||||
ModelType,
|
||||
ProtectAdoptableDeviceModel,
|
||||
ProtectModelWithId,
|
||||
WSSubscriptionMessage,
|
||||
)
|
||||
from pyunifiprotect.exceptions import ClientError, NotAuthorized
|
||||
@ -27,7 +27,6 @@ from homeassistant.helpers.event import async_track_time_interval
|
||||
from .const import (
|
||||
CONF_DISABLE_RTSP,
|
||||
DEVICES_THAT_ADOPT,
|
||||
DEVICES_WITH_ENTITIES,
|
||||
DISPATCH_ADOPT,
|
||||
DISPATCH_CHANNELS,
|
||||
DOMAIN,
|
||||
@ -39,6 +38,7 @@ from .utils import (
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
ProtectDeviceType = Union[ProtectAdoptableDeviceModel, NVR]
|
||||
|
||||
|
||||
@callback
|
||||
@ -68,7 +68,7 @@ class ProtectData:
|
||||
self._entry = entry
|
||||
self._hass = hass
|
||||
self._update_interval = update_interval
|
||||
self._subscriptions: dict[str, list[Callable[[ProtectModelWithId], None]]] = {}
|
||||
self._subscriptions: dict[str, list[Callable[[ProtectDeviceType], None]]] = {}
|
||||
self._pending_camera_ids: set[str] = set()
|
||||
self._unsub_interval: CALLBACK_TYPE | None = None
|
||||
self._unsub_websocket: CALLBACK_TYPE | None = None
|
||||
@ -152,7 +152,7 @@ class ProtectData:
|
||||
return
|
||||
|
||||
obj = message.new_obj
|
||||
if obj.model in DEVICES_WITH_ENTITIES:
|
||||
if isinstance(obj, (ProtectAdoptableDeviceModel, NVR)):
|
||||
self._async_signal_device_update(obj)
|
||||
if (
|
||||
obj.model == ModelType.CAMERA
|
||||
@ -211,41 +211,41 @@ class ProtectData:
|
||||
|
||||
@callback
|
||||
def async_subscribe_device_id(
|
||||
self, device_id: str, update_callback: Callable[[ProtectModelWithId], None]
|
||||
self, mac: str, update_callback: Callable[[ProtectDeviceType], None]
|
||||
) -> CALLBACK_TYPE:
|
||||
"""Add an callback subscriber."""
|
||||
if not self._subscriptions:
|
||||
self._unsub_interval = async_track_time_interval(
|
||||
self._hass, self.async_refresh, self._update_interval
|
||||
)
|
||||
self._subscriptions.setdefault(device_id, []).append(update_callback)
|
||||
self._subscriptions.setdefault(mac, []).append(update_callback)
|
||||
|
||||
def _unsubscribe() -> None:
|
||||
self.async_unsubscribe_device_id(device_id, update_callback)
|
||||
self.async_unsubscribe_device_id(mac, update_callback)
|
||||
|
||||
return _unsubscribe
|
||||
|
||||
@callback
|
||||
def async_unsubscribe_device_id(
|
||||
self, device_id: str, update_callback: Callable[[ProtectModelWithId], None]
|
||||
self, mac: str, update_callback: Callable[[ProtectDeviceType], None]
|
||||
) -> None:
|
||||
"""Remove a callback subscriber."""
|
||||
self._subscriptions[device_id].remove(update_callback)
|
||||
if not self._subscriptions[device_id]:
|
||||
del self._subscriptions[device_id]
|
||||
self._subscriptions[mac].remove(update_callback)
|
||||
if not self._subscriptions[mac]:
|
||||
del self._subscriptions[mac]
|
||||
if not self._subscriptions and self._unsub_interval:
|
||||
self._unsub_interval()
|
||||
self._unsub_interval = None
|
||||
|
||||
@callback
|
||||
def _async_signal_device_update(self, device: ProtectModelWithId) -> None:
|
||||
def _async_signal_device_update(self, device: ProtectDeviceType) -> None:
|
||||
"""Call the callbacks for a device_id."""
|
||||
device_id = device.id
|
||||
if not self._subscriptions.get(device_id):
|
||||
|
||||
if not self._subscriptions.get(device.mac):
|
||||
return
|
||||
|
||||
_LOGGER.debug("Updating device: %s", device_id)
|
||||
for update_callback in self._subscriptions[device_id]:
|
||||
_LOGGER.debug("Updating device: %s (%s)", device.name, device.mac)
|
||||
for update_callback in self._subscriptions[device.mac]:
|
||||
update_callback(device)
|
||||
|
||||
|
||||
|
@ -215,7 +215,7 @@ class ProtectDeviceEntity(Entity):
|
||||
await super().async_added_to_hass()
|
||||
self.async_on_remove(
|
||||
self.data.async_subscribe_device_id(
|
||||
self.device.id, self._async_updated_event
|
||||
self.device.mac, self._async_updated_event
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -42,7 +42,9 @@ async def async_setup_entry(
|
||||
):
|
||||
async_add_entities([ProtectLight(data, device)])
|
||||
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
entry.async_on_unload(
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
)
|
||||
|
||||
entities = []
|
||||
for device in data.api.bootstrap.lights.values():
|
||||
|
@ -40,7 +40,9 @@ async def async_setup_entry(
|
||||
if isinstance(device, Doorlock):
|
||||
async_add_entities([ProtectLock(data, device)])
|
||||
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
entry.async_on_unload(
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
)
|
||||
|
||||
entities = []
|
||||
for device in data.api.bootstrap.doorlocks.values():
|
||||
|
@ -49,7 +49,9 @@ async def async_setup_entry(
|
||||
if isinstance(device, Camera) and device.feature_flags.has_speaker:
|
||||
async_add_entities([ProtectMediaPlayer(data, device)])
|
||||
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
entry.async_on_unload(
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
)
|
||||
|
||||
entities = []
|
||||
for device in data.api.bootstrap.cameras.values():
|
||||
|
@ -206,7 +206,9 @@ async def async_setup_entry(
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
entry.async_on_unload(
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
)
|
||||
|
||||
entities: list[ProtectDeviceEntity] = async_all_device_entities(
|
||||
data,
|
||||
|
@ -336,7 +336,9 @@ async def async_setup_entry(
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
entry.async_on_unload(
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
)
|
||||
|
||||
entities: list[ProtectDeviceEntity] = async_all_device_entities(
|
||||
data,
|
||||
|
@ -613,7 +613,9 @@ async def async_setup_entry(
|
||||
entities += _async_motion_entities(data, ufp_device=device)
|
||||
async_add_entities(entities)
|
||||
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
entry.async_on_unload(
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
)
|
||||
|
||||
entities: list[ProtectDeviceEntity] = async_all_device_entities(
|
||||
data,
|
||||
|
@ -318,7 +318,9 @@ async def async_setup_entry(
|
||||
)
|
||||
async_add_entities(entities)
|
||||
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
entry.async_on_unload(
|
||||
async_dispatcher_connect(hass, _ufpd(entry, DISPATCH_ADOPT), _add_new_device)
|
||||
)
|
||||
|
||||
entities: list[ProtectDeviceEntity] = async_all_device_entities(
|
||||
data,
|
||||
|
@ -109,11 +109,10 @@ async def test_reload(hass: HomeAssistant, ufp: MockUFPFixture):
|
||||
assert ufp.api.async_disconnect_ws.called
|
||||
|
||||
|
||||
async def test_unload(hass: HomeAssistant, ufp: MockUFPFixture):
|
||||
async def test_unload(hass: HomeAssistant, ufp: MockUFPFixture, light: Light):
|
||||
"""Test unloading of unifiprotect entry."""
|
||||
|
||||
await hass.config_entries.async_setup(ufp.entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
await init_entry(hass, ufp, [light])
|
||||
assert ufp.entry.state == ConfigEntryState.LOADED
|
||||
|
||||
await hass.config_entries.async_unload(ufp.entry.entry_id)
|
||||
|
Loading…
x
Reference in New Issue
Block a user