fix: comprehensive shopping name audit + README update

Shopping name system hardening (api/index.php):
- phraseMap: added sugar subtypes (zucchero a velo), passato di verdure/patate
  → Verdure, aroma phrases → Ingredienti Spezie, farina 00 → Farina,
  explicit brodo subtypes; added ordering comment on 'farina integrale'
- keywordMap: added 'lievito' → Lievito, 'aroma' → Ingredienti Spezie
  single-token fallbacks

DB migration (sqlite3 direct):
- farina integrale / Farina integrale di grano tenero → Farina integrale (3 rows)
- Prodotto sconosciuto (Belbake, it:farina-integrale) → Farina integrale
- Zucchero di canna → Zucchero di canna
- Passato di patate e carote → Verdure (it's a blended veg purée)
- Aroma mandorla per dolci → Ingredienti Spezie (consistent with other aromas)
Total: 11 products re-classified

README.md:
- Shopping List: added generic shopping name feature + auto-add on depletion
- Cooking Mode: updated TTS description (browser / native Android / REST)
- Kiosk: added native TTS bridge bullet
- Roadmap: checked off 3 new completed items
This commit is contained in:
dadaloop82
2026-04-27 12:17:11 +00:00
parent 1a73ed91dd
commit fd5ff00d82
2 changed files with 28 additions and 6 deletions
+8 -2
View File
@@ -33,15 +33,17 @@
### 🛒 Shopping List ### 🛒 Shopping List
- **Bring! integration** — Sync with the [Bring!](https://www.getbring.com/) shopping list app - **Bring! integration** — Sync with the [Bring!](https://www.getbring.com/) shopping list app
- **Generic shopping names** — Products are grouped by type ("Latte", "Affettato", "Panna da cucina") rather than brand, keeping the Bring! list clean and consolidated
- **Smart predictions** — Know what you'll need before you run out - **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-remove on scan** — Products are removed from the shopping list when scanned in
- **DupliClick integration** — Online grocery ordering (Gruppo Poli) - **DupliClick integration** — Online grocery ordering (Gruppo Poli)
### 🍳 Cooking Mode ### 🍳 Cooking Mode
- **Step-by-step guidance** — Follow recipes with a hands-free cooking interface - **Step-by-step guidance** — Follow recipes with a hands-free cooking interface
- **Text-to-Speech** — Voice readout of recipe steps (configurable TTS endpoint) - **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.)
- **Built-in timer** — Automatic timer suggestions based on recipe instructions - **Built-in timer** — Automatic timer suggestions based on recipe instructions
- **Ingredient tracking** — Mark ingredients as used during cooking - **Ingredient tracking** — Mark ingredients as used during cooking; leftover quantities prompt a "move to another location" flow
### 📊 Dashboard ### 📊 Dashboard
- **Waste tracking** — Monitor consumed vs. wasted products over 30 days - **Waste tracking** — Monitor consumed vs. wasted products over 30 days
@@ -76,6 +78,7 @@
- **Setup wizard** — 3-step guided configuration (URL, connection test, gateway) - **Setup wizard** — 3-step guided configuration (URL, connection test, gateway)
- **Gateway auto-launch** — Launches the Scale Gateway in the background on startup - **Gateway auto-launch** — Launches the Scale Gateway in the background on startup
- **Camera & mic permissions** — Full hardware access for barcode scanning and voice - **Camera & mic permissions** — Full hardware access for barcode scanning and voice
- **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 - **Hard refresh** — ↻ button clears WebView cache to pick up web app updates
- **Update notifications** — Checks GitHub releases every 6h, shows banner when updates available - **Update notifications** — Checks GitHub releases every 6h, shows banner when updates available
- **SSL support** — Accepts self-signed certificates - **SSL support** — Accepts self-signed certificates
@@ -313,6 +316,9 @@ The application uses no build tools — edit files directly and refresh.
- [x] AI scan local matching — suggest existing pantry products before OFF lookup - [x] AI scan local matching — suggest existing pantry products before OFF lookup
- [x] Scale auto-fill improvements — 10g threshold, ml conversion hints - [x] Scale auto-fill improvements — 10g threshold, ml conversion hints
- [x] Update notification system — kiosk checks GitHub releases - [x] Update notification system — kiosk checks GitHub releases
- [x] Generic shopping name grouping — compound-phrase + keyword map (100+ entries) + Gemini AI fallback
- [x] Auto-add to Bring! on product depletion — no confirmation step when stock reaches zero
- [x] Native Android TTS in kiosk — bypasses Web Speech API voice detection issues
- [ ] Offline mode with service worker - [ ] Offline mode with service worker
- [ ] Export/import inventory data - [ ] Export/import inventory data
- [ ] Notification system (Telegram, email) for expiring products - [ ] Notification system (Telegram, email) for expiring products
+20 -4
View File
@@ -3703,25 +3703,37 @@ function computeShoppingName(string $name, string $category = '', string $brand
'glassa balsamic' => 'Aceto balsamico', 'glassa balsamic' => 'Aceto balsamico',
// Cold cuts — specific cuts // Cold cuts — specific cuts
'prosciutto cotto' => 'Prosciutto cotto', 'prosciutto cotto' => 'Prosciutto cotto',
// Flour subtypes // Flour subtypes (MUST come before generic "farina")
'farina di riso' => 'Farina di riso', 'farina di riso' => 'Farina di riso',
'farina riso' => 'Farina di riso', 'farina riso' => 'Farina di riso',
'farina di mais' => 'Farina di mais', 'farina di mais' => 'Farina di mais',
'farina mais' => 'Farina di mais', 'farina mais' => 'Farina di mais',
'farina integrale' => 'Farina integrale', 'farina integrale' => 'Farina integrale',
'farina 00' => 'Farina',
// Roux / sugar subtypes
'zucchero di canna' => 'Zucchero di canna',
'zucchero canna' => 'Zucchero di canna',
'zucchero velo' => 'Zucchero a velo',
'zucchero a velo' => 'Zucchero a velo',
// Fresh pasta // Fresh pasta
'pasta fresca' => 'Pasta fresca', 'pasta fresca' => 'Pasta fresca',
// Broth / stock // Broth / stock
'brodo vegetale' => 'Brodo', 'brodo vegetale' => 'Brodo',
'brodo pollo' => 'Brodo', 'brodo pollo' => 'Brodo',
'brodo manzo' => 'Brodo', 'brodo manzo' => 'Brodo',
// Sugar subtypes // Mixed vegetable purée / passato (MUST come before generic carote/patate)
'zucchero di canna' => 'Zucchero di canna', 'passato di verdure' => 'Verdure',
'zucchero canna' => 'Zucchero di canna', 'passato di patate' => 'Verdure',
// Water // Water
'acqua frizzante' => 'Acqua', 'acqua frizzante' => 'Acqua',
'acqua gassata' => 'Acqua', 'acqua gassata' => 'Acqua',
'acqua minerale' => 'Acqua', 'acqua minerale' => 'Acqua',
// Aroma / flavouring
'aroma vaniglia' => 'Ingredienti Spezie',
'aroma mandorla' => 'Ingredienti Spezie',
'aroma limone' => 'Ingredienti Spezie',
'aroma rum' => 'Ingredienti Spezie',
'aroma arancia' => 'Ingredienti Spezie',
]; ];
foreach ($phraseMap as $phrase => $canonical) { foreach ($phraseMap as $phrase => $canonical) {
if (mb_strpos($lower, $phrase) !== false) { if (mb_strpos($lower, $phrase) !== false) {
@@ -3771,6 +3783,10 @@ function computeShoppingName(string $name, string $category = '', string $brand
'grattato' => 'Pangrattato', 'grattato' => 'Pangrattato',
'pangrattato' => 'Pangrattato', 'pangrattato' => 'Pangrattato',
'biscottate' => 'Fette biscottate', 'biscottate' => 'Fette biscottate',
// Leavening agents
'lievito' => 'Lievito',
// Flavourings / aromas (single-token fallback; phrases handled above)
'aroma' => 'Ingredienti Spezie',
// Dairy // Dairy
'latte' => 'Latte', 'latte' => 'Latte',
'yogurt' => 'Yogurt', 'yogurt' => 'Yogurt',