diff --git a/app/diario-bordo.html b/app/diario-bordo.html
index abe1dd6..384b412 100644
--- a/app/diario-bordo.html
+++ b/app/diario-bordo.html
@@ -6079,7 +6079,13 @@ async function bmsProbeWebBluetooth(deviceId,deviceName){
if(!notifyChar&&(p.notify||p.indicate))notifyChar=c;
if(!writeChar&&(p.write||p.writeWithoutResponse))writeChar=c;
}
- if(!notifyChar||!writeChar){setBleDiag('Sem chars notify+write','err');return false}
+ if(!notifyChar){setBleDiag('Sem char notify','err');return false}
+ if(!writeChar){
+ // Workaround: BMS chinês declara ff02 só [read] mas aceita writes (firmware permissivo).
+ // Força usar primeira char não-notify e tenta writeValue mesmo assim.
+ writeChar=chars.find(c=>c.uuid.toLowerCase()!==notifyChar.uuid.toLowerCase())||chars[0];
+ setBleDiag(`⚠ Sem property write · forçando ${writeChar.uuid.slice(4,8)}`,'warn');
+ }
setBleDiag(`Notify=${notifyChar.uuid.slice(4,8)} Write=${writeChar.uuid.slice(4,8)}`,'ok');
notifyChar.addEventListener('characteristicvaluechanged',(ev)=>{
const dv=ev.target.value;
@@ -6440,7 +6446,7 @@ async function removeBluetoothDevice(id){
renderBluetoothCard();
}
-const APP_VERSION='1.10.13';
+const APP_VERSION='1.10.14';
function renderBluetoothCard(){
const el=document.getElementById('bt-list');
const supportEl=document.getElementById('bt-support');
diff --git a/server/public/index.html b/server/public/index.html
index abe1dd6..384b412 100644
--- a/server/public/index.html
+++ b/server/public/index.html
@@ -6079,7 +6079,13 @@ async function bmsProbeWebBluetooth(deviceId,deviceName){
if(!notifyChar&&(p.notify||p.indicate))notifyChar=c;
if(!writeChar&&(p.write||p.writeWithoutResponse))writeChar=c;
}
- if(!notifyChar||!writeChar){setBleDiag('Sem chars notify+write','err');return false}
+ if(!notifyChar){setBleDiag('Sem char notify','err');return false}
+ if(!writeChar){
+ // Workaround: BMS chinês declara ff02 só [read] mas aceita writes (firmware permissivo).
+ // Força usar primeira char não-notify e tenta writeValue mesmo assim.
+ writeChar=chars.find(c=>c.uuid.toLowerCase()!==notifyChar.uuid.toLowerCase())||chars[0];
+ setBleDiag(`⚠ Sem property write · forçando ${writeChar.uuid.slice(4,8)}`,'warn');
+ }
setBleDiag(`Notify=${notifyChar.uuid.slice(4,8)} Write=${writeChar.uuid.slice(4,8)}`,'ok');
notifyChar.addEventListener('characteristicvaluechanged',(ev)=>{
const dv=ev.target.value;
@@ -6440,7 +6446,7 @@ async function removeBluetoothDevice(id){
renderBluetoothCard();
}
-const APP_VERSION='1.10.13';
+const APP_VERSION='1.10.14';
function renderBluetoothCard(){
const el=document.getElementById('bt-list');
const supportEl=document.getElementById('bt-support');
diff --git a/server/public/sw.js b/server/public/sw.js
index da916c4..cbab32c 100644
--- a/server/public/sw.js
+++ b/server/public/sw.js
@@ -1,7 +1,7 @@
// Shivao Service Worker — offline real
// Estratégia: shell precachado, tiles cache-first, windy network-first, /api passa direto.
// Versão usada nos cache names — bumpa essa string pra invalidar caches antigos em deploys.
-const VERSION = 'shivao-v1.10.13';
+const VERSION = 'shivao-v1.10.14';
const SHELL_CACHE = `shivao-shell-${VERSION}`;
const TILES_CACHE = 'shivao-tiles-v1'; // separado pra não invalidar tiles em update do shell
const WINDY_CACHE = `shivao-windy-${VERSION}`;