Skip to content
Merged
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
4 changes: 2 additions & 2 deletions Cartfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
github "LoopKit/LoopKit" "dev"
github "LoopKit/CGMBLEKit" "dev"
github "i-schuetz/SwiftCharts" == 0.6.2
github "LoopKit/dexcom-share-client-swift" "carthage-recursive"
github "LoopKit/G4ShareSpy" "fix-cartfile"
github "LoopKit/dexcom-share-client-swift" "dev"
github "LoopKit/G4ShareSpy" "dev"
github "ps2/rileylink_ios" "dev"
github "LoopKit/Amplitude-iOS" "decreepify"
10 changes: 5 additions & 5 deletions Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
github "LoopKit/Amplitude-iOS" "2137d5fd44bf630ed33e1e72d7af6d8f8612f270"
github "LoopKit/CGMBLEKit" "bc7c6bb2aaea48e501ecdbb379cd3a533f5df42e"
github "LoopKit/G4ShareSpy" "67208c66abfbb3bff813f047b6c40612338a335b"
github "LoopKit/LoopKit" "1c23bb5fd54c0b0aaa81dbabcb35f2d4c9c2e8cf"
github "LoopKit/dexcom-share-client-swift" "27fd38c28dcb16093ddf660c2b6b84ae34733352"
github "LoopKit/CGMBLEKit" "f868f1366c8a464763fb86ca077e768282a9b197"
github "LoopKit/G4ShareSpy" "3c7040cc93f28b778b6d265e3224974b4ea31483"
github "LoopKit/LoopKit" "1dc7b3ec4b7c6bb9225545f5d8fe599bfe783a5c"
github "LoopKit/dexcom-share-client-swift" "13e0f5cfd98dd4bfdf5eff413ba3ed36617cbb08"
github "i-schuetz/SwiftCharts" "0.6.2"
github "ps2/rileylink_ios" "5df08f32f11ac2cb6097ba1411349f05b9171e72"
github "ps2/rileylink_ios" "f3597c78ae00da2f35ef98269056126af4ce070f"
4 changes: 3 additions & 1 deletion Loop/Extensions/NightscoutUploader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,13 @@ extension NightscoutUploader {
var treatments = [NightscoutTreatment]()

for event in events {

objectIDURLs.append(event.objectIDURL)

guard let treatment = event.treatment(enteredBy: source) else {
continue
}

objectIDURLs.append(event.objectIDURL)
treatments.append(treatment)
}

Expand Down
40 changes: 33 additions & 7 deletions Loop/Managers/DeviceDataManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ extension DeviceDataManager: CGMManagerDelegate {
/// TODO: Isolate to queue
switch result {
case .newData(let values):
log.default("CGMManager:\(type(of: manager)) did update with new data")

loopManager.addGlucose(values) { result in
if manager.shouldSyncToRemoteService {
switch result {
Expand All @@ -145,8 +147,12 @@ extension DeviceDataManager: CGMManagerDelegate {
self.pumpManager?.assertCurrentPumpData()
}
case .noData:
log.default("CGMManager:\(type(of: manager)) did update with no data")

pumpManager?.assertCurrentPumpData()
case .error(let error):
log.default("CGMManager:\(type(of: manager)) did update with error: \(error)")

self.setLastError(error: error)
pumpManager?.assertCurrentPumpData()
}
Expand All @@ -162,10 +168,14 @@ extension DeviceDataManager: CGMManagerDelegate {

extension DeviceDataManager: PumpManagerDelegate {
func pumpManager(_ pumpManager: PumpManager, didAdjustPumpClockBy adjustment: TimeInterval) {
log.default("PumpManager:\(type(of: pumpManager)) did adjust pump block by \(adjustment)s")

AnalyticsManager.shared.pumpTimeDidDrift(adjustment)
}

func pumpManagerDidUpdatePumpBatteryChargeRemaining(_ pumpManager: PumpManager, oldValue: Double?) {
log.default("PumpManager:\(type(of: pumpManager)) did update pump battery from \(String(describing: oldValue))")

if let newValue = pumpManager.pumpBatteryChargeRemaining {
if newValue == 0 {
NotificationManager.sendPumpBatteryLowNotification()
Expand All @@ -180,10 +190,14 @@ extension DeviceDataManager: PumpManagerDelegate {
}

func pumpManagerDidUpdateState(_ pumpManager: PumpManager) {
log.default("PumpManager:\(type(of: pumpManager)) did update state")

UserDefaults.appGroup.pumpManager = pumpManager
}

func pumpManagerBLEHeartbeatDidFire(_ pumpManager: PumpManager) {
log.default("PumpManager:\(type(of: pumpManager)) did fire BLE heartbeat")

cgmManager?.fetchNewDataIfNeeded { (result) in
if case .newData = result {
AnalyticsManager.shared.didFetchNewCGMData()
Expand All @@ -201,27 +215,37 @@ extension DeviceDataManager: PumpManagerDelegate {
}

func pumpManager(_ pumpManager: PumpManager, didUpdateStatus status: PumpManagerStatus) {
log.default("PumpManager:\(type(of: pumpManager)) did update status")

loopManager.doseStore.device = status.device
// Update the pump-schedule based settings
loopManager.setScheduleTimeZone(status.timeZone)
nightscoutDataManager.upload(pumpStatus: status)
}

func pumpManagerWillDeactivate(_ pumpManager: PumpManager) {
log.default("PumpManager:\(type(of: pumpManager)) will deactivate")

loopManager.doseStore.resetPumpData()
self.pumpManager = nil
}

func pumpManager(_ pumpManager: PumpManager, didUpdatePumpRecordsBasalProfileStartEvents pumpRecordsBasalProfileStartEvents: Bool) {
log.default("PumpManager:\(type(of: pumpManager)) did update pumpRecordsBasalProfileStartEvents to \(pumpRecordsBasalProfileStartEvents)")

loopManager.doseStore.pumpRecordsBasalProfileStartEvents = pumpRecordsBasalProfileStartEvents
}

func pumpManager(_ pumpManager: PumpManager, didError error: PumpManagerError) {
log.error("PumpManager:\(type(of: pumpManager)) did error: \(error)")

setLastError(error: error)
nightscoutDataManager.uploadLoopStatus(loopError: error)
}

func pumpManager(_ pumpManager: PumpManager, didReadPumpEvents events: [NewPumpEvent], completion: @escaping (_ error: Error?) -> Void) {
log.default("PumpManager:\(type(of: pumpManager)) did read pump events")

loopManager.addPumpEvents(events) { (error) in
if let error = error {
self.log.error("Failed to addPumpEvents to DoseStore: \(error)")
Expand All @@ -232,10 +256,12 @@ extension DeviceDataManager: PumpManagerDelegate {
}

func pumpManager(_ pumpManager: PumpManager, didReadReservoirValue units: Double, at date: Date, completion: @escaping (_ result: PumpManagerResult<(newValue: ReservoirValue, lastValue: ReservoirValue?, areStoredValuesContinuous: Bool)>) -> Void) {
log.default("PumpManager:\(type(of: pumpManager)) did read reservoir value")

loopManager.addReservoirValue(units, at: date) { (result) in
switch result {
case .failure(let error):
self.logger.addError(error, fromSource: "Bolus")
self.log.error("Failed to addReservoirValue: \(error)")
completion(.failure(error))
case .success(let (newValue, lastValue, areStoredValuesContinuous)):
completion(.success((newValue: newValue, lastValue: lastValue, areStoredValuesContinuous: areStoredValuesContinuous)))
Expand All @@ -247,29 +273,27 @@ extension DeviceDataManager: PumpManagerDelegate {
return
}

var didSendLowNotification = false
let warningThresholds: [Double] = [10, 20, 30]

for threshold in warningThresholds {
if newValue.unitVolume <= threshold && previousVolume > threshold {
NotificationManager.sendPumpReservoirLowNotificationForAmount(newValue.unitVolume, andTimeRemaining: nil)
didSendLowNotification = true
break
}
}

if !didSendLowNotification {
NotificationManager.clearPumpReservoirNotification()
}

if newValue.unitVolume > previousVolume + 1 {
AnalyticsManager.shared.reservoirWasRewound()

NotificationManager.clearPumpReservoirNotification()
}
}
}
}
}

func pumpManagerRecommendsLoop(_ pumpManager: PumpManager) {
log.default("PumpManager:\(type(of: pumpManager)) recommends loop")
loopManager.loop()
}

Expand Down Expand Up @@ -341,6 +365,8 @@ extension DeviceDataManager: LoopDataManagerDelegate {
return
}

log.default("LoopManager did recommend basal change")

pumpManager.enactTempBasal(
unitsPerHour: basal.recommendation.unitsPerHour,
for: basal.recommendation.duration,
Expand Down
2 changes: 2 additions & 0 deletions Loop/Managers/LoopDataManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,7 @@ extension LoopDataManager {
/// temporary basal rate.
func loop() {
self.dataAccessQueue.async {
self.logger.default("Loop running")
NotificationCenter.default.post(name: .LoopRunning, object: self)

self.lastLoopError = nil
Expand Down Expand Up @@ -582,6 +583,7 @@ extension LoopDataManager {
self.lastLoopError = error
}

self.logger.default("Loop ended")
self.notify(forChange: .tempBasal)
}
}
Expand Down
2 changes: 1 addition & 1 deletion Loop/Managers/StatusChartsManager+LoopKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ extension StatusChartsManager {
for entry in doseEntries {
let time = entry.endDate.timeIntervalSince(entry.startDate)

if entry.type == .bolus && entry.netBasalUnits > 0 && time < .minutes(5) {
if entry.type == .bolus && entry.netBasalUnits > 0 && time < .minutes(10) {
let x = ChartAxisValueDate(date: entry.startDate, formatter: dateFormatter)
let y = ChartAxisValueDoubleLog(actualDouble: entry.units, unitString: "U", formatter: doseFormatter)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
{
"idiom" : "watch",
"filename" : "Icon-AppleWatch-32x32@2x.png",
"filename" : "Icon-Complication-20x20@2x.png",
"screen-width" : ">161",
"scale" : "2x"
},
Expand All @@ -18,7 +18,7 @@
},
{
"idiom" : "watch",
"filename" : "Icon-AppleWatch-36x36@2x.png",
"filename" : "Icon-Complication-22x22@2x.png",
"screen-width" : ">183",
"scale" : "2x"
}
Expand Down
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion WatchApp Extension/Base.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@
/* The short unit display string for international units of insulin */
"U" = "U";


/* The short unit display string for international units of insulin delivery per hour */
"U/hr" = "U/hr";
98 changes: 47 additions & 51 deletions WatchApp Extension/Controllers/ChartHUDController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,23 @@ import WatchKit
import WatchConnectivity
import CGMBLEKit
import LoopKit
import HealthKit
import SpriteKit
import os.log

final class ChartHUDController: HUDInterfaceController, WKCrownDelegate {
@IBOutlet weak var basalLabel: WKInterfaceLabel!
@IBOutlet weak var iobLabel: WKInterfaceLabel!
@IBOutlet weak var cobLabel: WKInterfaceLabel!
@IBOutlet weak var glucoseScene: WKInterfaceSKScene!
@IBAction func setChartWindow1Hour() {
@IBOutlet private weak var tableGroup: WKInterfaceGroup!
@IBOutlet private weak var basalLabel: WKInterfaceLabel!
@IBOutlet private weak var iobLabel: WKInterfaceLabel!
@IBOutlet private weak var cobLabel: WKInterfaceLabel!
@IBOutlet private weak var glucoseScene: WKInterfaceSKScene!
@IBAction private func setChartWindow1Hour() {
scene.visibleDuration = .hours(2)
}
@IBAction func setChartWindow2Hours() {
@IBAction private func setChartWindow2Hours() {
scene.visibleDuration = .hours(4)
}
@IBAction func setChartWindow3Hours() {
@IBAction private func setChartWindow3Hours() {
scene.visibleDuration = .hours(6)
}
private let scene = GlucoseChartScene()
Expand Down Expand Up @@ -54,19 +56,27 @@ final class ChartHUDController: HUDInterfaceController, WKCrownDelegate {
override func didAppear() {
super.didAppear()

log.default("didAppear")
if glucoseScene.isPaused {
log.default("didAppear() unpausing")
glucoseScene.isPaused = false
} else {
log.default("didAppear() not paused")
glucoseScene.isPaused = false
}

// Force an update when our pixels need to move
let pixelsWide = scene.size.width * WKInterfaceDevice.current().screenScale
let pixelInterval = scene.visibleDuration / TimeInterval(pixelsWide)

timer = Timer.scheduledTimer(withTimeInterval: pixelInterval, repeats: true) { [weak self] _ in
self?.log.default("Timer fired, triggering update")
self?.scene.setNeedsUpdate()
}

if #available(watchOSApplicationExtension 5.0, *) {
scene.textInsets.left = max(scene.textInsets.left, systemMinimumLayoutMargins.leading)
scene.textInsets.right = max(scene.textInsets.right, systemMinimumLayoutMargins.trailing)
tableGroup.setContentInset(UIEdgeInsets(top: 0, left: systemMinimumLayoutMargins.leading, bottom: 0, right: systemMinimumLayoutMargins.trailing))
}
}

Expand All @@ -85,7 +95,7 @@ final class ChartHUDController: HUDInterfaceController, WKCrownDelegate {
log.default("willActivate() unpausing")
glucoseScene.isPaused = false
} else {
log.default("willActivate() unpausing")
log.default("willActivate()")
}

if !hasInitialActivation && UserDefaults.standard.startOnChartPage {
Expand All @@ -112,58 +122,44 @@ final class ChartHUDController: HUDInterfaceController, WKCrownDelegate {
return
}

let insulinFormatter: NumberFormatter = {
let numberFormatter = NumberFormatter()

numberFormatter.numberStyle = .decimal
numberFormatter.minimumFractionDigits = 1
numberFormatter.maximumFractionDigits = 1

return numberFormatter
}()

iobLabel.setHidden(true)
if let activeInsulin = activeContext.iob, let valueStr = insulinFormatter.string(from: activeInsulin) {
iobLabel.setText(String(format: NSLocalizedString(
"IOB %1$@ U",
comment: "The subtitle format describing units of active insulin. (1: localized insulin value description)"
),
valueStr
))
iobLabel.setHidden(false)
if let activeInsulin = activeContext.activeInsulin {
let insulinFormatter: QuantityFormatter = {
let insulinFormatter = QuantityFormatter()
insulinFormatter.numberFormatter.minimumFractionDigits = 1
insulinFormatter.numberFormatter.maximumFractionDigits = 1

return insulinFormatter
}()

iobLabel.setText(insulinFormatter.string(from: activeInsulin, for: .internationalUnit()))
} else {
iobLabel.setText("—")
}

cobLabel.setHidden(true)
if let carbsOnBoard = activeContext.cob {
let carbFormatter = NumberFormatter()
carbFormatter.numberStyle = .decimal
carbFormatter.maximumFractionDigits = 0
let valueStr = carbFormatter.string(from: carbsOnBoard)

cobLabel.setText(String(format: NSLocalizedString(
"COB %1$@ g",
comment: "The subtitle format describing grams of active carbs. (1: localized carb value description)"
),
valueStr!
))
cobLabel.setHidden(false)
if let carbsOnBoard = activeContext.activeCarbohydrates {
let carbFormatter = QuantityFormatter()
carbFormatter.numberFormatter.maximumFractionDigits = 0

cobLabel.setText(carbFormatter.string(from: carbsOnBoard, for: .gram()))
} else {
cobLabel.setText("—")
}

basalLabel.setHidden(true)
if let tempBasal = activeContext.lastNetTempBasalDose {
let basalFormatter = NumberFormatter()
basalFormatter.numberStyle = .decimal
basalFormatter.minimumFractionDigits = 1
basalFormatter.maximumFractionDigits = 3
basalFormatter.positivePrefix = basalFormatter.plusSign
let valueStr = basalFormatter.string(from: tempBasal)

let basalLabelText = String(format: NSLocalizedString(
"%1$@ U/hr",
comment: "The subtitle format describing the current temp basal rate. (1: localized basal rate description)"),
valueStr!)
basalLabel.setText(basalLabelText)
basalLabel.setHidden(false)

let unit = NSLocalizedString(
"U/hr",
comment: "The short unit display string for international units of insulin delivery per hour"
)

basalLabel.setText(basalFormatter.string(from: tempBasal, unit: unit))
} else {
basalLabel.setText("—")
}

if glucoseScene.isPaused {
Expand Down
Loading