aiohomematic 2025.11.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aiohomematic might be problematic. Click here for more details.
- aiohomematic/__init__.py +61 -0
- aiohomematic/async_support.py +212 -0
- aiohomematic/central/__init__.py +2309 -0
- aiohomematic/central/decorators.py +155 -0
- aiohomematic/central/rpc_server.py +295 -0
- aiohomematic/client/__init__.py +1848 -0
- aiohomematic/client/_rpc_errors.py +81 -0
- aiohomematic/client/json_rpc.py +1326 -0
- aiohomematic/client/rpc_proxy.py +311 -0
- aiohomematic/const.py +1127 -0
- aiohomematic/context.py +18 -0
- aiohomematic/converter.py +108 -0
- aiohomematic/decorators.py +302 -0
- aiohomematic/exceptions.py +164 -0
- aiohomematic/hmcli.py +186 -0
- aiohomematic/model/__init__.py +140 -0
- aiohomematic/model/calculated/__init__.py +84 -0
- aiohomematic/model/calculated/climate.py +290 -0
- aiohomematic/model/calculated/data_point.py +327 -0
- aiohomematic/model/calculated/operating_voltage_level.py +299 -0
- aiohomematic/model/calculated/support.py +234 -0
- aiohomematic/model/custom/__init__.py +177 -0
- aiohomematic/model/custom/climate.py +1532 -0
- aiohomematic/model/custom/cover.py +792 -0
- aiohomematic/model/custom/data_point.py +334 -0
- aiohomematic/model/custom/definition.py +871 -0
- aiohomematic/model/custom/light.py +1128 -0
- aiohomematic/model/custom/lock.py +394 -0
- aiohomematic/model/custom/siren.py +275 -0
- aiohomematic/model/custom/support.py +41 -0
- aiohomematic/model/custom/switch.py +175 -0
- aiohomematic/model/custom/valve.py +114 -0
- aiohomematic/model/data_point.py +1123 -0
- aiohomematic/model/device.py +1445 -0
- aiohomematic/model/event.py +208 -0
- aiohomematic/model/generic/__init__.py +217 -0
- aiohomematic/model/generic/action.py +34 -0
- aiohomematic/model/generic/binary_sensor.py +30 -0
- aiohomematic/model/generic/button.py +27 -0
- aiohomematic/model/generic/data_point.py +171 -0
- aiohomematic/model/generic/dummy.py +147 -0
- aiohomematic/model/generic/number.py +76 -0
- aiohomematic/model/generic/select.py +39 -0
- aiohomematic/model/generic/sensor.py +74 -0
- aiohomematic/model/generic/switch.py +54 -0
- aiohomematic/model/generic/text.py +29 -0
- aiohomematic/model/hub/__init__.py +333 -0
- aiohomematic/model/hub/binary_sensor.py +24 -0
- aiohomematic/model/hub/button.py +28 -0
- aiohomematic/model/hub/data_point.py +340 -0
- aiohomematic/model/hub/number.py +39 -0
- aiohomematic/model/hub/select.py +49 -0
- aiohomematic/model/hub/sensor.py +37 -0
- aiohomematic/model/hub/switch.py +44 -0
- aiohomematic/model/hub/text.py +30 -0
- aiohomematic/model/support.py +586 -0
- aiohomematic/model/update.py +143 -0
- aiohomematic/property_decorators.py +496 -0
- aiohomematic/py.typed +0 -0
- aiohomematic/rega_scripts/fetch_all_device_data.fn +92 -0
- aiohomematic/rega_scripts/get_program_descriptions.fn +30 -0
- aiohomematic/rega_scripts/get_serial.fn +44 -0
- aiohomematic/rega_scripts/get_system_variable_descriptions.fn +30 -0
- aiohomematic/rega_scripts/set_program_state.fn +12 -0
- aiohomematic/rega_scripts/set_system_variable.fn +15 -0
- aiohomematic/store/__init__.py +34 -0
- aiohomematic/store/dynamic.py +551 -0
- aiohomematic/store/persistent.py +988 -0
- aiohomematic/store/visibility.py +812 -0
- aiohomematic/support.py +664 -0
- aiohomematic/validator.py +112 -0
- aiohomematic-2025.11.3.dist-info/METADATA +144 -0
- aiohomematic-2025.11.3.dist-info/RECORD +77 -0
- aiohomematic-2025.11.3.dist-info/WHEEL +5 -0
- aiohomematic-2025.11.3.dist-info/entry_points.txt +2 -0
- aiohomematic-2025.11.3.dist-info/licenses/LICENSE +21 -0
- aiohomematic-2025.11.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1128 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
# Copyright (c) 2021-2025
|
|
3
|
+
"""Module for data points implemented using the light category."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
from enum import IntEnum, StrEnum
|
|
9
|
+
import math
|
|
10
|
+
from typing import Any, Final, TypedDict, Unpack
|
|
11
|
+
|
|
12
|
+
from aiohomematic.const import DataPointCategory, DataPointUsage, DeviceProfile, Field, Parameter
|
|
13
|
+
from aiohomematic.model import device as hmd
|
|
14
|
+
from aiohomematic.model.custom import definition as hmed
|
|
15
|
+
from aiohomematic.model.custom.data_point import CustomDataPoint
|
|
16
|
+
from aiohomematic.model.custom.support import CustomConfig, ExtendedConfig
|
|
17
|
+
from aiohomematic.model.data_point import CallParameterCollector, bind_collector
|
|
18
|
+
from aiohomematic.model.generic import DpAction, DpFloat, DpInteger, DpSelect, DpSensor, GenericDataPoint
|
|
19
|
+
from aiohomematic.property_decorators import state_property
|
|
20
|
+
|
|
21
|
+
_DIMMER_OFF: Final = 0.0
|
|
22
|
+
_EFFECT_OFF: Final = "Off"
|
|
23
|
+
_LEVEL_TO_BRIGHTNESS_MULTIPLIER: Final = 100
|
|
24
|
+
_MAX_BRIGHTNESS: Final = 255.0
|
|
25
|
+
_MAX_KELVIN: Final = 1000000
|
|
26
|
+
_MAX_MIREDS: Final = 500
|
|
27
|
+
_MAX_SATURATION: Final = 100.0
|
|
28
|
+
_MIN_BRIGHTNESS: Final = 0.0
|
|
29
|
+
_MIN_HUE: Final = 0.0
|
|
30
|
+
_MIN_MIREDS: Final = 153
|
|
31
|
+
_MIN_SATURATION: Final = 0.0
|
|
32
|
+
_NOT_USED: Final = 111600
|
|
33
|
+
_OLD_LEVEL: Final = 1.005
|
|
34
|
+
_SATURATION_MULTIPLIER: Final = 100
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _DeviceOperationMode(StrEnum):
|
|
38
|
+
"""Enum with device operation modes."""
|
|
39
|
+
|
|
40
|
+
PWM = "4_PWM"
|
|
41
|
+
RGB = "RGB"
|
|
42
|
+
RGBW = "RGBW"
|
|
43
|
+
TUNABLE_WHITE = "2_TUNABLE_WHITE"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class _ColorBehaviour(StrEnum):
|
|
47
|
+
"""Enum with color behaviours."""
|
|
48
|
+
|
|
49
|
+
DO_NOT_CARE = "DO_NOT_CARE"
|
|
50
|
+
OFF = "OFF"
|
|
51
|
+
OLD_VALUE = "OLD_VALUE"
|
|
52
|
+
ON = "ON"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class _FixedColor(StrEnum):
|
|
56
|
+
"""Enum with colors."""
|
|
57
|
+
|
|
58
|
+
BLACK = "BLACK"
|
|
59
|
+
BLUE = "BLUE"
|
|
60
|
+
DO_NOT_CARE = "DO_NOT_CARE"
|
|
61
|
+
GREEN = "GREEN"
|
|
62
|
+
OLD_VALUE = "OLD_VALUE"
|
|
63
|
+
PURPLE = "PURPLE"
|
|
64
|
+
RED = "RED"
|
|
65
|
+
TURQUOISE = "TURQUOISE"
|
|
66
|
+
WHITE = "WHITE"
|
|
67
|
+
YELLOW = "YELLOW"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class _StateChangeArg(StrEnum):
|
|
71
|
+
"""Enum with light state change arguments."""
|
|
72
|
+
|
|
73
|
+
BRIGHTNESS = "brightness"
|
|
74
|
+
COLOR_TEMP_KELVIN = "color_temp_kelvin"
|
|
75
|
+
EFFECT = "effect"
|
|
76
|
+
HS_COLOR = "hs_color"
|
|
77
|
+
OFF = "off"
|
|
78
|
+
ON = "on"
|
|
79
|
+
ON_TIME = "on_time"
|
|
80
|
+
RAMP_TIME = "ramp_time"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class _TimeUnit(IntEnum):
|
|
84
|
+
"""Enum with time units."""
|
|
85
|
+
|
|
86
|
+
SECONDS = 0
|
|
87
|
+
MINUTES = 1
|
|
88
|
+
HOURS = 2
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
_NO_COLOR: Final = (
|
|
92
|
+
_FixedColor.BLACK,
|
|
93
|
+
_FixedColor.DO_NOT_CARE,
|
|
94
|
+
_FixedColor.OLD_VALUE,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
_EXCLUDE_FROM_COLOR_BEHAVIOUR: Final = (
|
|
98
|
+
_ColorBehaviour.DO_NOT_CARE,
|
|
99
|
+
_ColorBehaviour.OFF,
|
|
100
|
+
_ColorBehaviour.OLD_VALUE,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
_OFF_COLOR_BEHAVIOUR: Final = (
|
|
104
|
+
_ColorBehaviour.DO_NOT_CARE,
|
|
105
|
+
_ColorBehaviour.OFF,
|
|
106
|
+
_ColorBehaviour.OLD_VALUE,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
_FIXED_COLOR_SWITCHER: Mapping[str, tuple[float, float]] = {
|
|
110
|
+
_FixedColor.WHITE: (_MIN_HUE, _MIN_SATURATION),
|
|
111
|
+
_FixedColor.RED: (_MIN_HUE, _MAX_SATURATION),
|
|
112
|
+
_FixedColor.YELLOW: (60.0, _MAX_SATURATION),
|
|
113
|
+
_FixedColor.GREEN: (120.0, _MAX_SATURATION),
|
|
114
|
+
_FixedColor.TURQUOISE: (180.0, _MAX_SATURATION),
|
|
115
|
+
_FixedColor.BLUE: (240.0, _MAX_SATURATION),
|
|
116
|
+
_FixedColor.PURPLE: (300.0, _MAX_SATURATION),
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class LightOnArgs(TypedDict, total=False):
|
|
121
|
+
"""Matcher for the light turn on arguments."""
|
|
122
|
+
|
|
123
|
+
brightness: int
|
|
124
|
+
color_temp_kelvin: int
|
|
125
|
+
effect: str
|
|
126
|
+
hs_color: tuple[float, float]
|
|
127
|
+
on_time: float
|
|
128
|
+
ramp_time: float
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class LightOffArgs(TypedDict, total=False):
|
|
132
|
+
"""Matcher for the light turn off arguments."""
|
|
133
|
+
|
|
134
|
+
on_time: float
|
|
135
|
+
ramp_time: float
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
class CustomDpDimmer(CustomDataPoint):
|
|
139
|
+
"""Base class for Homematic light data point."""
|
|
140
|
+
|
|
141
|
+
__slots__ = (
|
|
142
|
+
"_dp_group_level",
|
|
143
|
+
"_dp_level",
|
|
144
|
+
"_dp_on_time_value",
|
|
145
|
+
"_dp_ramp_time_value",
|
|
146
|
+
)
|
|
147
|
+
_category = DataPointCategory.LIGHT
|
|
148
|
+
|
|
149
|
+
def _init_data_point_fields(self) -> None:
|
|
150
|
+
"""Init the data_point fields."""
|
|
151
|
+
super()._init_data_point_fields()
|
|
152
|
+
self._dp_level: DpFloat = self._get_data_point(field=Field.LEVEL, data_point_type=DpFloat)
|
|
153
|
+
self._dp_group_level: DpSensor[float | None] = self._get_data_point(
|
|
154
|
+
field=Field.GROUP_LEVEL, data_point_type=DpSensor[float | None]
|
|
155
|
+
)
|
|
156
|
+
self._dp_on_time_value: DpAction = self._get_data_point(field=Field.ON_TIME_VALUE, data_point_type=DpAction)
|
|
157
|
+
self._dp_ramp_time_value: DpAction = self._get_data_point(field=Field.RAMP_TIME_VALUE, data_point_type=DpAction)
|
|
158
|
+
|
|
159
|
+
@state_property
|
|
160
|
+
def is_on(self) -> bool | None:
|
|
161
|
+
"""Return true if dimmer is on."""
|
|
162
|
+
return self._dp_level.value is not None and self._dp_level.value > _DIMMER_OFF
|
|
163
|
+
|
|
164
|
+
@state_property
|
|
165
|
+
def brightness(self) -> int | None:
|
|
166
|
+
"""Return the brightness of this light between min/max brightness."""
|
|
167
|
+
return int((self._dp_level.value or _MIN_BRIGHTNESS) * _MAX_BRIGHTNESS)
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def brightness_pct(self) -> int | None:
|
|
171
|
+
"""Return the brightness in percent of this light."""
|
|
172
|
+
return int((self._dp_level.value or _MIN_BRIGHTNESS) * _LEVEL_TO_BRIGHTNESS_MULTIPLIER)
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def group_brightness(self) -> int | None:
|
|
176
|
+
"""Return the group brightness of this light between min/max brightness."""
|
|
177
|
+
if self._dp_group_level.value is not None:
|
|
178
|
+
return int(self._dp_group_level.value * _MAX_BRIGHTNESS)
|
|
179
|
+
return None
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def group_brightness_pct(self) -> int | None:
|
|
183
|
+
"""Return the group brightness in percent of this light."""
|
|
184
|
+
if self._dp_group_level.value is not None:
|
|
185
|
+
return int(self._dp_group_level.value * _LEVEL_TO_BRIGHTNESS_MULTIPLIER)
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
@state_property
|
|
189
|
+
def color_temp_kelvin(self) -> int | None:
|
|
190
|
+
"""Return the color temperature in kelvin."""
|
|
191
|
+
return None
|
|
192
|
+
|
|
193
|
+
@state_property
|
|
194
|
+
def hs_color(self) -> tuple[float, float] | None:
|
|
195
|
+
"""Return the hue and saturation color value [float, float]."""
|
|
196
|
+
return None
|
|
197
|
+
|
|
198
|
+
@property
|
|
199
|
+
def supports_brightness(self) -> bool:
|
|
200
|
+
"""Flag if light supports brightness."""
|
|
201
|
+
return isinstance(self._dp_level, DpFloat)
|
|
202
|
+
|
|
203
|
+
@property
|
|
204
|
+
def supports_color_temperature(self) -> bool:
|
|
205
|
+
"""Flag if light supports color temperature."""
|
|
206
|
+
return self.color_temp_kelvin is not None
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def supports_effects(self) -> bool:
|
|
210
|
+
"""Flag if light supports effects."""
|
|
211
|
+
return self.effects is not None and len(self.effects) > 0
|
|
212
|
+
|
|
213
|
+
@property
|
|
214
|
+
def supports_hs_color(self) -> bool:
|
|
215
|
+
"""Flag if light supports color."""
|
|
216
|
+
return self.hs_color is not None
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
def supports_transition(self) -> bool:
|
|
220
|
+
"""Flag if light supports transition."""
|
|
221
|
+
return isinstance(self._dp_ramp_time_value, DpAction)
|
|
222
|
+
|
|
223
|
+
@state_property
|
|
224
|
+
def effect(self) -> str | None:
|
|
225
|
+
"""Return the current effect."""
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
@state_property
|
|
229
|
+
def effects(self) -> tuple[str, ...] | None:
|
|
230
|
+
"""Return the supported effects."""
|
|
231
|
+
return None
|
|
232
|
+
|
|
233
|
+
@bind_collector()
|
|
234
|
+
async def turn_on(self, *, collector: CallParameterCollector | None = None, **kwargs: Unpack[LightOnArgs]) -> None:
|
|
235
|
+
"""Turn the light on."""
|
|
236
|
+
if (on_time := kwargs.get("on_time")) is not None:
|
|
237
|
+
self.set_timer_on_time(on_time=on_time)
|
|
238
|
+
if not self.is_state_change(on=True, **kwargs):
|
|
239
|
+
return
|
|
240
|
+
|
|
241
|
+
if (timer := self.get_and_start_timer()) is not None:
|
|
242
|
+
await self._set_on_time_value(on_time=timer, collector=collector)
|
|
243
|
+
if ramp_time := kwargs.get("ramp_time"):
|
|
244
|
+
await self._set_ramp_time_on_value(ramp_time=ramp_time, collector=collector)
|
|
245
|
+
if not (brightness := kwargs.get("brightness", self.brightness)):
|
|
246
|
+
brightness = int(_MAX_BRIGHTNESS)
|
|
247
|
+
level = brightness / _MAX_BRIGHTNESS
|
|
248
|
+
await self._dp_level.send_value(value=level, collector=collector)
|
|
249
|
+
|
|
250
|
+
@bind_collector()
|
|
251
|
+
async def turn_off(
|
|
252
|
+
self, *, collector: CallParameterCollector | None = None, **kwargs: Unpack[LightOffArgs]
|
|
253
|
+
) -> None:
|
|
254
|
+
"""Turn the light off."""
|
|
255
|
+
self.reset_timer_on_time()
|
|
256
|
+
if not self.is_state_change(off=True, **kwargs):
|
|
257
|
+
return
|
|
258
|
+
if ramp_time := kwargs.get("ramp_time"):
|
|
259
|
+
await self._set_ramp_time_off_value(ramp_time=ramp_time, collector=collector)
|
|
260
|
+
await self._dp_level.send_value(value=_DIMMER_OFF, collector=collector)
|
|
261
|
+
|
|
262
|
+
@bind_collector()
|
|
263
|
+
async def _set_on_time_value(self, *, on_time: float, collector: CallParameterCollector | None = None) -> None:
|
|
264
|
+
"""Set the on time value in seconds."""
|
|
265
|
+
await self._dp_on_time_value.send_value(value=on_time, collector=collector, do_validate=False)
|
|
266
|
+
|
|
267
|
+
async def _set_ramp_time_on_value(
|
|
268
|
+
self, *, ramp_time: float, collector: CallParameterCollector | None = None
|
|
269
|
+
) -> None:
|
|
270
|
+
"""Set the ramp time value in seconds."""
|
|
271
|
+
await self._dp_ramp_time_value.send_value(value=ramp_time, collector=collector)
|
|
272
|
+
|
|
273
|
+
async def _set_ramp_time_off_value(
|
|
274
|
+
self, *, ramp_time: float, collector: CallParameterCollector | None = None
|
|
275
|
+
) -> None:
|
|
276
|
+
"""Set the ramp time value in seconds."""
|
|
277
|
+
await self._set_ramp_time_on_value(ramp_time=ramp_time, collector=collector)
|
|
278
|
+
|
|
279
|
+
def is_state_change(self, **kwargs: Any) -> bool:
|
|
280
|
+
"""Check if the state changes due to kwargs."""
|
|
281
|
+
if (on_time_running := self.timer_on_time_running) is not None and on_time_running is True:
|
|
282
|
+
return True
|
|
283
|
+
if self.timer_on_time is not None:
|
|
284
|
+
return True
|
|
285
|
+
if kwargs.get(_StateChangeArg.ON_TIME) is not None:
|
|
286
|
+
return True
|
|
287
|
+
if kwargs.get(_StateChangeArg.RAMP_TIME) is not None:
|
|
288
|
+
return True
|
|
289
|
+
if kwargs.get(_StateChangeArg.ON) is not None and self.is_on is not True and len(kwargs) == 1:
|
|
290
|
+
return True
|
|
291
|
+
if kwargs.get(_StateChangeArg.OFF) is not None and self.is_on is not False and len(kwargs) == 1:
|
|
292
|
+
return True
|
|
293
|
+
if (brightness := kwargs.get(_StateChangeArg.BRIGHTNESS)) is not None and brightness != self.brightness:
|
|
294
|
+
return True
|
|
295
|
+
if (hs_color := kwargs.get(_StateChangeArg.HS_COLOR)) is not None and hs_color != self.hs_color:
|
|
296
|
+
return True
|
|
297
|
+
if (
|
|
298
|
+
color_temp_kelvin := kwargs.get(_StateChangeArg.COLOR_TEMP_KELVIN)
|
|
299
|
+
) is not None and color_temp_kelvin != self.color_temp_kelvin:
|
|
300
|
+
return True
|
|
301
|
+
if (effect := kwargs.get(_StateChangeArg.EFFECT)) is not None and effect != self.effect:
|
|
302
|
+
return True
|
|
303
|
+
return super().is_state_change(**kwargs)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
class CustomDpColorDimmer(CustomDpDimmer):
|
|
307
|
+
"""Class for Homematic dimmer with color data point."""
|
|
308
|
+
|
|
309
|
+
__slots__ = ("_dp_color",)
|
|
310
|
+
|
|
311
|
+
def _init_data_point_fields(self) -> None:
|
|
312
|
+
"""Init the data_point fields."""
|
|
313
|
+
super()._init_data_point_fields()
|
|
314
|
+
self._dp_color: DpInteger = self._get_data_point(field=Field.COLOR, data_point_type=DpInteger)
|
|
315
|
+
|
|
316
|
+
@state_property
|
|
317
|
+
def hs_color(self) -> tuple[float, float] | None:
|
|
318
|
+
"""Return the hue and saturation color value [float, float]."""
|
|
319
|
+
if (color := self._dp_color.value) is not None:
|
|
320
|
+
if color >= 200:
|
|
321
|
+
# 200 is a special case (white), so we have a saturation of 0.
|
|
322
|
+
# Larger values are undefined.
|
|
323
|
+
# For the sake of robustness we return "white" anyway.
|
|
324
|
+
return _MIN_HUE, _MIN_SATURATION
|
|
325
|
+
|
|
326
|
+
# For all other colors we assume saturation of 1
|
|
327
|
+
return color / 200 * 360, _MAX_SATURATION
|
|
328
|
+
return _MIN_HUE, _MIN_SATURATION
|
|
329
|
+
|
|
330
|
+
@bind_collector()
|
|
331
|
+
async def turn_on(self, *, collector: CallParameterCollector | None = None, **kwargs: Unpack[LightOnArgs]) -> None:
|
|
332
|
+
"""Turn the light on."""
|
|
333
|
+
if not self.is_state_change(on=True, **kwargs):
|
|
334
|
+
return
|
|
335
|
+
if (hs_color := kwargs.get("hs_color")) is not None:
|
|
336
|
+
khue, ksaturation = hs_color
|
|
337
|
+
hue = khue / 360
|
|
338
|
+
saturation = ksaturation / _SATURATION_MULTIPLIER
|
|
339
|
+
color = 200 if saturation < 0.1 else int(round(max(min(hue, 1), 0) * 199))
|
|
340
|
+
await self._dp_color.send_value(value=color, collector=collector)
|
|
341
|
+
await super().turn_on(collector=collector, **kwargs)
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
class CustomDpColorDimmerEffect(CustomDpColorDimmer):
|
|
345
|
+
"""Class for Homematic dimmer with color data point."""
|
|
346
|
+
|
|
347
|
+
__slots__ = ("_dp_effect",)
|
|
348
|
+
|
|
349
|
+
_effects: tuple[str, ...] = (
|
|
350
|
+
_EFFECT_OFF,
|
|
351
|
+
"Slow color change",
|
|
352
|
+
"Medium color change",
|
|
353
|
+
"Fast color change",
|
|
354
|
+
"Campemit",
|
|
355
|
+
"Waterfall",
|
|
356
|
+
"TV simulation",
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
def _init_data_point_fields(self) -> None:
|
|
360
|
+
"""Init the data_point fields."""
|
|
361
|
+
super()._init_data_point_fields()
|
|
362
|
+
self._dp_effect: DpInteger = self._get_data_point(field=Field.PROGRAM, data_point_type=DpInteger)
|
|
363
|
+
|
|
364
|
+
@state_property
|
|
365
|
+
def effect(self) -> str | None:
|
|
366
|
+
"""Return the current effect."""
|
|
367
|
+
if self._dp_effect.value is not None:
|
|
368
|
+
return self._effects[int(self._dp_effect.value)]
|
|
369
|
+
return None
|
|
370
|
+
|
|
371
|
+
@state_property
|
|
372
|
+
def effects(self) -> tuple[str, ...] | None:
|
|
373
|
+
"""Return the supported effects."""
|
|
374
|
+
return self._effects
|
|
375
|
+
|
|
376
|
+
@bind_collector()
|
|
377
|
+
async def turn_on(self, *, collector: CallParameterCollector | None = None, **kwargs: Unpack[LightOnArgs]) -> None:
|
|
378
|
+
"""Turn the light on."""
|
|
379
|
+
if not self.is_state_change(on=True, **kwargs):
|
|
380
|
+
return
|
|
381
|
+
|
|
382
|
+
if "effect" not in kwargs and self.supports_effects and self.effect != _EFFECT_OFF:
|
|
383
|
+
await self._dp_effect.send_value(value=0, collector=collector, collector_order=5)
|
|
384
|
+
|
|
385
|
+
if (
|
|
386
|
+
self.supports_effects
|
|
387
|
+
and (effect := kwargs.get("effect")) is not None
|
|
388
|
+
and (effect_idx := self._effects.index(effect)) is not None
|
|
389
|
+
):
|
|
390
|
+
await self._dp_effect.send_value(value=effect_idx, collector=collector, collector_order=95)
|
|
391
|
+
|
|
392
|
+
await super().turn_on(collector=collector, **kwargs)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
class CustomDpColorTempDimmer(CustomDpDimmer):
|
|
396
|
+
"""Class for Homematic dimmer with color temperature."""
|
|
397
|
+
|
|
398
|
+
__slots__ = ("_dp_color_level",)
|
|
399
|
+
|
|
400
|
+
def _init_data_point_fields(self) -> None:
|
|
401
|
+
"""Init the data_point fields."""
|
|
402
|
+
super()._init_data_point_fields()
|
|
403
|
+
self._dp_color_level: DpFloat = self._get_data_point(field=Field.COLOR_LEVEL, data_point_type=DpFloat)
|
|
404
|
+
|
|
405
|
+
@state_property
|
|
406
|
+
def color_temp_kelvin(self) -> int | None:
|
|
407
|
+
"""Return the color temperature in kelvin."""
|
|
408
|
+
return math.floor(
|
|
409
|
+
_MAX_KELVIN / int(_MAX_MIREDS - (_MAX_MIREDS - _MIN_MIREDS) * (self._dp_color_level.value or _DIMMER_OFF))
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
@bind_collector()
|
|
413
|
+
async def turn_on(self, *, collector: CallParameterCollector | None = None, **kwargs: Unpack[LightOnArgs]) -> None:
|
|
414
|
+
"""Turn the light on."""
|
|
415
|
+
if not self.is_state_change(on=True, **kwargs):
|
|
416
|
+
return
|
|
417
|
+
if (color_temp_kelvin := kwargs.get("color_temp_kelvin")) is not None:
|
|
418
|
+
color_level = (_MAX_MIREDS - math.floor(_MAX_KELVIN / color_temp_kelvin)) / (_MAX_MIREDS - _MIN_MIREDS)
|
|
419
|
+
await self._dp_color_level.send_value(value=color_level, collector=collector)
|
|
420
|
+
|
|
421
|
+
await super().turn_on(collector=collector, **kwargs)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
class CustomDpIpRGBWLight(CustomDpDimmer):
|
|
425
|
+
"""Class for HomematicIP HmIP-RGBW light data point."""
|
|
426
|
+
|
|
427
|
+
__slots__ = (
|
|
428
|
+
"_dp_activity_state",
|
|
429
|
+
"_dp_color_temperature_kelvin",
|
|
430
|
+
"_dp_device_operation_mode",
|
|
431
|
+
"_dp_effect",
|
|
432
|
+
"_dp_hue",
|
|
433
|
+
"_dp_on_time_unit",
|
|
434
|
+
"_dp_ramp_time_to_off_unit",
|
|
435
|
+
"_dp_ramp_time_to_off_value",
|
|
436
|
+
"_dp_ramp_time_unit",
|
|
437
|
+
"_dp_saturation",
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
def _init_data_point_fields(self) -> None:
|
|
441
|
+
"""Init the data_point fields."""
|
|
442
|
+
super()._init_data_point_fields()
|
|
443
|
+
self._dp_activity_state: DpSensor[str | None] = self._get_data_point(
|
|
444
|
+
field=Field.DIRECTION, data_point_type=DpSensor[str | None]
|
|
445
|
+
)
|
|
446
|
+
self._dp_color_temperature_kelvin: DpInteger = self._get_data_point(
|
|
447
|
+
field=Field.COLOR_TEMPERATURE, data_point_type=DpInteger
|
|
448
|
+
)
|
|
449
|
+
self._dp_device_operation_mode: DpSelect = self._get_data_point(
|
|
450
|
+
field=Field.DEVICE_OPERATION_MODE, data_point_type=DpSelect
|
|
451
|
+
)
|
|
452
|
+
self._dp_on_time_unit: DpAction = self._get_data_point(field=Field.ON_TIME_UNIT, data_point_type=DpAction)
|
|
453
|
+
self._dp_effect: DpAction = self._get_data_point(field=Field.EFFECT, data_point_type=DpAction)
|
|
454
|
+
self._dp_hue: DpInteger = self._get_data_point(field=Field.HUE, data_point_type=DpInteger)
|
|
455
|
+
self._dp_ramp_time_to_off_unit: DpAction = self._get_data_point(
|
|
456
|
+
field=Field.RAMP_TIME_TO_OFF_UNIT, data_point_type=DpAction
|
|
457
|
+
)
|
|
458
|
+
self._dp_ramp_time_to_off_value: DpAction = self._get_data_point(
|
|
459
|
+
field=Field.RAMP_TIME_TO_OFF_VALUE, data_point_type=DpAction
|
|
460
|
+
)
|
|
461
|
+
self._dp_ramp_time_unit: DpAction = self._get_data_point(field=Field.RAMP_TIME_UNIT, data_point_type=DpAction)
|
|
462
|
+
self._dp_saturation: DpFloat = self._get_data_point(field=Field.SATURATION, data_point_type=DpFloat)
|
|
463
|
+
|
|
464
|
+
@state_property
|
|
465
|
+
def color_temp_kelvin(self) -> int | None:
|
|
466
|
+
"""Return the color temperature in kelvin."""
|
|
467
|
+
if not self._dp_color_temperature_kelvin.value:
|
|
468
|
+
return None
|
|
469
|
+
return self._dp_color_temperature_kelvin.value
|
|
470
|
+
|
|
471
|
+
@state_property
|
|
472
|
+
def hs_color(self) -> tuple[float, float] | None:
|
|
473
|
+
"""Return the hue and saturation color value [float, float]."""
|
|
474
|
+
if self._dp_hue.value is not None and self._dp_saturation.value is not None:
|
|
475
|
+
return self._dp_hue.value, self._dp_saturation.value * _SATURATION_MULTIPLIER
|
|
476
|
+
return None
|
|
477
|
+
|
|
478
|
+
@property
|
|
479
|
+
def _relevant_data_points(self) -> tuple[GenericDataPoint, ...]:
|
|
480
|
+
"""Returns the list of relevant data points. To be overridden by subclasses."""
|
|
481
|
+
if self._device_operation_mode == _DeviceOperationMode.RGBW:
|
|
482
|
+
return (
|
|
483
|
+
self._dp_hue,
|
|
484
|
+
self._dp_level,
|
|
485
|
+
self._dp_saturation,
|
|
486
|
+
self._dp_color_temperature_kelvin,
|
|
487
|
+
)
|
|
488
|
+
if self._device_operation_mode == _DeviceOperationMode.RGB:
|
|
489
|
+
return self._dp_hue, self._dp_level, self._dp_saturation
|
|
490
|
+
if self._device_operation_mode == _DeviceOperationMode.TUNABLE_WHITE:
|
|
491
|
+
return self._dp_level, self._dp_color_temperature_kelvin
|
|
492
|
+
return (self._dp_level,)
|
|
493
|
+
|
|
494
|
+
@property
|
|
495
|
+
def supports_color_temperature(self) -> bool:
|
|
496
|
+
"""Flag if light supports color temperature."""
|
|
497
|
+
return self._device_operation_mode == _DeviceOperationMode.TUNABLE_WHITE
|
|
498
|
+
|
|
499
|
+
@property
|
|
500
|
+
def supports_effects(self) -> bool:
|
|
501
|
+
"""Flag if light supports effects."""
|
|
502
|
+
return (
|
|
503
|
+
self._device_operation_mode != _DeviceOperationMode.PWM
|
|
504
|
+
and self.effects is not None
|
|
505
|
+
and len(self.effects) > 0
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
@property
|
|
509
|
+
def supports_hs_color(self) -> bool:
|
|
510
|
+
"""Flag if light supports color."""
|
|
511
|
+
return self._device_operation_mode in (
|
|
512
|
+
_DeviceOperationMode.RGBW,
|
|
513
|
+
_DeviceOperationMode.RGB,
|
|
514
|
+
)
|
|
515
|
+
|
|
516
|
+
@property
|
|
517
|
+
def usage(self) -> DataPointUsage:
|
|
518
|
+
"""
|
|
519
|
+
Return the data_point usage.
|
|
520
|
+
|
|
521
|
+
Avoid creating data points that are not usable in selected device operation mode.
|
|
522
|
+
"""
|
|
523
|
+
if (
|
|
524
|
+
self._device_operation_mode in (_DeviceOperationMode.RGB, _DeviceOperationMode.RGBW)
|
|
525
|
+
and self._channel.no in (2, 3, 4)
|
|
526
|
+
) or (self._device_operation_mode == _DeviceOperationMode.TUNABLE_WHITE and self._channel.no in (3, 4)):
|
|
527
|
+
return DataPointUsage.NO_CREATE
|
|
528
|
+
return self._get_data_point_usage()
|
|
529
|
+
|
|
530
|
+
@state_property
|
|
531
|
+
def effects(self) -> tuple[str, ...] | None:
|
|
532
|
+
"""Return the supported effects."""
|
|
533
|
+
return self._dp_effect.values or ()
|
|
534
|
+
|
|
535
|
+
@bind_collector()
|
|
536
|
+
async def turn_on(self, *, collector: CallParameterCollector | None = None, **kwargs: Unpack[LightOnArgs]) -> None:
|
|
537
|
+
"""Turn the light on."""
|
|
538
|
+
if on_time := (kwargs.get("on_time") or self.get_and_start_timer()):
|
|
539
|
+
kwargs["on_time"] = on_time
|
|
540
|
+
if not self.is_state_change(on=True, **kwargs):
|
|
541
|
+
return
|
|
542
|
+
if (hs_color := kwargs.get("hs_color")) is not None:
|
|
543
|
+
hue, ksaturation = hs_color
|
|
544
|
+
saturation = ksaturation / _SATURATION_MULTIPLIER
|
|
545
|
+
await self._dp_hue.send_value(value=int(hue), collector=collector)
|
|
546
|
+
await self._dp_saturation.send_value(value=saturation, collector=collector)
|
|
547
|
+
if color_temp_kelvin := kwargs.get("color_temp_kelvin"):
|
|
548
|
+
await self._dp_color_temperature_kelvin.send_value(value=color_temp_kelvin, collector=collector)
|
|
549
|
+
if on_time is None and kwargs.get("ramp_time"):
|
|
550
|
+
await self._set_on_time_value(on_time=_NOT_USED, collector=collector)
|
|
551
|
+
if self.supports_effects and (effect := kwargs.get("effect")) is not None:
|
|
552
|
+
await self._dp_effect.send_value(value=effect, collector=collector)
|
|
553
|
+
|
|
554
|
+
await super().turn_on(collector=collector, **kwargs)
|
|
555
|
+
|
|
556
|
+
@bind_collector()
|
|
557
|
+
async def turn_off(
|
|
558
|
+
self, *, collector: CallParameterCollector | None = None, **kwargs: Unpack[LightOffArgs]
|
|
559
|
+
) -> None:
|
|
560
|
+
"""Turn the light off."""
|
|
561
|
+
if kwargs.get("on_time") is None and kwargs.get("ramp_time"):
|
|
562
|
+
await self._set_on_time_value(on_time=_NOT_USED, collector=collector)
|
|
563
|
+
await super().turn_off(collector=collector, **kwargs)
|
|
564
|
+
|
|
565
|
+
@property
|
|
566
|
+
def _device_operation_mode(self) -> _DeviceOperationMode:
|
|
567
|
+
"""Return the device operation mode."""
|
|
568
|
+
if (mode := self._dp_device_operation_mode.value) is None:
|
|
569
|
+
return _DeviceOperationMode.RGBW
|
|
570
|
+
return _DeviceOperationMode(mode)
|
|
571
|
+
|
|
572
|
+
@bind_collector()
|
|
573
|
+
async def _set_on_time_value(self, *, on_time: float, collector: CallParameterCollector | None = None) -> None:
|
|
574
|
+
"""Set the on time value in seconds."""
|
|
575
|
+
on_time, on_time_unit = _recalc_unit_timer(time=on_time)
|
|
576
|
+
if on_time_unit is not None:
|
|
577
|
+
await self._dp_on_time_unit.send_value(value=on_time_unit, collector=collector)
|
|
578
|
+
await self._dp_on_time_value.send_value(value=float(on_time), collector=collector)
|
|
579
|
+
|
|
580
|
+
async def _set_ramp_time_on_value(
|
|
581
|
+
self, *, ramp_time: float, collector: CallParameterCollector | None = None
|
|
582
|
+
) -> None:
|
|
583
|
+
"""Set the ramp time value in seconds."""
|
|
584
|
+
ramp_time, ramp_time_unit = _recalc_unit_timer(time=ramp_time)
|
|
585
|
+
if ramp_time_unit is not None:
|
|
586
|
+
await self._dp_ramp_time_unit.send_value(value=ramp_time_unit, collector=collector)
|
|
587
|
+
await self._dp_ramp_time_value.send_value(value=float(ramp_time), collector=collector)
|
|
588
|
+
|
|
589
|
+
async def _set_ramp_time_off_value(
|
|
590
|
+
self, *, ramp_time: float, collector: CallParameterCollector | None = None
|
|
591
|
+
) -> None:
|
|
592
|
+
"""Set the ramp time value in seconds."""
|
|
593
|
+
ramp_time, ramp_time_unit = _recalc_unit_timer(time=ramp_time)
|
|
594
|
+
if ramp_time_unit is not None:
|
|
595
|
+
await self._dp_ramp_time_unit.send_value(value=ramp_time_unit, collector=collector)
|
|
596
|
+
await self._dp_ramp_time_value.send_value(value=float(ramp_time), collector=collector)
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
class CustomDpIpDrgDaliLight(CustomDpDimmer):
|
|
600
|
+
"""Class for HomematicIP HmIP-DRG-DALI light data point."""
|
|
601
|
+
|
|
602
|
+
__slots__ = (
|
|
603
|
+
"_dp_color_temperature_kelvin",
|
|
604
|
+
"_dp_effect",
|
|
605
|
+
"_dp_hue",
|
|
606
|
+
"_dp_on_time_unit",
|
|
607
|
+
"_dp_ramp_time_to_off_unit",
|
|
608
|
+
"_dp_ramp_time_to_off_value",
|
|
609
|
+
"_dp_ramp_time_unit",
|
|
610
|
+
"_dp_saturation",
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
def _init_data_point_fields(self) -> None:
|
|
614
|
+
"""Init the data_point fields."""
|
|
615
|
+
super()._init_data_point_fields()
|
|
616
|
+
self._dp_color_temperature_kelvin: DpInteger = self._get_data_point(
|
|
617
|
+
field=Field.COLOR_TEMPERATURE, data_point_type=DpInteger
|
|
618
|
+
)
|
|
619
|
+
self._dp_on_time_unit: DpAction = self._get_data_point(field=Field.ON_TIME_UNIT, data_point_type=DpAction)
|
|
620
|
+
self._dp_effect: DpAction = self._get_data_point(field=Field.EFFECT, data_point_type=DpAction)
|
|
621
|
+
self._dp_hue: DpInteger = self._get_data_point(field=Field.HUE, data_point_type=DpInteger)
|
|
622
|
+
self._dp_ramp_time_to_off_unit: DpAction = self._get_data_point(
|
|
623
|
+
field=Field.RAMP_TIME_TO_OFF_UNIT, data_point_type=DpAction
|
|
624
|
+
)
|
|
625
|
+
self._dp_ramp_time_to_off_value: DpAction = self._get_data_point(
|
|
626
|
+
field=Field.RAMP_TIME_TO_OFF_VALUE, data_point_type=DpAction
|
|
627
|
+
)
|
|
628
|
+
self._dp_ramp_time_unit: DpAction = self._get_data_point(field=Field.RAMP_TIME_UNIT, data_point_type=DpAction)
|
|
629
|
+
self._dp_saturation: DpFloat = self._get_data_point(field=Field.SATURATION, data_point_type=DpFloat)
|
|
630
|
+
|
|
631
|
+
@state_property
|
|
632
|
+
def color_temp_kelvin(self) -> int | None:
|
|
633
|
+
"""Return the color temperature in kelvin."""
|
|
634
|
+
if not self._dp_color_temperature_kelvin.value:
|
|
635
|
+
return None
|
|
636
|
+
return self._dp_color_temperature_kelvin.value
|
|
637
|
+
|
|
638
|
+
@state_property
|
|
639
|
+
def hs_color(self) -> tuple[float, float] | None:
|
|
640
|
+
"""Return the hue and saturation color value [float, float]."""
|
|
641
|
+
if self._dp_hue.value is not None and self._dp_saturation.value is not None:
|
|
642
|
+
return self._dp_hue.value, self._dp_saturation.value * _SATURATION_MULTIPLIER
|
|
643
|
+
return None
|
|
644
|
+
|
|
645
|
+
@property
|
|
646
|
+
def _relevant_data_points(self) -> tuple[GenericDataPoint, ...]:
|
|
647
|
+
"""Returns the list of relevant data points. To be overridden by subclasses."""
|
|
648
|
+
return (self._dp_level,)
|
|
649
|
+
|
|
650
|
+
@state_property
|
|
651
|
+
def effects(self) -> tuple[str, ...] | None:
|
|
652
|
+
"""Return the supported effects."""
|
|
653
|
+
return self._dp_effect.values or ()
|
|
654
|
+
|
|
655
|
+
@bind_collector()
|
|
656
|
+
async def turn_on(self, *, collector: CallParameterCollector | None = None, **kwargs: Unpack[LightOnArgs]) -> None:
|
|
657
|
+
"""Turn the light on."""
|
|
658
|
+
if not self.is_state_change(on=True, **kwargs):
|
|
659
|
+
return
|
|
660
|
+
if (hs_color := kwargs.get("hs_color")) is not None:
|
|
661
|
+
hue, ksaturation = hs_color
|
|
662
|
+
saturation = ksaturation / _SATURATION_MULTIPLIER
|
|
663
|
+
await self._dp_hue.send_value(value=int(hue), collector=collector)
|
|
664
|
+
await self._dp_saturation.send_value(value=saturation, collector=collector)
|
|
665
|
+
if color_temp_kelvin := kwargs.get("color_temp_kelvin"):
|
|
666
|
+
await self._dp_color_temperature_kelvin.send_value(value=color_temp_kelvin, collector=collector)
|
|
667
|
+
if kwargs.get("on_time") is None and kwargs.get("ramp_time"):
|
|
668
|
+
await self._set_on_time_value(on_time=_NOT_USED, collector=collector)
|
|
669
|
+
if self.supports_effects and (effect := kwargs.get("effect")) is not None:
|
|
670
|
+
await self._dp_effect.send_value(value=effect, collector=collector)
|
|
671
|
+
|
|
672
|
+
await super().turn_on(collector=collector, **kwargs)
|
|
673
|
+
|
|
674
|
+
@bind_collector()
|
|
675
|
+
async def _set_on_time_value(self, *, on_time: float, collector: CallParameterCollector | None = None) -> None:
|
|
676
|
+
"""Set the on time value in seconds."""
|
|
677
|
+
on_time, on_time_unit = _recalc_unit_timer(time=on_time)
|
|
678
|
+
if on_time_unit:
|
|
679
|
+
await self._dp_on_time_unit.send_value(value=on_time_unit, collector=collector)
|
|
680
|
+
await self._dp_on_time_value.send_value(value=float(on_time), collector=collector)
|
|
681
|
+
|
|
682
|
+
async def _set_ramp_time_on_value(
|
|
683
|
+
self, *, ramp_time: float, collector: CallParameterCollector | None = None
|
|
684
|
+
) -> None:
|
|
685
|
+
"""Set the ramp time value in seconds."""
|
|
686
|
+
ramp_time, ramp_time_unit = _recalc_unit_timer(time=ramp_time)
|
|
687
|
+
if ramp_time_unit:
|
|
688
|
+
await self._dp_ramp_time_unit.send_value(value=ramp_time_unit, collector=collector)
|
|
689
|
+
await self._dp_ramp_time_value.send_value(value=float(ramp_time), collector=collector)
|
|
690
|
+
|
|
691
|
+
async def _set_ramp_time_off_value(
|
|
692
|
+
self, *, ramp_time: float, collector: CallParameterCollector | None = None
|
|
693
|
+
) -> None:
|
|
694
|
+
"""Set the ramp time value in seconds."""
|
|
695
|
+
ramp_time, ramp_time_unit = _recalc_unit_timer(time=ramp_time)
|
|
696
|
+
if ramp_time_unit:
|
|
697
|
+
await self._dp_ramp_time_unit.send_value(value=ramp_time_unit, collector=collector)
|
|
698
|
+
await self._dp_ramp_time_value.send_value(value=float(ramp_time), collector=collector)
|
|
699
|
+
|
|
700
|
+
|
|
701
|
+
class CustomDpIpFixedColorLight(CustomDpDimmer):
|
|
702
|
+
"""Class for HomematicIP HmIP-BSL light data point."""
|
|
703
|
+
|
|
704
|
+
__slots__ = (
|
|
705
|
+
"_dp_channel_color",
|
|
706
|
+
"_dp_color",
|
|
707
|
+
"_dp_effect",
|
|
708
|
+
"_dp_on_time_unit",
|
|
709
|
+
"_dp_ramp_time_unit",
|
|
710
|
+
"_effect_list",
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
@state_property
|
|
714
|
+
def color_name(self) -> str | None:
|
|
715
|
+
"""Return the name of the color."""
|
|
716
|
+
return self._dp_color.value
|
|
717
|
+
|
|
718
|
+
@property
|
|
719
|
+
def channel_color_name(self) -> str | None:
|
|
720
|
+
"""Return the name of the channel color."""
|
|
721
|
+
return self._dp_channel_color.value
|
|
722
|
+
|
|
723
|
+
def _init_data_point_fields(self) -> None:
|
|
724
|
+
"""Init the data_point fields."""
|
|
725
|
+
super()._init_data_point_fields()
|
|
726
|
+
self._dp_color: DpSelect = self._get_data_point(field=Field.COLOR, data_point_type=DpSelect)
|
|
727
|
+
self._dp_channel_color: DpSensor[str | None] = self._get_data_point(
|
|
728
|
+
field=Field.CHANNEL_COLOR, data_point_type=DpSensor[str | None]
|
|
729
|
+
)
|
|
730
|
+
self._dp_on_time_unit: DpAction = self._get_data_point(field=Field.ON_TIME_UNIT, data_point_type=DpAction)
|
|
731
|
+
self._dp_ramp_time_unit: DpAction = self._get_data_point(field=Field.RAMP_TIME_UNIT, data_point_type=DpAction)
|
|
732
|
+
self._dp_effect: DpSelect = self._get_data_point(field=Field.COLOR_BEHAVIOUR, data_point_type=DpSelect)
|
|
733
|
+
self._effect_list = (
|
|
734
|
+
tuple(str(item) for item in self._dp_effect.values if item not in _EXCLUDE_FROM_COLOR_BEHAVIOUR)
|
|
735
|
+
if (self._dp_effect and self._dp_effect.values)
|
|
736
|
+
else ()
|
|
737
|
+
)
|
|
738
|
+
|
|
739
|
+
@state_property
|
|
740
|
+
def effect(self) -> str | None:
|
|
741
|
+
"""Return the current effect."""
|
|
742
|
+
if (effect := self._dp_effect.value) is not None and effect in self._effect_list:
|
|
743
|
+
return effect
|
|
744
|
+
return None
|
|
745
|
+
|
|
746
|
+
@state_property
|
|
747
|
+
def effects(self) -> tuple[str, ...] | None:
|
|
748
|
+
"""Return the supported effects."""
|
|
749
|
+
return self._effect_list
|
|
750
|
+
|
|
751
|
+
@state_property
|
|
752
|
+
def hs_color(self) -> tuple[float, float] | None:
|
|
753
|
+
"""Return the hue and saturation color value [float, float]."""
|
|
754
|
+
if (
|
|
755
|
+
self._dp_color.value is not None
|
|
756
|
+
and (hs_color := _FIXED_COLOR_SWITCHER.get(self._dp_color.value)) is not None
|
|
757
|
+
):
|
|
758
|
+
return hs_color
|
|
759
|
+
return _MIN_HUE, _MIN_SATURATION
|
|
760
|
+
|
|
761
|
+
@property
|
|
762
|
+
def channel_hs_color(self) -> tuple[float, float] | None:
|
|
763
|
+
"""Return the channel hue and saturation color value [float, float]."""
|
|
764
|
+
if self._dp_channel_color.value is not None:
|
|
765
|
+
return _FIXED_COLOR_SWITCHER.get(self._dp_channel_color.value, (_MIN_HUE, _MIN_SATURATION))
|
|
766
|
+
return None
|
|
767
|
+
|
|
768
|
+
@bind_collector()
|
|
769
|
+
async def turn_on(self, *, collector: CallParameterCollector | None = None, **kwargs: Unpack[LightOnArgs]) -> None:
|
|
770
|
+
"""Turn the light on."""
|
|
771
|
+
if not self.is_state_change(on=True, **kwargs):
|
|
772
|
+
return
|
|
773
|
+
if (hs_color := kwargs.get("hs_color")) is not None:
|
|
774
|
+
simple_rgb_color = _convert_color(color=hs_color)
|
|
775
|
+
await self._dp_color.send_value(value=simple_rgb_color, collector=collector)
|
|
776
|
+
elif self.color_name in _NO_COLOR:
|
|
777
|
+
await self._dp_color.send_value(value=_FixedColor.WHITE, collector=collector)
|
|
778
|
+
if (effect := kwargs.get("effect")) is not None and effect in self._effect_list:
|
|
779
|
+
await self._dp_effect.send_value(value=effect, collector=collector)
|
|
780
|
+
elif self._dp_effect.value not in self._effect_list:
|
|
781
|
+
await self._dp_effect.send_value(value=_ColorBehaviour.ON, collector=collector)
|
|
782
|
+
elif (color_behaviour := self._dp_effect.value) is not None:
|
|
783
|
+
await self._dp_effect.send_value(value=color_behaviour, collector=collector)
|
|
784
|
+
|
|
785
|
+
await super().turn_on(collector=collector, **kwargs)
|
|
786
|
+
|
|
787
|
+
@bind_collector()
|
|
788
|
+
async def _set_on_time_value(self, *, on_time: float, collector: CallParameterCollector | None = None) -> None:
|
|
789
|
+
"""Set the on time value in seconds."""
|
|
790
|
+
on_time, on_time_unit = _recalc_unit_timer(time=on_time)
|
|
791
|
+
if on_time_unit:
|
|
792
|
+
await self._dp_on_time_unit.send_value(value=on_time_unit, collector=collector)
|
|
793
|
+
await self._dp_on_time_value.send_value(value=float(on_time), collector=collector)
|
|
794
|
+
|
|
795
|
+
async def _set_ramp_time_on_value(
|
|
796
|
+
self, *, ramp_time: float, collector: CallParameterCollector | None = None
|
|
797
|
+
) -> None:
|
|
798
|
+
"""Set the ramp time value in seconds."""
|
|
799
|
+
ramp_time, ramp_time_unit = _recalc_unit_timer(time=ramp_time)
|
|
800
|
+
if ramp_time_unit:
|
|
801
|
+
await self._dp_ramp_time_unit.send_value(value=ramp_time_unit, collector=collector)
|
|
802
|
+
await self._dp_ramp_time_value.send_value(value=float(ramp_time), collector=collector)
|
|
803
|
+
|
|
804
|
+
|
|
805
|
+
def _recalc_unit_timer(*, time: float) -> tuple[float, int | None]:
|
|
806
|
+
"""Recalculate unit and value of timer."""
|
|
807
|
+
ramp_time_unit = _TimeUnit.SECONDS
|
|
808
|
+
if time == _NOT_USED:
|
|
809
|
+
return time, None
|
|
810
|
+
if time > 16343:
|
|
811
|
+
time /= 60
|
|
812
|
+
ramp_time_unit = _TimeUnit.MINUTES
|
|
813
|
+
if time > 16343:
|
|
814
|
+
time /= 60
|
|
815
|
+
ramp_time_unit = _TimeUnit.HOURS
|
|
816
|
+
return time, ramp_time_unit
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
def _convert_color(*, color: tuple[float, float]) -> str:
|
|
820
|
+
"""
|
|
821
|
+
Convert the given color to the reduced color of the device.
|
|
822
|
+
|
|
823
|
+
Device contains only 8 colors including white and black,
|
|
824
|
+
so a conversion is required.
|
|
825
|
+
"""
|
|
826
|
+
hue: int = int(color[0])
|
|
827
|
+
if int(color[1]) < 5:
|
|
828
|
+
return _FixedColor.WHITE
|
|
829
|
+
if 30 < hue <= 90:
|
|
830
|
+
return _FixedColor.YELLOW
|
|
831
|
+
if 90 < hue <= 150:
|
|
832
|
+
return _FixedColor.GREEN
|
|
833
|
+
if 150 < hue <= 210:
|
|
834
|
+
return _FixedColor.TURQUOISE
|
|
835
|
+
if 210 < hue <= 270:
|
|
836
|
+
return _FixedColor.BLUE
|
|
837
|
+
if 270 < hue <= 330:
|
|
838
|
+
return _FixedColor.PURPLE
|
|
839
|
+
return _FixedColor.RED
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
def make_ip_dimmer(
|
|
843
|
+
*,
|
|
844
|
+
channel: hmd.Channel,
|
|
845
|
+
custom_config: CustomConfig,
|
|
846
|
+
) -> None:
|
|
847
|
+
"""Create HomematicIP dimmer data point."""
|
|
848
|
+
hmed.make_custom_data_point(
|
|
849
|
+
channel=channel,
|
|
850
|
+
data_point_class=CustomDpDimmer,
|
|
851
|
+
device_profile=DeviceProfile.IP_DIMMER,
|
|
852
|
+
custom_config=custom_config,
|
|
853
|
+
)
|
|
854
|
+
|
|
855
|
+
|
|
856
|
+
def make_rf_dimmer(
|
|
857
|
+
*,
|
|
858
|
+
channel: hmd.Channel,
|
|
859
|
+
custom_config: CustomConfig,
|
|
860
|
+
) -> None:
|
|
861
|
+
"""Create Homematic classic dimmer data point."""
|
|
862
|
+
hmed.make_custom_data_point(
|
|
863
|
+
channel=channel,
|
|
864
|
+
data_point_class=CustomDpDimmer,
|
|
865
|
+
device_profile=DeviceProfile.RF_DIMMER,
|
|
866
|
+
custom_config=custom_config,
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
|
|
870
|
+
def make_rf_dimmer_color(
|
|
871
|
+
*,
|
|
872
|
+
channel: hmd.Channel,
|
|
873
|
+
custom_config: CustomConfig,
|
|
874
|
+
) -> None:
|
|
875
|
+
"""Create Homematic classic dimmer with color data point."""
|
|
876
|
+
hmed.make_custom_data_point(
|
|
877
|
+
channel=channel,
|
|
878
|
+
data_point_class=CustomDpColorDimmer,
|
|
879
|
+
device_profile=DeviceProfile.RF_DIMMER_COLOR,
|
|
880
|
+
custom_config=custom_config,
|
|
881
|
+
)
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
def make_rf_dimmer_color_fixed(
|
|
885
|
+
*,
|
|
886
|
+
channel: hmd.Channel,
|
|
887
|
+
custom_config: CustomConfig,
|
|
888
|
+
) -> None:
|
|
889
|
+
"""Create Homematic classic dimmer with fixed color data point."""
|
|
890
|
+
hmed.make_custom_data_point(
|
|
891
|
+
channel=channel,
|
|
892
|
+
data_point_class=CustomDpColorDimmer,
|
|
893
|
+
device_profile=DeviceProfile.RF_DIMMER_COLOR_FIXED,
|
|
894
|
+
custom_config=custom_config,
|
|
895
|
+
)
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
def make_rf_dimmer_color_effect(
|
|
899
|
+
*,
|
|
900
|
+
channel: hmd.Channel,
|
|
901
|
+
custom_config: CustomConfig,
|
|
902
|
+
) -> None:
|
|
903
|
+
"""Create Homematic classic dimmer and effect with color data point."""
|
|
904
|
+
hmed.make_custom_data_point(
|
|
905
|
+
channel=channel,
|
|
906
|
+
data_point_class=CustomDpColorDimmerEffect,
|
|
907
|
+
device_profile=DeviceProfile.RF_DIMMER_COLOR,
|
|
908
|
+
custom_config=custom_config,
|
|
909
|
+
)
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
def make_rf_dimmer_color_temp(
|
|
913
|
+
*,
|
|
914
|
+
channel: hmd.Channel,
|
|
915
|
+
custom_config: CustomConfig,
|
|
916
|
+
) -> None:
|
|
917
|
+
"""Create Homematic classic dimmer with color temperature data point."""
|
|
918
|
+
hmed.make_custom_data_point(
|
|
919
|
+
channel=channel,
|
|
920
|
+
data_point_class=CustomDpColorTempDimmer,
|
|
921
|
+
device_profile=DeviceProfile.RF_DIMMER_COLOR_TEMP,
|
|
922
|
+
custom_config=custom_config,
|
|
923
|
+
)
|
|
924
|
+
|
|
925
|
+
|
|
926
|
+
def make_rf_dimmer_with_virt_channel(
|
|
927
|
+
*,
|
|
928
|
+
channel: hmd.Channel,
|
|
929
|
+
custom_config: CustomConfig,
|
|
930
|
+
) -> None:
|
|
931
|
+
"""Create Homematic classic dimmer data point."""
|
|
932
|
+
hmed.make_custom_data_point(
|
|
933
|
+
channel=channel,
|
|
934
|
+
data_point_class=CustomDpDimmer,
|
|
935
|
+
device_profile=DeviceProfile.RF_DIMMER_WITH_VIRT_CHANNEL,
|
|
936
|
+
custom_config=custom_config,
|
|
937
|
+
)
|
|
938
|
+
|
|
939
|
+
|
|
940
|
+
def make_ip_fixed_color_light(
|
|
941
|
+
*,
|
|
942
|
+
channel: hmd.Channel,
|
|
943
|
+
custom_config: CustomConfig,
|
|
944
|
+
) -> None:
|
|
945
|
+
"""Create fixed color light data points like HmIP-BSL."""
|
|
946
|
+
hmed.make_custom_data_point(
|
|
947
|
+
channel=channel,
|
|
948
|
+
data_point_class=CustomDpIpFixedColorLight,
|
|
949
|
+
device_profile=DeviceProfile.IP_FIXED_COLOR_LIGHT,
|
|
950
|
+
custom_config=custom_config,
|
|
951
|
+
)
|
|
952
|
+
|
|
953
|
+
|
|
954
|
+
def make_ip_simple_fixed_color_light_wired(
|
|
955
|
+
*,
|
|
956
|
+
channel: hmd.Channel,
|
|
957
|
+
custom_config: CustomConfig,
|
|
958
|
+
) -> None:
|
|
959
|
+
"""Create simple fixed color light data points like HmIPW-WRC6."""
|
|
960
|
+
hmed.make_custom_data_point(
|
|
961
|
+
channel=channel,
|
|
962
|
+
data_point_class=CustomDpIpFixedColorLight,
|
|
963
|
+
device_profile=DeviceProfile.IP_SIMPLE_FIXED_COLOR_LIGHT_WIRED,
|
|
964
|
+
custom_config=custom_config,
|
|
965
|
+
)
|
|
966
|
+
|
|
967
|
+
|
|
968
|
+
def make_ip_rgbw_light(
|
|
969
|
+
*,
|
|
970
|
+
channel: hmd.Channel,
|
|
971
|
+
custom_config: CustomConfig,
|
|
972
|
+
) -> None:
|
|
973
|
+
"""Create simple fixed color light data points like HmIP-RGBW."""
|
|
974
|
+
hmed.make_custom_data_point(
|
|
975
|
+
channel=channel,
|
|
976
|
+
data_point_class=CustomDpIpRGBWLight,
|
|
977
|
+
device_profile=DeviceProfile.IP_RGBW_LIGHT,
|
|
978
|
+
custom_config=custom_config,
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
def make_ip_drg_dali_light(
|
|
983
|
+
*,
|
|
984
|
+
channel: hmd.Channel,
|
|
985
|
+
custom_config: CustomConfig,
|
|
986
|
+
) -> None:
|
|
987
|
+
"""Create color light data points like HmIP-DRG-DALI."""
|
|
988
|
+
hmed.make_custom_data_point(
|
|
989
|
+
channel=channel,
|
|
990
|
+
data_point_class=CustomDpIpDrgDaliLight,
|
|
991
|
+
device_profile=DeviceProfile.IP_DRG_DALI,
|
|
992
|
+
custom_config=custom_config,
|
|
993
|
+
)
|
|
994
|
+
|
|
995
|
+
|
|
996
|
+
# Case for device model is not relevant.
|
|
997
|
+
# HomeBrew (HB-) devices are always listed as HM-.
|
|
998
|
+
DEVICES: Mapping[str, CustomConfig | tuple[CustomConfig, ...]] = {
|
|
999
|
+
"263 132": CustomConfig(make_ce_func=make_rf_dimmer),
|
|
1000
|
+
"263 133": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1001
|
+
"263 134": CustomConfig(make_ce_func=make_rf_dimmer),
|
|
1002
|
+
"HBW-LC4-IN4-DR": CustomConfig(
|
|
1003
|
+
make_ce_func=make_rf_dimmer,
|
|
1004
|
+
channels=(
|
|
1005
|
+
5,
|
|
1006
|
+
6,
|
|
1007
|
+
7,
|
|
1008
|
+
8,
|
|
1009
|
+
),
|
|
1010
|
+
extended=ExtendedConfig(
|
|
1011
|
+
additional_data_points={
|
|
1012
|
+
1: (
|
|
1013
|
+
Parameter.PRESS_LONG,
|
|
1014
|
+
Parameter.PRESS_SHORT,
|
|
1015
|
+
Parameter.SENSOR,
|
|
1016
|
+
),
|
|
1017
|
+
2: (
|
|
1018
|
+
Parameter.PRESS_LONG,
|
|
1019
|
+
Parameter.PRESS_SHORT,
|
|
1020
|
+
Parameter.SENSOR,
|
|
1021
|
+
),
|
|
1022
|
+
3: (
|
|
1023
|
+
Parameter.PRESS_LONG,
|
|
1024
|
+
Parameter.PRESS_SHORT,
|
|
1025
|
+
Parameter.SENSOR,
|
|
1026
|
+
),
|
|
1027
|
+
4: (
|
|
1028
|
+
Parameter.PRESS_LONG,
|
|
1029
|
+
Parameter.PRESS_SHORT,
|
|
1030
|
+
Parameter.SENSOR,
|
|
1031
|
+
),
|
|
1032
|
+
}
|
|
1033
|
+
),
|
|
1034
|
+
),
|
|
1035
|
+
"HBW-LC-RGBWW-IN6-DR": (
|
|
1036
|
+
CustomConfig(
|
|
1037
|
+
make_ce_func=make_rf_dimmer,
|
|
1038
|
+
channels=(7, 8, 9, 10, 11, 12),
|
|
1039
|
+
extended=ExtendedConfig(
|
|
1040
|
+
additional_data_points={
|
|
1041
|
+
(
|
|
1042
|
+
1,
|
|
1043
|
+
2,
|
|
1044
|
+
3,
|
|
1045
|
+
4,
|
|
1046
|
+
5,
|
|
1047
|
+
6,
|
|
1048
|
+
): (
|
|
1049
|
+
Parameter.PRESS_LONG,
|
|
1050
|
+
Parameter.PRESS_SHORT,
|
|
1051
|
+
Parameter.SENSOR,
|
|
1052
|
+
)
|
|
1053
|
+
},
|
|
1054
|
+
),
|
|
1055
|
+
),
|
|
1056
|
+
CustomConfig(
|
|
1057
|
+
make_ce_func=make_rf_dimmer_color_fixed,
|
|
1058
|
+
channels=(13,),
|
|
1059
|
+
extended=ExtendedConfig(fixed_channels={15: {Field.COLOR: Parameter.COLOR}}),
|
|
1060
|
+
),
|
|
1061
|
+
CustomConfig(
|
|
1062
|
+
make_ce_func=make_rf_dimmer_color_fixed,
|
|
1063
|
+
channels=(14,),
|
|
1064
|
+
extended=ExtendedConfig(fixed_channels={16: {Field.COLOR: Parameter.COLOR}}),
|
|
1065
|
+
),
|
|
1066
|
+
),
|
|
1067
|
+
"HM-DW-WM": CustomConfig(make_ce_func=make_rf_dimmer, channels=(1, 2, 3, 4)),
|
|
1068
|
+
"HM-LC-AO-SM": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1069
|
+
"HM-LC-DW-WM": CustomConfig(make_ce_func=make_rf_dimmer_color_temp, channels=(1, 3, 5)),
|
|
1070
|
+
"HM-LC-Dim1L-CV": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1071
|
+
"HM-LC-Dim1L-CV-2": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1072
|
+
"HM-LC-Dim1L-Pl": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1073
|
+
"HM-LC-Dim1L-Pl-2": CustomConfig(make_ce_func=make_rf_dimmer),
|
|
1074
|
+
"HM-LC-Dim1L-Pl-3": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1075
|
+
"HM-LC-Dim1PWM-CV": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1076
|
+
"HM-LC-Dim1PWM-CV-2": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1077
|
+
"HM-LC-Dim1T-CV": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1078
|
+
"HM-LC-Dim1T-CV-2": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1079
|
+
"HM-LC-Dim1T-DR": CustomConfig(make_ce_func=make_rf_dimmer, channels=(1, 2, 3)),
|
|
1080
|
+
"HM-LC-Dim1T-FM": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1081
|
+
"HM-LC-Dim1T-FM-2": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1082
|
+
"HM-LC-Dim1T-FM-LF": CustomConfig(make_ce_func=make_rf_dimmer),
|
|
1083
|
+
"HM-LC-Dim1T-Pl": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1084
|
+
"HM-LC-Dim1T-Pl-2": CustomConfig(make_ce_func=make_rf_dimmer),
|
|
1085
|
+
"HM-LC-Dim1T-Pl-3": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1086
|
+
"HM-LC-Dim1TPBU-FM": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1087
|
+
"HM-LC-Dim1TPBU-FM-2": CustomConfig(make_ce_func=make_rf_dimmer_with_virt_channel),
|
|
1088
|
+
"HM-LC-Dim2L-CV": CustomConfig(make_ce_func=make_rf_dimmer, channels=(1, 2)),
|
|
1089
|
+
"HM-LC-Dim2L-SM": CustomConfig(make_ce_func=make_rf_dimmer, channels=(1, 2)),
|
|
1090
|
+
"HM-LC-Dim2L-SM-2": CustomConfig(make_ce_func=make_rf_dimmer, channels=(1, 2, 3, 4, 5, 6)),
|
|
1091
|
+
"HM-LC-Dim2T-SM": CustomConfig(make_ce_func=make_rf_dimmer, channels=(1, 2)),
|
|
1092
|
+
"HM-LC-Dim2T-SM-2": CustomConfig(make_ce_func=make_rf_dimmer, channels=(1, 2, 3, 4, 5, 6)),
|
|
1093
|
+
"HM-LC-RGBW-WM": CustomConfig(make_ce_func=make_rf_dimmer_color_effect),
|
|
1094
|
+
"HMW-LC-Dim1L-DR": CustomConfig(make_ce_func=make_rf_dimmer, channels=(3,)),
|
|
1095
|
+
"HSS-DX": CustomConfig(make_ce_func=make_rf_dimmer),
|
|
1096
|
+
"HmIP-DRG-DALI": CustomConfig(make_ce_func=make_ip_drg_dali_light, channels=tuple(range(1, 49))),
|
|
1097
|
+
"HmIP-BDT": CustomConfig(make_ce_func=make_ip_dimmer, channels=(4,)),
|
|
1098
|
+
"HmIP-BSL": CustomConfig(make_ce_func=make_ip_fixed_color_light, channels=(8, 12)),
|
|
1099
|
+
"HmIP-DRDI3": CustomConfig(
|
|
1100
|
+
make_ce_func=make_ip_dimmer,
|
|
1101
|
+
channels=(5, 9, 13),
|
|
1102
|
+
),
|
|
1103
|
+
"HmIP-FDT": CustomConfig(make_ce_func=make_ip_dimmer, channels=(2,)),
|
|
1104
|
+
"HmIP-PDT": CustomConfig(make_ce_func=make_ip_dimmer, channels=(3,)),
|
|
1105
|
+
"HmIP-RGBW": CustomConfig(make_ce_func=make_ip_rgbw_light),
|
|
1106
|
+
"HmIP-LSC": CustomConfig(make_ce_func=make_ip_rgbw_light),
|
|
1107
|
+
"HmIP-SCTH230": CustomConfig(
|
|
1108
|
+
make_ce_func=make_ip_dimmer,
|
|
1109
|
+
channels=(12,),
|
|
1110
|
+
extended=ExtendedConfig(
|
|
1111
|
+
additional_data_points={
|
|
1112
|
+
1: (Parameter.CONCENTRATION,),
|
|
1113
|
+
4: (
|
|
1114
|
+
Parameter.HUMIDITY,
|
|
1115
|
+
Parameter.ACTUAL_TEMPERATURE,
|
|
1116
|
+
),
|
|
1117
|
+
}
|
|
1118
|
+
),
|
|
1119
|
+
),
|
|
1120
|
+
"HmIP-WGT": CustomConfig(make_ce_func=make_ip_dimmer, channels=(2,)),
|
|
1121
|
+
"HmIPW-DRD3": CustomConfig(
|
|
1122
|
+
make_ce_func=make_ip_dimmer,
|
|
1123
|
+
channels=(2, 6, 10),
|
|
1124
|
+
),
|
|
1125
|
+
"HmIPW-WRC6": CustomConfig(make_ce_func=make_ip_simple_fixed_color_light_wired, channels=(7, 8, 9, 10, 11, 12, 13)),
|
|
1126
|
+
"OLIGO.smart.iq.HM": CustomConfig(make_ce_func=make_rf_dimmer, channels=(1, 2, 3, 4, 5, 6)),
|
|
1127
|
+
}
|
|
1128
|
+
hmed.ALL_DEVICES[DataPointCategory.LIGHT] = DEVICES
|