Selaa lähdekoodia

Upload profile to nightscout upon settings change (triggered by exiting the menu)

Jan Dittmer 4 vuotta sitten
vanhempi
commit
60cffa96c3

+ 1 - 0
FreeAPS/Sources/APS/OpenAPS/Constants.swift

@@ -77,6 +77,7 @@ extension OpenAPS {
         static let uploadedCarbs = "upload/uploaded-carbs.json"
         static let uploadedTempTargets = "upload/uploaded-temptargets.json"
         static let uploadedGlucose = "upload/uploaded-glucose.json"
+        static let uploadedProfile = "upload/uploaded-profile.json"
     }
 
     enum FreeAPS {

+ 30 - 0
FreeAPS/Sources/Models/NightscoutStatus.swift

@@ -26,3 +26,33 @@ struct Uploader: JSON {
     let batteryVoltage: Decimal?
     let battery: Int
 }
+
+struct NightscoutTimevalue: JSON {
+    // rep["time"] = String(format:"%02i:%02i", Int(hours), Int(minutes))
+    // rep["value"] = value
+    //  rep["timeAsSeconds"] = Int(offset)
+    let time: String
+    let value: Decimal
+    let timeAsSeconds: Int
+}
+
+struct ScheduledNightscoutProfile: JSON {
+    let dia: Decimal
+    let carbs_hr: Decimal
+    let delay: Decimal
+    let timezone: String
+    let target_low: [NightscoutTimevalue]
+    let target_high: [NightscoutTimevalue]
+    let sens: [NightscoutTimevalue]
+    let basal: [NightscoutTimevalue]
+    let carbratio: [NightscoutTimevalue]
+}
+
+struct NightscoutProfileStore: JSON {
+    let defaultProfile: String
+    let startDate: Date
+    let mills: Int
+    let units: String
+    let enteredBy: String
+    let store: [String: ScheduledNightscoutProfile]
+}

+ 11 - 0
FreeAPS/Sources/Modules/Settings/SettingsStateModel.swift

@@ -4,6 +4,7 @@ extension Settings {
     final class StateModel: BaseStateModel<Provider> {
         @Injected() private var broadcaster: Broadcaster!
         @Injected() private var fileManager: FileManager!
+        @Injected() private var nightscoutManager: NightscoutManager!
 
         @Published var closedLoop = false
         @Published var debugOptions = false
@@ -35,6 +36,16 @@ extension Settings {
 
             return items
         }
+
+        func uploadProfile() {
+            NSLog("SettingsState Upload Profile")
+            nightscoutManager.uploadProfile()
+        }
+
+        func hideSettingsModal() {
+            nightscoutManager.uploadProfile()
+            hideModal()
+        }
     }
 }
 

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

@@ -41,6 +41,13 @@ extension Settings {
                     Toggle("Debug options", isOn: $state.debugOptions)
                     if state.debugOptions {
                         Group {
+                            Text("NS Upload Profile").onTapGesture {
+                                state.uploadProfile()
+                            }
+                            Text("NS Uploaded Profile")
+                                .navigationLink(to: .configEditor(file: OpenAPS.Nightscout.uploadedProfile), from: self)
+                        }
+                        Group {
                             Text("Preferences")
                                 .navigationLink(to: .configEditor(file: OpenAPS.Settings.preferences), from: self)
                             Text("Pump Settings")
@@ -117,7 +124,7 @@ extension Settings {
             }
             .onAppear(perform: configureView)
             .navigationTitle("Settings")
-            .navigationBarItems(leading: Button("Close", action: state.hideModal))
+            .navigationBarItems(leading: Button("Close", action: state.hideSettingsModal))
             .navigationBarTitleDisplayMode(.automatic)
         }
     }

+ 25 - 0
FreeAPS/Sources/Services/Network/NightscoutAPI.swift

@@ -13,6 +13,7 @@ class NightscoutAPI {
         static let uploadEntriesPath = "/api/v1/entries.json"
         static let treatmentsPath = "/api/v1/treatments.json"
         static let statusPath = "/api/v1/devicestatus.json"
+        static let profilePath = "/api/v1/profile.json"
         static let retryCount = 1
         static let timeout: TimeInterval = 60
     }
@@ -315,6 +316,30 @@ extension NightscoutAPI {
             .map { _ in () }
             .eraseToAnyPublisher()
     }
+
+    func uploadProfile(_ profile: NightscoutProfileStore) -> AnyPublisher<Void, Swift.Error> {
+        var components = URLComponents()
+        components.scheme = url.scheme
+        components.host = url.host
+        components.port = url.port
+        components.path = Config.profilePath
+
+        var request = URLRequest(url: components.url!)
+        request.allowsConstrainedNetworkAccess = false
+        request.timeoutInterval = Config.timeout
+        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+
+        if let secret = secret {
+            request.addValue(secret.sha1(), forHTTPHeaderField: "api-secret")
+        }
+        request.httpBody = try! JSONCoding.encoder.encode(profile)
+        request.httpMethod = "POST"
+
+        return service.run(request)
+            .retry(Config.retryCount)
+            .map { _ in () }
+            .eraseToAnyPublisher()
+    }
 }
 
 private extension String {

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

@@ -11,6 +11,7 @@ protocol NightscoutManager: GlucoseSource {
     func deleteCarbs(at date: Date)
     func uploadStatus()
     func uploadGlucose()
+    func uploadProfile()
     var cgmURL: URL? { get }
 }
 
@@ -211,7 +212,7 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         let uploader = Uploader(batteryVoltage: nil, battery: Int(device.batteryLevel * 100))
 
         let status = NightscoutStatus(
-            device: "freeaps-x://" + device.name,
+            device: NigtscoutTreatment.local,
             openaps: openapsStatus,
             pump: pump,
             preferences: preferences,
@@ -238,6 +239,102 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         }
     }
 
+    func uploadProfile() {
+        // These should be modified anyways and not the defaults
+        guard let sensitivities = storage.retrieve(OpenAPS.Settings.insulinSensitivities, as: InsulinSensitivities.self),
+              let basalProfile = storage.retrieve(OpenAPS.Settings.basalProfile, as: [BasalProfileEntry].self),
+              let carbRatios = storage.retrieve(OpenAPS.Settings.carbRatios, as: CarbRatios.self),
+              let targets = storage.retrieve(OpenAPS.Settings.bgTargets, as: BGTargets.self)
+        else {
+            NSLog("NightscoutManager uploadProfile Not all settings found to build profile!")
+            return
+        }
+
+        let sens = sensitivities.sensitivities.map { item -> NightscoutTimevalue in
+            NightscoutTimevalue(
+                time: String(item.start.prefix(5)),
+                value: item.sensitivity,
+                timeAsSeconds: item.offset
+            )
+        }
+
+        let target_low = targets.targets.map { item -> NightscoutTimevalue in
+            NightscoutTimevalue(
+                time: String(item.start.prefix(5)),
+                value: item.low,
+                timeAsSeconds: item.offset
+            )
+        }
+        let target_high = targets.targets.map { item -> NightscoutTimevalue in
+            NightscoutTimevalue(
+                time: String(item.start.prefix(5)),
+                value: item.high,
+                timeAsSeconds: item.offset
+            )
+        }
+        let cr = carbRatios.schedule.map { item -> NightscoutTimevalue in
+            NightscoutTimevalue(
+                time: String(item.start.prefix(5)),
+                value: item.ratio,
+                timeAsSeconds: item.offset
+            )
+        }
+
+        let basal = basalProfile.map { item -> NightscoutTimevalue in
+            NightscoutTimevalue(
+                time: String(item.start.prefix(5)),
+                value: item.rate,
+                timeAsSeconds: item.minutes * 60
+            )
+        }
+
+        let ps = ScheduledNightscoutProfile(
+            dia: settingsManager.pumpSettings.insulinActionCurve,
+            carbs_hr: settingsManager.preferences.min5mCarbimpact * 12,
+            delay: 0,
+            timezone: TimeZone.current.identifier,
+            target_low: target_low,
+            target_high: target_high,
+            sens: sens,
+            basal: basal,
+            carbratio: cr
+        )
+        let defaultProfile = "default"
+        let now = Date()
+        let p = NightscoutProfileStore(
+            defaultProfile: defaultProfile,
+            startDate: now,
+            mills: Int(now.timeIntervalSince1970),
+            units: String(describing: settingsManager.settings.units),
+            enteredBy: NigtscoutTreatment.local,
+            store: [defaultProfile: ps]
+        )
+        NSLog(p.rawJSON)
+
+        if let uploadedProfile = storage.retrieve(OpenAPS.Nightscout.uploadedProfile, as: NightscoutProfileStore.self),
+           (uploadedProfile.store[defaultProfile]?.rawJSON ?? "") == ps.rawJSON
+        {
+            NSLog("NightscoutManager uploadProfile, no profile change")
+            return
+        }
+        guard let nightscout = nightscoutAPI, isNetworkReachable, isUploadEnabled else {
+            return // Just([]).eraseToAnyPublisher()
+        }
+        processQueue.async {
+            nightscout.uploadProfile(p)
+                .sink { completion in
+                    switch completion {
+                    case .finished:
+                        self.storage.save(p, as: OpenAPS.Nightscout.uploadedProfile)
+                        debug(.nightscout, "Profile uploaded")
+                    case let .failure(error):
+                        debug(.nightscout, error.localizedDescription)
+                    }
+                } receiveValue: {}
+                .store(in: &self.lifetime)
+        }
+    }
+
     func uploadGlucose() {
         uploadGlucose(glucoseStorage.nightscoutGlucoseNotUploaded(), fileToSave: OpenAPS.Nightscout.uploadedGlucose)
     }