Update dump_devinfo to include 20002 discovery results (#556)

* Fix dump_devinfo and add discovery_result to json

* Update following review.  Do not serialize aliases.

* Delete kasa/tests/fixtures/HS100(UK)_1.0_1.2.6.json
This commit is contained in:
sdb9696 2023-11-29 19:01:20 +00:00 committed by GitHub
parent 9728866afb
commit 9de3f69033
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 74 additions and 41 deletions

View File

@ -7,7 +7,6 @@ If you have new, yet unsupported device or a device with no devinfo file under
Executing this script will several modules and methods one by one,
and finally execute a query to query all of them at once.
"""
import asyncio
import collections.abc
import json
import logging
@ -15,9 +14,10 @@ import re
from collections import defaultdict, namedtuple
from pprint import pprint
import click
import asyncclick as click
from kasa import TPLinkSmartHomeProtocol
from kasa import Credentials, Discover
from kasa.discover import DiscoveryResult
Call = namedtuple("Call", "module method")
@ -35,6 +35,13 @@ def scrub(res):
"longitude_i",
"latitude",
"longitude",
"owner",
"device_id",
"ip",
"ssid",
"hw_id",
"fw_id",
"oem_id",
]
for k, v in res.items():
@ -44,6 +51,8 @@ def scrub(res):
if k in keys_to_scrub:
if k in ["latitude", "latitude_i", "longitude", "longitude_i"]:
v = 0
elif k in ["ip"]:
v = "127.0.0.123"
else:
v = re.sub(r"\w", "0", v)
@ -63,8 +72,22 @@ def default_to_regular(d):
@click.command()
@click.argument("host")
@click.option(
"--username",
default=None,
required=False,
envvar="TPLINK_CLOUD_USERNAME",
help="Username/email address to authenticate to device.",
)
@click.option(
"--password",
default=None,
required=False,
envvar="TPLINK_CLOUD_PASSWORD",
help="Password to use to authenticate to device.",
)
@click.option("-d", "--debug", is_flag=True)
def cli(host, debug):
async def cli(host, debug, username, password):
"""Generate devinfo file for given device."""
if debug:
logging.basicConfig(level=logging.DEBUG)
@ -83,15 +106,15 @@ def cli(host, debug):
successes = []
credentials = Credentials(username=username, password=password)
device = await Discover.discover_single(host, credentials=credentials)
for test_call in items:
async def _run_query(test_call):
protocol = TPLinkSmartHomeProtocol(host)
return await protocol.query({test_call.module: {test_call.method: None}})
try:
click.echo(f"Testing {test_call}..", nl=False)
info = asyncio.run(_run_query(test_call))
info = await device.protocol.query(
{test_call.module: {test_call.method: None}}
)
resp = info[test_call.module]
except Exception as ex:
click.echo(click.style(f"FAIL {ex}", fg="red"))
@ -111,12 +134,8 @@ def cli(host, debug):
final = default_to_regular(final)
async def _run_final_query():
protocol = TPLinkSmartHomeProtocol(host)
return await protocol.query(final_query)
try:
final = asyncio.run(_run_final_query())
final = await device.protocol.query(final_query)
except Exception as ex:
click.echo(
click.style(
@ -124,6 +143,14 @@ def cli(host, debug):
)
)
if device._discovery_info:
# 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.
dr = DiscoveryResult(**device._discovery_info)
final["discovery_result"] = dr.dict(
by_alias=False, exclude_unset=True, exclude_none=True, exclude_defaults=True
)
click.echo("Got %s successes" % len(successes))
click.echo(click.style("## device info file ##", bold=True))

View File

@ -423,7 +423,7 @@ class DiscoveryResult(BaseModel):
mac: str
mgt_encrypt_schm: EncryptionScheme
device_id: Optional[str] = Field(default=None, alias="device_type_hash")
device_id: Optional[str] = Field(default=None, alias="device_id_hash")
owner: Optional[str] = Field(default=None, alias="device_owner_hash")
hw_ver: Optional[str] = None
is_support_iot_cloud: Optional[bool] = None

View File

@ -211,6 +211,7 @@ class SmartDevice:
# checks in accessors. the @updated_required decorator does not ensure
# mypy that these are not accessed incorrectly.
self._last_update: Any = None
self._discovery_info: Optional[Dict[str, Any]] = None
self._sys_info: Any = None # TODO: this is here to avoid changing tests
self._features: Set[str] = set()
@ -371,6 +372,7 @@ class SmartDevice:
# This allows setting of some info properties directly
# from partial discovery info that will then be found
# by the requires_update decorator
self._discovery_info = info
self._set_sys_info(info)
def _set_sys_info(self, sys_info: Dict[str, Any]) -> None:

View File

@ -1,43 +1,46 @@
{
"emeter": {
"err_code": -1,
"err_msg": "module not support"
},
"smartlife.iot.common.emeter": {
"err_code": -1,
"err_msg": "module not support"
},
"smartlife.iot.dimmer": {
"err_code": -1,
"err_msg": "module not support"
},
"smartlife.iot.smartbulb.lightingservice": {
"err_code": -1,
"err_msg": "module not support"
"discovery_result": {
"device_id": "00000000000000000000000000000000",
"device_model": "HS100(UK)",
"device_type": "IOT.SMARTPLUGSWITCH",
"factory_default": true,
"hw_ver": "4.1",
"ip": "127.0.0.123",
"mac": "00-00-00-00-00-00",
"mgt_encrypt_schm": {
"encrypt_type": "KLAP",
"http_port": 80,
"is_support_https": false
},
"owner": "00000000000000000000000000000000"
},
"system": {
"get_sysinfo": {
"active_mode": "schedule",
"alias": "Unused 3",
"active_mode": "none",
"alias": "Bedroom Lamp 2",
"dev_name": "Smart Wi-Fi Plug",
"deviceId": "0000000000000000000000000000000000000000",
"err_code": 0,
"feature": "TIM",
"fwId": "00000000000000000000000000000000",
"hwId": "00000000000000000000000000000000",
"hw_ver": "4.1",
"icon_hash": "",
"latitude": 0,
"latitude_i": 0,
"led_off": 0,
"longitude": 0,
"longitude_i": 0,
"mac": "00:00:00:00:00:00",
"mic_type": "IOT.SMARTPLUGSWITCH",
"model": "HS100(UK)",
"next_action": {
"type": -1
},
"ntc_state": 0,
"oemId": "00000000000000000000000000000000",
"on_time": 0,
"relay_state": 0,
"rssi": -63,
"rssi": -66,
"status": "new",
"sw_ver": "1.1.0 Build 201016 Rel.175121",
"type": "IOT.SMARTPLUGSWITCH",
"updating": 0
}
}

View File

@ -294,9 +294,10 @@ class FakeTransportProtocol(TPLinkSmartHomeProtocol):
for target in info:
# print("target %s" % target)
for cmd in info[target]:
# print("initializing tgt %s cmd %s" % (target, cmd))
proto[target][cmd] = info[target][cmd]
if target != "discovery_result":
for cmd in info[target]:
# print("initializing tgt %s cmd %s" % (target, cmd))
proto[target][cmd] = info[target][cmd]
# if we have emeter support, we need to add the missing pieces
for module in ["emeter", "smartlife.iot.common.emeter"]:
if (