mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Add typing to deCONZ Fan and Light platforms (#59607)
This commit is contained in:
parent
476a59d248
commit
41e341028e
@ -1,6 +1,9 @@
|
|||||||
"""Support for deCONZ fans."""
|
"""Support for deCONZ fans."""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import ValuesView
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from pydeconz.light import (
|
from pydeconz.light import (
|
||||||
FAN_SPEED_25_PERCENT,
|
FAN_SPEED_25_PERCENT,
|
||||||
FAN_SPEED_50_PERCENT,
|
FAN_SPEED_50_PERCENT,
|
||||||
@ -19,15 +22,17 @@ from homeassistant.components.fan import (
|
|||||||
SUPPORT_SET_SPEED,
|
SUPPORT_SET_SPEED,
|
||||||
FanEntity,
|
FanEntity,
|
||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.util.percentage import (
|
from homeassistant.util.percentage import (
|
||||||
ordered_list_item_to_percentage,
|
ordered_list_item_to_percentage,
|
||||||
percentage_to_ordered_list_item,
|
percentage_to_ordered_list_item,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .deconz_device import DeconzDevice
|
from .deconz_device import DeconzDevice
|
||||||
from .gateway import get_gateway_from_config_entry
|
from .gateway import DeconzGateway, get_gateway_from_config_entry
|
||||||
|
|
||||||
ORDERED_NAMED_FAN_SPEEDS = [
|
ORDERED_NAMED_FAN_SPEEDS = [
|
||||||
FAN_SPEED_25_PERCENT,
|
FAN_SPEED_25_PERCENT,
|
||||||
@ -50,13 +55,19 @@ LEGACY_DECONZ_TO_SPEED = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities) -> None:
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
"""Set up fans for deCONZ component."""
|
"""Set up fans for deCONZ component."""
|
||||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||||
gateway.entities[DOMAIN] = set()
|
gateway.entities[DOMAIN] = set()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_add_fan(lights=gateway.api.lights.values()) -> None:
|
def async_add_fan(
|
||||||
|
lights: list[Fan] | ValuesView[Fan] = gateway.api.lights.values(),
|
||||||
|
) -> None:
|
||||||
"""Add fan from deCONZ."""
|
"""Add fan from deCONZ."""
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
@ -86,8 +97,11 @@ class DeconzFan(DeconzDevice, FanEntity):
|
|||||||
"""Representation of a deCONZ fan."""
|
"""Representation of a deCONZ fan."""
|
||||||
|
|
||||||
TYPE = DOMAIN
|
TYPE = DOMAIN
|
||||||
|
_device: Fan
|
||||||
|
|
||||||
def __init__(self, device, gateway) -> None:
|
_attr_supported_features = SUPPORT_SET_SPEED
|
||||||
|
|
||||||
|
def __init__(self, device: Fan, gateway: DeconzGateway) -> None:
|
||||||
"""Set up fan."""
|
"""Set up fan."""
|
||||||
super().__init__(device, gateway)
|
super().__init__(device, gateway)
|
||||||
|
|
||||||
@ -95,12 +109,10 @@ class DeconzFan(DeconzDevice, FanEntity):
|
|||||||
if self._device.speed in ORDERED_NAMED_FAN_SPEEDS:
|
if self._device.speed in ORDERED_NAMED_FAN_SPEEDS:
|
||||||
self._default_on_speed = self._device.speed
|
self._default_on_speed = self._device.speed
|
||||||
|
|
||||||
self._attr_supported_features = SUPPORT_SET_SPEED
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool:
|
def is_on(self) -> bool:
|
||||||
"""Return true if fan is on."""
|
"""Return true if fan is on."""
|
||||||
return self._device.speed != FAN_SPEED_OFF
|
return self._device.speed != FAN_SPEED_OFF # type: ignore[no-any-return]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def percentage(self) -> int | None:
|
def percentage(self) -> int | None:
|
||||||
@ -153,11 +165,6 @@ class DeconzFan(DeconzDevice, FanEntity):
|
|||||||
SPEED_MEDIUM,
|
SPEED_MEDIUM,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
|
||||||
def supported_features(self) -> int:
|
|
||||||
"""Flag supported features."""
|
|
||||||
return self._attr_supported_features
|
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_update_callback(self) -> None:
|
def async_update_callback(self) -> None:
|
||||||
"""Store latest configured speed from the device."""
|
"""Store latest configured speed from the device."""
|
||||||
@ -183,10 +190,10 @@ class DeconzFan(DeconzDevice, FanEntity):
|
|||||||
|
|
||||||
async def async_turn_on(
|
async def async_turn_on(
|
||||||
self,
|
self,
|
||||||
speed: str = None,
|
speed: str | None = None,
|
||||||
percentage: int = None,
|
percentage: int | None = None,
|
||||||
preset_mode: str = None,
|
preset_mode: str | None = None,
|
||||||
**kwargs,
|
**kwargs: Any,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Turn on fan."""
|
"""Turn on fan."""
|
||||||
new_speed = self._default_on_speed
|
new_speed = self._default_on_speed
|
||||||
@ -198,6 +205,6 @@ class DeconzFan(DeconzDevice, FanEntity):
|
|||||||
|
|
||||||
await self._device.set_speed(new_speed)
|
await self._device.set_speed(new_speed)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs) -> None:
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn off fan."""
|
"""Turn off fan."""
|
||||||
await self._device.set_speed(FAN_SPEED_OFF)
|
await self._device.set_speed(FAN_SPEED_OFF)
|
||||||
|
@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import ValuesView
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from pydeconz.group import DeconzGroup as Group
|
from pydeconz.group import DeconzGroup as Group
|
||||||
from pydeconz.light import (
|
from pydeconz.light import (
|
||||||
ALERT_LONG,
|
ALERT_LONG,
|
||||||
@ -33,27 +36,35 @@ from homeassistant.components.light import (
|
|||||||
SUPPORT_TRANSITION,
|
SUPPORT_TRANSITION,
|
||||||
LightEntity,
|
LightEntity,
|
||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
from homeassistant.core import HomeAssistant, callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity import DeviceInfo
|
from homeassistant.helpers.entity import DeviceInfo
|
||||||
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||||
from homeassistant.util.color import color_hs_to_xy
|
from homeassistant.util.color import color_hs_to_xy
|
||||||
|
|
||||||
from .const import DOMAIN as DECONZ_DOMAIN, POWER_PLUGS
|
from .const import DOMAIN as DECONZ_DOMAIN, POWER_PLUGS
|
||||||
from .deconz_device import DeconzDevice
|
from .deconz_device import DeconzDevice
|
||||||
from .gateway import get_gateway_from_config_entry
|
from .gateway import DeconzGateway, get_gateway_from_config_entry
|
||||||
|
|
||||||
DECONZ_GROUP = "is_deconz_group"
|
DECONZ_GROUP = "is_deconz_group"
|
||||||
EFFECT_TO_DECONZ = {EFFECT_COLORLOOP: EFFECT_COLOR_LOOP, "None": EFFECT_NONE}
|
EFFECT_TO_DECONZ = {EFFECT_COLORLOOP: EFFECT_COLOR_LOOP, "None": EFFECT_NONE}
|
||||||
FLASH_TO_DECONZ = {FLASH_SHORT: ALERT_SHORT, FLASH_LONG: ALERT_LONG}
|
FLASH_TO_DECONZ = {FLASH_SHORT: ALERT_SHORT, FLASH_LONG: ALERT_LONG}
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(hass, config_entry, async_add_entities):
|
async def async_setup_entry(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
config_entry: ConfigEntry,
|
||||||
|
async_add_entities: AddEntitiesCallback,
|
||||||
|
) -> None:
|
||||||
"""Set up the deCONZ lights and groups from a config entry."""
|
"""Set up the deCONZ lights and groups from a config entry."""
|
||||||
gateway = get_gateway_from_config_entry(hass, config_entry)
|
gateway = get_gateway_from_config_entry(hass, config_entry)
|
||||||
gateway.entities[DOMAIN] = set()
|
gateway.entities[DOMAIN] = set()
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_add_light(lights=gateway.api.lights.values()):
|
def async_add_light(
|
||||||
|
lights: list[Light] | ValuesView[Light] = gateway.api.lights.values(),
|
||||||
|
) -> None:
|
||||||
"""Add light from deCONZ."""
|
"""Add light from deCONZ."""
|
||||||
entities = []
|
entities = []
|
||||||
|
|
||||||
@ -77,7 +88,9 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_add_group(groups=gateway.api.groups.values()):
|
def async_add_group(
|
||||||
|
groups: list[Group] | ValuesView[Group] = gateway.api.groups.values(),
|
||||||
|
) -> None:
|
||||||
"""Add group from deCONZ."""
|
"""Add group from deCONZ."""
|
||||||
if not gateway.option_allow_deconz_groups:
|
if not gateway.option_allow_deconz_groups:
|
||||||
return
|
return
|
||||||
@ -113,11 +126,11 @@ class DeconzBaseLight(DeconzDevice, LightEntity):
|
|||||||
|
|
||||||
TYPE = DOMAIN
|
TYPE = DOMAIN
|
||||||
|
|
||||||
def __init__(self, device, gateway):
|
def __init__(self, device: Group | Light, gateway: DeconzGateway) -> None:
|
||||||
"""Set up light."""
|
"""Set up light."""
|
||||||
super().__init__(device, gateway)
|
super().__init__(device, gateway)
|
||||||
|
|
||||||
self._attr_supported_color_modes = set()
|
self._attr_supported_color_modes: set[str] = set()
|
||||||
|
|
||||||
if device.color_temp is not None:
|
if device.color_temp is not None:
|
||||||
self._attr_supported_color_modes.add(COLOR_MODE_COLOR_TEMP)
|
self._attr_supported_color_modes.add(COLOR_MODE_COLOR_TEMP)
|
||||||
@ -158,83 +171,83 @@ class DeconzBaseLight(DeconzDevice, LightEntity):
|
|||||||
return color_mode
|
return color_mode
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def brightness(self):
|
def brightness(self) -> int | None:
|
||||||
"""Return the brightness of this light between 0..255."""
|
"""Return the brightness of this light between 0..255."""
|
||||||
return self._device.brightness
|
return self._device.brightness # type: ignore[no-any-return]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def color_temp(self):
|
def color_temp(self) -> int:
|
||||||
"""Return the CT color value."""
|
"""Return the CT color value."""
|
||||||
return self._device.color_temp
|
return self._device.color_temp # type: ignore[no-any-return]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def hs_color(self) -> tuple:
|
def hs_color(self) -> tuple[float, float]:
|
||||||
"""Return the hs color value."""
|
"""Return the hs color value."""
|
||||||
return (self._device.hue / 65535 * 360, self._device.saturation / 255 * 100)
|
return (self._device.hue / 65535 * 360, self._device.saturation / 255 * 100)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def xy_color(self) -> tuple | None:
|
def xy_color(self) -> tuple[float, float] | None:
|
||||||
"""Return the XY color value."""
|
"""Return the XY color value."""
|
||||||
return self._device.xy
|
return self._device.xy # type: ignore[no-any-return]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_on(self):
|
def is_on(self) -> bool:
|
||||||
"""Return true if light is on."""
|
"""Return true if light is on."""
|
||||||
return self._device.state
|
return self._device.state # type: ignore[no-any-return]
|
||||||
|
|
||||||
async def async_turn_on(self, **kwargs):
|
async def async_turn_on(self, **kwargs: Any) -> None:
|
||||||
"""Turn on light."""
|
"""Turn on light."""
|
||||||
data = {"on": True}
|
data: dict[str, bool | float | int | str | tuple[float, float]] = {"on": True}
|
||||||
|
|
||||||
if ATTR_BRIGHTNESS in kwargs:
|
if attr_brightness := kwargs.get(ATTR_BRIGHTNESS):
|
||||||
data["brightness"] = kwargs[ATTR_BRIGHTNESS]
|
data["brightness"] = attr_brightness
|
||||||
|
|
||||||
if ATTR_COLOR_TEMP in kwargs:
|
if attr_color_temp := kwargs.get(ATTR_COLOR_TEMP):
|
||||||
data["color_temperature"] = kwargs[ATTR_COLOR_TEMP]
|
data["color_temperature"] = attr_color_temp
|
||||||
|
|
||||||
if ATTR_HS_COLOR in kwargs:
|
if attr_hs_color := kwargs.get(ATTR_HS_COLOR):
|
||||||
if COLOR_MODE_XY in self._attr_supported_color_modes:
|
if COLOR_MODE_XY in self._attr_supported_color_modes:
|
||||||
data["xy"] = color_hs_to_xy(*kwargs[ATTR_HS_COLOR])
|
data["xy"] = color_hs_to_xy(*attr_hs_color)
|
||||||
else:
|
else:
|
||||||
data["hue"] = int(kwargs[ATTR_HS_COLOR][0] / 360 * 65535)
|
data["hue"] = int(attr_hs_color[0] / 360 * 65535)
|
||||||
data["saturation"] = int(kwargs[ATTR_HS_COLOR][1] / 100 * 255)
|
data["saturation"] = int(attr_hs_color[1] / 100 * 255)
|
||||||
|
|
||||||
if ATTR_XY_COLOR in kwargs:
|
if ATTR_XY_COLOR in kwargs:
|
||||||
data["xy"] = kwargs[ATTR_XY_COLOR]
|
data["xy"] = kwargs[ATTR_XY_COLOR]
|
||||||
|
|
||||||
if ATTR_TRANSITION in kwargs:
|
if attr_transition := kwargs.get(ATTR_TRANSITION):
|
||||||
data["transition_time"] = int(kwargs[ATTR_TRANSITION] * 10)
|
data["transition_time"] = int(attr_transition * 10)
|
||||||
elif "IKEA" in self._device.manufacturer:
|
elif "IKEA" in self._device.manufacturer:
|
||||||
data["transition_time"] = 0
|
data["transition_time"] = 0
|
||||||
|
|
||||||
if (alert := FLASH_TO_DECONZ.get(kwargs.get(ATTR_FLASH))) is not None:
|
if (alert := FLASH_TO_DECONZ.get(kwargs.get(ATTR_FLASH, ""))) is not None:
|
||||||
data["alert"] = alert
|
data["alert"] = alert
|
||||||
del data["on"]
|
del data["on"]
|
||||||
|
|
||||||
if (effect := EFFECT_TO_DECONZ.get(kwargs.get(ATTR_EFFECT))) is not None:
|
if (effect := EFFECT_TO_DECONZ.get(kwargs.get(ATTR_EFFECT, ""))) is not None:
|
||||||
data["effect"] = effect
|
data["effect"] = effect
|
||||||
|
|
||||||
await self._device.set_state(**data)
|
await self._device.set_state(**data)
|
||||||
|
|
||||||
async def async_turn_off(self, **kwargs):
|
async def async_turn_off(self, **kwargs: Any) -> None:
|
||||||
"""Turn off light."""
|
"""Turn off light."""
|
||||||
if not self._device.state:
|
if not self._device.state:
|
||||||
return
|
return
|
||||||
|
|
||||||
data = {"on": False}
|
data: dict[str, bool | int | str] = {"on": False}
|
||||||
|
|
||||||
if ATTR_TRANSITION in kwargs:
|
if ATTR_TRANSITION in kwargs:
|
||||||
data["brightness"] = 0
|
data["brightness"] = 0
|
||||||
data["transition_time"] = int(kwargs[ATTR_TRANSITION] * 10)
|
data["transition_time"] = int(kwargs[ATTR_TRANSITION] * 10)
|
||||||
|
|
||||||
if (alert := FLASH_TO_DECONZ.get(kwargs.get(ATTR_FLASH))) is not None:
|
if (alert := FLASH_TO_DECONZ.get(kwargs.get(ATTR_FLASH, ""))) is not None:
|
||||||
data["alert"] = alert
|
data["alert"] = alert
|
||||||
del data["on"]
|
del data["on"]
|
||||||
|
|
||||||
await self._device.set_state(**data)
|
await self._device.set_state(**data)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self):
|
def extra_state_attributes(self) -> dict[str, bool]:
|
||||||
"""Return the device state attributes."""
|
"""Return the device state attributes."""
|
||||||
return {DECONZ_GROUP: isinstance(self._device, Group)}
|
return {DECONZ_GROUP: isinstance(self._device, Group)}
|
||||||
|
|
||||||
@ -242,13 +255,15 @@ class DeconzBaseLight(DeconzDevice, LightEntity):
|
|||||||
class DeconzLight(DeconzBaseLight):
|
class DeconzLight(DeconzBaseLight):
|
||||||
"""Representation of a deCONZ light."""
|
"""Representation of a deCONZ light."""
|
||||||
|
|
||||||
|
_device: Light
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def max_mireds(self):
|
def max_mireds(self) -> int:
|
||||||
"""Return the warmest color_temp that this light supports."""
|
"""Return the warmest color_temp that this light supports."""
|
||||||
return self._device.max_color_temp or super().max_mireds
|
return self._device.max_color_temp or super().max_mireds
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def min_mireds(self):
|
def min_mireds(self) -> int:
|
||||||
"""Return the coldest color_temp that this light supports."""
|
"""Return the coldest color_temp that this light supports."""
|
||||||
return self._device.min_color_temp or super().min_mireds
|
return self._device.min_color_temp or super().min_mireds
|
||||||
|
|
||||||
@ -256,13 +271,15 @@ class DeconzLight(DeconzBaseLight):
|
|||||||
class DeconzGroup(DeconzBaseLight):
|
class DeconzGroup(DeconzBaseLight):
|
||||||
"""Representation of a deCONZ group."""
|
"""Representation of a deCONZ group."""
|
||||||
|
|
||||||
def __init__(self, device, gateway):
|
_device: Group
|
||||||
|
|
||||||
|
def __init__(self, device: Group, gateway: DeconzGateway) -> None:
|
||||||
"""Set up group and create an unique id."""
|
"""Set up group and create an unique id."""
|
||||||
self._unique_id = f"{gateway.bridgeid}-{device.deconz_id}"
|
self._unique_id = f"{gateway.bridgeid}-{device.deconz_id}"
|
||||||
super().__init__(device, gateway)
|
super().__init__(device, gateway)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self) -> str:
|
||||||
"""Return a unique identifier for this device."""
|
"""Return a unique identifier for this device."""
|
||||||
return self._unique_id
|
return self._unique_id
|
||||||
|
|
||||||
@ -278,7 +295,7 @@ class DeconzGroup(DeconzBaseLight):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def extra_state_attributes(self):
|
def extra_state_attributes(self) -> dict[str, bool]:
|
||||||
"""Return the device state attributes."""
|
"""Return the device state attributes."""
|
||||||
attributes = dict(super().extra_state_attributes)
|
attributes = dict(super().extra_state_attributes)
|
||||||
attributes["all_on"] = self._device.all_on
|
attributes["all_on"] = self._device.all_on
|
||||||
|
Loading…
x
Reference in New Issue
Block a user