From 488c5bd1908f134725fe5fedec172e2d0f39daa4 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Mon, 10 Sep 2018 16:15:58 -0500 Subject: [PATCH 1/3] Working on Indications --- Sources/GATT/GATTClientConnection.swift | 35 ++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/Sources/GATT/GATTClientConnection.swift b/Sources/GATT/GATTClientConnection.swift index 613e1a9..a91a2c7 100644 --- a/Sources/GATT/GATTClientConnection.swift +++ b/Sources/GATT/GATTClientConnection.swift @@ -202,12 +202,39 @@ public final class GATTClientConnection { } } + let notify: GATTClient.Notification? + let indicate: GATTClient.Notification? + + /** + If the specified characteristic is configured to allow both notifications and indications, calling this method enables notifications only. You can disable notifications and indications for a characteristic’s value by calling this method with the enabled parameter set to false. + */ + + if gattCharacteristic.attribute.properties.contains(.notify) { + + notify = notification + indicate = nil + + } else if gattCharacteristic.attribute.properties.contains(.indicate) { + + notify = nil + indicate = notification + + } else { + + notify = nil + indicate = nil + + assertionFailure("Cannot enable notification or indication for characteristic \(characteristic.uuid)") + return + } + // GATT request try async(timeout: timeout) { - client.registerNotification(notification, - for: gattCharacteristic.attribute, - descriptors: descriptors, - completion: $0) + client.clientCharacteristicConfiguration(notification: notify, + indication: indicate, + for: gattCharacteristic.attribute, + descriptors: descriptors, + completion: $0) } } From 1b8e4ea00bbf3faeba6cc600dc05a5fa011f0db0 Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Mon, 10 Sep 2018 16:30:49 -0500 Subject: [PATCH 2/3] Updated unit tests --- Tests/GATTTests/GATTTests.swift | 124 +++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 1 deletion(-) diff --git a/Tests/GATTTests/GATTTests.swift b/Tests/GATTTests/GATTTests.swift index 9377d3e..cc9f67b 100644 --- a/Tests/GATTTests/GATTTests.swift +++ b/Tests/GATTTests/GATTTests.swift @@ -20,7 +20,8 @@ final class GATTTests: XCTestCase { ("testMTUExchange", testMTUExchange), ("testServiceDiscovery", testServiceDiscovery), ("testCharacteristicValue", testCharacteristicValue), - ("testNotification", testNotification) + ("testNotification", testNotification), + ("testIndication", testIndication) ] func testScanData() { @@ -606,6 +607,127 @@ final class GATTTests: XCTestCase { // stop notifications XCTAssertNoThrow(try central.notify(nil, for: foundCharacteristic)) } + + func testIndication() { + + // setup sockets + let serverSocket = TestL2CAPSocket(name: "Server") + let clientSocket = TestL2CAPSocket(name: "Client") + clientSocket.target = serverSocket + serverSocket.target = clientSocket + + // host controller + let serverHostController = PeripheralHostController(address: .any) + let clientHostController = CentralHostController(address: .any) + + // peripheral + typealias TestPeripheral = GATTPeripheral + let options = GATTPeripheralOptions(maximumTransmissionUnit: .default, maximumPreparedWrites: .max) + let peripheral = TestPeripheral(controller: serverHostController, options: options) + peripheral.log = { print("Peripheral:", $0) } + + var incomingConnections = [(serverSocket, Central(identifier: serverSocket.address))] + + peripheral.newConnection = { + + repeat { + if let newConnecion = incomingConnections.popFirst() { + return newConnecion + } else { + sleep(1) + } + } while true + } + + // service + let batteryLevel = GATTBatteryLevel(level: .min) + + let characteristics = [ + GATT.Characteristic(uuid: type(of: batteryLevel).uuid, + value: batteryLevel.data, + permissions: [.read], + properties: [.read, .indicate], + descriptors: [GATTClientCharacteristicConfiguration().descriptor]), + ] + + let service = GATT.Service(uuid: .batteryService, + primary: true, + characteristics: characteristics) + + let serviceAttribute = try! peripheral.add(service: service) + defer { peripheral.remove(service: serviceAttribute) } + + let characteristicValueHandle = peripheral.characteristics(for: .batteryLevel)[0] + + // start server + XCTAssertNoThrow(try peripheral.start()) + defer { peripheral.stop() } + + // central + typealias TestCentral = GATTCentral + let central = TestCentral(hostController: clientHostController) + central.log = { print("Central:", $0) } + central.newConnection = { (report) in + return clientSocket + } + central.hostController.advertisingReports = [ + Data([0x3E, 0x2A, 0x02, 0x01, 0x00, 0x00, 0x01, 0x1E, 0x62, 0x6D, 0xE3, 0x94, 0x1E, 0x02, 0x01, 0x06, 0x1A, 0xFF, 0x4C, 0x00, 0x02, 0x15, 0xFD, 0xA5, 0x06, 0x93, 0xA4, 0xE2, 0x4F, 0xB1, 0xAF, 0xCF, 0xC6, 0xEB, 0x07, 0x64, 0x78, 0x25, 0x27, 0x12, 0x0B, 0x86, 0xBE, 0xBF]), + Data([0x3E, 0x2B, 0x02, 0x01, 0x04, 0x00, 0x01, 0x1E, 0x62, 0x6D, 0xE3, 0x94, 0x1F, 0x0A, 0x09, 0x47, 0x68, 0x6F, 0x73, 0x74, 0x79, 0x75, 0x00, 0x00, 0x13, 0x16, 0x0A, 0x18, 0x47, 0x59, 0x94, 0xE3, 0x6D, 0x62, 0x1E, 0x01, 0x27, 0x12, 0x0B, 0x86, 0x5F, 0xFF, 0xFF, 0xFF, 0xBF]) + ] + + // scan for devices + var foundDevices = [Peripheral]() + XCTAssertNoThrow(foundDevices = try central.scan(duration: 0.001).map { $0.peripheral }) + + guard let device = foundDevices.first + else { XCTFail("No peripherals scanned"); return } + + XCTAssertNoThrow(try central.connect(to: device)) + defer { central.disconnect(peripheral: device) } + + var services = [Service]() + XCTAssertNoThrow(services = try central.discoverServices(for: device)) + + guard let foundService = services.first, + services.count == 1 + else { XCTFail(); return } + + XCTAssertEqual(foundService.uuid, .batteryService) + XCTAssertEqual(foundService.isPrimary, true) + + var foundCharacteristics = [Characteristic]() + XCTAssertNoThrow(foundCharacteristics = try central.discoverCharacteristics(for: foundService)) + + guard let foundCharacteristic = foundCharacteristics.first, + foundCharacteristics.count == 1 + else { XCTFail(); return } + + XCTAssertEqual(foundCharacteristic.uuid, .batteryLevel) + XCTAssertEqual(foundCharacteristic.properties, [.read, .indicate]) + + let notificationExpectation = self.expectation(description: "Notification") + + var notificationValue: GATTBatteryLevel? + XCTAssertNoThrow(try central.notify({ + notificationValue = GATTBatteryLevel(data: $0) + notificationExpectation.fulfill() + }, for: foundCharacteristic)) + + let newValue = GATTBatteryLevel(level: .max) + + // write new value, emit notifications + peripheral[characteristic: characteristicValueHandle] = newValue.data + + // wait + waitForExpectations(timeout: 2.0, handler: nil) + + // validate notification + XCTAssertEqual(peripheral[characteristic: characteristicValueHandle], newValue.data, "Value not updated on peripheral") + XCTAssertEqual(notificationValue, newValue, "Notification not recieved") + + // stop notifications + XCTAssertNoThrow(try central.notify(nil, for: foundCharacteristic)) + } } @available(OSX 10.12, *) From 0667f772d4d039f522a403d5c11d38d4f3da1d7b Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Mon, 10 Sep 2018 21:41:27 -0500 Subject: [PATCH 3/3] Updated dependencies --- Cartfile.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cartfile.resolved b/Cartfile.resolved index 58b5bfb..0cc04f2 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1 @@ -github "PureSwift/Bluetooth" "3399b9fbad13dd1081d7292b0bf0829482041110" +github "PureSwift/Bluetooth" "638ca0c775c15b485d48394e3ac9214f1206743c"