8000 Deprecate is_something attributes (#912) · python-kasa/python-kasa@ef49f44 · GitHub
[go: up one dir, main page]

Skip to content

Commit ef49f44

Browse files
authored
Deprecate is_something attributes (#912)
Deprecates the is_something attributes like is_bulb and is_dimmable in favour of the modular approach.
1 parent 33d8398 commit ef49f44

File tree

8 files changed

+142
-93
lines changed

8 files changed

+142
-93
lines changed

kasa/device.py

Lines changed: 51 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from dataclasses import dataclass
88
from datetime import datetime
99
from typing import TYPE_CHECKING, Any, Mapping, Sequence
10+
from warnings import warn
1011

1112
from .credentials import Credentials
1213
from .device_type import DeviceType
@@ -208,61 +209,6 @@ def get_child_device(self, id_: str) -> Device:
208209
def sys_info(self) -> dict[str, Any]:
209210
"""Returns the device info."""
210211

211-
@property
212-
def is_bulb(self) -> bool:
213-
"""Return True if the device is a bulb."""
214-
return self.device_type == DeviceType.Bulb
215-
216-
@property
217-
def is_light_strip(self) -> bool:
218-
"""Return True if the device is a led strip."""
219-
return self.device_type == DeviceType.LightStrip
220-
221-
@property
222-
def is_plug(self) -> bool:
223-
"""Return True if the device is a plug."""
224-
return self.device_type == DeviceType.Plug
225-
226-
@property
227-
def is_wallswitch(self) -> bool:
228-
"""Return True if the device is a switch."""
229-
return self.device_type == DeviceType.WallSwitch
230-
231-
@property
232-
def is_strip(self) -> bool:
233-
"""Return True if the device is a strip."""
234-
return self.device_type == DeviceType.Strip
235-
236-
@property
237-
def is_strip_socket(self) -> bool:
238-
"""Return True if the device is a strip socket."""
239-
return self.device_type == DeviceType.StripSocket
240-
241-
@property
242-
def is_dimmer(self) -> bool:
243-
"""Return True if the device is a dimmer."""
244-
return self.device_type == DeviceType.Dimmer
245-
246-
@property
247-
def is_dimmable(self) -> bool:
248-
"""Return True if the device is dimmable."""
249-
return False
250-
251-
@property
252-
def is_fan(self) -> bool:
253-
"""Return True if the device is a fan."""
254-
return self.device_type == DeviceType.Fan
255-
256-
@property
257-
def is_variable_color_temp(self) -> bool:
258-
"""Return True if the device supports color temperature."""
259-
return False
260-
261-
@property
262-
def is_color(self) -> bool:
263-
"""Return True if the device supports color changes."""
264-
return False
265-
266212
def get_plug_by_name(self, name: str) -> Device:
267213
"""Return child device for the given name."""
268214
for p in self.children:
@@ -383,3 +329,53 @@ def __repr__(self):
383329
if self._last_update is None:
384330
return f"<{self.device_type} at {self.host} - update() needed>"
385331
return f"<{self.device_type} at {self.host} - {self.alias} ({self.model})>"
332+
333+
_deprecated_attributes = {
334+
# is_type
335+
"is_bulb": (Module.Light, lambda self: self.device_type == DeviceType.Bulb),
336+
"is_dimmer": (
337+
Module.Light,
338+
lambda self: self.device_type == DeviceType.Dimmer,
339+
),
340+
"is_light_strip": (
341+
Module.LightEffect,
342+
lambda self: self.device_type == DeviceType.LightStrip,
343+
),
344+
"is_plug": (Module.Led, lambda self: self.device_type == DeviceType.Plug),
345+
"is_wallswitch": (
346+
Module.Led,
347+
lambda self: self.device_type == DeviceType.WallSwitch,
348+
),
349+
"is_strip": (None, lambda self: self.device_type == DeviceType.Strip),
350+
"is_strip_socket": (
351+
None,
352+
lambda self: self.device_type == DeviceType.StripSocket,
353+
), # TODO
354+
# is_light_function
355+
"is_color": (
356+
Module.Light,
357+
lambda self: Module.Light in self.modules
358+
and self.modules[Module.Light].is_color,
359+
),
360+
"is_dimmable": (
361+
Module.Light,
362+
lambda self: Module.Light in self.modules
363+
and self.modules[Module.Light].is_dimmable,
364+
),
365+
"is_variable_color_temp": (
366+
Module.Light,
367+
lambda self: Module.Light in self.modules
368+
and self.modules[Module.Light].is_variable_color_temp,
369+
),
370+
}
371+
372+
def __getattr__(self, name) -> bool:
373+
if name in self._deprecated_attributes:
374+
module = self._deprecated_attributes[name][0]
375+
func = self._deprecated_attributes[name][1]
376+
msg = f"{name} is deprecated"
377+
if module:
378+
msg += f", use: {module} in device.modules instead"
379+
warn(msg, DeprecationWarning, stacklevel=1)
380+
return func(self)
381+
raise AttributeError(f"Device has no attribute {name!r}")

kasa/iot/iotbulb.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -226,21 +226,21 @@ async def _initialize_modules(self):
226226

227227
@property # type: ignore
228228
@requires_update
229-
def is_color(self) -> bool:
229+
def _is_color(self) -> bool:
230230
"""Whether the bulb supports color changes."""
231231
sys_info = self.sys_info
232232
return bool(sys_info["is_color"])
233233

234234
@property # type: ignore
235235
@requires_update
236-
def is_dimmable(self) -> bool:
236+
def _is_dimmable(self) -> bool:
237237
"""Whether the bulb supports brightness changes."""
238238
sys_info = self.sys_info
239239
return bool(sys_info["is_dimmable"])
240240

241241
@property # type: ignore
242242
@requires_update
243-
def is_variable_color_temp(self) -> bool:
243+
def _is_variable_color_temp(self) -> bool:
244244
"""Whether the bulb supports color temperature changes."""
245245
sys_info = self.sys_info
246246
return bool(sys_info["is_variable_color_temp"])
@@ -252,7 +252,7 @@ def valid_temperature_range(self) -> ColorTempRange:
252252
253253
:return: White temperature range in Kelvin (minimum, maximum)
254254
"""
255-
if not self.is_variable_color_temp:
255+
if not self._is_variable_color_temp:
256256
raise KasaException("Color temperature not supported")
257257

258258
for model, temp_range in TPLINK_KELVIN.items():
@@ -352,7 +352,7 @@ def hsv(self) -> HSV:
352352
353353
:return: hue, saturation and value (degrees, %, %)
354354
"""
355-
if not self.is_color:
355+
if not self._is_color:
356356
raise KasaException("Bulb does not support color.")
357357

358358
light_state = cast(dict, self.light_state)
@@ -379,7 +379,7 @@ async def set_hsv(
379379
:param int value: value in percentage [0, 100]
380380
:param int transition: transition in milliseconds.
381381
"""
382-
if not self.is_color:
382+
if not self._is_color:
383383
raise KasaException("Bulb does not support color.")
384384

385385
if not isinstance(hue, int) or not (0 <= hue <= 360):
@@ -406,7 +406,7 @@ async def set_hsv(
406406
@requires_update
407407
def color_temp(self) -> int:
408408
"""Return color temperature of the device in kelvin."""
409-
if not self.is_variable_color_temp:
409+
if not self._is_variable_color_temp:
410410
raise KasaException("Bulb does not support colortemp.")
411411

412412
light_state = self.light_state
@@ -421,7 +421,7 @@ async def set_color_temp(
421421
:param int temp: The new color temperature, in Kelvin
422422
:param int transition: transition in milliseconds.
423423
"""
424-
if not self.is_variable_color_temp:
424+
if not self._is_variable_color_temp:
425425
raise KasaException("Bulb does not support colortemp.")
426426

427427
valid_temperature_range = self.valid_temperature_range
@@ -446,7 +446,7 @@ def _raise_for_invalid_brightness(self, value):
446446
@requires_update
447447
def brightness(self) -> int:
448448
"""Return the current brightness in percentage."""
449-
if not self.is_dimmable: # pragma: no cover
449+
if not self._is_dimmable: # pragma: no cover
450450
raise KasaException("Bulb is not dimmable.")
451451

452452
light_state = self.light_state
@@ -461,7 +461,7 @@ async def set_brightness(
461461
:param int brightness: brightness in percent
462462
:param int transition: transition in milliseconds.
463463
"""
464-
if not self.is_dimmable: # pragma: no cover
464+
if not self._is_dimmable: # pragma: no cover
465465
raise KasaException("Bulb is not dimmable.")
466466

467467
self._raise_for_invalid_brightness(brightness)

kasa/iot/iotdimmer.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ def brightness(self) -> int:
9696
9797
Will return a range between 0 - 100.
9898
"""
99-
if not self.is_dimmable:
99+
if not self._is_dimmable:
100100
raise KasaException("Device is not dimmable.")
101101

102102
sys_info = self.sys_info
@@ -109,7 +109,7 @@ async def set_brightness(self, brightness: int, *, transition: int | None = None
109109
:param int transition: transition duration in milliseconds.
110110
Using a transition will cause the dimmer to turn on.
111111
"""
112-
if not self.is_dimmable:
112+
if not self._is_dimmable:
113113
raise KasaException("Device is not dimmable.")
114114

115115
if not isinstance(brightness, int):
@@ -218,7 +218,7 @@ async def set_fade_time(self, fade_type: FadeType, time: int):
218218

219219
@property # type: ignore
220220
@requires_update
221-
def is_dimmable(self) -> bool:
221+
def _is_dimmable(self) -> bool:
222222
"""Whether the switch supports brightness changes."""
223223
sys_info = self.sys_info
224224
return "brightness" in sys_info

kasa/iot/modules/light.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from typing import TYPE_CHECKING, cast
66

7+
from ...device_type import DeviceType
78
from ...exceptions import KasaException
89
from ...feature import Feature
910
from ...interfaces.light import HSV, ColorTempRange
@@ -78,14 +79,19 @@ def query(self) -> dict:
7879
return {}
7980

8081
def _get_bulb_device(self) -> IotBulb | None:
81-
if self._device.is_bulb or self._device.is_light_strip:
82+
"""For type checker this gets an IotBulb.
83+
84+
IotDimmer is not a subclass of IotBulb and using isinstance
85+
here at runtime would create a circular import.
86+
"""
87+
if self._device.device_type in {DeviceType.Bulb, DeviceType.LightStrip}:
8288
return cast("IotBulb", self._device)
8389
return None
8490

8591
@property # type: ignore
8692
def is_dimmable(self) -> int:
8793
"""Whether the bulb supports brightness changes."""
88-
return self._device.is_dimmable
94+
return self._device._is_dimmable
8995

9096
@property # type: ignore
9197
def brightness(self) -> int:
@@ -107,14 +113,14 @@ def is_color(self) -> bool:
107113
"""Whether the light supports color changes."""
108114
if (bulb := self._get_bulb_device()) is None:
109115
return False
110-
return bulb.is_color
116+
return bulb._is_color
111117

112118
@property
113119
def is_variable_color_temp(self) -> bool:
114120
"""Whether the bulb supports color temperature changes."""
115121
if (bulb := self._get_bulb_device()) is None:
116122
return False
117-
return bulb.is_variable_color_temp
123+
return bulb._is_variable_color_temp
118124

119125
@property
120126
def has_effects(self) -> bool:
@@ -129,7 +135,7 @@ def hsv(self) -> HSV:
129135
130136
:return: hue, saturation and value (degrees, %, %)
131137
"""
132-
if (bulb := self._get_bulb_device()) is None or not bulb.is_color:
138+
if (bulb := self._get_bulb_device()) is None or not bulb._is_color:
133139
raise KasaException("Light does not support color.")
134140
return bulb.hsv
135141

@@ -150,7 +156,7 @@ async def set_hsv(
150156
:param int value: value in percentage [0, 100]
151157
:param int transition: transition in milliseconds.
152158
"""
153-
if (bulb := self._get_bulb_device()) is None or not bulb.is_color:
159+
if (bulb := self._get_bulb_device()) is None or not bulb._is_color:
154160
raise KasaException("Light does not support color.")
155161
return await bulb.set_hsv(hue, saturation, value, transition=transition)
156162

@@ -160,14 +166,18 @@ def valid_temperature_range(self) -> ColorTempRange:
160166
161167
:return: White temperature range in Kelvin (minimum, maximum)
162168
"""
163-
if (bulb := self._get_bulb_device()) is None or not bulb.is_variable_color_temp:
169+
if (
170+
bulb := self._get_bulb_device()
171+
) is None or not bulb._is_variable_color_temp:
164172
raise KasaException("Light does not support colortemp.")
165173
return bulb.valid_temperature_range
166174

167175
@property
168176
def color_temp(self) -> int:
169177
"""Whether the bulb supports color temperature changes."""
170-
if (bulb := self._get_bulb_device()) is None or not bulb.is_variable_color_temp:
178+
if (
179+
bulb := self._get_bulb_device()
180+
) is None or not bulb._is_variable_color_temp:
171181
raise KasaException("Light does not support colortemp.")
172182
return bulb.color_temp
173183

@@ -181,7 +191,9 @@ async def set_color_temp(
181191
:param int temp: The new color temperature, in Kelvin
182192
:param int transition: transition in milliseconds.
183193
"""
184-
if (bulb := self._get_bulb_device()) is None or not bulb.is_variable_color_temp:
194+
if (
195+
bulb := self._get_bulb_device()
196+
) is None or not bulb._is_variable_color_temp:
185197
raise KasaException("Light does not support colortemp.")
186198
return await bulb.set_color_temp(
187199
temp, brightness=brightness, transition=transition

kasa/smart/smartdevice.py

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from ..emeterstatus import EmeterStatus
1515
from ..exceptions import AuthenticationError, DeviceError, KasaException, SmartErrorCode
1616
from ..feature import Feature
17-
from ..interfaces.light import LightPreset
1817
from ..module import Module
1918
from ..modulemapping import ModuleMapping, ModuleName
2019
from ..smartprotocol import SmartProtocol
@@ -444,11 +443,6 @@ def has_emeter(self) -> bool:
444443
"""Return if the device has emeter."""
445444
return Module.Energy in self.modules
446445

447-
@property
448-
def is_dimmer(self) -> bool:
449-
"""Whether the device acts as a dimmer."""
450-
return self.is_dimmable
451-
452446
@property
453447
def is_on(self) -> bool:
454448
"""Return true if the device is on."""
@@ -648,8 +642,3 @@ def _get_device_type_from_components(
648642
return DeviceType.Thermostat
649643
_LOGGER.warning("Unknown device type, falling back to plug")
650644
return DeviceType.Plug
651-
652-
@property
653-
def presets(self) -> list[LightPreset]:
654-
"""Return a list of available bulb setting presets."""
655-
return []

kasa/tests/smart/modules/test_fan.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ async def test_fan_module(dev: SmartDevice, mocker: MockerFixture):
5858
fan = dev.modules.get(Module.Fan)
5959
assert fan
6060
device = fan._device
61-
assert device.is_fan
6261

6362
await fan.set_fan_speed_level(1)
6463
await dev.update()

kasa/tests/test_bulb.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ async def test_non_variable_temp(dev: Device):
208208
async def test_dimmable_brightness(dev: IotBulb, turn_on):
209209
assert isinstance(dev, (IotBulb, IotDimmer))
210210
await handle_turn_on(dev, turn_on)
211-
assert dev.is_dimmable
211+
assert dev._is_dimmable
212212

213213
await dev.set_brightness(50)
214214
await dev.update()
@@ -244,7 +244,7 @@ async def test_dimmable_brightness_transition(dev: IotBulb, mocker):
244244

245245
@dimmable_iot
246246
async def test_invalid_brightness(dev: IotBulb):
247-
assert dev.is_dimmable
247+
assert dev._is_dimmable
248248

249249
with pytest.raises(ValueError):
250250
await dev.set_brightness(110)
@@ -255,7 +255,7 @@ async def test_invalid_brightness(dev: IotBulb):
255255

256256
@non_dimmable_iot
257257
async def test_non_dimmable(dev: IotBulb):
258-
assert not dev.is_dimmable
258+
assert not dev._is_dimmable
259259

260260
with pytest.raises(KasaException):
261261
assert dev.brightness == 0

0 commit comments

Comments
 (0)
0