REST - Nutrislice JSON Values

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:

2 Likes

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!

1 Like

Hey I just drove through Bettendorf today, small world. You probably have already figured it out by now, but here is the fix you were looking for just in case:

Change:

{{  day.menu_items | selectattr("food.food_category","equalto","entree") | slice(2) | first | join('; ', attribute="food.name") }}<br/>

To:

 {{  day.menu_items | selectattr("food.name", 'defined') | slice(1) | first | join('; ', attribute="food.name") }}<br/>

The defined part is important because it will error if the value doesn’t exist. Also changing slice(2) to slice(1) gives all the results. Otherwise it was cutting off some menu items for me. I also went with food.name because at least for our school the food category is never used. I took your code and built on it. Here is what I am using:

List meals for entire week:

{% for day in state_attr('sensor.school_lunch', 'days') |
selectattr("menu_items") | selectattr("date", "greaterthan",
(now()-timedelta(hours=12)).strftime("%Y-%m-%d")) %}

{%- if day.date == now().strftime("%Y-%m-%d") -%}<b><u>TODAY</b></u>:{{" "}}

{%- elif day.date == (now()+timedelta(hours=24)).strftime("%Y-%m-%d")
-%}<b><u>TOMORROW</b></u>:{{" "}}{{"
"}}

{%- else -%}<b><u> {{- strptime(day.date, "%Y-%m-%d").strftime("%A").upper() -}}</b></u>:{{"
"}}

{%- endif -%}

{% for result in day.menu_items | selectattr("position", 'defined')  %}

{%- if result.text != "" -%}
<br><b><i>{{ result.text }}</b></i><br>
{%- endif -%}

{%- if result.food is not none -%}
{{ result.food.name }}<br>
{%- endif -%}

{% endfor %}

<br>

{% endfor %}

This method lists each item in order so that I could include the section titles as well (ex: alternate). It ends up looking like this:

Screenshot_9-9-2024_121926_localha.czerkacorp.com

I also created a grid card to show the lunch menu with pictures for the next two days. I have the screen real estate so mine is larger than some people may want. I set it to show today and tomorrow if it is before 8AM or to show the next two days if it is after 8AM. I also hide the whole thing on Saturdays:

square: false
type: grid
columns: 2
cards:
  - type: markdown
    content: |-
      <center>
      {% for day in state_attr('sensor.school_lunch', 'days') |
      selectattr("menu_items") | selectattr("date", "equalto",
      now().strftime("%Y-%m-%d")) %}

      <b><u>TODAY</b></u>


      {% for result in day.menu_items | selectattr("position", 'defined')  %}

      {%- if result.text != "" -%}
      <font color='yellow'><br><b><i>{{ result.text }}</b></i><br><br></font>
      {%- endif -%}

      {%- if result.food is not none -%}
      <img src='{{ result.food.image_url }}' width='150' ><br>
      {%- endif -%}

      {%- if result.food is not none -%}
      {{ result.food.name }}<br><br>
      {%- endif -%}

      {% endfor %}

      <br>

      {% endfor %}
      </center>
    visibility:
      - condition: state
        entity: binary_sensor.nutrislice_before_8
        state: 'on'
  - type: markdown
    content: |-
      <center>
      {% for day in state_attr('sensor.school_lunch', 'days') |
      selectattr("menu_items") | selectattr("date", "equalto",
      (now()+timedelta(hours=24)).strftime("%Y-%m-%d")) %}

      <b><u>TOMORROW</b></u>


      {% for result in day.menu_items | selectattr("position", 'defined')  %}

      {%- if result.text != "" -%}
      <font color='yellow'><br><b><i>{{ result.text }}</b></i><br><br></font>
      {%- endif -%}

      {%- if result.food is not none -%}
      <img src='{{ result.food.image_url }}' width='150' ><br>
      {%- endif -%}

      {%- if result.food is not none -%}
      {{ result.food.name }}<br><br>
      {%- endif -%}

      {% endfor %}

      <br>

      {% endfor %}
      </center>
  - type: markdown
    content: >+
      <center>

      {% for day in state_attr('sensor.school_lunch', 'days') |

      selectattr("menu_items") | selectattr("date", "equalto",

      (now()+timedelta(hours=48)).strftime("%Y-%m-%d")) %}


      <b><u> {{- strptime(day.date, "%Y-%m-%d").strftime("%A").upper()
      -}}</b></u>



      {% for result in day.menu_items | selectattr("position", 'defined')  %}


      {%- if result.text != "" -%}

      <font color='yellow'><br><b><i>{{ result.text }}</b></i><br><br></font>

      {%- endif -%}


      {%- if result.food is not none -%}

      <img src='{{ result.food.image_url }}' width='150' ><br>

      {%- endif -%}


      {%- if result.food is not none -%}

      {{ result.food.name }}<br><br>

      {%- endif -%}


      {% endfor %}


      <br>


      {% endfor %}

      </center>

    visibility:
      - condition: state
        entity: binary_sensor.nutrislice_before_8
        state: 'off'
title: School Lunch
visibility:
  - condition: state
    entity: sensor.day_of_the_week
    state_not: '6'

I’m no pro so there may be a better way of doing this, but it worked for me. Thanks for sharing your part!

@AdmiralAckbar This is really helpful, thank you for sharing your work!

As someone who is just getting my fingers into this kind of thing I wonder if I could ask:

How would you change the following code (just copied/pasted and changed the sensor) so that it only returns items where "food_category" = "entree" and then ignoring the first two items because they are for breakfast.

type: markdown
content: >-
{% for day in state_attr('sensor.bcps_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><u>TODAY</b></u>:{{" "}}

{%- elif day.date == (now()+timedelta(hours=24)).strftime("%Y-%m-%d") -%}<b><u>TOMORROW</b></u>:{{" "}}{{" "}}

{%- else -%}<b><u> {{- strptime(day.date, "%Y-%m-%d").strftime("%A").upper() -}}</b></u>:{{" "}}

{%- endif -%}

{% for result in day.menu_items | selectattr("position", 'defined')  %}

{%- if result.text != "" -%}

<br><b><i>{{ result.text }}</b></i><br>

{%- endif -%}

{%- if result.food is not none -%}

{{ result.food.name }}<br>

{%- endif -%}

{% endfor %}

<br>

{% endfor %}

I’ve tried splicing and joining and adding ifs and stuff but haven’t gotten anything to output yet.

1 Like

funny you ask this because i was wondering something very similar (excluding breakfast), and also for the same district! following