Artificial delay in readings when sending to Loop
Libre transmitteris tend to show erratic values. Smoothing improves those values a lot but only for historic values, not for the most recent values. To avoid that Loop takes actions based on those wrong values, this new option allows to introduce a delay. Most recent values will not be shown. Older values will shift in timestamp. To Loop it will look as if this is the most recent value. Advantage is that values used by Loop will be more reliable, but running a number of minutes behind the real values. New setting in development section. Only to be used for developers.
This commit is contained in:
parent
d2a292ee35
commit
08f827d1e3
|
@ -288,6 +288,11 @@ extension UserDefaults {
|
||||||
/// case smooth libre values
|
/// case smooth libre values
|
||||||
case smoothLibreValues = "smoothLibreValues"
|
case smoothLibreValues = "smoothLibreValues"
|
||||||
|
|
||||||
|
/// to create artificial delay in readings stored in sharedUserDefaults for loop. Minutes - so that Loop receives more smoothed values.
|
||||||
|
///
|
||||||
|
/// Default value 0, if used then recommende value between 4 and 9
|
||||||
|
case loopDelay = "loopDelay"
|
||||||
|
|
||||||
/// used for Libre data parsing - only for Libre 1 or Libre 2 read via transmitter, ie full NFC block
|
/// used for Libre data parsing - only for Libre 1 or Libre 2 read via transmitter, ie full NFC block
|
||||||
case previousRawLibreValues = "previousRawLibreValues"
|
case previousRawLibreValues = "previousRawLibreValues"
|
||||||
|
|
||||||
|
@ -396,6 +401,18 @@ extension UserDefaults {
|
||||||
set(newValue, forKey: Key.notificationInterval.rawValue)
|
set(newValue, forKey: Key.notificationInterval.rawValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// to create artificial delay in readings stored in sharedUserDefaults for loop. Minutes - so that Loop receives more smoothed values.
|
||||||
|
///
|
||||||
|
/// Default value 0, if used then recommende value between 4 and 9
|
||||||
|
@objc dynamic var loopDelay: Int {
|
||||||
|
get {
|
||||||
|
return integer(forKey: Key.loopDelay.rawValue)
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
set(newValue, forKey: Key.loopDelay.rawValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// should reading be shown in app badge yes or no
|
/// should reading be shown in app badge yes or no
|
||||||
@objc dynamic var showReadingInAppBadge: Bool {
|
@objc dynamic var showReadingInAppBadge: Bool {
|
||||||
|
|
|
@ -44,13 +44,24 @@ public class LoopManager:NSObject {
|
||||||
guard let sharedUserDefaults = sharedUserDefaults else {return}
|
guard let sharedUserDefaults = sharedUserDefaults else {return}
|
||||||
|
|
||||||
// get last readings with calculated value
|
// get last readings with calculated value
|
||||||
let lastReadings = bgReadingsAccessor.getLatestBgReadings(limit: ConstantsShareWithLoop.maxReadingsToShareWithLoop, fromDate: UserDefaults.standard.timeStampLatestLoopSharedBgReading, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false)
|
var lastReadings = bgReadingsAccessor.getLatestBgReadings(limit: ConstantsShareWithLoop.maxReadingsToShareWithLoop, fromDate: UserDefaults.standard.timeStampLatestLoopSharedBgReading, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false)
|
||||||
|
|
||||||
|
// if needed, remove readings less than loopDelay minutes old
|
||||||
|
if UserDefaults.standard.loopDelay > 0 {
|
||||||
|
|
||||||
|
while lastReadings.count > 0 && lastReadings[0].timeStamp.addingTimeInterval(TimeInterval(minutes: Double(UserDefaults.standard.loopDelay))) > Date() {
|
||||||
|
|
||||||
|
lastReadings.remove(at: 0)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// if there's no readings, then no further processing
|
// if there's no readings, then no further processing
|
||||||
if lastReadings.count == 0 {
|
if lastReadings.count == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert to json Dexcom Share format
|
// convert to json Dexcom Share format
|
||||||
var dictionary = [Dictionary<String, Any>]()
|
var dictionary = [Dictionary<String, Any>]()
|
||||||
for reading in lastReadings {
|
for reading in lastReadings {
|
||||||
|
@ -59,6 +70,58 @@ public class LoopManager:NSObject {
|
||||||
representation["from"] = "xDrip"
|
representation["from"] = "xDrip"
|
||||||
dictionary.append(representation)
|
dictionary.append(representation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now, if needed, increase the timestamp for each reading
|
||||||
|
if UserDefaults.standard.loopDelay > 0 {
|
||||||
|
|
||||||
|
// create new dictionary that will have the readings with timestamp increased
|
||||||
|
var newDictionary = [Dictionary<String, Any>]()
|
||||||
|
|
||||||
|
// iterate through dictionary
|
||||||
|
for reading in dictionary {
|
||||||
|
|
||||||
|
var readingTimeStamp: Date?
|
||||||
|
if let rawGlucoseStartDate = reading["DT"] as? String {
|
||||||
|
do {
|
||||||
|
|
||||||
|
readingTimeStamp = try self.parseTimestamp(rawGlucoseStartDate)
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let readingTimeStamp = readingTimeStamp, let slopeOrdinal = reading["Trend"] as? Int, let value = reading["Value"] as? Double {
|
||||||
|
|
||||||
|
// create new date : original date + loopDelay
|
||||||
|
let newReadingTimeStamp = readingTimeStamp.addingTimeInterval(TimeInterval(minutes: Double(UserDefaults.standard.loopDelay)))
|
||||||
|
|
||||||
|
// ignore the reading if newReadingTimeStamp > now
|
||||||
|
if newReadingTimeStamp < Date() {
|
||||||
|
|
||||||
|
// this is for the json representation
|
||||||
|
let dateAsString = "/Date(" + Int64(floor(newReadingTimeStamp.toMillisecondsAsDouble() / 1000) * 1000).description + ")/"
|
||||||
|
|
||||||
|
// create new reading and append to new dictionary
|
||||||
|
let newReading: [String : Any] = [
|
||||||
|
"Trend" : slopeOrdinal,
|
||||||
|
"ST" : dateAsString,
|
||||||
|
"DT" : dateAsString,
|
||||||
|
"Value" : value,
|
||||||
|
"direction" : slopeOrdinal
|
||||||
|
]
|
||||||
|
|
||||||
|
newDictionary.append(newReading)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dictionary = newDictionary
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// get Dictionary stored in UserDefaults from previous session
|
// get Dictionary stored in UserDefaults from previous session
|
||||||
// append readings already stored in this storedDictionary so that we get dictionary filled with maxReadingsToShareWithLoop readings, if possible
|
// append readings already stored in this storedDictionary so that we get dictionary filled with maxReadingsToShareWithLoop readings, if possible
|
||||||
|
@ -91,4 +154,13 @@ public class LoopManager:NSObject {
|
||||||
UserDefaults.standard.readingsStoredInSharedUserDefaultsAsDictionary = dictionary
|
UserDefaults.standard.readingsStoredInSharedUserDefaultsAsDictionary = dictionary
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func parseTimestamp(_ timestamp: String) throws -> Date? {
|
||||||
|
let regex = try NSRegularExpression(pattern: "\\((.*)\\)")
|
||||||
|
if let match = regex.firstMatch(in: timestamp, range: NSMakeRange(0, timestamp.count)) {
|
||||||
|
let epoch = Double((timestamp as NSString).substring(with: match.range(at: 1)))! / 1000
|
||||||
|
return Date(timeIntervalSince1970: epoch)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,11 @@ fileprivate enum Setting:Int, CaseIterable {
|
||||||
/// case smooth libre values
|
/// case smooth libre values
|
||||||
case smoothLibreValues = 2
|
case smoothLibreValues = 2
|
||||||
|
|
||||||
|
/// to create artificial delay in readings stored in sharedUserDefaults for loop. Minutes - so that Loop receives more smoothed values.
|
||||||
|
///
|
||||||
|
/// Default value 0, if used then recommende value between 4 and 9
|
||||||
|
case loopDelay = 3
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SettingsViewDevelopmentSettingsViewModel:SettingsViewModelProtocol {
|
struct SettingsViewDevelopmentSettingsViewModel:SettingsViewModelProtocol {
|
||||||
|
@ -42,6 +47,9 @@ struct SettingsViewDevelopmentSettingsViewModel:SettingsViewModelProtocol {
|
||||||
case .smoothLibreValues:
|
case .smoothLibreValues:
|
||||||
return Texts_SettingsView.smoothLibreValues
|
return Texts_SettingsView.smoothLibreValues
|
||||||
|
|
||||||
|
case .loopDelay:
|
||||||
|
return "Loop Delay"
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +62,9 @@ struct SettingsViewDevelopmentSettingsViewModel:SettingsViewModelProtocol {
|
||||||
case .NSLogEnabled, .OSLogEnabled, .smoothLibreValues:
|
case .NSLogEnabled, .OSLogEnabled, .smoothLibreValues:
|
||||||
return UITableViewCell.AccessoryType.none
|
return UITableViewCell.AccessoryType.none
|
||||||
|
|
||||||
|
case .loopDelay:
|
||||||
|
return UITableViewCell.AccessoryType.disclosureIndicator
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,6 +83,9 @@ struct SettingsViewDevelopmentSettingsViewModel:SettingsViewModelProtocol {
|
||||||
case .smoothLibreValues:
|
case .smoothLibreValues:
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
|
case .loopDelay:
|
||||||
|
return UserDefaults.standard.loopDelay.description
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -106,6 +120,9 @@ struct SettingsViewDevelopmentSettingsViewModel:SettingsViewModelProtocol {
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case .loopDelay:
|
||||||
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -123,6 +140,10 @@ struct SettingsViewDevelopmentSettingsViewModel:SettingsViewModelProtocol {
|
||||||
case .NSLogEnabled, .OSLogEnabled, .smoothLibreValues:
|
case .NSLogEnabled, .OSLogEnabled, .smoothLibreValues:
|
||||||
return .nothing
|
return .nothing
|
||||||
|
|
||||||
|
case .loopDelay:
|
||||||
|
return SettingsSelectedRowAction.askText(title: "Loop Delay", message: "Artificial delay in readings when sending to Loop (minutes) - 0 means no delay. Use maximum 10 minutes.", keyboardType: .numberPad, text: UserDefaults.standard.loopDelay.description, placeHolder: "0", actionTitle: nil, cancelTitle: nil, actionHandler: {(interval:String) in if let interval = Int(interval) {UserDefaults.standard.loopDelay = Int(interval)}}, cancelHandler: nil, inputValidator: nil)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue