EVDx v1.5.1-pre — Critical BLE/OBD-II fixes
Pre-releaseEVDx 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 scan5498d6b— v1.5.1+: CI workflow, README update, 9 audit-driven bug fixes6ccad7d— docs: bilingual user guide HTML sources3534fd8— 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:
BLEService.setCustomPIDs(defs)is called when the user picks a vehicle (viastore.setActiveVehicle).- During
connect(),detectCustomPIDs()probes each PID with a 1.5s timeout and stores the supported subset inadapterInfo.supportedCustomPIDs. startPolling()interleaves Mode 22 queries with the standard Mode 01 round-robin.parseOBDResponsehandles Mode 62 responses: looks up the PID def, evaluates the formula viaBLEService.evaluateFormula(strict-whitelist, noeval), and maps the result to the rightvehicleDatafield by name.- 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:
- Bundling Amiri-Regular.ttf + Amiri-Bold.ttf in
public/fonts/(1.2 MB total). loadArabicFont()fetches them at PDF-gen time and registers viadoc.addFileToVFS+addFont.shapeArabic()runs text througharabic-reshaper(new dep) to convert logical-order Arabic into presentation forms, then reverses for RTL display.writeText()helper shapes text + flips alignment for Arabic.- All 40
doc.text/doc.setFontcalls migrated towriteText/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:
- Calls
requestBlePermissions()(Android 12+: BLUETOOTH_CONNECT + BLUETOOTH_SCAN). - Initializes
bleServiceand runsscan(8000). - Shows the scanned device list inline so the user can pick one.
- On pick:
bleService.connect()+ ELM327 init +startPolling(). - On success: advances to the 'done' step.
- 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 concurrentbleService.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 inen/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
AlertThresholdsandQuietHourstypes inAppSettings. SettingsViewbinds alert threshold inputs directly tosettings.alertThresholds(was localuseStatewith 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 tosettings.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/sqlitebut 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.parseOBDResponseMode 03: was wiping pending (Mode 07) DTCs viasetDTCs(dtcs). Now merges:[...storedDTCs, ...existingPending].
Verification
- TypeScript:
tsc --noEmitclean. - Next.js production build: static export succeeds (Turbopack, 6.5s).
- Gradle
assembleRelease: BUILD SUCCESSFUL. aapt dump badgingconfirmsversionName=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
- Download
evdx-v1.5.1-pre-release.apkbelow. - Transfer to your Android device (Android 7.0+ / API 24+).
- Enable 'Install from unknown sources' in Settings.
- Open the APK to install.
- 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.