core/homeassistant/components/hue/scene.py

130 lines
4.4 KiB
Python

"""Support for scene platform for Hue scenes (V2 only)."""
from __future__ import annotations
from typing import Any
from aiohue.v2 import HueBridgeV2
from aiohue.v2.controllers.events import EventType
from aiohue.v2.controllers.scenes import ScenesController
from aiohue.v2.models.scene import Scene as HueScene
from homeassistant.components.scene import Scene as SceneEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .bridge import HueBridge
from .const import DOMAIN
from .v2.entity import HueBaseEntity
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up scene platform from Hue group scenes."""
bridge: HueBridge = hass.data[DOMAIN][config_entry.entry_id]
api: HueBridgeV2 = bridge.api
if bridge.api_version == 1:
# should not happen, but just in case
raise NotImplementedError("Scene support is only available for V2 bridges")
# add entities for all scenes
@callback
def async_add_entity(event_type: EventType, resource: HueScene) -> None:
"""Add entity from Hue resource."""
async_add_entities([HueSceneEntity(bridge, api.scenes, resource)])
# add all current items in controller
for item in api.scenes:
async_add_entity(EventType.RESOURCE_ADDED, item)
# register listener for new items only
config_entry.async_on_unload(
api.scenes.subscribe(async_add_entity, event_filter=EventType.RESOURCE_ADDED)
)
class HueSceneEntity(HueBaseEntity, SceneEntity):
"""Representation of a Scene entity from Hue Scenes."""
def __init__(
self,
bridge: HueBridge,
controller: ScenesController,
resource: HueScene,
) -> None:
"""Initialize the entity."""
super().__init__(bridge, controller, resource)
self.resource = resource
self.controller = controller
self.group = self.controller.get_group(self.resource.id)
async def async_added_to_hass(self) -> None:
"""Call when entity is added."""
await super().async_added_to_hass()
# Add value_changed callback for group to catch name changes.
self.async_on_remove(
self.bridge.api.groups.subscribe(
self._handle_event,
self.group.id,
(EventType.RESOURCE_UPDATED),
)
)
@property
def name(self) -> str:
"""Return default entity name."""
group = self.controller.get_group(self.resource.id)
return f"{group.metadata.name} - {self.resource.metadata.name}"
@property
def is_dynamic(self) -> bool:
"""Return if this scene has a dynamic color palette."""
if self.resource.palette.color and len(self.resource.palette.color) > 1:
return True
if (
self.resource.palette.color_temperature
and len(self.resource.palette.color_temperature) > 1
):
return True
return False
async def async_activate(self, **kwargs: Any) -> None:
"""Activate Hue scene."""
transition = kwargs.get("transition")
if transition is not None:
# hue transition duration is in milliseconds
transition = int(transition * 1000)
dynamic = kwargs.get("dynamic", self.is_dynamic)
await self.bridge.async_request_call(
self.controller.recall,
self.resource.id,
dynamic=dynamic,
duration=transition,
)
@property
def extra_state_attributes(self) -> dict[str, Any] | None:
"""Return the optional state attributes."""
brightness = None
if palette := self.resource.palette:
if palette.dimming:
brightness = palette.dimming[0].brightness
if brightness is None:
# get brightness from actions
for action in self.resource.actions:
if action.action.dimming:
brightness = action.action.dimming.brightness
break
return {
"group_name": self.group.metadata.name,
"group_type": self.group.type.value,
"name": self.resource.metadata.name,
"speed": self.resource.speed,
"brightness": brightness,
"is_dynamic": self.is_dynamic,
}