ساخت APK رایگان

API پل جاوااسکریپت

Web Bluetooth

شکل navigator.bluetooth که به‌صورت بومی پاسخ داده می‌شود تا صفحه‌ای که برای Chrome نوشته شده بدون تغییر کار کند.

navigator.bluetooth استاندارد وب#

navigator.bluetooth — Web Bluetooth

نمونه

Web Bluetooth استاندارد: همان کد navigator.bluetooth که برای Chrome می‌نویسید در اپلیکیشن نصب‌شده برای هر دستگاه Bluetooth Low Energy کار می‌کند.

بازگشتی: Promise‌ها، مطابق استاندارد وب. مقادیر DataView هستند. نیازمند: بلوتوث. کلیدی وجود ندارد: وقتی از ZIP یا یک صفحه‌ی HTML می‌سازید، اگر کد شما navigator.bluetooth (یا BluetoothUUID) را ذکر کند ساخت خودش آن را روشن می‌کند. ساخت وب‌سایت (URL) نمی‌تواند آن را روشن کند. بدون آن navigator.bluetooth وجود ندارد. اولین requestDevice() مجوز دستگاه‌های اطراف را می‌پرسد (در اندروید ۱۱ و قدیمی‌تر مکان). اگر بلوتوث خاموش باشد، اپلیکیشن از کاربر می‌خواهد روشنش کند و بعد خودش فهرست دستگاه را باز می‌کند؛ در اندروید ۶ تا ۱۱ برای تنظیم مکان هم همین کار را می‌کند، چون آن نسخه‌ها تا وقتی مکان خاموش است هیچ دستگاه بلوتوثی پیدا نمی‌کنند.

کمربند ضربان قلب، با ضربه‌ی دکمه. navigator.bluetooth پیش از اولین اسکریپت شما وجود دارد، پس تشخیص قابلیت در بالای صفحه کار می‌کند؛ بررسی داخل کنترل‌کننده، مثل اینجا، همچنان امن‌ترین عادت است:

var hrDevice = null;

document.getElementById('connect').addEventListener('click', async function () {
  if (!navigator.bluetooth) { showStatus('Bluetooth is not available in this app.'); return; }
  try {
    hrDevice = await navigator.bluetooth.requestDevice({
      filters: [{ services: ['heart_rate'] }],        // or { name: 'Polar H10' } / { namePrefix: 'Polar' }
      optionalServices: ['battery_service']
    });
    hrDevice.addEventListener('gattserverdisconnected', function () {
      showStatus('Strap disconnected');
    });
    var server = await hrDevice.gatt.connect();
    var hr = await server.getPrimaryService('heart_rate');
    var chr = await hr.getCharacteristic('heart_rate_measurement');
    chr.addEventListener('characteristicvaluechanged', function (e) {
      var v = e.target.value;                                   // DataView
      var bpm = (v.getUint8(0) & 1) ? v.getUint16(1, true) : v.getUint8(1);
      document.getElementById('bpm').textContent = bpm;
    });
    await chr.startNotifications();

    var battery = await server.getPrimaryService('battery_service');
    var level = await (await battery.getCharacteristic('battery_level')).readValue();
    document.getElementById('battery').textContent = level.getUint8(0) + '%';
  } catch (err) {
    // NotFoundError: user cancelled the chooser, Bluetooth is off, or no such service.
    // SecurityError: permission denied.  NetworkError: connection lost.
    showStatus(err.name + ': ' + err.message);
  }
});

document.getElementById('disconnect').addEventListener('click', function () {
  if (hrDevice && hrDevice.gatt.connected) hrDevice.gatt.disconnect();
});

نوشتن. writeValue و writeValueWithResponse منتظر تأیید دستگاه می‌مانند؛ writeValueWithoutResponse نمی‌ماند:

async function sendCommands(server) {
  // Heart Rate Control Point: 0x01 = reset energy expended
  var hr = await server.getPrimaryService('heart_rate');
  var ctrl = await hr.getCharacteristic('heart_rate_control_point');
  await ctrl.writeValueWithResponse(new Uint8Array([0x01]));

  // A custom UART-style service (full 128-bit UUIDs work everywhere)
  var uart = await server.getPrimaryService('6e400001-b5a3-f393-e0a9-e50e24dcca9e');
  var rx = await uart.getCharacteristic('6e400002-b5a3-f393-e0a9-e50e24dcca9e');
  await rx.writeValueWithoutResponse(new TextEncoder().encode('LED ON\n'));
}

کاوشگر دستگاه. هر دستگاه اطراف را انتخاب کنید و همه سرویس‌ها و ویژگی‌های آن را به همراه کاری که هرکدام می‌تواند انجام دهد فهرست کنید:

document.getElementById('explore').addEventListener('click', async function () {
  if (!navigator.bluetooth) { showStatus('Bluetooth is not available in this app.'); return; }
  try {
    var device = await navigator.bluetooth.requestDevice({ acceptAllDevices: true });
    var server = await device.gatt.connect();
    var lines = [];
    for (var service of await server.getPrimaryServices()) {
      lines.push(service.uuid === BluetoothUUID.getService('battery_service') ? 'Battery' : service.uuid);
      var chars = await service.getCharacteristics().catch(function () { return []; });
      chars.forEach(function (c) {
        var can = Object.keys(c.properties).filter(function (k) { return c.properties[k]; });
        lines.push('  ' + c.uuid + '  ' + can.join(', '));
      });
    }
    document.getElementById('tree').textContent = lines.join('\n');
    device.gatt.disconnect();
  } catch (err) {
    showStatus(err.name + ': ' + err.message);
  }
});

حسگر سرعت و آهنگ رکاب‌زنی. یک حسگر (یا یک جفت حسگر جدا برای سرعت و آهنگ) روی سرویس استاندارد CSC. مقدارها جمع تجمعی هستند، پس سرعت و آهنگ از تفاضل دو اعلان به دست می‌آید:

var WHEEL_MM = 2105;                       // 700x25c; set your own wheel circumference
var last = null;

async function connectCsc() {
  var device = await navigator.bluetooth.requestDevice({
    filters: [{ services: ['cycling_speed_and_cadence'] }]      // same as { services: [0x1816] }
  });
  device.addEventListener('gattserverdisconnected', function () { showStatus('Sensor disconnected'); });
  var server = await device.gatt.connect();
  var csc = await server.getPrimaryService('cycling_speed_and_cadence');
  var chr = await csc.getCharacteristic('csc_measurement');
  chr.addEventListener('characteristicvaluechanged', function (e) {
    var v = e.target.value, flags = v.getUint8(0), i = 1, now = {};
    if (flags & 1) { now.wheels = v.getUint32(i, true); now.wheelTime = v.getUint16(i + 4, true); i += 6; }
    if (flags & 2) { now.cranks = v.getUint16(i, true); now.crankTime = v.getUint16(i + 2, true); }
    if (last) {
      if (now.wheels !== undefined && now.wheelTime !== last.wheelTime) {
        var dt = ((now.wheelTime - last.wheelTime) & 0xffff) / 1024;          // seconds
        var kmh = (now.wheels - last.wheels) * WHEEL_MM / 1e6 / (dt / 3600);
        document.getElementById('speed').textContent = kmh.toFixed(1) + ' km/h';
      }
      if (now.cranks !== undefined && now.crankTime !== last.crankTime) {
        var ct = ((now.crankTime - last.crankTime) & 0xffff) / 1024;
        var rpm = ((now.cranks - last.cranks) & 0xffff) / ct * 60;
        document.getElementById('cadence').textContent = Math.round(rpm) + ' rpm';
      }
    }
    last = Object.assign({}, last, now);
  });
  await chr.startNotifications();
}

نمایش هر خطا روی صفحه. هر مرحله با یک خطای نام‌دار رد می‌شود تا صفحه به‌جای سکوت بتواند به کاربر بگوید چه کار کند:

async function connectWithMessage(connect) {
  try {
    await connect();
    showStatus('Connected');
  } catch (err) {
    var msg = {
      NotFoundError: 'No sensor chosen, Bluetooth was left off, or the sensor lacks that service.',
      SecurityError: 'Bluetooth permission was refused. Allow "Nearby devices" in the app settings.',
      NetworkError: 'The sensor went out of range or is connected to another app or watch. Try again.',
      TypeError: 'Programming error: ' + err.message
    }[err.name] || err.message;
    showStatus(err.name + ': ' + msg);
  }
}
document.getElementById('scan-hr').addEventListener('click', function () { connectWithMessage(connectHeartRate); });
document.getElementById('scan-csc').addEventListener('click', function () { connectWithMessage(connectCsc); });

نکات: انتخابگر دستگاه را اپلیکیشن رسم می‌کند و ۳۰ ثانیه جست‌وجو می‌کند، سپس «جست‌وجوی دوباره» را پیشنهاد می‌دهد؛ صفحه فقط به دستگاهی که کاربر انتخاب کرده دسترسی دارد. getAvailability() پاسخ می‌دهد که گوشی اصلاً بلوتوث دارد یا نه (مثل Chrome)، نه اینکه روشن است یا نه: requestDevice() درخواست روشن کردن آن را می‌دهد. حسگری که نامش را فقط در پاسخ جست‌وجو می‌فرستد، باز هم با نام فهرست می‌شود و با namePrefix مطابقت می‌کند. اتصال اولی که Android بلافاصله قطع کند، پیش از آنکه connect() رد شود دوباره تلاش می‌شود. نام سرویسی که غلط نوشته شده باشد در filters یا optionalServices با TypeError رد می‌شود. فیلتر services فقط با سرویس‌هایی که دستگاه تبلیغ می‌کند مطابقت دارد. پس از connect() می‌توان از همه سرویس‌های دستگاه استفاده کرد، حتی آن‌هایی که در optionalServices نیستند (باز هم آن‌ها را بنویسید، Chrome به آن‌ها نیاز دارد). همچنین در دسترس: getAvailability()، getPrimaryServices()، getCharacteristics()، stopNotifications()، characteristic.properties، device.forget()، BluetoothUUID.getService/getCharacteristic/canonicalUUID. getDevices()، requestLEScan()، watchAdvertisements() و توصیف‌گرها با NotSupportedError رد می‌شوند. برای حسگرهای استاندارد تناسب اندام که اعداد از قبل تجزیه شده‌اند، ببینید bleStartScan.

wbConnect پل#

window.WebToApk.wbConnect(requestId: String, deviceId: String)

انتقال خام برای WebBluetoothPolyfill. صفحه هرگز این‌ها را مستقیم فراخوانی نمی‌کند؛ مثل مرورگر، navigator.bluetooth را فراخوانی می‌کند.

به‌جای خودش، با گروهش Web Bluetooth (navigator.bluetooth) توضیح داده می‌شود.

نمونه

انتقال داخلی پشت device.gatt.connect(). صفحه‌ها از navigator.bluetooth استفاده می‌کنند؛ مستقیم این را صدا نزنید.

بازگشتی: (از طریق gatt.connect()) یک Promise از BluetoothRemoteGATTServer، پس از کشف سرویس‌های دستگاه. نیازمند: بلوتوث (با ساخت از ZIP یا HTML وقتی کد شما ذکر کند خودش روشن می‌شود navigator.bluetooth).

async function connectAndList(device) {
  try {
    var server = await device.gatt.connect();
    var services = await server.getPrimaryServices();
    services.forEach(function (s) { console.log('service', s.uuid); });
    return server;
  } catch (err) {
    showStatus('Could not connect: ' + err.message);    // NetworkError, NotFoundError
    return null;
  }
}

وقتی دستگاه قطع می‌شود دوباره وصل شوید. صدا زدن دوباره‌ی connect() روی همان دستگاه کار می‌کند:

device.addEventListener('gattserverdisconnected', async function () {
  showStatus('Reconnecting...');
  try { await connectAndList(device); showStatus('Connected'); } catch (e) {}
});

همزمان به دو حسگر وصل شوید، مثلاً یک کمربند ضربان قلب و یک توان‌سنج:

async function connectBoth(hrDevice, powerDevice) {
  var servers = await Promise.all([hrDevice.gatt.connect(), powerDevice.gatt.connect()]);
  var hr = await servers[0].getPrimaryService('heart_rate');
  var power = await servers[1].getPrimaryService('cycling_power');
  return {
    heartRate: await hr.getCharacteristic('heart_rate_measurement'),
    power: await power.getCharacteristic('cycling_power_measurement')
  };
}

نکته‌ها: امضای خام wbConnect(requestId, deviceId). اتصال به دستگاهی که از قبل وصل است دوباره بلافاصله حل می‌شود. getPrimaryService(s) پیش از اینکه connect() تمام شود با NetworkError رد می‌شود. چند دستگاه را می‌توان همزمان وصل کرد.

wbDisconnect پل#

window.WebToApk.wbDisconnect(deviceId: String)

انتقال خام برای WebBluetoothPolyfill. صفحه هرگز این‌ها را مستقیم فراخوانی نمی‌کند؛ مثل مرورگر، navigator.bluetooth را فراخوانی می‌کند.

به‌جای خودش، با گروهش Web Bluetooth (navigator.bluetooth) توضیح داده می‌شود.

نمونه

انتقال داخلی پشت device.gatt.disconnect() (و device.forget()). صفحه‌ها از navigator.bluetooth استفاده می‌کنند؛ این را مستقیم فراخوانی نکنید.

مقدار بازگشتی: هیچ (disconnect() همزمان است، مثل استاندارد وب). سپس دستگاه gattserverdisconnected را اجرا می‌کند. نیازمند: بلوتوث (وقتی کد شما به آن اشاره کند، در ساخت ZIP یا HTML روشن می‌شود navigator.bluetooth).

var device = null;   // from navigator.bluetooth.requestDevice(...)

document.getElementById('stop').addEventListener('click', function () {
  if (device && device.gatt.connected) device.gatt.disconnect();
});

// Fires for your own disconnect() and when the device goes away by itself.
function watch(dev) {
  dev.addEventListener('gattserverdisconnected', function () {
    showStatus('Disconnected from ' + (dev.name || dev.id));
  });
}

توقف خودتان را از خارج شدن حسگر از برد تشخیص دهید و فقط در حالت دوم دوباره وصل شوید:

var userStopped = false;

function stopSession(device) {
  userStopped = true;
  if (device.gatt.connected) device.gatt.disconnect();
}

function watchDrops(device) {
  device.addEventListener('gattserverdisconnected', function () {
    if (userStopped) { userStopped = false; showStatus('Session ended'); return; }
    showStatus('Sensor out of range, reconnecting...');
    device.gatt.connect()
      .then(resubscribe)                 // your code: get the characteristics, startNotifications()
      .catch(function () { showStatus('Could not reconnect'); });
  });
}

یک دکمه‌ی «حذف حسگر»: forget() قطع می‌کند و دستگاه را رها می‌کند و بعد از آن gattserverdisconnected نمی‌آید:

var device = null;   // from navigator.bluetooth.requestDevice(...)

document.getElementById('forget').addEventListener('click', async function () {
  if (!device) return;
  await device.forget();
  device = null;
  showStatus('Sensor removed. Tap Connect to pick one again.');
});

نکته‌ها: امضای خام wbDisconnect(deviceId). بعد از قطع اتصال، شیءهای قدیمی سرویس و مشخصه دیگر اعلان نمی‌گیرند؛ بعد از connect() بعدی دوباره آن‌ها را بگیرید و startNotifications() را دوباره صدا بزنید. اپلیکیشن هنگام بسته شدن همه‌ی اتصال‌ها را رها می‌کند.

wbGetCharacteristics پل#

window.WebToApk.wbGetCharacteristics(requestId: String, deviceId: String, service: String)

انتقال خام برای WebBluetoothPolyfill. صفحه هرگز این‌ها را مستقیم فراخوانی نمی‌کند؛ مثل مرورگر، navigator.bluetooth را فراخوانی می‌کند.

به‌جای خودش، با گروهش Web Bluetooth (navigator.bluetooth) توضیح داده می‌شود.

نمونه

انتقال داخلی پشت service.getCharacteristic(uuid) و service.getCharacteristics(). صفحه‌ها از navigator.bluetooth استفاده می‌کنند؛ مستقیم این را صدا نزنید.

بازگشتی: (از طریق سرویس) یک Promise از یک BluetoothRemoteGATTCharacteristic، یا از آرایه‌ای از آن‌ها، هر کدام با uuid و properties (read، write، writeWithoutResponse، notify، indicate، broadcast، authenticatedSignedWrites). نیازمند: بلوتوث (با ساخت از ZIP یا HTML وقتی کد شما ذکر کند خودش روشن می‌شود navigator.bluetooth).

async function describe(server) {
  var service = await server.getPrimaryService('heart_rate');

  // One characteristic, by standard name, 16-bit number or full UUID:
  var measurement = await service.getCharacteristic('heart_rate_measurement'); // same as 0x2a37

  // All of them:
  var list = await service.getCharacteristics();
  list.forEach(function (c) {
    var p = c.properties;
    console.log(c.uuid, p.read ? 'read' : '', p.write ? 'write' : '', p.notify ? 'notify' : '');
  });
  return measurement;
}

به‌جای نوشتن ثابت UUIDها در کد، ویژگی‌های یک سرویس سفارشی را بر اساس کاری که می‌توانند انجام دهند پیدا کنید:

async function findSerialPipes(server) {
  var svc = await server.getPrimaryService('0000ffe0-0000-1000-8000-00805f9b34fb');
  var list = await svc.getCharacteristics();
  var incoming = list.find(function (c) { return c.properties.notify || c.properties.indicate; });
  var outgoing = list.find(function (c) { return c.properties.write || c.properties.writeWithoutResponse; });
  if (!incoming || !outgoing) throw new Error('This device does not look like a serial adapter');
  return { incoming: incoming, outgoing: outgoing };
}

یک ویژگی اختیاری را بخوانید و NotFoundError را به معنی «این دستگاه آن را ندارد» در نظر بگیرید:

var LOCATIONS = ['Other', 'Chest', 'Wrist', 'Finger', 'Hand', 'Ear lobe', 'Foot'];

async function sensorLocation(server) {
  var hr = await server.getPrimaryService('heart_rate');
  try {
    var chr = await hr.getCharacteristic('body_sensor_location');   // 0x2a38, optional in the spec
    var v = await chr.readValue();
    return LOCATIONS[v.getUint8(0)] || 'Unknown';
  } catch (err) {
    if (err.name === 'NotFoundError') return null;                   // this strap does not report it
    throw err;
  }
}

نکات: امضای خام wbGetCharacteristics(requestId, deviceId, service). UUIDی که سرویس ندارد با NotFoundError رد می‌شود؛ نام ناشناخته TypeError پرتاب می‌کند (برای ویژگی‌های سفارشی UUID کامل بدهید). فهرست یک بار برای هر سرویس گرفته می‌شود و بعد دوباره استفاده می‌شود.

wbIsAvailable پل#

window.WebToApk.wbIsAvailable(): Boolean

getAvailability(): اینکه آیا گوشی بلوتوث دارد (پاسخ Chrome)، نه اینکه روشن است. requestDevice() از کاربر می‌خواهد آن را روشن کند؛ جواب false دادن وقتی فقط خاموش بود باعث می‌شد صفحه‌هایی که اول بررسی می‌کنند ("if (!await getAvailability())") بگویند «بلوتوث پشتیبانی نمی‌شود» و هرگز به آن درخواست نرسند (گزارش تناسب اندام ۲۰۲۶-۱۰-۰۳).

نمونه

انتقال داخلی پشت navigator.bluetooth.getAvailability(). صفحه‌ها از navigator.bluetooth استفاده می‌کنند؛ مستقیم این را صدا نزنید.

بازگشتی: (از طریق getAvailability()) یک Promise از true وقتی ساخت بلوتوث دارد و بلوتوث گوشی روشن است، وگرنه false. نیازمند: بلوتوث (با ساخت از ZIP یا HTML وقتی کد شما ذکر کند خودش روشن می‌شود navigator.bluetooth).

async function updateBluetoothHint() {
  var hint = document.getElementById('bt-hint');
  if (!navigator.bluetooth) { hint.textContent = 'Bluetooth is not available in this app.'; return; }
  var on = await navigator.bluetooth.getAvailability();
  hint.textContent = on ? '' : 'Turn on Bluetooth to connect your sensor.';
}
document.addEventListener('visibilitychange', function () {
  if (!document.hidden) updateBluetoothHint();
});

دکمه‌ی اتصال را فقط وقتی بلوتوث روشن است فعال کنید و هر بار که کاربر به اپلیکیشن برمی‌گردد دوباره بررسی کنید:

async function refreshConnectButton() {
  var btn = document.getElementById('connect');
  if (!navigator.bluetooth) { btn.hidden = true; return; }
  btn.disabled = !(await navigator.bluetooth.getAvailability());
}
window.addEventListener('load', refreshConnectButton);
document.addEventListener('visibilitychange', function () {
  if (!document.hidden) refreshConnectButton();
});

پیش از باز کردن انتخابگر یک پیام روشن «بلوتوث خاموش است» بدهید، به‌جای NotFoundError بعد از آن:

document.getElementById('scan').addEventListener('click', async function () {
  if (!navigator.bluetooth) { showStatus('Bluetooth is not available in this app.'); return; }
  if (!(await navigator.bluetooth.getAvailability())) {
    showStatus('Bluetooth is off. Turn it on, then tap Scan again.');
    return;
  }
  try {
    var device = await navigator.bluetooth.requestDevice({ acceptAllDevices: true });
    showStatus('Picked ' + (device.name || device.id));
  } catch (err) {
    showStatus(err.name === 'NotFoundError' ? 'No device chosen.' : err.message);
  }
});

نکته‌ها: امضای خام wbIsAvailable(): Boolean (همزمان). فقط آداپتور را بررسی می‌کند - هیچ مجوزی نمی‌خواهد. رویداد availabilitychanged استاندارد وب فعال نمی‌شود، پس وقتی صفحه قابل مشاهده شد دوباره بررسی کنید.

wbNotify پل#

window.WebToApk.wbNotify(

انتقال خام برای WebBluetoothPolyfill. صفحه هرگز این‌ها را مستقیم فراخوانی نمی‌کند؛ مثل مرورگر، navigator.bluetooth را فراخوانی می‌کند.

به‌جای خودش، با گروهش Web Bluetooth (navigator.bluetooth) توضیح داده می‌شود.

نمونه

انتقال داخلی پشت characteristic.startNotifications() و stopNotifications(). صفحه‌ها از navigator.bluetooth استفاده می‌کنند؛ مستقیم این را صدا نزنید.

بازگشتی: (از طریق مشخصه) یک Promise از خود مشخصه. هر مقدار جدید روی آن characteristicvaluechanged را فعال می‌کند، با مقدار در event.target.value (یک DataView). نیازمند: بلوتوث (با ساخت از ZIP یا HTML وقتی کد شما ذکر کند خودش روشن می‌شود navigator.bluetooth).

var cadenceChr = null;

async function startCadence(server) {
  var svc = await server.getPrimaryService('running_speed_and_cadence');
  cadenceChr = await svc.getCharacteristic('rsc_measurement');
  cadenceChr.addEventListener('characteristicvaluechanged', onRsc);
  await cadenceChr.startNotifications();
}

function onRsc(e) {
  var v = e.target.value;
  var kmh = v.getUint16(1, true) / 256 * 3.6;
  var spm = v.getUint8(3);
  show('run', kmh.toFixed(1) + ' km/h, ' + spm + ' steps/min');
}

async function stopCadence() {
  if (!cadenceChr) return;
  cadenceChr.removeEventListener('characteristicvaluechanged', onRsc);
  try { await cadenceChr.stopNotifications(); } catch (e) {}
}

دماسنجی که اندازه‌گیری آن فقط از نوع indication پشتیبانی می‌کند، به همین روش مشترک می‌شود؛ اینجا از طریق ویژگی oncharacteristicvaluechanged:

async function watchThermometer(server) {
  var svc = await server.getPrimaryService('health_thermometer');
  var chr = await svc.getCharacteristic('temperature_measurement');
  chr.oncharacteristicvaluechanged = function (e) {
    var v = e.target.value;
    var mantissa = v.getUint32(1, true) & 0x00ffffff;          // IEEE-11073 FLOAT
    if (mantissa & 0x800000) mantissa -= 0x1000000;
    var fahrenheit = v.getUint8(0) & 1;
    show('temp', (mantissa * Math.pow(10, v.getInt8(4))).toFixed(1) + (fahrenheit ? ' °F' : ' °C'));
  };
  await chr.startNotifications();
}

خطوط متن را از یک دستگاه از نوع سریال دریافت کنید، با اتصال اعلان‌هایی که یک خط را وسط می‌شکنند:

async function listenForLines(server, onLine) {
  var uart = await server.getPrimaryService('6e400001-b5a3-f393-e0a9-e50e24dcca9e');
  var tx = await uart.getCharacteristic('6e400003-b5a3-f393-e0a9-e50e24dcca9e');
  if (!tx.properties.notify && !tx.properties.indicate) throw new Error('This device does not stream data');
  var decoder = new TextDecoder(), buffer = '';
  tx.addEventListener('characteristicvaluechanged', function (e) {
    buffer += decoder.decode(e.target.value, { stream: true });
    var lines = buffer.split('\n');
    buffer = lines.pop();
    lines.forEach(onLine);
  });
  await tx.startNotifications();
  return tx;
}

نکته‌ها: امضای خام wbNotify(requestId, deviceId, service, characteristic, enable). مشخصه‌ای که فقط از indication پشتیبانی می‌کند خودکار با indication مشترک می‌شود. مشخصه‌ای که نمی‌تواند اعلان بدهد با NotSupportedError رد می‌شود. oncharacteristicvaluechanged = fn هم کار می‌کند. بعد از قطع اتصال، دوباره مشترک شوید.

wbRead پل#

window.WebToApk.wbRead(requestId: String, deviceId: String, service: String, characteristic: String)

انتقال خام برای WebBluetoothPolyfill. صفحه هرگز این‌ها را مستقیم فراخوانی نمی‌کند؛ مثل مرورگر، navigator.bluetooth را فراخوانی می‌کند.

به‌جای خودش، با گروهش Web Bluetooth (navigator.bluetooth) توضیح داده می‌شود.

نمونه

انتقال داخلی پشت characteristic.readValue(). صفحه‌ها از navigator.bluetooth استفاده می‌کنند؛ مستقیم این را صدا نزنید.

بازگشتی: (از طریق readValue()) یک Promise از یک DataView؛ characteristic.value هم به‌روز می‌شود. نیازمند: بلوتوث (با ساخت از ZIP یا HTML وقتی کد شما ذکر کند خودش روشن می‌شود navigator.bluetooth).

async function readInfo(server) {
  try {
    var battery = await server.getPrimaryService('battery_service');
    var level = await (await battery.getCharacteristic('battery_level')).readValue();
    show('battery', level.getUint8(0) + '%');

    var info = await server.getPrimaryService('device_information');
    var maker = await (await info.getCharacteristic('manufacturer_name_string')).readValue();
    show('maker', new TextDecoder().decode(maker));
  } catch (err) {
    // NotFoundError: the device has no such service/characteristic
    // NotSupportedError: read rejected by the device   NetworkError: not connected / read failed
    show('info', err.message);
  }
}

حسگری را که فقط خواندنی است به‌صورت دوره‌ای بخوانید؛ اینجا ویژگی استاندارد دما (0x2a6e، به صدم درجه):

var pollTimer = null;

async function startTemperaturePolling(server) {
  var env = await server.getPrimaryService('environmental_sensing');
  var temp = await env.getCharacteristic(0x2a6e);
  pollTimer = setInterval(async function () {
    try {
      var v = await temp.readValue();
      show('temp', (v.getInt16(0, true) / 100).toFixed(1) + ' °C');
    } catch (err) {
      clearInterval(pollTimer);          // NetworkError once the sensor is gone
      show('temp', err.message);
    }
  }, 5000);
}

پنل «درباره این دستگاه» را با شروع چند خواندن همزمان پر کنید؛ آن‌ها خودکار یکی‌یکی اجرا می‌شوند:

async function readDeviceInfo(server) {
  var info = await server.getPrimaryService('device_information');
  var names = ['model_number_string', 'serial_number_string', 'firmware_revision_string'];
  var decoder = new TextDecoder();
  var results = await Promise.allSettled(names.map(async function (name) {
    var chr = await info.getCharacteristic(name);
    return decoder.decode(await chr.readValue());
  }));
  results.forEach(function (r, i) {
    show(names[i], r.status === 'fulfilled' ? r.value : '-');   // missing ones reject with NotFoundError
  });
}

نکات: امضای خام wbRead(requestId, deviceId, service, characteristic). خواندن، نوشتن و تغییرات اعلان روی یک دستگاه به همان ترتیبی که فراخوانی می‌کنید، یکی‌یکی اجرا می‌شوند؛ پس می‌توانید چند مورد را بدون انتظار بین آن‌ها پشت سر هم بفرستید.

wbRequestDevice پل#

window.WebToApk.wbRequestDevice(requestId: String, optionsJson: String)

انتقال خام برای WebBluetoothPolyfill. صفحه هرگز این‌ها را مستقیم فراخوانی نمی‌کند؛ مثل مرورگر، navigator.bluetooth را فراخوانی می‌کند.

به‌جای خودش، با گروهش Web Bluetooth (navigator.bluetooth) توضیح داده می‌شود.

نمونه

انتقال داخلی پشت navigator.bluetooth.requestDevice(options): انتخابگر دستگاه اپلیکیشن را نشان می‌دهد. صفحه‌ها از navigator.bluetooth.requestDevice استفاده می‌کنند؛ مستقیم این را صدا نزنید.

بازگشتی: (از طریق requestDevice) یک Promise از یک BluetoothDevice {id, name, gatt}. نیازمند: بلوتوث (با ساخت از ZIP یا HTML وقتی کد شما navigator.bluetooth را ذکر کند خودش روشن می‌شود) و مجوز دستگاه‌های اطراف، که بار اول پرسیده می‌شود.

document.getElementById('pick').addEventListener('click', async function () {
  if (!navigator.bluetooth) { showStatus('Bluetooth is not available in this app.'); return; }
  try {
    var device = await navigator.bluetooth.requestDevice({
      filters: [{ services: ['cycling_power'] }, { namePrefix: 'KICKR' }],
      optionalServices: ['battery_service']
    });
    showStatus('Picked ' + (device.name || device.id));
  } catch (err) {
    if (err.name === 'NotFoundError') showStatus('No device chosen.');   // also: Bluetooth off
    else showStatus(err.name + ': ' + err.message);                     // SecurityError = permission
  }
});

برای فهرست کردن همه دستگاه‌های اطراف بدون فیلتر: requestDevice({ acceptAllDevices: true }).

یک دستگاه سفارشی را با نام دقیق یا پیشوند نام انتخاب کنید و سرویس ۱۲۸ بیتی خودش را فهرست کنید تا بعد از اتصال قابل استفاده باشد:

async function pickLamp() {
  try {
    return await navigator.bluetooth.requestDevice({
      filters: [{ name: 'Desk Lamp' }, { namePrefix: 'LAMP-' }],   // either one matches
      optionalServices: ['0000ffe0-0000-1000-8000-00805f9b34fb']
    });
  } catch (err) {
    if (err.name === 'SecurityError') showStatus('Allow Nearby devices to find your lamp.');
    else if (err.name !== 'NotFoundError') showStatus(err.message);  // NotFoundError = cancelled
    return null;
  }
}

دستگاه را یک بار در هر نشست درخواست کنید و در ضربه‌های بعدی دوباره از آن استفاده کنید (getDevices() در دسترس نیست، پس شیئی را که گرفتید نگه دارید):

var scale = null;

document.getElementById('weigh').addEventListener('click', async function () {
  if (!navigator.bluetooth) { showStatus('Bluetooth is not available in this app.'); return; }
  try {
    if (!scale) {
      scale = await navigator.bluetooth.requestDevice({
        filters: [{ services: ['weight_scale'], namePrefix: 'MI' }]   // both keys must match
      });
    }
    var server = await scale.gatt.connect();
    var svc = await server.getPrimaryService('weight_scale');
    var chr = await svc.getCharacteristic('weight_measurement');
    chr.addEventListener('characteristicvaluechanged', onWeight);
    await chr.startNotifications();
  } catch (err) {
    showStatus(err.name + ': ' + err.message);
  }
});

نکته‌ها: امضای خام wbRequestDevice(requestId, optionsJson)؛ پاسخ به polyfill برمی‌گردد، نه به صفحه. requestDevice را از یک ضربه صدا بزنید. فیلترها از services (باید تبلیغ شده باشد)، name (دقیق) و namePrefix استفاده می‌کنند؛ یک فیلتر وقتی همه‌ی کلیدهایش مطابقت دارند منطبق می‌شود و دستگاه وقتی هر فیلتری منطبق شود منطبق است. نه filters و نه acceptAllDevices → TypeError. لغو انتخابگر با NotFoundError رد می‌شود. انتخابگر ۳۰ ثانیه اسکن می‌کند. device.id نشانی بلوتوث دستگاه است.

wbWrite پل#

window.WebToApk.wbWrite(

انتقال خام برای WebBluetoothPolyfill. صفحه هرگز این‌ها را مستقیم فراخوانی نمی‌کند؛ مثل مرورگر، navigator.bluetooth را فراخوانی می‌کند.

به‌جای خودش، با گروهش Web Bluetooth (navigator.bluetooth) توضیح داده می‌شود.

نمونه

انتقال داخلی پشت characteristic.writeValue()، writeValueWithResponse() و writeValueWithoutResponse(). صفحه‌ها از navigator.bluetooth استفاده می‌کنند؛ مستقیم این را صدا نزنید.

مقدار بازگشتی: (از طریق ویژگی) یک Promise از undefined. نیازمند: بلوتوث (وقتی کد شما به آن اشاره کند، در ساخت ZIP یا HTML روشن می‌شود navigator.bluetooth).

با پاسخ (writeValue هم همین‌طور است): وقتی دستگاه نوشتن را تأیید کند، انجام می‌شود.

async function setTargetPower(server, watts) {
  var ftms = await server.getPrimaryService('fitness_machine');
  var cp = await ftms.getCharacteristic('00002ad9-0000-1000-8000-00805f9b34fb'); // FTMS control point
  await cp.writeValueWithResponse(new Uint8Array([0x00]));                       // request control
  var cmd = new DataView(new ArrayBuffer(3));
  cmd.setUint8(0, 0x05);                 // set target power
  cmd.setInt16(1, watts, true);
  await cp.writeValueWithResponse(cmd);
}

بدون پاسخ: سریع‌تر، برای جریان‌هایی که گم شدن یک بسته مهم نیست.

async function sendText(characteristic, text) {
  if (!characteristic.properties.writeWithoutResponse) throw new Error('This characteristic needs writeValue');
  await characteristic.writeValueWithoutResponse(new TextEncoder().encode(text));
}

یک فرمان متنی بلند را در تکه‌های ۲۰ بایتی بفرستید. اپلیکیشن از دستگاه اندازه‌ی بسته‌ی بزرگ‌تر نمی‌خواهد، پس ۲۰ بایت چیزی است که در یک نوشتن جا می‌شود:

async function sendLine(rx, text) {
  var bytes = new TextEncoder().encode(text + '\n');
  for (var i = 0; i < bytes.length; i += 20) {
    await rx.writeValueWithoutResponse(bytes.subarray(i, i + 20));
  }
}
// sendLine(rxCharacteristic, 'SET COLOR 255 120 0 BRIGHTNESS 80');

نکات: امضای خام wbWrite(requestId, deviceId, service, characteristic, valueBase64, withResponse). مقدار می‌تواند ArrayBuffer، هر آرایه تایپ‌شده، DataView یا آرایه ساده‌ای از بایت‌ها باشد؛ هر چیز دیگر TypeError پرتاب می‌کند. خطاها: NotSupportedError (نوشتن رد شد)، NetworkError (نوشتن ناموفق بود یا اتصال برقرار نیست)، NotFoundError.