Add P300 fixture (#717)

* Add P300 fixture

* fixture after update

* Add tests for p300
This commit is contained in:
Teemu R 2024-01-29 18:14:30 +01:00 committed by GitHub
parent 1ad2a05b65
commit f8e273981c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1123 additions and 39 deletions

View File

@ -303,6 +303,11 @@ At the moment, the following devices have been confirmed to work:
* Tapo S500D * Tapo S500D
* Tapo S505 * Tapo S505
#### Power strips
* Tapo P300
### Newer Kasa branded devices ### Newer Kasa branded devices
Some newer hardware versions of Kasa branded devices are now using the same protocol as Some newer hardware versions of Kasa branded devices are now using the same protocol as

View File

@ -1,6 +1,7 @@
"""Package for supporting tapo-branded and newer kasa devices.""" """Package for supporting tapo-branded and newer kasa devices."""
from .childdevice import ChildDevice
from .tapobulb import TapoBulb from .tapobulb import TapoBulb
from .tapodevice import TapoDevice from .tapodevice import TapoDevice
from .tapoplug import TapoPlug from .tapoplug import TapoPlug
__all__ = ["TapoDevice", "TapoPlug", "TapoBulb"] __all__ = ["TapoDevice", "TapoPlug", "TapoBulb", "ChildDevice"]

View File

@ -35,7 +35,7 @@ class ChildDevice(TapoDevice):
return child return child
raise SmartDeviceException( raise SmartDeviceException(
f"Unable to find child device with position {self._id}" f"Unable to find child device with id {self._id}"
) )
self._last_update = self._sys_info = self._info = _get_child_info() self._last_update = self._sys_info = self._info = _get_child_info()

View File

@ -13,15 +13,18 @@ import pytest # type: ignore # see https://github.com/pytest-dev/pytest/issues/
from kasa import ( from kasa import (
Credentials, Credentials,
DeviceConfig,
Discover, Discover,
SmartBulb, SmartBulb,
SmartDevice, SmartDevice,
SmartDimmer, SmartDimmer,
SmartLightStrip, SmartLightStrip,
SmartPlug, SmartPlug,
SmartProtocol,
SmartStrip, SmartStrip,
) )
from kasa.tapo import TapoBulb, TapoDevice, TapoPlug from kasa.protocol import BaseTransport
from kasa.tapo import TapoBulb, TapoPlug
from kasa.xortransport import XorEncryption from kasa.xortransport import XorEncryption
from .newfakes import FakeSmartProtocol, FakeTransportProtocol from .newfakes import FakeSmartProtocol, FakeTransportProtocol
@ -107,7 +110,7 @@ PLUGS = {
*PLUGS_SMART, *PLUGS_SMART,
} }
STRIPS_IOT = {"HS107", "HS300", "KP303", "KP200", "KP400", "EP40"} STRIPS_IOT = {"HS107", "HS300", "KP303", "KP200", "KP400", "EP40"}
STRIPS_SMART: Set[str] = set() STRIPS_SMART = {"P300"}
STRIPS = {*STRIPS_IOT, *STRIPS_SMART} STRIPS = {*STRIPS_IOT, *STRIPS_SMART}
DIMMERS_IOT = {"ES20M", "HS220", "KS220M", "KS230", "KP405"} DIMMERS_IOT = {"ES20M", "HS220", "KS220M", "KS230", "KP405"}
@ -210,7 +213,7 @@ no_emeter_iot = parametrize(
bulb = parametrize("bulbs", BULBS, protocol_filter={"SMART", "IOT"}) bulb = parametrize("bulbs", BULBS, protocol_filter={"SMART", "IOT"})
plug = parametrize("plugs", PLUGS, protocol_filter={"IOT"}) plug = parametrize("plugs", PLUGS, protocol_filter={"IOT"})
strip = parametrize("strips", STRIPS, protocol_filter={"IOT"}) strip = parametrize("strips", STRIPS, protocol_filter={"SMART", "IOT"})
dimmer = parametrize("dimmers", DIMMERS, protocol_filter={"IOT"}) dimmer = parametrize("dimmers", DIMMERS, protocol_filter={"IOT"})
lightstrip = parametrize("lightstrips", LIGHT_STRIPS, protocol_filter={"IOT"}) lightstrip = parametrize("lightstrips", LIGHT_STRIPS, protocol_filter={"IOT"})
@ -238,6 +241,11 @@ variable_temp_iot = parametrize(
) )
bulb_iot = parametrize("bulb devices iot", BULBS_IOT, protocol_filter={"IOT"}) bulb_iot = parametrize("bulb devices iot", BULBS_IOT, protocol_filter={"IOT"})
strip_iot = parametrize("strip devices iot", STRIPS_IOT, protocol_filter={"IOT"})
strip_smart = parametrize(
"strip devices smart", STRIPS_SMART, protocol_filter={"SMART"}
)
plug_smart = parametrize("plug devices smart", PLUGS_SMART, protocol_filter={"SMART"}) plug_smart = parametrize("plug devices smart", PLUGS_SMART, protocol_filter={"SMART"})
bulb_smart = parametrize("bulb devices smart", BULBS_SMART, protocol_filter={"SMART"}) bulb_smart = parametrize("bulb devices smart", BULBS_SMART, protocol_filter={"SMART"})
dimmers_smart = parametrize( dimmers_smart = parametrize(
@ -338,6 +346,9 @@ def device_for_file(model, protocol):
for d in DIMMERS_SMART: for d in DIMMERS_SMART:
if d in model: if d in model:
return TapoBulb return TapoBulb
for d in STRIPS_SMART:
if d in model:
return TapoPlug
else: else:
for d in STRIPS_IOT: for d in STRIPS_IOT:
if d in model: if d in model:
@ -556,6 +567,34 @@ def unsupported_device_info(request, mocker):
yield discovery_data yield discovery_data
@pytest.fixture()
def dummy_protocol():
"""Return a smart protocol instance with a mocking-ready dummy transport."""
class DummyTransport(BaseTransport):
@property
def default_port(self) -> int:
return -1
@property
def credentials_hash(self) -> str:
return "dummy hash"
async def send(self, request: str) -> Dict:
return {}
async def close(self) -> None:
pass
async def reset(self) -> None:
pass
transport = DummyTransport(config=DeviceConfig(host="127.0.0.123"))
protocol = SmartProtocol(transport=transport)
return protocol
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addoption( parser.addoption(
"--ip", action="store", default=None, help="run against device on given ip" "--ip", action="store", default=None, help="run against device on given ip"

View File

@ -0,0 +1,520 @@
{
"component_nego": {
"component_list": [
{
"id": "device",
"ver_code": 2
},
{
"id": "firmware",
"ver_code": 2
},
{
"id": "quick_setup",
"ver_code": 3
},
{
"id": "time",
"ver_code": 1
},
{
"id": "wireless",
"ver_code": 1
},
{
"id": "schedule",
"ver_code": 2
},
{
"id": "countdown",
"ver_code": 2
},
{
"id": "antitheft",
"ver_code": 1
},
{
"id": "account",
"ver_code": 1
},
{
"id": "synchronize",
"ver_code": 1
},
{
"id": "sunrise_sunset",
"ver_code": 1
},
{
"id": "led",
"ver_code": 1
},
{
"id": "cloud_connect",
"ver_code": 1
},
{
"id": "iot_cloud",
"ver_code": 1
},
{
"id": "device_local_time",
"ver_code": 1
},
{
"id": "default_states",
"ver_code": 1
},
{
"id": "control_child",
"ver_code": 2
},
{
"id": "child_device",
"ver_code": 2
},
{
"id": "homekit",
"ver_code": 2
},
{
"id": "overheat_protection",
"ver_code": 1
}
]
},
"discovery_result": {
"device_id": "00000000000000000000000000000000",
"device_model": "P300(EU)",
"device_type": "SMART.TAPOPLUG",
"factory_default": false,
"ip": "127.0.0.123",
"is_support_iot_cloud": true,
"mac": "78-8C-B5-00-00-00",
"mgt_encrypt_schm": {
"encrypt_type": "KLAP",
"http_port": 80,
"is_support_https": false,
"lv": 2
},
"obd_src": "tplink",
"owner": "00000000000000000000000000000000"
},
"get_auto_update_info": {
"enable": false,
"random_range": 120,
"time": 180
},
"get_child_device_component_list": {
"child_component_list": [
{
"component_list": [
{
"id": "device",
"ver_code": 2
},
{
"id": "quick_setup",
"ver_code": 3
},
{
"id": "time",
"ver_code": 1
},
{
"id": "device_local_time",
"ver_code": 1
},
{
"id": "schedule",
"ver_code": 2
},
{
"id": "countdown",
"ver_code": 2
},
{
"id": "antitheft",
"ver_code": 1
},
{
"id": "account",
"ver_code": 1
},
{
"id": "synchronize",
"ver_code": 1
},
{
"id": "sunrise_sunset",
"ver_code": 1
},
{
"id": "cloud_connect",
"ver_code": 1
},
{
"id": "iot_cloud",
"ver_code": 1
},
{
"id": "firmware",
"ver_code": 2
},
{
"id": "default_states",
"ver_code": 1
},
{
"id": "overheat_protection",
"ver_code": 1
}
],
"device_id": "000000000000000000000000000000000000000000"
},
{
"component_list": [
{
"id": "device",
"ver_code": 2
},
{
"id": "quick_setup",
"ver_code": 3
},
{
"id": "time",
"ver_code": 1
},
{
"id": "device_local_time",
"ver_code": 1
},
{
"id": "schedule",
"ver_code": 2
},
{
"id": "countdown",
"ver_code": 2
},
{
"id": "antitheft",
"ver_code": 1
},
{
"id": "account",
"ver_code": 1
},
{
"id": "synchronize",
"ver_code": 1
},
{
"id": "sunrise_sunset",
"ver_code": 1
},
{
"id": "cloud_connect",
"ver_code": 1
},
{
"id": "iot_cloud",
"ver_code": 1
},
{
"id": "firmware",
"ver_code": 2
},
{
"id": "default_states",
"ver_code": 1
},
{
"id": "overheat_protection",
"ver_code": 1
}
],
"device_id": "000000000000000000000000000000000000000000"
},
{
"component_list": [
{
"id": "device",
"ver_code": 2
},
{
"id": "quick_setup",
"ver_code": 3
},
{
"id": "time",
"ver_code": 1
},
{
"id": "device_local_time",
"ver_code": 1
},
{
"id": "schedule",
"ver_code": 2
},
{
"id": "countdown",
"ver_code": 2
},
{
"id": "antitheft",
"ver_code": 1
},
{
"id": "account",
"ver_code": 1
},
{
"id": "synchronize",
"ver_code": 1
},
{
"id": "sunrise_sunset",
"ver_code": 1
},
{
"id": "cloud_connect",
"ver_code": 1
},
{
"id": "iot_cloud",
"ver_code": 1
},
{
"id": "firmware",
"ver_code": 2
},
{
"id": "default_states",
"ver_code": 1
},
{
"id": "overheat_protection",
"ver_code": 1
}
],
"device_id": "000000000000000000000000000000000000000000"
}
],
"start_index": 0,
"sum": 3
},
"get_child_device_list": {
"child_device_list": [
{
"avatar": "",
"bind_count": 1,
"category": "plug.powerstrip.sub-plug",
"default_states": {
"state": {
"on": false
},
"type": "custom"
},
"device_id": "000000000000000000000000000000000000000000",
"device_on": false,
"fw_id": "00000000000000000000000000000000",
"fw_ver": "1.0.13 Build 230925 Rel.150200",
"has_set_location_info": true,
"hw_id": "00000000000000000000000000000000",
"hw_ver": "1.0",
"latitude": 0,
"longitude": 0,
"mac": "788CB5000000",
"model": "P300",
"nickname": "I01BU0tFRF9OQU1FIw==",
"oem_id": "00000000000000000000000000000000",
"on_time": 0,
"original_device_id": "8022852468EC205A8178C7CBE81FC119213BC020",
"overheat_status": "normal",
"position": 3,
"region": "Europe/Berlin",
"slot_number": 3,
"status_follow_edge": true,
"type": "SMART.TAPOPLUG"
},
{
"avatar": "",
"bind_count": 1,
"category": "plug.powerstrip.sub-plug",
"default_states": {
"type": "last_states"
},
"device_id": "000000000000000000000000000000000000000000",
"device_on": false,
"fw_id": "00000000000000000000000000000000",
"fw_ver": "1.0.13 Build 230925 Rel.150200",
"has_set_location_info": true,
"hw_id": "00000000000000000000000000000000",
"hw_ver": "1.0",
"latitude": 0,
"longitude": 0,
"mac": "788CB5000000",
"model": "P300",
"nickname": "I01BU0tFRF9OQU1FIw==",
"oem_id": "00000000000000000000000000000000",
"on_time": 0,
"original_device_id": "8022852468EC205A8178C7CBE81FC119213BC020",
"overheat_status": "normal",
"position": 2,
"region": "Europe/Berlin",
"slot_number": 3,
"status_follow_edge": true,
"type": "SMART.TAPOPLUG"
},
{
"avatar": "",
"bind_count": 1,
"category": "plug.powerstrip.sub-plug",
"default_states": {
"state": {
"on": true
},
"type": "custom"
},
"device_id": "000000000000000000000000000000000000000000",
"device_on": false,
"fw_id": "00000000000000000000000000000000",
"fw_ver": "1.0.13 Build 230925 Rel.150200",
"has_set_location_info": true,
"hw_id": "00000000000000000000000000000000",
"hw_ver": "1.0",
"latitude": 0,
"longitude": 0,
"mac": "788CB5000000",
"model": "P300",
"nickname": "I01BU0tFRF9OQU1FIw==",
"oem_id": "00000000000000000000000000000000",
"on_time": 0,
"original_device_id": "8022852468EC205A8178C7CBE81FC119213BC020",
"overheat_status": "normal",
"position": 1,
"region": "Europe/Berlin",
"slot_number": 3,
"status_follow_edge": true,
"type": "SMART.TAPOPLUG"
}
],
"start_index": 0,
"sum": 3
},
"get_connect_cloud_state": {
"status": 0
},
"get_device_info": {
"avatar": "",
"device_id": "0000000000000000000000000000000000000000",
"fw_id": "00000000000000000000000000000000",
"fw_ver": "1.0.13 Build 230925 Rel.150200",
"has_set_location_info": true,
"hw_id": "00000000000000000000000000000000",
"hw_ver": "1.0",
"ip": "127.0.0.123",
"lang": "de_DE",
"latitude": 0,
"longitude": 0,
"mac": "78-8C-B5-00-00-00",
"model": "P300",
"nickname": "I01BU0tFRF9OQU1FIw==",
"oem_id": "00000000000000000000000000000000",
"region": "Europe/Berlin",
"rssi": -62,
"signal_level": 2,
"specs": "",
"ssid": "I01BU0tFRF9TU0lEIw==",
"time_diff": 60,
"type": "SMART.TAPOPLUG"
},
"get_device_time": {
"region": "Europe/Berlin",
"time_diff": 60,
"timestamp": 1706320181
},
"get_device_usage": {
"time_usage": {
"past30": 0,
"past7": 0,
"today": 0
}
},
"get_fw_download_state": {
"auto_upgrade": false,
"download_progress": 0,
"reboot_time": 5,
"status": 0,
"upgrade_time": 5
},
"get_latest_fw": {
"fw_size": 0,
"fw_ver": "1.0.13 Build 230925 Rel.150200",
"hw_id": "",
"need_to_upgrade": false,
"oem_id": "",
"release_date": "",
"release_note": "",
"type": 0
},
"get_led_info": {
"led_rule": "night_mode",
"led_status": false,
"night_mode": {
"end_time": 496,
"night_mode_type": "sunrise_sunset",
"start_time": 1034,
"sunrise_offset": 0,
"sunset_offset": 0
}
},
"get_wireless_scan_info": {
"ap_list": [],
"wep_supported": false
},
"qs_component_nego": {
"component_list": [
{
"id": "quick_setup",
"ver_code": 3
},
{
"id": "sunrise_sunset",
"ver_code": 1
},
{
"id": "ble_whole_setup",
"ver_code": 1
},
{
"id": "iot_cloud",
"ver_code": 1
},
{
"id": "inherit",
"ver_code": 1
},
{
"id": "firmware",
"ver_code": 2
},
{
"id": "control_child",
"ver_code": 2
},
{
"id": "child_device",
"ver_code": 2
}
],
"extra_info": {
"device_model": "P300",
"device_type": "SMART.TAPOPLUG",
"is_klap": true
}
}
}

View File

@ -0,0 +1,512 @@
{
"component_nego": {
"component_list": [
{
"id": "device",
"ver_code": 2
},
{
"id": "firmware",
"ver_code": 2
},
{
"id": "quick_setup",
"ver_code": 3
},
{
"id": "time",
"ver_code": 1
},
{
"id": "wireless",
"ver_code": 1
},
{
"id": "schedule",
"ver_code": 2
},
{
"id": "countdown",
"ver_code": 2
},
{
"id": "antitheft",
"ver_code": 1
},
{
"id": "account",
"ver_code": 1
},
{
"id": "synchronize",
"ver_code": 1
},
{
"id": "sunrise_sunset",
"ver_code": 1
},
{
"id": "led",
"ver_code": 1
},
{
"id": "cloud_connect",
"ver_code": 1
},
{
"id": "iot_cloud",
"ver_code": 1
},
{
"id": "device_local_time",
"ver_code": 1
},
{
"id": "default_states",
"ver_code": 1
},
{
"id": "control_child",
"ver_code": 2
},
{
"id": "child_device",
"ver_code": 2
},
{
"id": "homekit",
"ver_code": 1
},
{
"id": "overheat_protection",
"ver_code": 1
}
]
},
"discovery_result": {
"device_id": "00000000000000000000000000000000",
"device_model": "P300(EU)",
"device_type": "SMART.TAPOPLUG",
"factory_default": false,
"ip": "127.0.0.123",
"is_support_iot_cloud": true,
"mac": "78-8C-B5-00-00-00",
"mgt_encrypt_schm": {
"encrypt_type": "AES",
"http_port": 80,
"is_support_https": false,
"lv": 2
},
"obd_src": "tplink",
"owner": "00000000000000000000000000000000"
},
"get_auto_update_info": {
"enable": true,
"random_range": 120,
"time": 180
},
"get_child_device_component_list": {
"child_component_list": [
{
"component_list": [
{
"id": "device",
"ver_code": 2
},
{
"id": "quick_setup",
"ver_code": 3
},
{
"id": "time",
"ver_code": 1
},
{
"id": "device_local_time",
"ver_code": 1
},
{
"id": "schedule",
"ver_code": 2
},
{
"id": "countdown",
"ver_code": 2
},
{
"id": "antitheft",
"ver_code": 1
},
{
"id": "account",
"ver_code": 1
},
{
"id": "synchronize",
"ver_code": 1
},
{
"id": "sunrise_sunset",
"ver_code": 1
},
{
"id": "cloud_connect",
"ver_code": 1
},
{
"id": "iot_cloud",
"ver_code": 1
},
{
"id": "firmware",
"ver_code": 2
},
{
"id": "default_states",
"ver_code": 1
},
{
"id": "overheat_protection",
"ver_code": 1
}
],
"device_id": "000000000000000000000000000000000000000000"
},
{
"component_list": [
{
"id": "device",
"ver_code": 2
},
{
"id": "quick_setup",
"ver_code": 3
},
{
"id": "time",
"ver_code": 1
},
{
"id": "device_local_time",
"ver_code": 1
},
{
"id": "schedule",
"ver_code": 2
},
{
"id": "countdown",
"ver_code": 2
},
{
"id": "antitheft",
"ver_code": 1
},
{
"id": "account",
"ver_code": 1
},
{
"id": "synchronize",
"ver_code": 1
},
{
"id": "sunrise_sunset",
"ver_code": 1
},
{
"id": "cloud_connect",
"ver_code": 1
},
{
"id": "iot_cloud",
"ver_code": 1
},
{
"id": "firmware",
"ver_code": 2
},
{
"id": "default_states",
"ver_code": 1
},
{
"id": "overheat_protection",
"ver_code": 1
}
],
"device_id": "000000000000000000000000000000000000000000"
},
{
"component_list": [
{
"id": "device",
"ver_code": 2
},
{
"id": "quick_setup",
"ver_code": 3
},
{
"id": "time",
"ver_code": 1
},
{
"id": "device_local_time",
"ver_code": 1
},
{
"id": "schedule",
"ver_code": 2
},
{
"id": "countdown",
"ver_code": 2
},
{
"id": "antitheft",
"ver_code": 1
},
{
"id": "account",
"ver_code": 1
},
{
"id": "synchronize",
"ver_code": 1
},
{
"id": "sunrise_sunset",
"ver_code": 1
},
{
"id": "cloud_connect",
"ver_code": 1
},
{
"id": "iot_cloud",
"ver_code": 1
},
{
"id": "firmware",
"ver_code": 2
},
{
"id": "default_states",
"ver_code": 1
},
{
"id": "overheat_protection",
"ver_code": 1
}
],
"device_id": "000000000000000000000000000000000000000000"
}
],
"start_index": 0,
"sum": 3
},
"get_child_device_list": {
"child_device_list": [
{
"avatar": "",
"bind_count": 1,
"category": "plug.powerstrip.sub-plug",
"default_states": {
"type": "last_states"
},
"device_id": "000000000000000000000000000000000000000000",
"device_on": true,
"fw_id": "00000000000000000000000000000000",
"fw_ver": "1.0.7 Build 220715 Rel.200458",
"has_set_location_info": true,
"hw_id": "00000000000000000000000000000000",
"hw_ver": "1.0",
"latitude": 0,
"longitude": 0,
"mac": "788CB5000000",
"model": "P300",
"nickname": "I01BU0tFRF9OQU1FIw==",
"oem_id": "00000000000000000000000000000000",
"on_time": 366,
"original_device_id": "8022852468EC205A8178C7CBE81FC119213BC020",
"overheat_status": "normal",
"position": 1,
"region": "Europe/Berlin",
"slot_number": 3,
"status_follow_edge": true,
"type": "SMART.TAPOPLUG"
},
{
"avatar": "",
"bind_count": 1,
"category": "plug.powerstrip.sub-plug",
"default_states": {
"type": "last_states"
},
"device_id": "000000000000000000000000000000000000000000",
"device_on": true,
"fw_id": "00000000000000000000000000000000",
"fw_ver": "1.0.7 Build 220715 Rel.200458",
"has_set_location_info": true,
"hw_id": "00000000000000000000000000000000",
"hw_ver": "1.0",
"latitude": 0,
"longitude": 0,
"mac": "788CB5000000",
"model": "P300",
"nickname": "I01BU0tFRF9OQU1FIw==",
"oem_id": "00000000000000000000000000000000",
"on_time": 366,
"original_device_id": "8022852468EC205A8178C7CBE81FC119213BC020",
"overheat_status": "normal",
"position": 2,
"region": "Europe/Berlin",
"slot_number": 3,
"status_follow_edge": true,
"type": "SMART.TAPOPLUG"
},
{
"avatar": "",
"bind_count": 1,
"category": "plug.powerstrip.sub-plug",
"default_states": {
"type": "last_states"
},
"device_id": "000000000000000000000000000000000000000000",
"device_on": true,
"fw_id": "00000000000000000000000000000000",
"fw_ver": "1.0.7 Build 220715 Rel.200458",
"has_set_location_info": true,
"hw_id": "00000000000000000000000000000000",
"hw_ver": "1.0",
"latitude": 0,
"longitude": 0,
"mac": "788CB5000000",
"model": "P300",
"nickname": "I01BU0tFRF9OQU1FIw==",
"oem_id": "00000000000000000000000000000000",
"on_time": 366,
"original_device_id": "8022852468EC205A8178C7CBE81FC119213BC020",
"overheat_status": "normal",
"position": 3,
"region": "Europe/Berlin",
"slot_number": 3,
"status_follow_edge": true,
"type": "SMART.TAPOPLUG"
}
],
"start_index": 0,
"sum": 3
},
"get_connect_cloud_state": {
"status": 1
},
"get_device_info": {
"avatar": "",
"device_id": "0000000000000000000000000000000000000000",
"fw_id": "00000000000000000000000000000000",
"fw_ver": "1.0.7 Build 220715 Rel.200458",
"has_set_location_info": true,
"hw_id": "00000000000000000000000000000000",
"hw_ver": "1.0",
"ip": "127.0.0.123",
"lang": "",
"latitude": 0,
"longitude": 0,
"mac": "78-8C-B5-00-00-00",
"model": "P300",
"nickname": "I01BU0tFRF9OQU1FIw==",
"oem_id": "00000000000000000000000000000000",
"region": "Europe/Berlin",
"rssi": -58,
"signal_level": 2,
"specs": "",
"ssid": "I01BU0tFRF9TU0lEIw==",
"time_diff": 60,
"type": "SMART.TAPOPLUG"
},
"get_device_time": {
"region": "Europe/Berlin",
"time_diff": 60,
"timestamp": 1706297406
},
"get_device_usage": {
"time_usage": {
"past30": 5,
"past7": 5,
"today": 5
}
},
"get_fw_download_state": {
"auto_upgrade": false,
"download_progress": 0,
"reboot_time": 5,
"status": 0,
"upgrade_time": 5
},
"get_led_info": {
"led_rule": "always",
"led_status": true,
"night_mode": {
"end_time": 497,
"night_mode_type": "sunrise_sunset",
"start_time": 1032,
"sunrise_offset": 0,
"sunset_offset": 0
}
},
"get_wireless_scan_info": {
"ap_list": [
{
"bssid": "000000000000",
"channel": 0,
"cipher_type": 2,
"key_type": "wpa_psk",
"signal_level": 2,
"ssid": "I01BU0tFRF9TU0lEIw=="
}
],
"wep_supported": false
},
"qs_component_nego": {
"component_list": [
{
"id": "quick_setup",
"ver_code": 3
},
{
"id": "sunrise_sunset",
"ver_code": 1
},
{
"id": "ble_whole_setup",
"ver_code": 1
},
{
"id": "iot_cloud",
"ver_code": 1
},
{
"id": "inherit",
"ver_code": 1
},
{
"id": "firmware",
"ver_code": 2
},
{
"id": "control_child",
"ver_code": 2
},
{
"id": "child_device",
"ver_code": 2
}
],
"extra_info": {
"device_model": "P300",
"device_type": "SMART.TAPOPLUG"
}
}
}

View File

@ -354,9 +354,36 @@ class FakeSmartTransport(BaseTransport):
def _send_request(self, request_dict: dict): def _send_request(self, request_dict: dict):
method = request_dict["method"] method = request_dict["method"]
params = request_dict["params"] params = request_dict["params"]
info = self.info
if method == "control_child":
device_id = params.get("device_id")
request_data = params.get("requestData")
child_method = request_data.get("method")
child_params = request_data.get("params")
children = info["get_child_device_list"]["child_device_list"]
for child in children:
if child["device_id"] == device_id:
info = child
break
# We only support get & set device info for now.
if child_method == "get_device_info":
return {"result": info, "error_code": 0}
elif child_method == "set_device_info":
info.update(child_params)
return {"error_code": 0}
raise NotImplementedError(
"Method %s not implemented for children" % child_method
)
if method == "component_nego" or method[:4] == "get_": if method == "component_nego" or method[:4] == "get_":
if method in self.info: if method in info:
return {"result": self.info[method], "error_code": 0} return {"result": info[method], "error_code": 0}
elif ( elif (
missing_result := self.FIXTURE_MISSING_MAP.get(method) missing_result := self.FIXTURE_MISSING_MAP.get(method)
) and missing_result[0] in self.components: ) and missing_result[0] in self.components:
@ -373,7 +400,7 @@ class FakeSmartTransport(BaseTransport):
return {"error_code": 0} return {"error_code": 0}
elif method[:4] == "set_": elif method[:4] == "set_":
target_method = f"get_{method[4:]}" target_method = f"get_{method[4:]}"
self.info[target_method].update(params) info[target_method].update(params)
return {"error_code": 0} return {"error_code": 0}
async def close(self) -> None: async def close(self) -> None:

View File

@ -27,7 +27,16 @@ from kasa.discover import DiscoveryResult, _DiscoverProtocol, json_dumps
from kasa.exceptions import AuthenticationException, UnsupportedDeviceException from kasa.exceptions import AuthenticationException, UnsupportedDeviceException
from kasa.xortransport import XorEncryption from kasa.xortransport import XorEncryption
from .conftest import bulb, bulb_iot, dimmer, lightstrip, new_discovery, plug, strip from .conftest import (
bulb,
bulb_iot,
dimmer,
lightstrip,
new_discovery,
plug,
strip,
strip_iot,
)
UNSUPPORTED = { UNSUPPORTED = {
"result": { "result": {
@ -67,7 +76,7 @@ async def test_type_detection_bulb(dev: SmartDevice):
assert d.device_type == DeviceType.Bulb assert d.device_type == DeviceType.Bulb
@strip @strip_iot
async def test_type_detection_strip(dev: SmartDevice): async def test_type_detection_strip(dev: SmartDevice):
d = Discover._get_device_class(dev._last_update)("localhost") d = Discover._get_device_class(dev._last_update)("localhost")
assert d.is_strip assert d.is_strip

View File

@ -22,35 +22,6 @@ DUMMY_MULTIPLE_QUERY = {
ERRORS = [e for e in SmartErrorCode if e != 0] ERRORS = [e for e in SmartErrorCode if e != 0]
# TODO: this could be moved to conftest to make it available for other tests?
@pytest.fixture()
def dummy_protocol():
"""Return a smart protocol instance with a mocking-ready dummy transport."""
class DummyTransport(BaseTransport):
@property
def default_port(self) -> int:
return -1
@property
def credentials_hash(self) -> str:
return "dummy hash"
async def send(self, request: str) -> Dict:
return {}
async def close(self) -> None:
pass
async def reset(self) -> None:
pass
transport = DummyTransport(config=DeviceConfig(host="127.0.0.123"))
protocol = SmartProtocol(transport=transport)
return protocol
@pytest.mark.parametrize("error_code", ERRORS, ids=lambda e: e.name) @pytest.mark.parametrize("error_code", ERRORS, ids=lambda e: e.name)
async def test_smart_device_errors(dummy_protocol, mocker, error_code): async def test_smart_device_errors(dummy_protocol, mocker, error_code):
mock_response = {"result": {"great": "success"}, "error_code": error_code.value} mock_response = {"result": {"great": "success"}, "error_code": error_code.value}