mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 14:17:45 +00:00
Merge unifiprotect entity and models modules (#126532)
This commit is contained in:
parent
0e0ac3efe5
commit
de88068c66
@ -29,12 +29,14 @@ from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
|
|||||||
from .entity import (
|
from .entity import (
|
||||||
BaseProtectEntity,
|
BaseProtectEntity,
|
||||||
EventEntityMixin,
|
EventEntityMixin,
|
||||||
|
PermRequired,
|
||||||
ProtectDeviceEntity,
|
ProtectDeviceEntity,
|
||||||
|
ProtectEntityDescription,
|
||||||
|
ProtectEventMixin,
|
||||||
ProtectIsOnEntity,
|
ProtectIsOnEntity,
|
||||||
ProtectNVREntity,
|
ProtectNVREntity,
|
||||||
async_all_device_entities,
|
async_all_device_entities,
|
||||||
)
|
)
|
||||||
from .models import PermRequired, ProtectEntityDescription, ProtectEventMixin
|
|
||||||
|
|
||||||
_KEY_DOOR = "door"
|
_KEY_DOOR = "door"
|
||||||
|
|
||||||
|
@ -23,8 +23,14 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
|
|
||||||
from .const import DEVICES_THAT_ADOPT, DOMAIN
|
from .const import DEVICES_THAT_ADOPT, DOMAIN
|
||||||
from .data import ProtectDeviceType, UFPConfigEntry
|
from .data import ProtectDeviceType, UFPConfigEntry
|
||||||
from .entity import ProtectDeviceEntity, async_all_device_entities
|
from .entity import (
|
||||||
from .models import PermRequired, ProtectEntityDescription, ProtectSetableKeysMixin, T
|
PermRequired,
|
||||||
|
ProtectDeviceEntity,
|
||||||
|
ProtectEntityDescription,
|
||||||
|
ProtectSetableKeysMixin,
|
||||||
|
T,
|
||||||
|
async_all_device_entities,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -2,14 +2,24 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable, Sequence
|
from collections.abc import Callable, Coroutine, Sequence
|
||||||
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from enum import Enum
|
||||||
from functools import partial
|
from functools import partial
|
||||||
import logging
|
import logging
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING, Any, Generic, TypeVar
|
||||||
|
|
||||||
from uiprotect.data import NVR, Event, ModelType, ProtectAdoptableDeviceModel, StateType
|
from uiprotect import make_enabled_getter, make_required_getter, make_value_getter
|
||||||
|
from uiprotect.data import (
|
||||||
|
NVR,
|
||||||
|
Event,
|
||||||
|
ModelType,
|
||||||
|
ProtectAdoptableDeviceModel,
|
||||||
|
SmartDetectObjectType,
|
||||||
|
StateType,
|
||||||
|
)
|
||||||
|
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
import homeassistant.helpers.device_registry as dr
|
import homeassistant.helpers.device_registry as dr
|
||||||
@ -24,10 +34,19 @@ from .const import (
|
|||||||
DOMAIN,
|
DOMAIN,
|
||||||
)
|
)
|
||||||
from .data import ProtectData, ProtectDeviceType
|
from .data import ProtectData, ProtectDeviceType
|
||||||
from .models import PermRequired, ProtectEntityDescription, ProtectEventMixin
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
T = TypeVar("T", bound=ProtectAdoptableDeviceModel | NVR)
|
||||||
|
|
||||||
|
|
||||||
|
class PermRequired(int, Enum):
|
||||||
|
"""Type of permission level required for entity."""
|
||||||
|
|
||||||
|
NO_WRITE = 1
|
||||||
|
WRITE = 2
|
||||||
|
DELETE = 3
|
||||||
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def _async_device_entities(
|
def _async_device_entities(
|
||||||
@ -352,3 +371,82 @@ class EventEntityMixin(ProtectDeviceEntity):
|
|||||||
and prev_event_end
|
and prev_event_end
|
||||||
and prev_event.id == event.id
|
and prev_event.id == event.id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class ProtectEntityDescription(EntityDescription, Generic[T]):
|
||||||
|
"""Base class for protect entity descriptions."""
|
||||||
|
|
||||||
|
ufp_required_field: str | None = None
|
||||||
|
ufp_value: str | None = None
|
||||||
|
ufp_value_fn: Callable[[T], Any] | None = None
|
||||||
|
ufp_enabled: str | None = None
|
||||||
|
ufp_perm: PermRequired | None = None
|
||||||
|
|
||||||
|
# The below are set in __post_init__
|
||||||
|
has_required: Callable[[T], bool] = bool
|
||||||
|
get_ufp_enabled: Callable[[T], bool] | None = None
|
||||||
|
|
||||||
|
def get_ufp_value(self, obj: T) -> Any:
|
||||||
|
"""Return value from UniFi Protect device; overridden in __post_init__."""
|
||||||
|
# ufp_value or ufp_value_fn are required, the
|
||||||
|
# RuntimeError is to catch any issues in the code
|
||||||
|
# with new descriptions.
|
||||||
|
raise RuntimeError( # pragma: no cover
|
||||||
|
f"`ufp_value` or `ufp_value_fn` is required for {self}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
"""Override get_ufp_value, has_required, and get_ufp_enabled if required."""
|
||||||
|
_setter = partial(object.__setattr__, self)
|
||||||
|
|
||||||
|
if (ufp_value := self.ufp_value) is not None:
|
||||||
|
_setter("get_ufp_value", make_value_getter(ufp_value))
|
||||||
|
elif (ufp_value_fn := self.ufp_value_fn) is not None:
|
||||||
|
_setter("get_ufp_value", ufp_value_fn)
|
||||||
|
|
||||||
|
if (ufp_enabled := self.ufp_enabled) is not None:
|
||||||
|
_setter("get_ufp_enabled", make_enabled_getter(ufp_enabled))
|
||||||
|
|
||||||
|
if (ufp_required_field := self.ufp_required_field) is not None:
|
||||||
|
_setter("has_required", make_required_getter(ufp_required_field))
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class ProtectEventMixin(ProtectEntityDescription[T]):
|
||||||
|
"""Mixin for events."""
|
||||||
|
|
||||||
|
ufp_event_obj: str | None = None
|
||||||
|
ufp_obj_type: SmartDetectObjectType | None = None
|
||||||
|
|
||||||
|
def get_event_obj(self, obj: T) -> Event | None:
|
||||||
|
"""Return value from UniFi Protect device."""
|
||||||
|
return None
|
||||||
|
|
||||||
|
def has_matching_smart(self, event: Event) -> bool:
|
||||||
|
"""Determine if the detection type is a match."""
|
||||||
|
return (
|
||||||
|
not (obj_type := self.ufp_obj_type) or obj_type in event.smart_detect_types
|
||||||
|
)
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
"""Override get_event_obj if ufp_event_obj is set."""
|
||||||
|
if (_ufp_event_obj := self.ufp_event_obj) is not None:
|
||||||
|
object.__setattr__(self, "get_event_obj", attrgetter(_ufp_event_obj))
|
||||||
|
super().__post_init__()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
class ProtectSetableKeysMixin(ProtectEntityDescription[T]):
|
||||||
|
"""Mixin for settable values."""
|
||||||
|
|
||||||
|
ufp_set_method: str | None = None
|
||||||
|
ufp_set_method_fn: Callable[[T, Any], Coroutine[Any, Any, None]] | None = None
|
||||||
|
|
||||||
|
async def ufp_set(self, obj: T, value: Any) -> None:
|
||||||
|
"""Set value for UniFi Protect device."""
|
||||||
|
_LOGGER.debug("Setting %s to %s for %s", self.name, value, obj.display_name)
|
||||||
|
if self.ufp_set_method is not None:
|
||||||
|
await getattr(obj, self.ufp_set_method)(value)
|
||||||
|
elif self.ufp_set_method_fn is not None:
|
||||||
|
await self.ufp_set_method_fn(obj, value)
|
||||||
|
@ -16,8 +16,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
|
|
||||||
from .const import ATTR_EVENT_ID
|
from .const import ATTR_EVENT_ID
|
||||||
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
|
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
|
||||||
from .entity import EventEntityMixin, ProtectDeviceEntity
|
from .entity import EventEntityMixin, ProtectDeviceEntity, ProtectEventMixin
|
||||||
from .models import ProtectEventMixin
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(frozen=True, kw_only=True)
|
@dataclasses.dataclass(frozen=True, kw_only=True)
|
||||||
|
@ -1,112 +0,0 @@
|
|||||||
"""The unifiprotect integration models."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from collections.abc import Callable, Coroutine
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from enum import Enum
|
|
||||||
from functools import partial
|
|
||||||
import logging
|
|
||||||
from operator import attrgetter
|
|
||||||
from typing import Any, Generic, TypeVar
|
|
||||||
|
|
||||||
from uiprotect import make_enabled_getter, make_required_getter, make_value_getter
|
|
||||||
from uiprotect.data import (
|
|
||||||
NVR,
|
|
||||||
Event,
|
|
||||||
ProtectAdoptableDeviceModel,
|
|
||||||
SmartDetectObjectType,
|
|
||||||
)
|
|
||||||
|
|
||||||
from homeassistant.helpers.entity import EntityDescription
|
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
T = TypeVar("T", bound=ProtectAdoptableDeviceModel | NVR)
|
|
||||||
|
|
||||||
|
|
||||||
class PermRequired(int, Enum):
|
|
||||||
"""Type of permission level required for entity."""
|
|
||||||
|
|
||||||
NO_WRITE = 1
|
|
||||||
WRITE = 2
|
|
||||||
DELETE = 3
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
|
||||||
class ProtectEntityDescription(EntityDescription, Generic[T]):
|
|
||||||
"""Base class for protect entity descriptions."""
|
|
||||||
|
|
||||||
ufp_required_field: str | None = None
|
|
||||||
ufp_value: str | None = None
|
|
||||||
ufp_value_fn: Callable[[T], Any] | None = None
|
|
||||||
ufp_enabled: str | None = None
|
|
||||||
ufp_perm: PermRequired | None = None
|
|
||||||
|
|
||||||
# The below are set in __post_init__
|
|
||||||
has_required: Callable[[T], bool] = bool
|
|
||||||
get_ufp_enabled: Callable[[T], bool] | None = None
|
|
||||||
|
|
||||||
def get_ufp_value(self, obj: T) -> Any:
|
|
||||||
"""Return value from UniFi Protect device; overridden in __post_init__."""
|
|
||||||
# ufp_value or ufp_value_fn are required, the
|
|
||||||
# RuntimeError is to catch any issues in the code
|
|
||||||
# with new descriptions.
|
|
||||||
raise RuntimeError( # pragma: no cover
|
|
||||||
f"`ufp_value` or `ufp_value_fn` is required for {self}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
|
||||||
"""Override get_ufp_value, has_required, and get_ufp_enabled if required."""
|
|
||||||
_setter = partial(object.__setattr__, self)
|
|
||||||
|
|
||||||
if (ufp_value := self.ufp_value) is not None:
|
|
||||||
_setter("get_ufp_value", make_value_getter(ufp_value))
|
|
||||||
elif (ufp_value_fn := self.ufp_value_fn) is not None:
|
|
||||||
_setter("get_ufp_value", ufp_value_fn)
|
|
||||||
|
|
||||||
if (ufp_enabled := self.ufp_enabled) is not None:
|
|
||||||
_setter("get_ufp_enabled", make_enabled_getter(ufp_enabled))
|
|
||||||
|
|
||||||
if (ufp_required_field := self.ufp_required_field) is not None:
|
|
||||||
_setter("has_required", make_required_getter(ufp_required_field))
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
|
||||||
class ProtectEventMixin(ProtectEntityDescription[T]):
|
|
||||||
"""Mixin for events."""
|
|
||||||
|
|
||||||
ufp_event_obj: str | None = None
|
|
||||||
ufp_obj_type: SmartDetectObjectType | None = None
|
|
||||||
|
|
||||||
def get_event_obj(self, obj: T) -> Event | None:
|
|
||||||
"""Return value from UniFi Protect device."""
|
|
||||||
return None
|
|
||||||
|
|
||||||
def has_matching_smart(self, event: Event) -> bool:
|
|
||||||
"""Determine if the detection type is a match."""
|
|
||||||
return (
|
|
||||||
not (obj_type := self.ufp_obj_type) or obj_type in event.smart_detect_types
|
|
||||||
)
|
|
||||||
|
|
||||||
def __post_init__(self) -> None:
|
|
||||||
"""Override get_event_obj if ufp_event_obj is set."""
|
|
||||||
if (_ufp_event_obj := self.ufp_event_obj) is not None:
|
|
||||||
object.__setattr__(self, "get_event_obj", attrgetter(_ufp_event_obj))
|
|
||||||
super().__post_init__()
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
|
||||||
class ProtectSetableKeysMixin(ProtectEntityDescription[T]):
|
|
||||||
"""Mixin for settable values."""
|
|
||||||
|
|
||||||
ufp_set_method: str | None = None
|
|
||||||
ufp_set_method_fn: Callable[[T, Any], Coroutine[Any, Any, None]] | None = None
|
|
||||||
|
|
||||||
async def ufp_set(self, obj: T, value: Any) -> None:
|
|
||||||
"""Set value for UniFi Protect device."""
|
|
||||||
_LOGGER.debug("Setting %s to %s for %s", self.name, value, obj.display_name)
|
|
||||||
if self.ufp_set_method is not None:
|
|
||||||
await getattr(obj, self.ufp_set_method)(value)
|
|
||||||
elif self.ufp_set_method_fn is not None:
|
|
||||||
await self.ufp_set_method_fn(obj, value)
|
|
@ -20,8 +20,14 @@ from homeassistant.core import HomeAssistant, callback
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
|
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
|
||||||
from .entity import ProtectDeviceEntity, async_all_device_entities
|
from .entity import (
|
||||||
from .models import PermRequired, ProtectEntityDescription, ProtectSetableKeysMixin, T
|
PermRequired,
|
||||||
|
ProtectDeviceEntity,
|
||||||
|
ProtectEntityDescription,
|
||||||
|
ProtectSetableKeysMixin,
|
||||||
|
T,
|
||||||
|
async_all_device_entities,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
@ -33,8 +33,14 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
|
|
||||||
from .const import TYPE_EMPTY_VALUE
|
from .const import TYPE_EMPTY_VALUE
|
||||||
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
|
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
|
||||||
from .entity import ProtectDeviceEntity, async_all_device_entities
|
from .entity import (
|
||||||
from .models import PermRequired, ProtectEntityDescription, ProtectSetableKeysMixin, T
|
PermRequired,
|
||||||
|
ProtectDeviceEntity,
|
||||||
|
ProtectEntityDescription,
|
||||||
|
ProtectSetableKeysMixin,
|
||||||
|
T,
|
||||||
|
async_all_device_entities,
|
||||||
|
)
|
||||||
from .utils import async_get_light_motion_current
|
from .utils import async_get_light_motion_current
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -44,11 +44,14 @@ from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
|
|||||||
from .entity import (
|
from .entity import (
|
||||||
BaseProtectEntity,
|
BaseProtectEntity,
|
||||||
EventEntityMixin,
|
EventEntityMixin,
|
||||||
|
PermRequired,
|
||||||
ProtectDeviceEntity,
|
ProtectDeviceEntity,
|
||||||
|
ProtectEntityDescription,
|
||||||
|
ProtectEventMixin,
|
||||||
ProtectNVREntity,
|
ProtectNVREntity,
|
||||||
|
T,
|
||||||
async_all_device_entities,
|
async_all_device_entities,
|
||||||
)
|
)
|
||||||
from .models import PermRequired, ProtectEntityDescription, ProtectEventMixin, T
|
|
||||||
from .utils import async_get_light_motion_current
|
from .utils import async_get_light_motion_current
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -24,12 +24,15 @@ from homeassistant.helpers.restore_state import RestoreEntity
|
|||||||
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
|
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
|
||||||
from .entity import (
|
from .entity import (
|
||||||
BaseProtectEntity,
|
BaseProtectEntity,
|
||||||
|
PermRequired,
|
||||||
ProtectDeviceEntity,
|
ProtectDeviceEntity,
|
||||||
|
ProtectEntityDescription,
|
||||||
ProtectIsOnEntity,
|
ProtectIsOnEntity,
|
||||||
ProtectNVREntity,
|
ProtectNVREntity,
|
||||||
|
ProtectSetableKeysMixin,
|
||||||
|
T,
|
||||||
async_all_device_entities,
|
async_all_device_entities,
|
||||||
)
|
)
|
||||||
from .models import PermRequired, ProtectEntityDescription, ProtectSetableKeysMixin, T
|
|
||||||
|
|
||||||
ATTR_PREV_MIC = "prev_mic_level"
|
ATTR_PREV_MIC = "prev_mic_level"
|
||||||
ATTR_PREV_RECORD = "prev_record_mode"
|
ATTR_PREV_RECORD = "prev_record_mode"
|
||||||
|
@ -18,8 +18,14 @@ from homeassistant.core import HomeAssistant, callback
|
|||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
|
||||||
from .data import ProtectDeviceType, UFPConfigEntry
|
from .data import ProtectDeviceType, UFPConfigEntry
|
||||||
from .entity import ProtectDeviceEntity, async_all_device_entities
|
from .entity import (
|
||||||
from .models import PermRequired, ProtectEntityDescription, ProtectSetableKeysMixin, T
|
PermRequired,
|
||||||
|
ProtectDeviceEntity,
|
||||||
|
ProtectEntityDescription,
|
||||||
|
ProtectSetableKeysMixin,
|
||||||
|
T,
|
||||||
|
async_all_device_entities,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, kw_only=True)
|
@dataclass(frozen=True, kw_only=True)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user