Skip to content

Commit 6cba9da

Browse files
committed
introduce time_cached for low-level APIs
1 parent 88f4f01 commit 6cba9da

2 files changed

Lines changed: 41 additions & 1 deletion

File tree

franklinwh/client.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
"""
66

77
from __future__ import annotations
8-
from collections.abc import Callable
98

109
import asyncio
10+
from collections.abc import Callable
1111
from dataclasses import dataclass
1212
from enum import Enum
1313
import hashlib
@@ -19,6 +19,7 @@
1919
import httpx
2020

2121
from .api import DEFAULT_URL_BASE
22+
from .time_cached import time_cached
2223

2324

2425
class AccessoryType(Enum):
@@ -599,19 +600,22 @@ def set_value(keys, value):
599600
return json.loads(data)
600601

601602
# Sends a 203 which is a high level status
603+
@time_cached()
602604
async def _status(self):
603605
payload = self._build_payload(203, {"opt": 1, "refreshData": 1})
604606
data = (await self._mqtt_send(payload))["result"]["dataArea"]
605607
return json.loads(data)
606608

607609
# Sends a 311 which appears to be a more specific switch command
610+
@time_cached()
608611
async def _switch_status(self):
609612
payload = self._build_payload(311, {"opt": 0, "order": self.gateway})
610613
data = (await self._mqtt_send(payload))["result"]["dataArea"]
611614
return json.loads(data)
612615

613616
# Sends a 353 which grabs real-time smart-circuit load information
614617
# https://github.com/richo/homeassistant-franklinwh/issues/27#issuecomment-2714422732
618+
@time_cached()
615619
async def _switch_usage(self):
616620
payload = self._build_payload(353, {"opt": 0, "order": self.gateway})
617621
data = (await self._mqtt_send(payload))["result"]["dataArea"]
@@ -739,6 +743,7 @@ async def set_grid_status(self, status: GridStatus, soc: int = 5):
739743
}
740744
await self._post(url, json.dumps(payload))
741745

746+
@time_cached()
742747
async def get_composite_info(self):
743748
"""Get composite information about the FranklinWH gateway."""
744749
url = self.url_base + "hes-gateway/terminal/getDeviceCompositeInfo"

franklinwh/time_cached.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""Cache a function result for a specified time.
2+
3+
This design provides a locked cache PER DECORATOR INSTANCE so SHOULD apply
4+
only to functions that are definitely called periodically, otherwise it may
5+
cache arguments and results indefinitely, think 'self' for member functions.
6+
"""
7+
8+
import asyncio
9+
from datetime import datetime, timedelta
10+
from functools import wraps
11+
12+
13+
def time_cached(ttl: timedelta = timedelta(seconds=2)):
14+
"""Decorator to cache function results for a specified time-to-live (TTL)."""
15+
16+
def wrapper(func):
17+
__cache = {}
18+
__lock = asyncio.Lock()
19+
20+
@wraps(func)
21+
async def wrapped(*args, **kwargs):
22+
async with __lock:
23+
now = datetime.now()
24+
for key, value in __cache.copy().items():
25+
if now > value[0]:
26+
del __cache[key]
27+
key = (args, frozenset(kwargs.items()))
28+
if key not in __cache:
29+
__cache[key] = (now + ttl, await func(*args, **kwargs))
30+
return __cache[key][1]
31+
32+
setattr(wrapped, "clear", __cache.clear)
33+
return wrapped
34+
35+
return wrapper

0 commit comments

Comments
 (0)