Home Assistant Unofficial Reference 2024.12.1
light.py
Go to the documentation of this file.
1 """Support for IKEA Tradfri lights."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from typing import Any, cast
7 
8 from pytradfri.command import Command
9 
11  ATTR_BRIGHTNESS,
12  ATTR_COLOR_TEMP,
13  ATTR_HS_COLOR,
14  ATTR_TRANSITION,
15  ColorMode,
16  LightEntity,
17  LightEntityFeature,
18  filter_supported_color_modes,
19 )
20 from homeassistant.config_entries import ConfigEntry
21 from homeassistant.core import HomeAssistant
22 from homeassistant.helpers.entity_platform import AddEntitiesCallback
23 import homeassistant.util.color as color_util
24 
25 from .const import CONF_GATEWAY_ID, COORDINATOR, COORDINATOR_LIST, DOMAIN, KEY_API
26 from .coordinator import TradfriDeviceDataUpdateCoordinator
27 from .entity import TradfriBaseEntity
28 
29 
31  hass: HomeAssistant,
32  config_entry: ConfigEntry,
33  async_add_entities: AddEntitiesCallback,
34 ) -> None:
35  """Load Tradfri lights based on a config entry."""
36  gateway_id = config_entry.data[CONF_GATEWAY_ID]
37  coordinator_data = hass.data[DOMAIN][config_entry.entry_id][COORDINATOR]
38  api = coordinator_data[KEY_API]
39 
42  device_coordinator,
43  api,
44  gateway_id,
45  )
46  for device_coordinator in coordinator_data[COORDINATOR_LIST]
47  if device_coordinator.device.has_light_control
48  )
49 
50 
52  """The platform class required by Home Assistant."""
53 
54  _attr_name = None
55  _attr_supported_features = LightEntityFeature.TRANSITION
56  _fixed_color_mode: ColorMode | None = None
57 
58  def __init__(
59  self,
60  device_coordinator: TradfriDeviceDataUpdateCoordinator,
61  api: Callable[[Command | list[Command]], Any],
62  gateway_id: str,
63  ) -> None:
64  """Initialize a Light."""
65  super().__init__(
66  device_coordinator=device_coordinator,
67  api=api,
68  gateway_id=gateway_id,
69  )
70 
71  self._device_control_device_control = self._device.light_control
72  self._device_data_device_data = self._device_control_device_control.lights[0]
73 
74  self._attr_unique_id_attr_unique_id_attr_unique_id = f"light-{gateway_id}-{self._device_id}"
75  self._hs_color_hs_color = None
76 
77  # Calculate supported color modes
78  modes: set[ColorMode] = {ColorMode.ONOFF}
79  if self._device.light_control.can_set_color:
80  modes.add(ColorMode.HS)
81  if self._device.light_control.can_set_temp:
82  modes.add(ColorMode.COLOR_TEMP)
83  if self._device.light_control.can_set_dimmer:
84  modes.add(ColorMode.BRIGHTNESS)
85  self._attr_supported_color_modes_attr_supported_color_modes = filter_supported_color_modes(modes)
86  if len(self._attr_supported_color_modes_attr_supported_color_modes) == 1:
87  self._fixed_color_mode_fixed_color_mode = next(iter(self._attr_supported_color_modes_attr_supported_color_modes))
88 
89  if self._device_control_device_control:
90  self._attr_min_mireds_attr_min_mireds = self._device_control_device_control.min_mireds
91  self._attr_max_mireds_attr_max_mireds = self._device_control_device_control.max_mireds
92 
93  def _refresh(self) -> None:
94  """Refresh the device."""
95  self._device_data_device_data = self.coordinator.data.light_control.lights[0]
96 
97  @property
98  def is_on(self) -> bool:
99  """Return true if light is on."""
100  if not self._device_data_device_data:
101  return False
102  return cast(bool, self._device_data_device_data.state)
103 
104  @property
105  def color_mode(self) -> ColorMode | None:
106  """Return the color mode of the light."""
107  if self._fixed_color_mode_fixed_color_mode:
108  return self._fixed_color_mode_fixed_color_mode
109  if self.hs_colorhs_colorhs_color:
110  return ColorMode.HS
111  return ColorMode.COLOR_TEMP
112 
113  @property
114  def brightness(self) -> int | None:
115  """Return the brightness of the light."""
116  if not self._device_data_device_data:
117  return None
118  return cast(int, self._device_data_device_data.dimmer)
119 
120  @property
121  def color_temp(self) -> int | None:
122  """Return the color temp value in mireds."""
123  if not self._device_data_device_data:
124  return None
125  return cast(int, self._device_data_device_data.color_temp)
126 
127  @property
128  def hs_color(self) -> tuple[float, float] | None:
129  """HS color of the light."""
130  if not self._device_control_device_control or not self._device_data_device_data:
131  return None
132  if self._device_control_device_control.can_set_color:
133  hsbxy = self._device_data_device_data.hsb_xy_color
134  hue = hsbxy[0] / (self._device_control_device_control.max_hue / 360)
135  sat = hsbxy[1] / (self._device_control_device_control.max_saturation / 100)
136  if hue is not None and sat is not None:
137  return hue, sat
138  return None
139 
140  async def async_turn_off(self, **kwargs: Any) -> None:
141  """Instruct the light to turn off."""
142  # This allows transitioning to off, but resets the brightness
143  # to 1 for the next set_state(True) command
144  if not self._device_control_device_control:
145  return
146  transition_time = None
147  if ATTR_TRANSITION in kwargs:
148  transition_time = int(kwargs[ATTR_TRANSITION]) * 10
149 
150  await self._api_api(
151  self._device_control_device_control.set_dimmer(
152  dimmer=0, transition_time=transition_time
153  )
154  )
155  else:
156  await self._api_api(self._device_control_device_control.set_state(False))
157 
158  async def async_turn_on(self, **kwargs: Any) -> None:
159  """Instruct the light to turn on."""
160  if not self._device_control_device_control:
161  return
162  transition_time = None
163  if ATTR_TRANSITION in kwargs:
164  transition_time = int(kwargs[ATTR_TRANSITION]) * 10
165 
166  dimmer_command = None
167  if ATTR_BRIGHTNESS in kwargs:
168  brightness = kwargs[ATTR_BRIGHTNESS]
169  brightness = min(brightness, 254)
170  dimmer_data = {
171  "dimmer": brightness,
172  "transition_time": transition_time,
173  }
174  dimmer_command = self._device_control_device_control.set_dimmer(**dimmer_data)
175  transition_time = None
176  else:
177  dimmer_command = self._device_control_device_control.set_state(True)
178 
179  color_command = None
180  if ATTR_HS_COLOR in kwargs and self._device_control_device_control.can_set_color:
181  hue = int(kwargs[ATTR_HS_COLOR][0] * (self._device_control_device_control.max_hue / 360))
182  sat = int(
183  kwargs[ATTR_HS_COLOR][1] * (self._device_control_device_control.max_saturation / 100)
184  )
185  color_data = {
186  "hue": hue,
187  "saturation": sat,
188  "transition_time": transition_time,
189  }
190  color_command = self._device_control_device_control.set_hsb(**color_data)
191  transition_time = None
192 
193  temp_command = None
194  if ATTR_COLOR_TEMP in kwargs and (
195  self._device_control_device_control.can_set_temp or self._device_control_device_control.can_set_color
196  ):
197  temp = kwargs[ATTR_COLOR_TEMP]
198  # White Spectrum bulb
199  if self._device_control_device_control.can_set_temp:
200  if temp > self.max_miredsmax_mireds:
201  temp = self.max_miredsmax_mireds
202  elif temp < self.min_miredsmin_mireds:
203  temp = self.min_miredsmin_mireds
204  temp_data = {
205  ATTR_COLOR_TEMP: temp,
206  "transition_time": transition_time,
207  }
208  temp_command = self._device_control_device_control.set_color_temp(**temp_data)
209  transition_time = None
210  # Color bulb (CWS)
211  # color_temp needs to be set with hue/saturation
212  elif self._device_control_device_control.can_set_color:
213  temp_k = color_util.color_temperature_mired_to_kelvin(temp)
214  hs_color = color_util.color_temperature_to_hs(temp_k)
215  hue = int(hs_color[0] * (self._device_control_device_control.max_hue / 360))
216  sat = int(hs_color[1] * (self._device_control_device_control.max_saturation / 100))
217  color_data = {
218  "hue": hue,
219  "saturation": sat,
220  "transition_time": transition_time,
221  }
222  color_command = self._device_control_device_control.set_hsb(**color_data)
223  transition_time = None
224 
225  # HSB can always be set, but color temp + brightness is bulb dependent
226  if (command := dimmer_command) is not None:
227  command += color_command
228  else:
229  command = color_command
230 
231  if self._device_control_device_control.can_combine_commands:
232  await self._api_api(command + temp_command)
233  else:
234  if temp_command is not None:
235  await self._api_api(temp_command)
236  if command is not None:
237  await self._api_api(command)
tuple[float, float]|None hs_color(self)
Definition: __init__.py:947
None __init__(self, TradfriDeviceDataUpdateCoordinator device_coordinator, Callable[[Command|list[Command]], Any] api, str gateway_id)
Definition: light.py:63
tuple[float, float]|None hs_color(self)
Definition: light.py:128
set[ColorMode] filter_supported_color_modes(Iterable[ColorMode] color_modes)
Definition: __init__.py:122
None async_setup_entry(HomeAssistant hass, ConfigEntry config_entry, AddEntitiesCallback async_add_entities)
Definition: light.py:34