Ivan Valkou 5 лет назад
Родитель
Сommit
532a7828ce

+ 6 - 0
FreeAPS/Sources/APS/APSManager.swift

@@ -90,6 +90,7 @@ final class BaseAPSManager: APSManager, Injectable {
                         $0.suggestionDidUpdate(suggested)
                         $0.suggestionDidUpdate(suggested)
                     }
                     }
                 }
                 }
+                self.nightscout.uploadStatus()
             }
             }
 
 
             if ok, self.settings.closedLoop {
             if ok, self.settings.closedLoop {
@@ -309,7 +310,9 @@ final class BaseAPSManager: APSManager, Injectable {
                 debug(.apsManager, "Loop succeeded")
                 debug(.apsManager, "Loop succeeded")
                 var enacted = suggested
                 var enacted = suggested
                 enacted.timestamp = Date()
                 enacted.timestamp = Date()
+                enacted.recieved = true
                 try? self?.storage.save(enacted, as: OpenAPS.Enact.enacted)
                 try? self?.storage.save(enacted, as: OpenAPS.Enact.enacted)
+                self?.nightscout.uploadStatus()
             }.store(in: &lifetime)
             }.store(in: &lifetime)
     }
     }
 }
 }
@@ -369,6 +372,9 @@ private extension PumpManager {
 extension BaseAPSManager: PumpManagerStatusObserver {
 extension BaseAPSManager: PumpManagerStatusObserver {
     func pumpManager(_: PumpManager, didUpdate status: PumpManagerStatus, oldStatus _: PumpManagerStatus) {
     func pumpManager(_: PumpManager, didUpdate status: PumpManagerStatus, oldStatus _: PumpManagerStatus) {
         try? storage.save(status.pumpStatus, as: OpenAPS.Monitor.status)
         try? storage.save(status.pumpStatus, as: OpenAPS.Monitor.status)
+        let percent = Int((status.pumpBatteryChargeRemaining ?? 1) * 100)
+        let battery = Battery(percent: percent, voltage: nil, string: percent > 10 ? .normal : .low)
+        try? storage.save(battery, as: OpenAPS.Monitor.battery)
     }
     }
 }
 }
 
 

+ 6 - 6
FreeAPS/Sources/Models/NightscoutStatus.swift

@@ -9,17 +9,17 @@ struct NightscoutStatus: JSON {
 }
 }
 
 
 struct OpenAPSStatus: JSON {
 struct OpenAPSStatus: JSON {
-    let iob: IOBEntry
-    let suggested: Suggestion
-    let enacted: Suggestion
+    let iob: IOBEntry?
+    let suggested: Suggestion?
+    let enacted: Suggestion?
     let version: String
     let version: String
 }
 }
 
 
 struct NSPumpStatus: JSON {
 struct NSPumpStatus: JSON {
     let clock: Date
     let clock: Date
-    let battery: Battery
-    let reservoir: Decimal
-    let status: PumpStatus
+    let battery: Battery?
+    let reservoir: Decimal?
+    let status: PumpStatus?
 }
 }
 
 
 struct Uploader: JSON {
 struct Uploader: JSON {

+ 2 - 0
FreeAPS/Sources/Models/Suggestion.swift

@@ -18,6 +18,7 @@ struct Suggestion: JSON {
     let tick: String?
     let tick: String?
     let reservoir: Decimal?
     let reservoir: Decimal?
     var timestamp: Date?
     var timestamp: Date?
+    var recieved: Bool?
 }
 }
 
 
 struct Predictions: JSON {
 struct Predictions: JSON {
@@ -46,6 +47,7 @@ extension Suggestion {
         case tick
         case tick
         case reservoir
         case reservoir
         case timestamp
         case timestamp
+        case recieved
     }
     }
 }
 }
 
 

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

@@ -11,6 +11,7 @@ class NightscoutAPI {
     private enum Config {
     private enum Config {
         static let entriesPath = "/api/v1/entries/sgv.json"
         static let entriesPath = "/api/v1/entries/sgv.json"
         static let treatmentsPath = "/api/v1/treatments.json"
         static let treatmentsPath = "/api/v1/treatments.json"
+        static let statusPath = "/api/v1/devicestatus.json"
         static let retryCount = 1
         static let retryCount = 1
         static let timeout: TimeInterval = 30
         static let timeout: TimeInterval = 30
     }
     }
@@ -219,6 +220,30 @@ extension NightscoutAPI {
             .map { _ in () }
             .map { _ in () }
             .eraseToAnyPublisher()
             .eraseToAnyPublisher()
     }
     }
+
+    func uploadStatus(_ status: NightscoutStatus) -> AnyPublisher<Void, Swift.Error> {
+        var components = URLComponents()
+        components.scheme = url.scheme
+        components.host = url.host
+        components.port = url.port
+        components.path = Config.statusPath
+
+        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(status)
+        request.httpMethod = "POST"
+
+        return service.run(request)
+            .retry(Config.retryCount)
+            .map { _ in () }
+            .eraseToAnyPublisher()
+    }
 }
 }
 
 
 private extension String {
 private extension String {

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

@@ -1,12 +1,14 @@
 import Combine
 import Combine
 import Foundation
 import Foundation
 import Swinject
 import Swinject
+import UIKit
 
 
 protocol NightscoutManager {
 protocol NightscoutManager {
     func fetchGlucose() -> AnyPublisher<Void, Never>
     func fetchGlucose() -> AnyPublisher<Void, Never>
     func fetchCarbs() -> AnyPublisher<Void, Never>
     func fetchCarbs() -> AnyPublisher<Void, Never>
     func fetchTempTargets() -> AnyPublisher<Void, Never>
     func fetchTempTargets() -> AnyPublisher<Void, Never>
     func fetchAnnouncements() -> AnyPublisher<Void, Never>
     func fetchAnnouncements() -> AnyPublisher<Void, Never>
+    func uploadStatus()
 }
 }
 
 
 final class BaseNightscoutManager: NightscoutManager, Injectable {
 final class BaseNightscoutManager: NightscoutManager, Injectable {
@@ -17,6 +19,7 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
     @Injected() private var pumpHistoryStorage: PumpHistoryStorage!
     @Injected() private var pumpHistoryStorage: PumpHistoryStorage!
     @Injected() private var storage: FileStorage!
     @Injected() private var storage: FileStorage!
     @Injected() private var announcementsStorage: AnnouncementsStorage!
     @Injected() private var announcementsStorage: AnnouncementsStorage!
+    @Injected() private var settingsManager: SettingsManager!
     @Injected() private var broadcaster: Broadcaster!
     @Injected() private var broadcaster: Broadcaster!
 
 
     private let processQueue = DispatchQueue(label: "BaseNetworkManager.processQueue")
     private let processQueue = DispatchQueue(label: "BaseNetworkManager.processQueue")
@@ -101,7 +104,63 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
             }.eraseToAnyPublisher()
             }.eraseToAnyPublisher()
     }
     }
 
 
-    private func uploadStatus() {}
+    func uploadStatus() {
+        let iob = (try? storage.retrieve(OpenAPS.Monitor.iob, as: [IOBEntry].self))?.first
+        var suggested = try? storage.retrieve(OpenAPS.Enact.suggested, as: Suggestion.self)
+        var enacted = try? storage.retrieve(OpenAPS.Enact.enacted, as: Suggestion.self)
+
+        if (suggested?.timestamp ?? .distantPast) > (enacted?.timestamp ?? .distantPast) {
+            enacted?.predictions = nil
+        } else {
+            suggested?.predictions = nil
+        }
+
+        let openapsStatus = OpenAPSStatus(
+            iob: iob,
+            suggested: suggested,
+            enacted: enacted,
+            version: "0.7.0"
+        )
+
+        let battery = try? storage.retrieve(OpenAPS.Monitor.battery, as: Battery.self)
+        let reservoir = Decimal(from: storage.retrieveRaw(OpenAPS.Monitor.reservoir) ?? "0")
+        let pumpStatus = try? storage.retrieve(OpenAPS.Monitor.status, as: PumpStatus.self)
+
+        let pump = NSPumpStatus(clock: Date(), battery: battery, reservoir: reservoir, status: pumpStatus)
+
+        let preferences = settingsManager.preferences
+
+        let device = UIDevice.current
+
+        let uploader = Uploader(batteryVoltage: nil, battery: Int(device.batteryLevel * 100))
+
+        let status = NightscoutStatus(
+            device: "freeaps-x://" + device.name,
+            openaps: openapsStatus,
+            pump: pump,
+            preferences: preferences,
+            uploader: uploader
+        )
+
+        try? storage.save(status, as: OpenAPS.Upload.nsStatus)
+
+        guard let nightscout = nightscoutAPI else {
+            return
+        }
+
+        processQueue.async {
+            nightscout.uploadStatus(status)
+                .sink { completion in
+                    switch completion {
+                    case .finished:
+                        debug(.nightscout, "Status uploaded")
+                    case let .failure(error):
+                        debug(.nightscout, error.localizedDescription)
+                    }
+                } receiveValue: {}
+                .store(in: &self.lifetime)
+        }
+    }
 
 
     private func uploadPumpHistory() {
     private func uploadPumpHistory() {
         uploadTreatments(pumpHistoryStorage.nightscoutTretmentsNotUploaded(), fileToSave: OpenAPS.Nightscout.uploadedPumphistory)
         uploadTreatments(pumpHistoryStorage.nightscoutTretmentsNotUploaded(), fileToSave: OpenAPS.Nightscout.uploadedPumphistory)