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.
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, thereturnValue
will be false witherrorCode:1003
anderrorText: 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
},
});
};