Skip to content
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
61 changes: 61 additions & 0 deletions devtools/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Tools for developers

This directory contains some simple scripts that can be useful for developers.

## dump_devinfo
* Queries the device and returns a fixture that can be added to the test suite

```shell
Usage: dump_devinfo.py [OPTIONS] HOST

Generate devinfo file for given device.

Options:
-d, --debug
--help Show this message and exit.
```

## parse_pcap

* Requires dpkt (pip install dpkt)
* Reads a pcap file and prints out the device communications

```shell
Usage: parse_pcap.py [OPTIONS] FILE

Parse pcap file and pretty print the communications and some statistics.

Options:
--help Show this message and exit.
```

## perftest

* Runs several rounds of update cycles for the given list of addresses, and prints out statistics about the performance

```shell
Usage: perf_test.py [OPTIONS] [ADDRS]...

Options:
--rounds INTEGER
--help Show this message and exit.
```

```shell
$ python perf_test.py 192.168.xx.x 192.168.xx.y 192.168.xx.z 192.168.xx.f
Running 5 rounds on ('192.168.xx.x', '192.168.xx.y', '192.168.xx.z', '192.168.xx.f')
=== Testing using gather on all devices ===
took
count mean std min 25% 50% 75% max
type
concurrently 5.0 0.097161 0.045544 0.05260 0.055332 0.088811 0.143082 0.145981
sequential 5.0 0.150506 0.005798 0.14162 0.149065 0.150499 0.155579 0.155768
=== Testing per-device performance ===
took
count mean std min 25% 50% 75% max
id
<id>-HS110(EU) 5.0 0.044917 0.014984 0.035836 0.037728 0.037950 0.041610 0.071458
<id>-KL130(EU) 5.0 0.067626 0.032027 0.046451 0.046797 0.048406 0.076136 0.120342
<id>-HS110(EU) 5.0 0.055700 0.016174 0.042086 0.045578 0.048905 0.059869 0.082064
<id>-KP303(UK) 5.0 0.010298 0.003765 0.007773 0.007968 0.008546 0.010439 0.016763
```
93 changes: 93 additions & 0 deletions devtools/perftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""Script for testing update performance on devices."""
import asyncio
import time

import asyncclick as click
import pandas as pd

from kasa import Discover


async def _update(dev, lock=None):
if lock is not None:
await lock.acquire()
await asyncio.sleep(2)
try:
start_time = time.time()
# print("%s >> Updating" % id(dev))
await dev.update()
# print("%s >> done in %s" % (id(dev), time.time() - start_time))
return {"id": f"{id(dev)}-{dev.model}", "took": (time.time() - start_time)}
finally:
if lock is not None:
lock.release()


async def _update_concurrently(devs):
start_time = time.time()
update_futures = [asyncio.ensure_future(_update(dev)) for dev in devs]
await asyncio.gather(*update_futures)
return {"type": "concurrently", "took": (time.time() - start_time)}


async def _update_sequentially(devs):
start_time = time.time()

for dev in devs:
await _update(dev)

return {"type": "sequential", "took": (time.time() - start_time)}


@click.command()
@click.argument("addrs", nargs=-1)
@click.option("--rounds", default=5)
async def main(addrs, rounds):
"""Test update performance on given devices."""
print(f"Running {rounds} rounds on {addrs}")
devs = []

for addr in addrs:
try:
dev = await Discover.discover_single(addr)
devs.append(dev)
except Exception as ex:
print(f"unable to add {addr}: {ex}")

data = []
test_gathered = True

if test_gathered:
print("=== Testing using gather on all devices ===")
for i in range(rounds):
data.append(await _update_concurrently(devs))
await asyncio.sleep(2)

await asyncio.sleep(5)

for i in range(rounds):
data.append(await _update_sequentially(devs))
await asyncio.sleep(2)

df = pd.DataFrame(data)
print(df.groupby("type").describe())

print("=== Testing per-device performance ===")

futs = []
data = []
locks = {dev: asyncio.Lock() for dev in devs}
for i in range(rounds):
for dev in devs:
futs.append(asyncio.ensure_future(_update(dev, locks[dev])))

for fut in asyncio.as_completed(futs):
res = await fut
data.append(res)

df = pd.DataFrame(data)
print(df.groupby("id").describe())


if __name__ == "__main__":
main(_anyio_backend="asyncio")