Explorar el Código

TempTargetEnd

Jonas Björkert hace 1 año
padre
commit
a36b7acfd2

+ 8 - 0
LoopFollow.xcodeproj/project.pbxproj

@@ -109,6 +109,8 @@
 		DD7F4C0D2DD51C8100D449E9 /* OverrideEndAlarmEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7F4C0C2DD51C8100D449E9 /* OverrideEndAlarmEditor.swift */; };
 		DD7F4C0F2DD51EC200D449E9 /* TempTargetStartCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7F4C0E2DD51EC200D449E9 /* TempTargetStartCondition.swift */; };
 		DD7F4C112DD51ED900D449E9 /* TempTargetStartAlarmEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7F4C102DD51ED900D449E9 /* TempTargetStartAlarmEditor.swift */; };
+		DD7F4C132DD51FD500D449E9 /* TempTargetEndCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7F4C122DD51FD500D449E9 /* TempTargetEndCondition.swift */; };
+		DD7F4C152DD51FEB00D449E9 /* TempTargetEndAlarmEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7F4C142DD51FEB00D449E9 /* TempTargetEndAlarmEditor.swift */; };
 		DD7FFAFD2A72953000C3A304 /* EKEventStore+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD7FFAFC2A72953000C3A304 /* EKEventStore+Extensions.swift */; };
 		DD85E9952D739CFE001C8BB7 /* OmnipodDashHeartbeatBluetoothTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD85E9942D739CED001C8BB7 /* OmnipodDashHeartbeatBluetoothTransmitter.swift */; };
 		DD91E4DD2BDEC3F8002D9E97 /* GlucoseConversion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD91E4DC2BDEC3F8002D9E97 /* GlucoseConversion.swift */; };
@@ -442,6 +444,8 @@
 		DD7F4C0C2DD51C8100D449E9 /* OverrideEndAlarmEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OverrideEndAlarmEditor.swift; sourceTree = "<group>"; };
 		DD7F4C0E2DD51EC200D449E9 /* TempTargetStartCondition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TempTargetStartCondition.swift; sourceTree = "<group>"; };
 		DD7F4C102DD51ED900D449E9 /* TempTargetStartAlarmEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TempTargetStartAlarmEditor.swift; sourceTree = "<group>"; };
+		DD7F4C122DD51FD500D449E9 /* TempTargetEndCondition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TempTargetEndCondition.swift; sourceTree = "<group>"; };
+		DD7F4C142DD51FEB00D449E9 /* TempTargetEndAlarmEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TempTargetEndAlarmEditor.swift; sourceTree = "<group>"; };
 		DD7FFAFC2A72953000C3A304 /* EKEventStore+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EKEventStore+Extensions.swift"; sourceTree = "<group>"; };
 		DD85E9942D739CED001C8BB7 /* OmnipodDashHeartbeatBluetoothTransmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmnipodDashHeartbeatBluetoothTransmitter.swift; sourceTree = "<group>"; };
 		DD91E4DC2BDEC3F8002D9E97 /* GlucoseConversion.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseConversion.swift; sourceTree = "<group>"; };
@@ -710,6 +714,7 @@
 		DD02475A2DB2E8CE00FCADF6 /* AlarmCondition */ = {
 			isa = PBXGroup;
 			children = (
+				DD7F4C122DD51FD500D449E9 /* TempTargetEndCondition.swift */,
 				DD7F4C0E2DD51EC200D449E9 /* TempTargetStartCondition.swift */,
 				DD7F4C0A2DD51C5500D449E9 /* OverrideEndCondition.swift */,
 				DD7F4C042DD4BBE200D449E9 /* NotLoopingCondition.swift */,
@@ -965,6 +970,7 @@
 		DDC7E53F2DBD8A1600EB1127 /* Editors */ = {
 			isa = PBXGroup;
 			children = (
+				DD7F4C142DD51FEB00D449E9 /* TempTargetEndAlarmEditor.swift */,
 				DD7F4C102DD51ED900D449E9 /* TempTargetStartAlarmEditor.swift */,
 				DD7F4C0C2DD51C8100D449E9 /* OverrideEndAlarmEditor.swift */,
 				DD7F4C062DD5042F00D449E9 /* OverrideStartAlarmEditor.swift */,
@@ -1856,6 +1862,7 @@
 				DDD10F052C529DA200D76A8E /* ObservableValue.swift in Sources */,
 				FC1BDD2D24A23204001B652C /* StatsView.swift in Sources */,
 				DD4878102C7B74BF0048F05C /* TrioRemoteControlView.swift in Sources */,
+				DD7F4C152DD51FEB00D449E9 /* TempTargetEndAlarmEditor.swift in Sources */,
 				DD2C2E542D3C37DC006413A5 /* DexcomSettingsViewModel.swift in Sources */,
 				FCE537BC249A4D7D00F80BF8 /* carbBolusArrays.swift in Sources */,
 				DD5334232C60ED3600062F9D /* IAge.swift in Sources */,
@@ -1867,6 +1874,7 @@
 				DD50C7552D0862770057AE6F /* ContactImageUpdater.swift in Sources */,
 				DD0650F52DCF303F004D3B41 /* AlarmStepperSection.swift in Sources */,
 				DDCF9A802D85FD0B004DF4DD /* Alarm.swift in Sources */,
+				DD7F4C132DD51FD500D449E9 /* TempTargetEndCondition.swift in Sources */,
 				DDD10F012C510C6B00D76A8E /* ObservableUserDefaults.swift in Sources */,
 				DD16AF0D2C98485400FB655A /* SecureStorageValue.swift in Sources */,
 				DDC7E5CF2DC77C2000EB1127 /* SnoozerViewModel.swift in Sources */,

+ 26 - 0
LoopFollow/Alarm/AlarmCondition/TempTargetEndCondition.swift

@@ -0,0 +1,26 @@
+//
+//  TempTargetEndCondition.swift
+//  LoopFollow
+//
+//  Created by Jonas Björkert on 2025-05-14.
+//  Copyright © 2025 Jon Fawcett. All rights reserved.
+//
+
+import Foundation
+
+/// Fires once when the active temp target ends.
+struct TempTargetEndCondition: AlarmCondition {
+    static let type: AlarmType = .tempTargetEnd
+    init() {}
+
+    func evaluate(alarm _: Alarm, data: AlarmData) -> Bool {
+        guard let endTS = data.latestTempTargetEnd, endTS > 0 else { return false }
+        guard Date().timeIntervalSince1970 - endTS <= 15 * 60 else { return false }
+
+        let last = Storage.shared.lastTempTargetEndNotified.value ?? 0
+        guard endTS > last else { return false }
+
+        Storage.shared.lastTempTargetEndNotified.value = endTS
+        return true
+    }
+}

+ 1 - 0
LoopFollow/Alarm/AlarmData.swift

@@ -16,6 +16,7 @@ struct AlarmData: Codable {
     let latestOverrideStart: TimeInterval?
     let latestOverrideEnd: TimeInterval?
     let latestTempTargetStart: TimeInterval?
+    let latestTempTargetEnd: TimeInterval?
 }
 
 /*

+ 2 - 0
LoopFollow/Alarm/AlarmEditing/AlarmEditor.swift

@@ -59,6 +59,8 @@ struct AlarmEditor: View {
             OverrideEndAlarmEditor(alarm: $alarm)
         case .tempTargetStart:
             TempTargetStartAlarmEditor(alarm: $alarm)
+        case .tempTargetEnd:
+            TempTargetEndAlarmEditor(alarm: $alarm)
         /* TODO: add other condition types here */
         default:
             Text("No editor for \(alarm.type.rawValue)")

+ 26 - 0
LoopFollow/Alarm/AlarmEditing/Editors/TempTargetEndAlarmEditor.swift

@@ -0,0 +1,26 @@
+//
+//  TempTargetEndAlarmEditor.swift
+//  LoopFollow
+//
+//  Created by Jonas Björkert on 2025-05-14.
+//  Copyright © 2025 Jon Fawcett. All rights reserved.
+//
+
+import SwiftUI
+
+struct TempTargetEndAlarmEditor: View {
+    @Binding var alarm: Alarm
+
+    var body: some View {
+        Form {
+            InfoBanner(text: "Alerts when a temp target ends.", alarmType: alarm.type)
+
+            AlarmGeneralSection(alarm: $alarm)
+
+            AlarmActiveSection(alarm: $alarm)
+            AlarmAudioSection(alarm: $alarm, hideRepeat: true)
+            AlarmSnoozeSection(alarm: $alarm, range: 10 ... 60, step: 5)
+        }
+        .navigationTitle(alarm.type.rawValue)
+    }
+}

+ 1 - 0
LoopFollow/Alarm/AlarmManager.swift

@@ -25,6 +25,7 @@ class AlarmManager {
             OverrideStartCondition.self,
             OverrideEndCondition.self,
             TempTargetStartCondition.self,
+            TempTargetEndCondition.self,
             // TODO: add other condition types here
         ]
     ) {

+ 1 - 0
LoopFollow/Storage/Storage.swift

@@ -56,6 +56,7 @@ class Storage {
     var lastOverrideStartNotified = StorageValue<TimeInterval?>(key: "lastOverrideStartNotified", defaultValue: nil)
     var lastOverrideEndNotified = StorageValue<TimeInterval?>(key: "lastOverrideEndNotified", defaultValue: nil)
     var lastTempTargetStartNotified = StorageValue<TimeInterval?>(key: "lastTempTargetStartNotified", defaultValue: nil)
+    var lastTempTargetEndNotified = StorageValue<TimeInterval?>(key: "lastTempTargetEndNotified", defaultValue: nil)
 
     static let shared = Storage()
     private init() {}

+ 3 - 1
LoopFollow/Task/AlarmTask.swift

@@ -22,6 +22,7 @@ extension MainViewController {
             let latestOverrideStart = self.overrideGraphData.last?.date
             let latestOverrideEnd = self.overrideGraphData.last?.endDate
             let latestTempTargetStart = self.tempTargetGraphData.last?.date
+            let latestTempTargetEnd = self.tempTargetGraphData.last?.endDate
 
             let alarmData = AlarmData(
                 bgReadings: self.bgData
@@ -34,7 +35,8 @@ extension MainViewController {
                 lastLoopTime: UserDefaultsRepository.alertLastLoopTime.value,
                 latestOverrideStart: latestOverrideStart,
                 latestOverrideEnd: latestOverrideEnd,
-                latestTempTargetStart: latestTempTargetStart
+                latestTempTargetStart: latestTempTargetStart,
+                latestTempTargetEnd: latestTempTargetEnd,
             )
 
             let finalAlarmData: AlarmData