API del puente de JavaScript
Sensores de fitness BLE
Frecuencia cardíaca, velocidad, cadencia, potencia y batería desde perfiles estándar de Bluetooth Low Energy.
AppMint.beacons ayudante#
AppMint.beacons.startScan(options) / stopScan() / isScanning()
Ejemplo
Encuentra beacons Bluetooth cercanos (iBeacon y Eddystone: UID, URL y TLM) y avisa a la página de cada uno aproximadamente una vez por segundo mediante el evento appmint:beacon, con la intensidad de la señal y una distancia aproximada.
Devuelve: startScan() → una Promesa de {ok: true} o {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked). stopScan() → {ok: true}. isScanning() → true/false. Requiere: nada que marcar: una compilación cuyo código llama a beacons.startScan declara los permisos de escaneo Bluetooth y de ubicación precisa (para Android los resultados de beacons son datos de ubicación, así que se piden ambos al usuario y Play lista la app como que usa ubicación).
Mide los beacons de un mismo lugar:
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);
}Solo Eddystone, con telemetría de batería:
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');
});Detén al salir de la pantalla:
document.addEventListener('visibilitychange', async function () {
if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});Campos del evento: todo beacon tiene type, rssi, address, timestamp; iBeacon añade uuid, major, minor, txPower, distance; Eddystone UID añade namespace, instance, txPower, distance; URL añade url; TLM añade batteryMilliVolts, temperatureC, advertCount, uptimeSeconds. uuids filtra los UUID de iBeacon y los namespaces de Eddystone (hexadecimal en minúsculas).
Notas: La distancia se calcula a partir de la intensidad de la señal y solo sirve para cerca / lejos: las paredes y los cuerpos la cambian en metros. El escaneo continúa hasta stopScan() o hasta que se cierre la app.
beaconIsScanning puente#
window.WebToApk.beaconIsScanning(): Boolean
Si hay un escaneo de beacons en marcha.
Ejemplo
Documentado junto con AppMint.beacons: el ejemplo de allí también muestra este.
Encuentra beacons Bluetooth cercanos (iBeacon y Eddystone: UID, URL y TLM) y avisa a la página de cada uno aproximadamente una vez por segundo mediante el evento appmint:beacon, con la intensidad de la señal y una distancia aproximada.
Devuelve: startScan() → una Promesa de {ok: true} o {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked). stopScan() → {ok: true}. isScanning() → true/false. Requiere: nada que marcar: una compilación cuyo código llama a beacons.startScan declara los permisos de escaneo Bluetooth y de ubicación precisa (para Android los resultados de beacons son datos de ubicación, así que se piden ambos al usuario y Play lista la app como que usa ubicación).
Mide los beacons de un mismo lugar:
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);
}Solo Eddystone, con telemetría de batería:
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');
});Detén al salir de la pantalla:
document.addEventListener('visibilitychange', async function () {
if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});Campos del evento: todo beacon tiene type, rssi, address, timestamp; iBeacon añade uuid, major, minor, txPower, distance; Eddystone UID añade namespace, instance, txPower, distance; URL añade url; TLM añade batteryMilliVolts, temperatureC, advertCount, uptimeSeconds. uuids filtra los UUID de iBeacon y los namespaces de Eddystone (hexadecimal en minúsculas).
Notas: La distancia se calcula a partir de la intensidad de la señal y solo sirve para cerca / lejos: las paredes y los cuerpos la cambian en metros. El escaneo continúa hasta stopScan() o hasta que se cierre la app.
beaconStartScan puente#
window.WebToApk.beaconStartScan(optionsJson: String, callbackId: String)
Inicia la medición de beacons con [optionsJson] `{uuids?, ibeacon?, eddystone?}`; resuelve [callbackId] con `{ok}` o `{ok:false, code}`.
Ejemplo
Documentado junto con AppMint.beacons: el ejemplo de allí también muestra este.
Encuentra beacons Bluetooth cercanos (iBeacon y Eddystone: UID, URL y TLM) y avisa a la página de cada uno aproximadamente una vez por segundo mediante el evento appmint:beacon, con la intensidad de la señal y una distancia aproximada.
Devuelve: startScan() → una Promesa de {ok: true} o {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked). stopScan() → {ok: true}. isScanning() → true/false. Requiere: nada que marcar: una compilación cuyo código llama a beacons.startScan declara los permisos de escaneo Bluetooth y de ubicación precisa (para Android los resultados de beacons son datos de ubicación, así que se piden ambos al usuario y Play lista la app como que usa ubicación).
Mide los beacons de un mismo lugar:
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);
}Solo Eddystone, con telemetría de batería:
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');
});Detén al salir de la pantalla:
document.addEventListener('visibilitychange', async function () {
if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});Campos del evento: todo beacon tiene type, rssi, address, timestamp; iBeacon añade uuid, major, minor, txPower, distance; Eddystone UID añade namespace, instance, txPower, distance; URL añade url; TLM añade batteryMilliVolts, temperatureC, advertCount, uptimeSeconds. uuids filtra los UUID de iBeacon y los namespaces de Eddystone (hexadecimal en minúsculas).
Notas: La distancia se calcula a partir de la intensidad de la señal y solo sirve para cerca / lejos: las paredes y los cuerpos la cambian en metros. El escaneo continúa hasta stopScan() o hasta que se cierre la app.
beaconStopScan puente#
window.WebToApk.beaconStopScan(callbackId: String)
Detiene la medición de beacons; resuelve [callbackId] con `{ok:true}`.
Ejemplo
Documentado junto con AppMint.beacons: el ejemplo de allí también muestra este.
Encuentra beacons Bluetooth cercanos (iBeacon y Eddystone: UID, URL y TLM) y avisa a la página de cada uno aproximadamente una vez por segundo mediante el evento appmint:beacon, con la intensidad de la señal y una distancia aproximada.
Devuelve: startScan() → una Promesa de {ok: true} o {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked). stopScan() → {ok: true}. isScanning() → true/false. Requiere: nada que marcar: una compilación cuyo código llama a beacons.startScan declara los permisos de escaneo Bluetooth y de ubicación precisa (para Android los resultados de beacons son datos de ubicación, así que se piden ambos al usuario y Play lista la app como que usa ubicación).
Mide los beacons de un mismo lugar:
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);
}Solo Eddystone, con telemetría de batería:
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');
});Detén al salir de la pantalla:
document.addEventListener('visibilitychange', async function () {
if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});Campos del evento: todo beacon tiene type, rssi, address, timestamp; iBeacon añade uuid, major, minor, txPower, distance; Eddystone UID añade namespace, instance, txPower, distance; URL añade url; TLM añade batteryMilliVolts, temperatureC, advertCount, uptimeSeconds. uuids filtra los UUID de iBeacon y los namespaces de Eddystone (hexadecimal en minúsculas).
Notas: La distancia se calcula a partir de la intensidad de la señal y solo sirve para cerca / lejos: las paredes y los cuerpos la cambian en metros. El escaneo continúa hasta stopScan() o hasta que se cierre la app.
bleConnect puente#
window.WebToApk.bleConnect(address: String)
Runtime de permBluetooth. Ningún interruptor del asistente lo activa: la compilación lo activa cuando el propio código de la página llama a estos métodos (o a Web Bluetooth). Antes de este runtime los permisos se declaraban sin que nada en el runtime tocara un sensor, el mismo patrón de declarado-pero-inactivo que documenta SmsBridge. Los resultados llegan como eventos `appmint:ble` / window.onAppMintBle({kind, ...}).
Descrito por su grupo, Sensores de fitness BLE (frecuencia cardíaca / velocidad / cadencia / potencia / batería), en lugar de por separado.
Ejemplo
Se conecta a un sensor de fitness encontrado con bleStartScan() y empieza a transmitir sus lecturas.
Devuelve: nada. El progreso y los datos llegan como eventos appmint:ble (y window.onAppMintBle): {kind:'state', state:'connecting', address}, {kind:'state', state:'connected', address}, {kind:'state', state:'ready', services:[...]}, y luego {kind:'data', type, data} por cada lectura. Requiere: Bluetooth (activado por una compilación ZIP o HTML cuando tu código usa el puente de sensores) y el permiso de Dispositivos cercanos, que se pide en el primer uso.
Pasa la address de un evento device, desde el toque del usuario:
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);
}
};Recuerda el sensor que se conectó y vuelve a conectarte a él la próxima vez sin escanear:
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
});Sigue una sesión de frecuencia cardíaca (promedio, máximo, contacto de la banda) a partir de las lecturas:
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);
});Notas: Un sensor a la vez: conectar detiene cualquier escaneo y desconecta primero el sensor anterior (recibes su disconnected). ready.services lista los UUID completos de los servicios compatibles que tiene este sensor. El primer paquete de velocidad/cadencia tras conectar no envía nada: la velocidad y la cadencia necesitan dos paquetes para calcularse. heart_rate también incluye rrIntervalsMs (intervalos latido a latido en ms, cuando la banda los envía), energyKj y contact cuando la banda los informa; power incluye cadenceRpm a partir de su segundo paquete cuando el medidor envía datos de biela. Errores: not_enabled, permission_denied, bluetooth_unavailable, invalid_address. Si el sensor sale del alcance recibes {kind:'state', state:'disconnected', address}.
bleDisconnect puente#
window.WebToApk.bleDisconnect()
Runtime de permBluetooth. Ningún interruptor del asistente lo activa: la compilación lo activa cuando el propio código de la página llama a estos métodos (o a Web Bluetooth). Antes de este runtime los permisos se declaraban sin que nada en el runtime tocara un sensor, el mismo patrón de declarado-pero-inactivo que documenta SmsBridge. Los resultados llegan como eventos `appmint:ble` / window.onAppMintBle({kind, ...}).
Descrito por su grupo, Sensores de fitness BLE (frecuencia cardíaca / velocidad / cadencia / potencia / batería), en lugar de por separado.
Ejemplo
Desconecta el sensor de fitness conectado con bleConnect().
Devuelve: nada. Si había un sensor conectado, sigue un evento appmint:ble {kind:'state', state:'disconnected'}. Requiere: Bluetooth (activado por una compilación ZIP o HTML cuando tu código usa el puente de sensores); sin él la llamada no hace 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');
}
});Vuelve a conectarte solo cuando el sensor se desconecte, pero no cuando el usuario haya terminado el entrenamiento:
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();
}
}Desconecta y olvida el sensor guardado desde una pantalla de ajustes:
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.');
});Notas: El evento disconnected que provocas tú mismo no tiene address; el que provoca el sensor (fuera de alcance, apagado) sí lo tiene. La app también se desconecta cuando se cierra.
bleSetWheelCircumference puente#
window.WebToApk.bleSetWheelCircumference(mm: Int)
Runtime de permBluetooth. Ningún interruptor del asistente lo activa: la compilación lo activa cuando el propio código de la página llama a estos métodos (o a Web Bluetooth). Antes de este runtime los permisos se declaraban sin que nada en el runtime tocara un sensor, el mismo patrón de declarado-pero-inactivo que documenta SmsBridge. Los resultados llegan como eventos `appmint:ble` / window.onAppMintBle({kind, ...}).
Descrito por su grupo, Sensores de fitness BLE (frecuencia cardíaca / velocidad / cadencia / potencia / batería), en lugar de por separado.
Ejemplo
Establece el tamaño de la rueda de la bicicleta (circunferencia en milímetros) que se usa para convertir las vueltas de rueda de un sensor de velocidad en km/h.
Devuelve: nada. Requiere: nada para llamarlo; las lecturas de velocidad en sí necesitan un sensor de velocidad de ciclismo conectado (ver 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));
});Aplica el tamaño de rueda guardado del ciclista cada vez que te conectes, ya que el ajuste no sobrevive a un reinicio:
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);
}Calibra a partir de una medición de avance (una vuelta completa de rueda, en cm) y comprueba tú mismo el rango, porque un valor fuera de rango se ignora sin error:
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');
});Notas: Los valores fuera de 500-4000 mm se ignoran en silencio (se conserva el valor anterior). El ajuste dura hasta que se cierra la app, así que vuelve a establecerlo en cada inicio, por ejemplo justo antes de bleConnect(). Cambia speedKmh en los datos speed_cadence de appmint:ble; la cadencia no se ve afectada.
bleStartScan puente#
window.WebToApk.bleStartScan()
Runtime de permBluetooth. Ningún interruptor del asistente lo activa: la compilación lo activa cuando el propio código de la página llama a estos métodos (o a Web Bluetooth). Antes de este runtime los permisos se declaraban sin que nada en el runtime tocara un sensor, el mismo patrón de declarado-pero-inactivo que documenta SmsBridge. Los resultados llegan como eventos `appmint:ble` / window.onAppMintBle({kind, ...}).
Descrito por su grupo, Sensores de fitness BLE (frecuencia cardíaca / velocidad / cadencia / potencia / batería), en lugar de por separado.
Ejemplo
Escanea sensores de fitness Bluetooth cercanos (frecuencia cardíaca, velocidad/cadencia de ciclismo, potencia, sensores de carrera) durante 15 segundos.
Devuelve: nada. Los resultados llegan como eventos appmint:ble (y window.onAppMintBle(detail)): un {kind:'device', address, name, sensors, rssi} por cada sensor encontrado, más {kind:'state', state:'scanning'} al inicio y {kind:'state', state:'scan_stopped'} al final. Requiere: Bluetooth. No hay interruptor para ello: cuando compilas desde un ZIP o una página HTML, la compilación activa Bluetooth por sí sola si tu código llama a bleStartScan / bleConnect o usa navigator.bluetooth. Una compilación de sitio web (URL) no puede activarlo. El primer escaneo pide al usuario el permiso de Dispositivos cercanos (Ubicación en Android 11 y anteriores).
Escucha primero y luego escanea desde el toque de un botón:
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.');
}
});Empareja automáticamente la banda de frecuencia cardíaca más fuerte cuando termine el escaneo:
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();
});Convierte cada error de escaneo en un mensaje que el usuario pueda atender:
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;
});Notas: Códigos de error en {kind:'error', error}: not_enabled (la compilación no tiene Bluetooth), permission_denied, bluetooth_unavailable (sin adaptador), bluetooth_off, scan_failed (puede incluir el code de Android). El escaneo solo informa de dispositivos que anuncian uno de los cuatro servicios de fitness, y cada dispositivo una sola vez por escaneo. Iniciar un escaneo nuevo detiene el anterior. Usa el listener appmint:ble o window.onAppMintBle, no ambos: los dos se llaman por cada mensaje.
bleStopScan puente#
window.WebToApk.bleStopScan()
Runtime de permBluetooth. Ningún interruptor del asistente lo activa: la compilación lo activa cuando el propio código de la página llama a estos métodos (o a Web Bluetooth). Antes de este runtime los permisos se declaraban sin que nada en el runtime tocara un sensor, el mismo patrón de declarado-pero-inactivo que documenta SmsBridge. Los resultados llegan como eventos `appmint:ble` / window.onAppMintBle({kind, ...}).
Descrito por su grupo, Sensores de fitness BLE (frecuencia cardíaca / velocidad / cadencia / potencia / batería), en lugar de por separado.
Ejemplo
Detiene un escaneo de sensores iniciado con bleStartScan() antes de que pasen sus 15 segundos.
Devuelve: nada. Si había un escaneo en marcha, sigue un evento appmint:ble {kind:'state', state:'scan_stopped'}. Requiere: Bluetooth (activado por una compilación ZIP o HTML cuando tu código usa el puente de sensores); sin él la llamada no hace 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();
}
});Detente antes en cuanto aparezca el tipo de sensor que necesitas:
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');
}
});Deja de escanear cuando el usuario cierre la hoja de emparejamiento o salga de la 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();
});Notas: Rara vez lo necesitas: el escaneo se detiene solo a los 15 segundos, y bleConnect() también lo detiene. Llamarlo cuando no hay un escaneo en marcha es seguro y no envía ningún evento.