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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Loop.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@
C15713821DAC6983005BC4D2 /* MealBolusNightscoutTreatment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15713811DAC6983005BC4D2 /* MealBolusNightscoutTreatment.swift */; };
C17884631D51A7A400405663 /* BatteryIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17884621D51A7A400405663 /* BatteryIndicator.swift */; };
C18C8C511D5A351900E043FB /* NightscoutDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C18C8C501D5A351900E043FB /* NightscoutDataManager.swift */; };
C5C743CC1DDB4817004F63B6 /* BatteryChemistryType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5C743CB1DDB4817004F63B6 /* BatteryChemistryType.swift */; };
C5C743CE1DDB4900004F63B6 /* BatteryTypeSelectionTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5C743CD1DDB4900004F63B6 /* BatteryTypeSelectionTableViewController.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -390,6 +392,8 @@
C15713811DAC6983005BC4D2 /* MealBolusNightscoutTreatment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MealBolusNightscoutTreatment.swift; sourceTree = "<group>"; };
C17884621D51A7A400405663 /* BatteryIndicator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatteryIndicator.swift; sourceTree = "<group>"; };
C18C8C501D5A351900E043FB /* NightscoutDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NightscoutDataManager.swift; sourceTree = "<group>"; };
C5C743CB1DDB4817004F63B6 /* BatteryChemistryType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatteryChemistryType.swift; sourceTree = "<group>"; };
C5C743CD1DDB4900004F63B6 /* BatteryTypeSelectionTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BatteryTypeSelectionTableViewController.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -488,6 +492,7 @@
43EA28611D517E42001BC233 /* SensorDisplayable.swift */,
43C418B41CE0575200405B6A /* ShareGlucose+GlucoseKit.swift */,
4328E0311CFC068900E199AA /* WatchContext+LoopKit.swift */,
C5C743CB1DDB4817004F63B6 /* BatteryChemistryType.swift */,
);
path = Models;
sourceTree = "<group>";
Expand Down Expand Up @@ -665,6 +670,7 @@
43F5C2DA1B92A5E1003EB13D /* SettingsTableViewController.swift */,
43E3449E1B9D68E900C85C07 /* StatusTableViewController.swift */,
4302F4E01D4E9C8900F0FCAF /* TextFieldTableViewController.swift */,
C5C743CD1DDB4900004F63B6 /* BatteryTypeSelectionTableViewController.swift */,
);
path = "View Controllers";
sourceTree = "<group>";
Expand Down Expand Up @@ -1028,6 +1034,7 @@
files = (
434F54571D287FDB002A9274 /* NibLoadable.swift in Sources */,
4315D28A1CA5F45E00589052 /* DiagnosticLogger+LoopKit.swift in Sources */,
C5C743CE1DDB4900004F63B6 /* BatteryTypeSelectionTableViewController.swift in Sources */,
43C418B51CE0575200405B6A /* ShareGlucose+GlucoseKit.swift in Sources */,
430DA58E1D4AEC230097D1CA /* NSBundle.swift in Sources */,
43776F901B8022E90074EA36 /* AppDelegate.swift in Sources */,
Expand Down Expand Up @@ -1095,6 +1102,7 @@
433EA4C21D9F39C900CD78FB /* PumpIDTableViewController.swift in Sources */,
43F78D261C8FC000002152D1 /* DoseMath.swift in Sources */,
438D42F91D7C88BC003244B0 /* PredictionInputEffect.swift in Sources */,
C5C743CC1DDB4817004F63B6 /* BatteryChemistryType.swift in Sources */,
4331E07A1C85650D00FBE832 /* ChartAxisValueDoubleLog.swift in Sources */,
434F54611D28859B002A9274 /* ServiceCredential.swift in Sources */,
436FACEE1D0BA636004E2427 /* InsulinDataSource.swift in Sources */,
Expand Down
14 changes: 14 additions & 0 deletions Loop/Extensions/NSUserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ extension UserDefaults {
case PumpRegion = "com.loopkit.Loop.PumpRegion"
case PumpTimeZone = "com.loudnate.Naterade.PumpTimeZone"
case RetrospectiveCorrectionEnabled = "com.loudnate.Loop.RetrospectiveCorrectionEnabled"
case BatteryChemistry = "com.loopkit.Loop.BatteryChemistry"
}

var basalRateSchedule: BasalRateSchedule? {
Expand Down Expand Up @@ -231,5 +232,18 @@ extension UserDefaults {
set(newValue, forKey: Key.G5TransmitterID.rawValue)
}
}

var batteryChemistry: BatteryChemistryType? {
get {
return BatteryChemistryType(rawValue: integer(forKey: Key.BatteryChemistry.rawValue))
}
set {
if let batteryChemistry = newValue {
set(batteryChemistry.rawValue, forKey: Key.BatteryChemistry.rawValue)
} else {
removeObject(forKey: Key.BatteryChemistry.rawValue)
}
}
}

}
45 changes: 45 additions & 0 deletions Loop/Managers/DeviceDataManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,12 @@ final class DeviceDataManager: CarbStoreDelegate, CarbStoreSyncDelegate, DoseSto
case .success(let (status, date)):
self.updateReservoirVolume(status.reservoir, at: date, withTimeLeft: nil)
let battery = BatteryStatus(voltage: status.batteryVolts, status: BatteryIndicator(batteryStatus: status.batteryStatus))

//Non MySentry Battery Status #141
if let sentrySupported = self.pumpState?.pumpModel?.hasMySentry , !sentrySupported {
self.setBatteryStatusforNonMySentryPumps(currVoltage: status.batteryVolts)
}

nsPumpStatus = NightscoutUploadKit.PumpStatus(clock: date, pumpID: status.pumpID, iob: nil, battery: battery, suspended: status.suspended, bolusing: status.bolusing, reservoir: status.reservoir)
case .failure(let error):
self.troubleshootPumpComms(using: device)
Expand All @@ -364,6 +370,38 @@ final class DeviceDataManager: CarbStoreDelegate, CarbStoreSyncDelegate, DoseSto
}
}

/// NonMySentry Battery Calculation for Alkaline and Lithuim #141
///
/// - parameter currVoltage: Current Voltage Reading from Pump
///
public var x22BatteryPercentRemaining : Double = -1
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we already have a battery % for x23 devices, let's track battery % as a single ivar.

Updating that ivar can trigger both battery change event (AnalyticsManager.sharedManager.pumpBatteryWasReplaced()) and notification (NotificationManager.sendPumpBatteryLowNotification()), and then the StatusTableViewController can check one place for battery percent, instead of checking both dataManager.latestPumpStatusFromMySentry.batteryRemainingPercent and dataManager.x22BatteryPercentRemaining

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use magic number sentinels; use an optional.
Since percentage is derived from chemistry, and chemistry can be changed at any time, this shouldn't be stored at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I understand it you are saying I should write the Battery % Remaining to dataManager.latestPumpStatusFromMySentry.batteryRemainingPercent. I totally agree reuse of the already existing structure would be great, however I am unsure how to write to the batteryRemainingPercent var.

latestPumpStatusFromMySentry?.batteryRemainingPercent = ((currVoltage - minVoltage)/(maxVoltage - minVoltage))

I receive an error

Cannot assign to property: 'batteryRemainingPercent' is a 'let' constant


private func setBatteryStatusforNonMySentryPumps(currVoltage : Double){
var minVoltage : Double
var maxVoltage : Double
var batteryNotification : Double

// if Lithium set min and max linear voltages
if (self.batteryChemistry == .lithium){
minVoltage = 1.32
maxVoltage = 1.58
batteryNotification = 0.12
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point at which we display 0% battery should be equal the point at which a notification should be made, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent of this Notification was to "warn" a user of an impending battery failure or intermittent communications failures due to low battery. This is a 8 to 12 hour warning prior to battery = 0.

https://docs.google.com/spreadsheets/d/19FpO5YO5ewQrdph_4iIXuear6xRGzl-AYTiFeHwPcVk/edit#gid=246762532.

}else{
// if Alkaline (default) set min and max linear voltages
minVoltage = 1.26
maxVoltage = 1.58
batteryNotification = 0.19
}

// Linear EQ ((currVoltage - minVoltage)/(maxVoltage - minVoltage))
self.x22BatteryPercentRemaining = ((currVoltage - minVoltage)/(maxVoltage - minVoltage))

// Notify if <= batteryNotification setpoint
if self.x22BatteryPercentRemaining <= (batteryNotification){
NotificationManager.sendPumpBatteryLowNotification()
}
}

/// Send a bolus command and handle the result
///
/// - parameter units: The number of units to deliver
Expand Down Expand Up @@ -698,6 +736,13 @@ final class DeviceDataManager: CarbStoreDelegate, CarbStoreSyncDelegate, DoseSto
UserDefaults.standard.preferredInsulinDataSource = preferredInsulinDataSource
}
}

/// The Default battery chemistry is Alkaline
var batteryChemistry = UserDefaults.standard.batteryChemistry ?? .alkaline {
didSet {
UserDefaults.standard.batteryChemistry = batteryChemistry
}
}

// MARK: G5 Transmitter

Expand Down
23 changes: 23 additions & 0 deletions Loop/Models/BatteryChemistryType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// BatteryChemistryType.swift
// Loop
//
// Created by Jerermy Lucas on 11/15/16 pattern derived from Nathan Racklyeft.
// Copyright © 2016 LoopKit Authors. All rights reserved.
//

import Foundation

enum BatteryChemistryType: Int, CustomStringConvertible {
case alkaline = 0
case lithium

var description: String {
switch self {
case .alkaline:
return NSLocalizedString("Alkaline", comment: "Describing the battery chemistry as Alkaline")
case .lithium:
return NSLocalizedString("Lithium", comment: "Describing the battery chemistry as Lithium")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// BatteryTypeSelectionTableViewController.swift
// Loop
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was it not possible to re-use RadioSelectionTableViewController?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sure it may be. I am just not swift enough to fully comprehend how. So I leveraged the existing pattern Nate had provided.

//
// Created by Jerermy Lucas on 11/15/16 pattern derived from Nathan Racklyeft.
// Copyright © 2016 LoopKit Authors. All rights reserved.
//

import UIKit


protocol BatteryTypeSelectionTableViewControllerDelegate: class {
func batteryTypeSelectionTableViewControllerDidChangeSelectedIndex(_ controller: BatteryTypeSelectionTableViewController)
}


class BatteryTypeSelectionTableViewController: 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?.batteryTypeSelectionTableViewControllerDidChangeSelectedIndex(self)
}
}
}

var contextHelp: String?

weak var delegate: BatteryTypeSelectionTableViewControllerDelegate?

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 BatteryTypeSelectionTableViewController {
typealias T = BatteryTypeSelectionTableViewController

static func insulinDataSource(_ value: BatteryChemistryType) -> T {
let vc = T()

vc.selectedIndex = value.rawValue
vc.options = (0..<2).flatMap({ BatteryChemistryType(rawValue: $0) }).map { String(describing: $0) }
vc.contextHelp = NSLocalizedString("Alkaline and Lithium batteries decay at differing rates. Alkaline tend to have a linear voltage drop over time whereas lithium cell batteries tend to maintain voltage until the end of their lifespan. Under normal usage in a Non-MySentry compatible Minimed (x22/x15) insulin pump running Loop, Alkaline batteries last approximately 4 to 5 days. Lithium batteries last between 7 and 8 days. This selection will use different battery voltage decay rates for each of the battery chemistry types and alert the user when a battery is approximately 8 to 10 hours from failure.", comment: "Instructions on selecting battery chemistry type")

return vc
}
}
24 changes: 23 additions & 1 deletion Loop/View Controllers/SettingsTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,9 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu
case insulinSensitivity
case maxBasal
case maxBolus
case batteryChemistry

static let count = 10
static let count = 11
}

fileprivate enum ServiceRow: Int {
Expand Down Expand Up @@ -272,6 +273,12 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu
} else {
configCell.detailTextLabel?.text = TapToSetString
}
case .batteryChemistry:
configCell.textLabel?.text = NSLocalizedString("Pump Battery Type", comment: "The title text for the battery type value")
configCell.detailTextLabel?.text = String(describing: dataManager.batteryChemistry)
if let sentrySupported = dataManager.pumpState?.pumpModel?.hasMySentry, sentrySupported {
configCell.isHidden = true
}
}

cell = configCell
Expand Down Expand Up @@ -456,6 +463,12 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu
}
case .receiverEnabled:
break
case .batteryChemistry:
let vc = BatteryTypeSelectionTableViewController.insulinDataSource(dataManager.batteryChemistry)
vc.title = sender?.textLabel?.text
vc.delegate = self

show(vc, sender: sender)
}
case .devices:
let vc = RileyLinkDeviceTableViewController()
Expand Down Expand Up @@ -605,6 +618,15 @@ extension SettingsTableViewController: RadioSelectionTableViewControllerDelegate
}
}

extension SettingsTableViewController: BatteryTypeSelectionTableViewControllerDelegate {
func batteryTypeSelectionTableViewControllerDidChangeSelectedIndex(_ controller: BatteryTypeSelectionTableViewController) {
if let selectedIndex = controller.selectedIndex, let dataSource = BatteryChemistryType(rawValue: selectedIndex) {
dataManager.batteryChemistry = dataSource

tableView.reloadRows(at: [IndexPath(row: ConfigurationRow.batteryChemistry.rawValue, section: Section.configuration.rawValue)], with: .none)
}
}
}

extension SettingsTableViewController: TextFieldTableViewControllerDelegate {
func textFieldTableViewControllerDidEndEditing(_ controller: TextFieldTableViewController) {
Expand Down
5 changes: 4 additions & 1 deletion Loop/View Controllers/StatusTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,10 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
if let capacity = dataManager.pumpState?.pumpModel?.reservoirCapacity {
reservoirVolumeHUD.reservoirLevel = min(1, max(0, Double(reservoir.unitVolume / Double(capacity))))
}


//Non-MySentry Battery Status here we don't have My Sentry (status)
if dataManager.x22BatteryPercentRemaining >= 0 {batteryLevelHUD.batteryLevel = dataManager.x22BatteryPercentRemaining}

reservoirVolumeHUD.setReservoirVolume(volume: reservoir.unitVolume, at: reservoir.startDate)
}

Expand Down