Timers.swift 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. //
  2. // Timers.swift
  3. // LoopFollow
  4. //
  5. // Created by Jon Fawcett on 9/3/20.
  6. // Copyright © 2020 Jon Fawcett. All rights reserved.
  7. //
  8. import Foundation
  9. import UIKit
  10. extension MainViewController {
  11. func restartAllTimers() {
  12. if !bgTimer.isValid { startBGTimer(time: 2) }
  13. if !profileTimer.isValid { startProfileTimer(time: 3) }
  14. if !deviceStatusTimer.isValid { startDeviceStatusTimer(time: 4) }
  15. if !treatmentsTimer.isValid { startTreatmentsTimer(time: 5) }
  16. if !minAgoTimer.isValid { startMinAgoTimer(time: minAgoTimeInterval) }
  17. if !calendarTimer.isValid { startCalendarTimer(time: 15) }
  18. if !alarmTimer.isValid { startAlarmTimer(time: 30) }
  19. }
  20. func invalidateTimers() {
  21. bgTimer.invalidate()
  22. profileTimer.invalidate()
  23. deviceStatusTimer.invalidate()
  24. treatmentsTimer.invalidate()
  25. minAgoTimer.invalidate()
  26. calendarTimer.invalidate()
  27. alarmTimer.invalidate()
  28. }
  29. // min Ago Timer
  30. func startMinAgoTimer(time: TimeInterval) {
  31. minAgoTimer = Timer.scheduledTimer(timeInterval: time,
  32. target: self,
  33. selector: #selector(MainViewController.minAgoTimerDidEnd(_:)),
  34. userInfo: nil,
  35. repeats: true)
  36. }
  37. @objc func minAgoTimerDidEnd(_ timer: Timer) {
  38. if bgData.count > 0 {
  39. let bgSeconds = bgData.last!.date
  40. let now = Date().timeIntervalSince1970
  41. let secondsAgo = now - bgSeconds
  42. // Update Min Ago Displays
  43. let formatter = DateComponentsFormatter()
  44. formatter.unitsStyle = .positional // Use the appropriate positioning for the current locale
  45. if secondsAgo >= 720 { // 720 seconds = 12 minutes
  46. formatter.allowedUnits = [.minute] // Only show minutes after 12 minutes have passed
  47. } else if secondsAgo < 270 { // Less than 4.5 minutes
  48. formatter.allowedUnits = [.minute] // Show only minutes if less than 4.5 minutes
  49. } else {
  50. formatter.allowedUnits = [.minute, .second] // Show minutes and seconds otherwise
  51. }
  52. let formattedDuration = formatter.string(from: secondsAgo) ?? ""
  53. let minAgoDisplayText = formattedDuration + " min ago"
  54. MinAgoText.text = minAgoDisplayText
  55. latestMinAgoString = minAgoDisplayText
  56. if let snoozer = self.tabBarController!.viewControllers?[2] as? SnoozeViewController {
  57. snoozer.MinAgoLabel.text = minAgoDisplayText
  58. // Start with the current BGLabel text
  59. let bgLabelText = snoozer.BGLabel.text ?? ""
  60. let attributeString = NSMutableAttributedString(string: bgLabelText)
  61. // Always apply the strikethrough style
  62. attributeString.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: NSRange(location: 0, length: attributeString.length))
  63. // Conditionally set the strikethrough color based on the freshness of the data
  64. if secondsAgo >= 720 { // Data is stale
  65. attributeString.addAttribute(.strikethroughColor, value: UIColor.systemRed, range: NSRange(location: 0, length: attributeString.length))
  66. } else { // Data is fresh
  67. attributeString.addAttribute(.strikethroughColor, value: UIColor.clear, range: NSRange(location: 0, length: attributeString.length))
  68. }
  69. snoozer.BGLabel.attributedText = attributeString
  70. }
  71. } else {
  72. MinAgoText.text = ""
  73. latestMinAgoString = ""
  74. if let snoozer = self.tabBarController!.viewControllers?[2] as? SnoozeViewController {
  75. snoozer.MinAgoLabel.text = ""
  76. // Reset BGLabel to ensure no formatting is carried over
  77. snoozer.BGLabel.text = ""
  78. snoozer.BGLabel.attributedText = NSAttributedString(string: "")
  79. }
  80. }
  81. }
  82. // Runs a 60 second timer when an alarm is snoozed
  83. // Prevents the alarm from triggering again while saving the snooze time to settings
  84. // End function needs nothing done
  85. func startGraphNowTimer(time: TimeInterval = 60) {
  86. graphNowTimer = Timer.scheduledTimer(timeInterval: time,
  87. target: self,
  88. selector: #selector(MainViewController.graphNowTimerDidEnd(_:)),
  89. userInfo: nil,
  90. repeats: true)
  91. }
  92. @objc func graphNowTimerDidEnd(_ timer:Timer) {
  93. createVerticalLines()
  94. }
  95. // Runs a 60 second timer when an alarm is snoozed
  96. // Prevents the alarm from triggering again while saving the snooze time to settings
  97. // End function needs nothing done
  98. func startCheckAlarmTimer(time: TimeInterval = 60) {
  99. checkAlarmTimer = Timer.scheduledTimer(timeInterval: time,
  100. target: self,
  101. selector: #selector(MainViewController.checkAlarmTimerDidEnd(_:)),
  102. userInfo: nil,
  103. repeats: false)
  104. }
  105. @objc func checkAlarmTimerDidEnd(_ timer:Timer) {
  106. }
  107. // BG Timer
  108. // Runs to 5:10 after last reading timestamp
  109. // Failed or no reading re-attempts after 10 second delay
  110. // Changes to 30 second increments after 7:00
  111. // Changes to 1 minute increments after 10:00
  112. // Changes to 5 minute increments after 20:00 stale data
  113. func startBGTimer(time: TimeInterval = 60 * 5) {
  114. bgTimer = Timer.scheduledTimer(timeInterval: time,
  115. target: self,
  116. selector: #selector(MainViewController.bgTimerDidEnd(_:)),
  117. userInfo: nil,
  118. repeats: false)
  119. }
  120. @objc func bgTimerDidEnd(_ timer:Timer) {
  121. // reset timer to 1 minute if settings aren't entered
  122. if UserDefaultsRepository.shareUserName.value == "" && UserDefaultsRepository.sharePassword.value == "" && UserDefaultsRepository.url.value == "" {
  123. startBGTimer(time: 60)
  124. return
  125. }
  126. if UserDefaultsRepository.shareUserName.value != "" && UserDefaultsRepository.sharePassword.value != "" {
  127. webLoadDexShare()
  128. } else {
  129. webLoadNSBGData()
  130. }
  131. }
  132. // Device Status Timer
  133. // Runs to 5:10 after last reading timestamp
  134. // Failed or no update re-attempts after 10 second delay
  135. // Changes to 30 second increments after 7:00
  136. // Changes to 1 minute increments after 10:00
  137. // Changes to 5 minute increments after 20:00 stale data
  138. func startDeviceStatusTimer(time: TimeInterval = 60 * 5) {
  139. deviceStatusTimer = Timer.scheduledTimer(timeInterval: time,
  140. target: self,
  141. selector: #selector(MainViewController.deviceStatusTimerDidEnd(_:)),
  142. userInfo: nil,
  143. repeats: false)
  144. }
  145. @objc func deviceStatusTimerDidEnd(_ timer:Timer) {
  146. // reset timer to 1 minute if settings aren't entered
  147. if UserDefaultsRepository.url.value == "" || !UserDefaultsRepository.loopUser.value {
  148. startDeviceStatusTimer(time: 60)
  149. return
  150. }
  151. if UserDefaultsRepository.url.value != "" {
  152. webLoadNSDeviceStatus()
  153. }
  154. }
  155. // Treatments Timer
  156. // Runs on 2 minute intervals
  157. // Pauses with stale BG data
  158. func startTreatmentsTimer(time: TimeInterval = 60 * 2) {
  159. treatmentsTimer = Timer.scheduledTimer(timeInterval: time,
  160. target: self,
  161. selector: #selector(MainViewController.treatmentsTimerDidEnd(_:)),
  162. userInfo: nil,
  163. repeats: false)
  164. }
  165. @objc func treatmentsTimerDidEnd(_ timer:Timer) {
  166. // reset timer to 1 minute if settings aren't entered
  167. if UserDefaultsRepository.url.value == "" || !UserDefaultsRepository.loopUser.value {
  168. startTreatmentsTimer(time: 60)
  169. return
  170. }
  171. if UserDefaultsRepository.url.value != "" && UserDefaultsRepository.downloadTreatments.value {
  172. WebLoadNSTreatments()
  173. }
  174. startTreatmentsTimer()
  175. }
  176. // Profile Timer
  177. // Runs on 10 minute intervals
  178. // Pauses with stale BG data
  179. func startProfileTimer(time: TimeInterval = 60 * 10) {
  180. profileTimer = Timer.scheduledTimer(timeInterval: time,
  181. target: self,
  182. selector: #selector(MainViewController.profileTimerDidEnd(_:)),
  183. userInfo: nil,
  184. repeats: false)
  185. }
  186. @objc func profileTimerDidEnd(_ timer:Timer) {
  187. // reset timer to 1 minute if settings aren't entered
  188. if UserDefaultsRepository.url.value == "" || !UserDefaultsRepository.loopUser.value {
  189. startProfileTimer(time: 60)
  190. return
  191. }
  192. if !isStaleData() && UserDefaultsRepository.url.value != "" {
  193. webLoadNSProfile()
  194. startProfileTimer()
  195. }
  196. }
  197. // Cancel and reset the playing alarm if it has not been snoozed after 4 min 50 seconds.
  198. // This allows the next BG reading to either start the timer going or not fire if the situation has been resolved
  199. func startAlarmPlayingTimer(time: TimeInterval = 290) {
  200. let alarmPlayingTimer = Timer.scheduledTimer(timeInterval: time,
  201. target: self,
  202. selector: #selector(MainViewController.alarmPlayingTimerDidEnd(_:)),
  203. userInfo: nil,
  204. repeats: false)
  205. }
  206. @objc func alarmPlayingTimerDidEnd(_ timer:Timer) {
  207. if AlarmSound.isPlaying {
  208. stopAlarmAtNextReading()
  209. }
  210. }
  211. // Alarm Timer
  212. // Run the alarm checker every 15 seconds
  213. func startAlarmTimer(time: TimeInterval) {
  214. alarmTimer = Timer.scheduledTimer(timeInterval: time,
  215. target: self,
  216. selector: #selector(MainViewController.alarmTimerDidEnd(_:)),
  217. userInfo: nil,
  218. repeats: true)
  219. }
  220. @objc func alarmTimerDidEnd(_ timer:Timer) {
  221. if bgData.count > 0 {
  222. self.checkAlarms(bgs: bgData)
  223. }
  224. if overrideGraphData.count > 0 {
  225. self.checkOverrideAlarms()
  226. }
  227. }
  228. // Calendar Timer
  229. // Run the calendar writer every 30 seconds
  230. func startCalendarTimer(time: TimeInterval) {
  231. calendarTimer = Timer.scheduledTimer(timeInterval: time,
  232. target: self,
  233. selector: #selector(MainViewController.calendarTimerDidEnd(_:)),
  234. userInfo: nil,
  235. repeats: true)
  236. }
  237. @objc func calendarTimerDidEnd(_ timer:Timer) {
  238. if UserDefaultsRepository.writeCalendarEvent.value && UserDefaultsRepository.calendarIdentifier.value != "" {
  239. self.writeCalendar()
  240. }
  241. }
  242. // Timer to allow us to write min ago calendar entries but not update them every 30 seconds
  243. func startCalTimer(time: TimeInterval) {
  244. calTimer = Timer.scheduledTimer(timeInterval: time,
  245. target: self,
  246. selector: #selector(MainViewController.calTimerDidEnd(_:)),
  247. userInfo: nil,
  248. repeats: false)
  249. }
  250. // Nothing should be done when this timer ends because it just blocks the calendar from writing when it's active
  251. @objc func calTimerDidEnd(_ timer:Timer) {
  252. }
  253. }