mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Add mealie service to set mealplan (#122317)
This commit is contained in:
parent
debebcfd25
commit
7ec41275d5
@ -15,5 +15,7 @@ ATTR_RECIPE_ID = "recipe_id"
|
||||
ATTR_URL = "url"
|
||||
ATTR_INCLUDE_TAGS = "include_tags"
|
||||
ATTR_ENTRY_TYPE = "entry_type"
|
||||
ATTR_NOTE_TITLE = "note_title"
|
||||
ATTR_NOTE_TEXT = "note_text"
|
||||
|
||||
MIN_REQUIRED_MEALIE_VERSION = AwesomeVersion("v1.0.0")
|
||||
|
@ -27,6 +27,7 @@
|
||||
"get_mealplan": "mdi:food",
|
||||
"get_recipe": "mdi:map",
|
||||
"import_recipe": "mdi:map-search",
|
||||
"set_random_mealplan": "mdi:dice-multiple"
|
||||
"set_random_mealplan": "mdi:dice-multiple",
|
||||
"set_mealplan": "mdi:food"
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ from .const import (
|
||||
ATTR_END_DATE,
|
||||
ATTR_ENTRY_TYPE,
|
||||
ATTR_INCLUDE_TAGS,
|
||||
ATTR_NOTE_TEXT,
|
||||
ATTR_NOTE_TITLE,
|
||||
ATTR_RECIPE_ID,
|
||||
ATTR_START_DATE,
|
||||
ATTR_URL,
|
||||
@ -70,6 +72,31 @@ SERVICE_SET_RANDOM_MEALPLAN_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
SERVICE_SET_MEALPLAN = "set_mealplan"
|
||||
SERVICE_SET_MEALPLAN_SCHEMA = vol.Any(
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_CONFIG_ENTRY_ID): str,
|
||||
vol.Required(ATTR_DATE): cv.date,
|
||||
vol.Required(ATTR_ENTRY_TYPE): vol.In(
|
||||
[x.lower() for x in MealplanEntryType]
|
||||
),
|
||||
vol.Required(ATTR_RECIPE_ID): str,
|
||||
}
|
||||
),
|
||||
vol.Schema(
|
||||
{
|
||||
vol.Required(ATTR_CONFIG_ENTRY_ID): str,
|
||||
vol.Required(ATTR_DATE): cv.date,
|
||||
vol.Required(ATTR_ENTRY_TYPE): vol.In(
|
||||
[x.lower() for x in MealplanEntryType]
|
||||
),
|
||||
vol.Required(ATTR_NOTE_TITLE): str,
|
||||
vol.Required(ATTR_NOTE_TEXT): str,
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def async_get_entry(hass: HomeAssistant, config_entry_id: str) -> MealieConfigEntry:
|
||||
"""Get the Mealie config entry."""
|
||||
@ -170,6 +197,29 @@ def setup_services(hass: HomeAssistant) -> None:
|
||||
return {"mealplan": asdict(mealplan)}
|
||||
return None
|
||||
|
||||
async def async_set_mealplan(call: ServiceCall) -> ServiceResponse:
|
||||
"""Set a mealplan."""
|
||||
entry = async_get_entry(hass, call.data[ATTR_CONFIG_ENTRY_ID])
|
||||
mealplan_date = call.data[ATTR_DATE]
|
||||
entry_type = MealplanEntryType(call.data[ATTR_ENTRY_TYPE])
|
||||
client = entry.runtime_data.client
|
||||
try:
|
||||
mealplan = await client.set_mealplan(
|
||||
mealplan_date,
|
||||
entry_type,
|
||||
recipe_id=call.data.get(ATTR_RECIPE_ID),
|
||||
note_title=call.data.get(ATTR_NOTE_TITLE),
|
||||
note_text=call.data.get(ATTR_NOTE_TEXT),
|
||||
)
|
||||
except MealieConnectionError as err:
|
||||
raise HomeAssistantError(
|
||||
translation_domain=DOMAIN,
|
||||
translation_key="connection_error",
|
||||
) from err
|
||||
if call.return_response:
|
||||
return {"mealplan": asdict(mealplan)}
|
||||
return None
|
||||
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_GET_MEALPLAN,
|
||||
@ -198,3 +248,10 @@ def setup_services(hass: HomeAssistant) -> None:
|
||||
schema=SERVICE_SET_RANDOM_MEALPLAN_SCHEMA,
|
||||
supports_response=SupportsResponse.OPTIONAL,
|
||||
)
|
||||
hass.services.async_register(
|
||||
DOMAIN,
|
||||
SERVICE_SET_MEALPLAN,
|
||||
async_set_mealplan,
|
||||
schema=SERVICE_SET_MEALPLAN_SCHEMA,
|
||||
supports_response=SupportsResponse.OPTIONAL,
|
||||
)
|
||||
|
@ -58,3 +58,32 @@ set_random_mealplan:
|
||||
- dinner
|
||||
- side
|
||||
translation_key: mealplan_entry_type
|
||||
|
||||
set_mealplan:
|
||||
fields:
|
||||
config_entry_id:
|
||||
required: true
|
||||
selector:
|
||||
config_entry:
|
||||
integration: mealie
|
||||
date:
|
||||
selector:
|
||||
date:
|
||||
entry_type:
|
||||
selector:
|
||||
select:
|
||||
options:
|
||||
- breakfast
|
||||
- lunch
|
||||
- dinner
|
||||
- side
|
||||
translation_key: mealplan_entry_type
|
||||
recipe_id:
|
||||
selector:
|
||||
text:
|
||||
note_title:
|
||||
selector:
|
||||
text:
|
||||
note_text:
|
||||
selector:
|
||||
text:
|
||||
|
@ -175,6 +175,36 @@
|
||||
"description": "The type of dish to randomize."
|
||||
}
|
||||
}
|
||||
},
|
||||
"set_mealplan": {
|
||||
"name": "Set a mealplan",
|
||||
"description": "Set a mealplan for a specific date",
|
||||
"fields": {
|
||||
"config_entry_id": {
|
||||
"name": "[%key:component::mealie::services::get_mealplan::fields::config_entry_id::name%]",
|
||||
"description": "[%key:component::mealie::services::get_mealplan::fields::config_entry_id::description%]"
|
||||
},
|
||||
"date": {
|
||||
"name": "[%key:component::mealie::services::set_random_mealplan::fields::date::name%]",
|
||||
"description": "[%key:component::mealie::services::set_random_mealplan::fields::date::description%]"
|
||||
},
|
||||
"entry_type": {
|
||||
"name": "[%key:component::mealie::services::set_random_mealplan::fields::entry_type::name%]",
|
||||
"description": "The type of dish to set the recipe to."
|
||||
},
|
||||
"recipe_id": {
|
||||
"name": "[%key:component::mealie::services::get_recipe::fields::recipe_id::name%]",
|
||||
"description": "[%key:component::mealie::services::get_recipe::fields::recipe_id::description%]"
|
||||
},
|
||||
"note_title": {
|
||||
"name": "Meal note title",
|
||||
"description": "Meal note title for when planning without recipe."
|
||||
},
|
||||
"note_text": {
|
||||
"name": "Note text",
|
||||
"description": "Meal note text for when planning without recipe."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
|
@ -74,9 +74,9 @@ def mock_mealie_client() -> Generator[AsyncMock]:
|
||||
client.get_statistics.return_value = Statistics.from_json(
|
||||
load_fixture("statistics.json", DOMAIN)
|
||||
)
|
||||
client.random_mealplan.return_value = Mealplan.from_json(
|
||||
load_fixture("mealplan.json", DOMAIN)
|
||||
)
|
||||
mealplan = Mealplan.from_json(load_fixture("mealplan.json", DOMAIN))
|
||||
client.random_mealplan.return_value = mealplan
|
||||
client.set_mealplan.return_value = mealplan
|
||||
yield client
|
||||
|
||||
|
||||
|
@ -675,6 +675,54 @@
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_service_set_mealplan[payload0-kwargs0]
|
||||
dict({
|
||||
'mealplan': dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': datetime.date(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',
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_service_set_mealplan[payload1-kwargs1]
|
||||
dict({
|
||||
'mealplan': dict({
|
||||
'description': None,
|
||||
'entry_type': <MealplanEntryType.DINNER: 'dinner'>,
|
||||
'group_id': '0bf60b2e-ca89-42a9-94d4-8f67ca72b157',
|
||||
'mealplan_date': datetime.date(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',
|
||||
}),
|
||||
})
|
||||
# ---
|
||||
# name: test_service_set_random_mealplan
|
||||
dict({
|
||||
'mealplan': dict({
|
||||
|
@ -18,6 +18,8 @@ from homeassistant.components.mealie.const import (
|
||||
ATTR_END_DATE,
|
||||
ATTR_ENTRY_TYPE,
|
||||
ATTR_INCLUDE_TAGS,
|
||||
ATTR_NOTE_TEXT,
|
||||
ATTR_NOTE_TITLE,
|
||||
ATTR_RECIPE_ID,
|
||||
ATTR_START_DATE,
|
||||
ATTR_URL,
|
||||
@ -27,6 +29,7 @@ from homeassistant.components.mealie.services import (
|
||||
SERVICE_GET_MEALPLAN,
|
||||
SERVICE_GET_RECIPE,
|
||||
SERVICE_IMPORT_RECIPE,
|
||||
SERVICE_SET_MEALPLAN,
|
||||
SERVICE_SET_RANDOM_MEALPLAN,
|
||||
)
|
||||
from homeassistant.const import ATTR_DATE
|
||||
@ -231,6 +234,71 @@ async def test_service_set_random_mealplan(
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("payload", "kwargs"),
|
||||
[
|
||||
(
|
||||
{
|
||||
ATTR_RECIPE_ID: "recipe_id",
|
||||
},
|
||||
{"recipe_id": "recipe_id", "note_title": None, "note_text": None},
|
||||
),
|
||||
(
|
||||
{
|
||||
ATTR_NOTE_TITLE: "Note Title",
|
||||
ATTR_NOTE_TEXT: "Note Text",
|
||||
},
|
||||
{"recipe_id": None, "note_title": "Note Title", "note_text": "Note Text"},
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_service_set_mealplan(
|
||||
hass: HomeAssistant,
|
||||
mock_mealie_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
snapshot: SnapshotAssertion,
|
||||
payload: dict[str, str],
|
||||
kwargs: dict[str, str],
|
||||
) -> None:
|
||||
"""Test the set_mealplan service."""
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
response = await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_MEALPLAN,
|
||||
{
|
||||
ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
|
||||
ATTR_DATE: "2023-10-21",
|
||||
ATTR_ENTRY_TYPE: "lunch",
|
||||
}
|
||||
| payload,
|
||||
blocking=True,
|
||||
return_response=True,
|
||||
)
|
||||
assert response == snapshot
|
||||
mock_mealie_client.set_mealplan.assert_called_with(
|
||||
date(2023, 10, 21), MealplanEntryType.LUNCH, **kwargs
|
||||
)
|
||||
|
||||
mock_mealie_client.random_mealplan.reset_mock()
|
||||
await hass.services.async_call(
|
||||
DOMAIN,
|
||||
SERVICE_SET_MEALPLAN,
|
||||
{
|
||||
ATTR_CONFIG_ENTRY_ID: mock_config_entry.entry_id,
|
||||
ATTR_DATE: "2023-10-21",
|
||||
ATTR_ENTRY_TYPE: "lunch",
|
||||
}
|
||||
| payload,
|
||||
blocking=True,
|
||||
return_response=False,
|
||||
)
|
||||
mock_mealie_client.set_mealplan.assert_called_with(
|
||||
date(2023, 10, 21), MealplanEntryType.LUNCH, **kwargs
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("service", "payload", "function", "exception", "raised_exception", "message"),
|
||||
[
|
||||
@ -282,6 +350,18 @@ async def test_service_set_random_mealplan(
|
||||
HomeAssistantError,
|
||||
"Error connecting to Mealie instance",
|
||||
),
|
||||
(
|
||||
SERVICE_SET_MEALPLAN,
|
||||
{
|
||||
ATTR_DATE: "2023-10-21",
|
||||
ATTR_ENTRY_TYPE: "lunch",
|
||||
ATTR_RECIPE_ID: "recipe_id",
|
||||
},
|
||||
"set_mealplan",
|
||||
MealieConnectionError,
|
||||
HomeAssistantError,
|
||||
"Error connecting to Mealie instance",
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_services_connection_error(
|
||||
@ -321,6 +401,14 @@ async def test_services_connection_error(
|
||||
SERVICE_SET_RANDOM_MEALPLAN,
|
||||
{ATTR_DATE: "2023-10-21", ATTR_ENTRY_TYPE: "lunch"},
|
||||
),
|
||||
(
|
||||
SERVICE_SET_MEALPLAN,
|
||||
{
|
||||
ATTR_DATE: "2023-10-21",
|
||||
ATTR_ENTRY_TYPE: "lunch",
|
||||
ATTR_RECIPE_ID: "recipe_id",
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
async def test_service_entry_availability(
|
||||
|
Loading…
x
Reference in New Issue
Block a user