From f082eeadad814a4eda787436ca2c04ba8e518a77 Mon Sep 17 00:00:00 2001 From: Schwifty Date: Mon, 23 Mar 2026 03:44:36 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20skip=20ACK=20wait=20on=20SaveConfig=20?= =?UTF-8?q?=E2=80=94=20beacon=20reboots,=20never=20ACKs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SaveConfig (0x60) causes the beacon MCU to reboot and save to flash. It never sends an ACK, so writeToCharAndWaitACK would wait for the 5s timeout, during which the beacon disconnects. The disconnect handler fires while writesCompleted is still false, causing a false "Unexpected disconnect: beacon timed out" error. Fix: fire-and-forget the SaveConfig write and return immediately. The BLE-level write (.withResponse) confirms delivery. writeConfig() returns before the disconnect callback runs, so writesCompleted gets set to true in time. Co-Authored-By: Claude Opus 4.6 (1M context) --- PayfritBeacon/Provisioners/DXSmartProvisioner.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/PayfritBeacon/Provisioners/DXSmartProvisioner.swift b/PayfritBeacon/Provisioners/DXSmartProvisioner.swift index 5185694..dc46b12 100644 --- a/PayfritBeacon/Provisioners/DXSmartProvisioner.swift +++ b/PayfritBeacon/Provisioners/DXSmartProvisioner.swift @@ -173,6 +173,15 @@ final class DXSmartProvisioner: NSObject, BeaconProvisioner { for (index, (name, packet)) in commands.enumerated() { await diagnosticLog?.log("write", "[\(index + 1)/\(commands.count)] \(name) (\(packet.count) bytes)") + // SaveConfig (last command) causes beacon MCU to reboot — it never sends an ACK. + // Fire the BLE write and return immediately; the disconnect is expected. + if name == "SaveConfig" { + peripheral.writeValue(packet, for: writeChar, type: .withResponse) + await diagnosticLog?.log("write", "✅ [\(index + 1)/\(commands.count)] SaveConfig sent — beacon will reboot") + await diagnosticLog?.log("write", "✅ All commands written successfully") + return + } + // Retry each command up to 2 times — beacon BLE stack can be flaky var lastError: Error? for writeAttempt in 1...2 {