Merge remote-tracking branch 'upstream/master' into feat/light_module_feats

This commit is contained in:
Steven B
2024-12-23 09:46:11 +00:00
216 changed files with 11945 additions and 2156 deletions

View File

@@ -22,7 +22,7 @@ from datetime import datetime, timedelta, tzinfo
from typing import TYPE_CHECKING, Any, cast
from warnings import warn
from ..device import Device, WifiNetwork, _DeviceInfo
from ..device import Device, DeviceInfo, WifiNetwork
from ..device_type import DeviceType
from ..deviceconfig import DeviceConfig
from ..exceptions import KasaException
@@ -43,7 +43,7 @@ def requires_update(f: Callable) -> Any:
@functools.wraps(f)
async def wrapped(*args: Any, **kwargs: Any) -> Any:
self = args[0]
if self._last_update is None and (
if not self._last_update and (
self._sys_info is None or f.__name__ not in self._sys_info
):
raise KasaException("You need to await update() to access the data")
@@ -54,7 +54,7 @@ def requires_update(f: Callable) -> Any:
@functools.wraps(f)
def wrapped(*args: Any, **kwargs: Any) -> Any:
self = args[0]
if self._last_update is None and (
if not self._last_update and (
self._sys_info is None or f.__name__ not in self._sys_info
):
raise KasaException("You need to await update() to access the data")
@@ -112,7 +112,7 @@ class IotDevice(Device):
>>> dev.alias
Bedroom Lamp Plug
>>> dev.model
HS110(EU)
HS110
>>> dev.rssi
-71
>>> dev.mac
@@ -310,7 +310,7 @@ class IotDevice(Device):
# If this is the initial update, check only for the sysinfo
# This is necessary as some devices crash on unexpected modules
# See #105, #120, #161
if self._last_update is None:
if not self._last_update:
_LOGGER.debug("Performing the initial update to obtain sysinfo")
response = await self.protocol.query(req)
self._last_update = response
@@ -452,7 +452,9 @@ class IotDevice(Device):
# This allows setting of some info properties directly
# from partial discovery info that will then be found
# by the requires_update decorator
self._set_sys_info(info)
discovery_model = info["device_model"]
no_region_model, _, _ = discovery_model.partition("(")
self._set_sys_info({**info, "model": no_region_model})
def _set_sys_info(self, sys_info: dict[str, Any]) -> None:
"""Set sys_info."""
@@ -471,18 +473,13 @@ class IotDevice(Device):
"""
return self._sys_info # type: ignore
@property # type: ignore
@requires_update
def model(self) -> str:
"""Return device model."""
sys_info = self._sys_info
return str(sys_info["model"])
@property
@requires_update
def _model_region(self) -> str:
"""Return device full model name and region."""
return self.model
def model(self) -> str:
"""Returns the device model."""
if self._last_update:
return self.device_info.short_name
return self._sys_info["model"]
@property # type: ignore
def alias(self) -> str | None:
@@ -748,7 +745,7 @@ class IotDevice(Device):
@staticmethod
def _get_device_info(
info: dict[str, Any], discovery_info: dict[str, Any] | None
) -> _DeviceInfo:
) -> DeviceInfo:
"""Get model information for a device."""
sys_info = _extract_sys_info(info)
@@ -766,7 +763,7 @@ class IotDevice(Device):
firmware_version, firmware_build = fw_version_full.split(" ", maxsplit=1)
auth = bool(discovery_info and ("mgt_encrypt_schm" in discovery_info))
return _DeviceInfo(
return DeviceInfo(
short_name=long_name,
long_name=long_name,
brand="kasa",

View File

@@ -207,17 +207,18 @@ class Light(IotModule, LightInterface):
return self._light_state
async def _post_update_hook(self) -> None:
if self._device.is_on is False:
device = self._device
if device.is_on is False:
state = LightState(light_on=False)
else:
state = LightState(light_on=True)
if self._device._is_dimmable:
if device._is_dimmable:
state.brightness = self.brightness
if self._device._is_color:
if device._is_color:
hsv = self.hsv
state.hue = hsv.hue
state.saturation = hsv.saturation
if self._device._is_variable_color_temp:
if device._is_variable_color_temp:
state.color_temp = self.color_temp
self._light_state = state

View File

@@ -85,17 +85,19 @@ class LightPreset(IotModule, LightPresetInterface):
def preset(self) -> str:
"""Return current preset name."""
light = self._device.modules[Module.Light]
is_color = light.has_feature("hsv")
is_variable_color_temp = light.has_feature("color_temp")
brightness = light.brightness
color_temp = light.color_temp if light.is_variable_color_temp else None
h, s = (light.hsv.hue, light.hsv.saturation) if light.is_color else (None, None)
color_temp = light.color_temp if is_variable_color_temp else None
h, s = (light.hsv.hue, light.hsv.saturation) if is_color else (None, None)
for preset_name, preset in self._presets.items():
if (
preset.brightness == brightness
and (
preset.color_temp == color_temp or not light.is_variable_color_temp
)
and (preset.hue == h or not light.is_color)
and (preset.saturation == s or not light.is_color)
and (preset.color_temp == color_temp or not is_variable_color_temp)
and (preset.hue == h or not is_color)
and (preset.saturation == s or not is_color)
):
return preset_name
return self.PRESET_NOT_SET
@@ -107,7 +109,7 @@ class LightPreset(IotModule, LightPresetInterface):
"""Set a light preset for the device."""
light = self._device.modules[Module.Light]
if preset_name == self.PRESET_NOT_SET:
if light.is_color:
if light.has_feature("hsv"):
preset = LightState(hue=0, saturation=0, brightness=100)
else:
preset = LightState(brightness=100)