diff --git a/Info_extra_tags.plist b/Info_extra_tags.plist
index a78e075..08b6cf1 100644
--- a/Info_extra_tags.plist
+++ b/Info_extra_tags.plist
@@ -3,8 +3,8 @@
NSBluetoothAlwaysUsageDescription
- This app uses Bluetooth to connect to devices.
+ This app uses Bluetooth to connect to the collar tag.
NSBluetoothPeripheralUsageDescription
- This app uses Bluetooth.
+ This app uses Bluetooth to connect to the collar tag.
diff --git a/Sources/scoobytag/BluetoothManager.swift b/Sources/scoobytag/BluetoothManager.swift
index d8dcdbe..9573e4e 100644
--- a/Sources/scoobytag/BluetoothManager.swift
+++ b/Sources/scoobytag/BluetoothManager.swift
@@ -1,8 +1,14 @@
import SwiftUI
import CoreBluetooth
+struct Constants {
+ static let scbtSvcUuid = "00010F2C-DEAD-BEEF-A455-DEAD69BEEF69";
+ static let scbtPetUuid: String = "00010F2D-DEAD-BEEF-A455-DEAD69BEEF69";
+}
+
final class BluetoothManager: NSObject, ObservableObject {
- private var periphManager: CBPeripheralManager!
+ private var centralManager: CBCentralManager!
+ private var myPeripheral: CBPeripheral?
@Published var status = "Starting..."
override init() {
@@ -11,22 +17,135 @@ final class BluetoothManager: NSObject, ObservableObject {
}
}
-extension BluetoothManager: CBPeripheralManagerDelegate {
- func peripheralManagerDidUpdateState(_ central: CBPeripheralManager) {
- switch central.state {
- case .poweredOn:
- status = "Broadcasting"
- let uuid = CBUUID(string: "12345678-1234-1234-1234-123456789ABC")
- periphManager.startAdvertising(
- [
- CBAdvertisementDataServiceUUIDsKey: [uuid],
- CBAdvertisementDataLocalNameKey: "scbt-Test"
- ])
- case .poweredOff:
- status = "Bluetooth off"
+extension BluetoothManager: CBCentralManagerDelegate, CBPeripheralDelegate {
+ func centralManagerDidUpdateState(_ central: CBCentralManager) {
+ guard central.state == .poweredOn else {
+ status = "Bluetooth OFF"
+ return
+ }
- default:
- status = "Bluetooth unavailable"
+ let uuid = CBUUID(string: Constants.scbtSvcUuid)
+ centralManager.scanForPeripherals(
+ withServices: [uuid],
+ options: [
+ CBCentralManagerScanOptionAllowDuplicatesKey: false
+ ]
+ )
+ }
+
+ // Discovery callback
+ func centralManager(
+ _ central: CBCentralManager,
+ didDiscover peripheral: CBPeripheral,
+ advertisementData: [String: Any],
+ rssi RSSI: NSNumber
+ ) {
+ if (peripheral.name != nil) {
+ status = "Found: \(peripheral.name!)"
+ } else {
+ status = "Found: \(peripheral.identifier)"
+ }
+
+ myPeripheral = peripheral
+ central.stopScan()
+ central.connect(peripheral)
+ }
+
+ // Connection callback
+ func centralManager(
+ _ central: CBCentralManager,
+ didConnect peripheral: CBPeripheral
+ ) {
+ status = "Connected"
+ let uuid = CBUUID(string: Constants.scbtSvcUuid)
+ peripheral.discoverServices([uuid])
+ peripheral.delegate = self
+ }
+
+ // Connection failure callback
+ func centralManager(
+ _ central: CBCentralManager,
+ didFailToConnect peripheral: CBPeripheral,
+ error: Error?
+ ) {
+ let error2: String
+ if (error == nil) { error2 = "unknown" }
+ else { error2 = "\(error!)" }
+ status = "Failed: \(error2)"
+ }
+
+ // Disconnection callback
+ func centralManager(
+ _ central: CBCentralManager,
+ didDisconnectPeripheral peripheral: CBPeripheral,
+ error: Error?
+ ) {
+ let error2: String
+ if (error == nil) { error2 = "unknown" }
+ else { error2 = "\(error!)" }
+ status = "Disconnected: \(error2)"
+ }
+
+ // Service callback
+ func peripheral(
+ _ peripheral: CBPeripheral,
+ didDiscoverServices error: Error?
+ ) {
+ guard let services = peripheral.services else { return }
+
+ status = "Service found"
+
+ let scbtSvc = CBUUID(string: Constants.scbtSvcUuid)
+ let scbtPet = CBUUID(string: Constants.scbtPetUuid)
+
+ for service in services {
+ // Ignore non scbtSvc
+ if (service.uuid != scbtSvc) { continue }
+
+ peripheral.discoverCharacteristics([scbtPet], for: service)
+ }
+ }
+
+ // Characteristic callback
+ func peripheral(
+ _ perihperal: CBPeripheral,
+ didDiscoverCharacteristicsFor service: CBService,
+ error: Error?
+ ) {
+ if (error != nil) {
+ status = "\(error!)"
+ return
+ }
+
+ guard let characteristics = service.characteristics else { return }
+
+ status = "Characteristics found (\(characteristics.count))"
+
+ let scbtPet = CBUUID(string: Constants.scbtPetUuid)
+
+ for characteristic in characteristics {
+ // Ignore non pet
+ if (characteristic.uuid != scbtPet) { continue }
+
+ var value: UInt32 = 0
+ let data = Data(bytes: &value, count: MemoryLayout.size)
+
+ perihperal.writeValue(data, for: characteristic, type: .withResponse)
+ status = "Written \(data)"
+ }
+ }
+
+ // Characteristic write callback
+ func peripheral(
+ _ perihperal: CBPeripheral,
+ didWriteValueFor characteristic: CBCharacteristic,
+ error: (any Error)?
+ ) {
+ if (error == nil) {
+ status = "Write success"
+ }
+ else {
+ status = "Write failed: \(error!)"
}
}
}