Add support for DNS host names (#104)

This commit is contained in:
K Henriksson 2018-01-17 13:03:19 -08:00 committed by Teemu R
parent a426488449
commit d40fff4f9e
7 changed files with 37 additions and 31 deletions

View File

@ -23,7 +23,7 @@ Python Library to control TPLink smart plugs/switches and smart bulbs.
# Usage # Usage
The package is shipped with a console tool named pyhs100, please refer to ```pyhs100 --help``` for detailed usage. The package is shipped with a console tool named pyhs100, please refer to ```pyhs100 --help``` for detailed usage.
The device to which the commands are sent is chosen by `PYHS100_IP` environment variable or passing `--ip <address>` as an option. The device to which the commands are sent is chosen by `PYHS100_HOST` environment variable or passing `--host <address>` as an option.
To see what is being sent to and received from the device, specify option `--debug`. To see what is being sent to and received from the device, specify option `--debug`.
To avoid discovering the devices when executing commands its type can be passed by specifying either `--plug` or `--bulb`, To avoid discovering the devices when executing commands its type can be passed by specifying either `--plug` or `--bulb`,

View File

@ -18,12 +18,17 @@ pass_dev = click.make_pass_decorator(SmartDevice)
@click.group(invoke_without_command=True) @click.group(invoke_without_command=True)
@click.option('--ip', envvar="PYHS100_IP", required=False) @click.option('--ip', envvar="PYHS100_IP", required=False,
help='The IP address of the device to connect to. This option '
'is deprecated and will be removed in the future; use --host '
'instead.')
@click.option('--host', envvar="PYHS100_HOST", required=False,
help='The host name or IP address of the device to connect to.')
@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.option('--plug', default=False, is_flag=True)
@click.pass_context @click.pass_context
def cli(ctx, ip, debug, bulb, plug): def cli(ctx, ip, host, 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)
@ -33,19 +38,22 @@ def cli(ctx, ip, debug, bulb, plug):
if ctx.invoked_subcommand == "discover": if ctx.invoked_subcommand == "discover":
return return
if ip is None: if ip is not None and host is None:
click.echo("No IP given, trying discovery..") host = ip
if host is None:
click.echo("No host name given, trying discovery..")
ctx.invoke(discover) ctx.invoke(discover)
return return
elif ip is not None: else:
if not bulb and not plug: if not bulb and not plug:
click.echo("No --bulb nor --plug given, discovering..") click.echo("No --bulb nor --plug given, discovering..")
dev = Discover.discover_single(ip) dev = Discover.discover_single(host)
elif bulb: elif bulb:
dev = SmartBulb(ip) dev = SmartBulb(host)
elif plug: elif plug:
dev = SmartPlug(ip) dev = SmartPlug(host)
else: else:
click.echo("Unable to detect type, use --bulb or --plug!") click.echo("Unable to detect type, use --bulb or --plug!")
return return
@ -90,7 +98,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) click.echo("Host/IP: %s" % dev.host)
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))

View File

@ -61,14 +61,14 @@ class Discover:
return devices return devices
@staticmethod @staticmethod
def discover_single(ip_address: str, def discover_single(host: str,
protocol: TPLinkSmartHomeProtocol = None protocol: TPLinkSmartHomeProtocol = None
) -> SmartDevice: ) -> SmartDevice:
""" """
Similar to discover(), except only return device object for single Similar to discover(), except only return device object for a single
passed device given by IP address. host.
:param ip_address: IP address of device to query :param host: Hostname of device to query
:param protocol: Protocol implementation to use :param protocol: Protocol implementation to use
:rtype: SmartDevice :rtype: SmartDevice
:return: Object for querying/controlling found device. :return: Object for querying/controlling found device.
@ -76,11 +76,11 @@ class Discover:
if protocol is None: if protocol is None:
protocol = TPLinkSmartHomeProtocol() protocol = TPLinkSmartHomeProtocol()
info = protocol.query(ip_address, Discover.DISCOVERY_QUERY) info = protocol.query(host, Discover.DISCOVERY_QUERY)
device_class = Discover._get_device_class(info) device_class = Discover._get_device_class(info)
if device_class is not None: if device_class is not None:
return device_class(ip_address) return device_class(host)
else: else:
return None return None

View File

@ -32,7 +32,7 @@ class TPLinkSmartHomeProtocol:
Request information from a TP-Link SmartHome Device and return the Request information from a TP-Link SmartHome Device and return the
response. response.
:param str host: ip address of the device :param str host: host name or ip address of the device
:param int port: port on the device (default: 9999) :param int port: port on the device (default: 9999)
:param request: command to send to the device (can be either dict or :param request: command to send to the device (can be either dict or
json string) json string)
@ -41,10 +41,9 @@ class TPLinkSmartHomeProtocol:
if isinstance(request, dict): if isinstance(request, dict):
request = json.dumps(request) request = json.dumps(request)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) timeout = TPLinkSmartHomeProtocol.DEFAULT_TIMEOUT
sock.settimeout(TPLinkSmartHomeProtocol.DEFAULT_TIMEOUT)
try: try:
sock.connect((host, port)) sock = socket.create_connection((host, port), timeout)
_LOGGER.debug("> (%i) %s", len(request), request) _LOGGER.debug("> (%i) %s", len(request), request)
sock.send(TPLinkSmartHomeProtocol.encrypt(request)) sock.send(TPLinkSmartHomeProtocol.encrypt(request))

View File

@ -42,9 +42,9 @@ class SmartBulb(SmartDevice):
BULB_STATE_OFF = 'OFF' BULB_STATE_OFF = 'OFF'
def __init__(self, def __init__(self,
ip_address: str, host: str,
protocol: 'TPLinkSmartHomeProtocol' = None) -> None: protocol: 'TPLinkSmartHomeProtocol' = None) -> None:
SmartDevice.__init__(self, ip_address, protocol) SmartDevice.__init__(self, host, protocol)
self.emeter_type = "smartlife.iot.common.emeter" self.emeter_type = "smartlife.iot.common.emeter"
self.emeter_units = True self.emeter_units = True

View File

@ -40,15 +40,14 @@ class SmartDevice(object):
ALL_FEATURES = (FEATURE_ENERGY_METER, FEATURE_TIMER) ALL_FEATURES = (FEATURE_ENERGY_METER, FEATURE_TIMER)
def __init__(self, def __init__(self,
ip_address: str, host: str,
protocol: Optional[TPLinkSmartHomeProtocol] = None) -> None: protocol: Optional[TPLinkSmartHomeProtocol] = None) -> None:
""" """
Create a new SmartDevice instance, identified through its IP address. Create a new SmartDevice instance.
:param str ip_address: ip address on which the device listens :param str host: host name or ip address on which the device listens
""" """
socket.inet_pton(socket.AF_INET, ip_address) self.host = host
self.ip_address = ip_address
if not protocol: if not protocol:
protocol = TPLinkSmartHomeProtocol() protocol = TPLinkSmartHomeProtocol()
self.protocol = protocol self.protocol = protocol
@ -73,7 +72,7 @@ class SmartDevice(object):
arg = {} arg = {}
try: try:
response = self.protocol.query( response = self.protocol.query(
host=self.ip_address, host=self.host,
request={target: {cmd: arg}} request={target: {cmd: arg}}
) )
except Exception as ex: except Exception as ex:
@ -523,7 +522,7 @@ class SmartDevice(object):
def __repr__(self): def __repr__(self):
return "<%s at %s (%s), is_on: %s - dev specific: %s>" % ( return "<%s at %s (%s), is_on: %s - dev specific: %s>" % (
self.__class__.__name__, self.__class__.__name__,
self.ip_address, self.host,
self.alias, self.alias,
self.is_on, self.is_on,
self.state_information) self.state_information)

View File

@ -32,9 +32,9 @@ class SmartPlug(SmartDevice):
SWITCH_STATE_UNKNOWN = 'UNKNOWN' SWITCH_STATE_UNKNOWN = 'UNKNOWN'
def __init__(self, def __init__(self,
ip_address: str, host: str,
protocol: 'TPLinkSmartHomeProtocol' = None) -> None: protocol: 'TPLinkSmartHomeProtocol' = None) -> None:
SmartDevice.__init__(self, ip_address, protocol) SmartDevice.__init__(self, host, protocol)
self.emeter_type = "emeter" self.emeter_type = "emeter"
self.emeter_units = False self.emeter_units = False