diff --git a/app/diario-bordo.html b/app/diario-bordo.html index d6e7c29..5375287 100644 --- a/app/diario-bordo.html +++ b/app/diario-bordo.html @@ -1977,7 +1977,11 @@ Marque zonas de proibição (alarme alto) ou atenção (aviso suave). Detecção
Verificando suporte...
-
Limitações: iOS Safari não suporta Web Bluetooth (use no Chrome PC ou Android). Reconexão automática varia por device.
+
+ 📋 Diagnóstico (logs do pareamento) +
+
+
Limitações: iOS Safari não suporta Web Bluetooth. APK Android usa plugin nativo. BMS proprietários (Victron, JBD) podem aparecer mas não expor Battery Service padrão.
@@ -5771,26 +5775,42 @@ async function ensureBleNativeReady(){ _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=`
${time} · ${escapeHtml(msg)}
`; + 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(){ const backend=bleBackend(); - if(!backend){ - toast('Bluetooth indisponível neste dispositivo.'); - return; - } + setBleDiag('Backend: '+(backend||'NENHUM'),backend?'info':'err'); + if(!backend){toast('Bluetooth indisponível');return} try{ let deviceId,deviceName; if(backend==='capacitor'){ + setBleDiag('Inicializando plugin nativo...'); await ensureBleNativeReady(); + setBleDiag('Plugin OK · abrindo picker...'); const ble=window.Capacitor.Plugins.BluetoothLe; const result=await ble.requestDevice({ services:[], optionalServices:[BLE_BATTERY_SERVICE,BLE_DEVICE_INFO], allowDuplicates:false, }); - if(!result?.deviceId){return} + if(!result?.deviceId){setBleDiag('Picker cancelado','warn');return} deviceId=result.deviceId; deviceName=result.name||'Dispositivo BLE'; + setBleDiag('Selecionado: '+deviceName+' ('+deviceId+')','ok'); }else{ + setBleDiag('Abrindo picker do navegador...'); const device=await navigator.bluetooth.requestDevice({ acceptAllDevices:true, optionalServices:[BLE_BATTERY_SERVICE,BLE_DEVICE_INFO], @@ -5802,11 +5822,13 @@ async function pairBluetoothDevice(){ device.addEventListener('gattserverdisconnected',()=>{ const c=_bleConnections.get(deviceId);if(c)c.connected=false; renderBluetoothCard(); + setBleDiag('Disconnected: '+deviceName,'warn'); }); + setBleDiag('Selecionado: '+deviceName,'ok'); } - // Conecta + lê info inicial + setBleDiag('Conectando GATT...'); 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=[]; const existing=state.btDevices.find(d=>d.id===deviceId); if(existing){ @@ -5821,28 +5843,44 @@ async function pairBluetoothDevice(){ } saveState(); renderBluetoothCard(); - toast('✓ '+deviceName+' pareado'+(info.battery!=null?' · '+info.battery+'%':'')); + toast('✓ '+deviceName+(info.battery!=null?' · '+info.battery+'%':' (sem leitura de bateria)')); }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); - toast('Erro: '+(e.message||e.errorMessage||'pareamento falhou')); + toast('Falhou: '+msg); } } 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(); try{ if(backend==='capacitor'){ 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)||{}; conn.backend='capacitor';conn.deviceId=deviceId;conn.connected=true; _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 try{ const r=await ble.read({deviceId,service:BLE_BATTERY_SERVICE,characteristic:BLE_BATTERY_CHAR}); info.battery=parseDataView(r.value).getUint8(0); + setBleDiag('Battery Service OK: '+info.battery+'%','ok'); try{ await ble.startNotifications({deviceId,service:BLE_BATTERY_SERVICE,characteristic:BLE_BATTERY_CHAR}); 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); if(dev){dev.lastBattery=newVal;dev.lastSeen=Date.now();saveState();renderBluetoothCard()} }); - }catch(e){} - }catch(e){} + setBleDiag('Notificações ativas','ok'); + }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 try{ const r=await ble.read({deviceId,service:BLE_DEVICE_INFO,characteristic:BLE_MANUFACTURER_CHAR}); info.manufacturer=new TextDecoder().decode(parseDataView(r.value)); + setBleDiag('Fabricante: '+info.manufacturer,'info'); }catch(e){} try{ const r=await ble.read({deviceId,service:BLE_DEVICE_INFO,characteristic:BLE_MODEL_CHAR}); info.model=new TextDecoder().decode(parseDataView(r.value)); + setBleDiag('Modelo: '+info.model,'info'); }catch(e){} }else{ const conn=_bleConnections.get(deviceId); diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle index de77655..778d4fd 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 14 - versionName "1.9.1" + versionCode 15 + versionName "1.9.2" 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 450c1ff..e8685a6 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -1,6 +1,6 @@ { "name": "shivao-mobile", - "version": "1.9.1", + "version": "1.9.2", "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 d6e7c29..5375287 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -1977,7 +1977,11 @@ Marque zonas de proibição (alarme alto) ou atenção (aviso suave). Detecção
Verificando suporte...
-
Limitações: iOS Safari não suporta Web Bluetooth (use no Chrome PC ou Android). Reconexão automática varia por device.
+
+ 📋 Diagnóstico (logs do pareamento) +
+
+
Limitações: iOS Safari não suporta Web Bluetooth. APK Android usa plugin nativo. BMS proprietários (Victron, JBD) podem aparecer mas não expor Battery Service padrão.
@@ -5771,26 +5775,42 @@ async function ensureBleNativeReady(){ _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=`
${time} · ${escapeHtml(msg)}
`; + 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(){ const backend=bleBackend(); - if(!backend){ - toast('Bluetooth indisponível neste dispositivo.'); - return; - } + setBleDiag('Backend: '+(backend||'NENHUM'),backend?'info':'err'); + if(!backend){toast('Bluetooth indisponível');return} try{ let deviceId,deviceName; if(backend==='capacitor'){ + setBleDiag('Inicializando plugin nativo...'); await ensureBleNativeReady(); + setBleDiag('Plugin OK · abrindo picker...'); const ble=window.Capacitor.Plugins.BluetoothLe; const result=await ble.requestDevice({ services:[], optionalServices:[BLE_BATTERY_SERVICE,BLE_DEVICE_INFO], allowDuplicates:false, }); - if(!result?.deviceId){return} + if(!result?.deviceId){setBleDiag('Picker cancelado','warn');return} deviceId=result.deviceId; deviceName=result.name||'Dispositivo BLE'; + setBleDiag('Selecionado: '+deviceName+' ('+deviceId+')','ok'); }else{ + setBleDiag('Abrindo picker do navegador...'); const device=await navigator.bluetooth.requestDevice({ acceptAllDevices:true, optionalServices:[BLE_BATTERY_SERVICE,BLE_DEVICE_INFO], @@ -5802,11 +5822,13 @@ async function pairBluetoothDevice(){ device.addEventListener('gattserverdisconnected',()=>{ const c=_bleConnections.get(deviceId);if(c)c.connected=false; renderBluetoothCard(); + setBleDiag('Disconnected: '+deviceName,'warn'); }); + setBleDiag('Selecionado: '+deviceName,'ok'); } - // Conecta + lê info inicial + setBleDiag('Conectando GATT...'); 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=[]; const existing=state.btDevices.find(d=>d.id===deviceId); if(existing){ @@ -5821,28 +5843,44 @@ async function pairBluetoothDevice(){ } saveState(); renderBluetoothCard(); - toast('✓ '+deviceName+' pareado'+(info.battery!=null?' · '+info.battery+'%':'')); + toast('✓ '+deviceName+(info.battery!=null?' · '+info.battery+'%':' (sem leitura de bateria)')); }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); - toast('Erro: '+(e.message||e.errorMessage||'pareamento falhou')); + toast('Falhou: '+msg); } } 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(); try{ if(backend==='capacitor'){ 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)||{}; conn.backend='capacitor';conn.deviceId=deviceId;conn.connected=true; _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 try{ const r=await ble.read({deviceId,service:BLE_BATTERY_SERVICE,characteristic:BLE_BATTERY_CHAR}); info.battery=parseDataView(r.value).getUint8(0); + setBleDiag('Battery Service OK: '+info.battery+'%','ok'); try{ await ble.startNotifications({deviceId,service:BLE_BATTERY_SERVICE,characteristic:BLE_BATTERY_CHAR}); 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); if(dev){dev.lastBattery=newVal;dev.lastSeen=Date.now();saveState();renderBluetoothCard()} }); - }catch(e){} - }catch(e){} + setBleDiag('Notificações ativas','ok'); + }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 try{ const r=await ble.read({deviceId,service:BLE_DEVICE_INFO,characteristic:BLE_MANUFACTURER_CHAR}); info.manufacturer=new TextDecoder().decode(parseDataView(r.value)); + setBleDiag('Fabricante: '+info.manufacturer,'info'); }catch(e){} try{ const r=await ble.read({deviceId,service:BLE_DEVICE_INFO,characteristic:BLE_MODEL_CHAR}); info.model=new TextDecoder().decode(parseDataView(r.value)); + setBleDiag('Modelo: '+info.model,'info'); }catch(e){} }else{ const conn=_bleConnections.get(deviceId); diff --git a/server/src/index.js b/server/src/index.js index 03071ff..4b28ad2 100644 --- a/server/src/index.js +++ b/server/src/index.js @@ -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.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)); // Página A4 imprimível com QR Code + instruções (cola no barco/marina)