Bladeren bron

Merge pull request #221 from dsnallfot/dev-test-stuff

Add toggle to allow downloads from Nightscout (And improve NS Config UI)
Auggie 2 jaren geleden
bovenliggende
commit
fc1fd4b864
32 gewijzigde bestanden met toevoegingen van 240 en 139 verwijderingen
  1. 12 0
      FreeAPS.xcodeproj/project.pbxproj
  2. 1 0
      FreeAPS/Resources/json/defaults/freeaps/freeaps_settings.json
  3. 2 2
      FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings
  4. 1 1
      FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings
  5. 2 2
      FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings
  6. 2 2
      FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings
  7. 2 2
      FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings
  8. 2 2
      FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings
  9. 2 2
      FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings
  10. 2 2
      FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings
  11. 2 2
      FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings
  12. 2 2
      FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings
  13. 2 2
      FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings
  14. 2 2
      FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings
  15. 2 2
      FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings
  16. 2 2
      FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings
  17. 2 2
      FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings
  18. 2 2
      FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings
  19. 2 2
      FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings
  20. 2 2
      FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings
  21. 2 2
      FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings
  22. 2 2
      FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings
  23. 2 2
      FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings
  24. 2 2
      FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings
  25. 3 3
      FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings
  26. 5 0
      FreeAPS/Sources/Models/FreeAPSSettings.swift
  27. 2 0
      FreeAPS/Sources/Modules/NightscoutConfig/NightscoutConfigStateModel.swift
  28. 42 90
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift
  29. 58 0
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConnectView.swift
  30. 41 0
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutFetchView.swift
  31. 26 0
      FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutUploadView.swift
  32. 7 3
      FreeAPS/Sources/Services/Network/NightscoutManager.swift

+ 12 - 0
FreeAPS.xcodeproj/project.pbxproj

@@ -240,6 +240,9 @@
 		45717281F743594AA9D87191 /* ConfigEditorRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 920DDB21E5D0EB813197500D /* ConfigEditorRootView.swift */; };
 		5075C1608E6249A51495C422 /* TargetsEditorProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BDEA2DC60EDE0A3CA54DC73 /* TargetsEditorProvider.swift */; };
 		53F2382465BF74DB1A967C8B /* PumpConfigProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8630D58BDAD6D9C650B9B39 /* PumpConfigProvider.swift */; };
+		5A2325522BFCBF55003518CA /* NightscoutUploadView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */; };
+		5A2325542BFCBF66003518CA /* NightscoutFetchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */; };
+		5A2325582BFCC168003518CA /* NightscoutConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5A2325572BFCC168003518CA /* NightscoutConnectView.swift */; };
 		5BFA1C2208114643B77F8CEB /* AddTempTargetProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEE53A13D26F101B332EFFC8 /* AddTempTargetProvider.swift */; };
 		5D16287A969E64D18CE40E44 /* PumpConfigStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3F60E97100041040446F44E7 /* PumpConfigStateModel.swift */; };
 		63E890B4D951EAA91C071D5C /* BasalProfileEditorStateModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAFF91130F2FCCC7EBBA11AD /* BasalProfileEditorStateModel.swift */; };
@@ -761,6 +764,9 @@
 		44080E4709E3AE4B73054563 /* ConfigEditorProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorProvider.swift; sourceTree = "<group>"; };
 		4DD795BA46B193644D48138C /* TargetsEditorRootView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = TargetsEditorRootView.swift; sourceTree = "<group>"; };
 		505E09DC17A0C3D0AF4B66FE /* ISFEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ISFEditorStateModel.swift; sourceTree = "<group>"; };
+		5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutUploadView.swift; sourceTree = "<group>"; };
+		5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutFetchView.swift; sourceTree = "<group>"; };
+		5A2325572BFCC168003518CA /* NightscoutConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NightscoutConnectView.swift; sourceTree = "<group>"; };
 		5B8A42073A2D03A278914448 /* AddTempTargetDataFlow.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = AddTempTargetDataFlow.swift; sourceTree = "<group>"; };
 		5C018D1680307A31C9ED7120 /* CGMStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = CGMStateModel.swift; sourceTree = "<group>"; };
 		5D5B4F8B4194BB7E260EF251 /* ConfigEditorStateModel.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ConfigEditorStateModel.swift; sourceTree = "<group>"; };
@@ -1868,6 +1874,9 @@
 			isa = PBXGroup;
 			children = (
 				8782B44544F38F2B2D82C38E /* NightscoutConfigRootView.swift */,
+				5A2325512BFCBF55003518CA /* NightscoutUploadView.swift */,
+				5A2325532BFCBF65003518CA /* NightscoutFetchView.swift */,
+				5A2325572BFCC168003518CA /* NightscoutConnectView.swift */,
 			);
 			path = View;
 			sourceTree = "<group>";
@@ -2601,6 +2610,7 @@
 				CEE9A6552BBB418300EB5194 /* CalibrationsProvider.swift in Sources */,
 				19F95FF529F10FCF00314DDC /* StatProvider.swift in Sources */,
 				38F3B2EF25ED8E2A005C48AA /* TempTargetsStorage.swift in Sources */,
+				5A2325542BFCBF66003518CA /* NightscoutFetchView.swift in Sources */,
 				19B0EF2128F6D66200069496 /* Statistics.swift in Sources */,
 				3811DF1025CAAAE200A708ED /* APSManager.swift in Sources */,
 				3870FF4725EC187A0088248F /* BloodGlucose.swift in Sources */,
@@ -2720,6 +2730,7 @@
 				38E989DD25F5021400C0CED0 /* PumpStatus.swift in Sources */,
 				38E98A2525F52C9300C0CED0 /* IssueReporter.swift in Sources */,
 				190EBCC429FF136900BA767D /* StatConfigDataFlow.swift in Sources */,
+				5A2325582BFCC168003518CA /* NightscoutConnectView.swift in Sources */,
 				3811DEB025C9D88300A708ED /* BaseKeychain.swift in Sources */,
 				3811DE4325C9D4A100A708ED /* SettingsProvider.swift in Sources */,
 				45252C95D220E796FDB3B022 /* ConfigEditorDataFlow.swift in Sources */,
@@ -2864,6 +2875,7 @@
 				BA00D96F7B2FF169A06FB530 /* CGMStateModel.swift in Sources */,
 				CE94598729E9E4110047C9C6 /* WatchConfigRootView.swift in Sources */,
 				19E1F7E829D082D0005C8D20 /* IconConfigDataFlow.swift in Sources */,
+				5A2325522BFCBF55003518CA /* NightscoutUploadView.swift in Sources */,
 				E3A08AAE59538BC8A8ABE477 /* NotificationsConfigDataFlow.swift in Sources */,
 				1956FB212AFF79E200C7B4FF /* CoreDataStorage.swift in Sources */,
 				0F7A65FBD2CD8D6477ED4539 /* NotificationsConfigProvider.swift in Sources */,

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

@@ -5,6 +5,7 @@
   "useAutotune" : false,
   "onlyAutotuneBasals" : false,
   "isUploadEnabled" : false,
+  "isDownloadEnabled" : false,
   "useLocalGlucoseSource" : false,
   "localGlucosePort" : 8080,
   "debugOptions" : false,

File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/ar.lproj/Localizable.strings


+ 1 - 1
FreeAPS/Sources/Localizations/Main/ca.lproj/Localizable.strings

@@ -312,7 +312,7 @@ Enact a temp Basal or a temp target */
 "Skip Bolus screen after carbs" = "Skip Bolus screen after carbs";
 
 /* Allow remote control from NS */
-"Remote control" = "Remote control";
+"Remote Control" = "Remote Control";
 
 /* Add Medtronic pump */
 "Add Medtronic" = "Add Medtronic";

File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/da.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/de.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/en.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/es.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/fi.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/fr.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/he.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/hu.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/it.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/nb.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/nl.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/pl.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/pt-BR.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/pt-PT.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/ru.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/sk.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/sv.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/tr.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/uk.lproj/Localizable.strings


File diff suppressed because it is too large
+ 2 - 2
FreeAPS/Sources/Localizations/Main/vi.lproj/Localizable.strings


File diff suppressed because it is too large
+ 3 - 3
FreeAPS/Sources/Localizations/Main/zh-Hans.lproj/Localizable.strings


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

@@ -6,6 +6,7 @@ struct FreeAPSSettings: JSON, Equatable {
     var allowAnnouncements: Bool = false
     var useAutotune: Bool = false
     var isUploadEnabled: Bool = false
+    var isDownloadEnabled: Bool = false
     var useLocalGlucoseSource: Bool = false
     var localGlucosePort: Int = 8080
     var debugOptions: Bool = false
@@ -73,6 +74,10 @@ extension FreeAPSSettings: Decodable {
             settings.isUploadEnabled = isUploadEnabled
         }
 
+        if let isDownloadEnabled = try? container.decode(Bool.self, forKey: .isDownloadEnabled) {
+            settings.isDownloadEnabled = isDownloadEnabled
+        }
+
         if let useLocalGlucoseSource = try? container.decode(Bool.self, forKey: .useLocalGlucoseSource) {
             settings.useLocalGlucoseSource = useLocalGlucoseSource
         }

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

@@ -22,6 +22,7 @@ extension NightscoutConfig {
         @Published var connecting = false
         @Published var backfilling = false
         @Published var isUploadEnabled = false // Allow uploads
+        @Published var isDownloadEnabled = false // Allow downloads
         @Published var uploadGlucose = true // Upload Glucose
         @Published var changeUploadGlucose = true // if plugin, need to be change in CGM configuration
         @Published var useLocalSource = false
@@ -43,6 +44,7 @@ extension NightscoutConfig {
 
             subscribeSetting(\.allowAnnouncements, on: $allowAnnouncements) { allowAnnouncements = $0 }
             subscribeSetting(\.isUploadEnabled, on: $isUploadEnabled) { isUploadEnabled = $0 }
+            subscribeSetting(\.isDownloadEnabled, on: $isDownloadEnabled) { isDownloadEnabled = $0 }
             subscribeSetting(\.useLocalGlucoseSource, on: $useLocalSource) { useLocalSource = $0 }
             subscribeSetting(\.localGlucosePort, on: $localPort.map(Int.init)) { localPort = Decimal($0) }
             subscribeSetting(\.uploadGlucose, on: $uploadGlucose, initial: { uploadGlucose = $0 })

+ 42 - 90
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConfigRootView.swift

@@ -7,72 +7,36 @@ extension NightscoutConfig {
         let resolver: Resolver
         let displayClose: Bool
         @StateObject var state = StateModel()
-        @State var importAlert: Alert?
-        @State var isImportAlertPresented = false
-        @State var importedHasRun = false
+        @State private var importAlert: Alert?
+        @State private var isImportAlertPresented = false
+        @State private var importedHasRun = false
 
         @FetchRequest(
             entity: ImportError.entity(),
-            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)], predicate: NSPredicate(
-                format: "date > %@", Date().addingTimeInterval(-1.minutes.timeInterval) as NSDate
-            )
+            sortDescriptors: [NSSortDescriptor(key: "date", ascending: false)],
+            predicate: NSPredicate(format: "date > %@", Date().addingTimeInterval(-1.minutes.timeInterval) as NSDate)
         ) var fetchedErrors: FetchedResults<ImportError>
 
-        private var portFormater: NumberFormatter {
-            let formatter = NumberFormatter()
-            formatter.allowsFloats = false
-            return formatter
-        }
-
         var body: some View {
             Form {
-                Section {
-                    TextField("URL", text: $state.url)
-                        .disableAutocorrection(true)
-                        .textContentType(.URL)
-                        .autocapitalization(.none)
-                        .keyboardType(.URL)
-                    SecureField("API secret", text: $state.secret)
-                        .disableAutocorrection(true)
-                        .autocapitalization(.none)
-                        .textContentType(.password)
-                        .keyboardType(.asciiCapable)
-                    if !state.message.isEmpty {
-                        Text(state.message)
-                    }
-                    if state.connecting {
-                        HStack {
-                            Text("Connecting...")
-                            Spacer()
-                            ProgressView()
-                        }
-                    }
-                }
-
-                Section {
-                    Button("Connect") { state.connect() }
-                        .disabled(state.url.isEmpty || state.connecting)
-                    Button("Delete") { state.delete() }.foregroundColor(.red).disabled(state.connecting)
-                }
+                NavigationLink("Connect", destination: NightscoutConnectView(state: state))
+                NavigationLink("Upload", destination: NightscoutUploadView(state: state))
+                NavigationLink("Fetch and Remote Control", destination: NightscoutFetchView(state: state))
 
-                Section {
-                    Button("Open Nighstcout") {
-                        UIApplication.shared.open(URL(string: state.url)!, options: [:], completionHandler: nil)
-                    }
-                    .disabled(state.url.isEmpty || state.connecting)
-                }
-
-                Section {
-                    Toggle("Upload", isOn: $state.isUploadEnabled)
-                    if state.isUploadEnabled {
-                        Toggle("Glucose", isOn: $state.uploadGlucose).disabled(!state.changeUploadGlucose)
+                Section(
+                    header: Text("Import Settings from Nightscout"),
+                    footer: VStack(alignment: .leading, spacing: 2) {
+                        Text(
+                            "Importing settings from Nightscout will overwrite these settings in Trio Settings -> Configuration:"
+                        )
+                        Text(" • ") + Text("DIA (Pump settings)")
+                        Text(" • ") + Text("Basal Profile")
+                        Text(" • ") + Text("Insulin Sensitivities")
+                        Text(" • ") + Text("Carb Ratios")
+                        Text(" • ") + Text("Target Glucose")
                     }
-                } header: {
-                    Text("Allow Uploads")
-                }
-
-                Section {
-                    Button("Import settings from Nightscout") {
+                ) {
+                    Button("Import settings") {
                         importAlert = Alert(
                             title: Text("Import settings?"),
                             message: Text(
@@ -94,50 +58,38 @@ extension NightscoutConfig {
                         )
                         isImportAlertPresented.toggle()
                     }.disabled(state.url.isEmpty || state.connecting)
-
-                } header: { Text("Import from Nightscout") }
-
-                    .alert(isPresented: $importedHasRun) {
-                        Alert(
-                            title: Text((fetchedErrors.first?.error ?? "").count < 4 ? "Settings imported" : "Import Error"),
-                            message: Text(
-                                (fetchedErrors.first?.error ?? "").count < 4 ?
-                                    NSLocalizedString(
-                                        "\nNow please verify all of your new settings thoroughly:\n\n* Basal Settings\n * Carb Ratios\n * Glucose Targets\n * Insulin Sensitivities\n * DIA\n\n in Trio Settings > Configuration.\n\nBad or invalid profile settings could have disatrous effects.",
-                                        comment: "Imported Profiles Alert"
-                                    ) :
-                                    NSLocalizedString(fetchedErrors.first?.error ?? "", comment: "Import Error")
-                            ),
-                            primaryButton: .destructive(
-                                Text("OK")
-                            ),
-                            secondaryButton: .cancel()
-                        )
-                    }
-
-                Section {
-                    Toggle("Use local glucose server", isOn: $state.useLocalSource)
-                    HStack {
-                        Text("Port")
-                        DecimalTextField("", value: $state.localPort, formatter: portFormater)
-                    }
-                } header: { Text("Local glucose source") }
+                        .alert(isPresented: $importedHasRun) {
+                            Alert(
+                                title: Text((fetchedErrors.first?.error ?? "").count < 4 ? "Settings imported" : "Import Error"),
+                                message: Text(
+                                    (fetchedErrors.first?.error ?? "").count < 4 ?
+                                        NSLocalizedString(
+                                            "\nNow please verify all of your new settings thoroughly: \n\n • DIA (Pump settings)\n • Basal Profile\n • Insulin Sensitivities\n • Carb Ratios\n • Target Glucose\n\n in Trio Settings -> Configuration.\n\nBad or invalid profile settings could have disastrous effects.",
+                                            comment: "Imported Profiles Alert"
+                                        ) :
+                                        NSLocalizedString(fetchedErrors.first?.error ?? "", comment: "Import Error")
+                                ),
+                                primaryButton: .destructive(
+                                    Text("OK")
+                                ),
+                                secondaryButton: .cancel()
+                            )
+                        }
+                }
                 Section {
                     Button("Backfill glucose") { state.backfillGlucose() }
                         .disabled(state.url.isEmpty || state.connecting || state.backfilling)
+                } header: { Text("Backfill glucose from Nightscout")
                 }
-
-                Section {
-                    Toggle("Remote control", isOn: $state.allowAnnouncements)
-                } header: { Text("Allow Remote control of Trio") }
             }
-            .onAppear(perform: configureView)
             .navigationBarTitle("Nightscout Config")
             .navigationBarTitleDisplayMode(.automatic)
             .navigationBarItems(leading: displayClose ? Button("Close", action: state.hideModal) : nil)
             .alert(isPresented: $isImportAlertPresented) {
                 importAlert!
             }
+
+            .onAppear(perform: configureView)
         }
     }
 }

+ 58 - 0
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutConnectView.swift

@@ -0,0 +1,58 @@
+import SwiftUI
+
+struct NightscoutConnectView: View {
+    @ObservedObject var state: NightscoutConfig.StateModel
+    @State private var portFormater: NumberFormatter
+
+    init(state: NightscoutConfig.StateModel) {
+        self.state = state
+        portFormater = NumberFormatter()
+        portFormater.allowsFloats = false
+    }
+
+    var body: some View {
+        Form {
+            Section {
+                TextField("URL", text: $state.url)
+                    .disableAutocorrection(true)
+                    .textContentType(.URL)
+                    .autocapitalization(.none)
+                    .keyboardType(.URL)
+                SecureField("API secret", text: $state.secret)
+                    .disableAutocorrection(true)
+                    .autocapitalization(.none)
+                    .textContentType(.password)
+                    .keyboardType(.asciiCapable)
+                if !state.message.isEmpty {
+                    Text(state.message)
+                }
+                if state.connecting {
+                    HStack {
+                        Text("Connecting...")
+                        Spacer()
+                        ProgressView()
+                    }
+                }
+            }
+            Section {
+                Button("Connect to Nightscout") { state.connect() }
+                    .disabled(state.url.isEmpty || state.connecting)
+                Button("Delete") { state.delete() }.foregroundColor(.red).disabled(state.connecting)
+            }
+            Section {
+                Button("Open Nightscout") {
+                    UIApplication.shared.open(URL(string: state.url)!, options: [:], completionHandler: nil)
+                }
+                .disabled(state.url.isEmpty || state.connecting)
+            }
+            Section {
+                Toggle("Use local glucose server", isOn: $state.useLocalSource)
+                HStack {
+                    Text("Port")
+                    DecimalTextField("", value: $state.localPort, formatter: portFormater)
+                }
+            } header: { Text("Local glucose source") }
+        }
+        .navigationTitle("Connect")
+    }
+}

+ 41 - 0
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutFetchView.swift

@@ -0,0 +1,41 @@
+
+import SwiftUI
+
+struct NightscoutFetchView: View {
+    @ObservedObject var state: NightscoutConfig.StateModel
+
+    var body: some View {
+        Form {
+            Section {
+                Toggle("Fetch Treatments", isOn: $state.isDownloadEnabled)
+                    .onChange(of: state.isDownloadEnabled) { newValue in
+                        if !newValue {
+                            state.allowAnnouncements = false
+                        }
+                    }
+            } header: {
+                Text("Allow Fetching from Nightscout")
+            } footer: {
+                Text(
+                    "The Fetch Treatments toggle enables fetching of carbs and temp targets entered in Careportal or by another uploading device than Trio."
+                )
+            }
+            Section(
+                header: Text("Allow Remote control of Trio"),
+                footer: VStack(alignment: .leading, spacing: 2) {
+                    Text("Fetch Treatments needs to be allowed to be able to toggle on Remote Control.")
+                    Text("\nWhen enabled you allow these remote functions through announcements from Nightscout:")
+                    Text(" • ") + Text("Suspend/Resume Pump")
+                    Text(" • ") + Text("Opening/Closing Loop")
+                    Text(" • ") + Text("Set Temp Basal")
+                    Text(" • ") + Text("Enact Bolus")
+                }
+            )
+                {
+                    Toggle("Remote Control", isOn: $state.allowAnnouncements)
+                        .disabled(!state.isDownloadEnabled)
+                }
+        }
+        .navigationTitle("Fetch and Remote")
+    }
+}

+ 26 - 0
FreeAPS/Sources/Modules/NightscoutConfig/View/NightscoutUploadView.swift

@@ -0,0 +1,26 @@
+
+import SwiftUI
+
+struct NightscoutUploadView: View {
+    @ObservedObject var state: NightscoutConfig.StateModel
+
+    var body: some View {
+        Form {
+            Section(
+                header: Text("Allow Uploading to Nightscout"),
+                footer: VStack(alignment: .leading, spacing: 2) {
+                    Text(
+                        "The Upload Treatments toggle enables uploading of carbs, temp targets, device status, preferences and settings."
+                    )
+                    Text("\nThe Upload Glucose toggle enables uploading of CGM readings.")
+                }
+            )
+                {
+                    Toggle("Upload Treatments and Settings", isOn: $state.isUploadEnabled)
+
+                    Toggle("Upload Glucose", isOn: $state.uploadGlucose).disabled(!state.changeUploadGlucose)
+                }
+        }
+        .navigationTitle("Upload")
+    }
+}

+ 7 - 3
FreeAPS/Sources/Services/Network/NightscoutManager.swift

@@ -44,6 +44,10 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
         settingsManager.settings.isUploadEnabled
     }
 
+    private var isDownloadEnabled: Bool {
+        settingsManager.settings.isDownloadEnabled
+    }
+
     private var isUploadGlucoseEnabled: Bool {
         settingsManager.settings.uploadGlucose
     }
@@ -140,7 +144,7 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
     }
 
     func fetchCarbs() -> AnyPublisher<[CarbsEntry], Never> {
-        guard let nightscout = nightscoutAPI, isNetworkReachable else {
+        guard let nightscout = nightscoutAPI, isNetworkReachable, isDownloadEnabled else {
             return Just([]).eraseToAnyPublisher()
         }
 
@@ -151,7 +155,7 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
     }
 
     func fetchTempTargets() -> AnyPublisher<[TempTarget], Never> {
-        guard let nightscout = nightscoutAPI, isNetworkReachable else {
+        guard let nightscout = nightscoutAPI, isNetworkReachable, isDownloadEnabled else {
             return Just([]).eraseToAnyPublisher()
         }
 
@@ -162,7 +166,7 @@ final class BaseNightscoutManager: NightscoutManager, Injectable {
     }
 
     func fetchAnnouncements() -> AnyPublisher<[Announcement], Never> {
-        guard let nightscout = nightscoutAPI, isNetworkReachable else {
+        guard let nightscout = nightscoutAPI, isNetworkReachable, isDownloadEnabled else {
             return Just([]).eraseToAnyPublisher()
         }