Skip to content

EVDx v1.5.1-pre — Critical BLE/OBD-II fixes

Pre-release
Pre-release

Choose a tag to compare

@waleedmandour waleedmandour released this 17 Jun 18:59

EVDx v1.5.1-pre

Pre-release with comprehensive fixes: BLE/OBD-II core fixes, real Mode 22 (manufacturer-specific) PID support, voice commands wired to actual actions, Arabic PDF rendering, onboarding flow with real BLE scan, WiFi cleartext whitelist expansion, settings persistence, and dead code removal.

Full details in CHANGELOG-v1.5.1.md. The commits in this pre-release:

  • c8bacdf — v1.5.1: Critical BLE/OBD-II fixes + real diagnostic scan
  • 5498d6b — v1.5.1+: CI workflow, README update, 9 audit-driven bug fixes
  • 6ccad7d — docs: bilingual user guide HTML sources
  • 3534fd8 — v1.5.1++: Voice commands, real OBD (Mode 22), PDF Arabic, onboarding, dead code

What's New in this Build

1. Real OBD fields via Mode 22 (manufacturer-specific) PIDs

The biggest user-visible improvement. Previously, when connected to a real OBD-II adapter, only the standard SAE J1979 Mode 01 PIDs (speed, RPM, coolant temp, voltage, etc.) were polled. All the EV-specific data — pack voltage, pack current, SOC, SOH, cell voltages, battery temp, motor temp, inverter temp, coolant temps, aux battery, insulation resistance — stayed at their default values (0, 100, 999). Users saw a mix of real data and stale defaults.

Now EVDx queries manufacturer-specific Mode 22 PIDs for each brand:

Brand Custom PIDs What's now exposed
BYD 0x2101–0x210A BMS Pack V/I, SOC, Cell Max/Min V, temps
Tesla 0x2110–0x2219 Pack V/I, nominal + remaining energy
Nissan 0x5B9A–0x5BA4 Battery Pack V/I, SOC, cell voltages
Hyundai/Kia 0x2100–0x21FF BMS data
VW Group UDS 0x22xx Battery data via UDS
+ 13 more brands see src/data/vehicles.json

How it works:

  1. BLEService.setCustomPIDs(defs) is called when the user picks a vehicle (via store.setActiveVehicle).
  2. During connect(), detectCustomPIDs() probes each PID with a 1.5s timeout and stores the supported subset in adapterInfo.supportedCustomPIDs.
  3. startPolling() interleaves Mode 22 queries with the standard Mode 01 round-robin.
  4. parseOBDResponse handles Mode 62 responses: looks up the PID def, evaluates the formula via BLEService.evaluateFormula (strict-whitelist, no eval), and maps the result to the right vehicleData field by name.
  5. Derived values are computed when standard PIDs update: power = V × A / 1000, range = SOC × capacity × 6 km/kWh.

Safety: The formula evaluator uses a strict regex whitelist (/^[ABCD\s\d+\-*/()<>|&^.,]*$/) and the Function constructor — it rejects property access, function calls, and global references. 8 new protocol tests cover BYD/Tesla/Nissan formulas + safety (34/34 tests pass, was 26).

2. Voice commands wired to actual actions (both EN + AR)

Previously, 12 voice commands spoke a confirmation but performed no action. Now all are wired:

Command Action
'clear codes' / 'امسح الأعطال' bleService.clearDTCs() (Mode 04) + clearDTCs() + setMilOn(false)
'switch to arabic' / 'غيّر اللغة' updateSettings({ language: 'ar' }) + i18n.changeLanguage('ar')
'switch to english' updateSettings({ language: 'en' }) + i18n.changeLanguage('en')
'turn off voice' / 'إيقاف الصوت' updateSettings({ voiceAssistant: false })
'enable alerts' / 'فعّل التنبيهات' updateSettings({ notifications: true })
'mute alerts' / 'كتم التنبيهات' updateSettings({ notifications: false })
'eco' / 'اقتصادي' updateVehicleData({ mode: 'eco' })
'sport' / 'رياضي' updateVehicleData({ mode: 'sport' })
'normal mode' / 'عادي' updateVehicleData({ mode: 'normal' })
'export report' / 'تصدير التقرير' generateReport() + downloadBlob() (PDF generated based on active tab)
'start demo' / 'بدء العرض' connectDemo()
'stop demo' / 'إيقاف العرض' disconnect() (only if in demo mode — won't kill real BLE)
'connect adapter' / 'وصّل' connectBluetooth() if autoConnect, else navigate to Device tab
'disconnect adapter' / 'افصل' disconnect()

3. PDF Arabic rendering fixed

jsPDF's built-in Helvetica font has no Arabic glyphs, and even with an Arabic font embedded, jsPDF doesn't shape Arabic letters (join them correctly) or handle RTL. Fixed by:

  1. Bundling Amiri-Regular.ttf + Amiri-Bold.ttf in public/fonts/ (1.2 MB total).
  2. loadArabicFont() fetches them at PDF-gen time and registers via doc.addFileToVFS + addFont.
  3. shapeArabic() runs text through arabic-reshaper (new dep) to convert logical-order Arabic into presentation forms, then reverses for RTL display.
  4. writeText() helper shapes text + flips alignment for Arabic.
  5. All 40 doc.text/doc.setFont calls migrated to writeText/setFont.

Also fixed: addDarkBackground() was missing on 3 of 5 addPage sites, causing white-on-white invisible text on multi-page reports. Now called after every addPage.

4. OnboardingFlow 'Connect Adapter' button — real scan + permissions

Was a fake 2-second timer that did nothing. Now:

  1. Calls requestBlePermissions() (Android 12+: BLUETOOTH_CONNECT + BLUETOOTH_SCAN).
  2. Initializes bleService and runs scan(8000).
  3. Shows the scanned device list inline so the user can pick one.
  4. On pick: bleService.connect() + ELM327 init + startPolling().
  5. On success: advances to the 'done' step.
  6. On failure: shows the error inline + offers a 'Skip — connect later' button so users can complete onboarding without an adapter.

Also added battery capacity input validation (min=1, max=1000, rejects NaN/negative).

5. ConnectOverlay connecting state + i18n

  • setConnectionStatus('connecting') is now set at the start of BLE/WiFi connect handlers (was jumping straight to 'connected', so the spinner never showed and users could tap Connect repeatedly, spawning concurrent bleService.connect() calls).
  • WiFi port input: type="number" min=1 max=65535 + integer validation.
  • WiFi IP input: regex validation for dotted-quad IPv4 or DNS hostname.
  • Demo button: disabled={!demoBrand || !demoModel} (was no validation — user could start demo with no vehicle profile, breaking the simulator).
  • All 25+ hardcoded English strings migrated to t() with new keys in en/ar common.json (bluetoothObd, wifiObd, noAdaptersFound, scanFailed, connectFailed, wifiConnectFailed, invalidPort, invalidIp, permissionsRequired, scanAgain, ipAddress, port, brand, model, selectBrand, selectModel, startDemoMode, etc.).

6. WiFi cleartext whitelist expanded

Was 25 specific IPs → now covers all common private-use subnets:

  • Full 192.168.0.x and 192.168.1.x (most common)
  • 192.168.2-10.x (OBDLink WiFi, vLinker)
  • 192.168.43.x (Android WiFi hotspot — when phone is AP, adapter is client)
  • 192.168.49.x (iOS Personal Hotspot)
  • 10.0.x.x, 10.10.10.x (enterprise/home)
  • 172.16.x.x (private enterprise)

Still blocks ALL public/non-private traffic — the privacy-first guarantee is preserved.

7. Settings persistence + Quiet Hours mislabel fixed

  • New AlertThresholds and QuietHours types in AppSettings.
  • SettingsView binds alert threshold inputs directly to settings.alertThresholds (was local useState with hardcoded '20'/'80'/'50' that never persisted — values were lost on tab change/app restart).
  • Quiet Hours switch now bound to settings.quietHours.enabled (was bound to settings.notifications — toggling it muted ALL notifications globally instead of enabling quiet hours).

8. Dead code removed

  • Deleted src/lib/db.ts (147 lines, never imported anywhere; claimed to use @capacitor-community/sqlite but actually used localStorage).
  • Deleted prisma/schema.prisma (Next.js template leftover with default User/Post models; Prisma not in package.json).

Additional fixes

  • simulator.resolveBrand: was failing to match most brand names ('VW Group' → 'vw-group' → no match → generic). Now uses substring matching with special cases for hyundai-kia and vw-group (matches vw/volkswagen/audi/porsche/skoda/seat/id./e-tron). This restores brand-specific chemistry (LFP vs NCA vs NMC), thermal coefficients, cell counts, charge curves, and DTC prefixes for most vehicles.
  • store.parseOBDResponse Mode 03: was wiping pending (Mode 07) DTCs via setDTCs(dtcs). Now merges: [...storedDTCs, ...existingPending].

Verification

  • TypeScript: tsc --noEmit clean.
  • Next.js production build: static export succeeds (Turbopack, 6.5s).
  • Gradle assembleRelease: BUILD SUCCESSFUL.
  • aapt dump badging confirms versionName=1.5.1 versionCode=8 targetSdk=36.
  • Node OBD-II protocol simulation test: 34/34 pass (was 26 — added 8 Mode 22 / Mode 62 tests).
  • APK size: 25.8 MB (was 25.3 MB — the extra 500 KB is the Amiri Arabic fonts).

⚠️ Note on hardware simulation

The task brief asked for a simulation verifying the APK detects OBD-II devices and reads diagnostic data accurately. Without physical ELM327 hardware or an Android emulator with ELM327 simulation, true end-to-end runtime testing against a live vehicle is not possible in a headless CI environment. The included Node-based protocol simulation test (scripts/obd-protocol-sim.test.mjs) exercises the exact byte-level parsing logic that the production app uses — including the new Mode 22 formula evaluator and Mode 62 response parser. For on-device verification, sideload the APK in this release and test against a real OBD-II adapter.

Install

  1. Download evdx-v1.5.1-pre-release.apk below.
  2. Transfer to your Android device (Android 7.0+ / API 24+).
  3. Enable 'Install from unknown sources' in Settings.
  4. Open the APK to install.
  5. Pair your OBD-II adapter via the in-app Connect screen.

For full setup, connection, and diagnostic workflow instructions, see the bilingual PDF user guides below.

Security reminder

The GitHub token used to push this release was shared in plain text in the original request. Revoke it immediately at https://github.com/settings/tokens and generate a new one through a secure channel.