mirror of
https://github.com/python-kasa/python-kasa.git
synced 2024-12-23 03:33:35 +00:00
Discover refactoring, enhancements to the cli tool (#71)
* Discover refactoring, enhancements to the cli tool * Discover tries to detect the type of the device from sysinfo response * Discover.discover() returns an IP address keyed dictionary, values are initialized instances of the automatically detected device type. * When no IP is given, autodetect all supported devices and print out their states * When only IP but no type is given, autodetect type and make a call based on that information. * One can define --bulb or --plug to skip the detection. * renamed pyHS100.py -> smartdevice.py * SmartPlugException -> SmartDeviceException in comments * fix mic_type check * make time() return None on failure as we don't know which devices support getting the time and it's used in the cli tool * hw_info: check if key exists before accessing it, add mic_mac and mic_type * Check for mic_mac on mac, based on work by kdschloesser on issue #59 * make hound happy, __init__ on SmartDevice cannot error out so removing 'raises' documentation
This commit is contained in:
parent
d7aade4e61
commit
07af48e41a
@ -13,8 +13,9 @@ Module-specific errors are raised as `SmartDeviceException` and are expected
|
|||||||
to be handled by the user of the library.
|
to be handled by the user of the library.
|
||||||
"""
|
"""
|
||||||
# flake8: noqa
|
# flake8: noqa
|
||||||
|
from .smartdevice import SmartDevice
|
||||||
from .smartplug import SmartPlug
|
from .smartplug import SmartPlug
|
||||||
from .pyHS100 import SmartDevice
|
|
||||||
from .types import SmartDeviceException
|
from .types import SmartDeviceException
|
||||||
from .smartbulb import SmartBulb
|
from .smartbulb import SmartBulb
|
||||||
from .protocol import TPLinkSmartHomeProtocol
|
from .protocol import TPLinkSmartHomeProtocol
|
||||||
|
from .discover import Discover
|
||||||
|
@ -2,7 +2,7 @@ import sys
|
|||||||
import click
|
import click
|
||||||
import logging
|
import logging
|
||||||
from click_datetime import Datetime
|
from click_datetime import Datetime
|
||||||
from pprint import pformat
|
from pprint import pformat as pf
|
||||||
|
|
||||||
if sys.version_info < (3, 4):
|
if sys.version_info < (3, 4):
|
||||||
print("To use this script you need python 3.4 or newer! got %s" %
|
print("To use this script you need python 3.4 or newer! got %s" %
|
||||||
@ -10,7 +10,7 @@ if sys.version_info < (3, 4):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
from pyHS100 import (SmartDevice, SmartPlug, SmartBulb,
|
from pyHS100 import (SmartDevice, SmartPlug, SmartBulb,
|
||||||
TPLinkSmartHomeProtocol) # noqa: E402
|
TPLinkSmartHomeProtocol, Discover) # noqa: E402
|
||||||
|
|
||||||
pass_dev = click.make_pass_decorator(SmartDevice)
|
pass_dev = click.make_pass_decorator(SmartDevice)
|
||||||
|
|
||||||
@ -19,8 +19,9 @@ pass_dev = click.make_pass_decorator(SmartDevice)
|
|||||||
@click.option('--ip', envvar="PYHS100_IP", required=False)
|
@click.option('--ip', envvar="PYHS100_IP", required=False)
|
||||||
@click.option('--debug/--normal', default=False)
|
@click.option('--debug/--normal', default=False)
|
||||||
@click.option('--bulb', default=False, is_flag=True)
|
@click.option('--bulb', default=False, is_flag=True)
|
||||||
|
@click.option('--plug', default=False, is_flag=True)
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def cli(ctx, ip, debug, bulb):
|
def cli(ctx, ip, debug, bulb, plug):
|
||||||
"""A cli tool for controlling TP-Link smart home plugs."""
|
"""A cli tool for controlling TP-Link smart home plugs."""
|
||||||
if debug:
|
if debug:
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
@ -31,13 +32,25 @@ def cli(ctx, ip, debug, bulb):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if ip is None:
|
if ip is None:
|
||||||
click.echo("You must specify the IP!")
|
click.echo("No IP given, trying discovery..")
|
||||||
sys.exit(-1)
|
ctx.invoke(discover)
|
||||||
|
return
|
||||||
|
|
||||||
if bulb:
|
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)
|
dev = SmartBulb(ip)
|
||||||
else:
|
elif plug:
|
||||||
dev = SmartPlug(ip)
|
dev = SmartPlug(ip)
|
||||||
|
else:
|
||||||
|
click.echo("Unable to detect type, use --bulb or --plug!")
|
||||||
|
return
|
||||||
ctx.obj = dev
|
ctx.obj = dev
|
||||||
|
|
||||||
if ctx.invoked_subcommand is None:
|
if ctx.invoked_subcommand is None:
|
||||||
@ -45,12 +58,20 @@ def cli(ctx, ip, debug, bulb):
|
|||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option('--timeout', default=5, required=False)
|
@click.option('--timeout', default=3, required=False)
|
||||||
def discover(timeout):
|
@click.option('--discover-only', default=False)
|
||||||
|
@click.pass_context
|
||||||
|
def discover(ctx, timeout, discover_only):
|
||||||
"""Discover devices in the network."""
|
"""Discover devices in the network."""
|
||||||
click.echo("Discovering devices for %s seconds" % timeout)
|
click.echo("Discovering devices for %s seconds" % timeout)
|
||||||
for dev in TPLinkSmartHomeProtocol.discover(timeout=timeout):
|
found_devs = Discover.discover(timeout=timeout).items()
|
||||||
print("Found device: %s" % pformat(dev))
|
if not discover_only:
|
||||||
|
for ip, dev in found_devs:
|
||||||
|
ctx.obj = dev
|
||||||
|
ctx.invoke(state)
|
||||||
|
print()
|
||||||
|
|
||||||
|
return found_devs
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@ -58,7 +79,7 @@ def discover(timeout):
|
|||||||
def sysinfo(dev):
|
def sysinfo(dev):
|
||||||
"""Print out full system information."""
|
"""Print out full system information."""
|
||||||
click.echo(click.style("== System info ==", bold=True))
|
click.echo(click.style("== System info ==", bold=True))
|
||||||
click.echo(pformat(dev.sys_info))
|
click.echo(pf(dev.sys_info))
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@ -71,6 +92,7 @@ def state(ctx, dev):
|
|||||||
|
|
||||||
click.echo(click.style("Device state: %s" % "ON" if dev.is_on else "OFF",
|
click.echo(click.style("Device state: %s" % "ON" if dev.is_on else "OFF",
|
||||||
fg="green" if dev.is_on else "red"))
|
fg="green" if dev.is_on else "red"))
|
||||||
|
click.echo("IP address: %s" % dev.ip_address)
|
||||||
for k, v in dev.state_information.items():
|
for k, v in dev.state_information.items():
|
||||||
click.echo("%s: %s" % (k, v))
|
click.echo("%s: %s" % (k, v))
|
||||||
click.echo(click.style("== Generic information ==", bold=True))
|
click.echo(click.style("== Generic information ==", bold=True))
|
||||||
|
68
pyHS100/discover.py
Normal file
68
pyHS100/discover.py
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import socket
|
||||||
|
import logging
|
||||||
|
import json
|
||||||
|
|
||||||
|
from pyHS100 import TPLinkSmartHomeProtocol, SmartPlug, SmartBulb
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Discover:
|
||||||
|
@staticmethod
|
||||||
|
def discover(protocol=None, port=9999, timeout=3):
|
||||||
|
"""
|
||||||
|
Sends discovery message to 255.255.255.255:9999 in order
|
||||||
|
to detect available supported devices in the local network,
|
||||||
|
and waits for given timeout for answers from devices.
|
||||||
|
|
||||||
|
:param timeout: How long to wait for responses, defaults to 5
|
||||||
|
:param port: port to send broadcast messages, defaults to 9999.
|
||||||
|
:rtype: dict
|
||||||
|
:return: Array of json objects {"ip", "port", "sys_info"}
|
||||||
|
"""
|
||||||
|
if protocol is None:
|
||||||
|
protocol = TPLinkSmartHomeProtocol()
|
||||||
|
|
||||||
|
discovery_query = {"system": {"get_sysinfo": None},
|
||||||
|
"emeter": {"get_realtime": None}}
|
||||||
|
target = "255.255.255.255"
|
||||||
|
|
||||||
|
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||||
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
sock.settimeout(timeout)
|
||||||
|
|
||||||
|
req = json.dumps(discovery_query)
|
||||||
|
_LOGGER.debug("Sending discovery to %s:%s", target, port)
|
||||||
|
|
||||||
|
encrypted_req = protocol.encrypt(req)
|
||||||
|
sock.sendto(encrypted_req[4:], (target, port))
|
||||||
|
|
||||||
|
devices = {}
|
||||||
|
_LOGGER.debug("Waiting %s seconds for responses...", timeout)
|
||||||
|
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
data, addr = sock.recvfrom(4096)
|
||||||
|
ip, port = addr
|
||||||
|
info = json.loads(protocol.decrypt(data))
|
||||||
|
if "system" in info and "get_sysinfo" in info["system"]:
|
||||||
|
sysinfo = info["system"]["get_sysinfo"]
|
||||||
|
if "type" in sysinfo:
|
||||||
|
type = sysinfo["type"]
|
||||||
|
elif "mic_type" in sysinfo:
|
||||||
|
type = sysinfo["mic_type"]
|
||||||
|
else:
|
||||||
|
_LOGGER.error("Unable to find the device type field!")
|
||||||
|
type = "UNKNOWN"
|
||||||
|
else:
|
||||||
|
_LOGGER.error("No 'system' nor 'get_sysinfo' in response")
|
||||||
|
if "smartplug" in type.lower():
|
||||||
|
devices[ip] = SmartPlug(ip)
|
||||||
|
elif "smartbulb" in type.lower():
|
||||||
|
devices[ip] = SmartBulb(ip)
|
||||||
|
except socket.timeout:
|
||||||
|
_LOGGER.debug("Got socket timeout, which is okay.")
|
||||||
|
except Exception as ex:
|
||||||
|
_LOGGER.error("Got exception %s", ex, exc_info=True)
|
||||||
|
return devices
|
@ -39,7 +39,7 @@ class TPLinkSmartHomeProtocol:
|
|||||||
request = json.dumps(request)
|
request = json.dumps(request)
|
||||||
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
sock.settimeout(5.0)
|
sock.settimeout(TPLinkSmartHomeProtocol.DEFAULT_TIMEOUT)
|
||||||
try:
|
try:
|
||||||
sock.connect((host, port))
|
sock.connect((host, port))
|
||||||
|
|
||||||
@ -74,50 +74,6 @@ class TPLinkSmartHomeProtocol:
|
|||||||
|
|
||||||
return json.loads(response)
|
return json.loads(response)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def discover(timeout=DEFAULT_TIMEOUT, port=DEFAULT_PORT):
|
|
||||||
"""
|
|
||||||
Sends discovery message to 255.255.255.255:9999 in order
|
|
||||||
to detect available supported devices in the local network,
|
|
||||||
and waits for given timeout for answers from devices.
|
|
||||||
|
|
||||||
:param timeout: How long to wait for responses, defaults to 5
|
|
||||||
:param port: port to send broadcast messages, defaults to 9999.
|
|
||||||
:rtype: list[dict]
|
|
||||||
:return: Array of json objects {"ip", "port", "sys_info"}
|
|
||||||
"""
|
|
||||||
discovery_query = {"system": {"get_sysinfo": None},
|
|
||||||
"emeter": {"get_realtime": None}}
|
|
||||||
target = "255.255.255.255"
|
|
||||||
|
|
||||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
||||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
|
||||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
||||||
sock.settimeout(timeout)
|
|
||||||
|
|
||||||
req = json.dumps(discovery_query)
|
|
||||||
_LOGGER.debug("Sending discovery to %s:%s", target, port)
|
|
||||||
|
|
||||||
encrypted_req = TPLinkSmartHomeProtocol.encrypt(req)
|
|
||||||
sock.sendto(encrypted_req[4:], (target, port))
|
|
||||||
|
|
||||||
devices = []
|
|
||||||
_LOGGER.debug("Waiting %s seconds for responses...", timeout)
|
|
||||||
|
|
||||||
try:
|
|
||||||
while True:
|
|
||||||
data, addr = sock.recvfrom(4096)
|
|
||||||
ip, port = addr
|
|
||||||
info = json.loads(TPLinkSmartHomeProtocol.decrypt(data))
|
|
||||||
|
|
||||||
devices.append({"ip": ip, "port": port, "sys_info": info})
|
|
||||||
except socket.timeout:
|
|
||||||
_LOGGER.debug("Got socket timeout, which is okay.")
|
|
||||||
except Exception as ex:
|
|
||||||
_LOGGER.error("Got exception %s", ex, exc_info=True)
|
|
||||||
|
|
||||||
return devices
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def encrypt(request):
|
def encrypt(request):
|
||||||
"""
|
"""
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from .pyHS100 import SmartDevice
|
from pyHS100 import SmartDevice
|
||||||
|
|
||||||
|
|
||||||
class SmartBulb(SmartDevice):
|
class SmartBulb(SmartDevice):
|
||||||
@ -32,7 +32,7 @@ class SmartBulb(SmartDevice):
|
|||||||
# check the current brightness
|
# check the current brightness
|
||||||
print(p.brightness)
|
print(p.brightness)
|
||||||
|
|
||||||
Errors reported by the device are raised as SmartPlugExceptions,
|
Errors reported by the device are raised as SmartDeviceExceptions,
|
||||||
and should be handled by the user of the library.
|
and should be handled by the user of the library.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -35,7 +35,6 @@ class SmartDevice(object):
|
|||||||
Create a new SmartDevice instance, identified through its IP address.
|
Create a new SmartDevice instance, identified through its IP address.
|
||||||
|
|
||||||
:param str ip_address: ip address on which the device listens
|
:param str ip_address: ip address on which the device listens
|
||||||
:raises SmartPlugException: when unable to communicate with the device
|
|
||||||
"""
|
"""
|
||||||
socket.inet_pton(socket.AF_INET, ip_address)
|
socket.inet_pton(socket.AF_INET, ip_address)
|
||||||
self.ip_address = ip_address
|
self.ip_address = ip_address
|
||||||
@ -52,7 +51,7 @@ class SmartDevice(object):
|
|||||||
:param arg: JSON object passed as parameter to the command
|
:param arg: JSON object passed as parameter to the command
|
||||||
:return: Unwrapped result for the call.
|
:return: Unwrapped result for the call.
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
:raises SmartPlugException: if command was not executed correctly
|
:raises SmartDeviceException: if command was not executed correctly
|
||||||
"""
|
"""
|
||||||
if arg is None:
|
if arg is None:
|
||||||
arg = {}
|
arg = {}
|
||||||
@ -121,7 +120,7 @@ class SmartDevice(object):
|
|||||||
|
|
||||||
:return: sysinfo
|
:return: sysinfo
|
||||||
:rtype dict
|
:rtype dict
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
return self._query_helper("system", "get_sysinfo")
|
return self._query_helper("system", "get_sysinfo")
|
||||||
|
|
||||||
@ -146,7 +145,7 @@ class SmartDevice(object):
|
|||||||
|
|
||||||
:return: device model
|
:return: device model
|
||||||
:rtype: str
|
:rtype: str
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
return self.sys_info['model']
|
return self.sys_info['model']
|
||||||
|
|
||||||
@ -166,7 +165,7 @@ class SmartDevice(object):
|
|||||||
Sets the device name aka alias.
|
Sets the device name aka alias.
|
||||||
|
|
||||||
:param alias: New alias (name)
|
:param alias: New alias (name)
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
self._query_helper("system", "set_dev_alias", {"alias": alias})
|
self._query_helper("system", "set_dev_alias", {"alias": alias})
|
||||||
|
|
||||||
@ -179,7 +178,7 @@ class SmartDevice(object):
|
|||||||
|
|
||||||
:return: icon and its hash
|
:return: icon and its hash
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
return self._query_helper("system", "get_dev_icon")
|
return self._query_helper("system", "get_dev_icon")
|
||||||
|
|
||||||
@ -204,12 +203,15 @@ class SmartDevice(object):
|
|||||||
Returns current time from the device.
|
Returns current time from the device.
|
||||||
|
|
||||||
:return: datetime for device's time
|
:return: datetime for device's time
|
||||||
:rtype: datetime.datetime
|
:rtype: datetime.datetime or None when not available
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
|
try:
|
||||||
res = self._query_helper("time", "get_time")
|
res = self._query_helper("time", "get_time")
|
||||||
return datetime.datetime(res["year"], res["month"], res["mday"],
|
return datetime.datetime(res["year"], res["month"], res["mday"],
|
||||||
res["hour"], res["min"], res["sec"])
|
res["hour"], res["min"], res["sec"])
|
||||||
|
except SmartDeviceException:
|
||||||
|
return None
|
||||||
|
|
||||||
@time.setter
|
@time.setter
|
||||||
def time(self, ts):
|
def time(self, ts):
|
||||||
@ -221,7 +223,7 @@ class SmartDevice(object):
|
|||||||
:return: result
|
:return: result
|
||||||
:type: dict
|
:type: dict
|
||||||
:raises NotImplemented: when not implemented.
|
:raises NotImplemented: when not implemented.
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("Fails with err_code == 0 with HS110.")
|
raise NotImplementedError("Fails with err_code == 0 with HS110.")
|
||||||
"""
|
"""
|
||||||
@ -252,7 +254,7 @@ class SmartDevice(object):
|
|||||||
|
|
||||||
:return: Timezone information
|
:return: Timezone information
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
return self._query_helper("time", "get_timezone")
|
return self._query_helper("time", "get_timezone")
|
||||||
|
|
||||||
@ -264,9 +266,10 @@ class SmartDevice(object):
|
|||||||
:return: Information about hardware
|
:return: Information about hardware
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
"""
|
"""
|
||||||
keys = ["sw_ver", "hw_ver", "mac", "hwId", "fwId", "oemId", "dev_name"]
|
keys = ["sw_ver", "hw_ver", "mac", "mic_mac", "type",
|
||||||
|
"mic_type", "hwId", "fwId", "oemId", "dev_name"]
|
||||||
info = self.sys_info
|
info = self.sys_info
|
||||||
return {key: info[key] for key in keys}
|
return {key: info[key] for key in keys if key in info}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def location(self):
|
def location(self):
|
||||||
@ -309,7 +312,15 @@ class SmartDevice(object):
|
|||||||
:return: mac address in hexadecimal with colons, e.g. 01:23:45:67:89:ab
|
:return: mac address in hexadecimal with colons, e.g. 01:23:45:67:89:ab
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
return self.sys_info["mac"]
|
info = self.sys_info
|
||||||
|
|
||||||
|
if 'mac' in info:
|
||||||
|
return info["mac"]
|
||||||
|
elif 'mic_mac' in info:
|
||||||
|
return info['mic_mac']
|
||||||
|
else:
|
||||||
|
raise SmartDeviceException("Unknown mac, please submit a bug"
|
||||||
|
"with sysinfo output.")
|
||||||
|
|
||||||
@mac.setter
|
@mac.setter
|
||||||
def mac(self, mac):
|
def mac(self, mac):
|
||||||
@ -317,7 +328,7 @@ class SmartDevice(object):
|
|||||||
Sets new mac address
|
Sets new mac address
|
||||||
|
|
||||||
:param str mac: mac in hexadecimal with colons, e.g. 01:23:45:67:89:ab
|
:param str mac: mac in hexadecimal with colons, e.g. 01:23:45:67:89:ab
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
self._query_helper("system", "set_mac_addr", {"mac": mac})
|
self._query_helper("system", "set_mac_addr", {"mac": mac})
|
||||||
|
|
||||||
@ -328,7 +339,7 @@ class SmartDevice(object):
|
|||||||
:returns: current readings or False
|
:returns: current readings or False
|
||||||
:rtype: dict, False
|
:rtype: dict, False
|
||||||
False if device has no energy meter or error occured
|
False if device has no energy meter or error occured
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
if not self.has_emeter:
|
if not self.has_emeter:
|
||||||
return False
|
return False
|
||||||
@ -345,7 +356,7 @@ class SmartDevice(object):
|
|||||||
:return: mapping of day of month to value
|
:return: mapping of day of month to value
|
||||||
False if device has no energy meter or error occured
|
False if device has no energy meter or error occured
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
if not self.has_emeter:
|
if not self.has_emeter:
|
||||||
return False
|
return False
|
||||||
@ -374,7 +385,7 @@ class SmartDevice(object):
|
|||||||
:return: dict: mapping of month to value
|
:return: dict: mapping of month to value
|
||||||
False if device has no energy meter
|
False if device has no energy meter
|
||||||
:rtype: dict
|
:rtype: dict
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
if not self.has_emeter:
|
if not self.has_emeter:
|
||||||
return False
|
return False
|
||||||
@ -397,7 +408,7 @@ class SmartDevice(object):
|
|||||||
:return: True if statistics were deleted
|
:return: True if statistics were deleted
|
||||||
False if device has no energy meter.
|
False if device has no energy meter.
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
if not self.has_emeter:
|
if not self.has_emeter:
|
||||||
return False
|
return False
|
||||||
@ -414,7 +425,7 @@ class SmartDevice(object):
|
|||||||
|
|
||||||
:return: the current power consumption in Watt.
|
:return: the current power consumption in Watt.
|
||||||
False if device has no energy meter.
|
False if device has no energy meter.
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
if not self.has_emeter:
|
if not self.has_emeter:
|
||||||
return False
|
return False
|
||||||
@ -466,3 +477,7 @@ class SmartDevice(object):
|
|||||||
:rtype: dict
|
:rtype: dict
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError("Device subclass needs to implement this.")
|
raise NotImplementedError("Device subclass needs to implement this.")
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<%s at %s: %s>" % (self.__class__.__name__,
|
||||||
|
self.ip_address, self.identify())
|
@ -1,7 +1,7 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from .pyHS100 import SmartDevice
|
from pyHS100 import SmartDevice
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -19,7 +19,7 @@ class SmartPlug(SmartDevice):
|
|||||||
# query and print current state of plug
|
# query and print current state of plug
|
||||||
print(p.state)
|
print(p.state)
|
||||||
|
|
||||||
Errors reported by the device are raised as SmartPlugExceptions,
|
Errors reported by the device are raised as SmartDeviceExceptions,
|
||||||
and should be handled by the user of the library.
|
and should be handled by the user of the library.
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
@ -65,7 +65,7 @@ class SmartPlug(SmartDevice):
|
|||||||
SWITCH_STATE_ON
|
SWITCH_STATE_ON
|
||||||
SWITCH_STATE_OFF
|
SWITCH_STATE_OFF
|
||||||
:raises ValueError: on invalid state
|
:raises ValueError: on invalid state
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
@ -90,7 +90,7 @@ class SmartPlug(SmartDevice):
|
|||||||
"""
|
"""
|
||||||
Turn the switch on.
|
Turn the switch on.
|
||||||
|
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
self._query_helper("system", "set_relay_state", {"state": 1})
|
self._query_helper("system", "set_relay_state", {"state": 1})
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ class SmartPlug(SmartDevice):
|
|||||||
"""
|
"""
|
||||||
Turn the switch off.
|
Turn the switch off.
|
||||||
|
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
self._query_helper("system", "set_relay_state", {"state": 0})
|
self._query_helper("system", "set_relay_state", {"state": 0})
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ class SmartPlug(SmartDevice):
|
|||||||
Sets the state of the led (night mode)
|
Sets the state of the led (night mode)
|
||||||
|
|
||||||
:param bool state: True to set led on, False to set led off
|
:param bool state: True to set led on, False to set led off
|
||||||
:raises SmartPlugException: on error
|
:raises SmartDeviceException: on error
|
||||||
"""
|
"""
|
||||||
self._query_helper("system", "set_led_off", {"off": int(not state)})
|
self._query_helper("system", "set_led_off", {"off": int(not state)})
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import enum
|
|||||||
|
|
||||||
class SmartDeviceException(Exception):
|
class SmartDeviceException(Exception):
|
||||||
"""
|
"""
|
||||||
SmartPlugException gets raised for errors reported by the plug.
|
SmartDeviceException gets raised for errors reported by the plug.
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user