BLE GATT

The BLE GATT API allows you to use webOS TV's BLE GATT client function. For devices that adopt webOS TV 24, you can use the BLE GATT API.

To use the BLE GATT API, your app should acquire user permission. If the app calls the API without the permission, a pop up to get user permission will appear, and only after the user choose to give the permission, the app can use the BLE GATT API. Once acquired, the permission of the app remains valid until the user deletes it and no pop up for getting permission will appear till then. If the user explicitly deletes the permission, the app cannot use the BLE GATT API, and only after the user gives the permission again, it can use the API again.

The operations of the API, introduced in this document, are verified with an Android mobile phone working as a GATT server, and the Android app for functioning as a GATT server is BLE Tool available on Google Play.

See BLE GATT API Reference for how to use each method of the BLE GATT API.

Note:

This API is available on webOS TV 24.

Overview

This guide describes an example on how a TV device can work as a GATT client. If any methods explained here on this document are called on a webOS TV platform that does not support the API, returnValue will be returned as false with errorText as Unknown method.

// When called BLE APIs from a webOS version that is not supported
 
var isEnabledHandle = webOS.service.request(
  "luna://com.webos.service.blegatt",
  {
    method: "isEnabled",
    parameters: {
      subscribe: true,
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
    },
    onFailure: function (inError) {
      // "errorCode": -1,
      // "errorText": "Unknown method "isEnabled" for category "/""
      console.log("[" + inError.errorCode + "]: " + inError.errorText); // To-Do something
      return;
    },
  }
);

While there is a problem in connecting the Bluetooth hardware or the Bluetooth hardware is not found, if any methods explained here on this document are called, returnValue will be returned as false with errorText saying Bluetooth adapter is not available.

// If there is a problem with the connection of the Bluetooth hardware or the
// hardware is missing
var isEnabledHandle = webOS.service.request(
  "luna://com.webos.service.blegatt",
  {
    method: "isEnabled",
    parameters: {
      subscribe: true,
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
    },
    onFailure: function (inError) {
      // "errorCode": 100,
      // "errorText": " Bluetooth adapter is not available"
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
      return;
    },
  }
);

Also, maintaining the BLE connection and all other BLE-related operations, such as read, write, and notify, can be performed while the app is in the foreground. If the app goes into the background or gets terminated, all the BLE connections will be lost. If the app goes back into the foreground or is newly launched, the following procedure in this section should be performed again.

Check Bluetooth adapter

Call the isEnabled method to check if Bluetooth is enabled. If the isEabled return of this method is false, it means the Bluetooth function of the TV device is currently unavailable. The isEnabled method supports subscription, and whenever there is a change to the availability of the Bluetooth function, the updated information is returned.

var isEnabledHandle = webOS.service.request(
  "luna://com.webos.service.blegatt",
  {
    method: "isEnabled",
    parameters: {
      subscribe: true,
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
      // check isEnabled
      if (inResponse.isEnabled === true) {
        isEnabledHandle.cancel(); // cancel subscribe
        // To-Do something
      }
    },
    onFailure: function (inError) {
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
      return;
    },
  }
);

Find BLE devices

To find BLE devices, use the startScan method. If you want to search for a certain type of peripheral devices, include the uuid parameter when calling the method. This uuid parameter should include values that indicates GATT services. Here are some points that you need to keep in mind about the startScan method.

  • If you do not stop scanning by calling the stopScan method after calling this method, the scanning will automatically stop after 60 seconds.
  • You can check whether scanning is currently underway from the return value of the getState method.
  • If the startScan method is called again while scanning is already underway, the returnValue will be false with errorCode:1003 and errorText: Scan is already in progress.
stopScan = function () {
  var request = webOS.service.request("luna://com.webos.service.blegatt", {
    method: "stopScan",
    parameters: {},
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
      // To-Do something
    },
    onFailure: function (inError) {
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
    },
  });
};
 
startScan = function () {
  var scanHandle = webOS.service.request("luna://com.webos.service.blegatt", {
    method: "startScan",
    parameters: {
      subscribe: true,
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
      if (inResponse.devices) {
        for (let i = 0; i < inResponse.devices.length; i++) {
          if (inResponse.devices[i].address === targetAddress) {
            // find target device
            scanHandle.cancel(); // cancel subscribe
            stopScan(); // stop scanning
            // To-Do something
            break;
          }
        }
      }
    },
    onFailure: function (inError) {
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
    },
  });
};

The following is an example of checking if scanning is currently underway using the getState method.

getState = function () {
  var request = webOS.service.request("luna://com.webos.service.blegatt", {
    method: "getState",
    parameters: {
      subscribe: true,
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
      if (inResponse.isScan === true) {
        // scanning in progress
        // To-Do something
      } else {
        // timeout or stopScan is called somewhere else
        // Scan request available status
        // To-Do something
      }
    },
    onFailure: function (inError) {
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
    },
  });
};

Connect to a GATT server

The first step of interaction with a BLE device is connecting to the device, or more specifically, connecting to the GATT server of the device. To connect to the GATT server of the BLE device, use the client/connect method. This method receives the mac address of the GATT server as a parameter.

connect = function () {
  var request = webOS.service.request("luna://com.webos.service.blegatt", {
    method: "client/connect",
    parameters: {
      subscribe: true,
      address: "f7:0e:a1:0b:e7:84", // your taeget device address
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
      // To-Do something
      handleEvent(inResponse);
    },
    onFailure: function (inError) {
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
    },
  });
};

The call in the previous example makes connection to the GATT server hosted by the BLE device, and webOS TV can work as a GATT client. From the subscription return, you can check the connection state and additional operation result of the GATT client.

handleEvent = function (inResponse) {
  console.log("Result: " + JSON.stringify(inResponse));
  var event = inResponse.event;
  var values = inResponse.vales;
 
  // check connection state
  if (event === "onConnectionStateChange") {
    if (values) {
      if (values.address === targetAddress && values.connected === true) {
        // device is connected
        // To-Do something
      } else {
        // device is disconnected
        // To-Do something
      }
    }
  }
  // new services discovered
  else if (event === "onServicesDiscovered") {
    // To-Do something
  }
  // Characteristic notification
  else if (event === "onCharacteristicChanged") {
    // To-Do something
  }
  // Result of a characteristic read operation
  else if (event === "onCharacteristicRead") {
    // To-Do something
  }
  // Result of a characteristic write operation
  else if (event === "onCharacteristicWrite") {
    // To-Do something
  }
  // Result of a descriptor read operation
  else if (event === "onDescriptorRead") {
    // To-Do something
  }
  // Result of a descriptor write operation
  else if (event === "onDescriptorWrite") {
    // To-Do something
  }
};

Through the event delivered as the subscription return of the client/connect method, the operation of the GATT client is delivered, and depending on the event received, you can add appropriate follow-up operations.

Discover/Get GATT server services

To read or write data into the characteristic of the GATT server, searching for the supported services of the server should be performed first. It can be done by calling the client/discoverServices method, and when the searching is complete, an onServicesDiscovered event is delivered as the subscription return of the the client/connect method.

discoverServices = function () {
  var request = webOS.service.request("luna://com.webos.service.blegatt", {
    method: " client/discoverServices",
    parameters: {
      address: "f7:0e:a1:0b:e7:84",
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
      // To-Do something
    },
    onFailure: function (inError) {
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
    },
  });
};
handleEvent = function (inResponse) {
  var event = inResponse.event;
  // Result of discover services operation
  if (event === "onServicesDiscovered") {
    // To-Do something
  }
};

The information about the discovered server services can be checked by calling the client/getServices method, and the information is delivered as the subscription return of the client/connect method together with the onServicesDiscovered event.

getServices = function () {
  var request = webOS.service.request("luna://com.webos.service.blegatt", {
    method: " client/getServices",
    parameters: {
      address: "f7:0e:a1:0b:e7:84",
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
      // To-Do something
    },
    onFailure: function (inError) {
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
    },
  });
};
handleEvent = function (inResponse) {
  var event = inResponse.event;
  var values = inResponse.vales;
  // Result of a characteristic read operation
  if (event === "onServicesDiscovered") {
    // To-Do something
    console.log("values: " + JSON.stringify(values));
  }
};

Read/Write BLE characteristic

After webOS TV is connected to the GATT service and discovers a service, it can read and write characteristic (if supported). It is possible to read characteristic using the client/readCharacteristic method, and the read information is delivered as the subscription return of the client/connect method with the onCharacteristicRead event.

readCharacteristic = function () {
  var request = webOS.service.request("luna://com.webos.service.blegatt", {
    method: " client/readCharacteristic",
    parameters: {
      address: "f7:0e:a1:0b:e7:84",
      characteristic: "0000fff4-0000-1000-8000-00805f9b34fb",
      service: "0000fff0-0000-1000-8000-00805f9b34fb",
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
      // To-Do something
    },
    onFailure: function (inError) {
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
    },
  });
};
handleEvent = function (inResponse) {
  var event = inResponse.event;
  var values = inResponse.vales;
  // Result of a characteristic read operation
  if (event === "onCharacteristicRead") {
    // To-Do something
    console.log("values: " + JSON.stringify(values));
  }
};

Using the client/writeCharacteristic method, you can write a value to the characteristic, and the result of writing is delivered as the subscription return of the client/connect method with the onCharacteristicWrite event. However, if it is necessary to write data into a certain characteristic continuously, call the client/writeCharacteristic method first. Then, after receiving an onCharacteristicWrite event as the subscription return of the client/connect method, write the data using the client/writeCharacteristic method.

writeCharacteristic = function () {
  var request = webOS.service.request("luna://com.webos.service.blegatt", {
    method: " client/writeCharacteristic",
    parameters: {
      address: "f7:0e:a1:0b:e7:84",
      characteristic: "0000fff4-0000-1000-8000-00805f9b34fb",
      service: "0000fff0-0000-1000-8000-00805f9b34fb",
      value: { bytes: [92, 91, 93] },
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
      // To-Do something
    },
    onFailure: function (inError) {
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
    },
  });
};
var availableWriteCharacteristic = true;
var pollingCount = 0;
var timerId = 0;
 
handleEvent = function (inResponse) {
  var event = inResponse.event;
  var values = inResponse.vales;
  // Result of a characteristic write operation
  if (event === "onCharacteristicWrite") {
    console.log("values: " + JSON.stringify(values));
    availableWriteCharacteristic = ture;
  }
};
 
checkWriteCharacteristic = function () {
  // check to available writeCharacteristic
  if (availableWriteCharacteristic === true) {
    // clear timer
    clearTimeout(timerId);
    timerId = 0;
    pollingCount = 0;
    // write characteristic
    availableWriteCharacteristic = false;
    writeCharacteristic();
  } else {
    // need to polling to check write characteristic
    // This is an example implemented in the case of repetition three times.
    if (pollingCount < 3) {
      pollingCount++;
      timerId = setTimeout(function () {
        checkWriteCharacteristic();
      }, 100); // period is 100ms
    } else {
      clearTimeout(timerId);
      timerId = 0;
      pollingCount = 0;
    }
  }
};

Receive GATT notification

You can turn on notification about changes to a certain characteristic. Use the setCharacteristicNotification method to turn on notification about a certain characteristic. If there is a change to a characteristic on a remote device while notification about the characteristic is enabled, an onCharacteristicChanged event and the changed information are delivered as the subscription return of the client/connect method.

setCharacteristicNotification = function () {
  var request = webOS.service.request("luna://com.webos.service.blegatt", {
    method: " client/setCharacteristicNotification",
    parameters: {
      address: "f7:0e:a1:0b:e7:84",
      characteristic: "0000fff4-0000-1000-8000-00805f9b34fb",
      service: "0000fff0-0000-1000-8000-00805f9b34fb",
      enable: true,
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
      // To-Do something
    },
    onFailure: function (inError) {
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
    },
  });
};
handleEvent = function (inResponse) {
  var event = inResponse.event;
  var values = inResponse.vales;
  // Result of a characteristic read operation
  if (event === " onCharacteristicChanged") {
    // To-Do something
    console.log("values: " + JSON.stringify(values.changed));
  }
};

If necessary, to receive notifications, you need to change the configuration of Client Characteristic Configuration Descriptor (CCCD) to enable notification. It can be done using the notification parameter of the client/writeDescriptor method.

setCharacteristicNotification = function () {
  var request = webOS.service.request("luna://com.webos.service.blegatt", {
    method: "writeDescriptor",
    parameters: {
      address: "4f:6b:40:db:5b:b2",
      descriptor: "00002902-0000-1000-8000-00805f9b34fb", // CCCD
      characteristic: "0000fff4-0000-1000-8000-00805f9b34fb",
      service: "0000fff0-0000-1000-8000-00805f9b34fb",
      notification: "ENABLE_NOTIFICATION_VALUE",
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
      // To-Do something
    },
    onFailure: function (inError) {
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
    },
  });
};

Close GATT connection

When the use of the BLE device is complete in an app, the connection with the device should be terminated by calling the client/disconnect method.

disconnect = function () {
  var request = webOS.service.request("luna://com.webos.service.blegatt", {
    method: " client/disconnect",
    parameters: {
      address: "f7:0e:a1:0b:e7:84",
    },
    onSuccess: function (inResponse) {
      console.log("Result: " + JSON.stringify(inResponse));
      // To-Do something
    },
    onFailure: function (inError) {
      console.log("[" + inError.errorCode + "]: " + inError.errorText);
      // To-Do something
    },
  });
};
No Headings