Criar APK grátis

API da ponte JavaScript

Sensores de fitness BLE

Frequência cardíaca, velocidade, cadência, potência e bateria a partir de perfis padrão de Bluetooth Low Energy.

AppMint.beacons helper#

AppMint.beacons.startScan(options) / stopScan() / isScanning()

Exemplo

Encontra beacons Bluetooth próximos - iBeacon e Eddystone (UID, URL e TLM) - e informa a página sobre cada um cerca de uma vez por segundo por meio do evento appmint:beacon, com a intensidade do sinal e uma distância aproximada.

Retorna: startScan() → uma Promise de {ok: true} ou {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked). stopScan() → {ok: true}. isScanning() → true/false. Requer: nada a marcar - um build cujo código chama beacons.startScan declara as permissões de busca Bluetooth e de localização precisa (os resultados de beacons são dados de localização para o Android, então o usuário é perguntado por ambas, e o Play lista o app como usando localização).

Rastreie os beacons de um local:

window.addEventListener('appmint:beacon', function (e) {
  var b = e.detail;
  if (b.type === 'ibeacon') {
    showNearest(b.uuid, b.major, b.minor, b.distance);      // metres, a rough estimate
  } else if (b.type === 'eddystone-url') {
    offerLink(b.url);
  } else if (b.type === 'state') {
    console.log('beacon scan ' + b.state);                  // 'scanning' | 'stopped'
  } else if (b.type === 'error') {
    console.warn('beacon scan error', b.error);
  }
});

async function startRanging() {
  if (!(window.AppMint && AppMint.beacons)) return;
  var r = await AppMint.beacons.startScan({ uuids: ['e2c56db5-dffb-48d2-b060-d0f5a71096e0'] });
  if (!r.ok) alert(r.code === 'bluetooth_off' ? 'Turn on Bluetooth' : 'Cannot scan: ' + r.code);
}

Somente Eddystone, com telemetria de bateria:

AppMint.beacons.startScan({ ibeacon: false });
window.addEventListener('appmint:beacon', function (e) {
  var b = e.detail;
  if (b.type === 'eddystone-uid') console.log(b.namespace, b.instance, b.rssi, b.distance);
  if (b.type === 'eddystone-tlm') console.log(b.address, b.batteryMilliVolts + ' mV', b.temperatureC + ' °C');
});

Pare quando a tela for deixada:

document.addEventListener('visibilitychange', async function () {
  if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});

Campos do evento: todo beacon tem type, rssi, address, timestamp; o iBeacon adiciona uuid, major, minor, txPower, distance; o Eddystone UID adiciona namespace, instance, txPower, distance; o URL adiciona url; o TLM adiciona batteryMilliVolts, temperatureC, advertCount, uptimeSeconds. uuids filtra UUIDs de iBeacon e namespaces Eddystone (hex em minúsculas).

Observações: A distância vem da intensidade do sinal e só serve para perto / longe - paredes e corpos a alteram em metros. A busca continua até stopScan() ou até o app fechar.

beaconIsScanning ponte#

window.WebToApk.beaconIsScanning(): Boolean

Se uma busca de beacons está em andamento.

Exemplo

Documentado junto com AppMint.beacons - o exemplo de lá mostra este também.

Encontra beacons Bluetooth próximos - iBeacon e Eddystone (UID, URL e TLM) - e informa a página sobre cada um cerca de uma vez por segundo por meio do evento appmint:beacon, com a intensidade do sinal e uma distância aproximada.

Retorna: startScan() → uma Promise de {ok: true} ou {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked). stopScan() → {ok: true}. isScanning() → true/false. Requer: nada a marcar - um build cujo código chama beacons.startScan declara as permissões de busca Bluetooth e de localização precisa (os resultados de beacons são dados de localização para o Android, então o usuário é perguntado por ambas, e o Play lista o app como usando localização).

Rastreie os beacons de um local:

window.addEventListener('appmint:beacon', function (e) {
  var b = e.detail;
  if (b.type === 'ibeacon') {
    showNearest(b.uuid, b.major, b.minor, b.distance);      // metres, a rough estimate
  } else if (b.type === 'eddystone-url') {
    offerLink(b.url);
  } else if (b.type === 'state') {
    console.log('beacon scan ' + b.state);                  // 'scanning' | 'stopped'
  } else if (b.type === 'error') {
    console.warn('beacon scan error', b.error);
  }
});

async function startRanging() {
  if (!(window.AppMint && AppMint.beacons)) return;
  var r = await AppMint.beacons.startScan({ uuids: ['e2c56db5-dffb-48d2-b060-d0f5a71096e0'] });
  if (!r.ok) alert(r.code === 'bluetooth_off' ? 'Turn on Bluetooth' : 'Cannot scan: ' + r.code);
}

Somente Eddystone, com telemetria de bateria:

AppMint.beacons.startScan({ ibeacon: false });
window.addEventListener('appmint:beacon', function (e) {
  var b = e.detail;
  if (b.type === 'eddystone-uid') console.log(b.namespace, b.instance, b.rssi, b.distance);
  if (b.type === 'eddystone-tlm') console.log(b.address, b.batteryMilliVolts + ' mV', b.temperatureC + ' °C');
});

Pare quando a tela for deixada:

document.addEventListener('visibilitychange', async function () {
  if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});

Campos do evento: todo beacon tem type, rssi, address, timestamp; o iBeacon adiciona uuid, major, minor, txPower, distance; o Eddystone UID adiciona namespace, instance, txPower, distance; o URL adiciona url; o TLM adiciona batteryMilliVolts, temperatureC, advertCount, uptimeSeconds. uuids filtra UUIDs de iBeacon e namespaces Eddystone (hex em minúsculas).

Observações: A distância vem da intensidade do sinal e só serve para perto / longe - paredes e corpos a alteram em metros. A busca continua até stopScan() ou até o app fechar.

beaconStartScan ponte#

window.WebToApk.beaconStartScan(optionsJson: String, callbackId: String)

Inicia o rastreamento de beacons com [optionsJson] `{uuids?, ibeacon?, eddystone?}`; resolve [callbackId] com `{ok}` ou `{ok:false, code}`.

Exemplo

Documentado junto com AppMint.beacons - o exemplo de lá mostra este também.

Encontra beacons Bluetooth próximos - iBeacon e Eddystone (UID, URL e TLM) - e informa a página sobre cada um cerca de uma vez por segundo por meio do evento appmint:beacon, com a intensidade do sinal e uma distância aproximada.

Retorna: startScan() → uma Promise de {ok: true} ou {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked). stopScan() → {ok: true}. isScanning() → true/false. Requer: nada a marcar - um build cujo código chama beacons.startScan declara as permissões de busca Bluetooth e de localização precisa (os resultados de beacons são dados de localização para o Android, então o usuário é perguntado por ambas, e o Play lista o app como usando localização).

Rastreie os beacons de um local:

window.addEventListener('appmint:beacon', function (e) {
  var b = e.detail;
  if (b.type === 'ibeacon') {
    showNearest(b.uuid, b.major, b.minor, b.distance);      // metres, a rough estimate
  } else if (b.type === 'eddystone-url') {
    offerLink(b.url);
  } else if (b.type === 'state') {
    console.log('beacon scan ' + b.state);                  // 'scanning' | 'stopped'
  } else if (b.type === 'error') {
    console.warn('beacon scan error', b.error);
  }
});

async function startRanging() {
  if (!(window.AppMint && AppMint.beacons)) return;
  var r = await AppMint.beacons.startScan({ uuids: ['e2c56db5-dffb-48d2-b060-d0f5a71096e0'] });
  if (!r.ok) alert(r.code === 'bluetooth_off' ? 'Turn on Bluetooth' : 'Cannot scan: ' + r.code);
}

Somente Eddystone, com telemetria de bateria:

AppMint.beacons.startScan({ ibeacon: false });
window.addEventListener('appmint:beacon', function (e) {
  var b = e.detail;
  if (b.type === 'eddystone-uid') console.log(b.namespace, b.instance, b.rssi, b.distance);
  if (b.type === 'eddystone-tlm') console.log(b.address, b.batteryMilliVolts + ' mV', b.temperatureC + ' °C');
});

Pare quando a tela for deixada:

document.addEventListener('visibilitychange', async function () {
  if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});

Campos do evento: todo beacon tem type, rssi, address, timestamp; o iBeacon adiciona uuid, major, minor, txPower, distance; o Eddystone UID adiciona namespace, instance, txPower, distance; o URL adiciona url; o TLM adiciona batteryMilliVolts, temperatureC, advertCount, uptimeSeconds. uuids filtra UUIDs de iBeacon e namespaces Eddystone (hex em minúsculas).

Observações: A distância vem da intensidade do sinal e só serve para perto / longe - paredes e corpos a alteram em metros. A busca continua até stopScan() ou até o app fechar.

beaconStopScan ponte#

window.WebToApk.beaconStopScan(callbackId: String)

Para o rastreamento de beacons; resolve [callbackId] com `{ok:true}`.

Exemplo

Documentado junto com AppMint.beacons - o exemplo de lá mostra este também.

Encontra beacons Bluetooth próximos - iBeacon e Eddystone (UID, URL e TLM) - e informa a página sobre cada um cerca de uma vez por segundo por meio do evento appmint:beacon, com a intensidade do sinal e uma distância aproximada.

Retorna: startScan() → uma Promise de {ok: true} ou {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked). stopScan() → {ok: true}. isScanning() → true/false. Requer: nada a marcar - um build cujo código chama beacons.startScan declara as permissões de busca Bluetooth e de localização precisa (os resultados de beacons são dados de localização para o Android, então o usuário é perguntado por ambas, e o Play lista o app como usando localização).

Rastreie os beacons de um local:

window.addEventListener('appmint:beacon', function (e) {
  var b = e.detail;
  if (b.type === 'ibeacon') {
    showNearest(b.uuid, b.major, b.minor, b.distance);      // metres, a rough estimate
  } else if (b.type === 'eddystone-url') {
    offerLink(b.url);
  } else if (b.type === 'state') {
    console.log('beacon scan ' + b.state);                  // 'scanning' | 'stopped'
  } else if (b.type === 'error') {
    console.warn('beacon scan error', b.error);
  }
});

async function startRanging() {
  if (!(window.AppMint && AppMint.beacons)) return;
  var r = await AppMint.beacons.startScan({ uuids: ['e2c56db5-dffb-48d2-b060-d0f5a71096e0'] });
  if (!r.ok) alert(r.code === 'bluetooth_off' ? 'Turn on Bluetooth' : 'Cannot scan: ' + r.code);
}

Somente Eddystone, com telemetria de bateria:

AppMint.beacons.startScan({ ibeacon: false });
window.addEventListener('appmint:beacon', function (e) {
  var b = e.detail;
  if (b.type === 'eddystone-uid') console.log(b.namespace, b.instance, b.rssi, b.distance);
  if (b.type === 'eddystone-tlm') console.log(b.address, b.batteryMilliVolts + ' mV', b.temperatureC + ' °C');
});

Pare quando a tela for deixada:

document.addEventListener('visibilitychange', async function () {
  if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});

Campos do evento: todo beacon tem type, rssi, address, timestamp; o iBeacon adiciona uuid, major, minor, txPower, distance; o Eddystone UID adiciona namespace, instance, txPower, distance; o URL adiciona url; o TLM adiciona batteryMilliVolts, temperatureC, advertCount, uptimeSeconds. uuids filtra UUIDs de iBeacon e namespaces Eddystone (hex em minúsculas).

Observações: A distância vem da intensidade do sinal e só serve para perto / longe - paredes e corpos a alteram em metros. A busca continua até stopScan() ou até o app fechar.

bleConnect ponte#

window.WebToApk.bleConnect(address: String)

Runtime para permBluetooth. Nenhuma opção do assistente o ativa: o build o arma quando o próprio código da página chama estes métodos (ou o Web Bluetooth). Antes deste runtime as permissões eram declaradas sem que nada no runtime tocasse um sensor, exatamente o padrão declarado-mas-morto que o SmsBridge documenta. Os resultados chegam como eventos `appmint:ble` / window.onAppMintBle({kind, ...}).

Descrito pelo seu grupo, Sensores de fitness BLE (frequência cardíaca / velocidade / cadência / potência / bateria), e não por conta própria.

Exemplo

Conecta a um sensor de fitness encontrado por bleStartScan() e começa a transmitir suas leituras.

Retorna: nada. O progresso e os dados chegam como eventos appmint:ble (e window.onAppMintBle): {kind:'state', state:'connecting', address}, {kind:'state', state:'connected', address}, {kind:'state', state:'ready', services:[...]}, depois {kind:'data', type, data} para cada leitura. Requer: Bluetooth (ativado por um build de ZIP ou HTML quando seu código usa a ponte de sensores) e a permissão de Dispositivos próximos, pedida no primeiro uso.

Passe o address de um evento device, a partir do toque do usuário:

function connectSensor(address) {
  if (!window.WebToApk || typeof window.WebToApk.bleConnect !== 'function') return;
  window.WebToApk.bleConnect(address);
}

window.onAppMintBle = function (d) {
  if (d.kind === 'state') {
    // 'connecting' | 'connected' | 'ready' | 'disconnected' (also 'scanning' / 'scan_stopped')
    showStatus(d.state);
  } else if (d.kind === 'data') {
    if (d.type === 'heart_rate')    show('bpm', d.data.bpm);
    if (d.type === 'speed_cadence') {                   // either key can be missing in one packet
      if (d.data.speedKmh !== undefined)   show('speed', d.data.speedKmh.toFixed(1) + ' km/h');
      if (d.data.cadenceRpm !== undefined) show('cadence', Math.round(d.data.cadenceRpm) + ' rpm');
    }
    if (d.type === 'power')   show('power', d.data.watts + ' W');
    if (d.type === 'running') show('pace', d.data.speedKmh.toFixed(1) + ' km/h, ' + d.data.cadenceSpm + ' spm');
    if (d.type === 'battery') show('battery', d.data.percent + '%');
  } else if (d.kind === 'error') {
    showStatus('Could not connect: ' + d.error);
  }
};

Lembre o sensor que conectou e reconecte a ele da próxima vez sem uma busca:

var SAVED_KEY = 'hrStrapAddress';

window.addEventListener('appmint:ble', function (e) {
  var d = e.detail;
  if (d.kind === 'state' && d.state === 'connected') localStorage.setItem(SAVED_KEY, d.address);
  if (d.kind === 'error' && d.error === 'invalid_address') localStorage.removeItem(SAVED_KEY);
});

document.getElementById('start-workout').addEventListener('click', function () {
  if (!window.WebToApk || typeof window.WebToApk.bleConnect !== 'function') return;
  var address = localStorage.getItem(SAVED_KEY);
  if (address) window.WebToApk.bleConnect(address);
  else window.WebToApk.bleStartScan();   // first time: let the user pick a sensor
});

Acompanhe uma sessão de frequência cardíaca (média, máxima, contato da cinta) a partir das leituras:

var beats = 0, sum = 0, max = 0;

window.addEventListener('appmint:ble', function (e) {
  var d = e.detail;
  if (d.kind === 'state' && d.state === 'ready' && d.services.length === 0) {
    showStatus('This sensor has no heart rate, speed, power or battery service.');
  }
  if (d.kind !== 'data' || d.type !== 'heart_rate') return;
  if (d.data.contact === false) {          // only sent by straps that report skin contact
    showStatus('Wet the strap: no skin contact');
    return;
  }
  beats++; sum += d.data.bpm; max = Math.max(max, d.data.bpm);
  show('bpm', d.data.bpm);
  show('avg', Math.round(sum / beats));
  show('max', max);
});

Observações: Um sensor por vez: conectar para qualquer busca e derruba antes o sensor anterior (você recebe o disconnected dele). ready.services lista os UUIDs completos dos serviços suportados que este sensor tem. O primeiro pacote de velocidade/cadência após conectar não envia nada - velocidade e cadência precisam de dois pacotes para serem calculadas. heart_rate também traz rrIntervalsMs (intervalos batimento a batimento em ms, quando a cinta os envia), energyKj e contact quando a cinta os informa; power traz cadenceRpm a partir do segundo pacote quando o medidor envia dados de pedivela. Erros: not_enabled, permission_denied, bluetooth_unavailable, invalid_address. Se o sensor sair do alcance você recebe {kind:'state', state:'disconnected', address}.

bleDisconnect ponte#

window.WebToApk.bleDisconnect()

Runtime para permBluetooth. Nenhuma opção do assistente o ativa: o build o arma quando o próprio código da página chama estes métodos (ou o Web Bluetooth). Antes deste runtime as permissões eram declaradas sem que nada no runtime tocasse um sensor, exatamente o padrão declarado-mas-morto que o SmsBridge documenta. Os resultados chegam como eventos `appmint:ble` / window.onAppMintBle({kind, ...}).

Descrito pelo seu grupo, Sensores de fitness BLE (frequência cardíaca / velocidade / cadência / potência / bateria), e não por conta própria.

Exemplo

Desconecta o sensor de fitness conectado com bleConnect().

Retorna: nada. Se um sensor estava conectado, um evento appmint:ble {kind:'state', state:'disconnected'} se segue. Requer: Bluetooth (ativado por um build de ZIP ou HTML quando seu código usa a ponte de sensores); sem ele a chamada não faz nada.

document.getElementById('end-workout').addEventListener('click', function () {
  if (window.WebToApk && typeof window.WebToApk.bleDisconnect === 'function') {
    window.WebToApk.bleDisconnect();
  }
});

window.addEventListener('appmint:ble', function (e) {
  if (e.detail.kind === 'state' && e.detail.state === 'disconnected') {
    showStatus('Sensor disconnected');
  }
});

Reconecte sozinho quando o sensor cair, mas não quando o usuário encerrou o treino:

var lastAddress = null, userEnded = false, retries = 0;

window.addEventListener('appmint:ble', function (e) {
  var d = e.detail;
  if (d.kind !== 'state') return;
  if (d.state === 'connected') { lastAddress = d.address; userEnded = false; retries = 0; }
  // Only a drop caused by the sensor carries an address.
  if (d.state === 'disconnected' && d.address && !userEnded && lastAddress && retries < 3) {
    retries++;
    showStatus('Sensor lost, reconnecting (' + retries + '/3)...');
    setTimeout(function () { window.WebToApk.bleConnect(lastAddress); }, 3000);
  }
});

function endWorkout() {
  userEnded = true;
  if (window.WebToApk && typeof window.WebToApk.bleDisconnect === 'function') {
    window.WebToApk.bleDisconnect();
  }
}

Desconecte e esqueça o sensor salvo a partir de uma tela de configurações:

document.getElementById('forget-sensor').addEventListener('click', function () {
  localStorage.removeItem('hrStrapAddress');   // wherever your app saved the address
  if (window.WebToApk && typeof window.WebToApk.bleDisconnect === 'function') {
    window.WebToApk.bleDisconnect();
  }
  showStatus('Sensor forgotten. Tap Scan to pair a new one.');
});

Observações: O evento disconnected que você mesmo causa não tem address; um causado pelo sensor (fora de alcance, desligado) o tem. O app também desconecta quando fecha.

bleSetWheelCircumference ponte#

window.WebToApk.bleSetWheelCircumference(mm: Int)

Runtime para permBluetooth. Nenhuma opção do assistente o ativa: o build o arma quando o próprio código da página chama estes métodos (ou o Web Bluetooth). Antes deste runtime as permissões eram declaradas sem que nada no runtime tocasse um sensor, exatamente o padrão declarado-mas-morto que o SmsBridge documenta. Os resultados chegam como eventos `appmint:ble` / window.onAppMintBle({kind, ...}).

Descrito pelo seu grupo, Sensores de fitness BLE (frequência cardíaca / velocidade / cadência / potência / bateria), e não por conta própria.

Exemplo

Define o tamanho da roda da bicicleta (circunferência em milímetros) usado para converter as voltas da roda de um sensor de velocidade em km/h.

Retorna: nada. Requer: nada para chamá-lo; as próprias leituras de velocidade precisam de um sensor de velocidade de ciclismo conectado (veja bleConnect).

// 2096 mm = 700x23c (the default). 2105 = 700x25c, 2136 = 700x28c, 2070 = 26x2.0 MTB.
function setWheel(mm) {
  if (window.WebToApk && typeof window.WebToApk.bleSetWheelCircumference === 'function') {
    window.WebToApk.bleSetWheelCircumference(mm);
  }
}

document.getElementById('wheel').addEventListener('change', function (e) {
  setWheel(parseInt(e.target.value, 10));
});

Aplique o tamanho de roda salvo do ciclista toda vez que conectar, já que a configuração não sobrevive a uma reinicialização:

function connectBike(address) {
  if (!window.WebToApk || typeof window.WebToApk.bleConnect !== 'function') return;
  var mm = parseInt(localStorage.getItem('wheelMm'), 10) || 2096;
  window.WebToApk.bleSetWheelCircumference(mm);
  window.WebToApk.bleConnect(address);
}

Calibre a partir de um rolamento medido (uma volta completa da roda, em cm) e verifique a faixa você mesmo, porque um valor fora da faixa é ignorado sem erro:

document.getElementById('save-rollout').addEventListener('click', function () {
  var mm = Math.round(parseFloat(document.getElementById('rollout-cm').value) * 10);
  if (!(mm >= 500 && mm <= 4000)) {
    showStatus('Enter a roll-out between 50 and 400 cm.');
    return;
  }
  localStorage.setItem('wheelMm', String(mm));
  if (window.WebToApk && typeof window.WebToApk.bleSetWheelCircumference === 'function') {
    window.WebToApk.bleSetWheelCircumference(mm);   // applies to the next speed reading
  }
  showStatus('Wheel set to ' + mm + ' mm');
});

Observações: Valores fora de 500-4000 mm são ignorados silenciosamente (o valor antigo permanece). A configuração dura até o app fechar, então defina-a de novo a cada inicialização, por exemplo logo antes de bleConnect(). Ela altera speedKmh nos dados speed_cadence de appmint:ble; a cadência não é afetada.

bleStartScan ponte#

window.WebToApk.bleStartScan()

Runtime para permBluetooth. Nenhuma opção do assistente o ativa: o build o arma quando o próprio código da página chama estes métodos (ou o Web Bluetooth). Antes deste runtime as permissões eram declaradas sem que nada no runtime tocasse um sensor, exatamente o padrão declarado-mas-morto que o SmsBridge documenta. Os resultados chegam como eventos `appmint:ble` / window.onAppMintBle({kind, ...}).

Descrito pelo seu grupo, Sensores de fitness BLE (frequência cardíaca / velocidade / cadência / potência / bateria), e não por conta própria.

Exemplo

Busca sensores de fitness Bluetooth próximos (frequência cardíaca, velocidade/cadência de ciclismo, potência, footpods de corrida) por 15 segundos.

Retorna: nada. Os resultados chegam como eventos appmint:ble (e window.onAppMintBle(detail)): um {kind:'device', address, name, sensors, rssi} por sensor encontrado, mais {kind:'state', state:'scanning'} no início e {kind:'state', state:'scan_stopped'} no fim. Requer: Bluetooth. Não há opção para isso: quando você compila a partir de um ZIP ou de uma página HTML, o build ativa o Bluetooth sozinho se seu código chama bleStartScan / bleConnect ou usa navigator.bluetooth. Um build de site (URL) não pode ativá-lo. A primeira busca pede ao usuário a permissão de Dispositivos próximos (Localização no Android 11 e anteriores).

Escute primeiro, depois faça a busca a partir do toque em um botão:

var found = {};

window.addEventListener('appmint:ble', function (e) {
  var d = e.detail;
  if (d.kind === 'device') {
    found[d.address] = d;
    // d.sensors: any of 'heart_rate', 'speed_cadence', 'power', 'running'
    addRow(d.address, (d.name || 'Unnamed sensor') + ' ' + d.sensors.join(', ') + ' (' + d.rssi + ' dBm)');
  } else if (d.kind === 'state' && d.state === 'scanning') {
    showStatus('Looking for sensors...');
  } else if (d.kind === 'state' && d.state === 'scan_stopped') {
    showStatus(Object.keys(found).length ? 'Pick a sensor' : 'No sensors found. Wake the sensor and try again.');
  } else if (d.kind === 'error') {
    showStatus('Bluetooth error: ' + d.error);
  }
});

document.getElementById('scan').addEventListener('click', function () {
  if (window.WebToApk && typeof window.WebToApk.bleStartScan === 'function') {
    found = {};
    window.WebToApk.bleStartScan();
  } else {
    showStatus('Sensors work in the installed app only.');
  }
});

Pareie automaticamente a cinta de frequência cardíaca mais forte quando a busca terminar:

var best = null, pairing = false;

window.addEventListener('appmint:ble', function (e) {
  var d = e.detail;
  if (!pairing) return;
  if (d.kind === 'device' && d.sensors.indexOf('heart_rate') !== -1) {
    if (!best || d.rssi > best.rssi) best = d;   // closer strap = higher (less negative) rssi
  } else if (d.kind === 'state' && d.state === 'scan_stopped') {
    pairing = false;
    if (best) window.WebToApk.bleConnect(best.address);
    else showStatus('No heart-rate strap found. Put it on and try again.');
  }
});

document.getElementById('pair-hr').addEventListener('click', function () {
  if (!window.WebToApk || typeof window.WebToApk.bleStartScan !== 'function') return;
  best = null;
  pairing = true;
  window.WebToApk.bleStartScan();
});

Transforme cada erro de busca em uma mensagem que o usuário possa seguir:

var SCAN_HELP = {
  not_enabled: 'This app was built without Bluetooth.',
  permission_denied: 'Allow Nearby devices for this app in Settings to find sensors.',
  bluetooth_unavailable: 'This phone has no Bluetooth.',
  bluetooth_off: 'Turn Bluetooth on, then tap Scan again.',
  scan_failed: 'The scan could not start. Wait a moment and try again.'
};

window.addEventListener('appmint:ble', function (e) {
  var d = e.detail;
  if (d.kind !== 'error') return;
  var text = SCAN_HELP[d.error] || ('Bluetooth error: ' + d.error);
  if (d.code !== undefined) text += ' (code ' + d.code + ')';
  showStatus(text);
  document.getElementById('scan').disabled = false;
});

Observações: Códigos de erro em {kind:'error', error}: not_enabled (o build não tem Bluetooth), permission_denied, bluetooth_unavailable (sem adaptador), bluetooth_off, scan_failed (pode trazer o code do Android). A busca só reporta dispositivos que anunciam um dos quatro serviços de fitness, e cada dispositivo apenas uma vez por busca. Iniciar uma nova busca para a antiga. Use o listener appmint:ble ou window.onAppMintBle, não os dois - ambos são chamados para cada mensagem.

bleStopScan ponte#

window.WebToApk.bleStopScan()

Runtime para permBluetooth. Nenhuma opção do assistente o ativa: o build o arma quando o próprio código da página chama estes métodos (ou o Web Bluetooth). Antes deste runtime as permissões eram declaradas sem que nada no runtime tocasse um sensor, exatamente o padrão declarado-mas-morto que o SmsBridge documenta. Os resultados chegam como eventos `appmint:ble` / window.onAppMintBle({kind, ...}).

Descrito pelo seu grupo, Sensores de fitness BLE (frequência cardíaca / velocidade / cadência / potência / bateria), e não por conta própria.

Exemplo

Para uma busca de sensores iniciada com bleStartScan() antes de seus 15 segundos acabarem.

Retorna: nada. Se uma busca estava em andamento, um evento appmint:ble {kind:'state', state:'scan_stopped'} se segue. Requer: Bluetooth (ativado por um build de ZIP ou HTML quando seu código usa a ponte de sensores); sem ele a chamada não faz nada.

window.addEventListener('appmint:ble', function (e) {
  if (e.detail.kind === 'state' && e.detail.state === 'scan_stopped') {
    document.getElementById('scan').disabled = false;
  }
});

document.getElementById('stop-scan').addEventListener('click', function () {
  if (window.WebToApk && typeof window.WebToApk.bleStopScan === 'function') {
    window.WebToApk.bleStopScan();
  }
});

Pare mais cedo assim que o tipo de sensor de que você precisa aparecer:

window.addEventListener('appmint:ble', function (e) {
  var d = e.detail;
  if (d.kind === 'device' && d.sensors.indexOf('power') !== -1) {
    if (window.WebToApk && typeof window.WebToApk.bleStopScan === 'function') {
      window.WebToApk.bleStopScan();   // no need to wait the full 15 seconds
    }
    showPowerMeter(d.address, d.name || 'Power meter');
  }
});

Pare a busca quando o usuário fechar a folha de pareamento ou sair do app:

function stopLooking() {
  if (window.WebToApk && typeof window.WebToApk.bleStopScan === 'function') {
    window.WebToApk.bleStopScan();   // safe even if the scan already ended
  }
}

document.getElementById('close-pairing').addEventListener('click', function () {
  stopLooking();
  document.getElementById('pairing-sheet').hidden = true;
});

document.addEventListener('visibilitychange', function () {
  if (document.hidden) stopLooking();
});

Observações: Você raramente precisa disto: a busca para sozinha após 15 segundos, e bleConnect() também a para. Chamar quando nenhuma busca está em andamento é seguro e não envia evento.

Gerado a partir do runtime do app e de seus arquivos de exemplo a cada build da documentação. Leia em Markdown · Todas as famílias.

Conferido com a ponte atual em 22/10/2026.