Easy BLE: Advanced Comms Between a ZeppOS Watch and BLE Peripherals
January 26, 2024
Silver - ZEPP HEALTH

🐦 What is the Easy BLE library?

The Easy BLE library is an advanced BLE management tool for ZeppOS 3.0 watches that features an automated profile generator, a hybrid asynchronous and sequential queue for efficient handling of all operations including writing and reading, user-friendly string-based interactions, seamless auto-conversions of data and addresses, support for multiple data types, and simplified device management through MAC address-centric commands, all designed to enhance usability and streamline BLE communications.

✨️ Easy BLE Interaction Flow

  1. SCAN (OPTIONAL)
  2. CONNECT
  3. BUILD PROFILE
  4. START LISTENER
  5. MANIPULATE
  6. STOP

💡#0: Install and Initialize the Library

You can install the Easy BLE from the NPM registry or download it directly from GitHub.

Easy BLE for ZeppOS 3.0+ [ NPM ]

npm i @silver-zepp/easy-ble

Download from [ GitHub ]

git clone https://github.com/silver-zepp/zeppos-easy-ble.git

📖 Note! Execute the npm install command from your project’s root folder, to add the library from the NPM registry.

// install -> npm i @silver-zepp/easy-ble
import BLEMaster from "@silver-zepp/easy-ble"
const ble = new BLEMaster();

💡#1: Scan for devices

Initially, you want to scan for all devices around and potentially find one or a couple of those that you are looking for.

const scan_success = ble.startScan((scan_result) => {    
     console.log(JSON.stringify(scan_result));
}

This will do two things: Print a found device on each scan tick, giving you an object that looks like this:

{    
"1a:2b:3c:4d:5e:6f": {        
    "dev_name":"ESP32_BLE_PERIPHERAL",        
     "rssi":-58,        
     "service_data_array":[],        
     "vendor_data":""    
     }
}

If the found device includes vendor or service data it will be included and stringified.

The next thing this scan does is populate the dictionary of devices that you can access at any given time with ble.get.devices();

// returns a singular device. MAC = "1A:2B:..."
const device = ble.get.devices()[MAC];
// returns dictionary with all found devices
const devices = ble.get.devices();

{  
  "a1:a2:a3:a4:a5:a6": {    
    // device #1    "dev_name": "my device",    
    "rssi": -92
   },  
  "c1:c2:c3:c4:c5:c6": {    
  // device #2    
  "dev_name": "another device",    
  "rssi": -69,    
  "service_uuid_array": [      "181D"    ],    
  "service_data_array": [  {        
    "uuid": "181D",        
    "service_data": "ff5ad4" // unique info      
  }  ]
}}

As you can tell the second device contains service data that can be used to identify a specific type of device and make your App connect to it straight away if a particular string “ff5ad4” is found. This data can be further decoded.

You can also rely on the UUID but many devices might use the same ones, so it’s not the most reliable fingerprint.

The other method you can use here is ble.get.hasMAC(…). Using it, you can keep your scan running and connect to the device only when it comes in range to handle further interaction like ble.connect(…) but before that, make sure to stop the scan with ble.stopScan().

// the device you are searching for
const MAC = "1A:2B:3C:4D:5E:6F";
// check if dictionary contains a specific device
if (ble.get.hasMAC(MAC)){
   // stop the scan
   ble.stopScan();
   // proceed with the connection
   ble.connect(MAC, (connect_result) => {
       // ...
   }
}

💡#2: Connect to a Device

Now that you have found your device, it’s time to connect to it with ble.connect(…). It takes a mac address as a parameter and receives a connect_result callback, which is an object that looks like this:

{ "connected": false, "status": "disconnected" }

Based on the response you can either proceed to further interaction or retry the connection. Additionally, the connect_result object that is received, contains a [string] status that can better describe the connection failure with strings like “in progress“, “invalid mac“, etc. Leveraging statuses might come in handy when dealing with unstable connections or introducing a reconnection mechanism.

ble.connect(MAC, (connect_result) => {    
// successfully connected    
if (connect_result.connected) {      
    // 1. generate profile        
    // 2. start listener        
    // 3. communicate    
  } else {      
    // handle connection failure        
    console.log('Failed to connect. Status:',  connect_result.status);    
}});

💡#3: Build a Profile

Before communicating with the device you have to build a profile by preparing a simplified services object.

// simplified object that describes a profile
const services = {
   // service #1
   "FF00": {       // service UUID
       "FF02": [], // READ characteristic UUID
       "FF03": ["2902"],   // NOTIFY chara UUID
               // ^--- here's a descriptor UUID
   },
   // ... add other services here if needed
}

And providing it to the generateProfileObject(…) method to do the rest of the job.

// generate a complex profile object just providing services
const profile_object = ble.generateProfileObject(services);

And that’s pretty much it. For the most part, you are ready to communicate with a device.

But we’ll have to look into one additional feature of the generate profile object method – it’s a third (optional) argument (object) that allows you to provide custom permissions per specific UUIDs. You only have to provide these for characteristics and descriptors that require them, and the rest will still be autogenerated.

Most of the time you won’t need to use it but when it comes to complex systems with encrypted communications they might have restricted rules for permissions and you will have to use permissions like READ_ENCRYPTED_MITM.

For this case, the BLE Master library contains a PERMISSIONS table that you can import and use like this:

const permissions = {    
  "char_uuid_1": PERMISSIONS.READ_ENCRYPTED,  
  "desc_uuid_1": PERMISSIONS.WRITE_DESCRIPTOR
};
const profile_object = generateProfileObject(services, permissions);

💡#4: Start the Listener

Now that you have crafted your profile_object, it’s time to feed it to the startListener(…) method

// start listening for the response
// from the watch's backend
ble.startListener(profile_object, (response) => {
   if (response.success){
       // #5: manipulate with
       // characteristics and descriptors
   }
});

💡#5: Manipulate

// >> inside the ble.startListener(...)
// manipulate with characteristics and descriptors
if (response.success){    
  // first subcribe to the events    
  ble.on.charaValueArrived((uuid, data, len) => {        
    console.log("Read result:", uuid, data, len);    
  });
 // then manipulate - read/write/etc
 ble.read.characteristic("FF02");

 // As a result of this execution you should log
 // Read result: 'FF02' 'BAT_LVL_77' '10'
}

💡#6: Stop

Whenever you’re done communicating with your device, you have to execute the ble.quit() command. It’s good practice to keep it in your onDestroy() lifecycle. This way everything gets stopped and destroyed when the app is closed.

The stop command does 4 different things at a time:

  1. it stops all the callbacks you are subscribed to
  2. it destroys a profile on the backend of your watch (which is very important)
  3. disconnects from the BLE peripheral.
  4. and deregisters user-created callbacks to save some memory

onDestroy(){
   ble.quit();
}

✨️ Full Communications Example

Here’s a full basic working communications example. Compared to the raw BLE communication approach that uses hmBle.mst…() methods, it’s super simple and straightforward.

// install -> npm i @silver-zepp/easy-ble
import BLEMaster from "@silver-zepp/easy-ble"const ble = new BLEMaster();
// the mac of a device you are connecting to
const MAC = "1A:2B:3C:4D:5E:6F";
// simplified object that describes a profile
const services = {  
  // service #1  "FF00": {      
  // service UUID    
  "FF02": [],         // READ chara UUID    
  "FF03": ["2902"],   // NOTIFY chara UUID            
                 //  ^--- descriptor UUID  },
  // ... add other services here if needed
}
// connect to a deviceble.connect(MAC, (connect_result)=>{  
  // proceed further if [bool] connected is true  
  if (connect_result.connected){    
  // generate a complex profile object providing just mac and services    
  const profile_object = ble.generateProfileObject(services);
 // start listening for the response
 // from watch's backend
 ble.startListener(profile_object, (response)=> {
  if (response.success){
    // first subcribe to the events
    ble.on.charaValueArrived((uuid, data, len)=> {
        console.log("Read result:", uuid, data, len);
      }
    );

    // then manipulate - read/write/etc
    ble.read.characteristic("FF02");

    // As a result you should log
    // Read result: 'FF02' 'BAT_LVL_77' '10'
  }
 });
}});

📝 Easy BLE (Master) API Reference

📍BLEMaster (default class)

startScan(response_callback, options = {})

Starts scanning for BLE devices.

Parameters

  • response_callback {Function} - Callback function called with each device's scan result.
  • options {Object} - Optional parameters for the scan.
  • options.duration {number} - Duration of the scan in milliseconds. Auto-stops after this duration.
  • options.on_duration {Function} - Callback function called when the scan stops after the specified duration.
  • options.throttle_interval {number} - Interval in milliseconds to throttle the processing of scan results. Default is 1000.
  • options.allow_duplicates {bool} - Whether to include duplicate devices in each callback. Defaults to false.

Examples

// Start scanning for devices and log each found device
.startScan((device) => { console.log('Found device:', device); });

// Advanced example: start scanning for 10 seconds with a custom throttle interval and allowing duplicates, then stop and log
.startScan((device) =>  { console.log('Found device during scan:', device); },
          { duration: 10000, throttle_interval: 1000, allow_duplicates=true, on_duration: () => console.log('Scan complete') });

Returns

{boolean} true if the scan started successfully, false otherwise.

stopScan()

Stops the ongoing scanning process for BLE devices.

Examples

// Simply stop the scan
.stopScan();

// Advanced example: start scanning for devices and then stop scanning after the device was found
.startScan((device) => {
 if (.get.hasMAC("1A:2B:3C:4D:5E:6F")) .stopScan();
});

Returns

{boolean} - true if the scan was successfully stopped, false if there was an error in stopping the scan.

connect(dev_addr, response_callback)

Attempts to connect to a BLE device.

Parameters

  • dev_addr {string} - The MAC address of the device to connect to.
  • response_callback {function} - Callback function that receives the result of the connection attempt. The callback is called with an object containing two properties:
  • connected: A boolean indicating if the connection was successful.
  • status: A string indicating the connection status. Possible values are connected, invalid mac, in progress, failed, or disconnected.

Examples

// Connect to a device and log the result
.connect("1A:2B:3C:4D:5E:6F", (result) => {
 if (result.connected) {
   console.log('Connected to device');
 } else {
   console.log('Failed to connect. Status:', result.status);
 }
});

Returns

{boolean} - true if the connection attempt started successfully, false otherwise.

disconnect()

Disconnects from a BLE device.

Examples

// Disconnect from a device
.disconnect();

Returns

{boolean} - true if the disconnection was successful, false if it failed or if the device was not connected.

pair()

Attempts to pair with a BLE device. WARNING: This method might not work as expected and could potentially cause crashes. Use with caution.

Examples

// Attempt to pair with a device
const success = .pair();
if (success) console.log('Pairing initiated successfully');
else console.log('Pairing failed or device not connected');

Returns

{boolean} - Returns true if the call to initiate pairing with the device succeeded, false if it failed or if the device was not connected.

startListener(profile_object, response_callback)

Starts listening for profile preparation events and builds a profile for interacting with a BLE device.

Parameters

  • profile_object {Object} - The profile object describing how to interact with the BLE device. This should be generated using generateProfileObject method.
  • response_callback {Function} - Callback function called with the result of the profile preparation. The callback receives an object containing 'success', 'message', and optionally 'code' properties.

Examples

// Start listener with a profile object
const profile_object = .generateProfileObject(MAC, services); // detailed profile object
.startListener(profile_object, (response) => {
 if (response.success) {
   console.log('Profile preparation successful:', response.message);
 } else {
   console.log('Profile preparation failed:', response.message, 'Code:', response.code);
 }
});

Returns

{void} - This method doesn’t return a value but invokes the response callback with the result of the profile preparation.

generateProfileObject(services, permissions = {})

Generates a generic profile object for interacting with a BLE device.

Parameters

  • services {object} - A list of services with their characteristics and descriptors. Each service is identified by its UUID and contains a map of its characteristics. Each characteristic, identified by its UUID, is an array of its descriptor UUIDs.
  • permissions {object} [permissions={}] - Optional. An object specifying custom permissions for characteristics and descriptors. If not provided, defaults to a permission value of 32 (all permissions) for each entry.

Examples

// Example of generating a profile object for a device with custom permissions
const services = {
 'service_uuid': {
   'char_uuid_1': ['desc_uuid_1', 'desc_uuid_2'],
   'char_uuid_2': []
 }
 // other services...
};
const permissions = {
 'char_uuid_1': PERMISSIONS.READ, // no need to provide perms for all UUIDs
};
const profile = .generateProfileObject(services, permissions);

Returns

{object|null} - A generic profile object for the device, or null if the device was not found. The profile object includes device connection information, services, characteristics, and their permissions.

quit()

Quits all interactions with the currently connected device.

Examples

// Stop interaction with a connected device
.quit();

SetDebugLevel(debug_level)

Sets the debug log level for the BLEMaster class.

Parameters

  • debug_level {number} - The debug level to set. Possible values:
  • 0 - No logs
  • 1 - Critical errors only
  • 2 - Errors and warnings
  • 3 - All logs (including debug information)

Examples

// Show all logs
BLEMaster.SetDebugLevel(3);

📍Write (sub-class)

characteristic(uuid, data, write_without_response = false)

Writes data to a characteristic of a BLE device. This operation is queued to ensure proper synchronization with other BLE operations.

Parameters

  • uuid {string} - The UUID of the characteristic to write to.
  • data {string|ArrayBuffer|Uint8Array} - The data to write, in various formats.
  • write_without_response {boolean} [write_without_response=false] - If true, writes without using the queue and without waiting for a response.

Examples

// Write a string to a characteristic
.write.characteristic('char_uuid', 'Hello World');

// Fast write an ArrayBuffer to a characteristic and don't wait for response
const buffer = new Uint8Array([1, 2, 3]).buffer;
.write.characteristic('char_uuid', buffer, true);

descriptor(chara, desc, data)

Writes data to a descriptor of a device's characteristic. This operation is queued to ensure proper synchronization with other BLE operations.

Parameters

  • chara {string} - The UUID of the characteristic that the descriptor belongs to.
  • desc {string} - The UUID of the descriptor to write to.
  • data {string|ArrayBuffer|Uint8Array} - The data to write. Can be an ArrayBuffer, a Uint8Array, a hex string, or a regular string.

Examples

// Write a hex string to a descriptor
.write.descriptor('char_uuid', 'desc_uuid', '0100');

// Write an ArrayBuffer to a descriptor
const buffer = new Uint8Array([1, 2, 3]).buffer;
.write.descriptor('char_uuid', 'desc_uuid', buffer);

enableCharaNotifications(chara, enable)

Enables or disables notifications for a characteristic by writing to the CCCD (Client Characteristic Configuration Descriptor). This operation is queued to ensure proper synchronization with other BLE operations.

Parameters

  • chara {string} - The UUID of the characteristic.
  • enable {boolean} - Set to true to enable notifications, false to disable.

Examples

// Toggle notifications for a characteristic (true/false)
.write.enableCharaNotifications('char_uuid', true);

📍Read (sub-class)

characteristic(uuid)

Reads data from a characteristic of a BLE device. This operation is queued to ensure proper synchronization with other BLE operations.

Parameters

  • uuid {string} - The UUID of the characteristic to read from.

Examples

// Read data from a characteristic
const read = .read.characteristic('char_uuid');
if (read.success) console.log('Read successful');
else console.log('Read failed:', read.error);

Returns

{Object} - An object containing a ‘success’ property and optionally an ‘error’ property.

descriptor(chara, desc)

Reads data from a descriptor of a characteristic of a BLE device. This operation is queued to ensure proper synchronization with other BLE operations.

Parameters

  • chara {string} - The UUID of the characteristic.
  • desc {string} - The UUID of the descriptor to read from.

Examples

// Read data from a descriptor
const desc = .read.descriptor("1A:2B:3C:4D:5E:6F", 'char_uuid', 'desc_uuid');
if (desc.success) console.log('Descriptor read successful');
else console.log('Descriptor read failed:', desc.error);

Returns

{Object} - An object containing a ‘success’ property and optionally an ‘error’ property.

📍On (sub-class)

charaReadComplete(callback)

Registers a callback for the characteristic read complete event. This callback is triggered after a read operation on a characteristic is completed.

Parameters

  • callback {Function} - The callback to execute on event trigger.

Examples

// Register callback for characteristic read complete event
.on.charaReadComplete((uuid, status) => {
 console.log('Characteristic read complete for UUID:', uuid, 'with status:', status);
});

Receives

uuid {string} - The UUID of the characteristic.status {number} - The status of the read operation.

charaValueArrived(callback)

Registers a callback for the characteristic value arrived event. This callback is triggered when new data is received from a characteristic.

Parameters

  • callback {Function} - The callback to execute on event trigger.

Examples

// Register callback for characteristic value arrived event
.on.charaValueArrived((uuid, data, length) => {
 console.log('Value arrived for UUID:', uuid, 'Data:', data, 'Length:', length);
});

Receives

uuid {string} - The UUID of the characteristic.data {ArrayBuffer} - The data received from the characteristic.length {number} - The length of the data.

charaWriteComplete(callback)

Registers a callback for the characteristic write complete event. This callback is triggered after a write operation on a characteristic is completed.

Parameters

  • callback {Function} - The callback to execute on event trigger.

Examples

// Register callback for characteristic write complete event
.on.charaWriteComplete((uuid, status) => {
 console.log('Characteristic write complete for UUID:', uuid, 'Status:', status);
});

Receives

uuid {string} - The UUID of the characteristic.status {number} - The status of the write operation.

descReadComplete(callback)

Registers a callback for the descriptor read complete event. This callback is triggered after a read operation on a descriptor is completed.

Parameters

  • callback {Function} - The callback to execute on event trigger.

Examples

// Register callback for descriptor read complete event
.on.descReadComplete((chara, desc, status) => {
 console.log(`Descriptor read complete for Characteristic UUID: ${chara},
              Descriptor UUID: ${desc}, Status: ${status}`);
});

Receives

chara {string} - UUID of the characteristicdesc {string} - UUID of the descriptorstatus {number} - Status of the read operation

descValueArrived(callback)

Registers a callback for the descriptor value arrived event. This callback is triggered when new data arrives at a descriptor.

Parameters

  • callback {Function} - The callback to execute on event trigger.

Examples

// Register callback for descriptor value arrived event
.on.descValueArrived((chara, desc, data, length) => {
 console.log(`Descriptor value arrived for Characteristic UUID: ${chara},
              Descriptor UUID: ${desc}, Data: ${data}, Length: ${length}`);
});

Receives

chara {string} - UUID of the characteristicdesc {string} - UUID of the descriptordata {ArrayBuffer} - Data receivedlength {number} - Length of the data

descWriteComplete(callback)

Registers a callback for the descriptor write complete event. This callback is triggered after a write operation on a descriptor is completed.

Parameters

  • callback {Function} - The callback to execute on event trigger.

Examples

// Register callback for descriptor write complete event
.on.descWriteComplete((chara, desc, status) => {
 console.log(`Descriptor write complete for Characteristic UUID: ${chara},
              Descriptor UUID: ${desc}, Status: ${status}`);
});

Receives

chara {string} - UUID of the characteristicdesc {string} - UUID of the descriptorstatus {number} - Status of the write operation

charaNotification(callback)

Registers a callback for the characteristic notification event. This callback is triggered when a notification is received from a characteristic.

Parameters

  • callback {Function} - The callback to execute on event trigger.

Examples

// Register callback for characteristic notification event
.on.charaNotification((uuid, data, length) => {
 console.log(`Notification received for UUID: ${uuid}, Data: ${data}, Length: ${length}`);
});

Receives

uuid {string} - UUID of the characteristicdata {ArrayBuffer} - Notification datalength {number} - Length of the data

serviceChangeBegin(callback)

Registers a callback for the service change begin event. This callback is triggered when a BLE service change process begins.

Parameters

  • callback {Function} - The callback to execute on event trigger.

Examples

// Register callback for service change begin event
.on.serviceChangeBegin(() => {
 console.log(`Service change has begun`);
});

serviceChangeEnd(callback)

Registers a callback for the service change end event. This callback is triggered when a BLE service change process ends.

Parameters

  • callback {Function} - The callback to execute on event trigger.

Examples

// Register callback for service change end event
.on.serviceChangeEnd(() => {
 console.log(`Service change has ended`);
});

📍Off (sub-class)

charaReadComplete()

Deregisters the callback for characteristic read complete event.

Examples

.off.charaReadComplete();

charaValueArrived()

Deregisters the callback for characteristic value arrived event.

Examples

.off.charaValueArrived();

charaWriteComplete()

Deregisters the callback for characteristic write complete event.

Examples

.off.charaWriteComplete();

charaWriteComplete()

Deregisters the callback for characteristic write complete event.

Examples

.off.charaWriteComplete();

descValueArrived()

Deregisters the callback for descriptor value arrived event.

Examples

.off.descValueArrived();

descWriteComplete()

Deregisters the callback for descriptor write complete event.

Examples

.off.descWriteComplete();

charaNotification()

Deregisters the callback for characteristic notification event.

Examples

.off.charaNotification();

serviceChangeBegin()

Deregisters the callback for service change begin event.

Examples

.off.serviceChangeBegin();

serviceChangeEnd()

Deregisters the callback for service change end event.

Examples

.off.serviceChangeEnd();

deregisterAll()

Deregisters all callbacks associated with the current BLE connection. This method ensures no event callbacks remain active after stopping BLE operations.

Examples

.off.deregisterAll();

📍Get (sub-class)

devices()

Retrieves information about all discovered devices.

Examples

// Get all discovered devices
const devices = .get.devices();
console.log('Discovered devices:', JSON.stringify(devices));

Returns

{Object} An object containing information about all discovered devices.

isConnected()

Checks if a specific device is currently connected.

Examples

// Check if a device is connected
const is_connected = .get.isConnected();
console.log('Is device connected:', is_connected);

Returns

{boolean} True if the device is connected, false otherwise.

hasMAC(dev_addr)

Checks if a device with a specific MAC address has been discovered.

Parameters

  • dev_addr {string} - The MAC address of the device.

Examples

// Check if a device has been discovered
const has_mac = .get.hasMAC("1A:2B:3C:4D:5E:6F");
console.log('Has the device been discovered:', has_mac);

Returns

{boolean} true if the device has been discovered, false otherwise.

hasService(service_uuid)

Checks if any discovered device has a specific service UUID.

Parameters

  • service_uuid {string} - The UUID of the service to check for.

Examples

// Check if any device has a specific service
const has_service = .get.hasService("1812");
console.log('Has service 1812:', has_service);

Returns

{boolean} true if any device has the specified service, false otherwise.

hasServiceData(service_data)

Checks if any discovered device contains specific service data.

Parameters

  • service_data {string} - The service data to check for.

Examples

// Check if any device contains specific service data
const has_service_data = .get.hasServiceData("somedata");
console.log('Has service data "somedata":', has_service_data);

Returns

{boolean} true if any device contains the specified service data, false otherwise.

hasVendorData(vendor_data)

Checks if any discovered device has a specific vendor data.

Parameters

  • vendor_data {string} - The vendor data to check for.

Examples

// Check if any device has "zepp" data
const has_vendor_data = .get.hasVendorData("zepp");
console.log('Has vendor "zepp":', has_vendor_data);

Returns

{boolean} true if any device has the specified vendor data, false otherwise.

profilePID()

Retrieves the profile pointer ID of a specific device. This is only useful if you need to communicate directly with hmBle.mst methods.

Examples

// Get the profile ID of a device
const profile_pid = .get.profileIDP();
console.log('Profile IDP:', profile_pid);

Returns

{number|null} The profile pointer ID of the device if available, null otherwise.

connectionID()

Retrieves the connection ID of a specific device. This is only useful if you need to communicate directly with hmBle.mst methods.

Examples

// Get the connection ID of a device
const connection_id = .get.connectionID();
console.log('Connection ID:', connection_id);

Returns

{number|null} The connection ID of the device if available, null otherwise.

📍Helpers (methods, collections)

export function ab2hex(buffer)

Converts an ArrayBuffer to a string of hexadecimal numbers. This function is useful when you need to represent binary data in a readable hexadecimal string format. For example, it can be used to display BLE device addresses or data in a human-readable form.

Parameters

  • buffer {ArrayBuffer} - The ArrayBuffer to be converted.

Examples

// Convert an ArrayBuffer to a hexadecimal string
const buffer = new Uint8Array([10, 20, 30]).buffer;
const hex_str = ab2hex(buffer);
console.log(hex_str); // Output: '0A 14 1E'

Returns

{string} The hexadecimal string representation of the ArrayBuffer. Each byte is represented as a two-character hex code.

export function ab2str(buffer)

Converts an ArrayBuffer into a string. This function is used when you need to convert binary data (ArrayBuffer) into a regular JavaScript string. It's particularly useful for converting data received from BLE devices into text, assuming the data represents text in a compatible encoding (e.g., UTF-8).

Parameters

  • buffer {ArrayBuffer} - The ArrayBuffer to be converted.

Examples

// Convert an ArrayBuffer to a string
const buffer = new Uint8Array([72, 101, 108, 108, 111]).buffer; // 'Hello' in ASCII
const str = ab2str(buffer);
console.log(str); // output: 'Hello'

Returns

{string} The resulting string. Note that the output is dependent on the encoding of the byte data in the ArrayBuffer.

export const PERMISSIONS = {...}

Object containing BLE permissions. Each permission is a property with a detailed description and a numeric value. In many scenarios, it's sufficient to use the built-in generateProfileObject(...) auto-permission 32. However, for complex and encrypted communications, specific permissions defined here may be required.

Properties

  • READ: Allows reading the characteristic value.
  • READ_ENCRYPTED: Allows reading the characteristic value with an encrypted link.
  • READ_ENCRYPTED_MITM: Allows reading the characteristic value with an encrypted and authenticated link (MITM protection).
  • WRITE: Allows writing the characteristic value.
  • WRITE_ENCRYPTED: Allows writing the characteristic value with an encrypted link.
  • WRITE_ENCRYPTED_MITM: Allows writing the characteristic value with an encrypted and authenticated link (MITM protection).
  • WRITE_SIGNED: Allows writing the characteristic value with a signed write (without response).
  • WRITE_SIGNED_MITM: Allows writing the characteristic value with a signed write (without response) and authenticated link (MITM protection).
  • READ_DESCRIPTOR: Allows reading the descriptor value.
  • WRITE_DESCRIPTOR: Allows writing the descriptor value.
  • READ_WRITE_DESCRIPTOR: Allows both reading and writing the descriptor value.
  • NONE: No permissions granted.
  • ALL: All permissions granted.

Each property is an object with a description and a value. The description is a string that explains the permission, and the value is a numeric value representing the permission.

❗ Raw BLE Communications

RAW APPROACH (using hmBle.mst..(…)) directly:

——————————

  1. SCAN (OPTIONAL)
  2. CONNECT
  3. START LISTENER
  4. BUILD PROFILE
  5. MANIPULATE
  6. STOP & DESTROY

If you would like to communicate with the BLE peripheral directly using the hmBle.mst…() here are the gifs that will help to understand how the connection works under the hood.