Add WallSwitch device type and autogenerate supported devices docs (#758)

This commit is contained in:
Steven B
2024-03-01 18:32:45 +00:00
committed by GitHub
parent 0306e05fb9
commit fcad0d2344
21 changed files with 714 additions and 211 deletions

View File

@@ -7,7 +7,7 @@ from kasa import (
Device,
Discover,
)
from kasa.iot import IotBulb, IotDimmer, IotLightStrip, IotPlug, IotStrip
from kasa.iot import IotBulb, IotDimmer, IotLightStrip, IotPlug, IotStrip, IotWallSwitch
from kasa.smart import SmartBulb, SmartDevice
from .fakeprotocol_iot import FakeIotProtocol
@@ -60,15 +60,12 @@ PLUGS_IOT = {
"HS103",
"HS105",
"HS110",
"HS200",
"HS210",
"EP10",
"KP100",
"KP105",
"KP115",
"KP125",
"KP401",
"KS200M",
}
# P135 supports dimming, but its not currently support
# by the library
@@ -77,15 +74,25 @@ PLUGS_SMART = {
"P110",
"KP125M",
"EP25",
"KS205",
"P125M",
"S505",
"TP15",
}
PLUGS = {
*PLUGS_IOT,
*PLUGS_SMART,
}
SWITCHES_IOT = {
"HS200",
"HS210",
"KS200M",
}
SWITCHES_SMART = {
"KS205",
"KS225",
"S500D",
"S505",
}
SWITCHES = {*SWITCHES_IOT, *SWITCHES_SMART}
STRIPS_IOT = {"HS107", "HS300", "KP303", "KP200", "KP400", "EP40"}
STRIPS_SMART = {"P300", "TP25"}
STRIPS = {*STRIPS_IOT, *STRIPS_SMART}
@@ -105,12 +112,15 @@ WITH_EMETER = {*WITH_EMETER_IOT, *WITH_EMETER_SMART}
DIMMABLE = {*BULBS, *DIMMERS}
ALL_DEVICES_IOT = BULBS_IOT.union(PLUGS_IOT).union(STRIPS_IOT).union(DIMMERS_IOT)
ALL_DEVICES_IOT = (
BULBS_IOT.union(PLUGS_IOT).union(STRIPS_IOT).union(DIMMERS_IOT).union(SWITCHES_IOT)
)
ALL_DEVICES_SMART = (
BULBS_SMART.union(PLUGS_SMART)
.union(STRIPS_SMART)
.union(DIMMERS_SMART)
.union(HUBS_SMART)
.union(SWITCHES_SMART)
)
ALL_DEVICES = ALL_DEVICES_IOT.union(ALL_DEVICES_SMART)
@@ -160,7 +170,14 @@ no_emeter_iot = parametrize(
)
bulb = parametrize("bulbs", model_filter=BULBS, protocol_filter={"SMART", "IOT"})
plug = parametrize("plugs", model_filter=PLUGS, protocol_filter={"IOT"})
plug = parametrize("plugs", model_filter=PLUGS, protocol_filter={"IOT", "SMART"})
plug_iot = parametrize("plugs iot", model_filter=PLUGS, protocol_filter={"IOT"})
wallswitch = parametrize(
"wall switches", model_filter=SWITCHES, protocol_filter={"IOT", "SMART"}
)
wallswitch_iot = parametrize(
"wall switches iot", model_filter=SWITCHES, protocol_filter={"IOT"}
)
strip = parametrize("strips", model_filter=STRIPS, protocol_filter={"SMART", "IOT"})
dimmer = parametrize("dimmers", model_filter=DIMMERS, protocol_filter={"IOT"})
lightstrip = parametrize(
@@ -213,6 +230,9 @@ strip_smart = parametrize(
plug_smart = parametrize(
"plug devices smart", model_filter=PLUGS_SMART, protocol_filter={"SMART"}
)
switch_smart = parametrize(
"switch devices smart", model_filter=SWITCHES_SMART, protocol_filter={"SMART"}
)
bulb_smart = parametrize(
"bulb devices smart", model_filter=BULBS_SMART, protocol_filter={"SMART"}
)
@@ -239,8 +259,8 @@ def check_categories():
+ strip.args[1]
+ plug.args[1]
+ bulb.args[1]
+ wallswitch.args[1]
+ lightstrip.args[1]
+ plug_smart.args[1]
+ bulb_smart.args[1]
+ dimmers_smart.args[1]
+ hubs_smart.args[1]
@@ -263,6 +283,9 @@ def device_for_fixture_name(model, protocol):
for d in PLUGS_SMART:
if d in model:
return SmartDevice
for d in SWITCHES_SMART:
if d in model:
return SmartDevice
for d in BULBS_SMART:
if d in model:
return SmartBulb
@@ -283,6 +306,9 @@ def device_for_fixture_name(model, protocol):
for d in PLUGS_IOT:
if d in model:
return IotPlug
for d in SWITCHES_IOT:
if d in model:
return IotWallSwitch
# Light strips are recognized also as bulbs, so this has to go first
for d in BULBS_IOT_LIGHT_STRIP:
@@ -325,6 +351,13 @@ async def get_device_for_fixture(fixture_data: FixtureInfo):
d.protocol = FakeSmartProtocol(fixture_data.data, fixture_data.name)
else:
d.protocol = FakeIotProtocol(fixture_data.data)
if "discovery_result" in fixture_data.data:
discovery_data = {"result": fixture_data.data["discovery_result"]}
else:
discovery_data = {
"system": {"get_sysinfo": fixture_data.data["system"]["get_sysinfo"]}
}
d.update_from_discover_info(discovery_data)
await _update_and_close(d)
return d

View File

@@ -10,7 +10,11 @@ from kasa import (
Discover,
KasaException,
)
from kasa.device_factory import connect, get_protocol
from kasa.device_factory import (
_get_device_type_from_sys_info,
connect,
get_protocol,
)
from kasa.deviceconfig import (
ConnectionType,
DeviceConfig,
@@ -18,6 +22,7 @@ from kasa.deviceconfig import (
EncryptType,
)
from kasa.discover import DiscoveryResult
from kasa.smart.smartdevice import SmartDevice
def _get_connection_type_device_class(discovery_info):
@@ -146,3 +151,16 @@ async def test_connect_http_client(discovery_data, mocker):
assert dev.protocol._transport._http_client.client == http_client
await dev.disconnect()
await http_client.close()
async def test_device_types(dev: Device):
await dev.update()
if isinstance(dev, SmartDevice):
device_type = dev._discovery_info["result"]["device_type"]
res = SmartDevice._get_device_type_from_components(
dev._components.keys(), device_type
)
else:
res = _get_device_type_from_sys_info(dev._last_update)
assert dev.device_type == res

View File

@@ -29,8 +29,9 @@ from .conftest import (
dimmer,
lightstrip,
new_discovery,
plug,
plug_iot,
strip_iot,
wallswitch_iot,
)
UNSUPPORTED = {
@@ -55,7 +56,14 @@ UNSUPPORTED = {
}
@plug
@wallswitch_iot
async def test_type_detection_switch(dev: Device):
d = Discover._get_device_class(dev._last_update)("localhost")
assert d.is_wallswitch
assert d.device_type == DeviceType.WallSwitch
@plug_iot
async def test_type_detection_plug(dev: Device):
d = Discover._get_device_class(dev._last_update)("localhost")
assert d.is_plug

View File

@@ -1,6 +1,6 @@
from kasa import DeviceType
from .conftest import plug, plug_smart
from .conftest import plug_iot, plug_smart, switch_smart, wallswitch_iot
from .test_smartdevice import SYSINFO_SCHEMA
# these schemas should go to the mainlib as
@@ -8,7 +8,7 @@ from .test_smartdevice import SYSINFO_SCHEMA
# as well as to check that faked devices are operating properly.
@plug
@plug_iot
async def test_plug_sysinfo(dev):
assert dev.sys_info is not None
SYSINFO_SCHEMA(dev.sys_info)
@@ -19,8 +19,34 @@ async def test_plug_sysinfo(dev):
assert dev.is_plug or dev.is_strip
@plug
async def test_led(dev):
@wallswitch_iot
async def test_switch_sysinfo(dev):
assert dev.sys_info is not None
SYSINFO_SCHEMA(dev.sys_info)
assert dev.model is not None
assert dev.device_type == DeviceType.WallSwitch
assert dev.is_wallswitch
@plug_iot
async def test_plug_led(dev):
original = dev.led
await dev.set_led(False)
await dev.update()
assert not dev.led
await dev.set_led(True)
await dev.update()
assert dev.led
await dev.set_led(original)
@wallswitch_iot
async def test_switch_led(dev):
original = dev.led
await dev.set_led(False)
@@ -40,3 +66,13 @@ async def test_plug_device_info(dev):
assert dev.model is not None
assert dev.device_type == DeviceType.Plug or dev.device_type == DeviceType.Strip
@switch_smart
async def test_switch_device_info(dev):
assert dev._info is not None
assert dev.model is not None
assert (
dev.device_type == DeviceType.WallSwitch or dev.device_type == DeviceType.Dimmer
)