FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Fixes for powerstrips initialized using the discover protocol by dlee1j1 · Pull Request #149 · python-kasa/python-kasa · GitHub

Repository navigation

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .json  (4) .py  (7) All 2 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
66 changes: 47 additions & 19 deletions kasa/cli.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@
required=False,
help="The broadcast address to be used for discovery.",
)
@click.option(
"--use_discovery",
default=False,
required=False,
is_flag=True,
help="Get device information via UDP discovery rather than direct TCP connection.",
)
@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)
Expand All @@ -58,7 +65,9 @@
)
@click.version_option(package_name="python-kasa")
@click.pass_context
async def cli(ctx, host, alias, target, debug, bulb, plug, lightstrip, strip, type):
async def cli(
ctx, host, alias, target, use_discovery, debug, bulb, plug, lightstrip, strip, type
):
"""A tool for controlling TP-Link smart home devices.""" # noqa
if debug:
logging.basicConfig(level=logging.DEBUG)
Expand All @@ -82,26 +91,33 @@ async def cli(ctx, host, alias, target, debug, bulb, plug, lightstrip, strip, ty
await ctx.invoke(discover)
return

if bulb or plug or strip or lightstrip:
click.echo(
"Using --bulb, --plug, --strip, and --lightstrip is deprecated. Use --type instead to define the type"
)
if bulb:
type = "bulb"
elif plug:
type = "plug"
elif strip:
type = "strip"
elif lightstrip:
type = "lightstrip"

if type is not None:
dev = TYPE_TO_CLASS[type](host)
if use_discovery:
dev = await find_dev_using_discovery(host)
if dev is None:
click.echo(f"Could not find {host} through discovery")
return
else:
click.echo("No --type defined, discovering..")
dev = await Discover.discover_single(host)
if bulb or plug or strip or lightstrip:
click.echo(
"Using --bulb, --plug, --strip, and --lightstrip is deprecated. Use --type instead to define the type"
)
if bulb:
type = "bulb"
elif plug:
type = "plug"
elif strip:
type = "strip"
elif lightstrip:
type = "lightstrip"

if type is not None:
dev = TYPE_TO_CLASS[type](host)
else:
click.echo("No --type defined, discovering..")
dev = await Discover.discover_single(host)

await dev.update()

await dev.update()
ctx.obj = dev

if ctx.invoked_subcommand is None:
Expand Down Expand Up @@ -165,6 +181,18 @@ async def discover(ctx, timeout, discover_only, dump_raw):
return found_devs


async def find_dev_using_discovery(
host, target="255.255.255.255", timeout=1, attempts=3
):
"""Find a device using discovery protocol to fill in the device details."""
for attempt in range(1, attempts):
found_devs = await Discover.discover(target=target, timeout=timeout)
for ip, dev in found_devs.items():
if ip == host:
return dev
return None


async def find_host_from_alias(alias, target="255.255.255.255", timeout=1, attempts=3):
"""Discover a device identified by its alias."""
for attempt in range(1, attempts):
Expand Down
20 changes: 14 additions & 6 deletions kasa/discover.py
100755 → 100644
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -79,18 +79,18 @@ def datagram_received(self, data, addr) -> None:
_LOGGER.debug("[DISCOVERY] %s << %s", ip, info)

try:
device_class = Discover._get_device_class(info)
device = Discover._create_device_from_discovery_info(ip, info)
except SmartDeviceException as ex:
_LOGGER.debug("Unable to find device type from %s: %s", info, ex)
return

device = device_class(ip)
device.update_from_discover_info(info)

self.discovered_devices[ip] = device

if self.on_discovered is not None:
asyncio.ensure_future(self.on_discovered(device))
if device is not None:
if self.on_discovered is not None:
asyncio.ensure_future(self.on_discovered(device))
else:
_LOGGER.error("Received invalid response: %s", info)
Comment thread
dlee1j1 marked this conversation as resolved.

def error_received(self, ex):
"""Handle asyncio.Protocol errors."""
Expand Down Expand Up @@ -236,3 +236,11 @@ def _get_device_class(info: dict) -> Type[SmartDevice]:
return SmartBulb

raise SmartDeviceException("Unknown device type: %s" % type_)

@staticmethod
def _create_device_from_discovery_info(ip, info: dict) -> SmartDevice:
"""Create a SmartDevice for device described by passed data."""
device_class = Discover._get_device_class(info)
device = device_class(ip)
device.update_from_discovery_info(info)
return device
2 changes: 1 addition & 1 deletion kasa/smartdevice.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ async def update(self, update_children: bool = True):
self._last_update = await self.protocol.query(req)
self._sys_info = self._last_update["system"]["get_sysinfo"]

def update_from_discover_info(self, info):
def update_from_discovery_info(self, info):
"""Update state from info from the discover call."""
self._last_update = info
self._sys_info = info["system"]["get_sysinfo"]
Expand Down
38 changes: 30 additions & 8 deletions kasa/smartstrip.py
100755 → 100644
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,22 @@ async def update(self, update_children: bool = True):
Needed for methods that are decorated with `requires_update`.
"""
await super().update(update_children)
self._create_children()
if update_children and self.has_emeter:
for plug in self.children:
await plug.update()

def update_from_discovery_info(self, info):
"""Update state from info from the discover call."""
super().update_from_discovery_info(info)
self._create_children()

# Initialize the child devices during the first update.
@requires_update
def _create_children(self):
"""Initialize the child devices during the first update.

Called by update and update_from_discovery
"""
if not self.children:
children = self.sys_info["children"]
_LOGGER.debug("Initializing %s child sockets", len(children))
Expand All @@ -103,10 +117,6 @@ async def update(self, update_children: bool = True):
SmartStripPlug(self.host, parent=self, child_id=child["id"])
)

if update_children and self.has_emeter:
for plug in self.children:
await plug.update()

async def turn_on(self, **kwargs):
"""Turn the strip on."""
await self._query_helper("system", "set_relay_state", {"state": 1})
Expand Down Expand Up @@ -265,9 +275,21 @@ async def _query_helper(
self, target: str, cmd: str, arg: Optional[Dict] = None, child_ids=None
) -> Any:
"""Override query helper to include the child_ids."""
return await self.parent._query_helper(
target, cmd, arg, child_ids=[self.child_id]
)
try:
return await self.parent._query_helper(
target, cmd, arg, child_ids=[self.child_id]
)
except Exception as ex:
if len(self.child_id) < 3:
# the discovery protocol only returns the index for the child ID
# but the 2021 KP400 requires the full ID including the parent ID
# so we swallow this exception, update the child_id and try again
self.child_id = self.parent.sys_info["deviceId"] + self.child_id
return await self.parent._query_helper(
target, cmd, arg, child_ids=[self.child_id]
)
else:
raise ex
Comment thread
dlee1j1 marked this conversation as resolved.

@property # type: ignore
@requires_update
Expand Down
2 changes: 1 addition & 1 deletion kasa/tests/fixtures/EP40(US)_1.0_1.0.2.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"state": 0
}
],
"deviceId": "0000000000000000000000000000000000000000",
"deviceId": "8006231E1499BAC4D4BC7EFCD4B075181E6393F2",
"err_code": 0,
"feature": "TIM",
"hwId": "00000000000000000000000000000000",
Expand Down
2 changes: 1 addition & 1 deletion kasa/tests/fixtures/HS300(US)_2.0_1.0.3.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"state": 0
}
],
"deviceId": "0000000000000000000000000000000000000000",
"deviceId": "8006AF35494E7DB13DDE9B8F40BF2E001E770319",
"err_code": 0,
"feature": "TIM:ENE",
"hwId": "00000000000000000000000000000000",
Expand Down
2 changes: 1 addition & 1 deletion kasa/tests/fixtures/KP303(US)_2.0_1.0.3.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"state": 0
}
],
"deviceId": "0000000000000000000000000000000000000000",
"deviceId": "800681855E0E9AEF096F4891B3DC88C71E59F42E",
"err_code": 0,
"feature": "TIM",
"hwId": "00000000000000000000000000000000",
Expand Down
2 changes: 1 addition & 1 deletion kasa/tests/fixtures/KP400(US)_2.0_1.0.6.json
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"state": 1
}
],
"deviceId": "0000000000000000000000000000000000000000",
"deviceId": "8006B8E953CC4149E2B13AA27E0D18EF1DCFBF34",
"err_code": 0,
"feature": "TIM",
"hwId": "00000000000000000000000000000000",
Expand Down
8 changes: 8 additions & 0 deletions kasa/tests/newfakes.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import re
from ctypes import sizeof

from voluptuous import ( # type: ignore
REMOVE_EXTRA,
Expand All @@ -12,6 +13,8 @@
Schema,
)

from kasa import SmartDeviceException

from ..protocol import TPLinkSmartHomeProtocol

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -331,10 +334,15 @@ def set_relay_state(self, x, child_ids=[]):

_LOGGER.info("child_ids: %s", child_ids)
if child_ids:
num_child_ids = len(child_ids)
num_toggled = 0
for child in self.proto["system"]["get_sysinfo"]["children"]:
if child["id"] in child_ids:
_LOGGER.info("Found %s, turning to %s", child, x["state"])
child["state"] = x["state"]
num_toggled += 1
if num_child_ids > num_toggled:
raise SmartDeviceException("Index not found")
else:
self.proto["system"]["get_sysinfo"]["relay_state"] = x["state"]

Expand Down
5 changes: 4 additions & 1 deletion kasa/tests/test_discovery.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,12 @@ async def test_type_detection_bulb(dev: SmartDevice):

@strip
async def test_type_detection_strip(dev: SmartDevice):
d = Discover._get_device_class(dev._last_update)("localhost")
d = Discover._create_device_from_discovery_info(
"localhost", dev.protocol.discovery_data
)
assert d.is_strip
assert d.device_type == DeviceType.Strip
assert len(d.children) > 0


@dimmer
Expand Down
31 changes: 31 additions & 0 deletions kasa/tests/test_strip.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from datetime import datetime
from re import I

import pytest
from pytest_mock import mocker

from kasa import SmartDeviceException, SmartStrip
from kasa.smartstrip import SmartStripPlug

from .conftest import handle_turn_on, pytestmark, strip, turn_on

Expand Down Expand Up @@ -87,6 +90,34 @@ async def test_get_plug_by_index(dev: SmartStrip):
dev.get_plug_by_index(len(dev.children))


@strip
async def test_toggle_plug_by_index(dev: SmartStrip):
assert isinstance(dev.children[0], SmartStripPlug)
plug: SmartStripPlug = dev.children[0]
await plug.turn_on()
await plug.update()
assert plug.is_on
await plug.turn_off()
await plug.update()
assert plug.is_off

# Discovery protocol for KP400 sets the child_id to be the
# index without the deviceID but expects the child to
# be called with the strip deviceID prepended as part of the childId
original_child_id = plug.child_id
plug.child_id = plug.child_id[-2:]
await plug.turn_on()
await plug.update()
assert plug.is_on
assert plug.child_id == original_child_id

plug.child_id = plug.child_id[-2:]
await plug.turn_off()
await plug.update()
assert plug.is_off
assert plug.child_id == original_child_id


@pytest.mark.skip("this test will wear out your relays")
async def test_all_binary_states(dev):
# test every binary state
Expand Down

Back | FazBrowse Home | New Git URL