Skip to content

School Lunch Menu

Daily school lunch menus from all configured schools, surfaced on any Lovelace dashboard. An at-a-glance card shows tomorrow's entrees for selected schools; a detail popup provides a full weekly view, month-by-month calendar navigation, and per-school selection settings.

Overview

The school_lunch_app AppDaemon app fetches menus from the School Nutrition and Fitness platform. Multiple schools can be configured in a single app instance. Menus refresh automatically every morning at 5:00 AM and are published to a Home Assistant virtual sensor that both Lovelace cards read from.

Data flow

School Nutrition and Fitness API
  ├─ downloadMenu.php (HTTP 302)  — resolves download ID → MongoDB ObjectId
  └─ GraphQL API                  — returns structured menu items per day
       + content overlay REST API — adds holiday / early-release notices
  school_menu provider (aiohttp client)
       │  returns MenuMonth dataclasses
  school_lunch_app (AppDaemon)
       │  publishes structured JSON to
  sensor.school_lunch_menu          ← Lovelace cards read from here
  input_text.school_lunch_selected_schools
  school-lunch-card.js              — compact at-a-glance card
  school-lunch-detail-card.js       — full detail popup card

Cards

At-a-glance card (custom:school-lunch-card)

A compact card showing the next relevant lunch menu for all selected schools. The display depends on the configurable show_tomorrow_after cutoff time:

Time of day Weekday Header shown
Before cutoff Mon–Fri Today's Lunch
After cutoff Mon–Thu Tomorrow's Lunch
After cutoff Friday Monday's Lunch
Any time Sat/Sun Monday's Lunch

Menu items are split into numbered options (main entree choices) and an Includes line for daily items like fruit and milk (auto-classified by the app based on which items appear on 75%+ of days).

School selection persists in input_text.school_lunch_selected_schools so the choice survives reloads and is shared across all dashboard instances.

type: custom:school-lunch-card
menu_entity: sensor.school_lunch_menu
selected_entity: input_text.school_lunch_selected_schools

Detail card (custom:school-lunch-detail-card)

A popup/dialog card with three tabs:

  • This week — full item list (entrees and sides) for each school day in the current week
  • Calendar — month view per school with prev/next month navigation
  • Settings — checkboxes to choose which schools appear in the at-a-glance card

Month navigation sends a fetch_month command via script.school_lunch_relay to fetch adjacent months on demand without a full refresh.

type: custom:school-lunch-detail-card
menu_entity: sensor.school_lunch_menu
selected_entity: input_text.school_lunch_selected_schools
relay_script: school_lunch_relay

Relay commands

Card → AppDaemon communication uses the standard relay script pattern:

Command Triggered by Effect
select_schools Settings tab save Updates input_text.school_lunch_selected_schools; at-a-glance card updates immediately
fetch_month Prev/next month button Fetches the adjacent month for a specific school and updates its entry in the sensor attributes

Configuration

The app is configured in apps-prod.yaml. Key fields:

Key Description
sid Site ID from the school district's URL (numeric string)
menus List of {name, download_id} — one entry per school
default_selected School names pre-selected in the at-a-glance card
show_tomorrow_after HH:MM:SS cutoff time — before this, cards show today's lunch; after, tomorrow's (default "15:00:00")

See appdaemon/apps/school_lunch_app/README.md for the full configuration reference, sensor attribute schema, relay command payload format, and manual setup steps (Lovelace resource registration).

Holiday and closure handling

Days with no menu items (holidays, school closures, early releases) appear as notice entries in the calendar view. The school_menu provider fetches content overlay data from the API and maps announcement text (NO SCHOOL, EARLY RELEASE, etc.) to the correct weekday using calendar grid geometry. Notice days are shown in the calendar with their announcement text in place of menu items.