REST - Nutrislice JSON Values

After several hours of messing around, thanks to a lot of research on this site, I have been successful in returning some values but reaching out for a nudge in the right direction.

In short, our school system posts their lunch menu online (Nutrislice) which is awesome - I want to pull this into Home Assistant so that it can be displayed every morning.

Source: https://fcps.api.nutrislice.com/menu/api/weeks/school/woodson-high-school/menu-type/hs-lunch-2/2023/08/31?format=json

Using the code below, I can get a specific item on a specific day, say chicken nuggets on day [4], but what I really want to do is have a list of all the “menu_items” for a given day.

Is there a way to get all the menu items for the current day so that I can have them displayed on a card or is that wishful thinking?

1 Like

With my halfway advanced knowledge I donot think you can do this easily as it is not posisble to ‘template’ the path.
I would create 7 sensors, one for each day (assuming 0 = sunday?) and list all menu_items in the attributes. Then you could use templating to get the name out iterating over the individual menu_items. The latter you could do e.g. in a markdown card

  - platform: rest
    name: cantine_thursday
    scan_interval: 36000
    resource: "https://fcps.api.nutrislice.com/menu/api/weeks/school/woodson-high-school/menu-type/hs-lunch-2/2023/08/31?format=json" 
    value_template: 'OK'
    json_attributes_path: $.days.4
    json_attributes:      
      - menu_items
{% set y = state_attr('sensor.cantine_thursday','menu_items')  %}
{% for x in range(0,y| count) %}
{% if y[x].food != None %}
{{ y[x].food.name }}
{% endif %}
{% endfor %}

You could probably also load ALL output (i.e. starting from ‘days’ in one sensor and then iterate through it. But the list is long and it may not accept this much data

Another more challenging option is to use ‘jq’, this allows soo many things but is not for the fainthearted

This is super helpful, I ended up creating 5 different sensors (Monday through Friday) and a markup card that shows all the items for a given day.

Instead of creating a bunch of markup cards- one for each day- is there a way to have a single markup card show the items for a given day - On Mondays, it shows sensor.cantine_mondays…On Tuesdays, it shows sensor.cantine_tuesdays…?

I’m thinking it’s an If ElseIf statement in the state_attr code but where you have “halfway advanced knowledge” I personally have “basic just enough to get myself into trouble but not to get out of it knowledge” :slight_smile:

Good question, I am not known to templating the sensor name itself…possibly try raising this on discord in the #templates
Else what you could do is something alike this

{% if now.strftime("a") == Thu %}
{% set y = state_attr('sensor.cantine_thursday','menu_items')  %}
{% elif now.strftime("a") == Fri %}
{% set y = state_attr('sensor.cantine_friday','menu_items')  %}
{% endif %}

you can try this

{{ state_attr('sensor.cantine_' + now().strftime('%A')|lower,'menu_items') }}

you could create 5 different markup cards, and then only show the one for today using the auto-entities card:

First, thank you to Thomas Loven as I use several of his projects!

That particular card has been deprecated, as Home Assistant has the capability native now?

Not sure if that changes things, or how to code if it doesn’t, but will absolutely share what ultimately gets the job done as I believe there are many other Home Assistant users who are seeking this capability - heck, maybe there’s an opportunity for an entrepreneurial spirit to craft an integration?

I wish I were that brilliant, but absent that ability can definitely help shine a light on the solution!

This was the inspiration for what it’s worth:

https://www.reddit.com/r/dakboard/comments/peqgdm/school_lunch_menu_plugin_from_nutrislicecom/

Where are you seeing that auto-entities has been deprecated? I’m still using it. And still seeing activity on the post: https://community.home-assistant.io/t/auto-entities-automatically-fill-cards-with-entities/

My apologies - I had too many tabs open and was looking at the “Markdown-Mod” card…Going to see if I can figure out the code to make the auto-entities card work and will share any updates.

This is very helpful. I’ve been trying to do the same thing. Could this be made into an integration for HA? Please share any updates to your integration. Following.

1 Like

I’m still playing around with things, but here’s where things currently stand:

1.) Created (5) sensors - one for each day of the week - and added them to my configuration.yaml file using File Editor.

You’ll want to edit the URL details to match your specific school but the format shouldn’t change much.

While I can’t speak to every scenario, the weekly calendar starts on Sunday with day “0” and runs through Saturday with day “6” so I called out only Monday through Friday (i.e., Days 1 - 5).

Also, I didn’t want to ping the site incessantly, so I set the refresh to every 8 hours (i.e., 28800 seconds) but you can certainly toggle that as you see fit.

#FCPS School Lunch
  - platform: rest
    name: lunch Monday
    scan_interval: 28800
    resource_template: https://fcps.api.nutrislice.com/menu/api/weeks/school/woodson-high-school/menu-type/hs-lunch/{{now().strftime('%Y/%m/%d')}}?format=json
    value_template: 'OK'
    json_attributes_path: $.days.1
    json_attributes:      
      - menu_items

  - platform: rest
    name: lunch Tuesday
    scan_interval: 28800
    resource_template: https://fcps.api.nutrislice.com/menu/api/weeks/school/woodson-high-school/menu-type/hs-lunch/{{now().strftime('%Y/%m/%d')}}?format=json
    value_template: 'OK'
    json_attributes_path: $.days.2
    json_attributes:      
      - menu_items    

  - platform: rest
    name: lunch Wednesday
    scan_interval: 28800
    resource_template: https://fcps.api.nutrislice.com/menu/api/weeks/school/woodson-high-school/menu-type/hs-lunch/{{now().strftime('%Y/%m/%d')}}?format=json
    value_template: 'OK'
    json_attributes_path: $.days.3
    json_attributes:      
      - menu_items    

  - platform: rest
    name: lunch Thursday
    scan_interval: 28800
    resource_template: https://fcps.api.nutrislice.com/menu/api/weeks/school/woodson-high-school/menu-type/hs-lunch/{{now().strftime('%Y/%m/%d')}}?format=json
    value_template: 'OK'
    json_attributes_path: $.days.4
    json_attributes:      
      - menu_items    

  - platform: rest
    name: lunch Friday
    scan_interval: 28800
    resource_template: https://fcps.api.nutrislice.com/menu/api/weeks/school/woodson-high-school/menu-type/hs-lunch/{{now().strftime('%Y/%m/%d')}}?format=json
    value_template: 'OK'
    json_attributes_path: $.days.5
    json_attributes:      
      - menu_items    

2.) Created a markup card that displays the menu items for the current day.

You’ll see that I “removed” milk from the results with a replace function, it’s on the menu every day and not really of interest to me personally. You could copy-and-paste that same code to filter out other items if you needed, or remove it altogether and see everything for a given day.

          - type: markdown
            title: Lunch
            content: >-
              {% set lunch = state_attr('sensor.lunch_' +
              now().strftime('%A')|lower,'menu_items')  %}

              {% for x in range(0,lunch| count) %}

              {% if lunch[x].food != None %}

              {{ lunch[x].food.name | replace('Milk, Fat Free Unflavored','')|
              replace('Milk, Fat Free Chocolate','')| replace('Milk, 1%
              Unflavored','')}}

              {% endif %}

              {% endfor %}

This has been a big hit - and thank you to @vingerha and @mekaneck for chiming in!

Two questions.

1.) I built five similar sensors for breakfast - is there a way to add those breakfast items to the same markup card? I’m thinking it’s something to do with the “content” code but I don’t really understanding templating well.

2.) Is there a way to make a card that only displays when there’s a given item - conditional formating or something…For example, if they have “pancakes” for breakfast, it would pop-up but would otherwise stay hidden.

I would say yes but it depends on what you expect, you can use multiple for/next loops on other sensors in the same card and yes…it requires fiddling with templates. There are multiple posts on markdown cards. Other options are to use JQ to pre-format/group all the data along your wishes, combine multiple sensors in one, etc.

If you want to hide the whole card based on a value then you could use ‘conditiontal card’ which requires a separate (binary)sensor to indicate that (or use card-templater on for that state)

Note: I am only presenting my views, I do not have time to go into solution mode itself …

This is so cool!! Thank you so much! I can’t wait to see how the markdown card looks later :slight_smile:

Thanks for posting this – it was super helpful to me. I use Home Assistant and a DAK Board display, so my output requirements are different than yours. I’ll share what I did in case it is helpful for anyone. This is what I used in my configuration.yaml:

sensor:
  - name: Bailey Lake Elementary Menu
    unique_id: bailey_lake_menu
    platform: rest
    resource_template: "https://clarkston.api.nutrislice.com/menu/api/weeks/school/bailey-lake/menu-type/lunch/{{now().strftime('%Y/%m/%d')}}?format=json"
    value_template: '1'
    json_attributes:
      - days
  - name: Clarkston Elementary Menu
    unique_id: clarkston_elementary_menu
    platform: rest
    resource_template: "https://clarkston.api.nutrislice.com/menu/api/weeks/school/clarkston-elementary/menu-type/lunch/{{now().strftime('%Y/%m/%d')}}?format=json"
    value_template: '1'
    json_attributes:
      - days

template:
  - sensor:
      - name: "Bailey Lake Elementary Meal Text"
        unique_id: ble_mealtext
        state: 1
        attributes:
            mealtext: >
                <b><u>BAILEY LAKE LUNCH</b></u><br/>
                {% for day in state_attr('sensor.bailey_lake_menu', 'days') | selectattr("menu_items") | selectattr("date", "greaterthan", (now()-timedelta(hours=12)).strftime("%Y-%m-%d")) %}
                    {%- if day.date == now().strftime("%Y-%m-%d") -%}<b>TODAY:</b>{{" "}}
                    {%- elif day.date == (now()+timedelta(hours=24)).strftime("%Y-%m-%d") -%}<b>TOMORROW:</b>{{" "}}
                    {%- else -%} {{- strptime(day.date, "%Y-%m-%d").strftime("%A").upper() -}}:{{" "}}
                    {%- endif -%}
                    {{  day.menu_items | selectattr("food.food_category","equalto","entree") | slice(2) | first | join('; ', attribute="food.name") }}<br/>
                {% endfor %}
        
      - name: "Clarkston Elementary Meal Text"
        unique_id: cle_mealtext
        state: 1
        attributes:
            mealtext: >
                <b><u>CLARKSTON ELEMENTARY LUNCH</b></u><br/>
                {% for day in state_attr('sensor.clarkston_elementary_menu', 'days') | selectattr("menu_items") | selectattr("date", "greaterthan", (now()-timedelta(hours=12)).strftime("%Y-%m-%d")) %}
                    {%- if day.date == now().strftime("%Y-%m-%d") -%}<b>TODAY:</b>{{" "}}
                    {%- elif day.date == (now()+timedelta(hours=24)).strftime("%Y-%m-%d") -%}<b>TOMORROW:</b>{{" "}}
                    {%- else -%} {{- strptime(day.date, "%Y-%m-%d").strftime("%A").upper() -}}:{{" "}}
                    {%- endif -%}
                    {{  day.menu_items | selectattr("food.food_category","equalto","entree") | slice(2) | first | join('; ', attribute="food.name") }}<br/>
                {% endfor %}

The template is set up to only fetch the things my kids care about – the changing first two entrees. I filtered out everything else because it is just noise for us. I only include the days left in the current week.

I then fetch the mealtext attribute in a JSON fetch block using the Home Assistant cloud states API to show the templated text on my DAKBoard display. I did this instead of the built-in DAKBoard widget because it isn’t very customizable and output a lot of unnecessary data over a large amount of space. DAKBoard doesn’t support sending POST API requests otherwise I would have used the template API instead of setting up a sensor.

Example templated text output:

BAILEY LAKE LUNCH
TOMORROW: Chicken Alfredo Sauce; Bagel, Yogurt & SunButter Fun Lunch
FRIDAY: Whole Grain Waffle; Crispy Chicken Patty Sandwich

On the screen:

image

1 Like

I discovered a variant of the api from another post that lists a much simpler digest of the menu items for a given date, rather than a week at a time.

API call is like:
/menu/api/digest/school/<school-name>/menu-type/lunch/date/yyyy/mm/dd

So my rest resource template is like:
https://<district>.api.nutrislice.com/menu/api/digest/school/<school-name>/menu-type/lunch/date/{{ now().strftime('%Y/%m/%d') }}

And the returned json is just:

{
    "date": "2024-01-04",
    "menu_items": [
        "Roasted Spaghetti Squash",
        "Marinara Sauce Cups",
        "Glazed Chicken Leg",
        "Whole Grain Biscuit"
    ],
    "images": [
        null,
        null,
        null,
        null
    ],
    "holiday_text": null
}

Then the rest sensor simply has to grab menu_items and I set the state to the date requested:

  json_attributes:
        - menu_items
  value_template: >
        {{ value_json.date }}

Lastly the markdown is very simple for loop to show each menu_items entry with a little logic around to deal with weekends (204 no content response) or no response from endpoint such that state is set to ‘unknown’:

content: >
  <ha-icon icon="mdi:food"></ha-icon><font size=4px>&nbsp; Lunch Menu for
  <b>{{ now().strftime('%A') }}</b></font>

  {% set menuState = states('sensor.ehs_menu_today') %}

  {% if menuState != 'unknown' %}
    {% set menuList = state_attr('sensor.ehs_menu_today', 'menu_items') %}
    {% for item in menuList %} 
    <font size=4px>{{ item }}</font>
    {% endfor %}
  {% else %}
     No menu today
  {% endif %}

Card looks like this for today:

1 Like

Are you able to post all of your code?

For some reason when I try this with the date in the url it works, but when I use {{now().strftime(‘%Y/%m/%d’)}} I get an error stating the requested resource was not found on the server.

If it helps, here’s the code I am testing with:

rest:
  - resource: "https://clarkston.api.nutrislice.com/menu/api/weeks/school/bailey-lake/menu-type/lunch/{{now().strftime('%Y/%m/')}}16"
    sensor:
      - name: "Date Test"
        value_template: "{{ value }}"

Thank you in advance!

The resource: key is for hard-coded URL’s. If you have a template you need to use resource_template: instead.

1 Like

Thank you for the help! I have everything working now.

I was able to reference @dbell68’s example to get a Markdown card working for my kids’ school… I was feeling all successful until it was pointed out that this api doesn’t publish the “alternate entrees.”

So now I’m struggling with getting things to work using @rairai82’s method… Full discretion: I am a complete novice so my capabilities are limited to barely more than copy/paste! Hoping maybe someone can help point me in the right direction…

My REST sensor looks like this:

  - name: PN Lunch Week
    unique_id: paul_norton_lunch_week
    platform: rest
    resource_template: "https://bettendorf.api.nutrislice.com/menu/api/weeks/school/paul-norton/menu-type/lunch/{{now().strftime('%Y/%m/%d')}}?format=json"
    value_template: '1'
    json_attributes:
      - days

As you can see, I literally just took @rairai82’s example and simply changed it to my kids’ local school. The sensor does pull all of the data into the “Days” attribute.
Similarly, I took the same approach to creating a markdown card:

  <b><u>PAUL NORTON LUNCH</b></u><br/>
  {% for day in state_attr('sensor.pn_lunch_week', 'days') | selectattr("menu_items") | selectattr("date", "greaterthan", (now()-timedelta(hours=12)).strftime("%Y-%m-%d")) %}
  {%- if day.date == now().strftime("%Y-%m-%d") -%}<b>TODAY:</b>{{" "}}
  {%- elif day.date == (now()+timedelta(hours=24)).strftime("%Y-%m-%d") -%}<b>TOMORROW:</b>{{" "}}
  {%- else -%} {{- strptime(day.date, "%Y-%m-%d").strftime("%A").upper() -}}:{{" "}}
  {%- endif -%}
  {{  day.menu_items | selectattr("food.food_category","equalto","entree") | slice(2) | first | join('; ', attribute="food.name") }}<br/>
  {% endfor %}

This returns a "UndefinedError: ‘None’ has no attribute ‘food_category’
I’m sure the answer is likely obvious to those with actual knowledge, but I am helpless!