diff --git a/Common/LocalizedString.swift b/Common/LocalizedString.swift index 72a61df82..e0376951c 100644 --- a/Common/LocalizedString.swift +++ b/Common/LocalizedString.swift @@ -12,6 +12,10 @@ private class FrameworkBundle { static let main = Bundle(for: FrameworkBundle.self) } -func LocalizedString(_ key: String, tableName: String? = nil, value: String = "", comment: String) -> String { - return NSLocalizedString(key, tableName: tableName, bundle: FrameworkBundle.main, value: value, comment: comment) +func LocalizedString(_ key: String, tableName: String? = nil, value: String? = nil, comment: String) -> String { + if let value = value { + return NSLocalizedString(key, tableName: tableName, bundle: FrameworkBundle.main, value: value, comment: comment) + } else { + return NSLocalizedString(key, tableName: tableName, bundle: FrameworkBundle.main, comment: comment) + } } diff --git a/Common/TimeInterval.swift b/Common/TimeInterval.swift index 5a8046a6c..03ea59869 100644 --- a/Common/TimeInterval.swift +++ b/Common/TimeInterval.swift @@ -10,6 +10,11 @@ import Foundation extension TimeInterval { + + static func days(_ days: Double) -> TimeInterval { + return self.init(days: days) + } + static func hours(_ hours: Double) -> TimeInterval { return self.init(hours: hours) } @@ -30,13 +35,17 @@ extension TimeInterval { return self.init(milliseconds / 1000) } - init(minutes: Double) { - self.init(minutes * 60) + init(days: Double) { + self.init(hours: days * 24) } - + init(hours: Double) { self.init(minutes: hours * 60) } + + init(minutes: Double) { + self.init(minutes * 60) + } init(seconds: Double) { self.init(seconds) diff --git a/Crypto/Info.plist b/Crypto/Info.plist index 21e7e6da6..5baf5d16b 100644 --- a/Crypto/Info.plist +++ b/Crypto/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/MinimedKit/Info.plist b/MinimedKit/Info.plist index 0de69c100..241ef8699 100644 --- a/MinimedKit/Info.plist +++ b/MinimedKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleSignature ???? CFBundleVersion diff --git a/MinimedKit/Messages/PumpErrorMessageBody.swift b/MinimedKit/Messages/PumpErrorMessageBody.swift index ea91b3ee6..ffff07b05 100644 --- a/MinimedKit/Messages/PumpErrorMessageBody.swift +++ b/MinimedKit/Messages/PumpErrorMessageBody.swift @@ -13,6 +13,7 @@ public enum PumpErrorCode: UInt8, CustomStringConvertible { case commandRefused = 0x08 case maxSettingExceeded = 0x09 case bolusInProgress = 0x0c + case pageDoesNotExist = 0x0d public var description: String { switch self { @@ -22,6 +23,8 @@ public enum PumpErrorCode: UInt8, CustomStringConvertible { return LocalizedString("Max setting exceeded", comment: "Pump error code describing max setting exceeded") case .bolusInProgress: return LocalizedString("Bolus in progress", comment: "Pump error code when bolus is in progress") + case .pageDoesNotExist: + return LocalizedString("History page does not exist", comment: "Pump error code when invalid history page is requested") } } diff --git a/MinimedKit/PumpManager/MinimedPumpManager.swift b/MinimedKit/PumpManager/MinimedPumpManager.swift index 0d377625b..cb1750d1d 100644 --- a/MinimedKit/PumpManager/MinimedPumpManager.swift +++ b/MinimedKit/PumpManager/MinimedPumpManager.swift @@ -15,42 +15,51 @@ import os.log public class MinimedPumpManager: RileyLinkPumpManager, PumpManager { public static let managerIdentifier: String = "Minimed500" - public init(state: MinimedPumpManagerState, rileyLinkManager: RileyLinkDeviceManager?) { + public init(state: MinimedPumpManagerState, rileyLinkDeviceProvider: RileyLinkDeviceProvider, rileyLinkConnectionManager: RileyLinkConnectionManager? = nil, pumpOps: PumpOps? = nil) { self.state = state - super.init(rileyLinkPumpManagerState: state.rileyLinkPumpManagerState, rileyLinkManager: rileyLinkManager) + super.init(rileyLinkDeviceProvider: rileyLinkDeviceProvider, rileyLinkConnectionManager: rileyLinkConnectionManager) // Pump communication let idleListeningEnabled = state.pumpModel.hasMySentry - self.pumpOps = PumpOps(pumpSettings: state.pumpSettings, pumpState: state.pumpState, delegate: self) + self.pumpOps = pumpOps ?? PumpOps(pumpSettings: state.pumpSettings, pumpState: state.pumpState, delegate: self) - self.rileyLinkManager.idleListeningState = idleListeningEnabled ? MinimedPumpManagerState.idleListeningEnabledDefaults : .disabled + self.rileyLinkDeviceProvider.idleListeningState = idleListeningEnabled ? MinimedPumpManagerState.idleListeningEnabledDefaults : .disabled } public required convenience init?(rawState: PumpManager.RawStateValue) { - guard let state = MinimedPumpManagerState(rawValue: rawState) else { + guard let state = MinimedPumpManagerState(rawValue: rawState), + let connectionManagerState = state.rileyLinkConnectionManagerState else + { return nil } - - self.init(state: state, rileyLinkManager: nil) + + let rileyLinkConnectionManager = RileyLinkConnectionManager(state: connectionManagerState) + + self.init(state: state, rileyLinkDeviceProvider: rileyLinkConnectionManager.deviceProvider, rileyLinkConnectionManager: rileyLinkConnectionManager) + + rileyLinkConnectionManager.delegate = self } public var rawState: PumpManager.RawStateValue { return state.rawValue } - override public var rileyLinkPumpManagerState: RileyLinkPumpManagerState { - didSet { - state.rileyLinkPumpManagerState = rileyLinkPumpManagerState - } - } - // TODO: apply lock public private(set) var state: MinimedPumpManagerState { didSet { pumpManagerDelegate?.pumpManagerDidUpdateState(self) } } + + override public var rileyLinkConnectionManagerState: RileyLinkConnectionManagerState? { + get { + return state.rileyLinkConnectionManagerState + } + set { + state.rileyLinkConnectionManagerState = newValue + } + } public weak var cgmManagerDelegate: CGMManagerDelegate? @@ -106,7 +115,7 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager { /// characteristic which can cause the app to wake. For most users, the G5 Transmitter and /// G4 Receiver are reliable as hearbeats, but users who find their resources extremely constrained /// due to greedy apps or older devices may choose to always enable the timer by always setting `true` - self.rileyLinkManager.timerTickEnabled = self.isPumpDataStale || (self.pumpManagerDelegate?.pumpManagerShouldProvideBLEHeartbeat(self) == true) + self.rileyLinkDeviceProvider.timerTickEnabled = self.isPumpDataStale || (self.pumpManagerDelegate?.pumpManagerShouldProvideBLEHeartbeat(self) == true) } } @@ -204,14 +213,14 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager { } } catch let error { self.log.error("Device %{public}@ auto-tune failed with error: %{public}@", device.name ?? "", String(describing: error)) - self.rileyLinkManager.deprioritize(device) + self.rileyLinkDeviceProvider.deprioritize(device, completion: nil) if let error = error as? LocalizedError { self.pumpManagerDelegate?.pumpManager(self, didError: PumpManagerError.communication(MinimedPumpManagerError.tuneFailed(error))) } } } } else { - rileyLinkManager.deprioritize(device) + rileyLinkDeviceProvider.deprioritize(device, completion: nil) } } @@ -343,7 +352,7 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager { /// - completion: A closure called once upon completion /// - error: An error describing why the fetch and/or store failed private func fetchPumpHistory(_ completion: @escaping (_ error: Error?) -> Void) { - rileyLinkManager.getDevices { (devices) in + rileyLinkDeviceProvider.getDevices { (devices) in guard let device = devices.firstConnected else { completion(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink)) return @@ -376,7 +385,7 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager { /// TODO: Isolate to queue private var isPumpDataStale: Bool { // How long should we wait before we poll for new pump data? - let pumpStatusAgeTolerance = rileyLinkManager.idleListeningEnabled ? TimeInterval(minutes: 6) : TimeInterval(minutes: 4) + let pumpStatusAgeTolerance = rileyLinkDeviceProvider.idleListeningEnabled ? TimeInterval(minutes: 6) : TimeInterval(minutes: 4) return isReservoirDataOlderThan(timeIntervalSinceNow: -pumpStatusAgeTolerance) } @@ -400,7 +409,7 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager { */ /// TODO: Isolate to queue public func assertCurrentPumpData() { - rileyLinkManager.assertIdleListening(forcingRestart: true) + rileyLinkDeviceProvider.assertIdleListening(forcingRestart: true) guard isPumpDataStale else { return @@ -408,7 +417,7 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager { self.log.debug("Pump data is stale, fetching.") - rileyLinkManager.getDevices { (devices) in + rileyLinkDeviceProvider.getDevices { (devices) in guard let device = devices.firstConnected else { let error = PumpManagerError.connection(MinimedPumpManagerError.noRileyLink) self.log.error("No devices found while fetching pump data") @@ -492,7 +501,7 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager { // If we don't have recent pump data, or the pump was recently rewound, read new pump data before bolusing. let shouldReadReservoir = isReservoirDataOlderThan(timeIntervalSinceNow: .minutes(-6)) - pumpOps.runSession(withName: "Bolus", using: rileyLinkManager.firstConnectedDevice) { (session) in + pumpOps.runSession(withName: "Bolus", using: rileyLinkDeviceProvider.firstConnectedDevice) { (session) in guard let session = session else { completion(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink)) return @@ -536,7 +545,7 @@ public class MinimedPumpManager: RileyLinkPumpManager, PumpManager { } public func enactTempBasal(unitsPerHour: Double, for duration: TimeInterval, completion: @escaping (PumpManagerResult) -> Void) { - pumpOps.runSession(withName: "Set Temp Basal", using: rileyLinkManager.firstConnectedDevice) { (session) in + pumpOps.runSession(withName: "Set Temp Basal", using: rileyLinkDeviceProvider.firstConnectedDevice) { (session) in guard let session = session else { completion(.failure(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink))) return @@ -646,7 +655,7 @@ extension MinimedPumpManager: CGMManager { } public func fetchNewDataIfNeeded(_ completion: @escaping (CGMResult) -> Void) { - rileyLinkManager.getDevices { (devices) in + rileyLinkDeviceProvider.getDevices { (devices) in guard let device = devices.firstConnected else { completion(.error(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink))) return @@ -685,3 +694,4 @@ extension MinimedPumpManager: CGMManager { } } } + diff --git a/MinimedKit/PumpManager/MinimedPumpManagerState.swift b/MinimedKit/PumpManager/MinimedPumpManagerState.swift index fb8b6d00d..3446cd8b3 100644 --- a/MinimedKit/PumpManager/MinimedPumpManagerState.swift +++ b/MinimedKit/PumpManager/MinimedPumpManagerState.swift @@ -13,7 +13,7 @@ import RileyLinkBLEKit public struct MinimedPumpManagerState: RawRepresentable, Equatable { public typealias RawValue = PumpManager.RawStateValue - public static let version = 1 + public static let version = 2 public var batteryChemistry: BatteryChemistryType @@ -52,30 +52,30 @@ public struct MinimedPumpManagerState: RawRepresentable, Equatable { } } - public var rileyLinkPumpManagerState: RileyLinkPumpManagerState + public var rileyLinkConnectionManagerState: RileyLinkConnectionManagerState? public var timeZone: TimeZone - public init(batteryChemistry: BatteryChemistryType = .alkaline, preferredInsulinDataSource: InsulinDataSource = .pumpHistory, pumpColor: PumpColor, pumpID: String, pumpModel: PumpModel, pumpRegion: PumpRegion, rileyLinkPumpManagerState: RileyLinkPumpManagerState, timeZone: TimeZone) { + public init(batteryChemistry: BatteryChemistryType = .alkaline, preferredInsulinDataSource: InsulinDataSource = .pumpHistory, pumpColor: PumpColor, pumpID: String, pumpModel: PumpModel, pumpRegion: PumpRegion, rileyLinkConnectionManagerState: RileyLinkConnectionManagerState?, timeZone: TimeZone) { self.batteryChemistry = batteryChemistry self.preferredInsulinDataSource = preferredInsulinDataSource self.pumpColor = pumpColor self.pumpID = pumpID self.pumpModel = pumpModel self.pumpRegion = pumpRegion - self.rileyLinkPumpManagerState = rileyLinkPumpManagerState + self.rileyLinkConnectionManagerState = rileyLinkConnectionManagerState self.timeZone = timeZone } public init?(rawValue: RawValue) { guard + let version = rawValue["version"] as? Int, let batteryChemistryRaw = rawValue["batteryChemistry"] as? BatteryChemistryType.RawValue, let insulinDataSourceRaw = rawValue["insulinDataSource"] as? InsulinDataSource.RawValue, let pumpColorRaw = rawValue["pumpColor"] as? PumpColor.RawValue, let pumpID = rawValue["pumpID"] as? String, let pumpModelNumber = rawValue["pumpModel"] as? PumpModel.RawValue, let pumpRegionRaw = rawValue["pumpRegion"] as? PumpRegion.RawValue, - let rileyLinkPumpManagerStateRaw = rawValue["rileyLinkPumpManagerState"] as? RileyLinkPumpManagerState.RawValue, let timeZoneSeconds = rawValue["timeZone"] as? Int, let batteryChemistry = BatteryChemistryType(rawValue: batteryChemistryRaw), @@ -83,12 +83,27 @@ public struct MinimedPumpManagerState: RawRepresentable, Equatable { let pumpColor = PumpColor(rawValue: pumpColorRaw), let pumpModel = PumpModel(rawValue: pumpModelNumber), let pumpRegion = PumpRegion(rawValue: pumpRegionRaw), - let rileyLinkPumpManagerState = RileyLinkPumpManagerState(rawValue: rileyLinkPumpManagerStateRaw), let timeZone = TimeZone(secondsFromGMT: timeZoneSeconds) else { return nil } - + + var rileyLinkConnectionManagerState: RileyLinkConnectionManagerState? = nil + + // Migrate + if version == 1 + { + if let oldRileyLinkPumpManagerStateRaw = rawValue["rileyLinkPumpManagerState"] as? [String : Any], + let connectedPeripheralIDs = oldRileyLinkPumpManagerStateRaw["connectedPeripheralIDs"] as? [String] + { + rileyLinkConnectionManagerState = RileyLinkConnectionManagerState(autoConnectIDs: Set(connectedPeripheralIDs)) + } + } else { + if let rawState = rawValue["rileyLinkConnectionManagerState"] as? RileyLinkConnectionManagerState.RawValue { + rileyLinkConnectionManagerState = RileyLinkConnectionManagerState(rawValue: rawState) + } + } + self.init( batteryChemistry: batteryChemistry, preferredInsulinDataSource: insulinDataSource, @@ -96,24 +111,28 @@ public struct MinimedPumpManagerState: RawRepresentable, Equatable { pumpID: pumpID, pumpModel: pumpModel, pumpRegion: pumpRegion, - rileyLinkPumpManagerState: rileyLinkPumpManagerState, + rileyLinkConnectionManagerState: rileyLinkConnectionManagerState, timeZone: timeZone ) } public var rawValue: RawValue { - return [ + var value: [String : Any] = [ "batteryChemistry": batteryChemistry.rawValue, "insulinDataSource": preferredInsulinDataSource.rawValue, "pumpColor": pumpColor.rawValue, "pumpID": pumpID, "pumpModel": pumpModel.rawValue, "pumpRegion": pumpRegion.rawValue, - "rileyLinkPumpManagerState": rileyLinkPumpManagerState.rawValue, "timeZone": timeZone.secondsFromGMT(), "version": MinimedPumpManagerState.version, - ] + ] + + if let rileyLinkConnectionManagerState = rileyLinkConnectionManagerState { + value["rileyLinkConnectionManagerState"] = rileyLinkConnectionManagerState.rawValue + } + return value } } @@ -134,7 +153,7 @@ extension MinimedPumpManagerState: CustomDebugStringConvertible { "pumpModel: \(pumpModel.rawValue)", "pumpRegion: \(pumpRegion)", "timeZone: \(timeZone)", - String(reflecting: rileyLinkPumpManagerState), + String(reflecting: rileyLinkConnectionManagerState), ].joined(separator: "\n") } } diff --git a/MinimedKitTests/Info.plist b/MinimedKitTests/Info.plist index 5a6398fbf..9b0436e59 100644 --- a/MinimedKitTests/Info.plist +++ b/MinimedKitTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleSignature ???? CFBundleVersion diff --git a/MinimedKitUI/Base.lproj/MinimedPumpManager.storyboard b/MinimedKitUI/Base.lproj/MinimedPumpManager.storyboard index 7719d3ed5..7acfa46f5 100644 --- a/MinimedKitUI/Base.lproj/MinimedPumpManager.storyboard +++ b/MinimedKitUI/Base.lproj/MinimedPumpManager.storyboard @@ -185,6 +185,7 @@ + @@ -573,6 +574,22 @@ + + + + + + + + + + + + + + + + @@ -581,5 +598,6 @@ + diff --git a/MinimedKitUI/CommandResponseViewController.swift b/MinimedKitUI/CommandResponseViewController.swift index 5c2352f46..91c7c69a1 100644 --- a/MinimedKitUI/CommandResponseViewController.swift +++ b/MinimedKitUI/CommandResponseViewController.swift @@ -38,9 +38,9 @@ extension CommandResponseViewController { } } - static func changeTime(ops: PumpOps?, rileyLinkManager: RileyLinkDeviceManager) -> T { + static func changeTime(ops: PumpOps?, rileyLinkDeviceProvider: RileyLinkDeviceProvider) -> T { return T { (completionHandler) -> String in - ops?.runSession(withName: "Set time", using: rileyLinkManager.firstConnectedDevice) { (session) in + ops?.runSession(withName: "Set time", using: rileyLinkDeviceProvider.firstConnectedDevice) { (session) in let response: String do { guard let session = session else { diff --git a/MinimedKitUI/Info.plist b/MinimedKitUI/Info.plist index f936f8d95..f841d8f2c 100644 --- a/MinimedKitUI/Info.plist +++ b/MinimedKitUI/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/MinimedKitUI/MinimedPumpManager+UI.swift b/MinimedKitUI/MinimedPumpManager+UI.swift index da9ccb9f5..36485b663 100644 --- a/MinimedKitUI/MinimedPumpManager+UI.swift +++ b/MinimedKitUI/MinimedPumpManager+UI.swift @@ -29,7 +29,7 @@ extension MinimedPumpManager: PumpManagerUI { // MARK: - DeliveryLimitSettingsTableViewControllerSyncSource extension MinimedPumpManager { public func syncDeliveryLimitSettings(for viewController: DeliveryLimitSettingsTableViewController, completion: @escaping (DeliveryLimitSettingsResult) -> Void) { - pumpOps.runSession(withName: "Save Settings", using: rileyLinkManager.firstConnectedDevice) { (session) in + pumpOps.runSession(withName: "Save Settings", using: rileyLinkDeviceProvider.firstConnectedDevice) { (session) in guard let session = session else { completion(.failure(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink))) return @@ -70,7 +70,7 @@ extension MinimedPumpManager { // MARK: - SingleValueScheduleTableViewControllerSyncSource extension MinimedPumpManager { public func syncScheduleValues(for viewController: SingleValueScheduleTableViewController, completion: @escaping (RepeatingScheduleValueResult) -> Void) { - pumpOps.runSession(withName: "Save Basal Profile", using: rileyLinkManager.firstConnectedDevice) { (session) in + pumpOps.runSession(withName: "Save Basal Profile", using: rileyLinkDeviceProvider.firstConnectedDevice) { (session) in guard let session = session else { completion(.failure(PumpManagerError.connection(MinimedPumpManagerError.noRileyLink))) return diff --git a/MinimedKitUI/MinimedPumpSettingsViewController.swift b/MinimedKitUI/MinimedPumpSettingsViewController.swift index f895ccd70..1cc8a6ff7 100644 --- a/MinimedKitUI/MinimedPumpSettingsViewController.swift +++ b/MinimedKitUI/MinimedPumpSettingsViewController.swift @@ -194,7 +194,7 @@ class MinimedPumpSettingsViewController: RileyLinkSettingsViewController { case .settings: switch SettingsRow(rawValue: indexPath.row)! { case .timeZoneOffset: - let vc = CommandResponseViewController.changeTime(ops: pumpManager.pumpOps, rileyLinkManager: pumpManager.rileyLinkManager) + let vc = CommandResponseViewController.changeTime(ops: pumpManager.pumpOps, rileyLinkDeviceProvider: pumpManager.rileyLinkDeviceProvider) vc.title = sender?.textLabel?.text show(vc, sender: indexPath) diff --git a/MinimedKitUI/RileyLinkMinimedDeviceTableViewController.swift b/MinimedKitUI/RileyLinkMinimedDeviceTableViewController.swift index 1365708c2..2aaa39d50 100644 --- a/MinimedKitUI/RileyLinkMinimedDeviceTableViewController.swift +++ b/MinimedKitUI/RileyLinkMinimedDeviceTableViewController.swift @@ -50,6 +50,16 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController { cellForRow(.version)?.detailTextLabel?.text = firmwareVersion } } + + private var uptime: TimeInterval? { + didSet { + guard isViewLoaded else { + return + } + + cellForRow(.uptime)?.setDetailAge(uptime) + } + } private var lastIdle: Date? { didSet { @@ -99,6 +109,17 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController { @objc func updateRSSI() { device.readRSSI() } + + func updateUptime() { + device.runSession(withName: "Get stats for uptime") { (session) in + do { + let statistics = try session.getRileyLinkStatistics() + DispatchQueue.main.async { + self.uptime = statistics.uptime + } + } catch { } + } + } private func updateDeviceStatus() { device.getStatus { (status) in @@ -165,6 +186,8 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController { appeared = true updateRSSI() + + updateUptime() } public override func viewWillDisappear(_ animated: Bool) { @@ -216,6 +239,7 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController { case version case rssi case connection + case uptime case idleStatus } @@ -289,8 +313,10 @@ public class RileyLinkMinimedDeviceTableViewController: UITableViewController { cell.detailTextLabel?.text = device.peripheralState.description case .rssi: cell.textLabel?.text = LocalizedString("Signal Strength", comment: "The title of the cell showing BLE signal strength (RSSI)") - cell.setDetailRSSI(bleRSSI, formatter: integerFormatter) + case .uptime: + cell.textLabel?.text = NSLocalizedString("Uptime", comment: "The title of the cell showing uptime") + cell.setDetailAge(uptime) case .idleStatus: cell.textLabel?.text = LocalizedString("On Idle", comment: "The title of the cell showing the last idle") cell.setDetailDate(lastIdle, formatter: dateFormatter) @@ -482,6 +508,18 @@ extension RileyLinkMinimedDeviceTableViewController: TextFieldTableViewControlle } } +private extension TimeInterval { + func format(using units: NSCalendar.Unit) -> String? { + let formatter = DateComponentsFormatter() + formatter.allowedUnits = units + formatter.unitsStyle = .full + formatter.zeroFormattingBehavior = .dropLeading + formatter.maximumUnitCount = 2 + + return formatter.string(from: self) + } +} + private extension UITableViewCell { func setDetailDate(_ date: Date?, formatter: DateFormatter) { @@ -496,6 +534,14 @@ private extension UITableViewCell { detailTextLabel?.text = formatter.decibleString(from: decibles) ?? "-" } + func setDetailAge(_ age: TimeInterval?) { + if let age = age { + detailTextLabel?.text = age.format(using: [.day, .hour, .minute]) + } else { + detailTextLabel?.text = "" + } + } + func setAwakeUntil(_ awakeUntil: Date?, formatter: DateFormatter) { switch awakeUntil { case let until? where until.timeIntervalSinceNow < 0: diff --git a/MinimedKitUI/Setup/MinimedPumpIDSetupViewController.swift b/MinimedKitUI/Setup/MinimedPumpIDSetupViewController.swift index 142186749..f2b26129f 100644 --- a/MinimedKitUI/Setup/MinimedPumpIDSetupViewController.swift +++ b/MinimedKitUI/Setup/MinimedPumpIDSetupViewController.swift @@ -85,7 +85,7 @@ class MinimedPumpIDSetupViewController: SetupTableViewController { pumpID: pumpID, pumpModel: pumpModel, pumpRegion: pumpRegion, - rileyLinkPumpManagerState: self.rileyLinkPumpManager.rileyLinkPumpManagerState, + rileyLinkConnectionManagerState: rileyLinkPumpManager.rileyLinkConnectionManagerState, timeZone: timeZone ) } @@ -96,7 +96,11 @@ class MinimedPumpIDSetupViewController: SetupTableViewController { return nil } - return MinimedPumpManager(state: pumpManagerState, rileyLinkManager: rileyLinkPumpManager.rileyLinkManager) + return MinimedPumpManager( + state: pumpManagerState, + rileyLinkDeviceProvider: rileyLinkPumpManager.rileyLinkDeviceProvider, + rileyLinkConnectionManager: rileyLinkPumpManager.rileyLinkConnectionManager, + pumpOps: self.pumpOps) } // MARK: - @@ -179,7 +183,6 @@ class MinimedPumpIDSetupViewController: SetupTableViewController { footerView.primaryButton.setConnectTitle() lastError = nil case .completed: - pumpOps = nil pumpIDTextField.isEnabled = true activityIndicator.state = .completed footerView.primaryButton.isEnabled = true @@ -233,7 +236,7 @@ class MinimedPumpIDSetupViewController: SetupTableViewController { let pumpOps = PumpOps(pumpSettings: settings, pumpState: pumpState, delegate: self) self.pumpOps = pumpOps - pumpOps.runSession(withName: "Pump ID Setup", using: rileyLinkPumpManager.rileyLinkManager.firstConnectedDevice, { (session) in + pumpOps.runSession(withName: "Pump ID Setup", using: rileyLinkPumpManager.rileyLinkDeviceProvider.firstConnectedDevice, { (session) in guard let session = session else { DispatchQueue.main.async { self.lastError = PumpManagerError.connection(MinimedPumpManagerError.noRileyLink) diff --git a/MinimedKitUI/Setup/MinimedPumpManagerSetupViewController.swift b/MinimedKitUI/Setup/MinimedPumpManagerSetupViewController.swift index a95c1875c..92d79b601 100644 --- a/MinimedKitUI/Setup/MinimedPumpManagerSetupViewController.swift +++ b/MinimedKitUI/Setup/MinimedPumpManagerSetupViewController.swift @@ -25,6 +25,11 @@ public class MinimedPumpManagerSetupViewController: RileyLinkManagerSetupViewCon view.backgroundColor = .white navigationBar.shadowImage = UIImage() + + if let pumpIDSetupVC = topViewController as? MinimedPumpIDSetupViewController, let rileyLinkPumpManager = rileyLinkPumpManager { + pumpIDSetupVC.rileyLinkPumpManager = rileyLinkPumpManager + } + } private(set) var pumpManager: MinimedPumpManager? diff --git a/MinimedKitUI/Setup/MinimedPumpSentrySetupViewController.swift b/MinimedKitUI/Setup/MinimedPumpSentrySetupViewController.swift index 52cdb68e6..b083c0134 100644 --- a/MinimedKitUI/Setup/MinimedPumpSentrySetupViewController.swift +++ b/MinimedKitUI/Setup/MinimedPumpSentrySetupViewController.swift @@ -84,7 +84,7 @@ class MinimedPumpSentrySetupViewController: SetupTableViewController { continueState = .listening - pumpManager.pumpOps.runSession(withName: "MySentry Pairing", using: pumpManager.rileyLinkManager.firstConnectedDevice) { (session) in + pumpManager.pumpOps.runSession(withName: "MySentry Pairing", using: pumpManager.rileyLinkDeviceProvider.firstConnectedDevice) { (session) in guard let session = session else { DispatchQueue.main.async { self.continueState = .notStarted diff --git a/NightscoutUploadKit/Info.plist b/NightscoutUploadKit/Info.plist index 0de69c100..241ef8699 100644 --- a/NightscoutUploadKit/Info.plist +++ b/NightscoutUploadKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleSignature ???? CFBundleVersion diff --git a/NightscoutUploadKitTests/Info.plist b/NightscoutUploadKitTests/Info.plist index f437a4c22..9c8492d9c 100644 --- a/NightscoutUploadKitTests/Info.plist +++ b/NightscoutUploadKitTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleSignature ???? CFBundleVersion diff --git a/RileyLink.xcodeproj/project.pbxproj b/RileyLink.xcodeproj/project.pbxproj index e87f1e28d..948a7b4cc 100644 --- a/RileyLink.xcodeproj/project.pbxproj +++ b/RileyLink.xcodeproj/project.pbxproj @@ -61,7 +61,6 @@ 43462E8B1CCB06F500F958A8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43462E8A1CCB06F500F958A8 /* AppDelegate.swift */; }; 434AB0C71CBCB76400422F4A /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1EAD6BA1C826B92006DBA60 /* Data.swift */; }; 434FF1DC1CF268BD000DB779 /* IdentifiableClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431185AE1CF25A590059ED98 /* IdentifiableClass.swift */; }; - 434FF1DE1CF268F3000DB779 /* RileyLinkDeviceTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434FF1DD1CF268F3000DB779 /* RileyLinkDeviceTableViewCell.swift */; }; 4352A71220DEC68100CAC200 /* RileyLinkKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43722FAE1CB9F7630038B7F2 /* RileyLinkKit.framework */; }; 4352A71520DEC72500CAC200 /* PumpMessageSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F962EC21E6873A10070EFBD /* PumpMessageSender.swift */; }; 4352A71620DEC78B00CAC200 /* PumpSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4384C8C71FB937E500D916E6 /* PumpSettings.swift */; }; @@ -99,7 +98,6 @@ 4352A75120DEDE9B00CAC200 /* LoopKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43FB610B20DDF55F002B996B /* LoopKit.framework */; }; 435535D61FB6D98400CE5A23 /* UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435535D51FB6D98400CE5A23 /* UserDefaults.swift */; }; 435D26B020DA08CE00891C17 /* RileyLinkPumpManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435D26AF20DA08CE00891C17 /* RileyLinkPumpManager.swift */; }; - 435D26B220DA091C00891C17 /* RileyLinkPumpManagerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435D26B120DA091B00891C17 /* RileyLinkPumpManagerState.swift */; }; 435D26B420DA0AAE00891C17 /* RileyLinkDevicesHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435D26B320DA0AAE00891C17 /* RileyLinkDevicesHeaderView.swift */; }; 435D26B620DA0BCC00891C17 /* RileyLinkDevicesTableViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435D26B520DA0BCC00891C17 /* RileyLinkDevicesTableViewDataSource.swift */; }; 435D26B920DC83F300891C17 /* Locked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435D26B720DC83E400891C17 /* Locked.swift */; }; @@ -211,7 +209,6 @@ 43EBE4531EAD23CE0073A0B5 /* TimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43EBE4501EAD238C0073A0B5 /* TimeInterval.swift */; }; 43EBE4541EAD23EC0073A0B5 /* TimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43EBE4501EAD238C0073A0B5 /* TimeInterval.swift */; }; 43EBE4551EAD24410073A0B5 /* TimeInterval.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43EBE4501EAD238C0073A0B5 /* TimeInterval.swift */; }; - 43EC9DCB1B786C6200DB0D18 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 43EC9DCA1B786C6200DB0D18 /* LaunchScreen.xib */; }; 43F348061D596270009933DC /* HKUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F348051D596270009933DC /* HKUnit.swift */; }; 43FF221C1CB9B9DE00024F30 /* NSDateComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43FF221B1CB9B9DE00024F30 /* NSDateComponents.swift */; }; 492526711E4521FB00ACBA5F /* NoteNightscoutTreatment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 492526701E4521FB00ACBA5F /* NoteNightscoutTreatment.swift */; }; @@ -273,10 +270,6 @@ 7D23674D21252A5E0028B67D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D23674B21252A5E0028B67D /* InfoPlist.strings */; }; 7D23675021252A5E0028B67D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D23674E21252A5E0028B67D /* InfoPlist.strings */; }; 7D23675321252A5E0028B67D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D23675121252A5E0028B67D /* InfoPlist.strings */; }; - 7D23676121252A9D0028B67D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D23674821252A5E0028B67D /* InfoPlist.strings */; }; - 7D23676721252A9D0028B67D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D23674521252A5E0028B67D /* InfoPlist.strings */; }; - 7D23676921252A9D0028B67D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D23674B21252A5E0028B67D /* InfoPlist.strings */; }; - 7D23676B21252A9D0028B67D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D23675121252A5E0028B67D /* InfoPlist.strings */; }; 7D23679421252EBC0028B67D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D23679221252EBC0028B67D /* Localizable.strings */; }; 7D23679721252EBC0028B67D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D23679521252EBC0028B67D /* Localizable.strings */; }; 7D70766D1FE092D4004AC8EA /* LoopKit.strings in Resources */ = {isa = PBXBuildFile; fileRef = 7D70766F1FE092D4004AC8EA /* LoopKit.strings */; }; @@ -300,26 +293,27 @@ C12198AD1C8F332500BC374C /* TimestampedPumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C12198AC1C8F332500BC374C /* TimestampedPumpEvent.swift */; }; C12198B31C8F730700BC374C /* BolusWizardEstimatePumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C12198B21C8F730700BC374C /* BolusWizardEstimatePumpEvent.swift */; }; C125728B211F7E6C0061BA2F /* UnknownPumpEvent57.swift in Sources */ = {isa = PBXBuildFile; fileRef = C125728A211F7E6C0061BA2F /* UnknownPumpEvent57.swift */; }; + C125728C2121D4D60061BA2F /* UITableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B4A9581D1E6357003B8985 /* UITableViewCell.swift */; }; + C125728D2121D56D0061BA2F /* CaseCountable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1C659181E16BA9D0025CC58 /* CaseCountable.swift */; }; + C125728F2121DB7C0061BA2F /* PumpManagerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C125728E2121DB7C0061BA2F /* PumpManagerState.swift */; }; + C12572922121EEEE0061BA2F /* SettingsImageTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C12572912121EEEE0061BA2F /* SettingsImageTableViewCell.swift */; }; + C125729421220FEC0061BA2F /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C125729321220FEC0061BA2F /* MainStoryboard.storyboard */; }; + C12572982125FA390061BA2F /* RileyLinkConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C12572972125FA390061BA2F /* RileyLinkConnectionManager.swift */; }; C12616441B685F0A001FAD87 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C12616431B685F0A001FAD87 /* CoreData.framework */; }; C1271B071A9A34E900B7C949 /* Log.m in Sources */ = {isa = PBXBuildFile; fileRef = C1271B061A9A34E900B7C949 /* Log.m */; }; C1274F771D8232580002912B /* DailyTotal515PumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1274F761D8232580002912B /* DailyTotal515PumpEvent.swift */; }; C1274F791D823A550002912B /* ChangeMeterIDPumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1274F781D823A550002912B /* ChangeMeterIDPumpEvent.swift */; }; - C1274F7F1D82411C0002912B /* AuthenticationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1274F7B1D82411C0002912B /* AuthenticationViewController.swift */; }; - C1274F801D82411C0002912B /* RileyLinkListTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1274F7C1D82411C0002912B /* RileyLinkListTableViewController.swift */; }; - C1274F811D82411C0002912B /* SettingsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1274F7D1D82411C0002912B /* SettingsTableViewController.swift */; }; - C1274F841D82420F0002912B /* RadioSelectionTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1274F831D82420F0002912B /* RadioSelectionTableViewController.swift */; }; + C1274F801D82411C0002912B /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1274F7C1D82411C0002912B /* MainViewController.swift */; }; C1274F861D8242BE0002912B /* PumpRegion.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1274F851D8242BE0002912B /* PumpRegion.swift */; }; C12EA23B198B436800309FA4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C12EA23A198B436800309FA4 /* Foundation.framework */; }; C12EA23D198B436800309FA4 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C12EA23C198B436800309FA4 /* CoreGraphics.framework */; }; C12EA23F198B436800309FA4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C12EA23E198B436800309FA4 /* UIKit.framework */; }; C12EA245198B436800309FA4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C12EA243198B436800309FA4 /* InfoPlist.strings */; }; - C12EA24D198B436800309FA4 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C12EA24C198B436800309FA4 /* Images.xcassets */; }; C12EA254198B436800309FA4 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C12EA253198B436800309FA4 /* XCTest.framework */; }; C12EA255198B436800309FA4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C12EA23A198B436800309FA4 /* Foundation.framework */; }; C12EA256198B436900309FA4 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C12EA23E198B436800309FA4 /* UIKit.framework */; }; C12EA25E198B436900309FA4 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C12EA25C198B436900309FA4 /* InfoPlist.strings */; }; C12EA260198B436900309FA4 /* RileyLinkTests.m in Sources */ = {isa = PBXBuildFile; fileRef = C12EA25F198B436900309FA4 /* RileyLinkTests.m */; }; - C12EA26A198B442100309FA4 /* Storyboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = C12EA269198B442100309FA4 /* Storyboard.storyboard */; }; C1330F431DBDA46400569064 /* ChangeSensorAlarmSilenceConfigPumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1330F421DBDA46400569064 /* ChangeSensorAlarmSilenceConfigPumpEvent.swift */; }; C133CF931D5943780034B82D /* PredictedBG.swift in Sources */ = {isa = PBXBuildFile; fileRef = C133CF921D5943780034B82D /* PredictedBG.swift */; }; C13D155A1DAACE8400ADC044 /* Either.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13D15591DAACE8400ADC044 /* Either.swift */; }; @@ -331,13 +325,8 @@ C14FFC4A1D3AF1AC0049CF85 /* JournalEntryInsulinMarkerPumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14FFC491D3AF1AC0049CF85 /* JournalEntryInsulinMarkerPumpEvent.swift */; }; C14FFC501D3D6DEF0049CF85 /* ServiceAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14FFC4E1D3D6DEF0049CF85 /* ServiceAuthentication.swift */; }; C14FFC511D3D6DEF0049CF85 /* ServiceCredential.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14FFC4F1D3D6DEF0049CF85 /* ServiceCredential.swift */; }; - C14FFC531D3D70170049CF85 /* ValidatingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14FFC521D3D70170049CF85 /* ValidatingIndicatorView.swift */; }; C14FFC551D3D72A50049CF85 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14FFC541D3D72A50049CF85 /* UIViewController.swift */; }; - C14FFC581D3D72D30049CF85 /* AuthenticationTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14FFC561D3D72D30049CF85 /* AuthenticationTableViewCell.swift */; }; - C14FFC591D3D72D30049CF85 /* AuthenticationTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C14FFC571D3D72D30049CF85 /* AuthenticationTableViewCell.xib */; }; C14FFC5B1D3D74F90049CF85 /* NibLoadable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14FFC5A1D3D74F90049CF85 /* NibLoadable.swift */; }; - C14FFC5E1D3D75200049CF85 /* ButtonTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14FFC5C1D3D75200049CF85 /* ButtonTableViewCell.swift */; }; - C14FFC5F1D3D75200049CF85 /* ButtonTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C14FFC5D1D3D75200049CF85 /* ButtonTableViewCell.xib */; }; C14FFC611D3D75470049CF85 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14FFC601D3D75470049CF85 /* UIColor.swift */; }; C14FFC631D3D7CE20049CF85 /* NightscoutService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14FFC621D3D7CE20049CF85 /* NightscoutService.swift */; }; C14FFC651D3D7E250049CF85 /* RemoteDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14FFC641D3D7E250049CF85 /* RemoteDataManager.swift */; }; @@ -346,7 +335,6 @@ C15AF2AD1D74929D0031FC9D /* ChangeBolusWizardSetupPumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15AF2AC1D74929D0031FC9D /* ChangeBolusWizardSetupPumpEvent.swift */; }; C15AF2AF1D7498930031FC9D /* RestoreMystery54PumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15AF2AE1D7498930031FC9D /* RestoreMystery54PumpEvent.swift */; }; C15AF2B11D7498DD0031FC9D /* RestoreMystery55PumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15AF2B01D7498DD0031FC9D /* RestoreMystery55PumpEvent.swift */; }; - C16843771CF00C0100D53CCD /* SwitchTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16843761CF00C0100D53CCD /* SwitchTableViewCell.swift */; }; C16A08311D389205001A200C /* JournalEntryMealMarkerPumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16A08301D389205001A200C /* JournalEntryMealMarkerPumpEvent.swift */; }; C1711A561C94F13400CB25BD /* ButtonPressCarelinkMessageBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1711A551C94F13400CB25BD /* ButtonPressCarelinkMessageBody.swift */; }; C1711A5A1C952D2900CB25BD /* GetPumpModelCarelinkMessageBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1711A591C952D2900CB25BD /* GetPumpModelCarelinkMessageBody.swift */; }; @@ -476,6 +464,8 @@ C1FDFCA91D964A3E00ADBC31 /* BolusReminderPumpEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FDFCA81D964A3E00ADBC31 /* BolusReminderPumpEvent.swift */; }; C1FFAF4D212944F600C50C1D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C1FFAF4B212944F600C50C1D /* Localizable.strings */; }; C1FFAF64212B126E00C50C1D /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C1FFAF62212B126E00C50C1D /* InfoPlist.strings */; }; + C1FFAF6F212CB4F100C50C1D /* RileyLinkConnectionManagerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FFAF6E212CB4F100C50C1D /* RileyLinkConnectionManagerState.swift */; }; + C1FFAF72212FAAEF00C50C1D /* RileyLink.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C1FFAF71212FAAEF00C50C1D /* RileyLink.xcassets */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -705,7 +695,6 @@ 434AB0921CBA0DF600422F4A /* PumpOps.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PumpOps.swift; sourceTree = ""; }; 434AB0931CBA0DF600422F4A /* PumpOpsSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PumpOpsSession.swift; path = ../../RileyLinkKit/PumpOpsSession.swift; sourceTree = ""; }; 434AB0941CBA0DF600422F4A /* PumpState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PumpState.swift; sourceTree = ""; }; - 434FF1DD1CF268F3000DB779 /* RileyLinkDeviceTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RileyLinkDeviceTableViewCell.swift; sourceTree = ""; }; 4352A72520DEC9B700CAC200 /* MinimedKitUI.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MinimedKitUI.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4352A72720DEC9B700CAC200 /* MinimedKitUI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MinimedKitUI.h; sourceTree = ""; }; 4352A72820DEC9B700CAC200 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -716,7 +705,6 @@ 435535D51FB6D98400CE5A23 /* UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaults.swift; sourceTree = ""; }; 435535DB1FB8B37E00CE5A23 /* PumpMessage+PumpOpsSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PumpMessage+PumpOpsSession.swift"; sourceTree = ""; }; 435D26AF20DA08CE00891C17 /* RileyLinkPumpManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RileyLinkPumpManager.swift; sourceTree = ""; }; - 435D26B120DA091B00891C17 /* RileyLinkPumpManagerState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RileyLinkPumpManagerState.swift; sourceTree = ""; }; 435D26B320DA0AAE00891C17 /* RileyLinkDevicesHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RileyLinkDevicesHeaderView.swift; sourceTree = ""; }; 435D26B520DA0BCC00891C17 /* RileyLinkDevicesTableViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RileyLinkDevicesTableViewDataSource.swift; sourceTree = ""; }; 435D26B720DC83E400891C17 /* Locked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Locked.swift; sourceTree = ""; }; @@ -801,7 +789,6 @@ 43DFB61220D37800008A7BAE /* ChangeMaxBasalRateMessageBodyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeMaxBasalRateMessageBodyTests.swift; sourceTree = ""; }; 43DFB61420D3791A008A7BAE /* ChangeMaxBasalRateMessageBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeMaxBasalRateMessageBody.swift; sourceTree = ""; }; 43EBE4501EAD238C0073A0B5 /* TimeInterval.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimeInterval.swift; sourceTree = ""; }; - 43EC9DCA1B786C6200DB0D18 /* LaunchScreen.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LaunchScreen.xib; sourceTree = ""; }; 43F348051D596270009933DC /* HKUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HKUnit.swift; sourceTree = ""; }; 43FB610A20DDF55E002B996B /* LoopKitUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LoopKitUI.framework; path = Carthage/Build/iOS/LoopKitUI.framework; sourceTree = ""; }; 43FB610B20DDF55F002B996B /* LoopKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LoopKit.framework; path = Carthage/Build/iOS/LoopKit.framework; sourceTree = ""; }; @@ -1027,18 +1014,16 @@ C12198AC1C8F332500BC374C /* TimestampedPumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = TimestampedPumpEvent.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C12198B21C8F730700BC374C /* BolusWizardEstimatePumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = BolusWizardEstimatePumpEvent.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; C125728A211F7E6C0061BA2F /* UnknownPumpEvent57.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnknownPumpEvent57.swift; sourceTree = ""; }; + C125728E2121DB7C0061BA2F /* PumpManagerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PumpManagerState.swift; sourceTree = ""; }; + C12572912121EEEE0061BA2F /* SettingsImageTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsImageTableViewCell.swift; sourceTree = ""; }; + C125729321220FEC0061BA2F /* MainStoryboard.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = MainStoryboard.storyboard; sourceTree = ""; }; + C12572972125FA390061BA2F /* RileyLinkConnectionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RileyLinkConnectionManager.swift; sourceTree = ""; }; C12616431B685F0A001FAD87 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; - C126164A1B685F93001FAD87 /* RileyLink.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = RileyLink.xcdatamodel; sourceTree = ""; }; - C12616521B6892DB001FAD87 /* RileyLinkRecord.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RileyLinkRecord.h; sourceTree = ""; }; - C12616531B6892DB001FAD87 /* RileyLinkRecord.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RileyLinkRecord.m; sourceTree = ""; }; C1271B061A9A34E900B7C949 /* Log.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Log.m; sourceTree = ""; }; C1271B081A9A350400B7C949 /* Log.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Log.h; sourceTree = ""; }; C1274F761D8232580002912B /* DailyTotal515PumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DailyTotal515PumpEvent.swift; sourceTree = ""; }; C1274F781D823A550002912B /* ChangeMeterIDPumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangeMeterIDPumpEvent.swift; sourceTree = ""; }; - C1274F7B1D82411C0002912B /* AuthenticationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationViewController.swift; sourceTree = ""; }; - C1274F7C1D82411C0002912B /* RileyLinkListTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RileyLinkListTableViewController.swift; sourceTree = ""; }; - C1274F7D1D82411C0002912B /* SettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTableViewController.swift; sourceTree = ""; }; - C1274F831D82420F0002912B /* RadioSelectionTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSelectionTableViewController.swift; sourceTree = ""; }; + C1274F7C1D82411C0002912B /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; C1274F851D8242BE0002912B /* PumpRegion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PumpRegion.swift; sourceTree = ""; }; C12EA237198B436800309FA4 /* RileyLink.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RileyLink.app; sourceTree = BUILT_PRODUCTS_DIR; }; C12EA23A198B436800309FA4 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -1046,13 +1031,11 @@ C12EA23E198B436800309FA4 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; C12EA242198B436800309FA4 /* RileyLink-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "RileyLink-Info.plist"; sourceTree = ""; }; C12EA244198B436800309FA4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; - C12EA24C198B436800309FA4 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; C12EA252198B436800309FA4 /* RileyLinkTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RileyLinkTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C12EA253198B436800309FA4 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; C12EA25B198B436900309FA4 /* RileyLinkTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "RileyLinkTests-Info.plist"; sourceTree = ""; }; C12EA25D198B436900309FA4 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; C12EA25F198B436900309FA4 /* RileyLinkTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RileyLinkTests.m; sourceTree = ""; }; - C12EA269198B442100309FA4 /* Storyboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Storyboard.storyboard; sourceTree = ""; }; C1330F421DBDA46400569064 /* ChangeSensorAlarmSilenceConfigPumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangeSensorAlarmSilenceConfigPumpEvent.swift; sourceTree = ""; }; C133CF921D5943780034B82D /* PredictedBG.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredictedBG.swift; sourceTree = ""; }; C13D15591DAACE8400ADC044 /* Either.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Either.swift; sourceTree = ""; }; @@ -1065,13 +1048,8 @@ C14FFC491D3AF1AC0049CF85 /* JournalEntryInsulinMarkerPumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JournalEntryInsulinMarkerPumpEvent.swift; sourceTree = ""; }; C14FFC4E1D3D6DEF0049CF85 /* ServiceAuthentication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceAuthentication.swift; sourceTree = ""; }; C14FFC4F1D3D6DEF0049CF85 /* ServiceCredential.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceCredential.swift; sourceTree = ""; }; - C14FFC521D3D70170049CF85 /* ValidatingIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValidatingIndicatorView.swift; sourceTree = ""; }; C14FFC541D3D72A50049CF85 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; - C14FFC561D3D72D30049CF85 /* AuthenticationTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationTableViewCell.swift; sourceTree = ""; }; - C14FFC571D3D72D30049CF85 /* AuthenticationTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = AuthenticationTableViewCell.xib; sourceTree = ""; }; C14FFC5A1D3D74F90049CF85 /* NibLoadable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NibLoadable.swift; sourceTree = ""; }; - C14FFC5C1D3D75200049CF85 /* ButtonTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ButtonTableViewCell.swift; sourceTree = ""; }; - C14FFC5D1D3D75200049CF85 /* ButtonTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ButtonTableViewCell.xib; sourceTree = ""; }; C14FFC601D3D75470049CF85 /* UIColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; C14FFC621D3D7CE20049CF85 /* NightscoutService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NightscoutService.swift; sourceTree = ""; }; C14FFC641D3D7E250049CF85 /* RemoteDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteDataManager.swift; sourceTree = ""; }; @@ -1080,7 +1058,6 @@ C15AF2AC1D74929D0031FC9D /* ChangeBolusWizardSetupPumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChangeBolusWizardSetupPumpEvent.swift; sourceTree = ""; }; C15AF2AE1D7498930031FC9D /* RestoreMystery54PumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestoreMystery54PumpEvent.swift; sourceTree = ""; }; C15AF2B01D7498DD0031FC9D /* RestoreMystery55PumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RestoreMystery55PumpEvent.swift; sourceTree = ""; }; - C16843761CF00C0100D53CCD /* SwitchTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwitchTableViewCell.swift; sourceTree = ""; }; C16A08301D389205001A200C /* JournalEntryMealMarkerPumpEvent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JournalEntryMealMarkerPumpEvent.swift; sourceTree = ""; }; C170C98D1CECD6F300F3D8E5 /* CBPeripheralState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CBPeripheralState.swift; sourceTree = ""; }; C170C9961CECD80000F3D8E5 /* CommandResponseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandResponseViewController.swift; sourceTree = ""; }; @@ -1235,6 +1212,8 @@ C1FFAF6B212B128800C50C1D /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; C1FFAF6C212B128A00C50C1D /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = ""; }; C1FFAF6D212B128C00C50C1D /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; + C1FFAF6E212CB4F100C50C1D /* RileyLinkConnectionManagerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RileyLinkConnectionManagerState.swift; sourceTree = ""; }; + C1FFAF71212FAAEF00C50C1D /* RileyLink.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = RileyLink.xcassets; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -1402,6 +1381,8 @@ 431CE79B1F9B21BA00255374 /* RileyLinkDevice.swift */, 433ABFFB2016FDF700E6C1FF /* RileyLinkDeviceError.swift */, 431CE7901F985D8D00255374 /* RileyLinkDeviceManager.swift */, + C12572972125FA390061BA2F /* RileyLinkConnectionManager.swift */, + C1FFAF6E212CB4F100C50C1D /* RileyLinkConnectionManagerState.swift */, ); path = RileyLinkBLEKit; sourceTree = ""; @@ -1498,7 +1479,6 @@ 43323EA91FA81C1B003FB0FA /* RileyLinkDevice.swift */, 4352A74020DED23000CAC200 /* RileyLinkDeviceManager.swift */, 435D26AF20DA08CE00891C17 /* RileyLinkPumpManager.swift */, - 435D26B120DA091B00891C17 /* RileyLinkPumpManagerState.swift */, ); path = RileyLinkKit; sourceTree = ""; @@ -1720,37 +1700,18 @@ path = Messages; sourceTree = ""; }; - C12616391B67CB9C001FAD87 /* Views */ = { + C12572902121EEDE0061BA2F /* Views */ = { isa = PBXGroup; children = ( - C14FFC561D3D72D30049CF85 /* AuthenticationTableViewCell.swift */, - C14FFC571D3D72D30049CF85 /* AuthenticationTableViewCell.xib */, - C14FFC521D3D70170049CF85 /* ValidatingIndicatorView.swift */, - C14FFC5C1D3D75200049CF85 /* ButtonTableViewCell.swift */, - C14FFC5D1D3D75200049CF85 /* ButtonTableViewCell.xib */, - 434FF1DD1CF268F3000DB779 /* RileyLinkDeviceTableViewCell.swift */, - C16843761CF00C0100D53CCD /* SwitchTableViewCell.swift */, + C12572912121EEEE0061BA2F /* SettingsImageTableViewCell.swift */, ); path = Views; sourceTree = ""; }; - C12616451B685F35001FAD87 /* CoreData */ = { - isa = PBXGroup; - children = ( - C12616521B6892DB001FAD87 /* RileyLinkRecord.h */, - C12616531B6892DB001FAD87 /* RileyLinkRecord.m */, - C12616491B685F93001FAD87 /* RileyLink.xcdatamodeld */, - ); - name = CoreData; - sourceTree = ""; - }; C1274F7A1D8240D00002912B /* View Controllers */ = { isa = PBXGroup; children = ( - C1274F831D82420F0002912B /* RadioSelectionTableViewController.swift */, - C1274F7B1D82411C0002912B /* AuthenticationViewController.swift */, - C1274F7C1D82411C0002912B /* RileyLinkListTableViewController.swift */, - C1274F7D1D82411C0002912B /* SettingsTableViewController.swift */, + C1274F7C1D82411C0002912B /* MainViewController.swift */, ); path = "View Controllers"; sourceTree = ""; @@ -1816,21 +1777,19 @@ C12EA240198B436800309FA4 /* RileyLink */ = { isa = PBXGroup; children = ( + C1FFAF71212FAAEF00C50C1D /* RileyLink.xcassets */, + C12572902121EEDE0061BA2F /* Views */, C1274F7A1D8240D00002912B /* View Controllers */, C14FFC4D1D3D6D8E0049CF85 /* Models */, C1B383341CD1BA6700CE7782 /* Managers */, C1AA398B1AB67F6A00BC9E33 /* Extensions */, - C12616451B685F35001FAD87 /* CoreData */, C12EA241198B436800309FA4 /* Supporting Files */, - C12616391B67CB9C001FAD87 /* Views */, 43462E8A1CCB06F500F958A8 /* AppDelegate.swift */, + C125729321220FEC0061BA2F /* MainStoryboard.storyboard */, C1EF58861B3F93FE001C8C80 /* Config.h */, C1EF58871B3F93FE001C8C80 /* Config.m */, - C12EA24C198B436800309FA4 /* Images.xcassets */, - 43EC9DCA1B786C6200DB0D18 /* LaunchScreen.xib */, C1271B081A9A350400B7C949 /* Log.h */, C1271B061A9A34E900B7C949 /* Log.m */, - C12EA269198B442100309FA4 /* Storyboard.storyboard */, ); path = RileyLink; sourceTree = ""; @@ -2036,6 +1995,7 @@ C14FFC681D3D7E560049CF85 /* KeychainManager+RileyLink.swift */, C14FFC641D3D7E250049CF85 /* RemoteDataManager.swift */, C1B383351CD1BA8100CE7782 /* DeviceDataManager.swift */, + C125728E2121DB7C0061BA2F /* PumpManagerState.swift */, ); name = Managers; sourceTree = ""; @@ -2541,7 +2501,6 @@ buildActionMask = 2147483647; files = ( 7D23674A21252A5E0028B67D /* InfoPlist.strings in Resources */, - 7D23676121252A9D0028B67D /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2568,7 +2527,6 @@ buildActionMask = 2147483647; files = ( 7D23674721252A5E0028B67D /* InfoPlist.strings in Resources */, - 7D23676721252A9D0028B67D /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2605,7 +2563,6 @@ buildActionMask = 2147483647; files = ( 7D23674D21252A5E0028B67D /* InfoPlist.strings in Resources */, - 7D23676921252A9D0028B67D /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2613,14 +2570,11 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 43EC9DCB1B786C6200DB0D18 /* LaunchScreen.xib in Resources */, - C14FFC5F1D3D75200049CF85 /* ButtonTableViewCell.xib in Resources */, + C125729421220FEC0061BA2F /* MainStoryboard.storyboard in Resources */, + C1FFAF72212FAAEF00C50C1D /* RileyLink.xcassets in Resources */, 7D70766D1FE092D4004AC8EA /* LoopKit.strings in Resources */, C12EA245198B436800309FA4 /* InfoPlist.strings in Resources */, - C12EA24D198B436800309FA4 /* Images.xcassets in Resources */, 7D7076951FE09311004AC8EA /* Localizable.strings in Resources */, - C12EA26A198B442100309FA4 /* Storyboard.storyboard in Resources */, - C14FFC591D3D72D30049CF85 /* AuthenticationTableViewCell.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2645,7 +2599,6 @@ buildActionMask = 2147483647; files = ( 7D23675321252A5E0028B67D /* InfoPlist.strings in Resources */, - 7D23676B21252A9D0028B67D /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2693,11 +2646,13 @@ files = ( 7D2366F5212527DA0028B67D /* LocalizedString.swift in Sources */, 43BA719D2026C9B00058961E /* ResponseBuffer.swift in Sources */, + C1FFAF6F212CB4F100C50C1D /* RileyLinkConnectionManagerState.swift in Sources */, 431CE78F1F985B6E00255374 /* CBPeripheral.swift in Sources */, 431CE79F1F9C670600255374 /* TimeInterval.swift in Sources */, 433ABFFC2016FDF700E6C1FF /* RileyLinkDeviceError.swift in Sources */, 43BA719B202591A70058961E /* Response.swift in Sources */, 43D5E7881FAEDAC4004ACDB7 /* PeripheralManagerError.swift in Sources */, + C12572982125FA390061BA2F /* RileyLinkConnectionManager.swift in Sources */, 431CE79C1F9B21BA00255374 /* RileyLinkDevice.swift in Sources */, 431CE7A71F9D98F700255374 /* CommandSession.swift in Sources */, 431CE7A31F9D737F00255374 /* Command.swift in Sources */, @@ -2759,7 +2714,6 @@ 43EBE4531EAD23CE0073A0B5 /* TimeInterval.swift in Sources */, 434AB0C71CBCB76400422F4A /* Data.swift in Sources */, 4352A74120DED23100CAC200 /* RileyLinkDeviceManager.swift in Sources */, - 435D26B220DA091C00891C17 /* RileyLinkPumpManagerState.swift in Sources */, 431CE7961F9B0F0200255374 /* OSLog.swift in Sources */, 437F54071FBD52120070FF2C /* DeviceState.swift in Sources */, 435D26B020DA08CE00891C17 /* RileyLinkPumpManager.swift in Sources */, @@ -3058,35 +3012,31 @@ buildActionMask = 2147483647; files = ( 435535D61FB6D98400CE5A23 /* UserDefaults.swift in Sources */, - C14FFC5E1D3D75200049CF85 /* ButtonTableViewCell.swift in Sources */, 43EBE4551EAD24410073A0B5 /* TimeInterval.swift in Sources */, C14FFC691D3D7E560049CF85 /* KeychainManager+RileyLink.swift in Sources */, 437F540A1FBFDAA60070FF2C /* Data.swift in Sources */, C14FFC511D3D6DEF0049CF85 /* ServiceCredential.swift in Sources */, C14FFC5B1D3D74F90049CF85 /* NibLoadable.swift in Sources */, + C12572922121EEEE0061BA2F /* SettingsImageTableViewCell.swift in Sources */, + C125728D2121D56D0061BA2F /* CaseCountable.swift in Sources */, C14FFC611D3D75470049CF85 /* UIColor.swift in Sources */, - C1274F811D82411C0002912B /* SettingsTableViewController.swift in Sources */, 43323EA71FA81A0F003FB0FA /* NumberFormatter.swift in Sources */, C14FFC671D3D7E390049CF85 /* KeychainManager.swift in Sources */, C14FFC501D3D6DEF0049CF85 /* ServiceAuthentication.swift in Sources */, - C1274F7F1D82411C0002912B /* AuthenticationViewController.swift in Sources */, 43462E8B1CCB06F500F958A8 /* AppDelegate.swift in Sources */, - C16843771CF00C0100D53CCD /* SwitchTableViewCell.swift in Sources */, + C125728C2121D4D60061BA2F /* UITableViewCell.swift in Sources */, C14FFC551D3D72A50049CF85 /* UIViewController.swift in Sources */, - 434FF1DE1CF268F3000DB779 /* RileyLinkDeviceTableViewCell.swift in Sources */, 7D2366F0212527DA0028B67D /* LocalizedString.swift in Sources */, C14FFC651D3D7E250049CF85 /* RemoteDataManager.swift in Sources */, C17884611D519F1E00405663 /* BatteryIndicator.swift in Sources */, 437462391FA9287A00643383 /* RileyLinkDevice.swift in Sources */, - C1274F801D82411C0002912B /* RileyLinkListTableViewController.swift in Sources */, - C1274F841D82420F0002912B /* RadioSelectionTableViewController.swift in Sources */, + C1274F801D82411C0002912B /* MainViewController.swift in Sources */, 431CE7981F9B0F0200255374 /* OSLog.swift in Sources */, C1EF58881B3F93FE001C8C80 /* Config.m in Sources */, - C14FFC581D3D72D30049CF85 /* AuthenticationTableViewCell.swift in Sources */, + C125728F2121DB7C0061BA2F /* PumpManagerState.swift in Sources */, C14FFC631D3D7CE20049CF85 /* NightscoutService.swift in Sources */, C1B383361CD1BA8100CE7782 /* DeviceDataManager.swift in Sources */, C1271B071A9A34E900B7C949 /* Log.m in Sources */, - C14FFC531D3D70170049CF85 /* ValidatingIndicatorView.swift in Sources */, 434FF1DC1CF268BD000DB779 /* IdentifiableClass.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -3595,7 +3545,7 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = RileyLinkBLEKit/Info.plist; @@ -3631,7 +3581,7 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = RileyLinkBLEKit/Info.plist; @@ -3708,14 +3658,14 @@ CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -3747,7 +3697,7 @@ CLANG_ENABLE_OBJC_WEAK = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_IDENTITY = ""; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; @@ -3755,7 +3705,7 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -3784,11 +3734,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -3821,11 +3771,11 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -3905,12 +3855,12 @@ CLANG_WARN_SUSPICIOUS_MOVES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Crypto/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -3936,12 +3886,12 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Crypto/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -3971,7 +3921,7 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -4010,7 +3960,7 @@ DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", @@ -4054,12 +4004,12 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -4091,11 +4041,11 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_SEARCH_PATHS = ( @@ -4198,7 +4148,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -4265,7 +4215,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = YES; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -4298,6 +4248,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -4323,6 +4274,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -4389,11 +4341,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; @@ -4419,11 +4371,11 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 39; + CURRENT_PROJECT_VERSION = 40; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 39; + DYLIB_CURRENT_VERSION = 40; DYLIB_INSTALL_NAME_BASE = "@rpath"; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; @@ -4623,19 +4575,6 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ - -/* Begin XCVersionGroup section */ - C12616491B685F93001FAD87 /* RileyLink.xcdatamodeld */ = { - isa = XCVersionGroup; - children = ( - C126164A1B685F93001FAD87 /* RileyLink.xcdatamodel */, - ); - currentVersion = C126164A1B685F93001FAD87 /* RileyLink.xcdatamodel */; - path = RileyLink.xcdatamodeld; - sourceTree = ""; - versionGroupType = wrapper.xcdatamodel; - }; -/* End XCVersionGroup section */ }; rootObject = C12EA22F198B436800309FA4 /* Project object */; } diff --git a/RileyLink/AppDelegate.swift b/RileyLink/AppDelegate.swift index 10985f27c..4802ab935 100644 --- a/RileyLink/AppDelegate.swift +++ b/RileyLink/AppDelegate.swift @@ -10,24 +10,19 @@ import UIKit import CoreData import RileyLinkKit + @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { + private(set) lazy var deviceDataManager = DeviceDataManager() + var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { - let logFileURL = applicationDocumentsDirectory().appendingPathComponent("logfile.txt") - do { - try FileManager.default.removeItem(at: logFileURL) - } catch let error { - NSLog("Could not remove file at path: \(logFileURL): \(error)") - } - - // Just instantiate the DeviceDataManager - let delayTime = DispatchTime.now() + .seconds(1) - DispatchQueue.main.asyncAfter(deadline: delayTime) { - _ = DeviceDataManager.sharedManager + if let navController = self.window?.rootViewController as? UINavigationController { + let mainViewController = MainViewController(deviceDataManager: deviceDataManager) + navController.pushViewController(mainViewController, animated: false) } return true @@ -64,7 +59,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { completionHandler(false) - } + } } diff --git a/RileyLink/DeviceDataManager.swift b/RileyLink/DeviceDataManager.swift index 8b7413411..f72d5ddff 100644 --- a/RileyLink/DeviceDataManager.swift +++ b/RileyLink/DeviceDataManager.swift @@ -13,393 +13,95 @@ import RileyLinkBLEKit import MinimedKit import MinimedKitUI import NightscoutUploadKit +import LoopKit +import LoopKitUI +import os.log class DeviceDataManager { - let rileyLinkManager: RileyLinkDeviceManager + let rileyLinkConnectionManager: RileyLinkConnectionManager - /// Manages remote data - let remoteDataManager = RemoteDataManager() - - private var connectedPeripheralIDs: Set = Config.sharedInstance().autoConnectIds as! Set { - didSet { - Config.sharedInstance().autoConnectIds = connectedPeripheralIDs - } - } - - var deviceStates: [UUID: DeviceState] = [:] - - private(set) var pumpOps: PumpOps? { + var pumpManager: PumpManagerUI? { didSet { - if pumpOps == nil { - UserDefaults.standard.pumpState = nil - } + pumpManager?.pumpManagerDelegate = self + UserDefaults.standard.pumpManagerRawValue = pumpManager?.rawValue } } + - private(set) var pumpSettings: PumpSettings? { - get { - return UserDefaults.standard.pumpSettings + public let log = OSLog(category: "DeviceDataManager") + + init() { + + if let connectionManagerState = UserDefaults.standard.rileyLinkConnectionManagerState { + rileyLinkConnectionManager = RileyLinkConnectionManager(state: connectionManagerState) + } else { + rileyLinkConnectionManager = RileyLinkConnectionManager(autoConnectIDs: []) } - set { - if let settings = newValue { - pumpOps = PumpOps(pumpSettings: settings, pumpState: nil, delegate: self) - } else { - pumpOps = nil - } + rileyLinkConnectionManager.delegate = self + rileyLinkConnectionManager.setScanningEnabled(true) - UserDefaults.standard.pumpSettings = newValue + if let pumpManagerRawValue = UserDefaults.standard.pumpManagerRawValue { + pumpManager = PumpManagerFromRawValue(pumpManagerRawValue, rileyLinkDeviceProvider: rileyLinkConnectionManager.deviceProvider) as? PumpManagerUI + pumpManager?.pumpManagerDelegate = self } } +} - func setPumpID(_ pumpID: String?) { - var newValue = pumpID - - if newValue?.count != 6 { - newValue = nil - } - - if let newValue = newValue { - if pumpSettings != nil { - pumpSettings?.pumpID = newValue - } else { - pumpSettings = PumpSettings(pumpID: newValue) - } - } - } +extension DeviceDataManager: RileyLinkConnectionManagerDelegate { + func rileyLinkConnectionManager(_ rileyLinkConnectionManager: RileyLinkConnectionManager, didChange state: RileyLinkConnectionManagerState) + { + UserDefaults.standard.rileyLinkConnectionManagerState = state + } +} - func setPumpRegion(_ pumpRegion: PumpRegion) { - pumpSettings?.pumpRegion = pumpRegion +extension DeviceDataManager: PumpManagerDelegate { + func pumpManager(_ pumpManager: PumpManager, didAdjustPumpClockBy adjustment: TimeInterval) { + log.debug("didAdjustPumpClockBy %@", adjustment) } - var pumpState: PumpState? { - return UserDefaults.standard.pumpState - } - - // MARK: - Operation helpers - - var latestPumpStatusDate: Date? - - var latestPumpStatusFromMySentry: MySentryPumpStatusMessageBody? { - didSet { - if let update = latestPumpStatusFromMySentry, let timeZone = pumpState?.timeZone { - var pumpClock = update.pumpDateComponents - pumpClock.timeZone = timeZone - latestPumpStatusDate = pumpClock.date - } - } + func pumpManagerDidUpdatePumpBatteryChargeRemaining(_ pumpManager: PumpManager, oldValue: Double?) { } - - - var latestPolledPumpStatus: MinimedKit.PumpStatus? { - didSet { - if let update = latestPolledPumpStatus { - latestPumpStatusDate = update.clock.date - } - } - } - - var lastHistoryAttempt: Date? = nil - var lastGlucoseEntry: Date = Date(timeIntervalSinceNow: TimeInterval(hours: -24)) - - /** - Called when a new idle message is received by the RileyLink. - - Only MySentryPumpStatus messages are handled. - - - parameter note: The notification object - */ - @objc private func receivedRileyLinkPacketNotification(_ note: Notification) { - guard - let device = note.object as? RileyLinkDevice, - let packet = note.userInfo?[RileyLinkDevice.notificationPacketKey] as? RFPacket, - let decoded = MinimedPacket(encodedData: packet.data), - let message = PumpMessage(rxData: decoded.data), - let address = pumpSettings?.pumpID, - message.address.hexadecimalString == address - else { - return - } - - switch message.packetType { - case .mySentry: - switch message.messageBody { - case let body as MySentryPumpStatusMessageBody: - pumpStatusUpdateReceived(body, fromDevice: device) - default: - break - } - default: - break - } + func pumpManagerDidUpdateState(_ pumpManager: PumpManager) { + UserDefaults.standard.pumpManagerRawValue = pumpManager.rawValue } - @objc private func receivedRileyLinkTimerTickNotification(_ note: Notification) { - if Config.sharedInstance().uploadEnabled { - rileyLinkManager.getDevices { (devices) in - if let device = devices.firstConnected { - self.assertCurrentPumpData(from: device) - } - } - } - } - - @objc private func deviceStateDidChange(_ note: Notification) { - guard - let device = note.object as? RileyLinkDevice, - let deviceState = note.userInfo?[RileyLinkDevice.notificationDeviceStateKey] as? DeviceState - else { - return - } - - deviceStates[device.peripheralIdentifier] = deviceState + func pumpManagerBLEHeartbeatDidFire(_ pumpManager: PumpManager) { } - func connectToRileyLink(_ device: RileyLinkDevice) { - connectedPeripheralIDs.insert(device.peripheralIdentifier.uuidString) - - rileyLinkManager.connect(device) + func pumpManagerShouldProvideBLEHeartbeat(_ pumpManager: PumpManager) -> Bool { + return true } - func disconnectFromRileyLink(_ device: RileyLinkDevice) { - connectedPeripheralIDs.remove(device.peripheralIdentifier.uuidString) - - rileyLinkManager.disconnect(device) + func pumpManager(_ pumpManager: PumpManager, didUpdateStatus status: PumpManagerStatus) { } - private func pumpStatusUpdateReceived(_ status: MySentryPumpStatusMessageBody, fromDevice device: RileyLinkDevice) { - - var pumpDateComponents = status.pumpDateComponents - var glucoseDateComponents = status.glucoseDateComponents - - pumpDateComponents.timeZone = pumpState?.timeZone - glucoseDateComponents?.timeZone = pumpState?.timeZone - - // Avoid duplicates - if status != latestPumpStatusFromMySentry { - latestPumpStatusFromMySentry = status - - // Sentry packets are sent in groups of 3, 5s apart. Wait 11s to avoid conflicting comms. - let delay = DispatchTime.now() + .seconds(11) - DispatchQueue.main.asyncAfter(deadline: delay) { - self.getPumpHistory(device) - } - - if status.batteryRemainingPercent == 0 { - //NotificationManager.sendPumpBatteryLowNotification() - } - - guard Config.sharedInstance().uploadEnabled, let pumpID = pumpSettings?.pumpID else { - return - } - - // Gather PumpStatus from MySentry packet - let pumpStatus: NightscoutUploadKit.PumpStatus? - if let pumpDate = pumpDateComponents.date { - - let batteryStatus = BatteryStatus(percent: status.batteryRemainingPercent) - let iobStatus = IOBStatus(timestamp: pumpDate, iob: status.iob) - - pumpStatus = NightscoutUploadKit.PumpStatus(clock: pumpDate, pumpID: pumpID, iob: iobStatus, battery: batteryStatus, reservoir: status.reservoirRemainingUnits) - } else { - pumpStatus = nil - print("Could not interpret pump clock: \(pumpDateComponents)") - } - - // Trigger device status upload, even if something is wrong with pumpStatus - self.uploadDeviceStatus(pumpStatus) - - // Send SGVs - remoteDataManager.nightscoutUploader?.uploadSGVFromMySentryPumpStatus(status, device: device.deviceURI) - } + func pumpManagerWillDeactivate(_ pumpManager: PumpManager) { + self.pumpManager = nil } - private func uploadDeviceStatus(_ pumpStatus: NightscoutUploadKit.PumpStatus? /*, loopStatus: LoopStatus */) { - - guard let uploader = remoteDataManager.nightscoutUploader else { - return - } - - // Gather UploaderStatus - let uploaderDevice = UIDevice.current - let uploaderStatus = UploaderStatus(name: uploaderDevice.name, timestamp: Date(), battery: uploaderDevice.batteryLevel) - - // Build DeviceStatus - let deviceStatus = DeviceStatus(device: "rileylink://" + uploaderDevice.name, timestamp: Date(), pumpStatus: pumpStatus, uploaderStatus: uploaderStatus) - - uploader.uploadDeviceStatus(deviceStatus) + func pumpManager(_ pumpManager: PumpManager, didUpdatePumpRecordsBasalProfileStartEvents pumpRecordsBasalProfileStartEvents: Bool) { } - /** - Ensures pump data is current by either waking and polling, or ensuring we're listening to sentry packets. - */ - private func assertCurrentPumpData(from device: RileyLinkDevice) { - device.assertIdleListening() - - // How long should we wait before we poll for new pump data? - let pumpStatusAgeTolerance = rileyLinkManager.idleListeningEnabled ? TimeInterval(minutes: 11) : TimeInterval(minutes: 4) - - // If we don't yet have pump status, or it's old, poll for it. - if latestPumpStatusDate == nil || latestPumpStatusDate!.timeIntervalSinceNow <= -pumpStatusAgeTolerance { - - guard let pumpOps = pumpOps else { - self.troubleshootPumpCommsWithDevice(device) - return - } - - pumpOps.runSession(withName: "Read pump status", using: device) { (session) in - do { - let status = try session.getCurrentPumpStatus() - DispatchQueue.main.async { - self.latestPolledPumpStatus = status - let battery = BatteryStatus(voltage: status.batteryVolts.converted(to: .volts).value, status: BatteryIndicator(batteryStatus: status.batteryStatus)) - guard let date = status.clock.date else { - print("Could not interpret clock") - return - } - let nsPumpStatus = NightscoutUploadKit.PumpStatus(clock: date, pumpID: status.pumpID, iob: nil, battery: battery, suspended: status.suspended, bolusing: status.bolusing, reservoir: status.reservoir) - self.uploadDeviceStatus(nsPumpStatus) - } - } catch { - DispatchQueue.main.async { - self.troubleshootPumpCommsWithDevice(device) - } - } - } - } - - if lastHistoryAttempt == nil || lastHistoryAttempt!.timeIntervalSinceNow < TimeInterval(minutes: -5) { - getPumpHistory(device) - } - } - - /** - Attempts to fix an extended communication failure between a RileyLink device and the pump - - - parameter device: The RileyLink device - */ - private func troubleshootPumpCommsWithDevice(_ device: RileyLinkDevice) { - - // How long we should wait before we re-tune the RileyLink - let tuneTolerance = TimeInterval(minutes: 14) - - guard let pumpOps = pumpOps else { - return - } - - let deviceState = deviceStates[device.peripheralIdentifier, default: DeviceState()] - let lastTuned = deviceState.lastTuned ?? .distantPast - - if lastTuned.timeIntervalSinceNow <= -tuneTolerance { - pumpOps.runSession(withName: "Tune pump", using: device) { (session) in - do { - let scanResult = try session.tuneRadio(current: deviceState.lastValidFrequency) - print("Device auto-tuned to \(scanResult.bestFrequency)") - - DispatchQueue.main.async { - self.deviceStates[device.peripheralIdentifier] = DeviceState(lastTuned: Date(), lastValidFrequency: scanResult.bestFrequency) - } - } catch let error { - print("Device auto-tune failed with error: \(error)") - } - } - } + func pumpManager(_ pumpManager: PumpManager, didError error: PumpManagerError) { + log.error("pumpManager didError %@", String(describing: error)) } - private func getPumpHistory(_ device: RileyLinkDevice) { - lastHistoryAttempt = Date() - - guard let pumpOps = pumpOps else { - print("Missing pumpOps; is your pumpId configured?") - return - } - - let oneDayAgo = Date(timeIntervalSinceNow: TimeInterval(hours: -24)) - - pumpOps.runSession(withName: "Get pump history", using: device) { (session) in - do { - let (events, pumpModel) = try session.getHistoryEvents(since: oneDayAgo) - NSLog("fetchHistory succeeded.") - DispatchQueue.main.async { - self.handleNewHistoryEvents(events, pumpModel: pumpModel, device: device) - } - } catch let error { - NSLog("History fetch failed: %@", String(describing: error)) - } - - if Config.sharedInstance().fetchCGMEnabled, self.lastGlucoseEntry.timeIntervalSinceNow < TimeInterval(minutes: -5) { - self.getPumpGlucoseHistory(device) - } - } + func pumpManager(_ pumpManager: PumpManager, didReadPumpEvents events: [NewPumpEvent], completion: @escaping (_ error: Error?) -> Void) { } - private func handleNewHistoryEvents(_ events: [TimestampedHistoryEvent], pumpModel: PumpModel, device: RileyLinkDevice) { - // TODO: get insulin doses from history - if Config.sharedInstance().uploadEnabled { - remoteDataManager.nightscoutUploader?.processPumpEvents(events, source: device.deviceURI, pumpModel: pumpModel) - } + func pumpManager(_ pumpManager: PumpManager, didReadReservoirValue units: Double, at date: Date, completion: @escaping (_ result: PumpManagerResult<(newValue: ReservoirValue, lastValue: ReservoirValue?, areStoredValuesContinuous: Bool)>) -> Void) { } - private func getPumpGlucoseHistory(_ device: RileyLinkDevice) { - guard let pumpOps = pumpOps else { - print("Missing pumpOps; is your pumpId configured?") - return - } - - pumpOps.runSession(withName: "Get glucose history", using: device) { (session) in - do { - let events = try session.getGlucoseHistoryEvents(since: self.lastGlucoseEntry) - NSLog("fetchGlucoseHistory succeeded.") - if let latestEntryDate: Date = self.handleNewGlucoseHistoryEvents(events, device: device) { - self.lastGlucoseEntry = latestEntryDate - } - } catch let error { - NSLog("Glucose History fetch failed: %@", String(describing: error)) - } - } + func pumpManagerRecommendsLoop(_ pumpManager: PumpManager) { } - private func handleNewGlucoseHistoryEvents(_ events: [TimestampedGlucoseEvent], device: RileyLinkDevice) -> Date? { - if Config.sharedInstance().uploadEnabled { - return remoteDataManager.nightscoutUploader?.processGlucoseEvents(events, source: device.deviceURI) - } - return nil + func startDateToFilterNewPumpEvents(for manager: PumpManager) -> Date { + return Date().addingTimeInterval(.hours(-2)) } - // MARK: - Initialization - - static let sharedManager = DeviceDataManager() - - init() { - rileyLinkManager = RileyLinkDeviceManager(autoConnectIDs: connectedPeripheralIDs) - - var idleListeningEnabled = true - - if let pumpSettings = UserDefaults.standard.pumpSettings { - idleListeningEnabled = self.pumpState?.pumpModel?.hasMySentry ?? true - - self.pumpOps = PumpOps(pumpSettings: pumpSettings, pumpState: self.pumpState, delegate: self) - } - - rileyLinkManager.idleListeningState = idleListeningEnabled ? .enabledWithDefaults : .disabled - - UIDevice.current.isBatteryMonitoringEnabled = true - - // Device observers - NotificationCenter.default.addObserver(self, selector: #selector(receivedRileyLinkPacketNotification(_:)), name: .DevicePacketReceived, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(receivedRileyLinkTimerTickNotification(_:)), name: .DeviceTimerDidTick, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(deviceStateDidChange(_:)), name: .DeviceStateDidChange, object: nil) - } -} - - -extension DeviceDataManager: PumpOpsDelegate { - func pumpOps(_ pumpOps: PumpOps, didChange state: PumpState) { - if let sentrySupported = state.pumpModel?.hasMySentry { - rileyLinkManager.idleListeningState = sentrySupported ? .enabledWithDefaults : .disabled - } - - UserDefaults.standard.pumpState = state + func startDateToFilterNewReservoirEvents(for manager: PumpManager) -> Date { + return Date().addingTimeInterval(.minutes(-15)) } } diff --git a/RileyLink/Extensions/UserDefaults.swift b/RileyLink/Extensions/UserDefaults.swift index 844793675..dee16412a 100644 --- a/RileyLink/Extensions/UserDefaults.swift +++ b/RileyLink/Extensions/UserDefaults.swift @@ -6,39 +6,37 @@ // import Foundation -import MinimedKit - +import LoopKit +import RileyLinkKit +import RileyLinkBLEKit extension UserDefaults { private enum Key: String { - case pumpSettings = "com.rileylink.pumpSettings" - case pumpState = "com.rileylink.pumpState" + case pumpManagerRawValue = "com.rileylink.PumpManagerRawValue" + case rileyLinkConnectionManagerState = "com.rileylink.RileyLinkConnectionManagerState" } - - var pumpSettings: PumpSettings? { + + var pumpManagerRawValue: PumpManager.RawStateValue? { get { - guard let raw = dictionary(forKey: Key.pumpSettings.rawValue) else { - return nil - } - - return PumpSettings(rawValue: raw) + return dictionary(forKey: Key.pumpManagerRawValue.rawValue) } set { - set(newValue?.rawValue - , forKey: Key.pumpSettings.rawValue) + set(newValue, forKey: Key.pumpManagerRawValue.rawValue) } } - - var pumpState: PumpState? { + + var rileyLinkConnectionManagerState: RileyLinkConnectionManagerState? { get { - guard let raw = dictionary(forKey: Key.pumpState.rawValue) else { + guard let rawValue = dictionary(forKey: Key.rileyLinkConnectionManagerState.rawValue) else + { return nil } - - return PumpState(rawValue: raw) + return RileyLinkConnectionManagerState(rawValue: rawValue) } set { - set(newValue?.rawValue, forKey: Key.pumpState.rawValue) + set(newValue?.rawValue, forKey: Key.rileyLinkConnectionManagerState.rawValue) } } + } + diff --git a/RileyLink/Images.xcassets/icon-settings.imageset/Contents.json b/RileyLink/Images.xcassets/icon-settings.imageset/Contents.json deleted file mode 100644 index a683f09c8..000000000 --- a/RileyLink/Images.xcassets/icon-settings.imageset/Contents.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "filename" : "settings.png", - "scale" : "1x" - }, - { - "idiom" : "universal", - "filename" : "settings@2x.png", - "scale" : "2x" - }, - { - "idiom" : "universal", - "filename" : "settings@3x.png", - "scale" : "3x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/RileyLink/Images.xcassets/icon-settings.imageset/settings.png b/RileyLink/Images.xcassets/icon-settings.imageset/settings.png deleted file mode 100644 index e4a03e849..000000000 Binary files a/RileyLink/Images.xcassets/icon-settings.imageset/settings.png and /dev/null differ diff --git a/RileyLink/Images.xcassets/icon-settings.imageset/settings@2x.png b/RileyLink/Images.xcassets/icon-settings.imageset/settings@2x.png deleted file mode 100644 index 128cb8ef5..000000000 Binary files a/RileyLink/Images.xcassets/icon-settings.imageset/settings@2x.png and /dev/null differ diff --git a/RileyLink/Images.xcassets/icon-settings.imageset/settings@3x.png b/RileyLink/Images.xcassets/icon-settings.imageset/settings@3x.png deleted file mode 100644 index d010f71d7..000000000 Binary files a/RileyLink/Images.xcassets/icon-settings.imageset/settings@3x.png and /dev/null differ diff --git a/RileyLink/LaunchScreen.xib b/RileyLink/LaunchScreen.xib deleted file mode 100644 index 77207a771..000000000 --- a/RileyLink/LaunchScreen.xib +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/RileyLink/MainStoryboard.storyboard b/RileyLink/MainStoryboard.storyboard new file mode 100644 index 000000000..fae14a8ab --- /dev/null +++ b/RileyLink/MainStoryboard.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/RileyLink/PumpManagerState.swift b/RileyLink/PumpManagerState.swift new file mode 100644 index 000000000..dfe5ded89 --- /dev/null +++ b/RileyLink/PumpManagerState.swift @@ -0,0 +1,43 @@ +// +// PumpManagerState.swift +// Loop +// +// Copyright © 2018 LoopKit Authors. All rights reserved. +// + +import Foundation +import LoopKit +import MinimedKit +import RileyLinkBLEKit + + +let allPumpManagers: [String: PumpManager.Type] = [ + MinimedPumpManager.managerIdentifier: MinimedPumpManager.self +] + +func PumpManagerFromRawValue(_ rawValue: [String: Any], rileyLinkDeviceProvider: RileyLinkDeviceProvider) -> PumpManager? { + guard let managerIdentifier = rawValue["managerIdentifier"] as? String, + let rawState = rawValue["state"] as? PumpManager.RawStateValue + else { + return nil + } + + switch (managerIdentifier) { + case MinimedPumpManager.managerIdentifier: + guard let state = MinimedPumpManagerState(rawValue: rawState) else { + return nil + } + return MinimedPumpManager(state: state, rileyLinkDeviceProvider: rileyLinkDeviceProvider) + default: + return nil + } +} + +extension PumpManager { + var rawValue: [String: Any] { + return [ + "managerIdentifier": type(of: self).managerIdentifier, + "state": self.rawState + ] + } +} diff --git a/RileyLink/RileyLink-Info.plist b/RileyLink/RileyLink-Info.plist index 3991a00ee..e843d670a 100644 --- a/RileyLink/RileyLink-Info.plist +++ b/RileyLink/RileyLink-Info.plist @@ -19,7 +19,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleSignature ???? CFBundleVersion @@ -40,9 +40,9 @@ UIFileSharingEnabled UILaunchStoryboardName - LaunchScreen + MainStoryboard UIMainStoryboardFile - Storyboard + MainStoryboard UIRequiredDeviceCapabilities bluetooth-le diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Contents.json b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Contents.json rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Contents.json diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-60@2x.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-60@2x.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-60@2x.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-72.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-72.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-72.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-72.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-72@2x.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-72@2x.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-72@2x.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-76.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-76.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-76.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-76.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-76@2x.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-76@2x.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-76@2x.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small-40.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small-40.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small-40.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small-40@2x.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small-40@3x.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small-50.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small-50.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small-50.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small-50@2x.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small@2x.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small@2x.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small@2x.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small@3x.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-Small@3x.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-Small@3x.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon-ipadpro.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-ipadpro.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon-ipadpro.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon-ipadpro.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/Icon@2x.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon@2x.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/Icon@2x.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/Icon@2x.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/iTunesArtwork@2x-1.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/iTunesArtwork@2x-1.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/iTunesArtwork@2x-1.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/iTunesArtwork@2x-1.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/iTunesArtwork@2x-2.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/iTunesArtwork@2x-2.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/iTunesArtwork@2x-2.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/iTunesArtwork@2x-2.png diff --git a/RileyLink/Images.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png b/RileyLink/RileyLink.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png similarity index 100% rename from RileyLink/Images.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png rename to RileyLink/RileyLink.xcassets/AppIcon.appiconset/iTunesArtwork@2x.png diff --git a/RileyLink/Images.xcassets/Contents.json b/RileyLink/RileyLink.xcassets/Contents.json similarity index 100% rename from RileyLink/Images.xcassets/Contents.json rename to RileyLink/RileyLink.xcassets/Contents.json diff --git a/RileyLink/RileyLink.xcassets/LaunchImage.launchimage/Contents.json b/RileyLink/RileyLink.xcassets/LaunchImage.launchimage/Contents.json new file mode 100644 index 000000000..5a2966687 --- /dev/null +++ b/RileyLink/RileyLink.xcassets/LaunchImage.launchimage/Contents.json @@ -0,0 +1,49 @@ +{ + "images" : [ + { + "orientation" : "portrait", + "idiom" : "ipad", + "minimum-system-version" : "7.0", + "extent" : "full-screen", + "scale" : "2x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "minimum-system-version" : "7.0", + "extent" : "full-screen", + "scale" : "1x" + }, + { + "orientation" : "landscape", + "idiom" : "ipad", + "minimum-system-version" : "7.0", + "extent" : "full-screen", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "minimum-system-version" : "7.0", + "subtype" : "retina4", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "ipad", + "minimum-system-version" : "7.0", + "extent" : "full-screen", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/RileyLink/RileyLink.xcassets/RileyLink Tint.colorset/Contents.json b/RileyLink/RileyLink.xcassets/RileyLink Tint.colorset/Contents.json new file mode 100644 index 000000000..bb0dcbefa --- /dev/null +++ b/RileyLink/RileyLink.xcassets/RileyLink Tint.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "colors" : [ + { + "idiom" : "universal", + "color" : { + "color-space" : "srgb", + "components" : { + "red" : "0.345", + "alpha" : "1.000", + "blue" : "0.839", + "green" : "0.337" + } + } + } + ] +} \ No newline at end of file diff --git a/RileyLink/RileyLink.xcassets/RileyLink.imageset/Contents.json b/RileyLink/RileyLink.xcassets/RileyLink.imageset/Contents.json new file mode 100644 index 000000000..cfe31b855 --- /dev/null +++ b/RileyLink/RileyLink.xcassets/RileyLink.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "RileyLink.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/RileyLink/RileyLink.xcassets/RileyLink.imageset/RileyLink.pdf b/RileyLink/RileyLink.xcassets/RileyLink.imageset/RileyLink.pdf new file mode 100644 index 000000000..69cc898de Binary files /dev/null and b/RileyLink/RileyLink.xcassets/RileyLink.imageset/RileyLink.pdf differ diff --git a/RileyLink/RileyLinkRecord.h b/RileyLink/RileyLinkRecord.h deleted file mode 100644 index defb93a72..000000000 --- a/RileyLink/RileyLinkRecord.h +++ /dev/null @@ -1,20 +0,0 @@ -// -// RileyLinkRecord.h -// RileyLink -// -// Created by Pete Schwamb on 7/28/15. -// Copyright (c) 2015 Pete Schwamb. All rights reserved. -// - -#import -#import - - -@interface RileyLinkRecord : NSManagedObject - -@property (nonatomic, retain) NSNumber * autoConnect; -@property (nonatomic, retain) NSString * name; -@property (nonatomic, retain) NSString * peripheralId; -@property (nonatomic, retain) NSDate * firstSeenAt; - -@end diff --git a/RileyLink/RileyLinkRecord.m b/RileyLink/RileyLinkRecord.m deleted file mode 100644 index eaaf67311..000000000 --- a/RileyLink/RileyLinkRecord.m +++ /dev/null @@ -1,19 +0,0 @@ -// -// RileyLinkRecord.m -// RileyLink -// -// Created by Pete Schwamb on 7/28/15. -// Copyright (c) 2015 Pete Schwamb. All rights reserved. -// - -#import "RileyLinkRecord.h" - - -@implementation RileyLinkRecord - -@dynamic autoConnect; -@dynamic name; -@dynamic peripheralId; -@dynamic firstSeenAt; - -@end diff --git a/RileyLink/Storyboard.storyboard b/RileyLink/Storyboard.storyboard deleted file mode 100644 index 3b7ef7574..000000000 --- a/RileyLink/Storyboard.storyboard +++ /dev/null @@ -1,138 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/RileyLink/View Controllers/AuthenticationViewController.swift b/RileyLink/View Controllers/AuthenticationViewController.swift deleted file mode 100644 index f73604b93..000000000 --- a/RileyLink/View Controllers/AuthenticationViewController.swift +++ /dev/null @@ -1,213 +0,0 @@ -// -// AuthenticationViewController.swift -// Loop -// -// Created by Nate Racklyeft on 7/2/16. -// Copyright © 2016 Nathan Racklyeft. All rights reserved. -// - -import UIKit - - -class AuthenticationViewController: UITableViewController, IdentifiableClass, UITextFieldDelegate { - - typealias AuthenticationObserver = (_ authentication: T) -> Void - - var authenticationObserver: AuthenticationObserver? - - var authentication: T - - private var state: AuthenticationState = .empty { - didSet { - switch (oldValue, state) { - case let (x, y) where x == y: - break - case (_, .verifying): - let titleView = ValidatingIndicatorView(frame: CGRect.zero) - UIView.animate(withDuration: 0.25, animations: { - self.navigationItem.hidesBackButton = true - self.navigationItem.titleView = titleView - }) - - tableView.reloadSections(IndexSet(integersIn: 0...1), with: .automatic) - authentication.verify { [weak self] (success, error) in - guard let strongSelf = self else { - return - } - - DispatchQueue.main.async { - UIView.animate(withDuration: 0.25, animations: { - strongSelf.navigationItem.titleView = nil - strongSelf.navigationItem.hidesBackButton = false - }) - - if success { - strongSelf.state = .authorized - } else { - if let error = error { - strongSelf.presentAlertControllerWithError(error) - } - - strongSelf.state = .unauthorized - } - } - } - case (_, .authorized), (_, .unauthorized): - authenticationObserver?(authentication) - tableView.reloadSections(IndexSet(integersIn: 0...1), with: .automatic) - default: - break - } - } - } - - init(authentication: T) { - self.authentication = authentication - - state = authentication.isAuthorized ? .authorized : .unauthorized - - super.init(style: .grouped) - - title = authentication.title - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - tableView.register(AuthenticationTableViewCell.nib(), forCellReuseIdentifier: AuthenticationTableViewCell.className) - tableView.register(ButtonTableViewCell.nib(), forCellReuseIdentifier: ButtonTableViewCell.className) - } - - // MARK: - Table view data source - - override func numberOfSections(in tableView: UITableView) -> Int { - return Section.count - } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - switch Section(rawValue: section)! { - case .credentials: - switch state { - case .authorized: - return authentication.credentials.filter({ !$0.isSecret }).count - default: - return authentication.credentials.count - } - case .button: - return 1 - } - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - switch Section(rawValue: indexPath.section)! { - case .button: - let cell = tableView.dequeueReusableCell(withIdentifier: ButtonTableViewCell.className, for: indexPath) as! ButtonTableViewCell - - switch state { - case .authorized: - cell.button.setTitle(LocalizedString("Delete Account", comment: "The title of the button to remove the credentials for a service"), for: UIControlState()) - cell.button.setTitleColor(UIColor.deleteColor, for: UIControlState()) - case .empty, .unauthorized, .verifying: - cell.button.setTitle(LocalizedString("Add Account", comment: "The title of the button to add the credentials for a service"), for: UIControlState()) - cell.button.setTitleColor(nil, for: UIControlState()) - } - - if case .verifying = state { - cell.button.isEnabled = false - } else { - cell.button.isEnabled = true - } - - cell.button.addTarget(self, action: #selector(buttonPressed(_:)), for: .touchUpInside) - - return cell - case .credentials: - let cell = tableView.dequeueReusableCell(withIdentifier: AuthenticationTableViewCell.className, for: indexPath) as! AuthenticationTableViewCell - - let credential = authentication.credentials[indexPath.row] - - cell.titleLabel.text = credential.title - cell.textField.tag = indexPath.row - cell.textField.keyboardType = credential.keyboardType - cell.textField.isSecureTextEntry = credential.isSecret - cell.textField.returnKeyType = (indexPath.row < authentication.credentials.count - 1) ? .next : .done - cell.textField.text = credential.value - cell.textField.placeholder = credential.placeholder ?? LocalizedString("Required", comment: "The default placeholder string for a credential") - - cell.textField.delegate = self - - switch state { - case .authorized, .verifying, .empty: - cell.textField.isEnabled = false - case .unauthorized: - cell.textField.isEnabled = true - } - - return cell - } - } - - private func validate() { - state = .verifying - } - - // MARK: - Actions - - @objc private func buttonPressed(_: AnyObject) { - tableView.endEditing(false) - - switch state { - case .authorized: - authentication.reset() - state = .unauthorized - case .unauthorized: - validate() - default: - break - } - - } - - // MARK: - UITextFieldDelegate - - func textFieldDidEndEditing(_ textField: UITextField) { - authentication.credentials[textField.tag].value = textField.text - } - - func textFieldShouldReturn(_ textField: UITextField) -> Bool { - if textField.returnKeyType == .done { - textField.resignFirstResponder() - } else { - let point = tableView.convert(textField.frame.origin, from: textField.superview) - if let indexPath = tableView.indexPathForRow(at: point), - let cell = tableView.cellForRow(at: IndexPath(row: indexPath.row + 1, section: indexPath.section)) as? AuthenticationTableViewCell - { - cell.textField.becomeFirstResponder() - - validate() - } - } - - return true - } -} - - -private enum Section: Int { - case credentials - case button - - static let count = 2 -} - - -enum AuthenticationState { - case empty - case authorized - case verifying - case unauthorized -} diff --git a/RileyLink/View Controllers/MainViewController.swift b/RileyLink/View Controllers/MainViewController.swift new file mode 100644 index 000000000..eadab0bcc --- /dev/null +++ b/RileyLink/View Controllers/MainViewController.swift @@ -0,0 +1,195 @@ +// +// MainViewController.swift +// RileyLink +// +// Created by Pete Schwamb on 5/11/16. +// Copyright © 2016 Pete Schwamb. All rights reserved. +// +import UIKit +import MinimedKit +import MinimedKitUI +import RileyLinkBLEKit +import RileyLinkKit +import RileyLinkKitUI +import LoopKit +import LoopKitUI + +class MainViewController: RileyLinkSettingsViewController { + + let deviceDataManager: DeviceDataManager + + init(deviceDataManager: DeviceDataManager) { + self.deviceDataManager = deviceDataManager + let rileyLinkPumpManager = RileyLinkPumpManager(rileyLinkDeviceProvider: deviceDataManager.rileyLinkConnectionManager.deviceProvider, rileyLinkConnectionManager: deviceDataManager.rileyLinkConnectionManager) + + super.init(rileyLinkPumpManager: rileyLinkPumpManager, devicesSectionIndex: Section.rileyLinks.rawValue, style: .grouped) + + self.title = NSLocalizedString("RileyLink Testing", comment: "Title for RileyLink Testing main view controller") + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + tableView.backgroundColor = UIColor.white + tableView.separatorStyle = .none + tableView.rowHeight = UITableViewAutomaticDimension + tableView.estimatedRowHeight = 44 + + tableView.sectionHeaderHeight = UITableViewAutomaticDimension + tableView.estimatedSectionHeaderHeight = 55 + + tableView.register(TextButtonTableViewCell.self, forCellReuseIdentifier: TextButtonTableViewCell.className) + tableView.register(SettingsImageTableViewCell.self, forCellReuseIdentifier: SettingsImageTableViewCell.className) + + let rlImage = UIImage(named: "RileyLink", in: Bundle.main, compatibleWith: tableView.traitCollection) + let imageView = UIImageView(image: rlImage) + imageView.tintColor = UIColor.white + imageView.contentMode = .center + imageView.frame.size.height += 30 // feels right + imageView.backgroundColor = UIColor(named: "RileyLink Tint", in: Bundle.main, compatibleWith: tableView.traitCollection) + tableView.tableHeaderView = imageView + + tableView.register(RileyLinkDeviceTableViewCell.self, forCellReuseIdentifier: RileyLinkDeviceTableViewCell.className) + } + + override func viewWillAppear(_ animated: Bool) { + // Manually invoke the delegate for rows deselecting on appear + for indexPath in tableView.indexPathsForSelectedRows ?? [] { + _ = tableView(tableView, willDeselectRowAt: indexPath) + } + + super.viewWillAppear(animated) + } + + fileprivate enum Section: Int, CaseCountable { + case rileyLinks = 0 + case pump + } + + fileprivate enum PumpActionRow: Int, CaseCountable { + case addMinimedPump = 0 + } + + weak var rileyLinkManager: RileyLinkDeviceManager! + + // MARK: Data Source + + override func numberOfSections(in tableView: UITableView) -> Int { + return Section.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch Section(rawValue: section)! { + case .rileyLinks: + return super.tableView(tableView, numberOfRowsInSection: section) + case .pump: + if let _ = deviceDataManager.pumpManager { + return 1 + } else { + return PumpActionRow.count + } + } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell: UITableViewCell + + switch(Section(rawValue: indexPath.section)!) { + case .rileyLinks: + return super.tableView(tableView, cellForRowAt: indexPath) + case .pump: + if let pumpManager = deviceDataManager.pumpManager { + cell = tableView.dequeueReusableCell(withIdentifier: SettingsImageTableViewCell.className, for: indexPath) + cell.imageView?.image = pumpManager.smallImage + cell.textLabel?.text = pumpManager.localizedTitle + cell.detailTextLabel?.text = nil + cell.accessoryType = .disclosureIndicator + } else { + switch(PumpActionRow(rawValue: indexPath.row)!) { + case .addMinimedPump: + cell = tableView.dequeueReusableCell(withIdentifier: TextButtonTableViewCell.className, for: indexPath) + cell.textLabel?.text = NSLocalizedString("Add Minimed Pump", comment: "Title text for button to set up a new minimed pump") + } + } + } + return cell + } + + public override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + switch Section(rawValue: section)! { + case .rileyLinks: + return super.tableView(tableView, titleForHeaderInSection: section) + case .pump: + return NSLocalizedString("Pumps", comment: "Title text for section listing configured pumps") + } + } + + public override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + switch Section(rawValue: section)! { + case .rileyLinks: + return super.tableView(tableView, viewForHeaderInSection: section) + case .pump: + return nil + } + } + + public override func tableView(_ tableView: UITableView, estimatedHeightForHeaderInSection section: Int) -> CGFloat { + return devicesDataSource.tableView(tableView, estimatedHeightForHeaderInSection: section) + } + + // MARK: - UITableViewDelegate + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let sender = tableView.cellForRow(at: indexPath) + + switch Section(rawValue: indexPath.section)! { + case .rileyLinks: + let device = devicesDataSource.devices[indexPath.row] + let vc = RileyLinkDeviceTableViewController(device: device) + show(vc, sender: indexPath) + case .pump: + if let pumpManager = deviceDataManager.pumpManager { + let settings = pumpManager.settingsViewController() + show(settings, sender: sender) + } else { + var setupViewController: PumpManagerSetupViewController & UIViewController + switch PumpActionRow(rawValue: indexPath.row)! { + case .addMinimedPump: + setupViewController = UIStoryboard(name: "MinimedPumpManager", bundle: Bundle(for: MinimedPumpManagerSetupViewController.self)).instantiateViewController(withIdentifier: "DevelopmentPumpSetup") as! MinimedPumpManagerSetupViewController + } + if let rileyLinkManagerViewController = setupViewController as? RileyLinkManagerSetupViewController { + rileyLinkManagerViewController.rileyLinkPumpManager = RileyLinkPumpManager(rileyLinkDeviceProvider: deviceDataManager.rileyLinkConnectionManager.deviceProvider) + } + setupViewController.setupDelegate = self + present(setupViewController, animated: true, completion: nil) + } + } + } + + override func tableView(_ tableView: UITableView, willDeselectRowAt indexPath: IndexPath) -> IndexPath? { + switch Section(rawValue: indexPath.section)! { + case .rileyLinks: + break + case .pump: + tableView.reloadSections(IndexSet([Section.pump.rawValue]), with: .none) + } + + return indexPath + } +} + +extension MainViewController: PumpManagerSetupViewControllerDelegate { + func pumpManagerSetupViewController(_ pumpManagerSetupViewController: PumpManagerSetupViewController, didSetUpPumpManager pumpManager: PumpManagerUI) { + deviceDataManager.pumpManager = pumpManager + show(pumpManager.settingsViewController(), sender: nil) + tableView.reloadSections(IndexSet([Section.pump.rawValue]), with: .none) + dismiss(animated: true, completion: nil) + } + + func pumpManagerSetupViewControllerDidCancel(_ pumpManagerSetupViewController: PumpManagerSetupViewController) { + dismiss(animated: true, completion: nil) + } +} diff --git a/RileyLink/View Controllers/RadioSelectionTableViewController.swift b/RileyLink/View Controllers/RadioSelectionTableViewController.swift deleted file mode 100644 index bd179917e..000000000 --- a/RileyLink/View Controllers/RadioSelectionTableViewController.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// RadioSelectionTableViewController.swift -// Loop -// -// Created by Nate Racklyeft on 8/26/16. -// Copyright © 2016 Nathan Racklyeft. All rights reserved. -// - -import UIKit -import MinimedKit - - -protocol RadioSelectionTableViewControllerDelegate: class { - func radioSelectionTableViewControllerDidChangeSelectedIndex(_ controller: RadioSelectionTableViewController) -} - - -class RadioSelectionTableViewController: UITableViewController, IdentifiableClass { - - var options = [String]() - - var selectedIndex: Int? { - didSet { - if let oldValue = oldValue , oldValue != selectedIndex { - tableView.cellForRow(at: IndexPath(row: oldValue, section: 0))?.accessoryType = .none - } - - if let selectedIndex = selectedIndex , oldValue != selectedIndex { - tableView.cellForRow(at: IndexPath(row: selectedIndex, section: 0))?.accessoryType = .checkmark - - delegate?.radioSelectionTableViewControllerDidChangeSelectedIndex(self) - } - } - } - - var contextHelp: String? - - weak var delegate: RadioSelectionTableViewControllerDelegate? - - convenience init() { - self.init(style: .grouped) - } - - // MARK: - Table view data source - - override func numberOfSections(in tableView: UITableView) -> Int { - return 1 - } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return options.count - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") ?? UITableViewCell(style: .default, reuseIdentifier: "Cell") - - cell.textLabel?.text = options[indexPath.row] - cell.accessoryType = selectedIndex == indexPath.row ? .checkmark : .none - - return cell - } - - override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { - return contextHelp - } - - override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - selectedIndex = indexPath.row - - tableView.deselectRow(at: indexPath, animated: true) - } -} - - -extension RadioSelectionTableViewController { - typealias T = RadioSelectionTableViewController - - static func pumpRegion(_ value: PumpRegion?) -> T { - let vc = T() - - vc.selectedIndex = value?.rawValue - vc.options = (0..<2).compactMap({ PumpRegion(rawValue: $0) }).map { String(describing: $0) } - vc.contextHelp = LocalizedString("Pump Region is listed on the back of your pump as two of the last three characters of the model string, which reads something like this: MMT-551NAB, or MMT-515LWWS. If your model has an \"NA\" in it, then the region is NorthAmerica. If your model has an \"WW\" in it, then the region is WorldWide.", comment: "Instructions on selecting the pump region") - return vc - } -} diff --git a/RileyLink/View Controllers/RileyLinkListTableViewController.swift b/RileyLink/View Controllers/RileyLinkListTableViewController.swift deleted file mode 100644 index c014ba92f..000000000 --- a/RileyLink/View Controllers/RileyLinkListTableViewController.swift +++ /dev/null @@ -1,172 +0,0 @@ -// -// RileyLinkListTableViewController.swift -// RileyLink -// -// Created by Pete Schwamb on 5/11/16. -// Copyright © 2016 Pete Schwamb. All rights reserved. -// - -import UIKit -import MinimedKitUI -import RileyLinkBLEKit -import RileyLinkKit -import RileyLinkKitUI - - -class RileyLinkListTableViewController: UITableViewController { - - private lazy var numberFormatter = NumberFormatter() - - override func viewDidLoad() { - super.viewDidLoad() - - tableView.register(RileyLinkDeviceTableViewCell.self, forCellReuseIdentifier: RileyLinkDeviceTableViewCell.className) - - // Register for manager notifications - NotificationCenter.default.addObserver(self, selector: #selector(reloadDevices), name: .ManagerDevicesDidChange, object: dataManager.rileyLinkManager) - - // Register for device notifications - for name in [.DeviceConnectionStateDidChange, .DeviceRSSIDidChange, .DeviceNameDidChange] as [Notification.Name] { - NotificationCenter.default.addObserver(self, selector: #selector(deviceDidUpdate(_:)), name: name, object: nil) - } - - reloadDevices() - } - - @objc private func reloadDevices() { - self.dataManager.rileyLinkManager.getDevices { (devices) in - DispatchQueue.main.async { - self.devices = devices - } - } - } - - @objc private func deviceDidUpdate(_ note: Notification) { - DispatchQueue.main.async { - if let device = note.object as? RileyLinkDevice, let index = self.devices.index(where: { $0 === device }) { - if let rssi = note.userInfo?[RileyLinkDevice.notificationRSSIKey] as? Int { - self.deviceRSSI[device.peripheralIdentifier] = rssi - } - - if let cell = self.tableView.cellForRow(at: IndexPath(row: index, section: 0)) as? RileyLinkDeviceTableViewCell { - cell.configureCellWithName(device.name, - signal: self.numberFormatter.decibleString(from: self.deviceRSSI[device.peripheralIdentifier]), - peripheralState: device.peripheralState - ) - } - } - } - } - - private var dataManager: DeviceDataManager { - return DeviceDataManager.sharedManager - } - - private var devices: [RileyLinkDevice] = [] { - didSet { - // Assume only appends are possible when count changes for algorithmic simplicity - guard oldValue.count < devices.count else { - tableView.reloadSections(IndexSet(integer: 0), with: .fade) - return - } - - tableView.beginUpdates() - - let insertedPaths = (oldValue.count.. IndexPath in - return IndexPath(row: index, section: 0) - } - tableView.insertRows(at: insertedPaths, with: .automatic) - - tableView.endUpdates() - } - } - - private var deviceRSSI: [UUID: Int] = [:] - - var rssiFetchTimer: Timer? { - willSet { - rssiFetchTimer?.invalidate() - } - } - - @objc func updateRSSI() { - for device in devices { - device.readRSSI() - } - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - dataManager.rileyLinkManager.setScanningEnabled(true) - - rssiFetchTimer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(updateRSSI), userInfo: nil, repeats: true) - - updateRSSI() - } - - override func viewWillDisappear(_ animated: Bool) { - super.viewWillDisappear(animated) - - dataManager.rileyLinkManager.setScanningEnabled(false) - - rssiFetchTimer = nil - } - - // MARK: Table view data source - - override func numberOfSections(in tableView: UITableView) -> Int { - return 1 - } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return devices.count - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell: UITableViewCell - - let deviceCell = tableView.dequeueReusableCell(withIdentifier: RileyLinkDeviceTableViewCell.className) as! RileyLinkDeviceTableViewCell - - let device = devices[indexPath.row] - - deviceCell.configureCellWithName( - device.name, - signal: numberFormatter.decibleString(from: deviceRSSI[device.peripheralIdentifier]), - peripheralState: device.peripheralState - ) - - deviceCell.connectSwitch?.addTarget(self, action: #selector(changeDeviceConnection(_:)), for: .valueChanged) - - cell = deviceCell - return cell - } - - @objc func changeDeviceConnection(_ connectSwitch: UISwitch) { - let switchOrigin = connectSwitch.convert(CGPoint.zero, to: tableView) - - if let indexPath = tableView.indexPathForRow(at: switchOrigin) { - let device = devices[indexPath.row] - - if connectSwitch.isOn { - dataManager.connectToRileyLink(device) - } else { - dataManager.disconnectFromRileyLink(device) - } - } - } - - // MARK: - UITableViewDelegate - - override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - guard let pumpOps = dataManager.pumpOps else { - return - } - let device = devices[indexPath.row] - let vc = RileyLinkMinimedDeviceTableViewController( - device: device, - deviceState: dataManager.deviceStates[device.peripheralIdentifier, default: DeviceState()], - pumpOps: pumpOps) - show(vc, sender: indexPath) - } -} diff --git a/RileyLink/View Controllers/SettingsTableViewController.swift b/RileyLink/View Controllers/SettingsTableViewController.swift deleted file mode 100644 index 0815b6283..000000000 --- a/RileyLink/View Controllers/SettingsTableViewController.swift +++ /dev/null @@ -1,242 +0,0 @@ -// -// SettingsTableViewController.swift -// RileyLink -// -// Created by Nathan Racklyeft on 8/29/15. -// Copyright © 2015 Nathan Racklyeft. All rights reserved. -// - -import UIKit -import LoopKitUI -import MinimedKit -import RileyLinkKit -import RileyLinkKitUI - -private let ConfigCellIdentifier = "ConfigTableViewCell" - -private let TapToSetString = LocalizedString("Tap to set", comment: "The empty-state text for a configuration value") - - -extension TextFieldTableViewController: IdentifiableClass { } - - -class SettingsTableViewController: UITableViewController, TextFieldTableViewControllerDelegate { - - fileprivate enum Section: Int { - case about = 0 - case upload - case configuration - - static let count = 3 - } - - fileprivate enum AboutRow: Int { - case version = 0 - - static let count = 1 - } - - fileprivate enum UploadRow: Int { - case upload = 0 - - static let count = 1 - } - - fileprivate enum ConfigurationRow: Int { - case pumpID = 0 - case pumpRegion - case nightscout - case fetchCGM - static let count = 4 - } - - fileprivate var dataManager: DeviceDataManager { - return DeviceDataManager.sharedManager - } - - // MARK: - UITableViewDataSource - - override func numberOfSections(in tableView: UITableView) -> Int { - return Section.count - } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - switch Section(rawValue: section)! { - case .about: - return AboutRow.count - case .upload: - return UploadRow.count - case .configuration: - return ConfigurationRow.count - } - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell: UITableViewCell - - switch Section(rawValue: indexPath.section)! { - case .about: - switch AboutRow(rawValue: indexPath.row)! { - case .version: - let versionCell = UITableViewCell(style: .default, reuseIdentifier: "Version") - versionCell.selectionStyle = .none - let version = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString")! - versionCell.textLabel?.text = "RileyLink iOS v\(version)" - - return versionCell - } - case .upload: - switch UploadRow(rawValue: indexPath.row)! { - case .upload: - let switchCell = tableView.dequeueReusableCell(withIdentifier: SwitchTableViewCell.className, for: indexPath) as! SwitchTableViewCell - - switchCell.`switch`?.isOn = Config.sharedInstance().uploadEnabled - switchCell.titleLabel.text = LocalizedString("Upload To Nightscout", comment: "The title text for the nightscout upload enabled switch cell") - switchCell.`switch`?.addTarget(self, action: #selector(uploadEnabledChanged(_:)), for: .valueChanged) - - return switchCell - } - case .configuration: - - switch ConfigurationRow(rawValue: indexPath.row)! { - case .pumpID: - let configCell = tableView.dequeueReusableCell(withIdentifier: ConfigCellIdentifier, for: indexPath) - configCell.textLabel?.text = LocalizedString("Pump ID", comment: "The title text for the pump ID config value") - configCell.detailTextLabel?.text = dataManager.pumpSettings?.pumpID ?? TapToSetString - cell = configCell - case .pumpRegion: - let configCell = tableView.dequeueReusableCell(withIdentifier: ConfigCellIdentifier, for: indexPath) - configCell.textLabel?.text = LocalizedString("Pump Region", comment: "The title text for the pump Region config value") - - if let pumpRegion = dataManager.pumpSettings?.pumpRegion { - configCell.detailTextLabel?.text = String(describing: pumpRegion) - } else { - configCell.detailTextLabel?.text = nil - } - - cell = configCell - case .nightscout: - let configCell = tableView.dequeueReusableCell(withIdentifier: ConfigCellIdentifier, for: indexPath) - let nightscoutService = dataManager.remoteDataManager.nightscoutService - - configCell.textLabel?.text = nightscoutService.title - configCell.detailTextLabel?.text = nightscoutService.siteURL?.absoluteString ?? TapToSetString - cell = configCell - case .fetchCGM: - let switchCell = tableView.dequeueReusableCell(withIdentifier: SwitchTableViewCell.className, for: indexPath) as! SwitchTableViewCell - - switchCell.`switch`?.isOn = Config.sharedInstance().fetchCGMEnabled - switchCell.titleLabel.text = LocalizedString("Fetch CGM", comment: "The title text for the pull cgm Data cell") - switchCell.`switch`?.addTarget(self, action: #selector(fetchCGMEnabledChanged(_:)), for: .valueChanged) - cell = switchCell - } - } - return cell - } - - override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { - switch Section(rawValue: section)! { - case .about: - return LocalizedString("About", comment: "The title of the about section") - case .upload: - return nil - case .configuration: - return LocalizedString("Configuration", comment: "The title of the configuration section in settings") - } - } - - // MARK: - UITableViewDelegate - - override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - switch Section(rawValue: indexPath.section)! { - case .configuration: - let sender = tableView.cellForRow(at: indexPath) - - switch ConfigurationRow(rawValue: indexPath.row)! { - case .pumpID: - let vc = TextFieldTableViewController() - - vc.placeholder = LocalizedString("Enter the 6-digit pump ID", comment: "The placeholder text instructing users how to enter a pump ID") - vc.value = dataManager.pumpSettings?.pumpID - - if let cell = tableView.cellForRow(at: indexPath) { - vc.title = cell.textLabel?.text - } - vc.indexPath = indexPath - vc.delegate = self - - show(vc, sender: indexPath) - case .pumpRegion: - let vc = RadioSelectionTableViewController.pumpRegion(dataManager.pumpSettings?.pumpRegion) - vc.title = sender?.textLabel?.text - vc.delegate = self - - show(vc, sender: sender) - case .nightscout: - let service = dataManager.remoteDataManager.nightscoutService - let vc = AuthenticationViewController(authentication: service) - vc.authenticationObserver = { [weak self] (service) in - self?.dataManager.remoteDataManager.nightscoutService = service - - self?.tableView.reloadRows(at: [indexPath], with: .none) - } - - show(vc, sender: indexPath) - default: - break - } - case .upload, .about: - break - } - } - - override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { - switch Section(rawValue: section)! { - case .upload, .configuration, .about: - return nil - } - } - - // MARK: - Uploader management - - @objc func uploadEnabledChanged(_ sender: UISwitch) { - Config.sharedInstance().uploadEnabled = sender.isOn - } - - // MARK: - CGM Page Fetching Management - - @objc func fetchCGMEnabledChanged(_ sender: UISwitch) { - Config.sharedInstance().fetchCGMEnabled = sender.isOn - } - - // MARK: - TextFieldTableViewControllerDelegate - - func textFieldTableViewControllerDidEndEditing(_ controller: TextFieldTableViewController) { - if let indexPath = controller.indexPath { - switch ConfigurationRow(rawValue: indexPath.row)! { - case .pumpID: - dataManager.setPumpID(controller.value) - default: - break - } - } - - tableView.reloadData() - } - - func textFieldTableViewControllerDidReturn(_ controller: TextFieldTableViewController) { - - } -} - - -extension SettingsTableViewController: RadioSelectionTableViewControllerDelegate { - func radioSelectionTableViewControllerDidChangeSelectedIndex(_ controller: RadioSelectionTableViewController) { - if let selectedIndex = controller.selectedIndex, let pumpRegion = PumpRegion(rawValue: selectedIndex) { - dataManager.setPumpRegion(pumpRegion) - - tableView.reloadRows(at: [IndexPath(row: ConfigurationRow.pumpRegion.rawValue, section: Section.configuration.rawValue)], with: .none) - } - } -} - diff --git a/RileyLink/Views/AuthenticationTableViewCell.swift b/RileyLink/Views/AuthenticationTableViewCell.swift deleted file mode 100644 index bd57c1b1b..000000000 --- a/RileyLink/Views/AuthenticationTableViewCell.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// AuthenticationTableViewCell.swift -// Loop -// -// Created by Nate Racklyeft on 7/2/16. -// Copyright © 2016 Nathan Racklyeft. All rights reserved. -// - -import UIKit - -class AuthenticationTableViewCell: UITableViewCell, NibLoadable { - - @IBOutlet weak var titleLabel: UILabel! - - @IBOutlet weak var textField: UITextField! - - override func setSelected(_ selected: Bool, animated: Bool) { - super.setSelected(selected, animated: animated) - - if selected { - textField.becomeFirstResponder() - } - } - - override func prepareForReuse() { - super.prepareForReuse() - - textField.delegate = nil - } - -} diff --git a/RileyLink/Views/AuthenticationTableViewCell.xib b/RileyLink/Views/AuthenticationTableViewCell.xib deleted file mode 100644 index 8114146a9..000000000 --- a/RileyLink/Views/AuthenticationTableViewCell.xib +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/RileyLink/Views/ButtonTableViewCell.swift b/RileyLink/Views/ButtonTableViewCell.swift deleted file mode 100644 index 46b975630..000000000 --- a/RileyLink/Views/ButtonTableViewCell.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// ButtonTableViewCell.swift -// Loop -// -// Created by Nate Racklyeft on 7/2/16. -// Copyright © 2016 Nathan Racklyeft. All rights reserved. -// - -import UIKit - -class ButtonTableViewCell: UITableViewCell, NibLoadable { - - @IBOutlet weak var button: UIButton! - - override func prepareForReuse() { - super.prepareForReuse() - - button.removeTarget(nil, action: nil, for: .touchUpInside) - } -} diff --git a/RileyLink/Views/ButtonTableViewCell.xib b/RileyLink/Views/ButtonTableViewCell.xib deleted file mode 100644 index f1e8f865d..000000000 --- a/RileyLink/Views/ButtonTableViewCell.xib +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/RileyLink/Views/RileyLinkDeviceTableViewCell.swift b/RileyLink/Views/RileyLinkDeviceTableViewCell.swift deleted file mode 100644 index b296c587d..000000000 --- a/RileyLink/Views/RileyLinkDeviceTableViewCell.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// RileyLinkDeviceTableViewCell.swift -// RileyLink -// -// Created by Nathan Racklyeft on 5/22/16. -// Copyright © 2016 Pete Schwamb. All rights reserved. -// - -import UIKit -import RileyLinkKitUI - -extension RileyLinkDeviceTableViewCell: IdentifiableClass { } diff --git a/RileyLink/Views/SettingsImageTableViewCell.swift b/RileyLink/Views/SettingsImageTableViewCell.swift new file mode 100644 index 000000000..505440bef --- /dev/null +++ b/RileyLink/Views/SettingsImageTableViewCell.swift @@ -0,0 +1,53 @@ +// +// SettingsImageTableViewCell.swift +// Loop +// +// Copyright © 2018 LoopKit Authors. All rights reserved. +// + +import UIKit + + +class SettingsImageTableViewCell: UITableViewCell { + override init(style: UITableViewCellStyle, reuseIdentifier: String?) { + super.init(style: .default, reuseIdentifier: reuseIdentifier) + + setup() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + + setup() + } + + private func setup() { + guard let textLabel = textLabel, let imageView = imageView else { + return + } + + textLabel.adjustsFontForContentSizeCategory = true + textLabel.font = UIFont.preferredFont(forTextStyle: .body) + textLabel.translatesAutoresizingMaskIntoConstraints = false + imageView.translatesAutoresizingMaskIntoConstraints = false + + let parent = contentView.layoutMarginsGuide + NSLayoutConstraint.activate([ + imageView.leadingAnchor.constraint(equalTo: parent.leadingAnchor), + imageView.topAnchor.constraint(greaterThanOrEqualTo: parent.topAnchor), + parent.bottomAnchor.constraint(greaterThanOrEqualTo: imageView.bottomAnchor), + imageView.centerYAnchor.constraint(equalTo: parent.centerYAnchor), + textLabel.leadingAnchor.constraintEqualToSystemSpacingAfter(imageView.trailingAnchor, multiplier: 2), + textLabel.topAnchor.constraint(greaterThanOrEqualTo: parent.topAnchor), + parent.bottomAnchor.constraint(greaterThanOrEqualTo: textLabel.bottomAnchor), + parent.trailingAnchor.constraint(equalTo: textLabel.trailingAnchor), + textLabel.centerYAnchor.constraint(equalTo: parent.centerYAnchor) + ]) + } + + override func prepareForReuse() { + super.prepareForReuse() + + imageView?.image = nil + } +} diff --git a/RileyLink/Views/SwitchTableViewCell.swift b/RileyLink/Views/SwitchTableViewCell.swift deleted file mode 100644 index 3e2a739e8..000000000 --- a/RileyLink/Views/SwitchTableViewCell.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// SwitchTableViewCell.swift -// Naterade -// -// Created by Nathan Racklyeft on 3/13/16. -// Copyright © 2016 Nathan Racklyeft. All rights reserved. -// - -import UIKit -import RileyLinkKit - -class SwitchTableViewCell: UITableViewCell, IdentifiableClass { - - @IBOutlet weak var titleLabel: UILabel! - - @IBOutlet var `switch`: UISwitch? - - override func prepareForReuse() { - super.prepareForReuse() - - `switch`?.removeTarget(nil, action: nil, for: .valueChanged) - } - -} diff --git a/RileyLink/Views/ValidatingIndicatorView.swift b/RileyLink/Views/ValidatingIndicatorView.swift deleted file mode 100644 index 91765a2d8..000000000 --- a/RileyLink/Views/ValidatingIndicatorView.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// ValidatingIndicatorView.swift -// Loop -// -// Created by Nate Racklyeft on 7/2/16. -// Copyright © 2016 Nathan Racklyeft. All rights reserved. -// - -import UIKit - -private let Margin: CGFloat = 8 - - -class ValidatingIndicatorView: UIView { - - let indicatorView = UIActivityIndicatorView(activityIndicatorStyle: .gray) - - let label = UILabel() - - override init(frame: CGRect) { - super.init(frame: frame) - label.font = UIFont.preferredFont(forTextStyle: UIFontTextStyle.headline) - label.text = LocalizedString("Verifying", comment: "Label indicating validation is occurring") - label.sizeToFit() - - addSubview(indicatorView) - addSubview(label) - - self.frame.size = intrinsicContentSize - - setNeedsLayout() - - indicatorView.startAnimating() - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func layoutSubviews() { - super.layoutSubviews() - - // Center the label in the bounds so it appears aligned, then let the indicator view hang from the left side - label.frame = bounds - indicatorView.center.y = bounds.midY - indicatorView.frame.origin.x = -indicatorView.frame.size.width - Margin - } - - override var intrinsicContentSize : CGSize { - return label.intrinsicContentSize - } -} diff --git a/RileyLinkBLEKit/Command.swift b/RileyLinkBLEKit/Command.swift index 542fcd243..c011d10bd 100644 --- a/RileyLinkBLEKit/Command.swift +++ b/RileyLinkBLEKit/Command.swift @@ -200,6 +200,35 @@ struct UpdateRegister: Command { } } +struct ReadRegister: Command { + typealias ResponseType = ReadRegisterResponse + + enum Response: UInt8 { + case success = 1 + case invalidRegister = 2 + } + + let address: CC111XRegister + let firmwareVersion: RadioFirmwareVersion + + init(_ address: CC111XRegister, firmwareVersion: RadioFirmwareVersion) { + self.address = address + self.firmwareVersion = firmwareVersion + } + + var data: Data { + var data = Data(bytes: [ + RileyLinkCommand.readRegister.rawValue, + address.rawValue, + ]) + if firmwareVersion.needsExtraByteForReadRegisterCommand { + data.append(address.rawValue) + } + return data + } +} + + struct SetModeRegisters: Command { typealias ResponseType = UpdateRegisterResponse diff --git a/RileyLinkBLEKit/CommandSession.swift b/RileyLinkBLEKit/CommandSession.swift index 3686f7e50..75e5e0055 100644 --- a/RileyLinkBLEKit/CommandSession.swift +++ b/RileyLinkBLEKit/CommandSession.swift @@ -128,6 +128,23 @@ public struct CommandSession { _ = try writeCommand(command, timeout: 0) } + /// - Throws: RileyLinkDeviceError + public func readRegister(_ address: CC111XRegister) throws -> UInt8 { + guard firmwareVersion.supportsReadRegister else { + throw RileyLinkDeviceError.unsupportedCommand(RileyLinkCommand.readRegister) + } + + let command = ReadRegister(address, firmwareVersion: firmwareVersion) + let response: ReadRegisterResponse = try writeCommand(command, timeout: 0) + + guard response.code == .success else { + throw RileyLinkDeviceError.invalidInput("Unsupported register: \(String(describing: address))") + } + + return response.value + } + + /// - Throws: RileyLinkDeviceError public func enableCCLEDs() throws { let enableBlue = SetLEDMode(.blue, mode: .auto) @@ -142,13 +159,24 @@ public struct CommandSession { public func setBaseFrequency(_ frequency: Measurement) throws { let val = Int( frequency.converted(to: .hertz).value / - (CommandSession.xtalFrequency / pow(2, 16)).converted(to: .hertz - ).value) + (CommandSession.xtalFrequency / pow(2, 16)).converted(to: .hertz).value) try updateRegister(.freq0, value: UInt8(val & 0xff)) try updateRegister(.freq1, value: UInt8((val >> 8) & 0xff)) try updateRegister(.freq2, value: UInt8((val >> 16) & 0xff)) } + + public func readBaseFrequency() throws -> Measurement { + let freq0 = try readRegister(.freq0) + let freq1 = try readRegister(.freq1) + let freq2 = try readRegister(.freq2) + + let value = Double(UInt32(freq0) + (UInt32(freq1) << 8) + (UInt32(freq2) << 16)) + + let frequency = value * (CommandSession.xtalFrequency / pow(2, 16)).converted(to: .hertz).value + + return Measurement(value: frequency, unit: .hertz).converted(to: .megahertz) + } /// Sends data to the pump, listening for a reply /// diff --git a/RileyLinkBLEKit/Info.plist b/RileyLinkBLEKit/Info.plist index f936f8d95..f841d8f2c 100644 --- a/RileyLinkBLEKit/Info.plist +++ b/RileyLinkBLEKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/RileyLinkBLEKit/PeripheralManager+RileyLink.swift b/RileyLinkBLEKit/PeripheralManager+RileyLink.swift index 4873e1d33..f3c671c5b 100644 --- a/RileyLinkBLEKit/PeripheralManager+RileyLink.swift +++ b/RileyLinkBLEKit/PeripheralManager+RileyLink.swift @@ -216,15 +216,16 @@ extension PeripheralManager { let value = try command.writableData() - log.debug("RL Send: %@", value.hexadecimalString) switch responseType { case .single: + log.debug("RL Send (single): %@", value.hexadecimalString) return try writeCommand(value, for: characteristic, timeout: timeout ) case .buffered: + log.debug("RL Send (buffered): %@", value.hexadecimalString) return try writeLegacyCommand(value, for: characteristic, timeout: timeout, @@ -249,7 +250,7 @@ extension PeripheralManager { let value = try command.writableData() - log.debug("RL Send: %@", value.hexadecimalString) + log.debug("RL Send (no response expected): %@", value.hexadecimalString) do { try writeValue(value, for: characteristic, type: .withResponse, timeout: timeout) diff --git a/RileyLinkBLEKit/RadioFirmwareVersion.swift b/RileyLinkBLEKit/RadioFirmwareVersion.swift index 34e959fae..2a1668be6 100644 --- a/RileyLinkBLEKit/RadioFirmwareVersion.swift +++ b/RileyLinkBLEKit/RadioFirmwareVersion.swift @@ -77,13 +77,31 @@ extension RadioFirmwareVersion { } var needsExtraByteForUpdateRegisterCommand: Bool { + // Fixed in 2.2 return !atLeastV2 } - + + var needsExtraByteForReadRegisterCommand: Bool { + // Fixed in 2.3 + guard components.count >= 2 else { + return true; + } + let major = components[0] + let minor = components[1] + return major < 2 || (major == 2 && minor <= 2) + } + var supportsRileyLinkStatistics: Bool { return atLeastV2_2 } + var supportsCustomPreamble: Bool { + return atLeastV2 + } + + var supportsReadRegister: Bool { + return atLeastV2 + } } diff --git a/RileyLinkBLEKit/Response.swift b/RileyLinkBLEKit/Response.swift index 0d576be95..2c5751da3 100644 --- a/RileyLinkBLEKit/Response.swift +++ b/RileyLinkBLEKit/Response.swift @@ -42,6 +42,28 @@ struct CodeResponse: Response { } } +struct ReadRegisterResponse: Response { + let code: ResponseCode + let value: UInt8 + + init?(data: Data) { + guard data.count > 0, let code = ResponseCode(rawValue: data[data.startIndex]) else { + return nil + } + + self.init(code: code, value: data[data.startIndex.advanced(by: 1)]) + } + + init?(legacyData data: Data) { + self.init(code: .success, value: data[0]) + } + + private init?(code: ResponseCode, value: UInt8) { + self.code = code + self.value = value + } +} + struct UpdateRegisterResponse: Response { let code: ResponseCode diff --git a/RileyLinkBLEKit/RileyLinkConnectionManager.swift b/RileyLinkBLEKit/RileyLinkConnectionManager.swift new file mode 100644 index 000000000..661f899fb --- /dev/null +++ b/RileyLinkBLEKit/RileyLinkConnectionManager.swift @@ -0,0 +1,99 @@ +// +// RileyLinkConnectionManager.swift +// RileyLinkBLEKit +// +// Created by Pete Schwamb on 8/16/18. +// Copyright © 2018 Pete Schwamb. All rights reserved. +// + +import Foundation + +public protocol RileyLinkConnectionManagerDelegate : class { + func rileyLinkConnectionManager(_ rileyLinkConnectionManager: RileyLinkConnectionManager, didChange state: RileyLinkConnectionManagerState) +} + +public class RileyLinkConnectionManager { + + public typealias RawStateValue = [String : Any] + + /// The current, serializable state of the manager + public var rawState: RawStateValue { + return state.rawValue + } + + public private(set) var state: RileyLinkConnectionManagerState { + didSet { + delegate?.rileyLinkConnectionManager(self, didChange: state) + } + } + + public var deviceProvider: RileyLinkDeviceProvider { + return rileyLinkDeviceManager + } + + public weak var delegate: RileyLinkConnectionManagerDelegate? + + private let rileyLinkDeviceManager: RileyLinkDeviceManager + + private var autoConnectIDs: Set { + get { + return state.autoConnectIDs + } + set { + state.autoConnectIDs = newValue + } + } + + public init(state: RileyLinkConnectionManagerState) { + self.rileyLinkDeviceManager = RileyLinkDeviceManager(autoConnectIDs: state.autoConnectIDs) + self.state = state + } + + public init(autoConnectIDs: Set) { + self.rileyLinkDeviceManager = RileyLinkDeviceManager(autoConnectIDs: autoConnectIDs) + self.state = RileyLinkConnectionManagerState(autoConnectIDs: autoConnectIDs) + } + + public convenience init?(rawValue: RawStateValue) { + if let state = RileyLinkConnectionManagerState(rawValue: rawValue) { + self.init(state: state) + } else { + return nil + } + } + + public var connectingCount: Int { + return self.autoConnectIDs.count + } + + public func shouldConnect(to deviceID: String) -> Bool { + return self.autoConnectIDs.contains(deviceID) + } + + public func connect(_ device: RileyLinkDevice) { + autoConnectIDs.insert(device.peripheralIdentifier.uuidString) + rileyLinkDeviceManager.connect(device) + } + + public func disconnect(_ device: RileyLinkDevice) { + autoConnectIDs.remove(device.peripheralIdentifier.uuidString) + rileyLinkDeviceManager.disconnect(device) + } + + public func setScanningEnabled(_ enabled: Bool) { + rileyLinkDeviceManager.setScanningEnabled(enabled) + } +} + +public protocol RileyLinkDeviceProvider: class { + func getDevices(_ completion: @escaping (_ devices: [RileyLinkDevice]) -> Void) + var idleListeningEnabled: Bool { get } + var timerTickEnabled: Bool { get set } + func deprioritize(_ device: RileyLinkDevice, completion: (() -> Void)?) + func assertIdleListening(forcingRestart: Bool) + var idleListeningState: RileyLinkDevice.IdleListeningState { get set } + + var debugDescription: String { get } +} + +extension RileyLinkDeviceManager: RileyLinkDeviceProvider {} diff --git a/RileyLinkBLEKit/RileyLinkConnectionManagerState.swift b/RileyLinkBLEKit/RileyLinkConnectionManagerState.swift new file mode 100644 index 000000000..25a22dcfa --- /dev/null +++ b/RileyLinkBLEKit/RileyLinkConnectionManagerState.swift @@ -0,0 +1,39 @@ +// +// RileyLinkConnectionManagerState.swift +// RileyLinkBLEKit +// +// Created by Pete Schwamb on 8/21/18. +// Copyright © 2018 Pete Schwamb. All rights reserved. +// + +import Foundation + +public struct RileyLinkConnectionManagerState: RawRepresentable, Equatable { + + public typealias RawValue = RileyLinkConnectionManager.RawStateValue + + public var autoConnectIDs: Set + + public init(autoConnectIDs: Set) { + self.autoConnectIDs = autoConnectIDs + } + + public init?(rawValue: RileyLinkConnectionManager.RawStateValue) { + guard + let autoConnectIDs = rawValue["autoConnectIDs"] as? [String] + else { + return nil + } + + self.init(autoConnectIDs: Set(autoConnectIDs)) + } + + public var rawValue: RawValue { + return [ + "autoConnectIDs": Array(autoConnectIDs), + ] + } + + + +} diff --git a/RileyLinkBLEKit/RileyLinkDevice.swift b/RileyLinkBLEKit/RileyLinkDevice.swift index e56286c76..6062c96d6 100644 --- a/RileyLinkBLEKit/RileyLinkDevice.swift +++ b/RileyLinkBLEKit/RileyLinkDevice.swift @@ -267,6 +267,7 @@ extension RileyLinkDevice { extension RileyLinkDevice: PeripheralManagerDelegate { // This is called from the central's queue func peripheralManager(_ manager: PeripheralManager, didUpdateValueFor characteristic: CBCharacteristic) { + log.debug("Did UpdateValueFor %@", characteristic) switch MainServiceCharacteristicUUID(rawValue: characteristic.uuid.uuidString) { case .data?: guard let value = characteristic.value, value.count > 0 else { diff --git a/RileyLinkBLEKit/RileyLinkDeviceManager.swift b/RileyLinkBLEKit/RileyLinkDeviceManager.swift index 0480dc25f..e536bc028 100644 --- a/RileyLinkBLEKit/RileyLinkDeviceManager.swift +++ b/RileyLinkBLEKit/RileyLinkDeviceManager.swift @@ -81,6 +81,12 @@ public class RileyLinkDeviceManager: NSObject { // MARK: - Connecting extension RileyLinkDeviceManager { + public func getAutoConnectIDs(_ completion: @escaping (_ autoConnectIDs: Set) -> Void) { + centralQueue.async { + completion(self.autoConnectIDs) + } + } + public func connect(_ device: RileyLinkDevice) { centralQueue.async { self.autoConnectIDs.insert(device.manager.peripheral.identifier.uuidString) @@ -167,7 +173,7 @@ extension RileyLinkDeviceManager { } } - public func deprioritize(_ device: RileyLinkDevice, _ completion: (() -> Void)? = nil) { + public func deprioritize(_ device: RileyLinkDevice, completion: (() -> Void)? = nil) { centralQueue.async { self.devices.deprioritize(device) completion?() @@ -309,3 +315,8 @@ extension RileyLinkDeviceManager { extension Notification.Name { public static let ManagerDevicesDidChange = Notification.Name("com.rileylink.RileyLinkBLEKit.DevicesDidChange") } + +extension RileyLinkDeviceManager { + public static let autoConnectIDsStateKey = "com.rileylink.RileyLinkBLEKit.AutoConnectIDs" +} + diff --git a/RileyLinkBLEKitTests/Info.plist b/RileyLinkBLEKitTests/Info.plist index 12db38e68..499029a0a 100644 --- a/RileyLinkBLEKitTests/Info.plist +++ b/RileyLinkBLEKitTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleVersion 1 diff --git a/RileyLinkKit/Info.plist b/RileyLinkKit/Info.plist index 0de69c100..241ef8699 100644 --- a/RileyLinkKit/Info.plist +++ b/RileyLinkKit/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleSignature ???? CFBundleVersion diff --git a/RileyLinkKit/PumpOpsSession.swift b/RileyLinkKit/PumpOpsSession.swift index 6df0e151b..34b39ad60 100644 --- a/RileyLinkKit/PumpOpsSession.swift +++ b/RileyLinkKit/PumpOpsSession.swift @@ -711,15 +711,6 @@ extension PumpOpsSession { /// - PumpOpsError.unexpectedResponse /// - PumpOpsError.unknownResponse public func changeWatchdogMarriageProfile(_ watchdogID: Data) throws { - try setRXFilterMode(.wide) - defer { - do { - try configureRadio(for: settings.pumpRegion) - } catch { - // Best effort resetting radio filter mode - } - } - let commandTimeout = TimeInterval(seconds: 30) // Wait for the pump to start polling @@ -963,8 +954,11 @@ extension PumpOpsSession { do { pageData = try getHistoryPage(pageNum) - } catch PumpOpsError.pumpError { - break pages + } catch PumpCommandError.arguments(let error) { + if case PumpOpsError.pumpError(.pageDoesNotExist) = error { + return (events, pumpModel) + } + throw PumpCommandError.arguments(error) } var idx = 0 diff --git a/RileyLinkKit/RileyLinkDeviceManager.swift b/RileyLinkKit/RileyLinkDeviceManager.swift index c1de5fa5c..8fafc5b4e 100644 --- a/RileyLinkKit/RileyLinkDeviceManager.swift +++ b/RileyLinkKit/RileyLinkDeviceManager.swift @@ -7,7 +7,7 @@ import RileyLinkBLEKit -extension RileyLinkDeviceManager { +extension RileyLinkDeviceProvider { public func firstConnectedDevice(_ completion: @escaping (_ device: RileyLinkDevice?) -> Void) { getDevices { (devices) in completion(devices.firstConnected) diff --git a/RileyLinkKit/RileyLinkPumpManager.swift b/RileyLinkKit/RileyLinkPumpManager.swift index 9bdd4b661..3eb8b6e25 100644 --- a/RileyLinkKit/RileyLinkPumpManager.swift +++ b/RileyLinkKit/RileyLinkPumpManager.swift @@ -7,33 +7,29 @@ import RileyLinkBLEKit - open class RileyLinkPumpManager { - public init(rileyLinkPumpManagerState: RileyLinkPumpManagerState, rileyLinkManager: RileyLinkDeviceManager? = nil) { - lockedRileyLinkPumpManagerState = Locked(rileyLinkPumpManagerState) - - self.rileyLinkManager = rileyLinkManager ?? RileyLinkDeviceManager(autoConnectIDs: rileyLinkPumpManagerState.connectedPeripheralIDs) - + + public init(rileyLinkDeviceProvider: RileyLinkDeviceProvider, + rileyLinkConnectionManager: RileyLinkConnectionManager? = nil) { + + self.rileyLinkDeviceProvider = rileyLinkDeviceProvider + self.rileyLinkConnectionManager = rileyLinkConnectionManager + // Listen for device notifications NotificationCenter.default.addObserver(self, selector: #selector(receivedRileyLinkPacketNotification(_:)), name: .DevicePacketReceived, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(receivedRileyLinkTimerTickNotification(_:)), name: .DeviceTimerDidTick, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(deviceStateDidChange(_:)), name: .DeviceStateDidChange, object: nil) } - - /// Manages all the RileyLinks - public let rileyLinkManager: RileyLinkDeviceManager - - open var rileyLinkPumpManagerState: RileyLinkPumpManagerState { - get { - return lockedRileyLinkPumpManagerState.value - } - set { - lockedRileyLinkPumpManagerState.value = newValue - } - } - private let lockedRileyLinkPumpManagerState: Locked - - // TODO: Eveluate if this is necessary + + /// Manages all the RileyLinks - access to management is optional + public let rileyLinkConnectionManager: RileyLinkConnectionManager? + + open var rileyLinkConnectionManagerState: RileyLinkConnectionManagerState? + + /// Access to rileylink devices + public let rileyLinkDeviceProvider: RileyLinkDeviceProvider + + // TODO: Evaluate if this is necessary public let queue = DispatchQueue(label: "com.loopkit.RileyLinkPumpManager", qos: .utility) /// Isolated to queue @@ -57,16 +53,15 @@ open class RileyLinkPumpManager { open var debugDescription: String { return [ "## RileyLinkPumpManager", - "rileyLinkPumpManagerState: \(String(reflecting: rileyLinkPumpManagerState))", + "rileyLinkConnectionManager: \(String(reflecting: rileyLinkConnectionManager))", "lastTimerTick: \(String(describing: lastTimerTick))", "deviceStates: \(String(reflecting: deviceStates))", "", - String(reflecting: rileyLinkManager), + String(reflecting: rileyLinkDeviceProvider), ].joined(separator: "\n") } } - // MARK: - RileyLink Updates extension RileyLinkPumpManager { @objc private func deviceStateDidChange(_ note: Notification) { @@ -111,14 +106,22 @@ extension RileyLinkPumpManager { self.deviceTimerDidTick(device) } } - + open func connectToRileyLink(_ device: RileyLinkDevice) { - rileyLinkPumpManagerState.connectedPeripheralIDs.insert(device.peripheralIdentifier.uuidString) - rileyLinkManager.connect(device) + rileyLinkConnectionManager?.connect(device) } open func disconnectFromRileyLink(_ device: RileyLinkDevice) { - rileyLinkPumpManagerState.connectedPeripheralIDs.remove(device.peripheralIdentifier.uuidString) - rileyLinkManager.disconnect(device) + rileyLinkConnectionManager?.disconnect(device) } + } + +// MARK: - RileyLinkConnectionManagerDelegate +extension RileyLinkPumpManager: RileyLinkConnectionManagerDelegate { + public func rileyLinkConnectionManager(_ rileyLinkConnectionManager: RileyLinkConnectionManager, didChange state: RileyLinkConnectionManagerState) { + self.rileyLinkConnectionManagerState = state + } +} + + diff --git a/RileyLinkKit/RileyLinkPumpManagerState.swift b/RileyLinkKit/RileyLinkPumpManagerState.swift deleted file mode 100644 index 95de3b7c5..000000000 --- a/RileyLinkKit/RileyLinkPumpManagerState.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// RileyLinkPumpManagerState.swift -// Loop -// -// Copyright © 2018 LoopKit Authors. All rights reserved. -// - -import Foundation -import LoopKit - - -public struct RileyLinkPumpManagerState: RawRepresentable, Equatable { - public typealias RawValue = PumpManager.RawStateValue - - public var connectedPeripheralIDs: Set - - public init(connectedPeripheralIDs: Set) { - self.connectedPeripheralIDs = connectedPeripheralIDs - } - - public init?(rawValue: RawValue) { - guard let connectedPeripheralIDs = rawValue["connectedPeripheralIDs"] as? [String] else { - return nil - } - - self.init(connectedPeripheralIDs: Set(connectedPeripheralIDs)) - } - - public var rawValue: RawValue { - return [ - "connectedPeripheralIDs": Array(connectedPeripheralIDs) - ] - } -} - - -extension RileyLinkPumpManagerState: CustomDebugStringConvertible { - public var debugDescription: String { - return [ - "## RileyLinkPumpManagerState", - "connectedPeripheralIDs: \(connectedPeripheralIDs)", - ].joined(separator: "\n") - } -} diff --git a/RileyLinkKitTests/Info.plist b/RileyLinkKitTests/Info.plist index 5a6398fbf..9b0436e59 100644 --- a/RileyLinkKitTests/Info.plist +++ b/RileyLinkKitTests/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleSignature ???? CFBundleVersion diff --git a/RileyLinkKitUI/Info.plist b/RileyLinkKitUI/Info.plist index f936f8d95..f841d8f2c 100644 --- a/RileyLinkKitUI/Info.plist +++ b/RileyLinkKitUI/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/RileyLinkKitUI/RileyLinkDeviceTableViewController.swift b/RileyLinkKitUI/RileyLinkDeviceTableViewController.swift index a0489ce39..a4a051f0d 100644 --- a/RileyLinkKitUI/RileyLinkDeviceTableViewController.swift +++ b/RileyLinkKitUI/RileyLinkDeviceTableViewController.swift @@ -30,22 +30,41 @@ public class RileyLinkDeviceTableViewController: UITableViewController { cellForRow(.version)?.detailTextLabel?.text = firmwareVersion } } - - private var lastIdle: Date? { + + private var uptime: TimeInterval? { didSet { guard isViewLoaded else { return } - - cellForRow(.idleStatus)?.setDetailDate(lastIdle, formatter: dateFormatter) + + cellForRow(.uptime)?.setDetailAge(uptime) } } + private var frequency: Measurement? { + didSet { + guard isViewLoaded else { + return + } + + cellForRow(.frequency)?.setDetailFrequency(frequency, formatter: frequencyFormatter) + } + } + var rssiFetchTimer: Timer? { willSet { rssiFetchTimer?.invalidate() } } + + private lazy var frequencyFormatter: MeasurementFormatter = { + let formatter = MeasurementFormatter() + + formatter.numberFormatter = decimalFormatter + + return formatter + }() + private var appeared = false @@ -77,11 +96,36 @@ public class RileyLinkDeviceTableViewController: UITableViewController { func updateDeviceStatus() { device.getStatus { (status) in DispatchQueue.main.async { - self.lastIdle = status.lastIdle self.firmwareVersion = status.firmwareDescription } } } + + func updateUptime() { + device.runSession(withName: "Get stats for uptime") { (session) in + do { + let statistics = try session.getRileyLinkStatistics() + DispatchQueue.main.async { + self.uptime = statistics.uptime + } + } catch { } + } + } + + func updateFrequency() { + + device.runSession(withName: "Get base frequency") { (session) in + do { + let frequency = try session.readBaseFrequency() + DispatchQueue.main.async { + self.frequency = frequency + } + } catch let error { + print("Error: \(error)") + } + } + + } // References to registered notification center observers private var notificationObservers: [Any] = [] @@ -134,6 +178,11 @@ public class RileyLinkDeviceTableViewController: UITableViewController { appeared = true updateRSSI() + + updateFrequency() + + updateUptime() + } public override func viewWillDisappear(_ animated: Bool) { @@ -184,7 +233,8 @@ public class RileyLinkDeviceTableViewController: UITableViewController { case version case rssi case connection - case idleStatus + case uptime + case frequency } private func cellForRow(_ row: DeviceRow) -> UITableViewCell? { @@ -230,11 +280,13 @@ public class RileyLinkDeviceTableViewController: UITableViewController { cell.detailTextLabel?.text = device.peripheralState.description case .rssi: cell.textLabel?.text = LocalizedString("Signal Strength", comment: "The title of the cell showing BLE signal strength (RSSI)") - cell.setDetailRSSI(bleRSSI, formatter: integerFormatter) - case .idleStatus: - cell.textLabel?.text = LocalizedString("On Idle", comment: "The title of the cell showing the last idle") - cell.setDetailDate(lastIdle, formatter: dateFormatter) + case .uptime: + cell.textLabel?.text = NSLocalizedString("Uptime", comment: "The title of the cell showing uptime") + cell.setDetailAge(uptime) + case .frequency: + cell.textLabel?.text = NSLocalizedString("Frequency", comment: "The title of the cell showing current rileylink frequency") + cell.setDetailFrequency(frequency, formatter: frequencyFormatter) } case .commands: cell.accessoryType = .disclosureIndicator @@ -315,6 +367,17 @@ extension RileyLinkDeviceTableViewController: TextFieldTableViewControllerDelega } } +private extension TimeInterval { + func format(using units: NSCalendar.Unit) -> String? { + let formatter = DateComponentsFormatter() + formatter.allowedUnits = units + formatter.unitsStyle = .full + formatter.zeroFormattingBehavior = .dropLeading + formatter.maximumUnitCount = 2 + + return formatter.string(from: self) + } +} private extension UITableViewCell { func setDetailDate(_ date: Date?, formatter: DateFormatter) { @@ -328,18 +391,21 @@ private extension UITableViewCell { func setDetailRSSI(_ decibles: Int?, formatter: NumberFormatter) { detailTextLabel?.text = formatter.decibleString(from: decibles) ?? "-" } - - func setAwakeUntil(_ awakeUntil: Date?, formatter: DateFormatter) { - switch awakeUntil { - case let until? where until.timeIntervalSinceNow < 0: - textLabel?.text = LocalizedString("Last Awake", comment: "The title of the cell describing an awake radio") - setDetailDate(until, formatter: formatter) - case let until?: - textLabel?.text = LocalizedString("Awake Until", comment: "The title of the cell describing an awake radio") - setDetailDate(until, formatter: formatter) - default: - textLabel?.text = LocalizedString("Listening Off", comment: "The title of the cell describing no radio awake data") - detailTextLabel?.text = nil + + func setDetailAge(_ age: TimeInterval?) { + if let age = age { + detailTextLabel?.text = age.format(using: [.day, .hour, .minute]) + } else { + detailTextLabel?.text = "" } } + + func setDetailFrequency(_ frequency: Measurement?, formatter: MeasurementFormatter) { + if let frequency = frequency { + detailTextLabel?.text = formatter.string(from: frequency) + } else { + detailTextLabel?.text = "" + } + } + } diff --git a/RileyLinkKitUI/RileyLinkDevicesTableViewDataSource.swift b/RileyLinkKitUI/RileyLinkDevicesTableViewDataSource.swift index 7c35bbd80..07395ade6 100644 --- a/RileyLinkKitUI/RileyLinkDevicesTableViewDataSource.swift +++ b/RileyLinkKitUI/RileyLinkDevicesTableViewDataSource.swift @@ -23,7 +23,7 @@ public class RileyLinkDevicesTableViewDataSource: NSObject { tableView.register(RileyLinkDevicesHeaderView.self, forHeaderFooterViewReuseIdentifier: RileyLinkDevicesHeaderView.className) // Register for manager notifications - NotificationCenter.default.addObserver(self, selector: #selector(reloadDevices), name: .ManagerDevicesDidChange, object: rileyLinkPumpManager.rileyLinkManager) + NotificationCenter.default.addObserver(self, selector: #selector(reloadDevices), name: .ManagerDevicesDidChange, object: rileyLinkPumpManager.rileyLinkDeviceProvider) // Register for device notifications for name in [.DeviceConnectionStateDidChange, .DeviceRSSIDidChange, .DeviceNameDidChange] as [Notification.Name] { @@ -54,7 +54,7 @@ public class RileyLinkDevicesTableViewDataSource: NSObject { public var isScanningEnabled: Bool = false { didSet { - rileyLinkPumpManager.rileyLinkManager.setScanningEnabled(isScanningEnabled) + rileyLinkPumpManager.rileyLinkConnectionManager?.setScanningEnabled(isScanningEnabled) if isScanningEnabled { rssiFetchTimer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(updateRSSI), userInfo: nil, repeats: true) @@ -89,8 +89,11 @@ public class RileyLinkDevicesTableViewDataSource: NSObject { /// /// - Parameter device: The peripheral /// - Returns: The adjusted connection state - private func preferenceStateForDevice(_ device: RileyLinkDevice) -> CBPeripheralState { - let isAutoConnectDevice = self.rileyLinkPumpManager.rileyLinkPumpManagerState.connectedPeripheralIDs.contains(device.peripheralIdentifier.uuidString) + private func preferenceStateForDevice(_ device: RileyLinkDevice) -> CBPeripheralState? { + guard let connectionManager = rileyLinkPumpManager.rileyLinkConnectionManager else { + return nil + } + let isAutoConnectDevice = connectionManager.shouldConnect(to: device.peripheralIdentifier.uuidString) var state = device.peripheralState switch state { @@ -114,7 +117,7 @@ public class RileyLinkDevicesTableViewDataSource: NSObject { } @objc private func reloadDevices() { - rileyLinkPumpManager.rileyLinkManager.getDevices { (devices) in + rileyLinkPumpManager.rileyLinkDeviceProvider.getDevices { (devices) in DispatchQueue.main.async { self.devices = devices } @@ -169,7 +172,7 @@ extension RileyLinkDevicesTableViewDataSource: UITableViewDataSource { public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let deviceCell = tableView.dequeueReusableCell(withIdentifier: RileyLinkDeviceTableViewCell.className) as! RileyLinkDeviceTableViewCell let device = devices[indexPath.row] - + deviceCell.configureCellWithName( device.name, signal: decimalFormatter.decibleString(from: deviceRSSI[device.peripheralIdentifier]), diff --git a/RileyLinkKitUI/RileyLinkManagerSetupViewController.swift b/RileyLinkKitUI/RileyLinkManagerSetupViewController.swift index fe2529eb1..2f0e1859e 100644 --- a/RileyLinkKitUI/RileyLinkManagerSetupViewController.swift +++ b/RileyLinkKitUI/RileyLinkManagerSetupViewController.swift @@ -21,18 +21,18 @@ open class RileyLinkManagerSetupViewController: UINavigationController, PumpMana open weak var setupDelegate: PumpManagerSetupViewControllerDelegate? - open private(set) var rileyLinkPumpManager: RileyLinkPumpManager? + open var rileyLinkPumpManager: RileyLinkPumpManager? open override func viewDidLoad() { super.viewDidLoad() - + delegate = self } - + open func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { let viewControllers = navigationController.viewControllers let count = navigationController.viewControllers.count - + if count >= 2, let setupViewController = viewControllers[count - 2] as? RileyLinkSetupTableViewController { rileyLinkPumpManager = setupViewController.rileyLinkPumpManager } diff --git a/RileyLinkKitUI/RileyLinkSetupTableViewController.swift b/RileyLinkKitUI/RileyLinkSetupTableViewController.swift index 8c0076622..20c0f34b1 100644 --- a/RileyLinkKitUI/RileyLinkSetupTableViewController.swift +++ b/RileyLinkKitUI/RileyLinkSetupTableViewController.swift @@ -9,11 +9,11 @@ import UIKit import LoopKit import LoopKitUI import RileyLinkKit - +import RileyLinkBLEKit public class RileyLinkSetupTableViewController: SetupTableViewController { - let rileyLinkPumpManager = RileyLinkPumpManager(rileyLinkPumpManagerState: RileyLinkPumpManagerState(connectedPeripheralIDs: [])) + let rileyLinkPumpManager: RileyLinkPumpManager private lazy var devicesDataSource: RileyLinkDevicesTableViewDataSource = { return RileyLinkDevicesTableViewDataSource( @@ -21,6 +21,16 @@ public class RileyLinkSetupTableViewController: SetupTableViewController { devicesSectionIndex: Section.devices.rawValue ) }() + + public required init?(coder aDecoder: NSCoder) { + let rileyLinkConnectionManager = RileyLinkConnectionManager(autoConnectIDs: []) + rileyLinkPumpManager = RileyLinkPumpManager(rileyLinkDeviceProvider: rileyLinkConnectionManager.deviceProvider, rileyLinkConnectionManager: rileyLinkConnectionManager) + + rileyLinkConnectionManager.delegate = rileyLinkPumpManager + + super.init(coder: aDecoder) + } + public override func viewDidLoad() { super.viewDidLoad() @@ -130,7 +140,11 @@ public class RileyLinkSetupTableViewController: SetupTableViewController { // MARK: - Navigation private var shouldContinue: Bool { - return devicesDataSource.rileyLinkPumpManager.rileyLinkPumpManagerState.connectedPeripheralIDs.count > 0 + guard let connectionManager = rileyLinkPumpManager.rileyLinkConnectionManager else { + return false + } + + return connectionManager.connectingCount > 0 } @objc private func deviceConnectionStateDidChange() { diff --git a/RileyLinkTests/RileyLinkTests-Info.plist b/RileyLinkTests/RileyLinkTests-Info.plist index f1cae6f2b..3c83b2ee6 100644 --- a/RileyLinkTests/RileyLinkTests-Info.plist +++ b/RileyLinkTests/RileyLinkTests-Info.plist @@ -13,7 +13,7 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 2.0.3 + 2.0.4 CFBundleSignature ???? CFBundleVersion