Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
93111d5
Open the app when the user taps on the widget
Dec 23, 2016
bdb02ab
Use MAIN_APP_BUNDLE_IDENTIFIER instead of hardcoding "Loop" as the UR…
Dec 23, 2016
82dc3da
Improve string concatenation style
Dec 23, 2016
67f0c3d
Merge pull request #334 from bharat/tap-opens-app
ps2 Dec 25, 2016
21ace16
Fix widget battery level for x22 pumps
wkcwells Dec 27, 2016
378b525
Fix broken build.
Dec 29, 2016
51b09c1
Merge pull request #346 from bharat/fix-build
ps2 Dec 30, 2016
2058195
Merge pull request #342 from wkcwells/fix-battery-in-widget
ps2 Dec 30, 2016
0b7afad
Merge pull request #340 from LoopKit/update-rl-frameworks
ps2 Dec 30, 2016
a3558d4
Refactor HUD views into LoopUI framework (#284) (#330)
Jan 1, 2017
d540cce
Monitor changes and update the widget in real time. (#335)
Jan 2, 2017
311ba44
Use a clear fill color for the circle. (#355)
Jan 2, 2017
9566eac
Refactor files with membership in multiple targets into a Common hier…
Jan 2, 2017
9d3cfad
Disable animation on first render, since we'd be animating from a (#354)
Jan 2, 2017
9d9890b
Enforce a fixed number of fractional digits per unit type. (#356)
Jan 5, 2017
7e221ee
Add an info.plist entry to widget info.plist to make tapping on widge…
wkcwells Jan 5, 2017
aac86be
Only allow one active glucose data source (#364)
Feb 11, 2017
f1e4342
Upgrade project to 0820 (#376)
Feb 13, 2017
c262b64
Merge complications changes back into dev (they were accidentally mer…
ps2 Feb 16, 2017
a0b425b
Merge branch 'master' into dev
ps2 Feb 16, 2017
bb7cb32
Merge branch 'master' into dev
ps2 Feb 16, 2017
1775f42
Tmecklem x22 enlite cgm (#378)
ps2 Feb 19, 2017
3ba50da
remove +ve momentum from bolus calculation (#372)
thebookins Feb 19, 2017
08591de
Minimum bg guard (#365)
ps2 Feb 20, 2017
841a366
Update LoopKit and Rileylink frameworks
ps2 Feb 20, 2017
3ae1077
Merge pull request #380 from LoopKit/update-frameworks
ps2 Feb 20, 2017
93da3d4
Bump version
ps2 Feb 20, 2017
59ce749
Merge pull request #381 from LoopKit/bump-version
ps2 Feb 20, 2017
27017e6
Show bg values in warning messages in bolus ui
ps2 Feb 24, 2017
cb394ca
Do not show last loop errors in bolus view; only recommendation alerts
ps2 Feb 24, 2017
2b6e739
Show last loop error on clicking loop status
ps2 Feb 24, 2017
32764bc
Use tap gesture recognizer instead of delegate
ps2 Feb 25, 2017
6ccc578
remove extraneous line
ps2 Feb 25, 2017
ef9f943
Add recovery text for configuration alerts, and single out minimum bg…
ps2 Feb 25, 2017
1a47658
Merge pull request #384 from LoopKit/last-loop-error
ps2 Feb 25, 2017
c92e780
Merge pull request #382 from LoopKit/bolus-ui-updates
ps2 Feb 25, 2017
d3e2a6f
update to new rl framework
ps2 Mar 2, 2017
e8b207e
Add mmol watchapp units support. Add modularLarge complication. Chang…
walker0 Mar 2, 2017
630bb96
Complications update (#389)
ps2 Mar 2, 2017
5ea6f8d
Last loop completed and battery level restored from userDefaults on a…
ps2 Mar 6, 2017
6a993c6
Merge pull request #391 from LoopKit/status-extension-refactor
ps2 Mar 6, 2017
f82acbe
Fixing truncated utilitarian small complication
ps2 Mar 6, 2017
514ce93
Merge pull request #392 from LoopKit/complications
ps2 Mar 6, 2017
5bd4f11
Refactoring charting code to remove unnecessary work and unnecessary …
ps2 Mar 6, 2017
05560b0
Update to rileylink frameworks v1.0
ps2 Mar 9, 2017
8bde18c
Remove development team ids
ps2 Mar 9, 2017
884819a
Merge pull request #393 from LoopKit/charts-refactor
ps2 Mar 10, 2017
49bbd40
Merge pull request #394 from LoopKit/rl-update
ps2 Mar 10, 2017
2297c54
MySentry off should report as non valid sensor info
ps2 Mar 12, 2017
3873fdc
Merge pull request #401 from LoopKit/sensorinfo
ps2 Mar 12, 2017
8988d22
prep version for release
ps2 Mar 13, 2017
e3efa6d
Merge pull request #402 from LoopKit/version-bump
ps2 Mar 13, 2017
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
Prev Previous commit
Next Next commit
Tmecklem x22 enlite cgm (#378)
* Fetch cgm data from x22 pump

* Add fetch glucose flag

* Add SensorDisplayable wrapper for pump glucose history events

* Restructure glucose history handling

* Rename functions to Enlite to improve clarity

Group Enlite functions and data together

* Add pumpDataIsStale, move enlite fetch into timer tick

Rename user default key

* Keep stale glucose check to avoid fetching enlite pages too often
  • Loading branch information
ps2 authored Feb 19, 2017
commit 1775f42f071e79e8e6709a537707414f6e8966e2
4 changes: 4 additions & 0 deletions Loop.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@
4FF4D0F91E17268800846527 /* IdentifiableClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 434FF1E91CF26C29000DB779 /* IdentifiableClass.swift */; };
4FF4D1001E18374700846527 /* WatchContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF4D0FF1E18374700846527 /* WatchContext.swift */; };
4FF4D1011E18375000846527 /* WatchContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FF4D0FF1E18374700846527 /* WatchContext.swift */; };
540DED971E14C75F002B2491 /* EnliteSensorDisplayable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 540DED961E14C75F002B2491 /* EnliteSensorDisplayable.swift */; };
C10428971D17BAD400DD539A /* NightscoutUploadKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C10428961D17BAD400DD539A /* NightscoutUploadKit.framework */; };
C12F21A71DFA79CB00748193 /* recommend_tamp_basal_very_low_end_in_range.json in Resources */ = {isa = PBXBuildFile; fileRef = C12F21A61DFA79CB00748193 /* recommend_tamp_basal_very_low_end_in_range.json */; };
C15713821DAC6983005BC4D2 /* MealBolusNightscoutTreatment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15713811DAC6983005BC4D2 /* MealBolusNightscoutTreatment.swift */; };
Expand Down Expand Up @@ -471,6 +472,7 @@
4F75288E1DFE1DC600C322D6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
4FC8C8001DEB93E400A1452E /* NSUserDefaults+StatusExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSUserDefaults+StatusExtension.swift"; sourceTree = "<group>"; };
4FF4D0FF1E18374700846527 /* WatchContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WatchContext.swift; sourceTree = "<group>"; };
540DED961E14C75F002B2491 /* EnliteSensorDisplayable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnliteSensorDisplayable.swift; sourceTree = "<group>"; };
C10428961D17BAD400DD539A /* NightscoutUploadKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NightscoutUploadKit.framework; path = Carthage/Build/iOS/NightscoutUploadKit.framework; sourceTree = "<group>"; };
C12F21A61DFA79CB00748193 /* recommend_tamp_basal_very_low_end_in_range.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = recommend_tamp_basal_very_low_end_in_range.json; sourceTree = "<group>"; };
C15713811DAC6983005BC4D2 /* MealBolusNightscoutTreatment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MealBolusNightscoutTreatment.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -595,6 +597,7 @@
438D42F81D7C88BC003244B0 /* PredictionInputEffect.swift */,
43C418B41CE0575200405B6A /* ShareGlucose+GlucoseKit.swift */,
4328E0311CFC068900E199AA /* WatchContext+LoopKit.swift */,
540DED961E14C75F002B2491 /* EnliteSensorDisplayable.swift */,
);
path = Models;
sourceTree = "<group>";
Expand Down Expand Up @@ -1356,6 +1359,7 @@
4328E0331CFC091100E199AA /* WatchContext+LoopKit.swift in Sources */,
4F526D611DF8D9A900A04910 /* NetBasal.swift in Sources */,
4398973B1CD2FC2000223065 /* NSDateFormatter.swift in Sources */,
540DED971E14C75F002B2491 /* EnliteSensorDisplayable.swift in Sources */,
436A0DA51D236A2A00104B24 /* LoopError.swift in Sources */,
43E2D8C61D204678004DA55F /* KeychainManager.swift in Sources */,
433EA4C21D9F39C900CD78FB /* PumpIDTableViewController.swift in Sources */,
Expand Down
10 changes: 10 additions & 0 deletions Loop/Extensions/NSUserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ extension UserDefaults {
case MaximumBasalRatePerHour = "com.loudnate.Naterade.MaximumBasalRatePerHour"
case MaximumBolus = "com.loudnate.Naterade.MaximumBolus"
case PreferredInsulinDataSource = "com.loudnate.Loop.PreferredInsulinDataSource"
case FetchEnliteDataEnabled = "com.loopkit.Loop.FetchEnliteDataEnabled"
case PumpID = "com.loudnate.Naterade.PumpID"
case PumpModelNumber = "com.loudnate.Naterade.PumpModelNumber"
case PumpRegion = "com.loopkit.Loop.PumpRegion"
Expand Down Expand Up @@ -215,6 +216,15 @@ extension UserDefaults {
}
}

var fetchEnliteDataEnabled: Bool {
get {
return bool(forKey: Key.FetchEnliteDataEnabled.rawValue)
}
set {
set(newValue, forKey: Key.FetchEnliteDataEnabled.rawValue)
}
}

var retrospectiveCorrectionEnabled: Bool {
get {
return bool(forKey: Key.RetrospectiveCorrectionEnabled.rawValue)
Expand Down
121 changes: 97 additions & 24 deletions Loop/Managers/DeviceDataManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,17 @@ final class DeviceDataManager: CarbStoreDelegate, CarbStoreSyncDelegate, DoseSto
}
}

var fetchEnliteDataEnabled: Bool {
get {
return UserDefaults.standard.fetchEnliteDataEnabled
}
set {
UserDefaults.standard.fetchEnliteDataEnabled = newValue
}
}

var sensorInfo: SensorDisplayable? {
return latestGlucoseG5 ?? latestGlucoseG4 ?? latestGlucoseFromShare ?? latestPumpStatusFromMySentry
return latestGlucoseG5 ?? latestGlucoseG4 ?? latestGlucoseFromShare ?? latestPumpStatusFromMySentry ?? latestEnliteData
}

var latestPumpStatus: RileyLinkKit.PumpStatus?
Expand Down Expand Up @@ -135,6 +144,10 @@ final class DeviceDataManager: CarbStoreDelegate, CarbStoreSyncDelegate, DoseSto

@objc private func receivedRileyLinkTimerTickNotification(_ note: Notification) {
backfillGlucoseFromShareIfNeeded() {
if UserDefaults.standard.fetchEnliteDataEnabled {
self.assertCurrentEnliteData()
}

self.assertCurrentPumpData()
}
}
Expand Down Expand Up @@ -365,42 +378,43 @@ final class DeviceDataManager: CarbStoreDelegate, CarbStoreSyncDelegate, DoseSto
}
}

private func pumpDataIsStale() -> Bool {
// How long should we wait before we poll for new pump data?
let pumpStatusAgeTolerance = rileyLinkManager.idleListeningEnabled ? TimeInterval(minutes: 11) : TimeInterval(minutes: 4)

return doseStore.lastReservoirValue == nil
|| doseStore.lastReservoirValue!.startDate.timeIntervalSinceNow <= -pumpStatusAgeTolerance
}

/**
Ensures pump data is current by either waking and polling, or ensuring we're listening to sentry packets.
*/
private func assertCurrentPumpData() {
guard let device = rileyLinkManager.firstConnectedDevice else {
guard let device = rileyLinkManager.firstConnectedDevice, pumpDataIsStale() else {
return
}

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 doseStore.lastReservoirValue == nil ||
doseStore.lastReservoirValue!.startDate.timeIntervalSinceNow <= -pumpStatusAgeTolerance {
readPumpData { (result) in
let nsPumpStatus: NightscoutUploadKit.PumpStatus?
switch result {
case .success(let (status, date)):
self.observeBatteryDuring {
self.latestPumpStatus = status
}
readPumpData { (result) in
let nsPumpStatus: NightscoutUploadKit.PumpStatus?
switch result {
case .success(let (status, date)):
self.observeBatteryDuring {
self.latestPumpStatus = status
}

self.updateReservoirVolume(status.reservoir, at: date, withTimeLeft: nil)
let battery = BatteryStatus(voltage: status.batteryVolts, status: BatteryIndicator(batteryStatus: status.batteryStatus))
self.updateReservoirVolume(status.reservoir, at: date, withTimeLeft: nil)
let battery = BatteryStatus(voltage: status.batteryVolts, status: BatteryIndicator(batteryStatus: status.batteryStatus))


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)
self.nightscoutDataManager.uploadLoopStatus(loopError: error)
nsPumpStatus = nil
}
self.nightscoutDataManager.uploadDeviceStatus(nsPumpStatus)
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)
self.nightscoutDataManager.uploadLoopStatus(loopError: error)
nsPumpStatus = nil
}
self.nightscoutDataManager.uploadDeviceStatus(nsPumpStatus)
}
}

Expand Down Expand Up @@ -484,6 +498,65 @@ final class DeviceDataManager: CarbStoreDelegate, CarbStoreSyncDelegate, DoseSto
}
}

// MARK: - Enlite

fileprivate var latestEnliteData: EnliteSensorDisplayable?

private func updateEnliteSensorStatus(_ events: [TimestampedGlucoseEvent]) {
let sensorEvents = events.filter({ $0.glucoseEvent is RelativeTimestampedGlucoseEvent })

if let latestSensorEvent = sensorEvents.last?.glucoseEvent as? RelativeTimestampedGlucoseEvent {
self.latestEnliteData = EnliteSensorDisplayable(latestSensorEvent)
}
}

private func assertCurrentEnliteData() {
guard let device = rileyLinkManager.firstConnectedDevice, pumpDataIsStale() else {
return
}

device.assertIdleListening()

let fetchGlucoseSince = glucoseStore?.latestGlucose?.startDate.addingTimeInterval(TimeInterval(minutes: 1)) ?? Date(timeIntervalSinceNow: TimeInterval(hours: -24))

guard fetchGlucoseSince.timeIntervalSinceNow <= TimeInterval(minutes: -4.5) else {
return
}

device.ops?.getGlucoseHistoryEvents(since: fetchGlucoseSince, completion: { (result) in
switch result {
case .success(let glucoseEvents):

defer {
_ = self.remoteDataManager.nightscoutUploader?.processGlucoseEvents(glucoseEvents, source: device.deviceURI)
}

self.updateEnliteSensorStatus(glucoseEvents)

let glucoseValues = glucoseEvents
.filter({ $0.glucoseEvent is SensorValueGlucoseEvent && $0.date > fetchGlucoseSince })
.map({ (e:TimestampedGlucoseEvent) -> (quantity: HKQuantity, date: Date, isDisplayOnly: Bool) in
let glucoseEvent = e.glucoseEvent as! SensorValueGlucoseEvent
let quantity = HKQuantity(unit: HKUnit.milligramsPerDeciliterUnit(), doubleValue: Double(glucoseEvent.sgv))
return (quantity: quantity, date: e.date, isDisplayOnly: false)
})

self.glucoseStore?.addGlucoseValues(glucoseValues, device: nil, resultHandler: { (success, _, error) in
if let error = error {
self.logger.addError(error, fromSource: "GlucoseStore")
}

if success {
NotificationCenter.default.post(name: .GlucoseUpdated, object: self)
}
})

case .failure(let error):
self.logger.addError(error, fromSource: "PumpOps")
}
})
}

// MARK: - G5 Transmitter
/// The G5 transmitter is a reliable heartbeat by which we can assert the loop state.

Expand Down
21 changes: 21 additions & 0 deletions Loop/Models/EnliteSensorDisplayable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// EnliteSensorDisplayable.swift
// Loop
//
// Created by Timothy Mecklem on 12/28/16.
// Copyright © 2016 LoopKit Authors. All rights reserved.
//

import Foundation
import LoopUI
import MinimedKit

struct EnliteSensorDisplayable: SensorDisplayable {
public let isStateValid: Bool
public let trendType: LoopUI.GlucoseTrend? = nil
public let isLocal = true

public init?(_ e: RelativeTimestampedGlucoseEvent) {
isStateValid = e is SensorValueGlucoseEvent
}
}
37 changes: 35 additions & 2 deletions Loop/View Controllers/SettingsTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,12 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu
}

fileprivate enum CGMRow: Int {
case receiverEnabled = 0
case fetchEnlite = 0
case receiverEnabled
case transmitterEnabled
case transmitterID // optional, only displayed if transmitterEnabled

static let count = 3
static let count = 4
}

fileprivate enum ConfigurationRow: Int {
Expand Down Expand Up @@ -210,6 +211,17 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu
}
cell = configCell
case .cgm:
if case .fetchEnlite = CGMRow(rawValue: indexPath.row)! {
let switchCell = tableView.dequeueReusableCell(withIdentifier: SwitchTableViewCell.className, for: indexPath) as! SwitchTableViewCell

switchCell.`switch`?.isOn = dataManager.fetchEnliteDataEnabled
switchCell.titleLabel.text = NSLocalizedString("Fetch Enlite Data", comment: "The title text for the fetch enlite data enabled switch cell")

switchCell.`switch`?.addTarget(self, action: #selector(fetchEnliteEnabledChanged(_:)), for: .valueChanged)

return switchCell
}

if case .receiverEnabled = CGMRow(rawValue: indexPath.row)! {
let switchCell = tableView.dequeueReusableCell(withIdentifier: SwitchTableViewCell.className, for: indexPath) as! SwitchTableViewCell

Expand All @@ -235,6 +247,8 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu

let configCell = tableView.dequeueReusableCell(withIdentifier: ConfigCellIdentifier, for: indexPath)
switch CGMRow(rawValue: indexPath.row)! {
case .fetchEnlite:
break
case .transmitterEnabled:
break
case .transmitterID:
Expand Down Expand Up @@ -418,6 +432,8 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu
case .cgm:
let row = CGMRow(rawValue: indexPath.row)!
switch row {
case .fetchEnlite:
break
case .transmitterEnabled:
break
case .transmitterID:
Expand Down Expand Up @@ -660,6 +676,7 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu
if dataManager.transmitterEnabled == false {
dataManager.transmitterEnabled = true
disableReceiver()
disableEnlite()
tableView.insertRows(at: [IndexPath(row: CGMRow.transmitterID.rawValue, section:Section.cgm.rawValue)], with: .top)
}
}
Expand All @@ -678,6 +695,7 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu

if sender.isOn {
disableTransmitter()
disableEnlite()
}
}

Expand All @@ -687,6 +705,21 @@ final class SettingsTableViewController: UITableViewController, DailyValueSchedu
switchCell.`switch`?.setOn(false, animated: true)
}

func fetchEnliteEnabledChanged(_ sender: UISwitch) {
dataManager.fetchEnliteDataEnabled = sender.isOn

if sender.isOn {
disableTransmitter()
disableReceiver();
}
}

private func disableEnlite() {
dataManager.fetchEnliteDataEnabled = false
let switchCell = tableView.cellForRow(at: IndexPath(row: CGMRow.fetchEnlite.rawValue, section: Section.cgm.rawValue)) as! SwitchTableViewCell
switchCell.`switch`?.setOn(false, animated: true)
}

// MARK: - DailyValueScheduleTableViewControllerDelegate

func dailyValueScheduleTableViewControllerWillFinishUpdating(_ controller: DailyValueScheduleTableViewController) {
Expand Down
2 changes: 1 addition & 1 deletion LoopUI/Models/SensorDisplayable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ public protocol SensorDisplayable {
/// Enumerates the trend of the sensor values
var trendType: GlucoseTrend? { get }

/// Returns wheter the data is from a locally-connected device
/// Returns whether the data is from a locally-connected device
var isLocal: Bool { get }
}