mirror of
https://github.com/python-kasa/python-kasa.git
synced 2026-10-12 15:53:50 +00:00
Drop the `XorTransport` guard and always query sysinfo for `IotProtocol` connections, so `get_device_class_from_sys_info` sees `dev_name` (\"Wi-Fi Smart Dimmer\", etc.) and picks the right class to initialize. This is necessary, as some IOT-speaking devices have moved to use KLAP instead of XOR as their transport, which in turn caused devices like dimmers to be detected incorrectly as plugs. --------- Co-authored-by: bluntman001 <noreply@github.com> Co-authored-by: Teemu Rytilahti <tpr@iki.fi>
This commit is contained in:
@@ -104,9 +104,9 @@ async def _connect(config: DeviceConfig, protocol: BaseProtocol) -> Device:
|
|||||||
device_class: type[Device] | None
|
device_class: type[Device] | None
|
||||||
device: Device | None = None
|
device: Device | None = None
|
||||||
|
|
||||||
if isinstance(protocol, IotProtocol) and isinstance(
|
# For IOT devices, we need to obtain the initial sysinfo in order decide
|
||||||
protocol._transport, XorTransport
|
# on the device class to initialize.
|
||||||
):
|
if isinstance(protocol, IotProtocol):
|
||||||
info = await protocol.query(GET_SYSINFO_QUERY)
|
info = await protocol.query(GET_SYSINFO_QUERY)
|
||||||
_perf_log(True, "get_sysinfo")
|
_perf_log(True, "get_sysinfo")
|
||||||
device_class = get_device_class_from_sys_info(info)
|
device_class = get_device_class_from_sys_info(info)
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ from kasa import (
|
|||||||
from kasa.device_factory import (
|
from kasa.device_factory import (
|
||||||
Device,
|
Device,
|
||||||
IotDevice,
|
IotDevice,
|
||||||
|
IotDimmer,
|
||||||
|
IotPlug,
|
||||||
SmartCamDevice,
|
SmartCamDevice,
|
||||||
SmartDevice,
|
SmartDevice,
|
||||||
connect,
|
connect,
|
||||||
@@ -307,3 +309,63 @@ async def test_get_protocol(
|
|||||||
protocol = get_protocol(config)
|
protocol = get_protocol(config)
|
||||||
assert isinstance(protocol, expected_protocol)
|
assert isinstance(protocol, expected_protocol)
|
||||||
assert isinstance(protocol._transport, expected_transport)
|
assert isinstance(protocol._transport, expected_transport)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_connect_iot_klap_dimmer_classification(mocker):
|
||||||
|
"""Test that IOT dimmers reached via KLAP initialize IotDimmer.
|
||||||
|
|
||||||
|
Regression test for the bug where `_connect` only queried sysinfo for
|
||||||
|
IOT devices reached via XorTransport, so KLAP-authenticated
|
||||||
|
IOT.SMARTPLUGSWITCH devices were always classified incorrectly as IotPlugs.
|
||||||
|
"""
|
||||||
|
dimmer_sysinfo = {
|
||||||
|
"system": {
|
||||||
|
"get_sysinfo": {
|
||||||
|
"sw_ver": "1.1.4 Build 241220 Rel.171050",
|
||||||
|
"hw_ver": "3.0",
|
||||||
|
"model": "HS220(US)",
|
||||||
|
"deviceId": "0" * 40,
|
||||||
|
"hwId": "0" * 32,
|
||||||
|
"fwId": "0" * 32,
|
||||||
|
"oemId": "0" * 32,
|
||||||
|
"alias": "Dimmer under test",
|
||||||
|
"dev_name": "Wi-Fi Smart Dimmer",
|
||||||
|
"mic_type": "IOT.SMARTPLUGSWITCH",
|
||||||
|
"relay_state": 0,
|
||||||
|
"brightness": 25,
|
||||||
|
"on_time": 0,
|
||||||
|
"active_mode": "none",
|
||||||
|
"feature": "TIM",
|
||||||
|
"updating": 0,
|
||||||
|
"rssi": -60,
|
||||||
|
"led_off": 0,
|
||||||
|
"latitude_i": 0,
|
||||||
|
"longitude_i": 0,
|
||||||
|
"err_code": 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mocker.patch("kasa.IotProtocol.query", return_value=dimmer_sysinfo)
|
||||||
|
# Avoid touching the wire on the update() that runs after class selection.
|
||||||
|
mocker.patch.object(IotDevice, "update", return_value=None)
|
||||||
|
|
||||||
|
ctype = DeviceConnectionParameters(
|
||||||
|
device_family=DeviceFamily.IotSmartPlugSwitch,
|
||||||
|
encryption_type=DeviceEncryptionType.Klap,
|
||||||
|
login_version=2,
|
||||||
|
https=False,
|
||||||
|
)
|
||||||
|
config = DeviceConfig(
|
||||||
|
host=DISCOVERY_MOCK_IP,
|
||||||
|
credentials=Credentials("user", "pass"),
|
||||||
|
connection_type=ctype,
|
||||||
|
)
|
||||||
|
dev = await connect(config=config)
|
||||||
|
try:
|
||||||
|
assert isinstance(dev, IotDimmer), (
|
||||||
|
f"HS220 via KLAP must be IotDimmer, got {type(dev).__name__}"
|
||||||
|
)
|
||||||
|
assert not isinstance(dev, IotPlug) or isinstance(dev, IotDimmer)
|
||||||
|
finally:
|
||||||
|
await dev.disconnect()
|
||||||
|
|||||||
Reference in New Issue
Block a user