make SmartStrip use asyncio

This commit is contained in:
Bas Nijholt 2019-11-11 17:55:56 +01:00
parent b6a0542f43
commit ba2600cb93

View File

@ -1,9 +1,11 @@
import asyncio
import datetime import datetime
import logging import logging
from typing import Any, Dict, Optional, Union 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 from .protocol import TPLinkSmartHomeProtocol
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@ -42,7 +44,9 @@ class SmartStrip(SmartPlug):
self.emeter_type = "emeter" self.emeter_type = "emeter"
self._device_type = DeviceType.Strip self._device_type = DeviceType.Strip
self.plugs = {} self.plugs = {}
children = self.sys_info["children"]
sys_info = asyncio.run(self.get_sys_info())
children = sys_info["children"]
self.num_children = len(children) self.num_children = len(children)
for plug in range(self.num_children): for plug in range(self.num_children):
self.plugs[plug] = SmartPlug( self.plugs[plug] = SmartPlug(
@ -59,14 +63,7 @@ class SmartStrip(SmartPlug):
if index not in range(self.num_children): if index not in range(self.num_children):
raise SmartStripException("plug index of %d " "is out of bounds" % index) raise SmartStripException("plug index of %d " "is out of bounds" % index)
@property async def get_state(self, *, index=-1) -> Dict[int, str]:
@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]:
"""Retrieve the switch state """Retrieve the switch state
:returns: list with the state of each child plug :returns: list with the state of each child plug
@ -75,36 +72,14 @@ class SmartStrip(SmartPlug):
:rtype: dict :rtype: dict
""" """
def _state_for_bool(b): def _state_for_bool(_bool):
return SmartPlug.STATE_ON if b else SmartPlug.STATE_OFF 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): if isinstance(is_on, bool):
return _state_for_bool(is_on) return _state_for_bool(is_on)
print(is_on) return {k: _state_for_bool(v) for k, v in is_on.items()}
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)
def set_state(self, value: str, *, index: int = -1): def set_state(self, value: str, *, index: int = -1):
"""Sets the state of a plug on the strip """Sets the state of a plug on the strip
@ -123,12 +98,12 @@ class SmartStrip(SmartPlug):
self.raise_for_index(index) self.raise_for_index(index)
self.plugs[index].state = value self.plugs[index].state = value
@property async def is_on(self) -> bool:
def is_on(self) -> bool:
"""Return if any of the outlets are on""" """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. Returns whether device is on.
@ -138,7 +113,8 @@ class SmartStrip(SmartPlug):
Dict[int, bool] if no index provided Dict[int, bool] if no index provided
:raises SmartStripException: index out of bounds :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: if index < 0:
is_on = {} is_on = {}
for plug in range(self.num_children): for plug in range(self.num_children):
@ -148,14 +124,14 @@ class SmartStrip(SmartPlug):
self.raise_for_index(index) self.raise_for_index(index)
return bool(children[index]["state"]) return bool(children[index]["state"])
def get_is_off(self, *, index: int = -1) -> Any: async def get_is_off(self, *, index: int = -1) -> Any:
is_on = self.get_is_on(index=index) is_on = await self.get_is_on(index=index)
if isinstance(is_on, bool): if isinstance(is_on, bool):
return not is_on return not is_on
else: else:
return {k: not v for k, v in is_on} 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 Turns outlets on
@ -164,12 +140,12 @@ class SmartStrip(SmartPlug):
:raises SmartStripException: index out of bounds :raises SmartStripException: index out of bounds
""" """
if index < 0: if index < 0:
self._query_helper("system", "set_relay_state", {"state": 1}) await self._query_helper("system", "set_relay_state", {"state": 1})
else: else:
self.raise_for_index(index) 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 Turns outlets off
@ -178,17 +154,17 @@ class SmartStrip(SmartPlug):
:raises SmartStripException: index out of bounds :raises SmartStripException: index out of bounds
""" """
if index < 0: if index < 0:
self._query_helper("system", "set_relay_state", {"state": 0}) await self._query_helper("system", "set_relay_state", {"state": 0})
else: else:
self.raise_for_index(index) self.raise_for_index(index)
self.plugs[index].turn_off() await self.plugs[index].turn_off()
@property async def get_on_since(self) -> datetime:
def on_since(self) -> datetime:
"""Returns the maximum on-time of all outlets.""" """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 Returns pretty-printed on-time
@ -200,7 +176,8 @@ class SmartStrip(SmartPlug):
""" """
if index < 0: if index < 0:
on_since = {} 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): for plug in range(self.num_children):
child_ontime = children[plug]["on_time"] child_ontime = children[plug]["on_time"]
@ -210,19 +187,18 @@ class SmartStrip(SmartPlug):
return on_since return on_since
else: else:
self.raise_for_index(index) self.raise_for_index(index)
return self.plugs[index].on_since return await self.plugs[index].get_on_since()
@property async def get_state_information(self) -> Dict[str, Any]:
def state_information(self) -> Dict[str, Any]:
""" """
Returns strip-specific state information. Returns strip-specific state information.
:return: Strip information dict, keys in user-presentable form. :return: Strip information dict, keys in user-presentable form.
:rtype: dict :rtype: dict
""" """
state = {"LED state": self.led} state = {"LED state": await self.get_led()} # XXX: from where?
is_on = self.get_is_on() is_on = await self.get_is_on()
on_since = self.get_on_since() on_since = await self._get_on_since()
for plug_index in range(self.num_children): for plug_index in range(self.num_children):
plug_number = plug_index + 1 plug_number = plug_index + 1
if is_on[plug_index]: if is_on[plug_index]:
@ -230,7 +206,7 @@ class SmartStrip(SmartPlug):
return state 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 Retrieve current energy readings from device
@ -243,19 +219,19 @@ class SmartStrip(SmartPlug):
:raises SmartDeviceException: on error :raises SmartDeviceException: on error
:raises SmartStripException: index out of bounds :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") raise SmartStripException("Device has no emeter")
if index < 0: if index < 0:
emeter_status = {} emeter_status = {}
for plug in range(self.num_children): 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 return emeter_status
else: else:
self.raise_for_index(index) 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. Get the current power consumption in Watts.
@ -269,19 +245,19 @@ class SmartStrip(SmartPlug):
:raises SmartDeviceException: on error :raises SmartDeviceException: on error
:raises SmartStripException: index out of bounds :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") raise SmartStripException("Device has no emeter")
if index < 0: if index < 0:
consumption = {} consumption = {}
emeter_reading = self.get_emeter_realtime() emeter_reading = await self.get_emeter_realtime()
for plug in range(self.num_children): for plug in range(self.num_children):
response = EmeterStatus(emeter_reading[plug]) response = EmeterStatus(emeter_reading[plug])
consumption[plug] = response["power"] consumption[plug] = response["power"]
return consumption return consumption
else: else:
self.raise_for_index(index) 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"] return response["power"]
@property @property
@ -291,7 +267,7 @@ class SmartStrip(SmartPlug):
""" """
return {"icon": "SMARTSTRIP-DUMMY", "hash": "SMARTSTRIP-DUMMY"} 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. """Gets the alias for a plug.
:param index: plug index (-1 for all) :param index: plug index (-1 for all)
@ -301,7 +277,8 @@ class SmartStrip(SmartPlug):
Dict[int, str] if no index provided Dict[int, str] if no index provided
:raises SmartStripException: index out of bounds :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: if index < 0:
alias = {} alias = {}
@ -312,7 +289,7 @@ class SmartStrip(SmartPlug):
self.raise_for_index(index) self.raise_for_index(index)
return children[index]["alias"] 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 """Sets the alias for a plug
:param index: plug index :param index: plug index
@ -325,9 +302,9 @@ class SmartStrip(SmartPlug):
return super().set_alias(alias) return super().set_alias(alias)
self.raise_for_index(index) 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 self, year: int = None, month: int = None, kwh: bool = True, *, index: int = -1
) -> Dict: ) -> Dict:
"""Retrieve daily statistics for a given month """Retrieve daily statistics for a given month
@ -342,21 +319,23 @@ class SmartStrip(SmartPlug):
:raises SmartDeviceException: on error :raises SmartDeviceException: on error
:raises SmartStripException: index out of bounds :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") raise SmartStripException("Device has no emeter")
emeter_daily = {} emeter_daily = {}
if index < 0: if index < 0:
for plug in range(self.num_children): 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 year=year, month=month, kwh=kwh
) )
return emeter_daily return emeter_daily
else: else:
self.raise_for_index(index) 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 self, year: int = None, kwh: bool = True, *, index: int = -1
) -> Dict: ) -> Dict:
"""Retrieve monthly statistics for a given year. """Retrieve monthly statistics for a given year.
@ -369,19 +348,21 @@ class SmartStrip(SmartPlug):
:raises SmartDeviceException: on error :raises SmartDeviceException: on error
:raises SmartStripException: index out of bounds :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") raise SmartStripException("Device has no emeter")
emeter_monthly = {} emeter_monthly = {}
if index < 0: if index < 0:
for plug in range(self.num_children): 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 return emeter_monthly
else: else:
self.raise_for_index(index) 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 """Erase energy meter statistics
:param index: plug index (-1 for all) :param index: plug index (-1 for all)
@ -391,15 +372,15 @@ class SmartStrip(SmartPlug):
:raises SmartDeviceException: on error :raises SmartDeviceException: on error
:raises SmartStripException: index out of bounds :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") raise SmartStripException("Device has no emeter")
if index < 0: if index < 0:
for plug in range(self.num_children): for plug in range(self.num_children):
self.plugs[plug].erase_emeter_stats() await self.plugs[plug].erase_emeter_stats()
else: else:
self.raise_for_index(index) 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 # As query_helper raises exception in case of failure, we have
# succeeded when we are this far. # succeeded when we are this far.