core/homeassistant/components/matter/light.py

174 lines
5.9 KiB
Python

"""Matter light."""
from __future__ import annotations
from dataclasses import dataclass
from functools import partial
from typing import Any
from chip.clusters import Objects as clusters
from matter_server.common.models import device_types
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ColorMode,
LightEntity,
LightEntityDescription,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .entity import MatterEntity, MatterEntityDescriptionBaseClass
from .helpers import get_matter
from .util import renormalize
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Matter Light from Config Entry."""
matter = get_matter(hass)
matter.register_platform_handler(Platform.LIGHT, async_add_entities)
class MatterLight(MatterEntity, LightEntity):
"""Representation of a Matter light."""
entity_description: MatterLightEntityDescription
def _supports_brightness(self) -> bool:
"""Return if device supports brightness."""
return (
clusters.LevelControl.Attributes.CurrentLevel
in self.entity_description.subscribe_attributes
)
async def async_turn_on(self, **kwargs: Any) -> None:
"""Turn light on."""
if ATTR_BRIGHTNESS not in kwargs or not self._supports_brightness():
await self.matter_client.send_device_command(
node_id=self._device_type_instance.node.node_id,
endpoint=self._device_type_instance.endpoint,
command=clusters.OnOff.Commands.On(),
)
return
level_control = self._device_type_instance.get_cluster(clusters.LevelControl)
# We check above that the device supports brightness, ie level control.
assert level_control is not None
level = round(
renormalize(
kwargs[ATTR_BRIGHTNESS],
(0, 255),
(level_control.minLevel, level_control.maxLevel),
)
)
await self.matter_client.send_device_command(
node_id=self._device_type_instance.node.node_id,
endpoint=self._device_type_instance.endpoint,
command=clusters.LevelControl.Commands.MoveToLevelWithOnOff(
level=level,
# It's required in TLV. We don't implement transition time yet.
transitionTime=0,
),
)
async def async_turn_off(self, **kwargs: Any) -> None:
"""Turn light off."""
await self.matter_client.send_device_command(
node_id=self._device_type_instance.node.node_id,
endpoint=self._device_type_instance.endpoint,
command=clusters.OnOff.Commands.Off(),
)
@callback
def _update_from_device(self) -> None:
"""Update from device."""
supports_brigthness = self._supports_brightness()
if self._attr_supported_color_modes is None and supports_brigthness:
self._attr_supported_color_modes = {ColorMode.BRIGHTNESS}
if attr := self.get_matter_attribute(clusters.OnOff.Attributes.OnOff):
self._attr_is_on = attr.value
if supports_brigthness:
level_control = self._device_type_instance.get_cluster(
clusters.LevelControl
)
# We check above that the device supports brightness, ie level control.
assert level_control is not None
# Convert brightness to Home Assistant = 0..255
self._attr_brightness = round(
renormalize(
level_control.currentLevel,
(level_control.minLevel, level_control.maxLevel),
(0, 255),
)
)
@dataclass
class MatterLightEntityDescription(
LightEntityDescription,
MatterEntityDescriptionBaseClass,
):
"""Matter light entity description."""
# You can't set default values on inherited data classes
MatterLightEntityDescriptionFactory = partial(
MatterLightEntityDescription, entity_cls=MatterLight
)
# Mapping of a Matter Device type to Light Entity Description.
# A Matter device type (instance) can consist of multiple attributes.
# For example a Color Light which has an attribute to control brightness
# but also for color.
DEVICE_ENTITY: dict[
type[device_types.DeviceType],
MatterEntityDescriptionBaseClass | list[MatterEntityDescriptionBaseClass],
] = {
device_types.OnOffLight: MatterLightEntityDescriptionFactory(
key=device_types.OnOffLight,
subscribe_attributes=(clusters.OnOff.Attributes.OnOff,),
),
device_types.DimmableLight: MatterLightEntityDescriptionFactory(
key=device_types.DimmableLight,
subscribe_attributes=(
clusters.OnOff.Attributes.OnOff,
clusters.LevelControl.Attributes.CurrentLevel,
),
),
device_types.DimmablePlugInUnit: MatterLightEntityDescriptionFactory(
key=device_types.DimmablePlugInUnit,
subscribe_attributes=(
clusters.OnOff.Attributes.OnOff,
clusters.LevelControl.Attributes.CurrentLevel,
),
),
device_types.ColorTemperatureLight: MatterLightEntityDescriptionFactory(
key=device_types.ColorTemperatureLight,
subscribe_attributes=(
clusters.OnOff.Attributes.OnOff,
clusters.LevelControl.Attributes.CurrentLevel,
clusters.ColorControl,
),
),
device_types.ExtendedColorLight: MatterLightEntityDescriptionFactory(
key=device_types.ExtendedColorLight,
subscribe_attributes=(
clusters.OnOff.Attributes.OnOff,
clusters.LevelControl.Attributes.CurrentLevel,
clusters.ColorControl,
),
),
}