8000 move dump-devinfo into a separate tool under devtools · python-kasa/python-kasa@e8ec9f1 · GitHub
[go: up one dir, main page]

Skip to content

Commit e8ec9f1

Browse files
committed
move dump-devinfo into a separate tool under devtools
1 parent 0d34187 commit e8ec9f1

File tree

2 files changed

+143
-35
lines changed

2 files changed

+143
-35
lines changed

devtools/dump_devinfo.py

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
"""This script generates devinfo files for the test suite.
2+
3+
If you have new, yet unsupported device or a device with no devinfo file under kasa/tests/fixtures,
4+
feel free to run this script and create a PR to add the file to the repository.
5+
6+
Executing this script will several modules and methods one by one,
7+
and finally execute a query to query all of them at once.
8+
"""
9+
import asyncio
10+
import collections.abc
11+
import json
12+
import logging
13+
import re
14+
from collections import defaultdict, namedtuple
15+
from pprint import pprint
16+
17+
import click
18+
from kasa import TPLinkSmartHomeProtocol
19+
20+
Call = namedtuple("Call", "module method")
21+
22+
23+
def scrub(res):
24+
"""Remove identifiers from the given dict."""
25+
keys_to_scrub = [
26+
"deviceId",
27+
"fwId",
28+
"hwId",
29+
"oemId",
30+
"mac",
31+
"mic_mac",
32+
"latitude_i",
33+
"longitude_i",
34+
"latitude",
35+
"longitude",
36+
]
37+
38+
for k, v in res.items():
39+
if isinstance(v, collections.abc.Mapping):
40+
res[k] = scrub(res.get(k))
41+
else:
42+
if k in keys_to_scrub:
43+
if k in ["latitude_i", "longitude_i"]:
44+
v = 0
45+
else:
46+
v = re.sub(r"\w", "0", v)
47+
48+
res[k] = v
49+
return res
50+
51+
52+
def default_to_regular(d):
53+
"""Convert nested defaultdicts to regular ones.
54+
55+
From https://stackoverflow.com/a/26496899
56+
"""
57+
if isinstance(d, defaultdict):
58+
d = {k: default_to_regular(v) for k, v in d.items()}
59+
return d
60+
61+
62+
@click.command()
63+
@click.argument("host")
64+
@click.option("--debug")
65+
def cli(host, debug):
66+
"""Generate devinfo file for given device."""
67+
if debug:
68+
logging.basicConfig(level=logging.DEBUG)
69+
70+
items = [
71+
Call(module="system", method="get_sysinfo"),
72+
Call(module="emeter", method="get_realtime"),
73+
Call(module="smartlife.iot.dimmer", method="get_dimmer_parameters"),
74+
Call(module="smartlife.iot.common.emeter", method="get_realtime"),
75+
Call(
76+
module="smartlife.iot.smartbulb.lightingservice", method="get_light_state"
77+
),
78+
]
79+
80+
protocol = TPLinkSmartHomeProtocol()
81+
82+
successes = []
83+
84+
for test_call in items:
85+
try:
86+
click.echo(f"Testing {test_call}..", nl=False)
87+
info = asyncio.run(
88+
protocol.query(host, {test_call.module: {test_call.method: None}})
89+
)
90+
resp = info[test_call.module]
91+
except Exception as ex:
92+
click.echo(click.style(f"FAIL {ex}", fg="red"))
93+
else:
94+
if "err_msg" in resp:
95+
click.echo(click.style(f"FAIL {resp['err_msg']}", fg="red"))
96+
else:
97+
click.echo(click.style("OK", fg="green"))
98+
successes.append((test_call, info))
99+
100+
final_query = defaultdict(defaultdict)
101+
final = defaultdict(defaultdict)
102+
103+
for succ, resp in successes:
104+
final_query[succ.module][succ.method] = None
105+
final[succ.module][succ.method] = resp
106+
107+
final = default_to_regular(final)
108+
109+
try:
110+
final = asyncio.run(protocol.query(host, final_query))
111+
except Exception as ex:
112+
click.echo(
113+
click.style(
114+< F438 div class="diff-text-inner"> f"Unable to query all successes at once: {ex}", bold=True, fg="red"
115+
)
116+
)
117+
118+
click.echo("Got %s successes" % len(successes))
119+
click.echo(click.style("## device info file ##", bold=True))
120+
121+
sysinfo = final["system"]["get_sysinfo"]
122+
model = sysinfo["model"]
123+
hw_version = sysinfo["hw_ver"]
124+
sw_version = sysinfo["sw_ver"]
125+
sw_version = sw_version.split(" ", maxsplit=1)[0]
126+
save_to = f"{model}_{hw_version}_{sw_version}.json"
127+
pprint(scrub(final))
128+
save = click.prompt(f"Do you want to save the above content to {save_to} (y/n)")
129+
if save == "y":
130+
click.echo(f"Saving info to {save_to}")
131+
132+
with open(save_to, "w") as f:
133+
json.dump(final, f, sort_keys=True, indent=4)
134+
f.write("\n")
135+
else:
136+
click.echo("Not saving.")
137+
138+
139+
if __name__ == "__main__":
140+
cli()

kasa/cli.py

Lines changed: 3 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
"""python-kasa cli tool."""
2-
import json
32
import logging
4-
import re
53
from pprint import pformat as pf
64
from typing import cast
75

@@ -131,39 +129,9 @@ async def dump_discover(ctx, scrub):
131129
132130
Useful for dumping into a file to be added to the test suite.
133131
"""
134-
target = ctx.parent.params["target"]
135-
keys_to_scrub = [
136-
"deviceId",
137-
"fwId",
138-
"hwId",
139-
"oemId",
140-
"mac",
141-
"latitude_i",
142-
"longitude_i",
143-
"latitude",
144-
"longitude",
145-
]
146-
devs = await Discover.discover(target=target, return_raw=True)
147-
if scrub:
148-
click.echo("Scrubbing personal data before writing")
149-
for dev in devs.values():
150-
if scrub:
151-
for key in keys_to_scrub:
152-
if key in dev["system"]["get_sysinfo"]:
153-
val = dev["system"]["get_sysinfo"][key]
154-
if key in ["latitude_i", "longitude_i"]:
155-
val = 0
156-
else:
157-
val = re.sub(r"\w", "0", val)
158-
dev["system"]["get_sysinfo"][key] = val
159-
160-
model = dev["system"]["get_sysinfo"]["model"]
161-
hw_version = dev["system"]["get_sysinfo"]["hw_ver"]
162-
save_to = f"{model}_{hw_version}.json"
163-
click.echo(f"Saving info to {save_to}")
164-
with open(save_to, "w") as f:
165-
json.dump(dev, f, sort_keys=True, indent=4)
166-
f.write("\n")
132+
click.echo(
133+
"This is deprecated, use the script inside devtools to generate a devinfo file."
134+
)
167135

168136

169137
@cli.command()

0 commit comments

Comments
 (0)
0