From ba2600cb931631830562ee833709405e85d8b7c7 Mon Sep 17 00:00:00 2001 From: Bas Nijholt Date: Mon, 11 Nov 2019 17:55:56 +0100 Subject: [PATCH] make SmartStrip use asyncio --- pyHS100/smartstrip.py | 151 ++++++++++++++++++------------------------ 1 file changed, 66 insertions(+), 85 deletions(-) diff --git a/pyHS100/smartstrip.py b/pyHS100/smartstrip.py index 7cfe7e95..fad1a6de 100755 --- a/pyHS100/smartstrip.py +++ b/pyHS100/smartstrip.py @@ -1,9 +1,11 @@ +import asyncio import datetime import logging from typing import Any, Dict, Optional, Union -from deprecation import deprecated -from pyHS100 import SmartPlug, SmartDeviceException, EmeterStatus, DeviceType +from deprecation import deprecated +from pyHS100 import DeviceType, EmeterStatus, SmartDeviceException, SmartPlug + from .protocol import TPLinkSmartHomeProtocol _LOGGER = logging.getLogger(__name__) @@ -42,7 +44,9 @@ class SmartStrip(SmartPlug): self.emeter_type = "emeter" self._device_type = DeviceType.Strip self.plugs = {} - children = self.sys_info["children"] + + sys_info = asyncio.run(self.get_sys_info()) + children = sys_info["children"] self.num_children = len(children) for plug in range(self.num_children): self.plugs[plug] = SmartPlug( @@ -59,14 +63,7 @@ class SmartStrip(SmartPlug): if index not in range(self.num_children): raise SmartStripException("plug index of %d " "is out of bounds" % index) - @property - @deprecated(details="use is_on, get_is_on()") - def state(self) -> bool: - if self.is_on: - return self.STATE_ON - return self.STATE_OFF - - def get_state(self, *, index=-1) -> Dict[int, str]: + async def get_state(self, *, index=-1) -> Dict[int, str]: """Retrieve the switch state :returns: list with the state of each child plug @@ -75,36 +72,14 @@ class SmartStrip(SmartPlug): :rtype: dict """ - def _state_for_bool(b): - return SmartPlug.STATE_ON if b else SmartPlug.STATE_OFF + def _state_for_bool(_bool): + return SmartPlug.STATE_ON if _bool else SmartPlug.STATE_OFF - is_on = self.get_is_on(index=index) + is_on = await self.get_is_on(index=index) if isinstance(is_on, bool): return _state_for_bool(is_on) - print(is_on) - - return {k: _state_for_bool(v) for k, v in self.get_is_on().items()} - - @state.setter - @deprecated(details="use turn_on(), turn_off()") - def state(self, value: str): - """Sets the state of all plugs in the strip - - :param value: one of - STATE_ON - STATE_OFF - :raises ValueError: on invalid state - :raises SmartDeviceException: on error - """ - if not isinstance(value, str): - raise ValueError("State must be str, not of %s.", type(value)) - elif value.upper() == SmartPlug.STATE_ON: - self.turn_on() - elif value.upper() == SmartPlug.STATE_OFF: - self.turn_off() - else: - raise ValueError("State %s is not valid.", value) + return {k: _state_for_bool(v) for k, v in is_on.items()} def set_state(self, value: str, *, index: int = -1): """Sets the state of a plug on the strip @@ -123,12 +98,12 @@ class SmartStrip(SmartPlug): self.raise_for_index(index) self.plugs[index].state = value - @property - def is_on(self) -> bool: + async def is_on(self) -> bool: """Return if any of the outlets are on""" - return any(state == "ON" for state in self.get_state().values()) + states = await self.get_state() + return any(state == "ON" for state in states.values()) - def get_is_on(self, *, index: int = -1) -> Any: + async def get_is_on(self, *, index: int = -1) -> Any: """ Returns whether device is on. @@ -138,7 +113,8 @@ class SmartStrip(SmartPlug): Dict[int, bool] if no index provided :raises SmartStripException: index out of bounds """ - children = self.sys_info["children"] + sys_info = await self.get_sys_info() + children = sys_info["children"] if index < 0: is_on = {} for plug in range(self.num_children): @@ -148,14 +124,14 @@ class SmartStrip(SmartPlug): self.raise_for_index(index) return bool(children[index]["state"]) - def get_is_off(self, *, index: int = -1) -> Any: - is_on = self.get_is_on(index=index) + async def get_is_off(self, *, index: int = -1) -> Any: + is_on = await self.get_is_on(index=index) if isinstance(is_on, bool): return not is_on else: return {k: not v for k, v in is_on} - def turn_on(self, *, index: int = -1): + async def turn_on(self, *, index: int = -1): """ Turns outlets on @@ -164,12 +140,12 @@ class SmartStrip(SmartPlug): :raises SmartStripException: index out of bounds """ if index < 0: - self._query_helper("system", "set_relay_state", {"state": 1}) + await self._query_helper("system", "set_relay_state", {"state": 1}) else: self.raise_for_index(index) - self.plugs[index].turn_on() + await self.plugs[index].turn_on() - def turn_off(self, *, index: int = -1): + async def turn_off(self, *, index: int = -1): """ Turns outlets off @@ -178,17 +154,17 @@ class SmartStrip(SmartPlug): :raises SmartStripException: index out of bounds """ if index < 0: - self._query_helper("system", "set_relay_state", {"state": 0}) + await self._query_helper("system", "set_relay_state", {"state": 0}) else: self.raise_for_index(index) - self.plugs[index].turn_off() + await self.plugs[index].turn_off() - @property - def on_since(self) -> datetime: + async def get_on_since(self) -> datetime: """Returns the maximum on-time of all outlets.""" - return max(v for v in self.get_on_since().values()) + on_since = await self._get_on_since() + return max(v for v in await on_since.values()) - def get_on_since(self, *, index: int = -1) -> Any: + async def _get_on_since(self, *, index: int = -1) -> Any: """ Returns pretty-printed on-time @@ -200,7 +176,8 @@ class SmartStrip(SmartPlug): """ if index < 0: on_since = {} - children = self.sys_info["children"] + sys_info = await self.get_sys_info() + children = sys_info["children"] for plug in range(self.num_children): child_ontime = children[plug]["on_time"] @@ -210,19 +187,18 @@ class SmartStrip(SmartPlug): return on_since else: self.raise_for_index(index) - return self.plugs[index].on_since + return await self.plugs[index].get_on_since() - @property - def state_information(self) -> Dict[str, Any]: + async def get_state_information(self) -> Dict[str, Any]: """ Returns strip-specific state information. :return: Strip information dict, keys in user-presentable form. :rtype: dict """ - state = {"LED state": self.led} - is_on = self.get_is_on() - on_since = self.get_on_since() + state = {"LED state": await self.get_led()} # XXX: from where? + is_on = await self.get_is_on() + on_since = await self._get_on_since() for plug_index in range(self.num_children): plug_number = plug_index + 1 if is_on[plug_index]: @@ -230,7 +206,7 @@ class SmartStrip(SmartPlug): return state - def get_emeter_realtime(self, *, index: int = -1) -> Optional[Any]: + async def get_emeter_realtime(self, *, index: int = -1) -> Optional[Any]: """ Retrieve current energy readings from device @@ -243,19 +219,19 @@ class SmartStrip(SmartPlug): :raises SmartDeviceException: on error :raises SmartStripException: index out of bounds """ - if not self.has_emeter: # pragma: no cover + if not await self.get_has_emeter(): # pragma: no cover raise SmartStripException("Device has no emeter") if index < 0: emeter_status = {} for plug in range(self.num_children): - emeter_status[plug] = self.plugs[plug].get_emeter_realtime() + emeter_status[plug] = await self.plugs[plug].get_emeter_realtime() return emeter_status else: self.raise_for_index(index) - return self.plugs[index].get_emeter_realtime() + return await self.plugs[index].get_emeter_realtime() - def current_consumption(self, *, index: int = -1) -> Optional[Any]: + async def current_consumption(self, *, index: int = -1) -> Optional[Any]: """ Get the current power consumption in Watts. @@ -269,19 +245,19 @@ class SmartStrip(SmartPlug): :raises SmartDeviceException: on error :raises SmartStripException: index out of bounds """ - if not self.has_emeter: # pragma: no cover + if not await self.get_has_emeter(): # pragma: no cover raise SmartStripException("Device has no emeter") if index < 0: consumption = {} - emeter_reading = self.get_emeter_realtime() + emeter_reading = await self.get_emeter_realtime() for plug in range(self.num_children): response = EmeterStatus(emeter_reading[plug]) consumption[plug] = response["power"] return consumption else: self.raise_for_index(index) - response = EmeterStatus(self.get_emeter_realtime(index=index)) + response = EmeterStatus(await self.get_emeter_realtime(index=index)) return response["power"] @property @@ -291,7 +267,7 @@ class SmartStrip(SmartPlug): """ return {"icon": "SMARTSTRIP-DUMMY", "hash": "SMARTSTRIP-DUMMY"} - def get_alias(self, *, index: int = -1) -> Union[str, Dict[int, str]]: + async def get_alias(self, *, index: int = -1) -> Union[str, Dict[int, str]]: """Gets the alias for a plug. :param index: plug index (-1 for all) @@ -301,7 +277,8 @@ class SmartStrip(SmartPlug): Dict[int, str] if no index provided :raises SmartStripException: index out of bounds """ - children = self.sys_info["children"] + sys_info = await self.get_sys_info() + children = sys_info["children"] if index < 0: alias = {} @@ -312,7 +289,7 @@ class SmartStrip(SmartPlug): self.raise_for_index(index) return children[index]["alias"] - def set_alias(self, alias: str, *, index: int = -1): + async def set_alias(self, alias: str, *, index: int = -1): """Sets the alias for a plug :param index: plug index @@ -325,9 +302,9 @@ class SmartStrip(SmartPlug): return super().set_alias(alias) self.raise_for_index(index) - self.plugs[index].set_alias(alias) + await self.plugs[index].set_alias(alias) - def get_emeter_daily( + async def get_emeter_daily( self, year: int = None, month: int = None, kwh: bool = True, *, index: int = -1 ) -> Dict: """Retrieve daily statistics for a given month @@ -342,21 +319,23 @@ class SmartStrip(SmartPlug): :raises SmartDeviceException: on error :raises SmartStripException: index out of bounds """ - if not self.has_emeter: # pragma: no cover + if not await self.get_has_emeter(): # pragma: no cover raise SmartStripException("Device has no emeter") emeter_daily = {} if index < 0: for plug in range(self.num_children): - emeter_daily = self.plugs[plug].get_emeter_daily( + emeter_daily = await self.plugs[plug].get_emeter_daily( year=year, month=month, kwh=kwh ) return emeter_daily else: self.raise_for_index(index) - return self.plugs[index].get_emeter_daily(year=year, month=month, kwh=kwh) + return await self.plugs[index].get_emeter_daily( + year=year, month=month, kwh=kwh + ) - def get_emeter_monthly( + async def get_emeter_monthly( self, year: int = None, kwh: bool = True, *, index: int = -1 ) -> Dict: """Retrieve monthly statistics for a given year. @@ -369,19 +348,21 @@ class SmartStrip(SmartPlug): :raises SmartDeviceException: on error :raises SmartStripException: index out of bounds """ - if not self.has_emeter: # pragma: no cover + if not await self.get_has_emeter(): # pragma: no cover raise SmartStripException("Device has no emeter") emeter_monthly = {} if index < 0: for plug in range(self.num_children): - emeter_monthly = self.plugs[plug].get_emeter_monthly(year=year, kwh=kwh) + emeter_monthly[plug] = await self.plugs[plug].get_emeter_monthly( + year=year, kwh=kwh + ) return emeter_monthly else: self.raise_for_index(index) - return self.plugs[index].get_emeter_monthly(year=year, kwh=kwh) + return await self.plugs[index].get_emeter_monthly(year=year, kwh=kwh) - def erase_emeter_stats(self, *, index: int = -1) -> bool: + async def erase_emeter_stats(self, *, index: int = -1) -> bool: """Erase energy meter statistics :param index: plug index (-1 for all) @@ -391,15 +372,15 @@ class SmartStrip(SmartPlug): :raises SmartDeviceException: on error :raises SmartStripException: index out of bounds """ - if not self.has_emeter: # pragma: no cover + if not await self.get_has_emeter(): # pragma: no cover raise SmartStripException("Device has no emeter") if index < 0: for plug in range(self.num_children): - self.plugs[plug].erase_emeter_stats() + await self.plugs[plug].erase_emeter_stats() else: self.raise_for_index(index) - self.plugs[index].erase_emeter_stats() + await self.plugs[index].erase_emeter_stats() # As query_helper raises exception in case of failure, we have # succeeded when we are this far.