core/homeassistant/components/mealie/coordinator.py

140 lines
4.0 KiB
Python

"""Define an object to manage fetching Mealie data."""
from __future__ import annotations
from abc import abstractmethod
from dataclasses import dataclass
from datetime import timedelta
from aiomealie import (
MealieAuthenticationError,
MealieClient,
MealieConnectionError,
Mealplan,
MealplanEntryType,
ShoppingItem,
ShoppingList,
Statistics,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
import homeassistant.util.dt as dt_util
from .const import LOGGER
WEEK = timedelta(days=7)
@dataclass
class MealieData:
"""Mealie data type."""
client: MealieClient
mealplan_coordinator: MealieMealplanCoordinator
shoppinglist_coordinator: MealieShoppingListCoordinator
statistics_coordinator: MealieStatisticsCoordinator
type MealieConfigEntry = ConfigEntry[MealieData]
class MealieDataUpdateCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
"""Base coordinator."""
config_entry: MealieConfigEntry
_name: str
_update_interval: timedelta
def __init__(self, hass: HomeAssistant, client: MealieClient) -> None:
"""Initialize the Mealie data coordinator."""
super().__init__(
hass,
LOGGER,
name=self._name,
update_interval=self._update_interval,
)
self.client = client
async def _async_update_data(self) -> _DataT:
"""Fetch data from Mealie."""
try:
return await self._async_update_internal()
except MealieAuthenticationError as error:
raise ConfigEntryAuthFailed from error
except MealieConnectionError as error:
raise UpdateFailed(error) from error
@abstractmethod
async def _async_update_internal(self) -> _DataT:
"""Fetch data from Mealie."""
class MealieMealplanCoordinator(
MealieDataUpdateCoordinator[dict[MealplanEntryType, list[Mealplan]]]
):
"""Class to manage fetching Mealie data."""
_name = "MealieMealplan"
_update_interval = timedelta(hours=1)
async def _async_update_internal(self) -> dict[MealplanEntryType, list[Mealplan]]:
next_week = dt_util.now() + WEEK
current_date = dt_util.now().date()
next_week_date = next_week.date()
response = await self.client.get_mealplans(current_date, next_week_date)
res: dict[MealplanEntryType, list[Mealplan]] = {
type_: [] for type_ in MealplanEntryType
}
for meal in response.items:
res[meal.entry_type].append(meal)
return res
@dataclass
class ShoppingListData:
"""Data class for shopping list data."""
shopping_list: ShoppingList
items: list[ShoppingItem]
class MealieShoppingListCoordinator(
MealieDataUpdateCoordinator[dict[str, ShoppingListData]]
):
"""Class to manage fetching Mealie Shopping list data."""
_name = "MealieShoppingList"
_update_interval = timedelta(minutes=5)
async def _async_update_internal(
self,
) -> dict[str, ShoppingListData]:
shopping_list_items = {}
shopping_lists = (await self.client.get_shopping_lists()).items
for shopping_list in shopping_lists:
shopping_list_id = shopping_list.list_id
shopping_items = (
await self.client.get_shopping_items(shopping_list_id)
).items
shopping_list_items[shopping_list_id] = ShoppingListData(
shopping_list=shopping_list, items=shopping_items
)
return shopping_list_items
class MealieStatisticsCoordinator(MealieDataUpdateCoordinator[Statistics]):
"""Class to manage fetching Mealie Statistics data."""
_name = "MealieStatistics"
_update_interval = timedelta(minutes=15)
async def _async_update_internal(
self,
) -> Statistics:
return await self.client.get_statistics()