Skip to content

Commit 0d4a107

Browse files
authored
Display sensor state as a binary warning on the glucose HUD (#164)
Fixes #121
1 parent 2d2ec63 commit 0d4a107

File tree

10 files changed

+116
-85
lines changed

10 files changed

+116
-85
lines changed

Loop/Base.lproj/Main.storyboard

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,17 +343,29 @@
343343
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
344344
<nil key="highlightedColor"/>
345345
</label>
346+
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="!" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Nps-jD-4lb">
347+
<rect key="frame" x="102" y="6" width="18" height="18"/>
348+
<constraints>
349+
<constraint firstAttribute="height" constant="18" id="FG3-dI-VYg"/>
350+
<constraint firstAttribute="width" constant="18" id="emP-bu-bja"/>
351+
</constraints>
352+
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption2"/>
353+
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
354+
<nil key="highlightedColor"/>
355+
</label>
346356
</subviews>
347357
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
348358
<accessibility key="accessibilityConfiguration">
349359
<accessibilityTraits key="traits" summaryElement="YES"/>
350360
<bool key="isElement" value="YES"/>
351361
</accessibility>
352362
<constraints>
363+
<constraint firstAttribute="trailing" secondItem="Nps-jD-4lb" secondAttribute="trailing" id="5OT-1d-znT"/>
353364
<constraint firstItem="DKc-Kc-dgR" firstAttribute="top" secondItem="kbE-jN-1cR" secondAttribute="bottom" constant="1" id="9cW-4R-GGV"/>
354365
<constraint firstAttribute="trailing" secondItem="01f-Du-MVi" secondAttribute="trailing" id="EbP-QZ-v2O"/>
355366
<constraint firstItem="DKc-Kc-dgR" firstAttribute="leading" secondItem="hJo-df-An0" secondAttribute="leading" id="LaF-v9-myA"/>
356367
<constraint firstItem="01f-Du-MVi" firstAttribute="leading" secondItem="hJo-df-An0" secondAttribute="leading" id="U9a-jl-mu7"/>
368+
<constraint firstItem="Nps-jD-4lb" firstAttribute="top" secondItem="hJo-df-An0" secondAttribute="top" constant="6" id="Wbe-rN-XLL"/>
357369
<constraint firstItem="01f-Du-MVi" firstAttribute="top" secondItem="hJo-df-An0" secondAttribute="top" constant="8" id="axM-gE-UZC"/>
358370
<constraint firstAttribute="trailing" secondItem="kbE-jN-1cR" secondAttribute="trailing" id="mtr-Xt-FOr"/>
359371
<constraint firstItem="kbE-jN-1cR" firstAttribute="leading" secondItem="hJo-df-An0" secondAttribute="leading" id="nOa-rB-64f"/>
@@ -362,6 +374,7 @@
362374
<constraint firstAttribute="bottom" secondItem="DKc-Kc-dgR" secondAttribute="bottom" id="ygo-QQ-xlW"/>
363375
</constraints>
364376
<connections>
377+
<outlet property="alertLabel" destination="Nps-jD-4lb" id="UQv-FW-Ut3"/>
365378
<outlet property="caption" destination="DKc-Kc-dgR" id="8HE-Yf-fpz"/>
366379
<outlet property="glucoseLabel" destination="01f-Du-MVi" id="XKI-xs-Avm"/>
367380
<outlet property="unitLabel" destination="kbE-jN-1cR" id="xgn-g1-Ovf"/>

Loop/Managers/DeviceDataManager.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter
6262
}
6363

6464
var sensorInfo: SensorDisplayable? {
65-
return latestGlucoseG5 ?? latestGlucoseG4 ?? latestPumpStatusFromMySentry
65+
return latestGlucoseG5 ?? latestGlucoseG4 ?? latestGlucoseFromShare ?? latestPumpStatusFromMySentry
6666
}
6767

6868
// MARK: - RileyLink
@@ -492,6 +492,8 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter
492492

493493
private var latestGlucoseG5: xDripG5.Glucose?
494494

495+
private var latestGlucoseFromShare: ShareGlucose?
496+
495497
/**
496498
Attempts to backfill glucose data from the share servers if a G5 connection hasn't been established.
497499

@@ -519,6 +521,8 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter
519521
return
520522
}
521523

524+
self.latestGlucoseFromShare = glucose.first
525+
522526
// Ignore glucose values that are up to a minute newer than our previous value, to account for possible time shifting in Share data
523527
let newGlucose = glucose.filterDateRange(glucoseStore.latestGlucose?.startDate.dateByAddingTimeInterval(NSTimeInterval(minutes: 1)), nil).map {
524528
return (quantity: $0.quantity, date: $0.startDate, isDisplayOnly: false)
@@ -560,7 +564,7 @@ final class DeviceDataManager: CarbStoreDelegate, DoseStoreDelegate, Transmitter
560564
let includeAfter = glucoseStore.latestGlucose?.startDate.dateByAddingTimeInterval(NSTimeInterval(minutes: 1))
561565

562566
let validGlucose = glucoseHistory.flatMap({
563-
$0.isValid ? $0 : nil
567+
$0.isStateValid ? $0 : nil
564568
}).filterDateRange(includeAfter, nil).map({
565569
(quantity: $0.quantity, date: $0.startDate, isDisplayOnly: $0.isDisplayOnly)
566570
})

Loop/Models/Glucose.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import xDripG5
1111

1212

1313
extension Glucose: SensorDisplayable {
14+
var isStateValid: Bool {
15+
return state == .OK && status == .OK
16+
}
17+
1418
var stateDescription: String {
1519
let status: String
1620
switch self.status {

Loop/Models/GlucoseG4.swift

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,6 @@ import HealthKit
1212
import LoopKit
1313

1414

15-
extension GlucoseG4 {
16-
var isValid: Bool {
17-
return glucose >= 20
18-
}
19-
}
20-
21-
2215
extension GlucoseG4: GlucoseValue {
2316
public var quantity: HKQuantity {
2417
return HKQuantity(unit: HKUnit.milligramsPerDeciliterUnit(), doubleValue: Double(glucose))
@@ -31,11 +24,15 @@ extension GlucoseG4: GlucoseValue {
3124

3225

3326
extension GlucoseG4: SensorDisplayable {
27+
var isStateValid: Bool {
28+
return glucose >= 20
29+
}
30+
3431
var stateDescription: String {
35-
if isValid {
36-
return ""
32+
if isStateValid {
33+
return NSLocalizedString("OK", comment: "Sensor state description for the valid state")
3734
} else {
38-
return String(format: "%02x", glucose)
35+
return NSLocalizedString("Needs Attention", comment: "Sensor state description for the non-valid state")
3936
}
4037
}
4138

Loop/Models/MySentryPumpStatusMessageBody.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,12 @@ import MinimedKit
1111

1212

1313
extension MySentryPumpStatusMessageBody: SensorDisplayable {
14-
var stateDescription: String {
14+
var isStateValid: Bool {
1515
switch glucose {
16-
case .Active:
17-
return ""
18-
case .Off:
19-
return ""
16+
case .Active, .Off:
17+
return true
2018
default:
21-
return String(glucose)
19+
return false
2220
}
2321
}
2422

Loop/Models/SensorDisplayable.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,27 @@
66
// Copyright © 2016 Nathan Racklyeft. All rights reserved.
77
//
88

9+
import Foundation
10+
911

1012
protocol SensorDisplayable {
11-
// Describes the state of the sensor in the current localization
13+
/// Returns whether the current state is valid
14+
var isStateValid: Bool { get }
15+
16+
/// Describes the state of the sensor in the current localization
1217
var stateDescription: String { get }
1318

1419
/// Enumerates the trend of the sensor values
1520
var trendType: GlucoseTrend? { get }
1621
}
22+
23+
24+
extension SensorDisplayable {
25+
var stateDescription: String {
26+
if isStateValid {
27+
return NSLocalizedString("OK", comment: "Sensor state description for the valid state")
28+
} else {
29+
return NSLocalizedString("Needs Attention", comment: "Sensor state description for the non-valid state")
30+
}
31+
}
32+
}

Loop/Models/ShareGlucose+GlucoseKit.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,14 @@ extension ShareGlucose: GlucoseValue {
2121
return HKQuantity(unit: HKUnit.milligramsPerDeciliterUnit(), doubleValue: Double(glucose))
2222
}
2323
}
24+
25+
26+
extension ShareGlucose: SensorDisplayable {
27+
var isStateValid: Bool {
28+
return glucose >= 20
29+
}
30+
31+
var trendType: GlucoseTrend? {
32+
return GlucoseTrend(rawValue: Int(trend))
33+
}
34+
}

Loop/View Controllers/PredictionTableViewController.swift

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
import UIKit
1010
import HealthKit
11+
import LoopKit
12+
1113

1214
class PredictionTableViewController: UITableViewController, IdentifiableClass {
1315

@@ -85,12 +87,6 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass {
8587

8688
var dataManager: DeviceDataManager!
8789

88-
private var active = true {
89-
didSet {
90-
reloadData()
91-
}
92-
}
93-
9490
private lazy var charts: StatusChartsManager = {
9591
let charts = StatusChartsManager()
9692

@@ -102,6 +98,14 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass {
10298
return charts
10399
}()
104100

101+
private var retrospectivePredictedGlucose: [GlucoseValue]?
102+
103+
private var active = true {
104+
didSet {
105+
reloadData()
106+
}
107+
}
108+
105109
private var needsRefresh = true
106110

107111
private var visible = false {
@@ -149,11 +153,12 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass {
149153
}
150154

151155
dispatch_group_enter(reloadGroup)
152-
dataManager.loopManager.getLoopStatus { (predictedGlucose, _, _, _, _, _, error) in
156+
dataManager.loopManager.getLoopStatus { (predictedGlucose, retrospectivePredictedGlucose, _, _, _, _, error) in
153157
if error != nil {
154158
self.needsRefresh = true
155159
}
156160

161+
self.retrospectivePredictedGlucose = retrospectivePredictedGlucose
157162
self.charts.predictedGlucoseValues = predictedGlucose ?? []
158163

159164
dispatch_group_leave(reloadGroup)
@@ -180,7 +185,7 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass {
180185
self.charts.prerender()
181186

182187
self.tableView.reloadSections(NSIndexSet(indexesInRange: NSMakeRange(Section.charts.rawValue, 1)),
183-
withRowAnimation: animated ? .Fade : .None
188+
withRowAnimation: .None
184189
)
185190

186191
self.reloading = false
@@ -242,11 +247,29 @@ class PredictionTableViewController: UITableViewController, IdentifiableClass {
242247
let (input, selected) = selectedInputs[indexPath.row]
243248

244249
cell.titleLabel?.text = input.localizedTitle
245-
cell.subtitleLabel?.text = input.localizedDescription(forGlucoseUnit: charts.glucoseUnit)
246250
cell.accessoryType = selected ? .Checkmark : .None
247-
248251
cell.enabled = input != .retrospection || dataManager.loopManager.retrospectiveCorrectionEnabled
249252

253+
var subtitleText = input.localizedDescription(forGlucoseUnit: charts.glucoseUnit)
254+
255+
if input == .retrospection,
256+
let startGlucose = retrospectivePredictedGlucose?.first,
257+
let endGlucose = retrospectivePredictedGlucose?.last,
258+
let currentGlucose = self.dataManager.glucoseStore?.latestGlucose
259+
{
260+
let formatter = NSNumberFormatter.glucoseFormatter(for: charts.glucoseUnit)
261+
let values = [startGlucose, endGlucose, currentGlucose].map { formatter.stringFromNumber($0.quantity.doubleValueForUnit(charts.glucoseUnit)) ?? "?" }
262+
263+
let retro = String(
264+
format: NSLocalizedString("Last comparison: %1$@ → %2$@ vs %3$@", comment: "Format string describing retrospective glucose prediction comparison. (1: Previous glucose)(2: Predicted glucose)(3: Actual glucose)"),
265+
values[0], values[1], values[2]
266+
)
267+
268+
subtitleText = String(format: "%@\n%@", subtitleText, retro)
269+
}
270+
271+
cell.subtitleLabel?.text = subtitleText
272+
250273
cell.contentView.layoutMargins.left = tableView.separatorInset.left
251274

252275
return cell

Loop/View Controllers/StatusTableViewController.swift

Lines changed: 2 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,6 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
136136
needsRefresh = false
137137
reloading = true
138138

139-
tableView.reloadSections(NSIndexSet(indexesInRange: NSMakeRange(Section.Sensor.rawValue, Section.count - Section.Sensor.rawValue)
140-
), withRowAnimation: visible ? .Automatic : .None)
141-
142139
let calendar = NSCalendar.currentCalendar()
143140
let components = NSDateComponents()
144141
components.minute = 0
@@ -265,9 +262,8 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
265262
private enum Section: Int {
266263
case Charts = 0
267264
case Status
268-
case Sensor
269265

270-
static let count = 3
266+
static let count = 2
271267
}
272268

273269
// MARK: - Chart Section Data
@@ -367,32 +363,10 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
367363

368364
// MARK: - Pump/Sensor Section Data
369365

370-
private enum SensorRow: Int {
371-
case State
372-
373-
static let count = 1
374-
}
375-
376366
private lazy var emptyValueString: String = NSLocalizedString("––",
377367
comment: "The detail value of a numeric cell with no value"
378368
)
379369

380-
private lazy var dateComponentsFormatter: NSDateComponentsFormatter = {
381-
let formatter = NSDateComponentsFormatter()
382-
formatter.unitsStyle = .Short
383-
384-
return formatter
385-
}()
386-
387-
private lazy var numberFormatter = NSNumberFormatter()
388-
389-
private lazy var dateFormatter: NSDateFormatter = {
390-
let formatter = NSDateFormatter()
391-
formatter.dateStyle = .MediumStyle
392-
formatter.timeStyle = .MediumStyle
393-
return formatter
394-
}()
395-
396370
private lazy var timeFormatter: NSDateFormatter = {
397371
let formatter = NSDateFormatter()
398372
formatter.dateStyle = .NoStyle
@@ -413,8 +387,6 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
413387
return ChartRow.count
414388
case .Status:
415389
return StatusRow.count
416-
case .Sensor:
417-
return SensorRow.count
418390
}
419391
}
420392

@@ -483,18 +455,6 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
483455
}
484456
}
485457

486-
return cell
487-
case .Sensor:
488-
let cell = tableView.dequeueReusableCellWithIdentifier(UITableViewCell.className, forIndexPath: indexPath)
489-
cell.selectionStyle = .None
490-
491-
switch SensorRow(rawValue: indexPath.row)! {
492-
case .State:
493-
cell.textLabel?.text = NSLocalizedString("Sensor State", comment: "The title of the cell containing the current sensor state")
494-
495-
cell.detailTextLabel?.text = dataManager.sensorInfo?.stateDescription ?? emptyValueString
496-
}
497-
498458
return cell
499459
}
500460
}
@@ -510,7 +470,7 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
510470
case .IOB, .Dose, .COB:
511471
return 85
512472
}
513-
case .Status, .Sensor:
473+
case .Status:
514474
return UITableViewAutomaticDimension
515475
}
516476
}
@@ -548,10 +508,6 @@ final class StatusTableViewController: UITableViewController, UIGestureRecognize
548508
}
549509
}
550510
}
551-
case .Sensor:
552-
if let URL = NSURL(string: "dexcomcgm://") {
553-
UIApplication.sharedApplication().openURL(URL)
554-
}
555511
}
556512
}
557513

0 commit comments

Comments
 (0)