mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-08-09 20:24:02 +00:00
Enable and convert to future annotations (#838)
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
"""Module for bulbs (LB*, KL*, KB*)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Optional, cast
|
||||
from typing import Optional, cast
|
||||
|
||||
try:
|
||||
from pydantic.v1 import BaseModel, Field, root_validator
|
||||
@@ -40,7 +42,7 @@ class TurnOnBehavior(BaseModel):
|
||||
"""
|
||||
|
||||
#: Index of preset to use, or ``None`` for the last known state.
|
||||
preset: Optional[int] = Field(alias="index", default=None)
|
||||
preset: Optional[int] = Field(alias="index", default=None) # noqa: UP007
|
||||
#: Wanted behavior
|
||||
mode: BehaviorMode
|
||||
|
||||
@@ -193,8 +195,8 @@ class IotBulb(IotDevice, Bulb):
|
||||
self,
|
||||
host: str,
|
||||
*,
|
||||
config: Optional[DeviceConfig] = None,
|
||||
protocol: Optional[BaseProtocol] = None,
|
||||
config: DeviceConfig | None = None,
|
||||
protocol: BaseProtocol | None = None,
|
||||
) -> None:
|
||||
super().__init__(host=host, config=config, protocol=protocol)
|
||||
self._device_type = DeviceType.Bulb
|
||||
@@ -275,7 +277,7 @@ class IotBulb(IotDevice, Bulb):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def light_state(self) -> Dict[str, str]:
|
||||
def light_state(self) -> dict[str, str]:
|
||||
"""Query the light state."""
|
||||
light_state = self.sys_info["light_state"]
|
||||
if light_state is None:
|
||||
@@ -298,7 +300,7 @@ class IotBulb(IotDevice, Bulb):
|
||||
"""Return True if the device supports effects."""
|
||||
return "lighting_effect_state" in self.sys_info
|
||||
|
||||
async def get_light_details(self) -> Dict[str, int]:
|
||||
async def get_light_details(self) -> dict[str, int]:
|
||||
"""Return light details.
|
||||
|
||||
Example::
|
||||
@@ -325,14 +327,14 @@ class IotBulb(IotDevice, Bulb):
|
||||
self.LIGHT_SERVICE, "set_default_behavior", behavior.dict(by_alias=True)
|
||||
)
|
||||
|
||||
async def get_light_state(self) -> Dict[str, Dict]:
|
||||
async def get_light_state(self) -> dict[str, dict]:
|
||||
"""Query the light state."""
|
||||
# TODO: add warning and refer to use light.state?
|
||||
return await self._query_helper(self.LIGHT_SERVICE, "get_light_state")
|
||||
|
||||
async def set_light_state(
|
||||
self, state: Dict, *, transition: Optional[int] = None
|
||||
) -> Dict:
|
||||
self, state: dict, *, transition: int | None = None
|
||||
) -> dict:
|
||||
"""Set the light state."""
|
||||
if transition is not None:
|
||||
state["transition_period"] = transition
|
||||
@@ -378,10 +380,10 @@ class IotBulb(IotDevice, Bulb):
|
||||
self,
|
||||
hue: int,
|
||||
saturation: int,
|
||||
value: Optional[int] = None,
|
||||
value: int | None = None,
|
||||
*,
|
||||
transition: Optional[int] = None,
|
||||
) -> Dict:
|
||||
transition: int | None = None,
|
||||
) -> dict:
|
||||
"""Set new HSV.
|
||||
|
||||
:param int hue: hue in degrees
|
||||
@@ -424,8 +426,8 @@ class IotBulb(IotDevice, Bulb):
|
||||
|
||||
@requires_update
|
||||
async def set_color_temp(
|
||||
self, temp: int, *, brightness=None, transition: Optional[int] = None
|
||||
) -> Dict:
|
||||
self, temp: int, *, brightness=None, transition: int | None = None
|
||||
) -> dict:
|
||||
"""Set the color temperature of the device in kelvin.
|
||||
|
||||
:param int temp: The new color temperature, in Kelvin
|
||||
@@ -460,8 +462,8 @@ class IotBulb(IotDevice, Bulb):
|
||||
|
||||
@requires_update
|
||||
async def set_brightness(
|
||||
self, brightness: int, *, transition: Optional[int] = None
|
||||
) -> Dict:
|
||||
self, brightness: int, *, transition: int | None = None
|
||||
) -> dict:
|
||||
"""Set the brightness in percentage.
|
||||
|
||||
:param int brightness: brightness in percent
|
||||
@@ -482,14 +484,14 @@ class IotBulb(IotDevice, Bulb):
|
||||
light_state = self.light_state
|
||||
return bool(light_state["on_off"])
|
||||
|
||||
async def turn_off(self, *, transition: Optional[int] = None, **kwargs) -> Dict:
|
||||
async def turn_off(self, *, transition: int | None = None, **kwargs) -> dict:
|
||||
"""Turn the bulb off.
|
||||
|
||||
:param int transition: transition in milliseconds.
|
||||
"""
|
||||
return await self.set_light_state({"on_off": 0}, transition=transition)
|
||||
|
||||
async def turn_on(self, *, transition: Optional[int] = None, **kwargs) -> Dict:
|
||||
async def turn_on(self, *, transition: int | None = None, **kwargs) -> dict:
|
||||
"""Turn the bulb on.
|
||||
|
||||
:param int transition: transition in milliseconds.
|
||||
@@ -513,7 +515,7 @@ class IotBulb(IotDevice, Bulb):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def presets(self) -> List[BulbPreset]:
|
||||
def presets(self) -> list[BulbPreset]:
|
||||
"""Return a list of available bulb setting presets."""
|
||||
return [BulbPreset(**vals) for vals in self.sys_info["preferred_state"]]
|
||||
|
||||
|
@@ -12,12 +12,14 @@ You may obtain a copy of the license at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import collections.abc
|
||||
import functools
|
||||
import inspect
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Dict, List, Mapping, Optional, Sequence, Set
|
||||
from typing import Any, Mapping, Sequence
|
||||
|
||||
from ..device import Device, WifiNetwork
|
||||
from ..deviceconfig import DeviceConfig
|
||||
@@ -66,7 +68,7 @@ def requires_update(f):
|
||||
|
||||
|
||||
@functools.lru_cache
|
||||
def _parse_features(features: str) -> Set[str]:
|
||||
def _parse_features(features: str) -> set[str]:
|
||||
"""Parse features string."""
|
||||
return set(features.split(":"))
|
||||
|
||||
@@ -177,19 +179,19 @@ class IotDevice(Device):
|
||||
self,
|
||||
host: str,
|
||||
*,
|
||||
config: Optional[DeviceConfig] = None,
|
||||
protocol: Optional[BaseProtocol] = None,
|
||||
config: DeviceConfig | None = None,
|
||||
protocol: BaseProtocol | None = None,
|
||||
) -> None:
|
||||
"""Create a new IotDevice instance."""
|
||||
super().__init__(host=host, config=config, protocol=protocol)
|
||||
|
||||
self._sys_info: Any = None # TODO: this is here to avoid changing tests
|
||||
self._supported_modules: Optional[Dict[str, IotModule]] = None
|
||||
self._legacy_features: Set[str] = set()
|
||||
self._children: Mapping[str, "IotDevice"] = {}
|
||||
self._supported_modules: dict[str, IotModule] | None = None
|
||||
self._legacy_features: set[str] = set()
|
||||
self._children: Mapping[str, IotDevice] = {}
|
||||
|
||||
@property
|
||||
def children(self) -> Sequence["IotDevice"]:
|
||||
def children(self) -> Sequence[IotDevice]:
|
||||
"""Return list of children."""
|
||||
return list(self._children.values())
|
||||
|
||||
@@ -203,9 +205,9 @@ class IotDevice(Device):
|
||||
self.modules[name] = module
|
||||
|
||||
def _create_request(
|
||||
self, target: str, cmd: str, arg: Optional[Dict] = None, child_ids=None
|
||||
self, target: str, cmd: str, arg: dict | None = None, child_ids=None
|
||||
):
|
||||
request: Dict[str, Any] = {target: {cmd: arg}}
|
||||
request: dict[str, Any] = {target: {cmd: arg}}
|
||||
if child_ids is not None:
|
||||
request = {"context": {"child_ids": child_ids}, target: {cmd: arg}}
|
||||
|
||||
@@ -219,7 +221,7 @@ class IotDevice(Device):
|
||||
raise KasaException("update() required prior accessing emeter")
|
||||
|
||||
async def _query_helper(
|
||||
self, target: str, cmd: str, arg: Optional[Dict] = None, child_ids=None
|
||||
self, target: str, cmd: str, arg: dict | None = None, child_ids=None
|
||||
) -> Any:
|
||||
"""Query device, return results or raise an exception.
|
||||
|
||||
@@ -256,13 +258,13 @@ class IotDevice(Device):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def features(self) -> Dict[str, Feature]:
|
||||
def features(self) -> dict[str, Feature]:
|
||||
"""Return a set of features that the device supports."""
|
||||
return self._features
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def supported_modules(self) -> List[str]:
|
||||
def supported_modules(self) -> list[str]:
|
||||
"""Return a set of modules supported by the device."""
|
||||
# TODO: this should rather be called `features`, but we don't want to break
|
||||
# the API now. Maybe just deprecate it and point the users to use this?
|
||||
@@ -274,7 +276,7 @@ class IotDevice(Device):
|
||||
"""Return True if device has an energy meter."""
|
||||
return "ENE" in self._legacy_features
|
||||
|
||||
async def get_sys_info(self) -> Dict[str, Any]:
|
||||
async def get_sys_info(self) -> dict[str, Any]:
|
||||
"""Retrieve system information."""
|
||||
return await self._query_helper("system", "get_sysinfo")
|
||||
|
||||
@@ -363,12 +365,12 @@ class IotDevice(Device):
|
||||
# responses on top of it so we remember
|
||||
# which modules are not supported, otherwise
|
||||
# every other update will query for them
|
||||
update: Dict = self._last_update.copy() if self._last_update else {}
|
||||
update: dict = self._last_update.copy() if self._last_update else {}
|
||||
for response in responses:
|
||||
update = {**update, **response}
|
||||
self._last_update = update
|
||||
|
||||
def update_from_discover_info(self, info: Dict[str, Any]) -> None:
|
||||
def update_from_discover_info(self, info: dict[str, Any]) -> None:
|
||||
"""Update state from info from the discover call."""
|
||||
self._discovery_info = info
|
||||
if "system" in info and (sys_info := info["system"].get("get_sysinfo")):
|
||||
@@ -380,7 +382,7 @@ class IotDevice(Device):
|
||||
# by the requires_update decorator
|
||||
self._set_sys_info(info)
|
||||
|
||||
def _set_sys_info(self, sys_info: Dict[str, Any]) -> None:
|
||||
def _set_sys_info(self, sys_info: dict[str, Any]) -> None:
|
||||
"""Set sys_info."""
|
||||
self._sys_info = sys_info
|
||||
if features := sys_info.get("feature"):
|
||||
@@ -388,7 +390,7 @@ class IotDevice(Device):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def sys_info(self) -> Dict[str, Any]:
|
||||
def sys_info(self) -> dict[str, Any]:
|
||||
"""
|
||||
Return system information.
|
||||
|
||||
@@ -405,7 +407,7 @@ class IotDevice(Device):
|
||||
return str(sys_info["model"])
|
||||
|
||||
@property # type: ignore
|
||||
def alias(self) -> Optional[str]:
|
||||
def alias(self) -> str | None:
|
||||
"""Return device name (alias)."""
|
||||
sys_info = self._sys_info
|
||||
return sys_info.get("alias") if sys_info else None
|
||||
@@ -422,18 +424,18 @@ class IotDevice(Device):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def timezone(self) -> Dict:
|
||||
def timezone(self) -> dict:
|
||||
"""Return the current timezone."""
|
||||
return self.modules["time"].timezone
|
||||
|
||||
async def get_time(self) -> Optional[datetime]:
|
||||
async def get_time(self) -> datetime | None:
|
||||
"""Return current time from the device, if available."""
|
||||
_LOGGER.warning(
|
||||
"Use `time` property instead, this call will be removed in the future."
|
||||
)
|
||||
return await self.modules["time"].get_time()
|
||||
|
||||
async def get_timezone(self) -> Dict:
|
||||
async def get_timezone(self) -> dict:
|
||||
"""Return timezone information."""
|
||||
_LOGGER.warning(
|
||||
"Use `timezone` property instead, this call will be removed in the future."
|
||||
@@ -442,7 +444,7 @@ class IotDevice(Device):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def hw_info(self) -> Dict:
|
||||
def hw_info(self) -> dict:
|
||||
"""Return hardware information.
|
||||
|
||||
This returns just a selection of sysinfo keys that are related to hardware.
|
||||
@@ -464,7 +466,7 @@ class IotDevice(Device):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def location(self) -> Dict:
|
||||
def location(self) -> dict:
|
||||
"""Return geographical location."""
|
||||
sys_info = self._sys_info
|
||||
loc = {"latitude": None, "longitude": None}
|
||||
@@ -482,7 +484,7 @@ class IotDevice(Device):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def rssi(self) -> Optional[int]:
|
||||
def rssi(self) -> int | None:
|
||||
"""Return WiFi signal strength (rssi)."""
|
||||
rssi = self._sys_info.get("rssi")
|
||||
return None if rssi is None else int(rssi)
|
||||
@@ -528,21 +530,21 @@ class IotDevice(Device):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def emeter_today(self) -> Optional[float]:
|
||||
def emeter_today(self) -> float | None:
|
||||
"""Return today's energy consumption in kWh."""
|
||||
self._verify_emeter()
|
||||
return self.modules["emeter"].emeter_today
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def emeter_this_month(self) -> Optional[float]:
|
||||
def emeter_this_month(self) -> float | None:
|
||||
"""Return this month's energy consumption in kWh."""
|
||||
self._verify_emeter()
|
||||
return self.modules["emeter"].emeter_this_month
|
||||
|
||||
async def get_emeter_daily(
|
||||
self, year: Optional[int] = None, month: Optional[int] = None, kwh: bool = True
|
||||
) -> Dict:
|
||||
self, year: int | None = None, month: int | None = None, kwh: bool = True
|
||||
) -> dict:
|
||||
"""Retrieve daily statistics for a given month.
|
||||
|
||||
:param year: year for which to retrieve statistics (default: this year)
|
||||
@@ -556,8 +558,8 @@ class IotDevice(Device):
|
||||
|
||||
@requires_update
|
||||
async def get_emeter_monthly(
|
||||
self, year: Optional[int] = None, kwh: bool = True
|
||||
) -> Dict:
|
||||
self, year: int | None = None, kwh: bool = True
|
||||
) -> dict:
|
||||
"""Retrieve monthly statistics for a given year.
|
||||
|
||||
:param year: year for which to retrieve statistics (default: this year)
|
||||
@@ -568,7 +570,7 @@ class IotDevice(Device):
|
||||
return await self.modules["emeter"].get_monthstat(year=year, kwh=kwh)
|
||||
|
||||
@requires_update
|
||||
async def erase_emeter_stats(self) -> Dict:
|
||||
async def erase_emeter_stats(self) -> dict:
|
||||
"""Erase energy meter statistics."""
|
||||
self._verify_emeter()
|
||||
return await self.modules["emeter"].erase_stats()
|
||||
@@ -588,11 +590,11 @@ class IotDevice(Device):
|
||||
"""
|
||||
await self._query_helper("system", "reboot", {"delay": delay})
|
||||
|
||||
async def turn_off(self, **kwargs) -> Dict:
|
||||
async def turn_off(self, **kwargs) -> dict:
|
||||
"""Turn off the device."""
|
||||
raise NotImplementedError("Device subclass needs to implement this.")
|
||||
|
||||
async def turn_on(self, **kwargs) -> Optional[Dict]:
|
||||
async def turn_on(self, **kwargs) -> dict | None:
|
||||
"""Turn device on."""
|
||||
raise NotImplementedError("Device subclass needs to implement this.")
|
||||
|
||||
@@ -604,7 +606,7 @@ class IotDevice(Device):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def on_since(self) -> Optional[datetime]:
|
||||
def on_since(self) -> datetime | None:
|
||||
"""Return pretty-printed on-time, or None if not available."""
|
||||
if "on_time" not in self._sys_info:
|
||||
return None
|
||||
@@ -626,7 +628,7 @@ class IotDevice(Device):
|
||||
"""
|
||||
return self.mac
|
||||
|
||||
async def wifi_scan(self) -> List[WifiNetwork]: # noqa: D202
|
||||
async def wifi_scan(self) -> list[WifiNetwork]: # noqa: D202
|
||||
"""Scan for available wifi networks."""
|
||||
|
||||
async def _scan(target):
|
||||
|
@@ -1,7 +1,9 @@
|
||||
"""Module for dimmers (currently only HS220)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any
|
||||
|
||||
from ..device_type import DeviceType
|
||||
from ..deviceconfig import DeviceConfig
|
||||
@@ -72,8 +74,8 @@ class IotDimmer(IotPlug):
|
||||
self,
|
||||
host: str,
|
||||
*,
|
||||
config: Optional[DeviceConfig] = None,
|
||||
protocol: Optional[BaseProtocol] = None,
|
||||
config: DeviceConfig | None = None,
|
||||
protocol: BaseProtocol | None = None,
|
||||
) -> None:
|
||||
super().__init__(host=host, config=config, protocol=protocol)
|
||||
self._device_type = DeviceType.Dimmer
|
||||
@@ -112,9 +114,7 @@ class IotDimmer(IotPlug):
|
||||
return int(sys_info["brightness"])
|
||||
|
||||
@requires_update
|
||||
async def set_brightness(
|
||||
self, brightness: int, *, transition: Optional[int] = 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.
|
||||
@@ -143,7 +143,7 @@ class IotDimmer(IotPlug):
|
||||
self.DIMMER_SERVICE, "set_brightness", {"brightness": brightness}
|
||||
)
|
||||
|
||||
async def turn_off(self, *, transition: Optional[int] = None, **kwargs):
|
||||
async def turn_off(self, *, transition: int | None = None, **kwargs):
|
||||
"""Turn the bulb off.
|
||||
|
||||
:param int transition: transition duration in milliseconds.
|
||||
@@ -154,7 +154,7 @@ class IotDimmer(IotPlug):
|
||||
return await super().turn_off()
|
||||
|
||||
@requires_update
|
||||
async def turn_on(self, *, transition: Optional[int] = None, **kwargs):
|
||||
async def turn_on(self, *, transition: int | None = None, **kwargs):
|
||||
"""Turn the bulb on.
|
||||
|
||||
:param int transition: transition duration in milliseconds.
|
||||
@@ -202,7 +202,7 @@ class IotDimmer(IotPlug):
|
||||
|
||||
@requires_update
|
||||
async def set_button_action(
|
||||
self, action_type: ActionType, action: ButtonAction, index: Optional[int] = None
|
||||
self, action_type: ActionType, action: ButtonAction, index: int | None = None
|
||||
):
|
||||
"""Set action to perform on button click/hold.
|
||||
|
||||
@@ -213,7 +213,7 @@ class IotDimmer(IotPlug):
|
||||
"""
|
||||
action_type_setter = f"set_{action_type}"
|
||||
|
||||
payload: Dict[str, Any] = {"mode": str(action)}
|
||||
payload: dict[str, Any] = {"mode": str(action)}
|
||||
if index is not None:
|
||||
payload["index"] = index
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
"""Module for light strips (KL430)."""
|
||||
|
||||
from typing import Dict, List, Optional
|
||||
from __future__ import annotations
|
||||
|
||||
from ..device_type import DeviceType
|
||||
from ..deviceconfig import DeviceConfig
|
||||
@@ -49,8 +49,8 @@ class IotLightStrip(IotBulb):
|
||||
self,
|
||||
host: str,
|
||||
*,
|
||||
config: Optional[DeviceConfig] = None,
|
||||
protocol: Optional[BaseProtocol] = None,
|
||||
config: DeviceConfig | None = None,
|
||||
protocol: BaseProtocol | None = None,
|
||||
) -> None:
|
||||
super().__init__(host=host, config=config, protocol=protocol)
|
||||
self._device_type = DeviceType.LightStrip
|
||||
@@ -63,7 +63,7 @@ class IotLightStrip(IotBulb):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def effect(self) -> Dict:
|
||||
def effect(self) -> dict:
|
||||
"""Return effect state.
|
||||
|
||||
Example:
|
||||
@@ -77,7 +77,7 @@ class IotLightStrip(IotBulb):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def effect_list(self) -> Optional[List[str]]:
|
||||
def effect_list(self) -> list[str] | None:
|
||||
"""Return built-in effects list.
|
||||
|
||||
Example:
|
||||
@@ -90,8 +90,8 @@ class IotLightStrip(IotBulb):
|
||||
self,
|
||||
effect: str,
|
||||
*,
|
||||
brightness: Optional[int] = None,
|
||||
transition: Optional[int] = None,
|
||||
brightness: int | None = None,
|
||||
transition: int | None = None,
|
||||
) -> None:
|
||||
"""Set an effect on the device.
|
||||
|
||||
@@ -118,7 +118,7 @@ class IotLightStrip(IotBulb):
|
||||
@requires_update
|
||||
async def set_custom_effect(
|
||||
self,
|
||||
effect_dict: Dict,
|
||||
effect_dict: dict,
|
||||
) -> None:
|
||||
"""Set a custom effect on the device.
|
||||
|
||||
|
@@ -1,7 +1,8 @@
|
||||
"""Module for smart plugs (HS100, HS110, ..)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Optional
|
||||
|
||||
from ..device_type import DeviceType
|
||||
from ..deviceconfig import DeviceConfig
|
||||
@@ -47,8 +48,8 @@ class IotPlug(IotDevice):
|
||||
self,
|
||||
host: str,
|
||||
*,
|
||||
config: Optional[DeviceConfig] = None,
|
||||
protocol: Optional[BaseProtocol] = None,
|
||||
config: DeviceConfig | None = None,
|
||||
protocol: BaseProtocol | None = None,
|
||||
) -> None:
|
||||
super().__init__(host=host, config=config, protocol=protocol)
|
||||
self._device_type = DeviceType.Plug
|
||||
@@ -108,8 +109,8 @@ class IotWallSwitch(IotPlug):
|
||||
self,
|
||||
host: str,
|
||||
*,
|
||||
config: Optional[DeviceConfig] = None,
|
||||
protocol: Optional[BaseProtocol] = None,
|
||||
config: DeviceConfig | None = None,
|
||||
protocol: BaseProtocol | None = None,
|
||||
) -> None:
|
||||
super().__init__(host=host, config=config, protocol=protocol)
|
||||
self._device_type = DeviceType.WallSwitch
|
||||
|
@@ -1,9 +1,11 @@
|
||||
"""Module for multi-socket devices (HS300, HS107, KP303, ..)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, DefaultDict, Dict, Optional
|
||||
from typing import Any
|
||||
|
||||
from ..device_type import DeviceType
|
||||
from ..deviceconfig import DeviceConfig
|
||||
@@ -23,7 +25,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
def merge_sums(dicts):
|
||||
"""Merge the sum of dicts."""
|
||||
total_dict: DefaultDict[int, float] = defaultdict(lambda: 0.0)
|
||||
total_dict: defaultdict[int, float] = defaultdict(lambda: 0.0)
|
||||
for sum_dict in dicts:
|
||||
for day, value in sum_dict.items():
|
||||
total_dict[day] += value
|
||||
@@ -86,8 +88,8 @@ class IotStrip(IotDevice):
|
||||
self,
|
||||
host: str,
|
||||
*,
|
||||
config: Optional[DeviceConfig] = None,
|
||||
protocol: Optional[BaseProtocol] = None,
|
||||
config: DeviceConfig | None = None,
|
||||
protocol: BaseProtocol | None = None,
|
||||
) -> None:
|
||||
super().__init__(host=host, config=config, protocol=protocol)
|
||||
self.emeter_type = "emeter"
|
||||
@@ -137,7 +139,7 @@ class IotStrip(IotDevice):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def on_since(self) -> Optional[datetime]:
|
||||
def on_since(self) -> datetime | None:
|
||||
"""Return the maximum on-time of all outlets."""
|
||||
if self.is_off:
|
||||
return None
|
||||
@@ -170,8 +172,8 @@ class IotStrip(IotDevice):
|
||||
|
||||
@requires_update
|
||||
async def get_emeter_daily(
|
||||
self, year: Optional[int] = None, month: Optional[int] = None, kwh: bool = True
|
||||
) -> Dict:
|
||||
self, year: int | None = None, month: int | None = None, kwh: bool = True
|
||||
) -> dict:
|
||||
"""Retrieve daily statistics for a given month.
|
||||
|
||||
:param year: year for which to retrieve statistics (default: this year)
|
||||
@@ -186,8 +188,8 @@ class IotStrip(IotDevice):
|
||||
|
||||
@requires_update
|
||||
async def get_emeter_monthly(
|
||||
self, year: Optional[int] = None, kwh: bool = True
|
||||
) -> Dict:
|
||||
self, year: int | None = None, kwh: bool = True
|
||||
) -> dict:
|
||||
"""Retrieve monthly statistics for a given year.
|
||||
|
||||
:param year: year for which to retrieve statistics (default: this year)
|
||||
@@ -197,7 +199,7 @@ class IotStrip(IotDevice):
|
||||
"get_emeter_monthly", {"year": year, "kwh": kwh}
|
||||
)
|
||||
|
||||
async def _async_get_emeter_sum(self, func: str, kwargs: Dict[str, Any]) -> Dict:
|
||||
async def _async_get_emeter_sum(self, func: str, kwargs: dict[str, Any]) -> dict:
|
||||
"""Retreive emeter stats for a time period from children."""
|
||||
self._verify_emeter()
|
||||
return merge_sums(
|
||||
@@ -212,13 +214,13 @@ class IotStrip(IotDevice):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def emeter_this_month(self) -> Optional[float]:
|
||||
def emeter_this_month(self) -> float | None:
|
||||
"""Return this month's energy consumption in kWh."""
|
||||
return sum(plug.emeter_this_month for plug in self.children)
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def emeter_today(self) -> Optional[float]:
|
||||
def emeter_today(self) -> float | None:
|
||||
"""Return this month's energy consumption in kWh."""
|
||||
return sum(plug.emeter_today for plug in self.children)
|
||||
|
||||
@@ -243,7 +245,7 @@ class IotStripPlug(IotPlug):
|
||||
The plug inherits (most of) the system information from the parent.
|
||||
"""
|
||||
|
||||
def __init__(self, host: str, parent: "IotStrip", child_id: str) -> None:
|
||||
def __init__(self, host: str, parent: IotStrip, child_id: str) -> None:
|
||||
super().__init__(host)
|
||||
|
||||
self.parent = parent
|
||||
@@ -262,16 +264,14 @@ class IotStripPlug(IotPlug):
|
||||
"""
|
||||
await self._modular_update({})
|
||||
|
||||
def _create_emeter_request(
|
||||
self, year: Optional[int] = None, month: Optional[int] = None
|
||||
):
|
||||
def _create_emeter_request(self, year: int | None = None, month: int | None = None):
|
||||
"""Create a request for requesting all emeter statistics at once."""
|
||||
if year is None:
|
||||
year = datetime.now().year
|
||||
if month is None:
|
||||
month = datetime.now().month
|
||||
|
||||
req: Dict[str, Any] = {}
|
||||
req: dict[str, Any] = {}
|
||||
|
||||
merge(req, self._create_request("emeter", "get_realtime"))
|
||||
merge(req, self._create_request("emeter", "get_monthstat", {"year": year}))
|
||||
@@ -285,16 +285,16 @@ class IotStripPlug(IotPlug):
|
||||
return req
|
||||
|
||||
def _create_request(
|
||||
self, target: str, cmd: str, arg: Optional[Dict] = None, child_ids=None
|
||||
self, target: str, cmd: str, arg: dict | None = None, child_ids=None
|
||||
):
|
||||
request: Dict[str, Any] = {
|
||||
request: dict[str, Any] = {
|
||||
"context": {"child_ids": [self.child_id]},
|
||||
target: {cmd: arg},
|
||||
}
|
||||
return request
|
||||
|
||||
async def _query_helper(
|
||||
self, target: str, cmd: str, arg: Optional[Dict] = None, child_ids=None
|
||||
self, target: str, cmd: str, arg: dict | None = None, child_ids=None
|
||||
) -> Any:
|
||||
"""Override query helper to include the child_ids."""
|
||||
return await self.parent._query_helper(
|
||||
@@ -335,14 +335,14 @@ class IotStripPlug(IotPlug):
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def next_action(self) -> Dict:
|
||||
def next_action(self) -> dict:
|
||||
"""Return next scheduled(?) action."""
|
||||
info = self._get_child_info()
|
||||
return info["next_action"]
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def on_since(self) -> Optional[datetime]:
|
||||
def on_since(self) -> datetime | None:
|
||||
"""Return on-time, if available."""
|
||||
if self.is_off:
|
||||
return None
|
||||
@@ -359,7 +359,7 @@ class IotStripPlug(IotPlug):
|
||||
sys_info = self.parent.sys_info
|
||||
return f"Socket for {sys_info['model']}"
|
||||
|
||||
def _get_child_info(self) -> Dict:
|
||||
def _get_child_info(self) -> dict:
|
||||
"""Return the subdevice information for this device."""
|
||||
for plug in self.parent.sys_info["children"]:
|
||||
if plug["id"] == self.child_id:
|
||||
|
@@ -1,7 +1,8 @@
|
||||
"""Implementation of the emeter module."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional, Union
|
||||
|
||||
from ...emeterstatus import EmeterStatus
|
||||
from .usage import Usage
|
||||
@@ -16,7 +17,7 @@ class Emeter(Usage):
|
||||
return EmeterStatus(self.data["get_realtime"])
|
||||
|
||||
@property
|
||||
def emeter_today(self) -> Optional[float]:
|
||||
def emeter_today(self) -> float | None:
|
||||
"""Return today's energy consumption in kWh."""
|
||||
raw_data = self.daily_data
|
||||
today = datetime.now().day
|
||||
@@ -24,7 +25,7 @@ class Emeter(Usage):
|
||||
return data.get(today)
|
||||
|
||||
@property
|
||||
def emeter_this_month(self) -> Optional[float]:
|
||||
def emeter_this_month(self) -> float | None:
|
||||
"""Return this month's energy consumption in kWh."""
|
||||
raw_data = self.monthly_data
|
||||
current_month = datetime.now().month
|
||||
@@ -42,7 +43,7 @@ class Emeter(Usage):
|
||||
"""Return real-time statistics."""
|
||||
return await self.call("get_realtime")
|
||||
|
||||
async def get_daystat(self, *, year=None, month=None, kwh=True) -> Dict:
|
||||
async def get_daystat(self, *, year=None, month=None, kwh=True) -> dict:
|
||||
"""Return daily stats for the given year & month.
|
||||
|
||||
The return value is a dictionary of {day: energy, ...}.
|
||||
@@ -51,7 +52,7 @@ class Emeter(Usage):
|
||||
data = self._convert_stat_data(data["day_list"], entry_key="day", kwh=kwh)
|
||||
return data
|
||||
|
||||
async def get_monthstat(self, *, year=None, kwh=True) -> Dict:
|
||||
async def get_monthstat(self, *, year=None, kwh=True) -> dict:
|
||||
"""Return monthly stats for the given year.
|
||||
|
||||
The return value is a dictionary of {month: energy, ...}.
|
||||
@@ -62,11 +63,11 @@ class Emeter(Usage):
|
||||
|
||||
def _convert_stat_data(
|
||||
self,
|
||||
data: List[Dict[str, Union[int, float]]],
|
||||
data: list[dict[str, int | float]],
|
||||
entry_key: str,
|
||||
kwh: bool = True,
|
||||
key: Optional[int] = None,
|
||||
) -> Dict[Union[int, float], Union[int, float]]:
|
||||
key: int | None = None,
|
||||
) -> dict[int | float, int | float]:
|
||||
"""Return emeter information keyed with the day/month.
|
||||
|
||||
The incoming data is a list of dictionaries::
|
||||
|
@@ -1,7 +1,8 @@
|
||||
"""Implementation of the motion detection (PIR) module found in some dimmers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
from ...exceptions import KasaException
|
||||
from ..iotmodule import IotModule
|
||||
@@ -43,7 +44,7 @@ class Motion(IotModule):
|
||||
return await self.call("set_enable", {"enable": int(state)})
|
||||
|
||||
async def set_range(
|
||||
self, *, range: Optional[Range] = None, custom_range: Optional[int] = None
|
||||
self, *, range: Range | None = None, custom_range: int | None = None
|
||||
):
|
||||
"""Set the range for the sensor.
|
||||
|
||||
|
@@ -1,5 +1,7 @@
|
||||
"""Base implementation for all rule-based modules."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Optional
|
||||
@@ -37,20 +39,20 @@ class Rule(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
enable: bool
|
||||
wday: List[int]
|
||||
wday: List[int] # noqa: UP006
|
||||
repeat: bool
|
||||
|
||||
# start action
|
||||
sact: Optional[Action]
|
||||
sact: Optional[Action] # noqa: UP007
|
||||
stime_opt: TimeOption
|
||||
smin: int
|
||||
|
||||
eact: Optional[Action]
|
||||
eact: Optional[Action] # noqa: UP007
|
||||
etime_opt: TimeOption
|
||||
emin: int
|
||||
|
||||
# Only on bulbs
|
||||
s_light: Optional[Dict]
|
||||
s_light: Optional[Dict] # noqa: UP006,UP007
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -65,7 +67,7 @@ class RuleModule(IotModule):
|
||||
return merge(q, self.query_for_command("get_next_action"))
|
||||
|
||||
@property
|
||||
def rules(self) -> List[Rule]:
|
||||
def rules(self) -> list[Rule]:
|
||||
"""Return the list of rules for the service."""
|
||||
try:
|
||||
return [
|
||||
|
@@ -1,7 +1,8 @@
|
||||
"""Implementation of the usage interface."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Dict
|
||||
|
||||
from ..iotmodule import IotModule, merge
|
||||
|
||||
@@ -58,7 +59,7 @@ class Usage(IotModule):
|
||||
return entry["time"]
|
||||
return None
|
||||
|
||||
async def get_raw_daystat(self, *, year=None, month=None) -> Dict:
|
||||
async def get_raw_daystat(self, *, year=None, month=None) -> dict:
|
||||
"""Return raw daily stats for the given year & month."""
|
||||
if year is None:
|
||||
year = datetime.now().year
|
||||
@@ -67,14 +68,14 @@ class Usage(IotModule):
|
||||
|
||||
return await self.call("get_daystat", {"year": year, "month": month})
|
||||
|
||||
async def get_raw_monthstat(self, *, year=None) -> Dict:
|
||||
async def get_raw_monthstat(self, *, year=None) -> dict:
|
||||
"""Return raw monthly stats for the given year."""
|
||||
if year is None:
|
||||
year = datetime.now().year
|
||||
|
||||
return await self.call("get_monthstat", {"year": year})
|
||||
|
||||
async def get_daystat(self, *, year=None, month=None) -> Dict:
|
||||
async def get_daystat(self, *, year=None, month=None) -> dict:
|
||||
"""Return daily stats for the given year & month.
|
||||
|
||||
The return value is a dictionary of {day: time, ...}.
|
||||
@@ -83,7 +84,7 @@ class Usage(IotModule):
|
||||
data = self._convert_stat_data(data["day_list"], entry_key="day")
|
||||
return data
|
||||
|
||||
async def get_monthstat(self, *, year=None) -> Dict:
|
||||
async def get_monthstat(self, *, year=None) -> dict:
|
||||
"""Return monthly stats for the given year.
|
||||
|
||||
The return value is a dictionary of {month: time, ...}.
|
||||
@@ -96,7 +97,7 @@ class Usage(IotModule):
|
||||
"""Erase all stats."""
|
||||
return await self.call("erase_runtime_stat")
|
||||
|
||||
def _convert_stat_data(self, data, entry_key) -> Dict:
|
||||
def _convert_stat_data(self, data, entry_key) -> dict:
|
||||
"""Return usage information keyed with the day/month.
|
||||
|
||||
The incoming data is a list of dictionaries::
|
||||
|
Reference in New Issue
Block a user