Self-hosted pantry management system โ Track your food inventory, scan barcodes, get AI-powered recipe suggestions, and reduce waste.
โถ Open Live Demo ย ยทย ๐ Project Website ย ยทย ๐ Wiki
The demo runs with mock pantry data. AI features are fully enabled. All write operations are safely sandboxed.
โ ๏ธ Name disambiguation: There is an unrelated iOS app also called EverShelf, developed and published by Joshumi Technologies LLC on the Apple App Store. That application is a completely separate, independent product with no affiliation, association, or collaboration with this open-source project. This repository has no connection to Joshumi Technologies LLC, its products, or its services.
EverShelf has a native Home Assistant integration available on HACS.
Connect your pantry to your smart home in minutes โ no YAML, no manual sensor setup.
What you get:
| 16 sensors | Expiry counts, stock levels by location (pantry / fridge / freezer), shopping list total, AI API usage, last backup timestamp, days to next expiry |
| 6 binary sensors | Expired items, expiring items, expiring today, shopping list active, backup overdue, Bring! connected |
| 5 action buttons | Refresh data, Refresh prices, Suggest Recipe (AI โ result as HA notification), Sync smart shopping, Clear expired rows |
| Shopping list todo | Bidirectional sync โ add, remove, check off items directly from HA |
| Expiry calendar | Every product's expiry date as a native HA calendar event โ works with the calendar card and any calendar automation |
| Quick-add text entity | Type a product name in HA to instantly add it to the shopping list (great for voice assistants / Assist) |
| 6 services | add_to_shopping, mark_used, refresh, suggest_recipe, refresh_prices, clear_expired |
| Auto-discovery | Detected automatically via Zeroconf/mDNS when avahi-daemon runs on the EverShelf host |
| 5 languages | English, Italian, German, French, Spanish |
Requires a self-hosted EverShelf instance. The integration talks directly to your server โ no cloud involved.
Full documentation: ha-evershelf on GitHub
- Export inventory โ Download the full inventory as a UTF-8 CSV (Excel-compatible) or open a print-ready page to save as PDF; export button always visible in the inventory page header
- Barcode scanning โ Scan products with your phone camera using QuaggaJS; last 20 scanned products saved as tappable chips so you can re-select them without rescanning
- AI identification โ Take a photo and let Google Gemini identify the product, with suggestions from your existing inventory; gracefully shows a friendly message when AI quota is exhausted instead of a raw API error
- Smart locations โ Track items across Pantry, Fridge, Freezer, and custom locations
- Expiry tracking โ Automatic shelf-life estimation based on product type and storage
- Opened product tracking โ Reduced shelf-life calculation when packages are opened; opened-product expiry is now also checked when building banner alerts (not just the dashboard section)
- Vacuum-sealed support โ Extended expiry dates for vacuum-sealed items; products sealed under vacuum are only flagged as expired after a configurable grace period past the printed date (
VACUUM_EXPIRY_EXTENSION_DAYS, default 30 days, configurable in.env) - Anomaly detection โ Banner alerts for suspicious quantities and consumption predictions with inline correction; dismiss button now shows the current inventory quantity so the action is unambiguous ("Quantity is correct (2 pcs)")
- Expiry date reading โ Photograph a label and extract the expiry date automatically
- Product identification โ Point your camera at any product for instant recognition
- Existing product matching โ AI scan shows matching products already in your pantry before suggesting new ones
- Storage & shelf-life hint โ When adding a new product, Gemini suggests the optimal storage location and shelf-life in the background; shown as an inline AI badge next to the expiry estimate
- Recipe generation โ Get personalized recipes based on what's in your pantry; streams live via Server-Sent Events so results appear as they are generated
- Smart chat assistant โ Ask questions about your inventory, get cooking tips
- Shopping suggestions with tips โ AI-powered purchase recommendations, each enriched with a short practical buying/storing tip
- Anomaly explanation โ "Explain" button on anomaly banners explains in plain language why a discrepancy likely occurred and what to do
- Model fallback โ All AI endpoints try
gemini-2.5-flashfirst and fall back togemini-2.0-flashautomatically - Graceful no-key state โ When no Gemini key is configured, AI entry points show a friendly message; the header button is visually greyed with an amber dot
- Bring! integration โ Sync with the Bring! shopping list app
- Generic shopping names โ Products are grouped by type (e.g. "Milk", "Cold cuts", "Cooking cream") rather than brand, keeping the Bring! list clean and consolidated
- Smart predictions โ Know what you'll need before you run out
- Auto-add on depletion โ When a product reaches zero the app adds it to Bring! automatically, no confirmation needed
- Auto-remove on scan โ Products are removed from the shopping list when scanned in - Auto-migration โ Items already on the Bring! list are silently renamed to their generic name in the background (throttled, runs on list load)
- Catalog coverage โ All product types resolve to a German Bring! catalog key for icon and category display in the Bring! app
- โป๏ธ Zero-waste tips โ For each cooking step that generates reusable scraps (peels, cooking water, egg whites, cheese rinds, bread crusts, vegetable tops, etc.), a dismissible โป๏ธ tip card appears with a practical reuse idea; tips are generated by Gemini as part of the recipe at no extra API cost; opt-in toggle in Settings (default OFF)
- Step-by-step guidance โ Follow recipes with a hands-free cooking interface
- Text-to-Speech โ Voice readout of recipe steps; supports browser Web Speech API, native Android TTS (kiosk), or a custom REST endpoint (Home Assistant, etc.); retries voice loading for up to 10 seconds with a fallback refresh button; TTS activates automatically without requiring the global TTS setting to be enabled
- Auto-read on navigate โ Each step is read aloud automatically when you tap Next or Previous; the first step is read when entering cooking mode
- Timer voice alerts โ 10-second countdown warning spoken aloud before each timer expires; expiry announced vocally when time is up
- Recipe completion โ "Bon appรฉtit!" announced via TTS when the last step is confirmed
- Built-in timer โ Automatic timer suggestions based on recipe instructions
- Ingredient tracking โ Mark ingredients as used during cooking; leftover quantities prompt a "move to another location" flow
- Waste tracking โ Monitor consumed vs. wasted products over 30 days
- Anti-waste report โ Personalised waste rate vs. national average with annual kg estimate; shown above the expiring-items list
- Expiry alerts โ Visual warnings for expired and soon-to-expire items
- Opened products panel โ Tracks partially-used items; expiry is recalculated from the opening date using AI (Gemini) + per-category rule fallback; whole sealed packages always keep their original manufacturer expiry; conf items with mixed whole + fractional units are shown as two separate entries
- Freezer shelf-life โ Granular per-product estimates (USDA/EFSA): fish 120 d, poultry 270 d, whole red-meat cuts 365 d, mince 120 d, vegetables/fruit 270 d, generic 180 d; AI + cache still take priority over rules
- Safety ratings โ Smart assessment of expired product safety (by category and location); expired unsafe items shown with a red danger banner and a discard action as the primary action
- Expired product banner โ Products that have passed their effective shelf-life (including opened-product reduced expiry) appear in the top notification banner; icon, colour and title adapt to the actual safety level (โ green for safe, ๐ amber to check, ๐ซ red for danger); high-risk items get a prominent discard action
- Quick recipe bar โ One-tap recipe suggestion using expiring products
- Anomaly banner โ Scrollable banner with suspicious quantities and consumption prediction mismatches, with one-tap correction or inline edit
- Expired/expiring alerts โ Priority-sorted banner notifications for expired and soon-to-expire products with use, throw, edit, and dismiss actions
- Swipe navigation โ Touch swipe or tap arrows/dots to browse banner notifications
- Quick-access buttons โ Recently used and most popular products shown on the inventory page for fast access
- Dark mode โ Three modes: Light, Dark, and Auto (time-based: dark from 20:00 to 07:00, light otherwise); applies immediately without page reload; auto mode re-evaluates every 5 minutes, so night/day transitions happen automatically even on always-on kiosk displays; theme is applied before the first render to prevent a white flash
- Global settings tab โ A dedicated โ๏ธ General tab groups all system-wide settings (language, currency, theme, screensaver, zero-waste tips, export) at the top of the Settings panel
- Automatic cleanup โ Recipes older than
RECIPE_RETENTION_DAYS(default 7) and transactions older thanTRANSACTION_RETENTION_DAYS(default 7) are deleted automatically on every cron cycle; SQLiteVACUUMruns after each cleanup to keep the file compact - Manual cleanup โ Trigger immediately via
GET /api/?action=db_cleanup - Compact by default โ Fresh installs stay small; large accumulated databases shrink back to a few hundred KB within one cron cycle
- Mobile-first design โ Optimized for phones, works on tablets and desktop
- Installable โ Add to home screen for a native app experience
- Multi-device โ All user data (shopping tags, pinned items, location preferences, scan history) is stored server-side in SQLite and shared across every device on the same instance; no data is siloed in a single browser's localStorage
- Automatic detection โ Full-screen overlay appears immediately on network loss; shows a "Continue offline" button after 3 s, and auto-enters offline mode after 8 s
- Local inventory cache โ Inventory is synced to
localStorageat every startup and on each successful API call; the offline view always reflects the last known state - Write queue โ Add, use, update and delete operations performed while offline are queued locally and synced to the server automatically on reconnect (including after a page refresh)
- Optimistic UI โ Queued writes are applied immediately to the local cache so the interface stays responsive
- Offline-computed stats โ Expiring and expired items are derived client-side from the cache; dashboard stat cards show real counts instead of zeros
- AI/network sections hidden โ Anti-waste chart, nutrition analysis, recipe generator, price fetching, and Gemini chat are hidden in offline mode; the inventory, history, and manually-managed shopping list remain fully functional
- Broken image fallback โ External product images (Open Food Facts, etc.) that fail to load are replaced with a neutral grey placeholder, keeping the layout intact
- Startup recovery โ If the page is refreshed while operations are queued, they are detected and synced automatically on the next successful startup
- Buffered error reporting โ
remoteLogandreportErrorcalls made while offline are stored locally and flushed to the server (and to GitHub issues) when the connection is restored
- Bluetooth gateway โ Connects a BLE smart scale to EverShelf via local WebSocket
- SSE relay โ Server-side relay avoids mixed-content (HTTPSโWS) issues
- Auto-discovery โ Server scans LAN to find the gateway automatically
- Auto weight reading โ When adding/using a product with unit g/ml, weight fills automatically
- 10g threshold โ Ignores readings that haven't changed enough between products - Duplicate-reading prevention โ Server-side 12-second dedup window rejects a second scale-triggered deduction of the same product, guarding against BLE multi-fire- ml conversion hint โ Shows "weight in grams โ will be converted to ml" when product unit is ml
- Stability + auto-confirm โ 10s stable wait + 5s countdown before confirming
- Real-time status โ Scale connection indicator always visible in the header
- Multi-protocol โ Supports Bluetooth SIG Weight Scale, Body Composition, Xiaomi Mi Scale 2 and 100+ models
- Built into kiosk (v1.6.0+) โ BLE gateway runs as an integrated foreground service inside the EverShelf Kiosk app; no separate APK needed.
- Dedicated tablet app โ Full-screen WebView wrapper for wall-mounted kitchen tablets
- True kiosk lock โ Screen pinning blocks home/recent buttons
- Setup wizard โ 6-step guided configuration (language, welcome, permissions, server URL, BLE scale scan, screensaver, summary)
- Smart auto-discovery โ Scans the LAN in parallel (60 threads, TCP pre-check, ports 80/443/8080/8443) with real-time UI feedback; correctly identifies the device's Wi-Fi/Ethernet subnet (VPN and cellular interfaces are filtered out)
- Built-in BLE scale gateway โ
GatewayServiceforeground service; BLE scanning + WebSocket server:8765run directly inside the kiosk app. Select your scale in step 5 of the wizard โ no external app required - Scale auto-configuration โ After selecting the BLE device, the wizard writes
scale_enabledandscale_gateway_url=ws://127.0.0.1:8765to the server automatically - Camera & mic permissions โ Full hardware access for barcode scanning and voice; grant button transforms to a green confirmation after granting
- Native TTS bridge โ Cooking mode voice readout uses the Android TextToSpeech engine directly, bypassing Web Speech API voice limitations; no offline voice packs required
- Hard refresh โ โป button clears WebView cache to pick up web app updates
- Update notifications โ Checks GitHub releases every 6h, shows banner when updates available
- SSL support โ Accepts self-signed certificates
- Android kiosk app โ
evershelf-kiosk/โ downloadable APK
- Web server with PHP 8.0+ (Apache or Nginx)
- PHP extensions:
pdo_sqlite,curl,mbstring,json - HTTPS recommended (required for camera access on mobile)
# 1. Clone the repository
git clone https://github.com/dadaloop82/EverShelf.git
cd EverShelf
# 2. Create configuration file
cp .env.example .env
nano .env
# 3. Start with Docker Compose
docker compose up -d
# โ Open http://localhost:8080# 1. Clone the repository
git clone https://github.com/dadaloop82/EverShelf.git
cd EverShelf
# 2. Create configuration file
cp .env.example .env
# 3. Set permissions
chmod 755 data/
chmod 664 data/.gitkeep
chown -R www-data:www-data data/
# 4. Edit your configuration
nano .env# Required for AI features (get a key at https://aistudio.google.com/app/apikey)
GEMINI_API_KEY=your_api_key_here
# Optional: Bring! shopping list integration
BRING_EMAIL=your_email@example.com
BRING_PASSWORD=your_password
# Optional: Text-to-Speech for cooking mode
TTS_URL=http://your-home-assistant:8123/api/events/tts_speak
TTS_TOKEN=your_long_lived_token
TTS_ENABLED=true
# Optional: DB retention and cleanup (applied automatically each cron cycle)
RECIPE_RETENTION_DAYS=7 # delete recipe plans older than N days
TRANSACTION_RETENTION_DAYS=7 # delete stock transactions older than N days
# Optional: Vacuum-sealed expiry grace period
VACUUM_EXPIRY_EXTENSION_DAYS=30 # extra days before vacuum-sealed items are flagged expired
# Optional: Gemini cost rates (USD per million tokens, for the Info tab cost estimate)
GEMINI_COST_25F_IN=0.15
GEMINI_COST_25F_OUT=0.60
GEMINI_COST_20F_IN=0.10
GEMINI_COST_20F_OUT=0.40
# Optional: Security โ protect the save_settings endpoint
# Set a strong random string; the Settings UI will ask for it before saving
SETTINGS_TOKEN=
# Optional: Demo mode โ block all write operations at the router level
DEMO_MODE=false
# Optional: Logging
# LOG_LEVEL sets the minimum severity written to disk (DEBUG / INFO / WARN / ERROR)
# DEBUG also logs every SQL query executed against the database
LOG_LEVEL=INFO
LOG_ROTATE_HOURS=24 # hours before opening a new log file (default: 24)
LOG_MAX_FILES=14 # maximum number of rotated files to keep (default: 14)Apache (.htaccess)
The app works out of the box with Apache if placed in the web root or a subdirectory. Make sure mod_rewrite is enabled and AllowOverride All is set.
<Directory /var/www/html/evershelf>
AllowOverride All
Require all granted
</Directory>Nginx
server {
listen 80;
server_name your-server.local;
root /var/www/html/evershelf;
index index.html;
location /api/ {
try_files $uri $uri/ =404;
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
# Deny access to sensitive files
location ~ /\.env { deny all; }
location ~ /data/ { deny all; }
location ~ /backup\.sh { deny all; }
}Camera access requires HTTPS on most mobile browsers. Options:
- Let's Encrypt with Certbot (for public-facing servers)
- Self-signed certificate (for local network only)
- Reverse proxy (e.g., Caddy, Traefik) with automatic TLS
Set up a cron job for smart shopping predictions:
# Run every 5 minutes
*/5 * * * * php /path/to/evershelf/api/cron_smart_shopping.php >> /path/to/evershelf/data/cron.log 2>&1The included backup.sh creates local daily backups of your database:
# Run daily at 3 AM
0 3 * * * /path/to/evershelf/backup.shEverShelf supports automatic daily backups to Google Drive via OAuth 2.0. This works on any server, including private IP / local network setups (no public domain required).
Setup:
- Go to console.cloud.google.com and select or create a project.
- Enable the Google Drive API (
APIs & Services โ Enable APIs โ Google Drive API). - Go to
APIs & Services โ Credentials โ Create Credentials โ OAuth client ID. - Application type: Web application.
- Add
http://localhostas an Authorized Redirect URI (this is the key โ it works even without a real domain). - Copy Client ID and Client Secret into EverShelf Settings โ Backup.
- Enter your Google Drive Folder ID (the last part of the folder URL).
- Click Authorize with Google and sign in.
- The browser will redirect to
http://localhostand may show a connection error โ this is expected. Copy the full URL from the address bar (e.g.http://localhost/?code=4%2F0A...) and paste it into the field that appears in EverShelf, then click Submit.
Note: While the OAuth app is in Testing status in Google Cloud Console, you must add your Google account as a test user under
APIs & Services โ OAuth consent screen โ Test users.
evershelf/
โโโ index.html # Single-page application (SPA)
โโโ manifest.json # PWA manifest
โโโ .env.example # Configuration template
โโโ backup.sh # Local database backup script
โโโ LICENSE # MIT License
โ
โโโ api/
โ โโโ index.php # Main API router (all endpoints)
โ โโโ database.php # SQLite schema, migrations, helpers
โ โโโ cron_smart_shopping.php # Background job for predictions
โ
โโโ assets/
โ โโโ css/style.css # All application styles
โ โโโ js/app.js # All application logic
โ โโโ img/ # Static images
โ
โโโ data/ # Runtime data (gitignored)
โโโ evershelf.db # SQLite database (auto-created)
โโโ backups/ # Local DB backups
โโโ *.json # Token/cache files
evershelf-scale-gateway/ # โ๏ธ Android BLE gateway [DEPRECATED โ integrated into kiosk v1.6.0+]
โโโ README.md # Deprecation notice + legacy docs
โโโ app/src/ # Kotlin Android source (WebSocket + BLE)
evershelf-kiosk/ # ๐บ Android kiosk app (add-on)
โโโ README.md # Setup & feature docs
โโโ app/src/ # Kotlin Android source (WebView wrapper)
| Category | Action | Method | Description |
|---|---|---|---|
| Products | search_barcode |
GET | Find product by barcode |
lookup_barcode |
GET | Look up barcode on Open Food Facts | |
product_save |
POST | Create or update a product | |
products_list |
GET | List all products | |
| Inventory | inventory_list |
GET | List inventory items |
inventory_add |
POST | Add product to inventory | |
inventory_use |
POST | Use/consume from inventory | |
inventory_summary |
GET | Count by location | |
| AI | gemini_identify |
POST | Identify product from photo |
gemini_expiry |
POST | Read expiry date from photo | |
gemini_chat |
POST | Chat with AI assistant | |
generate_recipe |
POST | Generate recipe from inventory | |
gemini_product_hint |
POST | Storage location + shelf-life hint | |
gemini_shopping_enrich |
POST | Enrich shopping suggestions with tips | |
gemini_anomaly_explain |
POST | Plain-language anomaly explanation | |
| Shopping | bring_list |
GET | Get Bring! shopping list |
bring_add |
POST | Add items to Bring! | |
smart_shopping |
GET | Smart shopping predictions | |
| Settings | get_settings |
GET | Get server configuration |
save_settings |
POST | Update server configuration |
- Credentials are stored in
.env(server-side, never committed to Git) - Database stays local โ never pushed to remote repositories
- API keys are never exposed to the browser โ
get_settingsreturns only boolean flags (gemini_key_set,settings_token_set), never raw key values - Settings write protection โ set
SETTINGS_TOKENin.envto require a secret token (X-Settings-Tokenheader) for allsave_settingscalls; validated withhash_equalsto prevent timing attacks - Demo / public mode โ set
DEMO_MODE=trueto block all write operations at the PHP router level before any business logic runs - The API uses parameterized SQL queries (PDO prepared statements) against injection
- Input validation on all inventory operations (quantity bounds, location whitelist)
- Consider adding reverse-proxy authentication (e.g. Authelia, Nginx
auth_basic) if the server is accessible from the internet
# Run PHP's built-in server for local development
php -S localhost:8080 -t /path/to/evershelf
# Check PHP syntax
php -l api/index.php
php -l api/database.phpThe application uses no build tools โ edit files directly and refresh.
Feature requests, bug reports and planned work are tracked in the EverShelf Roadmap GitHub Project.
The app supports multiple languages via JSON translation files in the translations/ folder.
| Language | Status |
|---|---|
| ๐ฎ๐น Italian (it) | โ Complete (base) |
| ๐ฌ๐ง English (en) | โ Complete |
| ๐ฉ๐ช German (de) | โ Complete |
| ๐ซ๐ท French (fr) | โ Complete |
| ๐ช๐ธ Spanish (es) | โ Complete |
Want to add your language? See the Translation Guide โ just copy translations/it.json, translate the values, and submit a PR!
Contributions are welcome! See CONTRIBUTING.md for detailed guidelines.
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Commit your changes (
git commit -m 'Add my feature') - Push to the branch (
git push origin feature/my-feature) - Open a Pull Request
EverShelf is a community project and contributions of any size are welcome!
Translations are just JSON files. No coding, no setup โ fork โ edit โ PR.
translations/
โโโ it.json โ
Italian (base)
โโโ en.json โ
English
โโโ de.json โ
German
โโโ fr.json โ
French
โโโ es.json โ
Spanish
โโโ pt.json โ Portuguese โ wanted!
โโโ nl.json โ Dutch โ wanted!
โโโ ... โ Your language here!
๐ See issue #93 to claim a language.
| What | Skill needed |
|---|---|
| ๐ Report a bug | None |
| ๐ Improve the wiki | Markdown |
| ๐ Add a translation | JSON editing |
| ๐จ Fix a CSS/UI issue | CSS / HTML |
| โ๏ธ Implement a feature | PHP / JS |
| โญ Star the repo | Clicking |
๐ Browse help wanted issues for good starting points.
Read CONTRIBUTING.md for the full guide (branch naming, code style, how to run locally).
Join the conversation in GitHub Discussions:
- Vote on upcoming features โ tell us what to build next
- Show your setup โ share your kitchen kiosk
- Ask questions โ get help from the community
This project is licensed under the MIT License โ see the LICENSE file for details.
Stimpfl Daniel โ evershelfproject@gmail.com
- Website: evershelfproject.dadaloop.it
- GitHub: @dadaloop82
For a live walkthrough with real data and full AI enabled, visit the live demo โ no installation required.
Want to contribute additional screenshots? See CONTRIBUTING.md โ PRs welcome!
