tibber refactor

Signed-off-by: Daniel Hjelseth Høyer <github@dahoiv.net>
This commit is contained in:
Daniel Hjelseth Høyer
2026-02-15 15:18:07 +01:00
parent e42195bfed
commit 02e5f2c234
3 changed files with 40 additions and 18 deletions

View File

@@ -2,7 +2,7 @@
from __future__ import annotations
from dataclasses import dataclass, field
from dataclasses import dataclass
import logging
import aiohttp
@@ -38,8 +38,8 @@ class TibberRuntimeData:
"""Runtime data for Tibber API entries."""
session: OAuth2Session
data_api_coordinator: TibberDataAPICoordinator | None = field(default=None)
data_coordinator: TibberDataCoordinator | None = field(default=None)
data_api_coordinator: TibberDataAPICoordinator
data_coordinator: TibberDataCoordinator
_client: tibber.Tibber | None = None
async def async_get_client(self, hass: HomeAssistant) -> tibber.Tibber:
@@ -101,8 +101,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: TibberConfigEntry) -> bo
except ClientError as err:
raise ConfigEntryNotReady from err
data_api_coordinator = TibberDataAPICoordinator(hass, entry)
await data_api_coordinator.async_config_entry_first_refresh()
data_coordinator = TibberDataCoordinator(hass, entry, entry.runtime_data)
await data_coordinator.async_config_entry_first_refresh()
await data_coordinator.update_listeners(dt_util.utcnow())
entry.runtime_data = TibberRuntimeData(
session=session,
data_api_coordinator=data_api_coordinator,
data_coordinator=data_coordinator,
)
tibber_connection = await entry.runtime_data.async_get_client(hass)
@@ -125,14 +132,6 @@ async def async_setup_entry(hass: HomeAssistant, entry: TibberConfigEntry) -> bo
except tibber.FatalHttpExceptionError as err:
raise ConfigEntryNotReady("Fatal HTTP error from Tibber API") from err
data_api_coordinator = TibberDataAPICoordinator(hass, entry)
await data_api_coordinator.async_config_entry_first_refresh()
entry.runtime_data.data_api_coordinator = data_api_coordinator
data_coordinator = TibberDataCoordinator(hass, entry, entry.runtime_data)
await data_coordinator.async_config_entry_first_refresh()
entry.runtime_data.data_coordinator = data_coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True

View File

@@ -27,6 +27,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EVENT_HOMEASSISTANT_STOP, UnitOfEnergy
from homeassistant.core import Event, HomeAssistant, callback
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
@@ -109,9 +110,33 @@ class TibberDataCoordinator(DataUpdateCoordinator[dict[str, TibberHomeData]]):
_LOGGER,
config_entry=config_entry,
name="Tibber",
update_interval=timedelta(minutes=15),
)
self._runtime_data = runtime_data
self._listener_unsub: Callable[[], None] | None = None
def _get_next_15_interval(self, now: datetime) -> datetime:
"""Compute next time we need to notify listeners (minutes 0, 15, 30, 45)."""
next_run = dt_util.utcnow() + timedelta(minutes=15)
next_minute = next_run.minute // 15 * 15
return next_run.replace(
minute=next_minute, second=0, microsecond=0, tzinfo=dt_util.UTC
)
async def async_shutdown(self) -> None:
"""Cancel any scheduled listener updates."""
await super().async_shutdown()
if self._listener_unsub:
self._listener_unsub()
self._listener_unsub = None
async def update_listeners(self, now: datetime) -> None:
"""Notify listeners at 15-min boundaries."""
self._listener_unsub = async_track_point_in_utc_time(
self.hass,
self.update_listeners,
self._get_next_15_interval(now),
)
self.async_update_listeners()
async def _async_update_data(self) -> dict[str, TibberHomeData]:
"""Update data via API and return per-home data for sensors."""

View File

@@ -113,7 +113,7 @@ RT_SENSORS: tuple[SensorEntityDescription, ...] = (
translation_key="accumulated_consumption",
device_class=SensorDeviceClass.ENERGY,
native_unit_of_measurement=UnitOfEnergy.KILO_WATT_HOUR,
state_class=SensorStateClass.TOTAL_INCREASING,
state_class=SensorStateClass.TOTAL,
),
SensorEntityDescription(
key="accumulatedConsumptionLastHour",
@@ -641,14 +641,14 @@ async def _async_setup_graphql_sensors(
entry: TibberConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Tibber GraphQL-based sensors."""
"""Set up the Tibber sensor."""
tibber_connection = await entry.runtime_data.async_get_client(hass)
entity_registry = er.async_get(hass)
coordinator: TibberDataCoordinator = entry.runtime_data.data_coordinator
entities: list[TibberSensor] = []
coordinator = entry.runtime_data.data_coordinator
for home in tibber_connection.get_homes(only_active=False):
try:
await home.update_info()
@@ -663,7 +663,7 @@ async def _async_setup_graphql_sensors(
_LOGGER.error("Error connecting to Tibber home: %s ", err)
raise PlatformNotReady from err
if coordinator is not None and home.has_active_subscription:
if home.has_active_subscription:
entities.extend(TibberSensor(home, coordinator, desc) for desc in SENSORS)
if home.has_real_time_consumption:
@@ -689,8 +689,6 @@ def _setup_data_api_sensors(
"""Set up sensors backed by the Tibber Data API."""
coordinator = entry.runtime_data.data_api_coordinator
if coordinator is None:
return
entities: list[TibberDataAPISensor] = []
api_sensors = {sensor.key: sensor for sensor in DATA_API_SENSORS}