diff --git a/api/database.php b/api/database.php
index ffe8ad5..8d0c593 100644
--- a/api/database.php
+++ b/api/database.php
@@ -380,7 +380,7 @@ function estimateOpenedExpiryDaysPHP(string $name, string $category, string $loc
if (preg_match('/mozzarella|burrata|stracciatella/', $n)) return 3;
if (preg_match('/philadelphia|spalmabile/', $n)) return 7;
if (preg_match('/formaggio.*(fresco|ricotta|mascarpone|stracchino|crescenza)/', $n)) return 5;
- if (preg_match('/parmigiano|grana|pecorino|provolone/', $n)) return 21;
+ if (preg_match('/parmigiano|grana|pecorino|provolone|asiago|fontina|emmental|gruyere|scamorza/', $n)) return 28;
if (preg_match('/formaggio/', $n)) return 10;
if (preg_match('/\bburro\b/', $n)) return 30;
if (preg_match('/\bpanna\b/', $n)) return 4;
@@ -449,7 +449,7 @@ function estimateSealedExpiryDaysPHP(string $name, string $category, string $loc
elseif (preg_match('/yogurt/', $n)) $days = 21;
elseif (preg_match('/mozzarella|burrata|stracciatella/', $n)) $days = 5;
elseif (preg_match('/formaggio\s+(fresco|ricotta|mascarpone|stracchino|crescenza)/', $n)) $days = 10;
- elseif (preg_match('/parmigiano|grana|pecorino|provolone/', $n)) $days = 60;
+ elseif (preg_match('/parmigiano|grana|pecorino|provolone|asiago|fontina|emmental|gruyere|scamorza|groviera/', $n)) $days = 60;
elseif (preg_match('/burro/', $n)) $days = 60;
elseif (preg_match('/panna/', $n)) $days = 14;
elseif (preg_match('/prosciutto\s+cotto|mortadella|wurstel/', $n)) $days = 7;
diff --git a/assets/css/style.css b/assets/css/style.css
index 028827e..9757498 100644
--- a/assets/css/style.css
+++ b/assets/css/style.css
@@ -122,8 +122,7 @@ body {
flex-direction: column;
align-items: center;
gap: 10px;
- width: 310px;
- max-width: 92vw;
+ width: min(92vw, 600px);
animation: zwFadeIn 0.2s ease;
}
.preloader-bar-track {
@@ -144,60 +143,69 @@ body {
.preloader-bar.bar-warn { background: linear-gradient(90deg, #fbbf24, #f59e0b); }
.preloader-check-label { display: none; } /* replaced by check-wheel */
-/* ── Startup check wheel (3-D scroll) ──────────────────────────────── */
-.check-wheel {
+/* ── Startup check ticker (smooth fade queue) ───────────────────────── */
+.preloader-progress-wrap {
+ width: min(96vw, 860px) !important;
+}
+.check-ticker {
position: relative;
width: 100%;
- height: 108px;
- perspective: 640px;
+ height: 170px;
overflow: hidden;
- display: flex;
- align-items: center;
- justify-content: center;
+ margin-top: 6px;
}
-.check-wheel-current {
- position: relative;
- z-index: 3;
- width: 100%;
- text-align: center;
- font-size: clamp(0.88rem, 2.6vw, 1.08rem);
- font-weight: 700;
- padding: 10px 14px;
- border-radius: 14px;
- border: 1px solid rgba(52,211,153,0.35);
- background: rgba(52,211,153,0.08);
- color: #34d399;
- transform-style: preserve-3d;
- animation: checkWheelIn 0.30s ease;
- letter-spacing: 0.01em;
- line-height: 1.4;
-}
-.check-wheel-current.state-ok { color: #34d399; background: rgba(52,211,153,0.08); border-color: rgba(52,211,153,0.35); }
-.check-wheel-current.state-warn { color: #fbbf24; background: rgba(251,191,36,0.08); border-color: rgba(251,191,36,0.35); }
-.check-wheel-current.state-error { color: #f87171; background: rgba(248,113,113,0.08); border-color: rgba(248,113,113,0.35); }
-.check-wheel-prev {
+.ticker-item {
position: absolute;
- top: 3px;
- left: 6%; right: 6%;
+ left: 0; right: 0;
text-align: center;
- font-size: clamp(0.67rem, 1.8vw, 0.82rem);
- font-weight: 600;
- color: rgba(203,213,225,0.45);
- opacity: 0.52;
- transform: rotateX(54deg) scale(0.85);
- transform-origin: center bottom;
+ padding: 0 12px;
+ line-height: 1.45;
+ pointer-events: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
- pointer-events: none;
- font-family: monospace;
}
-@keyframes checkWheelIn {
- from { transform: translateY(24px) rotateX(-18deg); opacity: 0.35; }
- to { transform: translateY(0) rotateX(0deg); opacity: 1; }
+/* Current — fades in from below, bright and large */
+.ticker-current {
+ bottom: 8px;
+ font-size: clamp(1.1rem, 3.2vw, 1.35rem);
+ font-weight: 700;
+ opacity: 1;
+ color: #4ade80;
+ letter-spacing: 0.01em;
+ animation: tickerFadeIn 0.5s cubic-bezier(0.22, 1, 0.36, 1);
+}
+.ticker-current.state-ok { color: #4ade80; }
+.ticker-current.state-warn { color: #fbbf24; }
+.ticker-current.state-error { color: #f87171; }
+/* Previous items — progressively dimmer and smaller, scrolling up */
+.ticker-prev-1 {
+ bottom: 54px;
+ font-size: clamp(0.92rem, 2.5vw, 1.1rem);
+ font-weight: 600;
+ opacity: 0.48;
+ color: #94a3b8;
+}
+.ticker-prev-2 {
+ bottom: 94px;
+ font-size: clamp(0.78rem, 2vw, 0.92rem);
+ font-weight: 500;
+ opacity: 0.22;
+ color: #64748b;
+}
+.ticker-prev-3 {
+ bottom: 128px;
+ font-size: clamp(0.66rem, 1.7vw, 0.78rem);
+ font-weight: 400;
+ opacity: 0.09;
+ color: #475569;
+}
+@keyframes tickerFadeIn {
+ from { opacity: 0; transform: translateY(22px); }
+ to { opacity: 1; transform: translateY(0); }
}
.preloader-warnings {
- max-width: 310px;
+ max-width: min(92vw, 600px);
width: 100%;
animation: zwFadeIn 0.3s ease;
}
diff --git a/assets/js/app.js b/assets/js/app.js
index cd54568..f27a5ad 100644
--- a/assets/js/app.js
+++ b/assets/js/app.js
@@ -1601,7 +1601,7 @@ function estimateExpiryDays(product, location) {
else if (/yogurt/.test(name)) days = 21;
else if (/mozzarella|burrata|stracciatella/.test(name)) days = 5;
else if (/formaggio\s+(fresco|ricotta|mascarpone|stracchino|crescenza)/.test(name)) days = 10;
- else if (/parmigiano|grana|pecorino|provolone/.test(name)) days = 60;
+ else if (/parmigiano|grana|pecorino|provolone|asiago|fontina|emmental|gruyere|scamorza|groviera/.test(name)) days = 60;
else if (/burro/.test(name)) days = 60;
else if (/panna/.test(name)) days = 14;
else if (/prosciutto\s+cotto|mortadella|wurstel/.test(name)) days = 7;
@@ -1741,7 +1741,7 @@ function estimateOpenedExpiryDays(product, location) {
if (/mozzarella|burrata|stracciatella/.test(name)) return 3;
if (/philadelphia|spalmabile/.test(name)) return 7;
if (/formaggio.*(fresco|ricotta|mascarpone|stracchino|crescenza)/.test(name)) return 5;
- if (/parmigiano|grana|pecorino|provolone/.test(name)) return 21;
+ if (/parmigiano|grana|pecorino|provolone|asiago|fontina|emmental|gruyere|scamorza/.test(name)) return 28;
if (/formaggio/.test(name)) return 10;
if (/\bburro\b/.test(name)) return 30;
if (/\bpanna\b/.test(name)) return 4;
@@ -14899,24 +14899,25 @@ async function _runStartupCheck() {
if (spinnerEl) spinnerEl.style.display = 'none';
wrapEl.style.display = '';
- // Helper: set progress bar + 3D check wheel
+ // Helper: set progress bar + fade ticker queue
let _curPct = 0;
+ const _tickerHistory = [];
const setProgress = (pct, label, state) => {
_curPct = pct;
if (barEl) {
barEl.style.width = pct + '%';
barEl.className = 'preloader-bar' + (state === 'error' ? ' bar-error' : state === 'warn' ? ' bar-warn' : '');
}
- const wheelPrev = document.getElementById('check-wheel-prev');
- const wheelCurr = document.getElementById('check-wheel-current');
- if (wheelCurr) {
- const prevText = wheelCurr.textContent.trim();
- if (wheelPrev && prevText && prevText !== '\u00a0') wheelPrev.textContent = prevText;
- wheelCurr.textContent = label || '';
- const sc = state === 'error' ? 'state-error' : state === 'warn' ? 'state-warn' : 'state-ok';
- wheelCurr.className = 'check-wheel-current ' + sc;
- void wheelCurr.offsetWidth; // re-trigger CSS animation
- }
+ if (!label) return;
+ const ticker = document.getElementById('check-ticker');
+ if (!ticker) return;
+ _tickerHistory.unshift({ label, state: state || 'ok' });
+ if (_tickerHistory.length > 4) _tickerHistory.pop();
+ const posClass = ['ticker-current','ticker-prev-1','ticker-prev-2','ticker-prev-3'];
+ ticker.innerHTML = _tickerHistory.map((item, i) => {
+ const sc = item.state === 'error' ? 'state-error' : item.state === 'warn' ? 'state-warn' : 'state-ok';
+ return `
${item.label}
`;
+ }).join('');
};
// Phase 1: animate 0→15% while fetching (so it never looks stuck)
diff --git a/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/SetupActivity.kt b/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/SetupActivity.kt
index 8001cc8..2186d06 100644
--- a/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/SetupActivity.kt
+++ b/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/SetupActivity.kt
@@ -58,8 +58,10 @@ import javax.net.ssl.X509TrustManager
* 2 — Permissions rationale + grant
* 3 — Server URL + auto-discovery + connection test
* 4 — Smart scale question → gateway info + install
- * 5 — Screensaver toggle (NEW)
- * 6 — Done
+ * 5 — Features (screensaver / prices / meal-plan / zero-waste)
+ * 6 — Gemini AI key (optional, auto-skipped if already set)
+ * 7 — Bring! credentials (optional, auto-skipped if already set)
+ * 8 — Done
*/
class SetupActivity : AppCompatActivity() {
@@ -73,6 +75,8 @@ class SetupActivity : AppCompatActivity() {
private lateinit var stepServer: LinearLayout
private lateinit var stepScale: LinearLayout
private lateinit var stepScreensaver: LinearLayout
+ private lateinit var stepGemini: LinearLayout
+ private lateinit var stepBring: LinearLayout
private lateinit var stepDone: LinearLayout
// Progress dots
@@ -114,6 +118,11 @@ class SetupActivity : AppCompatActivity() {
private lateinit var setupSwitchMealPlan: SwitchMaterial
private lateinit var setupSwitchZeroWaste: SwitchMaterial
+ // Gemini + Bring steps
+ private lateinit var setupGeminiKeyEdit: EditText
+ private lateinit var setupBringEmailEdit: EditText
+ private lateinit var setupBringPasswordEdit: EditText
+
// Done step
private lateinit var summaryText: TextView
@@ -134,6 +143,9 @@ class SetupActivity : AppCompatActivity() {
private const val KEY_PRICE_ENABLED = "price_enabled"
private const val KEY_MEAL_PLAN = "meal_plan_enabled"
private const val KEY_ZEROWASTE_TIPS = "zerowaste_tips_enabled"
+ private const val KEY_GEMINI_KEY = "gemini_api_key"
+ private const val KEY_BRING_EMAIL = "bring_email"
+ private const val KEY_BRING_PASSWORD = "bring_password"
private const val PERMISSION_REQUEST_CODE = 2004
private const val BLE_PERMISSION_REQUEST = 2006
@@ -184,8 +196,11 @@ class SetupActivity : AppCompatActivity() {
override fun onBackPressed() {
when (currentStep) {
- 0 -> confirmExit()
- 1 -> showStep(0) // back to language
+ 0 -> confirmExit()
+ 1 -> showStep(0) // back to language
+ 8 -> showStep(7) // done → bring
+ 7 -> showStep(6) // bring → gemini
+ 6 -> showStep(5) // gemini → features
else -> showStep(currentStep - 1)
}
}
@@ -221,8 +236,18 @@ class SetupActivity : AppCompatActivity() {
stepServer = findViewById(R.id.stepServer)
stepScale = findViewById(R.id.stepScale)
stepScreensaver = findViewById(R.id.stepScreensaver)
+ stepGemini = findViewById(R.id.stepGemini)
+ stepBring = findViewById(R.id.stepBring)
stepDone = findViewById(R.id.stepDone)
+ // Gemini + Bring fields
+ setupGeminiKeyEdit = findViewById(R.id.setupGeminiKeyEdit)
+ setupBringEmailEdit = findViewById(R.id.setupBringEmailEdit)
+ setupBringPasswordEdit = findViewById(R.id.setupBringPasswordEdit)
+ // Pre-fill from saved prefs
+ (prefs.getString(KEY_GEMINI_KEY, "") ?: "").takeIf { it.isNotEmpty() }?.let { setupGeminiKeyEdit.setText(it) }
+ (prefs.getString(KEY_BRING_EMAIL, "") ?: "").takeIf { it.isNotEmpty() }?.let { setupBringEmailEdit.setText(it) }
+
// Server step
urlEdit = findViewById(R.id.setupUrlEdit)
urlStatus = findViewById(R.id.setupUrlStatus)
@@ -273,6 +298,8 @@ class SetupActivity : AppCompatActivity() {
findViewById(R.id.btnLangIt).setOnClickListener { selectLanguage("it") }
findViewById(R.id.btnLangEn).setOnClickListener { selectLanguage("en") }
findViewById(R.id.btnLangDe).setOnClickListener { selectLanguage("de") }
+ findViewById(R.id.btnLangEs).setOnClickListener { selectLanguage("es") }
+ findViewById(R.id.btnLangFr).setOnClickListener { selectLanguage("fr") }
// ── Welcome ──────────────────────────────────────────────────────
findViewById(R.id.btnSetupExit).setOnClickListener { confirmExit() }
@@ -375,10 +402,10 @@ class SetupActivity : AppCompatActivity() {
bleSetupCard.visibility = View.VISIBLE
tvSelectedScale.text = ""
tvSelectedScale.visibility = View.GONE
- tvScanStatus.text = "Bilancia non confermata. Riprova la scansione."
+ tvScanStatus.text = getString(R.string.ble_not_confirmed)
tvScanStatus.setTextColor(0xFFfbbf24.toInt())
btnScanBle.isEnabled = true
- btnScanBle.text = "🔍 Cerca bilancia"
+ btnScanBle.text = getString(R.string.ble_scan_again)
findViewById(R.id.btnScaleNext).isEnabled = false
}
findViewById(R.id.btnTestSkip).setOnClickListener {
@@ -398,14 +425,34 @@ class SetupActivity : AppCompatActivity() {
findViewById(R.id.btnScreensaverBack).setOnClickListener { showStep(4) }
findViewById(R.id.btnScreensaverNext).setOnClickListener {
prefs.edit()
- .putBoolean(KEY_SCREENSAVER, setupSwitchScreensaver.isChecked)
- .putBoolean(KEY_PRICE_ENABLED, setupSwitchPrices.isChecked)
- .putBoolean(KEY_MEAL_PLAN, setupSwitchMealPlan.isChecked)
+ .putBoolean(KEY_SCREENSAVER, setupSwitchScreensaver.isChecked)
+ .putBoolean(KEY_PRICE_ENABLED, setupSwitchPrices.isChecked)
+ .putBoolean(KEY_MEAL_PLAN, setupSwitchMealPlan.isChecked)
.putBoolean(KEY_ZEROWASTE_TIPS, setupSwitchZeroWaste.isChecked)
.apply()
showStep(6)
}
+ // ── Gemini step ───────────────────────────────────────────────────
+ findViewById(R.id.btnGeminiBack).setOnClickListener { showStep(5) }
+ findViewById(R.id.btnGeminiSkip).setOnClickListener { showStep(7) }
+ findViewById(R.id.btnGeminiNext).setOnClickListener {
+ val key = setupGeminiKeyEdit.text.toString().trim()
+ if (key.isNotEmpty()) prefs.edit().putString(KEY_GEMINI_KEY, key).apply()
+ showStep(7)
+ }
+
+ // ── Bring step ────────────────────────────────────────────────────
+ findViewById(R.id.btnBringBack).setOnClickListener { showStep(6) }
+ findViewById(R.id.btnBringSkip).setOnClickListener { showStep(8) }
+ findViewById(R.id.btnBringNext).setOnClickListener {
+ val email = setupBringEmailEdit.text.toString().trim()
+ val pass = setupBringPasswordEdit.text.toString().trim()
+ if (email.isNotEmpty()) prefs.edit().putString(KEY_BRING_EMAIL, email).apply()
+ if (pass.isNotEmpty()) prefs.edit().putString(KEY_BRING_PASSWORD, pass).apply()
+ showStep(8)
+ }
+
// ── Done ──────────────────────────────────────────────────────────
findViewById(R.id.btnLaunch).setOnClickListener { finishSetup() }
}
@@ -421,20 +468,27 @@ class SetupActivity : AppCompatActivity() {
private fun highlightSelectedLang() {
val saved = prefs.getString(KEY_LANGUAGE, null) ?: return
- val (btnIt, btnEn, btnDe) = Triple(
- findViewById(R.id.btnLangIt),
- findViewById(R.id.btnLangEn),
- findViewById(R.id.btnLangDe)
- )
+ val btnIt = findViewById(R.id.btnLangIt)
+ val btnEn = findViewById(R.id.btnLangEn)
+ val btnDe = findViewById(R.id.btnLangDe)
+ val btnEs = findViewById(R.id.btnLangEs)
+ val btnFr = findViewById(R.id.btnLangFr)
// Add checkmark to selected
btnIt.text = if (saved == "it") "✅ 🇮🇹 Italiano" else "🇮🇹 Italiano"
btnEn.text = if (saved == "en") "✅ 🇬🇧 English" else "🇬🇧 English"
btnDe.text = if (saved == "de") "✅ 🇩🇪 Deutsch" else "🇩🇪 Deutsch"
+ btnEs.text = if (saved == "es") "✅ 🇪🇸 Español" else "🇪🇸 Español"
+ btnFr.text = if (saved == "fr") "✅ 🇫🇷 Français" else "🇫🇷 Français"
}
// ── Step navigation ───────────────────────────────────────────────────
private fun showStep(step: Int) {
+ // Auto-skip Gemini step if already configured
+ if (step == 6 && !(prefs.getString(KEY_GEMINI_KEY, "") ?: "").isNullOrEmpty()) { showStep(7); return }
+ // Auto-skip Bring step if already configured
+ if (step == 7 && !(prefs.getString(KEY_BRING_EMAIL, "") ?: "").isNullOrEmpty()) { showStep(8); return }
+
currentStep = step
stepLanguage.visibility = if (step == 0) View.VISIBLE else View.GONE
stepWelcome.visibility = if (step == 1) View.VISIBLE else View.GONE
@@ -442,7 +496,9 @@ class SetupActivity : AppCompatActivity() {
stepServer.visibility = if (step == 3) View.VISIBLE else View.GONE
stepScale.visibility = if (step == 4) View.VISIBLE else View.GONE
stepScreensaver.visibility = if (step == 5) View.VISIBLE else View.GONE
- stepDone.visibility = if (step == 6) View.VISIBLE else View.GONE
+ stepGemini.visibility = if (step == 6) View.VISIBLE else View.GONE
+ stepBring.visibility = if (step == 7) View.VISIBLE else View.GONE
+ stepDone.visibility = if (step == 8) View.VISIBLE else View.GONE
updateProgressDots()
@@ -478,7 +534,7 @@ class SetupActivity : AppCompatActivity() {
}
// Build summary when entering done step
- if (step == 6) buildSummary()
+ if (step == 8) buildSummary()
// Cancel auto-discover when leaving server step
if (step != 3) discoverCancelled.set(true)
@@ -489,11 +545,11 @@ class SetupActivity : AppCompatActivity() {
private fun updateProgressDots() {
progressDots.removeAllViews()
- // Show 5 dots for steps 1-5; step 0 (language) and step 6 (done) have no dots
- if (currentStep == 0 || currentStep == 6) return
- val active = currentStep // 1..5
+ // Show 7 dots for steps 1-7; step 0 (language) and step 8 (done) have no dots
+ if (currentStep == 0 || currentStep == 8) return
+ val active = currentStep // 1..7
val density = resources.displayMetrics.density
- for (i in 1..5) {
+ for (i in 1..7) {
val dot = View(this)
val sizeDp = if (i == active) 10 else 7
val px = (sizeDp * density).toInt()
@@ -837,7 +893,7 @@ class SetupActivity : AppCompatActivity() {
}
discoveredDevices.clear()
deviceAdapter?.notifyDataSetChanged()
- tvScanStatus.text = "🔍 Scansione in corso…"
+ tvScanStatus.text = getString(R.string.ble_scanning)
tvScanStatus.setTextColor(0xFF94a3b8.toInt())
btnScanBle.isEnabled = false
mgr.startScan()
@@ -850,7 +906,7 @@ class SetupActivity : AppCompatActivity() {
tvSelectedScale.text = "✅ ${info.name}"
tvSelectedScale.visibility = View.VISIBLE
btnScanBle.isEnabled = true
- btnScanBle.text = "🔄 Scansiona di nuovo"
+ btnScanBle.text = getString(R.string.ble_scan_again)
// Start connection test
startScaleTest(info)
}
@@ -863,7 +919,7 @@ class SetupActivity : AppCompatActivity() {
scaleTestCard.visibility = View.VISIBLE
testWeightBox.visibility = View.GONE
step3NextButtons.visibility = View.GONE
- tvTestStatus.text = "🔗 Connessione a ${info.name}…"
+ tvTestStatus.text = getString(R.string.ble_connecting_to).format(info.name)
tvTestStatus.setTextColor(0xFF94a3b8.toInt())
tvTestWeight.text = "— g"
// Disable confirm/retry until we have data
@@ -887,19 +943,19 @@ class SetupActivity : AppCompatActivity() {
}
override fun onConnecting(device: BluetoothDevice) {
if (!isInTestMode) return
- tvTestStatus.text = "🔗 Connessione in corso…"
+ tvTestStatus.text = getString(R.string.ble_connecting)
tvTestStatus.setTextColor(0xFF94a3b8.toInt())
}
override fun onConnected(deviceName: String) {
if (!isInTestMode) return
- tvTestStatus.text = "⚖️ Connesso! Posiziona un oggetto sulla bilancia…"
+ tvTestStatus.text = getString(R.string.ble_connected)
tvTestStatus.setTextColor(0xFF34d399.toInt())
testWeightBox.visibility = View.VISIBLE
findViewById(R.id.btnTestRetry).isEnabled = true
}
override fun onDisconnected() {
if (!isInTestMode) return
- tvTestStatus.text = "⚠️ Connessione persa. Riprova."
+ tvTestStatus.text = getString(R.string.ble_disconnected)
tvTestStatus.setTextColor(0xFFfbbf24.toInt())
testWeightBox.visibility = View.GONE
testHasWeight = false
@@ -914,7 +970,7 @@ class SetupActivity : AppCompatActivity() {
"%g ${reading.unit}".format(reading.value)
tvTestWeight.text = display
testWeightBox.visibility = View.VISIBLE
- tvTestStatus.text = "Peso ricevuto — coincide con quello sulla bilancia?"
+ tvTestStatus.text = getString(R.string.ble_weight_received)
tvTestStatus.setTextColor(0xFF94a3b8.toInt())
findViewById(R.id.btnTestConfirm).isEnabled = true
findViewById(R.id.btnTestRetry).isEnabled = true
@@ -936,10 +992,10 @@ class SetupActivity : AppCompatActivity() {
override fun onScanStopped() {
btnScanBle.isEnabled = true
if (discoveredDevices.isEmpty()) {
- tvScanStatus.text = "Nessuna bilancia trovata. Assicurati che sia accesa e vicina, poi riprova."
+ tvScanStatus.text = getString(R.string.ble_no_scale_found)
tvScanStatus.setTextColor(0xFFfbbf24.toInt())
} else {
- tvScanStatus.text = "Seleziona la tua bilancia dall'elenco."
+ tvScanStatus.text = getString(R.string.ble_select_from_list)
tvScanStatus.setTextColor(0xFF94a3b8.toInt())
}
}
@@ -998,19 +1054,23 @@ class SetupActivity : AppCompatActivity() {
val scaleName = bleManager?.getSavedDeviceName()
val scaleOk = hasScale && scaleName != null
val lang = prefs.getString(KEY_LANGUAGE, "it") ?: "it"
- val langLabel = when (lang) { "en" -> "English 🇬🇧"; "de" -> "Deutsch 🇩🇪"; else -> "Italiano 🇮🇹" }
+ val langLabel = when (lang) { "en" -> "English 🇬🇧"; "de" -> "Deutsch 🇩🇪"; "es" -> "Español 🇪🇸"; "fr" -> "Français 🇫🇷"; else -> "Italiano 🇮🇹" }
val sb = StringBuilder()
sb.appendLine("🌐 ${getString(R.string.summary_lang)}: $langLabel")
if (url.isNotEmpty()) sb.appendLine("🖥️ Server: $url")
sb.appendLine(when {
- scaleOk -> "✅ Bilancia: $scaleName"
- hasScale -> "⚠️ Bilancia: da configurare"
+ scaleOk -> getString(R.string.summary_scale_ok).format(scaleName)
+ hasScale -> "⚠️ ${getString(R.string.summary_scale_warn)}"
else -> "⏭ ${getString(R.string.summary_scale_skip)}"
})
sb.appendLine(if (screensOn) getString(R.string.summary_screensaver_on) else getString(R.string.summary_screensaver_off))
if (pricesOn) sb.appendLine(getString(R.string.summary_prices_on))
if (mealPlanOn) sb.appendLine(getString(R.string.summary_mealplan_on))
if (zeroWasteOn) sb.appendLine(getString(R.string.summary_zerowaste_on))
+ val geminiSet = !(prefs.getString(KEY_GEMINI_KEY, "") ?: "").isNullOrEmpty()
+ val bringSet = !(prefs.getString(KEY_BRING_EMAIL, "") ?: "").isNullOrEmpty()
+ sb.appendLine(if (geminiSet) getString(R.string.summary_gemini_set) else getString(R.string.summary_gemini_skip))
+ sb.appendLine(if (bringSet) getString(R.string.summary_bring_set) else getString(R.string.summary_bring_skip))
summaryText.text = sb.toString().trimEnd()
}
@@ -1026,6 +1086,9 @@ class SetupActivity : AppCompatActivity() {
Thread {
try {
val url = "$baseUrl/api/index.php?action=save_settings"
+ val geminiKey = prefs.getString(KEY_GEMINI_KEY, "") ?: ""
+ val bringEmail = prefs.getString(KEY_BRING_EMAIL, "") ?: ""
+ val bringPassword = prefs.getString(KEY_BRING_PASSWORD, "") ?: ""
val body = buildString {
append("{\"screensaver_enabled\":$screensaver")
append(",\"price_enabled\":$priceEnabled")
@@ -1035,6 +1098,9 @@ class SetupActivity : AppCompatActivity() {
val lanIp = getDeviceLanIp() ?: "127.0.0.1"
append(",\"scale_enabled\":true,\"scale_gateway_url\":\"ws://$lanIp:8765\"")
}
+ if (geminiKey.isNotEmpty()) append(",\"gemini_api_key\":\"${geminiKey.replace("\"", "\\\"\")}\"")
+ if (bringEmail.isNotEmpty()) append(",\"bring_email\":\"${bringEmail.replace("\"", "\\\"\")}\"")
+ if (bringPassword.isNotEmpty()) append(",\"bring_password\":\"${bringPassword.replace("\"", "\\\"\")}\"")
append("}")
}
val conn = (java.net.URL(url).openConnection() as java.net.HttpURLConnection).apply {
diff --git a/evershelf-kiosk/app/src/main/res/layout/activity_setup.xml b/evershelf-kiosk/app/src/main/res/layout/activity_setup.xml
index e16176a..51f1dee 100644
--- a/evershelf-kiosk/app/src/main/res/layout/activity_setup.xml
+++ b/evershelf-kiosk/app/src/main/res/layout/activity_setup.xml
@@ -78,11 +78,11 @@
android:layout_marginBottom="24dp"
android:contentDescription="EverShelf" />
-
+
+ android:backgroundTint="#b91c1c"
+ android:layout_marginBottom="16dp" />
+
+
+
+
@@ -1241,7 +1261,7 @@
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="1"
- android:text="← Indietro"
+ android:text="@string/setup_step_back"
android:textSize="14sp"
android:textAllCaps="false"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
@@ -1254,7 +1274,7 @@
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_weight="2"
- android:text="Avanti →"
+ android:text="@string/setup_step_next"
android:textSize="15sp"
android:textAllCaps="false"
android:backgroundTint="#7c3aed" />
@@ -1262,7 +1282,230 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -1331,7 +1574,7 @@
android:id="@+id/btnLaunch"
android:layout_width="match_parent"
android:layout_height="60dp"
- android:text="🚀 Avvia EverShelf"
+ android:text="@string/btn_launch"
android:textSize="18sp"
android:textAllCaps="false"
android:backgroundTint="#059669" />
diff --git a/evershelf-kiosk/app/src/main/res/values-de/strings.xml b/evershelf-kiosk/app/src/main/res/values-de/strings.xml
index 17da8ef..0e504a3 100644
--- a/evershelf-kiosk/app/src/main/res/values-de/strings.xml
+++ b/evershelf-kiosk/app/src/main/res/values-de/strings.xml
@@ -1,29 +1,37 @@
EverShelf Kiosk
-
-
Bitte zuerst eine URL eingeben
Verbindung wird getestet…
EverShelf-Server gefunden und API aktiv!
Server erreichbar, aber EverShelf-API nicht gefunden. Pfad prüfen.
Server nicht erreichbar
- 🔍 Lokales Netzwerk durchsuchen ✅ Berechtigungen erteilt — Weiter → Suche läuft…
+ 🔍 Lokales Netzwerk durchsuchen
+ ✅ Berechtigungen erteilt — Weiter →
+ Suche läuft…
Suche nach EverShelf-Servern im lokalen Netzwerk…
Kein EverShelf-Server automatisch gefunden. URL manuell eingeben.
Setup beenden?
Die Einrichtung kann später beim erneuten Öffnen der App abgeschlossen werden.
Beenden
Weiter
-
-
+ ← Zurück
+ Weiter →
+ Später einrichten
+ Bestätigen →
Smart-Waage (Optional)
Um eine Bluetooth-Küchenwaage zu verwenden, musst du die EverShelf Scale Gateway App separat installieren.
Hast du eine Bluetooth-Küchenwaage?
✅ Ja, ich habe eine Waage
➡️ Nein, überspringen
-
-
+ 🔍 Suche läuft…
+ Verbunden! Gegenstand auf die Waage legen…
+ Verbindung getrennt. Erneut versuchen.
+ Keine Waage gefunden. Sicherstellen, dass sie eingeschaltet und in der Nähe ist, und erneut versuchen.
+ Waage aus der Liste auswählen.
+ Waage nicht bestätigt. Erneut scannen.
+ 🔄 Erneut scannen
+ Gewicht empfangen — Stimmt es mit der Anzeige überein?
Scale Gateway installiert ✅
Wird beim Fortfahren im Hintergrund gestartet.
Scale Gateway nicht installiert
@@ -32,8 +40,6 @@
Scale Gateway ist aktuell.
Update für Scale Gateway verfügbar
Tippe auf den Button, um jetzt zu aktualisieren.
-
-
Download läuft…
Bitte warten, die Datei wird heruntergeladen.
Installation läuft…
@@ -43,31 +49,56 @@
Download fehlgeschlagen
Verbindung prüfen und erneut versuchen.
Installation fehlgeschlagen
- Aktiviere \'Unbekannte Apps installieren\' in den Einstellungen, dann komm zurück.
+ Aktiviere 'Unbekannte Apps installieren' in den Einstellungen, dann komm zurück.
↩ Nochmal versuchen
-
-
Zurück
🚀 EverShelf starten
🚀 Ohne Waage starten
📥 Scale Gateway installieren
📥 Scale Gateway aktualisieren
-
-
Server-Verbindung wird geprüft…
Server erreichbar ✅
Fehlerberichterstattung aktiv — Installationsfehler werden automatisch an GitHub Issues gesendet.
Server nicht erreichbar ⚠️
Fehler werden GitHub Issues nicht erreichen. URL in Schritt 2 prüfen.
-
- Bildschirmschoner
- Zeigt nach 5 Minuten Inaktivität eine Uhr mit nützlichen Fakten. Standardmäßig deaktiviert (Bildschirm bleibt immer an).
- Bildschirmschoner aktivieren
- Wenn deaktiviert, bleibt der Bildschirm immer an.
+ Funktionen
+ Aktiviere die gewünschten Funktionen. Du kannst sie später jederzeit in den Servereinstellungen ändern.
+ Uhr-Bildschirmschoner
+ Zeigt eine Uhranzeige nach 5 Min. Inaktivität.
+ Einkaufslisten-Preise
+ KI-gestützte automatische Kostensätzung für jeden Artikel.
+ Mahlzeitenplan
+ Plane die Wöchentliche Mahlzeiten mit Rezepten aus deiner Vorratskammer.
+ Zero-Waste-Tipps
+ Beim Kochen Tipps zur Wiederverwendung von Resten anzeigen (Schalen, Kochwasser usw.).
+ Google Gemini AI
+ EverShelf nutzt Google Gemini AI für Rezeptvorschläge, smarte Einkaufsschätzungen und mehr.
-
+Zum Aktivieren den kostenlosen Gemini API-Schlüssel eingeben.
+ Kostenlosen Schlüssel unter: aistudio.google.com → "API-Schlüssel erhalten"
+ API-Schlüssel einfügen (beginnt mit AIza…)
+ Bring! Einkaufsliste
+ EverShelf kann die Einkaufsliste mit der Bring!-App synchronisieren.
+
+Bring!-Zugangsdaten eingeben, um die Integration zu aktivieren.
+ Bring!-E-Mail-Adresse
+ Bring!-Passwort
+ Alles bereit!
+ Die Einrichtung ist abgeschlossen. Auf den Button tippen, um EverShelf im Kiosk-Modus zu starten.
+ KONFIGURATIONSSÜBERSICHT
Sprache
Waage: nicht konfiguriert
Bildschirmschoner: aktiv
Bildschirm immer an (Bildschirmschoner deaktiviert)
-
+ Einkaufslisten-Preise: aktiviert
+ Mahlzeitenplan: aktiviert
+ Zero-Waste-Tipps: aktiviert
+ Gemini AI: aktiviert
+ Gemini AI: nicht konfiguriert
+ Bring!: verbunden
+ Bring!: nicht konfiguriert
+ 🔗 Verbinde mit %s…
+ 🔗 Verbindung wird hergestellt…
+ Waage: %s
+ Waage: nicht bestätigt
+
\ No newline at end of file
diff --git a/evershelf-kiosk/app/src/main/res/values-es/strings.xml b/evershelf-kiosk/app/src/main/res/values-es/strings.xml
new file mode 100644
index 0000000..1646f5f
--- /dev/null
+++ b/evershelf-kiosk/app/src/main/res/values-es/strings.xml
@@ -0,0 +1,104 @@
+
+
+ EverShelf Kiosk
+ Introduce primero una URL
+ Probando conexión…
+ ¡Servidor EverShelf encontrado y API activa!
+ Servidor accesible pero API EverShelf no encontrada. Comprueba la ruta.
+ No se puede alcanzar el servidor
+ 🔍 Buscar en la red local
+ ✅ Permisos concedidos — Continuar →
+ Escaneando…
+ Buscando servidores EverShelf en la red local…
+ Ningún servidor EverShelf encontrado automáticamente. Introduce la URL manualmente.
+ ¿Salir de la configuración?
+ Puedes completar la configuración más tarde cuando vuelvas a abrir la app.
+ Salir
+ Continuar
+ ← Atrás
+ Siguiente →
+ Configurar después
+ Confirmar →
+ Báscula inteligente
+ EverShelf Kiosk incluye una pasarela Bluetooth integrada — no necesitas ninguna app externa. Selecciona tu báscula abajo.
+ ¿Tienes una báscula inteligente Bluetooth?
+ ✅ Sí, tengo una báscula
+ ➡️ No, saltar este paso
+ 🔍 Escaneando…
+ ¡Conectado! Coloca un objeto en la báscula…
+ Conexión perdida. Reintentar.
+ No se encontró ninguna báscula. Asegúrate de que esté encendida y cerca, e inténtalo de nuevo.
+ Selecciona tu báscula de la lista.
+ Báscula no confirmada. Vuelve a escanear.
+ 🔄 Volver a escanear
+ Peso recibido — ¿coincide con el mostrado en la báscula?
+ Báscula guardada ✅
+ La pasarela BLE integrada se conectará automáticamente al inicio.
+ Ninguna báscula seleccionada
+ Escanea las básculas BLE cercanas y toca una para seleccionarla.
+ Escaneando básculas BLE…
+ Servicio BLE de báscula listo.
+ Báscula BLE encontrada
+ Toca la báscula en la lista para conectarte.
+ Descargando…
+ Por favor, espera mientras se descarga el archivo.
+ Instalando…
+ Confirma la instalación en el diálogo que se ha abierto.
+ ¡Instalado correctamente!
+ La app ha sido actualizada.
+ Descarga fallida
+ Comprueba la conexión e inténtalo de nuevo.
+ Instalación fallida
+ Habilita 'Instalar apps desconocidas' en los ajustes y vuelve aquí.
+ ↩ Reintentar
+ Atrás
+ 🚀 Iniciar EverShelf
+ 🚀 Iniciar sin báscula
+ 📥 Instalar Scale Gateway
+ 📥 Actualizar Scale Gateway
+ Comprobando conexión al servidor…
+ Servidor accesible ✅
+ Informe de errores activo — los fallos de instalación se enviarán automáticamente a GitHub Issues.
+ Servidor no accesible ⚠️
+ Los errores no llegarán a GitHub Issues. Comprueba la URL introducida en el paso 2.
+ Funcionalidades
+ Activa las funciones que quieras usar. Puedes cambiarlas en cualquier momento desde los ajustes del servidor.
+ Salvapantallas reloj
+ Muestra un reloj después de 5 min de inactividad.
+ Precios lista de la compra
+ Estimación automática del coste de cada artículo mediante IA.
+ Plan de comidas
+ Planifica las comidas de la semana con recetas basadas en tu despensa.
+ Consejos zero-waste
+ Muestra consejos para reutilizar restos (cáscaras, agua de cocción, etc.) al cocinar.
+ Google Gemini AI
+ EverShelf usa Google Gemini AI para sugerencias de recetas, estimaciones inteligentes de la compra y más.
+
+Para activarla, introduce tu clave API de Gemini gratuita.
+ Obtén tu clave gratuita en: aistudio.google.com → "Obtener clave API"
+ Pega la clave API aquí (empieza por AIza…)
+ Bring! Lista de la compra
+ EverShelf puede sincronizar tu lista de la compra con la app Bring!.
+
+Introduce tus credenciales de Bring! para activar la integración.
+ Correo electrónico de Bring!
+ Contraseña de Bring!
+ ¡Todo listo!
+ La configuración está completa. Pulsa el botón para iniciar EverShelf en modo quiosco.
+ RESUMEN DE CONFIGURACIÓN
+ Idioma
+ Báscula: no configurada
+ Salvapantallas: activo
+ Pantalla siempre encendida (salvapantallas desactivado)
+ Precios lista de la compra: activados
+ Plan de comidas: activado
+ Consejos zero-waste: activados
+ Gemini AI: activada
+ Gemini AI: no configurada
+ Bring!: conectada
+ Bring!: no configurada
+ 🔗 Conectando con %s…
+ 🔗 Estableciendo conexión…
+ Báscula: %s
+ Báscula: no confirmada
+
\ No newline at end of file
diff --git a/evershelf-kiosk/app/src/main/res/values-fr/strings.xml b/evershelf-kiosk/app/src/main/res/values-fr/strings.xml
new file mode 100644
index 0000000..c0c5415
--- /dev/null
+++ b/evershelf-kiosk/app/src/main/res/values-fr/strings.xml
@@ -0,0 +1,104 @@
+
+
+ EverShelf Kiosk
+ Veuillez d'abord saisir une URL
+ Test de connexion…
+ Serveur EverShelf trouvé et API active !
+ Serveur accessible mais API EverShelf introuvable. Vérifiez le chemin.
+ Impossible d'atteindre le serveur
+ 🔍 Rechercher sur le réseau local
+ ✅ Permissions accordées — Continuer →
+ Analyse en cours…
+ Recherche de serveurs EverShelf sur le réseau local…
+ Aucun serveur EverShelf trouvé automatiquement. Entrez l'URL manuellement.
+ Quitter la configuration ?
+ Vous pouvez terminer la configuration plus tard en rouvrant l'app.
+ Quitter
+ Continuer
+ ← Retour
+ Suivant →
+ Configurer plus tard
+ Confirmer →
+ Balance intelligente
+ EverShelf Kiosk inclut une passerelle Bluetooth intégrée — aucune app externe nécessaire. Sélectionnez votre balance ci-dessous.
+ Avez-vous une balance intelligente Bluetooth ?
+ ✅ Oui, j'ai une balance
+ ➡️ Non, ignorer cette étape
+ 🔍 Scan en cours…
+ Connecté ! Posez un objet sur la balance…
+ Connexion perdue. Réessayer.
+ Aucune balance trouvée. Vérifiez qu'elle est allumée et à proximité, puis réessayez.
+ Sélectionnez votre balance dans la liste.
+ Balance non confirmée. Relancer le scan.
+ 🔄 Scanner à nouveau
+ Poids reçu — correspond-il à l'affichage de la balance ?
+ Balance enregistrée ✅
+ La passerelle BLE intégrée se connectera automatiquement au démarrage.
+ Aucune balance sélectionnée
+ Scannez les balances BLE à proximité et appuyez sur l'une d'elles pour la sélectionner.
+ Scan des balances BLE en cours…
+ Service BLE de la balance prêt.
+ Balance BLE trouvée
+ Appuyez sur la balance dans la liste pour vous connecter.
+ Téléchargement en cours…
+ Veuillez patienter, le fichier est en cours de téléchargement.
+ Installation en cours…
+ Confirmez l'installation dans la boîte de dialogue ouverte.
+ Installé avec succès !
+ L'app a été mise à jour.
+ Téléchargement échoué
+ Vérifiez la connexion et réessayez.
+ Installation échouée
+ Activez 'Installer des apps inconnues' dans les paramètres, puis revenez ici.
+ ↩ Réessayer
+ Retour
+ 🚀 Lancer EverShelf
+ 🚀 Lancer sans balance
+ 📥 Installer Scale Gateway
+ 📥 Mettre à jour Scale Gateway
+ Vérification de la connexion au serveur…
+ Serveur accessible ✅
+ Rapport d'erreurs actif — les échecs d'installation seront envoyés automatiquement aux GitHub Issues.
+ Serveur inaccessible ⚠️
+ Les erreurs n'atteindront pas GitHub Issues. Vérifiez l'URL saisie à l'étape 2.
+ Fonctionnalités
+ Activez les fonctions que vous souhaitez utiliser. Vous pourrez les modifier plus tard dans les paramètres du serveur.
+ Horloge écran de veille
+ Affiche une horloge après 5 min d'inactivité.
+ Prix liste de courses
+ Estimation automatique du coût de chaque article via IA.
+ Plan de repas
+ Planifiez les repas de la semaine avec des recettes basées sur votre garde-manger.
+ Conseils zéro déchet
+ Affiche des conseils pour réutiliser les restes (peaux, eau de cuisson, etc.) pendant la cuisson.
+ Google Gemini AI
+ EverShelf utilise Google Gemini AI pour les suggestions de recettes, les estimations intelligentes des courses et plus encore.
+
+Pour l'activer, entrez votre clé API Gemini gratuite.
+ Obtenez votre clé gratuite sur : aistudio.google.com → "Obtenir une clé API"
+ Collez la clé API ici (commence par AIza…)
+ Bring! Liste de courses
+ EverShelf peut synchroniser votre liste de courses avec l'app Bring!.
+
+Entrez vos identifiants Bring! pour activer l'intégration.
+ Adresse e-mail Bring!
+ Mot de passe Bring!
+ Tout est prêt !
+ La configuration est terminée. Appuyez sur le bouton pour lancer EverShelf en mode kiosque.
+ RÉSUMÉ DE CONFIGURATION
+ Langue
+ Balance : non configurée
+ Écran de veille : actif
+ Écran toujours allumé (écran de veille désactivé)
+ Prix liste de courses : activés
+ Plan de repas : activé
+ Conseils zéro déchet : activés
+ Gemini AI : activée
+ Gemini AI : non configurée
+ Bring! : connectée
+ Bring! : non configurée
+ 🔗 Connexion à %s…
+ 🔗 Connexion en cours…
+ Balance : %s
+ Balance : non confirmée
+
\ No newline at end of file
diff --git a/evershelf-kiosk/app/src/main/res/values-it/strings.xml b/evershelf-kiosk/app/src/main/res/values-it/strings.xml
index 368d159..b36a629 100644
--- a/evershelf-kiosk/app/src/main/res/values-it/strings.xml
+++ b/evershelf-kiosk/app/src/main/res/values-it/strings.xml
@@ -1,73 +1,104 @@
EverShelf Kiosk
-
-
Inserisci prima un URL
Verifica connessione…
Server EverShelf trovato e API attiva!
Server raggiungibile ma API EverShelf non trovata. Verifica il percorso.
Impossibile raggiungere il server
- 🔍 Cerca nella rete locale ✅ Permessi concessi — Continua → Scansione in corso…
+ 🔍 Cerca nella rete locale
+ ✅ Permessi concessi — Continua →
+ Scansione in corso…
Ricerca server EverShelf nella rete locale…
- Nessun server EverShelf trovato automaticamente. Inserisci l\'URL manualmente.
+ Nessun server EverShelf trovato automaticamente. Inserisci l'URL manualmente.
Uscire dalla configurazione?
- Puoi completare la configurazione più tardi riaprendo l\'app.
+ Puoi completare la configurazione più tardi riaprendo l'app.
Esci
Continua
-
-
+ ← Indietro
+ Avanti →
+ Lo faccio dopo
+ Conferma →
Bilancia Smart
EverShelf Kiosk include un gateway Bluetooth integrato — nessuna app esterna necessaria. Seleziona la tua bilancia qui sotto.
Hai una bilancia smart Bluetooth?
✅ Sì, ho una bilancia
➡️ No, salta questo passaggio
-
-
+ 🔍 Scansione in corso…
+ Connesso! Posiziona un oggetto sulla bilancia…
+ Connessione persa. Riprova.
+ Nessuna bilancia trovata. Assicurati che sia accesa e vicina, poi riprova.
+ Seleziona la tua bilancia dall'elenco.
+ Bilancia non confermata. Riprova la scansione.
+ 🔄 Scansiona di nuovo
+ Peso ricevuto — coincide con quello sulla bilancia?
Bilancia salvata ✅
- Il gateway BLE integrato si collegherà automaticamente all\'avvio.
+ Il gateway BLE integrato si collegherà automaticamente all'avvio.
Nessuna bilancia selezionata
Scansiona le bilance BLE nelle vicinanze e tocca una per selezionarla.
Scansione bilance BLE in corso…
Servizio BLE bilancia pronto.
Bilancia BLE trovata
- Tocca la bilancia nell\'elenco per connettersi.
-
-
+ Tocca la bilancia nell'elenco per connettersi.
Scaricamento in corso…
Attendi, il file viene scaricato.
Installazione in corso…
- Conferma l\'installazione nel dialog che si è aperto.
+ Conferma l'installazione nel dialog che si è aperto.
Installato con successo!
- L\'app è stata aggiornata.
+ L'app è stata aggiornata.
Download fallito
Controlla la connessione e riprova.
Installazione fallita
- Abilita \'Installa app sconosciute\' nelle impostazioni, poi torna qui.
+ Abilita 'Installa app sconosciute' nelle impostazioni, poi torna qui.
↩ Riprova
-
-
Indietro
🚀 Avvia EverShelf
🚀 Avvia senza bilancia
📥 Installa Scale Gateway
📥 Aggiorna Scale Gateway
-
-
Verifica connessione server…
Server raggiungibile ✅
Segnalazione errori attiva — i problemi di installazione vengono inviati automaticamente alle GitHub Issues.
Server non raggiungibile ⚠️
- Gli errori non raggiungeranno GitHub Issues. Verifica l\'URL inserito al passaggio 2.
-
- Salvaschermo
- Mostra un orologio con fatti utili dopo 5 minuti di inattività. Di default è disattivato (lo schermo resta sempre acceso).
- Attiva salvaschermo
- Se disattivo, lo schermo resta sempre acceso.
+ Gli errori non raggiungeranno GitHub Issues. Verifica l'URL inserito al passaggio 2.
+ Funzionalità
+ Attiva le funzioni che vuoi usare. Puoi sempre cambiarle in seguito dalle impostazioni del server.
+ Salvaschermo orologio
+ Mostra l'overlay orologio dopo 5 min di inattività.
+ Prezzi lista spesa
+ Stima automatica del costo di ogni articolo in lista tramite AI.
+ Piano pasti
+ Pianifica i pasti della settimana suggerendo ricette basate sulla dispensa.
+ Suggerimenti zero-waste
+ Durante la cottura mostra consigli per riutilizzare scarti (bucce, acqua di cottura, ecc.).
+ Google Gemini AI
+ EverShelf usa Google Gemini AI per suggerimenti di ricette, stime intelligenti della spesa e altro ancora.
-
+Per abilitarla, inserisci la tua chiave API Gemini gratuita.
+ Ottieni la chiave gratuita su: aistudio.google.com → "Ottieni chiave API"
+ Incolla la chiave API (inizia con AIza…)
+ Bring! Lista della spesa
+ EverShelf può sincronizzare la lista della spesa con l'app Bring!.
+
+Inserisci le credenziali del tuo account Bring! per abilitare l'integrazione.
+ Email Bring!
+ Password Bring!
+ Tutto pronto!
+ La configurazione è completa. Premi il pulsante per avviare EverShelf in modalità kiosk.
+ RIEPILOGO CONFIGURAZIONE
Lingua
Bilancia: non configurata
Salvaschermo: attivo
Schermo sempre acceso (salvaschermo disattivato)
-
+ Prezzi lista spesa: abilitati
+ Piano pasti: abilitato
+ Suggerimenti zero-waste: abilitati
+ Gemini AI: abilitata
+ Gemini AI: non configurata
+ Bring!: connessa
+ Bring!: non configurata
+ 🔗 Connessione a %s…
+ 🔗 Connessione in corso…
+ Bilancia: %s
+ Bilancia: da configurare
+
\ No newline at end of file
diff --git a/evershelf-kiosk/app/src/main/res/values/strings.xml b/evershelf-kiosk/app/src/main/res/values/strings.xml
index 65cf3ab..9c7cc8e 100644
--- a/evershelf-kiosk/app/src/main/res/values/strings.xml
+++ b/evershelf-kiosk/app/src/main/res/values/strings.xml
@@ -1,28 +1,45 @@
+
EverShelf Kiosk
-
+
Please enter a URL first
Testing connection…
EverShelf server found and API active!
Server reachable but EverShelf API not found. Check the path.
Cannot reach server
- 🔍 Search local network ✅ Permissions granted — Continue → Scanning…
+ 🔍 Search local network
+ ✅ Permissions granted — Continue →
+ Scanning…
Searching for EverShelf servers on the local network…
No EverShelf server found automatically. Enter the URL manually.
Exit setup?
You can complete setup later when you reopen the app.
Exit
Continue
+ ← Back
+ Next →
+ Set up later
+ Confirm →
-
+
Smart Scale
EverShelf Kiosk includes a built-in Bluetooth gateway — no external app needed. Select your scale below.
Do you have a Bluetooth smart scale?
✅ Yes, I have a scale
➡️ No, skip this step
-
+
+ 🔍 Scanning…
+ Connected! Place an object on the scale…
+ Connection lost. Retry.
+ No scale found. Make sure it is on and nearby, then retry.
+ Select your scale from the list.
+ Scale not confirmed. Retry scan.
+ 🔄 Scan again
+ Weight received — does it match the display?
+
+
Scale device saved ✅
The integrated BLE gateway will connect automatically on startup.
No scale selected
@@ -32,52 +49,76 @@
BLE scale found
Tap the scale in the list to connect.
-
- Scaricamento in corso…
- Attendi, il file viene scaricato.
- Installazione in corso…
- Conferma l\'installazione nel dialog che si è aperto.
- Installato con successo!
- L\'app è stata aggiornata.
- Download fallito
- Controlla la connessione e riprova.
- Installazione fallita
- Abilita \'Installa app sconosciute\' nelle impostazioni, poi torna qui.
- ↩ Riprova
+
+ Downloading…
+ Please wait, the file is being downloaded.
+ Installing…
+ Confirm the installation in the dialog that has opened.
+ Installed successfully!
+ The app has been updated.
+ Download failed
+ Check your connection and try again.
+ Installation failed
+ Enable \'Install unknown apps\' in settings, then come back here.
+ ↩ Retry
-
+
Back
🚀 Launch EverShelf
🚀 Launch without scale
📥 Install Scale Gateway
📥 Update Scale Gateway
-
+
Checking server connection…
Server reachable ✅
Error reporting is active — install failures will be sent to GitHub Issues automatically.
Server not reachable ⚠️
Install errors won\'t reach GitHub Issues. Check the URL entered in step 2.
-
- Funzionalità
- Attiva le funzioni che vuoi usare. Puoi sempre cambiarle in seguito dalle impostazioni del server.
- Salvaschermo in-app
- Shows a clock with useful facts after 5 minutes of inactivity. Off by default (screen stays always on).
- Salvaschermo orologio
- Mostra l\'overlay orologio dopo 5 min di inattività.
- Prezzi lista spesa
- Stima automatica del costo di ogni articolo in lista tramite AI.
- Piano pasti
- Pianifica i pasti della settimana suggerendo ricette basate sulla dispensa.
- Suggerimenti zero-waste
- Durante la cottura mostra consigli per riutilizzare scarti (bucce, acqua di cottura, ecc.).
-
+
+ Features
+ Enable the features you want to use. You can always change them later in the server settings.
+ Clock screensaver
+ Shows a clock overlay after 5 min of inactivity.
+ Shopping list prices
+ AI-powered automatic cost estimate for each item in the list.
+ Meal plan
+ Plan the week\'s meals with recipes based on your pantry.
+ Zero-waste tips
+ Show tips for reusing scraps (peels, cooking water, etc.) while cooking.
+
+
+ Google Gemini AI
+ EverShelf uses Google Gemini AI for recipe suggestions, smart shopping estimates and more.\n\nTo enable it, enter your free Gemini API key below.
+ Get your free key at: aistudio.google.com → \"Get API key\"
+ Paste your API key here (starts with AIza…)
+
+
+ Bring! Shopping List
+ EverShelf can sync your shopping list with the Bring! app.\n\nEnter your Bring! account credentials to enable this integration.
+ Bring! email address
+ Bring! password
+
+
+ All set!
+ Setup is complete. Press the button below to launch EverShelf in kiosk mode.
+ CONFIGURATION SUMMARY
+
+
Language
Scale: not configured
- Screensaver: abilitato
- Screensaver: disabilitato
- Prezzi lista spesa: abilitati
- Piano pasti: abilitato
- Suggerimenti zero-waste: abilitati
+ Screensaver: enabled
+ Screen always on (screensaver disabled)
+ Shopping list prices: enabled
+ Meal plan: enabled
+ Zero-waste tips: enabled
+ Gemini AI: enabled
+ Gemini AI: not configured
+ Bring!: connected
+ Bring!: not configured
+ 🔗 Connecting to %s…
+ 🔗 Connecting…
+ Scale: %s
+ Scale: not confirmed
diff --git a/index.html b/index.html
index fcdbe6b..e624d8c 100644
--- a/index.html
+++ b/index.html
@@ -59,10 +59,7 @@
-
+