3
3
from __future__ import annotations
4
4
5
5
import logging
6
+ from datetime import timedelta
7
+ from typing import Any , Final , cast
6
8
7
9
from ...exceptions import KasaException
8
10
from ...feature import Feature
11
13
_LOGGER = logging .getLogger (__name__ )
12
14
13
15
16
+ def _td_to_ms (td : timedelta ) -> int :
17
+ """
18
+ Convert timedelta to integer milliseconds.
19
+
20
+ Uses default float to integer rounding.
21
+ """
22
+ return int (td / timedelta (milliseconds = 1 ))
23
+
24
+
14
25
class Dimmer (IotModule ):
15
26
"""Implements the dimmer config module."""
16
27
28
+ THRESHOLD_ABS_MIN : Final [int ] = 0
29
+ THRESHOLD_ABS_MAX : Final [int ] = (
30
+ 51 # Strange value, but verified against hardware (KS220).
31
+ )
32
+ FADE_TIME_ABS_MIN : Final [timedelta ] = timedelta (seconds = 0 )
33
+ FADE_TIME_ABS_MAX : Final [timedelta ] = timedelta (seconds = 10 ) # Completely arbitrary.
34
+ GENTLE_TIME_ABS_MIN : Final [timedelta ] = timedelta (seconds = 0 )
35
+ GENTLE_TIME_ABS_MAX : Final [timedelta ] = timedelta (
36
+ seconds = 120
37
+ ) # Completely arbitrary.
38
+ RAMP_RATE_ABS_MIN : Final [int ] = 10 # Verified against KS220.
39
+ RAMP_RATE_ABS_MAX : Final [int ] = 50 # Verified against KS220.
40
+
17
41
def _initialize_features (self ) -> None :
18
42
"""Initialize features after the initial update."""
19
43
# Only add features if the device supports the module
@@ -25,11 +49,12 @@ def _initialize_features(self) -> None:
25
49
device = self ._device ,
26
50
container = self ,
27
51
id = "dimmer_threshold_min" ,
28
- name = "Minimum Dimming Level " ,
52
+ name = "Minimum dimming level " ,
29
53
icon = "mdi:lightbulb-on-20" ,
30
54
attribute_getter = "threshold_min" ,
31
55
attribute_setter = "set_threshold_min" ,
32
- type = Feature .Type .Switch ,
56
+ range_getter = lambda : (self .THRESHOLD_ABS_MIN , self .THRESHOLD_ABS_MAX ),
57
+ type = Feature .Type .Number ,
33
58
category = Feature .Category .Config ,
34
59
)
35
60
)
@@ -39,11 +64,15 @@ def _initialize_features(self) -> None:
39
64
device = self ._device ,
40
65
container = self ,
41
66
id = "dimmer_fade_off_time" ,
42
- name = "Dimmer Fade Off Time " ,
67
+ name = "Dimmer fade off time " ,
43
68
icon = "mdi:clock-in" ,
44
69
attribute_getter = "fade_off_time" ,
45
70
attribute_setter = "set_fade_off_time" ,
46
- type = Feature .Type .Switch ,
71
+ range_getter = lambda : (
72
+ _td_to_ms (self .FADE_TIME_ABS_MIN ),
73
+ _td_to_ms (self .FADE_TIME_ABS_MAX ),
74
+ ),
75
+ type = Feature .Type .Number ,
47
76
category = Feature .Category .Config ,
48
77
)
49
78
)
@@ -53,11 +82,15 @@ def _initialize_features(self) -> None:
53
82
device = self ._device ,
54
83
container = self ,
55
84
id = "dimmer_fade_on_time" ,
56
- name = "Dimmer Fade On Time " ,
85
+ name = "Dimmer fade on time " ,
57
86
icon = "mdi:clock-out" ,
58
87
attribute_getter = "fade_on_time" ,
59
88
attribute_setter = "set_fade_on_time" ,
60
- type = Feature .Type .Switch ,
89
+ range_getter = lambda : (
90
+ _td_to_ms (self .FADE_TIME_ABS_MIN ),
91
+ _td_to_ms (self .FADE_TIME_ABS_MAX ),
92
+ ),
93
+ type = Feature .Type .Number ,
61
94
category = Feature .Category .Config ,
62
95
)
<
17AE
code>63 96
)
@@ -67,11 +100,15 @@ def _initialize_features(self) -> None:
67
100
device = self ._device ,
68
101
container = self ,
69
102
id = "dimmer_gentle_off_time" ,
70
- name = "Dimmer Gentle Off Time " ,
103
+ name = "Dimmer gentle off time " ,
71
104
icon = "mdi:clock-in" ,
72
105
attribute_getter = "gentle_off_time" ,
73
106
attribute_setter = "set_gentle_off_time" ,
74
- type = Feature .Type .Switch ,
107
+ range_getter = lambda : (
108
+ _td_to_ms (self .GENTLE_TIME_ABS_MIN ),
109
+ _td_to_ms (self .GENTLE_TIME_ABS_MAX ),
110
+ ),
111
+ type = Feature .Type .Number ,
75
112
category = Feature .Category .Config ,
76
113
)
77
114
)
@@ -81,11 +118,15 @@ def _initialize_features(self) -> None:
81
118
device = self ._device ,
82
119
container = self ,
83
120
id = "dimmer_gentle_on_time" ,
84
- name = "Dimmer Gentle On Time " ,
121
+ name = "Dimmer gentle on time " ,
85
122
icon = "mdi:clock-out" ,
86
123
attribute_getter = "gentle_on_time" ,
87
124
attribute_setter = "set_gentle_on_time" ,
88
- type = Feature .Type .Switch ,
125
+ range_getter = lambda : (
126
+ _td_to_ms (self .GENTLE_TIME_ABS_MIN ),
127
+ _td_to_ms (self .GENTLE_TIME_ABS_MAX ),
128
+ ),
129
+ type = Feature .Type .Number ,
89
130
category = Feature .Category .Config ,
90
131
)
91
132
)
@@ -95,11 +136,12 @@ def _initialize_features(self) -> None:
95
136
device = self ._device ,
96
137
container = self ,
97
138
id = "dimmer_ramp_rate" ,
98
- name = "Dimmer Ramp Rate " ,
139
+ name = "Dimmer ramp rate " ,
99
140
icon = "mdi:clock-fast" ,
100
141
attribute_getter = "ramp_rate" ,
101
142
attribute_setter = "set_ramp_rate" ,
102
- type = Feature .Type .Switch ,
143
+ range_getter =lambda : (self .RAMP_RATE_ABS_MIN , self .RAMP_RATE_ABS_MAX ),
144
+ type = Feature .Type .Number ,
103
145
category = Feature .Category .Config ,
104
146
)
105
147
)
@@ -114,16 +156,14 @@ def query(self) -> dict:
114
156
return req
115
157
116
158
@property
117
- def config (self ) -> dict :
159
+ def config (self ) -> dict [ str , Any ] :
118
160
"""Return current configuration."""
119
161
return self .data ["get_dimmer_parameters" ]
120
162
121
163
@property
122
- def threshold_min (self ) -> int | None :
164
+ def threshold_min (self ) -> int :
123
165
"""Return the minimum dimming level for this dimmer."""
124
- if (min := self .config .get ("minThreshold" )) is not None :
125
- return int (min )
126
- return None
166
+ return self .config ["minThreshold" ]
127
167
128
168
async def set_threshold_min (self , min : int ) -> dict :
129
169
"""
@@ -133,113 +173,107 @@ async def set_threshold_min(self, min: int) -> dict:
133
173
134
174
:param min: The minimum dimming level, in the range 0-51.
135
175
"""
136
- if (min < 0 ) or (min > 51 ):
176
+ if (min < self . THRESHOLD_ABS_MIN ) or (min > self . THRESHOLD_ABS_MAX ):
137
177
raise KasaException (
138
- "Minimum dimming threshold is outside the supported range: 0-51."
178
+ "Minimum dimming threshold is outside the supported range: "
179
+ f"{ self .THRESHOLD_ABS_MIN } -{ self .THRESHOLD_ABS_MAX } "
139
180
)
140
181
return await self .call ("calibrate_brightness" , {"minThreshold" : min })
141
182
142
183
@property
143
- def fade_off_time (self ) -> int | None :
184
+ def fade_off_time (self ) -> timedelta :
144
185
"""Return the fade off animation duration."""
145
- if (fade_time := self .config .get ("fadeOffTime" )) is not None :
146
- return int (fade_time )
147
- return None
186
+ return timedelta (milliseconds = cast (int , self .config ["fadeOffTime" ]))
148
187
149
- async def set_fade_off_time (self , time : int ) -> dict :
188
+ async def set_fade_off_time (self , time : int | timedelta ) -> dict :
150
189
"""
151
190
Set the duration of the fade off animation.
152
191
153
192
:param time: The animation duration, in ms.
154
193
"""
155
- if (time < 0 ) or ( time > 10_000 ):
156
- # FYI: Not sure if there is really a max bound here,
157
- # but anything above 10s seems ridiculous.
194
+ if isinstance (time , int ):
195
+ time = timedelta ( milliseconds = time )
196
+ if ( time < self . FADE_TIME_ABS_MIN ) or ( time > self . FADE_TIME_ABS_MAX ):
158
197
raise KasaException (
159
- "Fade time is outside the bounds of the supported range: 0-10,000."
198
+ "Fade time is outside the bounds of the supported range:"
199
+ f"{ self .FADE_TIME_ABS_MIN } -{ self .FADE_TIME_ABS_MAX } "
160
200
)
161
- return await self .call ("set_fade_on_time" , {"fadeTime" : time })
201
+ return await self .call ("set_fade_on_time" , {"fadeTime" : _td_to_ms ( time ) })
162
202
163
203
@property
164
- def fade_on_time (self ) -> int | None :
204
+ def fade_on_time (self ) -> timedelta :
165
205
"""Return the fade on animation duration."""
166
- if (fade_time := self .config .get ("fadeOnTime" )) is not None :
167
- return int (fade_time )
168
- return None
206
+ return timedelta (milliseconds = cast (int , self .config ["fadeOnTime" ]))
169
207
170
- async def set_fade_on_time (self , time : int ) -> dict :
208
+ async def set_fade_on_time (self , time : int | timedelta ) -> dict :
171
209
"""
172
210
Set the duration of the fade on animation.
173
211
174
212
:param time: The animation duration, in ms.
175
213
"""
176
- if (time < 0 ) or ( time > 10_000 ):
177
- # FYI: Not sure if there is really a max bound here,
178
- # but anything above 10s seems ridiculous.
214
+ if isinstance (time , int ):
215
+ time = timedelta ( milliseconds = time )
216
+ if ( time < self . FADE_TIME_ABS_MIN ) or ( time > self . FADE_TIME_ABS_MAX ):
179
217
raise KasaException (
180
- "Fade time is outside the bounds of the supported range: 0-10,000."
218
+ "Fade time is outside the bounds of the supported range:"
219
+ f"{ self .FADE_TIME_ABS_MIN } -{ self .FADE_TIME_ABS_MAX } "
181
220
)
182
- return await self .call ("set_fade_on_time" , {"fadeTime" : time })
221
+ return await self .call ("set_fade_on_time" , {"fadeTime" : _td_to_ms ( time ) })
183
222
184
223
@property
185
- def gentle_off_time (self ) -> int | None :
224
+ def gentle_off_time (self ) -> timedelta :
186
225
"""Return the gentle fade off animation duration."""
187
- if (duration := self .config .get ("gentleOffTime" )) is not None :
188
- return int (duration )
189
- return None
226
+ return timedelta (milliseconds = cast (int , self .config ["gentleOffTime" ]))
190
227
191
- async def set_gentle_off_time (self , time : int ) -> dict :
228
+ async def set_gentle_off_time (self , time : int | timedelta ) -> dict :
192
229
"""
193
230
Set the duration of the gentle fade off animation.
194
231
195
232
:param time: The animation duration, in ms.
196
233
"""
197
- if (time < 0 ) or ( time > 100_000 ):
198
- # FYI: Not sure if there is really a max bound here,
199
- # but anything above 100s seems ridiculous.
234
+ if isinstance (time , int ):
235
+ time = timedelta ( milliseconds = time )
236
+ if ( time < self . GENTLE_TIME_ABS_MIN ) or ( time > self . GENTLE_TIME_ABS_MAX ):
200
237
raise KasaException (
201
238
"Gentle off time is outside the bounds of the supported range: "
202
- "0-100,000 ."
239
+ f" { self . GENTLE_TIME_ABS_MIN } - { self . GENTLE_TIME_ABS_MAX } ."
203
240
)
204
- return await self .call ("set_gentle_off_time" , {"duration" : time })
241
+ return await self .call ("set_gentle_off_time" , {"duration" : _td_to_ms ( time ) })
205
242
206
243
@property
207
- def gentle_on_time (self ) -> int | None :
244
+ def gentle_on_time (self ) -> timedelta :
208
245
"""Return the gentle fade on animation duration."""
209
- if (duration := self .config .get ("gentleOnTime" )) is not None :
210
- return int (duration )
211
- return None
246
+ return timedelta (milliseconds = cast (int , self .config ["gentleOnTime" ]))
212
247
213
- async def set_gentle_on_time (self , time : int ) -> dict :
248
+ async def set_gentle_on_time (self , time : int | timedelta ) -> dict :
214
249
"""
215
250
Set the duration of the gentle fade on animation.
216
251
217
252
:param time: The animation duration, in ms.
218
253
"""
219
- if (time < 0 ) or ( time > 100_000 ):
220
- # FYI: Not sure if there is really a max bound here,
221
- # but anything above 100s seems ridiculous.
254
+ if isinstance (time , int ):
255
+ time = timedelta ( milliseconds = time )
256
+ if ( time < self . GENTLE_TIME_ABS_MIN ) or ( time > self . GENTLE_TIME_ABS_MAX ):
222
257
raise KasaException (
223
258
"Gentle off time is outside the bounds of the supported range: "
224
- "0-100,000 ."
259
+ f" { self . GENTLE_TIME_ABS_MIN } - { self . GENTLE_TIME_ABS_MAX } ."
225
260
)
226
- return await self .call ("set_gentle_on_time" , {"duration" : time })
261
+ return await self .call ("set_gentle_on_time" , {"duration" : _td_to_ms ( time ) })
227
262
228
263
@property
229
- def ramp_rate (self ) -> int | None :
264
+ def ramp_rate (self ) -> int :
230
265
"""Return the rate that the dimmer buttons increment the dimmer level."""
231
- if (rate := self .config .get ("rampRate" )) is not None :
232
- return int (rate )
233
- return None
266
+ return self .config ["rampRate" ]
234
267
235
268
async def set_ramp_rate (self , rate : int ) -> dict :
236
269
"""
237
270
Set how quickly to ramp the dimming level when using the dimmer buttons.
238
271
239
272
:param rate: The rate to increment the dimming level with each press.
240
273
"""
241
- if (rate < 10 ) or (rate > 50 ):
274
+ if (rate < self . RAMP_RATE_ABS_MIN ) or (rate > self . RAMP_RATE_ABS_MAX ):
242
275
raise KasaException (
243
- "Gentle off time is outside the bounds of the supported range: 10-50"
276
+ "Gentle off time is outside the bounds of the supported range:"
277
+ f"{ self .RAMP_RATE_ABS_MIN } -{ self .RAMP_RATE_ABS_MAX } "
244
278
)
245
279
return await self .call ("set_button_ramp_rate" , {"rampRate" : rate })
0 commit comments