Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Fix devtools for P100 and add fixture #753

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 22 additions & 5 deletions devtools/dump_devinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
import json
import logging
import re
import traceback
from collections import defaultdict, namedtuple
from pathlib import Path
from pprint import pprint
from typing import Dict, List, Union

import asyncclick as click

from devtools.helpers.smartrequests import COMPONENT_REQUESTS, SmartRequest
from devtools.helpers.smartrequests import SmartRequest, get_component_requests
from kasa import (
AuthenticationException,
Credentials,
Expand All @@ -35,6 +36,8 @@
Call = namedtuple("Call", "module method")
SmartCall = namedtuple("SmartCall", "module request should_succeed")

_LOGGER = logging.getLogger(__name__)


def scrub(res):
"""Remove identifiers from the given dict."""
Expand Down Expand Up @@ -228,6 +231,8 @@ async def get_legacy_fixture(device):
else:
click.echo(click.style("OK", fg="green"))
successes.append((test_call, info))
finally:
await device.protocol.close()

final_query = defaultdict(defaultdict)
final = defaultdict(defaultdict)
Expand All @@ -241,7 +246,8 @@ async def get_legacy_fixture(device):
final = await device.protocol.query(final_query)
except Exception as ex:
_echo_error(f"Unable to query all successes at once: {ex}", bold=True, fg="red")

finally:
await device.protocol.close()
if device._discovery_info and not device._discovery_info.get("system"):
# Need to recreate a DiscoverResult here because we don't want the aliases
# in the fixture, we want the actual field names as returned by the device.
Expand Down Expand Up @@ -316,7 +322,11 @@ async def _make_requests_or_exit(
_echo_error(
f"Unexpected exception querying {name} at once: {ex}",
)
if _LOGGER.isEnabledFor(logging.DEBUG):
traceback.print_stack()
exit(1)
finally:
await device.protocol.close()


async def get_smart_fixture(device: SmartDevice, batch_size: int):
Expand Down Expand Up @@ -367,14 +377,15 @@ async def get_smart_fixture(device: SmartDevice, batch_size: int):

for item in component_info_response["component_list"]:
component_id = item["id"]
if requests := COMPONENT_REQUESTS.get(component_id):
ver_code = item["ver_code"]
if (requests := get_component_requests(component_id, ver_code)) is not None:
component_test_calls = [
SmartCall(module=component_id, request=request, should_succeed=True)
for request in requests
]
test_calls.extend(component_test_calls)
should_succeed.extend(component_test_calls)
elif component_id not in COMPONENT_REQUESTS:
else:
click.echo(f"Skipping {component_id}..", nl=False)
click.echo(click.style("UNSUPPORTED", fg="yellow"))

Expand All @@ -396,7 +407,11 @@ async def get_smart_fixture(device: SmartDevice, batch_size: int):
if (
not test_call.should_succeed
and hasattr(ex, "error_code")
and ex.error_code == SmartErrorCode.UNKNOWN_METHOD_ERROR
and ex.error_code
in [
SmartErrorCode.UNKNOWN_METHOD_ERROR,
SmartErrorCode.TRANSPORT_NOT_AVAILABLE_ERROR,
]
):
click.echo(click.style("FAIL - EXPECTED", fg="green"))
else:
Expand All @@ -410,6 +425,8 @@ async def get_smart_fixture(device: SmartDevice, batch_size: int):
else:
click.echo(click.style("OK", fg="green"))
successes.append(test_call)
finally:
await device.protocol.close()

requests = []
for succ in successes:
Expand Down
63 changes: 41 additions & 22 deletions devtools/helpers/smartrequests.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,14 @@ def get_device_usage() -> "SmartRequest":
return SmartRequest("get_device_usage")

@staticmethod
def device_info_list() -> List["SmartRequest"]:
def device_info_list(ver_code) -> List["SmartRequest"]:
"""Get device info list."""
if ver_code == 1:
return [SmartRequest.get_device_info()]
return [
SmartRequest.get_device_info(),
SmartRequest.get_device_usage(),
SmartRequest.get_auto_update_info(),
]

@staticmethod
Expand All @@ -149,7 +152,6 @@ def get_auto_update_info() -> "SmartRequest":
def firmware_info_list() -> List["SmartRequest"]:
"""Get info list."""
return [
SmartRequest.get_auto_update_info(),
SmartRequest.get_raw_request("get_fw_download_state"),
SmartRequest.get_raw_request("get_latest_fw"),
]
Expand All @@ -165,9 +167,13 @@ def get_device_time() -> "SmartRequest":
return SmartRequest("get_device_time")

@staticmethod
def get_wireless_scan_info() -> "SmartRequest":
def get_wireless_scan_info(
params: Optional[GetRulesParams] = None
) -> "SmartRequest":
"""Get wireless scan info."""
return SmartRequest("get_wireless_scan_info")
return SmartRequest(
"get_wireless_scan_info", params or SmartRequest.GetRulesParams()
)

@staticmethod
def get_schedule_rules(params: Optional[GetRulesParams] = None) -> "SmartRequest":
Expand Down Expand Up @@ -294,9 +300,13 @@ def set_dynamic_light_effect_rule_enable(
@staticmethod
def get_component_info_requests(component_nego_response) -> List["SmartRequest"]:
"""Get a list of requests based on the component info response."""
request_list = []
request_list: List["SmartRequest"] = []
for component in component_nego_response["component_list"]:
if requests := COMPONENT_REQUESTS.get(component["id"]):
if (
requests := get_component_requests(
component["id"], int(component["ver_code"])
)
) is not None:
request_list.extend(requests)
return request_list

Expand All @@ -314,8 +324,17 @@ def _create_request_dict(
return request


def get_component_requests(component_id, ver_code):
"""Get the requests supported by the component and version."""
if (cr := COMPONENT_REQUESTS.get(component_id)) is None:
return None
if callable(cr):
return cr(ver_code)
return cr


COMPONENT_REQUESTS = {
"device": SmartRequest.device_info_list(),
"device": SmartRequest.device_info_list,
"firmware": SmartRequest.firmware_info_list(),
"quick_setup": [SmartRequest.qs_component_nego()],
"inherit": [SmartRequest.get_raw_request("get_inherit_info")],
Expand All @@ -324,33 +343,33 @@ def _create_request_dict(
"schedule": SmartRequest.schedule_info_list(),
"countdown": [SmartRequest.get_countdown_rules()],
"antitheft": [SmartRequest.get_antitheft_rules()],
"account": None,
"synchronize": None, # sync_env
"sunrise_sunset": None, # for schedules
"account": [],
"synchronize": [], # sync_env
"sunrise_sunset": [], # for schedules
"led": [SmartRequest.get_led_info()],
"cloud_connect": [SmartRequest.get_raw_request("get_connect_cloud_state")],
"iot_cloud": None,
"device_local_time": None,
"default_states": None, # in device_info
"iot_cloud": [],
"device_local_time": [],
"default_states": [], # in device_info
"auto_off": [SmartRequest.get_auto_off_config()],
"localSmart": None,
"localSmart": [],
"energy_monitoring": SmartRequest.energy_monitoring_list(),
"power_protection": SmartRequest.power_protection_list(),
"current_protection": None, # overcurrent in device_info
"matter": None,
"current_protection": [], # overcurrent in device_info
"matter": [],
"preset": [SmartRequest.get_preset_rules()],
"brightness": None, # in device_info
"color": None, # in device_info
"color_temperature": None, # in device_info
"brightness": [], # in device_info
"color": [], # in device_info
"color_temperature": [], # in device_info
"auto_light": [SmartRequest.get_auto_light_info()],
"light_effect": [SmartRequest.get_dynamic_light_effect_rules()],
"bulb_quick_control": None,
"bulb_quick_control": [],
"on_off_gradually": [SmartRequest.get_raw_request("get_on_off_gradually_info")],
"light_strip": None,
"light_strip": [],
"light_strip_lighting_effect": [
SmartRequest.get_raw_request("get_lighting_effect")
],
"music_rhythm": None, # music_rhythm_enable in device_info
"music_rhythm": [], # music_rhythm_enable in device_info
"segment": [SmartRequest.get_raw_request("get_device_segment")],
"segment_effect": [SmartRequest.get_raw_request("get_segment_effect_rule")],
}
173 changes: 173 additions & 0 deletions kasa/tests/fixtures/smart/P100_1.0.0_1.1.3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
{
"component_nego": {
"component_list": [
{
"id": "device",
"ver_code": 1
},
{
"id": "firmware",
"ver_code": 1
},
{
"id": "quick_setup",
"ver_code": 1
},
{
"id": "time",
"ver_code": 1
},
{
"id": "wireless",
"ver_code": 1
},
{
"id": "schedule",
"ver_code": 1
},
{
"id": "countdown",
"ver_code": 1
},
{
"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
}
]
},
"discovery_result": {
"device_id": "00000000000000000000000000000000",
"device_model": "P100",
"device_type": "SMART.TAPOPLUG",
"factory_default": false,
"ip": "127.0.0.123",
"mac": "1C-3B-F3-00-00-00",
"mgt_encrypt_schm": {
"encrypt_type": "AES",
"http_port": 80,
"is_support_https": false
},
"owner": "00000000000000000000000000000000"
},
"get_antitheft_rules": {
"antitheft_rule_max_count": 1,
"enable": false,
"rule_list": []
},
"get_connect_cloud_state": {
"status": -1001
},
"get_countdown_rules": {
"countdown_rule_max_count": 1,
"enable": false,
"rule_list": []
},
"get_device_info": {
"avatar": "plug",
"device_id": "0000000000000000000000000000000000000000",
"device_on": true,
"fw_id": "00000000000000000000000000000000",
"fw_ver": "1.1.3 Build 20191017 Rel. 57937",
"has_set_location_info": true,
"hw_id": "00000000000000000000000000000000",
"hw_ver": "1.0.0",
"ip": "127.0.0.123",
"latitude": 0,
"location": "hallway",
"longitude": 0,
"mac": "1C-3B-F3-00-00-00",
"model": "P100",
"nickname": "I01BU0tFRF9OQU1FIw==",
"oem_id": "00000000000000000000000000000000",
"on_time": 6868,
"overheated": false,
"signal_level": 2,
"specs": "US",
"ssid": "I01BU0tFRF9TU0lEIw==",
"time_usage_past30": 114,
"time_usage_past7": 114,
"time_usage_today": 114,
"type": "SMART.TAPOPLUG"
},
"get_device_time": {
"region": "Europe/London",
"time_diff": 0,
"timestamp": 1707905077
},
"get_fw_download_state": {
"download_progress": 0,
"reboot_time": 10,
"status": 0,
"upgrade_time": 0
},
"get_latest_fw": {
"fw_size": 786432,
"fw_ver": "1.3.7 Build 20230711 Rel.61904",
"hw_id": "00000000000000000000000000000000",
"need_to_upgrade": true,
"oem_id": "00000000000000000000000000000000",
"release_date": "2023-07-26",
"release_note": "Modifications and Bug fixes:\nEnhanced device security.",
"type": 3
},
"get_led_info": {
"led_rule": "always",
"led_status": true
},
"get_next_event": {
"action": -1,
"e_time": 0,
"id": "0",
"s_time": 0,
"type": 0
},
"get_schedule_rules": {
"enable": false,
"rule_list": [],
"schedule_rule_max_count": 20,
"start_index": 0,
"sum": 0
},
"get_wireless_scan_info": {
"ap_list": [],
"start_index": 0,
"sum": 0,
"wep_supported": false
},
"qs_component_nego": {
"component_list": [
{
"id": "quick_setup",
"ver_code": 1
},
{
"id": "sunrise_sunset",
"ver_code": 1
}
],
"extra_info": {
"device_model": "P100",
"device_type": "SMART.TAPOPLUG"
}
}
}