Refactor and drop py2 support (#49)

* move is_off property to SmartDevice, implement is_on for bulb and use it

* refactor by moving smartbulb and smartplug to their own classes

* drop python2 compatibility, make flake8 happy

* travis: remove 2.7, add 3.6
This commit is contained in:
Teemu R 2017-04-14 14:24:58 +02:00 committed by GitHub
parent c9662fcb7b
commit 86f14642c8
13 changed files with 497 additions and 497 deletions

View File

@ -1,9 +1,9 @@
sudo: false
language: python
python:
- "2.7"
- "3.4"
- "3.5"
- "3.6"
install: pip install tox-travis
script: tox

View File

@ -1,4 +1,5 @@
# flake8: noqa
from __future__ import absolute_import
from __future__ import unicode_literals
from pyHS100.pyHS100 import SmartBulb, SmartPlug, TPLinkSmartHomeProtocol, SmartPlugException
from .smartplug import SmartPlug
from .pyHS100 import SmartPlugException, SmartDevice
from .smartbulb import SmartBulb
from .protocol import TPLinkSmartHomeProtocol

View File

@ -4,7 +4,12 @@ import logging
from click_datetime import Datetime
from pprint import pformat
from pyHS100 import SmartPlug, TPLinkSmartHomeProtocol
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)
from pyHS100 import SmartPlug, TPLinkSmartHomeProtocol # noqa: E402
pass_dev = click.make_pass_decorator(SmartPlug)
@ -24,7 +29,7 @@ def cli(ctx, ip, debug):
return
if ip is None:
click.echo("You must specify the IP either by --ip or setting PYHS100_IP environment variable!")
click.echo("You must specify the IP!")
sys.exit(-1)
plug = SmartPlug(ip)

View File

@ -1,6 +1,3 @@
from __future__ import absolute_import
from __future__ import unicode_literals
import json
import socket
import struct

View File

@ -13,21 +13,11 @@ Stroetmann which is licensed under the Apache License, Version 2.0.
You may obtain a copy of the license at
http://www.apache.org/licenses/LICENSE-2.0
"""
# python2 compatibility
from __future__ import absolute_import
from __future__ import unicode_literals
from future.utils import raise_from
try:
basestring
except NameError:
basestring = str
import datetime
import logging
import socket
from pyHS100.protocol import TPLinkSmartHomeProtocol
from .protocol import TPLinkSmartHomeProtocol
_LOGGER = logging.getLogger(__name__)
@ -53,32 +43,35 @@ class SmartDevice(object):
protocol = TPLinkSmartHomeProtocol()
self.protocol = protocol
def _query_helper(self, target, cmd, arg={}):
def _query_helper(self, target, cmd, arg=None):
"""
Helper returning unwrapped result object and doing error handling.
:param target: Target system {system, time, emeter, ..}
:param cmd: Command to execute
:param arg: JSON object passed as parameter to the command, defaults to {}
:param arg: JSON object passed as parameter to the command
:return: Unwrapped result for the call.
:rtype: dict
:raises SmartPlugException: if command was not executed correctly
"""
if arg is None:
arg = {}
try:
response = self.protocol.query(
host=self.ip_address,
request={target: {cmd: arg}}
)
except Exception as ex:
raise_from(SmartPlugException(), ex)
raise SmartPlugException('Communication error') from ex
if target not in response:
raise SmartPlugException("No required {} in response: {}".format(target, response))
raise SmartPlugException("No required {} in response: {}"
.format(target, response))
result = response[target]
if "err_code" in result and result["err_code"] != 0:
raise SmartPlugException("Error on {}.{}: {}".format(target, cmd, result))
raise SmartPlugException("Error on {}.{}: {}"
.format(target, cmd, result))
result = result[cmd]
del result["err_code"]
@ -90,6 +83,27 @@ class SmartDevice(object):
# TODO use volyptuous
return self.get_sysinfo()
@property
def is_off(self):
"""
Returns whether device is off.
:return: True if device is off, False otherwise.
:rtype: bool
"""
return not self.is_on
@property
def is_on(self):
"""
Returns whether the device is on.
:return: True if the device is on, False otherwise.
:rtype: bool
:return:
"""
raise NotImplementedError("Your subclass needs to implement this.")
def get_sysinfo(self):
"""
Retrieve system information.
@ -167,9 +181,10 @@ class SmartDevice(object):
:raises NotImplementedError: when not implemented
:raises SmartPlugError: on error
"""
raise NotImplementedError("Values for this call are unknown at this point.")
raise NotImplementedError()
# here just for the sake of completeness
# self._query_helper("system", "set_dev_icon", {"icon": "", "hash": ""})
# self._query_helper("system",
# "set_dev_icon", {"icon": "", "hash": ""})
# self.initialize()
@property
@ -198,7 +213,10 @@ class SmartDevice(object):
:raises SmartPlugException: on error
"""
raise NotImplementedError("Fails with err_code == 0 with HS110.")
""" here just for the sake of completeness / if someone figures out why it doesn't work.
"""
here just for the sake of completeness.
if someone figures out why it doesn't work,
please create a PR :-)
ts_obj = {
"index": self.timezone["index"],
"hour": ts.hour,
@ -384,407 +402,3 @@ class SmartDevice(object):
return response['power_mw']
else:
return response['power']
class SmartBulb(SmartDevice):
"""Representation of a TP-Link Smart Bulb.
Usage example when used as library:
p = SmartBulb("192.168.1.105")
# print the devices alias
print(p.alias)
# change state of bulb
p.state = "ON"
p.state = "OFF"
# query and print current state of plug
print(p.state)
# check whether the bulb supports color changes
if p.is_color:
# set the color to an HSV tuple
p.hsv = (100, 0, 255)
# get the current HSV value
print(p.hsv)
# check whether the bulb supports setting color temperature
if p.is_variable_color_temp:
# set the color temperature in Kelvin
p.color_temp = 3000
# get the current color temperature
print(p.color_temp)
# check whether the bulb is dimmable
if p.is_dimmable:
# set the bulb to 50% brightness
p.brightness = 50
# check the current brightness
print(p.brightness)
Errors reported by the device are raised as SmartPlugExceptions,
and should be handled by the user of the library.
"""
# bulb states
BULB_STATE_ON = 'ON'
BULB_STATE_OFF = 'OFF'
def __init__(self, ip_address, protocol=None):
SmartDevice.__init__(self, ip_address, protocol)
self.emeter_type = "smartlife.iot.common.emeter"
self.emeter_units = True
@property
def is_color(self):
"""
Whether the bulb supports color changes
:return: True if the bulb supports color changes, False otherwise
:rtype: bool
"""
return bool(self.sys_info['is_color'])
@property
def is_dimmable(self):
"""
Whether the bulb supports brightness changes
:return: True if the bulb supports brightness changes, False otherwise
:rtype: bool
"""
return bool(self.sys_info['is_dimmable'])
@property
def is_variable_color_temp(self):
"""
Whether the bulb supports color temperature changes
:return: True if the bulb supports color temperature changes, False
otherwise
:rtype: bool
"""
return bool(self.sys_info['is_variable_color_temp'])
def get_light_state(self):
return self._query_helper("smartlife.iot.smartbulb.lightingservice",
"get_light_state")
def set_light_state(self, state):
return self._query_helper("smartlife.iot.smartbulb.lightingservice",
"transition_light_state", state)
@property
def hsv(self):
"""
Returns the current HSV state of the bulb, if supported
:return: tuple containing current hue, saturation and value (0-255)
:rtype: tuple
"""
if not self.is_color:
return None
light_state = self.get_light_state()
if light_state['on_off'] == 0:
hue = light_state['dft_on_state']['hue']
saturation = light_state['dft_on_state']['saturation']
value = int(light_state['dft_on_state']['brightness'] * 255 / 100)
else:
hue = light_state['hue']
saturation = light_state['saturation']
value = int(light_state['brightness'] * 255 / 100)
return(hue, saturation, value)
@hsv.setter
def hsv(self, state):
"""
Sets new HSV, if supported
:param tuple state: hue, saturation and value (0-255 each)
"""
if not self.is_color:
return None
light_state = {
"hue": state[0],
"saturation": state[1],
"brightness": int(state[2] * 100 / 255),
"color_temp": 0
}
return self.set_light_state(light_state)
@property
def color_temp(self):
"""
Color temperature of the device, if supported
:return: Color temperature in Kelvin
:rtype: int
"""
if not self.is_variable_color_temp:
return None
light_state = self.get_light_state()
if light_state['on_off'] == 0:
return(light_state['dft_on_state']['color_temp'])
else:
return(light_state['color_temp'])
@color_temp.setter
def color_temp(self, temp):
"""
Set the color temperature of the device, if supported
:param int temp: The new color temperature, in Kelvin
"""
if not self.is_variable_color_temp:
return None
light_state = {
"color_temp": temp,
}
return self.set_light_state(light_state)
@property
def brightness(self):
"""
Current brightness of the device, if supported
:return: brightness in percent
:rtype: int
"""
if not self.is_dimmable:
return None
light_state = self.get_light_state()
if light_state['on_off'] == 0:
return(light_state['dft_on_state']['brightness'])
else:
return(light_state['brightness'])
@brightness.setter
def brightness(self, brightness):
"""
Set the current brightness of the device, if supported
:param int brightness: brightness in percent
"""
if not self.is_dimmable:
return None
light_state = {
"brightness": brightness,
}
return self.set_light_state(light_state)
@property
def state(self):
"""
Retrieve the bulb state
:returns: one of
BULB_STATE_ON
BULB_STATE_OFF
:rtype: str
"""
light_state = self.get_light_state()
if light_state['on_off']:
return self.BULB_STATE_ON
return self.BULB_STATE_OFF
@state.setter
def state(self, bulb_state):
"""
Set the new bulb state
:param bulb_state: one of
BULB_STATE_ON
BULB_STATE_OFF
"""
print(bulb_state)
print(self.BULB_STATE_ON)
print(self.BULB_STATE_OFF)
if bulb_state == self.BULB_STATE_ON:
bulb_state = 1
elif bulb_state == self.BULB_STATE_OFF:
bulb_state = 0
else:
raise ValueError
light_state = {
"on_off": bulb_state,
}
return self.set_light_state(light_state)
@property
def has_emeter(self):
return True
class SmartPlug(SmartDevice):
"""Representation of a TP-Link Smart Switch.
Usage example when used as library:
p = SmartPlug("192.168.1.105")
# print the devices alias
print(p.alias)
# change state of plug
p.state = "ON"
p.state = "OFF"
# query and print current state of plug
print(p.state)
Errors reported by the device are raised as SmartPlugExceptions,
and should be handled by the user of the library.
Note:
The library references the same structure as defined for the D-Link Switch
"""
# switch states
SWITCH_STATE_ON = 'ON'
SWITCH_STATE_OFF = 'OFF'
SWITCH_STATE_UNKNOWN = 'UNKNOWN'
# possible device features
FEATURE_ENERGY_METER = 'ENE'
FEATURE_TIMER = 'TIM'
ALL_FEATURES = (FEATURE_ENERGY_METER, FEATURE_TIMER)
def __init__(self, ip_address, protocol=None):
SmartDevice.__init__(self, ip_address, protocol)
self.emeter_type = "emeter"
self.emeter_units = False
@property
def state(self):
"""
Retrieve the switch state
:returns: one of
SWITCH_STATE_ON
SWITCH_STATE_OFF
SWITCH_STATE_UNKNOWN
:rtype: str
"""
relay_state = self.sys_info['relay_state']
if relay_state == 0:
return SmartPlug.SWITCH_STATE_OFF
elif relay_state == 1:
return SmartPlug.SWITCH_STATE_ON
else:
_LOGGER.warning("Unknown state %s returned.", relay_state)
return SmartPlug.SWITCH_STATE_UNKNOWN
@state.setter
def state(self, value):
"""
Set the new switch state
:param value: one of
SWITCH_STATE_ON
SWITCH_STATE_OFF
:raises ValueError: on invalid state
:raises SmartPlugException: on error
"""
if not isinstance(value, basestring):
raise ValueError("State must be str, not of %s.", type(value))
elif value.upper() == SmartPlug.SWITCH_STATE_ON:
self.turn_on()
elif value.upper() == SmartPlug.SWITCH_STATE_OFF:
self.turn_off()
else:
raise ValueError("State %s is not valid.", value)
@property
def is_on(self):
"""
Returns whether device is on.
:return: True if device is on, False otherwise
"""
return bool(self.sys_info['relay_state'])
@property
def is_off(self):
"""
Returns whether device is off.
:return: True if device is off, False otherwise.
:rtype: bool
"""
return not self.is_on
def turn_on(self):
"""
Turn the switch on.
:raises SmartPlugException: on error
"""
self._query_helper("system", "set_relay_state", {"state": 1})
def turn_off(self):
"""
Turn the switch off.
:raises SmartPlugException: on error
"""
self._query_helper("system", "set_relay_state", {"state": 0})
@property
def has_emeter(self):
"""
Checks feature list for energey meter support.
:return: True if energey meter is available
False if energymeter is missing
"""
return SmartPlug.FEATURE_ENERGY_METER in self.features
@property
def features(self):
"""
Returns features of the devices
:return: list of features
:rtype: list
"""
features = self.sys_info['feature'].split(':')
for feature in features:
if feature not in SmartPlug.ALL_FEATURES:
_LOGGER.warning("Unknown feature %s on device %s.",
feature, self.model)
return features
@property
def led(self):
"""
Returns the state of the led.
:return: True if led is on, False otherwise
:rtype: bool
"""
return bool(1 - self.sys_info["led_off"])
@led.setter
def led(self, state):
"""
Sets the state of the led (night mode)
:param bool state: True to set led on, False to set led off
:raises SmartPlugException: on error
"""
self._query_helper("system", "set_led_off", {"off": int(not state)})
@property
def on_since(self):
"""
Returns pretty-printed on-time
:return: datetime for on since
:rtype: datetime
"""
return datetime.datetime.now() - \
datetime.timedelta(seconds=self.sys_info["on_time"])

238
pyHS100/smartbulb.py Normal file
View File

@ -0,0 +1,238 @@
from .pyHS100 import SmartDevice
class SmartBulb(SmartDevice):
"""Representation of a TP-Link Smart Bulb.
Usage example when used as library:
p = SmartBulb("192.168.1.105")
# print the devices alias
print(p.alias)
# change state of bulb
p.state = "ON"
p.state = "OFF"
# query and print current state of plug
print(p.state)
# check whether the bulb supports color changes
if p.is_color:
# set the color to an HSV tuple
p.hsv = (100, 0, 255)
# get the current HSV value
print(p.hsv)
# check whether the bulb supports setting color temperature
if p.is_variable_color_temp:
# set the color temperature in Kelvin
p.color_temp = 3000
# get the current color temperature
print(p.color_temp)
# check whether the bulb is dimmable
if p.is_dimmable:
# set the bulb to 50% brightness
p.brightness = 50
# check the current brightness
print(p.brightness)
Errors reported by the device are raised as SmartPlugExceptions,
and should be handled by the user of the library.
"""
# bulb states
BULB_STATE_ON = 'ON'
BULB_STATE_OFF = 'OFF'
def __init__(self, ip_address, protocol=None):
SmartDevice.__init__(self, ip_address, protocol)
self.emeter_type = "smartlife.iot.common.emeter"
self.emeter_units = True
@property
def is_color(self):
"""
Whether the bulb supports color changes
:return: True if the bulb supports color changes, False otherwise
:rtype: bool
"""
return bool(self.sys_info['is_color'])
@property
def is_dimmable(self):
"""
Whether the bulb supports brightness changes
:return: True if the bulb supports brightness changes, False otherwise
:rtype: bool
"""
return bool(self.sys_info['is_dimmable'])
@property
def is_variable_color_temp(self):
"""
Whether the bulb supports color temperature changes
:return: True if the bulb supports color temperature changes, False
otherwise
:rtype: bool
"""
return bool(self.sys_info['is_variable_color_temp'])
def get_light_state(self):
return self._query_helper("smartlife.iot.smartbulb.lightingservice",
"get_light_state")
def set_light_state(self, state):
return self._query_helper("smartlife.iot.smartbulb.lightingservice",
"transition_light_state", state)
@property
def hsv(self):
"""
Returns the current HSV state of the bulb, if supported
:return: tuple containing current hue, saturation and value (0-255)
:rtype: tuple
"""
if not self.is_color:
return None
light_state = self.get_light_state()
if not self.is_on:
hue = light_state['dft_on_state']['hue']
saturation = light_state['dft_on_state']['saturation']
value = int(light_state['dft_on_state']['brightness'] * 255 / 100)
else:
hue = light_state['hue']
saturation = light_state['saturation']
value = int(light_state['brightness'] * 255 / 100)
return(hue, saturation, value)
@hsv.setter
def hsv(self, state):
"""
Sets new HSV, if supported
:param tuple state: hue, saturation and value (0-255 each)
"""
if not self.is_color:
return None
light_state = {
"hue": state[0],
"saturation": state[1],
"brightness": int(state[2] * 100 / 255),
"color_temp": 0
}
return self.set_light_state(light_state)
@property
def color_temp(self):
"""
Color temperature of the device, if supported
:return: Color temperature in Kelvin
:rtype: int
"""
if not self.is_variable_color_temp:
return None
light_state = self.get_light_state()
if not self.is_on:
return(light_state['dft_on_state']['color_temp'])
else:
return(light_state['color_temp'])
@color_temp.setter
def color_temp(self, temp):
"""
Set the color temperature of the device, if supported
:param int temp: The new color temperature, in Kelvin
"""
if not self.is_variable_color_temp:
return None
light_state = {
"color_temp": temp,
}
return self.set_light_state(light_state)
@property
def brightness(self):
"""
Current brightness of the device, if supported
:return: brightness in percent
:rtype: int
"""
if not self.is_dimmable:
return None
light_state = self.get_light_state()
if not self.is_on:
return(light_state['dft_on_state']['brightness'])
else:
return(light_state['brightness'])
@brightness.setter
def brightness(self, brightness):
"""
Set the current brightness of the device, if supported
:param int brightness: brightness in percent
"""
if not self.is_dimmable:
return None
light_state = {
"brightness": brightness,
}
return self.set_light_state(light_state)
@property
def state(self):
"""
Retrieve the bulb state
:returns: one of
BULB_STATE_ON
BULB_STATE_OFF
:rtype: str
"""
light_state = self.get_light_state()
if light_state['on_off']:
return self.BULB_STATE_ON
return self.BULB_STATE_OFF
@property
def is_on(self):
return self.state == self.BULB_STATE_ON
@state.setter
def state(self, bulb_state):
"""
Set the new bulb state
:param bulb_state: one of
BULB_STATE_ON
BULB_STATE_OFF
"""
print(bulb_state)
print(self.BULB_STATE_ON)
print(self.BULB_STATE_OFF)
if bulb_state == self.BULB_STATE_ON:
bulb_state = 1
elif bulb_state == self.BULB_STATE_OFF:
bulb_state = 0
else:
raise ValueError
light_state = {
"on_off": bulb_state,
}
return self.set_light_state(light_state)
@property
def has_emeter(self):
return True

167
pyHS100/smartplug.py Normal file
View File

@ -0,0 +1,167 @@
import datetime
import logging
from .pyHS100 import SmartDevice
_LOGGER = logging.getLogger(__name__)
class SmartPlug(SmartDevice):
"""Representation of a TP-Link Smart Switch.
Usage example when used as library:
p = SmartPlug("192.168.1.105")
# print the devices alias
print(p.alias)
# change state of plug
p.state = "ON"
p.state = "OFF"
# query and print current state of plug
print(p.state)
Errors reported by the device are raised as SmartPlugExceptions,
and should be handled by the user of the library.
Note:
The library references the same structure as defined for the D-Link Switch
"""
# switch states
SWITCH_STATE_ON = 'ON'
SWITCH_STATE_OFF = 'OFF'
SWITCH_STATE_UNKNOWN = 'UNKNOWN'
# possible device features
FEATURE_ENERGY_METER = 'ENE'
FEATURE_TIMER = 'TIM'
ALL_FEATURES = (FEATURE_ENERGY_METER, FEATURE_TIMER)
def __init__(self, ip_address, protocol=None):
SmartDevice.__init__(self, ip_address, protocol)
self.emeter_type = "emeter"
self.emeter_units = False
@property
def state(self):
"""
Retrieve the switch state
:returns: one of
SWITCH_STATE_ON
SWITCH_STATE_OFF
SWITCH_STATE_UNKNOWN
:rtype: str
"""
relay_state = self.sys_info['relay_state']
if relay_state == 0:
return SmartPlug.SWITCH_STATE_OFF
elif relay_state == 1:
return SmartPlug.SWITCH_STATE_ON
else:
_LOGGER.warning("Unknown state %s returned.", relay_state)
return SmartPlug.SWITCH_STATE_UNKNOWN
@state.setter
def state(self, value):
"""
Set the new switch state
:param value: one of
SWITCH_STATE_ON
SWITCH_STATE_OFF
:raises ValueError: on invalid state
:raises SmartPlugException: on error
"""
if not isinstance(value, str):
raise ValueError("State must be str, not of %s.", type(value))
elif value.upper() == SmartPlug.SWITCH_STATE_ON:
self.turn_on()
elif value.upper() == SmartPlug.SWITCH_STATE_OFF:
self.turn_off()
else:
raise ValueError("State %s is not valid.", value)
@property
def is_on(self):
"""
Returns whether device is on.
:return: True if device is on, False otherwise
"""
return bool(self.sys_info['relay_state'])
def turn_on(self):
"""
Turn the switch on.
:raises SmartPlugException: on error
"""
self._query_helper("system", "set_relay_state", {"state": 1})
def turn_off(self):
"""
Turn the switch off.
:raises SmartPlugException: on error
"""
self._query_helper("system", "set_relay_state", {"state": 0})
@property
def has_emeter(self):
"""
Checks feature list for energey meter support.
:return: True if energey meter is available
False if energymeter is missing
"""
return SmartPlug.FEATURE_ENERGY_METER in self.features
@property
def features(self):
"""
Returns features of the devices
:return: list of features
:rtype: list
"""
features = self.sys_info['feature'].split(':')
for feature in features:
if feature not in SmartPlug.ALL_FEATURES:
_LOGGER.warning("Unknown feature %s on device %s.",
feature, self.model)
return features
@property
def led(self):
"""
Returns the state of the led.
:return: True if led is on, False otherwise
:rtype: bool
"""
return bool(1 - self.sys_info["led_off"])
@led.setter
def led(self, state):
"""
Sets the state of the led (night mode)
:param bool state: True to set led on, False to set led off
:raises SmartPlugException: on error
"""
self._query_helper("system", "set_led_off", {"off": int(not state)})
@property
def on_since(self):
"""
Returns pretty-printed on-time
:return: datetime for on since
:rtype: datetime
"""
return datetime.datetime.now() - \
datetime.timedelta(seconds=self.sys_info["on_time"])

View File

@ -1,5 +1,5 @@
from pyHS100.protocol import TPLinkSmartHomeProtocol
from pyHS100 import SmartPlugException
from ..protocol import TPLinkSmartHomeProtocol
from .. import SmartPlugException
import logging

View File

@ -1,22 +1,13 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from unittest import TestCase, skip, skipIf
from voluptuous import Schema, Invalid, All, Range
from functools import partial
from pyHS100 import SmartBulb, SmartPlugException
from pyHS100.tests.fakes import FakeTransportProtocol, sysinfo_lb130
from .. import SmartBulb, SmartPlugException
from .fakes import FakeTransportProtocol, sysinfo_lb130
BULB_IP = '192.168.250.186'
SKIP_STATE_TESTS = False
# python2 compatibility
try:
basestring
except NameError:
basestring = str
def check_int_bool(x):
if x != 0 and x != 1:
@ -37,18 +28,18 @@ class TestSmartBulb(TestCase):
# as well as to check that faked devices are operating properly.
sysinfo_schema = Schema({
'active_mode': check_mode,
'alias': basestring,
'alias': str,
'ctrl_protocols': {
'name': basestring,
'version': basestring,
'name': str,
'version': str,
},
'description': basestring,
'dev_state': basestring,
'deviceId': basestring,
'disco_ver': basestring,
'description': str,
'dev_state': str,
'deviceId': str,
'disco_ver': str,
'heapsize': int,
'hwId': basestring,
'hw_ver': basestring,
'hwId': str,
'hw_ver': str,
'is_color': check_int_bool,
'is_dimmable': check_int_bool,
'is_factory': bool,
@ -57,14 +48,14 @@ class TestSmartBulb(TestCase):
'brightness': All(int, Range(min=0, max=100)),
'color_temp': int,
'hue': All(int, Range(min=0, max=255)),
'mode': basestring,
'mode': str,
'on_off': check_int_bool,
'saturation': All(int, Range(min=0, max=255)),
},
'mic_mac': basestring,
'mic_type': basestring,
'model': basestring,
'oemId': basestring,
'mic_mac': str,
'mic_type': str,
'model': str,
'oemId': str,
'preferred_state': [{
'brightness': All(int, Range(min=0, max=100)),
'color_temp': int,
@ -73,7 +64,7 @@ class TestSmartBulb(TestCase):
'saturation': All(int, Range(min=0, max=255)),
}],
'rssi': All(int, Range(max=0)),
'sw_ver': basestring,
'sw_ver': str,
})
current_consumption_schema = Schema({
@ -81,10 +72,10 @@ class TestSmartBulb(TestCase):
})
tz_schema = Schema({
'zone_str': basestring,
'zone_str': str,
'dst_offset': int,
'index': All(int, Range(min=0)),
'tz_str': basestring,
'tz_str': str,
})
def setUp(self):
@ -185,7 +176,7 @@ class TestSmartBulb(TestCase):
def test_alias(self):
test_alias = "TEST1234"
original = self.bulb.alias
self.assertTrue(isinstance(original, basestring))
self.assertTrue(isinstance(original, str))
self.bulb.alias = test_alias
self.assertEqual(self.bulb.alias, test_alias)
self.bulb.alias = original

View File

@ -1,8 +1,5 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from unittest import TestCase
from pyHS100.protocol import TPLinkSmartHomeProtocol
from ..protocol import TPLinkSmartHomeProtocol
import json

View File

@ -1,24 +1,15 @@
from __future__ import absolute_import
from __future__ import unicode_literals
from unittest import TestCase, skip, skipIf
from voluptuous import Schema, Invalid, All, Range
from functools import partial
import datetime
import re
from pyHS100 import SmartPlug, SmartPlugException
from pyHS100.tests.fakes import FakeTransportProtocol, sysinfo_hs110
from .. import SmartPlug, SmartPlugException
from .fakes import FakeTransportProtocol, sysinfo_hs110
PLUG_IP = '192.168.250.186'
SKIP_STATE_TESTS = False
# python2 compatibility
try:
basestring
except NameError:
basestring = str
def check_int_bool(x):
if x != 0 and x != 1:
@ -45,25 +36,25 @@ class TestSmartPlug(TestCase):
# as well as to check that faked devices are operating properly.
sysinfo_schema = Schema({
'active_mode': check_mode,
'alias': basestring,
'dev_name': basestring,
'deviceId': basestring,
'feature': basestring,
'fwId': basestring,
'hwId': basestring,
'hw_ver': basestring,
'icon_hash': basestring,
'alias': str,
'dev_name': str,
'deviceId': str,
'feature': str,
'fwId': str,
'hwId': str,
'hw_ver': str,
'icon_hash': str,
'latitude': All(float, Range(min=-90, max=90)),
'led_off': check_int_bool,
'longitude': All(float, Range(min=-180, max=180)),
'mac': check_mac,
'model': basestring,
'oemId': basestring,
'model': str,
'oemId': str,
'on_time': int,
'relay_state': int,
'rssi': All(int, Range(max=0)),
'sw_ver': basestring,
'type': basestring,
'sw_ver': str,
'type': str,
'updating': check_int_bool,
})
@ -75,10 +66,10 @@ class TestSmartPlug(TestCase):
})
tz_schema = Schema({
'zone_str': basestring,
'zone_str': str,
'dst_offset': int,
'index': All(int, Range(min=0)),
'tz_str': basestring,
'tz_str': str,
})
def setUp(self):
@ -195,7 +186,7 @@ class TestSmartPlug(TestCase):
def test_alias(self):
test_alias = "TEST1234"
original = self.plug.alias
self.assertTrue(isinstance(original, basestring))
self.assertTrue(isinstance(original, str))
self.plug.alias = test_alias
self.assertEqual(self.plug.alias, test_alias)
self.plug.alias = original

View File

@ -8,7 +8,7 @@ setup(name='pyHS100',
author_email='sean@gadgetreactor.com',
license='GPLv3',
packages=['pyHS100'],
install_requires=['click', 'click-datetime', 'future'],
install_requires=['click', 'click-datetime'],
entry_points={
'console_scripts': [
'pyhs100=pyHS100.cli:cli',

View File

@ -1,5 +1,5 @@
[tox]
envlist=py27,py34,py35,py36,flake8
envlist=py34,py35,py36,flake8
[tox:travis]
2.7 = py27
@ -9,7 +9,6 @@ envlist=py27,py34,py35,py36,flake8
[testenv]
deps=
future
pytest
voluptuous
commands=py.test pyHS100