واجهة JavaScript bridge API
مستشعرات اللياقة BLE
معدل ضربات القلب والسرعة والإيقاع والقدرة والبطارية من ملفات Bluetooth Low Energy القياسية.
AppMint.beacons دالة مساعدة (helper)#
AppMint.beacons.startScan(options) / stopScan() / isScanning()
مثال
يجد منارات Bluetooth القريبة - iBeacon وEddystone (UID وURL وTLM) - ويخبر الصفحة عن كل واحدة نحو مرة كل ثانية عبر الحدث appmint:beacon، مع قوة الإشارة ومسافة تقريبية.
يعيد: startScan() ← Promise لـ {ok: true} أو {ok: false, code} (not_enabled، permission_denied، bluetooth_off، bluetooth_unavailable، scan_failed، blocked). 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 معرّفات UUID لـ iBeacon ومساحات أسماء Eddystone (hex بأحرف صغيرة).
ملاحظات: تأتي المسافة من قوة الإشارة وتصلح فقط لـ قريب / بعيد - فالجدران والأجسام تغيّرها بأمتار. يستمر المسح حتى stopScan() أو إغلاق التطبيق.
beaconIsScanning الجسر (bridge)#
window.WebToApk.beaconIsScanning(): Boolean
ما إذا كان مسح المنارات يعمل.
مثال
موثَّق مع AppMint.beacons - والمثال هناك يعرض هذا أيضًا.
يجد منارات Bluetooth القريبة - iBeacon وEddystone (UID وURL وTLM) - ويخبر الصفحة عن كل واحدة نحو مرة كل ثانية عبر الحدث appmint:beacon، مع قوة الإشارة ومسافة تقريبية.
يعيد: startScan() ← Promise لـ {ok: true} أو {ok: false, code} (not_enabled، permission_denied، bluetooth_off، bluetooth_unavailable، scan_failed، blocked). 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 معرّفات UUID لـ iBeacon ومساحات أسماء Eddystone (hex بأحرف صغيرة).
ملاحظات: تأتي المسافة من قوة الإشارة وتصلح فقط لـ قريب / بعيد - فالجدران والأجسام تغيّرها بأمتار. يستمر المسح حتى stopScan() أو إغلاق التطبيق.
beaconStartScan الجسر (bridge)#
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() ← Promise لـ {ok: true} أو {ok: false, code} (not_enabled، permission_denied، bluetooth_off، bluetooth_unavailable، scan_failed، blocked). 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 معرّفات UUID لـ iBeacon ومساحات أسماء Eddystone (hex بأحرف صغيرة).
ملاحظات: تأتي المسافة من قوة الإشارة وتصلح فقط لـ قريب / بعيد - فالجدران والأجسام تغيّرها بأمتار. يستمر المسح حتى stopScan() أو إغلاق التطبيق.
beaconStopScan الجسر (bridge)#
window.WebToApk.beaconStopScan(callbackId: String)
يوقف قياس المنارات (beacons)؛ ويُنجز [callbackId] بـ `{ok:true}`.
مثال
موثَّق مع AppMint.beacons - والمثال هناك يعرض هذا أيضًا.
يجد منارات Bluetooth القريبة - iBeacon وEddystone (UID وURL وTLM) - ويخبر الصفحة عن كل واحدة نحو مرة كل ثانية عبر الحدث appmint:beacon، مع قوة الإشارة ومسافة تقريبية.
يعيد: startScan() ← Promise لـ {ok: true} أو {ok: false, code} (not_enabled، permission_denied، bluetooth_off، bluetooth_unavailable، scan_failed، blocked). 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 معرّفات UUID لـ iBeacon ومساحات أسماء Eddystone (hex بأحرف صغيرة).
ملاحظات: تأتي المسافة من قوة الإشارة وتصلح فقط لـ قريب / بعيد - فالجدران والأجسام تغيّرها بأمتار. يستمر المسح حتى stopScan() أو إغلاق التطبيق.
bleConnect الجسر (bridge)#
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 عندما يستخدم كودك جسر المستشعرات) وإذن الأجهزة القريبة، الذي يُطلب عند أول استخدام.
مرّر 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 (فترات بين النبضات بالمللي ثانية، عندما يرسلها الحزام) وenergyKj وcontact عندما يبلغ عنها الحزام؛ ويحمل power القيمة cadenceRpm من حزمته الثانية فصاعدًا عندما يرسل العداد بيانات الكرنك. الأخطاء: not_enabled، permission_denied، bluetooth_unavailable، invalid_address. إذا خرج المستشعر من النطاق تحصل على {kind:'state', state:'disconnected', address}.
bleDisconnect الجسر (bridge)#
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 الجسر (bridge)#
window.WebToApk.bleSetWheelCircumference(mm: Int)
بيئة التشغيل لـ permBluetooth. لا يوجد مفتاح في المعالج يفعّله: يفعّله البناء عندما يستدعي كود الصفحة نفسه هذه الدوال (أو Web Bluetooth). قبل بيئة التشغيل هذه كانت الأذونات تُعلن بينما لا يلمس شيء في بيئة التشغيل أي مستشعر، وهو بالضبط نمط "معلن لكنه ميت" الذي يوثقه SmsBridge. تُبثّ النتائج كأحداث `appmint:ble` / window.onAppMintBle({kind, ...}).
موصوف مع مجموعته، مستشعرات اللياقة BLE (معدل ضربات القلب / السرعة / الإيقاع / القدرة / البطارية)، وليس وحده.
مثال
يضبط حجم عجلة الدراجة (المحيط بالمليمترات) المستخدم لتحويل دورات عجلة مستشعر السرعة إلى كم/س.
يعيد: لا شيء. يتطلب: لا شيء لاستدعائه؛ أما قراءات السرعة نفسها فتحتاج مستشعر سرعة دراجة متصلًا (انظر 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);
}عايِر من دورة عجلة مقاسة (دورة كاملة، بالسنتيمتر) وتحقق من النطاق بنفسك، لأن القيمة خارج النطاق تُتجاهل دون خطأ:
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 مم تُتجاهل بصمت (تبقى القيمة القديمة). يدوم الإعداد حتى إغلاق التطبيق، لذا اضبطه مجددًا بعد كل تشغيل، مثلًا قبل bleConnect() مباشرة. يغيّر speedKmh في بيانات speed_cadence الخاصة بـ appmint:ble؛ ولا يتأثر الإيقاع.
bleStartScan الجسر (bridge)#
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، يفعّل البناء Bluetooth تلقائيًا إذا استدعى كودك bleStartScan / bleConnect أو استخدم navigator.bluetooth. لا يمكن لبناء موقع (URL) تفعيله. يطلب أول مسح من المستخدم إذن الأجهزة القريبة (الموقع في Android 11 وما قبله).
استمع أولًا، ثم امسح من نقرة زر:
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 (قد يحمل code الخاص بـ Android). لا يبلغ المسح إلا عن الأجهزة التي تعلن إحدى خدمات اللياقة الأربع، وكل جهاز مرة واحدة فقط في كل مسح. بدء مسح جديد يوقف القديم. استخدم إما مستمع appmint:ble أو window.onAppMintBle، وليس كليهما - فكلاهما يُستدعى لكل رسالة.
bleStopScan الجسر (bridge)#
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() يوقفه أيضًا. استدعاؤه عندما لا يعمل مسح آمن ولا يرسل أي حدث.