mirror of
https://github.com/python-kasa/python-kasa.git
synced 2025-08-06 10:44:04 +00:00
Switch from TPLinkSmartHomeProtocol to IotProtocol/XorTransport (#710)
* Switch from TPLinkSmartHomeProtocol to IotProtocol/XorTransport * Add test * Update docs * Fix ruff deleting deprecated import
This commit is contained in:
@@ -20,9 +20,9 @@ from kasa import (
|
||||
SmartLightStrip,
|
||||
SmartPlug,
|
||||
SmartStrip,
|
||||
TPLinkSmartHomeProtocol,
|
||||
)
|
||||
from kasa.tapo import TapoBulb, TapoDevice, TapoPlug
|
||||
from kasa.xortransport import XorEncryption
|
||||
|
||||
from .newfakes import FakeSmartProtocol, FakeTransportProtocol
|
||||
|
||||
@@ -478,7 +478,7 @@ def discovery_mock(all_fixture_data, mocker):
|
||||
device_type = sys_info.get("mic_type") or sys_info.get("type")
|
||||
encrypt_type = "XOR"
|
||||
login_version = None
|
||||
datagram = TPLinkSmartHomeProtocol.encrypt(json_dumps(discovery_data))[4:]
|
||||
datagram = XorEncryption.encrypt(json_dumps(discovery_data))[4:]
|
||||
dm = _DiscoveryMock(
|
||||
"127.0.0.123",
|
||||
9999,
|
||||
@@ -517,7 +517,6 @@ def discovery_mock(all_fixture_data, mocker):
|
||||
|
||||
mocker.patch("kasa.IotProtocol.query", side_effect=_query)
|
||||
mocker.patch("kasa.SmartProtocol.query", side_effect=_query)
|
||||
mocker.patch("kasa.TPLinkSmartHomeProtocol.query", side_effect=_query)
|
||||
|
||||
yield dm
|
||||
|
||||
|
@@ -19,8 +19,10 @@ from voluptuous import (
|
||||
from ..credentials import Credentials
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..exceptions import SmartDeviceException
|
||||
from ..protocol import BaseTransport, TPLinkSmartHomeProtocol, _XorTransport
|
||||
from ..iotprotocol import IotProtocol
|
||||
from ..protocol import BaseTransport
|
||||
from ..smartprotocol import SmartProtocol
|
||||
from ..xortransport import XorTransport
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@@ -381,10 +383,10 @@ class FakeSmartTransport(BaseTransport):
|
||||
pass
|
||||
|
||||
|
||||
class FakeTransportProtocol(TPLinkSmartHomeProtocol):
|
||||
class FakeTransportProtocol(IotProtocol):
|
||||
def __init__(self, info):
|
||||
super().__init__(
|
||||
transport=_XorTransport(
|
||||
transport=XorTransport(
|
||||
config=DeviceConfig("127.0.0.123"),
|
||||
)
|
||||
)
|
||||
|
@@ -11,7 +11,6 @@ from kasa import (
|
||||
EmeterStatus,
|
||||
SmartDevice,
|
||||
SmartDeviceException,
|
||||
TPLinkSmartHomeProtocol,
|
||||
UnsupportedDeviceException,
|
||||
)
|
||||
from kasa.cli import (
|
||||
|
@@ -54,7 +54,6 @@ async def test_connect(
|
||||
|
||||
mocker.patch("kasa.IotProtocol.query", return_value=all_fixture_data)
|
||||
mocker.patch("kasa.SmartProtocol.query", return_value=all_fixture_data)
|
||||
mocker.patch("kasa.TPLinkSmartHomeProtocol.query", return_value=all_fixture_data)
|
||||
|
||||
config = DeviceConfig(
|
||||
host=host, credentials=Credentials("foor", "bar"), connection_type=ctype
|
||||
@@ -87,7 +86,6 @@ async def test_connect_custom_port(all_fixture_data: dict, mocker, custom_port):
|
||||
default_port = 80 if "discovery_result" in all_fixture_data else 9999
|
||||
|
||||
ctype, _ = _get_connection_type_device_class(all_fixture_data)
|
||||
mocker.patch("kasa.TPLinkSmartHomeProtocol.query", return_value=all_fixture_data)
|
||||
mocker.patch("kasa.IotProtocol.query", return_value=all_fixture_data)
|
||||
mocker.patch("kasa.SmartProtocol.query", return_value=all_fixture_data)
|
||||
dev = await connect(config=config)
|
||||
@@ -102,7 +100,6 @@ async def test_connect_logs_connect_time(
|
||||
ctype, _ = _get_connection_type_device_class(all_fixture_data)
|
||||
mocker.patch("kasa.IotProtocol.query", return_value=all_fixture_data)
|
||||
mocker.patch("kasa.SmartProtocol.query", return_value=all_fixture_data)
|
||||
mocker.patch("kasa.TPLinkSmartHomeProtocol.query", return_value=all_fixture_data)
|
||||
|
||||
host = "127.0.0.1"
|
||||
config = DeviceConfig(
|
||||
@@ -118,7 +115,6 @@ async def test_connect_logs_connect_time(
|
||||
async def test_connect_query_fails(all_fixture_data: dict, mocker):
|
||||
"""Make sure that connect fails when query fails."""
|
||||
host = "127.0.0.1"
|
||||
mocker.patch("kasa.TPLinkSmartHomeProtocol.query", side_effect=SmartDeviceException)
|
||||
mocker.patch("kasa.IotProtocol.query", side_effect=SmartDeviceException)
|
||||
mocker.patch("kasa.SmartProtocol.query", side_effect=SmartDeviceException)
|
||||
|
||||
@@ -138,7 +134,6 @@ async def test_connect_http_client(all_fixture_data, mocker):
|
||||
|
||||
mocker.patch("kasa.IotProtocol.query", return_value=all_fixture_data)
|
||||
mocker.patch("kasa.SmartProtocol.query", return_value=all_fixture_data)
|
||||
mocker.patch("kasa.TPLinkSmartHomeProtocol.query", return_value=all_fixture_data)
|
||||
|
||||
http_client = aiohttp.ClientSession()
|
||||
|
||||
|
@@ -15,7 +15,6 @@ from kasa import (
|
||||
Discover,
|
||||
SmartDevice,
|
||||
SmartDeviceException,
|
||||
TPLinkSmartHomeProtocol,
|
||||
protocol,
|
||||
)
|
||||
from kasa.deviceconfig import (
|
||||
@@ -26,6 +25,7 @@ from kasa.deviceconfig import (
|
||||
)
|
||||
from kasa.discover import DiscoveryResult, _DiscoverProtocol, json_dumps
|
||||
from kasa.exceptions import AuthenticationException, UnsupportedDeviceException
|
||||
from kasa.xortransport import XorEncryption
|
||||
|
||||
from .conftest import bulb, bulb_iot, dimmer, lightstrip, new_discovery, plug, strip
|
||||
|
||||
@@ -189,7 +189,7 @@ async def test_discover_invalid_info(msg, data, mocker):
|
||||
|
||||
def mock_discover(self):
|
||||
self.datagram_received(
|
||||
protocol.TPLinkSmartHomeProtocol.encrypt(json_dumps(data))[4:], (host, 9999)
|
||||
XorEncryption.encrypt(json_dumps(data))[4:], (host, 9999)
|
||||
)
|
||||
|
||||
mocker.patch.object(_DiscoverProtocol, "do_discover", mock_discover)
|
||||
@@ -212,7 +212,7 @@ async def test_discover_datagram_received(mocker, discovery_data):
|
||||
"""Verify that datagram received fills discovered_devices."""
|
||||
proto = _DiscoverProtocol()
|
||||
|
||||
mocker.patch.object(protocol.TPLinkSmartHomeProtocol, "decrypt")
|
||||
mocker.patch.object(XorEncryption, "decrypt")
|
||||
|
||||
addr = "127.0.0.1"
|
||||
port = 20002 if "result" in discovery_data else 9999
|
||||
@@ -238,8 +238,8 @@ async def test_discover_invalid_responses(msg, data, mocker):
|
||||
"""Verify that we don't crash whole discovery if some devices in the network are sending unexpected data."""
|
||||
proto = _DiscoverProtocol()
|
||||
mocker.patch("kasa.discover.json_loads", return_value=data)
|
||||
mocker.patch.object(protocol.TPLinkSmartHomeProtocol, "encrypt")
|
||||
mocker.patch.object(protocol.TPLinkSmartHomeProtocol, "decrypt")
|
||||
mocker.patch.object(XorEncryption, "encrypt")
|
||||
mocker.patch.object(XorEncryption, "decrypt")
|
||||
|
||||
proto.datagram_received(data, ("127.0.0.1", 9999))
|
||||
assert len(proto.discovered_devices) == 0
|
||||
@@ -375,9 +375,7 @@ class FakeDatagramTransport(asyncio.DatagramTransport):
|
||||
self.do_not_reply_count = do_not_reply_count
|
||||
self.send_count = 0
|
||||
if port == 9999:
|
||||
self.datagram = TPLinkSmartHomeProtocol.encrypt(
|
||||
json_dumps(LEGACY_DISCOVER_DATA)
|
||||
)[4:]
|
||||
self.datagram = XorEncryption.encrypt(json_dumps(LEGACY_DISCOVER_DATA))[4:]
|
||||
elif port == 20002:
|
||||
discovery_data = UNSUPPORTED if unsupported else AUTHENTICATION_DATA_KLAP
|
||||
self.datagram = (
|
||||
|
@@ -15,13 +15,11 @@ from ..aestransport import AesTransport
|
||||
from ..credentials import Credentials
|
||||
from ..deviceconfig import DeviceConfig
|
||||
from ..exceptions import SmartDeviceException
|
||||
from ..iotprotocol import IotProtocol
|
||||
from ..iotprotocol import IotProtocol, _deprecated_TPLinkSmartHomeProtocol
|
||||
from ..klaptransport import KlapTransport, KlapTransportV2
|
||||
from ..protocol import (
|
||||
BaseProtocol,
|
||||
BaseTransport,
|
||||
TPLinkSmartHomeProtocol,
|
||||
_XorTransport,
|
||||
)
|
||||
from ..xortransport import XorEncryption, XorTransport
|
||||
|
||||
@@ -29,10 +27,10 @@ from ..xortransport import XorEncryption, XorTransport
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class",
|
||||
[
|
||||
(TPLinkSmartHomeProtocol, _XorTransport),
|
||||
(_deprecated_TPLinkSmartHomeProtocol, XorTransport),
|
||||
(IotProtocol, XorTransport),
|
||||
],
|
||||
ids=("TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
ids=("_deprecated_TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
)
|
||||
@pytest.mark.parametrize("retry_count", [1, 3, 5])
|
||||
async def test_protocol_retries(mocker, retry_count, protocol_class, transport_class):
|
||||
@@ -59,10 +57,10 @@ async def test_protocol_retries(mocker, retry_count, protocol_class, transport_c
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class",
|
||||
[
|
||||
(TPLinkSmartHomeProtocol, _XorTransport),
|
||||
(_deprecated_TPLinkSmartHomeProtocol, XorTransport),
|
||||
(IotProtocol, XorTransport),
|
||||
],
|
||||
ids=("TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
ids=("_deprecated_TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
)
|
||||
async def test_protocol_no_retry_on_unreachable(
|
||||
mocker, protocol_class, transport_class
|
||||
@@ -83,10 +81,10 @@ async def test_protocol_no_retry_on_unreachable(
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class",
|
||||
[
|
||||
(TPLinkSmartHomeProtocol, _XorTransport),
|
||||
(_deprecated_TPLinkSmartHomeProtocol, XorTransport),
|
||||
(IotProtocol, XorTransport),
|
||||
],
|
||||
ids=("TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
ids=("_deprecated_TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
)
|
||||
async def test_protocol_no_retry_connection_refused(
|
||||
mocker, protocol_class, transport_class
|
||||
@@ -107,10 +105,10 @@ async def test_protocol_no_retry_connection_refused(
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class",
|
||||
[
|
||||
(TPLinkSmartHomeProtocol, _XorTransport),
|
||||
(_deprecated_TPLinkSmartHomeProtocol, XorTransport),
|
||||
(IotProtocol, XorTransport),
|
||||
],
|
||||
ids=("TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
ids=("_deprecated_TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
)
|
||||
async def test_protocol_retry_recoverable_error(
|
||||
mocker, protocol_class, transport_class
|
||||
@@ -131,10 +129,14 @@ async def test_protocol_retry_recoverable_error(
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class, encryption_class",
|
||||
[
|
||||
(TPLinkSmartHomeProtocol, _XorTransport, TPLinkSmartHomeProtocol),
|
||||
(
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
XorTransport,
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
),
|
||||
(IotProtocol, XorTransport, XorEncryption),
|
||||
],
|
||||
ids=("TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
ids=("_deprecated_TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
)
|
||||
@pytest.mark.parametrize("retry_count", [1, 3, 5])
|
||||
async def test_protocol_reconnect(
|
||||
@@ -177,10 +179,14 @@ async def test_protocol_reconnect(
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class, encryption_class",
|
||||
[
|
||||
(TPLinkSmartHomeProtocol, _XorTransport, TPLinkSmartHomeProtocol),
|
||||
(
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
XorTransport,
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
),
|
||||
(IotProtocol, XorTransport, XorEncryption),
|
||||
],
|
||||
ids=("TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
ids=("_deprecated_TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
)
|
||||
async def test_protocol_handles_cancellation_during_write(
|
||||
mocker, protocol_class, transport_class, encryption_class
|
||||
@@ -227,10 +233,14 @@ async def test_protocol_handles_cancellation_during_write(
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class, encryption_class",
|
||||
[
|
||||
(TPLinkSmartHomeProtocol, _XorTransport, TPLinkSmartHomeProtocol),
|
||||
(
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
XorTransport,
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
),
|
||||
(IotProtocol, XorTransport, XorEncryption),
|
||||
],
|
||||
ids=("TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
ids=("_deprecated_TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
)
|
||||
async def test_protocol_handles_cancellation_during_connection(
|
||||
mocker, protocol_class, transport_class, encryption_class
|
||||
@@ -275,10 +285,14 @@ async def test_protocol_handles_cancellation_during_connection(
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class, encryption_class",
|
||||
[
|
||||
(TPLinkSmartHomeProtocol, _XorTransport, TPLinkSmartHomeProtocol),
|
||||
(
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
XorTransport,
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
),
|
||||
(IotProtocol, XorTransport, XorEncryption),
|
||||
],
|
||||
ids=("TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
ids=("_deprecated_TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
)
|
||||
@pytest.mark.parametrize("log_level", [logging.WARNING, logging.DEBUG])
|
||||
async def test_protocol_logging(
|
||||
@@ -318,10 +332,14 @@ async def test_protocol_logging(
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class, encryption_class",
|
||||
[
|
||||
(TPLinkSmartHomeProtocol, _XorTransport, TPLinkSmartHomeProtocol),
|
||||
(
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
XorTransport,
|
||||
_deprecated_TPLinkSmartHomeProtocol,
|
||||
),
|
||||
(IotProtocol, XorTransport, XorEncryption),
|
||||
],
|
||||
ids=("TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
ids=("_deprecated_TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
)
|
||||
@pytest.mark.parametrize("custom_port", [123, None])
|
||||
async def test_protocol_custom_port(
|
||||
@@ -358,11 +376,11 @@ async def test_protocol_custom_port(
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"encrypt_class",
|
||||
[TPLinkSmartHomeProtocol, XorEncryption],
|
||||
[_deprecated_TPLinkSmartHomeProtocol, XorEncryption],
|
||||
)
|
||||
@pytest.mark.parametrize(
|
||||
"decrypt_class",
|
||||
[TPLinkSmartHomeProtocol, XorEncryption],
|
||||
[_deprecated_TPLinkSmartHomeProtocol, XorEncryption],
|
||||
)
|
||||
def test_encrypt(encrypt_class, decrypt_class):
|
||||
d = json.dumps({"foo": 1, "bar": 2})
|
||||
@@ -374,7 +392,7 @@ def test_encrypt(encrypt_class, decrypt_class):
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"encrypt_class",
|
||||
[TPLinkSmartHomeProtocol, XorEncryption],
|
||||
[_deprecated_TPLinkSmartHomeProtocol, XorEncryption],
|
||||
)
|
||||
def test_encrypt_unicode(encrypt_class):
|
||||
d = "{'snowman': '\u2603'}"
|
||||
@@ -411,7 +429,7 @@ def test_encrypt_unicode(encrypt_class):
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"decrypt_class",
|
||||
[TPLinkSmartHomeProtocol, XorEncryption],
|
||||
[_deprecated_TPLinkSmartHomeProtocol, XorEncryption],
|
||||
)
|
||||
def test_decrypt_unicode(decrypt_class):
|
||||
e = bytes(
|
||||
@@ -451,7 +469,11 @@ def _get_subclasses(of_class):
|
||||
importlib.import_module("." + modname, package="kasa")
|
||||
module = sys.modules["kasa." + modname]
|
||||
for name, obj in inspect.getmembers(module):
|
||||
if inspect.isclass(obj) and issubclass(obj, of_class):
|
||||
if (
|
||||
inspect.isclass(obj)
|
||||
and issubclass(obj, of_class)
|
||||
and name != "_deprecated_TPLinkSmartHomeProtocol"
|
||||
):
|
||||
subclasses.add((name, obj))
|
||||
return subclasses
|
||||
|
||||
@@ -491,7 +513,7 @@ def test_transport_init_signature(class_name_obj):
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"transport_class",
|
||||
[AesTransport, KlapTransport, KlapTransportV2, _XorTransport, XorTransport],
|
||||
[AesTransport, KlapTransport, KlapTransportV2, XorTransport, XorTransport],
|
||||
)
|
||||
async def test_transport_credentials_hash(mocker, transport_class):
|
||||
host = "127.0.0.1"
|
||||
@@ -519,10 +541,10 @@ async def test_transport_credentials_hash(mocker, transport_class):
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class",
|
||||
[
|
||||
(TPLinkSmartHomeProtocol, _XorTransport),
|
||||
(_deprecated_TPLinkSmartHomeProtocol, XorTransport),
|
||||
(IotProtocol, XorTransport),
|
||||
],
|
||||
ids=("TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
ids=("_deprecated_TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
)
|
||||
async def test_protocol_will_retry_on_connect(
|
||||
mocker, protocol_class, transport_class, error, retry_expectation
|
||||
@@ -551,10 +573,10 @@ async def test_protocol_will_retry_on_connect(
|
||||
@pytest.mark.parametrize(
|
||||
"protocol_class, transport_class",
|
||||
[
|
||||
(TPLinkSmartHomeProtocol, _XorTransport),
|
||||
(_deprecated_TPLinkSmartHomeProtocol, XorTransport),
|
||||
(IotProtocol, XorTransport),
|
||||
],
|
||||
ids=("TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
ids=("_deprecated_TPLinkSmartHomeProtocol", "IotProtocol-XorTransport"),
|
||||
)
|
||||
async def test_protocol_will_retry_on_write(
|
||||
mocker, protocol_class, transport_class, error, retry_expectation
|
||||
@@ -580,3 +602,16 @@ async def test_protocol_will_retry_on_write(
|
||||
expected_call_count = retry_count + 1 if retry_expectation else 1
|
||||
assert conn.call_count == expected_call_count
|
||||
assert write_mock.call_count == expected_call_count
|
||||
|
||||
|
||||
def test_deprecated_protocol():
|
||||
with pytest.deprecated_call():
|
||||
from kasa import TPLinkSmartHomeProtocol
|
||||
|
||||
with pytest.raises(
|
||||
SmartDeviceException, match="host or transport must be supplied"
|
||||
):
|
||||
proto = TPLinkSmartHomeProtocol()
|
||||
host = "127.0.0.1"
|
||||
proto = TPLinkSmartHomeProtocol(host=host)
|
||||
assert proto.config.host == host
|
||||
|
Reference in New Issue
Block a user