add wifi request ble characteristic
This commit is contained in:
@@ -16,6 +16,8 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
||||
static let serviceUUID = CBUUID(string: "A0B0C0D0-E0F0-4A0B-9C0D-0E0F1A2B3C4D")
|
||||
static let feedTxUUID = CBUUID(string: "A0B0C0D1-E0F0-4A0B-9C0D-0E0F1A2B3C4D")
|
||||
static let controlRxUUID = CBUUID(string: "A0B0C0D2-E0F0-4A0B-9C0D-0E0F1A2B3C4D")
|
||||
// Read/Notify: 1-byte value (0x00=OFF, 0x01=ON) that requests Glass to enable Wi‑Fi.
|
||||
static let wifiRequestTxUUID = CBUUID(string: "A0B0C0D3-E0F0-4A0B-9C0D-0E0F1A2B3C4D")
|
||||
private static let restoreIdentifier = "iris.ble.peripheral.v1"
|
||||
|
||||
@Published private(set) var bluetoothState: CBManagerState = .unknown
|
||||
@@ -30,6 +32,7 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
||||
@Published private(set) var droppedNotifyPackets: Int = 0
|
||||
@Published private(set) var lastNotifyAt: Date? = nil
|
||||
@Published private(set) var lastDataAt: Date? = nil
|
||||
@Published private(set) var wifiRequested: Bool = false
|
||||
|
||||
private let queue = DispatchQueue(label: "iris.ble.peripheral.queue")
|
||||
private lazy var peripheral = CBPeripheralManager(
|
||||
@@ -43,10 +46,13 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
||||
private var service: CBMutableService?
|
||||
private var feedTx: CBMutableCharacteristic?
|
||||
private var controlRx: CBMutableCharacteristic?
|
||||
private var wifiRequestTx: CBMutableCharacteristic?
|
||||
|
||||
private var subscribedCentralIds = Set<UUID>()
|
||||
private var centralMaxUpdateLength: [UUID: Int] = [:]
|
||||
private var lastReadValue: Data = Data()
|
||||
private var wifiRequestValue: Data = Data([0x00])
|
||||
private var wifiRequestNotifyPending: Bool = false
|
||||
|
||||
private struct PendingMessage {
|
||||
let msgId: UInt32
|
||||
@@ -101,11 +107,23 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func setWifiRequested(_ requested: Bool) {
|
||||
queue.async { [weak self] in
|
||||
guard let self else { return }
|
||||
let newValue = Data([requested ? 0x01 : 0x00])
|
||||
guard newValue != self.wifiRequestValue else { return }
|
||||
self.wifiRequestValue = newValue
|
||||
self.publish { self.wifiRequested = requested }
|
||||
self.flushWifiRequestNotifyIfPossible()
|
||||
}
|
||||
}
|
||||
|
||||
func copyUUIDsToPasteboard() {
|
||||
let text = """
|
||||
SERVICE_UUID=\(Self.serviceUUID.uuidString)
|
||||
FEED_TX_UUID=\(Self.feedTxUUID.uuidString)
|
||||
CONTROL_RX_UUID=\(Self.controlRxUUID.uuidString)
|
||||
WIFI_REQUEST_TX_UUID=\(Self.wifiRequestTxUUID.uuidString)
|
||||
"""
|
||||
#if canImport(UIKit)
|
||||
DispatchQueue.main.async {
|
||||
@@ -116,6 +134,18 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
||||
|
||||
private func ensureService() {
|
||||
guard peripheral.state == .poweredOn else { return }
|
||||
|
||||
if let existingService = service {
|
||||
let hasWifiChar = (existingService.characteristics ?? []).contains { $0.uuid == Self.wifiRequestTxUUID }
|
||||
if wifiRequestTx == nil || !hasWifiChar {
|
||||
stopAdvertising()
|
||||
peripheral.removeAllServices()
|
||||
service = nil
|
||||
feedTx = nil
|
||||
controlRx = nil
|
||||
wifiRequestTx = nil
|
||||
}
|
||||
}
|
||||
guard service == nil else { return }
|
||||
|
||||
let feedTx = CBMutableCharacteristic(
|
||||
@@ -130,11 +160,18 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
||||
value: nil,
|
||||
permissions: [.writeable]
|
||||
)
|
||||
let wifiRequestTx = CBMutableCharacteristic(
|
||||
type: Self.wifiRequestTxUUID,
|
||||
properties: [.notify, .read],
|
||||
value: nil,
|
||||
permissions: [.readable]
|
||||
)
|
||||
let service = CBMutableService(type: Self.serviceUUID, primary: true)
|
||||
service.characteristics = [feedTx, controlRx]
|
||||
service.characteristics = [feedTx, controlRx, wifiRequestTx]
|
||||
self.service = service
|
||||
self.feedTx = feedTx
|
||||
self.controlRx = controlRx
|
||||
self.wifiRequestTx = wifiRequestTx
|
||||
peripheral.add(service)
|
||||
}
|
||||
|
||||
@@ -278,6 +315,20 @@ final class BlePeripheralManager: NSObject, ObservableObject {
|
||||
publishNotifyQueueDepth()
|
||||
}
|
||||
|
||||
private func flushWifiRequestNotifyIfPossible() {
|
||||
guard let wifiRequestTx else { return }
|
||||
let hasSubscribers = !(wifiRequestTx.subscribedCentrals ?? []).isEmpty
|
||||
guard hasSubscribers else {
|
||||
wifiRequestNotifyPending = false
|
||||
return
|
||||
}
|
||||
if peripheral.updateValue(wifiRequestValue, for: wifiRequestTx, onSubscribedCentrals: nil) {
|
||||
wifiRequestNotifyPending = false
|
||||
} else {
|
||||
wifiRequestNotifyPending = true
|
||||
}
|
||||
}
|
||||
|
||||
private func enqueuePendingMessage(_ message: PendingMessage) {
|
||||
pendingMessages.append(message)
|
||||
enforceNotifyQueueLimits()
|
||||
@@ -334,6 +385,7 @@ extension BlePeripheralManager: CBPeripheralManagerDelegate {
|
||||
self.service = restoredMutableService
|
||||
self.feedTx = characteristics.first(where: { $0.uuid == Self.feedTxUUID }) as? CBMutableCharacteristic
|
||||
self.controlRx = characteristics.first(where: { $0.uuid == Self.controlRxUUID }) as? CBMutableCharacteristic
|
||||
self.wifiRequestTx = characteristics.first(where: { $0.uuid == Self.wifiRequestTxUUID }) as? CBMutableCharacteristic
|
||||
}
|
||||
|
||||
self.publish {
|
||||
@@ -362,6 +414,7 @@ extension BlePeripheralManager: CBPeripheralManagerDelegate {
|
||||
self.service = nil
|
||||
self.feedTx = nil
|
||||
self.controlRx = nil
|
||||
self.wifiRequestTx = nil
|
||||
return
|
||||
}
|
||||
self.ensureService()
|
||||
@@ -380,6 +433,12 @@ extension BlePeripheralManager: CBPeripheralManagerDelegate {
|
||||
}
|
||||
|
||||
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
|
||||
if characteristic.uuid == Self.wifiRequestTxUUID {
|
||||
queue.async { [weak self] in
|
||||
self?.flushWifiRequestNotifyIfPossible()
|
||||
}
|
||||
return
|
||||
}
|
||||
guard characteristic.uuid == Self.feedTxUUID else { return }
|
||||
queue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
@@ -400,6 +459,9 @@ extension BlePeripheralManager: CBPeripheralManagerDelegate {
|
||||
}
|
||||
|
||||
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didUnsubscribeFrom characteristic: CBCharacteristic) {
|
||||
if characteristic.uuid == Self.wifiRequestTxUUID {
|
||||
return
|
||||
}
|
||||
guard characteristic.uuid == Self.feedTxUUID else { return }
|
||||
queue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
@@ -420,17 +482,25 @@ extension BlePeripheralManager: CBPeripheralManagerDelegate {
|
||||
func peripheralManagerIsReady(toUpdateSubscribers peripheral: CBPeripheralManager) {
|
||||
queue.async { [weak self] in
|
||||
self?.flushPendingMessages()
|
||||
if let self, self.wifiRequestNotifyPending {
|
||||
self.flushWifiRequestNotifyIfPossible()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
|
||||
guard request.characteristic.uuid == Self.feedTxUUID else {
|
||||
peripheral.respond(to: request, withResult: .requestNotSupported)
|
||||
let maxLen = max(0, request.central.maximumUpdateValueLength)
|
||||
if request.characteristic.uuid == Self.feedTxUUID {
|
||||
request.value = maxLen > 0 ? lastReadValue.prefix(maxLen) : lastReadValue
|
||||
peripheral.respond(to: request, withResult: .success)
|
||||
return
|
||||
}
|
||||
let maxLen = max(0, request.central.maximumUpdateValueLength)
|
||||
request.value = maxLen > 0 ? lastReadValue.prefix(maxLen) : lastReadValue
|
||||
peripheral.respond(to: request, withResult: .success)
|
||||
if request.characteristic.uuid == Self.wifiRequestTxUUID {
|
||||
request.value = maxLen > 0 ? wifiRequestValue.prefix(maxLen) : wifiRequestValue
|
||||
peripheral.respond(to: request, withResult: .success)
|
||||
return
|
||||
}
|
||||
peripheral.respond(to: request, withResult: .requestNotSupported)
|
||||
}
|
||||
|
||||
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
|
||||
|
||||
@@ -34,6 +34,16 @@ struct BleStatusView: View {
|
||||
ble.start()
|
||||
}
|
||||
|
||||
Toggle(isOn: Binding(get: { ble.wifiRequested }, set: { ble.setWifiRequested($0) })) {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Request Wi-Fi (Glass)")
|
||||
.font(.headline)
|
||||
Text(ble.wifiRequested ? "On" : "Off")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
}
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("Connection")
|
||||
.font(.headline)
|
||||
@@ -86,7 +96,7 @@ struct BleStatusView: View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Text("UUIDs")
|
||||
.font(.headline)
|
||||
Text("Service: \(BlePeripheralManager.serviceUUID.uuidString)\nFEED_TX: \(BlePeripheralManager.feedTxUUID.uuidString)\nCONTROL_RX: \(BlePeripheralManager.controlRxUUID.uuidString)")
|
||||
Text("Service: \(BlePeripheralManager.serviceUUID.uuidString)\nFEED_TX: \(BlePeripheralManager.feedTxUUID.uuidString)\nCONTROL_RX: \(BlePeripheralManager.controlRxUUID.uuidString)\nWIFI_REQUEST_TX: \(BlePeripheralManager.wifiRequestTxUUID.uuidString)")
|
||||
.font(.caption)
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user