diff --git a/Sources/CombustionBLE/AdvertisingData.swift b/Sources/CombustionBLE/AdvertisingData.swift index 5e3b729..8e555ae 100644 --- a/Sources/CombustionBLE/AdvertisingData.swift +++ b/Sources/CombustionBLE/AdvertisingData.swift @@ -78,7 +78,6 @@ extension AdvertisingData { serialNumber = value - // Temperatures (8 13-bit) values let tempData = data.subdata(in: Constants.TEMPERATURE_RANGE) temperatures = ProbeTemperatures.fromRawData(data: tempData) @@ -88,9 +87,16 @@ extension AdvertisingData { extension AdvertisingData { // Fake data initializer for previews - public init?(fakeSerial: UInt32) { + public init(fakeSerial: UInt32) { type = .PROBE temperatures = ProbeTemperatures.withFakeData() serialNumber = fakeSerial } + + // Fake data initializer for Simulated Probe + public init(fakeSerial: UInt32, fakeTemperatures: ProbeTemperatures) { + type = .PROBE + temperatures = fakeTemperatures + serialNumber = fakeSerial + } } diff --git a/Sources/CombustionBLE/Device.swift b/Sources/CombustionBLE/Device.swift index ccd5d59..4582e5f 100644 --- a/Sources/CombustionBLE/Device.swift +++ b/Sources/CombustionBLE/Device.swift @@ -84,9 +84,6 @@ extension Device { if(connectionState != .connected) { DeviceManager.shared.connectToDevice(self) } - - // Update the DeviceManager's record for this device - DeviceManager.shared.devices[self.id] = self } /// Mark that app should no longer attempt to maintain a connection to this device. @@ -96,9 +93,6 @@ extension Device { // Disconnect if connected DeviceManager.shared.disconnectFromDevice(self) - - // Update the DeviceManager's record for this device - DeviceManager.shared.devices[self.id] = self } func updateConnectionState(_ state: ConnectionState) { diff --git a/Sources/CombustionBLE/DeviceManager.swift b/Sources/CombustionBLE/DeviceManager.swift index dbb1efa..3402b4c 100644 --- a/Sources/CombustionBLE/DeviceManager.swift +++ b/Sources/CombustionBLE/DeviceManager.swift @@ -36,7 +36,7 @@ public class DeviceManager : ObservableObject { /// Dictionary of discovered devices. /// key = string representation of device identifier (UUID) - @Published public var devices : [String: Device] = [String: Device]() + @Published public private(set) var devices : [String: Device] = [String: Device]() /// Dictionary of discovered probes (subset of devices). @@ -50,6 +50,10 @@ public class DeviceManager : ObservableObject { } } + public func addSimulatedProbe() { + addDevice(device: SimulatedProbe()) + } + /// Private initializer to enforce singleton private init() { BleManager.shared.delegate = self @@ -62,7 +66,6 @@ public class DeviceManager : ObservableObject { } } - /// Adds a device to the local list. /// - parameter device: Add device to list of known devices. private func addDevice(device: Device) { @@ -99,13 +102,21 @@ public class DeviceManager : ObservableObject { } func connectToDevice(_ device: Device) { - // print("Connect to : \(device.serialNumber)") - BleManager.shared.connect(id: device.id) + if let _ = device as? SimulatedProbe, let uuid = UUID(uuidString: device.id) { + didConnectTo(id: uuid) + } + else { + BleManager.shared.connect(id: device.id) + } } func disconnectFromDevice(_ device: Device) { - // print("Disconnect from : \(device.serialNumber)") - BleManager.shared.disconnect(id: device.id) + if let _ = device as? SimulatedProbe, let uuid = UUID(uuidString: device.id) { + didDisconnectFrom(id: uuid) + } + else { + BleManager.shared.disconnect(id: device.id) + } } /// Request log messages from the specified device. diff --git a/Sources/CombustionBLE/Probe.swift b/Sources/CombustionBLE/Probe.swift index 943800c..62463d1 100644 --- a/Sources/CombustionBLE/Probe.swift +++ b/Sources/CombustionBLE/Probe.swift @@ -98,7 +98,7 @@ extension Probe { logsUpToDate = true } - print("Updating status! Temperature log size: \(temperatureLog.dataPoints.count)") +// print("Updating status! Temperature log size: \(temperatureLog.dataPoints.count)") lastUpdateTime = Date() } diff --git a/Sources/CombustionBLE/ProbeTemperatures.swift b/Sources/CombustionBLE/ProbeTemperatures.swift index c09023e..463b2ef 100644 --- a/Sources/CombustionBLE/ProbeTemperatures.swift +++ b/Sources/CombustionBLE/ProbeTemperatures.swift @@ -83,4 +83,19 @@ extension ProbeTemperatures { ] return ProbeTemperatures(values: temperatures) } + + // Generates randome data for Simulated Probe + static func withRandomData() -> ProbeTemperatures { + let temperatures : [Double] = [ + Double.random(in: 45.0 ..< 60.0), + Double.random(in: 45.0 ..< 60.0), + Double.random(in: 45.0 ..< 60.0), + Double.random(in: 45.0 ..< 60.0), + Double.random(in: 45.0 ..< 60.0), + Double.random(in: 45.0 ..< 60.0), + Double.random(in: 45.0 ..< 60.0), + Double.random(in: 45.0 ..< 60.0), + ] + return ProbeTemperatures(values: temperatures) + } } diff --git a/Sources/CombustionBLE/SimulatedProbe.swift b/Sources/CombustionBLE/SimulatedProbe.swift new file mode 100644 index 0000000..bbade23 --- /dev/null +++ b/Sources/CombustionBLE/SimulatedProbe.swift @@ -0,0 +1,84 @@ +// SimulatedProbe.swift +// Simulated Probe + +/*-- +MIT License + +Copyright (c) 2021 Combustion Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--*/ + +import Foundation + +class SimulatedProbe: Probe { + init() { + let advertising = AdvertisingData(fakeSerial: UInt32.random(in: 0 ..< UINT32_MAX), + fakeTemperatures: ProbeTemperatures.withRandomData()) + super.init(advertising, RSSI: SimulatedProbe.randomeRSSI(), id: UUID()) + + firmareVersion = "v1.2.3" + + // Create timer to update probe with fake advertising packets + Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in + self?.updateFakeAdvertising() + } + + // Create timer to update probe with fake status notifications + Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in + self?.updateFakeStatus() + } + } + + public override var name: String { + var nameStr = super.name + nameStr.removeLast(4) + return String(format: "SIM-\(nameStr)") + } + + static func randomeRSSI() -> NSNumber { + return NSNumber(value: Int.random(in: -80 ..< -40)) + } + + private func updateFakeAdvertising() { + let advertising = AdvertisingData(fakeSerial: UInt32.random(in: 0 ..< UINT32_MAX), + fakeTemperatures: ProbeTemperatures.withRandomData()) + updateWithAdvertising(advertising, RSSI: SimulatedProbe.randomeRSSI()) + } + + private func updateFakeStatus() { + guard connectionState == .connected else { return } + + let firstSeq = temperatureLog.dataPoints.first?.sequenceNum ?? 0 + + let lastSequence: UInt32 + if let last = temperatureLog.dataPoints.last?.sequenceNum { + lastSequence = last + 1 + } + else { + lastSequence = 0 + } + + let deviceStatus = DeviceStatus(minSequenceNumber: firstSeq, + maxSequenceNumber: lastSequence, + temperatures: ProbeTemperatures.withRandomData()) + + updateProbeStatus(deviceStatus: deviceStatus) + } +}