def ping_plug(ip_addr): try: plug = SmartPlug(ip_addr) plug.get_sysinfo() logger.info("Plug %s is up and running!", ip_addr) except SmartDeviceException: logger.error("Failed to connect to the plug with IP %s", ip_addr)
def add_entity(device: SmartPlug, async_add_entities): """Check if device is online and add the entity.""" # Attempt to get the sysinfo. If it fails, it will raise an # exception that is caught by async_add_entities_retry which # will try again later. device.get_sysinfo() async_add_entities([SmartPlugSwitch(device)], update_before_add=True)
async def test_configuring_devices_from_multiple_sources(hass): """Test static and discover devices are not duplicated.""" with patch( "homeassistant.components.tplink.common.Discover.discover" ) as discover, patch( "homeassistant.components.tplink.common.SmartDevice._query_helper" ), patch( "homeassistant.config_entries.ConfigEntries.async_forward_entry_setup" ): discover_device_fail = SmartPlug("123.123.123.123") discover_device_fail.get_sysinfo = MagicMock( side_effect=SmartDeviceException()) discover.return_value = { "123.123.123.1": SmartBulb("123.123.123.1"), "123.123.123.2": SmartPlug("123.123.123.2"), "123.123.123.3": SmartBulb("123.123.123.3"), "123.123.123.4": SmartPlug("123.123.123.4"), "123.123.123.123": discover_device_fail, "123.123.123.124": UnknownSmartDevice("123.123.123.124"), } await async_setup_component( hass, tplink.DOMAIN, { tplink.DOMAIN: { CONF_LIGHT: [{ CONF_HOST: "123.123.123.1" }], CONF_SWITCH: [{ CONF_HOST: "123.123.123.2" }], CONF_DIMMER: [{ CONF_HOST: "123.123.123.22" }], } }, ) await hass.async_block_till_done() assert len(discover.mock_calls) == 1 assert len(hass.data[tplink.DOMAIN][CONF_LIGHT]) == 3 assert len(hass.data[tplink.DOMAIN][CONF_SWITCH]) == 2
async def test_unload(hass, platform): """Test that the async_unload_entry works.""" # As we have currently no configuration, we just to pass the domain here. entry = MockConfigEntry(domain=tplink.DOMAIN) entry.add_to_hass(hass) with patch( "homeassistant.components.tplink.get_static_devices" ) as get_static_devices, patch( "homeassistant.components.tplink.common.SmartDevice._query_helper" ), patch( f"homeassistant.components.tplink.{platform}.async_setup_entry", return_value=mock_coro(True), ) as async_setup_entry: config = { tplink.DOMAIN: { platform: [{ CONF_HOST: "123.123.123.123" }], CONF_DISCOVERY: False, } } light = SmartBulb("123.123.123.123") switch = SmartPlug("321.321.321.321") switch.get_sysinfo = MagicMock( return_value=SMARTPLUG_HS110_DATA["sysinfo"]) switch.get_emeter_realtime = MagicMock( return_value=EmeterStatus(SMARTPLUG_HS110_DATA["realtime"])) if platform == "light": get_static_devices.return_value = SmartDevices([light], []) elif platform == "switch": get_static_devices.return_value = SmartDevices([], [switch]) assert await async_setup_component(hass, tplink.DOMAIN, config) await hass.async_block_till_done() assert len(async_setup_entry.mock_calls) == 1 assert tplink.DOMAIN in hass.data assert await tplink.async_unload_entry(hass, entry) assert not hass.data[tplink.DOMAIN]
async def test_smartplug_without_consumption_sensors(hass: HomeAssistant): """Test that platforms are initialized per configuration array.""" config = { tplink.DOMAIN: { CONF_DISCOVERY: False, CONF_SWITCH: [{ CONF_HOST: "321.321.321.321" }], } } with patch( "homeassistant.components.tplink.common.Discover.discover" ), patch( "homeassistant.components.tplink.get_static_devices" ) as get_static_devices, patch( "homeassistant.components.tplink.common.SmartDevice._query_helper" ), patch( "homeassistant.components.tplink.light.async_setup_entry", return_value=mock_coro(True), ), patch( "homeassistant.components.tplink.switch.async_setup_entry", return_value=mock_coro(True), ), patch("homeassistant.components.tplink.common.SmartPlug.is_dimmable", False): switch = SmartPlug("321.321.321.321") switch.get_sysinfo = MagicMock( return_value=SMARTPLUGSWITCH_DATA["sysinfo"]) get_static_devices.return_value = SmartDevices([], [switch]) await async_setup_component(hass, tplink.DOMAIN, config) await hass.async_block_till_done() for description in ENERGY_SENSORS: state = hass.states.get( f"sensor.{switch.alias}_{slugify(description.name)}") assert state is None
async def test_not_ready(hass: HomeAssistant): """Test for not ready when configured devices are not available.""" config = { tplink.DOMAIN: { CONF_DISCOVERY: False, CONF_SWITCH: [{ CONF_HOST: "321.321.321.321" }], } } with patch( "homeassistant.components.tplink.common.Discover.discover" ), patch( "homeassistant.components.tplink.get_static_devices" ) as get_static_devices, patch( "homeassistant.components.tplink.common.SmartDevice._query_helper" ), patch( "homeassistant.components.tplink.light.async_setup_entry", return_value=mock_coro(True), ), patch( "homeassistant.components.tplink.switch.async_setup_entry", return_value=mock_coro(True), ), patch("homeassistant.components.tplink.common.SmartPlug.is_dimmable", False): switch = SmartPlug("321.321.321.321") switch.get_sysinfo = MagicMock(side_effect=SmartDeviceException()) get_static_devices.return_value = SmartDevices([], [switch]) await async_setup_component(hass, tplink.DOMAIN, config) await hass.async_block_till_done() entries = hass.config_entries.async_entries(tplink.DOMAIN) assert len(entries) == 1 assert entries[0].state is config_entries.ConfigEntryState.SETUP_RETRY
plug = SmartPlug("192.168.43.216") row = {} # row["currentstate"] = plug.state emeterdaily = plug.get_emeter_daily(year=2018, month=10) for k, v in emeterdaily.items(): row["day%s" % k] = v hwinfo = plug.hw_info for k, v in hwinfo.items(): row["%s" % k] = v sysinfo = plug.get_sysinfo() for k, v in sysinfo.items(): row["%s" % k] = v timezone = plug.timezone for k, v in timezone.items(): row["%s" % k] = v emetermonthly = plug.get_emeter_monthly(year=2018) for k, v in emetermonthly.items(): row["month%s" % k] = v realtime = plug.get_emeter_realtime() for k, v in realtime.items(): row["%s" % k] = v
def plugInfo(name): plug = SmartPlug(deviceList[name]) print("Hardware: %s" % pf(plug.hw_info)) print("Full sysinfo: %s" % pf(plug.get_sysinfo())) print("Current state: %s" % plug.state)
async def test_not_available_at_startup(hass: HomeAssistant): """Test when configured devices are not available.""" config = { tplink.DOMAIN: { CONF_DISCOVERY: False, CONF_SWITCH: [{ CONF_HOST: "321.321.321.321" }], } } with patch( "homeassistant.components.tplink.common.Discover.discover" ), patch( "homeassistant.components.tplink.get_static_devices" ) as get_static_devices, patch( "homeassistant.components.tplink.common.SmartDevice._query_helper" ), patch( "homeassistant.components.tplink.light.async_setup_entry", return_value=mock_coro(True), ), patch("homeassistant.components.tplink.common.SmartPlug.is_dimmable", False): switch = SmartPlug("321.321.321.321") switch.get_sysinfo = MagicMock(side_effect=SmartDeviceException()) get_static_devices.return_value = SmartDevices([], [switch]) # run setup while device unreachable await async_setup_component(hass, tplink.DOMAIN, config) await hass.async_block_till_done() entries = hass.config_entries.async_entries(tplink.DOMAIN) assert len(entries) == 1 assert entries[0].state is config_entries.ConfigEntryState.LOADED entities = hass.states.async_entity_ids(SWITCH_DOMAIN) assert len(entities) == 0 # retrying with still unreachable device async_fire_time_changed(hass, dt.utcnow() + UNAVAILABLE_RETRY_DELAY) await hass.async_block_till_done() entries = hass.config_entries.async_entries(tplink.DOMAIN) assert len(entries) == 1 assert entries[0].state is config_entries.ConfigEntryState.LOADED entities = hass.states.async_entity_ids(SWITCH_DOMAIN) assert len(entities) == 0 # retrying with now reachable device switch.get_sysinfo = MagicMock( return_value=SMARTPLUG_HS100_DATA["sysinfo"]) async_fire_time_changed(hass, dt.utcnow() + UNAVAILABLE_RETRY_DELAY) await hass.async_block_till_done() entries = hass.config_entries.async_entries(tplink.DOMAIN) assert len(entries) == 1 assert entries[0].state is config_entries.ConfigEntryState.LOADED entities = hass.states.async_entity_ids(SWITCH_DOMAIN) assert len(entities) == 1
async def test_platforms_are_initialized(hass: HomeAssistant): """Test that platforms are initialized per configuration array.""" config = { tplink.DOMAIN: { CONF_DISCOVERY: False, CONF_LIGHT: [{ CONF_HOST: "123.123.123.123" }], CONF_SWITCH: [{ CONF_HOST: "321.321.321.321" }], } } with patch( "homeassistant.components.tplink.common.Discover.discover" ), patch( "homeassistant.components.tplink.get_static_devices" ) as get_static_devices, patch( "homeassistant.components.tplink.common.SmartDevice._query_helper" ), patch( "homeassistant.components.tplink.light.async_setup_entry", return_value=mock_coro(True), ), patch( "homeassistant.components.tplink.common.SmartPlug.is_dimmable", False, ): light = SmartBulb("123.123.123.123") switch = SmartPlug("321.321.321.321") switch.get_sysinfo = MagicMock( return_value=SMARTPLUG_HS110_DATA["sysinfo"]) switch.get_emeter_realtime = MagicMock( return_value=EmeterStatus(SMARTPLUG_HS110_DATA["realtime"])) switch.get_emeter_daily = MagicMock( return_value={int(time.strftime("%e")): 1.123}) get_static_devices.return_value = SmartDevices([light], [switch]) # patching is_dimmable is necessray to avoid misdetection as light. await async_setup_component(hass, tplink.DOMAIN, config) await hass.async_block_till_done() state = hass.states.get(f"switch.{switch.alias}") assert state assert state.name == switch.alias for description in ENERGY_SENSORS: state = hass.states.get( f"sensor.{switch.alias}_{slugify(description.name)}") assert state assert state.state is not None assert state.name == f"{switch.alias} {description.name}" device_registry = dr.async_get(hass) assert len(device_registry.devices) == 1 device = next(iter(device_registry.devices.values())) assert device.name == switch.alias assert device.model == switch.model assert device.connections == {(dr.CONNECTION_NETWORK_MAC, switch.mac.lower())} assert device.sw_version == switch.sys_info[CONF_SW_VERSION]
class TestSmartPlug(TestCase): # 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, 'alias': basestring, 'dev_name': basestring, 'deviceId': basestring, 'feature': basestring, 'fwId': basestring, 'hwId': basestring, 'hw_ver': basestring, 'icon_hash': basestring, 'latitude': All(float, Range(min=-90, max=90)), 'led_off': check_int_bool, 'longitude': All(float, Range(min=-180, max=180)), 'mac': check_mac, 'model': basestring, 'oemId': basestring, 'on_time': int, 'relay_state': int, 'rssi': All(int, Range(max=0)), 'sw_ver': basestring, 'type': basestring, 'updating': check_int_bool, }) current_consumption_schema = Schema({ 'voltage': All(float, Range(min=0, max=300)), 'power': All(float, Range(min=0)), 'total': All(float, Range(min=0)), 'current': All(float, Range(min=0)), }) tz_schema = Schema({ 'zone_str': basestring, 'dst_offset': int, 'index': All(int, Range(min=0)), 'tz_str': basestring, }) def setUp(self): self.plug = SmartPlug(PLUG_IP, protocol=FakeTransportProtocol(sysinfo_hs110)) def tearDown(self): self.plug = None def test_initialize(self): self.assertIsNotNone(self.plug.sys_info) self.sysinfo_schema(self.plug.sys_info) def test_initialize_invalid_connection(self): plug = SmartPlug('127.0.0.1', protocol=FakeTransportProtocol(sysinfo_hs110, invalid=True)) with self.assertRaises(SmartPlugException): plug.sys_info['model'] def test_query_helper(self): with self.assertRaises(SmartPlugException): 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 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.plug.state if orig_state == SmartPlug.SWITCH_STATE_OFF: self.plug.state = "ON" self.assertTrue(self.plug.state == SmartPlug.SWITCH_STATE_ON) self.plug.state = "OFF" self.assertTrue(self.plug.state == SmartPlug.SWITCH_STATE_OFF) elif orig_state == SmartPlug.SWITCH_STATE_ON: self.plug.state = "OFF" self.assertTrue(self.plug.state == SmartPlug.SWITCH_STATE_OFF) self.plug.state = "ON" self.assertTrue(self.plug.state == SmartPlug.SWITCH_STATE_ON) elif orig_state == SmartPlug.SWITCH_STATE_UNKNOWN: self.fail("can't test for unknown state") def test_get_sysinfo(self): # 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 if orig_state: self.plug.turn_off() self.assertFalse(self.plug.is_on) self.assertTrue(self.plug.is_off) self.plug.turn_on() self.assertTrue(self.plug.is_on) else: self.plug.turn_on() self.assertFalse(self.plug.is_off) self.assertTrue(self.plug.is_on) self.plug.turn_off() self.assertTrue(self.plug.is_off) def test_has_emeter(self): # a not so nice way for checking for emeter availability.. if "110" in self.plug.sys_info["model"]: self.assertTrue(self.plug.has_emeter) else: self.assertFalse(self.plug.has_emeter) def test_get_emeter_realtime(self): self.current_consumption_schema((self.plug.get_emeter_realtime())) def test_get_emeter_daily(self): self.assertEqual(self.plug.get_emeter_daily(year=1900, month=1), {}) k, v = self.plug.get_emeter_daily().popitem() self.assertTrue(isinstance(k, int)) self.assertTrue(isinstance(v, float)) def test_get_emeter_monthly(self): self.assertEqual(self.plug.get_emeter_monthly(year=1900), {}) d = self.plug.get_emeter_monthly() 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() def test_current_consumption(self): x = self.plug.current_consumption() self.assertTrue(isinstance(x, float)) self.assertTrue(x >= 0.0) 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 self.assertTrue(isinstance(original, basestring)) self.plug.alias = test_alias self.assertEqual(self.plug.alias, test_alias) self.plug.alias = original self.assertEqual(self.plug.alias, original) def test_led(self): original = self.plug.led self.plug.led = False self.assertFalse(self.plug.led) self.plug.led = True self.assertTrue(self.plug.led) self.plug.led = original def test_icon(self): self.assertEqual(set(self.plug.icon.keys()), {'icon', 'hash'}) def test_time(self): self.assertTrue(isinstance(self.plug.time, datetime.datetime)) # TODO check setting? def test_timezone(self): self.tz_schema(self.plug.timezone) def test_hw_info(self): self.sysinfo_schema(self.plug.hw_info) def test_on_since(self): self.assertTrue(isinstance(self.plug.on_since, datetime.datetime)) def test_location(self): self.sysinfo_schema(self.plug.location) def test_rssi(self): self.sysinfo_schema({'rssi': self.plug.rssi}) # wrapping for vol def test_mac(self): self.sysinfo_schema({'mac': self.plug.mac}) # wrapping for val
from pyHS100 import SmartPlug, SmartBulb from pprint import pformat as pf plug = SmartPlug("10.0.0.122") print("Hardware: %s" % pf(plug.hw_info)) print( "Full sysinfo: %s" % pf(plug.get_sysinfo())) # this prints lots of information about the device plug.turn_off()
import sys import logging from pprint import pformat as pf from pyHS100 import SmartPlug logging.basicConfig(level=logging.DEBUG) if len(sys.argv) < 2: print("%s <ip>" % sys.argv[0]) sys.exit(1) hs = SmartPlug(sys.argv[1]) logging.info("Identify: %s", hs.identify()) logging.info("Sysinfo: %s", pf(hs.get_sysinfo())) has_emeter = hs.has_emeter if has_emeter: logging.info("== Emeter ==") logging.info("- Current: %s", hs.get_emeter_realtime()) logging.info("== Monthly ==") logging.info(hs.get_emeter_monthly()) logging.info("== Daily ==") logging.info(hs.get_emeter_daily(month=11, year=2016))