Implement generic in Deconz base device (#76015)

* Make DevonzBase a generic

* Adjust alarm_control_panel

* Adjust binary_sensor

* Adjust climate

* More platforms

* Adjust light

* Ignore type-var

* Add space

* Implement recommendation

* Use type: ignore[union-attr]

* Revert "Use type: ignore[union-attr]"

This reverts commit 983443062aab0a9c599b2750d823d0c5148c05ce.

* Adjust assert

* Adjust lock

* Rename type variables

* type: ignore[union-attr]

* Formatting

Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
This commit is contained in:
epenet 2022-08-01 19:18:29 +02:00 committed by GitHub
parent 567f181a21
commit deff0ad61e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 41 additions and 46 deletions

View File

@ -80,11 +80,10 @@ async def async_setup_entry(
)
class DeconzAlarmControlPanel(DeconzDevice, AlarmControlPanelEntity):
class DeconzAlarmControlPanel(DeconzDevice[AncillaryControl], AlarmControlPanelEntity):
"""Representation of a deCONZ alarm control panel."""
TYPE = DOMAIN
_device: AncillaryControl
_attr_code_format = CodeFormat.NUMBER
_attr_supported_features = (

View File

@ -233,11 +233,10 @@ async def async_setup_entry(
)
class DeconzBinarySensor(DeconzDevice, BinarySensorEntity):
class DeconzBinarySensor(DeconzDevice[SensorResources], BinarySensorEntity):
"""Representation of a deCONZ binary sensor."""
TYPE = DOMAIN
_device: SensorResources
entity_description: DeconzBinarySensorDescription
def __init__(

View File

@ -92,11 +92,10 @@ async def async_setup_entry(
)
class DeconzThermostat(DeconzDevice, ClimateEntity):
class DeconzThermostat(DeconzDevice[Thermostat], ClimateEntity):
"""Representation of a deCONZ thermostat."""
TYPE = DOMAIN
_device: Thermostat
_attr_temperature_unit = TEMP_CELSIUS

View File

@ -49,11 +49,10 @@ async def async_setup_entry(
)
class DeconzCover(DeconzDevice, CoverEntity):
class DeconzCover(DeconzDevice[Cover], CoverEntity):
"""Representation of a deCONZ cover."""
TYPE = DOMAIN
_device: Cover
def __init__(self, cover_id: str, gateway: DeconzGateway) -> None:
"""Set up cover device."""

View File

@ -2,10 +2,13 @@
from __future__ import annotations
from pydeconz.models.group import Group as DeconzGroup
from pydeconz.models.light import LightBase as DeconzLight
from typing import Generic, TypeVar, Union
from pydeconz.models.deconz_device import DeconzDevice as PydeconzDevice
from pydeconz.models.group import Group as PydeconzGroup
from pydeconz.models.light import LightBase as PydeconzLightBase
from pydeconz.models.scene import Scene as PydeconzScene
from pydeconz.models.sensor import SensorBase as DeconzSensor
from pydeconz.models.sensor import SensorBase as PydeconzSensorBase
from homeassistant.core import callback
from homeassistant.helpers.device_registry import CONNECTION_ZIGBEE
@ -15,29 +18,39 @@ from homeassistant.helpers.entity import DeviceInfo, Entity
from .const import DOMAIN as DECONZ_DOMAIN
from .gateway import DeconzGateway
_DeviceT = TypeVar(
"_DeviceT",
bound=Union[
PydeconzGroup,
PydeconzLightBase,
PydeconzSensorBase,
PydeconzScene,
],
)
class DeconzBase:
class DeconzBase(Generic[_DeviceT]):
"""Common base for deconz entities and events."""
def __init__(
self,
device: DeconzGroup | DeconzLight | DeconzSensor | PydeconzScene,
device: _DeviceT,
gateway: DeconzGateway,
) -> None:
"""Set up device and add update callback to get data from websocket."""
self._device = device
self._device: _DeviceT = device
self.gateway = gateway
@property
def unique_id(self) -> str:
"""Return a unique identifier for this device."""
assert not isinstance(self._device, PydeconzScene)
assert isinstance(self._device, PydeconzDevice)
return self._device.unique_id
@property
def serial(self) -> str | None:
"""Return a serial number for this device."""
assert not isinstance(self._device, PydeconzScene)
assert isinstance(self._device, PydeconzDevice)
if not self._device.unique_id or self._device.unique_id.count(":") != 7:
return None
return self._device.unique_id.split("-", 1)[0]
@ -45,7 +58,7 @@ class DeconzBase:
@property
def device_info(self) -> DeviceInfo | None:
"""Return a device description for device registry."""
assert not isinstance(self._device, PydeconzScene)
assert isinstance(self._device, PydeconzDevice)
if self.serial is None:
return None
@ -60,7 +73,7 @@ class DeconzBase:
)
class DeconzDevice(DeconzBase, Entity):
class DeconzDevice(DeconzBase[_DeviceT], Entity):
"""Representation of a deCONZ device."""
_attr_should_poll = False
@ -69,7 +82,7 @@ class DeconzDevice(DeconzBase, Entity):
def __init__(
self,
device: DeconzGroup | DeconzLight | DeconzSensor | PydeconzScene,
device: _DeviceT,
gateway: DeconzGateway,
) -> None:
"""Set up device and add update callback to get data from websocket."""
@ -114,16 +127,14 @@ class DeconzDevice(DeconzBase, Entity):
"""Return True if device is available."""
if isinstance(self._device, PydeconzScene):
return self.gateway.available
return self.gateway.available and self._device.reachable
return self.gateway.available and self._device.reachable # type: ignore[union-attr]
class DeconzSceneMixin(DeconzDevice):
class DeconzSceneMixin(DeconzDevice[PydeconzScene]):
"""Representation of a deCONZ scene."""
_attr_has_entity_name = True
_device: PydeconzScene
def __init__(
self,
device: PydeconzScene,

View File

@ -49,11 +49,10 @@ async def async_setup_entry(
)
class DeconzFan(DeconzDevice, FanEntity):
class DeconzFan(DeconzDevice[Light], FanEntity):
"""Representation of a deCONZ fan."""
TYPE = DOMAIN
_device: Light
_default_on_speed = LightFanSpeed.PERCENT_50
_attr_supported_features = FanEntityFeature.SET_SPEED

View File

@ -1,7 +1,7 @@
"""Support for deCONZ lights."""
from __future__ import annotations
from typing import Any, Generic, TypedDict, TypeVar
from typing import Any, TypedDict, TypeVar, Union
from pydeconz.interfaces.groups import GroupHandler
from pydeconz.interfaces.lights import LightHandler
@ -47,7 +47,7 @@ DECONZ_TO_COLOR_MODE = {
LightColorMode.XY: ColorMode.XY,
}
_L = TypeVar("_L", Group, Light)
_LightDeviceT = TypeVar("_LightDeviceT", bound=Union[Group, Light])
class SetStateAttributes(TypedDict, total=False):
@ -121,14 +121,12 @@ async def async_setup_entry(
)
class DeconzBaseLight(Generic[_L], DeconzDevice, LightEntity):
class DeconzBaseLight(DeconzDevice[_LightDeviceT], LightEntity):
"""Representation of a deCONZ light."""
TYPE = DOMAIN
_device: _L
def __init__(self, device: _L, gateway: DeconzGateway) -> None:
def __init__(self, device: _LightDeviceT, gateway: DeconzGateway) -> None:
"""Set up light."""
super().__init__(device, gateway)
@ -261,8 +259,6 @@ class DeconzBaseLight(Generic[_L], DeconzDevice, LightEntity):
class DeconzLight(DeconzBaseLight[Light]):
"""Representation of a deCONZ light."""
_device: Light
@property
def max_mireds(self) -> int:
"""Return the warmest color_temp that this light supports."""
@ -289,8 +285,6 @@ class DeconzGroup(DeconzBaseLight[Group]):
_attr_has_entity_name = True
_device: Group
def __init__(self, device: Group, gateway: DeconzGateway) -> None:
"""Set up group and create an unique id."""
self._unique_id = f"{gateway.bridgeid}-{device.deconz_id}"

View File

@ -2,7 +2,7 @@
from __future__ import annotations
from typing import Any
from typing import Any, Union
from pydeconz.models.event import EventType
from pydeconz.models.light.lock import Lock
@ -50,11 +50,10 @@ async def async_setup_entry(
)
class DeconzLock(DeconzDevice, LockEntity):
class DeconzLock(DeconzDevice[Union[DoorLock, Lock]], LockEntity):
"""Representation of a deCONZ lock."""
TYPE = DOMAIN
_device: DoorLock | Lock
@property
def is_locked(self) -> bool:

View File

@ -81,11 +81,10 @@ async def async_setup_entry(
)
class DeconzNumber(DeconzDevice, NumberEntity):
class DeconzNumber(DeconzDevice[Presence], NumberEntity):
"""Representation of a deCONZ number entity."""
TYPE = DOMAIN
_device: Presence
def __init__(
self,

View File

@ -273,11 +273,10 @@ async def async_setup_entry(
)
class DeconzSensor(DeconzDevice, SensorEntity):
class DeconzSensor(DeconzDevice[SensorResources], SensorEntity):
"""Representation of a deCONZ sensor."""
TYPE = DOMAIN
_device: SensorResources
entity_description: DeconzSensorDescription
def __init__(

View File

@ -41,7 +41,7 @@ async def async_setup_entry(
)
class DeconzSiren(DeconzDevice, SirenEntity):
class DeconzSiren(DeconzDevice[Siren], SirenEntity):
"""Representation of a deCONZ siren."""
TYPE = DOMAIN
@ -50,7 +50,6 @@ class DeconzSiren(DeconzDevice, SirenEntity):
| SirenEntityFeature.TURN_OFF
| SirenEntityFeature.DURATION
)
_device: Siren
@property
def is_on(self) -> bool:

View File

@ -43,11 +43,10 @@ async def async_setup_entry(
)
class DeconzPowerPlug(DeconzDevice, SwitchEntity):
class DeconzPowerPlug(DeconzDevice[Light], SwitchEntity):
"""Representation of a deCONZ power plug."""
TYPE = DOMAIN
_device: Light
@property
def is_on(self) -> bool: