JavaScript ब्रिज API
BLE फ़िटनेस सेंसर
स्टैंडर्ड Bluetooth Low Energy प्रोफ़ाइल से हार्ट रेट, स्पीड, कैडेंस, पावर और बैटरी।
AppMint.beacons हेल्पर#
AppMint.beacons.startScan(options) / stopScan() / isScanning()
उदाहरण
आस-पास के Bluetooth बीकन खोजता है - iBeacon और Eddystone (UID, URL और TLM) - और हर बीकन के बारे में लगभग हर सेकंड appmint:beacon इवेंट के ज़रिए पेज को बताता है, साथ में सिग्नल की ताक़त और अंदाज़न दूरी भी देता है।
लौटाता है: startScan() → {ok: true} या {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked) का Promise। stopScan() → {ok: true}। isScanning() → true/false। ज़रूरत: टिक करने के लिए कुछ नहीं - जिस बिल्ड के कोड में beacons.startScan कॉल होता है, उसमें Bluetooth स्कैन और सटीक लोकेशन की अनुमतियाँ जुड़ जाती हैं (Android के लिए बीकन के नतीजे लोकेशन डेटा हैं, इसलिए यूज़र से दोनों माँगी जाती हैं, और Play ऐप को लोकेशन इस्तेमाल करने वाला दिखाता है)।
किसी एक जगह के बीकन की रेंज देखें:
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, बैटरी टेलीमेट्री के साथ:
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');
});स्क्रीन छोड़ने पर रोकें:
document.addEventListener('visibilitychange', async function () {
if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});इवेंट के फ़ील्ड: हर बीकन में type, rssi, address, timestamp होते हैं; iBeacon में uuid, major, minor, txPower, distance जुड़ते हैं; Eddystone UID में namespace, instance, txPower, distance; URL में url; TLM में batteryMilliVolts, temperatureC, advertCount, uptimeSeconds। uuids iBeacon UUID और Eddystone नेमस्पेस को फ़िल्टर करता है (छोटे अक्षरों में hex)।
ध्यान दें: दूरी सिग्नल की ताकत से आती है और सिर्फ़ पास / दूर के लिए ठीक है - दीवारें और शरीर इसे कई मीटर बदल देते हैं। स्कैन stopScan() या ऐप बंद होने तक चलता है।
beaconIsScanning ब्रिज#
window.WebToApk.beaconIsScanning(): Boolean
क्या बीकन स्कैन चल रहा है।
उदाहरण
AppMint.beacons के साथ दस्तावेज़ित - वहाँ का उदाहरण इसे भी दिखाता है।
आस-पास के Bluetooth बीकन खोजता है - iBeacon और Eddystone (UID, URL और TLM) - और हर बीकन के बारे में लगभग हर सेकंड appmint:beacon इवेंट के ज़रिए पेज को बताता है, साथ में सिग्नल की ताक़त और अंदाज़न दूरी भी देता है।
लौटाता है: startScan() → {ok: true} या {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked) का Promise। stopScan() → {ok: true}। isScanning() → true/false। ज़रूरत: टिक करने के लिए कुछ नहीं - जिस बिल्ड के कोड में beacons.startScan कॉल होता है, उसमें Bluetooth स्कैन और सटीक लोकेशन की अनुमतियाँ जुड़ जाती हैं (Android के लिए बीकन के नतीजे लोकेशन डेटा हैं, इसलिए यूज़र से दोनों माँगी जाती हैं, और Play ऐप को लोकेशन इस्तेमाल करने वाला दिखाता है)।
किसी एक जगह के बीकन की रेंज देखें:
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, बैटरी टेलीमेट्री के साथ:
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');
});स्क्रीन छोड़ने पर रोकें:
document.addEventListener('visibilitychange', async function () {
if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});इवेंट के फ़ील्ड: हर बीकन में type, rssi, address, timestamp होते हैं; iBeacon में uuid, major, minor, txPower, distance जुड़ते हैं; Eddystone UID में namespace, instance, txPower, distance; URL में url; TLM में batteryMilliVolts, temperatureC, advertCount, uptimeSeconds। uuids iBeacon UUID और Eddystone नेमस्पेस को फ़िल्टर करता है (छोटे अक्षरों में hex)।
ध्यान दें: दूरी सिग्नल की ताकत से आती है और सिर्फ़ पास / दूर के लिए ठीक है - दीवारें और शरीर इसे कई मीटर बदल देते हैं। स्कैन stopScan() या ऐप बंद होने तक चलता है।
beaconStartScan ब्रिज#
window.WebToApk.beaconStartScan(optionsJson: String, callbackId: String)
[optionsJson] `{uuids?, ibeacon?, eddystone?}` के साथ बीकन रेंजिंग शुरू करता है; [callbackId] को `{ok}` या `{ok:false, code}` के साथ पूरा करता है।
उदाहरण
AppMint.beacons के साथ दस्तावेज़ित - वहाँ का उदाहरण इसे भी दिखाता है।
आस-पास के Bluetooth बीकन खोजता है - iBeacon और Eddystone (UID, URL और TLM) - और हर बीकन के बारे में लगभग हर सेकंड appmint:beacon इवेंट के ज़रिए पेज को बताता है, साथ में सिग्नल की ताक़त और अंदाज़न दूरी भी देता है।
लौटाता है: startScan() → {ok: true} या {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked) का Promise। stopScan() → {ok: true}। isScanning() → true/false। ज़रूरत: टिक करने के लिए कुछ नहीं - जिस बिल्ड के कोड में beacons.startScan कॉल होता है, उसमें Bluetooth स्कैन और सटीक लोकेशन की अनुमतियाँ जुड़ जाती हैं (Android के लिए बीकन के नतीजे लोकेशन डेटा हैं, इसलिए यूज़र से दोनों माँगी जाती हैं, और Play ऐप को लोकेशन इस्तेमाल करने वाला दिखाता है)।
किसी एक जगह के बीकन की रेंज देखें:
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, बैटरी टेलीमेट्री के साथ:
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');
});स्क्रीन छोड़ने पर रोकें:
document.addEventListener('visibilitychange', async function () {
if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});इवेंट के फ़ील्ड: हर बीकन में type, rssi, address, timestamp होते हैं; iBeacon में uuid, major, minor, txPower, distance जुड़ते हैं; Eddystone UID में namespace, instance, txPower, distance; URL में url; TLM में batteryMilliVolts, temperatureC, advertCount, uptimeSeconds। uuids iBeacon UUID और Eddystone नेमस्पेस को फ़िल्टर करता है (छोटे अक्षरों में hex)।
ध्यान दें: दूरी सिग्नल की ताकत से आती है और सिर्फ़ पास / दूर के लिए ठीक है - दीवारें और शरीर इसे कई मीटर बदल देते हैं। स्कैन stopScan() या ऐप बंद होने तक चलता है।
beaconStopScan ब्रिज#
window.WebToApk.beaconStopScan(callbackId: String)
बीकन रेंजिंग रोकता है; [callbackId] को `{ok:true}` के साथ पूरा करता है।
उदाहरण
AppMint.beacons के साथ दस्तावेज़ित - वहाँ का उदाहरण इसे भी दिखाता है।
आस-पास के Bluetooth बीकन खोजता है - iBeacon और Eddystone (UID, URL और TLM) - और हर बीकन के बारे में लगभग हर सेकंड appmint:beacon इवेंट के ज़रिए पेज को बताता है, साथ में सिग्नल की ताक़त और अंदाज़न दूरी भी देता है।
लौटाता है: startScan() → {ok: true} या {ok: false, code} (not_enabled, permission_denied, bluetooth_off, bluetooth_unavailable, scan_failed, blocked) का Promise। stopScan() → {ok: true}। isScanning() → true/false। ज़रूरत: टिक करने के लिए कुछ नहीं - जिस बिल्ड के कोड में beacons.startScan कॉल होता है, उसमें Bluetooth स्कैन और सटीक लोकेशन की अनुमतियाँ जुड़ जाती हैं (Android के लिए बीकन के नतीजे लोकेशन डेटा हैं, इसलिए यूज़र से दोनों माँगी जाती हैं, और Play ऐप को लोकेशन इस्तेमाल करने वाला दिखाता है)।
किसी एक जगह के बीकन की रेंज देखें:
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, बैटरी टेलीमेट्री के साथ:
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');
});स्क्रीन छोड़ने पर रोकें:
document.addEventListener('visibilitychange', async function () {
if (document.visibilityState === 'hidden' && await AppMint.beacons.isScanning()) AppMint.beacons.stopScan();
});इवेंट के फ़ील्ड: हर बीकन में type, rssi, address, timestamp होते हैं; iBeacon में uuid, major, minor, txPower, distance जुड़ते हैं; Eddystone UID में namespace, instance, txPower, distance; URL में url; TLM में batteryMilliVolts, temperatureC, advertCount, uptimeSeconds। uuids iBeacon UUID और Eddystone नेमस्पेस को फ़िल्टर करता है (छोटे अक्षरों में hex)।
ध्यान दें: दूरी सिग्नल की ताकत से आती है और सिर्फ़ पास / दूर के लिए ठीक है - दीवारें और शरीर इसे कई मीटर बदल देते हैं। स्कैन stopScan() या ऐप बंद होने तक चलता है।
bleConnect ब्रिज#
window.WebToApk.bleConnect(address: String)
permBluetooth के लिए रनटाइम। इसे कोई विज़ार्ड स्विच सेट नहीं करता: बिल्ड इसे तब चालू करता है जब पेज का अपना कोड इन मेथड (या Web Bluetooth) को कॉल करता है। इस रनटाइम से पहले अनुमतियाँ घोषित थीं जबकि रनटाइम में कुछ भी सेंसर को नहीं छूता था, ठीक वही घोषित-पर-बेकार पैटर्न जो SmsBridge बताता है। नतीजे `appmint:ble` इवेंट / window.onAppMintBle({kind, ...}) के रूप में स्ट्रीम होते हैं।
इसका वर्णन अलग से नहीं, बल्कि इसके समूह BLE फ़िटनेस सेंसर (हार्ट रेट / स्पीड / कैडेंस / पावर / बैटरी) में किया गया है।
उदाहरण
bleStartScan() से मिले एक फ़िटनेस सेंसर से जुड़ता है और उसकी रीडिंग स्ट्रीम करना शुरू करता है।
लौटाता है: कुछ नहीं। प्रगति और डेटा appmint:ble इवेंट (और window.onAppMintBle) के रूप में आते हैं: {kind:'state', state:'connecting', address}, {kind:'state', state:'connected', address}, {kind:'state', state:'ready', services:[...]}, फिर हर रीडिंग के लिए {kind:'data', type, data}। ज़रूरी: Bluetooth (ZIP या HTML बिल्ड में तब अपने आप चालू होता है जब आपका कोड सेंसर ब्रिज इस्तेमाल करता है) और Nearby devices अनुमति, जो पहली बार इस्तेमाल पर माँगी जाती है।
address को device इवेंट से, यूज़र के टैप पर पास करें:
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);
}
};जिस सेंसर से कनेक्ट हुआ था उसे याद रखें और अगली बार बिना स्कैन किए उसी से दोबारा कनेक्ट करें:
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
});रीडिंग से हार्ट-रेट सेशन (औसत, अधिकतम, स्ट्रैप संपर्क) ट्रैक करें:
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);
});ध्यान दें: एक बार में एक ही सेंसर: कनेक्ट करने पर कोई भी स्कैन रुक जाता है और पिछला सेंसर पहले डिस्कनेक्ट हो जाता है (आपको उसका disconnected मिलता है)। ready.services इस सेंसर की सपोर्टेड सर्विस के पूरे UUID की सूची देता है। कनेक्ट होने के बाद पहला स्पीड/कैडेंस पैकेट कुछ नहीं भेजता - स्पीड और कैडेंस निकालने के लिए दो पैकेट चाहिए। heart_rate में rrIntervalsMs (धड़कनों के बीच का अंतराल ms में, जब स्ट्रैप भेजे), energyKj और contact भी आते हैं जब स्ट्रैप उन्हें बताए; power में दूसरे पैकेट से cadenceRpm आता है जब मीटर क्रैंक डेटा भेजे। त्रुटियाँ: not_enabled, permission_denied, bluetooth_unavailable, invalid_address। अगर सेंसर रेंज से बाहर चला जाए तो आपको {kind:'state', state:'disconnected', address}.
bleDisconnect ब्रिज#
window.WebToApk.bleDisconnect()
permBluetooth के लिए रनटाइम। इसे कोई विज़ार्ड स्विच सेट नहीं करता: बिल्ड इसे तब चालू करता है जब पेज का अपना कोड इन मेथड (या Web Bluetooth) को कॉल करता है। इस रनटाइम से पहले अनुमतियाँ घोषित थीं जबकि रनटाइम में कुछ भी सेंसर को नहीं छूता था, ठीक वही घोषित-पर-बेकार पैटर्न जो SmsBridge बताता है। नतीजे `appmint:ble` इवेंट / window.onAppMintBle({kind, ...}) के रूप में स्ट्रीम होते हैं।
इसका वर्णन अलग से नहीं, बल्कि इसके समूह BLE फ़िटनेस सेंसर (हार्ट रेट / स्पीड / कैडेंस / पावर / बैटरी) में किया गया है।
उदाहरण
इससे जुड़े फ़िटनेस सेंसर को डिस्कनेक्ट करता है bleConnect().
लौटाता है: कुछ नहीं। अगर कोई सेंसर जुड़ा था, तो उसके बाद appmint:ble इवेंट {kind:'state', state:'disconnected'} आता है। ज़रूरी: Bluetooth (ZIP या HTML बिल्ड में तब अपने आप चालू होता है जब आपका कोड सेंसर ब्रिज इस्तेमाल करता है); इसके बिना कॉल कुछ नहीं करता।
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');
}
});सेंसर छूट जाए तो अपने आप दोबारा कनेक्ट करें, लेकिन यूज़र ने वर्कआउट खुद ख़त्म किया हो तो नहीं:
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();
}
}सेटिंग्स स्क्रीन से सेव किए गए सेंसर को डिस्कनेक्ट करें और भुला दें:
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.');
});ध्यान दें: जो disconnected इवेंट आप खुद करवाते हैं उसमें address नहीं होता; सेंसर की वजह से हुए (रेंज से बाहर, बंद किया गया) इवेंट में होता है। ऐप बंद होने पर भी डिस्कनेक्ट हो जाता है।
bleSetWheelCircumference ब्रिज#
window.WebToApk.bleSetWheelCircumference(mm: Int)
permBluetooth के लिए रनटाइम। इसे कोई विज़ार्ड स्विच सेट नहीं करता: बिल्ड इसे तब चालू करता है जब पेज का अपना कोड इन मेथड (या Web Bluetooth) को कॉल करता है। इस रनटाइम से पहले अनुमतियाँ घोषित थीं जबकि रनटाइम में कुछ भी सेंसर को नहीं छूता था, ठीक वही घोषित-पर-बेकार पैटर्न जो SmsBridge बताता है। नतीजे `appmint:ble` इवेंट / window.onAppMintBle({kind, ...}) के रूप में स्ट्रीम होते हैं।
इसका वर्णन अलग से नहीं, बल्कि इसके समूह BLE फ़िटनेस सेंसर (हार्ट रेट / स्पीड / कैडेंस / पावर / बैटरी) में किया गया है।
उदाहरण
साइकिल के पहिये का साइज़ (परिधि, मिलीमीटर में) सेट करता है, जिससे स्पीड सेंसर के पहिये के चक्कर km/h में बदलते हैं।
लौटाता है: कुछ नहीं। ज़रूरत: इसे कॉल करने के लिए कुछ नहीं; स्पीड रीडिंग के लिए एक जुड़ा हुआ साइकिलिंग स्पीड सेंसर चाहिए (देखें 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));
});हर बार कनेक्ट करते समय राइडर का सेव किया हुआ पहिये का साइज़ लगाएँ, क्योंकि सेटिंग रीस्टार्ट के बाद नहीं रहती:
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);
}नापे हुए रोल-आउट (पहिये का एक पूरा चक्कर, cm में) से कैलिब्रेट करें और रेंज खुद जाँचें, क्योंकि रेंज से बाहर की वैल्यू बिना त्रुटि के अनदेखी कर दी जाती है:
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');
});ध्यान दें: 500-4000 mm से बाहर की वैल्यू चुपचाप अनदेखी की जाती हैं (पुरानी वैल्यू बनी रहती है)। सेटिंग ऐप बंद होने तक रहती है, इसलिए हर लॉन्च के बाद इसे दोबारा सेट करें, जैसे bleConnect() से ठीक पहले। यह appmint:ble के speed_cadence डेटा में speedKmh बदलती है; कैडेंस पर असर नहीं पड़ता।
bleStartScan ब्रिज#
window.WebToApk.bleStartScan()
permBluetooth के लिए रनटाइम। इसे कोई विज़ार्ड स्विच सेट नहीं करता: बिल्ड इसे तब चालू करता है जब पेज का अपना कोड इन मेथड (या Web Bluetooth) को कॉल करता है। इस रनटाइम से पहले अनुमतियाँ घोषित थीं जबकि रनटाइम में कुछ भी सेंसर को नहीं छूता था, ठीक वही घोषित-पर-बेकार पैटर्न जो SmsBridge बताता है। नतीजे `appmint:ble` इवेंट / window.onAppMintBle({kind, ...}) के रूप में स्ट्रीम होते हैं।
इसका वर्णन अलग से नहीं, बल्कि इसके समूह BLE फ़िटनेस सेंसर (हार्ट रेट / स्पीड / कैडेंस / पावर / बैटरी) में किया गया है।
उदाहरण
पास के Bluetooth फ़िटनेस सेंसर (हार्ट रेट, साइकिलिंग स्पीड/कैडेंस, पावर, रनिंग फ़ुटपॉड) 15 सेकंड तक स्कैन करता है।
लौटाता है: कुछ नहीं। नतीजे appmint:ble इवेंट (और window.onAppMintBle(detail)) के रूप में आते हैं: मिले हर सेंसर के लिए एक {kind:'device', address, name, sensors, rssi}, साथ में शुरुआत में {kind:'state', state:'scanning'} और अंत में {kind:'state', state:'scan_stopped'}। ज़रूरी: Bluetooth। इसके लिए कोई स्विच नहीं है: जब आप ZIP या HTML पेज से बिल्ड करते हैं, और आपका कोड bleStartScan / bleConnect कॉल करता है या navigator.bluetooth इस्तेमाल करता है, तो बिल्ड Bluetooth अपने आप चालू कर देता है। वेबसाइट (URL) बिल्ड इसे चालू नहीं कर सकता। पहला स्कैन यूज़र से Nearby devices अनुमति माँगता है (Android 11 और पुराने में Location)।
पहले सुनना शुरू करें, फिर बटन दबाने पर स्कैन करें:
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.');
}
});स्कैन खत्म होने पर सबसे मज़बूत हार्ट-रेट स्ट्रैप अपने आप पेयर करें:
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();
});हर स्कैन त्रुटि को ऐसे संदेश में बदलें जिस पर यूज़र कुछ कर सके:
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;
});ध्यान दें: {kind:'error', error} में त्रुटि कोड: not_enabled (बिल्ड में Bluetooth नहीं है), permission_denied, bluetooth_unavailable (कोई अडैप्टर नहीं), bluetooth_off, scan_failed (इसमें Android का code हो सकता है)। स्कैन सिर्फ़ उन डिवाइस को बताता है जो चार फ़िटनेस सर्विस में से कोई एक विज्ञापित करते हैं, और हर डिवाइस हर स्कैन में सिर्फ़ एक बार। नया स्कैन शुरू करने पर पुराना रुक जाता है। या तो appmint:ble लिसनर इस्तेमाल करें या window.onAppMintBle, दोनों नहीं - हर संदेश पर दोनों कॉल होते हैं।
bleStopScan ब्रिज#
window.WebToApk.bleStopScan()
permBluetooth के लिए रनटाइम। इसे कोई विज़ार्ड स्विच सेट नहीं करता: बिल्ड इसे तब चालू करता है जब पेज का अपना कोड इन मेथड (या Web Bluetooth) को कॉल करता है। इस रनटाइम से पहले अनुमतियाँ घोषित थीं जबकि रनटाइम में कुछ भी सेंसर को नहीं छूता था, ठीक वही घोषित-पर-बेकार पैटर्न जो SmsBridge बताता है। नतीजे `appmint:ble` इवेंट / window.onAppMintBle({kind, ...}) के रूप में स्ट्रीम होते हैं।
इसका वर्णन अलग से नहीं, बल्कि इसके समूह BLE फ़िटनेस सेंसर (हार्ट रेट / स्पीड / कैडेंस / पावर / बैटरी) में किया गया है।
उदाहरण
bleStartScan() से शुरू हुआ सेंसर स्कैन उसके 15 सेकंड पूरे होने से पहले रोकता है।
लौटाता है: कुछ नहीं। अगर स्कैन चल रहा था, तो उसके बाद appmint:ble इवेंट {kind:'state', state:'scan_stopped'} आता है। ज़रूरी: Bluetooth (ZIP या HTML बिल्ड में तब अपने आप चालू होता है जब आपका कोड सेंसर ब्रिज इस्तेमाल करता है); इसके बिना कॉल कुछ नहीं करता।
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();
}
});जैसे ही आपके काम का सेंसर दिखे, जल्दी रोक दें:
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');
}
});जब यूज़र पेयरिंग शीट बंद करे या ऐप छोड़े, तब स्कैन रोकें:
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();
});ध्यान दें: आपको इसकी कम ही ज़रूरत पड़ेगी: स्कैन 15 सेकंड बाद अपने आप रुक जाता है, और bleConnect() भी इसे रोकता है। जब कोई स्कैन नहीं चल रहा हो तब इसे कॉल करना सुरक्षित है और कोई इवेंट नहीं भेजता।