| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- //
- // Alert.swift
- // OmniKit
- //
- // Created by Pete Schwamb on 10/24/18.
- // Copyright © 2018 Pete Schwamb. All rights reserved.
- //
- import Foundation
- public enum AlertTrigger {
- case unitsRemaining(Double)
- case timeUntilAlert(TimeInterval)
- }
- public enum BeepRepeat: UInt8 {
- case once = 0
- case every1MinuteFor3MinutesAndRepeatEvery60Minutes = 1
- case every1MinuteFor15Minutes = 2
- case every1MinuteFor3MinutesAndRepeatEvery15Minutes = 3
- case every3MinutesFor60minutesStartingAt2Minutes = 4
- case every60Minutes = 5
- case every15Minutes = 6
- case every15MinutesFor60minutesStartingAt14Minutes = 7
- case every5Minutes = 8
- }
- public struct AlertConfiguration {
- let slot: AlertSlot
- let trigger: AlertTrigger
- let active: Bool
- let duration: TimeInterval
- let beepRepeat: BeepRepeat
- let beepType: BeepType
- let autoOffModifier: Bool
- static let length = 6
- public init(alertType: AlertSlot, active: Bool = true, autoOffModifier: Bool = false, duration: TimeInterval, trigger: AlertTrigger, beepRepeat: BeepRepeat, beepType: BeepType) {
- self.slot = alertType
- self.active = active
- self.autoOffModifier = autoOffModifier
- self.duration = duration
- self.trigger = trigger
- self.beepRepeat = beepRepeat
- self.beepType = beepType
- }
- }
- extension AlertConfiguration: CustomDebugStringConvertible {
- public var debugDescription: String {
- return "AlertConfiguration(slot:\(slot), active:\(active), autoOffModifier:\(autoOffModifier), duration:\(duration), trigger:\(trigger), beepRepeat:\(beepRepeat), beepType:\(beepType))"
- }
- }
- public enum PodAlert: CustomStringConvertible, RawRepresentable, Equatable {
- public typealias RawValue = [String: Any]
- // 2 hours long, time for user to start pairing process
- case waitingForPairingReminder
- // 1 hour long, time for user to finish priming, cannula insertion
- case finishSetupReminder
- // User configurable with PDM (1-24 hours before 72 hour expiration) "Change Pod Soon"
- case expirationAlert(TimeInterval)
- // 72 hour alarm
- case expirationAdvisoryAlarm(alarmTime: TimeInterval, duration: TimeInterval)
- // 79 hour alarm (1 hour before shutdown)
- case shutdownImminentAlarm(TimeInterval)
- // reservoir below configured value alarm
- case lowReservoirAlarm(Double)
- // auto-off timer; requires user input every x minutes
- case autoOffAlarm(active: Bool, countdownDuration: TimeInterval)
- // pod suspended reminder, before suspendTime; short beep every 15 minutes if >= 30 min, else every 5 minutes
- case podSuspendedReminder(active: Bool, suspendTime: TimeInterval)
- // pod suspend time expired alarm, after suspendTime; 2 sets of beeps every min for 3 minutes repeated every 15 minutes
- case suspendTimeExpired(suspendTime: TimeInterval)
- public var description: String {
- var alertName: String
- switch self {
- case .waitingForPairingReminder:
- return LocalizedString("Waiting for pairing reminder", comment: "Description waiting for pairing reminder")
- case .finishSetupReminder:
- return LocalizedString("Finish setup reminder", comment: "Description for finish setup reminder")
- case .expirationAlert:
- alertName = LocalizedString("Expiration alert", comment: "Description for expiration alert")
- case .expirationAdvisoryAlarm:
- alertName = LocalizedString("Expiration advisory", comment: "Description for expiration advisory")
- case .shutdownImminentAlarm:
- alertName = LocalizedString("Shutdown imminent", comment: "Description for shutdown imminent")
- case .lowReservoirAlarm:
- alertName = LocalizedString("Low reservoir advisory", comment: "Description for low reservoir advisory")
- case .autoOffAlarm:
- alertName = LocalizedString("Auto-off", comment: "Description for auto-off")
- case .podSuspendedReminder:
- alertName = LocalizedString("Pod suspended reminder", comment: "Description for pod suspended reminder")
- case .suspendTimeExpired:
- alertName = LocalizedString("Suspend time expired", comment: "Description for suspend time expired")
- }
- if self.configuration.active == false {
- alertName += LocalizedString(" (inactive)", comment: "Description for an inactive alert modifier")
- }
- return alertName
- }
- public var configuration: AlertConfiguration {
- switch self {
- case .waitingForPairingReminder:
- return AlertConfiguration(alertType: .slot7, duration: .minutes(110), trigger: .timeUntilAlert(.minutes(10)), beepRepeat: .every5Minutes, beepType: .bipBeepBipBeepBipBeepBipBeep)
- case .finishSetupReminder:
- return AlertConfiguration(alertType: .slot7, duration: .minutes(55), trigger: .timeUntilAlert(.minutes(5)), beepRepeat: .every5Minutes, beepType: .bipBeepBipBeepBipBeepBipBeep)
- case .expirationAlert(let alertTime):
- let active = alertTime != 0 // disable if alertTime is 0
- return AlertConfiguration(alertType: .slot3, active: active, duration: 0, trigger: .timeUntilAlert(alertTime), beepRepeat: .every1MinuteFor3MinutesAndRepeatEvery15Minutes, beepType: .bipBeepBipBeepBipBeepBipBeep)
- case .expirationAdvisoryAlarm(let alarmTime, let duration):
- let active = alarmTime != 0 // disable if alarmTime is 0
- return AlertConfiguration(alertType: .slot7, active: active, duration: duration, trigger: .timeUntilAlert(alarmTime), beepRepeat: .every60Minutes, beepType: .bipBeepBipBeepBipBeepBipBeep)
- case .shutdownImminentAlarm(let alarmTime):
- let active = alarmTime != 0 // disable if alarmTime is 0
- return AlertConfiguration(alertType: .slot2, active: active, duration: 0, trigger: .timeUntilAlert(alarmTime), beepRepeat: .every15Minutes, beepType: .bipBeepBipBeepBipBeepBipBeep)
- case .lowReservoirAlarm(let units):
- let active = units != 0 // disable if units is 0
- return AlertConfiguration(alertType: .slot4, active: active, duration: 0, trigger: .unitsRemaining(units), beepRepeat: .every1MinuteFor3MinutesAndRepeatEvery60Minutes, beepType: .bipBeepBipBeepBipBeepBipBeep)
- case .autoOffAlarm(let active, let countdownDuration):
- return AlertConfiguration(alertType: .slot0, active: active, autoOffModifier: true, duration: .minutes(15), trigger: .timeUntilAlert(countdownDuration), beepRepeat: .every1MinuteFor15Minutes, beepType: .bipBeepBipBeepBipBeepBipBeep)
- case .podSuspendedReminder(let active, let suspendTime):
- // A suspendTime of 0 is an untimed suspend
- let reminderInterval, duration: TimeInterval
- let trigger: AlertTrigger
- let beepRepeat: BeepRepeat
- let beepType: BeepType
- if active {
- if suspendTime >= TimeInterval(minutes :30) {
- // Use 15-minute pod suspended reminder beeps for longer scheduled suspend times as per PDM.
- reminderInterval = TimeInterval(minutes: 15)
- beepRepeat = .every15Minutes
- } else {
- // Use 5-minute pod suspended reminder beeps for untimed & shorter scheduled suspend times.
- reminderInterval = TimeInterval(minutes: 5)
- beepRepeat = .every5Minutes
- }
- if suspendTime == 0 {
- duration = 0 // Untimed suspend, no duration
- } else if suspendTime > reminderInterval {
- duration = suspendTime - reminderInterval // End after suspendTime total time
- } else {
- duration = .minutes(1) // Degenerate case, end ASAP
- }
- trigger = .timeUntilAlert(reminderInterval) // Start after reminderInterval has passed
- beepType = .beep
- } else {
- duration = 0
- trigger = .timeUntilAlert(.minutes(0))
- beepRepeat = .once
- beepType = .noBeep
- }
- return AlertConfiguration(alertType: .slot5, active: active, duration: duration, trigger: trigger, beepRepeat: beepRepeat, beepType: beepType)
- case .suspendTimeExpired(let suspendTime):
- let active = suspendTime != 0 // disable if suspendTime is 0
- let trigger: AlertTrigger
- let beepRepeat: BeepRepeat
- let beepType: BeepType
- if active {
- trigger = .timeUntilAlert(suspendTime)
- beepRepeat = .every1MinuteFor3MinutesAndRepeatEvery15Minutes
- beepType = .bipBeepBipBeepBipBeepBipBeep
- } else {
- trigger = .timeUntilAlert(.minutes(0))
- beepRepeat = .once
- beepType = .noBeep
- }
- return AlertConfiguration(alertType: .slot6, active: active, duration: 0, trigger: trigger, beepRepeat: beepRepeat, beepType: beepType)
- }
- }
- // MARK: - RawRepresentable
- public init?(rawValue: RawValue) {
- guard let name = rawValue["name"] as? String else {
- return nil
- }
- switch name {
- case "waitingForPairingReminder":
- self = .waitingForPairingReminder
- case "finishSetupReminder":
- self = .finishSetupReminder
- case "expirationAlert":
- guard let alertTime = rawValue["alertTime"] as? Double else {
- return nil
- }
- self = .expirationAlert(TimeInterval(alertTime))
- case "expirationAdvisoryAlarm":
- guard let alarmTime = rawValue["alarmTime"] as? Double,
- let duration = rawValue["duration"] as? Double else
- {
- return nil
- }
- self = .expirationAdvisoryAlarm(alarmTime: TimeInterval(alarmTime), duration: TimeInterval(duration))
- case "shutdownImminentAlarm":
- guard let alarmTime = rawValue["alarmTime"] as? Double else {
- return nil
- }
- self = .shutdownImminentAlarm(alarmTime)
- case "lowReservoirAlarm":
- guard let units = rawValue["units"] as? Double else {
- return nil
- }
- self = .lowReservoirAlarm(units)
- case "autoOffAlarm":
- guard let active = rawValue["active"] as? Bool,
- let countdownDuration = rawValue["countdownDuration"] as? Double else
- {
- return nil
- }
- self = .autoOffAlarm(active: active, countdownDuration: TimeInterval(countdownDuration))
- case "podSuspendedReminder":
- guard let active = rawValue["active"] as? Bool,
- let suspendTime = rawValue["suspendTime"] as? Double else
- {
- return nil
- }
- self = .podSuspendedReminder(active: active, suspendTime: suspendTime)
- case "suspendTimeExpired":
- guard let suspendTime = rawValue["suspendTime"] as? Double else {
- return nil
- }
- self = .suspendTimeExpired(suspendTime: suspendTime)
- default:
- return nil
- }
- }
- public var rawValue: RawValue {
- let name: String = {
- switch self {
- case .waitingForPairingReminder:
- return "waitingForPairingReminder"
- case .finishSetupReminder:
- return "finishSetupReminder"
- case .expirationAlert:
- return "expirationAlert"
- case .expirationAdvisoryAlarm:
- return "expirationAdvisoryAlarm"
- case .shutdownImminentAlarm:
- return "shutdownImminentAlarm"
- case .lowReservoirAlarm:
- return "lowReservoirAlarm"
- case .autoOffAlarm:
- return "autoOffAlarm"
- case .podSuspendedReminder:
- return "podSuspendedReminder"
- case .suspendTimeExpired:
- return "suspendTimeExpired"
- }
- }()
- var rawValue: RawValue = [
- "name": name,
- ]
- switch self {
- case .expirationAlert(let alertTime):
- rawValue["alertTime"] = alertTime
- case .expirationAdvisoryAlarm(let alarmTime, let duration):
- rawValue["alarmTime"] = alarmTime
- rawValue["duration"] = duration
- case .shutdownImminentAlarm(let alarmTime):
- rawValue["alarmTime"] = alarmTime
- case .lowReservoirAlarm(let units):
- rawValue["units"] = units
- case .autoOffAlarm(let active, let countdownDuration):
- rawValue["active"] = active
- rawValue["countdownDuration"] = countdownDuration
- case .podSuspendedReminder(let active, let suspendTime):
- rawValue["active"] = active
- rawValue["suspendTime"] = suspendTime
- case .suspendTimeExpired(let suspendTime):
- rawValue["suspendTime"] = suspendTime
- default:
- break
- }
- return rawValue
- }
- }
- public enum AlertSlot: UInt8 {
- case slot0 = 0x00
- case slot1 = 0x01
- case slot2 = 0x02
- case slot3 = 0x03
- case slot4 = 0x04
- case slot5 = 0x05
- case slot6 = 0x06
- case slot7 = 0x07
- public var bitMaskValue: UInt8 {
- return 1<<rawValue
- }
- public typealias AllCases = [AlertSlot]
-
- static var allCases: AllCases {
- return (0..<8).map { AlertSlot(rawValue: $0)! }
- }
- }
- public struct AlertSet: RawRepresentable, Collection, CustomStringConvertible, Equatable {
-
- public typealias RawValue = UInt8
- public typealias Index = Int
-
- public let startIndex: Int
- public let endIndex: Int
-
- private let elements: [AlertSlot]
-
- public static let none = AlertSet(rawValue: 0)
-
- public var rawValue: UInt8 {
- return elements.reduce(0) { $0 | $1.bitMaskValue }
- }
-
- public init(slots: [AlertSlot]) {
- self.elements = slots
- self.startIndex = 0
- self.endIndex = self.elements.count
- }
- public init(rawValue: UInt8) {
- self.init(slots: AlertSlot.allCases.filter { rawValue & $0.bitMaskValue != 0 })
- }
- public subscript(index: Index) -> AlertSlot {
- return elements[index]
- }
-
- public func index(after i: Int) -> Int {
- return i+1
- }
-
- public var description: String {
- if elements.count == 0 {
- return LocalizedString("No alerts", comment: "Pod alert state when no alerts are active")
- } else {
- let alarmDescriptions = elements.map { String(describing: $0) }
- return alarmDescriptions.joined(separator: ", ")
- }
- }
- }
|