mirror of
https://github.com/home-assistant/core.git
synced 2025-04-25 17:57:55 +00:00
Cleans up various asserts/type ignores for UniFi Protect (#63824)
Co-authored-by: epenet <epenet@users.noreply.github.com>
This commit is contained in:
parent
b9cfaae3de
commit
259befa65f
@ -4,9 +4,9 @@ from __future__ import annotations
|
|||||||
from collections.abc import Callable, Coroutine
|
from collections.abc import Callable, Coroutine
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any, Generic, TypeVar
|
||||||
|
|
||||||
from pyunifiprotect.data import NVR, ProtectAdoptableDeviceModel
|
from pyunifiprotect.data import ProtectDeviceModel
|
||||||
|
|
||||||
from homeassistant.helpers.entity import EntityDescription
|
from homeassistant.helpers.entity import EntityDescription
|
||||||
|
|
||||||
@ -14,17 +14,19 @@ from .utils import get_nested_attr
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
T = TypeVar("T", bound=ProtectDeviceModel)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ProtectRequiredKeysMixin:
|
class ProtectRequiredKeysMixin(Generic[T]):
|
||||||
"""Mixin for required keys."""
|
"""Mixin for required keys."""
|
||||||
|
|
||||||
ufp_required_field: str | None = None
|
ufp_required_field: str | None = None
|
||||||
ufp_value: str | None = None
|
ufp_value: str | None = None
|
||||||
ufp_value_fn: Callable[[ProtectAdoptableDeviceModel | NVR], Any] | None = None
|
ufp_value_fn: Callable[[T], Any] | None = None
|
||||||
ufp_enabled: str | None = None
|
ufp_enabled: str | None = None
|
||||||
|
|
||||||
def get_ufp_value(self, obj: ProtectAdoptableDeviceModel | NVR) -> Any:
|
def get_ufp_value(self, obj: T) -> Any:
|
||||||
"""Return value from UniFi Protect device."""
|
"""Return value from UniFi Protect device."""
|
||||||
if self.ufp_value is not None:
|
if self.ufp_value is not None:
|
||||||
return get_nested_attr(obj, self.ufp_value)
|
return get_nested_attr(obj, self.ufp_value)
|
||||||
@ -36,7 +38,7 @@ class ProtectRequiredKeysMixin:
|
|||||||
"`ufp_value` or `ufp_value_fn` is required"
|
"`ufp_value` or `ufp_value_fn` is required"
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_ufp_enabled(self, obj: ProtectAdoptableDeviceModel | NVR) -> bool:
|
def get_ufp_enabled(self, obj: T) -> bool:
|
||||||
"""Return value from UniFi Protect device."""
|
"""Return value from UniFi Protect device."""
|
||||||
if self.ufp_enabled is not None:
|
if self.ufp_enabled is not None:
|
||||||
return bool(get_nested_attr(obj, self.ufp_enabled))
|
return bool(get_nested_attr(obj, self.ufp_enabled))
|
||||||
@ -44,15 +46,13 @@ class ProtectRequiredKeysMixin:
|
|||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ProtectSetableKeysMixin(ProtectRequiredKeysMixin):
|
class ProtectSetableKeysMixin(ProtectRequiredKeysMixin, Generic[T]):
|
||||||
"""Mixin to for settable values."""
|
"""Mixin for settable values."""
|
||||||
|
|
||||||
ufp_set_method: str | None = None
|
ufp_set_method: str | None = None
|
||||||
ufp_set_method_fn: Callable[
|
ufp_set_method_fn: Callable[[T, Any], Coroutine[Any, Any, None]] | None = None
|
||||||
[ProtectAdoptableDeviceModel, Any], Coroutine[Any, Any, None]
|
|
||||||
] | None = None
|
|
||||||
|
|
||||||
async def ufp_set(self, obj: ProtectAdoptableDeviceModel, value: Any) -> None:
|
async def ufp_set(self, obj: T, value: Any) -> None:
|
||||||
"""Set value for UniFi Protect device."""
|
"""Set value for UniFi Protect device."""
|
||||||
assert isinstance(self, EntityDescription)
|
assert isinstance(self, EntityDescription)
|
||||||
_LOGGER.debug("Setting %s to %s for %s", self.name, value, obj.name)
|
_LOGGER.debug("Setting %s to %s for %s", self.name, value, obj.name)
|
||||||
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Any
|
from typing import Generic
|
||||||
|
|
||||||
from pyunifiprotect.data.devices import Camera, Light
|
from pyunifiprotect.data.devices import Camera, Light
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .data import ProtectData
|
from .data import ProtectData
|
||||||
from .entity import ProtectDeviceEntity, async_all_device_entities
|
from .entity import ProtectDeviceEntity, async_all_device_entities
|
||||||
from .models import ProtectSetableKeysMixin
|
from .models import ProtectSetableKeysMixin, T
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -30,18 +30,16 @@ class NumberKeysMixin:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ProtectNumberEntityDescription(
|
class ProtectNumberEntityDescription(
|
||||||
ProtectSetableKeysMixin, NumberEntityDescription, NumberKeysMixin
|
ProtectSetableKeysMixin, NumberEntityDescription, NumberKeysMixin, Generic[T]
|
||||||
):
|
):
|
||||||
"""Describes UniFi Protect Number entity."""
|
"""Describes UniFi Protect Number entity."""
|
||||||
|
|
||||||
|
|
||||||
def _get_pir_duration(obj: Any) -> int:
|
def _get_pir_duration(obj: Light) -> int:
|
||||||
assert isinstance(obj, Light)
|
|
||||||
return int(obj.light_device_settings.pir_duration.total_seconds())
|
return int(obj.light_device_settings.pir_duration.total_seconds())
|
||||||
|
|
||||||
|
|
||||||
async def _set_pir_duration(obj: Any, value: float) -> None:
|
async def _set_pir_duration(obj: Light, value: float) -> None:
|
||||||
assert isinstance(obj, Light)
|
|
||||||
await obj.set_duration(timedelta(seconds=value))
|
await obj.set_duration(timedelta(seconds=value))
|
||||||
|
|
||||||
|
|
||||||
@ -97,7 +95,7 @@ LIGHT_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = (
|
|||||||
ufp_value="light_device_settings.pir_sensitivity",
|
ufp_value="light_device_settings.pir_sensitivity",
|
||||||
ufp_set_method="set_sensitivity",
|
ufp_set_method="set_sensitivity",
|
||||||
),
|
),
|
||||||
ProtectNumberEntityDescription(
|
ProtectNumberEntityDescription[Light](
|
||||||
key="duration",
|
key="duration",
|
||||||
name="Auto-shutoff Duration",
|
name="Auto-shutoff Duration",
|
||||||
icon="mdi:camera-timer",
|
icon="mdi:camera-timer",
|
||||||
|
@ -6,7 +6,7 @@ from dataclasses import dataclass
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Final
|
from typing import Any, Final, Generic
|
||||||
|
|
||||||
from pyunifiprotect.api import ProtectApiClient
|
from pyunifiprotect.api import ProtectApiClient
|
||||||
from pyunifiprotect.data import (
|
from pyunifiprotect.data import (
|
||||||
@ -19,9 +19,7 @@ from pyunifiprotect.data import (
|
|||||||
RecordingMode,
|
RecordingMode,
|
||||||
Viewer,
|
Viewer,
|
||||||
)
|
)
|
||||||
from pyunifiprotect.data.base import ProtectAdoptableDeviceModel
|
|
||||||
from pyunifiprotect.data.devices import Sensor
|
from pyunifiprotect.data.devices import Sensor
|
||||||
from pyunifiprotect.data.nvr import NVR
|
|
||||||
from pyunifiprotect.data.types import ChimeType, MountType
|
from pyunifiprotect.data.types import ChimeType, MountType
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
@ -37,7 +35,7 @@ from homeassistant.util.dt import utcnow
|
|||||||
from .const import ATTR_DURATION, ATTR_MESSAGE, DOMAIN, TYPE_EMPTY_VALUE
|
from .const import ATTR_DURATION, ATTR_MESSAGE, DOMAIN, TYPE_EMPTY_VALUE
|
||||||
from .data import ProtectData
|
from .data import ProtectData
|
||||||
from .entity import ProtectDeviceEntity, async_all_device_entities
|
from .entity import ProtectDeviceEntity, async_all_device_entities
|
||||||
from .models import ProtectSetableKeysMixin
|
from .models import ProtectSetableKeysMixin, T
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_KEY_LIGHT_MOTION = "light_motion"
|
_KEY_LIGHT_MOTION = "light_motion"
|
||||||
@ -104,15 +102,14 @@ SET_DOORBELL_LCD_MESSAGE_SCHEMA = vol.Schema(
|
|||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ProtectSelectEntityDescription(ProtectSetableKeysMixin, SelectEntityDescription):
|
class ProtectSelectEntityDescription(
|
||||||
|
ProtectSetableKeysMixin, SelectEntityDescription, Generic[T]
|
||||||
|
):
|
||||||
"""Describes UniFi Protect Select entity."""
|
"""Describes UniFi Protect Select entity."""
|
||||||
|
|
||||||
ufp_options: list[dict[str, Any]] | None = None
|
ufp_options: list[dict[str, Any]] | None = None
|
||||||
ufp_options_callable: Callable[
|
ufp_options_fn: Callable[[ProtectApiClient], list[dict[str, Any]]] | None = None
|
||||||
[ProtectApiClient], list[dict[str, Any]]
|
|
||||||
] | None = None
|
|
||||||
ufp_enum_type: type[Enum] | None = None
|
ufp_enum_type: type[Enum] | None = None
|
||||||
ufp_set_method: str | None = None
|
|
||||||
|
|
||||||
|
|
||||||
def _get_viewer_options(api: ProtectApiClient) -> list[dict[str, Any]]:
|
def _get_viewer_options(api: ProtectApiClient) -> list[dict[str, Any]]:
|
||||||
@ -140,13 +137,11 @@ def _get_paired_camera_options(api: ProtectApiClient) -> list[dict[str, Any]]:
|
|||||||
return options
|
return options
|
||||||
|
|
||||||
|
|
||||||
def _get_viewer_current(obj: Any) -> str:
|
def _get_viewer_current(obj: Viewer) -> str:
|
||||||
assert isinstance(obj, Viewer)
|
|
||||||
return obj.liveview_id
|
return obj.liveview_id
|
||||||
|
|
||||||
|
|
||||||
def _get_light_motion_current(obj: Any) -> str:
|
def _get_light_motion_current(obj: Light) -> str:
|
||||||
assert isinstance(obj, Light)
|
|
||||||
# a bit of extra to allow On Motion Always/Dark
|
# a bit of extra to allow On Motion Always/Dark
|
||||||
if (
|
if (
|
||||||
obj.light_mode_settings.mode == LightModeType.MOTION
|
obj.light_mode_settings.mode == LightModeType.MOTION
|
||||||
@ -156,15 +151,13 @@ def _get_light_motion_current(obj: Any) -> str:
|
|||||||
return obj.light_mode_settings.mode.value
|
return obj.light_mode_settings.mode.value
|
||||||
|
|
||||||
|
|
||||||
def _get_doorbell_current(obj: Any) -> str | None:
|
def _get_doorbell_current(obj: Camera) -> str | None:
|
||||||
assert isinstance(obj, Camera)
|
|
||||||
if obj.lcd_message is None:
|
if obj.lcd_message is None:
|
||||||
return None
|
return None
|
||||||
return obj.lcd_message.text
|
return obj.lcd_message.text
|
||||||
|
|
||||||
|
|
||||||
async def _set_light_mode(obj: Any, mode: str) -> None:
|
async def _set_light_mode(obj: Light, mode: str) -> None:
|
||||||
assert isinstance(obj, Light)
|
|
||||||
lightmode, timing = LIGHT_MODE_TO_SETTINGS[mode]
|
lightmode, timing = LIGHT_MODE_TO_SETTINGS[mode]
|
||||||
await obj.set_light_settings(
|
await obj.set_light_settings(
|
||||||
LightModeType(lightmode),
|
LightModeType(lightmode),
|
||||||
@ -172,10 +165,7 @@ async def _set_light_mode(obj: Any, mode: str) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _set_paired_camera(
|
async def _set_paired_camera(obj: Light | Sensor, camera_id: str) -> None:
|
||||||
obj: ProtectAdoptableDeviceModel | NVR, camera_id: str
|
|
||||||
) -> None:
|
|
||||||
assert isinstance(obj, (Sensor, Light))
|
|
||||||
if camera_id == TYPE_EMPTY_VALUE:
|
if camera_id == TYPE_EMPTY_VALUE:
|
||||||
camera: Camera | None = None
|
camera: Camera | None = None
|
||||||
else:
|
else:
|
||||||
@ -183,8 +173,7 @@ async def _set_paired_camera(
|
|||||||
await obj.set_paired_camera(camera)
|
await obj.set_paired_camera(camera)
|
||||||
|
|
||||||
|
|
||||||
async def _set_doorbell_message(obj: Any, message: str) -> None:
|
async def _set_doorbell_message(obj: Camera, message: str) -> None:
|
||||||
assert isinstance(obj, Camera)
|
|
||||||
if message.startswith(DoorbellMessageType.CUSTOM_MESSAGE.value):
|
if message.startswith(DoorbellMessageType.CUSTOM_MESSAGE.value):
|
||||||
await obj.set_lcd_text(DoorbellMessageType.CUSTOM_MESSAGE, text=message)
|
await obj.set_lcd_text(DoorbellMessageType.CUSTOM_MESSAGE, text=message)
|
||||||
elif message == TYPE_EMPTY_VALUE:
|
elif message == TYPE_EMPTY_VALUE:
|
||||||
@ -193,8 +182,7 @@ async def _set_doorbell_message(obj: Any, message: str) -> None:
|
|||||||
await obj.set_lcd_text(DoorbellMessageType(message))
|
await obj.set_lcd_text(DoorbellMessageType(message))
|
||||||
|
|
||||||
|
|
||||||
async def _set_liveview(obj: Any, liveview_id: str) -> None:
|
async def _set_liveview(obj: Viewer, liveview_id: str) -> None:
|
||||||
assert isinstance(obj, Viewer)
|
|
||||||
liveview = obj.api.bootstrap.liveviews[liveview_id]
|
liveview = obj.api.bootstrap.liveviews[liveview_id]
|
||||||
await obj.set_liveview(liveview)
|
await obj.set_liveview(liveview)
|
||||||
|
|
||||||
@ -221,7 +209,7 @@ CAMERA_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
|||||||
ufp_value="isp_settings.ir_led_mode",
|
ufp_value="isp_settings.ir_led_mode",
|
||||||
ufp_set_method="set_ir_led_model",
|
ufp_set_method="set_ir_led_model",
|
||||||
),
|
),
|
||||||
ProtectSelectEntityDescription(
|
ProtectSelectEntityDescription[Camera](
|
||||||
key="doorbell_text",
|
key="doorbell_text",
|
||||||
name="Doorbell Text",
|
name="Doorbell Text",
|
||||||
icon="mdi:card-text",
|
icon="mdi:card-text",
|
||||||
@ -229,7 +217,7 @@ CAMERA_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
|||||||
device_class=DEVICE_CLASS_LCD_MESSAGE,
|
device_class=DEVICE_CLASS_LCD_MESSAGE,
|
||||||
ufp_required_field="feature_flags.has_lcd_screen",
|
ufp_required_field="feature_flags.has_lcd_screen",
|
||||||
ufp_value_fn=_get_doorbell_current,
|
ufp_value_fn=_get_doorbell_current,
|
||||||
ufp_options_callable=_get_doorbell_options,
|
ufp_options_fn=_get_doorbell_options,
|
||||||
ufp_set_method_fn=_set_doorbell_message,
|
ufp_set_method_fn=_set_doorbell_message,
|
||||||
),
|
),
|
||||||
ProtectSelectEntityDescription(
|
ProtectSelectEntityDescription(
|
||||||
@ -246,7 +234,7 @@ CAMERA_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
LIGHT_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
LIGHT_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
||||||
ProtectSelectEntityDescription(
|
ProtectSelectEntityDescription[Light](
|
||||||
key=_KEY_LIGHT_MOTION,
|
key=_KEY_LIGHT_MOTION,
|
||||||
name="Light Mode",
|
name="Light Mode",
|
||||||
icon="mdi:spotlight",
|
icon="mdi:spotlight",
|
||||||
@ -255,13 +243,13 @@ LIGHT_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
|||||||
ufp_value_fn=_get_light_motion_current,
|
ufp_value_fn=_get_light_motion_current,
|
||||||
ufp_set_method_fn=_set_light_mode,
|
ufp_set_method_fn=_set_light_mode,
|
||||||
),
|
),
|
||||||
ProtectSelectEntityDescription(
|
ProtectSelectEntityDescription[Light](
|
||||||
key="paired_camera",
|
key="paired_camera",
|
||||||
name="Paired Camera",
|
name="Paired Camera",
|
||||||
icon="mdi:cctv",
|
icon="mdi:cctv",
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="camera_id",
|
ufp_value="camera_id",
|
||||||
ufp_options_callable=_get_paired_camera_options,
|
ufp_options_fn=_get_paired_camera_options,
|
||||||
ufp_set_method_fn=_set_paired_camera,
|
ufp_set_method_fn=_set_paired_camera,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -277,24 +265,24 @@ SENSE_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
|||||||
ufp_value="mount_type",
|
ufp_value="mount_type",
|
||||||
ufp_set_method="set_mount_type",
|
ufp_set_method="set_mount_type",
|
||||||
),
|
),
|
||||||
ProtectSelectEntityDescription(
|
ProtectSelectEntityDescription[Sensor](
|
||||||
key="paired_camera",
|
key="paired_camera",
|
||||||
name="Paired Camera",
|
name="Paired Camera",
|
||||||
icon="mdi:cctv",
|
icon="mdi:cctv",
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="camera_id",
|
ufp_value="camera_id",
|
||||||
ufp_options_callable=_get_paired_camera_options,
|
ufp_options_fn=_get_paired_camera_options,
|
||||||
ufp_set_method_fn=_set_paired_camera,
|
ufp_set_method_fn=_set_paired_camera,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
VIEWER_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
VIEWER_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
||||||
ProtectSelectEntityDescription(
|
ProtectSelectEntityDescription[Viewer](
|
||||||
key="viewer",
|
key="viewer",
|
||||||
name="Liveview",
|
name="Liveview",
|
||||||
icon="mdi:view-dashboard",
|
icon="mdi:view-dashboard",
|
||||||
entity_category=None,
|
entity_category=None,
|
||||||
ufp_options_callable=_get_viewer_options,
|
ufp_options_fn=_get_viewer_options,
|
||||||
ufp_value_fn=_get_viewer_current,
|
ufp_value_fn=_get_viewer_current,
|
||||||
ufp_set_method_fn=_set_liveview,
|
ufp_set_method_fn=_set_liveview,
|
||||||
),
|
),
|
||||||
@ -350,7 +338,7 @@ class ProtectSelects(ProtectDeviceEntity, SelectEntity):
|
|||||||
# entities with categories are not exposed for voice and safe to update dynamically
|
# entities with categories are not exposed for voice and safe to update dynamically
|
||||||
if (
|
if (
|
||||||
self.entity_description.entity_category is not None
|
self.entity_description.entity_category is not None
|
||||||
and self.entity_description.ufp_options_callable is not None
|
and self.entity_description.ufp_options_fn is not None
|
||||||
):
|
):
|
||||||
_LOGGER.debug(
|
_LOGGER.debug(
|
||||||
"Updating dynamic select options for %s", self.entity_description.name
|
"Updating dynamic select options for %s", self.entity_description.name
|
||||||
@ -364,8 +352,8 @@ class ProtectSelects(ProtectDeviceEntity, SelectEntity):
|
|||||||
if self.entity_description.ufp_options is not None:
|
if self.entity_description.ufp_options is not None:
|
||||||
options = self.entity_description.ufp_options
|
options = self.entity_description.ufp_options
|
||||||
else:
|
else:
|
||||||
assert self.entity_description.ufp_options_callable is not None
|
assert self.entity_description.ufp_options_fn is not None
|
||||||
options = self.entity_description.ufp_options_callable(self.data.api)
|
options = self.entity_description.ufp_options_fn(self.data.api)
|
||||||
|
|
||||||
self._attr_options = [item["name"] for item in options]
|
self._attr_options = [item["name"] for item in options]
|
||||||
self._hass_to_unifi_options = {item["name"]: item["id"] for item in options}
|
self._hass_to_unifi_options = {item["name"]: item["id"] for item in options}
|
||||||
|
@ -4,11 +4,16 @@ from __future__ import annotations
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any, Generic
|
||||||
|
|
||||||
from pyunifiprotect.data import NVR, Camera, Event
|
from pyunifiprotect.data import (
|
||||||
from pyunifiprotect.data.base import ProtectAdoptableDeviceModel
|
NVR,
|
||||||
from pyunifiprotect.data.devices import Sensor
|
Camera,
|
||||||
|
Event,
|
||||||
|
ProtectAdoptableDeviceModel,
|
||||||
|
ProtectDeviceModel,
|
||||||
|
Sensor,
|
||||||
|
)
|
||||||
|
|
||||||
from homeassistant.components.sensor import (
|
from homeassistant.components.sensor import (
|
||||||
SensorDeviceClass,
|
SensorDeviceClass,
|
||||||
@ -40,7 +45,7 @@ from .entity import (
|
|||||||
ProtectNVREntity,
|
ProtectNVREntity,
|
||||||
async_all_device_entities,
|
async_all_device_entities,
|
||||||
)
|
)
|
||||||
from .models import ProtectRequiredKeysMixin
|
from .models import ProtectRequiredKeysMixin, T
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
OBJECT_TYPE_NONE = "none"
|
OBJECT_TYPE_NONE = "none"
|
||||||
@ -48,12 +53,14 @@ DEVICE_CLASS_DETECTION = "unifiprotect__detection"
|
|||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ProtectSensorEntityDescription(ProtectRequiredKeysMixin, SensorEntityDescription):
|
class ProtectSensorEntityDescription(
|
||||||
|
ProtectRequiredKeysMixin, SensorEntityDescription, Generic[T]
|
||||||
|
):
|
||||||
"""Describes UniFi Protect Sensor entity."""
|
"""Describes UniFi Protect Sensor entity."""
|
||||||
|
|
||||||
precision: int | None = None
|
precision: int | None = None
|
||||||
|
|
||||||
def get_ufp_value(self, obj: ProtectAdoptableDeviceModel | NVR) -> Any:
|
def get_ufp_value(self, obj: ProtectDeviceModel) -> Any:
|
||||||
"""Return value from UniFi Protect device."""
|
"""Return value from UniFi Protect device."""
|
||||||
value = super().get_ufp_value(obj)
|
value = super().get_ufp_value(obj)
|
||||||
|
|
||||||
@ -62,7 +69,7 @@ class ProtectSensorEntityDescription(ProtectRequiredKeysMixin, SensorEntityDescr
|
|||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
def _get_uptime(obj: ProtectAdoptableDeviceModel | NVR) -> datetime | None:
|
def _get_uptime(obj: ProtectDeviceModel) -> datetime | None:
|
||||||
if obj.up_since is None:
|
if obj.up_since is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -71,26 +78,21 @@ def _get_uptime(obj: ProtectAdoptableDeviceModel | NVR) -> datetime | None:
|
|||||||
return obj.up_since.replace(second=0, microsecond=0)
|
return obj.up_since.replace(second=0, microsecond=0)
|
||||||
|
|
||||||
|
|
||||||
def _get_nvr_recording_capacity(obj: Any) -> int:
|
def _get_nvr_recording_capacity(obj: NVR) -> int:
|
||||||
assert isinstance(obj, NVR)
|
|
||||||
|
|
||||||
if obj.storage_stats.capacity is None:
|
if obj.storage_stats.capacity is None:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
return int(obj.storage_stats.capacity.total_seconds())
|
return int(obj.storage_stats.capacity.total_seconds())
|
||||||
|
|
||||||
|
|
||||||
def _get_nvr_memory(obj: Any) -> float | None:
|
def _get_nvr_memory(obj: NVR) -> float | None:
|
||||||
assert isinstance(obj, NVR)
|
|
||||||
|
|
||||||
memory = obj.system_info.memory
|
memory = obj.system_info.memory
|
||||||
if memory.available is None or memory.total is None:
|
if memory.available is None or memory.total is None:
|
||||||
return None
|
return None
|
||||||
return (1 - memory.available / memory.total) * 100
|
return (1 - memory.available / memory.total) * 100
|
||||||
|
|
||||||
|
|
||||||
def _get_alarm_sound(obj: ProtectAdoptableDeviceModel | NVR) -> str:
|
def _get_alarm_sound(obj: Sensor) -> str:
|
||||||
assert isinstance(obj, Sensor)
|
|
||||||
|
|
||||||
alarm_type = OBJECT_TYPE_NONE
|
alarm_type = OBJECT_TYPE_NONE
|
||||||
if (
|
if (
|
||||||
@ -103,7 +105,7 @@ def _get_alarm_sound(obj: ProtectAdoptableDeviceModel | NVR) -> str:
|
|||||||
|
|
||||||
|
|
||||||
ALL_DEVICES_SENSORS: tuple[ProtectSensorEntityDescription, ...] = (
|
ALL_DEVICES_SENSORS: tuple[ProtectSensorEntityDescription, ...] = (
|
||||||
ProtectSensorEntityDescription(
|
ProtectSensorEntityDescription[ProtectDeviceModel](
|
||||||
key="uptime",
|
key="uptime",
|
||||||
name="Uptime",
|
name="Uptime",
|
||||||
icon="mdi:clock",
|
icon="mdi:clock",
|
||||||
@ -244,7 +246,7 @@ SENSE_SENSORS: tuple[ProtectSensorEntityDescription, ...] = (
|
|||||||
ufp_value="stats.temperature.value",
|
ufp_value="stats.temperature.value",
|
||||||
ufp_enabled="is_temperature_sensor_enabled",
|
ufp_enabled="is_temperature_sensor_enabled",
|
||||||
),
|
),
|
||||||
ProtectSensorEntityDescription(
|
ProtectSensorEntityDescription[Sensor](
|
||||||
key="alarm_sound",
|
key="alarm_sound",
|
||||||
name="Alarm Sound Detected",
|
name="Alarm Sound Detected",
|
||||||
ufp_value_fn=_get_alarm_sound,
|
ufp_value_fn=_get_alarm_sound,
|
||||||
@ -253,7 +255,7 @@ SENSE_SENSORS: tuple[ProtectSensorEntityDescription, ...] = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
NVR_SENSORS: tuple[ProtectSensorEntityDescription, ...] = (
|
NVR_SENSORS: tuple[ProtectSensorEntityDescription, ...] = (
|
||||||
ProtectSensorEntityDescription(
|
ProtectSensorEntityDescription[ProtectDeviceModel](
|
||||||
key="uptime",
|
key="uptime",
|
||||||
name="Uptime",
|
name="Uptime",
|
||||||
icon="mdi:clock",
|
icon="mdi:clock",
|
||||||
@ -331,7 +333,7 @@ NVR_SENSORS: tuple[ProtectSensorEntityDescription, ...] = (
|
|||||||
ufp_value="storage_stats.storage_distribution.free.percentage",
|
ufp_value="storage_stats.storage_distribution.free.percentage",
|
||||||
precision=2,
|
precision=2,
|
||||||
),
|
),
|
||||||
ProtectSensorEntityDescription(
|
ProtectSensorEntityDescription[NVR](
|
||||||
key="record_capacity",
|
key="record_capacity",
|
||||||
name="Recording Capacity",
|
name="Recording Capacity",
|
||||||
native_unit_of_measurement=TIME_SECONDS,
|
native_unit_of_measurement=TIME_SECONDS,
|
||||||
@ -363,7 +365,7 @@ NVR_DISABLED_SENSORS: tuple[ProtectSensorEntityDescription, ...] = (
|
|||||||
state_class=SensorStateClass.MEASUREMENT,
|
state_class=SensorStateClass.MEASUREMENT,
|
||||||
ufp_value="system_info.cpu.temperature",
|
ufp_value="system_info.cpu.temperature",
|
||||||
),
|
),
|
||||||
ProtectSensorEntityDescription(
|
ProtectSensorEntityDescription[NVR](
|
||||||
key="memory_utilization",
|
key="memory_utilization",
|
||||||
name="Memory Utilization",
|
name="Memory Utilization",
|
||||||
native_unit_of_measurement=PERCENTAGE,
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
|
@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import logging
|
import logging
|
||||||
from typing import Any
|
from typing import Any, Generic
|
||||||
|
|
||||||
from pyunifiprotect.data import Camera, RecordingMode, VideoMode
|
from pyunifiprotect.data import Camera, RecordingMode, VideoMode
|
||||||
from pyunifiprotect.data.base import ProtectAdoptableDeviceModel
|
from pyunifiprotect.data.base import ProtectAdoptableDeviceModel
|
||||||
@ -17,26 +17,26 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .data import ProtectData
|
from .data import ProtectData
|
||||||
from .entity import ProtectDeviceEntity, async_all_device_entities
|
from .entity import ProtectDeviceEntity, async_all_device_entities
|
||||||
from .models import ProtectSetableKeysMixin
|
from .models import ProtectSetableKeysMixin, T
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ProtectSwitchEntityDescription(ProtectSetableKeysMixin, SwitchEntityDescription):
|
class ProtectSwitchEntityDescription(
|
||||||
|
ProtectSetableKeysMixin, SwitchEntityDescription, Generic[T]
|
||||||
|
):
|
||||||
"""Describes UniFi Protect Switch entity."""
|
"""Describes UniFi Protect Switch entity."""
|
||||||
|
|
||||||
|
|
||||||
_KEY_PRIVACY_MODE = "privacy_mode"
|
_KEY_PRIVACY_MODE = "privacy_mode"
|
||||||
|
|
||||||
|
|
||||||
def _get_is_highfps(obj: Any) -> bool:
|
def _get_is_highfps(obj: Camera) -> bool:
|
||||||
assert isinstance(obj, Camera)
|
|
||||||
return bool(obj.video_mode == VideoMode.HIGH_FPS)
|
return bool(obj.video_mode == VideoMode.HIGH_FPS)
|
||||||
|
|
||||||
|
|
||||||
async def _set_highfps(obj: Any, value: bool) -> None:
|
async def _set_highfps(obj: Camera, value: bool) -> None:
|
||||||
assert isinstance(obj, Camera)
|
|
||||||
if value:
|
if value:
|
||||||
await obj.set_video_mode(VideoMode.HIGH_FPS)
|
await obj.set_video_mode(VideoMode.HIGH_FPS)
|
||||||
else:
|
else:
|
||||||
@ -74,7 +74,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
|||||||
ufp_value="hdr_mode",
|
ufp_value="hdr_mode",
|
||||||
ufp_set_method="set_hdr",
|
ufp_set_method="set_hdr",
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription[Camera](
|
||||||
key="high_fps",
|
key="high_fps",
|
||||||
name="High FPS",
|
name="High FPS",
|
||||||
icon="mdi:video-high-definition",
|
icon="mdi:video-high-definition",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user