Home Assistant Unofficial Reference 2024.12.1
light.py
Go to the documentation of this file.
1 """Support for HomematicIP Cloud lights."""
2 
3 from __future__ import annotations
4 
5 from typing import Any
6 
7 from homematicip.aio.device import (
8  AsyncBrandDimmer,
9  AsyncBrandSwitchMeasuring,
10  AsyncBrandSwitchNotificationLight,
11  AsyncDimmer,
12  AsyncDinRailDimmer3,
13  AsyncFullFlushDimmer,
14  AsyncPluggableDimmer,
15  AsyncWiredDimmer3,
16 )
17 from homematicip.base.enums import OpticalSignalBehaviour, RGBColorState
18 from homematicip.base.functionalChannels import NotificationLightChannel
19 from packaging.version import Version
20 
22  ATTR_BRIGHTNESS,
23  ATTR_COLOR_NAME,
24  ATTR_EFFECT,
25  ATTR_HS_COLOR,
26  ATTR_TRANSITION,
27  ColorMode,
28  LightEntity,
29  LightEntityFeature,
30 )
31 from homeassistant.config_entries import ConfigEntry
32 from homeassistant.core import HomeAssistant
33 from homeassistant.helpers.entity_platform import AddEntitiesCallback
34 
35 from .const import DOMAIN
36 from .entity import HomematicipGenericEntity
37 from .hap import HomematicipHAP
38 
39 
41  hass: HomeAssistant,
42  config_entry: ConfigEntry,
43  async_add_entities: AddEntitiesCallback,
44 ) -> None:
45  """Set up the HomematicIP Cloud lights from a config entry."""
46  hap = hass.data[DOMAIN][config_entry.unique_id]
47  entities: list[HomematicipGenericEntity] = []
48  for device in hap.home.devices:
49  if isinstance(device, AsyncBrandSwitchMeasuring):
50  entities.append(HomematicipLightMeasuring(hap, device))
51  elif isinstance(device, AsyncBrandSwitchNotificationLight):
52  device_version = Version(device.firmwareVersion)
53  entities.append(HomematicipLight(hap, device))
54 
55  entity_class = (
56  HomematicipNotificationLightV2
57  if device_version > Version("2.0.0")
58  else HomematicipNotificationLight
59  )
60 
61  entities.append(
62  entity_class(hap, device, device.topLightChannelIndex, "Top")
63  )
64  entities.append(
65  entity_class(hap, device, device.bottomLightChannelIndex, "Bottom")
66  )
67 
68  elif isinstance(device, (AsyncWiredDimmer3, AsyncDinRailDimmer3)):
69  entities.extend(
70  HomematicipMultiDimmer(hap, device, channel=channel)
71  for channel in range(1, 4)
72  )
73  elif isinstance(
74  device,
75  (AsyncDimmer, AsyncPluggableDimmer, AsyncBrandDimmer, AsyncFullFlushDimmer),
76  ):
77  entities.append(HomematicipDimmer(hap, device))
78 
79  async_add_entities(entities)
80 
81 
83  """Representation of the HomematicIP light."""
84 
85  _attr_color_mode = ColorMode.ONOFF
86  _attr_supported_color_modes = {ColorMode.ONOFF}
87 
88  def __init__(self, hap: HomematicipHAP, device) -> None:
89  """Initialize the light entity."""
90  super().__init__(hap, device)
91 
92  @property
93  def is_on(self) -> bool:
94  """Return true if light is on."""
95  return self._device_device.on
96 
97  async def async_turn_on(self, **kwargs: Any) -> None:
98  """Turn the light on."""
99  await self._device_device.turn_on()
100 
101  async def async_turn_off(self, **kwargs: Any) -> None:
102  """Turn the light off."""
103  await self._device_device.turn_off()
104 
105 
107  """Representation of the HomematicIP measuring light."""
108 
109 
110 class HomematicipMultiDimmer(HomematicipGenericEntity, LightEntity):
111  """Representation of HomematicIP Cloud dimmer."""
112 
113  _attr_color_mode = ColorMode.BRIGHTNESS
114  _attr_supported_color_modes = {ColorMode.BRIGHTNESS}
115 
116  def __init__(
117  self,
118  hap: HomematicipHAP,
119  device,
120  channel=1,
121  is_multi_channel=True,
122  ) -> None:
123  """Initialize the dimmer light entity."""
124  super().__init__(
125  hap, device, channel=channel, is_multi_channel=is_multi_channel
126  )
127 
128  @property
129  def is_on(self) -> bool:
130  """Return true if dimmer is on."""
131  func_channel = self._device_device.functionalChannels[self._channel_channel]
132  return func_channel.dimLevel is not None and func_channel.dimLevel > 0.0
133 
134  @property
135  def brightness(self) -> int:
136  """Return the brightness of this light between 0..255."""
137  return int(
138  (self._device_device.functionalChannels[self._channel_channel].dimLevel or 0.0) * 255
139  )
140 
141  async def async_turn_on(self, **kwargs: Any) -> None:
142  """Turn the dimmer on."""
143  if ATTR_BRIGHTNESS in kwargs:
144  await self._device_device.set_dim_level(
145  kwargs[ATTR_BRIGHTNESS] / 255.0, self._channel_channel
146  )
147  else:
148  await self._device_device.set_dim_level(1, self._channel_channel)
149 
150  async def async_turn_off(self, **kwargs: Any) -> None:
151  """Turn the dimmer off."""
152  await self._device_device.set_dim_level(0, self._channel_channel)
153 
154 
156  """Representation of HomematicIP Cloud dimmer."""
157 
158  def __init__(self, hap: HomematicipHAP, device) -> None:
159  """Initialize the dimmer light entity."""
160  super().__init__(hap, device, is_multi_channel=False)
161 
162 
164  """Representation of HomematicIP Cloud notification light."""
165 
166  _attr_color_mode = ColorMode.HS
167  _attr_supported_color_modes = {ColorMode.HS}
168  _attr_supported_features = LightEntityFeature.TRANSITION
169 
170  def __init__(self, hap: HomematicipHAP, device, channel: int, post: str) -> None:
171  """Initialize the notification light entity."""
172  super().__init__(hap, device, post=post, channel=channel, is_multi_channel=True)
173 
174  self._color_switcher: dict[str, tuple[float, float]] = {
175  RGBColorState.WHITE: (0.0, 0.0),
176  RGBColorState.RED: (0.0, 100.0),
177  RGBColorState.YELLOW: (60.0, 100.0),
178  RGBColorState.GREEN: (120.0, 100.0),
179  RGBColorState.TURQUOISE: (180.0, 100.0),
180  RGBColorState.BLUE: (240.0, 100.0),
181  RGBColorState.PURPLE: (300.0, 100.0),
182  }
183 
184  @property
185  def _func_channel(self) -> NotificationLightChannel:
186  return self._device_device.functionalChannels[self._channel_channel]
187 
188  @property
189  def is_on(self) -> bool:
190  """Return true if light is on."""
191  return (
192  self._func_channel_func_channel.dimLevel is not None
193  and self._func_channel_func_channel.dimLevel > 0.0
194  )
195 
196  @property
197  def brightness(self) -> int:
198  """Return the brightness of this light between 0..255."""
199  return int((self._func_channel_func_channel.dimLevel or 0.0) * 255)
200 
201  @property
202  def hs_color(self) -> tuple[float, float]:
203  """Return the hue and saturation color value [float, float]."""
204  simple_rgb_color = self._func_channel_func_channel.simpleRGBColorState
205  return self._color_switcher.get(simple_rgb_color, (0.0, 0.0))
206 
207  @property
208  def extra_state_attributes(self) -> dict[str, Any]:
209  """Return the state attributes of the notification light sensor."""
210  state_attr = super().extra_state_attributes
211 
212  if self.is_onis_onis_on:
213  state_attr[ATTR_COLOR_NAME] = self._func_channel_func_channel.simpleRGBColorState
214 
215  return state_attr
216 
217  @property
218  def unique_id(self) -> str:
219  """Return a unique ID."""
220  return f"{self.__class__.__name__}_{self._post}_{self._device.id}"
221 
222  async def async_turn_on(self, **kwargs: Any) -> None:
223  """Turn the light on."""
224  # Use hs_color from kwargs,
225  # if not applicable use current hs_color.
226  hs_color = kwargs.get(ATTR_HS_COLOR, self.hs_colorhs_colorhs_color)
227  simple_rgb_color = _convert_color(hs_color)
228 
229  # Use brightness from kwargs,
230  # if not applicable use current brightness.
231  brightness = kwargs.get(ATTR_BRIGHTNESS, self.brightnessbrightnessbrightness)
232 
233  # If no kwargs, use default value.
234  if not kwargs:
235  brightness = 255
236 
237  # Minimum brightness is 10, otherwise the led is disabled
238  brightness = max(10, brightness)
239  dim_level = brightness / 255.0
240  transition = kwargs.get(ATTR_TRANSITION, 0.5)
241 
242  await self._device_device.set_rgb_dim_level_with_time(
243  channelIndex=self._channel_channel,
244  rgb=simple_rgb_color,
245  dimLevel=dim_level,
246  onTime=0,
247  rampTime=transition,
248  )
249 
250  async def async_turn_off(self, **kwargs: Any) -> None:
251  """Turn the light off."""
252  simple_rgb_color = self._func_channel_func_channel.simpleRGBColorState
253  transition = kwargs.get(ATTR_TRANSITION, 0.5)
254 
255  await self._device_device.set_rgb_dim_level_with_time(
256  channelIndex=self._channel_channel,
257  rgb=simple_rgb_color,
258  dimLevel=0.0,
259  onTime=0,
260  rampTime=transition,
261  )
262 
263 
265  """Representation of HomematicIP Cloud notification light."""
266 
267  _effect_list = [
268  OpticalSignalBehaviour.BILLOW_MIDDLE,
269  OpticalSignalBehaviour.BLINKING_MIDDLE,
270  OpticalSignalBehaviour.FLASH_MIDDLE,
271  OpticalSignalBehaviour.OFF,
272  OpticalSignalBehaviour.ON,
273  ]
274 
275  def __init__(self, hap: HomematicipHAP, device, channel: int, post: str) -> None:
276  """Initialize the notification light entity."""
277  super().__init__(hap, device, post=post, channel=channel)
278  self._attr_supported_features_attr_supported_features |= LightEntityFeature.EFFECT
279 
280  @property
281  def effect_list(self) -> list[str] | None:
282  """Return the list of supported effects."""
283  return self._effect_list_effect_list
284 
285  @property
286  def effect(self) -> str | None:
287  """Return the current effect."""
288  return self._func_channel_func_channel.opticalSignalBehaviour
289 
290  @property
291  def is_on(self) -> bool:
292  """Return true if light is on."""
293  return self._func_channel_func_channel.on
294 
295  async def async_turn_on(self, **kwargs: Any) -> None:
296  """Turn the light on."""
297  # Use hs_color from kwargs,
298  # if not applicable use current hs_color.
299  hs_color = kwargs.get(ATTR_HS_COLOR, self.hs_colorhs_colorhs_color)
300  simple_rgb_color = _convert_color(hs_color)
301 
302  # If no kwargs, use default value.
303  brightness = 255
304  if ATTR_BRIGHTNESS in kwargs:
305  brightness = kwargs[ATTR_BRIGHTNESS]
306 
307  # Minimum brightness is 10, otherwise the led is disabled
308  brightness = max(10, brightness)
309  dim_level = round(brightness / 255.0, 2)
310 
311  effect = self.effecteffecteffect
312  if ATTR_EFFECT in kwargs:
313  effect = kwargs[ATTR_EFFECT]
314 
315  await self._func_channel_func_channel.async_set_optical_signal(
316  opticalSignalBehaviour=effect, rgb=simple_rgb_color, dimLevel=dim_level
317  )
318 
319  async def async_turn_off(self, **kwargs: Any) -> None:
320  """Turn the light off."""
321  await self._func_channel_func_channel.async_turn_off()
322 
323 
324 def _convert_color(color: tuple) -> RGBColorState:
325  """Convert the given color to the reduced RGBColorState color.
326 
327  RGBColorStat contains only 8 colors including white and black,
328  so a conversion is required.
329  """
330  if color is None:
331  return RGBColorState.WHITE
332 
333  hue = int(color[0])
334  saturation = int(color[1])
335  if saturation < 5:
336  return RGBColorState.WHITE
337  if 30 < hue <= 90:
338  return RGBColorState.YELLOW
339  if 90 < hue <= 160:
340  return RGBColorState.GREEN
341  if 150 < hue <= 210:
342  return RGBColorState.TURQUOISE
343  if 210 < hue <= 270:
344  return RGBColorState.BLUE
345  if 270 < hue <= 330:
346  return RGBColorState.PURPLE
347  return RGBColorState.RED
None __init__(self, HomematicipHAP hap, device)
Definition: light.py:158
None __init__(self, HomematicipHAP hap, device)
Definition: light.py:88
None __init__(self, HomematicipHAP hap, device, channel=1, is_multi_channel=True)
Definition: light.py:122
None __init__(self, HomematicipHAP hap, device, int channel, str post)
Definition: light.py:275
None __init__(self, HomematicipHAP hap, device, int channel, str post)
Definition: light.py:170
tuple[float, float]|None hs_color(self)
Definition: __init__.py:947
None turn_off(self, **Any kwargs)
Definition: entity.py:1705
None turn_on(self, **Any kwargs)
Definition: entity.py:1697
web.Response get(self, web.Request request, str config_key)
Definition: view.py:88
RGBColorState _convert_color(tuple color)
Definition: light.py:324
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: light.py:44