2024-07-23 18:13:52 +00:00
|
|
|
"""Module for cli device commands."""
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
from pprint import pformat as pf
|
|
|
|
|
|
|
|
import asyncclick as click
|
|
|
|
|
|
|
|
from kasa import (
|
|
|
|
Device,
|
|
|
|
Module,
|
|
|
|
)
|
|
|
|
from kasa.smart import SmartDevice
|
|
|
|
|
|
|
|
from .common import (
|
|
|
|
echo,
|
|
|
|
error,
|
|
|
|
pass_dev,
|
|
|
|
pass_dev_or_child,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@click.group()
|
|
|
|
@pass_dev_or_child
|
|
|
|
def device(dev):
|
|
|
|
"""Commands to control basic device settings."""
|
|
|
|
|
|
|
|
|
|
|
|
@device.command()
|
|
|
|
@pass_dev_or_child
|
|
|
|
@click.pass_context
|
|
|
|
async def state(ctx, dev: Device):
|
|
|
|
"""Print out device state and versions."""
|
|
|
|
from .feature import _echo_all_features
|
|
|
|
|
|
|
|
verbose = ctx.parent.params.get("verbose", False) if ctx.parent else False
|
|
|
|
|
|
|
|
echo(f"[bold]== {dev.alias} - {dev.model} ==[/bold]")
|
|
|
|
echo(f"Host: {dev.host}")
|
|
|
|
echo(f"Port: {dev.port}")
|
|
|
|
echo(f"Device state: {dev.is_on}")
|
|
|
|
|
2024-10-08 07:16:51 +00:00
|
|
|
echo(f"Time: {dev.time} (tz: {dev.timezone})")
|
2024-07-23 18:13:52 +00:00
|
|
|
echo(f"Hardware: {dev.hw_info['hw_ver']}")
|
|
|
|
echo(f"Software: {dev.hw_info['sw_ver']}")
|
|
|
|
echo(f"MAC (rssi): {dev.mac} ({dev.rssi})")
|
|
|
|
if verbose:
|
|
|
|
echo(f"Location: {dev.location}")
|
|
|
|
|
|
|
|
echo()
|
|
|
|
_echo_all_features(dev.features, verbose=verbose)
|
|
|
|
|
|
|
|
if verbose:
|
|
|
|
echo("\n[bold]== Modules ==[/bold]")
|
|
|
|
for module in dev.modules.values():
|
|
|
|
echo(f"[green]+ {module}[/green]")
|
|
|
|
|
|
|
|
if dev.children:
|
|
|
|
echo("\n[bold]== Children ==[/bold]")
|
|
|
|
for child in dev.children:
|
|
|
|
_echo_all_features(
|
|
|
|
child.features,
|
|
|
|
title_prefix=f"{child.alias} ({child.model})",
|
|
|
|
verbose=verbose,
|
|
|
|
indent="\t",
|
|
|
|
)
|
|
|
|
if verbose:
|
|
|
|
echo(f"\n\t[bold]== Child {child.alias} Modules ==[/bold]")
|
|
|
|
for module in child.modules.values():
|
|
|
|
echo(f"\t[green]+ {module}[/green]")
|
|
|
|
echo()
|
|
|
|
|
|
|
|
if verbose:
|
|
|
|
echo("\n\t[bold]== Protocol information ==[/bold]")
|
|
|
|
echo(f"\tCredentials hash: {dev.credentials_hash}")
|
|
|
|
echo()
|
|
|
|
from .discover import _echo_discovery_info
|
|
|
|
|
|
|
|
_echo_discovery_info(dev._discovery_info)
|
|
|
|
|
|
|
|
return dev.internal_state
|
|
|
|
|
|
|
|
|
|
|
|
@device.command()
|
|
|
|
@pass_dev_or_child
|
|
|
|
async def sysinfo(dev):
|
|
|
|
"""Print out full system information."""
|
|
|
|
echo("== System info ==")
|
|
|
|
echo(pf(dev.sys_info))
|
|
|
|
return dev.sys_info
|
|
|
|
|
|
|
|
|
|
|
|
@device.command()
|
|
|
|
@click.option("--transition", type=int, required=False)
|
|
|
|
@pass_dev_or_child
|
|
|
|
async def on(dev: Device, transition: int):
|
|
|
|
"""Turn the device on."""
|
|
|
|
echo(f"Turning on {dev.alias}")
|
|
|
|
return await dev.turn_on(transition=transition)
|
|
|
|
|
|
|
|
|
2024-09-27 16:36:45 +00:00
|
|
|
@device.command
|
2024-07-23 18:13:52 +00:00
|
|
|
@click.option("--transition", type=int, required=False)
|
|
|
|
@pass_dev_or_child
|
|
|
|
async def off(dev: Device, transition: int):
|
|
|
|
"""Turn the device off."""
|
|
|
|
echo(f"Turning off {dev.alias}")
|
|
|
|
return await dev.turn_off(transition=transition)
|
|
|
|
|
|
|
|
|
|
|
|
@device.command()
|
|
|
|
@click.option("--transition", type=int, required=False)
|
|
|
|
@pass_dev_or_child
|
|
|
|
async def toggle(dev: Device, transition: int):
|
|
|
|
"""Toggle the device on/off."""
|
|
|
|
if dev.is_on:
|
|
|
|
echo(f"Turning off {dev.alias}")
|
|
|
|
return await dev.turn_off(transition=transition)
|
|
|
|
|
|
|
|
echo(f"Turning on {dev.alias}")
|
|
|
|
return await dev.turn_on(transition=transition)
|
|
|
|
|
|
|
|
|
|
|
|
@device.command()
|
|
|
|
@click.argument("state", type=bool, required=False)
|
|
|
|
@pass_dev_or_child
|
|
|
|
async def led(dev: Device, state):
|
|
|
|
"""Get or set (Plug's) led state."""
|
|
|
|
if not (led := dev.modules.get(Module.Led)):
|
|
|
|
error("Device does not support led.")
|
|
|
|
return
|
|
|
|
if state is not None:
|
|
|
|
echo(f"Turning led to {state}")
|
|
|
|
return await led.set_led(state)
|
|
|
|
else:
|
|
|
|
echo(f"LED state: {led.led}")
|
|
|
|
return led.led
|
|
|
|
|
|
|
|
|
|
|
|
@device.command()
|
|
|
|
@click.argument("new_alias", required=False, default=None)
|
|
|
|
@pass_dev_or_child
|
|
|
|
async def alias(dev, new_alias):
|
|
|
|
"""Get or set the device (or plug) alias."""
|
|
|
|
if new_alias is not None:
|
|
|
|
echo(f"Setting alias to {new_alias}")
|
|
|
|
res = await dev.set_alias(new_alias)
|
|
|
|
await dev.update()
|
|
|
|
echo(f"Alias set to: {dev.alias}")
|
|
|
|
return res
|
|
|
|
|
|
|
|
echo(f"Alias: {dev.alias}")
|
|
|
|
if dev.children:
|
|
|
|
for plug in dev.children:
|
|
|
|
echo(f" * {plug.alias}")
|
|
|
|
|
|
|
|
return dev.alias
|
|
|
|
|
|
|
|
|
|
|
|
@device.command()
|
|
|
|
@click.option("--delay", default=1)
|
|
|
|
@pass_dev
|
|
|
|
async def reboot(plug, delay):
|
|
|
|
"""Reboot the device."""
|
|
|
|
echo("Rebooting the device..")
|
|
|
|
return await plug.reboot(delay)
|
|
|
|
|
|
|
|
|
2024-09-21 14:52:52 +00:00
|
|
|
@device.command()
|
|
|
|
@pass_dev
|
|
|
|
async def factory_reset(plug):
|
|
|
|
"""Reset device to factory settings."""
|
|
|
|
click.confirm(
|
|
|
|
"Do you really want to reset the device to factory settings?", abort=True
|
|
|
|
)
|
|
|
|
|
|
|
|
return await plug.factory_reset()
|
|
|
|
|
|
|
|
|
2024-07-23 18:13:52 +00:00
|
|
|
@device.command()
|
|
|
|
@pass_dev
|
|
|
|
@click.option(
|
|
|
|
"--username", required=True, prompt=True, help="New username to set on the device"
|
|
|
|
)
|
|
|
|
@click.option(
|
|
|
|
"--password", required=True, prompt=True, help="New password to set on the device"
|
|
|
|
)
|
|
|
|
async def update_credentials(dev, username, password):
|
|
|
|
"""Update device credentials for authenticated devices."""
|
|
|
|
if not isinstance(dev, SmartDevice):
|
|
|
|
error("Credentials can only be updated on authenticated devices.")
|
|
|
|
|
|
|
|
click.confirm("Do you really want to replace the existing credentials?", abort=True)
|
|
|
|
|
|
|
|
return await dev.update_credentials(username, password)
|