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)