diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index 45963db5fa..3501d13642 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -48,6 +48,8 @@ 435400351C9F878D00D5819C /* SetBolusUserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435400331C9F878D00D5819C /* SetBolusUserInfo.swift */; }; 4354003A1C9FB81100D5819C /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43DE92501C541832001FFDE1 /* UIColor.swift */; }; 43649A631C7A347F00523D7F /* CollectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43649A621C7A347F00523D7F /* CollectionType.swift */; }; + 436FACEC1D0BA246004E2427 /* SegmentedControlTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 436FACEB1D0BA246004E2427 /* SegmentedControlTableViewCell.swift */; }; + 436FACEE1D0BA636004E2427 /* InsulinDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 436FACED1D0BA636004E2427 /* InsulinDataSource.swift */; }; 43776F901B8022E90074EA36 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43776F8F1B8022E90074EA36 /* AppDelegate.swift */; }; 43776F971B8022E90074EA36 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 43776F951B8022E90074EA36 /* Main.storyboard */; }; 43776F991B8022E90074EA36 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 43776F981B8022E90074EA36 /* Assets.xcassets */; }; @@ -232,6 +234,8 @@ 435400301C9F744E00D5819C /* BolusSuggestionUserInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BolusSuggestionUserInfo.swift; sourceTree = ""; }; 435400331C9F878D00D5819C /* SetBolusUserInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SetBolusUserInfo.swift; sourceTree = ""; }; 43649A621C7A347F00523D7F /* CollectionType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionType.swift; sourceTree = ""; }; + 436FACEB1D0BA246004E2427 /* SegmentedControlTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SegmentedControlTableViewCell.swift; sourceTree = ""; }; + 436FACED1D0BA636004E2427 /* InsulinDataSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InsulinDataSource.swift; sourceTree = ""; }; 43776F8C1B8022E90074EA36 /* Loop.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Loop.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43776F8F1B8022E90074EA36 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 43776F961B8022E90074EA36 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; @@ -397,6 +401,7 @@ 43DE92601C555C26001FFDE1 /* AbsorptionTimeType+CarbKit.swift */, 4331E0791C85650D00FBE832 /* ChartAxisValueDoubleLog.swift */, 434F24CA1CFCB7AB0004498F /* GlucoseRxMessage.swift */, + 436FACED1D0BA636004E2427 /* InsulinDataSource.swift */, 43C418B41CE0575200405B6A /* ShareGlucose+GlucoseKit.swift */, 434F24C81CFCA8940004498F /* TransmitterGlucose.swift */, 4328E0311CFC068900E199AA /* WatchContext+LoopKit.swift */, @@ -544,6 +549,7 @@ 437CEEBD1CD6E0CB003C8C80 /* LoopCompletionHUDView.swift */, 438DADC71CDE8F8B007697A5 /* LoopStateView.swift */, 437CEEC71CD84CBB003C8C80 /* ReservoirVolumeHUDView.swift */, + 436FACEB1D0BA246004E2427 /* SegmentedControlTableViewCell.swift */, 43EB40C11C83F84900472A8C /* StatusChartHighlightLayer.swift */, 43A5676A1C96155700334FAC /* SwitchTableViewCell.swift */, ); @@ -874,6 +880,7 @@ 43DBF0531C93EC8200B3C386 /* DeviceDataManager.swift in Sources */, 4346D1E71C77F5FE00ABAFE3 /* ChartTableViewCell.swift in Sources */, 437CEEE41CDE5C0A003C8C80 /* UIImage.swift in Sources */, + 436FACEC1D0BA246004E2427 /* SegmentedControlTableViewCell.swift in Sources */, 43DBF0591C93F73800B3C386 /* CarbEntryTableViewController.swift in Sources */, 43DE925C1C547A20001FFDE1 /* WatchContext.swift in Sources */, 43EB40861C82646A00472A8C /* StatusChartManager.swift in Sources */, @@ -896,6 +903,7 @@ 437CEECA1CD84DB7003C8C80 /* BatteryLevelHUDView.swift in Sources */, 43F78D261C8FC000002152D1 /* DoseMath.swift in Sources */, 4331E07A1C85650D00FBE832 /* ChartAxisValueDoubleLog.swift in Sources */, + 436FACEE1D0BA636004E2427 /* InsulinDataSource.swift in Sources */, 439897371CD2F80600223065 /* AnalyticsManager.swift in Sources */, 4346D1F61C78501000ABAFE3 /* ChartPoint.swift in Sources */, 43F4EF1D1BA2A57600526CE1 /* DiagnosticLogger.swift in Sources */, diff --git a/Loop/Base.lproj/Main.storyboard b/Loop/Base.lproj/Main.storyboard index 26cd8a55c5..f32b92e2e0 100644 --- a/Loop/Base.lproj/Main.storyboard +++ b/Loop/Base.lproj/Main.storyboard @@ -10,13 +10,13 @@ - + - + @@ -65,11 +65,46 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -138,7 +173,7 @@ - + @@ -150,7 +185,7 @@ - + - + @@ -188,7 +223,7 @@ - + - + @@ -229,7 +264,7 @@ - + - + - + @@ -358,7 +393,7 @@ - + @@ -366,7 +401,7 @@ - + @@ -374,7 +409,7 @@ - + @@ -398,7 +433,7 @@ - + @@ -413,35 +448,36 @@ - - - - - - - - + + + - - - - + + + + + + + @@ -452,37 +488,38 @@ - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - + + + + + + + diff --git a/Loop/Extensions/NSUserDefaults.swift b/Loop/Extensions/NSUserDefaults.swift index c741c659d2..3bb2c02186 100644 --- a/Loop/Extensions/NSUserDefaults.swift +++ b/Loop/Extensions/NSUserDefaults.swift @@ -22,6 +22,7 @@ extension NSUserDefaults { case GlucoseTargetRangeSchedule = "com.loudnate.Naterade.GlucoseTargetRangeSchedule" case MaximumBasalRatePerHour = "com.loudnate.Naterade.MaximumBasalRatePerHour" case MaximumBolus = "com.loudnate.Naterade.MaximumBolus" + case PreferredInsulinDataSource = "com.loudnate.Loop.PreferredInsulinDataSource" case PumpID = "com.loudnate.Naterade.PumpID" case PumpModelNumber = "com.loudnate.Naterade.PumpModelNumber" case PumpTimeZone = "com.loudnate.Naterade.PumpTimeZone" @@ -144,6 +145,19 @@ extension NSUserDefaults { } } + var preferredInsulinDataSource: InsulinDataSource? { + get { + return InsulinDataSource(rawValue: integerForKey(Key.PreferredInsulinDataSource.rawValue)) + } + set { + if let preferredInsulinDataSource = preferredInsulinDataSource { + setInteger(preferredInsulinDataSource.rawValue, forKey: Key.PreferredInsulinDataSource.rawValue) + } else { + removeObjectForKey(Key.PreferredInsulinDataSource.rawValue) + } + } + } + var pumpID: String? { get { return stringForKey(Key.PumpID.rawValue) diff --git a/Loop/Managers/DeviceDataManager.swift b/Loop/Managers/DeviceDataManager.swift index dea2718199..90ed8278b0 100644 --- a/Loop/Managers/DeviceDataManager.swift +++ b/Loop/Managers/DeviceDataManager.swift @@ -30,10 +30,6 @@ class DeviceDataManager: CarbStoreDelegate, TransmitterDelegate { /// Notification posted by the instance when new pump data was processed static let PumpStatusUpdatedNotification = "com.loudnate.Naterade.notification.PumpStatusUpdated" - enum Error: ErrorType { - case ValueError(String) - } - // MARK: - Utilities lazy var logger = DiagnosticLogger() @@ -60,6 +56,13 @@ class DeviceDataManager: CarbStoreDelegate, TransmitterDelegate { NSNotificationCenter.defaultCenter().postNotificationName(note.name, object: self, userInfo: note.userInfo) } + /** + 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: NSNotification) { if let device = note.object as? RileyLinkDevice, @@ -320,17 +323,7 @@ class DeviceDataManager: CarbStoreDelegate, TransmitterDelegate { // MARK: - Configuration - private var transmitterState: State = .NeedsConfiguration { - didSet { - switch transmitterState { - case .Ready(let transmitter): - transmitter.delegate = self - rileyLinkManager.timerTickEnabled = false - case .NeedsConfiguration: - rileyLinkManager.timerTickEnabled = true - } - } - } + // MARK: Pump private var connectedPeripheralIDs: Set = Set(NSUserDefaults.standardUserDefaults().connectedPeripheralIDs) { didSet { @@ -414,6 +407,27 @@ class DeviceDataManager: CarbStoreDelegate, TransmitterDelegate { } } + /// The user's preferred method of fetching insulin data from the pump + var preferredInsulinDataSource = NSUserDefaults.standardUserDefaults().preferredInsulinDataSource ?? .PumpHistory { + didSet { + NSUserDefaults.standardUserDefaults().preferredInsulinDataSource = preferredInsulinDataSource + } + } + + // MARK: G5 Transmitter + + private var transmitterState: State = .NeedsConfiguration { + didSet { + switch transmitterState { + case .Ready(let transmitter): + transmitter.delegate = self + rileyLinkManager.timerTickEnabled = false + case .NeedsConfiguration: + rileyLinkManager.timerTickEnabled = true + } + } + } + var transmitterID: String? { didSet { if transmitterID?.characters.count != 6 { @@ -440,6 +454,8 @@ class DeviceDataManager: CarbStoreDelegate, TransmitterDelegate { } } + // MARK: Loop model inputs + var basalRateSchedule: BasalRateSchedule? = NSUserDefaults.standardUserDefaults().basalRateSchedule { didSet { doseStore.basalProfile = basalRateSchedule diff --git a/Loop/Managers/WatchDataManager.swift b/Loop/Managers/WatchDataManager.swift index 840a1e161e..ffae6e2c40 100644 --- a/Loop/Managers/WatchDataManager.swift +++ b/Loop/Managers/WatchDataManager.swift @@ -117,7 +117,7 @@ class WatchDataManager: NSObject, WCSessionDelegate { } } - private func addCarbEntryFromWatchMessage(message: [String: AnyObject], completionHandler: ((units: Double?, error: ErrorType?) -> Void)? = nil) { + private func addCarbEntryFromWatchMessage(message: [String: AnyObject], completionHandler: ((units: Double?) -> Void)? = nil) { if let carbStore = deviceDataManager.carbStore, carbEntry = CarbEntryUserInfo(rawValue: message) { let newEntry = NewCarbEntry( quantity: HKQuantity(unit: carbStore.preferredUnit, doubleValue: carbEntry.value), @@ -133,10 +133,10 @@ class WatchDataManager: NSObject, WCSessionDelegate { AnalyticsManager.didAddCarbsFromWatch(carbEntry.value) } - completionHandler?(units: units, error: error) + completionHandler?(units: units) } } else { - completionHandler?(units: nil, error: DeviceDataManager.Error.ValueError("Unable to parse CarbEntryUserInfo: \(message)")) + completionHandler?(units: nil) } } @@ -145,7 +145,7 @@ class WatchDataManager: NSObject, WCSessionDelegate { func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String: AnyObject]) -> Void) { switch message["name"] as? String { case CarbEntryUserInfo.name?: - addCarbEntryFromWatchMessage(message) { (units, error) in + addCarbEntryFromWatchMessage(message) { (units) in replyHandler(BolusSuggestionUserInfo(recommendedBolus: units ?? 0).rawValue) } case SetBolusUserInfo.name?: diff --git a/Loop/Models/InsulinDataSource.swift b/Loop/Models/InsulinDataSource.swift new file mode 100644 index 0000000000..f15d649036 --- /dev/null +++ b/Loop/Models/InsulinDataSource.swift @@ -0,0 +1,13 @@ +// +// InsulinDataSource.swift +// Loop +// +// Created by Nathan Racklyeft on 6/10/16. +// Copyright © 2016 Nathan Racklyeft. All rights reserved. +// + + +enum InsulinDataSource: Int { + case PumpHistory + case Reservoir +} diff --git a/Loop/View Controllers/SettingsTableViewController.swift b/Loop/View Controllers/SettingsTableViewController.swift index 967b4e93ac..37f3b37549 100644 --- a/Loop/View Controllers/SettingsTableViewController.swift +++ b/Loop/View Controllers/SettingsTableViewController.swift @@ -91,8 +91,9 @@ class SettingsTableViewController: UITableViewController, DailyValueScheduleTabl private enum LoopRow: Int { case Dosing = 0 + case PreferredInsulinDataSource - static let count = 1 + static let count = 2 } private enum ConfigurationRow: Int { @@ -151,6 +152,14 @@ class SettingsTableViewController: UITableViewController, DailyValueScheduleTabl switchCell.`switch`?.addTarget(self, action: #selector(dosingEnabledChanged(_:)), forControlEvents: .ValueChanged) return switchCell + case .PreferredInsulinDataSource: + let segmentCell = tableView.dequeueReusableCellWithIdentifier(SegmentedControlTableViewCell.className, forIndexPath: indexPath) as! SegmentedControlTableViewCell + + segmentCell.titleLabel.text = NSLocalizedString("Insulin Data Source", comment: "The title text for the preferred insulin data source config") + segmentCell.segmentedControl.selectedSegmentIndex = dataManager.preferredInsulinDataSource.rawValue + segmentCell.segmentedControl.addTarget(self, action: #selector(preferredInsulinDataSourceChanged(_:)), forControlEvents: .ValueChanged) + + return segmentCell } case .Configuration: let configCell = tableView.dequeueReusableCellWithIdentifier(ConfigCellIdentifier, forIndexPath: indexPath) @@ -436,6 +445,12 @@ class SettingsTableViewController: UITableViewController, DailyValueScheduleTabl } } + func preferredInsulinDataSourceChanged(sender: UISegmentedControl) { + if let dataSource = InsulinDataSource(rawValue: sender.selectedSegmentIndex) { + dataManager.preferredInsulinDataSource = dataSource + } + } + // MARK: - TextFieldTableViewControllerDelegate func textFieldTableViewControllerDidEndEditing(controller: TextFieldTableViewController) { diff --git a/Loop/Views/SegmentedControlTableViewCell.swift b/Loop/Views/SegmentedControlTableViewCell.swift new file mode 100644 index 0000000000..f679619041 --- /dev/null +++ b/Loop/Views/SegmentedControlTableViewCell.swift @@ -0,0 +1,29 @@ +// +// SegmentedControlTableViewCell.swift +// Loop +// +// Created by Nathan Racklyeft on 6/10/16. +// Copyright © 2016 Nathan Racklyeft. All rights reserved. +// + +import UIKit + +class SegmentedControlTableViewCell: UITableViewCell { + + @IBOutlet weak var titleLabel: UILabel! + + @IBOutlet weak var segmentedControl: UISegmentedControl! + + override func awakeFromNib() { + super.awakeFromNib() + + segmentedControl.selectedSegmentIndex = 0 + } + + override func prepareForReuse() { + super.prepareForReuse() + + segmentedControl.removeTarget(nil, action: nil, forControlEvents: .ValueChanged) + } + +}