140 lines
4.0 KiB
Python
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()
|