Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Deprecate device level light, effect and led attributes #916

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 63 additions & 42 deletions kasa/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from .xortransport import XorTransport

if TYPE_CHECKING:
from .modulemapping import ModuleMapping
from .modulemapping import ModuleMapping, ModuleName


@dataclass
Expand Down Expand Up @@ -330,52 +330,73 @@ def __repr__(self):
return f"<{self.device_type} at {self.host} - update() needed>"
return f"<{self.device_type} at {self.host} - {self.alias} ({self.model})>"

_deprecated_attributes = {
_deprecated_device_type_attributes = {
# is_type
"is_bulb": (Module.Light, lambda self: self.device_type == DeviceType.Bulb),
"is_dimmer": (
Module.Light,
lambda self: self.device_type == DeviceType.Dimmer,
),
"is_light_strip": (
Module.LightEffect,
lambda self: self.device_type == DeviceType.LightStrip,
),
"is_plug": (Module.Led, lambda self: self.device_type == DeviceType.Plug),
"is_wallswitch": (
Module.Led,
lambda self: self.device_type == DeviceType.WallSwitch,
),
"is_strip": (None, lambda self: self.device_type == DeviceType.Strip),
"is_strip_socket": (
None,
lambda self: self.device_type == DeviceType.StripSocket,
), # TODO
# is_light_function
"is_color": (
Module.Light,
lambda self: Module.Light in self.modules
and self.modules[Module.Light].is_color,
),
"is_dimmable": (
Module.Light,
lambda self: Module.Light in self.modules
and self.modules[Module.Light].is_dimmable,
),
"is_variable_color_temp": (
Module.Light,
lambda self: Module.Light in self.modules
and self.modules[Module.Light].is_variable_color_temp,
),
"is_bulb": (Module.Light, DeviceType.Bulb),
"is_dimmer": (Module.Light, DeviceType.Dimmer),
"is_light_strip": (Module.LightEffect, DeviceType.LightStrip),
"is_plug": (Module.Led, DeviceType.Plug),
"is_wallswitch": (Module.Led, DeviceType.WallSwitch),
"is_strip": (None, DeviceType.Strip),
"is_strip_socket": (None, DeviceType.StripSocket),
}

def __getattr__(self, name) -> bool:
if name in self._deprecated_attributes:
module = self._deprecated_attributes[name][0]
func = self._deprecated_attributes[name][1]
def _get_replacing_attr(self, module_name: ModuleName, *attrs):
if module_name not in self.modules:
return None

for attr in attrs:
if hasattr(self.modules[module_name], attr):
return getattr(self.modules[module_name], attr)

return None

_deprecated_other_attributes = {
# light attributes
"is_color": (Module.Light, ["is_color"]),
"is_dimmable": (Module.Light, ["is_dimmable"]),
"is_variable_color_temp": (Module.Light, ["is_variable_color_temp"]),
"brightness": (Module.Light, ["brightness"]),
"set_brightness": (Module.Light, ["set_brightness"]),
"hsv": (Module.Light, ["hsv"]),
"set_hsv": (Module.Light, ["set_hsv"]),
"color_temp": (Module.Light, ["color_temp"]),
"set_color_temp": (Module.Light, ["set_color_temp"]),
"valid_temperature_range": (Module.Light, ["valid_temperature_range"]),
"has_effects": (Module.Light, ["has_effects"]),
# led attributes
"led": (Module.Led, ["led"]),
"set_led": (Module.Led, ["set_led"]),
# light effect attributes
# The return values for effect is a str instead of dict so the lightstrip
# modules have a _deprecated method to return the value as before.
"effect": (Module.LightEffect, ["_deprecated_effect", "effect"]),
# The return values for effect_list includes the Off effect so the lightstrip
# modules have a _deprecated method to return the values as before.
"effect_list": (Module.LightEffect, ["_deprecated_effect_list", "effect_list"]),
"set_effect": (Module.LightEffect, ["set_effect"]),
"set_custom_effect": (Module.LightEffect, ["set_custom_effect"]),
}

def __getattr__(self, name):
# is_device_type
if dep_device_type_attr := self._deprecated_device_type_attributes.get(name):
module = dep_device_type_attr[0]
msg = f"{name} is deprecated"
if module:
msg += f", use: {module} in device.modules instead"
warn(msg, DeprecationWarning, stacklevel=1)
return func(self)
return self.device_type == dep_device_type_attr[1]
# Other deprecated attributes
if (dep_attr := self._deprecated_other_attributes.get(name)) and (
(replacing_attr := self._get_replacing_attr(dep_attr[0], *dep_attr[1]))
is not None
):
module_name = dep_attr[0]
msg = (
f"{name} is deprecated, use: "
+ f"Module.{module_name} in device.modules instead"
)
warn(msg, DeprecationWarning, stacklevel=1)
return replacing_attr
raise AttributeError(f"Device has no attribute {name!r}")
16 changes: 8 additions & 8 deletions kasa/iot/iotbulb.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ def _is_variable_color_temp(self) -> bool:

@property # type: ignore
@requires_update
def valid_temperature_range(self) -> ColorTempRange:
def _valid_temperature_range(self) -> ColorTempRange:
"""Return the device-specific white temperature range (in Kelvin).

:return: White temperature range in Kelvin (minimum, maximum)
Expand Down Expand Up @@ -284,7 +284,7 @@ def light_state(self) -> dict[str, str]:

@property # type: ignore
@requires_update
def has_effects(self) -> bool:
def _has_effects(self) -> bool:
"""Return True if the device supports effects."""
return "lighting_effect_state" in self.sys_info

Expand Down Expand Up @@ -347,7 +347,7 @@ async def set_light_state(

@property # type: ignore
@requires_update
def hsv(self) -> HSV:
def _hsv(self) -> HSV:
"""Return the current HSV state of the bulb.

:return: hue, saturation and value (degrees, %, %)
Expand All @@ -364,7 +364,7 @@ def hsv(self) -> HSV:
return HSV(hue, saturation, value)

@requires_update
async def set_hsv(
async def _set_hsv(
self,
hue: int,
saturation: int,
Expand Down Expand Up @@ -404,7 +404,7 @@ async def set_hsv(

@property # type: ignore
@requires_update
def color_temp(self) -> int:
def _color_temp(self) -> int:
"""Return color temperature of the device in kelvin."""
if not self._is_variable_color_temp:
raise KasaException("Bulb does not support colortemp.")
Expand All @@ -413,7 +413,7 @@ def color_temp(self) -> int:
return int(light_state["color_temp"])

@requires_update
async def set_color_temp(
async def _set_color_temp(
self, temp: int, *, brightness=None, transition: int | None = None
) -> dict:
"""Set the color temperature of the device in kelvin.
Expand Down Expand Up @@ -444,7 +444,7 @@ def _raise_for_invalid_brightness(self, value):

@property # type: ignore
@requires_update
def brightness(self) -> int:
def _brightness(self) -> int:
"""Return the current brightness in percentage."""
if not self._is_dimmable: # pragma: no cover
raise KasaException("Bulb is not dimmable.")
Expand All @@ -453,7 +453,7 @@ def brightness(self) -> int:
return int(light_state["brightness"])

@requires_update
async def set_brightness(
async def _set_brightness(
self, brightness: int, *, transition: int | None = None
) -> dict:
"""Set the brightness in percentage.
Expand Down
14 changes: 12 additions & 2 deletions kasa/iot/iotdimmer.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ async def _initialize_modules(self):

@property # type: ignore
@requires_update
def brightness(self) -> int:
def _brightness(self) -> int:
"""Return current brightness on dimmers.

Will return a range between 0 - 100.
Expand All @@ -103,7 +103,7 @@ def brightness(self) -> int:
return int(sys_info["brightness"])

@requires_update
async def set_brightness(self, brightness: int, *, transition: int | None = None):
async def _set_brightness(self, brightness: int, *, transition: int | None = None):
"""Set the new dimmer brightness level in percentage.

:param int transition: transition duration in milliseconds.
Expand Down Expand Up @@ -222,3 +222,13 @@ def _is_dimmable(self) -> bool:
"""Whether the switch supports brightness changes."""
sys_info = self.sys_info
return "brightness" in sys_info

@property
def _is_variable_color_temp(self) -> bool:
"""Whether the device supports variable color temp."""
return False

@property
def _is_color(self) -> bool:
"""Whether the device supports color."""
return False
68 changes: 1 addition & 67 deletions kasa/iot/iotlightstrip.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@
from ..deviceconfig import DeviceConfig
from ..module import Module
from ..protocol import BaseProtocol
from .effects import EFFECT_NAMES_V1
from .iotbulb import IotBulb
from .iotdevice import KasaException, requires_update
from .iotdevice import requires_update
from .modules.lighteffect import LightEffect


Expand Down Expand Up @@ -70,68 +69,3 @@ async def _initialize_modules(self):
def length(self) -> int:
"""Return length of the strip."""
return self.sys_info["length"]

@property # type: ignore
@requires_update
def effect(self) -> dict:
"""Return effect state.

Example:
{'brightness': 50,
'custom': 0,
'enable': 0,
'id': '',
'name': ''}
"""
# LightEffectModule returns the current effect name
# so return the dict here for backwards compatibility
return self.sys_info["lighting_effect_state"]

@property # type: ignore
@requires_update
def effect_list(self) -> list[str] | None:
"""Return built-in effects list.

Example:
['Aurora', 'Bubbling Cauldron', ...]
"""
# LightEffectModule returns effect names along with a LIGHT_EFFECTS_OFF value
# so return the original effect names here for backwards compatibility
return EFFECT_NAMES_V1 if self.has_effects else None

@requires_update
async def set_effect(
self,
effect: str,
*,
brightness: int | None = None,
transition: int | None = None,
) -> None:
"""Set an effect on the device.

If brightness or transition is defined,
its value will be used instead of the effect-specific default.

See :meth:`effect_list` for available effects,
or use :meth:`set_custom_effect` for custom effects.

:param str effect: The effect to set
:param int brightness: The wanted brightness
:param int transition: The wanted transition time
"""
await self.modules[Module.LightEffect].set_effect(
effect, brightness=brightness, transition=transition
)

@requires_update
async def set_custom_effect(
self,
effect_dict: dict,
) -> None:
"""Set a custom effect on the device.

:param str effect_dict: The custom effect dict to set
"""
if not self.has_effects:
raise KasaException("Bulb does not support effects.")
await self.modules[Module.LightEffect].set_custom_effect(effect_dict)
10 changes: 0 additions & 10 deletions kasa/iot/iotplug.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,6 @@ async def turn_off(self, **kwargs):
"""Turn the switch off."""
return await self._query_helper("system", "set_relay_state", {"state": 0})

@property # type: ignore
@requires_update
def led(self) -> bool:
"""Return the state of the led."""
return self.modules[Module.Led].led

async def set_led(self, state: bool):
"""Set the state of the led (night mode)."""
return await self.modules[Module.Led].set_led(state)


class IotWallSwitch(IotPlug):
"""Representation of a TP-Link Smart Wall Switch."""
Expand Down
11 changes: 0 additions & 11 deletions kasa/iot/iotstrip.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,17 +147,6 @@ def on_since(self) -> datetime | None:

return max(plug.on_since for plug in self.children if plug.on_since is not None)

@property # type: ignore
@requires_update
def led(self) -> bool:
"""Return the state of the led."""
sys_info = self.sys_info
return bool(1 - sys_info["led_off"])

async def set_led(self, state: bool):
"""Set the state of the led (night mode)."""
await self._query_helper("system", "set_led_off", {"off": int(not state)})

async def current_consumption(self) -> float:
"""Get the current power consumption in watts."""
return sum([await plug.current_consumption() for plug in self.children])
Expand Down
Loading