JavaScript bridge API
BLE fitness sensors
Heart rate, speed, cadence, power and battery from standard Bluetooth Low Energy profiles.
AppMint.beacons helper#
AppMint.beacons.startScan(options) / stopScan() / isScanning()
Example
Finds Bluetooth beacons nearby - iBeacon and Eddystone (UID, URL and TLM) - and tells the page about each one about once a second through the appmint:beacon event, with the signal strength and a rough distance.
Returns: startScan() β a Promise of {ok: true} or {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked). stopScan() β {ok: true}. isScanning() β true/false. Needs: nothing to tick - a build whose code calls beacons.startScan declares the Bluetooth scan and precise-location permissions (beacon results are location data to Android, so the user is asked for both, and Play lists the app as using location).
Range the beacons of one venue:
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);
}Eddystone only, with battery telemetry:
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');
});Stop when the screen is left:
document.addEventListener('visibilitychange', async function () {
if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});Event fields: every beacon has type, rssi, address, timestamp; iBeacon adds uuid, major, minor, txPower, distance; Eddystone UID adds namespace, instance, txPower, distance; URL adds url; TLM adds batteryMilliVolts, temperatureC, advertCount, uptimeSeconds. uuids filters iBeacon UUIDs and Eddystone namespaces (lower-case hex).
Notes: Distance comes from the signal strength and is only good for near / far - walls and bodies change it by metres. The scan runs until stopScan() or the app closes.
beaconIsScanning bridge#
window.WebToApk.beaconIsScanning(): Boolean
Whether a beacon scan is running.
Example
Documented together with AppMint.beacons - the example there shows this one too.
Finds Bluetooth beacons nearby - iBeacon and Eddystone (UID, URL and TLM) - and tells the page about each one about once a second through the appmint:beacon event, with the signal strength and a rough distance.
Returns: startScan() β a Promise of {ok: true} or {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked). stopScan() β {ok: true}. isScanning() β true/false. Needs: nothing to tick - a build whose code calls beacons.startScan declares the Bluetooth scan and precise-location permissions (beacon results are location data to Android, so the user is asked for both, and Play lists the app as using location).
Range the beacons of one venue:
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);
}Eddystone only, with battery telemetry:
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');
});Stop when the screen is left:
document.addEventListener('visibilitychange', async function () {
if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});Event fields: every beacon has type, rssi, address, timestamp; iBeacon adds uuid, major, minor, txPower, distance; Eddystone UID adds namespace, instance, txPower, distance; URL adds url; TLM adds batteryMilliVolts, temperatureC, advertCount, uptimeSeconds. uuids filters iBeacon UUIDs and Eddystone namespaces (lower-case hex).
Notes: Distance comes from the signal strength and is only good for near / far - walls and bodies change it by metres. The scan runs until stopScan() or the app closes.
beaconStartScan bridge#
window.WebToApk.beaconStartScan(optionsJson: String, callbackId: String)
Starts beacon ranging with [optionsJson] `{uuids?, ibeacon?, eddystone?}`; resolves [callbackId] with `{ok}` or `{ok:false, code}`.
Example
Documented together with AppMint.beacons - the example there shows this one too.
Finds Bluetooth beacons nearby - iBeacon and Eddystone (UID, URL and TLM) - and tells the page about each one about once a second through the appmint:beacon event, with the signal strength and a rough distance.
Returns: startScan() β a Promise of {ok: true} or {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked). stopScan() β {ok: true}. isScanning() β true/false. Needs: nothing to tick - a build whose code calls beacons.startScan declares the Bluetooth scan and precise-location permissions (beacon results are location data to Android, so the user is asked for both, and Play lists the app as using location).
Range the beacons of one venue:
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);
}Eddystone only, with battery telemetry:
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');
});Stop when the screen is left:
document.addEventListener('visibilitychange', async function () {
if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});Event fields: every beacon has type, rssi, address, timestamp; iBeacon adds uuid, major, minor, txPower, distance; Eddystone UID adds namespace, instance, txPower, distance; URL adds url; TLM adds batteryMilliVolts, temperatureC, advertCount, uptimeSeconds. uuids filters iBeacon UUIDs and Eddystone namespaces (lower-case hex).
Notes: Distance comes from the signal strength and is only good for near / far - walls and bodies change it by metres. The scan runs until stopScan() or the app closes.
beaconStopScan bridge#
window.WebToApk.beaconStopScan(callbackId: String)
Stops beacon ranging; resolves [callbackId] with `{ok:true}`.
Example
Documented together with AppMint.beacons - the example there shows this one too.
Finds Bluetooth beacons nearby - iBeacon and Eddystone (UID, URL and TLM) - and tells the page about each one about once a second through the appmint:beacon event, with the signal strength and a rough distance.
Returns: startScan() β a Promise of {ok: true} or {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked). stopScan() β {ok: true}. isScanning() β true/false. Needs: nothing to tick - a build whose code calls beacons.startScan declares the Bluetooth scan and precise-location permissions (beacon results are location data to Android, so the user is asked for both, and Play lists the app as using location).
Range the beacons of one venue:
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);
}Eddystone only, with battery telemetry:
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');
});Stop when the screen is left:
document.addEventListener('visibilitychange', async function () {
if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});Event fields: every beacon has type, rssi, address, timestamp; iBeacon adds uuid, major, minor, txPower, distance; Eddystone UID adds namespace, instance, txPower, distance; URL adds url; TLM adds batteryMilliVolts, temperatureC, advertCount, uptimeSeconds. uuids filters iBeacon UUIDs and Eddystone namespaces (lower-case hex).
Notes: Distance comes from the signal strength and is only good for near / far - walls and bodies change it by metres. The scan runs until stopScan() or the app closes.
bleConnect bridge#
window.WebToApk.bleConnect(address: String)
Runtime for permBluetooth. No wizard switch sets it: the build arms it when the page's own code calls these methods (or Web Bluetooth). Before this runtime the permissions were declared while nothing in the runtime touched a sensor, the exact declared-but-dead pattern SmsBridge documents. Results stream as `appmint:ble` events / window.onAppMintBle({kind, ...}).
Described by its group, BLE fitness sensors (heart rate / speed / cadence / power / battery), rather than on its own.
Example
Connects to one fitness sensor found by bleStartScan() and starts streaming its readings.
Returns: nothing. Progress and data arrive as appmint:ble events (and window.onAppMintBle): {kind:'state', state:'connecting', address}, {kind:'state', state:'connected', address}, {kind:'state', state:'ready', services:[...]}, then {kind:'data', type, data} for every reading. Needs: Bluetooth (turned on by a ZIP or HTML build when your code uses the sensor bridge) and the Nearby devices permission, asked on first use.
Pass the address from a device event, from the user's tap:
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);
}
};Remember the sensor that connected and reconnect to it next time without a scan:
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
});Track a heart-rate session (average, max, strap contact) from the readings:
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);
});Notes: One sensor at a time: connecting stops any scan and drops the previous sensor first (you get its disconnected). ready.services lists the full UUIDs of the supported services this sensor has. The first speed/cadence packet after connecting sends nothing - speed and cadence need two packets to compute. heart_rate also carries rrIntervalsMs (beat-to-beat intervals in ms, when the strap sends them), energyKj and contact when the strap reports them; power carries cadenceRpm from its second packet on when the meter sends crank data. Errors: not_enabled, permission_denied, bluetooth_unavailable, invalid_address. If the sensor walks out of range you get {kind:'state', state:'disconnected', address}.
bleDisconnect bridge#
window.WebToApk.bleDisconnect()
Runtime for permBluetooth. No wizard switch sets it: the build arms it when the page's own code calls these methods (or Web Bluetooth). Before this runtime the permissions were declared while nothing in the runtime touched a sensor, the exact declared-but-dead pattern SmsBridge documents. Results stream as `appmint:ble` events / window.onAppMintBle({kind, ...}).
Described by its group, BLE fitness sensors (heart rate / speed / cadence / power / battery), rather than on its own.
Example
Disconnects the fitness sensor connected with bleConnect().
Returns: nothing. If a sensor was connected, an appmint:ble event {kind:'state', state:'disconnected'} follows. Needs: Bluetooth (turned on by a ZIP or HTML build when your code uses the sensor bridge); without it the call does nothing.
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');
}
});Reconnect by itself when the sensor drops out, but not when the user ended the workout:
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();
}
}Disconnect and forget the saved sensor from a settings screen:
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.');
});Notes: The disconnected event you cause yourself has no address; one caused by the sensor (out of range, switched off) has it. The app also disconnects when it closes.
bleSetWheelCircumference bridge#
window.WebToApk.bleSetWheelCircumference(mm: Int)
Runtime for permBluetooth. No wizard switch sets it: the build arms it when the page's own code calls these methods (or Web Bluetooth). Before this runtime the permissions were declared while nothing in the runtime touched a sensor, the exact declared-but-dead pattern SmsBridge documents. Results stream as `appmint:ble` events / window.onAppMintBle({kind, ...}).
Described by its group, BLE fitness sensors (heart rate / speed / cadence / power / battery), rather than on its own.
Example
Sets the bike wheel size (circumference in millimetres) used to turn a speed sensor's wheel turns into km/h.
Returns: nothing. Needs: nothing to call it; the speed readings themselves need a connected cycling speed sensor (see 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));
});Apply the rider's saved wheel size every time you connect, since the setting does not survive a restart:
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);
}Calibrate from a measured roll-out (one full wheel turn, in cm) and check the range yourself, because an out-of-range value is ignored without an 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');
});Notes: Values outside 500-4000 mm are ignored silently (the old value stays). The setting lasts until the app closes, so set it again after each launch, for example right before bleConnect(). It changes speedKmh in the speed_cadence data of appmint:ble; cadence is not affected.
bleStartScan bridge#
window.WebToApk.bleStartScan()
Runtime for permBluetooth. No wizard switch sets it: the build arms it when the page's own code calls these methods (or Web Bluetooth). Before this runtime the permissions were declared while nothing in the runtime touched a sensor, the exact declared-but-dead pattern SmsBridge documents. Results stream as `appmint:ble` events / window.onAppMintBle({kind, ...}).
Described by its group, BLE fitness sensors (heart rate / speed / cadence / power / battery), rather than on its own.
Example
Scans for nearby Bluetooth fitness sensors (heart rate, cycling speed/cadence, power, running footpods) for 15 seconds.
Returns: nothing. Results arrive as appmint:ble events (and window.onAppMintBle(detail)): one {kind:'device', address, name, sensors, rssi} per sensor found, plus {kind:'state', state:'scanning'} at the start and {kind:'state', state:'scan_stopped'} at the end. Needs: Bluetooth. There is no switch for it: when you build from a ZIP or an HTML page, the build turns Bluetooth on by itself if your code calls bleStartScan / bleConnect or uses navigator.bluetooth. A website (URL) build cannot turn it on. The first scan asks the user for the Nearby devices permission (Location on Android 11 and older).
Listen first, then scan from a button tap:
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.');
}
});Pair the strongest heart-rate strap automatically when the scan ends:
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();
});Turn each scan error into a message the user can act on:
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;
});Notes: Error codes in {kind:'error', error}: not_enabled (the build has no Bluetooth), permission_denied, bluetooth_unavailable (no adapter), bluetooth_off, scan_failed (may carry the Android code). The scan only reports devices that advertise one of the four fitness services, and each device only once per scan. Starting a new scan stops the old one. Use either the appmint:ble listener or window.onAppMintBle, not both - both are called for every message.
bleStopScan bridge#
window.WebToApk.bleStopScan()
Runtime for permBluetooth. No wizard switch sets it: the build arms it when the page's own code calls these methods (or Web Bluetooth). Before this runtime the permissions were declared while nothing in the runtime touched a sensor, the exact declared-but-dead pattern SmsBridge documents. Results stream as `appmint:ble` events / window.onAppMintBle({kind, ...}).
Described by its group, BLE fitness sensors (heart rate / speed / cadence / power / battery), rather than on its own.
Example
Stops a sensor scan started with bleStartScan() before its 15 seconds are up.
Returns: nothing. If a scan was running, an appmint:ble event {kind:'state', state:'scan_stopped'} follows. Needs: Bluetooth (turned on by a ZIP or HTML build when your code uses the sensor bridge); without it the call does nothing.
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();
}
});Stop early as soon as the kind of sensor you need shows up:
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');
}
});Stop scanning when the user closes the pairing sheet or leaves the 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();
});Notes: You rarely need this: the scan stops by itself after 15 seconds, and bleConnect() stops it too. Calling it when no scan is running is safe and sends no event.