diff --git a/OpenHaystack/OpenHaystack/HaystackApp/AccessoryNearbyMonitor.swift b/OpenHaystack/OpenHaystack/HaystackApp/AccessoryNearbyMonitor.swift index 866dcdf..9f77ac6 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/AccessoryNearbyMonitor.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/AccessoryNearbyMonitor.swift @@ -14,32 +14,66 @@ class AccessoryNearbyMonitor: BluetoothAccessoryDelegate { var accessoryController: AccessoryController var scanner: BluetoothAccessoryScanner + var cleanup: Timer? + init(accessoryController: AccessoryController) { self.accessoryController = accessoryController self.scanner = BluetoothAccessoryScanner() self.initScanner() + self.initTimer() } func initScanner() { self.scanner.delegate = self } + func initTimer() { + self.cleanup = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in + self.removeNearbyAccessories() + } + } + func received(_ advertisement: Advertisement) { guard let accessory = getAccessoryForAdvertisement(advertisement) else { return } + updateNearbyAccessory(accessory) + } + + func updateNearbyAccessory(_ accessory: Accessory) { if !accessory.isNearby { // Only set on state change accessory.isNearby = true } + accessory.lastAdvertisement = Date() + } + + func removeNearbyAccessories(now: Date = Date(), timeout: TimeInterval = 10.0) { + let nearbyAccessories = self.accessoryController.accessories.filter({ $0.isNearby }) + for accessory in nearbyAccessories { + guard let lastAdvertisement = accessory.lastAdvertisement else { + continue + } + if lastAdvertisement + timeout < now { + accessory.isNearby = false + } + } } func getAccessoryForAdvertisement(_ advertisement: Advertisement) -> Accessory? { let accessory = - try? self.accessoryController.accessories.first { - let accessoryPublicKey = try $0.getAdvertisementKey().advanced(by: 6) - return accessoryPublicKey == advertisement.publicKeyPayload + self.accessoryController.accessories.first { + isAdvertisement(advertisement, from: $0) } ?? nil return accessory } + + func isAdvertisement(_ advertisement: Advertisement, from: Accessory) -> Bool { + do { + let accessoryPublicKey = try from.getAdvertisementKey().advanced(by: 6) + return accessoryPublicKey == advertisement.publicKeyPayload + } catch { + return false + } + } } diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Bluetooth/Advertisement.swift b/OpenHaystack/OpenHaystack/HaystackApp/Bluetooth/Advertisement.swift index 68fba1b..af29772 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Bluetooth/Advertisement.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Bluetooth/Advertisement.swift @@ -18,7 +18,7 @@ struct Advertisement { guard let manufacturerData = fromAdvertisementData[CBAdvertisementDataManufacturerDataKey] as? Data else { return nil } - try? self.init(fromManufacturerData: manufacturerData) + self.init(fromManufacturerData: manufacturerData) } init?(fromManufacturerData: Data) { diff --git a/OpenHaystack/OpenHaystack/HaystackApp/Model/Accessory.swift b/OpenHaystack/OpenHaystack/HaystackApp/Model/Accessory.swift index 3bb083c..0736ba9 100644 --- a/OpenHaystack/OpenHaystack/HaystackApp/Model/Accessory.swift +++ b/OpenHaystack/OpenHaystack/HaystackApp/Model/Accessory.swift @@ -53,6 +53,7 @@ class Accessory: ObservableObject, Codable, Identifiable, Equatable, Hashable { } } } + var lastAdvertisement: Date? init(name: String = "New accessory", color: Color = randomColor(), iconName: String = randomIcon()) throws { self.name = name