Fix emeter support for newer HS110 firmwares (#107)

* Add support for new-style emeter

This commit adds a straightforward dict-extending container,
which converts between the old and new keys of the get_emeter_realtime()
Furthermore the unit tests are converted to base on HS100
instead of HS110.

This is the first step to fix #103, other emeter-using functionality
has not yet been converted, only getting the current consumption.

* fix a couple of linting issues

* Convert new-style emeter values also for get_emeter_daily() and get_emeter_monthly()

* Adds a new 'kwh' parameter for those calls, which defaults to True
* This changes the behavior of bulbs emeter reporting, use False if you prefer the preciser values
This commit is contained in:
Teemu R
2018-06-16 21:16:35 +02:00
committed by GitHub
parent 11a7042a04
commit ef2e21ff69
6 changed files with 162 additions and 39 deletions

View File

@@ -113,9 +113,34 @@ sysinfo_hs110 = {'system': {'get_sysinfo':
'type': 'IOT.SMARTPLUGSWITCH',
'updating': 0}
},
"emeter": emeter_support,
'emeter': emeter_support,
}
sysinfo_hs110_au_v2 = {'system': {'get_sysinfo':
{'active_mode': 'none',
'alias': 'Tplink Test',
'dev_name': 'Smart Wi-Fi Plug With Energy Monitoring',
'deviceId': '80062952E2F3D9461CFB91FF21B7868F194F627A',
'feature': 'TIM:ENE',
'fwId': '00000000000000000000000000000000',
'hwId': 'A28C8BB92AFCB6CAFB83A8C00145F7E2',
'hw_ver': '2.0',
'icon_hash': '',
'latitude_i': -1.1,
'led_off': 0,
'longitude_i': 2.2,
'mac': '70:4F:57:12:12:12',
'model': 'HS110(AU)',
'oemId': '6480C2101948463DC65D7009CAECDECC',
'on_time': 0,
'relay_state': 0,
'rssi': -70,
'sw_ver': '1.5.2 Build 171201 Rel.084625',
'type': 'IOT.SMARTPLUGSWITCH',
'updating': 0}
},
'emeter': {'voltage_mv': 246520, 'power_mw': 258401, 'current_ma': 3104, 'total_wh': 387}}
sysinfo_hs200 = {'system': {'get_sysinfo': {'active_mode': 'schedule',
'alias': 'Christmas Tree Switch',
'dev_name': 'Wi-Fi Smart Light Switch',

View File

@@ -156,18 +156,27 @@ class TestSmartBulb(TestCase):
def test_get_emeter_daily(self):
self.assertEqual(self.bulb.get_emeter_daily(year=1900, month=1), {})
k, v = self.bulb.get_emeter_daily().popitem()
k, v = self.bulb.get_emeter_daily(kwh=False).popitem()
self.assertTrue(isinstance(k, int))
self.assertTrue(isinstance(v, int))
k, v = self.bulb.get_emeter_daily(kwh=True).popitem()
self.assertTrue(isinstance(k, int))
self.assertTrue(isinstance(v, float))
def test_get_emeter_monthly(self):
self.assertEqual(self.bulb.get_emeter_monthly(year=1900), {})
d = self.bulb.get_emeter_monthly()
d = self.bulb.get_emeter_monthly(kwh=False)
k, v = d.popitem()
self.assertTrue(isinstance(k, int))
self.assertTrue(isinstance(v, int))
d = self.bulb.get_emeter_monthly(kwh=True)
k, v = d.popitem()
self.assertTrue(isinstance(k, int))
self.assertTrue(isinstance(v, float))
@skip("not clearing your stats..")
def test_erase_emeter_stats(self):
self.fail()

View File

@@ -10,6 +10,7 @@ from .fakes import (FakeTransportProtocol,
sysinfo_hs100,
sysinfo_hs105,
sysinfo_hs110,
sysinfo_hs110_au_v2,
sysinfo_hs200)
# Set IP instead of None if you want to run tests on a device.
@@ -35,8 +36,8 @@ def check_mode(x):
raise Invalid("invalid mode {}".format(x))
class TestSmartPlugHS110(TestCase):
SYSINFO = sysinfo_hs110 # type: Dict
class TestSmartPlugHS100(TestCase):
SYSINFO = sysinfo_hs100 # type: Dict
# 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.
@@ -68,10 +69,15 @@ class TestSmartPlugHS110(TestCase):
})
current_consumption_schema = Schema(Any({
'voltage': All(float, Range(min=0, max=300)),
'power': Coerce(float, Range(min=0)),
'total': Coerce(float, Range(min=0)),
'current': All(float, Range(min=0)),
'voltage': Any(All(float, Range(min=0, max=300)), None),
'power': Any(Coerce(float, Range(min=0)), None),
'total': Any(Coerce(float, Range(min=0)), None),
'current': Any(All(float, Range(min=0)), None),
'voltage_mw': Any(All(float, Range(min=0, max=300000)), None),
'power_mw': Any(Coerce(float, Range(min=0)), None),
'total_wh': Any(Coerce(float, Range(min=0)), None),
'current_ma': Any(All(float, Range(min=0)), None),
}, None))
tz_schema = Schema({
@@ -210,11 +216,6 @@ class TestSmartPlugHS110(TestCase):
else:
self.assertEqual(self.plug.current_consumption(), None)
def test_identify(self):
ident = self.plug.identify()
self.assertTrue(isinstance(ident, tuple))
self.assertTrue(len(ident) == 3)
def test_alias(self):
test_alias = "TEST1234"
original = self.plug.alias
@@ -261,15 +262,69 @@ class TestSmartPlugHS110(TestCase):
# TODO check setting?
class TestSmartPlugHS100(TestSmartPlugHS110):
SYSINFO = sysinfo_hs100
class TestSmartPlugHS110(TestSmartPlugHS100):
SYSINFO = sysinfo_hs110
def test_emeter_upcast(self):
emeter = self.plug.get_emeter_realtime()
self.assertAlmostEqual(emeter["power"] * 10**3, emeter["power_mw"])
self.assertAlmostEqual(emeter["voltage"] * 10**3, emeter["voltage_mv"])
self.assertAlmostEqual(emeter["current"] * 10**3, emeter["current_ma"])
self.assertAlmostEqual(emeter["total"] * 10**3, emeter["total_wh"])
def test_emeter_daily_upcast(self):
emeter = self.plug.get_emeter_daily()
_, v = emeter.popitem()
emeter = self.plug.get_emeter_daily(kwh=False)
_, v2 = emeter.popitem()
self.assertAlmostEqual(v * 10**3, v2)
def test_get_emeter_monthly_upcast(self):
emeter = self.plug.get_emeter_monthly()
_, v = emeter.popitem()
emeter = self.plug.get_emeter_monthly(kwh=False)
_, v2 = emeter.popitem()
self.assertAlmostEqual(v * 10**3, v2)
class TestSmartPlugHS200(TestSmartPlugHS110):
class TestSmartPlugHS110_HW2(TestSmartPlugHS100):
SYSINFO = sysinfo_hs110_au_v2
def test_emeter_downcast(self):
emeter = self.plug.get_emeter_realtime()
self.assertAlmostEqual(emeter["power"], emeter["power_mw"] / 10**3)
self.assertAlmostEqual(emeter["voltage"], emeter["voltage_mv"] / 10**3)
self.assertAlmostEqual(emeter["current"], emeter["current_ma"] / 10**3)
self.assertAlmostEqual(emeter["total"], emeter["total_wh"] / 10**3)
def test_emeter_daily_downcast(self):
emeter = self.plug.get_emeter_daily()
_, v = emeter.popitem()
emeter = self.plug.get_emeter_daily(kwh=False)
_, v2 = emeter.popitem()
self.assertAlmostEqual(v * 10**3, v2)
def test_get_emeter_monthly_downcast(self):
emeter = self.plug.get_emeter_monthly()
_, v = emeter.popitem()
emeter = self.plug.get_emeter_monthly(kwh=False)
_, v2 = emeter.popitem()
self.assertAlmostEqual(v * 10**3, v2)
class TestSmartPlugHS200(TestSmartPlugHS100):
SYSINFO = sysinfo_hs200
class TestSmartPlugHS105(TestSmartPlugHS110):
class TestSmartPlugHS105(TestSmartPlugHS100):
SYSINFO = sysinfo_hs105
def test_location_i(self):