mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 19:23:34 +00:00
Add a connect_single method to Discover to avoid the need for UDP (#528)
This should equate to a significant reliability improvement for networks with poor wifi (edge of range)/udp.
This commit is contained in:
parent
528f5e9e07
commit
85c8410c3d
@ -256,6 +256,11 @@ class Discover:
|
||||
) -> SmartDevice:
|
||||
"""Discover a single device by the given IP address.
|
||||
|
||||
It is generally preferred to avoid :func:`discover_single()` and
|
||||
use :func:`connect_single()` instead as it should perform better when
|
||||
the WiFi network is congested or the device is not responding
|
||||
to discovery requests.
|
||||
|
||||
:param host: Hostname of device to query
|
||||
:rtype: SmartDevice
|
||||
:return: Object for querying/controlling found device.
|
||||
@ -299,6 +304,43 @@ class Discover:
|
||||
else:
|
||||
raise SmartDeviceException(f"Unable to get discovery response for {host}")
|
||||
|
||||
@staticmethod
|
||||
async def connect_single(
|
||||
host: str,
|
||||
*,
|
||||
port: Optional[int] = None,
|
||||
timeout=5,
|
||||
credentials: Optional[Credentials] = None,
|
||||
) -> SmartDevice:
|
||||
"""Connect to a single device by the given IP address.
|
||||
|
||||
This method avoids the UDP based discovery process and
|
||||
will connect directly to the device to query its type.
|
||||
|
||||
It is generally preferred to avoid :func:`discover_single()` and
|
||||
use this function instead as it should perform better when
|
||||
the WiFi network is congested or the device is not responding
|
||||
to discovery requests.
|
||||
|
||||
The device type is discovered by querying the device.
|
||||
|
||||
:param host: Hostname of device to query
|
||||
:rtype: SmartDevice
|
||||
:return: Object for querying/controlling found device.
|
||||
"""
|
||||
unknown_dev = SmartDevice(
|
||||
host=host, port=port, credentials=credentials, timeout=timeout
|
||||
)
|
||||
await unknown_dev.update()
|
||||
device_class = Discover._get_device_class(unknown_dev.internal_state)
|
||||
dev = device_class(
|
||||
host=host, port=port, credentials=credentials, timeout=timeout
|
||||
)
|
||||
# Reuse the connection from the unknown device
|
||||
# so we don't have to reconnect
|
||||
dev.protocol = unknown_dev.protocol
|
||||
return dev
|
||||
|
||||
@staticmethod
|
||||
def _get_device_class(info: dict) -> Type[SmartDevice]:
|
||||
"""Find SmartDevice subclass for device described by passed data."""
|
||||
|
@ -74,6 +74,26 @@ async def test_discover_single(discovery_data: dict, mocker, custom_port):
|
||||
assert x.port == custom_port or 9999
|
||||
|
||||
|
||||
@pytest.mark.parametrize("custom_port", [123, None])
|
||||
async def test_connect_single(discovery_data: dict, mocker, custom_port):
|
||||
"""Make sure that connect_single returns an initialized SmartDevice instance."""
|
||||
host = "127.0.0.1"
|
||||
mocker.patch("kasa.TPLinkSmartHomeProtocol.query", return_value=discovery_data)
|
||||
|
||||
dev = await Discover.connect_single(host, port=custom_port)
|
||||
assert issubclass(dev.__class__, SmartDevice)
|
||||
assert dev.port == custom_port or 9999
|
||||
|
||||
|
||||
async def test_connect_single_query_fails(discovery_data: dict, mocker):
|
||||
"""Make sure that connect_single fails when query fails."""
|
||||
host = "127.0.0.1"
|
||||
mocker.patch("kasa.TPLinkSmartHomeProtocol.query", side_effect=SmartDeviceException)
|
||||
|
||||
with pytest.raises(SmartDeviceException):
|
||||
await Discover.connect_single(host)
|
||||
|
||||
|
||||
UNSUPPORTED = {
|
||||
"result": {
|
||||
"device_id": "xx",
|
||||
|
Loading…
Reference in New Issue
Block a user