Merge pull request #76 from rytilahti/extend_tests

Extend tests
This commit is contained in:
Georgi Kirichkov 2017-08-06 15:36:40 +03:00 committed by GitHub
commit d851540380
5 changed files with 154 additions and 110 deletions

View File

@ -9,8 +9,10 @@ if sys.version_info < (3, 4):
sys.version_info) sys.version_info)
sys.exit(1) sys.exit(1)
from pyHS100 import (SmartDevice, SmartPlug, SmartBulb, from pyHS100 import (SmartDevice,
TPLinkSmartHomeProtocol, Discover) # noqa: E402 SmartPlug,
SmartBulb,
Discover) # noqa: E402
pass_dev = click.make_pass_decorator(SmartDevice) pass_dev = click.make_pass_decorator(SmartDevice)

View File

@ -338,12 +338,12 @@ class SmartDevice(object):
Retrive current energy readings from device. Retrive current energy readings from device.
:returns: current readings or False :returns: current readings or False
:rtype: dict, False :rtype: dict, None
False if device has no energy meter or error occured None if device has no energy meter or error occured
:raises SmartDeviceException: on error :raises SmartDeviceException: on error
""" """
if not self.has_emeter: if not self.has_emeter:
return False return None
return self._query_helper(self.emeter_type, "get_realtime") return self._query_helper(self.emeter_type, "get_realtime")
@ -355,12 +355,12 @@ class SmartDevice(object):
:param month: month for which to retrieve statistcs (default: this :param month: month for which to retrieve statistcs (default: this
month) month)
: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 None if device has no energy meter or error occured
:rtype: dict :rtype: dict
:raises SmartDeviceException: on error :raises SmartDeviceException: on error
""" """
if not self.has_emeter: if not self.has_emeter:
return False return None
if year is None: if year is None:
year = datetime.datetime.now().year year = datetime.datetime.now().year
@ -384,12 +384,12 @@ class SmartDevice(object):
:param year: year for which to retrieve statistics (default: this year) :param year: year for which to retrieve statistics (default: this year)
:return: dict: mapping of month to value :return: dict: mapping of month to value
False if device has no energy meter None if device has no energy meter
:rtype: dict :rtype: dict
:raises SmartDeviceException: on error :raises SmartDeviceException: on error
""" """
if not self.has_emeter: if not self.has_emeter:
return False return None
response = self._query_helper(self.emeter_type, "get_monthstat", response = self._query_helper(self.emeter_type, "get_monthstat",
{'year': year}) {'year': year})
@ -412,7 +412,7 @@ class SmartDevice(object):
:raises SmartDeviceException: on error :raises SmartDeviceException: on error
""" """
if not self.has_emeter: if not self.has_emeter:
return False return None
self._query_helper(self.emeter_type, "erase_emeter_stat", None) self._query_helper(self.emeter_type, "erase_emeter_stat", None)
@ -425,11 +425,11 @@ class SmartDevice(object):
Get the current power consumption in Watt. Get the current power consumption in Watt.
:return: the current power consumption in Watt. :return: the current power consumption in Watt.
False if device has no energy meter. None if device has no energy meter.
:raises SmartDeviceException: on error :raises SmartDeviceException: on error
""" """
if not self.has_emeter: if not self.has_emeter:
return False return None
response = self.get_emeter_realtime() response = self.get_emeter_realtime()
if self.emeter_units: if self.emeter_units:

View File

@ -58,7 +58,7 @@ sysinfo_hs100 = {'system': {'get_sysinfo':
'latitude': 12.2, 'latitude': 12.2,
'led_off': 0, 'led_off': 0,
'longitude': 12.2, 'longitude': 12.2,
'mac': '50:C7:BF:xx:xx:xx', 'mac': '50:C7:BF:11:22:33',
'model': 'HS100(EU)', 'model': 'HS100(EU)',
'oemId': '812A90EB2FCF306A993FAD8748024B07', 'oemId': '812A90EB2FCF306A993FAD8748024B07',
'on_time': 255419, 'on_time': 255419,
@ -71,7 +71,7 @@ sysinfo_hs105 = {'system': {'get_sysinfo':
{'sw_ver': '1.0.6 Build 160722 Rel.081616', {'sw_ver': '1.0.6 Build 160722 Rel.081616',
'hw_ver': '1.0', 'type': 'IOT.SMARTPLUGSWITCH', 'hw_ver': '1.0', 'type': 'IOT.SMARTPLUGSWITCH',
'model': 'HS105(US)', 'model': 'HS105(US)',
'mac': '50:C7:BF:xx:xx:xx', 'mac': '50:C7:BF:11:22:33',
'dev_name': 'Smart Wi-Fi Plug Mini', 'dev_name': 'Smart Wi-Fi Plug Mini',
'alias': 'TP-LINK_Smart Plug_CF0B', 'alias': 'TP-LINK_Smart Plug_CF0B',
'relay_state': 0, 'relay_state': 0,
@ -198,70 +198,73 @@ sysinfo_lb130 = {'system': {'get_sysinfo':
'smartlife.iot.common.emeter': emeter_units_support, 'smartlife.iot.common.emeter': emeter_units_support,
} }
sysinfo_lb110 = {'sys_info': {'emeter': sysinfo_lb110 = {'system': {
{'err_code': -2001, 'sys_info':
'err_msg': 'Module not support'}, {'emeter':
'system': {'err_code': -2001,
{'get_sysinfo': 'err_msg': 'Module not support'},
{'active_mode': 'schedule', 'system':
'alias': 'Downstairs Light', {'get_sysinfo':
'ctrl_protocols': {'active_mode': 'schedule',
{'name': 'Linkie', 'alias': 'Downstairs Light',
'version': '1.0'}, 'ctrl_protocols':
'description': 'Smart Wi-Fi LED Bulb ' {'name': 'Linkie',
'with Dimmable Light', 'version': '1.0'},
'dev_state': 'normal', 'description': 'Smart Wi-Fi LED Bulb '
'deviceId': 'with Dimmable Light',
'80120B3D03E0B639CDF33E3CB1466490187FEF32', 'dev_state': 'normal',
'disco_ver': '1.0', 'deviceId':
'err_code': 0, '80120B3D03E0B639CDF33E3CB1466490187FEF32',
'heapsize': 309908, 'disco_ver': '1.0',
'hwId': '111E35908497A05512E259BB76801E10', 'err_code': 0,
'hw_ver': '1.0', 'heapsize': 309908,
'is_color': 0, 'hwId': '111E35908497A05512E259BB76801E10',
'is_dimmable': 1, 'hw_ver': '1.0',
'is_factory': False, 'is_color': 0,
'is_variable_color_temp': 0, 'is_dimmable': 1,
'light_state': 'is_factory': False,
{'dft_on_state': 'is_variable_color_temp': 0,
{'brightness': 92, 'light_state':
'color_temp': 2700, {'dft_on_state':
'hue': 0, {'brightness': 92,
'mode': 'normal', 'color_temp': 2700,
'saturation': 0}, 'hue': 0,
'on_off': 0}, 'mode': 'normal',
'mic_mac': '50C7BF7BE306', 'saturation': 0},
'mic_type': 'IOT.SMARTBULB', 'on_off': 0},
'model': 'LB110(EU)', 'mic_mac': '50C7BF7BE306',
'oemId': 'mic_type': 'IOT.SMARTBULB',
'A68E15472071CB761E5CCFB388A1D8AE', 'model': 'LB110(EU)',
'preferred_state': [{'brightness': 100, 'oemId':
'color_temp': 2700, 'A68E15472071CB761E5CCFB388A1D8AE',
'hue': 0, 'preferred_state': [{'brightness': 100,
'index': 0, 'color_temp': 2700,
'saturation': 0}, 'hue': 0,
{'brightness': 58, 'index': 0,
'color_temp': 2700, 'saturation': 0},
'hue': 0, {'brightness': 58,
'index': 1, 'color_temp': 2700,
'saturation': 0}, 'hue': 0,
{'brightness': 25, 'index': 1,
'color_temp': 2700, 'saturation': 0},
'hue': 0, {'brightness': 25,
'index': 2, 'color_temp': 2700,
'saturation': 0}, 'hue': 0,
{'brightness': 1, 'index': 2,
'color_temp': 2700, 'saturation': 0},
'hue': 0, {'brightness': 1,
'index': 3, 'color_temp': 2700,
'saturation': 0}], 'hue': 0,
'rssi': -61, 'index': 3,
'sw_ver': '1.5.5 Build 170623 ' 'saturation': 0}],
'Rel.090105' 'rssi': -61,
} 'sw_ver': '1.5.5 Build 170623 '
} 'Rel.090105'
} }
} }
}
}}
def error(cls, target, cmd="no-command", msg="default msg"): def error(cls, target, cmd="no-command", msg="default msg"):
return {target: {cmd: {"err_code": -1323, "msg": msg}}} return {target: {cmd: {"err_code": -1323, "msg": msg}}}

View File

@ -3,7 +3,7 @@ from voluptuous import Schema, Invalid, All, Range
from functools import partial from functools import partial
from .. import SmartBulb, SmartDeviceException from .. import SmartBulb, SmartDeviceException
from .fakes import FakeTransportProtocol, sysinfo_lb130 from .fakes import FakeTransportProtocol, sysinfo_lb130, sysinfo_lb110
BULB_IP = '192.168.250.186' BULB_IP = '192.168.250.186'
SKIP_STATE_TESTS = False SKIP_STATE_TESTS = False
@ -23,6 +23,7 @@ def check_mode(x):
class TestSmartBulb(TestCase): class TestSmartBulb(TestCase):
SYSINFO = sysinfo_lb130
# these schemas should go to the mainlib as # these schemas should go to the mainlib as
# they can be useful when adding support for new features/devices # they can be useful when adding support for new features/devices
# as well as to check that faked devices are operating properly. # as well as to check that faked devices are operating properly.
@ -80,7 +81,7 @@ class TestSmartBulb(TestCase):
def setUp(self): def setUp(self):
self.bulb = SmartBulb(BULB_IP, self.bulb = SmartBulb(BULB_IP,
protocol=FakeTransportProtocol(sysinfo_lb130)) protocol=FakeTransportProtocol(self.SYSINFO))
def tearDown(self): def tearDown(self):
self.bulb = None self.bulb = None
@ -91,7 +92,7 @@ class TestSmartBulb(TestCase):
def test_initialize_invalid_connection(self): def test_initialize_invalid_connection(self):
bulb = SmartBulb('127.0.0.1', bulb = SmartBulb('127.0.0.1',
protocol=FakeTransportProtocol(sysinfo_lb130, protocol=FakeTransportProtocol(self.SYSINFO,
invalid=True)) invalid=True))
with self.assertRaises(SmartDeviceException): with self.assertRaises(SmartDeviceException):
bulb.sys_info['model'] bulb.sys_info['model']
@ -187,3 +188,7 @@ class TestSmartBulb(TestCase):
def test_rssi(self): def test_rssi(self):
self.sysinfo_schema({'rssi': self.bulb.rssi}) # wrapping for vol self.sysinfo_schema({'rssi': self.bulb.rssi}) # wrapping for vol
class TestSmartBulbLB110(TestSmartBulb):
SYSINFO = sysinfo_lb110

View File

@ -1,11 +1,15 @@
from unittest import TestCase, skip, skipIf from unittest import TestCase, skip, skipIf
from voluptuous import Schema, Invalid, All, Range from voluptuous import Schema, Invalid, All, Any, Range
from functools import partial from functools import partial
import datetime import datetime
import re import re
from .. import SmartPlug, SmartDeviceException from .. import SmartPlug, SmartDeviceException
from .fakes import FakeTransportProtocol, sysinfo_hs110, sysinfo_hs105 from .fakes import (FakeTransportProtocol,
sysinfo_hs100,
sysinfo_hs105,
sysinfo_hs110,
sysinfo_hs200)
PLUG_IP = '192.168.250.186' PLUG_IP = '192.168.250.186'
SKIP_STATE_TESTS = False SKIP_STATE_TESTS = False
@ -24,13 +28,14 @@ def check_mac(x):
def check_mode(x): def check_mode(x):
if x in ['schedule']: if x in ['schedule', 'none']:
return x return x
raise Invalid("invalid mode {}".format(x)) raise Invalid("invalid mode {}".format(x))
class TestSmartPlug(TestCase): class TestSmartPlugHS110(TestCase):
SYSINFO = sysinfo_hs110
# these schemas should go to the mainlib as # these schemas should go to the mainlib as
# they can be useful when adding support for new features/devices # they can be useful when adding support for new features/devices
# as well as to check that faked devices are operating properly. # as well as to check that faked devices are operating properly.
@ -44,26 +49,29 @@ class TestSmartPlug(TestCase):
'hwId': str, 'hwId': str,
'hw_ver': str, 'hw_ver': str,
'icon_hash': str, 'icon_hash': str,
'latitude': All(float, Range(min=-90, max=90)),
'led_off': check_int_bool, 'led_off': check_int_bool,
'longitude': All(float, Range(min=-180, max=180)), 'latitude': Any(All(float, Range(min=-90, max=90)), None),
'latitude_i': Any(All(float, Range(min=-90, max=90)), None),
'longitude': Any(All(float, Range(min=-180, max=180)), None),
'longitude_i': Any(All(float, Range(min=-180, max=180)), None),
'mac': check_mac, 'mac': check_mac,
'model': str, 'model': str,
'oemId': str, 'oemId': str,
'on_time': int, 'on_time': int,
'relay_state': int, 'relay_state': int,
'rssi': All(int, Range(max=0)), 'rssi': Any(int, None), # rssi can also be positive, see #54
'sw_ver': str, 'sw_ver': str,
'type': str, 'type': str,
'mic_type': str,
'updating': check_int_bool, 'updating': check_int_bool,
}) })
current_consumption_schema = Schema({ current_consumption_schema = Schema(Any({
'voltage': All(float, Range(min=0, max=300)), 'voltage': All(float, Range(min=0, max=300)),
'power': All(float, Range(min=0)), 'power': All(float, Range(min=0)),
'total': All(float, Range(min=0)), 'total': All(float, Range(min=0)),
'current': All(float, Range(min=0)), 'current': All(float, Range(min=0)),
}) }, None))
tz_schema = Schema({ tz_schema = Schema({
'zone_str': str, 'zone_str': str,
@ -74,7 +82,7 @@ class TestSmartPlug(TestCase):
def setUp(self): def setUp(self):
self.plug = SmartPlug(PLUG_IP, self.plug = SmartPlug(PLUG_IP,
protocol=FakeTransportProtocol(sysinfo_hs110)) protocol=FakeTransportProtocol(self.SYSINFO))
def tearDown(self): def tearDown(self):
self.plug = None self.plug = None
@ -85,7 +93,7 @@ class TestSmartPlug(TestCase):
def test_initialize_invalid_connection(self): def test_initialize_invalid_connection(self):
plug = SmartPlug('127.0.0.1', plug = SmartPlug('127.0.0.1',
protocol=FakeTransportProtocol(sysinfo_hs110, protocol=FakeTransportProtocol(self.SYSINFO,
invalid=True)) invalid=True))
with self.assertRaises(SmartDeviceException): with self.assertRaises(SmartDeviceException):
plug.sys_info['model'] plug.sys_info['model']
@ -152,31 +160,45 @@ class TestSmartPlug(TestCase):
self.assertFalse(self.plug.has_emeter) self.assertFalse(self.plug.has_emeter)
def test_get_emeter_realtime(self): def test_get_emeter_realtime(self):
self.current_consumption_schema((self.plug.get_emeter_realtime())) if self.plug.has_emeter:
self.current_consumption_schema((self.plug.get_emeter_realtime()))
else:
self.assertEqual(self.plug.get_emeter_realtime(), None)
def test_get_emeter_daily(self): def test_get_emeter_daily(self):
self.assertEqual(self.plug.get_emeter_daily(year=1900, month=1), {}) if self.plug.has_emeter:
self.assertEqual(self.plug.get_emeter_daily(year=1900, month=1),
{})
k, v = self.plug.get_emeter_daily().popitem() k, v = self.plug.get_emeter_daily().popitem()
self.assertTrue(isinstance(k, int)) self.assertTrue(isinstance(k, int))
self.assertTrue(isinstance(v, float)) self.assertTrue(isinstance(v, float))
else:
self.assertEqual(self.plug.get_emeter_daily(year=1900, month=1),
None)
def test_get_emeter_monthly(self): def test_get_emeter_monthly(self):
self.assertEqual(self.plug.get_emeter_monthly(year=1900), {}) if self.plug.has_emeter:
self.assertEqual(self.plug.get_emeter_monthly(year=1900), {})
d = self.plug.get_emeter_monthly() d = self.plug.get_emeter_monthly()
k, v = d.popitem() k, v = d.popitem()
self.assertTrue(isinstance(k, int)) self.assertTrue(isinstance(k, int))
self.assertTrue(isinstance(v, float)) self.assertTrue(isinstance(v, float))
else:
self.assertEqual(self.plug.get_emeter_monthly(year=1900), None)
@skip("not clearing your stats..") @skip("not clearing your stats..")
def test_erase_emeter_stats(self): def test_erase_emeter_stats(self):
self.fail() self.fail()
def test_current_consumption(self): def test_current_consumption(self):
x = self.plug.current_consumption() if self.plug.has_emeter:
self.assertTrue(isinstance(x, float)) x = self.plug.current_consumption()
self.assertTrue(x >= 0.0) self.assertTrue(isinstance(x, float))
self.assertTrue(x >= 0.0)
else:
self.assertEqual(self.plug.current_consumption(), None)
def test_identify(self): def test_identify(self):
ident = self.plug.identify() ident = self.plug.identify()
@ -221,14 +243,26 @@ class TestSmartPlug(TestCase):
def test_location(self): def test_location(self):
self.sysinfo_schema(self.plug.location) self.sysinfo_schema(self.plug.location)
def test_location_i(self):
plug_i = SmartPlug(PLUG_IP,
protocol=FakeTransportProtocol(sysinfo_hs105))
self.sysinfo_schema(plug_i.location)
def test_rssi(self): def test_rssi(self):
self.sysinfo_schema({'rssi': self.plug.rssi}) # wrapping for vol self.sysinfo_schema({'rssi': self.plug.rssi}) # wrapping for vol
def test_mac(self): def test_mac(self):
self.sysinfo_schema({'mac': self.plug.mac}) # wrapping for val self.sysinfo_schema({'mac': self.plug.mac}) # wrapping for val
# TODO check setting? # TODO check setting?
class TestSmartPlugHS100(TestSmartPlugHS110):
SYSINFO = sysinfo_hs100
class TestSmartPlugHS200(TestSmartPlugHS110):
SYSINFO = sysinfo_hs200
class TestSmartPlugHS105(TestSmartPlugHS110):
SYSINFO = sysinfo_hs105
def test_location_i(self):
plug_i = SmartPlug(PLUG_IP,
protocol=FakeTransportProtocol(self.SYSINFO))
self.sysinfo_schema(plug_i.location)