2017-03-26 01:19:44 +00:00
|
|
|
import sys
|
2017-03-20 18:03:19 +00:00
|
|
|
import click
|
|
|
|
import logging
|
|
|
|
from click_datetime import Datetime
|
2017-08-05 13:49:56 +00:00
|
|
|
from pprint import pformat as pf
|
2017-03-20 18:03:19 +00:00
|
|
|
|
2017-04-14 12:24:58 +00:00
|
|
|
if sys.version_info < (3, 4):
|
|
|
|
print("To use this script you need python 3.4 or newer! got %s" %
|
|
|
|
sys.version_info)
|
|
|
|
sys.exit(1)
|
|
|
|
|
2017-08-05 15:21:54 +00:00
|
|
|
from pyHS100 import (SmartDevice,
|
|
|
|
SmartPlug,
|
|
|
|
SmartBulb,
|
|
|
|
Discover) # noqa: E402
|
2017-03-20 18:03:19 +00:00
|
|
|
|
2017-04-24 17:28:22 +00:00
|
|
|
pass_dev = click.make_pass_decorator(SmartDevice)
|
2017-03-20 18:03:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
@click.group(invoke_without_command=True)
|
|
|
|
@click.option('--ip', envvar="PYHS100_IP", required=False)
|
|
|
|
@click.option('--debug/--normal', default=False)
|
2017-04-24 17:28:22 +00:00
|
|
|
@click.option('--bulb', default=False, is_flag=True)
|
2017-08-05 13:49:56 +00:00
|
|
|
@click.option('--plug', default=False, is_flag=True)
|
2017-03-20 18:03:19 +00:00
|
|
|
@click.pass_context
|
2017-08-05 13:49:56 +00:00
|
|
|
def cli(ctx, ip, debug, bulb, plug):
|
2017-03-20 18:03:19 +00:00
|
|
|
"""A cli tool for controlling TP-Link smart home plugs."""
|
|
|
|
if debug:
|
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
else:
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
|
|
|
|
if ctx.invoked_subcommand == "discover":
|
|
|
|
return
|
|
|
|
|
2017-03-26 01:19:44 +00:00
|
|
|
if ip is None:
|
2017-08-05 13:49:56 +00:00
|
|
|
click.echo("No IP given, trying discovery..")
|
|
|
|
ctx.invoke(discover)
|
|
|
|
return
|
2017-03-26 01:19:44 +00:00
|
|
|
|
2017-08-05 13:49:56 +00:00
|
|
|
elif ip is not None:
|
|
|
|
if not bulb and not plug:
|
|
|
|
click.echo("No --bulb nor --plug given, discovering..")
|
|
|
|
devs = ctx.invoke(discover, discover_only=True)
|
|
|
|
for discovered_ip, discovered_dev in devs:
|
|
|
|
if discovered_ip == ip:
|
|
|
|
dev = discovered_dev
|
|
|
|
break
|
|
|
|
elif bulb:
|
|
|
|
dev = SmartBulb(ip)
|
|
|
|
elif plug:
|
|
|
|
dev = SmartPlug(ip)
|
|
|
|
else:
|
|
|
|
click.echo("Unable to detect type, use --bulb or --plug!")
|
|
|
|
return
|
|
|
|
ctx.obj = dev
|
2017-03-20 18:03:19 +00:00
|
|
|
|
|
|
|
if ctx.invoked_subcommand is None:
|
|
|
|
ctx.invoke(state)
|
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
2017-08-05 13:49:56 +00:00
|
|
|
@click.option('--timeout', default=3, required=False)
|
|
|
|
@click.option('--discover-only', default=False)
|
|
|
|
@click.pass_context
|
|
|
|
def discover(ctx, timeout, discover_only):
|
2017-03-20 18:03:19 +00:00
|
|
|
"""Discover devices in the network."""
|
|
|
|
click.echo("Discovering devices for %s seconds" % timeout)
|
2017-08-05 13:49:56 +00:00
|
|
|
found_devs = Discover.discover(timeout=timeout).items()
|
|
|
|
if not discover_only:
|
|
|
|
for ip, dev in found_devs:
|
|
|
|
ctx.obj = dev
|
|
|
|
ctx.invoke(state)
|
|
|
|
print()
|
|
|
|
|
|
|
|
return found_devs
|
2017-03-20 18:03:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
|
|
|
@pass_dev
|
2017-04-24 17:28:22 +00:00
|
|
|
def sysinfo(dev):
|
2017-03-20 18:03:19 +00:00
|
|
|
"""Print out full system information."""
|
|
|
|
click.echo(click.style("== System info ==", bold=True))
|
2017-08-05 13:49:56 +00:00
|
|
|
click.echo(pf(dev.sys_info))
|
2017-03-20 18:03:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
|
|
|
@pass_dev
|
|
|
|
@click.pass_context
|
2017-04-24 17:28:22 +00:00
|
|
|
def state(ctx, dev):
|
2017-03-20 18:03:19 +00:00
|
|
|
"""Print out device state and versions."""
|
2017-04-24 17:28:22 +00:00
|
|
|
click.echo(click.style("== %s - %s ==" % (dev.alias, dev.model),
|
2017-03-20 18:03:19 +00:00
|
|
|
bold=True))
|
|
|
|
|
2017-04-24 17:28:22 +00:00
|
|
|
click.echo(click.style("Device state: %s" % "ON" if dev.is_on else "OFF",
|
|
|
|
fg="green" if dev.is_on else "red"))
|
2017-08-05 13:49:56 +00:00
|
|
|
click.echo("IP address: %s" % dev.ip_address)
|
2017-04-24 17:28:22 +00:00
|
|
|
for k, v in dev.state_information.items():
|
|
|
|
click.echo("%s: %s" % (k, v))
|
|
|
|
click.echo(click.style("== Generic information ==", bold=True))
|
|
|
|
click.echo("Time: %s" % dev.time)
|
|
|
|
click.echo("Hardware: %s" % dev.hw_info["hw_ver"])
|
|
|
|
click.echo("Software: %s" % dev.hw_info["sw_ver"])
|
|
|
|
click.echo("MAC (rssi): %s (%s)" % (dev.mac, dev.rssi))
|
|
|
|
click.echo("Location: %s" % dev.location)
|
|
|
|
|
2017-03-20 18:03:19 +00:00
|
|
|
ctx.invoke(emeter)
|
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
|
|
|
@pass_dev
|
|
|
|
@click.option('--year', type=Datetime(format='%Y'),
|
|
|
|
default=None, required=False)
|
|
|
|
@click.option('--month', type=Datetime(format='%Y-%m'),
|
|
|
|
default=None, required=False)
|
|
|
|
@click.option('--erase', is_flag=True)
|
2017-04-24 17:28:22 +00:00
|
|
|
def emeter(dev, year, month, erase):
|
2017-03-20 18:03:19 +00:00
|
|
|
"""Query emeter for historical consumption."""
|
|
|
|
click.echo(click.style("== Emeter ==", bold=True))
|
2017-04-24 17:28:22 +00:00
|
|
|
if not dev.has_emeter:
|
2017-03-20 18:03:19 +00:00
|
|
|
click.echo("Device has no emeter")
|
|
|
|
return
|
|
|
|
|
|
|
|
if erase:
|
|
|
|
click.echo("Erasing emeter statistics..")
|
2017-04-24 17:28:22 +00:00
|
|
|
dev.erase_emeter_stats()
|
2017-03-20 18:03:19 +00:00
|
|
|
return
|
|
|
|
|
2017-04-24 17:28:22 +00:00
|
|
|
click.echo("Current state: %s" % dev.get_emeter_realtime())
|
2017-03-20 18:03:19 +00:00
|
|
|
if year:
|
|
|
|
click.echo("== For year %s ==" % year.year)
|
2017-04-24 17:28:22 +00:00
|
|
|
click.echo(dev.get_emeter_monthly(year.year))
|
2017-03-20 18:03:19 +00:00
|
|
|
elif month:
|
|
|
|
click.echo("== For month %s of %s ==" % (month.month, month.year))
|
2017-04-24 17:28:22 +00:00
|
|
|
dev.get_emeter_daily(year=month.year, month=month.month)
|
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
2017-09-15 12:23:06 +00:00
|
|
|
@click.argument("brightness", type=click.IntRange(0, 100), default=None,
|
|
|
|
required=False)
|
2017-04-24 17:28:22 +00:00
|
|
|
@pass_dev
|
2017-09-15 12:23:06 +00:00
|
|
|
def brightness(dev, brightness):
|
2017-04-24 17:28:22 +00:00
|
|
|
"""Get or set brightness. (Bulb Only)"""
|
2017-09-15 12:23:06 +00:00
|
|
|
if brightness is None:
|
2017-04-24 17:28:22 +00:00
|
|
|
click.echo("Brightness: %s" % dev.brightness)
|
|
|
|
else:
|
2017-09-15 12:23:06 +00:00
|
|
|
click.echo("Setting brightness to %s" % brightness)
|
|
|
|
dev.brightness = brightness
|
2017-04-24 17:28:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
2017-09-15 12:23:06 +00:00
|
|
|
@click.argument("temperature", type=click.IntRange(2700, 6500), default=None,
|
|
|
|
required=False)
|
2017-04-24 17:28:22 +00:00
|
|
|
@pass_dev
|
2017-09-15 12:23:06 +00:00
|
|
|
def temperature(dev, temperature):
|
2017-04-24 17:28:22 +00:00
|
|
|
"""Get or set color temperature. (Bulb only)"""
|
2017-09-15 12:23:06 +00:00
|
|
|
if temperature is None:
|
2017-04-24 17:28:22 +00:00
|
|
|
click.echo("Color temperature: %s" % dev.color_temp)
|
|
|
|
else:
|
2017-09-15 12:23:06 +00:00
|
|
|
click.echo("Setting color temperature to %s" % temperature)
|
|
|
|
dev.color_temp = temperature
|
2017-04-24 17:28:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
2017-08-03 20:13:56 +00:00
|
|
|
@click.argument("h", type=click.IntRange(0, 360), default=None)
|
|
|
|
@click.argument("s", type=click.IntRange(0, 100), default=None)
|
|
|
|
@click.argument("v", type=click.IntRange(0, 100), default=None)
|
|
|
|
@pass_dev
|
2017-04-24 17:28:22 +00:00
|
|
|
def hsv(dev, h, s, v):
|
|
|
|
"""Get or set color in HSV. (Bulb only)"""
|
|
|
|
if h is None or s is None or v is None:
|
|
|
|
click.echo("Current HSV: %s" % dev.hsv)
|
|
|
|
else:
|
|
|
|
click.echo("Setting HSV: %s %s %s" % (h, s, v))
|
|
|
|
dev.hsv = (h, s, v)
|
2017-03-20 18:03:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
|
|
|
@click.argument('state', type=bool, required=False)
|
|
|
|
@pass_dev
|
2017-04-24 17:28:22 +00:00
|
|
|
def led(dev, state):
|
|
|
|
"""Get or set led state. (Plug only)"""
|
2017-03-20 18:03:19 +00:00
|
|
|
if state is not None:
|
|
|
|
click.echo("Turning led to %s" % state)
|
2017-04-24 17:28:22 +00:00
|
|
|
dev.led = state
|
2017-03-20 18:03:19 +00:00
|
|
|
else:
|
2017-04-24 17:28:22 +00:00
|
|
|
click.echo("LED state: %s" % dev.led)
|
2017-03-20 18:03:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
|
|
|
@pass_dev
|
|
|
|
def on(plug):
|
|
|
|
"""Turn the device on."""
|
|
|
|
click.echo("Turning on..")
|
|
|
|
plug.turn_on()
|
|
|
|
|
|
|
|
|
|
|
|
@cli.command()
|
|
|
|
@pass_dev
|
|
|
|
def off(plug):
|
|
|
|
"""Turn the device off."""
|
|
|
|
click.echo("Turning off..")
|
|
|
|
plug.turn_off()
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
cli()
|