From a5094920bf048b1ba6b7366c7bc3770911648071 Mon Sep 17 00:00:00 2001 From: dadaloop82 Date: Tue, 5 May 2026 17:31:14 +0000 Subject: [PATCH] kiosk: remove all external gateway app references + update docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SettingsActivity: replace GATEWAY_PACKAGE / PackageManager check with GatewayService status; show BLE device name + live :8765 probe; buttons now restart GatewayService or redirect to setup wizard - activity_settings.xml: rename section label to 'BILANCIA SMART', update button text to '⚙️ Configura bilancia' - evershelf-scale-gateway/README.md: add DEPRECATED notice (gateway is now integrated into kiosk v1.6.0+, this app is no longer maintained) - evershelf-kiosk/README.md: full rewrite — reflects v1.6.0, integrated BLE gateway, 6-step wizard, permissions table, protocol reference - README.md: update kiosk features (remove gateway APK install/launch, add integrated BLE service), update scale section, update architecture, add kiosk v1.6.0 entry in Recent Updates --- README.md | 16 +-- evershelf-kiosk/README.md | 134 ++++++++++++++---- .../evershelf/kiosk/SettingsActivity.kt | 101 ++++++------- .../src/main/res/layout/activity_settings.xml | 6 +- evershelf-scale-gateway/README.md | 16 ++- 5 files changed, 182 insertions(+), 91 deletions(-) diff --git a/README.md b/README.md index a4bd587..ea4962a 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,9 @@ - **Demo mode (JS)** — Full frontend demo with mock pantry data, Gemini enabled, Bring! writes silently no-op'd; accessible via `?demo=1` or `.env` `DEMO_MODE=true`. - **Graceful Bring! no-key state** — When Bring! credentials are not configured, the shopping tab shows a friendly message with a direct link to Settings instead of a raw error. - **Use-quantity guard** — Consuming more than the stocked quantity at a given location is now blocked client-side with a shake animation on the quantity field. +- **Kiosk v1.6.0: BLE scale gateway integrated** — The standalone Scale Gateway app is no longer needed. BLE scanning, GATT connection and the WebSocket server (`:8765`) now run as a built-in `GatewayService` foreground service inside the kiosk app. Setup step 4 shows a live BLE device scan — users select their scale directly, no external APK install required. The external gateway app is deprecated. - **Kiosk setup wizard overhaul** — Auto-discovery rewritten with `ExecutorCompletionService` + `NetworkInterface` (no deprecated `WifiManager`), 60 parallel TCP pre-checks, real-time UI feedback, ports 80/443/8080/8443, correct LAN subnet detection (VPN/cellular interfaces filtered, `wlan`/`eth` prioritised). - **Kiosk permissions flow** — Grant button transforms into a green "✅ Permessi concessi — Continua →" button after permissions are granted instead of just showing a card. -- **Gateway install error reporting** — APK installation failures now call `ErrorReporter.reportMessage()` with status code and message; `ErrorReporter.init()` is called at `onCreate` so errors in step 4 are never silently dropped. -- **Gateway auto pre-configuration** — After a successful gateway install the setup wizard POSTs `scale_enabled=true` + `scale_gateway_url=ws://127.0.0.1:8765` to the server, so the webapp works with the scale out-of-the-box without manual settings. - **3 new AI features (Gemini)** — Storage/shelf-life hint shown inline in the add form; AI-enriched shopping suggestions with a short practical tip per item; plain-language anomaly explanation via a "🤖 Spiega" button on anomaly banners. - **Security hardening** — `get_settings` no longer exposes API keys in plain text (boolean flags only); `save_settings` protected by optional `SETTINGS_TOKEN` (validated with `hash_equals`); native `DEMO_MODE` in `.env` blocks all write operations at the PHP router level before any other guard. - **Real-time webapp update detection** — An inline header pill appears when a newer release is on GitHub (checked on load + every 30 min); no intrusive full-page banners. @@ -114,16 +113,15 @@ - **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 -- **Android gateway app** — [`evershelf-scale-gateway/`](evershelf-scale-gateway/) — open-source, downloadable APK +- **Built into kiosk (v1.6.0+)** — BLE gateway runs as an integrated foreground service inside the [EverShelf Kiosk](evershelf-kiosk/) app; no separate APK needed. The standalone gateway app in [`evershelf-scale-gateway/`](evershelf-scale-gateway/) is deprecated but kept for non-kiosk use cases. ### 📺 Android Kiosk Mode (Add-on) - **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, scale/gateway, screensaver, summary) +- **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) -- **Gateway auto-install** — Downloads and installs the Scale Gateway APK directly from the wizard; errors are automatically reported via `ErrorReporter` -- **Gateway auto-pre-configuration** — On successful install, the wizard writes `scale_enabled` and `scale_gateway_url` to the EverShelf server so the webapp is ready immediately -- **Gateway auto-launch** — Launches the Scale Gateway in the background on startup +- **Built-in BLE scale gateway** — `GatewayService` foreground service; BLE scanning + WebSocket server `:8765` run 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_enabled` and `scale_gateway_url=ws://127.0.0.1:8765` to 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 @@ -297,8 +295,8 @@ evershelf/ ├── backups/ # Local DB backups └── *.json # Token/cache files -evershelf-scale-gateway/ # ⚖️ Android BLE gateway (add-on) - ├── README.md # Setup & protocol docs +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) diff --git a/evershelf-kiosk/README.md b/evershelf-kiosk/README.md index 231f65e..ae66bb9 100644 --- a/evershelf-kiosk/README.md +++ b/evershelf-kiosk/README.md @@ -1,58 +1,84 @@ # EverShelf Kiosk -Android kiosk app for wall-mounted kitchen tablets. A pure full-screen WebView wrapper that displays the EverShelf web interface in immersive mode — no BLE, no gateway, just the web app locked to screen. +Android kiosk app for wall-mounted kitchen tablets. Full-screen WebView wrapper with integrated BLE scale gateway — no external apps required. -> **Version:** 1.2.0 (versionCode 3) +> **Version:** 1.6.0 (versionCode 10) > **Package:** `it.dadaloop.evershelf.kiosk` +> **Min SDK:** Android 7.0 (API 24) + +--- ## Features +### Kiosk Mode - **Full-screen WebView** — immersive mode hides status bar and navigation bar - **True kiosk lock** — screen pinning (`startLockTask`) blocks home/recent/back buttons -- **3-step setup wizard** — Welcome → Server URL (with connection test) → Scale Gateway detection -- **Gateway auto-launch** — starts [EverShelf Scale Gateway](../evershelf-scale-gateway/) in background on boot - **Exit button (✕)** — visible in header, requires confirmation dialog to exit kiosk - **Hard refresh (↻)** — clears WebView cache to pick up web app updates instantly -- **Camera & microphone** — runtime permission handling for barcode scanning and voice - **SSL support** — accepts self-signed certificates for local HTTPS servers -- **Splash screen** — branded 1.5-second splash on startup - **Update notifications** — checks GitHub releases every 6 hours, shows auto-dismiss banner +- **Native TTS bridge** — cooking mode voice readout uses Android TextToSpeech directly - **Settings activity** — change server URL, test connection, re-run setup wizard -- **Error recovery** — retry page when server is unreachable + +### BLE Scale Gateway (integrated, no external app) +- **Built-in BLE gateway** — `GatewayService` foreground service handles BLE scanning and connection automatically when a scale is configured +- **WebSocket server** — exposes scale data on `ws://127.0.0.1:8765`, fully protocol-compatible with the legacy standalone gateway app (no webapp JS changes needed) +- **Auto-start** — service starts automatically on kiosk launch if a scale device is configured +- **Auto-reconnect** — reconnects automatically after 8 seconds if the BLE link drops +- **Multi-protocol** — supports Bluetooth SIG Weight Scale (`0x181D`/`0x2A9D`), Body Composition (`0x181B`/`0x2A9C`), QN/Yolanda scales, and 100+ models via generic fallback heuristic + +### Setup Wizard (6 steps) +1. **Language** — choose Italian / English / German +2. **Welcome** — intro and privacy information +3. **Permissions** — camera, microphone, BLE permissions with in-wizard grant flow +4. **Server URL** — enter your EverShelf URL; auto-discovery scans the LAN (60 parallel threads, ports 80/443/8080/8443) +5. **Smart Scale** — optional: scan for BLE scales and select yours from the discovered device list (mandatory before proceeding if you choose "yes") +6. **Screensaver** — toggle display sleep after inactivity + +--- ## Architecture ``` KioskActivity (WebView — full-screen EverShelf) - ├── Setup wizard (3 steps, shown on first launch only) + ├── SetupActivity (6-step wizard, shown on first launch only) + ├── SettingsActivity (URL, scale status, re-run wizard) ├── Immersive mode (SYSTEM_UI_FLAG_IMMERSIVE_STICKY) ├── Screen pinning (startLockTask / stopLockTask) ├── JS bridge (_kioskBridge: exit, hardReload) - ├── Header injection (✕ exit + ↻ refresh buttons) - └── Gateway launcher (launches gateway APK in background) - ↓ FLAG_ACTIVITY_NEW_TASK - EverShelf Scale Gateway (separate app, runs in background) + └── GatewayService (foreground service — BLE + WebSocket) + ├── BleScaleManager — BLE scanning, GATT, auto-reconnect + ├── GatewayWebSocketServer — WebSocket server :8765 + └── ScaleProtocol — multi-protocol BLE weight parser ``` -The kiosk app does **not** contain any BLE or scale code. Scale functionality is handled entirely by the separate [EverShelf Scale Gateway](../evershelf-scale-gateway/) app. +The kiosk app is fully self-contained. No separate gateway app is required. + +--- ## Setup -1. Install both APKs on your Android tablet: - - **EverShelf Kiosk** (this app) - - **[EverShelf Scale Gateway](../evershelf-scale-gateway/)** (optional, for smart scale support) -2. Launch the kiosk app — the setup wizard starts automatically -3. Enter your EverShelf server URL (e.g. `https://192.168.1.100/dispensa`) -4. The wizard tests the connection and detects the gateway app -5. Done — the web app loads in full-screen kiosk mode +1. Install the **EverShelf Kiosk** APK on your Android tablet +2. Launch the app — the setup wizard starts automatically +3. Choose your language +4. Grant camera, microphone and Bluetooth permissions when prompted +5. Enter your EverShelf server URL (e.g. `https://192.168.1.100/dispensa`) or use auto-discovery +6. If you have a Bluetooth scale: tap **"Sì, ho una bilancia"**, wait for the BLE scan, then tap your scale in the list +7. Done — the web app loads in full-screen kiosk mode + +### Scale Configuration + +BLE scale setup happens inside the kiosk app itself — **no external app needed**: + +- During the **setup wizard (step 5)**, the app scans for nearby BLE scales and shows them in a list. Devices most likely to be scales are marked with ⭐. +- Tap a device to select it. The selection is saved and the "Next" button becomes enabled. +- From the **Settings screen**, you can restart the BLE service or reconfigure the scale device. ### Exiting Kiosk Mode -Tap the **✕** button in the header (left of the title). A confirmation dialog appears — tap "Esci" to exit. +Tap the **✕** button in the header. A confirmation dialog appears — tap "Esci" to exit. -### Triple-tap (Developer) - -Triple-tap the setup wizard title to access hidden settings. +--- ## Permissions @@ -60,12 +86,57 @@ Triple-tap the setup wizard title to access hidden settings. |---|---| | `INTERNET` | Load EverShelf web app | | `ACCESS_NETWORK_STATE` | Check connectivity | -| `ACCESS_WIFI_STATE` | WiFi status | +| `ACCESS_WIFI_STATE` | LAN subnet detection for auto-discovery | | `WAKE_LOCK` | Keep screen on | | `CAMERA` | Barcode scanning, AI photo identification | | `RECORD_AUDIO` | Voice input in chat assistant | | `READ_MEDIA_IMAGES` / `READ_EXTERNAL_STORAGE` | Image access for AI scan | -| `REORDER_TASKS` | Bring kiosk to foreground after gateway launch | +| `REORDER_TASKS` | Bring kiosk to foreground | +| `BLUETOOTH` / `BLUETOOTH_ADMIN` | BLE (Android ≤ 11) | +| `BLUETOOTH_SCAN` / `BLUETOOTH_CONNECT` | BLE scan and connect (Android 12+) | +| `ACCESS_FINE_LOCATION` | Required for BLE scan on Android < 12 | +| `FOREGROUND_SERVICE` | Run BLE gateway as foreground service | +| `FOREGROUND_SERVICE_CONNECTED_DEVICE` | Service type for BLE (Android 14+) | + +--- + +## Supported Scale Protocols + +| Protocol | Service UUID | Notes | +|---|---|---| +| **Bluetooth SIG Weight Scale** | `0x181D` / char `0x2A9D` | Most compatible | +| **Bluetooth SIG Body Composition** | `0x181B` / char `0x2A9C` | Weight + body fat %, BMI | +| **QN/Yolanda** | Custom UUIDs | Xiaomi Mi Scale 2, Renpho, etc. | +| **Generic fallback** | Any notifiable characteristic | Auto-heuristic parsing for 100+ models | + +### Verified compatible scales +- Xiaomi Mi Body Composition Scale 2 +- Renpho Smart Body Fat Scale +- INEVIFIT Smart Body Fat Scale +- Any [openScale-compatible scale](https://github.com/oliexdev/openScale/wiki/Supported-scales) + +--- + +## WebSocket Protocol + +The built-in WebSocket server speaks the same protocol as the legacy standalone gateway app — the EverShelf webapp needs no changes. + +**Server → client:** +```json +{"type":"status","state":"connected","device":"Mi Scale 2","battery":85} +{"type":"status","state":"disconnected"} +{"type":"weight","value":72.50,"unit":"kg","stable":true,"timestamp":1712345678000} +{"type":"pong"} +``` + +**Client → server:** +```json +{"type":"get_status"} +{"type":"get_weight"} +{"type":"ping"} +``` + +--- ## Building @@ -75,17 +146,22 @@ cd evershelf-kiosk # APK at app/build/outputs/apk/debug/app-debug.apk ``` -For release builds: +For release: ```bash ./gradlew assembleRelease ``` +--- + ## Requirements - Android 7.0+ (API 24) +- Bluetooth LE support (for scale integration) - Network access to EverShelf server -- EverShelf Scale Gateway app (optional, for smart scale support) + +--- ## License -GPLv3 — see [LICENSE](../LICENSE) +MIT — see [LICENSE](../LICENSE) + diff --git a/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/SettingsActivity.kt b/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/SettingsActivity.kt index 4d539a6..0b91838 100644 --- a/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/SettingsActivity.kt +++ b/evershelf-kiosk/app/src/main/kotlin/it/dadaloop/evershelf/kiosk/SettingsActivity.kt @@ -3,8 +3,7 @@ package it.dadaloop.evershelf.kiosk import android.content.Context import android.content.Intent import android.content.SharedPreferences -import android.content.pm.PackageManager -import android.net.Uri +import android.os.Build import android.os.Bundle import android.view.WindowManager import android.widget.EditText @@ -13,6 +12,7 @@ import android.widget.Toast import androidx.appcompat.app.AppCompatActivity import com.google.android.material.button.MaterialButton import com.google.android.material.switchmaterial.SwitchMaterial +import it.dadaloop.evershelf.kiosk.scale.GatewayService import java.net.URL import javax.net.ssl.HttpsURLConnection import javax.net.ssl.SSLContext @@ -29,7 +29,7 @@ class SettingsActivity : AppCompatActivity() { private const val KEY_URL = "evershelf_url" private const val KEY_SETUP_COMPLETE = "setup_complete" private const val KEY_SCREENSAVER = "screensaver_enabled" - private const val GATEWAY_PACKAGE = "it.dadaloop.evershelf.scalegate" + private const val KEY_HAS_SCALE = "has_scale" } override fun attachBaseContext(newBase: Context) { @@ -51,53 +51,58 @@ class SettingsActivity : AppCompatActivity() { val switchScreensaver = findViewById(R.id.switchScreensaver) switchScreensaver.isChecked = prefs.getBoolean(KEY_SCREENSAVER, false) - // Gateway status - val gatewayInstalled = try { - packageManager.getPackageInfo(GATEWAY_PACKAGE, 0) - true - } catch (e: PackageManager.NameNotFoundException) { - false - } - val statusView = findViewById(R.id.scaleGatewayStatus) - val deviceView = findViewById(R.id.scaleDeviceInfo) - if (gatewayInstalled) { - statusView.text = "Installed" - statusView.setTextColor(0xFF34d399.toInt()) - deviceView.text = "EverShelf Scale Gateway app is installed" - } else { - statusView.text = "Not installed" - statusView.setTextColor(0xFFfbbf24.toInt()) - deviceView.text = "Install the Scale Gateway app to use a Bluetooth scale" - } + // ── Smart Scale (BLE gateway service) ────────────────────────────── + val hasScale = prefs.getBoolean(KEY_HAS_SCALE, false) + val deviceName = prefs.getString("scale_device_name", null) + val deviceAddr = prefs.getString("scale_device_address", null) + val statusView = findViewById(R.id.scaleGatewayStatus) + val deviceView = findViewById(R.id.scaleDeviceInfo) + val btnScaleAction = findViewById(R.id.btnConfigureGateway) - val btnConfigureGateway = findViewById(R.id.btnConfigureGateway) - if (gatewayInstalled) { - btnConfigureGateway.visibility = android.view.View.VISIBLE - btnConfigureGateway.setOnClickListener { - val intent = packageManager.getLaunchIntentForPackage(GATEWAY_PACKAGE) - if (intent != null) startActivity(intent) - } - // Probe WebSocket port in background to show live status - Thread { - val running = try { - java.net.Socket().use { s -> - s.connect(java.net.InetSocketAddress("127.0.0.1", 8765), 1200); true - } - } catch (_: Exception) { false } - runOnUiThread { - if (running) { - statusView.text = "Attivo ✅" - statusView.setTextColor(0xFF34d399.toInt()) - deviceView.text = "Gateway in ascolto su ws://127.0.0.1:8765" - } else { - statusView.text = "Installato, non avviato ⚠️" - statusView.setTextColor(0xFFfbbf24.toInt()) - deviceView.text = "Premi \"Apri Gateway\" per avviarlo e configurarlo" - } + when { + !hasScale || deviceAddr == null -> { + statusView.text = "Non configurata" + statusView.setTextColor(0xFF94a3b8.toInt()) + deviceView.text = "Nessuna bilancia configurata — riesegui il wizard per aggiungerne una" + btnScaleAction.visibility = android.view.View.VISIBLE + btnScaleAction.text = "⚙️ Configura bilancia" + btnScaleAction.setOnClickListener { + prefs.edit().putBoolean(KEY_SETUP_COMPLETE, false).apply() + startActivity(Intent(this, SetupActivity::class.java)) + finish() } - }.start() - } else { - btnConfigureGateway.visibility = android.view.View.GONE + } + else -> { + statusView.text = "Configurata" + statusView.setTextColor(0xFF34d399.toInt()) + deviceView.text = deviceName ?: deviceAddr + btnScaleAction.visibility = android.view.View.VISIBLE + btnScaleAction.text = "🔄 Riavvia servizio bilancia" + btnScaleAction.setOnClickListener { + GatewayService.stop(this) + GatewayService.start(this) + Toast.makeText(this, "Servizio bilancia riavviato", Toast.LENGTH_SHORT).show() + } + // Probe WebSocket port to show live status + Thread { + val running = try { + java.net.Socket().use { s -> + s.connect(java.net.InetSocketAddress("127.0.0.1", 8765), 1200); true + } + } catch (_: Exception) { false } + runOnUiThread { + if (running) { + statusView.text = "Attivo ✅" + statusView.setTextColor(0xFF34d399.toInt()) + deviceView.text = "${deviceName ?: "Bilancia"} — ws://127.0.0.1:8765" + } else { + statusView.text = "Non avviato ⚠️" + statusView.setTextColor(0xFFfbbf24.toInt()) + deviceView.text = "${deviceName ?: "Bilancia"} — servizio non in esecuzione" + } + } + }.start() + } } // Back diff --git a/evershelf-kiosk/app/src/main/res/layout/activity_settings.xml b/evershelf-kiosk/app/src/main/res/layout/activity_settings.xml index c5a3e3d..4b6fcd9 100644 --- a/evershelf-kiosk/app/src/main/res/layout/activity_settings.xml +++ b/evershelf-kiosk/app/src/main/res/layout/activity_settings.xml @@ -95,7 +95,7 @@ @@ -146,7 +146,7 @@ android:id="@+id/btnConfigureGateway" android:layout_width="match_parent" android:layout_height="40dp" - android:text="⚙️ Apri Gateway per configurarlo" + android:text="⚙️ Configura bilancia" android:textSize="13sp" android:textAllCaps="false" style="@style/Widget.MaterialComponents.Button.OutlinedButton" diff --git a/evershelf-scale-gateway/README.md b/evershelf-scale-gateway/README.md index 81ee071..43f4eb2 100644 --- a/evershelf-scale-gateway/README.md +++ b/evershelf-scale-gateway/README.md @@ -1,4 +1,16 @@ -# EverShelf Scale Gateway +# ~~EverShelf Scale Gateway~~ — DEPRECATED + +> ⚠️ **This app is deprecated and no longer maintained.** +> +> As of **EverShelf Kiosk v1.6.0**, BLE scale support is fully integrated into the kiosk app itself. You no longer need to install or configure this separate gateway app. +> +> **If you are using the EverShelf Kiosk app** → the scale gateway runs automatically as a background service. Configure your Bluetooth scale in **step 4 of the setup wizard**. +> +> **If you are NOT using the kiosk app** (standalone Android tablet) → you may still use this APK, but no new releases will be published. + +--- + +# EverShelf Scale Gateway (legacy) > Android gateway app that bridges Bluetooth LE smart scales with EverShelf via WebSocket. @@ -12,7 +24,7 @@ Smart Scale ──(BLE)──► Android Gateway App ──(WebSocket/LAN)── The app runs a local WebSocket server (port **8765**) on your Android device. The EverShelf server connects to it via a server-side relay (`api/scale_relay.php` SSE + `api/scale_ping.php` WebSocket client), avoiding mixed-content (HTTPS→WS) issues. Weight readings are streamed to the browser in real time. -> **Kiosk integration:** When using the [EverShelf Kiosk](../evershelf-kiosk/) app, the gateway is launched automatically in the background — no manual setup needed. +> **Kiosk integration (v1.6.0+):** The gateway is now **built into the EverShelf Kiosk app** as a foreground service. This separate app is not needed when using the kiosk. ---