Compare commits

...

12 Commits

Author SHA1 Message Date
Daniel Hjelseth Høyer
d1fbd2f51f Merge branch 'dev' into 168007 2026-04-15 10:58:14 +02:00
Daniel Hjelseth Høyer
30a0b5812d Update homeassistant/components/tibber/coordinator.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-13 17:12:12 +02:00
Daniel Hjelseth Høyer
7d20308fda Update homeassistant/components/tibber/coordinator.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-13 17:11:37 +02:00
Daniel Hjelseth Høyer
6fe2132e88 Update homeassistant/components/tibber/coordinator.py
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-13 17:11:14 +02:00
Daniel Hjelseth Høyer
bbcaf8fd2d Merge branch 'dev' into 168007 2026-04-13 17:00:25 +02:00
Daniel Hjelseth Høyer
442df2733a Improve data updating for Tibber, WIP
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-13 08:27:34 +02:00
Daniel Hjelseth Høyer
bb46c1c2ac Improve data updating for Tibber
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-12 21:44:04 +02:00
Daniel Hjelseth Høyer
0052055db5 Improve data updating for Tibber
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-12 21:26:30 +02:00
Daniel Hjelseth Høyer
300ed2ac1e Improve data updating for Tibber
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-12 21:15:54 +02:00
Daniel Hjelseth Høyer
58031a87cc Improve data updating for Tibber
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-12 21:00:03 +02:00
Daniel Hjelseth Høyer
a3fadbd7b9 Improve data updating for Tibber
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-12 20:47:29 +02:00
Daniel Hjelseth Høyer
525335b022 Improve data updating for Tibber
Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
2026-04-12 20:31:23 +02:00

View File

@@ -24,7 +24,9 @@ from homeassistant.components.recorder.statistics import (
statistics_during_period,
)
from homeassistant.const import UnitOfEnergy
from homeassistant.core import HomeAssistant
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from homeassistant.util import dt as dt_util
from homeassistant.util.unit_conversion import EnergyConverter
@@ -292,7 +294,41 @@ class TibberPriceCoordinator(TibberCoordinator[dict[str, TibberHomeData]]):
name=f"{DOMAIN} price",
update_interval=timedelta(minutes=1),
)
self._tomorrow_price_poll_threshold_seconds = random.uniform(0, 3600 * 10)
self._tomorrow_price_poll_threshold_seconds = random.uniform(
3600 * 14, 3600 * 23
)
self._unsub_tomorrow_price_poll: CALLBACK_TYPE | None = None
initial_tomorrow_price_poll = dt_util.start_of_local_day() + timedelta(
seconds=self._tomorrow_price_poll_threshold_seconds
)
if initial_tomorrow_price_poll <= dt_util.utcnow():
initial_tomorrow_price_poll += timedelta(days=1)
self._schedule_tomorrow_price_poll(initial_tomorrow_price_poll)
self._tibber_homes: list[tibber.TibberHome] | None = None
async def async_shutdown(self) -> None:
"""Cancel any scheduled call, and ignore new runs."""
await super().async_shutdown()
if self._unsub_tomorrow_price_poll:
self._unsub_tomorrow_price_poll()
self._unsub_tomorrow_price_poll = None
def _schedule_tomorrow_price_poll(self, point_in_time: datetime) -> None:
"""Schedule the next one-shot tomorrow price poll."""
if point_in_time <= (now := dt_util.utcnow()):
point_in_time = now + timedelta(seconds=1)
if self._unsub_tomorrow_price_poll:
self._unsub_tomorrow_price_poll()
self._unsub_tomorrow_price_poll = async_track_point_in_utc_time(
self.hass,
self._async_handle_tomorrow_price_poll,
point_in_time,
)
async def _async_handle_tomorrow_price_poll(self, _: datetime) -> None:
"""Handle the scheduled tomorrow price poll."""
self._unsub_tomorrow_price_poll = None
await self._fetch_data()
def _time_until_next_15_minute(self) -> timedelta:
"""Return time until the next 15-minute boundary (0, 15, 30, 45) in UTC."""
@@ -309,7 +345,23 @@ class TibberPriceCoordinator(TibberCoordinator[dict[str, TibberHomeData]]):
return next_run - now
async def _async_update_data(self) -> dict[str, TibberHomeData]:
"""Update data via API and return per-home data for sensors."""
if self._tibber_homes is None:
await self._fetch_data()
homes = self._tibber_homes
if homes is None:
raise UpdateFailed("No Tibber homes available")
result = {home.home_id: _build_home_data(home) for home in homes}
self.update_interval = self._time_until_next_15_minute()
return result
async def _fetch_data(self) -> None:
"""Fetch latest price data via API and update cached home data."""
self._schedule_tomorrow_price_poll(
dt_util.utcnow() + timedelta(seconds=random.uniform(60, 60 * 10))
)
tibber_connection = await self._async_get_client()
active_homes = tibber_connection.get_homes(only_active=True)
@@ -348,21 +400,31 @@ class TibberPriceCoordinator(TibberCoordinator[dict[str, TibberHomeData]]):
return False
homes_to_update = [home for home in active_homes if _needs_update(home)]
try:
if homes_to_update:
await asyncio.gather(
*(home.update_info_and_price_info() for home in homes_to_update)
)
except tibber.RetryableHttpExceptionError as err:
raise UpdateFailed(f"Error communicating with API ({err.status})") from err
except tibber.FatalHttpExceptionError as err:
raise UpdateFailed(f"Error communicating with API ({err.status})") from err
except tibber.exceptions.RateLimitExceededError as err:
self._schedule_tomorrow_price_poll(
dt_util.utcnow() + timedelta(seconds=err.retry_after)
)
raise UpdateFailed(
f"Rate limit exceeded, retry after {err.retry_after} seconds",
retry_after=err.retry_after,
) from err
except tibber.exceptions.HttpExceptionError as err:
raise UpdateFailed(f"Error communicating with API ({err})") from err
result = {home.home_id: _build_home_data(home) for home in active_homes}
self._schedule_tomorrow_price_poll(
dt_util.start_of_local_day(now)
+ timedelta(days=1, seconds=self._tomorrow_price_poll_threshold_seconds)
)
self._tibber_homes = active_homes
self.update_interval = self._time_until_next_15_minute()
return result
class TibberFetchPriceCoordinator(TibberPriceCoordinator):
"""Backward-compatible alias for the merged price fetch coordinator."""
class TibberDataAPICoordinator(TibberCoordinator[dict[str, TibberDevice]]):