oop web : site and token can be changed in settings. Also again some code redesign
This commit is contained in:
parent
d3528d0d35
commit
91b5f12a71
|
@ -242,6 +242,8 @@
|
|||
F821CF9622AE589E005C1E43 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; };
|
||||
F821CF9822AE589E005C1E43 /* xdrip.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = xdrip.entitlements; sourceTree = "<group>"; };
|
||||
F821CF9C22AEF483005C1E43 /* BGReadingSpeaker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BGReadingSpeaker.swift; sourceTree = "<group>"; };
|
||||
F846CDD523046BAC00DCF016 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/SettingsViews.strings"; sourceTree = "<group>"; };
|
||||
F846CDD623046BAE00DCF016 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/SettingsViews.strings; sourceTree = "<group>"; };
|
||||
F856CE5A22EDC8E50083E436 /* ConstantsBluetoothPairing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsBluetoothPairing.swift; sourceTree = "<group>"; };
|
||||
F85DC2E721CFE2F500B9F74A /* BgReading+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "BgReading+CoreDataProperties.swift"; path = "../Extensions/BgReading+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
F85DC2E921CFE2F500B9F74A /* Sensor+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Sensor+CoreDataProperties.swift"; path = "../Extensions/Sensor+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
|
@ -1750,6 +1752,8 @@
|
|||
F8B48AFA22B37C5C009BCC01 /* it */,
|
||||
F8B48B0622B37C68009BCC01 /* fr */,
|
||||
F8B48B1222B37C84009BCC01 /* zh */,
|
||||
F846CDD523046BAC00DCF016 /* pt-BR */,
|
||||
F846CDD623046BAE00DCF016 /* pt */,
|
||||
);
|
||||
name = SettingsViews.strings;
|
||||
sourceTree = "<group>";
|
||||
|
|
|
@ -22,7 +22,13 @@ extension UserDefaults {
|
|||
case transmitterTypeAsString = "transmitterTypeAsString"
|
||||
/// transmitterid
|
||||
case transmitterId = "transmitterId"
|
||||
|
||||
/// is web OOP enabled or not
|
||||
case webOOPEnabled = "webOOPEnabled"
|
||||
/// if webOOP enabled, what site to use
|
||||
case webOOPsite = "webOOPsite"
|
||||
/// if webOOP enabled, value of the token
|
||||
case webOOPtoken = "webOOPtoken"
|
||||
|
||||
// Nightscout
|
||||
|
||||
/// should readings be uploaded to nightscout
|
||||
|
@ -115,8 +121,6 @@ extension UserDefaults {
|
|||
/// G6 factor2 - for testing G6 scaling
|
||||
case G6v2ScalingFactor2 = "G6v2ScalingFactor2"
|
||||
|
||||
/// Bubble web oop
|
||||
case webOOPEnabled = "webOOPEnabled"
|
||||
}
|
||||
|
||||
// MARK: - ===== User Configurable Settings ======
|
||||
|
@ -251,6 +255,42 @@ extension UserDefaults {
|
|||
}
|
||||
}
|
||||
|
||||
/// web oop enabled
|
||||
@objc dynamic var webOOPEnabled: Bool {
|
||||
get {
|
||||
return bool(forKey: Key.webOOPEnabled.rawValue)
|
||||
}
|
||||
set {
|
||||
set(newValue, forKey: Key.webOOPEnabled.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
/// web oop site
|
||||
@objc dynamic var webOOPSite:String? {
|
||||
get {
|
||||
return string(forKey: Key.webOOPsite.rawValue)
|
||||
}
|
||||
set {
|
||||
var value = newValue
|
||||
if let newValue = newValue {
|
||||
if !newValue.startsWith("http") {
|
||||
value = "https://" + newValue
|
||||
}
|
||||
}
|
||||
set(value, forKey: Key.webOOPsite.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
/// web oop token
|
||||
@objc dynamic var webOOPtoken:String? {
|
||||
get {
|
||||
return string(forKey: Key.webOOPtoken.rawValue)
|
||||
}
|
||||
set {
|
||||
set(newValue, forKey: Key.webOOPtoken.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Nightscout Share Settings
|
||||
|
||||
/// nightscout enabled ? this impacts follower mode (download) and master mode (upload)
|
||||
|
@ -574,15 +614,7 @@ extension UserDefaults {
|
|||
}
|
||||
|
||||
|
||||
/// web oop enabled
|
||||
@objc dynamic var webOOPEnabled: Bool {
|
||||
get {
|
||||
return bool(forKey: Key.webOOPEnabled.rawValue)
|
||||
}
|
||||
set {
|
||||
set(newValue, forKey: Key.webOOPEnabled.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -39,4 +39,8 @@
|
|||
"settingsviews_Version" = "Version";
|
||||
"settingsviews_license" = "License";
|
||||
"settingsviews_speakIntervalMessage" = "Minimum interval between two readings, in minutes";
|
||||
|
||||
"settingsviews_labelWebOOPSite" = "Web OOP site";
|
||||
"settingsviews_labelWebOOPtoken" = "Web OOP token";
|
||||
"settingsviews_labelWebOOPSiteExplainingText" = "Web OOP site (leave empty to use default value)";
|
||||
"settingsviews_labelWebOOPtokenExplainingText" = "Web OOP token (leave empty to use default value)";
|
||||
"settingsviews_labelWebOOP" = "Web OOP";
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
"settingsviews_settingstitle" = "Settings";
|
||||
"settingsviews_sectiontitlegeneral" = "General";
|
||||
"settingsviews_selectbgunit" = "Select Unit";
|
||||
"settingsviews_lowValue" = "Low Value";
|
||||
"settingsviews_highValue" = "High Value";
|
||||
"settingsviews_sectiontitletransmitter" = "Transmitter";
|
||||
"settingsviews_transmittertype" = "Transmitter Type";
|
||||
"settingsviews_transmitterid" = "Transmitter Id";
|
||||
"settingsviews_givetransmitterid" = "Enter Transmitter Id";
|
||||
"settingsviews_sectiontitlealerting" = "Alerting";
|
||||
"settingsviews_row_alert_types" = "Alert Types";
|
||||
"settingsviews_row_alerts" = "Alerts";
|
||||
"settingsviews_sectiontitlehealthkit" = "HealthKit";
|
||||
"settingsviews_sectiontitledexcomshare" = "Dexcom Share";
|
||||
"settingsviews_uploadReadingstoDexcomShare" = "Dexcom Share Upload";
|
||||
"settingsviews_dexcomShareSerialNumber" = "Serial";
|
||||
"settingsviews_giveDexcomShareSerialNumber" = "Enter Dexcom Share Serial Number";
|
||||
"settingsviews_useUSDexcomShareurl" = "Use US url";
|
||||
"settingsviews_dexcomShareAccountName" = "Account";
|
||||
"settingsviews_giveDexcomShareAccountName" = "Enter Dexcom Share Account Name";
|
||||
"settingsviews_giveDexcomSharePassword" = "Give Dexcom Share Password";
|
||||
"settingsviews_sectiontitlenightscout" = "NightScout";
|
||||
"settingsviews_nightScoutEnabled" = "NightScout Enabled";
|
||||
"settingsviews_nightScoutUrl" = "Url";
|
||||
"settingsviews_giveNightScoutUrl" = "Enter NightScout Url";
|
||||
"settingsviews_nightScoutAPIKey" = "API Secret";
|
||||
"settingsviews_giveNightScoutAPIKey" = "Enter NightScout API Key";
|
||||
"settingsviews_sectiontitlespeak" = "Speak";
|
||||
"settingsviews_speakBgReadings" = "Speak BG Readings";
|
||||
"settingsviews_speakTrend" = "Speak Trend";
|
||||
"settingsviews_speakDelta" = "Speak Delta";
|
||||
"settingsviews_speakInterval" = "Interval";
|
||||
"settingsviews_masterorfollower" = "Master or Follower ?";
|
||||
"settingsviews_master" = "Master";
|
||||
"settingsviews_follower" = "Follower";
|
||||
"settingsviews_speakreadingslanguageselection" = "Select Language";
|
||||
"settingsviews_speakBgReadingslanguage" = "Language";
|
||||
"settingsviews_resettransmitter" = "Reset Transmitter";
|
||||
"settingsviews_Version" = "Version";
|
||||
"settingsviews_license" = "License";
|
||||
"settingsviews_speakIntervalMessage" = "Minimum interval between two readings, in minutes";
|
||||
"settingsviews_labelWebOOPSite" = "Web OOP site";
|
||||
"settingsviews_labelWebOOPtoken" = "Web OOP token";
|
||||
"settingsviews_labelWebOOPSiteExplainingText" = "Web OOP site (leave empty to use default value)";
|
||||
"settingsviews_labelWebOOPtokenExplainingText" = "Web OOP token (leave empty to use default value)";
|
||||
"settingsviews_labelWebOOP" = "Web OOP";
|
|
@ -0,0 +1,46 @@
|
|||
"settingsviews_settingstitle" = "Settings";
|
||||
"settingsviews_sectiontitlegeneral" = "General";
|
||||
"settingsviews_selectbgunit" = "Select Unit";
|
||||
"settingsviews_lowValue" = "Low Value";
|
||||
"settingsviews_highValue" = "High Value";
|
||||
"settingsviews_sectiontitletransmitter" = "Transmitter";
|
||||
"settingsviews_transmittertype" = "Transmitter Type";
|
||||
"settingsviews_transmitterid" = "Transmitter Id";
|
||||
"settingsviews_givetransmitterid" = "Enter Transmitter Id";
|
||||
"settingsviews_sectiontitlealerting" = "Alerting";
|
||||
"settingsviews_row_alert_types" = "Alert Types";
|
||||
"settingsviews_row_alerts" = "Alerts";
|
||||
"settingsviews_sectiontitlehealthkit" = "HealthKit";
|
||||
"settingsviews_sectiontitledexcomshare" = "Dexcom Share";
|
||||
"settingsviews_uploadReadingstoDexcomShare" = "Dexcom Share Upload";
|
||||
"settingsviews_dexcomShareSerialNumber" = "Serial";
|
||||
"settingsviews_giveDexcomShareSerialNumber" = "Enter Dexcom Share Serial Number";
|
||||
"settingsviews_useUSDexcomShareurl" = "Use US url";
|
||||
"settingsviews_dexcomShareAccountName" = "Account";
|
||||
"settingsviews_giveDexcomShareAccountName" = "Enter Dexcom Share Account Name";
|
||||
"settingsviews_giveDexcomSharePassword" = "Give Dexcom Share Password";
|
||||
"settingsviews_sectiontitlenightscout" = "NightScout";
|
||||
"settingsviews_nightScoutEnabled" = "NightScout Enabled";
|
||||
"settingsviews_nightScoutUrl" = "Url";
|
||||
"settingsviews_giveNightScoutUrl" = "Enter NightScout Url";
|
||||
"settingsviews_nightScoutAPIKey" = "API Secret";
|
||||
"settingsviews_giveNightScoutAPIKey" = "Enter NightScout API Key";
|
||||
"settingsviews_sectiontitlespeak" = "Speak";
|
||||
"settingsviews_speakBgReadings" = "Speak BG Readings";
|
||||
"settingsviews_speakTrend" = "Speak Trend";
|
||||
"settingsviews_speakDelta" = "Speak Delta";
|
||||
"settingsviews_speakInterval" = "Interval";
|
||||
"settingsviews_masterorfollower" = "Master or Follower ?";
|
||||
"settingsviews_master" = "Master";
|
||||
"settingsviews_follower" = "Follower";
|
||||
"settingsviews_speakreadingslanguageselection" = "Select Language";
|
||||
"settingsviews_speakBgReadingslanguage" = "Language";
|
||||
"settingsviews_resettransmitter" = "Reset Transmitter";
|
||||
"settingsviews_Version" = "Version";
|
||||
"settingsviews_license" = "License";
|
||||
"settingsviews_speakIntervalMessage" = "Minimum interval between two readings, in minutes";
|
||||
"settingsviews_labelWebOOPSite" = "Web OOP site";
|
||||
"settingsviews_labelWebOOPtoken" = "Web OOP token";
|
||||
"settingsviews_labelWebOOPSiteExplainingText" = "Web OOP site (leave empty to use default value)";
|
||||
"settingsviews_labelWebOOPtokenExplainingText" = "Web OOP token (leave empty to use default value)";
|
||||
"settingsviews_labelWebOOP" = "Web OOP";
|
|
@ -63,7 +63,27 @@ class Texts_SettingsView {
|
|||
}()
|
||||
|
||||
static let labelWebOOPTransmitter:String = {
|
||||
return NSLocalizedString("settingsviews_webooptransmitter", tableName: filename, bundle: Bundle.main, value: "Web OOP Enabled", comment: "transmitter settings, to explain that settings is about resetting the transmitter")
|
||||
return NSLocalizedString("settingsviews_webooptransmitter", tableName: filename, bundle: Bundle.main, value: "Web OOP Enabled", comment: "transmitter settings, to enable or didsable web oop")
|
||||
}()
|
||||
|
||||
static let labelWebOOPSite:String = {
|
||||
return NSLocalizedString("settingsviews_labelWebOOPSite", tableName: filename, bundle: Bundle.main, value: "Web OOP site", comment: "transmitter settings, the web oop site url")
|
||||
}()
|
||||
|
||||
static let labelWebOOPtoken:String = {
|
||||
return NSLocalizedString("settingsviews_labelWebOOPtoken", tableName: filename, bundle: Bundle.main, value: "Web OOP token", comment: "transmitter settings, the web oop token")
|
||||
}()
|
||||
|
||||
static let labelWebOOPSiteExplainingText:String = {
|
||||
return NSLocalizedString("settingsviews_labelWebOOPSiteExplainingText", tableName: filename, bundle: Bundle.main, value: "Web OOP site (leave empty to use default value)", comment: "transmitter settings, the web oop site url, explaining text in dialog")
|
||||
}()
|
||||
|
||||
static let labelWebOOPtokenExplainingText:String = {
|
||||
return NSLocalizedString("settingsviews_labelWebOOPtokenExplainingText", tableName: filename, bundle: Bundle.main, value: "Web OOP token (leave empty to use default value)", comment: "transmitter settings, the web oop token, explaining text in dialog")
|
||||
}()
|
||||
|
||||
static let labelWebOOP:String = {
|
||||
return NSLocalizedString("settingsviews_labelWebOOP", tableName: filename, bundle: Bundle.main, value: "Web OOP", comment: "transmitter settings, title of the dialogs where site and token are asked")
|
||||
}()
|
||||
|
||||
// MARK: - Section Alerts
|
||||
|
|
|
@ -170,6 +170,9 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
|
|||
func setWebOOPEnabled(enabled: Bool) {
|
||||
}
|
||||
|
||||
/// this transmitter does not support oop web
|
||||
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {}
|
||||
|
||||
// MARK: helper functions
|
||||
|
||||
private func processxBridgeDataPacket(value:Data) -> (glucoseData:GlucoseData?, batteryLevel:Int?, transmitterID:String?) {
|
||||
|
|
|
@ -266,6 +266,9 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
func setWebOOPEnabled(enabled: Bool) {
|
||||
}
|
||||
|
||||
/// this transmitter does not support oop web
|
||||
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {}
|
||||
|
||||
// MARK: BluetoothTransmitterDelegate functions
|
||||
|
||||
func centralManagerDidConnect(address:String?, name:String?) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import Foundation
|
||||
import CoreBluetooth
|
||||
|
||||
/// defines functions that every cgm transmitter should conform to, mainly used by rootviewcontroller to get transmitter address, name, deterine status etc.
|
||||
/// defines functions that every cgm transmitter should conform to, mainly used by rootviewcontroller
|
||||
///
|
||||
/// Most of the functions are already defined by BlueToothTransmitter.swift - so most of these functions don't need re-implementation in CGMTransmitter classes that conform to this protocol.
|
||||
///
|
||||
|
@ -46,6 +46,12 @@ protocol CGMTransmitter {
|
|||
/// for transmitters who don't support webOOP, there's no need to implemented this function<br>
|
||||
/// --- for transmitters who support webOOP (Bubble, MiaoMiao, ..) this should be implemented
|
||||
func setWebOOPEnabled(enabled:Bool)
|
||||
|
||||
/// to set oopWebSite and oopWebToken - called when user change the setting
|
||||
///
|
||||
/// for transmitters who don't support webOOP, there's no need to implemented this function<br>
|
||||
/// --- for transmitters who support webOOP (Bubble, MiaoMiao, ..) this should be implemented
|
||||
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String)
|
||||
|
||||
}
|
||||
|
||||
|
@ -135,9 +141,9 @@ enum CGMTransmitterType:String, CaseIterable {
|
|||
|
||||
func canWebOOP() -> Bool {
|
||||
|
||||
return false
|
||||
//return false
|
||||
|
||||
/*switch self {
|
||||
switch self {
|
||||
|
||||
case .dexcomG4:
|
||||
return false
|
||||
|
@ -160,7 +166,7 @@ enum CGMTransmitterType:String, CaseIterable {
|
|||
case .Droplet1:
|
||||
return false
|
||||
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
/// returns nil if id to validate has expected length and type of characters etc.
|
||||
|
|
|
@ -45,6 +45,9 @@ protocol CGMTransmitterDelegate:AnyObject {
|
|||
|
||||
/// transmitter reset result
|
||||
func reset(successful: Bool)
|
||||
|
||||
/// to pass some text error message, delegate can decide to show to user, log, ...
|
||||
func error(message: String)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -231,7 +231,8 @@ class CGMBluconTransmitter: BluetoothTransmitter {
|
|||
return(curGluc)
|
||||
}
|
||||
|
||||
|
||||
/// this transmitter does not support oop web
|
||||
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,12 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
|
|||
// current sensor serial number, if nil then it's not known yet
|
||||
private var sensorSerialNumber:String?
|
||||
|
||||
/// oop website url to use in case oop web would be enabled
|
||||
private var oopWebSite: String
|
||||
|
||||
/// oop token to use in case oop web would be enabled
|
||||
private var oopWebToken: String
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// - parameters:
|
||||
|
@ -53,7 +59,9 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
|
|||
/// - delegate : CGMTransmitterDelegate intance
|
||||
/// - timeStampLastBgReading : timestamp of last bgReading
|
||||
/// - webOOPEnabled : enabled or not
|
||||
init(address:String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?, webOOPEnabled: Bool) {
|
||||
/// - oopWebSite : oop web site url to use, only used in case webOOPEnabled = true
|
||||
/// - oopWebToken : oop web token to use, only used in case webOOPEnabled = true
|
||||
init(address:String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?, webOOPEnabled: Bool, oopWebSite: String, oopWebToken: String) {
|
||||
|
||||
// assign addressname and name or expected devicename
|
||||
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: expectedDeviceNameBubble)
|
||||
|
@ -77,6 +85,10 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
|
|||
// initialize webOOPEnabled
|
||||
self.webOOPEnabled = webOOPEnabled
|
||||
|
||||
// initialize oopWebToken and oopWebSite
|
||||
self.oopWebToken = oopWebToken
|
||||
self.oopWebSite = oopWebSite
|
||||
|
||||
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_Bubble)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_Bubble, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_Bubble, startScanningAfterInit: CGMTransmitterType.Bubble.startScanningAfterInit())
|
||||
|
||||
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
|
||||
|
@ -176,7 +188,7 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
|
|||
|
||||
}
|
||||
|
||||
LibreDataParser.libreDataProcessor(sensorSerialNumber: sensorSerialNumber, webOOPEnabled: webOOPEnabled, libreData: (rxBuffer.subdata(in: bubbleHeaderLength..<(344 + bubbleHeaderLength))), cgmTransmitterDelegate: cgmTransmitterDelegate, transmitterBatteryInfo: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, timeStampLastBgReading: timeStampLastBgReading, completionHandler: {(timeStampLastBgReading:Date) in
|
||||
LibreDataParser.libreDataProcessor(sensorSerialNumber: sensorSerialNumber, webOOPEnabled: webOOPEnabled, oopWebSite: oopWebSite, oopWebToken: oopWebToken, libreData: (rxBuffer.subdata(in: bubbleHeaderLength..<(344 + bubbleHeaderLength))), cgmTransmitterDelegate: cgmTransmitterDelegate, transmitterBatteryInfo: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, timeStampLastBgReading: timeStampLastBgReading, completionHandler: {(timeStampLastBgReading:Date) in
|
||||
self.timeStampLastBgReading = timeStampLastBgReading
|
||||
|
||||
})
|
||||
|
@ -222,6 +234,12 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
|
|||
webOOPEnabled = enabled
|
||||
}
|
||||
|
||||
|
||||
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {
|
||||
self.oopWebToken = oopWebToken
|
||||
self.oopWebSite = oopWebSite
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate enum BubbleResponseType: UInt8 {
|
||||
|
|
|
@ -144,4 +144,7 @@ class CGMDroplet1Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
func setWebOOPEnabled(enabled: Bool) {
|
||||
}
|
||||
|
||||
/// this transmitter does not support oop web
|
||||
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {}
|
||||
|
||||
}
|
||||
|
|
|
@ -267,6 +267,9 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
func setWebOOPEnabled(enabled: Bool) {
|
||||
}
|
||||
|
||||
/// this transmitter does not support oop web
|
||||
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {}
|
||||
|
||||
// MARK: CBCentralManager overriden functions
|
||||
|
||||
override func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
|
||||
|
|
|
@ -46,13 +46,21 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
/// is the transmitter oop web enabled or not
|
||||
private var webOOPEnabled: Bool
|
||||
|
||||
/// oop website url to use in case oop web would be enabled
|
||||
private var oopWebSite: String
|
||||
|
||||
/// oop token to use in case oop web would be enabled
|
||||
private var oopWebToken: String
|
||||
|
||||
// MARK: - Initialization
|
||||
/// - parameters:
|
||||
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
/// - delegate : CGMTransmitterDelegate intance
|
||||
/// - timeStampLastBgReading : timestamp of last bgReading
|
||||
/// - webOOPEnabled : enabled or not
|
||||
init(address:String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, webOOPEnabled: Bool) {
|
||||
/// - oopWebSite : oop web site url to use, only used in case webOOPEnabled = true
|
||||
/// - oopWebToken : oop web token to use, only used in case webOOPEnabled = true
|
||||
init(address:String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, webOOPEnabled: Bool, oopWebSite: String, oopWebToken: String) {
|
||||
|
||||
// assign addressname and name or expected devicename
|
||||
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: expectedDeviceNameMiaoMiao)
|
||||
|
@ -73,6 +81,10 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
// initialize webOOPEnabled
|
||||
self.webOOPEnabled = webOOPEnabled
|
||||
|
||||
// initialize oopWebToken and oopWebSite
|
||||
self.oopWebToken = oopWebToken
|
||||
self.oopWebSite = oopWebSite
|
||||
|
||||
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_MiaoMiao)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_MiaoMiao, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_MiaoMiao, startScanningAfterInit: CGMTransmitterType.miaomiao.startScanningAfterInit())
|
||||
|
||||
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
|
||||
|
@ -142,7 +154,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
let hardware = String(describing: rxBuffer[16...17].hexEncodedString())
|
||||
let batteryPercentage = Int(rxBuffer[13])
|
||||
|
||||
LibreDataParser.libreDataProcessor(sensorSerialNumber: LibreSensorSerialNumber(withUID: Data(rxBuffer.subdata(in: 5..<13)))?.serialNumber, webOOPEnabled: webOOPEnabled, libreData: (rxBuffer.subdata(in: miaoMiaoHeaderLength..<(344 + miaoMiaoHeaderLength))), cgmTransmitterDelegate: cgmTransmitterDelegate, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), firmware: firmware, hardware: hardware, hardwareSerialNumber: nil, bootloader: nil, timeStampLastBgReading: timeStampLastBgReading, completionHandler: {(timeStampLastBgReading:Date) in
|
||||
LibreDataParser.libreDataProcessor(sensorSerialNumber: LibreSensorSerialNumber(withUID: Data(rxBuffer.subdata(in: 5..<13)))?.serialNumber, webOOPEnabled: webOOPEnabled, oopWebSite: oopWebSite, oopWebToken: oopWebToken, libreData: (rxBuffer.subdata(in: miaoMiaoHeaderLength..<(344 + miaoMiaoHeaderLength))), cgmTransmitterDelegate: cgmTransmitterDelegate, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), firmware: firmware, hardware: hardware, hardwareSerialNumber: nil, bootloader: nil, timeStampLastBgReading: timeStampLastBgReading, completionHandler: {(timeStampLastBgReading:Date) in
|
||||
self.timeStampLastBgReading = timeStampLastBgReading
|
||||
|
||||
})
|
||||
|
@ -236,6 +248,11 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
_ = sendStartReadingCommmand()
|
||||
}
|
||||
|
||||
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {
|
||||
self.oopWebToken = oopWebToken
|
||||
self.oopWebSite = oopWebSite
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fileprivate enum MiaoMiaoResponseType: UInt8 {
|
||||
|
|
|
@ -5,9 +5,9 @@ class LibreDataParser {
|
|||
/// parses libre block
|
||||
/// - parameters:
|
||||
/// - libreData: the 344 bytes block from Libre
|
||||
/// - timeStampLastBgReadingStoredInDatabase: this is of the timestamp of the latest reading we already received during previous session
|
||||
/// - timeStampLastBgReading: this is of the timestamp of the latest reading we already received during previous session
|
||||
/// - returns:
|
||||
/// - array of GlucoseData, first is the most recent, LibreSensorState. Only returns recent readings, ie not the ones that are older than timeStampLastBgReadingStoredInDatabase. 30 seconds are added here, meaning, new reading should be at least 30 seconds more recent than timeStampLastBgReadingStoredInDatabase
|
||||
/// - array of GlucoseData, first is the most recent. Only returns recent readings, ie not the ones that are older than timeStampLastBgReading. 30 seconds are added here, meaning, new reading should be at least 30 seconds more recent than timeStampLastBgReading
|
||||
/// - sensorState: status of the sensor
|
||||
/// - sensorTimeInMinutes: age of sensor in minutes
|
||||
public static func parse(libreData: Data, timeStampLastBgReading:Date) -> (glucoseData:[GlucoseData], sensorState:LibreSensorState, sensorTimeInMinutes:Int) {
|
||||
|
@ -34,7 +34,7 @@ class LibreDataParser {
|
|||
if i < 0 {i += 16}
|
||||
timeInMinutes = max(0, (Double)(sensorTimeInMinutes - index))
|
||||
let timeStampOfNewGlucoseData = sensorStartTimeInMilliseconds + timeInMinutes * 60 * 1000
|
||||
//new reading should be at least 30 seconds younger than timeStampLastBgReadingStoredInDatabase
|
||||
//new reading should be at least 30 seconds younger than timeStampLastBgReading
|
||||
if timeStampOfNewGlucoseData > (timeStampLastBgReading.toMillisecondsAsDouble() + 30000.0)
|
||||
{
|
||||
if timeStampOfNewGlucoseData < timeStampLastAddedGlucoseData - (5 * 60 * 1000 - 10000) {
|
||||
|
@ -59,7 +59,7 @@ class LibreDataParser {
|
|||
if i < 0 {i += 32}
|
||||
timeInMinutes = max(0,(Double)(abs(sensorTimeInMinutes - 3)/15)*15 - (Double)(index*15))
|
||||
let timeStampOfNewGlucoseData = sensorStartTimeInMilliseconds + timeInMinutes * 60 * 1000
|
||||
//new reading should be at least 30 seconds younger than timeStampLastBgReadingStoredInDatabase
|
||||
//new reading should be at least 30 seconds younger than timeStampLastBgReading
|
||||
if timeStampOfNewGlucoseData > (timeStampLastBgReading.toMillisecondsAsDouble() + 30000.0)
|
||||
{
|
||||
if timeStampOfNewGlucoseData < timeStampLastAddedGlucoseData - (5 * 60 * 1000 - 10000) {
|
||||
|
@ -87,23 +87,24 @@ class LibreDataParser {
|
|||
/// - libreData : the 344 bytes from Libre sensor
|
||||
/// - timeStampLastBgReading : timestamp of last reading, older readings will be ignored
|
||||
/// - webOOPEnabled : is webOOP enabled or not, if not enabled, local parsing is used
|
||||
/// - oopWebSite : the site url to use if oop web would be enabled
|
||||
/// - oopWebToken : the token to use if oop web would be enabled
|
||||
/// - cgmTransmitterDelegate : the cgmTransmitterDelegate
|
||||
/// - completionHandler : will be called when glucose data is read with as parameter the timestamp of the last reading. Goal is that caller an set timeStampLastBgReading to the new value
|
||||
/// - transmitterBatteryInfo : not mandatory, if nil then delegate will simply not receive it, possibly the delegate already received it before, and if not
|
||||
/// - firmware : not mandatory, if nil then delegate will simply not receive it, possibly the delegate already received it before, and if not or maybe it doesn't exist for the specific type of transmitter
|
||||
/// - hardware : not mandatory, if nil then delegate will simply not receive it, possibly the delegate already received it before, and if not or maybe it doesn't exist for the specific type of transmitter
|
||||
/// - hardwareSerialNumber : not mandatory, if nil then delegate will simply not receive it, possibly the delegate already received it before, and if not or maybe it doesn't exist for the specific type of transmitter
|
||||
/// - bootloader : not mandatory, if nil then delegate will simply not receive it, possibly the delegate already received it before, and if not or maybe it doesn't exist for the specific type of transmitter
|
||||
/// - completionHandler : will be called when glucose data is read with as parameter the timestamp of the last reading. Goal is that caller an set timeStampLastBgReading to the new value
|
||||
///
|
||||
/// parameter values that are not known, simply ignore them, if they are not known then they are probably not important, or they've already been passed to the delegate before.
|
||||
public static func libreDataProcessor(sensorSerialNumber: String?, webOOPEnabled: Bool, libreData: Data, cgmTransmitterDelegate : CGMTransmitterDelegate?, transmitterBatteryInfo:TransmitterBatteryInfo?, firmware: String?, hardware: String?, hardwareSerialNumber: String?, bootloader:String?, timeStampLastBgReading: Date, completionHandler:@escaping ((_ timeStampLastBgReading: Date) -> ())) {
|
||||
public static func libreDataProcessor(sensorSerialNumber: String?, webOOPEnabled: Bool, oopWebSite: String, oopWebToken: String, libreData: Data, cgmTransmitterDelegate : CGMTransmitterDelegate?, transmitterBatteryInfo:TransmitterBatteryInfo?, firmware: String?, hardware: String?, hardwareSerialNumber: String?, bootloader:String?, timeStampLastBgReading: Date, completionHandler:@escaping ((_ timeStampLastBgReading: Date) -> ())) {
|
||||
|
||||
if let sensorSerialNumber = sensorSerialNumber, webOOPEnabled {
|
||||
LibreOOPClient.handleLibreData(libreData: [UInt8](libreData), timeStampLastBgReading: timeStampLastBgReading, serialNumber: sensorSerialNumber) {
|
||||
LibreOOPClient.handleLibreData(libreData: libreData, timeStampLastBgReading: timeStampLastBgReading, serialNumber: sensorSerialNumber, oopWebSite: oopWebSite, oopWebToken: oopWebToken) {
|
||||
(result) in
|
||||
if let res = result {
|
||||
handleGlucoseData(result: res, cgmTransmitterDelegate: cgmTransmitterDelegate, transmitterBatteryInfo: transmitterBatteryInfo, firmware: firmware, hardware: hardware, hardwareSerialNumber: hardwareSerialNumber, bootloader: bootloader, sensorSerialNumber: sensorSerialNumber, completionHandler: completionHandler)
|
||||
}
|
||||
handleGlucoseData(result: result, cgmTransmitterDelegate: cgmTransmitterDelegate, transmitterBatteryInfo: transmitterBatteryInfo, firmware: firmware, hardware: hardware, hardwareSerialNumber: hardwareSerialNumber, bootloader: bootloader, sensorSerialNumber: sensorSerialNumber, completionHandler: completionHandler)
|
||||
|
||||
}
|
||||
} else if !webOOPEnabled {
|
||||
// use local parser
|
||||
|
@ -127,21 +128,31 @@ fileprivate func getGlucoseRaw(bytes:Data) -> Int {
|
|||
/// calls LibreDataParser.parse - calls handleGlucoseData
|
||||
fileprivate func process(libreData: Data, timeStampLastBgReading: Date, cgmTransmitterDelegate : CGMTransmitterDelegate?, transmitterBatteryInfo:TransmitterBatteryInfo?, firmware: String?, hardware: String?, hardwareSerialNumber: String?, bootloader:String?, sensorSerialNumber:String?, completionHandler:((_ timeStampLastBgReading: Date) -> ())) {
|
||||
|
||||
//get readings from buffer and send to delegate
|
||||
//get readings from buffer
|
||||
let result = LibreDataParser.parse(libreData: libreData, timeStampLastBgReading: timeStampLastBgReading)
|
||||
|
||||
handleGlucoseData(result: result, cgmTransmitterDelegate: cgmTransmitterDelegate, transmitterBatteryInfo: transmitterBatteryInfo, firmware: firmware, hardware: hardware, hardwareSerialNumber: hardwareSerialNumber, bootloader: bootloader, sensorSerialNumber: sensorSerialNumber, completionHandler: completionHandler)
|
||||
// add errordescription nil, needed by handleGlucoseData
|
||||
let resultWithErrorDescription: ([GlucoseData], LibreSensorState, Int, String?) = (result.glucoseData, result.sensorState, result.sensorTimeInMinutes, nil)
|
||||
|
||||
handleGlucoseData(result: resultWithErrorDescription, cgmTransmitterDelegate: cgmTransmitterDelegate, transmitterBatteryInfo: transmitterBatteryInfo, firmware: firmware, hardware: hardware, hardwareSerialNumber: hardwareSerialNumber, bootloader: bootloader, sensorSerialNumber: sensorSerialNumber, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
/// calls delegate with parameters from result, will change value of timeStampLastBgReading
|
||||
fileprivate func handleGlucoseData(result: (glucoseData:[GlucoseData], sensorState: LibreSensorState, sensorTimeInMinutes:Int), cgmTransmitterDelegate : CGMTransmitterDelegate?, transmitterBatteryInfo:TransmitterBatteryInfo?, firmware:String?, hardware:String?, hardwareSerialNumber:String?, bootloader:String?, sensorSerialNumber:String?, completionHandler:((_ timeStampLastBgReading: Date) -> ())) {
|
||||
/// calls delegate with parameters from result
|
||||
///
|
||||
/// if result.errorDescription not nil, then delegate function error will be called
|
||||
fileprivate func handleGlucoseData(result: (glucoseData:[GlucoseData], sensorState: LibreSensorState, sensorTimeInMinutes:Int, errorDescription: String?), cgmTransmitterDelegate : CGMTransmitterDelegate?, transmitterBatteryInfo:TransmitterBatteryInfo?, firmware:String?, hardware:String?, hardwareSerialNumber:String?, bootloader:String?, sensorSerialNumber:String?, completionHandler:((_ timeStampLastBgReading: Date) -> ())) {
|
||||
|
||||
var result = result
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: result.sensorState, sensorTimeInMinutes: result.sensorTimeInMinutes, firmware: firmware, hardware: hardware, hardwareSerialNumber: hardwareSerialNumber, bootloader: bootloader, sensorSerialNumber: sensorSerialNumber)
|
||||
|
||||
//set timeStampLastBgReading to timestamp of latest reading in the response so that next time we parse only the more recent readings
|
||||
if result.glucoseData.count > 0 {
|
||||
completionHandler(result.glucoseData[0].timeStamp)
|
||||
if let errorDescription = result.errorDescription {
|
||||
cgmTransmitterDelegate?.error(message: "Web OOP : " + errorDescription)
|
||||
} else {
|
||||
var result = result
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: result.sensorState, sensorTimeInMinutes: result.sensorTimeInMinutes, firmware: firmware, hardware: hardware, hardwareSerialNumber: hardwareSerialNumber, bootloader: bootloader, sensorSerialNumber: sensorSerialNumber)
|
||||
|
||||
//set timeStampLastBgReading to timestamp of latest reading in the response so that next time we parse only the more recent readings
|
||||
if result.glucoseData.count > 0 {
|
||||
completionHandler(result.glucoseData[0].timeStamp)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -16,13 +16,13 @@ public struct LibreDerivedAlgorithmParameters: Codable, CustomStringConvertible
|
|||
public var isValidForFooterWithReverseCRCs: Int
|
||||
public var extraSlope : Double = 1
|
||||
public var extraOffset: Double = 0
|
||||
public var serialNumber: String?
|
||||
public var serialNumber: String
|
||||
|
||||
public var description: String {
|
||||
return "LibreDerivedAlgorithmParameters:: slopeslope: \(slope_slope), slopeoffset: \(slope_offset), offsetoffset: \(offset_offset), offsetSlope: \(offset_slope), extraSlope: \(extraSlope), extraOffset: \(extraOffset), isValidForFooterWithReverseCRCs: \(isValidForFooterWithReverseCRCs)"
|
||||
}
|
||||
|
||||
public init(slope_slope: Double, slope_offset:Double, offset_slope: Double, offset_offset: Double, isValidForFooterWithReverseCRCs: Int, extraSlope: Double, extraOffset: Double) {
|
||||
public init(slope_slope: Double, slope_offset:Double, offset_slope: Double, offset_offset: Double, isValidForFooterWithReverseCRCs: Int, extraSlope: Double, extraOffset: Double, sensorSerialNumber:String) {
|
||||
self.slope_slope = slope_slope
|
||||
self.slope_offset = slope_offset
|
||||
self.offset_slope = offset_slope
|
||||
|
@ -30,6 +30,7 @@ public struct LibreDerivedAlgorithmParameters: Codable, CustomStringConvertible
|
|||
self.isValidForFooterWithReverseCRCs = isValidForFooterWithReverseCRCs
|
||||
self.extraSlope = extraSlope
|
||||
self.extraOffset = extraOffset
|
||||
self.serialNumber = sensorSerialNumber
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,147 +29,174 @@ class LibreOOPClient {
|
|||
|
||||
// MARK: - public functions
|
||||
|
||||
public static func handleLibreData(libreData: [UInt8], timeStampLastBgReading: Date, serialNumber: String, _ callback: @escaping ((glucoseData: [GlucoseData], sensorState: LibreSensorState, sensorTimeInMinutes: Int)?) -> Void) {
|
||||
|
||||
//only care about the once per minute readings here, historical data will not be considered
|
||||
public static func handleLibreData(libreData: Data, timeStampLastBgReading: Date, serialNumber: String, oopWebSite: String, oopWebToken: String, _ callback: @escaping ((glucoseData: [GlucoseData], sensorState: LibreSensorState, sensorTimeInMinutes: Int, errorDescription:String?)) -> Void) {
|
||||
|
||||
let sensorState = LibreSensorState(stateByte: libreData[4])
|
||||
|
||||
LibreOOPClient.calibrateSensor(bytes: libreData, serialNumber: serialNumber) {
|
||||
(calibrationparams) in
|
||||
guard let params = calibrationparams else {
|
||||
|
||||
callback(nil)
|
||||
|
||||
return
|
||||
|
||||
LibreOOPClient.calibrateSensor(bytes: libreData, serialNumber: serialNumber, site: oopWebSite, token: oopWebToken) {
|
||||
(libreDerivedAlgorithmParameters, errorDescription) in
|
||||
|
||||
// define default result that will be returned in defer statement
|
||||
var finalResult:[GlucoseData] = []
|
||||
let sensorTimeInMinutes:Int = 256 * (Int)(libreData.uint8(position: 317) & 0xFF) + (Int)(libreData.uint8(position: 316) & 0xFF)
|
||||
var errorDescription = errorDescription
|
||||
defer {
|
||||
callback((finalResult, sensorState, sensorTimeInMinutes, errorDescription))
|
||||
}
|
||||
|
||||
// if errorDescription received from call to LibreOOPClient.calibrateSensor not nil then no need to continue
|
||||
if errorDescription != nil {return}
|
||||
|
||||
guard let libreDerivedAlgorithmParameters = libreDerivedAlgorithmParameters else {
|
||||
// shouldn't happen because if libreDerivedAlgorithmParameters is nil, it means something went wrong in call to calibrateSensor and so errorDescription should not be nil
|
||||
errorDescription = "libreDerivedAlgorithmParameters is nil"
|
||||
return
|
||||
}
|
||||
|
||||
//here we assume success, data is not changed,
|
||||
//and we trust that the remote endpoint returns correct data for the sensor
|
||||
let last16 = trendMeasurements(bytes: libreData, date: Date(), timeStampLastBgReading: timeStampLastBgReading, LibreDerivedAlgorithmParameterSet: params)
|
||||
if let glucoseData = trendToLibreGlucose(last16) {
|
||||
let last16 = trendMeasurements(bytes: libreData, date: Date(), timeStampLastBgReading: timeStampLastBgReading, LibreDerivedAlgorithmParameterSet: libreDerivedAlgorithmParameters)
|
||||
|
||||
let glucoseData = trendToLibreGlucose(last16)
|
||||
|
||||
// return only readings that are at least 5 minutes away from each other, except the first, same approach as in LibreDataParser.parse
|
||||
|
||||
var finalResult:[GlucoseData] = []
|
||||
|
||||
// we will add the most recent readings, but then we'll only add the readings that are at least 5 minutes apart (giving 10 seconds spare)
|
||||
// for that variable timeStampLastAddedGlucoseData is used. It's initially set to now + 5 minutes
|
||||
var timeStampLastAddedGlucoseData = Date().toMillisecondsAsDouble() + 5 * 60 * 1000
|
||||
// return only readings that are at least 5 minutes away from each other, except the first, same approach as in LibreDataParser.parse
|
||||
|
||||
// we will add the most recent readings, but then we'll only add the readings that are at least 5 minutes apart (giving 10 seconds spare)
|
||||
// for that variable timeStampLastAddedGlucoseData is used. It's initially set to now + 5 minutes
|
||||
var timeStampLastAddedGlucoseData = Date().toMillisecondsAsDouble() + 5 * 60 * 1000
|
||||
|
||||
for glucose in glucoseData {
|
||||
|
||||
for glucose in glucoseData {
|
||||
|
||||
let timeStampOfNewGlucoseData = glucose.timeStamp
|
||||
if timeStampOfNewGlucoseData.toMillisecondsAsDouble() > (timeStampLastBgReading.toMillisecondsAsDouble() + 30000.0) {
|
||||
if timeStampOfNewGlucoseData.toMillisecondsAsDouble() < timeStampLastAddedGlucoseData - (5 * 60 * 1000 - 10000) {
|
||||
timeStampLastAddedGlucoseData = timeStampOfNewGlucoseData.toMillisecondsAsDouble()
|
||||
finalResult.append(glucose)
|
||||
}
|
||||
} else {
|
||||
break
|
||||
let timeStampOfNewGlucoseData = glucose.timeStamp
|
||||
if timeStampOfNewGlucoseData.toMillisecondsAsDouble() > (timeStampLastBgReading.toMillisecondsAsDouble() + 30000.0) {
|
||||
if timeStampOfNewGlucoseData.toMillisecondsAsDouble() < timeStampLastAddedGlucoseData - (5 * 60 * 1000 - 10000) {
|
||||
timeStampLastAddedGlucoseData = timeStampOfNewGlucoseData.toMillisecondsAsDouble()
|
||||
finalResult.append(glucose)
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
||||
callback((finalResult, sensorState, 0))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static func calibrateSensor(bytes: [UInt8], serialNumber: String, callback: @escaping (LibreDerivedAlgorithmParameters?) -> Void) {
|
||||
private static func calibrateSensor(bytes: Data, serialNumber: String, site: String, token: String, callback: @escaping (LibreDerivedAlgorithmParameters?, _ errorDescription:String?) -> Void) {
|
||||
|
||||
/// first try to get libreDerivedAlgorithmParameters for the sensor from disk
|
||||
let url = URL.init(fileURLWithPath: filePath)
|
||||
if FileManager.default.fileExists(atPath: url.path) {
|
||||
let decoder = JSONDecoder()
|
||||
do {
|
||||
let data = try Data.init(contentsOf: url)
|
||||
let response = try decoder.decode(LibreDerivedAlgorithmParameters.self, from: data)
|
||||
if response.serialNumber == serialNumber {
|
||||
callback(response)
|
||||
let libreDerivedAlgorithmParameters = try decoder.decode(LibreDerivedAlgorithmParameters.self, from: data)
|
||||
if libreDerivedAlgorithmParameters.serialNumber == serialNumber {
|
||||
// successfully retrieved libreDerivedAlgorithmParameters for current sensor, from disk
|
||||
callback(libreDerivedAlgorithmParameters, nil)
|
||||
return
|
||||
}
|
||||
} catch {
|
||||
|
||||
print("decoder error:", error)
|
||||
|
||||
// data not found on disk, we need to continue
|
||||
}
|
||||
}
|
||||
|
||||
post(bytes: bytes, { (data, str, can) in
|
||||
let decoder = JSONDecoder()
|
||||
do {
|
||||
let response = try decoder.decode(GetCalibrationStatus.self, from: data)
|
||||
if let slope = response.slope {
|
||||
var para = LibreDerivedAlgorithmParameters.init(slope_slope: slope.slopeSlope ?? 0, slope_offset: slope.slopeOffset ?? 0, offset_slope: slope.offsetSlope ?? 0, offset_offset: slope.offsetOffset ?? 0, isValidForFooterWithReverseCRCs: Int(slope.isValidForFooterWithReverseCRCs ?? 1), extraSlope: 1.0, extraOffset: 0.0)
|
||||
para.serialNumber = serialNumber
|
||||
do {
|
||||
let data = try JSONEncoder().encode(para)
|
||||
save(data: data)
|
||||
} catch {
|
||||
trace("in calibrateSensor, error : %{public}@@", log: log, type: .error, error.localizedDescription)
|
||||
}
|
||||
|
||||
callback(para)
|
||||
|
||||
} else {
|
||||
trace("in calibrateSensor, failed to decode", log: log, type: .error)
|
||||
callback(nil)
|
||||
}
|
||||
} catch {
|
||||
trace("in calibrateSensor, got error trying to decode GetCalibrationStatus", log: log, type: .error)
|
||||
callback(nil)
|
||||
// get libreDerivedAlgorithmParameters from remote server
|
||||
post(bytes: bytes, site: site, token: token, { (data, errorDescription) in
|
||||
|
||||
// define default result that will be returned in defer statement
|
||||
var libreDerivedAlgorithmParameters:LibreDerivedAlgorithmParameters? = nil
|
||||
var errorDescription = errorDescription
|
||||
defer {
|
||||
callback(libreDerivedAlgorithmParameters, errorDescription)
|
||||
}
|
||||
|
||||
// if errorDescription is not nil then something went wrong
|
||||
if errorDescription != nil {return}
|
||||
|
||||
// if data is nil then no need to continue
|
||||
guard let data = data else {
|
||||
// shouldn't happen because if data is nil it means something went wrong in call to post and so errorDescription should not be nil
|
||||
errorDescription = "data received form remote server is nil"
|
||||
return
|
||||
}
|
||||
|
||||
var getCalibrationStatus:GetCalibrationStatus?
|
||||
do {
|
||||
getCalibrationStatus = try JSONDecoder().decode(GetCalibrationStatus.self, from: data)
|
||||
} catch {
|
||||
trace("Failed to decode data received from remote server. data received from remote server = %{public}@", log: log, type: .error, String(bytes: data, encoding: .utf8) ?? "")
|
||||
errorDescription = String(bytes: data, encoding: .utf8) ?? "Failed to decode data received from remote server."
|
||||
return
|
||||
}
|
||||
|
||||
if let getCalibrationStatus = getCalibrationStatus, let slope = getCalibrationStatus.slope {
|
||||
libreDerivedAlgorithmParameters = LibreDerivedAlgorithmParameters(slope_slope: slope.slopeSlope ?? 0, slope_offset: slope.slopeOffset ?? 0, offset_slope: slope.offsetSlope ?? 0, offset_offset: slope.offsetOffset ?? 0, isValidForFooterWithReverseCRCs: Int(slope.isValidForFooterWithReverseCRCs ?? 1), extraSlope: 1.0, extraOffset: 0.0, sensorSerialNumber: serialNumber)
|
||||
do {
|
||||
let data = try JSONEncoder().encode(libreDerivedAlgorithmParameters)
|
||||
save(data: data)
|
||||
} catch {
|
||||
// encoding data failed, no need to handle as an error, it means probably next time a new post will be done to the oop web server
|
||||
trace("in calibrateSensor, error while encoding data : %{public}@", log: log, type: .error, error.localizedDescription)
|
||||
}
|
||||
} else {
|
||||
trace("in calibrateSensor, slope is nil", log: log, type: .error)
|
||||
errorDescription = "slope is nil"
|
||||
return
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// MARK: - private functions
|
||||
|
||||
private static func post(bytes: [UInt8],_ completion:@escaping (( _ data_: Data, _ response: String, _ success: Bool ) -> Void)) {
|
||||
/// - parameters:
|
||||
/// - bytes : the data to post
|
||||
/// - site : the oop web site (inclusive http ...)
|
||||
/// - token : the token to use
|
||||
/// - completion : takes data returned from the remote server optional, errorDescription which is a string if anything went wrong, eg host could not be reached, if errorDescription not nil then it failed
|
||||
private static func post(bytes: Data, site: String, token: String, _ completion:@escaping (( _ data_: Data?, _ errorDescription: String?) -> Void)) {
|
||||
|
||||
let date = Date().toMillisecondsAsInt64()
|
||||
let bytesAsData = Data(bytes: bytes, count: bytes.count)
|
||||
|
||||
let json: [String: String] = [
|
||||
"token": ConstantsLibreOOP.token,
|
||||
"content": "\(bytesAsData.hexEncodedString())",
|
||||
"token": token,
|
||||
"content": "\(bytes.hexEncodedString())",
|
||||
"timestamp": "\(date)"]
|
||||
if let uploadURL = URL.init(string: ConstantsLibreOOP.site) {
|
||||
|
||||
if let uploadURL = URL.init(string: site) {
|
||||
|
||||
let request = NSMutableURLRequest(url: uploadURL)
|
||||
request.httpMethod = "POST"
|
||||
|
||||
request.setBodyContent(contentMap: json)
|
||||
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
|
||||
|
||||
let task = URLSession.shared.dataTask(with: request as URLRequest) {
|
||||
data, response, _ in
|
||||
data, urlResponse, error in
|
||||
|
||||
guard let data = data else {
|
||||
|
||||
trace("in post, network error", log: log, type: .error)
|
||||
|
||||
// TODO: check urlResponse and http error code ? (see also NightScoutUploadManager)
|
||||
|
||||
// define default result that will be returned in defer statement
|
||||
var errorDescription:String? = nil
|
||||
defer {
|
||||
DispatchQueue.main.sync {
|
||||
completion("network error".data(using: .utf8)!, "network error", false)
|
||||
completion(data, errorDescription)
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
if let response = String(data: data, encoding: String.Encoding.utf8) {
|
||||
|
||||
trace("in post, successful", log: log, type: .info)
|
||||
|
||||
DispatchQueue.main.sync {
|
||||
completion(data, response, true)
|
||||
}
|
||||
// error cases
|
||||
if let error = error {
|
||||
|
||||
trace("post failed, error = %{public}@", log: self.log, type: .error, error.localizedDescription)
|
||||
errorDescription = error.localizedDescription
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
trace("in post, response error", log: log, type: .error)
|
||||
DispatchQueue.main.sync {
|
||||
completion("response error".data(using: .utf8)!, "response error", false)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
task.resume()
|
||||
} else {
|
||||
completion(nil, "failed to create url from " + site)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,11 +205,11 @@ class LibreOOPClient {
|
|||
do {
|
||||
try data.write(to: url)
|
||||
} catch {
|
||||
print("write error:", error)
|
||||
trace("in save, failed to save data", log: log, type: .error)
|
||||
}
|
||||
}
|
||||
|
||||
private static func trendMeasurements(bytes: [UInt8], date: Date, timeStampLastBgReading: Date, _ offset: Double = 0.0, slope: Double = 0.1, LibreDerivedAlgorithmParameterSet: LibreDerivedAlgorithmParameters?) -> [LibreMeasurement] {
|
||||
private static func trendMeasurements(bytes: Data, date: Date, timeStampLastBgReading: Date, _ offset: Double = 0.0, slope: Double = 0.1, LibreDerivedAlgorithmParameterSet: LibreDerivedAlgorithmParameters?) -> [LibreMeasurement] {
|
||||
|
||||
// let headerRange = 0..<24 // 24 bytes, i.e. 3 blocks a 8 bytes
|
||||
let bodyRange = 24..<320 // 296 bytes, i.e. 37 blocks a 8 bytes
|
||||
|
@ -212,7 +239,7 @@ class LibreOOPClient {
|
|||
}
|
||||
|
||||
|
||||
private static func trendToLibreGlucose(_ measurements: [LibreMeasurement]) -> [LibreRawGlucoseData]?{
|
||||
private static func trendToLibreGlucose(_ measurements: [LibreMeasurement]) -> [LibreRawGlucoseData]{
|
||||
|
||||
var origarr = [LibreRawGlucoseData]()
|
||||
|
||||
|
@ -222,14 +249,9 @@ class LibreOOPClient {
|
|||
origarr.append(glucose)
|
||||
}
|
||||
|
||||
var arr : [LibreRawGlucoseData]
|
||||
arr = LibreGlucoseSmoothing.CalculateSmothedData5Points(origtrends: origarr)
|
||||
return LibreGlucoseSmoothing.CalculateSmothedData5Points(origtrends: origarr)
|
||||
|
||||
/*for glucose in arr {
|
||||
debuglogging("in trendToLibreGlucose after CalculateSmothedData5Points, glucose.glucoseLevelRaw = " + glucose.glucoseLevelRaw.description + ", glucose.unsmoothedGlucose = " + glucose.unsmoothedGlucose.description)
|
||||
}*/
|
||||
|
||||
return arr
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -157,7 +157,11 @@ final class RootViewController: UIViewController {
|
|||
, context: nil)
|
||||
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.webOOPEnabled.rawValue, options: .new
|
||||
, context: nil)
|
||||
|
||||
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.webOOPtoken.rawValue, options: .new
|
||||
, context: nil)
|
||||
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.webOOPsite.rawValue, options: .new
|
||||
, context: nil)
|
||||
|
||||
// setup delegate for UNUserNotificationCenter
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
|
||||
|
@ -268,9 +272,9 @@ final class RootViewController: UIViewController {
|
|||
/// - sensorTimeInMinutes : should be present only if it's the first reading(s) being processed for a specific sensor and is needed if it's a transmitterType that returns true to the function canDetectNewSensor
|
||||
private func processNewGlucoseData(glucoseData: inout [GlucoseData], sensorTimeInMinutes: Int?) {
|
||||
|
||||
/*for glucose in glucoseData {
|
||||
for glucose in glucoseData {
|
||||
debuglogging(glucose.description)
|
||||
}*/
|
||||
}
|
||||
|
||||
// check that calibrations and coredata manager is not nil
|
||||
guard let calibrationsAccessor = calibrationsAccessor, let coreDataManager = coreDataManager else {
|
||||
|
@ -423,6 +427,9 @@ final class RootViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
case UserDefaults.Key.webOOPtoken, UserDefaults.Key.webOOPsite:
|
||||
cgmTransmitter?.setWebOOPSiteAndToken(oopWebSite: UserDefaults.standard.webOOPSite ?? ConstantsLibreOOP.site, oopWebToken: UserDefaults.standard.webOOPtoken ?? ConstantsLibreOOP.token)
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
@ -613,10 +620,10 @@ final class RootViewController: UIViewController {
|
|||
}
|
||||
|
||||
case .miaomiao:
|
||||
cgmTransmitter = CGMMiaoMiaoTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), webOOPEnabled: UserDefaults.standard.webOOPEnabled)
|
||||
cgmTransmitter = CGMMiaoMiaoTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), webOOPEnabled: UserDefaults.standard.webOOPEnabled, oopWebSite: UserDefaults.standard.webOOPSite ?? ConstantsLibreOOP.site, oopWebToken: UserDefaults.standard.webOOPtoken ?? ConstantsLibreOOP.token)
|
||||
|
||||
case .Bubble:
|
||||
cgmTransmitter = CGMBubbleTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber, webOOPEnabled: UserDefaults.standard.webOOPEnabled)
|
||||
cgmTransmitter = CGMBubbleTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber, webOOPEnabled: UserDefaults.standard.webOOPEnabled, oopWebSite: UserDefaults.standard.webOOPSite ?? ConstantsLibreOOP.site, oopWebToken: UserDefaults.standard.webOOPtoken ?? ConstantsLibreOOP.token)
|
||||
|
||||
case .GNSentry:
|
||||
cgmTransmitter = CGMGNSEntryTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0))
|
||||
|
@ -1045,6 +1052,10 @@ final class RootViewController: UIViewController {
|
|||
/// conform to CGMTransmitterDelegate
|
||||
extension RootViewController:CGMTransmitterDelegate {
|
||||
|
||||
func error(message: String) {
|
||||
UIAlertController(title: Texts_Common.warning, message: message, actionHandler: nil).presentInOwnWindow(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func reset(successful: Bool) {
|
||||
|
||||
// reset setting to false
|
||||
|
|
|
@ -9,6 +9,10 @@ fileprivate enum Setting:Int, CaseIterable {
|
|||
case resetRequired = 2
|
||||
/// is webOOP enabled or not
|
||||
case webOOP = 3
|
||||
/// if webOOP enabled, what site to use
|
||||
case webOOPsite = 4
|
||||
/// if webOOP enabled, value of the token
|
||||
case webOOPtoken = 5
|
||||
}
|
||||
|
||||
/// conforms to SettingsViewModelProtocol for all transmitter settings in the first sections screen
|
||||
|
@ -72,6 +76,13 @@ struct SettingsViewTransmitterSettingsViewModel:SettingsViewModelProtocol {
|
|||
|
||||
case .webOOP:
|
||||
return SettingsSelectedRowAction.nothing
|
||||
|
||||
case .webOOPsite:
|
||||
return SettingsSelectedRowAction.askText(title: Texts_SettingsView.labelWebOOP, message: Texts_SettingsView.labelWebOOPSiteExplainingText, keyboardType: .URL, text: UserDefaults.standard.webOOPSite, placeHolder: Texts_Common.default0, actionTitle: nil, cancelTitle: nil, actionHandler: {(oopwebsiteurl:String) in UserDefaults.standard.webOOPSite = oopwebsiteurl.toNilIfLength0()}, cancelHandler: nil)
|
||||
|
||||
case .webOOPtoken:
|
||||
return SettingsSelectedRowAction.askText(title: Texts_SettingsView.labelWebOOP, message: Texts_SettingsView.labelWebOOPtokenExplainingText, keyboardType: .default, text: UserDefaults.standard.webOOPtoken, placeHolder: Texts_Common.default0, actionTitle: nil, cancelTitle: nil, actionHandler: {(oopwebtoken:String) in UserDefaults.standard.webOOPtoken = oopwebtoken.toNilIfLength0()}, cancelHandler: nil)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,10 +112,14 @@ struct SettingsViewTransmitterSettingsViewModel:SettingsViewModelProtocol {
|
|||
}
|
||||
|
||||
// for now WebOOP is only for transmitters that don't need transmitterId and no reset possible.
|
||||
// So for those transmitters, if canWebOOP, then amount of rows = 2
|
||||
// So for those transmitters, if canWebOOP and if enabled, then amount of rows = 4 (enable weboop, weboop site and weboop token. If webOOP not enabled, then only show setting to enabled weboop
|
||||
// Needs adaptation in case we would enable webOOP for transmitters with transmitterId, like Blucon
|
||||
if transmitterType.canWebOOP() {
|
||||
count = 2
|
||||
if UserDefaults.standard.webOOPEnabled {
|
||||
count = 4
|
||||
} else {
|
||||
count = 2
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
|
@ -130,6 +145,12 @@ struct SettingsViewTransmitterSettingsViewModel:SettingsViewModelProtocol {
|
|||
|
||||
case .webOOP:
|
||||
return Texts_SettingsView.labelWebOOPTransmitter
|
||||
|
||||
case .webOOPsite:
|
||||
return Texts_SettingsView.labelWebOOPSite
|
||||
|
||||
case .webOOPtoken:
|
||||
return Texts_SettingsView.labelWebOOPtoken
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,6 +167,10 @@ struct SettingsViewTransmitterSettingsViewModel:SettingsViewModelProtocol {
|
|||
return UITableViewCell.AccessoryType.none
|
||||
case .webOOP:
|
||||
return UITableViewCell.AccessoryType.none
|
||||
case .webOOPsite:
|
||||
return UITableViewCell.AccessoryType.disclosureIndicator
|
||||
case .webOOPtoken:
|
||||
return UITableViewCell.AccessoryType.disclosureIndicator
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -163,6 +188,19 @@ struct SettingsViewTransmitterSettingsViewModel:SettingsViewModelProtocol {
|
|||
return UserDefaults.standard.transmitterResetRequired ? Texts_Common.yes:Texts_Common.no
|
||||
case .webOOP:
|
||||
return nil
|
||||
case .webOOPsite:
|
||||
if let site = UserDefaults.standard.webOOPSite {
|
||||
return site
|
||||
} else {
|
||||
return Texts_Common.default0
|
||||
}
|
||||
case .webOOPtoken:
|
||||
if let token = UserDefaults.standard.webOOPtoken {
|
||||
return token
|
||||
} else {
|
||||
return Texts_Common.default0
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,14 +217,14 @@ struct SettingsViewTransmitterSettingsViewModel:SettingsViewModelProtocol {
|
|||
|
||||
// MARK: - private helper functions
|
||||
|
||||
/// if it's a transmitterType that canWebOOP, then when user clicks second row (ie index = 1), then fix to 3 is done
|
||||
/// if it's a transmitterType that canWebOOP, then when user clicks second row or highter (ie index >= 1), then fix to index + 2 is done
|
||||
private func fixWebOOPIndex(_ index: Int) -> Int {
|
||||
|
||||
var index = index
|
||||
|
||||
if let transmitterType = UserDefaults.standard.transmitterType {
|
||||
if transmitterType.canWebOOP() && index == 1 {
|
||||
index = 3
|
||||
if transmitterType.canWebOOP() && index >= 1 {
|
||||
index = index + 2
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue