2023-11-21 22:48:53 +00:00
|
|
|
# type: ignore
|
|
|
|
import logging
|
|
|
|
|
2024-01-18 17:32:26 +00:00
|
|
|
import aiohttp
|
2023-11-21 22:48:53 +00:00
|
|
|
import pytest # type: ignore # https://github.com/pytest-dev/pytest/issues/3342
|
|
|
|
|
|
|
|
from kasa import (
|
2023-12-04 18:50:05 +00:00
|
|
|
Credentials,
|
2024-02-04 15:20:08 +00:00
|
|
|
Device,
|
2023-12-04 18:50:05 +00:00
|
|
|
Discover,
|
2024-02-21 15:52:55 +00:00
|
|
|
KasaException,
|
2023-11-21 22:48:53 +00:00
|
|
|
)
|
2023-12-29 19:17:15 +00:00
|
|
|
from kasa.device_factory import connect, get_protocol
|
|
|
|
from kasa.deviceconfig import (
|
|
|
|
ConnectionType,
|
|
|
|
DeviceConfig,
|
|
|
|
DeviceFamilyType,
|
|
|
|
EncryptType,
|
2023-12-04 18:50:05 +00:00
|
|
|
)
|
|
|
|
from kasa.discover import DiscoveryResult
|
2023-11-21 22:48:53 +00:00
|
|
|
|
|
|
|
|
2023-12-29 19:17:15 +00:00
|
|
|
def _get_connection_type_device_class(the_fixture_data):
|
|
|
|
if "discovery_result" in the_fixture_data:
|
|
|
|
discovery_info = {"result": the_fixture_data["discovery_result"]}
|
|
|
|
device_class = Discover._get_device_class(discovery_info)
|
|
|
|
dr = DiscoveryResult(**discovery_info["result"])
|
2023-11-21 22:48:53 +00:00
|
|
|
|
2023-12-29 19:17:15 +00:00
|
|
|
connection_type = ConnectionType.from_values(
|
|
|
|
dr.device_type, dr.mgt_encrypt_schm.encrypt_type
|
|
|
|
)
|
2023-12-04 18:50:05 +00:00
|
|
|
else:
|
2023-12-29 19:17:15 +00:00
|
|
|
connection_type = ConnectionType.from_values(
|
|
|
|
DeviceFamilyType.IotSmartPlugSwitch.value, EncryptType.Xor.value
|
|
|
|
)
|
|
|
|
device_class = Discover._get_device_class(the_fixture_data)
|
2023-11-21 22:48:53 +00:00
|
|
|
|
2023-12-29 19:17:15 +00:00
|
|
|
return connection_type, device_class
|
2023-11-21 22:48:53 +00:00
|
|
|
|
2023-12-29 19:17:15 +00:00
|
|
|
|
|
|
|
async def test_connect(
|
|
|
|
all_fixture_data: dict,
|
2023-11-21 22:48:53 +00:00
|
|
|
mocker,
|
|
|
|
):
|
2023-12-29 19:17:15 +00:00
|
|
|
"""Test that if the protocol is passed in it gets set correctly."""
|
2023-11-21 22:48:53 +00:00
|
|
|
host = "127.0.0.1"
|
2023-12-29 19:17:15 +00:00
|
|
|
ctype, device_class = _get_connection_type_device_class(all_fixture_data)
|
2023-11-21 22:48:53 +00:00
|
|
|
|
2023-12-29 19:17:15 +00:00
|
|
|
mocker.patch("kasa.IotProtocol.query", return_value=all_fixture_data)
|
|
|
|
mocker.patch("kasa.SmartProtocol.query", return_value=all_fixture_data)
|
2023-11-21 22:48:53 +00:00
|
|
|
|
2023-12-29 19:17:15 +00:00
|
|
|
config = DeviceConfig(
|
|
|
|
host=host, credentials=Credentials("foor", "bar"), connection_type=ctype
|
|
|
|
)
|
|
|
|
protocol_class = get_protocol(config).__class__
|
2024-02-14 17:03:50 +00:00
|
|
|
close_mock = mocker.patch.object(protocol_class, "close")
|
2023-12-29 19:17:15 +00:00
|
|
|
dev = await connect(
|
|
|
|
config=config,
|
|
|
|
)
|
|
|
|
assert isinstance(dev, device_class)
|
|
|
|
assert isinstance(dev.protocol, protocol_class)
|
|
|
|
|
|
|
|
assert dev.config == config
|
2024-02-14 17:03:50 +00:00
|
|
|
assert close_mock.call_count == 0
|
2024-01-23 22:15:18 +00:00
|
|
|
await dev.disconnect()
|
2024-02-14 17:03:50 +00:00
|
|
|
assert close_mock.call_count == 1
|
2024-01-23 22:15:18 +00:00
|
|
|
|
2023-12-29 19:17:15 +00:00
|
|
|
|
|
|
|
@pytest.mark.parametrize("custom_port", [123, None])
|
|
|
|
async def test_connect_custom_port(all_fixture_data: dict, mocker, custom_port):
|
|
|
|
"""Make sure that connect returns an initialized SmartDevice instance."""
|
2023-11-21 22:48:53 +00:00
|
|
|
host = "127.0.0.1"
|
|
|
|
|
2023-12-29 19:17:15 +00:00
|
|
|
ctype, _ = _get_connection_type_device_class(all_fixture_data)
|
2024-01-03 18:26:52 +00:00
|
|
|
config = DeviceConfig(
|
|
|
|
host=host,
|
|
|
|
port_override=custom_port,
|
|
|
|
connection_type=ctype,
|
|
|
|
credentials=Credentials("dummy_user", "dummy_password"),
|
|
|
|
)
|
2023-12-29 19:17:15 +00:00
|
|
|
default_port = 80 if "discovery_result" in all_fixture_data else 9999
|
|
|
|
|
|
|
|
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)
|
|
|
|
dev = await connect(config=config)
|
2024-02-04 15:20:08 +00:00
|
|
|
assert issubclass(dev.__class__, Device)
|
2023-12-29 19:17:15 +00:00
|
|
|
assert dev.port == custom_port or dev.port == default_port
|
2023-11-21 22:48:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_connect_logs_connect_time(
|
2023-12-29 19:17:15 +00:00
|
|
|
all_fixture_data: dict, caplog: pytest.LogCaptureFixture, mocker
|
2023-11-21 22:48:53 +00:00
|
|
|
):
|
|
|
|
"""Test that the connect time is logged when debug logging is enabled."""
|
2023-12-29 19:17:15 +00:00
|
|
|
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)
|
|
|
|
|
2023-11-21 22:48:53 +00:00
|
|
|
host = "127.0.0.1"
|
2023-12-29 19:17:15 +00:00
|
|
|
config = DeviceConfig(
|
|
|
|
host=host, credentials=Credentials("foor", "bar"), connection_type=ctype
|
|
|
|
)
|
|
|
|
logging.getLogger("kasa").setLevel(logging.DEBUG)
|
|
|
|
await connect(
|
|
|
|
config=config,
|
|
|
|
)
|
|
|
|
assert "seconds to update" in caplog.text
|
2023-11-28 19:13:15 +00:00
|
|
|
|
|
|
|
|
2023-12-29 19:17:15 +00:00
|
|
|
async def test_connect_query_fails(all_fixture_data: dict, mocker):
|
|
|
|
"""Make sure that connect fails when query fails."""
|
|
|
|
host = "127.0.0.1"
|
2024-02-21 15:52:55 +00:00
|
|
|
mocker.patch("kasa.IotProtocol.query", side_effect=KasaException)
|
|
|
|
mocker.patch("kasa.SmartProtocol.query", side_effect=KasaException)
|
2023-12-04 18:50:05 +00:00
|
|
|
|
2023-12-29 19:17:15 +00:00
|
|
|
ctype, _ = _get_connection_type_device_class(all_fixture_data)
|
|
|
|
config = DeviceConfig(
|
|
|
|
host=host, credentials=Credentials("foor", "bar"), connection_type=ctype
|
|
|
|
)
|
2024-02-14 17:03:50 +00:00
|
|
|
protocol_class = get_protocol(config).__class__
|
|
|
|
close_mock = mocker.patch.object(protocol_class, "close")
|
|
|
|
assert close_mock.call_count == 0
|
2024-02-21 15:52:55 +00:00
|
|
|
with pytest.raises(KasaException):
|
2023-12-29 19:17:15 +00:00
|
|
|
await connect(config=config)
|
2024-02-14 17:03:50 +00:00
|
|
|
assert close_mock.call_count == 1
|
2023-12-29 19:17:15 +00:00
|
|
|
|
|
|
|
|
|
|
|
async def test_connect_http_client(all_fixture_data, mocker):
|
|
|
|
"""Make sure that discover_single returns an initialized SmartDevice instance."""
|
2023-11-28 19:13:15 +00:00
|
|
|
host = "127.0.0.1"
|
2023-12-04 18:50:05 +00:00
|
|
|
|
2023-12-29 19:17:15 +00:00
|
|
|
ctype, _ = _get_connection_type_device_class(all_fixture_data)
|
2023-12-04 18:50:05 +00:00
|
|
|
|
2023-12-29 19:17:15 +00:00
|
|
|
mocker.patch("kasa.IotProtocol.query", return_value=all_fixture_data)
|
|
|
|
mocker.patch("kasa.SmartProtocol.query", return_value=all_fixture_data)
|
|
|
|
|
2024-01-18 17:32:26 +00:00
|
|
|
http_client = aiohttp.ClientSession()
|
2023-12-29 19:17:15 +00:00
|
|
|
|
|
|
|
config = DeviceConfig(
|
|
|
|
host=host, credentials=Credentials("foor", "bar"), connection_type=ctype
|
2023-12-04 18:50:05 +00:00
|
|
|
)
|
2023-12-29 19:17:15 +00:00
|
|
|
dev = await connect(config=config)
|
|
|
|
if ctype.encryption_type != EncryptType.Xor:
|
2024-01-18 09:57:33 +00:00
|
|
|
assert dev.protocol._transport._http_client.client != http_client
|
2023-12-29 19:17:15 +00:00
|
|
|
|
|
|
|
config = DeviceConfig(
|
|
|
|
host=host,
|
|
|
|
credentials=Credentials("foor", "bar"),
|
|
|
|
connection_type=ctype,
|
|
|
|
http_client=http_client,
|
|
|
|
)
|
|
|
|
dev = await connect(config=config)
|
|
|
|
if ctype.encryption_type != EncryptType.Xor:
|
2024-01-18 09:57:33 +00:00
|
|
|
assert dev.protocol._transport._http_client.client == http_client
|