From d22eceefae5acc3c7312dca0e83c2bb6781379e0 Mon Sep 17 00:00:00 2001 From: Teemu R Date: Sat, 7 Oct 2017 17:36:49 +0200 Subject: [PATCH] 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 --- pyHS100/smartdevice.py | 11 ++++++---- pyHS100/smartplug.py | 10 +++++++++ pyHS100/tests/test_pyHS100.py | 39 +++++++++++++++++++++++------------ 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/pyHS100/smartdevice.py b/pyHS100/smartdevice.py index df8b5152..d894c787 100644 --- a/pyHS100/smartdevice.py +++ b/pyHS100/smartdevice.py @@ -99,7 +99,8 @@ class SmartDevice(object): warnings.warn( "features works only on plugs and its use is discouraged, " "and it will likely to be removed at some point", - DeprecationWarning + DeprecationWarning, + stacklevel=2 ) warnings.simplefilter('default', DeprecationWarning) if "feature" not in self.sys_info: @@ -117,12 +118,13 @@ class SmartDevice(object): @property 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 False if energymeter is missing """ - return SmartDevice.FEATURE_ENERGY_METER in self.features + raise NotImplementedError() @property def sys_info(self) -> Dict[str, Any]: @@ -154,7 +156,8 @@ class SmartDevice(object): warnings.simplefilter('always', DeprecationWarning) warnings.warn( "use alias and model instead of idenfity()", - DeprecationWarning + DeprecationWarning, + stacklevel=2 ) warnings.simplefilter('default', DeprecationWarning) diff --git a/pyHS100/smartplug.py b/pyHS100/smartplug.py index 02a36a8c..7161dbbd 100644 --- a/pyHS100/smartplug.py +++ b/pyHS100/smartplug.py @@ -80,6 +80,16 @@ class SmartPlug(SmartDevice): else: 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 def is_on(self) -> bool: """ diff --git a/pyHS100/tests/test_pyHS100.py b/pyHS100/tests/test_pyHS100.py index adb8dfa1..17b41d2b 100644 --- a/pyHS100/tests/test_pyHS100.py +++ b/pyHS100/tests/test_pyHS100.py @@ -1,5 +1,5 @@ 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 import datetime import re @@ -12,8 +12,8 @@ from .fakes import (FakeTransportProtocol, sysinfo_hs110, sysinfo_hs200) -PLUG_IP = '192.168.250.186' -SKIP_STATE_TESTS = False +# Set IP instead of None if you want to run tests on a device. +PLUG_IP = None def check_int_bool(x): @@ -69,8 +69,8 @@ class TestSmartPlugHS110(TestCase): current_consumption_schema = Schema(Any({ 'voltage': All(float, Range(min=0, max=300)), - 'power': All(float, Range(min=0)), - 'total': All(float, Range(min=0)), + 'power': Coerce(float, Range(min=0)), + 'total': Coerce(float, Range(min=0)), 'current': All(float, Range(min=0)), }, None)) @@ -82,8 +82,11 @@ class TestSmartPlugHS110(TestCase): }) def setUp(self): - self.plug = SmartPlug(PLUG_IP, - protocol=FakeTransportProtocol(self.SYSINFO)) + if PLUG_IP is not None: + self.plug = SmartPlug(PLUG_IP) + else: + self.plug = SmartPlug("127.0.0.1", + protocol=FakeTransportProtocol(self.SYSINFO)) def tearDown(self): self.plug = None @@ -104,7 +107,6 @@ class TestSmartPlugHS110(TestCase): self.plug._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.plug.state = x @@ -136,7 +138,6 @@ class TestSmartPlugHS110(TestCase): # initialize checks for this already, but just to be sure self.sysinfo_schema(self.plug.get_sysinfo()) - @skipIf(SKIP_STATE_TESTS, "SKIP_STATE_TESTS is True, skipping") def test_turns_and_isses(self): orig_state = self.plug.is_on @@ -162,7 +163,8 @@ class TestSmartPlugHS110(TestCase): def test_get_emeter_realtime(self): 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: 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), {}) - 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(v, float)) else: @@ -183,6 +189,9 @@ class TestSmartPlugHS110(TestCase): self.assertEqual(self.plug.get_emeter_monthly(year=1900), {}) d = self.plug.get_emeter_monthly() + if len(d) < 1: + print("no emeter monthly information, skipping..") + return k, v = d.popitem() self.assertTrue(isinstance(k, int)) self.assertTrue(isinstance(v, float)) @@ -264,6 +273,10 @@ class TestSmartPlugHS105(TestSmartPlugHS110): SYSINFO = sysinfo_hs105 def test_location_i(self): - plug_i = SmartPlug(PLUG_IP, - protocol=FakeTransportProtocol(self.SYSINFO)) + if PLUG_IP is not None: + plug_i = SmartPlug(PLUG_IP) + else: + plug_i = SmartPlug("127.0.0.1", + protocol=FakeTransportProtocol(self.SYSINFO)) + self.sysinfo_schema(plug_i.location)