Cleanup unifiprotect entity classes (#121184)

This commit is contained in:
J. Nick Koston 2024-07-05 02:31:31 -05:00 committed by GitHub
parent 22718ca32a
commit d3f424227f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 129 additions and 165 deletions

View File

@ -8,11 +8,9 @@ import dataclasses
from uiprotect.data import (
NVR,
Camera,
Light,
ModelType,
MountType,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
Sensor,
SmartDetectObjectType,
)
@ -27,11 +25,12 @@ from homeassistant.const import EntityCategory
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,
)
@ -623,31 +622,22 @@ _MOUNTABLE_MODEL_DESCRIPTIONS: dict[ModelType, Sequence[ProtectEntityDescription
}
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(
@ -673,7 +663,6 @@ class ProtectDiskBinarySensor(ProtectNVREntity, BinarySensorEntity):
self._disk = disk
# backwards compat with old unique IDs
index = self._disk.slot - 1
description = dataclasses.replace(
description,
key=f"{description.key}_{index}",
@ -682,7 +671,7 @@ class ProtectDiskBinarySensor(ProtectNVREntity, BinarySensorEntity):
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
@ -712,7 +701,7 @@ class ProtectEventBinarySensor(EventEntityMixin, BinarySensorEntity):
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

View File

@ -4,10 +4,11 @@ from __future__ import annotations
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,
@ -21,7 +22,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
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
@ -38,7 +39,6 @@ class ProtectButtonEntityDescription(
DEVICE_CLASS_CHIME_BUTTON: Final = "unifiprotect__chime_button"
KEY_ADOPT = "adopt"
ALL_DEVICE_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = (
@ -61,7 +61,7 @@ ALL_DEVICE_BUTTONS: tuple[ProtectButtonEntityDescription, ...] = (
)
ADOPT_BUTTON = ProtectButtonEntityDescription[ProtectAdoptableDeviceModel](
key=KEY_ADOPT,
key="adopt",
name="Adopt device",
icon="mdi:plus-circle",
ufp_press="adopt",
@ -119,17 +119,25 @@ 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
@ -137,29 +145,13 @@ async def async_setup_entry(
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)
@ -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
)

View File

@ -9,7 +9,6 @@ from uiprotect.data import (
Camera as UFPCamera,
CameraChannel,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
StateType,
)
@ -28,7 +27,7 @@ from .const import (
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
@ -216,7 +215,7 @@ class ProtectCamera(ProtectDeviceEntity, Camera):
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]

View File

@ -9,14 +9,7 @@ import logging
from operator import attrgetter
from typing import TYPE_CHECKING
from uiprotect.data import (
NVR,
Event,
ModelType,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
StateType,
)
from uiprotect.data import NVR, Event, ModelType, ProtectAdoptableDeviceModel, StateType
from homeassistant.core import callback
import homeassistant.helpers.device_registry as dr
@ -30,7 +23,7 @@ from .const import (
DEFAULT_BRAND,
DOMAIN,
)
from .data import ProtectData
from .data import ProtectData, ProtectDeviceType
from .models import PermRequired, ProtectEntityDescription, ProtectEventMixin
_LOGGER = logging.getLogger(__name__)
@ -160,7 +153,7 @@ def async_all_device_entities(
class BaseProtectEntity(Entity):
"""Base class for UniFi protect entities."""
device: ProtectAdoptableDeviceModel | NVR
device: ProtectDeviceType
_attr_should_poll = False
_attr_attribution = DEFAULT_ATTRIBUTION
@ -171,7 +164,7 @@ class BaseProtectEntity(Entity):
def __init__(
self,
data: ProtectData,
device: ProtectAdoptableDeviceModel | NVR,
device: ProtectDeviceType,
description: EntityDescription | None = None,
) -> None:
"""Initialize the entity."""
@ -203,37 +196,32 @@ class BaseProtectEntity(Entity):
@callback
def _async_set_device_info(self) -> None:
self._attr_device_info = DeviceInfo(
name=self.device.display_name,
manufacturer=DEFAULT_BRAND,
model=self.device.type,
via_device=(DOMAIN, self.data.api.bootstrap.nvr.mac),
sw_version=self.device.firmware_version,
connections={(dr.CONNECTION_NETWORK_MAC, self.device.mac)},
configuration_url=self.device.protect_url,
)
"""Set device info."""
@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
def _async_update_device_from_protect(self, device: ProtectDeviceType) -> None:
"""Update Entity object from Protect device."""
if TYPE_CHECKING:
assert isinstance(device, ProtectAdoptableDeviceModel)
if last_update_success := self.data.last_update_success:
was_available = self._attr_available
if last_updated_success := self.data.last_update_success:
self.device = device
async_get_ufp_enabled = self._async_get_ufp_enabled
self._attr_available = (
last_update_success
and (
device.state is StateType.CONNECTED
or (not device.is_adopted_by_us and device.can_adopt)
if device.model is ModelType.NVR:
available = last_updated_success
else:
if TYPE_CHECKING:
assert isinstance(device, ProtectAdoptableDeviceModel)
connected = device.state is StateType.CONNECTED or (
not device.is_adopted_by_us and device.can_adopt
)
and (not async_get_ufp_enabled or async_get_ufp_enabled(device))
)
async_get_ufp_enabled = self._async_get_ufp_enabled
enabled = not async_get_ufp_enabled or async_get_ufp_enabled(device)
available = last_updated_success and connected and enabled
if available != was_available:
self._attr_available = available
@callback
def _async_updated_event(self, device: ProtectAdoptableDeviceModel | NVR) -> None:
def _async_updated_event(self, device: ProtectDeviceType) -> None:
"""When device is updated from Protect."""
previous_attrs = [getter() for getter in self._state_getters]
self._async_update_device_from_protect(device)
@ -266,10 +254,36 @@ class BaseProtectEntity(Entity):
)
class ProtectIsOnEntity(BaseProtectEntity):
"""Base class for entities with is_on property."""
_state_attrs: tuple[str, ...] = ("_attr_available", "_attr_is_on")
_attr_is_on: bool | None
entity_description: ProtectEntityDescription
def _async_update_device_from_protect(
self, device: ProtectAdoptableDeviceModel | NVR
) -> None:
super()._async_update_device_from_protect(device)
was_on = self._attr_is_on
if was_on != (is_on := self.entity_description.get_ufp_value(device) is True):
self._attr_is_on = is_on
class ProtectDeviceEntity(BaseProtectEntity):
"""Base class for UniFi protect entities."""
device: ProtectAdoptableDeviceModel
@callback
def _async_set_device_info(self) -> None:
self._attr_device_info = DeviceInfo(
name=self.device.display_name,
manufacturer=DEFAULT_BRAND,
model=self.device.type,
via_device=(DOMAIN, self.data.api.bootstrap.nvr.mac),
sw_version=self.device.firmware_version,
connections={(dr.CONNECTION_NETWORK_MAC, self.device.mac)},
configuration_url=self.device.protect_url,
)
class ProtectNVREntity(BaseProtectEntity):
@ -289,14 +303,6 @@ class ProtectNVREntity(BaseProtectEntity):
configuration_url=self.device.api.base_url,
)
@callback
def _async_update_device_from_protect(self, device: ProtectModelWithId) -> None:
data = self.data
if last_update_success := data.last_update_success:
self.device = data.api.bootstrap.nvr
self._attr_available = last_update_success
class EventEntityMixin(ProtectDeviceEntity):
"""Adds motion event attributes to sensor."""
@ -338,9 +344,8 @@ class EventEntityMixin(ProtectDeviceEntity):
event object so we need to check the datetime object that was
saved from the last time the entity was updated.
"""
event = self._event
return bool(
event
(event := self._event)
and event.end
and prev_event
and prev_event_end

View File

@ -4,12 +4,7 @@ from __future__ import annotations
import dataclasses
from uiprotect.data import (
Camera,
EventType,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
)
from uiprotect.data import Camera, EventType, ProtectAdoptableDeviceModel
from homeassistant.components.event import (
EventDeviceClass,
@ -20,7 +15,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import ATTR_EVENT_ID
from .data import ProtectData, UFPConfigEntry
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
from .entity import EventEntityMixin, ProtectDeviceEntity
from .models import ProtectEventMixin
@ -50,7 +45,7 @@ class ProtectDeviceEventEntity(EventEntityMixin, ProtectDeviceEntity, EventEntit
entity_description: ProtectEventEntityDescription
@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

View File

@ -5,18 +5,13 @@ from __future__ import annotations
import logging
from typing import Any
from uiprotect.data import (
Light,
ModelType,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
)
from uiprotect.data import Light, ModelType, ProtectAdoptableDeviceModel
from homeassistant.components.light import ATTR_BRIGHTNESS, ColorMode, LightEntity
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .data import UFPConfigEntry
from .data import ProtectDeviceType, UFPConfigEntry
from .entity import ProtectDeviceEntity
_LOGGER = logging.getLogger(__name__)
@ -66,7 +61,7 @@ class ProtectLight(ProtectDeviceEntity, LightEntity):
_state_attrs = ("_attr_available", "_attr_is_on", "_attr_brightness")
@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
self._attr_is_on = updated_device.is_light_on

View File

@ -10,14 +10,13 @@ from uiprotect.data import (
LockStatusType,
ModelType,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
)
from homeassistant.components.lock import LockEntity, LockEntityDescription
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .data import UFPConfigEntry
from .data import ProtectDeviceType, UFPConfigEntry
from .entity import ProtectDeviceEntity
_LOGGER = logging.getLogger(__name__)
@ -60,7 +59,7 @@ class ProtectLock(ProtectDeviceEntity, LockEntity):
)
@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)
lock_status = self.device.lock_status

View File

@ -5,12 +5,7 @@ from __future__ import annotations
import logging
from typing import Any
from uiprotect.data import (
Camera,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
StateType,
)
from uiprotect.data import Camera, ProtectAdoptableDeviceModel, StateType
from uiprotect.exceptions import StreamError
from homeassistant.components import media_source
@ -28,7 +23,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .data import UFPConfigEntry
from .data import ProtectDeviceType, UFPConfigEntry
from .entity import ProtectDeviceEntity
_LOGGER = logging.getLogger(__name__)
@ -77,7 +72,7 @@ class ProtectMediaPlayer(ProtectDeviceEntity, MediaPlayerEntity):
_state_attrs = ("_attr_available", "_attr_state", "_attr_volume_level")
@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
self._attr_volume_level = float(updated_device.speaker_settings.volume / 100)

View File

@ -12,7 +12,6 @@ from uiprotect.data import (
Light,
ModelType,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
)
from homeassistant.components.number import NumberEntity, NumberEntityDescription
@ -20,7 +19,7 @@ from homeassistant.const import PERCENTAGE, EntityCategory, UnitOfTime
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 ProtectDeviceEntity, async_all_device_entities
from .models import PermRequired, ProtectEntityDescription, ProtectSetableKeysMixin, T
@ -268,7 +267,7 @@ class ProtectNumbers(ProtectDeviceEntity, NumberEntity):
self._attr_native_step = self.entity_description.ufp_step
@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)
self._attr_native_value = self.entity_description.get_ufp_value(self.device)

View File

@ -21,7 +21,6 @@ from uiprotect.data import (
ModelType,
MountType,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
RecordingMode,
Sensor,
Viewer,
@ -33,7 +32,7 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .const import TYPE_EMPTY_VALUE
from .data import ProtectData, UFPConfigEntry
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
from .entity import ProtectDeviceEntity, async_all_device_entities
from .models import PermRequired, ProtectEntityDescription, ProtectSetableKeysMixin, T
from .utils import async_get_light_motion_current
@ -371,7 +370,7 @@ class ProtectSelects(ProtectDeviceEntity, SelectEntity):
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)
entity_description = self.entity_description
# entities with categories are not exposed for voice

View File

@ -16,7 +16,6 @@ from uiprotect.data import (
ModelType,
ProtectAdoptableDeviceModel,
ProtectDeviceModel,
ProtectModelWithId,
Sensor,
SmartDetectObjectType,
)
@ -41,7 +40,7 @@ from homeassistant.const import (
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,
@ -721,7 +720,7 @@ class BaseProtectSensor(BaseProtectEntity, SensorEntity):
entity_description: ProtectSensorEntityDescription
_state_attrs = ("_attr_available", "_attr_native_value")
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)
self._attr_native_value = self.entity_description.get_ufp_value(self.device)
@ -756,7 +755,7 @@ class ProtectLicensePlateEventSensor(ProtectEventSensor):
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

View File

@ -5,14 +5,12 @@ from __future__ import annotations
from collections.abc import Sequence
from dataclasses import dataclass
from functools import partial
import logging
from typing import Any
from uiprotect.data import (
Camera,
ModelType,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
RecordingMode,
VideoMode,
)
@ -23,16 +21,16 @@ from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity
from .data import ProtectData, UFPConfigEntry
from .data import ProtectData, ProtectDeviceType, UFPConfigEntry
from .entity import (
BaseProtectEntity,
ProtectDeviceEntity,
ProtectIsOnEntity,
ProtectNVREntity,
async_all_device_entities,
)
from .models import PermRequired, ProtectEntityDescription, ProtectSetableKeysMixin, T
_LOGGER = logging.getLogger(__name__)
ATTR_PREV_MIC = "prev_mic_level"
ATTR_PREV_RECORD = "prev_record_mode"
@ -45,10 +43,7 @@ class ProtectSwitchEntityDescription(
async def _set_highfps(obj: Camera, value: bool) -> None:
if value:
await obj.set_video_mode(VideoMode.HIGH_FPS)
else:
await obj.set_video_mode(VideoMode.DEFAULT)
await obj.set_video_mode(VideoMode.HIGH_FPS if value else VideoMode.DEFAULT)
CAMERA_SWITCHES: tuple[ProtectSwitchEntityDescription, ...] = (
@ -472,15 +467,10 @@ _PRIVACY_DESCRIPTIONS: dict[ModelType, Sequence[ProtectEntityDescription]] = {
}
class ProtectBaseSwitch(BaseProtectEntity, SwitchEntity):
class ProtectBaseSwitch(ProtectIsOnEntity):
"""Base class for UniFi Protect Switch."""
entity_description: ProtectSwitchEntityDescription
_state_attrs = ("_attr_available", "_attr_is_on")
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) is True
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn the device on."""
@ -491,18 +481,23 @@ class ProtectBaseSwitch(BaseProtectEntity, SwitchEntity):
await self.entity_description.ufp_set(self.device, False)
class ProtectSwitch(ProtectBaseSwitch, ProtectDeviceEntity):
class ProtectSwitch(ProtectDeviceEntity, ProtectBaseSwitch, SwitchEntity):
"""A UniFi Protect Switch."""
entity_description: ProtectSwitchEntityDescription
class ProtectNVRSwitch(ProtectBaseSwitch, ProtectNVREntity):
class ProtectNVRSwitch(ProtectNVREntity, ProtectBaseSwitch, SwitchEntity):
"""A UniFi Protect NVR Switch."""
entity_description: ProtectSwitchEntityDescription
class ProtectPrivacyModeSwitch(RestoreEntity, ProtectSwitch):
"""A UniFi Protect Switch."""
device: Camera
entity_description: ProtectSwitchEntityDescription
def __init__(
self,
@ -533,7 +528,7 @@ class ProtectPrivacyModeSwitch(RestoreEntity, ProtectSwitch):
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:
super()._async_update_device_from_protect(device)
# do not add extra state attribute on initialize
if self.entity_id:

View File

@ -10,7 +10,6 @@ from uiprotect.data import (
DoorbellMessageType,
ModelType,
ProtectAdoptableDeviceModel,
ProtectModelWithId,
)
from homeassistant.components.text import TextEntity, TextEntityDescription
@ -18,7 +17,7 @@ from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .data import UFPConfigEntry
from .data import ProtectDeviceType, UFPConfigEntry
from .entity import ProtectDeviceEntity, async_all_device_entities
from .models import PermRequired, ProtectEntityDescription, ProtectSetableKeysMixin, T
@ -89,7 +88,7 @@ class ProtectDeviceText(ProtectDeviceEntity, TextEntity):
_state_attrs = ("_attr_available", "_attr_native_value")
@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)
self._attr_native_value = self.entity_description.get_ufp_value(self.device)