Create update_coordinator.py
This commit is contained in:
112
custom_components/pstryk/update_coordinator.py
Normal file
112
custom_components/pstryk/update_coordinator.py
Normal file
@ -0,0 +1,112 @@
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
import aiohttp
|
||||
import async_timeout
|
||||
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
|
||||
from homeassistant.helpers.event import async_track_point_in_time
|
||||
from homeassistant.util import dt as dt_util
|
||||
from .const import API_URL, API_TIMEOUT, BUY_ENDPOINT, SELL_ENDPOINT, DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
def convert_price(value):
|
||||
try:
|
||||
return round(float(str(value).replace(",", ".").strip()), 2)
|
||||
except (ValueError, TypeError) as e:
|
||||
_LOGGER.warning("Price conversion error: %s", e)
|
||||
return None
|
||||
|
||||
class PstrykDataUpdateCoordinator(DataUpdateCoordinator):
|
||||
def __init__(self, hass, api_key, price_type):
|
||||
"""Coordinator to fetch both current price and today's table."""
|
||||
self.hass = hass
|
||||
self.api_key = api_key
|
||||
self.price_type = price_type
|
||||
self._unsub_hourly = None
|
||||
self._unsub_midnight = None
|
||||
|
||||
super().__init__(
|
||||
hass,
|
||||
_LOGGER,
|
||||
name=f"{DOMAIN}_{price_type}",
|
||||
update_interval=None, # we'll schedule manually
|
||||
)
|
||||
|
||||
async def _async_update_data(self):
|
||||
"""Fetch 48h of frames and extract current + today's list."""
|
||||
today_local = dt_util.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
window_end_local = today_local + timedelta(days=2)
|
||||
start_utc = dt_util.as_utc(today_local).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
end_utc = dt_util.as_utc(window_end_local).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
endpoint_tpl = BUY_ENDPOINT if self.price_type == "buy" else SELL_ENDPOINT
|
||||
endpoint = endpoint_tpl.format(start=start_utc, end=end_utc)
|
||||
|
||||
try:
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with async_timeout.timeout(API_TIMEOUT):
|
||||
resp = await session.get(
|
||||
f"{API_URL}{endpoint}",
|
||||
headers={"Authorization": self.api_key, "Accept": "application/json"}
|
||||
)
|
||||
if resp.status != 200:
|
||||
raise UpdateFailed(f"API error {resp.status}")
|
||||
data = await resp.json()
|
||||
|
||||
frames = data.get("frames", [])
|
||||
now_utc = dt_util.utcnow()
|
||||
prices = []
|
||||
current_price = None
|
||||
|
||||
for f in frames:
|
||||
val = convert_price(f.get("price_gross"))
|
||||
if val is None:
|
||||
continue
|
||||
start = dt_util.parse_datetime(f["start"])
|
||||
end = dt_util.parse_datetime(f["end"])
|
||||
local_start = dt_util.as_local(start).strftime("%Y-%m-%dT%H:%M:%S")
|
||||
prices.append({"start": local_start, "price": val})
|
||||
if start <= now_utc < end:
|
||||
current_price = val
|
||||
|
||||
# only today's entries
|
||||
today_str = today_local.strftime("%Y-%m-%d")
|
||||
prices_today = [p for p in prices if p["start"].startswith(today_str)]
|
||||
|
||||
return {
|
||||
"prices_today": prices_today,
|
||||
"prices": prices,
|
||||
"current": current_price,
|
||||
}
|
||||
|
||||
except Exception as err:
|
||||
raise UpdateFailed(err)
|
||||
|
||||
def schedule_hourly_update(self):
|
||||
"""Schedule next refresh 1 min after each full hour."""
|
||||
if self._unsub_hourly:
|
||||
self._unsub_hourly()
|
||||
now = dt_util.now()
|
||||
next_run = (now.replace(minute=0, second=0, microsecond=0)
|
||||
+ timedelta(hours=1, minutes=1))
|
||||
self._unsub_hourly = async_track_point_in_time(
|
||||
self.hass, self._handle_hourly_update, dt_util.as_utc(next_run)
|
||||
)
|
||||
|
||||
async def _handle_hourly_update(self, _):
|
||||
await self.async_request_refresh()
|
||||
self.schedule_hourly_update()
|
||||
|
||||
def schedule_midnight_update(self):
|
||||
"""Schedule next refresh 1 min after local midnight."""
|
||||
if self._unsub_midnight:
|
||||
self._unsub_midnight()
|
||||
now = dt_util.now()
|
||||
next_mid = (now + timedelta(days=1)).replace(hour=0, minute=1, second=0, microsecond=0)
|
||||
self._unsub_midnight = async_track_point_in_time(
|
||||
self.hass, self._handle_midnight_update, dt_util.as_utc(next_mid)
|
||||
)
|
||||
|
||||
async def _handle_midnight_update(self, _):
|
||||
await self.async_request_refresh()
|
||||
self.schedule_midnight_update()
|
||||
Reference in New Issue
Block a user