Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup unifiprotect entity classes #121184

Merged
merged 31 commits into from
Jul 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
23cd401
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
d642d0e
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
5e3a93b
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
49e9166
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
9c21adb
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
7fa912c
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
25dfcd7
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
128c398
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
b3dcf91
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
7deee4d
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
b9d461f
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
0f28fd2
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
4065a1d
Revert "Reduce duplicate code in unifiprotect"
bdraco Jul 4, 2024
d8fe634
Reduce duplicate code in unifiprotect
bdraco Jul 4, 2024
0943798
cleanups
bdraco Jul 4, 2024
86e50a9
cleanups
bdraco Jul 4, 2024
b84d555
cleanups
bdraco Jul 4, 2024
2eacdd6
cleanups
bdraco Jul 4, 2024
925655a
cleanups
bdraco Jul 4, 2024
0c45a6e
cleanups
bdraco Jul 4, 2024
bf74c10
fix storage
bdraco Jul 4, 2024
d2a61a0
fix storage
bdraco Jul 4, 2024
aa0d7bf
fix storage
bdraco Jul 4, 2024
6da0717
fix storage
bdraco Jul 4, 2024
e0a8f77
fix storage
bdraco Jul 4, 2024
b0b7361
fix storage
bdraco Jul 4, 2024
90898bb
fix storage
bdraco Jul 4, 2024
2c13392
typing
bdraco Jul 4, 2024
07079f0
clean
bdraco Jul 4, 2024
fcb8750
clean
bdraco Jul 4, 2024
31ee74b
drop bugfix
bdraco Jul 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 9 additions & 20 deletions homeassistant/components/unifiprotect/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
from uiprotect.data import (
NVR,
Camera,
Light,
ModelType,
MountType,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
Sensor,
SmartDetectObjectType,
)
Expand All @@ -27,11 +25,12 @@
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .data import ProtectData, UFPConfigEntry
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
from .entity import (
BaseProtectEntity,
EventEntityMixin,
ProtectDeviceEntity,
ProtectIsOnEntity,
ProtectNVREntity,
async_all_device_entities,
)
Expand Down Expand Up @@ -623,31 +622,22 @@ class ProtectBinaryEventEntityDescription(
}


class ProtectDeviceBinarySensor(ProtectDeviceEntity, BinarySensorEntity):
class ProtectDeviceBinarySensor(
ProtectIsOnEntity, ProtectDeviceEntity, BinarySensorEntity
):
"""A UniFi Protect Device Binary Sensor."""

device: Camera | Light | Sensor
entity_description: ProtectBinaryEntityDescription
_state_attrs: tuple[str, ...] = ("_attr_available", "_attr_is_on")

@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
super()._async_update_device_from_protect(device)
self._attr_is_on = self.entity_description.get_ufp_value(self.device)


class MountableProtectDeviceBinarySensor(ProtectDeviceBinarySensor):
"""A UniFi Protect Device Binary Sensor that can change device class at runtime."""

device: Sensor
_state_attrs: tuple[str, ...] = (
"_attr_available",
"_attr_is_on",
"_attr_device_class",
)
_state_attrs = ("_attr_available", "_attr_is_on", "_attr_device_class")

@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
def _async_update_device_from_protect(self, device: ProtectDeviceType) -> None:
super()._async_update_device_from_protect(device)
# UP Sense can be any of the 3 contact sensor device classes
self._attr_device_class = MOUNT_DEVICE_CLASS_MAP.get(
Expand All @@ -673,7 +663,6 @@ def __init__(
self._disk = disk
# backwards compat with old unique IDs
index = self._disk.slot - 1

description = dataclasses.replace(
description,
key=f"{description.key}_{index}",
Expand All @@ -682,7 +671,7 @@ def __init__(
super().__init__(data, device, description)

@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
def _async_update_device_from_protect(self, device: ProtectDeviceType) -> None:
super()._async_update_device_from_protect(device)
slot = self._disk.slot
self._attr_available = False
Expand Down Expand Up @@ -712,7 +701,7 @@ def _set_event_done(self) -> None:
self._attr_extra_state_attributes = {}

@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
def _async_update_device_from_protect(self, device: ProtectDeviceType) -> None:
description = self.entity_description

prev_event = self._event
Expand Down
76 changes: 36 additions & 40 deletions homeassistant/components/unifiprotect/button.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

from collections.abc import Sequence
from dataclasses import dataclass
from functools import partial
import logging
from typing import Final
from typing import TYPE_CHECKING, Final

from uiprotect.data import ModelType, ProtectAdoptableDeviceModel, ProtectModelWithId
from uiprotect.data import ModelType, ProtectAdoptableDeviceModel

from homeassistant.components.button import (
ButtonDeviceClass,
Expand All @@ -21,7 +22,7 @@
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .const import DEVICES_THAT_ADOPT, DOMAIN
from .data import UFPConfigEntry
from .data import ProtectDeviceType, UFPConfigEntry
from .entity import ProtectDeviceEntity, async_all_device_entities
from .models import PermRequired, ProtectEntityDescription, ProtectSetableKeysMixin, T

Expand All @@ -38,7 +39,6 @@ class ProtectButtonEntityDescription(


DEVICE_CLASS_CHIME_BUTTON: Final = "unifiprotect__chime_button"
KEY_ADOPT = "adopt"


ALL_DEVICE_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = (
Expand All @@ -61,7 +61,7 @@ class ProtectButtonEntityDescription(
)

ADOPT_BUTTON = ProtectButtonEntityDescription[ProtectAdoptableDeviceModel](
key=KEY_ADOPT,
key="adopt",
name="Adopt device",
icon="mdi:plus-circle",
ufp_press="adopt",
Expand Down Expand Up @@ -119,47 +119,39 @@ async def async_setup_entry(
"""Discover devices on a UniFi Protect NVR."""
data = entry.runtime_data

adopt_entities = partial(
async_all_device_entities,
data,
ProtectAdoptButton,
unadopted_descs=[ADOPT_BUTTON],
)
base_entities = partial(
async_all_device_entities,
data,
ProtectButton,
all_descs=ALL_DEVICE_BUTTONS,
model_descriptions=_MODEL_DESCRIPTIONS,
)

@callback
def _add_new_device(device: ProtectAdoptableDeviceModel) -> None:
entities = async_all_device_entities(
data,
ProtectButton,
all_descs=ALL_DEVICE_BUTTONS,
unadopted_descs=[ADOPT_BUTTON],
model_descriptions=_MODEL_DESCRIPTIONS,
ufp_device=device,
async_add_entities(
[*base_entities(ufp_device=device), *adopt_entities(ufp_device=device)]
)
async_add_entities(entities)
_async_remove_adopt_button(hass, device)

@callback
def _async_add_unadopted_device(device: ProtectAdoptableDeviceModel) -> None:
if not device.can_adopt or not device.can_create(data.api.bootstrap.auth_user):
_LOGGER.debug("Device is not adoptable: %s", device.id)
return
async_add_entities(
async_all_device_entities(
data,
ProtectButton,
unadopted_descs=[ADOPT_BUTTON],
ufp_device=device,
)
)
async_add_entities(adopt_entities(ufp_device=device))

data.async_subscribe_adopt(_add_new_device)
entry.async_on_unload(
async_dispatcher_connect(hass, data.add_signal, _async_add_unadopted_device)
)

async_add_entities(
async_all_device_entities(
data,
ProtectButton,
all_descs=ALL_DEVICE_BUTTONS,
unadopted_descs=[ADOPT_BUTTON],
model_descriptions=_MODEL_DESCRIPTIONS,
)
)
async_add_entities([*base_entities(), *adopt_entities()])

for device in data.get_by_types(DEVICES_THAT_ADOPT):
_async_remove_adopt_button(hass, device)
Expand All @@ -170,16 +162,20 @@ class ProtectButton(ProtectDeviceEntity, ButtonEntity):

entity_description: ProtectButtonEntityDescription

@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
super()._async_update_device_from_protect(device)
if self.entity_description.key == KEY_ADOPT:
device = self.device
self._attr_available = device.can_adopt and device.can_create(
self.data.api.bootstrap.auth_user
)

async def async_press(self) -> None:
"""Press the button."""
if self.entity_description.ufp_press is not None:
await getattr(self.device, self.entity_description.ufp_press)()


class ProtectAdoptButton(ProtectButton):
"""A Ubiquiti UniFi Protect Adopt button."""

@callback
def _async_update_device_from_protect(self, device: ProtectDeviceType) -> None:
super()._async_update_device_from_protect(device)
if TYPE_CHECKING:
assert isinstance(device, ProtectAdoptableDeviceModel)
self._attr_available = device.can_adopt and device.can_create(
self.data.api.bootstrap.auth_user
)
5 changes: 2 additions & 3 deletions homeassistant/components/unifiprotect/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
Camera as UFPCamera,
CameraChannel,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
StateType,
)

Expand All @@ -28,7 +27,7 @@
ATTR_WIDTH,
DOMAIN,
)
from .data import ProtectData, UFPConfigEntry
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
from .entity import ProtectDeviceEntity
from .utils import get_camera_base_name

Expand Down Expand Up @@ -216,7 +215,7 @@ def _async_set_stream_source(self) -> None:
self._attr_supported_features = _EMPTY_CAMERA_FEATURES

@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
def _async_update_device_from_protect(self, device: ProtectDeviceType) -> None:
super()._async_update_device_from_protect(device)
updated_device = self.device
channel = updated_device.channels[self.channel.id]
Expand Down
Loading
Loading