Parse features only during updates (#527)

Every time emeter functions were called features had to be re-parsed. For power strips, thats a lot of re-parses. Only parse them when we update.
This commit is contained in:
J. Nick Koston 2023-10-07 09:18:47 -10:00 committed by GitHub
parent 0ec0826cc7
commit 9930311b54
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 25 additions and 11 deletions

View File

@ -92,6 +92,12 @@ def requires_update(f):
return wrapped return wrapped
@functools.lru_cache
def _parse_features(features: str) -> Set[str]:
"""Parse features string."""
return set(features.split(":"))
class SmartDevice: class SmartDevice:
"""Base class for all supported device types. """Base class for all supported device types.
@ -213,6 +219,7 @@ class SmartDevice:
# are not accessed incorrectly. # are not accessed incorrectly.
self._last_update: Any = None self._last_update: Any = None
self._sys_info: Any = None # TODO: this is here to avoid changing tests self._sys_info: Any = None # TODO: this is here to avoid changing tests
self._features: Set[str] = set()
self.modules: Dict[str, Any] = {} self.modules: Dict[str, Any] = {}
self.children: List["SmartDevice"] = [] self.children: List["SmartDevice"] = []
@ -284,11 +291,7 @@ class SmartDevice:
@requires_update @requires_update
def features(self) -> Set[str]: def features(self) -> Set[str]:
"""Return a set of features that the device supports.""" """Return a set of features that the device supports."""
try: return self._features
return set(self.sys_info["feature"].split(":"))
except KeyError:
_LOGGER.debug("Device does not have feature information")
return set()
@property # type: ignore @property # type: ignore
@requires_update @requires_update
@ -321,11 +324,12 @@ class SmartDevice:
# See #105, #120, #161 # See #105, #120, #161
if self._last_update is None: if self._last_update is None:
_LOGGER.debug("Performing the initial update to obtain sysinfo") _LOGGER.debug("Performing the initial update to obtain sysinfo")
self._last_update = await self.protocol.query(req) response = await self.protocol.query(req)
self._sys_info = self._last_update["system"]["get_sysinfo"] self._last_update = response
self._set_sys_info(response["system"]["get_sysinfo"])
await self._modular_update(req) await self._modular_update(req)
self._sys_info = self._last_update["system"]["get_sysinfo"] self._set_sys_info(self._last_update["system"]["get_sysinfo"])
async def _modular_update(self, req: dict) -> None: async def _modular_update(self, req: dict) -> None:
"""Execute an update query.""" """Execute an update query."""
@ -366,10 +370,18 @@ class SmartDevice:
update = {**update, **response} update = {**update, **response}
self._last_update = update self._last_update = update
def update_from_discover_info(self, info): def update_from_discover_info(self, info: Dict[str, Any]) -> None:
"""Update state from info from the discover call.""" """Update state from info from the discover call."""
self._last_update = info self._last_update = info
self._sys_info = info["system"]["get_sysinfo"] self._set_sys_info(info["system"]["get_sysinfo"])
def _set_sys_info(self, sys_info: Dict[str, Any]) -> None:
"""Set sys_info."""
self._sys_info = sys_info
if features := sys_info.get("feature"):
self._features = _parse_features(features)
else:
self._features = set()
@property # type: ignore @property # type: ignore
@requires_update @requires_update

View File

@ -259,7 +259,7 @@ class SmartStripPlug(SmartPlug):
self.parent = parent self.parent = parent
self.child_id = child_id self.child_id = child_id
self._last_update = parent._last_update self._last_update = parent._last_update
self._sys_info = parent._sys_info self._set_sys_info(parent.sys_info)
self._device_type = DeviceType.StripSocket self._device_type = DeviceType.StripSocket
self.modules = {} self.modules = {}
self.protocol = parent.protocol # Must use the same connection as the parent self.protocol = parent.protocol # Must use the same connection as the parent

View File

@ -37,6 +37,7 @@ async def test_invalid_connection(dev):
async def test_initial_update_emeter(dev, mocker): async def test_initial_update_emeter(dev, mocker):
"""Test that the initial update performs second query if emeter is available.""" """Test that the initial update performs second query if emeter is available."""
dev._last_update = None dev._last_update = None
dev._features = set()
spy = mocker.spy(dev.protocol, "query") spy = mocker.spy(dev.protocol, "query")
await dev.update() await dev.update()
# Devices with small buffers may require 3 queries # Devices with small buffers may require 3 queries
@ -48,6 +49,7 @@ async def test_initial_update_emeter(dev, mocker):
async def test_initial_update_no_emeter(dev, mocker): async def test_initial_update_no_emeter(dev, mocker):
"""Test that the initial update performs second query if emeter is available.""" """Test that the initial update performs second query if emeter is available."""
dev._last_update = None dev._last_update = None
dev._features = set()
spy = mocker.spy(dev.protocol, "query") spy = mocker.spy(dev.protocol, "query")
await dev.update() await dev.update()
# 2 calls are necessary as some devices crash on unexpected modules # 2 calls are necessary as some devices crash on unexpected modules