Skip to content

Conversation

emmanuelorobinson
Copy link

Pull Request: Add Service Data Filtering Support

Pull Request Title

feat: Add service data filtering support for BLE scanning across all platforms

Pull Request Description

📋 Summary

Adds comprehensive service data filtering capability to requestLEScan and requestDevice methods, enabling filtering of BLE advertisements based on service-specific data payloads. This is particularly useful for protocols like OpenDroneID that use service data for device identification.

🎯 Motivation

Previously, the plugin only supported filtering by:

  • Service UUIDs
  • Device names
  • Manufacturer data

Many BLE protocols (e.g., OpenDroneID) use service data fields in advertisement packets for identification and filtering. This PR adds native support for service data filtering across all platforms.

✨ Changes

TypeScript/Web (src)

  • Added new ServiceDataFilter interface with serviceUuid, dataPrefix, and mask properties
  • Updated RequestBleDeviceOptions to include optional serviceData parameter
  • Implemented client-side service data filtering in BluetoothLeWeb.ts
  • Added matchesServiceDataFilter() method for advertisement filtering

iOS (ios/Plugin)

  • Added ServiceDataFilter struct in Plugin.swift
  • Implemented getServiceDataFilters() method to parse filter options
  • Added passesServiceDataFilter() method in DeviceManager.swift for filtering logic
  • Updated requestDevice and requestLEScan to accept and apply service data filters

Android (android/src/main/java/.../bluetoothle/)

  • Updated getScanFilters() in BluetoothLe.kt to handle service data filters
  • Uses native ScanFilter.Builder().setServiceData() for hardware-level filtering
  • Supports service UUID, data prefix, and bit mask filtering

📚 Usage Example

import { BleClient } from '@capacitor-community/bluetooth-le';

// Scan for OpenDroneID devices
await BleClient.requestLEScan(
  {
    serviceData: [
      {
        serviceUuid: '0000fffa-0000-1000-8000-00805f9b34fb',
        dataPrefix: new Uint8Array([0x0D])  // OpenDroneID AD code
      }
    ],
    allowDuplicates: true
  },
  (result) => {
    console.log('Found device:', result);
    console.log('Service data:', result.serviceData);
  }
);

🧪 Testing

  • ✅ TypeScript definitions compile without errors
  • ✅ Android: Hardware-level filtering via ScanFilter.setServiceData()
  • ✅ iOS: Software filtering in scan delegate callback
  • ✅ Web: Client-side filtering in advertisement event handler

📱 Platform Support

Platform Implementation Filtering Level
Android ScanFilter.setServiceData() Hardware (most efficient)
iOS passesServiceDataFilter() Software (in delegate)
Web matchesServiceDataFilter() Client-side (JS)

🔧 API Compatibility

  • ✅ Backward compatible - serviceData is optional
  • ✅ Consistent API across all platforms
  • ✅ No breaking changes to existing functionality

📖 Related Issues

Closes #XXX (if applicable)

✅ Checklist

  • TypeScript definitions updated
  • Android implementation complete
  • iOS implementation complete
  • Web implementation complete
  • No breaking changes
  • Documentation comments added
  • Example usage provided

emmanuelorobinson and others added 5 commits October 1, 2025 08:53
- Add `serviceData` option and `ServiceDataFilter` docs to README (serviceUuid, dataPrefix, mask; mention OpenDroneID)
- Remove rollup invocation from npm build script in package.json (use docgen + tsc only)
Copy link
Collaborator

@peitschie peitschie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few very minors, but this looks in good shape to me.

Apologies for the delayed review, and thanks for your patience and contribution!

"esModuleInterop": true,
"inlineSources": true,
"lib": ["dom", "es2017"],
"lib": ["dom", "es2018"],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the changes to this file mandatory? I can't see any new browser APIs being used in the code you've added...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change to es2018 is required because the code uses Promise.prototype.finally() in src/timeout.ts:8. This method was introduced in ES2018/ES2019 spec.

Without this change, the TypeScript compiler throws

'error TS2550: Property 'finally' does not exist on type 'Promise'.
Do you need to change your target library? Try changing the 'lib' compiler option to 'es2018' or later.'

The finally() method is used in the timeout utility to ensure cleanup (clearing the timeout) happens regardless of whether the promise resolves or rejects. This is the only ES2018 feature being used, but it's essential for the proper cleanup logic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants