Add Mealie service to get mealplan (#120824)
Co-authored-by: Robert Resch <robert@resch.dev>
This commit is contained in:
parent
833ac4db49
commit
f126360c67
9 changed files with 572 additions and 2 deletions
|
@ -7,15 +7,25 @@ from aiomealie import MealieAuthenticationError, MealieClient, MealieConnectionE
|
|||
from homeassistant.const import CONF_API_TOKEN, CONF_HOST, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryError, ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
from homeassistant.helpers import config_validation as cv, device_registry as dr
|
||||
from homeassistant.helpers.aiohttp_client import async_get_clientsession
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .const import DOMAIN
|
||||
from .coordinator import MealieConfigEntry, MealieCoordinator
|
||||
from .services import setup_services
|
||||
|
||||
PLATFORMS: list[Platform] = [Platform.CALENDAR]
|
||||
|
||||
CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
|
||||
"""Set up the Mealie component."""
|
||||
setup_services(hass)
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: MealieConfigEntry) -> bool:
|
||||
"""Set up Mealie from a config entry."""
|
||||
|
|
|
@ -10,7 +10,7 @@ from homeassistant.components.calendar import CalendarEntity, CalendarEvent
|
|||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import MealieConfigEntry, MealieCoordinator
|
||||
from .coordinator import MealieConfigEntry, MealieCoordinator
|
||||
from .entity import MealieEntity
|
||||
|
||||
|
||||
|
|
|
@ -5,3 +5,7 @@ import logging
|
|||
DOMAIN = "mealie"
|
||||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
ATTR_CONFIG_ENTRY_ID = "config_entry_id"
|
||||
ATTR_START_DATE = "start_date"
|
||||
ATTR_END_DATE = "end_date"
|
||||
|
|
5
homeassistant/components/mealie/icons.json
Normal file
5
homeassistant/components/mealie/icons.json
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"services": {
|
||||
"get_mealplan": "mdi:food"
|
||||
}
|
||||
}
|
69
homeassistant/components/mealie/services.py
Normal file
69
homeassistant/components/mealie/services.py
Normal file
|
@ -0,0 +1,69 @@
|
|||
"""Define services for the Mealie integration."""
|
||||
|
||||
from dataclasses import asdict
|
||||
from datetime import date
|
||||
from typing import cast
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import (
|
||||
HomeAssistant,
|
||||
ServiceCall,
|
||||
ServiceResponse,
|
||||
SupportsResponse,
|
||||
)
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
|
||||
from .const import ATTR_CONFIG_ENTRY_ID, ATTR_END_DATE, ATTR_START_DATE, DOMAIN
|
||||
from .coordinator import MealieConfigEntry
|
||||
|
||||
SERVICE_GET_MEALPLAN = "get_mealplan"
|
||||
SERVICE_GET_MEALPLAN_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_CONFIG_ENTRY_ID): str,
|
||||
vol.Optional(ATTR_START_DATE): date,
|
||||
vol.Optional(ATTR_END_DATE): date,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def setup_services(hass: HomeAssistant) -> None:
|
||||
"""Set up the services for the Mealie integration."""
|
||||
|
||||
async def async_get_mealplan(call: ServiceCall) -> ServiceResponse:
|
||||
"""Get the mealplan for a specific range."""
|
||||
if not (
|
||||
entry := hass.config_entries.async_get_entry(
|
||||
call.data[ATTR_CONFIG_ENTRY_ID]
|
||||
)
|
||||
):
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="integration_not_found",
|
||||
translation_placeholders={"target": DOMAIN},
|
||||
)
|
||||
if entry.state is not ConfigEntryState.LOADED:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="not_loaded",
|
||||
translation_placeholders={"target": entry.title},
|
||||
)
|
||||
start_date = call.data.get(ATTR_START_DATE, date.today())
|
||||
end_date = call.data.get(ATTR_END_DATE, date.today())
|
||||
if end_date < start_date:
|
||||
raise ServiceValidationError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="end_date_before_start_date",
|
||||
)
|
||||
client = cast(MealieConfigEntry, entry).runtime_data.client
|
||||
mealplans = await client.get_mealplans(start_date, end_date)
|
||||
return {"mealplan": [asdict(x) for x in mealplans.items]}
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_GET_MEALPLAN,
|
||||
async_get_mealplan,
|
||||
schema=SERVICE_GET_MEALPLAN_SCHEMA,
|
||||
supports_response=SupportsResponse.ONLY,
|
||||
)
|
13
homeassistant/components/mealie/services.yaml
Normal file
13
homeassistant/components/mealie/services.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
get_mealplan:
|
||||
fields:
|
||||
config_entry_id:
|
||||
required: true
|
||||
selector:
|
||||
config_entry:
|
||||
integration: mealie
|
||||
start_date:
|
||||
selector:
|
||||
date:
|
||||
end_date:
|
||||
selector:
|
||||
date:
|
|
@ -35,5 +35,36 @@
|
|||
"name": "Side"
|
||||
}
|
||||
}
|
||||
},
|
||||
"exceptions": {
|
||||
"not_loaded": {
|
||||
"message": "{target} is not loaded."
|
||||
},
|
||||
"integration_not_found": {
|
||||
"message": "Integration \"{target}\" not found in registry."
|
||||
},
|
||||
"end_date_before_start_date": {
|
||||
"message": "End date must be after start date."
|
||||
}
|
||||
},
|
||||
"services": {
|
||||
"get_mealplan": {
|
||||
"name": "Get mealplan",
|
||||
"description": "Get meaplan from Mealie",
|
||||
"fields": {
|
||||
"config_entry_id": {
|
||||
"name": "Mealie instance",
|
||||
"description": "Select the Mealie instance to get mealplan from"
|
||||
},
|
||||
"start_date": {
|
||||
"name": "Start date",
|
||||
"description": "The startdate of the data to get (default: today)."
|
||||
},
|
||||
"end_date": {
|
||||
"name": "End date",
|
||||
"description": "The enddate of the data to get (default: today)."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
297
tests/components/mealie/snapshots/test_services.ambr
Normal file
297
tests/components/mealie/snapshots/test_services.ambr
Normal file
|
@ -0,0 +1,297 @@
|
|||
# serializer version: 1
|
||||
# name: test_service_mealplan
|
||||
dict({
|
||||
'mealplan': list([
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 22),
|
||||
'mealplan_id': 230,
|
||||
'recipe': dict({
|
||||
'description': "Een traybake is eigenlijk altijd een goed idee. Deze zoete aardappel curry traybake dus ook. Waarom? Omdat je alleen maar wat groenten - en in dit geval kip - op een bakplaat (traybake dus) legt, hier wat kruiden aan toevoegt en deze in de oven schuift. Ideaal dus als je geen zin hebt om lang in de keuken te staan. Maar gewoon lekker op de bank wil ploffen om te wachten tot de oven klaar is. Joe! That\\'s what we like. Deze zoete aardappel curry traybake bevat behalve zoete aardappel en curry ook kikkererwten, kippendijfilet en bloemkoolroosjes. Je gebruikt yoghurt en limoen als een soort dressing. En je serveert deze heerlijke traybake met naanbrood. Je kunt natuurljk ook voor deze traybake met chipolataworstjes gaan. Wil je graag meer ovengerechten? Dan moet je eigenlijk even kijken naar onze Ovenbijbel. Onmisbaar in je keuken! We willen je deze zoete aardappelstamppot met prei ook niet onthouden. Megalekker bordje comfortfood als je \\'t ons vraagt.",
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': 'AiIo',
|
||||
'name': 'Zoete aardappel curry traybake',
|
||||
'original_url': 'https://chickslovefood.com/recept/zoete-aardappel-curry-traybake/',
|
||||
'recipe_id': 'c5f00a93-71a2-4e48-900f-d9ad0bb9de93',
|
||||
'recipe_yield': '2 servings',
|
||||
'slug': 'zoete-aardappel-curry-traybake',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.BREAKFAST: 'breakfast'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 23),
|
||||
'mealplan_id': 229,
|
||||
'recipe': dict({
|
||||
'description': 'The BEST Roast Chicken recipe is simple, budget friendly, and gives you a tender, mouth-watering chicken full of flavor! Served with roasted vegetables, this recipe is simple enough for any cook!',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': 'JeQ2',
|
||||
'name': 'Roast Chicken',
|
||||
'original_url': 'https://tastesbetterfromscratch.com/roast-chicken/',
|
||||
'recipe_id': '5b055066-d57d-4fd0-8dfd-a2c2f07b36f1',
|
||||
'recipe_yield': '6 servings',
|
||||
'slug': 'roast-chicken',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.LUNCH: 'lunch'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 23),
|
||||
'mealplan_id': 226,
|
||||
'recipe': dict({
|
||||
'description': 'Te explicamos paso a paso, de manera sencilla, la elaboración de la receta de pollo al curry con leche de coco en 10 minutos. Ingredientes, tiempo de...',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': 'INQz',
|
||||
'name': 'Receta de pollo al curry en 10 minutos (con vídeo incluido)',
|
||||
'original_url': 'https://www.directoalpaladar.com/recetas-de-carnes-y-aves/receta-de-pollo-al-curry-en-10-minutos',
|
||||
'recipe_id': 'e360a0cc-18b0-4a84-a91b-8aa59e2451c9',
|
||||
'recipe_yield': '2 servings',
|
||||
'slug': 'receta-de-pollo-al-curry-en-10-minutos-con-video-incluido',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.LUNCH: 'lunch'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 23),
|
||||
'mealplan_id': 224,
|
||||
'recipe': dict({
|
||||
'description': 'bourguignon, oignon, carotte, bouquet garni, vin rouge, beurre, sel, poivre',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': 'nj5M',
|
||||
'name': 'Boeuf bourguignon : la vraie recette (2)',
|
||||
'original_url': 'https://www.marmiton.org/recettes/recette_boeuf-bourguignon_18889.aspx',
|
||||
'recipe_id': '9c7b8aee-c93c-4b1b-ab48-2625d444743a',
|
||||
'recipe_yield': '4 servings',
|
||||
'slug': 'boeuf-bourguignon-la-vraie-recette-2',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 23),
|
||||
'mealplan_id': 222,
|
||||
'recipe': dict({
|
||||
'description': 'Εύκολη μακαρονάδα με κεφτεδάκια στον φούρνο από τον Άκη Πετρετζίκη. Φτιάξτε την πιο εύκολη μακαρονάδα με κεφτεδάκια σε μόνο ένα σκεύος.',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': 'En9o',
|
||||
'name': 'Εύκολη μακαρονάδα με κεφτεδάκια στον φούρνο (1)',
|
||||
'original_url': 'https://akispetretzikis.com/recipe/7959/efkolh-makaronada-me-keftedakia-ston-fourno',
|
||||
'recipe_id': 'f79f7e9d-4b58-4930-a586-2b127f16ee34',
|
||||
'recipe_yield': '6 servings',
|
||||
'slug': 'eukole-makaronada-me-kephtedakia-ston-phourno-1',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 23),
|
||||
'mealplan_id': 221,
|
||||
'recipe': dict({
|
||||
'description': 'Delicious Greek turkey meatballs with lemon orzo, tender veggies, and a creamy feta yogurt sauce. These healthy baked Greek turkey meatballs are filled with tons of wonderful herbs and make the perfect protein-packed weeknight meal!',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': 'Kn62',
|
||||
'name': 'Greek Turkey Meatballs with Lemon Orzo & Creamy Feta Yogurt Sauce',
|
||||
'original_url': 'https://www.ambitiouskitchen.com/greek-turkey-meatballs/',
|
||||
'recipe_id': '47595e4c-52bc-441d-b273-3edf4258806d',
|
||||
'recipe_yield': '4 servings',
|
||||
'slug': 'greek-turkey-meatballs-with-lemon-orzo-creamy-feta-yogurt-sauce',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.SIDE: 'side'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 23),
|
||||
'mealplan_id': 220,
|
||||
'recipe': dict({
|
||||
'description': 'Einfacher Nudelauflauf mit Brokkoli, Sahnesauce und extra Käse. Dieses vegetarische 5 Zutaten Rezept ist super schnell gemacht und SO gut!',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': 'nOPT',
|
||||
'name': 'Einfacher Nudelauflauf mit Brokkoli',
|
||||
'original_url': 'https://kochkarussell.com/einfacher-nudelauflauf-brokkoli/',
|
||||
'recipe_id': '9d553779-607e-471b-acf3-84e6be27b159',
|
||||
'recipe_yield': '4 servings',
|
||||
'slug': 'einfacher-nudelauflauf-mit-brokkoli',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 23),
|
||||
'mealplan_id': 219,
|
||||
'recipe': dict({
|
||||
'description': 'This is a modified Pampered Chef recipe. You can use a trifle bowl or large glass punch/salad bowl to show it off. It is really easy to make and I never have any leftovers. Cook time includes chill time.',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': 'ibL6',
|
||||
'name': 'Pampered Chef Double Chocolate Mocha Trifle',
|
||||
'original_url': 'https://www.food.com/recipe/pampered-chef-double-chocolate-mocha-trifle-74963',
|
||||
'recipe_id': '92635fd0-f2dc-4e78-a6e4-ecd556ad361f',
|
||||
'recipe_yield': '12 servings',
|
||||
'slug': 'pampered-chef-double-chocolate-mocha-trifle',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 22),
|
||||
'mealplan_id': 217,
|
||||
'recipe': dict({
|
||||
'description': 'Cheeseburger Sliders are juicy, cheesy and beefy - everything we love about classic burgers! These sliders are quick and easy plus they are make-ahead and reheat really well.',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': 'beGq',
|
||||
'name': 'Cheeseburger Sliders (Easy, 30-min Recipe)',
|
||||
'original_url': 'https://natashaskitchen.com/cheeseburger-sliders/',
|
||||
'recipe_id': '8bdd3656-5e7e-45d3-a3c4-557390846a22',
|
||||
'recipe_yield': '24 servings',
|
||||
'slug': 'cheeseburger-sliders-easy-30-min-recipe',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.LUNCH: 'lunch'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 22),
|
||||
'mealplan_id': 216,
|
||||
'recipe': dict({
|
||||
'description': 'This All-American beef stew recipe includes tender beef coated in a rich, intense sauce and vegetables that bring complementary texture and flavor.',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': '356X',
|
||||
'name': 'All-American Beef Stew Recipe',
|
||||
'original_url': 'https://www.seriouseats.com/all-american-beef-stew-recipe',
|
||||
'recipe_id': '48f39d27-4b8e-4c14-bf36-4e1e6497e75e',
|
||||
'recipe_yield': '6 servings',
|
||||
'slug': 'all-american-beef-stew-recipe',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 23),
|
||||
'mealplan_id': 212,
|
||||
'recipe': dict({
|
||||
'description': 'This All-American beef stew recipe includes tender beef coated in a rich, intense sauce and vegetables that bring complementary texture and flavor.',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': '356X',
|
||||
'name': 'All-American Beef Stew Recipe',
|
||||
'original_url': 'https://www.seriouseats.com/all-american-beef-stew-recipe',
|
||||
'recipe_id': '48f39d27-4b8e-4c14-bf36-4e1e6497e75e',
|
||||
'recipe_yield': '6 servings',
|
||||
'slug': 'all-american-beef-stew-recipe',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 22),
|
||||
'mealplan_id': 211,
|
||||
'recipe': dict({
|
||||
'description': 'Einfacher Nudelauflauf mit Brokkoli, Sahnesauce und extra Käse. Dieses vegetarische 5 Zutaten Rezept ist super schnell gemacht und SO gut!',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': 'nOPT',
|
||||
'name': 'Einfacher Nudelauflauf mit Brokkoli',
|
||||
'original_url': 'https://kochkarussell.com/einfacher-nudelauflauf-brokkoli/',
|
||||
'recipe_id': '9d553779-607e-471b-acf3-84e6be27b159',
|
||||
'recipe_yield': '4 servings',
|
||||
'slug': 'einfacher-nudelauflauf-mit-brokkoli',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 23),
|
||||
'mealplan_id': 196,
|
||||
'recipe': dict({
|
||||
'description': 'Simple to prepare and ready in 25 minutes, this vegetarian miso noodle recipe can be eaten on its own or served as a side.',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': '5G1v',
|
||||
'name': 'Miso Udon Noodles with Spinach and Tofu',
|
||||
'original_url': 'https://www.allrecipes.com/recipe/284039/miso-udon-noodles-with-spinach-and-tofu/',
|
||||
'recipe_id': '25b814f2-d9bf-4df0-b40d-d2f2457b4317',
|
||||
'recipe_yield': '2 servings',
|
||||
'slug': 'miso-udon-noodles-with-spinach-and-tofu',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': FakeDate(2024, 1, 22),
|
||||
'mealplan_id': 195,
|
||||
'recipe': dict({
|
||||
'description': 'Avis aux nostalgiques des années 1980, la mousse de saumon est de retour dans une présentation adaptée au goût du jour. On utilise une technique sans faille : un saumon frais cuit au micro-ondes et mélangé au robot avec du fromage à la crème et de la crème sure. On obtient ainsi une texture onctueuse à tartiner, qui n’a rien à envier aux préparations gélatineuses d’antan !',
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'image': 'rrNL',
|
||||
'name': 'Mousse de saumon',
|
||||
'original_url': 'https://www.ricardocuisine.com/recettes/8919-mousse-de-saumon',
|
||||
'recipe_id': '55c88810-4cf1-4d86-ae50-63b15fd173fb',
|
||||
'recipe_yield': '12 servings',
|
||||
'slug': 'mousse-de-saumon',
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
'title': None,
|
||||
'user_id': '1ce8b5fe-04e8-4b80-aab1-d92c94685c6d',
|
||||
}),
|
||||
dict({
|
||||
'description': 'Dineren met de boys',
|
||||
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
|
||||
'group_id': '3931df86-0679-4579-8c63-4bedc9ca9a85',
|
||||
'mealplan_date': FakeDate(2024, 1, 21),
|
||||
'mealplan_id': 1,
|
||||
'recipe': None,
|
||||
'title': 'Aquavite',
|
||||
'user_id': '6caa6e4d-521f-4ef4-9ed7-388bdd63f47d',
|
||||
}),
|
||||
]),
|
||||
})
|
||||
# ---
|
141
tests/components/mealie/test_services.py
Normal file
141
tests/components/mealie/test_services.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
"""Tests for the Mealie services."""
|
||||
|
||||
from datetime import date
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from syrupy import SnapshotAssertion
|
||||
|
||||
from homeassistant.components.mealie.const import (
|
||||
ATTR_CONFIG_ENTRY_ID,
|
||||
ATTR_END_DATE,
|
||||
ATTR_START_DATE,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.components.mealie.services import SERVICE_GET_MEALPLAN
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ServiceValidationError
|
||||
|
||||
from . import setup_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def test_service_mealplan(
|
||||
hass: HomeAssistant,
|
||||
mock_mealie_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test the get_mealplan service."""
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
freezer.move_to("2023-10-21")
|
||||
|
||||
response = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_MEALPLAN,
|
||||
{ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
assert mock_mealie_client.get_mealplans.call_args_list[1][0] == (
|
||||
date(2023, 10, 21),
|
||||
date(2023, 10, 21),
|
||||
)
|
||||
assert response == snapshot
|
||||
|
||||
response = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_MEALPLAN,
|
||||
{
|
||||
ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
|
||||
ATTR_START_DATE: date(2023, 10, 22),
|
||||
ATTR_END_DATE: date(2023, 10, 25),
|
||||
},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
assert response
|
||||
assert mock_mealie_client.get_mealplans.call_args_list[2][0] == (
|
||||
date(2023, 10, 22),
|
||||
date(2023, 10, 25),
|
||||
)
|
||||
|
||||
response = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_MEALPLAN,
|
||||
{
|
||||
ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
|
||||
ATTR_START_DATE: date(2023, 10, 19),
|
||||
},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
assert response
|
||||
assert mock_mealie_client.get_mealplans.call_args_list[3][0] == (
|
||||
date(2023, 10, 19),
|
||||
date(2023, 10, 21),
|
||||
)
|
||||
|
||||
response = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_MEALPLAN,
|
||||
{
|
||||
ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
|
||||
ATTR_END_DATE: date(2023, 10, 22),
|
||||
},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
assert response
|
||||
assert mock_mealie_client.get_mealplans.call_args_list[4][0] == (
|
||||
date(2023, 10, 21),
|
||||
date(2023, 10, 22),
|
||||
)
|
||||
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_MEALPLAN,
|
||||
{
|
||||
ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
|
||||
ATTR_START_DATE: date(2023, 10, 22),
|
||||
ATTR_END_DATE: date(2023, 10, 19),
|
||||
},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
|
||||
|
||||
async def test_service_mealplan_without_entry(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test the get_mealplan service without entry."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
mock_config_entry2 = MockConfigEntry(domain=DOMAIN)
|
||||
mock_config_entry2.add_to_hass(hass)
|
||||
await hass.config_entries.async_setup(mock_config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_MEALPLAN,
|
||||
{ATTR_CONFIG_ENTRY_ID: mock_config_entry2.entry_id},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
|
||||
with pytest.raises(ServiceValidationError):
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_GET_MEALPLAN,
|
||||
{ATTR_CONFIG_ENTRY_ID: "bad-config_id"},
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
Loading…
Add table
Reference in a new issue