2017-01-17 13:38:23 +00:00
|
|
|
from unittest import TestCase, skip, skipIf
|
|
|
|
from voluptuous import Schema, Invalid, All, Range
|
|
|
|
from functools import partial
|
2017-09-18 16:13:06 +00:00
|
|
|
from typing import Any, Dict # noqa: F401
|
2017-01-17 13:38:23 +00:00
|
|
|
|
2017-05-26 14:11:03 +00:00
|
|
|
from .. import SmartBulb, SmartDeviceException
|
2017-08-22 14:55:49 +00:00
|
|
|
from .fakes import (FakeTransportProtocol,
|
2017-08-30 00:35:34 +00:00
|
|
|
sysinfo_lb100, sysinfo_lb110,
|
|
|
|
sysinfo_lb120, sysinfo_lb130)
|
2017-09-18 16:13:06 +00:00
|
|
|
|
2017-01-17 13:38:23 +00:00
|
|
|
BULB_IP = '192.168.250.186'
|
|
|
|
SKIP_STATE_TESTS = False
|
|
|
|
|
|
|
|
|
|
|
|
def check_int_bool(x):
|
|
|
|
if x != 0 and x != 1:
|
|
|
|
raise Invalid(x)
|
|
|
|
return x
|
|
|
|
|
|
|
|
|
|
|
|
def check_mode(x):
|
|
|
|
if x in ['schedule', 'none']:
|
|
|
|
return x
|
|
|
|
|
|
|
|
raise Invalid("invalid mode {}".format(x))
|
|
|
|
|
|
|
|
|
|
|
|
class TestSmartBulb(TestCase):
|
2017-09-18 16:13:06 +00:00
|
|
|
SYSINFO = sysinfo_lb130 # type: Dict[str, Any]
|
2017-01-17 13:38:23 +00:00
|
|
|
# these schemas should go to the mainlib as
|
|
|
|
# they can be useful when adding support for new features/devices
|
|
|
|
# as well as to check that faked devices are operating properly.
|
|
|
|
sysinfo_schema = Schema({
|
|
|
|
'active_mode': check_mode,
|
2017-04-14 12:24:58 +00:00
|
|
|
'alias': str,
|
2017-01-17 13:38:23 +00:00
|
|
|
'ctrl_protocols': {
|
2017-04-14 12:24:58 +00:00
|
|
|
'name': str,
|
|
|
|
'version': str,
|
2017-01-17 13:38:23 +00:00
|
|
|
},
|
2017-04-14 12:24:58 +00:00
|
|
|
'description': str,
|
|
|
|
'dev_state': str,
|
|
|
|
'deviceId': str,
|
|
|
|
'disco_ver': str,
|
2017-01-17 13:38:23 +00:00
|
|
|
'heapsize': int,
|
2017-04-14 12:24:58 +00:00
|
|
|
'hwId': str,
|
|
|
|
'hw_ver': str,
|
2017-01-17 13:38:23 +00:00
|
|
|
'is_color': check_int_bool,
|
|
|
|
'is_dimmable': check_int_bool,
|
|
|
|
'is_factory': bool,
|
|
|
|
'is_variable_color_temp': check_int_bool,
|
|
|
|
'light_state': {
|
|
|
|
'brightness': All(int, Range(min=0, max=100)),
|
|
|
|
'color_temp': int,
|
|
|
|
'hue': All(int, Range(min=0, max=255)),
|
2017-04-14 12:24:58 +00:00
|
|
|
'mode': str,
|
2017-01-17 13:38:23 +00:00
|
|
|
'on_off': check_int_bool,
|
|
|
|
'saturation': All(int, Range(min=0, max=255)),
|
|
|
|
},
|
2017-04-14 12:24:58 +00:00
|
|
|
'mic_mac': str,
|
|
|
|
'mic_type': str,
|
|
|
|
'model': str,
|
|
|
|
'oemId': str,
|
2017-01-17 13:38:23 +00:00
|
|
|
'preferred_state': [{
|
|
|
|
'brightness': All(int, Range(min=0, max=100)),
|
|
|
|
'color_temp': int,
|
|
|
|
'hue': All(int, Range(min=0, max=255)),
|
|
|
|
'index': int,
|
|
|
|
'saturation': All(int, Range(min=0, max=255)),
|
|
|
|
}],
|
|
|
|
'rssi': All(int, Range(max=0)),
|
2017-04-14 12:24:58 +00:00
|
|
|
'sw_ver': str,
|
2017-01-17 13:38:23 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
current_consumption_schema = Schema({
|
|
|
|
'power_mw': int,
|
|
|
|
})
|
|
|
|
|
|
|
|
tz_schema = Schema({
|
2017-04-14 12:24:58 +00:00
|
|
|
'zone_str': str,
|
2017-01-17 13:38:23 +00:00
|
|
|
'dst_offset': int,
|
|
|
|
'index': All(int, Range(min=0)),
|
2017-04-14 12:24:58 +00:00
|
|
|
'tz_str': str,
|
2017-01-17 13:38:23 +00:00
|
|
|
})
|
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
self.bulb = SmartBulb(BULB_IP,
|
2017-08-05 15:22:36 +00:00
|
|
|
protocol=FakeTransportProtocol(self.SYSINFO))
|
2017-01-17 13:38:23 +00:00
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
self.bulb = None
|
|
|
|
|
|
|
|
def test_initialize(self):
|
|
|
|
self.assertIsNotNone(self.bulb.sys_info)
|
|
|
|
self.sysinfo_schema(self.bulb.sys_info)
|
|
|
|
|
|
|
|
def test_initialize_invalid_connection(self):
|
|
|
|
bulb = SmartBulb('127.0.0.1',
|
2017-08-05 15:22:36 +00:00
|
|
|
protocol=FakeTransportProtocol(self.SYSINFO,
|
2017-01-17 13:38:23 +00:00
|
|
|
invalid=True))
|
2017-05-26 14:11:03 +00:00
|
|
|
with self.assertRaises(SmartDeviceException):
|
2017-01-17 13:38:23 +00:00
|
|
|
bulb.sys_info['model']
|
|
|
|
|
|
|
|
def test_query_helper(self):
|
2017-05-26 14:11:03 +00:00
|
|
|
with self.assertRaises(SmartDeviceException):
|
2017-01-17 13:38:23 +00:00
|
|
|
self.bulb._query_helper("test", "testcmd", {})
|
|
|
|
# TODO check for unwrapping?
|
|
|
|
|
|
|
|
@skipIf(SKIP_STATE_TESTS, "SKIP_STATE_TESTS is True, skipping")
|
|
|
|
def test_state(self):
|
|
|
|
def set_invalid(x):
|
|
|
|
self.bulb.state = x
|
|
|
|
|
|
|
|
set_invalid_int = partial(set_invalid, 1234)
|
|
|
|
self.assertRaises(ValueError, set_invalid_int)
|
|
|
|
|
|
|
|
set_invalid_str = partial(set_invalid, "1234")
|
|
|
|
self.assertRaises(ValueError, set_invalid_str)
|
|
|
|
|
|
|
|
set_invalid_bool = partial(set_invalid, True)
|
|
|
|
self.assertRaises(ValueError, set_invalid_bool)
|
|
|
|
|
|
|
|
orig_state = self.bulb.state
|
|
|
|
if orig_state == SmartBulb.BULB_STATE_OFF:
|
|
|
|
self.bulb.state = SmartBulb.BULB_STATE_ON
|
|
|
|
self.assertTrue(self.bulb.state == SmartBulb.BULB_STATE_ON)
|
|
|
|
self.bulb.state = SmartBulb.BULB_STATE_OFF
|
|
|
|
self.assertTrue(self.bulb.state == SmartBulb.BULB_STATE_OFF)
|
|
|
|
elif orig_state == SmartBulb.BULB_STATE_ON:
|
|
|
|
self.bulb.state = SmartBulb.BULB_STATE_OFF
|
|
|
|
self.assertTrue(self.bulb.state == SmartBulb.BULB_STATE_OFF)
|
|
|
|
self.bulb.state = SmartBulb.BULB_STATE_ON
|
|
|
|
self.assertTrue(self.bulb.state == SmartBulb.BULB_STATE_ON)
|
|
|
|
|
|
|
|
def test_get_sysinfo(self):
|
|
|
|
# initialize checks for this already, but just to be sure
|
|
|
|
self.sysinfo_schema(self.bulb.get_sysinfo())
|
|
|
|
|
|
|
|
@skipIf(SKIP_STATE_TESTS, "SKIP_STATE_TESTS is True, skipping")
|
|
|
|
def test_turns_and_isses(self):
|
|
|
|
orig_state = self.bulb.state
|
|
|
|
|
|
|
|
if orig_state == SmartBulb.BULB_STATE_ON:
|
|
|
|
self.bulb.state = SmartBulb.BULB_STATE_OFF
|
|
|
|
self.assertTrue(self.bulb.state == SmartBulb.BULB_STATE_OFF)
|
|
|
|
self.bulb.state = SmartBulb.BULB_STATE_ON
|
|
|
|
self.assertTrue(self.bulb.state == SmartBulb.BULB_STATE_ON)
|
|
|
|
else:
|
|
|
|
self.bulb.state = SmartBulb.BULB_STATE_ON
|
|
|
|
self.assertTrue(self.bulb.state == SmartBulb.BULB_STATE_ON)
|
|
|
|
self.bulb.state = SmartBulb.BULB_STATE_OFF
|
|
|
|
self.assertTrue(self.bulb.state == SmartBulb.BULB_STATE_OFF)
|
|
|
|
|
|
|
|
def test_get_emeter_realtime(self):
|
|
|
|
self.current_consumption_schema((self.bulb.get_emeter_realtime()))
|
|
|
|
|
|
|
|
def test_get_emeter_daily(self):
|
|
|
|
self.assertEqual(self.bulb.get_emeter_daily(year=1900, month=1), {})
|
|
|
|
|
2018-06-16 19:16:35 +00:00
|
|
|
k, v = self.bulb.get_emeter_daily(kwh=False).popitem()
|
2017-01-17 13:38:23 +00:00
|
|
|
self.assertTrue(isinstance(k, int))
|
|
|
|
self.assertTrue(isinstance(v, int))
|
|
|
|
|
2018-06-16 19:16:35 +00:00
|
|
|
k, v = self.bulb.get_emeter_daily(kwh=True).popitem()
|
|
|
|
self.assertTrue(isinstance(k, int))
|
|
|
|
self.assertTrue(isinstance(v, float))
|
|
|
|
|
2017-01-17 13:38:23 +00:00
|
|
|
def test_get_emeter_monthly(self):
|
|
|
|
self.assertEqual(self.bulb.get_emeter_monthly(year=1900), {})
|
|
|
|
|
2018-06-16 19:16:35 +00:00
|
|
|
d = self.bulb.get_emeter_monthly(kwh=False)
|
2017-01-17 13:38:23 +00:00
|
|
|
k, v = d.popitem()
|
|
|
|
self.assertTrue(isinstance(k, int))
|
|
|
|
self.assertTrue(isinstance(v, int))
|
|
|
|
|
2018-06-16 19:16:35 +00:00
|
|
|
d = self.bulb.get_emeter_monthly(kwh=True)
|
|
|
|
k, v = d.popitem()
|
|
|
|
self.assertTrue(isinstance(k, int))
|
|
|
|
self.assertTrue(isinstance(v, float))
|
|
|
|
|
2017-01-17 13:38:23 +00:00
|
|
|
@skip("not clearing your stats..")
|
|
|
|
def test_erase_emeter_stats(self):
|
|
|
|
self.fail()
|
|
|
|
|
|
|
|
def test_current_consumption(self):
|
|
|
|
x = self.bulb.current_consumption()
|
2017-09-18 16:13:06 +00:00
|
|
|
self.assertTrue(isinstance(x, float))
|
2017-01-17 13:38:23 +00:00
|
|
|
self.assertTrue(x >= 0.0)
|
|
|
|
|
|
|
|
def test_alias(self):
|
|
|
|
test_alias = "TEST1234"
|
|
|
|
original = self.bulb.alias
|
2017-04-14 12:24:58 +00:00
|
|
|
self.assertTrue(isinstance(original, str))
|
2017-01-17 13:38:23 +00:00
|
|
|
self.bulb.alias = test_alias
|
|
|
|
self.assertEqual(self.bulb.alias, test_alias)
|
|
|
|
self.bulb.alias = original
|
|
|
|
self.assertEqual(self.bulb.alias, original)
|
|
|
|
|
|
|
|
def test_icon(self):
|
|
|
|
self.assertEqual(set(self.bulb.icon.keys()), {'icon', 'hash'})
|
|
|
|
|
|
|
|
def test_rssi(self):
|
|
|
|
self.sysinfo_schema({'rssi': self.bulb.rssi}) # wrapping for vol
|
2017-08-05 15:22:36 +00:00
|
|
|
|
2018-06-17 17:01:18 +00:00
|
|
|
def test_temperature_range(self):
|
|
|
|
self.assertEqual(self.bulb.valid_temperature_range, (2500, 9000))
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
self.bulb.color_temp = 1000
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
self.bulb.color_temp = 10000
|
|
|
|
|
2018-09-05 20:44:44 +00:00
|
|
|
def test_hsv(self):
|
|
|
|
hue, saturation, brightness = self.bulb.hsv
|
|
|
|
self.assertTrue(0 <= hue <= 255)
|
|
|
|
self.assertTrue(0 <= saturation <= 100)
|
|
|
|
self.assertTrue(0 <= brightness <= 100)
|
|
|
|
for invalid_hue in [-1, 256, 0.5]:
|
|
|
|
with self.assertRaises(SmartDeviceException):
|
|
|
|
self.bulb.hsv = (invalid_hue, 0, 0)
|
|
|
|
|
|
|
|
for invalid_saturation in [-1, 101, 0.5]:
|
|
|
|
with self.assertRaises(SmartDeviceException):
|
|
|
|
self.bulb.hsv = (0, invalid_saturation, 0)
|
|
|
|
|
|
|
|
for invalid_brightness in [-1, 101, 0.5]:
|
|
|
|
with self.assertRaises(SmartDeviceException):
|
|
|
|
self.bulb.hsv = (0, 0, invalid_brightness)
|
|
|
|
|
2017-08-05 15:22:36 +00:00
|
|
|
|
2017-08-22 14:55:49 +00:00
|
|
|
class TestSmartBulbLB100(TestSmartBulb):
|
|
|
|
SYSINFO = sysinfo_lb100
|
|
|
|
|
|
|
|
|
2017-08-05 15:22:36 +00:00
|
|
|
class TestSmartBulbLB110(TestSmartBulb):
|
|
|
|
SYSINFO = sysinfo_lb110
|
2017-08-30 00:35:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
class TestSmartBulbLB120(TestSmartBulb):
|
|
|
|
SYSINFO = sysinfo_lb120
|