Refactor adding entities to unifiprotect (#119512)

This commit is contained in:
J. Nick Koston 2024-06-12 11:11:59 -05:00 committed by GitHub
parent b92372c4ca
commit 4fb8202de1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 145 additions and 154 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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
)

View file

@ -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)

View file

@ -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)

View file

@ -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)

View file

@ -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 (

View file

@ -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)