Add Permission checking for UniFi Protect (#73765)
Co-authored-by: J. Nick Koston <nick@koston.org>pull/73768/head
parent
a816348616
commit
3823edda32
|
@ -18,7 +18,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, T
|
from .models import PermRequired, ProtectSetableKeysMixin, T
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -40,6 +40,7 @@ ALL_DEVICE_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = (
|
||||||
device_class=ButtonDeviceClass.RESTART,
|
device_class=ButtonDeviceClass.RESTART,
|
||||||
name="Reboot Device",
|
name="Reboot Device",
|
||||||
ufp_press="reboot",
|
ufp_press="reboot",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,6 +50,7 @@ SENSOR_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = (
|
||||||
name="Clear Tamper",
|
name="Clear Tamper",
|
||||||
icon="mdi:notification-clear-all",
|
icon="mdi:notification-clear-all",
|
||||||
ufp_press="clear_tamper",
|
ufp_press="clear_tamper",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ from homeassistant.helpers.entity import DeviceInfo, Entity, EntityDescription
|
||||||
|
|
||||||
from .const import ATTR_EVENT_SCORE, DEFAULT_ATTRIBUTION, DEFAULT_BRAND, DOMAIN
|
from .const import ATTR_EVENT_SCORE, DEFAULT_ATTRIBUTION, DEFAULT_BRAND, DOMAIN
|
||||||
from .data import ProtectData
|
from .data import ProtectData
|
||||||
from .models import ProtectRequiredKeysMixin
|
from .models import PermRequired, ProtectRequiredKeysMixin
|
||||||
from .utils import get_nested_attr
|
from .utils import get_nested_attr
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
@ -46,6 +46,11 @@ def _async_device_entities(
|
||||||
for device in data.get_by_types({model_type}):
|
for device in data.get_by_types({model_type}):
|
||||||
assert isinstance(device, (Camera, Light, Sensor, Viewer, Doorlock, Chime))
|
assert isinstance(device, (Camera, Light, Sensor, Viewer, Doorlock, Chime))
|
||||||
for description in descs:
|
for description in descs:
|
||||||
|
if description.ufp_perm is not None:
|
||||||
|
can_write = device.can_write(data.api.bootstrap.auth_user)
|
||||||
|
if description.ufp_perm == PermRequired.WRITE and not can_write:
|
||||||
|
continue
|
||||||
|
|
||||||
if description.ufp_required_field:
|
if description.ufp_required_field:
|
||||||
required_field = get_nested_attr(device, description.ufp_required_field)
|
required_field = get_nested_attr(device, description.ufp_required_field)
|
||||||
if not required_field:
|
if not required_field:
|
||||||
|
|
|
@ -25,13 +25,10 @@ async def async_setup_entry(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up lights for UniFi Protect integration."""
|
"""Set up lights for UniFi Protect integration."""
|
||||||
data: ProtectData = hass.data[DOMAIN][entry.entry_id]
|
data: ProtectData = hass.data[DOMAIN][entry.entry_id]
|
||||||
entities = [
|
entities = []
|
||||||
ProtectLight(
|
for device in data.api.bootstrap.lights.values():
|
||||||
data,
|
if device.can_write(data.api.bootstrap.auth_user):
|
||||||
device,
|
entities.append(ProtectLight(data, device))
|
||||||
)
|
|
||||||
for device in data.api.bootstrap.lights.values()
|
|
||||||
]
|
|
||||||
|
|
||||||
if not entities:
|
if not entities:
|
||||||
return
|
return
|
||||||
|
|
|
@ -3,6 +3,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable, Coroutine
|
from collections.abc import Callable, Coroutine
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from enum import Enum
|
||||||
import logging
|
import logging
|
||||||
from typing import Any, Generic, TypeVar
|
from typing import Any, Generic, TypeVar
|
||||||
|
|
||||||
|
@ -17,6 +18,13 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
T = TypeVar("T", bound=ProtectDeviceModel)
|
T = TypeVar("T", bound=ProtectDeviceModel)
|
||||||
|
|
||||||
|
|
||||||
|
class PermRequired(int, Enum):
|
||||||
|
"""Type of permission level required for entity."""
|
||||||
|
|
||||||
|
NO_WRITE = 1
|
||||||
|
WRITE = 2
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ProtectRequiredKeysMixin(EntityDescription, Generic[T]):
|
class ProtectRequiredKeysMixin(EntityDescription, Generic[T]):
|
||||||
"""Mixin for required keys."""
|
"""Mixin for required keys."""
|
||||||
|
@ -25,6 +33,7 @@ class ProtectRequiredKeysMixin(EntityDescription, Generic[T]):
|
||||||
ufp_value: str | None = None
|
ufp_value: str | None = None
|
||||||
ufp_value_fn: Callable[[T], Any] | None = None
|
ufp_value_fn: Callable[[T], Any] | None = None
|
||||||
ufp_enabled: str | None = None
|
ufp_enabled: str | None = None
|
||||||
|
ufp_perm: PermRequired | None = None
|
||||||
|
|
||||||
def get_ufp_value(self, obj: T) -> Any:
|
def get_ufp_value(self, obj: T) -> Any:
|
||||||
"""Return value from UniFi Protect device."""
|
"""Return value from UniFi Protect device."""
|
||||||
|
|
|
@ -8,7 +8,7 @@ from pyunifiprotect.data import Camera, Doorlock, Light, ProtectModelWithId
|
||||||
|
|
||||||
from homeassistant.components.number import NumberEntity, NumberEntityDescription
|
from homeassistant.components.number import NumberEntity, NumberEntityDescription
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import TIME_SECONDS
|
from homeassistant.const import PERCENTAGE, TIME_SECONDS
|
||||||
from homeassistant.core import HomeAssistant, callback
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.entity import EntityCategory
|
from homeassistant.helpers.entity import EntityCategory
|
||||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
|
@ -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, T
|
from .models import PermRequired, ProtectSetableKeysMixin, T
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -63,30 +63,35 @@ CAMERA_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = (
|
||||||
ufp_required_field="feature_flags.has_wdr",
|
ufp_required_field="feature_flags.has_wdr",
|
||||||
ufp_value="isp_settings.wdr",
|
ufp_value="isp_settings.wdr",
|
||||||
ufp_set_method="set_wdr_level",
|
ufp_set_method="set_wdr_level",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectNumberEntityDescription(
|
ProtectNumberEntityDescription(
|
||||||
key="mic_level",
|
key="mic_level",
|
||||||
name="Microphone Level",
|
name="Microphone Level",
|
||||||
icon="mdi:microphone",
|
icon="mdi:microphone",
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
ufp_min=0,
|
ufp_min=0,
|
||||||
ufp_max=100,
|
ufp_max=100,
|
||||||
ufp_step=1,
|
ufp_step=1,
|
||||||
ufp_required_field="feature_flags.has_mic",
|
ufp_required_field="feature_flags.has_mic",
|
||||||
ufp_value="mic_volume",
|
ufp_value="mic_volume",
|
||||||
ufp_set_method="set_mic_volume",
|
ufp_set_method="set_mic_volume",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectNumberEntityDescription(
|
ProtectNumberEntityDescription(
|
||||||
key="zoom_position",
|
key="zoom_position",
|
||||||
name="Zoom Level",
|
name="Zoom Level",
|
||||||
icon="mdi:magnify-plus-outline",
|
icon="mdi:magnify-plus-outline",
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
ufp_min=0,
|
ufp_min=0,
|
||||||
ufp_max=100,
|
ufp_max=100,
|
||||||
ufp_step=1,
|
ufp_step=1,
|
||||||
ufp_required_field="feature_flags.can_optical_zoom",
|
ufp_required_field="feature_flags.can_optical_zoom",
|
||||||
ufp_value="isp_settings.zoom_position",
|
ufp_value="isp_settings.zoom_position",
|
||||||
ufp_set_method="set_camera_zoom",
|
ufp_set_method="set_camera_zoom",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -96,12 +101,14 @@ LIGHT_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = (
|
||||||
name="Motion Sensitivity",
|
name="Motion Sensitivity",
|
||||||
icon="mdi:walk",
|
icon="mdi:walk",
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
ufp_min=0,
|
ufp_min=0,
|
||||||
ufp_max=100,
|
ufp_max=100,
|
||||||
ufp_step=1,
|
ufp_step=1,
|
||||||
ufp_required_field=None,
|
ufp_required_field=None,
|
||||||
ufp_value="light_device_settings.pir_sensitivity",
|
ufp_value="light_device_settings.pir_sensitivity",
|
||||||
ufp_set_method="set_sensitivity",
|
ufp_set_method="set_sensitivity",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectNumberEntityDescription[Light](
|
ProtectNumberEntityDescription[Light](
|
||||||
key="duration",
|
key="duration",
|
||||||
|
@ -115,6 +122,7 @@ LIGHT_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = (
|
||||||
ufp_required_field=None,
|
ufp_required_field=None,
|
||||||
ufp_value_fn=_get_pir_duration,
|
ufp_value_fn=_get_pir_duration,
|
||||||
ufp_set_method_fn=_set_pir_duration,
|
ufp_set_method_fn=_set_pir_duration,
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -124,12 +132,14 @@ SENSE_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = (
|
||||||
name="Motion Sensitivity",
|
name="Motion Sensitivity",
|
||||||
icon="mdi:walk",
|
icon="mdi:walk",
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
ufp_min=0,
|
ufp_min=0,
|
||||||
ufp_max=100,
|
ufp_max=100,
|
||||||
ufp_step=1,
|
ufp_step=1,
|
||||||
ufp_required_field=None,
|
ufp_required_field=None,
|
||||||
ufp_value="motion_settings.sensitivity",
|
ufp_value="motion_settings.sensitivity",
|
||||||
ufp_set_method="set_motion_sensitivity",
|
ufp_set_method="set_motion_sensitivity",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -146,6 +156,7 @@ DOORLOCK_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = (
|
||||||
ufp_required_field=None,
|
ufp_required_field=None,
|
||||||
ufp_value_fn=_get_auto_close,
|
ufp_value_fn=_get_auto_close,
|
||||||
ufp_set_method_fn=_set_auto_close,
|
ufp_set_method_fn=_set_auto_close,
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -155,11 +166,13 @@ CHIME_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = (
|
||||||
name="Volume",
|
name="Volume",
|
||||||
icon="mdi:speaker",
|
icon="mdi:speaker",
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
|
native_unit_of_measurement=PERCENTAGE,
|
||||||
ufp_min=0,
|
ufp_min=0,
|
||||||
ufp_max=100,
|
ufp_max=100,
|
||||||
ufp_step=1,
|
ufp_step=1,
|
||||||
ufp_value="volume",
|
ufp_value="volume",
|
||||||
ufp_set_method="set_volume",
|
ufp_set_method="set_volume",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,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, T
|
from .models import PermRequired, ProtectSetableKeysMixin, T
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
_KEY_LIGHT_MOTION = "light_motion"
|
_KEY_LIGHT_MOTION = "light_motion"
|
||||||
|
@ -208,6 +208,7 @@ CAMERA_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
||||||
ufp_enum_type=RecordingMode,
|
ufp_enum_type=RecordingMode,
|
||||||
ufp_value="recording_settings.mode",
|
ufp_value="recording_settings.mode",
|
||||||
ufp_set_method="set_recording_mode",
|
ufp_set_method="set_recording_mode",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSelectEntityDescription(
|
ProtectSelectEntityDescription(
|
||||||
key="infrared",
|
key="infrared",
|
||||||
|
@ -219,6 +220,7 @@ CAMERA_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
||||||
ufp_enum_type=IRLEDMode,
|
ufp_enum_type=IRLEDMode,
|
||||||
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",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSelectEntityDescription[Camera](
|
ProtectSelectEntityDescription[Camera](
|
||||||
key="doorbell_text",
|
key="doorbell_text",
|
||||||
|
@ -230,6 +232,7 @@ CAMERA_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
||||||
ufp_value_fn=_get_doorbell_current,
|
ufp_value_fn=_get_doorbell_current,
|
||||||
ufp_options_fn=_get_doorbell_options,
|
ufp_options_fn=_get_doorbell_options,
|
||||||
ufp_set_method_fn=_set_doorbell_message,
|
ufp_set_method_fn=_set_doorbell_message,
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSelectEntityDescription(
|
ProtectSelectEntityDescription(
|
||||||
key="chime_type",
|
key="chime_type",
|
||||||
|
@ -241,6 +244,7 @@ CAMERA_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
||||||
ufp_enum_type=ChimeType,
|
ufp_enum_type=ChimeType,
|
||||||
ufp_value="chime_type",
|
ufp_value="chime_type",
|
||||||
ufp_set_method="set_chime_type",
|
ufp_set_method="set_chime_type",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -253,6 +257,7 @@ LIGHT_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
||||||
ufp_options=MOTION_MODE_TO_LIGHT_MODE,
|
ufp_options=MOTION_MODE_TO_LIGHT_MODE,
|
||||||
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,
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSelectEntityDescription[Light](
|
ProtectSelectEntityDescription[Light](
|
||||||
key="paired_camera",
|
key="paired_camera",
|
||||||
|
@ -262,6 +267,7 @@ LIGHT_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
||||||
ufp_value="camera_id",
|
ufp_value="camera_id",
|
||||||
ufp_options_fn=_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,
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -275,6 +281,7 @@ SENSE_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
||||||
ufp_enum_type=MountType,
|
ufp_enum_type=MountType,
|
||||||
ufp_value="mount_type",
|
ufp_value="mount_type",
|
||||||
ufp_set_method="set_mount_type",
|
ufp_set_method="set_mount_type",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSelectEntityDescription[Sensor](
|
ProtectSelectEntityDescription[Sensor](
|
||||||
key="paired_camera",
|
key="paired_camera",
|
||||||
|
@ -284,6 +291,7 @@ SENSE_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
||||||
ufp_value="camera_id",
|
ufp_value="camera_id",
|
||||||
ufp_options_fn=_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,
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -296,6 +304,7 @@ DOORLOCK_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
||||||
ufp_value="camera_id",
|
ufp_value="camera_id",
|
||||||
ufp_options_fn=_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,
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -308,6 +317,7 @@ VIEWER_SELECTS: tuple[ProtectSelectEntityDescription, ...] = (
|
||||||
ufp_options_fn=_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,
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,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, T
|
from .models import PermRequired, ProtectSetableKeysMixin, T
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="is_ssh_enabled",
|
ufp_value="is_ssh_enabled",
|
||||||
ufp_set_method="set_ssh",
|
ufp_set_method="set_ssh",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="status_light",
|
key="status_light",
|
||||||
|
@ -65,6 +66,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
ufp_required_field="feature_flags.has_led_status",
|
ufp_required_field="feature_flags.has_led_status",
|
||||||
ufp_value="led_settings.is_enabled",
|
ufp_value="led_settings.is_enabled",
|
||||||
ufp_set_method="set_status_light",
|
ufp_set_method="set_status_light",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="hdr_mode",
|
key="hdr_mode",
|
||||||
|
@ -74,6 +76,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
ufp_required_field="feature_flags.has_hdr",
|
ufp_required_field="feature_flags.has_hdr",
|
||||||
ufp_value="hdr_mode",
|
ufp_value="hdr_mode",
|
||||||
ufp_set_method="set_hdr",
|
ufp_set_method="set_hdr",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription[Camera](
|
ProtectSwitchEntityDescription[Camera](
|
||||||
key="high_fps",
|
key="high_fps",
|
||||||
|
@ -83,6 +86,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
ufp_required_field="feature_flags.has_highfps",
|
ufp_required_field="feature_flags.has_highfps",
|
||||||
ufp_value_fn=_get_is_highfps,
|
ufp_value_fn=_get_is_highfps,
|
||||||
ufp_set_method_fn=_set_highfps,
|
ufp_set_method_fn=_set_highfps,
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key=_KEY_PRIVACY_MODE,
|
key=_KEY_PRIVACY_MODE,
|
||||||
|
@ -91,6 +95,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_required_field="feature_flags.has_privacy_mask",
|
ufp_required_field="feature_flags.has_privacy_mask",
|
||||||
ufp_value="is_privacy_on",
|
ufp_value="is_privacy_on",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="system_sounds",
|
key="system_sounds",
|
||||||
|
@ -100,6 +105,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
ufp_required_field="feature_flags.has_speaker",
|
ufp_required_field="feature_flags.has_speaker",
|
||||||
ufp_value="speaker_settings.are_system_sounds_enabled",
|
ufp_value="speaker_settings.are_system_sounds_enabled",
|
||||||
ufp_set_method="set_system_sounds",
|
ufp_set_method="set_system_sounds",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="osd_name",
|
key="osd_name",
|
||||||
|
@ -108,6 +114,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="osd_settings.is_name_enabled",
|
ufp_value="osd_settings.is_name_enabled",
|
||||||
ufp_set_method="set_osd_name",
|
ufp_set_method="set_osd_name",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="osd_date",
|
key="osd_date",
|
||||||
|
@ -116,6 +123,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="osd_settings.is_date_enabled",
|
ufp_value="osd_settings.is_date_enabled",
|
||||||
ufp_set_method="set_osd_date",
|
ufp_set_method="set_osd_date",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="osd_logo",
|
key="osd_logo",
|
||||||
|
@ -124,6 +132,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="osd_settings.is_logo_enabled",
|
ufp_value="osd_settings.is_logo_enabled",
|
||||||
ufp_set_method="set_osd_logo",
|
ufp_set_method="set_osd_logo",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="osd_bitrate",
|
key="osd_bitrate",
|
||||||
|
@ -132,6 +141,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="osd_settings.is_debug_enabled",
|
ufp_value="osd_settings.is_debug_enabled",
|
||||||
ufp_set_method="set_osd_bitrate",
|
ufp_set_method="set_osd_bitrate",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="motion",
|
key="motion",
|
||||||
|
@ -140,6 +150,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="recording_settings.enable_motion_detection",
|
ufp_value="recording_settings.enable_motion_detection",
|
||||||
ufp_set_method="set_motion_detection",
|
ufp_set_method="set_motion_detection",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="smart_person",
|
key="smart_person",
|
||||||
|
@ -149,6 +160,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
ufp_required_field="can_detect_person",
|
ufp_required_field="can_detect_person",
|
||||||
ufp_value="is_person_detection_on",
|
ufp_value="is_person_detection_on",
|
||||||
ufp_set_method="set_person_detection",
|
ufp_set_method="set_person_detection",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="smart_vehicle",
|
key="smart_vehicle",
|
||||||
|
@ -158,6 +170,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
ufp_required_field="can_detect_vehicle",
|
ufp_required_field="can_detect_vehicle",
|
||||||
ufp_value="is_vehicle_detection_on",
|
ufp_value="is_vehicle_detection_on",
|
||||||
ufp_set_method="set_vehicle_detection",
|
ufp_set_method="set_vehicle_detection",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="smart_face",
|
key="smart_face",
|
||||||
|
@ -167,6 +180,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
ufp_required_field="can_detect_face",
|
ufp_required_field="can_detect_face",
|
||||||
ufp_value="is_face_detection_on",
|
ufp_value="is_face_detection_on",
|
||||||
ufp_set_method="set_face_detection",
|
ufp_set_method="set_face_detection",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="smart_package",
|
key="smart_package",
|
||||||
|
@ -176,6 +190,7 @@ CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
ufp_required_field="can_detect_package",
|
ufp_required_field="can_detect_package",
|
||||||
ufp_value="is_package_detection_on",
|
ufp_value="is_package_detection_on",
|
||||||
ufp_set_method="set_package_detection",
|
ufp_set_method="set_package_detection",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -187,6 +202,7 @@ SENSE_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="led_settings.is_enabled",
|
ufp_value="led_settings.is_enabled",
|
||||||
ufp_set_method="set_status_light",
|
ufp_set_method="set_status_light",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="motion",
|
key="motion",
|
||||||
|
@ -195,6 +211,7 @@ SENSE_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="motion_settings.is_enabled",
|
ufp_value="motion_settings.is_enabled",
|
||||||
ufp_set_method="set_motion_status",
|
ufp_set_method="set_motion_status",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="temperature",
|
key="temperature",
|
||||||
|
@ -203,6 +220,7 @@ SENSE_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="temperature_settings.is_enabled",
|
ufp_value="temperature_settings.is_enabled",
|
||||||
ufp_set_method="set_temperature_status",
|
ufp_set_method="set_temperature_status",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="humidity",
|
key="humidity",
|
||||||
|
@ -211,6 +229,7 @@ SENSE_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="humidity_settings.is_enabled",
|
ufp_value="humidity_settings.is_enabled",
|
||||||
ufp_set_method="set_humidity_status",
|
ufp_set_method="set_humidity_status",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="light",
|
key="light",
|
||||||
|
@ -219,6 +238,7 @@ SENSE_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="light_settings.is_enabled",
|
ufp_value="light_settings.is_enabled",
|
||||||
ufp_set_method="set_light_status",
|
ufp_set_method="set_light_status",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="alarm",
|
key="alarm",
|
||||||
|
@ -226,6 +246,7 @@ SENSE_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="alarm_settings.is_enabled",
|
ufp_value="alarm_settings.is_enabled",
|
||||||
ufp_set_method="set_alarm_status",
|
ufp_set_method="set_alarm_status",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -239,6 +260,7 @@ LIGHT_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="is_ssh_enabled",
|
ufp_value="is_ssh_enabled",
|
||||||
ufp_set_method="set_ssh",
|
ufp_set_method="set_ssh",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
ProtectSwitchEntityDescription(
|
ProtectSwitchEntityDescription(
|
||||||
key="status_light",
|
key="status_light",
|
||||||
|
@ -247,6 +269,7 @@ LIGHT_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="light_device_settings.is_indicator_enabled",
|
ufp_value="light_device_settings.is_indicator_enabled",
|
||||||
ufp_set_method="set_status_light",
|
ufp_set_method="set_status_light",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -258,6 +281,7 @@ DOORLOCK_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="led_settings.is_enabled",
|
ufp_value="led_settings.is_enabled",
|
||||||
ufp_set_method="set_status_light",
|
ufp_set_method="set_status_light",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -270,6 +294,7 @@ VIEWER_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
|
||||||
entity_category=EntityCategory.CONFIG,
|
entity_category=EntityCategory.CONFIG,
|
||||||
ufp_value="is_ssh_enabled",
|
ufp_value="is_ssh_enabled",
|
||||||
ufp_set_method="set_ssh",
|
ufp_set_method="set_ssh",
|
||||||
|
ufp_perm=PermRequired.WRITE,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import pytest
|
||||||
from pyunifiprotect.data import (
|
from pyunifiprotect.data import (
|
||||||
Camera,
|
Camera,
|
||||||
Light,
|
Light,
|
||||||
|
Permission,
|
||||||
RecordingMode,
|
RecordingMode,
|
||||||
SmartDetectObjectType,
|
SmartDetectObjectType,
|
||||||
VideoMode,
|
VideoMode,
|
||||||
|
@ -214,6 +215,40 @@ async def camera_privacy_fixture(
|
||||||
Camera.__config__.validate_assignment = True
|
Camera.__config__.validate_assignment = True
|
||||||
|
|
||||||
|
|
||||||
|
async def test_switch_setup_no_perm(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
mock_entry: MockEntityFixture,
|
||||||
|
mock_light: Light,
|
||||||
|
mock_camera: Camera,
|
||||||
|
):
|
||||||
|
"""Test switch entity setup for light devices."""
|
||||||
|
|
||||||
|
light_obj = mock_light.copy()
|
||||||
|
light_obj._api = mock_entry.api
|
||||||
|
|
||||||
|
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
|
||||||
|
camera_obj.channels[2]._api = mock_entry.api
|
||||||
|
|
||||||
|
reset_objects(mock_entry.api.bootstrap)
|
||||||
|
mock_entry.api.bootstrap.lights = {
|
||||||
|
light_obj.id: light_obj,
|
||||||
|
}
|
||||||
|
mock_entry.api.bootstrap.cameras = {
|
||||||
|
camera_obj.id: camera_obj,
|
||||||
|
}
|
||||||
|
mock_entry.api.bootstrap.auth_user.all_permissions = [
|
||||||
|
Permission.unifi_dict_to_dict({"rawPermission": "light:read:*"})
|
||||||
|
]
|
||||||
|
|
||||||
|
await hass.config_entries.async_setup(mock_entry.entry.entry_id)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert_entity_counts(hass, Platform.SWITCH, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
async def test_switch_setup_light(
|
async def test_switch_setup_light(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
mock_entry: MockEntityFixture,
|
mock_entry: MockEntityFixture,
|
||||||
|
|
Loading…
Reference in New Issue