async++, small powerstrip improvements (#46)

* async++, small powerstrip improvements

* use asyncclick instead of click, allows defining the commands with async def to avoid manual eventloop/asyncio.run handling
* improve powerstrip support:
  * new powerstrip api: turn_{on,off}_by_{name,index} methods
  * cli: fix on/off for powerstrip using the new apis
* add missing update()s for cli's hsv, led, temperature (fixes #43)
* prettyprint the received payloads when debug mode in use
* cli: debug mode can be activated now with '-d'

* update requirements_test.txt

* remove outdated click-datetime, replace click with asyncclick

* debug is a flag

* make smartstripplug to inherit the sysinfo from its parent, allows for simple access of general plug properties

* proper bound checking for index accesses, allow controlling the plug at index 0

* remove the mess of turn_{on,off}_by_{name,index}, get_plug_by_{name,index} are enough.

* adapt cli to use that
* allow changing the alias per index

* use f-strings consistently everywhere in the cli

* add tests for get_plug_by_{index,name}
This commit is contained in:
Teemu R
2020-04-21 20:46:13 +02:00
committed by GitHub
parent 852ae494af
commit 3fe578cf26
11 changed files with 203 additions and 120 deletions

View File

@@ -92,13 +92,12 @@ def dev(request):
Provides a device (given --ip) or parametrized fixture for the supported devices.
The initial update is called automatically before returning the device.
"""
loop = asyncio.get_event_loop()
file = request.param
ip = request.config.getoption("--ip")
if ip:
d = loop.run_until_complete(Discover.discover_single(ip))
loop.run_until_complete(d.update())
d = asyncio.run(Discover.discover_single(ip))
asyncio.run(d.update())
print(d.model)
if d.model in file:
return d
@@ -125,7 +124,7 @@ def dev(request):
model = basename(file)
p = device_for_file(model)(host="123.123.123.123")
p.protocol = FakeTransportProtocol(sysinfo)
loop.run_until_complete(p.update())
asyncio.run(p.update())
yield p

View File

@@ -1,25 +1,26 @@
import asyncio
from click.testing import CliRunner
import pytest
from asyncclick.testing import CliRunner
from kasa import SmartDevice
from kasa.cli import alias, brightness, emeter, raw_command, state, sysinfo
from .conftest import handle_turn_on, turn_on
pytestmark = pytest.mark.asyncio
def test_sysinfo(dev):
async def test_sysinfo(dev):
runner = CliRunner()
res = runner.invoke(sysinfo, obj=dev)
res = await runner.invoke(sysinfo, obj=dev)
assert "System info" in res.output
assert dev.alias in res.output
@turn_on
def test_state(dev, turn_on):
asyncio.run(handle_turn_on(dev, turn_on))
async def test_state(dev, turn_on):
await handle_turn_on(dev, turn_on)
runner = CliRunner()
res = runner.invoke(state, obj=dev)
res = await runner.invoke(state, obj=dev)
print(res.output)
if dev.is_on:
@@ -31,36 +32,36 @@ def test_state(dev, turn_on):
assert "Device has no emeter" in res.output
def test_alias(dev):
async def test_alias(dev):
runner = CliRunner()
res = runner.invoke(alias, obj=dev)
res = await runner.invoke(alias, obj=dev)
assert f"Alias: {dev.alias}" in res.output
new_alias = "new alias"
res = runner.invoke(alias, [new_alias], obj=dev)
res = await runner.invoke(alias, [new_alias], obj=dev)
assert f"Setting alias to {new_alias}" in res.output
res = runner.invoke(alias, obj=dev)
res = await runner.invoke(alias, obj=dev)
assert f"Alias: {new_alias}" in res.output
def test_raw_command(dev):
async def test_raw_command(dev):
runner = CliRunner()
res = runner.invoke(raw_command, ["system", "get_sysinfo"], obj=dev)
res = await runner.invoke(raw_command, ["system", "get_sysinfo"], obj=dev)
assert res.exit_code == 0
assert dev.alias in res.output
res = runner.invoke(raw_command, obj=dev)
res = await runner.invoke(raw_command, obj=dev)
assert res.exit_code != 0
assert "Usage" in res.output
def test_emeter(dev: SmartDevice, mocker):
async def test_emeter(dev: SmartDevice, mocker):
runner = CliRunner()
res = runner.invoke(emeter, obj=dev)
res = await runner.invoke(emeter, obj=dev)
if not dev.has_emeter:
assert "Device has no emeter" in res.output
return
@@ -68,52 +69,52 @@ def test_emeter(dev: SmartDevice, mocker):
assert "Current State" in res.output
monthly = mocker.patch.object(dev, "get_emeter_monthly")
res = runner.invoke(emeter, ["--year", "1900"], obj=dev)
res = await runner.invoke(emeter, ["--year", "1900"], obj=dev)
assert "For year" in res.output
monthly.assert_called()
daily = mocker.patch.object(dev, "get_emeter_daily")
res = runner.invoke(emeter, ["--month", "1900-12"], obj=dev)
res = await runner.invoke(emeter, ["--month", "1900-12"], obj=dev)
assert "For month" in res.output
daily.assert_called()
def test_brightness(dev):
async def test_brightness(dev):
runner = CliRunner()
res = runner.invoke(brightness, obj=dev)
res = await runner.invoke(brightness, obj=dev)
if not dev.is_dimmable:
assert "This device does not support brightness." in res.output
return
res = runner.invoke(brightness, obj=dev)
res = await runner.invoke(brightness, obj=dev)
assert f"Brightness: {dev.brightness}" in res.output
res = runner.invoke(brightness, ["12"], obj=dev)
res = await runner.invoke(brightness, ["12"], obj=dev)
assert "Setting brightness" in res.output
res = runner.invoke(brightness, obj=dev)
res = await runner.invoke(brightness, obj=dev)
assert f"Brightness: 12" in res.output
def test_temperature(dev):
async def test_temperature(dev):
pass
def test_hsv(dev):
async def test_hsv(dev):
pass
def test_led(dev):
async def test_led(dev):
pass
def test_on(dev):
async def test_on(dev):
pass
def test_off(dev):
async def test_off(dev):
pass
def test_reboot(dev):
async def test_reboot(dev):
pass

View File

@@ -425,6 +425,26 @@ async def test_children_on_since(dev):
assert plug.on_since
@strip
async def test_get_plug_by_name(dev: SmartStrip):
name = dev.plugs[0].alias
assert dev.get_plug_by_name(name) == dev.plugs[0]
with pytest.raises(SmartDeviceException):
dev.get_plug_by_name("NONEXISTING NAME")
@strip
async def test_get_plug_by_index(dev: SmartStrip):
assert dev.get_plug_by_index(0) == dev.plugs[0]
with pytest.raises(SmartDeviceException):
dev.get_plug_by_index(-1)
with pytest.raises(SmartDeviceException):
dev.get_plug_by_index(len(dev.plugs))
@pytest.mark.skip("this test will wear out your relays")
async def test_all_binary_states(dev):
# test every binary state