mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-10-13 02:48:07 +00:00
General cleanups all around (janitoring) (#63)
* Move tests to device-type specific test files to make improvements more approachable * protocol: remove the port parameter from query, as there are no other known ports, fix docstrings * Revise docstrings, remove superfluous information and remove unused methods ({set,get_icon} and set_time) * cli: indent device output to make it more easily readable when having multiple devices * remove adjust flake8 ignores (we have no setup.py anymore) * pyproject: include cli tool to coverage, add config for interrogate (docstring coverage) * bulb: raise exception on color_temp error cases instead of returning zero values * improve bulb tests, simplify conftest * strip: rename plugs property to children and move it to smartdevice
This commit is contained in:
@@ -1,11 +1,8 @@
|
||||
"""Module for multi-socket devices (HS300, HS107).
|
||||
|
||||
.. todo:: describe how this interfaces with single plugs.
|
||||
"""
|
||||
"""Module for multi-socket devices (HS300, HS107, KP303, ..)."""
|
||||
import logging
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, DefaultDict, Dict, List, Optional
|
||||
from typing import Any, DefaultDict, Dict, Optional
|
||||
|
||||
from kasa.smartdevice import (
|
||||
DeviceType,
|
||||
@@ -49,13 +46,12 @@ class SmartStrip(SmartDevice):
|
||||
super().__init__(host=host)
|
||||
self.emeter_type = "emeter"
|
||||
self._device_type = DeviceType.Strip
|
||||
self.plugs: List[SmartStripPlug] = []
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def is_on(self) -> bool:
|
||||
"""Return if any of the outlets are on."""
|
||||
for plug in self.plugs:
|
||||
for plug in self.children:
|
||||
is_on = plug.is_on
|
||||
if is_on:
|
||||
return True
|
||||
@@ -69,46 +65,24 @@ class SmartStrip(SmartDevice):
|
||||
await super().update()
|
||||
|
||||
# Initialize the child devices during the first update.
|
||||
if not self.plugs:
|
||||
if not self.children:
|
||||
children = self.sys_info["children"]
|
||||
_LOGGER.debug("Initializing %s child sockets", len(children))
|
||||
for child in children:
|
||||
self.plugs.append(
|
||||
self.children.append(
|
||||
SmartStripPlug(self.host, parent=self, child_id=child["id"])
|
||||
)
|
||||
|
||||
async def turn_on(self):
|
||||
"""Turn the strip on.
|
||||
|
||||
:raises SmartDeviceException: on error
|
||||
"""
|
||||
"""Turn the strip on."""
|
||||
await self._query_helper("system", "set_relay_state", {"state": 1})
|
||||
await self.update()
|
||||
|
||||
async def turn_off(self):
|
||||
"""Turn the strip off.
|
||||
|
||||
:raises SmartDeviceException: on error
|
||||
"""
|
||||
"""Turn the strip off."""
|
||||
await self._query_helper("system", "set_relay_state", {"state": 0})
|
||||
await self.update()
|
||||
|
||||
def get_plug_by_name(self, name: str) -> "SmartStripPlug":
|
||||
"""Return child plug for given name."""
|
||||
for p in self.plugs:
|
||||
if p.alias == name:
|
||||
return p
|
||||
|
||||
raise SmartDeviceException(f"Device has no child with {name}")
|
||||
|
||||
def get_plug_by_index(self, index: int) -> "SmartStripPlug":
|
||||
"""Return child plug for given index."""
|
||||
if index + 1 > len(self.plugs) or index < 0:
|
||||
raise SmartDeviceException(
|
||||
f"Invalid index {index}, device has {len(self.plugs)} plugs"
|
||||
)
|
||||
return self.plugs[index]
|
||||
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def on_since(self) -> Optional[datetime]:
|
||||
@@ -116,25 +90,17 @@ class SmartStrip(SmartDevice):
|
||||
if self.is_off:
|
||||
return None
|
||||
|
||||
return max(plug.on_since for plug in self.plugs if plug.on_since is not 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.
|
||||
|
||||
:return: True if led is on, False otherwise
|
||||
:rtype: 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).
|
||||
|
||||
:param bool state: True to set led on, False to set led off
|
||||
:raises SmartDeviceException: on error
|
||||
"""
|
||||
"""Set the state of the led (night mode)."""
|
||||
await self._query_helper("system", "set_led_off", {"off": int(not state)})
|
||||
await self.update()
|
||||
|
||||
@@ -144,38 +110,23 @@ class SmartStrip(SmartDevice):
|
||||
"""Return strip-specific state information.
|
||||
|
||||
:return: Strip information dict, keys in user-presentable form.
|
||||
:rtype: dict
|
||||
"""
|
||||
return {
|
||||
"LED state": self.led,
|
||||
"Childs count": len(self.plugs),
|
||||
"Childs count": len(self.children),
|
||||
"On since": self.on_since,
|
||||
}
|
||||
|
||||
async def current_consumption(self) -> float:
|
||||
"""Get the current power consumption in watts.
|
||||
|
||||
:return: the current power consumption in watts.
|
||||
:rtype: float
|
||||
:raises SmartDeviceException: on error
|
||||
"""
|
||||
consumption = sum([await plug.current_consumption() for plug in self.plugs])
|
||||
"""Get the current power consumption in watts."""
|
||||
consumption = sum([await plug.current_consumption() for plug in self.children])
|
||||
|
||||
return consumption
|
||||
|
||||
async def get_icon(self) -> Dict:
|
||||
"""Icon for the device.
|
||||
|
||||
Overriden to keep the API, as the SmartStrip and children do not
|
||||
have icons, we just return dummy strings.
|
||||
"""
|
||||
return {"icon": "SMARTSTRIP-DUMMY", "hash": "SMARTSTRIP-DUMMY"}
|
||||
|
||||
async def set_alias(self, alias: str) -> None:
|
||||
"""Set the alias for the strip.
|
||||
|
||||
:param alias: new alias
|
||||
:raises SmartDeviceException: on error
|
||||
"""
|
||||
return await super().set_alias(alias)
|
||||
|
||||
@@ -190,11 +141,9 @@ class SmartStrip(SmartDevice):
|
||||
month)
|
||||
:param kwh: return usage in kWh (default: True)
|
||||
:return: mapping of day of month to value
|
||||
:rtype: dict
|
||||
:raises SmartDeviceException: on error
|
||||
"""
|
||||
emeter_daily: DefaultDict[int, float] = defaultdict(lambda: 0.0)
|
||||
for plug in self.plugs:
|
||||
for plug in self.children:
|
||||
plug_emeter_daily = await plug.get_emeter_daily(
|
||||
year=year, month=month, kwh=kwh
|
||||
)
|
||||
@@ -208,12 +157,9 @@ class SmartStrip(SmartDevice):
|
||||
|
||||
:param year: year for which to retrieve statistics (default: this year)
|
||||
:param kwh: return usage in kWh (default: True)
|
||||
:return: dict: mapping of month to value
|
||||
:rtype: dict
|
||||
:raises SmartDeviceException: on error
|
||||
"""
|
||||
emeter_monthly: DefaultDict[int, float] = defaultdict(lambda: 0.0)
|
||||
for plug in self.plugs:
|
||||
for plug in self.children:
|
||||
plug_emeter_monthly = await plug.get_emeter_monthly(year=year, kwh=kwh)
|
||||
for month, value in plug_emeter_monthly:
|
||||
emeter_monthly[month] += value
|
||||
@@ -222,11 +168,8 @@ class SmartStrip(SmartDevice):
|
||||
|
||||
@requires_update
|
||||
async def erase_emeter_stats(self):
|
||||
"""Erase energy meter statistics for all plugs.
|
||||
|
||||
:raises SmartDeviceException: on error
|
||||
"""
|
||||
for plug in self.plugs:
|
||||
"""Erase energy meter statistics for all plugs."""
|
||||
for plug in self.children:
|
||||
await plug.erase_emeter_stats()
|
||||
|
||||
|
||||
@@ -267,10 +210,7 @@ class SmartStripPlug(SmartPlug):
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def is_on(self) -> bool:
|
||||
"""Return whether device is on.
|
||||
|
||||
:return: True if device is on, False otherwise
|
||||
"""
|
||||
"""Return whether device is on."""
|
||||
info = self._get_child_info()
|
||||
return info["state"]
|
||||
|
||||
@@ -280,9 +220,6 @@ class SmartStripPlug(SmartPlug):
|
||||
"""Return the state of the led.
|
||||
|
||||
This is always false for subdevices.
|
||||
|
||||
:return: True if led is on, False otherwise
|
||||
:rtype: bool
|
||||
"""
|
||||
return False
|
||||
|
||||
@@ -304,11 +241,7 @@ class SmartStripPlug(SmartPlug):
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def alias(self) -> str:
|
||||
"""Return device name (alias).
|
||||
|
||||
:return: Device name aka alias.
|
||||
:rtype: str
|
||||
"""
|
||||
"""Return device name (alias)."""
|
||||
info = self._get_child_info()
|
||||
return info["alias"]
|
||||
|
||||
@@ -322,11 +255,7 @@ class SmartStripPlug(SmartPlug):
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def on_since(self) -> Optional[datetime]:
|
||||
"""Return pretty-printed on-time.
|
||||
|
||||
:return: datetime for on since
|
||||
:rtype: datetime
|
||||
"""
|
||||
"""Return on-time, if available."""
|
||||
if self.is_off:
|
||||
return None
|
||||
|
||||
@@ -338,21 +267,14 @@ class SmartStripPlug(SmartPlug):
|
||||
@property # type: ignore
|
||||
@requires_update
|
||||
def model(self) -> str:
|
||||
"""Return device model for a child socket.
|
||||
|
||||
:return: device model
|
||||
:rtype: str
|
||||
:raises SmartDeviceException: on error
|
||||
"""
|
||||
"""Return device model for a child socket."""
|
||||
sys_info = self.parent.sys_info
|
||||
return f"Socket for {sys_info['model']}"
|
||||
|
||||
def _get_child_info(self) -> Dict:
|
||||
"""Return the subdevice information for this device.
|
||||
|
||||
:raises SmartDeviceException: if the information is not found.
|
||||
"""
|
||||
"""Return the subdevice information for this device."""
|
||||
for plug in self.parent.sys_info["children"]:
|
||||
if plug["id"] == self.child_id:
|
||||
return plug
|
||||
|
||||
raise SmartDeviceException(f"Unable to find children {self.child_id}")
|
||||
|
Reference in New Issue
Block a user