mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-22 19:23:34 +00:00
Add --child option to feature command (#789)
This allows listing and changing child device features that were previously not accessible using the cli tool.
This commit is contained in:
parent
7884436679
commit
c61f2e931c
25
kasa/cli.py
25
kasa/cli.py
@ -1156,22 +1156,39 @@ async def shell(dev: Device):
|
|||||||
@cli.command(name="feature")
|
@cli.command(name="feature")
|
||||||
@click.argument("name", required=False)
|
@click.argument("name", required=False)
|
||||||
@click.argument("value", required=False)
|
@click.argument("value", required=False)
|
||||||
|
@click.option("--child", required=False)
|
||||||
@pass_dev
|
@pass_dev
|
||||||
async def feature(dev, name: str, value):
|
async def feature(dev: Device, child: str, name: str, value):
|
||||||
"""Access and modify features.
|
"""Access and modify features.
|
||||||
|
|
||||||
If no *name* is given, lists available features and their values.
|
If no *name* is given, lists available features and their values.
|
||||||
If only *name* is given, the value of named feature is returned.
|
If only *name* is given, the value of named feature is returned.
|
||||||
If both *name* and *value* are set, the described setting is changed.
|
If both *name* and *value* are set, the described setting is changed.
|
||||||
"""
|
"""
|
||||||
|
if child is not None:
|
||||||
|
echo(f"Targeting child device {child}")
|
||||||
|
dev = dev.get_child_device(child)
|
||||||
if not name:
|
if not name:
|
||||||
|
|
||||||
|
def _print_features(dev):
|
||||||
|
for name, feat in dev.features.items():
|
||||||
|
try:
|
||||||
|
echo(f"\t{feat.name} ({name}): {feat.value}")
|
||||||
|
except Exception as ex:
|
||||||
|
echo(f"\t{feat.name} ({name}): [red]{ex}[/red]")
|
||||||
|
|
||||||
echo("[bold]== Features ==[/bold]")
|
echo("[bold]== Features ==[/bold]")
|
||||||
for name, feat in dev.features.items():
|
_print_features(dev)
|
||||||
echo(f"{feat.name} ({name}): {feat.value}")
|
|
||||||
|
if dev.children:
|
||||||
|
for child_dev in dev.children:
|
||||||
|
echo(f"[bold]== Child {child_dev.alias} ==")
|
||||||
|
_print_features(child_dev)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if name not in dev.features:
|
if name not in dev.features:
|
||||||
echo(f"No feature by name {name}")
|
echo(f"No feature by name '{name}'")
|
||||||
return
|
return
|
||||||
|
|
||||||
feat = dev.features[name]
|
feat = dev.features[name]
|
||||||
|
@ -573,7 +573,7 @@ def unsupported_device_info(request, mocker):
|
|||||||
yield discovery_data
|
yield discovery_data
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture
|
||||||
def dummy_protocol():
|
def dummy_protocol():
|
||||||
"""Return a smart protocol instance with a mocking-ready dummy transport."""
|
"""Return a smart protocol instance with a mocking-ready dummy transport."""
|
||||||
|
|
||||||
|
@ -32,7 +32,14 @@ from kasa.cli import (
|
|||||||
from kasa.discover import Discover, DiscoveryResult
|
from kasa.discover import Discover, DiscoveryResult
|
||||||
from kasa.iot import IotDevice
|
from kasa.iot import IotDevice
|
||||||
|
|
||||||
from .conftest import device_iot, device_smart, handle_turn_on, new_discovery, turn_on
|
from .conftest import (
|
||||||
|
device_iot,
|
||||||
|
device_smart,
|
||||||
|
get_device_for_file,
|
||||||
|
handle_turn_on,
|
||||||
|
new_discovery,
|
||||||
|
turn_on,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_update_called_by_cli(dev, mocker):
|
async def test_update_called_by_cli(dev, mocker):
|
||||||
@ -684,3 +691,99 @@ async def test_errors(mocker):
|
|||||||
)
|
)
|
||||||
assert res.exit_code == 2
|
assert res.exit_code == 2
|
||||||
assert "Raised error:" not in res.output
|
assert "Raised error:" not in res.output
|
||||||
|
|
||||||
|
|
||||||
|
async def test_feature(mocker):
|
||||||
|
"""Test feature command."""
|
||||||
|
dummy_device = await get_device_for_file("P300(EU)_1.0_1.0.13.json", "SMART")
|
||||||
|
mocker.patch("kasa.discover.Discover.discover_single", return_value=dummy_device)
|
||||||
|
runner = CliRunner()
|
||||||
|
res = await runner.invoke(
|
||||||
|
cli,
|
||||||
|
["--host", "127.0.0.123", "--debug", "feature"],
|
||||||
|
catch_exceptions=False,
|
||||||
|
)
|
||||||
|
assert "LED" in res.output
|
||||||
|
assert "== Child " in res.output # child listing
|
||||||
|
|
||||||
|
assert res.exit_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
async def test_feature_single(mocker):
|
||||||
|
"""Test feature command returning single value."""
|
||||||
|
dummy_device = await get_device_for_file("P300(EU)_1.0_1.0.13.json", "SMART")
|
||||||
|
mocker.patch("kasa.discover.Discover.discover_single", return_value=dummy_device)
|
||||||
|
runner = CliRunner()
|
||||||
|
res = await runner.invoke(
|
||||||
|
cli,
|
||||||
|
["--host", "127.0.0.123", "--debug", "feature", "led"],
|
||||||
|
catch_exceptions=False,
|
||||||
|
)
|
||||||
|
assert "LED" in res.output
|
||||||
|
assert "== Features ==" not in res.output
|
||||||
|
assert res.exit_code == 0
|
||||||
|
|
||||||
|
async def test_feature_missing(mocker):
|
||||||
|
"""Test feature command returning single value."""
|
||||||
|
dummy_device = await get_device_for_file("P300(EU)_1.0_1.0.13.json", "SMART")
|
||||||
|
mocker.patch("kasa.discover.Discover.discover_single", return_value=dummy_device)
|
||||||
|
runner = CliRunner()
|
||||||
|
res = await runner.invoke(
|
||||||
|
cli,
|
||||||
|
["--host", "127.0.0.123", "--debug", "feature", "missing"],
|
||||||
|
catch_exceptions=False,
|
||||||
|
)
|
||||||
|
assert "No feature by name 'missing'" in res.output
|
||||||
|
assert "== Features ==" not in res.output
|
||||||
|
assert res.exit_code == 0
|
||||||
|
|
||||||
|
async def test_feature_set(mocker):
|
||||||
|
"""Test feature command's set value."""
|
||||||
|
dummy_device = await get_device_for_file("P300(EU)_1.0_1.0.13.json", "SMART")
|
||||||
|
led_setter = mocker.patch("kasa.smart.modules.ledmodule.LedModule.set_led")
|
||||||
|
mocker.patch("kasa.discover.Discover.discover_single", return_value=dummy_device)
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
res = await runner.invoke(
|
||||||
|
cli,
|
||||||
|
["--host", "127.0.0.123", "--debug", "feature", "led", "True"],
|
||||||
|
catch_exceptions=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
led_setter.assert_called_with(True)
|
||||||
|
assert "Setting led to True" in res.output
|
||||||
|
assert res.exit_code == 0
|
||||||
|
|
||||||
|
|
||||||
|
async def test_feature_set_child(mocker):
|
||||||
|
"""Test feature command's set value."""
|
||||||
|
dummy_device = await get_device_for_file("P300(EU)_1.0_1.0.13.json", "SMART")
|
||||||
|
setter = mocker.patch("kasa.smart.smartdevice.SmartDevice.set_state")
|
||||||
|
|
||||||
|
mocker.patch("kasa.discover.Discover.discover_single", return_value=dummy_device)
|
||||||
|
get_child_device = mocker.spy(dummy_device, "get_child_device")
|
||||||
|
|
||||||
|
child_id = "000000000000000000000000000000000000000001"
|
||||||
|
|
||||||
|
runner = CliRunner()
|
||||||
|
res = await runner.invoke(
|
||||||
|
cli,
|
||||||
|
[
|
||||||
|
"--host",
|
||||||
|
"127.0.0.123",
|
||||||
|
"--debug",
|
||||||
|
"feature",
|
||||||
|
"--child",
|
||||||
|
child_id,
|
||||||
|
"state",
|
||||||
|
"False",
|
||||||
|
],
|
||||||
|
catch_exceptions=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
get_child_device.assert_called()
|
||||||
|
setter.assert_called_with(False)
|
||||||
|
|
||||||
|
assert f"Targeting child device {child_id}"
|
||||||
|
assert "Setting state to False" in res.output
|
||||||
|
assert res.exit_code == 0
|
||||||
|
Loading…
Reference in New Issue
Block a user