Parcourir la source

Merge pull request #803 from bastiaanv/feat/dev-medtrum

feat: update MedtrumKit
Sjoerd Bozon il y a 6 mois
Parent
commit
f2a23fb27e

+ 1 - 1
MedtrumKit

@@ -1 +1 @@
-Subproject commit 8b46b60835072f02340de351d99b7c1ca2264ec1
+Subproject commit a85496e90d0a16ba8fb5dae7781dfbc3e5f66267

+ 5 - 0
Trio/Sources/APS/APSManager.swift

@@ -18,6 +18,7 @@ protocol APSManager {
     var lastLoopDateSubject: PassthroughSubject<Date, Never> { get }
     var bolusProgress: CurrentValueSubject<Decimal?, Never> { get }
     var pumpExpiresAtDate: CurrentValueSubject<Date?, Never> { get }
+    var pumpActivatedAtDate: CurrentValueSubject<Date?, Never> { get }
     var isManualTempBasal: Bool { get }
     func enactTempBasal(rate: Double, duration: TimeInterval) async
     func determineBasal() async throws
@@ -124,6 +125,10 @@ final class BaseAPSManager: APSManager, Injectable {
         deviceDataManager.pumpExpiresAtDate
     }
 
+    var pumpActivatedAtDate: CurrentValueSubject<Date?, Never> {
+        deviceDataManager.pumpActivatedAtDate
+    }
+
     var settings: TrioSettings {
         get { settingsManager.settings }
         set { settingsManager.settings = newValue }

+ 26 - 7
Trio/Sources/APS/DeviceDataManager.swift

@@ -26,6 +26,7 @@ protocol DeviceDataManager: GlucoseSource {
     var errorSubject: PassthroughSubject<Error, Never> { get }
     var pumpName: CurrentValueSubject<String, Never> { get }
     var pumpExpiresAtDate: CurrentValueSubject<Date?, Never> { get }
+    var pumpActivatedAtDate: CurrentValueSubject<Date?, Never> { get }
 
     func heartbeat(date: Date)
     func createBolusProgressReporter() -> DoseProgressReporter?
@@ -101,6 +102,7 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
                 storage.save(modifiedPreferences, as: OpenAPS.Settings.preferences)
 
                 if let omnipod = pumpManager as? OmnipodPumpManager {
+                    pumpActivatedAtDate.send(nil)
                     guard let endTime = omnipod.state.podState?.expiresAt else {
                         pumpExpiresAtDate.send(nil)
                         return
@@ -108,6 +110,7 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
                     pumpExpiresAtDate.send(endTime)
                 }
                 if let omnipodBLE = pumpManager as? OmniBLEPumpManager {
+                    pumpActivatedAtDate.send(nil)
                     guard let endTime = omnipodBLE.state.podState?.expiresAt else {
                         pumpExpiresAtDate.send(nil)
                         return
@@ -115,11 +118,17 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
                     pumpExpiresAtDate.send(endTime)
                 }
                 if let medtrum = pumpManager as? MedtrumPumpManager {
-                    guard let endTime = medtrum.state.patchExpiresAt else {
+                    if medtrum.state.expirationTimer == 1 {
+                        pumpActivatedAtDate.send(nil)
+                        guard let endTime = medtrum.state.patchExpiresAt else {
+                            pumpExpiresAtDate.send(nil)
+                            return
+                        }
+                        pumpExpiresAtDate.send(endTime)
+                    } else {
+                        pumpActivatedAtDate.send(medtrum.state.patchActivatedAt)
                         pumpExpiresAtDate.send(nil)
-                        return
                     }
-                    pumpExpiresAtDate.send(endTime)
                 }
                 if let simulatorPump = pumpManager as? MockPumpManager {
                     pumpDisplayState.value = PumpDisplayState(name: simulatorPump.localizedTitle, image: simulatorPump.smallImage)
@@ -165,6 +174,7 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
             } else {
                 pumpDisplayState.value = nil
                 pumpExpiresAtDate.send(nil)
+                pumpActivatedAtDate.send(nil)
                 pumpName.send("")
                 // Reset bolusIncrement setting to default value, which is 0.1 U
                 var modifiedPreferences = settingsManager.preferences
@@ -204,6 +214,7 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
 
     let pumpDisplayState = CurrentValueSubject<PumpDisplayState?, Never>(nil)
     let pumpExpiresAtDate = CurrentValueSubject<Date?, Never>(nil)
+    let pumpActivatedAtDate = CurrentValueSubject<Date?, Never>(nil)
     let pumpName = CurrentValueSubject<String, Never>("Pump")
 
     init(resolver: Resolver) {
@@ -446,6 +457,7 @@ extension BaseDeviceDataManager: PumpManagerDelegate {
                 manualTempBasal.send(false)
             }
 
+            pumpActivatedAtDate.send(nil)
             guard let endTime = omnipod.state.podState?.expiresAt else {
                 pumpExpiresAtDate.send(nil)
                 return
@@ -479,6 +491,7 @@ extension BaseDeviceDataManager: PumpManagerDelegate {
                 manualTempBasal.send(false)
             }
 
+            pumpActivatedAtDate.send(nil)
             guard let endTime = omnipodBLE.state.podState?.expiresAt else {
                 pumpExpiresAtDate.send(nil)
                 return
@@ -489,18 +502,24 @@ extension BaseDeviceDataManager: PumpManagerDelegate {
                 storage.save(startTime, as: OpenAPS.Monitor.podAge)
             }
         }
-        
+
         if let medtrumPump = pumpManager as? MedtrumPumpManager {
             storage.save(Decimal(medtrumPump.state.reservoir), as: OpenAPS.Monitor.reservoir)
             broadcaster.notify(PumpReservoirObserver.self, on: processQueue) {
                 $0.pumpReservoirDidChange(Decimal(medtrumPump.state.reservoir))
             }
 
-            guard let endTime = medtrumPump.state.patchExpiresAt else {
+            if medtrumPump.state.expirationTimer == 1 {
+                pumpActivatedAtDate.send(nil)
+                guard let endTime = medtrumPump.state.patchExpiresAt else {
+                    pumpExpiresAtDate.send(nil)
+                    return
+                }
+                pumpExpiresAtDate.send(endTime)
+            } else {
+                pumpActivatedAtDate.send(medtrumPump.state.patchActivatedAt)
                 pumpExpiresAtDate.send(nil)
-                return
             }
-            pumpExpiresAtDate.send(endTime)
         }
 
         if let simulatorPump = pumpManager as? MockPumpManager {

+ 6 - 0
Trio/Sources/Modules/Home/HomeStateModel.swift

@@ -48,6 +48,7 @@ extension Home {
         var reservoir: Decimal?
         var pumpName = ""
         var pumpExpiresAtDate: Date?
+        var pumpActivatedAtDate: Date?
         var highTTraisesSens: Bool = false
         var lowTTlowersSens: Bool = false
         var isExerciseModeActive: Bool = false
@@ -343,6 +344,11 @@ extension Home {
                 .weakAssign(to: \.pumpExpiresAtDate, on: self)
                 .store(in: &lifetime)
 
+            apsManager.pumpActivatedAtDate
+                .receive(on: DispatchQueue.main)
+                .weakAssign(to: \.pumpActivatedAtDate, on: self)
+                .store(in: &lifetime)
+
             apsManager.lastError
                 .receive(on: DispatchQueue.main)
                 .map { [weak self] error in

+ 67 - 24
Trio/Sources/Modules/Home/View/Header/PumpView.swift

@@ -5,11 +5,14 @@ struct PumpView: View {
     let reservoir: Decimal?
     let name: String
     let expiresAtDate: Date?
+    let activatedAtDate: Date?
     let timerDate: Date
     let pumpStatusHighlightMessage: String?
     let battery: [OpenAPS_Battery]
     @Environment(\.colorScheme) var colorScheme
 
+    let NORMAL_PATCH_AGE = TimeInterval.hours(80)
+
     private var batteryFormatter: NumberFormatter {
         let formatter = NumberFormatter()
         formatter.numberStyle = .percent
@@ -17,6 +20,7 @@ struct PumpView: View {
     }
 
     private var hourglassIcon: String {
+        if activatedAtDate != nil { return "hourglass.badge.plus" }
         guard let expiration = expiresAtDate else { return "hourglass" }
 
         let hoursRemaining = expiration.timeIntervalSince(timerDate) / 3600
@@ -96,34 +100,44 @@ struct PumpView: View {
                 }
 
                 if let date = expiresAtDate {
-                    HStack {
-                        Image(systemName: hourglassIcon)
-                            .font(.callout)
-                            .foregroundStyle(timerColor, Color.yellow)
-                            .symbolRenderingMode(.palette)
-
-                        let remainingTimeString = remainingTimeString(time: date.timeIntervalSince(timerDate))
+                    PatchTimer(date, isExpiration: true)
+                }
 
-                        Text(remainingTimeString)
-                            .font(date.timeIntervalSince(timerDate) > 0 ? .callout : .subheadline)
-                            .fontWeight(.bold)
-                            .fontDesign(.rounded)
-                            .lineLimit(2)
-                            .multilineTextAlignment(.leading)
-                            .frame(
-                                // If the string is > 6 chars, i.e., exceeds "xd yh", limit width to 80 pts
-                                // This forces the "Replace pod" string to wrap to 2 lines.
-                                maxWidth: remainingTimeString.count > 6 ? 80 : .infinity,
-                                alignment: .leading
-                            )
-                    }
-                    // aligns the stopwatch icon exactly with the first pixel of the reservoir icon
-                    .padding(.leading, date.timeIntervalSince(timerDate) > 0 ? 12 : 0)
+                if let date = activatedAtDate {
+                    PatchTimer(date, isExpiration: false)
                 }
             }
         }
     }
 
+    @ViewBuilder private func PatchTimer(_ date: Date, isExpiration: Bool) -> some View {
+        HStack {
+            Image(systemName: hourglassIcon)
+                .font(.callout)
+                .foregroundStyle(timerColor, timerColorSecondary)
+                .symbolRenderingMode(.palette)
+
+            let remainingTimeString = isExpiration ?
+                remainingTimeString(time: date.timeIntervalSince(timerDate)) :
+                activeTimeString(time: timerDate.timeIntervalSince(date))
+
+            Text(remainingTimeString)
+                .font(date.timeIntervalSince(timerDate) > 0 ? .callout : .subheadline)
+                .fontWeight(.bold)
+                .fontDesign(.rounded)
+                .lineLimit(2)
+                .multilineTextAlignment(.leading)
+                .frame(
+                    // If the string is > 6 chars, i.e., exceeds "xd yh", limit width to 80 pts
+                    // This forces the "Replace pod" string to wrap to 2 lines.
+                    maxWidth: remainingTimeString.count > 6 ? 80 : .infinity,
+                    alignment: .leading
+                )
+        }
+        // aligns the stopwatch icon exactly with the first pixel of the reservoir icon
+        .padding(.leading, date.timeIntervalSince(timerDate) > 0 || !isExpiration ? 12 : 0)
+    }
+
     private func remainingTimeString(time: TimeInterval) -> String {
         guard time > 0 else {
             return String(localized: "Replace pod", comment: "View/Header when pod expired")
@@ -148,6 +162,23 @@ struct PumpView: View {
         return "\(minutes)" + String(localized: "m", comment: "abbreviation for minutes")
     }
 
+    private func activeTimeString(time: TimeInterval) -> String {
+        var time = time
+        let days = Int(time / 1.days.timeInterval)
+        time -= days.days.timeInterval
+        let hours = Int(time / 1.hours.timeInterval)
+        time -= hours.hours.timeInterval
+        let minutes = Int(time / 1.minutes.timeInterval)
+
+        if days >= 1 {
+            return "\(days)" + String(localized: "d", comment: "abbreviation for days") + " \(hours)" +
+                String(localized: "h", comment: "abbreviation for hours")
+        }
+
+        return "\(hours)" + String(localized: "h", comment: "abbreviation for hours") + "\(minutes)" +
+            String(localized: "m", comment: "abbreviation for minutes")
+    }
+
     private var batteryColor: Color {
         guard let battery = battery.first else {
             return .gray
@@ -179,11 +210,15 @@ struct PumpView: View {
     }
 
     private var timerColor: Color {
-        guard let expisesAt = expiresAtDate else {
+        if let activatedAt = activatedAtDate {
+            return abs(activatedAt.timeIntervalSinceNow) > NORMAL_PATCH_AGE ? Color.yellow : Color.loopGreen
+        }
+
+        guard let expiresAt = expiresAtDate else {
             return .gray
         }
 
-        let time = expisesAt.timeIntervalSince(timerDate)
+        let time = expiresAt.timeIntervalSince(timerDate)
 
         switch time {
         case ...8.hours.timeInterval:
@@ -194,6 +229,14 @@ struct PumpView: View {
             return Color.loopGreen
         }
     }
+
+    private var timerColorSecondary: Color {
+        if activatedAtDate != nil {
+            return Color.gray
+        }
+
+        return Color.yellow
+    }
 }
 
 // #Preview("message") {

+ 1 - 0
Trio/Sources/Modules/Home/View/HomeRootView.swift

@@ -147,6 +147,7 @@ extension Home {
                 reservoir: state.reservoir,
                 name: state.pumpName,
                 expiresAtDate: state.pumpExpiresAtDate,
+                activatedAtDate: state.pumpActivatedAtDate,
                 timerDate: state.timerDate,
                 pumpStatusHighlightMessage: state.pumpStatusHighlightMessage,
                 battery: state.batteryFromPersistence