Browse Source

Release 226 (#374)

* Minutes ago fix for G7 sensors.

* Fix compile issue in Xcode 14 (#235)

* Improve the update of the date of Glucose View Header.

* Allow looping when HIGH (#259)
- Allow lflat glucose when CGM readings are HIGH (but disable SM and high temps) for IOB computations etc.
- Display HIGH when CGM reading is HIGH.
- Display real glucose when entering capillary glucose.
- Allow (capillary readings over HIGH for normal looping.
- Display in Enacted pop-up.
- Display warning in same pop-up
- Disable SMB and high temps in oref2 code when reading is HIGH (potential invalid CGM reading).

* Add Dexcom G7 SAGE.Upload activation and session start date to NS.

* Fix run script for branch name and commit ID to work with Xcode 15 (#274)

* BuildBranch fix for Xcode 15

* Display build number like TestFlight (build number in brackets)

* Hard limit of 55 for insulinPeakTime
to avoid endless logs and errors in oref0

* Prevent Nightscout collisions from occurring due to non-unique glucose-IDs IDs Fix for all CGMs.

* Check for a trailing slash (/) at the end of the URL entered by the user and remove it to prevent 404's
---------

Co-authored-by: Pierre L <pn.lagarde@gmail.com>
Co-authored-by: bjornoleh <63544115+bjornoleh@users.noreply.github.com>
Co-authored-by: Liroy van Hoewijk <4643445+LiroyvH@users.noreply.github.com>
Jon B Mårtensson 2 năm trước cách đây
mục cha
commit
cf5d562178

+ 2 - 0
.gitignore

@@ -80,3 +80,5 @@ fastlane/test_output
 fastlane/FastlaneRunner
 
 ConfigOverride.xcconfig
+
+branch.txt

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 9 - 5
FreeAPS.xcodeproj/project.pbxproj


+ 0 - 2
FreeAPS/Resources/Info.plist

@@ -8,8 +8,6 @@
 	<array>
 		<string>com.freeapsx.background-task.critical-event-log</string>
 	</array>
-	<key>BuildBranch</key>
-	<string></string>
 	<key>CBBundleDisplayName</key>
 	<string>$(APP_DISPLAY_NAME)</string>
 	<key>CFBundleDevelopmentRegion</key>

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1 - 1
FreeAPS/Resources/javascript/bundle/determine-basal.js


+ 2 - 1
FreeAPS/Resources/javascript/prepare/profile.js

@@ -13,7 +13,7 @@ function generate(pumpsettings_data, bgtargets_data, isf_data, basalprofile_data
             return { "error" : 'BG Target data is expected to be expressed in mg/dL or mmol/L. Found '+ bgtargets_data.units };
         }
     }
-
+    
     if (isf_data.units !== 'mg/dL') {
         if (isf_data.units === 'mmol/L') {
             for (var i = 0, len = isf_data.sensitivities.length; i < len; i++) {
@@ -63,6 +63,7 @@ function generate(pumpsettings_data, bgtargets_data, isf_data, basalprofile_data
     var preferences = { };
     if (preferences_input) {
         preferences = preferences_input;
+        preferences.insulinPeakTime = Math.max(preferences.insulinPeakTime, 55);
     }
 
     var inputs = { };

+ 30 - 5
FreeAPS/Sources/APS/APSManager.swift

@@ -347,10 +347,13 @@ final class BaseAPSManager: APSManager, Injectable {
             return Just(false).eraseToAnyPublisher()
         }
 
-        guard glucoseStorage.isGlucoseNotFlat() else {
-            debug(.apsManager, "Glucose data is too flat")
-            processError(APSError.glucoseError(message: "Glucose data is too flat"))
-            return Just(false).eraseToAnyPublisher()
+        // Only let glucose be flat when 400 mg/dl
+        if (glucoseStorage.recent().last?.glucose ?? 100) != 400 {
+            guard glucoseStorage.isGlucoseNotFlat() else {
+                debug(.apsManager, "Glucose data is too flat")
+                processError(APSError.glucoseError(message: "Glucose data is too flat"))
+                return Just(false).eraseToAnyPublisher()
+            }
         }
 
         let now = Date()
@@ -941,7 +944,29 @@ final class BaseAPSManager: APSManager, Injectable {
                 let buildDate = Bundle.main.buildDate
                 let version = Bundle.main.releaseVersionNumber
                 let build = Bundle.main.buildVersionNumber
-                let branch = Bundle.main.infoDictionary?["BuildBranch"] as? String ?? ""
+
+                // Read branch information from branch.txt instead of infoDictionary
+                var branch = "Unknown"
+                if let branchFileURL = Bundle.main.url(forResource: "branch", withExtension: "txt"),
+                   let branchFileContent = try? String(contentsOf: branchFileURL)
+                {
+                    let lines = branchFileContent.components(separatedBy: .newlines)
+                    for line in lines {
+                        let components = line.components(separatedBy: "=")
+                        if components.count == 2 {
+                            let key = components[0].trimmingCharacters(in: .whitespaces)
+                            let value = components[1].trimmingCharacters(in: .whitespaces)
+
+                            if key == "BRANCH" {
+                                branch = value
+                                break
+                            }
+                        }
+                    }
+                } else {
+                    branch = "Unknown"
+                }
+
                 let copyrightNotice_ = Bundle.main.infoDictionary?["NSHumanReadableCopyright"] as? String ?? ""
                 let pump_ = pumpManager?.localizedTitle ?? ""
                 let cgm = settingsManager.settings.cgm

+ 1 - 1
FreeAPS/Sources/APS/CGM/DexcomSourceG5.swift

@@ -148,7 +148,7 @@ extension DexcomSourceG5: CGMManagerDelegate {
                 let quantity = newGlucoseSample.quantity
                 let value = Int(quantity.doubleValue(for: .milligramsPerDeciliter))
                 return BloodGlucose(
-                    _id: newGlucoseSample.syncIdentifier,
+                    _id: UUID().uuidString,
                     sgv: value,
                     direction: .init(trendType: newGlucoseSample.trend),
                     date: Decimal(Int(newGlucoseSample.date.timeIntervalSince1970 * 1000)),

+ 1 - 1
FreeAPS/Sources/APS/CGM/DexcomSourceG6.swift

@@ -153,7 +153,7 @@ extension DexcomSourceG6: CGMManagerDelegate {
                     let quantity = newGlucoseSample.quantity
                     let value = Int(quantity.doubleValue(for: .milligramsPerDeciliter))
                     return BloodGlucose(
-                        _id: newGlucoseSample.syncIdentifier,
+                        _id: UUID().uuidString,
                         sgv: value,
                         direction: .init(trendType: newGlucoseSample.trend),
                         date: Decimal(Int(newGlucoseSample.date.timeIntervalSince1970 * 1000)),

+ 1 - 1
FreeAPS/Sources/APS/CGM/LibreTransmitterSource.swift

@@ -72,7 +72,7 @@ extension BaseLibreTransmitterSource: LibreTransmitterManagerDelegate {
         case let .success(newGlucose):
             let glucose = newGlucose.map { value -> BloodGlucose in
                 BloodGlucose(
-                    _id: value.syncId,
+                    _id: UUID().uuidString,
                     sgv: Int(value.glucose),
                     direction: manager.glucoseDisplay?.trendType
                         .map { .init(trendType: $0) },

+ 13 - 2
FreeAPS/Sources/APS/CGM/dexcomSourceG7.swift

@@ -135,11 +135,20 @@ extension DexcomSourceG7: CGMManagerDelegate {
         debug(.deviceManager, "DEXCOMG7 - Process CGM Reading Result launched")
         switch readingResult {
         case let .newData(values):
+
+            var activationDate: Date = .distantPast
+            var sessionStart: Date = .distantPast
+            if let cgmG7Manager = cgmManager as? G7CGMManager {
+                activationDate = cgmG7Manager.sensorActivatedAt ?? .distantPast
+                sessionStart = cgmG7Manager.sensorFinishesWarmupAt ?? .distantPast
+                print("Activastion date: " + activationDate.description)
+            }
+
             let bloodGlucose = values.compactMap { newGlucoseSample -> BloodGlucose? in
                 let quantity = newGlucoseSample.quantity
                 let value = Int(quantity.doubleValue(for: .milligramsPerDeciliter))
                 return BloodGlucose(
-                    _id: newGlucoseSample.syncIdentifier,
+                    _id: UUID().uuidString,
                     sgv: value,
                     direction: .init(trendType: newGlucoseSample.trend),
                     date: Decimal(Int(newGlucoseSample.date.timeIntervalSince1970 * 1000)),
@@ -148,7 +157,9 @@ extension DexcomSourceG7: CGMManagerDelegate {
                     filtered: nil,
                     noise: nil,
                     glucose: value,
-                    type: "sgv"
+                    type: "sgv",
+                    activationDate: activationDate,
+                    sessionStartDate: sessionStart
                 )
             }
 

+ 1 - 1
FreeAPS/Sources/Modules/DataTable/View/DataTableRootView.swift

@@ -76,7 +76,7 @@ extension DataTable {
                         }
                         label: { Text("Save") }
                             .frame(maxWidth: .infinity, alignment: .trailing)
-                            .disabled(state.manualGlcuose < limitLow || state.manualGlcuose > limitHigh)
+                        // .disabled(state.manualGlcuose < limitLow || state.manualGlcuose > limitHigh)
 
                     }.padding(20)
                 }

+ 5 - 4
FreeAPS/Sources/Modules/Home/View/Header/CurrentGlucoseView.swift

@@ -2,6 +2,7 @@ import SwiftUI
 
 struct CurrentGlucoseView: View {
     @Binding var recentGlucose: BloodGlucose?
+    @Binding var timerDate: Date
     @Binding var delta: Int?
     @Binding var units: GlucoseUnits
     @Binding var alarm: GlucoseAlarm?
@@ -47,7 +48,7 @@ struct CurrentGlucoseView: View {
         VStack(alignment: .center) {
             HStack {
                 Text(
-                    recentGlucose?.glucose
+                    (recentGlucose?.glucose ?? 100) == 400 ? "HIGH" : recentGlucose?.glucose
                         .map {
                             glucoseFormatter
                                 .string(from: Double(units == .mmolL ? $0.asMmolL : Decimal($0)) as NSNumber)! }
@@ -59,10 +60,10 @@ struct CurrentGlucoseView: View {
                 image
             }
             HStack {
-                let minutes = (recentGlucose?.dateString.timeIntervalSinceNow ?? 0) / 60
-                let text = timaAgoFormatter.string(for: Double(minutes)) ?? ""
+                let minutesAgo = -1 * (recentGlucose?.dateString.timeIntervalSinceNow ?? 0) / 60
+                let text = timaAgoFormatter.string(for: Double(minutesAgo)) ?? ""
                 Text(
-                    text == "0" ? "< 1 " + NSLocalizedString("min", comment: "Short form for minutes") : (
+                    minutesAgo <= 1 ? "< 1 " + NSLocalizedString("min", comment: "Short form for minutes") : (
                         text + " " +
                             NSLocalizedString("min", comment: "Short form for minutes") + " "
                     )

+ 4 - 0
FreeAPS/Sources/Modules/Home/View/HomeRootView.swift

@@ -122,6 +122,7 @@ extension Home {
         var glucoseView: some View {
             CurrentGlucoseView(
                 recentGlucose: $state.recentGlucose,
+                timerDate: $state.timerDate,
                 delta: $state.glucoseDelta,
                 units: $state.units,
                 alarm: $state.alarm,
@@ -617,6 +618,9 @@ extension Home {
                         .padding(.bottom, 4)
                         .padding(.top, 8)
                     Text(errorMessage).font(.caption).foregroundColor(.loopRed)
+                } else if let suggestion = state.suggestion, (suggestion.bg ?? 100) == 400 {
+                    Text("Invalid CGM reading (HIGH).").font(.callout).bold().foregroundColor(.loopRed).padding(.top, 8)
+                    Text("SMBs and High Temps Disabled.").font(.caption).foregroundColor(.white).padding(.bottom, 4)
                 }
             }
         }

+ 4 - 0
FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift

@@ -45,6 +45,10 @@ extension NightscoutConfig {
         }
 
         func connect() {
+            if let CheckURL = url.last, CheckURL == "/" {
+                let fixedURL = url.dropLast()
+                url = String(fixedURL)
+            }
             guard let url = URL(string: url) else {
                 message = "Invalid URL"
                 return

+ 20 - 1
FreeAPS/Sources/Modules/Settings/SettingsStateModel.swift

@@ -25,7 +25,26 @@ extension Settings {
 
             versionNumber = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
 
-            branch = Bundle.main.infoDictionary?["BuildBranch"] as? String ?? "Unknown"
+            // Read branch information from the branch.txt instead of infoDictionary
+            if let branchFileURL = Bundle.main.url(forResource: "branch", withExtension: "txt"),
+               let branchFileContent = try? String(contentsOf: branchFileURL)
+            {
+                let lines = branchFileContent.components(separatedBy: .newlines)
+                for line in lines {
+                    let components = line.components(separatedBy: "=")
+                    if components.count == 2 {
+                        let key = components[0].trimmingCharacters(in: .whitespaces)
+                        let value = components[1].trimmingCharacters(in: .whitespaces)
+
+                        if key == "BRANCH" {
+                            branch = value
+                            break
+                        }
+                    }
+                }
+            } else {
+                branch = "Unknown"
+            }
 
             copyrightNotice = Bundle.main.infoDictionary?["NSHumanReadableCopyright"] as? String ?? ""
 

+ 1 - 1
FreeAPS/Sources/Modules/Settings/View/SettingsRootView.swift

@@ -12,7 +12,7 @@ extension Settings {
             Form {
                 Section(
                     header: Text(
-                        "iAPS v\(state.versionNumber) - \(state.buildNumber) \nBranch: \(state.branch) \(state.copyrightNotice) "
+                        "iAPS v\(state.versionNumber) (\(state.buildNumber))\nBranch: \(state.branch) \(state.copyrightNotice) "
                     ).textCase(nil)
                 ) {
                     Toggle("Closed loop", isOn: $state.closedLoop)

+ 16 - 0
FreeAPS/Sources/Services/Calendar/CalendarManager.swift

@@ -39,6 +39,22 @@ final class BaseCalendarManager: CalendarManager, Injectable {
                 promise(.success(false))
             case .authorized:
                 promise(.success(true))
+
+            #if swift(>=5.9)
+                case .fullAccess:
+                    promise(.success(true))
+                case .writeOnly:
+                    if #available(iOS 17.0, *) {
+                        EKEventStore().requestFullAccessToEvents(completion: { (granted: Bool, error: Error?) -> Void in
+                            if let error = error {
+                                print("Calendar access not upgraded")
+                                warning(.service, "Calendar access not upgraded", error: error)
+                            }
+                            promise(.success(granted))
+                        })
+                    }
+            #endif
+
             @unknown default:
                 warning(.service, "Unknown calendar access status")
                 promise(.success(false))

+ 4 - 1
FreeAPS/Sources/Services/Network/NightscoutManager.swift

@@ -540,9 +540,12 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         guard !glucose.isEmpty, let nightscout = nightscoutAPI, isUploadEnabled, isUploadGlucoseEnabled else {
             return
         }
+        // check if unique code
+        // var uuid = UUID(uuidString: yourString) This will return nil if yourString is not a valid UUID
+        let glucoseWithoutCorrectID = glucose.filter { UUID(uuidString: $0._id) != nil }
 
         processQueue.async {
-            glucose.chunks(ofCount: 100)
+            glucoseWithoutCorrectID.chunks(ofCount: 100)
                 .map { chunk -> AnyPublisher<Void, Error> in
                     nightscout.uploadGlucose(Array(chunk))
                 }

+ 2 - 2
FreeAPS/Sources/Shortcuts/State/ListStateView.swift

@@ -76,10 +76,10 @@ struct ListStateView: View {
                 image
             }
             HStack {
-                let minutes = state.date.timeIntervalSinceNow / 60
+                let minutes = -1 * state.date.timeIntervalSinceNow / 60
                 let text = timaAgoFormatter.string(for: Double(minutes)) ?? ""
                 Text(
-                    text == "0" ? "< 1 " + NSLocalizedString("min", comment: "Short form for minutes") : (
+                    minutes <= 1 ? "< 1 " + NSLocalizedString("min", comment: "Short form for minutes") : (
                         text + " " +
                             NSLocalizedString("min", comment: "Short form for minutes") + " "
                     )