/
light.py
205 lines (178 loc) · 7.16 KB
/
light.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
"""Support for KNX/IP lights."""
from xknx.devices import Light as XknxLight
from homeassistant.components.light import (
ATTR_BRIGHTNESS,
ATTR_COLOR_TEMP,
ATTR_HS_COLOR,
ATTR_WHITE_VALUE,
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR,
SUPPORT_COLOR_TEMP,
SUPPORT_WHITE_VALUE,
LightEntity,
)
import homeassistant.util.color as color_util
from .const import DOMAIN
from .knx_entity import KnxEntity
DEFAULT_COLOR = (0.0, 0.0)
DEFAULT_BRIGHTNESS = 255
DEFAULT_WHITE_VALUE = 255
async def async_setup_platform(hass, config, async_add_entities, discovery_info=None):
"""Set up lights for KNX platform."""
entities = []
for device in hass.data[DOMAIN].xknx.devices:
if isinstance(device, XknxLight):
entities.append(KNXLight(device))
async_add_entities(entities)
class KNXLight(KnxEntity, LightEntity):
"""Representation of a KNX light."""
def __init__(self, device: XknxLight):
"""Initialize of KNX light."""
super().__init__(device)
self._min_kelvin = device.min_kelvin
self._max_kelvin = device.max_kelvin
self._min_mireds = color_util.color_temperature_kelvin_to_mired(
self._max_kelvin
)
self._max_mireds = color_util.color_temperature_kelvin_to_mired(
self._min_kelvin
)
@property
def brightness(self):
"""Return the brightness of this light between 0..255."""
if self._device.supports_brightness:
return self._device.current_brightness
hsv_color = self._hsv_color
if self._device.supports_color and hsv_color:
return round(hsv_color[-1] / 100 * 255)
return None
@property
def hs_color(self):
"""Return the HS color value."""
rgb = None
if self._device.supports_rgbw or self._device.supports_color:
rgb, _ = self._device.current_color
return color_util.color_RGB_to_hs(*rgb) if rgb else None
@property
def _hsv_color(self):
"""Return the HSV color value."""
rgb = None
if self._device.supports_rgbw or self._device.supports_color:
rgb, _ = self._device.current_color
return color_util.color_RGB_to_hsv(*rgb) if rgb else None
@property
def white_value(self):
"""Return the white value."""
white = None
if self._device.supports_rgbw:
_, white = self._device.current_color
return white
@property
def color_temp(self):
"""Return the color temperature in mireds."""
if self._device.supports_color_temperature:
kelvin = self._device.current_color_temperature
# Avoid division by zero if actuator reported 0 Kelvin (e.g., uninitialized DALI-Gateway)
if kelvin is not None and kelvin > 0:
return color_util.color_temperature_kelvin_to_mired(kelvin)
if self._device.supports_tunable_white:
relative_ct = self._device.current_tunable_white
if relative_ct is not None:
# as KNX devices typically use Kelvin we use it as base for
# calculating ct from percent
return color_util.color_temperature_kelvin_to_mired(
self._min_kelvin
+ ((relative_ct / 255) * (self._max_kelvin - self._min_kelvin))
)
return None
@property
def min_mireds(self):
"""Return the coldest color temp this light supports in mireds."""
return self._min_mireds
@property
def max_mireds(self):
"""Return the warmest color temp this light supports in mireds."""
return self._max_mireds
@property
def effect_list(self):
"""Return the list of supported effects."""
return None
@property
def effect(self):
"""Return the current effect."""
return None
@property
def is_on(self):
"""Return true if light is on."""
return self._device.state
@property
def supported_features(self):
"""Flag supported features."""
flags = 0
if self._device.supports_brightness:
flags |= SUPPORT_BRIGHTNESS
if self._device.supports_color:
flags |= SUPPORT_COLOR | SUPPORT_BRIGHTNESS
if self._device.supports_rgbw:
flags |= SUPPORT_COLOR | SUPPORT_WHITE_VALUE
if (
self._device.supports_color_temperature
or self._device.supports_tunable_white
):
flags |= SUPPORT_COLOR_TEMP
return flags
async def async_turn_on(self, **kwargs):
"""Turn the light on."""
brightness = kwargs.get(ATTR_BRIGHTNESS, self.brightness)
hs_color = kwargs.get(ATTR_HS_COLOR, self.hs_color)
white_value = kwargs.get(ATTR_WHITE_VALUE, self.white_value)
mireds = kwargs.get(ATTR_COLOR_TEMP, self.color_temp)
update_brightness = ATTR_BRIGHTNESS in kwargs
update_color = ATTR_HS_COLOR in kwargs
update_white_value = ATTR_WHITE_VALUE in kwargs
update_color_temp = ATTR_COLOR_TEMP in kwargs
# avoid conflicting changes and weird effects
if not (
self.is_on
or update_brightness
or update_color
or update_white_value
or update_color_temp
):
await self._device.set_on()
if self._device.supports_brightness and (
update_brightness and not update_color
):
# if we don't need to update the color, try updating brightness
# directly if supported; don't do it if color also has to be
# changed, as RGB color implicitly sets the brightness as well
await self._device.set_brightness(brightness)
elif (self._device.supports_rgbw or self._device.supports_color) and (
update_brightness or update_color or update_white_value
):
# change RGB color, white value (if supported), and brightness
# if brightness or hs_color was not yet set use the default value
# to calculate RGB from as a fallback
if brightness is None:
brightness = DEFAULT_BRIGHTNESS
if hs_color is None:
hs_color = DEFAULT_COLOR
if white_value is None and self._device.supports_rgbw:
white_value = DEFAULT_WHITE_VALUE
rgb = color_util.color_hsv_to_RGB(*hs_color, brightness * 100 / 255)
await self._device.set_color(rgb, white_value)
if update_color_temp:
kelvin = int(color_util.color_temperature_mired_to_kelvin(mireds))
kelvin = min(self._max_kelvin, max(self._min_kelvin, kelvin))
if self._device.supports_color_temperature:
await self._device.set_color_temperature(kelvin)
elif self._device.supports_tunable_white:
relative_ct = int(
255
* (kelvin - self._min_kelvin)
/ (self._max_kelvin - self._min_kelvin)
)
await self._device.set_tunable_white(relative_ct)
async def async_turn_off(self, **kwargs):
"""Turn the light off."""
await self._device.set_off()