Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions Magic Switch/Model/Store/BluetoothPeripheralStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -337,9 +337,11 @@ final class BluetoothPeripheralStore: NSObject, ObservableObject, BluetoothPerip
self.intentionalReleases.removeValue(forKey: id)
guard let peripheral = self.peripherals.first(where: { $0.id == id }) else { continue }
guard let device = NetworkDeviceStore.shared.networkDevices.first,
device.pendingFingerprint == nil,
PairingStore.shared.isPaired
else {
// No peer to ask — these are ours; take them back.
// No trusted peer to ask — none registered, or one flagged as a
// TOFU identity mismatch. Either way, reclaim locally.
self.connectPeripheral(peripheral)
continue
}
Expand Down Expand Up @@ -1221,9 +1223,12 @@ final class BluetoothPeripheralStore: NSObject, ObservableObject, BluetoothPerip
private func reclaimIfPeerIsFree(_ peripheral: BluetoothPeripheral) {
let id = peripheral.id
guard PairingStore.shared.isPaired,
let device = NetworkDeviceStore.shared.networkDevices.first
let device = NetworkDeviceStore.shared.networkDevices.first,
device.pendingFingerprint == nil
else {
// No peer to consult — it's ours; take it.
// No trusted peer to consult — none registered, or one flagged as a
// TOFU identity mismatch. Either way it's ours; reclaim locally rather
// than auto-probing an untrusted peer with our now-stale key.
reconnectInFlight.remove(id)
connectPeripheral(peripheral, announcePairTimeout: false)
return
Expand Down
12 changes: 10 additions & 2 deletions Magic Switch/Model/Store/NetworkDeviceStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,12 @@ extension NetworkDeviceStore {
on device: NetworkDevice,
completion: @escaping (Result<Void, OutgoingFailure>) -> Void
) {
sendTwoFrameCommand(.holdsOne, payload: address, to: device, completion: completion)
// `HOLDS_ONE` is only ever a background watcher/reclaim probe, never a user
// action — opt out of the outbound limiter so its fixed cadence can't trip
// the limiter that gates real switches (mirrors the reachability poll).
sendTwoFrameCommand(
.holdsOne, payload: address, to: device, countsTowardRateLimit: false,
completion: completion)
}

/// Shared helper for "opcode + single payload frame, await OP_SUCCESS".
Expand All @@ -524,13 +529,16 @@ extension NetworkDeviceStore {
_ command: DeviceCommand,
payload: String,
to device: NetworkDevice,
countsTowardRateLimit: Bool = true,
completion: @escaping (Result<Void, OutgoingFailure>) -> Void
) {
guard PairingStore.shared.isPaired else {
completion(.failure(.notPaired))
return
}
let outgoing = OutgoingConnection(host: device.host, port: UInt16(device.port))
let outgoing = OutgoingConnection(
host: device.host, port: UInt16(device.port),
countsTowardRateLimit: countsTowardRateLimit)
outgoing.run(
body: { channel, done in
channel.send(Data(command.rawValue.utf8)) { err in
Expand Down