Bluetooth

במסמך הזה מוסבר איך להשתמש בממשקי ה-API של Bluetooth, של Bluetooth ושל Bluetooth עם צריכת אנרגיה נמוכה כדי לתקשר עם מכשירי Bluetooth ו-Bluetooth עם צריכת אנרגיה נמוכה.

מידע רקע על Bluetooth זמין במפרטים הרשמיים של Bluetooth.

דרישות למניפסט

באפליקציות Chrome שבהן נעשה שימוש ב-Bluetooth, צריך להוסיף את הערך Bluetooth למניפסט ולציין, אם צריך, את מזהי ה-UUID של הפרופילים, הפרוטוקולים והשירותים שרוצים להטמיע, יחד עם האפשרות להטמיע אותם באמצעות ממשקי ה-socket או Low Energy API.

לדוגמה, בהטמעה של socket:

"bluetooth": {
  "uuids": [ "1105", "1106" ],
  "socket": true
}

ובנוגע ליישום של צריכת אנרגיה נמוכה:

"bluetooth": {
  "uuids": [ "180D", "1809", "180F" ],
  "low_energy": true
}

כדי לגשת רק למצב המתאם, לאתר מכשירים בקרבת מקום ולקבל מידע בסיסי על מכשירים, נדרשת רק הרשומה עצמה:

"bluetooth": {}

פרטי המתאם

קבלת מצב המתאם

כדי לברר את המצב של מתאם ה-Bluetooth, משתמשים בשיטה bluetooth.getAdapterState:

chrome.bluetooth.getAdapterState(function(adapter) {
  console.log("Adapter " + adapter.address + ": " + adapter.name);
});

התראות מתאם

האירוע bluetooth.onAdapterStateChanged נשלח בכל פעם שמצב המתאם משתנה. ניתן להשתמש בנתון הזה, למשל, כדי לקבוע מתי רדיו המתאם יפעל או כבה.

var powered = false;
chrome.bluetooth.getAdapterState(function(adapter) {
  powered = adapter.powered;
});

chrome.bluetooth.onAdapterStateChanged.addListener(
  function(adapter) {
    if (adapter.powered != powered) {
      powered = adapter.powered;
      if (powered) {
        console.log("Adapter radio is on");
      } else {
        console.log("Adapter radio is off");
      }
    }
  });

פרטי המכשיר

הצגת רשימה של מכשירים מוכרים

כדי לקבל רשימה של המכשירים הידועים למתאם ה-Bluetooth, משתמשים בשיטה bluetooth.getDevices:

chrome.bluetooth.getDevices(function(devices) {
  for (var i = 0; i < devices.length; i++) {
    console.log(devices[i].address);
  }
});

כל המכשירים מוחזרים, כולל מכשירים מותאמים ומכשירים שהתגלו לאחרונה. הוא לא יתחיל לגלות מכשירים חדשים (ראו גילוי מכשירים בקרבת מקום).

מתקבלות התראות במכשיר

במקום להתקשר שוב ושוב ל-bluetooth.getDevices, תוכלו להשתמש באירועים של bluetooth.onDeviceAdded, bluetooth.onDeviceChanged ו-bluetooth.onDeviceRemoved כדי לקבל התראות.

האירוע bluetooth.onDeviceAdded נשלח בכל פעם שהמתאם מזהה מכשיר או יוצר חיבור למתאם:

chrome.bluetooth.onDeviceAdded.addListener(function(device) {
  console.log(device.address);
});

הוספת האזנה לאירוע הזה לא תגרום להפעלה של זיהוי מכשירים (מידע נוסף מופיע בקטע איתור מכשירים בקרבת מקום).

שינויים במכשירים, כולל מכשירים שזוהו בעבר כמתאימים, מקבלים התראה באמצעות האירוע bluetooth.onDeviceChanged:

chrome.bluetooth.onDeviceChanged.addListener(function(device) {
  console.log(device.address);
});

לבסוף, האירוע bluetooth.onDeviceRemoved נשלח בכל פעם שמסירים מהמערכת מכשיר מותאם או שמכשיר שנמצא לא נראה לאחרונה:

chrome.bluetooth.onDeviceRemoved.addListener(function(device) {
  console.log(device.address);
});

מחפש מכשירים בקרבת מקום

כדי להתחיל לגלות מכשירים בקרבת מקום, יש להשתמש בשיטה bluetooth.startDiscovery. הגילוי הנאות עשוי להיות אינטנסיבי, לכן מומלץ להפעיל את bluetooth.stopDiscovery בסיום.

עליך להפעיל את bluetooth.startDiscovery בכל פעם שהאפליקציה צריכה לאתר מכשירים בקרבת מקום. אין צורך לקבוע את תנאי השיחה במאפיין discovering של bluetooth.AdapterState. הקריאה תסתיים גם אם אפליקציה אחרת מוצאת מכשירים בקרבת מקום. היא תבטיח שהמתאם ימשיך לבצע גילוי גם כשהאפליקציה האחרת תפסיק לפעול.

מידע על כל מכשיר חדש שהתגלה מתקבל באמצעות האירוע bluetooth.onDeviceAdded. לגבי מכשירים שכבר התגלו לאחרונה, או כאלה שהותאמו או שהתחברו אליהם בעבר, האירוע לא יישלח. במקום זאת, כדאי לקרוא ל-bluetooth.getDevices כדי לקבל את המידע הנוכחי, ולהשתמש באירוע bluetooth.onDeviceChanged כדי לקבל הודעה על שינויים במידע הזה שנובעים מהגילוי.

דוגמה:

var device_names = {};
var updateDeviceName = function(device) {
  device_names[device.address] = device.name;
};
var removeDeviceName = function(device) {
  delete device_names[device.address];
}

// Add listeners to receive newly found devices and updates
// to the previously known devices.
chrome.bluetooth.onDeviceAdded.addListener(updateDeviceName);
chrome.bluetooth.onDeviceChanged.addListener(updateDeviceName);
chrome.bluetooth.onDeviceRemoved.addListener(removeDeviceName);

// With the listeners in place, get the list of devices found in
// previous discovery sessions, or any currently active ones,
// along with paired devices.
chrome.bluetooth.getDevices(function(devices) {
  for (var i = 0; i < devices.length; i++) {
    updateDeviceName(devices[i]);
  }
});

// Now begin the discovery process.
chrome.bluetooth.startDiscovery(function() {
  // Stop discovery after 30 seconds.
  setTimeout(function() {
    chrome.bluetooth.stopDiscovery(function() {});
  }, 30000);
});

אם המשתמש יכבה את רדיו ה-Bluetooth, כל פעילויות הגילוי יסתיימו ולא יחודשו אוטומטית כשהרדיו יופעל. אם זה חשוב לאפליקציה שלכם, אתם צריכים לצפות באירוע bluetooth.onAdapterStateChanged. אם המאפיין discovering ישתנה ל-false, תצטרכו להפעיל שוב את bluetooth.startDiscovery באפליקציה כדי להמשיך. היזהרו כתוצאה מזיהוי אינטנסיבי של המשאבים.

זיהוי של מכשירים

יש כמה אפשרויות לזיהוי מכשירים שהוחזרו על ידי bluetooth.getDevices והאירועים הקשורים אליהם.

אם המכשיר תומך במפרט מזהה המכשיר של Bluetooth, כמה מאפיינים נוספים לאובייקט Device (מכשיר) שמכיל את השדות שהוגדרו במפרט הזה. דוגמה:

chrome.bluetooth.getDevices(function(devices) {
  for (var i = 0; i < devices.length; i++) {
    if (devices[0].vendorIdSource != undefined) {
      console.log(devices[0].address + ' = ' +
                  devices[0].vendorIdSource + ':' +
                  devices[0].vendorId.toString(16) + ':' +
                  devices[0].productId.toString(16) + ':' +
                  devices[0].deviceId.toString(16));
    }
  }
});

מפרט מזהה המכשיר בדרך כלל מספיק כדי לזהות דגם מסוים, ואפילו גרסה קודמת של מכשיר מטעם ספק. במקרים שבהם היא לא נמצאת, צריך להסתמך על מידע לגבי הסיווג או הסוג של המכשיר, בשילוב עם קידומת היצרן ב-address.

רוב מכשירי ה-Bluetooth מספקים את פרטי סוג המכשיר כשדה ביט, שמפורש בהתאם למסמך Baseband Assigned Numbers. שדה הביט הזה זמין בנכס deviceClass.

chrome.bluetooth.getDevices(function(devices) {
  for (var i = 0; i < devices.length; i++) {
    if (devices[0].vendorIdSource != undefined) {
      console.log(devices[0].address + ' = ' +
                  devices[0].deviceClass.toString(16));
    }
  }
});

ניתוח השדה יכול להיות מורכב, לכן Chrome מטפל בזה ומגדיר את השדה type בסוגי המכשירים הנפוצים ביותר. במקומות שבהם האפשרות הזו לא זמינה או שהיא לא מתאימה לצרכים שלכם, תצטרכו לנתח את deviceClass בעצמכם.

chrome.bluetooth.getDevices(function(devices) {
  for (var i = 0; i < devices.length; i++) {
    if (devices[0].vendorIdSource != undefined) {
      console.log(devices[0].address + ' = ' + devices[0].type);
    }
  }
});

שימוש ב-RFCOMM וב-L2CAP

אפליקציות Chrome יכולות ליצור חיבורים לכל מכשיר שתומך בשירותי RFCOMM או L2CAP. זה כולל את רוב מכשירי ה-Bluetooth הקלאסיים בשוק.

מתבצע חיבור לשקע חשמל

כדי ליצור חיבור למכשיר, אתה זקוק לשלושה דברים. שקע ליצירת החיבור שנוצר באמצעות bluetoothSocket.create, כתובת המכשיר שאליו רוצים להתחבר וה-UUID של השירות עצמו.

לפני יצירת החיבור, עליך לוודא שהמתאם מודע למכשיר באמצעות bluetooth.getDevice או באמצעות ממשקי ה-API לגילוי המכשיר.

המידע הנחוץ כדי ליצור את החיבור הבסיסי, כולל האם להשתמש בפרוטוקול RFCOMM או L2CAP, ואיזה ערוץ או PSM התקבלו באמצעות גילוי SDP במכשיר.

דוגמה:

var uuid = '1105';
var onConnectedCallback = function() {
  if (chrome.runtime.lastError) {
    console.log("Connection failed: " + chrome.runtime.lastError.message);
  } else {
    // Profile implementation here.
  }
};

chrome.bluetoothSocket.create(function(createInfo) {
  chrome.bluetoothSocket.connect(createInfo.socketId,
    device.address, uuid, onConnectedCallback);
});

מומלץ לשמור כינוי ל-socketId, כדי שאפשר יהיה לשלוח נתונים (bluetoothSocket.send) לשקע הזה מאוחר יותר.

קבלה ושליחה מ-socket

קבלת נתונים משקע ושליחה אליו מתבצעת באמצעות ArrayBuffer אובייקטים. למידע נוסף על ArrayBuffer, קראו את הסקירה הכללית, מערכים מסוג JavaScript הקלידים ואת המדריך איך להמיר את ArrayBuffer ל-String וממנו.

כדי לשלוח נתונים שיש לך ב-arrayBuffer, צריך להשתמש ב-bluetoothSocket.send:

chrome.bluetoothSocket.send(socketId, arrayBuffer, function(bytes_sent) {
  if (chrome.runtime.lastError) {
    console.log("Send failed: " + chrome.runtime.lastError.message);
  } else {
    console.log("Sent " + bytes_sent + " bytes")
  }
})

בניגוד לשיטה לשליחת נתונים, הנתונים מתקבלים באירוע (bluetoothSocket.onReceive. השקעים נוצרים ללא השהיה (ראו bluetoothSocket.setPaused), כך שה-listener של האירוע הזה מתווסף בדרך כלל בין bluetoothSocket.create ל-bluetoothSocket.connect.

chrome.bluetoothSocket.onRecieve.addListener(function(receiveInfo) {
  if (receiveInfo.socketId != socketId)
    return;
  // receiveInfo.data is an ArrayBuffer.
});

מתקבלות שגיאות שקע וניתוק

כדי לקבל התראות על שגיאות socket, כולל ניתוק, צריך להוסיף האזנה לאירוע bluetoothSocket.onReceiveError.

chrome.bluetoothSocket.onReceiveError.addListener(function(errorInfo) {
  // Cause is in errorInfo.error.
  console.log(errorInfo.errorMessage);
});

התנתקות משקע

כדי לנתק את החיבור ולנתק את השקע, צריך להשתמש בקישור bluetoothSocket.disconnect.

chrome.bluetoothSocket.disconnect(socketId);

שירותי הוצאה לאור

בנוסף ליצירת חיבורים יוצאים למכשירים, אפליקציות Chrome יכולות לפרסם שירותים שבהם ניתן להשתמש בכל מכשיר שתומך ב-RFCOMM או ב-L2CAP.

האזנה ב-socket

יש תמיכה בשני סוגים של שירותים שפורסמו. RFCOMM הוא הסוג הנפוץ ביותר, והוא מכסה את רוב המכשירים והפרופילים:

var uuid = '1105';
chrome.bluetoothSocket.create(function(createInfo) {
  chrome.bluetoothSocket.listenUsingRfcomm(createInfo.socketId,
    uuid, onListenCallback);
});

L2CAP הוא הסוג השני, והוא מכסה סוגי מכשירים אחרים ושימושים ספציפיים לספקים, כמו העלאת קושחה.

var uuid = '0b87367c-f188-47cd-bc20-a5f4f70973c6';
chrome.bluetoothSocket.create(function(createInfo) {
  chrome.bluetoothSocket.listenUsingL2cap(createInfo.socketId,
    uuid, onListenCallback);
});

בשני המקרים, ניתן להעביר ערך אופציונלי של bluetoothSocket.ListenOptions כדי להקצות ערוץ או PSM ספציפיים. הקריאה החוזרת (callback) מצביעה על שגיאה עד chrome.runtime.lastError, ואם כן, הצלחה. מומלץ לשמור כינוי ל-socketId, כדי שאפשר יהיה לאשר את החיבורים (bluetoothSocket.onAccept) מהשקע הזה מאוחר יותר.

קבלת חיבורי לקוחות

חיבורי לקוח מתקבלים ומועברים לאפליקציה שלכם דרך האירוע bluetoothSocket.onAccept.

chrome.bluetoothSocket.onAccept.addListener(function(acceptInfo) {
  if (info.socketId != serverSocketId)
    return;

  // Say hello...
  chrome.bluetoothSocket.send(acceptInfo.clientSocketId,
    data, onSendCallback);

  // Accepted sockets are initially paused,
  // set the onReceive listener first.
  chrome.bluetoothSocket.onReceive.addListener(onReceive);
  chrome.bluetoothSocket.setPaused(false);
});

הפסקת האישור של חיבורי לקוחות

כדי להפסיק לקבל חיבורי לקוחות ולבטל את הפרסום של השירות, יש להשתמש ב-bluetoothSocket.disconnect.

chrome.bluetoothSocket.disconnect(serverSocketId);

אינטראקציה עם מכשירים עם צריכת אנרגיה נמוכה

Bluetooth עם צריכת אנרגיה נמוכה (Bluetooth Smart) היא טכנולוגיה אלחוטית שמטרתה להפחית את צריכת החשמל. ממשק ה-API של Bluetooth עם צריכת אנרגיה נמוכה מאפשר לאפליקציות להטמיע את התפקיד המרכזי בחיבור LE לציוד היקפי. בקטעים הבאים מוסבר איך לאתר ציוד היקפי עם צריכת אנרגיה נמוכה, להתחבר אליו ולקיים איתו אינטראקציה.

זיהוי ציוד היקפי והתחברות אליו

כמו במכשירי Bluetooth מסורתיים, אפשר לגלות את הציוד ההיקפי של LE באמצעות השיטות שמתוארות במאמר גילוי מכשירים בקרבת מקום . מכשיר LE הופך את עצמו לגלוי על ידי שליחת חבילות נתונים שנקראות 'נתוני פרסום', והמכשיר נמצא במצב פרסום. נתוני הפרסום עשויים לכלול מזהים ייחודיים אוניברסליים (UUID) של שירותים שזמינים במכשיר. אם קיימים, ניתן יהיה לגשת למזהי UUID האלה באמצעות המאפיין uuids של האובייקט bluetooth.Device המתאים.

ברגע שמכשיר LE נמצא, אפשר לחבר אליו את המכשיר bluetoothLowEnergy.connect, כך שהאפליקציה תוכל לתקשר עם השירותים שלה:

chrome.bluetooth.onDeviceAdded.addListener(function(device) {
  var uuid = '0000180d-0000-1000-8000-00805f9b34fb';
  if (!device.uuids || device.uuids.indexOf(uuid) < 0)
    return;

  // The device has a service with the desired UUID.
  chrome.bluetoothLowEnergy.connect(device.address, function () {
    if (chrome.runtime.lastError) {
      console.log('Failed to connect: ' + chrome.runtime.lastError.message);
      return;
    }

    // Connected! Do stuff...
    ...
  });
});

לאחר החיבור, המאפיין connected של האובייקט bluetooth.Device התואם יקבל את הערך true. קריאה ל-bluetoothLowEnergy.connect יוצרת תביעת בעלות על ידי האפליקציה בחיבור הפיזי למכשיר. יכול להיות שיהיה חיבור פיזי למכשיר בלי להפעיל את bluetoothLowEnergy.connect (למשל, דרך אפליקציה אחרת). במקרה כזה, האפליקציה עדיין יכולה לקיים אינטראקציה עם השירותים של המכשיר, אבל היא תמיד צריכה להפעיל bluetoothLowEnergy.connect, כדי למנוע מאפליקציה אחרת לנתק את הקישור הפיזי.

אחרי שמבטלים את החיבור של האפליקציה, אפשר לבטל את תביעת הבעלות שלה על החיבור באמצעות קריאה ל-bluetoothLowEnergy.disconnect:

chrome.bluetoothLowEnergy.disconnect(deviceAddress);

חשוב לזכור שהפעולה הזו לא בהכרח תשבית את הקישור הפיזי למכשיר, כי יכולות להיות אפליקציות אחרות עם חיבורים פעילים למכשיר. לפעמים המכשיר יתנתק מסיבות שלא בשליטת האפליקציה (למשל, אם המכשיר נעלם או התנתק באופן מפורש על ידי המשתמש באמצעות כלי עזר של מערכת ההפעלה). האפליקציה שלך צריכה לתעד את האירוע bluetooth.onDeviceChanged כדי לקבל התראה על שינויים בחיבור, ולהתחבר מחדש במידת הצורך.

לאחר החיבור, המכשיר שבו פועל Chrome יהיה בתפקיד מרכזי כזה, ואילו המכשיר המחובר לרשת אחרת נחשב לתפקיד היקפי. בשלב הזה, האפליקציה יכולה לקיים אינטראקציה עם השירותים במכשיר באמצעות השיטות שמתוארות בקטע הבא. הערה: בשלב הזה ממשקי ה-API לא תומכים בפעולה כציוד היקפי של LE. באפליקציות אפשר להטמיע רק את התפקיד המרכזי.

שירותים, מאפיינים ומתארים

Bluetooth Low Energy מבוסס על פרוטוקול פשוט לבקשת תגובה שנקרא Attribute Protocol (ATT). כשמשתמשים ב-ATT, מכשיר מרכזי מקיים אינטראקציה עם מאפיינים בציוד היקפי, על ידי תאימות לפרופיל Bluetooth מיוחד שנקרא פרופיל מאפיין כללי (GATT). GATT מגדיר את המושגים הבאים:

  • שירות: שירות GATT מייצג אוסף של נתונים והתנהגויות משויכות כדי לבצע פונקציה מסוימת במכשיר. לדוגמה, במוניטור דופק יש בדרך כלל לפחות "שירות דופק" אחד. מידע על שירות GATT כלול באובייקט bluetoothLowEnergy.Service.
  • מאפיין: מאפיין GATT הוא רכיב נתונים בסיסי שמשמש ליצירת שירות GATT, שמכיל ערך לצד מאפיינים שמגדירים את הגישה לערך הזה. לדוגמה, ב"שירות הדופק" יש את המאפיין "מדידת קצב הלב", שמשמש לחישוב ערך הדופק של המשתמש. מידע על מאפיין GATT נכלל באובייקט bluetoothLowEnergy.Characteristic.
  • מתאר: מתאר של מאפיין GATT מכיל מידע נוסף על מאפיין. מידע על מתאר של מאפיין GATT כלול באובייקט bluetoothLowEnergy.Descriptor.

ה-API Bluetooth Low Energy מאפשר לאפליקציות למצוא מידע לגבי השירותים, המאפיינים והתיאורים של המכשיר באמצעות קריאה ל-bluetoothLowEnergy.getServices, ל-bluetoothLowEnergy.getCharacteristics ול-bluetoothLowEnergy.getDescriptors. אפליקציות יכולות לסנן שירותים, מאפיינים ומתארים באמצעות השוואה בין השדה uuid לבין ה-UUID הרצוי של GATT:

chrome.bluetoothLowEnergy.getServices(deviceAddress, function(services) {
  ...
  for (var i = 0; i < services.length; i++) {
    if (services[i].uuid == HEART_RATE_SERVICE_UUID) {
      heartRateService = services[i];
      break;
    }
  }
  ...
});

לכל שירות, מאפיין ומתאר שאפשר לגשת אליהם דרך ה-API מקבלים מזהה מופע ייחודי, שאותו אפשר לקבל באמצעות השדה instanceId. אפשר להשתמש במזהה המכונה הזה כדי לזהות אובייקט GATT ולבצע בו פעולות ספציפיות:

chrome.bluetoothLowEnergy.getCharacteristics(heartRateService.instanceId,
                                             function(chracteristics) {
  ...
  for (var i = 0; i < characteristics.length; i++) {
    if (characteristics[i].uuid == HEART_RATE_MEASUREMENT_UUID) {
      measurementChar = characteristics[i];
      break;
    }
  }
  ...
  chrome.bluetoothLowEnergy.getDescriptors(measurementChar.instanceId,
                                           function(descriptors) {
    ...
  });
});

אירועי שירות

לאחר חיבור המכשיר, Chrome יגלה את השירותים שלו. ברגע שמגלים שירות ומסירים אותו, האפליקציה תקבל את האירועים bluetoothLowEnergy.onServiceAdded ו-bluetoothLowEnergy.onServiceRemoved:

  var initializeService = function(service) {
    if (!service) {
      console.log('No service selected!');
      // Reset UI, etc.
      ...
      return;
    }

    myService = service;

    // Get all the characteristics and descriptors and bootstrap the app.
    ...
  };

  chrome.bluetoothLowEnergy.onServiceAdded.addListener(function(service) {
    if (service.uuid == MY_SERVICE_UUID)
      initializeService(service);
  });

  chrome.bluetoothLowEnergy.onServiceRemoved.addListener(function(service) {
    if (service.instanceId == myService.instanceId)
      initializeService(null);
  });

Chrome מגלה את כל המאפיינים והמתארים של שירות באופן אסינכרוני, ושולח את האירוע bluetoothLowEnergy.onServiceAdded לאחר השלמת הגילוי. אם החיבור לציוד היקפי נפסק, Chrome מסיר את כל השירותים הקשורים ושולח את האירוע bluetoothLowEnergy.onServiceRemoved.

ציוד היקפי מסוים עשוי לשנות את השירותים שלו, למשל, מאפייני השירות עשויים להשתנות או ששירותים יתווספו ויוסרו לגמרי. אפליקציות Chrome יקבלו התראות על השינויים האלה באמצעות האירועים bluetoothLowEnergy.onServiceChanged, bluetoothLowEnergy.onServiceAdded ו-bluetoothLowEnergy.onServiceRemoved.

  chrome.bluetoothLowEnergy.onServiceChanged.addListener(function(service) {
    if (service.instanceId != myService.instanceId)
      return;

    updateMyService(service);
  });

קריאה וכתיבה של ערך של מאפיין

מאפיין GATT מקודד היבט אחד של השירות שלו. אפליקציה מרכזית קוראת, פועלת ומשנה את המצב של השירות לציוד היקפי על סמך ערך של מאפיין. ערך המאפיין הוא רצף של בייטים, והמשמעות שלו מוגדרת על ידי המפרט ברמה העליונה שמגדיר מאפיין מסוים. לדוגמה, הערך של המאפיין מדידת קצב הלב מקודד את הדופק של המשתמש ואת כמות הקלוריות הכוללת שהוא שרף, בעוד שהמאפיין מיקום חיישן הגוף מקודד את המיקום בגוף שבו צריך לענוד את חיישן הדופק.

ב-Chrome פועלת השיטה bluetoothLowEnergy.readCharacteristicValue כדי לקרוא את הערך של מאפיין:

chrome.bluetoothLowEnergy.readCharacteristicValue(chrc.instanceId,
                                                  function(result) {
  if (chrome.runtime.lastError) {
    console.log('Failed to read value: ' + chrome.runtime.lastError.message);
    return;
  }

  var bytes = new Uint8Array(result.value);

  // Do stuff with the bytes.
  ...
});

יש מאפיינים שניתנים לכתיבה, במיוחד כאלה שמתנהגים כ-Control Points (נקודות בקרה), כאשר לכתיבת הערך יש תופעות לוואי. לדוגמה, המאפיין נקודת בקרת קצב הלב משמש להורות לחיישן דופק לאפס את מספר הקלוריות שנשרפו, והוא תומך רק בכתיבה. כדי לעשות זאת, Chrome מספק את השיטה bluetoothLowEnergy.writeCharacteristicValue:

var myBytes = new Uint8Array([ ... ]);
chrome.bluetoothLowEnergy.writeCharacteristicValue(chrc.instanceId,
                                                   myBytes.buffer,
                                                   function() {
  if (chrome.runtime.lastError) {
    console.log('Failed to write value: ' +
                chrome.runtime.lastError.message);
    return;
  }

  // Value is written now.
});

מתארי מאפיינים מתנהגים באותה צורה ויכולים להיות קריאים ו/או ניתנים לכתיבה. ב-Chrome נעשה שימוש בשיטות bluetoothLowEnergy.readDescriptorValue ו-bluetoothLowEnergy.writeDescriptorValue, כדי לקרוא ולכתוב את הערך של מתאר.

כדי לבדוק אם מאפיין מסוים תומך בקריאה או בכתיבה, אפליקציות יכולות לבדוק את השדה properties של אובייקט bluetoothLowEnergy.Characteristic. השדה הזה לא מכיל מידע על דרישות האבטחה לקבלת גישה לערך, אבל הוא כן מתאר באיזו פעולה של ערך המאפיין תומך באופן כללי.

טיפול בהתראות על ערך

חלק מהמאפיינים מציינים את הערך שלהם באמצעות התראות או סימנים. לדוגמה, המאפיין מדידת קצב הלב לא ניתן לקריאה או לכתיבה, אבל הוא שולח עדכונים לגבי הערך הנוכחי שלו במרווחי זמן קבועים. האפליקציות יכולות להאזין להתראות האלה באמצעות האירוע bluetoothLowEnergy.onCharacteristicValueChanged.

  chrome.bluetoothLowEnergy.onCharacteristicValueChanged.addListener(
      function(chrc) {
    if (chrc.instanceId != myCharId)
      return;

    var bytes = new Uint8Array(chrc.value);

    // Do stuff with the bytes.
    ...
  });

גם אם מאפיין תומך בהתראות/אינדיקטורים, הם לא מופעלים כברירת מחדל. האפליקציות צריכות להפעיל את השיטות bluetoothLowEnergy.startCharacteristicNotifications ו-bluetoothLowEnergy.stopCharacteristicNotifications יתחילו או יפסיקו לקבל את האירוע bluetoothLowEnergy.onCharacteristicValueChanged.

  // Start receiving characteristic value notifications.
  var notifying = false;
  chrome.bluetoothLowEnergy.startCharacteristicNotifications(chrc.instanceId,
                                                             function() {
    if (chrome.runtime.lastError) {
      console.log('Failed to enable notifications: ' +
                  chrome.runtime.lastError.message);
      return;
    }

    notifying = true;
  });

  ...

  // No longer interested in notifications from this characteristic.
  if (notifying) {
    chrome.bluetoothLowEnergy.stopCharacteristicNotifications(
        chrc.instanceId);
  }

לאחר הפעלת ההתראות, האפליקציה תקבל את הערך bluetoothLowEnergy.onCharacteristicValueChanged בכל פעם שתתקבל התראה או אינדיקציה לגבי המאפיין. אם המאפיין תומך בקריאות, האירוע הזה יישלח גם אחרי קריאה מוצלחת ל-bluetoothLowEnergy.readCharacteristicValue. כך האפליקציות יכולות לאחד את זרימת הבקרה של עדכון ערך שמופעל על ידי בקשת קריאה והתראות:

  chrome.bluetoothLowEnergy.onCharacteristicValueChanged.addListener(
      function(chrc) {
    // Process the value.
    ...
  });

  chrome.bluetoothLowEnergy.startCharacteristicNotifications(chrc.instanceId,
                                                             function() {
    // Notifications started. Read the initial value.
    chrome.bluetoothLowEnergy.readCharacteristicValue(chrc.instanceId,
                                                      function(result) {
      ...
      // No need to do anything here since onCharacteristicValueChanged
      // will handle it.
    });
  });

אם מאפיין תומך בהתראות, השדה properties שלו יכיל את המאפיין "notify" או את המאפיין "indicate".

הערה: אם אחד מהמאפיינים תומך בהתראות/אינדיקטורים, יהיה לו תיאור "תצורת לקוח", כדי להפעיל/להשבית התראות. Chrome לא מאפשר לאפליקציות לכתוב לתיאור הזה. כדי לשלוט בהתנהגות ההתראות, אפליקציות צריכות להשתמש בשיטות bluetoothLowEnergy.startCharacteristicNotifications ו-bluetoothLowEnergy.stopCharacteristicNotifications.