Ivan Valkou 5 年 前
コミット
05408df78f

+ 1 - 0
FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json

@@ -3,4 +3,5 @@
     "closedLoop": false,
     "allowAnnouncements": false,
     "useAutotune": false
+    "isUploadEnabled": true
 }

+ 20 - 4
FreeAPS/Sources/APS/DeviceDataManager.swift

@@ -30,6 +30,7 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
     @Injected() private var storage: FileStorage!
 
     @Persisted(key: "BaseDeviceDataManager.lastEventDate") var lastEventDate: Date? = nil
+    @Persisted(key: "BaseDeviceDataManager.lastHeartBeatTime") var lastHeartBeatTime: Date = .distantPast
 
     let recommendsLoop = PassthroughSubject<Void, Never>()
 
@@ -47,10 +48,18 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
 
     let pumpDisplayState = CurrentValueSubject<PumpDisplayState?, Never>(nil)
 
+    var heartBeat: Timer!
+
     init(resolver: Resolver) {
         injectServices(resolver)
         setupPumpManager()
         UIDevice.current.isBatteryMonitoringEnabled = true
+
+        heartBeat = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in
+            debug(.deviceManager, "Timer Heartbeat")
+            self.updatePumpData()
+        }
+        updatePumpData()
     }
 
     func setupPumpManager() {
@@ -59,6 +68,15 @@ final class BaseDeviceDataManager: DeviceDataManager, Injectable {
         }
     }
 
+    private func updatePumpData() {
+        let now = Date()
+        guard now.timeIntervalSince(lastHeartBeatTime) >= Config.loopInterval else { return }
+        pumpManager?.ensureCurrentPumpData {
+            debug(.deviceManager, "Pump Data updated")
+            self.lastHeartBeatTime = now
+        }
+    }
+
     private func pumpManagerFromRawValue(_ rawValue: [String: Any]) -> PumpManagerUI? {
         guard let rawState = rawValue["state"] as? PumpManager.RawStateValue,
               let Manager = pumpManagerTypeFromRawValue(rawValue)
@@ -87,11 +105,9 @@ extension BaseDeviceDataManager: PumpManagerDelegate {
         UserDefaults.standard.pumpManagerRawValue = pumpManager.rawValue
     }
 
-    func pumpManagerBLEHeartbeatDidFire(_ pumpManager: PumpManager) {
+    func pumpManagerBLEHeartbeatDidFire(_: PumpManager) {
         debug(.deviceManager, "Pump Heartbeat")
-        pumpManager.ensureCurrentPumpData {
-            debug(.deviceManager, "Pump Data updated")
-        }
+        updatePumpData()
     }
 
     func pumpManagerMustProvideBLEHeartbeat(_: PumpManager) -> Bool {

+ 2 - 0
FreeAPS/Sources/Config/Config.swift

@@ -1,6 +1,8 @@
 import Foundation
+import SwiftDate
 
 enum Config {
     static let treatWarningsAsErrors = true
     static let withSignPosts = false
+    static let loopInterval = 5.minutes.timeInterval
 }

+ 1 - 0
FreeAPS/Sources/Models/FreeAPSSettings.swift

@@ -5,4 +5,5 @@ struct FreeAPSSettings: JSON {
     var closedLoop: Bool
     var allowAnnouncements: Bool
     var useAutotune: Bool
+    var isUploadEnabled: Bool?
 }

+ 1 - 1
FreeAPS/Sources/Modules/AutotuneConfig/View/AutotuneConfigRootView.swift

@@ -7,7 +7,7 @@ extension AutotuneConfig {
         private var isfFormatter: NumberFormatter {
             let formatter = NumberFormatter()
             formatter.numberStyle = .decimal
-            formatter.maximumFractionDigits = 1
+            formatter.maximumFractionDigits = 2
             return formatter
         }
 

+ 1 - 0
FreeAPS/Sources/Modules/CREditor/CREditorDataFlow.swift

@@ -26,4 +26,5 @@ enum CREditor {
 protocol CREditorProvider: Provider {
     var profile: CarbRatios { get }
     func saveProfile(_ profile: CarbRatios)
+    var autotune: Autotune? { get }
 }

+ 4 - 0
FreeAPS/Sources/Modules/CREditor/CREditorProvider.swift

@@ -11,5 +11,9 @@ extension CREditor {
         func saveProfile(_ profile: CarbRatios) {
             try? storage.save(profile, as: OpenAPS.Settings.carbRatios)
         }
+
+        var autotune: Autotune? {
+            try? storage.retrieve(OpenAPS.Settings.autotune, as: Autotune.self)
+        }
     }
 }

+ 3 - 0
FreeAPS/Sources/Modules/CREditor/CREditorViewModel.swift

@@ -3,6 +3,7 @@ import SwiftUI
 extension CREditor {
     class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: CREditorProvider {
         @Published var items: [Item] = []
+        @Published var autotune: Autotune?
 
         let timeValues = stride(from: 0.0, to: 1.days.timeInterval, by: 30.minutes.timeInterval).map { $0 }
 
@@ -19,6 +20,8 @@ extension CREditor {
                 let rateIndex = rateValues.firstIndex(of: Double(value.ratio)) ?? 0
                 return Item(rateIndex: rateIndex, timeIndex: timeIndex)
             }
+
+            autotune = provider.autotune
         }
 
         func add() {

+ 10 - 0
FreeAPS/Sources/Modules/CREditor/View/CREditorRootView.swift

@@ -20,6 +20,16 @@ extension CREditor {
 
         var body: some View {
             Form {
+                if let autotune = viewModel.autotune {
+                    Section(header: Text("Autotune")) {
+                        HStack {
+                            Text("Calculated Ratio")
+                            Spacer()
+                            Text(rateFormatter.string(from: autotune.carbRatio as NSNumber) ?? "0")
+                            Text("g/U").foregroundColor(.secondary)
+                        }
+                    }
+                }
                 Section(header: Text("Schedule")) {
                     list
                     addButton

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

@@ -25,6 +25,10 @@ struct GlucoseChartView: UIViewRepresentable {
     }
 
     private func makeDataPointsFor(view: LineChartView) {
+        guard !glucose.isEmpty else {
+            return
+        }
+
         let dataPoints = glucose.map {
             ChartDataEntry(x: $0.dateString.timeIntervalSince1970, y: Double($0.sgv ?? 0))
         }

+ 1 - 0
FreeAPS/Sources/Modules/ISFEditor/ISFEditorDataFlow.swift

@@ -27,4 +27,5 @@ protocol ISFEditorProvider: Provider {
     var profile: InsulinSensitivities { get }
     func saveProfile(_ profile: InsulinSensitivities)
     var autosense: Autosens { get }
+    var autotune: Autotune? { get }
 }

+ 4 - 0
FreeAPS/Sources/Modules/ISFEditor/ISFEditorProvider.swift

@@ -19,5 +19,9 @@ extension ISFEditor {
                 ?? Autosens(from: OpenAPS.defaults(for: OpenAPS.Settings.autosense))
                 ?? Autosens(ratio: 1, newisf: nil, timestamp: nil)
         }
+
+        var autotune: Autotune? {
+            try? storage.retrieve(OpenAPS.Settings.autotune, as: Autotune.self)
+        }
     }
 }

+ 3 - 1
FreeAPS/Sources/Modules/ISFEditor/ISFEditorViewModel.swift

@@ -6,6 +6,7 @@ extension ISFEditor {
         @Published var items: [Item] = []
         private(set) var autosensISF: Double?
         private(set) var autosensRatio: Double = 0
+        @Published var autotune: Autotune?
 
         let timeValues = stride(from: 0.0, to: 1.days.timeInterval, by: 30.minutes.timeInterval).map { $0 }
 
@@ -33,8 +34,9 @@ extension ISFEditor {
                 let rateIndex = rateValues.firstIndex(of: Double(value.sensitivity)) ?? 0
                 return Item(rateIndex: rateIndex, timeIndex: timeIndex)
             }
+            autotune = provider.autotune
 
-            if let newISF = provider.autosense.newisf, provider.autosense.ratio != 1 {
+            if let newISF = provider.autosense.newisf {
                 switch units {
                 case .mgdL:
                     autosensISF = Double(newISF)

+ 18 - 3
FreeAPS/Sources/Modules/ISFEditor/View/ISFEditorRootView.swift

@@ -21,17 +21,32 @@ extension ISFEditor {
 
         var body: some View {
             Form {
+                if let autotune = viewModel.autotune {
+                    Section(header: Text("Autotune")) {
+                        HStack {
+                            Text("Calculated Sensitivity")
+                            Spacer()
+                            if viewModel.units == .mmolL {
+                                Text(rateFormatter.string(from: autotune.sensitivity.asMmolL as NSNumber) ?? "0")
+                            } else {
+                                Text(rateFormatter.string(from: autotune.sensitivity as NSNumber) ?? "0")
+                            }
+                            Text(viewModel.units.rawValue + "/U").foregroundColor(.secondary)
+                        }
+                    }
+                }
                 if let newISF = viewModel.autosensISF {
                     Section(header: Text("Autosens")) {
                         HStack {
-                            Text("Sensitivity Ratio").foregroundColor(.secondary)
+                            Text("Sensitivity Ratio")
                             Spacer()
                             Text(rateFormatter.string(from: viewModel.autosensRatio as NSNumber) ?? "1")
                         }
                         HStack {
-                            Text("Calculated ISF").foregroundColor(.secondary)
+                            Text("Calculated Sensitivity")
                             Spacer()
-                            Text((rateFormatter.string(from: newISF as NSNumber) ?? "0") + " \(viewModel.units.rawValue)/U")
+                            Text(rateFormatter.string(from: newISF as NSNumber) ?? "0")
+                            Text(viewModel.units.rawValue + "/U").foregroundColor(.secondary)
                         }
                     }
                 }

+ 8 - 0
FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigViewModel.swift

@@ -4,15 +4,23 @@ import SwiftUI
 extension NightscoutConfig {
     class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: NightscoutConfigProvider {
         @Injected() var keychain: Keychain!
+        @Injected() var settingsManager: SettingsManager!
 
         @Published var url = ""
         @Published var secret = ""
         @Published var message = ""
         @Published var connecting = false
+        @Published var isUploadEnabled = false
 
         override func subscribe() {
             url = keychain.getValue(String.self, forKey: Config.urlKey) ?? ""
             secret = keychain.getValue(String.self, forKey: Config.secretKey) ?? ""
+            isUploadEnabled = settingsManager.settings.isUploadEnabled ?? false
+            $isUploadEnabled
+                .removeDuplicates()
+                .sink { [weak self] enabled in
+                    self?.settingsManager.settings.isUploadEnabled = enabled
+                }.store(in: &lifetime)
         }
 
         func connect() {

+ 4 - 0
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift

@@ -34,6 +34,10 @@ extension NightscoutConfig {
                         .disabled(viewModel.url.isEmpty || viewModel.secret.isEmpty || viewModel.connecting)
                     Button("Delete") { viewModel.delete() }.foregroundColor(.red).disabled(viewModel.connecting)
                 }
+
+                Section {
+                    Toggle("Allow uploads", isOn: $viewModel.isUploadEnabled)
+                }
             }
             .navigationBarTitle("Nightscout Config", displayMode: .automatic)
         }

+ 6 - 2
FreeAPS/Sources/Services/Network/NightscoutManager.swift

@@ -26,6 +26,10 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
 
     private var lifetime = Set<AnyCancellable>()
 
+    private var isUploadEnabled: Bool {
+        settingsManager.settings.isUploadEnabled ?? false
+    }
+
     private var nightscoutAPI: NightscoutAPI? {
         guard let urlString = keychain.getValue(String.self, forKey: NightscoutConfig.Config.urlKey),
               let url = URL(string: urlString),
@@ -144,7 +148,7 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
 
         try? storage.save(status, as: OpenAPS.Upload.nsStatus)
 
-        guard let nightscout = nightscoutAPI else {
+        guard let nightscout = nightscoutAPI, isUploadEnabled else {
             return
         }
 
@@ -175,7 +179,7 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
     }
 
     private func uploadTreatments(_ treatments: [NigtscoutTreatment], fileToSave: String) {
-        guard !treatments.isEmpty, let nightscout = nightscoutAPI else {
+        guard !treatments.isEmpty, let nightscout = nightscoutAPI, isUploadEnabled else {
             return
         }