move has_emeter implementation from SmartDevice to SmartPlug, avoid using features() internally (#93)

* move has_emeter implementation from SmartDevice to SmartPlug, avoid using features() internally

* add stacklevel to deprecation warnings to see where they are really called

* make tests pass on a real device. if PLUG_IP is not None, the tests will be run on a device at the defined IP address
This commit is contained in:
Teemu R 2017-10-07 17:36:49 +02:00 committed by GitHub
parent af90a36153
commit d22eceefae
3 changed files with 43 additions and 17 deletions

View File

@ -99,7 +99,8 @@ class SmartDevice(object):
warnings.warn( warnings.warn(
"features works only on plugs and its use is discouraged, " "features works only on plugs and its use is discouraged, "
"and it will likely to be removed at some point", "and it will likely to be removed at some point",
DeprecationWarning DeprecationWarning,
stacklevel=2
) )
warnings.simplefilter('default', DeprecationWarning) warnings.simplefilter('default', DeprecationWarning)
if "feature" not in self.sys_info: if "feature" not in self.sys_info:
@ -117,12 +118,13 @@ class SmartDevice(object):
@property @property
def has_emeter(self) -> bool: def has_emeter(self) -> bool:
""" """
Checks feature list for energey meter support. Checks feature list for energy meter support.
Note: this has to be implemented on a device specific class.
:return: True if energey meter is available :return: True if energey meter is available
False if energymeter is missing False if energymeter is missing
""" """
return SmartDevice.FEATURE_ENERGY_METER in self.features raise NotImplementedError()
@property @property
def sys_info(self) -> Dict[str, Any]: def sys_info(self) -> Dict[str, Any]:
@ -154,7 +156,8 @@ class SmartDevice(object):
warnings.simplefilter('always', DeprecationWarning) warnings.simplefilter('always', DeprecationWarning)
warnings.warn( warnings.warn(
"use alias and model instead of idenfity()", "use alias and model instead of idenfity()",
DeprecationWarning DeprecationWarning,
stacklevel=2
) )
warnings.simplefilter('default', DeprecationWarning) warnings.simplefilter('default', DeprecationWarning)

View File

@ -80,6 +80,16 @@ class SmartPlug(SmartDevice):
else: else:
raise ValueError("State %s is not valid.", value) raise ValueError("State %s is not valid.", value)
@property
def has_emeter(self):
"""
Returns whether device has an energy meter.
:return: True if energy meter is available
False otherwise
"""
features = self.sys_info['feature'].split(':')
return SmartDevice.FEATURE_ENERGY_METER in features
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:
""" """

View File

@ -1,5 +1,5 @@
from unittest import TestCase, skip, skipIf from unittest import TestCase, skip, skipIf
from voluptuous import Schema, Invalid, All, Any, Range from voluptuous import Schema, Invalid, All, Any, Range, Coerce
from functools import partial from functools import partial
import datetime import datetime
import re import re
@ -12,8 +12,8 @@ from .fakes import (FakeTransportProtocol,
sysinfo_hs110, sysinfo_hs110,
sysinfo_hs200) sysinfo_hs200)
PLUG_IP = '192.168.250.186' # Set IP instead of None if you want to run tests on a device.
SKIP_STATE_TESTS = False PLUG_IP = None
def check_int_bool(x): def check_int_bool(x):
@ -69,8 +69,8 @@ class TestSmartPlugHS110(TestCase):
current_consumption_schema = Schema(Any({ 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': Coerce(float, Range(min=0)),
'total': All(float, Range(min=0)), 'total': Coerce(float, Range(min=0)),
'current': All(float, Range(min=0)), 'current': All(float, Range(min=0)),
}, None)) }, None))
@ -82,8 +82,11 @@ class TestSmartPlugHS110(TestCase):
}) })
def setUp(self): def setUp(self):
self.plug = SmartPlug(PLUG_IP, if PLUG_IP is not None:
protocol=FakeTransportProtocol(self.SYSINFO)) self.plug = SmartPlug(PLUG_IP)
else:
self.plug = SmartPlug("127.0.0.1",
protocol=FakeTransportProtocol(self.SYSINFO))
def tearDown(self): def tearDown(self):
self.plug = None self.plug = None
@ -104,7 +107,6 @@ class TestSmartPlugHS110(TestCase):
self.plug._query_helper("test", "testcmd", {}) self.plug._query_helper("test", "testcmd", {})
# TODO check for unwrapping? # TODO check for unwrapping?
@skipIf(SKIP_STATE_TESTS, "SKIP_STATE_TESTS is True, skipping")
def test_state(self): def test_state(self):
def set_invalid(x): def set_invalid(x):
self.plug.state = x self.plug.state = x
@ -136,7 +138,6 @@ class TestSmartPlugHS110(TestCase):
# initialize checks for this already, but just to be sure # initialize checks for this already, but just to be sure
self.sysinfo_schema(self.plug.get_sysinfo()) self.sysinfo_schema(self.plug.get_sysinfo())
@skipIf(SKIP_STATE_TESTS, "SKIP_STATE_TESTS is True, skipping")
def test_turns_and_isses(self): def test_turns_and_isses(self):
orig_state = self.plug.is_on orig_state = self.plug.is_on
@ -162,7 +163,8 @@ class TestSmartPlugHS110(TestCase):
def test_get_emeter_realtime(self): def test_get_emeter_realtime(self):
if self.plug.has_emeter: if self.plug.has_emeter:
self.current_consumption_schema((self.plug.get_emeter_realtime())) current_emeter = self.plug.get_emeter_realtime()
self.current_consumption_schema(current_emeter)
else: else:
self.assertEqual(self.plug.get_emeter_realtime(), None) self.assertEqual(self.plug.get_emeter_realtime(), None)
@ -171,7 +173,11 @@ class TestSmartPlugHS110(TestCase):
self.assertEqual(self.plug.get_emeter_daily(year=1900, month=1), self.assertEqual(self.plug.get_emeter_daily(year=1900, month=1),
{}) {})
k, v = self.plug.get_emeter_daily().popitem() d = self.plug.get_emeter_daily()
if len(d) < 1:
print("no emeter daily information, skipping..")
return
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: else:
@ -183,6 +189,9 @@ class TestSmartPlugHS110(TestCase):
self.assertEqual(self.plug.get_emeter_monthly(year=1900), {}) self.assertEqual(self.plug.get_emeter_monthly(year=1900), {})
d = self.plug.get_emeter_monthly() d = self.plug.get_emeter_monthly()
if len(d) < 1:
print("no emeter monthly information, skipping..")
return
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))
@ -264,6 +273,10 @@ class TestSmartPlugHS105(TestSmartPlugHS110):
SYSINFO = sysinfo_hs105 SYSINFO = sysinfo_hs105
def test_location_i(self): def test_location_i(self):
plug_i = SmartPlug(PLUG_IP, if PLUG_IP is not None:
protocol=FakeTransportProtocol(self.SYSINFO)) plug_i = SmartPlug(PLUG_IP)
else:
plug_i = SmartPlug("127.0.0.1",
protocol=FakeTransportProtocol(self.SYSINFO))
self.sysinfo_schema(plug_i.location) self.sysinfo_schema(plug_i.location)