import Foundation enum BolusShortcutLimit: String, JSON, CaseIterable, Identifiable { var id: String { rawValue } case notAllowed case limitBolusMax var displayName: String { switch self { case .notAllowed: return String(localized: "Not allowed", table: "ShortcutsDetail") case .limitBolusMax: return String(localized: "Max bolus", table: "ShortcutsDetail") } } } struct FreeAPSSettings: JSON, Equatable { var units: GlucoseUnits = .mgdL var closedLoop: Bool = false 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 var displayHR: Bool = false var cgm: CGMType = .none var cgmPluginIdentifier: String = "" var uploadGlucose: Bool = true var useCalendar: Bool = false var displayCalendarIOBandCOB: Bool = false var displayCalendarEmojis: Bool = false var glucoseBadge: Bool = false var glucoseNotificationsAlways: Bool = false var useAlarmSound: Bool = false var addSourceInfoToGlucoseNotifications: Bool = false var lowGlucose: Decimal = 72 var highGlucose: Decimal = 270 var carbsRequiredThreshold: Decimal = 10 var showCarbsRequiredBadge: Bool = true var useFPUconversion: Bool = true var totalInsulinDisplayType: TotalInsulinDisplayType = .totalDailyDose var individualAdjustmentFactor: Decimal = 0.5 var timeCap: Int = 8 var minuteInterval: Int = 30 var delay: Int = 60 var useAppleHealth: Bool = false var smoothGlucose: Bool = false var displayOnWatch: AwConfig = .BGTarget var overrideHbA1cUnit: Bool = false var high: Decimal = 180 var low: Decimal = 70 var hours: Int = 6 var glucoseColorScheme: GlucoseColorScheme = .staticColor var xGridLines: Bool = true var yGridLines: Bool = true var oneDimensionalGraph: Bool = false var rulerMarks: Bool = true var forecastDisplayType: ForecastDisplayType = .cone var maxCarbs: Decimal = 250 var maxFat: Decimal = 250 var maxProtein: Decimal = 250 var displayFatAndProteinOnWatch: Bool = false var confirmBolusFaster: Bool = false var onlyAutotuneBasals: Bool = false var overrideFactor: Decimal = 0.8 var fattyMeals: Bool = false var fattyMealFactor: Decimal = 0.7 var sweetMeals: Bool = false var sweetMealFactor: Decimal = 2 var displayPresets: Bool = true var useLiveActivity: Bool = false var lockScreenView: LockScreenView = .simple var bolusShortcut: BolusShortcutLimit = .notAllowed } extension FreeAPSSettings: Decodable { // Needed to decode incomplete JSON init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) var settings = FreeAPSSettings() if let units = try? container.decode(GlucoseUnits.self, forKey: .units) { settings.units = units } if let closedLoop = try? container.decode(Bool.self, forKey: .closedLoop) { settings.closedLoop = closedLoop } if let allowAnnouncements = try? container.decode(Bool.self, forKey: .allowAnnouncements) { settings.allowAnnouncements = allowAnnouncements } if let useAutotune = try? container.decode(Bool.self, forKey: .useAutotune) { settings.useAutotune = useAutotune } if let isUploadEnabled = try? container.decode(Bool.self, forKey: .isUploadEnabled) { 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 } if let localGlucosePort = try? container.decode(Int.self, forKey: .localGlucosePort) { settings.localGlucosePort = localGlucosePort } if let debugOptions = try? container.decode(Bool.self, forKey: .debugOptions) { settings.debugOptions = debugOptions } if let displayHR = try? container.decode(Bool.self, forKey: .displayHR) { settings.displayHR = displayHR // compatibility if displayOnWatch is not available in json files settings.displayOnWatch = (displayHR == true) ? AwConfig.HR : AwConfig.BGTarget } if let displayOnWatch = try? container.decode(AwConfig.self, forKey: .displayOnWatch) { settings.displayOnWatch = displayOnWatch } if let cgm = try? container.decode(CGMType.self, forKey: .cgm) { settings.cgm = cgm } if let cgmPluginIdentifier = try? container.decode(String.self, forKey: .cgmPluginIdentifier) { settings.cgmPluginIdentifier = cgmPluginIdentifier } if let uploadGlucose = try? container.decode(Bool.self, forKey: .uploadGlucose) { settings.uploadGlucose = uploadGlucose } if let useCalendar = try? container.decode(Bool.self, forKey: .useCalendar) { settings.useCalendar = useCalendar } if let displayCalendarIOBandCOB = try? container.decode(Bool.self, forKey: .displayCalendarIOBandCOB) { settings.displayCalendarIOBandCOB = displayCalendarIOBandCOB } if let displayCalendarEmojis = try? container.decode(Bool.self, forKey: .displayCalendarEmojis) { settings.displayCalendarEmojis = displayCalendarEmojis } if let useAppleHealth = try? container.decode(Bool.self, forKey: .useAppleHealth) { settings.useAppleHealth = useAppleHealth } if let glucoseBadge = try? container.decode(Bool.self, forKey: .glucoseBadge) { settings.glucoseBadge = glucoseBadge } if let useFPUconversion = try? container.decode(Bool.self, forKey: .useFPUconversion) { settings.useFPUconversion = useFPUconversion } if let totalInsulinDisplayType = try? container.decode(TotalInsulinDisplayType.self, forKey: .totalInsulinDisplayType) { settings.totalInsulinDisplayType = totalInsulinDisplayType } if let individualAdjustmentFactor = try? container.decode(Decimal.self, forKey: .individualAdjustmentFactor) { settings.individualAdjustmentFactor = individualAdjustmentFactor } if let fattyMeals = try? container.decode(Bool.self, forKey: .fattyMeals) { settings.fattyMeals = fattyMeals } if let fattyMealFactor = try? container.decode(Decimal.self, forKey: .fattyMealFactor) { settings.fattyMealFactor = fattyMealFactor } if let sweetMeals = try? container.decode(Bool.self, forKey: .sweetMeals) { settings.sweetMeals = sweetMeals } if let sweetMealFactor = try? container.decode(Decimal.self, forKey: .sweetMealFactor) { settings.sweetMealFactor = sweetMealFactor } if let overrideFactor = try? container.decode(Decimal.self, forKey: .overrideFactor) { settings.overrideFactor = overrideFactor } if let timeCap = try? container.decode(Int.self, forKey: .timeCap) { settings.timeCap = timeCap } if let minuteInterval = try? container.decode(Int.self, forKey: .minuteInterval) { settings.minuteInterval = minuteInterval } if let delay = try? container.decode(Int.self, forKey: .delay) { settings.delay = delay } if let glucoseNotificationsAlways = try? container.decode(Bool.self, forKey: .glucoseNotificationsAlways) { settings.glucoseNotificationsAlways = glucoseNotificationsAlways } if let useAlarmSound = try? container.decode(Bool.self, forKey: .useAlarmSound) { settings.useAlarmSound = useAlarmSound } if let addSourceInfoToGlucoseNotifications = try? container.decode( Bool.self, forKey: .addSourceInfoToGlucoseNotifications ) { settings.addSourceInfoToGlucoseNotifications = addSourceInfoToGlucoseNotifications } if let lowGlucose = try? container.decode(Decimal.self, forKey: .lowGlucose) { settings.lowGlucose = lowGlucose } if let highGlucose = try? container.decode(Decimal.self, forKey: .highGlucose) { settings.highGlucose = highGlucose } if let carbsRequiredThreshold = try? container.decode(Decimal.self, forKey: .carbsRequiredThreshold) { settings.carbsRequiredThreshold = carbsRequiredThreshold } if let showCarbsRequiredBadge = try? container.decode(Bool.self, forKey: .showCarbsRequiredBadge) { settings.showCarbsRequiredBadge = showCarbsRequiredBadge } if let smoothGlucose = try? container.decode(Bool.self, forKey: .smoothGlucose) { settings.smoothGlucose = smoothGlucose } if let low = try? container.decode(Decimal.self, forKey: .low) { settings.low = low } if let high = try? container.decode(Decimal.self, forKey: .high) { settings.high = high } if let hours = try? container.decode(Int.self, forKey: .hours) { settings.hours = hours } if let glucoseColorScheme = try? container.decode(GlucoseColorScheme.self, forKey: .glucoseColorScheme) { settings.glucoseColorScheme = glucoseColorScheme } if let xGridLines = try? container.decode(Bool.self, forKey: .xGridLines) { settings.xGridLines = xGridLines } if let yGridLines = try? container.decode(Bool.self, forKey: .yGridLines) { settings.yGridLines = yGridLines } if let oneDimensionalGraph = try? container.decode(Bool.self, forKey: .oneDimensionalGraph) { settings.oneDimensionalGraph = oneDimensionalGraph } if let rulerMarks = try? container.decode(Bool.self, forKey: .rulerMarks) { settings.rulerMarks = rulerMarks } if let forecastDisplayType = try? container.decode(ForecastDisplayType.self, forKey: .forecastDisplayType) { settings.forecastDisplayType = forecastDisplayType } if let overrideHbA1cUnit = try? container.decode(Bool.self, forKey: .overrideHbA1cUnit) { settings.overrideHbA1cUnit = overrideHbA1cUnit } if let maxCarbs = try? container.decode(Decimal.self, forKey: .maxCarbs) { settings.maxCarbs = maxCarbs } if let maxFat = try? container.decode(Decimal.self, forKey: .maxFat) { settings.maxFat = maxFat } if let maxProtein = try? container.decode(Decimal.self, forKey: .maxProtein) { settings.maxProtein = maxProtein } if let displayFatAndProteinOnWatch = try? container.decode(Bool.self, forKey: .displayFatAndProteinOnWatch) { settings.displayFatAndProteinOnWatch = displayFatAndProteinOnWatch } if let confirmBolusFaster = try? container.decode(Bool.self, forKey: .confirmBolusFaster) { settings.confirmBolusFaster = confirmBolusFaster } if let onlyAutotuneBasals = try? container.decode(Bool.self, forKey: .onlyAutotuneBasals) { settings.onlyAutotuneBasals = onlyAutotuneBasals } if let displayPresets = try? container.decode(Bool.self, forKey: .displayPresets) { settings.displayPresets = displayPresets } if let useLiveActivity = try? container.decode(Bool.self, forKey: .useLiveActivity) { settings.useLiveActivity = useLiveActivity } if let lockScreenView = try? container.decode(LockScreenView.self, forKey: .lockScreenView) { settings.lockScreenView = lockScreenView } if let bolusShortcut = try? container.decode(BolusShortcutLimit.self, forKey: .bolusShortcut) { settings.bolusShortcut = bolusShortcut } self = settings } }