mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-08-06 10:44:04 +00:00
Enable ruff check for ANN (#1139)
This commit is contained in:
@@ -15,7 +15,9 @@ class SmartLightEffect(LightEffectInterface, ABC):
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
async def set_brightness(self, brightness: int, *, transition: int | None = None):
|
||||
async def set_brightness(
|
||||
self, brightness: int, *, transition: int | None = None
|
||||
) -> dict:
|
||||
"""Set effect brightness."""
|
||||
|
||||
@property
|
||||
|
@@ -20,7 +20,7 @@ class Alarm(SmartModule):
|
||||
"get_support_alarm_type_list": None, # This should be needed only once
|
||||
}
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features.
|
||||
|
||||
This is implemented as some features depend on device responses.
|
||||
@@ -100,7 +100,7 @@ class Alarm(SmartModule):
|
||||
"""Return current alarm sound."""
|
||||
return self.data["get_alarm_configure"]["type"]
|
||||
|
||||
async def set_alarm_sound(self, sound: str):
|
||||
async def set_alarm_sound(self, sound: str) -> dict:
|
||||
"""Set alarm sound.
|
||||
|
||||
See *alarm_sounds* for list of available sounds.
|
||||
@@ -119,7 +119,7 @@ class Alarm(SmartModule):
|
||||
"""Return alarm volume."""
|
||||
return self.data["get_alarm_configure"]["volume"]
|
||||
|
||||
async def set_alarm_volume(self, volume: Literal["low", "normal", "high"]):
|
||||
async def set_alarm_volume(self, volume: Literal["low", "normal", "high"]) -> dict:
|
||||
"""Set alarm volume."""
|
||||
payload = self.data["get_alarm_configure"].copy()
|
||||
payload["volume"] = volume
|
||||
|
@@ -17,7 +17,7 @@ class AutoOff(SmartModule):
|
||||
REQUIRED_COMPONENT = "auto_off"
|
||||
QUERY_GETTER_NAME = "get_auto_off_config"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -63,7 +63,7 @@ class AutoOff(SmartModule):
|
||||
"""Return True if enabled."""
|
||||
return self.data["enable"]
|
||||
|
||||
async def set_enabled(self, enable: bool):
|
||||
async def set_enabled(self, enable: bool) -> dict:
|
||||
"""Enable/disable auto off."""
|
||||
return await self.call(
|
||||
"set_auto_off_config",
|
||||
@@ -75,7 +75,7 @@ class AutoOff(SmartModule):
|
||||
"""Return time until auto off."""
|
||||
return self.data["delay_min"]
|
||||
|
||||
async def set_delay(self, delay: int):
|
||||
async def set_delay(self, delay: int) -> dict:
|
||||
"""Set time until auto off."""
|
||||
return await self.call(
|
||||
"set_auto_off_config", {"delay_min": delay, "enable": self.data["enable"]}
|
||||
@@ -96,7 +96,7 @@ class AutoOff(SmartModule):
|
||||
|
||||
return self._device.time + timedelta(seconds=sysinfo["auto_off_remain_time"])
|
||||
|
||||
async def _check_supported(self):
|
||||
async def _check_supported(self) -> bool:
|
||||
"""Additional check to see if the module is supported by the device.
|
||||
|
||||
Parent devices that report components of children such as P300 will not have
|
||||
|
@@ -12,7 +12,7 @@ class BatterySensor(SmartModule):
|
||||
REQUIRED_COMPONENT = "battery_detect"
|
||||
QUERY_GETTER_NAME = "get_battery_detect_info"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -48,11 +48,11 @@ class BatterySensor(SmartModule):
|
||||
return {}
|
||||
|
||||
@property
|
||||
def battery(self):
|
||||
def battery(self) -> int:
|
||||
"""Return battery level."""
|
||||
return self._device.sys_info["battery_percentage"]
|
||||
|
||||
@property
|
||||
def battery_low(self):
|
||||
def battery_low(self) -> bool:
|
||||
"""Return True if battery is low."""
|
||||
return self._device.sys_info["at_low_battery"]
|
||||
|
@@ -14,7 +14,7 @@ class Brightness(SmartModule):
|
||||
|
||||
REQUIRED_COMPONENT = "brightness"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features."""
|
||||
super()._initialize_features()
|
||||
|
||||
@@ -39,7 +39,7 @@ class Brightness(SmartModule):
|
||||
return {}
|
||||
|
||||
@property
|
||||
def brightness(self):
|
||||
def brightness(self) -> int:
|
||||
"""Return current brightness."""
|
||||
# If the device supports effects and one is active, use its brightness
|
||||
if (
|
||||
@@ -49,7 +49,9 @@ class Brightness(SmartModule):
|
||||
|
||||
return self.data["brightness"]
|
||||
|
||||
async def set_brightness(self, brightness: int, *, transition: int | None = None):
|
||||
async def set_brightness(
|
||||
self, brightness: int, *, transition: int | None = None
|
||||
) -> dict:
|
||||
"""Set the brightness. A brightness value of 0 will turn off the light.
|
||||
|
||||
Note, transition is not supported and will be ignored.
|
||||
@@ -73,6 +75,6 @@ class Brightness(SmartModule):
|
||||
|
||||
return await self.call("set_device_info", {"brightness": brightness})
|
||||
|
||||
async def _check_supported(self):
|
||||
async def _check_supported(self) -> bool:
|
||||
"""Additional check to see if the module is supported by the device."""
|
||||
return "brightness" in self.data
|
||||
|
@@ -12,7 +12,7 @@ class ChildProtection(SmartModule):
|
||||
REQUIRED_COMPONENT = "child_protection"
|
||||
QUERY_GETTER_NAME = "get_child_protection"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
|
@@ -13,7 +13,7 @@ class Cloud(SmartModule):
|
||||
REQUIRED_COMPONENT = "cloud_connect"
|
||||
MINIMUM_UPDATE_INTERVAL_SECS = 60
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -29,7 +29,7 @@ class Cloud(SmartModule):
|
||||
)
|
||||
|
||||
@property
|
||||
def is_connected(self):
|
||||
def is_connected(self) -> bool:
|
||||
"""Return True if device is connected to the cloud."""
|
||||
if self._has_data_error():
|
||||
return False
|
||||
|
@@ -12,7 +12,7 @@ class Color(SmartModule):
|
||||
|
||||
REQUIRED_COMPONENT = "color"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -48,7 +48,7 @@ class Color(SmartModule):
|
||||
# due to the cpython implementation.
|
||||
return tuple.__new__(HSV, (h, s, v))
|
||||
|
||||
def _raise_for_invalid_brightness(self, value):
|
||||
def _raise_for_invalid_brightness(self, value: int) -> None:
|
||||
"""Raise error on invalid brightness value."""
|
||||
if not isinstance(value, int):
|
||||
raise TypeError("Brightness must be an integer")
|
||||
|
@@ -18,7 +18,7 @@ class ColorTemperature(SmartModule):
|
||||
|
||||
REQUIRED_COMPONENT = "color_temperature"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -52,11 +52,11 @@ class ColorTemperature(SmartModule):
|
||||
return ColorTempRange(*ct_range)
|
||||
|
||||
@property
|
||||
def color_temp(self):
|
||||
def color_temp(self) -> int:
|
||||
"""Return current color temperature."""
|
||||
return self.data["color_temp"]
|
||||
|
||||
async def set_color_temp(self, temp: int, *, brightness=None):
|
||||
async def set_color_temp(self, temp: int, *, brightness: int | None = None) -> dict:
|
||||
"""Set the color temperature."""
|
||||
valid_temperature_range = self.valid_temperature_range
|
||||
if temp < valid_temperature_range[0] or temp > valid_temperature_range[1]:
|
||||
|
@@ -12,7 +12,7 @@ class ContactSensor(SmartModule):
|
||||
REQUIRED_COMPONENT = None # we depend on availability of key
|
||||
REQUIRED_KEY_ON_PARENT = "open"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -32,6 +32,6 @@ class ContactSensor(SmartModule):
|
||||
return {}
|
||||
|
||||
@property
|
||||
def is_open(self):
|
||||
def is_open(self) -> bool:
|
||||
"""Return True if the contact sensor is open."""
|
||||
return self._device.sys_info["open"]
|
||||
|
@@ -10,7 +10,7 @@ class DeviceModule(SmartModule):
|
||||
|
||||
REQUIRED_COMPONENT = "device"
|
||||
|
||||
async def _post_update_hook(self):
|
||||
async def _post_update_hook(self) -> None:
|
||||
"""Perform actions after a device update.
|
||||
|
||||
Overrides the default behaviour to disable a module if the query returns
|
||||
|
@@ -2,6 +2,8 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import NoReturn
|
||||
|
||||
from ...emeterstatus import EmeterStatus
|
||||
from ...exceptions import KasaException
|
||||
from ...interfaces.energy import Energy as EnergyInterface
|
||||
@@ -31,34 +33,34 @@ class Energy(SmartModule, EnergyInterface):
|
||||
# Fallback if get_energy_usage does not provide current_power,
|
||||
# which can happen on some newer devices (e.g. P304M).
|
||||
elif (
|
||||
power := self.data.get("get_current_power").get("current_power")
|
||||
power := self.data.get("get_current_power", {}).get("current_power")
|
||||
) is not None:
|
||||
return power
|
||||
return None
|
||||
|
||||
@property
|
||||
@raise_if_update_error
|
||||
def energy(self):
|
||||
def energy(self) -> dict:
|
||||
"""Return get_energy_usage results."""
|
||||
if en := self.data.get("get_energy_usage"):
|
||||
return en
|
||||
return self.data
|
||||
|
||||
def _get_status_from_energy(self, energy) -> EmeterStatus:
|
||||
def _get_status_from_energy(self, energy: dict) -> EmeterStatus:
|
||||
return EmeterStatus(
|
||||
{
|
||||
"power_mw": energy.get("current_power"),
|
||||
"total": energy.get("today_energy") / 1_000,
|
||||
"power_mw": energy.get("current_power", 0),
|
||||
"total": energy.get("today_energy", 0) / 1_000,
|
||||
}
|
||||
)
|
||||
|
||||
@property
|
||||
@raise_if_update_error
|
||||
def status(self):
|
||||
def status(self) -> EmeterStatus:
|
||||
"""Get the emeter status."""
|
||||
return self._get_status_from_energy(self.energy)
|
||||
|
||||
async def get_status(self):
|
||||
async def get_status(self) -> EmeterStatus:
|
||||
"""Return real-time statistics."""
|
||||
res = await self.call("get_energy_usage")
|
||||
return self._get_status_from_energy(res["get_energy_usage"])
|
||||
@@ -67,13 +69,13 @@ class Energy(SmartModule, EnergyInterface):
|
||||
@raise_if_update_error
|
||||
def consumption_this_month(self) -> float | None:
|
||||
"""Get the emeter value for this month in kWh."""
|
||||
return self.energy.get("month_energy") / 1_000
|
||||
return self.energy.get("month_energy", 0) / 1_000
|
||||
|
||||
@property
|
||||
@raise_if_update_error
|
||||
def consumption_today(self) -> float | None:
|
||||
"""Get the emeter value for today in kWh."""
|
||||
return self.energy.get("today_energy") / 1_000
|
||||
return self.energy.get("today_energy", 0) / 1_000
|
||||
|
||||
@property
|
||||
@raise_if_update_error
|
||||
@@ -97,22 +99,26 @@ class Energy(SmartModule, EnergyInterface):
|
||||
"""Retrieve current energy readings."""
|
||||
return self.status
|
||||
|
||||
async def erase_stats(self):
|
||||
async def erase_stats(self) -> NoReturn:
|
||||
"""Erase all stats."""
|
||||
raise KasaException("Device does not support periodic statistics")
|
||||
|
||||
async def get_daily_stats(self, *, year=None, month=None, kwh=True) -> dict:
|
||||
async def get_daily_stats(
|
||||
self, *, year: int | None = None, month: int | None = None, kwh: bool = True
|
||||
) -> dict:
|
||||
"""Return daily stats for the given year & month.
|
||||
|
||||
The return value is a dictionary of {day: energy, ...}.
|
||||
"""
|
||||
raise KasaException("Device does not support periodic statistics")
|
||||
|
||||
async def get_monthly_stats(self, *, year=None, kwh=True) -> dict:
|
||||
async def get_monthly_stats(
|
||||
self, *, year: int | None = None, kwh: bool = True
|
||||
) -> dict:
|
||||
"""Return monthly stats for the given year."""
|
||||
raise KasaException("Device does not support periodic statistics")
|
||||
|
||||
async def _check_supported(self):
|
||||
async def _check_supported(self) -> bool:
|
||||
"""Additional check to see if the module is supported by the device."""
|
||||
# Energy module is not supported on P304M parent device
|
||||
return "device_on" in self._device.sys_info
|
||||
|
@@ -12,7 +12,7 @@ class Fan(SmartModule, FanInterface):
|
||||
|
||||
REQUIRED_COMPONENT = "fan_control"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -50,7 +50,7 @@ class Fan(SmartModule, FanInterface):
|
||||
"""Return fan speed level."""
|
||||
return 0 if self.data["device_on"] is False else self.data["fan_speed_level"]
|
||||
|
||||
async def set_fan_speed_level(self, level: int):
|
||||
async def set_fan_speed_level(self, level: int) -> dict:
|
||||
"""Set fan speed level, 0 for off, 1-4 for on."""
|
||||
if level < 0 or level > 4:
|
||||
raise ValueError("Invalid level, should be in range 0-4.")
|
||||
@@ -65,10 +65,10 @@ class Fan(SmartModule, FanInterface):
|
||||
"""Return sleep mode status."""
|
||||
return self.data["fan_sleep_mode_on"]
|
||||
|
||||
async def set_sleep_mode(self, on: bool):
|
||||
async def set_sleep_mode(self, on: bool) -> dict:
|
||||
"""Set sleep mode."""
|
||||
return await self.call("set_device_info", {"fan_sleep_mode_on": on})
|
||||
|
||||
async def _check_supported(self):
|
||||
async def _check_supported(self) -> bool:
|
||||
"""Is the module available on this device."""
|
||||
return "fan_speed_level" in self.data
|
||||
|
@@ -49,14 +49,14 @@ class UpdateInfo(BaseModel):
|
||||
needs_upgrade: bool = Field(alias="need_to_upgrade")
|
||||
|
||||
@validator("release_date", pre=True)
|
||||
def _release_date_optional(cls, v):
|
||||
def _release_date_optional(cls, v: str) -> str | None:
|
||||
if not v:
|
||||
return None
|
||||
|
||||
return v
|
||||
|
||||
@property
|
||||
def update_available(self):
|
||||
def update_available(self) -> bool:
|
||||
"""Return True if update available."""
|
||||
if self.status != 0:
|
||||
return True
|
||||
@@ -69,11 +69,11 @@ class Firmware(SmartModule):
|
||||
REQUIRED_COMPONENT = "firmware"
|
||||
MINIMUM_UPDATE_INTERVAL_SECS = 60 * 60 * 24
|
||||
|
||||
def __init__(self, device: SmartDevice, module: str):
|
||||
def __init__(self, device: SmartDevice, module: str) -> None:
|
||||
super().__init__(device, module)
|
||||
self._firmware_update_info: UpdateInfo | None = None
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features."""
|
||||
device = self._device
|
||||
if self.supported_version > 1:
|
||||
@@ -183,7 +183,7 @@ class Firmware(SmartModule):
|
||||
@allow_update_after
|
||||
async def update(
|
||||
self, progress_cb: Callable[[DownloadState], Coroutine] | None = None
|
||||
):
|
||||
) -> dict:
|
||||
"""Update the device firmware."""
|
||||
if not self._firmware_update_info:
|
||||
raise KasaException(
|
||||
@@ -236,13 +236,15 @@ class Firmware(SmartModule):
|
||||
else:
|
||||
_LOGGER.warning("Unhandled state code: %s", state)
|
||||
|
||||
return state.dict()
|
||||
|
||||
@property
|
||||
def auto_update_enabled(self) -> bool:
|
||||
"""Return True if autoupdate is enabled."""
|
||||
return "enable" in self.data and self.data["enable"]
|
||||
|
||||
@allow_update_after
|
||||
async def set_auto_update_enabled(self, enabled: bool):
|
||||
async def set_auto_update_enabled(self, enabled: bool) -> dict:
|
||||
"""Change autoupdate setting."""
|
||||
data = {**self.data, "enable": enabled}
|
||||
await self.call("set_auto_update_info", data)
|
||||
return await self.call("set_auto_update_info", data)
|
||||
|
@@ -23,7 +23,7 @@ class FrostProtection(SmartModule):
|
||||
"""Return True if frost protection is on."""
|
||||
return self._device.sys_info["frost_protection_on"]
|
||||
|
||||
async def set_enabled(self, enable: bool):
|
||||
async def set_enabled(self, enable: bool) -> dict:
|
||||
"""Enable/disable frost protection."""
|
||||
return await self.call(
|
||||
"set_device_info",
|
||||
|
@@ -12,7 +12,7 @@ class HumiditySensor(SmartModule):
|
||||
REQUIRED_COMPONENT = "humidity"
|
||||
QUERY_GETTER_NAME = "get_comfort_humidity_config"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -45,7 +45,7 @@ class HumiditySensor(SmartModule):
|
||||
return {}
|
||||
|
||||
@property
|
||||
def humidity(self):
|
||||
def humidity(self) -> int:
|
||||
"""Return current humidity in percentage."""
|
||||
return self._device.sys_info["current_humidity"]
|
||||
|
||||
|
@@ -19,7 +19,7 @@ class Led(SmartModule, LedInterface):
|
||||
return {self.QUERY_GETTER_NAME: None}
|
||||
|
||||
@property
|
||||
def mode(self):
|
||||
def mode(self) -> str:
|
||||
"""LED mode setting.
|
||||
|
||||
"always", "never", "night_mode"
|
||||
@@ -27,12 +27,12 @@ class Led(SmartModule, LedInterface):
|
||||
return self.data["led_rule"]
|
||||
|
||||
@property
|
||||
def led(self):
|
||||
def led(self) -> bool:
|
||||
"""Return current led status."""
|
||||
return self.data["led_rule"] != "never"
|
||||
|
||||
@allow_update_after
|
||||
async def set_led(self, enable: bool):
|
||||
async def set_led(self, enable: bool) -> dict:
|
||||
"""Set led.
|
||||
|
||||
This should probably be a select with always/never/nightmode.
|
||||
@@ -41,7 +41,7 @@ class Led(SmartModule, LedInterface):
|
||||
return await self.call("set_led_info", dict(self.data, **{"led_rule": rule}))
|
||||
|
||||
@property
|
||||
def night_mode_settings(self):
|
||||
def night_mode_settings(self) -> dict:
|
||||
"""Night mode settings."""
|
||||
return {
|
||||
"start": self.data["start_time"],
|
||||
|
@@ -96,7 +96,7 @@ class Light(SmartModule, LightInterface):
|
||||
return await self._device.modules[Module.Color].set_hsv(hue, saturation, value)
|
||||
|
||||
async def set_color_temp(
|
||||
self, temp: int, *, brightness=None, transition: int | None = None
|
||||
self, temp: int, *, brightness: int | None = None, transition: int | None = None
|
||||
) -> dict:
|
||||
"""Set the color temperature of the device in kelvin.
|
||||
|
||||
|
@@ -81,7 +81,7 @@ class LightEffect(SmartModule, SmartLightEffect):
|
||||
*,
|
||||
brightness: int | None = None,
|
||||
transition: int | None = None,
|
||||
) -> None:
|
||||
) -> dict:
|
||||
"""Set an effect for the device.
|
||||
|
||||
Calling this will modify the brightness of the effect on the device.
|
||||
@@ -107,7 +107,7 @@ class LightEffect(SmartModule, SmartLightEffect):
|
||||
)
|
||||
await self.set_brightness(brightness, effect_id=effect_id)
|
||||
|
||||
await self.call("set_dynamic_light_effect_rule_enable", params)
|
||||
return await self.call("set_dynamic_light_effect_rule_enable", params)
|
||||
|
||||
@property
|
||||
def is_active(self) -> bool:
|
||||
@@ -139,11 +139,11 @@ class LightEffect(SmartModule, SmartLightEffect):
|
||||
*,
|
||||
transition: int | None = None,
|
||||
effect_id: str | None = None,
|
||||
):
|
||||
) -> dict:
|
||||
"""Set effect brightness."""
|
||||
new_effect = self._get_effect_data(effect_id=effect_id).copy()
|
||||
|
||||
def _replace_brightness(data, new_brightness):
|
||||
def _replace_brightness(data: list[int], new_brightness: int) -> list[int]:
|
||||
"""Replace brightness.
|
||||
|
||||
The first element is the brightness, the rest are unknown.
|
||||
@@ -163,7 +163,7 @@ class LightEffect(SmartModule, SmartLightEffect):
|
||||
async def set_custom_effect(
|
||||
self,
|
||||
effect_dict: dict,
|
||||
) -> None:
|
||||
) -> dict:
|
||||
"""Set a custom effect on the device.
|
||||
|
||||
:param str effect_dict: The custom effect dict to set
|
||||
|
@@ -29,12 +29,12 @@ class LightPreset(SmartModule, LightPresetInterface):
|
||||
_presets: dict[str, LightState]
|
||||
_preset_list: list[str]
|
||||
|
||||
def __init__(self, device: SmartDevice, module: str):
|
||||
def __init__(self, device: SmartDevice, module: str) -> None:
|
||||
super().__init__(device, module)
|
||||
self._state_in_sysinfo = self.SYS_INFO_STATE_KEY in device.sys_info
|
||||
self._brightness_only: bool = False
|
||||
|
||||
async def _post_update_hook(self):
|
||||
async def _post_update_hook(self) -> None:
|
||||
"""Update the internal presets."""
|
||||
index = 0
|
||||
self._presets = {}
|
||||
@@ -113,7 +113,7 @@ class LightPreset(SmartModule, LightPresetInterface):
|
||||
async def set_preset(
|
||||
self,
|
||||
preset_name: str,
|
||||
) -> None:
|
||||
) -> dict:
|
||||
"""Set a light preset for the device."""
|
||||
light = self._device.modules[SmartModule.Light]
|
||||
if preset_name == self.PRESET_NOT_SET:
|
||||
@@ -123,14 +123,14 @@ class LightPreset(SmartModule, LightPresetInterface):
|
||||
preset = LightState(brightness=100)
|
||||
elif (preset := self._presets.get(preset_name)) is None: # type: ignore[assignment]
|
||||
raise ValueError(f"{preset_name} is not a valid preset: {self.preset_list}")
|
||||
await self._device.modules[SmartModule.Light].set_state(preset)
|
||||
return await self._device.modules[SmartModule.Light].set_state(preset)
|
||||
|
||||
@allow_update_after
|
||||
async def save_preset(
|
||||
self,
|
||||
preset_name: str,
|
||||
preset_state: LightState,
|
||||
) -> None:
|
||||
) -> dict:
|
||||
"""Update the preset with preset_name with the new preset_info."""
|
||||
if preset_name not in self._presets:
|
||||
raise ValueError(f"{preset_name} is not a valid preset: {self.preset_list}")
|
||||
@@ -138,11 +138,13 @@ class LightPreset(SmartModule, LightPresetInterface):
|
||||
if self._brightness_only:
|
||||
bright_list = [state.brightness for state in self._presets.values()]
|
||||
bright_list[index] = preset_state.brightness
|
||||
await self.call("set_preset_rules", {"brightness": bright_list})
|
||||
return await self.call("set_preset_rules", {"brightness": bright_list})
|
||||
else:
|
||||
state_params = asdict(preset_state)
|
||||
new_info = {k: v for k, v in state_params.items() if v is not None}
|
||||
await self.call("edit_preset_rules", {"index": index, "state": new_info})
|
||||
return await self.call(
|
||||
"edit_preset_rules", {"index": index, "state": new_info}
|
||||
)
|
||||
|
||||
@property
|
||||
def has_save_preset(self) -> bool:
|
||||
@@ -158,7 +160,7 @@ class LightPreset(SmartModule, LightPresetInterface):
|
||||
|
||||
return {self.QUERY_GETTER_NAME: {"start_index": 0}}
|
||||
|
||||
async def _check_supported(self):
|
||||
async def _check_supported(self) -> bool:
|
||||
"""Additional check to see if the module is supported by the device.
|
||||
|
||||
Parent devices that report components of children such as ks240 will not have
|
||||
|
@@ -16,7 +16,7 @@ class LightStripEffect(SmartModule, SmartLightEffect):
|
||||
|
||||
REQUIRED_COMPONENT = "light_strip_lighting_effect"
|
||||
|
||||
def __init__(self, device: SmartDevice, module: str):
|
||||
def __init__(self, device: SmartDevice, module: str) -> None:
|
||||
super().__init__(device, module)
|
||||
effect_list = [self.LIGHT_EFFECTS_OFF]
|
||||
effect_list.extend(EFFECT_NAMES)
|
||||
@@ -66,7 +66,9 @@ class LightStripEffect(SmartModule, SmartLightEffect):
|
||||
eff = self.data["lighting_effect"]
|
||||
return eff["brightness"]
|
||||
|
||||
async def set_brightness(self, brightness: int, *, transition: int | None = None):
|
||||
async def set_brightness(
|
||||
self, brightness: int, *, transition: int | None = None
|
||||
) -> dict:
|
||||
"""Set effect brightness."""
|
||||
if brightness <= 0:
|
||||
return await self.set_effect(self.LIGHT_EFFECTS_OFF)
|
||||
@@ -91,7 +93,7 @@ class LightStripEffect(SmartModule, SmartLightEffect):
|
||||
*,
|
||||
brightness: int | None = None,
|
||||
transition: int | None = None,
|
||||
) -> None:
|
||||
) -> dict:
|
||||
"""Set an effect on the device.
|
||||
|
||||
If brightness or transition is defined,
|
||||
@@ -115,8 +117,7 @@ class LightStripEffect(SmartModule, SmartLightEffect):
|
||||
effect_dict = self._effect_mapping["Aurora"]
|
||||
effect_dict = {**effect_dict}
|
||||
effect_dict["enable"] = 0
|
||||
await self.set_custom_effect(effect_dict)
|
||||
return
|
||||
return await self.set_custom_effect(effect_dict)
|
||||
|
||||
if effect not in self._effect_mapping:
|
||||
raise ValueError(f"The effect {effect} is not a built in effect.")
|
||||
@@ -134,13 +135,13 @@ class LightStripEffect(SmartModule, SmartLightEffect):
|
||||
if transition is not None:
|
||||
effect_dict["transition"] = transition
|
||||
|
||||
await self.set_custom_effect(effect_dict)
|
||||
return await self.set_custom_effect(effect_dict)
|
||||
|
||||
@allow_update_after
|
||||
async def set_custom_effect(
|
||||
self,
|
||||
effect_dict: dict,
|
||||
) -> None:
|
||||
) -> dict:
|
||||
"""Set a custom effect on the device.
|
||||
|
||||
:param str effect_dict: The custom effect dict to set
|
||||
@@ -155,7 +156,7 @@ class LightStripEffect(SmartModule, SmartLightEffect):
|
||||
"""Return True if the device supports setting custom effects."""
|
||||
return True
|
||||
|
||||
def query(self):
|
||||
def query(self) -> dict:
|
||||
"""Return the base query."""
|
||||
return {}
|
||||
|
||||
|
@@ -39,14 +39,14 @@ class LightTransition(SmartModule):
|
||||
_off_state: _State
|
||||
_enabled: bool
|
||||
|
||||
def __init__(self, device: SmartDevice, module: str):
|
||||
def __init__(self, device: SmartDevice, module: str) -> None:
|
||||
super().__init__(device, module)
|
||||
self._state_in_sysinfo = all(
|
||||
key in device.sys_info for key in self.SYS_INFO_STATE_KEYS
|
||||
)
|
||||
self._supports_on_and_off: bool = self.supported_version > 1
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features."""
|
||||
icon = "mdi:transition"
|
||||
if not self._supports_on_and_off:
|
||||
@@ -138,7 +138,7 @@ class LightTransition(SmartModule):
|
||||
}
|
||||
|
||||
@allow_update_after
|
||||
async def set_enabled(self, enable: bool):
|
||||
async def set_enabled(self, enable: bool) -> dict:
|
||||
"""Enable gradual on/off."""
|
||||
if not self._supports_on_and_off:
|
||||
return await self.call("set_on_off_gradually_info", {"enable": enable})
|
||||
@@ -171,7 +171,7 @@ class LightTransition(SmartModule):
|
||||
return self._on_state["max_duration"]
|
||||
|
||||
@allow_update_after
|
||||
async def set_turn_on_transition(self, seconds: int):
|
||||
async def set_turn_on_transition(self, seconds: int) -> dict:
|
||||
"""Set turn on transition in seconds.
|
||||
|
||||
Setting to 0 turns the feature off.
|
||||
@@ -207,7 +207,7 @@ class LightTransition(SmartModule):
|
||||
return self._off_state["max_duration"]
|
||||
|
||||
@allow_update_after
|
||||
async def set_turn_off_transition(self, seconds: int):
|
||||
async def set_turn_off_transition(self, seconds: int) -> dict:
|
||||
"""Set turn on transition in seconds.
|
||||
|
||||
Setting to 0 turns the feature off.
|
||||
@@ -236,7 +236,7 @@ class LightTransition(SmartModule):
|
||||
else:
|
||||
return {self.QUERY_GETTER_NAME: None}
|
||||
|
||||
async def _check_supported(self):
|
||||
async def _check_supported(self) -> bool:
|
||||
"""Additional check to see if the module is supported by the device."""
|
||||
# For devices that report child components on the parent that are not
|
||||
# actually supported by the parent.
|
||||
|
@@ -11,7 +11,7 @@ class MotionSensor(SmartModule):
|
||||
|
||||
REQUIRED_COMPONENT = "sensitivity"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -31,6 +31,6 @@ class MotionSensor(SmartModule):
|
||||
return {}
|
||||
|
||||
@property
|
||||
def motion_detected(self):
|
||||
def motion_detected(self) -> bool:
|
||||
"""Return True if the motion has been detected."""
|
||||
return self._device.sys_info["detected"]
|
||||
|
@@ -12,7 +12,7 @@ class ReportMode(SmartModule):
|
||||
REQUIRED_COMPONENT = "report_mode"
|
||||
QUERY_GETTER_NAME = "get_report_mode"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -32,6 +32,6 @@ class ReportMode(SmartModule):
|
||||
return {}
|
||||
|
||||
@property
|
||||
def report_interval(self):
|
||||
def report_interval(self) -> int:
|
||||
"""Reporting interval of a sensor device."""
|
||||
return self._device.sys_info["report_interval"]
|
||||
|
@@ -26,7 +26,7 @@ class TemperatureControl(SmartModule):
|
||||
|
||||
REQUIRED_COMPONENT = "temp_control"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -92,7 +92,7 @@ class TemperatureControl(SmartModule):
|
||||
"""Return thermostat state."""
|
||||
return self._device.sys_info["frost_protection_on"] is False
|
||||
|
||||
async def set_state(self, enabled: bool):
|
||||
async def set_state(self, enabled: bool) -> dict:
|
||||
"""Set thermostat state."""
|
||||
return await self.call("set_device_info", {"frost_protection_on": not enabled})
|
||||
|
||||
@@ -147,7 +147,7 @@ class TemperatureControl(SmartModule):
|
||||
"""Return thermostat states."""
|
||||
return set(self._device.sys_info["trv_states"])
|
||||
|
||||
async def set_target_temperature(self, target: float):
|
||||
async def set_target_temperature(self, target: float) -> dict:
|
||||
"""Set target temperature."""
|
||||
if (
|
||||
target < self.minimum_target_temperature
|
||||
@@ -170,7 +170,7 @@ class TemperatureControl(SmartModule):
|
||||
"""Return temperature offset."""
|
||||
return self._device.sys_info["temp_offset"]
|
||||
|
||||
async def set_temperature_offset(self, offset: int):
|
||||
async def set_temperature_offset(self, offset: int) -> dict:
|
||||
"""Set temperature offset."""
|
||||
if offset < -10 or offset > 10:
|
||||
raise ValueError("Temperature offset must be [-10, 10]")
|
||||
|
@@ -14,7 +14,7 @@ class TemperatureSensor(SmartModule):
|
||||
REQUIRED_COMPONENT = "temperature"
|
||||
QUERY_GETTER_NAME = "get_comfort_temp_config"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -60,7 +60,7 @@ class TemperatureSensor(SmartModule):
|
||||
return {}
|
||||
|
||||
@property
|
||||
def temperature(self):
|
||||
def temperature(self) -> float:
|
||||
"""Return current humidity in percentage."""
|
||||
return self._device.sys_info["current_temp"]
|
||||
|
||||
@@ -74,6 +74,8 @@ class TemperatureSensor(SmartModule):
|
||||
"""Return current temperature unit."""
|
||||
return self._device.sys_info["temp_unit"]
|
||||
|
||||
async def set_temperature_unit(self, unit: Literal["celsius", "fahrenheit"]):
|
||||
async def set_temperature_unit(
|
||||
self, unit: Literal["celsius", "fahrenheit"]
|
||||
) -> dict:
|
||||
"""Set the device temperature unit."""
|
||||
return await self.call("set_temperature_unit", {"temp_unit": unit})
|
||||
|
@@ -21,7 +21,7 @@ class Time(SmartModule, TimeInterface):
|
||||
|
||||
_timezone: tzinfo = timezone.utc
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -35,7 +35,7 @@ class Time(SmartModule, TimeInterface):
|
||||
)
|
||||
)
|
||||
|
||||
async def _post_update_hook(self):
|
||||
async def _post_update_hook(self) -> None:
|
||||
"""Perform actions after a device update."""
|
||||
td = timedelta(minutes=cast(float, self.data.get("time_diff")))
|
||||
if region := self.data.get("region"):
|
||||
@@ -84,7 +84,7 @@ class Time(SmartModule, TimeInterface):
|
||||
params["region"] = region
|
||||
return await self.call("set_device_time", params)
|
||||
|
||||
async def _check_supported(self):
|
||||
async def _check_supported(self) -> bool:
|
||||
"""Additional check to see if the module is supported by the device.
|
||||
|
||||
Hub attached sensors report the time module but do return device time.
|
||||
|
@@ -22,7 +22,7 @@ class WaterleakSensor(SmartModule):
|
||||
|
||||
REQUIRED_COMPONENT = "sensor_alarm"
|
||||
|
||||
def _initialize_features(self):
|
||||
def _initialize_features(self) -> None:
|
||||
"""Initialize features after the initial update."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
|
@@ -49,7 +49,7 @@ class SmartChildDevice(SmartDevice):
|
||||
self._update_internal_state(info)
|
||||
self._components = component_info
|
||||
|
||||
async def update(self, update_children: bool = True):
|
||||
async def update(self, update_children: bool = True) -> None:
|
||||
"""Update child module info.
|
||||
|
||||
The parent updates our internal info so just update modules with
|
||||
@@ -57,7 +57,7 @@ class SmartChildDevice(SmartDevice):
|
||||
"""
|
||||
await self._update(update_children)
|
||||
|
||||
async def _update(self, update_children: bool = True):
|
||||
async def _update(self, update_children: bool = True) -> None:
|
||||
"""Update child module info.
|
||||
|
||||
Internal implementation to allow patching of public update in the cli
|
||||
@@ -118,5 +118,5 @@ class SmartChildDevice(SmartDevice):
|
||||
dev_type = DeviceType.Unknown
|
||||
return dev_type
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self.device_type} {self.alias} ({self.model}) of {self._parent}>"
|
||||
|
@@ -69,7 +69,7 @@ class SmartDevice(Device):
|
||||
self._on_since: datetime | None = None
|
||||
self._info: dict[str, Any] = {}
|
||||
|
||||
async def _initialize_children(self):
|
||||
async def _initialize_children(self) -> None:
|
||||
"""Initialize children for power strips."""
|
||||
child_info_query = {
|
||||
"get_child_device_component_list": None,
|
||||
@@ -108,7 +108,9 @@ class SmartDevice(Device):
|
||||
"""Return the device modules."""
|
||||
return cast(ModuleMapping[SmartModule], self._modules)
|
||||
|
||||
def _try_get_response(self, responses: dict, request: str, default=None) -> dict:
|
||||
def _try_get_response(
|
||||
self, responses: dict, request: str, default: Any | None = None
|
||||
) -> dict:
|
||||
response = responses.get(request)
|
||||
if isinstance(response, SmartErrorCode):
|
||||
_LOGGER.debug(
|
||||
@@ -126,7 +128,7 @@ class SmartDevice(Device):
|
||||
f"{request} not found in {responses} for device {self.host}"
|
||||
)
|
||||
|
||||
async def _negotiate(self):
|
||||
async def _negotiate(self) -> None:
|
||||
"""Perform initialization.
|
||||
|
||||
We fetch the device info and the available components as early as possible.
|
||||
@@ -146,7 +148,8 @@ class SmartDevice(Device):
|
||||
self._info = self._try_get_response(resp, "get_device_info")
|
||||
|
||||
# Create our internal presentation of available components
|
||||
self._components_raw = resp["component_nego"]
|
||||
self._components_raw = cast(dict, resp["component_nego"])
|
||||
|
||||
self._components = {
|
||||
comp["id"]: int(comp["ver_code"])
|
||||
for comp in self._components_raw["component_list"]
|
||||
@@ -167,7 +170,7 @@ class SmartDevice(Device):
|
||||
"""Update the internal device info."""
|
||||
self._info = self._try_get_response(info_resp, "get_device_info")
|
||||
|
||||
async def update(self, update_children: bool = False):
|
||||
async def update(self, update_children: bool = False) -> None:
|
||||
"""Update the device."""
|
||||
if self.credentials is None and self.credentials_hash is None:
|
||||
raise AuthenticationError("Tapo plug requires authentication.")
|
||||
@@ -206,7 +209,7 @@ class SmartDevice(Device):
|
||||
|
||||
async def _handle_module_post_update(
|
||||
self, module: SmartModule, update_time: float, had_query: bool
|
||||
):
|
||||
) -> None:
|
||||
if module.disabled:
|
||||
return # pragma: no cover
|
||||
if had_query:
|
||||
@@ -312,7 +315,7 @@ class SmartDevice(Device):
|
||||
responses[meth] = SmartErrorCode.INTERNAL_QUERY_ERROR
|
||||
return responses
|
||||
|
||||
async def _initialize_modules(self):
|
||||
async def _initialize_modules(self) -> None:
|
||||
"""Initialize modules based on component negotiation response."""
|
||||
from .smartmodule import SmartModule
|
||||
|
||||
@@ -324,7 +327,7 @@ class SmartDevice(Device):
|
||||
# It also ensures that devices like power strips do not add modules such as
|
||||
# firmware to the child devices.
|
||||
skip_parent_only_modules = False
|
||||
child_modules_to_skip = {}
|
||||
child_modules_to_skip: dict = {} # TODO: this is never non-empty
|
||||
if self._parent and self._parent.device_type != DeviceType.Hub:
|
||||
skip_parent_only_modules = True
|
||||
|
||||
@@ -333,17 +336,18 @@ class SmartDevice(Device):
|
||||
skip_parent_only_modules and mod in NON_HUB_PARENT_ONLY_MODULES
|
||||
) or mod.__name__ in child_modules_to_skip:
|
||||
continue
|
||||
if (
|
||||
mod.REQUIRED_COMPONENT in self._components
|
||||
or self.sys_info.get(mod.REQUIRED_KEY_ON_PARENT) is not None
|
||||
required_component = cast(str, mod.REQUIRED_COMPONENT)
|
||||
if required_component in self._components or (
|
||||
mod.REQUIRED_KEY_ON_PARENT
|
||||
and self.sys_info.get(mod.REQUIRED_KEY_ON_PARENT) is not None
|
||||
):
|
||||
_LOGGER.debug(
|
||||
"Device %s, found required %s, adding %s to modules.",
|
||||
self.host,
|
||||
mod.REQUIRED_COMPONENT,
|
||||
required_component,
|
||||
mod.__name__,
|
||||
)
|
||||
module = mod(self, mod.REQUIRED_COMPONENT)
|
||||
module = mod(self, required_component)
|
||||
if await module._check_supported():
|
||||
self._modules[module.name] = module
|
||||
|
||||
@@ -354,7 +358,7 @@ class SmartDevice(Device):
|
||||
):
|
||||
self._modules[Light.__name__] = Light(self, "light")
|
||||
|
||||
async def _initialize_features(self):
|
||||
async def _initialize_features(self) -> None:
|
||||
"""Initialize device features."""
|
||||
self._add_feature(
|
||||
Feature(
|
||||
@@ -575,11 +579,11 @@ class SmartDevice(Device):
|
||||
return str(self._info.get("device_id"))
|
||||
|
||||
@property
|
||||
def internal_state(self) -> Any:
|
||||
def internal_state(self) -> dict:
|
||||
"""Return all the internal state data."""
|
||||
return self._last_update
|
||||
|
||||
def _update_internal_state(self, info: dict) -> None:
|
||||
def _update_internal_state(self, info: dict[str, Any]) -> None:
|
||||
"""Update the internal info state.
|
||||
|
||||
This is used by the parent to push updates to its children.
|
||||
@@ -587,8 +591,8 @@ class SmartDevice(Device):
|
||||
self._info = info
|
||||
|
||||
async def _query_helper(
|
||||
self, method: str, params: dict | None = None, child_ids=None
|
||||
) -> Any:
|
||||
self, method: str, params: dict | None = None, child_ids: None = None
|
||||
) -> dict:
|
||||
res = await self.protocol.query({method: params})
|
||||
|
||||
return res
|
||||
@@ -610,22 +614,25 @@ class SmartDevice(Device):
|
||||
"""Return true if the device is on."""
|
||||
return bool(self._info.get("device_on"))
|
||||
|
||||
async def set_state(self, on: bool): # TODO: better name wanted.
|
||||
async def set_state(self, on: bool) -> dict:
|
||||
"""Set the device state.
|
||||
|
||||
See :meth:`is_on`.
|
||||
"""
|
||||
return await self.protocol.query({"set_device_info": {"device_on": on}})
|
||||
|
||||
async def turn_on(self, **kwargs):
|
||||
async def turn_on(self, **kwargs: Any) -> dict:
|
||||
"""Turn on the device."""
|
||||
await self.set_state(True)
|
||||
return await self.set_state(True)
|
||||
|
||||
async def turn_off(self, **kwargs):
|
||||
async def turn_off(self, **kwargs: Any) -> dict:
|
||||
"""Turn off the device."""
|
||||
await self.set_state(False)
|
||||
return await self.set_state(False)
|
||||
|
||||
def update_from_discover_info(self, info):
|
||||
def update_from_discover_info(
|
||||
self,
|
||||
info: dict,
|
||||
) -> None:
|
||||
"""Update state from info from the discover call."""
|
||||
self._discovery_info = info
|
||||
self._info = info
|
||||
@@ -633,7 +640,7 @@ class SmartDevice(Device):
|
||||
async def wifi_scan(self) -> list[WifiNetwork]:
|
||||
"""Scan for available wifi networks."""
|
||||
|
||||
def _net_for_scan_info(res):
|
||||
def _net_for_scan_info(res: dict) -> WifiNetwork:
|
||||
return WifiNetwork(
|
||||
ssid=base64.b64decode(res["ssid"]).decode(),
|
||||
cipher_type=res["cipher_type"],
|
||||
@@ -651,7 +658,9 @@ class SmartDevice(Device):
|
||||
]
|
||||
return networks
|
||||
|
||||
async def wifi_join(self, ssid: str, password: str, keytype: str = "wpa2_psk"):
|
||||
async def wifi_join(
|
||||
self, ssid: str, password: str, keytype: str = "wpa2_psk"
|
||||
) -> dict:
|
||||
"""Join the given wifi network.
|
||||
|
||||
This method returns nothing as the device tries to activate the new
|
||||
@@ -688,9 +697,12 @@ class SmartDevice(Device):
|
||||
except DeviceError:
|
||||
raise # Re-raise on device-reported errors
|
||||
except KasaException:
|
||||
_LOGGER.debug("Received an expected for wifi join, but this is expected")
|
||||
_LOGGER.debug(
|
||||
"Received a kasa exception for wifi join, but this is expected"
|
||||
)
|
||||
return {}
|
||||
|
||||
async def update_credentials(self, username: str, password: str):
|
||||
async def update_credentials(self, username: str, password: str) -> dict:
|
||||
"""Update device credentials.
|
||||
|
||||
This will replace the existing authentication credentials on the device.
|
||||
@@ -705,7 +717,7 @@ class SmartDevice(Device):
|
||||
}
|
||||
return await self.protocol.query({"set_qs_info": payload})
|
||||
|
||||
async def set_alias(self, alias: str):
|
||||
async def set_alias(self, alias: str) -> dict:
|
||||
"""Set the device name (alias)."""
|
||||
return await self.protocol.query(
|
||||
{"set_device_info": {"nickname": base64.b64encode(alias.encode()).decode()}}
|
||||
|
@@ -22,17 +22,17 @@ _R = TypeVar("_R")
|
||||
|
||||
|
||||
def allow_update_after(
|
||||
func: Callable[Concatenate[_T, _P], Awaitable[None]],
|
||||
) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, None]]:
|
||||
func: Callable[Concatenate[_T, _P], Awaitable[dict]],
|
||||
) -> Callable[Concatenate[_T, _P], Coroutine[Any, Any, dict]]:
|
||||
"""Define a wrapper to set _last_update_time to None.
|
||||
|
||||
This will ensure that a module is updated in the next update cycle after
|
||||
a value has been changed.
|
||||
"""
|
||||
|
||||
async def _async_wrap(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> None:
|
||||
async def _async_wrap(self: _T, *args: _P.args, **kwargs: _P.kwargs) -> dict:
|
||||
try:
|
||||
await func(self, *args, **kwargs)
|
||||
return await func(self, *args, **kwargs)
|
||||
finally:
|
||||
self._last_update_time = None
|
||||
|
||||
@@ -68,21 +68,21 @@ class SmartModule(Module):
|
||||
|
||||
DISABLE_AFTER_ERROR_COUNT = 10
|
||||
|
||||
def __init__(self, device: SmartDevice, module: str):
|
||||
def __init__(self, device: SmartDevice, module: str) -> None:
|
||||
self._device: SmartDevice
|
||||
super().__init__(device, module)
|
||||
self._last_update_time: float | None = None
|
||||
self._last_update_error: KasaException | None = None
|
||||
self._error_count = 0
|
||||
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
def __init_subclass__(cls, **kwargs) -> None:
|
||||
# We only want to register submodules in a modules package so that
|
||||
# other classes can inherit from smartmodule and not be registered
|
||||
if cls.__module__.split(".")[-2] == "modules":
|
||||
_LOGGER.debug("Registering %s", cls)
|
||||
cls.REGISTERED_MODULES[cls._module_name()] = cls
|
||||
|
||||
def _set_error(self, err: Exception | None):
|
||||
def _set_error(self, err: Exception | None) -> None:
|
||||
if err is None:
|
||||
self._error_count = 0
|
||||
self._last_update_error = None
|
||||
@@ -119,7 +119,7 @@ class SmartModule(Module):
|
||||
return self._error_count >= self.DISABLE_AFTER_ERROR_COUNT
|
||||
|
||||
@classmethod
|
||||
def _module_name(cls):
|
||||
def _module_name(cls) -> str:
|
||||
return getattr(cls, "NAME", cls.__name__)
|
||||
|
||||
@property
|
||||
@@ -127,7 +127,7 @@ class SmartModule(Module):
|
||||
"""Name of the module."""
|
||||
return self._module_name()
|
||||
|
||||
async def _post_update_hook(self): # noqa: B027
|
||||
async def _post_update_hook(self) -> None: # noqa: B027
|
||||
"""Perform actions after a device update.
|
||||
|
||||
Any modules overriding this should ensure that self.data is
|
||||
@@ -142,7 +142,7 @@ class SmartModule(Module):
|
||||
"""
|
||||
return {self.QUERY_GETTER_NAME: None}
|
||||
|
||||
async def call(self, method, params=None):
|
||||
async def call(self, method: str, params: dict | None = None) -> dict:
|
||||
"""Call a method.
|
||||
|
||||
Just a helper method.
|
||||
@@ -150,7 +150,7 @@ class SmartModule(Module):
|
||||
return await self._device._query_helper(method, params)
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
def data(self) -> dict[str, Any]:
|
||||
"""Return response data for the module.
|
||||
|
||||
If the module performs only a single query, the resulting response is unwrapped.
|
||||
|
Reference in New Issue
Block a user