diff --git a/kasa/__init__.py b/kasa/__init__.py index 06bb3514..6e937dc3 100755 --- a/kasa/__init__.py +++ b/kasa/__init__.py @@ -134,7 +134,6 @@ if TYPE_CHECKING: from . import smart smart.SmartDevice("127.0.0.1") - smart.SmartPlug("127.0.0.1") smart.SmartBulb("127.0.0.1") iot.IotDevice("127.0.0.1") iot.IotPlug("127.0.0.1") diff --git a/kasa/cli.py b/kasa/cli.py index 395022cc..e92c6652 100755 --- a/kasa/cli.py +++ b/kasa/cli.py @@ -27,7 +27,7 @@ from kasa import ( ) from kasa.discover import DiscoveryResult from kasa.iot import IotBulb, IotDevice, IotDimmer, IotLightStrip, IotPlug, IotStrip -from kasa.smart import SmartBulb, SmartDevice, SmartPlug +from kasa.smart import SmartBulb, SmartDevice try: from pydantic.v1 import ValidationError @@ -72,7 +72,7 @@ TYPE_TO_CLASS = { "iot.dimmer": IotDimmer, "iot.strip": IotStrip, "iot.lightstrip": IotLightStrip, - "smart.plug": SmartPlug, + "smart.plug": SmartDevice, "smart.bulb": SmartBulb, } diff --git a/kasa/device.py b/kasa/device.py index d2af7e60..6e104f88 100644 --- a/kasa/device.py +++ b/kasa/device.py @@ -194,32 +194,32 @@ class Device(ABC): @property def is_bulb(self) -> bool: """Return True if the device is a bulb.""" - return self._device_type == DeviceType.Bulb + return self.device_type == DeviceType.Bulb @property def is_light_strip(self) -> bool: """Return True if the device is a led strip.""" - return self._device_type == DeviceType.LightStrip + return self.device_type == DeviceType.LightStrip @property def is_plug(self) -> bool: """Return True if the device is a plug.""" - return self._device_type == DeviceType.Plug + return self.device_type == DeviceType.Plug @property def is_strip(self) -> bool: """Return True if the device is a strip.""" - return self._device_type == DeviceType.Strip + return self.device_type == DeviceType.Strip @property def is_strip_socket(self) -> bool: """Return True if the device is a strip socket.""" - return self._device_type == DeviceType.StripSocket + return self.device_type == DeviceType.StripSocket @property def is_dimmer(self) -> bool: """Return True if the device is a dimmer.""" - return self._device_type == DeviceType.Dimmer + return self.device_type == DeviceType.Dimmer @property def is_dimmable(self) -> bool: @@ -354,9 +354,9 @@ class Device(ABC): def __repr__(self): 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 ( - 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" - dev specific: {self.state_information}>" ) diff --git a/kasa/device_factory.py b/kasa/device_factory.py index 4fc0996b..66903468 100755 --- a/kasa/device_factory.py +++ b/kasa/device_factory.py @@ -14,7 +14,7 @@ from .protocol import ( BaseProtocol, BaseTransport, ) -from .smart import SmartBulb, SmartPlug +from .smart import SmartBulb, SmartDevice from .smartprotocol import SmartProtocol 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]]: """Return the device class from the type name.""" supported_device_types: Dict[str, Type[Device]] = { - "SMART.TAPOPLUG": SmartPlug, + "SMART.TAPOPLUG": SmartDevice, "SMART.TAPOBULB": SmartBulb, "SMART.TAPOSWITCH": SmartBulb, - "SMART.KASAPLUG": SmartPlug, + "SMART.KASAPLUG": SmartDevice, "SMART.KASASWITCH": SmartBulb, "IOT.SMARTPLUGSWITCH": IotPlug, "IOT.SMARTBULB": IotBulb, diff --git a/kasa/smart/__init__.py b/kasa/smart/__init__.py index c075ba32..936fa7fd 100644 --- a/kasa/smart/__init__.py +++ b/kasa/smart/__init__.py @@ -2,6 +2,5 @@ from .smartbulb import SmartBulb from .smartchilddevice import SmartChildDevice from .smartdevice import SmartDevice -from .smartplug import SmartPlug -__all__ = ["SmartDevice", "SmartPlug", "SmartBulb", "SmartChildDevice"] +__all__ = ["SmartDevice", "SmartBulb", "SmartChildDevice"] diff --git a/kasa/smart/smartdevice.py b/kasa/smart/smartdevice.py index 2a90beeb..ab45eb42 100644 --- a/kasa/smart/smartdevice.py +++ b/kasa/smart/smartdevice.py @@ -485,3 +485,28 @@ class SmartDevice(Device): Note, this does not downgrade the firmware. """ 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 diff --git a/kasa/smart/smartplug.py b/kasa/smart/smartplug.py deleted file mode 100644 index bd96b421..00000000 --- a/kasa/smart/smartplug.py +++ /dev/null @@ -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"), - }, - } diff --git a/kasa/tests/conftest.py b/kasa/tests/conftest.py index b5b711d9..e69b73fa 100644 --- a/kasa/tests/conftest.py +++ b/kasa/tests/conftest.py @@ -20,7 +20,7 @@ from kasa import ( ) from kasa.iot import IotBulb, IotDimmer, IotLightStrip, IotPlug, IotStrip from kasa.protocol import BaseTransport -from kasa.smart import SmartBulb, SmartPlug +from kasa.smart import SmartBulb, SmartDevice from kasa.xortransport import XorEncryption from .fakeprotocol_iot import FakeIotProtocol @@ -108,7 +108,6 @@ PLUGS_SMART = { "EP25", "KS205", "P125M", - "P135", "S505", "TP15", } @@ -121,7 +120,7 @@ STRIPS_SMART = {"P300", "TP25"} STRIPS = {*STRIPS_IOT, *STRIPS_SMART} DIMMERS_IOT = {"ES20M", "HS220", "KS220M", "KS230", "KP405"} -DIMMERS_SMART = {"S500D"} +DIMMERS_SMART = {"S500D", "P135"} DIMMERS = { *DIMMERS_IOT, *DIMMERS_SMART, @@ -346,7 +345,7 @@ def device_for_file(model, protocol): if protocol == "SMART": for d in PLUGS_SMART: if d in model: - return SmartPlug + return SmartDevice for d in BULBS_SMART: if d in model: return SmartBulb @@ -355,7 +354,7 @@ def device_for_file(model, protocol): return SmartBulb for d in STRIPS_SMART: if d in model: - return SmartPlug + return SmartDevice else: for d in STRIPS_IOT: if d in model: diff --git a/kasa/tests/test_plug.py b/kasa/tests/test_plug.py index 7cde008d..64c420f9 100644 --- a/kasa/tests/test_plug.py +++ b/kasa/tests/test_plug.py @@ -37,9 +37,6 @@ async def test_led(dev): @plug_smart async def test_plug_device_info(dev): assert dev._info is not None - # PLUG_SCHEMA(dev.sys_info) - assert dev.model is not None assert dev.device_type == DeviceType.Plug or dev.device_type == DeviceType.Strip - # assert dev.is_plug or dev.is_strip diff --git a/kasa/tests/test_smartdevice.py b/kasa/tests/test_smartdevice.py index 1a397b89..a9412380 100644 --- a/kasa/tests/test_smartdevice.py +++ b/kasa/tests/test_smartdevice.py @@ -22,16 +22,21 @@ from voluptuous import ( import kasa from kasa import Credentials, Device, DeviceConfig, KasaException +from kasa.device_type import DeviceType from kasa.exceptions import SmartErrorCode from kasa.iot import IotDevice from kasa.smart import SmartChildDevice, SmartDevice from .conftest import ( + bulb, device_iot, device_smart, + dimmer, handle_turn_on, has_emeter_iot, + lightstrip, no_emeter_iot, + plug, turn_on, ) from .fakeprotocol_iot import FakeIotProtocol @@ -416,3 +421,25 @@ SYSINFO_SCHEMA = Schema( }, 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