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