feat(bms): probe automático de protocolo BMS (JBD/JK/Daly) v1.10.2
Some checks are pending
Build Android (APK + AAB) / build-android (push) Waiting to run
Some checks are pending
Build Android (APK + AAB) / build-android (push) Waiting to run
Bug v1.10.1: BMS bat2 do Karlão expõe service ff00 mas zero RX no log após enviar comando JBD 0x03. Significa BMS usa firmware proprietário não-JBD. Implementação probe automático: - Enumera characteristics de cada vendor service (ff00, fff0, ffe0, 0203) - Lista UUIDs + propriedades (notify/indicate/write/wnr/read) no diagnóstico - Auto-detecta notify char (com property notify ou indicate) - Auto-detecta write char (com property write ou writeWithoutResponse) - Salva config em dev.bmsService/Notify/WriteChar pra reuso - Subscribe na notify char + listener com hex dump dos chunks RX - Tenta 4 protocolos sequencialmente (espera 2.5s entre cada): 1. JBD-0x03 (DD A5 03 00 FF FD 77) 2. JK-getInfo (AA 55 90 EB 96 00 ... — 20 bytes) 3. Daly-getInfo (A5 80 90 08 00 00 ... — 13 bytes) 4. JBD writeWithoutResponse fallback - Detecta resposta por byte de início (0xDD=JBD, 0xAA=JK, 0xA5=Daly) - Salva bmsProtocol no device pra usar no poll periódico - Stubs JK/Daly handlers (parsers específicos virão se BMS responder) Botão Re-ler agora re-roda probe completo (não só re-envia mesmo comando). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
578793d097
commit
ca3dd4d7b2
5 changed files with 230 additions and 76 deletions
|
|
@ -5992,65 +5992,142 @@ function bytesToBase64(arr){
|
|||
return btoa(bin);
|
||||
}
|
||||
|
||||
async function bmsAttachJBD(deviceId,deviceName){
|
||||
// Probe: lista characteristics + identifica notify/write chars + tenta protocolos
|
||||
async function bmsProbeAndAttach(deviceId,deviceName){
|
||||
const backend=bleBackend();
|
||||
if(backend!=='capacitor'){setBleDiag('JBD parser requer Capacitor (APK)','warn');return false}
|
||||
if(backend!=='capacitor'){setBleDiag('Probe requer Capacitor (APK)','warn');return false}
|
||||
const ble=window.Capacitor.Plugins.BluetoothLe;
|
||||
try{
|
||||
setBleDiag('Detectando JBD BMS protocol...','info');
|
||||
// Subscribe nas notificações com listener registrado ANTES de start
|
||||
const listenerKey='notification|'+deviceId+'|'+BMS_JBD_SERVICE+'|'+BMS_JBD_NOTIFY;
|
||||
setBleDiag('🔍 Enumerando characteristics...','info');
|
||||
// Tenta serviços vendor: ff00, fff0 (Daly), ffe0 (JK), 0203
|
||||
const VENDOR_SVCS=[
|
||||
'0000ff00-0000-1000-8000-00805f9b34fb',
|
||||
'0000fff0-0000-1000-8000-00805f9b34fb',
|
||||
'0000ffe0-0000-1000-8000-00805f9b34fb',
|
||||
'00000203-0000-1000-8000-00805f9b34fb',
|
||||
];
|
||||
let notifyChar=null,writeChar=null,foundService=null;
|
||||
for(const svcId of VENDOR_SVCS){
|
||||
try{
|
||||
const r=await ble.getServices({deviceId});
|
||||
const svcs=r.services||r||[];
|
||||
const svc=svcs.find(s=>(s.uuid||'').toLowerCase()===svcId);
|
||||
if(!svc)continue;
|
||||
const chars=svc.characteristics||[];
|
||||
if(chars.length===0)continue;
|
||||
setBleDiag(`Service ${svcId.slice(4,8)} · ${chars.length} chars`,'info');
|
||||
for(const c of chars){
|
||||
const props=c.properties||{};
|
||||
const propsStr=[props.notify&&'notify',props.indicate&&'indicate',props.write&&'write',props.writeWithoutResponse&&'wnr',props.read&&'read'].filter(Boolean).join(',');
|
||||
setBleDiag(` ${(c.uuid||'').slice(4,8)} [${propsStr}]`,'info');
|
||||
if(!notifyChar&&(props.notify||props.indicate)){notifyChar=c.uuid;foundService=svc.uuid}
|
||||
if(!writeChar&&(props.write||props.writeWithoutResponse))writeChar=c.uuid;
|
||||
}
|
||||
if(notifyChar&&writeChar)break;
|
||||
}catch(e){}
|
||||
}
|
||||
if(!notifyChar||!writeChar){
|
||||
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');
|
||||
// Subscribe + handler
|
||||
const listenerKey='notification|'+deviceId+'|'+foundService+'|'+notifyChar;
|
||||
ble.addListener(listenerKey,(ev)=>{
|
||||
const dv=parseDataView(ev.value);
|
||||
const hex=Array.from(new Uint8Array(dv.buffer)).map(b=>b.toString(16).padStart(2,'0')).join(' ');
|
||||
setBleDiag('← RX '+dv.byteLength+' bytes: '+hex.slice(0,80)+(hex.length>80?'...':''),'info');
|
||||
bmsHandleChunk(deviceId,dv,deviceName);
|
||||
setBleDiag('← RX '+dv.byteLength+'b: '+hex.slice(0,100)+(hex.length>100?'...':''),'ok');
|
||||
// Detecta protocolo por byte de início
|
||||
const first=new Uint8Array(dv.buffer)[0];
|
||||
if(first===0xDD)bmsHandleChunk(deviceId,dv,deviceName); // JBD
|
||||
else if(first===0xAA)bmsHandleJK(deviceId,dv,deviceName); // JK BMS
|
||||
else if(first===0xA5)bmsHandleDaly(deviceId,dv,deviceName); // Daly
|
||||
});
|
||||
await ble.startNotifications({deviceId,service:BMS_JBD_SERVICE,characteristic:BMS_JBD_NOTIFY});
|
||||
setBleDiag('Notify ff01 ativo · aguardando 500ms...','ok');
|
||||
await new Promise(r=>setTimeout(r,500)); // alguns BMS precisam wake-up
|
||||
setBleDiag('→ TX comando 0x03 (basic info)','info');
|
||||
await bmsQueryBasic(deviceId);
|
||||
await ble.startNotifications({deviceId,service:foundService,characteristic:notifyChar});
|
||||
setBleDiag('Notify ativo · aguardando 800ms...','ok');
|
||||
await new Promise(r=>setTimeout(r,800));
|
||||
// Salva config no device pra reuso
|
||||
const dev=state.btDevices?.find(d=>d.id===deviceId);
|
||||
if(dev){
|
||||
dev.bmsService=foundService;
|
||||
dev.bmsNotifyChar=notifyChar;
|
||||
dev.bmsWriteChar=writeChar;
|
||||
dev.isJBD=true;
|
||||
if(dev._pollInterval)clearInterval(dev._pollInterval);
|
||||
saveState();
|
||||
}
|
||||
// Se em 5s não chegou resposta, tenta com writeWithoutResponse
|
||||
setTimeout(async()=>{
|
||||
const dev2=state.btDevices?.find(d=>d.id===deviceId);
|
||||
if(dev2&&!dev2.bms?.voltage){
|
||||
setBleDiag('Sem resposta · tentando writeWithoutResponse...','warn');
|
||||
await bmsQueryBasic(deviceId,true).catch(e=>setBleDiag('writeWoR falhou: '+e.message,'err'));
|
||||
}
|
||||
},5000);
|
||||
setInterval(()=>bmsQueryBasic(deviceId).catch(()=>{}),30000);
|
||||
return true;
|
||||
// Tenta cada protocolo até alguém responder
|
||||
return await bmsTryProtocols(deviceId);
|
||||
}catch(e){
|
||||
setBleDiag('JBD attach falhou: '+(e.message||e.errorMessage),'err');
|
||||
setBleDiag('Probe falhou: '+(e.message||e.errorMessage),'err');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function bmsQueryBasic(deviceId,withoutResponse){
|
||||
async function bmsWriteCmd(deviceId,bytes,withoutResponse){
|
||||
const ble=window.Capacitor?.Plugins?.BluetoothLe;
|
||||
if(!ble)return;
|
||||
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';
|
||||
await ble[fn]({deviceId,service:BMS_JBD_SERVICE,characteristic:BMS_JBD_WRITE,value:bytesToBase64(BMS_CMD_BASIC)});
|
||||
await ble[fn]({deviceId,service:dev.bmsService,characteristic:dev.bmsWriteChar,value:bytesToBase64(bytes)});
|
||||
}
|
||||
|
||||
async function bmsTryProtocols(deviceId){
|
||||
const PROTOCOLS=[
|
||||
{name:'JBD-0x03',bytes:[0xDD,0xA5,0x03,0x00,0xFF,0xFD,0x77]},
|
||||
{name:'JK-getInfo',bytes:[0xAA,0x55,0x90,0xEB,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10]},
|
||||
{name:'Daly-getInfo',bytes:[0xA5,0x80,0x90,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xBD]},
|
||||
{name:'JBD-write-no-response',bytes:[0xDD,0xA5,0x03,0x00,0xFF,0xFD,0x77],wnr:true},
|
||||
];
|
||||
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
|
||||
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;
|
||||
}
|
||||
}catch(e){setBleDiag(`${p.name} falhou: ${e.message||e.errorMessage}`,'warn')}
|
||||
}
|
||||
setBleDiag('⚠ Nenhum protocolo funcionou. BMS pode usar firmware proprietário não documentado.','err');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stubs pra protocolos JK e Daly (parsers básicos)
|
||||
function bmsHandleJK(deviceId,dv,deviceName){
|
||||
const dev=state.btDevices?.find(d=>d.id===deviceId);if(!dev)return;
|
||||
dev._lastRxAt=Date.now();
|
||||
// JK protocol: header AA 55 90 EB
|
||||
// Frame info varia muito por modelo — por hora só confirma RX
|
||||
setBleDiag('JK BMS frame recebido (parser específico em desenvolvimento)','info');
|
||||
}
|
||||
function bmsHandleDaly(deviceId,dv,deviceName){
|
||||
const dev=state.btDevices?.find(d=>d.id===deviceId);if(!dev)return;
|
||||
dev._lastRxAt=Date.now();
|
||||
setBleDiag('Daly BMS frame recebido (parser específico em desenvolvimento)','info');
|
||||
}
|
||||
|
||||
// Compat alias - chamadas antigas viram probe
|
||||
async function bmsAttachJBD(deviceId,deviceName){
|
||||
return bmsProbeAndAttach(deviceId,deviceName);
|
||||
}
|
||||
|
||||
async function bmsQueryBasic(deviceId,withoutResponse){
|
||||
// Usa config descoberta no probe
|
||||
await bmsWriteCmd(deviceId,BMS_CMD_BASIC,withoutResponse);
|
||||
}
|
||||
|
||||
// Re-leitura manual a partir do botão UI
|
||||
async function bmsManualRead(deviceId){
|
||||
setBleDiag('🔄 Re-leitura manual...','info');
|
||||
setBleDiag('🔄 Re-leitura manual · re-rodando probe completo...','info');
|
||||
try{
|
||||
await bmsQueryBasic(deviceId);
|
||||
setTimeout(async()=>{
|
||||
const dev=state.btDevices?.find(d=>d.id===deviceId);
|
||||
if(dev&&!dev.bms?.voltage){
|
||||
setBleDiag('Tentando writeWithoutResponse...','warn');
|
||||
await bmsQueryBasic(deviceId,true);
|
||||
}
|
||||
},3000);
|
||||
// Re-roda probe completo (lista chars de novo + tenta protocolos)
|
||||
await bmsProbeAndAttach(deviceId,state.btDevices?.find(d=>d.id===deviceId)?.name||'BMS');
|
||||
}catch(e){setBleDiag('Manual read falhou: '+(e.message||e.errorMessage),'err')}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ android {
|
|||
applicationId "br.com.pontualtech.shivao"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 17
|
||||
versionName "1.10.1"
|
||||
versionCode 18
|
||||
versionName "1.10.2"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
aaptOptions {
|
||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "shivao-mobile",
|
||||
"version": "1.10.1",
|
||||
"version": "1.10.2",
|
||||
"description": "Shivao app nativo (Capacitor wrapper Android/iOS)",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
|
|
|
|||
|
|
@ -5992,65 +5992,142 @@ function bytesToBase64(arr){
|
|||
return btoa(bin);
|
||||
}
|
||||
|
||||
async function bmsAttachJBD(deviceId,deviceName){
|
||||
// Probe: lista characteristics + identifica notify/write chars + tenta protocolos
|
||||
async function bmsProbeAndAttach(deviceId,deviceName){
|
||||
const backend=bleBackend();
|
||||
if(backend!=='capacitor'){setBleDiag('JBD parser requer Capacitor (APK)','warn');return false}
|
||||
if(backend!=='capacitor'){setBleDiag('Probe requer Capacitor (APK)','warn');return false}
|
||||
const ble=window.Capacitor.Plugins.BluetoothLe;
|
||||
try{
|
||||
setBleDiag('Detectando JBD BMS protocol...','info');
|
||||
// Subscribe nas notificações com listener registrado ANTES de start
|
||||
const listenerKey='notification|'+deviceId+'|'+BMS_JBD_SERVICE+'|'+BMS_JBD_NOTIFY;
|
||||
setBleDiag('🔍 Enumerando characteristics...','info');
|
||||
// Tenta serviços vendor: ff00, fff0 (Daly), ffe0 (JK), 0203
|
||||
const VENDOR_SVCS=[
|
||||
'0000ff00-0000-1000-8000-00805f9b34fb',
|
||||
'0000fff0-0000-1000-8000-00805f9b34fb',
|
||||
'0000ffe0-0000-1000-8000-00805f9b34fb',
|
||||
'00000203-0000-1000-8000-00805f9b34fb',
|
||||
];
|
||||
let notifyChar=null,writeChar=null,foundService=null;
|
||||
for(const svcId of VENDOR_SVCS){
|
||||
try{
|
||||
const r=await ble.getServices({deviceId});
|
||||
const svcs=r.services||r||[];
|
||||
const svc=svcs.find(s=>(s.uuid||'').toLowerCase()===svcId);
|
||||
if(!svc)continue;
|
||||
const chars=svc.characteristics||[];
|
||||
if(chars.length===0)continue;
|
||||
setBleDiag(`Service ${svcId.slice(4,8)} · ${chars.length} chars`,'info');
|
||||
for(const c of chars){
|
||||
const props=c.properties||{};
|
||||
const propsStr=[props.notify&&'notify',props.indicate&&'indicate',props.write&&'write',props.writeWithoutResponse&&'wnr',props.read&&'read'].filter(Boolean).join(',');
|
||||
setBleDiag(` ${(c.uuid||'').slice(4,8)} [${propsStr}]`,'info');
|
||||
if(!notifyChar&&(props.notify||props.indicate)){notifyChar=c.uuid;foundService=svc.uuid}
|
||||
if(!writeChar&&(props.write||props.writeWithoutResponse))writeChar=c.uuid;
|
||||
}
|
||||
if(notifyChar&&writeChar)break;
|
||||
}catch(e){}
|
||||
}
|
||||
if(!notifyChar||!writeChar){
|
||||
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');
|
||||
// Subscribe + handler
|
||||
const listenerKey='notification|'+deviceId+'|'+foundService+'|'+notifyChar;
|
||||
ble.addListener(listenerKey,(ev)=>{
|
||||
const dv=parseDataView(ev.value);
|
||||
const hex=Array.from(new Uint8Array(dv.buffer)).map(b=>b.toString(16).padStart(2,'0')).join(' ');
|
||||
setBleDiag('← RX '+dv.byteLength+' bytes: '+hex.slice(0,80)+(hex.length>80?'...':''),'info');
|
||||
bmsHandleChunk(deviceId,dv,deviceName);
|
||||
setBleDiag('← RX '+dv.byteLength+'b: '+hex.slice(0,100)+(hex.length>100?'...':''),'ok');
|
||||
// Detecta protocolo por byte de início
|
||||
const first=new Uint8Array(dv.buffer)[0];
|
||||
if(first===0xDD)bmsHandleChunk(deviceId,dv,deviceName); // JBD
|
||||
else if(first===0xAA)bmsHandleJK(deviceId,dv,deviceName); // JK BMS
|
||||
else if(first===0xA5)bmsHandleDaly(deviceId,dv,deviceName); // Daly
|
||||
});
|
||||
await ble.startNotifications({deviceId,service:BMS_JBD_SERVICE,characteristic:BMS_JBD_NOTIFY});
|
||||
setBleDiag('Notify ff01 ativo · aguardando 500ms...','ok');
|
||||
await new Promise(r=>setTimeout(r,500)); // alguns BMS precisam wake-up
|
||||
setBleDiag('→ TX comando 0x03 (basic info)','info');
|
||||
await bmsQueryBasic(deviceId);
|
||||
await ble.startNotifications({deviceId,service:foundService,characteristic:notifyChar});
|
||||
setBleDiag('Notify ativo · aguardando 800ms...','ok');
|
||||
await new Promise(r=>setTimeout(r,800));
|
||||
// Salva config no device pra reuso
|
||||
const dev=state.btDevices?.find(d=>d.id===deviceId);
|
||||
if(dev){
|
||||
dev.bmsService=foundService;
|
||||
dev.bmsNotifyChar=notifyChar;
|
||||
dev.bmsWriteChar=writeChar;
|
||||
dev.isJBD=true;
|
||||
if(dev._pollInterval)clearInterval(dev._pollInterval);
|
||||
saveState();
|
||||
}
|
||||
// Se em 5s não chegou resposta, tenta com writeWithoutResponse
|
||||
setTimeout(async()=>{
|
||||
const dev2=state.btDevices?.find(d=>d.id===deviceId);
|
||||
if(dev2&&!dev2.bms?.voltage){
|
||||
setBleDiag('Sem resposta · tentando writeWithoutResponse...','warn');
|
||||
await bmsQueryBasic(deviceId,true).catch(e=>setBleDiag('writeWoR falhou: '+e.message,'err'));
|
||||
}
|
||||
},5000);
|
||||
setInterval(()=>bmsQueryBasic(deviceId).catch(()=>{}),30000);
|
||||
return true;
|
||||
// Tenta cada protocolo até alguém responder
|
||||
return await bmsTryProtocols(deviceId);
|
||||
}catch(e){
|
||||
setBleDiag('JBD attach falhou: '+(e.message||e.errorMessage),'err');
|
||||
setBleDiag('Probe falhou: '+(e.message||e.errorMessage),'err');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function bmsQueryBasic(deviceId,withoutResponse){
|
||||
async function bmsWriteCmd(deviceId,bytes,withoutResponse){
|
||||
const ble=window.Capacitor?.Plugins?.BluetoothLe;
|
||||
if(!ble)return;
|
||||
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';
|
||||
await ble[fn]({deviceId,service:BMS_JBD_SERVICE,characteristic:BMS_JBD_WRITE,value:bytesToBase64(BMS_CMD_BASIC)});
|
||||
await ble[fn]({deviceId,service:dev.bmsService,characteristic:dev.bmsWriteChar,value:bytesToBase64(bytes)});
|
||||
}
|
||||
|
||||
async function bmsTryProtocols(deviceId){
|
||||
const PROTOCOLS=[
|
||||
{name:'JBD-0x03',bytes:[0xDD,0xA5,0x03,0x00,0xFF,0xFD,0x77]},
|
||||
{name:'JK-getInfo',bytes:[0xAA,0x55,0x90,0xEB,0x96,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10]},
|
||||
{name:'Daly-getInfo',bytes:[0xA5,0x80,0x90,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xBD]},
|
||||
{name:'JBD-write-no-response',bytes:[0xDD,0xA5,0x03,0x00,0xFF,0xFD,0x77],wnr:true},
|
||||
];
|
||||
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
|
||||
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;
|
||||
}
|
||||
}catch(e){setBleDiag(`${p.name} falhou: ${e.message||e.errorMessage}`,'warn')}
|
||||
}
|
||||
setBleDiag('⚠ Nenhum protocolo funcionou. BMS pode usar firmware proprietário não documentado.','err');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stubs pra protocolos JK e Daly (parsers básicos)
|
||||
function bmsHandleJK(deviceId,dv,deviceName){
|
||||
const dev=state.btDevices?.find(d=>d.id===deviceId);if(!dev)return;
|
||||
dev._lastRxAt=Date.now();
|
||||
// JK protocol: header AA 55 90 EB
|
||||
// Frame info varia muito por modelo — por hora só confirma RX
|
||||
setBleDiag('JK BMS frame recebido (parser específico em desenvolvimento)','info');
|
||||
}
|
||||
function bmsHandleDaly(deviceId,dv,deviceName){
|
||||
const dev=state.btDevices?.find(d=>d.id===deviceId);if(!dev)return;
|
||||
dev._lastRxAt=Date.now();
|
||||
setBleDiag('Daly BMS frame recebido (parser específico em desenvolvimento)','info');
|
||||
}
|
||||
|
||||
// Compat alias - chamadas antigas viram probe
|
||||
async function bmsAttachJBD(deviceId,deviceName){
|
||||
return bmsProbeAndAttach(deviceId,deviceName);
|
||||
}
|
||||
|
||||
async function bmsQueryBasic(deviceId,withoutResponse){
|
||||
// Usa config descoberta no probe
|
||||
await bmsWriteCmd(deviceId,BMS_CMD_BASIC,withoutResponse);
|
||||
}
|
||||
|
||||
// Re-leitura manual a partir do botão UI
|
||||
async function bmsManualRead(deviceId){
|
||||
setBleDiag('🔄 Re-leitura manual...','info');
|
||||
setBleDiag('🔄 Re-leitura manual · re-rodando probe completo...','info');
|
||||
try{
|
||||
await bmsQueryBasic(deviceId);
|
||||
setTimeout(async()=>{
|
||||
const dev=state.btDevices?.find(d=>d.id===deviceId);
|
||||
if(dev&&!dev.bms?.voltage){
|
||||
setBleDiag('Tentando writeWithoutResponse...','warn');
|
||||
await bmsQueryBasic(deviceId,true);
|
||||
}
|
||||
},3000);
|
||||
// Re-roda probe completo (lista chars de novo + tenta protocolos)
|
||||
await bmsProbeAndAttach(deviceId,state.btDevices?.find(d=>d.id===deviceId)?.name||'BMS');
|
||||
}catch(e){setBleDiag('Manual read falhou: '+(e.message||e.errorMessage),'err')}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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.1/Shivao-v1.10.1.apk';
|
||||
const LATEST_APK_URL = 'https://git.pontualtech.work/karlao/shivao-projeto/releases/download/v1.10.2/Shivao-v1.10.2.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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue