Mealie in HA

Great addition with filtering down to a specific shopping list! I don’t use the Mealie in-built shopping lists for anything else, but I could see that being very useful if you do, to not have everything sucked away by HA. :slight_smile:

It’s interesting the comments in the jinja template don’t work for you - they seem supported fine in my configuration. But you’re right, they’re not required - was just using them to try and explain what the template was doing in my example.

I noticed this in my logs as well the other day, I didn’t realise it was from this Mealie setup! I’ll have a play around with it and let you know if I can prevent those log messages. I’m sure I’m doing something dodgy somewhere. :slight_smile:

EDIT: I think it’s the json_attributes_path in the rest sensor that’s causing it. Not sure how to work around that yet.

EDIT 2:
I got this resolved, but it required a fair few changes, essentially to avoid using the rest sensor and instead use the restful platform to extract the ingredient ID and the item name in different sensors. This way I can use a jinja template to handle the null scenario where item[0] in the array doesn’t exist, which occurs when there’s nothing on the Mealie shopping list.

TLDR with complete config at bottom of this post.

First, you need to replace the entire sensor section with this rest section instead:

rest:
  - resource_template: !secret mealie_shopping_items_api_endpoint
    headers:
      Authorization: !secret mealie_api_token
    params:
      perPage: 1
      queryFilter: checked=false
      orderBy: food.name
      orderDirection: asc
    sensor:
      - name: Mealie Shopping List Items
        availability: >
          {{ value_json['total'] }}
        #Sensor to view items which are on any Mealie shopping list
        #Extract the ingredient data in the following format: "food - quantity unit" eg. "apples - 1.5 kilogram", or "Empty List" if no items are left in the list
        value_template: >
          {# If no items in list, return "Empty List" #}
          {% if value_json['total'] == 0 %}
            Empty List

          {# If distinct food is not defined use display name instead #}
          {% elif value_json['items'][0]['food'] == none %}
            {{ value_json['items'][0]['display'] }}

          {# If quantity is available, include it in the output #}
          {% elif value_json['items'][0]['quantity'] > 0 %}
            {{ value_json['items'][0]['food']['name'] }} - {{ (value_json['items'][0]['quantity'] | round(3) | string).rstrip('0').rstrip('.') }} {{ value_json['items'][0]['unit']['name'] if value_json['items'][0]['unit'] is not none }}

          {# Otherwise, print just the food name #}
          {% else %}
            {{ value_json['items'][0]['food']['name'] }}
            
          {% endif %}
      - name: Mealie Shopping List Item To Delete
        availability: >
          {{ value_json['total'] }}
        #Extract the ingredient's ID. Required to delete through the API later on.
        value_template: >
          {{ value_json['items'][0]['id'] }}

Then you need to update the mealie_delete_from_shopping_list rest command to refer to the new sensor’s state, rather than the old sensor’s attribute:

rest_command:
  #Command to delete a Mealing shopping list item
  mealie_delete_from_shopping_list:
    url: >
      {{ url }}/{{ states('sensor.mealie_shopping_list_item_to_delete') }}
    method: delete
    headers:
      Authorization:  !secret mealie_api_token

And finally, update automation.mealie_update_rest_sensors to include the new sensor in the homeassistant.update_entity step:

automation:
  #Automation to regularly refresh the "Mealie Shopping List Items" sensor 
  - id: mealie_update_rest_sensors
    alias: "Mealie - Update Rest Sensors"
    initial_state: "on"
    trigger:
      - platform: time_pattern
        seconds: "/10" #How often do you want to check for updates in Mealie?
    action:
      - service: homeassistant.update_entity
        data:
          entity_id:
            - sensor.mealie_shopping_list_items
            - sensor.mealie_shopping_list_item_to_delete

Looks like this has done the trick for me to remove that log statement - hopefully it does the same for you!

TLDR - here’s the final complete configuration, which fixes the “JSON result was not a dictionary or list with 0th element a dictionary” log statement:

rest:
  - resource_template: !secret mealie_shopping_items_api_endpoint
    headers:
      Authorization: !secret mealie_api_token
    params:
      perPage: 1
      queryFilter: checked=false
      orderBy: food.name
      orderDirection: asc
    sensor:
      - name: Mealie Shopping List Items
        availability: >
          {{ value_json['total'] }}
        #Sensor to view items which are on any Mealie shopping list
        #Extract the ingredient data in the following format: "food - quantity unit" eg. "apples - 1.5 kilogram", or "Empty List" if no items are left in the list
        value_template: >
          {# If no items in list, return "Empty List" #}
          {% if value_json['total'] == 0 %}
            Empty List

          {# If distinct food is not defined use display name instead #}
          {% elif value_json['items'][0]['food'] == none %}
            {{ value_json['items'][0]['display'] }}

          {# If quantity is available, include it in the output #}
          {% elif value_json['items'][0]['quantity'] > 0 %}
            {{ value_json['items'][0]['food']['name'] }} - {{ (value_json['items'][0]['quantity'] | round(3) | string).rstrip('0').rstrip('.') }} {{ value_json['items'][0]['unit']['name'] if value_json['items'][0]['unit'] is not none }}

          {# Otherwise, print just the food name #}
          {% else %}
            {{ value_json['items'][0]['food']['name'] }}
            
          {% endif %}
      - name: Mealie Shopping List Item To Delete
        availability: >
          {{ value_json['total'] }}
        #Extract the ingredient's ID. Required to delete through the API later on.
        value_template: >
          {{ value_json['items'][0]['id'] }}

rest_command:
  #Command to delete a Mealing shopping list item
  mealie_delete_from_shopping_list:
    url: >
      {{ url }}/{{ states('sensor.mealie_shopping_list_item_to_delete') }}
    method: delete
    headers:
      Authorization:  !secret mealie_api_token

automation:
  #Automation to regularly refresh the "Mealie Shopping List Items" sensor 
  - id: mealie_update_rest_sensors
    alias: "Mealie - Update Rest Sensors"
    initial_state: "on"
    trigger:
      - platform: time_pattern
        seconds: "/10" #How often do you want to check for updates in Mealie?
    action:
      - service: homeassistant.update_entity
        data:
          entity_id:
            - sensor.mealie_shopping_list_items
            - sensor.mealie_shopping_list_item_to_delete

  #Automation to transfer items from the Mealie shopping list to another list of your choice
  - id: mealie_transfer_shopping_list
    alias: "Mealie - Transfer Shopping List"
    description: "When shopping list items are added to the Mealie shopping list, transfer them to an alternative list."
    mode: queued
    max: 10
    initial_state: "on"
    variables:
      target_shopping_list: todo.groceries_list #CHANGEME: entity ID of your shopping list
      notify_service: notify.mobile_app_michael_s_galaxy_s10 #CHANGEME: where to be notified if items fail to delete from Mealie shopping list
    trigger:
      - id: "regular_trigger"
        platform: state
        entity_id: sensor.mealie_shopping_list_items
      - id: "fallback_trigger"
        platform: time_pattern
        minutes: "/10"
    condition:
      - condition: not
        conditions:
          - condition: state
            entity_id: sensor.mealie_shopping_list_items
            state: "Empty List"
    action:
      - alias: "Ensure shopping list item isn't now 'Empty List'"
        condition: not
        conditions:
          - condition: state
            entity_id: sensor.mealie_shopping_list_items
            state: "Empty List"

      - alias: "Add shopping list item to target list"
        service: todo.add_item
        target:
          entity_id: "{{ target_shopping_list }}"
        data:
          item: "{{ states('sensor.mealie_shopping_list_items') }}"
          description: Imported From Mealie

      - alias: "Delete shopping list item from Mealie"
        service: rest_command.mealie_delete_from_shopping_list
        data:
          url: !secret mealie_shopping_items_api_endpoint
        response_variable: mealie_delete_response

      - alias: "Notify if item failed to delete from Mealie"
        if: "{{ mealie_delete_response['status'] != 200 }}"
        then:
          - service: "{{ notify_service }}"
            data:
              title: "Failed to delete item from Mealie list"
              message: "{{ mealie_delete_response['content'] }}"
              data:
                notification_icon: "mdi:food-off"
                
      - alias: "Refresh Mealie shopping list items sensor, in case more items need processing"
        service: homeassistant.update_entity
        data:
          entity_id:
            - sensor.mealie_shopping_list_items
1 Like

I’ve also realised another issue with this shopping list setup the other day. It’s occasionally adding “Unavailable” as an item to my todoist shopping list. I think it happens when I restart my home assistant instance, most likely because the automation isn’t handling the unavailable state properly in the trigger. I’ll let you know if I fix that, but if anybody beats me to it then I’ll happily steal your work! :wink:

Working like a dream. Huge thanks! And phenomenal effort to recode so quickly!

1 Like

Thanks for updating this, I was able to get it to work also (after several hours)…one thing, i’ve noticed a lot of additional sensors being created…and I can’t explain ‘why’
While admitedly i did make some modifications for specific grocery list, nothing that i modified would result in an items_2,3,4, being created. I’ve searched my yaml, there is no “_items_2…3…4.”. anything, so I don’t know why that’s happening.

Otherwise, the update seems to be transferring fine… I’ve noticed that it’s a slower transfer than with the first iteration of code…the first iteration was basically a rapid fire of transfers this iteration is more of a 10-15 seconds in between each item getting moved. Not sure if that’s happening with anyone else or just me.

update disregard the multiple entries mention, I think they cleared up after restarting.

This would bo perfect for my new dashboard (wall tablet) in the kitchen.
I tried the last config of @my.spider.died but cannot get it al to work, the rest command wont work.
The modifications that did to make it somewhat work, are offcourse not working (newbie in all of this) maybe somebody can point me in the right direction.

- platform: rest
    resource: "http://****/api/groups/mealplans/today"
    method: GET
    name: Mealie todays meal
    headers:
      Authorization: Bearer ****
    value_template: "{{ value_json[0].recipe.name }}"
    force_update: true
    scan_interval: 30 

  - platform: rest
    resource: "http://****/api/groups/mealplans/today"
    method: GET
    name: Mealie todays meal ID
    headers:
      Authorization: Bearer ****
    value_template: "{{ value_json[0].recipe.id }}"
    force_update: true
    scan_interval: 30
    
  #Sensor to view items which are on any Mealie shopping list
  - platform: rest
    name: Mealie Shopping List Items
    resource_template: "http://****/api/groups/shopping/items"
    headers:
      Authorization: Bearer ****
    params:
      perPage: 1
      queryFilter: checked=false
      orderBy: food.name
      orderDirection: asc
    # As long as the request contains "total", the API was successfully reached
    availability: >
      {{ value_json['total'] }}
    
    value_template: >
      {# If no items in list, return "Empty List" #}
      {% if value_json['total'] == 0 %}
        Empty List

      {# If distinct food is not defined use display name instead #}
      {% elif value_json['items'][0]['food'] == none %}
        {{ value_json['items'][0]['display'] }}

      {# If quantity is available, include it in the output #}
      {% elif value_json['items'][0]['quantity'] > 0 %}
        {{ value_json['items'][0]['food']['name'] }} - {{ (value_json['items'][0]['quantity'] | round(3) | string).rstrip('0').rstrip('.') }} {{ value_json['items'][0]['unit']['name'] if value_json['items'][0]['unit'] is not none }}

      {# Otherwise, print just the food name #}
      {% else %}
        {{ value_json['items'][0]['food']['name'] }}
        
      {% endif %}

No logs = no issue

@edi023 You can try first in a tool like Postman if all your call config works correct.

To get the rest commands to work, I made my own rests.yaml file and put “rest: !include rests.yaml” in the config file.

my rests file looks like this…(bare in mind that my code is modified from the original for a specific shopping list in mealie)

#Mealie rest call for grocery store
- resource_template: !secret mealie_shopping_items_api_endpoint
  headers:
    Authorization: !secret mealie_api_token
  params:
    perPage: 1
    queryFilter: checked=false AND shoppingListId=[shoppinglistid]
    orderBy: food.name
    orderDirection: asc
  sensor:
    - name: Mealie grocery store Shopping List Items
      availability: >
        {{ value_json['total'] }}
      #Sensor to view items which are on any Mealie shopping list
      #Extract the ingredient data in the following format: "food - quantity unit" eg. "apples - 1.5 kilogram", or "Empty List" if no items are left in the list
      value_template: >
        {# If no items in list, return "Empty List" #}
        {% if value_json['total'] == 0 %}
          Empty List

        {# If distinct food is not defined use display name instead #}
        {% elif value_json['items'][0]['food'] == none %}
          {{ value_json['items'][0]['display'] }}

        {# If quantity is available, include it in the output #}
        {% elif value_json['items'][0]['quantity'] > 0 %}
          {{ value_json['items'][0]['food']['name'] }} - {{ (value_json['items'][0]['quantity'] | round(3) | string).rstrip('0').rstrip('.') }} {{ value_json['items'][0]['unit']['name'] if value_json['items'][0]['unit'] is not none }}

        {# Otherwise, print just the food name #}
        {% else %}
          {{ value_json['items'][0]['food']['name'] }}
          
        {% endif %}
    - name: Mealie grocery store Shopping List Item To Delete
      availability: >
        {{ value_json['total'] }}
      #Extract the ingredient's ID. Required to delete through the API later on.
      value_template: >
        {{ value_json['items'][0]['id'] }}

it’s easier to test the api with https://[mealiedomanname]/docs (assuming mealie is behind a proxy)

i’m using a split config setup, in configuration.yaml i got
rest: !include rests.yaml
rest_command: !include rest_commands.yaml
automation: !include automations.yaml

using the scripts from my.spider.died and splitting this up to the appropriate files gives errors.

2024-05-08 17:13:00.419 ERROR (MainThread) [homeassistant.components.automation.mealie_transfer_shopping_list] Mealie - Transfer Shopping List: Notify if item failed to delete from Mealie: Error executing script. Error for call_service at pos 1: Template rendered invalid service: 
2024-05-08 17:13:00.419 ERROR (MainThread) [homeassistant.components.automation.mealie_transfer_shopping_list] Mealie - Transfer Shopping List: Error executing script. Error for if at pos 4: Template rendered invalid service: 

it looks like the correct parameters are not passed between the automation, rest and rest_command.

does anyone know how to make this work ?

adding 1 item in the mealie shoppinglist, now gets repeated over and over as ‘unknown’ in my todolist

rests.yaml

- resource_template: !secret mealie_shopping_items_api_endpoint
  headers:
    Authorization: !secret mealie_api_token
  params:
    perPage: 1
    queryFilter: checked=false
    orderBy: food.name
    orderDirection: asc
  sensor:
    - name: Mealie Shopping List Items
      availability: >
        {{ value_json['total'] }}
      #Sensor to view items which are on any Mealie shopping list
      #Extract the ingredient data in the following format: "food - quantity unit" eg. "apples - 1.5 kilogram", or "Empty List" if no items are left in the list
      value_template: >
        {# If no items in list, return "Empty List" #}
        {% if value_json['total'] == 0 %}
          Empty List

        {# If distinct food is not defined use display name instead #}
        {% elif value_json['items'][0]['food'] == none %}
          {{ value_json['items'][0]['display'] }}

        {# If quantity is available, include it in the output #}
        {% elif value_json['items'][0]['quantity'] > 0 %}
          {{ value_json['items'][0]['food']['name'] }} - {{ (value_json['items'][0]['quantity'] | round(3) | string).rstrip('0').rstrip('.') }} {{ value_json['items'][0]['unit']['name'] if value_json['items'][0]['unit'] is not none }}

        {# Otherwise, print just the food name #}
        {% else %}
          {{ value_json['items'][0]['food']['name'] }}
          
        {% endif %}
    - name: Mealie Shopping List Item To Delete
      availability: >
        {{ value_json['total'] }}
      #Extract the ingredient's ID. Required to delete through the API later on.
      value_template: >
        {{ value_json['items'][0]['id'] }}

rest_commands.yaml

#Command to delete a Mealing shopping list item
mealie_delete_from_shopping_list:
  url: >
    {{ url }}/{{ states('sensor.mealie_shopping_list_item_to_delete') }}
  method: delete
  headers:
    Authorization:  !secret mealie_api_token

automations.yaml

- id: mealie_transfer_shopping_list
  alias: Mealie - Transfer Shopping List
  description: When shopping list items are added to the Mealie shopping list, transfer
    them to an alternative list.
  trigger:
  - id: regular_trigger
    platform: state
    entity_id: sensor.mealie_shopping_list_items
  - id: fallback_trigger
    platform: time_pattern
    minutes: /1
  condition:
  - condition: not
    conditions:
    - condition: state
      entity_id: sensor.mealie_shopping_list_items
      state: Empty List
  action:
  - alias: Ensure shopping list item isn't now 'Empty List'
    condition: not
    conditions:
    - condition: state
      entity_id: sensor.mealie_shopping_list_items
      state: Empty List
  - alias: Add shopping list item to target list
    service: todo.add_item
    target:
      entity_id: '{{ target_shopping_list }}'
    data:
      item: '{{ states(''sensor.mealie_shopping_list_items'') }}'
  - alias: Delete shopping list item from Mealie
    service: rest_command.mealie_delete_from_shopping_list
    data:
      url: "https://xxx/api/groups/shopping/items"
    response_variable: mealie_delete_response
  - alias: Notify if item failed to delete from Mealie
    if:
    - condition: template
      value_template: '{{ mealie_delete_response[''status''] != 200 }}'
    then:
    - service: '{{ notify_service }}'
      data:
        title: Failed to delete item from Mealie list
        message: '{{ mealie_delete_response[''content''] }}'
        data:
          notification_icon: mdi:food-off
  - alias: Refresh Mealie shopping list items sensor, in case more items need processing
    service: homeassistant.update_entity
    data:
      entity_id:
      - sensor.mealie_shopping_list_items
  mode: queued
  max: 10
  initial_state: 'on'
  variables:
    target_shopping_list: todo.shopping_list
- id: mealie_update_rest_sensors
  alias: Mealie - Update Rest Sensors
  initial_state: 'on'
  trigger:
  - platform: time_pattern
    seconds: /10
  action:
  - service: homeassistant.update_entity
    data:
      entity_id:
      - sensor.mealie_shopping_list_items
      - sensor.mealie_shopping_list_item_to_delete

solved.
in secrets.yaml the mealie_api_token needs to start with "Bearer "

1 Like

Hi there, I have built a custom docker image, so that Mealie can now run as HA add-on with ingress support! You can find it in my GitHub repository.

1 Like

Thx for your blog.
I followed it.
I do have problems at the end, with your list…
I copied the code, but its not showing right…

@skank I see it works for you!
This is indeed what you see if you check the code in the Developer tool > Template editor OR in the Markdown Card configuration but when you paste the code in the Visual editor state in stead of the Code editor state. Press on the blue text below to switch editor mode and paste you code again. Or check your line indent for each line.

This is how the code in the code editor mode looks like.

(This evening I will also add to my blog page how you make the text clickable)

I have it different now, i have it like this:

Its ok.

The clickable code you posted doesnt work here.

1 Like

What doesn’t work about the link? Can you click the weekmenu text and you get redirected to an other page? Your url is ofcourse different then the mine.

Thank you so much for your reply.
i got i running except i only get the first ingredient from my shopping list when i add it.

sounds like something with the automation job/sensor update/ or the rest call to delete the entry…that’s the thing that removes the first item in the shopping list from mealie and move on to the next. Double check those things.