1 #include <ble/BleUUID.h>
2 #include <ble/CHIPBleServiceData.h>
3 #include <platform/Darwin/UUIDHelper.h>
4 #include <support/CHIPMem.h>
5 #include <support/logging/CHIPLogging.h>
7 #import <CoreBluetooth/CoreBluetooth.h>
11 using DeviceScannedCallback
12 = void (*)(PyObject * context, const char * address, uint16_t discriminator, uint16_t vendorId, uint16_t productId);
13 using ScanCompleteCallback = void (*)(PyObject * context);
16 @interface ChipDeviceBleScanner : NSObject <CBCentralManagerDelegate>
18 @property (strong, nonatomic) dispatch_queue_t workQueue;
19 @property (nonatomic, readonly, nullable) dispatch_source_t timer;
20 @property (strong, nonatomic) CBCentralManager * centralManager;
21 @property (strong, nonatomic) CBUUID * shortServiceUUID;
23 @property (assign, nonatomic) PyObject * context;
24 @property (assign, nonatomic) DeviceScannedCallback scanCallback;
25 @property (assign, nonatomic) ScanCompleteCallback completeCallback;
27 - (id)initWithContext:(PyObject *)context
28 scanCallback:(DeviceScannedCallback)scanCallback
29 completeCallback:(ScanCompleteCallback)completeCallback
30 timeoutMs:(uint32_t)timeout;
32 - (void)stopTimeoutReached;
36 @implementation ChipDeviceBleScanner
38 - (id)initWithContext:(PyObject *)context
39 scanCallback:(DeviceScannedCallback)scanCallback
40 completeCallback:(ScanCompleteCallback)completeCallback
41 timeoutMs:(uint32_t)timeout
45 self.shortServiceUUID = [UUIDHelper GetShortestServiceUUID:&chip::Ble::CHIP_BLE_SVC_ID];
47 _workQueue = dispatch_queue_create("com.chip.python.ble.work_queue", DISPATCH_QUEUE_SERIAL);
48 _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _workQueue);
49 _centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:_workQueue];
51 _scanCallback = scanCallback;
52 _completeCallback = completeCallback;
54 dispatch_source_set_event_handler(_timer, ^{
55 [self stopTimeoutReached];
57 dispatch_source_set_timer(
58 _timer, dispatch_walltime(NULL, timeout * NSEC_PER_MSEC), DISPATCH_TIME_FOREVER, 50 * NSEC_PER_MSEC);
59 dispatch_resume(_timer);
64 - (void)centralManager:(CBCentralManager *)central
65 didDiscoverPeripheral:(CBPeripheral *)peripheral
66 advertisementData:(NSDictionary *)advertisementData
69 NSNumber * isConnectable = [advertisementData objectForKey:CBAdvertisementDataIsConnectable];
71 if (![isConnectable boolValue]) {
75 NSDictionary * servicesData = [advertisementData objectForKey:CBAdvertisementDataServiceDataKey];
76 for (CBUUID * serviceUUID in servicesData) {
77 if (![serviceUUID.data isEqualToData:_shortServiceUUID.data]) {
80 NSData * serviceData = [servicesData objectForKey:serviceUUID];
82 NSUInteger length = [serviceData length];
83 if (length != sizeof(chip::Ble::ChipBLEDeviceIdentificationInfo)) {
84 ChipLogError(Ble, "Device has invalid advertisement data length.");
88 chip::Ble::ChipBLEDeviceIdentificationInfo data;
89 memcpy(&data, [serviceData bytes], sizeof(data));
91 _scanCallback(_context, [peripheral.identifier.UUIDString UTF8String], data.GetDeviceDiscriminator(), data.GetVendorId(),
98 - (void)stopTimeoutReached
100 ChipLogProgress(Ble, "Scan timeout reached.");
102 _completeCallback(_context);
104 dispatch_source_cancel(_timer);
105 [self.centralManager stopScan];
106 self.centralManager = nil;
109 - (void)centralManagerDidUpdateState:(CBCentralManager *)central
111 switch (central.state) {
112 case CBManagerStatePoweredOn:
113 ChipLogProgress(Ble, "Central BLE Manager is on. Starting to scan.");
114 [central scanForPeripheralsWithServices:@[ _shortServiceUUID ] options:nil];
117 ChipLogError(Ble, "CBManagerState is NOT ON. Unable to scan.");
122 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
128 extern "C" void * pychip_ble_start_scanning(
129 PyObject * context, void * adapter, uint32_t timeout, DeviceScannedCallback scanCallback, ScanCompleteCallback completeCallback)
131 // NOTE: adapter is ignored as it does not apply to mac
133 ChipDeviceBleScanner * scanner = [[ChipDeviceBleScanner alloc] initWithContext:context
134 scanCallback:scanCallback
135 completeCallback:completeCallback
138 return static_cast<void *>(scanner);