From 6680f8b09ba75ec07cfb342bfdf488538adee4c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?PontualTech=20/=20Karl=C3=A3o?= Date: Wed, 29 Apr 2026 07:45:28 -0300 Subject: [PATCH] =?UTF-8?q?fix(ble):=20writeWithoutResponse=20for=C3=A7ado?= =?UTF-8?q?=20quando=20ff02=20s=C3=B3=20tem=20wnr=20v1.10.7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DESCOBERTA crítica do log do Karlão: ff02 (write char) tem properties [wnr,read] — SÓ writeWithoutResponse, sem write. Meu probe primeiro tentava ble.write() (com response) que trava silenciosamente esperando ACK que o BMS nunca envia. Por isso log para em '→ TX JBD-0x03' sem testar próximos protocolos. Fix: - Probe detecta properties da writeChar e seta dev.bmsForceWnr=true quando char tem wnr mas não write - Log mostra '(force-wnr)' ao lado do Write= no diagnóstico - bmsWriteCmd respeita bmsForceWnr e usa writeWithoutResponse mesmo quando o protocol não pediu Esperado v1.10.7: TX JBD-0x03 com writeWithoutResponse → BMS responde com pacote 0xDD ... 0x77 → dashboard mostra V/A/SoC/células reais. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/diario-bordo.html | 20 +++++++++++++++++--- mobile/android/app/build.gradle | 4 ++-- mobile/package.json | 2 +- server/public/index.html | 20 +++++++++++++++++--- server/src/index.js | 2 +- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/app/diario-bordo.html b/app/diario-bordo.html index d65063b..43731dc 100644 --- a/app/diario-bordo.html +++ b/app/diario-bordo.html @@ -6057,7 +6057,18 @@ async function bmsProbeAndAttach(deviceId,deviceName){ setBleDiag('Não achei chars notify+write em services vendor','err'); return false; } - setBleDiag(`Notify=${notifyChar.slice(4,8)} Write=${writeChar.slice(4,8)} Svc=${foundService.slice(4,8)}`,'ok'); + // Detecta se write char só aceita writeWithoutResponse + let writeOnlyWnr=false; + for(const svc of allSvcs){ + if((svc.uuid||'').toLowerCase()!==foundService)continue; + for(const c of (svc.characteristics||[])){ + if((c.uuid||'').toLowerCase()===writeChar){ + const p=c.properties||{}; + writeOnlyWnr=(!p.write&&p.writeWithoutResponse); + } + } + } + setBleDiag(`Notify=${notifyChar.slice(4,8)} Write=${writeChar.slice(4,8)}${writeOnlyWnr?' (force-wnr)':''} Svc=${foundService.slice(4,8)}`,'ok'); // Subscribe + handler const listenerKey='notification|'+deviceId+'|'+foundService+'|'+notifyChar; ble.addListener(listenerKey,(ev)=>{ @@ -6079,6 +6090,7 @@ async function bmsProbeAndAttach(deviceId,deviceName){ dev.bmsService=foundService; dev.bmsNotifyChar=notifyChar; dev.bmsWriteChar=writeChar; + dev.bmsForceWnr=writeOnlyWnr; dev.isJBD=true; saveState(); } @@ -6094,7 +6106,9 @@ async function bmsWriteCmd(deviceId,bytes,withoutResponse){ const ble=window.Capacitor?.Plugins?.BluetoothLe; const dev=state.btDevices?.find(d=>d.id===deviceId); if(!ble||!dev?.bmsService||!dev?.bmsWriteChar)throw new Error('config BMS ausente'); - const fn=withoutResponse?'writeWithoutResponse':'write'; + // Se a write char só aceita wnr, força writeWithoutResponse independente do parâmetro + const useWnr=withoutResponse||dev.bmsForceWnr; + const fn=useWnr?'writeWithoutResponse':'write'; await ble[fn]({deviceId,service:dev.bmsService,characteristic:dev.bmsWriteChar,value:bytesToBase64(bytes)}); } @@ -6289,7 +6303,7 @@ async function removeBluetoothDevice(id){ renderBluetoothCard(); } -const APP_VERSION='1.10.6'; +const APP_VERSION='1.10.7'; function renderBluetoothCard(){ const el=document.getElementById('bt-list'); const supportEl=document.getElementById('bt-support'); diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle index 4df6605..f69740d 100644 --- a/mobile/android/app/build.gradle +++ b/mobile/android/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "br.com.pontualtech.shivao" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 22 - versionName "1.10.6" + versionCode 23 + versionName "1.10.7" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/mobile/package.json b/mobile/package.json index 82c8e32..1b3a604 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -1,6 +1,6 @@ { "name": "shivao-mobile", - "version": "1.10.6", + "version": "1.10.7", "description": "Shivao app nativo (Capacitor wrapper Android/iOS)", "main": "index.js", "type": "module", diff --git a/server/public/index.html b/server/public/index.html index d65063b..43731dc 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -6057,7 +6057,18 @@ async function bmsProbeAndAttach(deviceId,deviceName){ setBleDiag('Não achei chars notify+write em services vendor','err'); return false; } - setBleDiag(`Notify=${notifyChar.slice(4,8)} Write=${writeChar.slice(4,8)} Svc=${foundService.slice(4,8)}`,'ok'); + // Detecta se write char só aceita writeWithoutResponse + let writeOnlyWnr=false; + for(const svc of allSvcs){ + if((svc.uuid||'').toLowerCase()!==foundService)continue; + for(const c of (svc.characteristics||[])){ + if((c.uuid||'').toLowerCase()===writeChar){ + const p=c.properties||{}; + writeOnlyWnr=(!p.write&&p.writeWithoutResponse); + } + } + } + setBleDiag(`Notify=${notifyChar.slice(4,8)} Write=${writeChar.slice(4,8)}${writeOnlyWnr?' (force-wnr)':''} Svc=${foundService.slice(4,8)}`,'ok'); // Subscribe + handler const listenerKey='notification|'+deviceId+'|'+foundService+'|'+notifyChar; ble.addListener(listenerKey,(ev)=>{ @@ -6079,6 +6090,7 @@ async function bmsProbeAndAttach(deviceId,deviceName){ dev.bmsService=foundService; dev.bmsNotifyChar=notifyChar; dev.bmsWriteChar=writeChar; + dev.bmsForceWnr=writeOnlyWnr; dev.isJBD=true; saveState(); } @@ -6094,7 +6106,9 @@ async function bmsWriteCmd(deviceId,bytes,withoutResponse){ const ble=window.Capacitor?.Plugins?.BluetoothLe; const dev=state.btDevices?.find(d=>d.id===deviceId); if(!ble||!dev?.bmsService||!dev?.bmsWriteChar)throw new Error('config BMS ausente'); - const fn=withoutResponse?'writeWithoutResponse':'write'; + // Se a write char só aceita wnr, força writeWithoutResponse independente do parâmetro + const useWnr=withoutResponse||dev.bmsForceWnr; + const fn=useWnr?'writeWithoutResponse':'write'; await ble[fn]({deviceId,service:dev.bmsService,characteristic:dev.bmsWriteChar,value:bytesToBase64(bytes)}); } @@ -6289,7 +6303,7 @@ async function removeBluetoothDevice(id){ renderBluetoothCard(); } -const APP_VERSION='1.10.6'; +const APP_VERSION='1.10.7'; function renderBluetoothCard(){ const el=document.getElementById('bt-list'); const supportEl=document.getElementById('bt-support'); diff --git a/server/src/index.js b/server/src/index.js index 7e2d730..ebd6fe0 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -347,7 +347,7 @@ app.get('/.well-known/assetlinks.json', (req, res) => { }); // Atalho: /apk redireciona pra última APK release no Forgejo -const LATEST_APK_URL = 'https://git.pontualtech.work/karlao/shivao-projeto/releases/download/v1.10.6/Shivao-v1.10.6.apk'; +const LATEST_APK_URL = 'https://git.pontualtech.work/karlao/shivao-projeto/releases/download/v1.10.7/Shivao-v1.10.7.apk'; app.get('/apk', (req, res) => res.redirect(302, LATEST_APK_URL)); // Página A4 imprimível com QR Code + instruções (cola no barco/marina)