Compare commits
No commits in common. "6eaccb6bf602f625d4129e486f550d1499037b9c" and "ed9a57a938b9165a35c8f873a3fc2bb25a3b200c" have entirely different histories.
6eaccb6bf6
...
ed9a57a938
2 changed files with 4 additions and 52 deletions
|
|
@ -47,9 +47,6 @@ final class KBeaconProvisioner: NSObject, BeaconProvisioner {
|
||||||
var diagnosticLog: ProvisionLog?
|
var diagnosticLog: ProvisionLog?
|
||||||
var bleManager: BLEManager?
|
var bleManager: BLEManager?
|
||||||
|
|
||||||
/// Status callback — provisioner reports what phase it's in so UI can update
|
|
||||||
var onStatusUpdate: ((String) -> Void)?
|
|
||||||
|
|
||||||
// MARK: - Init
|
// MARK: - Init
|
||||||
|
|
||||||
init(peripheral: CBPeripheral, centralManager: CBCentralManager) {
|
init(peripheral: CBPeripheral, centralManager: CBCentralManager) {
|
||||||
|
|
@ -65,27 +62,14 @@ final class KBeaconProvisioner: NSObject, BeaconProvisioner {
|
||||||
// Connect with retry
|
// Connect with retry
|
||||||
for attempt in 1...GATTConstants.maxRetries {
|
for attempt in 1...GATTConstants.maxRetries {
|
||||||
do {
|
do {
|
||||||
let attemptLabel = GATTConstants.maxRetries > 1 ? " (attempt \(attempt)/\(GATTConstants.maxRetries))" : ""
|
|
||||||
await MainActor.run { onStatusUpdate?("Connecting to beacon…\(attemptLabel)") }
|
|
||||||
await diagnosticLog?.log("connect", "Attempt \(attempt)/\(GATTConstants.maxRetries)")
|
|
||||||
try await connectOnce()
|
try await connectOnce()
|
||||||
|
|
||||||
await MainActor.run { onStatusUpdate?("Discovering services…") }
|
|
||||||
await diagnosticLog?.log("connect", "Connected — discovering services…")
|
|
||||||
try await discoverServices()
|
try await discoverServices()
|
||||||
await diagnosticLog?.log("connect", "Services found — write:\(writeChar != nil) notify:\(notifyChar != nil)")
|
|
||||||
|
|
||||||
await MainActor.run { onStatusUpdate?("Authenticating…") }
|
|
||||||
await diagnosticLog?.log("auth", "Trying \(Self.passwords.count) passwords…")
|
|
||||||
try await authenticate()
|
try await authenticate()
|
||||||
await diagnosticLog?.log("auth", "Auth success")
|
|
||||||
isConnected = true
|
isConnected = true
|
||||||
return
|
return
|
||||||
} catch {
|
} catch {
|
||||||
await diagnosticLog?.log("connect", "Attempt \(attempt) failed: \(error.localizedDescription)", isError: true)
|
|
||||||
disconnect()
|
disconnect()
|
||||||
if attempt < GATTConstants.maxRetries {
|
if attempt < GATTConstants.maxRetries {
|
||||||
await MainActor.run { onStatusUpdate?("Retrying… (\(attempt)/\(GATTConstants.maxRetries))") }
|
|
||||||
try await Task.sleep(nanoseconds: UInt64(GATTConstants.retryDelay * 1_000_000_000))
|
try await Task.sleep(nanoseconds: UInt64(GATTConstants.retryDelay * 1_000_000_000))
|
||||||
} else {
|
} else {
|
||||||
throw error
|
throw error
|
||||||
|
|
@ -127,23 +111,17 @@ final class KBeaconProvisioner: NSObject, BeaconProvisioner {
|
||||||
params.append(UInt8(config.advInterval & 0xFF))
|
params.append(UInt8(config.advInterval & 0xFF))
|
||||||
|
|
||||||
// Send CMD_WRITE_PARAMS
|
// Send CMD_WRITE_PARAMS
|
||||||
await MainActor.run { onStatusUpdate?("Writing beacon parameters…") }
|
|
||||||
await diagnosticLog?.log("write", "Sending CMD_WRITE_PARAMS (\(params.count) bytes)…")
|
|
||||||
let writeCmd = Data([CMD.writeParams.rawValue]) + params
|
let writeCmd = Data([CMD.writeParams.rawValue]) + params
|
||||||
let writeResp = try await sendCommand(writeCmd)
|
let writeResp = try await sendCommand(writeCmd)
|
||||||
guard writeResp.first == CMD.writeParams.rawValue else {
|
guard writeResp.first == CMD.writeParams.rawValue else {
|
||||||
throw ProvisionError.writeFailed("Unexpected write response")
|
throw ProvisionError.writeFailed("Unexpected write response")
|
||||||
}
|
}
|
||||||
await diagnosticLog?.log("write", "Params written OK — saving to flash…")
|
|
||||||
|
|
||||||
// Send CMD_SAVE to flash
|
// Send CMD_SAVE to flash
|
||||||
await MainActor.run { onStatusUpdate?("Saving to flash…") }
|
|
||||||
let saveResp = try await sendCommand(Data([CMD.save.rawValue]))
|
let saveResp = try await sendCommand(Data([CMD.save.rawValue]))
|
||||||
guard saveResp.first == CMD.save.rawValue else {
|
guard saveResp.first == CMD.save.rawValue else {
|
||||||
throw ProvisionError.saveFailed
|
throw ProvisionError.saveFailed
|
||||||
}
|
}
|
||||||
await MainActor.run { onStatusUpdate?("Config saved ✓") }
|
|
||||||
await diagnosticLog?.log("write", "Save confirmed")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func disconnect() {
|
func disconnect() {
|
||||||
|
|
@ -202,20 +180,14 @@ final class KBeaconProvisioner: NSObject, BeaconProvisioner {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func authenticate() async throws {
|
private func authenticate() async throws {
|
||||||
for (index, password) in Self.passwords.enumerated() {
|
for password in Self.passwords {
|
||||||
let passwordLabel = String(data: password.prefix(6), encoding: .utf8) ?? "binary"
|
|
||||||
await MainActor.run { onStatusUpdate?("Authenticating… (\(index + 1)/\(Self.passwords.count))") }
|
|
||||||
await diagnosticLog?.log("auth", "Trying password \(index + 1)/\(Self.passwords.count): \(passwordLabel)…")
|
|
||||||
let cmd = Data([CMD.auth.rawValue]) + password
|
let cmd = Data([CMD.auth.rawValue]) + password
|
||||||
do {
|
do {
|
||||||
let resp = try await sendCommand(cmd)
|
let resp = try await sendCommand(cmd)
|
||||||
if resp.first == CMD.auth.rawValue && resp.count > 1 && resp[1] == 0x00 {
|
if resp.first == CMD.auth.rawValue && resp.count > 1 && resp[1] == 0x00 {
|
||||||
await MainActor.run { onStatusUpdate?("Authenticated ✓") }
|
|
||||||
return // Auth success
|
return // Auth success
|
||||||
}
|
}
|
||||||
await diagnosticLog?.log("auth", "Password \(index + 1) rejected (response: \(resp.map { String(format: "%02X", $0) }.joined()))")
|
|
||||||
} catch {
|
} catch {
|
||||||
await diagnosticLog?.log("auth", "Password \(index + 1) timeout: \(error.localizedDescription)")
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -619,10 +619,6 @@ struct ScanView: View {
|
||||||
dxProvisioner.onStatusUpdate = { [weak self] status in
|
dxProvisioner.onStatusUpdate = { [weak self] status in
|
||||||
self?.statusMessage = status
|
self?.statusMessage = status
|
||||||
}
|
}
|
||||||
} else if let kbProvisioner = provisioner as? KBeaconProvisioner {
|
|
||||||
kbProvisioner.onStatusUpdate = { [weak self] status in
|
|
||||||
self?.statusMessage = status
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
statusMessage = "Connecting to \(beacon.displayName)…"
|
statusMessage = "Connecting to \(beacon.displayName)…"
|
||||||
|
|
@ -634,16 +630,9 @@ struct ScanView: View {
|
||||||
Task { @MainActor [weak self] in
|
Task { @MainActor [weak self] in
|
||||||
let reason = error?.localizedDescription ?? "beacon timed out"
|
let reason = error?.localizedDescription ?? "beacon timed out"
|
||||||
provisionLog?.log("disconnect", "Unexpected disconnect: \(reason)", isError: true)
|
provisionLog?.log("disconnect", "Unexpected disconnect: \(reason)", isError: true)
|
||||||
guard let self = self else { return }
|
// If we're still in connecting or connected state, show failure
|
||||||
// DXSmart: disconnect during .connected is expected — beacon keeps
|
if let self = self,
|
||||||
// flashing after BLE drops. We'll reconnect when user taps Write Config.
|
self.provisioningState == .connecting || self.provisioningState == .connected {
|
||||||
if self.provisioningState == .connected && beacon.type == .dxsmart {
|
|
||||||
provisionLog?.log("disconnect", "DXSmart idle disconnect — beacon still flashing, ignoring")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// For all other active states, treat disconnect as failure
|
|
||||||
if self.provisioningState == .connecting || self.provisioningState == .connected ||
|
|
||||||
self.provisioningState == .writing || self.provisioningState == .verifying {
|
|
||||||
self.provisioningState = .failed
|
self.provisioningState = .failed
|
||||||
self.errorMessage = "Beacon disconnected: \(reason)"
|
self.errorMessage = "Beacon disconnected: \(reason)"
|
||||||
}
|
}
|
||||||
|
|
@ -724,15 +713,6 @@ struct ScanView: View {
|
||||||
statusMessage = "Writing config to DX-Smart…"
|
statusMessage = "Writing config to DX-Smart…"
|
||||||
|
|
||||||
do {
|
do {
|
||||||
// Reconnect if the beacon dropped BLE during the "confirm flashing" wait
|
|
||||||
if !provisioner.isConnected {
|
|
||||||
provisionLog.log("write", "Beacon disconnected while waiting — reconnecting…")
|
|
||||||
statusMessage = "Reconnecting to beacon…"
|
|
||||||
try await provisioner.connect()
|
|
||||||
provisionLog.log("write", "Reconnected — writing config…")
|
|
||||||
statusMessage = "Writing config to DX-Smart…"
|
|
||||||
}
|
|
||||||
|
|
||||||
try await provisioner.writeConfig(config)
|
try await provisioner.writeConfig(config)
|
||||||
provisioner.disconnect()
|
provisioner.disconnect()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue