Skip to content

Commit

Permalink
Merge branch 'indications'
Browse files Browse the repository at this point in the history
  • Loading branch information
colemancda committed Sep 11, 2018
2 parents d7a8145 + 0667f77 commit 4388f92
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "PureSwift/Bluetooth" "3399b9fbad13dd1081d7292b0bf0829482041110"
github "PureSwift/Bluetooth" "638ca0c775c15b485d48394e3ac9214f1206743c"
35 changes: 31 additions & 4 deletions Sources/GATT/GATTClientConnection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,39 @@ public final class GATTClientConnection <L2CAPSocket: L2CAPSocketProtocol> {
}
}

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)
}
}

Expand Down
124 changes: 123 additions & 1 deletion Tests/GATTTests/GATTTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ final class GATTTests: XCTestCase {
("testMTUExchange", testMTUExchange),
("testServiceDiscovery", testServiceDiscovery),
("testCharacteristicValue", testCharacteristicValue),
("testNotification", testNotification)
("testNotification", testNotification),
("testIndication", testIndication)
]

func testScanData() {
Expand Down Expand Up @@ -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<PeripheralHostController, TestL2CAPSocket>
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<CentralHostController, TestL2CAPSocket>
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<Peripheral>]()
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<Peripheral>]()
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, *)
Expand Down

0 comments on commit 4388f92

Please sign in to comment.