Instead of immediately failing on disconnect during authenticating or
writing states, retry up to 2 times with backoff. Resets passwordIndex
on reconnect so re-auth starts fresh (fixes issue where burned password
attempts caused retry failures). Also fixes passwordIndex reset in the
device-info safety-net reconnect path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The DX-Smart 0x30 device info query consistently causes beacon BLE disconnects,
which exhausted the retry counter and failed provisioning. The MAC address is
optional — API falls back to iBeacon UUID as hardware ID. Device info read is
still available in readConfig/check mode where it doesn't block provisioning.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The device info disconnect handler was sharing connectionRetryCount with
the initial connection retry logic. If earlier connection attempts burned
through retries, the device info handler had zero retries left and
immediately hit "retries exhausted" — causing the "Disconnected while
reading device information" error John reported.
Now uses a separate deviceInfoRetryCount (max 2) so device info retries
are independent of connection retries.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The beacon sometimes drops BLE connection during the optional MAC address
query (0x30) after auth. Previously this failed with "Disconnected after
auth during device info read". Now we reconnect and skip the MAC read on
retry, going straight to config write. MAC is nice-to-have, not required.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The comment said "treat as non-fatal" but the code calls fail() — which
is correct behavior since we can't write config without a connection.
Updated comment to accurately describe the fail-with-retry-prompt flow.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add isTerminating flag to guard succeed()/fail() against double invocation
from racing didWriteValueFor + didDisconnectPeripheral callbacks
- Only call cancelPeripheralConnection when peripheral.state == .connected
(avoids triggering spurious didDisconnectPeripheral on already-disconnected peripheral)
- Handle disconnect during device info read (post-auth) with specific error message
- Include state info in unexpected disconnect errors for easier debugging
- Early-return structure in disconnect handler for clearer control flow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous fix only caught the disconnect callback path. But CoreBluetooth
can also fire didWriteValueFor with an error when the beacon reboots mid-ATT
response. This was hitting fail() at the isNonFatalCommand check instead of
being treated as success.
Now handles both paths:
1. didDisconnectPeripheral with state=.writing at last command → succeed()
2. didWriteValueFor error for SaveConfig (last command) → succeed()
Also added detailed state/index logging to disconnect handler for diagnostics.
The DX-Smart CP28 beacon reboots after receiving SaveConfig (0x60) to
persist config to flash. This drops the BLE connection before the write
callback fires, causing the app to report "Unexpected disconnect" even
though the config was successfully saved.
Now we check if we're on the last command (SaveConfig) when disconnect
occurs — if so, treat it as success instead of failure.
Co-Authored-By: Luna <luna@payfrit.com>
Without this reset, if provision() was called first and incremented
passwordIndex, a subsequent readConfig() call would start at the wrong
password index and potentially skip the correct password entirely.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- DX-Smart auth now tries multiple passwords in sequence (555555, dx1234, 000000)
instead of hardcoding a single password. Matches Android behavior for better
compatibility across firmware versions.
- Added ProvisioningError enum with structured error codes (CONNECTION_FAILED,
AUTH_FAILED, SERVICE_NOT_FOUND, WRITE_FAILED, etc.) matching Android's
BeaconConfig error codes. All fail() calls now tagged with codes for better
debugging and error reporting.
- Added ProvisioningResult.failureWithCode case and handling in ScanView.
- Added missing API endpoints that Android has:
- getBusiness() - single business fetch
- getBusinessName() - cached business name lookup
- allocateServicePointMinor() - minor value allocation
- Fixed stray print() in Api.swift to use DebugLog.shared.log() for consistency.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Created UUIDFormatting.swift with .normalizedUUID and .uuidWithDashes
String extensions, replacing 4 duplicate formatUuidWithDashes() methods
and 6+ inline .replacingOccurrences(of: "-", with: "").uppercased() calls
across Api.swift, BeaconScanner.swift, ScanView.swift,
ServicePointListView.swift, BeaconBanList.swift, and BeaconProvisioner.swift.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Modified BeaconProvisioner to read device info (0x30) before writing config
- Extract MAC address from beacon and return in ProvisioningResult
- Use MAC address as hardware_id field (snake_case for backend)
- Reorder scan view: Configurable Devices section now appears first
- Add debug logging for beacon registration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fix critical packet format bugs matching SDK: frame select/type/trigger/disable
commands now send empty data, RSSI@1m corrected to -59 dBm. Add DebugLog,
read-config mode, service point list, and dev scheme.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fixed App Store icon display with ios-marketing idiom
- Added iPad orientation support for multitasking
- Added UILaunchScreen for iPad requirements
- Removed unused BLE permissions and files from build
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>