oop web : site and token can be changed in settings. Also again some code redesign

This commit is contained in:
Johan Degraeve 2019-08-15 20:30:19 +02:00
parent d3528d0d35
commit 91b5f12a71
20 changed files with 447 additions and 155 deletions

View File

@ -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>";

View File

@ -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)
}
}
}

View File

@ -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";

View File

@ -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";

View File

@ -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";

View File

@ -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

View File

@ -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?) {

View File

@ -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?) {

View File

@ -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.

View File

@ -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)
}

View File

@ -231,7 +231,8 @@ class CGMBluconTransmitter: BluetoothTransmitter {
return(curGluc)
}
/// this transmitter does not support oop web
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {}
}

View File

@ -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 {

View File

@ -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) {}
}

View File

@ -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?) {

View File

@ -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 {

View File

@ -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)
}
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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
}
}