From af7245cddbbd51b68ef0b60e0382737eb2422f9c Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Sat, 20 Jun 2020 16:09:05 +0200 Subject: [PATCH 1/9] Preliminary support for light strips --- kasa/__init__.py | 2 ++ kasa/cli.py | 16 +++++++++++++--- kasa/smartbulb.py | 3 ++- kasa/smartdevice.py | 6 ++++++ kasa/smartlightstrip.py | 16 ++++++++++++++++ 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 kasa/smartlightstrip.py diff --git a/kasa/__init__.py b/kasa/__init__.py index e77aa7dde..911a7dc39 100755 --- a/kasa/__init__.py +++ b/kasa/__init__.py @@ -18,6 +18,7 @@ from kasa.smartbulb import SmartBulb from kasa.smartdevice import DeviceType, EmeterStatus, SmartDevice from kasa.smartdimmer import SmartDimmer +from kasa.smartlightstrip import SmartLightStrip from kasa.smartplug import SmartPlug from kasa.smartstrip import SmartStrip @@ -35,4 +36,5 @@ "SmartPlug", "SmartStrip", "SmartDimmer", + "SmartLightStrip", ] diff --git a/kasa/cli.py b/kasa/cli.py index 4c643b13a..31e0bfd3b 100755 --- a/kasa/cli.py +++ b/kasa/cli.py @@ -7,7 +7,14 @@ import asyncclick as click -from kasa import Discover, SmartBulb, SmartDevice, SmartPlug, SmartStrip +from kasa import ( + Discover, + SmartBulb, + SmartDevice, + SmartLightStrip, + SmartPlug, + SmartStrip, +) click.anyio_backend = "asyncio" @@ -37,10 +44,11 @@ @click.option("-d", "--debug", default=False, is_flag=True) @click.option("--bulb", default=False, is_flag=True) @click.option("--plug", default=False, is_flag=True) +@click.option("--ledstrip", default=False, is_flag=True) @click.option("--strip", default=False, is_flag=True) @click.version_option() @click.pass_context -async def cli(ctx, host, alias, target, debug, bulb, plug, strip): +async def cli(ctx, host, alias, target, debug, bulb, plug, ledstrip, strip): """A tool for controlling TP-Link smart home devices.""" # noqa if debug: logging.basicConfig(level=logging.DEBUG) @@ -64,7 +72,7 @@ async def cli(ctx, host, alias, target, debug, bulb, plug, strip): await ctx.invoke(discover) return else: - if not bulb and not plug and not strip: + if not bulb and not plug and not strip and not ledstrip: click.echo("No --strip nor --bulb nor --plug given, discovering..") dev = await Discover.discover_single(host) elif bulb: @@ -73,6 +81,8 @@ async def cli(ctx, host, alias, target, debug, bulb, plug, strip): dev = SmartPlug(host) elif strip: dev = SmartStrip(host) + elif ledstrip: + dev = SmartLightStrip(host) else: click.echo("Unable to detect type, use --strip or --bulb or --plug!") return diff --git a/kasa/smartbulb.py b/kasa/smartbulb.py index 5241635ae..1f2b2cd4c 100644 --- a/kasa/smartbulb.py +++ b/kasa/smartbulb.py @@ -89,6 +89,7 @@ class SmartBulb(SmartDevice): """ LIGHT_SERVICE = "smartlife.iot.smartbulb.lightingservice" + SET_LIGHT_METHOD = "transition_light_state" def __init__(self, host: str) -> None: super().__init__(host=host) @@ -190,7 +191,7 @@ async def set_light_state(self, state: Dict, *, transition: int = None) -> Dict: state["ignore_default"] = 1 light_state = await self._query_helper( - self.LIGHT_SERVICE, "transition_light_state", state + self.LIGHT_SERVICE, self.SET_LIGHT_METHOD, state ) return light_state diff --git a/kasa/smartdevice.py b/kasa/smartdevice.py index 644ff958d..c4f71fa61 100755 --- a/kasa/smartdevice.py +++ b/kasa/smartdevice.py @@ -32,6 +32,7 @@ class DeviceType(Enum): Bulb = 2 Strip = 3 Dimmer = 4 + LightStrip = 5 Unknown = -1 @@ -702,6 +703,11 @@ def is_bulb(self) -> bool: """Return True if the device is a 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 + @property def is_plug(self) -> bool: """Return True if the device is a plug.""" diff --git a/kasa/smartlightstrip.py b/kasa/smartlightstrip.py new file mode 100644 index 000000000..53f3d4386 --- /dev/null +++ b/kasa/smartlightstrip.py @@ -0,0 +1,16 @@ +"""Module for LED strips.""" +from .smartbulb import SmartBulb +from .smartdevice import DeviceType + + +class SmartLightStrip(SmartBulb): + """Representation of a TP-Link Smart light strip.""" + + LIGHT_SERVICE = "smartlife.iot.lightStrip" + SET_LIGHT_METHOD = SmartBulb.SET_LIGHT_METHOD + + def __init__(self, host: str, force_set_light=False) -> None: + super().__init__(host=host) + self._device_type = DeviceType.LightStrip + if force_set_light: + self.SET_LIGHT_METHOD = "set_light_state" From 16ae4ff45e992ae2d2d4dffb35a941f47d775eb9 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Sun, 19 Jul 2020 11:17:04 +0200 Subject: [PATCH 2/9] Add color temperature range and cleanup, thanks to @darkoppressor * Use lightstrip instead of {led,light}strip consistently everywhere * The cli flag is now --lightstrip --- kasa/cli.py | 8 ++++---- kasa/smartbulb.py | 1 + kasa/smartlightstrip.py | 15 ++++++++------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/kasa/cli.py b/kasa/cli.py index 31e0bfd3b..ed1264101 100755 --- a/kasa/cli.py +++ b/kasa/cli.py @@ -44,11 +44,11 @@ @click.option("-d", "--debug", default=False, is_flag=True) @click.option("--bulb", default=False, is_flag=True) @click.option("--plug", default=False, is_flag=True) -@click.option("--ledstrip", default=False, is_flag=True) +@click.option("--lightstrip", default=False, is_flag=True) @click.option("--strip", default=False, is_flag=True) @click.version_option() @click.pass_context -async def cli(ctx, host, alias, target, debug, bulb, plug, ledstrip, strip): +async def cli(ctx, host, alias, target, debug, bulb, plug, lightstrip, strip): """A tool for controlling TP-Link smart home devices.""" # noqa if debug: logging.basicConfig(level=logging.DEBUG) @@ -72,7 +72,7 @@ async def cli(ctx, host, alias, target, debug, bulb, plug, ledstrip, strip): await ctx.invoke(discover) return else: - if not bulb and not plug and not strip and not ledstrip: + if not bulb and not plug and not strip and not lightstrip: click.echo("No --strip nor --bulb nor --plug given, discovering..") dev = await Discover.discover_single(host) elif bulb: @@ -81,7 +81,7 @@ async def cli(ctx, host, alias, target, debug, bulb, plug, ledstrip, strip): dev = SmartPlug(host) elif strip: dev = SmartStrip(host) - elif ledstrip: + elif lightstrip: dev = SmartLightStrip(host) else: click.echo("Unable to detect type, use --strip or --bulb or --plug!") diff --git a/kasa/smartbulb.py b/kasa/smartbulb.py index 1f2b2cd4c..be81c1346 100644 --- a/kasa/smartbulb.py +++ b/kasa/smartbulb.py @@ -17,6 +17,7 @@ "KL130": (2500, 9000), r"KL120\(EU\)": (2700, 6500), r"KL120\(US\)": (2700, 5000), + r"KL430\(US\)": (2500, 9000), } diff --git a/kasa/smartlightstrip.py b/kasa/smartlightstrip.py index 53f3d4386..8acecb193 100644 --- a/kasa/smartlightstrip.py +++ b/kasa/smartlightstrip.py @@ -1,16 +1,17 @@ -"""Module for LED strips.""" +"""Module for light strips (KL430).""" from .smartbulb import SmartBulb from .smartdevice import DeviceType class SmartLightStrip(SmartBulb): - """Representation of a TP-Link Smart light strip.""" + """Representation of a TP-Link Smart light strip. + + Interaction works just like with the bulbs, only the service name + for controlling the device is different. + """ LIGHT_SERVICE = "smartlife.iot.lightStrip" - SET_LIGHT_METHOD = SmartBulb.SET_LIGHT_METHOD - def __init__(self, host: str, force_set_light=False) -> None: - super().__init__(host=host) + def __init__(self, host: str) -> None: + super().__init__(host) self._device_type = DeviceType.LightStrip - if force_set_light: - self.SET_LIGHT_METHOD = "set_light_state" From 732c12a55298f7495c5c066eaad9f2e1ab5b9b2f Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Sun, 19 Jul 2020 11:23:27 +0200 Subject: [PATCH 3/9] add apidocs --- docs/source/index.rst | 1 + docs/source/smartlightstrip.rst | 6 ++++++ 2 files changed, 7 insertions(+) create mode 100644 docs/source/smartlightstrip.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index 7d59f5f47..59897b394 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -15,3 +15,4 @@ python-kasa documentation smartplug smartdimmer smartstrip + smartlightstrip diff --git a/docs/source/smartlightstrip.rst b/docs/source/smartlightstrip.rst new file mode 100644 index 000000000..b02342ed9 --- /dev/null +++ b/docs/source/smartlightstrip.rst @@ -0,0 +1,6 @@ +Light strips +============ + +.. autoclass:: kasa.SmartLightStrip + :members: + :undoc-members: From 1185c22c3ad12bcec17036faaf41ff52c96a3184 Mon Sep 17 00:00:00 2001 From: Kevin Wells Date: Sun, 19 Jul 2020 14:40:12 -0400 Subject: [PATCH 4/9] Add fixture file for KL430 Signed-off-by: Kevin Wells --- kasa/tests/fixtures/KL430(US)_1.0.json | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 kasa/tests/fixtures/KL430(US)_1.0.json diff --git a/kasa/tests/fixtures/KL430(US)_1.0.json b/kasa/tests/fixtures/KL430(US)_1.0.json new file mode 100644 index 000000000..f12e7d500 --- /dev/null +++ b/kasa/tests/fixtures/KL430(US)_1.0.json @@ -0,0 +1,70 @@ +{ + "emeter": { + "err_code": -1, + "err_msg": "module not support" + }, + "smartlife.iot.common.emeter": { + "get_realtime": { + "current_ma": 0, + "err_code": 0, + "power_mw": 8729, + "total_wh": 21, + "voltage_mv": 0 + } + }, + "smartlife.iot.dimmer": { + "err_code": -1, + "err_msg": "module not support" + }, + "smartlife.iot.smartbulb.lightingservice": { + "err_code": -1, + "err_msg": "module not support" + }, + "system": { + "get_sysinfo": { + "active_mode": "none", + "alias": "KL430 pantry lightstrip", + "ctrl_protocols": { + "name": "Linkie", + "version": "1.0" + }, + "description": "Kasa Smart Light Strip, Multicolor", + "dev_state": "normal", + "deviceId": "0000000000000000000000000000000000000000", + "disco_ver": "1.0", + "err_code": 0, + "hwId": "00000000000000000000000000000000", + "hw_ver": "1.0", + "is_color": 1, + "is_dimmable": 1, + "is_factory": false, + "is_variable_color_temp": 1, + "latitude_i": 0, + "length": 16, + "light_state": { + "brightness": 50, + "color_temp": 3630, + "hue": 0, + "mode": "normal", + "on_off": 1, + "saturation": 0 + }, + "lighting_effect_state": { + "brightness": 50, + "custom": 0, + "enable": 0, + "id": "", + "name": "" + }, + "longitude_i": 0, + "mic_mac": "CC32E5230F55", + "mic_type": "IOT.SMARTBULB", + "model": "KL430(US)", + "oemId": "00000000000000000000000000000000", + "preferred_state": [], + "rssi": -56, + "status": "new", + "sw_ver": "1.0.10 Build 200522 Rel.104340" + } + } +} From 611a0b204b1d87ea74dc3fda47f55e85279e200b Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Sun, 19 Jul 2020 21:15:13 +0200 Subject: [PATCH 5/9] Add discovery support, expose effect and length of the strip --- kasa/discover.py | 9 +++++++++ kasa/smartlightstrip.py | 28 +++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/kasa/discover.py b/kasa/discover.py index ba5d702b5..9fe0f4514 100755 --- a/kasa/discover.py +++ b/kasa/discover.py @@ -9,6 +9,7 @@ from kasa.smartbulb import SmartBulb from kasa.smartdevice import SmartDevice, SmartDeviceException from kasa.smartdimmer import SmartDimmer +from kasa.smartlightstrip import SmartLightStrip from kasa.smartplug import SmartPlug from kasa.smartstrip import SmartStrip @@ -227,11 +228,19 @@ def _get_device_class(info: dict) -> Type[SmartDevice]: and "get_dimmer_parameters" in info["smartlife.iot.dimmer"] ): return SmartDimmer + elif "smartplug" in type_.lower() and "children" in sysinfo: return SmartStrip + elif "smartplug" in type_.lower(): + if "children" in sysinfo: + return SmartStrip + return SmartPlug elif "smartbulb" in type_.lower(): + if "length" in sysinfo: # strips have length + return SmartLightStrip + return SmartBulb raise SmartDeviceException("Unknown device type: %s", type_) diff --git a/kasa/smartlightstrip.py b/kasa/smartlightstrip.py index 8acecb193..a2c442d9c 100644 --- a/kasa/smartlightstrip.py +++ b/kasa/smartlightstrip.py @@ -1,6 +1,8 @@ """Module for light strips (KL430).""" +from typing import Any, Dict + from .smartbulb import SmartBulb -from .smartdevice import DeviceType +from .smartdevice import DeviceType, requires_update class SmartLightStrip(SmartBulb): @@ -8,6 +10,8 @@ class SmartLightStrip(SmartBulb): Interaction works just like with the bulbs, only the service name for controlling the device is different. + + See :class:`SmartBulb` for more information. """ LIGHT_SERVICE = "smartlife.iot.lightStrip" @@ -15,3 +19,25 @@ class SmartLightStrip(SmartBulb): def __init__(self, host: str) -> None: super().__init__(host) self._device_type = DeviceType.LightStrip + + @property # type: ignore + @requires_update + def length(self) -> int: + """Return length of the strip.""" + return self.sys_info["length"] + + @property # type: ignore + @requires_update + def effect(self) -> Dict: + """Return effect state.""" + return self.sys_info["lighting_effect_state"] + + @property # type: ignore + @requires_update + def state_information(self) -> Dict[str, Any]: + """Return strip specific state information.""" + info = super().state_information + + info["Length"] = self.length + + return info From f88144a05189e1eb56e744532b9b121223b77931 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Sun, 19 Jul 2020 21:48:51 +0200 Subject: [PATCH 6/9] use set_light_state instead of transition_light_state --- kasa/smartlightstrip.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kasa/smartlightstrip.py b/kasa/smartlightstrip.py index a2c442d9c..8e0ffb16e 100644 --- a/kasa/smartlightstrip.py +++ b/kasa/smartlightstrip.py @@ -15,6 +15,7 @@ class SmartLightStrip(SmartBulb): """ LIGHT_SERVICE = "smartlife.iot.lightStrip" + SET_LIGHT_METHOD = "set_light_state" def __init__(self, host: str) -> None: super().__init__(host) From d05f27327c1c003f08af634118ea5f147e870d09 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Sun, 19 Jul 2020 22:13:15 +0200 Subject: [PATCH 7/9] Add tests for lightstrip --- kasa/tests/conftest.py | 30 +++++++++++++++++++++++++----- kasa/tests/newfakes.py | 8 +++++++- kasa/tests/test_bulb.py | 6 ++++-- kasa/tests/test_discovery.py | 15 ++++++++++++--- 4 files changed, 48 insertions(+), 11 deletions(-) diff --git a/kasa/tests/conftest.py b/kasa/tests/conftest.py index f530b5db8..69f1f3b72 100644 --- a/kasa/tests/conftest.py +++ b/kasa/tests/conftest.py @@ -8,7 +8,14 @@ import pytest # type: ignore # see https://github.com/pytest-dev/pytest/issues/3342 -from kasa import Discover, SmartBulb, SmartDimmer, SmartPlug, SmartStrip +from kasa import ( + Discover, + SmartBulb, + SmartDimmer, + SmartLightStrip, + SmartPlug, + SmartStrip, +) from .newfakes import FakeTransportProtocol @@ -17,9 +24,11 @@ ) -BULBS = {"KL60", "LB100", "LB120", "LB130", "KL120", "KL130"} -VARIABLE_TEMP = {"LB120", "LB130", "KL120", "KL130"} -COLOR_BULBS = {"LB130", "KL130"} +LIGHT_STRIPS = {"KL430"} +BULBS = {"KL60", "LB100", "LB120", "LB130", "KL120", "KL130", *LIGHT_STRIPS} +VARIABLE_TEMP = {"LB120", "LB130", "KL120", "KL130", "KL430", *LIGHT_STRIPS} +COLOR_BULBS = {"LB130", "KL130", *LIGHT_STRIPS} + PLUGS = {"HS100", "HS103", "HS105", "HS110", "HS200", "HS210"} STRIPS = {"HS107", "HS300", "KP303", "KP400"} @@ -65,9 +74,12 @@ def name_for_filename(x): plug = parametrize("plugs", PLUGS, ids=name_for_filename) strip = parametrize("strips", STRIPS, ids=name_for_filename) dimmer = parametrize("dimmers", DIMMERS, ids=name_for_filename) +lightstrip = parametrize("lightstrips", LIGHT_STRIPS, ids=name_for_filename) # This ensures that every single file inside fixtures/ is being placed in some category -categorized_fixtures = set(dimmer.args[1] + strip.args[1] + plug.args[1] + bulb.args[1]) +categorized_fixtures = set( + dimmer.args[1] + strip.args[1] + plug.args[1] + bulb.args[1] + lightstrip.args[1] +) diff = set(SUPPORTED_DEVICES) - set(categorized_fixtures) if diff: for file in diff: @@ -105,12 +117,20 @@ def device_for_file(model): for d in STRIPS: if d in model: return SmartStrip + for d in PLUGS: if d in model: return SmartPlug + + # Light strips are recognized also as bulbs, so this has to go first + for d in LIGHT_STRIPS: + if d in model: + return SmartLightStrip + for d in BULBS: if d in model: return SmartBulb + for d in DIMMERS: if d in model: return SmartDimmer diff --git a/kasa/tests/newfakes.py b/kasa/tests/newfakes.py index b0d2f29fc..55c3e00cb 100644 --- a/kasa/tests/newfakes.py +++ b/kasa/tests/newfakes.py @@ -355,7 +355,8 @@ def light_state(self, x, *args): light_state = self.proto["system"]["get_sysinfo"]["light_state"] # Our tests have light state off, so we simply return the dft_on_state when device is on. _LOGGER.debug("reporting light state: %s", light_state) - if light_state["on_off"]: + # TODO: hack to go around KL430 fixture differences + if light_state["on_off"] and "dft_on_state" in light_state: return light_state["dft_on_state"] else: return light_state @@ -385,6 +386,11 @@ def light_state(self, x, *args): "get_light_state": light_state, "transition_light_state": transition_light_state, }, + # lightstrip follows the same payloads but uses different module & method + "smartlife.iot.lightStrip": { + "set_light_state": transition_light_state, + "get_light_state": light_state, + }, "time": { "get_time": { "year": 2017, diff --git a/kasa/tests/test_bulb.py b/kasa/tests/test_bulb.py index b8d3ab3c2..7d6e45e02 100644 --- a/kasa/tests/test_bulb.py +++ b/kasa/tests/test_bulb.py @@ -24,8 +24,10 @@ async def test_bulb_sysinfo(dev): assert dev.model is not None - assert dev.device_type == DeviceType.Bulb - assert dev.is_bulb + # TODO: remove special handling for lightstrip + if not dev.is_light_strip: + assert dev.device_type == DeviceType.Bulb + assert dev.is_bulb @bulb diff --git a/kasa/tests/test_discovery.py b/kasa/tests/test_discovery.py index 10de7b997..529ad8d63 100644 --- a/kasa/tests/test_discovery.py +++ b/kasa/tests/test_discovery.py @@ -3,7 +3,7 @@ from kasa import DeviceType, Discover, SmartDevice, SmartDeviceException -from .conftest import bulb, dimmer, plug, pytestmark, strip +from .conftest import bulb, dimmer, lightstrip, plug, pytestmark, strip @plug @@ -16,8 +16,10 @@ async def test_type_detection_plug(dev: SmartDevice): @bulb async def test_type_detection_bulb(dev: SmartDevice): d = Discover._get_device_class(dev.protocol.discovery_data)("localhost") - assert d.is_bulb - assert d.device_type == DeviceType.Bulb + # TODO: light_strip is a special case for now to force bulb tests on it + if not d.is_light_strip: + assert d.is_bulb + assert d.device_type == DeviceType.Bulb @strip @@ -34,6 +36,13 @@ async def test_type_detection_dimmer(dev: SmartDevice): assert d.device_type == DeviceType.Dimmer +@lightstrip +async def test_type_detection_lightstrip(dev: SmartDevice): + d = Discover._get_device_class(dev.protocol.discovery_data)("localhost") + assert d.is_light_strip + assert d.device_type == DeviceType.LightStrip + + async def test_type_unknown(): invalid_info = {"system": {"get_sysinfo": {"type": "nosuchtype"}}} with pytest.raises(SmartDeviceException): From cd11ae8f56c9527d28bdb01449d8d84fd9894056 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Sun, 19 Jul 2020 22:23:50 +0200 Subject: [PATCH 8/9] add doctests --- kasa/smartlightstrip.py | 39 +++++++++++++++++++++++++++--- kasa/tests/test_readme_examples.py | 9 +++++++ 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/kasa/smartlightstrip.py b/kasa/smartlightstrip.py index 8e0ffb16e..c579fec20 100644 --- a/kasa/smartlightstrip.py +++ b/kasa/smartlightstrip.py @@ -8,10 +8,33 @@ class SmartLightStrip(SmartBulb): """Representation of a TP-Link Smart light strip. - Interaction works just like with the bulbs, only the service name - for controlling the device is different. + Light strips work similarly to bulbs, but use a different service for controlling, + and expose some extra information (such as length and active effect). + This class extends :class:`SmartBulb` interface. - See :class:`SmartBulb` for more information. + Examples: + >>> import asyncio + >>> strip = SmartLightStrip("127.0.0.1") + >>> asyncio.run(strip.update()) + >>> print(strip.alias) + KL430 pantry lightstrip + + Getting the length of the strip: + + >>> strip.length + 16 + + Currently active effect: + + >>> strip.effect + {'brightness': 50, 'custom': 0, 'enable': 0, 'id': '', 'name': ''} + + .. note:: + The device supports some features that are not currently implemented, + feel free to find out how to control them and create a PR! + + + See :class:`SmartBulb` for more examples. """ LIGHT_SERVICE = "smartlife.iot.lightStrip" @@ -30,7 +53,15 @@ def length(self) -> int: @property # type: ignore @requires_update def effect(self) -> Dict: - """Return effect state.""" + """Return effect state. + + Example: + {'brightness': 50, + 'custom': 0, + 'enable': 0, + 'id': '', + 'name': ''} + """ return self.sys_info["lighting_effect_state"] @property # type: ignore diff --git a/kasa/tests/test_readme_examples.py b/kasa/tests/test_readme_examples.py index 204a923e7..c4d9f693e 100644 --- a/kasa/tests/test_readme_examples.py +++ b/kasa/tests/test_readme_examples.py @@ -51,6 +51,15 @@ def test_dimmer_examples(mocker): assert not res["failed"] +def test_lightstrip_examples(mocker): + """Test lightstrip examples.""" + p = get_device_for_file("KL430(US)_1.0.json") + mocker.patch("kasa.smartlightstrip.SmartLightStrip", return_value=p) + mocker.patch("kasa.smartlightstrip.SmartLightStrip.update") + res = xdoctest.doctest_module("kasa.smartlightstrip", "all") + assert not res["failed"] + + @pytest.mark.skipif( sys.version_info < (3, 8), reason="3.7 handles asyncio.run differently" ) From 6b7b63e7eb21008c7bb64e7e538207b1bdf39268 Mon Sep 17 00:00:00 2001 From: Teemu Rytilahti Date: Sun, 19 Jul 2020 22:26:12 +0200 Subject: [PATCH 9/9] Add KL430 to supported devices in README --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index a4783fee1..41e342c4b 100644 --- a/README.md +++ b/README.md @@ -112,15 +112,18 @@ or the `parse_pcap.py` script contained inside the `devtools` directory. * HS110 ### Power Strips + * HS300 * KP303 ### Wall switches + * HS200 * HS210 * HS220 ### Bulbs + * LB100 * LB110 * LB120 @@ -131,6 +134,10 @@ or the `parse_pcap.py` script contained inside the `devtools` directory. * KL120 * KL130 +### Light strips + +* KL430 + **Contributions (be it adding missing features, fixing bugs or improving documentation) are more than welcome, feel free to submit pull requests!** ### Resources