Commit Graph

252 Commits

Author SHA1 Message Date
dadaloop82 3ff91b3018 fix(scale): progress-bar restart loop + low-weight warning + gateway auto-reconnect cycle
- Fix progress-bar restarting continuously when weight is stable:
  add _cancelScaleTimersOnly() that stops timers/animations without
  _cancelScaleTimersOnly() so the same value resumes counting when
  stability returns instead of always restarting the 5-s wait.
  Add 'else if' branch in _scaleAutoFillUse / _scaleAutoFillRecipeUse
  to restart stability wait after brief instability for the same value.

- Show red blinking warning in scale-live-box when weight < 10 g:
  adds scale-low-weight CSS class with pulsing border/shadow animation,
  the label shows '< 10 g · inserisci manualmente' instead of the
  stability progress bar.  No auto-confirm fires below 10 g.

- Gateway Android app: scale auto-reconnect now retries indefinitely.
  isAutoReconnecting flag keeps the scan→wait→scan cycle running until
  the scale is found again; onScanStopped schedules a new scan after
  10 s whenever autoReconnect is active and scale is still offline.
2026-04-16 06:25:40 +00:00
dadaloop82 951ef1d64f fix(scale): auto-fill broken for conf products (e.g. latte)
Two root causes:
1. _useNormalUnit was stale ('pz') for conf products because it's only
   updated in normal mode — fix: resolve effective unit from
   _useConfMode.packageUnit when in sub mode
2. Food scales in liquid mode send unit='ml' directly — was falling
   through to raw value, skipping density; fix: detect scaleAlreadyMl
   flag to use ml directly for ml target, or apply density for g target
Also: add scale pre-fill call after switchUseUnit('sub') in conf mode
2026-04-15 21:12:37 +00:00
dadaloop82 7144ec7386 feat(scale): g→ml density conversion for liquid products
- _scaleDensityForProduct(): returns g/ml density based on product name/category
  olio oliva 0.91, girasole 0.92, spirits 0.94, aceto/panna 1.01, latte 1.03,
  yogurt 1.05, succo 1.04, miele/sciroppo 1.40, default 1.00 (water)
- _scaleAutoFillUse(): normalises all scale units to grams first, then converts
  to target unit; when unit=ml applies density; never touches pz/conf
2026-04-15 21:08:41 +00:00
dadaloop82 d26229800c feat(scale): auto-fill use-quantity live from scale reading
- _scaleAutoFillUse(): converts and fills use-quantity on stable readings
- _scaleLatestWeight pre-fills when use page opens (after unit is known)
- oninput on use-quantity pauses auto-fill when user types manually
- showUseForm() resets paused flag so next opening resumes auto-fill
- Small 'live' hint text shown while auto-filling, hidden when overridden
2026-04-15 21:06:12 +00:00
dadaloop82 6f5bc15734 fix: use relative API paths (leading / broke /dispensa/ subdir routing) 2026-04-15 21:00:34 +00:00
dadaloop82 55c5b34381 feat(scale): auto-discover gateway on local network
- api/scale_discover.php: async TCP scan of whole /24 subnet on port 8765,
  confirms with WebSocket handshake, returns found ws:// URLs in ~1.5s
- index.html: '🔍 Auto' button next to gateway URL field
- app.js: discoverScaleGateway() — calls relay, fills URL field and
  auto-saves settings + reconnects on success
2026-04-15 20:56:54 +00:00
dadaloop82 9523b68fea ci: add GitHub Actions workflow to build & release Scale Gateway APK
- Add .github/workflows/build-scale-gateway.yml
  Triggers on push to main (evershelf-scale-gateway/** path filter)
  Builds debug APK with Gradle/JDK 17, renames to evershelf-scale-gateway.apk
  Creates/updates 'latest' GitHub Release so the direct download URL resolves
- Bump web app version v1.2.0 -> v1.3.0 (index.html)
- Bump Android versionCode 1->2, versionName 1.0.0->1.3.0 (app/build.gradle.kts)
2026-04-14 16:29:00 +00:00
dadaloop82 0893742f05 feat: add smart scale BLE gateway integration
- Add evershelf-scale-gateway/ Android app (Kotlin):
  - BLE scanning and GATT connection to smart scales
  - Supports BT SIG Weight Scale (0x181D), Body Composition (0x181B), and generic heuristic parser
  - WebSocket server on port 8765 (local LAN)
  - Real-time weight broadcasting to EverShelf browser client
- Add scale status indicator in header (green/orange/grey dot)
- Add Settings tab for scale configuration (URL, enable toggle, test, APK download link)
- Add 'Read from scale' button in Add/Use forms when unit is g or ml
- Add scale WebSocket client logic in app.js with auto-reconnect
- Fix recipe suggestion: expiry-prioritized ingredients now only injected into
  AI prompt when user explicitly selects 'Priorità Scadenze' or 'Zero Sprechi'
- Update README with smart scale section and website link
- Update all translations (it, en, de) with scale strings
2026-04-14 15:59:40 +00:00
dadaloop82 0c6a07ee95 fix: preserve version badge during i18n translatePage
Move data-i18n to inner <span> so translatePage() with textContent
does not overwrite the sibling .header-version span.
2026-04-13 10:23:26 +00:00
dadaloop82 874a242149 release: v1.2.0 - EverShelf rebrand + version badge
- Add v1.2.0 version badge to app header
- Update CHANGELOG with v1.2.0 entries
- Bump manifest.json version to 1.2.0
- Bump OpenAPI spec version to 1.2.0
2026-04-13 10:21:16 +00:00
dadaloop82 a1873d3f81 fix: bump asset versions to bust browser cache (20260413a)
After JS truncation and recovery, browsers may have cached the broken app.js.
Bumping ?v= query strings forces fresh download of app.js and style.css.
2026-04-13 10:18:56 +00:00
dadaloop82 c52a91e779 fix: restore app.js after sed truncation, update nav title to EverShelf
- Recover app.js from pre-rebrand commit and re-apply all substitutions safely
- Fix all localStorage keys: dispensa_* -> evershelf_* (settings, setup, lang)
- Fix TTS test strings: 'Dispensa Manager' -> 'EverShelf'
- Set nav.title to EverShelf in it/en/de translations and HTML fallback
- Fix JS syntax error (Unexpected end of input) that broke the site
- CI JavaScript Lint should now pass
2026-04-13 10:14:40 +00:00
dadaloop82 20f734d54a rebrand: rename project from Dispensa Manager to EverShelf
- Update app name across all files (manifest, index.html, README, docs)
- Update contact email to evershelfproject@gmail.com
- Rename Docker service/container/volume to evershelf
- Rename localStorage keys: dispensa_* → evershelf_*
- Rename SQLite DB reference: dispensa.db → evershelf.db
- Update SSH remote to dadaloop82/EverShelf
- Update Apache conf file name to evershelf.conf
- Update CI workflow Docker image/container names
- Update cron job example path
- Add data/dispensa.db to .gitignore to prevent accidental commit
2026-04-13 10:09:33 +00:00
dadaloop82 da962581c0 feat: offline browser TTS engine with voice selector
Add Web Speech API as alternative TTS engine (fully offline, no config needed).
- Engine selector in settings: 'browser' (offline) or 'server' (HTTP endpoint)
- Voice picker populated from speechSynthesis.getVoices(), Italian voices first
- Auto-selects Paola voice on macOS/iOS if available
- Rate and pitch sliders (0.5x-2x, 0-2)
- testTTS() and speakCookingStep() branch on selected engine
- Existing users with tts_url keep 'server' as default engine
2026-04-10 10:19:02 +00:00
dadaloop82 d13f744aea feat: v1.1.0 - Docker, i18n, setup wizard, rate limiting, OpenAPI
New features:
- Docker support (Dockerfile + docker-compose.yml)
- GitHub Actions CI pipeline (PHP lint, JS lint, Docker build, i18n validation)
- Internationalization system with 3 languages (it, en, de) and 347 translation keys
- First-run setup wizard (4-step configuration)
- File-based API rate limiting (120/15/5 req/min tiers)
- OpenAPI 3.1.0 specification for all 43 API endpoints
- CONTRIBUTING.md with translation and development guide
- Screenshots directory placeholder

Modified:
- README.md: Docker badges, install instructions, translations section
- api/index.php: rate limiting middleware
- assets/js/app.js: i18n system, setup wizard, t() function
- assets/css/style.css: setup wizard styles
- index.html: data-i18n attributes, setup wizard overlay, language settings
- .gitignore: rate_limits exclusion
2026-04-10 06:03:11 +00:00
dadaloop82 e0956c6043 Prepare for public distribution v1.0.0
- Remove all personal data from source code (HA IP, JWT tokens)
- Move secrets to .env configuration (gitignored)
- Create .env.example template for new installations
- Add centralized env() helper, eliminate code duplication (~120 lines removed)
- Add input validation on inventory operations (quantity bounds, location whitelist)
- Remove sensitive credential exposure in API responses
- Remove database and runtime files from Git tracking
- Disable database push-to-GitHub backup (local-only backup now)
- Update .gitignore for distribution
- Add comprehensive README with installation guide
- Add CHANGELOG.md for version tracking
- Add MIT LICENSE
- Add author/license headers to all source files
- TTS defaults now empty (configured per-installation via .env)
2026-04-10 05:24:27 +00:00
dadaloop82 d690ad826c Suggerisci confezione da usare per prima (scad. più vicina)
- Nel form USA, se il prodotto ha >=2 slot in inventario con scadenze
  diverse (es. 2 lotti dispensa, o frigo+dispensa), mostra un banner
  giallo: '⚠️ Usa prima quella in Frigo — scade il 12/04 (tra 3 giorni)!'
- Logica: filtra item con expiry_date, ordina per scadenza ASC,
  mostra hint solo se ci sono almeno 2 scadenze diverse O 2 location diverse
- Nessun hint se tutto ha la stessa scadenza (inutile)
2026-04-09 05:12:25 +00:00
dadaloop82 4e576559a9 feat: barcode scan button + reminder in manual product form
- Add 📷 scan button next to the barcode field in product form
  Opens a camera modal (BarcodeDetector if available, manual fallback)
  Detects barcode after 2 consistent frames, fills field and closes modal
- Show ⚠️ hint below the barcode field when it's empty (new products only):
  'Aggiungi il barcode così al prossimo acquisto basta scansionarlo!'
  Hint hides automatically when a code is entered or scanned
- Hint is hidden in edit-product mode (barcode already saved)
- scanBarcodeForForm() reuses the modal overlay; handles camera permission
  errors gracefully (shows manual input only)
2026-04-07 12:17:18 +00:00
dadaloop82 4e8b586201 feat: AI photo identification from product form
When creating a new product (manual entry), a '📷 Scatta foto e identifica con AI'
button appears at the top of the form. Tapping it:
1. Opens a camera modal (same pattern as expiry scanner)
2. User takes photo of product/label
3. Sends to gemini_identify — returns name, brand, category + OpenFoodFacts matches
4. User can pick a specific OFF match (fills barcode + full details via lookup_barcode)
   or tap 'Usa dati AI' to fill just name/brand/category from Gemini
5. All matching fields are auto-filled: name, brand, category, barcode, image, unit/qty
6. Button hidden when editing an existing product (not needed)
2026-04-06 09:23:41 +00:00
dadaloop82 a6bc05cd2d feat: spesa mode stats banner + scan zoom x1/x2 toggle
Spesa mode banner:
- Tracks each added product in _spesaSession[]
- Shows a rotating stat/phrase below the title: count, top category,
  duplicates, fun milestone messages (primo prodotto, ottimo ritmo, spesa epica…)
- Banner gains two-line layout (title + stat)

Scan zoom:
- Small pill button 'x1'/'x2' overlaid top-right of the camera viewport
- On hardware-zoom capable devices (Android Chrome) uses track.applyConstraints zoom
- Falls back to CSS scale(2) on video element for all other browsers
- Zoom resets to x1 on stopScanner()
2026-04-06 09:16:50 +00:00
dadaloop82 da5552e992 fix: hide meal-plan banner on chip uncheck; fix recipe variety (variation counter, temp scaling, client-side title tracking) 2026-04-04 15:29:07 +00:00
dadaloop82 475d482184 feat: TTS generic API builder, remove HA refs, pre-fill credentials 2026-04-04 14:40:48 +00:00
dadaloop82 7bc1c87d5c feat: TTS via Home Assistant API, settings panel, remove browser speechSynthesis 2026-04-04 14:37:00 +00:00
dadaloop82 63db7cc114 feat: bring urgency sync, background auto-sync, recipe mealplan chip, screensaver fix 2026-04-04 14:32:25 +00:00
dadaloop82 6f81846942 Smart shopping: timestamp ultimo aggiornamento, CSS progress dots e timer bar; fix layout modalità cucina 2026-04-01 05:52:46 +00:00
dadaloop82 fb7bb4d675 Modalità cucina: timer multipli persistenti con etichetta, riprendi dal passo salvato, progress dots, pulsante Ricomincia; priorità ricette basata su scadenze con ingredienti obbligatori 2026-03-31 15:55:35 +00:00
dadaloop82 bcddba46d4 Remove kg/l units everywhere — only g (grammi) and ml (millilitri)
- HTML: removed kg/l options from all unit selector dropdowns
- JS detectUnitAndQuantity(): auto-converts kg→g (*1000) and l→ml (*1000)
- JS unit labels: removed all kg/l entries from unitLabels maps
- JS category defaults: frutta 1000g, verdura 500g, bevande 1000ml
- JS step/min logic: simplified for g/ml only (no more 0.01 steps)
- JS getSubUnitStep(): removed kg/l cases
- JS isLowStock(): removed kg/l threshold
- JS spec parser: labels now show g/ml instead of kg/L
- PHP recipe parser: converts kg→g and l→ml immediately on parse
- PHP AI prompt: updated to specify only g/ml/pz/conf units
- PHP migration endpoint available at ?action=migrate_units (no-op if DB already clean)
2026-03-30 14:13:11 +00:00
dadaloop82 c4938457ac Fix min quantity for kg/l units in use forms
- Normal mode (non-conf) now sets min=0.01 for kg/l, min=1 for g/ml
- +/- buttons use unit-aware steps: 0.01 for small kg/l values, 0.1 for
  values <1, 0.5 for values >=1 (instead of fixed 0.5)
- Same fix applied to recipe use form
- Allows inputting e.g. 0.07kg (70g) when product is tracked in kg
2026-03-30 13:45:02 +00:00
dadaloop82 c63faf56e4 Conservative Bring! cleanup + operations log
- cleanupObsoleteBringItems() now much more conservative:
  * Only removes items matching a known DB product (preserves manual additions)
  * Only removes if the product has current_qty > 0 (has stock)
  * AND item is no longer flagged by smart shopping
- Added logOperation() — stores all Bring! operations in localStorage '_opLog'
  (bring_auto_add, bring_cleanup, bring_found, bring_manual_remove)
  Capped at 200 entries, each with timestamp + action + details
- All Bring! add/remove paths now log their operations
2026-03-30 13:36:51 +00:00
dadaloop82 4b3e5f2ce4 Cleanup obsolete Bring! items after smart shopping algorithm fix
- cleanupObsoleteBringItems(): one-time per session, removes items from Bring!
  that the updated smart shopping algorithm no longer considers relevant
- Cross-references shoppingItems vs smartShoppingItems using exact + token match
- Shows toast with count of removed items
- Called alongside autoAddCriticalItems after loading smart shopping data
2026-03-29 19:54:05 +00:00
dadaloop82 7be6ae8cd7 feat: timer integrato nella modalita cucina
- Rileva automaticamente durate nel testo dello step (minuti, ore, secondi,
  mezz'ora, un quarto d'ora, qualche minuto, un paio di minuti, ecc.)
- Mostra countdown grande con Avvia/Pausa/Reset
- Ultimi 30 secondi in arancione, scaduto in rosso pulsante
- Allo scadere: vibrazione + TTS 'Tempo scaduto!'
- Timer continua a contare in overtime (+00:XX) dopo lo zero
- Timer si resetta automaticamente cambiando step o chiudendo
2026-03-29 16:09:12 +00:00
dadaloop82 2d754526a5 fix: TTS primo step con voce corretta, bottone Rileggi sempre visibile
- startCookingMode: attende voiceschanged prima di parlare il primo step
  cosi la voce Google (donna) viene usata subito, non il fallback robot
- _cookingTTS parte sempre true (attivato), icona 🔊
- Bottone Rileggi visibile di default senza display:none
- Cache buster aggiornato a v=20260329c
2026-03-29 16:00:24 +00:00
dadaloop82 23e8d9a6b8 fix: voce TTS migliore (Google/online) + cache buster aggiornato 2026-03-29 15:50:03 +00:00
dadaloop82 227c31d9f9 fix: TTS sincrono + pulsante Rileggi nella modalità cucina 2026-03-29 15:46:43 +00:00
dadaloop82 f87e2204a9 fix: cooking overlay fuori dallo screensaver (era nascosto da display:none) 2026-03-29 15:38:33 +00:00
dadaloop82 917aa56001 feat: sezioni reparto lista spesa, gradient urgenza, modalità cucina con TTS
- renderShoppingItems: raggruppamento per reparto (sezioni), ordinamento
  per urgenza+frequenza, sfondo con gradiente colore urgenza
- renderSmartShopping: stesso raggruppamento per reparto in tab previsione
- Modalità Cucina: overlay fullscreen nero, step per step con navigazione,
  TTS italiano via Web Speech API, pulsante 'Usa' ingredienti per step
- CSS: modal z-index 600 in cooking-mode-active per sovrapposizione corretta
2026-03-29 15:30:55 +00:00
dadaloop82 a38a5d670f feat: lista spesa con tab Da comprare/In previsione, tag, frequenza, tap-to-scan
- Counter nei tab aggiornati dinamicamente
- Auto-aggiunta prodotti CRITICI a Bring! al caricamento (1x per sessione)
- Badge urgenza e frequenza sugli item in lista (cross-ref smart shopping)
- Tag locali per item (Urgente/Priorità/Verificare) con menu dropdown
- Ordinamento automatico per frequenza utilizzo (item più usati in cima)
- Tap su un item → scanner barcode, con banner 'Trovato! Rimuovi dalla lista'
- Fix pctLeft: usa max(1, qty) come fallback refQty per evitare falsi alert
- Fix daysLeft capped a 365gg per pulire stringhe di previsione
- Back button on action page → torna a shopping se aperto da lista
2026-03-29 14:12:37 +00:00
dadaloop82 11a703d274 20260319a: Auto-refresh recipe archive after save, Bring spec uses product name not brand 2026-03-19 13:18:35 +00:00
dadaloop82 dae4c4d435 20260318b: Fix countdown bar color - was using non-existent CSS variable 2026-03-18 06:33:04 +00:00
dadaloop82 4536decfaf 20260318a: Bump cache busters 2026-03-18 06:16:11 +00:00
dadaloop82 9517225d32 20260317e: Persist recipe ingredient used state to DB 2026-03-17 14:18:25 +00:00
dadaloop82 66e175e863 20260317d: Auto-dismiss move modal after 15s with progress bar countdown 2026-03-17 14:15:16 +00:00
dadaloop82 7cc557d4df 20260317c: Torta->Dolce, Bring specification with brand for product variants 2026-03-17 13:55:30 +00:00
dadaloop82 d1df6a6862 20260317b: Replace Comfort Food with Priorita cose aperte in recipe options 2026-03-17 12:06:14 +00:00
dadaloop82 9491cf0e0b 20260317a: Replace Break Mattutino/Spuntino Serale with Torta/Succo di Frutta in recipe meals 2026-03-17 06:28:42 +00:00
dadaloop82 b4b68d6579 20260316i: Low-stock Bring prompt after use
- API returns total_remaining, product_name, unit, default_qty after use
- isLowStock() detects when inventory is running low (pz<=2, conf<=1, weight/vol<=25% of default)
- After using a product, if low stock detected, shows prompt asking to add to Bring shopping list
- Works in main USE form, USE ALL, and recipe ingredient USE
- Prompt chains properly: low-stock then move-after-use (if applicable)
- Skips prompt when product is fully depleted (already auto-added to Bring)
2026-03-16 14:59:55 +00:00
dadaloop82 1d8fb55f58 20260316h: Recipe ingredient USE popup with qty/location controls
- Clicking 'Usa' on a recipe ingredient now opens a modal popup instead of
  directly consuming. Shows location selector, quantity controls (+/- buttons),
  conf/sub-unit mode, and 'Usa TUTTO' option.
- Quantity is pre-filled with the recipe's suggested amount
- Auto-selects location with opened package
- After use, shows move-after-use modal (stays on recipe page, not dashboard)
- Separate recipe-context move functions (showRecipeMoveModal, confirmRecipeMove)
- Modal overlay z-index 250 to appear above recipe dialog
2026-03-16 12:42:12 +00:00
dadaloop82 134e5dfa4e 20260316g: Fix conf anomaly detection + auto-split opened packages
- Fixed isSuspiciousDefaultQty: for conf products, checks package_unit thresholds
  (375g is fine for g-max=10000, not checked against conf-max=50)
- Auto-split on use: when using from a conf product with whole+fractional qty,
  automatically separates whole confs from opened part
  e.g. Panna 2.6conf → use 5g → 2conf (sealed) + 0.56conf (opened)
- Move modal now moves only the opened row (via opened_id)
- Use query prefers fractional rows (opened packages) first
- Non-conf products still get standard move-after-use behavior
2026-03-16 08:07:17 +00:00
dadaloop82 5ca809b31f 20260316f: Fix Cracker integrali default_qty (500→16) + detect absurd package sizes
- Fixed Cracker integrali default_quantity from 500 to 16 (was weight in grams, should be pieces)
- Review system now also flags products with absurd default_quantity (package size)
  e.g. a 'pz' product claiming 500pz per package gets flagged as '📦 Conf. sospetta'
- Uses same QTY_THRESHOLDS max values for detection
2026-03-16 08:01:58 +00:00
dadaloop82 74d99a11bf 20260316e: Fix quick recipe + use from opened packages first
- Quick recipe: prompt now specifies 1 persona, excludes freezer products
- USE form: auto-selects location with opened package as default
  (deducts from already-opened packages before sealed ones)
- Move-after-use modal verified working (shows after partial use)
2026-03-16 07:59:56 +00:00