Remove SmartPlug in favor of SmartDevice (#781)

With the move towards autodetecting available features, there is no reason to keep SmartPlug around.

kasa.smart.SmartPlug is removed in favor of kasa.smart.SmartDevice which offers the same functionality.
Information about auto_off can be accessed using Features of the AutoOffModule on supported devices.

Co-authored-by: Steven B. <51370195+sdb9696@users.noreply.github.com>
This commit is contained in:
Teemu R 2024-02-22 14:34:55 +01:00 committed by GitHub
parent 8c39e81a40
commit d9d2f1a430
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 70 additions and 61 deletions

View File

@ -134,7 +134,6 @@ if TYPE_CHECKING:
from . import smart from . import smart
smart.SmartDevice("127.0.0.1") smart.SmartDevice("127.0.0.1")
smart.SmartPlug("127.0.0.1")
smart.SmartBulb("127.0.0.1") smart.SmartBulb("127.0.0.1")
iot.IotDevice("127.0.0.1") iot.IotDevice("127.0.0.1")
iot.IotPlug("127.0.0.1") iot.IotPlug("127.0.0.1")

View File

@ -27,7 +27,7 @@ from kasa import (
) )
from kasa.discover import DiscoveryResult from kasa.discover import DiscoveryResult
from kasa.iot import IotBulb, IotDevice, IotDimmer, IotLightStrip, IotPlug, IotStrip from kasa.iot import IotBulb, IotDevice, IotDimmer, IotLightStrip, IotPlug, IotStrip
from kasa.smart import SmartBulb, SmartDevice, SmartPlug from kasa.smart import SmartBulb, SmartDevice
try: try:
from pydantic.v1 import ValidationError from pydantic.v1 import ValidationError
@ -72,7 +72,7 @@ TYPE_TO_CLASS = {
"iot.dimmer": IotDimmer, "iot.dimmer": IotDimmer,
"iot.strip": IotStrip, "iot.strip": IotStrip,
"iot.lightstrip": IotLightStrip, "iot.lightstrip": IotLightStrip,
"smart.plug": SmartPlug, "smart.plug": SmartDevice,
"smart.bulb": SmartBulb, "smart.bulb": SmartBulb,
} }

View File

@ -194,32 +194,32 @@ class Device(ABC):
@property @property
def is_bulb(self) -> bool: def is_bulb(self) -> bool:
"""Return True if the device is a bulb.""" """Return True if the device is a bulb."""
return self._device_type == DeviceType.Bulb return self.device_type == DeviceType.Bulb
@property @property
def is_light_strip(self) -> bool: def is_light_strip(self) -> bool:
"""Return True if the device is a led strip.""" """Return True if the device is a led strip."""
return self._device_type == DeviceType.LightStrip return self.device_type == DeviceType.LightStrip
@property @property
def is_plug(self) -> bool: def is_plug(self) -> bool:
"""Return True if the device is a plug.""" """Return True if the device is a plug."""
return self._device_type == DeviceType.Plug return self.device_type == DeviceType.Plug
@property @property
def is_strip(self) -> bool: def is_strip(self) -> bool:
"""Return True if the device is a strip.""" """Return True if the device is a strip."""
return self._device_type == DeviceType.Strip return self.device_type == DeviceType.Strip
@property @property
def is_strip_socket(self) -> bool: def is_strip_socket(self) -> bool:
"""Return True if the device is a strip socket.""" """Return True if the device is a strip socket."""
return self._device_type == DeviceType.StripSocket return self.device_type == DeviceType.StripSocket
@property @property
def is_dimmer(self) -> bool: def is_dimmer(self) -> bool:
"""Return True if the device is a dimmer.""" """Return True if the device is a dimmer."""
return self._device_type == DeviceType.Dimmer return self.device_type == DeviceType.Dimmer
@property @property
def is_dimmable(self) -> bool: def is_dimmable(self) -> bool:
@ -354,9 +354,9 @@ class Device(ABC):
def __repr__(self): def __repr__(self):
if self._last_update is None: if self._last_update is None:
return f"<{self._device_type} at {self.host} - update() needed>" return f"<{self.device_type} at {self.host} - update() needed>"
return ( return (
f"<{self._device_type} model {self.model} at {self.host}" f"<{self.device_type} model {self.model} at {self.host}"
f" ({self.alias}), is_on: {self.is_on}" f" ({self.alias}), is_on: {self.is_on}"
f" - dev specific: {self.state_information}>" f" - dev specific: {self.state_information}>"
) )

View File

@ -14,7 +14,7 @@ from .protocol import (
BaseProtocol, BaseProtocol,
BaseTransport, BaseTransport,
) )
from .smart import SmartBulb, SmartPlug from .smart import SmartBulb, SmartDevice
from .smartprotocol import SmartProtocol from .smartprotocol import SmartProtocol
from .xortransport import XorTransport from .xortransport import XorTransport
@ -135,10 +135,10 @@ def get_device_class_from_sys_info(info: Dict[str, Any]) -> Type[IotDevice]:
def get_device_class_from_family(device_type: str) -> Optional[Type[Device]]: def get_device_class_from_family(device_type: str) -> Optional[Type[Device]]:
"""Return the device class from the type name.""" """Return the device class from the type name."""
supported_device_types: Dict[str, Type[Device]] = { supported_device_types: Dict[str, Type[Device]] = {
"SMART.TAPOPLUG": SmartPlug, "SMART.TAPOPLUG": SmartDevice,
"SMART.TAPOBULB": SmartBulb, "SMART.TAPOBULB": SmartBulb,
"SMART.TAPOSWITCH": SmartBulb, "SMART.TAPOSWITCH": SmartBulb,
"SMART.KASAPLUG": SmartPlug, "SMART.KASAPLUG": SmartDevice,
"SMART.KASASWITCH": SmartBulb, "SMART.KASASWITCH": SmartBulb,
"IOT.SMARTPLUGSWITCH": IotPlug, "IOT.SMARTPLUGSWITCH": IotPlug,
"IOT.SMARTBULB": IotBulb, "IOT.SMARTBULB": IotBulb,

View File

@ -2,6 +2,5 @@
from .smartbulb import SmartBulb from .smartbulb import SmartBulb
from .smartchilddevice import SmartChildDevice from .smartchilddevice import SmartChildDevice
from .smartdevice import SmartDevice from .smartdevice import SmartDevice
from .smartplug import SmartPlug
__all__ = ["SmartDevice", "SmartPlug", "SmartBulb", "SmartChildDevice"] __all__ = ["SmartDevice", "SmartBulb", "SmartChildDevice"]

View File

@ -485,3 +485,28 @@ class SmartDevice(Device):
Note, this does not downgrade the firmware. Note, this does not downgrade the firmware.
""" """
await self.protocol.query("device_reset") await self.protocol.query("device_reset")
@property
def device_type(self) -> DeviceType:
"""Return the device type."""
if self._device_type is not DeviceType.Unknown:
return self._device_type
if self.children:
if "SMART.TAPOHUB" in self.sys_info["type"]:
pass # TODO: placeholder for future hub PR
else:
self._device_type = DeviceType.Strip
elif "light_strip" in self._components:
self._device_type = DeviceType.LightStrip
elif "dimmer_calibration" in self._components:
self._device_type = DeviceType.Dimmer
elif "brightness" in self._components:
self._device_type = DeviceType.Bulb
elif "PLUG" in self.sys_info["type"]:
self._device_type = DeviceType.Plug
else:
_LOGGER.warning("Unknown device type, falling back to plug")
self._device_type = DeviceType.Plug
return self._device_type

View File

@ -1,37 +0,0 @@
"""Module for a TAPO Plug."""
import logging
from typing import Any, Dict, Optional
from ..device_type import DeviceType
from ..deviceconfig import DeviceConfig
from ..plug import Plug
from ..smartprotocol import SmartProtocol
from .smartdevice import SmartDevice
_LOGGER = logging.getLogger(__name__)
class SmartPlug(SmartDevice, Plug):
"""Class to represent a TAPO Plug."""
def __init__(
self,
host: str,
*,
config: Optional[DeviceConfig] = None,
protocol: Optional[SmartProtocol] = None,
) -> None:
super().__init__(host=host, config=config, protocol=protocol)
self._device_type = DeviceType.Plug
@property
def state_information(self) -> Dict[str, Any]:
"""Return the key state information."""
return {
**super().state_information,
**{
"On since": self.on_since,
"auto_off_status": self._info.get("auto_off_status"),
"auto_off_remain_time": self._info.get("auto_off_remain_time"),
},
}

View File

@ -20,7 +20,7 @@ from kasa import (
) )
from kasa.iot import IotBulb, IotDimmer, IotLightStrip, IotPlug, IotStrip from kasa.iot import IotBulb, IotDimmer, IotLightStrip, IotPlug, IotStrip
from kasa.protocol import BaseTransport from kasa.protocol import BaseTransport
from kasa.smart import SmartBulb, SmartPlug from kasa.smart import SmartBulb, SmartDevice
from kasa.xortransport import XorEncryption from kasa.xortransport import XorEncryption
from .fakeprotocol_iot import FakeIotProtocol from .fakeprotocol_iot import FakeIotProtocol
@ -108,7 +108,6 @@ PLUGS_SMART = {
"EP25", "EP25",
"KS205", "KS205",
"P125M", "P125M",
"P135",
"S505", "S505",
"TP15", "TP15",
} }
@ -121,7 +120,7 @@ STRIPS_SMART = {"P300", "TP25"}
STRIPS = {*STRIPS_IOT, *STRIPS_SMART} STRIPS = {*STRIPS_IOT, *STRIPS_SMART}
DIMMERS_IOT = {"ES20M", "HS220", "KS220M", "KS230", "KP405"} DIMMERS_IOT = {"ES20M", "HS220", "KS220M", "KS230", "KP405"}
DIMMERS_SMART = {"S500D"} DIMMERS_SMART = {"S500D", "P135"}
DIMMERS = { DIMMERS = {
*DIMMERS_IOT, *DIMMERS_IOT,
*DIMMERS_SMART, *DIMMERS_SMART,
@ -346,7 +345,7 @@ def device_for_file(model, protocol):
if protocol == "SMART": if protocol == "SMART":
for d in PLUGS_SMART: for d in PLUGS_SMART:
if d in model: if d in model:
return SmartPlug return SmartDevice
for d in BULBS_SMART: for d in BULBS_SMART:
if d in model: if d in model:
return SmartBulb return SmartBulb
@ -355,7 +354,7 @@ def device_for_file(model, protocol):
return SmartBulb return SmartBulb
for d in STRIPS_SMART: for d in STRIPS_SMART:
if d in model: if d in model:
return SmartPlug return SmartDevice
else: else:
for d in STRIPS_IOT: for d in STRIPS_IOT:
if d in model: if d in model:

View File

@ -37,9 +37,6 @@ async def test_led(dev):
@plug_smart @plug_smart
async def test_plug_device_info(dev): async def test_plug_device_info(dev):
assert dev._info is not None assert dev._info is not None
# PLUG_SCHEMA(dev.sys_info)
assert dev.model is not None assert dev.model is not None
assert dev.device_type == DeviceType.Plug or dev.device_type == DeviceType.Strip assert dev.device_type == DeviceType.Plug or dev.device_type == DeviceType.Strip
# assert dev.is_plug or dev.is_strip

View File

@ -22,16 +22,21 @@ from voluptuous import (
import kasa import kasa
from kasa import Credentials, Device, DeviceConfig, KasaException from kasa import Credentials, Device, DeviceConfig, KasaException
from kasa.device_type import DeviceType
from kasa.exceptions import SmartErrorCode from kasa.exceptions import SmartErrorCode
from kasa.iot import IotDevice from kasa.iot import IotDevice
from kasa.smart import SmartChildDevice, SmartDevice from kasa.smart import SmartChildDevice, SmartDevice
from .conftest import ( from .conftest import (
bulb,
device_iot, device_iot,
device_smart, device_smart,
dimmer,
handle_turn_on, handle_turn_on,
has_emeter_iot, has_emeter_iot,
lightstrip,
no_emeter_iot, no_emeter_iot,
plug,
turn_on, turn_on,
) )
from .fakeprotocol_iot import FakeIotProtocol from .fakeprotocol_iot import FakeIotProtocol
@ -416,3 +421,25 @@ SYSINFO_SCHEMA = Schema(
}, },
extra=REMOVE_EXTRA, extra=REMOVE_EXTRA,
) )
@dimmer
def test_device_type_dimmer(dev):
assert dev.device_type == DeviceType.Dimmer
@bulb
def test_device_type_bulb(dev):
if dev.is_light_strip:
pytest.skip("bulb has also lightstrips to test the api")
assert dev.device_type == DeviceType.Bulb
@plug
def test_device_type_plug(dev):
assert dev.device_type == DeviceType.Plug
@lightstrip
def test_device_type_lightstrip(dev):
assert dev.device_type == DeviceType.LightStrip