Ivan Valkou пре 5 година
родитељ
комит
03039a18d7

+ 2 - 2
FreeAPS.xcodeproj/xcuserdata/i.valkou.xcuserdatad/xcschemes/xcschememanagement.plist

@@ -28,12 +28,12 @@
 		<key>FreeAPS X.xcscheme_^#shared#^_</key>
 		<dict>
 			<key>orderHint</key>
-			<integer>4</integer>
+			<integer>3</integer>
 		</dict>
 		<key>FreeAPS.xcscheme_^#shared#^_</key>
 		<dict>
 			<key>orderHint</key>
-			<integer>1</integer>
+			<integer>0</integer>
 		</dict>
 		<key>ReactiveSwift (Playground) 1.xcscheme</key>
 		<dict>

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

@@ -0,0 +1 @@
+[]

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

@@ -57,6 +57,11 @@ final class BaseAPSManager: APSManager, Injectable {
     }
 
     func fetchAndLoop() {
+        guard pumpManager != nil else {
+            loop()
+            return
+        }
+
         remoteCancellable = nightscout.fetchAnnouncements()
             .sink { [weak self] in
                 if let recent = self?.announcementsStorage.recent(), recent.action != nil {
@@ -120,6 +125,7 @@ final class BaseAPSManager: APSManager, Injectable {
                 switch result {
                 case .success:
                     print("Announcement Bolus succeeded")
+                    self.announcementsStorage.storeAnnouncements([announcement], enacted: true)
                 case let .failure(error):
                     print("Announcement Bolus failed with error: \(error.localizedDescription)")
                 }
@@ -132,6 +138,7 @@ final class BaseAPSManager: APSManager, Injectable {
                         print("Pump not suspended by Announcement: \(error.localizedDescription)")
                     } else {
                         print("Pump suspended by Announcement")
+                        self.announcementsStorage.storeAnnouncements([announcement], enacted: true)
                     }
                 }
             case .resume:
@@ -140,9 +147,24 @@ final class BaseAPSManager: APSManager, Injectable {
                         print("Pump not resumed by Announcement: \(error.localizedDescription)")
                     } else {
                         print("Pump resumed by Announcement")
+                        self.announcementsStorage.storeAnnouncements([announcement], enacted: true)
                     }
                 }
             }
+        case let .looping(closedLoop):
+            settings.closedLoop = closedLoop
+            print("Closed loop \(closedLoop) by Announcement")
+            announcementsStorage.storeAnnouncements([announcement], enacted: true)
+        case let .tempbasal(rate, duration):
+            pumpManager?.enactTempBasal(unitsPerHour: Double(rate), for: TimeInterval(duration) * 60) { result in
+                switch result {
+                case .success:
+                    print("Announcement TempBasal succeeded")
+                    self.announcementsStorage.storeAnnouncements([announcement], enacted: true)
+                case let .failure(error):
+                    print("Announcement TempBasal failed with error: \(error.localizedDescription)")
+                }
+            }
         }
     }
 

+ 20 - 5
FreeAPS/Sources/APS/Storage/AnnouncementsStorage.swift

@@ -9,6 +9,10 @@ protocol AnnouncementsStorage {
 }
 
 final class BaseAnnouncementsStorage: AnnouncementsStorage, Injectable {
+    enum Config {
+        static let recentInterval = 10.minutes.timeInterval
+    }
+
     private let processQueue = DispatchQueue(label: "BaseAnnouncementsStorage.processQueue")
     @Injected() private var storage: FileStorage!
 
@@ -39,14 +43,25 @@ final class BaseAnnouncementsStorage: AnnouncementsStorage, Injectable {
     }
 
     func recent() -> Announcement? {
-        guard let events = try? storage.retrieve(OpenAPS.FreeAPS.announcements, as: [Announcement].self) else { return nil }
+        guard let events = try? storage.retrieve(OpenAPS.FreeAPS.announcements, as: [Announcement].self)
+        else {
+            return nil
+        }
         guard let recent = events
-            .filter({ $0.enteredBy != Announcement.remote && $0.createdAt.addingTimeInterval(10.minutes.timeInterval) > Date() })
-            .first else { return nil }
+            .filter({
+                $0.enteredBy == Announcement.remote && $0.createdAt.addingTimeInterval(Config.recentInterval) > Date()
+            })
+            .first
+        else {
+            return nil
+        }
         guard let enactedEvents = try? storage.retrieve(OpenAPS.FreeAPS.announcementsEnacted, as: [Announcement].self)
-        else { return recent }
+        else {
+            return recent
+        }
 
-        guard enactedEvents.first(where: { $0.createdAt == recent.createdAt }) == nil else {
+        guard enactedEvents.first(where: { $0.createdAt == recent.createdAt }) == nil
+        else {
             return nil
         }
         return recent

+ 17 - 4
FreeAPS/Sources/Models/Announcement.swift

@@ -12,14 +12,25 @@ struct Announcement: JSON {
         guard components.count == 2 else {
             return nil
         }
-
-        switch String(components[0]) {
+        let command = String(components[0])
+        let arguments = String(components[1])
+        switch command {
         case "bolus":
-            guard let amount = Decimal(from: String(components[1])) else { return nil }
+            guard let amount = Decimal(from: arguments) else { return nil }
             return .bolus(amount)
         case "pump":
-            guard let action = PumpAction(rawValue: String(components[1])) else { return nil }
+            guard let action = PumpAction(rawValue: arguments) else { return nil }
             return .pump(action)
+        case "looping":
+            guard let looping = Bool(from: arguments) else { return nil }
+            return .looping(looping)
+        case "tempbasal":
+            let basalComponents = arguments.split(separator: ",")
+            guard basalComponents.count == 2 else { return nil }
+            let rateArg = String(basalComponents[0])
+            let durationArg = String(basalComponents[1])
+            guard let rate = Decimal(from: rateArg), let duration = Decimal(from: durationArg) else { return nil }
+            return .tempbasal(rate: rate, duration: duration)
         default: return nil
         }
     }
@@ -36,6 +47,8 @@ extension Announcement {
 enum AnnouncementAction {
     case bolus(Decimal)
     case pump(PumpAction)
+    case looping(Bool)
+    case tempbasal(rate: Decimal, duration: Decimal)
 }
 
 enum PumpAction: String {

+ 9 - 0
FreeAPS/Sources/Modules/Settings/SettingsViewModel.swift

@@ -3,6 +3,7 @@ import SwiftUI
 extension Settings {
     class ViewModel<Provider>: BaseViewModel<Provider>, ObservableObject where Provider: SettingsProvider {
         @Injected() private var settingsManager: SettingsManager!
+        @Injected() private var broadcaster: Broadcaster!
         @Published var closedLoop = false
 
         override func subscribe() {
@@ -13,6 +14,14 @@ extension Settings {
                 .sink { [weak self] value in
                     self?.settingsManager.settings.closedLoop = value
                 }.store(in: &lifetime)
+
+            broadcaster.register(SettingsObserver.self, observer: self)
         }
     }
 }
+
+extension Settings.ViewModel: SettingsObserver {
+    func settingsDidChange(_ settings: FreeAPSSettings) {
+        closedLoop = settings.closedLoop
+    }
+}

+ 13 - 1
FreeAPS/Sources/Services/SettingsManager/SettingsManager.swift

@@ -5,9 +5,21 @@ protocol SettingsManager {
     var settings: FreeAPSSettings { get set }
 }
 
+protocol SettingsObserver {
+    func settingsDidChange(_: FreeAPSSettings)
+}
+
 final class BaseSettingsManager: SettingsManager, Injectable {
+    @Injected() var broadcaster: Broadcaster!
     var settings: FreeAPSSettings {
-        didSet { save() }
+        didSet {
+            save()
+            DispatchQueue.main.async {
+                self.broadcaster.notify(SettingsObserver.self, on: .main) {
+                    $0.settingsDidChange(self.settings)
+                }
+            }
+        }
     }
 
     @Injected() var storage: FileStorage!