| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- // LoopFollow
- // RemoteCommandSettings.swift
- import Foundation
- import HealthKit
- struct RemoteCommandSettings: Codable {
- let remoteType: RemoteType
- let user: String
- let sharedSecret: String
- let remoteApnsKey: String
- let remoteKeyId: String
- let teamId: String?
- let maxBolus: Double
- let maxCarbs: Double
- let maxProtein: Double
- let maxFat: Double
- let mealWithBolus: Bool
- let mealWithFatProtein: Bool
- let productionEnvironment: Bool
- let loopAPNSQrCodeURL: String
- let url: String
- let token: String
- let version: String
- init(
- remoteType: RemoteType,
- user: String,
- sharedSecret: String,
- remoteApnsKey: String,
- remoteKeyId: String,
- teamId: String?,
- maxBolus: Double,
- maxCarbs: Double,
- maxProtein: Double,
- maxFat: Double,
- mealWithBolus: Bool,
- mealWithFatProtein: Bool,
- productionEnvironment: Bool,
- loopAPNSQrCodeURL: String,
- url: String,
- token: String
- ) {
- self.remoteType = remoteType
- self.user = user
- self.sharedSecret = sharedSecret
- self.remoteApnsKey = remoteApnsKey
- self.remoteKeyId = remoteKeyId
- self.teamId = teamId
- self.maxBolus = maxBolus
- self.maxCarbs = maxCarbs
- self.maxProtein = maxProtein
- self.maxFat = maxFat
- self.mealWithBolus = mealWithBolus
- self.mealWithFatProtein = mealWithFatProtein
- self.productionEnvironment = productionEnvironment
- self.loopAPNSQrCodeURL = loopAPNSQrCodeURL
- self.url = url
- self.token = token
- version = "1.0"
- }
- /// Creates RemoteCommandSettings from the current Storage values
- static func fromCurrentStorage() -> RemoteCommandSettings {
- let storage = Storage.shared
- return RemoteCommandSettings(
- remoteType: storage.remoteType.value,
- user: storage.user.value,
- sharedSecret: storage.sharedSecret.value,
- remoteApnsKey: storage.remoteApnsKey.value,
- remoteKeyId: storage.remoteKeyId.value,
- teamId: storage.teamId.value,
- maxBolus: storage.maxBolus.value.doubleValue(for: .internationalUnit()),
- maxCarbs: storage.maxCarbs.value.doubleValue(for: .gram()),
- maxProtein: storage.maxProtein.value.doubleValue(for: .gram()),
- maxFat: storage.maxFat.value.doubleValue(for: .gram()),
- mealWithBolus: storage.mealWithBolus.value,
- mealWithFatProtein: storage.mealWithFatProtein.value,
- productionEnvironment: storage.productionEnvironment.value,
- loopAPNSQrCodeURL: storage.loopAPNSQrCodeURL.value,
- url: storage.url.value,
- token: storage.token.value
- )
- }
- /// Applies the settings to the current Storage
- func applyToStorage() {
- let storage = Storage.shared
- storage.remoteType.value = remoteType
- storage.user.value = user
- storage.sharedSecret.value = sharedSecret
- storage.remoteApnsKey.value = remoteApnsKey
- storage.remoteKeyId.value = remoteKeyId
- storage.teamId.value = teamId
- storage.maxBolus.value = HKQuantity(unit: .internationalUnit(), doubleValue: maxBolus)
- storage.maxCarbs.value = HKQuantity(unit: .gram(), doubleValue: maxCarbs)
- storage.maxProtein.value = HKQuantity(unit: .gram(), doubleValue: maxProtein)
- storage.maxFat.value = HKQuantity(unit: .gram(), doubleValue: maxFat)
- storage.mealWithBolus.value = mealWithBolus
- storage.mealWithFatProtein.value = mealWithFatProtein
- storage.productionEnvironment.value = productionEnvironment
- storage.loopAPNSQrCodeURL.value = loopAPNSQrCodeURL
- storage.url.value = url
- storage.token.value = token
- // Automatically set device type based on remote type
- switch remoteType {
- case .loopAPNS:
- storage.device.value = "Loop"
- case .trc:
- storage.device.value = "Trio"
- case .none:
- // For none, we don't change the device type
- break
- }
- }
- /// Encodes the settings to a JSON string for QR code generation
- func encodeToJSON() -> String? {
- do {
- let data = try JSONEncoder().encode(self)
- return String(data: data, encoding: .utf8)
- } catch {
- return nil
- }
- }
- /// Decodes settings from a JSON string
- static func decodeFromJSON(_ jsonString: String) -> RemoteCommandSettings? {
- guard let data = jsonString.data(using: .utf8) else {
- return nil
- }
- do {
- return try JSONDecoder().decode(RemoteCommandSettings.self, from: data)
- } catch {
- return nil
- }
- }
- /// Checks if the settings are valid for the given remote type
- func hasValidSettings() -> Bool {
- switch remoteType {
- case .none:
- return true
- case .trc:
- return !user.isEmpty && !sharedSecret.isEmpty && !remoteApnsKey.isEmpty && !remoteKeyId.isEmpty
- case .loopAPNS:
- return !remoteKeyId.isEmpty && !remoteApnsKey.isEmpty && teamId != nil && !loopAPNSQrCodeURL.isEmpty
- }
- }
- /// Validates URL and token compatibility with current storage
- /// Returns a tuple with (isCompatible, shouldPromptForURL, shouldPromptForToken, message)
- func validateCompatibilityWithCurrentStorage() -> (isCompatible: Bool, shouldPromptForURL: Bool, shouldPromptForToken: Bool, message: String) {
- let storage = Storage.shared
- let currentURL = storage.url.value
- let currentToken = storage.token.value
- var shouldPromptForURL = false
- var shouldPromptForToken = false
- var message = ""
- // Check if current user has URL set
- let hasCurrentURL = !currentURL.isEmpty
- let hasCurrentToken = !currentToken.isEmpty
- // Check if scanned settings have URL/token
- let hasScannedURL = !url.isEmpty
- let hasScannedToken = !token.isEmpty
- // If current user doesn't have URL but scanned settings do, prompt to set it
- if !hasCurrentURL, hasScannedURL {
- shouldPromptForURL = true
- message = String(localized: "The scanned settings include a Nightscout URL. Would you like to set this as your Nightscout address?")
- }
- // If current user doesn't have token but scanned settings do, prompt to set it
- if !hasCurrentToken, hasScannedToken {
- shouldPromptForToken = true
- if !message.isEmpty {
- message += "\n\n" + String(localized: "The scanned settings also include a token. Would you like to set this as your access token?")
- } else {
- message = String(localized: "The scanned settings include a token. Would you like to set this as your access token?")
- }
- }
- // If both have URLs but they don't match, show warning
- if hasCurrentURL, hasScannedURL, currentURL != url {
- shouldPromptForURL = true
- message = "The scanned Nightscout URL (\(url)) doesn't match your current Nightscout address (\(currentURL)). Would you like to change your Nightscout address to match the scanned settings?"
- }
- // If both have tokens but they don't match, show warning
- if hasCurrentToken, hasScannedToken, currentToken != token {
- shouldPromptForToken = true
- if !message.isEmpty {
- message += "\n\n" + String(localized: "The scanned token doesn't match your current access token. Would you like to update your token?")
- } else {
- message = String(localized: "The scanned token doesn't match your current access token. Would you like to update your token?")
- }
- }
- let isCompatible = !shouldPromptForURL && !shouldPromptForToken
- return (isCompatible, shouldPromptForURL, shouldPromptForToken, message)
- }
- }
|