Browse documentation

🧭 Start Here

Blackcap Overview ✨ Blackcap Feature Catalog Installation First Run

🚀 Deploy Blackcap

Platform Stacks and Raspberry Pi Hardware Raspberry Pi Deployment Raspberry Pi Client Services GCP Deployment Application Updates Environment Variables and Secrets Reverse Proxy and TLS Background Jobs and Schedules

🛠️ Administer Blackcap

Organizations Users, Permissions, and Authentication Configuration Workspace Backups and Restore Database Administration Regression Testing Performance and Job Status Audit, Access Activity, and Logging GeoIP and Access Location Data Retention and Purge Support Requests API Tester and Postman Instance Reporting

🍽️ Use Recipes

Recipes and the Recipe Library Recipe Import and Discovery Recipe Editing and Cache Artifacts Recipe Sharing Social Recipe Import AI Recipe Image Generation

📅 Plan Meals

Meal Planner

🛒 Use Shopping Lists

Shopping Lists and Shop a List External and Household Shopping 🧩 Chrome Extension Shop With

🧺 Manage Kitchen Inventory

🧺 Kitchen Inventory

🖥️ Use Displays

Displays and Connections Assigning and Scheduling Display Content Remote Pi Client E-Ink Rendering Menu Refresh and Rendering Noun Project Footer Images

🧑‍🍳 Cook with Let’s Cook

🧑‍🍳 Let’s Cook 🧑‍🍳 Let’s Cook Controls and Timers

🤖 Use and Administer AI

🤖 AI in Blackcap 🤖 AI Providers and Connections 🤖 AI Seeds and Usage

🧩 Use the Chrome Extension

🧩 Blackcap Chrome Extension 🧩 Chrome Extension Recipe Capture 🧩 Chrome Extension Shop With 🧩 Chrome Extension Release and Privacy

🎮 Play Games

🎮 Games and Trivia

🔌 Integrations

Email Integration Cloud Storage Integrations Voice Assistants Shop With Integrations Authentication Providers

⚙️ Develop Blackcap

Application Architecture Database Service and Data Access SQLite and PostgreSQL Database Migrations Background Job Architecture Testing API Architecture Security and Organization Scoping UI, Icons, and Documentation Assets Blackcap-Safe Emoji Documentation Standards Terminology

⚠️ Troubleshoot Blackcap

⚠️ Troubleshooting Deployment Troubleshooting Display Troubleshooting Recipe Import Troubleshooting 🤖 AI Troubleshooting Backup Troubleshooting Database Troubleshooting Diagnostic Organization Clones Support Requests

Menu Refresh and Rendering

Audience: User, Org Admin, Deployment Administrator, Developer Related: Overview · Eink Rendering · Configuration · Background Jobs And Schedules

Menu mode renders an organization’s configured menu source for supported displays. Scheduled refresh performs a lightweight source probe, skips expensive browser rendering when unchanged, groups compatible display work, and publishes display-specific output.

A missing menu URL is a configuration condition, not a reason to launch Playwright. Noun Project footer rules and e-ink render settings are applied through the current organization and render profile.

Blackcap Pi supports two menu-source styles:

  • published Google Sheets, especially pubhtml menu sheets;
  • compatible rich HTML webpages that can be captured by Playwright.

Playwright remains the authoritative image-export path, but the scheduled hourly refresh now performs a lightweight source probe before launching a browser. This keeps Raspberry Pi Zero 2 deployments responsive while preserving accurate rendering when a menu actually changes.

Scheduled refresh flow

Unbound inky_menu.py is the menu refresh dispatcher. It no longer assumes the default organization or the default display.

  1. Find active displays whose current content is Menu.
  2. Group them by organization.
  3. Probe the organization's configured menu URL once.
  4. If the source fingerprint matches the last successfully rendered baseline, skip that organization without launching Playwright.
  5. If the source changed, the probe is uncertain, or --full-refresh is used, render by organization + effective render profile.
  6. Compare/update each display using its own display-preview artifacts.

A successful unchanged run should show 0 browser capture(s) in the summary.

Source probe state

The source probe records hidden org-scoped JSON under menu.source_probe_state. The setting is registered by migration 065_menu_source_probe_state.sql and is accessed through the settings/DB service layer.

The JSON stores details such as:

  • menu URL;
  • probe URL when a stable alternate is available;
  • HTTP status/ETag/Last-Modified/Content-Length when provided;
  • body length/hash;
  • normalized source fingerprint;
  • check timestamps;
  • last rendered baseline.

Published Google Sheet pubhtml URLs use a stable spreadsheet-style fingerprint where possible because Google's raw published HTML wrapper can change between requests even when the visible menu does not. Generic HTML pages use normalized visible text so layout wrapper noise is less likely to trigger a false refresh.

Full refresh

--full-refresh bypasses the skip decision and runs the Playwright/render path, but it still records the current source-probe JSON. This gives the next smart refresh a current baseline.

/home/pi/inky_env/bin/python3 /home/pi/Blackcap-Pi/inky_menu.py --full-refresh

Grouped rendering

When rendering is required, displays are grouped by organization and effective render profile. Displays that differ in resolution, color support, image support, display type, or output profile do not share the same final image.

Examples:

  • 960x680 B&W e-ink target;
  • 1920x1080 color hosted receiver;
  • 1280x720 color mock/rich preview.

The browser capture and image processing honor the target profile so a 1920x1080 color display does not receive a small B&W e-ink image.

Menu footer matching uses organization-scoped rows in noun_project_rules. The old noun_cache/NounProjectWords.csv file is no longer active configuration. noun_cache/ remains a filesystem cache for downloaded icon assets only, with child organizations using folders such as noun_cache/org_<organization_id>/. Schema migration 201 backfills the standard rule set when the Default organization has no rows, covering installations that had already advanced beyond the earlier foundation-seed migration.

For a menu such as:

Wednesday: Meatball Sandwich with Salad
Saturday: Swedish Meatballs and Egg Noodles with Salad
Sunday: Chicken Wraps with Chips

OCR and normalization may identify useful concepts such as chicken, bread, sandwich, noodles, and salad. Organization-scoped rules map approved words or patterns to Noun Project search terms. Unknown/noise words are skipped, icons are cached after first use, and the footer is rebuilt from the current matching rules.

Footer placement can be Menu + Meal Plan, Menu only, Meal Plan only, or None. Organization admins can manage their organization’s keyword rules without seeing the shared platform API credential. The Default organization uses the root noun_cache/; child organizations use scoped folders and may reuse compatible cached icons from the Default cache.

Assigning Menu content from the UI behaves like Return to Default when Menu is the configured default content. It restores the last rendered menu image for that display instead of running smart refresh. Scheduled inky_menu.py is responsible for checking whether the menu source changed and updating displays currently showing Menu.

Locking

Top-level inky_menu.py runs create a menu-priority request and acquire the shared scheduler lock. This includes cron/unbound runs and UI/manual bound runs such as:

/home/pi/inky_env/bin/python3 /home/pi/Blackcap-Pi/inky_menu.py --org-id org_... --display-id disp_...

Child workers launched by a locked parent run with BLACKCAP_SCHEDULER_LOCK_HELD=1 so they do not deadlock their parent. The display-content refresh runner yields while menu refresh is pending or active. The e-ink hardware lock remains separate and is still required around local display writes.

On this page