diff --git a/app/diario-bordo.html b/app/diario-bordo.html index 6c75450..d9c64a8 100644 --- a/app/diario-bordo.html +++ b/app/diario-bordo.html @@ -6146,10 +6146,12 @@ 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'); - // 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)}); + // Timeout 3s: plugins/firmware podem travar mesmo com WNR + const writePromise=ble[fn]({deviceId,service:dev.bmsService,characteristic:dev.bmsWriteChar,value:bytesToBase64(bytes)}); + const timeoutPromise=new Promise((_,rej)=>setTimeout(()=>rej(new Error('write timeout 3s')),3000)); + await Promise.race([writePromise,timeoutPromise]); } async function bmsTryProtocols(deviceId){ @@ -6162,18 +6164,25 @@ async function bmsTryProtocols(deviceId){ for(const p of PROTOCOLS){ try{ setBleDiag(`→ TX ${p.name}: ${p.bytes.map(b=>b.toString(16).padStart(2,'0')).join(' ').slice(0,40)}`,'info'); - await bmsWriteCmd(deviceId,p.bytes,p.wnr); - // Espera 2s pra ver se gerou RX + try{ + await bmsWriteCmd(deviceId,p.bytes,p.wnr); + setBleDiag(`✔ write ${p.name} retornou`,'info'); + }catch(we){ + setBleDiag(`✗ write ${p.name} erro: ${we.message||we.errorMessage}`,'warn'); + // Continue pro próximo protocolo mesmo se write falhar + } + // Aguarda RX por 2.5s await new Promise(r=>setTimeout(r,2500)); const dev=state.btDevices?.find(d=>d.id===deviceId); if(dev?.bms?.voltage||dev?._lastRxAt){ setBleDiag(`✓ ${p.name} respondeu!`,'ok'); - // Configura poll periódico com este protocolo if(dev)dev.bmsProtocol=p.name; setInterval(async()=>{try{await bmsWriteCmd(deviceId,p.bytes,p.wnr)}catch{}},30000); return true; + }else{ + setBleDiag(`✗ ${p.name} sem RX em 2.5s`,'info'); } - }catch(e){setBleDiag(`${p.name} falhou: ${e.message||e.errorMessage}`,'warn')} + }catch(e){setBleDiag(`${p.name} loop erro: ${e.message||e.errorMessage}`,'warn')} } setBleDiag('⚠ Nenhum protocolo funcionou. BMS pode usar firmware proprietário não documentado.','err'); return false; @@ -6343,7 +6352,7 @@ async function removeBluetoothDevice(id){ renderBluetoothCard(); } -const APP_VERSION='1.10.9'; +const APP_VERSION='1.10.10'; 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 369f379..ccd0f6e 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 25 - versionName "1.10.9" + versionCode 26 + versionName "1.10.10" 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 8be079e..5ebc487 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -1,6 +1,6 @@ { "name": "shivao-mobile", - "version": "1.10.9", + "version": "1.10.10", "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 6c75450..d9c64a8 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -6146,10 +6146,12 @@ 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'); - // 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)}); + // Timeout 3s: plugins/firmware podem travar mesmo com WNR + const writePromise=ble[fn]({deviceId,service:dev.bmsService,characteristic:dev.bmsWriteChar,value:bytesToBase64(bytes)}); + const timeoutPromise=new Promise((_,rej)=>setTimeout(()=>rej(new Error('write timeout 3s')),3000)); + await Promise.race([writePromise,timeoutPromise]); } async function bmsTryProtocols(deviceId){ @@ -6162,18 +6164,25 @@ async function bmsTryProtocols(deviceId){ for(const p of PROTOCOLS){ try{ setBleDiag(`→ TX ${p.name}: ${p.bytes.map(b=>b.toString(16).padStart(2,'0')).join(' ').slice(0,40)}`,'info'); - await bmsWriteCmd(deviceId,p.bytes,p.wnr); - // Espera 2s pra ver se gerou RX + try{ + await bmsWriteCmd(deviceId,p.bytes,p.wnr); + setBleDiag(`✔ write ${p.name} retornou`,'info'); + }catch(we){ + setBleDiag(`✗ write ${p.name} erro: ${we.message||we.errorMessage}`,'warn'); + // Continue pro próximo protocolo mesmo se write falhar + } + // Aguarda RX por 2.5s await new Promise(r=>setTimeout(r,2500)); const dev=state.btDevices?.find(d=>d.id===deviceId); if(dev?.bms?.voltage||dev?._lastRxAt){ setBleDiag(`✓ ${p.name} respondeu!`,'ok'); - // Configura poll periódico com este protocolo if(dev)dev.bmsProtocol=p.name; setInterval(async()=>{try{await bmsWriteCmd(deviceId,p.bytes,p.wnr)}catch{}},30000); return true; + }else{ + setBleDiag(`✗ ${p.name} sem RX em 2.5s`,'info'); } - }catch(e){setBleDiag(`${p.name} falhou: ${e.message||e.errorMessage}`,'warn')} + }catch(e){setBleDiag(`${p.name} loop erro: ${e.message||e.errorMessage}`,'warn')} } setBleDiag('⚠ Nenhum protocolo funcionou. BMS pode usar firmware proprietário não documentado.','err'); return false; @@ -6343,7 +6352,7 @@ async function removeBluetoothDevice(id){ renderBluetoothCard(); } -const APP_VERSION='1.10.9'; +const APP_VERSION='1.10.10'; 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 ee8e56b..c12f6b0 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -413,7 +413,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.9/Shivao-v1.10.9.apk'; +const LATEST_APK_URL = 'https://git.pontualtech.work/karlao/shivao-projeto/releases/download/v1.10.10/Shivao-v1.10.10.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)