feat(ble): diagnóstico verboso pra debugar pareamento BLE v1.9.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
Karlão reportou: "localiza mas não pareia" (picker abre, seleciona
device, mas conexão falha silenciosa). Sem ver onde trava, impossível fix.
Adicionado:
- setBleDiag() exibe cada step com timestamp + cor (info/ok/warn/err)
- Painel <details> expansível "📋 Diagnóstico" no card BLE
- Logs em cada operação: backend, init, picker, connect, getServices,
battery read, notifications, device info
- Timeout do connect aumentado: 15s → 30s (BMS podem demorar)
- getServices() lista UUIDs descobertos no device — descobre se BMS
expõe Battery Service padrão ou só protocolo proprietário
- Mensagens explícitas de erro em cada catch (e.message ou errorMessage)
Próximo passo: Karlão testa, abre painel diagnóstico, me passa screenshot
ou copia o log. Daí descubro exatamente onde trava (timeout, sem service,
permissão negada, etc).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
52ee668879
commit
5dd3362469
5 changed files with 116 additions and 34 deletions
|
|
@ -1977,7 +1977,11 @@ Marque zonas de proibição (alarme alto) ou atenção (aviso suave). Detecção
|
||||||
<div id="bt-support" style="font-family:var(--f-mono);font-size:10.5px;letter-spacing:.04em;margin-bottom:10px">Verificando suporte...</div>
|
<div id="bt-support" style="font-family:var(--f-mono);font-size:10.5px;letter-spacing:.04em;margin-bottom:10px">Verificando suporte...</div>
|
||||||
<button class="btn btn-block btn-primary" onclick="pairBluetoothDevice()">+ Parear novo dispositivo Bluetooth</button>
|
<button class="btn btn-block btn-primary" onclick="pairBluetoothDevice()">+ Parear novo dispositivo Bluetooth</button>
|
||||||
<div id="bt-list" style="margin-top:14px"></div>
|
<div id="bt-list" style="margin-top:14px"></div>
|
||||||
<div class="field-hint" style="margin-top:8px">Limitações: <strong>iOS Safari não suporta Web Bluetooth</strong> (use no Chrome PC ou Android). Reconexão automática varia por device.</div>
|
<details style="margin-top:10px">
|
||||||
|
<summary style="cursor:pointer;font-family:var(--f-mono);font-size:11px;color:var(--m-text-soft,#7d97ad);letter-spacing:.06em">📋 Diagnóstico (logs do pareamento)</summary>
|
||||||
|
<div id="bt-diag" style="background:var(--m-bg-2,#0f2a40);border:1px solid var(--m-border,rgba(255,255,255,.08));border-radius:6px;padding:10px;margin-top:6px;max-height:200px;overflow-y:auto"></div>
|
||||||
|
</details>
|
||||||
|
<div class="field-hint" style="margin-top:8px">Limitações: <strong>iOS Safari não suporta Web Bluetooth</strong>. APK Android usa plugin nativo. BMS proprietários (Victron, JBD) podem aparecer mas não expor Battery Service padrão.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Raymarine Gateway -->
|
<!-- Raymarine Gateway -->
|
||||||
|
|
@ -5771,26 +5775,42 @@ async function ensureBleNativeReady(){
|
||||||
_bleNativeInitialized=true;
|
_bleNativeInitialized=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Diagnóstico visível: mostra cada passo no card BLE
|
||||||
|
function setBleDiag(msg,type){
|
||||||
|
const el=document.getElementById('bt-diag');
|
||||||
|
if(!el)return;
|
||||||
|
const colors={info:'var(--m-text-mid,#b3c5d6)',ok:'var(--m-ok,#10b981)',err:'var(--m-danger,#ef4444)',warn:'var(--m-warn,#f59e0b)'};
|
||||||
|
const time=new Date().toLocaleTimeString('pt-BR',{hour12:false});
|
||||||
|
const line=`<div style="color:${colors[type||'info']};font-family:var(--f-mono);font-size:11px;line-height:1.5">${time} · ${escapeHtml(msg)}</div>`;
|
||||||
|
el.innerHTML=line+el.innerHTML;
|
||||||
|
// Mantém só últimas 12 linhas
|
||||||
|
const lines=el.children;
|
||||||
|
while(lines.length>12)el.removeChild(lines[lines.length-1]);
|
||||||
|
console.log('[ble]',msg);
|
||||||
|
}
|
||||||
|
|
||||||
async function pairBluetoothDevice(){
|
async function pairBluetoothDevice(){
|
||||||
const backend=bleBackend();
|
const backend=bleBackend();
|
||||||
if(!backend){
|
setBleDiag('Backend: '+(backend||'NENHUM'),backend?'info':'err');
|
||||||
toast('Bluetooth indisponível neste dispositivo.');
|
if(!backend){toast('Bluetooth indisponível');return}
|
||||||
return;
|
|
||||||
}
|
|
||||||
try{
|
try{
|
||||||
let deviceId,deviceName;
|
let deviceId,deviceName;
|
||||||
if(backend==='capacitor'){
|
if(backend==='capacitor'){
|
||||||
|
setBleDiag('Inicializando plugin nativo...');
|
||||||
await ensureBleNativeReady();
|
await ensureBleNativeReady();
|
||||||
|
setBleDiag('Plugin OK · abrindo picker...');
|
||||||
const ble=window.Capacitor.Plugins.BluetoothLe;
|
const ble=window.Capacitor.Plugins.BluetoothLe;
|
||||||
const result=await ble.requestDevice({
|
const result=await ble.requestDevice({
|
||||||
services:[],
|
services:[],
|
||||||
optionalServices:[BLE_BATTERY_SERVICE,BLE_DEVICE_INFO],
|
optionalServices:[BLE_BATTERY_SERVICE,BLE_DEVICE_INFO],
|
||||||
allowDuplicates:false,
|
allowDuplicates:false,
|
||||||
});
|
});
|
||||||
if(!result?.deviceId){return}
|
if(!result?.deviceId){setBleDiag('Picker cancelado','warn');return}
|
||||||
deviceId=result.deviceId;
|
deviceId=result.deviceId;
|
||||||
deviceName=result.name||'Dispositivo BLE';
|
deviceName=result.name||'Dispositivo BLE';
|
||||||
|
setBleDiag('Selecionado: '+deviceName+' ('+deviceId+')','ok');
|
||||||
}else{
|
}else{
|
||||||
|
setBleDiag('Abrindo picker do navegador...');
|
||||||
const device=await navigator.bluetooth.requestDevice({
|
const device=await navigator.bluetooth.requestDevice({
|
||||||
acceptAllDevices:true,
|
acceptAllDevices:true,
|
||||||
optionalServices:[BLE_BATTERY_SERVICE,BLE_DEVICE_INFO],
|
optionalServices:[BLE_BATTERY_SERVICE,BLE_DEVICE_INFO],
|
||||||
|
|
@ -5802,11 +5822,13 @@ async function pairBluetoothDevice(){
|
||||||
device.addEventListener('gattserverdisconnected',()=>{
|
device.addEventListener('gattserverdisconnected',()=>{
|
||||||
const c=_bleConnections.get(deviceId);if(c)c.connected=false;
|
const c=_bleConnections.get(deviceId);if(c)c.connected=false;
|
||||||
renderBluetoothCard();
|
renderBluetoothCard();
|
||||||
|
setBleDiag('Disconnected: '+deviceName,'warn');
|
||||||
});
|
});
|
||||||
|
setBleDiag('Selecionado: '+deviceName,'ok');
|
||||||
}
|
}
|
||||||
// Conecta + lê info inicial
|
setBleDiag('Conectando GATT...');
|
||||||
const info=await connectAndRead(deviceId,deviceName);
|
const info=await connectAndRead(deviceId,deviceName);
|
||||||
// Salva no state
|
setBleDiag('Connect OK · battery='+(info.battery??'N/A')+' · mfr='+(info.manufacturer||'N/A'),info.battery!=null?'ok':'warn');
|
||||||
if(!state.btDevices)state.btDevices=[];
|
if(!state.btDevices)state.btDevices=[];
|
||||||
const existing=state.btDevices.find(d=>d.id===deviceId);
|
const existing=state.btDevices.find(d=>d.id===deviceId);
|
||||||
if(existing){
|
if(existing){
|
||||||
|
|
@ -5821,28 +5843,44 @@ async function pairBluetoothDevice(){
|
||||||
}
|
}
|
||||||
saveState();
|
saveState();
|
||||||
renderBluetoothCard();
|
renderBluetoothCard();
|
||||||
toast('✓ '+deviceName+' pareado'+(info.battery!=null?' · '+info.battery+'%':''));
|
toast('✓ '+deviceName+(info.battery!=null?' · '+info.battery+'%':' (sem leitura de bateria)'));
|
||||||
}catch(e){
|
}catch(e){
|
||||||
if(e.name==='NotFoundError'||/cancel/i.test(e.message||''))return;
|
if(e.name==='NotFoundError'||/cancel/i.test(e.message||'')){setBleDiag('Cancelado','warn');return}
|
||||||
|
const msg=e.message||e.errorMessage||JSON.stringify(e).slice(0,100)||'erro desconhecido';
|
||||||
|
setBleDiag('ERRO: '+msg,'err');
|
||||||
console.warn('[ble] pair failed',e);
|
console.warn('[ble] pair failed',e);
|
||||||
toast('Erro: '+(e.message||e.errorMessage||'pareamento falhou'));
|
toast('Falhou: '+msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function connectAndRead(deviceId,deviceName){
|
async function connectAndRead(deviceId,deviceName){
|
||||||
const info={battery:null,manufacturer:null,model:null};
|
const info={battery:null,manufacturer:null,model:null,services:[]};
|
||||||
const backend=bleBackend();
|
const backend=bleBackend();
|
||||||
try{
|
try{
|
||||||
if(backend==='capacitor'){
|
if(backend==='capacitor'){
|
||||||
const ble=window.Capacitor.Plugins.BluetoothLe;
|
const ble=window.Capacitor.Plugins.BluetoothLe;
|
||||||
await ble.connect({deviceId,timeout:15000});
|
try{
|
||||||
|
await ble.connect({deviceId,timeout:30000});
|
||||||
|
setBleDiag('GATT conectado','ok');
|
||||||
|
}catch(e){
|
||||||
|
setBleDiag('connect() falhou: '+(e.message||e.errorMessage||'?'),'err');
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
const conn=_bleConnections.get(deviceId)||{};
|
const conn=_bleConnections.get(deviceId)||{};
|
||||||
conn.backend='capacitor';conn.deviceId=deviceId;conn.connected=true;
|
conn.backend='capacitor';conn.deviceId=deviceId;conn.connected=true;
|
||||||
_bleConnections.set(deviceId,conn);
|
_bleConnections.set(deviceId,conn);
|
||||||
|
// Discover all services pra diagnóstico
|
||||||
|
try{
|
||||||
|
const r=await ble.getServices({deviceId});
|
||||||
|
const svcs=(r.services||r||[]).map(s=>s.uuid||s).slice(0,8);
|
||||||
|
setBleDiag('Serviços encontrados: '+svcs.length+' ('+svcs.map(u=>u.slice(4,8)).join(', ')+')','info');
|
||||||
|
info.services=svcs;
|
||||||
|
}catch(e){setBleDiag('getServices falhou: '+e.message,'warn')}
|
||||||
// Battery
|
// Battery
|
||||||
try{
|
try{
|
||||||
const r=await ble.read({deviceId,service:BLE_BATTERY_SERVICE,characteristic:BLE_BATTERY_CHAR});
|
const r=await ble.read({deviceId,service:BLE_BATTERY_SERVICE,characteristic:BLE_BATTERY_CHAR});
|
||||||
info.battery=parseDataView(r.value).getUint8(0);
|
info.battery=parseDataView(r.value).getUint8(0);
|
||||||
|
setBleDiag('Battery Service OK: '+info.battery+'%','ok');
|
||||||
try{
|
try{
|
||||||
await ble.startNotifications({deviceId,service:BLE_BATTERY_SERVICE,characteristic:BLE_BATTERY_CHAR});
|
await ble.startNotifications({deviceId,service:BLE_BATTERY_SERVICE,characteristic:BLE_BATTERY_CHAR});
|
||||||
ble.addListener('notification|'+deviceId+'|'+BLE_BATTERY_SERVICE+'|'+BLE_BATTERY_CHAR,(ev)=>{
|
ble.addListener('notification|'+deviceId+'|'+BLE_BATTERY_SERVICE+'|'+BLE_BATTERY_CHAR,(ev)=>{
|
||||||
|
|
@ -5850,16 +5888,19 @@ async function connectAndRead(deviceId,deviceName){
|
||||||
const dev=state.btDevices?.find(d=>d.id===deviceId);
|
const dev=state.btDevices?.find(d=>d.id===deviceId);
|
||||||
if(dev){dev.lastBattery=newVal;dev.lastSeen=Date.now();saveState();renderBluetoothCard()}
|
if(dev){dev.lastBattery=newVal;dev.lastSeen=Date.now();saveState();renderBluetoothCard()}
|
||||||
});
|
});
|
||||||
}catch(e){}
|
setBleDiag('Notificações ativas','ok');
|
||||||
}catch(e){}
|
}catch(e){setBleDiag('startNotifications falhou: '+e.message,'warn')}
|
||||||
|
}catch(e){setBleDiag('Sem Battery Service padrão (BMS pode usar protocolo proprietário)','warn')}
|
||||||
// Device info
|
// Device info
|
||||||
try{
|
try{
|
||||||
const r=await ble.read({deviceId,service:BLE_DEVICE_INFO,characteristic:BLE_MANUFACTURER_CHAR});
|
const r=await ble.read({deviceId,service:BLE_DEVICE_INFO,characteristic:BLE_MANUFACTURER_CHAR});
|
||||||
info.manufacturer=new TextDecoder().decode(parseDataView(r.value));
|
info.manufacturer=new TextDecoder().decode(parseDataView(r.value));
|
||||||
|
setBleDiag('Fabricante: '+info.manufacturer,'info');
|
||||||
}catch(e){}
|
}catch(e){}
|
||||||
try{
|
try{
|
||||||
const r=await ble.read({deviceId,service:BLE_DEVICE_INFO,characteristic:BLE_MODEL_CHAR});
|
const r=await ble.read({deviceId,service:BLE_DEVICE_INFO,characteristic:BLE_MODEL_CHAR});
|
||||||
info.model=new TextDecoder().decode(parseDataView(r.value));
|
info.model=new TextDecoder().decode(parseDataView(r.value));
|
||||||
|
setBleDiag('Modelo: '+info.model,'info');
|
||||||
}catch(e){}
|
}catch(e){}
|
||||||
}else{
|
}else{
|
||||||
const conn=_bleConnections.get(deviceId);
|
const conn=_bleConnections.get(deviceId);
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ android {
|
||||||
applicationId "br.com.pontualtech.shivao"
|
applicationId "br.com.pontualtech.shivao"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 14
|
versionCode 15
|
||||||
versionName "1.9.1"
|
versionName "1.9.2"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "shivao-mobile",
|
"name": "shivao-mobile",
|
||||||
"version": "1.9.1",
|
"version": "1.9.2",
|
||||||
"description": "Shivao app nativo (Capacitor wrapper Android/iOS)",
|
"description": "Shivao app nativo (Capacitor wrapper Android/iOS)",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
||||||
|
|
@ -1977,7 +1977,11 @@ Marque zonas de proibição (alarme alto) ou atenção (aviso suave). Detecção
|
||||||
<div id="bt-support" style="font-family:var(--f-mono);font-size:10.5px;letter-spacing:.04em;margin-bottom:10px">Verificando suporte...</div>
|
<div id="bt-support" style="font-family:var(--f-mono);font-size:10.5px;letter-spacing:.04em;margin-bottom:10px">Verificando suporte...</div>
|
||||||
<button class="btn btn-block btn-primary" onclick="pairBluetoothDevice()">+ Parear novo dispositivo Bluetooth</button>
|
<button class="btn btn-block btn-primary" onclick="pairBluetoothDevice()">+ Parear novo dispositivo Bluetooth</button>
|
||||||
<div id="bt-list" style="margin-top:14px"></div>
|
<div id="bt-list" style="margin-top:14px"></div>
|
||||||
<div class="field-hint" style="margin-top:8px">Limitações: <strong>iOS Safari não suporta Web Bluetooth</strong> (use no Chrome PC ou Android). Reconexão automática varia por device.</div>
|
<details style="margin-top:10px">
|
||||||
|
<summary style="cursor:pointer;font-family:var(--f-mono);font-size:11px;color:var(--m-text-soft,#7d97ad);letter-spacing:.06em">📋 Diagnóstico (logs do pareamento)</summary>
|
||||||
|
<div id="bt-diag" style="background:var(--m-bg-2,#0f2a40);border:1px solid var(--m-border,rgba(255,255,255,.08));border-radius:6px;padding:10px;margin-top:6px;max-height:200px;overflow-y:auto"></div>
|
||||||
|
</details>
|
||||||
|
<div class="field-hint" style="margin-top:8px">Limitações: <strong>iOS Safari não suporta Web Bluetooth</strong>. APK Android usa plugin nativo. BMS proprietários (Victron, JBD) podem aparecer mas não expor Battery Service padrão.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Raymarine Gateway -->
|
<!-- Raymarine Gateway -->
|
||||||
|
|
@ -5771,26 +5775,42 @@ async function ensureBleNativeReady(){
|
||||||
_bleNativeInitialized=true;
|
_bleNativeInitialized=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Diagnóstico visível: mostra cada passo no card BLE
|
||||||
|
function setBleDiag(msg,type){
|
||||||
|
const el=document.getElementById('bt-diag');
|
||||||
|
if(!el)return;
|
||||||
|
const colors={info:'var(--m-text-mid,#b3c5d6)',ok:'var(--m-ok,#10b981)',err:'var(--m-danger,#ef4444)',warn:'var(--m-warn,#f59e0b)'};
|
||||||
|
const time=new Date().toLocaleTimeString('pt-BR',{hour12:false});
|
||||||
|
const line=`<div style="color:${colors[type||'info']};font-family:var(--f-mono);font-size:11px;line-height:1.5">${time} · ${escapeHtml(msg)}</div>`;
|
||||||
|
el.innerHTML=line+el.innerHTML;
|
||||||
|
// Mantém só últimas 12 linhas
|
||||||
|
const lines=el.children;
|
||||||
|
while(lines.length>12)el.removeChild(lines[lines.length-1]);
|
||||||
|
console.log('[ble]',msg);
|
||||||
|
}
|
||||||
|
|
||||||
async function pairBluetoothDevice(){
|
async function pairBluetoothDevice(){
|
||||||
const backend=bleBackend();
|
const backend=bleBackend();
|
||||||
if(!backend){
|
setBleDiag('Backend: '+(backend||'NENHUM'),backend?'info':'err');
|
||||||
toast('Bluetooth indisponível neste dispositivo.');
|
if(!backend){toast('Bluetooth indisponível');return}
|
||||||
return;
|
|
||||||
}
|
|
||||||
try{
|
try{
|
||||||
let deviceId,deviceName;
|
let deviceId,deviceName;
|
||||||
if(backend==='capacitor'){
|
if(backend==='capacitor'){
|
||||||
|
setBleDiag('Inicializando plugin nativo...');
|
||||||
await ensureBleNativeReady();
|
await ensureBleNativeReady();
|
||||||
|
setBleDiag('Plugin OK · abrindo picker...');
|
||||||
const ble=window.Capacitor.Plugins.BluetoothLe;
|
const ble=window.Capacitor.Plugins.BluetoothLe;
|
||||||
const result=await ble.requestDevice({
|
const result=await ble.requestDevice({
|
||||||
services:[],
|
services:[],
|
||||||
optionalServices:[BLE_BATTERY_SERVICE,BLE_DEVICE_INFO],
|
optionalServices:[BLE_BATTERY_SERVICE,BLE_DEVICE_INFO],
|
||||||
allowDuplicates:false,
|
allowDuplicates:false,
|
||||||
});
|
});
|
||||||
if(!result?.deviceId){return}
|
if(!result?.deviceId){setBleDiag('Picker cancelado','warn');return}
|
||||||
deviceId=result.deviceId;
|
deviceId=result.deviceId;
|
||||||
deviceName=result.name||'Dispositivo BLE';
|
deviceName=result.name||'Dispositivo BLE';
|
||||||
|
setBleDiag('Selecionado: '+deviceName+' ('+deviceId+')','ok');
|
||||||
}else{
|
}else{
|
||||||
|
setBleDiag('Abrindo picker do navegador...');
|
||||||
const device=await navigator.bluetooth.requestDevice({
|
const device=await navigator.bluetooth.requestDevice({
|
||||||
acceptAllDevices:true,
|
acceptAllDevices:true,
|
||||||
optionalServices:[BLE_BATTERY_SERVICE,BLE_DEVICE_INFO],
|
optionalServices:[BLE_BATTERY_SERVICE,BLE_DEVICE_INFO],
|
||||||
|
|
@ -5802,11 +5822,13 @@ async function pairBluetoothDevice(){
|
||||||
device.addEventListener('gattserverdisconnected',()=>{
|
device.addEventListener('gattserverdisconnected',()=>{
|
||||||
const c=_bleConnections.get(deviceId);if(c)c.connected=false;
|
const c=_bleConnections.get(deviceId);if(c)c.connected=false;
|
||||||
renderBluetoothCard();
|
renderBluetoothCard();
|
||||||
|
setBleDiag('Disconnected: '+deviceName,'warn');
|
||||||
});
|
});
|
||||||
|
setBleDiag('Selecionado: '+deviceName,'ok');
|
||||||
}
|
}
|
||||||
// Conecta + lê info inicial
|
setBleDiag('Conectando GATT...');
|
||||||
const info=await connectAndRead(deviceId,deviceName);
|
const info=await connectAndRead(deviceId,deviceName);
|
||||||
// Salva no state
|
setBleDiag('Connect OK · battery='+(info.battery??'N/A')+' · mfr='+(info.manufacturer||'N/A'),info.battery!=null?'ok':'warn');
|
||||||
if(!state.btDevices)state.btDevices=[];
|
if(!state.btDevices)state.btDevices=[];
|
||||||
const existing=state.btDevices.find(d=>d.id===deviceId);
|
const existing=state.btDevices.find(d=>d.id===deviceId);
|
||||||
if(existing){
|
if(existing){
|
||||||
|
|
@ -5821,28 +5843,44 @@ async function pairBluetoothDevice(){
|
||||||
}
|
}
|
||||||
saveState();
|
saveState();
|
||||||
renderBluetoothCard();
|
renderBluetoothCard();
|
||||||
toast('✓ '+deviceName+' pareado'+(info.battery!=null?' · '+info.battery+'%':''));
|
toast('✓ '+deviceName+(info.battery!=null?' · '+info.battery+'%':' (sem leitura de bateria)'));
|
||||||
}catch(e){
|
}catch(e){
|
||||||
if(e.name==='NotFoundError'||/cancel/i.test(e.message||''))return;
|
if(e.name==='NotFoundError'||/cancel/i.test(e.message||'')){setBleDiag('Cancelado','warn');return}
|
||||||
|
const msg=e.message||e.errorMessage||JSON.stringify(e).slice(0,100)||'erro desconhecido';
|
||||||
|
setBleDiag('ERRO: '+msg,'err');
|
||||||
console.warn('[ble] pair failed',e);
|
console.warn('[ble] pair failed',e);
|
||||||
toast('Erro: '+(e.message||e.errorMessage||'pareamento falhou'));
|
toast('Falhou: '+msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function connectAndRead(deviceId,deviceName){
|
async function connectAndRead(deviceId,deviceName){
|
||||||
const info={battery:null,manufacturer:null,model:null};
|
const info={battery:null,manufacturer:null,model:null,services:[]};
|
||||||
const backend=bleBackend();
|
const backend=bleBackend();
|
||||||
try{
|
try{
|
||||||
if(backend==='capacitor'){
|
if(backend==='capacitor'){
|
||||||
const ble=window.Capacitor.Plugins.BluetoothLe;
|
const ble=window.Capacitor.Plugins.BluetoothLe;
|
||||||
await ble.connect({deviceId,timeout:15000});
|
try{
|
||||||
|
await ble.connect({deviceId,timeout:30000});
|
||||||
|
setBleDiag('GATT conectado','ok');
|
||||||
|
}catch(e){
|
||||||
|
setBleDiag('connect() falhou: '+(e.message||e.errorMessage||'?'),'err');
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
const conn=_bleConnections.get(deviceId)||{};
|
const conn=_bleConnections.get(deviceId)||{};
|
||||||
conn.backend='capacitor';conn.deviceId=deviceId;conn.connected=true;
|
conn.backend='capacitor';conn.deviceId=deviceId;conn.connected=true;
|
||||||
_bleConnections.set(deviceId,conn);
|
_bleConnections.set(deviceId,conn);
|
||||||
|
// Discover all services pra diagnóstico
|
||||||
|
try{
|
||||||
|
const r=await ble.getServices({deviceId});
|
||||||
|
const svcs=(r.services||r||[]).map(s=>s.uuid||s).slice(0,8);
|
||||||
|
setBleDiag('Serviços encontrados: '+svcs.length+' ('+svcs.map(u=>u.slice(4,8)).join(', ')+')','info');
|
||||||
|
info.services=svcs;
|
||||||
|
}catch(e){setBleDiag('getServices falhou: '+e.message,'warn')}
|
||||||
// Battery
|
// Battery
|
||||||
try{
|
try{
|
||||||
const r=await ble.read({deviceId,service:BLE_BATTERY_SERVICE,characteristic:BLE_BATTERY_CHAR});
|
const r=await ble.read({deviceId,service:BLE_BATTERY_SERVICE,characteristic:BLE_BATTERY_CHAR});
|
||||||
info.battery=parseDataView(r.value).getUint8(0);
|
info.battery=parseDataView(r.value).getUint8(0);
|
||||||
|
setBleDiag('Battery Service OK: '+info.battery+'%','ok');
|
||||||
try{
|
try{
|
||||||
await ble.startNotifications({deviceId,service:BLE_BATTERY_SERVICE,characteristic:BLE_BATTERY_CHAR});
|
await ble.startNotifications({deviceId,service:BLE_BATTERY_SERVICE,characteristic:BLE_BATTERY_CHAR});
|
||||||
ble.addListener('notification|'+deviceId+'|'+BLE_BATTERY_SERVICE+'|'+BLE_BATTERY_CHAR,(ev)=>{
|
ble.addListener('notification|'+deviceId+'|'+BLE_BATTERY_SERVICE+'|'+BLE_BATTERY_CHAR,(ev)=>{
|
||||||
|
|
@ -5850,16 +5888,19 @@ async function connectAndRead(deviceId,deviceName){
|
||||||
const dev=state.btDevices?.find(d=>d.id===deviceId);
|
const dev=state.btDevices?.find(d=>d.id===deviceId);
|
||||||
if(dev){dev.lastBattery=newVal;dev.lastSeen=Date.now();saveState();renderBluetoothCard()}
|
if(dev){dev.lastBattery=newVal;dev.lastSeen=Date.now();saveState();renderBluetoothCard()}
|
||||||
});
|
});
|
||||||
}catch(e){}
|
setBleDiag('Notificações ativas','ok');
|
||||||
}catch(e){}
|
}catch(e){setBleDiag('startNotifications falhou: '+e.message,'warn')}
|
||||||
|
}catch(e){setBleDiag('Sem Battery Service padrão (BMS pode usar protocolo proprietário)','warn')}
|
||||||
// Device info
|
// Device info
|
||||||
try{
|
try{
|
||||||
const r=await ble.read({deviceId,service:BLE_DEVICE_INFO,characteristic:BLE_MANUFACTURER_CHAR});
|
const r=await ble.read({deviceId,service:BLE_DEVICE_INFO,characteristic:BLE_MANUFACTURER_CHAR});
|
||||||
info.manufacturer=new TextDecoder().decode(parseDataView(r.value));
|
info.manufacturer=new TextDecoder().decode(parseDataView(r.value));
|
||||||
|
setBleDiag('Fabricante: '+info.manufacturer,'info');
|
||||||
}catch(e){}
|
}catch(e){}
|
||||||
try{
|
try{
|
||||||
const r=await ble.read({deviceId,service:BLE_DEVICE_INFO,characteristic:BLE_MODEL_CHAR});
|
const r=await ble.read({deviceId,service:BLE_DEVICE_INFO,characteristic:BLE_MODEL_CHAR});
|
||||||
info.model=new TextDecoder().decode(parseDataView(r.value));
|
info.model=new TextDecoder().decode(parseDataView(r.value));
|
||||||
|
setBleDiag('Modelo: '+info.model,'info');
|
||||||
}catch(e){}
|
}catch(e){}
|
||||||
}else{
|
}else{
|
||||||
const conn=_bleConnections.get(deviceId);
|
const conn=_bleConnections.get(deviceId);
|
||||||
|
|
|
||||||
|
|
@ -347,7 +347,7 @@ app.get('/.well-known/assetlinks.json', (req, res) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Atalho: /apk redireciona pra última APK release no Forgejo
|
// Atalho: /apk redireciona pra última APK release no Forgejo
|
||||||
const LATEST_APK_URL = 'https://git.pontualtech.work/karlao/shivao-projeto/releases/download/v1.9.1/Shivao-v1.9.1.apk';
|
const LATEST_APK_URL = 'https://git.pontualtech.work/karlao/shivao-projeto/releases/download/v1.9.2/Shivao-v1.9.2.apk';
|
||||||
app.get('/apk', (req, res) => res.redirect(302, LATEST_APK_URL));
|
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)
|
// Página A4 imprimível com QR Code + instruções (cola no barco/marina)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue