diff --git a/homeassistant/components/unifiprotect/binary_sensor.py b/homeassistant/components/unifiprotect/binary_sensor.py index c97197fea5e..f42d2d09211 100644 --- a/homeassistant/components/unifiprotect/binary_sensor.py +++ b/homeassistant/components/unifiprotect/binary_sensor.py @@ -2,6 +2,7 @@ from __future__ import annotations +from collections.abc import Sequence import dataclasses import logging from typing import Any @@ -610,6 +611,14 @@ DISK_SENSORS: tuple[ProtectBinaryEntityDescription, ...] = ( ), ) +_MODEL_DESCRIPTIONS: dict[ModelType, Sequence[ProtectRequiredKeysMixin]] = { + ModelType.CAMERA: CAMERA_SENSORS, + ModelType.LIGHT: LIGHT_SENSORS, + ModelType.SENSOR: SENSE_SENSORS, + ModelType.DOORLOCK: DOORLOCK_SENSORS, + ModelType.VIEWPORT: VIEWER_SENSORS, +} + async def async_setup_entry( hass: HomeAssistant, @@ -624,11 +633,7 @@ async def async_setup_entry( entities: list[ProtectDeviceEntity] = async_all_device_entities( data, ProtectDeviceBinarySensor, - camera_descs=CAMERA_SENSORS, - light_descs=LIGHT_SENSORS, - sense_descs=SENSE_SENSORS, - lock_descs=DOORLOCK_SENSORS, - viewer_descs=VIEWER_SENSORS, + model_descriptions=_MODEL_DESCRIPTIONS, ufp_device=device, ) if device.is_adopted and isinstance(device, Camera): @@ -640,13 +645,7 @@ async def async_setup_entry( ) entities: list[ProtectDeviceEntity] = async_all_device_entities( - data, - ProtectDeviceBinarySensor, - camera_descs=CAMERA_SENSORS, - light_descs=LIGHT_SENSORS, - sense_descs=SENSE_SENSORS, - lock_descs=DOORLOCK_SENSORS, - viewer_descs=VIEWER_SENSORS, + data, ProtectDeviceBinarySensor, model_descriptions=_MODEL_DESCRIPTIONS ) entities += _async_event_entities(data) entities += _async_nvr_entities(data) diff --git a/homeassistant/components/unifiprotect/button.py b/homeassistant/components/unifiprotect/button.py index 009f9b275dc..98d226e9e76 100644 --- a/homeassistant/components/unifiprotect/button.py +++ b/homeassistant/components/unifiprotect/button.py @@ -2,11 +2,12 @@ from __future__ import annotations +from collections.abc import Sequence from dataclasses import dataclass import logging from typing import Final -from uiprotect.data import ProtectAdoptableDeviceModel, ProtectModelWithId +from uiprotect.data import ModelType, ProtectAdoptableDeviceModel, ProtectModelWithId from homeassistant.components.button import ( ButtonDeviceClass, @@ -22,7 +23,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DEVICES_THAT_ADOPT, DISPATCH_ADD, DISPATCH_ADOPT, DOMAIN from .data import ProtectData, UFPConfigEntry from .entity import ProtectDeviceEntity, async_all_device_entities -from .models import PermRequired, ProtectSetableKeysMixin, T +from .models import PermRequired, ProtectRequiredKeysMixin, ProtectSetableKeysMixin, T from .utils import async_dispatch_id as _ufpd _LOGGER = logging.getLogger(__name__) @@ -94,6 +95,12 @@ CHIME_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = ( ) +_MODEL_DESCRIPTIONS: dict[ModelType, Sequence[ProtectRequiredKeysMixin]] = { + ModelType.CHIME: CHIME_BUTTONS, + ModelType.SENSOR: SENSOR_BUTTONS, +} + + @callback def _async_remove_adopt_button( hass: HomeAssistant, device: ProtectAdoptableDeviceModel @@ -120,8 +127,7 @@ async def async_setup_entry( ProtectButton, all_descs=ALL_DEVICE_BUTTONS, unadopted_descs=[ADOPT_BUTTON], - chime_descs=CHIME_BUTTONS, - sense_descs=SENSOR_BUTTONS, + model_descriptions=_MODEL_DESCRIPTIONS, ufp_device=device, ) async_add_entities(entities) @@ -155,8 +161,7 @@ async def async_setup_entry( ProtectButton, all_descs=ALL_DEVICE_BUTTONS, unadopted_descs=[ADOPT_BUTTON], - chime_descs=CHIME_BUTTONS, - sense_descs=SENSOR_BUTTONS, + model_descriptions=_MODEL_DESCRIPTIONS, ) async_add_entities(entities) diff --git a/homeassistant/components/unifiprotect/entity.py b/homeassistant/components/unifiprotect/entity.py index 766c93949bd..137f8c532ee 100644 --- a/homeassistant/components/unifiprotect/entity.py +++ b/homeassistant/components/unifiprotect/entity.py @@ -8,17 +8,11 @@ from typing import TYPE_CHECKING, Any from uiprotect.data import ( NVR, - Camera, - Chime, - Doorlock, Event, - Light, ModelType, ProtectAdoptableDeviceModel, ProtectModelWithId, - Sensor, StateType, - Viewer, ) from homeassistant.core import callback @@ -46,7 +40,7 @@ def _async_device_entities( klass: type[ProtectDeviceEntity], model_type: ModelType, descs: Sequence[ProtectRequiredKeysMixin], - unadopted_descs: Sequence[ProtectRequiredKeysMixin], + unadopted_descs: Sequence[ProtectRequiredKeysMixin] | None = None, ufp_device: ProtectAdoptableDeviceModel | None = None, ) -> list[ProtectDeviceEntity]: if not descs and not unadopted_descs: @@ -58,37 +52,36 @@ def _async_device_entities( if ufp_device is not None else data.get_by_types({model_type}, ignore_unadopted=False) ) + auth_user = data.api.bootstrap.auth_user for device in devices: if TYPE_CHECKING: - assert isinstance(device, (Camera, Light, Sensor, Viewer, Doorlock, Chime)) + assert isinstance(device, ProtectAdoptableDeviceModel) if not device.is_adopted_by_us: - for description in unadopted_descs: - entities.append( - klass( - data, - device=device, - description=description, + if unadopted_descs: + for description in unadopted_descs: + entities.append( + klass( + data, + device=device, + description=description, + ) + ) + _LOGGER.debug( + "Adding %s entity %s for %s", + klass.__name__, + description.name, + device.display_name, ) - ) - _LOGGER.debug( - "Adding %s entity %s for %s", - klass.__name__, - description.name, - device.display_name, - ) continue - can_write = device.can_write(data.api.bootstrap.auth_user) + can_write = device.can_write(auth_user) for description in descs: - if description.ufp_perm is not None: - if description.ufp_perm is PermRequired.WRITE and not can_write: + if (perms := description.ufp_perm) is not None: + if perms is PermRequired.WRITE and not can_write: continue - if description.ufp_perm is PermRequired.NO_WRITE and can_write: + if perms is PermRequired.NO_WRITE and can_write: continue - if ( - description.ufp_perm is PermRequired.DELETE - and not device.can_delete(data.api.bootstrap.auth_user) - ): + if perms is PermRequired.DELETE and not device.can_delete(auth_user): continue if not description.has_required(device): @@ -111,70 +104,54 @@ def _async_device_entities( return entities +_ALL_MODEL_TYPES = ( + ModelType.CAMERA, + ModelType.LIGHT, + ModelType.SENSOR, + ModelType.VIEWPORT, + ModelType.DOORLOCK, + ModelType.CHIME, +) + + +@callback +def _combine_model_descs( + model_type: ModelType, + model_descriptions: dict[ModelType, Sequence[ProtectRequiredKeysMixin]] | None, + all_descs: Sequence[ProtectRequiredKeysMixin] | None, +) -> list[ProtectRequiredKeysMixin]: + """Combine all the descriptions with descriptions a model type.""" + descs: list[ProtectRequiredKeysMixin] = list(all_descs) if all_descs else [] + if model_descriptions and (model_descs := model_descriptions.get(model_type)): + descs.extend(model_descs) + return descs + + @callback def async_all_device_entities( data: ProtectData, klass: type[ProtectDeviceEntity], - camera_descs: Sequence[ProtectRequiredKeysMixin] | None = None, - light_descs: Sequence[ProtectRequiredKeysMixin] | None = None, - sense_descs: Sequence[ProtectRequiredKeysMixin] | None = None, - viewer_descs: Sequence[ProtectRequiredKeysMixin] | None = None, - lock_descs: Sequence[ProtectRequiredKeysMixin] | None = None, - chime_descs: Sequence[ProtectRequiredKeysMixin] | None = None, + model_descriptions: dict[ModelType, Sequence[ProtectRequiredKeysMixin]] + | None = None, all_descs: Sequence[ProtectRequiredKeysMixin] | None = None, - unadopted_descs: Sequence[ProtectRequiredKeysMixin] | None = None, + unadopted_descs: list[ProtectRequiredKeysMixin] | None = None, ufp_device: ProtectAdoptableDeviceModel | None = None, ) -> list[ProtectDeviceEntity]: """Generate a list of all the device entities.""" - all_descs = list(all_descs or []) - unadopted_descs = list(unadopted_descs or []) - camera_descs = list(camera_descs or []) + all_descs - light_descs = list(light_descs or []) + all_descs - sense_descs = list(sense_descs or []) + all_descs - viewer_descs = list(viewer_descs or []) + all_descs - lock_descs = list(lock_descs or []) + all_descs - chime_descs = list(chime_descs or []) + all_descs - if ufp_device is None: - return ( - _async_device_entities( - data, klass, ModelType.CAMERA, camera_descs, unadopted_descs + entities: list[ProtectDeviceEntity] = [] + for model_type in _ALL_MODEL_TYPES: + descs = _combine_model_descs(model_type, model_descriptions, all_descs) + entities.extend( + _async_device_entities(data, klass, model_type, descs, unadopted_descs) ) - + _async_device_entities( - data, klass, ModelType.LIGHT, light_descs, unadopted_descs - ) - + _async_device_entities( - data, klass, ModelType.SENSOR, sense_descs, unadopted_descs - ) - + _async_device_entities( - data, klass, ModelType.VIEWPORT, viewer_descs, unadopted_descs - ) - + _async_device_entities( - data, klass, ModelType.DOORLOCK, lock_descs, unadopted_descs - ) - + _async_device_entities( - data, klass, ModelType.CHIME, chime_descs, unadopted_descs - ) - ) + return entities - descs = [] - if ufp_device.model is ModelType.CAMERA: - descs = camera_descs - elif ufp_device.model is ModelType.LIGHT: - descs = light_descs - elif ufp_device.model is ModelType.SENSOR: - descs = sense_descs - elif ufp_device.model is ModelType.VIEWPORT: - descs = viewer_descs - elif ufp_device.model is ModelType.DOORLOCK: - descs = lock_descs - elif ufp_device.model is ModelType.CHIME: - descs = chime_descs - - if not descs and not unadopted_descs or ufp_device.model is None: - return [] + device_model_type = ufp_device.model + assert device_model_type is not None + descs = _combine_model_descs(device_model_type, model_descriptions, all_descs) return _async_device_entities( - data, klass, ufp_device.model, descs, unadopted_descs, ufp_device + data, klass, device_model_type, descs, unadopted_descs, ufp_device ) diff --git a/homeassistant/components/unifiprotect/number.py b/homeassistant/components/unifiprotect/number.py index 2a8137f50f7..05d07203191 100644 --- a/homeassistant/components/unifiprotect/number.py +++ b/homeassistant/components/unifiprotect/number.py @@ -2,6 +2,7 @@ from __future__ import annotations +from collections.abc import Sequence from dataclasses import dataclass from datetime import timedelta import logging @@ -11,6 +12,7 @@ from uiprotect.data import ( Camera, Doorlock, Light, + ModelType, ProtectAdoptableDeviceModel, ProtectModelWithId, ) @@ -24,7 +26,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DISPATCH_ADOPT from .data import ProtectData, UFPConfigEntry from .entity import ProtectDeviceEntity, async_all_device_entities -from .models import PermRequired, ProtectSetableKeysMixin, T +from .models import PermRequired, ProtectRequiredKeysMixin, ProtectSetableKeysMixin, T from .utils import async_dispatch_id as _ufpd _LOGGER = logging.getLogger(__name__) @@ -215,6 +217,13 @@ CHIME_NUMBERS: tuple[ProtectNumberEntityDescription, ...] = ( ufp_perm=PermRequired.WRITE, ), ) +_MODEL_DESCRIPTIONS: dict[ModelType, Sequence[ProtectRequiredKeysMixin]] = { + ModelType.CAMERA: CAMERA_NUMBERS, + ModelType.LIGHT: LIGHT_NUMBERS, + ModelType.SENSOR: SENSE_NUMBERS, + ModelType.DOORLOCK: DOORLOCK_NUMBERS, + ModelType.CHIME: CHIME_NUMBERS, +} async def async_setup_entry( @@ -230,11 +239,7 @@ async def async_setup_entry( entities = async_all_device_entities( data, ProtectNumbers, - camera_descs=CAMERA_NUMBERS, - light_descs=LIGHT_NUMBERS, - sense_descs=SENSE_NUMBERS, - lock_descs=DOORLOCK_NUMBERS, - chime_descs=CHIME_NUMBERS, + model_descriptions=_MODEL_DESCRIPTIONS, ufp_device=device, ) async_add_entities(entities) @@ -246,11 +251,7 @@ async def async_setup_entry( entities: list[ProtectDeviceEntity] = async_all_device_entities( data, ProtectNumbers, - camera_descs=CAMERA_NUMBERS, - light_descs=LIGHT_NUMBERS, - sense_descs=SENSE_NUMBERS, - lock_descs=DOORLOCK_NUMBERS, - chime_descs=CHIME_NUMBERS, + model_descriptions=_MODEL_DESCRIPTIONS, ) async_add_entities(entities) diff --git a/homeassistant/components/unifiprotect/select.py b/homeassistant/components/unifiprotect/select.py index 5ba557a8af6..678d0007347 100644 --- a/homeassistant/components/unifiprotect/select.py +++ b/homeassistant/components/unifiprotect/select.py @@ -2,7 +2,7 @@ from __future__ import annotations -from collections.abc import Callable +from collections.abc import Callable, Sequence from dataclasses import dataclass from enum import Enum import logging @@ -18,6 +18,7 @@ from uiprotect.data import ( Light, LightModeEnableType, LightModeType, + ModelType, MountType, ProtectAdoptableDeviceModel, ProtectModelWithId, @@ -35,7 +36,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DISPATCH_ADOPT, TYPE_EMPTY_VALUE from .data import ProtectData, UFPConfigEntry from .entity import ProtectDeviceEntity, async_all_device_entities -from .models import PermRequired, ProtectSetableKeysMixin, T +from .models import PermRequired, ProtectRequiredKeysMixin, ProtectSetableKeysMixin, T from .utils import async_dispatch_id as _ufpd, async_get_light_motion_current _LOGGER = logging.getLogger(__name__) @@ -319,6 +320,14 @@ VIEWER_SELECTS: tuple[ProtectSelectEntityDescription, ...] = ( ), ) +_MODEL_DESCRIPTIONS: dict[ModelType, Sequence[ProtectRequiredKeysMixin]] = { + ModelType.CAMERA: CAMERA_SELECTS, + ModelType.LIGHT: LIGHT_SELECTS, + ModelType.SENSOR: SENSE_SELECTS, + ModelType.VIEWPORT: VIEWER_SELECTS, + ModelType.DOORLOCK: DOORLOCK_SELECTS, +} + async def async_setup_entry( hass: HomeAssistant, entry: UFPConfigEntry, async_add_entities: AddEntitiesCallback @@ -331,11 +340,7 @@ async def async_setup_entry( entities = async_all_device_entities( data, ProtectSelects, - camera_descs=CAMERA_SELECTS, - light_descs=LIGHT_SELECTS, - sense_descs=SENSE_SELECTS, - viewer_descs=VIEWER_SELECTS, - lock_descs=DOORLOCK_SELECTS, + model_descriptions=_MODEL_DESCRIPTIONS, ufp_device=device, ) async_add_entities(entities) @@ -345,13 +350,7 @@ async def async_setup_entry( ) entities: list[ProtectDeviceEntity] = async_all_device_entities( - data, - ProtectSelects, - camera_descs=CAMERA_SELECTS, - light_descs=LIGHT_SELECTS, - sense_descs=SENSE_SELECTS, - viewer_descs=VIEWER_SELECTS, - lock_descs=DOORLOCK_SELECTS, + data, ProtectSelects, model_descriptions=_MODEL_DESCRIPTIONS ) async_add_entities(entities) diff --git a/homeassistant/components/unifiprotect/sensor.py b/homeassistant/components/unifiprotect/sensor.py index 95b01710b9b..7624a659d38 100644 --- a/homeassistant/components/unifiprotect/sensor.py +++ b/homeassistant/components/unifiprotect/sensor.py @@ -2,6 +2,7 @@ from __future__ import annotations +from collections.abc import Sequence from dataclasses import dataclass from datetime import datetime import logging @@ -608,6 +609,15 @@ VIEWER_SENSORS: tuple[ProtectSensorEntityDescription, ...] = ( ), ) +_MODEL_DESCRIPTIONS: dict[ModelType, Sequence[ProtectRequiredKeysMixin]] = { + ModelType.CAMERA: CAMERA_SENSORS + CAMERA_DISABLED_SENSORS, + ModelType.SENSOR: SENSE_SENSORS, + ModelType.LIGHT: LIGHT_SENSORS, + ModelType.DOORLOCK: DOORLOCK_SENSORS, + ModelType.CHIME: CHIME_SENSORS, + ModelType.VIEWPORT: VIEWER_SENSORS, +} + async def async_setup_entry( hass: HomeAssistant, @@ -623,12 +633,7 @@ async def async_setup_entry( data, ProtectDeviceSensor, all_descs=ALL_DEVICES_SENSORS, - camera_descs=CAMERA_SENSORS + CAMERA_DISABLED_SENSORS, - sense_descs=SENSE_SENSORS, - light_descs=LIGHT_SENSORS, - lock_descs=DOORLOCK_SENSORS, - chime_descs=CHIME_SENSORS, - viewer_descs=VIEWER_SENSORS, + model_descriptions=_MODEL_DESCRIPTIONS, ufp_device=device, ) if device.is_adopted_by_us and isinstance(device, Camera): @@ -643,12 +648,7 @@ async def async_setup_entry( data, ProtectDeviceSensor, all_descs=ALL_DEVICES_SENSORS, - camera_descs=CAMERA_SENSORS + CAMERA_DISABLED_SENSORS, - sense_descs=SENSE_SENSORS, - light_descs=LIGHT_SENSORS, - lock_descs=DOORLOCK_SENSORS, - chime_descs=CHIME_SENSORS, - viewer_descs=VIEWER_SENSORS, + model_descriptions=_MODEL_DESCRIPTIONS, ) entities += _async_event_entities(data) entities += _async_nvr_entities(data) diff --git a/homeassistant/components/unifiprotect/switch.py b/homeassistant/components/unifiprotect/switch.py index d13c49af882..fafa9d1f90d 100644 --- a/homeassistant/components/unifiprotect/switch.py +++ b/homeassistant/components/unifiprotect/switch.py @@ -2,6 +2,7 @@ from __future__ import annotations +from collections.abc import Sequence from dataclasses import dataclass import logging from typing import Any @@ -9,6 +10,7 @@ from typing import Any from uiprotect.data import ( NVR, Camera, + ModelType, ProtectAdoptableDeviceModel, ProtectModelWithId, RecordingMode, @@ -25,7 +27,7 @@ from homeassistant.helpers.restore_state import RestoreEntity from .const import DISPATCH_ADOPT from .data import ProtectData, UFPConfigEntry from .entity import ProtectDeviceEntity, ProtectNVREntity, async_all_device_entities -from .models import PermRequired, ProtectSetableKeysMixin, T +from .models import PermRequired, ProtectRequiredKeysMixin, ProtectSetableKeysMixin, T from .utils import async_dispatch_id as _ufpd _LOGGER = logging.getLogger(__name__) @@ -455,6 +457,18 @@ NVR_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = ( ), ) +_MODEL_DESCRIPTIONS: dict[ModelType, Sequence[ProtectRequiredKeysMixin]] = { + ModelType.CAMERA: CAMERA_SWITCHES, + ModelType.LIGHT: LIGHT_SWITCHES, + ModelType.SENSOR: SENSE_SWITCHES, + ModelType.DOORLOCK: DOORLOCK_SWITCHES, + ModelType.VIEWPORT: VIEWER_SWITCHES, +} + +_PRIVACY_MODEL_DESCRIPTIONS: dict[ModelType, Sequence[ProtectRequiredKeysMixin]] = { + ModelType.CAMERA: [PRIVACY_MODE_SWITCH] +} + async def async_setup_entry( hass: HomeAssistant, @@ -469,17 +483,13 @@ async def async_setup_entry( entities = async_all_device_entities( data, ProtectSwitch, - camera_descs=CAMERA_SWITCHES, - light_descs=LIGHT_SWITCHES, - sense_descs=SENSE_SWITCHES, - lock_descs=DOORLOCK_SWITCHES, - viewer_descs=VIEWER_SWITCHES, + model_descriptions=_MODEL_DESCRIPTIONS, ufp_device=device, ) entities += async_all_device_entities( data, ProtectPrivacyModeSwitch, - camera_descs=[PRIVACY_MODE_SWITCH], + model_descriptions=_PRIVACY_MODEL_DESCRIPTIONS, ufp_device=device, ) async_add_entities(entities) @@ -491,16 +501,12 @@ async def async_setup_entry( entities: list[ProtectDeviceEntity] = async_all_device_entities( data, ProtectSwitch, - camera_descs=CAMERA_SWITCHES, - light_descs=LIGHT_SWITCHES, - sense_descs=SENSE_SWITCHES, - lock_descs=DOORLOCK_SWITCHES, - viewer_descs=VIEWER_SWITCHES, + model_descriptions=_MODEL_DESCRIPTIONS, ) entities += async_all_device_entities( data, ProtectPrivacyModeSwitch, - camera_descs=[PRIVACY_MODE_SWITCH], + model_descriptions=_PRIVACY_MODEL_DESCRIPTIONS, ) if ( diff --git a/homeassistant/components/unifiprotect/text.py b/homeassistant/components/unifiprotect/text.py index c267419bd6d..5fc11546fae 100644 --- a/homeassistant/components/unifiprotect/text.py +++ b/homeassistant/components/unifiprotect/text.py @@ -2,12 +2,14 @@ from __future__ import annotations +from collections.abc import Sequence from dataclasses import dataclass from typing import Any from uiprotect.data import ( Camera, DoorbellMessageType, + ModelType, ProtectAdoptableDeviceModel, ProtectModelWithId, ) @@ -21,7 +23,7 @@ from homeassistant.helpers.entity_platform import AddEntitiesCallback from .const import DISPATCH_ADOPT from .data import ProtectData, UFPConfigEntry from .entity import ProtectDeviceEntity, async_all_device_entities -from .models import PermRequired, ProtectSetableKeysMixin, T +from .models import PermRequired, ProtectRequiredKeysMixin, ProtectSetableKeysMixin, T from .utils import async_dispatch_id as _ufpd @@ -52,6 +54,10 @@ CAMERA: tuple[ProtectTextEntityDescription, ...] = ( ), ) +_MODEL_DESCRIPTIONS: dict[ModelType, Sequence[ProtectRequiredKeysMixin]] = { + ModelType.CAMERA: CAMERA, +} + async def async_setup_entry( hass: HomeAssistant, @@ -66,7 +72,7 @@ async def async_setup_entry( entities = async_all_device_entities( data, ProtectDeviceText, - camera_descs=CAMERA, + model_descriptions=_MODEL_DESCRIPTIONS, ufp_device=device, ) async_add_entities(entities) @@ -76,9 +82,7 @@ async def async_setup_entry( ) entities: list[ProtectDeviceEntity] = async_all_device_entities( - data, - ProtectDeviceText, - camera_descs=CAMERA, + data, ProtectDeviceText, model_descriptions=_MODEL_DESCRIPTIONS ) async_add_entities(entities)